Internationalization / Localization - Part

 


 C# - DirectX 3-D Basics

 

What is An IDL File ?


ActiveX Data Objects


OLE DB


  1.    DCOM -  Distributed Component Object Model

COM - Component Object Model 

 

         COM FAQ's 

 


 

As the number of Internet users grew worldwide, need to create software supporting multiple languages was felt intensely because only ten percent of people use English as primary language. People wanted an application that could communicate with them in their native language. This became simple with the release of .NET framework. 

Locale and .NET Culture

Geographically speaking, a locale is a place. In software terms, a locale is a set of information associated with a place. Locale information includes the name and identifier of the spoken language, cultural conventions, etc. A Locale Identifier (LCID) is used to retrieve information about the locale. LCID is a 32-bit unsigned integer value that is divided into four parts. The first two parts identify the language and sub-language (sub-language corresponds to country/region) and the last two parts specify the sorting order for text strings. 

The culture in .NET refers to user’s language and region. A culture is identified by language code–region/country code. For example, French language spoken in France is identified by ‘fr-FR’. Here, ‘fr’ is the short form for ‘French’ and ‘FR’ is abbreviation of France. Similarly, ‘fr-CA’ is the culture identifier for French in Canada. There are cultures that are identified by language only. These cultures are called neutral cultures. The cultures having language-region combination are called specific cultures. For example, ‘fr’ is a neutral culture, whereas, ‘fr-FR’ is a specific culture.

.NET encapsulates the culture related information in the System.Globalization and System.Resources namespaces. The object of the Globalization.CultureInfo class represents a specific culture. Using the methods of this class, we can obtain the information of the culture that is set for the current thread. Following code snippet shows how to set the culture for the current thread and obtain its name, number format and currency format. 

foreach ( CultureInfo c in CultureInfo.GetCultures( CultureTypes.AllCultures ) )
{

Thread.CurrentThread.CurrentCulture = new CultureInfo ( c.Name ) ;
Console.WriteLine ( "{0}:{1},Num:{2},Dt:{3},Curr:{4}",c.Name,
c.EnglishName, (12345).ToString ( "n" ), ( 12345.50 ).ToString ( "c" ), 
( DateTime.Now ).ToShortDateString( ) ) ;

}

The CultureInfo.GetCultures( ) method returns the list of all the supported cultures. CultureTypes is an enum that contains the types of culture viz. specific, neutral, all cultures and only those cultures that are installed on the system. To set the culture for a thread, firstly we must obtain the reference to that thread. Here, we have obtained the same by using the CurrentThread property of the Thread class. The two culture values of an application determine what resources are loaded for an application and how information like currency, numbers, and dates is formatted. The resources loaded are determined by the UI culture setting, and the formatting options are determined by the culture setting. The Thread class provides two properties—CurrentCulture and CurrentUICulture to change the respective culture settings. We have used the CurrentCulture property since we needed to set the culture specific formatting options. The Name and EnglishName are properties of the CultureInfo class that give the culture name and its corresponding Language (Country/Region) combination respectively. Of course, we need to use the Globalization and Threading namespaces to run this code.

We would now see how to localize resources. We would create a localized WinForm application that would allow user to select a language. On clicking a button another form would get displayed in the selected language. This form would contain a textbox where you can type your name. On clicking a button you would be greeted in the selected language. Our form would look as shown below.

Name the radio buttons as reng, rger, rita, and rfre respectively. Name the ‘Display’ button as bdisplay. Add the Click event handler for the ‘Display’ button. Here is the handler.

void bdisplay_Click (object sender, System.EventArgs e )
{

Thread t = Thread.CurrentThread ;
if ( rfre.Checked )

t.CurrentUICulture = new CultureInfo ( "fr-FR" ) ;

if ( rger.Checked )

t.CurrentUICulture = new CultureInfo ( "de-DE" ) ;

if ( rita.Checked )

t.CurrentUICulture = new CultureInfo ( "it-IT" ) ;

if ( reng.Checked )

t.CurrentUICulture = new CultureInfo ( "en-US" ) ;

greetform g = new greetform( ) ;
g.ShowDialog( ) ;

}

Here, we have only checked which radio button the user has selected and set the UI culture accordingly. After this we have displayed another form. We need to add this form to the project. Add controls to this form as shown below.


The controls and their names are given in the following table.


  Control   Name 
  Name text box   tname
  Greet button   bgreet 
  Label   lgreet 
  Close button   bclose

Add the Click event handlers for the ‘Greet’ and ‘Close’ buttons. The bgreet_Click( ) handler is given below. 

void bgreet_Click ( object sender, System.EventArgs e )
{

String s ;

DateTime t = DateTime.Now ;

}

we had designed the greeting form and added code to change the culture. Let us now add localization support to it. 

