I18nMobileCode

Internationalizing C code for Ubuntu Mobile

Rationale

Ubuntu Mobile code should use GNU gettext capabilities to look up and display translations of strings exposed in the user interface based on the current locale. This is called "internationalization", or "i18n" for short.

This document summarizes steps you can take to set up your C code for gettext-based i18n.

Note: Python code can also be internationalized with gettext. Please review Moblin Image Creator for an example. XML, Glade and other formats are supported by the toolset.

In addition, source packages should be set up to use intltool. Intltool assists in creating templates, translation files, and binary translation files through ongoing cycles of development. intltool is not covered in detail here but is recommended practice.

Resources

See the GNU gettext online manual: http://www.gnu.org/software/gettext/manual/gettext.html

Example applications

Drivel, Mousepad, and Galculator seem to work. Application Status

Runtime

  • The system knows the current locale -- display it with locale. (Set and generate locales as described below)

  • Executing code calls the gettext (String) function, where the String becomes the msgid

  • gettext returns the translation of the msgid by looking up the msgid in the right binary translation file based on the domain and on the locale and returning the translation (the msgstr)

  • Generally, the domain is set to equal the package name at build time

  • The domain can also be specified for each lookup by passing the domain and the msgid with the dgettxt (domain, msgid) function. Thus, one package can use multiple domains, or multiple packages could use a single domain.

Note: It may be preferable that each package have its own unique domain to keep things simple and maintainable.

  • A domain corresponds to the relevant set of binary translation files, each named <domain>.mo, for example, galculator.mo

  • For each translation, there's a separate <domain>.mo file. These reside in the system's locale directories: /usr/share/locale/<locale>/LC_MESSAGES. For example, if galculator has translations for Chinese/China and French/Canada, it would have the following two .mo files;

    • /usr/share/locale/zh_CN/LC_MESSAGES/galculator.mo

    • /usr/share/locale/fr_CA/LC_MESSAGES/galculator.mo

Where: The <locale> is: <two letter language code>_<two letter country code>. (It may be possible to use only the language code, but I have not yet tested whether this fallback mechanism exists.)

Note: Ubuntu Desktop also looks in /usr/share/locale-langpack because the language pack mechanism delivers mo files there. It is not yet decided whether Mobile will use this mechanism.

Life cycle

  • gettext tools scan source code (C, Glade, XML, C++, Python, etc.) for strings marked for tranlsation

Note: Internationalizing packages that use more than just C files (Python, Glade, XML...) is greatly facilitated by setting up the package to use intltool, which can automatically include at least these source file types

  • gettext tools extracts all marked strings into a po template file (sometimes called a pot file)
  • For every translatable string, there is a msgid field and a msgstr field (this msgstr field will contain the translated string)

  • Various comments that can assist translators are also extracted and passed through
  • gettext tools are used to convert the po template into po files, one for each language
  • translators read the msgid, look at any comments, and enter translated text into the msgstr field. (Various front ends exist for this process, including Launchpad and emacs PO mode)
  • gettext tools convert translated po files into binary .mo files, which are delivered by various means to the image for run-time use

... Source code is modified ...

  • New po templates are generated
  • po templates are merged with existing po files to pass though relevant changes
  • Translations are updated
  • New .mo files are created/delivered/used

Code time

  • #include <libintl.h> so that code can use gettext and related functions. The precise approach for this may vary. See linked URL.

  • Include/create block macros to replace gettext function calls with conventional short hand (see below)
  • In main.c, initialize gettext, including binding the domain (see below)

  • Modify code so that translatable strings are marked and wrapped in the appropriate gettext function (see below)

Creating block macros for conventional short cuts

Each gettext function has a conventional shorthand version used in code that is replaced with the correct/full version by block macros.

#define _(String) gettext (String)
#define N_(String) gettext_noop (String)

The first is for strightforward string replacement.

The second (gettext_noop) is used to mark strings for translation in parts of the code where function calls are syntactically illegal, for example when initializing static arrays. For info, please see http://www.gnu.org/software/gettext/manual/gettext.html#Special-cases

There are other useful gettext functions, such as ngettext for translating strings in which the grammatical form used depends on a number. Please review the linked URL.

Initializing gettext

For non-library programs, place this in your main.c:

     main (int argc, char *argv[])
     {
       ...
       setlocale (LC_ALL, "");
       bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
       textdomain (GETTEXT_PACKAGE);
       ...
     }

Where:

  • GETTEXT_PACKAGE is the program's domain and is usually defined in the program's build in configure.in or configure.ac (in Drivel, it is defined in configure.ac)

  • LOCALEDIR is the root directory in which locales containing mo files are found and is usally defined in package build files. (For Ubuntu Mobile the actual location is: /usr/share/locale.)

Note: Libs have slightly different rules. See gettext manual linked to above for further discussion.

Marking translatable strings in code

Strings need to be marked so they can be found and extracted by tools to create/update po templates.

You can mark strings in standard and non-standard ways. (Non-standard markings involve passing appropriate flags to xgettext.)

Standard ways include using these functions, described previously:

  • gettext_noop(String) (usually written in code as 'N_(String)' and converted via macro to the full version)

  • gettext_(String) (usually written in code as '_(String)' and converted via macro to the full version)

Miscellaneous notes

Tools

  • gettext: performs run-time translations
  • intltool: assists creating templates, translation files, and binary translation files, through ongoing cycles of development. It wraps the following, although they can be used individually:
  • xgettext: creates a template file from source
  • msginit: creates a text file for translation from a template
  • msgfmt: creates a run-time binary translation file from text ranslation file
  • msgmerge: updates text translation files from updated template file

Testing

To test your translation, you need to:

  • generate the desired locale (if it doesn't already exist)
  • switch to the desired locale from a terminal in the target
  • launch your program from the terminal (to use the locale you just switched to)

These steps are covered next.

Generating the locale

Ubuntu Locales typically consist of:

  • a two letter language code (for example, "en" for english, or "zh" for Chinese)
  • an underscore
  • a two letter country code (for example, "GB" for Great Britain, or "TW" for Taiwan)
  • followed by ".UTF-8"

Here are some locales:

  • de_DE.UTF-8: German/Germany
  • en_US.UTF-8: English/USA
  • es_ES.UTF-8: Spanish/Spain
  • fr_FR.UTF-8: French/France
  • zh_TW.UTF-8: Chinese/Taiwan
  • zh_CN.UTF-8: Chinese/China

You can generate a locale with the locale-gen <locale> command. For example: locale-gen zh_CN.UTF-8

Switch to the desired locale

Set the LANG and the LC_ALL environment variables to the desirced locale. For example to switch to Chinese/Taiwan, enter:

LANG=zh_CN.UTF-8
LC_ALL=zh_CN.UTF-8

Note: test whether the locale is set properly with the locale command.

Launch and test

From the terminal in which you set the env variables, launch your application.

Evaluate whether the desired translations display.

MobileAndEmbedded/I18nMobileCode (last edited 2008-08-06 16:21:32 by localhost)