| Home · All Classes · Main Classes · Deprecated |
This document provides guidelines on developing locale-aware applications for the MeeGo Touch UI Framework. Developers, translators, and user interface designers are the main target audience of this document.
Generally, as the MeeGo Touch UI Framework uses Qt as it’s base, some of the Qt internationalisation practices also apply. However, the MeeGo Touch UI Framework provides additional functionalities, hence it is recommended that developers always use MeeGo Touch UI Framework classes to achieve full internationalisation support in their applications.
This document discusses several general internationalisation guidelines for user interface designers. They are general enough and may also be applicable in another projects and vice versa.
Below is the list of officially supported languages both for writing systems and text rendering:
Although the implementation contains support for several calendar systems, officially the MeeGo Touch UI Framework currently supports only Gregorian and Islamic calendar.
The MeeGo Touch UI locale system uses the following main settings:
| gconf key | description | example value |
|---|---|---|
| /meegotouch/i18n/language | main language setting | en_US |
| /meegotouch/i18n/lc_time | for date, time, and calendar | ar@calendar=islamic |
| /meegotouch/i18n/lc_collate | for sorting | de_DE@collation=phonebook |
| /meegotouch/i18n/lc_numeric | formatting of numbers | hi |
| /meegotouch/i18n/lc_monetary | for formatting of amounts of money | fi_FI@currency=EUR |
Each of these settings can be set to a ICU locale specifier as described in the ICU User Guide.
The value of /meegotouch/i18n/language (“Language”) tells the locale system about the UI language of the device. This affects all UI texts, writing direction and widget layouts (It does not affect the user content, i.e. it does not affect things like the browser content language etc.).
“Language” is also used as the default for time, date, calendar, sorting, number formatting, and currency formatting if the more specific settings are empty.
“LcTime” selects how time and date are formatted. If it is left empty the value of “Language” is used. Examples: ar@calendar=islamic means to format date and time the Arabic way using Islamic calendar, ar@calendar=gregorian would use Arabic formatting but with Gregorian calendar, fi_FI would use Finnish formatting rules for time and date.
“LcCollate” selects how textual data is sorted. If it is left empty the value of “Language” is used. Examples: de_DE@collation=phonebook sorts the way German phone books are sorted, de_DE@collation=standard (or de_DE ) sorts the “normal” way German dictionaries are sorted. zh_CN@collation=pinyin sorts Chinese according to the pinyin phonetics, zh_CN@collation=stroke sorts Chinese according to the stroke count of the characters.
“LcNumeric” selects how numbers are formatted. If it is left empty the value of “Language” is used. Examples: hi formats numbers according to the rules for the Hindi language, using localized digits, i.e. Devanagari numerals (०,१,२,३,४,५,६,७,८,९), ar formats numbers using Eastern Arabic numerals (٠,١,٢,٣,٤,٥,٦,٧,٨,٩), de_CH formats the Swiss German way with apostrophes as thousands separators (12'345'670.89), and en_US formats the American English way (12,345,670.89).
“LcMonetary” selects how currency amounts are formatted. If it is left empty the value of “Language” is used.
A MeeGo Touch UI application is normally using the system default MLocale object;
MLocale myLocale; // get the current locale
If the application needs to use different locale settings than the system locale, then a MLocale is created by making an instance of a MLocale object:
MLocale locale ("fi_FI");
where "fi" and "FI" mean to use Finnish language in Finland.
To do collation, get the comparator with MLocale::collator() method or create it MCollator() for default locale collator. This object acts as a functor and can be passed to Qt's qSort() or any other sorting methods. The comparator basically does the comparison of two items as "lessThan" function and returns true if the first parameter should come before the second one.
If other collation system than the locale's default is needed, then use setCollation() method before getting the comparator. After all are set then the sorting can be started by using Qt's qSort() function.
To use the default collation:
QStringList sl; // This contains the strings which need to be sorted ... MLocale myLocale; // get the current locale MCollator c = myLocale.collator(); // getting the comparator ... qSort(sl.begin, sl.end, c); // start to sort! ...
To use the different collation:
QStringList sl; // This contains the strings which need to be sorted ... MLocale myLocale; // get the current locale myLocale.setCollation (MLocale::PhonebookCollation); // sort using German's phonebook sorting order MCollator c = myLocale.collator(); // getting the comparator ... qSort(sl.begin, sl.end, c); // start to sort! ...
MeeGo Touch UI comes with several formatters for several data types. (Almost) Always use the formatters before displaying locale-dependant data to the screen to achieve uniformity and consistency of the data presentation.
To format a number use formatNumber() method. It works with qlonglong, short, int, double, and float data types.
... MLocale myLocale; // get the current locale ... int number = 1234; QString formattedNumber = myLocale.formatNumber(number); // format the number, the result is in QString ...
Use formatDateTime() to format the date and time.
... MLocale myLocale; // get the current locale ... QDateTime now = QDateTime::currentDateTime(); QString formattedDateTime = myLocale.formatDateTime(now); ...
As mentioned previously, date format can also be affected by the device language, region, and calendar settings.
... MLocale myLocale; // get the current locale ... QDateTime now = QDateTime::currentDateTime(); QString formattedDateTime = myLocale.formatDateTime(now, MLocale::IslamicCalendar); // format using IslamicCalendar ...
Custom formatting is supported with ISO-14652 format (also used in libc's strftime ) by using formatDateTime() .
formatPercent() is used to format a percentage value.
... MLocale myLocale; // get the current locale ... double percent = 0.29; // = 29% int decimals = 2; QString formattedPercentage = myLocale.formatPercent(percent, decimals); ...
To format any amount of money with a currency, use formatCurrency() method.
... MLocale myLocale; // get the current locale ... double money = 150.40; QString currency = "EUR"; QString formattedCurrency = myLocale.formatCurrency(money, currency); ...
Use MName class to populate the name information, and use MLocale::formatName() method to format the name.
Use MAddress class to populate the address information, and use format() method to format the name using the format specified as parameter.
MLocale provides Gregorian as well as non-Gregorian calendar support. Use formatDateTime() to format the date according to the custom of the selected calendar. To set another calendar than the default Gregorian calendar, use setCalendarType() method.
MLocale myLocale; // get the current locale ... myLocale.setCalendarType(MLocale::IslamicCalendar); // set the calendar to use Islamic calendar ...
Further date manipulations can be done using MCalendar object.
On general case iterating words in a string is not possible by searching whitespace: e.g. thai language just concatenates words and in chinese language a character is a word. For this need a MBreakIterator class can be used. It is constructed with a locale and a string and provides an interface for iterating word boundaries. Following example iterates word boundaries from beginning to the end:
MLocale myLocale; // current locale QString text("this is text to be iterated"); MBreakIterator iterator(myLocale, text, MBreakIterator::WordIterator); while (iterator.hasNext()) { int next = iterator.next(); ... }
The MeeGo Touch UI Framework can use both the Qt standard tr() method or the idbased qtTrId() method. For the Nokia internal localisation process, only qtTrId() should be used. Other parties using logical IDs instead of engineering English IDs for the UI messages might also find qtTrId() useful.
Contrary to the approach of using engineering English as the message ids, which is commonly used in the open source community, the logical ID approach uses message ids which are guaranteed to be globally unique.
With qtTrId(), the logical ID is an argument of the function and the engineering English is supplied as a comment above. See the example in the Qt documentation of qtTrId(), reproduced here for convenience:
//% "%Ln fooish bar(s) found.\n" //% "Do you want to continue?" QString text = qtTrId("qtn_foo_bar", n);
If no translation files at all are available, qtTrId() would return the logical ID which looks pretty ugly and is not nice for testing the user interface.
But from code which uses qtTrId() with the special engineering English comments, .qm files with engineering English translations can be generated automatically. This can be done as follows:
Create a subdirectory translations/ somewhere in your source tree. In that folder, create a translations.pro file with the contents:
LANGUAGES = # empty if only engineering English is needed!
CATALOGNAME = foobar # what ever catalog name you want to use
SOURCEDIR = $$PWD/.. # more then one directory is possible
TRANSLATIONDIR = $$PWD
include($$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_defines.prf)
include($$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_translations.prf)
Introducing the extra translations/translations.pro file makes it necessary to add the translations/ directory to the list of sub-directories in a .pro file higher up in the directory hierarchy. I.e. you usually need something like
SUBDIRS = \
src \
translations \
...
After doing that, a foobar.ts and a foobar.qm file which contain the engineering English translations will be automatically generated when calling “make” for your project. And “make install” will install foobar.qm to the usual place as described in section Translation data storage.
Minor detail: with the above setup, when creating foobar.qm from foobar.ts, lrelease uses the option -markuntranslated to prepend the string “!! ” at the beginning of each engineering English string. I.e. if the engineering English string in the source code is “Hello”, it will be written as “!! Hello” into the foobar.qm file and will be displayed as “!! Hello” at runtime. This is to make it obvious that engineering English for testing purposes is displayed and not the “real” English which is done by the translators.
To setup the translation system using qtTrId(), you can use the following code snippets in your main function:
By default, the system uses the name of the application executable (without the directory path) as the name for the file of the translation catalog and loads this catalog automatically. So you can start using qtTrId() immediately:
... int main(int argc, char** argv){ ... MApplication app (argc, argv); ... //% "hello" qtTrId("xxx_hello"); ... } ...
In the above example, if the application executable is for example /usr/bin/foo, i.e. the base file name of the executable is foo, the translation catalog <top-directory>/foo_<locale-code>.qm will be used.
If you need to specify a file for the translation catalog wich differs from the name of the executable you can use the optional third parameter appIdentifier of the MApplication constructor as in this example:
... int main(int argc, char** argv){ ... MApplication application(argc, argv, "bar"); ... //% "hello" qtTrId("xxx_hello"); ... } ...
In the above example, the translation catalog <top-directory>/bar_<locale-code>.qm will be used no matter what the file name of the application executable is.
But note that the appIdentifier is used not only for localization files but also for themes. I.e. changing the appIdentifier will also affect themes. Usually this should be no problem because in most cases these should be kept the same, if possible.
When it is not possible to use the appIdentifier to specify the translation catalog which should be loaded, one can use the installTrCatalog() function to load a translation catalog with a completely different name as in this example:
... int main(int argc, char** argv){ ... MApplication app (argc, argv); // create a MLocale. Without parameters in the constructor, // it gets a copy of the system default locale, which may already // have some translation catalogs, for example it usually already // has the “common” translation catalog. MLocale locale; // add a different translation catalog for this locale. // This catalog is added to the list of already loaded catalogs. // Catalogs loaded last are used with highest priority. locale.installTrCatalog("othercatalog"); // the order is important, set the locale as the default *after* // installing the translation catalog: MLocale::setDefault(locale); ... // Now the following call to qtTrId() will use messages // from the translation catalog file othercatalog_<locale-code>.qm // with highest priority: //% "hello" qtTrId("xxx_hello"); ... } ...
To setup your translation using the tr() method known from regular Qt programs, you can use the installTrCatalog() method as in the following example:
... int main(int argc, char** argv){ ... MApplication app (argc, argv); // create a MLocale. Without parameters in the constructor, // it gets a copy of the system default locale, which may already // have some translation catalogs, for example it usually already // has the “common” translation catalog. MLocale locale; // install the catalog for use with tr(). It is probably // a good idea to use the application name for "mycatalog": locale.installTrCatalog("mycatalog"); // Make the locale the default to enable the message catalog just // installed above for tr(). The order is important, do this *after* // installing the translation catalog: MLocale::setDefault(locale); ... // Now you can use tr() as in regular Qt programs: tr("hello"); ... } ...
Let’s say there is an MApplication “foo” which uses a library “libbar”. And the library “libbar” also uses the MeeGo Touch Framwork and has some translations of its own which are in files like libbar_<locale_name>.qm.
Now the question is how to load the “libbar” translation catalog.
On way to do it would be to let the application “foo” load it, i.e. “foo” would do something like
... int main(int argc, char** argv){ ... MApplication app (argc, argv); ... // translations from the “foo” translation catalog // can already be used without any setup when the binary // name is identical to the catalog name: //% "hello" qtTrId("qtn_foo_hello"); ... // get a copy of the system default locale // which already has the translation catalogs for “common” and “foo” // installed:create a MLocale. Without parameters in the constructor, MLocale locale; // add a the “libbar” translation catalog: locale.installTrCatalog("libbar"); // set the locale with the “libbar” catalog added as the // new system default: MLocale::setDefault(locale); ... // and a call of a function in the library “libbar” // may use messages from the “libbar” translation catalog: ... libbar_some_function(); ... } ...
The disadvantage of this way of doing it is that the application “foo” needs to know how the translation catalog of “libbar” is called and maybe even require an packages like “libbar-l10n-*”. That creates unnecessary dependencies between “foo” and “libbar”. To avoid this, the recommended way of loading the “libbar” translations is letting the “libbar” library take care of this. I.e. “libbar” somewhere has an initialization function like
...
void libbar_init() {
...
MLocale locale; // get copy of system locale
locale.installTrCatalog("libbar"); // add a the “libbar” translation catalog
MLocale::setDefault(locale); // set new system default with “libbar” catalog added
...
// other initialization stuff if necessary
...
}
...
The application “foo” then does not have to load the translations for “libbar” and only does:
... int main(int argc, char** argv){ ... MApplication app (argc, argv); ... // translations from the “foo” translation catalog // can already be used without any setup when the binary // name is identical to the catalog name: //% "hello" qtTrId("qtn_foo_hello"); ... // initialize “libbar” which also installs the “libbar” // catalog into the default system locale: libbar_init(); // now a call of a function in the library “libbar” // may use messages from the “libbar” translation catalog: ... libbar_some_function(); ... } ...
Instead of having a public initialization function in “libbar” one could also make “libbar” call some internal initialization code when any public function of “libbar” is used for the first time, i.e. one could design “libbar” to make the following code already work:
... int main(int argc, char** argv){ ... MApplication app (argc, argv); ... // translations from the “foo” translation catalog // can already be used without any setup when the binary // name is identical to the catalog name: //% "hello" qtTrId("qtn_foo_hello"); ... // Calling any public function in the library “libbar” // triggers installation of the “libbar” translation catalog // into the default system locale and “libbar” can then // use messages from its translation catalog: ... libbar_some_function(); ... } ...
Whether a special initialization function is used or not, the important thing is that “libbar” does the installing of its translation catalog into the system default locale. The application “foo” then does not need to know how the catalog is called and does not need to require translation packages of “libbar”. “libbar” should require its translation package of course. But this way of doing it reduces the dependencies between “foo” and “libbar”.
qtTrId() is used to translate the UI messages.
QString qtTrId ( const char * id, int n = -1 )
id is the logical name for a UI message which needs to be translated. If n >= 0, all occurrences of %Ln or %n in the resulting string are replaced with a decimal representation of n. In addition, depending on n’s value, the translation text may vary.
The difference between %Ln and %n is that %Ln may be replaced using localized numerals like Arabic-Indic numerals in Arabic locales whereas %n is always replaced using Arabic (= Western) numerals.
qtTrId() is useful only if the project is using logical names as the UI message. If your project is using English string then use tr() instead.
The positional parameters %1 to %99 and %L1 to %L99 which are used in QString can also be used in translations for the MeeGo Touch UI Framework. For a description of %L1 to %L99 see also the documentation of QLocale.
For parameters which are integer numbers, %L1 to %L99 is a better choice than %1 to %99 because %L1 to %L99 automatically do locale specific number formatting which achieves the same results as using MLocale’s formatNumber() for the current system locale.
Let’s illustrate the behaviour in detail with an example. We assume that an application is running with the language locale settings set to Arabic to get Arabic translations (for example if the gconf key “/meegotouch/i18n/language” is set to “ar”) and the numeric locale settings are also set to Arabic to get Arabic number formatting (for example if the gconf key “/meegotouch/i18n/lc_numeric” is also set to “ar” or is empty and this the numeric locale settings are inherited from the language settings).
Now a message id is translated using qtTrId("xx_some_message_id"). And let’s assume that the Arabic translation of this happens to be “%L1 %2”.
The following example code illustrates how numbers used as parameters would be formatted in that case:
QString translation = qtTrId("xx_some_message_id"); // “translation” now contains “%L1 %2”. translation.arg("123456.7").arg("123456.7"); // The line above returns “123456.7 123456.7”. // // I.e. if strings are used as the arguments // there is no difference in the behaviour of %L1 and %2. translation.arg(123456).arg(123456); // The line above returns “١٢٣٬٤٥٧ 123457” // // Here the arguments are numbers and we can see the // difference: In case of %L1 the number is formatted // by using QLocale which is set by MLocale to the numeric // settings of MLocale. Therefore, %L1 is replaced by // Arabic-Indic numerals here. However, %2 is still replaced // by Western-Arabic numerals, and has no thousands separators either. // In English locale the replacement for %2 would be “123457” // and not “123,456”, the place holders %1 to %99 // never do any automatic locale specific number formatting. translation.arg(123456.7, 0, 'g', 10).arg(123456.7, 0, 'g', 10); // The line above returns “١٢٣٬٤٥٦٫٧ 123456.7” MLocale locale; translation.arg(locale.formatNumber(123456.7)).arg(locale.formatNumber(123456.7)); // The line above returns “١٢٣٬٤٥٦٫٧ ١٢٣٬٤٥٦٫٧” // // Here the number formatting is done using MLocale. // locale.formatNumber(123456.7) already returns a QString // containing “١٢٣٬٤٥٦٫٧”, i.e. the arguments used for %L1 // and %2 in the translations are not numbers but QStrings. // Therefore, no further formatting is done by %L1 and %2.
Passing an integer number as the argument from L1 and letting QLocale format that number always gives the same result as passing the the result of formatNumber() using the system MLocale for that integer. The reason is that the MeegoTouch framework always sets QLocale to the value of the numeric setting of the system default MLocale and both the number formatter in MLocale and the number formatter in QLocale use the CLDR data for the number format.
Message ids do not have to indicate whether the translation for this id contains place holders or not. For example if the message id is “xx_greet_user" and the English translation is “Hello 1”, then the following code works fine:
// The following returns “Hello Joe” if the translation is “Hello %1”. // If no translation can be found it returns “xx_greet_user”: qTrId("xx_greet_user").arg("Joe");
It is possible to include place holders in message ids, for example if the message id for the translation were “xx_greet_user_1”, the following code would work just as well:
// The following returns “Hello Joe” if the translation is “Hello %1” // If no translation can be found it returns “xx_greet_user_Joe”: qTrId("xx_greet_user_%1").arg("Joe");
So it doesn’t matter whether a message id for a translation which contains place holders contains these place holders itself or not.
Although place holders in message ids are possible, the message ids used by MeeGo Touch never contain any place holders. The message ids used in MeeGo Touch only use lowercase letters from “a” to “z” and underscores “_”.
This is perfectly OK and does not cause any problems for translations which use place holders.
In a plural message, pass the number to select the correct plural translation as the second argument n in qtTrId() (This works the same way with qtTrId() as it does with tr()).
int n = messages.count(); //% "%Ln message(s) saved" showMessage(qtTrId("xx_messages_saved", n));
Usually, the number of different translations used for plural, among which the parameter “n” selects the appropriate one, depends on the language, see also the “Qt Translation Rules for Plurals” and the “CLDR Language Plural Rules”.
However, for the Meego Touch UI framework, it has been decided to use only 2 plural forms and only English rules. I.e. the .ts translation files used by the Meego Touch UI framework always contain only two different translations for plural and are always marked in the xml as English language translation files, even if the language is not English.
For example if a .ts file for English translation contains something like
<?xml version="1.0" encoding="UTF-8"?> <TS language="en" version="3.0"> <context> <name>message_context</name> ... <message id="xx_amount_events" numerus="yes"> <source>%Ln event</source> <translation> <numerusform>%Ln event</numerusform> <numerusform>%Ln events</numerusform> </translation> </message> ... <message id="xx_time_day" numerus="yes"> <source>%Ln day</source> <translation> <numerusform>%Ln day</numerusform> <numerusform>%Ln days</numerusform> </translation> </message> ... </context> </TS>
the corresponding Russian translation file might contain something like:
<?xml version="1.0" encoding="UTF-8"?> <TS language="en" version="3.0"> <context> <name>message_context</name> ... <message id="xx_amount_events" numerus="yes"> <source>%Ln event</source> <translation> <numerusform>%Ln событие</numerusform> <numerusform>Событий: %Ln</numerusform> </translation> </message> ... <message id="xx_time_day" numerus="yes"> <source>%Ln day</source> <translation> <numerusform>%Ln день</numerusform> <numerusform>%Ln дн.</numerusform> </translation> </message> ... </context> </TS>
Note that this Russian translation file has “language="en"” in the header and thus English rules are used to select the plural forms, i.e. the first plural form is selected if “n” is equal to 1, the second plural form for all other cases.
Usually, Russian would use one plural form for “n” in 1, 21, 31, 41, 51, 61…, another one for “n” in 2-4, 22-24, 32-34…, and yet another one for “n” in 0, 5-20, 25-30, 35-40….
If only 2 forms with English rules are used as above, i.e. only “n” equal to 1 is special cased, the second plural form has to be translated in a way that it is acceptable for all other values of “n”.
In the Russian example translation file above this is in one case achieved by abbreviating, i.e. using “%Ln дн.” for all “n” not equal to one and thus hide the different plural forms with the abbreviation dot. In the other case, the number is grammatically “detached”, i.e. by writing “Событий: %Ln” the part of the translation before the “:” is detached from the part with the number and thus the word “Событий” does not need to change for different values of “n”.
Messages using plural should use the place holder %Ln and not %L1, i.e. the source code should should look like
int n = messages.count(); //% "%Ln message(s) saved" showMessage(qtTrId("xx_messages_saved", n));
and the .ts file with the translations should look like:
<?xml version="1.0" encoding="UTF-8"?> <TS language="en" version="3.0"> <context> <name>message_context</name> ... <message id="xx_amount_events" numerus="yes"> <source>%Ln event</source> <translation> <numerusform>%Ln message saved.</numerusform> <numerusform>%Ln messages saved.</numerusform> </translation> </message> ... </context> </TS>
The second parameter “n” of qtTrId() does two things:
If %L1 were used instead of %Ln in the translation, the second parameter of qtTrId() would still select the correct translation for the value of “n” but would not replace %L1, i.e. in this case the code
showMessage(qtTrId("xx_messages_saved", 2));
would display “%L1 messages saved.”. If it is known that the plural translations mistakenly use %L1 instead of %Ln, one can work around the issue by using an extra .arg() in the code like this:
showMessage(qtTrId("xx_messages_saved", 2).arg(2));
In this example, the “2” used as the second parameter of would select that correct plural form, i.e. “%L1 messages saved.” and the .arg(2) would replace the %L1 with 2 so the final result would be “2 messages saved.” as desired. But as soon as the translation is fixed to use the correct %Ln this code prints a warning
QString::arg: Argument missing: "2 messages saved." , 2
because the .arg(2) tries to replace a %L1 which is not there.
The convention for the path to store the compiled translation data (in .qm format) on the device is:
<top-directory>/<catalog-name>_<locale-code>.qm
top-directory can be anywhere on the device. locale-code is something like en_US for US-English, en_GB for Britisch English, zh_CN for Simplified Chinese, de_DE for German in Germany, de_CH for Swiss German, etc. <catalog-name> is usually the name of the application. If a translation for a more specific locale name like en_US cannot be found, a less specific locale name like en is tried. For example, if the application name is foo and the locale name is en_US, the first one of the following files which exists is used:
<top-directory>/foo_en_US.qm <top-directory>/foo_en_US <top-directory>/foo_en.qm <top-directory>/foo_en <top-directory>/foo.qm <top-directory>/foo
See also the documentation of QTranslator::load().
The top-directory used by default is /usr/share/l10n/meegotouch (This is defined as M_TRANSLATION_DIR in meegotouch_defines.prf which can be found in ./mkspecs/features/meegotouch_defines.prf` in the libmeegotouch source code).
`/usr/share/l10n/meegotouch` is also the default directory used by translation packages to store the .qm files. Applications can set additional search paths by using MLocale::setTranslationPaths() and MLocale::addTranslationPath() . This supports the variability needed for over-the-air translation installation as well as for user defined translations.
The packaging of translations into debian packages is illustrated here using the demo application “widgetsgallery” as an example.
| Package name | Contents |
| meegotouch-demos-widgetsgallery-l10n-engineering-english | /usr/share/l10n/meegotouch/widgetsgallery.qm /usr/share/doc/meegotouch-demos-widgetsgallery-l10n-engineering-english/widgetsgallery.ts /usr/share/doc/meegotouch-demos-widgetsgallery-l10n-engineering-english/changelog.Debian.gz |
| meegotouch-demos-widgetsgallery-l10n-ar | /usr/share/l10n/meegotouch/widgetsgallery_ar.qm /usr/share/doc/meegotouch-demos-widgetsgallery-l10n-ar/changelog.Debian.gz |
| ... | ... |
| meegotouch-demos-widgetsgallery-l10n-zh-cn | /usr/share/l10n/meegotouch/widgetsgallery_zh_CN.qm /usr/share/doc/meegotouch-demos-widgetsgallery-l10n-zh-cn/changelog.Debian.gz |
Every application needing translations should supply also a debian package containing the “engineering English” translations. The package name for the engineering English translations should end with -l10n-engineering-english as in the above example. The package name “...-engineering-english” should make it clear that these are not real translations, instead they are completely auto-generated from the source code.
In the above example, the package “meegotouch-demos-widgetsgallery-l10n-engineering-english” contains “widgetsgallery.qm” and “widgetsgallery.ts”. “widgetsgallery” is the name of the application executable. “widgetsgallery.ts” is automatically generated from the source code using “lupdate”. “widgetsgallery.qm” is automatically generated from “widgetsgallery.ts” using “lrelease”. The make files in the application source code should contain support to do this automatically when building the application without extra effort (The current source code of widgetsgallery already gives an example how this can be done).
The “widgetsgallery.qm” file serves several purposes:
It is used at runtime by the application to display engineering English instead of message ids to make testing the application easier while no real translations are available yet.
It supplies fallback engineering English when real translations are available but incomplete. Of course this should never happen in the final product but it will happen during the development and testing and then this fallback engineering English is useful.
It gives us an easy way to extract a list of ids which are actually used in the source code. Using the tool “lconvert” one can easily extract all ids from a .qm file. If all meegotouch applications supply “...-l10n-engineering-english” packages, one can easily install all these packages (for example with “apt-get”) and then extract all ids which are actually used. Then these ids can be used for cross-checking with the ids used in the specifications and in the real translations.
“widgetsgallery” is only a test application used by the libmeegotouch developers for testing and demos. Therefore, the localisation department will not supply translations for “widgetsgallery”. The libmeegotouch developers supply some demo translations for “widgetsgallery” to test their code and to make sure the system works.
Only because of this, “widgetsgallery” has some “real” translation packages like “meegotouch-demos-widgetsgallery-l10n-zh-cn” containing “/usr/share/l10n/meegotouch/widgetsgallery_zh_CN.qm” (see the table above). In that respect, “widgetsgallery” is an exception.
For a “real” application, say “camera” which is intended to be shipped to the customer, the “real” translation packages are supplied by the translation department, not by the developers. I.e. the developers will only supply “camera-l10n-engineering-english” and all the other packages “camera-l10n-ar”, “camera-l10n-zh-cn”, ..., will be supplied by the translation department.
As long as the real translations are still missing, the engineering English is displayed. As soon as the real translations are available and installed, they are used automatically. As can be seen in the above table, there is no file conflict between the engineering English packages and the real translations, they can both be installed at the same time without problems.
For the final product, the engineering English packages can be omitted. If it has been verified during the testing phase that the “real” translations are complete, the engineering English is not needed anymore.
The translation of .desktop files for MeeGo Touch UI differs a bit from the usual practise of Freedesktop.org. The rationale behind it is that we have different localisation processes which make it impossible to modify the source of .desktop files during build time. To accommodate the special needs of our localisation processes, the .desktop file shall contain at least two extra (key, value) pairs:
For example:
[Desktop Entry] # engineering english Name=Setting # logical name of "Setting" X-MeeGo-Logical-Id=xx_setting_something # the catalog name X-MeeGo-Translation-Catalog=meegotouchsettings
For the freedesktop specification of the .desktop files see
http://standards.freedesktop.org/desktop-entry-spec/latest/
In MeeGo Touch UI, such .desktop files are parsed using MDesktopEntry.
FIXME: No settings available now
Translation data can be downloaded to the device and the translation system can immediately use the new downloaded data.
The current language setting is stored in the gconf key /meegotouch/i18n/language . From the command line, the current language can be queried using gconftool-2 like this:
~$ gconftool-2 -g /meegotouch/i18n/language
ar
In the above example, the current language is Arabic. Setting the current language from the command line can of course also be done with gconftool-2 :
~$ gconftool-2 -t string -s /meegotouch/i18n/language zh_CN
The above command sets the current language to simplified Chinese.
The preferred way to query the current language from a MApplication works like this:
MLocale currentLocale; gets a copy of the system default locale which is connected to the gconf settings and follows the gconf settings.
Alternatively it is possible to read the gconf settings directly:
MGConfItem languageItem("/meegotouch/i18n/language"); QString language = languageItem.value().toString();
This has the same effect as getting the current system locale and using currentLocale.name().
Setting the current language permanently from a MApplication works like this:
MGConfItem languageItem("/meegotouch/i18n/language"); languageItem.set("zh_CN");
One can also set a new current language temporarily from a MApplication by doing:
MLocale newLocale("zh_CN"); MLocale::setDefault(newLocale);
This does not change the gconf settings and therefore this change will be lost as soon as one of the locale related gconf keys changes again. And it will be lost when the application restarts. Nevertheless this can be useful for temporary changes for testing and it is currently the only way to change the current language on systems without gconf like Windows or Mac OS X.
If a the system locale changes, either because a locale related gconf key changed or because MLocale::setDefault() has been called explicitly by the application, the following things happen:
QEvent::LanguageChange will be sent to the Application, if necessary. MWindow will propagate a QEvent::LanguageChange to all MWidgets on the scene. A MWidget which receives this event by calls its retranslateUi() method. QEvent::ApplicationLayoutDirectionChange will be sent to the Application, if necessary. MWindow will propagate a QEvent::LayoutDirectionChange to all MWidgets as appropriate to enable the widgets to change their layout on the fly. settingsChanged() will be emitted by the locale. localeSettingsChanged() will be emitted by the MApplication . In most cases, an application doesn’t have to use the above signals settingsChanged() and localeSettingsChanged() because most stuff like reloading the translation catalogs already happens automatically. If retranslateUi() methods are implemented for the widgets used in the application, the language of the user interface will already change automatically.
Optionally, an application can use these signals though, if it wants to do additional special things which do not happen automatically. For example:
main.cpp: #include "foohandlelocalechange.h" ... int main (int argc, char **argv) { ... FooHandleLocaleChange fooHandleLocaleChange; QObject::connect(&application, SIGNAL(localeSettingsChanged()), &fooHandleLocaleChange, SLOT(fooHandleLocaleChange())); ... }
where the FooHandleLocaleChange class looks like:
foohandlelocalechange.h:
class FooHandleLocaleChange : public QObject
{
Q_OBJECT
public slots:
void fooHandleLocaleChange();
};
foohandlelocalechange.cpp:
void FooHandleLocaleChange::fooHandleLocaleChange()
{
// do whatever is needed when the system locale
// changes, but remember that the basic stuff like
// reloading the translation catalogs and changing the layout
// direction already happens automatically.
//
// Therefore, unless you have very special needs, you do not
// need to do anything here.
//
// In any case, do *not* do anything here which changes the
// system default locale again because this would trigger
// an endless loop.
}
A MWidget which receives the QEvent::LanguageChange calls its virtual method
virtual void retranslateUi();
MWidgets can reimplement this retranslateUi() method to do whatever is necessary to retranslate itself, as in the following example:
foopage.h
class FooPage : public MWidget
{
...
private:
...
MLabel* label;
...
};
foopage.cpp
void FooPage::createContent()
{
label = new MLabel();
retranslateUi();
}
void FooPage::retranslateUi()
{
...
//% "Label Text"
propertiesLabel->setText(qtTrId("qtn_label_text"));
...
}
This can be quite tricky in some cases but makes it possible to re-translate the user interface of an application on the fly if the locale settings change.
Widget mirroring is the automatic behaviour of putting the widgets in the reverse direction when the language is switched to Right-to-left languages (such as Arabic or Hebrew). In this direction, the widgets are put following the natural flow of text writing direction. Widget mirroring works automatically when the widgets are arranged using QGraphicsLayout or MLayout, with some exceptions with the specialist MLayout policies.
The layout direction will be automatically set to the correct value by MApplication when the language is changed. The current layout direction can be obtained from MApplication::layoutDirection() . Note that during the application lifetime, the direction may change if the user changes it from the global setting.
If you are developing a widget in which it does not make sense for the layout to be reversed (for example, a number keypad), then use QGraphicsWidget::setLayoutDirection() to override the layout direction. If you need to paint your widget differently in right-to-left mode, simply call `qApp->layoutDirection()` and draw as appropriate. If you are implementing a custom MAbstractLayoutPolicy, however, you should use the MLayout::layoutDirection() function instead so that the user can override it.
In advanced situations you can handle the QEvent::ApplicationLayoutDirectionChange() event directly in your widget's event handler.
| Widget | Works with RTL | Comments |
| MAnimatedIcon | OK | Nothing to do for RTL |
| MApplicationMenu | broken? | In widgetsgallery, click "Application Menu", then click "Application Menu" in the navigation bar. A menu with offering the names "Plato", "Twain", "Einstein", "Adams" is openened. Hit Control-D to change the layout direction, you see that the these names which were originally "almost" centred are aligned right in RTL mode. They should be aligned in a mirrored way, i.e. if they are a bit left of the centre in LTR mode, they should be a bit right of the centre in RTL mode. Is this a problem with MApplicationMenu or with MAction? |
| MApplicationPage | OK | Nothing to do for RTL |
| MApplicationWindow | OK | Nothing to do for RTL |
| MButton | OK | But the switches in the switchpage of widgetsgallery (which are MButtons) do not reverse the alignment of their ON/OFF texts. Problem in MButton or somewhere else? |
| MButtonGroup | OK | Nothing to do for RTL |
| MComboBox | OK | |
| MContainer | OK | Nothing to do for RTL |
| MDialog | OK | |
| MDockWidget | OK | Nothing to do for RTL |
| MEscapeButtonPanel | OK | Nothing to do for RTL |
| MGrid | broken |
There seems to be a grid with gray squares in the background and blue buttons in the foreground in the widgetsgallery grid page. In LTR mode, the gray squares align with the blue buttons, i.e. on top of each gray square is a blue button. In LTR mode, the two arrays are shifted against each other. This doesn't look right. |
| MHomeButtonPanel | OK | Nothing to do for RTL |
| MImage | OK | Nothing to do for RTL |
| MInfoBanner | broken | Always slides in from the left side, see "Dialogs and Notifications" page in widgetsgallery. |
| MLabel | OK | It did not change alignment on the fly until recently but this is fixed now, see NB#144377 |
| MMessageBox | OK | |
| MModalSceneWindow | OK | Nothing to do for RTL |
| MNavigationBar | broken | Icon in the navigation bar is always on the left side |
| MObjectMenu | probably OK | Where is this used? |
| MPannableViewport | OK | Position indicator correctly changes side |
| MPannableWidget | OK | Nothing to do for RTL |
| MPopupList | OK | |
| MProgressIndicator | OK | |
| MPositionIndicator | broken | Is always on the right side. Can be seen on the start page of Widgetsgallery. |
| MSeekBar | broken | slides correctly from the right in RTL mode but the black area showing the "already" downloaded stuff is only visible in LTR mode. |
| MSeparator | OK | Nothing to do for RTL |
| MSlider | OK | |
| MSlideShow | OK | Nothing to do for RTL |
| MTextEdit | OK | NB#104397 is very obvious here, but this is not the fault of MTextEdit. |
| MToolBar | OK | |
| MWindow | OK | Nothing to do for RTL |
The MeeGo Touch UI Framework uses the Qt character set conversion mechanisms directly. Therefore, developers can follow the Qt documentation to find out how to use the character set conversion functionalities.
Some languages require bigger font size than the one used in the English text. This may impact the layout design.
Some languages use plural and some do not. Some other languages use more than one form of plurals.
Sorting order is dependent on the language setting. Always use MeeGo Touch UI collator to sort the strings.
In some languages there is no space between words. In most cases the line wrapping will work and the system will break the sentence into smaller chunks. If it fails, translators shall use whitespace to force word boundaries.
When a sentence contains several variables, the order of the words in other languages may be different. Use QString place marker to define the order of the variables.
//% "Download and install %1 from %2? (Size is %L3 MB)" QString s = qtTrId("xx_download_and_install_from_size_mb") .arg(packageName).arg(repository).arg(size);
When translating, translators shall also keep the place markers and put them in the correct places.
Note how L3 was used for the size parameter which is a number. Using L3 instead of just 3 has the advantage that localized numerals may be used. For example if the language of the locale is Arabic and the numeric settings of the locale do not override it, L3 causes Arabic-Indic numerals to be used. If the translator wants to disable this behaviour for this particular message and force usage of Arabic (= Western) numerals always, no matter what the locale settings are, the translator can use 3 instead of L3 on purpose.
This is the general checklist which need to be confirmed during the development. Use only MeeGo Touch UI Framework locale system API to gain full internationalisation support. Use of POSIX and libc API is not recommended and may cause discrepancies in the representation of the data.
To do sorting, never use Qt's QString localeAwareCompare(), use MeeGo Touch UI collation system instead.
Some data such as graphics, icons, sounds, etc sometimes need to be correctly loaded dependent on the regional setting. In this case, never hard code the data directory, instead use the regional setting information to build the string to point to the correct directory containing the data.
If you need to construct a string involving variables, never concate the UI strings. The string order is never guaranteed to the same in all languages. Never do this:
QString message = "You have " + numMessages + " messages";
Instead use a variable when constructing the string:
//% "You have %Ln messages" QString message = qtTrId("xx_you_have_messages", numMessages);
Date, time, numbers, name, address, currency data are required to be formatted before displayed on the screen. Never display unformatted data as it breaks the data presentation consistency. Formatted data always use the correct presentation according to the regional setting. Special case for address data, it shall be formatted according to the customary of the country which stated in the address.
In general, avoid using abbreviations as part of the UI string. The abbreviations ofter are misleading and confuse the translators. Instead, allow for more text expansion
Never use casing when indicating functionality or emphasis. Some languages do not observe distinction between lowercase and uppercase. The translator may have difficulties to find out how to emphasise the string when the original English string uses uppercase for the emphasis.
Other methods such as using certain typeface to show emphasis also discouraged as not all writing systems support them.
Never use characters outside the ASCII repertoire as part of original English strings. The font needed to display the character may not available in the device variant used for other languages.
Some languages create words by combining a few words together. The resulting words are often longer than the original English string. Make sure that the UI allows the text expansion in these languages.
Some languages use their own digits (term: national digits) to show the numbers. Allow the usage of national digits in some cases (for example when displaying general strings containing numbers) and force the UI to only use the Latin digits in other cases (for example when displaying IP addresses).
Some languages use different writing direction than the Latin based languages. The UI presentation generally follows the natural flow of the writing direction. Therefore, the widget layout and composition is mirrored when the device language is set to the languages which use the right-to-left writing direction. The mirroring is done automatically by the MeeGo Touch UI Framework. If the automatic mirroring is not sufficient then consult the developers to support different layout for right-to-left direction.
MeeGo Touch UI Framework currently only supports left-to-right and right-to-left writing directions.
Some languages use gender when referring to objects or words.
Colours often have different meanings and connotations in different cultures. In Western culture, red is often used to indicate stop or danger. In China, red is symbolic of celebration, luck and prosperity. White signifies purity, virginity, or death. In Western culture it is the colour worn at weddings. In parts of Japan and China the colour worn at funerals.
Never embed text and/or numbers in graphics, otherwise the graphics need to be translated and it may be difficult and expensive.
Some icons or drawings considered offensive in some cultures.
See section "Translation involving date/time format" above.
| Copyright © 2010 Nokia Corporation | Generated on Thu Nov 4 2010 18:14:23 (PDT) Doxygen 1.7.1 |
MeeGo Touch |