Compartilhar via


Como adicionar o suporte à interface do usuário multilíngue a um aplicativo

Este tutorial demonstra como tornar um aplicativo monolíngue pronto para o mundo. Esse aplicativo está na forma de uma solução completa que foi compilada no Microsoft Visual Studio.

Visão geral

A partir do Windows Vista, o próprio sistema operacional Windows foi criado do zero para ser uma plataforma multilíngue, com um suporte adicional que lhe permite criar aplicativos multilíngues que usam o modelo de recursos da MUI do Windows.

Este tutorial ilustra esse novo suporte a aplicativos multilíngues abrangendo os seguintes aspectos:

  • Usa o suporte aprimorado à plataforma MUI para habilitar aplicativos multilíngues com facilidade.
  • Amplia os aplicativos multilíngues para que possam ser executados em versões do Windows antes do Windows Vista.
  • Aborda as considerações adicionais envolvidas no desenvolvimento de aplicativos multilíngues especializados como, por exemplo, aplicativos de console.

Esses links ajudam a fornecer uma atualização rápida dos conceitos referentes à internacionalização e à MUI:

A ideia por trás da MUI do Hello (Olá, MUI)

Você provavelmente está familiarizado com o aplicativo Olá, Mundo clássico, que ilustra conceitos básicos de programação. Este tutorial usa uma abordagem semelhante para ilustrar como usar o modelo de recursos da MUI para atualizar um aplicativo monolíngue e criar uma versão multilíngue chamada Olá, MUI.

Observação

As tarefas neste tutorial são descritas em etapas detalhadas devido à precisão com que essas atividades devem ser executadas e à necessidade de explicar os detalhes aos desenvolvedores que têm pouca experiência com essas tarefas.

 

Como configurar a solução MUI do Hello

Essas etapas descrevem como se preparar para criar a solução MUI do Hello.

Requisitos de plataforma

Você precisa compilar as amostras de código neste tutorial usando o Kit de Desenvolvimento de Software (SDK) do Windows para Windows 7 e o Visual Studio 2008. O SDK do Windows 7 será instalado no Windows XP, Windows Vista e Windows 7 e a amostra da solução poderá ser compilada em qualquer uma dessas versões do sistema operacional.

Todas as amostras de código neste tutorial foram projetadas para serem executadas em versões x86 e x64 do Windows XP, Windows Vista e Windows 7. Chamaremos a atenção para as partes específicas que não irão funcionar no Windows XP quando apropriado.

Pré-requisitos

  1. Instalar o Visual Studio 2008.

    Para obter mais informações, consulte o Centro de Desenvolvedores do Visual Studio.

  2. Instalar o SDK do Windows para Windows 7.

    Você pode instalá-lo a partir da página do SDK do Windows no Centro de Desenvolvedores do Windows. O SDK inclui utilitários para desenvolver aplicativos para as versões do sistema operacional — a partir do Windows XP até a mais recente.

    Observação

    Se não estiver instalando o pacote no local padrão, ou se não estiver instalando na unidade do sistema, que geralmente é a unidade C, anote o caminho de instalação.

     

  3. Configure os parâmetros da linha de comando do Visual Studio.

    1. Abra uma janela de comando do Visual Studio.
    2. Digite set path.
    3. Confirme se a variável do caminho inclui o caminho da pasta bin do SDK do Windows 7: ...Microsoft SDKs\Windows\v7.0\bin
  4. Instale o pacote de complementos de APIs de nível inferior do NLS da Microsoft.

    Observação

    No contexto deste tutorial, esse pacote será necessário somente se você estiver personalizando o aplicativo a ser executado em versões do Windows anteriores ao Windows Vista. Confira a Etapa 5: Personalizar o Olá, MUI.

    1. Baixe e instale o pacote, que não está mais disponível no Centro de Download da Microsoft. Use as APIs de globalização da ICU na atualização de maio de 2019 para o Windows 10 e versões posteriores.

    2. Assim como ocorre com o SDK do Windows, se não estiver instalando o pacote no local padrão ou se não estiver instalando na unidade do sistema, que geralmente é a unidade C, anote o caminho da instalação.

    3. Se a sua plataforma de desenvolvimento for Windows XP ou Windows Server 2003, confirme se Nlsdl.dll está instalado e registrado corretamente.

      1. Navegue até a pasta "redist" no local do caminho de instalação.
      2. Execute o nlsdl.*.exe redistribuível apropriado como, por exemplo, nlsdl.x86.exe. Essa etapa instala e registra o Nlsdl.dll.

    Observação

    Se você desenvolver um aplicativo que usa a MUI e precisará ser executado em versões do Windows anteriores ao Windows Vista, Nlsdl.dll precisará estar presente na plataforma Windows de destino. Na maioria dos casos, isso significa que o aplicativo precisará incluir e instalar o instalador Nlsdl redistribuível (e não simplesmente copiar o Nlsdl.dll propriamente dito).

     

