Internationalizing and Localizing .NET Desktop Applications

DevToolsGuy / Thursday, January 26, 2017

Introduction

This blog will provide examples of internationalization and localization across two .NET platforms - Windows Forms and WPF. The approaches are similar although the syntax is slightly different. A similar approach can be used in ASP.NET. Things get much different in HTML5 and JavaScript, where 3rd party libraries are often used.

Overview of the Project

The project, which we'll create in both Windows Forms and WPF, is a very simple UI with a label and a dropdown. Using just these elements, we can demonstrate the following aspects of internationalization:

  1. Strings in an external file
  2. Modification of UI layout
  3. An alternative to checking for string equality

Windows Forms Example

    1. In Visual Studio, create a new Windows Forms project.

    2. From the Common Controls section of the toolbox, add a Label and a ComboBox. Name them lblQuestion and cmbColor.
    3. Set the Text property of the Label to "What is your favorite color?". Instead of laying out the UI horizontally, we can do it vertically. This will allow more space for the translation of the question, as the spacing was tight in English, and other languages could have a longer string, which would truncate.


    4. Instead of adding strings directly to the Items property in the designer, add a few colors to the ComboBox through the code behind. This is done so that there can be a Value property associated with each item, instead of only Text.

      cmbColor.DisplayMember = "Text";
      cmbColor.ValueMember = "Value";
      
      var colors = new[] {
         new { Text = "Red", Value = "red" },
         new { Text = "Blue", Value = "blue" },
         new { Text = "Yellow", Value = "yellow" },
         new { Text = "Green", Value = "green" },
         new { Text = "Pink", Value = "pink" }
      };
      
      cmbColor.DataSource = colors;
      
    5. Add code to the SelectedValueChanged event to check if the user selected the green dropdown item. Note that this uses "SelectedValue", not "SelectedText", which is why we had to set the items in code to include a value that won't change based on locale.

      private void cmbColor_SelectedValueChanged(object sender, EventArgs e)
      {
        if ((string)cmbColor.SelectedValue == "green")
        {
            MessageBox.Show("Green is my favorite color too!");
        }
      }
      
    6. Now that we have everything set up, let's externalize the UI string resources. This can be done either before or after the initial design, as Visual Studio handles it for you either way.

      1. Set the Localizable property of the Form to true. This will setup the form resource file for you.
      2. In the Solution Explorer, open the Form1.resx file. You can see that the question has been added here.
      3. You can copy this file, rename the copy according to your target locale's culture code, and add it to the project. I'm using Japanese (ja) as an example.
      4. In the target locale's file, you can remove any entries beginning with ">>", as these don't need to be duplicated. You can also remove any entries under "Other", as well as any images, icons, files, etc. that you don't plan to change. Then you can translate the question.
    7. This won't work for anything in the code behind, because the Form1.resx file is autogenerated by the designer. If you add something to the .resx file that doesn't exist in the designer, then change the designer, it will remove what you added. Instead, for the code behind, let's add a separate resource file to the project.

      1. Add the key and value for the message in the code behind. Add the colors in here too.
      2. Copy, rename, and add the file to the project.
      3. Translate the entries from the code behind.
      4. Replace the hardcoded strings from the code behind with references to this resource file. You may have to rebuild to see the intellisense.

        private void cmbColor_SelectedValueChanged(object sender, EventArgs e)
        {
           if ((string)cmbColor.SelectedValue == "green")
          {
              MessageBox.Show(Strings.favoriteColorMatch);
           }
        }
        
        var colors = new[] {
          new { Text = Strings.colorRed, Value = "red" },
          new { Text = Strings.colorBlue, Value = "blue" },
          new { Text = Strings.colorYellow, Value = "yellow" },
          new { Text = Strings.colorGreen, Value = "green" },
          new { Text = Strings.colorPink, Value = "pink" }
        };
        
    8. Build and run the project.

You can test out your other locale by using the following code in the constructor, before InitializeComponent:

System.Threading.Thread.CurrentThread.CurrentUICulture = 
        new System.Globalization.CultureInfo("ja"); 

