Go to content Go to navigation Go to search

C# – Aplicaciones multilenguaje ( globalización y localización )

August 29th, 2010 by JuanK

Continuamente en mis conferencias y de paso por los foros a través de internet encuentro multitud de dudas e inquietudes al respecto de Cómo hacer una aplicación que soporte múltiples idiomas, ciertamente hay varias formas de conseguirlo y no todas esas formas son buenas en todos los escenarios, dependiendo de diferentes factores como por ejemplo el tamaño de la aplicación o la cantidad de idiomas soportados.

 

Sin embargo yo tengo mi método preferido, el cual considero que es válido para la inmensa mayoría de aplicaciones desarrolladas, el método de los archivos de recursos y ese es el tema principal de este artículo.

 

En tres pasos enseñare como hacerlo.

  1. Utilizar archivos de recursos para manipular las cadenas de texto
  2. Soportar múltiples lenguajes con los archivos de recursos
  3. Aislar los archivos de recursos para facilitar la actualización

 

Finalmente relacionare otros temas de interés para tener una funcionalidad más robusta.

 

1. UTILIZAR ARCHIVOS DE RECURSOS PARA MANIPULAR LAS CADENAS DE TEXTO

PREPARAR LA APLICACION

Lo primero que debemos hacer es crear una aplicación de Windows Forms con 1 ListBox y 5 Botones como se ven la siguiente imagen.

image

Para fines de este ejemplo con excepción de los textos del listbox ninguno de los textos es obligatorio.

Ahora, dentro de la solución, creamos una carpeta la cual llamaremos “Recursos Localizables”, justo como se ve a continuación.

image

Ahora creamos un nuevo archivo de recursos y lo llamaremos StringResources, debemos crearlo junto dentro de la carpeta “Recursos Localizables” que acabamos de crear.

 

Revisamos las propiedades del archivo recién creado y nos aseguramos que en Build Action diga “Embedded Resource”.

 

Ahora utilizaremos ese archivo de recursos para guardar las cadenas en el idioma principal (por defecto ) de nuestra aplicación, así que le damos doble clic y adicionamos los valores que se ven en la siguiente imagen.

 

image

 

Para efectos del ejemplo es importante hacerlo con los valores que ven.

 

UTILIZAR EL ARCHIVO DE RECURSOS EN NUESTRA APLICACION

Esta parte es muy sencilla, creamos un método llamado AplicarIdioma con el siguiente código:

private void AplicarIdioma()
{
    button1.Text = StringResources.ButtonLabel1;
    button2.Text = StringResources.ButtonLabel2;
    button3.Text = StringResources.ButtonLabel3;
    button4.Text = StringResources.ButtonLabel4;
    button5.Text = StringResources.ButtonLabel5;
    this.Text = StringResources.WindowTitle;
}

Y lo llamamos desde el Form_Load, ejecutamos la aplicación y obtenemos esto

 

image

 

2. SOPORTAR MÚLTIPLES LENGUAJES CON LOS ARCHIVOS DE RECURSOS

Casi todo el trabajo de codificación ha terminado ahora viene ‘la magia’ . En la carpeta “Recursos Localizables” creamos 3 copias de el archivo de recursos original, nos aseguramos de que cada una de ellas quede con los siguientes nombres.

 

image

 

Ahora editamos cada uno de ellos dejándolos con los textos que se ven a continuación (pueden ayudarse de traductores online para obtener los caracteres en japonés).

 

image

image

 

 

 

 

 

 

 

 

 

 

image

 

Una vez hecho esto procedemos a modificar el evento SelectedIndexChanged del listbox y lo dejamos así:

private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    Thread.CurrentThread.CurrentUICulture = new CultureInfo((string)listBox1.SelectedItem);
    AplicarIdioma();
}

Lo que hemos hecho es hacer que cada vez que se de clic en uno de los miembros de la lista se cambie la cultura de la interfaz de usuario del hilo actual de la aplicación, y dado que cada miembro del listbox es el nombre corto de cada una de las tres culturas que tenemos, basta con crear un nuevo objeto de tipo CultureInfo que coincida con el nombre corto del ítem seleccionado.

 

PORQUÉ Y PARA QUÉ?

Esto que acabamos de hacer tiene un importante objetivo.

Cuando se utiliza un archivo de recursos el CLR  busca primero cual es la cultura del hilo actual, entonces con ese dato automáticamente utiliza el archivo de recursos cuya nomenclatura coincide con esa cultura, sino encuentra dicho archivo de recursos entonces el CLR utiliza el archivo de recursos que no tiene cultura específica.

 

FUNCIONA!

