Skip to content

Commit 7abc83a

Browse files
authored
Merge pull request #11421 from Calinou/i18n-add-csv-plural-context
Document plural forms and translation context in Localization using spreadsheets
2 parents 452a0e7 + e64fed5 commit 7abc83a

File tree

3 files changed

+136
-61
lines changed

3 files changed

+136
-61
lines changed

tutorials/i18n/internationalizing_games.rst

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ Internationalizing games
66
Introduction
77
------------
88

9-
While indie or niche games usually
10-
do not need localization, games targeting a more massive market
11-
often require localization. Godot offers many tools to make this process
12-
more straightforward, so this tutorial is more like a collection of
13-
tips and tricks.
9+
While indie or niche games usually do not need localization, games targeting
10+
a more massive market often require localization. Godot offers many tools to
11+
make this process more straightforward, so this tutorial is more like a
12+
collection of tips and tricks.
1413

1514
Localization is usually done by specific studios hired for the job. Despite the
1615
huge amount of software and file formats available for this, the most common way
@@ -60,10 +59,14 @@ Select the resource to be remapped then add some alternatives for each locale.
6059

6160
Automatically setting a language
6261
--------------------------------
63-
It is recommended to default to the user's preferred language which can be obtained via :ref:`OS.get_locale_language() <class_OS_method_get_locale_language>`.
64-
If your game is not available in that language, it will fall back to the :ref:`Fallback <class_ProjectSettings_property_internationalization/locale/fallback>`
62+
63+
It is recommended to default to the user's preferred language which can be
64+
obtained via :ref:`OS.get_locale_language() <class_OS_method_get_locale_language>`.
65+
If your game is not available in that language, it will fall back to the
66+
:ref:`Fallback <class_ProjectSettings_property_internationalization/locale/fallback>`
6567
in **Project Settings > Internationalization > Locale**, or to ``en`` if empty.
66-
Nevertheless letting players change the language in game is recommended for various reasons (e.g. translation quality or player preference).
68+
Nevertheless, letting players change the language in game is recommended for
69+
various reasons (e.g. translation quality or player preference).
6770

6871
.. tabs::
6972
.. code-tab:: gdscript
@@ -78,7 +81,9 @@ Nevertheless letting players change the language in game is recommended for vari
7881

7982
Locale vs. language
8083
-------------------
81-
A :ref:`locale <doc_locales>` is commonly a combination of a language with a region or country, but can also contain information like a script or a variant.
84+
85+
A :ref:`locale <doc_locales>` is commonly a combination of a language with a
86+
region or country, but can also contain information like a script or a variant.
8287

8388
Examples:
8489

@@ -87,21 +92,26 @@ Examples:
8792
- ``en_US``: English in the USA / American English
8893
- ``en_DE``: English in Germany
8994

90-
Indie games generally only need to care about language, but read on for more information.
95+
Indie games generally only need to care about language, but read on for more
96+
information.
97+
98+
Why locales exist can be illustrated through the USA and Great Britain.
99+
Both speak the same language (English), yet differ in many aspects:
91100

92-
Why locales exist can be illustrated through the USA and Great Britain. Both speak the same language (English), yet differ in many aspects:
93-
- Spelling: E.g. gray (USA), grey (GB)
94-
- Use of words: E.g. eggplant (USA), aubergine (GB)
95-
- Units or currencies: E.g. feet/inches (USA), metres/cm (GB)
101+
- Spelling: e.g. gray (USA), grey (GB)
102+
- Use of words: e.g. eggplant (USA), aubergine (GB)
103+
- Units or currencies: e.g. feet/inches (USA), metres/cm (GB)
96104

97-
It can get more complex however. Imagine you offer different content in Europe and in China (e.g. in an MMO). You will need to translate each of those content variations into many languages and store and load them accordingly.
105+
It can get more complex however. Imagine you offer different content in Europe
106+
and in China (e.g. in an MMO). You will need to translate each of those content
107+
variations into many languages and store and load them accordingly.
98108

99109
Converting keys to text
100110
-----------------------
101111

102112
Some controls, such as :ref:`Button <class_Button>` and :ref:`Label <class_Label>`,
103113
will automatically fetch a translation if their text matches a translation key.
104-
For example, if a label's text is "MAIN_SCREEN_GREETING1" and that key exists
114+
For example, if a label's text is ``MAIN_SCREEN_GREETING1`` and that key exists
105115
in the current translation, then the text will automatically be translated.
106116

107117
This automatic translation behavior may be undesirable in certain cases. For
@@ -160,6 +170,8 @@ allow translators to choose the *order* in which placeholders appear:
160170
# Additionally, this form gives more context for translators to work with.
161171
message.text = tr("{character} picked up the {weapon}").format({character = "Ogre", weapon = "Sword"})
162172

173+
.. _doc_internationalizing_games_translation_contexts:
174+
163175
Translation contexts
164176
~~~~~~~~~~~~~~~~~~~~
165177

@@ -187,6 +199,8 @@ identical:
187199
// "Close", as in a distance (opposite of "far").
188200
GetNode<Label>("Distance").Text = Tr("Close", "Distance");
189201

202+
.. _doc_internationalizing_games_pluralization:
203+
190204
Pluralization
191205
~~~~~~~~~~~~~
192206

@@ -226,11 +240,6 @@ This can be combined with a context if needed:
226240
int numJobs = 1;
227241
GetNode<Label>("Label").Text = string.Format(TrN("{0} job", "{0} jobs", numJobs, "Task Manager"), numJobs);
228242

229-
.. note::
230-
231-
Providing pluralized translations is only supported with
232-
:ref:`doc_localization_using_gettext`, not CSV.
233-
234243
Making controls resizable
235244
-------------------------
236245

@@ -277,7 +286,7 @@ the current language can also be changed at runtime.
277286

278287
.. _doc_internationalizing_games_bidi:
279288

280-
Bidirectional text and UI Mirroring
289+
Bidirectional text and UI mirroring
281290
-----------------------------------
282291

283292
Arabic and Hebrew are written from right to left (except for the numbers and Latin
@@ -289,18 +298,27 @@ usually need to change anything or have any knowledge of the specific writing sy
289298

290299
For RTL languages, Godot will automatically do the following changes to the UI:
291300

292-
- Mirrors left/right anchors and margins.
293-
- Swaps left and right text alignment.
294-
- Mirrors horizontal order of the child controls in the containers, and items in Tree/ItemList controls.
295-
- Uses mirrored order of the internal control elements (e.g. OptionButton dropdown button, checkbox alignment, List column order, Tree item icons and connecting line alignment, e.t.c.), in some cases mirrored controls use separate theme styles.
296-
- Coordinate system is not mirrored, and non-UI nodes (sprites, e.t.c) are not affected.
297-
298-
It is possible to override text and control layout direction by using the following control properties:
299-
300-
- ``text_direction``, sets the base text direction. When set to "auto", direction depends on the first strong directional character in the text according to the Unicode Bidirectional Algorithm,
301-
- ``language``, overrides current project locale.
302-
- ``structured_text_bidi_override`` property and ``_structured_text_parser`` callback, enables special handling for structured text.
303-
- ``layout_direction``, overrides control mirroring.
301+
- Mirrors left/right anchors and margins.
302+
- Swaps left and right text alignment.
303+
- Mirrors horizontal order of the child controls in the containers, and items in
304+
Tree/ItemList controls.
305+
- Uses mirrored order of the internal control elements (e.g., OptionButton
306+
dropdown button, CheckBox/CheckButton alignment, List column order, TreeItem icons
307+
and connecting line alignment). In some cases, mirrored controls
308+
use separate theme styles.
309+
- Coordinate system is **not** mirrored.
310+
- Non-UI nodes (sprites, etc.) are **not** affected.
311+
312+
It is possible to override text and control layout direction by using
313+
the following control properties:
314+
315+
- ``text_direction``, sets the base text direction. When set to "auto",
316+
the direction depends on the first strong directional character in the text
317+
according to the Unicode Bidirectional Algorithm.
318+
- ``language``, overrides the current project locale.
319+
- The ``structured_text_bidi_override`` property and ``_structured_text_parser``
320+
callback, enable special handling for structured text.
321+
- ``layout_direction``, overrides control mirroring.
304322

305323
.. image:: img/ui_mirror.png
306324

@@ -317,7 +335,8 @@ word and line breaking require more than rules over character sequences.
317335
Godot includes ICU rule and dictionary-based break iterator data, but this data
318336
is not included in exported projects by default.
319337

320-
To include it, go to **Project → Project Settings**, enable **Internationalization → Locale → Include Text Server Data**,
338+
To include it, go to **Project → Project Settings**, enable
339+
**Internationalization → Locale → Include Text Server Data**,
321340
then export the project. Break iterator data is about 4 MB in size.
322341

323342
Structured text BiDi override
@@ -359,19 +378,19 @@ Testing translations
359378
You may want to test a project's translation before releasing it. Godot provides three ways
360379
to do this.
361380

362-
First, in the Project Settings, under :menu:`Internationalization > Locale` (with advanced settings enabled), there is a **Test**
363-
property. Set this property to the locale code of the language you want to test. Godot will
364-
run the project with that locale when the project is run (either from the editor or when
365-
exported).
381+
First, in the Project Settings, under :menu:`Internationalization > Locale`
382+
(with advanced settings enabled), there is a **Test** property. Set this property
383+
to the locale code of the language you want to test. Godot will run the project
384+
with that locale when the project is run (either from the editor or when exported).
366385

367386
.. image:: img/locale_test.webp
368387

369388
Keep in mind that since this is a project setting, it will show up in version control when
370389
it is set to a non-empty value. Therefore, it should be set back to an empty value before
371390
committing changes to version control.
372391

373-
Second, from within the editor go to the top bar and click on :button:`View` on the top bar, then go down to
374-
:ui:`Preview Translation` and select the language you want to preview.
392+
Second, from within the editor go to the top bar and click on :button:`View` on the top bar,
393+
then go down to :ui:`Preview Translation` and select the language you want to preview.
375394

376395
.. image:: img/locale_editor_preview.webp
377396

tutorials/i18n/localization_using_gettext.rst

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ Advantages
2121
- gettext is a standard format, which can be edited using any text editor
2222
or GUI editors such as `Poedit <https://poedit.net/>`_. This can be significant
2323
as it provides a lot of tools for translators, such as marking outdated
24-
strings, finding strings that haven't been translated etc.
25-
- gettext supports plurals and context.
24+
strings, finding strings that haven't been translated etc.
2625
- gettext is supported by translation platforms such as
2726
`Transifex <https://www.transifex.com/>`_ and `Weblate <https://weblate.org/>`_,
2827
which makes it easier for people to collaborate to localization.
@@ -39,8 +38,8 @@ Disadvantages
3938
- People who maintain localization files will have to install gettext tools
4039
on their system. However, as Godot supports using text-based message files
4140
(``.po``), translators can test their work without having to install gettext tools.
42-
- gettext PO files usually use English as the base language. Translators will use
43-
this base language to translate to other languages. You could still user other
41+
- gettext PO files usually use English as the base language. Translators will use
42+
this base language to translate to other languages. You could still user other
4443
languages as the base language, but this is not common.
4544

4645
Installing gettext tools
@@ -286,7 +285,7 @@ Using context
286285
The ``context`` parameter can be used to differentiate the situation where a translation
287286
is used, or to differentiate polysemic words (words with multiple meanings).
288287

289-
For example:
288+
For example:
290289

291290
::
292291

@@ -298,13 +297,16 @@ For example:
298297
Updating PO files
299298
-----------------
300299

301-
Some time or later, you'll add new content to our game, and there will be new strings that need to be translated. When this happens, you'll
300+
Some time or later, you'll add new content to our game, and there will
301+
be new strings that need to be translated. When this happens, you'll
302302
need to update the existing PO files to include the new strings.
303303

304-
First, generate a new POT file containing all the existing strings plus the newly added strings. After that, merge the existing
305-
PO files with the new POT file. There are two ways to do this:
304+
First, generate a new POT file containing all the existing strings plus
305+
the newly added strings. After that, merge the existing PO files
306+
with the new POT file. There are two ways to do this:
306307

307-
- Use a gettext editor, and it should have an option to update a PO file from a POT file.
308+
- Use a gettext editor, and it should have an option to update a PO file
309+
from a POT file.
308310

309311
- Use the gettext ``msgmerge`` tool:
310312

@@ -313,12 +315,14 @@ PO files with the new POT file. There are two ways to do this:
313315
# The order matters: specify the message file *then* the PO template!
314316
msgmerge --update --backup=none fr.po messages.pot
315317
316-
If you want to keep a backup of the original message file (which would be saved as ``fr.po~`` in this example),
317-
remove the ``--backup=none`` argument.
318+
If you want to keep a backup of the original message file (which would be saved
319+
as ``fr.po~`` in this example), remove the ``--backup=none`` argument.
318320

319321
POT generation custom plugin
320322
----------------------------
321323

322-
If you have any extra file format to deal with, you could write a custom plugin to parse and and extract the strings from the custom file.
323-
This custom plugin will extract the strings and write into the POT file when you hit **Generate POT**. To learn more about how to
324-
create the translation parser plugin, see :ref:`EditorTranslationParserPlugin <class_EditorTranslationParserPlugin>`.
324+
If you have any extra file format to deal with, you could write a custom plugin
325+
to parse and and extract the strings from the custom file. This custom plugin
326+
will extract the strings and write into the POT file when you hit **Generate POT**.
327+
To learn more about how to create the translation parser plugin, see
328+
:ref:`EditorTranslationParserPlugin <class_EditorTranslationParserPlugin>`.

tutorials/i18n/localization_using_spreadsheets.rst

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ CSV files must be formatted as follows:
3535
+--------+----------+----------+----------+
3636

3737
The "lang" tags must represent a language, which must be one of the :ref:`valid
38-
locales <doc_locales>` supported by the engine, or they must start with an underscore (`_`),
39-
which means the related column is served as comment and won't be imported.
40-
The "KEY" tags must be unique and represent a string universally (they are usually in
41-
uppercase, to differentiate from other strings). These keys will be replaced at
42-
runtime by the matching translated string. Note that the case is important,
43-
"KEY1" and "Key1" will be different keys.
38+
locales <doc_locales>` supported by the engine, or they must start with an underscore (``_``),
39+
which means the related column is served as comment and won't be imported.
40+
The ``KEY`` tags must be unique and represent a string universally. By convention, these are
41+
usually in uppercase to differentiate them from other strings. These keys will be replaced at
42+
runtime by the matching translated string. Note that the case is important:
43+
``KEY1`` and ``Key1`` will be different keys.
4444
The top-left cell is ignored and can be left empty or having any content.
4545
Here's an example:
4646

@@ -74,6 +74,58 @@ comma in the import options.
7474
BYE,Goodbye,Adiós,さようなら
7575
QUOTE,"""Hello"" said the man.","""Hola"" dijo el hombre.",「こんにちは」男は言いました
7676
77+
Specifying plural forms
78+
~~~~~~~~~~~~~~~~~~~~~~~
79+
80+
Since Godot 4.6, it is possible to specify
81+
:ref:`plural forms <doc_internationalizing_games_pluralization>` in CSV files.
82+
83+
This is done by adding a column named ``?plural`` anywhere in the table
84+
(except on the first column, which is reserved for translation keys).
85+
By convention, it's recommended to place it on the second column.
86+
Note that in the example below, the key column is the one that contains English
87+
localization.
88+
89+
.. code-block:: none
90+
91+
en,?plural,fr,ru,ja,zh
92+
?pluralrule,,nplurals=2; plural=(n >= 2);,,
93+
There is %d apple,There are %d apples,Il y a %d pomme,Есть %d яблоко,リンゴが%d個あります,那里有%d个苹果
94+
,,Il y a %d pommes,Есть %d яблока,,
95+
,,,Есть %d яблок,,
96+
97+
.. note::
98+
99+
Automatic Control translation is not supported when using plural forms. You must
100+
translate the string manually using :ref:`tr_n()<class_Object_method_tr_n>`.
101+
102+
Specifying translation contexts
103+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104+
105+
Since Godot 4.6, it is possible to specify
106+
:ref:`translation contexts <doc_internationalizing_games_translation_contexts>`
107+
in CSV files. This can be used to disambiguate identical source strings that
108+
have different meanings. While this is generally not needed when using translation
109+
keys ``LIKE_THIS``, it's useful when using plain English text as translation keys.
110+
111+
This is done by adding a column named ``?context`` column anywhere in the table
112+
(except on the first column, which is reserved for translation keys).
113+
By convention, it's recommended to place it on the second column, or after
114+
``?plural`` if it's also used. Note that in the example below, the key column
115+
is the one that contains English localization.
116+
117+
.. code-block:: none
118+
119+
en,?context,fr,ru,ja,zh
120+
Letter,Alphabet,Lettre,Буква,字母,字母
121+
Letter,Message,Courrier,Письмо,手紙,信件
122+
123+
.. note::
124+
125+
Automatic Control translation is not supported when using context. You must
126+
translate the string manually using :ref:`tr() <class_Object_method_tr>`
127+
or :ref:`tr_n() <class_Object_method_tr_n>`.
128+
77129
CSV importer
78130
------------
79131

0 commit comments

Comments
 (0)