Créer un programme c'est bien. Encore faut-il que les utilisateurs puissent y accéder. Et malheureusement, les utilisateurs ne sont pas forcément des gens ayant exactement le même schéma de pensée et les mêmes connaissances que les développeurs. Conséquence : il faut s'adapter aux utilisateurs, notamment il faut que le programme soit disponible dans leur langue.

Lorsque le-dit programme est réalisé en python, et éventuellement son interface avec glade, c'est relativement trivial, encore faut-il savoir comment faire... et la documentation manque ou date un peu. Voici donc la méthode - je l'espère - complète, si vous avez des suggestions, n'hésitez pas, les commentaires sont là pour ça.

Les informations de ce document sont principalement tirées de la documentation du module gettext de python, du How do I internationalize a PyGTK and libglade program? de la FAQ de PyGTK, et du wiki de wxPython.

Introduction

La traduction d'un programme va se dérouler en deux phases. En premier, la préparation du programme (i18n), phase qu'il est préférable de poursuivre tout au long du développement, qui consiste à écrire le programme en anglais (générallement), et à marquer tous les textes et toutes les données qui sont spécifiques à l'anglais ou au Royaume-Unis. Et en second la traduction et l'adaptation à chaque langue ou pays (l10n).

Pour réaliser tout ça, nous allons utiliser les outils GNU gettext, et le module python gettext.

Les outils GNU gettext sont fournis avec certains systèmes comme les distributions GNU/Linux. Ils vont nous servir à réaliser les opérations des développeurs et des localisateurs, mais ne sont pas utiles sur la machine de l'utilisateur, où notre programme n'aura besoin que du module python gettext, fourni en standard avec la distribution python.

Les outils GNU gettext sont très pratiques, s'ils ne sont pas installés sur votre système, je vous recommande de vous rendre sur leur site officiel et de les télécharger. Néanmoins, si vous souhaitez vous en passer, il existe deux programmes fournis en standard avec votre distribution python, pygettext et msgfmt.py qui peuvent éventuellement remplacer xgettext et msgfmt des outils GNU gettext. Mais ces outils ne remplissent pas toutes les fonctions des outils GNU gettext, et sont considérés comme obsolètes (malgré que la documentation python officielle les conseille). En particulier, si vous voulez traduire une interface réalisée avec glade, vous devrez utiliser les outils GNU gettext.

Dans tous les cas, je recommande les outils GNU gettext, ce tutoriel sera fait pour eux.

Internationalisation

La première étape consiste à marquer dans les différents modules python les chaînes de caractère à traduire. Pour cela, on les passera comme argument à une fonction nommée par convention _ (le caractère de soulignement). Par exemple :

print "Hello world"

va devenir :

print _("Hello world")

Vous devez aussi faire attention aux chaînes de caractères qui contiennent des parties variables. Par exemple, n'écrivez pas :

print _("Hello ") + name + _("!")

qui obligerait le traducteur à traduire d'abord "Hello " (avec une espace), puis "!", mais plutôt :

print _("Hello %s!") % name

Et si plusieurs variables doivent être insérées dans la chaîne, nommez-les et utilisez un dictionnaire et non une liste, ce qui permettra au traducteur d'intervertir les variables. Par exemple, n'écrivez pas :

print _("Hello %s, today is %s") % (name, day)

mais plutôt :

print _("Hello %(name)s, today is %(day)s") % {'name': name, 'day': day}

Faites aussi attention aux formats de nombre, de date, de monnaie, qui dépendent évidement du pays de l'utilisateur. Si vous utilisez glade pour réaliser votre interface, vous pouvez choisir quels textes sont à ne pas traduire (ils sont traduisibles par défaut).

La seconde étape consiste à modifier le programme pour définir la fonction _() en utilisant le module gettext de python. Cela se fait simplement sous un système de type GNU/Linux, mais demande bien plus de code sous Microsoft Windows, aussi vous trouverez un exemple uniquement pour GNU/Linux, et un exemple multi-plateforme.

Dans tous les exemples qui suivent, notre application s'appelle coincoin, est lancée par coincoin.py, et son éventuelle interface glade est contenue dans le fichier coincoin.glade.

Le seul fichier à modifier est le fichier qui sera exécuté (coincoin.py). S'il importe des modules avec des chaînes marquées par _(), celles-ci seront aussi traduite (si vous ne voulez pas que cela ait lieu, consultez la documentation python).

Les localisations seront stockées dans un dossier locale. Celui-ci est en général placé dans /usr/share/locale dans les systèmes GNU, et détecté automatiquement par le module gettext, mais devra être précisé sous Microsoft Windows (j'utiliserai le dossier locale du dossier parent où est exécuté le programme, mais vous pouvez en choisir un autre).

Exemple uniquement pour les systèmes GNU/Linux (et probablement autres systèmes non Microsoft Windows) :

# i18n
application = 'coincoin'
import gettext
gettext.install(application)

[...]

# Si votre programme utilise glade, précisez bien le domaine gettext application:
gui = gtk.glade.XML(fname="coincoin.glade", domain=application)

Exemple multi-plateforme :

# i18n
application = 'coincoin'
import gettext
if os.name == 'nt':
    # Code pour Microsoft Windows

    # Chemin du dossier locale sous windows
    win_local_path = os.path.abspath(os.path.join(os.path.pardir, 'locale'))
    
    # Code pour une éventuelle interface glade
    gtk.glade.bindtextdomain(application, win_local_path)
    gtk.glade.textdomain(application)
    
    # Code pour le programme python (le module local permet de déterminer la langue actuelle)
    import locale
    lang = locale.getdefaultlocale()[0][:2]
    try:
        cur_lang = gettext.translation(application, localedir=win_local_path, \
                                   languages=[lang])
        cur_lang.install()
    except IOError:
        # Si la langue locale n'est pas supportée, on définit tout de même _()
        # On le fait dans les __builtins__ pour que la fonction soit définie dans
        # les modules importés (c'est ce que fait gettext.install()).
        __builtins__._ = lambda text:text
else :
    # Code pour les autres systèmes d'exploitation
    gettext.install(application)

[...]

# Si votre programme utilise glade, précisez bien le domaine gettext application:
gui = gtk.glade.XML(fname="coincoin.glade", domain=application)

Il reste une dernière étape : la création d'un modèle de traduction. Celui-ci servira pour créer de nouvelles traductions ou les mettre à jour. Nous allons utiliser l'outil xgettext. Dans un terminal, rendez-vous dans le dossier où sont stockés vos modules python et vos éventuels fichiers glade, et lancez la commande :

xgettext -k_ -kN_ -o coincoin.pot *.py *.glade

Vous allez obtenir un fichier coincoin.pot, qui est le modèle de vos futures traductions. À chaque changement du programme vous devrez le mettre à jour en relançant cette commande (vous avez bien sûr le droit de ne le faire que lorsque votre release est terminée, pas la peine de vous embêter à le faire tous les caractères modifiés).

Localisation

Cette partie devra être refaite pour chaque langue.

La communauté francophone des utilisateurs de coincoin râle : coincoin est en anglais, et ils aimeraient que ça change. Un gentil traducteur se propose. Voilà ce qu'il devra faire :

  1. Créer une nouvelle localisation, en utilisant par exemple msginit. Dans un terminal et dans le dossier où se trouve coincoin.pot :

    msginit -i coincoin.pot -o locale/fr/LC_MESSAGES/coincoin.po

    Dans cet exemple, locale qui représente le dossier locale du programme, et fr le code de la lange (les caractères avant le _ dans $LANG). Le programme msginit va créer une nouvelle traduction basée sur la langue actuelle de l'utilisateur (si vous souhaitez créer une traducion pour une autre langue, lisez man msginit), et demander quelques informations comme l'adresse mail du traducteur.

  2. Ouvrir le fichier coincoin.po avec un éditeur de texte (il existe aussi des logiciels dédiés à l'édition de fichier po), et traduire les différentes chaînes de caractères. Un fichier po s'organise de la façon suivante :

    #: fichiers_de_la_chaine:position
    msgid "Original string"
    msgstr "Chaîne traduite"
    
    #: coincoin.py:1024
    msgid "Coin coin string"
    msgstr ""
    "Cette chaîne s'étend "
    "sur plusieurs lignes."
    

    N'oubliez pas d'informer votre traducteur de la signification en python de %s et %(bidule)s.

  3. Si votre programme a été modifié depuis la création de la traduction, le traducteur devra mettre à jour son fichier depuis le modèle pot (que vous aurez pris soin de recréer) à l'aide de la commande (lancée depuis le dossier où se trouve le modèle) :

    msgmerge -U locale/fr/LC_MESSAGES/coincoin.po coincoin.pot

    Celle-ci commentera (par ajout de dièse # en début de ligne) les traductions obsolètes et rajoutera les nouvelles chaînes à traduire. Le fichier devra être relu et complété.

  4. Enfin, pour être utilisable par le programme, la traduction doit être compilée. Cela s'effectue par la commande (dans le dossier où se trouve le fichier po) :

    msgfmt coincoin.po -o coincoin.mo

    Le fichier mo produit devra être distribué dans les paquets binaires de votre application, dans l'arborescence correspondante du dossier local de votre programme.

Ça y est, la communautée francophone des utilisateurs de coincoin est contente ! Le programme fonctionne dans leur langue.