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;
|
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 {
|
.header-link, .anchor {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #bbb;
|
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 "main";
|
||||||
@import "content";
|
@import "content";
|
||||||
@import "code";
|
@import "code";
|
||||||
|
@import "notice";
|
||||||
|
Loading…
Reference in New Issue
Block a user