Internacionalizando aplicaciones con Java. Visión básica.

La internacionalización, también conocida como i18n (es curioso, pero en inglés hay 18 letras entre la letra «i» y la letra «n» de la palabra internacionalization), es la habilidad de una aplicación para adaptarse a distintos idiomas sin tener que realizar cambios en el código fuente.

Las características principales que debe cumplir una aplicación internacionalizada son:

  • con un mismo ejecutable de la aplicación se debe poder ejecutar la aplicación en distintos idioma simplemente cambiando la información de localización regional del usuario.
  • los textos susceptibles de cambiar de idioma no pueden incluirse directamente en el código. A cambio en el código aparecerá una variable que servirá para identificar el verdadero texto a mostrar.
  • soportar nuevos idiomas no debe requerir la re-compilación de la aplicación.
  • la información que pueda depender de la configuración regional del usuario aparecerá con el formato adecuado. Por ejemplo, moneda, números, fechas, etcétera.
  • el diseño de la aplicación (desde un inicio) debe estar orientado a la internacionalización.

En un alto porcentaje de los proyectos en los que participo conviven Euskera y Castellano, por lo que es muy necesario tener claros los conceptos básicos de la internacionalización.

Mi intención en esta entrada es, a través de un ejemplo, explorar los conceptos más básicos de i18n, entendiendo las ventajas que proporciona y la manera en la que lo hace.

La aplicación de ejemplo.

La aplicación de ejemplo que voy a utilizar para ilustrar el uso de la internacionalización recoge un nombre por teclado y lo escribe en la consola. Es un ejemplo muy simple pero muy útil para mostrar el funcionamiento de i18n.

La aplicación en funcionamiento.

El código fuente de la aplicación, sin internacionalizar es una clase verdaderamente simple; BasicConsoleLogin.java. Consta de dos métodos, uno privado y otro publico, los dos estáticos. El método privado readInput() es el encargado de leer el nombre introducido por el usuario a través del teclado. El método publico main(String[] args) contiene el código funcional de la aplicación.

/**
 * <p>Recoge la entrada del usuario vía teclado. </p>
 * @return java.lang.String la entrada insertada por teclado.
 */
 private static String readInput()
 {
   BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
   String s = new String("");
   try {
     s = bufferRead.readLine(); 
   } catch (IOException e) {
     s = getStackTrace(e);
   }
   return s;
 }

 public static void main(String[] args) {
    System.out.println("Introduce tú nombre:");
    String nombre= readInput();
    System.out.println("Tu nombre es " + nombre);
    System.out.println("Muchas gracias por darme uso");
 
 }

Paso 1:  creación de los bundles de internacionalización.

A través de la internacionalización, el texto de la aplicación que es susceptible de ser traducido a diferentes idiomas se organiza en ficheros de propiedades, uno por idioma.

Estos ficheros se conocen como ‘bundles’ y contienen múltiples filas que constan de una estructura clave=mensaje_traducido. La clave aparecerá en todos los bundles y el mensaje_traducido variará conteniendo la cadena de texto que corresponde con el idioma.

Para entender lo anterior lo mejor es explicarlo con los ‘bundles’ que voy a utilizar en la aplicación de ejemplo:

Bundle: BasicConsoleLoginMessages_es_ES.properties

LanguageSelection = El idioma elegido es:
InputMessage = Introduce tú nombre:
OutputMessage = Tu nombre es 
AcknowledgeMessage = Muchas gracias por darme uso.

Bundle: BasicConsoleLoginMessages_eu_ES.properties

LanguageSelection = Hizkuntza hau aukeratu duzu:
InputMessage = Idatzi zure izena:
OutputMessage = Zure izena ... da: 
AcknowledgeMessage = Eskerrik asko erabiltzeagatik!

Bundle: BasicConsoleLoginMessages_en_UK.properties

LanguageSelection = The Language chosen is:
InputMessage = Please, type your name:
OutputMessage = Your name is 
AcknowledgeMessage = Thanks for using me!

La aplicación de ejemplo está traducida a tres idiomas: castellano (es_ES), euskera (eu_ES) e inglés (en_UK).

Por cada idioma hay un ‘bundles’ que contienen pares de valores clave = traducción. La clave es igual en todos los ‘bundles’ independientemente del idioma, sin embargo, la traducción es propia de cada uno. Por ejemplo, el mensaje que se usa para animar al usuario a teclear su nombre se llama InputMessage. Este nombre será el que se utilice en la aplicación y en cada ‘bundle’ tendrá una traducción distinta.

Paso 2: Recoger el idioma en el que se ejecuta la aplicación.

Para establecer el idioma en el que el usuario quiere ver la aplicación, inicialmente se pregunta por el idioma. El usuario podrá elegir entre castellano, euskera e inglés, y con esa información la aplicación continuará ejecutándose en el idioma elegido.

La aplicación en euskera.

Normalmente, las aplicaciones suelen recoger la configuración de idioma de la configuración regional del sistema operativo o pueden incorporar un icono en pantalla para el cambio de idioma. Existen múltiples métodos.

En nuestra aplicación de ejemplo, el usuario teclea el idioma que desea. La información que teclea el usuario sirve para crear una instancia de la clase java.util.Locale que usaremos para acceder al ‘bundle’ correspondiente del idioma elegido. El método getLocale(String appIdioma) muestra esta manera de proceder.

private static Locale getLocale(String appIdioma)
 {
   Locale locale;
   if ("es_ES".equals(appIdioma)) {
     locale = new Locale("es", "ES");
   } else if ("eu_ES".equals(appIdioma)) {
     locale = new Locale("eu", "ES");
   } else if ("en_UK".equals(appIdioma)) {
     locale = new Locale("en", "UK");
   } else {
     locale = new Locale("eu", "ES");
   }
   return locale;
 }

Paso 3. Internacionalizar la aplicación

El último paso es adaptar la aplicación para ser internacionalizada. El código fuente internacionalizado quedaría así:

public static void main(String[] args) {
  System.out.println("Sartu aplikazioaren hizkuntza / Introduce el idioma de la aplicación / Tell us application's language (eu_ES|es_ES|en_UK): ");
 
  String appIdioma = readInput();
 
  Locale miLocale = getLocale(appIdioma);
  ResourceBundle rb = ResourceBundle.getBundle("BasicConsoleLoginMessages", miLocale);
 
  System.out.println(rb.getString("LanguageSelection") + miLocale.getLanguage() + "_" + miLocale.getCountry());
  System.out.println(rb.getString("InputMessage"));
  String nombre= readInput();
 
  System.out.println(rb.getString("OutputMessage") + nombre);
  System.out.println(rb.getString("AcknowledgeMessage"));
 
 }

El punto clave es la recuperación del bundle correspondiente al idioma que elija el usuario. Esta recuperación se hace a través de la clase java.util.ResourceBundle, el único requisito es que los ficheros de los ‘bundles’ sean accesibles desde el classloader de la aplicación.

 ResourceBundle rb = ResourceBundle.getBundle("BasicConsoleLoginMessages", miLocale);

Una vez accedido el bundle, recuperar los mensajes de texto es una sencilla llamada al método getString(String clave);

rb.getString("OutputMessage")

Conclusión

La internacionalización no aporta excesiva dificultad adicional al código y permite traducir las aplicaciones a diferentes idiomas de forma muy sencilla.

Esta aplicación que he utilizado en el ejemplo es una aplicación excesivamente sencilla y no ilustra la dificultad que puede llegar a ser internacionalizar una aplicación no preparada para ello. En las aplicaciones complejas se hace necesario sino obligatorio un diseño inicial i18n, ya que incorporarlo posteriormente puede darnos más de un quebradero de cabeza.

Links

2 comentarios en “Internacionalizando aplicaciones con Java. Visión básica.

Deja un comentario