Programmez avec le langage C++

Programmez avec le langage C++

Mis à jour le mardi 10 juin 2014

Si vous avez de l'ambition pour vos programmes, vous aurez peut-être envie un jour de les traduire dans d'autres langues. En effet, ce serait dommage de limiter votre programme seulement aux francophones, il y a certainement de nombreuses autres personnes qui aimeraient pouvoir en profiter !

La traduction d'applications n'est normalement pas une chose facile. D'ailleurs, il ne s'agit pas seulement de traduire des mots ou des phrases. Il ne suffit pas toujours de dire "Ouvrir" = "Open".

En effet, on parle des milliers de langues et dialectes différents sur notre bonne vieille planète. Certaines ressemblent au français, certaines écrivent de droite à gauche (l'arabe par exemple), d'autres ont des accents très particuliers que nous n'avons pas l'habitude d'utiliser (le ñ espagnol par exemple). Et je vous parle même pas des caractères hébraïques et japonais. :-°

On ne parle donc pas seulement de traduction mais de localisation. Il faut que notre programme puisse s'adapter avec les habitudes de chaque langue. Il faut que les phrases soient écrites de droite à gauche si nécessaire.

C'est là que Qt excelle et vous simplifie littéralement la tâche. Tout est prévu. Tous les outils pour traduire au mieux vos applications sont installés de base.
Comment ça fonctionne ? Nous allons voir ça. :)

Les étapes de la traduction

La traduction de programmes Qt est un processus bien pensé... mais encore faut-il comprendre comment ça fonctionne. :p

Qt suppose que les développeurs (vous) ne sont pas des traducteurs. Il suppose donc que ce sont 2 personnes différentes.
Tout a été fait pour que les traducteurs, même si ce ne sont pas des informaticiens, soient capables de traduire votre programme.

Je vous propose de regarder ce schéma de mon crû qui résume grosso modo les étapes de la traduction :

Schema de la traduction
  1. Tout d'abord, il y a le développeur. C'est vous. Vous écrivez normalement votre programme, en rédigeant les messages dans le code source dans votre langue maternelle (le français). Bref, rien ne change, à part un ou deux petits détails dont on reparlera dans la prochaine sous-partie.

  2. Puis, vous générez un fichier contenant les chaînes à traduire. Un programme livré avec Qt le fait automatiquement pour vous. Ce fichier porte l'extension .ts, et est généralement de la forme : nomduprogramme_langue.ts.
    Par exemple, pour le ZeroClassGenerator, ça donne quelque chose comme zeroclassgenerator_en.ts pour la traduction anglaise, zeroclassgenerator_es.ts pour la traduction espagnole, etc. Il faut connaître le symbole à 2 lettres de la langue de destination pour donner un nom correct au fichier .ts. Généralement c'est la même chose que les extensions des noms de domaine : fr (français), pl (polonais), ru (russe)...

  3. Le traducteur récupère le ou les fichiers .ts à traduire (un par langue). Il les traduit via le programme Qt Linguist qu'on découvrira dans quelques minutes.

  4. Une fois que le traducteur a fini, il retourne les fichiers .ts traduits au développeur, qui les "compile" en fichiers .qm binaires. La différence entre un .ts et un .qm, c'est un peu comme la différence entre un .cpp (la source) et un .exe (le programme binaire final).
    Le .qm contenant les traductions au format binaire, Qt pourra le charger et le lire très rapidement lors de l'exécution du programme, ce qui fait qu'on ne sentira pas de ralentissement si on charge une version traduite du programme.

Je vous propose de découvrir pas à pas chacune de ces étapes dans ce chapitre. :)
Nous allons commencer par vous, le développeur. Que faut-il faire de spécial lorsque vous écrivez le code source du programme ?

Préparer son code à une traduction

La toute première étape de la traduction consiste à écrire son code de manière adaptée, afin que des traducteurs puissent ensuite récupérer tous les messages à traduire.

Utilisez QString pour manipuler des chaînes de caractères

