Schplurtzeries
Le wiki de schplurtz
Dokuwiki

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

envrac:bash-international [2012/02/23 23:14]
ztrulphcs [Il ne peut y en avoir qu'un]
envrac:bash-international [2018/01/30 06:09] (Version actuelle)
ztrulphcs [Le principe] bon lien pour strace(1)
Ligne 1: Ligne 1:
 ====== Script bash internationalisé ====== ====== Script bash internationalisé ======
 +
 +Ou comment traduire un script bash.
 +------------------
  
 Bash possède un mécanisme interne d'internationalisation. C'est pas bien difficile à trouver. Par exemple y'a [[http://www.pclinuxos.com/forum/index.php?topic=59145.0|ce how-to]], mais il est en anglais, et puis après le forum part en quenouille sur une sombre histoire de locale par défaut... Bash possède un mécanisme interne d'internationalisation. C'est pas bien difficile à trouver. Par exemple y'a [[http://www.pclinuxos.com/forum/index.php?topic=59145.0|ce how-to]], mais il est en anglais, et puis après le forum part en quenouille sur une sombre histoire de locale par défaut...
Ligne 5: Ligne 8:
 En fait, la base est simple; Les chaines entre ''%%$"%%'' et ''%%"%%'' seront candidates à la traduction. On peut donc écrire des choses comme ça : En fait, la base est simple; Les chaines entre ''%%$"%%'' et ''%%"%%'' seront candidates à la traduction. On peut donc écrire des choses comme ça :
  
-  echo $"Hello World !"+<code bash essai-001> 
 +echo $"Hello World !"
 +</code>;
  
 Et si on a fait tout ce qui est nécessaire pour la traduction, on aura : Et si on a fait tout ce qui est nécessaire pour la traduction, on aura :
Ligne 39: Ligne 44:
  
  
-Notez au passage que bash se donne vraiment beaucoup de mal pour trouver le catalogue de chaines traduites. Si on fait exprès de le taquiner un peu et qu'on regarde avec ''[[http://linuxmanpages.com/man1/strace.1.php|strace]]'' ce qu'il essaie de faire, on peut voir ceci :+Notez au passage que bash se donne vraiment beaucoup de mal pour trouver le catalogue de chaines traduites. Si on fait exprès de le taquiner un peu et qu'on regarde avec ''[[http://man7.org/linux/man-pages/man1/strace.1.html|strace]]'' ce qu'il essaie de faire, on peut voir ceci :
 <code txt> <code txt>
 open(".../locale/fr_FR.UTF-8/LC_MESSAGES/multilingue.mo", O_RDONLY) = -1 ENOENT (No such file or directory) open(".../locale/fr_FR.UTF-8/LC_MESSAGES/multilingue.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
Ligne 74: Ligne 79:
 Demandons à bash de nous donner les chaines à traduire qui sont dans notre script, et plaçons les dans un fichier ''.po'' que nous traduirons, puis transformerons en fichier ''.mo'' Demandons à bash de nous donner les chaines à traduire qui sont dans notre script, et plaçons les dans un fichier ''.po'' que nous traduirons, puis transformerons en fichier ''.mo''
  
-  langue=fr_FR +<code bash> 
-  mkdir -p locale/$langue/LC_MESSAGES +langue=fr_FR 
-  bash --dump-po-strings multilingue >multilingue.po +mkdir -p locale/$langue/LC_MESSAGES 
-  editor multilingue.po +bash --dump-po-strings multilingue >multilingue.po 
-  msgfmt -o locale/$langue/LC_MESSAGES/multilingue.mo multilingue.po+editor multilingue.po 
 +msgfmt -o locale/$langue/LC_MESSAGES/multilingue.mo multilingue.po 
 +</code>
  
  
-traduire le fichier ''.po'' est très simple, il est composé de paire de lignes ''msgid'', ''msgstr''. ''msgid'' contenant le texte d'origine auquel il faut faire correspondre une traduction dans une ligne ''msgstr''. Voilà pour notre exemple, les textes qu'on pourrait utiliser.+traduire le fichier ''.po'' est très simple, il est composé de paires de lignes ''msgid'', ''msgstr''. ''msgid'' contenant le texte d'origine auquel il faut faire correspondre une traduction dans une ligne ''msgstr''. Voilà pour notre exemple, les textes qu'on pourrait utiliser.
  
 <code po multilingue.po> <code po multilingue.po>
Ligne 109: Ligne 116:
 ==== Traduire plus sérieusement ==== ==== Traduire plus sérieusement ====
  
-Ben pourquoi c'était pas sérieux ? Disons que c'était maladroit. D'abord, un fichier ''.po'' peut contenir plus d'informations que les seules chaines traduites, le nom et l'adresse de courriel du traducteur pour ne citer qu'eux. Ensuite, remarquons que notre procédure n'est pas adaptée aux changements qui peuvent intervenir dans le script. Si on rajoute une chaine dans le script, ou si on en change une, il faudrait tout retraduire puisque la méthode ''bash --dump... >toto.po'' va écrabouiller le précédent travail de traduction. Voilà, ces réponses suffisent ? pas tout a fait ? Allez, cerise sur la gâteau, si les modifications dans le script sont suffisamment légères, alors on peut conserver automatiquement l'ancienne traduction dans le fichier ''.po'' --celui qu'on édite pour y inclure les traductions-- tout en ayant un commentaire qui indique qu'il y a eu un certain léger changement. Ça allège le travail de traduction. +Ben pourquoic'était pas sérieux ? Disons que c'était maladroit. D'abord, un fichier ''.po'' peut contenir plus d'informations que les seules chaines traduites, le nom et l'adresse de courriel du traducteur pour ne citer qu'eux. Ensuite, remarquons que notre procédure n'est pas adaptée aux changements qui peuvent intervenir dans le script. Si on rajoute une chaine dans le script, ou si on en change une, il faudrait tout retraduire puisque la méthode ''%%bash --dump... >toto.po%%'' va écrabouiller le précédent travail de traduction. Notons encore que si une chaine apparait plusieurs fois dans le script, elle se trouvera aussi plusieurs fois dans le fichier à traduire, et ce sera alors à vous d'éliminer les doublons de ce fichier, sinon la génération du catalogue échouera. Voilà, ces réponses suffisent ? pas tout a fait ? Allez, cerise sur la gâteau, si les modifications dans le script sont suffisamment légères, alors on peut conserver automatiquement l'ancienne traduction dans le fichier ''.po'' --celui qu'on édite pour y inclure les traductions-- tout en ayant un commentaire qui indique qu'il y a eu un léger changement. Ça allège le travail de traduction. 
  
 La meilleure méthode est donc  La meilleure méthode est donc 
Ligne 122: Ligne 129:
 Voilà les commandes : Voilà les commandes :
  
-  langue=fr_FR +<code bash> 
-  mkdir -p locale/$langue/LC_MESSAGES +langue=fr_FR 
-  bash --dump-po-strings multilingue | xgettext -L PO -o multilingue.pot - +mkdir -p locale/$langue/LC_MESSAGES 
-  msginit -l  $langue -i multilingue.pot -o locale/$langue/LC_MESSAGES/multilingue.po +bash --dump-po-strings multilingue | xgettext -L PO -o multilingue.pot - 
-  poedit locale/$langue/LC_MESSAGES/multilingue.po +msginit -l  $langue -i multilingue.pot -o locale/$langue/LC_MESSAGES/multilingue.po 
-  # ca crée le fichier locale/$langue/LC_MESSAGES/multilingue.mo  +poedit locale/$langue/LC_MESSAGES/multilingue.po 
 +# ca crée le fichier locale/$langue/LC_MESSAGES/multilingue.mo   
 +</code>
      
 Après modification du script il faudra passer les commandes suivantes : Après modification du script il faudra passer les commandes suivantes :
  
-  langue=fr_FR +<code bash> 
-  bash --dump-po-strings multilingue | xgettext -L PO -o multilingue.pot - +langue=fr_FR 
-  msgmerge -U locale/$langue/LC_MESSAGES/multilingue.po multilingue.pot +bash --dump-po-strings multilingue | xgettext -L PO -o multilingue.pot - 
-  poedit locale/$langue/LC_MESSAGES/multilingue.po+msgmerge -U locale/$langue/LC_MESSAGES/multilingue.po multilingue.pot 
 +poedit locale/$langue/LC_MESSAGES/multilingue.po 
 +</code>
  
 ===== Il ne peut y en avoir qu'un ===== ===== Il ne peut y en avoir qu'un =====
Ligne 142: Ligne 153:
 Non, un quoi j'ai dit. Non, un quoi j'ai dit.
  
-Ok, voilà un exemple qui fonctionne, on l'a déjà vu. il y a une variable dans la chaine à traduire, tout est nickel.+Ok, voilà un exemple. La chaine contient des variables mais ne cela n'a pas d'importanceet ça fonctionne on l'a déjà vu.
  
-  firstname=toto +<code bash essai-002> 
-  secondname='le heros' +firstname=toto 
-  echo $"Hello $firstname $secondname !"+secondname='le heros' 
 +echo $"Hello $firstname $secondname !"
 +</code>;
  
-Maintenant, un peu plus compliqué, on a plein de chaine de résultats dans un tableau, et on veut afficher le statut.+Maintenant, un peu plus compliqué, on a plein de chaines de résultats dans un tableau, et on veut afficher le statut en fonction d'une autre valeur.
  
-  name=toto +<code bash essai-003> 
-  status=( +status=( 
-    $"Perfect", +  $"Perfect", 
-    $"almost correct", +  $"almost correct", 
-    $"could be better", +  $"could be better", 
-    $"all wrong" +  $"all wrong" 
-  +
-  result=3 +name=toto 
-  echo $"$name ${status[$result]}" +result=3 
-  echo  &quot;$name ${status[$result]}&quot;+echo  "$name ${status[$result]}" 
 +&lt;/code&gt;
  
 Jusque là, on n'a qu'un niveau de variable dans la chaine à afficher. ''$name'' et ''${status[$result]}'' sont au même niveau, donc tout va bien. Jusque là, on n'a qu'un niveau de variable dans la chaine à afficher. ''$name'' et ''${status[$result]}'' sont au même niveau, donc tout va bien.
  
-Imaginons maintenant qu'on veuille faire rentrer ''$name'' dans les chaine du tableau, ce qui permettra aux traducteur de choisir la place que doit occuper le nom dans la phrase. Peut être au début dans une langue, et à la fin dans une autre langue. Emporté par notre élan, on écrit le script naïf suivant :+Imaginons maintenant qu'on veuille faire rentrer ''$name'' dans les chaines du tableau, ce qui permettra aux traducteurs de choisir la place que doit occuper le nom dans la phrase. Peut être au début dans une langue (''%%"$name blablabla"%%''), et à la fin dans une autre langue (''%%"gnak gnak gnik $name"%%''). Emporté par notre élan, on écrit le script naïf suivant :
  
-  status=( +<code bash essai-004> 
-    $"$name Perfect", +status=( 
-    $"$name almost correct", +  $"$name Perfect", 
-    $"$name could be better", +  $"$name almost correct", 
-    $"$name all wrong" +  $"$name could be better", 
-  +  $"$name all wrong" 
-  name=toto +
-  result=3 +name=toto 
-  echo "${status[$result]}"+result=3 
 +echo "${status[$result]}"
 +</code>;
  
-Et là, cata ! Rien ne va plus. Ben oui quoi,+Et là, cata ! Rien ne va plus. ''%%$name%%'' semble complètement ignorédisparu, effacé...
  
-bash voit une chaine ''"${status[$result]}"'', procède à l'expansion des variables, effectue le remplacement par la valeur qui est une traduction déjà réalisée et s'arrête là, content de lui. ''$name'' dans l'histoire ? il n'en est pas question au moment où on le souhaiterai. En fait, lors de la **définition** du tableau, bash a déjà fait tout le boulotrechercher un équivallent dans le catalogue, il trouve des chaines du genre ''%%"parfait $name", "presque bien $name", "$name : tu peux faire mieux"%%'' et ''%%"tout faux $name"%%'', après avoir trouvé la correspindance, il fait le remplacement de variable '%%$name%%'' => ''%%''%%'' car *à ce moment* ''%%$name%%'' n'est pas encore définie...+Ben oui, mais normal quoi. Si on prend pas à pas ce qui se passe lors de l'affichage, voilà ce qu'on obtient : 
 + 
 +  * bash voit une chaine ''%%"${status[$result]}"%%'' qui n'est pas une chaine à traduire, 
 +  * procède à l'expansion normale des variables et remplace ''%%"${status[$result]}"%%'' par la valeur idoine du tableau ''status''; cette valeur est une traduction **déjà** réalisée
 +  * et il s'arrête là, content de lui. 
 + 
 +''$name'' dans l'histoire ? il n'en est pas question **au moment où on le souhaiterai**. En fait, ''%%$name%%'' a été évalué bien avant, lors de la **définition** du tableau:  
 + 
 +  * bash tombe sur la définition du tableau, 
 +  * trouve des chaines à traduire 
 +  * recherche un équivalent dans le catalogue, 
 +  * il trouve des chaines du genre ''%%"parfait $name", "presque bien $name", "$name : tu peux faire mieux""tout faux $name"%%'', 
 +  * remplace les chaines d'origine par leur traduction, 
 +  * puis, au moment où il exécute la commande, c'est à dire l'affectation au tableau,\\ fait le remplacement de variable ''%%$name%%'' => ''%%''%%'' car **à ce moment** ''%%$name%%'' n'est pas encore définie..
 +  * range ce résultat dans la tableau et passe à la suite.
  
 Si on essaie frénétiquement ceci : Si on essaie frénétiquement ceci :
  
-  echo $"${status[$result]}"+<code bash> 
 +echo $"${status[$result]}"
 +</code>;
  
-on a le même résultat, mais pour plus chèr : bash voit une chaine à traduire : ''%%$"${status[$result]}"%%'' il va chercher dans le catalogue, il trouve .... la même chose : ''%%$"${status[$result]}"%%''((sauf si vous avez pris des libertés de traduction)). Il passe ensuite à l'expansion des variables, remplace ''${status[$result]}'' par sa valeur qui se trouve être une traduction et s'arrête là, content de son travail. Le reste est identique au cas précédent.+on a le même résultat, mais pour plus cher : bash voit une chaine à traduire : ''%%$"${status[$result]}"%%'' il va chercher dans le catalogue, il trouve .... la même chose : ''%%$"${status[$result]}"%%''((sauf si vous avez pris des libertés de traduction)). Il passe ensuite à l'expansion des variables, remplace ''${status[$result]}'' par sa valeur qui se trouve être une traduction **déjà réalisée** et s'arrête là, content de son travail. Le reste est identique au cas précédent.
  
-Il n'y a pas de solution alors ? ben si. Y'a [[http://linuxmanpages.com/man1/printf.1.php|printf]]. il se trouveque ''printf'' est une commande [[http://linuxmanpages.com/man1/bash.1.php#lbCZ|interne de bash]] et donc peu coûteuse. Un exemple correct pour ce script est :+Si on essaie alors ceci : 
 +<code bash essai-005> 
 +status=( 
 +  "$name Perfect", 
 +  "$name almost correct", 
 +  "$name could be better", 
 +  "$name all wrong" 
 +
 +name=toto 
 +result=3 
 +echo $"${status[$result]}" 
 +</code>
  
-  status=( +C'est pas mieux. Voir l'explication ci dessus, mais en plus, les chaines du tableau sont même pas traduites... 
-    $"%s Perfect", + 
-    $"%s almost correct", +Il n'y a pas de solution alors ? ben si. Y'a [[http://linuxmanpages.com/man1/printf.1.php|printf]]. Au passage, il se trouve, que ''printf'' est une commande [[https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtin-Commands|interne de bash]] (et aussi de dash et ash) et donc peu coûteuse -- même si le lien donné pointe vers la page de manuel de la commande externe du même nom. Un exemple correct pour ce script est : 
-    $"%s could be better", + 
-    $"%s all wrong" +<code bash essai-006> 
-  +#! /bin/bash 
-  name=toto + 
-  result=3 +TEXTDOMAIN=${0##*/} 
-  printf "${status[$result]}" "$name" +TEXTDOMAINDIR="$PWD/locale" 
-  dialog --msgbox "$( printf "${status[$result]}" "$name" )" 0 0 + 
-  zenity --info --text="$( printf "${status[$result]}" "$name" )"+status=( 
 + $"%s Perfect\\n", 
 + $"%s almost correct\\n", 
 + $"%s could be better\\n", 
 + $"%s all wrong\\n
 +
 +name=toto 
 +result=$(( $RANDOM % 4 )) 
 +printf "${status[$result]}" "$name" 
 +read -p $"press the return key" 
 +# ou alors, si on fait des boites de dialogue, on peut utiliser ceci : 
 +result=$(( $RANDOM % 4 )) 
 +dialog --msgbox "$( printf "${status[$result]}" "$name" )" 0 0 
 +result=$(( $RANDOM % 4 )) 
 +zenity --info --text="$( printf "${status[$result]}" "$name" )"
 +</code>;
  
 Les traducteurs pourront mettre les noms aux endroits où ils le veulent. Sauf qu'ils peuvent éventuellement ne même pas vraiment savoir à quoi correspond le ''%s'' dans la chaine à traduire. Les traducteurs pourront mettre les noms aux endroits où ils le veulent. Sauf qu'ils peuvent éventuellement ne même pas vraiment savoir à quoi correspond le ''%s'' dans la chaine à traduire.
  
 +Notez l'emploi de ''TEXTDOMAIN=${0##*/}''. ''${0##*/}'' est **toujours** égal au nom du script, quelque soit ce nom.
 +
 +Voilà le fichier .po pour ceux qui voudraient essayer :
 +<code po essai-006.po>
 +#: essai-007:7
 +msgid "%s Perfect\\\\n"
 +msgstr "%s, C'est parfait !\\\\n"
 +#: essai-006:8
 +msgid "%s almost correct\\\\n"
 +msgstr "Presque correcte %s\\\\n"
 +#: essai-006:9
 +msgid "%s could be better\\\\n"
 +msgstr "%s : Peut mieux faire\\\\n"
 +#: essai-006:10
 +msgid "%s all wrong\\\\n"
 +msgstr "%s a tout faux !\\\\n"
 +#: essai-006:15
 +msgid "press the return key"
 +msgstr "Appuyez sur la touche « entrée »"
 +</code>
 +
 +et voilà aussi les commandes à passer :
 +<code bash>
 +langue=fr_FR
 +mkdir -p locale/$langue/LC_MESSAGES/
 +msgfmt -o locale/$langue/LC_MESSAGES/essai-006.mo essai-006.po
 +bash essai-006
 +</code>
  
 ===== Téléchargement bonus ===== ===== Téléchargement bonus =====
 +
 +un script d'exemple et des Makefile pour traduire...
 +
 +{{:envrac:multilingue.tar.bz2|}}