Translating Applications

Applications developed using GNOME Builder, or using Meson and Ninja as build tools, can be easily written with translation in mind. This document describes aspects of the development and translation process that should help you to make your application available in multiple languages.

We begin by looking at the tasks that need to be done when making an application ready for translation.

Overview

For an application to be translatable into multiple languages, it needs three things:

  1. Source code that distinguishes between user-visible, translatable text and non-translatable text. See Handling User-Visible Text for ways to do this.
  2. A way to make the text available to translators so that it can be translated. This can be part of a build system or a set of tools that are run separately.
  3. A build system that takes the translations and bundles them with the application so that they can be used at run-time.

With these in place, there is a framework for a translation process.

Handling User-Visible Text

In Python applications, user-visible text in labels and other widgets is wrapped in function calls, using the _() translation function. This special underscore method is used by tools to identify the translatable text in source files.

In the following code from the Simple Weather example, only the user-visible text, Remove Place, 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 unchanged 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 when the application is built:

# 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.

Generating a POT File

If you want to generate a POT file without updating the existing translation files, you can run the simple_weather-pot rule that is generated by Meson when an application’s build is configured. This can be done using the Ninja build tool on the command line from the repository root. For example, this is how the Simple Weather example’s initial POT file was generated:

meson . _build
ninja -C _build simple_weather-pot

In this case, running these commands causes the po/simple_weather.pot file to be created if it does not already exist. Otherwise, the existing file is updated with any new strings that are found in the translatable files.

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:

# Try the British English translation
LANGUAGE=en_GB simple-weather
# Try the Norwegian Bokmål translation
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.