Namespace System.Globalization


The System.Globalization namespace holds all culture and region classes to support different date formats, different number formats, and even different calendars that are represented in classes such as GregorianCalendar, HebrewCalendar, JapaneseCalendar, and so on. By using these classes, you can display different representations depending on the user’s locale.

This section looks at the following issues and considerations with using the System.Globalization namespace:

  • Unicode issues

  • Cultures and regions

  • An example showing all cultures and their characteristics

  • Sorting

Unicode Issues

A Unicode character has 16 bits, so there is room for 65,536 characters. Is this enough for all languages currently used in information technology? In the case of the Chinese language, for example, more than 80,000 characters are needed. However, Unicode has been designed to deal with this issue. With Unicode you have to differentiate between base characters and combining characters. You can add multiple combining characters to a base character to build up a single display character or a text element.

Take, for example, the Icelandic character Ogonek. Ogonek can be combined by using the base character 0x006F (Latin small letter o) and the combining characters 0x0328 (combining Ogonek) and 0x0304 (combining Macron) as shown in Figure 20-1. Combining characters are defined within ranges from 0x0300 to 0x0345. For American and European markets, predefined characters exist to facilitate dealing with special characters. The character Ogonek is also defined with the predefined character 0x01ED.

image from book
Figure 20-1

For Asian markets, where more than 80,000 characters are necessary for Chinese alone, such predefined characters do not exist. In the case of Asian languages, you always have to deal with combining characters. The problem with this issue is getting the right number of display characters or text elements, and getting to the base characters instead of the combined characters. The namespace System.Globalization offers the class StringInfo, which you can use to deal with this issue.

The following table lists the static methods of the class StringInfo that help in dealing with combined characters.

Open table as spreadsheet

Method

Description

GetNextTextElement

Returns the first text element (base character and all combining characters) of a specified string

GetTextElementEnumerator

Returns a TextElementEnumerator object that allows iterating all text elements of a string

ParseCombiningCharacters

Returns an integer array referencing all base characters of a string

Important 

A single display character can contain multiple Unicode characters. To address this issue, if you write applications that support international markets, don’t use the data type char; use string instead. A string can hold a text element that contains both base characters and combining characters, whereas a char cannot.

Cultures and Regions

The world is divided into multiple cultures and regions, and applications have to be aware of these cultural and regional differences. A culture is a set of preferences based on a user’s language and cultural habits. RFC 1766 defines culture names that are used worldwide depending on a language and a country or region. Some examples are en-AU, en-CA, en-GB, and en-US for the English language in Australia, Canada, United Kingdom, and the United States, respectively.

Possibly the most important class in the System.Globalization namespace is CultureInfo. CultureInfo represents a culture and defines calendars, formatting of numbers and dates, and sorting strings used with the culture.

The class RegionInfo represents regional settings (such as the currency) and shows whether the region is using the metric system. Some regions can use multiple languages. One example is the region of Spain that has Basque (eu-ES), Catalan (ca-ES), Spanish (es-ES), and Galician (gl-ES) cultures. Similarly to one region having multiple languages, one language can be spoken in different regions; for example, Spanish is spoken in Mexico, Spain, Guatemala, Argentina, and Peru, to name only a few countries.

Later in this chapter, you see a sample application that demonstrates these characteristics of cultures and regions.

Specific, Neutral, and Invariant Cultures

With the use of cultures in.NET Framework, you have to differentiate between three types: specific, neutral, and invariant cultures.

A specific culture is associated with a real, existing culture defined with RFC 1766, as you saw in the preceding section. A specific culture can be mapped to a neutral culture. For example, de is the neutral culture of the specific cultures de-AT, de-DE, de-CH, and others. de is the shorthand for the language German; AT, DE, CH are shorthand for the countries Austria, Germany, and Switzerland.

When translating applications, it is typically not necessary to do translations for every region; not much difference exists between the German language in the countries Austria and Germany. Instead of using specific cultures, you can use a neutral culture for localizing applications.

The invariant culture is independent of a real culture. When storing formatted numbers or dates in files, or sending them across a network to a server, using a culture that is independent of any user settings is the best option.

Figure 20-2 shows how the culture types relate to each other.

image from book
Figure 20-2

CurrentCulture and CurrentUICulture

When you set cultures, you have to differentiate between a culture for the user interface and a culture for the number and date formats. Cultures are associated with a thread, and with these two culture types, two culture settings can be applied to a thread. The Thread class has the properties CurrentCulture and 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.

Users can change the default setting of the CurrentCulture by using the Regional and Language options in the Windows Control Panel (see Figure 20-3). With this configuration, it is also possible to change the default number, the time, and the date format for the culture.

image from book
Figure 20-3

