Localisation d'une application Silverlight

Benjamin Roux

Cet article vous présentera comment localiser une application Silverlight.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Localisation d'une application Silverlight

- Auteur : Benjamin Roux

- Parution : 7 décembre 2009

- Licence : Copyright ® 2009 Benjamin Roux. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Comment ça marche

- III. Création des fichiers ressources

- IV. Définition de la langue par défaut et des langues supportées

- V. Création du XAML localisé

- VI. Initialisation de la langue

- VII. Changement de langage au runtime

- VIII. Localisation de propriétés non « bindable »

- IX. Formatage de chaîne localisée

- X. Conclusion

- Remerciements

I - Introduction

Dans cet article, nous allons voir comment localiser une application Silverlight.

II - Comment ça marche

La localisation en Silverlight est basée sur l'utilisation de fichiers ressources. Nous avons un fichier ressource par langues supportées.

III - Création des fichiers ressources

Premièrement, nous allons partir d'un projet Silverlight tout simple auquel nous allons rajouter des fichiers resources.

Il faut maintenant rajouter autant de fichiers ressources que nous avons de langages supportés par notre application.

Pour le langage par défaut, nous allons appeler notre fichier Language.resx. Pour les autres langues, il faut respecter la conventionLanguage.xx.resx où xx représente la culture de la langue contenue dans le fichier ressource. Par exemple, si votre fichier ressource contient les textes pour la langue française, le nom de votre fichier sera Language.fr.resx. On peut également spécifier pour une culture spécifique, par exemple Language.fr-ca.resx.

Votre projet devrait donc ressembler à ça maintenant :

Voici le contenu de Language.resx.

Notre application comportera donc un bouton dont le texte sera localisé.

À noter qu'il faille modifier le modificateur d'accès de la classe d'Internal à Public.

Un bug dans l'outil de génération nous oblige à modifier le fichier .cs créé à la main.

Ouvrez le .cs et modifiez le modificateur d'accès du constructeur de internal à public.

internal Language() {

devient donc :

public Language() {

Nous allons maintenant renseigner le fichier Language.fr.resx.

Nous venons de terminer la création de nos fichiers de ressources. Passons à la suite.

IV - Définition de la langue par défaut et des langues supportées

Nous allons maintenant définir la langue par défaut de notre application.

Clic droit sur notre projet -> Propriétés -> Assembly Info.

Il suffit de renseigner la case NeutralLanguage.

Cela rajoute simplement une ligne dans le fichier AssemblyInfo.cs.

[assembly: NeutralResourcesLanguageAttribute("en")]

La définition de langues supportées est un peu plus délicate (en espérant que ce soit amélioré dans une prochaine version).

Il faut tout d'abord décharger le projet : clic droit -> Unload Project.

Puis éditez le fichier csproj : clic droit -> Edit xxx.csproj.

Dans le fichier, repérez la section <SupportedCultures> et rajoutez les langues gérées par votre application, séparées par un « ; ».

Vous devriez obtenir quelque chose dans ce genre :

<SupportedCultures>fr</SupportedCultures>

Cette section pourrait également ressembler à ceci si notre application gérait plusieurs langues.

<SupportedCultures>fr;fr-CA;de;ru</SupportedCultures>

Rechargeons le projet : clic droit -> Reload Project.

Vous pouvez maintenant réaliser un petit test en compilant votre projet et en regardant le contenu du fichier xap généré.

On peut voir que Visual Studio a généré un répertoire fr qui contient les ressources liées à la langue française.

V - Création du XAML localisé

Maintenant que nous avons créé nos ressources, nous allons les appliquer sur notre XAML.

Créons d'abord un simple bouton sans contenu.

<Button Width="100" Height="25" />

Ajoutons maintenant les références à notre fichier de ressource.

<UserControl x:Class="SLocalization.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lang="clr-namespace:SLocalization.Languages"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<lang:Language x:Key="Lang" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Button Width="100" Height="25" />
</Grid>
</UserControl>

Maintenant, le principe est de binder le contenu de notre bouton sur notre texte localisé.

<Button Content="{Binding Path=ButtonPress, Source={StaticResource Lang}}" Width="100" Height="25" />

On dit que le texte de notre bouton se trouve à la propriété ButtonPress de la ressource Lang qui n'est ni plus ni moins que notre fichier Resource.

Le résultat :

Testons maintenant en français.

Dans le fichier App.xaml.cs :

private void Application_Startup(object sender, StartupEventArgs e)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");

this.RootVisual = new MainPage();
}

Avant de créer notre application, nous spécifions la culture.

Le résultat :

Le contenu de notre bouton est donc maintenant localisé !

VI - Initialisation de la langue

Pour initialiser la langue de l'application, le plus simple reste d'utiliser des paramètres d'initialisation ou bien des paramètres dans l'URL.

Paramètres dans l'URL :

private void Application_Startup(object sender, StartupEventArgs e)
{
if (HtmlPage.Document.QueryString.Keys.Contains("lang"))
{
string lang = HtmlPage.Document.QueryString["lang"];
Thread.CurrentThread.CurrentCulture = new CultureInfo(lang);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
}

this.RootVisual = new MainPage();
}

Utilisation : http://localhost:55466/SLocalizationTestPage.aspx?lang=fr

Paramètres d'initialisation :

if (e.InitParams.Keys.Contains("lang"))
{
string lang = e.InitParams["lang"];
Thread.CurrentThread.CurrentCulture = new CultureInfo(lang);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
}

Utilisation (dans la création du plugin dans la page aspx ou html) :

<param name="initParams" value="lang=fr" />

VII - Changement de langage au runtime

Avec la méthode décrite plus haut, la modification de la culture au runtime ne rafraîchit pas l'UI.

Pour obtenir ce résultat, il faut légèrement modifier notre code.

Tout d'abord, nous allons créer une classe LangHelper exposant notre fichier ressource. Le petit plus sera l'implémentation de l'interface INotifyPropertyChanged.

Le code est donc le suivant :

public class LangHelper : INotifyPropertyChanged
{
private static Language mResource = new Language();

public Language Resource
{
get { return mResource; }
}

public void ChangeCulture(string culture)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
NotifyPropertyChanged("Resource");
}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}

