DocumentationCodeBonnesPratiques » Historique » Version 33
Benjamin Bohard, 25/04/2014 12:04
| 1 | 20 | Daniel Dehennin | {{toc}} |
|---|---|---|---|
| 2 | 20 | Daniel Dehennin | |
| 3 | 1 | Emmanuel GARETTE | h1. DocumentationCodeBonnesPratiques |
| 4 | 1 | Emmanuel GARETTE | |
| 5 | 18 | Gwenael Remond | **cette page concerne la documentation développeur** |
| 6 | 18 | Gwenael Remond | (voir aussi [[TestsCodeBonnesPratiques]] pour les autres bonnes pratiques développeur) |
| 7 | 18 | Gwenael Remond | |
| 8 | 1 | Emmanuel GARETTE | h2. Généralité |
| 9 | 1 | Emmanuel GARETTE | |
| 10 | 1 | Emmanuel GARETTE | * La documentation technique doit être en anglais. |
| 11 | 5 | Gérald Schwartzmann | * Elle est placée dans les docstrings du code. |
| 12 | 1 | Emmanuel GARETTE | |
| 13 | 1 | Emmanuel GARETTE | h2. Description du module |
| 14 | 1 | Emmanuel GARETTE | |
| 15 | 12 | Daniel Dehennin | La docstring du module est placée en haut du fichier juste après le "shebang":https://fr.wikipedia.org/wiki/Shebang, la déclaration d’"encodage des caractères":https://fr.wikipedia.org/wiki/Codage_de_caract%C3%A8res et la licence du code. |
| 16 | 2 | Gwenael Remond | |
| 17 | 1 | Emmanuel GARETTE | <pre> |
| 18 | 12 | Daniel Dehennin | #!/usr/bin/python |
| 19 | 12 | Daniel Dehennin | # -*- coding: utf-8 -*- |
| 20 | 12 | Daniel Dehennin | # |
| 21 | 16 | Emmanuel GARETTE | ########################################################################## |
| 22 | 16 | Emmanuel GARETTE | # <nom du projet> |
| 23 | 19 | Daniel Dehennin | # Copyright © 2013 Pôle de compétences EOLE <eole@ac-dijon.fr> |
| 24 | 14 | Daniel Dehennin | # |
| 25 | 16 | Emmanuel GARETTE | # License CeCILL: |
| 26 | 16 | Emmanuel GARETTE | # * in french: http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html |
| 27 | 16 | Emmanuel GARETTE | # * in english http://www.cecill.info/licences/Licence_CeCILL_V2-en.html |
| 28 | 16 | Emmanuel GARETTE | ########################################################################## |
| 29 | 2 | Gwenael Remond | </pre> |
| 30 | 2 | Gwenael Remond | |
| 31 | 11 | Daniel Dehennin | La docstring doit contenir : |
| 32 | 1 | Emmanuel GARETTE | * description rapide en une phrase du module ; |
| 33 | 1 | Emmanuel GARETTE | * description avancée qui explique le rôle du module ; |
| 34 | 1 | Emmanuel GARETTE | * des exemples simples d'utilisation. |
| 35 | 1 | Emmanuel GARETTE | |
| 36 | 1 | Emmanuel GARETTE | Les exemples peuvent être un prompt : |
| 37 | 1 | Emmanuel GARETTE | |
| 38 | 1 | Emmanuel GARETTE | <pre> |
| 39 | 1 | Emmanuel GARETTE | >>> is_locked() |
| 40 | 1 | Emmanuel GARETTE | True |
| 41 | 1 | Emmanuel GARETTE | </pre> |
| 42 | 1 | Emmanuel GARETTE | |
| 43 | 1 | Emmanuel GARETTE | ou une portion de code : |
| 44 | 1 | Emmanuel GARETTE | |
| 45 | 1 | Emmanuel GARETTE | <pre> |
| 46 | 1 | Emmanuel GARETTE | :: |
| 47 | 1 | Emmanuel GARETTE | |
| 48 | 1 | Emmanuel GARETTE | from toto.titi import is_locked |
| 49 | 1 | Emmanuel GARETTE | |
| 50 | 1 | Emmanuel GARETTE | toto() |
| 51 | 1 | Emmanuel GARETTE | is_locked() |
| 52 | 1 | Emmanuel GARETTE | </pre> |
| 53 | 1 | Emmanuel GARETTE | |
| 54 | 1 | Emmanuel GARETTE | h2. Docstring des méthodes |
| 55 | 1 | Emmanuel GARETTE | |
| 56 | 3 | Gwenael Remond | Seules les méthodes publiques (qui ne commencent pas par "_") seront mises dans la documentation (mais les méthodes privées sont aussi à documenter). |
| 57 | 1 | Emmanuel GARETTE | |
| 58 | 1 | Emmanuel GARETTE | La docstring doit contenir : |
| 59 | 1 | Emmanuel GARETTE | |
| 60 | 1 | Emmanuel GARETTE | * description rapide en une phrase de la méthode ; |
| 61 | 1 | Emmanuel GARETTE | * si nécessaire une description avancée qui explique le rôle du module ; |
| 62 | 1 | Emmanuel GARETTE | * si nécessaire des exemples simples d'utilisation. |
| 63 | 1 | Emmanuel GARETTE | * les paramètres de la façon suivante (xxx étant le nom du paramètre et yyyyyy la description) : |
| 64 | 1 | Emmanuel GARETTE | |
| 65 | 1 | Emmanuel GARETTE | <pre> |
| 66 | 1 | Emmanuel GARETTE | :param xxx: yyyyyy |
| 67 | 1 | Emmanuel GARETTE | </pre> |
| 68 | 1 | Emmanuel GARETTE | |
| 69 | 1 | Emmanuel GARETTE | Si nécessaire les valeurs de retour (yyyyyy étant la description) : |
| 70 | 1 | Emmanuel GARETTE | |
| 71 | 1 | Emmanuel GARETTE | <pre> |
| 72 | 1 | Emmanuel GARETTE | :return: yyyyyy |
| 73 | 1 | Emmanuel GARETTE | </pre> |
| 74 | 1 | Emmanuel GARETTE | |
| 75 | 4 | Gwenael Remond | plus d'info sur la syntaxe des docstrings: http://sphinx-doc.org/domains.html#info-field-lists |
| 76 | 4 | Gwenael Remond | |
| 77 | 1 | Emmanuel GARETTE | h2. Docstring des classes |
| 78 | 1 | Emmanuel GARETTE | |
| 79 | 10 | Gwenael Remond | comme pour la docstring des modules (mais sans la licence) |
| 80 | 8 | Gwenael Remond | |
| 81 | 8 | Gwenael Remond | h2. Tips |
| 82 | 8 | Gwenael Remond | |
| 83 | 8 | Gwenael Remond | il est possible de faire un lien portant directement sur le redmine de deux manières : |
| 84 | 8 | Gwenael Remond | |
| 85 | 8 | Gwenael Remond | <pre> |
| 86 | 8 | Gwenael Remond | |
| 87 | 8 | Gwenael Remond | :issue:`410` |
| 88 | 8 | Gwenael Remond | |
| 89 | 8 | Gwenael Remond | </pre> |
| 90 | 8 | Gwenael Remond | |
| 91 | 8 | Gwenael Remond | est un lien qui porte directement sur la demande 410 |
| 92 | 8 | Gwenael Remond | |
| 93 | 8 | Gwenael Remond | et |
| 94 | 8 | Gwenael Remond | |
| 95 | 8 | Gwenael Remond | <pre> |
| 96 | 8 | Gwenael Remond | |
| 97 | 8 | Gwenael Remond | :eole:`/projects/creole/wiki/Lock24` |
| 98 | 8 | Gwenael Remond | |
| 99 | 8 | Gwenael Remond | </pre> |
| 100 | 8 | Gwenael Remond | |
| 101 | 8 | Gwenael Remond | qui est simplement un raccourci vers http://dev-eole.ac-dijon.fr/projects/creole/wiki/Lock24 |
| 102 | 17 | Daniel Dehennin | |
| 103 | 17 | Daniel Dehennin | h2. Exemples |
| 104 | 17 | Daniel Dehennin | |
| 105 | 17 | Daniel Dehennin | Plusieurs fichiers sources ont reçu une attention assez particulière sur la documentation : |
| 106 | 17 | Daniel Dehennin | |
| 107 | 17 | Daniel Dehennin | - python-pyeole:source:pyeole/decorator.py |
| 108 | 17 | Daniel Dehennin | - python-pyeole:source:pyeole/log.py |
| 109 | 17 | Daniel Dehennin | - python-pyeole:source:pyeole/lock.py |
| 110 | 21 | Philippe Caseiro | |
| 111 | 21 | Philippe Caseiro | h2. Traduction et python |
| 112 | 22 | Philippe Caseiro | |
| 113 | 24 | Philippe Caseiro | |
| 114 | 24 | Philippe Caseiro | |
| 115 | 22 | Philippe Caseiro | Voici un petit aide mémoire pour traduire une application python de la manière la plus simple possible. |
| 116 | 22 | Philippe Caseiro | |
| 117 | 32 | Benjamin Bohard | Pour traduire une application il faut avant tout une bibliothèque qui gère les traductions. La première |
| 118 | 32 | Benjamin Bohard | qui vient à l'esprit est _gettext_, donc logiquement nous allons nous tourner vers _python-gettext_. |
| 119 | 22 | Philippe Caseiro | |
| 120 | 26 | Philippe Caseiro | Il faut noter qu'il est important de faire les choses dans l'ordre, les messages par défaut des applications doivent être dans *une seule et unique langue*, l'usage |
| 121 | 22 | Philippe Caseiro | est de tout écrire en anglais et ensuite de mettre une traduction pour les autres langues. |
| 122 | 22 | Philippe Caseiro | |
| 123 | 32 | Benjamin Bohard | Le principe de gettext est de remplacer tous les messages par l'appel à une "fonction/méthode/whatever" qui va |
| 124 | 22 | Philippe Caseiro | faire les opérations de traduction. |
| 125 | 22 | Philippe Caseiro | |
| 126 | 26 | Philippe Caseiro | Dans le monde _gettext_ cette "fonction" a pour nom '_'. |
| 127 | 22 | Philippe Caseiro | |
| 128 | 32 | Benjamin Bohard | La première chose à faire est de mettre à disposition une "fonction" '_' dans votre application python. (Les exemples suivants proviennent de pyeole) |
| 129 | 22 | Philippe Caseiro | |
| 130 | 22 | Philippe Caseiro | h3. i18n.py |
| 131 | 22 | Philippe Caseiro | |
| 132 | 32 | Benjamin Bohard | On crée un "module" i18n.py (mais il pourrait très bien avoir un autre nom) et on y fait les déclarations et les initialisations nécessaires pour la traduction. |
| 133 | 22 | Philippe Caseiro | |
| 134 | 26 | Philippe Caseiro | * Dans ce module on importe gettext et les modules nécessaires à la gestion de la traduction (python-gettext) |
| 135 | 22 | Philippe Caseiro | <pre> |
| 136 | 22 | Philippe Caseiro | import gettext # Gettext lui même |
| 137 | 22 | Philippe Caseiro | import os # Pour les variables d'environement LANG/LC_ALL |
| 138 | 22 | Philippe Caseiro | import sys # Pour la gestion des fichiers de traduction |
| 139 | 22 | Philippe Caseiro | import locale # Pour la gestion des "locale" |
| 140 | 22 | Philippe Caseiro | </pre> |
| 141 | 32 | Benjamin Bohard | * On définit le nom du domaine de traduction (le plus souvent le nom de l'application) |
| 142 | 22 | Philippe Caseiro | <pre> |
| 143 | 22 | Philippe Caseiro | APP_NAME = 'pyeole' |
| 144 | 22 | Philippe Caseiro | </pre> |
| 145 | 22 | Philippe Caseiro | * On récupère les répertoires qui vont contentir les traductions |
| 146 | 22 | Philippe Caseiro | <pre> |
| 147 | 22 | Philippe Caseiro | # Traduction dir |
| 148 | 22 | Philippe Caseiro | APP_DIR = os.path.join(sys.prefix, 'share') |
| 149 | 22 | Philippe Caseiro | LOCALE_DIR = os.path.join(APP_DIR, 'locale') |
| 150 | 22 | Philippe Caseiro | </pre> |
| 151 | 22 | Philippe Caseiro | * On récupère la langue par défaut et on y ajoute l'anglais pour les messages non traduits |
| 152 | 23 | Philippe Caseiro | <pre> |
| 153 | 32 | Benjamin Bohard | # Default Language |
| 154 | 22 | Philippe Caseiro | DEFAULT_LANG = os.environ.get('LANG', '').split(':') |
| 155 | 1 | Emmanuel GARETTE | DEFAULT_LANG += ['en_US'] |
| 156 | 23 | Philippe Caseiro | </pre> |
| 157 | 32 | Benjamin Bohard | * On définit la langue par défaut (je ne suis pas certain que cette partie ne soit pas inutile :) |
| 158 | 23 | Philippe Caseiro | <pre> |
| 159 | 22 | Philippe Caseiro | languages = [] |
| 160 | 22 | Philippe Caseiro | lc, encoding = locale.getdefaultlocale() |
| 161 | 22 | Philippe Caseiro | if lc: |
| 162 | 22 | Philippe Caseiro | languages = [lc] |
| 163 | 1 | Emmanuel GARETTE | |
| 164 | 1 | Emmanuel GARETTE | languages += DEFAULT_LANG |
| 165 | 23 | Philippe Caseiro | </pre> |
| 166 | 23 | Philippe Caseiro | * On fait les initialisations gettext |
| 167 | 23 | Philippe Caseiro | <pre> |
| 168 | 1 | Emmanuel GARETTE | mo_location = LOCALE_DIR |
| 169 | 1 | Emmanuel GARETTE | |
| 170 | 23 | Philippe Caseiro | gettext.find(APP_NAME, mo_location) # On cherche les traductions |
| 171 | 32 | Benjamin Bohard | gettext.textdomain(APP_NAME) # On crée le domaine de traduction (où il va chercher les messages) |
| 172 | 32 | Benjamin Bohard | gettext.bind_textdomain_codeset(APP_NAME, "UTF-8") # On définit le jeux de caractères des traductions (utf8) |
| 173 | 23 | Philippe Caseiro | </pre> |
| 174 | 32 | Benjamin Bohard | * On initialise l'objet qui va réaliser les traductions pour notre application |
| 175 | 23 | Philippe Caseiro | <pre> |
| 176 | 22 | Philippe Caseiro | t = gettext.translation(APP_NAME, fallback=True) |
| 177 | 23 | Philippe Caseiro | </pre> |
| 178 | 32 | Benjamin Bohard | * Enfin on crée la "fonction/methode/whatever" '_' : |
| 179 | 23 | Philippe Caseiro | <pre> |
| 180 | 1 | Emmanuel GARETTE | def _(msg): |
| 181 | 23 | Philippe Caseiro | return t.ugettext(msg) # on retourne le résultat de la traduction ! ugettext peut poser problème il ne prend que des chaines utf8 |
| 182 | 1 | Emmanuel GARETTE | </pre> |
| 183 | 24 | Philippe Caseiro | |
| 184 | 32 | Benjamin Bohard | Vous n'avez plus qu'à importer la "fonction/méthode" '_' dans vos fichier .py et l'utiliser |
| 185 | 24 | Philippe Caseiro | |
| 186 | 24 | Philippe Caseiro | Exemple : |
| 187 | 24 | Philippe Caseiro | <pre> |
| 188 | 24 | Philippe Caseiro | from pyeole.translation.i18n import _ # j'ai mis le module i18n.py dans un répertoire spécifique (pour le plaisir) |
| 189 | 24 | Philippe Caseiro | |
| 190 | 24 | Philippe Caseiro | .... |
| 191 | 24 | Philippe Caseiro | |
| 192 | 24 | Philippe Caseiro | msg = _(u'Install: {0}.') |
| 193 | 24 | Philippe Caseiro | log.info(msg.format(u', '.join(to_install))) |
| 194 | 24 | Philippe Caseiro | </pre> |
| 195 | 24 | Philippe Caseiro | |
| 196 | 24 | Philippe Caseiro | h3. let's translate |
| 197 | 24 | Philippe Caseiro | |
| 198 | 27 | Philippe Caseiro | Une fois le code en place il faut créer les fichiers de traduction. Il existe plusieurs utilitaires pour faire de la traduction, personnellement j'utilise "poedit" (ou vim :) |
| 199 | 24 | Philippe Caseiro | |
| 200 | 27 | Philippe Caseiro | Il faut générer le fichier de référence des messages, pour ce faire on utilise la commande _xgettext_. |
| 201 | 1 | Emmanuel GARETTE | |
| 202 | 27 | Philippe Caseiro | On place les fichiers de traductions dans un répertoire *translation* à la racine du dépôt. |
| 203 | 27 | Philippe Caseiro | |
| 204 | 1 | Emmanuel GARETTE | <pre> |
| 205 | 28 | Philippe Caseiro | dev@host:~ $ cd code/pyeole |
| 206 | 28 | Philippe Caseiro | dev@host:~/code/pyeole $ # mkdir translation |
| 207 | 28 | Philippe Caseiro | dev@host:~/code/pyeole cd translation |
| 208 | 28 | Philippe Caseiro | dev@host:~/code/pyeole/translation # xgettext --language=Python --keyword=_ --output=pyeole-next.pot --from-code=UTF-8 `find ../pyeole -name "*.py"` |
| 209 | 24 | Philippe Caseiro | </pre> |
| 210 | 24 | Philippe Caseiro | |
| 211 | 32 | Benjamin Bohard | Attention il est possible que _xgettext_ plante à cause de certaines chaines de commentaire. |
| 212 | 24 | Philippe Caseiro | |
| 213 | 32 | Benjamin Bohard | Une fois que la commande _xgettext_ se termine, elle génère un fichier ".pot" |
| 214 | 24 | Philippe Caseiro | |
| 215 | 24 | Philippe Caseiro | Pour générer les traductions on utilise poedit. |
| 216 | 24 | Philippe Caseiro | |
| 217 | 24 | Philippe Caseiro | # Lancer poedit |
| 218 | 24 | Philippe Caseiro | # Fichier --> Nouveau depuis un fichier POT/PO |
| 219 | 24 | Philippe Caseiro | # Ouvrir votre fichier .pot |
| 220 | 24 | Philippe Caseiro | # Séléctionner la langue de traduction |
| 221 | 24 | Philippe Caseiro | # Traduire les chaines disponibles |
| 222 | 24 | Philippe Caseiro | # Sauvegarder le fichier de traduction dans le répertoire translation/<CODE_LANGUE>/<mon_appli>.po (translation/fr/pyeole.po) |
| 223 | 24 | Philippe Caseiro | |
| 224 | 32 | Benjamin Bohard | h3. plus qu'à installer |
| 225 | 25 | Philippe Caseiro | |
| 226 | 32 | Benjamin Bohard | Pour que les fichiers de traductions soit pris en compte par la génération du paquet il faut faire des mises à jour dans le setup.py. |
| 227 | 25 | Philippe Caseiro | |
| 228 | 32 | Benjamin Bohard | On ajoute une petite fonction qui va créer les fichiers de traduction à partir des .po. Et oui, les .po ne sont pas les fichiers |
| 229 | 25 | Philippe Caseiro | utilisés par gettext mais juste leur "code source". Il faut impérativement que la traduction soit la plus rapide possible donc qui dit |
| 230 | 25 | Philippe Caseiro | rapide dit fichiers binaires. |
| 231 | 25 | Philippe Caseiro | |
| 232 | 32 | Benjamin Bohard | Voici le code qui génère les .mo |
| 233 | 25 | Philippe Caseiro | |
| 234 | 25 | Philippe Caseiro | <pre> |
| 235 | 25 | Philippe Caseiro | trad_root = "share/locale" |
| 236 | 25 | Philippe Caseiro | trad_dir = "LC_MESSAGES" |
| 237 | 25 | Philippe Caseiro | here = dirname(abspath(__file__)) |
| 238 | 25 | Philippe Caseiro | |
| 239 | 25 | Philippe Caseiro | def build_translation(): |
| 240 | 25 | Philippe Caseiro | po_path = join(here, "translation") |
| 241 | 25 | Philippe Caseiro | result = [] |
| 242 | 25 | Philippe Caseiro | |
| 243 | 25 | Philippe Caseiro | for top, dirs, files in walk(po_path): |
| 244 | 25 | Philippe Caseiro | for directory in dirs: |
| 245 | 25 | Philippe Caseiro | tr_dir = join(top, directory) |
| 246 | 25 | Philippe Caseiro | # Search for .po file for a traduction |
| 247 | 25 | Philippe Caseiro | trad_files = [] |
| 248 | 25 | Philippe Caseiro | for pot_file in glob(join(tr_dir, "*.po")): |
| 249 | 25 | Philippe Caseiro | mo_file = splitext(pot_file)[0] + ".mo" |
| 250 | 25 | Philippe Caseiro | lang = basename(tr_dir) |
| 251 | 25 | Philippe Caseiro | cmd = ["msgfmt", pot_file, "-o", mo_file] |
| 252 | 25 | Philippe Caseiro | dest = join(trad_root, lang, trad_dir) |
| 253 | 25 | Philippe Caseiro | trad_files.append(join("translation", lang, basename(mo_file))) |
| 254 | 25 | Philippe Caseiro | call(cmd) |
| 255 | 25 | Philippe Caseiro | result.append((dest, trad_files)) |
| 256 | 25 | Philippe Caseiro | |
| 257 | 25 | Philippe Caseiro | return result |
| 258 | 25 | Philippe Caseiro | |
| 259 | 25 | Philippe Caseiro | translations = build_translation() |
| 260 | 25 | Philippe Caseiro | </pre> |
| 261 | 25 | Philippe Caseiro | |
| 262 | 25 | Philippe Caseiro | Voici les directives supplémentaires pour la fonction setup() |
| 263 | 25 | Philippe Caseiro | |
| 264 | 25 | Philippe Caseiro | <pre> |
| 265 | 25 | Philippe Caseiro | data_files = translations |
| 266 | 29 | Philippe Caseiro | </pre> |
| 267 | 25 | Philippe Caseiro | |
| 268 | 25 | Philippe Caseiro | h2. Des questions ? |
| 269 | 25 | Philippe Caseiro | |
| 270 | 33 | Benjamin Bohard | Voilà une fois que vous avez fait toutes ces opérations votre appli doit parler d'autres langues. |
| 271 | 30 | Philippe Caseiro | |
| 272 | 33 | Benjamin Bohard | Notez que cette intégration a été faite sur un délai très court par un non-spécialiste (moi) et qu'elle ne représente absolument pas la meilleure solution |
| 273 | 33 | Benjamin Bohard | et encore moins la plus élégante. Elle a très certainement de grosses lacunes et des bugs, mais pour l'instant dans le cas présent |
| 274 | 30 | Philippe Caseiro | elle fonctionne et offre une solution rapide. |
| 275 | 24 | Philippe Caseiro | |
| 276 | 33 | Benjamin Bohard | Si vous avez des questions cette partie a été écrite par Philippe "Puppet_Master" Caseiro. |