The CurrentUICulture does not depend on this configuration. The CurrentUICulture setting depends on the language of the operating system. There is one exception, though: If a multilanguage user interface (MUI) is installed with Windows Vista or Windows XP, it is possible to change the language of the user interface with the regional configuration, and this influences the property CurrentUICulture.

These settings make a very good default, and in many cases, there is no need to change the default behavior. If the culture should be changed, you can easily do this by changing both cultures of the thread to, say, the Spanish culture, as shown in this code snippet:

  System.Globalization.CultureInfo ci = new    System.Globalization.CultureInfo("es-ES"); System.Threading.Thread.CurrentThread.CurrentCulture = ci; System.Threading.Thread.CurrentThread.CurrentUICulture = ci; 

Now that you know about setting the culture, the following sections discuss number and date formatting, which are influenced by the CurrentCulture setting.

Number Formatting

The number structures Int16, Int32, Int64, and so on in the System namespace have an overloaded ToString() method. This method can be used to create a different representation of the number depending on the locale. For the Int32 structure, ToString() is overloaded with these four versions:

  public string ToString(); public string ToString(IFormatProvider); public string ToString(string); public string ToString(string, IFormatProvider); 

ToString() without arguments returns a string without format options. You can also pass a string and a class that implements IFormatProvider.

The string specifies the format of the representation. The format can be a standard numeric formatting string or a picture numeric formatting string. For standard numeric formatting, strings are predefined, where C specifies the currency notation, D creates a decimal output, E creates scientific output, F creates fixed-point output, G creates general output, N creates number output, and X creates hexadecimal output. With a picture numeric format string, it is possible to specify the number of digits, section and group separators, percent notation, and so on. The picture numeric format string ###,### means two 3-digit blocks separated by a group separator.

The IFormatProvider interface is implemented by the NumberFormatInfo, DateTimeFormatInfo, and CultureInfo classes. This interface defines a single method, GetFormat(), that returns a format object.

NumberFormatInfo can be used to define custom formats for numbers. With the default constructor of NumberFormatInfo, a culture-independent or invariant object is created. Using the properties of NumberFormatInfo, it is possible to change all the formatting options such as a positive sign, a percent symbol, a number group separator, a currency symbol, and a lot more. A read-only culture-independent NumberFormatInfo object is returned from the static property InvariantInfo. A NumberFormatInfo object where the format values are based on the CultureInfo of the current thread is returned from the static property CurrentInfo.

To create the next example you can start with a simple console project. In this code, the first example shows a number displayed in the format of the culture of the thread (here: English-US, the setting of the operating system). The second example uses the ToString() method with the IFormatProvider argument. CultureInfo implements IFormatProvider, so create a CultureInfo object using the French culture. The third example changes the culture of the thread. The culture is changed to German using the property CurrentCulture of the Thread instance:

  using System; using System.Globalization; using System.Threading; namespace Wrox.ProCSharp.Localization {    class Program    {       static void Main()       {          int val = 1234567890;          // culture of the current thread          Console.WriteLine(val.ToString("N"));          // use IFormatProvider          Console.WriteLine(val.ToString("N",                            new CultureInfo("fr-FR")));          // change the culture of the thread          Thread.CurrentThread.CurrentCulture =                            new CultureInfo("de-DE");          Console.WriteLine(val.ToString("N"));       }    } } 

The output is shown in Figure 20-4. You can compare the outputs with the previously listed differences for U.S. English, French, and German.

image from book
Figure 20-4

Date Formatting

The same support for numbers is available for dates. The DateTime structure has some methods for date-to-string conversions. The public instance methods ToLongDateString(), ToLongTimeString(), ToShortDateString(), and ToShortTimeString() create string representations using the current culture. You can use the ToString() method to assign a different culture:

  public string ToString(); public string ToString(IFormatProvider); public string ToString(string); public string ToString(string, IFormatProvider); 

With the string argument of the ToString() method, you can specify a predefined format character or a custom format string for converting the date to a string. The class DateTimeFormatInfo specifies the possible values. With the IFormatProvider argument, you can specify the culture. Using an overloaded method without the IFormatProvider argument implies that the culture of the current thread is used:

  DateTime d = new DateTime(2007, 02, 14); // current culture Console.WriteLine(d.ToLongDateString()); // use IFormatProvider Console.WriteLine(d.ToString("D", new CultureInfo("fr-FR"))); // use culture of thread CultureInfo ci = Thread.CurrentThread.CurrentCulture; Console.WriteLine(ci.ToString() + ": " + d.ToString("D")); ci = new CultureInfo("es-ES"); Thread.CurrentThread.CurrentCulture = ci; Console.WriteLine(ci.ToString() + ": " + d.ToString("D")); 

The output of this example program shows ToLongDateString() with the current culture of the thread, a French version where a CultureInfo instance is passed to the ToString() method, and a Spanish version where the CurrentCulture property of the thread is changed to es-ES (see Figure 20-5).

image from book
Figure 20-5

Cultures in Action

To see all cultures in action, you can use a sample Windows Forms application that lists all cultures and demonstrates different characteristics of culture properties. Figure 20-6 shows the user interface of the application in the Visual Studio 2005 Forms Designer.

image from book
Figure 20-6

During initialization of the application, all available cultures are added to the tree view control that is placed on the left side of the application. This initialization happens in the method AddCulturesToTree() that is called in the constructor of the form class CultureDemoForm:

 public CultureDemoForm() {    InitializeComponent();    AddCulturesToTree(); }

In the method AddCulturesToTree(), you get all cultures from the static method CultureInfo .GetCultures(). Passing CultureTypes.AllCultures to this method returns an unsorted array of all available cultures. The array is sorted using an anonymous method that is passed to the Comparison delegate of the second argument of the Array.Sort() method. Next in the foreach loop every single culture is added to the tree view. A TreeNode object is created for every single culture, because the TreeView class uses TreeNode objects for display. The Tag property of the TreeNode object is set to the CultureInfo object, so that you can access the CultureInfo object at a later time from within the tree.

Where the TreeNode is added inside the tree depends on the culture type. If the culture is a neutral culture or an invariant culture, it is added to the root nodes of the tree. TreeNodes that represent specific cultures are added to their parent neutral culture node:

  // add all cultures to the tree view public void AddCulturesToTree() {    // get all cultures    CultureInfo[] cultures =       CultureInfo.GetCultures(CultureTypes.AllAvailableCultures);    Array.Sort(cultures,          delegate(CultureInfo c1, CultureInfo c2)          {             return c1.Name.CompareTo(c2.Name);          });    TreeNode[] nodes = new TreeNode[cultures.Length];    int i = 0;    TreeNode parent = null;    foreach (CultureInfo ci in cultures)    {       nodes[i] = new TreeNode();       nodes[i].Text = ci.DisplayName;       nodes[i].Tag = ci;       if (ci.IsNeutralCulture)       {          // remember neutral cultures as parent of the          // following cultures          parent = nodes[i];          treeCultures.Nodes.Add(nodes[i]);       }       else if (ci.ThreeLetterISOLanguageName ==          CultureInfo.InvariantCulture.ThreeLetterISOLanguageName)       {          // invariant cultures don't have a parent          treeCultures.Nodes.Add(nodes[i]);       }       else       {          // specific cultures are added to the neutral parent          parent.Nodes.Add(nodes[i]);       }       i++;    } } 

When the user selects a node inside the tree, the handler of the AfterSelect event of the TreeView will be called. Here the handler is implemented in the method OnSelectCulture(). Within this method all fields are cleared by calling the method ClearTextFields() before you get the CultureInfo object from the tree by selecting the Tag property of the TreeNode. Then some text fields are set using the properties Name, NativeName, and EnglishName of the CultureInfo object. If the CultureInfo is a neutral culture that can be queried with the IsNeutralCulture property, the corresponding check box will be set:

  private void OnSelectCulture(object sender,                 System.Windows.Forms.TreeViewEventArgs e) {    ClearTextFields();    // get CultureInfo object from tree    CultureInfo ci = (CultureInfo)e.Node.Tag;    textName.Text = ci.Name;    textNativeName.Text = ci.NativeName;    textEnglishName.Text = ci.EnglishName;    checkIsNeutral.Checked = ci.IsNeutralCulture; 

Then you get the calendar information about the culture. The Calendar property of the CultureInfo class returns the default Calendar object for the specific culture. Because the Calender class doesn’t have a property to tell its name, you use the ToString() method of the base class to get the name of the class, and remove the namespace of this string to be displayed in the text field textCalendar.

Because a single culture might support multiple calendars, the OptionalCalendars property returns an array of additional supported Calendar objects. These optional calendars are displayed in the list box listCalendars. The GregorianCalendar class that derives from Calendar has an additional property called CalendarType that lists the type of the Gregorian calendar. This type can be a value of the enumeration GregorianCalendarTypes: Arabic, MiddleEastFrench, TransliteratedFrench, USEnglish, or Localized depending on the culture. With Gregorian calendars, the type is also displayed in the list box:

  // default calendar textCalendar.Text = ci.Calendar.ToString().       Remove(0, 21).Replace("Calendar", ""); // fill optional calendars listCalendars.Items.Clear(); foreach (Calendar optCal in ci.OptionalCalendars) {    StringBuilder calName = new StringBuilder(50);    calName.Append(optCal.ToString());    calName.Remove(0, 21);    calName.Replace("Calendar", "");    // for GregorianCalendar add type information    if (optCal is System.Globalization.GregorianCalendar)    {       GregorianCalendar gregCal = optCal as GregorianCalendar;       calName.AppendFormat(" {0}", gregCal.CalendarType.ToString());    }    listCalendars.Items.Add(calName.ToString()); } 

Next, you check whether the culture is a specific culture (not a neutral culture) by using !ci .IsNeutralCulture in an if statement. The method ShowSamples() displays number and date samples. This method is implemented in the next code section. The method ShowRegionInformation() is used to display some information about the region. With the invariant culture, you can only display number and date samples, but no region information. The invariant culture is not related to any real language, and therefore it is not associated with a region:

     // display number and date samples    if (!ci.IsNeutralCulture)    {       groupSamples.Enabled = true;       ShowSamples(ci);       // invariant culture doesn't have a region       if (ci.ThreeLetterISOLanguageName == "IVL")       {          groupRegionInformation.Enabled = false;       }       else       {          groupRegionInformation.Enabled = true;          ShowRegionInformation(ci.Name);       }    }    else // neutral culture: no region, no number/date formatting    {       groupSamples.Enabled = false;       groupRegionInformation.Enabled = false;    } } 

To show some localized sample numbers and dates, the selected object of type CultureInfo is passed with the IFormatProvider argument of the ToString() method:

  private void ShowSamples(CultureInfo ci) {    double number = 9876543.21;    textSampleNumber.Text = number.ToString("N", ci);    DateTime today = DateTime.Today;    textSampleDate.Text = today.ToString("D", ci);    DateTime now = DateTime.Now;    textSampleTime.Text = now.ToString("T", ci); } 

To display the information associated with a RegionInfo object, in the method ShowRegionInformation() a RegionInfo object is constructed passing the selected culture identifier.

Then you access the properties DisplayName, CurrencySymbol, ISOCurrencySymbol, and IsMetric properties to display this information:

  private void ShowRegionInformation(string culture) {    RegionInfo ri = new RegionInfo(culture);    textRegionName.Text = ri.DisplayName;    textCurrency.Text = ri.CurrencySymbol;    textCurrencyName.Text = ri.ISOCurrencySymbol;    checkIsMetric.Checked = ri.IsMetric; } 

When you start the application, you can see all available cultures in the tree view, and selecting a culture lists the cultural characteristics, as shown in Figure 20-7.

image from book
Figure 20-7

Sorting

Sorting strings is dependent on the culture. Some cultures have different sorting orders. One example is Finnish, where the characters V and W are treated the same. The algorithms that compare strings for sorting by default use a culture-sensitive sort, where the sort is dependent on the culture.

To demonstrate this behavior with a Finnish sort, the following code creates a small sample console application where some U.S. states are stored unsorted inside an array. You are going to use classes from the namespaces System.Collections.Generic, System.Threading, and System.Globalization, so these namespaces must be declared. The method DisplayNames() shown here is used to display all elements of an array or of a collection on the console:

  static void DisplayNames(string title, IEnumerable<string> e) {    Console.WriteLine(title);    foreach (string s in e)       Console.Write(s + " - ");    Console.WriteLine();    Console.WriteLine(); } 

In the Main() method, after creating the array with some of the U.S. states, the thread property CurrentCulture is set to the Finnish culture, so that the following Array.Sort() uses the Finnish sort order. Calling the method DisplayNames() displays all the states on the console:

  static void Main() {    string[] names = {"Alabama", "Texas", "Washington",                      "Virginia", "Wisconsin", "Wyoming",                      "Kentucky", "Missouri", "Utah", "Hawaii",                      "Kansas", "Lousiana", "Alaska", "Arizona"};    Thread.CurrentThread.CurrentCulture =       new CultureInfo("fi-FI");    Array.Sort(names);    DisplayNames("Sorted using the Finnish culture", names); 

After the first display of some U.S. states in the Finnish sort order, the array is sorted once again. If you want to have a sort that is independent of the users’ culture, which would be useful when the sorted array is sent to a server or stored somewhere, you can use the invariant culture.

You can do this by passing a second argument to Array.Sort(). The Sort() method expects an object implementing IComparer with the second argument. The Comparer class from the System.Collections namespace implements IComparer. Comparer.DefaultInvariant returns a Comparer object that uses the invariant culture for comparing the array values for a culture-independent sort:

     // sort using the invariant culture    Array.Sort(names, System.Collections.Comparer.DefaultInvariant);    DisplayNames("Sorted using the invariant culture", names); } 

Figure 20-8 shows the output of this program: a sort with the Finnish culture and a culture-independent order and Virginia goes before Washington when using the invariant sort order.

image from book
Figure 20-8

Important 

If sorting a collection should be independent of a culture, the collection must be sorted with the invariant culture. This can be particularly useful when sending the sort result to a server or storing it inside a file.

In addition to a locale-dependent formatting and measurement system, text and pictures may differ depending on the culture. This is where resources come into play.




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

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