Localizing an application

Depending on the kind of application, localization can become much more than having the strings that appear in the GUI available in different languages. If you came to learn about those more in-depth techniques, dealing with formatting and using ICU etc., this isn't the article you seek... This article discusses the relatively simple problem of localizing an app's GUI strings.

1. Changes to the source code

The needed changes to the source code are minimal. You just have to include the Catalog.h header and use the macros it provides on all the strings that appear in the GUI. The macros are:

  • B_TRANSLATION_CONTEXT gives a hint to the people that later translate your strings where those appear. So entering

    #define B_TRANSLATION_CONTEXT "MainWindow"

    will designate the following strings to belong to the "MainWindow".

  • B_TRANSLATE_SYSTEM_NAME is reserved for the app's name, e.g. here it's used in the window title of an "AwesomeApp":

    BWindow(frame, B_TRANSLATE_SYSTEM_NAME("AwesomeApp"), B_TITLED_WINDOW,
  • B_TRANSLATE is the usually deployed macro to mark a string to be translated. For example:

    BString *string = new BString(B_TRANSLATE("Invert selection"));
  • B_TRANSLATE_COMMENT allows you to leave an explanatory comment for the translators as a second parameter. For example:

    BString string = B_TRANSLATE_COMMENT("Connection refused", "As it's shown in the status bar, translate the string as short as possible.");

Here are two slightly more involved changes to make certain situations properly localizable.

  • You mustn't concatenate strings by just stringing them together. The order of the strings may be different for different languages. Assuming the "filename" is a variable holding a file name, you often see something like this:

    BString text(filename);
    text += " already exists.";

    Instead, you should reference that file name in a variable, that you then replace with the file name:

    BString text(B_TRANSLATE("%file% already exists."));
    text.ReplaceAll("%file%", filename);
  • Correctly handling plural forms is where a localization can shine or suck.
    This sucks:

    BString text(B_TRANSLATE("You have %number% mail(s)."));
    text.ReplaceAll("%number%", numberOfMailsAsString);

    While this looks a bit more complicated, but is much nicer:

    BString text;
    static BStringFormat format(B_TRANSLATE("{0, plural,"
    	"=0{You have no mail.}"
    	"=1{You have one mail.}"
    	"other{You have # mails.}}"));
    format.Format(text, numberOfMailsAsInt);

    The "#" is replaced with the integer variable "numberOfMailsAsInt" when formatting it all and putting it into the "text" string in the last line.
    You'll have to include "StringFormat.h".

2. Changes to the Makefile

You find a sample Makefile at /system/develop/etc/Makefile that includes Haiku's makefile-engine which greatly simplifies things.

  • You have to fill in the APP_MIME_SIG, e.g. "APP_MIME_SIG = application/x-vnd.AwesomeApp".

  • As you have included Catalog.h in the source, don't forget to add localestub to the LIBS section.

  • Under LOCALES you list the two-letter language codes you'll provide, e.g. "LOCALE = de en es fr ja".

3. Creating "catkeys"

"catkeys" are the plain text files that contain your strings, the context you assigned, optionally an explanatory comment, and the (to be) translated string.

You start by creating the English version – en.catkeys – like this:

make catkeys

This will create a folder locales with the en.catkeys in it. You'll duplicate this file, rename it to e.g. de.catkeys and send it to your German friend for translation. Here are the first lines of a catkeys file:

1	English	application/x-vnd.AwesomeApp	680205015
Add to ignore list					ListView						Add to ignore list
Found no matches.					ListView						Found no matches.
Show application path					SetupWindow				Show application path

The first line always starts with a "1", then the language, the app's signature, and a checksum. The checksum has to stay the same across all translations!
Then follow the rows of strings; the 1st column for the original string, the 2nd column shows the context, then optionally a comment, and the translated string last.

The translator must only work on the last column string (and possibly replace the "English" of the first line with their language name in English, e.g. "German". But that is only for human readers of that file, it doesn't matter when compiling the application).

You have a much more convenient and safe way to work with catkeys files by installing CatKeysEditor from the HaikuDepot. It provides a simple and clean interface to the otherwise confusing string jungle presented when opening catkeys in a text editor.
Better yet, use the online tool "Polyglot", see below.

4. Compiling the application

After you've got back all those translated catkeys and put them in the locales folder, you compile your app like this:

make bindcatalogs

So, after the regular built, you do the make bindcatalogs to put the catalogs created from the catkeys into the executable's resources.

5. Tips

The following tips are mostly geared towards the enthusiastic translators that would like to test their work. Seasoned coders are probably aware of them already.

  • When you test translated catkeys, make sure you launch the right app executable, the one you did the make bindcatalogs on. Having more than one application with that same application signature in the system may result in unexpected results. Especially true when it comes to Replicants!
    Best to remove/uninstall other instances of the app. A reboot may even be needed, if the app in question is auto-(re)launched on bootup and runs in the background.

  • When testing a localized build, be aware that the application may expect data or help files in certain locations. You may have to move some of those files around. The app's project page should inform on the specifics on how to build.

  • If you open a Makefile with Pe, you have to invoke the menu item Edit | Edit as text or click on the little Pe icon in the tool bar to get to the LOCALE entry.

If you need catkeys translations for your apps, you should have a look at Polyglot and the forum thread Help translating applications with Polyglot.