New upstream version 20180811
Julien Puydt
5 years ago
0 | ||
1 | read_globals = { | |
2 | "minetest", | |
3 | } | |
4 | ||
5 | globals = { | |
6 | "intllib", | |
7 | } | |
8 | ||
9 | files["intltest/init.lua"] = { | |
10 | ignore = { | |
11 | "212", -- Unused argument. | |
12 | }, | |
13 | } |
0 | ||
1 | This is free and unencumbered software released into the public domain. | |
2 | ||
3 | Anyone is free to copy, modify, publish, use, compile, sell, or | |
4 | distribute this software, either in source code form or as a compiled | |
5 | binary, for any purpose, commercial or non-commercial, and by any | |
6 | means. | |
7 | ||
8 | In jurisdictions that recognize copyright laws, the author or authors | |
9 | of this software dedicate any and all copyright interest in the | |
10 | software to the public domain. We make this dedication for the benefit | |
11 | of the public at large and to the detriment of our heirs and | |
12 | successors. We intend this dedication to be an overt act of | |
13 | relinquishment in perpetuity of all present and future rights to this | |
14 | software under copyright law. | |
15 | ||
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
22 | OTHER DEALINGS IN THE SOFTWARE. | |
23 | ||
24 | For more information, please refer to <http://unlicense.org/> |
0 | ||
1 | # Bilioteca de internacionalización para Minetest | |
2 | ||
3 | Por Diego Martínez (kaeza). | |
4 | Lanzada bajo Unlicense. Véase `LICENSE.md` para más detalles. | |
5 | ||
6 | Éste mod es un intento por proveer soporte para internacionalización | |
7 | de los mods (algo que a Minetest le falta de momento). | |
8 | ||
9 | Si tienes alguna duda/comentario, por favor publica en el | |
10 | [tema del foro][topic]. Por reporte de errores, use el | |
11 | [bugtracker][bugtracker] en Github. | |
12 | ||
13 | ## Cómo usar | |
14 | ||
15 | Si eres un jugador regular en busca de textos traducidos, simplemente | |
16 | [instala][installing_mods] éste mod como cualquier otro. | |
17 | ||
18 | El mod trata de detectar tu idioma, pero ya que no hay una forma portable de | |
19 | hacerlo, prueba varias alternativas: | |
20 | ||
21 | * `language` setting in `minetest.conf`. | |
22 | * `LANGUAGE` environment variable. | |
23 | * `LANG` environment variable. | |
24 | ||
25 | En cualquier caso, el resultado final debería ser el | |
26 | [Código de idioma ISO 639-1][ISO639-1] del idioma deseado. | |
27 | ||
28 | ### Desarrolladores | |
29 | ||
30 | Si desarrollas mods y estás buscando añadir soporte de internacionalización | |
31 | a tu mod, ve el fichero `doc/developer.md`. | |
32 | ||
33 | ### Traductores | |
34 | ||
35 | Si eres un traductor, ve el fichero `doc/translator.md`. | |
36 | ||
37 | [topic]: https://forum.minetest.net/viewtopic.php?id=4929 | |
38 | [bugtracker]: https://github.com/minetest-mods/intllib/issues | |
39 | [installing_mods]: https://wiki.minetest.net/Installing_mods/es | |
40 | [ISO639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes |
0 | ||
1 | # Libreria di internazionalizzazione per Minetest | |
2 | ||
3 | Di Diego Martínez (kaeza). | |
4 | Rilasciata sotto licenza Unlicense. Si veda `LICENSE.md` per i dettagli. | |
5 | ||
6 | Questo modulo è un tentativo per fornire il supporto di internazionalizzazione | |
7 | per i moduli (cosa che attualmente manca a Minetest). | |
8 | ||
9 | Se aveste qualunque commento o suggerimento, per piacere scriveteli nella | |
10 | [discussione sul forum][topic]. Per i rapporti sui bug, usate il | |
11 | [tracciatore di bug][bugtracker] su Github. | |
12 | ||
13 | ## Come usarla | |
14 | ||
15 | Se siete un* giocatrice/tore che vuole i testi tradotti, | |
16 | [installate][installing_mods] questo modulo come qualunque altro, | |
17 | poi abilitatelo tramite l'interfaccia grafica. | |
18 | ||
19 | Il modulo tenta di rilevare la vostra lingua, ma dato che al momento non c'è | |
20 | un metodo portabile per farlo, prova diverse alternative: | |
21 | ||
22 | * `language` impostazione in `minetest.conf`. | |
23 | * `LANGUAGE` variabile d'ambiente. | |
24 | * `LANG` variabile d'ambiente. | |
25 | * Se nessuna funziona, usa `en`. | |
26 | ||
27 | In ogni caso, il risultato finale dovrebbe essere il | |
28 | [codice di lingua ISO 639-1][ISO639-1] del linguaggio desiderato. | |
29 | ||
30 | ### Sviluppatrici/tori di moduli | |
31 | ||
32 | Se siete un* sviluppatrice/tore di moduli desideros* di aggiungere il supporto | |
33 | per l'internazionalizzazione al vostro modulo, leggete `doc/developer-it.md`. | |
34 | ||
35 | ### Traduttrici/tori | |
36 | ||
37 | Se siete un* traduttrice/tore, leggete `doc/translator-it.md`. | |
38 | ||
39 | [topic]: https://forum.minetest.net/viewtopic.php?id=4929 | |
40 | [bugtracker]: https://github.com/minetest-mods/intllib/issues | |
41 | [installing_mods]: https://wiki.minetest.net/Installing_mods | |
42 | [ISO639-1]: https://it.wikipedia.org/wiki/ISO_639-1 |
0 | ||
1 | # Pustaka Pengantarabangsaan untuk Minetest | |
2 | ||
3 | Oleh Diego Martínez (kaeza). | |
4 | Diterbitkan bawah Unlicense. Lihat `LICENSE.md` untuk maklumat lanjut. | |
5 | ||
6 | Mods ini ialah suatu usaha untuk menyediakan sokongan pengantarabangsaan | |
7 | kepada mods (sesuatu yang Minetest tiada ketika ini). | |
8 | ||
9 | Jika anda mempunyai sebarang komen/cadangan, sila tulis ke dalam [topik forum][topik]. | |
10 | Untuk melaporkan pepijat, sila gunakan [penjejak pepijat][pepijat] Github. | |
11 | ||
12 | ## Bagaimanakah cara untuk menggunakannya? | |
13 | ||
14 | Jika anda pemain biasa yang mencari teks terjemahan, hanya [pasangkan][pasang_mods] | |
15 | mods ini seperti mods lain, kemudian bolehkannya melalui GUI. | |
16 | ||
17 | Mods ini cuba untuk mengesan bahasa anda, tetapi oleh kerana tiada | |
18 | cara mudah alih untuk melakukannya, ia cuba beberapa cara yang lain: | |
19 | ||
20 | * Tetapan `language` di dalam fail `minetest.conf`. | |
21 | * Pembolehubah sekitaran `LANGUAGE`. | |
22 | * Pembolehubah sekitaran `LANG`. | |
23 | * Jika semua di atas gagal, ia gunakan `en`. | |
24 | ||
25 | Dalam apa jua keadaan, hasil akhirnya sepatutnya menjadi | |
26 | [Kod Bahasa ISO 639-1][ISO639-1] untuk bahasa yang dikehendaki. | |
27 | ||
28 | ### Pembangun mods | |
29 | ||
30 | Jika anda seorang pembangun mods yang ingin menambah sokongan | |
31 | pengantarabangsaan kepada mods anda, sila lihat `doc/developer.md`. | |
32 | ||
33 | ### Penterjemah | |
34 | ||
35 | Jika anda seorang penterjemah, sila lihat `doc/translator.md`. | |
36 | ||
37 | [topik]: https://forum.minetest.net/viewtopic.php?id=4929 | |
38 | [pepijat]: https://github.com/minetest-mods/intllib/issues | |
39 | [pasang_mods]: https://wiki.minetest.net/Installing_Mods/ms | |
40 | [ISO639-1]: https://ms.wikipedia.org/wiki/Senarai_kod_ISO_639-1 |
0 | # Lib de Internacionalização para Minetest | |
1 | ||
2 | Por Diego Martínez (kaeza). | |
3 | Lançado sob Unlicense. Veja `LICENSE.md` para detalhes. | |
4 | ||
5 | Este mod é uma tentativa de fornecer suporte de internacionalização para mods | |
6 | (algo que Minetest atualmente carece). | |
7 | ||
8 | ||
9 | Se você tiver algum comentário/sugestão, favor postar no | |
10 | [tópico do fórum][topico]. Para reportar bugs, use o | |
11 | [rastreador de bugs][bugtracker] no GitHub. | |
12 | ||
13 | ||
14 | ## Como usar | |
15 | ||
16 | Se você é um jogador regular procurando por textos traduzidos, | |
17 | basta instalar este mod como qualquer outro, e então habilite-lo na GUI. | |
18 | ||
19 | O mod tenta detectar o seu idioma, mas como não há atualmente nenhuma | |
20 | maneira portátil de fazer isso, ele tenta várias alternativas: | |
21 | ||
22 | Para usar este mod, basta [instalá-lo][instalando_mods] | |
23 | e habilita-lo na GUI. | |
24 | ||
25 | O modificador tenta detectar o idioma do usuário, mas já que não há atualmente | |
26 | nenhuma maneira portátil para fazer isso, ele tenta várias alternativas, e usa | |
27 | o primeiro encontrado: | |
28 | ||
29 | * `language` definido em `minetest.conf`. | |
30 | * Variável de ambiente `LANGUAGE`. | |
31 | * Variável de ambiente `LANG`. | |
32 | * Se todos falharem, usa `en` (inglês). | |
33 | ||
34 | Em todo caso, o resultado final deve ser um | |
35 | [Código de Idioma ISO 639-1][ISO639-1] do idioma desejado. | |
36 | ||
37 | ### Desenvolvedores de mods | |
38 | ||
39 | Se você é um desenvolvedor de mod procurando adicionar suporte de | |
40 | internacionalização ao seu mod, consulte `doc/developer.md`. | |
41 | ||
42 | ### Tradutores | |
43 | ||
44 | Se você é um tradutor, consulte `doc/translator.md`. | |
45 | ||
46 | [topico]: https://forum.minetest.net/viewtopic.php?id=4929 | |
47 | [bugtracker]: https://github.com/minetest-mods/intllib/issues | |
48 | [instalando_mods]: http://wiki.minetest.net/Installing_Mods/pt-br | |
49 | [ISO639-1]: https://pt.wikipedia.org/wiki/ISO_639 |
0 | ||
1 | # Internationalization Lib for Minetest | |
2 | ||
3 | By Diego Martínez (kaeza). | |
4 | Released under Unlicense. See `LICENSE.md` for details. | |
5 | ||
6 | This mod is an attempt at providing internationalization support for mods | |
7 | (something Minetest currently lacks). | |
8 | ||
9 | Should you have any comments/suggestions, please post them in the | |
10 | [forum topic][topic]. For bug reports, use the [bug tracker][bugtracker] | |
11 | on Github. | |
12 | ||
13 | ## How to use | |
14 | ||
15 | If you are a regular player looking for translated texts, just | |
16 | [install][installing_mods] this mod like any other one, then enable it | |
17 | in the GUI. | |
18 | ||
19 | The mod tries to detect your language, but since there's currently no | |
20 | portable way to do this, it tries several alternatives: | |
21 | ||
22 | * `language` setting in `minetest.conf`. | |
23 | * `LANGUAGE` environment variable. | |
24 | * `LANG` environment variable. | |
25 | * If all else fails, uses `en`. | |
26 | ||
27 | In any case, the end result should be the [ISO 639-1 Language Code][ISO639-1] | |
28 | of the desired language. | |
29 | ||
30 | ### Mod developers | |
31 | ||
32 | If you are a mod developer looking to add internationalization support to | |
33 | your mod, see `doc/developer.md`. | |
34 | ||
35 | ### Translators | |
36 | ||
37 | If you are a translator, see `doc/translator.md`. | |
38 | ||
39 | [topic]: https://forum.minetest.net/viewtopic.php?id=4929 | |
40 | [bugtracker]: https://github.com/minetest-mods/intllib/issues | |
41 | [installing_mods]: https://wiki.minetest.net/Installing_mods | |
42 | [ISO639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes |
0 | Internationalization library. | |
1 | This mod provides a way to internationalize/localize mods to other languages in an easy way. | |
2 | See the README file for details. |
0 | ||
1 | # Intllib - documentazione per sviluppatrici/tori | |
2 | ||
3 | ## Abilitare l'internazionalizzazione | |
4 | ||
5 | Per abilitare l'internazionalizzazione del vostro modulo, dovete copiare il file | |
6 | `lib/intllib.lua` nella cartella principale del vostro modulo, poi inserite | |
7 | questo codice standard nei file che necessitano la traduzione: | |
8 | ||
9 | -- Load support for intllib. | |
10 | local MP = minetest.get_modpath(minetest.get_current_modname()) | |
11 | local S, NS = dofile(MP.."/intllib.lua") | |
12 | ||
13 | Dovrete anche aggiungere la dipendenza facoltativa da intllib per il vostro | |
14 | modulo, per farlo aggiungete `intllib?` su una riga vuota nel vostro | |
15 | `depends.txt`. Si noti anche che se intllib non è installata, le funzioni di | |
16 | acquisizione del testo sono fatte in modo da restituire la stringa di testo | |
17 | originale. Questo è stato fatto in modo che non dobbiate spargere tonnellate | |
18 | di `if` (o costrutti simili) per controllare se la libreria è installata. | |
19 | ||
20 | Dopo avere messo il codice, dovete marcare le stringhe di testo che necessitano | |
21 | una traduzione. Per ciascuna stringa traducibile nei vostri codici sorgenti, | |
22 | usate la funzione `S` (si veda sopra) per restituire la stringa tradotta. | |
23 | Per esempio: | |
24 | ||
25 | minetest.register_node("miomod:mionodo", { | |
26 | -- Stringa semplice: | |
27 | description = S("Il mio fantastico nodo"), | |
28 | -- Stringa con inserti: | |
29 | description = S("Macchina @1", "Blu"), | |
30 | -- ... | |
31 | }) | |
32 | ||
33 | La funzione `NS` è l'equivalente di `ngettext`. Dovrebbe essere usata quando la | |
34 | stringa da tradurre ha forma singolare e plurale. Per esempio: | |
35 | ||
36 | -- Il primo `count` è per consentire a `ngettext` di stabilire quale forma | |
37 | -- usare. Il secondo `count` è per il sostituto effettivo. | |
38 | ||
39 | print(NS("Avete un oggetto.", "Avete @1 oggetti.", count, count)) | |
40 | ||
41 | ## Generare e aggiornare cataloghi | |
42 | ||
43 | Questo è il procedimento di base per lavorare con [gettext][gettext] | |
44 | ||
45 | Ogni volta che avete nuove stringhe da tradurre, dovreste fare quanto segue: | |
46 | ||
47 | cd /percorso/del/modulo | |
48 | /percorso/degli/strumenti/intllib/xgettext.sh file1.lua file2.lua ... | |
49 | ||
50 | Lo script creerà una cartella chiamata `locale` se non esiste già, e genererà | |
51 | il file `template.pot` (un modello con tutte le stringhe traducibili). Se avete | |
52 | già delle traduzioni, lo script provvederà al loro aggiornamento con le nuove | |
53 | stringhe. | |
54 | ||
55 | Lo script fornisce alcune opzioni al vero `xgettext` che dovrebbero essere | |
56 | sufficienti per la maggior parte dei casi. Se lo desiderate potete specificare | |
57 | altre opzioni: | |
58 | ||
59 | xgettext.sh -o file.pot --keyword=blaaaah:4,5 a.lua b.lua ... | |
60 | ||
61 | NOTA: C'è anche un file batch di Windows `xgettext.bat` per gli utenti di | |
62 | Windows, ma dovrete installare separatamente gli strumenti di gettext per la | |
63 | riga di comando. Si veda la parte superiore del file per la configurazione. | |
64 | ||
65 | [gettext]: https://www.gnu.org/software/gettext/ |
0 | ||
1 | # Intllib developer documentation | |
2 | ||
3 | ## Enabling internationalization | |
4 | ||
5 | In order to enable internationalization for your mod, you will need to copy the | |
6 | file `lib/intllib.lua` into the root directory of your mod, then include this | |
7 | boilerplate code in files needing localization: | |
8 | ||
9 | -- Load support for intllib. | |
10 | local MP = minetest.get_modpath(minetest.get_current_modname()) | |
11 | local S, NS = dofile(MP.."/intllib.lua") | |
12 | ||
13 | You will also need to optionally depend on intllib, to do so add `intllib?` | |
14 | to an empty line in your `depends.txt`. Also note that if intllib is not | |
15 | installed, the getter functions are defined so they return the string | |
16 | unchanged. This is done so you don't have to sprinkle tons of `if`s (or | |
17 | similar constructs) to check if the lib is actually installed. | |
18 | ||
19 | Once you have the code in place, you need to mark strings that need | |
20 | translation. For each translatable string in your sources, use the `S` | |
21 | function (see above) to return the translated string. For example: | |
22 | ||
23 | minetest.register_node("mymod:mynode", { | |
24 | -- Simple string: | |
25 | description = S("My Fabulous Node"), | |
26 | -- String with insertions: | |
27 | description = S("@1 Car", "Blue"), | |
28 | -- ... | |
29 | }) | |
30 | ||
31 | The `NS` function is the equivalent of `ngettext`. It should be used when the | |
32 | string to be translated has singular and plural forms. For example: | |
33 | ||
34 | -- The first `count` is for `ngettext` to determine which form to use. | |
35 | -- The second `count` is the actual replacement. | |
36 | print(NS("You have one item.", "You have @1 items.", count, count)) | |
37 | ||
38 | ## Generating and updating catalogs | |
39 | ||
40 | This is the basic workflow for working with [gettext][gettext] | |
41 | ||
42 | Each time you have new strings to be translated, you should do the following: | |
43 | ||
44 | cd /path/to/mod | |
45 | /path/to/intllib/tools/xgettext.sh file1.lua file2.lua ... | |
46 | ||
47 | The script will create a directory named `locale` if it doesn't exist yet, | |
48 | and will generate the file `template.pot` (a template with all the translatable | |
49 | strings). If you already have translations, the script will proceed to update | |
50 | all of them with the new strings. | |
51 | ||
52 | The script passes some options to the real `xgettext` that should be enough | |
53 | for most cases. You may specify other options if desired: | |
54 | ||
55 | xgettext.sh -o file.pot --keyword=blargh:4,5 a.lua b.lua ... | |
56 | ||
57 | NOTE: There's also a Windows batch file `xgettext.bat` for Windows users, | |
58 | but you will need to install the gettext command line tools separately. See | |
59 | the top of the file for configuration. | |
60 | ||
61 | [gettext]: https://www.gnu.org/software/gettext/ |
0 | ||
1 | # Formato del file di traduzione | |
2 | ||
3 | *Nota: Questo documento spiega il vecchio formato in stile conf/ini. | |
4 | La nuova interfaccia usa file [gettext][gettext] `.po`. | |
5 | Si veda [Il formato dei file PO][PO-Files] per ulteriori informazioni.* | |
6 | ||
7 | Questo è un esempio per un file di traduzione in Italiano (`it.txt`): | |
8 | ||
9 | # Un commento. | |
10 | # Un altro commento. | |
11 | Questa riga viene ignorata dato che non ha il segno di uguale. | |
12 | Hello, World! = Ciao, Mondo! | |
13 | String with\nnewlines = Stringa con\na capo | |
14 | String with an \= equals sign = Stringa con un segno di uguaglianza \= | |
15 | ||
16 | I file "locale" (o di traduzione) sono file di testo semplice formati da righe | |
17 | nel formato `testo originale = testo tradotto`. Il file deve stare nella | |
18 | sottocartella `locale` del modulo, e il suo nome deve essere lo stesso del | |
19 | [codice di lingua ISO 639-1][ISO639-1] della lingua che volete fornire. | |
20 | ||
21 | I file di traduzione dovrebbero usare la codifica UTF-8. | |
22 | ||
23 | Le righe che iniziano con un cancelletto sono commenti e vengono ignorate dal | |
24 | lettore. Si noti che i commenti si estendono solo fino al termine della riga; | |
25 | non c'è nessun supporto per i commenti multiriga. Le righe senza un segno di | |
26 | uguale sono anch'esse ignorate. | |
27 | ||
28 | I caratteri che sono considerati "speciali" possono essere "escaped" di modo | |
29 | che siano presi letteralmente. Inoltre esistono molte sequenze di escape che | |
30 | possono essere utilizzate: | |
31 | ||
32 | * Qualsiasi `#`, `=` può essere escaped di modo da essere preso letteralmente. | |
33 | La sequenza `\#` è utile se il vostro testo sorgente inizia con `#`. | |
34 | * Le sequenze di escape comuni `\n` e `\t`, significano rispettivamente | |
35 | newline (a capo) e tabulazione orizzontale. | |
36 | * La sequenza speciale di escape`\s` rappresenta il carattere di spazio. | |
37 | È utile principalmente per aggiungere spazi prefissi o suffissi ai testi | |
38 | originali o tradotti, perché altrimenti quegli spazi verrebbero rimossi. | |
39 | ||
40 | [gettext]: https://www.gnu.org/software/gettext | |
41 | [PO-Files]: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html | |
42 | [ISO639-1]: https://it.wikipedia.org/wiki/ISO_639-1 |
0 | ||
1 | # Locale file format | |
2 | ||
3 | *Note: This document explains the old conf/ini-like file format. | |
4 | The new interface uses [gettext][gettext] `.po` files. | |
5 | See [The Format of PO Files][PO-Files] for more information.* | |
6 | ||
7 | Here's an example for a Spanish locale file (`es.txt`): | |
8 | ||
9 | # A comment. | |
10 | # Another comment. | |
11 | This line is ignored since it has no equals sign. | |
12 | Hello, World! = Hola, Mundo! | |
13 | String with\nnewlines = Cadena con\nsaltos de linea | |
14 | String with an \= equals sign = Cadena con un signo de \= igualdad | |
15 | ||
16 | Locale (or translation) files are plain text files consisting of lines of the | |
17 | form `source text = translated text`. The file must reside in the mod's `locale` | |
18 | subdirectory, and must be named after the two-letter | |
19 | [ISO 639-1 Language Code][ISO639-1] of the language you want to support. | |
20 | ||
21 | The translation files should use the UTF-8 encoding. | |
22 | ||
23 | Lines beginning with a pound sign are comments and are effectively ignored | |
24 | by the reader. Note that comments only span until the end of the line; | |
25 | there's no support for multiline comments. Lines without an equals sign are | |
26 | also ignored. | |
27 | ||
28 | Characters that are considered "special" can be "escaped" so they are taken | |
29 | literally. There are also several escape sequences that can be used: | |
30 | ||
31 | * Any of `#`, `=` can be escaped to take them literally. The `\#` | |
32 | sequence is useful if your source text begins with `#`. | |
33 | * The common escape sequences `\n` and `\t`, meaning newline and | |
34 | horizontal tab respectively. | |
35 | * The special `\s` escape sequence represents the space character. It | |
36 | is mainly useful to add leading or trailing spaces to source or | |
37 | translated texts, as these spaces would be removed otherwise. | |
38 | ||
39 | [gettext]: https://www.gnu.org/software/gettext | |
40 | [PO-Files]: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html | |
41 | [ISO639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes |
0 | ||
1 | # Intllib - documentazione per traduttrici/tori | |
2 | ||
3 | #### Nuova interfaccia | |
4 | ||
5 | Usate i vostri strumenti preferiti per modificare i file `.po`. | |
6 | ||
7 | #### Vecchia interfaccia | |
8 | ||
9 | Per tradurre nella lingua che desiderate un modulo che supporta intllib, | |
10 | copiate il file `locale/template.txt` come `locale/LINGUA.txt` (dove `LINGUA` è | |
11 | il [codice di lingua ISO 639-1][ISO639-1] del vostro linguaggio. | |
12 | ||
13 | Aprite il nuovo file nel vostro editor preferito, e traducete ciascuna riga | |
14 | inserendo il testo tradotto dopo il segno di uguale. | |
15 | ||
16 | Si veda `localefile-it.md` per ulteriori informazioni sul formato del file. | |
17 | ||
18 | [gettext]: https://www.gnu.org/software/gettext/ | |
19 | [ISO639-1]: https://it.wikipedia.org/wiki/ISO_639-1 |
0 | ||
1 | # Intllib translator documentation | |
2 | ||
3 | #### New interface | |
4 | ||
5 | Use your favorite tools to edit the `.po` files. | |
6 | ||
7 | #### Old interface | |
8 | ||
9 | To translate an intllib-supporting mod to your desired language, copy the | |
10 | `locale/template.txt` file to `locale/LANGUAGE.txt` (where `LANGUAGE` is the | |
11 | [ISO 639-1 Language Code][ISO639-1] of your language. | |
12 | ||
13 | Open up the new file in your favorite editor, and translate each line putting | |
14 | the translated text after the equals sign. | |
15 | ||
16 | See `localefile.md` for more information about the file format. | |
17 | ||
18 | [gettext]: https://www.gnu.org/software/gettext/ | |
19 | [ISO639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes |
0 | ||
1 | local strsub, strrep = string.sub, string.rep | |
2 | local strmatch, strgsub = string.match, string.gsub | |
3 | ||
4 | local function trim(str) | |
5 | return strmatch(str, "^%s*(.-)%s*$") | |
6 | end | |
7 | ||
8 | local escapes = { n="\n", r="\r", t="\t" } | |
9 | ||
10 | local function unescape(str) | |
11 | return (strgsub(str, "(\\+)([nrt]?)", function(bs, c) | |
12 | local bsl = #bs | |
13 | local realbs = strrep("\\", bsl/2) | |
14 | if bsl%2 == 1 then | |
15 | c = escapes[c] or c | |
16 | end | |
17 | return realbs..c | |
18 | end)) | |
19 | end | |
20 | ||
21 | local function parse_po(str) | |
22 | local state, msgid, msgid_plural, msgstrind | |
23 | local texts = { } | |
24 | local lineno = 0 | |
25 | local function perror(msg) | |
26 | return error(msg.." at line "..lineno) | |
27 | end | |
28 | for _, line in ipairs(str:split("\n")) do repeat | |
29 | lineno = lineno + 1 | |
30 | line = trim(line) | |
31 | ||
32 | if line == "" or strmatch(line, "^#") then | |
33 | state, msgid, msgid_plural = nil, nil, nil | |
34 | break -- continue | |
35 | end | |
36 | ||
37 | local mid = strmatch(line, "^%s*msgid%s*\"(.*)\"%s*$") | |
38 | if mid then | |
39 | if state == "id" then | |
40 | return perror("unexpected msgid") | |
41 | end | |
42 | state, msgid = "id", unescape(mid) | |
43 | break -- continue | |
44 | end | |
45 | ||
46 | mid = strmatch(line, "^%s*msgid_plural%s*\"(.*)\"%s*$") | |
47 | if mid then | |
48 | if state ~= "id" then | |
49 | return perror("unexpected msgid_plural") | |
50 | end | |
51 | state, msgid_plural = "idp", unescape(mid) | |
52 | break -- continue | |
53 | end | |
54 | ||
55 | local ind, mstr = strmatch(line, | |
56 | "^%s*msgstr([0-9%[%]]*)%s*\"(.*)\"%s*$") | |
57 | if ind then | |
58 | if not msgid then | |
59 | return perror("missing msgid") | |
60 | elseif ind == "" then | |
61 | msgstrind = 0 | |
62 | elseif strmatch(ind, "%[[0-9]+%]") then | |
63 | msgstrind = tonumber(strsub(ind, 2, -2)) | |
64 | else | |
65 | return perror("malformed msgstr") | |
66 | end | |
67 | texts[msgid] = texts[msgid] or { } | |
68 | if msgid_plural then | |
69 | texts[msgid_plural] = texts[msgid] | |
70 | end | |
71 | texts[msgid][msgstrind] = unescape(mstr) | |
72 | state = "str" | |
73 | break -- continue | |
74 | end | |
75 | ||
76 | mstr = strmatch(line, "^%s*\"(.*)\"%s*$") | |
77 | if mstr then | |
78 | if state == "id" then | |
79 | msgid = msgid..unescape(mstr) | |
80 | break -- continue | |
81 | elseif state == "idp" then | |
82 | msgid_plural = msgid_plural..unescape(mstr) | |
83 | break -- continue | |
84 | elseif state == "str" then | |
85 | local text = texts[msgid][msgstrind] | |
86 | texts[msgid][msgstrind] = text..unescape(mstr) | |
87 | break -- continue | |
88 | end | |
89 | end | |
90 | ||
91 | return perror("malformed line") | |
92 | ||
93 | -- luacheck: ignore | |
94 | until true end -- end for | |
95 | ||
96 | return texts | |
97 | end | |
98 | ||
99 | local M = { } | |
100 | ||
101 | local function warn(msg) | |
102 | minetest.log("warning", "[intllib] "..msg) | |
103 | end | |
104 | ||
105 | -- hax! | |
106 | -- This function converts a C expression to an equivalent Lua expression. | |
107 | -- It handles enough stuff to parse the `Plural-Forms` header correctly. | |
108 | -- Note that it assumes the C expression is valid to begin with. | |
109 | local function compile_plural_forms(str) | |
110 | local plural = strmatch(str, "plural=([^;]+);?$") | |
111 | local function replace_ternary(s) | |
112 | local c, t, f = strmatch(s, "^(.-)%?(.-):(.*)") | |
113 | if c then | |
114 | return ("__if(" | |
115 | ..replace_ternary(c) | |
116 | ..","..replace_ternary(t) | |
117 | ..","..replace_ternary(f) | |
118 | ..")") | |
119 | end | |
120 | return s | |
121 | end | |
122 | plural = replace_ternary(plural) | |
123 | plural = strgsub(plural, "&&", " and ") | |
124 | plural = strgsub(plural, "||", " or ") | |
125 | plural = strgsub(plural, "!=", "~=") | |
126 | plural = strgsub(plural, "!", " not ") | |
127 | local f, err = loadstring([[ | |
128 | local function __if(c, t, f) | |
129 | if c and c~=0 then return t else return f end | |
130 | end | |
131 | local function __f(n) | |
132 | return (]]..plural..[[) | |
133 | end | |
134 | return (__f(...)) | |
135 | ]]) | |
136 | if not f then return nil, err end | |
137 | local env = { } | |
138 | env._ENV, env._G = env, env | |
139 | setfenv(f, env) | |
140 | return function(n) | |
141 | local v = f(n) | |
142 | if type(v) == "boolean" then | |
143 | -- Handle things like a plain `n != 1` | |
144 | v = v and 1 or 0 | |
145 | end | |
146 | return v | |
147 | end | |
148 | end | |
149 | ||
150 | local function parse_headers(str) | |
151 | local headers = { } | |
152 | for _, line in ipairs(str:split("\n")) do | |
153 | local k, v = strmatch(line, "^([^:]+):%s*(.*)") | |
154 | if k then | |
155 | headers[k] = v | |
156 | end | |
157 | end | |
158 | return headers | |
159 | end | |
160 | ||
161 | local function load_catalog(filename) | |
162 | local f, data, err | |
163 | ||
164 | local function bail(msg) | |
165 | warn(msg..(err and ": " or "")..(err or "")) | |
166 | return nil | |
167 | end | |
168 | ||
169 | f, err = io.open(filename, "rb") | |
170 | if not f then | |
171 | return --bail("failed to open catalog") | |
172 | end | |
173 | ||
174 | data, err = f:read("*a") | |
175 | ||
176 | f:close() | |
177 | ||
178 | if not data then | |
179 | return bail("failed to read catalog") | |
180 | end | |
181 | ||
182 | data, err = parse_po(data) | |
183 | if not data then | |
184 | return bail("failed to parse catalog") | |
185 | end | |
186 | ||
187 | err = nil | |
188 | local hdrs = data[""] | |
189 | if not (hdrs and hdrs[0]) then | |
190 | return bail("catalog has no headers") | |
191 | end | |
192 | ||
193 | hdrs = parse_headers(hdrs[0]) | |
194 | ||
195 | local pf = hdrs["Plural-Forms"] | |
196 | if not pf then | |
197 | -- XXX: Is this right? Gettext assumes this if header not present. | |
198 | pf = "nplurals=2; plural=n != 1" | |
199 | end | |
200 | ||
201 | data.plural_index, err = compile_plural_forms(pf) | |
202 | if not data.plural_index then | |
203 | return bail("failed to compile plural forms") | |
204 | end | |
205 | ||
206 | --warn("loaded: "..filename) | |
207 | ||
208 | return data | |
209 | end | |
210 | ||
211 | function M.load_catalogs(path) | |
212 | local langs = intllib.get_detected_languages() | |
213 | ||
214 | local cats = { } | |
215 | for _, lang in ipairs(langs) do | |
216 | local cat = load_catalog(path.."/"..lang..".po") | |
217 | if cat then | |
218 | cats[#cats+1] = cat | |
219 | end | |
220 | end | |
221 | ||
222 | return cats | |
223 | end | |
224 | ||
225 | return M |
0 | ||
1 | -- Old multi-load method compatibility | |
2 | if rawget(_G, "intllib") then return end | |
3 | ||
4 | intllib = { | |
5 | getters = {}, | |
6 | strings = {}, | |
7 | } | |
8 | ||
9 | ||
10 | local MP = minetest.get_modpath("intllib") | |
11 | ||
12 | dofile(MP.."/lib.lua") | |
13 | ||
14 | ||
15 | local LANG = minetest.settings:get("language") | |
16 | if not (LANG and (LANG ~= "")) then LANG = os.getenv("LANG") end | |
17 | if not (LANG and (LANG ~= "")) then LANG = "en" end | |
18 | ||
19 | ||
20 | local INS_CHAR = intllib.INSERTION_CHAR | |
21 | local insertion_pattern = "("..INS_CHAR.."?)"..INS_CHAR.."(%(?)(%d+)(%)?)" | |
22 | ||
23 | local function do_replacements(str, ...) | |
24 | local args = {...} | |
25 | -- Outer parens discard extra return values | |
26 | return (str:gsub(insertion_pattern, function(escape, open, num, close) | |
27 | if escape == "" then | |
28 | local replacement = tostring(args[tonumber(num)]) | |
29 | if open == "" then | |
30 | replacement = replacement..close | |
31 | end | |
32 | return replacement | |
33 | else | |
34 | return INS_CHAR..open..num..close | |
35 | end | |
36 | end)) | |
37 | end | |
38 | ||
39 | local function make_getter(msgstrs) | |
40 | return function(s, ...) | |
41 | local str | |
42 | if msgstrs then | |
43 | str = msgstrs[s] | |
44 | end | |
45 | if not str or str == "" then | |
46 | str = s | |
47 | end | |
48 | if select("#", ...) == 0 then | |
49 | return str | |
50 | end | |
51 | return do_replacements(str, ...) | |
52 | end | |
53 | end | |
54 | ||
55 | ||
56 | local function Getter(modname) | |
57 | modname = modname or minetest.get_current_modname() | |
58 | if not intllib.getters[modname] then | |
59 | local msgstr = intllib.get_strings(modname) | |
60 | intllib.getters[modname] = make_getter(msgstr) | |
61 | end | |
62 | return intllib.getters[modname] | |
63 | end | |
64 | ||
65 | ||
66 | function intllib.Getter(modname) | |
67 | local info = debug and debug.getinfo and debug.getinfo(2) | |
68 | local loc = info and info.short_src..":"..info.currentline | |
69 | minetest.log("deprecated", "intllib.Getter is deprecated." | |
70 | .." Please use intllib.make_gettext_pair instead." | |
71 | ..(info and " (called from "..loc..")" or "")) | |
72 | return Getter(modname) | |
73 | end | |
74 | ||
75 | ||
76 | local strfind, strsub = string.find, string.sub | |
77 | local langs | |
78 | ||
79 | local function split(str, sep) | |
80 | local pos, endp = 1, #str+1 | |
81 | return function() | |
82 | if (not pos) or pos > endp then return end | |
83 | local s, e = strfind(str, sep, pos, true) | |
84 | local part = strsub(str, pos, s and s-1) | |
85 | pos = e and e + 1 | |
86 | return part | |
87 | end | |
88 | end | |
89 | ||
90 | function intllib.get_detected_languages() | |
91 | if langs then return langs end | |
92 | ||
93 | langs = { } | |
94 | ||
95 | local function addlang(l) | |
96 | local sep | |
97 | langs[#langs+1] = l | |
98 | sep = strfind(l, ".", 1, true) | |
99 | if sep then | |
100 | l = strsub(l, 1, sep-1) | |
101 | langs[#langs+1] = l | |
102 | end | |
103 | sep = strfind(l, "_", 1, true) | |
104 | if sep then | |
105 | langs[#langs+1] = strsub(l, 1, sep-1) | |
106 | end | |
107 | end | |
108 | ||
109 | local v | |
110 | ||
111 | v = minetest.settings:get("language") | |
112 | if v and v~="" then | |
113 | addlang(v) | |
114 | end | |
115 | ||
116 | v = os.getenv("LANGUAGE") | |
117 | if v then | |
118 | for item in split(v, ":") do | |
119 | langs[#langs+1] = item | |
120 | end | |
121 | end | |
122 | ||
123 | v = os.getenv("LANG") | |
124 | if v then | |
125 | addlang(v) | |
126 | end | |
127 | ||
128 | langs[#langs+1] = "en" | |
129 | ||
130 | return langs | |
131 | end | |
132 | ||
133 | ||
134 | local gettext = dofile(minetest.get_modpath("intllib").."/gettext.lua") | |
135 | ||
136 | ||
137 | local function catgettext(catalogs, msgid) | |
138 | for _, cat in ipairs(catalogs) do | |
139 | local msgstr = cat and cat[msgid] | |
140 | if msgstr and msgstr~="" then | |
141 | local msg = msgstr[0] | |
142 | return msg~="" and msg or nil | |
143 | end | |
144 | end | |
145 | end | |
146 | ||
147 | local function catngettext(catalogs, msgid, msgid_plural, n) | |
148 | n = math.floor(n) | |
149 | for _, cat in ipairs(catalogs) do | |
150 | local msgstr = cat and cat[msgid] | |
151 | if msgstr then | |
152 | local index = cat.plural_index(n) | |
153 | local msg = msgstr[index] | |
154 | return msg~="" and msg or nil | |
155 | end | |
156 | end | |
157 | return n==1 and msgid or msgid_plural | |
158 | end | |
159 | ||
160 | ||
161 | local gettext_getters = { } | |
162 | function intllib.make_gettext_pair(modname) | |
163 | modname = modname or minetest.get_current_modname() | |
164 | if gettext_getters[modname] then | |
165 | return unpack(gettext_getters[modname]) | |
166 | end | |
167 | local localedir = minetest.get_modpath(modname).."/locale" | |
168 | local catalogs = gettext.load_catalogs(localedir) | |
169 | local getter = Getter(modname) | |
170 | local function gettext_func(msgid, ...) | |
171 | local msgstr = (catgettext(catalogs, msgid) | |
172 | or getter(msgid)) | |
173 | return do_replacements(msgstr, ...) | |
174 | end | |
175 | local function ngettext_func(msgid, msgid_plural, n, ...) | |
176 | local msgstr = (catngettext(catalogs, msgid, msgid_plural, n) | |
177 | or getter(msgid)) | |
178 | return do_replacements(msgstr, ...) | |
179 | end | |
180 | gettext_getters[modname] = { gettext_func, ngettext_func } | |
181 | return gettext_func, ngettext_func | |
182 | end | |
183 | ||
184 | ||
185 | local function get_locales(code) | |
186 | local ll, cc = code:match("^(..)_(..)") | |
187 | if ll then | |
188 | return { ll.."_"..cc, ll, ll~="en" and "en" or nil } | |
189 | else | |
190 | return { code, code~="en" and "en" or nil } | |
191 | end | |
192 | end | |
193 | ||
194 | ||
195 | function intllib.get_strings(modname, langcode) | |
196 | langcode = langcode or LANG | |
197 | modname = modname or minetest.get_current_modname() | |
198 | local msgstr = intllib.strings[modname] | |
199 | if not msgstr then | |
200 | local modpath = minetest.get_modpath(modname) | |
201 | msgstr = { } | |
202 | for _, l in ipairs(get_locales(langcode)) do | |
203 | local t = intllib.load_strings(modpath.."/locale/"..l..".txt") or { } | |
204 | for k, v in pairs(t) do | |
205 | msgstr[k] = msgstr[k] or v | |
206 | end | |
207 | end | |
208 | intllib.strings[modname] = msgstr | |
209 | end | |
210 | return msgstr | |
211 | end | |
212 |
0 | ||
1 | # Intllib example | |
2 | ||
3 | This is a simple mod showing how to use intllib. | |
4 | ||
5 | It defines a test `intltest:test` item whose description is translated | |
6 | according to the user's language. | |
7 | ||
8 | Additionally, it demonstrates how to use plural forms by counting the | |
9 | number of times the item has been used. |
0 | intllib? |
0 | ||
1 | -- Load support for intllib. | |
2 | local MP = minetest.get_modpath(minetest.get_current_modname()) | |
3 | local S, NS = dofile(MP.."/intllib.lua") | |
4 | ||
5 | local use_count = 0 | |
6 | ||
7 | minetest.log("action", S("Hello, world!")) | |
8 | ||
9 | minetest.register_craftitem("intltest:test", { | |
10 | -- Example use of replacements. | |
11 | -- Translators: @1 is color, @2 is object. | |
12 | description = S("Test: @1 @2", S("Blue"), S("Car")), | |
13 | ||
14 | inventory_image = "default_sand.png", | |
15 | ||
16 | on_use = function(stack, user, pt) | |
17 | use_count = use_count + 1 | |
18 | -- Example use of `ngettext` function. | |
19 | -- First `use_count` is `n` for ngettext; | |
20 | -- Second one is actual replacement. | |
21 | -- Translators: @1 is use count. | |
22 | local message = NS("Item has been used @1 time.", | |
23 | "Item has been used @1 times.", | |
24 | use_count, use_count) | |
25 | minetest.chat_send_player(user:get_player_name(), message) | |
26 | end, | |
27 | }) |
0 | ||
1 | -- This file should be replaced by `intllib/lib/intllib.lua`. | |
2 | return dofile(minetest.get_modpath("intllib").."/lib/intllib.lua") |
0 | # I18N Test Mod. | |
1 | # Copyright (C) 2013-2017 Diego Martínez <kaeza@users.sf.net> | |
2 | # This file is distributed under the same license as the intllib mod. | |
3 | # Diego Martínez <kaeza@users.sf.net>, 2013-2017. | |
4 | # | |
5 | msgid "" | |
6 | msgstr "" | |
7 | "Project-Id-Version: I18N Test Mod 0.1.0\n" | |
8 | "Report-Msgid-Bugs-To: \n" | |
9 | "POT-Creation-Date: 2017-02-25 20:40-0300\n" | |
10 | "PO-Revision-Date: 2017-01-23 17:36-0300\n" | |
11 | "Last-Translator: Diego Martnez <kaeza@users.sf.net>\n" | |
12 | "Language-Team: Spanish\n" | |
13 | "Language: es\n" | |
14 | "MIME-Version: 1.0\n" | |
15 | "Content-Type: text/plain; charset=UTF-8\n" | |
16 | "Content-Transfer-Encoding: 8bit\n" | |
17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | |
18 | ||
19 | #: init.lua | |
20 | msgid "Hello, world!" | |
21 | msgstr "¡Hola, mundo!" | |
22 | ||
23 | #. Translators: @1 is color, @2 is object. | |
24 | #: init.lua | |
25 | msgid "Blue" | |
26 | msgstr "Azul" | |
27 | ||
28 | #: init.lua | |
29 | msgid "Car" | |
30 | msgstr "Carro" | |
31 | ||
32 | #. Translators: @1 is color, @2 is object. | |
33 | #: init.lua | |
34 | msgid "Test: @1 @2" | |
35 | msgstr "Prueba: @2 @1" | |
36 | ||
37 | #. Translators: @1 is use count. | |
38 | #: init.lua | |
39 | msgid "Item has been used @1 time." | |
40 | msgid_plural "Item has been used @1 times." | |
41 | msgstr[0] "El objeto ha sido usado @1 vez." | |
42 | msgstr[1] "El objeto ha sido usado @1 veces." |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |
2 | # This file is distributed under the same license as the PACKAGE package. | |
3 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |
4 | # | |
5 | #, fuzzy | |
6 | msgid "" | |
7 | msgstr "" | |
8 | "Project-Id-Version: PACKAGE VERSION\n" | |
9 | "Report-Msgid-Bugs-To: \n" | |
10 | "POT-Creation-Date: 2017-02-25 20:40-0300\n" | |
11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | |
12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |
13 | "Language-Team: LANGUAGE <LL@li.org>\n" | |
14 | "Language: \n" | |
15 | "MIME-Version: 1.0\n" | |
16 | "Content-Type: text/plain; charset=CHARSET\n" | |
17 | "Content-Transfer-Encoding: 8bit\n" | |
18 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" | |
19 | ||
20 | #: init.lua | |
21 | msgid "Hello, world!" | |
22 | msgstr "" | |
23 | ||
24 | #. Translators: @1 is color, @2 is object. | |
25 | #: init.lua | |
26 | msgid "Blue" | |
27 | msgstr "" | |
28 | ||
29 | #: init.lua | |
30 | msgid "Car" | |
31 | msgstr "" | |
32 | ||
33 | #. Translators: @1 is color, @2 is object. | |
34 | #: init.lua | |
35 | msgid "Test: @1 @2" | |
36 | msgstr "" | |
37 | ||
38 | #. Translators: @1 is use count. | |
39 | #: init.lua | |
40 | msgid "Item has been used @1 time." | |
41 | msgid_plural "Item has been used @1 times." | |
42 | msgstr[0] "" | |
43 | msgstr[1] "" |
0 | ||
1 | -- Fallback functions for when `intllib` is not installed. | |
2 | -- Code released under Unlicense <http://unlicense.org>. | |
3 | ||
4 | -- Get the latest version of this file at: | |
5 | -- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua | |
6 | ||
7 | local function format(str, ...) | |
8 | local args = { ... } | |
9 | local function repl(escape, open, num, close) | |
10 | if escape == "" then | |
11 | local replacement = tostring(args[tonumber(num)]) | |
12 | if open == "" then | |
13 | replacement = replacement..close | |
14 | end | |
15 | return replacement | |
16 | else | |
17 | return "@"..open..num..close | |
18 | end | |
19 | end | |
20 | return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) | |
21 | end | |
22 | ||
23 | local gettext, ngettext | |
24 | if minetest.get_modpath("intllib") then | |
25 | if intllib.make_gettext_pair then | |
26 | -- New method using gettext. | |
27 | gettext, ngettext = intllib.make_gettext_pair() | |
28 | else | |
29 | -- Old method using text files. | |
30 | gettext = intllib.Getter() | |
31 | end | |
32 | end | |
33 | ||
34 | -- Fill in missing functions. | |
35 | ||
36 | gettext = gettext or function(msgid, ...) | |
37 | return format(msgid, ...) | |
38 | end | |
39 | ||
40 | ngettext = ngettext or function(msgid, msgid_plural, n, ...) | |
41 | return format(n==1 and msgid or msgid_plural, ...) | |
42 | end | |
43 | ||
44 | return gettext, ngettext |
0 | ||
1 | intllib = intllib or {} | |
2 | ||
3 | local INS_CHAR = "@" | |
4 | intllib.INSERTION_CHAR = INS_CHAR | |
5 | ||
6 | local escapes = { | |
7 | ["\\"] = "\\", | |
8 | ["n"] = "\n", | |
9 | ["s"] = " ", | |
10 | ["t"] = "\t", | |
11 | ["r"] = "\r", | |
12 | ["f"] = "\f", | |
13 | [INS_CHAR] = INS_CHAR..INS_CHAR, | |
14 | } | |
15 | ||
16 | local function unescape(str) | |
17 | local parts = {} | |
18 | local n = 1 | |
19 | local function add(s) | |
20 | parts[n] = s | |
21 | n = n + 1 | |
22 | end | |
23 | ||
24 | local start = 1 | |
25 | while true do | |
26 | local pos = str:find("\\", start, true) | |
27 | if pos then | |
28 | add(str:sub(start, pos - 1)) | |
29 | else | |
30 | add(str:sub(start)) | |
31 | break | |
32 | end | |
33 | local c = str:sub(pos + 1, pos + 1) | |
34 | add(escapes[c] or c) | |
35 | start = pos + 2 | |
36 | end | |
37 | return table.concat(parts) | |
38 | end | |
39 | ||
40 | local function find_eq(s) | |
41 | for slashes, pos in s:gmatch("([\\]*)=()") do | |
42 | if (slashes:len() % 2) == 0 then | |
43 | return pos - 1 | |
44 | end | |
45 | end | |
46 | end | |
47 | ||
48 | function intllib.load_strings(filename) | |
49 | local file, err = io.open(filename, "r") | |
50 | if not file then | |
51 | return nil, err | |
52 | end | |
53 | local strings = {} | |
54 | for line in file:lines() do | |
55 | line = line:trim() | |
56 | if line ~= "" and line:sub(1, 1) ~= "#" then | |
57 | local pos = find_eq(line) | |
58 | if pos then | |
59 | local msgid = unescape(line:sub(1, pos - 1):trim()) | |
60 | strings[msgid] = unescape(line:sub(pos + 1):trim()) | |
61 | end | |
62 | end | |
63 | end | |
64 | file:close() | |
65 | return strings | |
66 | end |
0 | #! /usr/bin/env lua | |
1 | ||
2 | local me = arg[0]:gsub(".*[/\\](.*)$", "%1") | |
3 | ||
4 | local function err(fmt, ...) | |
5 | io.stderr:write(("%s: %s\n"):format(me, fmt:format(...))) | |
6 | os.exit(1) | |
7 | end | |
8 | ||
9 | local output | |
10 | local inputs = { } | |
11 | local lang | |
12 | local author | |
13 | ||
14 | local i = 1 | |
15 | ||
16 | local function usage() | |
17 | print([[ | |
18 | Usage: ]]..me..[[ [OPTIONS] FILE... | |
19 | ||
20 | Extract translatable strings from the given FILE(s). | |
21 | ||
22 | Available options: | |
23 | -h,--help Show this help screen and exit. | |
24 | -o,--output X Set output file (default: stdout). | |
25 | -a,--author X Set author. | |
26 | -l,--lang X Set language name. | |
27 | ]]) | |
28 | os.exit(0) | |
29 | end | |
30 | ||
31 | while i <= #arg do | |
32 | local a = arg[i] | |
33 | if (a == "-h") or (a == "--help") then | |
34 | usage() | |
35 | elseif (a == "-o") or (a == "--output") then | |
36 | i = i + 1 | |
37 | if i > #arg then | |
38 | err("missing required argument to `%s'", a) | |
39 | end | |
40 | output = arg[i] | |
41 | elseif (a == "-a") or (a == "--author") then | |
42 | i = i + 1 | |
43 | if i > #arg then | |
44 | err("missing required argument to `%s'", a) | |
45 | end | |
46 | author = arg[i] | |
47 | elseif (a == "-l") or (a == "--lang") then | |
48 | i = i + 1 | |
49 | if i > #arg then | |
50 | err("missing required argument to `%s'", a) | |
51 | end | |
52 | lang = arg[i] | |
53 | elseif a:sub(1, 1) ~= "-" then | |
54 | table.insert(inputs, a) | |
55 | else | |
56 | err("unrecognized option `%s'", a) | |
57 | end | |
58 | i = i + 1 | |
59 | end | |
60 | ||
61 | if #inputs == 0 then | |
62 | err("no input files") | |
63 | end | |
64 | ||
65 | local outfile = io.stdout | |
66 | ||
67 | local function printf(fmt, ...) | |
68 | outfile:write(fmt:format(...)) | |
69 | end | |
70 | ||
71 | if output then | |
72 | local e | |
73 | outfile, e = io.open(output, "w") | |
74 | if not outfile then | |
75 | err("error opening file for writing: %s", e) | |
76 | end | |
77 | end | |
78 | ||
79 | if author or lang then | |
80 | outfile:write("\n") | |
81 | end | |
82 | ||
83 | if lang then | |
84 | printf("# Language: %s\n", lang) | |
85 | end | |
86 | ||
87 | if author then | |
88 | printf("# Author: %s\n", author) | |
89 | end | |
90 | ||
91 | if author or lang then | |
92 | outfile:write("\n") | |
93 | end | |
94 | ||
95 | local escapes = { | |
96 | ["\n"] = "\\n", | |
97 | ["="] = "\\=", | |
98 | ["\\"] = "\\\\", | |
99 | } | |
100 | ||
101 | local function escape(s) | |
102 | return s:gsub("[\\\n=]", escapes) | |
103 | end | |
104 | ||
105 | local messages = { } | |
106 | ||
107 | for _, file in ipairs(inputs) do | |
108 | local infile, e = io.open(file, "r") | |
109 | if infile then | |
110 | for line in infile:lines() do | |
111 | for s in line:gmatch('S%("([^"]*)"') do | |
112 | table.insert(messages, s) | |
113 | end | |
114 | end | |
115 | infile:close() | |
116 | else | |
117 | io.stderr:write(("%s: WARNING: error opening file: %s\n"):format(me, e)) | |
118 | end | |
119 | end | |
120 | ||
121 | table.sort(messages) | |
122 | ||
123 | local last_msg | |
124 | ||
125 | for _, msg in ipairs(messages) do | |
126 | if msg ~= last_msg then | |
127 | printf("%s =\n", escape(msg)) | |
128 | end | |
129 | last_msg = msg | |
130 | end | |
131 | ||
132 | if output then | |
133 | outfile:close() | |
134 | end | |
135 | ||
136 | --[[ | |
137 | TESTS: | |
138 | S("foo") S("bar") | |
139 | S("bar") | |
140 | S("foo") | |
141 | ]] |
0 | #! /usr/bin/env lua | |
1 | ||
2 | local basedir = "" | |
3 | if arg[0]:find("[/\\]") then | |
4 | basedir = arg[0]:gsub("(.*[/\\]).*$", "%1"):gsub("\\", "/") | |
5 | end | |
6 | if basedir == "" then basedir = "./" end | |
7 | ||
8 | -- Required by load_strings() | |
9 | function string.trim(s) -- luacheck: ignore | |
10 | return s:gsub("^%s*(.-)%s*$", "%1") | |
11 | end | |
12 | ||
13 | dofile(basedir.."/../lib.lua") | |
14 | ||
15 | local me = arg[0]:gsub(".*[/\\](.*)$", "%1") | |
16 | ||
17 | local function err(fmt, ...) | |
18 | io.stderr:write(("%s: %s\n"):format(me, fmt:format(...))) | |
19 | os.exit(1) | |
20 | end | |
21 | ||
22 | local output, outfile, template | |
23 | local catalogs = { } | |
24 | ||
25 | local function usage() | |
26 | print([[ | |
27 | Usage: ]]..me..[[ [OPTIONS] TEMPLATE CATALOG... | |
28 | ||
29 | Update a catalog with new strings from a template. | |
30 | ||
31 | Available options: | |
32 | -h,--help Show this help screen and exit. | |
33 | -o,--output X Set output file (default: stdout). | |
34 | ||
35 | Messages in the template that are not on the catalog are added to the | |
36 | catalog at the end. | |
37 | ||
38 | This tool also checks messages that are in the catalog but not in the | |
39 | template, and reports such lines. It's up to the user to remove such | |
40 | lines, if so desired. | |
41 | ]]) | |
42 | os.exit(0) | |
43 | end | |
44 | ||
45 | local i = 1 | |
46 | ||
47 | while i <= #arg do | |
48 | local a = arg[i] | |
49 | if (a == "-h") or (a == "--help") then | |
50 | usage() | |
51 | elseif (a == "-o") or (a == "--output") then | |
52 | i = i + 1 | |
53 | if i > #arg then | |
54 | err("missing required argument to `%s'", a) | |
55 | end | |
56 | output = arg[i] | |
57 | elseif a:sub(1, 1) ~= "-" then | |
58 | if not template then | |
59 | template = a | |
60 | else | |
61 | table.insert(catalogs, a) | |
62 | end | |
63 | else | |
64 | err("unrecognized option `%s'", a) | |
65 | end | |
66 | i = i + 1 | |
67 | end | |
68 | ||
69 | if not template then | |
70 | err("no template specified") | |
71 | elseif #catalogs == 0 then | |
72 | err("no catalogs specified") | |
73 | end | |
74 | ||
75 | local f, e = io.open(template, "r") | |
76 | if not f then | |
77 | err("error opening template: %s", e) | |
78 | end | |
79 | ||
80 | local escapes = { ["\n"] = "\\n", ["="] = "\\=", ["\\"] = "\\\\", } | |
81 | local function escape(s) | |
82 | return s:gsub("[\\\n=]", escapes) | |
83 | end | |
84 | ||
85 | if output then | |
86 | outfile, e = io.open(output, "w") | |
87 | if not outfile then | |
88 | err("error opening file for writing: %s", e) | |
89 | end | |
90 | end | |
91 | ||
92 | local template_msgs = intllib.load_strings(template) | |
93 | ||
94 | for _, file in ipairs(catalogs) do | |
95 | print("Processing: "..file) | |
96 | local catalog_msgs = intllib.load_strings(file) | |
97 | local dirty_lines = { } | |
98 | if catalog_msgs then | |
99 | -- Add new entries from template. | |
100 | for k in pairs(template_msgs) do | |
101 | if not catalog_msgs[k] then | |
102 | print("NEW: "..k) | |
103 | table.insert(dirty_lines, escape(k).." =") | |
104 | end | |
105 | end | |
106 | -- Check for old messages. | |
107 | for k, v in pairs(catalog_msgs) do | |
108 | if not template_msgs[k] then | |
109 | print("OLD: "..k) | |
110 | table.insert(dirty_lines, "OLD: "..escape(k).." = "..escape(v)) | |
111 | end | |
112 | end | |
113 | if #dirty_lines > 0 then | |
114 | local outf | |
115 | outf, e = io.open(file, "a+") | |
116 | if outf then | |
117 | outf:write("\n") | |
118 | for _, line in ipairs(dirty_lines) do | |
119 | outf:write(line) | |
120 | outf:write("\n") | |
121 | end | |
122 | outf:close() | |
123 | else | |
124 | io.stderr:write(("%s: WARNING: cannot write: %s\n"):format(me, e)) | |
125 | end | |
126 | end | |
127 | else | |
128 | io.stderr:write(("%s: WARNING: could not load catalog\n"):format(me)) | |
129 | end | |
130 | end |
0 | @echo off | |
1 | setlocal | |
2 | ||
3 | set me=%~n0 | |
4 | ||
5 | rem # Uncomment the following line if gettext is not in your PATH. | |
6 | rem # Value must be absolute and end in a backslash. | |
7 | rem set gtprefix=C:\path\to\gettext\bin\ | |
8 | ||
9 | if "%1" == "" ( | |
10 | echo Usage: %me% FILE... 1>&2 | |
11 | exit 1 | |
12 | ) | |
13 | ||
14 | set xgettext=%gtprefix%xgettext.exe | |
15 | set msgmerge=%gtprefix%msgmerge.exe | |
16 | ||
17 | md locale > nul 2>&1 | |
18 | echo Generating template... 1>&2 | |
19 | echo %xgettext% --from-code=UTF-8 -kS -kNS:1,2 -k_ -o locale/template.pot %* | |
20 | %xgettext% --from-code=UTF-8 -kS -kNS:1,2 -k_ -o locale/template.pot %* | |
21 | if %ERRORLEVEL% neq 0 goto done | |
22 | ||
23 | cd locale | |
24 | ||
25 | for %%f in (*.po) do ( | |
26 | echo Updating %%f... 1>&2 | |
27 | %msgmerge% --update %%f template.pot | |
28 | ) | |
29 | ||
30 | echo DONE! 1>&2 | |
31 | ||
32 | :done |
0 | #! /bin/bash | |
1 | ||
2 | me=$(basename "${BASH_SOURCE[0]}"); | |
3 | ||
4 | if [[ $# -lt 1 ]]; then | |
5 | echo "Usage: $me FILE..." >&2; | |
6 | exit 1; | |
7 | fi | |
8 | ||
9 | mkdir -p locale; | |
10 | echo "Generating template..." >&2; | |
11 | xgettext --from-code=UTF-8 \ | |
12 | --keyword=S \ | |
13 | --keyword=NS:1,2 \ | |
14 | --keyword=N_ \ | |
15 | --add-comments='Translators:' \ | |
16 | --add-location=file \ | |
17 | -o locale/template.pot \ | |
18 | "$@" \ | |
19 | || exit; | |
20 | ||
21 | find locale -name '*.po' -type f | while read -r file; do | |
22 | echo "Updating $file..." >&2; | |
23 | msgmerge --update "$file" locale/template.pot; | |
24 | done | |
25 | ||
26 | echo "DONE!" >&2; |