Translation support for Unity games

Unity provides you with pretty much all the tools you need for creating a game - one really handy feature that’s unfortunately missing though is inbuilt support for doing translations into different languages.

It wasn’t too complicated to add it myself in the end, but since I couldn’t really find any articles on this topic I thought I’d do a quick write-up of how I approached it in case it’s helpful for anyone :)

So, if you want to add translation support to software you’ll have to ask yourself how and where to store your text strings and their translated versions, and how to load them into your program. Especially the latter was really important to me - I wanted to have a system that requires minimal code changes and requires the least possible amount of work when adding new strings that needed translations. What I especially did not want was having to manually manage a database of all strings in the game with cryptic lookup keys like ID_UI_INGAME_WINDOW_BUILDER_TITLE that some systems use, because having to maintain that database would be super uncomfortable and I’d rather not have to copy these keys around where after a couple weeks I don’t know anymore which text exactly is hidden away behind them.

When researching translation systems you’ll quickly stumble over gettext (up-to-date Windows binaries can be found here), and it satisfies the above requirements perfectly.
All you have to do is to wrap your strings that need translations into some function, so for example this:

string text = “Some text”;

becomes this:

string text = I18N.GetString(“Some text”);

You then run a command line program named “xgettext” that’s part of the gettext system over your source code. It looks for appearances of your wrapper function and extracts their string argument into a separate .pot file. Using “msginit” you then take this .pot file and create individual .po files for each language you want to support. The .po is a simple plaintext file containing both your original language strings and their translations.
Here’s an example of what that .po file looks like for German:

msgid “Some text”
    msgstr “Irgendein Text”

    msgid “Another text”
    msgstr “Ein weiterer Text”

You then parse this file and put its contents into a hashtable, using the msgid as key and msgstr as value. Your GetString()-wrapper method from the example above then simply looks up and returns the proper value for the given key (or returns the key if it doesn’t exist in your hashtable, so if some strings are missing translations they’ll at least show up in English).
There’s a source code example of how to do this here that should get you started.

If you’re like me and try to use the Windows command line only if you really have to you can make it even less of a hassle by creating a couple of batch files that do all the gettext stuff and a Unity editor window that executes them. Now you can run the string extraction with a single click, without having to leave the Unity Editor!

Another benefit of using gettext is that it’s a pretty common translation system, so there are plenty of tools already supporting it. For example there’s poEdit, a GUI application for editing the .po files, which makes life easier for your translators. And if you’re neither into command line programs nor Unity editor scripting it can do the string extraction for you as well.

Making it work with Unitys UI system

So, this system is great for text that you’re directly assigning from within your code - but what about the Text components in Unitys UI system? I certainly didn’t want to set all the Text component values from code and wanted to keep being able to visually create my UI in the Unity editor.

My solution is to do the same thing as for the code basically. I “tag” the Text components that need to be translated by adding another special component to them (named “I18NText”):

It does nothing more than take the text value and run it through the wrapper function:

public class I18NText : MonoBehaviour {
     void Awake() {
        Text textComponent = GetComponent<Text>();
        textComponent.text = I18N.GetString(textComponent.text);
    }
}

Of course it’s also necessary to extract these UI strings into the .po file, so I wrote a small editor script that loops over my UI assets, gets their Text components and writes them into a .pot. The .pot containing the UI strings and the .pot containing the source strings are merged into a single .pot (using the gettext tool “msgcat”), that is then used for creating the .po files.

Phew! :)
This sounds like a lot, but once it’s set up all you really have to do in the end is to wrap your strings into the lookup function, mark your UI texts with the additional component, and run the gettext tools before doing a build. That’s it!