Вы находитесь на странице: 1из 4

Extendiendo el Runtime de Java con nuevos idiomas

Héctor Rivas

Cuando desarrollamos una aplicación con soporte para internacionalización, solemos (y


debemos) confiar el formateo y análisis de fechas y valores monetarios a clases que actúen
de forma independiente del lenguaje y país actual. Estas clases están incluidas en la propia
plataforma Java1: DateFormat y NumberFormat.

Por desgracia, el número de locales soportados por estas clases es limitado2 y si intentamos
formatear una fecha con un locale desconocido, obtendremos el texto formateado con el
locale por defecto del sistema.

Este problema se agrava al no estar documentada ni definida ninguna forma de extender el


sistema con nuevos locales, obligando al programador a escribir código dependiente del
idioma o utilizar clases propias para el formateo de fechas. Además, numerosas APIs y
frameworks, como Struts, JSTL, SWING, SWT, etc. utilizan el sistema de
internacionalización de Java, comportamiento que en pocas ocasiones podemos modificar,
anulando cualquier solución alternativa.

Podemos comprobar este comportamiento con este sencillo código de prueba:

import java.io.*;
import java.text.*;
import java.util.*;

public class HelloGalician {

public static void main (String Argv[]) {

Locale locale = new Locale("gl", "ES");

DateFormat fmt = DateFormat.getDateTimeInstance(


DateFormat.LONG, DateFormat.LONG, locale);

fmt.setTimeZone(TimeZone.getDefault());

System.out.println("En galego:");
System.out.println("\u00a1Hola Mundo!");
System.out.println(fmt.format(new Date()));
}
}

Si compilamos y ejecutamos este código con un runtime sin soporte de gl_ES la salida es:

En galego:
¡Hola Mundo!
January 2, 2005 4:44:19 PM CET
Pero no todo está perdido, existe una forma de agregar nuestros propios locales de una
forma que, aun siendo poco ortodoxa, funciona correctamente y permite mantener la
independencia del código respecto al idioma.

Si analizamos el contenido de los ficheros .jar de la plataforma (jar –tvf), y en particular los
ficheros jre/lib/rt.jar y jre/lib/ext/localedata.jar , observamos que para todos los locale
soportados existen las siguientes clases:

...
sun/text/resources/LocaleElements_es_MX.class
sun/text/resources/LocaleElements_es_ES.class
sun/text/resources/LocaleElements_es_CA.class
...
sun/text/resources/DateFormatZoneData_es.class
sun/text/resources/DateFormatZoneData_et.class
sun/text/resources/DateFormatZoneData_fi.class
...

Seguramente estas son las clases que debemos añadir. Cabe preguntarse qué son estas
clases. Buceando por internet3 pronto descubrimos que ambas clases heredan de
java.util.ListResourceBundle, que son utilizadas por DateFormat para el formateo de las
fechas y que efectivamente deben existir ambas clases por cada locale formateado.

Sólo nos falta determinar los valores que deben contener estos ListResourceBundle.
Decompilar sería una opción si no lo prohibiese la licencia de SUN, pero por suerte
también resulta sencillo encontrar en internet una implementación de las mismas o,
simplemente, podemos consultar el método getContents() de dichas clases para el locale
por defecto:

import java.util.ResourceBundle;
import java.util.Locale;
import java.util.Enumeration;

public class ListResourcePrint {

static String toString(Object o) {

if (o instanceof Object []) {


Object array [] = (Object []) o;
String str = "[";
for (int i = 0; i < array.length; i++) {
if (i>0) str+=", ";
str+=toString(array[i]);
}
str += "]";
return str;
} else if (o instanceof String) {
return "\""+o.toString()+"\"";
} else {
return o.toString();
}
}

static void printResource(ResourceBundle l) {


for (Enumeration i = l.getKeys();
i.hasMoreElements(); ) {

String key = (String) i.nextElement();


Object value = l.getObject(key);
if (value != null)
System.out.println(key + " = "
+ toString(value));
else
System.out.println(key + " = null");
}
}

public static final void main(String args[]) {

System.out.println("LocaleElements:");
printResource(ResourceBundle.getBundle(
"sun.text.resources.LocaleElements",
Locale.getDefault()));
System.out.println("DateFormatZoneData:");
printResource(ResourceBundle.getBundle(
"sun.text.resources.LocaleElements",
Locale.getDefault()));
}
}

Es ahora cuando podemos implementar nuestras propias clases LocaleElements_gl_ES y


DateFormatZoneData_gl_ES:

package sun.text.resources;
import java.util.ListResourceBundle;

public class LocaleElements_gl extends ListResourceBundle {

/**
* Overrides ListResourceBundle
*/
public Object[][] getContents() {
return new Object[][] {
// locale id based on iso codes
{ "LocaleString", "gl_ES" },

(..)

{ "MonthNames",
new String[] {
"xaneiro", // january
(..)
},
{ "CollationElements", "@" }
};
}}
Una vez implementadas y compiladas, debemos agregarlas a la plataforma. El problema
ahora es que el paquete sun.text.resources es un paquete protegido, y la máquina virtual de
java no las cargará desde el classpath de usuario.

Para que las cargue, debemos construir un fichero .jar con nuestras clases y copiarlo en el
classpath del sistema, concretamente en el directorio de extensiones de Java (normalmente
$JAVA_HOME/jre/lib/ext).

Una vez instalado, ya estamos listos para probar nuestro nuevo locale simplemente
ejecutando el código anterior:

En galego:
¡Hola Mundo!
2 de xaneiro de 2005 16:46:52 CET

Tenemos ahora una máquina virtual parcheada que soporta nuestro nuevo idioma en la que
podemos emplear el API de internazionalización de Java y cualquier otro API o framework
que emplee internazionalización.

De todos modos esta solución plantea algunos problemas. Las clases LocaleElements y
DateFormatZoneData no entran dentro del API de Java, y su funcionamiento puede ser
modificado en futuras versiones de la plataforma. Además, no es posible extender los
locales a nivel de aplicación, sino que la máquina virtual debe parchearse instalando un
paquete en el directorio de extensiones, al cual no siempre tenemos acceso y además
complica la instalación y distribución de la aplicación.

La solución más elegante sería, sin duda, que el propio API de Java permitiese registrar
nuevos locale aparte de los ofrecidos por la plataforma. Mientras tanto, habrá que limitarse
a pequeños trucos como éste y a solicitar el soporte de nuestro locale a los fabricantes.
1
Todo lo comentado a continuación ha sido realizado y probado sobre Java 2 Platform
Standard Edition (J2SE) de Sun. »ir al texto
2
Podemos conocer los locale soportados por el runtime consultando la documentación
(lista de locale soportados por j2se 1.4.2) o consultando el método
Locale.getAvailableLocales(). »ir al texto

Вам также может понравиться