Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

UWP et Localisation des Apps

La localisation d’une application est un passage obligé avant toute distribution sur le Store et même pour une application Lob dès lors que l’entreprise possède des antennes ailleurs qu’en France. Mais comment faire ?

Localisation et Globalisation

L’OS ne peut pas s’occuper de tout. Déjà il s’occupe de connaitre toutes les façons de formater les nombres ou les dates par exemple, et pour quasiment toutes cultures du monde ! C’est un travail énorme qu’il fallait faire à la main il n’y a pas si longtemps de cela…

Mais l’OS ne peut tout faire à la place du développeur. Globaliser une application – la rendre accessible au monde – ou  la localiser – point de vue inverse pour la même chose – la rendre utilisable dans un endroit donné, est ainsi un point de passage obligé dans de nombreux cas.

Cette tâche, loin d’être anodine, représente une charge et s’étend bien plus largement qu’on le suppose de prime abord. Souvent on s’imagine la localisation comme synonyme de “traduction”, or c’est une vision très limitée de ce que la tâche englobe.

Réfléchissez, un logiciel qui utilise des icônes, des images, ces dernières auront-elles le même sens dans toutes les cultures ? Un splashscreen sous forme de bitmap conçu avec du texte devra aussi être modifié. Une icône avec un “S”  stylisé pour “Sentence” devra devenir un “P” pour “phrase”, etc.

Ici le designer et l’infographiste auront encore du travail à fournir pour mettre à disposition des développeurs de nouvelles images adaptées à chaque culture…

Je ne parle pas des sons, des vidéos d’aide etc, tout cela réclamera souvent d’être produit à nouveau avec les coûts que cela représente !

Ainsi, localiser ou globaliser une application n’est pas une activité mineure ou annexe dévolue uniquement au développeur. C’est un travail énorme qui réclame sa propre planification, ses compétences propres et surtout son propre budget !

Localisation des UI XAML

Bien entendu je ne vais pas vous parler de la façon de tourner une vidéo d’aide en catalan ni de comment enregistrer en studio une bande son en japonais. Ce sont des sujets passionnants dont je pourrais vous parler mais restons dans ce qui est de notre domaine ici, l’application elle-même et donc forcément son UI. Et comme dans ce blog je ne parle que de C# et de XAML (ou de F# pour se détendre les neurones parfois) je vais me concentrer sur le problème principal dans ce contexte : localiser les UI XAML.

XAML est une merveille mais surtout c’est un moyen très pratique de décrire une UI puisque c’est du XML. On peut donc intervenir graphiquement ou textuellement pour produire ou modifier une UI. Certaines opérations se font plus facilement (ou obligatoirement) dans Blend, ce sont les opérations graphiques comme dessiner une courbe de Béziers pour templater le visuel d’un contrôle par exemple, d’autres sont plus rapides à faire en mode texte dans le code XAML lui-même, les exemples sont innombrables. Les deux méthodes sont même utilisables en même temps en partageant l’écran entre visuel et code que cela soit sous Blend ou Visual Studio.

Un autre point fort de XAML est son binding. Grâce à lui des patterns comme MVVM ont pu voir le jour et permettre de mieux concevoir le code et sa séparation avec l’UI justement. Ce binding est très large puisqu’il permet de lier à peu près n’importe quelle propriété d’un contrôle à une “source” qui elle aussi peut être de différentes natures. On binde des propriétés de dépendances à des propriétés de POCO, ses propriétés pouvant être des valeurs simples (chaînes, entiers…) ou des instances d’objets complexes.

On peut aussi par ce principe binder des Styles ou utiliser des ressources comme celle du thème courant notamment.

Le binding à une ressource est une des clés de la localisation que l’on passe par le mot clé “Binding” ou tout simplement qu’on lie une propriété à une source (comme l’URL du bitmap dans un contrôle Image par exemple).

