Une de mes problématiques en ce moment est liée à la localisation de mes applications. En effet, sous Windows Store App, si l'on veut switcher d'une langue à l'autre, il faut soit changer la langue de son OS et relancer l'application (si celle-ci utilise la localisation à partir des ressources) ou alors créer son système custom et tout ça doit être bien intégré dans votre pattern MVVM, or moi je voudrais faire ça à la volé. Bref un vrai casse-tête.
Personnellement j'ai planché un peu sur le problème et j'ai donc trouvé une première solution, certes ce n'est pas spécialement la meilleur ou la plus performante, mais elle offre je pense un bon compromis entre Resources / Binding / MVVM. Voilà en substance ma solution personnelle :

Le principe est de conserver le système de ressource (ici en rouge avec les fichiers "Resources.resw" et "Resources.lang-fr-FR.resw"), de créer une classe de gestion des ressources (ici en rouge avec la classe "ResourcesSwitcher") et pour faire le lien entre les deux par du Binding, un Converter (ici en rouge avec la classe "LanguageConverter"). Je vous laisse regarder en détail le code source (lien du projet en fin d'article), je vais juste faire un focus sur les éléments importants.
Tout d'abord il faut récupérer le contenu des ressources via la classe du Framework "ResourceManager", cela permet d'avoir l'arbre des différents dictionnaires dans les différentes langues :
// Récupération de la map
ResourceMap map = ResourceManager.Current.MainResourceMap.GetSubtree("Resources");
Ensuite, il faut créer les contextes, cela représente au final les langues que l'ont veut traiter, cela sera utile plus tard quand on voudra récupérer des informations dans la "map" des ressources, en spécifiant la langue le Framework pourra allez chercher la bonne valeur dans la bonne langue.
ResourceContext english = new ResourceContext() { Languages = new List<string>() { "en-Us" } };
ResourceContext french = new ResourceContext() { Languages = new List<string>() { "fr-FR" } };
Pour finir, pour récupérer une valeur dans la "map" des ressources il suffit de faire ceci :
map.GetValue("CleDictionnaire", french).ValueAsString
Ça c'est pour la récupération d'un élément localisé dans les ressources par le code. Vous comprenez donc que pour passer d'une langue à l'autre il suffit de changer le deuxième paramètre de "GetValue()", par exemple on peut remplacer le "ResouceContext" "french" par "english".
Ensuite il faut faire le lien entre le XAML et ce mécanisme. Moi j'ai choisi de créer une classe ("ResourcesSwitcher") qui sera une classe avec propriété notifiée qui exposera la "map". Pour créer un singleton de cette classe, je la déclare dans mon "App.xaml" :
<Application x:Class="BlackBlog.DynamicLocalization.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="using:BlackBlog.DynamicLocalization.Converters"
xmlns:vcore="using:BlackBlog.DynamicLocalization.Views.Core">
<Application.Resources>
<ResourceDictionary>
<!-- Converteur pour récupérer les conversions -->
<conv:LanguageConverter x:Key="LanguageConverter" />
<!-- Singleton pour le moteur de conversion -->
<vcore:ResourcesSwitcher x:Key="ResourcesSwitcher" />
</ResourceDictionary>
</Application.Resources>
</Application>
Au passage vous remarquerez que j'ai déclaré aussi un Converter. Celui-ci va servir a extraire une valeur (avec le "GetValue()" de la "map") depuis le XAML en utilisant le "ConverterParameter". En gros ça donne ca si on essaye de binder une ressource sur une propriété "Text" d'un "TextBlock" :
<TextBlock FontSize="16"
Foreground="White"
Text="{Binding Source={StaticResource ResourcesSwitcher},
Path=Map,
Converter={StaticResource LanguageConverter},
ConverterParameter='Sentence'}" />
Avec ce système on peut donc binder nos ressources sur nos contrôles. Mais à quoi ça sert tout ça me direz-vous ! Et bien justement, si vous y regardez de plus prês, vous remarquez que nous sommes en binding, qui dit binding dit notification. Comme je l'ai souligné plus haut, dans ma classe "ResourcesSwitcher" j'ai exposé la "Map" via une propriété notifiée, donc il suffit de gérer qu'elle langue est sélectionnée actuellement (pour ma part j'ai mis une propriété dans mon "ResourcesSwitcher"), et au moment du changement de langue il suffit de notifier cette propriété, et là le moteur de binding va réévaluer toutes vos binding et repasser par le converter :
public object Convert(object value, Type targetType, object parameter, string language)
{
// Récupération de la Map de ressource
ResourceMap map = value as ResourceMap;
if (map != null && !string.IsNullOrWhiteSpace((string)parameter))
return (map.GetValue((string)parameter, ResourcesSwitcher.Instance.CurrentLanguage).ValueAsString);
// Pas de convertion
return (value);
}
Et dans le converter, je vais chercher la langue en sélection pour l'évaluation de la valeur. Grâce à ça vous avez un système de localisation dynamique.
Ceci est bien sûr une solution parmi tant d'autre, qui a ses avantages et ses défaut, libre à vous l'améliorer. Vous pouvez télécharger le code par ce lien :
BlackBlog.DynamicLocalization.rar (130,97 kb)