After we are done with developing the form, select the Localized property of the greetform to True. This property specifies whether localized code is to be generated for this form. We must now specify the languages that our application would support. For this, select the Language property. Click the down arrow. It would display list of cultures. Select ‘French (France)’ then select ‘Italian (Italy)’ and ‘German (Germany)’ one after other. As soon as we select a language, say, French (France) two resource files ‘greetform.fr.resx’ and ‘greetform.fr-FR.resx’ get added to our project. In these files we need to add French equivalents for the resources. Add the data as shown in the following table in the ‘greetform.fr-FR.resx’ file. We will see the purpose of ‘greetform.fr.resx’ file later.


  Name   Value
  Lname.Text   Nom:
  bgreet.Text   Saluer
  bclose.Text   Fermer

Following table shows the data to be added to German and Italian resource files.


  Name   Value
  German 
  lname.Text   Name:
  bgreet.Text   Begrüßen
  bclose.Text  Enden
Italian 
  lname.Text  Nome:
  bgreet.Text  Saluτare
  Bclose.Text   Argomentare

Now you can run the program. Select a language and display the greetform. The greetform in Italian is shown in the following figure.

But there is a problem in this output. Although resources are appearing in Italian, the greeting message is still in English. This is because, when we localize resources, an entry is made in their properties to extract proper resource from appropriate resource file. Since welcome messages are string literals their properties are not changed. We have to do this job ourselves. We also need to make few changes in the program. Add the following translations in the resource files for German, French and Italian.


  Name   Value
French
  GoodMorning   Bonjour
  GoodAfternoon   Bonsoir
  GoodEvening  Bonaprèsmidi
German
  GoodMorning   Guten Morgen
  GoodAfternoon   Guten Tag
  GoodEvening    Guten Abend
Italian
  GoodMorning   Boungiorno
  GoodAfternoon   Bounpomeriggio
  GoodEvening   Buonasera

We must also provide default culture settings for English. For this, we can modify the main resource file i.e. ‘greetform.resx’. In this file we must add three resource strings and their English equivalents that happen to be same.

Change the bgreet_Click( ) handler as given below.


private void bgreet_Click ( object sender, System.EventArgs e )
{

ResourceManager r = new ResourceManager ( "locale_greet.greetform", 
Assembly.GetExecutingAssembly( ) ) ;
String s ;

DateTime t = DateTime.Now ;
if ( t.Hour <= 12 )

s = r.GetString ( "GoodMorning" ) ;

else
{

if ( t.Hour <= 19 )

s = r.GetString ( "GoodAfternoon" ) ;

else

s = r.GetString ( "GoodEvening" ) ;

}
s += " " + tname.Text ;
lgreet.Text = s ;

}

The ResourceManager class provides methods to work with culture specific resources at run-time. The ‘locale_greet.greetform’ is the root name of the resources (If resource file name is “MyResource.en-US.resources” then root name is “MyResource”) that we want to access from the current assembly. We have specified fully qualified name of the resource file. We have used the ResourceManager.GetString( ) method to extract the value of the resource passed to it as parameter. Use the System.Resources and System.Reflection namespace for the ResourceManager and Assembly classes respectively.

Now run the program again and confirm that the greetings are displayed in the chosen language.

Behind The Scene

Let us see what happens behind the scene when we localize the form, select the language and compile the program. 

When we set the Localizable property to true, information about all the resource strings, properties, embedded pictures, etc. gets stored in the root resource file i.e ‘greetform.resx’. The following statement gets added to the InitializeComponent( ) method.

ResourceManager resources = new ResourceManager( typeof ( greetform ) ) ;

The type greetform provided to the constructor is used to gather all information like assembly name, root name of resource, etc. for finding the resources. Instead of assigning values directly to the properties, methods like GetString( ), GetObject( ) are used to retrieve the strings and pictures from the resource file. Before retrieving the values, the CurrentUICulture property is checked for the current culture.

On compilation, for every localized resource separate assembly called ‘satellite assembly’ gets created in the folder named after the culture name. We can find this folder in ‘bin\Debug’ folder. The satellite assembly contains manifest describing the localized resource. Since resources are shipped as separate assemblies and not as part of main assembly we can easily replace or update resources corresponding to a specific culture without replacing the application's main assembly.

We know that on selecting the language two resource files get created. One is for specific culture (greetform.fr-FR.resx) and another for neutral culture (greetform.fr.resx). To understand the purpose of creating two files, we must first understand the path in which localized resources are searched.

The localized resources are searched for in a hierarchical manner. At the top of hierarchy is the resources for default culture i.e English (“en”). Below that are the resources for any neutral culture. Below those are the resources for any specific cultures. Suppose a user requests for the resources localized in French (France). The CLR would first search in the global assembly cache for the assembly matching requested culture (“fr-FR”). If not found, it searches in the folder of currently executing assembly for the ‘fr-FR’ folder. If not found it searches the GAC again for the fallback culture contained in the parent assembly (“fr’). If the parent assembly is not found run-time searches all potential levels of parent assemblies. On failing that then it uses the resources for default culture. If we don’t provide the neutral cultures problem may arise in some cases. For example, it may so happen that a machine contains culture of “fr-CA” and our program loads the culture “fr-FR”. If “fr-FR” is not found then run-time would directly load the English culture instead of French culture that we could provide in neutral “fr” assembly.