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