NHibernate y localización

Edit: Cross-posted in NHForge: Localization techniques

Hace unos días estuve dándole vueltas en el grupo de NHUsers de nuevo al tema de la localización de propiedades usando NHibernate para ver si se me ocurría una forma más potente de hacerlas, dado que el mecanismo que usamos actualmente no permite usar ordenación a través de SQL, por lo que para ordenar los datos por la columna localizable, hay que procesar todas las entidades, lo que es un problema cuando hay muchas.

En resumen, hay 2 modos fundamentales y ampliamente documentados de hacer campos localizables con NHibernate.

Método 1: Serializando la propiedad a través de un IUserType

Esta aproximación es por ejemplo la que describe Fabio Maulo en su blog, usando LocalizablePropertyType del proyecto uNHaddins. Básicamente se trata de almacenar todas las cadenas de traducción en un solo campo y el IUserType se encarga de obtener un diccionario u otra estructura más compleja a partir del valor del campo y de almacenar esa misma estructura en forma de string.

En mi caso, no usé exactamente este tipo porque quería almacenar también un identificador de idioma por defecto y algunas cosas más, pero no es algo complicado de hacer.

Internamente normalmente almacenaremos algún tipo de diccionario con el idioma como clave y el valor la traducción pero es sencillo exponer la propiedad como un string haciendo que el get obtenga el valor correcto para el idioma actual (si es que esta funcionalidad no la implementamos en nuestro propio diccionario).

Ventajas:

  • Sencillo de realizar
  • No se necesitan tablas adicionales
  • Transparente

Inconvenientes:

  • Al ser un campo calculado, no permite usar ORDER BY
  • La propiedad no puede ser un string

Método 2: Almacenar la propiedad en otras tablas

Hay varios ejemplos de cómo realizar esto, básicamente se trata de mapear la propiedad a una estructura de tipo diccionario donde se almacenan las distintas traducciones del valor de la propiedad indexadas por código de la localización (bien en texto o usando el LCID). Esto se puede hacer más o menos complejo, pero fundamentalmente el principio es el mismo.

Todo esto se suele apoyar en una clase que se encarga de almacenar el diccionario de localizaciones y provee métodos para añadir, eliminar y obtener de forma sencilla dichas cadenas, haciendo uso del Thread.CurrentThread.CurrentCulture para obtener el idioma seleccionado actualmente.

Como no, Ayende ofrece una solución al problema basada en la simplicidad en Localizing NHibernate: Contextual Parameters, usando una fórmula en el mapping para obtener la propiedad Name de la entidad y un filtro en la sesión para que funcione la fórmula. Igual no soluciona los problemas en todos los escenarios, pero es una lectura interesante.

En esta otra propuesta de WebDevBros, Michal nos propone Create a multi languaged domain model with NHibernate and C#. Muy interesante. Por un lado, se crean dos tablas, una con los diferentes diccionarios que corresponderán a las distintas propiedades a localizar, y otra tabla con los valores de cada diccionario, que se mapea con un <map> de forma sencilla. También una clase que sirve para contener las traducciones y facilitar el acceso a las mismas.

En esta misma linea, Siim Viikman muestra en Mapping translations in NHibernate una aproximación muy similar a la anterior que conviene también leer porque aporta otra variante a la solución. Esta quizás no es tan cómoda de usar como la anterior pero puede tener sus ventajas.

En Localizable entities with Nhibernate (Part1, Part2 y Part3), Alkampfer propone una solución algo compleja, que se basa en un objeto que mantiene el idioma actual seleccionado durante el ciclo de vida de la aplicación y en una clase de apoyo que gestiona las traducciones. Es de lectura muy interesante aunque en mi opinión se puede hacer lo mismo evitando el objeto Registry que crea y usando Thread.CurrentThread.CurrentCulture.

Ventajas:

  • Uso de todas las posibilidades de la base de datos y SQL

Inconvenientes:

  • Mapeo más complejo (dificil en algunos casos o imposible usando Fluent NHibernate)
  • Más tablas, más datos

Conclusión

En resumen, las propiedades traducibles o localizables son un problema difícil de resolver que generan una capa de complejidad en el acceso a datos y en la gestión de dichas traducciones. Aun así me puedo aventurar a dar ciertos consejos

  • Antes de localizar una propiedad, pensar si realmente es necesario ¿realmente es necesario?
  • Si no vamos a necesitar ordenación u indexación por SQL, el método basado en IUserType es más que suficiente y ahorramos problemas. NOTA: Si se quiere indexación, siempre se puede usar Lucene.NET y NHibernate.Search con un Bridge personalizado que almacene todos los idiomas.
  • Si necesitamos ordenación, seguramente será solo en algunos campos concretos, así que podemos solamente usar el segundo método en los campos más críticos para simplificar nuestra vida.

En mi caso al final hemos optado por una solución mixta, usando el primer método en la mayoría de los casos y cuando se ha necesitado tener listados grandes ordenados por la propiedad localizable, hemos optado por una solución parecida a la de WebDevBros pero con una tabla por cada campo localizable porque no son muchos y el mapeo es más simple.

Lo mejor de 2009

Como cada año, este post hace un repaso de lo más leído de 2009. La verdad que ha sido un año de altibajos, con pocas entradas pero en mi opinión de mejor calidad y más específicos y útiles (para el que le sirvan) que los publicados anteriormente. Además he introducido algunos posts más en inglés con temas que creo que no estaban explicados en ningún sitio.

El más leido de 2009, NHibernate Burrow and NHibernate Search, explicando la integración de estas dos estupendas librerías. Burrow permite manejar sencillamente las sesiones de NHibernate en entornos web y Search permite integrar con Lucene. También escribí la versión en español del artículo.

Más de NHibernate: NHibernate Validator custom messages, o cómo conseguí personalizar los mensajes del validator mediante extensibilidad de un modo muy sencillo y potente. Supongo que con el tiempo Validator soportará esto nativamente.

Después otro más en inglés, ASP.NET MVC and ReturnUrl, quejándome de que muchos ejemplos de autenticación con MVC no respetan el parámetro ReturnUrl correctamente, lo que es un error a la hora de hacer un mecanismo útil de autenticación.

Y por último, los Principios básicos de OOP: SOLID que siempre deberíamos tener todos presentes a la hora de diseñar una aplicación y mantener la coherencia, la facilidad de uso y la mantenibilidad.

Y dado que este año no he escrito mucho, lo voy a dejar aquí. En 2010, más ASP.NET MVC, más NHibernate y más de todo.

¡Feliz 2010!

Mis feeds sobre NHibernate y ASP.NET MVC

Hace tiempo que tengo un par de feeds en Google Reader que me gustaría compartir con todo el mundo que le puedan interesar.

El primero es sobre NHibernate y el segundo sobre ASP.NET MVC, dos temas que sigo de cerca porque los uso diariamente en mi trabajo, recogiendo noticias que van apareciendo en los blogs a los que estoy suscrito. Por supuesto hay muchas en inglés porque la mayoría de la información se publica en este idioma aunque intento poner cosas en español también:

Espero que resulten de utilidad.