199 lines
7.3 KiB
Markdown
199 lines
7.3 KiB
Markdown
---
|
|
title: Translation (i18n / l10n)
|
|
layout: default
|
|
root: ../..
|
|
idx: 8.05
|
|
marked_text_encoding:
|
|
level: info
|
|
title: Marked Text Encoding
|
|
message: |
|
|
You don't need to know the exact format of marked text, but it might help
|
|
you understand.
|
|
|
|
```
|
|
"\27(T@mymod)Hello everyone!\27E"
|
|
```
|
|
|
|
* `\27` is the escape character - it's used to tell Minetest to pay attention as
|
|
something special is coming up. This is used for both translations and text
|
|
colorisation.
|
|
* `(T@mymod)` says that the following text is translatable using the `mymod`
|
|
textdomain.
|
|
* `Hello everyone!` is the translatable text in English, as passed to the
|
|
translator function.
|
|
* `\27E` is the escape character again and `E`, used to signal that the end has
|
|
been reached.
|
|
---
|
|
|
|
## Introduction <!-- omit in toc -->
|
|
|
|
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?](#how-does-client-side-translation-work)
|
|
- [Marked up text](#marked-up-text)
|
|
- [Translation files](#translation-files)
|
|
- [Format strings](#format-strings)
|
|
- [Best practices and Common Falsehoods about Translation](#best-practices-and-common-falsehoods-about-translation)
|
|
- [Server-side translations](#server-side-translations)
|
|
- [Conclusion](#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
|
|
`minetest.get_translator(textdomain)`:
|
|
|
|
```lua
|
|
local S = minetest.get_translator("mymod")
|
|
|
|
minetest.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 `minetest.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 `minetest.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](https://github.com/minetest-tools/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:
|
|
|
|
```lua
|
|
minetest.register_on_joinplayer(function(player)
|
|
minetest.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.
|
|
|
|
```lua
|
|
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](https://github.com/minetest-tools/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.
|
|
|
|
```lua
|
|
local list = {
|
|
S("Hello world!"),
|
|
S("Potato")
|
|
}
|
|
|
|
minetest.register_chatcommand("find", {
|
|
func = function(name, param)
|
|
local info = minetest.get_player_information(name)
|
|
local language = info and info.language or "en"
|
|
|
|
for _, line in ipairs(list) do
|
|
local trans = minetest.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.
|