Outils pour utilisateurs

Outils du site


pyqt4_multilingue

Internationalisez vos programmes PyQt4

En construction!

Versions utilisées ici: Python v2.7, PyQt4 v4.10

Objectif

Vous avez fait un beau programme en français, il fonctionne bien et vous en êtes fier.

Vous voulez en assurer une plus grande diffusion non seulement dans les pays francophones, mais ailleurs partout dans le monde: vous devez assurer que les utilisateurs pourront utiliser votre programme dans leur langue, ou au minimum en anglais: c'est l'objectif de cette page.

Bien sûr, rien ne vous empêche d'écrire votre programme d'abord en anglais ou dans n'importe quel autre langue, et d'assurer la traduction en français et dans n'importe quelle autre langue: la méthode décrite ici est générale.

Présentation générale de la méthode

Voilà la méthode qu'on va utiliser:

1- Toutes les chaines de caractères à traduire, écrites dans le code, deviennent argument d'une méthode permettant la traduction. Par exemple dans une classe: “Bonjour” ⇒ self.tr(“Bonjour”). On verra qu'il y a d'autres méthodes que “tr()”.

2- on définit une fichier projet “xxx.pro” qui contient la liste des fichiers à considérer (.py, .pyw, .ui), ainsi que la liste des fichiers de traduction à construire (.ts)

3- on utilise le programme “pylupdate4” dans une console pour trouver toutes les lignes à traduire de tous les fichiers définis dans le fichier projet “xxx.pro”. Le résultat se trouve dans des fichiers “.ts” (par exemple “xxx_en_EN.ts” pour contenir les traductions français ⇒ anglais si votre programme a été écrit en français).

4- on utilise un programme graphique “linguist” avec lequel on va définir toutes les traductions de toutes les chaines de caractères à traduire, dans toutes les langues souhaitées, à partir des fichiers “.ts”. Le résultat se trouve dans des fichiers “.qm” (par exemple xxx_en_EN.ts donnera xxx_en_EN.qm), et ce seront ces fichiers .qm qui seront utilisés pendant l'exécution du programme.

5- il ne reste plus qu'à définir au lancement ou pendant l'exécution du programme la langue qui sera effectivement à utiliser pendant l'utilisation du programme. Plusieurs façons de faire sont possible, mais on peut, par exemple, partir de la langue de l'OS utilisé.

Heureusement, pendant le développement, il ne faut pas tout refaire à partir de zéro à chaque fois qu'on modifie le code: les programmes utilisés ci-dessus savent dans une large mesure mettre à jour les fichiers “.ts” et “.qm” existants, et il ne faudra ré-intervenir avec linguist que pour ajouter, supprimer ou modifier les traductions.

On verra de plus les points suivants:

Changement de langue statique ou dynamique: le cas général est de sélectionner la langue au lancement du programme concerné. On peut, comme ça existe souvent, sélectionner une autre langue, mais cela nécessite de relancer le programme. Le fait de changer la langue en dynamique, c'est à dire pendant l'exécution du programme et sans le relancer, est moins courant mais possible. Cela amène cependant une complexité supplémentaire.

Dictionnaire standard de traduction (les phrasebooks): Les programmes graphiques contiennent souvent les mêmes mots dans les différents programmes et dans toutes les langues. Par exemple: “fichier”⇒“file”, “outils”⇒“tools”, “aide”⇒“help”, etc… On peut donc utiliser des sortes de “dictionnaires standards” dans chacune des traductions langue1⇒langue2, par exemple français⇒anglais, pour gagner du temps. Ces dictionnaires s'appellent des “phrasebooks” et se trouveront dans des fichiers texte “.qph”. On pourra les définir, les modifier et les utiliser dans le logiciel “linguist”.

Voyons maintenant dans le détail chacun des points de cette méthode.

La méthode

1- le codage des chaines à traduire

Il s'agit d'identifier chacune des chaines à traduire, et ceci pour 2 motifs:

  • permettre de les repérer (pylupdate4) par une analyse de texte
  • permettre à l'exécution du programme d'afficher les textes dans la bonne langue