Il existe des stratégies de nommage pour UWP comme on en trouve par exemple sous Android, c’est à dire des noms de répertoires et des modifications du nom des ressources pour indiquer quand elles doivent être chargées. Ainsi une image, qui est une ressource (ou un “Asset”), sera liée par son nom court dans le code XAML mais le chargement de la ressource pourra se faire dans un répertoire différent pour le français ou le chinois, avec des modifications de résolution ou d’échelles, tout cela étant indiqué dans le vrai nom du fichier existant ainsi en plusieurs versions, l’OS sachant charger le bon fichier selon le contexte.

Tous ces aspects de la localisation réclament une attention particulière et une bonne planification du travail.

Il reste encore un aspect, que je traite volontairement ici en dernier pour rappeler qu’il est loin d’être la seule et unique tâche d’une localisation : la traduction des UI.

Car c’est malgré tout l’une des tâches les plus importantes pour que l’utilisateur s’y retrouve, lui fournir une UI dans sa langue.

Comme je l’ai indiqué le respect de la culture implique souvent des modifications des images, voire des présentations ou du sens de l’écriture. Ces aspects là peuvent remettre en cause la conception visuelle d’une application et ne doivent en aucun cas être négligés. Il est même préférable de les prendre en compte dès le départ, à la base base du travail de design.

Mais en fin de course il existe toujours du texte… Et il faut bien le traduire et charger la bonne version selon la culture de l’utilisateur !

Celui qui s’est déjà lancé dans ce type d’opération sait à quel point cela est à la fois fastidieux et délicat. Là où aucun système ne peut rien pour vous hélas c’est la différence de longueur entre les mots et les phrases d’une langue à une autre. Ici seuls des tests intensifs et exhaustifs permettent de noter tous les problèmes puis de les corriger (ce qui renvoie à l’UI et sa conception donc au designer).

Comment relever tous ces défis sous UWP ?

Le multilingual toolkit

Maintenant que nous avons conscience de l’ampleur phénoménale de la tâche de localisation, essayons de résoudre les problèmes qu’elle pose un à un. Et commençons par la traduction de l’UI.

UWP est très bien conçu de ce point de vue, de façon proche de celle de WinRT. Si ce dernier n’a pas eu de succès et n’a pas été l’occasion de développer tous ses avantages j’espère qu’UWP par son originalité et ses atouts largement supérieurs saura convaincre plus de développeurs et que j’aurai l’occasion d’en parler plus longuement que son ainé (auquel j’ai tout de même consacré un tome de All.Dot.Blog en plus d’une collaboration au livre paru sur le sujet chez Eyrolles, ce qui n’est finalement pas si mal !).

Avec WinRT Microsoft s’est mis à proposer un outil de traduction issu de ses propres outils internes. Avec UWP on retrouve donc les mêmes outils, adaptés à ses spécificités.

Actuellement proposé en Beta le “multilingual toolkit” reste assez frustre visuellement (UI de type Windows Forms avec ruban) mais répond en revanche très bien au besoin.

Cette extension de Visual Studio permet de gérer la traductions des ressources, de créer des ressources pour des cultures ou des langues précises, de se reposer sur les outils Microsoft pour effectuer des traductions automatiques, pouvoir réviser ces traductions, etc. On peut être averti qu’un texte original déjà traduit à été modifié ce qui impose de vérifier les traductions, on peut filtrer les traductions effectuées en fonction de leur état (traduit, à réviser, etc), et plein d’autres possibilités que vous découvrirez seuls car en faire le catalogue ici ne serait pas très palpitant.

Ce que vous voulez savoir là maintenant, c’est comment on fait… “Il va la cracher sa pastille !” disent des petites voix derrière l’écran… Mais je vous entends vous savez et j’ai les noms ! Sourire

Multilingual App Toolkit

C’est son petit nom. Ok ce n’est pas essentiel mais pour le retrouver sur le web je vous assure que c’est pourtant une info de première qualité !

La description qu’en donne Microsoft est la suivante :

“Intégration à l’IDE Visual Studio. Ajoutez des fichiers de traduction à une solution de projet et gérez-les à l’aide des menus et boîtes de dialogue de Visual Studio.

