Globalisation in the .NET framework
Lesson 1: Using Culture Information
The System.Globalization namespace helps developers address globalisation concerns.
CultureInfo Class
Core tool for manipulating and retrieving information about cultural context in which an application is executing. It represents the culture name, its writing system, calendar, language and sub language (if applicable), country and region of culture together with methods to manipulate these aspects. Controls how:
- string comparisons are performed
- number comparisons are performed
- date comparisons are performed
- how resources are retrieved and used
Cultures are represented using the Neutral Culture - Specific Culture format, e.g. en-GB. Only exceptions are the neutral cultures zh-CHS and zh-CHT (simplified and traditional Chinese).
Culture grouped into one of three categories:
- invariant - culture insensitive. Used as a default culture when consistency is desired. One situation where desirable is when creating trial application with hard coded expiration date. Using invariant allows you to check for a specific date, regardless of cultures format - greatly simplifies task of comparing dates. Invariant not based on English, but is associated with it and bears more similarities to it than any other culture. Tempting to use invariant for every comparison, but doing so is a great mistake. Without intending to do so can overuse invariant and end up with language that is syntactically incorrect or inappropriate.
- neutral - English (en), French (fr), Spanish (sp), etc. are all neutral cultures. Associated with language, but no relationship to country or region. As with invariant cultures their use should be avoided (for the same reasons).
- specific culture - represented by neutral culture, a hyphen and then specific culture, e.g. en-US, en-GB, etc. Should be used if at all possible.
To detect users current culture information use the CurrentCulture property of the executing threads CurrentThread property.
CultureInfo usersCulture = Thread.CurrentThread.CurrentCulture;
Console.WriteLine(usersCulture.Name);
Console.WriteLine(usersCulture.NativeName);
Console.WriteLine(usersCulture.TwoLetterISOLanguageName);
CurrentCulture cannot reformat hard coded strings, e.g. a text string of "$100,000.00" will always display as "$100,000.00". Instead of hard coding strings use formatters, e.g.
(100000).ToString("C");
Can also change current culture for thread as follows...
CultureInfo usersCulture = Thread.CurrentThread.CurrentCulture;
// Change to Venezuelan
Thread.CurrentThread.CurrentCulture = new CultureInfo("es-VE");
CultureTypes enumeration
Pass to GetCultures method to get desired CultureInfos. Possible values:
- AllCultures - All available cultures
- FrameworkCultures - Neutral and specific cultures included in framework
- InstalledWin32Cultures - All cultures installed on operating system
- NeutralCultures - Cultures associated with a language, but not a country / region
- ReplacementCultures - Custom cultures created by user that replace those shipped with framework
- SpecificCultures - Cultures specific to country / region
- UserCustomCulture - Custom cultures created by user
- WindowsOnlyCulture - Cultures installed in operating system, but not in the framework.
RegionInfo class
More granular information than that provided by CultureInfo.
CultureInfo Has 2 types of property that allow it work in conjunction with RegionInfo - Name and LCID. e.g.
CultureInfo usersCulture = Thread.CurrentThread.CurrentCulture;
RegionInfo demoRegion = new RegionInfo(usersCulutre.Name);
Then properties can be accessed:
Console.WriteLine(demoRegion.EnglishName);
Console.WriteLine(demoRegion.DisplayName);
Console.WriteLine(demoRegion.CurrencySymbol);
DateTimeFormatInfo and NumberFormatInfo
Provide mechanisms for manipulating how differences between cultures are handled.
For example, to find Venezuelan day names:
CulturInfo userCultrue = new CultureInfo("es-VE")
String[] days = userCulture.DateTimeFormat.DayNames;
foreach(string day in days)
{
Console.WriteLine(day);
}
To determine currency symbol and thousands separator...
CultureInfo userCulture = new CultureInfo("es-VE");
Console.WriteLine(userCulture.NumberFormat.CurrencySymbol);
Console.WriteLine(userCulture.NumberFormat.NumberDecimalSeperator);
Using CompareInfo and CompareOptions for culturally aware comparisons
Comparisons typically based on one culture, e.g. app written in America will probably perform comparisons based on en-US culture. Consequently often best to create CompareInfo object based on current culture. Can also create a new CultureInfo object based on any culture desired, e,g.
CompareInfo demoInfo = new CultureInfo("fr-FR").CompareInfo;
Most basic approach to using CompareInfo class is to call its compare method passing in values to be compared:
String first = "Cote";
String second = "cote";
CompareInfo demonInfo = new CultureInfo("fr-FR").CompareInfo;
demoInfo.Compare(first, second);
As currently stands the above comparison will fail as the strings are of different cases. Can use members of the CompareOptions enumeration to control how comparisons are performed. This enumeration includes options to ignore case, to treat Japanese hiragana and katakana sounds as the same, to ignore white spaces in a string, to ignore symbols in a string, to treat full width and half width versions of the same character as being the same, to perform comparisons based on the ordinal value of the Unicode character, to perform comparisons based on the ordinal value of the Unicode character (ignoring case)
To ignore case in comparisons do the following:
String first = "Cote";
String second = "cote";
CompareInfo demonInfo = new CultureInfo("fr-FR").CompareInfo;
demoInfo.Compare(first, second, CompareOptions.IgnoreCase);
Lesson 2: Creating a Custom Culture
The cultures provided by the .NET framework are not always sufficient. Framework provides mechanism to overcome this - the CultureAndRegionInfoBuilder class. Can create a culture from scratch, base one on an existing culture or use an existing one entirely.
To use an existing culture...
CultueInfo usCulture = new CultureInfo("en-US");
RegionInfo usRegion = new RegionInfo("US);
CultureAndRegionInfoBuilder demoBuilder = new CultureAndRegionInfoBuilder("en-US", CultureAndRegionModifiers.Neutral);
demoBuilder.LoadDataFromCultureInfo(usCulture);
demoBuilder.LoadDataFromRegionInfo(usRegion);
To create customised culture...
CultueInfo usCulture = new CultureInfo("en-US");
RegionInfo usRegion = new RegionInfo("US);
CultureAndRegionInfoBuilder demoBuilder = new CultureAndRegionInfoBuilder("en-MS", CultureAndRegionModifiers.Neutral);
demoBuilder.LoadDataFromCultureInfo(usCulture);
demoBuilder.LoadDataFromRegionInfo(usRegion);
All that has been done above is to create a new culture (en-MS) basing it on the en-US definitions. Afterwards can adjust properties such as CurrencyName, CultureName, NumberFormat, etc.
The second argument to the CultureAndRegionInfoBuilder is a CultureAndRegionModifiers enumeration:
- Neutral - neutral custom culture
- None - specific, supplemental custom culture
- Replacement - replaces an existing framework culture (or Windows locale)
If wanted to use existing culture, but no region specify Neutral. If going to specify supplemental information for an object (e.g. currency, thousand separator) use None to minimise the amount of inherited information. If defining new culture or replacing an existing culture specify Replacement - note all properties must be set by the developer.