views:

321

answers:

3

How would you build your domain objects and create its respective NHibernate mapping files for a multi language application. The UI part is stored in resource files but user data needs to go into the database.

I want to do the following:

Product p = DALProduct.getByID(2)
p.name //results in the language of the current UICulture

I have found the following article which is really close: http://ayende.com/Blog/archive/2006/12/26/LocalizingNHibernateContextualParameters.aspx As I am new to NHibernate I am not sure if that will perfectly work for enterprise solutions.

Do you have other suggestions? How do you solve such scenario?

It should be flexible to:

  • Inserting, Updating and selecting
  • Collections
+2  A: 

Ayendes post is a great start how it should be designed.

It will perfectly work for enterprise solutions. The names in a separate table is like any other list of values. The special thing is, that it is filtered in the mapping.

Edit - Options:

Use another entity to edit the data

There is Product entity that has all the names as list. LocalizedProduct has only the current languages name.

Get the filtered entity

  • by either mapping it as described in the blog, with the filter.
  • by selecting it with a result transformer (Transformers.AliasToBean) or with 'select new LocalizedProduct(id, name, prize ...)'. LocalizedProduct would not be mapped in this case. Should be second-level-cache-friendly.

If you have many references to Product, it is probably not so nice to have two classes, because you don't know which class the reference should have.

Use the same entity for editing and displaying

class Product
{
  string LocalizedName 
  { 
    get { return AllProductNames[Thread.CurrentThread.CurrentCulture.LCID]; }
  }

  IDictionary<int, string> AllProductNames { get; private set; }
}

There are properties for a localized product name (get) and all product names.

  • don't filter them at all :-) There is a bit of network overhead. if you have only 3 to 5 languages, it is not so bad. if you have 20 or more, it's probably better to filter the names.
  • use a (optional) filter as described in the blog, but on the product names
  • use a (optional) ResultTransformer (CriteriaUtil.AliasToEntityMap) to filter the names.

To be honest, I would choose a option that dies not need filters in the mapping files. Filters belong to queries where they are more maintainable.

Stefan Steinegger
I have some concerns about this solution:* I need to create all INSERT and UPDATE statements manually (cause the translation table needs to be updated as well)* The application will run with ONE culture at a time but how do I update the productnames (e.g. in the masterdata)
Michal
do you know what the answer is for this comment in the post is "Nice... what happens with 2nd level caching? How do you distinguish between the same object in different languages? "
Surya
We don't use second level cache in our project, I don't have experience in that.
Stefan Steinegger
@Michal: You probably need another entity for editing the products. The question is: how common is it in your application to update the masterdata (or just to have all languages at once) an how often you need only the current language? If you need all languages more often then only one, you could map them normally and cut the unneeded languages in the data access layer.
Stefan Steinegger
@Michal: I'm talking bullsh!t. You don't need another entity for the filter. You just should not enable the filter when getting data to be changed.
Stefan Steinegger
@Surya: I do not understand your comment at all. Sorry for my bad english. @Stefan: My App definitely uses one language at one time most of the times. The editing is just in the masterdata. I am confused with your last answer.. Could you tell me more what you mean. It confuses me because if I have Product.Name then Name cant have more values, cant it?
Michal
@Michal: Sorry, I wasn't clear at all. I need more space to explain what I mean and added it to the answer. I hope this helps.
Stefan Steinegger
@Stefan: I am trying various solutions and the one you suggest with the IDictionary looks good to me. Is it possible to use the Criteria API so that only the current translation will be loaded? or using a LIKE query on productnames?
Michal
@Michal: Try this CriteriaUtil.AliasToEntityMap I mentioned in the answer. http://knol.google.com/k/fabio-maulo/nhibernate-chapter-13/1nr4enxv3dpeq/16#13(2E)4(2E)(C2)(A0)Associations Read the paragraph starting with "Note that the kittens collections..."
Stefan Steinegger
@stefan: thanks for the link. Unfortunately it does not tell me how to access the dictionary key or item. It only shows how to create a criteria for Entity properties.
Michal
@Michal: You should find this in the documentation. Not sure what you are looking for, but it seems to me as if it were not related to the localization question here. You could as a new question if you can't figure it out.
Stefan Steinegger
Michal
@Michal: In HQL there is an index() function to access the list index. See http://knol.google.com/k/fabio-maulo/nhibernate-chapter-12/1nr4enxv3dpeq/15# I don't know if it exists for criteria. If you really can't find it out, make the dictionary a list.
Stefan Steinegger
Thanks stefan. I hope that helps me more.
Michal
Good luck :-) Here is the link to the nhibernate group: http://groups.google.com.ar/group/nhusers Fabio Maulo himself (he is the main developer) is answering many questions there.
Stefan Steinegger
A: 

Here is Gavin King's Post I which seems to offer a different solution.

Surya
A: 

The approach mentioned as an answer has been explained in detail here

Michal