Es frecuente que las aplicaciones informáticas ofrezcan la posibilidad de cambiar el idioma de su interfaz de usuario, mostrando los textos de etiquetas, mensajes, etc. en el idioma preferido por el usuario. Java ofrece la clase ResourceBundle para facilitar esta tarea durante el desarrollo de las aplicaciones.

El uso de esta clase se fundamenta en la utilización de una serie de archivos de propiedades que contienen los textos que se mostrarán al usuario, disponiendo de un archivo de propiedades para cada idioma que se ofrezca al usuario. Esos archivos de propiedades tienen las mismas características que los utilizados con la clase Properties. Por tanto, para cada mensaje que se desee mostrar al usuario con posibilidad de hacerlo en diferentes idiomas, se tendrá una pareja del tipo etiqueta=valorMensaje, donde etiqueta será un identificador para un determinado mensaje que deberá usarse siempre igual para el mismo mensaje en los diferentes idiomas, mientras que valorMensaje será el mensaje concreto en cada idioma que se mostrará al usuario.

Supongamos el caso en el que se quiere mostrar en pantalla el nombre y apellidos de una persona, precedidos en cada caso de una etiqueta informativa que indique se está mostrando el nombre en un caso y los apellidos en otro, de manera similar a la siguiente:

Nombre: FERNANDO
Apellidos: FLORES GARCIA

Si no se usara la internacionalización usando Bundles, para poder mostrar una determinada etiqueta en distintos idiomas habría que utilizar algo como lo siguiente:

String personName = "FERNANDO";
if(locale.equals(Locale.ENGLISH)) {
    System.out.println("Name:" + personName);
} else if(locale.equals(Locale.FRENCH)) {
    System.out.println("Nom:" + personName);
} else {
    System.out.println("Nombre:" + personName);
}

Es decir, para cada etiqueta que se quisiera mostrar en pantalla, habría que preguntar por cada posible idioma cómo debe mostrarse. Si pensamos en un formulario con muchos datos el código sería demasiado extenso y complejo de mantener.

En cambio, usando objetos ResourceBundle tan sólo se debe seleccionar una vez el conjunto de etiquetas que se quiere utilizar en función del idioma deseado. Ese conjunto de etiquetas se obtendrá de un archivo independiente que se podrá editar sin tener que tocar el código fuente de la aplicación.

ResourceBundle bundle = ResourceBundle.getBundle("res/strings", locale);
String personName = "FERNANDO";
System.out.print(bundle.getString("name")+": "+personName);

Para este último ejemplo, se deberá disponer de un paquete llamado res (abreviatura de resources, aunque puede ser cualquier otro nombre de paquete), y dentro de él existirá un archivo de propiedades llamado strings (o con el nombre que se indique), que contendrá los valores correspondientes a las etiquetas para el idioma que se establezca por defecto, y otros archivos de propiedades cuyo nombre empezará de la misma manera (strings) pero seguido del código correspondiente al idioma y región asociado a las etiquetas que contiene dicho archivo.

bundle01

En la imagen que se muestra se puede ver que se han creado 3 archivos de propiedades (strings, strings_en_US y strings_fr). De esa manera, el primer archivo contendrá las etiquetas preestablecidas para la aplicación, el segundo sería para el caso del idioma Inglés para Estados Unidos, y el tercero para Francés de cualquier región. Ten en cuenta que si no se indica un código de región, se tomará el archivo correspondiente para el idioma indicado independientemente de la región que se utilice.

Un ejemplo del contenido que podrían tener dichos archivos se muestra en la siguiente imagen:

bundle02

Puedes ver que todos los archivos contienen la mismos nombres clave pero el valor que se le asocia está relacionado con el idioma correspondiente al nombre del archivo.

Recuerda que para añadir propiedades a uno de estos archivos se hace de la misma manera que en cualquier otro archivo de tipo Properties desde NetBeans. Puedes editarlo directamente como cualquier archivo de texto, o bien usar el menú Add > Property.

bundle03

Si utilizas la opción Open del menú anterior, verás que dispones de otra manera de editar estos archivos, de una manera más gráfica, teniendo en la misma ventana una vista de todas las etiquetas en los idiomas creados, con la opción de editarlos o añadir nuevas propiedades.

bundle04

Para crear los archivos de los diferentes idiomas debes partir del archivo correspondiente al idioma predefinido, y a partir de él, usando en su menú la opción Add > Locale podrás ayudarte de una ventana de diálogo que facilita la asignación del nombre del archivo correspondiente, y automáticamente copiará las mismas propiedades que se tuviera en el archivo del idioma predeterminado. Será labor del programador hacer las traducciones de cada texto contenido en los valores.

bundle05

Uso desde código Java

El uso de cualquiera de esas propiedades debe partir de tener un objeto de la clase ResourceBundle. Para ello, esta clase dispone del método getBundle que directamente retorna un objeto de dicha clase. Dicho objeto estará relacionado con el nombre de archivo de propiedades (sin la parte del idioma) que se indique en el primer parámetro, y el idioma deseado (Locale). Opcionalmente puedes omitir el segundo parámetro para obtener las propiedades correspondiente al idioma predefinido.

ResourceBundle getBundle(String baseName, Locale locale)

ResourceBundle getBundle(String baseName)

Por ejemplo, puedes hacer algo como lo siguiente para que se carguen las propiedades del archivo res/strings_en_US, que tendrá los mensajes en inglés de Estados Unidos.

Locale locale = new Locale("en","US");
ResourceBundle bundle = ResourceBundle.getBundle("res/strings", locale);

(Recuerda que para obtener el Locale correspondiente a la configuración del sistema, más concretamente a la configuración de la máquina virtual Java puedes usar: Locale.getDefault()).

De esa manera tendríamos en el objeto bundle de la clase ResourceBundle todas las propiedades que se encuentren en el archivo correspondiente. A partir de este momento, para obtener una propiedad en concreto puedes usar el método getString.

String getString(String key)

Este método retorna el valor (String) correspondiente a la propiedad cuyo nombre clave se indique en el parámetro. Por ejemplo, para mostrar en la salida estándar el valor contenido en la propiedad name:

System.out.print(bundle.getString("name"));

Así, en función del parámetro Locale que se haya pasado al obtener el ResourceBundle, se mostrará en pantalla: "Nombre", "Name" o "Nom".

Ahora puedes ver un ejemplo completo en el que se usarían los archivos de propiedades mostrados anteriormente:

public class EjemploInternacional {

    //Variable que almacenará el idioma seleccionado
    private static Locale locale;
    
    public static void main(String[] args) {
        //Obtener el idioma por defecto del sistema (de la máquina virtual Java)
        locale = Locale.getDefault();
        
        //Método que muestra una serie de etiquetas en el idioma seleccionado
        showPersonData();
        
        //Ahora en inglés americano
        locale = new Locale("en","US");
        showPersonData();
        
        //En este caso se utilizará francés de Francia, y se cargará el contenido
        //  del archivo <strings_fr>, aunque no haya <strings_fr_FR>
        locale = new Locale("fr","FR");
        showPersonData();
        
        //Si se intenta usar un idioma del que no haya recursos, se tomarán los
        //  recursos del archivo por defecto <strings>
        locale = new Locale("it","IT");
        showPersonData();    
    }
    
    public static void showPersonData() {
        //Mostrar el nombre del idioma obtenido
        System.out.println(locale.getDisplayName());
        System.out.println("====================");

        //Obtener los recursos desde el archivo <res/strings> en función del idioma <locale>
        ResourceBundle bundle = ResourceBundle.getBundle("res/strings", locale);
        
        System.out.print(bundle.getString("name")+": ");
        System.out.println("FERNANDO");
        System.out.print(bundle.getString("surnames")+": ");
        System.out.println("FLORES GARCIA");
        System.out.print(bundle.getString("address")+": ");
        System.out.println("AVENIDA GRAN VIA, 10");
        System.out.println("");
    }
    
}

El resultado obtenido por la salida estándar es el siguiente:

español (España)
====================
Nombre: FERNANDO
Apellidos: FLORES GARCIA
Dirección: AVENIDA GRAN VIA, 10

inglés (Estados Unidos)
====================
Name: FERNANDO
Apellidos: FLORES GARCIA
Address: AVENIDA GRAN VIA, 10

francés (Francia)
====================
Nom: FERNANDO
Prénom: FLORES GARCIA
Adresse: AVENIDA GRAN VIA, 10

italiano (Italia)
====================
Nombre: FERNANDO
Apellidos: FLORES GARCIA
Dirección: AVENIDA GRAN VIA, 10

Uso desde la vista Diseño de NetBeans

En la vista Diseño de NetBeans puedes indicar qué valor debe tomar un determinado elemento de la ventana en función del idioma que se haya determinado en el código de la aplicación. Para ello, se debe acceder a la opción Text del cuadro de diálogo de las propiedades.

bundle06 

Al usar el botón de los puntos suspensivos se abrirá una ventana de diálogo donde deberás seleccionar en la lista desplegable superior la opción Resource Bundle. En el cuadro Bundle Name deberás seleccionar el nombre del archivo de propiedades que corresponda con los valores para el idioma predeterminada y en el cuadro Key, el nombre clave del valor a asociar.

bundle07

Esta operación se traducirá automaticamente en el código fuente a algo similar a los siguiente:

java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("res/strings");
jLabel2.setText(bundle.getString("name"));

Como verás, al obtener el objeto bundle no se especifica un objeto Locale, por lo que siempre tomará el idioma que haya establecido por defecto. Así que si se quiere que la aplicación mustre los mensajes en otro idioma, tendrás que cambiar el idioma por defecto de la aplicación, con el método setDefault de la clase Locale, por ejemplo:

Locale.setDefault(new Locale("en","US"));

O para establecer el idioma en francés, sin especificar región puedes usar la constante Locale.FRENCH:

Locale.setDefault(Locale.FRENCH);

Si estableces el idioma de cualquiera de esas maneras antes de que se inicialicen (initcomponents()) los componentes de la ventana podrás ver cómo cambian los textos de la misma, y aparecerán, por ejemplo, en inglés:

bundle08