From 966b3c41a61d9d94c09d91ffa17e466b78953255 Mon Sep 17 00:00:00 2001 From: Fab Date: Thu, 19 Mar 2015 19:41:56 +0100 Subject: [PATCH] i18n mod using gettext tools and logic. --- .gitignore | 1 + mods/i18n/README.txt | 76 ++++++++++++++++++++++++++++++++ mods/i18n/init.lua | 100 +++++++++++++++++++++++++++++++++++++++++++ scripts/i18n.sh | 80 ++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 mods/i18n/README.txt create mode 100644 mods/i18n/init.lua create mode 100644 scripts/i18n.sh diff --git a/.gitignore b/.gitignore index a57dbc90..01fb3024 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ *bak* tags *.vim +*.mo diff --git a/mods/i18n/README.txt b/mods/i18n/README.txt new file mode 100644 index 00000000..af8e0ae1 --- /dev/null +++ b/mods/i18n/README.txt @@ -0,0 +1,76 @@ +i18n mod for minetest_game, using gettext logic +=============================================== + +Into init.lua some parts of code were originally released as WTFPL or +as public domain. See init.lua for details. + +------------------------------------------------- +Remaining code is released with following license +------------------------------------------------- + +Copyright (C) 2015 netfab + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +http://www.gnu.org/licenses/lgpl-2.1.html + + +------------------------------------ +How do I enable i18n in my foo mod ? +------------------------------------ + 0 - You need following gettext tools on your system to use scripts/i18n.sh + bash script : + - xgettext + - msgfmt + + 1 - Add i18n dependency to mods/foo/depends.txt + + 2 - In your code, set strings as translatable. + Example (in patch format) : + +- minetest.chat_send_player(name, "You can only sleep at night.") ++ minetest.chat_send_player(name, _("You can only sleep at night.")) + + 3 - In mods/foo/init.lua, load *your own* mo file by using + i18n.load_mo_file() function : + + local modpath = minetest.get_modpath("foo") ++ i18n.load_mo_file(modpath, "foo") + + Above example is loading : + + mods/foo/i18n/«language_code»/foo.mo + + Language_code is auto-detected from minetest language setting, or from LANG + env variable. Default is : en + + 4 - Generate a po template file that will be usable by translators : + + $ cd scripts/ + $ bash i18n.sh --po-templates + entering ../mods/foo ... created i18n/template.po + + For each mod which depends on the i18n mod, this script will create an i18n/ + directory and will (re)generate a template.po file. Each mod will have its + own i18n/template.po. + + 5 - Prepare translation file (for example in french) : + + $ cd mods/foo/i18n/ + $ mkdir fr + $ cp template.po fr/foo.po + + Now you can translate strings from mods/foo/i18n/fr/foo.po + + +------------------------------------------- +How do I build all mo files for packaging ? +------------------------------------------- + $ cd scripts/ + $ bash i18n.sh --build-mo + ../mods/foo/i18n/fr/foo.po : 9 translated messages. + ../mods/bar/i18n/fr/bar.po : 7 translated messages. + diff --git a/mods/i18n/init.lua b/mods/i18n/init.lua new file mode 100644 index 00000000..f7b341f4 --- /dev/null +++ b/mods/i18n/init.lua @@ -0,0 +1,100 @@ +-- minetest_game/mods/i18n/init.lua + +i18n = {} +i18n.hash = {} + +-- Following 4 lines coming from https://github.com/kaeza/minetest-intllib +-- (released as WTFPL) +local LANG = minetest.setting_get("language") +if not (LANG and (LANG ~= "")) then LANG = os.getenv("LANG") end +if not (LANG and (LANG ~= "")) then LANG = "en" end +i18n.LANG = LANG:sub(1, 2) + +function i18n.gettext(text) + return i18n.hash[text] or text +end + +_=assert(i18n.gettext) + +-- +-- Original load_mo_file() function coming from : +-- http://lua-users.org/lists/lua-l/2010-04/msg00005.html +-- (released as public domain) +-- +-- Function sligthly modified, original comment is below. +-- +----------------------------------------------------------- +-- load an mo file and return a lua table +-- @param mo_file name of the file to load +-- @return table on success +-- @return nil,string on failure +-- @copyright J.Jorgen von Bargen +-- @licence I provide this as public domain +-- @see http://www.gnu.org/software/hello/manual/gettext/MO-Files.html +----------------------------------------------------------- + +function i18n.load_mo_file(modpath, modname) + local mo_file = modpath.."/i18n/"..i18n.LANG.."/"..modname..".mo" + + -------------------------------- + -- open file and read data + -------------------------------- + local fd,err=io.open(mo_file,"rb") + if not fd then return nil,err end + local mo_data=fd:read("*all") + fd:close() + + -------------------------------- + -- precache some functions + -------------------------------- + local byte=string.byte + local sub=string.sub + + -------------------------------- + -- check format + -------------------------------- + local peek_long --localize + local magic=sub(mo_data,1,4) + -- intel magic 0xde120495 + if magic=="\222\018\004\149" then + peek_long=function(offs) + local a,b,c,d=byte(mo_data,offs+1,offs+4) + return ((d*256+c)*256+b)*256+a + end + -- motorola magic = 0x950412de + elseif magic=="\149\004\018\222" then + peek_long=function(offs) + local a,b,c,d=byte(mo_data,offs+1,offs+4) + return ((a*256+b)*256+c)*256+d + end + else + return nil,"no valid mo-file" + end + + -------------------------------- + -- version + -------------------------------- + local V=peek_long(4) + if V~=0 then + return nul,"unsupported version" + end + + ------------------------------ + -- get number of offsets of table + ------------------------------ + local N,O,T=peek_long(8),peek_long(12),peek_long(16) + ------------------------------ + -- traverse and get strings + ------------------------------ + local hash={} + for nstr=1,N do + local ol,oo=peek_long(O),peek_long(O+4) O=O+8 + local tl,to=peek_long(T),peek_long(T+4) T=T+8 + hash[sub(mo_data,oo+1,oo+ol)]=sub(mo_data,to+1,to+tl) + end +-- return function(text) +-- return hash[text] or text +-- end + i18n.hash = hash +end + diff --git a/scripts/i18n.sh b/scripts/i18n.sh new file mode 100644 index 00000000..06a2299c --- /dev/null +++ b/scripts/i18n.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# +# i18n.sh -- Shell script to update/build gettext files +# -- Released with minetest_game mods/i18n mod +# +# Copyright (C) 2015 netfab +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +ROOT_DIRECTORY=.. + +function display_help() { + echo + echo "Usage :" + echo " $ $0 --po-templates" + echo " $ $0 --build-mo" + echo + echo "If your mod depends on the i18n mod, « --po-templates » will create" + echo "an i18n/ directory into your mod path, then it will extract gettext" + echo "strings from your lua files. Finally, result will be saved as i18n/template.po" + echo + echo "« --build-mo » will scan for po files in mods/*/i18n/ subdirs and will" + echo "compile them to binary format (using gettext's msgfmt)." + echo + echo "See also mods/i18n/README.txt." + echo +} + +function update_mods_templates() { + for MOD_DIR in ${ROOT_DIRECTORY}/mods/*; do + depfile="$MOD_DIR/depends.txt" + if [[ -f "$depfile" ]]; then + i18n=$(grep -c i18n "$depfile") + if [[ $i18n -gt 0 ]]; then + OLDPWD=$PWD + cd "$MOD_DIR" || exit 7 + echo -n "entering $MOD_DIR ... " + mkdir -p i18n || exit 8 + xgettext -L Lua *.lua --from-code=UTF-8 -o i18n/template.po + echo "created i18n/template.po" + cd "$OLDPWD" || exit 9 + fi + fi + done +} + +function compile_catalogs() { + for x in $(find ${ROOT_DIRECTORY}/mods/ -mindepth 4 -name *.po); do + pofile=${x##*/} + pofile=${pofile:0:-3} + filepath=${x%/*} + echo -n "${filepath}/${pofile}.po : " + msgfmt "${filepath}/${pofile}.po" -cv -o "${filepath}/${pofile}.mo" + done +} + +case "$1" in + '--po-templates') + update_mods_templates + ;; + '--build-mo') + compile_catalogs + ;; + *) + display_help + ;; +esac