Los datos espec�ficos de la localidad deben ser creados de acuerdo a las convenciones del idioma y la regi�n del usuario final. El texto mostrado por un interface de usuario es el ejemplo m�s obvio de datos expec�ficos de la localidad. Por ejemplo, una aplicaci�n con un bot�n "Cancel" en los Estados Unidos, tendr� un bot�n "Abbrechen" en Aleman�a. En otros paises este bot�n tendr� otras etiquetas. Obviamente, tu no quieres codificar la etiqueta de este bot�n. �No ser�a bonito poder obtener autom�ticamente la etiqueta correcta para una Localidad dada? Afortunadamente, se puede, asilando los objetos espec�ficos de la localidad en un ResourceBundle.
En esta lecci�n, aprender�s c�mo crear, cargar y acceder a objetos ResourceBundle. Si tienes prisa por examinar alg�n c�digo fuente, adelantate y chequea las dos �ltimas lecciones. Luego podr�s volver a las dos primeras para obtener alguna informaci�n conceptual sobre los objetos ResourceBundle.
�La clase ResourceBundle
Los objetos ResourceBundle contienen objetos espec�ficos de la localidad. Cuando se necesita uno de estos objetos, se busca en el ResourceBundle, que devuelve el objeto que corresponde con la Localidad del usuario final. En esta secci�n, explicaremos como un ResourceBundle se relaciona con una Locale. Tambi�n describiremos las subclases de ResourceBundle, y cuando se pueden utilizar.
�C�mo se relaciona un ResourceBundle con una Locale
Conceptualmente hablando, un ResourceBundle es un conjunto de subclases relacionadas que comparten el mismo nombre base. La siguiente lista muestra un conjunto de subclases relacionadas. El nombre base es ButtonLabel. Los caracteres que siguen al nombre base son el c�digo del idioma, el c�dido del pa�s y el c�digo de la variante de un Locale. Por ejemplo, ButtonLabel_en_GB corresponde con la localidad espec�ficada por el c�digo del idioma Ingl�s, (en) y el c�digo del pa�s para Gran Breta�a (GB).
ButtonLabel ButtonLabel_de ButtonLabel_en_GB ButtonLabel_fr_CA_UNIX
Para seleccionar el ResourceBundle apropiado, se llama al m�todo getBundle. Los siguientes ejemplo seleccionan el ButtonLabel ResourceBundle para localidad que corresponden con el idioma Franc�s , el pa�s Canada y la plataforma UNIX.
Locale currentLocale = new Locale("fr", "FR", "UNIX"); ResourceBundle introLabels = ResourceBundle.getBundle("ButtonLabel", currentLocale);
Si no existe una clase ResourceBundle para la localidad especificada. getBundle trata de encontrar la correspondencia m�s cercarna. Por ejemplo, si no existiera una clase para ButtonLabel_fr_CA_UNIX, getBundle buscar� las clases en el siguiente orden.
ButtonLabel_fr_CA_UNIX ButtonLabel_fr_CA ButtonLabel_fr ButtonLabel
Si getBundle no encuentra una correspondencia en la lista anterior, intentar� una b�squeda similar utilizando la localidad por defecto. Si vuelve a fallar, getBundle lanzar� una MissingResourceException.
Siempre se debe proporciona una clase base sin sufijos. En el ejemplo anterior, si existiera una clase llamada ButtonLabel, getBundle no lanzar�a MissingResourceException.
�Las subclases ListResourceBundle y PropertyResourceBundle
La clase abstracta ResourceBundle tiene dos subclases: ListResourceBundle y PropertyResourceBundle. La subclase elegida depende de c�mo est�n localizados los datos.
Un PropertyResourceBundle est� constituido por uno o m�s ficheros de propiedades. Se deber�an almacenar objetos String traducibles en ficheros de propiedades. C�mo los ficheros de propiedades son s�lo ficheros de texto y no forman parte del c�digo fuente Java, pueden ser creados y actualizados por los traductores. No se requiere experiencia en programaci�n. Un traductor puede a�adir soporte para una localidad adicional con s�lo crear un nuevo fichero de propiedades. No se necesita un nuevo fichero de clase. Los ficheros de propiedades s�lo pueden contener valores para objetos String. Si necesitamos otros tipos de objetos, se debe utilizar un ListResourceBundle. Construir un ResourceBundle con Ficheros de Propiedades te muestra como utilizar PropertyResourceBundle.
La clase ListResourceBundle maneja recursos con una lista. Cada ListResourceBundle est� constituido por un fichero de clase. Se puede almacenar cualquier objeto espec�fico de la localidad en un ListResourceBundle. Para a�adir soporte para nuevas localidades, se debe crear otro fichero fuente, y compilarlo. Como los traductores normalmente no son programadores, no se deber�an almacenar objetos String que requieran traducci�n en un ListResourceBundle. Utilizar un ListResourceBundle contiene c�digo de ejemplo que de puede resultar �til.
La clase ResourceBundle es flexible. Si primero decidimos cargar nuestros objetos Strings espec�ficos de la localidad en un ListResourceBundle, y luego decidimos utilizar un PropertyResourceBundle, el impacto en nuestro c�digo ser� limitado. Por ejemplo, la siguiente llamada a getBundle recuperar� un ResourceBundle para la localidad apropiada, tanto si ButtonLabel est� constituido por una clase o por un fichero de propiedades.
ResourceBundle introLabels = ResourceBundle.getBundle("ButtonLabel", currentLocale);
� Parejas Clave-Valor
Los objetos ResourceBundle contienen un array de parejas clave-valor. La clave, que debe ser un String, es lo que se especificar� cuando querramos recuperar el valor desde el ResourceBundle. El valor es el objeto espec�fico de la localidad. En el siguiente ejemplo, las claves son los objetos String "OkKey" y "CancelKey".
class ButtonLabel_en extends ListResourceBundle { // English version public Object[][] getContents() { return contents; } static final Object[][] contents = { {"OkKey", "OK"}, {"CancelKey", "Cancel"}, }; }
Para recuperar el String "OK" desde el ResourceBundle, deber�amos especificar la clave apropiada cuando llamemos a getString.
String okLabel = ButtonLabel.getString("OkKey");
El ejemplo precedente es muy simple, porque los valores String est�n codificados en el c�digo fuente. Esto no es una buena pr�ctica, porque tus traductores necesitan trabajar con ficheros de propiedades que est�n separados del c�digo fuente.
Un fichero de propiedades contiene parejas de clave-valor. La clave est� en el lado izquiedo del signo igual y el valor en el lado derecho. Cada pareja est� en una l�nea independiente. Las claves s�lo pueden estar representadas por objetos String. El siguiente ejemplo muestra el contenido de un fichero de propiedades llamado ButtonLabel.properties.
OkKey = OK CancelKey = Cancel
�Preparar el uso de un ResourceBundle
Antes de crear y cargar objetos ResourceBundle, se deber�a planificar un poco. Primero, identificar los objetos espec�ficos de la localidad de nuestro programa. Luego, organizar estos objetos en categorias y almacenarlos en diferentes objetos ResourceBundle.
� Identificar los Objetos Espec�ficos de la Localidad
Si la aplicaci�n tiene un interface de usuario, contendr� muchos objetos espec�ficos de la localidad. Deber�amos empezar leyendo el c�digo y busc�ndo objetos que varien con la localidad. La lista podr�a incluir objetos ejemplarizados de las siguientes clases.
- String
- Component
- Graphics
- Image
- Color
- AudioClip
Observar�s que est� lista no contiene objetos que representen n�meros, fechas, horas o monedas. El formato de esos objetos var�a con la localidad pero no as� los propios objetos. Por ejemplo, se formatea un objeto Date de acuerdo a la localidad, pero se sigue utilizando el mismo objeto Date sin importar la localidad. En vez de aislar estos objetos en un ResourceBundle, se deben formatear con clases especiales de formateo sensible a la localidad. Veremos como hacer esto en Formateo de Fechas y Horas.
En general, los objetos almacenados en un ResourceBundle est�n predefinidos y se venden con el producto. Estos objetos no se modifican mientras el programa se est� ejecutando. Por ejemplo, se deber�a almacenar le etiqueta de un Menu en un ResourceBundle porque es espec�fico de la localidad, y no cambia durante la sesi�n del programa. Sin embargo, no se deber�an aislar en un ResourceBundle los objetos String introducidos por el usuario final en un TextField. Los datos de este estilo podr�an variar de d�a a d�a. Son espec�ficos de la sesi�n del programa, no de la localidad en que se est� ejecutando el programa.
Normalmente, la mayor�a de los objetos que se necesita aislar en un ResourceBundle son objetos String. Sin embargo, no todos los objetos String son espec�ficos de la Localidad. Por ejemplo, si un String es un elemento de protocolo utilizado en un proceso de inter-comunicaci�n, no necesita ser localizado porque el usuario final nunca lo ver�. La decisi�n de cuando localizar algunos objetos String no siempre est� clara. Los ficheros LOG son un buen ejemplo. Si un fichero Log es escrito por un programa y le�do por otro, ambos programas est�n utilzando el fichero Log como un buffer de comunicaci�n. Supongamos que el usuario final chequea ocasionalmente el contenido de este fichero. �Deber�a estar localizado este fichero Log? Por otro lado, si el fichero es raramente chequeado por los usuarios finales, el coste de la traduci�n podr�a no merecer la pena. La decisi�n de localizar este fichero Log depende de un n�mero de factores: dise�o del programa, f�cilidad de utilizaci�n, coste de la traduci�n y soportabilidad.
�Organizar Objetos ResourceBundle
Puedes organizar tus objetos ResourceBundle cargando cada uno de ellos en una categor�a diferente de objetos. Por ejemplo, podr�amos querer cargar todos las etiquetas de Button dentro de un ResourceBundle llamada ButtonLabelsBundle. Cargar los objetos relacionados en diferentes objetos ResourceBundle tiene varias ventajas.
- El c�digo es f�cil de leer y de mantener.
- Recuperar un objeto del ResourceBundle es m�s r�pido si este contiene un peque�o n�mero de objetos.
- Los Traductores encontrar�n m�s sencillo el trabajar con ficheros de propiedades m�s peque�os.
La mayor�a de los datos espec�ficos de la localidad consisten en objetos String. Si estos objetos necesitan ser traducidos, se almacenan en un objeto ResourceBundle que est� constituido por ficheros de propiedades. Si se utilizan ficheros de propiedades, los taductores pueden a�adir soporte para idiomas adicionales creando nuevos ficheros de propiedades.
�Utilizar Ficheros de Propiedades
Si nuestra aplicaci�n contiene objetos String que necesitan ser traducidos a diferentes idiomas, deber�amos almacener estos objetos String en un PropertyResourceBundle, que est� constituido por un conjunto de ficheros de propiedades. Como los ficheros de propiedades son sencillos ficheros de texto, pueden ser creados y mantenidos por tus traductores. No necesitas cambiar el c�digo fuente. En esta secci�n aprender�s c�mo seleccionar los ficheros de propiedades que constituyen un PropertyResourceBundle.
Esta secci�n pasea a trav�s de un programa de ejemplo llamado PropertiesDemo. El c�digo fuente del programa est� en PropertiesDemo.java. Tambi�n podr�as encontrar �til examinar la salida generada por este programa.
� 1. Crear el Fichero de Propiedades por defecto
Un fichero de propiedades es un sencillo fichero de texto. Los ficheros de propiedades se pueden crear y mantener con un sencillo editor de texto.
Siempre se debe crear un fichero de propiedades por defecto. El nombre de este fichero empieza con el nombre base del ResourceBundle y termina con el sufijo .properties. En el programa PropertiesDemo, el nombre base es LabelsBundle. Por lo tanto, el fichero de propiedades por defecto se llama LabelsBundle.properties. Este fichero contiene las siguientes l�neas.
# This is the default LabelsBundle.properties file s1 = computer s2 = disk s3 = monitor s4 = keyboard
En este fichero se puede observar que las l�neas de comentarios empiezan con una almohadilla (#). Las otras l�neas contienen parejas de clave-valor. Las claves est�n en el lado izquierdo del signo igual y los valores en el lado derecho. Por ejemplo, "s2" es la clave que corresponde con el valor "disk". Esta clave es arbitraria. Podr�amos haberla llamado algo como "msg5" o "diskID.", por ejemplo. Sin embargo, una vez definida, la clave no deber�a cambiar porque es referenciada dentro del c�digo fuente. De echo, cuando los localizadores crean un nuevo fichero de propiedades para acomodar idiomas adicionales, traducir�n los valores a diferentes idiomas, pero no las claves.
�2. Crear Ficheros de Propiedades Adiciones si son Necearios
Para soportar una nueva Locale, los localizadores crear�n un nuevo fichero de propiedades que contenga los valores traducidos. No se necesita cambiar el c�digo fuente, ya que el programa referencia las claves, no los valores.
Por ejemplo, para a�adir soporte para el idioma Alem�n, los localizadores tendr�n que traducir los valores de LabelsBundle.properties y situarlos en un fichero llamado LabelsBundle_de_DE.properties.
Observa que el nombre de este fichero, al igual que el fichero por defecto, empieza con el nombre base LabelsBundle y termina con el sufijo .properties. Sin embargo, como el fichero se ha creado para una localidad espec�fica, el nombre base es seguido por el c�digo del idioma (de) y el c�digo del pa�s (DE). El contenido de LabelsBundle_de_DE.properties es �ste.
# This is the LabelsBundle_de_DE.properties file s1 = Computer s2 = Platte s3 = Monitor s4 = Tastatur
Hemos lanzado tres ficheros de propiedades con el programa de ejemplo PropertiesDemo.
LabelsBundle.properties LabelsBundle_de_DE.properties LabelsBundle_fr.properties
�3. Especificar la Localidad
En el programa PropertiesDemo hemos creado los objetos Locale de esta forma.
Locale[] supportedLocales = { new Locale("fr","FR"), new Locale("de","DE"), new Locale("en","US") Locale currentLocale = new Locale("fr","FR");
Para cada uno de estos objetos hemos espec�ficado un c�digo de idioma y un c�digo de pa�s. Esto c�digos corresponden con los ficheros de propiedades creados en los pasos anteriores. Por ejemplo, el Locale creado con los c�digos de y DE corresponde con el fichero LabelsBundle_de_DE.properties.
�4. Crear el ResourceBundle
Este es el paso que muestra como se relacionan, la localidad, los ficheros de propiedades y el ResourceBundle. Para crear el ResourceBundle, llamamos al m�todo getBundle, especificando el nombre base y la localidad.
ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle",currentLocale);
El m�todo getBundle primero busca un fichero de clase que corresponda con el nombre base. Si no puede encontrar el fichero de clase, comprueba los ficheros de propiedades. En el programa PropertiesDemo, hemos constituido el ResourceBundle con ficheros de propiedades en vez de ficheros de clases. Cuando el m�todo getBundle localiza el fichero de propiedades correcto, devuelve un objeto PropertyResourceBundle cargado con las parejas clave-valor del fichero de propiedades.
Si no existe un fichero de propiedades para la localidad espec�ficada, getBundle selecciona el fichero de propiedades con la correspondencia m�s cercana. La siguiente tabla identifica los ficheros de propiedades que buscar� el programa PropertiesDemo para cada localidad.
Par�metros Locale | Fichero de Propiedades | Explicaci�n |
---|---|---|
de DE |
LabelsBundle_de_DE.properties | Correspondencia Exacta. |
fr FR |
LabelsBundle_fr.properties | LabelsBundle_fr_FR.properties no existe, pero es la correspondencia m�s cercana. |
en US |
LabelsBundle.properties | Se selecciona el fichero por defecto porque los par�metros de la localidad no corresponden. |
En lugar de llamar a getBundle, podr�amos haber creado el objeto PropertyResourceBundle llamando a su constructor, que acepta un InputStream como argumento. Para crear el InputStream debemos espec�ficar el nombre exacto del fichero de propiedades en la llamada al constructor de FileInputStream. Crear el PropertyResourceBundle llamando al m�todo getBundle es m�s flexible, porque buscar� los ficheros de propiedades con la correspondencia m�s cercana a la localidad espec�ficada.
�5. Obtener el Texto Localizado
Para recuperar los valores traducidos desde el ResourceBundle, llamamos al m�todo getString.
String value = labels.getString(key);
El String devuelto por getString corresponde con la clave que hemos especificado. El String est� en el idioma apropiado, proporcionado por un fichero de propiedades existente para la localidad espec�ficada. Como las claves no cambian, los localizadores a�aden ficheros de propiedades adicionales posteriormente,. Nuestra llamada a getString no necesita cambiar.
�6. Iterar a trav�s de todas las Claves
Si queremos recuperar los valores para todas las claves de un ResourceBundle, necesitamos llamar al m�todo getKeys. Este m�todo devuelve una Enumeration con todas las claves de un ResourceBundle. Se puede iterar a trav�s de la Enumeration y recuperar cada valor con el m�todo getString. Las siguientes l�neas de c�digo del programa PropertiesDemo, muestran como se hace esto.
ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle",currentLocale); Enumeration bundleKeys = labels.getKeys(); while (bundleKeys.hasMoreElements()) { String key = (String)bundleKeys.nextElement(); String value = labels.getString(key); System.out.println("key = " + key + ", " + "value = " + value); }
�Utilizar un ListResourceBundle
La clase ListResourceBundle, que es una subclase de ResourceBundle, maneja objetos espec�ficos de la localidad con una lista. Un ListResourceBundle est� constituido por un fichero de clase, lo que significa que se deber� codificar y compilar un nuevo fichero fuente cada vez que querramos soportar una nueva localidad. Por lo tanto, no se deber�a utilizar un ListResourceBundle para aislar objetos String que deban ser traducidos a otros idiomas. Para aislar texto traducible, se deber�a utilizar un PropertyResourceBundle porque est� constituido por un conjunto de ficheros de propiedades editables. Sin embargo, los objetos ListResourceBundle son �tiles, porque al contrario que los ficheros de propiedades, pueden almacenar cualquier tipo de objeto espec�fico de la localidad. Pasando a trav�s de un programa de ejemplo, en esta secci�n veremos c�mo utilizar un ListResourceBundle.
Esta secci�n ilustra el uso de un objeto ListResourceBundle con un programa de ejemplo llamado ListDemo. Explicaremos cada paso involucrado en la creacci�n del programa ListDemo, junto con las subclases de ListResourceBundle que soporta. El c�digo fuente del programa est� en ListDemo.java. Tambi�n podr�as querer examinar la salida producida por el programa.
�1. Crear las Subclases de ListResourceBundle
Un ListResourceBundle est� constituido por un fichero de clase. Por lo tanto, nuestro primer paso es crear el fichero de clase para cada Localidad soportada. En el programa ListDemo, el nombre base del ListResourceBundle es StatsBundle. C�mo ListDemo soporta tres localidades diferentes, requiere los siguientes ficheros de clases.
StatsBundle_en_CA.class StatsBundle_fr_FR.class StatsBundle_ja_JA.class
La clase StatsBundle para Jap�n est� definida en el c�digo fuente de la siguiente forma. Observa que el nombre de la clase est� construido a�adiendo el c�digo de idioma y del pa�s al nombre base del ListResourceBundle. Dentro de la clase, se incializa un array de dos dimensiones contents con parejas de clave valor. Las claves son el primer elemento de cada pareja: GDP, Population, y Literacy. Las claves deben ser objetos String, y deben ser iguales en cada fichero class del conjunto StatsBundle. Los valores pueden ser cualquier tipo de objeto. En este ejemplo, los valores son dos objetos Integer y un objeto Float.
import java.util.*; public class StatsBundle_ja_JA extends ListResourceBundle { public Object[][] getContents() { return contents; } private Object[][] contents = { {"GDP", new Integer(21300)}, {"Population", new Integer(125449703)}, {"Literacy", new Double(0.99)}, }; }
�2. Especificar la Localidad
En el programa ListDemo, hemos definido los siguientes objetos Locale.
Locale[] supportedLocales = { new Locale("en","CA"), new Locale("ja","JA"), new Locale("fr","FR") };
Cada objeto Locale corresponde a una clase de StatsBundle. Por ejemplo, la localidad Japonesa, que fue definida con los c�digos ja y JA, corresponde con StatsBundle_ja_JA.class.
�3. Crear el ResourceBundle
Para crear el ListResourceBundle, llamamos al m�todo getBundle. En la siguiente l�nea de c�dido, observa que hemos espec�ficado el nombre base de la clase (StatsBundle) y la Localidad.
ResourceBundle stats = ResourceBundle.getBundle("StatsBundle",currentLocale);
El m�todo getBundle buscar� una clase cuyo nombre empiece con StatsBundle y est� seguido por los c�digos de idioma y de p�is espec�ficados por la Locale. Por ejemplo, si currentLocale se crea con los c�digos ja y JA, getBundle devuelve un objeto ListResourceBundle cargado con la clase StatsBundle_ja_JA.
�4. Recuperar Objetos Localizados
Ahora que tenemos un ListResourceBundle para la Localidad apropiada, recuperaremos los objetos localizados por sus claves. En la siguiente l�nea de c�digo, recuperarmos el ratio de alfabetizaci�n llamando al m�todo getObject con el par�metro "Literacy". Como getObject devuelve un objeto, debemos tiparlo a Double.
Double lit = (Double)stats.getObject("Literacy");