Ejecutamos la aplicación, damos clic en cada uno de los ítems del listbox y podemos ver esto Sorpresa:

 

CultureAnimation

 

3. AISLAR LOS ARCHIVOS DE RECURSOS PARA FACILITAR LA ACTUALIZACIÓN

Hasta aquí nuestro proyecto ya esta terminado y ya estamos en capacidad de hacer aplicaciones multilenguaje, sin embargo como tip final de este artículo les recomiendo que si piensan incluir alguna funcionalidad de actualizaciones en sus aplicaciones y tienen planeado actualizar o incluir nuevos idiomas en su aplicación, coloquen los archivo de recursos en una dl por aparte donde solo se coloquen archivos de recursos, esto permitirá que las actualizaciones relacionadas con cadenas de lenguaje se aíslen en un solo componente.

 

Para que esto funcione, en el proyecto de la librería que utilicen para tal fin, recuerden establecer en cada archivo de recursos el modificador de acceso en public como lo muestra esta imagen.

 

image

 

LECTURAS RECOMENDADAS

Este tema puede ir aún más allá de donde lo hemos llevado, los invito averiguar acerca de como utilizar ensamblados satélite para brindar algunas funcionalidades adicionales y que intenten utilizar la clase CultureInfo y demás relacionadas para detectar el idioma de la interfaz de usuario actual o el idioma con el cual se ha instalado el sistema operativo.

 

En Sistemas como Windows 7 si instalas el sistema en inglés y luego le actualizadas el lenguaje a español, algunas funciones te seguirán reportando que el idioma del sistema esta en inglés por lo que hay que hacer un trabajo adicional para detectar el idioma actual de la interfaz de usuario actual.

 

Pueden bajar la solución completa aquí: Articulo Aplicaciones multilenguaje ( globalización y localización )

 

Espero que le puedan sacar mucho provecho.

 

Saludos.

Bookmark and Share

C# – El extraño caso de la ventana sin borde que no se deja maximizar ni minimizar

February 2nd, 2010 by JuanK

En algunas aplicaciones llega a ser necesario tener una ventana sin borde en algún momento, una ventana sin borde se logra estableciendo la propiedad FormBorderStyle = None en el diseñador de Windows Forms o a través de código:

 
this.FormBorderStyle = FormBorderStyle.None;

Hasta ahí todo esta bien y no hay ningún problema al respecto, hasta que nos damos cuenta que una ventana sin borde no se deja maximizar ni minimizar.

De esto tratare en este artículo, el porqué de esta situación y como solucionarlo.

 

Como sabe una ventana que se debe maximizar o minimizar?

El bucle de mensajes

Las ventanas – y los demás controles – funcionan gracias a un bucle de mensajes, todo lo que manejamos nosotros como eventos : click del mouse, mover, cerrar, cambiar tamaño, maximizar etc, realmente es controlado por un bucle en donde se envían diferentes mensajes a la ventana, esta a su vez tiene un procedimiento que recibe estos mensajes y con base a los mensajes recibidos puede hacer una u otra cosa según se programe.

 

Si, para algunos esto ya debe estar sonando a cuento, pero las cosas son así por debajo de lo que usamos tradicionalmente. El tema del artículo no es explicar como funciona un ciclo de mensajes así que por el momento lo dejaremos hasta allí y quien quiera profundizar puede consultar esta fuente en internet http://www.winprog.org/tutorial/message_loop.html

 

Por el momento lo que si nos interesa del bucle de mensajes es que algunos de esos mensajes se utilizan para maximizar y minimizar las ventanas, es decir cuando uno utiliza alguna funcionalidad para minimizar una ventana, lo que ocurre realmente es que se envía el mensaje que dice: hey! minimízate y ya el manejador de la ventana hará lo necesario para minimizarse.

El problema de la ventana sin borde.

Resulta que cuando se crea una ventana el sistema de ventanas se encarga de asignar ciertas características de acuerdo a sus parámetros de creación, una de esas características es incluir llamados a las funciones internas de Windows Forms que minimizan y maximizan ventanas, pero cuando se esta creando una ventana sin borde, al no tener esta los botones de minimizar o maximizar simplemente se pasa por alto la necesidad de incluir llamados a esas funciones.

Tan es así que las ventanas sin borde ni siquiera reciben mensajes relacionados con maximizar y minimizar desde la barra de tareas de Windows, esto lo podemos verificar así:

  • Crear una ventana con borde en el diseñador
  • Sobre escribir el método WndProc ( que es el que procesa la cadena de mensajes enviados a la ventana)
  • Interceptar el mensaje de minimizar la ventana y lanzar un MessageBox:

 

Lo que sucederá es que el mensaje se mostrará, pero si creamos desde un comienzo la ventana como ventana sin borde nos daremos cuenta que el mensaje nunca se lanza puesto que la ventana nunca recibe el mensaje indicando que se minimize, y aunque lo recibiera no haría nada.

 

Este es el código de como se debe dejar el WndProc en la forma para hacer las pruebas con y sin borde.

 
const int WM_SYSCOMMAND = 0x112;
const int SC_MINIMIZE = 0xF020;
 
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_SYSCOMMAND)
    {
        if (m.WParam == (IntPtr)SC_MINIMIZE)
            MessageBox.Show"Hacer lo que quieras en vez de minimizar");
 
        base.WndProc(ref m);
    }
    else
        base.WndProc(ref m);
}

Esto se convierte en un problema, pero como hacer para solucionarlo?

 

*Próximamente escribiré un breve artículo profundizando un poco más en la interceptación de mensajes y en el caso particular expuesto de minimizar la ventana.

Cambiar el comportamiento de la ventana

Como deshacernos de este problema y poder minimizar la ventana sin borde?

La respuesta esta escondida en mis líneas anteriores:

 

resulta que cuando se crea una ventana el sistema de ventanas se encarga de asignar ciertas características de acuerdo a sus parámetros de creación

Técnicamente hablando podríamos crear una ventana con borde y una vez inicializada cambiarle el estilo para que ahora estando sin borde se deje minimizar… ERROR eso no es posible porque apenas cambiamos el estilo de la ventana se llama una rutina que inicializa toda su estructura nuevamente… y al hacerlo elimina de nuevo la funcionalidad de minimizar y maximizar. :(

 

Para lograrlo hacer hay que hallar la forma de ‘engañar’ al sistema de ventanas del Windows Forms y hacerle creer que tiene una ventana con borde pero que realmente sea sin borde. Es decir debemos desde el comienzo crear una ventana con borde y luego volverla sin borde, PERO haciendo que el sistema de ventanas de Windows Forms no se entere, como es eso? con nuestra amiga la API de Windows haciendo llamados directamente al manejador de ventanas del sistema operativo sin pasar por Windows Forms.

 

Cuando una forma en Windows Forms es creada, esta inicializa todas sus estructuras de acuerdo a las propiedades establecidas y esto lo hace en un  método llamado CreateParams quien es el que internamente esta haciendo llamados a la API, bueno realmente es una propiedad, así que si reemplazamos  esta propiedad podemos hacer creer a Windows Forms que esta creando una ventana con bordes pero ya nos hemos encargado de quitarle dichos bordes “a la mala”.

Los pasos a seguir son los siguientes:

 

  1. Crear la forma con el estilo normal que incluye los botones minimizar y maximizar
  2. Sobre Escribir el método CreateParams
  3. Introducir modificaciones al estilo de la ventana  pero no utilizando las propiedades de Windows Forms sino modificando los parámetros con los cuales Windows Forms le pedirá al sistema operativo que cree la nueva ventana.

Todos los pasos son fáciles, el que es un poco críptico es el paso 3, así que lo analizare en más detalle.

CreateParams

Esta propiedad tiene a su vez su propia estructura, y parte de esa estructura es el campo Style de tipo int, cuando Windows esta inicializando la forma se revisa ese campo para determinar el estilo de la ventana, y de hecho cada vez que modificamos el estilo de la ventana esta recrea su apariencia modificando no solo el valor de Style sino también modificando comportamientos como ya lo hemos visto anteriormente. Sin embargo desde el propio manejador de ventanas de Windows cambiar el estilo no implica cambiar de una vez el comportamiento – como ya vimos que si sucede en Windows Forms  – así que podemos cambiar el valor de Style sin necesidad de cambiar nada más.

 

Como nuestra forma justo antes de comenzar el paso 3 ya esta lista para minimizarse y maximizarse, lo que haremos en el paso 3 será modificar la propiedad Style de CreateParams para suprimirle ‘la caja de titulo’ y ‘el borde de cambiar tamaño’, como hacemos esto si Style es un tipo int? pues lo haremos a través de mascaras como si fuera una enum en Windows Forms.  He definido WS_THICKFRAME nada más para preservar la definición inicial que se da en la API de Windows. El código quedaría así:

 

const int WS_CAPTION    = 0xC00000;
const int WS_THICKFRAME = 0x00040000;
const int WS_SIZEBOX    = WS_THICKFRAME;
protected override CreateParams CreateParams
{
  get
  {
      CreateParams p = base.CreateParams;
      p.Style &= ~(WS_CAPTION | WS_SIZEBOX);
      return p;
  }
}

Simplemente estamos tomando los valores originales de CreateParams los cuales incluyen una ventana con bordes, pero reasignamos la propiedad Style para dejarla como estaba pero quitándole el borde de Resize y la barra de titulo.

Y Listo!!!

Eso es todo, ahora la ventana no tiene bordes y adicionalmente recibe el mensaje de minimizar, es más podemos combinar este código con el que veíamos en la primera parte y veremos como ahora si podemos interceptar el mensaje de minimizar !! ;)

 

