Plone est multilingue. Cette partie explique comment rendre votre composant disponible en plusieurs langues.
Internationalisation et localisation
Nous distinguerons l’internationalisation (i18n), qui consiste à utiliser l’API de production de textes dans le code Python et les templates, de la localisation (l10n), qui consiste à fournir la traduction des labels produits lors de durant l’internationalisation.
A faire
Placer des liens sur les grandes lignes de gettext (fichiers .pot, .po, .mo)
Nous recommandons aux lecteurs de prendre préalablement connaissance des mécanismes standard d’internationalisation des applications, basé sur le standard “GNU-gettext” http://fr.wikipedia.org/wiki/Gettext
Les amateurs de détails pourront se reporter à la documentation complète en anglais ici : http://www.gnu.org/software/gettext/manual/gettext.html
Plone et plus généralement Zope (puisque nous parlons d’internationalisation Zope) sont des écosystèmes générant des centaines de composants d’extension sur l’utilité desquels nous ne reviendrons pas ici. Un intégrateur est susceptible de mixer nombre parmi ces composants.
Ceci risque donc d’amener nombre de conflits de labels, le même label pouvant être traduit différemment selon l’emploi qui en est fait.
Pour éviter ceci, le standard “gettext” définit la notion de “domaine” qui est un espace de nommage pour les traductions de labels. Ainsi le label “foo” peut être traduit différemment dans autant de domaines distincts.
Dès lors qu’un composant propose une interface utilisateurs comportant du texte, il doit déclarer son domaine et le répertoire racine des traductions (traditionnellement, le répertoire locales/ de votre egg). Vous allez donc ajouter au fichier configure.zcml principal de votre composant les ligne suivantes :
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="AgileKnowledgeBase">
...
<i18n:registerTranslations directory="locales" />
...
</configure>
Vous placez ensuite dans le __init__.py principal de votre composant les lignes suivantes :
from zope.i18nmessageid import MessageFactory
MyMessageFactory = MessageFactory('AgileKnowledgeBase')
Écrit en C
Afin d’optimiser les performances, les primitives des MessageFactory sont écrites en C.
Lorsque vous voulez placer un texte susceptible d’être traduit dans votre code, vous devez procéder ainsi.
from Products.AgileKnowledgeBase import MyMessageFactory as _
# ...
def foo(bar):
message = _(u'my_message', default=u"Some words")
# ...
Quelques explications :
Messages avec variables
Il est parfois nécessaire d’introduire des parties variables dans les messages à traduire. Par exemple : “Ce dossier contient 5 image(s)”. Utilisez pour ceci le paramètre nommé mapping ayant pour valeur un dictionnaire (ou objet quelconque ayant le même protocole) fournissant toutes les clés prévues par les emplacements réservés aux variables dans le message. Ces emplacements variables sont marqués dans le message sous la forme ${clé}.
def bar(value):
# ...
msg_map = {'count': value}
message = _(u'images_in_folder',
default=u"This folder has ${count} images",
mapping=msg_map)
A faire
expliquer le marquage des templates
De la même façon que le code Python, les templates ZPT peuvent être internationalisée.
Le marquage d’internationalisation est effectué en utilisant l’espace de nommage XML i18n.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
>
...
</html>
i18n:domain : Déclaration du domaine
Pour marquer le domaine de traduction d’une template (ou d’un élément quelconque figurant dans une template) la déclaration préalable de ce dernier est nécessaire dans un élément englobant.
...
<div i18n:domain="machin">
<!-- Les textes marqués pour traductions utiliseront les
messages des dictionnaires du domaine "machin"
-->
...
<table i18n:domain="truc">
<!-- Les textes marqués de ce tableau utiliseront par
contre les dictionnaires du domaine "truc"
-->
...
</table>
</div>
i18n:translate : Traduction du contenu d’un élément HTML
Le contenu de cette <div> sera traduit selon la clé label_truc du domaine de traduction courant.
...
<div i18n:translate="label_truc">Some stuff</div>
...
Le contenu de cette <div> sera traduit selon la clé Some stuff du domaine de traduction courant. En effet, lorsque l’attribut i18n:translate est vide, le contenu de la balise est utilisé comme clé de traduction.
...
<div i18n:translate="">Some stuff</div>
...
Ceci permet de traduire des textes dont le contenu ne peut être connu qu’au moment de la publication de la template.
...
<!-- On traduit le titre lorsque c'est possible -->
<span i18n:translate="" tal:content="context/title_or_id">Ze title</span>
i18n:attributes : Traduction des attributs d’un élément
Les attributs title et alt sont traduits selon la clé fournie par leurs valeurs (statique ou calculée)
...
<img title="img_title" alt=""
tal:attributes="alt image/title"
i18n:attributes="alt title" />
...
i18n:name placer des données variables dans une traduction.
Le problème consiste à traduire des constructions du type :
A faire
exemple avec i18n:name
...
<p>Hi</p>
Note
Utilisez tout le temps i18n:translate=”“ dans vos templates, même si vous appelez une fonction qui retourne un objet Message (une chaine commencant par _( dans votre code Python). Bien que la traduction marcherait sans le préciser avec le moteur de template d’origine, cela ne marcherait pas avec le moteur de template alternatif Chameleon qui peut être utilisé dans Plone 4.
Créez un environnement avec i18ndude installé :
$ mkvirtualenv i18n
$ easy_install i18ndude
Attention
N’installez jamais i18ndude dans l’environnement Python standard ou l’environnement virtuel spécifique à un projet mais bien dans un environnement virtuel dédié.
Dans Products/AgileKnowledgeBase créez un script update-l10n.sh :
$ i18ndude rebuild-pot --pot i18n/kb-plone.pot --create plone profiles
$ i18ndude filter i18n/kb-plone.pot \
~/.buildout/eggs/plone.app.locales-3.3.1-py2.4.egg/plone/app/locales/i18n/plone.pot \
> i18n/kb-plone.pot_
$ mv i18n/kb-plone.pot_ i18n/kb-plone.pot
$ i18ndude sync --pot i18n/kb-plone.pot i18n/kb-plone-*.po
$ i18ndude rebuild-pot --pot locales/AgileKnowledgeBase.pot --create AgileKnowledgeBase .
$ i18ndude sync --pot locales/AgileKnowledgeBase.pot locales/*/LC_MESSAGES/AgileKnowledgeBase.po
Exécutez i18ndude -h pour savoir à quoi servent les différentes commandes.
Installez le package Ubuntu gettext qui contient notamment la commande msginit. Installez également poedit (ou kbabel) pour traduire facilement :
$ sudo apt-get install gettext poedit
Exécutez le script update-l10n.sh, pour cela ajoutez le mode exécution :
$ chmod u+x update-l10n.sh
$ ./update-l10n.sh
La première exécution du script échouera pour les commandes i18ndude sync car il n’y a encore aucune traduction.
Vous allez donc maintenant initialiser la traduction française :
$ msginit -i i18n/kb-plone.pot -o i18n/kb-plone-fr.po
La commande msginit fait une copie du fichier pot en fichier po et remplie certains headers comme la date et le traducteur.
Ouvrez ensuite kb-plone-fr.po pour finir de remplir les headers. Remplacez notamment PACKAGE par AgileKnowledgeBase, supprimez VERSION et surtout modifiez ces headers :
"Language-Code: fr\n"
"Language-Name: French\n"
Vous pouvez maintenant passer à la traduction :
$ poedit i18n/kb-plone-fr.po
Vous utiliserez le dossier i18n uniquement pour le domaine plone.
Pour le domaine de votre produit, vous utiliserez le nouveau dossier locales qui est le standard gettext.
Pour le domaine AgileKnowledgeBase, il vous faut tout d’abord créer le dossier :
$ mkdir -p locales/fr/LC_MESSAGES
Générez le fichier AgileKnowledgeBase.pot avec le script :
$ ./update-l10n.sh
Ensuite créez le fichier de traduction :
$ msginit -i locales/AgileKnowledgeBase.pot -o locales/fr/LC_MESSAGES/AgileKnowledgeBase.po
éditez les headers du fichier po comme ceci :
"Language-Code: fr\n"
"Language-Name: French\n"
"Domain: AgileKnowledgeBase\n"
Traduisez ensuite avec poedit.
Pour que vos traductions soient prises au démarrage de l’instance, vous devez également avoir la directive i18n:registerTranslations dans votre configure.zcml, par exemple :
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="AgileKnowledgeBase">
<i18n:registerTranslations directory="locales" />
<grok:grok package="." />
</configure>
Voir http://dev.plone.org/plone/ticket/9090 pour l’état de la fusion des documents ci-dessus.
Traduction du template de mail créé précédemment.