Internacionalización (i18n) en JavaScript

Escrito por el , actualizado el .
javascript planeta-codigo programacion
Enlace permanente Comentarios

JavaScript

Si desarrollamos una aplicación web, que es usada en múltiples idiomas y el lado del cliente tiene cierta complejidad quizá nos encontremos con la necesidad de proporcionar internacionalización (i18n) para los textos o mensajes mediante una librería javascript. Una de la que más me ha gustado de las que he encontrado ha sido i18next pero hay varias opciones más, incluidas dos que merecen ser nombradas que son polyglot y messageformat, estas tres opciones son parecidas pero no tienen exactamente las mismas funcionalidades, deberemos evaluarlas para elegir una según lo que necesitemos.

La documentación de i18next no es muy extensa pero es suficiente, las funcionalidades que ofrece son:

  • Soporte para variables (interpolación)
  • Soporte para variables anidadas
  • Soporte para múltiples formas plurales e indefinidos
  • Soporte sprintf
  • Integración con jquery
  • Búsqueda de locales
  • Obtención de traducciones del servidor
  • Cacheo de recursos en el navegador
  • Algunas otras adicionales

Para mostrar su uso me basaré en el ejemplo Lista de tareas con Backbone y React al que le añadiré el soporte de internacionalización con i18next. Como en el ejemplo uso RequireJS usaré la versión de i18next con soporte para AMD. Una vez descargada y colocada en el directorio de los módulos de RequireJS deberemos añadir la dependencia a los módulos donde la usemos, en el caso del ejemplo en tareas.js. En el ejemplo los textos que se necesitan internacionalizar son el título, el texto que aparece en el input antes de introducir el nombre de una tarea, el número de tareas completadas y el texto del botón limpiar, para mostrar estos textos usaremos la función t de i18next que nos devolverá dada la clave del texto que queremos el valor adecuado según los archivos de literales y el idioma de la aplicación. Todo esto lo hacemos de la siguiente forma:

1
2
i18n.t('Lista_de_tareas')

tareas-1.js

Los archivos de literales son poco más que una relación de claves valor similar a los archivos properties de Java aunque en el caso de i18next se definen en archivos con formato json. En este ejemplo la localización (l10n) que proporcionaré será para español (translation-dev.json, idioma por defecto) y para inglés (translation-en.json). Por cada idioma localizado necesitamos crea un archivo con los literales:

1
2
3
4
5
6
7
8
{
    "Lista_de_tareas": "Lista de tareas",
    "COMPLETADAS_tareas_completadas_de_TOTAL": "__completadas__ tarea completada de __total__",
    "COMPLETADAS_tareas_completadas_de_TOTAL_plural": "__completadas__ tareas completadas de __total__",
    "Muy_bien_has_completado_todas_las_tareas": "¡Muy bien! has completado todas las tareas",
    "Limpiar": "Limpiar",
    "Introduce_una_nueva_tarea": "Introduce una nueva tarea"
}
translation-dev.json
1
2
3
4
5
6
7
8
{
    "Lista_de_tareas": "Tasks List",
    "COMPLETADAS_tareas_completadas_de_TOTAL": "__completadas__ task completed of __total__",
    "COMPLETADAS_tareas_completadas_de_TOTAL_plural": "__completadas__ tasks completed of __total__",
    "Muy_bien_has_completado_todas_las_tareas": "¡Perfect! You have done all tasks",
    "Limpiar": "Clean",
    "Introduce_una_nueva_tarea": "Type a new task"
}
translation-en.json

Los lenguajes tienen diferentes formas plurales, por ejemplo, en español hay dos formas plurales (1 y más de uno) pero dependiendo del número de elementos a los que hagamos referencia y el lenguaje puede variar el número de formas plurales. Si en una aplicación ves las típicas eses entre paréntesis, (s), es porque esa aplicación aunque esté internacionalizada no soporta las múltiples formas plurales de los lenguajes, para un usuario ver esos (s) crea confusión y dificulta la lectura del texto. Si nos encontramos con este caso el literal de la forma plural lo definiríamos y lo obtendríamos de la siguiente forma en el caso del español, el parámetro count se utiliza para determinada la forma plural a utilizar (en el caso de español, singular o plural) y los parámetros completadas y total como parámetros del literal usando sus valores en la interpolación en la cadena:

1
2
3
4
5
6
7
8
9
i18n.t('COMPLETADAS_tareas_completadas_de_TOTAL', { count: 1, completadas: 1, total: 4 }); 
// -> { count: 1 } -> COMPLETADAS_tareas_completadas_de_TOTAL -> 
// -> __completadas__ tarea completada de __total__ + { completadas: 1, total: 4 }
// -> 1 tarea completada de 4

i18n.t('COMPLETADAS_tareas_completadas_de_TOTAL', { count: 2, completadas: 2, total: 4 });
// -> { count: 2 } -> COMPLETADAS_tareas_completadas_de_TOTAL_plural
// -> __completadas__ tareas completadas de __total__ + { completadas: 2, total: 4 }
// -> 2 tareas completadas de 4
tareas-2.js

Para completar el ejemplo debemos inicializar la librería i18next con la configuración que queramos por lo menos para definir el locale y la disposición de los archivos de literales. Esta configuración en el ejemplo está antes de inicializar el componente de la lista de tareas:

1
2
3
4
5
6
7
8
9
define(['react', 'tareas', 'i18next'], function(React, tareas, i18n) {
	i18n.init({ 
		lng: requirejs.s.contexts._.config.locale,
		getAsync: false,
		resGetPath: 'js/locales/__lng__/__ns__.json'
	});
	
	// ...
});
main.js

Cambiando el idioma preferido en el navegador podemos ver los textos de la aplicación según el mismo:

Aplicación en español Aplicación en inglés

El texto que indica cuantas tareas están completadas usa las diferentes formas plurales del lenguaje, en el caso del español dos, singular y plural cuando se marca una o dos tareas completadas.

Una tarea completada Dos tareas completadas

i18next se encarga de obtener los archivos de traducciones automáticamente según el idioma en que se deba mostrar la aplicación, para el caso de que el usuario tenga como idioma preferido es-ES se buscarán los archivos localizados es-ES, es y finalmente dev:

Búsqueda de traducciones Búsqueda de traducciones

Una vez hechos los cambios y habiendo modificado las pruebas unitarias de javascript comprobamos que todos siguen pasando correctamente:

Pruebas unitarias

Eso es todo, este ejemplo aunque sencillo muestra bastantes cosas que se podrían utilizar como base para algo real, usa RequireJS, Mustache, Backbone, React, Jasmine, Grunt y ahora i18next por la parte cliente y RESTEasy y Apache Tapestry por la parte servidor, cada una de estas tecnologías las he comentado individualmente en varios artículos.

El código fuente completo de este ejemplo está en mi repositorio de GitHub, una vez descargado el código puedes probarlo en tu equipo con el siguiente comando:

1
2
$ ./gradlew tomcatRun

gradle-tomcatRun.sh


Comparte el artículo: