Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Globalization, Internationalization (I18N), and Localization using C# and .NET 2.0

0.00/5 (No votes)
2 Nov 2009 1  
The globalization, internationalization (I18N), and localization of Windows applications in the .NET 2.0 platform.

Introduction

Globalization is the process of making an application able to handle different cultures and regions. For a Windows application, globalization means it is intended for worldwide distribution. There are two aspects of globalization:

  • Internationalization: Enabling the application to be used without language or culture barriers; i.e., language and culture information comes from a resource file rather than being hard coded in the application.
  • Localization: Translating and enabling the product for a specific locale. Based on a resource file, the application is translated into a specific language and culture.

In this article, we will discuss the internationalization and localization of Windows applications using the .NET 2.0 platform.

Background

Reading this article would give you a general understanding of the basic ideas discussed here, but to have a complete understanding of every concept, you must have some knowledge of the .NET API calls and their uses, about the CultureInfo class, .resx files creation, adding text and images to a resource file etc...

Localizing an application

Resource files (.resx) are used to localize resources in an application. We use classes in the System.Globalization namespace to make our application culture aware.

The first step to globalize an application is setting the Localizable property of the Windows Form. To do this, we need to open the Form in Designer mode.

Properties to set

By setting the above properties, all the settings/values of the Form and the controls of the Form are saved in the .resx file for that language. Initially, the language will be set to 'Default'. In our demo application, we can see these .resx files for the Windows Form, MainForm.

Once we change this language to some other language, a corresponding .resx file will be generated for the Form. As an example, for German language, 'MainForm.de-DE.resx'.

Now, if we change the location, size, text etc., for the controls, all these modified values will be stored in the corresponding language .resx file. While launching the application, System.Resources.ResourceManager detects the language and loads the correct resource file for it. We will have different .resx files for each language (that we support) for a Windows Form. If we use this approach for i18n, we should never touch our resource files, let the designer change the things.

Custom .resx file creation

Before going into the code details, first we will learn how to create and add a custom resource file to our project. We can do it by right-clicking on our Project file in Solution Explorer, and then clicking the Add --> New Item... menu. In the Add New Item window, select the template Resource file and give an appropriate name (as shown below).

How to add the resx file

Adding a resource in the .resx file

We can add resources to our project either as linked resources, which are external files, or as embedded resources, which are embedded directly into the .resx file. Strings can only be stored as embedded resources.

When we add a linked resource, the .resx file that stores our project resource information includes only a relative path to the resource file on disk. Images will be added as a linked resource.

Resources for the .resx file

The .resx resource file format consists of XML entries, which specify objects and strings inside XML tags.

When we add a string to the .resx file, a name/value pair in .resx format is wrapped in XML code, which describes string or object values; the name of the string is embedded in a <data> tag, and the value is enclosed in a <value> tag, as in the following example:

<data name="string1">
    <value>hello</value>
</data>

In our demo application, we have a custom .resx file, named CommonResource, and we can access the strings of the .resx file from our code using the following syntax:

// To get the strings from .resx file we need to write the code syntax 
// as - ResourceName.DataName
// string returnString = Resource.string1; // returns hello.

myAppCultureName.Text = CommonResource.ApplicationCultureName;
myAppCultureNativeName.Text = CommonResource.ApplicationCultureNativeName;

In order to globalize the application, we have to create a custom .resx file for each language. The name format of the newly created .resx file should be similar to the system created .resx files. For example, in the demo application, we have a custom .resx file CommonResource.resx, so the German .resx should be CommonResource.de-DE.resx and the French .resx should be CommonResource.fr-FR.resx.

ResourceManager will first look into the culture specific .resx file for the given string data. If the string data is not available in that file, it will read it from the neutral culture (Default Language) .resx file.

Adding images in the .resx file

Adding images in the .resx is as simple as adding strings in the .resx file, and the naming format is also similar, but it is actually a little cruel. ;)

Since the strings are embedded resources, there is no problem in creating the .resx files for different languages. But to add an image in the .resx file, we need to provide a distinct path of the image to each .resx file.

The simple approach to achieve this task is to create a Resource folder for each language in the project and add the corresponding image files in those folders (as shown below):

How to add images in .resx file

Now open the .resx file in Visual Studio and select the resource type as Images. Then, click on Add Resource --> Add Existing File... to add the images for each language in their corresponding .resx file.

It's done; now we can refer the image in the same fashion we are referring the strings. In our demo application, we have an image resx file Images with a resource Image1.

// Display the image on PictureBox a/c to the culture
myLanguagePictureBox.BackgroundImage = Images.Image1;

Using the code

