Internacionalización en Java

De ChuWiki
Saltar a: navegación, buscar

La internacionalización en java consiste en hacer nuestro código de forma que funcione correctamente en varios idiomas. Por ejemplo, si estamos en español, nos gustaría ver un botón que diga "Púlsame". Si estamos en inglés, nos gustaría que pusiera "Hit me!". De igual forma las fechas se escriben de distinta manera según el país, etc.

Vamos a centrarnos básicamente en que los textos visibles estén en el idioma del usuario.


¿Cómo se cambian los textos en java?.

Lo fundamental es no escribir dichos textos directamente en el código, sino obtenerlos a partir de algún sitio. Para ello, en algún sitio escribiremos algo como "el texto para el botón es Púlsame". No escribiremos eso exactamente, sino algo similar a Texto_Boton=Pulsame.


¿Dónde escribimos las etiquetas?

Hay básicamente dos opciones. Una es escribirlo en una clase de java especialmente diseñada para eso. La otra opción es escribirlo en un fichero de texto normal. Cuando tengamos el código correctamente hecho y todo bien configurado, java buscará primero si existe esa clase especial para ver qué etiqueta tiene que ponerle al botón. Si no existe esa clase especial, java buscará el fichero. Si no lo hay, saltará una excepción.


¿Qué nombre pongo a la clase o al fichero con las etiquetas?

Se le puede dar el nombre que se quiera. Sin embargo, debemos hacer tantas clases o ficheros como idiomas y paises queramos soportar. Si sólo hacemos una clase de nombre, por ejemplo, Etiquetas, da igual en qué idioma estemos, java siempre mostrará las mismas etiquetas, las de esa clase. Idem si sólo hacemos un fichero Etiquetas.properties. La extensión .properties sí es obligatoria si decidimos poner nuestras etiquetas en un fichero.

El nombre de la clase o del fichero en todos los idiomas debe ser igual, salvo la terminación, que indicará el idioma y el país. Por ejemplo, la clases podrían llamarse Etiquetas, Etiquetas_es, Etiquetas_es_ES, Etiquetas_en, Etiquetas_en_US. Los ficheros, igual pero con extensión .properties, es decir, Etiquetas.properties, Etiquetas_es.properties, Etiquetas_es_ES.properties, etc.

Las dos terminaciones corresponden respectivamente a idioma (_es español, _en inglés) y al país (_ES España, _US Estados Unidos). Por supuesto, dichas terminaciones son estándar y no nos las podemos inventar.

¿Cómo busca java una etiqueta?

Java determina el idioma y país por defecto, en mi caso, español en España, es decir, buscará Etiquetas_es_ES. Si no encuentra la clase con esta terminación ni el fichero, buscará sólo el idioma Etiquetas_es. Si no lo encuentra, irá directamente a la clase o fichero sin terminación, en nuestro caso, Etiquetas.

Supongamos que estamos usando ficheros y existen los tres, Etiquetas_es_ES, Etiquetas_es y Etiquetas. Java buscará en el primero la etiqueta del botón. Si no la encuentra, se irá al segundo. Si ahí tampoco la encuentra, se irá al tercero. Con esto quiero decir que podemos, por ejemplo, poner todas las etiquetas en español en el fichero Etiquetas_es y poner en el siguiente sólo las que cambien para un país concreto de habla hispana.

¿Cómo decimos en java que busque la etiqueta?

Para poner la etiqueta al botón, debemos poner algo como esto

boton.setText(ResourceBundle.getBundle("Etiquetas").getString("Texto_Boton"));

Etiquetas es el nombre de nuestro fichero o clase. Si es fichero, debe situarse en el directorio de ejecución. Si es clase, debe no pertenecer a ningún paquete. Con el método ResourceBundle.getBundle(), java determinará el idioma y país y añadirá la extensión al parámetro Etiqueta que le hemos pasado, es decir, construirá automáticamente la cadena Etiquetas_es_ES.

Luego, buscará una clase con ese nombre Etiquetas_es_ES.class. Si no la hay, buscará un fichero de nombre Etiquetas_es_ES.properties. Una vez que lo encuentre, con el método getString("Texto_Boton") buscará el texto correspondiente a la "Texto_Boton" y ese será el texto que se ponga en el botón.

