Gestion traduction » Historique » Version 36
Gérald Schwartzmann, 11/01/2019 11:05
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 | 27 | Fabrice Barconnière | * 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 | 27 | Fabrice Barconnière | 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 | 27 | Fabrice Barconnière | 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 | 27 | Fabrice Barconnière | # extraction des chaînes de caractères à traduire dans un ensemble de fichiers (création ou mise à jour des fichiers *.po*), |
19 | 27 | Fabrice Barconnière | # édition des fichiers *.po*, |
20 | 27 | Fabrice Barconnière | # compilation des fichiers *.mo* à partir des fichiers *.po*. |
21 | 1 | Benjamin Bohard | |
22 | 36 | Gérald Schwartzmann | Exemples du projet creole : |
23 | 36 | Gérald Schwartzmann | * ./creole/eosfunc.py |
24 | 36 | Gérald Schwartzmann | * ./translation/fr/creole.po |
25 | 36 | Gérald Schwartzmann | |
26 | 27 | Fabrice Barconnière | 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*). |
27 | 27 | Fabrice Barconnière | Le passage des *.po* aux *.mo* et inversement est possible par l'emploi des commandes *msgfmt* et *msgunfmt* réciproquement. |
28 | 1 | Benjamin Bohard | |
29 | 27 | Fabrice Barconnière | Les fichiers *.po* et *.mo* sont redondants et il n'est pas nécessaire de conserver les deux formats dans les dépôts. |
30 | 27 | Fabrice Barconnière | 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. |
31 | 1 | Benjamin Bohard | |
32 | 1 | Benjamin Bohard | h2. Implémentation |
33 | 1 | Benjamin Bohard | |
34 | 27 | Fabrice Barconnière | # 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), |
35 | 27 | Fabrice Barconnière | # si les fichiers *.po* ont été mis à jour, les éditer, |
36 | 27 | Fabrice Barconnière | # compiler les fichiers *.mo* dans un répertoire installé à chaque construction de paquet (cible *Makefile* lancée automatiquement). |
37 | 7 | Benjamin Bohard | |
38 | 27 | Fabrice Barconnière | Les utilitaires à disposition (voir également la documentation *gettext* au format texinfo, chapitre 9): |
39 | 27 | Fabrice Barconnière | * *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 ; |
40 | 27 | Fabrice Barconnière | * *msginit* : création d'un catalogue *.po* pour une langue donnée, à partir d'un catalogue vide *.pot* ; |
41 | 27 | Fabrice Barconnière | * *msgfmt* : compilation d'un catalogue *.po* au format binaire *.mo* ; |
42 | 27 | Fabrice Barconnière | * *msgunfmt* : décompilation d'un catalogue au format binaire *.mo* en un catalogue *.po* ; |
43 | 27 | Fabrice Barconnière | * *msgmerge* : assemblage d'un catalogue avec traductions et d'un catalogue avec références mises à jour. |
44 | 8 | Benjamin Bohard | |
45 | 8 | Benjamin Bohard | h3. Arborescence des projets |
46 | 8 | Benjamin Bohard | |
47 | 27 | Fabrice Barconnière | Le fonctionnement de *gettext* nécessite de disposer, pour chaque langue, d'un fichier portant le même nom. |
48 | 8 | Benjamin Bohard | Sur le serveur, cela est rendu possible par l'organisation en arborescence avec un répertoire par langue, variante linguistique. |
49 | 8 | Benjamin Bohard | |
50 | 8 | Benjamin Bohard | Dans le dépôt, on propose de distinguer les différentes langues en utilisant également une arborescence : |
51 | 8 | Benjamin Bohard | <pre> |
52 | 8 | Benjamin Bohard | | |
53 | 20 | Benjamin Bohard | `-- translation |
54 | 8 | Benjamin Bohard | |-- creole.pot |
55 | 8 | Benjamin Bohard | |-- en |
56 | 8 | Benjamin Bohard | | `-- creole.po |
57 | 8 | Benjamin Bohard | `-- fr |
58 | 8 | Benjamin Bohard | `-- creole.po |
59 | 8 | Benjamin Bohard | </pre> |
60 | 8 | Benjamin Bohard | Le niveau de répertoire LC_MESSAGES présent sur les serveurs n'est pas reproduit dans les dépôts. |
61 | 8 | Benjamin Bohard | |
62 | 27 | Fabrice Barconnière | Au moment de la compilation, on recherchera tous les fichiers correspondant au motif *<notextile>translation/*/*.po</notextile>*. |
63 | 8 | Benjamin Bohard | |
64 | 28 | Fabrice Barconnière | h3. Mise à jour des _.pot_ et des _.po_ |
65 | 8 | Benjamin Bohard | |
66 | 28 | Fabrice Barconnière | La mise à jour des fichiers *.pot* et *.po* est une opération lancée si besoin au moyen du script *po_update.sh*. |
67 | 1 | Benjamin Bohard | |
68 | 28 | Fabrice Barconnière | Ce script commence par créer (en écrasant si nécessaire) une version à jour du modèle *.pot*. |
69 | 14 | Benjamin Bohard | Il met ensuite à jour les fichiers de traduction existant déjà dans l'arborescence. |
70 | 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. |
71 | 14 | Benjamin Bohard | Il lui suffit pour cela de recopier les chaînes à traduire dans les chaînes traduites. |
72 | 14 | Benjamin Bohard | Attention donc à bien utiliser la langue anglaise dans le corps du code pour les chaînes de caractères à traduire. |
73 | 14 | Benjamin Bohard | |
74 | 28 | Fabrice Barconnière | 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. |
75 | 28 | Fabrice Barconnière | 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é). |
76 | 28 | Fabrice Barconnière | Ces informations seront fournies sous la forme du fichier texte *po_list* structuré comme suit : |
77 | 8 | Benjamin Bohard | <pre> |
78 | 21 | Benjamin Bohard | projet fichier1.py fichiers2*.py |
79 | 1 | Benjamin Bohard | </pre> |
80 | 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). |
81 | 11 | Benjamin Bohard | |
82 | 28 | Fabrice Barconnière | *Attention :* le langage défini, en dur, dans le script générant le *.pot* est *python* |
83 | 8 | Benjamin Bohard | |
84 | 28 | Fabrice Barconnière | h3. Compilation des _.mo_ |
85 | 8 | Benjamin Bohard | |
86 | 28 | Fabrice Barconnière | La compilation des *.mo* sera faite à la compilation des paquets : le *.mo* ne sera pas présent dans le dépôt. |
87 | 28 | Fabrice Barconnière | Les cibles ajoutées dans *eole.mk* seront tous les fichiers *.po* disponibles. |
88 | 28 | Fabrice Barconnière | La recette compilera les *.po* en *.mo* directement dans *$(DESTDIR)/usr/share/locale/…* |
89 | 14 | Benjamin Bohard | |
90 | 14 | Benjamin Bohard | h2. Préparation des programmes à traduire |
91 | 14 | Benjamin Bohard | |
92 | 14 | Benjamin Bohard | h3. Bibliothèques python |
93 | 14 | Benjamin Bohard | |
94 | 28 | Fabrice Barconnière | Les bibliothèques python peuvent utiliser *gettext* pour afficher les chaînes de caractères dans une langue spécifique. |
95 | 28 | Fabrice Barconnière | Il faut utiliser les fonctions du module *gettext* auxquelles on passe toutes les chaînes de caractères à traduire. |
96 | 14 | Benjamin Bohard | Ces fonctions retournent les chaînes de caractères traduites. |
97 | 14 | Benjamin Bohard | |
98 | 28 | Fabrice Barconnière | h4. modèle de fichier _i18n.py_ |
99 | 23 | Benjamin Bohard | |
100 | 28 | Fabrice Barconnière | h5. _i18n.py_ à l'échelle du module |
101 | 14 | Benjamin Bohard | |
102 | 14 | Benjamin Bohard | <pre> |
103 | 14 | Benjamin Bohard | import gettext |
104 | 14 | Benjamin Bohard | import os |
105 | 14 | Benjamin Bohard | import sys |
106 | 14 | Benjamin Bohard | |
107 | 14 | Benjamin Bohard | # Application Name |
108 | 14 | Benjamin Bohard | APP_NAME = 'creole' |
109 | 14 | Benjamin Bohard | |
110 | 14 | Benjamin Bohard | # Traduction dir |
111 | 14 | Benjamin Bohard | APP_DIR = os.path.join(sys.prefix, 'share') |
112 | 14 | Benjamin Bohard | LOCALE_DIR = os.path.join(APP_DIR, 'locale') |
113 | 14 | Benjamin Bohard | |
114 | 14 | Benjamin Bohard | mo_location = LOCALE_DIR |
115 | 14 | Benjamin Bohard | |
116 | 14 | Benjamin Bohard | t = gettext.translation(APP_NAME, fallback=True) |
117 | 14 | Benjamin Bohard | |
118 | 14 | Benjamin Bohard | _ = t.ugettext |
119 | 14 | Benjamin Bohard | </pre> |
120 | 14 | Benjamin Bohard | |
121 | 28 | Fabrice Barconnière | La variable *LOCALE_DIR* pointe vers */usr/share/locale*. |
122 | 14 | Benjamin Bohard | |
123 | 28 | Fabrice Barconnière | La variable *APP_NAME* définit le nom du domaine, soit le nom permettant d'associer le bon fichier *.mo* à la bibliothèque. |
124 | 14 | Benjamin Bohard | |
125 | 28 | Fabrice Barconnière | _ est utilisé comme "alias" pour la fonction *gettext.translation(APP_NAME, "UTF-8").ugettext*. |
126 | 14 | Benjamin Bohard | _() sera appelée dans le code à chaque fois qu'une chaîne de caractère devra être traduite. |
127 | 14 | Benjamin Bohard | |
128 | 14 | Benjamin Bohard | L'utilisation de *ugettext* plutôt que *gettext* fait que la chaîne retournée sera de l' *unicode*. |
129 | 14 | Benjamin Bohard | |
130 | 28 | Fabrice Barconnière | Dans le cas d'un fichier *i18n* dans chaque projet : |
131 | 24 | Benjamin Bohard | <pre> |
132 | 24 | Benjamin Bohard | from i18n import _ |
133 | 24 | Benjamin Bohard | </pre> |
134 | 22 | Benjamin Bohard | |
135 | 22 | Benjamin Bohard | h5. i18n à l'échelle du projet EOLE |
136 | 22 | Benjamin Bohard | |
137 | 28 | Fabrice Barconnière | Pour limiter le nombre de fichiers *i18n.py*, il est possible d'utiliser une fonction générique apportée par *pyeole*. |
138 | 22 | Benjamin Bohard | <pre> |
139 | 22 | Benjamin Bohard | # -*- coding: UTF-8 -*- |
140 | 22 | Benjamin Bohard | # Copyright (C) 2014 Équipe EOLE. |
141 | 22 | Benjamin Bohard | # |
142 | 22 | Benjamin Bohard | "internationalisation utility" |
143 | 22 | Benjamin Bohard | import gettext |
144 | 22 | Benjamin Bohard | |
145 | 22 | Benjamin Bohard | system_locale_dir = "/usr/share/locale" |
146 | 22 | Benjamin Bohard | |
147 | 22 | Benjamin Bohard | class i18n(object): |
148 | 22 | Benjamin Bohard | """callable version of gettext |
149 | 22 | Benjamin Bohard | """ |
150 | 22 | Benjamin Bohard | def __init__(self, app_name): |
151 | 22 | Benjamin Bohard | self.t = gettext.translation(app_name, localedir=system_locale_dir, fallback=True) |
152 | 22 | Benjamin Bohard | |
153 | 22 | Benjamin Bohard | def __call__(self, *args): |
154 | 22 | Benjamin Bohard | # "polymorphism" rather than duck-typing to ease xgettext extraction work (keywordspec format) |
155 | 22 | Benjamin Bohard | if len(args) == 1: |
156 | 22 | Benjamin Bohard | return self.t.ugettext(*args) |
157 | 22 | Benjamin Bohard | else: |
158 | 22 | Benjamin Bohard | return self.t.ungettext(*args) |
159 | 22 | Benjamin Bohard | </pre> |
160 | 22 | Benjamin Bohard | |
161 | 32 | Fabrice Barconnière | 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. |
162 | 22 | Benjamin Bohard | <pre> |
163 | 22 | Benjamin Bohard | from pyeole.i18n import i18n |
164 | 22 | Benjamin Bohard | _ = i18n('lib') |
165 | 22 | Benjamin Bohard | </pre> |
166 | 22 | Benjamin Bohard | |
167 | 28 | Fabrice Barconnière | 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. |
168 | 1 | Benjamin Bohard | |
169 | 1 | Benjamin Bohard | h4. exemple d'utilisation |
170 | 1 | Benjamin Bohard | |
171 | 15 | Benjamin Bohard | h5. traduction d'une chaîne de caractères |
172 | 15 | Benjamin Bohard | |
173 | 15 | Benjamin Bohard | <pre> |
174 | 26 | Fabrice Barconnière | # -*- coding: UTF-8 -*- |
175 | 26 | Fabrice Barconnière | """ |
176 | 26 | Fabrice Barconnière | Translation test file 1 |
177 | 26 | Fabrice Barconnière | """ |
178 | 26 | Fabrice Barconnière | |
179 | 1 | Benjamin Bohard | from pyeole.i18n import i18n |
180 | 1 | Benjamin Bohard | _ = i18n('project_name') |
181 | 1 | Benjamin Bohard | |
182 | 1 | Benjamin Bohard | |
183 | 1 | Benjamin Bohard | # chaîne non traduite |
184 | 1 | Benjamin Bohard | print u"some words" |
185 | 1 | Benjamin Bohard | |
186 | 1 | Benjamin Bohard | #chaîne qui sera traduite |
187 | 1 | Benjamin Bohard | #en fonction des paramètres linguistiques du contexte d'exécution de la bibliothèque |
188 | 1 | Benjamin Bohard | #et de la disponibilité de la traduction dans /usr/share/locale |
189 | 1 | Benjamin Bohard | print _(u"some words") |
190 | 1 | Benjamin Bohard | |
191 | 26 | Fabrice Barconnière | #chaîne incluant des variations entre pluriel et singulier |
192 | 26 | Fabrice Barconnière | #appel valide uniquement avec la classe i18n (voir i18n à l'échelle du projet EOLE) |
193 | 26 | Fabrice Barconnière | num_phrase = 1|2 |
194 | 26 | Fabrice Barconnière | print _(u"{0} phrase traduite", u"{0} phrases traduites", num_phrase).format(num_phrase) |
195 | 15 | Benjamin Bohard | </pre> |
196 | 14 | Benjamin Bohard | |
197 | 33 | Fabrice Barconnière | *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*. |
198 | 14 | Benjamin Bohard | Il peut être nécessaire de l'encoder. |
199 | 14 | Benjamin Bohard | |
200 | 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. |
201 | 14 | Benjamin Bohard | |
202 | 28 | Fabrice Barconnière | h2. Étapes de mise en oeuvre |
203 | 24 | Benjamin Bohard | |
204 | 27 | Fabrice Barconnière | Le projet *eole-skeletor* intègre les fichiers nécessaires à la mise en oeuvre de la traduction. |
205 | 27 | Fabrice Barconnière | Pour cela, dans votre projet, il faudra reprendre ces fichiers de la façon suivante : |
206 | 28 | Fabrice Barconnière | * Vérifier que *eole.mk* soit à jour pour prendre en compte la compilation des fichiers de traduction ; |
207 | 29 | Fabrice Barconnière | * ajouter *po_update.sh* à la racine du projet ; |
208 | 34 | Fabrice Barconnière | * créer un fichier "*po_list*":https://dev-eole.ac-dijon.fr/projects/modules-eole/wiki/Gestion_traduction#Mise-%C3%A0-jour-des-pot-et-des-po à la racine du projet ; |
209 | 28 | Fabrice Barconnière | * utiliser la classe *i18n* de *pyeole* pour indiquer ce qu'il faut traduire, exemple : *_ = i18n('project_name')* ; |
210 | 28 | Fabrice Barconnière | * les messages en anglais à traduire apparaissent ainsi dans le code python : *_(u"English message")* ; |
211 | 30 | Fabrice Barconnière | * exécuter *po_update.sh* pour créer ou actualiser l'arborescence de traduction des projets ; |
212 | 35 | Fabrice Barconnière | * effectuer la traduction en français dans les fichier *.po* du répertoire *translation/fr/* : |
213 | 35 | Fabrice Barconnière | ** attention aux lignes contenant le mot clé *fuzzy*, il s'agit de proposition de traduction qu'il faut éventuellement corriger ; |
214 | 35 | Fabrice Barconnière | ** une fois la traduction corrigée, supprimer le mot clé *fuzzy* (avec sa virgule) ; |
215 | 29 | Fabrice Barconnière | * ajouter le répertoire de traduction au projet ; |
216 | 29 | Fabrice Barconnière | * _*commiter*_ les changements ; |