Partager via


Ajout de la prise en charge de l’interface utilisateur multilingue à une application

Ce tutoriel décrit comment adapter une application unilingue à un environnement international. Cette application se présente sous la forme d’une solution complète développée dans Microsoft Visual Studio.

Vue d’ensemble

Depuis Windows Vista, le système d’exploitation Windows a été entièrement conçu pour être une plateforme multilingue, avec une prise en charge supplémentaire qui vous permet de créer des applications multilingues utilisant le modèle de ressources MUI de Windows.

Ce tutoriel illustre cette nouvelle prise en charge des applications multilingues en couvrant les aspects suivants :

  • Utilise la prise en charge améliorée de la plateforme MUI pour activer facilement les applications multilingues.
  • Étend l’utilisation des applications multilingues aux versions de Windows antérieures à Windows Vista.
  • Aborde les considérations supplémentaires relatives au développement d’applications multilingues spécialisées, telles que les applications console.

Ces liens permettent de rafraîchir rapidement les notions d’internationalisation et de MUI :

L’idée derrière Hello MUI

Vous êtes probablement familiarisé avec l’application Hello World classique, qui illustre les concepts de programmation de base. Ce tutoriel adopte une approche similaire pour illustrer comment utiliser le modèle de ressources MUI pour mettre à jour une application unilingue et créer une version multilingue appelée Hello MUI.

Remarque

Les tâches de ce tutoriel sont décrites en étapes détaillées en raison de la précision avec laquelle ces activités doivent être effectuées et de la nécessité d’expliquer les détails aux développeurs qui ont peu d’expérience de ces tâches.

 

Configuration de la solution Hello MUI

Ces étapes décrivent comment préparer la création de la solution Hello MUI.

Conditions requises par la plateforme

Vous devez compiler les exemples de code de ce tutoriel en utilisant le Kit de développement logiciel (Kit SDK Windows) pour Windows 7 et Visual Studio 2008. Le Kit SDK Windows 7 s’installe sur Windows XP, Windows Vista et Windows 7, et l’exemple de solution peut être développé sur toutes les versions de ces systèmes d’exploitation.

Tous les exemples de code de ce tutoriel sont conçus pour être exécutés sur les versions x86 et x64 de Windows XP, Windows Vista et Windows 7. Windows 7. Les parties spécifiques qui ne fonctionneront pas sous Windows XP sont signalées le cas échéant.

Prérequis

  1. Installer Visual Studio 2008.

    Pour plus d’informations, consultez le Centre de développement Visual Studio.

  2. Installez le Kit SDK Windows pour Windows 7.

    Vous pouvez l’installer à partir de la page Windows SDK du Centre de développement Windows. Le Kit de développement logiciel (SDK) inclut des utilitaires qui permettent de développer des applications pour les versions du système d’exploitation allant de Windows XP à la version la plus récente.

    Remarque

    Si vous n’installez pas le package à l’emplacement par défaut, ou si vous ne l’installez pas sur le lecteur système, généralement le lecteur C, prenez note du chemin d’installation.

     

  3. Configurez les paramètres de ligne de commande de Visual Studio.

    1. Ouvrez une invite de commandes Visual Studio.
    2. Tapez définir le chemin d’accès.
    3. Confirmez que la variable de chemin d’accès inclut le chemin du dossier bin du Kit SDK Windows 7 : ...Microsoft SDKs\Windows\v7.0\bin
  4. Installez le package de composant additionnel des API de niveau inférieur Microsoft NLS.

    Remarque

    Dans le contexte de ce tutoriel, ce package est nécessaire uniquement si vous personnalisez l’application pour qu’elle s’exécute sur les versions de Windows antérieures à Windows Vista. Consultez Étape 5 : Personnalisation de« Hello MUI ».

    1. Téléchargez et installez le package, qui n’est plus disponible à partir du Centre de téléchargement Microsoft. Utilisez les API de globalisation ICU sur la mise à jour Windows 10 de mai 2019 et les versions ultérieures.

    2. Comme pour le Kit SDK Windows, si vous n’installez pas le package à l’emplacement par défaut, ou si vous ne l’installez pas sur le lecteur système, généralement le lecteur C, prenez note du chemin d’installation.

    3. Si votre plateforme de développement est Windows XP ou Windows Server 2003, vérifiez que Nlsdl.dll est installé et inscrit correctement.

      1. Accédez au dossier « redist » sous l’emplacement du chemin d’installation.
      2. Exécutez le fichier Nlsdl.*.exe redistribuable approprié, tel que nlsdl.x86.exe. Cette étape permet d’installer et d’enregistrer Nlsdl.dll.

    Remarque

    Si vous développez une application qui utilise MUI et qui doit s’exécuter sur les versions de Windows antérieures à Windows Vista, Nlsdl.dll doit être présente sur la plateforme Windows de destination. Dans la plupart des cas, cela signifie que l’application doit transporter et installer le programme d’installation de Nlsdl redistribuable (et non pas simplement copier Nlsdl.dll lui-même).

     

Étape 0 : Création du MUI Hello codé en dur

Ce tutoriel commence par la version unilingue de l’application Hello MUI. L’application suppose l’utilisation du langage de programmation C++, de chaînes de caractères larges et de la fonction MessageBoxW pour la sortie.

