Obtención de los literales de i18n de base de datos en JSF2.

0
8409

Obtención de los literales de i18n de base de datos en JSF2.

0. Índice de contenidos.


1. Introducción

Hace poco publicábamos un tutorial sobre cómo obtener los literales de internacionalización (i18n) de una base de datos con el soporte de iBatis en una aplicación Spring MVC y, en este tutorial, siguiendo el mismo hilo argumental vamos a exponer cómo realizarlo en JSF2, configurando el sistema estándar y, haciendo uso del soporte de inyección de Spring, con Spring Data.

Este tutorial surge también como complemento al publicado sobre «selección manual de idioma en la interfaz de usuario con JSF2«, y respondiendo a una petición realizada por los asistentes a uno de nuestros últimos cursos de formación sobre JSF2; si, como es lógico, en el transcurso de los mismos no podemos abarcar aquello que queda fuera de temario, hacemos el esfuerzo de cubrirlo después a través de adictosaltrabajo.com.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.4 GHz Intel Core i7, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.7.4
  • JSF 2.1.12


3. Configuración.

La configuración a nivel de JSF, se centra en el xml de configuración faces-config.xml, asignando los idiomas soportados y el de por defecto.

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
    version="2.0">
	<application>
		<locale-config>
			<default-locale>es</default-locale>
			<supported-locale>es</supported-locale>
			<supported-locale>en</supported-locale>
		</locale-config>
		<resource-bundle>
			<base-name>com.autentia.training.web.util.DatabaseDrivenResourceBundle</base-name>
			<var>msg</var>
		</resource-bundle>
		...
	</application>
</faces-config>

Además, en la etiqueta <resource-bundle> en vez de asignar un prefijo para nuestros ficheros de recursos «messages.properties», declaramos una referencia a una clase que resuelva la internacionalización de los literales. Esa clase es el punto crítico, que veremos en el siguiente punto.


4. Implementación del ResourceBundle.

Declarada la clase solo nos resta implementarla, y podría tener un código similar al siguiente, extendiendo de ResourceBundle:

package com.autentia.training.web.util;

import java.util.Collections;
import java.util.Enumeration;
import java.util.ResourceBundle;

import javax.faces.context.FacesContext;

import com.autentia.training.core.persistence.jpa.entities.MessageResource;
import com.autentia.training.core.persistence.jpa.repository.MessageResourceRepository;
import com.autentia.training.core.utils.SpringUtils;

public class DatabaseDrivenResourceBundle extends ResourceBundle {

	private static final String PREFIX_NOT_FOUND = "???";
	
	private MessageResourceRepository messageResourceRepository;

	public DatabaseDrivenResourceBundle() {
		messageResourceRepository = SpringUtils.getBean(MessageResourceRepository.class);
	}

	@Override
	protected Object handleGetObject(String key) {
		final MessageResource messageResource = messageResourceRepository
				.findOneByCodeAndLocale(key, FacesContext.getCurrentInstance()
						.getViewRoot().getLocale().getLanguage());
		if (messageResource != null) {
			return messageResource.getText();
		}
		return new StringBuilder(PREFIX_NOT_FOUND).append(key).append(PREFIX_NOT_FOUND).toString();
	}

	@Override
	public Enumeration<String> getKeys() {
		return Collections.enumeration(messageResourceRepository
				.findByLocale(FacesContext.getCurrentInstance().getViewRoot()
						.getLocale().getLanguage()));
	}

}

Dos cuestiones claves:

  • Línea 20: la obtención del repositorio de MessageResourceRepository para realizar las consultas de los literales, para lo cuál se hace uso de una clase de utilidades que permite obtener un bean del contexto de Spring invocando a un método estático, y
  • Líneas 25 a 27: invocación a un método del repositorio pasando como parámetros, además del código del literal, el idioma obtenido del nodo raíz del contexto JSF.

La clase de utilidades que comentábamos podría tener un código como el siguiente:

package com.autentia.training.core.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils {
	private static ApplicationContext applicationContext;
	
	@Autowired
	public SpringUtils(ApplicationContext applicationContext) {
		SpringUtils.applicationContext = applicationContext;
	}
	
	public static <T> T getBean(String beanName, Class<T> beanClass) {
		return (T)applicationContext.getBean(beanName, beanClass);
	}
	
	public static <T> T getBean(Class<T> beanClass) {
		return (T)applicationContext.getBean(beanClass);
	}
}

Para construir el repositorio simplemente creamos la interfaz, siguiendo los pasos ya descritos en el tutorial sobre Spring Data no necesitamos nada más:

package com.autentia.training.core.persistence.jpa.repository;

import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import com.autentia.training.core.persistence.jpa.entities.MessageResource;


public interface MessageResourceRepository extends CrudRepository<MessageResource, Long>{

	@Query("from MessageResource m where m.code = :code and m.locale = :locale")
	MessageResource findOneByCodeAndLocale(@Param("code") String code, @Param("locale") String locale);
	
	@Query("select m.code from MessageResource m where m.locale = :locale")
	List<String> findByLocale(@Param("locale") String locale);
	
}

La entidad de persistencia para recuperar los literales podría tener las siguientes propiedades:

@Entity
public class MessageResource {

	@Id
	@GeneratedValue
	private Long id;
	
	private String code;

	private String locale;
	
	private String text;

	// setters & getters
	
}

Esas propiedades se traducirán en las siguientes columnas en la tabla correspondiente de base de datos:

Para cubrir el resto de aspectos de nuestra aplicación es la configuración habitual tanto de JSF como de Spring para hacerlos convivir.


5. Conclusiones.

Con el soporte de Spring Data, la parte de persistencia queda bastante limpia de código y solo nos quedaría dar un toque de calidad añadiendo a la recuperación de base de datos una capa de caché que evite repetir las consultas puesto que serán, efectivamente, muy repetitivas.

Un saludo.

Jose

jmsanchez@autentia.com

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad