1
0
Fork 0
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:
y5nw 2024-10-13 11:29:08 +02:00 committed by GitHub
parent dbbe0ca065
commit e3aa79cffb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1360 additions and 74 deletions

View file

@ -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
-------