Projet

Général

Profil

Gestion traduction » Historique » Version 22

Benjamin Bohard, 30/10/2014 12:21

1 1 Benjamin Bohard
h1. Gestion traduction
2 1 Benjamin Bohard
3 1 Benjamin Bohard
h2. Besoins
4 1 Benjamin Bohard
5 1 Benjamin Bohard
Pouvoir mettre à jour les fichiers de traduction distribués dans les paquets.
6 1 Benjamin Bohard
7 2 Benjamin Bohard
Idéalement, il faut :
8 2 Benjamin Bohard
* un mécanisme permettant de mettre à jour les fichiers .po pour chaque langue déjà traduite et le fichier .pot pouvant servir de point de départ à la traduction dans une nouvelle langue ;
9 6 Benjamin Bohard
* un mécanisme permettant d'être sûr que la version compilée installée est bien à jour.
10 2 Benjamin Bohard
11 1 Benjamin Bohard
h2. Procédures
12 1 Benjamin Bohard
13 3 Benjamin Bohard
Les fichiers de traduction sont installés sous forme compilée (.mo).
14 1 Benjamin Bohard
Ces fichiers compilés ne sont pas éditables directement.
15 3 Benjamin Bohard
Ils sont générés à partir de fichiers texte (.po).
16 1 Benjamin Bohard
17 1 Benjamin Bohard
La mise à jour des fichiers de traduction est faite en trois étapes :
18 1 Benjamin Bohard
# extraction des chaînes de caractères à traduire dans un ensemble de fichiers (création ou mise à jour des fichiers .po),
19 1 Benjamin Bohard
# édition des fichiers .po,
20 1 Benjamin Bohard
# compilation des fichiers .mo à partir des fichiers .po.
21 1 Benjamin Bohard
22 1 Benjamin Bohard
L'extraction des chaînes de caractères à traduire est possible par l'emploi de la commande xgettext (également conseillée par l'auteur de pygettext depuis qu'elle gère le code python).
23 1 Benjamin Bohard
Le passage des .po aux .mo et inversement est possible par l'emploi des commandes msgfmt et msgunfmt réciproquement.
24 1 Benjamin Bohard
25 6 Benjamin Bohard
Les fichiers .po et .mo sont redondants et il n'est pas nécessaire de conserver les deux formats dans les dépôts.
26 1 Benjamin Bohard
Les fichiers .mo ne sont utiles que pour l'exécution des programmes traduits et les fichiers .po sont plus facilement exploitables dans un contexte de dépôt git.
27 1 Benjamin Bohard
28 1 Benjamin Bohard
h2. Implémentation
29 1 Benjamin Bohard
30 13 Benjamin Bohard
# si nécessaire, mettre à jour tous les fichiers .po déjà créés et écraser le fichier .pot servant à créer le point de départ des nouvelles traductions (script lancé par le développeur après changement du code contenant les chaînes à traduire),
31 4 Benjamin Bohard
# si les fichiers .po ont été mis à jour, les éditer,
32 4 Benjamin Bohard
# compiler les fichiers .mo dans un répertoire installé à chaque construction de paquet (cible Makefile lancée automatiquement).
33 7 Benjamin Bohard
34 7 Benjamin Bohard
Les utilitaires à disposition (voir également la documentation gettext au format texinfo, chapitre 9):
35 7 Benjamin Bohard
* xgettext : extraction de chaînes de caractères et création d'un catalogue vide ou ajout des chaînes à un catalogue déjà existant ;
36 7 Benjamin Bohard
* msginit : création d'un catalogue .po pour une langue donnée, à partir d'un catalogue vide .pot ;
37 7 Benjamin Bohard
* msgfmt : compilation d'un catalogue .po au format binaire .mo ;
38 7 Benjamin Bohard
* msgunfmt : décompilation d'un catalogue au format binaire .mo en un catalogue .po ;
39 7 Benjamin Bohard
* msgmerge : assemblage d'un catalogue avec traductions et d'un catalogue avec références mises à jour.
40 8 Benjamin Bohard
41 8 Benjamin Bohard
h3. Arborescence des projets
42 8 Benjamin Bohard
43 8 Benjamin Bohard
Le fonctionnement de gettext nécessite de disposer, pour chaque langue, d'un fichier portant le même nom.
44 8 Benjamin Bohard
Sur le serveur, cela est rendu possible par l'organisation en arborescence avec un répertoire par langue, variante linguistique.
45 8 Benjamin Bohard
46 8 Benjamin Bohard
Dans le dépôt, on propose de distinguer les différentes langues en utilisant également une arborescence :
47 8 Benjamin Bohard
<pre>
48 8 Benjamin Bohard
|
49 20 Benjamin Bohard
`-- translation
50 8 Benjamin Bohard
    |-- creole.pot
51 8 Benjamin Bohard
    |-- en
52 8 Benjamin Bohard
    |   `-- creole.po
53 8 Benjamin Bohard
    `-- fr
54 8 Benjamin Bohard
        `-- creole.po
55 8 Benjamin Bohard
</pre>
56 8 Benjamin Bohard
Le niveau de répertoire LC_MESSAGES présent sur les serveurs n'est pas reproduit dans les dépôts.
57 8 Benjamin Bohard
58 20 Benjamin Bohard
Au moment de la compilation, on recherchera tous les fichiers correspondant au motif translation/*/*.po.
59 8 Benjamin Bohard
60 8 Benjamin Bohard
h3. Mise à jour des .pot et des .po
61 8 Benjamin Bohard
62 14 Benjamin Bohard
La mise à jour des fichiers .pot et .po est une opération lancée si besoin au moyen du script po_update.sh.
63 1 Benjamin Bohard
64 14 Benjamin Bohard
Ce script commence par créer (en écrasant si nécessaire) une version à jour du modèle .pot.
65 14 Benjamin Bohard
Il met ensuite à jour les fichiers de traduction existant déjà dans l'arborescence.
66 14 Benjamin Bohard
Il gére le cas particulier du fichier de traduction en langue anglaise en le créant si il n'existe pas.
67 14 Benjamin Bohard
Il lui suffit pour cela de recopier les chaînes à traduire dans les chaînes traduites.
68 14 Benjamin Bohard
Attention donc à bien utiliser la langue anglaise dans le corps du code pour les chaînes de caractères à traduire.
69 14 Benjamin Bohard
70 8 Benjamin Bohard
Les informations nécessaires pour générer le .pot sont un nom de projet (domaine, également utilisé pour le nom de paquet) et les répertoires ou fichiers dans lesquels chercher les chaînes de caractères.
71 9 Benjamin Bohard
Ces informations doivent être fournies en paramètres au script assurant la génération du .pot et la mise à jour des .po (en cas de chaîne vide, le message d'origine est utilisé).
72 10 Benjamin Bohard
Ces informations seront fournies sous la forme d'un fichier texte structuré comme suit :
73 8 Benjamin Bohard
<pre>
74 21 Benjamin Bohard
projet fichier1.py fichiers2*.py
75 1 Benjamin Bohard
</pre>
76 12 Benjamin Bohard
Le premier mot est le nom du projet, les autres mots de la ligne, des motifs désignant les fichiers (et non les répertoires).
77 11 Benjamin Bohard
78 11 Benjamin Bohard
*Attention :* le langage défini, en dur, dans le script générant le .pot est *python*
79 8 Benjamin Bohard
80 8 Benjamin Bohard
h3. Compilation des .mo
81 8 Benjamin Bohard
82 8 Benjamin Bohard
La compilation des .mo sera faite à la compilation des paquets : le .mo ne sera pas présent dans le dépôt.
83 8 Benjamin Bohard
Les cibles ajoutées dans eole.mk seront tous les fichiers .po disponibles.
84 1 Benjamin Bohard
La recette compilera les .po en .mo directement dans $(DESTDIR)/usr/share/locale/…
85 14 Benjamin Bohard
86 14 Benjamin Bohard
h2. Préparation des programmes à traduire
87 14 Benjamin Bohard
88 14 Benjamin Bohard
h3. Bibliothèques python
89 14 Benjamin Bohard
90 14 Benjamin Bohard
Les bibliothèques python peuvent utiliser gettext pour afficher les chaînes de caractères dans une langue spécifique.
91 14 Benjamin Bohard
Il faut utiliser les fonctions du module gettext auxquelles on passe toutes les chaînes de caractères à traduire.
92 14 Benjamin Bohard
Ces fonctions retournent les chaînes de caractères traduites.
93 14 Benjamin Bohard
94 22 Benjamin Bohard
h4. modèle de fichier i18n.py
95 22 Benjamin Bohard
h5. i18n.py à l'échelle du module
96 14 Benjamin Bohard
97 14 Benjamin Bohard
<pre>
98 14 Benjamin Bohard
import gettext
99 14 Benjamin Bohard
import os
100 14 Benjamin Bohard
import sys
101 14 Benjamin Bohard
102 14 Benjamin Bohard
# Application Name
103 14 Benjamin Bohard
APP_NAME = 'creole'
104 14 Benjamin Bohard
105 14 Benjamin Bohard
# Traduction dir
106 14 Benjamin Bohard
APP_DIR = os.path.join(sys.prefix, 'share')
107 14 Benjamin Bohard
LOCALE_DIR = os.path.join(APP_DIR, 'locale')
108 14 Benjamin Bohard
109 14 Benjamin Bohard
mo_location = LOCALE_DIR
110 14 Benjamin Bohard
111 14 Benjamin Bohard
t = gettext.translation(APP_NAME, fallback=True)
112 14 Benjamin Bohard
113 14 Benjamin Bohard
_ = t.ugettext
114 14 Benjamin Bohard
</pre>
115 14 Benjamin Bohard
116 14 Benjamin Bohard
La variable LOCALE_DIR pointe vers /usr/share/locale.
117 14 Benjamin Bohard
118 14 Benjamin Bohard
La variable APP_NAME définit le nom du domaine, soit le nom permettant d'associer le bon fichier .mo à la bibliothèque.
119 14 Benjamin Bohard
120 14 Benjamin Bohard
_ est utilisé comme "alias" pour la fonction gettext.translation(APP_NAME, "UTF-8").ugettext.
121 14 Benjamin Bohard
_() sera appelée dans le code à chaque fois qu'une chaîne de caractère devra être traduite. 
122 14 Benjamin Bohard
123 14 Benjamin Bohard
L'utilisation de *ugettext* plutôt que *gettext* fait que la chaîne retournée sera de l' *unicode*.
124 14 Benjamin Bohard
125 18 Benjamin Bohard
*Note :* la seule variable dépendante de la bibliothèque est APP_NAME. Le code i18n pourrait être centralisé au niveau de pyeole
126 1 Benjamin Bohard
en utilisant un objet callable.
127 22 Benjamin Bohard
128 22 Benjamin Bohard
h5. i18n à l'échelle du projet EOLE
129 22 Benjamin Bohard
130 22 Benjamin Bohard
Pour limiter le nombre de fichiers i18n.py, il est possible d'utiliser une fonction générique apportée par pyeole.
131 22 Benjamin Bohard
<pre>
132 22 Benjamin Bohard
# -*- coding: UTF-8 -*-
133 22 Benjamin Bohard
# Copyright (C) 2014 Équipe EOLE.
134 22 Benjamin Bohard
#
135 22 Benjamin Bohard
"internationalisation utility"
136 22 Benjamin Bohard
import gettext
137 22 Benjamin Bohard
138 22 Benjamin Bohard
system_locale_dir = "/usr/share/locale"
139 22 Benjamin Bohard
140 22 Benjamin Bohard
class i18n(object):
141 22 Benjamin Bohard
    """callable version of gettext
142 22 Benjamin Bohard
    """
143 22 Benjamin Bohard
    def __init__(self, app_name):
144 22 Benjamin Bohard
        self.t = gettext.translation(app_name, localedir=system_locale_dir, fallback=True)
145 22 Benjamin Bohard
146 22 Benjamin Bohard
    def __call__(self, *args):
147 22 Benjamin Bohard
        # "polymorphism" rather than duck-typing to ease xgettext extraction work (keywordspec format)
148 22 Benjamin Bohard
        if len(args) == 1:
149 22 Benjamin Bohard
            return self.t.ugettext(*args)
150 22 Benjamin Bohard
        else:
151 22 Benjamin Bohard
            return self.t.ungettext(*args)
152 22 Benjamin Bohard
</pre>
153 22 Benjamin Bohard
154 22 Benjamin Bohard
L'appel dans les bibliothèques se fait en deux temps : import de la classe i18n et assignation de la variable _ pour disposer d'un appel peu intrusif.
155 22 Benjamin Bohard
<pre>
156 22 Benjamin Bohard
from pyeole.i18n import i18n
157 22 Benjamin Bohard
_ = i18n('lib')
158 22 Benjamin Bohard
</pre>
159 22 Benjamin Bohard
160 22 Benjamin Bohard
Le contructeur de la classe i18n prend en argument le nom du projet utilisé pour nommer le fichier *.po et identifier le domaine de l'application.
161 18 Benjamin Bohard
162 14 Benjamin Bohard
h4. exemple d'utilisation
163 14 Benjamin Bohard
164 14 Benjamin Bohard
h5. importation de la fonction _()
165 14 Benjamin Bohard
166 15 Benjamin Bohard
Dans le cas d'un fichier i18n dans chaque projet :
167 14 Benjamin Bohard
<pre>
168 14 Benjamin Bohard
from i18n import _
169 14 Benjamin Bohard
</pre>
170 1 Benjamin Bohard
171 17 Benjamin Bohard
*FIXME* Dans le cas d'une mutualisation de i18n au niveau de pyeole :
172 15 Benjamin Bohard
173 15 Benjamin Bohard
<pre>
174 15 Benjamin Bohard
175 15 Benjamin Bohard
</pre>
176 14 Benjamin Bohard
177 14 Benjamin Bohard
h5. traduction d'une chaîne de caractères
178 14 Benjamin Bohard
179 14 Benjamin Bohard
<pre>
180 14 Benjamin Bohard
# chaîne non traduite
181 14 Benjamin Bohard
print u"some words"
182 14 Benjamin Bohard
183 14 Benjamin Bohard
#chaîne qui sera traduite 
184 14 Benjamin Bohard
#en fonction des paramètres linguistiques du contexte d'exécution de la bibliothèque
185 14 Benjamin Bohard
#et de la disponibilité de la traduction dans /usr/share/locale
186 14 Benjamin Bohard
print _(u"some words")
187 14 Benjamin Bohard
</pre>
188 14 Benjamin Bohard
189 14 Benjamin Bohard
*Attention :* toujours garder à l'esprit que la chaîne retournée par la fonction _() est de l'unicode si la fonction ugettext a été utilisée dans le fichier i18n.py.
190 14 Benjamin Bohard
Il peut être nécessaire de l'encoder.
191 14 Benjamin Bohard
192 14 Benjamin Bohard
Par convention, on utilisera la langue anglaise dans le code et on fournira la traduction française dans les fichiers de traduction.