Commencez par créer l’application initiale GuiStep_0, ainsi que la solution HelloMUI, qui contient toutes les applications de ce tutoriel.

  1. Créez un projet dans Visual Studio 2008. Utilisez les paramètres et valeurs suivants :

    1. Type de projet : Sous Visual C++, sélectionnez Win32, puis sous les modèles installés par Visual Studio, sélectionnez Projet Win32.
    2. Nom : GuiStep_0.
    3. Emplacement : ProjectRootDirectory (les étapes suivantes font référence à ce répertoire).
    4. Nom de la solution : HelloMUI.
    5. Sélectionnez Créer un répertoire pour la solution.
    6. Dans l’Assistant Application Win32, sélectionnez le type d’application par défaut : Application Windows.
  2. Configurez le modèle de threading du projet :

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet GuiStep_0, puis sélectionnez Propriétés.

    2. Dans la boîte de dialogue Pages de propriétés du projet :

      1. Dans le menu déroulant en haut à gauche, réglez Configuration sur Toutes les configurations.
      2. Sous Propriétés de configuration, développez C/C++, sélectionnez Génération de code et définissez Bibliothèque d’exécution : Débogage multithread (/MTd).
  3. Remplacez le contenu de GuiStep_0.cpp par le code suivant :

    // GuiStep_0.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_0.h"
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        MessageBoxW(NULL,L"Hello MUI",L"HelloMUI!",MB_OK | MB_ICONINFORMATION);
        return 0;
    }
    
  4. Créer et exécuter l’application.

Le code source simple ci-dessus fait le choix de conception simplifiée de coder en dur, ou d’intégrer, tous les résultats que l’utilisateur verra : dans ce cas, le texte « Hello MUI ». Ce choix limite l’utilité de l’application pour les utilisateurs qui ne reconnaissent pas le mot anglais « Hello ». Étant donné que MUI est un acronyme anglais basé sur la technologie, on suppose dans ce tutoriel que la chaîne reste « MUI » pour toutes les langues.

Étape 1 : Implémentation du module de ressources de base

Microsoft Win32 permet depuis longtemps aux développeurs d’applications de séparer les données des ressources de l’interface utilisateur du code source de l’application. Cette séparation se présente sous la forme du modèle de ressources Win32, dans lequel les chaînes de caractères, les bitmaps, les icônes, les messages et autres éléments normalement affichés à l’intention de l’utilisateur sont empaquetés dans une section distincte de l’exécutable, séparée du code exécutable.

Pour illustrer cette séparation entre le code exécutable et l’empaquetage des données de ressources, dans cette étape, le tutoriel place la chaîne « Hello » précédemment codée en dur (la ressource « en-US ») dans la section des ressources d’un module DLL dans le projet HelloModule_en_us.

Cette DLL Win32 peut également contenir des fonctionnalités exécutables de type bibliothèque (comme toute autre DLL). Mais pour nous concentrer sur les aspects liés aux ressources Win32, nous laissons le code de la DLL d’exécution dans le fichier dllmain.cpp. Les sections suivantes de ce tutoriel utilisent les données de ressources de HelloModule générées ici et présentent également le code d’exécution approprié.

Pour construire un module de ressources Win32, commencez par créer une DLL dotée d’un dllmain :

  1. Ajoutez un nouveau projet à la solution HelloMUI :

    1. Dans le menu Fichier, sélectionnez Ajouter, puis Nouveau projet.
    2. Type de projet : Projet Win32.
    3. Nom : HelloModule_en_us.
    4. Emplacement : ProjectRootDirectory\HelloMUI.
    5. Dans l’Assistant Application Win32, sélectionnez Type d’application : DLL.
  2. Configurez le modèle de threading de ce projet :

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet HelloModule_en_us, puis sélectionnez Propriétés.

    2. Dans la boîte de dialogue Pages de propriétés du projet :

      1. Dans le menu déroulant en haut à gauche, réglez Configuration sur Toutes les configurations.
      2. Sous Propriétés de configuration, développez C/C++, sélectionnez Génération de code et définissez Bibliothèque d’exécution : Débogage multithread (/MTd).
  3. Examinez le fichier dllmain.cpp. (Vous pouvez ajouter les macros UNREFERENCED_PARAMETER au code généré, comme nous l’avons fait ici, pour lui permettre de compiler au niveau d’avertissement 4).

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        UNREFERENCED_PARAMETER(hModule);
        UNREFERENCED_PARAMETER(lpReserved);
    
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
  4. Ajouter un fichier de définition des ressources HelloModule.rc au projet :

    1. Sous le projet HelloModule_en_us, cliquez avec le bouton droit sur le dossier Fichiers de ressources et sélectionnez Ajouter, puis Nouvel élément.

    2. Dans la boîte de dialogue Ajouter un nouvel élément, sélectionnez les éléments suivants :

      1. Catégories : Sous Visual C++, sélectionnez Ressource, puis sous « Modèles installés par Visual Studio », sélectionnez Fichier de ressources (.rc).
      2. Nom : HelloModule.
      3. Emplacement : Acceptez la valeur par défaut.
      4. Cliquez sur Ajouter.
    3. Spécifier que le nouveau fichier HelloModule.rc doit être sauvegardé en Unicode :

      1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur HelloModule.rc, puis sélectionnez Afficher le code.
      2. Si un message vous indique que le fichier est déjà ouvert et vous demande si vous souhaitez le fermer, cliquez sur Oui.
      3. Le fichier étant affiché sous forme de texte, sélectionnez le menu Fichier, puis Options d’enregistrement avancées.
      4. Sous Encodage, spécifiez Unicode - Codepage 1200.
      5. Cliquez sur OK.
      6. Enregistrez et fermez HelloModule.rc.
    4. Ajoutez une table de chaînes contenant la chaîne « Hello » :

      1. Dans Affichage des ressources, cliquez avec le bouton droit sur HelloModule.rc, puis sélectionnez Ajouter une ressource.

      2. Sélectionnez Table de chaînes.

      3. Cliquez sur Nouveau.

      4. Ajoutez une chaîne à la Table de chaînes :

        1. ID: HELLO_MUI_STR_0.
        2. Valeur : 0.
        3. Légende : Hello.

      Si vous affichez HelloModule.rc sous forme de texte, vous verrez différents éléments de code source spécifique à la ressource. La section la plus intéressante est celle qui décrit la chaîne « Hello » :

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "Hello"
      END
      

      Cette chaîne « Hello » est la ressource qui doit être localisée (c’est-à-dire traduite) dans chaque langue que l’application souhaite prendre en charge. Par exemple, le projet HelloModule_ta_in (que vous allez créer à l’étape suivante) contiendra sa propre version localisée de HelloModule.rc pour « ta-IN » :

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "வணக்கம்"
      END
      
    5. Générez le projet HelloModule_en_us pour vous assurer qu’il n’y a aucune erreur.

  5. Créez six projets supplémentaires dans la solution HelloMUI (ou autant que vous le souhaitez) pour créer six DLL de ressources supplémentaires pour d’autres langues. Utilisez les valeurs de ce tableau :

    Nom du projet Nom du fichier .rc Identificateur de chaîne Valeur de chaîne Chaîne légende
    HelloModule_de_de HelloModule HELLO_MUI_STR_0 0 Hallo
    HelloModule_es_es HelloModule HELLO_MUI_STR_0 0 Hola
    HelloModule_fr_fr HelloModule HELLO_MUI_STR_0 0 Bonjour
    HelloModule_hi_in HelloModule HELLO_MUI_STR_0 0 नमस्
    HelloModule_ru_ru HelloModule HELLO_MUI_STR_0 0 Здравствуйте
    HelloModule_ta_in HelloModule HELLO_MUI_STR_0 0 வணக்கம்

     

