7.3 KiB
title | layout | root | idx | marked_text_encoding | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Translation (i18n / l10n) | default | ../.. | 8.05 |
|
Introduction
Adding support for translation to your mods and games allows more people to enjoy them. According to Google Play, 64% of Minetest Android users don't have English as their primary language. Minetest doesn't track stats for user languages across all platforms, but there's likely to be a high proportion of non-English speaking users.
Minetest allows you to translate your mods and games into different languages by writing your text in English, and using translation files to map into other languages. Translation is done on each player's client, allowing each player to see a different language.
- How does client-side translation work?
- Format strings
- Best practices and Common Falsehoods about Translation
- Server-side translations
- Conclusion
How does client-side translation work?
Marked up text
The server needs to tell clients how to translate text. This is done by placing control characters in text, telling Minetest where and how to translate text. This is referred to as marked up text, and will be discussed more later.
To mark text as translatable, use a translator function (S()
), obtained using
core.get_translator(textdomain)
:
local S = core.get_translator("mymod")
core.register_craftitem("mymod:item", {
description = S("My Item"),
})
The first argument of get_translator
is the textdomain
, which acts as a
namespace. Rather than having all translations for a language stored in the same
file, translations are separated into textdomains, with a file per textdomain
per language. The textdomain should be the same as the mod name, as it helps
avoid mod conflicts.
Marked up text can be used in most places where human-readable text is accepted,
including formspecs, item def fields, infotext, and more. When including marked
text in formspecs, you need to escape the text using core.formspec_escape
.
When the client encounters translatable text, such as that passed to
description
, it looks it up in the player's language's translation file. If a
translation cannot be found, it falls back to the English translation.
Translatable marked up text contains the English source text, the textdomain,
and any additional arguments passed to S()
. It's essentially a text encoding
of the S
call, containing all the required information.
Another type of marked up text is that returned by core.colorize
.
{% include notice.html notice=page.marked_text_encoding %}
Translation files
Translation files are media files that can be found in the locale
folder for
each mod. Currently, the only supported format is .tr
, but support for more
common formats is likely in the future. Translation files must be named
in the following way: [textdomain].[lang].tr
.
Files in the .tr
start with a comment specifying the textdomain, and then
further lines mapping from the English source text to the translation.
For example, mymod.fr.tr
:
# textdomain: mymod
Hello everyone!=Bonjour à tous !
I like grapefruit=J'aime le pamplemousse
You should create translation files based on your mod/game's source code,
using a tool like
update_translations.
This tool will look for S(
in your Lua code, and automatically create a
template that translators can use to translate into their language.
It also handles updating the translation files when your source changes.
You should always put literal text ("
) inside S rather than using a variable,
as it helps tools find translations.
Format strings
It's common to need to include variable information within a translation string. It's important that text isn't just concatenated, as that prevents translators from changing the order of variables within a sentence. Instead, you should use the translation system's format/arguments system:
core.register_on_joinplayer(function(player)
core.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
end)
If you want to include a literal @
in your translation, you'll need to escape
by writing @@
.
You should avoid concatenation within a sentence, but it's recommended that you join multiple sentences using concatenation. This helps translators by keeping strings smaller.
S("Hello @1!", player_name) .. " " .. S("You have @1 new messages.", #msgs)
Best practices and Common Falsehoods about Translation
- Avoid concatenating text and use format arguments instead. This gives translators full control over changing the order of things.
- Create translation files automatically, using update_translations.
- It's common for variables to change the surrounding text, for example, with gender and pluralisation. This is often hard to deal with, so is frequently glossed over or worked around with gender neutral phrasings.
- Translations may be much longer or much smaller than the English text. Make sure to leave plenty of space.
- Other languages may write numbers in a different way, for example, with commas
as decimal points.
1.000,23
,1'000'000,32
- Don't assume that other languages use capitalisation in the same way.
Server-side translations
Sometimes you need to know the translation of text on the server, for example,
to sort or search text. You can use get_player_information
to get a player's
language and get_translated_string
to translate marked text.
local list = {
S("Hello world!"),
S("Potato")
}
core.register_chatcommand("find", {
func = function(name, param)
local info = core.get_player_information(name)
local language = info and info.language or "en"
for _, line in ipairs(list) do
local trans = core.get_translated_string(language, line)
if trans:contains(query) then
return line
end
end
end,
})
Conclusion
The translation API allows making mods and games more accessible, but care is needed in order to use it correctly.
Minetest is continuously improving, and the translation API is likely to be extended in the future. For example, support for gettext translation files will allow common translator tools and platforms (like weblate) to be used, and there's likely to be support for pluralisation and gender added.