summaryrefslogtreecommitdiff
path: root/src/mscorlib/corefx/System/Globalization/CultureData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/corefx/System/Globalization/CultureData.cs')
-rw-r--r--src/mscorlib/corefx/System/Globalization/CultureData.cs2176
1 files changed, 2176 insertions, 0 deletions
diff --git a/src/mscorlib/corefx/System/Globalization/CultureData.cs b/src/mscorlib/corefx/System/Globalization/CultureData.cs
new file mode 100644
index 0000000000..0dd488bcbf
--- /dev/null
+++ b/src/mscorlib/corefx/System/Globalization/CultureData.cs
@@ -0,0 +1,2176 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+
+#if INSIDE_CLR
+ using StringStringDictionary = Dictionary<string, string>;
+ using StringCultureDataDictionary = Dictionary<String, CultureData>;
+ using Lock = Object;
+#else
+ using StringStringDictionary = LowLevelDictionary<string, string>;
+ using StringCultureDataDictionary = LowLevelDictionary<string, CultureData>;
+#endif
+
+ //
+ // List of culture data
+ // Note the we cache overrides.
+ // Note that localized names (resource names) aren't available from here.
+ //
+
+ //
+ // Our names are a tad confusing.
+ //
+ // sWindowsName -- The name that windows thinks this culture is, ie:
+ // en-US if you pass in en-US
+ // de-DE_phoneb if you pass in de-DE_phoneb
+ // fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
+ // fj if you pass in fj (neutral, post-Windows 7 machine)
+ //
+ // sRealName -- The name you used to construct the culture, in pretty form
+ // en-US if you pass in EN-us
+ // en if you pass in en
+ // de-DE_phoneb if you pass in de-DE_phoneb
+ //
+ // sSpecificCulture -- The specific culture for this culture
+ // en-US for en-US
+ // en-US for en
+ // de-DE_phoneb for alt sort
+ // fj-FJ for fj (neutral)
+ //
+ // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
+ // en-US if you pass in en-US
+ // en if you pass in en
+ // de-DE if you pass in de-DE_phoneb
+ //
+ internal partial class CultureData
+ {
+ const int undef = -1;
+
+ // Override flag
+ private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
+ private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
+
+ // Identity
+ private String sName; // locale name (ie: en-us, NO sort info, but could be neutral)
+ private String sParent; // Parent name (which may be a custom locale/culture)
+ private String sLocalizedDisplayName; // Localized pretty name for this locale
+ private String sEnglishDisplayName; // English pretty name for this locale
+ private String sNativeDisplayName; // Native pretty name for this locale
+ private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
+
+ // Language
+ private String sISO639Language; // ISO 639 Language Name
+ private String sLocalizedLanguage; // Localized name for this language
+ private String sEnglishLanguage; // English name for this language
+ private String sNativeLanguage; // Native name of this language
+
+ // Region
+ private String sRegionName; // (RegionInfo)
+ private String sLocalizedCountry; // localized country name
+ private String sEnglishCountry; // english country name (RegionInfo)
+ private String sNativeCountry; // native country name
+ private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
+
+ // Numbers
+ private String sPositiveSign; // (user can override) positive sign
+ private String sNegativeSign; // (user can override) negative sign
+ private String[] saNativeDigits; // (user can override) native characters for digits 0-9
+ // (nfi populates these 5, don't have to be = undef)
+ private int iDigits; // (user can override) number of fractional digits
+ private int iNegativeNumber; // (user can override) negative number format
+ private int[] waGrouping; // (user can override) grouping of digits
+ private String sDecimalSeparator; // (user can override) decimal separator
+ private String sThousandSeparator; // (user can override) thousands separator
+ private String sNaN; // Not a Number
+ private String sPositiveInfinity; // + Infinity
+ private String sNegativeInfinity; // - Infinity
+
+ // Percent
+ private int iNegativePercent = undef; // Negative Percent (0-3)
+ private int iPositivePercent = undef; // Positive Percent (0-11)
+ private String sPercent; // Percent (%) symbol
+ private String sPerMille; // PerMille (‰) symbol
+
+ // Currency
+ private String sCurrency; // (user can override) local monetary symbol
+ private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
+ // (nfi populates these 4, don't have to be = undef)
+ private int iCurrencyDigits; // (user can override) # local monetary fractional digits
+ private int iCurrency; // (user can override) positive currency format
+ private int iNegativeCurrency; // (user can override) negative currency format
+ private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
+ private String sMonetaryDecimal; // (user can override) monetary decimal separator
+ private String sMonetaryThousand; // (user can override) monetary thousands separator
+
+ // Misc
+ private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
+ private String sListSeparator; // (user can override) list separator
+
+ // Time
+ private String sAM1159; // (user can override) AM designator
+ private String sPM2359; // (user can override) PM designator
+ private String sTimeSeparator;
+ private volatile String[] saLongTimes; // (user can override) time format
+ private volatile String[] saShortTimes; // short time format
+ private volatile String[] saDurationFormats; // time duration format
+
+ // Calendar specific data
+ private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
+ private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
+ private volatile CalendarId[] waCalendars; // all available calendar type(s). The first one is the default calendar
+
+ // Store for specific data about each calendar
+ private CalendarData[] calendars; // Store for specific calendar data
+
+ // Text information
+ private int iReadingLayout = undef; // Reading layout data
+ // 0 - Left to right (eg en-US)
+ // 1 - Right to left (eg arabic locales)
+ // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
+ // 3 - Vertical top to bottom with columns proceeding to the right
+
+ // CoreCLR depends on this even though its not exposed publicly.
+
+ private int iLanguage; // locale ID (0409) - NO sort information
+ private bool bUseOverrides; // use user overrides?
+ private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
+
+
+ // Region Name to Culture Name mapping table
+ // (In future would be nice to be in registry or something)
+
+ //Using a property so we avoid creating the dictionary untill we need it
+ private static StringStringDictionary RegionNames
+ {
+ get
+ {
+ if (s_RegionNames == null)
+ {
+ StringStringDictionary regionNames = new StringStringDictionary(211 /* prime */);
+
+ regionNames.Add("029", "en-029");
+ regionNames.Add("AE", "ar-AE");
+ regionNames.Add("AF", "prs-AF");
+ regionNames.Add("AL", "sq-AL");
+ regionNames.Add("AM", "hy-AM");
+ regionNames.Add("AR", "es-AR");
+ regionNames.Add("AT", "de-AT");
+ regionNames.Add("AU", "en-AU");
+ regionNames.Add("AZ", "az-Cyrl-AZ");
+ regionNames.Add("BA", "bs-Latn-BA");
+ regionNames.Add("BD", "bn-BD");
+ regionNames.Add("BE", "nl-BE");
+ regionNames.Add("BG", "bg-BG");
+ regionNames.Add("BH", "ar-BH");
+ regionNames.Add("BN", "ms-BN");
+ regionNames.Add("BO", "es-BO");
+ regionNames.Add("BR", "pt-BR");
+ regionNames.Add("BY", "be-BY");
+ regionNames.Add("BZ", "en-BZ");
+ regionNames.Add("CA", "en-CA");
+ regionNames.Add("CH", "it-CH");
+ regionNames.Add("CL", "es-CL");
+ regionNames.Add("CN", "zh-CN");
+ regionNames.Add("CO", "es-CO");
+ regionNames.Add("CR", "es-CR");
+ regionNames.Add("CS", "sr-Cyrl-CS");
+ regionNames.Add("CZ", "cs-CZ");
+ regionNames.Add("DE", "de-DE");
+ regionNames.Add("DK", "da-DK");
+ regionNames.Add("DO", "es-DO");
+ regionNames.Add("DZ", "ar-DZ");
+ regionNames.Add("EC", "es-EC");
+ regionNames.Add("EE", "et-EE");
+ regionNames.Add("EG", "ar-EG");
+ regionNames.Add("ES", "es-ES");
+ regionNames.Add("ET", "am-ET");
+ regionNames.Add("FI", "fi-FI");
+ regionNames.Add("FO", "fo-FO");
+ regionNames.Add("FR", "fr-FR");
+ regionNames.Add("GB", "en-GB");
+ regionNames.Add("GE", "ka-GE");
+ regionNames.Add("GL", "kl-GL");
+ regionNames.Add("GR", "el-GR");
+ regionNames.Add("GT", "es-GT");
+ regionNames.Add("HK", "zh-HK");
+ regionNames.Add("HN", "es-HN");
+ regionNames.Add("HR", "hr-HR");
+ regionNames.Add("HU", "hu-HU");
+ regionNames.Add("ID", "id-ID");
+ regionNames.Add("IE", "en-IE");
+ regionNames.Add("IL", "he-IL");
+ regionNames.Add("IN", "hi-IN");
+ regionNames.Add("IQ", "ar-IQ");
+ regionNames.Add("IR", "fa-IR");
+ regionNames.Add("IS", "is-IS");
+ regionNames.Add("IT", "it-IT");
+ regionNames.Add("IV", "");
+ regionNames.Add("JM", "en-JM");
+ regionNames.Add("JO", "ar-JO");
+ regionNames.Add("JP", "ja-JP");
+ regionNames.Add("KE", "sw-KE");
+ regionNames.Add("KG", "ky-KG");
+ regionNames.Add("KH", "km-KH");
+ regionNames.Add("KR", "ko-KR");
+ regionNames.Add("KW", "ar-KW");
+ regionNames.Add("KZ", "kk-KZ");
+ regionNames.Add("LA", "lo-LA");
+ regionNames.Add("LB", "ar-LB");
+ regionNames.Add("LI", "de-LI");
+ regionNames.Add("LK", "si-LK");
+ regionNames.Add("LT", "lt-LT");
+ regionNames.Add("LU", "lb-LU");
+ regionNames.Add("LV", "lv-LV");
+ regionNames.Add("LY", "ar-LY");
+ regionNames.Add("MA", "ar-MA");
+ regionNames.Add("MC", "fr-MC");
+ regionNames.Add("ME", "sr-Latn-ME");
+ regionNames.Add("MK", "mk-MK");
+ regionNames.Add("MN", "mn-MN");
+ regionNames.Add("MO", "zh-MO");
+ regionNames.Add("MT", "mt-MT");
+ regionNames.Add("MV", "dv-MV");
+ regionNames.Add("MX", "es-MX");
+ regionNames.Add("MY", "ms-MY");
+ regionNames.Add("NG", "ig-NG");
+ regionNames.Add("NI", "es-NI");
+ regionNames.Add("NL", "nl-NL");
+ regionNames.Add("NO", "nn-NO");
+ regionNames.Add("NP", "ne-NP");
+ regionNames.Add("NZ", "en-NZ");
+ regionNames.Add("OM", "ar-OM");
+ regionNames.Add("PA", "es-PA");
+ regionNames.Add("PE", "es-PE");
+ regionNames.Add("PH", "en-PH");
+ regionNames.Add("PK", "ur-PK");
+ regionNames.Add("PL", "pl-PL");
+ regionNames.Add("PR", "es-PR");
+ regionNames.Add("PT", "pt-PT");
+ regionNames.Add("PY", "es-PY");
+ regionNames.Add("QA", "ar-QA");
+ regionNames.Add("RO", "ro-RO");
+ regionNames.Add("RS", "sr-Latn-RS");
+ regionNames.Add("RU", "ru-RU");
+ regionNames.Add("RW", "rw-RW");
+ regionNames.Add("SA", "ar-SA");
+ regionNames.Add("SE", "sv-SE");
+ regionNames.Add("SG", "zh-SG");
+ regionNames.Add("SI", "sl-SI");
+ regionNames.Add("SK", "sk-SK");
+ regionNames.Add("SN", "wo-SN");
+ regionNames.Add("SV", "es-SV");
+ regionNames.Add("SY", "ar-SY");
+ regionNames.Add("TH", "th-TH");
+ regionNames.Add("TJ", "tg-Cyrl-TJ");
+ regionNames.Add("TM", "tk-TM");
+ regionNames.Add("TN", "ar-TN");
+ regionNames.Add("TR", "tr-TR");
+ regionNames.Add("TT", "en-TT");
+ regionNames.Add("TW", "zh-TW");
+ regionNames.Add("UA", "uk-UA");
+ regionNames.Add("US", "en-US");
+ regionNames.Add("UY", "es-UY");
+ regionNames.Add("UZ", "uz-Cyrl-UZ");
+ regionNames.Add("VE", "es-VE");
+ regionNames.Add("VN", "vi-VN");
+ regionNames.Add("YE", "ar-YE");
+ regionNames.Add("ZA", "af-ZA");
+ regionNames.Add("ZW", "en-ZW");
+
+ s_RegionNames = regionNames;
+ }
+
+ return s_RegionNames;
+ }
+ }
+
+ // Cache of regions we've already looked up
+ private static volatile StringCultureDataDictionary s_cachedRegions;
+ private static volatile StringStringDictionary s_RegionNames;
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
+ {
+ // First do a shortcut for Invariant
+ if (String.IsNullOrEmpty(cultureName))
+ {
+ return CultureData.Invariant;
+ }
+
+ //
+ // First check if GetCultureData() can find it (ie: its a real culture)
+ //
+ CultureData retVal = GetCultureData(cultureName, useUserOverride);
+ if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
+
+ //
+ // Not a specific culture, perhaps it's region-only name
+ // (Remember this isn't a core clr path where that's not supported)
+ //
+
+ // If it was neutral remember that so that RegionInfo() can throw the right exception
+ CultureData neutral = retVal;
+
+ // Try the hash table next
+ String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
+ StringCultureDataDictionary tempHashTable = s_cachedRegions;
+ if (tempHashTable == null)
+ {
+ // No table yet, make a new one
+ tempHashTable = new StringCultureDataDictionary();
+ }
+ else
+ {
+ // Check the hash table
+ lock (m_lock)
+ {
+ tempHashTable.TryGetValue(hashName, out retVal);
+ }
+ if (retVal != null)
+ {
+ return retVal;
+ }
+ }
+
+ //
+ // Not found in the hash table, look it up the hard way
+ //
+
+ // If not a valid mapping from the registry we'll have to try the hard coded table
+ if (retVal == null || (retVal.IsNeutralCulture == true))
+ {
+ // Not a valid mapping, try the hard coded table
+ string name;
+ if (RegionNames.TryGetValue(cultureName, out name))
+ {
+ // Make sure we can get culture data for it
+ retVal = GetCultureData(name, useUserOverride);
+ }
+ }
+
+ // If not found in the hard coded table we'll have to find a culture that works for us
+ if (retVal == null || (retVal.IsNeutralCulture == true))
+ {
+ retVal = GetCultureDataFromRegionName(cultureName);
+ }
+
+ // If we found one we can use, then cache it for next time
+ if (retVal != null && (retVal.IsNeutralCulture == false))
+ {
+ // first add it to the cache
+ lock (m_lock)
+ {
+ tempHashTable[hashName] = retVal;
+ }
+
+ // Copy the hashtable to the corresponding member variables. This will potentially overwrite
+ // new tables simultaneously created by a new thread, but maximizes thread safety.
+ s_cachedRegions = tempHashTable;
+ }
+ else
+ {
+ // Unable to find a matching culture/region, return null or neutral
+ // (regionInfo throws a more specific exception on neutrals)
+ retVal = neutral;
+ }
+
+ // Return the found culture to use, null, or the neutral culture.
+ return retVal;
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // Build our invariant information
+ //
+ // We need an invariant instance, which we build hard-coded
+ /////////////////////////////////////////////////////////////////////////
+ internal static CultureData Invariant
+ {
+ get
+ {
+ if (s_Invariant == null)
+ {
+ // Make a new culturedata
+ CultureData invariant = new CultureData();
+
+ // Basics
+ // Note that we override the resources since this IS NOT supposed to change (by definition)
+ invariant.bUseOverrides = false;
+ invariant.sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
+ invariant.sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
+
+ // Identity
+ invariant.sName = ""; // locale name (ie: en-us)
+ invariant.sParent = ""; // Parent name (which may be a custom locale/culture)
+ invariant.bNeutral = false; // Flags for the culture (ie: neutral or not right now)
+ invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
+ invariant.sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
+ invariant.sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
+
+ // Language
+ invariant.sISO639Language = "iv"; // ISO 639 Language Name
+ invariant.sLocalizedLanguage = "Invariant Language"; // Display name for this Language
+ invariant.sEnglishLanguage = "Invariant Language"; // English name for this language
+ invariant.sNativeLanguage = "Invariant Language"; // Native name of this language
+
+ // Region
+ invariant.sRegionName = "IV"; // (RegionInfo)
+ invariant.sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
+ invariant.sNativeCountry = "Invariant Country"; // native country name (Windows Only)
+ invariant.sISO3166CountryName = "IV"; // (RegionInfo), ie: US
+
+ // Numbers
+ invariant.sPositiveSign = "+"; // positive sign
+ invariant.sNegativeSign = "-"; // negative sign
+ invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9
+ invariant.iDigits = 2; // number of fractional digits
+ invariant.iNegativeNumber = 1; // negative number format
+ invariant.waGrouping = new int[] { 3 }; // grouping of digits
+ invariant.sDecimalSeparator = "."; // decimal separator
+ invariant.sThousandSeparator = ","; // thousands separator
+ invariant.sNaN = "NaN"; // Not a Number
+ invariant.sPositiveInfinity = "Infinity"; // + Infinity
+ invariant.sNegativeInfinity = "-Infinity"; // - Infinity
+
+ // Percent
+ invariant.iNegativePercent = 0; // Negative Percent (0-3)
+ invariant.iPositivePercent = 0; // Positive Percent (0-11)
+ invariant.sPercent = "%"; // Percent (%) symbol
+ invariant.sPerMille = "\x2030"; // PerMille(‰) symbol
+
+ // Currency
+ invariant.sCurrency = "\x00a4"; // local monetary symbol "¤: for international monetary symbol
+ invariant.sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
+ invariant.iCurrencyDigits = 2; // # local monetary fractional digits
+ invariant.iCurrency = 0; // positive currency format
+ invariant.iNegativeCurrency = 0; // negative currency format
+ invariant.waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
+ invariant.sMonetaryDecimal = "."; // monetary decimal separator
+ invariant.sMonetaryThousand = ","; // monetary thousands separator
+
+ // Misc
+ invariant.iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
+ invariant.sListSeparator = ","; // list separator
+
+ // Time
+ invariant.sAM1159 = "AM"; // AM designator
+ invariant.sPM2359 = "PM"; // PM designator
+ invariant.saLongTimes = new String[] { "HH:mm:ss" }; // time format
+ invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
+ invariant.saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format
+
+
+ // Calendar specific data
+ invariant.iFirstDayOfWeek = 0; // first day of week
+ invariant.iFirstWeekOfYear = 0; // first week of year
+ invariant.waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
+
+ // Store for specific data about each calendar
+ invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS];
+ invariant.calendars[0] = CalendarData.Invariant;
+
+ // Text information
+ invariant.iReadingLayout = 0;
+
+ // These are desktop only, not coreclr
+
+ invariant.iLanguage = 0x007f; // locale ID (0409) - NO sort information
+ // Remember it
+ s_Invariant = invariant;
+ }
+ return s_Invariant;
+ }
+ }
+ private volatile static CultureData s_Invariant;
+
+ ///////////////
+ // Constructors //
+ ///////////////
+ // Cache of cultures we've already looked up
+ private static volatile StringCultureDataDictionary s_cachedCultures;
+ private static readonly Lock m_lock = new Lock();
+
+ internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
+ {
+ // First do a shortcut for Invariant
+ if (String.IsNullOrEmpty(cultureName))
+ {
+ return CultureData.Invariant;
+ }
+
+ // Try the hash table first
+ String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
+ StringCultureDataDictionary tempHashTable = s_cachedCultures;
+ if (tempHashTable == null)
+ {
+ // No table yet, make a new one
+ tempHashTable = new StringCultureDataDictionary();
+ }
+ else
+ {
+ // Check the hash table
+ bool ret;
+ CultureData retVal;
+ lock (m_lock)
+ {
+ ret = tempHashTable.TryGetValue(hashName, out retVal);
+ }
+ if (ret && retVal != null)
+ {
+ return retVal;
+ }
+ }
+
+ // Not found in the hash table, need to see if we can build one that works for us
+ CultureData culture = CreateCultureData(cultureName, useUserOverride);
+ if (culture == null)
+ {
+ return null;
+ }
+
+ // Found one, add it to the cache
+ lock (m_lock)
+ {
+ tempHashTable[hashName] = culture;
+ }
+
+ // Copy the hashtable to the corresponding member variables. This will potentially overwrite
+ // new tables simultaneously created by a new thread, but maximizes thread safety.
+ s_cachedCultures = tempHashTable;
+
+ return culture;
+ }
+
+ private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
+ {
+ CultureData culture = new CultureData();
+ culture.bUseOverrides = useUserOverride;
+ culture.sRealName = cultureName;
+
+ // Ask native code if that one's real
+ if (culture.InitCultureData() == false)
+ {
+ if (culture.InitCompatibilityCultureData() == false)
+ {
+ return null;
+ }
+ }
+
+ return culture;
+ }
+
+ private bool InitCompatibilityCultureData()
+ {
+ // for compatibility handle the deprecated ids: zh-chs, zh-cht
+ string cultureName = this.sRealName;
+
+ string fallbackCultureName;
+ string realCultureName;
+ switch (AnsiToLower(cultureName))
+ {
+ case "zh-chs":
+ fallbackCultureName = "zh-Hans";
+ realCultureName = "zh-CHS";
+ break;
+ case "zh-cht":
+ fallbackCultureName = "zh-Hant";
+ realCultureName = "zh-CHT";
+ break;
+ default:
+ return false;
+ }
+
+ this.sRealName = fallbackCultureName;
+ if (InitCultureData() == false)
+ {
+ return false;
+ }
+ // fixup our data
+ this.sName = realCultureName; // the name that goes back to the user
+ this.sParent = fallbackCultureName;
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // All the accessors
+ //
+ // Accessors for our data object items
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ ///////////
+ // Identity //
+ ///////////
+
+ // The real name used to construct the locale (ie: de-DE_phoneb)
+ internal String CultureName
+ {
+ get
+ {
+ Contract.Assert(this.sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by already");
+ // since windows doesn't know about zh-CHS and zh-CHT,
+ // we leave sRealName == zh-Hanx but we still need to
+ // pretend that it was zh-CHX.
+ switch (this.sName)
+ {
+ case "zh-CHS":
+ case "zh-CHT":
+ return this.sName;
+ }
+ return this.sRealName;
+ }
+ }
+
+ // Are overrides enabled?
+ internal bool UseUserOverride
+ {
+ get
+ {
+ return this.bUseOverrides;
+ }
+ }
+
+ // locale name (ie: de-DE, NO sort information)
+ internal String SNAME
+ {
+ get
+ {
+ if (this.sName == null)
+ {
+ this.sName = String.Empty;
+ }
+ return this.sName;
+ }
+ }
+
+ // Parent name (which may be a custom locale/culture)
+ internal String SPARENT
+ {
+ get
+ {
+ if (this.sParent == null)
+ {
+ // Ask using the real name, so that we get parents of neutrals
+ this.sParent = GetLocaleInfo(this.sRealName, LocaleStringData.ParentName);
+ }
+ return this.sParent;
+ }
+ }
+
+ // Localized pretty name for this locale (ie: Inglis (estados Unitos))
+ internal String SLOCALIZEDDISPLAYNAME
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sLocalizedDisplayName == null)
+ {
+ if (this.IsSupplementalCustomCulture)
+ {
+ if (this.IsNeutralCulture)
+ {
+ this.sLocalizedDisplayName = this.SNATIVELANGUAGE;
+ }
+ else
+ {
+ this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
+ }
+ }
+ else
+ {
+ try
+ {
+ const string ZH_CHT = "zh-CHT";
+ const string ZH_CHS = "zh-CHS";
+
+ if (SNAME.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase))
+ {
+ this.sLocalizedDisplayName = GetLanguageDisplayName("zh-Hant");
+ }
+ else if (SNAME.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase))
+ {
+ this.sLocalizedDisplayName = GetLanguageDisplayName("zh-Hans");
+ }
+ else
+ {
+ this.sLocalizedDisplayName = GetLanguageDisplayName(SNAME);
+ }
+ }
+ catch (Exception)
+ {
+ // do nothing
+ }
+ }
+ // If it hasn't been found (Windows 8 and up), fallback to the system
+ if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
+ {
+ // If its neutral use the language name
+ if (this.IsNeutralCulture)
+ {
+ this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
+ }
+ else
+ {
+ // Usually the UI culture shouldn't be different than what we got from WinRT except
+ // if DefaultThreadCurrentUICulture was set
+ CultureInfo ci;
+
+ if (CultureInfo.DefaultThreadCurrentUICulture != null &&
+ ((ci = GetUserDefaultCulture()) != null) &&
+ !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
+ {
+ this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
+ }
+ else
+ {
+ this.sLocalizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName);
+ }
+ }
+ }
+ }
+
+ return this.sLocalizedDisplayName;
+ }
+ }
+
+ // English pretty name for this locale (ie: English (United States))
+ internal String SENGDISPLAYNAME
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sEnglishDisplayName == null)
+ {
+ // If its neutral use the language name
+ if (this.IsNeutralCulture)
+ {
+ this.sEnglishDisplayName = this.SENGLISHLANGUAGE;
+ // differentiate the legacy display names
+ switch (this.sName)
+ {
+ case "zh-CHS":
+ case "zh-CHT":
+ this.sEnglishDisplayName += " Legacy";
+ break;
+ }
+ }
+ else
+ {
+ this.sEnglishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
+
+ // if it isn't found build one:
+ if (String.IsNullOrEmpty(this.sEnglishDisplayName))
+ {
+ // Our existing names mostly look like:
+ // "English" + "United States" -> "English (United States)"
+ // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
+ if (this.SENGLISHLANGUAGE[this.SENGLISHLANGUAGE.Length - 1] == ')')
+ {
+ // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
+ this.sEnglishDisplayName =
+ this.SENGLISHLANGUAGE.Substring(0, this.sEnglishLanguage.Length - 1) +
+ ", " + this.SENGCOUNTRY + ")";
+ }
+ else
+ {
+ // "English" + "United States" -> "English (United States)"
+ this.sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
+ }
+ }
+ }
+ }
+ return this.sEnglishDisplayName;
+ }
+ }
+
+ // Native pretty name for this locale (ie: Deutsch (Deutschland))
+ internal String SNATIVEDISPLAYNAME
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sNativeDisplayName == null)
+ {
+ // If its neutral use the language name
+ if (this.IsNeutralCulture)
+ {
+ this.sNativeDisplayName = this.SNATIVELANGUAGE;
+ // differentiate the legacy display names
+ switch (this.sName)
+ {
+ case "zh-CHS":
+ this.sNativeDisplayName += " \u65E7\u7248";
+ break;
+ case "zh-CHT":
+ this.sNativeDisplayName += " \u820A\u7248";
+ break;
+ }
+ }
+ else
+ {
+ this.sNativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
+
+ // if it isn't found build one:
+ if (String.IsNullOrEmpty(this.sNativeDisplayName))
+ {
+ // These should primarily be "Deutsch (Deutschland)" type names
+ this.sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
+ }
+ }
+ }
+ return this.sNativeDisplayName;
+ }
+ }
+
+ /////////////
+ // Language //
+ /////////////
+
+ // iso 639 language name, ie: en
+ internal String SISO639LANGNAME
+ {
+ get
+ {
+ if (this.sISO639Language == null)
+ {
+ this.sISO639Language = GetLocaleInfo(LocaleStringData.Iso639LanguageName);
+ }
+ return this.sISO639Language;
+ }
+ }
+
+ // Localized name for this language (Windows Only) ie: Inglis
+ // This is only valid for Windows 8 and higher neutrals:
+ internal String SLOCALIZEDLANGUAGE
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sLocalizedLanguage == null)
+ {
+ // Usually the UI culture shouldn't be different than what we got from WinRT except
+ // if DefaultThreadCurrentUICulture was set
+ CultureInfo ci;
+
+ if (CultureInfo.DefaultThreadCurrentUICulture != null &&
+ ((ci = GetUserDefaultCulture()) != null) &&
+ !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
+ {
+ this.sLocalizedLanguage = SNATIVELANGUAGE;
+ }
+ else
+ {
+ this.sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName);
+ }
+ }
+
+ return this.sLocalizedLanguage;
+ }
+ }
+
+ // English name for this language (Windows Only) ie: German
+ internal String SENGLISHLANGUAGE
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sEnglishLanguage == null)
+ {
+ this.sEnglishLanguage = GetLocaleInfo(LocaleStringData.EnglishLanguageName);
+ }
+ return this.sEnglishLanguage;
+ }
+ }
+
+ // Native name of this language (Windows Only) ie: Deutsch
+ internal String SNATIVELANGUAGE
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sNativeLanguage == null)
+ {
+ this.sNativeLanguage = GetLocaleInfo(LocaleStringData.NativeLanguageName);
+ }
+ return this.sNativeLanguage;
+ }
+ }
+
+ ///////////
+ // Region //
+ ///////////
+
+ // region name (eg US)
+ internal String SREGIONNAME
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sRegionName == null)
+ {
+ this.sRegionName = GetLocaleInfo(LocaleStringData.Iso3166CountryName);
+ }
+ return this.sRegionName;
+ }
+ }
+
+
+ // localized name for the country
+ internal string SLOCALIZEDCOUNTRY
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sLocalizedCountry == null)
+ {
+ try
+ {
+ this.sLocalizedCountry = GetRegionDisplayName(SISO3166CTRYNAME);
+ }
+ catch (Exception)
+ {
+ // do nothing. we'll fallback
+ }
+
+ if (this.sLocalizedCountry == null)
+ {
+ this.sLocalizedCountry = SNATIVECOUNTRY;
+ }
+ }
+ return this.sLocalizedCountry;
+ }
+ }
+
+ // english country name (RegionInfo) ie: Germany
+ internal String SENGCOUNTRY
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sEnglishCountry == null)
+ {
+ this.sEnglishCountry = GetLocaleInfo(LocaleStringData.EnglishCountryName);
+ }
+ return this.sEnglishCountry;
+ }
+ }
+
+ // native country name (RegionInfo) ie: Deutschland
+ internal String SNATIVECOUNTRY
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sNativeCountry == null)
+ {
+ this.sNativeCountry = GetLocaleInfo(LocaleStringData.NativeCountryName);
+ }
+ return this.sNativeCountry;
+ }
+ }
+
+ // ISO 3166 Country Name
+ internal String SISO3166CTRYNAME
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sISO3166CountryName == null)
+ {
+ this.sISO3166CountryName = GetLocaleInfo(LocaleStringData.Iso3166CountryName);
+ }
+ return this.sISO3166CountryName;
+ }
+ }
+
+ /////////////
+ // Numbers //
+ ////////////
+
+ // internal String sPositiveSign ; // (user can override) positive sign
+ // internal String sNegativeSign ; // (user can override) negative sign
+ // internal String[] saNativeDigits ; // (user can override) native characters for digits 0-9
+ // internal int iDigits ; // (user can override) number of fractional digits
+ // internal int iNegativeNumber ; // (user can override) negative number format
+
+
+
+ // (user can override) grouping of digits
+ internal int[] WAGROUPING
+ {
+ get
+ {
+ if (this.waGrouping == null)
+ {
+ this.waGrouping = GetLocaleInfo(LocaleGroupingData.Digit);
+ }
+ return this.waGrouping;
+ }
+ }
+
+
+ // internal String sDecimalSeparator ; // (user can override) decimal separator
+ // internal String sThousandSeparator ; // (user can override) thousands separator
+
+ // Not a Number
+ internal String SNAN
+ {
+ get
+ {
+ if (this.sNaN == null)
+ {
+ this.sNaN = GetLocaleInfo(LocaleStringData.NaNSymbol);
+ }
+ return this.sNaN;
+ }
+ }
+
+ // + Infinity
+ internal String SPOSINFINITY
+ {
+ get
+ {
+ if (this.sPositiveInfinity == null)
+ {
+ this.sPositiveInfinity = GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol);
+ }
+ return this.sPositiveInfinity;
+ }
+ }
+
+ // - Infinity
+ internal String SNEGINFINITY
+ {
+ get
+ {
+ if (this.sNegativeInfinity == null)
+ {
+ this.sNegativeInfinity = GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol);
+ }
+ return this.sNegativeInfinity;
+ }
+ }
+
+
+ ////////////
+ // Percent //
+ ///////////
+
+ // Negative Percent (0-3)
+ internal int INEGATIVEPERCENT
+ {
+ get
+ {
+ if (this.iNegativePercent == undef)
+ {
+ // Note that <= Windows Vista this is synthesized by native code
+ this.iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat);
+ }
+ return this.iNegativePercent;
+ }
+ }
+
+ // Positive Percent (0-11)
+ internal int IPOSITIVEPERCENT
+ {
+ get
+ {
+ if (this.iPositivePercent == undef)
+ {
+ // Note that <= Windows Vista this is synthesized by native code
+ this.iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat);
+ }
+ return this.iPositivePercent;
+ }
+ }
+
+ // Percent (%) symbol
+ internal String SPERCENT
+ {
+ get
+ {
+ if (this.sPercent == null)
+ {
+ this.sPercent = GetLocaleInfo(LocaleStringData.PercentSymbol);
+ }
+ return this.sPercent;
+ }
+ }
+
+ // PerMille (‰) symbol
+ internal String SPERMILLE
+ {
+ get
+ {
+ if (this.sPerMille == null)
+ {
+ this.sPerMille = GetLocaleInfo(LocaleStringData.PerMilleSymbol);
+ }
+ return this.sPerMille;
+ }
+ }
+
+ /////////////
+ // Currency //
+ /////////////
+
+ // (user can override) local monetary symbol, eg: $
+ internal String SCURRENCY
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sCurrency == null)
+ {
+ this.sCurrency = GetLocaleInfo(LocaleStringData.MonetarySymbol);
+ }
+ return this.sCurrency;
+ }
+ }
+
+ // international monetary symbol (RegionInfo), eg: USD
+ internal String SINTLSYMBOL
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (this.sIntlMonetarySymbol == null)
+ {
+ this.sIntlMonetarySymbol = GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol);
+ }
+ return this.sIntlMonetarySymbol;
+ }
+ }
+
+ // internal int iCurrencyDigits ; // (user can override) # local monetary fractional digits
+ // internal int iCurrency ; // (user can override) positive currency format
+ // internal int iNegativeCurrency ; // (user can override) negative currency format
+
+ // (user can override) monetary grouping of digits
+ internal int[] WAMONGROUPING
+ {
+ get
+ {
+ if (this.waMonetaryGrouping == null)
+ {
+ this.waMonetaryGrouping = GetLocaleInfo(LocaleGroupingData.Monetary);
+ }
+ return this.waMonetaryGrouping;
+ }
+ }
+
+ // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
+ internal int IMEASURE
+ {
+ get
+ {
+ if (this.iMeasure == undef)
+ {
+ this.iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem);
+ }
+ return this.iMeasure;
+ }
+ }
+
+ // (user can override) list Separator
+ internal String SLIST
+ {
+ get
+ {
+ if (this.sListSeparator == null)
+ {
+ this.sListSeparator = GetLocaleInfo(LocaleStringData.ListSeparator);
+ }
+ return this.sListSeparator;
+ }
+ }
+
+
+ ////////////////////////////
+ // Calendar/Time (Gregorian) //
+ ////////////////////////////
+
+ // (user can override) AM designator
+ internal String SAM1159
+ {
+ get
+ {
+ if (this.sAM1159 == null)
+ {
+ this.sAM1159 = GetLocaleInfo(LocaleStringData.AMDesignator);
+ }
+ return this.sAM1159;
+ }
+ }
+
+ // (user can override) PM designator
+ internal String SPM2359
+ {
+ get
+ {
+ if (this.sPM2359 == null)
+ {
+ this.sPM2359 = GetLocaleInfo(LocaleStringData.PMDesignator);
+ }
+ return this.sPM2359;
+ }
+ }
+
+ // (user can override) time format
+ internal String[] LongTimes
+ {
+ get
+ {
+ if (this.saLongTimes == null)
+ {
+ String[] longTimes = GetTimeFormats();
+ if (longTimes == null || longTimes.Length == 0)
+ {
+ this.saLongTimes = Invariant.saLongTimes;
+ }
+ else
+ {
+ this.saLongTimes = longTimes;
+ }
+ }
+ return this.saLongTimes;
+ }
+ }
+
+ // short time format
+ // Short times (derived from long times format)
+ // TODO: NLS Arrowhead - On Windows 7 we should have short times so this isn't necessary
+ internal String[] ShortTimes
+ {
+ get
+ {
+ if (this.saShortTimes == null)
+ {
+ // Try to get the short times from the OS/culture.dll
+ String[] shortTimes = null;
+ shortTimes = GetShortTimeFormats();
+
+ if (shortTimes == null || shortTimes.Length == 0)
+ {
+ //
+ // If we couldn't find short times, then compute them from long times
+ // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
+ //
+ shortTimes = DeriveShortTimesFromLong();
+ }
+
+ /* The above logic doesn't make sense on Mac, since the OS can provide us a "short time pattern".
+ * currently this is the 4th element in the array returned by LongTimes. We'll add this to our array
+ * if it doesn't exist.
+ */
+ shortTimes = AdjustShortTimesForMac(shortTimes);
+
+ // Found short times, use them
+ this.saShortTimes = shortTimes;
+ }
+ return this.saShortTimes;
+ }
+ }
+
+ private string[] AdjustShortTimesForMac(string[] shortTimes)
+ {
+ return shortTimes;
+ }
+
+ private string[] DeriveShortTimesFromLong()
+ {
+ // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string
+ // between it and the previous marker, if any. If its a short, unescaped separator,
+ // then we don't retain that part.
+ // We then check after the ss and remove anything before the next h,H,m,t...
+ string[] shortTimes = new string[LongTimes.Length];
+
+ for (int i = 0; i < LongTimes.Length; i++)
+ {
+ shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
+ }
+ return shortTimes;
+ }
+
+ private static string StripSecondsFromPattern(string time)
+ {
+ bool bEscape = false;
+ int iLastToken = -1;
+
+ // Find the seconds
+ for (int j = 0; j < time.Length; j++)
+ {
+ // Change escape mode?
+ if (time[j] == '\'')
+ {
+ // Continue
+ bEscape = !bEscape;
+ continue;
+ }
+
+ // See if there was a single \
+ if (time[j] == '\\')
+ {
+ // Skip next char
+ j++;
+ continue;
+ }
+
+ if (bEscape)
+ {
+ continue;
+ }
+
+ switch (time[j])
+ {
+ // Check for seconds
+ case 's':
+ // Found seconds, see if there was something unescaped and short between
+ // the last marker and the seconds. Windows says separator can be a
+ // maximum of three characters (without null)
+ // If 1st or last characters were ', then ignore it
+ if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
+ (time[iLastToken + 1] != '\'') &&
+ (time[j - 1] != '\''))
+ {
+ // There was something there we want to remember
+ if (iLastToken >= 0)
+ {
+ j = iLastToken + 1;
+ }
+ }
+
+ bool containsSpace;
+ int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
+
+ string sep;
+
+ if (containsSpace)
+ {
+ sep = " ";
+ }
+ else
+ {
+ sep = "";
+ }
+
+ time = time.Substring(0, j) + sep + time.Substring(endIndex);
+ break;
+ case 'm':
+ case 'H':
+ case 'h':
+ iLastToken = j;
+ break;
+ }
+ }
+ return time;
+ }
+
+ private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
+ {
+ bool bEscape = false;
+ containsSpace = false;
+ for (; index < time.Length; index++)
+ {
+ switch (time[index])
+ {
+ case '\'':
+ bEscape = !bEscape;
+ continue;
+ case '\\':
+ index++;
+ if (time[index] == ' ')
+ {
+ containsSpace = true;
+ }
+ continue;
+ case ' ':
+ containsSpace = true;
+ break;
+ case 't':
+ case 'm':
+ case 'H':
+ case 'h':
+ if (bEscape)
+ {
+ continue;
+ }
+ return index;
+ }
+ }
+ containsSpace = false;
+ return index;
+ }
+
+ // (user can override) first day of week
+ internal int IFIRSTDAYOFWEEK
+ {
+ get
+ {
+ if (this.iFirstDayOfWeek == undef)
+ {
+ this.iFirstDayOfWeek = GetFirstDayOfWeek();
+ }
+ return this.iFirstDayOfWeek;
+ }
+ }
+
+ // (user can override) first week of year
+ internal int IFIRSTWEEKOFYEAR
+ {
+ get
+ {
+ if (this.iFirstWeekOfYear == undef)
+ {
+ this.iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear);
+ }
+ return this.iFirstWeekOfYear;
+ }
+ }
+
+ // (user can override default only) short date format
+ internal String[] ShortDates(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saShortDates;
+ }
+
+ // (user can override default only) long date format
+ internal String[] LongDates(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saLongDates;
+ }
+
+ // (user can override) date year/month format.
+ internal String[] YearMonths(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saYearMonths;
+ }
+
+ // day names
+ internal string[] DayNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saDayNames;
+ }
+
+ // abbreviated day names
+ internal string[] AbbreviatedDayNames(CalendarId calendarId)
+ {
+ // Get abbreviated day names for this calendar from the OS if necessary
+ return GetCalendar(calendarId).saAbbrevDayNames;
+ }
+
+ // The super short day names
+ internal string[] SuperShortDayNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saSuperShortDayNames;
+ }
+
+ // month names
+ internal string[] MonthNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saMonthNames;
+ }
+
+ // Genitive month names
+ internal string[] GenitiveMonthNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saMonthGenitiveNames;
+ }
+
+ // month names
+ internal string[] AbbreviatedMonthNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saAbbrevMonthNames;
+ }
+
+ // Genitive month names
+ internal string[] AbbreviatedGenitiveMonthNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
+ }
+
+ // Leap year month names
+ // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
+ // the non-leap names skip the 7th name in the normal month name array
+ internal string[] LeapYearMonthNames(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).saLeapYearMonthNames;
+ }
+
+ // month/day format (single string, no override)
+ internal String MonthDay(CalendarId calendarId)
+ {
+ return GetCalendar(calendarId).sMonthDay;
+ }
+
+
+
+ /////////////
+ // Calendars //
+ /////////////
+
+ // all available calendar type(s), The first one is the default calendar.
+ internal CalendarId[] CalendarIds
+ {
+ get
+ {
+ if (this.waCalendars == null)
+ {
+ // We pass in an array of ints, and native side fills it up with count calendars.
+ // We then have to copy that list to a new array of the right size.
+ // Default calendar should be first
+ CalendarId[] calendars = new CalendarId[23];
+ Contract.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by already");
+ int count = CalendarData.GetCalendars(this.sWindowsName, this.bUseOverrides, calendars);
+
+ // See if we had a calendar to add.
+ if (count == 0)
+ {
+ // Failed for some reason, just grab Gregorian from Invariant
+ this.waCalendars = Invariant.waCalendars;
+ }
+ else
+ {
+ // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
+ // TODO: Is this necessary long-term?
+ if (this.sWindowsName == "zh-TW")
+ {
+ bool found = false;
+
+ // Do we need to insert calendar 4?
+ for (int i = 0; i < count; i++)
+ {
+ // Stop if we found calendar four
+ if (calendars[i] == CalendarId.TAIWAN)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ // If not found then insert it
+ if (!found)
+ {
+ // Insert it as the 2nd calendar
+ count++;
+ // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
+ Array.Copy(calendars, 1, calendars, 2, 23 - 1 - 1);
+ calendars[1] = CalendarId.TAIWAN;
+ }
+ }
+
+ // It worked, remember the list
+ CalendarId[] temp = new CalendarId[count];
+ Array.Copy(calendars, temp, count);
+
+ // Want 1st calendar to be default
+ // Prior to Vista the enumeration didn't have default calendar first
+ if (temp.Length > 1)
+ {
+ CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
+ if (temp[1] == i)
+ {
+ temp[1] = temp[0];
+ temp[0] = i;
+ }
+ }
+
+ this.waCalendars = temp;
+ }
+ }
+
+ return this.waCalendars;
+ }
+ }
+
+ internal CalendarData GetCalendar(CalendarId calendarId)
+ {
+ Contract.Assert(calendarId > 0 && calendarId <= CalendarId.LAST_CALENDAR,
+ "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
+
+ // arrays are 0 based, calendarIds are 1 based
+ int calendarIndex = (int)calendarId - 1;
+
+ // Have to have calendars
+ if (calendars == null)
+ {
+ calendars = new CalendarData[CalendarData.MAX_CALENDARS];
+ }
+
+ // we need the following local variable to avoid returning null
+ // when another thread creates a new array of CalendarData (above)
+ // right after we insert the newly created CalendarData (below)
+ CalendarData calendarData = calendars[calendarIndex];
+ // Make sure that calendar has data
+ if (calendarData == null)
+ {
+ Contract.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by already");
+ calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
+ calendars[calendarIndex] = calendarData;
+ }
+
+ return calendarData;
+ }
+
+ ///////////////////
+ // Text Information //
+ ///////////////////
+
+ // IsRightToLeft
+ internal bool IsRightToLeft
+ {
+ get
+ {
+ // Returns one of the following 4 reading layout values:
+ // 0 - Left to right (eg en-US)
+ // 1 - Right to left (eg arabic locales)
+ // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
+ // 3 - Vertical top to bottom with columns proceeding to the right
+ return (this.IREADINGLAYOUT == 1);
+ }
+ }
+
+ // IREADINGLAYOUT
+ // Returns one of the following 4 reading layout values:
+ // 0 - Left to right (eg en-US)
+ // 1 - Right to left (eg arabic locales)
+ // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
+ // 3 - Vertical top to bottom with columns proceeding to the right
+ //
+ // If exposed as a public API, we'd have an enum with those 4 values
+ private int IREADINGLAYOUT
+ {
+ get
+ {
+ if (this.iReadingLayout == undef)
+ {
+ Contract.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by already");
+ this.iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout);
+ }
+
+ return (this.iReadingLayout);
+ }
+ }
+
+ // The TextInfo name never includes that alternate sort and is always specific
+ // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
+ // en -> en-US
+ // en-US -> en-US
+ // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
+ // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
+ // es-ES_tradnl -> es-ES
+ internal String STEXTINFO // Text info name to use for text information
+ {
+ get
+ {
+ // Note: Custom cultures might point at another culture's textinfo, however windows knows how
+ // to redirect it to the desired textinfo culture, so this is OK.
+ Contract.Assert(this.sWindowsName != null, "[CultureData.STEXTINFO] Expected this.sWindowsName to be populated by already");
+ return (this.sWindowsName);
+ }
+ }
+
+ // Compare info name (including sorting key) to use if custom
+ internal String SCOMPAREINFO
+ {
+ get
+ {
+ Contract.Assert(this.sWindowsName != null, "[CultureData.SCOMPAREINFO] Expected this.sWindowsName to be populated by already");
+ return (this.sWindowsName);
+ }
+ }
+
+ internal bool IsSupplementalCustomCulture
+ {
+ get
+ {
+ return IsCustomCultureId(this.ILANGUAGE);
+ }
+ }
+
+ internal int ILANGUAGE
+ {
+ get
+ {
+ return this.iLanguage;
+ }
+ }
+
+ internal bool IsNeutralCulture
+ {
+ get
+ {
+ // InitCultureData told us if we're neutral or not
+ return this.bNeutral;
+ }
+ }
+
+ internal bool IsInvariantCulture
+ {
+ get
+ {
+ return String.IsNullOrEmpty(this.SNAME);
+ }
+ }
+
+ // Get an instance of our default calendar
+ internal Calendar DefaultCalendar
+ {
+ get
+ {
+ CalendarId defaultCalId = (CalendarId) GetLocaleInfo(LocaleNumberData.CalendarType);
+
+ if (defaultCalId == 0)
+ {
+ defaultCalId = this.CalendarIds[0];
+ }
+
+ return CultureInfo.GetCalendarInstance(defaultCalId);
+ }
+ }
+
+ // All of our era names
+ internal String[] EraNames(CalendarId calendarId)
+ {
+ Contract.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
+
+ return this.GetCalendar(calendarId).saEraNames;
+ }
+
+ internal String[] AbbrevEraNames(CalendarId calendarId)
+ {
+ Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
+
+ return this.GetCalendar(calendarId).saAbbrevEraNames;
+ }
+
+ internal String[] AbbreviatedEnglishEraNames(CalendarId calendarId)
+ {
+ Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
+
+ return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
+ }
+
+ //// String array DEFAULTS
+ //// Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
+
+
+ // Time separator (derived from time format)
+ internal String TimeSeparator
+ {
+ get
+ {
+ if (sTimeSeparator == null)
+ {
+ string longTimeFormat = GetTimeFormatString();
+ if (String.IsNullOrEmpty(longTimeFormat))
+ {
+ longTimeFormat = LongTimes[0];
+ }
+
+ // Compute STIME from time format
+ sTimeSeparator = GetTimeSeparator(longTimeFormat);
+ }
+ return sTimeSeparator;
+ }
+ }
+
+ // Date separator (derived from short date format)
+ internal String DateSeparator(CalendarId calendarId)
+ {
+ return GetDateSeparator(ShortDates(calendarId)[0]);
+ }
+
+ //////////////////////////////////////
+ // Helper Functions to get derived properties //
+ //////////////////////////////////////
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Unescape a NLS style quote string
+ //
+ // This removes single quotes:
+ // 'fred' -> fred
+ // 'fred -> fred
+ // fred' -> fred
+ // fred's -> freds
+ //
+ // This removes the first \ of escaped characters:
+ // fred\'s -> fred's
+ // a\\b -> a\b
+ // a\b -> ab
+ //
+ // We don't build the stringbuilder unless we find a ' or a \. If we find a ' or a \, we
+ // always build a stringbuilder because we need to remove the ' or \.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static String UnescapeNlsString(String str, int start, int end)
+ {
+ Contract.Requires(str != null);
+ Contract.Requires(start >= 0);
+ Contract.Requires(end >= 0);
+ StringBuilder result = null;
+
+ for (int i = start; i < str.Length && i <= end; i++)
+ {
+ switch (str[i])
+ {
+ case '\'':
+ if (result == null)
+ {
+ result = new StringBuilder(str, start, i - start, str.Length);
+ }
+ break;
+ case '\\':
+ if (result == null)
+ {
+ result = new StringBuilder(str, start, i - start, str.Length);
+ }
+ ++i;
+ if (i < str.Length)
+ {
+ result.Append(str[i]);
+ }
+ break;
+ default:
+ if (result != null)
+ {
+ result.Append(str[i]);
+ }
+ break;
+ }
+ }
+
+ if (result == null)
+ return (str.Substring(start, end - start + 1));
+
+ return (result.ToString());
+ }
+
+ private static String GetTimeSeparator(String format)
+ {
+ // Time format separator (ie: : in 12:39:00)
+ //
+ // We calculate this from the provided time format
+ //
+
+ //
+ // Find the time separator so that we can pretend we know STIME.
+ //
+ return GetSeparator(format, "Hhms");
+ }
+
+ private static String GetDateSeparator(String format)
+ {
+ // Date format separator (ie: / in 9/1/03)
+ //
+ // We calculate this from the provided short date
+ //
+
+ //
+ // Find the date separator so that we can pretend we know SDATE.
+ //
+ return GetSeparator(format, "dyM");
+ }
+
+ private static string GetSeparator(string format, string timeParts)
+ {
+ int index = IndexOfTimePart(format, 0, timeParts);
+
+ if (index != -1)
+ {
+ // Found a time part, find out when it changes
+ char cTimePart = format[index];
+
+ do
+ {
+ index++;
+ } while (index < format.Length && format[index] == cTimePart);
+
+ int separatorStart = index;
+
+ // Now we need to find the end of the separator
+ if (separatorStart < format.Length)
+ {
+ int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
+ if (separatorEnd != -1)
+ {
+ // From [separatorStart, count) is our string, except we need to unescape
+ return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
+ }
+ }
+ }
+
+ return String.Empty;
+ }
+
+ private static int IndexOfTimePart(string format, int startIndex, string timeParts)
+ {
+ Contract.Assert(startIndex >= 0, "startIndex cannot be negative");
+ Contract.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
+ bool inQuote = false;
+ for (int i = startIndex; i < format.Length; ++i)
+ {
+ // See if we have a time Part
+ if (!inQuote && timeParts.IndexOf(format[i]) != -1)
+ {
+ return i;
+ }
+ switch (format[i])
+ {
+ case '\\':
+ if (i + 1 < format.Length)
+ {
+ ++i;
+ switch (format[i])
+ {
+ case '\'':
+ case '\\':
+ break;
+ default:
+ --i; //backup since we will move over this next
+ break;
+ }
+ }
+ break;
+ case '\'':
+ inQuote = !inQuote;
+ break;
+ }
+ }
+
+ return -1;
+ }
+
+ internal void GetNFIValues(NumberFormatInfo nfi)
+ {
+ if (this.IsInvariantCulture)
+ {
+ nfi.positiveSign = this.sPositiveSign;
+ nfi.negativeSign = this.sNegativeSign;
+
+ nfi.numberGroupSeparator = this.sThousandSeparator;
+ nfi.numberDecimalSeparator = this.sDecimalSeparator;
+ nfi.numberDecimalDigits = this.iDigits;
+ nfi.numberNegativePattern = this.iNegativeNumber;
+
+ nfi.currencySymbol = this.sCurrency;
+ nfi.currencyGroupSeparator = this.sMonetaryThousand;
+ nfi.currencyDecimalSeparator = this.sMonetaryDecimal;
+ nfi.currencyDecimalDigits = this.iCurrencyDigits;
+ nfi.currencyNegativePattern = this.iNegativeCurrency;
+ nfi.currencyPositivePattern = this.iCurrency;
+ }
+ else
+ {
+ Contract.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by already");
+ // String values
+ nfi.positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign);
+ nfi.negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign);
+
+ nfi.numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator);
+ nfi.numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator);
+ nfi.currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator);
+ nfi.currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator);
+ nfi.currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol);
+
+ // Numeric values
+ nfi.numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount);
+ nfi.currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount);
+ nfi.currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat);
+ nfi.currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat);
+ nfi.numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat);
+
+ // LOCALE_SNATIVEDIGITS (array of 10 single character strings).
+ string digits = GetLocaleInfo(LocaleStringData.Digits);
+ nfi.nativeDigits = new string[10];
+ for (int i = 0; i < nfi.nativeDigits.Length; i++)
+ {
+ nfi.nativeDigits[i] = new string(digits[i], 1);
+ }
+ }
+
+ //
+ // Gather additional data
+ //
+ nfi.numberGroupSizes = this.WAGROUPING;
+ nfi.currencyGroupSizes = this.WAMONGROUPING;
+
+ // prefer the cached value since these do not have user overrides
+ nfi.percentNegativePattern = this.INEGATIVEPERCENT;
+ nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
+ nfi.percentSymbol = this.SPERCENT;
+ nfi.perMilleSymbol = this.SPERMILLE;
+
+ nfi.negativeInfinitySymbol = this.SNEGINFINITY;
+ nfi.positiveInfinitySymbol = this.SPOSINFINITY;
+ nfi.nanSymbol = this.SNAN;
+
+ //
+ // We don't have percent values, so use the number values
+ //
+ nfi.percentDecimalDigits = nfi.numberDecimalDigits;
+ nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
+ nfi.percentGroupSizes = nfi.numberGroupSizes;
+ nfi.percentGroupSeparator = nfi.numberGroupSeparator;
+
+ //
+ // Clean up a few odd values
+ //
+
+ // Windows usually returns an empty positive sign, but we like it to be "+"
+ if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
+
+ //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user
+ //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
+ //decimal point doesn't show up. Our default currency format will never use nfi.
+ if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
+ {
+ nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
+ }
+ }
+
+ // Helper
+ // This is ONLY used for caching names and shouldn't be used for anything else
+ internal static string AnsiToLower(string testString)
+ {
+ StringBuilder sb = new StringBuilder(testString.Length);
+
+ for (int ich = 0; ich < testString.Length; ich++)
+ {
+ char ch = testString[ich];
+ sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
+ }
+
+ return (sb.ToString());
+ }
+
+ /// <remarks>
+ /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
+ /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
+ /// </remarks>
+ private enum LocaleStringData : uint
+ {
+ /// <summary>localized name of locale, eg "German (Germany)" in UI language (coresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
+ LocalizedDisplayName = 0x00000002,
+ /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (coresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
+ EnglishDisplayName = 0x00000072,
+ /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (coresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
+ NativeDisplayName = 0x00000073,
+ /// <summary>Language Display Name for a language, eg "German" in UI language (coresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
+ LocalizedLanguageName = 0x0000006f,
+ /// <summary>English name of language, eg "German" (coresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
+ EnglishLanguageName = 0x00001001,
+ /// <summary>native name of language, eg "Deutsch" (coresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
+ NativeLanguageName = 0x00000004,
+ /// <summary>English name of country, eg "Germany" (coresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
+ EnglishCountryName = 0x00001002,
+ /// <summary>native name of country, eg "Deutschland" (coresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
+ NativeCountryName = 0x00000008,
+ /// <summary>list item separator (coresponds to LOCALE_SLIST)</summary>
+ ListSeparator = 0x0000000C,
+ /// <summary>decimal separator (coresponds to LOCALE_SDECIMAL)</summary>
+ DecimalSeparator = 0x0000000E,
+ /// <summary>thousand separator (coresponds to LOCALE_STHOUSAND)</summary>
+ ThousandSeparator = 0x0000000F,
+ /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ Digits = 0x00000013,
+ /// <summary>local monetary symbol (coresponds to LOCALE_SCURRENCY)</summary>
+ MonetarySymbol = 0x00000014,
+ /// <summary>uintl monetary symbol (coresponds to LOCALE_SINTLSYMBOL)</summary>
+ Iso4217MonetarySymbol = 0x00000015,
+ /// <summary>monetary decimal separator (coresponds to LOCALE_SMONDECIMALSEP)</summary>
+ MonetaryDecimalSeparator = 0x00000016,
+ /// <summary>monetary thousand separator (coresponds to LOCALE_SMONTHOUSANDSEP)</summary>
+ MonetaryThousandSeparator = 0x00000017,
+ /// <summary>AM designator (coresponds to LOCALE_S1159)</summary>
+ AMDesignator = 0x00000028,
+ /// <summary>PM designator (coresponds to LOCALE_S2359)</summary>
+ PMDesignator = 0x00000029,
+ /// <summary>positive sign (coresponds to LOCALE_SPOSITIVESIGN)</summary>
+ PositiveSign = 0x00000050,
+ /// <summary>negative sign (coresponds to LOCALE_SNEGATIVESIGN)</summary>
+ NegativeSign = 0x00000051,
+ /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
+ Iso639LanguageName = 0x00000059,
+ /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO3166CTRYNAME)</summary>
+ Iso3166CountryName = 0x0000005A,
+ /// <summary>Not a Number (coresponds to LOCALE_SNAN)</summary>
+ NaNSymbol = 0x00000069,
+ /// <summary>+ Infinity (coresponds to LOCALE_SPOSINFINITY)</summary>
+ PositiveInfinitySymbol = 0x0000006a,
+ /// <summary>- Infinity (coresponds to LOCALE_SNEGINFINITY)</summary>
+ NegativeInfinitySymbol = 0x0000006b,
+ /// <summary>Fallback name for resources (coresponds to LOCALE_SPARENT)</summary>
+ ParentName = 0x0000006d,
+ /// <summary>Returns the percent symbol (coresponds to LOCALE_SPERCENT)</summary>
+ PercentSymbol = 0x00000076,
+ /// <summary>Returns the permille (U+2030) symbol (coresponds to LOCALE_SPERMILLE)</summary>
+ PerMilleSymbol = 0x00000077
+ }
+
+ /// <remarks>
+ /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
+ /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
+ /// </remarks>
+ private enum LocaleGroupingData : uint
+ {
+ /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ Digit = 0x00000010,
+ /// <summary>monetary grouping (coresponds to LOCALE_SMONGROUPING)</summary>
+ Monetary = 0x00000018,
+ }
+
+ /// <remarks>
+ /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
+ /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
+ /// </remarks>
+ private enum LocaleNumberData : uint
+ {
+ /// <summary>language id (coresponds to LOCALE_ILANGUAGE)</summary>
+ LanguageId = 0x00000001,
+ /// <summary>0 = metric, 1 = US (coresponds to LOCALE_IMEASURE)</summary>
+ MeasurementSystem = 0x0000000D,
+ /// <summary>number of fractional digits (coresponds to LOCALE_IDIGITS)</summary>
+ FractionalDigitsCount = 0x00000011,
+ /// <summary>negative number mode (coresponds to LOCALE_INEGNUMBER)</summary>
+ NegativeNumberFormat = 0x00001010,
+ /// <summary># local monetary digits (coresponds to LOCALE_ICURRDIGITS)</summary>
+ MonetaryFractionalDigitsCount = 0x00000019,
+ /// <summary>positive currency mode (coresponds to LOCALE_ICURRENCY)</summary>
+ PositiveMonetaryNumberFormat = 0x0000001B,
+ /// <summary>negative currency mode (coresponds to LOCALE_INEGCURR)</summary>
+ NegativeMonetaryNumberFormat = 0x0000001C,
+ /// <summary>type of calendar specifier (coresponds to LOCALE_ICALENDARTYPE)</summary>
+ CalendarType = 0x00001009,
+ /// <summary>first week of year specifier (coresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
+ FirstWeekOfYear = 0x0000100D,
+ /// <summary>
+ /// Returns one of the following 4 reading layout values:
+ /// 0 - Left to right (eg en-US)
+ /// 1 - Right to left (eg arabic locales)
+ /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
+ /// 3 - Vertical top to bottom with columns proceeding to the right
+ /// (coresponds to LOCALE_IREADINGLAYOUT)
+ /// </summary>
+ ReadingLayout = 0x00000070,
+ /// <summary>Returns 0-11 for the negative percent format (coresponds to LOCALE_INEGATIVEPERCENT)</summary>
+ NegativePercentFormat = 0x00000074,
+ /// <summary>Returns 0-3 for the positive percent format (coresponds to LOCALE_IPOSITIVEPERCENT)</summary>
+ PositivePercentFormat = 0x00000075
+ }
+ }
+}