Programación en castellano
Inicio > Tutoriales > Lenguajes orientados a objeto > J2SE > Internacionalización de Programas Java
-Tutoriales

Internacionalización de Programas Java


Aislar los objetos específicos de la localidad en un ResourceBundle

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");
 
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad
Mantenida por: Claudio y Dani.

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: jugar gratis | amor | navidad 2009 | registro de dominios | servidores dedicados
más internet: comprar | gratis | posicionamiento en buscadores | decoración libre | gifs animados