Internationalizing and Localizing a Windows Application

Although many (perhaps even most) commercial software users speak and read English, not all do. Those for whom English is a second language often prefer to use software that works in their own language. If you want to ship versions of your software that support more than one language, you need to think about internationalization and localization.

LANGUAGE, CULTURE, AND LOCALE

There are several words used in internationalization and localization that might need a little explaining. Let's start with internationalization: That's the overall process of realizing that an application might be used by people who read a different language than the developer creating the application, and might format dates and numbers differently as well. It's an architectural approach that you use from the beginning of a project, for example, by using ToShortDateString() instead of passing in a hard-coded date format. Localization is the job of making a new satellite assembly or resource file for a particular location. It is done once for each locale into which the application will be translated.

A locale is more specific than a language. English, for example, is spoken in many countries , but not identically so. Words such as color (which might appear on a button or menu item) are spelled differently in US English than in Canadian English, for example, and there are differences between French in France and French in Canada, or Spanish in the US and Spanish in Spain. A culture includes a language, a country, and a set of rules about formatting numbers, currency, times, and dates.

A Windows application uses two culture settings: one called Culture , used for dates and numbers, and the other called UICulture . Generally speaking, users can change Culture themselves on the Control Panel, but UICulture reflects the version of Windows the user has installed. If you bought the US English version of Windows, your UICulture is US English regardless of your Control Panel settings. You can install the Multilingual User Interface Pack, which among other things lets you change your UICulture from the Control Panel.


Designing for Internationalization

Windows has recognized the need for multi-language support for years , enabling users to set their locale and culture information on the Control Panel, and then using that information wherever possible in the user interface. Your application almost certainly leverages that power without knowing it. For example, when you convert a DateTime object to a string by calling its ToShortDateString() method, the format of the string depends on the user's culture settings. The same date might be represented as 03/04/2003 or 04/03/2003, depending on the user's location and settings. Similarly the number 1.234 in North America is written as 1,234 in Europe, and you don't have to write code to support this: When a number is converted to a string the local format rules are applied.

Support for different formats for numbers and dates is useful and convenient , but what about that form with buttons that read Add or Delete? What about your error messages and your prompts? There are many places within an application that reflect the language in which an application was developed. Building a WinForms application makes it simpler to support multiple languages and localization rules without writing a lot of code.

To build an application that supports multiple languages, you actually create multiple assemblies. The first contains all your executable code and one set of resources (text properties of controls and the like) prepared in the original language you used when developing the application. Satellite assemblies contain only the resources, translated into a different language or locale. The Framework checks the user's culture setting and loads the appropriate satellite assembly. If there is no satellite assembly corresponding to the user's setting, the original resources in the main assembly are used.

Localizing a Windows Application

To see how to localize a WinForms application, drag another button onto Form1 of the AdvancedUI project. Change the button's name to Hello and the text to Hello World . Double-click the button to add a handler, but don't add any code to the handler yet. Build the project and make sure it runs.

CULTURE NAMES

Culture names come in two parts : a language and a locale. The language part is the ISO standard two letter abbreviation: en for English, fr for French, de for German, and so on. The ISO list is available at

http://www.loc.gov/standards/iso639-2/englangn.html

The locale part is also a standard two letter abbreviation: CA for Canada, US for the US, DE for Germany, and so on. The ISO list is kept up to date at (be sure to type this all on one line)

http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html


In the designer, select the entire form, and use the Properties window to change the Localizable property to True . Change the Language property of the form to French (Canada) . Click the button and change the Text property to Bonjour Tout Le Monde . Lengthen the button to accommodate the text.

Build and run the application. Unless your copy of Windows is French Canadian, you should see Hello World on the button.

To simulate owning a French-Canadian copy of Windows, add this line to the Form1 constructor, before the call to InitializeComponent() :

 
 System::Threading::Thread::CurrentThread->CurrentUICulture =       new System::Globalization::CultureInfo("fr-CA"); 

When you set your UICulture with this line of code, the application displays the French version of the button. You can see how simple it is to support multiple languages on your buttons and other form components with this technique. Follow these steps:

  1. Code the entire application and test it. The user interface should be frozen before localization begins.

  2. Set the Localizable property of the form to True .

  3. Set the Language property of the form to the language in which you want to distribute the application.

  4. Change the Text property of buttons, labels, and other form components to their equivalents in the new language. Resize and rearrange components, if necessary.

  5. Repeat steps 3 and 4 for each language and locale you want to target.

Of course, this is only part of the story. What about your error messages and other strings? You don't set these in the designer, so you can't localize them this way. Instead you can keep them in a resource file, and create a different version of the resource file for each locale.

Adapting an existing application to get its strings from a resource file is quite a bit of work. That's why internationalization starts at the beginning of a project. Rather than adjusting the rest of the AdvancedUI project to use a resource file, this section shows you how to use a resource file in a new section of code.

Make sure that the Language property of Form1 is set to Default , and build the application. Right-click the AdvancedUI project in Solution Explorer and choose Add, Add New Item. Select an Assembly Resource File and name it Strings . The file is XML and it opens in the data view for you to edit. There are five columns in the resource file:

  • name The name of the resource. Avoid generic names such as ErrorMessage ; take the time to decide on a naming convention for all the error messages, prompts, and so on that will be used in your application.

  • value The actual string to display. This first resource file is in your default language.

  • comment A place for the programmer to document this resource.

  • type Resources can be strings, but can also be binary files such as images or audio clips. For nonstring resources, enter a class name such as System.Drawing.Bitmap for the type.

  • mimetype For nonstring resources, the MIME type of the data, such as application/x-microsoft.net.object.bytearray.base64 for binary (Base64) data.

Enter Hello for the name and Hello World for the value of the resource. Save the resource file.

