Culture


As our first and most important step, we'll examine cultures. A culture represents a language optionally combined with a geographical or political location. This combination is often referred to synonymously as a locale (e.g., in Win32 and Java). A user of an internationalized application will ordinarily have a single preferred culture, which is used to customize presented data based on regional, cultural, and language implications of such a preference. For example, if your user lives and works in France, it's likely he or she would appreciate a UI whose text was presented in French. Likewise, for somebody who is inputting text in Arabic, it would be nice if your application were sensitive to the fact that Arabic is written from right to left, and indeed probably requires some customized string logic as a result. If you ship reusable APIs, international users will likely expect culture-specific switches where appropriate, and similarly to receive exception text (and documentation) in the local language.

Many applications are written regardless of culture preferences, leading to poor user experiences for international users. Some code may even function improperly should it be run with culture preferences different than those the application was originally developed and tested with. Thus, even if you don't intend to localize your program for specific cultures, it's still a worthwhile exercise to test aggressively for international-specific problems. This will reduce the chance that your program crashes or exposes reliability and/or security holes when running on a foreign platform. We will discuss some examples of this later in this section.

Representing Cultures (CultureInfo)

The System.Globalization.CultureInfo class is the center of the .NET Framework's hub and spoke globalization model. As mentioned above, it represents an instance of a particular language and (optionally) location combination. Such a combination is represented textually by a culture code. Culture codes are based on the ISO-639, Code for the representation of names of languages and ISO-3166, Codes for country and dependent area names international standards, which define language and country codes, respectively.

The country code part of a culture code is optional because frequently an application will only deliver localized content based on language — dealing with all possible combinations of both language and region is not feasible, although a select few are ordinarily of interest. (The realities of languages broadly spoken in certain geographical regions limit the number of valid permutations, but it is still a daunting number.) A culture that does not have a country preference is called neutral, whereas one with both language and region is called specific.

There are many culture-sensitive APIs in the .NET Framework that will automatically adapt behavior based on the user's current culture. We saw a few examples above. This can be surprising to many developers when running their applications on international platforms, especially if they're not familiar with the Framework's internationalization features. These include the primitive-to-string and string-to-primitive operations, that is, ToString and Parse. You might be storing en-US-formatted currency, dates and times, and numbers in a database, for example, and then run into problems when your application automatically tries to work with such information when run on a machine in another country (with machine culture settings you'd never tested on).

As an example of a valid culture code, the Spanish language is represented by the ISO-639 language code es, and the country Spain by the two-character ISO-3166 code ES. Notice that, by convention, language codes are lowercase and country codes are uppercase, as defined in the respective ISO standards. We could create a CultureInfo based on just the language code es, in which case it would be considered neutral. But to represent the specific culture of the Spanish language in the country Spain, we would combine these codes together to create the culture code es-ES. This would be different from a specific culture that, say, had a language of Spanish and location of the Dominican Republic, the culture code for which would be es-DO. It's likely that an application might not appreciate the difference between es-ES and es-DO, instead customizing simply based on language.

Some additional examples of neutral and specific cultures are shown in Figure 8-2 toward the beginning of this chapter. Figure 8-2 shows a larger number of neutral cultures (although still only a very small subset of those which are available):

There are a multiple ways to obtain a reference to a CultureInfo object for a desired culture. You can obtain the current client's culture based on their Windows configuration and preferences, ask for a specific culture by code, enumerate over a list of cultures that are available, or even create your own custom culture.

image from book
Figure 8-2: Tree of sample neutral cultures.

Asking for a Specific Culture

To create an instance of a CultureInfo based on a culture code, you can use the static CultureInfo.GetCultureInfo method. This is useful, for example, when customizing functionality in an application, where it's likely that you'd store a user's preferences in a database — or perhaps read it from the web browser's agent string — which means that you'll have to take a raw string value and produce a CultureInfo object from it. You pass in <language>-<country code> string to this method, and a fully constructed and configured CultureInfo object is returned. Alternatively, you can use one of CultureInfo's constructors. The benefit of the static method is that it will often just reuse a cached version of the culture instead of having to allocate a new one.

The following snippet of code demonstrates how to generate CultureInfo instances for a few of the cultures mentioned above:

 CultureInfo cultureEnNeutral = CultureInfo.GetCultureInfo("en"); CultureInfo cultureEnGb = CultureInfo.GetCultureInfo("en-GB"); CultureInfo cultureEnUs = CultureInfo.GetCultureInfo("en-US"); 

When using one of CultureInfo's APIs that takes as input a culture code, you are responsible for ensuring it is formatted correctly. If the code was formatted incorrectly or culture support is not available for the supplied code, an ArgumentException will be generated. If the string appears to be formatted correctly, yet an exception is still generated stating that a culture doesn't exist, language pack support likely hasn't been installed on the client. (This is often the case with Asian languages that require specific language pack support.)

Enumerating through Available Cultures

The static CultureInfo.GetCultures method is useful for enumerating over the set of installed cultures on the current machine. This can be used to prompt a user to select a supported culture, for example, either in a client or web application. You just pass a CultureType enumeration value to the method to specify the type of cultures to retrieve. As with GetCultureInfo, the data returned is dependent on what language packs have been installed on the OS running the code.

 CultureInfo[] neutralCultures =     CultureInfo.GetCultures(CultureTypes.NeutralCultures); for (int i = 0; i < neutralCultures.Length; i++) {     CultureInfo culture = neutralCultures[i];     Console.WriteLine("{0}: {1} [{2}]", i, culture.DisplayName, culture.Name); } 

This example uses the CultureTypes.NeutralCultures enumeration value in order to retrieve all of the neutral cultures supported on the machine. There is also a CultureTypes.SpecificCultures value, which will return a list of the specific cultures on the local system. Other enumeration values are available, such as CultureTypes.AllCultures, which obtains the union of the neutral and specific cultures.

Managing Culture Context

By default, programs inherit the culture preferences from the operating system preferences of the current user profile. You can manage these through the Windows Control Panel, under the Regional and Language Options menu.

Each thread has a culture that is used by code executing on that thread. For most client programs, this setting won't differ from thread to thread running inside a program. But using diverse cultures on threads inside your application enables you to run independent fragments of code in with different culture settings, which can prove useful in server-side scenarios. In such cases, multiple users are typically being served simultaneously and might have cultural preferences that must be recognized. Simply changing the thread-wide culture is a way to accommodate this requirement, enabling you to use the default formatting behaviors in the Framework instead of having to maintain a CultureInfo instance in session state, for example.

Formatting Culture (CurrentCulture)

The thread-wide formatting culture for the currently executing thread can be accessed through the static read-only property CultureInfo.CurrentCulture. (This is also available through the Thread.CurrentThread.CurrentCulture property.) This is where most Framework APIs look for culture information when performing formatting tasks:

 using System.Globalization; //... CultureInfo culture = CultureInfo.CurrentCulture; 

For example, the ToString and Parse methods on the native data types use this to interpret textual representations appropriately. This affects how dates, times, numbers, currency are formatted, as well as the way in which string comparisons and collation happens. It is explicitly not used by the resources system for language and localization purposes (CurrentUICulture is used instead, as discussed below).

Because its function is limited to region-based globalization, it is required that your formatting culture be specific — that is, contains both a language and country setting. Possible ambiguities would arise were you only to supply neutral culture. Take the neutral culture en, for example: English is spoken in a number of countries, including the United States and some countries in Europe. But European formatting rules for many countries differs from the United States, and indeed even from country to country. Assuming the formatting rules of any particular country based on your en culture would be incorrect.

Sometimes this is not a worry, as is the case with Japanese (which, although spoken outside of Japan, is ubiquitous only in Japan itself). If you want to obtain the default country setting for a given language, you can pass the language code to the static CultureInfo.CreateSpecificCulture method. For example, to obtain the default culture for en:

 CultureInfo culture = CultureInfo.CreateSpecificCulture("en"); 

On my system, this line of code gives me the en-US culture. Realize that, for reasons outlined above, this isn't always what you want.

UI Culture (CurrentUICulture)

There is a separate thread-wide culture, CultureInfo.CurrentUICulture. (This can also be retrieved via the Thread.CurrentThread.CurrentUICulture property.) The UI culture is used by the resource system when deciding the appropriate localized version of content to retrieve. It has no impact on regional formatting but instead is used for text and media (and anything that can be stored inside a resource package). We discuss the .NET Framework's resources subsystem in depth below.

In most circumstances, the CurrentCulture and CurrentUICulture will hold identical values, although you can certainly use separate cultures for language and regional formatting. If you override the default settings on one but not the other, however, you should consider the implications of having a difference. In most circumstances, you should keep them identical.

Because CurrentUICulture is generally meant for language-only, neutral cultures are sufficient. Specific cultures are permitted.

Installation and User-Default Culture

When the OS is installed, a system-wide culture is established. Somebody installing the Japanese SKU of Windows XP, for example, will have a system-wide default of Japanese. This will be used as the default culture for all threads unless overridden by the user. The CultureInfo class exposes this property to managed code for inspection with the InstalledUICulture property. This simply makes a call through to the Win32 function GetSystemDefaultLCID and wraps it in a CultureInfo.

As noted in the introduction to this section, a user may override this system-wide default using the Control Panel's Regional and Language Options menu. This is called the user default culture. If the user has set a user default, the OS ensures that it gets stamped on each new thread created. And, of course, managed code just uses this default. CultureInfo does not expose these settings directly (there are internal-only methods to retrieve them), although you can P/Invoke to GetUserDefaultLCID and GetUserDefaultLangID functions to get the formatting and UI culture, respectively.

Changing Culture Values

As noted, the System.Threading.Thread class also has CultureInfo and CurrentUICulture properties. These are in fact the same underlying value. The CultureInfo property getters actually just call through to the Thread properties to retrieve the information. But typically for internationalized applications you will have already imported System.Globalization, so having the information on CultureInfo is certainly useful. The setters for both properties make a security demand for ControlThread, so untrusted code isn't able to abuse them. For more information on code access security (CAS), see Chapter 9.

The primary difference between these two locations, however, is that Thread's properties are settable, whereas CultureInfo's are read-only. For example, if you want to change the current thread's culture to Spanish in Mexico (es-MX), you could use the following snippet of code:

 CultureInfo mexicanSpanishCi = CultureInfo.GetCultureInfo("es-MX"); Thread.CurrentThread.CurrentCulture = mexicanSpanishCi; Thread.CurrentThread.CurrentUICulture = mexicanSpanishCi; 

All of the default formatting and localization behavior would, from this point forward, use Mexican Spanish. This is useful, for example, when loading culture preferences from a configuration file or database record associated with the user. But beware of this practice. When a thread is tagged with a specific culture, it maintains that culture until you explicitly put it back. In the worst case, it might end up being reused for other tasks for other users (for example, in a web application). If it does, it'll start out with whatever culture you left on it. Worse, if you were to change the culture on a thread-pool thread or the sole finalizer thread, for example, a array of weird and impossible-to-debug problems are likely to arise.

Invariant Culture

The invariant culture is a special culture used ordinarily for suppressing international-specific behavior. It sits at the culture hierarchy and, thus, explicitly specifies "no cultural preference." While it's seldom something that a user would elect as a cultural preference, it is used frequently in applications that have not been tested for international behavior. For example, if you pass it to the various Parse and ToString methods (mentioned numerous times thus far), the APIs will use their "default" logic.

The static property, CultureInfo.InvariantCulture, is the invariant culture object singleton. The invariant culture doesn't have a culture code:

 CultureInfo invariant = CultureInfo.InvariantCulture; 

As mentioned above, the invariant culture can be supplied to APIs that expect either a culture or IFormatProvider, or those that automatically perform some culture-sensitive formatting or specialized logic that you wish to suppress. For example, the DateTime.ToString method will automatically format the return string using the CultureInfo.CurrentCulture property. To format the string using the invariant culture, you can pass it as an argument to ToString:

 DateTime now = DateTime.Now; Console.WriteLine(now.ToString(CultureInfo.InvariantCulture)); 

Why would you want to do this? Well, it makes such calls behave identical across all localized platforms. If there were a bug that occurred as a result of your date swapping the month and day fields — for example, some whacky custom date-parsing code — an international date format might cause it to crash. Of course, in most cases not using the whacky custom date parsing code is probably the better answer, but sometimes you don't have control over it, for example if it's located in a third-party library.

In some circumstances it's actually dangerous to accept the default culture-sensitive behavior. Malicious users can sometimes find new and interesting bugs in your software simply by changing their Windows culture settings or manually changing the thread cultures before calling into your code. If you've not tested localizability sufficiently, you might be inadvertently opening security or reliability holes, or you might just be shipping your software with a large quantity of hard-to-reproduce bugs that affect only international users. The .NET Framework itself found two such security holes, one after shipping 1.0 and another just prior to shipping 2.0.

Formatting

Cultures serve two primary purposes: formatting and delivering localized content. This focus of this section is on the formatting portion of this responsibility. While completely understanding the capabilities of all of these types isn't necessary to build simple internationalized applications — that is, most of it "just works" — you will sometimes have to interact with the formatting system directly to either invoke or suppress such behavior.

Note

There is a growing sentiment among both the designers of the .NET Framework and its users that, in retrospect, the "internationalization by default" policy as implemented today is too liberal. It leads to testing responsibility that most users don't understand or even know about. The platform's solution to this problem is a growing tendency to provide APIs biased toward invariant cultures, enabling culture-specific behavior via API parameters. It's likely the platform will evolve further in this direction in future releases.

The common type used to represent formatting specifications is the System.IFormatProvider interface. CultureInfo implements IFormatProvider, as do types such as DateTimeFormatInfo and NumberFormatInfo. Most of the formatting functions described in this section rely on these types in order to do their jobs. These types were discussed in relation to the native data types in Chapter 5 in the sections on dates and times. Other types, such as Calendar for dates and times, are used by the format info classes in order to perform the appropriate transformations




Professional. NET Framework 2.0
Professional .NET Framework 2.0 (Programmer to Programmer)
ISBN: 0764571354
EAN: 2147483647
Year: N/A
Pages: 116
Authors: Joe Duffy

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