2/26/11

C#- Change form language at runtime.

Recently I was trying to create multi language application in C#.

I found a lot of useful information in net.

You can always create a resource file and use it for localization:

Localization with resource file.
It's quite simple, but you always need to change text properties in form constructor... Not useful if we have a lot of forms.

Better solution is to localize your form in a designer.
Localization in Designer
It is easier to localize forms and all code is created by designer.

But how we can change language at run time? All procedures described before enables localization changing when new form is created.

I'll describe language change at run time in case when forms are localized using Designer.

In order to change application language we need to:
1. set a proper CultureInfo

            mCI = new CultureInfo(language);
            Thread.CurrentThread.CurrentCulture = mCI;
            Thread.CurrentThread.CurrentUICulture = mCI;

If you want to know all possible CultureInfos you can check it by:
CultureInfo[] a = CultureInfo.GetCultures(CultureTypes.AllCultures);

2. Take Form ComponentResourceManager.

ComponentResourceManager resources = new ComponentResourceManager(inputForm.GetType());

3. Change language.
using System.Globalization;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;

public void ApplyLanguageToForm(Form inputForm, string language)
        {
            CultureInfo mCI = new CultureInfo(language);
            Thread.CurrentThread.CurrentCulture = mCI;
            Thread.CurrentThread.CurrentUICulture = mCI;

            ComponentResourceManager resources = new ComponentResourceManager(inputForm.GetType());
            foreach (Control c in inputForm.Controls)
            {
                resources.ApplyResources(c, c.Name, mCI);
            }
        }

Unfortunately in this approach, you need to iterate through all controls, menus, labels etc... Code above iterates just through controls, so some of your form elements will not be changed.

In my opinion best approach is to use reflection.

4. Using reflection to change language at run time.

Unfortunately I haven't found any complete solution, but reflection simplifies code.

I've attached class which changes all controls placed on form, any drop down items, menu strips, or owned forms. Please notice that in ApplyLanguageToForm you can pass active form. If it's a modal dialog form, all owner controlls will be reloaded.

Here is an example of how it works:








And code of LanguageChange class:

using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;

namespace Lang
{
    public class LanguageChange
    {
        private CultureInfo mCI = null;

        public void ChangeLanguage(string language)
        {
            mCI = new CultureInfo(language);
            Thread.CurrentThread.CurrentCulture = mCI;
            Thread.CurrentThread.CurrentUICulture = mCI;
        }

        public void ApplyLanguageToForm(Form inputForm)
        {
            Form mainForm = inputForm;
            while (mainForm.Owner != null)
            {
                mainForm = mainForm.Owner;
            }
            ApplyLanguage(mainForm, null);
        }

        private void ApplyLanguage(object value, ComponentResourceManager resources)
        {
            if (value is Form)
            {
                resources = new ComponentResourceManager(value.GetType());
                resources.ApplyResources(value, "$this");
            }
            Type type = value.GetType();

            foreach (PropertyInfo info in type.GetProperties())
            {
                switch (info.Name)
                {
                    case "Items":
                    case "DropDownItems":
                    case "Controls":
                    case "OwnedForms":

                        if (info.PropertyType.GetInterface("IEnumerable") != null)
                        {
                            IEnumerable collection = (IEnumerable)info.GetValue(value, null);
                            if (collection != null)
                            {
                                foreach (object o in collection)
                                {
                                    PropertyInfo objNameProp = o.GetType().GetProperty("Name");
                                    ApplyResourceOnType(resources, o, objNameProp);
                                }

                            }
                        }
                        break;
                    case "ContextMenuStrip":
                        object obj = (object)info.GetValue(value, null);
                        if (obj != null)
                        {
                            ApplyLanguage(obj, resources);
                            resources.ApplyResources(obj, info.Name, mCI);
                        }
                        break;
                    default:
                        break;
                }
            }

        }

        private void ApplyResourceOnType(ComponentResourceManager resources, object o, PropertyInfo objNameProp)
        {
            switch (o.GetType().Name)
            {
                case "ComboBox":
                    for (int i = 0; i < ((ComboBox)o).Items.Count; i++)
                    {
                        ((ComboBox)o).Items[i] = resources.GetString(GetItemName(o, objNameProp, i), mCI);
                    }
                    break;
                case "ListBox":
                    for (int i = 0; i < ((ListBox)o).Items.Count; i++)
                    {
                        ((ListBox)o).Items[i] = resources.GetString(GetItemName(o, objNameProp, i), mCI);
                    }
                    break;
                // Other classes with string items
                default:
                    if (objNameProp != null)
                    {
                        string name = objNameProp.GetValue(o, null).ToString();
                        ApplyLanguage(o, resources);
                        resources.ApplyResources(o, name, mCI);
                    }
                    break;
            }
        }

        private string GetItemName(object o, PropertyInfo objNameProp, int i)
        {
            string name = String.Format("{0}.{1}",
                objNameProp.GetValue(o, null).ToString(),
                "Items");
            if (i != 0) name = String.Format("{0}{1}", name, i);
            return name;
        }
    }
}