Comme vous le savez déjà, Qt utilise exclusivement sa classe QString pour gérer les chaînes de caractères. Cette classe, très complète, gère nativement l'Unicode.

QString n'a donc aucun problème pour gérer des alphabets cyrilliques ou arabes.
C'est pourquoi il est recommandé, si votre application est susceptible d'être traduite, d'utiliser toujours des QString pour manipuler des chaînes de caractères.

QString chaine = "Bonjour"; // Bon : adapté pour la traduction
char chaine[] = "Bonjour"; // Mauvais : inadapté pour la traduction

Voilà, c'est juste un conseil que je vous donne là : de manière générale, utilisez autant que possible des QString. Evitez les tableaux de char.

Faites passer les chaînes à traduire par la méthode tr()

Utilisation basique

La méthode tr() permet d'indiquer qu'une chaîne devra être traduite. Par exemple, avant vous faisiez :

quitter = new QPushButton("&Quitter");

Cela ne permettra pas de traduire le texte du bouton. En revanche, si vous faites d'abord appel à la méthode tr() :

quitter = new QPushButton(tr("&Quitter"));

... alors le texte pourra être traduit ensuite. :)
Vous rédigez donc les textes de votre programme dans votre langue maternelle (ici le français) en les entourant d'un tr().

Facultatif : ajouter un message de contexte

Parfois, il arrivera que le texte à traduire ne soit pas suffisamment explicite à lui tout seul, ce qui rendra difficile sa traduction pour le traducteur qui ne le verra pas dans son contexte.

Vous pouvez ajouter en second paramètre un message pour expliquer le contexte au traducteur.

quitter = new QPushButton(tr("&Quitter", "Utilisé pour le bouton de fermeture"));

Ce message ne sera pas affiché dans votre programme : il aidera juste le traducteur à comprendre ce qu'il doit traduire. En effet, dans certaines langues "Quitter" se traduit peut-être de plusieurs manières différentes. Avec le message de contexte, le traducteur saura comment bien traduire le mot.

En général, le message de contexte n'est pas obligatoire.
Parfois cependant, il devient vraiment indispensable. Par exemple quand on doit traduire un raccourci clavier (eh oui !) :

actionQuitter->setShortcut(QKeySequence(tr("Ctrl+Q", "Raccourci clavier pour quitter")));

Le traducteur pourra ainsi traduire la chaîne en "Ctrl+S" si c'est le raccourci adapté dans la langue de destination.

Facultatif : gestion des pluriels

Parfois, une chaîne doit être écrite différemment selon le nombre d'éléments.
Imaginons un programme qui lit le nombre de fichiers dans un répertoire. Il affiche le message "Il y a X fichier(s)". Comment traduire ça correctement ?

En fait, ça dépend vraiment des langues. Le pluriel est géré différemment en anglais et en français par exemple :

Nombre

Français

Anglais

0

Il y a 0 fichier.

There are 0 files.

1

Il y a 1 fichier.

There is 1 file.

2

Il y a 2 fichiers.

There are 2 files.

Comme vous pouvez le voir, en français on dit "Il y a 0 fichier.", et en anglais "There are 0 files.". Les anglais mettent du pluriel quand le nombre d'éléments est à 0 !

Et encore, je vous parle pas des russes, qui ont un pluriel pour quand il y a 2 éléments et un autre pluriel pour quand il y en a 3 !
(je simplifie parce qu'en fait c'est même un peu plus compliqué que ça encore)

:waw:
J'vais jamais m'en sortir avec tous ces cas à gérer ! :'(

Eh bien si, rassurez-vous. Qt est capable de gérer tous les pluriels pour chacune des langues.
Ce sera bien entendu le rôle du traducteur ensuite de traduire ces pluriels correctement.

Comment faire ? Utilisez le 3ème paramètre facultatif qui indique la cardinalité (le nombre d'éléments).
Exemple :

tr("Il y a %n fichier(s)", "", nombreFichiers);

Qt utilisera automatiquement la bonne version du texte traduit selon la langue de destination et le nombre d'éléments.
Par ailleurs, le %n sera remplacé par le nombre indiqué en 3ème paramètre.

Bon, avec tout ça, votre programme est codé de manière à pouvoir être traduit.
Maintenant, comment se passe l'étape de la traduction ?

Créer les fichiers de traduction .ts

Nous avons maintenant un programme qui fait appel à la méthode tr() pour désigner toutes les chaînes de caractères qui doivent être traduites.

On va prendre l'exemple de notre TP ZeroClassGenerator. Je l'ai adapté pour qu'il utilise des tr().
On souhaite que ZeroClassGenerator soit traduit dans les langues suivantes :

  • Anglais

  • Espagnol

Nous devons générer 2 fichiers de traduction :

  • zeroclassgenerator_en.ts pour l'anglais

  • zeroclassgenerator_es.ts pour l'espagnol

Il va falloir éditer le fichier .pro. Celui-ci se trouve dans le dossier de votre projet et a normalement été généré automatiquement par Qt Creator.
Ouvrez ce fichier (dans mon cas ZeroClassGenerator.pro) avec un éditeur de texte comme Bloc-Notes, ou Notepad++, ou ce que vous voulez.

Pour le moment il devrait contenir quelque chose comme ça :

TEMPLATE = app
TARGET = 
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += FenCodeGenere.h FenPrincipale.h
SOURCES += FenCodeGenere.cpp FenPrincipale.cpp main.cpp

QT += widgets

Rajoutez à la fin une directive TRANSLATIONS en indiquant les noms des fichiers de traduction à générer. Ici, nous rajoutons un fichier pour la traduction anglaise, et un autre pour la traduction espagnole :

TEMPLATE = app
TARGET = 
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += FenCodeGenere.h FenPrincipale.h
SOURCES += FenCodeGenere.cpp FenPrincipale.cpp main.cpp
TRANSLATIONS = zeroclassgenerator_en.ts zeroclassgenerator_es.ts

QT += widgets
 

Bien. Maintenant, nous allons faire appel à un programme en console de Qt qui permet de générer automatiquement les fichiers .ts.

Ouvrez une console (sous Windows, utilisez le raccourci Qt Command Prompt). Allez dans le dossier de votre projet.
Tapez :

lupdate NomDuProjet.pro

lupdate est un programme qui va mettre à jour les fichiers de traduction .ts, ou les créer si ceux-ci n'existent pas.

Essayons d'exécuter lupdate sur ZeroClassGenerator.pro :

C:\Users\Mateo\Projets\ZeroClassGenerator>lupdate ZeroClassGenerator.pro
Updating 'zeroclassgenerator_en.ts'...
    Found 17 source text(s) (17 new and 0 already existing)
Updating 'zeroclassgenerator_es.ts'...
    Found 17 source text(s) (17 new and 0 already existing)

Le programme lupdate a trouvé dans mon code source 17 chaînes à traduire. Il a vérifié si les .ts existaient (ce qui n'était pas le cas) et les a donc créé.

Ce programme est intelligent puisque, si vous l'exécutez une seconde fois, il ne mettra à jour que les chaînes qui ont changé. C'est très pratique, puisque cela permet d'avoir à faire traduire au traducteur seulement ce qui a changé par rapport à la version précédente de votre programme !

Vous devriez maintenant avoir 2 fichiers supplémentaires dans le dossier de votre projet : zeroclassgenerator_en.ts et zeroclassgenerator_es.ts.

Envoyez-les à votre traducteur (s'il sait parler anglais et espagnol :p ). Bien entendu, le fait qu'on ait 2 fichiers distincts nous permet d'envoyer le premier à un traducteur anglais, et le second à un traducteur espagnol.

Traduire l'application sous Qt Linguist

Icône Qt Linguist

Qt a installé plusieurs programmes, vous vous souvenez ?
L'un d'eux va nous être sacrément utile maintenant : c'est Qt Linguist.

Votre traducteur a besoin de 2 choses :

  • Du fichier .ts à traduire

  • Et de Qt Linguist pour pouvoir le traduire !

Votre traducteur lance donc Qt Linguist. Il devrait voir quelque chose comme ça :

Qt Linguist

Ca a l'air un petit peu compliqué (et encore, vous avez pas vu Qt Designer :p ).
Vous reconnaissez d'ailleurs sûrement une QMainWindow, avec une barre de menus, une barre d'outils, des docks, et un widget central (bah oui, les programmes livrés avec Qt sont faits avec Qt :-° ).

En fait, les docks prennent tellement de place qu'on a du mal à savoir où est le widget central. Pour vous aider, c'est l'espèce de bulle ronde au milieu à droite, avec "Source text" et "Translation". C'est justement là qu'on traduira les chaînes de caractères.

Ouvrez un des fichiers .ts avec Qt Linguist, par exemple zeroclassgenerator_en.ts. La fenêtre se remplit :

Qt Linguist chargé

Détaillons un peu chaque section de la fenêtre :

  • Context: affiche la liste des fichiers source qui contiennent des chaînes à traduire. Vous reconnaissez vos fenêtres FenCodeGenere et FenPrincipale. Le nombre de chaînes traduites est indiqué à droite.

  • Strings : c'est la liste des chaînes à traduire pour le fichier sélectionné. Ces chaînes ont été extraites grâce à la présence de la méthode tr().

  • Au milieu : vous avez la version française de la chaîne, et on vous demande d'écrire la version anglaise. Notez que Qt a automatiquement détecté que vous alliez traduire en anglais, grâce au nom du fichier qui contient "en".
    Si la chaîne à traduire peut être mise au pluriel, Qt Linguist vous demandera 2 traductions : une au singulier ("There is %n file") et une au pluriel ("There are %n files").

  • Warnings : affiche des avertissements bien utiles , comme "Vous avez oublié de mettre un & pour faire un raccourci", ou "La chaîne traduite ne se termine pas par le même signe de ponctuation" (ici un deux-points). Cette zone peut afficher aussi la chaîne à traduire dans son contexte du code source.

C'est maintenant au traducteur de traduire tout ça ! :)

Lorsqu'il est sûr de sa traduction, il doit marquer la chaîne comme étant validée (en cliquant sur le petit "?" ou en faisant Ctrl + Entrée). Un petit symbole coché vert doit apparaître, et le dock context doit afficher que toutes les chaînes ont bien été traduites (16/16 par exemple).

Voici les 3 états que peut avoir chaque message :

Etapes de la traduction

On procède donc en 2 temps : d'abord on traduit, puis ensuite on se relit et on valide. Lorsque toutes les chaînes sont validées (en vert), le traducteur vous rend le fichier .ts.

Il ne nous reste plus qu'une étape : compiler ce .ts en un .qm, et adapter notre programme pour qu'il charge automatiquement le programme dans la bonne langue.

Lancer l'application traduite

Dernière ligne droite !
Nous avons le .ts entièrement traduit par notre traducteur adoré, il ne nous reste plus qu'à le compiler dans le format final binaire .qm, et à le charger dans l'application.

Compiler le .ts en .qm

Pour effectuer cette compilation, nous devons utiliser un autre programme de Qt : lrelease.

Ouvrez donc une console Qt (Qt Command Prompt), rendez-vous dans le dossier de votre projet, et tapez :

lrelease nomDuFichier.ts

... pour compiler le fichier .ts indiqué.
Vous pouvez aussi faire :

lrelease nomDuProjet.pro

... pour compiler tous les fichiers .ts du projet.

Comme je viens de terminer la traduction anglaise, je vais compiler le fichier .ts anglais :

C:\Users\Mateo\Projets\ZeroClassGenerator>lrelease zeroclassgenerator_en.ts
Updating 'zeroclassgenerator_en.qm'...
    Generated 17 translation(s) (17 finished and 0 unfinished)

Vous pouvez voir que lrelease ne compile que les chaînes marquées comme terminées (celles qui ont le symbole vert dans Qt Linguist). Si certaines ne sont pas marquées comme terminées, elles ne seront pas compilées dans le .qm.

Nous avons maintenant un fichier zeroclassgenerator_en.qm dans le dossier de notre projet. Cool.
Si on le chargeait dans notre programme maintenant ? :)

Charger un fichier de langue .qm dans l'application

Le chargement d'un fichier de langue s'effectue au début de la fonction main().
Pour le moment, votre fonction main() devrait ressembler à quelque chose comme ceci :

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

    FenPrincipale fenetre;
    fenetre.show();

    return app.exec();
}

Juste après la création de l'objet de type QApplication, nous allons rajouter les lignes suivantes :

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

    QTranslator translator;
    translator.load("zeroclassgenerator_en");
    app.installTranslator(&translator);

    FenPrincipale fenetre;
    fenetre.show();

    return app.exec();
}

Si tout va bien, bravo, vous avez traduit votre application ! :D

ZeroClassGenerator en anglais

Euh, c'est bien mais c'est pas pratique. Là, mon application se chargera toujours en anglais. Il n'y a pas moyen qu'elle s'adapte à la langue de l'utilisateur ? :euh:

Si, bien sûr, c'est faisable. C'est même ce qu'on fera dans 99% des cas.
Dans ce cas, on peut procéder comme ceci :

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
 
    QString locale = QLocale::system().name().section('_', 0, 0);
 
    QTranslator translator;
    translator.load(QString("zeroclassgenerator_") + locale);
    app.installTranslator(&translator);

    FenPrincipale fenetre;
    fenetre.show();

    return app.exec();
}

Explication : on veut récupérer le code à 2 lettres de la langue du PC de l'utilisateur. On utilise une méthode statique de QLocale pour récupérer des informations sur le système d'exploitation sur lequel le programme a été lancé.

La méthode QLocale::system().name() renvoie un code ressemblant à ceci : "fr_FR", où "fr" est la langue (français) et "FR" le pays (France).
Si vous êtes québecois, vous aurez par exemple "fr_CA" (français au Canada).

Locale

On veut juste récupérer les 2 premières lettres. On utilise la méthode section() pour couper la chaîne en deux autour de l'underscore "_". Les 2 autres paramètres permettent d'indiquer qu'on veut le premier mot à gauche de l'underscore, à savoir le "fr".

