Frameworks/Frameworks Localization Policy: Difference between revisions

From KDE Community Wiki
 
(8 intermediate revisions by 3 users not shown)
Line 7: Line 7:
Nothing unusual for KI18n-translated frameworks, they must use the KI18n API.
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-translated frameworks must use the Qt API (<tt>QObject::tr()</tt> and <tt>QCoreApplication::translate()</tt>), 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 [http://doc-snapshot.qt-project.org/qt5-stable/i18n-source-translation.html#adding-meta-data-to-strings string metadata]. For this to work, plural strings must be annotated with ''//~ singular <string>'' and ''//~ plural <string>''.
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 [http://doc-snapshot.qt-project.org/qt5-stable/i18n-source-translation.html#adding-meta-data-to-strings string metadata]. For this to work, plural strings must be annotated with <tt>//~ singular <string></tt> and <tt>//~ plural <string></tt>.


Here is an example:
Here is an example:
Line 21: Line 21:
== Extracting user visible text ==
== Extracting user visible text ==


Frameworks use standard KDE ''[http://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems#handling_i18n_in_KDE.27s_subversion_repository Messages.sh]'' files to extract user visible text into .po files.
All frameworks use standard KDE <tt>[http://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems#handling_i18n_in_KDE.27s_subversion_repository Messages.sh]</tt> files to extract user visible text into Gettext catalog source files (<tt>.po</tt>).


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


.po files are used for all frameworks, including those which use the Qt translation system. The only difference is that instead of calling ''$XGETTEXT'', they must call ''$EXTRACT_TR_STRINGS''. ''$EXTRACT_TR_STRINGS'' is the path to the [http://websvn.kde.org/trunk/l10n-kf5/scripts/extract-tr-strings?view=markup 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 [http://websvn.kde.org/trunk/l10n-kf5/scripts/generate-en-po?view=markup generate-en-po]).
1. Instead of calling <tt>$XGETTEXT</tt> in their <tt>Messages.sh</tt> file, Qt-translated frameworks must call <tt>$EXTRACT_TR_STRINGS</tt>. <tt>$EXTRACT_TR_STRINGS</tt> is the path to the [https://invent.kde.org/sysadmin/l10n-scripty/-/blob/master/extract-tr-strings extract-tr-strings] script, which uses <tt>lupdate</tt> and <tt>lconvert</tt> instead of <tt>xgettext</tt>. <tt>extract-tr-strings</tt> takes care of generating a plural-only en.po file from the plural annotations (using <tt>[https://invent.kde.org/sysadmin/l10n-scripty/-/blob/master/generate-en-po generate-en-po]</tt>).


.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 feature such as transcript. Other  [http://websvn.kde.org/trunk/l10n-kf5/scripts/ translation scripts] need to know this as well.
2. .po files for Qt-translated frameworks must be suffixed with <tt>_qt</tt>. 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  [https://invent.kde.org/sysadmin/l10n-scripty/-/tree/master/ translation scripts] need to know this as well.


== Loading translations ==
== Compiling and installing translations ==
 
=== KI18n ===
 
For KI18n to load translations from the framework catalog, the preprocessor variable ''TRANSLATION_DOMAIN'' must be set to the catalog name. The simplest way to define this variable is to do so in the root CMakeLists.txt, like so:
 
<syntaxhighlight lang="cmake">
add_definition(TRANSLATION_DOMAIN \"foo5\")
</syntaxhighlight>
 
=== 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:
 
<syntaxhighlight lang="cmake">
ecm_create_qm_loader(bar_QM_LOADER bar5_qt)
 
set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
    ${bar_QM_LOADER}
)
</syntaxhighlight>
 
You can also reuse the existing source variable:
 
<syntaxhighlight lang="cmake">
set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
)
 
ecm_create_qm_loader(bar_SRCS bar)
</syntaxhighlight>
 
== 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.
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.
Line 103: Line 64:
=== Installation code ===
=== Installation code ===


Even if the translated files are not in the framework git repository. The code responsible for installing them must be there. To avoid errors while building from git (where the ''po'' directory does not exist), this code must first check whether the directory exists, like so:
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 <tt>po</tt> does not exist, so there is no need to guard them. If any code is used which depends on the existence of the <tt>po</tt>, this code must first check whether the directory exists, like so:


<syntaxhighlight lang="cmake">
<syntaxhighlight lang="cmake">
Line 111: Line 72:
</syntaxhighlight>
</syntaxhighlight>


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


=== KI18n installation code ===
=== 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.
KI18n provides a <tt>ki18n_install()</tt> function. Similarly, KDocTools provides a <tt>kdoctools_install()</tt> function. These functions take one argument: the path to the <tt>po</tt> directory.


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


<syntaxhighlight lang="cmake">
<syntaxhighlight lang="cmake">
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po")
ki18n_install(po)
    ki18n_install(po)
kdoctools_install(po)
    kdoctools_install(po)
endif()
</syntaxhighlight>
</syntaxhighlight>


=== Qt installation code ===
=== 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:
ECM provides the <tt>ecm_install_po_files_as_qm()</tt> function to install the translated <tt>.po</tt> files. It works the same way <tt>ki18n_install()</tt> works:


<syntaxhighlight lang="cmake">
<syntaxhighlight lang="cmake">
Line 134: Line 93:
     ecm_install_po_files_as_qm(po)  
     ecm_install_po_files_as_qm(po)  
endif()
endif()
</syntaxhighlight>
== Loading translations ==
=== KI18n ===
For KI18n to load translations from the correct compiled catalog file, the preprocessor variable <tt>TRANSLATION_DOMAIN</tt> must be set to the catalog name. The simplest way to define this variable is to define this variable in the root <tt>CMakeLists.txt</tt>, like so:
<syntaxhighlight lang="cmake">
add_definition(TRANSLATION_DOMAIN \"foo5\")
</syntaxhighlight>
=== 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 <tt>[http://qt-project.org/doc/qt-5/qcoreapplication.html#Q_COREAPP_STARTUP_FUNCTION Q_COREAPP_STARTUP_FUNCTION()]</tt>.
The <tt>ecm_create_qm_loader()</tt> 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:
<syntaxhighlight lang="cmake">
ecm_create_qm_loader(bar_QM_LOADER bar5_qt)
set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
    ${bar_QM_LOADER}
)
</syntaxhighlight>
You can also reuse the existing source variable:
<syntaxhighlight lang="cmake">
set(bar_SRCS
    manager.cpp
    engine.cpp
    ...
)
ecm_create_qm_loader(bar_SRCS bar5_qt)
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 18:10, 10 January 2023

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)