Meego Wiki
Views

QtInternationalization

From MeeGo wiki
(Difference between revisions)
Jump to: navigation, search
(Creating the OBS project)
(Minor reorg... I clearly did not read ahead to the next section ;))
 
(15 intermediate revisions not shown)
Line 1: Line 1:
-
This page is a draft work in progress and not complete; mainly a stream of consciousness...
+
=Internationalization=
-
=Overview=
+
This is a developer's guide to internationalization (i18n) within the MeeGo UX project.
-
MeeGo applications are are to be internationalized by the application developer.  For information on how to write internationalized code, see the [http://apidocs.meego.com/mtf/i18n.html MeeGo Touch Framework: i18n] documentation.  In its simplest form, the developer should use qtTrId() to wrap all of the user visible strings.  If the application uses any type of concatenation, make sure it uses the argument version of QString().  For example:
+
-
//% "Displayed contact name %1 is first name.  %2 is last name"
+
-
QString name = qtTrId("%1 %2").arg(contact.firstName()).arg(contact.lastName());
+
-
Make sure you provide a contextual comment describing the string before its usage (as is done in the above example)  This facilitates the translation process.  The string comment is also used when generating the "engineering English" which is displayed when no translation exist.
+
-
==Engineering English==
+
-
The engineering English strings are prefixed with '!!' to ensure they are visibly identifiable as having not been officially translated.
+
-
The base application projects do not provide '''localized''' translations.  Language packages will be installed which provide the various language translations.  Lacking a translation, the translation ID will be shown by the system if a translation is not found.
+
==Localizable Strings==
-
Application packages should install the engineering English translation file into the system. See the section [http://wiki.meego.com/Internationalization#Packaging_engineering_English Packaging engineering English].
+
The most basic step in internationalization is to identify any user-visible strings in your application and replace them with code that looks up the appropriate string for the current locale. The way this is accomplished in Qt C++ code is to simply wrap the string with <code>tr()</code>:
-
The following attempts to describe the general process used by MeeGo to create localized language packs.
+
<pre>QString label = tr("Ring tone:");</pre>
-
==Supported locales==
+
This is accomplished in Qt Quick (QML) code by wrapping the string with qsTr():
-
The locales which must be enabled, per http://bugs.meego.com, are:
+
-
American English (en_US), British English (en_GB), French (fr), Spanish (es), German (de), Italian (it), Polish (pl), Dutch (nl), Russian (ru), Swedish (sv), Finnish (fi), Brazilian-Portuguese (pt_BR), Canadian-French (fr_CA), Portuguese (pt), Japanese (ja), Korean (ko), Chinese Simplified (zh_CN), Chinese Traditional (zh_TW).
+
-
If in doubt of a language code, see here: http://www.transifex.net/languages/
+
<pre>Text {
 +
    id: dialogTitle
 +
    text: qsTr("Find contacts")
 +
...</pre>
-
=Step-by-Step=
+
There is a Qt command-line tool ''lupdate'' that scans source code for these <code>tr()</code> and <code>qsTr()</code> macros and pulls out the identified strings. It writes them into a ".ts file," an XML-format file that lists information about where the strings were found. In UX projects, we generate these TS files automatically when creating the dist tarball that we push to OBS.
-
For this example, we will create the language package project for the meego-handset-sms application.
+
-
The naming convention used is that the GIT tree and OBS package for a translation project are named based on the application GIT tree + -translationsIn this case meego-handset-sms-translations.
+
Your top-level .pro file should have lines like these:
 +
   
 +
<pre>TRANSLATIONS += .qml lib/.h lib/*.cpp
 +
PROJECT_NAME = meego-app-example
-
At the end of this sample, you will have the following directories:
+
dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION} &&
-
${BASE}/meego-handset-sms                  <---- application .git project
+
dist.commands += git clone . $${PROJECT_NAME}-$${VERSION} &&
-
${BASE}/meego-handset-sms-translations      <---- translation .git project for application
+
dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION}/.git &&
-
${BASE}/obs/meego-handset-sms-translations  <---- OBS project for translation package
+
dist.commands += rm -f $${PROJECT_NAME}-$${VERSION}/.gitignore &&
 +
dist.commands += mkdir -p $${PROJECT_NAME}-$${VERSION}/ts &&
 +
dist.commands += lupdate $${TRANSLATIONS} -ts $${PROJECT_NAME}-$${VERSION}/ts/$${PROJECT_NAME}.ts &&
 +
dist.commands += tar jcpvf $${PROJECT_NAME}-$${VERSION}.tar.bz2 $${PROJECT_NAME}-$${VERSION} &&
 +
dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION} &&
 +
dist.commands += echo; echo Created $${PROJECT_NAME}-$${VERSION}.tar.bz2</pre>
-
The following exports are used throughout these script snippets; you can export them once to your shell and then paste the other script components.  If you later come back to run an individual step, make sure you set these variables back up.
+
Make sure that <code>TRANSLATIONS</code> is set to include all files with translatable strings (you may need to include other subdirectories and .js files, for instance). Make sure that <code>PROJECT_NAME</code> is set to your application name.
-
export BASE='''~'''                              # Root directory for the various projects
+
-
export APP_PACKAGE='''meego-handset-sms'''        # Name of the application project
+
-
export APP_BINARY='''sms'''                      # TARGET binary created by application project
+
-
export SOURCE_SUBDIR='''/src'''                  # Subdirectory under ${APP_PACKAGE} which contains the package source files
+
-
export PACKAGE=${APP_PACKAGE}-translations
+
-
export LANGUAGES="en en_US en_GB fr es de it pl nl ru sv fi pt_BR fr_CA pt ja ko zh_CN zh_TW"
+
-
'''BOLD''' items are the ones you need to change for your project.
+
-
==Checkout the application .git project==
+
-
The first step is to obtain the application project that we'll be creating the translation project for.
+
-
cd ${BASE}
+
-
git clone git://gitorious.org/meego-handset-ux/${APP_PACKAGE}
+
-
Look in the meego-handset-sms directory and you will notice that all of the sources are in the src/ directory.  Look at the project files for meego-handset-sms -- notice that while the project is called meego-handset-sms, the binary it installs is called '''sms'''.
+
-
You can typically tell the binary name by looking for the TARGET= line in the source's .pro file.
+
Whenever you check in an update to OBS, you should create a clean tarball from git source.  To create this tarball, run the following commands from the toplevel directory in your git tree:
-
Knowing the location of the source is important when we define the SOURCES variable in the translations.pro file, which is used to derive the translation files from the sources.  Knowing the binary installed is important when we define the APP_BINARY value.
+
<pre>$ qmake
 +
$ make dist
 +
</pre>
-
The name of the binary is important as it is used by Qt to find the translation file. The translation file used can be overridden by passing a 3rd parameter to MApplication(). The compiled translation files (.qm) are installed using the binary name as their filename base.
+
Then copy the generated tarball up to OBS. Bump the version up once a week. You should never submit an "sr" for the same version twice. But you can push to OBS multiple times throughout the week without doing an "sr."
-
==Create the initial translations .git project==
+
Later these TS files are translated into various languages, and each one is compiled into a .qm binary file that allows efficient string lookup at runtime. Elsewhere you need to load a translator for each .qm file in your app, but this is handled automatically by our meego-qml-launcher for QML applications, so you generally don't have to think about. You just name your .ts file with your package name and it will be handled correctly.
-
The following snippet will create a blank translations .git repository for you:
+
-
cd ${BASE}
+
-
[ ! -d ${PACKAGE} ] && mkdir ${PACKAGE}
+
-
cd ${PACKAGE}
+
-
cat << EOF >> translations.pro
+
-
LANGUAGES = ${LANGUAGES}
+
-
CATALOGNAME = ${APP_BINARY}
+
-
SOURCEDIR = '''\$\$PWD/../${APP_PACKAGE}${SOURCE_SUBDIR}'''
+
-
TRANSLATIONDIR = \$\$PWD
+
-
DISABLE_QTTRID_ENGINEERING_ENGLISH=yes
+
-
include(\$\$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_defines.prf)
+
-
include(\$\$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_translations.prf)
+
-
EOF
+
-
git init
+
-
git add *
+
-
git commit -s -a -m "Initial translation package commit for ${APP_PACKAGE}"
+
-
qmake
+
-
make updatets
+
-
git add *.ts Makefile
+
-
git commit -s -a -m "Initial .ts files generated from ${APP_PACKAGE}"
+
-
'''NOTE:''' Do not check in the '.qm' files. Those are generated during package build time and should not be managed in GIT.
+
-
'''NOTE2:''' The 'dummy-dependency-this-file-will-never-be-created' file is created and committed to the GIT tree to keep the build process from attempting to update the .ts files every time the .qm files are compiled.
+
-
===What is the license for the translation package?===
+
==String Concatentation==
-
Translation files (.ts) are derived from the source package.  The original author of the source package therefore holds a copyright interest in the derived translation package.  In order to comply with the license terms of the source application package, typically the translation package must use the same license as the base source package.
+
-
Once you know the license which applies to the translation package, place a copy of that license in the file LICENSE and add it to the git tree:
+
A common pitfall for i18n is building up strings by concatenation. You do this using assumptions about grammar and order that are true for your own language, but may not be true for another language. For example, consider this code:
-
git add LICENSE
+
-
git commit -s -a -m "Added LICENSE terms"
+
-
==Changing the set of supported locales==
+
<pre>function getBestPepperColor() {
-
To add additional translations, you would add the translation name to LANGUAGES in translations.pro:
+
    return qsTr("red")
-
export LANGUAGES="en fi cz dk he"
+
}
-
cd ${BASE}/${PACKAGE}
+
-
sed -i -e "s,^LANGUAGES[ /t]*=.*$,LANGUAGES = ${LANGUAGES},g" translations.pro
+
-
qmake
+
-
make updatets
+
-
git add *ts
+
-
git commit -s -a -m "Updated languages to support '${LANGUAGES}' based on ${APP_PACKAGE} commit-id '${REVISION}'"
+
-
==Updating the .ts files to match latest source package==
+
var message = qsTr("Tabasco is made from peppers selected using a ") + getBestPepperColor() + qsTr(" stick")</pre>
-
cd ${BASE}/${PACKAGE}
+
-
export REVISION=$(git --git-dir=../${APP_PACKAGE}/.git log --pretty=format:"%h" HEAD^..HEAD)
+
-
make updatets
+
-
git commit -s -a -m "Updated .ts files to package ${APP_PACKAGE} commit-id '${REVISION}'"
+
-
'''NOTE:''' You only need to run '''make updatets''' periodically when new application releases are made.  It should not be run when translations are being updated.  Translators should never need to run the '''make updatets''' script.
+
-
=Packaging the translations=
+
But in French, "red stick" is "bâton rouge". If you use concatenation as above, it will come out as "rouge bâton" in translation... incorrect!
-
Language packs should be provided as one RPM per locale.  Typically, multiple packages would be built from a single OBS package.
+
-
==Creating the tar ball==
+
-
The first step of packaging is to create a tar file. In the following example, we set the version to 0.0.1. That version string should be updated to match the version of ${APP_PACKAGE} that is in OBS (the VERSION string is used for required dependency checking during package installation)
+
-
export '''VERSION=0.0.1'''
+
-
To create the tar file in ${BASE}/obs/${PACKAGE}/:
+
-
cd ${BASE}
+
-
[ ! -d ${BASE}/obs/${PACKAGE} ] && mkdir -p ${BASE}/obs/${PACKAGE}
+
-
tar cvjf ${BASE}/obs/${PACKAGE}/${PACKAGE}-${VERSION}.tar.bz2 ${PACKAGE} --exclude=.git
+
-
That above tar file would contain:
+
So here's the solution:  
-
Makefile
+
-
sms-en.ts
+
-
sms-fi.ts
+
-
sms-de.ts
+
-
translations.pro
+
-
==Creating the OBS project==
+
<pre>var message = qsTr("Tabasco is made from peppers selected using a %1 stick").arg(getBestPepperColor())</pre>
-
The contents of the '''meego-handset-sms-translations''' OBS project for this example needs to consist of:
+
-
meego-handset-sms-translations-0.0.1.tar.bz2
+
-
meego-handset-sms-translations.yaml
+
-
meego-handset-sms-translations.spec
+
-
To create the .yaml file you can use the following script.  This assumes the directory .tar.bz2 file was created and exists in ${BASE}/obs/${PACKAGE}:
+
-
cd ${BASE}/obs/${PACKAGE}
+
-
cat << EOF > ${PACKAGE}.yaml
+
-
Name: ${PACKAGE}
+
-
Summary: Translation files for ${APP_PACKAGE}
+
-
Version: ${VERSION}
+
-
Release: 1
+
-
Group:  System/GUI/Other
+
-
License: Apache 
+
-
URL: http://gitorious.org/meego-handset-ux/${PACKAGE}
+
-
Sources:
+
-
      - "%{name}-%{version}.tar.bz2"
+
-
Description: Translation files for ${APP_PACKAGE}
+
-
Requires:
+
-
      - ${APP_PACKAGE} >= %{version}
+
-
+
-
PkgConfigBR:
+
-
    - QtCore >= 4.6.0
+
-
    - meegotouch
+
-
Configure: none
+
-
Builder: make
+
-
NoFiles: yes
+
-
EOF
+
-
for locale in $(tar xf ${PACKAGE}-${VERSION}.tar.bz2 ${PACKAGE}/translations.pro -O | sed -ne 's,^LANGUAGES[ \t]*=[ \t]*\(.*\),\1,p'); do
+
-
cat << EOF >> ${PACKAGE}.yaml
+
-
SubPackages:
+
-
      - Name: ${locale}
+
-
        Summary: ${locale} translation file for ${APP_PACKAGE}
+
-
        Group: Base/System
+
-
        Description: ${locale} translation file for ${APP_PACKAGE}
+
-
        Files:
+
-
          - "%lang(${locale}) %{_datadir}/l10n/meegotouch/%{name}_${locale}.qm"
+
-
     
+
-
+
-
EOF
+
-
done
+
-
See the sample .yaml file at the end of this page for an example of what the above script generates (the sample has additional comments that the above script does not add)
+
-
Once you have the .yaml file, you can create the .spec file via spectacle.
+
Now the French translator will come up with something like:
-
specify ${PACKAGE}.yaml
+
<source>Tabasco est faite à partir de piments sélectionnés à l'aide d'un bâton %1.</source>
 +
They are able to move the <code>%1</code> around to the right place for their language.
-
==Updating the OBS package==
+
Another example is where there are multiple variables in the string. The order of the variables may need to change in translation. This cannot happen if you're concatenating.
-
Create a new translations package tar ball named meego-handset-sms-translations-${VERSION}.tar.bz2
+
-
${VERSION} value for the tar ball must correspond to the 'Version:' string in the yaml file:
+
Wrong:  
-
sed -i -e 's,^Version:[ \t]*\(.*\)$,${VERSION},p' meego-handset-translations.yaml
+
-
=Sample yaml file=
+
<pre>// dateHeader will be e.g. "March 14th"
-
#
+
var dateHeader = getLocalizedMonth() + " " + getLocalizedDay()</pre>
-
# Use application package name + "-translations"
+
 
-
Name: meego-handset-sms-translations
+
This looks right because the month and day are localized, but in another language maybe the day needs to come first, like "14. Марта" in Russian.
-
Summary: Translation files for meego-handset-sms
+
 
-
#
+
Right:
-
# Keep version # in sync with the application package
+
-
Version: 0.0.1
+
-
Release: 1
+
-
Group: System/GUI/Other
+
-
#
+
-
# Use application package license; .ts files are derived works from application application
+
-
License: Apache 
+
-
# Use GIT url for translations project on gitorious
+
-
URL: http://gitorious.org/meego-handset/meego-handset-sms-translations
+
-
Sources:
+
-
    - "%{name}-%{version}.tar.bz2"
+
-
Description: Translation files for meego-handset-sms
+
-
# Require the application package - tie these
+
-
Requires:
+
-
    - meego-handset-sms >= %{version}
+
-
+
-
PkgConfigBR:
+
-
    - QtCore >= 4.6.0
+
-
Configure: none
+
-
Builder: make
+
-
NoFiles: yes
+
-
SubPackages:
+
-
    - Name: en
+
-
      Summary: en translation for meego-handset-sms
+
-
      Group: System/GUI/Other
+
-
      Description: en translation for meego-handset-sms
+
-
      Files:
+
-
        - "%lang(en) %{_datadir}/l10n/meegotouch/sms-en.qm"
+
-
     
+
-
+
-
    - Name: fi
+
-
      Summary: fi translation for meego-handset-sms
+
-
      Group: System/GUI/Other
+
-
      Description: fi translation for meego-handset-sms
+
-
      Files:
+
-
        - "%lang(fi) %{_datadir}/l10n/meegotouch/sms-fi.qm"
+
-
     
+
   
   
-
    - Name: de
+
<pre>// localized date header: %1 is month, %2 is day of month
-
      Summary: de translation for meego-handset-sms
+
var dateHeader = qsTr("%1 %2").arg(getLocalizedMonth()).arg(getLocalizedDay())</pre>
-
      Group: System/GUI/Other
+
-
      Description: de translation for meego-handset-sms
+
-
      Files:
+
-
        - "%lang(de) %{_datadir}/l10n/meegotouch/sms-de.qm"
+
-
=Packaging engineering English=
+
'''Note:''' There is a problem with the tools in QML in that the translator will only see "%1 %2" so they won't know what to do with it. In Qt, you can pass a second argument to tr() to give the translator a hint, for example:
-
cd ${BASE}/${APP_PACKAGE}
+
-
[ ! -e translations ] && mkdir translations
+
-
cd translations
+
-
cat << EOF >> translations.pro
+
-
LANGUAGES =  # We only create engineering English in the application package
+
-
CATALOGNAME = ${APP_BINARY}
+
-
SOURCEDIR = ../${SOURCE_SUBDIR}
+
-
TRANSLATIONDIR = \$\$PWD
+
-
include(\$\$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_defines.prf)
+
-
include(\$\$[QT_INSTALL_DATA]/mkspecs/features/meegotouch_translations.prf)
+
-
EOF
+
-
qmake
+
-
make
+
-
git add translations.pro
+
-
git commit -s -a -m "Engineering English translation project generated"
+
-
Now you need to add '''translations''' to the SUBDIRS in the top level project file (sms.pro in this example):
+
-
sed -i -e 's,\(SUBDIRS = .*\),\1 translations,g' *.pro
+
-
You don't need to commit the .ts file to GIT.  It will be extracted from the source code each time the build is run.  This ensures the engineering English always provides all of the non-translated strings.  Official English translation should be installed via the ${APP_PACKAGE}-translations-en package.
+
-
You now need to add the engineering English file to your yaml file for your project in OBS to the FILES section.  Edit the yaml file and looking for Files and add the following:
+
<pre>QString dateFormat(tr("%1 %2", "%1 is the month, %2 is the day of the month"));
-
- "%{_datadir}/l10n/meegotouch/%{name}.qm"
+
QString dateHeader = dateFormat.arg(getLocalizedMonth(), getLocalizedDay());</pre>
-
If you don't have a Files section in your yaml, you'll can add one to your yaml, or you can edit your spec file directly.  See the sample yaml file above for an example how to add a Files section.
+
-
After you have done the above you can cut a new release, build, install, and testAll of the displayed strings should now be prefixed with "!!" until a translation package is provided.
+
Also notice that the "<code>arg()</code>" function on QString can take multiple arguments, unlike in QML (to my knowledge).
 +
 
 +
==Translator comments==
 +
 
 +
Strings like "%1 %2" cannot get correctly translated by translators without knowing some context. It is possible to add a comment intended for translators to help them.
 +
 
 +
See http://doc.qt.nokia.com/latest/i18n-source-translation.html#translator-comments for more information.
 +
 
 +
In QML, a translator comment must be added to the line immediately preceding the affected stringFor example:
 +
 
 +
<pre>//: This is a date range (start date - end date)
 +
string: qsTr("%1 - %2")
 +
</pre>
 +
 
 +
For more information on this topic, see http://lists.meego.com/pipermail/meego-il10n/2011-June/000516.html
 +
 
 +
==Disambiguation of strings ==
 +
 
 +
Imagine an application that uses the translatable string "Read" in one place as an action (e.g. a button that enables the user to read something), and in another place uses the translatable string "Read" to mark an element that has been read. The two different meanings (and translations to other languages) require disambiguation as otherwise they will end up as the same string in the translation files and can only receive exactly one translation that will be wrong in 50% of the cases.
 +
 
 +
See http://doc.qt.nokia.com/latest/i18n-source-translation.html#disambiguation for more information how to solve this.
 +
 
 +
An example for QML:
 +
 
 +
<pre>//: %1 is the number of transactions
 +
text: qsTr("Total volume is %1", "transactions").arg(num)
 +
 
 +
//: %1 is the volume of water
 +
text: qsTr("Total volume is %1", "watervolume").arg(vol)
 +
</pre>
 +
 
 +
==Plural handling==
 +
 
 +
Many languages have more than one plural form (e.g. Czech has one plural form for 2-4 and another one for >=5) or complex rules (e.g. Arabic uses the singular form for amounts of 1, 11, 21, 31, ...), hence code like
 +
<pre>n == 1 ? tr("%n message saved") : tr("%n messages saved")</pre>
 +
will fail for these languages.
 +
 
 +
See http://doc.qt.nokia.com/latest/i18n-source-translation.html#handling-plurals for more information how to solve this.
 +
 
 +
==Supported locales==
 +
 
 +
The locales which must be enabled, per http://bugs.meego.com, are:
 +
* American English (en_US)
 +
* British English (en_GB)
 +
* French (fr)
 +
* Spanish (es)
 +
* German (de)
 +
* Italian (it)
 +
* Polish (pl)
 +
* Dutch (nl)
 +
* Russian (ru)
 +
* Swedish (sv)
 +
* Finnish (fi)
 +
* Brazilian-Portuguese (pt_BR)
 +
* Canadian-French (fr_CA)
 +
* Portuguese (pt)
 +
* Japanese (ja)
 +
* Korean (ko)
 +
* Chinese Simplified (zh_CN)
 +
* Chinese Traditional (zh_TW)
 +
 
 +
If in doubt of a language code, see here: http://www.transifex.net/languages/
[[Category: Localization]]
[[Category: Localization]]

Latest revision as of 18:37, 14 June 2011

Contents

Internationalization

This is a developer's guide to internationalization (i18n) within the MeeGo UX project.

Localizable Strings

The most basic step in internationalization is to identify any user-visible strings in your application and replace them with code that looks up the appropriate string for the current locale. The way this is accomplished in Qt C++ code is to simply wrap the string with tr():

QString label = tr("Ring tone:");

This is accomplished in Qt Quick (QML) code by wrapping the string with qsTr():

Text {
    id: dialogTitle
    text: qsTr("Find contacts")
...

There is a Qt command-line tool lupdate that scans source code for these tr() and qsTr() macros and pulls out the identified strings. It writes them into a ".ts file," an XML-format file that lists information about where the strings were found. In UX projects, we generate these TS files automatically when creating the dist tarball that we push to OBS.

Your top-level .pro file should have lines like these:

TRANSLATIONS += .qml lib/.h lib/*.cpp
PROJECT_NAME = meego-app-example

dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION} &&
dist.commands += git clone . $${PROJECT_NAME}-$${VERSION} &&
dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION}/.git &&
dist.commands += rm -f $${PROJECT_NAME}-$${VERSION}/.gitignore &&
dist.commands += mkdir -p $${PROJECT_NAME}-$${VERSION}/ts &&
dist.commands += lupdate $${TRANSLATIONS} -ts $${PROJECT_NAME}-$${VERSION}/ts/$${PROJECT_NAME}.ts &&
dist.commands += tar jcpvf $${PROJECT_NAME}-$${VERSION}.tar.bz2 $${PROJECT_NAME}-$${VERSION} &&
dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION} &&
dist.commands += echo; echo Created $${PROJECT_NAME}-$${VERSION}.tar.bz2

Make sure that TRANSLATIONS is set to include all files with translatable strings (you may need to include other subdirectories and .js files, for instance). Make sure that PROJECT_NAME is set to your application name.

Whenever you check in an update to OBS, you should create a clean tarball from git source. To create this tarball, run the following commands from the toplevel directory in your git tree:

$ qmake
$ make dist

Then copy the generated tarball up to OBS. Bump the version up once a week. You should never submit an "sr" for the same version twice. But you can push to OBS multiple times throughout the week without doing an "sr."

Later these TS files are translated into various languages, and each one is compiled into a .qm binary file that allows efficient string lookup at runtime. Elsewhere you need to load a translator for each .qm file in your app, but this is handled automatically by our meego-qml-launcher for QML applications, so you generally don't have to think about. You just name your .ts file with your package name and it will be handled correctly.

String Concatentation

A common pitfall for i18n is building up strings by concatenation. You do this using assumptions about grammar and order that are true for your own language, but may not be true for another language. For example, consider this code:

function getBestPepperColor() {
    return qsTr("red")
}

var message = qsTr("Tabasco is made from peppers selected using a ") + getBestPepperColor() + qsTr(" stick")

But in French, "red stick" is "bâton rouge". If you use concatenation as above, it will come out as "rouge bâton" in translation... incorrect!

So here's the solution:

var message = qsTr("Tabasco is made from peppers selected using a %1 stick").arg(getBestPepperColor())

Now the French translator will come up with something like: <source>Tabasco est faite à partir de piments sélectionnés à l'aide d'un bâton %1.</source> They are able to move the %1 around to the right place for their language.

Another example is where there are multiple variables in the string. The order of the variables may need to change in translation. This cannot happen if you're concatenating.

Wrong:

// dateHeader will be e.g. "March 14th"
var dateHeader = getLocalizedMonth() + " " + getLocalizedDay()

This looks right because the month and day are localized, but in another language maybe the day needs to come first, like "14. Марта" in Russian.

Right:

// localized date header: %1 is month, %2 is day of month
var dateHeader = qsTr("%1 %2").arg(getLocalizedMonth()).arg(getLocalizedDay())

Note: There is a problem with the tools in QML in that the translator will only see "%1 %2" so they won't know what to do with it. In Qt, you can pass a second argument to tr() to give the translator a hint, for example:

QString dateFormat(tr("%1 %2", "%1 is the month, %2 is the day of the month"));
QString dateHeader = dateFormat.arg(getLocalizedMonth(), getLocalizedDay());

Also notice that the "arg()" function on QString can take multiple arguments, unlike in QML (to my knowledge).

Translator comments

Strings like "%1 %2" cannot get correctly translated by translators without knowing some context. It is possible to add a comment intended for translators to help them.

See http://doc.qt.nokia.com/latest/i18n-source-translation.html#translator-comments for more information.

In QML, a translator comment must be added to the line immediately preceding the affected string. For example:

//: This is a date range (start date - end date)
string: qsTr("%1 - %2")

For more information on this topic, see http://lists.meego.com/pipermail/meego-il10n/2011-June/000516.html

Disambiguation of strings

Imagine an application that uses the translatable string "Read" in one place as an action (e.g. a button that enables the user to read something), and in another place uses the translatable string "Read" to mark an element that has been read. The two different meanings (and translations to other languages) require disambiguation as otherwise they will end up as the same string in the translation files and can only receive exactly one translation that will be wrong in 50% of the cases.

See http://doc.qt.nokia.com/latest/i18n-source-translation.html#disambiguation for more information how to solve this.

An example for QML:

//: %1 is the number of transactions
text: qsTr("Total volume is %1", "transactions").arg(num)

//: %1 is the volume of water
text: qsTr("Total volume is %1", "watervolume").arg(vol)

Plural handling

Many languages have more than one plural form (e.g. Czech has one plural form for 2-4 and another one for >=5) or complex rules (e.g. Arabic uses the singular form for amounts of 1, 11, 21, 31, ...), hence code like

n == 1 ? tr("%n message saved") : tr("%n messages saved")

will fail for these languages.

See http://doc.qt.nokia.com/latest/i18n-source-translation.html#handling-plurals for more information how to solve this.

Supported locales

The locales which must be enabled, per http://bugs.meego.com, are:

  • American English (en_US)
  • British English (en_GB)
  • French (fr)
  • Spanish (es)
  • German (de)
  • Italian (it)
  • Polish (pl)
  • Dutch (nl)
  • Russian (ru)
  • Swedish (sv)
  • Finnish (fi)
  • Brazilian-Portuguese (pt_BR)
  • Canadian-French (fr_CA)
  • Portuguese (pt)
  • Japanese (ja)
  • Korean (ko)
  • Chinese Simplified (zh_CN)
  • Chinese Traditional (zh_TW)

If in doubt of a language code, see here: http://www.transifex.net/languages/

Personal tools