Click here to Skip to main content
Click here to Skip to main content

Localizing a Windows Application with Satellite Assemblies

, 18 Feb 2010
Rate this:
Please Sign up or sign in to vote.
This article explains how to localize a C# Windows application using Satellite assembly with an example
UI.PNG

Introduction

Localization is a concept that should be known by every developer creating products or applications supporting multiple languages. I was searching for the article about implementing localization for windows application using satellite DLLs. I found many articles which explain the subject well. I am sharing my experience and findings while doing this.

Hopefully this article will be helpful to the people looking to implement localization with satellite DLLs.

The Overview

There have been many applications where end users feel happy to use a software with their local language. Localization does exactly the same! It is the process of converting user interfaces to a user’s local culture. This involves converting strings to the user's local culture and can also involve time, date, and number formats.

Please note: I am not a language expert and I have used fish to translate from English into other languages. The example attached may have incorrect translations.

Satellite Assembly

In simple words, satellite assembly is a resource-only assembly. A definition from MSDN says something like this: "A .NET Framework assembly containing resources specific to a given language is called as satellite assembly". Thus a satellite assembly is a localised assembly having only resources in it and not a code. As the name suggests, each satellite assembly is associated with a master assembly known as the neutral assembly or main assembly. All the application code is present in main assembly which loads the required satellite assembly depending on the UI culture to get the localized resources. In my example, Localization.exe is the neutral (main) assembly which contains all the application code. This example has several satellite assemblies associated with the main assembly i.e. Localization.exe.

Deploying Satellite Assemblies

Each satellite assembly is deployed to AppBase directory according to the rules of the .NET assembly loader. Each satellite assembly should be deployed to the directory <culturename> under AppBase directory. E.g. If we want to deploy the satellite assembly localized for United States Engilsh, it should be deployed as ApplicationName.resources.dll (e.g. in my example Localization.resources.dll) under the directory 'de-DE' which is a subdirectory of AppBase as below:

Deploy.PNG

Thankfully, Visual Studio and .NET compiler are intelligent enough and deploy the satellite assemblies to bin\debug\<culturename> or bin\release\<culturename> directory after successful compilation. See below:

debugdeploy.PNG

Satellite assemblies can be strongly named if needed to put in GAC. To create satellite assembly manually, refer to this article.

How .NET Framework Loads Satellite Assembly

To explain how .NET Framework loads satellite assembly for appropriate culture, have a look at the resource files available for the application example of this article. This application has a default resource file, i.e. Localization.resx. All the default strings and any other resources will be stored here. Now if you want to localize this application to 'fr-FR' culture, you need to simply create another resource file named as Localization.fr-FR.resx.

Now before loading an appropriate satellite assembly .NET framework queries the current culture to the Thread's CultureInfo.CurrentUICulture property. If your CurrentUICulture is 'fr-FR', it will first look for the Localization.fr-FR.resx resource file, i.e. satellite assembly AppBase/fr-FR/Localization.resources.dll. If this file is missing, it will look for the neutral culture of the given UICulture and it is the Localization.fr.resx file i.e satellite assembly AppBase/fr/Localization.resources.dll. If this resource file is also missing, default resource file named Localization.resx will be used.

Note: A neutral culture is specified by only the two-digit lowercase language code. For example, "fr" specifies the neutral culture for French, and "de" specifies the neutral culture for German.

Using the Code

Step 1: Create New Windows Application

Create a new C# Windows Application under New | Project. Let's call this application as Localization and let it create a solution directory. By this, you should have a blank form with Form1.cs file. Now add labels, radiobuttons and groupbox with some default text as shown below:

UI.PNG

Step 2: Add Resource Files Required for Localization

Right click on the project in solution explorer and click add new item. From the available options, select 'Resource File' and rename 'Resource1.resx' to 'ProjectName.<culturename>.resx'. Thus if you intend to have application localized for de-DE, fr-FR and pt-BR, then you should have Localization.de-DE.resx, Localization.fr-FR.resx and Localization.pt-BR.resx files in your project as below:

Solution.PNG

In addition to this, you should add a default resource file named Localization.resx which will be embedded in your main assembly. This file specifies the resources in neutral culture and will be used to load these default resources in case you are trying to load a culture for which your application is not localized.

Now you are ready to feed in the key and value pair for the strings to be used in UI. Add keys and its appropriate value in the specified culture in the resx file as below :

DefaultResx.PNG

GermanResx.PNG

FrenchResx.PNG

PortugueseResx.PNG

Now in case, if you forget to add String key in Localization.de-DE.resx. While loading this string resource manager will fail to search for it in AppBase/de-DE/Localization.resources.dll and will finally fetch the same from the resources embedded in the main assembly, i.e. Localization.resx. More details follow below in the section 'Specifying a default resource'.

Note that the form has a property Localizable which when set to true creates a default resource file for that form.

Step 3: Set CurrentUICulture to Machine Culture

If you are using English version of Windows, your application will always use English resource by default, i.e., Application built on the machine with English as locale would have CurrentUICulture = en-US by default. But this should be set to the appropriate culture automatically if the application is launched on a different locale machine. In order do this, you can add the following code to the application startup:

Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;  

When an application is launched on a machine having English as a locale, Thread.CurrentThread.CurrentCulture will be initialized to 'en-US', If machine has German locale, this will be initialized to 'de-DE' and so on... Thus the above code would make sure to set your CurrentUICulture to the appropriate culture automatically.

Step 4: Instantiating a ResourceManger

ResourceManager is a class which provides convenient access to culture-specific resources at run time. Add a member variable to your application as below:

private ResourceManager m_resourceManger = null;

Now, instantiate this object in the application load event as below:

m_resourceManger = new ResourceManager("Localization.Localization", 
				Assembly.GetExecutingAssembly());

Parameters

  • baseName

    The root name of the resources. This is typically will be in the form
    For example, the root name for the resource file named "MyResource.de-DE.resx" is "MyResource". Project in the example with this article has a default namespace 'Localization' hence baseName used here is 'Localization.Localization'.

  • assembly

    The main assembly for the resources.
    This instantiation can also be done in the constructor of Form1 class if you are sure that Form1 is called only once in your application.

If all the resx files are kept under some folder say Resources as below :

Resources.PNG

Here Localization.de-DE.resx is kept under Resources folder hence baseName would be Localization.Resources.Localization. i.e. DefaultNamespace.FolderName.baseName.

Please note: Localization can be implemented in two ways:

  • Creating a satellite assembly for culture specific resource file and using it in the executing assembly
  • Creating a file based resource manager which enables accessing and reading from a resource file from a location outside the assembly manifest

and ResourceManager can be instantiated slightly differently in both the cases. Refer to this article for more details.

Step 6: Update UI

Now, add a method 'UpdateUIControls' to update all UI controls as below:

private void UpdateUIControls
{
     try
     {
        if (m_resourceManger != null)
        {
           this.Text = m_resourceManger.GetString("A demo application");
           this.lblTrans.Text = m_resourceManger.GetString("String");
           this.lblMessage.Text = m_resourceManger.GetString
		("This is a demo application for localization");
           this.grpLanguages.Text = m_resourceManger.GetString("Select a language");
        }
     }
     catch (System.Exception e) 
     {
        MessageBox.Show(e.Message);
     }
}

Call this method from the constructor right after InitializeComponent().

Step 7: Implementing Runtime Localization

Now register a CheckedChanged event of each radiobutton to a function say OnLanguageChange(...) and set the appropriate culture to the UI and update UI controls with new culture as below:

private void OnLanguageChange(object sender, EventArgs e)
{
    RadioButton radioButton = sender as RadioButton;
    string culture = string.Empty;

    switch(radioButton.Text)
    {
       case "U.S. English (en-US)":
         culture = "en-US";
         break;
       case "German-Germany (de-DE)":
         culture = "de-DE";
         break;
       case "French - France (fr-FR)":
         culture = "fr-FR";
         break;
       case "Portuguese - Brazil (pt-BR)":
         culture = "pt-BR";
         break;
    }

    // This is used for the language of the user interface
    Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(culture);
    // This is used with formatting and sort options (e.g. number and date formats)
    // e.g. a float value 2.352 will be 2,3.52 if CurrentCulture is set to de-DE
     Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);

     /************************************************************************/
     /*  Below code can be used to set default language if the required one is not found.
     *  This can also be done by specifying 
     *  [assembly: NeutralResourcesLanguageAttribute
     *		("en-US", UltimateResourceFallbackLocation.Satellite)]
     *  in AssemblyInfo.cs                                                  */
     /************************************************************************/
     /*if (m_resourceManger != null && m_resourceManger.GetResourceSet
     /*(Thread.CurrentThread.CurrentUICulture, true, false) == null)
       {
           Thread.CurrentThread.CurrentUICulture = 
		new System.Globalization.CultureInfo("en-US");
           Thread.CurrentThread.CurrentCulture = 
		new System.Globalization.CultureInfo("en-US");
        }*/

     UpdateUIControls();
}

Specifying a Default Resource

If the application has default resource embedded into the main assembly, then resources from that file will be treated as default resources. But in case if you want to set one of the satellite resource file as a default one, then you need to add the below lines in AssemblyInfo.cs:

[assembly: NeutralResourcesLanguageAttribute
	("en-US", UltimateResourceFallbackLocation.Satellite)]

Alternatively, this can be done programmatically as below:

if (m_resourceManger != null && m_resourceManger.GetResourceSet
	(Thread.CurrentThread.CurrentUICulture, true, false) == null)
{
    Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
    Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
}  

CurrentCulture Vs CurrentUICulture

The property CurrentCulture is for setting the culture that is used with formatting and sort options, whereas the property CurrentUICulture is used for the language of the user interface. While CurrentUICulture = CurrentCulture is fine, the opposite is not true, CurrentCulture = CurrentUICulture will result in an exception of type System.NotSupportedException if CurrentUICulture has been set to a neutral culture. More details follow here.

History

  • 02/18/2010: Initial draft

License

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

About the Author

Motiram P Patil
Software Developer
India India
A Software development professional with an experience in designing software modules / libraries from scratch, re-architecture of the existing legacy software modules and managed a small size development team.
I have hands on programming experience in C++ (Native and managed), C#, VB.NET, MFC, Win32 SDK, Windows Installer Kit (WiX) and Computer Graphics using DirectX 9.0 and passionate to work on Windows internals, mobile devices and C++ 11.

Comments and Discussions

 
QuestionNicely done code example. PinmemberMember 81519514-Feb-14 7:19 
Questionif(CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft) [modified] PinmemberMember 81519514-Feb-14 7:00 
QuestionThanks , but i need more one tip PinmemberHussein Nagdy3-Feb-14 22:42 
QuestionThanks Pinmemberpitas14-Feb-13 5:49 
GeneralRe: Thanks PinmemberMotiram P Patil14-Feb-13 8:38 
GeneralMultiple Assemblies PinmemberMizan Rahman20-Jul-10 12:38 
GeneralThe (standard) alternative to this approach PinmemberGrant Frisken23-Feb-10 11:19 
GeneralRe: The (standard) alternative to this approach PinmemberMizan Rahman20-Jul-10 12:47 
GeneralRe: The (standard) alternative to this approach PinmemberGrant Frisken20-Jul-10 12:59 
GeneralRe: The (standard) alternative to this approach PinmemberHexadigm Systems17-Feb-11 8:22 
GeneralVery nice PinmemberMISCC22-Feb-10 13:54 
GeneralRe: Very nice PinmemberMotiram P Patil22-Feb-10 17:30 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 18 Feb 2010
Article Copyright 2010 by Motiram P Patil
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid