Windows 8 dont la sortie est normalement prévue pour le second semestre (aucune date confirmée pour le moment) prend peu à peu sa place dans le viseur des DSI et le nombre de projets Windows 8 ne cesse de croitre. Parmi les besoins communs aux différentes applications, c’est très souvent le besoin d’une version localisée qui se fait ressentir. Ce sujet, qui peut paraitre simple nous a laissé quelques surprises au fil des versions bêta de Windows 8.

Localisation d’une application XAML

Il existe de très nombreuses façons de localiser une application, que ce soit par le code-behind ou même le XAML et il n’est pas nécessaire ici de refaire une passe sur toutes ces solutions. je vais parler ici de la plus simple et bien souvent la plus propre car elle simplifie le code au maximum mais elle demande de la rigueur notamment dans la maintenance du fichier de ressource et spécifiquement sur le nom des clés. Voyons pourquoi.

La solution la plus simple de localiser votre application est de définir l’attribut x:Uid sur tous les éléments que vous souhaitez localiser. Que ce soit un bouton, un textblock ou tout autre élément, votre objet peut avoir cet attribut et celui-ci va correspondre à la clé du fichier de ressource pour retourner une chaine de caractère spécifique.

Pour se faire commencez par ajouter un fichier de ressource à votre projet (Ajouter > nouveau élément > Fichier de ressource)

loc1

 

Puis dans ce fichier, ajoutez vos clés et la chaine traduite puis notez la spécificité des clés de la capture suivante :

loc2

Vous remarquerez que certaines clés finissent par .Text. C’est par ce suffixe que le moteur XAML va savoir, grâce à l’attribut x:Uid, aller chercher la clé puis utiliser le suffixe pour déterminer l’attribut de l’élément à localiser. Pour être plus clair, pour localiser le texte d’un label et d’un bouton comme ci-dessous

<TextBlock x:Uid=”Cle1/>

<Button x:Uid=”Cle2/>

 

Les clés devront s’appeler

Cle1.Text Texte de mon label
Cle2.Content Texte de mon bouton

Il est ainsi permis de localiser n’importe quel attribut d’un élément XAML. Par contre, cela demande une bonne organisation du fichier de ressource car si un label est utilisé à la fois sur un bouton et un label, du fait que l’attribut visé sera différent, le nom de la clé aussi et finalement l’information devra être dupliquée.

 

Le cas particulier de l’application bar

L’application bar est un cas particulier et je n’aurai personnellement jamais trouvé la solution sans notre évangéliste de choc David Catuhe. Voici les détails.

Un bouton d’AppBar XAML Metro ressemble au code suivant :

<Button x:Name="Add" Style="{StaticResource AddAppBarButtonStyle}" >
 

Avec un style qui redéfinit les propriétés d’automation pour définir le label du bouton (Automation.Name)

<UserControl.Resources>
...
    <Style x:Key="WebViewAppBarButtonStyle" TargetType="Button" 
           BasedOn="{StaticResource AppBarButtonStyle}">
        <Setter Property="AutomationProperties.Name" Value="Votre texte"/>
        <Setter Property="Content" Value="&#xE12B;"/>
    </Style>
</UserControl.Resources>

 

Sauf que, comme expliqué plus haut le mécanisme de localisation se base sur le nom de la clé, ce qui donnerait dans notre cas NotreCle.Automation.Name et là... c’est le drame. En effet, le moteur XAML n’apprécie pas du tout d’avoir deux fois un “.” dans la clé car il ne sait pas s’il doit chercher un attribut Name ou un attribut Automation.Name. Nous sommes bloqués.

Du fait des mécanismes internes concernant les propriétés d’automation notamment que le fait que la valeur de Automation.Name est automatiquement prise depuis la valeur de la propriété Content, il peut être tentant de passer par cette propriété mais dans ce cas, impossible d’utiliser la propriété Content pour afficher une image grâce à la police Segoe UI par exemple.  Encore bloqués.

La solution de se passer complètement d’un label n’étant pas une solution viable, il me fallait trouver autre chose tout en restant au maximum dans la mentalité du full XAML (sans code-behind). La solution se trouve simplement dans une syntaxe non documentée qui permet de localiser un champ non accessible normalement :

VotreCle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name
 

Ainsi dans notre cas et notre bouton about, la clé sera “AppBar-General-About.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name” tandis que le XAML ressemblera à ceci

<Button  x:Name="About" 
                Style="{StaticResource AboutAppBarButtonStyle}" 
                x:Uid="AppBar-General-About" 
               ...>
 </Button>

 

 

Quid des message box?

Contrairement aux Winforms où vous étiez obligés d’avoir une partie (notamment les boutons OK, Cancel, Oui, Non, etc.) liés non pas à la langue de votre application mais à celle du système, les applications Metro vont vous permettre de localiser le contenu de ces boutons mais surtout d’en rajouter autant que vous voulez en leur assignant des actions, simplement avec une classe de ce type :

public static class MessageBox
        {
            /// <summary>
            /// Display a error messagebox
            /// </summary>
            /// <param name="title"></param>
            /// <param name="message"></param>
            public static async Task<bool> Show(string title, string message)
            {
                MessageDialog dialog = new MessageDialog(message, title);
                bool result = false;
                dialog.Commands.Add(
                new UICommand(Localizer.GetKey("General-OK"), new UICommandInvokedHandler((cmd) => result = true)));
                dialog.Commands.Add(
                   new UICommand(Localizer.GetKey("General-Cancel"), new UICommandInvokedHandler((cmd) => result = false)));
                await dialog.ShowAsync();
                return result;
            }

            /// <summary>
            /// Display a error messagebox with additionnal commands
            /// </summary>
            /// <param name="title"></param>
            /// <param name="message"></param>
            public static async void Show(string title, string message, List<UICommand> commands, int defaultCommandIndex)
            {
                MessageDialog dialog = new MessageDialog(message, title);
                foreach (UICommand comm in commands)
                {
                    dialog.Commands.Add(comm);
                }

                if (defaultCommandIndex <= (commands.Count - 1))
                {
                    dialog.DefaultCommandIndex = (uint) defaultCommandIndex;
                }

                await dialog.ShowAsync();
            }
        }

 

Ainsi, il vous faudra donc user de ces trois techniques pour localiser complètement votre application mais vous pourrez ainsi permettre à l’utilisateur d’avoir une application complètement traduite dans la langue de son choix, même si son système ne gère pas cette langue.

 

Et pour le HTML5?

Et bien pour le HTML5, un mécanisme plus ou moins similaire a été mis en place et il devient très simple de localiser son application.

Je vous encourage également à lire l’article très clair de David Catuhe concernant ce sujet ou bien vous inspirer du sample du SDK concernant la localisation.

 

- Louis-Guillaume Morand -