Aplicaciones multi-idiomas con 4D
Hoy en día es casi obligatorio incluir la gestión de idiomas en cualquier aplicación. Como siempre, existen varias formas de hacerlo. Aquí expondremos uno de los métodos más interesantes porque es muy flexible para el programador y a la vez muy amigable para el usuario final, puesto que cada persona puede elegir su idioma de interfaz individualmente y también corregir traducciones o añadir su propio idioma en unos minutos!
En este ejemplo vamos a añadir una gestión de idiomas en una de las bases de ejemplos de 4D 2004, disponible en el instalador Demo (Invoices).


Los beneficios de los STR#
Utilizaremos las listas STR# que se ubican en los recursos del programa y de la base. Esos recursos son los mismos que se utilizan para traducir la interfaz de la propia aplicación 4D. Gracias a la arquitectura de las bases de datos 4D, gestionaremos nuestras propias listas dentro del archivo .RSR de la estructura. De esta forma, no tocamos al programa en sí y para evitar cualquier conflicto debemos recordar asignar siempre números de ID superiores a 15000 a los recursos que vamos a crear.
Para crear y manejar recursos, podemos utilizar 4D Insider, disponible en las herramientas que acompañan 4D, pero esta utilidad no permite crear listas de más de 256 elementos. También es posible editar el archivo de recursos con Resorcerer o ResEdit pero esos programas no están disponibles bajo Windows.
Sin duda la opción más sencilla es utilizar el propio 4D que tiene todos los comandos necesarios para manejar recursos.
Definir un código por idioma
Porque nos será útil luego, incluimos el código siguiente en el método On Startup
◊vl_English:=16000
◊vl_French:=17000
◊vl_Spanish:=18000
`
◊vl_CurrentLang:=◊vl_Spanish
`
ARRAY LONGINT(◊al_Lang;3)
◊al_Lang{1}:=◊vl_English
◊al_Lang{2}:=◊vl_French
◊al_Lang{3}:=◊vl_Spanish
Se trata de asignar un número de recurso disponible a cada idioma que nos interese incluir. Definimos el idioma por defecto de la aplicación y guardamos los ID en un array. En seguida reiniciamos la aplicación para cargar esas variables en memoria.
Crear una tabla dedicada para recursos
Luego creamos una tabla en la estructura con un campo ID y un campo texto para cada idioma que nos interese añadir

En seguida añadimos un trigger para asignar un número automático al campo ID. Será importante mantener una lista completa, sin huecos en la serie ID.


Dejamos 4D crear automáticamente los formularios de salida y entrada para la nueva tabla, o utilizamos el Asistente de formularios, y empezamos a añadir las distintas cadenas de la interfaz en cada idioma, o los importamos desde un archivo externo con el editor de Importaciones, sin olvidar traducir cada uno de los idiomas disponibles en cada uno de los demás. Para evitar cualquier error, podemos asignar la propiedad No editable al campo ID.
Nos falta añadir un botón “Actualizar recursos” en el listado con el código siguiente
ALL RECORDS([Strings])
ORDER BY([Strings];[Strings]ID)
`
ARRAY TEXT(at_English;0)
ARRAY TEXT(at_French;0)
ARRAY TEXT(at_Spanish;0)
`
SELECTION TO ARRAY([Strings]English;at_English; …
… [Strings]French;at_French;[Strings]Spanish;at_Spanish)
`
ARRAY TO STRING LIST(at_English; ◊vl_English)
ARRAY TO STRING LIST(at_French;◊vl_French)
ARRAY TO STRING LIST(at_Spanish;◊vl_Spanish)
`
ALERT(”Terminado”)
Al presionar el botón, automáticamente, los recursos STR# se van a crear o reemplazar en el archivo .RSR de la estructura abierta.

Es posible reservar el acceso de esta tabla al Diseñador, pero aún más interesante podríamos permitir al Administrador de la base modificar y enriquecer esos recursos, en la propia interfaz de usuario.
Insertar las referencias en los objetos
Nos queda asignar los códigos correspondientes a cada ID de cadena a los distintos objetos de la interfaz: menús, variables y objetos estáticos, con la siguiente sintaxis: “:◊vl_CurrentLang,x” en el que “x” corresponde al ID de la cadena referenciada, por ejemplo “:◊vl_CurrentLang,15” para la cadena “Quantity”
Menús
Cambiamos los títulos originales de menú por las referencias de nuestra tabla Strings. En seguida se traducen de acuerdo al valor de la variable ◊vl_CurrentLang

Variables
Para todos los objetos de tipo botón, casilla de selección, pestaña, etc.. hay que cambiar el título del objeto o los valores de la lista con el mismo truco. En seguida el idioma por defecto es el que se usa en el diseño de formulario. Para verificar que los tamaños de objetos son correctos para cada idioma, simplemente tendremos que cambiar el valor de la variable ◊vl_CurrentLang (con un método de proyecto que ejecutaremos en cualquier momento)

Objetos estáticos
La misma sintaxis se aplica para las etiquetas de todos los objetos fijos, esta vez cambiando el texto mismo de la caja de texto por la referencia


Añadir un control de idioma
Para permitir al usuario cambiar su idioma preferido sobre la marcha, añadimos un menú desplegable en el formulario listado con las referencias de idioma en el mismo orden que los del array ◊al_Lang creado en el Startup.

El contenido del menú desplegable se debe construir programáticamente. Lo haremos en el método de formulario.
Case of
: (Form event=On Load )
ARRAY TEXT(at_Lang;3)
at_Lang{1}:=Get indexed string(◊vl_CurrentLang;26)
at_Lang{2}:=Get indexed string(◊vl_CurrentLang;27)
at_Lang{3}:=Get indexed string(◊vl_CurrentLang;28)
at_Lang:=Find in array(◊al_Lang;◊vl_CurrentLang)
End case

El método de objeto del menú desplegable contiene el siguiente código
Case of
: (Form event=On Clicked )
◊vl_CurrentLang:=◊al_Lang{at_Lang}
CANCEL
DisplayInvoices
End case
Para permitir el refresco de los objetos del formulario, lo más sencillo es cerrar el formulario y abrir lo en seguida lanzando de nuevo el método correspondiente. El cambio no se notará por el usuario. (Pero el procediemiento solo es válido ni no se ejecuta código adicional al cerrar la ventana, el riesgo sería entonces saturar la memoria en posibles bucles infinitos)
Y ya podemos disfrutar de nuestra interfaz multilingüe!



Luego podremos pulir varios aspectos de esta programación para hacerla totalmente genérica, independientemente del número de idiomas.
También sería factible permitir el cambio de interfaz sin refresco de pantalla y sin cerrar la ventana principal, pero esto implicaría gestionar las etiquetas de botones y de textos fijos por programación. Para ver un ejemplo relacionado con este caso, abrir cualquiera de las bases de demostración de 4D 2004, y observar la programación del formulario AboutBox