Pour plus d’informations sur la structure et la syntaxe des fichiers .rc, consultez À propos des fichiers de ressources.

Étape 2 : Génération du module de ressources de base

Si vous utilisez les modèles de ressources précédents, la création de l’un des sept projets HelloModule entraînerait la création de sept DLL distinctes. Chaque DLL comportera une section de ressources contenant une seule chaîne de caractères localisée dans la langue appropriée. Bien qu’elle soit appropriée pour le modèle historique de ressources Win32, cette conception ne tire pas profit de MUI.

Dans le Kit SDK Windows Vista et les versions ultérieures, MUI permet de fractionner les exécutables en code source et en modules de contenu localisable. Avec la personnalisation supplémentaire décrite plus en détail à l’étape 5, les applications peuvent être activées pour la prise en charge multilingue afin de fonctionner sur les versions antérieures à Windows Vista.

Les mécanismes principaux disponibles pour fractionner les ressources du code exécutable, à partir de Windows Vista, sont les suivants :

  • Utilisation de rc.exe (compilateur RC) avec des options spécifiques, ou
  • Utilisation d’un outil de fractionnement de style post-build nommé muirct.exe.

Pour plus d’informations, consultez Utilitaires de ressources.

Pour des raisons de simplicité, ce tutoriel utilise muirct.exe pour fractionner l’exécutable « Hello MUI ».

Fractionnement des différents modules de ressources linguistiques : Une vue d’ensemble

Un processus en plusieurs parties est utilisé pour le fractionnement des DLL en un fichier exécutable HelloModule.dll, ainsi qu’un fichier HelloModule.dll.mui pour chacune des sept langues prises en charge dans le cadre de ce tutoriel. Cette vue d’ensemble décrit les étapes nécessaires ; la section suivante présente un fichier de commandes qui exécute ces étapes.

Tout d’abord, fractionnez le module HelloModule.dll en anglais uniquement à l’aide de la commande :

mkdir .\en-US
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui

La ligne de commande ci-dessus utilise le fichier de configuration DoReverseMuiLoc.rcconfig. Ce type de fichier de configuration est généralement utilisé par muirct.exe pour fractionner les ressources entre la DLL indépendante de la langue (LN) et les fichiers .mui qui dépendent de la langue. Dans ce cas, le fichier xml DoReverseMuiLoc.rcconfig (répertorié dans la section suivante) indique de nombreux types de ressources, mais ils appartiennent tous à la catégorie de fichiers « localizedResources » ou .mui, et il n’y a pas de ressources dans la catégorie indépendante de la langue. Pour plus d’informations sur la préparation d’un fichier de configuration des ressources, consultez Préparation d’un fichier de configuration de ressource.

En plus de la création d’un fichier HelloModule.dll.mui qui contient la chaîne de caractères anglaise "Hello", muirct.exe intègre également une ressource MUI dans le module HelloModule.dll pendant le fractionnement. Afin de charger correctement les ressources appropriées des modules HelloModule.dll.mui spécifiques à la langue lors de l’exécution, chaque fichier .mui doit avoir ses sommes de contrôle corrigées pour correspondre aux sommes de contrôle du module LN indépendant de la langue de base. Cette opération s’effectue à l’aide d’une commande telle que :

muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui

De même, muirct.exe est appelé pour créer un fichier HelloModule.dll.mui pour chacune des autres langues. Toutefois, dans ce cas, la DLL indépendante de la langue est supprimée, car seule la première DLL créée sera nécessaire. Les commandes qui traitent les ressources espagnoles et françaises se présentent de la manière suivante :