Au final, notre variable locale contiendra juste ce qu'on veut : la langue de l'utilisateur (par exemple "fr").
On combine cette variable avec le début du nom du fichier de traduction, comme ceci :

QString("zeroclassgenerator_") + locale

Si locale vaut "fr", le fichier de traduction chargé sera "zeroclassgenerator_fr".
Si locale vaut "en", le fichier de traduction chargé sera "zeroclassgenerator_en".

C'est compris ? ;)

Grâce à ça, notre programme ira chercher le fichier de traduction correspondant à la langue de l'utilisateur. Au pire des cas, si le fichier de traduction n'existe pas car vous n'avez pas fait de traduction dans cette langue, c'est la langue française qui sera utilisée.

Vous voilà maintenant aptes à traduire dans n'importe quelle langue ! :D
Pour information, voilà ce que donne le ZeroClassGenerator traduit en arabe (merci à zoro_2009 pour la traduction !) :

ZeroClassGenerator en arabe

Voilà donc la preuve que Qt peut vraiment gérer tous les caractères de la planète grâce à son support de l'Unicode. :)

Comme vous avez pu le constater, la traduction d'applications Qt est un processus bien rôdé : tout est prévu ! :D

Vous avez maintenant tous les outils en main pour diffuser votre programme partout dans le monde, même au Japon ! Encore faut-il trouver un traducteur japonais...

... et pour ça, désolé les amis, mais je ne pourrai vraiment pas vous aider. :-°

Exemple de certificat de réussite
Exemple de certificat de réussite