mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Gettext and plural support for client-side translations (#14726)
--------- Co-authored-by: Ekdohibs <nathanael.courant@laposte.net> Co-authored-by: y5nw <y5nw@protonmail.com> Co-authored-by: rubenwardy <rw@rubenwardy.com>
This commit is contained in:
parent
dbbe0ca065
commit
e3aa79cffb
28 changed files with 1360 additions and 74 deletions
131
doc/lua_api.md
131
doc/lua_api.md
|
@ -4178,10 +4178,6 @@ Translations
|
|||
Texts can be translated client-side with the help of `minetest.translate` and
|
||||
translation files.
|
||||
|
||||
Consider using the script `mod_translation_updater.py` in the Minetest
|
||||
[modtools](https://github.com/minetest/modtools) repository to generate and
|
||||
update translation files automatically from the Lua sources.
|
||||
|
||||
Translating a string
|
||||
--------------------
|
||||
|
||||
|
@ -4189,13 +4185,15 @@ Two functions are provided to translate strings: `minetest.translate` and
|
|||
`minetest.get_translator`.
|
||||
|
||||
* `minetest.get_translator(textdomain)` is a simple wrapper around
|
||||
`minetest.translate`, and `minetest.get_translator(textdomain)(str, ...)` is
|
||||
equivalent to `minetest.translate(textdomain, str, ...)`.
|
||||
`minetest.translate` and `minetest.translate_n`.
|
||||
After `local S, NS = minetest.get_translator(textdomain)`, we have
|
||||
`S(str, ...)` equivalent to `minetest.translate(textdomain, str, ...)`, and
|
||||
`NS(str, str_plural, n, ...)` to `minetest.translate_n(textdomain, str, str_plural, n, ...)`.
|
||||
It is intended to be used in the following way, so that it avoids verbose
|
||||
repetitions of `minetest.translate`:
|
||||
|
||||
```lua
|
||||
local S = minetest.get_translator(textdomain)
|
||||
local S, NS = minetest.get_translator(textdomain)
|
||||
S(str, ...)
|
||||
```
|
||||
|
||||
|
@ -4212,29 +4210,102 @@ Two functions are provided to translate strings: `minetest.translate` and
|
|||
arguments the translated string expects.
|
||||
Arguments are literal strings -- they will not be translated.
|
||||
|
||||
For instance, suppose we want to greet players when they join. We can do the
|
||||
* `minetest.translate_n(textdomain, str, str_plural, n, ...)` translates the
|
||||
string `str` with the given `textdomain` for disambiguaion. The value of
|
||||
`n`, which must be a nonnegative integer, is used to decide whether to use
|
||||
the singular or the plural version of the string. Depending on the locale of
|
||||
the client, the choice between singular and plural might be more complicated,
|
||||
but the choice will be done automatically using the value of `n`.
|
||||
|
||||
You can read https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
|
||||
for more details on the differences of plurals between languages.
|
||||
|
||||
Also note that plurals are only handled in .po or .mo files, and not in .tr files.
|
||||
|
||||
For instance, suppose we want to greet players when they join and provide a
|
||||
command that shows the amount of time since the player joined. We can do the
|
||||
following:
|
||||
|
||||
```lua
|
||||
local S = minetest.get_translator("hello")
|
||||
local S, NS = minetest.get_translator("hello")
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
minetest.chat_send_player(name, S("Hello @1, how are you today?", name))
|
||||
end)
|
||||
minetest.register_chatcommand("playtime", {
|
||||
func = function(name)
|
||||
local last_login = core.get_auth_handler().get_auth(name).last_login
|
||||
local playtime = math.floor((last_login-os.time())/60)
|
||||
return true, NS(
|
||||
"You have been playing for @1 minute.",
|
||||
"You have been playing for @1 minutes.",
|
||||
minutes, tostring(minutes))
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
When someone called "CoolGuy" joins the game with an old client or a client
|
||||
that does not have localization enabled, they will see `Hello CoolGuy, how are
|
||||
you today?`
|
||||
you today?`. If they use the `/playtime` command, they will see `You have been
|
||||
playing for 1 minute` or (for example) `You have been playing for 4 minutes.`
|
||||
|
||||
However, if we have for instance a translation file named `hello.de.tr`
|
||||
However, if we have for instance a translation file named `hello.de.po`
|
||||
containing the following:
|
||||
|
||||
# textdomain: hello
|
||||
Hello @1, how are you today?=Hallo @1, wie geht es dir heute?
|
||||
```po
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Hello @1, how are you today?"
|
||||
msgstr "Hallo @1, wie geht es dir heute?"
|
||||
|
||||
msgid "You have been playing for @1 minute."
|
||||
msgid_plural "You have been playing for @1 minutes."
|
||||
msgstr[0] "Du spielst seit @1 Minute."
|
||||
msgstr[1] "Du spielst seit @1 Minuten."
|
||||
```
|
||||
|
||||
and CoolGuy has set a German locale, they will see `Hallo CoolGuy, wie geht es
|
||||
dir heute?`
|
||||
dir heute?` when they join, and the `/playtime` command will show them `Du
|
||||
spielst seit 1 Minute.` or (for example) `Du spielst seit 4 Minuten.`
|
||||
|
||||
Creating and updating translation files
|
||||
---------------------------------------
|
||||
|
||||
As an alternative to writing translation files by hand (as shown in the above
|
||||
example), it is also possible to generate translation files based on the source
|
||||
code.
|
||||
|
||||
It is recommended to first generate a translation template. The translation
|
||||
template includes translatable strings that translators can directly work on.
|
||||
After creating the `locale` directory, a translation template for the above
|
||||
example using the following command:
|
||||
|
||||
```sh
|
||||
xgettext -L lua -kS -kNS:1,2 -kminetest.translate:1c,2 -kminetest.translate_n:1c,2,3 \
|
||||
-d hello -o locale/hello.pot *.lua
|
||||
```
|
||||
|
||||
The above command can also be used to update the translation template when new
|
||||
translatable strings are added.
|
||||
|
||||
The German translator can then create the translation file with
|
||||
|
||||
```sh
|
||||
msginit -l de -i locale/hello.pot -o locale/hello.de.po
|
||||
```
|
||||
|
||||
and provide the translations by editing `locale/hello.de.po`.
|
||||
|
||||
The translation file can be updated using
|
||||
|
||||
```sh
|
||||
msgmerge -U locale/hello.de.po locale/hello.pot
|
||||
```
|
||||
|
||||
Refer to the [Gettext manual](https://www.gnu.org/software/gettext/manual/) for
|
||||
further information on creating and updating translation files.
|
||||
|
||||
Operations on translated strings
|
||||
--------------------------------
|
||||
|
@ -4248,8 +4319,8 @@ expected manner. However, string concatenation will still work as expected
|
|||
sentences by breaking them into parts; arguments should be used instead), and
|
||||
operations such as `minetest.colorize` which are also concatenation.
|
||||
|
||||
Translation file format
|
||||
-----------------------
|
||||
Old translation file format
|
||||
---------------------------
|
||||
|
||||
A translation file has the suffix `.[lang].tr`, where `[lang]` is the language
|
||||
it corresponds to. It must be put into the `locale` subdirectory of the mod.
|
||||
|
@ -4264,6 +4335,34 @@ The file should be a text file, with the following format:
|
|||
There must be no extraneous whitespace around the `=` or at the beginning or
|
||||
the end of the line.
|
||||
|
||||
Using the earlier example of greeting the player, the translation file would be
|
||||
|
||||
```
|
||||
# textdomain: hello
|
||||
Hello @1, how are you today?=Hallo @1, wie geht es dir heute?
|
||||
```
|
||||
|
||||
For old translation files, consider using the script `mod_translation_updater.py`
|
||||
in the Minetest [modtools](https://github.com/minetest/modtools) repository to
|
||||
generate and update translation files automatically from the Lua sources.
|
||||
|
||||
Gettext translation file format
|
||||
-------------------------------
|
||||
|
||||
Gettext files can also be used as translations. A translation file has the suffix
|
||||
`.[lang].po` or `.[lang].mo`, depending on whether it is compiled or not, and must
|
||||
also be placed in the `locale` subdirectory of the mod. The value of `textdomain`
|
||||
is `msgctxt` in the gettext files. If `msgctxt` is not provided, the name of the
|
||||
translation file is used instead.
|
||||
|
||||
A typical entry in a `.po` file would look like:
|
||||
|
||||
```po
|
||||
msgctxt "textdomain"
|
||||
msgid "Hello world!"
|
||||
msgstr "Bonjour le monde!"
|
||||
```
|
||||
|
||||
Escapes
|
||||
-------
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue