This page is a summary of the two main different approaches found all around the web on Property Localization using NHibernate, based on a post in spanish on my own blog.
There are roughly two main paths
Method 1: Using a custom IUserType
This method is exposed thoroughly in a Fabio Maulo`s post called Localized Property with NHibernate, in which he discusses the use of the LocalizablePropertyType found in uNhAddIns.
It is based on implementing IUserType, providing methods to serialize and deserialize the property. This way, we make use of NHibernate extensibility to provide a seamless mechanism to store and retrieve the property in a field of the database.
In the example, the property is a Dictionary (internally) but we choose to expose it as a String developing the getter (in Fabio's post) to make it easier for the user.
Alternatively, you can create your our own class to contain all the translations and ease the addition and removal of them as well as properties to have quick access to the current localization string based on Thread.CurrentThread.CurrentCulture. In some of the proposals in the second method discussed there are examples of such classes that can be easily adapted to this method. This, as well, can be exposed as a string through the proper getter.
- Simple and easy
- Data stored in the same table as the entity
- Fairly transparent, specially if a custom object is used to simplify the access to the translations
- The value of the field is not understandable by the DBMS (no indexing, sorting or whatever)
Method 2: Using several tables
This second method has many variants, but all of them use one or more tables to store the translations, making them visible to the DBMS.
Proposal 2.1: Localizing NHibernate: Contextual Parameters by Ayende
This is an interesting approach, quite simple in explanation but uses a couple of advanced NHibernate features such as formulas and filters. The advantage is that the property of the entity is a simple string and the selection of the language to load is made through the formula based on a session parameter.
Proposal 2.2: Create a multi languaged domain model with NHibernate and C# by Michal @ WebDevBros
This one uses a nice crafted class to store and retrieve localization information and a couple of tables, one containing the dictionaries that create the relationship between the entity and the translations, that are accessed via a <map> in the mapping. (NOTE: I have not tested it, and he says that you can map several properties in the same entity with this method but not with the provided mapping auto insurance quotes)
The class called PhraseDictionary can be easily used through a IUserType with the first method.
Proposal 2.3: Mapping translations in NHibernate by Siim Viikman
The method proposed here is very similar to the former one, in this case using three tables, but that's up to you. It explains how to do the mapping using an Id different from the PK (which can't be done at this moment using Fluent NHibernate, just in case you want to).
The PhraseDictionary has been replaced by the Translation class, but essentially are the same, holders of the translation dictionary. Can be adapted as well to use it with method 1.
Proposal 2.4: Localizable entities with Nhibernate part 1 (part 2 and part 3) by Alkampfer
A very thorough example. It maps only one property per entity, simplifying the mappings and the table structure and can be more than enough for many solutions. It discusses a lot about a Registry object to hold the current locale, but you can change that to rely in Thread.CurrentThread.CurrentCulture and simplify a little bit.
- More complex mappings
- More complex data structure
Localization is painful in most scenarios and some guidelines can be followed:
- If you don't really need that a property is localizable, don't make it localizable. Ok, in most cases is not your decision. If you have choice, think it twice or thrice before making a property localizable.
- If you don't need to use DBMS capabilities on the field (such as sorting), go for the first method, it's easy to to the IUserType and the mappings and as well as the serialization is well tested won't give much trouble
- If you need DBMS capabilities, in most cases only some properties will need this, so instead of embracing blindly the second method, think if a mixed solution is not simpler and better for you. (Was in my case)
As a side note, if you need to index the property, think of using NHibernate.Search and Lucene.NET using method 1 and a custom Bridge implementation to store the localizations in the index.
Finally, thanks to all that participated in NHUsers discussion on the topic.