i18n y L10n de Alfresco

La documentación acerca de la i18n no es suficientemente clara al respecto y es frecuente encontrarse con que las cosas no funcionan a pesar de haber seguido las instrucciones. El presente artículo pretende explicar por qué es tan confuso el tema y como se debe hacer para salir airoso de la situacion.

1.- i18n de Alfresco en versiones anteriores a 3.0

alfresco_i18

Alfresco proporciona dos métodos para obtener mensajes localizados :

org.alfresco.web.app.Application#getMessage(FacesContext context, String msg)
org.alfresco.i18n.I18NUtil#getMessage(String messageKey, Locale locale)

Una diferencia fundamental entre ambos es que Application.getMessage delega en org.alfresco.web.app.ResoruceBundleWrapper, mientras que I18NUtil.getMessage delega directamente en java.util.ResourceBundle. ResoruceBundleWrapper no es más que una extensión de ResourceBundle para que, si no se encuentra una deternminada clave en el properties se muestre al usuario como $$key.value$$ en el web-client.

Otra diferencia es que Application.getMessage emplea exclusivamente como properties el webclient.properties, mientras que I18NUtil se nutre de todos los properties indicados mediante beans de spring del estilo :

1   <bean id="actionResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
2       <property name="resourceBundles">
3          <list>

4             <value>alfresco.messages.action-config</value>
5          </list>
6       </property>
7    </bean>

1.2.- Problemas derivados de la existencia de dos métodos de recuperación de mensajes

Una consecuencia desagradable de lo expuesto es que es difícil saber en qué properties se deben poner las claves ya que dependiendo del método que la clase de Alfresco en cuestión emplee para recuperar el mensaje ( Application#getMessage o I18NUtil#getMessage ) se debe poner en el webclient.properties o bien en otro properties inyectable mediante Spring.

Y otra consecuencia es que todos los mensajes que se recuperen a través del Application#getMessage deben ir en el webclient.properties, lo que puede ser molesto si el proyecto se compone de módulos independientes y se quisiera que cada módulo gestionase su propia i18n.

2.- i18n de Alfresco en versiones posteriores a 3.0

2.1.- Alfresco > 3.0 no está limitado a un único webclient.properties

Los problemas derivados de que todos los mensajes tengan que ir en un único webclient.properties se ha resuelto a partir de la versión 3.0 ya que se permite indicar varios properties para ser usados con Application#getMessage y que se definen a través de org.alfresco.i18n.ResourceBundleBootstrapComponent; por ejemplo :

1    <bean id="resourceBundlesWebApp" class="org.alfresco.web.app.ResourceBundleBootstrap"> 
2       <property name="resourceBundles">
3          <list>
4              <value>alfresco.extension.custom-webclient</value>

5          </list>
6       </property>       
7    </bean>
8 

2.2.- ¿Cómo saber qué clase (Application ó I18NUtil) emplea Alfresco para la recuperación de mensajes?

Como se ha explicado con anterioridad el hecho de que existan dos métodos para recuperar los mensajes lleva a confusión ya que obliga a saber qué clase (Application ó I18NUtil) emplea Alfresco para la recuperación de mensajes. Y por tanto esto conlleva a tener dudas de en qué archivo poner el properties y si debe ser inyectado mediante org.alfresco.web.app.ResourceBundleBootstrap ó org.alfresco.i18n.ResourceBundleBootstrapComponent. La regla en este caso es :

Los mensajes usados por el webclient1 se recuperan mediante Application#getMessage(FacesContext context, String msg) y por tanto deben ser configurados mediante org.alfresco.web.app.ResourceBundleBootstrap. Por otro lado, los mensajes usados por el repositorio2 y sus servicios emplean I18NUtil#getMessage(String messageKey, Locale locale) y por tanto deben ser configurados mediante org.alfresco.i18n.ResourceBundleBootstrapComponent.

La idea es que cuando sea necesario que se muestre un mensaje alternativo, si no se encuentra el valor de clave correspondiente en el .properties, se delegue en el ResoruceBundleWrapper para mostrar algo del estilo $$actio.create_space$$. Por esta razón las clases que no requieran de tal mensaje alternativo (de tal wrapper) emplean el I18NUtil y el resto emplean Application#getMessage

3.- Recursos


[1] Las clases del webclient se encuentran en el alfresco-web-client-XXX.jar (código de alfresco-web-client-XXX.jar) y se corresponden con paquetes del estilo org.alfresco.web
[2] Las clases que implementan el repositorio de Alfresco se encuentran en alfresco-repository-XXX.jar (código de alfresco-repository-XXX.jar).