mkdir .\es-ES
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui

mkdir .\fr-FR
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui

Parmi les éléments les plus importants à noter dans les lignes de commande muirct.exe ci-dessus, l’indicateur -x est utilisé pour spécifier un ID de langue cible. La valeur fournie à muirct.exe est différente pour chaque module HelloModule.dll spécifique à une langue. Cette valeur linguistique est centrale et est utilisée par muirct.exe pour marquer correctement le fichier .mui pendant le fractionnement. Une valeur incorrecte entraîne des échecs de chargement des ressources pour ce fichier .mui particulier au moment de l’exécution. Pour plus de détails sur la correspondance entre le nom de la langue et le LCID, consultez Constantes et chaînes d’identification de langue.

Chaque fichier .mui fractionné finit par être placé dans un répertoire correspondant à son nom de langue, directement sous le répertoire dans lequel se trouve le fichier HelloModule.dll, qui est indépendant de la langue. Pour plus d’informations sur le placement des fichiers .mui, consultez Déploiement des applications.

Fractionnement des différents modules de ressources linguistiques : Création des fichiers

Pour ce tutoriel, vous créez un fichier de commandes contenant les commandes pour fractionner les différentes DLL et vous l’appelez manuellement. Notez que dans le cadre d’un développement réel, vous pourriez réduire le risque d’erreurs de compilation en incluant ces commandes dans les événements pré-génération ou post-génération de la solution HelloMUI, mais cela dépasse les sujets couverts par ce tutoriel.

Créez les fichiers de la build de débogage :

  1. Créez un fichier de commandes DoReverseMuiLoc.cmd contenant les commandes suivantes :

    mkdir .\en-US
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
    
    mkdir .\de-DE
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0407 -g 0x0407 .\HelloModule_de_de.dll .\HelloModule_discard.dll .\de-DE\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e de-DE\HelloModule.dll.mui
    
    mkdir .\es-ES
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui
    
    mkdir .\fr-FR
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui
    
    mkdir .\hi-IN
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0439 -g 0x0407 .\HelloModule_hi_in.dll .\HelloModule_discard.dll .\hi-IN\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e hi-IN\HelloModule.dll.mui
    
    mkdir .\ru-RU
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0419 -g 0x0407 .\HelloModule_ru_ru.dll .\HelloModule_discard.dll .\ru-RU\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e ru-RU\HelloModule.dll.mui
    
    mkdir .\ta-IN
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0449 -g 0x0407 .\HelloModule_ta_in.dll .\HelloModule_discard.dll .\ta-IN\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e ta-IN\HelloModule.dll.mui
    pause
    
  2. Créer un fichier XML DoReverseMuiLoc.rcconfig contenant les lignes suivantes :

    <?xml version="1.0" encoding="utf-8"?>
        <localization>
            <resources>
                <win32Resources fileType="Application">
                    <neutralResources>
                    </neutralResources>
                    <localizedResources>
                        <resourceType typeNameId="#1"/>
                        <resourceType typeNameId="#10"/>
                        <resourceType typeNameId="#1024"/>
                        <resourceType typeNameId="#11"/>
                        <resourceType typeNameId="#12"/>
                        <resourceType typeNameId="#13"/>
                        <resourceType typeNameId="#14"/>
                        <resourceType typeNameId="#15"/>
                        <resourceType typeNameId="#16"/>
                        <resourceType typeNameId="#17"/>
                        <resourceType typeNameId="#18"/>
                        <resourceType typeNameId="#19"/>
                        <resourceType typeNameId="#2"/>
                        <resourceType typeNameId="#20"/>
                        <resourceType typeNameId="#2110"/>
                        <resourceType typeNameId="#23"/>
                        <resourceType typeNameId="#240"/>
                        <resourceType typeNameId="#3"/>
                        <resourceType typeNameId="#4"/>
                        <resourceType typeNameId="#5"/>
                        <resourceType typeNameId="#6"/>
                        <resourceType typeNameId="#7"/>
                        <resourceType typeNameId="#8"/>
                        <resourceType typeNameId="#9"/>
                        <resourceType typeNameId="HTML"/>
                        <resourceType typeNameId="MOFDATA"/>
                    </localizedResources>
                </win32Resources>
            </resources>
        </localization>
    
  3. Copiez DoReverseMuiLoc.cmd et DoReverseMuiLoc.rcconfig dans ProjectRootDirectory\HelloMUI\Debug.

  4. Ouvrez l’invite de commande de Visual Studio 2008 et accédez au répertoire de débogage (Debug).

  5. Exécutez DoReverseMuiLoc.cmd.

Lorsque vous créez une build de mise en production, vous copiez les mêmes fichiers DoReverseMuiLoc.cmd et DoReverseMuiLoc.rcconfig dans le répertoire Release et vous exécutez le fichier de commande à cet endroit.

Étape 3 : Création d’une ressource « Hello MUI » adaptée aux besoins des utilisateurs

