Flex 2 - Gestion des langues
Aujourd'hui, mes recherches portent sur la localisation. Adobe nous donne une solution mais elle n'est pas géniale.
Compiler en spécifiant la langue
Adobe nous donne la possibilité de spécifier la langue à la compilation d'une application, pas terrible. Ca veut dire que si on veut l'application dans une autre langue, il faut recompiler.
Comment ça marche ?
Les fichiers qui servent à la localisation sont des .properties, on en trouve dans le dossier flex_sdk_2/frameworks/locale/en_US/.
Pour utiliser ces fichiers, il suffit d'utiliser un metadata avant la déclaration d'une variable, comme ceci :
import mx.resources.ResourceBundle; [ResourceBundle("truc")] private var ma_ressource_truc:ResourceBundle;
Cette ressource va utiliser le fichier truc.properties dans le dossier de langue. Pour récupérer une traduction, on fait :
trace(ma_ressource_truc.getString("une_cle"));
Il y a aussi une syntaxe pour afficher une traduction dans un MXML : @Resource(bundle='truc', key='une_cle').
Compiler avec plusieurs langues
Ca c'est la mauvaise idée quand on veut faire une application multilangue. Ca a été suggéré par Jeff Taper dans son article Internationalizing Flex 2 Apps Pt 2 (en fait, je ne sais pas qui c'est :) ).
Je pense que je n'ai pas besoin d'expliquer pourquoi c'est pas bien, c'est presque du bon sens. D'ailleurs, j'ai même vu des exemples sur l'utilisation des namespace pour changer de langue. Il ne faut pas utiliser ça pour la traduction d'une application.
Charger dynamiquement une langue
Si c'est pas super d'inclure la langue à la compilation, alors on se tourne vers le chargement dynamique de la langue.
Par exemple, on peut charger des fichiers XML, des fichiers JSON ou autres. Bref, il faut se programmer son petit gestionnaire de langue avec le chargement, le parsage et tout. Ce qui ne doit pas être très long d'ailleurs.
Compiler un swf pour une langue
Une idée qui m'est venue, c'est d'utiliser les ResourceBundle de Adobe mais juste pour un swf qu'on va charger ensuite.
Je compile donc mon fichier de langue comme ça :
package { import flash.display.Sprite; import mx.resources.ResourceBundle; public class Locale extends Sprite { [ResourceBundle("common")] private var rb_common:ResourceBundle; [ResourceBundle("error")] private var rb_error:ResourceBundle; /** * Constructeur */ public function Locale() { } /** * Récupération de la valeur d'une clé dans un bundle * * @param pBundle Le nom du bundle * @param pKey La clé * @return La valeur */ public function getString(pBundle:String, pKey:String):String { var rb:ResourceBundle = this["rb_"+pBundle]; return rb.getString(pKey); } } }
J'obtiens un swf qui va inclure les contenus des fichiers common.properties et error.properties dans le dossier de langue (je zappe la procédure de création d'un swf).
Et dans mon application, je fais ça :
import flash.display.Loader; import flash.net.URLRequest; import flash.events.*; private var loader:Loader; private function init():void { // Chargement du fichier swf qu'on a créé pour contenir toutes les traductions d'une langue var urlRequest:URLRequest = new URLRequest("locale/fr_FR.swf"); loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler); loader.load(urlRequest); } private function completeHandler(e:Event):void { // Une fois chargé, on instancie la class Locale var domain:ApplicationDomain = loader.contentLoaderInfo.applicationDomain; var localeClass:Object = domain.getDefinition("Locale"); var locale:Object = new localeClass(); // Voilà un petit test pour vérifier que ça marche trace(locale.getString("common", "hello")); }
Vous avez remarqué l'utilisation de Object comme type d'instance. Il est possible d'implémenter une interface dans Locale, puis de typer l'instance avec l'interface, pour avoir du typage à la compilation. Je ne l'ai pas fait ici pour faire plus court.
Conclusion
Je ne sais pas ce qu'il y a derrière le chargement des .properties ni des performances par rapport à un chargement perso de fichier texte. En tout cas, ça m'évite de faire une deserialisation.
Je vais utiliser ce dernier système pour l'instant, si quelqu'un a une autre solution ... :) .
PS: Les icones de l'illustration du billet ont été créées par Maurice Svay.
Commentaires
1. Le vendredi, octobre 20 2006, 16:24 par thecaptain
Hello,
Elle est définie ou ta clé "hello" ? Dans ton common.properties ??? Il est intégré comment à la compilation ? Tu peux donner plus de détails dessus ? :)
@++
2. Le vendredi, octobre 20 2006, 18:17 par neolao
ouais, c'est dans le common.properties, et il est intégré grâce à [ResourceBundle("common")]. Uniquement ça.
3. Le mercredi, octobre 25 2006, 19:51 par ekameleon
Hello :)
Pour ma part j'utilise le protocole EDEN (ou JSON au pire) pour mes fichiers de localizations (car la localization c'est des textes mais aussi parfois des url de swf etc... selon la langue courante) et au niveau de mon framework AS2 , j'utilise un Singleton qui charge dans une HashMap les contenus pour chaque langue, avec un identifiant basé sur une classe Lang qui globalise les langues par défaut de Flash (on peut en ajouter du coup) :
svn1.cvsdude.com/osflash/...
Voir les classes Lang, Localization et les exemples dans :
svn1.cvsdude.com/osflash/...
A noter que l'on peut choisir un "Loader" pour avoir la possibilité d'utiliser des fichiers json, eden, xml etc... (j'ai pas encore fait de version XML d'ailleurs.)
Tu me fais penser d'ailleurs qu'il faudrait que je finisse la version AS3 au passage...
Dans tous les cas la localization au niveau du code d'un framework directement c'est intéressant mais je trouve cela pratique au niveau des erreurs et des logs.. pour le reste je vois pas l'intérêt.
EKA+ :)
4. Le jeudi, octobre 26 2006, 00:24 par neolao
C'est pour ça qu'il faudrait faire des benchmarks avec des méthodes persos. C'est vrai que de toute façon, on ne charge pas souvent une langue, voire qu'une seule fois. Faire des tests sur les techniques de chargement n'a pas de sens je crois.
Il faudrait faire un bench sur la récupération d'une localisation, voir on gagne beaucoup à le faire soit même.
5. Le lundi, mars 10 2008, 15:05 par Arrangeur
je suis drôlement surpris par lez peu de commentaires laisqsés à ton billet ;)
6. Le lundi, mars 10 2008, 15:25 par neolao
je ne suis pas assez lu :(
7. Le jeudi, mars 13 2008, 18:10 par Daniel
Mais si, ce n'est pas parce que l'on ne commente pas que l'on ne lit pas ;-)
Tu pourrais aussi en déduire que le post est tellement complet qu'il n'y a rien à ajouter en commentaire !