Globalization and Localization


The terms "globalization" and "localization" are frequently confused. Often people choose one of the terms to mean both when, in fact, each has a specific meaning:

  • Globalization refers to designing and developing software that supports localized user interfaces and regional data for users of multiple cultures.

  • Localization refers to the translation of the application's resources into localized versions for each culture supported by the application.

As you can see, you need both globalization and localization for an application to support multiple cultures. Basically, globalization is the underlying architecture and localization is the actual translation. This is why the .NET Framework provides a System::Globalization namespace and not a localization one.

To globalize an application, you need to be able to specify cultural differences in things such as numbers, dates, and calendars. For example, Table 17-5 shows some number formats based on culture.

Table 17-5: Number Formats Based on Culture

CULTURE

NUMBER FORMAT

France (French)

123 456 789,01

Germany (German)

123.456.789,01

Switzerland (German)

123'456'789.01

U.S. (English)

123,456,789.01

Notice in Table 17-5 that there are two different ways of displaying numbers in German cultures. The Swiss have what is known as a subculture (but don't tell the Swiss that!). This points out that to support globalization, an application must also support subcultures. Seems to me things are starting to get complex. Okay, let's throw Chinese and Japanese character sets into the mix—now you're talking complex!

Fortunately, the .NET Framework has a few things up its sleeve to help support all these complexities. Don't get me wrong: Writing globalization code isn't for the faint of heart. It's tough! This section will only show you where to begin in globalizing your application. Please consult the many books that have been written on the subject for further information.

The Globalization Tools

The first line of attack for handling globalization by the .NET Framework is that it uses Unicode to support the various cultural-specific encoding types you may use in your applications. Unicode allows you to support complex character sets such as Chinese and Japanese, as well as the generic ASCII character set.

The next thing the .NET Framework does is provide intelligence in its classes and structures to support multiple cultures. For example, the DateTime and String objects generate appropriate culture-specific information. To add this intelligence, the .NET Framework relies on the System::Globalization namespace (see Table 17-6) to provide support.

Table 17-6: Common System::Globalization Namespace Classes

CLASS NAME

DESCRIPTION

Calendar

Specifies how to divide time into pieces (e.g., weeks, months, and years)

CultureInfo

Provides specific information about a culture

DateTimeFormatInfo

Specifies how dates and times are formatted

NumberFormatInfo

Specifies how numbers are formatted

RegionInfo

Provides information about the country and region

TextInfo

Specifies the properties and behaviors of the writing system

The final thing that the .NET Framework does to help support globalization was hinted at previously when I covered resources. The .NET Framework supports culture-specific resources using the ResourceManager class.

Culture

A culture in computer terms is a set of display preferences based on the language, beliefs, social norms, and so on (i.e., culture) of the user. How a computer processes the actual program internally does not differ based on culture. Culture only changes how the information is finally displayed. For example, adding two Int32s together using the German culture will not differ from how it is done using the French culture—the difference lies in how the final outcome is displayed.

The .NET Framework uses culture names based on RFC1766. If that means nothing to you, don't worry. It just means the .NET Framework uses a two-letter language and a two-letter country/region code separated by a hyphen (-) to specify a culture. It's possible to only specify a two-letter language if the country/region isn't significant.

Table 17-7 lists some of the many cultures available to you.

Table 17-7: Computer Cultures

NAME

CODE

English

en

English (Canada)

en-ca

English (United Kingdom)

en-gb

English (United States)

en-us

French

fr

French (Canada)

fr-ca

French (France)

fr-fr

German

de

German (Germany)

de-de

German (Switzerland)

de-ch

You use the System::Globalization::CultureInfo class to convert one of the codes in Table 17-7 into something that the .NET Framework understands:

 CultureInfo *cinfo = new CultureInfo(S"en-ca"); 

Setting the Culture

To get globalization to work within the CLR, you need to do one of two things:

  • Use a special version of the ToString() method that takes the culture as a parameter.

  • Set the culture you wish to use in the thread of execution.

The first method enables you to restrict globalization only to areas of your application that you specify. The second method of changing the CultureInfo in the CurrentThread changes the culture everywhere.

For example, if you want to display a date in multiple cultural styles, you could code it as shown in Listing 17-12.

Listing 17-12: Multicultural Dates

start example
 using namespace System; using namespace System::Globalization; Int32 main() {     DateTime dt = DateTime::Now;     Console::WriteLine(S"en-us {0}",dt.ToString("D",new CultureInfo(S"en-us")));     Console::WriteLine(S"en-gb {0}",dt.ToString("D",new CultureInfo(S"en-gb")));     Console::WriteLine(S"fr-fr {0}",dt.ToString("D",new CultureInfo(S"fr-fr")));     Console::WriteLine(S"de-de {0}",dt.ToString("D",new CultureInfo(S"de-de")));     return 0; } 
end example

Figure 17-15 shows MulticulturalDates.exe run on December 4, 2002.

click to expand
Figure 17-15: The result of executing the MulticulturalDates program

Now here comes a tricky part. There are two cultures you can set in the CurrentThread. The first is CurrentCulture, which is used by the Globalization namespace to handle culture-specific formatting. The second is CurrentUICulture, which is used by the ResourceManager to handle culture-specific resources. You may need to set one or both depending on what you are doing. Here is how you can set both to the French (France) culture:

 Thread::CurrentThread->CurrentCulture = new CultureInfo(S"fr-fr"); Thread::CurrentThread->CurrentUICulture = Thread::CurrentThread->CurrentCulture; 

The Localization Tools

Once you have an application designed and coded for multiple cultures, you then have to go through the long process of localizing it for each culture you want to support. Fortunately, Visual Studio .NET provides much of the functionality you need to localize your application if you happen to be building a Windows application. It also provides much of the localization functionality for a console application, providing you use a minor trick.

The way in which localization works is actually very elegant. First, you create a default version of all of your display elements, placing each in a resource file. Then for every other culture, you create a satellite resource file. Within that satellite resource file are replacement elements for the default view. Thus, when the culture is changed, the ResourceManager looks into the satellite resource of that culture first for display elements. If it finds the element it wants there, then it uses it. If it doesn't find the element it wants there, then it takes the default value.

Building a Multicultural Windows Application

The addition of localization to a Windows application is quite impressive. You really don't see how impressive it is until you try it yourself. Let's start off by creating a very simple Windows Form containing a single label that looks like Figure 17-16.

click to expand
Figure 17-16: A very simple Windows Form

When you look at the autogenerated code in the InitializeComponent() method, you see pretty standard and unimpressive code (see Listing 17-13).

Listing 17-13: Very Simple Windows Form Code

start example
 void InitializeComponent(void) {     this->SuspendLayout();     //     // lbHello     //     this->lbHello->Font =         new System::Drawing::Font(S"Microsoft Sans Serif", 12,                                     System::Drawing::FontStyle::Bold);     this->lbHello->ImeMode = System::Windows::Forms::ImeMode::NoControl;     this->lbHello->Location = System::Drawing::Point(24, 16);     this->lbHello->Name = S"label2";     this->lbHello->Size = System::Drawing::Size(424, 23);     this->lbHello->TabIndex = 6;     this->lbHello->Text = S"Hello, my name is Stephen";     //     // Form1     //     //...Standard Form stuff     this->ResumeLayout(false); } 
end example

Okay, now let's take this same code and make it localizable. To do this, simply set the Form's Localizable property to true (see Figure 17-17).

click to expand
Figure 17-17: Setting the Localizable flag to true

Now take a look at the code in the InitializeComponent() method (see Listing 17-14).

Listing 17-14: Localizable Simple Application

start example
 void InitializeComponent(void) {     System::Resources::ResourceManager *resources =         new System::Resources::ResourceManager(              __typeof(MultiCulturalApp::Form1));     this->SuspendLayout();     //     // lbHello     //     this->lbHello->AccessibleDescription =         resources->GetString(S"lbHello.AccessibleDescription");     this->lbHello->AccessibleName =         resources->GetString(S"lbHello.AccessibleName");     this->lbHello->Anchor = (*__try_cast<__box AnchorStyles*>         (resources->GetObject(S"lbHello.Anchor")));     this->lbHello->AutoSize = (*__try_cast<__box System::Boolean *>         (resources->GetObject(S"lbHello.AutoSize")));     this->lbHello->Dock = (*__try_cast<__box DockStyle *>         (resources->GetObject(S"lbHello.Dock")));     this->lbHello->Enabled = (*__try_cast<__box System::Boolean *>         (resources->GetObject(S"lbHello.Enabled")));     this->lbHello->Font = (__try_cast<System::Drawing::Font *>         (resources->GetObject(S"lbHello.Font")));     this->lbHello->Image = (__try_cast<System::Drawing::Image *>         (resources->GetObject(S"lbHello.Image")));     this->lbHello->ImageAlign = (*__try_cast<__box ContentAlignment*>         (resources->GetObject(S"lbHello.ImageAlign")));     this->lbHello->ImageIndex = (*__try_cast<__box System::Int32 *>         (resources->GetObject(S"lbHello.ImageIndex")));     this->lbHello->ImeMode = (*__try_cast<__box ImeMode *>         (resources->GetObject(S"lbHello.ImeMode")));     this->lbHello->Location = (*__try_cast<__box System::Drawing::Point *>         (resources->GetObject(S"lbHello.Location")));     this->lbHello->Name = S"lbHello";     this->lbHello->RightToLeft = (*__try_cast<__box RightToLeft *>         (resources->GetObject(S"lbHello.RightToLeft")));     this->lbHello->Size = (*__try_cast<__box System::Drawing::Size *>         (resources->GetObject(S"lbHello.Size")));     this->lbHello->TabIndex = (*__try_cast<__box System::Int32 *>         (resources->GetObject(S"lbHello.TabIndex")));     this->lbHello->Text = resources->GetString(S"lbHello.Text");     this->lbHello->TextAlign = (*__try_cast<__box ContentAlignment *>         (resources->GetObject(S"lbHello.TextAlign")));     this->lbHello->Visible = (*__try_cast<__box System::Boolean *>         (resources->GetObject(S"lbHello.Visible")));     //     // Form1     //     //...More resources stuff cut to save space     this->ResumeLayout(false); } 
end example

Wow, we're not in Kansas anymore! Every aspect of the label has now become a resource. As such, it can take on any look and feel you want based on the values you place within the resource file that populates this label. At this point, all the information about the label (and the Form, incidentally) is stored in a resource file called Form1.resx. This resource file contains all the default information about the Windows Form, as I pointed out as the first part of localization.

Now you'll add a new culture, French (France), to the Form. To do this you set the Form's Language property to French (France). Scrolling up and down in the Language property's selection displays quite a few cultures, don't you think?

Notice any difference in the Windows Form design? Nope, me neither. Here's the fun part: Go wild and change any property of the label, but just don't delete it. Now toggle between the default language and the French (France) language. Notice that they retain the information specific to each culture. (Well, apparently you can't go too wild there, as it seems a few of the properties aren't stored in the resource file automatically. Border and background color are two that surprised me by not working.)

Anyway, now that you've created a French (France) culture, notice there's now a Form1.fr.resx file and a Form1.fr-fr.resx resource file added to your Solution Explorer. Both of these compile to satellite assemblies. You get both of these resource files because ResourceManager is designed to look at subculture, then culture, and finally the default for culture-related display changes. Because it checks both of these places, Visual Studio .NET is giving the culture for free when you create a subculture. If you look at the subculture resource file contents, you'll notice that it contain all the changes that you made to the default label and the culture resource is empty (unless you change the culture's display).

Now let's see what happens when you compile the Windows Form application. After you compile, go ahead and open Windows Explorer and navigate to the directory structure where the application runs. There are now two new directories, one for each culture using the culture's RFC1766 code as a name. Also, in each directory is a file called [ApplicationName].resources.dll, as shown in Figure 17-18. (My image has two additional directories because I also created a German culture.) These two new .dll files are your satellite assemblies.

click to expand
Figure 17-18: The Windows Explorer view of satellite assemblies

Run your new multicultured application. You see none of your French stuff, right? That is because your native culture is not French (France) so the ResourceManager took the default resource values and not the French one. (Oh, of course if you are reading this book in France and your machine is configured for French, then you would see the French. French readers might try some other culture for this example.)

As I stated previously, you need to change the CurrentThread class's CurrentUICulture to the satellite assembly's culture you want to access. Do this by adding the following lines before you call the InitializeComponent() method:

 Thread::CurrentThread->CurrentCulture = new CultureInfo(S"fr-fr"); Thread::CurrentThread->CurrentUICulture = Thread::CurrentThread->CurrentCulture; 

In the accompanying source code that you can download from the Downloads area of the Apress Web site (http://www.apress.com), I actually pop up a dialog box to select the culture, but the preceding code works just as well to demonstrate that the program works.

Figure 17-19 shows MultiCulturalApp.exe in action.

click to expand
Figure 17-19: The result of executing the MultiCulturalApp program

Building a Multicultural Console Application

When you build an assembly that isn't a Windows application, things aren't quite as easy. But it doesn't take much to fool Visual Studio .NET into believing it's building Windows-like satellite assemblies.

Let's create a simple little program call MulticulturalConsole (see Listing 17-15) that writes four colors stored in a resource string table.

Listing 17-15: Writing Out Four Colors from a Resource

start example
 using namespace System; using namespace System::Reflection; using namespace System::Resources; Int32 main() {     Assembly *assembly = Assembly::GetExecutingAssembly();     ResourceManager *rmgr =         new ResourceManager(S"MulticulturalConsole.Colors", assembly);     Console::WriteLine(rmgr->GetObject(S"Color1"));     Console::WriteLine(rmgr->GetObject(S"Color2"));     Console::WriteLine(rmgr->GetObject(S"Color3"));     Console::WriteLine(rmgr->GetObject(S"Color4"));     return 0; } 
end example

Add a new item of type Assembly Resource File (.resx) and name it Colors. Then add the string resources as shown in Figure 17-20. Finally, rename the generated resource file as $(IntDir)/$(RootNamespace).Colors.resources.

click to expand
Figure 17-20: The Colors assembly resource file

When you run MulticulturalConsole.exe you should get something like Figure 17-21. There is nothing new so far.

click to expand
Figure 17-21: The first result of MulticulturalConsole

Now let's make the program multicultural. The first step is to add the code to the application so that it will display based on another culture or, in other words, you globalize the application. You do this by setting the CurrentThread CurrentUICulture to something else. Let's change it to "fr-fr" or French (France), as shown in Listing 17-16.

Listing 17-16: Writing Out Four Colors from a Resource Multiculturally

start example
 using namespace System; using namespace System::Reflection; using namespace System::Resources; using namespace System::Threading; using namespace System::Globalization; Int32 main() {     Assembly *assembly = Assembly::GetExecutingAssembly();     ResourceManager *rmgr =         new ResourceManager(S"MulticulturalConsole.Colors", assembly);     Console::WriteLine(rmgr->GetObject(S"Color1"));     Console::WriteLine(rmgr->GetObject(S"Color2"));     Console::WriteLine(rmgr->GetObject(S"Color3"));     Console::WriteLine(rmgr->GetObject(S"Color4"));     Thread::CurrentThread->CurrentUICulture = new CultureInfo(S"fr-fr");     Console::WriteLine(rmgr->GetObject(S"Color1"));     Console::WriteLine(rmgr->GetObject(S"Color2"));     Console::WriteLine(rmgr->GetObject(S"Color3"));     Console::WriteLine(rmgr->GetObject(S"Color4"));     return 0; } 
end example

The only new thing you did was change the CurrentUICulture. I just cut and pasted the four lines that display the colors.

Now it's time to fool Visual Studio .NET. When Visual Studio .NET created its resource files (which later became satellite assemblies) for the multiculture example, it did so in a very specific manner. The fortunate thing is that if you create your resource files in the same way, even in a console application, you will also get correctly built satellite assemblies.

Basically, here is how you do it. Create an assembly resource file (.resx) named WhatYouWant.resx that contains all the resource items for the default language. Also rename the autogenerated resource file as $(IntDir)/$(RootNamespace).WhatYouWant.resources. Notice that this is the same procedure you followed earlier when you embedded the standard resource file.

Now here's the trick to add, let's say, a French culture. Create a new assembly resource file (.resx) and name it WhatYouWant.fr-fr.resx. Add all the replacement values that you want for that culture. Then rename the autogenerated resource file as $(IntDir)/$(RootNamespace).WhatYouWant.fr-fr.resources. That's it! Placing the culture just before the .resx and .resources files is enough to trick Visual Studio .NET into creating a culture-specific satellite assembly.

So for the previous MulticulturalConsole example, create an assembly resource file (.resx) named Colors.fr-fr.resx. Then add the string resources as shown in Figure 17-22.

click to expand
Figure 17-22: French Colors assembly resource file

Notice that it is important that the names of the name/value pairs match between the default and the French resource files. Finally, rename the generated resource file as $(IntDir)/$(RootNamespace).fr-fr.Colors.resources.

When you run the revised MulticulturalConsole.exe, you should get something like Figure 17-23. There is nothing new so far.

click to expand
Figure 17-23: Revised result of MulticulturalConsole

As you can see, once you change the culture to French, the ResourceManager looks first in the French satellite assembly for the value. Because there is no Color2, the English (default) value is written.




Managed C++ and. NET Development
Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
ISBN: 1590590333
EAN: 2147483647
Year: 2005
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net