From 6e5e3503dfe4a7858c5b85ab6ad4255d57cab2e1 Mon Sep 17 00:00:00 2001 From: AntumDeluge Date: Fri, 5 Aug 2016 19:10:04 -0700 Subject: [PATCH] Add "intllib" mod. --- README.md | 2 + mods/intllib/README-es_UY.md | 136 ++++++++++++++++++++++++++++ mods/intllib/README-pt_BR.md | 87 ++++++++++++++++++ mods/intllib/README.md | 143 ++++++++++++++++++++++++++++++ mods/intllib/init.lua | 74 ++++++++++++++++ mods/intllib/intllib.lua | 3 + mods/intllib/lib.lua | 63 +++++++++++++ mods/intllib/tools/findtext.lua | 142 +++++++++++++++++++++++++++++ mods/intllib/tools/updatetext.lua | 141 +++++++++++++++++++++++++++++ 9 files changed, 791 insertions(+) create mode 100644 mods/intllib/README-es_UY.md create mode 100644 mods/intllib/README-pt_BR.md create mode 100644 mods/intllib/README.md create mode 100644 mods/intllib/init.lua create mode 100644 mods/intllib/intllib.lua create mode 100644 mods/intllib/lib.lua create mode 100755 mods/intllib/tools/findtext.lua create mode 100644 mods/intllib/tools/updatetext.lua diff --git a/README.md b/README.md index ca72228c..38c8cf87 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ The following mods are also included: * oerrki ([Creatures MOB-Engine][cme]) * [spidermob][] ([CC-BY-SA / WTFPL / CC0](mods/hostils/spidermob/LICENSE)) * zombie ([Creatures MOB-Engine][cme]) +* [intllib][] (WTFPL) * lib/ * [biome_lib][] ([WTFPL](mods/lib/biome_lib/README.md)) * [signs_lib][] ([BSD/WTFPL](mods/lib/signs_lib/copyright.txt)) @@ -127,6 +128,7 @@ The following mods are also included: [glow]: https://forum.minetest.net/viewtopic.php?t=6300 [helicopter]: https://forum.minetest.net/viewtopic.php?t=6183 [homedecor]: https://forum.minetest.net/viewtopic.php?t=2041 +[intllib]: https://forum.minetest.net/viewtopic.php?t=4929 [inventory_plus]: https://forum.minetest.net/viewtopic.php?t=3100 [kpgmobs]: https://forum.minetest.net/viewtopic.php?t=8798 [lightning]: https://forum.minetest.net/viewtopic.php?t=13886 diff --git a/mods/intllib/README-es_UY.md b/mods/intllib/README-es_UY.md new file mode 100644 index 00000000..79ea84dc --- /dev/null +++ b/mods/intllib/README-es_UY.md @@ -0,0 +1,136 @@ + +# Biblioteca de Internacionalización para Minetest + +Por Diego Martínez (kaeza). +Lanzada bajo WTFPL. + +Éste mod es un intento de proveer soporte para internacionalización para otros mods +(lo cual Minetest carece actualmente). + +## Cómo usar + +### Para usuarios finales + +Para usar éste mod, simplemente [instálalo](http://wiki.minetest.net/Installing_Mods) +y habilítalo en la interfaz. + +Éste mod intenta detectar el idioma del usuario, pero ya que no existe una solución +portable para hacerlo, éste intenta varias alternativas, y utiliza la primera +encontrada: + + * Opción `language` en `minetest.conf`. + * Si ésta no está definida, usa la variable de entorno `LANG` (ésta está + siempre definida en SOs como Unix). + * Si todo falla, usa `en` (lo cual básicamente significa textos sin traducir). + +En todo caso, el resultado final debe ser el In any case, the end result should be the +[Código de Idioma ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +del idioma deseado. Tenga en cuenta tambien que (de momento) solo los dos primeros +caracteres son usados, así que por ejemplo, las opciones `de_DE.UTF-8`, `de_DE`, +y `de` son iguales. + +Algunos códigos comúnes: `es` para Español, `pt` para Portugués, `fr` para Francés, +`it` para Italiano, `de` para Aleman. + +### Para desarrolladores + +Para habilitar funcionalidad en tu mod, copia el siguiente fragmento de código y pégalo +al comienzo de tus archivos fuente: + +```lua +-- Boilerplate to support localized strings if intllib mod is installed. +local S +if minetest.get_modpath("intllib") then + S = intllib.Getter() +else + -- Si no requieres patrones de reemplazo (@1, @2, etc) usa ésto: + S = function(s) return s end + + -- Si requieres patrones de reemplazo, pero no escapes, usa ésto: + S = function(s,a,...)a={a,...}return s:gsub("@(%d+)",function(n)return a[tonumber(n)]end)end + + -- Usa ésto si necesitas funcionalidad completa: + S = function(s,a,...)if a==nil then return s end a={a,...}return s:gsub("(@?)@(%(?)(%d+)(%)?)",function(e,o,n,c)if e==""then return a[tonumber(n)]..(o==""and c or"")else return"@"..o..n..c end end) end +end +``` + +Tambien necesitarás depender opcionalmente de intllib. Para hacerlo, añade `intllib?` +a tu archivo `depends.txt`. Ten en cuenta tambien que si intllib no está instalado, +la función `S` es definida para regresar la cadena sin cambios. Ésto se hace para +evitar la necesidad de llenar tu código con montones de `if`s (o similar) para verificar +que la biblioteca está instalada. + +Luego, para cada cadena de texto a traducir en tu código, usa la función `S` +(definida en el fragmento de arriba) para regresar la cadena traducida. Por ejemplo: + +```lua +minetest.register_node("mimod:minodo", { + -- Cadena simple: + description = S("My Fabulous Node"), + -- Cadena con patrones de reemplazo: + description = S("@1 Car", "Blue"), + -- ... +}) +``` + +Nota: Las cadenas en el código fuente por lo general deben estar en ingles ya que +es el idioma que más se habla. Es perfectamente posible especificar las cadenas +fuente en español y proveer una traducción al ingles, pero no se recomienda. + +Luego, crea un directorio llamado `locale` dentro del directorio de tu mod, y crea +un archivo "plantilla" (llamado `template.txt` por lo general) con todas las cadenas +a traducir (ver *Formato de archivo de traducciones* más abajo). Los traductores +traducirán las cadenas en éste archivo para agregar idiomas a tu mod. + +### Para traductores + +Para traducir un mod que tenga soporte para intllib al idioma deseado, copia el +archivo `locale/template.txt` a `locale/IDIOMA.txt` (donde `IDIOMA` es el +[Código de Idioma ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +de tu idioma (`es` para español). + +Abre el archivo en tu editor favorito, y traduce cada línea colocando el texto +traducido luego del signo de igualdad. + +Ver *Formato de archivo de traducciones* más abajo. + +## Formato de archivo de traducciones + +He aquí un ejemplo de archivo de idioma para el español (`es.txt`): + +```cfg +# Un comentario. +# Otro comentario. +Ésta línea es ignorada porque no tiene un signo de igualdad. +Hello, World! = Hola, Mundo! +String with\nnewlines = Cadena con\nsaltos de linea +String with an \= equals sign = Cadena con un signo de \= igualdad +``` + +Archivos de idioma (o traducción) son archivos de texto sin formato que consisten de +líneas con el formato `texto fuente = texto traducido`. El archivo debe ubicarse en el +subdirectorio `locale` del mod, y su nombre debe ser las dos letras del +[Código de Idioma ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +del lenguaje al cual se desea traducir. + +Los archivos deben usar la codificación UTF-8. + +Las líneas que comienzan en el símbolo numeral (`#`) son comentarios y son ignoradas +por el lector. Tenga en cuenta que los comentarios terminan al final de la línea; +no hay soporte para comentarios multilínea. Las líneas que no contengan un signo +de igualdad (`=`) tambien son ignoradas. + +## Palabras finales + +Gracias por leer hasta aquí. +Si tienes algún comentario/sugerencia, por favor publica en el +[tema en los foros](https://forum.minetest.net/viewtopic.php?id=4929). Para +reportar errores, usa el [rastreador](https://github.com/minetest-mods/intllib/issues/new) +en Github. + +¡Que se hagan las traducciones! :P + +\-- + +Suyo, +Kaeza diff --git a/mods/intllib/README-pt_BR.md b/mods/intllib/README-pt_BR.md new file mode 100644 index 00000000..01f273ee --- /dev/null +++ b/mods/intllib/README-pt_BR.md @@ -0,0 +1,87 @@ +Internacionalização Lib para Minetest +Por Diego Martínez (a.k.a. "Kaeza"). +Lançado como WTFPL. + +Este mod é uma tentativa de fornecer suporte de internacionalização para mods +(algo que Minetest atualmente carece). + +Como posso usá-lo? +A fim de habilitá-lo para o seu mod, copie o seguinte trecho de código e cole no início de seu(s) arquivo(s) fonte: + + -- Padronizado para suportar cadeias (strings) locais se o mod intllib estiver instalado + local S + if minetest.get_modpath("intllib") then + S = intllib.Getter() + else + -- Se você não usar inserções (@1, @2, etc) você pode usar este: + S = function(s) return s end + + -- Se você usar inserções, mas não usar escapes de inserção (\=, \n, etc) isso vai funcionar: + S = function(s,a,...)a={a,...}return s:gsub("@(%d+)",function(n)return a[tonumber(n)]end)end + + -- Use isso se você precisar de funcionalidade total: + S = function(s,a,...)if a==nil then return s end a={a,...}return s:gsub("(@?)@(%(?)(%d+)(%)?)",function(e,o,n,c)if e==""then return a[tonumber(n)]..(o==""and c or"")else return"@"..o..n..c end end) end + end + +Você também vai precisar depender opcionalmente do mod intllib, adicionando "intllib?" +em uma linha vazia de seu depends.txt. Observe também que se intllib não estiver +instalado, a função S() é definido para retornar a string inalterada. Isto é feito +para que você não tenha que regar toneladas de 'if's (ou de estruturas semelhantes) +para verificar se a lib está realmente instalada. + +Em seguida, para cada string "traduzível" em suas fontes, use a função S() +(definida no trecho anterior) para retornar uma string traduzida. Por exemplo: + + minetest.register_node("meu_mod:meu_node", { + description = S("Meu Fabuloso Node"), + <...> + }) + +Em seguida, dentro do diretório do seu mod, crie um diretório chamado 'locale' +no qual você deve colocar os arquivos nomeados com duas letras ( de acordo +com a ISO para códigos de idiomas) para os idiomas que seu mod vai suportar. +Aqui vai um exemplo de arquivo para idioma espanhol ('es.txt'): + + # As linhas que começam com um sinal de libra '#' são comentários e + # efetivamente ignorados pelo carregamento. + # Note-se que comentários duram apenas até o fim da linha; + # Não há nenhum suporte para comentários de várias linhas. + Ola, Mundo! = Hola, Mundo! + String com\npulo de linha = Cadena con\nsaltos de linea + String com um sinal de \= igualdade = Cadena con un signo de \= igualdad + +Como atualmente não existe nenhuma maneira portátil para detectar o idioma, +esta biblioteca tenta várias alternativas, e usa o primeiro encontrado: + - Valor de 'language' definido em 'minetest.conf' + - Variavel de ambiente 'LANG' (normalmente definida em Unix-like SO's). + - Padrão "en". + +Note que, em qualquer caso, apenas até os dois primeiros caracteres são usados +para cada idioma, por exemplo, as definições de "pt_BR.UTF-8", "pt_BR", e "pt" +são todos iguais. +Os usuários do Windows não têm a variavel de ambiente 'LANG' por padrão. +Para adicioná-lo, faça o seguinte: + - Clique em Iniciar > Configurações > Painel de Controle. + - Iniciar o aplicativo "System". + - Clique na aba "Avançado". + - Clique no botão "Variáveis ​​de Ambiente" + - Clique em "Novo". + - Tipo "LANG" (sem aspas) com o nome e o código de linguagem como valor. + - Clique em OK até que todas as caixas de diálogo estão fechadas. +Como alternativa para todas as plataformas, se você não quiser modificar as +configurações do sistema, você pode adicionar a seguinte linha ao seu +arquivo 'minetest.conf': + language = + +Note também que existem alguns problemas com o uso acentos gráficos e, em geral +caracteres não-latinos em strings. Até que uma correção seja encontrada, +por favor, limite-se a usar apenas caracteres da US-ASCII. + + +Obrigado por ler até este ponto. +Se você tiver quaisquer comentários/sugestões, por favor poste no tópico do fórum. + +Haja textos traduzidos! :P +-- +Tutorial criado por Kaeza +Traduzido para Português do Brasil por BrunoMine diff --git a/mods/intllib/README.md b/mods/intllib/README.md new file mode 100644 index 00000000..185ca1df --- /dev/null +++ b/mods/intllib/README.md @@ -0,0 +1,143 @@ + +# Internationalization Lib for Minetest + +By Diego Martínez (kaeza). +Released as WTFPL. + +This mod is an attempt at providing internationalization support for mods +(something Minetest currently lacks). + +## How to use + +### For end users + +To use this mod, just [install it](http://wiki.minetest.net/Installing_Mods) +and enable it in the GUI. + +The mod tries to detect the user's language, but since there's currently no +portable way to do this, it tries several alternatives, and uses the first one +found: + + * `language` setting in `minetest.conf`. + * If that's not set, it uses the `LANG` environment variable (this is + always set on Unix-like OSes). + * If all else fails, uses `en` (which basically means untranslated strings). + +In any case, the end result should be the +[ISO 639-1 Language Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +of the desired language. Also note that (currently) only up to the first two +characters are used, so for example, the settings `de_DE.UTF-8`, `de_DE`, +and `de` are all equal. + +Some common codes are `es` for Spanish, `pt` for Portuguese, `fr` for French, +`it` for Italian, `de` for German. + +### For mod developers + +In order to enable it for your mod, copy the following code snippet and paste +it at the beginning of your source file(s): + +```lua +-- Boilerplate to support localized strings if intllib mod is installed. +local S +if minetest.get_modpath("intllib") then + S = intllib.Getter() +else + -- If you don't use insertions (@1, @2, etc) you can use this: + S = function(s) return s end + + -- If you use insertions, but not insertion escapes this will work: + S = function(s,a,...)a={a,...}return s:gsub("@(%d+)",function(n)return a[tonumber(n)]end)end + + -- Use this if you require full functionality + S = function(s,a,...)if a==nil then return s end a={a,...}return s:gsub("(@?)@(%(?)(%d+)(%)?)",function(e,o,n,c)if e==""then return a[tonumber(n)]..(o==""and c or"")else return"@"..o..n..c end end) end +end +``` + +You will also need to optionally depend on intllib, to do so add `intllib?` to +an empty line in your `depends.txt`. Also note that if intllib is not installed, +the `S` function is defined so it returns the string unchanged. This is done +so you don't have to sprinkle tons of `if`s (or similar constructs) to check +if the lib is actually installed. + +Next, for each translatable string in your sources, use the `S` function +(defined in the snippet) to return the translated string. For example: + +```lua +minetest.register_node("mymod:mynode", { + -- Simple string: + description = S("My Fabulous Node"), + -- String with insertions: + description = S("@1 Car", "Blue"), + -- ... +}) +``` + +Then, you create a `locale` directory inside your mod directory, and create +a "template" file (by convention, named `template.txt`) with all the +translatable strings (see *Locale file format* below). Translators will +translate the strings in this file to add languages to your mod. + +### For translators + +To translate an intllib-supporting mod to your desired language, copy the +`locale/template.txt` file to `locale/LANGUAGE.txt` (where `LANGUAGE` is the +[ISO 639-1 Language Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +of your language. + +Open up the new file in your favorite editor, and translate each line putting +the translated text after the equals sign. + +See *Locale file format* below for more information about the file format. + +## Locale file format + +Here's an example for a Spanish locale file (`es.txt`): + +```cfg +# A comment. +# Another comment. +This line is ignored since it has no equals sign. +Hello, World! = Hola, Mundo! +String with\nnewlines = Cadena con\nsaltos de linea +String with an \= equals sign = Cadena con un signo de \= igualdad +``` + +Locale (or translation) files are plain text files consisting of lines of the +form `source text = translated text`. The file must reside in the mod's `locale` +subdirectory, and must be named after the two-letter +[ISO 639-1 Language Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) +of the language you want to support. + +The translation files should use the UTF-8 encoding. + +Lines beginning with a pound sign are comments and are effectively ignored +by the reader. Note that comments only span until the end of the line; +there's no support for multiline comments. Lines without an equals sign are +also ignored. + +Characters that are considered "special" can be "escaped" so they are taken +literally. There are also several escape sequences that can be used: + + * Any of `#`, `=` can be escaped to take them literally. The `\#` + sequence is useful if your source text begins with `#`. + * The common escape sequences `\n` and `\t`, meaning newline and + horizontal tab respectively. + * The special `\s` escape sequence represents the space character. It + is mainly useful to add leading or trailing spaces to source or + translated texts, as these spaces would be removed otherwise. + +## Final words + +Thanks for reading up to this point. +Should you have any comments/suggestions, please post them in the +[forum topic](https://forum.minetest.net/viewtopic.php?id=4929). For bug +reports, use the [bug tracker](https://github.com/minetest-mods/intllib/issues/new) +on Github. + +Let there be translated texts! :P + +\-- + +Yours Truly, +Kaeza diff --git a/mods/intllib/init.lua b/mods/intllib/init.lua new file mode 100644 index 00000000..21d4e024 --- /dev/null +++ b/mods/intllib/init.lua @@ -0,0 +1,74 @@ + +-- Old multi-load method compatibility +if rawget(_G, "intllib") then return end + +intllib = { + getters = {}, + strings = {}, +} + + +local MP = minetest.get_modpath("intllib") + +dofile(MP.."/lib.lua") + + +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 +LANG = LANG:sub(1, 2) + + +local INS_CHAR = intllib.INSERTION_CHAR +local insertion_pattern = "("..INS_CHAR.."?)"..INS_CHAR.."(%(?)(%d+)(%)?)" + +local function make_getter(msgstrs) + return function(s, ...) + local str + if msgstrs then + str = msgstrs[s] + end + if not str or str == "" then + str = s + end + if select("#", ...) == 0 then + return str + end + local args = {...} + str = str:gsub(insertion_pattern, function(escape, open, num, close) + if escape == "" then + local replacement = tostring(args[tonumber(num)]) + if open == "" then + replacement = replacement..close + end + return replacement + else + return INS_CHAR..open..num..close + end + end) + return str + end +end + + +function intllib.Getter(modname) + modname = modname or minetest.get_current_modname() + if not intllib.getters[modname] then + local msgstr = intllib.get_strings(modname) + intllib.getters[modname] = make_getter(msgstr) + end + return intllib.getters[modname] +end + + +function intllib.get_strings(modname) + modname = modname or minetest.get_current_modname() + local msgstr = intllib.strings[modname] + if not msgstr then + local modpath = minetest.get_modpath(modname) + msgstr = intllib.load_strings(modpath.."/locale/"..LANG..".txt") + intllib.strings[modname] = msgstr + end + return msgstr or nil +end + diff --git a/mods/intllib/intllib.lua b/mods/intllib/intllib.lua new file mode 100644 index 00000000..adb0f886 --- /dev/null +++ b/mods/intllib/intllib.lua @@ -0,0 +1,3 @@ +-- Support for the old multi-load method +dofile(minetest.get_modpath("intllib").."/init.lua") + diff --git a/mods/intllib/lib.lua b/mods/intllib/lib.lua new file mode 100644 index 00000000..2349de46 --- /dev/null +++ b/mods/intllib/lib.lua @@ -0,0 +1,63 @@ + +intllib = intllib or {} + +local INS_CHAR = "@" +intllib.INSERTION_CHAR = INS_CHAR + +local escapes = { + ["\\"] = "\\", + ["n"] = "\n", + [INS_CHAR] = INS_CHAR..INS_CHAR, +} + +local function unescape(str) + local parts = {} + local n = 1 + local function add(s) + parts[n] = s + n = n + 1 + end + + local start = 1 + while true do + local pos = str:find("\\", start, true) + if pos then + add(str:sub(start, pos - 1)) + else + add(str:sub(start)) + break + end + local c = str:sub(pos + 1, pos + 1) + add(escapes[c] or c) + start = pos + 2 + end + return table.concat(parts) +end + +local function find_eq(s) + for slashes, pos in s:gmatch("([\\]*)=()") do + if (slashes:len() % 2) == 0 then + return pos - 1 + end + end +end + +function intllib.load_strings(filename) + local file, err = io.open(filename, "r") + if not file then + return nil + end + local strings = {} + for line in file:lines() do + line = line:trim() + if line ~= "" and line:sub(1, 1) ~= "#" then + local pos = find_eq(line) + if pos then + local msgid = unescape(line:sub(1, pos - 1):trim()) + strings[msgid] = unescape(line:sub(pos + 1):trim()) + end + end + end + file:close() + return strings +end diff --git a/mods/intllib/tools/findtext.lua b/mods/intllib/tools/findtext.lua new file mode 100755 index 00000000..b6360cb6 --- /dev/null +++ b/mods/intllib/tools/findtext.lua @@ -0,0 +1,142 @@ +#! /usr/bin/env lua + +local me = arg[0]:gsub(".*[/\\](.*)$", "%1") + +local function err(fmt, ...) + io.stderr:write(("%s: %s\n"):format(me, fmt:format(...))) + os.exit(1) +end + +local output +local inputs = { } +local lang +local author + +local i = 1 + +local function usage() + print([[ +Usage: ]]..me..[[ [OPTIONS] FILE... + +Extract translatable strings from the given FILE(s). + +Available options: + -h,--help Show this help screen and exit. + -o,--output X Set output file (default: stdout). + -a,--author X Set author. + -l,--lang X Set language name. +]]) + os.exit(0) +end + +while i <= #arg do + local a = arg[i] + if (a == "-h") or (a == "--help") then + usage() + elseif (a == "-o") or (a == "--output") then + i = i + 1 + if i > #arg then + err("missing required argument to `%s'", a) + end + output = arg[i] + elseif (a == "-a") or (a == "--author") then + i = i + 1 + if i > #arg then + err("missing required argument to `%s'", a) + end + author = arg[i] + elseif (a == "-l") or (a == "--lang") then + i = i + 1 + if i > #arg then + err("missing required argument to `%s'", a) + end + lang = arg[i] + elseif a:sub(1, 1) ~= "-" then + table.insert(inputs, a) + else + err("unrecognized option `%s'", a) + end + i = i + 1 +end + +if #inputs == 0 then + err("no input files") +end + +local outfile = io.stdout + +local function printf(fmt, ...) + outfile:write(fmt:format(...)) +end + +if output then + local e + outfile, e = io.open(output, "w") + if not outfile then + err("error opening file for writing: %s", e) + end +end + +if author or lang then + outfile:write("\n") +end + +if lang then + printf("# Language: %s\n", lang) +end + +if author then + printf("# Author: %s\n", author) +end + +if author or lang then + outfile:write("\n") +end + +local escapes = { + ["\n"] = "\\n", + ["="] = "\\=", + ["\\"] = "\\\\", +} + +local function escape(s) + return s:gsub("[\\\n=]", escapes) +end + +local messages = { } + +for _, file in ipairs(inputs) do + local infile, e = io.open(file, "r") + if infile then + for line in infile:lines() do + for s in line:gmatch('S%("([^"]*)"') do + table.insert(messages, s) + end + end + infile:close() + else + io.stderr:write(("%s: WARNING: error opening file: %s\n"):format(me, e)) + end +end + +table.sort(messages) + +local last_msg + +for i, msg in ipairs(messages) do + if msg ~= last_msg then + printf("%s =\n", escape(msg)) + end + last_msg = msg +end + +if output then + outfile:close() +end + +--[[ +TESTS: +S("foo") S("bar") +S("bar") +S("foo") +]] diff --git a/mods/intllib/tools/updatetext.lua b/mods/intllib/tools/updatetext.lua new file mode 100644 index 00000000..00f9bf68 --- /dev/null +++ b/mods/intllib/tools/updatetext.lua @@ -0,0 +1,141 @@ +#! /usr/bin/env lua + +local basedir = "" +if arg[0]:find("[/\\]") then + basedir = arg[0]:gsub("(.*[/\\]).*$", "%1"):gsub("\\", "/") +end +if basedir == "" then basedir = "./" end + +-- Required by load_strings() +function string.trim(s) + return s:gsub("^%s*(.-)%s*$", "%1") +end + +dofile(basedir.."/../lib.lua") + +local me = arg[0]:gsub(".*[/\\](.*)$", "%1") + +local function err(fmt, ...) + io.stderr:write(("%s: %s\n"):format(me, fmt:format(...))) + os.exit(1) +end + +local template +local catalogs = { } + +local function usage() + print([[ +Usage: ]]..me..[[ [OPTIONS] TEMPLATE CATALOG... + +Update a catalog with new strings from a template. + +Available options: + -h,--help Show this help screen and exit. + -o,--output X Set output file (default: stdout). + +Messages in the template that are not on the catalog are added to the +catalog at the end. + +This tool also checks messages that are in the catalog but not in the +template, and reports such lines. It's up to the user to remove such +lines, if so desired. +]]) + os.exit(0) +end + +local i = 1 + +while i <= #arg do + local a = arg[i] + if (a == "-h") or (a == "--help") then + usage() + elseif (a == "-o") or (a == "--output") then + i = i + 1 + if i > #arg then + err("missing required argument to `%s'", a) + end + elseif (a == "-c") or (a == "--comment") then + old_msg_mode = "c" + elseif (a == "-d") or (a == "--delete") then + old_msg_mode = "d" + elseif a:sub(1, 1) ~= "-" then + if not template then + template = a + else + table.insert(catalogs, a) + end + else + err("unrecognized option `%s'", a) + end + i = i + 1 +end + +if not template then + err("no template specified") +elseif #catalogs == 0 then + err("no catalogs specified") +end + +local f, e = io.open(template, "r") +if not f then + err("error opening template: %s", e) +end + +local function printf(fmt, ...) + outfile:write(fmt:format(...)) +end + +local escapes = { ["\n"] = "\\n", ["="] = "\\=", ["\\"] = "\\\\", } +local function escape(s) + return s:gsub("[\\\n=]", escapes) +end + +if output then + local e + outfile, e = io.open(output, "w") + if not outfile then + err("error opening file for writing: %s", e) + end +end + +local function printf(fmt, ...) + io.stdout:write(fmt:format(...)) +end + +local template_msgs = intllib.load_strings(template) + +for _, file in ipairs(catalogs) do + print("Processing: "..file) + local catalog_msgs = intllib.load_strings(file) + local dirty_lines = { } + if catalog_msgs then + -- Add new entries from template. + for k in pairs(template_msgs) do + if not catalog_msgs[k] then + print("NEW: "..k) + table.insert(dirty_lines, escape(k).." =") + end + end + -- Check for old messages. + for k, v in pairs(catalog_msgs) do + if not template_msgs[k] then + print("OLD: "..k) + end + end + if #dirty_lines > 0 then + local outf, e = io.open(file, "a+") + if outf then + outf:write("\n") + for _, line in ipairs(dirty_lines) do + outf:write(line) + outf:write("\n") + end + outf:close() + else + io.stderr:write(("%s: WARNING: cannot write: %s\n"):format(me, e)) + end + end + else + io.stderr:write(("%s: WARNING: could not load catalog\n"):format(me)) + end +end