Etapa 0: Criar a MUI diretamente no código do Hello

Este tutorial começa com a versão monolíngue do aplicativo Olá, MUI. O aplicativo pressupõe o uso da linguagem de programação C++, cadeias de caracteres amplas e a função MessageBoxW para o resultado.

Comece criando o aplicativo GuiStep_0 inicial, além da solução HelloMUI, que contém todos os aplicativos incluídos neste tutorial.

  1. No Visual Studio 2008, crie um novo projeto. Use as seguintes configurações e valores:

    1. Tipo de projeto: no Visual C++, selecione Win32 e, em seguida, em modelos instalados do Visual Studio, selecione Projeto do Win32.
    2. Nome: GuiStep_0.
    3. Local: ProjectRootDirectory (as etapas posteriores fazem referência a esse diretório).
    4. Nome da solução: HelloMUI.
    5. Selecione "Criar diretório para a solução".
    6. No Assistente de Aplicativos do Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
  2. Configure o modelo de threading do projeto:

    1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto GuiStep_0 e, a seguir, selecione Propriedades.

    2. Na caixa de diálogo Páginas de Propriedades do projeto:

      1. Na lista suspensa no canto superior esquerdo, defina Configuração como Todas as Configurações.
      2. Em Propriedades da Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca do Runtime: Depuração Multi-threaded (/MTd).
  3. Substitua o conteúdo de GuiStep_0.cpp pelo seguinte código:

    // 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. Crie e execute o aplicativo.

O código-fonte simples e direto acima faz uma opção de design simplista entre a codificação direta ou a incorporação de todos os resultados que o usuário verá — nesse caso, o texto "Olá, MUI". Essa opção limita o utilitário do aplicativo para usuários que não reconheçam a palavra em português "Olá". Como MUI é um acrônimo em inglês baseado em tecnologia, este tutorial pressupõe que a cadeia de caracteres permaneça sendo "MUI" para todos os idiomas.

Etapa 1: Implementar o módulo de recursos básico

Faz tempo que o Microsoft Win32 expôs para os desenvolvedores de aplicativos a capacidade de separar seus dados de recursos de interface do usuário do código-fonte do aplicativo. Essa separação vem na forma do modelo de recursos Win32, no qual as cadeias de caracteres, bitmaps, ícones, mensagens e outros itens normalmente exibidos para um usuário são empacotados em uma seção distinta do executável, separadas do código executável.

Para ilustrar essa separação entre o código executável e o empacotamento de dados do recurso, nessa etapa o tutorial coloca a cadeia de caracteres "Olá" anteriormente incluída diretamente no código (o recurso "pt-BR") na seção de recursos de um módulo DLL no projeto HelloModule_pt_br.

Essa DLL do Win32 também pode conter funcionalidades executáveis do tipo biblioteca (como qualquer outra DLL). Mas para ajudar a manter o foco nos aspectos relacionados aos recursos do Win32, deixamos o código DLL do runtime como um espaço reservado em dllmain.cpp. As seções subsequentes deste tutorial fazem uso dos dados do recurso HelloModule que estão sendo compilados aqui e também apresentam o código do runtime apropriado.

Para construir um módulo de recurso do Win32, comece criando uma DLL com um espaço reservado no dllmain:

  1. Adicione um novo projeto à solução HelloMUI:

    1. No menu Arquivo, selecione Adicionar e, em seguida, Novo Projeto.
    2. Tipo de projeto: Projeto do Win32.
    3. Nome: HelloModule_pt_br.
    4. Local: ProjectRootDirectory\HelloMUI.
    5. No Assistente de Aplicativo do Win32, selecione o tipo de aplicativo: DLL.
  2. Configure o modelo de threading desse projeto:

    1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto HelloModule_pt_br e selecione Propriedades.

    2. Na caixa de diálogo Páginas de Propriedades do projeto:

      1. Na lista suspensa no canto superior esquerdo, defina Configuração como Todas as Configurações.
      2. Em Propriedades da Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca do Runtime: Depuração Multi-threaded (/MTd).
  3. Examine dllmain.cpp. (Talvez você queira adicionar as macros UNREFERENCED_PARAMETER ao código gerado, como temos aqui, para habilitá-lo a compilar no aviso nível 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. Adicione ao projeto um arquivo de definição de recurso HelloModule.rc:

    1. No projeto HelloModule_pt_br, clique com o botão direito do mouse na pasta Arquivos de Recursos, selecione Adicionar e, a seguir, selecione Novo Item.

    2. Na caixa de diálogo Adicionar Novo Item, escolha o seguinte:

      1. Categorias: em Visual C++, selecione Recurso e, em "Modelos instalados do Visual Studio", selecione Arquivo de Recurso (.rc).
      2. Nome: HelloModule.
      3. Local: aceite o padrão.
      4. Clique em Adicionar.
    3. Especifique que o novo arquivo HelloModule.rc deve ser salvo como Unicode:

      1. No Gerenciador de Soluções, clique com o botão direito do mouse em HelloModule.rc e selecione Ver Código.
      2. Se estiver vendo uma mensagem lhe dizendo que o arquivo já está aberto e perguntando se você quer fechá-lo, clique em Sim.
      3. Com o arquivo exibido como texto, escolha File na seleção do menu e, em seguida, Opções de Salvamento Avançadas.
      4. Em Codificação, especifique Unicode — Codepage 1200.
      5. Clique em OK.
      6. Salve e feche o HelloModule.rc.
    4. Adicione uma tabela de cadeia de caracteres contendo a cadeia de caracteres "Olá":

      1. No Modo de Exibição de Recursos, clique com o botão direito do mouse em HelloModule.rc e selecione Adicionar Recurso.

      2. Selecione Tabela de Cadeia de Caracteres.

      3. Clique em Novo.

      4. Adicione uma cadeia de caracteres à Tabela de Cadeia de Caracteres:

        1. ID: HELLO_MUI_STR_0.
        2. Valor: 0.
        3. Legenda: Olá.

      Se tentar ver HelloModule.rc como texto agora, você verá várias partes do código-fonte específicas para o recurso. A parte que mais interessa é a seção que descreve a cadeia de caracteres "Olá":

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

      Essa cadeia de caracteres "Olá" é o recurso que precisa ser localizado (ou seja, traduzido) em cada idioma que o aplicativo espera suportar. Por exemplo, o projeto HelloModule_pt_br (que você vai criar na próxima etapa) conterá sua própria versão localizada do HelloModule.rc para "pt-BR":

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "வணக்கம்"
      END
      
    5. Compile o projeto HelloModule_pt_br de modo a ter certeza de que não haja nenhum erro.

  5. Crie mais seis projetos na solução HelloMUI (ou quantos quiser) para criar mais seis DLLs de recurso para idiomas adicionais. Use os valores dessa tabela:

    Nome do projeto Nome do arquivo .rc ID da Cadeia de Caracteres Valor da cadeia de caracteres Legenda da cadeia de caracteres
    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 வணக்கம்

     

Para obter mais informações sobre a estrutura e a sintaxe do arquivo .rc, confira Sobre os arquivos de recursos.

Etapa 2: Compilar o módulo de recursos básico

Usando modelos de recursos anteriores, a compilação de qualquer um dos sete projetos do HelloModule resultaria em sete DLLs separadas. Cada DLL conteria uma seção de recurso com uma única cadeia de caracteres localizada no idioma apropriado. Embora apropriado para o modelo de recurso histórico do Win32, esse design não tira proveito da MUI.

No SDK do Windows Vista e posterior, a MUI fornece a capacidade de dividir módulos de conteúdo localizáveis e executáveis no código-fonte. Com a personalização adicional que será abordada posteriormente na Etapa 5, os aplicativos podem ser habilitados para que o suporte multilíngue seja executado em versões anteriores ao Windows Vista.

Os principais mecanismos disponíveis para dividir recursos do código executável, começando pelo Windows Vista, são:

  • Usar rc.exe (o Compilador RC) com comutadores específicos, ou
  • Usar uma ferramenta de divisão no estilo pós-compilação chamada muirct.exe.

Confira Utilitários de Recursos para obter mais informações.

Para fins de simplificação, este tutorial usa muirct.exe para dividir o executável "Hello MUI".

Como dividir os vários módulos de recurso de idioma: uma visão geral

Um processo de várias partes está envolvido na divisão das DLLs em um HelloModule.dll executável, além de um HelloModule.dll.mui para cada um dos sete idiomas com suporte neste tutorial. Essa visão geral descreve as etapas necessárias; a próxima seção apresenta um arquivo de comando que executa essas etapas.

Primeiro, divida o módulo HelloModule.dll somente em inglês usando o comando:

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

A linha de comando acima usa o arquivo de configuração DoReverseMuiLoc.rcconfig. Esse tipo de arquivo de configuração normalmente é usado por muirct.exe para dividir os recursos entre a DLL neutra de idioma (LN) e os arquivos .mui dependentes de idioma. Nesse caso, o arquivo xml DoReverseMuiLoc.rcconfig (listado na próxima seção) indica vários tipos de recursos, mas todos eles se enquadram na categoria de arquivo "localizedResources" ou .mui, e não há nenhum recurso na categoria neutra de idioma. Para obter mais informações sobre como preparar um arquivo de configuração de recurso, confira Como preparar um arquivo de configuração de recurso.

Além de criar um arquivo HelloModule.dll.mui que contém a cadeia de caracteres em inglês "Hello", o muirct.exe também incorpora um recurso MUI no módulo HelloModule.dll durante a divisão. Para carregar corretamente no runtime os recursos apropriados dos módulos HelloModule.dll.mui específicos por idioma, cada arquivo .mui deve ter suas somas de verificação corrigidas para corresponder às somas de verificação do módulo LN neutro de idioma da linha de base. Isso é feito por um comando como, por exemplo:

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

Da mesma forma, muirct.exe é invocado para criar um arquivo HelloModule.dll.mui para cada um dos outros idiomas. No entanto, nesses casos, a DLL neutra em idioma é descartada, já que somente a primeira criada será necessária. Os comandos que processam os recursos em espanhol e francês são semelhantes ao seguinte:

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

Uma das coisas mais importantes a observar nas linhas do comando muirct.exe acima é que o sinalizador -x é usado para especificar uma ID de idioma de destino. O valor fornecido para muirct.exe é diferente para cada módulo HelloModule.dll específico para o idioma. Esse valor de idioma é crucial e é usado por muirct.exe para marcar adequadamente o arquivo .mui durante a divisão. Um valor incorreto irá produzir falhas de carregamento de recursos para esse arquivo .mui específico no runtime. Confira Constantes e cadeias de caracteres do identificador de idiomas para obter mais detalhes sobre como mapear o nome do idioma para o LCID.

Cada arquivo .mui dividido acaba sendo colocado em um diretório correspondente ao nome do respectivo idioma, imediatamente abaixo do diretório no qual o HelloModule.dll neutro em idioma irá residir. Para obter mais informações sobre o posicionamento do arquivo .mui, confira Implantação de aplicativos.

Como dividir os vários módulos de recursos de idioma: criação dos arquivos

Para este tutorial, você cria um arquivo de comando contendo os comandos para dividir as várias DLLs e o invoca manualmente. Observe que, no trabalho de desenvolvimento real, você poderá reduzir a possibilidade de erros de compilação se incluir esses comandos como eventos pré-compilação ou pós-compilação na solução HelloMUI, mas essa opção está fora do escopo deste tutorial.

Crie os arquivos para a compilação de depuração:

  1. Crie um arquivo de comando DoReverseMuiLoc.cmd contendo os seguintes comandos:

    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. Crie um arquivo xml DoReverseMuiLoc.rcconfig contendo as seguintes linhas:

    <?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. Copie DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig para ProjectRootDirectory\HelloMUI\Debug.

  4. Abra o prompt de comando do Visual Studio 2008 e navegue até o diretório Depuração.

  5. Execute DoReverseMuiLoc.cmd.

Ao criar um build de versão, você copia os mesmos arquivos DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig para o diretório Versão e executa o arquivo de comando lá.

Etapa 3: Criar um "Olá, MUI" experiente em recursos

Com base no exemplo inicial GuiStep_0.exe acima incluído diretamente no código, você pode ampliar o alcance do aplicativo para usuários de vários idiomas se optar por incorporar o modelo de recurso do Win32. O novo código do runtime apresentado nessa etapa inclui o carregamento do módulo (LoadLibraryEx) e a lógica de recuperação da cadeia de caracteres (LoadString).

  1. Adicione um novo projeto à solução HelloMUI (usando a seleção do menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:

    1. Tipo de projeto: Projeto do Win32.
    2. Nome: GuiStep_1.
    3. Local: aceite o padrão.
    4. No Assistente de Aplicativos do Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
  2. Configure esse projeto para ser executado de dentro do Visual Studio e configure o respectivo modelo de threading:

    1. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto GuiStep_1 e selecione Configurar como Projeto de Inicialização.

    2. Clique nele com o botão direito do mouse novamente e selecione Propriedades.

    3. Na caixa de diálogo Páginas de Propriedades do projeto:

      1. Na lista suspensa no canto superior esquerdo, defina Configuração como Todas as Configurações.
      2. Em Propriedades da Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca do Runtime: Depuração Multi-threaded (/MTd).
  3. Substitua o conteúdo de GuiStep_1.cpp pelo seguinte código:

    // 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. Crie e execute o aplicativo. O resultado será exibido no idioma definido atualmente como o idioma de exibição do computador (desde que seja um dos sete idiomas que criamos).

Etapa 4: Globalizar o "Olá, MUI"

Embora seja capaz de exibir seu resultado em diferentes idiomas, o exemplo anterior deixa a desejar em diversas áreas. Talvez a mais fácil de perceber seja que o aplicativo só está disponível em um pequeno subconjunto de idiomas quando comparado ao sistema operacional Windows propriamente dito. Se, por exemplo, o aplicativo GuiStep_1 da etapa anterior fosse instalado em um build do Windows em japonês, a localização do recurso provavelmente iria falhar.

Para resolver essa situação, você tem duas opções principais:

  • Certifique-se de que os recursos em um idioma de fallback final predeterminado estejam incluídos.
  • Forneça uma maneira para o usuário final configurar suas preferências de idioma dentro de um subconjunto de idiomas especificamente compatível com o aplicativo.

Dessas opções, a que fornece um idioma de fallback final é altamente aconselhável e é implementada neste tutorial ao repassarmos o sinalizador -g para muirct.exe, conforme vimos acima. Esse sinalizador instrui o muirct.exe a escolher um idioma específico (como o alemão, em de-DE/0x0407) para o idioma de fallback final associado ao módulo dll neutro de idioma (HelloModule.dll). No runtime, esse parâmetro é usado como último recurso para gerar o texto a ser exibido para o usuário. Caso um idioma de fallback final não seja encontrado e nenhum recurso apropriado esteja disponível no binário neutro do idioma, o carregamento do recurso irá falhar. Portanto, você deve determinar cuidadosamente os cenários que seu aplicativo poderá encontrar e planejar o idioma do fallback final de acordo.

A outra opção, de permitir preferências de idioma configuráveis e carregar recursos com base nessa hierarquia definida pelo usuário, pode aumentar consideravelmente o grau de satisfação do cliente. Infelizmente, isso também complica a funcionalidade necessária dentro do aplicativo.

Essa etapa do tutorial usa um mecanismo de arquivo de texto simplificado para habilitar a configuração personalizada do idioma do usuário. O arquivo de texto é analisado no runtime pelo aplicativo e a lista de idiomas analisada e validada é usada para estabelecer uma lista de fallback personalizada. Depois que a lista de fallback personalizado for estabelecida, as APIs do Windows carregarão recursos de acordo com a precedência de idioma estabelecida nessa lista. O restante do código é semelhante ao encontrado na etapa anterior.

  1. Adicione um novo projeto à solução HelloMUI (usando a seleção do menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:

    1. Tipo de projeto: Projeto do Win32.
    2. Nome: GuiStep_2.
    3. Local: aceite o padrão.
    4. No Assistente de Aplicativos do Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
  2. Configure esse projeto para ser executado de dentro do Visual Studio e configure o respectivo modelo de threading:

    1. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto GuiStep_2 e selecione Configurar como Projeto de Inicialização.

    2. Clique nele com o botão direito do mouse novamente e selecione Propriedades.

    3. Na caixa de diálogo Páginas de Propriedades do projeto:

      1. Na lista suspensa no canto superior esquerdo, defina Configuração como Todas as Configurações.
      2. Em Propriedades da Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca do Runtime: Depuração Multi-threaded (/MTd).
  3. Substitua o conteúdo de GuiStep_2.cpp pelo seguinte código:

    // 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. Crie um arquivo de texto Unicode langs.txt contendo a seguinte linha:

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

    Observação

    Certifique-se de salvar o arquivo como Unicode.

     

    Copie langs.txt para o diretório do qual o programa será executado:

    • Se estiver em execução dentro do Visual Studio, copie-o para ProjectRootDirectory\HelloMUI\GuiStep_2.
    • Se estiver em execução no Windows Explorer, copie-o para o mesmo diretório de GuiStep_2.exe.
  5. Compile e execute o projeto. Tente editar o langs.txt para que diferentes idiomas apareçam na frente, na lista.

Etapa 5: Personalizar o "Olá, MUI"

Os diversos recursos do runtime mencionados até agora neste tutorial estão disponíveis apenas no Windows Vista e posterior. Você talvez queira reutilizar o esforço investido na localização e divisão de recursos fazendo com que o aplicativo funcione em versões de sistema operacional Windows de nível inferior, como o Windows XP. Esse processo envolve o ajuste do exemplo anterior em duas áreas cruciais:

  • As funções de carregamento de recursos anteriores ao Windows Vista (como LoadString, LoadIcon, LoadBitmap, FormatMessagee outras) não estão cientes da MUI. Os aplicativos enviados com recursos divididos (arquivos LN e .mui) precisam carregar os módulos de recursos usando uma dessas duas funções:

    • Caso se destine a ser executado somente no Windows Vista e posterior, o aplicativo deverá carregar os módulos de recursos com LoadLibraryEx.
    • Caso se destine a ser executado em versões anteriores ao Windows Vista, e também no Windows Vista ou posterior, o aplicativo deverá usar LoadMUILibrary, que é uma função de nível inferior específica fornecida no SDK do Windows 7.
  • O suporte ao gerenciamento de idiomas e à ordem de fallback de idioma oferecido nas versões anteriores ao Windows Vista do sistema operacional Windows difere de forma significativa do que é oferecido no Windows Vista e posterior. Por esse motivo, os aplicativos que permitem o fallback de idioma configurado pelo usuário precisam ajustar suas práticas de gerenciamento de idiomas:

    • Se o aplicativo se destinar a ser executado somente no Windows Vista e posterior, a definição da lista de idiomas usando SetThreadPreferredUILanguages será suficiente.
    • Caso o aplicativo se destine a ser executado em todas as versões do Windows, deverá ser construído um código a ser executado em plataformas de nível inferior para iterar ao longo da lista de idiomas configurada pelo usuário e procurar o módulo de recursos desejado. Isso pode ser visto nas seções 1c e 2 do código, fornecidas posteriormente nessa etapa.

Criar um projeto que possa usar os módulos de recursos localizados em qualquer versão do Windows:

  1. Adicione um novo projeto à solução HelloMUI (usando a seleção do menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:

    1. Tipo de projeto: Projeto do Win32.
    2. Nome: GuiStep_3.
    3. Local: aceite o padrão.
    4. No Assistente de Aplicativos do Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
  2. Configure esse projeto para ser executado de dentro do Visual Studio e configure o respectivo modelo de threading. Além disso, configure-o para adicionar as bibliotecas e os cabeçalhos necessários.

    Observação

    Os caminhos usados neste tutorial pressupõem que o SDK do Windows 7 e o pacote de APIs de nível inferior do NLS da Microsoft foram instalados em seus diretórios padrão. Se não for esse o caso, modifique os caminhos adequadamente.

     

    1. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto GuiStep_3 e selecione Configurar como Projeto de Inicialização.

    2. Clique nele com o botão direito do mouse novamente e selecione Propriedades.

    3. Na caixa de diálogo Páginas de Propriedades do projeto:

      1. Na lista suspensa no canto superior esquerdo, defina Configuração como Todas as Configurações.

      2. Em Propriedades da Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca do Runtime: Depuração Multi-threaded (/MTd).

      3. Selecione Geral e adicione a Incluir Diretórios Adicionais:

        • "C:\APIs de nível inferior do NLS da Microsoft\Include".
      4. Selecione Idioma e configure Tratar wchar_t como Tipo Integrado: Não (/Zc:wchar_t-).

      5. Selecione Avançado e configure Convenção de Chamada: _stdcall (/Gz).

      6. Em Propriedades de Configuração, expanda o Vinculador, selecione Entrada e adicione a Dependências Adicionais:

        • "C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
        • "C:\APIs de nível inferior do NLS da Microsoft\Lib\x86\Nlsdl.lib".
  3. Substitua o conteúdo de GuiStep_3.cpp pelo seguinte código:

    // 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. Crie ou copie langs.txt para o diretório apropriado, conforme descrito anteriormente na Etapa 4: Globalizar o "Olá, MUI".

  5. Compile e execute o projeto.

Observação

Se o aplicativo se destinar a ser executado em versões do Windows anteriores ao Windows Vista, certifique-se de ler os documentos que vieram com o pacote de APIs de nível inferior do NLS da Microsoft referentes a como redistribuir o Nlsdl.dll. (Isso não está mais disponível no Centro de Download da Microsoft. Use APIs de globalização da ICU na atualização de maio de 2019 para o Windows 10 e versões posteriores.)

 

Considerações adicionais para a MUI

Suporte para aplicativos de console

As técnicas abordadas neste tutorial também podem ser usadas em aplicativos de console. No entanto, ao contrário da maioria dos controles de GUI padrão, a janela de comando do Windows não pode exibir caracteres para todos os idiomas. Por esse motivo, os aplicativos de console multilíngue requerem uma atenção especial.

Chamar as APIs SetThreadUILanguage ou SetThreadPreferredUILanguages com sinalizadores de filtragem específicos faz com que as funções de carregamento de recursos removam as buscas de recursos de idioma para idiomas específicos que não costumam ser exibidos em uma janela de comando. Quando esses sinalizadores estão configurados, os algoritmos de configuração de idioma permitem que somente os idiomas que serão exibidos corretamente na janela de comando estejam na lista de fallback.

Para obter mais informações sobre como usar essas APIs para compilar um aplicativo de console multilíngue, confira as seções de comentários em SetThreadUILanguage e SetThreadPreferredUILanguages.

Determinação dos idiomas que terão suporte no runtime

Você pode adotar uma das seguintes sugestões de design para determinar a quais idiomas seu aplicativo deve dar suporte no runtime:

  • Durante a instalação, habilite o usuário final para selecionar o idioma preferencial a partir de uma lista de idiomas com suporte

  • Ler uma lista de idiomas de um arquivo de configuração

    Alguns dos projetos incluídos neste tutorial contêm uma função usada para analisar um arquivo de configuração langs.txt, que contém uma lista de idiomas.

    Como essa função usa uma entrada de dados externa, é preciso validar os idiomas que são fornecidos como uma entrada de dados. Confira as funções IsValidLocaleName ou DownLevelLocaleNameToLCID para obter mais detalhes sobre como executar essa validação.

  • Consultar o sistema operacional para determinar quais idiomas estão instalados

    Essa abordagem ajuda o aplicativo a usar o mesmo idioma do sistema operacional. Embora isso não exija uma solicitação ao usuário, se você escolher essa opção lembre-se de que os idiomas do sistema operacional podem ser adicionados ou removidos a qualquer momento e ser alterados após o usuário instalar o aplicativo. Além disso, lembre-se de que, em alguns casos, o sistema operacional é instalado com suporte a idiomas limitado, e o aplicativo oferecerá mais valor se oferecer suporte a idiomas aos quais o sistema operacional não dá suporte.

    Para obter mais informações sobre como determinar os idiomas instalados atualmente no sistema operacional, confira a função EnumUILanguages.

Suporte a scripts complexos em versões anteriores ao Windows Vista

Quando um aplicativo que dá suporte a determinados scripts complexos é executado em uma versão do Windows anterior ao Windows Vista, o texto no script em questão poderá não ser exibido corretamente nos componentes da GUI. Por exemplo, no projeto de nível inferior dentro deste tutorial, os scripts para hi-IN e ta-IN podem não ser exibidos na caixa de mensagem devido a problemas com o processamento de scripts complexos e a falta de fontes relacionadas. Normalmente, problemas dessa natureza se mostram como caixas quadradas no componente da GUI.

Mais informações sobre como habilitar o processamento de scripts complexos podem ser encontradas no Suporte a Scripts e Fontes no Windows.

Resumo

Este tutorial globalizou um aplicativo monolíngue e demonstrou as boas práticas a seguir.

  • Projetar o aplicativo de modo a tirar proveito do modelo de recursos do Win32.
  • Utilizar a divisão dos recursos da MUI em binários satélites (arquivos .mui).
  • Verificar se o processo de localização atualiza os recursos nos arquivos .mui para se tornarem apropriados para o idioma de destino.
  • Verificar se o empacotamento e a implantação do aplicativo, os arquivos .mui associados e o conteúdo de configuração são feitos corretamente de modo a permitir que as APIs de carregamento de recursos encontrem o conteúdo localizado.
  • Fornecer ao usuário final um mecanismo para ajustar a configuração de idioma do aplicativo.
  • Ajustar o código do runtime de modo a tirar proveito da configuração de idioma e tornar o aplicativo mais responsivo às necessidades do usuário final.