A todos nos gusta utilizar programas que nos cuenten lo que est�n haciendo. Los programas que nos mantienen informados normalmente lo hacen mostrando mensajes de estado o de error. Por supuesto, estos mensajes necesitan ser traducidos para que puedan ser entendidos por los usuarios finales de todo el mundo. Explicamos la traduci�n de mensajes de textos en Aislar Objetos Espec�ficos de la Localidad en un ResourceBundle. Normalmente con mover el mensaje String dentro de un ResourceBundle, ya est� hecho. Sin embargo, si un mensaje contiene datos variables tendremos que hacer unos pasos extra para preparar su traducci�n.
En la siguiente lista de mensajes, hemos subrayado los datos variables.
The disk named MyDisk contains 300 files. The current balance of account #34-98-222 is $2,745.72. 405,390 people have visited your website since January 1, 1998. Delete all files older than 120 days.
Podr�amos estar tentados a construir el �ltimo mensaje de la lista concatenando frases y variables.
double numDays; ResourceBundle mssgBundle; . . String message = mssgBundle.getString("deleteolder") + numDays.toString() + mssgBundle.getString("days");
Esta aproximaci�n funciona bien en Ingl�s, pero no funciona en idiomas donde los verbos aparezcan al final de la sentencia. Como el orden de las palabras de este mensaje est� codificado, los localizadores no podr�n crear traducciones gram�ticamente correctas para todos los idiomas.
�C�mo podemos hacer que nuestros programas sean f�cilmente localizables si necesitamos utilizar mensajes concatenados? Podemos hacerlo utilizando la clase MessageFormat, que es el punto principal de esta lecci�n.
�Tratar con Mensajes Concatenados
Un mensaje concatenado podr�a contener varios tipos de variables: fechas, horas, cadenas, n�meros, monedas y porcentajes. Para formatear un mensaje concatenado de un forma independiente de la Localidad, se construye un patr�n que luego se aplica a un objeto MessageFormat.
En esta secci�n pasaremos a trav�s de un programa de ejemplo para demostrar c�mo internacionalizar un mensaje concatenado. El programa de ejemplo hace uso de la clase MessageFormat. El c�digo fuente completo los puedes encontrar en MessageFormatDemo.java.
�1. Identificar las Variables del Mensaje
La versi�n inglesa del mensaje que queremos internacionalizar es esta.
At 1:15 PM on April 13, 1998, we detected 7 spaceships on the planet Mars. ^ ^ ^ ^ | | | | Date Date Number String
Observa que hemos subrayado las variables, y hemos identificado qu� tipo de objetos representar� estos datos.
�2. Aislar el Patr�n del Mensaje en un ResourceBundle
Vamos a almecenar el mensaje en un ResourceBundle llamado MessageBundle. Aqu� tienes el c�digo de creacci�n del ResourceBundle.
ResourceBundle messages = ResourceBundle.getBundle("MessageBundle",currentLocale);
Este ResourceBundle est� compuesto por un fichero de propiedades para cada Localidad. Como nuestro ResourceBundle se llama MessageBundle, el fichero de propiedades para la Localidad U.S. English se llamar� MessageBundle_en_US.properties. Aqu� tienes los contendos de este fichero de propiedades.
template = At {2,time,short} on {2,date,long}, we detected {1,number,integer} spaceships on the planet {0}. planet = Mars
Hemos especificado el patr�n en la primera l�nea del fichero de propiedades. Si comparamos este patr�n con el mensaje de texto mostrado en el paso 1, veremos que se ha reemplazado cada variable del mensaje con un argumento encerrado entre corchetes. Cada argumento empieza con un d�gito llamado el n�mero de argumento, que corresponde con el �ndice de un elemento en un array de Object que contiene los valores de los argumentos. Observa que en el patr�n, estos argumentos no est�n en ning�n orden particular. Se pueden situar los argumentos en cualquier orden dentro del patr�n. El �nico requerimiento es que el n�mero del argumento tenga un elemento correspondiente en el array de valores de los argumentos. En el siguiente paso explicaremos el array de valores de argumentos, pero primero echemos un vistazo a todos los argumentos del patr�n. La siguiente tabla proporciona algunos detalles sobre los argumentos.
Argumento | Descripci�n |
---|---|
{2,time,short} | La parte horaria de un objeto Date. El estilo "short" especifica el estilo de formato DateFormat.SHORT. |
{2,date,long} | La parte de la fecha de un objeto Date. El mismo objeto Date se utiliza para las dos variables de fecha y hora. En el array de argumentos Object el �ndice que contiene el objeto Date es el 2. |
{1,number,integer} | Un objeto Number, adem�s cualificado con el estilo num�rico "integer". |
{0} | El String del ResourceBundle que corresponde con la clave "planet". |
Para una descripci�n completa de la s�ntaxis de argumentos, puedes ver la documentaci�n del API para la clase MessageFormat.
�3. Seleccionar los Argumentos del Mensaje
En las siguientes l�neas de c�digo, asignamos los calores para cada argumento del patr�n. Los �ndices de los elementos del array messageArguments corresponden con los n�meros de los argumentos del patr�n. Por ejemplo, el elemento de �ndice 1, que es un Integer(7), corresponde con el argumento {1,number,integer} del patr�n. Extraemos los objetos String, que son los elementos 0 y 3, del ResourceBundle con getString, porque deben ser traducidos. El array de argumentos del mensaje se define de esta forma.
Object[] messageArguments = { messages.getString("planet"), new Integer(7), new Date() };
�4. Crear el Formateador
Luego, creamos un objeto MessageFormat. Seleccionamos la Localidad porque nuestro mensaje contiene objetos Date y Number, que deber�an ser formateados de una manera sensible a la Localidad. Por ejemlo en ingl�s NorteAmericano la fechas 4/13/98 est� en el formato correcto, pero en franc�s deber�a formatearse como 13/04/98. Creamos el formateador de mensajes de la siguiente forma.
MessageFormat formatter = new MessageFormat(""); formatter.setLocale(currentLocale);
�5. Formatear el Mensaje utilizando el Patr�n y los Argumentos
En este paso, demostraremos como trabajan juntos, el patr�n, los argumentos del mensaje y el formateador. Primero, extraemos el String del patr�n del ResourceBundle con el m�todo getString. La clave para el patr�n es "template." Pasamos el patr�n al formateador con el m�todo applyPattern. Luego, formateamos el mensaje utilizando el array de argumentos del mensaje llamando al m�todo format. El String devuelto por este m�todo ya est� listo para utilizar. Todo esto se realiza con s�lo dos l�neas de c�digo.
formatter.applyPattern(messages.getString("template")); String output = formatter.format(messageArguments);
�6. Ejecutar el Programa de Desmostraci�n
Veamos que sucede cuando el programa se ejecuta con la Localidad U.S. English.
% java MessageFormatDemo en US currentLocale = en_US At 1:15 PM on April 13, 1998, we detected 7 spaceships on the planet Mars.
Cuando se ejecuta el programa para la Localidad Alemana, observamos que la fecha y la hora han sido localizadas.
% java MessageFormatDemo de DE currentLocale = de_DE Um 13.15 Uhr am 13. April 1998 haben wir 7 Raumschiffe auf dem Planeten Mars entdeckt.
�Manejar Plurales
Las palabras de un mensaje pueden variar si son en plural o singular. Con la clase ChoiceFormat, se puede mapear un n�mero a una palabra o frase, permitiendo construir mensajes que sean gram�ticamente correctos.
En Ingl�s, las formas plural y singular de una palabra nomalmente son diferentes. Esto puede representar un problema cuando se construyen mensajes que se refieren a cantidades. Por ejemplo, si el mensaje informa del n�mero de ficheros en un disco, son posibles las siguientes variaciones.
There are no files on XDisk. There is one file on XDisk. There are 2 files on XDisk.
La forma m�s r�pida de resolver este problema es crear un patr�n de MessageFormat como �ste.
There are {0,number} file(s) on {1}.
Desafortunadamente, este patr�n resulta gram�ticamente incorrecto.
There are 1 file(s) on XDisk.
Podemos hacer algo mejor que esto utilizando la clase ChoiceFormat. En esta secci�n, veremos como trarar con plurales en un mensaje, pasando a trav�s de un programa de ejemplo llamado ChoiceFormatDemo.java. Este programa tambi�n hace uso de la clase MessageFormat que se describi� en la secci�n anterior, Tratar con Mensajes Concatenados.
�1. Definir el Patr�n del Mensaje
Primero, identifiquemos las variables de nuestro mensaje.
There | are no files | on | XDisk | . There | is one file | on | XDisk | . There | are 2 files | on | XDisk | . |______________| |_______| ^ ^ | | variable variable
Luego, reemplazamos las variables del mensaje con argumentos, creando un patr�n que puede aplicarse a un objeto MessageFormat.
There {0} on {1}.
Es muy sencillo trabajar con el argumento para el nombre del disco, que est� representado por {1}. Lo trataremos igual que cualquier otra variable String en un patr�n MessageFormat. Este argumento corresponde con el elemento 1 del array de valores (Ver paso 7).
Tratar con el argumento {0} es m�s complejo por un par de razones.
- Primero, la frase que reemplaza este argumento var�a con el n�mero de ficheros. Para construir esta frase en tiempo de ejecuci�n, necesitamos mapear el n�mero de ficheros en un String particular. Por ejemplo, el n�mero 1, mapear� el String "is one file." La clase ChoiceFormat permite realizar el mapeado necesario.
- Segundo, si el disco contiene varios ficheros, la frase incluir� un entero. La clase MessageFormat nos permite insertar n�meros en una frase.
�2. Crear un ResourceBundle
Aislaremos el texto del mensaje en un ResourceBundle porque debe ser traducido.
ResourceBundle bundle = ResourceBundle.getBundle("ChoiceBundle",currentLocale);
Hemos decidido construir nuestro ResourceBundle con ficheros de propiedades. El fichero ChoiceBundle_en_US.properties contiene las siguientes l�neas.
pattern = There {0} on {1}. noFiles = are no files oneFile = is one file multipleFiles = are {2} files
El contenido de este fichero de propiedades muestra c�mo se construir�n y formatear�n los mensajes. La primera l�nea contiene el patr�n para MessageFormat, que explicamos en el paso anterior. Las otras l�neas contienen frases que reemplazar�n el argumento {0} en el patr�n. La frase para la clave "multipleFiles" contiene el argumento {2}, que ser� reemplazado por un n�mero.
Aqu� podemos ver la versi�n francesa del fichero de propiedades ChoiceBundle_fr_FR.properties.
pattern = Il {0} sur {1}. noFiles = n' y a pas des fichiers oneFile = y a un fichier multipleFiles = y a {2} fichiers
�3. Crear un formateador de Mensaje
En este paso, ejemplarizamos MessageFormat y seleccionamos su Localidad.
MessageFormat messageForm = new MessageFormat(""); messageForm.setLocale(currentLocale);
�4. Crear un formateador de Choice
El objeto ChoiceFormat nos permite elegir, bas�ndose en un n�mero double, un String particular. El rango de n�meros double y los objetos String con los que se mapean, se especifican en arrays.
double[] fileLimits = {0,1,2}; String [] fileStrings = { bundle.getString("noFiles"), bundle.getString("oneFile"), bundle.getString("multipleFiles") };
ChoiceFormat mapea cada elemento del array double con el elemento del array String que tiene el mismo �ndice. En nuestro c�digo de ejemplo, el 0 mapea el String devuelto por la llamada a bundle.getString("noFiles"). Por coincidencia, en nuestro ejemplo, el �ndice es el mismo que el valor en el array fileLimits. si hubieramos seleccionado fileLimits[0] a 7, ChoiceFormat mapear�a el n�mero 7 con fileStrings[0].
Especificamos los arrays double y String cuando ejemplarizamos ChoiceFormat.
ChoiceFormat choiceForm = new ChoiceFormat(fileLimits, fileStrings);
�5. Aplicar el Patr�n
�Recuerdas el patr�n que construimos en el paso 1? Ahora es el momento de recuperar el patr�n del ResourceBundle y aplicarlo al objeto MessageFormat.
String pattern = bundle.getString("pattern"); messageForm.applyPattern(pattern);
�6. Asignar lo formatos
En este paso, asignamos el objeto ChoiceFormat creado en el paso 4 al objeto MessageFormat.
Format[] formats = {choiceForm, null, NumberFormat.getInstance()}; messageForm.setFormats(formats);
El m�todo setFormats asigna objetos Format a los argumentos del patr�n del mensaje. Debemos llamar al m�todo applyPattern antes del llamar al m�todo setFormats. La siguiente tabla muestra c�mo el array Format corresponde con los argumentos del patr�n del mensaje.
Elemento del Array | Argumento del Patr�n |
---|---|
choiceForm | {0} |
null | {1} |
NumberFormat.getInstance() | {2} |
�7. Seleccionar los Argumentos y el Formato del Mensaje
En tiempo de ejecuci�n, asignamos las variables al array de argumentos que pasamos al objeto MessageFormat. Los elementos del array corresponden con los argumentos del patr�n. Por ejemplo, messageArgument[1] mapea al argumento {1} del patr�n, que es un String que contiene el nombre del disco. En el paso anterior, asignamos un objeto ChoiceFormat al argumento {0} del patr�n. Por lo tanto, el n�mero asignado a messageArgument[0] el String seleccionado por el objeto ChoiceFormat. Si messageArgument[0] es mayor o igual que 2, el String "are {2} files" reemplaza al argumento {0} del patr�n. El n�mero asignado a messageArgument[2] ser� substituido en lugar del argumento {2} del patr�n. Intentaremos hacer esto con las siguientes l�neas de c�digo.
Object[] messageArguments = {null, "XDisk", null}; for (int numFiles = 0; numFiles < 4; numFiles++) { messageArguments[0] = new Integer(numFiles); messageArguments[2] = new Integer(numFiles); String result = messageForm.format(messageArguments); System.out.println(result); }
�8. Ejecutar el Programa de Demostraci�n
Ejecutemos el programa para la Localidad U.S. English.
% java ChoiceFormatDemo en US currentLocale = en_US There are no files on XDisk. There is one file on XDisk. There are 2 files on XDisk. There are 3 files on XDisk.
Compara los mensajes mostrados por el programa con las frases del ResourceBundle del paso 2. Observa que el objeto ChoiceFormat selecciona la frase corecta, que el objeto MessageFormat utiliza para construir el mensaje apropiado.
La versi�n francesa del mensaje tambi�n parece correcta.
% java ChoiceFormatDemo fr FR currentLocale = fr_FR Il n' y a pas des fichiers sur XDisk. Il y a un fichier sur XDisk. Il y a 2 fichiers sur XDisk. Il y a 3 fichiers sur XDisk.