Frameworks/Frameworks Localization Policy

From KDE Community Wiki

Introduction

User visible text provided by frameworks should be translated and adapted to the user local settings. Most frameworks use the KI18n tier 1 framework to handle their translations. The only exception is other tier 1 frameworks, which cannot depend on KI18n. These frameworks use the Qt translation system.

Marking user visible text

Nothing unusual for KI18n-translated frameworks, they must use the KI18n API.

Qt-translated frameworks must use the Qt API (QObject::tr() and QCoreApplication::translate()), but with one subtlety regarding plural handling:

Qt handles plural forms differently from KI18n. It does not allow to define two different English strings. Instead it expects the application to use an internal id for the source string and ship with a plural-only en.qm file containing singular and plural English strings. The KDE translation scripts can generate this file automatically, based on string metadata. For this to work, plural strings must be annotated with //~ singular <string> and //~ plural <string>.

Here is an example:

//~ singular %n day
//~ plural %n days
return tr("%n day(s)", 0, n);

Extracting user visible text

All frameworks use standard KDE Messages.sh files to extract user visible text into Gettext catalog source files (.po).

Even Qt-translated frameworks use .po files rather than Linguist source files (.ts). This make it possible for translators to use the same tools for all frameworks. There are two important differences though:

1. Instead of calling $XGETTEXT in their Messages.sh file, Qt-translated frameworks must call $EXTRACT_TR_STRINGS. $EXTRACT_TR_STRINGS is the path to the extract-tr-strings script, which uses lupdate and lconvert instead of xgettext. extract-tr-strings takes care of generating a plural-only en.po file from the plural annotations (using generate-en-po).

2. .po files for Qt-translated frameworks must be suffixed with _qt. This is necessary for KDE translation infrastructure. Translators need to be aware that they should not use any KI18n-specific feature, such as Transcript. Other translation scripts need to know this as well.

Compiling and installing translations

Frameworks released archives are shipped with translations for all languages. This ensure a framework archive is self-contained, making it easier to use for a third-party developer. This is different from the way we used to work in the KDE4 days, where kdelibs translations were not in the kdelibs release archive.

Translated files are integrated at release time by the release managers: they are not stored in the framework git repository.

po directory hierarchy

Translated files are integrated in the release archive using this standard directory hierarchy:

    po/
        <lang>/
            *.po
            scripts/ # Used by KI18n for some languages which require special treatments
                <domain>
                    <domain>.js
            docs/
                *.docbook
                man-*.<section>.docbook
                kioslave5/
                    <name>/
                        *.docbook
                kcontrol5/
                    <name>/
                        *.docbook
                khelpcenter5/
                    <name>/
                        *.docbook

Depending on the framework some files may or may not be there. But if they are, they are organized using this hierarchy.

Installation code

Even if the translated files are not in the framework git repository, the code responsible for installing them must be there. The functions which handle this usually work even if the po does not exist, so there is no need to guard them. If any code is used which depends on the existence of the po, this code must first check whether the directory exists, like so:

if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po")
    # Installation code goes here
endif()

Note: the IF uses the absolute path because according to CMake documentation IS_DIRECTORY only works when given absolute paths.

KI18n installation code

KI18n provides a ki18n_install() function. Similarly, KDocTools provides a kdoctools_install() function. These functions take one argument: the path to the po directory.

This is a typical example for a framework which uses KI18n and KDocTools:

ki18n_install(po)
kdoctools_install(po)

Qt installation code

ECM provides the ecm_install_po_files_as_qm() function to install the translated .po files. It works the same way ki18n_install() works:

if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po")
    ecm_install_po_files_as_qm(po) 
endif()

Loading translations

KI18n

For KI18n to load translations from the correct compiled catalog file, the preprocessor variable TRANSLATION_DOMAIN must be set to the catalog name. The simplest way to define this variable is to define this variable in the root CMakeLists.txt, like so:

add_definition(TRANSLATION_DOMAIN \"foo5\")

Qt

The classic way for a Qt-translated library to provide a translation is to install a .qm file and let the user code loads it, but this is error-prone. To avoid this, ECM can generate a source file which will load the .qm file at startup using Q_COREAPP_STARTUP_FUNCTION().

The ecm_create_qm_loader() function provides this feature. It accepts two arguments: a variable name where it stores the path to the generated source file and the catalog name. You use it like this:

ecm_create_qm_loader(bar_QM_LOADER bar5_qt)

set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
    ${bar_QM_LOADER}
)

You can also reuse the existing source variable:

set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
)

ecm_create_qm_loader(bar_SRCS bar5_qt)