Locale kit: quick developer guide

Blog post by PulkoMandy on Thu, 2009-07-16 16:36

These lines must be executed before any localization occurs. You can put it at the beginning of your main() function to be safe, or at some other place like your main window constructor if you know what you are doing. You don't need to keep the cat variable around, the locale kit will handle everything by itself. Now, you need to define contexts in your application. Contexts are a way to separate different parts of the application. This is needed because, depending on the context, a string may have a different translation. For example, you usually find "Edit" in english applications. Depending on the context, this will in french become "Éditer" or "Édition". If your application is small or you don't know what to separate, it seems a good idea to use a single context for the whole application. You can #define TR_CONTEXT to anything you want, but try to be helpful to the translators so they know where the string comes from. For example, you can have a different context for each window in your app. This way, if a french translator sees "Edit" in your file, looking at the context he can know if he need to look for it in the "Main window" or the "Setting window" to see which translation he must choose. The last step, and the biffest one, is to enclose all the strings you want to localize in the TR() macro. For example, printf("hello"); becomes printf(TR("hello"));. It is possible to be more precise, for example if you want to help the translator, you can replace "This program is free software" by TR_CMT("This program is free software","free as in free speech, not as in free beer"). This way our french guy knows he needt to use "libre" and not "gratuit" as a translation. Finally, it is possible to override the context just for one string, but then you are forced to add a comment. So, if you have an Edit button (Éditer in french) and an Edit menu (Édition in french), you can do something like that:

#define TR_CONTEXT "Main window"
TR_ALL("Edit","Main window menu","This is édition in french");
TR_CMT("Edit","These french guys are strange, this one is éditer");
Finally, it is possible to translate strings by id to make your program language-neutral (and unreadable) this way:
TR_ID(1844535) // This is "Hello, world!"
I advise you not to do that. It was planned to support multiple datatypes instead of just strings, so you could use this ID system to get a picture or a sound. However, as everything in a catalog will be loaded into ram at startup, this would be pretty heavy for big programs. So we found another solution for you:
fopen(TR_CMT("path/to/english/file.wav","this is me shouting Hello World"));
The translator will then record a translated version of the sound and "translate" the path to ath/to/french/file.wav. You can do the same for everything using a string, so it should be possible to access pretty much anything this way.

Build system

That's all for the code, but now you have to generate the files to send to your translators. Note that as long as you don't use TR_ID, your program will work fine in the original language without any file. If the locale kit can't find a translation for something it will return the original string. There are two different cases for the build tools : are you localizing something integrated in the haiku build tree (a preflet for example), or are you working on an external app?

Manual tool invocation

If it's your own app, you have to do the work yourself. There are two commands to use, they are both included in Haiku images. The first one is collectcatkeys. It will travell trough your sourcecode and find the strings you want to be localized and dump them to a simple text file you can send to your translators. Here is how to use it:
cpp *.cpp > preprocessed.cpp.tmp
collectcatkeys preprocessed.cpp.tmp -l english -o english.catkeys
rm preprocessed.cpp.tmp
The other tool is linkcatkeys. Linkcatkeys will compile the catkeys files into a binary format suitable for use at application runtime (just a flatenned BMessage, actually).
linkcatkeys english.catkeys -l english -s mimesignature -o english.catalog
The mimesignature should be the one of your application. You now just have to place the generated catalog file with your executable, in the locale/catalogs/mimesignature/ folder. It is also possible to tell linkcatkeys to put the catalog directly as an attribute or a resource in your executable file if you want to avoid having all the catalogs around, but this is still untested. There are some other folders where you can put your catalog files too, the locale kit will look at /boot/home/config/etc and /boot/system/etc/ . These are meant for users who want to override catalogs for a specific app, and for system-wide catalogs from haiku we don't want to spread all over the system.

Haiku jam rules

If you are inside the haiku jam system, the rules for localization are already written for you (they lie in build/jam/BeOSRules if you ever want to look at them). Here is the example from the locale preflet, which is the first localized application in Haiku:
DoCatalogs Locale #App. executable name
 : x-vnd.Haiku-Locale # mime signature
 : Locale.cpp #source files
   LocaleWindow.cpp
 : english.catalog #default catalog generated from sourcecode
 :
 [ FDirName $(HAIKU_TOP) src preferences locale french.catkeys ] # list of availabe translations
 ;
Thats all. Jam will take care of everything else.