martes, 15 de septiembre de 2009

Internacionalización (II). Sakai

En esta entrada vamos a ver con cierto detalle cómo internacionalizar una herramienta en Sakai. Podréis encontrar más información en la web de i18n de Sakai o la de Spanish Sakai, cuya visita os recomiendo.

Antes de empezar
Java proporciona utilidades estándar empleadas para cambiar el formato de número, de las fechas, etc. Siempre que sea posible, se usarán estas clases. En el tutorial sobre i18n que hay la web de Sun encontramos se explican estos aspectos con cierto nivel de detalle.

Los ficheros de recursos
Como dijimos en la entrada anterior, entenderemos por ficheros de recursos de internacionalización (de ahora en adelantate, solamente recursos) como aquellos ficheros que contienen los mensajes que se muestran al usuario y cuyo contenido varía dinámicamente en función de la información de contexto (habitualmente, el idioma seleccionado por dicho usuario). En el caso de Sakai, estos ficheros reciben la denominación genérica de location bundles y físicamente se corresponden con ficheros de texto con el formato nombre_etiqueta=valor guardados con la extensión .properties.

Un aspecto importante es el del orden. Tan sólo en una herramienta de Sakai pueden llega a haber varios cientos de literales y si no los organizamos con cierto criterio, estos ficheros pueden llegar a hacerse verdaderamente inmanejables. Un ejemplo de organización es el de la figura siguiente:

page.message.key = This is a message will be shown in page "page"



Es decir, el nombre del literal ofrece una referencia de la página a la que pertenece (page), del contenido del mensaje (message) y, por ejemplo, de la acción para la que sirve (key).

Otro aspecto importante relacionado con los ficheros de recursos es que no es necesario (ni recomendable) dividir una sola frase en varios literales únicamente porque algunas partes de dicha frase es variable, bien porque contiene parámetros, o bien para adarpar su estructura a la construcción gramatical de sujeto/verbo/objeto en los diferentes idiomas. Puede y debe dejarse todo en el mismo literal y construir el mensaje apropiado en cada idioma utilizando el método getFormattedMessage de org.sakaiproject.util.ResourceLoader.

Aclararemos este último punto con un ejemplo. Supongamos que el mensaje que queremos mostrar por pantalla un saludo personalizado para cada usuario:

Bienvenido a Sakai, David. Esperamos que disfrutes la experiencia.



El modo erróneo de hacerlo, sería

page.statement.1 = Bienvenido a Sakai,
page.statement.2 = . Esperamos que disfrutes la experiencia



En su lugar, el literal correcto sería:

page.statement.1 = Bienvenido a Sakai, {0}. Esperamos que disfrutes la experiencia



La clase ResourceLoader
Sakai proporciona una clase, org.sakaiproject.util.java.ResourceLoader, que actúa de envoltorio (wrapper) de java.util.ResourceBundle y que selecciona el Locale (idioma) con el que se cargan los ficheros de recursos según la siguiente orden:

  1. Preferencias de idioma del usuario

  2. Sesión del usuario

  3. Configuración del sistema (JVM)


El ResourceLoader se ubica en kernel-util de Sakai. Por ello, todas las herramientas que lo utilicen deben incluir en el lugar adecuado de su pom.xml la siguiente dependencia:

<dependency>
<groupId>org.sakaiproject.kernel</groupId>
<artifactId>sakai-kernel-util</artifactId>
</dependency>


Una vez hecho esto, cualquier literal podrá ser obtenido del fichero de recursos con el siguiente código JAVA:

ResourceLoader rl = new ResourceLoader("ruta_al_fichero_de_recursos");
String foo = rb.getString("page.statement.1");


Errores debidos a que faltan literales
En http://qa1-nl.sakaiproject.org/international/ está registrado cuál es el estado de traducción de ficheros de properties para distintas versiones de Sakai.
Generalmente, cuando nos encontramos un mensaje en inglés (el idioma por defecto), es porque el mensaje en cuestión no tiene literal asociado en el fichero de properties del idioma seleccionado. Cuando esto ocurre, Sakai carga el fichero en inglés y busca la clave en él.
Si éste es el caso, únicamente hay que copiar la línea del mensaje en inglés y traducir el mensaje al idioma deseado, dejando invariable eso sí, la clave.
Corrección de errores en una JSP/JSF
Una vez que está incluida la dependencia respecto de ResourceLoader, el paso siguiente es utilizar esta clase correctamente.
Una alternativa es cargar el fichero de recursos en el faces-config.xml de la herramienta como si se tratase de un bean más:

<managed-bean>
<description>
Dynamic Resource Bundle Loader
</description>
<managed-bean-name>msgs</managed-bean-name>
<managed-bean-class>org.sakaiproject.util.java.ResourceLoader</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<description>Bundle baseName</description>
<property-name>baseName</property-name>
<value>ruta_al_fichero_de_properties</value>
</managed-property>
</managed-bean>


Otra opción es incluir el bean directamente en la página JSP/JSF:

<jsp:useBean id="msgs" class="org.sakaiproject.util.ResourceLoader" scope="session">
<jsp:setProperty name="msgs" property="baseName" value="_org.sakaiproject.tool.foobar.bundle.Messages_"/>
</jsp:useBean>


Ahora supongamos que encontramos un error de internacionalización consistente en que siempre se muestra el mismo mensaje en pantalla, independientemente del idioma activo (es el más común). Seguramente, será porque en la página JSP/JSF que muestra el mensaje, encontraremos un fragmento de código similar a éste:

<h:outputText value="This is an English text">


Lo primero es detectar la JSP/JSF en la que está texto fijo y llevar este texto a los ficheros de properties, por ejemplo, paquete.de.mi.herramienta.Messages_xx:
  • Messages.properties -> sample_text = This is an English text

  • Messages_es.properties -> sample_text = Éste es un texto en inglés


Cuando lo hayamos hecho, solamente habrá que invocar al bean que haga referencia al ResourceLoader.
Corrección de errores en una VM
En las Velocity Templates, el proceso es muy similar alterior, salvo por el hecho de que ResourceLoader, en lugar de cargarlo en un fichero de configuración (faces-config.xml) o directamente en la página (con la directiva jsp:useBean), se carga en el contexto de la plantilla:

ResourceLoader rb = new ResourceLoader("ruta_al_fichero_de_properties");
context.put("tlang", rb );


Para, posteriormente, poder hacer referencia al mismo de las páginas vm:

$tlang.getString("page.sentence.1");


Corrección de errores en el código JAVA

Cuando el literal se encuentra inmerso en el código JAVA, el proceso es algo más complejo.
El primero paso será detectar en qué clase JAVA se encuentra el gazapo. Para ello, podemos utilizar cualquier herramienta que busque en el contenido de un fichero.
Seguidamente, habrá que incluir en el mensaje en los ficheros de recursos correspondientes.
Hecho esto, nos quedan dos cosas:
  1. Cargar dinámicamente el fichero de recursos en función de las preferencias de idioma del usuario. Esto se consigue importando la clase org.sakaiproject.util.ResourceLoader e indicando el fichero de recursos.

  2. Construir el mensaje con la cadena obtenida por el ResourceLoader.


Llegados a este punto, si desplegamos y volvemos a la herramienta veremos que se ha solucionado.
Os propongo que detectéis uno error de este tipo y lo intentéis solucionar. Yo estaré encantado de asistiros en el proceso. Tan sólo tenéis que preguntar. Eso sí, os pediría que, por favor, si encontráis algún error, lo indiquéis en el JIRA, el sistema de bugtracking de Sakai.

No hay comentarios:

Publicar un comentario