Translating UI texts in Jolla applications

During this weekend I did a little application for Jolla phone, which displays free parking places in parking halls at Oulu using open data provided by the City of Oulu.

There weren’t too many UI widgets in the app, but it provided great opportinity to look into how localization is made in Sailfish apps. I refined my notes from going through Qt documentation to this post.

1. Generate UI texts using qsTr function

Function qsTr() is used to mark texts for translation, and for providing fallback text if translations are not available.

PullDownMenu {
    MenuItem {
        text: qsTr("Refresh")
        onClicked: model.reload()
    }
}

More sophisticated uses of qsTr like adding context or parameters is shown in QtQuick internalization wiki article.

2. Generate/update ts files from sources

Command line tool lupdate is used to scan source files and find qsTr calls from them. It creates new *.ts file, or updates old one, which will contain translation info, source and target languages etc in human readable XML form.

In Fedora systems lupdate and lrelease tools could be found in Yum repositories from qt5-qttools-devel package and they are installed as lupdate-qt5 and lrelease-qt5.

I used following command to scan *.qml files under directory qml in my project, and to generate ts file for Finnish under i18n directory.

$ lupdate-qt5 qml -ts i18n/translations_fi.ts

3. Translate texts

Normal text editor or Qt Linguist found also in qt5-qttools-devel as linguist-qt5 binary is used to add finished translations to *.ts files.

Below is example i18n/translations_fi.ts from my project.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fi" sourcelanguage="en">
<context>
    <name>Listing</name>
    <message>
        <source>Refresh</source>
        <translation>Päivitä</translation>
    </message>
    <message>
        <source>Vacant</source>
        <translation>Vapaana</translation>
    </message>
</context>
</TS>

4. Generate *.mq files used with application

Lrelease tools is used to generate binary *.mq files for translations which are actually used by application.

$ lrelease-qt5 i18n/translations_fi.ts -qm i18n/translations_fi.qm

5. Include translations in RPM

Add following lines to *.pro file in project root to make Sailfish SDK include translations in final RPM package.

i18n.path = /usr/share/your-app-name/i18n
i18n.files = i18n/translations_fi.qm

INSTALLS += i18n

6. Enable translations in application for current system locale

Translations for current locale are loaded during initialization phase of the application. Below is an example main -function which loads translations and installs translator for the app.

#include <QtQuick>

#include <sailfishapp.h>

int main(int argc, char *argv[])
{
    QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv));

    QTranslator translator;
    translator.load("translations_" + QLocale::system().name(),
                    "/usr/share/harbour-carpark-oulu/i18n");
    app->installTranslator(&translator);

    QScopedPointer<QQuickView> view(SailfishApp::createView());
    view->setSource(SailfishApp::pathTo("qml/harbour-carpark-oulu.qml"));
    view->show();

    return app->exec();
}

QTranslator handles loading of translations and provides localizations for the application while QLocale handles determining current system locale.

QScopedPointer handles memory management for app and view instances and frees them automatically when main() finishes.

Don’t worry if QLocale::system().name() returns “C” when application is run in debug mode. There’s probably a bug in SDK and it works properly when application is launched from the application menu on device.