(→Supported locales) |
(add <code>) |
||
| Line 3: | Line 3: | ||
==Localizable Strings== | ==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(): | + | 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>: |
<pre>QString label = tr("Ring tone:");</pre> | <pre>QString label = tr("Ring tone:");</pre> | ||
| Line 14: | Line 14: | ||
...</pre> | ...</pre> | ||
| - | 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 | + | 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. |
Your top-level .pro file should have lines like these: | Your top-level .pro file should have lines like these: | ||
| Line 31: | Line 31: | ||
dist.commands += echo; echo Created $${PROJECT_NAME}-$${VERSION}.tar.bz2</pre> | dist.commands += echo; echo Created $${PROJECT_NAME}-$${VERSION}.tar.bz2</pre> | ||
| - | 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. | + | 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. |
Whenever you check in an update to OBS, you should create a clean tarball to check in by running "make dist" from your toplevel directory which will execute the commands above. 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." | Whenever you check in an update to OBS, you should create a clean tarball to check in by running "make dist" from your toplevel directory which will execute the commands above. 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." | ||
| Line 55: | Line 55: | ||
Now the French translator will come up with something like: | 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> | <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. | + | They are able to move the <code>%1</code> 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. | 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. | ||
| Line 76: | Line 76: | ||
QString dateHeader = dateFormat.arg(getLocalizedMonth(), getLocalizedDay());</pre> | QString dateHeader = dateFormat.arg(getLocalizedMonth(), getLocalizedDay());</pre> | ||
| - | Also notice that the "arg()" function on QString can take multiple arguments, unlike in QML (to my knowledge). | + | Also notice that the "<code>arg()</code>" function on QString can take multiple arguments, unlike in QML (to my knowledge). |
==Supported locales== | ==Supported locales== | ||
| Line 101: | Line 101: | ||
If in doubt of a language code, see here: http://www.transifex.net/languages/ | If in doubt of a language code, see here: http://www.transifex.net/languages/ | ||
| - | |||
[[Category: Localization]] | [[Category: Localization]] | ||
This is a developer's guide to internationalization (i18n) within the MeeGo UX project.
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 to check in by running "make dist" from your toplevel directory which will execute the commands above. 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.
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).
The locales which must be enabled, per http://bugs.meego.com, are:
If in doubt of a language code, see here: http://www.transifex.net/languages/