Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / Windows Forms

ResourceBundle - An Alternative Way to Do Localization in .Net

Rate me:
Please Sign up or sign in to vote.
4.13/5 (6 votes)
5 Sep 2009CPOL7 min read 46.8K   694   27   9
How to do localization in .Net with a ResourceBundle class and change language dynamically at run time

 Download SampleResourceBundle For WPF

Introduction  

If you search on the Internet on how to do localization in .Net, you can easily find some good references and samples.  Here, I want to introduce a way to do localization in .Net with a ResourceBundle class I wrote.  Also I didn't come up with this idea all by myself.  The way how this ResourceBundle class works is similar to how localization is done in Java or Flex development.  I just want to use my example to show that it is possible to do localization in .Net this way. 

One sample is for a Windows Application and the other one is for a WPF Application. 

Using ResourceBundle Class   

I am going to walk through here how to use ResourceBundle class and change locale at run time.   

   1.  Add ResourceBundle class to the project 

After creating a new project, either a Windows Application with WinForms or a WPF application, add the folder System.Resources and the files inside to the project. 

I used the namespace "System.Resources" so you don't have to worry about renaming their namespaces when adding them to other projects.   

ResourceBundle.cs is pretty small and has less than 150 lines.  But instead of going to the details of this class, I want to explain how to use it to change language dynamically in a .Net  Application. <o:p>

<o:p>   2.  Create locale folders and files     

All locale files will go to the same place, a "locale" folder.  Then create sub-folders for locales to support, such as "en_US" and "zh_CN" in the samples.  Then you can either create a single resource file for each locale or multiple resource files for each locale.  To create a resource file for "en_US", right click "en_US" folder and select "Add- a new Item".  Then select "Resource file" as an item to add and rename the file.   

When a resource file is added, a .resx file and a Designer.cs will be added.  You can simply delete the Designer.cs file in locale/en_US folder.  The benefits of those Designer.cs files in locale folder are to enbale you to refer to a resource item by using its name, such as “SampleResourceBundle.locale.en_US.Form1.buttonText”.  However, by using the resource bundle approach, we don’t plan to refer to a resource item this way.  Instead, we will be using resourceBundle.GetString() to retrieve the values of resource items. So those Designer.cs files can be deleted safely and everything else will still be working.  (Sometimes you edit something or rebuild the application and Visual Studio will add the Design.cs files back.  Then that's fine too.  Just leave them alone.)

<o:p>

Create resource files for "en_US" only and defer the creation of resource files for other languages until the whole implementation is done.  It is good to make sure that everything for "en_US" works before moving on to another language.<o:p>  

a.  WinForms Sample -  In this sample, I created Form1.resx and Form2.resx for en_US locale. 

The sample application includes 2 Hello World Forms, Form1 and Form2.  Clicking on a button in Form1 will bring up Form2.  Clicking on a button in Form2 will close Form2 and go back to Form1.   

The folder structure is like this:<o:p> 

Image 1

b.  WPF Sample - In this sample, I created Resources.resx for en_US locale.  

The sample WPF application has only one window which shows a ListBox to select language, a Hello World label and a button.     

In the default WPF application created by Visual Studio 2008, there will be an App.xaml, which is the start up of the application.   Delete this class because we want to start with an App.cs directly so we can put more codes there.  The App.cs will look like this:  

 class App : Application
{
   [STAThread]
   static void Main()
   {
       App myApp = new App();
       myApp.Startup += AppStartUp;

       myApp.Run();
   }

   static void AppStartUp(object sender, StartupEventArgs e)
   {

       Window1 mainWindow = new Window1();
       mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
       mainWindow.Show();
   }

 So App class will start the application and show Window1. 

      3.  Init ResourceBundle  <o:p>

Add a static method InitResourceBundle() to the Program.cs or App.cs.  The method may look like this: <o:p>  

a.  WinForms Sample  

static void InitResourceBundle()
{
ResourceBundle resBundle = ResourceBundle.GetInstance();
resBundle.LocaleRoot = "SampleResourceBundle.locale";
    resBundle.AddResourceFile("Form1");
    resBundle.AddResourceFile("Form2");

    string culture = Properties.Settings.Default.LastCulture;
    resBundle.Locale = culture;
    Application.CurrentCulture = new CultureInfo(culture);

} 

b.  WPF Sample 
static void InitResourceBundle()
{
    ResourceBundle resBundle = ResourceBundle.GetInstance();
    resBundle.LocaleRoot = "SampleResourceBundleWPF.locale";

    resBundle.AddResourceFile("Resources");

    string culture = "en-US";
    resBundle.Locale = culture;
    System.Threading.Thread.CurrentThread.CurrentCulture=new CultureInfo(culture);
}

ResourceBundle is a Singleton class, so call its GetInstance() to get an instance.  After that, set LocaleRoot, where the locale files are located.  The namespace for this project is "SampleResourceBundle" and all resource files are in "locale" folder.  So the LocaleRoot is "SampleResourceBundle.locale".  <o:p>

Then add all resource files as those in locale folders, such as "Form1" and "Form2", or "Resources".  <o:p>

Then set resourceBundle's locale.  In the code example for WinForms Application, I tried to retrieve user's last setting.  Or simply set default locale to "en_US". <o:p>

      4. Call InitResourceBundle() in <st1:place w:st="on">Main() <o:p>

In <st1:place w:st="on">Main(), call InitResourceBundle() before showing the first window.  <o:p> 

[STAThread]
 static void Main()
 {
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);

     //init Resource Bundle before start the MainForm
     InitResourceBundle();

     Application.Run(new Form1());
 }

        5.  Add a resourceBundle as a data member in Form1.cs or Window1  <o:p>

      Define a resourceBundle as a data member in Form1 or Window1 class. 

protected ResourceBundle resBundle = ResourceBundle.GetInstance();

   6.  Add a Refresh() to pull strings from ResourceBundle for UI 

Call resourceBundle's GetString() method to get string value for a resource item, such as  <o:p>

          resBundle.GetString("Form1", "title");    <o:p>   

The first parameter is the resource file name, and the second parameter is the item name.  The returned value is the value defined in Form1.resx file in locale folder, "My First Form" for "title".  

a.  WinForms Sample   

public override void Refresh()
  {
      this.Text = resBundle.GetString("Form1", "title");
      this.m_lbl1.Text = resBundle.GetString("Form1", "helloText");
      this.m_btn1.Text = resBundle.GetString("Form1", "buttonText");

      m_langue.Text = resBundle.GetString("Form1", "changeLanguage");

      base.Refresh();
  } 

b.  WPF Sample 

private void Refresh()
  {
      this.Title = resBundle.GetString("Resources", "win1Title");
      m_win1Label.Content = resBundle.GetString("Resources", "win1Label");
      m_wind1Button.Content = resBundle.GetString("Resources", "win1ButtonTxt");
  }

7.  Call Refresh() at Window Load

 a.  WinForms Sample 

private void Form1_Load(object sender, EventArgs e)
    {
        ///////////////////////////
        //This has to be called at Load to put the correct language in
        this.Refresh();
        //////////////////////////

b.  WPF Sample 

  In Window1.xaml file, add an event handler for window's Loaded event in <Window> tag.

Loaded="Window_Loaded"

In Window1.cs file, add the implementation of the handler, which is to call Refresh.

     private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Refresh(); 

} 

At this point, the App can be up and running and use the resource strings defined in the resource files.  

       8.  Add another locale <o:p>

After everything is working for the base locale, or en_US, other locales can be added o the locale folder.  For this sample, I added a zh_CN folder for Chinese.  Copy the files under en_US to zh_CN and then you can put translations to the zh_CN folder. 

If you decide to leave the Designer.cs file there in locale folder, just make sure the one copied to another locale is modified to have the correct namespace, such as  "SampleResourceBundle.locale.en_US".

       9.  Add a UI for language change <o:p>

a.  WinForms Sample 

In this sample application, I used a combo box to do language change.  At Form1’s Load handler, call the fillAppLangueCombo() to initialize the combo box with English and Chinese selection:  

private void fillAppLangueCombo()
    {
        CultureInfo enCulture = new CultureInfo(SpecificCultures.EN_US);
        CultureInfo zhCulture = new CultureInfo(SpecificCultures.ZH_CN);
        m_langue.Items.Add(enCulture);
        m_langue.Items.Add(zhCulture);
    }

Then click on the combo box to add a SelectedIndexChanged handler.<o:p>  

  private void m_langue_SelectedIndexChanged(object sender, EventArgs e)
{
    CultureInfo culture = (CultureInfo)m_langue.SelectedItem;

    //inform resourceBundle of the locale change
    resBundle.Locale = culture.Name;

    Application.CurrentCulture = culture;

    //let's remember user's setting for culture
    Properties.Settings.Default.LastCulture = culture.Name;
    Properties.Settings.Default.Save();

    this.Refresh();
}

So at language selection change, change the locale for resource bundle.  Save user’s selection to Properties.Settings.  Then call Refresh method so the Form will pick up resource items according to the new locale through resource bundle class.   

b.  WPF Sample 

 Add a ListBox in the window1.xaml. 

<ListBox Name="m_langue" Width="100" HorizontalAlignment="Left" SelectionChanged="m_langue_SelectionChanged" >
          <ListBoxItem>English</ListBoxItem>
          <ListBoxItem>Chinese</ListBoxItem>
      </ListBox> 

Add an event handler for ListBox' SelectionChanged event in xmal.  Then add the handler in .cs file as follows:

private void m_langue_SelectionChanged(object sender, SelectionChangedEventArgs e)
 {
     CultureInfo culture;
     if (m_langue.SelectedIndex==0)
         culture = new CultureInfo(SpecificCultures.EN_US);
     else
         culture = new CultureInfo(SpecificCultures.ZH_CN);

     resBundle.Locale = culture.Name;

     System.Threading.Thread.CurrentThread.CurrentCulture = culture;

     this.Refresh();
 }

Then language can be changed at run time. 

Image 2

Using LocaleManager Tool  

I’ve listed 9 steps to follow to make Resource Bundle work.  But once you can make it work, it is actually not that complicated and you can apply it to other applications.

One of the most troublesome headache of localization is to maintain all the locale files and keep them in sync.  I’ve developed a locale Synchronization and Translation software called LocaleManger, which is posted at code project.  Click here to go to the article about LocaleManager tool.   

It is very easy to use this tool.  It pulls back the files in base locale, such as en_US and files in different locales and displays them in one worksheet side by side.  If there is any new item in base locale that has not been translated, you can easily see that in the worksheet.  Also double click any cell in a target locale column will get that item machine-translated.  So when you don’t have translation done yet but simply want to see how it is going to look like in another language, you can use the tool to do some machine translation. 

  Image 3

Image 4

 

Image 5

History 

09/04/2009 Added Sample for WPF 

09/02/2009  Initial  - WinForms Sample 

License

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


Written By
United States United States

Comments and Discussions

 
QuestionGood Alternative to Multi-Language Toolkit Limitations Pin
snowbird770919-Jan-17 1:39
snowbird770919-Jan-17 1:39 
QuestionWeb Forms Pin
kpkrish5-Sep-12 23:05
kpkrish5-Sep-12 23:05 
General[My vote of 1] Strange way to do it Pin
justastupidgurl7-Sep-09 18:59
justastupidgurl7-Sep-09 18:59 
GeneralRe: [My vote of 1] Strange way to do it Pin
Angela Han8-Sep-09 7:13
Angela Han8-Sep-09 7:13 
GeneralRe: [My vote of 1] Strange way to do it Pin
justastupidgurl8-Sep-09 20:51
justastupidgurl8-Sep-09 20:51 
GeneralRe: [My vote of 1] Strange way to do it Pin
Angela Han9-Sep-09 12:34
Angela Han9-Sep-09 12:34 
GeneralRe: [My vote of 1] Strange way to do it Pin
Valeriu zabulica9-Sep-09 2:17
Valeriu zabulica9-Sep-09 2:17 
GeneralRe: [My vote of 1] Strange way to do it Pin
Grant Frisken13-Sep-09 21:34
Grant Frisken13-Sep-09 21:34 

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.