Add another resource file named Strings.fr-CA.resx. The name is important: The part immediately before the .RESX extension must match the language and locale combination for which you are localizing. The first part must match the resource file you already created. Make sure you provide the .RESX extensionbecause there is a dot in the filename, the wizard will not add the extension for you if you omit it. (It thinks .fr-CA is the extension.)

In the data view of the French Canadian resource file, enter a resource named Hello with a value of Bonjour Mes Amis . Save the resource file.

Open the code for Form1.h and scroll to the bottom, where the empty Hello_Click handler was added earlier. Edit the handler so it reads like this:

 
 private: System::Void Hello_Click(System::Object *  sender,                                   System::EventArgs *  e) {    Resources::ResourceManager* rm =        new Resources::ResourceManager("AdvancedUI.ResourceFiles",                          Reflection::Assembly::GetExecutingAssembly());    String* greeting = rm->GetString("Hello");    Windows::Forms::MessageBox::Show(greeting); } 

This code starts by creating a ResourceManager . The two parameters that are passed to the constructor are the resource file and the assembly. When you add .RESX files to your project, the build process converts them to a single resource file called < projectname >.ResourceFiles.resources . The first part of this name, < projectname >.ResourceFiles , is what you pass to the constructor to identify the resource file. The assembly to pass is the one that is currently executing, available from a static method of the Assembly class.

Once a ResourceManager instance is created, you use it to access resources such as strings. In a large application you might make the ResourceManager reference a member variable of the form, create it in the form Load event, and just use it in various button handlers. The GetString() method looks up a string, and takes the name as a parameter. This code just puts up a message box with that string.

Build and run the application. If your UICulture is still set to French Canadian by the extra line in the constructor, you will see the Bonjour Tout Le Monde button, and when you click it, the message box will read Bonjour Mes Amis. Comment out the line in the constructor, build and run the application, and you should see Hello World on the button and in the message box.

If you wanted to make a version of this application in another language, you would add another .RESX file, but make no changes to the code. That's how the .NET Framework can make localization feasible even for small applications and small development teams . Just remember that you must plan for internationalization from the beginningfinding every hard-coded string in your application and replacing it with a call to GetString() will be very tedious .

Being Localizable

It's natural to wonder why you have to set the Localizable property of the form to True . Shouldn't forms always be localizable? Making a form localizable means that you no longer make any assumptions about user interfaces, not even assuming that text runs left to right instead of right to left. The number of properties for each component that is set in InitializeComponent goes up dramatically when a form is localizable. For example, when you first add the Hello button to this application, these lines are added to InitializeComponent() :

 
 this->Hello->Location = System::Drawing::Point(112, 224); this->Hello->Name = S"Hello"; this->Hello->TabIndex = 5; this->Hello->Text = S"Hello World"; this->Hello->Click += new System::EventHandler(this, Hello_Click); 

When the Localizable property of the form is set to True , this block of code changes to

 
 this->Hello->AccessibleDescription =     resources->GetString(S"Hello.AccessibleDescription"); this->Hello->AccessibleName =     resources->GetString(S"Hello.AccessibleName"); this->Hello->Anchor =      (*__try_cast<__box System::Windows::Forms::AnchorStyles *  >          (resources->GetObject(S"Hello.Anchor"))); this->Hello->BackgroundImage =      (__try_cast<System::Drawing::Image *  >         (resources->GetObject(S"Hello.BackgroundImage"))); this->Hello->Dock =       (*__try_cast<__box System::Windows::Forms::DockStyle *  >          (resources->GetObject(S"Hello.Dock"))); this->Hello->Enabled =       (*__try_cast<__box System::Boolean *  >          (resources->GetObject(S"Hello.Enabled"))); this->Hello->FlatStyle =       (*__try_cast<__box System::Windows::Forms::FlatStyle *  >          (resources->GetObject(S"Hello.FlatStyle"))); this->Hello->Font =       (__try_cast<System::Drawing::Font *  >          (resources->GetObject(S"Hello.Font"))); this->Hello->Image =        (__try_cast<System::Drawing::Image *  >          (resources->GetObject(S"Hello.Image"))); this->Hello->ImageAlign =       (*__try_cast<__box System::Drawing::ContentAlignment *  >           (resources->GetObject(S"Hello.ImageAlign"))); this->Hello->ImageIndex =       (*__try_cast<__box System::Int32 *  >           (resources->GetObject(S"Hello.ImageIndex"))); this->Hello->ImeMode =       (*__try_cast<__box System::Windows::Forms::ImeMode *  >          (resources->GetObject(S"Hello.ImeMode"))); this->Hello->Location =       (*__try_cast<__box System::Drawing::Point *  >           (resources->GetObject(S"Hello.Location"))); this->Hello->Name = S"Hello" ; this->Hello->RightToLeft =       (*__try_cast<__box System::Windows::Forms::RightToLeft *  >           (resources->GetObject(S"Hello.RightToLeft"))); this->Hello->Size =       (*__try_cast<__box System::Drawing::Size *  >           (resources->GetObject(S"Hello.Size"))); this->Hello->TabIndex =       (*__try_cast<__box System::Int32 *  >           (resources->GetObject(S"Hello.TabIndex"))); this->Hello->Text = resources->GetString(S" Hello.Text"); this->Hello->TextAlign =       (*__try_cast<__box System::Drawing::ContentAlignment *  >          (resources->GetObject(S"Hello.TextAlign"))); this->Hello->Visible =       (*__try_cast<__box System::Boolean *  >           (resources->GetObject(S"Hello.Visible"))); this->Hello->Click += new System::EventHandler(this, Hello_Click); 

As you can see, you should turn this property on only when you plan to localize your application.



Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory

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