[C#] Et la localisation/globalisation : la solution

Et la localisation/globalisation : la solution [C#] - C#/.NET managed - Programmation

Marsh Posté le 04-04-2006 à 10:42:10    

Quand j'ai fini par trouver comment ça marchait, j'ai bien du me rendre compte à quel point mes questions étaient stupides et étranges aux yeux de ceux qui avaient pigé :D
 
Par hasard, j'ai découvert comment localiser sans une ligne de code et de façon 100% automatisée une application avec Visual Studio 2003. A priori, c'est tellement simple que ça n'a pas dû changer d'un poil en 2005.
 
On va commencer par la solution "100%" automatique, mais qui ne permet pas de faire des choses très dynamiques.
 
Dans un premier temps, on crée sa form, et on place les éléments avec comme textes leurs noms (un label ayant pour ID "Label1" s'appellera donc à l'écran "Label1" ).
 
Une fois la forme correctement dessinée, aller dans les propriétés de la form, et choisir "Localizable" et le passer à "True".
Dans la propriété "Language", laisser pour le moment "(Default)".
 
Là, on vient d'expliquer à .NET qu'il ne dois plus écrire les libellés et les propriétés de la Form et de ses éléments directement dans le code, mais dans le fichier "Form1.resx" global (sans indication de langue).
La langue "Default" permet est la langue de "fall back". On utilisera par convention l'anglais standard (américain).
 
Maintenant, sans toucher à rien, on peut aller dans tous les contrôles de la Form, et modifier les textes, afin de mettre les traductions dans la langue américaine. (toujours passer par les propriétés de l'objet). Ca marche pour les labels, mais aussi pour les menus et autres éléments du framework.
 
Une fois la form totalement traduite en anglais, retourner dans les propriétés, et choisir une langue alternative. On va par exemple choisir "Français (Standard)" (c'est à dire le "fallback" de toutes les langues françaises - Suisse, canadien, côte d'ivoire, etc. -). Maintenant, on re-traduit tout. On verra dans le détail des fichiers que sous le fichier "Form1.cs", on a maintenant deux fichiers "resx" : Form1.resx et Form1.fr.resx
Maintenant, on va dire qu'on a une application de location de voitures, et qu'elle va être déployée aussi bien en France qu'au canada.
Autant dans 90% des contrées francophones, on appelle une voiture une voiture, autant au Québec on appelle ça un "char". Zou ! On retourne dans propriétés, on passe la langue en "Français (Canada)", et on va corriger le mot "voiture" partout dans la Form.
 
Maintenant, on aura 3 fichiers resx. Le dernier, vu qu'on n'a pas corrigé tous les textes, ne contiendra pas tous les textes. Aucune importance, le framework sait utiliser le "fallback" pour retrouver la traduction de substitution qui fonctionne le mieux, c'est à dire ici le français standard, et à défaut, la langue "default" (elle doit donc impérativement toujours être intégralement renseignée).
 
On notera aussi la la localisation ne s'arrête pas là. Si en Québécois, j'estime que les utilisateurs sont des gros crétins et que mon bouton "ok" doit être vert, et le bouton "annuler" doit être rouge, alors pendant que je suis en langage "français (canada)" je peux changer ses propriétés. Les nouvelles valeurs ne seront alors enregistrées que pour cette langue !
Par la même occasion, il est très impératif de toujours retourner en langage "Default" lorsqu'on modifie l'aspect des éléments, sinon seule la langue sélectionnée sera modifiée ! (très pratique pour traduire dans des langues écrites de gauche à droite notamment...)
 
Ensuite, comme ça marche ?
Le fichier de la langue par défaut sera directement compilé dans l'exe de l'application qui contient la form. Ainsi, quoiqu'on fasse subir aux fichiers durant l'installation, les libellés standard ne manqueront jamais à l'appel.
Toutes les autres langues vont se retrouver dans des fichiers DLL, qu'on pourra choisir ou non de distribuer avec l'application. En effet, ils ne sont pas liés à l'exe lors de la compilation. Ainsi, on peut très bien ne pas tous les livrer, ou en rajouter par la suite, sans devoir redistribuer toutes l'application (contrairement aux énormités que j'ai pu lire sur le net, annonçant cette fonctionnalité comme inédite dans la 2.0, ou justifiant l'utilisation d'une méthode "à la main" plutôt que les fonctions du framework).
 
Selon la culture (cultureUI, ou a défaut, culture) du thread qui fait tourner l'application, le framework va rechercher la DLL de traduction qui correspond le mieux. Cette culture est par défaut celle de Windows, telle que configurée dans les paramètres régionaux.
 
Ainsi, si je suis paramétré en "Français (France) / fr-fr" sur mon PC, et que je fais tourner l'application, le Framework aura au choix : "Français (canada) / fr-ca", "Français (standard) / fr" ou "Default / "
fr-ca n'est pas contenu dans "fr-fr". Par contre, "fr" tout court est bien contenu, il sera donc choisi. Maintenant, si je m'étais pointé en "Anglais (US) / en-US" ou en japonais "jp-jp", aucun code français n'aura correspondu, et je serait donc tombé vers la langue par défaut. Un japonais ayant plus de chances de parler anglais que français, on comprendra vite pourquoi il est important d'utiliser l'anglais comme langue par défaut ! Vous pouvez toujours vous amuser à écrire en espéranto, histoire d'être sur que personne ne vous comprenne ;)
 
 
Mais maintenant, c'est bien bô, mais moi je suis français, et à l'occasion, je dois utiliser le programme au japon, sur un PC qui n'est pas le mien. Je ne peux pas trop me permettre de changer la langue de ce dernier, au risque que le propriétaire ne s'y retrouver plus !
C'est ici que vient l'intérêt d'utiliser "la langue du thread" plutôt que celle de l'environnement : celle du thread est modifiable à la volée !
 
Imaginons qu'on récupère "tmpCultureId" dans la base de registre, une base de données ou un fichier de configuration. Elle contient le code sous forme "languagecode2/<country/regioncode2>" (à savoir fr-fr, en-us, etc.)
Le FrameWork reconnait aussi les format :
- LCID (entier, genre pour le français ça doit être 33 ou 1033 - à vérifier)
- ISO 639-2 (fra, eng, ita, etc.)
- Code Windows 3 lettres (grossomodo le même, mais en majuscules, et tel que défini dans l'API système)
- ISO 639-2 (fr, en, it, etc.)
 
Ici, selon si ce paramètre est existant ou non, on va attribuer une culture correspondante à ce code au thread, ou lui indiquer de continuer à utiliser la culture de l'environnement :

Code :
  1. Thread.CurrentThread.CurrentUICulture = (tmpCultureId != string.Empty)?
  2.                                     new System.Globalization.CultureInfo(tmpCultureId, true)
  3.                           :         System.Globalization.CultureInfo.CurrentCulture;


 
C'est pas tout... Moi j'ai des popups, et fichiers de log, ou simplement des textes d'information qui change durant l'execution de mon programme. Je ne peux donc pas les mettre en dur comme ça.
 
Deux solutions : une qui ne pose aucun problème à 1.1, mais qui fait hurler 2.0 Elle est un peu gore, mais la plus simple à mettre en oeuvre : ajouter des codes associés à des traductions durectement dans les fichiers resx, puis y faire appel. Pour ajouter des lignes dans ces fichiers, rien de plus simple : double-click sur le fichier, et on n'a plus qu'à le manipuler à la façon d'un fichier Excel.
Ensuite, dans le code :
 
On crée un resourcemanager, qui va permettre d'aller chercher les infos à l'éxécution dans la dll, suivant la langue désirée de façon automatique :

Code :
  1. private ResourceManager rm = new ResourceManager("Catalogue.MainForm",
  2.                                           System.Reflection.Assembly.GetCallingAssembly());


(Catalogue = nom du projet / MainForm = nom de la form)
 
Puis lors de l'affichage d'un message :
 

Code :
  1. this.statusBar1.Panels[0].Text = rm.GetString("statusLoad" );


 
(ici je met le texte indiquant le chargement de l'appication dans la statusbar).
 
 
La seconde méthode, moins pratique mais qui finalement est la même :
Créer un fichier resx nommé "mesTraductions.resx", qui correspondra à la langue "Default".
Un autre "mesTraductions.fr.resx", qui correspondra à la langue "Français (standard)".
Et enfin "mesTraductions.fr-ca.resx" qui correspondra à la langue "Français (Canada)".
 
Attention, on n'est pas libre pour le coup du "fr" et "fr-ca" ! ;)
 
Ensuite, lors de l'instantiation de "rm", on écrit plutôt :

Code :
  1. private ResourceManager rm = new ResourceManager("Catalogue.mesTraductions",
  2.                                             System.Reflection.Assembly.GetCallingAssembly());


 
(l'objet n'est plus "MainForm", mais le nom de mes fichiers resources, sans extensions)
Cette syntaxe est à vérifier, car à chaque fois je me plante et je passe une heure à retrouver le bon nom :D
Par contre, elle est meilleure, car elle permet notamment de bien séparer les ressources directement gérées par le framework des ressources gérées à la main.
 
PS: ResourceManager contient une propriété "GetString", mais aussi "GetPicture" et d'autres. Dans ces fichiers resx, on peut y mettre un peu n'importe quoi. Si dans la form on met une banière avec une image, et qu'on veut localiser l'image, on peut faire rigoureusement pareil que pour les textes. L'image ne sera alors pas distribuée séparément de l'exe, mais directement dans les DLL de localisation.
 
 
 
 
Voilà voilà. En espérant que ça pourra éviter à d'autres de se faire chier comme moi j'ai pu me faire chier... Fallait bien qu'une personne sur le net se décide enfin à poster une explication "claire" plutôt que 3 bous de code imbitables sans explications :o


Message édité par Arjuna le 04-04-2006 à 10:49:44
Reply

Marsh Posté le 04-04-2006 à 10:42:10   

Reply

Marsh Posté le 04-04-2006 à 10:46:40    

aha trop merci, j'ai déjà aussi bien galéré avec ça :jap:
 
[:drapal] donc.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed