Python et gettext

L'internationalisation facile avec Python

Alexandre GALODE

Dans tout langage, si l'on souhaite diffuser aisément ses logiciels, nous avons le choix de créer le texte de l'interface en anglais, en dur dans le code, ou bien de passer par un système multilingue.

Dans ce dernier cas, chacun peut y aller à sa façon: base de données, JSON, INI, fichier texte, …

Python est livré de base avec un module dédié fort pratique que je vous invite à découvrir ici.

- Titre : Python et gettext

- Auteur : Alexandre GALODE

- Parution : 11 novembre 2017

- Public visé :

- Licence : Le contenu de cet article est rédigé par Alexandre GALODE et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. La théorie

- III. La pratique

- III-A. Présentation de poedit

- III-B. Préciser le texte à traduire dans votre code

- III-C. Extraire le texte à traduire de votre code

- III-D. Générer un template

- III-E. Générer un fichier de traduction

- III-F. Mettre à jour un fichier de traduction à partir du code

- III-G. Insérer un fichier de traduction dans votre code

- III-H. Mise en œuvre

- IV. Conclusion

- V. Remerciements

I - Introduction

L'internationalisation d'une application est parfois abrégée en i18n (18 représente alors le nombre de caractères entre le " i " et le " n ").

Derrière ces noms un peu " barbares " se cache un principe très simple: permettre une traduction aisée dans une nouvelle langue. Et pour arriver à cela, chaque développeur peut utiliser la méthode qu'il souhaite.

Il existe cependant un mécanisme plus ou moins standard en informatique: les fichiers " po ".

Même s'il peuvent sembler difficile d'accès, ces fichiers sont en réalité très simples à mettre en œuvre. D'autant plus en Python, grâce à la librairie " gettext ".

Cet article est écrit pour Python3.x.

II - La théorie

Avant de plonger dans le code, nous allons démystifier un peu le mécanisme des traductions.

Avant toute chose, sachez que vous devrez coder du texte en dur dans votre application. En général, il est recommandé d'écrire ce texte en anglais.

Une fois votre application terminée, l'ensemble du texte sera extrait dans un fichier dit " template ", à l'extension " .pot ", cela grâce à un logiciel externe.

En ouvrant ce fichier template depuis un logiciel dédié, il ne vous restera plus qu'à indiquer la langue de la traduction, puis à enregistrer le fichier avec l'extension " .po ".

Dans les faits, vous verrez que cela est un peu plus compliqué, mais pas de beaucoup, rassurez vous.

III - La pratique

III-A - Présentation de poedit

Dans notre cas, Poedit est le logiciel qui permettra à la fois l'extraction du texte,= depuis le code (fichiers " .pot "), et la traduction (fichiers " .po ").

Poedit est un logiciel Open Source, écrit en C++, basé sur WxWidgets et multiplateformes, sous licence MIT.

Son interface est très simple et se résume au strict nécessaire : la liste du texte à traduire, une zone avec le texte originel, et le texte traduit.

III-B - Préciser le texte à traduire dans votre code

Avant toute chose, le texte devra être écrit en dur, de préférence en anglais, dans votre code. Il devra être entouré de parenthèses, et ces parenthèses devront être précédées d'un underscore.

1. exemple = _("exemple de texte")

Cependant, si vous faites cela dans votre code, vous aurez fort probablement des erreurs à l'exécution.

Pour y pallier, il est nécessaire de rajouter quelques lignes au début de chacun de vos fichiers.

1. import gettext

2. try:

3. traduction = gettext.translation('<nom_du_fichier>', localedir='locale', language=['es'])

4. traduction.install('<nom_du_fichier>')

5. except:

6. gettext.install('<texte_au_choix>')

8. ...

Et voilà. Quelques explication maintenant.

Tout d'abord la première ligne présente dans le " try ". Nous demandons à Python de charger le fichier de traduction " <nom_du_fichier>.po " de la langue indiquée (ici " es ", donc espagnol), et de le chercher dans le dossier " locale ".

Une fois cela effectué, nous utilisons la méthode " install ". Cette méthode permet à Python d'interpréter correctement les " _(...) ".

Si le fichier n'existe pas, nous fonctionnerons sur le texte codé en dur et le logiciel sera alors simplement en anglais (ou dans la langue dans laquelle vous aurez choisi de l'écrire ).

Enfin, nous utilisons les traductions chargées pour traduire notre texte.

Mais revenons un peu sur la recherche de la langue demandée. En général, la structure des fichiers de traduction est:

/<dossier logiciel>/locale/<sigle_de_la_langue>/ LC_MESSAGES/<nom_du_fichier>.mo

Le paramètre <sigle_de_la_langue> respecte la norme ISO3166-1. Le paramètre <nom_du_fichier> est parfois également appelé domain.

Pour la suite de ce tutoriel, je vous propose le petit logiciel suivant. Rien de bien sorcier, mais le but ici est de voir le fonctionnement du mécanisme de traduction.

test_po.py

1. import gettext

3. try:

4. gettext.find("test")

5. traduction = gettext.translation('test', localedir='locale', languages=['fr'])

6. traduction.install()

7. except:

8. gettext.install('test')

11. def display():

12. print(_("Hello everybody on {}").format("DVP"))

14. if __name__ == "__main__":

15. display()

III-C - Extraire le texte à traduire de votre code

Il s'agit de la première étape. Lancez poedit.

Maintenant, ouvrez le menu Fichier, cliquez ensuite sur Nouveau. Sur la nouvelle fenêtre, renseignez une langue dans la pop-up (en général la langue du texte en dur de votre code), validez, puis cliquez sur Enregistrer.

Vous avez alors l'écran suivant:

Cliquez alors sur l'onglet Extraire depuis les sources. Une fenêtre à trois onglets apparaît alors. Dans les faits, seules les deux premières vous serviront la plupart du temps.

Dans ce premier onglet, vous devez indiquer le nom du projet associé, et sa version.

Dans ce second onglet, vous pouvez indiquer les dossiers à analyser, et/ou à exclure. Notez que l'analyse est récursive.

Une fois ces deux onglets paramétrés, cliquez sur Valider. L'analyse des sources est alors lancée puis une fenêtre avec les traductions à effectuer apparaît.

Rendu à ce niveau, ne touchez à rien, vous êtes prêt pour l'étape suivante

III-D - Générer un template

Un fichier template est un fichier sans aucune traduction. Dans les faits, une fois l'étape précédente effectuée, fermez Poedit.

Modifier l'extension de votre fichier de .po à .pot. Votre fichier template est prêt.

III-E - Générer un fichier de traduction

Pour générer un fichier de langue, à partir de votre fichier template, commencez par ouvrir Poedit.

Cliquez sur Créer une nouvelle traduction, et sélectionnez votre fichier template.

Une pop-up vous demande alors de choisir la langue de la traduction. Ici nous allons sélectionner Francais.

Vous êtes alors libre d'effectuer les diverses traductions.

Lorsque vous allez cliquer sur Enregistrer, l'enregistrement aura lieu au format .po.

Les fichiers .po et .mo (fichier compilé à partir du .po, exploité par gettext) sont alors à placer dans la bonne arborescence.

Voici un exemple typique, avec une seconde traduction, espagnole.

Nous voyons ici le dossier locale qui contient des dossiers respectant la norme internationale de sigles. Chacun de ces dossiers contient un dossier LC_MESSAGES. Enfin ces dossiers contiennent les fichier de langues.

III-F - Mettre à jour un fichier de traduction à partir du code

Maintenant, je vais vous proposer une petite mise à jour de notre code.

test_po.py

1. import gettext

3. try:

4. gettext.find("test")

5. traduction = gettext.translation('test', localedir='locale', languages=['fr'])

6. traduction.install()

7. except:

8. gettext.install('test')

11. def display():

12. print(_("Hello everybody on {}").format("DVP"))

13. print(_("Please have fun"))

15. if __name__ == "__main__":

16. display()

Nous avons simplement ajouté une ligne avec un texte. Cela simulera une évolution de code, en l'occurence ici un nouveau texte à traduire.

Il faut donc maintenant mettre à jour à la fois notre template et nos fichier de traduction. Mais comment ?

Avec Poedit, rien de plus simple.

Ouvrez le fichier template avec Poedit, puis cliquer sur le bouton Mettre à jour. Il se peut alors qu'un message d'erreur apparaisse.

Dans ce cas, il faut mettre à jour les chemins des codes sources. Direction le menu Catalogue, puis Propriétés. Dans l'onglet Chemins des sources, mettez alors à jour les listings, tel que vu précédemment.

Lors de la sélection d'un dossier dans les chemins à analyser, regardez bien le chemin indiqué par la fenêtre de sélection. Si vous n'avez pas à sélectionner le dossier désiré, cliquez sur un autre disque, puis revenez sur l'emplacement désiré.

Et voilà, notre nouveau texte vient d'apparaître dans la liste des traductions à effectuer. Vous n'avez plus qu'à enregistrer le fichier.

Pour les fichiers de langues, procéder de la même façon.

III-G - Insérer un fichier de traduction dans votre code

Quand vous exécutez votre code, il peux être pratique de connaître la liste des langues disponible.

Voici un petit code vous permettant de lister les dossiers à un emplacement donné.

1. import os

2. ...

3. l = [item for item in os.listdir(".") if os.path.isdir(os.path.join(".", item)) and not item.startswith(".")]

III-H - Mise en œuvre

Maintenant, voyons un peu ce que cela donne à l'exécution. Nous allons commencer par traduire correctement notre code en français puis en espagnol.

test_po.py

1. import gettext

3. try:

4. gettext.find("test")

5. traduction = gettext.translation('fr', localedir='locale', languages=['fr'])

6. traduction.install()

7. except:

8. gettext.install('test')

11. def display():

12. print(_("Hello everybody on {}").format("DVP"))

13. print(_("Please have fun"))

15. if __name__ == "__main__":

16. display()

test_po.py

1. import gettext

3. try:

4. gettext.find("test")

5. traduction = gettext.translation('es', localedir='locale', languages=['es'])

6. traduction.install()

7. except:

8. gettext.install('test')

11. def display():

12. print(_("Hello everybody on {}").format("DVP"))

13. print(_("Please have fun"))

15. if __name__ == "__main__":

16. display()

Tout cela fonctionne, mais maintenant que se passe-t-il si nous avons passé un nom de fichier erroné, si le fichier cible n'existe pas ou si tout simplement la langue demandée n'est pas gérée?

test_po.py

1. import gettext

3. try:

4. gettext.find("test")

5. traduction = gettext.translation('bzh', localedir='locale', languages=['bzh'])

6. traduction.install()

7. except:

8. gettext.install('test')

11. def display():

12. print(_("Hello everybody on {}").format("DVP"))

13. print(_("Please have fun"))

15. if __name__ == "__main__":

16. display()

Avez vous remarqué ? En cas d'erreur, c'est simplement le texte codé en dur qui est affiché. De fait, votre logiciel sera toujours fonctionnel, mais dans la langue codée en dur.

IV - Conclusion

Comme nous venons de le voir ensemble, faire de l'internationalisation avec Python est on ne peut plus facile.

Le mécanisme est très simple à mettre en œuvre et à maintenir à jour. De plus, se basant sur les standard " po ", il sera aisé de trouver de bonnes âmes pour réaliser les traductions nécessaires à une diffusion multilingues.

J'espère que ce petit tutoriel vous aura permis d'appréhender cette méthode de traduction multilingue, et qu'elle sera rapidement mis en application dans vos futurs projets.

V - Remerciements