Translation: Add chapter
This commit is contained in:
parent
a5b822122f
commit
8163b0cb37
191
_en/quality/translations.md
Normal file
191
_en/quality/translations.md
Normal file
@ -0,0 +1,191 @@
|
||||
---
|
||||
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 text](#marked-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 text
|
||||
|
||||
The server needs to tell clients how to translate text. This is done by marking
|
||||
text as translatable using a translator function (`S()`), returned by
|
||||
`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 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 marked 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.
|
||||
|
||||
Marked 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.
|
||||
|
||||
{% 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.
|
||||
|
||||
|
||||
## 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("Hwllo @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
|
||||
lang,uage 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 likey 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.
|
@ -33,68 +33,6 @@ figure {
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
|
||||
.notice-info {
|
||||
background: #ececec !important;
|
||||
border: 1px solid #aaa !important;
|
||||
}
|
||||
|
||||
.notice-danger {
|
||||
background: #fcc !important;
|
||||
border: 1px solid #a66 !important;
|
||||
}
|
||||
|
||||
.notice-warning {
|
||||
background: #FED;
|
||||
border: 1px solid #fc9;
|
||||
}
|
||||
|
||||
.notice-tip {
|
||||
background: #ccf;
|
||||
border: 1px solid #66a;
|
||||
}
|
||||
|
||||
.notice-green {
|
||||
background: #161;
|
||||
border: 1px solid #393;
|
||||
}
|
||||
|
||||
.notice {
|
||||
margin: 10px;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notice p {
|
||||
margin: 0 0 17px 0;
|
||||
}
|
||||
|
||||
.notice p:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.notice > span {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notice > div {
|
||||
margin-left: 35px;
|
||||
}
|
||||
|
||||
.notice h2 {
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0 0 2px 0;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.header-link, .anchor {
|
||||
text-decoration: none;
|
||||
color: #bbb;
|
||||
|
61
_sass/_notice.scss
Normal file
61
_sass/_notice.scss
Normal file
@ -0,0 +1,61 @@
|
||||
.notice {
|
||||
margin: 2em 0;
|
||||
display: block;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& > span {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
& > div {
|
||||
margin-left: 35px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0 0 2px 0;
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-info {
|
||||
background: #ececec !important;
|
||||
border: 1px solid #aaa !important;
|
||||
}
|
||||
|
||||
.notice-danger {
|
||||
background: #fcc !important;
|
||||
border: 1px solid #a66 !important;
|
||||
}
|
||||
|
||||
.notice-warning {
|
||||
background: #FED;
|
||||
border: 1px solid #fc9;
|
||||
}
|
||||
|
||||
.notice-tip {
|
||||
background: #ccf;
|
||||
border: 1px solid #66a;
|
||||
}
|
||||
|
||||
.notice-green {
|
||||
background: #161;
|
||||
border: 1px solid #393;
|
||||
}
|
@ -4,3 +4,4 @@
|
||||
@import "main";
|
||||
@import "content";
|
||||
@import "code";
|
||||
@import "notice";
|
||||
|
Loading…
Reference in New Issue
Block a user