#endregion
}

On expose notre fichier ressource via une propriété, et créons une méthode permettant de modifier la culture de notre application et déclenchant l'évènement PropertyChanged.

Retour dans notre fichier xaml principal qui va subir quelques changements :

<UserControl x:Class="SLocalization.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lang="clr-namespace:SLocalization.Helpers"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<lang:LangHelper x:Key="Lang" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Button Content="{Binding Path=Resource.ButtonPress, Source={StaticResource Lang}}" Width="100" Height="25" />
</Grid>
</UserControl>

Le texte de notre bouton est maintenant toujours bindé sur la propriété ButtonPress de notre fichier de ressource, mais cette fois-ci en passant par une propriété intermédiaire.

Maintenant en modifiant la langue via la méthode ChangeCulture tous les bindings de notre UI seront rafraîchis.

Cette méthode peut facilement être adaptée dans une application utilisant le pattern MVVM.

VIII - Localisation de propriétés non « bindable »

Il peut arriver que l'on veuille localiser une propriété sur laquelle le binding n'est pas possible (une propriété n'étant pas une Dependency Property le plus souvent), comme le header d'une DataGridTextColumn.

Silverlight ne propose pas de méthode type pour ça, mais seulement des « astuces ».

Pour le cas présent, il va falloir passer par du code behind.

column1.Header = Languages.Language.ButtonPress;

Correspondant au code XAML suivant

<data:DataGridTextColumn x:Name="column1" . />

Le problème ici vient du changement de langue au runtime. Il faudra reprendre toutes les propriétés une par une pour remettre la valeur dedans.

On peut bien évidemment passer par une méthode qui fait tout pour nous, mais cela reste moins pratique que le binding.

IX - Formatage de chaîne localisée

Il peut arriver que l'on veuille localiser du texte contenant des paramètres.

Exemple, je veux afficher un TextBlock contenant un message de bienvenue pour l'utilisateur courant.

Selon la langue je veux donc afficher Bonjour ou Hello suivi du nom de l'utilisateur.

Pour ce faire, nous allons rajouter une entrée dans chaque fichier ressource.

Maintenant pour afficher notre texte, nous allons simplement passer par un converter.

public class TextFormatterConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return string.Format(value.ToString(), parameter.ToString());
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

#endregion
}

Maintenant lors de notre binding, il faut simplement spécifier notre converter ainsi que son paramètre.

<UserControl x:Class="SLocalization.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lang="clr-namespace:SLocalization.Helpers"
xmlns:converter="clr-namespace:SLocalization.Converters"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<lang:LangHelper x:Key="Lang" />
<converter:TextFormatterConverter x:Key="TextFormatter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding Path=Resource.Hello,
Source={StaticResource Lang},
Converter={StaticResource TextFormatter},
ConverterParameter=Benjamin}" />
</Grid>
</UserControl>

Pour utiliser un paramètre variable, on peut définir le binding en code au lieu de passer par le XAML.

X - Conclusion

La localisation d'une application Silverlight est, comme vous avez pu le voir, assez facile. La méthode proposée est quand même assez basique.

Tout ce processus de localisation devrait être amélioré dans une future version de Silverght et Xaml 2009.

Remerciements

Merci à toute l'équipe .NET pour leurs relectures et corrections.

Merci également à Wachter pour sa relecture et correction finale.