Intégration des langages artificiels. Testez « en interne » les applications localisées, en identifiant les problèmes de localisation durant le développement. Ces problèmes peuvent inclure des chaînes codées en dur, concaténées ou tronquées, ainsi que d’autres problèmes visuels qui surviennent quand vous utilisez des langues différentes. Les traductions artificielles sont stockées dans des fichiers au format de localisation XLIFF. Vous pouvez les modifier comme n’importe quelle autre traduction. Vous gardez ainsi un contrôle minutieux sur le test des traductions artificielles.

Intégration au Portail linguistique Microsoft. Récupérez la terminologie et les traductions d’interface utilisateur tirées des produits Microsoft, grâce à l’API du service de terminologie Microsoft (connexion Internet active requise).

Intégration aux outils de traduction automatique. Profitez des suggestions grâce aux services Microsoft Translator intégrés (connexion Internet active requise). L’intégration aux services Microsoft Translator vous permet de visualiser et de tester instantanément plusieurs langues, sans l’aide d’un traducteur humain.

Importation et exportation de fichiers de traduction. Échangez des ressources avec des traducteurs ou un service de traduction, grâce à des fichiers XLIFF.

Éditeur de localisation dédié. Utilisez cet éditeur pour modifier simplement les chaînes traduites. Obtenez rapidement des traductions et des suggestions, grâce au portail linguistique Microsoft et les services Microsoft Translator (connexion Internet active requise). Vous pouvez aussi modifier rapidement les données des fichiers XLIFF en adaptant les traductions artificielles et réelles.

Logiciels requis : Visual Studio 2013, y compris les éditions Express, Visual Studio 2015, y compris les éditions Express”

Télécharger le kit

Une adresse : https://visualstudiogallery.msdn.microsoft.com/6dab9154-a7e1-46e4-bbfa-18b5e81df520

Cela se trouve donc dans la Visual Studio Gallery et c’est une extension à installer (fermer VS 2015 avant de lancer l’installation).

Toutefois depuis la version Beta (qui succède à Technical Preview) on peut plus facilement retrouver l’extension directement depuis Visual Studio via le menu Tools / extensions & updates puis “online”, et enfin chercher “multilingual app” :

image

Le principe reste le même, télécharger l’extension (un installable “msi”), fermer Visual Studio avant de l’installer.

Durant l’installation choisissez l’option “typical” (normalement par défaut).

image

Utiliser le toolkit

Pour utiliser le toolkit dans un projet il faut l’activer volontairement. Mais avant de le faire il faut suivre plusieurs étapes sinon le kit ne voudra pas s’activer…

Définir un langage par défaut

Avant de localiser un projet il faut qu’il possède une langue par défaut, celle qui sera choisie si aucune ressource n’existe pour la culture de l’utilisateur. C’est aussi un point de repère pour la localisation via le toolkit, la langue de référence.

Chacun à ses préférences et sa façon de faire, personnellement je conçois les logiciels devant être traduits en anglais. Le faire en allemand est assez intéressant pour la conception visuelle car les mots sont généralement longs ce qui impose des modifications de l’UI en général. Mais sinon l’anglais convient parfaitement sachant que le problème des longueurs de texte se posera inéluctablement et que seuls des tests bien menés permettront de lever tous les loups…

L’anglais comme langue par défaut est un choix intéressant tout bêtement parce que ma machine est en français… Et donc quand je conçois le logiciel je vois la version anglaise et quand j’exécute je vois immédiatement le français qui n’est qu’une traduction parmi d’autres et cela me permet sans bricolage de voir toute de suite ce qui ne va pas (en général des trucs oubliés, des liens de traduction qui ne marchent pas etc).

Si vous êtes à l’aise en italien en catalan ou en hongrois n’hésitez pas à choisir cette langue là par défaut, l’essentiel étant de ne pas prendre le français pour avoir ce différentiel intéressant entre conception et runtime. Et le fait que la première traduction soit le français permet très vite à œil (français) de voir les trucs qui clochent. Si votre langue maternelle est le javanais, procédez de la même façon en changeant le mot “français” par “javanais” dans ce qui précède (à condition que votre Windows soit configuré en javanais aussi ce qui fait partie de l’astuce)…