La solution utilisée en C++ par Qt4 est utilisable en PyQt4:

  • chaine ⇒ tr(chaine) ou self.tr(chaine) pour le code écrit en latin1
  • chaine ⇒ trUtf8(chaine) ou self.trUtf8(chaine) pour le code écrit en utf-8

La solution avec self est utilisée à l'intérieur des classes, et a l'avantage de considérer le nom de la classe comme “contexte”, ce qui permet de ne pas mélanger les traductions de l'ensemble du code.

Cette solution a le grand mérite d'être très simple, mais possède 2 inconvénients signalés pour PyQt4 par riverbank:

  • 1- sous PyQt4, tr et trUtf8 ne transmettent pas les traductions par héritage, du fait d'une différence de conception entre C++ et Python.
  • 2- dans le futur, le maintien de tr et trUtf8 n'est pas garanti. La méthode recommandée est plutôt: QtGui.QApplication.translate(…).

Le point 1 est embêtant mais pas incontournable: par exemple, vous créez une classe avec des chaines à traduire. Si vous créez une nouvelle classe qui hérite de la classe précédente, il suffira de recommencer les traductions! Selon les programmes, cela peut être plus ou moins rare et donc plus ou moins génant.

On peut aussi apporter une modification à l'utilisation de tr et trUtf8: changer sa définition:

class Traduc(QtCore.QObject):
    def __init__(self): 
        QtCore.QObject.__init__(self)
    def __call__(self, texte, disambig=None, n=-1):
        return QtGui.QApplication.translate("@default", texte, disambig, 
                                            QtGui.QApplication.CodecForTr, n)
tr = Traduc()
 
class TraducUtf8(QtCore.QObject):
    def __init__(self): 
        QtCore.QObject.__init__(self)
    def __call__(self, texte, disambig=None, n=-1):
        return QtGui.QApplication.translate("@default", texte, disambig, 
                                            QtGui.QApplication.UnicodeUTF8, n)
trUtf8 = TraducUtf8()

Ces 2 modifications peuvent être placées:

  • en tête de chacun des modules Python du programme comportant des chaines à traduire,
  • ou dans un module spécifique importé par ces modules (ex: “from traduc import tr, trUtf8”).

L'avantage de ces 2 modifications est que c'est toujours aussi simple qu'en Qt4/C++ de définir les chaines à traduire.

Mais il y a tout de même un inconvénient: on perd la possibilité de distinguer les chaines à traduire par leur contexte, parce que le contexte est “@default” pour toutes les chaines du programme.

Cependant, il est tout de même possible de distinguer 2 chaines identiques ayant des traductions différentes grâce au paramètre supplémentaire “disambig”. Par exemple, on peut distinguer la chaine “Salut”, qui peut avoir en anglais la traduction “Hello” ou “Bye”, de la façon suivante:

  • tr(“Salut”, “1”) #⇒ “Hello”
  • tr(“Salut”, “2”) #⇒ “Bye”

Ces 2 chaines apparaîtront séparément dans les fichiers .ts, ce qui permettra avec linguist de leur affecter les 2 traductions différentes “Hello” et “Bye”.

Au lieu d'utiliser tr ou trUtf8, on peut aussi utiliser la solution recommandée pour PyQt4: QtGui.QApplication.translate(…). Voyons comment.

Pour un code écrit en encodage latin1:

# pour la chaine "Bonjour"
QtGui.QApplication.translate("contexte", "Bonjour")

Pour un code écrit en encodage utf-8:

# pour la chaine "Bonjour":
QtGui.QApplication.translate("contexte", "Bonjour", None, QtGui.QApplication.UnicodeUTF8)

Le contexte est en général le nom de la classe. Si ce n'est pas dans une classe, vous pouvez mettre le nom du module, “@default” ou n'importe quoi d'autre qui ne rentre pas en conflit avec le nom d'une classe.

