Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / Windows Forms

Adding Multilanguage Support to Your Objects

Rate me:
Please Sign up or sign in to vote.
4.71/5 (27 votes)
21 Dec 2011CPOL2 min read 38.1K   525   72   14
Using localized data fetched from a data source is a problem. Here is a good solution.

Introduction

In today's world, localization has become a must requirement in applications that we develop. We can easily achieve this goal by using resource files for static resources such as exception messages, label texts, etc. However, using localized data fetched from a data source will be a problem. Since the data is dynamic, this problem cannot be solved with resource files.

Using the code

Most developers solve this problem by using a design like below.

1000002697_1.jpg

If we look at the design, we can see that we are creating a new object for each property that contains multi-language data. All these objects cause the developer to write more code, and also will make the application code less manageable. If the developer is using an ORM tool, s/he will end up creating lots of mapping.

Now let's think from another perspective and try to implement a design like below.

1000002697_2.jpg

As you can see, this design is much simpler to understand and has high reusability. You can use MultilanguageProperty to store multi-language data as a property of an object.

Now let's see how we can write this code from design.

First of all, let's code the Language class that we will use in MultilanguageProperty.

C#
public class Language
{
    public enum ApplicationLanguage
    {
        Turkish,
        English,
    }
    public static ApplicationLanguage GetDefaultLanguage()
    {
        return ApplicationLanguage.Turkish;
    }
}

As you can see, we define the languages we want to use in an enum. And we implement a method that is responsible for returning the default language. This method can return the default language per session or per user based on our implementation.

Let's start coding the MultilanguageProperty object.

C#
public class MultilanguageProperty<T>:IEnumerable<KeyValuePair<Language.ApplicationLanguage,T>>
{
    public IDictionary<Language.ApplicationLanguage, T> Values { get; set; }
    public MultilanguageProperty()
    {
        Values = new Dictionary<Language.ApplicationLanguage, T>();
    }

First of all, to use our object with any type, we are making the object generic. And to allow users iterate through the child items in this object, we implement the IEnumerable interface as above.

Declaring a property whose type is IDictionary is the main idea of this implementation. This Dictionary will store the data based on the language value. As you can see, IDictinary is a generic type of ApplicationLanguage enum as key and T generic type as value. And in the constructor of our code, we create an instance of this Dictionary.

Now let's implement some code to access the data stored in the Values property.

C#
        public T GetValue(Language.ApplicationLanguage language)
        {
            return (T)Values[language];
        }
        public T GetValue()
        {
            Language.ApplicationLanguage applicationLanguage = Language.GetDefaultLanguage();
            return (T)Values[applicationLanguage];
        }
        public T this[Language.ApplicationLanguage l]
        {
            get
            {
                return Values[l];
            }
            set
            {
                Values[l] = value;
            }
        }
        public static implicit operator T(MultilanguageProperty<T> instance)
        {
            if (instance == null)
            {
                return default(T);
            }
            return instance.GetValue();
        }
        public static implicit operator 
          List<KeyValuePair<Language.ApplicationLanguage,T>>(MultilanguageProperty<T> instance)
        {
            if (instance == null)
            {
                return null;
            }
            return instance.Values.ToList();
        }
        #region IEnumerable<KeyValuePair<ApplicationLanguage,T>> Members
 
        public IEnumerator<KeyValuePair<Language.ApplicationLanguage, T>> GetEnumerator()
        {
            return Values.GetEnumerator();
        }
 
        #endregion
 
        #region IEnumerable Members
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Values.GetEnumerator();
        }
 
        #endregion
    }
}

As you can see, two overloads of the GetValue method are developed. One of them returns the value for the default language and the other returns the value for the language passed as parameter.

Also, there is an indexer property to access values by an indexer. And there is an implicit operator that is defined to access the default language value without using any cast or convert function.

Now let's create a test application.

C#
public class TestObject
{
    public TestObject()
    {
        this.Name = new MultilanguageProperty<string>();
    }
    public int Id { get; set; }
    public string UniversalCode { get; set; }
    public MultilanguageProperty<string> Name { get; set; }
}
class Program
{
    public static List<TestObject> GetSampleDataForTestObject()
    {
        List<TestObject> listTestObject = new List<TestObject>();

        TestObject testObject1 = new TestObject { Id = 1, UniversalCode = "TR" };
        testObject1.Name.Values.Add(Language.ApplicationLanguage.Turkish, "Türkiye");
        testObject1.Name.Values.Add(Language.ApplicationLanguage.English, "Turkey");

        TestObject testObject2 = new TestObject { Id = 2, UniversalCode = "USA" };
        testObject2.Name.Values.Add(Language.ApplicationLanguage.Turkish, "Amerika Birleşik Devletleri");
        testObject2.Name.Values.Add(Language.ApplicationLanguage.English, "United States Of America");

        TestObject testObject3 = new TestObject { Id = 3, UniversalCode = "FR" };
        testObject3.Name.Values.Add(Language.ApplicationLanguage.Turkish, "Fransa");
        testObject3.Name.Values.Add(Language.ApplicationLanguage.English, "France");

        listTestObject.Add(testObject1);
        listTestObject.Add(testObject2);
        listTestObject.Add(testObject3);

        return listTestObject;
    }
    static void Main(string[] args)
    {

        List<TestObject> listTestObject = GetSampleDataForTestObject();
        WriteAllList(listTestObject);
        Console.WriteLine();
        WriteForSpecificLanguageByIndexer(listTestObject);
        Console.WriteLine();
        WriteForSpecificLanguageByGetValue(listTestObject);
        Console.WriteLine();
        WriteForDefaultLanguageByIndexer(listTestObject);
        Console.WriteLine();
        WriteForDefaultLanguageByGetValue(listTestObject);
        Console.WriteLine();
        WriteForDefaultLanguageByImplicit(listTestObject);
        Console.Read();
    }
    private static void WriteAllList(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing All List");
        foreach (TestObject to in listTestObject)
        {
            Console.WriteLine("Id:" + to.Id + " Code:" + to.UniversalCode);
            foreach (KeyValuePair<Language.ApplicationLanguage, string> kvp in to.Name)
            {
                Console.WriteLine("     Language:" + kvp.Key.ToString() + " Name:" + kvp.Value.ToString());
            }
        }
    }
    private static void WriteForSpecificLanguageByIndexer(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing Specific Language Value By Indexer");
        string s = listTestObject[0].Name[Language.ApplicationLanguage.English];
        Console.WriteLine(s);
    }
    private static void WriteForSpecificLanguageByGetValue(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing Specific Language Value By GetValue Method");
        string s = listTestObject[0].Name.GetValue(Language.ApplicationLanguage.English);
        Console.WriteLine(s);
    }
    private static void WriteForDefaultLanguageByIndexer(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing Default Language Value By Indexer");
        string s = listTestObject[1].Name[Language.GetDefaultLanguage()];
        Console.WriteLine(s);
    }
    private static void WriteForDefaultLanguageByGetValue(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing Default Language Value By GetValue Method");
        string s = listTestObject[1].Name.GetValue();
        Console.WriteLine(s);
    }
    private static void WriteForDefaultLanguageByImplicit(List<TestObject> listTestObject)
    {
        Console.WriteLine("Writing Default Language Value By Implicit Operator");
        string s = listTestObject[2].Name;
        Console.WriteLine(s);
    }

}

And here is the output for the test application.

1000002697_3.jpg

Please feel free to contact me for any questions.

History

  • 22.12.2011 : Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
Turkey Turkey
Tamer Oz is a Microsoft MVP and works as Assistant Unit Manager.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun21-Jul-13 19:59
Humayun Kabir Mamun21-Jul-13 19:59 
GeneralMy vote of 5 Pin
Anurag Gandhi17-Jul-13 4:55
professionalAnurag Gandhi17-Jul-13 4:55 
GeneralMy vote of 5 Pin
BadassAlien12-Nov-12 23:49
professionalBadassAlien12-Nov-12 23:49 
Simple yet elegant and effective solution. I like it
QuestionNice! Pin
Pieter Van Parys11-Jan-12 1:31
Pieter Van Parys11-Jan-12 1:31 
GeneralMy vote of 5 Pin
ezekjh31-Dec-11 7:44
ezekjh31-Dec-11 7:44 
QuestionLanguage as in what we speak, not what we program in. Pin
richinsea30-Dec-11 9:53
richinsea30-Dec-11 9:53 
GeneralMy vote of 5 Pin
Mahmoud Fathy Afify28-Dec-11 21:04
Mahmoud Fathy Afify28-Dec-11 21:04 
GeneralRe: My vote of 5 Pin
Tamer Oz30-Dec-11 7:31
Tamer Oz30-Dec-11 7:31 
GeneralMy vote of 5 Pin
sinan kul26-Dec-11 21:25
sinan kul26-Dec-11 21:25 
GeneralRe: My vote of 5 Pin
Tamer Oz27-Dec-11 20:33
Tamer Oz27-Dec-11 20:33 
GeneralMy vote of 5 Pin
Monjurul Habib22-Dec-11 11:03
professionalMonjurul Habib22-Dec-11 11:03 
GeneralRe: My vote of 5 Pin
Tamer Oz22-Dec-11 17:57
Tamer Oz22-Dec-11 17:57 
GeneralVery useful article Pin
sudhansu_k12321-Dec-11 19:33
sudhansu_k12321-Dec-11 19:33 
GeneralRe: Very useful article Pin
Tamer Oz21-Dec-11 20:12
Tamer Oz21-Dec-11 20:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.