Development/Tutorials/Localization/i18n Build Systems: Difference between revisions

From KDE TechBase
No edit summary
m (Development/Tutorials/i18n Build Systems moved to Development/Tutorials/Localization/i18n Build Systems: moving into it's own "namespace" as per conversation the other day on irc with dhaumann and milliams)
(No difference)

Revision as of 05:20, 10 January 2007

Building KDE's l10n Module
Tutorial Series   Localization
Previous   Writing Applications With Localization in Mind
What's Next   n/a
Further Reading   n/a

Abstract

Now that your application is ready to be localized, we next look at how to incorporate the necessary mechanisms into the CMake build system of your application.

CMake and i18n

Warning
This section needs improvements: Please help us to

cleanup confusing sections and fix sections which contain a todo


The Translation Process

Assuming the build system is set up properly, the following outlines the process that occurs to translate your application.

Tip
If your application is in the KDE code repository, all this happens automatically. If your code is not in the KDE repository, you must perform these steps yourself. See the section below on distributing message catalogs.


Warning
This section needs improvements: Please help us to

cleanup confusing sections and fix sections which contain a todo


Periodically, the script kde-common/makemessages (a.k.a. scripty) runs on the KDE server. This program extracts all the i18n messages from your application per the messages targets in your Makefile.am's. The extracted messages are stored in template (.pot) files in the templates folder of the l10n module. See What Is Scripty for more information.

The KDE translation teams translate the messages and commit them into a messages folder corresponding to the language, which is further broken down by module. For example, German translated messages for konqueror are committed to l10n/de/messages/kdebase/konqueror.po.

When the l10n module is built, the .po files are compiled into a binary format for fast lookup and installed as .mo files to $KDEDIR/share/locale/xx/LC_MESSAGES/, where xx is the two-letter ISO 639 code for the language. These are called the message catalogs.

At runtime, the i18n(...) function, using the original string you coded, looks up the string in the message catalog of the user's desktop language and returns the translated string. If the message catalog is missing, or the specific string is not found, i18n(...) falls back to the original string in your code.

.desktop files in your project are handled separately. makemessages extracts strings, such as Name and Comment from the .desktop files and places them into a file named desktop_mmmm.pot, where mmmm is the module name, in the templates folder. Once translators have translated this file, makemessages inserts the translated strings back into the .desktop files. The list of strings extracted is in l10n/scripts/apply.cc. Here's the code that checks for them:

if (checkTag("Name", in, argc, argv, newFile))

   continue;

if (checkTag("Comment", in, argc, argv, newFile))

   continue;

if (checkTag("Language", in, argc, argv, newFile))

   continue;

if (checkTag("Keywords", in, argc, argv, newFile))

   continue;

if (checkTag("About", in, argc, argv, newFile))

   continue;

if (checkTag("Description", in, argc, argv, newFile))

   continue;

if (checkTag("GenericName", in, argc, argv, newFile))

   continue;

if (checkTag("Query", in, argc, argv, newFile))

   continue;

if (checkTag("ExtraNames", in, argc, argv, newFile))

   continue;

Runtime Loading Of Catalogs

To have translations show up properly in an application, the name of the .pot file must match the name of the message catalog (.mo) file that your application will read at runtime. But what name will be used at runtime?

In general, it will be the value returned by KGlobal::instance()->instanceName(). But what will that be? The general rules for standalone applications are:

  • the message catalog will default to the name of the application passed as the first argument to KAboutData
  • if your code calls KLocale::setMainCatalog(), that name will be used instead
  • if your code calls KLocale::insertCatalog(const QString&), the specified catalog will also be searched in addition to the main catalog

If your code does not call either of the KLocale methods mentioned above and your application is a plugin, it gets a little more complicated. If your code exports your plugin using the K_EXPORT_COMPONENT_FACTORY macro, like this:

K_EXPORT_COMPONENT_FACTORY( libkhtmlkttsdplugin,

 KGenericFactory<KHTMLPluginKTTSD>("khtmlkttsd") )

then the catalog name will be the name passed in the KGenericFactory constructor - in the example above that woudl be khtmlkttsd. This is because the macro creates a Kinstance for you with the specified name.

However, some classes, such as KTextEditor create a KPart containing your component and the name passed in the macro is not used. In this case, you should call KLocale::insertCatalog(const QString&) in the component's constructor.

If in doubt, the safest thing to do is to call KLocale::insertCatalog(const QString&).

Tip
If you're not using the export macro for your plugin, you probably should be. See the API documentation of KGenericFactory for more information.


Calling KLocale::insertCatalog(const QString&) if the catalog has already been inserted does nothing. However, calling KLocale::removeCatalog(const QString&) removes the catalog no matter how many times it has been inserted. Therefore, take care that you do not inadvertently remove the catalog when multiple components are sharing a single catalog. For example, the following sequence will break translations:

// Component A loads and uses catalog xyz by calling insertCatalogue("xyz"); // Component B loads and also uses the same catalog. insertCatalogue("xyz"); // component B unloads and calls removeCatalogue("xyz") // Component A now doesn't translate properly.

Notice that a sequence such as above will occur automatically if both components A and B are loaded using the K_EXPORT_COMPONENT_FACTORY macro and pass the same name argument to the KGenericFactory constructor.

Naming .pot Files

The name that you settle on for both the .pot file and catalog should be governed by where your code resides. If it is a component of another application and resides in that application's source tree, you'll want to share the main catalog of the application.

If is a component of another application but resides elsewhere in the code repository, you will probably have to create a separate .pot, but keep in mind that .pot filenames must be unique across all of KDE.

If it is a component of another application, but resides in the source tree of a second application, you should share with the second application. For example, the KDE text-to-speach daemon (KTTSD) includes a plugin for embedded Kate in kdeaccessibility/kttsd source tree. Since the plugin is not installed unless kttsd is installed, it shares its catalog with kttsd and calls KLocale::insertCatalog("kttsd") to do this.

Distributing Message Catalogs

Warning
This section needs improvements: Please help us to

cleanup confusing sections and fix sections which contain a todo


Note
The following improvements are needed in this section:

How can messages for .desktop files be extracted? How can translated .desktop messages be inserted back into .desktop files?

"make -f admin/Makefile.common package-messages" is obviously no longer correct


If your application is in the KDE code repository, you don't have to do anything to distribute message catalogs. Users will either download the l10n source and build it or install binaries prepared by packagers.

In addition, if your top-level folder and .pot file have the same name, the svn2dist script will automatically include .po files when you use it to make a source tarball for your app.

If your application is not in the KDE code repository, you have to extract the messages yourself and provide the translated PO files within your distribution. To extract the messages you need to have xgettext installed and extractrc from kdesdk has to be in your path.

Then do the following commands 8 in the base folder of your KDE application's source:

mkdir po
make -f admin/Makefile.common package-messages
echo po>>SUBDIRS
echo "POFILES = AUTO">po/Makefile.am

After that you will find a file <appname>.pot, which contains all messages of your application in the subfolder po.

Now you just have to find translators to translate the extracted messages into the various languages. As your application gets used by more people, you will find that translators will volunteer to do this. Translated PO files then have to be stored in the po folder with the naming scheme <languagecode>.po.

Technical details

The actual translation is done with the gettext package. It contains tools for extracting messages from source files and to handle changed messages, so that translators do not have to start over and over again. The extracted messages and the translations are stored in so called ``PO files using different encodings. These files are then compiled into a binary format (MO files) which then get installed.

All translations of a language have to be stored in the same encoding which is defined in the charset file. KLocale reads this file when constructed and uses this information to decode the translations.

Nowadays, UTF-8 is required as the PO files encoding in KDE code repository.

Handbooks

Handbooks, which are written in docbook format, are handled different from applications. The translated Handbooks are committed into the l10n module in the docs folder under each language. In addition, the docs folder is broken down by module and application. For example, the German Kate Handbook is committed to the l10n/de/docs/kdebase/kate/ folder. When compiled, the German Kate Handbook is installed to $KDEDIR/share/doc/HTML/de/kate/index.cache.bz2.

Note that it is up to each translation team to generate the translated Handbook when they feel it is complete.