The main class in the System.Globalization namespace is the CultureInfo class. It contains many methods and properties to identify different cultures and configure our application to use a specific culture.

Types of Culture

There are three types of Culture our application can use: InvariantCulture, NeutralCulture, and SpecificCulture.

InvariantCulture actually means culture independent. We use InvariantCulture when we want to compare strings, or display or compare dates in a culture independent way. By default, it uses the en-US culture. We have used this approach in the demo application to read the culture independent format for date:

// Invariant Culture can read the format specified
// in TextBox (DD/MM/YYYY), otherwise it 
// will expect the date in respective culture format.
// Change the CultureInfo type and see the difference.

DateTime.TryParse(myAgeTextBox.Text, CultureInfo.InvariantCulture,
        DateTimeStyles.AssumeLocal, out dateOfBirth);

NeutralCulture refers to cultures that are associated with a language but are not specific to a country or region. We can specify a NeutralCulture with two lower case characters identifying the language. For example, 'en' for English, 'fr' for French, etc.

SpecificCulture is the language and country specific culture which determines the language to use with our application and also the date-time and currency format to use with our application. We specify the specific culture with two lower case characters identifying the language, and two upper case characters identifying the country separated by hyphen. For example, 'en-US' for English (US), 'en-GB' for English (UK), 'fr-FR' for French (France), etc.

Setting a specific Culture

We use System.Threading.Thread.CurrentCulture and System.Threading.Thread.CurrentUICulture to configure our application to use a specific culture setting, like:

public void SetCultureInfo(string culture)
{
    // culture could be 'en-US', 'de-DE' etc...
    CultureInfo myCultureInfo = new CultureInfo(culture);
    Thread.CurrentThread.CurrentCulture = myCultureInfo;
    Thread.CurrentThread.CurrentUICulture = myCultureInfo;
        .
        .
    InitializeComponent();
        .
}

CurrentCulture represents the culture used by the current thread, whereas CurrentUICulture represents the culture used by the Resource Manager to look up culture-specific resources at run time.

Reading .resx with a specific culture at runtime

Sometimes we may need to read the resources from a .resx file with different cultures at runtime. In our demo application, we have a class ResourceManager for achieving this:

/// <summary>
/// Resource Manager Class
/// </summary>
public class ResourceManager
{
    /// <summary>
    /// Read the resource file and return the string of specified culture
    /// </summary>
    /// <param name="data">String Data to be read</param>
    /// <param name="cultureInfo">Type of Culture</param>
    /// <returns></returns>
    public static string GetString(string data, CultureInfo cultureInfo)
    {
        return CommonResource.ResourceManager.GetString(data, cultureInfo);
    }
}

We can modify this class or method to read the resources from a specific .resx file too. Now at runtime, we can pass the arguments to get culture specific value for the data.

if (!myShowCultureCheckBox.Checked)
{
    myDisplayLabel.Text = string.Format("{0}", 
      ResourceManager.GetString(FIXED_TEXT, CultureInfo.InvariantCulture));
}
else
{
    myDisplayLabel.Text = string.Format("{0}", 
      ResourceManager.GetString(FIXED_TEXT, CultureInfo.CurrentUICulture));
}

UI designing

If our Windows Form is localized, we can set the size, location etc., of controls specific to each culture. We can see it in our demo application where the control's location and size are different for each culture.

I hope the ideas discussed here will help you in making your application globalized. ;)

Points of interest

  1. If you want to do everything manually, just add a resource file, put all your common/shared strings in it, and embed the file on your project. Maintaining too many .resx files will be difficult. Suppose you have a resource string which is common for all the Forms in the application or project and you keep it separate for each Form, then updating a string value needs updating in all the .resx files, which is a little knotty. In our demo application, we have a resource file, CommonResource.resx, that contains all the shared strings for the application.
  2. If you use automatic internationalization, you should never touch your resource files; use the designer to change things. Changing this file could cause an application to behave weird.
  3. Try not to use the default language file (Form1.resx). Instead, create one for English and one for your other language (Form1.en-EN.resx and Form1.xy-XY.resx).
  4. Always use CultureInfo while dealing with DateTime, Calendar, or when converting objects to string - ToString(). For example, while validating 'Date', the code might expect the dd/mm/yyyy format for one language and mm/dd/yyyy for another. If the user enters the date according to the culture, we should validate it with culture information; otherwise, with invariant culture. In our demo application, we have a function for calculating age where InvariantCulture is used, since the user always has to enter the date in the dd/mm/yyyy format.

History

  • 31 Oct. 2009 - Updated demo files.
  • 30 Oct. 2009 - Updated the snapshots and description.
  • 29 Oct. 2009 - Posted the article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here