Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
CHAPTER
Resources and Localization In this chapter, you will • Implement localizability for the user interface • Convert existing encodings • Implement right-to-left and left-to-right mirroring • Prepare culture-specific formatting
Localization is rapidly becoming a central part of application development, not only for building applications that can be used in different languages, but that also accurately mirror the local culture of the client. Localization becomes paramount when you think of web sites that will potentially be used by clients from all the corners of the world. Localization is the process of writing software that is able to sense the user’s locale and change its behavior based on that information. The user can change his or her locale by making changes to the Regional Settings in the control panel. The term culture refers to a combination of parameters and methods that allow a program to adjust to the geographical location of the user. For example, the program can correctly display the currency of a region and print it in the proper location (for example, $42.00 for the United States and Canada; 42,00 kr for Sweden). The culture also contains information on how to display dates and numbers. One part of localization is the use of strings in the language of the region. In this chapter you will learn how to use strings from many different cultures to write as generic a program as possible. This will allow you the freedom to define the strings and resources used in a program in a central store that can be maintained for multiple languages and locales. All applications contain resources that can be centralized for better management, such as the string literals used for labels and button controls. The .NET Framework draws on the ancestry of Windows to provide resources for strings, images, icons, and custom resources.
1 P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:22 PM
7
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
2
String Resources String resources are language-specific strings that are made available to a program so that the resource manager can find the properly localized string to use when the application is executed on the user’s computer. Using string resources enables you to write code that has no string literals in it—the literals are defined in either text files or XML-formatted resource files. We will start with our old friend, the Hello World program, and localize it. Using System; class Hello { public static void Main() { Console.WriteLine("Welcome to the Hello Program"); Console.WriteLine("\n\nHello World!"); Console.WriteLine("\n\nSee you Later"); } }
This program will always display the same thing, irrespective of where in the world it is executed. To make the program display in German, we would have to create a new version for Germany, and yet another version for any other language that might be needed. This is an almost impossible situation to manage. The way to start on the road towards localization is to move the strings out from the program and store them in a separate file. In our example, the file will be named strings.txt, and it is located in the same directory as the source file for the program. It has this content: txtGreeting = Welcome to the Hello Program txtBye = See you soon. txtHello = Hello World!
This text file is not directly usable by itself—it needs to be compiled, and the tool that is used to compile resource files is the resgen utility. To create a resource file from the strings.txt file that can be embedded in an assembly, use the following command: resgen strings.txt
The result is a new file named strings.resources that can be used in our program. We also need to make some changes to our program to be able to use the new resource file. First we need to add some namespaces to support localization: using System.Globalization; using System.Resources;
System.Globalization adds the support for culture-related information, while System.Resources adds the support for resources, and most importantly, the Resource Manager.
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:22 PM
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
3 Once we have the namespaces added, we can proceed to get a reference to the Resource Manager. The Resource Manager is a rather heavy resource for the application, so you should take care to only create one. The common technique is to create the Resource Manager as a static member of the class—the code looks like this: static ResourceManager rm = new ResourceManager("strings", Assembly.GetExecutingAssembly());
Now we are ready to use the strings in the string resource file. We do that by using the GetString() method of the Resource Manager: Console.WriteLine(rm.GetString("txtHello"));
The final program will be as follows. The CultureInfo object is used to retrieve the current culture with this statement: CultureInfo ci = Thread.CurrentThread.CurrentCulture;
The current thread of our program uses the culture of the operating system for the UI (user interface) culture, the current culture is what the user has configured in the Regional Settings. To make the current thread’s UI culture the same as the current culture, we use this statement: Thread.CurrentThread.CurrentUICulture = gh.ci;
We also changed the name of the class to GlobalHello to indicate the global nature of the program, and the file is now called HelloGlobalWorld.cs. Here’s the revised program: using System; using System.Globalization; using System.Resources; using System.Reflection; using System.Threading; class GlobalHello { // create the resource manager once only, it is resource heavy static ResourceManager rm = new ResourceManager("strings", Assembly.GetExecutingAssembly()); // create a culture information object CultureInfo ci = Thread.CurrentThread.CurrentCulture; public static void Main() { GlobalHello gh = new GlobalHello(); // ensure the current thread is using the culture Thread.CurrentThread.CurrentUICulture = gh.ci; // Display a welcome Console.WriteLine(rm.GetString("txtGreeting")); Console.Write("\n\n"); // say hello world
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:23 PM
PART II
EXAM TIP The name of the resource in the ResourceManager constructor is case-sensitive: “string” is different from “String”.
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
4 Console.WriteLine(rm.GetString("txtHello")); Console.Write("\n\n"); // say bye Console.WriteLine(rm.GetString("txtBye")); } }
To compile the program use the csc compiler with the /res: switch to indicate the default (fallback) resource to use: csc /res:strings.resources HelloGlobalWorld.cs
When you run the program, it will display the following: C:\gc\global\demo>HelloGlobalWorld Welcome to the Hello Program Hello World! See you soon.
If you go in now and change your Regional Setting in the control panel to French, and you run the program again, there will not be any change to the output—there are no French resources defined for the program yet. That’s what we need to do next. To define language-specific resources, you will need to create a subdirectory under the directory where the executable is located. For the French language, you use the two-letter code from the RFC 1766 hierarchy to name the directory for French as fr; other language codes are en for English, de for German, sv for Swedish, and so on. The electronic documentation that comes along with Visual Studio .NET contains a complete listing of the codes. Once the directory is created, you need to create a language-specific string resource file in the new directory. The following is an example of a language-specific string resource: txtGreeting = Bienvenue bonjour au programme txtBye = Voyez-vous plus tard. txtHello = Bonjour Monde!
In order to make the resource available, you must compile it into a resource file (as we did earlier for the English version) and then create a satellite assembly (for a refresher on assemblies, see Chapter 6). It is important to remember that the naming of the file is critical, and it is case-sensitive. The resource file we used as the fallback was called strings.txt, so we will call the French version strings.fr.txt. When we compile it using the resgen utility, the resulting resource will be called strings.fr .resources. To create the satellite assembly, you need to use the assembly-building utility, al, as follows: al /t:lib /culture:fr /embed:strings.fr.resources /out:HelloGlobalWorld.resources.dll
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:23 PM
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
5 Remember that the commands are case-sensitive; the output file must have the same name as the program (HelloGlobalWorld) and must end with resources.dll. The result is that there is an assembly in the fr directory. Now use the Regional Settings applet in the control panel to change your region to “French (France)” as in Figure 7-1, and run the program. The result is as follows:
Bonjour Monde! Voyez-vous plus tard.
The language has changed to the language of the client’s Regional Settings.
Localized Formatting Once the strings are localized, we need to look at the formatting of data such as currency, numbers, and the date and time. The localization of these items is done through the boxing (conversion to objects) of the data. As an example, the following code segment will create an int variable that is then printed using the boxing of the int to an Int32, on which we can call the ToString Figure 7-1 The Regional Options dialog box
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:23 PM
PART II
C:\gc\global\demo>HelloGlobalWorld Bienvenue bonjour au programme
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
6 method (for a refresher in boxing and unboxing see Chapter 2). The formatting string passed to the method specifies how the data is to be formatted: int i = 420000000; Console.WriteLine(" " + i.ToString("N"));
The resulting display depends on the client’s locale, as shown in the following examples: 420 000 000,00 420.000.000,00 420,000,000.00
// French // German // English
The boxing technique is an elegant way of ensuring that the client’s locale is honored. The same technique can be used with other data types, like double and long. The special treatment of currency formatting takes the currency sign and position (pre or post) into consideration, as well as the representation of the decimal and thousands separator. The following code segment will display the double 42.00 as currency using the client’s locale: double d = 42.00; Console.WriteLine(" " + d.ToString("C"));
The resulting output will be as follows: $42.00 42,00 kr L. 42 42.00 DM
// // // //
English (United States) Swedish (Sweden) Italian (Italy) German (Germany)
EXAM TIP The boxing operation, and the use of the ToString() method makes the localization of numeric dates seamless. Date and time values can be treated in the same fashion, and by taking advantage of the formatting, we can display the date and time in a format familiar to the user. The following code does just that—the key again is to use the ToString() method to perform the localized action: DateTime dt = DateTime.Now; Console.WriteLine(" " + dt.ToString("F"));
The following program is the final version of the HelloGlobalWorld program that is localized to work with any culture: using System; using System.Globalization; using System.Resources; using System.Reflection; using System.Threading; class GlobalHello { // create the resource manager once only, it is resource heavy static ResourceManager rm = new ResourceManager("strings",
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:23 PM
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
7
}
The string resource that is used would look like this: txtGreeting = Welcome to the Hello Program txtBye = See you soon. txtHello = Hello World! txtNumber = written in localized format is: txtCurrency = written using the localized currency: txtDate = Today is:
Running this program with the regional setting for English would produce this output: C:\gc\global\demo>HelloGlobalWorld Welcome to the Hello Program Hello World! 420000000 written in localized format is: 420,000,000.00 42 written using the localized currency: $42.00 Today is: Tuesday, March 26, 2002 7:53:31 PM See you soon.
All the strings in this program are localized. There are no string literals used in the code, nor are the numeric values displayed without using the formatting of the ToString() method.
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:23 PM
PART II
Assembly.GetExecutingAssembly()); // create a culture information object CultureInfo ci = Thread.CurrentThread.CurrentCulture; public static void Main() { GlobalHello gh = new GlobalHello(); // ensure the current thread is using the culture Thread.CurrentThread.CurrentUICulture = gh.ci; // Display a welcome Console.WriteLine(rm.GetString("txtGreeting")); Console.Write("\n\n"); // say hello world Console.WriteLine(rm.GetString("txtHello")); Console.Write("\n\n"); // print out some numbers using the culture // assign the value 420000000 to a variable and display it int i = 420000000; Console.Write("{0} " + rm.GetString("txtNumber"), i); Console.WriteLine(" " + i.ToString("N")); // print out 42.00 of the default currency for the culture double d = 42.00; Console.Write("{0} " + rm.GetString("txtCurrency"), d); Console.WriteLine(" " + d.ToString("C")); // print out todays date and week day using the culture DateTime dt = DateTime.Now; Console.Write(rm.GetString("txtDate")); Console.WriteLine(" " + dt.ToString("F")); // say bye Console.WriteLine(rm.GetString("txtBye")); }
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
8
Implementing Right-to-Left Mirroring and Encoding For locales that use a right-to-left script direction, you will have to implement the proper interface for direction, which is done by setting the RightToLeft property to RightToLeft.Yes for all the controls and forms in the application. The controls on a form all inherit the RightToLeft property of the form. In Figure 7-2 you can see the property set to Yes, and that the form’s caption has moved to the right side of the window bar. When working with ASP.NET pages, you must also work with RightToLeft script direction as well as allow the UTF-8 encoding (Unicode characters encoded using UCS Transformation Format, 8-bit form) to be used to send Unicode characters to the client.
Figure 7-2
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
Setting the RightToLeft property
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
9 There are two UTF encodings available: UTF-7, which encodes 2-byte wide Unicode characters in 7-bit characters for the original US-ASCII e-mail systems, and UTF-8, which encodes Unicode characters as two 8-bit characters for transmission over the network. ASP.NET uses Unicode internally, and by including the following directive at the top of the ASP.NET page, UTF-8 will be returned to the client: <%@Page Language="C#" ResponseEncoding="UTF-8"%>
Best Practices Localization is a State of Mind; it has to be designed into the application from the beginning. There are some things that must be done to successfully implement a localized application, and the following list highlights the most important rules: • Never use string literals in the code. • Never set static string values to any interface elements in a form. • Always build the default (fallback) string resource first, and then add additional locales as needed. • Always set the string values for the form and controls from the string resource. • When adding locales, store them in subdirectories that are named after the locale. The resources can be stored in satellite assemblies, as we have seen in this chapter, or they can be added to a strongly-named assembly that is added to the GAC. The naming of the resources is very important. The Resource Manager uses the names to
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
PART II
To set the script direction, the element that needs the direction specified must have the dir attribute set to rtl, as in the following Hebrew text inserted in the div element:
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
10 find the requested locale: the format is
..txt for the strings, ..resources for the resource, and finally .resources.dll for the assembly. The Resource Manager is very unforgiving when it comes to errors in the resources. Any error will be ignored, and the Resource Manager will use the fallback (default) without any indication of the error having occurred.
Summary In this chapter you were introduced to the world of localization, and you used string resources to turn our Hello World program into something that works all over the world. You were also introduced to the RightToLeft script direction, as well as the encoding needed to send 16-bit Unicode characters over the 8-bit Internet by using the UTF-8 encoding. The need for localization will only grow as we continue towards a more integrated world where software, and especially the Internet, will be global products working in the culture of the client. The next step in preparing for the C# exams is to look at how our code can be used to generate its own documentation in XML format.
Test Questions 1. What is the code for the German language? A. ge B. gb C. de D. dk 2. What namespace contains the ResourceManager class? A. System.Localization B. System.Resources C. System.Globalization D. System.Threading 3. What is the process called that converts a primitive to a class? A. Primary B. Boxing C. Conversion D. Encoding 4. Which of the following code segments will correctly display the string resource txtHello? (All objects are correctly created.)
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
11 A. Console.Write(rm.ToString("txtHello"); B. Console.WriteLine(rm.Strings("txtHello"); C. Console.Write(txtHello.ToString("s")); D. Console.Write(rm.GetString("txtHello")); 5. What does the following command do? A. Builds only the HelloGlobalWorld program. B. Builds the HelloGlobalWorld and links the fallback resource. C. Creates an assembly for the HelloGlobalWorld program. D. Creates a name resolution report for the HelloGlobalWorld program. 6. When localizing a web application, you find that you need to encode Unicode characters that are sent to the client. What attribute would you set? A. ResponseEncoding="UTF-8" B. Encoding="UTF-8" C. ResponseCode="UTF-8" D. EncodedResponse="UTF-8" 7. What happens when the Resource Manager fails to find the localized resource for a locale? A. It uses the closest locale. B. It throws an exception. C. Nothing, the resource will be blank. D. It uses the fallback resource. 8. What namespace contains the CultureInfo class? A. System.Localization B. System.Resources C. System.Globalization D. System.Threading 9. Do you have to produce all the locale-specific assemblies before deploying the application? A. Yes, the assemblies must be present for the final compile of the application. B. Yes, the fallback manifest must be built from all the satellite assemblies. C. Yes, the .NET Framework must update the registry with all the information at deployment. D. No, the satellite assemblies can be deployed at will after initial deployment.
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
PART II
csc /res:strings.resources HelloGlobalWorld.cs
Color profile: Generic CMYK printer profile Composite Default screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7 All-In-One
MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide
12 10. In the following code segment, what is the significance of the "Strings" literal? static ResourceManager rm = new ResourceManager("Strings", Assembly.GetExecutingAssembly());
A. Arbitrary name for the assembly. B. The base name of the resource to be loaded. C. The base name of the assembly to be loaded. D. Alias for the Resource Manager. 11. Where in the object model is the information relating to the date format stored for a specific locale? A. ResourceManager B. CultureInfo C. LocalFormat D. Reflection 12. Your application is called AccountingOne.exe. What must the name of the French string resource be? A. AccountingOne.resources.dll B. strings.resources C. strings.fr.resources.dll D. strings.fr.resources 13. What does the attribute dir="rtf" stand for? A. The direction of RTF files. B. The encoding of RTF files. C. The direction for the display of characters. D. A directory listing of all RTF files. 14. Your application is called AccountingOne.exe. What must the name of the satellite assemblies be? A. Accountingone.resources.dll B. accounting.Resources.dll C. AccountingOne.resources.dll D. Accounting.resources.dll
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
Color profile: Generic CMYK printer profile Composite DefaultAll-In-One screen / MCAD/MCSD Visual C# .NET Certification All-in-One Exam Guide / Rempel & Lind / 222443-6 / Chapter 7
Chapter 7: Resources and Localization
13 Test Answers 1. C. 2. B. 3. B. 4. D. 6. A. 7. D. 8. C. 9. D. 10. B. 11. B. 12. D. 13. C. 14. C.
P:\010Comp\All-in-1\443-6\ch07.vp Monday, August 26, 2002 12:11:24 PM
PART II
5. B.