Si hubiesemos puesto

boton.setText(ResourceBundle.getBundle("chuidiang.ejemplos.Etiquetas").getString("Texto_Boton"));

la clase correspondiente debería pertenecer al paquete chuidiang.ejemplos, o bien el fichero debería estar en un directorio llamado ejemplos, este a su vez dentro de un directorio chuidiang, que debería a su vez estar colocado en el directorio de ejecución del programa.


¿Cómo es un fichero de etiquetas?

El fichero de etiquetas sólo tiene un montón de líneas del estilo clave=valor. Por ejemplo, para español, podría ser

# Etiquetas_es_ES
Texto_Boton=Pulsame
Titulo_Ventana=Una ventana
Texto_Boton_Salir=Salir

el de inglés debería tener exactamente las mismas claves, pero con distintos valores, en inglés

# Etiquetas_en_US
Texto_Boton=Hit me!
Titulo_Ventana=A window
Texto_Boton_Salir=Exit

Las # al principio de línea sirven para comentarios.

Un pequeño detalle, en un fichero de properties no deben ponerse caracteres extraños, como acentos, eñes o caracteres cirílicos. En su lugar deben ponerse los códigos unicode correspondientes. Por ejemplo, para poner la palabra eñe, debemos poner e\u00a4e donde \u00a4 representa la ñ en unicode.

Java nos facilita la obtención de estos caracteres unicode. Java viene con una herramienta llamada native2ascii. Si desde una ventana de msdos escribimos este comando, nos pedirá una cadena de texto. Escribiéndola y pulsando <intro>, obtendremos el equivalente unicode

C:\>native2ascii
eñe
e\u00a4e

Para los ficheros de properties con las etiquetas, podemos escribirlas de forma normal con algo como el notepad, con caracteres extraños. Por ejemplo, podemos escribir el fichero_unicode.properties con este contenido

texto=eñe

Luego salvamos el fichero, pero eligiendo formato UNICODE. El fichero_unicode.properties así salvado no es legible, puesto que va en binario. Pero ahora podemos ejecutar el siguiente comando desde una ventana de ms-dos

C:\>native2ascii -encoding UNICODE fichero_unicode.properties fichero_es.properties

y en fichero_es.properties tendremos el fichero de propiedades con los caracteres extraños reemplazados

texto=e\u00f1e

¿Cómo es una clase de etiquetas?

La clase de Etiquetas debe heredar de ListResourceBundle y definir el método abstracto de la misma. La de español puede ser

import java.util.ListResourceBundle;

public class Etiquetas_es_ES extends ListResourceBundle{
    
    private Object[][] contenido = {
        {"Texto_Boton","Pulsame"},
        {"Titulo_Ventana","Una ventana"},  
        {"Texto_Boton_Salir","Salir"}  
    };
    protected Object[][] getContents() {
        return contenido;
    }
    
}

y para inglés

import java.util.ListResourceBundle;

public class Etiquetas_en_US extends ListResourceBundle{
    
    private Object[][] contenido = {
        {"Texto_Boton","Hit me!"},
        {"Titulo_Ventana","A window"},  
        {"Texto_Boton_Salir","Exit"}  
    };
    protected Object[][] getContents() {
        return contenido;
    }
    
}


¿Mejor fichero o clase?

Los ficheros tienen la ventaja de ser más fácilmente cambiables. Las clases hay que hacerlas y compilarlas, mientras que el fichero basta editarlo. Para añadir un nuevo idioma, basta añadir un nuevo fichero, sin necesidad de recompilar clases.

Sin embargo, el fichero sólo permite obtener String. Con la clase, el valor de cada clave puede ser cualquier Object de java.


¿No es mucho codificar?

Pues sí, pero afortunadamente, los IDEs más mejores, como eclipse o netbeans, tienen opciones de internacionalización, que permiten crear de forma más o menos rápida los ficheros de propiedades y reemplazar en el código la etiqueta por esa llamada tan larga.