WPF Example

      1. In Visual Studio, create a new WPF project.
      2. From the Common WPF Controls section of the toolbox, add a Label and a ComboBox to the <Grid> section of the XAML file. Name them lblQuestion and cmbColor. Set the Content property of the Label to "What is your favorite color?". You can arrange them vertically in a stack panel, which will allow more space for localized text.

        <Grid>
         <StackPanel VerticalAlignment="Center">
           <Label Content="What is your favorite color?" HorizontalAlignment="Center" 
                            Name="lblQuestion" />
           <ComboBox HorizontalAlignment="Center" Name="cmbColor" Width="120" />
         </StackPanel>
        </Grid>
        
      3. Add items to the ComboBox using the designer. Set the Content for the text, and the Tag for the value. The resulting XAML will look like this:

         <ComboBox HorizontalAlignment="Center" Name="cmbColor" Width="120" 
                       DropDownClosed="cmbColor_DropDownClosed">
             <ComboBoxItem Content="Red" Tag="red" />
             <ComboBoxItem Content="Blue" Tag="blue" />
             <ComboBoxItem Content="Yellow" Tag="yellow" />
             <ComboBoxItem Content="Green" Tag="green" />
             <ComboBoxItem Content="Pink" Tag="pink" />
         </ComboBox>
        
      4. Add code to the DropDownClosed event to see if the user selected the green dropdown item. Note that we will check the Tag property, not the Content property, to avoid internationalization issues.

         private void cmbColor_DropDownClosed(object sender, EventArgs e)
         {
             ComboBoxItem cbi = (ComboBoxItem)cmbColor.SelectedItem;
             if (cbi.Tag.ToString() == "green")
             {
                 MessageBox.Show(Properties.Resources.favoriteColorMatch);
             }
         }
        
      5. Now that we have everything set up, let's externalize the UI string resources.

        1. Open the Resources.resx file that comes with the project.
        2. Add the keys and values for the question, message in the code behind, and colors.
        3. Copy, rename, and add the file to the project.
        4. Translate the entries from the code behind.
        5. Replace the hardcoded strings from the XAML file and the code behind with references to this resource file. First, add a namespace declaration to the XAML file.
          xmlns:resx="clr-namespace:WPFI18N.Properties" 
          Make sure you have set the resource file's access modifier to Public.

          Then you can use bindings to the file in XAML.
          <Label Content="{x:Static resx:Resources.question}" 
                  HorizontalAlignment="Center" Name="lblQuestion" />
          <ComboBox HorizontalAlignment="Center" Name="cmbColor" Width="120" 
                  DropDownClosed="cmbColor_DropDownClosed">
            <ComboBoxItem Content="{x:Static resx:Resources.colorRed}" 
               Tag="red" />
            <ComboBoxItem Content="{x:Static resx:Resources.colorBlue}" 
               Tag="blue" />
            <ComboBoxItem Content="{x:Static resx:Resources.colorYellow}" 
               Tag="yellow" />
            <ComboBoxItem Content="{x:Static resx:Resources.colorGreen}" 
               Tag="green" />
            <ComboBoxItem Content="{x:Static resx:Resources.colorPink}" 
               Tag="pink" />
          </ComboBox>
          
          In the code behind:
          MessageBox.Show(Properties.Resources.favoriteColorMatch); 
      6. Build and run the project.

You can test out your other locale by using the following code in the constructor, before InitializeComponent:

System.Threading.Thread.CurrentThread.CurrentUICulture = 
               new System.Globalization.CultureInfo("ja"); 

Conclusion

In these two examples, we demonstrated how to take text, layout, and comparison into consideration when designing a desktop application.

For testing, you can also change the CurrentCulture which will affect things like dates and number formatting. This doesn't apply in our simple example, but would impact a more complete application.

Note that even in another language, the message only shows up if the green item is selected. This is why we didn't compare the text of the selected item - so that we don't have to worry about it not working.

By planning for internationalization early, we saved ourselves from having to change the layout of the UI or change the comparison logic later in the development cycle.