[[PageOutline(1-100, Table of Contents)]] This topic explains the implementation details of internationalization and localization in the game. For documentation on how to use those features to either internationalize new game content or localize the game, see [wiki:Internationalization_and_Localization Internationalization and Localization]. = Internationalization = == Third-Party Libraries == The internationalization and localization implementation for the game relies on two libraries: * '''[https://icu.unicode.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. * '''[https://github.com/tinygettext/tinygettext tinygettext]''' is a small library that we forked. This library provides the structures and methods that we use to load PO files in memory. == The Localization Singleton == 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 access an instance of the singleton including the `i18n/L10n.h` header file and using `g_L10n`. 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). === Initialization === The singleton is initialized on engine startup, where it performs the following tasks: 1. Creates a list of available locales based on the filenames of the PO files in the `l10n` virtual folder. The `l10n` virtual folder holds the combination of the files in the `binaries/data/l10n` folder, which contains the engine PO files, and the `l10n` folder of each enabled mod. See `source/ps/GameSetup/GameSetup.cpp` . You can obtain the list of available locales calculated here at any later time calling `g_L10n.GetSupportedLocaleCodes()` . This method returns an array of strings with the locale codes found here. `g_L10n.GetSupportedLocaleDisplayNames()` returns a similar vector, this one with display names instead of codes, where the name of each locale is in the language of the locale itself. The special locale "Long Strings", of code "long", is only returned if yours is a development copy (if the `config/dev.cfg` file exists in the virtual filesystem). 2. Determines the current locale, first looking at the configuration value `locale` and, if undefined, looking at the system locale using [http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a020c6966493a8f00572616b64b5527c3 icu::Locale::getDefault()]. Once the locale is defined, it loads the dictionary of the target locale if it is available. The dictionary is a structure from the tinygettext library that holds the list of English strings linked to translated strings (including context and plural considerations). You can change the locale later using `g_L10n.SetLocale()` . You can pass this method either a locale code as a string, such as "en_GB", or an instance of [http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html icu::Locale] . The method reloads the dictionary if required. === Localization Functions === The `L10n` singleton provides many localization functions that you can use from C++, some of which are also published to be used in JavaScript: * [http://svn.wildfiregames.com/docs/classL10n-members.html L10n members] == GUI == === Text === The `source/gui/CGUI.cpp` and `source/gui/COList.cpp` files implement the parsing of the GUI XML localization elements: * `attribute` (along with `keep` and `translate`) * `translatableAttribute`. See [wiki:Internationalization#InternationalizingGUIFiles Internationalizing GUI Files] to see how to use them. === Images === The `source/gui/GUIRenderer.cpp` file, responsible for rendering images on the GUI, is configured to use `g_L10n.LocalizePath()`, which loads localized versions of images if available. See [wiki:Localization#LocalizingImages Localizing Images] for more information. == Functions Published for JavaScript == The `source/i18n/scripting/JSI_L10n.cpp` file publishes some of the functions that the `L10n` singleton offers, as well as some custom internationalization functions based on the `L10n` functionality. * [http://svn.wildfiregames.com/docs/namespaceJSI__L10n.html JSI_L10n functions] == JavaScript-Side Implementation == The following sections describe some implementation details that are specific to the JavaScript side. === JavaScript Translation Cache System === Although you can access the C++ functions listed above from JavaScript simply prefixing them with `Engine`, as in `Engine.Translate()`, the `binaries/data/mods/public/globalscripts/l10n.js` file defines the following equivalent global internationalization functions: {{{ translate(message); translatePlural(singularMessage, pluralMessage, number); translateWithContext(context, message); translatePluralWithContext(context, singularMessage, pluralMessage, number); }}} These global functions are basically wrappers for the engine internationalization functions, however they are not just that. These global functions use JavaScript-side caching to reduce the number of calls to the engine functions, because calls to engine functions require string conversions that are far from cheap. In JavaScript, you should use these functions intead of the `Engine.` whenever possible. === Object Translation Helper Function === The `binaries/data/mods/public/globalscripts/l10n.js` file defines a function, `translateObjectKeys`, which is a helper function that can translate specific properties of a !JavaScript object: {{{ translateObjectKeys(object, keys); }}} The `keys` parameter is an array of strings with the names of the object properties to translate. === String Formatting Function === 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]. = Message Extraction = 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. 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. The format of the messages.json file is a custom format. See [wiki:Message_Extraction Message Extraction] for details. The resulting POT files are generated on the same `l10n` folder that contains the `messages.json` file that define their configuration. `updateTemplates.py` relies on the Python library [http://pology.nedohodnik.net/ pology]. = Localization = == Transifex Integration == Currently, the translation of the game happens in Transifex, at https://www.transifex.com/projects/p/0ad/ 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. 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 library to automatically download the latest PO files into their respective folders. To run this script you need a Transifex account and need to install the Transifex client library. See [https://developers.transifex.com/docs/cli the Transifex documentation] for installation and updating. == Localization Debug == The `source/tools/i18n/generateDebugTranslation.py` is a special script that generates a PO files for debugging purposes. Currently it has two functionalities. === Long String Locale === Running `source/tools/i18n/generateDebugTranslation.py --long` generates PO files 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. The `generateDebugTranslation.py --long` 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. === Non Translated Strings === Running `source/tools/i18n/generateDebugTranslation.py --debug` generates a language, prefixing all untranslated string with `DEBUG_PREFIX`. This makes it easy to identify untranslated strings, while the game is still navigable. The language code is "debug". = Language Selection Menu = The dialog box that you open when you select '''Options → Language '''in the main menu is defined in the following files: * `gui/page_locale.xml` * `gui/locale/locale.js` * `gui/locale/locale.xml`