Adición de compatibilidad con Interfaz de usuario multilingüe a una aplicación
En este tutorial se muestra cómo tomar una aplicación monolingüe y prepararla para todo el mundo. Esta aplicación tiene la forma de una solución completa que se compila en Microsoft Visual Studio.
- Información general
- Configuración de la solución Hello MUI
- Paso 0: Creación de Hello MUI codificada de forma rígida
- Paso 1: Implementación del módulo de recursos básico
- Paso 2: Creación del módulo de recursos básico
- Paso 3: Creación de los recursos de "Hello MUI"
- Paso 4: Globalización de "Hello MUI"
- Paso 5: Personalización de "Hello MUI"
- Consideraciones adicionales de MUI
- Resumen
Información general
A partir de Windows Vista, el propio sistema operativo Windows se creó desde cero para ser una plataforma multilingüe, con compatibilidad adicional que le permite crear aplicaciones multilingües que usan el modelo de recursos MUI de Windows.
En este tutorial se muestra esta nueva compatibilidad con aplicaciones multilingües, para lo que se tratan los siguientes aspectos:
- Usa la compatibilidad mejorada con la plataforma MUI para habilitar fácilmente aplicaciones multilingües.
- Amplía las aplicaciones multilingües para que se ejecuten en versiones de Windows anteriores a Windows Vista.
- Menciona consideraciones adicionales implicadas en el desarrollo de aplicaciones multilingües especializadas, como las aplicaciones de consola.
Estos vínculos ayudan a proporcionar un repaso rápido de los conceptos sobre internacionalización y MUI:
- Introducción rápida a la internacionalización.
- Conceptos de MUI.
- Uso de MUI para desarrollar aplicaciones Win32.
La idea detrás de Hello MUI
Probablemente esté familiarizado con la aplicación clásica Hola mundo, que ilustra conceptos básicos de programación. En este tutorial se adopta un enfoque similar para ilustrar cómo usar el modelo de recursos MUI para actualizar una aplicación monolingüe y crear una versión multilingüe denominada Hello MUI.
Nota:
Las tareas de este tutorial se describen en pasos detallados debido a la precisión con la que se deben realizar estas actividades y la necesidad de explicar los detalles a los desarrolladores que tienen poca experiencia con estas tareas.
Configuración de la solución Hello MUI
En estos pasos se describe cómo prepararse para crear la solución Hello MUI.
Requisitos de la plataforma
Debe compilar los ejemplos de código de este tutorial mediante el Kit de desarrollo de software (SDK) de Windows para Windows 7 y Visual Studio 2008. El SDK de Windows 7 se instalará en Windows XP, Windows Vista y Windows 7, y la solución de ejemplo se puede compilar en cualquiera de estas versiones del sistema operativo.
Todos los ejemplos de código de este tutorial están diseñados para ejecutarse en versiones x86 y x64 de Windows XP, Windows Vista y Windows 7. Las partes específicas que no funcionarán en Windows XP se destacarán cuando proceda.
Requisitos previos
Instale Visual Studio 2008.
Para más información, vea el Centro para desarrolladores de Visual Studio.
Instale Windows SDK para Windows 7.
Puede instalarlo desde la página Windows SDK del Centro para desarrolladores de Windows. El SDK incluye utilidades para desarrollar aplicaciones para versiones del sistema operativo a partir de Windows XP hasta las más recientes.
Nota:
Si no va a instalar el paquete en la ubicación predeterminada, ni en la unidad del sistema, que suele ser la unidad C, anote la ruta de acceso de instalación.
Configure los parámetros de la línea de comandos de Visual Studio.
- Abra una ventana de comandos de Visual Studio.
- Escriba set path.
- Confirme que la variable path incluye la ruta de acceso de la carpeta bin del SDK de Windows 7: ...Microsoft SDKs\Windows\v7.0\bin
Instale el paquete de complemento de las API de nivel inferior de NLS de Microsoft.
Nota:
En el contexto de este tutorial, este paquete solo es necesario si va a personalizar la aplicación para que se ejecute en versiones de Windows anteriores a Windows Vista. Vea Paso 5: Personalización de Hello MUI.
Descargue e instale el paquete, que ya no está disponible en el Centro de descarga de Microsoft. Use las API de globalización de ICU en la actualización de mayo de 2019 de Windows 10 y versiones posteriores.
Como sucede con Windows SDK, si no va a instalar el paquete en la ubicación predeterminada, ni en la unidad del sistema, que suele ser la unidad C, anote la ruta de acceso de instalación.
Si la plataforma de desarrollo es Windows XP o Windows Server 2003, confirme que Nlsdl.dll está instalado y registrado correctamente.
- Vaya a la carpeta "redist" en la ubicación de la ruta de instalación.
- Ejecute el redistribuible Nlsdl.*.exe adecuado, como nlsdl.x86.exe. En este paso se instala y registra Nlsdl.dll.
Nota:
Si desarrolla una aplicación que usa MUI y que se debe ejecutar en versiones de Windows anteriores a Windows Vista, Nlsdl.dll debe estar presente en la plataforma Windows de destino. En la mayoría de los casos, esto significa que la aplicación debe incluir e instalar el instalador de Nlsdl redistribuible (y no simplemente copiar Nlsdl.dll).
Paso 0: Creación de Hello MUI codificada de forma rígida
Este tutorial comienza con la versión monolingüe de la aplicación Hello MUI. La aplicación supone el uso del lenguaje de programación C++, cadenas de caracteres anchos y la función MessageBoxW para la salida.
Para empezar, cree la aplicación GuiStep_0 inicial, así como la solución HelloMUI, que contiene todas las aplicaciones de este tutorial.
En Visual Studio 2008, cree un proyecto. Use la siguiente configuración y valores:
- Tipo de proyecto: en Visual C++, seleccione Win32 y, en Plantillas instaladas de Visual Studio, seleccione Proyecto de Win32.
- Nombre: GuiStep_0.
- Ubicación: ProjectRootDirectory (en pasos posteriores se hace referencia a este directorio).
- Nombre de la solución: HelloMUI.
- Seleccione Crear directorio para la solución.
- En el Asistente para aplicaciones Win32, seleccione el tipo de aplicación predeterminado: Aplicación Windows.
Configure el modelo de subprocesos del proyecto:
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto GuiStep_0 y, después, seleccione Propiedades.
En el cuadro de diálogo Páginas de propiedades del proyecto:
- En la lista desplegable superior izquierda, establezca Configuración en Todas las configuraciones.
- En Propiedades de configuración, expanda C/C++, seleccione Generación de código y establezca Biblioteca en tiempo de ejecución: Depuración multiproceso (/MTd).
Reemplace el contenido de GuiStep_0.cpp por el código siguiente:
// 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; }
Genere y ejecute la aplicación.
El código fuente sencillo anterior hace la elección de diseño simplista de codificar de forma rígida, o insertar, toda la salida que verá el usuario, en este caso el texto "Hello MUI". Esta opción limita la utilidad de la aplicación para los usuarios que no reconocen la palabra en inglés "Hello". Como MUI es un acrónimo inglés basado en tecnología, en este tutorial se supone que la cadena sigue siendo "MUI" para todos los idiomas.
Paso 1: Implementación del módulo de recursos básico
Microsoft Win32 ha expuesto durante mucho tiempo la funcionalidad para los desarrolladores de aplicaciones de separar sus datos de recursos de interfaz de usuario del código fuente de la aplicación. Esta separación adopta la forma del modelo de recursos win32, en el que las cadenas, mapas de bits, iconos, mensajes y otros elementos que se muestran normalmente a un usuario se empaquetan en una sección distinta del ejecutable, separadas del código ejecutable.
Para ilustrar esta separación entre el código ejecutable y el empaquetado de datos de recursos, en este paso del tutorial se coloca la cadena "Hello" codificada previamente (el recurso "en-US") en la sección de recursos de un módulo DLL del proyecto HelloModule_en_us.
Este archivo DLL de Win32 también puede contener la funcionalidad ejecutable de tipo de biblioteca (como cualquier otro archivo DLL). Pero para ayudar a centrarse en los aspectos relacionados con los recursos de Win32, se deja el código DLL en tiempo de ejecución guardado en dllmain.cpp. En secciones posteriores de este tutorial se usan los datos de recursos HelloModule que se compilan aquí y también se presenta el código en tiempo de ejecución adecuado.
Para construir un módulo de recursos Win32, empiece por crear un archivo DLL con un archivo dllmain de código auxiliar:
Agregue un nuevo proyecto a la solución HelloMUI:
- En el menú Archivo, seleccione Agregar, y luego, Nuevo proyecto.
- Tipo de proyecto: Proyecto Win32.
- Name: HelloModule_en_us.
- Ubicación: ProjectRootDirectory\HelloMUI.
- En el Asistente para aplicaciones Win32, seleccione Tipo de aplicación: DLL.
Configure el modelo de subprocesos de este proyecto:
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto HelloModule_en_us y seleccione Propiedades.
En el cuadro de diálogo Páginas de propiedades del proyecto:
- En la lista desplegable superior izquierda, establezca Configuración en Todas las configuraciones.
- En Propiedades de configuración, expanda C/C++, seleccione Generación de código y establezca Biblioteca en tiempo de ejecución: Depuración multiproceso (/MTd).
Examine dllmain.cpp. (Es posible que quiera agregar las macros UNREFERENCED_PARAMETER al código generado, como aquí, a fin de habilitarlo para compilarlo en el nivel de advertencia 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; }
Agregue un archivo de definición de recursos HelloModule.rc al proyecto:
En el proyecto HelloModule_en_us, haga clic con el botón derecho en la carpeta Archivos de recursos, seleccione Agregar y después Nuevo elemento.
En el cuadro de diálogo Agregar nuevo elemento, elija lo siguiente:
- Categorías: En Visual C++, seleccione Recurso y, después, en "Plantillas instaladas de Visual Studio" seleccione Archivo de recursos (.rc).
- Name: HelloModule.
- Ubicación: acepte la predeterminada.
- Haga clic en Agregar.
Especifique que el nuevo archivo HelloModule.rc se va a guardar como Unicode:
- En el Explorador de soluciones, haga clic con el botón derecho en HelloModule.rc y seleccione Ver código.
- Si ve un mensaje que indica que el archivo ya está abierto y se le pregunta si quiere cerrarlo, haga clic en Sí.
- Con el archivo mostrado como texto, seleccione el menú Archivo y, después, Opciones avanzadas para guardar.
- En Codificación, especifique Unicode - Página de códigos 1200.
- Haga clic en OK.
- Guarde y cierre HelloModule.rc.
Agregue una tabla de cadenas que contenga la cadena "Hello":
En la vista de recursos, haga clic con el botón derecho en HelloModule.rc y seleccione Agregar recurso.
Seleccione Tabla de cadenas.
Haga clic en Nueva.
Agregue una cadena a la tabla de cadenas:
- ID: HELLO_MUI_STR_0.
- Valor: 0.
- Título: Hola.
Si ahora ve HelloModule.rc como texto, verá varios fragmentos de código fuente específico del recurso. El de mayor interés es la sección en la que se describe la cadena "Hello":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "Hello" END
Esta cadena "Hello" es el recurso que se debe localizar (es decir, traducir) en cada idioma que la aplicación espera admitir. Por ejemplo, el proyecto HelloModule_ta_in (que creará en el paso siguiente) contendrá su propia versión localizada de HelloModule.rc para "ta-IN":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "வணக்கம்" END
Compile el proyecto HelloModule_en_us para asegurarse de que no hay errores.
Cree seis proyectos más en la solución HelloMUI (o tantos como quiera) para crear seis archivos DLL de recursos más para idiomas adicionales. Use los valores de esta tabla:
Nombre de proyecto Nombre del archivo .rc Id. de cadena Valor de cadena Subtítulo de cadena 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 más información sobre la sintaxis y la estructura de los archivos .rc, vea Acerca de los archivos de recursos.
Paso 2: Creación del módulo de recursos básico
Con los modelos de recursos anteriores, la compilación de cualquiera de los siete proyectos HelloModule daría como resultado siete archivos DLL independientes. Cada DLL contendrá una sección de recursos con una sola cadena localizada en el idioma adecuado. Aunque es adecuado para el modelo de recursos histórico de Win32, este diseño no saca provecho de MUI.
En el SDK de Windows Vista y versiones posteriores, MUI proporciona la capacidad de dividir archivos ejecutables en módulos de contenido localizable y código fuente. Con la personalización adicional que se trata más adelante en el paso 5, las aplicaciones se pueden habilitar para la compatibilidad multilingüe a fin de que se ejecuten en versiones anteriores a Windows Vista.
Los mecanismos principales disponibles para dividir los recursos del código ejecutable, a partir de Windows Vista, son los siguientes:
- Usar rc.exe (el compilador de RC) con modificadores específicos, o bien
- Usar una herramienta de estilo de división posterior a la compilación denominada muirct.exe.
Vea Utilidades de recursos para más información.
Por motivos de simplicidad, en este tutorial se usa muirct.exe para dividir el ejecutable "Hello MUI".
Separación de los distintos módulos de recursos de lenguaje: introducción
La separación de los archivos DLL en un archivo ejecutable HelloModule.dll, además de un helloModule.dll.mui para cada uno de los siete idiomas admitidos en este tutorial es un proceso de varias partes. En esta introducción se describen los pasos necesarios; en la sección siguiente se presenta un archivo de comandos que realiza esos pasos.
En primer lugar, divida el módulo HelloModule.dll solo en inglés mediante el 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
En la línea de comandos anterior se usa el archivo de configuración DoReverseMuiLoc.rcconfig. Este tipo de archivo de configuración se usa normalmente en muirct.exe para dividir los recursos entre el archivo DLL independiente del idioma (LN) y los archivos .mui dependientes del idioma. En este caso, el archivo XML DoReverseMuiLoc.rcconfig (enumerado en la sección siguiente) indica muchos tipos de recursos, pero todos se dividen en la categoría "localizedResources" o archivo .mui, y no hay recursos en la categoría de idioma neutro. Para más información sobre cómo preparar un archivo de configuración de recursos, vea Preparación de un archivo de configuración de recursos.
Además de crear un archivo HelloModule.dll.mui que contiene la cadena en inglés "Hello", muirct.exe también inserta un recurso MUI en el módulo HelloModule.dll durante la separación. Para cargar correctamente en tiempo de ejecución los recursos adecuados de los módulos HelloModule.dll.mui específicos del idioma, en cada archivo .mui se deben fijar las sumas de comprobación para que coincidan con las del módulo LN independiente del idioma de línea base. Esto se realiza con un comando como el siguiente:
muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
Del mismo modo, se invoca muirct.exe para crear un archivo HelloModule.dll.mui para cada uno de los otros idiomas. Pero en esos casos se descarta la DLL independiente del idioma, ya que solo se necesitará la primera. Los comandos que procesan los recursos en español y francés tienen este aspecto:
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
Uno de los aspectos más importantes que se debe observar en las líneas de comandos muirct.exe anteriores es que se usa la marca -x para especificar un identificador de idioma de destino. El valor proporcionado a muirct.exe es diferente para cada módulo HelloModule.dll específico del idioma. Este valor de idioma es central y lo usa muirct.exe para marcar adecuadamente el archivo .mui durante la separación. Un valor incorrecto genera errores de carga de recursos para ese archivo .mui determinado en tiempo de ejecución. Vea Constantes y cadenas de identificador de idioma para más información sobre cómo asignar el nombre del idioma a LCID.
Cada archivo .mui dividido se colocar en un directorio correspondiente a su nombre de idioma, inmediatamente debajo del directorio en el que residirá HelloModule.dll de idioma neutro. Para más información sobre la colocación de los archivos .mui, vea Implementación de aplicaciones.
Separación de los distintos módulos de recursos de lenguaje: creación de los archivos
En este tutorial se crea un archivo de comandos que contiene los comandos para dividir los distintos archivos DLL y se invoca manualmente. Tenga en cuenta que, en el trabajo de desarrollo real, podría reducir la posibilidad de errores de compilación mediante la inclusión de estos comandos como eventos previos o posteriores a la compilación en la solución HelloMUI, pero eso está fuera del ámbito de este tutorial.
Cree los archivos para la compilación de depuración:
Cree un archivo de comandos DoReverseMuiLoc.cmd que contenga los siguientes 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
Cree un archivo XML DoReverseMuiLoc.rcconfig que contenga las líneas siguientes:
<?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 y DoReverseMuiLoc.rcconfig en ProjectRootDirectory\HelloMUI\Debug.
Abra el símbolo de comandos de Visual Studio 2008 y vaya al directorio Depurar.
Ejecute DoReverseMuiLoc.cmd.
Al crear una compilación de versión, copie los mismos archivos DoReverseMuiLoc.cmd y DoReverseMuiLoc.rcconfig en el directorio Release y ejecute allí el archivo de comandos.
Paso 3: Creación de los recursos de "Hello MUI"
Basándose en el ejemplo inicial GuiStep_0.exe anterior codificado de forma rígida, podría ampliar el alcance de la aplicación a usuarios de varios idiomas si incorpora el modelo de recursos de Win32. El nuevo código en tiempo de ejecución presentado en este paso incluye la lógica de carga de módulos (LoadLibraryEx) y recuperación de cadenas (LoadString).
Agregue un nuevo proyecto a la solución HelloMUI (mediante la selección de menú Archivo, Agregar y Nuevo proyecto) con los siguientes valores :
- Tipo de proyecto: Proyecto Win32.
- Nombre: GuiStep_1.
- Ubicación: acepte la predeterminada.
- En el Asistente para aplicaciones Win32, seleccione el tipo de aplicación predeterminado: Aplicación Windows.
Establezca este proyecto para que se ejecute desde Visual Studio y configure su modelo de subprocesos:
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto GuiStep_1 y seleccione Establecer como proyecto de inicio.
Vuelva a hacer clic con el botón derecho y seleccione Propiedades.
En el cuadro de diálogo Páginas de propiedades del proyecto:
- En la lista desplegable superior izquierda, establezca Configuración en Todas las configuraciones.
- En Propiedades de configuración, expanda C/C++, seleccione Generación de código y establezca Biblioteca en tiempo de ejecución: Depuración multiproceso (/MTd).
Reemplace el contenido de GuiStep_1.cpp por el código siguiente:
// 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; }
Genere y ejecute la aplicación. La salida se mostrará en el idioma establecido actualmente como idioma para mostrar del equipo (siempre que sea uno de los siete que se han creado).
Paso 4: Globalización de "Hello MUI"
Aunque el ejemplo anterior es capaz de mostrar su salida en diferentes idiomas, se queda corto en una serie de áreas. Quizás lo más notable es que la aplicación solo está disponible en un pequeño subconjunto de idiomas en comparación con el propio sistema operativo Windows. Si, por ejemplo, la aplicación GuiStep_1 del paso anterior se instalara en una compilación japonesa de Windows, es probable que se produjeran errores de ubicación de recursos.
Para solucionar esta situación, tiene dos opciones principales:
- Asegúrese de que se incluyen los recursos de un idioma de reserva final predeterminado.
- Proporcione una manera de que el usuario final configure sus preferencias de idioma entre el subconjunto de idiomas admitidos específicamente por la aplicación.
De estas opciones, la que proporciona un idioma de reserva definitivo es muy recomendable y se implementa en este tutorial, para lo que se pasa la marca -g a muirct.exe, como se ha visto anteriormente. Esta marca indica a muirct.exe que convierta un idioma específico (de-DE / 0x0407) en el idioma de reserva asociado al módulo dll de idioma neutral (HelloModule.dll). En tiempo de ejecución, este parámetro se usa como último recurso a fin de generar texto para mostrar al usuario. En caso de que no se encuentre un idioma de reserva final y no haya ningún recurso adecuado disponible en el binario de idioma neutro, se produce un error en la carga del recurso. Por tanto, debe determinar cuidadosamente los escenarios que la aplicación podría encontrar y planear el idioma de reserva final en consecuencia.
La otra opción de permitir preferencias de idioma configurables y cargar recursos en función de esta jerarquía definida por el usuario puede aumentar considerablemente la satisfacción del cliente. Desafortunadamente, también complica la funcionalidad necesaria dentro de la aplicación.
En este paso del tutorial se usa un mecanismo simplificado de archivos de texto para habilitar la configuración personalizada del idioma del usuario. La aplicación analiza el archivo de texto en tiempo de ejecución y la lista de idiomas analizados y validados se usa para establecer una lista de reserva personalizada. Una vez que se establece la lista de reserva personalizada, las API de Windows cargarán los recursos según la precedencia de idioma establecida en esta lista. El resto del código es similar al del paso anterior.
Agregue un nuevo proyecto a la solución HelloMUI (mediante la selección de menú Archivo, Agregar y Nuevo proyecto) con los siguientes valores :
- Tipo de proyecto: Proyecto Win32.
- Nombre: GuiStep_2.
- Ubicación: acepte la predeterminada.
- En el Asistente para aplicaciones Win32, seleccione el tipo de aplicación predeterminado: Aplicación Windows.
Establezca este proyecto para que se ejecute desde Visual Studio y configure su modelo de subprocesos:
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto GuiStep_2 y seleccione Establecer como proyecto de inicio.
Vuelva a hacer clic con el botón derecho y seleccione Propiedades.
En el cuadro de diálogo Páginas de propiedades del proyecto:
- En la lista desplegable superior izquierda, establezca Configuración en Todas las configuraciones.
- En Propiedades de configuración, expanda C/C++, seleccione Generación de código y establezca Biblioteca en tiempo de ejecución: Depuración multiproceso (/MTd).
Reemplace el contenido de GuiStep_2.cpp por el código siguiente:
// 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; }
Cree un archivo de texto langs.txt de Unicode que contenga la línea siguiente:
hi-IN ta-IN ru-RU fr-FR es-ES en-US
Nota:
Asegúrese de guardar el archivo como Unicode.
Copie langs.txt en el directorio desde el que se ejecutará el programa:
- Si se va a ejecutar desde Visual Studio, cópielo en ProjectRootDirectory\HelloMUI\GuiStep_2.
- Si se va a ejecutar desde el Explorador de Windows, cópielo en el mismo directorio que GuiStep_2.exe.
Compile y ejecute el proyecto. Pruebe a editar langs.txt para que los distintos idiomas aparezcan al principio de la lista.
Paso 5: Personalización de "Hello MUI"
Varias de las características en tiempo de ejecución mencionadas hasta ahora en este tutorial solo están disponibles en Windows Vista y versiones posteriores. Es posible que quiera volver a usar el esfuerzo invertido en localizar y dividir recursos, y hacer que la aplicación funcione en versiones del sistema operativo Windows de nivel inferior, como Windows XP. Este proceso implica ajustar el ejemplo anterior en dos áreas clave:
Las funciones de carga de recursos anteriores a Windows Vista (como LoadString, LoadIcon, LoadBitmap, FormatMessage y otras) no son compatibles con MUI. Las aplicaciones que se envían con recursos divididos (archivos LN y .mui) deben cargar módulos de recursos mediante una de estas dos funciones:
- Si la aplicación solo se va a ejecutar en Windows Vista y versiones posteriores, debe cargar módulos de recursos con LoadLibraryEx.
- Si la aplicación se va a ejecutar en versiones anteriores a Windows Vista, así como en Windows Vista o posteriores, debe usar LoadMUILibrary, que es una función de nivel inferior específica proporcionada en el SDK de Windows 7.
La compatibilidad con la administración de idiomas y el orden de reserva de idiomas que se ofrece en versiones anteriores a Windows Vista del sistema operativo Windows difiere significativamente en Windows Vista y versiones posteriores. Por este motivo, las aplicaciones que permiten la reserva de idioma configurada por el usuario deben ajustar sus procedimientos de administración de idiomas:
- Si la aplicación solo se va a ejecutar en Windows Vista y versiones posteriores, basta con establecer la lista de idiomas mediante SetThreadPreferredUILanguages.
- Si la aplicación se va a ejecutar en todas las versiones de Windows, se debe crear código que se ejecutará en plataformas de nivel inferior para iterar por la lista de idiomas configurada por el usuario y sondear el módulo de recursos deseado. Esto se puede ver en las secciones 1c y 2 del código proporcionado más adelante en este paso.
Cree un proyecto que pueda usar los módulos de recursos localizados en cualquier versión de Windows:
Agregue un nuevo proyecto a la solución HelloMUI (mediante la selección de menú Archivo, Agregar y Nuevo proyecto) con los siguientes valores :
- Tipo de proyecto: Proyecto Win32.
- Nombre: GuiStep_3.
- Ubicación: acepte la predeterminada.
- En el Asistente para aplicaciones Win32, seleccione el tipo de aplicación predeterminado: Aplicación Windows.
Establezca este proyecto para que se ejecute desde Visual Studio y configure su modelo de subprocesos. Además, configúrelo para agregar los encabezados y bibliotecas necesarios.
Nota:
En las rutas de acceso que se usan en este tutorial se asumen que el SDK de Windows 7 y el paquete de API de nivel inferior de Microsoft NLS se han instalado en sus directorios predeterminados. Si no es así, modifique las rutas de acceso de forma adecuada.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto GuiStep_3 y seleccione Establecer como proyecto de inicio.
Vuelva a hacer clic con el botón derecho y seleccione Propiedades.
En el cuadro de diálogo Páginas de propiedades del proyecto:
En la lista desplegable superior izquierda, establezca Configuración en Todas las configuraciones.
En Propiedades de configuración, expanda C/C++, seleccione Generación de código y establezca Biblioteca en tiempo de ejecución: Depuración multiproceso (/MTd).
Seleccione General y agregue a Directorios de inclusión adicionales:
- "C:\Microsoft NLS Downlevel APIs\Include".
Seleccione Idioma y establezca Tratar wchar_t como Tipo integrado: No (/Zc:wchar_t-).
Seleccione Avanzadas y establezca Convención de llamada: _stdcall (/Gz).
En Propiedades de configuración, expanda Enlazador, seleccione Entrada y agregue a Dependencias adicionales:
- "C:\Archivos de programa\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
- "C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib".
Reemplace el contenido de GuiStep_3.cpp por el código siguiente:
// 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; }
Cree o copie langs.txt en el directorio adecuado, como se ha descrito anteriormente en Paso 4: Globalización de "Hello MUI".
Compile y ejecute el proyecto.
Nota:
Si la aplicación se debe ejecutar en versiones de Windows anteriores a Windows Vista, asegúrese de leer los documentos que se incluyen con el paquete de API de nivel inferior de NLS de Microsoft sobre cómo redistribuir Nlsdl.dll. (Ya no está disponible en el Centro de descarga de Microsoft. Use las API de globalización de ICU en la actualización de mayo de 2019 de Windows 10 y versiones posteriores).
Consideraciones adicionales de MUI
Compatibilidad con aplicaciones de consola
Las técnicas que se tratan en este tutorial también se pueden usar en aplicaciones de consola. Pero a diferencia de la mayoría de los controles de GUI estándar, la ventana de comandos de Windows no puede mostrar caracteres para todos los idiomas. Por este motivo, las aplicaciones de consola multilingüe necesitan especial atención.
Llamar a las API SetThreadUILanguage oSetThreadPreferredUILanguages con marcas de filtrado específicas hace que las funciones de carga de recursos quiten sondeos de recursos de idioma para idiomas específicos que normalmente no se muestran dentro de una ventana de comandos. Cuando se establecen estas marcas, los algoritmos de configuración de idioma solo permiten que los idiomas que se mostrarán correctamente en la ventana de comandos estén en la lista de reserva.
Para más información sobre el uso de estas API para compilar una aplicación de consola multilingüe, vea las secciones de comentarios de SetThreadUILanguage y SetThreadPreferredUILanguages.
Determinación de los idiomas para admitir en tiempo de ejecución
Puede adoptar una de las siguientes sugerencias de diseño para determinar qué idiomas debe admitir la aplicación en tiempo de ejecución:
Durante la instalación, permita que el usuario final pueda seleccionar el idioma preferido en una lista de idiomas admitidos
Leer una lista de idiomas de un archivo de configuración
Algunos de los proyectos de este tutorial contienen una función que se usa para analizar un archivo de configuración langs.txt, que contiene una lista de idiomas.
Como esta función toma entradas externas, valide los idiomas que se proporcionan como entrada. Vea las funciones IsValidLocaleName r DownLevelLocaleNameToLCID para más información sobre cómo realizar esa validación.
Consulta del sistema operativo para determinar qué idiomas están instalados
Este enfoque ayuda a la aplicación a usar el mismo idioma que el sistema operativo. Aunque para esto no se necesita preguntar al usuario, si elige esta opción, tenga en cuenta que los idiomas del sistema operativo se pueden agregar o quitar en cualquier momento, y se pueden cambiar después de que el usuario instale la aplicación. Además, tenga en cuenta que, en algunos casos, el sistema operativo se instala con compatibilidad con idiomas limitada y la aplicación ofrece más valor si admite idiomas que el sistema operativo no admite.
Para más información sobre cómo determinar los idiomas instalados actualmente en el sistema operativo, vea la función EnumUILanguages.
Compatibilidad con scripts complejos en versiones anteriores a Windows Vista
Cuando una aplicación que admite determinados scripts complejos se ejecuta en una versión de Windows anterior a Windows Vista, es posible que el texto de ese script no se muestre correctamente en los componentes de la GUI. Por ejemplo, en el proyecto de nivel inferior de este tutorial, es posible que los scripts hi-IN y ta-IN no se muestren en el cuadro de mensaje debido a problemas con el procesamiento de scripts complejos y la falta de fuentes relacionadas. Normalmente, los problemas de esta naturaleza se presentan como cuadros cuadrados en el componente de la GUI.
Puede encontrar más información sobre cómo habilitar el procesamiento de scripts complejos en Compatibilidad con scripts y fuentes en Windows.
Resumen
En este tutorial se ha globalizado una aplicación monolingüe y se han demostrado los siguientes procedimientos recomendados.
- Diseño de la aplicación para aprovechar el modelo de recursos de Win32.
- Uso de la separación de MUI de recursos en archivos binarios satélite (archivos .mui).
- Comprobación de que el proceso de localización actualiza los recursos de los archivos .mui a fin de que sean adecuados para el idioma de destino.
- Comprobación de que el empaquetado y la implementación de la aplicación, los archivos .mui asociados y el contenido de configuración se realizan correctamente para permitir que las API de carga de recursos encuentren el contenido localizado.
- Proporcionar al usuario final un mecanismo para ajustar la configuración de idioma de la aplicación.
- Ajuste del código en tiempo de ejecución para aprovechar las ventajas de la configuración de idiomas, a fin de que la aplicación responda mejor a las necesidades del usuario final.