Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este tutorial demonstra como pegar um aplicativo monolíngüe e torná-lo pronto para o mundo. Este aplicativo está na forma de uma solução completa que é criada dentro do Microsoft Visual Studio.
- Visão geral
-
Configurando a solução Hello MUI
- Requisitos da plataforma
- Pré-requisitos
- Etapa 0: Criando o Hello MUI codificado
- Etapa 1: Implementando o módulo de recursos básicos
- Etapa 2: Construindo o módulo de recursos básicos
- Passo 3: Criar um Resource-Savvy "Hello MUI"
- Passo 4: Globalizar o "Hello MUI"
- Etapa 5: Personalizando o "Hello MUI"
- Considerações adicionais para MUI
- Resumo
Visão geral
Começando com o Windows Vista, o próprio sistema operacional Windows foi construído desde o início para ser uma plataforma multilíngue, com suporte adicional que permite criar aplicativos multilíngues que usam o modelo de recursos MUI do Windows.
Este tutorial ilustra esse novo suporte para aplicativos multilíngues, cobrindo os seguintes aspetos:
- Utiliza o suporte melhorado à plataforma MUI para permitir facilmente aplicações multilingues.
- Estende aplicativos multilíngues para serem executados em versões do Windows anteriores ao Windows Vista.
- Aborda as considerações adicionais envolvidas no desenvolvimento de aplicativos multilíngues especializados, como aplicativos de console.
Estes links ajudam a fornecer uma rápida atualização sobre os conceitos sobre internacionalização e MUI:
- Breve visão geral da internacionalização.
- Conceitos MUI.
- Usando MUI para desenvolver aplicativos Win32.
A ideia por trás do Hello MUI
Você provavelmente está familiarizado com o aplicativo clássico Hello World, que ilustra conceitos básicos de programação. Este tutorial adota uma abordagem semelhante para ilustrar como usar o modelo de recursos MUI para atualizar um aplicativo monolíngüe para criar uma versão multilíngue chamada Hello 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.
Configurando a solução Hello MUI
Estas etapas descrevem como se preparar para criar a solução Hello MUI.
Requisitos da plataforma
Você deve compilar os exemplos de código neste tutorial usando o Windows Software Development Kit (SDK) para Windows 7 e Visual Studio 2008. O SDK do Windows 7 será instalado no Windows XP, Windows Vista e Windows 7, e a solução de exemplo pode ser criada em qualquer uma dessas versões do sistema operacional.
Todos os exemplos de código neste tutorial foram projetados para serem executados nas versões x86 e x64 do Windows XP, Windows Vista e Windows 7. Partes específicas que não funcionarão no Windows XP são destacadas, quando apropriado.
Pré-requisitos
Instale o Visual Studio 2008.
Para obter mais informações, consulte o Visual Studio Developer Center.
Instale o SDK do Windows para Windows 7.
Você pode instalá-lo a partir da página SDK do Windows do Windows Developer Center. O SDK inclui utilitários para desenvolver aplicativos para versões do sistema operacional a partir do Windows XP até as mais recentes.
Observação
Se você 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.
Configure os parâmetros de linha de comando do Visual Studio.
- Abra uma janela de comando do Visual Studio.
- Digite definir caminho.
- Confirme se a variável path inclui o caminho da pasta bin do SDK do Windows 7: ... SDKs da Microsoft\Windows\v7.0\bin
Instale o pacote complementar de APIs de nível inferior do Microsoft NLS.
Observação
No contexto deste tutorial, este pacote é necessário somente se você estiver personalizando o aplicativo para ser executado em versões do Windows anteriores ao Windows Vista. Consulte Etapa 5: Personalizando o Hello MUI.
Transfira e instale o pacote, que já não está disponível no Centro de Transferências Microsoft . Use de APIs de globalização da UTI na Atualização de maio de 2019 do Windows 10 e versões posteriores.
Tal como acontece com o SDK do Windows, se você não estiver instalando o pacote no local padrão, ou se você não estiver instalando na unidade do sistema, que geralmente é a unidade C, anote o caminho de instalação.
Se sua plataforma de desenvolvimento for Windows XP ou Windows Server 2003, confirme se Nlsdl.dll está instalada e registrada corretamente.
- Navegue até à pasta "redist" no caminho de instalação.
- Execute o Nlsdl.* redistribuível apropriado.exe, como nlsdl.x86.exe. Esta etapa instala e registra Nlsdl.dll.
Observação
Se você desenvolver um aplicativo que usa MUI e que deve ser executado em versões do Windows anteriores ao Windows Vista, Nlsdl.dll deve estar presente na plataforma Windows de destino. Na maioria dos casos, isso significa que o aplicativo precisa incluir e instalar o Nlsdl redistribuível (e não apenas copiar Nlsdl.dll).
Etapa 0: Criando o Hello MUI codificado
Este tutorial começa com a versão monolíngüe do aplicativo Hello MUI. O aplicativo assume o uso da linguagem de programação C++, cadeias de caracteres largos e a funçãoMessageBoxW para saída.
Comece criando o aplicativo GuiStep_0 inicial, bem como a solução HelloMUI, que contém todos os aplicativos neste tutorial.
No Visual Studio 2008, crie um novo projeto. Use as seguintes configurações e valores:
- Tipo de projeto: Em Visual C++, selecione Win32 e, em seguida, em Modelos instalados do Visual Studio, selecione Projeto Win32.
- Nome: GuiStep_0.
- Local: ProjectRootDirectory (Etapas posteriores fazem referência a este diretório).
- Nome da solução: HelloMUI.
- Selecione Criar diretório para solução.
- No Assistente de aplicativo Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
Configure o modelo de encadeamento do projeto:
No Gerenciador de Soluções, clique com o botão direito do mouse na GuiStep_0 do projeto e selecione Propriedades.
Na caixa de diálogo Páginas de propriedades do projeto:
- No menu pendente no canto superior esquerdo, defina Configuração como Todas as Configurações.
- Em Propriedades de Configuração, expanda C/C++, selecione Geração de Código e defina Biblioteca de Tempo de Execução: Depuração Multi-threaded (/MTd).
Substitua o conteúdo do 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; }Compile e execute o aplicativo.
O código-fonte simples acima faz a escolha simplista de design ao fixar ou incorporar toda a saída que o utilizador verá — neste caso, o texto "Olá MUI". Essa escolha limita a utilidade do aplicativo para usuários que não reconhecem a palavra em inglês "Hello". Como MUI é um acrônimo em inglês baseado em tecnologia, presume-se para este tutorial que a string permanece "MUI" para todos os idiomas.
Etapa 1: Implementando o módulo de recursos básicos
O Microsoft Win32 há muito tempo expõe a capacidade de os desenvolvedores de aplicativos separarem seus dados de recursos da interface do usuário do código-fonte do aplicativo. Essa separação vem na forma do modelo de recursos Win32, no qual strings, bitmaps, ícones, mensagens e outros itens normalmente exibidos para um usuário são empacotados em uma seção distinta do executável, separados do código executável.
Para ilustrar essa separação entre código executável e pacote de dados de recurso, nesta etapa o tutorial coloca a cadeia de caracteres "Olá" previamente codificada (o recurso "en-US") na seção de recursos de um módulo DLL no projeto HelloModule_en_us.
Esta DLL Win32 também pode conter funcionalidade executável tipo biblioteca (como qualquer outra DLL poderia). Mas para ajudar a se concentrar nos aspetos relacionados a recursos do Win32, deixamos o código DLL em tempo de execução esgotado em dllmain.cpp. As seções subsequentes deste tutorial usam os dados de recursos do HelloModule que estão sendo criados aqui e também apresentam o código de tempo de execução apropriado.
Para construir um módulo de recurso Win32, comece por criar uma DLL com um dllmain esboçado.
Adicione um novo projeto à solução HelloMUI:
- No menu Arquivo, selecione Adicionar e, em seguida, Novo Projeto.
- Tipo de projeto: Projeto Win32.
- Nome: HelloModule_en_us.
- Localização: ProjectRootDirectory\HelloMUI.
- No Assistente de aplicativo Win32, selecione Tipo de aplicativo: DLL.
Configure o modelo de threading deste projeto:
No Gerenciador de Soluções, clique com o botão direito do mouse na HelloModule_en_us do projeto e selecione Propriedades.
Na caixa de diálogo Páginas de Propriedades do projeto:
- No menu suspenso superior esquerdo, defina Configuração como Todas as configurações.
- Em Propriedades de Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca de Tempo de Execução para Depuração Multi-threaded (/MTd).
Examine dllmain.cpp. (Você pode adicionar as macros UNREFERENCED_PARAMETER ao código gerado, como temos aqui, para permitir que ele seja compilado no nível de aviso 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; }Adicione um arquivo de definição de recurso HelloModule.rc ao projeto:
Na pasta Arquivos de Recursos do projeto HelloModule_en_us, clique com o botão direito do mouse, selecione Adicionar e selecione Novo Item.
Na caixa de diálogo Adicionar Novo Item, escolha o seguinte:
- Categorias: Em Visual C++, selecione Recurso e, em "Modelos instalados do Visual Studio", selecione Arquivo de Recurso (.rc).
- Nome: HelloModule.
- Localização: deixe no padrão.
- Clique em Adicionar.
Especifique que o novo arquivo HelloModule.rc deve ser salvo como Unicode:
- No Gerenciador de Soluções, clique com o botão direito do mouse em HelloModule.rc e selecione Exibir Código.
- Se vir uma mensagem a indicar que o ficheiro já está aberto e a perguntar se pretende fechá-lo, clique em Sim.
- Com o arquivo exibido como texto, faça a seleção do menu Arquivo e, em seguida, Opções avançadas de salvamento.
- Em Codificação, especifique Unicode - Codepage 1200.
- Clique em OK.
- Salve e feche HelloModule.rc.
Adicione uma tabela de cadeia de caracteres contendo a cadeia de caracteres "Olá":
Na Visualização de Recurso, clique com o botão direito do mouse em HelloModule.rc e selecione Adicionar Recurso.
Selecione Tabela de Strings.
Clique Novo.
Adicione uma cadeia de caracteres à tabela de cadeias de caracteres:
- Identificação: HELLO_MUI_STR_0.
- Valor: 0.
- Legenda: Olá.
Se você visualizar HelloModule.rc como texto agora, você verá várias partes do código-fonte específico do recurso. O de maior interesse é a seção que descreve a string "Olá":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "Hello" ENDEssa string "Olá" é o recurso que precisa ser localizado (ou seja, traduzido) em todos os idiomas que o aplicativo espera suportar. Por exemplo, o projeto HelloModule_ta_in (que você criará na próxima etapa) conterá sua própria versão localizada do HelloModule.rc para "ta-IN":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "வணக்கம்" ENDCrie o projeto HelloModule_en_us para ter certeza de que não há erros.
Crie mais seis projetos na solução HelloMUI (ou quantos deles desejar) para criar mais seis DLLs de recursos para idiomas adicionais. Use os valores nesta 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 Olá OláModulo_es_es HelloModule HELLO_MUI_STR_0 0 Olá 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 sintaxe e a estrutura do arquivo .rc, consulte Sobre arquivos de recursos.
Etapa 2: Construindo o módulo de recursos básicos
Usando modelos de recursos anteriores, a criação de qualquer um dos sete projetos HelloModule resultaria em sete DLLs separadas. Cada DLL conteria uma seção de recursos com uma única cadeia de caracteres localizada no idioma apropriado. Embora apropriado para o modelo de recursos histórico do Win32, esse design não aproveita o MUI.
No SDK do Windows Vista e posterior, o MUI fornece a capacidade de dividir executáveis em código-fonte e módulos de conteúdo localizáveis. Com a personalização adicional abordada posteriormente na Etapa 5, os aplicativos podem ser habilitados para suporte multilíngue para execução em versões anteriores ao Windows Vista.
Os principais mecanismos disponíveis para dividir recursos do código executável, começando com o Windows Vista, são:
- Utilizando rc.exe (o compilador RC) com opções específicas, ou
- Usar uma ferramenta de divisão de estilos depois da compilação, chamada muirct.exe.
Consulte Utilitários de Recursos para obter mais informações.
Por uma questão de simplicidade, este tutorial usa muirct.exe para dividir o executável "Hello MUI".
Dividindo os vários módulos de recursos de linguagem: uma visão geral
Um processo de várias partes está envolvido na divisão das DLLs em um HelloModule.dllexecutável, além de um .mui HelloModule.dllpara cada um dos sete idiomas suportados neste tutorial. Esta 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 utilizando o comando disponível apenas em inglês.
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 recursos entre a DLL LN (language-neutral) e os arquivos .mui dependentes do idioma. Nesse caso, o arquivo xml DoReverseMuiLoc.rcconfig (listado na próxima seção) indica muitos tipos de recursos, mas todos eles se enquadram na categoria de arquivo "localizedResources" ou .mui e não há recursos na categoria de idioma neutro. Para obter mais informações sobre como preparar um arquivo de configuração de recurso, consulte Preparando um arquivo de configuração de recurso.
Além de criar um arquivo .mui HelloModule.dllque contém a string em inglês "Hello", muirct.exe também incorpora um recurso MUI no módulo HelloModule.dll durante a divisão. Para carregar corretamente, em tempo de execução, os recursos apropriados dos módulos .mui de HelloModule.dllespecíficos do idioma, cada ficheiro .mui deve ter as suas somas de verificação ajustadas para corresponder às somas de verificação no módulo neutro em relação à linguagem LN de linha de base. Isso é feito por um comando como:
muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
Da mesma forma, muirct.exe é invocado para criar um arquivo .mui HelloModule.dllpara cada um dos outros idiomas. No entanto, nesses casos, a DLL com neutralidade de idioma é descartada, pois apenas a primeira criada será necessária. Os comandos que processam os recursos espanhóis e franceses têm esta aparência:
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 de comando muirct.exe acima é que o sinalizador -x é usado para especificar um ID de idioma de destino. O valor fornecido ao muirct.exe é diferente para cada módulo de HelloModule.dll específico do idioma. Esse valor de idioma é central e é usado por muirct.exe para marcar adequadamente o arquivo .mui durante a divisão. Um valor incorreto produz falhas de carregamento de recursos para esse arquivo .mui específico em tempo de execução. Consulte Language Identifier Constants and Strings para obter mais detalhes sobre como mapear o nome da linguagem para LCID.
Cada arquivo .mui dividido acaba sendo colocado em um diretório correspondente ao seu nome de idioma, imediatamente abaixo do diretório no qual o HelloModule.dll neutro de idioma residirá. Para obter mais informações sobre o posicionamento do arquivo .mui, consulte Application Deployment.
Dividindo os vários módulos de recursos de linguagem: Criando os 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ê pode reduzir o potencial de erros de compilação incluindo esses comandos como eventos pré ou pós-compilação na solução HelloMUI, mas isso está além do escopo deste tutorial.
Crie os ficheiros para o build de debug.
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 pauseCrie 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>Copie DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig para ProjectRootDirectory\HelloMUI\Debug.
Abra o prompt de comando do Visual Studio 2008 e navegue até o diretório Debug.
Execute DoReverseMuiLoc.cmd.
Quando você cria uma compilação de versão, copia os mesmos arquivos DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig para o diretório Release e executa o arquivo de comando lá.
Etapa 3: Criando um Resource-Savvy "Olá MUI"
Com base no exemplo inicial de GuiStep_0.exe codificado acima, você pode estender o alcance do aplicativo para vários usuários de idioma escolhendo incorporar o modelo de recursos do Win32. O novo código de tempo de execução apresentado nesta etapa inclui o carregamento do módulo (LoadLibraryEx) e a lógica de recuperação de cadeia de caracteres (LoadString).
Adicione um novo projeto à solução HelloMUI (usando a seleção de menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:
- Tipo de projeto: Projeto Win32.
- Nome: GuiStep_1.
- Localização: aceite o valor padrão.
- No Assistente de aplicativo Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
Defina este projeto para ser executado de dentro do Visual Studio e configure seu modelo de threading:
No Gerenciador de Soluções, clique com o botão direito do mouse no GuiStep_1 do projeto e selecione Definir como Projeto de Inicialização.
Clique com o botão direito do mouse nele novamente e selecione Propriedades.
Na caixa de diálogo "Páginas de Propriedades" do projeto:
- No menu suspenso superior esquerdo, defina Configuração como Todas as configurações.
- Em Propriedades de Configuração, expanda C/C++, selecione Geração de Código e defina a Biblioteca de Tempo de Execução para Depuração Multi-threaded (/MTd).
Substitua o conteúdo do 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; }Compile e execute o aplicativo. A saída será exibida no idioma atualmente definido como o idioma de exibição do computador (desde que seja um dos sete idiomas que construímos).
Passo 4: Globalizar "Hello MUI"
Embora o exemplo anterior seja capaz de exibir seus resultados em diferentes idiomas, ele fica aquém em várias áreas. Talvez o mais notável é que o aplicativo só está disponível em um pequeno subconjunto de idiomas quando comparado ao próprio sistema operacional Windows. Se, por exemplo, o aplicativo GuiStep_1 da etapa anterior fosse instalado em uma compilação japonesa do Windows, falhas de localização de recursos seriam prováveis.
Para resolver essa situação, você tem duas opções principais:
- Certifique-se de que os recursos em um idioma de reserva último predeterminado estejam incluídos.
- Fornecer uma maneira para o usuário final configurar suas preferências de idioma entre o subconjunto de idiomas especificamente suportados pelo aplicativo.
Dessas opções, a que fornece uma linguagem de fallback final é altamente aconselhável e é implementada neste tutorial, passando o sinalizador -g para muirct.exe, como visto acima. Este indicador informa muirct.exe para definir um idioma específico (de-DE / 0x0407) como o idioma de reserva final associado ao módulo DLL neutro em relação ao idioma (HelloModule.dll). Em tempo de execução, esse parâmetro é usado como último recurso para gerar texto para exibir ao usuário. No caso de uma linguagem de fallback final não ser encontrada e nenhum recurso apropriado estiver disponível no binário de idioma neutro, o carregamento do recurso falhará. Portanto, você deve determinar cuidadosamente os cenários que seu aplicativo pode encontrar e planejar a linguagem de 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 muito a satisfação do cliente. Infelizmente, isso também complica a funcionalidade necessária dentro do aplicativo.
Esta 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 em tempo de execução pelo aplicativo e a lista de idiomas analisada e validada é usada para estabelecer uma lista de fallback personalizada. Depois que a lista de fallback personalizada 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.
Adicione um novo projeto à solução HelloMUI (usando a seleção de menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:
- Tipo de projeto: Projeto Win32.
- Nome: GuiStep_2.
- Localização: aceitar o padrão.
- No Assistente de aplicativo Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
Defina este projeto para ser executado de dentro do Visual Studio e configure seu modelo de threading:
No Gerenciador de Soluções, clique com o botão direito do mouse na GuiStep_2 do projeto e selecione Definir como Projeto de Inicialização.
Clique com o botão direito do mouse nele novamente e selecione Propriedades.
Na caixa de diálogo Páginas de propriedades do projeto:
- No menu suspenso superior esquerdo, defina Configuração como Todas as configurações.
- Em Propriedades de Configuração, expanda C/C++, selecione Geração de Código e defina Runtime Library: Multi-threaded Debug (/MTd).
Substitua o conteúdo do 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; }Crie um arquivo de texto Unicode langs.txt contendo a seguinte linha:
hi-IN ta-IN ru-RU fr-FR es-ES en-USObservação
Certifique-se de salvar o arquivo como Unicode.
Copie langs.txt para o diretório a partir do qual o programa será executado:
- Se estiver sendo executado de dentro do Visual Studio, copie-o para ProjectRootDirectory\HelloMUI\GuiStep_2.
- Se estiver sendo executado a partir do Windows Explorer, copie-o para o mesmo diretório que GuiStep_2.exe.
Crie e execute o projeto. Tente editar langs.txt para que diferentes idiomas apareçam na frente da lista.
Etapa 5: Personalizando o "Hello MUI"
Vários dos recursos de tempo de execução mencionados até agora neste tutorial estão disponíveis apenas no Windows Vista e posterior. Você pode querer reutilizar o esforço investido na localização e divisão de recursos, fazendo com que o aplicativo funcione em versões de nível inferior do sistema operacional Windows, como o Windows XP. Este processo envolve o ajuste do exemplo anterior em duas áreas principais:
As funções de carregamento de recursos anteriores ao Windows Vista (como LoadString, LoadIcon, LoadBitmap, FormatMessagee outras) não reconhecem MUI. Os aplicativos fornecidos com recursos divididos (arquivos LN e .mui) devem carregar módulos de recursos usando uma destas duas funções:
- Se o aplicativo deve ser executado somente no Windows Vista e posterior, ele deve carregar módulos de recursos com LoadLibraryEx.
- Se o aplicativo deve ser executado em versões anteriores ao Windows Vista, bem como o Windows Vista ou posterior, ele deve usar LoadMUILibrary, que é uma função de nível inferior específica fornecida no SDK do Windows 7.
O gerenciamento de idiomas e o suporte à ordem de fallback de idioma oferecidos em versões anteriores ao Windows Vista do sistema operacional Windows diferem significativamente daquele no Windows Vista e posteriores. Por esse motivo, os aplicativos que permitem fallback de idioma configurado pelo usuário devem ajustar suas práticas de gerenciamento de idioma:
- Se o aplicativo deve ser executado somente no Windows Vista e posterior, definir a lista de idiomas usando SetThreadPreferredUILanguages é suficiente.
- Se o aplicativo deve ser executado em todas as versões do Windows, deve ser construído código que será executado em plataformas mais antigas para percorrer a lista de idiomas configurada pelo usuário e procurar o módulo de recurso desejado. Isso pode ser visto nas seções 1c e 2 do código fornecido mais adiante nesta etapa.
Crie um projeto que possa usar os módulos de recursos localizados em qualquer versão do Windows:
Adicione um novo projeto à solução HelloMUI (usando a seleção de menu Arquivo, Adicionar e Novo Projeto) com as seguintes configurações e valores:
- Tipo de projeto: Projeto Win32.
- Nome: GuiStep_3.
- Localização: aceite o padrão.
- No Assistente de aplicativo Win32, selecione o tipo de aplicativo padrão: aplicativo do Windows.
Defina este projeto para ser executado de dentro do Visual Studio e configure seu modelo de threading. Além disso, configure-o para adicionar os cabeçalhos e bibliotecas 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 Microsoft NLS foram instalados em seus diretórios padrão. Se não for esse o caso, modifique os caminhos adequadamente.
No Gerenciador de Soluções, clique com o botão direito do mouse na GuiStep_3 do projeto e selecione Definir como Projeto de Inicialização.
Clique com o botão direito do mouse nele novamente e selecione Propriedades.
Na caixa de diálogo Páginas de Propriedades do projeto:
No menu suspenso superior esquerdo, defina Configuração como Todas as configurações.
Em Propriedades de Configuração, expanda C/C++, selecione Geração de Código e defina Biblioteca de Tempo de Execução: Depuração Multi-threaded (/MTd).
Selecione Geral e adicione a Diretórios de inclusão adicionais:
- "C:\Microsoft NLS Downlevel APIs\Include".
Selecione Idioma e defina Tratar wchar_t como Tipo incorporado: Não (/Zc:wchar_t-).
Selecione Avançado e defina Convenção de chamada: _stdcall (/Gz).
Em Propriedades de Configuração, expanda Linker, selecione Entrada e adicione às Dependências Adicionais:
- "C:\Arquivos de Programas\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
- "C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib".
Substitua o conteúdo do 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; }Crie ou copie langs.txt para o diretório apropriado, conforme descrito anteriormente em Etapa 4: Globalizando "Hello MUI".
Crie e execute o projeto.
Observação
Se o aplicativo deve 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 Microsoft NLS sobre como redistribuir Nlsdl.dll. (Isso não está mais disponível no Centro de Download da Microsoft. Use APIs de globalização ICU na Atualização de maio de 2019 do Windows 10 e versões posteriores.)
Considerações adicionais para 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 GUI padrão, a janela de comando do Windows não pode exibir caracteres para todos os idiomas. Por esta razão, as aplicações de consola multilingues 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 sondas de recursos linguísticos para idiomas específicos que não são normalmente exibidos numa janela de comando. Quando esses sinalizadores são definidos, os algoritmos de configuração de idioma permitem que apenas 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 criar um aplicativo de console multilíngue, consulte as seções de comentários do SetThreadUILanguage e SetThreadPreferredUILanguages.
Determinação dos idiomas a serem suportados no Run-Time
Você pode adotar uma das seguintes sugestões de design para determinar quais idiomas seu aplicativo deve suportar em tempo de execução:
Durante a instalação, permita que o usuário final selecione o idioma preferido em uma lista de idiomas suportados
Ler uma lista de idiomas a partir de um arquivo de configuração
Alguns dos projetos 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 entrada externa, valide os idiomas fornecidos como entrada. Consulte 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 a mesma linguagem do sistema operacional. Embora isso não exija solicitação do usuário, se você escolher essa opção, esteja ciente de que os idiomas do sistema operacional podem ser adicionados ou removidos a qualquer momento e podem ser alterados depois que o usuário instala o aplicativo. Além disso, esteja ciente de que, em alguns casos, o sistema operacional é instalado com suporte limitado a idiomas, e o aplicativo oferece mais valor se ele suporta idiomas que o sistema operacional não suporta.
Para obter mais informações sobre como determinar os idiomas atualmente instalados no sistema operacional, consulte a funçãoEnumUILanguages.
Suporte a scripts complexos em versões anteriores ao Windows Vista
Quando um aplicativo que oferece suporte a determinados scripts complexos é executado em uma versão do Windows anterior ao Windows Vista, o texto nesse script pode não ser exibido corretamente nos componentes da GUI. Por exemplo, no projeto de nível inferior deste tutorial, os scripts hi-IN e ta-IN podem não ser exibidos na caixa de mensagem devido a problemas com o processamento de scripts complexos e à falta de fontes relacionadas. Normalmente, problemas desta natureza apresentam-se como caixas quadradas no componente GUI.
Mais informações sobre como habilitar o processamento de scripts complexos podem ser encontradas em Suporte a Scripts e Fontes no Windows.
Resumo
Este tutorial globalizou um aplicativo monolíngüe e demonstrou as seguintes práticas recomendadas.
- Projete o aplicativo para aproveitar o modelo de recursos do Win32.
- Utilizar a divisão MUI de recursos em binários de satélite (ficheiros .mui).
- Certifique-se de que o processo de localização atualize os recursos nos arquivos .mui para que sejam apropriados para o idioma de destino.
- Certifique-se de que o empacotamento e a implantação do aplicativo, dos arquivos .mui associados e do conteúdo de configuração sejam feitos corretamente para 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.
- Ajuste o código de tempo de execução para aproveitar a configuração de linguagem, para tornar o aplicativo mais responsivo às necessidades do usuário final.