Testing internationalized (i18n) Qt applications

Last edited on

Overview

Qt provides a translation mechanism for internationalization (i18n) that is used by some applications to show texts (e.g., menu options, labels, button texts) in the local language, rather than the language used for developing the application.

Squish uses object texts (e.g., menu option texts, label texts, button texts) to identify application objects. So, if an application is switched to a different language, Squish won't be able to match the texts and the tests will fail to playback properly as a result of lookup errors.

Avoiding "translatable" object properties by using object names

It is possible to make Squish able to identify objects completely independently of those properties that are subject to translation. This is done by giving explicit names to all the Qt objects that the test script should interact with. This means that the application's developers must use the QObject.setObjectName(QString) method. For example, to give a button a unique name in C++:

QPushButton quitButton(tr("Quit"));
quitButton.setObjectName("mainwindow_QDialog/quit_QPushButton");

The button's "Quit" text may change (e.g., to "Quittez"), because it is subject to translation, but the objectName property will always be "mainwindow_QDialog/quit_QPushButton", and so can be relied upon in tests.

Limitation

Even when objects are explicitly named, Squish will still create new object names with translatable properties by default.

To avoid this it is also possible to configure Squish not to use those properties that you specify, but if this approach is used it is important to use explicit naming or at least to leave sufficient properties for reliable object identification. For more details see Object Name Generation .

For a small number of object names it can be a solution to delete those properties that are subject to translation, for example a QPushButton's text property and adding a name property with the object's name as value.

Automatic reverse translations

Squish for Qt can attempt to do reverse translations for window titles, button texts, etc. If this feature is used, the tests must be recorded/created using the application's original language (which is almost always what is normally done anyway).

Then, when you want to test the application in its translated form, use the same tests as usual, but with Squish's reverse translation switched on so that Squish can try to match up object names by reverse translating translated texts.

To switch on reverse translation, set the environment variable, SQUISH_TRANSLATION_AWARE_LOOKUP to 1:

Example for Windows:

set SQUISH_TRANSLATION_AWARE_LOOKUP=1
C:\squish-qt47x-win32-msvc10\bin\squishide

Reverse translation is disabled by default because it can slow down test script execution.

Limitation due to "disambiguation"

Unfortunately, reverse translations can fail for translations where a "disambiguation" text is specified. For example, in this C++ code, the text to be translated is "&Settings" and the disambiguation text is "editor":

viewMenu = menuBar()->addMenu(tr("&Settings", "editor"));

Translations for Qt applications are in files: these files may be distributed with the application or embedded in the executable. When the translation files are made the disambiguation texts are stripped out (in effect, they are replaced by numerical indexes), so Squish cannot access the now non-existent disambiguation text, and so cannot reverse translate disambiguated texts.

A possible workaround to this problem is to remove the "disambiguation" parameter from calls to the tr() method in the C++ source code.

Programmatically translate object names

Squish contains functions for iterating over the object names in the object map. With these it is possible to translate every object name in the object map from inside the test script, write the translated object names out to a new file, and load that file as the object map. The functions to be used for this are:

objectMap.symbolicNames()

objectMap.realName()

objectMap.load()

This requires somewhat more script programming know-how than most normal Squish test scripts require.

Translating items

The approach outlined above only covers the object names contained in the Object Map. In practice, most recorded test scripts usually also contain texts of "items", for example, in calls to the waitForObjectItem() and activateItem() functions.

These item texts must also be translated. For example, if you create a suitable translation function called i18n(), you can then use it to translate items.

For example, if you originally recorded this statement:

activateItem(waitForObjectItem(names.File_QMenu, "Quit"));

You would now need to change it to use your translation function:

activateItem(waitForObjectItem(names.File_QMenu, i18n("Quit")));

Writing a good i18n() function is straightforward if you use the QApplication.translate() method, but only if your translations don't have any disambiguation texts. Also, you must account for accelerators ("&") which won't necessarily be in the same place (or even present) in a translated text compared with the original text.

Using QApplication.translate() in test scripts

function i18n(context, sourceText, disambiguation)
{
    if (disambiguation == null) {
        return QApplication.translate(context, sourceText);
    } else {
        return QApplication.translate(context, sourceText, disambiguation);
    }
}

The function shown here does not account for keyboard accelerators. The disambiguation can only be supplied based on knowledge of the application's source code: it cannot be derived from the running application.