VERSIONES
- 08/01/2014 (Primera publicación)
- 07/12/2015: Revisión y mejoras
El soporte multilenguaje o la capacidad de internacionalización (i18n) de los textos de una aplicación es una de las funcionalidades básicas de cualquier framework. Lo habitual en el mundo Java es utilizar ficheros de texto plano (los .properties gestionados mediante ResourceBundle) para definir las cadenas de texto que deban ser internacionalizadas junto a una clave única para poder recuperar dicha cadena. Tendremos un fichero de localización por cada idioma o por cada idioma y país (por ejemplo, inglés británico e inglés de Estados Unidos) y habrá un mecanismo que permita seleccionar el idioma a mostrar, ya sea de forma automática (por ejemplo, según el idioma del navegador) o bien mediante alguna selección o preferencia indicada por el usuario.
Localizar los textos de nuestras pantallas con JSP y Struts 2 es sencillo. Por defecto, Struts 2 utiliza como origen para los textos localizados de una pantalla el properties con el idioma que corresponda al que tenga seleccionado el usuario en el navegador. Este fichero se deberá ubicar físicamente en el mismo directorio que el .class con el Action que muestra la pantalla y tendrá el nombre del action, el sufijo con el idioma (y país si fuera necesario) y la extensión .properties.
Sin embargo, resulta más habitual «centralizar» todos los textos en un único fichero para cada idioma simplemente indicando el prefijo de ese fichero en el struts.xml. Por ejemplo, en este ejemplo se va a utilizar como prefijo «i18n», también es muy habitual usar «global».
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="i18n" /> <!-- enable 'action' attribute --> <constant name="struts.mapper.action.prefix.enabled" value="false" /> <!-- enable 'method' attribute--> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <package name="default" namespace="/" extends="struts-default"> <action name="mainAction" class="com.danielme.tips.struts2.tip11.actions.MainAction"> <result name="success">/jsp/i18n.jsp</result> </action> </package> </struts>
El proyecto de ejemplo tendrá dos ficheros: uno con los textos en inglés y otro con los textos en español. El idioma que consideremos por defecto (inglés) estará en el fichero i18n.properties, aunque también es posible configurar Struts 2 para establecer cierto idioma como predeterminado:
<constant name="struts.locale" value="es" />
El otro fichero se llamará i18n_es.properties. Los sufijos con los idiomas y países son los que utiliza Java de forma estándar, y suelen resultar intuitivos. Los ficheros de ejemplo:
/src/main/resources/i18n.properties
title=tip 11 - i18n msg = Hello World!!
/src/main/resources/i18n_es.properties
title=tip 11 - i18n msg = ¡Hola mundo!
En estos ficheros hay que tener especial cuidado con la codificación de las tildes y caracteres especiales,y utilizar las secuencias de escape para HTML o directamente el código Unicode en función de cómo se haya configurado la aplicación. También para una correcta visualización puede ser necesario utilizar el atributo escape de las etiquetas de Struts 2.
Para recuperar estos textos en una JSP, Struts 2 proporciona la etiqueta s:text a la que se indicará la clave del texto a mostrar. Lo mismo se puede conseguir mediante código invocando una de las versiones del método getText que tenemos disponible en nuestro Action si heredamos de ActionSupport. Este método es accesible a través de OGNL, por lo que podemos invocarlo en la JSP dentro de cualquier atributo que admita una expresión OGNL. En la siguiente JSP se muestran estas dos formas de acceder al texto localizado.
src/main/webapp/jsp/i18n.jsp
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <%@include file="/jsp/head.jsp" %> </head> <body> <h4>s:text: <s:text name="msg"/></h4> </br> <h3>s:property <s:property value="getText('msg')"/> </h3> <%@include file="/jsp/footer.jsp" %> </body> </html>
A continuación se muestra la pantalla para Internet Explorer 8 en español y Chromium 28 en inglés.
Selección de idioma
Una funcionalidad que probablemente necesitaremos al trabajar con aplicaciones que admitan múltiples idiomas es la de permitir que el usuario elija el idioma que desee mediante un enlace.
Struts 2 proporciona esta funcionalidad en el interceptor I18nInterceptor. Lo que haremos será poner un enlace al action que tenemos pasando el parámetro «request_locale» con el locale deseado (el nombre de este parámetro es configurable). El locale seleccionado se utilizará durante toda la sesión actual a menos que el usuario vuelva a cambiar de idioma. Esta será la JSP final (obsérvese que se ha localizado también el nombre del idioma para el enlace).
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <%@include file="/jsp/head.jsp"%> </head> <body> <s:url id="en" action="mainAction"> <s:param name="request_locale">en</s:param> </s:url> <s:a href="%{en}"> <s:text name="en" /> </s:a> <s:url id="es" action="mainAction"> <s:param name="request_locale">es</s:param> </s:url> <s:a href="%{es}"> <s:text name="esp" /> </s:a> <h4> s:text: <s:text name="msg" /> </h4> </br> <h3> s:property <s:property value="getText('msg')" /> </h3> <%@include file="/jsp/footer.jsp"%> </body> </html>
Debe tenerse en cuenta que a los enlaces de cambio de idioma hay que indicarle un action que «recarge» la pantalla por lo que si estos enlaces se encuentran en más de una página habrá que personalizar en cada caso el action de los mismos.
Si fuera necesario, podemos recuperar el idioma seleccionado fácilmente:
<s:property value="locale.language"/>
String language = getLocale().getLanguage();
Localización de fechas en JSP
Para formatear en JSP fechas según un patrón definido en los ficheros i18n y que nos permitirá mostrar fechas debidamente localizadas lo más cómodo es utilizar la siguiente etiqueta:
<s:date name="date" format="%{getText('datetime.format')}"/>
Localización de números en JSP
La localización de formatos numéricos incluyendo moneda se puede realizar definiendo el formato deseado en los .properties de localización según la documentación de MessageFormat. Por ejemplo:
format.currency = {0,number,#,##0.00}
Aplicamos este patrón en la JSP así:
<s:text name="format.currency"> <s:param value="cantidad"/> </s:text>
Mensajes con parámetros
Los textos localizados pueden contener elementos variables parametrizados. Por ejemplo, podemos definir el siguiente texto:
texto.parametros= El usuario {0} no tiene permiso de acceso a esta sección
El valor {0} se reemplazaría así en un Action
String msg = MessageFormat.format(getText("texto.parametros"), "Daniel"));
Y en una JSP
<s:text name="texto.parametros" > <s:param>Daniel</s:param> </s:text>
El proyecto completo para Maven se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.