Para profundizar un poco más acerca de como modificar el comportamiento de las ventanas les recomiendo revisar este link:

http://juank.black-byte.com/c-modificar-boton-minimizar-maximizar/

 

Happy Learning!

Bookmark and Share

C# – Cómo modificar el comportamiento del botón minimizar, maximizar, etc.

September 13th, 2009 by JuanK

Para cambiar el comportamiento del botón minimizar se debe recurrir a un mecanismo pocas veces utilizado por los desarrolladores de código administrado, hay que acceder al bucle de mensajes del sistemas de ventana de ventanas, más conocido como WndProc.

En Windows Forms esta es una tarea relativamente sencilla, la cual veremos unas líneas más adelante.

 

Como sabe una ventana que se debe maximizar o minimizar?

El bucle de mensajes

Las ventanas – y los demás controles – funcionan gracias a un bucle de mensajes, todo lo que manejamos nosotros como eventos : click del mouse, mover, cerrar, cambiar tamaño, maximizar etc, realmente es controlado por un bucle en donde se envían diferentes mensajes a la ventana, esta a su vez tiene un procedimiento que recibe estos mensajes y con base a los mensajes recibidos puede hacer una u otra cosa según se programe.

 

Si, para algunos esto ya debe estar sonando a cuento, pero las cosas son así por debajo de lo que usamos tradicionalmente. El tema del artículo no es explicar como funciona un ciclo de mensajes así que por el momento lo dejaremos hasta allí y quien quiera profundizar puede consultar esta fuente en internet http://www.winprog.org/tutorial/message_loop.html

 

Por el momento lo que si nos interesa del bucle de mensajes es que algunos de esos mensajes se utilizan para maximizar y minimizar las ventanas, es decir cuando uno utiliza alguna funcionalidad para minimizar una ventana, lo que ocurre realmente es que se envía el mensaje que dice: hey! minimízate y ya el manejador de la ventana hará lo necesario para minimizarse.

Los Mensajes

Cada vez que se presiona cualquiera de los botones del marco de la ventana (maximizar, minimizar, restaurar, cerrar, etc.) o que se ejecute alguna acción sobre el menú de la ventana, el sistema mensajes envía a la ventana el mensaje WM_SYSCOMMAND, este mensaje esta definido dentro de la API de Windows de esta manera:

const int
WM_SYSCOMMAND = 0x112;

 

Este mensaje a su ves esta acompañado de un parámetro WParam el cual contiene información relevante a que acción se ha realizado con el menú o los botones de la ventana, entre estos mensajes encontramos:

const int SC_MINIMIZE     = 0xF020;
const int SC_MAXIMIZE     = 0xF030;
const int SC_CLOSE        = 0xF060;

 

WndProc en Windows Forms

Para acceder a este procedimiento desde Windows Forms basta con sobre escribir el método WndProc:

 
protected override void WndProc(ref Message m)
{
   base.WndProc(ref m);
}

Implementación

Para encajar las piezas lo que se debe hacer es preguntar dentro de WndProc acerca de que mensaje se ha recibido, si el mensaje es WM_SYSCOMMAND entonces se pregunta acerca del parámetro LParam para verificar que comando ha sido enviado, es allí donde podemos interceptar el mensaje que nos interesa.

 

Siempre es necesario llamar al código base cuando no vayamos a hacer nada con un mensaje para que de esta forma la ventana se comporte de manera normal.

 
const int WM_SYSCOMMAND = 0x112;
const int SC_MINIMIZE = 0xF020;
 
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_SYSCOMMAND)
    {
        if (m.WParam == (IntPtr)SC_MINIMIZE)
            MessageBox.Show("Hacer lo que quieras en vez de minimizar");
 
        base.WndProc(ref m);
    }
    else
        base.WndProc(ref m);
}

Para profundizar un poco más acerca de como modificar el comportamiento de las ventanas les recomiendo revisar este link: http://juank.black-byte.com/c-minimizar-maximizar-ventana-sin-borde/

 

Bookmark and Share