Interfaz de usuario multilingüe: un ejecutable, N idiomas (segunda parte)

En la anterior entrega, estudiamos un método para separar el código de los recursos a traducir utilizando archivos de recursos.  Ahora veremos como es posible separar estos recursos del código no sólo en nuestras fuentes, sino también en los binarios que se distribuyen.

Normalmente nuestro código compilado se puede describir por la siguiente imagen:

Datos y código en el mismo archivo

Donde vemos que tanto el código como los datos usados para desplegar el interfaz de usuario.  Utilizando MUI lo que podemos hacer es lo siguiente:

 Código en ejecutable y recursos en MUI

En este caso tenemos un archivo de tipo .exe (o .dll) que contiene el código y un archivo .mui que contiene los recursos a traducir.

Esto ya es muy bueno, pues nos permite sustituir el archivo con los recursos independientemente del código.  Pero además el diseño de MUI nos permite instalar múltiples idiomas conforme sean necesarios.  Nuestros archivos ahora deben contener lo siguiente:

  • Hello01.exe - Código ejecutable válido para todos los idiomas
  • Hello01.exe.mui - Recursos traducidos para un idioma (inglés)

Y para que esto funcione correctamente necesitamos instalarlos con la siguiente estructura de directorios:

<appdir>\Hello01.exe
<appdir>\en-us\Hello01.exe.mui

Esto nos permite crear un nuevo archivo .mui para otro idioma y con instalarlo en el directorio correcto tendremos una aplicación que soporta múltiples idiomas.

Volviendo al ejemplo

Regresando a nuestro ejemplo anterior, veamos como compilar nuestro ejecutable en dos archivos, uno conteniendo el código y otro con los recursos a traducir.

Los comandos descritos a continuación se ejecutan en la línea de comandos de VS 2008, y en el directorio donde trabajaremos debemos tener los siguientes archivos:

  • resource.h

        1: #define IDS_TITULO 1001
    
        2: #define IDS_MENSAJE 1002
    
  • resource.rc - Nótese que ahora definimos el lenguaje en qué están creados los recursos.

        1: #include "winnt.h"
    
        2: #include "resource.h"
    
        3:  
    
        4: LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
    
        5:  
    
        6: STRINGTABLE
    
        7: BEGIN
    
        8:     IDS_TITULO "Hello world!"
    
        9:     IDS_MENSAJE "Sample App"
    
       10: END
    
  • resource.rcconfig - Este archivo indica qué tipo de recursos son localizables y deben traducirse.  Aquí indicamos qué recursos se mantienen en el archivo ejecutable y cuáles van al archivo .mui.  Al final de este artículo hay una liga donde se describe la sintaxis de los archivos .rcconfig.

        1: <?xml version="1.0" encoding="utf-8"?> 
    
        2: <localization>
    
        3:   <resources>
    
        4:     <win32Resources fileType="Application">
    
        5:       <neutralResources>
    
        6:         <resourceType
    
        7:            typeNameId="#16"
    
        8:         />
    
        9:       </neutralResources>
    
       10:       <localizedResources> 
    
       11:          <resourceType
    
       12:                 typeNameId="#2"
    
       13:                 itemId="5 6 7 8 9 10 11 12"
    
       14:                 itemName="HTML PRI"
    
       15:          />
    
       16:          <resourceType
    
       17:                 typeNameId="#4"
    
       18:          />
    
       19:          <resourceType
    
       20:                 typeNameId="#5"
    
       21:          />
    
       22:          <resourceType
    
       23:                 typeNameId="#6"
    
       24:          />
    
       25:          <resourceType
    
       26:                 typeNameId="#9"
    
       27:          />
    
       28:          <resourceType
    
       29:                 typeNameId="#11"
    
       30:          />
    
       31:          <resourceType
    
       32:                 typeNameId="#16"
    
       33:          />
    
       34:          <resourceType
    
       35:                 typeNameId="HTML"
    
       36:          />
    
       37:          <resourceType
    
       38:                 typeNameId="#23"
    
       39:          />
    
       40:          <resourceType
    
       41:                 typeNameId="#240"
    
       42:          />
    
       43:          <resourceType
    
       44:                 typeNameId="#1024"
    
       45:          />
    
       46:          <resourceType
    
       47:                 typeNameId="MY_TYPE"
    
       48:          />
    
       49:       </localizedResources> 
    
       50:     </win32Resources>
    
       51:   </resources>
    
       52: </localization>
    
  • hello01.cpp

        1: #include <windows.h>
    
        2: #include "resource.h"
    
        3:  
    
        4: #define BUFFER_MAX 100
    
        5:  
    
        6: int main(int argc, WCHAR** argv)
    
        7: {
    
        8:     WCHAR mensaje[BUFFER_MAX];
    
        9:     WCHAR titulo[BUFFER_MAX];
    
       10:     ::LoadString(::GetModuleHandle(NULL), IDS_MENSAJE, mensaje, BUFFER_MAX);
    
       11:     ::LoadString(::GetModuleHandle(NULL), IDS_TITULO, titulo, BUFFER_MAX);
    
       12:     ::MessageBox(NULL, mensaje, titulo, MB_OK);
    
       13:     return 0;
    
       14: }
    

Y deben existir los subdirectorios

  • out
  • out\en-us

Nota:  Todos los comandos para compilar se ejecutan desde el directorio donde tenemos nuestro código.  Comenzamos compilando el código fuente:

 cl /Fo"out\\" /D "UNICODE" /D "_UNICODE" /c Hello01.cpp

Y a continuación compilamos los recursos:

 rc -fo out\resource.res -fm out\resource.loc.res -q resource.rcconfig resource.rc

Finalmente enlazamos los resultados de los pasos anteriores para crear nuestro ejecutable final:

 link /out:"out\Hello01.complete.exe" out\Hello01.obj out\resource.res user32.lib

Hasta ahora no hemos hecho nada distinto, excepto por etiquetar los recursos utilizando con el archivo .rcconfig.  El siguiente paso es el que utilizará esta información para separar nuestro ejecutable en un archivo que contiene el código y otro con los recursos.

 muirct -q resource.rcconfig out\Hello01.complete.exe out\Hello01.exe out\en-us\Hello01.exe.mui

Es muy importante hacer notar que si el ejecutable se encuentra en el directorio x, el archivo .mui debe aparecer en el directorio x\<codigo de lenguaje>.  Si no hacemos esto correctamente, el manejador de recursos no podrá encontrarlo.  Ejecutando nuestro programa, obtenemos el resultado esperado:

Cuadro de diálogo con texto

Ahora, sólo para confirmar qué sucede si eliminamos el archivo .mui (o se encuentra en una ubicación incorrecta), esto es lo que ocurre con el programa después de borrar out\en-us\Hello01.exe.mui:

 Cuadro de diálogo sin texto

En la tercera parte veremos cómo se pueden generar recursos para otros idiomas y cómo determina el sistema que recursos utilizar.

Referencias: