I. Classe permettant de contourner le problème lié aux fichiers .resources▲
Afin de faciliter la mise à jour des différents textes de manière dynamique sans une recompilation (en cas de faute d'orthographe par exemple), nous utilisons notre propre classe reposant sur les différents principes cités dans l'article sur les techniques d'internationalisation en .NET. Ceci permet à une personne autre que les développeurs actuels, par exemple les administrateurs de l'application, de mettre à jour les différents textes. Ces textes se trouvent dans un fichier XML qui sera présenté dans le point suivant.
Une page d'administration des textes sert d'interface entre l'administrateur et le fichier XML.
Concrètement, cette classe permet de vérifier si le fichier XML a été mis à jour. Si c'est le cas, les fichiers .resources sont également mis à jour.
En voici les éléments les plus importants :
public
class
Globalization
{
static
protected
ResourceManager manager =
null
;
static
private
string
resourceName =
null
;
static
private
DateTime lastModification =
DateTime.
Now;
static
Thread ThreadcheckForUpdates;
static
private
string
xmlName;
static
private
string
path;
// Propriétés diverses
static
public
string
ResourcePath
{
set
{
resourceName =
value
;
manager =
ResourceManager.
CreateFileBasedResourceManager
(
value
,
path,
null
);
}
}
Remarque: Afin d'être certain que le manager a bien été spécifié, on pourrait éviter de mettre la classe en static. Un constructeur avec comme paramètres les valeurs nécessaires (nom du fichier .resources, répertoire dans lequel celui-ci se trouve et nom du fichier XML) serait préférable.
static
public
string
GetString
(
string
page,
string
component)
{
FileInfo fi =
new
FileInfo
(
path +
resourceName +
".resources"
);
if
(!
fi.
Exists)
GenerateResourceFiles
(
);
if
(
manager ==
null
)
throw
new
Exception
(
"Xml File Url is undefined"
);
// Récupération de la langue
string
returnString;
try
{
returnString =
manager.
GetString
(
page +
"."
+
component);
}
catch
(
Exception) {
returnString =
""
;
}
manager.
ReleaseAllResources
(
);
return
returnString;
}
Vu que les textes sont contenus dans un fichier XML, on utilise un parseur XML (DOM) pour les récupérer. On va ainsi boucler sur les différents nœuds pour en récupérer les valeurs correspondantes. Il est dès lors possible d'ajouter ces valeurs dans les fichiers .resources.
Cet exemple n'utilise que deux fichiers .resources. Cependant, il serait nécessaire d'effectuer quelques modifications afin de générer autant de fichiers .resources qu'il n'y a de nœuds au sein des éléments « data » et ce de manière automatique.
static
private
void
GenerateResourceFiles
(
)
{
FileStream fs =
new
FileStream
(
path +
resourceName +
".resources"
,
FileMode.
OpenOrCreate,
FileAccess.
Write);
FileStream fsEN =
new
FileStream
(
path +
resourceName +
".EN.resources"
,
FileMode.
OpenOrCreate,
FileAccess.
Write);
IResourceWriter writer =
new
ResourceWriter
(
fs);
IResourceWriter writerEN =
new
ResourceWriter
(
fsEN);
FileInfo fi =
new
FileInfo
(
path +
"_"
+
xmlName);
if
(!
fi.
Exists)
XMLTransform
(
);
XmlDocument document =
new
XmlDocument
(
);
document.
Load
(
path +
"_"
+
xmlName);
XmlNode rootnode =
document.
FirstChild;
foreach
(
XmlNode datanode in
rootnode)
foreach
(
XmlNode nodeLanguage in
datanode)
if
(
nodeLanguage.
Name ==
"FR"
)
writer.
AddResource
(
datanode.
Attributes[
"name"
].
Value,
nodeLanguage.
InnerText);
else
writerEN.
AddResource
(
datanode.
Attributes[
"name"
].
Value,
nodeLanguage.
InnerText);
writer.
Generate
(
);
writer.
Close
(
);
writerEN.
Generate
(
);
writerEN.
Close
(
);
}
Sauf si un codage particulier a été spécifié, un fichier XML ne doit comporter ni des caractères accentués ni des caractères spéciaux tels que « & ». La transformation de ces caractères serait trop fastidieuse pour l'utilisateur qui modifierait les textes, c'est pourquoi il est nécessaire de transformer automatiquement ceux-ci sans qu'il ne s'en rende compte. La transformation effectue une lecture dans le fichier XML de base et remplace les caractères qui le nécessitent avant d'écrire dans un autre fichier le contenu modifié. C'est ce fichier qui sera réellement utilisé pour la génération des fichiers .resources. Cette version ne permet cependant pas de remplacer les « < », « > » et « ? ». En utilisant le parseur DOM, le remplacement est envisageable.
static
private
void
XMLTransform
(
)
{
StreamReader sr =
new
StreamReader
(
path +
xmlName,
System.
Text.
Encoding.
Default);
StreamWriter sw =
new
StreamWriter
(
path +
"_"
+
xmlName,
false
,
System.
Text.
Encoding.
UTF8);
string
buffer =
sr.
ReadToEnd
(
);
buffer =
buffer.
Replace
(
"&"
,
"&"
);
// Autres remplacements de caractères
sw.
Write
(
buffer);
sw.
Close
(
);
sr.
Close
(
);
}
static
private
void
CheckForUpdatesThread
(
)
{
while
(
true
)
{
FileInfo fi =
new
FileInfo
(
path +
xmlName);
if
(
fi.
Exists &&
fi.
LastWriteTime !=
lastModification)
{
Globalization.
GenerateResourceFiles
(
);
lastModification =
fi.
LastWriteTime;
}
Thread.
Sleep
(
60000
);
// 1 minute
}
}
Dans le Global.asax, il est alors nécessaire d'appeler cette classe.
protected
void
Application_Start
(
Object sender,
EventArgs e)
{
Globalization.
Path =
Server.
MapPath
(
"."
) +
"
\\
"
;
Globalization.
ResourcePath =
"items"
;
Globalization.
XmlPath =
"globalization.xml"
;
Globalization.
CheckForUpdates
(
);
}
Pour récupérer une valeur provenant de ce fichier .resources, rien de plus simple, il suffit de passer le nom du composant (si les conventions ont été respectées), le nom de la page étant passé implicitement :
LaText.
Text =
Globalization.
Globalization.
GetString
(
"LaText"
);
ou, si l'on a utilisé une autre convention,
LaText.
Text =
Globalization.
Globalization.
GetString
(
"NomUserControl"
,
"LaText"
);
II. Description du fichier XML contenant les traductions▲
Le fichier XML est très simple. Chaque nœud, excepté le nom père, contient un attribut name. Cet attribut est le nom de l'élément. Pour rappel, la convention choisie est « Nomdelapage.NomControle ».
À chacun de ces nœuds sont attachés des nœuds fils qui ont pour nom les initiales de la langue.
<root>
<data
name
=
"Books_Add.ExceptionListingCategories"
>
<FR>
La liste des catégories n'a pas pu être chargée</FR>
<EN></EN>
</data>
<data
name
=
"Books_Add.ExceptionInvalidURL"
>
<FR>
La liste des catégories n'a pas pu être chargée</FR>
<EN></EN>
</data>
</root>
Le fichier, après transformation, ne contient plus de caractères accentués, mais bien les codes ASCII correspondants.
<root>
<data
name
=
"Books_Add.ExceptionListingCategories"
>
<FR>
La liste des catégories n'
a pas pu être chargée</FR>
<EN></EN>
</data>
<data
name
=
"Books_Add.ExceptionInvalidURL"
>
<FR>
La liste des catégories n'
a pas pu être chargée</FR>
<EN></EN>
</data>
</root>
L'utilisation de ce fichier pour y stocker de plus grandes quantités d'informations avec, entre autres, des balises de mise en forme, est tout aussi simple. La norme XML résout tous les problèmes qui pourraient survenir suite à l'insertion d'éléments HTML qui pourraient interférer dans le fichier XML et le rendre mal formé grâce à la notion de CDATA. Le contenu de cette « directive » est simplement lu et non pas interprété par le parseur.
<root>
<data
name
=
"FavoriteBooks.Title"
>
<FR>
<![CDATA[
Livres
<br/>
Suite…
]]>
</FR>
<EN>
Books</EN>
</data>
</root>
III. Code source de la classe▲
Le code de la classe est disponible ici : Classe Globalization.
IV. Conclusion▲
Je l'ai dit et je le répète, ce n'est pas LA solution, mais bien une solution. Cette classe évoluera en fonction des besoins, mais les étapes certaines sont :
- gestion plus de deux langues (nombre illimité, possibilité de manipuler plusieurs fichiers .resources simultanément lors du parsing des informations à ajouter) ;
- Custom Control d'administration (c'est-à-dire qui peut s'ajouter dans la toolbox de Visual Studio.NET, le détail de celui-ci fera l'objet d'un tutoriel complet) ;
- améliorer l'interface d'administration (mise online prochainement) ;
- …
J'espère en tout cas que cet article vous donnera des idées pour compléter cette solution (n'hésitez pas à me les envoyer) ou pour développer votre propre solution qui répondra mieux à vos besoins.
Cet article sera modifié lors de la mise à jour de la classe.