A question of Design (handling the same StringProperty in mutiple Languages)

A question of Design (handling the same StringProperty in mutiple Languages)

Old forum URL: forums.lhotka.net/forums/t/1497.aspx


pirithoos posted on Monday, October 16, 2006

Hi all.

 

[Version: VB Csla 2.1]

 

This is basically more a question of class design then Csla. Anyway I would like to ask if someone has figured out a ‘best practice’ using the Csla Framework.

 

In Brief:

 

Certain Properties (all Strings) of a BO must be available in different languages as I cannot foresee how much languages are handled by the users of my application I will not offer the BO an individual property in all languages. So I intend to spend the BO a List (Collection) of child objects containing exactly the same Data, but in the individual languages required. For instance a BO ‘Product’ with a ‘product description’ in three languages (English, German, French). I do look for a good design to provide consumers of this ProductBO access to the descriptions in the individual varying languages.

 

Details:

 

I do have a BO ‘Product’ with the following properties

 

BO: Product (Inherits BusinessBase)

==============================

Public ReadOnly Property ID as Guid

Public Property ProductGroup as Integer

Public Property Description as String

 

Within the application the Product.Description is processed in different languages (typically English, German, French), however I would like to have the option to add more languages in future. Therefore I abstained from adding additional Properties for each Language (e.g. DescriptionInEnglish, DescriptionInGerman…) in the Product BO, but I did add a ‘Languages’ property of type LanguageList inherited from BusinessListBase.

 

This ChildBOs of LanguageList are ProductLanguageInfos with the following Properties

 

BO: ProductLanguageInfo (Inherits BusinessBase)

====================================

Public ReadOnly Property ProductID  as Guid  (Foreign Key from BO ‘Product’)

Public Property LanguageID as Integer

Public Property Description as String

 

The Product BO gets an additional Property…

 

BO : Product (Inherits BusinessBase)

==============================

Public ReadOnly Property ID as Guid

Public Property ProductGroup as Integer

Public Property Description as String

Public Property Languages as LanguageList

 

The UI currently processes 3 Languages English, German, and French.

 

The question is how to access the Description in the individual language, considering that the user should edit the Description in all languages together in one form/user control

 

The GUI shows 3 Textboxes:

Description (EN)

Description (DE)

Description (FR)

 

My idea is to provide an overloaded parameterized Description property in the Product BO…

 

BO : Product (Inherits BusinessBase)

==============================

Public ReadOnly Property ID as Guid

Public Property ProductGroup as Integer

Public Property Description as String

Public Property Description (LanguageID as Integer) as String

Public Property Languages as LanguageList

 

The assessors of this overloaded Description property interact with the ProductLanguageInfo objects of the LaguagesList to get and set the Description.

 

Each Textbox is bound to a corresponding Description.

 

 

My Questions:

 

Is that a ‘legal’ and/or recommended design?

Does somebody have figured out a better design?

As the ProductLanguageList / ProductLanguageInfo are only handled together with the Product BO, does it make sense to nest those classes in the Product BO?

 

As my application has this ‘language requirements’ for certain properties in quite a lot of different BOs I wonder if there is a easier design pattern available (to avoid to code 3 classes for each BO (BO, LanguageList, LanguageInfo).

SonOfPirate replied on Monday, October 16, 2006

IMO, you're on the right track.  However, a couple of key things to note...

Consider placing your Product Descriptions in a separate table in your DB with a foreign key reference to the Product and a column for the language (which you may already be doing).  The language identifier should be the standard used by .NET (e.g. en, en-US, fr and so on).

Then, when you instantiate your business object, Product, have your data access code pass the current language identifier to the stored procedure.  Your stored procedure can perform a join on the Products and ProductDescriptions table and use the language identifier to filter the rows in ProductDescriptions to only the one corresponding to the current language.

For your UI, you can still accomodate this by having a drop-down list with supported languages and only a single textbox for the Description populated with the appropriate text based on the language selected in the drop-down box.  This would allow you to unify/tie-in any other fields that you may need multi-lingual support for such as the product's name or pricing information (which you may want to localize).

Using this approach, you only have one business object, Product, with the necessary logic to handle multiple languages.

Another approach you may want to consider if the description is the only multi-lingual field you are dealing with is to have your Product object define an internal (private) dictionary of descriptions keyed on the language.  Then, you can load them all into the Product and in your description property accessor, select the item from the collection that matches the language.  So, you would have code such as:

public System.String Description
{
    get
    {
        return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.Name];
    }
}

This approach works well if you only have a small number of multi-lingual fields because you don't have to incur a round-trip to the DB and instantiate a new BO each time you change languages.  On the other hand, if you have a lot of multi-lingual fields and are not frequently switching languages on-the-fly, there is quite a bit more overhead loading all of the lists with this approach versus the first where you simply return the appropriate string right from the DB.

You can still maintain a list & lookup table of supported languages to help drive your UI.  Then, when a new Product is created, you either insert a (blank?) record into the ProductDescriptions table for each supported language or build-in some logic to traverse the culture chain to a default in the event the string for the selected language is not present.  For instance:

public System.String Description
{
    get
    {
        if (_descriptions.Contains[System.Globalization.CultureInfo.CurrentUICulture.Name)
            return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.Name];
        else if (_descriptions.Contains[System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)
            return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName];
        else
            return _descriptions["en"];
    }
}

(Just an example).

 

Hope that helps.

 

ajj3085 replied on Monday, October 16, 2006

I would recommend having another class that handles getting the translated strings.  Its not the main purpose of your BOs.  Your BOs responsibility is to enforce business rules.  So you should have another object that the BOs can use to get the proper translation.

HTH
Andy

SonOfPirate replied on Monday, October 16, 2006

Keep in mind, at least imo, the language aspect is not a behavior - we are not performing translations, only retrieving the appropriate string from the DB.  So creating a separate BO is not consistent with a behavior-first approach.  That is why I like (& use) the first approach where the BO is responsible for tracking the correct language.  Afterall, making sure data is displayed in the correct language is typically a "business rule" albeit not in the same sense as "'x' must be greater than 5".  From a behavioral perspective, the current language doesn't change anything other than which string is retrieved.

ajj3085 replied on Monday, October 16, 2006

My comments were aimed to the OP.

pirithoos replied on Monday, October 16, 2006

Thanks for your prompt response.

Mayby I did not express clearly what I want / need.

The ProductBO, when initiated, must immediately allow to access all different descriptions multiligual. So to say I do not what to pre-select in what language the ProductBO / or certain Properties is/are poplulated, but  I want to make all language descriptions avaiable at the same time. Still I think this is only possible when the ProductBO holds a list of childobjects  whith those DescriptionsStrings and a LanguageID.

Consindering this I would like to know what's the best practice accessing those DescriptionStrings from one only ProductBO.

I thought of a
Public Property Description (LanguageID) as String, but
this makes Databinding an adventure-tour.

It would be ideal if the ProductBO could expose this certain  'ChildProperties' (the Description in the different languages) for Databinding directly without having to  access those via the LanguageList (the nested collection of ChildObjects).

I dream of a solution where the ProductBO has not only a Desription Property but also Properties for all current languages. (but as said the number of languages may increase in future, and I want to avoid adding a new properties for each future language into the ProductBO).


ajj3085 replied on Monday, October 16, 2006

What exactly is your Product class supposed to do?  Display information?  Or allow a user to edit all the desciptions for all languages?  Those are two distinct tasks which require different objects.  I think it would help if we knew more about your use case that you are trying to implement.

pirithoos replied on Monday, October 16, 2006

It's intended use is to allow the user to create new and or edit existing products.
Some of its data (e.g. DescriptionString and maybe one or two more strings in future)
must be available in different languages. It must allow the user to edit all descriptions
for all languages.

The application uses a many BO's which must allow  to edit multi-ligual Strings
e.g. PaymentTermsBO, ProductGroupsBO etc.

Normally I would design this ProductBO with the followings Properties
Description_english
Descritpion_german
Description_france

However the number of languages is not clear as yet. So I cannot spend properties
for each language in advance. At this point it was clear (IMO) to spend a List of childobjects
to the ProductBO which contains the Description multi-ligual.

Later other objects like OrderPrinterBO uses the ProductBO and depending on
the LanguageID provide to the OrderPrinterBO it should print out the Description
in the corresponding language.

The OrderPrinterBO must not load the ProductBO each time the language changes
once the OrderPrinterBO is povided with a ProductBO it must be capable to
print out the order with the the Product.Description in the right language currently
selected in the OrderPrinterBO.

ajj3085 replied on Monday, October 16, 2006

Ok.

The editable product likely needs a collection of descriptions.  Each ProductDescription has a certain language associated with it as well as the text.  That's how you should probably tackle this problem.  Use a gird for editing. 

For display, the ProductInfo (or whatever you call it) should be able to figure out the proper translation.  Behind the scenes though, you'll likely want something that can get arbitrary translated strings.

HTH
andy

pirithoos replied on Monday, October 16, 2006

Dear Andy,

thanks for your advice.

Probably I just think to complicated. In fact leting the Descriptions be edited by a grid is the most easy way. If I want to get the Description in different languages from the ProductBO without accessing the collection I could think of a function ProductBO.GetDescription(LanguageID as integer) as String which returns the right Description from the nested collection of descriptions for all languages.

If the GUI still requires to have a single Textbox for each LanguageDescription instead of a Grid , I could think of wrapping the ProductBO in another Object which provides a property for each language.If in future other languages are added only this wrapper plus the GUI must be re-designed, but the library containing the ProductBO can remain unchanged.

Best Regards
Frank


SonOfPirate replied on Monday, October 16, 2006

I agree with Andy.  You've got two use cases that call for two different approaches.  Since you want to be able to edit all of the languages on the same UI, you will want a BO with a child collection containing the descriptions in all of the languages.  But, your OrderPrinterBO should only have a Description property and use the language to select the right string from the database when retrieving the information.

I would also agree that a grid is a better UI for your situation - especially since you want to be able to add more languages in the future.  Databinding the grid to the collection will allow you to add them easily without having to make changes to your UI as you would if you tried to have a separate textbox for each (unless you created them dynamically in a loop during rendering - ouch!).

Hope that helps.

 

jupp replied on Monday, October 16, 2006

I have had a similary dream.  I hope the CustomTypeProvider is also useful for your problem.

In short.

If you use the CustomTypeProvider you can craft at run time your own pseudo -properties that grap each of your object fields or fields embedded in lists, thus allowing those information to be displayed and edited with "normal " Databinding.

Please visit the following link for details :

http://msdn.microsoft.com/msdnmag/issues/05/05/NETMatters/ 

pirithoos replied on Wednesday, October 18, 2006

Dear Jupp,


Sounds promising!


I will go through this article a.s.a.p. Anyway if you have already used this approach to follow up a similarly idea like mine (exposing properties of the items in a child collection to the root BO which contains this collection) I would very much appreciate if you could share the code with me.


Thanks


Frank


jupp replied on Thursday, October 19, 2006

Dear Frank,

when I will be back im my home office at the weekend I will look for an example and share it with you.

Josef Eißing

Germany

 

Copyright (c) Marimer LLC