FAQ sur l'internationalisation

| Jasmin Blanchette | traducteur : Thibaut Cuvelier | Qt Quarterly |

Cet article fournit des réponses à quelques questions fréquemment posées sur l'internationalisation des programmes Qt. Il couvre des sujets variés, comme la distribution des fichiers de traduction, les traductions inversées et le changement dynamique de langue.

Cet article est une traduction autorisée de Internationalization Q & A, par Jasmin Blanchette.

Commentez

- Titre : FAQ sur l'internationalisation

- Auteurs : Jasmin Blanchette, traducteur : Thibaut Cuvelier, Qt Quarterly

- Parution : 1 septembre 2009

- Mise à jour : 1 août 2011

- Licence : Copyright ® 2003 Jasmin Blanchette. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. L'article original

- II. Comment puis-je distribuer les fichiers de traduction de mon application ?

- III. Mon application a été développée avec Qt Designer. J'aimerais donner les fichier de Qt Designer au traducteur, avec Qt Designer, mais je ne trouve pas de moyen pour spécifier une traduction dans Qt Designer. Y a-t-il un moyen de prévisualiser un fichier Qt Designer avec une traduction ?

- IV. Dans mon application, je dois chercher des traductions pour plusieurs langues, mais tr() ne renvoie que la traduction de la dernière langue installée. Y a-t-il un paramètre de langue à la fonction tr() pour spécifier le fichier de traduction à utiliser ?

- V. J'aimerais stocker mes traductions dans une base de données. Comment dire à tr() d'aller les y chercher ?

- VI. Mon application contient un texte en allemand ("Bestätigen"), mais, quand je charge le fichier de traduction dans un éditeur de texte, c'est une autre chaîne qui apparaît ("BestÃtigen"). Comment le faire changer d'avis ?

- VII. Comment changer la langue de mon application quand elle est lancée ?

- VIII. Je crée une boîte avec les boutons tr("Yes") et tr("No"). Grâce à la fonction tr(), les chaînes sont traduites. Par contre, à d'autres endroits, l'application nécessite les chaînes en anglais. Qt fournit-il une manière de faire correspondre tr("Yes") à "Yes" ?

- IX. Divers

I - L'article original

Qt Quarterly est une revue trimestrielle électronique proposée par Nokia à destination des développeurs et utilisateurs de Qt. Vous pouvez trouver les versions originales.

Nokia, Qt, Qt Quarterly et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.

Cet article est la traduction de l'article Internationalization Q & A de Jasmin Blanchette paru dans la Qt Quarterly Issue 08.

Cet article est une traduction d'un des tutoriels écrits par Nokia Corporation and/or its subsidiary(-ies) inclus dans la documentation de Qt, en anglais. Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia.

II - Comment puis-je distribuer les fichiers de traduction de mon application ?

La manière standard est de les mettre dans un dossier sur le client, et de les charger à l'exécution. Cependant, si elle ne peut pas les trouver, l'application se lancera sans traduction.

Pour éviter ce problème, on peut utiliser l'outil qembed, disponible dans le dossier tools, pour incorporer ces fichiers à l'application.

qembed myapp_de.qm myapp_fr.qm > qm_files.h

L'outil crée un tableau constant et statique des données qu'il suffit alors d'inclure.

#include "qm_files.h"

int main(int argc, char *argv[])
{
...
QTranslator translator;
translator.load(myapp_de_qm_data, myapp_de_qm_len);
app.installTranslator(&translator);
...
}

La fonction load(), surcharge apparue dans Qt 3.2, accepte les données brutes du fichier de traduction, au lieu du nom de fichier.

qembed doit être appelé à chaque changement dans les fichiers de traduction, pour garder la version de l'application à jour. Dans un futur, ceci pourrait être intégré à qmake, comme ce qui arrive avec les collections d'images.

III - Mon application a été développée avec Qt Designer. J'aimerais donner les fichier de Qt Designer au traducteur, avec Qt Designer, mais je ne trouve pas de moyen pour spécifier une traduction dans Qt Designer. Y a-t-il un moyen de prévisualiser un fichier Qt Designer avec une traduction ?

Ni Qt Designer, ni Qt Linguist ne proposent cette fonctionnalité. Cependant, il est assez facile de le faire avec QWidgetFactory, une classe qui construit dynamiquement un widget sur base d'un fichier d'interface utilisateur.

#include <qapplication.h>
#include <qtranslator.h>
#include <qwidgetfactory.h>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

if (argc < 3)
{
qWarning("Usage: lpreview " "file.qm form1.ui...");
return 1;
}

QTranslator translator;
translator.load(argv[1]);
app.installTranslator(&translator);

for (int i = 2; i < argc; ++i)
{
QWidget *widget = QWidgetFactory::create(argv[i]);
widget->show();
}

QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
return app.exec();
}

Cet outil prend, en paramètres, un fichier de traduction et un fichier d'interface utilisateur. Pour compiler l'exemple, vous devrez lier contre la librairie qui de cette manière :

LIBS += -lqui

La ligne LIBS fonctionne correctement sur plusieurs plateformes sans problème depuis Qt 3.2. Pour les versions plus anciennes, utilisez ces lignes-ci :

unix:LIBS += -lqui
win:LIBS  += $(QTDIR)/lib/qui.lib

IV - Dans mon application, je dois chercher des traductions pour plusieurs langues, mais tr() ne renvoie que la traduction de la dernière langue installée. Y a-t-il un paramètre de langue à la fonction tr() pour spécifier le fichier de traduction à utiliser ?

tr() est basé sur l'idée qu'une seule langue doit être chargée à la fois. Cependant, vous pouvez instancier plusieurs QTranslator, chacun chargeant une langue, et, ensuite, appeler findMessage() pour trouver une traduction, au lieu de tr().

QTranslator thaiTranslator;
thaiTranslator.load("myapp_th.qm");

QTranslator urduTranslator;
urduTranslator.load("myapp_da.qm");

thaiStr = thaiTranslator.findMessage("MainForm", "Help").translation();
urduStr = urduTranslator.findMessage("MainForm", "Help").translation();

V - J'aimerais stocker mes traductions dans une base de données. Comment dire à tr() d'aller les y chercher ?

La solution est de dériver QTranslator et de réimplémenter findMessage().

class DBTranslator : public QTranslator
{
public:
QTranslatorMessage findMessage(const char *context,
const char *sourceText, const char *comment);
};

QTranslatorMessage DBTranslator::findMessage(const char *context,
const char *sourceText, const char *comment)
{
QSqlQuery query;
query.prepare("SELECT translation FROM message "
"WHERE context=? AND sourcetext=? AND comment=?");
query.addBindValue(context);
query.addBindValue(sourceText);
query.addBindValue(comment);
query.exec();
if (query.next())
{
return QTranslatorMessage(context, sourceText, comment, query.value(0).toString());
}
return QTranslatorMessage();
}

Dans la fonction main(), vous devriez écrire quelque chose de ce genre :

int main(int argc, char *argv[])
{
...
DBTranslator translator;
app.installTranslator(&translator);
...
}

VI - Mon application contient un texte en allemand ("Bestätigen"), mais, quand je charge le fichier de traduction dans un éditeur de texte, c'est une autre chaîne qui apparaît ("BestÃtigen"). Comment le faire changer d'avis ?

Les fichiers XML, les sources de la traduction générés par lupdate, utilisent l'encodage UTF-8. C'est l'encodage par défaut, mais il est incompatible avec d'autres encodages populaires, comme le Latin-1 (ISO 8859-1) dans l'Europe de l'Ouest.

Pour convertir un fichier XML, il ne suffit pas de changer l'encodage à l'enregistrement, il faut aussi modifier son en-tête, que nous devons mettre au début du fichier.

<?xml version="1.0" encoding="ISO-8859-1"?>

On peut écrire un outil pour automatiser cette conversion.

int main(int argc, char *argv[])
{
if (argc < 3) {
qWarning("Usage: lencode encoding file1.ts...");
return 1;
}
QTextCodec *codec = QTextCodec::codecForName(argv[1]);
if (!codec) {
qWarning("Unknown encoding: %s", argv[1]);
return 1;
}
for (int i = 2; i < argc; ++i)
encodeFile(codec, argv[i]);
return 0;
}

L'outil prend deux paramètres : l'encodage, et le fichier à encoder. Ensuite, il convertit le fichier dans l'encodage spécifié. La majeure partie du travail est effectuée par cette fonction.

void encodeFile(QTextCodec *codec, const char *fileName)
{
QFile file(fileName);
QDomDocument doc;

if (!file.open(IO_ReadOnly | IO_Translate))
; // handle error
if (!doc.setContent(&file, true))
; // handle error

if (doc.firstChild().isProcessingInstruction() && doc.firstChild().nodeName() == "xml")
doc.removeChild(doc.firstChild());

QDomNode node = doc.createProcessingInstruction("xml",
QString("version=\"1.0\" encoding=\"") + codec->mimeName() + "\"");
doc.insertBefore(node, doc.firstChild());

file.close();
if (!file.open(IO_WriteOnly | IO_Translate))
; // handle error
QTextStream out(&file);
doc.save(out, 4);
}

La fonction encodeFile() charge le fichier dans un arbre DOM. Ensuite, elle supprime tout en-tête préexistant, et ajoute l'en-tête correspondant à l'encodage. À la fin, elle appelle save() pour enregistrer l'arbre sur le disque dur. Cette fonction lit l'en-tête nouvellement ajouté, et utilise l'encodage spécifié pour l'enregistrement. S'il n'est pas précisé, elle retombe sur UTF-8. Le code pour la gestion des erreurs a été enlevé pour garder un peu d'espace pour le reste.

VII - Comment changer la langue de mon application quand elle est lancée ?

Ce serait bien si l'application pouvait changer de langue en même temps que l'utilisateur change ses paramètres de langue. Cependant, cela est impossible, car les widgets n'ont accès qu'aux versions traduites des chaînes.

tr("Host %1 found").arg(host)

Cette chaîne pourrait être vu comme "Hôte www.troll.no trouvé", depuis laquelle il est virtuellement impossible de découvrir d'où elle vient, et de la traduire vers "Rechner %1 gefunden".

Néanmoins, il est assez facile de changer dynamiquement de langue avec Qt. L'idée de base est de créer une fonction qui donne à tous les widgets leur texte.

MyDialog::MyDialog(QWidget *parent, const char *name)
: QDialog(parent, name)
{
label = new QLabel(this);
lineEdit = new QLineEdit(this);
label->setBuddy(lineEdit);
okButton = new QPushButton(this);
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
retranslateStrings();
}

void MyDialog::retranslateStrings()
{
label->setText(tr("&Name:"));
okButton->setText(tr("OK"));
}

Il suffit alors d'appeler la fonction retranslateStrings() à chaque changement de langue.

VIII - Je crée une boîte avec les boutons tr("Yes") et tr("No"). Grâce à la fonction tr(), les chaînes sont traduites. Par contre, à d'autres endroits, l'application nécessite les chaînes en anglais. Qt fournit-il une manière de faire correspondre tr("Yes") à "Yes" ?

Oui. Bien que QTranslator ne fournisse pas de méthode appropriée pour cela, messages() retourne une liste des entrées du fichier de traduction. Vous pouvez ensuite la parcourir pour trouver une entrée avec la bonne traduction.

QCString reverseLookup(const QTranslator &translator,
const char *context, const QString &translation)
{
typedef QValueList<QTranslatorMessage> MessageList;

MessageList messages = translator.messages();
MessageList::iterator it = messages.begin();
while (it != messages.end())
{
if ((*it).translation() == translation && (*it).context() == QCString(context))
return (*it).sourceText();
++it;
}
return "";
}

Ceci ne fonctionne que sur des fichiers non compressés. Sinon, la valeur de retour de la fonction est indéterminée. pour générer des fichiers non compressés, appelez lrelease avec le paramètre -nocompress, apparu avec Qt 3.2.

IX - Divers

Ceci a été écrit pour Qt3. Cependant, la majorité du contenu reste d'actualité pour Qt4.

Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à Nokia pour nous avoir autorisé la traduction de cet article !