jueves, 14 de enero de 2016

Internacionalización i18n en Spring MVC

En muchas ocasiones es necesario tener nuestra aplicación web en varios idiomas. Para ello lo primero que necesitamos en un bean que implemente el interface MessageSource para localizar el
origen de nuestros archivos que contienen el texto en varios idiomas.

Estos archivos tendrán la extension .properties y un sufijo que indicará el idioma (basado en la ISO 639), por ejemplo:
  • "texto_es.properties" -> idioma español.
  • "texto_en.properties" -> idioma inglés.
También se puede añadir un sufijo con el país basado en la ISO 3166:
  • "texto_es_ES.properties" -> español de España.
  • "texto_es_MX.properties" -> español de México.
El código completo del ejemplo esta disponible en github: https://github.com/Jaime-Alonso/TutorialSpringMVC

Spring cuenta con dos clases para obtener el origen de estos archivos:
  1. ResourceBundleMessageSource
  2. ReloadableResourceBundleMessageSource
Estas clases tienen algunas diferencias, la primera es el origen de los archivos, Mientras que ResourceBundleMessageSource mira dentro de la carpeta resources, la segunda puede además buscarlos en el exterior utilizando el prefijo classpath  para dentro de la aplicación, file: dentro del sistema de archivos y http: busca en una url.

Otra diferencia sustancial es que mientra en la primera si modificamos un archivo de propiedades no refleja los cambios hasta que no se re-compile la aplicación y en la segunda no es necesario.

Definición del bean:


@Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("WEB-INF/messages/global");        
        messageSource.setDefaultEncoding("UTF-8");        
        return messageSource;
    }


En la carpeta /WEB-INF/messages/ he creado dos archivos con el siguiente contenido:

global_es.properties
Message.welcome=¡Hola!, Bienvenido a los tutoriales sobre Spring MVC

 y global_en.properties:
Message.welcome=Hello, Welcome to Spring MVC Tutorials


Ahora para utilizar el texto, en nuestros JSP tenemos la opción de hacerlo mediante los TAGs de Spring o mediante JSTL:

<%@taglib uri="http://www.springframework.org/tags" prefix="s"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
...
<h1><s:message code="Message.welcome" /></h1>
<!-- JSTL -->
<h1><fmt:message key="Message.welcome" /></h1>

Con todo ésto podría ser suficiente para internacionalizar la aplicación, pero podríamos personalizar quién debe resolver qué idioma se va a elegir. Aquí entra en acción el LocaleResolver en el que tenemos 3 opciones:
  • AcceptHeaderLocaleResolver: Lee el valor de una petición / request
  • CookieLocaleResolver: Lee/escribe el valor del locale de una cookie llamada org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE (por defecto)
  • SessionLocaleResolver: Lee/Escribe el valor del locale de una sesión HTTP

Si no se especifica un LocaleResolver, se utiliza por defecto: AcceptHeaderLocaleResolver, el cual nos devuelve valor del idioma establecido en las preferencias del navegador y viene reflejado en las cabeceras de las peticiones HTTP.

Si no encuentra el idioma dentro de nuestros archivos de propiedades nos encontraremos con un error:

Estado HTTP 500 - javax.servlet.ServletException: javax.servlet.jsp.JspTagException: No message found under code 'Message.welcome' for locale 'es_ES'.

Por lo que podemos implementar por ejemplo el que se basa en una Cookie y así podemos definir un lenguaje por defecto en el caso de no haber contemplado el idioma del cliente.

    @Bean
    public LocaleResolver localeResolver() {
        CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
        cookieLocaleResolver.setCookieName("language");
        cookieLocaleResolver.setCookieMaxAge(3600);
        cookieLocaleResolver.setDefaultLocale(new Locale("es"));
        return cookieLocaleResolver;
    }

Y para terminar, en ocasiones una aplicación permite en el panel de preferencias del usuario cambiar el idioma, si queremos tener esta característica es necesario utilizar el bean: LocaleChangeInterceptor y añadirlo a la lista de interceptores en la configuración:


@Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {        
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("language");
        return localeChangeInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

En nuestro ejemplo con los siguientes enlaces cambiaríamos el valor de la cookie, en el caso de haber utilizado SessionLocaleResolver pues lo haría en la sesión.

 <a href="?language=en">Cambiar idioma a Inglés</a><br />
 <a href="?language=es">Cambiar idioma a Español</a>  

Si te ha gustado el artículo no olvides de darme un +1 en Google!

No hay comentarios:

Publicar un comentario