Author: jcouteau Date: 2011-03-24 15:37:51 +0100 (Thu, 24 Mar 2011) New Revision: 1894 Url: http://nuiton.org/repositories/revision/i18n/1894 Log: Add doc for GWT Added: trunk/src/site/apt/gwt.apt trunk/src/site/en/apt/gwt.apt Modified: trunk/src/site/site_en.xml trunk/src/site/site_fr.xml Added: trunk/src/site/apt/gwt.apt =================================================================== --- trunk/src/site/apt/gwt.apt (rev 0) +++ trunk/src/site/apt/gwt.apt 2011-03-24 14:37:51 UTC (rev 1894) @@ -0,0 +1,177 @@ +GWT UiBinder et i18n, vous avez dit un cauchemard ? + +* GWT avec nuiton-i18n (et Maven) et ça coule de source + +** Le fonctionnement classique de l'i18n dans GWT + + Si vous avez des écrans que vous avez créé avec UiBinder et que vous souhaitez + les internationaliser, vous aller vous tourner vers la page ad-hoc de la + documentation de GWT + (http://code.google.com/intl/fr/webtoolkit/doc/latest/DevGuideUiBinderI18n.ht...). + Dans cette documentation, on vous apprend que vous devez utiliser, pour + l'internationalisation, la syntaxe suivante : + +-------------------------------------------------------------------------------- + <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator" + ui:generateLocales="default"> + <div><ui:msg description="Greeting">Hello, world.</ui:msg></div> + </ui:UiBinder> +-------------------------------------------------------------------------------- + + Cela vous donnera un magnifique fichier de propriété qui ressemble à ça : + +-------------------------------------------------------------------------------- + # Generated from my.app.HelloWorldMyBinderImplGenMessages + # for locale default + # Description: Greeting + 022A824F26735ED0582324BE34F3CAE1=Hello, world. +-------------------------------------------------------------------------------- + + Ok, cool, mais ce beau fichier de propriété, vous le donnez à un traducteur, + il vous regarde avec des gros yeux, il claque la porte et vous le revoyez + jamais. D'autant plus que vous allez avoir un fichier de propriété par fichier + UiBinder. Oui oui, vous avez bien lu. + + Bon admettons, on va chercher à améliorer ce fichier de propriétés alors. + Déjà, en changeant le générateur on arrive à avoir de beaux fichiers. Bien. + Mais on a toujours pleins de fichiers de propriétés, qui en plus sont générés + dans pleins d'endroits différents. Un beau cauchemard. + + En cherchant un peu sur le net, on voit que si on met toutes nos chaines dans + un fichier LocalizableResources.properties, on n'a plus besoins de tous les + autres ficheirs de propriétés. On trouve un script python qui nous met tout ça + en un seul endroit mais vous vous voyez utiliser un script python a l'heure de + l'automatisation de toutes les taches répétitives. Un script python, alors que + vous faites du Java ! + + Si vous êtes arrivés jusqu'ici et que vous avez persévéré, vous voyez bien que + l'internationalisation en utilisant UiBinder est un cauchemard. + +** Le fonctionnement attendu + + Bon, et si on se posait les bonnes questions. Qu'est ce que veulent toutes les + personnes impliquées dans la boucle ? Les développeurs ? Les traducteurs ? + + Les développeurs, ils veulent surtout en écrire le moins possible. Répéter les + informations de génération en haut de chaque fichier c'est un peu répétitif + et, comment dire, pénible. Appliquer un script python c'est pas optimum, si on + pouvait faire ça avec un outil de gestion des builds (comme Maven) ça serait + bien quand même. Le développeur ne doit faire que coder, toutes les + manipulations de fichier doivent être laissées à l'outil de gestion de build. + + Les traducteurs, ils veulent des fichiers facile à manipuler, au maximum un + fichier par langue. Il faut donc grouper les traductions de tous les fichiers. + Il ne faut aps aller chercher les nouvelles clés dans le code. Tout doit être + dans le fichier de propriété, seule la traduction doit être à la charge du + traducteur. + + Donc en résumé, on veut que les chaînes soient extraites de façon automatisées + et placées dans un fichier de propriété unique (un par langue). Il faut que + l'on conserve les chaînes déjà traduites d'une version à l'autre. Il faut que + la configuration de la génération soit centralisée et que cette génération + soit gérée par l'outil de gestion de build. + +** Et comment on s'y prend ? + + Le processus de build peut déjà être géré par Maven + (http://mojo.codehaus.org/gwt-maven-plugin/), on va donc se baser sur Maven, + d'autant plus que la librairie nuiton-i18n + (http://maven-site.nuiton.org/i18n/) fournit un plugin qui gère les clés de + traduction et leur extraction déjà très bien. On va donc se baser sur ces deux + outils. + + On va considérer que vous avez réussi à compiler/lancer votre projet avec + Maven pour se consacrer à ce qui nous intéresse, la traduction de nos fichiers + UiBinder. On va donc utiliser nuiton-i18n et plus particulièrement le parser + xml avec nos propres règles. + +** Création de notre règle de parsing + + Nuiton-i18n fournit un parser XML qui est très puissant et peut être configuré + par des règles externes. Nous allons donc créer une règle pour parser les + fichiers UiBinder de GWT. Notre règle s'appelera 'gwt.rules' et sera placée + dans notre répertoire *src/main/i18n*. Son contenu sera uniquement : + +-------------------------------------------------------------------------------- +//ui:msg/@key +-------------------------------------------------------------------------------- + + La valeur est du jxpath indiquant que l'on recherche tous les attributs key + des éléments ui:msg. Jusque là, pas de soucis. Vous pourrez améliorer cette + règle si vous avez plus de champs à traduire, mais elle devrait suffire à la + plupart des cas. + +** Configuration du plugin + + Nous allons configurer le plugin nuiton-i18n pour arriver à nos fins. + + Dans une première exécution, nous allons lui dire de parser nos fichiers + UiBinder (*.ui.xml) situés dans notre dossier src/main/java en utilisant la + rule créée précédemment. Il ne faut pas oublier de préciser les namespace pour + que le plugin s'y retrouve dans les path. + + Dans une deuxième execution, on génère les fichiers de propriété qui seront + situés dans src/main/resources/i18n. + + Dans une dernière exécution, on génère le bundle. On précise le fichier et + répertoire de sortie, soit LocalizableResource (les langues et le .properties + sont ajoutés automatiquement) et WEB-INF/com/google/gwt/i18n/client dans le + répertoire de votre hostedWebapp (ici src/main/webapp). + +-------------------------------------------------------------------------------- +<plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>maven-i18n-plugin</artifactId> + <executions> + <execution> + <id>scan-gwt-sources</id> + <goals> + <goal>parserXml</goal> + </goals> + <configuration> + <basedir>${project.basedir}/src/main/java</basedir> + <includes>**/*.ui.xml</includes> + <userRulesFiles> + <file>${basedir}/src/main/i18n/gwt.rules</file> + </userRulesFiles> + <namespaces> + <gwt>urn:import:com.google.gwt.user.client.ui</gwt> + <ui>urn:ui:com.google.gwt.uibinder</ui> + </namespaces> + </configuration> + </execution> + <execution> + <id>gen</id> + <goals> + <goal>gen</goal> + </goals> + </execution> + <execution> + <id>make-bundle</id> + <goals> + <goal>bundle</goal> + </goals> + <configuration> + <generateDefinitionFile>false</generateDefinitionFile> + <addBundleOuputDirParent>false</addBundleOuputDirParent> + <bundleOutputPackage>com/google/gwt/i18n/client</bundleOutputPackage> + <bundleOutputName>LocalizableResource</bundleOutputName> + </configuration> + </execution> + </executions> +</plugin> +-------------------------------------------------------------------------------- + +** Premier build + + Lancez un premier build, vous allez avoir vos fichiers de propriétés vides qui + vont venir se placer dans votre répertoire src/main/resources/i18n. + Remplissez-les (pensez à bien échapper les caractères spéciaux en les + remplaçant par leur code unicode. + +** Second build, c'était compliqué ? + + Lancez un deuxième build, lancez votre application. Vos langues sont + disponibles ? Bien... C'est toujours un cauchemard ? Added: trunk/src/site/en/apt/gwt.apt =================================================================== --- trunk/src/site/en/apt/gwt.apt (rev 0) +++ trunk/src/site/en/apt/gwt.apt 2011-03-24 14:37:51 UTC (rev 1894) @@ -0,0 +1,164 @@ +GWT UiBinder and i18n, you said nightmare ? + +* GWT with nuiton-i18n (and Maven) and it's easy + +** The way i18n works in GWT + + If you got screens you made using UiBider and that you want to localize, you + will go to the ad-hoc page of the GWT documentation + (http://code.google.com/intl/fr/webtoolkit/doc/latest/DevGuideUiBinderI18n.ht...). + In this documentation, you learn tha tyou must use, for localization, the + following syntax : + +-------------------------------------------------------------------------------- + <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator" + ui:generateLocales="default"> + <div><ui:msg description="Greeting">Hello, world.</ui:msg></div> + </ui:UiBinder> +-------------------------------------------------------------------------------- + + This will give you a nice property file that looks like that : + +-------------------------------------------------------------------------------- + # Generated from my.app.HelloWorldMyBinderImplGenMessages + # for locale default + # Description: Greeting + 022A824F26735ED0582324BE34F3CAE1=Hello, world. +-------------------------------------------------------------------------------- + + Nice, but this property file, if you give it to a translator, he will look at + you with big eyes, smash the door and you will never see him again. And you + will got a property file per UiBinder file. Yes you read well. + + Let's admit that, try to improve the property file then. Changing the + generator, we succeed in getting nice files. Ok. But we still get lots of + property files, that are generated in different directories. A nightmare. + + If you search on the net, we see that if we put all our translated strings in + a file called LocalizableResources.properties, we do not need all the other + files anymore. You can find a python script that puts everything in a single + place, but you see yourself using python script whereas all repetitive tasks + are automatized. A python script, but you are coding in Java ! + + So if you get there, you found out that localization in GWT when using + UiBinder can be quite a pain. + +** How we want it to work + + So let's ask the right questions. What all people involved in the localization + process want ? Developers ? Translators ? + + Developers want to write the minimum code. Repeat generation documentation on + top of each file can be quite repetitive and useless. Applying a python script + is not optimum, if we could process this with our build manager (like Maven or + Ant) that could be great. The developer must only have to code, all the files + manipulations must be left to the build manager. + + The translators want files easy to deal with. And one file per language + maximum. We must gather the translation sof all files in one. The translator + should not have to search for translation keys in the code. Everything must be + in the property file, only translation must be done by the translator. + + So to summarize, we want the strings to be extracted in an automatized way and + placed in a unique property file (one per language). We must keep the already + translated strings from a version to the other. And the generation + configuration must be centralised and the generation dealt with by the build + manager. + +** How we do that ? + + The build process can already be managed by Maven + (http://mojo.codehaus.org/gwt-maven-plugin/), so we will be based on Maven, + and nuiton-i18n got a Maven plugin managing translation keys and extraction + quite well. + + We will consider here that you can compile/run your GWT project with Maven to + go to the interesting part, the translation of UiBinder files. So we will use + the xml parser with our own rules. + +** Creating our own parsing rule + + Nuiton-i18n got a XML parser that is quite powerful and can be configured with + external rules. So we will create a rule to parse GWT UiBinder files. Our rule + will be named gwt.rules and will be placed in *src/main/i18n* directory. It's + content will be : + +-------------------------------------------------------------------------------- + //ui:msg/@key +-------------------------------------------------------------------------------- + + The value is jxpath indicating that we look all the key attributes of the + ui:msg elements. So far no problem. You can update this rule if you got other + stuff to translate, but this should cover most of the cases. + +** Plugin configuration + + We will configure the nuiton-i18n plugin to be able to do what we want. + + In a first execution, we will tell him to parse our UiBinder files (*.ui.xml) + located in our src/main/java directory using the rule previously created. + Do not forget to indicate the namespaces so the plugin can deal with paths. + + In a second execution, we generate the property files located in + src/main/resources/i18n. + + The last execution generate the Bundle. We give the name of the file and its + package : LocalizableResource (language and .properties are added by the + plugin) and com/google/gwt/i18n/client. + +-------------------------------------------------------------------------------- +<plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>maven-i18n-plugin</artifactId> + <executions> + <execution> + <id>scan-gwt-sources</id> + <goals> + <goal>parserXml</goal> + </goals> + <configuration> + <basedir>${project.basedir}/src/main/java</basedir> + <includes>**/*.ui.xml</includes> + <userRulesFiles> + <file>${basedir}/src/main/i18n/gwt.rules</file> + </userRulesFiles> + <namespaces> + <gwt>urn:import:com.google.gwt.user.client.ui</gwt> + <ui>urn:ui:com.google.gwt.uibinder</ui> + </namespaces> + </configuration> + </execution> + <execution> + <id>gen</id> + <goals> + <goal>gen</goal> + </goals> + </execution> + <execution> + <id>make-bundle</id> + <goals> + <goal>bundle</goal> + </goals> + <configuration> + <generateDefinitionFile>false</generateDefinitionFile> + <addBundleOuputDirParent>false</addBundleOuputDirParent> + <bundleOutputPackage>com/google/gwt/i18n/client</bundleOutputPackage> + <bundleOutputName>LocalizableResource</bundleOutputName> + </configuration> + </execution> + </executions> +</plugin> +-------------------------------------------------------------------------------- + +** First build + + Launch a first build, your empty (not translated) property files will be + created and in your src/main/resources/i18n. Fill them (translate) (think + about escaping special caracters by replacing them with their unicode code). + +** Second build, said difficult ? + + Launch a second build, launch your app. Your languages are available ? Ok... + still getting nightmares ? Modified: trunk/src/site/site_en.xml =================================================================== --- trunk/src/site/site_en.xml 2011-03-24 13:24:18 UTC (rev 1893) +++ trunk/src/site/site_en.xml 2011-03-24 14:37:51 UTC (rev 1894) @@ -57,6 +57,7 @@ <item name="Home" href="index.html"/> <item name="Presentation" href="presentation.html"/> <item name="Best Practices" href="bestPractices.html"/> + <item name="Use with GWT" href="gwt.html"/> </menu> <menu name="Tutorials"> Modified: trunk/src/site/site_fr.xml =================================================================== --- trunk/src/site/site_fr.xml 2011-03-24 13:24:18 UTC (rev 1893) +++ trunk/src/site/site_fr.xml 2011-03-24 14:37:51 UTC (rev 1894) @@ -57,6 +57,7 @@ <item name="Accueil" href="index.html"/> <item name="Présentation" href="presentation.html"/> <item name="Best Practices" href="bestPractices.html"/> + <item name="Utilisation avec GWT" href="gwt.html"/> </menu> <menu name="Tutoriaux">
participants (1)
-
jcouteau@users.nuiton.org