Draft of i18n HowTo focusing on Qt Quick, Qt SDK & Transifex
-
Hi, I have been asked to write a HowTo for N9 developers willing to do proper i18n for their apps, with an addition to consider community contributions for translations. I have just gone through this myself, so it is a good chance to share my findings through several docs and tips spread out there.
Here you have a first rough version in text-only. Tomorrow I will polish it and put in HTML format for nice header, links, etc. I welcome your feedback, corrections, etc. Thank you!
http://paste.debian.net/136143/
(The forum tool won't allow me to paste the text here: too long)
-
Hey Quim,
This is looking really good! I've been through the process recently myself with File Manager and Shortcuts, and I've got a couple of things to add :)
You can take this a step further to enable translation of the application icon itself. It's a slight bit of extra work, but it's worth it especially when integration is key. I'll specify the areas that are different from yours rather than rewrite the whole thing.
Firstly, you need your .qm files in the official MeegoTouch internationalisation folder. So change your QTranslator line to:
@if (translator.load("yourapplicationname_" + locale, "/usr/share/l10n/meegotouch/"))@We'll get to installing them there later. Slight benefit here is that you're reducing the size of your binary.
Second, use qtTrId/qsTrId (Qt/QML) instead of tr/qsTr, and use Translation IDs instead of Engineering English as the identifier. TrIds are required for app name translation, and also means that if you rework your EE then Linguist won't have an issue linking the translations up when you lupdate your translation files. The third advantage is that if your translations stop working you'll notice straight away because the TrIds will show instead of your EE.
When using TrIds you can use the //% comment to specify your EE (note required double quotes):
@//: "rock" doesn't mean "stone" here
//% "You rock!"
text: qsTrId("congratulations_label")@I don't think QML supports the QString::arg() function (not from my experience anyway), so you would use qsTrId("id").replace("%1", "first argument").replace("%2", "second argument"), etc.
If your app doesn't have an about page or a place that specifies the app name on its own, add this to your top-level element in main.qml:
@
//: The name of the application. Feel free to translate to your language. Please use Qt Linguist's multi-length tool to provide abbreviations if your translation is longer than 10 characters or so.
//% "App Name"
property string applicationName: qsTr("application_name")@
The Harmattan menu doesn't give very much room for the app name, so multi-length is crucial to prevent elipses.Thirdly, when generating your .ts files make sure there is one without any language codes on it (the one that will be loaded if you don't have any translations for the user's language). This would be named appname.ts. I always put UK English here because it means that if you don't have the user's language it will show the default (and most global) UK English instead of all your TrIds. This also means I don't need an appname_en.ts file for UK English (though I do create an appname_en_US.ts translation to make the US market feel at home!).
Fourth, add the -idbased argument to the lrelease command. Otherwise it uses your EE notes as the identifiers and the Harmattan menu won't pick up your application name.
Fifth, instead of adding the .qm files to your Resource file, place this in your qmlapplicationviewer.pri file, right before INSTALLS += desktopfile icon target:
@ translations.files =
relative-path-to-default.qm
relative-path-to_sv.qm
relative-path-to_tr.qm
relative-path-to_nl.qm
translations.path = /usr/share/l10n/meegotouch
export(translations.files)
export(translations.path)
INSTALLS += translations@Lastly, modify your .desktop file and add the following lines:
@X-MeeGo-Logical-Id=application_name #Or whichever TrId you used in QML for your app name
X-MeeGo-Translation-Catalog=appname@
The catalog option above should be changed to everything before the country code in your .qm filenames, for example filemanager_en_US.qm would be in a catalog called filemanager.Done!
Pros:
- Translated icon name
- Easy to detect if your translation system has stopped working
- More flexible if you decide to modify your EE in the future
- No need for a central strings Item -- each TrId only needs to be translated once
- Slightly less assembly in your binary file meaning slightly faster launching (probably negligible, but good for larger apps)
Cons:
- Platform-dependent -- easily fixed with a couple of #ifdefs
I have noticed that sometimes the device doesn't pick up the translation files right away when developing (especially if you didn't do this from the very beginning). A reboot or two gets it going, and it definitely works when installed first-time.
What do you think?
-
Wow nikrolls, the speed, depth and precision of you answer is impressive! Thank you very much for putting your time on this.
My conclusion after thinking a lot about your points: you are right but still I feel that it's worth keeping the article as it is. It is already a long one and making the app name translatable would add even more length and complexity. If you convert your answer in an appendix then I will be happy linking to it. I'm still open to be convinced on your approach, though. Please read below.
Reasoning:
-
I'm trying to keep the scope of the article to the simplest-but-useful case, including everything that makes sense but nothing more, using links to additional references if needed.
-
The frequent case for apps these days is to have a static name (some would call it brand) that is kept across languages. I would happily include the instructions to localize the name of the app in the app grid... if this wouldn't imply having to move all your handling of strings to identifiers. If there is a way to have engineering English strings + localizable app name then I'm willing to learn and report.
-
Surely my OSS background is to blame here, but I have a preference for engineering English as opposed to identifiers. Is it a prejudice? I don't know. :) For what is worth I believe life for translators is easier with engineering English since they can see the strings directly in the online form for translations, instead of having to click on the "Details" tab where the comments in code are shown. I can see how identifiers are useful for more complex apps with more strings, used several times, handled by teams of commissioned translators but for the case of a mobile app translated with the help of volunteers...
-
I honestly didn't know about the specific folder for translations in MeeGo Touch. Still, I'm assuming that someone developing a Qt Quick app for the N9 will want it in other Qt platforms and will want to minimize the differences. We are talking about a bunch of small files, a small price to pay in exchange of avoiding an ifdef. Still, it is indeed a good practice and I will mention it in the HowTo, linking to whatever doc explains it in detail.
-
"when generating your .ts files make sure there is one without any language codes on it" - In the current doc that is the role of the first .ts file generated out of the engineering English strings, which become the default for any languages not supported. ;)
-
"instead of adding the .qm files to your Resource file, place this in your qmlapplicationviewer.pri file". What is this qmlapplicationviewer.pri file? I'm suggesting the .pro file because I believe that is what the docs refer to (would need to check again) but if there is a more kosher way to do this I'm happy including it. Or is this also related to the case where you want your app name to be translatable?
-
-
Hi Quim,
No worries, this is your article so you drive :)
Rather than being simply to get the application name translatable I simply found over time that this was the definitely most reliable way to maintain a translated app, simply because I could change the engineering English as often as I liked without Linguist getting confused and I could also see instantly if the translation wasn't being loaded properly. The number of times early in development when I wanted to swap around blocks of text and then had Qt Linguist break because it didn't know what had gone where was frustrating -- no such issues now that I use TrIds.
Fully understand about keeping your brand across multiple languages -- the ones I have translated so far have been intended to look as 'integrated' as possible, hence translating everything. I have also talked to translators (Turkish, French, for example) who state that people from their country will often not look twice at an application that even has an English name and description in the Nokia Store, so this encouraged me to take it quite seriously.
On your note about viewing TrIds on Transifex unless you press Details -- I currently have File Manager on Transifex and it shows you the engineering English I placed within the //% comment automatically (you only need to press Details to get your //: comment). Qt Linguist also completely hides them -- none of my translators so far (2 apps in 5 languages, a few more on the way) have needed to know about the TrIds.
There aren't too many extra steps really -- when describing it compared to the original it seems like there are, but it's mainly tweaking a few steps along the way.
--
If you simply wanted to use this method only to get the application name translated then there would be another optional way to do it. Simply create a dummy .qml file like such:
@import QtQuick 1.1Item {
//: Please translate this into the closest equivalent in your language
//% "My Application"
property string applicationName: qsTrId("application_name")
}@
Make sure this file is excluded from the lrelease step and instead manually process it with the -idbased argument as I stated. Then you simply add the lines in the qmlapplicationviewer.pri file to copy this to the MeeGo Touch L10n folder, make the change to your .desktop file, and voila -- the application name is translated without worrying about making all the rest of the changes.Note that this method has almost as many steps as using TrIds from the start though :)
--
Regarding #ifdefs and platform-specific changes -- as far as I can see there would be very few. You would #ifdef your translator.load statement, like such:
@#if defined(MEEGO_EDITION_HARMATTAN)
if (translator.load("yourapplicationname_" + locale, "/usr/share/l10n/meegotouch/"))
#else
if (translator.load("yourapplicationname_" + locale, QCoreApplication::applicationDirPath() + "/i18n/"))
#endif@You'd also want to modify the translations.source and translations.target files from what I first proposed -- I figured out a better way which makes it cross-platform:
@# Place this before the symbian { line in your qmlapplicationviewer.pri file
translations.source = relative/path/to/your/translations/folder
unix:!isEmpty(MEEGO_VERSION_MAJOR) {
translations.path = /usr/share/l10n/meegotouch
export(translations)
INSTALLS += translations
} else {
translations.target = i18n
DEPLOYMENT += translations
}@This will install your translation files to the meegotouch folder when on MeeGo, else deploy it to your install folder on any other platform.
--
When referring to the qmlapplicationviewer.pri file, I'm talking about the qmlapplicationviewer sub-project that Qt adds when creating a new Qt Quick Application. If you have written your qmake files from scratch then I'm referring to whichever .pro file you store your deployment instructions in.
I have also used this to install (for example) .list files into /etc/apt/sources.d/ (for non-Store releases) so that my application's repository gets checked for updates along with the system updates and gets its own notificiation on the home screen when there's a new version available.
--
To summarise again ...
This is your HowTo, I didn't want to meddle with it -- simply wanted to help avoid others going through the hours of frustration that I did when first figuring it out. In my mind the very few extra steps are outweighed by the ongoing benefits.
A couple of things like editing the deployment file and using #ifdefs can be a little daunting for first timers, but a quick explanation (or a 'just do this, don't worry' :) ) would get them through it.
If you want to stick with yours but have access to my method as well, I could possibly write an 'Advanced Companion' instead -- let me know :)
-
Ooh, another couple of tips just shared with me by the developer of Butaca (and not related to rewriting your tutorial :) ) is that you can load apps in another language without rebooting, using the following command from the terminal (in this example, for File Manager)
@$ LANG=es /opt/filemanager/bin/filemanager@
You can specify a language variant (dialect?) using the full language code:
@$ LANG=en_US /opt/filemanager/bin/filemanager@You can also translate your application name and description in the debian control file (so they they localise in the Application Manager in Settings). The easiest way to describe it is to just show you mine:
@Description: A comprehensive file manager for your Nokia N9.
File Manager is the best file manager application for your Nokia N9. Simple to use and with a native look and feel, you can view, copy, move, rename, delete and share files with ease.
XB-Description-nl: Een uitgebreide bestandsbeheerder voor uw N9.
File Manager is de beste applicatie voor bestandsbeheer op uw Nokia N9. Dankzij de eenvoudige interface met de gekende vormgeving en interactieve stijl kan je probleemloos bestanden bekijken, kopiëren, verplaatsen, hernoemen, verwijderen en delen.
Nederlandse vertaling verstrekt door Jakob Isebaert.
XB-Description-tr: N9 için kapsamli bir dosya yöneticisi.
Dosya Yöneticisi, Nokia N9'unuz için tasarlanmıştır. Kolay kullanımı ve görüntüsüyle, telefonunuzdaki bütün dosyaları görüntüleyebilir, kopyalayabilir, taşıyabilir, yeniden adlandırabilir, silebilirve kolayca paylaşabilirsiniz.
Bu uygulamanın yerelleştirilmesi MeeGo Türkiye tarafından yapılmıştır.
XB-Description-sv: En omfattande filhanterare för din N9.
Filhanteraren är den bästa filhanteraren för din Nokia N9. Simpel att använda och med ett gediget utseende är det lätt att navigera runt, kopiera, flytta, byta namn, ta bort och dela filer enkelt.
Svensk översättning av MeeGoSweden.
XSBC-Maemo-Display-Name: File Manager
XSBC-Maemo-Display-Name-tr: Dosya Yöneticisi
XSBC-Maemo-Display-Name-sv: Filhanteraren@
You can again use full language codes when you need to.Thanks to Simón Pena who's been translation my apps into Spanish for me!
-
Ok, you convinced me about recommending string IDs upfront. :)
I will add then the reference to the localization of the app name as well, although I keep thinking it's not that relevant (I wonder whether your French / Turkish friends downloaded "Angry Birds", "Facebook", "Twitter" etc apps) :)
The Nokia Store allows you to publish translated strings for app name, description and tags from their web interface. I believe this is good and simple enough for most users and developers, since the Store app in the N9 gets all the data from there. This is actually something worth mentioning, and we have an example at https://www.transifex.net/projects/p/miniature/resource/miniaturestorets/
I will skip the optimizations involving MeeGo Touch folders and deb packages taking into account the likely scenario of devs willing to bring their apps to other Qt enabled platforms.
Thank you for all your patience with this text (and me)!
-
No worries! I probably stressed the importance of translating the app name more than it's worth :) My first two N9 apps were designed to look like built-in applications that shipped with the phone, therefore it was important to me, however an 'definitely third-party' app wouldn't need it. And using TrIds from the beginning means that if you do decide to localise the app name then the largest bulk of work is already done.
Glad I could help in any way -- I think I'll write an article on my blog regarding the rest of the post, simply because I figured it out after a lot of trial and error and it would be good to save those who are interested the hassle.
Nice work on the tutorial, looking forward to seeing the final!