<< Creating forms   

Translation and i18n


What you need

Exponential 3 requires two programs to create and maintain translations, ezlupdate and linguist. These programs are based on the same tools from the Qt toolkit by ez.no/developer.

The linguist is unmodified from the Qt original, so you can also get it from other sources, such as RPMs. If you run Linux or a similar system, you will find the linguist as part of qt-3.*, qt-devel-3.* or a similarly named package.

The ezlupdate program is modified to make it understand Exponential files. You will find the source code and build instructions in support/lupdate-ezpublish3 in the Exponential distribution.


Making translations

Note: The database content of Exponential is not translated, meaning that for instance class attributes are shown in English wherever this information is visible. This is normally only in the admin interface. The classes provided with Exponential are merely examples provided to get you up and running quickly. You are encouraged to extend and/or replace these classes with your own classes. If you need a non-English administrative interface, you can translate your classes in the "Setup" section. (The translation system covers content in templates and PHP code.)

You must decide the locale code of your language. Exponential 3 uses locale codes on the form aaa-AA, where the 3 first lowercase letters describe the language, while the last two uppercase letter describe the country in which the language is spoken. For instance, English as it is spoken in Great Britain would be eng-GB, while US English is eng-US.

Countries are specified by the ISO 3166 Country Code: http://www.iso.ch/iso/en/prods-services/iso3166ma/index.html

Language is specified by the ISO 639 Language Code: http://www.w3.org/WAI/ER/IG/ert/iso639.htm

You can also create a variation of a locale, you will for instance find two variations of nor-NO, nor-NO@intl and nor-NO@spraakraad, that are slight modifications of the original.

For the rest of this part, I assume you are translating nor-NO.

Copy share/locale/eng-GB.ini to share/locale/nor-NO.ini. Edit this file with a text editor. Here you set locale details such as time formats, currency and the names of the week days.

Now, enter the main Exponential directory in a terminal and type

bin/linux/ezlupdate -v nor-NO

(Provided that nor-NO is your locale, of course. -v is for verbose, showing messages about what happens. You can also use -vv for extra verbose output, or omit it for silent behavior. Run bin/linux/ezlupdate -h for an explanation of the arguments.

You will now find a translation in share/translations/nor-NO/translation.ts or similar for other locales. Open this file in linguist and do the translation.

You will find documentation on linguist on Trolltechs page: http://doc.trolltech.com/3.0/linguist-manual-3.html

When you are done translating in linguist (or earlier, if you want to test part of a translation), open settings/site.ini. Go to the section [RegionalSettings] and set Locale=nor-NO. Also set TextTranslation=enabled, or the default (eng-GB) will be used.

Sample entry in settings/site.ini:

[RegionalSettings]
Locale=nor-NO
TextTranslation=enabled

If you run a multi-language site, you will also need to translate content objects. Set ContentObjectLocale=nor-NO if you want the default language to be nor-NO. Important: Before you do this, you should make sure that the new locale is added to the system. Go to Setup -> Translations and add your locale here if it does not exist. You should also translate the most used objects of your site before you change the ContentObjectLocale. To translate an object, edit it, and click "Edit" under "Translations" in the right-hand menu.

To distribute your translation, create a compressed archive, for instance .zip or tar.gz, of these two files:
share/locale/nor-NO.ini
share/translations/nor-NO/translation.ts

You could for instance do it like this:

tar -zcvf nor-NO.tar.gz \
 share/locale/nor-NO.ini share/translations/nor-NO/translation.ts

Installing translations

To install a translation, unpack the package in the Exponential root directory. Make sure that the files go in the right place. The file translation.ts should be placed in the share/translations/[your language]/ directory, while the ini-file should be placed in share/locale. (If you unpack in the Exponential root directory, this should be correct by default.)

Next, set the appropriate entries in settings/site.ini or the corresponding override file. These are the keys you need to change to enable the translation:

[RegionalSettings]
Locale=nor-NO
TextTranslation=enabled
TranslationCache=enabled

After you have enabled the translation, the translation cache must be generated. This takes some time, so the first page view will be slow. After that, the site should be almost as fast as with the eng-GB translation. Note: The cache generation will be very slow if you don't have the PHP mbstring extension. If you get timeouts, that is probably the cause. You can disable the translation cache by setting TranslationCache=disabled. This will work, but will be a lot slower than using the cache.


Making i18n-friendly templates

All user-visible strings in templates should be embedded in the i18n operator. All such strings can be translated in the linguist. The i18n operator is used like this:

<b>{"This text can be translated"|i18n("design/mydesign/path")}</b>
<input type="submit" name="MyButton"
 value="{'My Cool Button'|i18n('design/mydesign/path')}" />
{include uri="design:gui/button.tpl" name=remove id_name=RemoveButton
 value="Click to remove"|i18n("design/mydesign/path")}

The path in this example, design/mydesign/path, is used as a context. It is by this context that the strings are sorted in the linguist. The context does not have to be the actual path to the template file in question, but this can be useful. See the templates of the admin interface for examples.

You can also add an optional comment as a second argument to the i18n operator. Such comments are shown in the linguist. Use this for instance to explain how a word is used, this can be helpful for translators.

If you need to add variables, use the third argument to the i18n operator.

<b>{"The node %1 is a child of %2."|i18n("design/mydesign/path",,
 array($node.name,$parent_node.name))}</b>
<b>{"The node %node is a child of %parent_node."|i18n("design/mydesign/path",,
 hash("%node",$node.name,"%parent_node",$parent_node.name))}</b>

As the examples show, variables can be used in two ways. You can use the keys %1 to %9, and an array of values to replace them with. You can also use any key you want, as long as you specify them in a hash. Note that automatic translators, such as the Bork translator, may not treat the keys correctly.

The i18n operator calls the ezi18n() function in kernel/common/i18n.php. If translation is not enabled in settings/site.ini, then it will simply output the source it is given. Otherwise, it will look up the string in the corresponding .ts file. If a translation is not found, it will use the automatic Bork translation. This is useful, as it makes it easy to spot strings that don't have the i18n operator.

In extensions you should use the x18n operator instead of i18n. The only difference is that x18n takes an additional parameter before the others: The name of the extension.

<b>{"The node %1 is a child of %2."|x18n("myextension","design/mydesign/path",,
 array($node.name,$parent_node.name))}</b>

Making i18n-friendly PHP code

All user-visible strings in PHP-code should be embedded in the ezi18n() function. It is similar to the template operator, except that it takes the source as the second argument. For extensions, use ezx18n(). As with the template operator, you can also use arguments. Use either a plain array and the keys %1 to %9, or an associative array where you specify the keys yourself.

$myObject->myFunction( ezi18n( 'kernel/classes/datatypes',
                               'Input is not integer.',
                               'Comment: eZIntegerType' ) );

$myObject->myFunction( ezx18n( 'myextension'
                               'modules/mymodule',
                               'Input is not integer.',
                               'Comment: eZIntegerType' ) );

$myObject->myFunction( ezi18n( 'kernel/classes/datatypes',
                               'The node %1 is a child of %2.',
                               null,
                               array( $node->name(), $parent_node->name() ) ) );

$myObject->myFunction( ezx18n( 'myextension'
                               'modules/mymodule',
                               'The node %node is a child of %parent_node.',
                               null,
                               array( "%node" => $node->name(),
                                      "%parent_node" => $parent_node->name() ) ) );

Exponential