En partant de l’exemple initial GuiStep_0.exe codé en dur, vous pouvez étendre la portée de l’application à des utilisateurs multilingues en choisissant d’incorporer le modèle de ressources Win32. Le nouveau code d’exécution présenté dans cette étape comprend la logique de chargement du modulee (LoadLibraryEx) et de récupération de la chaîne de caractères (LoadString).

  1. Ajoutez un nouveau projet à la solution HelloMUI (en utilisant la sélection de menu Fichier, Ajouter, et Nouveau projet) avec les paramètres et les valeurs suivants :

    1. Type de projet : Projet Win32.
    2. Nom : GuiStep_1.
    3. Emplacement : Acceptez la valeur par défaut.
    4. Dans l’Assistant Application Win32, sélectionnez le type d’application par défaut : Application Windows.
  2. Définissez ce projet pour qu’il s’exécute à partir de Visual Studio et configurez son modèle de threading :

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet GuiStep_1 et sélectionnez Définir comme projet de démarrage.

    2. Cliquez à nouveau avec le bouton droit et sélectionnez Propriétés.

    3. Dans la boîte de dialogue Pages de propriétés du projet :

      1. Dans le menu déroulant en haut à gauche, réglez Configuration sur Toutes les configurations.
      2. Sous Propriétés de configuration, développez C/C++, sélectionnez Génération de code et définissez Bibliothèque d’exécution : Débogage multithread (/MTd).
  3. Remplacez le contenu de GuiStep_1.cpp par le code suivant :

    // GuiStep_1.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_1.h"
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Basic application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx
        // LoadLibraryEx is the preferred alternative for resource modules as used below because it
        // provides increased security and performance over that of LoadLibrary
        HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 2. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 3. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 4. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeLibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
  4. Créer et exécuter l’application. La sortie s’affiche dans la langue actuellement définie comme langue d’affichage de l’ordinateur (à condition qu’il s’agisse de l’une des sept langues que nous avons créées).

Étape 4 : Globalisation de « Hello MUI »

Bien que l’exemple précédent permette d’afficher les résultats dans différentes langues, certains aspects ne sont pas pris en compte. La principale différence est peut-être que l’application n’est disponible que dans un petit sous-ensemble de langues par rapport au système d’exploitation Windows lui-même. Si, par exemple, l’application GuiStep_1 de l’étape précédente était installée sur une buid japonaise de Windows, il est probable que les emplacements des ressources ne soient pas corrects.

Pour remédier à cette situation, deux options principales sont envisageables :

  • Veiller à ce que les ressources dans une langue de base par défaut prédéterminée soient bien incluses.
  • Permettez à l’utilisateur final de configurer ses préférences linguistiques parmi le sous-ensemble de langues spécifiquement prises en charge par l’application.

Parmi ces options, celle qui fournit un langage de base par défaut est fortement conseillée et est implémentée dans ce tutoriel en passant l’option -g à muirct.exe, comme indiqué ci-dessus. Cet indicateur signale à muirct.exe de faire d’une langue spécifique (de-DE / 0x0407) la langue de base par défaut associée au module dll indépendant de la langue (HelloModule.dll). Lors de l’exécution, ce paramètre est utilisé en dernier recours pour générer le texte à afficher à l’utilisateur. Dans le cas où une langue de base par défaut n’est pas trouvée et qu’aucune ressource appropriée n’est disponible dans le binaire indépendant de la langue, le chargement de la ressource échoue. Il convient donc de déterminer avec soin les scénarios auxquels votre application pourrait être confrontée et de planifier en conséquence le langage de base par défaut.

L’autre option, qui permet de configurer les préférences linguistiques et de charger les ressources en fonction de cette hiérarchie définie par l’utilisateur, peut accroître considérablement la satisfaction du client. Malheureusement, cela complique également la fonctionnalité nécessaire au sein de l’application.