Attention: comme le logiciel pylupdate4 repèrera les chaines à traduire uniquement par analyse de texte, les chaines doivent être en clair (et non sous forme de variables) et les mots “translate” et (pour l'utf-8), “UnicodeUTF8” doivent pouvoir être trouvés. On peut donc simplifier un peu la ligne en mettant au début de chaque page de code:

translate = QtGui.QApplication.translate
UnicodeUTF8 = QtGui.QApplication.UnicodeUTF8

les lignes deviennent alors:

translate("contexte", "Bonjour")
translate("contexte", "Bonjour", None, UnicodeUTF8)

Résumé: vous choisissez l'une des 3 solutions suivantes:

1- utilisation de tr() ou trUtf8() comme en C++, mais si vous avez des classes qui héritent d'autres classes ayant des traductions, vous devrez AUSSI les traduire.

2- idem avec une définition modifiée comme ci-dessus. Les traductions seront transmises par héritage, mais tous les contextes seront “@defaut” et vous devrez utiliser l'argument disambig pour distinguer les chaines identiques qui ont des traductions différentes.

3- utilisation de QtGui.QApplication.translate(…) qui est la solution recommandée par riverbank pour PyQt4, avec pour seul inconvénient des lignes beaucoup plus longues. On peut simplifier un peu en créant les variables translate et unicodeUTF8 comme expliqué ci-dessus: c'est probablement la meilleure solution sur le plan technique.

2- créer un fichier projet .pro

Si votre programme à traduire n'est composé que d'une seule page, vous n'avez peut-être pas besoin d'un fichier projet, mais c'est tout de même très pratique, parce que ça simplifie beaucoup l'appel des programmes suivants: pylupdate4 (création ou mise à jour des fichiers .ts) et lrelease (mise à jour des fichiers .qm).

Le fichier projet est un fichier texte avec une extension “.pro”, et qui contient, par exemple:

FORMS = test01_ui.ui
SOURCES = test01.py
TRANSLATIONS = test01_en_EN.ts

FORMS donne la liste des fichiers .ui issus de qtdesigner, séparés par des espaces. Il est à noter qu'on n'utilise pas dans ce cas les fichiers Python traduits grâce à pyuic4.

SOURCES donne la liste des fichiers .py ou .pyw séparés par des espaces.

TRANSLATIONS donne la liste des fichiers .ts, séparés par des espaces, qui seront créés par pylupdate4, et qui, après traitement par linguist, porteront les traductions.

Chaque fichier peut être entouré par des guillemets si son nom contient des espaces. S'il y a plusieurs fichiers, on peut les mettre sur plusieurs lignes en utilisant '\' pour signaler la ligne suivante. Par exemple:

SOURCES = test01.pyw \
          test02.py \
          test03.py

Il pourrait y avoir aussi des fichiers “ressource” avec:

RESOURCES = fichieressource.qrc

Chaque nom de fichier peut être précédé par son chemin relatif ou absolu.

Il y a aussi 2 lignes supplémentaires possibles concernant les encodages. Par exemple:

CODECFORSRC     = UTF-8
CODECFORTR   = UTF-8

Enfin, il est possible d'avoir des commentaires dans les fichiers pro: il suffit de commencer la ligne par '#'.

3- détecter les chaines à traduire avec pylupdate4

pylupdate4 est un programme à utiliser en console qui va faire une analyse de texte des codes Python et va créer ou mettre à jour les fichiers .ts.

Si on n'a pas créé de fichier .pro (voir le point précédent), la syntaxe d'appel est (par exemple):

pylupdate4 -noobsolete -verbose fichier.ui fichier.py -ts fichier_en_EN.ts fichier_de_CH.ts

Et si on a créé un fichier pro:

pylupdate4 -noobsolete -verbose fichier.pro

On peut examiner le fichier .ts créé ainsi pour comprendre comment il est constitué. Par exemple:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0" language="en" sourcelanguage="fr">
<context>
    <name>Fenetre</name>
...



...
</context>
</TS>

4- créer les traduction avec linguist

5- sélectionner la langue dans le code

Changement de langue statique ou dynamique

Dictionnaire standard de traduction (les phrasebooks)

pyqt4_multilingue.txt · Dernière modification: 2013/11/29 11:05 par tyrtamos

Outils de la page