views:

193

answers:

3

o will always come from an entiryRef/TEntity (from linq 2 sql)

I'm ok with a c# or vb.net solution (I will convert it into vb.net if you can't)

Public Function desc(Of t)(ByRef o As t, Optional ByVal PropPrefix As String = "desc") As String
    If o Is Nothing Then
        Return ""
    Else
        Dim bind = Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase Or Reflection.BindingFlags.Instance
        Dim _desc = PropPrefix & If(var.Sess.lang = Sess.elang.en, PropPrefix & "en", "fr")
        Dim pinfo As Reflection.PropertyInfo = o.GetType.GetProperty(_desc, bind)

        Return pinfo.GetValue(o, Nothing).ToString
    End If
End Function

little background

I have a database like this

tableUser
-----------------
id
name
countryid

tableCountry
--------
countryid
descEn
descFr

when I'm showing the information about a user, I want to easily show the good language selected in the application

that one example, I have lots more table and lot more lookup table

with linq 2 sql if the field in the database is null, the object will be "nothing"(null) and I'm catching that with the first if then I'm getting the corresponding language field

A: 

Unfortunately, runtime reflection will be required unless you can integrate a desc method into each object, perhaps through some sort of entity base class or code generation and partial classes.

The code generation approach: For each entity class, if it contains one or more descFoo properties, generate a desc property that pulls the correct one. If you cannot modify the CodeDom before it is written, you can also compile the entity classes, reflect/introspect the results, generate partial classes, and recompile with the added code.

Since these are entity classes, though, it appears to me as though a schema change might be preferable. Rather than using multiple descFoo columns, add a table consisting of {entity PK, language, text}.

Jeffrey Hantin
+1  A: 

You cannot do it (much) better with your current design. Instead you should think about a major redesign and obtain the localized values from a dedicated table instead of having columns for all languages.

Your description field could just hold an DescriptionId, and you can look this id up in the Descriptions table by additionally supplying a language identifier.

DescriptionId | Language | Description
--------------------------------------
            1 | fr       | French Foo
            1 | en       | English Foo
            2 | fr       | French Bar
            2 | fr       | English Bar

This easily allows adding other languages later. If you don't expect other languages, you can use the following.

DescriptionId | English     | French
----------------------------------------
            1 | English Foo | French Foo
            2 | English Bar | French Bar

Just to clarify - I don't suggest to introduce one localization table per entity or column, but one for all entities and columns. So all columns containing localizable information would become foreign keys to the same table. In consequence it would become possible to centralize the code obtaining the localized string instead of recreating it for every entity.

I had the same idea as papper1337, but did not present it, because I believe redesigning the database is the way to go. I would have suggest the following.

Create an interface ILocalizedEntity.

public interface ILocalizedEntity
{
    String descEn { get; }
    String descFr { get; }
}

Add this interface to all localized entities. This can be easily done because LINQ to SQL creates partial classes. In C# I would for example create a new file Country.cs and add the following code.

public partial class Country : ILocalizedEntity
{
}

The interface is implemented by the LINQ to SQL generated code.

Now you create a method like the following.

public static GetLocalizedDescription(this ILocalizedEntity entity)
{
    if (entity == null)
    {
        return String.Empty;
    }
    else
    {
        switch (Session.Language)
        {
            case Language.English:
                return entity.descrEn;
                break;

            case Language.French:
                return entity.descrFr;
                break;

            default:
                throw new InvalidOperationException();
        }
    }
}

This is a C# extension method and you can use it as follows.

someEntity.GetLocalizedDescription();
Daniel Brückner
this is what I have right now this issue is inside the application, which field to select
Fredou
The schema revision is the better way to go however if you are married to the dual language in 1 table approach then why not just have an "If (var.Sess.lang = Sess.elang.en) Then Return tableCountry.descen Else Return tableCountry.descfr End If". Apologies if the code is poor there, long time since I ventured into VB.
Lazarus
@Lazarus, my schema got dedicated table for each field that can be french or english so I'm already good with that part, read the end of my question for the actual problem and doing that kind of if hundred of time everywhere in the code is not a good option, don't forget, I need to check if the object is null and if not then I can decide which field I want to take.
Fredou
You don't have this right now - you have the localization tied to different tables instead of a single table for all localizations. In consequence you cannot centralize the code selecting the required language, but you could with a single table.
Daniel Brückner
this could bring another problem, you need one more column to identify what the row mean, is it a unit? a country? a substance? a type of etc? and from that point on you need an enum in the code and one more table linked to it to identify this kind of information. back to the code, you then need more code in every dropdownlist etc to make sure you list the correct information from the global table. I'm not really sure if I want to go that way... unless you know an easy way to fix the issues I just said.
Fredou
I can see no problem at all because the description table is referenced. You have country object. You want the description in English. Just access country.Descriptions.Single(d => d.Language == "en") and you are done. Or did I miss your point?
Daniel Brückner
let say you have a table "user" with field firstname, lastname, titleid. You have a webpage to fill that information. with your method how do I populate the dropdown list for the title field from that global table.
Fredou
A: 

I agree with Daniel Brückner.

That being said, an alternative implementation would be to use interfaces like so:

public interface IEnglish
{
 public string DescriptionEN { get; }
}

public interface IFrench
{
 public string DescriptionFR { get; }
}

public interface IMultilingual : IEnglish, IFrench { }

public void desc<T>(ref T o, string propPrefix) where T : IMultilingual
{
 return Sess.lang = Sess.elang.en ? o.DescriptionEN : o.DescriptionFR
}

Then whatever class contained the english / french properties would implement IMultilingual and live happily ever after.

Chris Shouts
the class come from a linq2sql object so I cannot modify it
Fredou
You can use this approach by using partial classes. I added details to my answer.
Daniel Brückner