Cette étape du tutoriel utilise un mécanisme de fichier texte simplifié pour permettre la configuration de la langue de l’utilisateur. Le fichier texte est analysé au moment de l’exécution par l’application, et la liste de langues analysée et validée est utilisée pour établir une liste de substitution personnalisée. Une fois la liste de substitution personnalisée établie, les API Windows chargeront les ressources en fonction de la priorité linguistique établie dans cette liste. Les autres éléments du code sont similaires à ceux de l’étape précédente.

  1. Ajoutez un nouveau projet à la solution HelloMUI (en utilisant la sélection de menu Fichier, Ajouter, et Nouveau projet) avec les paramètres et les valeurs suivants :

    1. Type de projet : Projet Win32.
    2. Nom : GuiStep_2.
    3. Emplacement : Acceptez la valeur par défaut.
    4. Dans l’Assistant Application Win32, sélectionnez le type d’application par défaut : Application Windows.
  2. Définissez ce projet pour qu’il s’exécute à partir de Visual Studio et configurez son modèle de threading :

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet GuiStep_2 et sélectionnez Définir comme projet de démarrage.

    2. Cliquez à nouveau avec le bouton droit et sélectionnez Propriétés.

    3. Dans la boîte de dialogue Pages de propriétés du projet :

      1. Dans le menu déroulant en haut à gauche, réglez Configuration sur Toutes les configurations.
      2. Sous Propriétés de configuration, développez C/C++, sélectionnez Génération de code et définissez Bibliothèque d’exécution : Débogage multithread (/MTd).
  3. Remplacez le contenu de GuiStep_2.cpp par le code suivant :

    // GuiStep_2.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_2.h"
    #include <strsafe.h>
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize);
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Application starts by applying any user defined language preferences
        // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback)
        // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.)
        WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2];
        if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format
        WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER];
        if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1c. Application now sets the appropriate fallback list
        DWORD langCount = 0;
        // next commented out line of code could be used on Windows 7 and later
        // using SetProcessPreferredUILanguages is recomended for new applications (esp. multi-threaded applications)
    //    if(!SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0)
        // the following line of code is supported on Windows Vista and later
        if(!SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to set the user defined languages, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // NOTES on step #1:
        // an application developer that makes the assumption the fallback list provided by the
        // system / OS is entirely sufficient may or may not be making a good assumption based 
        // mostly on:
        // A. your choice of languages installed with your application
        // B. the languages on the OS at application install time
        // C. the OS users propensity to install/uninstall language packs
        // D. the OS users propensity to change laguage settings
    
        // 2. Application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx
        // LoadLibraryEx is the preferred alternative for resource modules as used below because it
        // provides increased security and performance over that of LoadLibrary
        HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 3. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 4. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 5. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeLibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize)
    {
        BOOL rtnVal = FALSE;
        // very simple implementation - assumes that first 'langStrSize' characters of the 
        // L".\\langs.txt" file comprises a string of one or more languages
        HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(langConfigFileHandle != INVALID_HANDLE_VALUE)
        {
            // clear out the input variables
            DWORD bytesActuallyRead = 0;
            if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0)
            {
                rtnVal = TRUE;
                DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize;
                langStr[nullIndex] = L'\0';
            }
            CloseHandle(langConfigFileHandle);
        }
        return rtnVal;
    }
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize)
    {
        BOOL rtnVal = FALSE;
        size_t strLen = 0;
        rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen));
        if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0)
        {
            WCHAR * langMultiStrPtr = langMultiStr;
            WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0);
            WCHAR * context = last;
            WCHAR * next = wcstok_s(last,L",; :",&context);
            while(next && rtnVal)
            {
                // make sure you validate the user input
                if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) && 
                    IsValidLocaleName(next))
                {
                    langMultiStrPtr[0] = L'\0';
                    rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next));
                    langMultiStrPtr += strLen + 1;
                }
                next = wcstok_s(NULL,L",; :",&context);
                if(next)
                    last = next;
            }
            if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string
            {
                langMultiStrPtr[0] = L'\0';
            }
            else // fail and guard anyone whom might use the multi-string
            {
                langMultiStr[0] = L'\0';
                langMultiStr[1] = L'\0';
            }
        }
        return rtnVal;
    }
    
  4. Créez un fichier texte Unicode langs.txt contenant la ligne suivante :

    hi-IN ta-IN ru-RU fr-FR es-ES en-US
    

    Remarque

    Veillez à enregistrer le fichier en Unicode.

     

    Copiez langs.txt dans le répertoire à partir duquel le programme sera exécuté :

    • Si vous l’exécutez à partir de Visual Studio, copiez-le dans ProjectRootDirectory\HelloMUI\GuiStep_2.
    • Si vous l’exécutez à partir de l’explorateur Windows, copiez-le dans le même répertoire que GuiStep_2.exe.
  5. Générez et exécutez le projet. Essayez de modifier le fichier langs.txt pour que les différentes langues apparaissent en début de liste.

Étape 5 : Personnalisation de« Hello MUI »

Un certain nombre de fonctionnalités d’exécution mentionnées jusque-là dans ce tutoriel ne sont disponibles que sous Windows Vista et les versions ultérieures. Vous pouvez souhaiter réutiliser les efforts investis dans la localisation et le fractionnement des ressources en rendant l’application opérationnelle sur les versions de systèmes d’exploitation Windows de niveau inférieur, telles que Windows XP. Ce processus implique d’ajuster l’exemple précédent dans deux domaines clés :

  • Les fonctions de chargement de ressources antérieures à Windows Vista (telles que LoadString, LoadIcon, LoadBitmap, FormatMessage, et autres) ne sont pas compatibles avec MUI. Les applications fournies avec des ressources fractionnées (fichiers LN et .mui) doivent charger les modules de ressources à l’aide de l’une de ces deux fonctions :

    • Si l’application doit être exécutée uniquement sous Windows Vista ou les versions ultérieures, le chargement des modules de ressources doit être effectué à l’aide de LoadLibraryEx.
    • Si l’application doit être exécutée sur des versions antérieures à Windows Vista, ainsi que sur Windows Vista ou une version ultérieure, celle-ci doit utiliser LoadMUILibrary, qui est une fonction spécifique de niveau inférieur fournie dans le Kit de développement logiciel (SDK) Windows 7.
  • La prise en charge de la gestion des langues et des ordres de substitution des langues proposée dans les versions antérieures à Windows Vista du système d’exploitation Windows diffère sensiblement de celle de Windows Vista et des versions ultérieures. C’est la raison pour laquelle les applications qui autorisent la substitution linguistique configuré par l’utilisateur doivent adapter leurs pratiques de gestion des langues :

    • Si l’application doit être exécutée uniquement sous Windows Vista ou une version ultérieure, il est suffisant de définir la liste des langues à l’aide de SetThreadPreferredUILanguages.
    • Si l’application doit être exécutée sur toutes les versions de Windows, le code doit être conçu pour fonctionner sur les plateformes de niveau inférieur afin de parcourir la liste de langues configurée par l’utilisateur et de rechercher le module de ressources souhaité. Vous pouvez retrouver ces informations dans les sections 1c et 2 du code fourni plus loin dans cette étape.

Créez un projet qui peut utiliser les modules de ressources localisées sur toutes les versions de Windows :

  1. Ajoutez un nouveau projet à la solution HelloMUI (en utilisant la sélection de menu Fichier, Ajouter, et Nouveau projet) avec les paramètres et les valeurs suivants :

    1. Type de projet : Projet Win32.
    2. Nom : GuiStep_3.
    3. Emplacement : Acceptez la valeur par défaut.
    4. Dans l’Assistant Application Win32, sélectionnez le type d’application par défaut : Application Windows.
  2. Définissez ce projet pour qu’il s’exécute à partir de Visual Studio et configurez son modèle de threading. Configurez-le également pour que les en-têtes et les bibliothèques nécessaires puissent y être ajoutés.

    Remarque

    Les chemins utilisés dans ce tutoriel supposent que le kit SDK Windows 7 et le package d’API de niveau inférieur Microsoft NLS ont été installés dans leurs répertoires par défaut. Si ce n’est pas le cas, modifiez les chemins d’accès en conséquence.

     

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet GuiStep_3 et sélectionnez Définir comme projet de démarrage.

    2. Cliquez à nouveau avec le bouton droit et sélectionnez Propriétés.

    3. Dans la boîte de dialogue Pages de propriétés du projet :

      1. Dans le menu déroulant en haut à gauche, réglez Configuration sur Toutes les configurations.

      2. Sous Propriétés de configuration, développez C/C++, sélectionnez Génération de code et définissez Bibliothèque d’exécution : Débogage multithread (/MTd).

      3. Sélectionnez Général et ajoutez-y des répertoires Include supplémentaires :

        • « C :\Microsoft NLS Downlevel API\Include ».
      4. Sélectionnez Langue et définissez Traiter wchar_t comme Type intégré : Non (/Zc:wchar_t-).

      5. Sélectionnez Avancé et réglez Convention d’appel : _stdcall (/Gz).

      6. Sous Propriétés de configuration, développez Éditeur de liens, sélectionnez Entrée, et ajoutez à Dépendances supplémentaires :

        • « C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib ».
        • « C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib ».
  3. Remplacez le contenu de GuiStep_3.cpp par le code suivant :

    // GuiStep_3.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_3.h"
    #include <strsafe.h>
    #include <Nlsdl.h>
    #include <MUILoad.h>
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize);
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Application starts by applying any user defined language preferences
        // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback)
        // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.)
        WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2];
        if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format
        WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER];
        if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1c. Application now attempts to set the fallback list - this is much different for a down-level 
        // shipping application when compared to a Windows Vista or Windows 7 only shipping application    
        BOOL setSuccess = FALSE;
        DWORD setLangCount = 0;
        HMODULE hDLL = GetModuleHandleW(L"kernel32.dll");
        if( hDLL )
        {
            typedef BOOL (* SET_PREFERRED_UI_LANGUAGES_PROTOTYPE ) ( DWORD, PCWSTR, PULONG );
            SET_PREFERRED_UI_LANGUAGES_PROTOTYPE fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE)NULL;
            fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetProcessPreferredUILanguages");
            if( fp_SetPreferredUILanguages )
            {
                // call SetProcessPreferredUILanguages if it is available in Kernel32.dll's export table - Windows 7 and later
                setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount);
            }
            else
            {
                fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetThreadPreferredUILanguages");
                // call SetThreadPreferredUILanguages if it is available in Kernel32.dll's export table - Windows Vista and later
                if(fp_SetPreferredUILanguages)
                    setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount);
            }
        }
    
        // 2. Application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module
        // LoadMUILibrary is the preferred alternative for loading of resource modules
        // when the application is potentially run on OS versions prior to Windows Vista
        // LoadMUILibrary is available via Windows SDK releases in Windows Vista and later
        // When available, it is advised to get the most up-to-date Windows SDK (e.g., Windows 7)
        HMODULE resContainer = NULL;
        if(setSuccess) // Windows Vista and later OS scenario
        {
            resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0);
        }
        else // this block should only be hit on Windows XP and earlier OS platforms as setSuccess will be TRUE on Windows Vista and later
        {
            // need to provide your own fallback mechanism such as the implementation below
            // in essence the application will iterate through the user configured language list
            WCHAR * next = userLanguagesMultiString;
            while(!resContainer && *next != L'\0')
            {
                // convert the language name to an appropriate LCID
                // DownlevelLocaleNameToLCID is available via standalone download package 
                // and is contained in Nlsdl.h / Nlsdl.lib
                LCID nextLcid = DownlevelLocaleNameToLCID(next,DOWNLEVEL_LOCALE_NAME);
                // then have LoadMUILibrary attempt to probe for the right .mui module
                resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,(LANGID)nextLcid);
                // increment to the next language name in our list
                size_t nextStrLen = 0;
                if(SUCCEEDED(StringCchLengthW(next,LOCALE_NAME_MAX_LENGTH,&nextStrLen)))
                    next += (nextStrLen + 1);
                else
                    break; // string is invalid - need to exit
            }
            // if the user configured list did not locate a module then try the languages associated with the system
            if(!resContainer)
                resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0);
        }
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 3. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 4. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 5. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeMUILibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize)
    {
        BOOL rtnVal = FALSE;
        // very simple implementation - assumes that first 'langStrSize' characters of the 
        // L".\\langs.txt" file comprises a string of one or more languages
        HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(langConfigFileHandle != INVALID_HANDLE_VALUE)
        {
            // clear out the input variables
            DWORD bytesActuallyRead = 0;
            if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0)
            {
                rtnVal = TRUE;
                DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize;
                langStr[nullIndex] = L'\0';
            }
            CloseHandle(langConfigFileHandle);
        }
        return rtnVal;
    }
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize)
    {
        BOOL rtnVal = FALSE;
        size_t strLen = 0;
        rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen));
        if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0)
        {
            WCHAR * langMultiStrPtr = langMultiStr;
            WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0);
            WCHAR * context = last;
            WCHAR * next = wcstok_s(last,L",; :",&context);
            while(next && rtnVal)
            {
                // make sure you validate the user input
                if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) 
                    && DownlevelLocaleNameToLCID(next,0) != 0)
                {
                    langMultiStrPtr[0] = L'\0';
                    rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next));
                    langMultiStrPtr += strLen + 1;
                }
                next = wcstok_s(NULL,L",; :",&context);
                if(next)
                    last = next;
            }
            if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string 
            {
                langMultiStrPtr[0] = L'\0';
            }
            else // fail and guard anyone whom might use the multi-string
            {
                langMultiStr[0] = L'\0';
                langMultiStr[1] = L'\0';
            }
        }
        return rtnVal;
    }
    
  4. Créez ou copiez langs.txt dans le répertoire approprié, comme décrit précédemment à lÉtape 4 : Globalisation de « Hello MUI ».

  5. Générez et exécutez le projet.

Remarque

Si l’application doit être exécutée sur des versions de Windows antérieures à Windows Vista, veillez à lire les documents fournis avec le package d’API NLS de niveau inférieur de Microsoft sur la manière de redistribuer le fichier Nlsdl.dll. (Ce module n’est plus disponible dans le Centre de téléchargement de Microsoft. Utilisez les API de globalisation ICU sur la mise à jour de mai 2019 de Windows 10 et versions ultérieures).

 

Considérations supplémentaires pour MUI

Prise en charge des applications console

Les techniques abordées dans ce tutoriel peuvent également être utilisées dans les applications console. Toutefois, contrairement à la plupart des contrôles d’interface graphique standard, la fenêtre de commande Windows ne peut pas afficher les caractères pour toutes les langues. C’est pourquoi les applications console multilingues doivent faire l’objet d’une attention particulière.

L’appel aux API SetThreadUILanguage ou SetThreadPreferredUILanguages avec des indicateurs de filtrage spécifiques permet aux fonctions de chargement des ressources de supprimer les sondes de ressources linguistiques pour des langues spécifiques qui ne sont normalement pas affichées dans une fenêtre de commande. Lorsque ces indicateurs sont définis, les algorithmes de configuration des langues autorisent uniquement les langues qui s’affichent correctement dans la fenêtre de commande à figurer dans la liste de substitution.

Pour plus d’informations sur l’utilisation de ces API pour créer une application console multilingue, consultez les sections des remarques de SetThreadUILanguage et SetThreadPreferredUILanguages.

Détermination des langues à prendre en charge au moment de l’exécution

Vous pouvez adopter l’une des suggestions de conception suivantes pour déterminer les langues que votre application doit prendre en charge au moment de l’exécution :

  • Pendant l’installation, permettez à l’utilisateur final de sélectionner sa langue préférée à partir d’une liste de langues prises en charge

  • Lire une liste de langues à partir d’un fichier de configuration

    Certains des projets de ce tutoriel contiennent une fonction utilisée pour analyser un fichier de configuration langs.txt, qui contient une liste de langues.

    Étant donné que cette fonction utilise des données externes, il convient de valider les langues qui sont fournies comme données d’entrée. Pour plus de détails sur l’exécution de cette validation, consultez les fonctions IsValidLocaleName ou DownLevelLocaleNameToLCID.

  • Interroger le système d’exploitation pour déterminer quelles langues sont installées

    Cette approche permet à l’application d’utiliser la même langue que le système d’exploitation. Bien qu’il ne soit pas nécessaire de solliciter l’utilisateur pour cela, si vous choisissez cette option, sachez que les langues du système d’exploitation peuvent être ajoutées ou supprimées à tout moment et qu’elles peuvent être modifiées après l’installation de l’application par l’utilisateur. Sachez également que, dans certains cas, le système d’exploitation est installé avec une prise en charge limitée des langues et que l’application offre plus de valeur si elle prend en charge des langues que le système d’exploitation ne prend pas en charge.

    Pour plus d’informations sur la manière de déterminer les langues actuellement installées dans le système d’exploitation, consultez la fonction EnumUILanguages.

Prise en charge des scripts complexes dans les versions antérieures à Windows Vista

Lorsqu’une application qui prend en charge certains scripts complexes s’exécute sur une version de Windows antérieure à Windows Vista, le texte de ce script peut ne pas s’afficher correctement dans les composants de l’interface graphique. Par exemple, dans le projet de niveau inférieur de ce tutoriel, les scripts hi-IN et ta-IN peuvent ne pas s’afficher dans la zone de message en raison de problèmes liés au traitement de scripts complexes et à l’absence de polices de caractères correspondantes. Normalement, les problèmes de cette nature se présentent sous la forme de zones carrées dans le composant de l’interface graphique.

Pour plus d’informations sur l’activation du traitement de script complexe, consultez Prise en charge des scripts et des polices dans Windows.

Résumé

Ce tutoriel a permis de globaliser une application unilingue et de démontrer les meilleures pratiques suivantes.

  • Concevez l’application pour tirer parti du modèle de ressource Win32.
  • Utilisez le fractionnement MUI des ressources en fichiers binaires satellites (fichiers.mui).
  • Assurez-vous que le processus de localisation met à jour les ressources dans les fichiers .mui afin qu’elles soient adaptées à la langue cible.
  • Assurez-vous que l’empaquetage et le déploiement de l’application, des fichiers .mui associés et du contenu de la configuration sont effectués correctement pour permettre aux API de chargement des ressources de trouver le contenu localisé.
  • Fournissez à l’utilisateur final un mécanisme permettant d’ajuster la configuration linguistique de l’application.
  • Ajustez le code d’exécution pour tirer parti de la configuration de la langue, afin que l’application réponde mieux aux besoins de l’utilisateur final.