Le toolkit permet de gérer des langues “artificielles” comme de vraies langues. Ces fausses langues sont utilisées pour justement permettre de tester l’UI dans des configurations extrêmes (mots très longs ou très courts, etc). Personnellement la manière de faire que je viens d’expliquer me convient parfaitement sans utiliser de langues artificielles.

Bref, il faut donc commencer par définir le “default language” et cela se trouve dans le manifeste  (double clic sur celui-ci dans le projet et l’éditeur de manifeste s’affichera), onglet “Application” :

image

 

Définir le répertoire de base

Une fois la langue par défaut choisie vous devez créer à la main le début de la structure de répertoire permettant le stockage des ressources. Rien de compliqué, il faut créer un sous-répertoire dans le projet et l’appeler “Strings”, puis à l’intérieur de celui-ci un autre sous-répertoire nommé par le même sigle de culture que le choix par défaut effectué juste avant. Comment ici j’ai choisi “en-US” je créée un sous-répertoire “en-US” dans “Strings” donc.

image

Créer la première ressource

Ce n’est pas encore terminé. Il faut maintenant ajouter le premier fichier de ressource, celui de la langue par défaut, dans le répertoire créé à l’étape précédente. Clic droit sur le sous-répertoire puis ajouter un nouvel item et choisir d’ajouter un nouvel item fichier ressource en acceptant le nom par défaut (“Resources.resw”).

Activer le toolkit

Maintenant on peut activer le toolkit ! Si vous le faites avant cela ne marchera pas car il ne trouvera pas les éléments que nous venons de créer à la main. Le reste est très automatisé mais les premières étapes sont à deviner pour que ça marche.

Donc dans l’arbre de votre solution commencez par sélectionner le projet qui va être traduit. Il faut absolument que ce soit le bon et qu’il soit sélectionné. C’est tout bête mais sinon encore une fois ça ne marche pas.

Puis dans le menu tools / multilingual support  cliquez sur “enable selection” :

image

Dans la fenêtre de sortie de Visual Studio vous devriez voir passer des messages vous indiquant que le toolkit s’est bien activé.

Traduire l’UI

Une fois le toolkit installé et activé, on peut commencer le vrai travail…

Le principe

Le principe du Toolkit est le suivant : un fichier de ressource est créé pour la langue par défaut, c’est ce fichier qui va contenir les textes originaux assortis d’un nom de ressource. Dans l’UI XAML il faudra indiquer le même nom de ressource pour les objets traduits. La propriété affectée se distingue dans le nom de la ressource comme on l’écrirait en code C#, par exemple si je veux modifier la propriété Text d’un Textblock en appelant la ressource “ApplicationTitle” dans le fichier de ressource je donnerais en réalité le nom “ApplicationTitle.Text”. Mais dans le code XAML je repèrerais le TextBlock uniquement par “ApplicationTitle”. De cette façon le toolkit repère à la fois la ressource et le nom de la propriété. Ici c’est “Text” mais pour un bouton c’est “Content”, dans d’autres contrôle le nom peut être autre chose… C’est donc très souple.

On peut même utiliser le même identificateur de ressource dans plusieurs écrans par exemple le nom d’une entrée de menu peut être réutilisé pour le titre de la Vue qui correspond à cette dernière. Attention toutefois au nom de la propriété. On ne peut pas réutiliser “ApplicationTitle.Text” pour un bouton puisqu’il faudrait avoir défini “ApplicationTitle.Content” … si un bouton et un texte sont équivalent mieux vaut définir deux fois la valeur (donc doubler les traductions) ou bien créer une ressource avec un nom spécial et initialiser l’UI dans le constructeur de la Vue en allant chercher le texte par le chargeur de ressources (ce que nous verrons plus loin).

Comment s’effectue le repérage avec le nom de ressource au niveau du contrôle ? Le toolkit ne vient pas interférer avec les “x:Name” éventuellement déjà définis, heureusement. Il utilise une autre propriété “x:Uid”. Un contrôle peut donc déjà posséder un nom (x:Name) cela ne pose aucun problème de lui ajouter un x:Uid=”ApplicationTitle” pour suivre le même exemple.

Ensuite tout est automatique et Windows chargera les bonnes traductions selon le contexte…

x:Uid

Dans le code XAML il faut donc créer des x:Uid pour les objets localisés. Ce marqueur sera utilisé au chargement pour obtenir automatiquement la chaîne traduite. Et grâce au nom de propriété ajouté à ce nom dans le nom de la ressource le système saura laquelle il doit modifier (Text ou Content par exemple).

Le texte ou contenu du contrôle peut ainsi être laissé vide, il sera rempli à l’exécution mais je vous conseille de laisser quelque chose écrit dans la langue par défaut, pour la mise en page c’est bien plus pratique que des textes vides, invisibles par essence …

Donc on modifie par ici un TextBlock pour lui donner un x:Uid unique (la valeur “MenuWords” ci-dessous) :

image

La ressource

Le code XAML est maintenant modifié et le contrôle à localiser possède un x:Uid. Encore faut-il que cela corresponde à une ressource de même nom dans le fichier ressource que nous avons créé au début (celui de la langue par défaut).

Ouvrez ainsi le fichier de ressources de la langue par défaut et créer l’entrée correspondant à l’id ajouté dans le code XAML :

image

Ci-dessus on voit le fichier de ressources ouvert ainsi que sa première ligne portant dans la colonne “name” le x:Uid donné au TextBlock suivi d’un point et du nom de la propriété (Text). La valeurs est “Words”.

Répétez ces opérations pour toutes les ressources à localiser… Création d’un x:Uid dans le code XAML, création d’une ligne dans le fichier des ressources, etc.

Traduire

Une fois qu’on a traité toutes les propriétés de tous les contrôles devant être localisées il faut passer à l’étape de la traduction.

Ajouter une langue

Avant de traduire il faudrait savoir dans quelle langue… Pour l’instant et selon ma façon de procéder, le logiciel a été conçu en anglais et la langue par défaut est “en-US”, c’est aussi cette même langue qui a été utilisée pour créer le premier fichier de ressource qui deviendra la référence.

Je vais donc naturellement ajouter le français comme première cible.

Pour ce faire un clic droit sur le fichier de ressources original ce qui ouvre le menu contextuel dans lequel on choisit “add translation language” :

image

un dialogue s’affiche offrant toutes les cultures et langues possibles, il suffit de cocher la ou les langues à ajouter :

 

image

 

Une fois le dialogue validé le toolkit va ajouter des sous-répertoires à l’embryon d’arborescence que nous avons créé au début. Ici j’ai choisi le français (globalement et pas des variantes comme fr-FR sinon il faudra traduire chacune de ces variantes !) :

image

Le nouveau répertoire est “fr” et il contient aussi un fichier “Resources.resw”.

Mais un autre sous-répertoire de l’application a aussi été créé, c’est “MultilingualResources”. Et il contient un fichier très important s’appelant du nom du projet suivi de celui de la culture avec l’extension “xfl” :

image

Ce fichier est important car c’est là que le toolkit gère toutes les informations sur l’état de la traduction de chaque langue, informations qui ne pourraient être stockées dans les fichiers de ressources qui n’ont pas été conçus pour un tel usage. Au final ce sont bien ces fichiers ressources qui seront utilisés mais pour pister les différentes phases de traduction il faut une structure plus complexe.

Traduire automatiquement

Le plus facile pour remplir le ou les nouveaux fichiers de ressources ajoutés est de demander une traduction automatique. Dans le meilleur des cas on possède une bonne moitié déjà remplie, et dans le pire on évite d’avoir à tout recopier notamment le nom de chaque bouton ou autres… Le toolkit est vraiment bien fait et il n’efface pas les traductions déjà validées, on ne risque donc rien à relancer ce processus dès qu’on ajoute des choses dans le fichier original.

Faire un clic droit sur le fichier XLF et demander la traduction machine :

image

 

Editer les traductions

Désormais il ne reste plus qu’à vérifier les traductions automatiques, à corriger ce qui doit l’être et l’affaire sera terminée…

Double clic sur le fichier XLF et un autre monde apparait !

image

(cliquez pour une image à 100%)

On voit chaque élément (ici un seul), le sens de la traduction (tout en bas) et l’état de la traduction (automatique ou non, traduit ou pas, symbole “à réviser”, etc).

C’est un peu vieillot comme interface, mais on s’y retrouve et ça fait le job.

Si on clique sur le bouton Suggérer dans la barre d’outil on voit apparaitre des tas de propositions avec un indice de confiance selon la provenance et la probabilité que le choix soit correct. Tout l’intérêt est que ces fonctions utilisent les bases de données Microsoft de localisation ainsi que Microsoft Translator. On peut ainsi choisir ce qui va le mieux et accepter ou non une suggestion… Mais ne vous fiez pas aux apparences, toute traduction automatique est un piège, seul un humain peut comprendre certaines choses. Ne vous lancez pas dans le support d’une langue étrangère si vous ne la parlez pas parfaitement, sinon votre logiciel ressemblera aux notices en français des trucs fabriqués en chine…

Bien entendu ne pas oublier de faire un Save (commande de la barre en haut à droite) puis un build du projet…

Si on exécute et puisque je n’ai traduit ici qu’un seul TextBlock et que je suis une machine en français, seul le mot “Mots” à la place de “Words” sera remplacé.

Vous me croyez sur parole et vous avez raison… Car pour voir plus le mieux sera de tester vous mêmes !

L’intraduisible automatiquement

Tout cela est bien joli mais il y a aussi des textes utilisés par le code, dans les ViewModels ou même les Models comme par exemple des exceptions, des messages d’erreur ou de confirmation, des informations composées (type String.Format) etc…

Il y a donc un besoin absolu de pouvoir aussi utiliser des ressources traduites dans le code pour tout ce qui ne peut pas être automatiquement lié à une ressource.

Heureusement cela aussi est parfaitement prévu et très simple à faire.

Il faut d’abord créer une instance du chargeur de ressources puis simplement appeler sa méthode GetString() en lui donnant le nom de la ressource :

var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var name = $"{Uid}{value}";
tb.Text = loader.GetString(name);

On notera que le nom de la ressource peut bien entendu être n’importe quoi, aucune norme à suivre, aucun nom de propriété à ajouter etc. Si je veux une ressource “AffichageHeure” je la créée sous ce nom là si je le veux, tant qu’aucune autre du même nom n’existe.

Dans l’exemple ci-dessus le nom est fabriquée en suivant la norme vue jusqu’ici (Id plus nom de propriété). C’est un exemple qui montre comment récupérer une ressource existante dont le nom est déjà constitué des deux parties, mais un nom unique pour une ressource uniquement utilisée par code est tout aussi valable bien entendu.

Dernière chose à noter, la syntaxe curieuse de la variable “name”… C’est du C# 6, une notation très pratique qui remplace avantageusement String.Format en utilisant des expressions entre crochets. Mais bon ce n’est pas le sujet, juste pour le faire voir si vous ne connaissez pas…

Un peu de lecture

Je dis plein de choses mais il en reste plein d’autres à savoir sur le sujet !

Je vous ai préparé une petite liste pour vous occuper après la lecture de Dot.Blog :

Conclusion

Le sujet est vaste, et on pourrait en parler des heures mais la petite liste de lecture ci-dessus devrait calmer votre soif de savoir !

Le plus difficile ici est de savoir par où commencer et comment procéder… Un peut comme avec SQLite dont j’ai parlé. Ce n’est pas bien difficile une fois qu’on sait, encore faut-il avoir la bonne information pour bien commencer au bon endroit.

Mais vous avez la chance de lire Dot.Blog !

Stay Tuned !

Faites des heureux, partagez l'article !
blog comments powered by Disqus