Internationalization

Applications should be developed in a way that makes it possible for them to be customized for different natural languages. This requires user-facing text in an application’s source code to be handled specially, as well as support in the build system for management of translation files.

In this example we use only a few user-visible labels, but we handle them in a way that makes it possible to provide translations for them.

Handling User-Visible Text

In Python applications, user-visible text in labels and other widgets is wrapped in calls to the _() translation function. In this example, the application’s do_startup method sets the application name, which needs to be translatable:

class Application(Gtk.Application):

    def __init__(self):
        super().__init__(application_id='com.example.simple_weather')
        GLib.set_application_name(_('Simple Weather'))

In the following code from the src/widgets.py file, only the user-visible text is passed to the translation function:

self.long_press_menu.append(_('Remove Place'), 'win.remove')

The win.remove text is not translated because it defines the scope and name of an action that is only used by the code, and must remain the same for the application to work correctly.

Setting up the Translation Function

The _() function is provided by the standard Python gettext module and is made available throughout the application when the application starts. We do this in the src/simple-weather.in file which becomes the executable simple-weather file at run-time:

# Enable translation to occur in the extension libraries.
locale.bindtextdomain('@project_name@', localedir)
locale.textdomain('@project_name@')

# Install a translator for Python code to use throughout the application.
gettext.install('@project_name@', localedir, names=['ngettext'])

The build system replaces the @project_name@ placeholder with simple_weather when the application is built. The resulting calls to the functions in gettext and locale ensure that the application can use the translation files installed with it.

The translation files contain translations of the original user-visible text strings into other languages. These are generated from a single file that contains all the user-visible text for the application.

Handling Translation Files

In order to provide a translation of the application, a Portable Object Template (POT) file needs to be generated from all the files that contain user-visible text, including both source code and metadata files. The list of translatable files is the kept in the POTFILES file in the application’s po directory:

data/com.example.simple_weather.desktop.in
src/main.py
src/widgets.py

These files are searched for translatable text strings, which are extracted from the arguments of the _() function, when the simple_weather-update-po rule is run. This can be performed as part of the process of generating and updating Portable Object (PO) files for each of the languages that the application is translated into. You can run this rule on the command line from the repository root:

meson . _build
ninja -C _build simple_weather-update-po

This is something you should do after adding new user-visible strings to any of the translatable files in the application. The newly created or updated PO files can then be completed by translators before the application is built.

The LINGUAS file contains a list of the languages that the application aims to support. PO files will be generated for each of these languages. In this example, the list is very small, but other applications may contain extensive lists of languages for which translations are available.

The po/meson.build file contains the declaration that causes the necessary build rules to be generated:

i18n.gettext('simple_weather',
    preset: 'glib',
    args: [
        '--copyright-holder=Purism SPC',
        '--package-name="Simple Weather"',
        '--package-version=' + meson.project_version(),
        '--msgid-bugs-address=david.boddie@puri.sm'
    ]
)

When the application is built, any PO files are compiled to Machine Object (MO) files that will be installed with the application. These files are loaded at run-time when the application calls gettext.install to install the translation function, as described above.

Testing Translations

Different translations for the application can be tested easily if it is started from the command line. Use the LANGUAGE environment variable to specify the language shown in the user interface, as in these examples:

LANGUAGE=en_GB simple-weather
LANGUAGE=nb simple-weather

By default, the application will run in the current locale, so this approach can help developers to discover issues with translations without having to change their working environments.

Further Reading

The GNU gettext utilities manual contains detailed information about working with translations. The Python gettext module documentation also contains good background information specific to the Python language.