Back Part 4 – Localize texts using PO files 4 | ♥ 2



PO files are part of the GNU GetText localization technique. It’s a very simple format that can be edited using various tools, like POEdit (available on Windows, Mac & Linux).

The philosophy

The “classic” way to do things is to write “dev English” sentences in your code, then use this Dev-English to kind-of translate to “proper English” (which will be used in the release version of your game), or translated to “proper whatever-language” you might want.

Note that your Dev-English are the keys for the translation texts. So changing your Dev-English texts will break the keys. The good thing is POedit handles that well, and properly suggests “lost” translations.

My implementation

My GetText implementation has 3 elements:

If you want to use the GetText lib for Haxe, you will need to install it:

haxelib install deepnightLibs


Marking translatable strings with Lang.hx

In your code, just use calls like: Lang._("Some translatable sentence"). This will allow our tools to extract all the strings that require some translation, so we can feed our PO tool.

For example:

function myStuff() {
  trace("This is an untranslated text");
  trace( Lang.t._("This text is meant to be translated, and will be extracted.") );

Extracting texts

You can run haxe langParser.hxml from the command line to extract all strings in Lang.t._(...) calls.

All these strings will be stored inside a POT file, which is a Catalog file.

Using POedit to translate

In my above example, the POT file is my “Dev-English” string catalog. It should be stored it in the /res/lang folder.

In POedit, start a new translation by setting the target language (could be French for example, or English again, if you want to turn your dev-English into something better).

Import your POT catalog using the menu Catalog -> Update from POT. This will add all the untranslated strings extracted by the LangParser script.

Translate or validate existing entries “as-is”, and save your PO file. It will also generate a MO file which is basically the optimized/binary version of the PO file.

Import translations into your code

Modify the Lang.init() method to import new languages. All your Lang.t._("My string that needs translation") calls will be replaced by the translated string found in your MO file on runtime.

Important: all the PO and MO files should be stored in /res/lang folder!

Advanced features

Strings with parameters

If you need some dynamic content inside a translatable string, you use the ::myVar:: system. To replace myVar in your string by the actual value you want, you need to provide an anonymous object to the Lang.t._(“…”) call.

See the following examples:

Lang.t._("Hello ::name::! How are you?", { });
Lang.t._("::v:: + ::v:: equals ::eq::", { v:5, eq:10 });

Note that the compiler will check any anomaly here:

Lang.t._("Hello ::name::! How are you?");
Lang.t._("Hello ::username::! How are you?", { name:"foo" });

Both will pop a compile error of variable name mismatching.

Adding translator comment

Sometimes, you may have some strings that could translate differently based on context.

For example, in your app, you might have a setting for some font size, and one possible value is the string “Large“. In this case, it means “Big“. For another setting, like a world size, you might have a setting string “Large” again, but this time it means “Vast”. They would translate in different ways in French for example.

To disambiguate these strings, you can use Translator comments:

Lang.t._("Large||for a font size"); 
Lang.t._("Large||for a level size");

Both will return “Large” at runtime by default, but they can now have distinct translations, if needed (so the return will vary in this case).

Warning: due to some technical limitations, you shouldn’t have ANY space around the “||” (double pipes) characters.

In POEdit, translation comments appear right in front of the corresponding entry:

Leave a Reply

Your email address will not be published.

  1. hosey:

    Any thoughts on making this work live. Switching languages without recompiling?

    June 16, 2020 at 19:34
  2. hosey:

    Thank you for a response. Ya. My biggest issue with non js/html browser releases currently is multiple fonts. I was hoping you had the silver bullet just lying around.

    May 1, 2020 at 02:33
  3. Hosey:

    Any thoughts on fonts? Like including Chinese and English?

    April 30, 2020 at 21:33
    • Sébastien Bénard:

      This tutorial only describes text extraction for translations purpose: having a complete workflow for a Chinese translation is something more complicated as it implies some specificity. It would require a dedicated guide :)

      April 30, 2020 at 22:34