3 | | = The Localization Singleton = |
4 | | |
5 | | The heart of the internationalization and localization implementation is at `source/i18n/L10n.cpp`. The `L10n` class implemented in this file works as a [http://en.wikipedia.org/wiki/Singleton_pattern singleton], and provides many functions that can be used from anywhere in the engine C++ codebase. You can get an instance of the singleton including the `i18n/L10n.h` header file and calling `L10n::instance()`. |
6 | | |
7 | | == Initialization == |
| 5 | = Internationalization = |
| 6 | |
| 7 | == Third-Party Libraries == |
| 8 | |
| 9 | The internationalization and localization implementation fo the game relies on two libraries: |
| 10 | |
| 11 | * '''[http://site.icu-project.org/ ICU]''' is a common internationalization library that provides structures to handle locales, easily obtain the default system locale, handle date and number representations on each locale, or provide the localized names of locales to show them in a language combo box. |
| 12 | |
| 13 | * '''tinygettext''' is a small library that we forked from the the [https://github.com/supertuxkart/stk-code/tree/master/src/tinygettext SuperTuxKart repository]. This library provides the structures and methods that we use to load PO files in memory. |
| 14 | |
| 15 | == The Localization Singleton == |
| 16 | |
| 17 | The heart of the internationalization and localization implementation is at `source/i18n/L10n.cpp`. |
| 18 | |
| 19 | The `L10n` class implemented in this file works as a [http://en.wikipedia.org/wiki/Singleton_pattern singleton], and provides many functions that can be used from anywhere in the engine C++ codebase. You can get an instance of the singleton including the `i18n/L10n.h` header file and calling `L10n::instance()`. |
| 20 | |
| 21 | This class holds the current locale and the translations for the current locale at any given time, and its methods are used all over the C++ implementation (and published to JavaScript as well). |
| 22 | |
| 23 | === Initialization === |
79 | | TODO: |
80 | | * source/gui/scripting/ScriptFunctions.cpp |
81 | | |
82 | | == Third-Party Libraries == |
83 | | |
84 | | TODO: |
85 | | * tinygettext (third_party/tinygettext) |
86 | | * ICU |
87 | | |
88 | | |
89 | | == Message Extraction System == |
90 | | |
91 | | TODO: |
92 | | * source/tools/i18n/updateTemplates.py |
93 | | * <mod>/l10n/messages.json |
94 | | * source/tools/i18n/potter/ |
95 | | |
96 | | == Long Strings Locale == |
97 | | |
98 | | TODO: |
99 | | * source/tools/i18n/generateLongStringTranslations.py |
100 | | |
101 | | == Transifex Integration == |
102 | | |
103 | | TODO: |
104 | | * .tx/config |
105 | | * source/tools/i18n/pullTranslations.py |
106 | | * source/tools/i18n/tx |
107 | | * source/tools/i18n/txclib/ |
108 | | |
109 | | = JavaScript = |
| 99 | The `source/gui/scripting/ScriptFunctions.cpp` file publishes some of the functions that the `L10n` singleton offers, as well as some custom internationalization functions based on the `L10n` functionality. |
| 100 | |
| 101 | The following functions from `L10n` are published: |
| 102 | {{{ |
| 103 | scriptInterface.RegisterFunction<std::string, &getCurrentLocale>("getCurrentLocale"); |
| 104 | scriptInterface.RegisterFunction<void, std::string, &SetLocale>("SetLocale"); |
| 105 | |
| 106 | scriptInterface.RegisterFunction<std::vector<std::string>, &GetSupportedLocaleCodes>("GetSupportedLocaleCodes"); |
| 107 | scriptInterface.RegisterFunction<std::vector<std::wstring>, &GetSupportedLocaleDisplayNames>("GetSupportedLocaleDisplayNames"); |
| 108 | scriptInterface.RegisterFunction<int, &GetCurrentLocaleIndex>("GetCurrentLocaleIndex"); |
| 109 | |
| 110 | scriptInterface.RegisterFunction<std::wstring, std::wstring, &translate>("translate"); |
| 111 | scriptInterface.RegisterFunction<std::wstring, std::string, std::wstring, &translateWithContext>("translateWithContext"); |
| 112 | scriptInterface.RegisterFunction<std::wstring, std::wstring, std::wstring, int, &translatePlural>("translatePlural"); |
| 113 | scriptInterface.RegisterFunction<std::wstring, std::string, std::wstring, std::wstring, int, &translatePluralWithContext>("translatePluralWithContext"); |
| 114 | scriptInterface.RegisterFunction<std::wstring, std::wstring, &translateLines>("translateLines"); |
| 115 | |
| 116 | scriptInterface.RegisterFunction<std::wstring, int, std::wstring, &formatMillisecondsIntoDateString>("formatMillisecondsIntoDateString"); |
| 117 | |
| 118 | scriptInterface.RegisterFunction<std::wstring, double, &formatDecimalNumberIntoString>("formatDecimalNumberIntoString"); |
| 119 | }}} |
| 120 | |
| 121 | The following custom functions are also published: |
| 122 | {{{ |
| 123 | // Returns the build time of the game formatted for the current locale. |
| 124 | scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp"); |
| 125 | |
| 126 | // Translates an array of strings, avoiding what would otherwise be many calls |
| 127 | // to the C++ engine from JavaScript. |
| 128 | scriptInterface.RegisterFunction<std::vector<std::wstring>, std::vector<std::wstring>, &translateArray>("translateArray"); |
| 129 | |
| 130 | // Simply returns the string that it receives. This function is required to mark |
| 131 | // some strings from translation in places where they should not be translated |
| 132 | // yet, so that translate() cannot be used. The point of this function is that |
| 133 | // strings passed to it are catched by the message extraction system (see |
| 134 | // below). For more information, see: |
| 135 | // http://www.gnu.org/software/gettext/manual/html_node/Special-cases.html |
| 136 | scriptInterface.RegisterFunction<std::wstring, std::wstring, &markToTranslate>("markToTranslate"); |
| 137 | }}} |
| 138 | |
| 139 | == JavaScript-Side Implementation == |
158 | | The first four functions are simply wrappers for the engine internationalization functions (such as `Engine.translate`). These global functions use caching to reduce the number of calls to the engine functions, because calls to engine functions require string conversions that are far from cheap. |
159 | | |
160 | | `translateObjectKeys` is a helper function that can translate specific properties (`keys` array) of a !JavaScript object. |
161 | | |
162 | | ==== sprintf.js ==== |
163 | | Implements the `sprintf()` function, used for [wiki:Internationalization#UsingStringFormattingInsteadofConcatenatingStringsinJavaScript string formatting]. |
164 | | |
165 | | === gui/ === |
166 | | ==== page_locale.xml ==== |
167 | | Represents the dialog box that you open when you select '''Options → Language '''in the main menu. The actual content of the dialog box is defined in `gui/locale/`. |
| 165 | |
| 166 | The `keys` parameter is an array of strings with the names of the object properties to translate. |
| 167 | |
| 168 | === String Formatting Function === |
| 169 | |
| 170 | The `binaries/data/mods/public/globalscripts/sprintf.js` file defines a function, `sprintf`, that can be used for string formatting. To learn how to use it, see [wiki:Internationalization#UsingStringFormattingInsteadofConcatenatingStringsinJavaScript Using String Formatting Instead of Concatenating Strings in JavaScript]. |
| 171 | |
| 172 | = Message Extraction = |
| 173 | |
| 174 | Message extractions is the process of parsing the source files searching for strings that need to be translated, and generating a translation template file (POT) from them that translators can use. |
| 175 | |
| 176 | The script responsible for generating POT files is `source/tools/i18n/updateTemplates.py`. This scripts goes through the `l10n` folders of the sources (`binaries/data/l10n` and `l10n` in mod folders) and it reads the `messages.json` file there, which defines where to extract the strings from, and how to extract them. |
| 177 | |
| 178 | The format of the messages.json file is a custom format. Check existing `messages.json` files to learn the syntax. |
| 179 | |
| 180 | The resulting POT files are generated on the same `l10n` folder that contains the `messages.json` file that define their configuration. |
| 181 | |
| 182 | `updateTemplates.py` relies on a Python library to create POT files, `potter`, which is the name of our fork of [http://babel.edgewall.org/log/trunk/babel/messages babel-messages]. It is located at `source/tools/i18n/potter`. |
| 183 | |
| 184 | = Localization = |
| 185 | |
| 186 | == Transifex Integration == |
| 187 | |
| 188 | Currently, the translation of the game happens in Transifex, at https://www.transifex.com/projects/p/0ad/ |
| 189 | |
| 190 | The `l10n` folders of the game sources contain a hidden folder, `.tx`, which contains a `config` file. This config file determines where in Transifex are the PO files that must be downloaded to that specific `l10n` folder. |
| 191 | |
| 192 | To download the translation files from Transifex, you can call `source/tools/i18n/pullTranslations.py`, which uses those `.tx/config` files and the Transifex client (`source/tools/i18n/tx`) and library (`source/tools/i18n/txclib/`) to automatically download the latest PO files into their respective folders. |
| 193 | |
| 194 | To use this script, you need a Transifex account. |
| 195 | |
| 196 | == Long Strings Locale == |
| 197 | |
| 198 | The `source/tools/i18n/generateLongStringTranslations.py` is a special script that generates a PO file for an artificial language with code "long". This PO file consists of the longest strings (as in number of characters) of every available language for each message. |
| 199 | |
| 200 | The `generateLongStringTranslations.py` script parses the PO files in the `l10n` folders of the sources, and generates the new PO file in each of those `l10n` folders as well. Remember to download the real PO files before you call the script, so that the script can read the translations from them in order to find the longest strings. |
| 201 | |
| 202 | = Language Selection Menu = |
| 203 | |
| 204 | The dialog box that you open when you select '''Options → Language '''in the main menu is defined in the following files: |
| 205 | * `gui/page_locale.xml` |
| 206 | * `gui/locale/locale.js` |
| 207 | * `gui/locale/locale.xml` |