3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 namespace System.Globalization
10 using System.Collections;
11 using System.Collections.Generic;
13 using System.Threading;
15 using System.Reflection;
16 using System.Resources;
18 using System.Runtime.CompilerServices;
19 using System.Runtime.InteropServices;
20 using System.Runtime.Versioning;
21 using System.Diagnostics.Contracts;
22 using System.Security;
23 using System.Security.Principal;
26 // List of culture data
27 // Note the we cache overrides.
28 // Note that localized names (resource names) aren't available from here.
32 // Our names are a tad confusing.
34 // sWindowsName -- The name that windows thinks this culture is, ie:
35 // en-US if you pass in en-US
36 // de-DE_phoneb if you pass in de-DE_phoneb
37 // fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
38 // fj if you pass in fj (neutral, post-Windows 7 machine)
40 // sRealName -- The name you used to construct the culture, in pretty form
41 // en-US if you pass in EN-us
42 // en if you pass in en
43 // de-DE_phoneb if you pass in de-DE_phoneb
45 // sSpecificCulture -- The specific culture for this culture
48 // de-DE_phoneb for alt sort
49 // fj-FJ for fj (neutral)
51 // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
52 // en-US if you pass in en-US
53 // en if you pass in en
54 // de-DE if you pass in de-DE_phoneb
57 // StructLayout is needed here otherwise compiler can re-arrange the fields.
58 // We have to keep this in-sync with the definition in comnlsinfo.h
60 // WARNING WARNING WARNING
62 // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CultureDataBaseObject)
63 // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureDataBaseObject
64 // WARNING: must be manually structured to match the true loaded class layout
67 internal class CultureData
72 private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
73 private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
76 private String sName; // locale name (ie: en-us, NO sort info, but could be neutral)
77 private String sParent; // Parent name (which may be a custom locale/culture)
78 private String sLocalizedDisplayName; // Localized pretty name for this locale
79 private String sEnglishDisplayName; // English pretty name for this locale
80 private String sNativeDisplayName; // Native pretty name for this locale
81 private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
84 private String sISO639Language; // ISO 639 Language Name
85 private String sLocalizedLanguage; // Localized name for this language
86 private String sEnglishLanguage; // English name for this language
87 private String sNativeLanguage; // Native name of this language
90 private String sRegionName; // (RegionInfo)
91 // private int iCountry=undef ; // (user can override) ---- code (RegionInfo)
92 private int iGeoId = undef; // GeoId
93 private String sLocalizedCountry; // localized country name
94 private String sEnglishCountry; // english country name (RegionInfo)
95 private String sNativeCountry; // native country name
96 private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
99 private String sPositiveSign; // (user can override) positive sign
100 private String sNegativeSign; // (user can override) negative sign
101 private String[] saNativeDigits; // (user can override) native characters for digits 0-9
102 // (nfi populates these 5, don't have to be = undef)
103 private int iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
104 private int iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
105 private int iDigits; // (user can override) number of fractional digits
106 private int iNegativeNumber; // (user can override) negative number format
107 private int[] waGrouping; // (user can override) grouping of digits
108 private String sDecimalSeparator; // (user can override) decimal separator
109 private String sThousandSeparator; // (user can override) thousands separator
110 private String sNaN; // Not a Number
111 private String sPositiveInfinity; // + Infinity
112 private String sNegativeInfinity; // - Infinity
115 private int iNegativePercent = undef; // Negative Percent (0-3)
116 private int iPositivePercent = undef; // Positive Percent (0-11)
117 private String sPercent; // Percent (%) symbol
118 private String sPerMille; // PerMille (‰) symbol
121 private String sCurrency; // (user can override) local monetary symbol
122 private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
123 private String sEnglishCurrency; // English name for this currency
124 private String sNativeCurrency; // Native name for this currency
125 // (nfi populates these 4, don't have to be = undef)
126 private int iCurrencyDigits; // (user can override) # local monetary fractional digits
127 private int iCurrency; // (user can override) positive currency format
128 private int iNegativeCurrency; // (user can override) negative currency format
129 private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
130 private String sMonetaryDecimal; // (user can override) monetary decimal separator
131 private String sMonetaryThousand; // (user can override) monetary thousands separator
134 private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
135 private String sListSeparator; // (user can override) list separator
136 // private int iPaperSize ; // default paper size (RegionInfo)
139 private String sAM1159; // (user can override) AM designator
140 private String sPM2359; // (user can override) PM designator
141 private String sTimeSeparator;
142 private volatile String[] saLongTimes; // (user can override) time format
143 private volatile String[] saShortTimes; // short time format
144 private volatile String[] saDurationFormats; // time duration format
146 // Calendar specific data
147 private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
148 private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
149 private volatile int[] waCalendars; // all available calendar type(s). The first one is the default calendar
151 // Store for specific data about each calendar
152 private CalendarData[] calendars; // Store for specific calendar data
155 private int iReadingLayout = undef; // Reading layout data
156 // 0 - Left to right (eg en-US)
157 // 1 - Right to left (eg arabic locales)
158 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
159 // 3 - Vertical top to bottom with columns proceeding to the right
161 private String sTextInfo; // Text info name to use for custom
162 private String sCompareInfo; // Compare info name (including sorting key) to use if custom
163 private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
165 // CoreCLR depends on this even though its not exposed publicly.
167 private int iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
168 private int iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
169 private int iDefaultMacCodePage = undef; // default macintosh code page
170 private int iDefaultEbcdicCodePage = undef; // default EBCDIC code page
172 // These are desktop only, not coreclr
173 private int iLanguage; // locale ID (0409) - NO sort information
174 private String sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
175 private String sAbbrevCountry; // abbreviated country name (RegionInfo) (Windows Region Name) ex: USA
176 private String sISO639Language2; // 3 char ISO 639 lang name 2 ex: eng
177 private String sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
178 private int iInputLanguageHandle=undef;// input language handle
179 private String sConsoleFallbackName; // The culture name for the console fallback UI culture
180 private String sKeyboardsToInstall; // Keyboard installation string.
181 private String fontSignature; // Font signature (16 WORDS)
184 // The bools all need to be in one spot
185 private bool bUseOverrides; // use user overrides?
186 private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
188 private bool bWin32Installed; // Flags indicate if the culture is Win32 installed
189 private bool bFramework; // Flags for indicate if the culture is one of Whidbey cultures
193 // Region Name to Culture Name mapping table
194 // (In future would be nice to be in registry or something)
196 //Using a property so we avoid creating the dictionary untill we need it
197 private static Dictionary<string, string> RegionNames
201 if (s_RegionNames == null)
203 var regionNames = new Dictionary<string, string> {
212 { "AZ", "az-Cyrl-AZ" },
213 { "BA", "bs-Latn-BA" },
229 { "CS", "sr-Cyrl-CS" },
280 { "ME", "sr-Latn-ME" },
305 { "RS", "sr-Latn-RS" },
317 { "TJ", "tg-Cyrl-TJ" },
326 { "UZ", "uz-Cyrl-UZ" },
333 s_RegionNames = regionNames;
335 return s_RegionNames;
338 private volatile static Dictionary<string, string> s_RegionNames;
342 /////////////////////////////////////////////////////////////////////////
343 // Build our invariant information
345 // We need an invariant instance, which we build hard-coded
346 /////////////////////////////////////////////////////////////////////////
347 internal static CultureData Invariant
351 if (s_Invariant == null)
353 // Make a new culturedata
354 CultureData invariant = new CultureData();
357 // Call the native code to get the value of bWin32Installed.
358 // For versions <= Vista, we set this to false for compatibility with v2.
359 // For Windows 7, the flag is true.
360 invariant.bUseOverrides = false;
361 invariant.sRealName = "";
363 // Ask the native code to fill it out for us, we only need the field IsWin32Installed
364 nativeInitCultureData(invariant);
368 // Note that we override the resources since this IS NOT supposed to change (by definition)
369 invariant.bUseOverrides = false;
370 invariant.sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
371 invariant.sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
374 invariant.sName = ""; // locale name (ie: en-us)
375 invariant.sParent = ""; // Parent name (which may be a custom locale/culture)
376 invariant.bNeutral = false; // Flags for the culture (ie: neutral or not right now)
378 // Don't set invariant.bWin32Installed, we used nativeInitCultureData for that.
379 invariant.bFramework = true;
382 invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
383 invariant.sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
384 invariant.sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
387 invariant.sISO639Language = "iv"; // ISO 639 Language Name
388 invariant.sLocalizedLanguage = "Invariant Language"; // Display name for this Language
389 invariant.sEnglishLanguage = "Invariant Language"; // English name for this language
390 invariant.sNativeLanguage = "Invariant Language"; // Native name of this language
393 invariant.sRegionName = "IV"; // (RegionInfo)
395 // invariant.iCountry =1; // ---- code (RegionInfo)
396 invariant.iGeoId = 244; // GeoId (Windows Only)
397 invariant.sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
398 invariant.sNativeCountry = "Invariant Country"; // native country name (Windows Only)
399 invariant.sISO3166CountryName = "IV"; // (RegionInfo), ie: US
402 invariant.sPositiveSign = "+"; // positive sign
403 invariant.sNegativeSign = "-"; // negative sign
404 invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9
405 invariant.iDigitSubstitution = 1; // Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
406 invariant.iLeadingZeros = 1; // leading zeros 0=no leading zeros, 1=leading zeros
407 invariant.iDigits = 2; // number of fractional digits
408 invariant.iNegativeNumber = 1; // negative number format
409 invariant.waGrouping = new int[] { 3 }; // grouping of digits
410 invariant.sDecimalSeparator = "."; // decimal separator
411 invariant.sThousandSeparator = ","; // thousands separator
412 invariant.sNaN = "NaN"; // Not a Number
413 invariant.sPositiveInfinity = "Infinity"; // + Infinity
414 invariant.sNegativeInfinity = "-Infinity"; // - Infinity
417 invariant.iNegativePercent = 0; // Negative Percent (0-3)
418 invariant.iPositivePercent = 0; // Positive Percent (0-11)
419 invariant.sPercent = "%"; // Percent (%) symbol
420 invariant.sPerMille = "\x2030"; // PerMille(‰) symbol
423 invariant.sCurrency = "\x00a4"; // local monetary symbol "¤: for international monetary symbol
424 invariant.sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
425 invariant.sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
426 invariant.sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
427 invariant.iCurrencyDigits = 2; // # local monetary fractional digits
428 invariant.iCurrency = 0; // positive currency format
429 invariant.iNegativeCurrency = 0; // negative currency format
430 invariant.waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
431 invariant.sMonetaryDecimal = "."; // monetary decimal separator
432 invariant.sMonetaryThousand = ","; // monetary thousands separator
435 invariant.iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
436 invariant.sListSeparator = ","; // list separator
438 // invariant.iPaperSize =9; // default paper size (RegionInfo)
439 // invariant.waFontSignature ="\x0002\x0000\x0000\x0000\x0000\x0000\x0000\x8000\x0001\x0000\x0000\x8000\x0001\x0000\x0000\x8000"; // Font signature (16 WORDS) (Windows Only)
442 invariant.sAM1159 = "AM"; // AM designator
443 invariant.sPM2359 = "PM"; // PM designator
444 invariant.saLongTimes = new String[] { "HH:mm:ss" }; // time format
445 invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
446 invariant.saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format
448 // Calendar specific data
449 invariant.iFirstDayOfWeek = 0; // first day of week
450 invariant.iFirstWeekOfYear = 0; // first week of year
451 invariant.waCalendars = new int[] { (int)CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
453 // Store for specific data about each calendar
454 invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS];
455 invariant.calendars[0] = CalendarData.Invariant;
458 invariant.iReadingLayout = 0; // Reading Layout = RTL
460 invariant.sTextInfo = ""; // Text info name to use for custom
461 invariant.sCompareInfo = ""; // Compare info name (including sorting key) to use if custom
462 invariant.sScripts = "Latn;"; // Typical Scripts for this locale (latn,cyrl, etc)
464 // These are desktop only, not coreclr
466 invariant.iLanguage = 0x007f; // locale ID (0409) - NO sort information
467 invariant.iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
468 invariant.iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
469 invariant.iDefaultMacCodePage = 10000; // default macintosh code page
470 invariant.iDefaultEbcdicCodePage = 037; // default EBCDIC code page
471 invariant.sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
472 invariant.sAbbrevCountry = "IVC"; // abbreviated country name (RegionInfo) (Windows Region Name)
473 invariant.sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
474 invariant.sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
475 invariant.iInputLanguageHandle = 0x007f; // input language handle
476 invariant.sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
477 invariant.sKeyboardsToInstall = "0409:00000409"; // Keyboard installation string.
480 s_Invariant = invariant;
485 private volatile static CultureData s_Invariant;
489 internal static volatile ResourceSet MscorlibResourceSet;
493 [System.Security.SecurityCritical] // auto-generated
494 private static bool IsResourcePresent(String resourceKey)
496 if (MscorlibResourceSet == null)
498 MscorlibResourceSet = new ResourceSet(typeof(Environment).Assembly.GetManifestResourceStream("mscorlib.resources"));
500 return MscorlibResourceSet.GetString(resourceKey) != null;
507 // Cache of cultures we've already looked up
508 private static volatile Dictionary<String, CultureData> s_cachedCultures;
510 [FriendAccessAllowed]
511 internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
513 // First do a shortcut for Invariant
514 if (String.IsNullOrEmpty(cultureName))
516 return CultureData.Invariant;
519 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
521 // WinCE named some locales differently than Windows.
522 if (cultureName.Equals("iw", StringComparison.OrdinalIgnoreCase))
526 else if (cultureName.Equals("tl", StringComparison.OrdinalIgnoreCase))
530 else if (cultureName.Equals("english", StringComparison.OrdinalIgnoreCase))
536 // Try the hash table first
537 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
538 Dictionary<String, CultureData> tempHashTable = s_cachedCultures;
539 if (tempHashTable == null)
541 // No table yet, make a new one
542 tempHashTable = new Dictionary<String, CultureData>();
546 // Check the hash table
548 lock (((ICollection)tempHashTable).SyncRoot)
550 tempHashTable.TryGetValue(hashName, out retVal);
558 // Not found in the hash table, need to see if we can build one that works for us
559 CultureData culture = CreateCultureData(cultureName, useUserOverride);
565 // Found one, add it to the cache
566 lock (((ICollection)tempHashTable).SyncRoot)
568 tempHashTable[hashName] = culture;
571 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
572 // new tables simultaneously created by a new thread, but maximizes thread safety.
573 s_cachedCultures = tempHashTable;
578 private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
580 CultureData culture = new CultureData();
581 culture.bUseOverrides = useUserOverride;
582 culture.sRealName = cultureName;
584 // Ask native code if that one's real
585 if (culture.InitCultureData() == false)
588 if (culture.InitCompatibilityCultureData() == false
589 && culture.InitLegacyAlternateSortData() == false)
599 private bool InitCultureData()
601 if (nativeInitCultureData(this) == false)
607 if (CultureInfo.IsTaiwanSku)
609 TreatTaiwanParentChainAsHavingTaiwanAsSpecific();
616 [System.Security.SecuritySafeCritical]
617 private void TreatTaiwanParentChainAsHavingTaiwanAsSpecific()
619 if (IsNeutralInParentChainOfTaiwan() && IsOsPriorToWin7() && !IsReplacementCulture)
621 // force population of fields that should have information that is
622 // different than zh-TW:
623 string s = SNATIVELANGUAGE;
624 s = SENGLISHLANGUAGE;
625 s = SLOCALIZEDLANGUAGE;
629 int i = IDEFAULTANSICODEPAGE;
630 i = IDEFAULTOEMCODEPAGE;
631 i = IDEFAULTMACCODEPAGE;
633 // all other fields will be populated with values that are the same
635 this.sSpecificCulture = "zh-TW";
636 this.sWindowsName = "zh-TW";
640 private bool IsNeutralInParentChainOfTaiwan()
642 return this.sRealName == "zh" || this.sRealName == "zh-Hant";
645 static readonly Version s_win7Version = new Version(6, 1);
646 static private bool IsOsPriorToWin7()
648 return Environment.OSVersion.Platform == PlatformID.Win32NT &&
649 Environment.OSVersion.Version < s_win7Version;
651 static private bool IsOsWin7OrPrior()
653 return Environment.OSVersion.Platform == PlatformID.Win32NT &&
654 Environment.OSVersion.Version < new Version(6, 2); // Win7 is 6.1.Build.Revision so we have to check for anything less than 6.2
657 private bool InitCompatibilityCultureData()
659 // for compatibility handle the deprecated ids: zh-chs, zh-cht
660 string cultureName = this.sRealName;
662 string fallbackCultureName;
663 string realCultureName;
664 switch (AnsiToLower(cultureName))
667 fallbackCultureName = "zh-Hans";
668 realCultureName = "zh-CHS";
671 fallbackCultureName = "zh-Hant";
672 realCultureName = "zh-CHT";
678 this.sRealName = fallbackCultureName;
679 if (InitCultureData() == false)
684 this.sName = realCultureName; // the name that goes back to the user
685 this.sParent = fallbackCultureName;
686 this.bFramework = true;
691 private bool InitLegacyAlternateSortData()
693 if (!CompareInfo.IsLegacy20SortingBehaviorRequested)
698 // For V2 compatability, handle deprecated alternate sorts
699 string cultureName = this.sRealName;
701 switch (AnsiToLower(cultureName))
704 cultureName = "ko-KR_unicod";
705 this.sRealName = "ko-KR";
706 this.iLanguage = 0x00010412;
709 cultureName = "ja-JP_unicod";
710 this.sRealName = "ja-JP";
711 this.iLanguage = 0x00010411;
714 cultureName = "zh-HK_stroke";
715 this.sRealName = "zh-HK";
716 this.iLanguage = 0x00020c04;
722 if (nativeInitCultureData(this) == false)
727 this.sRealName = cultureName;
728 this.sCompareInfo = cultureName;
729 this.bFramework = true;
734 #if FEATURE_WIN32_REGISTRY
735 private static String s_RegionKey = @"System\CurrentControlSet\Control\Nls\RegionMapping";
736 #endif // FEATURE_WIN32_REGISTRY
738 #endif // !FEATURE_CORECLR
739 // Cache of regions we've already looked up
740 private static volatile Dictionary<String, CultureData> s_cachedRegions;
742 [System.Security.SecurityCritical] // auto-generated
743 internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
745 // First do a shortcut for Invariant
746 if (String.IsNullOrEmpty(cultureName))
748 return CultureData.Invariant;
752 // First check if GetCultureData() can find it (ie: its a real culture)
754 CultureData retVal = GetCultureData(cultureName, useUserOverride);
755 if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
758 // Not a specific culture, perhaps it's region-only name
759 // (Remember this isn't a core clr path where that's not supported)
762 // If it was neutral remember that so that RegionInfo() can throw the right exception
763 CultureData neutral = retVal;
765 // Try the hash table next
766 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
767 Dictionary<String, CultureData> tempHashTable = s_cachedRegions;
768 if (tempHashTable == null)
770 // No table yet, make a new one
771 tempHashTable = new Dictionary<String, CultureData>();
775 // Check the hash table
776 lock (((ICollection)tempHashTable).SyncRoot)
778 tempHashTable.TryGetValue(hashName, out retVal);
788 // Not found in the hash table, look it up the hard way
790 #if FEATURE_WIN32_REGISTRY
791 // First try the registry in case there are overrides of our table
794 // Open in read-only mode.
795 // Use InternalOpenSubKey so that we avoid the security check.
796 Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.InternalOpenSubKey(s_RegionKey, false);
802 Object value = key.InternalGetValue(cultureName, null, false, false);
806 // Get the name of the locale to try.
807 String specificForRegion = value.ToString();
810 retVal = GetCultureData(specificForRegion, useUserOverride);
819 // If this fails for any reason, we'll just ignore it, likely it just isn't there.
820 catch (ObjectDisposedException) { }
821 catch (ArgumentException) { }
822 #endif // FEATURE_WIN32_REGISTRY
824 // If not a valid mapping from the registry we'll have to try the hard coded table
825 if (retVal == null || (retVal.IsNeutralCulture == true))
827 // Not a valid mapping, try the hard coded table
828 if (RegionNames.ContainsKey(cultureName))
830 // Make sure we can get culture data for it
831 retVal = GetCultureData(RegionNames[cultureName], useUserOverride);
834 #endif // !FEATURE_CORECLR
835 // If not found in the hard coded table we'll have to find a culture that works for us
836 if (retVal == null || (retVal.IsNeutralCulture == true))
838 // Not found in the hard coded table, need to see if we can find a culture that works for us
839 // Not a real culture name, see if it matches a region name
840 // (we just return the first culture we match)
841 CultureInfo[] specifics = SpecificCultures;
842 for (int i = 0; i < specifics.Length; i++)
844 if (String.Compare(specifics[i].m_cultureData.SREGIONNAME, cultureName, StringComparison.OrdinalIgnoreCase) == 0)
846 // Matched, use this culture
847 retVal = specifics[i].m_cultureData;
853 // If we found one we can use, then cash it for next time
854 if (retVal != null && (retVal.IsNeutralCulture == false))
856 // first add it to the cache
857 lock (((ICollection)tempHashTable).SyncRoot)
859 tempHashTable[hashName] = retVal;
862 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
863 // new tables simultaneously created by a new thread, but maximizes thread safety.
864 s_cachedRegions = tempHashTable;
868 // Unable to find a matching culture/region, return null or neutral
869 // (regionInfo throws a more specific exception on neutrals)
873 // Return the found culture to use, null, or the neutral culture.
878 // Obtain locale name from LCID
879 // NOTE: This will get neutral names, unlike the OS API
880 [System.Security.SecuritySafeCritical] // auto-generated
881 [ResourceExposure(ResourceScope.None)]
882 [MethodImplAttribute(MethodImplOptions.InternalCall)]
883 internal static extern String LCIDToLocaleName(int lcid);
885 // We'd rather people use the named version since this doesn't allow custom locales
886 internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
888 String localeName = null;
889 CultureData retVal = null;
892 // If V2 legacy sort is requested, then provide deprecated alternate sorts
893 if (CompareInfo.IsLegacy20SortingBehaviorRequested)
898 localeName = "ko-KR_unicod";
901 localeName = "ja-JP_unicod";
904 localeName = "zh-HK_stroke";
910 if (localeName == null)
912 // Convert the lcid to a name, then use that
913 // Note that this'll return neutral names (unlike Vista native API)
914 localeName = LCIDToLocaleName(culture);
917 // If its not valid, then throw
918 if (String.IsNullOrEmpty(localeName))
920 // Could be valid for Invariant
921 if (culture == 0x007f)
929 // for compatibility with Whidbey, when requesting
930 // a locale from LCID, return the old localeName
932 localeName = "zh-CHS";
935 localeName = "zh-CHT";
939 // Valid name, use it
940 retVal = GetCultureData(localeName, bUseUserOverride);
943 // If not successful, throw
945 throw new CultureNotFoundException(
946 "culture", culture, Environment.GetResourceString("Argument_CultureNotSupported"));
948 // Return the one we found
953 // Clear our internal caches
954 internal static void ClearCachedData()
956 s_cachedCultures = null;
958 s_cachedRegions = null;
959 s_replacementCultureNames = null;
963 [System.Security.SecuritySafeCritical] // auto-generated
964 internal static CultureInfo[] GetCultures(CultureTypes types)
966 // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
967 #pragma warning disable 618
969 if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
970 CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
971 CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
972 CultureTypes.FrameworkCultures)) != 0)
974 throw new ArgumentOutOfRangeException(
977 CultureInfo.CurrentCulture,
978 Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
982 // CHANGE FROM Whidbey
984 // We have deprecated CultureTypes.FrameworkCultures.
985 // When this enum is used, we will enumerate Whidbey framework cultures (for compatability).
988 // We have deprecated CultureTypes.WindowsOnlyCultures.
989 // When this enum is used, we will return an empty array for this enum.
990 if ((types & CultureTypes.WindowsOnlyCultures) != 0)
992 // Remove the enum as it is an no-op.
993 types &= (~CultureTypes.WindowsOnlyCultures);
996 String[] cultureNames = null;
999 // Call nativeEnumCultureNames() to get a string array of culture names based on the specified
1000 // enumeration type.
1002 // nativeEnumCulturNames is a QCall. We need to use a reference to return the string array
1003 // allocated from the QCall. That ref has to be wrapped as object handle.
1004 // See vm\qcall.h for details in QCall.
1007 if (nativeEnumCultureNames((int)types, JitHelpers.GetObjectHandleOnStack(ref cultureNames)) == 0)
1009 return new CultureInfo[0];
1012 int arrayLength = cultureNames.Length;
1014 if ((types & (CultureTypes.NeutralCultures | CultureTypes.FrameworkCultures)) != 0) // add zh-CHT and zh-CHS
1019 CultureInfo[] cultures = new CultureInfo[arrayLength];
1021 for (int i = 0; i < cultureNames.Length; i++)
1023 cultures[i] = new CultureInfo(cultureNames[i]);
1026 if ((types & (CultureTypes.NeutralCultures | CultureTypes.FrameworkCultures)) != 0) // add zh-CHT and zh-CHS
1028 Contract.Assert(arrayLength == cultureNames.Length + 2, "CultureData.nativeEnumCultureNames() Incorrect array size");
1029 cultures[cultureNames.Length] = new CultureInfo("zh-CHS");
1030 cultures[cultureNames.Length + 1] = new CultureInfo("zh-CHT");
1033 #pragma warning restore 618
1038 internal static volatile CultureInfo[] specificCultures;
1040 private static CultureInfo[] SpecificCultures
1044 if (specificCultures == null)
1045 specificCultures = GetCultures(CultureTypes.SpecificCultures);
1047 return specificCultures;
1051 #if !FEATURE_CORECLR
1052 internal bool IsReplacementCulture
1056 return IsReplacementCultureName(this.SNAME);
1060 internal static volatile String[] s_replacementCultureNames;
1062 ////////////////////////////////////////////////////////////////////////
1064 // Cache for the known replacement cultures.
1065 // This is used by CultureInfo.CultureType to check if a culture is a
1066 // replacement culture.
1068 ////////////////////////////////////////////////////////////////////////
1071 [System.Security.SecuritySafeCritical] // auto-generated
1072 private static bool IsReplacementCultureName(String name)
1074 Contract.Assert(name != null, "IsReplacementCultureName(): name should not be null");
1075 String[] replacementCultureNames = s_replacementCultureNames;
1076 if (replacementCultureNames == null)
1078 if (nativeEnumCultureNames((int)CultureTypes.ReplacementCultures, JitHelpers.GetObjectHandleOnStack(ref replacementCultureNames)) == 0)
1083 // Even if we don't have any replacement cultures, the returned replacementCultureNames will still an empty string array, not null.
1084 Contract.Assert(name != null, "IsReplacementCultureName(): replacementCultureNames should not be null");
1085 Array.Sort(replacementCultureNames);
1086 s_replacementCultureNames = replacementCultureNames;
1088 return Array.BinarySearch(replacementCultureNames, name) >= 0;
1092 ////////////////////////////////////////////////////////////////////////
1094 // All the accessors
1096 // Accessors for our data object items
1098 ////////////////////////////////////////////////////////////////////////
1104 // The real name used to construct the locale (ie: de-DE_phoneb)
1105 internal String CultureName
1109 Contract.Assert(this.sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
1110 // since windows doesn't know about zh-CHS and zh-CHT,
1111 // we leave sRealName == zh-Hanx but we still need to
1112 // pretend that it was zh-CHX.
1119 return this.sRealName;
1123 // Are overrides enabled?
1124 internal bool UseUserOverride
1128 return this.bUseOverrides;
1132 // locale name (ie: de-DE, NO sort information)
1133 internal String SNAME
1137 // Contract.Assert(this.sName != null,
1138 // "[CultureData.SNAME] Expected this.sName to be populated by COMNlsInfo::nativeInitCultureData already");
1139 if (this.sName == null)
1141 this.sName = String.Empty;
1147 // Parent name (which may be a custom locale/culture)
1148 internal String SPARENT
1150 [System.Security.SecurityCritical] // auto-generated
1153 if (this.sParent == null)
1155 // Ask using the real name, so that we get parents of neutrals
1156 this.sParent = DoGetLocaleInfo(this.sRealName, LOCALE_SPARENT);
1158 #if !FEATURE_CORECLR
1159 // for compatibility, the chain should be:
1160 // zh-CN -> zh-CHS -> zh-Hans -> zh
1161 // zh-TW -> zh-CHT -> zh-Hant -> zh
1162 Contract.Assert(this.sName != "zh-CHS" && this.sName != "zh-CHT",
1163 "sParent should have been initialized for zh-CHS and zh-CHT when they were constructed, otherwise we get recursion");
1164 switch (this.sParent)
1167 this.sParent = "zh-CHS";
1170 this.sParent = "zh-CHT";
1176 return this.sParent;
1180 // Localized pretty name for this locale (ie: Inglis (estados Unitos))
1181 internal String SLOCALIZEDDISPLAYNAME
1183 [System.Security.SecurityCritical] // auto-generated
1186 if (this.sLocalizedDisplayName == null)
1188 #if !FEATURE_CORECLR
1189 if (this.IsSupplementalCustomCulture)
1191 if (this.IsNeutralCulture)
1193 this.sLocalizedDisplayName = this.SNATIVELANGUAGE;
1197 this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
1202 String resourceKey = "Globalization.ci_" + this.sName;
1203 if (IsResourcePresent(resourceKey))
1205 this.sLocalizedDisplayName = Environment.GetResourceString(resourceKey);
1209 // If it hasn't been found (Windows 8 and up), fallback to the system
1210 if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1212 // If its neutral use the language name
1213 if (this.IsNeutralCulture)
1215 this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
1217 #if FEATURE_LEGACYNETCF
1218 // NetCF renders Invariant DisplayName differently from Windows
1219 // Quirk it for NetCF apps
1220 else if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8 && object.ReferenceEquals(this, s_Invariant))
1222 this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
1227 // We have to make the neutral distinction in case the OS returns a specific name
1228 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1230 this.sLocalizedDisplayName = DoGetLocaleInfo(LOCALE_SLOCALIZEDDISPLAYNAME);
1232 if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1234 this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
1239 return this.sLocalizedDisplayName;
1243 // English pretty name for this locale (ie: English (United States))
1244 internal String SENGDISPLAYNAME
1246 [System.Security.SecurityCritical] // auto-generated
1249 if (this.sEnglishDisplayName == null)
1251 // If its neutral use the language name
1252 if (this.IsNeutralCulture)
1254 this.sEnglishDisplayName = this.SENGLISHLANGUAGE;
1255 #if !FEATURE_CORECLR
1256 // differentiate the legacy display names
1261 this.sEnglishDisplayName += " Legacy";
1269 this.sEnglishDisplayName = DoGetLocaleInfo(LOCALE_SENGLISHDISPLAYNAME);
1271 // if it isn't found build one:
1272 if (String.IsNullOrEmpty(this.sEnglishDisplayName))
1274 // Our existing names mostly look like:
1275 // "English" + "United States" -> "English (United States)"
1276 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
1277 if (this.SENGLISHLANGUAGE.EndsWith(')'))
1279 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
1280 this.sEnglishDisplayName =
1281 this.SENGLISHLANGUAGE.Substring(0, this.sEnglishLanguage.Length - 1) +
1282 ", " + this.SENGCOUNTRY + ")";
1286 // "English" + "United States" -> "English (United States)"
1287 this.sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
1292 return this.sEnglishDisplayName;
1296 // Native pretty name for this locale (ie: Deutsch (Deutschland))
1297 internal String SNATIVEDISPLAYNAME
1299 [System.Security.SecurityCritical] // auto-generated
1302 if (this.sNativeDisplayName == null)
1304 // If its neutral use the language name
1305 if (this.IsNeutralCulture)
1307 this.sNativeDisplayName = this.SNATIVELANGUAGE;
1308 #if !FEATURE_CORECLR
1309 // differentiate the legacy display names
1313 this.sNativeDisplayName += " \u65E7\u7248";
1316 this.sNativeDisplayName += " \u820A\u7248";
1323 #if !FEATURE_CORECLR
1324 if (IsIncorrectNativeLanguageForSinhala())
1327 this.sNativeDisplayName ="\x0dc3\x0dd2\x0d82\x0dc4\x0dbd (\x0DC1\x0DCA\x200D\x0DBB\x0DD3\x0020\x0DBD\x0D82\x0D9A\x0DCF)";
1332 this.sNativeDisplayName = DoGetLocaleInfo(LOCALE_SNATIVEDISPLAYNAME);
1335 // if it isn't found build one:
1336 if (String.IsNullOrEmpty(this.sNativeDisplayName))
1338 // These should primarily be "Deutsch (Deutschland)" type names
1339 this.sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
1343 return this.sNativeDisplayName;
1347 // The culture name to be used in CultureInfo.CreateSpecificCulture()
1348 internal String SSPECIFICCULTURE
1352 // This got populated when ComNlsInfo::nativeInitCultureData told us we had a culture
1353 Contract.Assert(this.sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by COMNlsInfo::nativeInitCultureData already");
1354 return this.sSpecificCulture;
1362 // iso 639 language name, ie: en
1363 internal String SISO639LANGNAME
1365 [System.Security.SecurityCritical] // auto-generated
1368 if (this.sISO639Language == null)
1370 this.sISO639Language = DoGetLocaleInfo(LOCALE_SISO639LANGNAME);
1372 return this.sISO639Language;
1376 #if !FEATURE_CORECLR
1377 // iso 639 language name, ie: eng
1378 internal String SISO639LANGNAME2
1380 [System.Security.SecurityCritical] // auto-generated
1383 if (this.sISO639Language2 == null)
1385 this.sISO639Language2 = DoGetLocaleInfo(LOCALE_SISO639LANGNAME2);
1387 return this.sISO639Language2;
1391 // abbreviated windows language name (ie: enu) (non-standard, avoid this)
1392 internal String SABBREVLANGNAME
1394 [System.Security.SecurityCritical] // auto-generated
1397 if (this.sAbbrevLang == null)
1399 this.sAbbrevLang = DoGetLocaleInfo(LOCALE_SABBREVLANGNAME);
1401 return this.sAbbrevLang;
1406 // Localized name for this language (Windows Only) ie: Inglis
1407 // This is only valid for Windows 8 and higher neutrals:
1408 internal String SLOCALIZEDLANGUAGE
1410 [System.Security.SecurityCritical] // auto-generated
1413 if (this.sLocalizedLanguage == null)
1415 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1417 this.sLocalizedLanguage = DoGetLocaleInfo(LOCALE_SLOCALIZEDLANGUAGENAME);
1419 // Some OS's might not have this resource or LCTYPE
1420 if (String.IsNullOrEmpty(this.sLocalizedLanguage))
1422 this.sLocalizedLanguage = SNATIVELANGUAGE;
1426 return this.sLocalizedLanguage;
1430 // English name for this language (Windows Only) ie: German
1431 internal String SENGLISHLANGUAGE
1433 [System.Security.SecurityCritical] // auto-generated
1436 if (this.sEnglishLanguage == null)
1438 this.sEnglishLanguage = DoGetLocaleInfo(LOCALE_SENGLISHLANGUAGENAME);
1440 return this.sEnglishLanguage;
1444 // Native name of this language (Windows Only) ie: Deutsch
1445 internal String SNATIVELANGUAGE
1447 [System.Security.SecurityCritical] // auto-generated
1450 if (this.sNativeLanguage == null)
1452 #if !FEATURE_CORECLR
1453 if (IsIncorrectNativeLanguageForSinhala())
1456 this.sNativeLanguage = "\x0dc3\x0dd2\x0d82\x0dc4\x0dbd";
1461 this.sNativeLanguage = DoGetLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
1464 return this.sNativeLanguage;
1468 #if !FEATURE_CORECLR
1469 private bool IsIncorrectNativeLanguageForSinhala()
1471 return IsOsWin7OrPrior()
1472 && (sName == "si-LK" || sName == "si")
1473 && !IsReplacementCulture;
1481 // region name (eg US)
1482 internal String SREGIONNAME
1484 [System.Security.SecurityCritical] // auto-generated
1487 if (this.sRegionName == null)
1489 this.sRegionName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1491 return this.sRegionName;
1495 // (user can override) ---- code (RegionInfo)
1496 #if !FEATURE_CORECLR
1497 internal int ICOUNTRY
1501 return DoGetLocaleInfoInt(LOCALE_ICOUNTRY);
1511 if (this.iGeoId == undef)
1513 this.iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID);
1519 // localized name for the ----
1520 internal string SLOCALIZEDCOUNTRY
1522 [System.Security.SecurityCritical] // auto-generated
1525 if (this.sLocalizedCountry == null)
1527 #if !FEATURE_CORECLR
1528 if (this.IsSupplementalCustomCulture)
1530 this.sLocalizedCountry = SNATIVECOUNTRY;
1534 String resourceKey = "Globalization.ri_" + this.SREGIONNAME;
1535 if (IsResourcePresent(resourceKey))
1537 this.sLocalizedCountry = Environment.GetResourceString(resourceKey);
1541 // If it hasn't been found (Windows 8 and up), fallback to the system
1542 if (String.IsNullOrEmpty(this.sLocalizedCountry))
1544 // We have to make the neutral distinction in case the OS returns a specific name
1545 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1547 this.sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
1549 if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1551 this.sLocalizedCountry = SNATIVECOUNTRY;
1555 return this.sLocalizedCountry;
1559 // english ---- name (RegionInfo) ie: Germany
1560 internal String SENGCOUNTRY
1562 [System.Security.SecurityCritical] // auto-generated
1565 if (this.sEnglishCountry == null)
1567 this.sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME);
1569 return this.sEnglishCountry;
1573 // native ---- name (RegionInfo) ie: Deutschland
1574 internal String SNATIVECOUNTRY
1576 [System.Security.SecurityCritical] // auto-generated
1579 if (this.sNativeCountry == null)
1581 this.sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
1583 return this.sNativeCountry;
1587 // ISO 3166 ---- Name
1588 internal String SISO3166CTRYNAME
1590 [System.Security.SecurityCritical] // auto-generated
1593 if (this.sISO3166CountryName == null)
1595 this.sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1597 return this.sISO3166CountryName;
1601 #if !FEATURE_CORECLR
1602 // ISO 3166 ---- Name
1603 internal String SISO3166CTRYNAME2
1605 [System.Security.SecurityCritical] // auto-generated
1608 if (this.sISO3166CountryName2 == null)
1610 this.sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2);
1612 return this.sISO3166CountryName2;
1616 // abbreviated ---- Name (windows version, non-standard, avoid)
1617 internal String SABBREVCTRYNAME
1619 [System.Security.SecurityCritical] // auto-generated
1622 if (this.sAbbrevCountry == null)
1624 this.sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME);
1626 return this.sAbbrevCountry;
1630 #if !FEATURE_CORECLR
1632 private int IDEFAULTCOUNTRY
1636 return DoGetLocaleInfoInt(LOCALE_IDEFAULTCOUNTRY);
1641 // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1642 internal int IINPUTLANGUAGEHANDLE
1646 if (this.iInputLanguageHandle == undef)
1648 if (IsSupplementalCustomCulture)
1651 this.iInputLanguageHandle = 0x0409;
1655 // Input Language is same as LCID for built-in cultures
1656 this.iInputLanguageHandle = this.ILANGUAGE;
1659 return this.iInputLanguageHandle;
1663 // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1664 internal String SCONSOLEFALLBACKNAME
1666 [System.Security.SecurityCritical] // auto-generated
1669 if (this.sConsoleFallbackName == null)
1671 string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME);
1672 if (consoleFallbackName == "es-ES_tradnl")
1674 consoleFallbackName = "es-ES";
1676 this.sConsoleFallbackName = consoleFallbackName;
1678 return this.sConsoleFallbackName;
1687 // internal String sPositiveSign ; // (user can override) positive sign
1688 // internal String sNegativeSign ; // (user can override) negative sign
1689 // internal String[] saNativeDigits ; // (user can override) native characters for digits 0-9
1690 // internal int iDigitSubstitution ; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
1691 // internal int iDigits ; // (user can override) number of fractional digits
1692 // internal int iNegativeNumber ; // (user can override) negative number format
1694 #if !FEATURE_CORECLR
1696 private bool ILEADINGZEROS
1700 return (DoGetLocaleInfoInt(LOCALE_ILZERO) == 1);
1706 // (user can override) grouping of digits
1707 internal int[] WAGROUPING
1709 [System.Security.SecurityCritical] // auto-generated
1712 if (this.waGrouping == null
1713 #if !FEATURE_CORECLR
1718 this.waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING));
1720 return this.waGrouping;
1725 // internal String sDecimalSeparator ; // (user can override) decimal separator
1726 // internal String sThousandSeparator ; // (user can override) thousands separator
1729 internal String SNAN
1731 [System.Security.SecurityCritical] // auto-generated
1734 if (this.sNaN == null)
1736 this.sNaN = DoGetLocaleInfo(LOCALE_SNAN);
1743 internal String SPOSINFINITY
1745 [System.Security.SecurityCritical] // auto-generated
1748 if (this.sPositiveInfinity == null)
1750 this.sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY);
1752 return this.sPositiveInfinity;
1757 internal String SNEGINFINITY
1759 [System.Security.SecurityCritical] // auto-generated
1762 if (this.sNegativeInfinity == null)
1764 this.sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY);
1766 return this.sNegativeInfinity;
1775 // Negative Percent (0-3)
1776 internal int INEGATIVEPERCENT
1780 if (this.iNegativePercent == undef)
1782 // Note that <= Windows Vista this is synthesized by native code
1783 this.iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT);
1785 return this.iNegativePercent;
1789 // Positive Percent (0-11)
1790 internal int IPOSITIVEPERCENT
1794 if (this.iPositivePercent == undef)
1796 // Note that <= Windows Vista this is synthesized by native code
1797 this.iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT);
1799 return this.iPositivePercent;
1803 // Percent (%) symbol
1804 internal String SPERCENT
1806 [System.Security.SecurityCritical] // auto-generated
1809 if (this.sPercent == null)
1811 // Note that <= Windows Vista this is synthesized by native code
1812 this.sPercent = DoGetLocaleInfo(LOCALE_SPERCENT);
1814 return this.sPercent;
1818 // PerMille (‰) symbol
1819 internal String SPERMILLE
1821 [System.Security.SecurityCritical] // auto-generated
1824 if (this.sPerMille == null)
1826 // Note that <= Windows Vista this is synthesized by native code
1827 this.sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE);
1829 return this.sPerMille;
1837 // (user can override) local monetary symbol, eg: $
1838 internal String SCURRENCY
1840 [System.Security.SecurityCritical] // auto-generated
1843 if (this.sCurrency == null
1844 #if !FEATURE_CORECLR
1849 this.sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY);
1851 return this.sCurrency;
1855 // international monetary symbol (RegionInfo), eg: USD
1856 internal String SINTLSYMBOL
1858 [System.Security.SecurityCritical] // auto-generated
1861 if (this.sIntlMonetarySymbol == null)
1863 this.sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL);
1865 return this.sIntlMonetarySymbol;
1869 // English name for this currency (RegionInfo), eg: US Dollar
1870 internal String SENGLISHCURRENCY
1872 [System.Security.SecurityCritical] // auto-generated
1875 if (this.sEnglishCurrency == null)
1877 this.sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME);
1879 return this.sEnglishCurrency;
1883 // Native name for this currency (RegionInfo), eg: Schweiz Frank
1884 internal String SNATIVECURRENCY
1886 [System.Security.SecurityCritical] // auto-generated
1889 if (this.sNativeCurrency == null)
1891 this.sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME);
1893 return this.sNativeCurrency;
1897 // internal int iCurrencyDigits ; // (user can override) # local monetary fractional digits
1898 // internal int iCurrency ; // (user can override) positive currency format
1899 // internal int iNegativeCurrency ; // (user can override) negative currency format
1901 // (user can override) monetary grouping of digits
1902 internal int[] WAMONGROUPING
1904 [System.Security.SecurityCritical] // auto-generated
1907 if (this.waMonetaryGrouping == null
1908 #if !FEATURE_CORECLR
1913 this.waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING));
1915 return this.waMonetaryGrouping;
1919 // internal String sMonetaryDecimal ; // (user can override) monetary decimal separator
1920 // internal String sMonetaryThousand ; // (user can override) monetary thousands separator
1926 // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1927 internal int IMEASURE
1931 if (this.iMeasure == undef
1932 #if !FEATURE_CORECLR
1937 this.iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE);
1939 return this.iMeasure;
1943 // (user can override) list Separator
1944 internal String SLIST
1946 [System.Security.SecurityCritical] // auto-generated
1949 if (this.sListSeparator == null
1950 #if !FEATURE_CORECLR
1955 this.sListSeparator = DoGetLocaleInfo(LOCALE_SLIST);
1957 return this.sListSeparator;
1961 #if !FEATURE_CORECLR
1963 private int IPAPERSIZE
1967 return DoGetLocaleInfoInt(LOCALE_IPAPERSIZE);
1972 ////////////////////////////
1973 // Calendar/Time (Gregorian) //
1974 ////////////////////////////
1976 // (user can override) AM designator
1977 internal String SAM1159
1979 [System.Security.SecurityCritical] // auto-generated
1982 if (this.sAM1159 == null
1983 #if !FEATURE_CORECLR
1988 this.sAM1159 = DoGetLocaleInfo(LOCALE_S1159);
1990 return this.sAM1159;
1994 // (user can override) PM designator
1995 internal String SPM2359
1997 [System.Security.SecurityCritical] // auto-generated
2000 if (this.sPM2359 == null
2001 #if !FEATURE_CORECLR
2006 this.sPM2359 = DoGetLocaleInfo(LOCALE_S2359);
2008 return this.sPM2359;
2012 // (user can override) time format
2013 internal String[] LongTimes
2017 if (this.saLongTimes == null
2018 #if !FEATURE_CORECLR
2023 String[] longTimes = DoEnumTimeFormats();
2024 if (longTimes == null || longTimes.Length == 0)
2026 this.saLongTimes = Invariant.saLongTimes;
2030 this.saLongTimes = longTimes;
2033 return this.saLongTimes;
2037 // short time format
2038 // Short times (derived from long times format)
2040 internal String[] ShortTimes
2044 if (this.saShortTimes == null
2045 #if !FEATURE_CORECLR
2050 // Try to get the short times from the OS/culture.dll
2051 String[] shortTimes = DoEnumShortTimeFormats();
2053 if (shortTimes == null || shortTimes.Length == 0)
2056 // If we couldn't find short times, then compute them from long times
2057 // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
2059 shortTimes = DeriveShortTimesFromLong();
2062 // Found short times, use them
2063 this.saShortTimes = shortTimes;
2065 return this.saShortTimes;
2069 private string[] DeriveShortTimesFromLong()
2071 // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string
2072 // between it and the previous marker, if any. If its a short, unescaped separator,
2073 // then we don't retain that part.
2074 // We then check after the ss and remove anything before the next h,H,m,t...
2075 string[] shortTimes = new string[LongTimes.Length];
2077 for (int i = 0; i < LongTimes.Length; i++)
2079 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
2084 private static string StripSecondsFromPattern(string time)
2086 bool bEscape = false;
2087 int iLastToken = -1;
2090 for (int j = 0; j < time.Length; j++)
2092 // Change escape mode?
2093 if (time[j] == '\'')
2100 // See if there was a single \
2101 if (time[j] == '\\')
2115 // Check for seconds
2117 // Found seconds, see if there was something unescaped and short between
2118 // the last marker and the seconds. Windows says separator can be a
2119 // maximum of three characters (without null)
2120 // If 1st or last characters were ', then ignore it
2121 if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
2122 (time[iLastToken + 1] != '\'') &&
2123 (time[j - 1] != '\''))
2125 // There was something there we want to remember
2126 if (iLastToken >= 0)
2133 int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
2134 StringBuilder sb = new StringBuilder(time.Substring(0, j));
2139 sb.Append(time.Substring(endIndex));
2140 time = sb.ToString();
2152 private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
2154 bool bEscape = false;
2155 containsSpace = false;
2156 for (; index < time.Length; index++)
2158 switch (time[index])
2165 if (time[index] == ' ')
2167 containsSpace = true;
2171 containsSpace = true;
2184 containsSpace = false;
2188 // time duration format
2189 internal String[] SADURATION
2191 [System.Security.SecurityCritical] // auto-generated
2195 if (this.saDurationFormats == null)
2197 String durationFormat = DoGetLocaleInfo(LOCALE_SDURATION);
2198 this.saDurationFormats = new String[] { ReescapeWin32String(durationFormat) };
2200 return this.saDurationFormats;
2204 // (user can override) first day of week
2205 internal int IFIRSTDAYOFWEEK
2209 if (this.iFirstDayOfWeek == undef
2210 #if !FEATURE_CORECLR
2215 // Have to convert it from windows to .Net formats
2216 this.iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK));
2218 return this.iFirstDayOfWeek;
2222 // (user can override) first week of year
2223 internal int IFIRSTWEEKOFYEAR
2227 if (this.iFirstWeekOfYear == undef
2228 #if !FEATURE_CORECLR
2233 this.iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR);
2235 return this.iFirstWeekOfYear;
2239 // (user can override default only) short date format
2240 internal String[] ShortDates(int calendarId)
2242 return GetCalendar(calendarId).saShortDates;
2245 // (user can override default only) long date format
2246 internal String[] LongDates(int calendarId)
2248 return GetCalendar(calendarId).saLongDates;
2251 // (user can override) date year/month format.
2252 internal String[] YearMonths(int calendarId)
2254 return GetCalendar(calendarId).saYearMonths;
2258 internal string[] DayNames(int calendarId)
2260 return GetCalendar(calendarId).saDayNames;
2263 // abbreviated day names
2264 internal string[] AbbreviatedDayNames(int calendarId)
2266 // Get abbreviated day names for this calendar from the OS if necessary
2267 return GetCalendar(calendarId).saAbbrevDayNames;
2270 // The super short day names
2271 internal string[] SuperShortDayNames(int calendarId)
2273 return GetCalendar(calendarId).saSuperShortDayNames;
2277 internal string[] MonthNames(int calendarId)
2279 return GetCalendar(calendarId).saMonthNames;
2282 // Genitive month names
2283 internal string[] GenitiveMonthNames(int calendarId)
2285 return GetCalendar(calendarId).saMonthGenitiveNames;
2289 internal string[] AbbreviatedMonthNames(int calendarId)
2291 return GetCalendar(calendarId).saAbbrevMonthNames;
2294 // Genitive month names
2295 internal string[] AbbreviatedGenitiveMonthNames(int calendarId)
2297 return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
2300 // Leap year month names
2301 // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
2302 // the non-leap names skip the 7th name in the normal month name array
2303 internal string[] LeapYearMonthNames(int calendarId)
2305 return GetCalendar(calendarId).saLeapYearMonthNames;
2308 // month/day format (single string, no override)
2309 internal String MonthDay(int calendarId)
2311 return GetCalendar(calendarId).sMonthDay;
2320 // all available calendar type(s), The first one is the default calendar.
2321 internal int[] CalendarIds
2325 if (this.waCalendars == null)
2327 // We pass in an array of ints, and native side fills it up with count calendars.
2328 // We then have to copy that list to a new array of the right size.
2329 // Default calendar should be first
2331 int[] calendarInts = new int[23];
2332 Contract.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2333 int count = CalendarData.nativeGetCalendars(this.sWindowsName, this.bUseOverrides, calendarInts);
2335 // See if we had a calendar to add.
2338 // Failed for some reason, just grab Gregorian from Invariant
2339 this.waCalendars = Invariant.waCalendars;
2343 // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
2345 if (this.sWindowsName == "zh-TW")
2349 // Do we need to insert calendar 4?
2350 for (int i = 0; i < count; i++)
2352 // Stop if we found calendar four
2353 if (calendarInts[i] == Calendar.CAL_TAIWAN)
2360 // If not found then insert it
2363 // Insert it as the 2nd calendar
2365 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
2366 Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1);
2367 calendarInts[1] = Calendar.CAL_TAIWAN;
2371 // It worked, remember the list
2372 int[] temp = new int[count];
2373 Array.Copy(calendarInts, temp, count);
2375 // Want 1st calendar to be default
2376 // Prior to Vista the enumeration didn't have default calendar first
2377 // Only a coreclr concern, culture.dll does the right thing.
2379 if (temp.Length > 1)
2381 int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2390 this.waCalendars = temp;
2394 return this.waCalendars;
2398 // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number
2399 internal String CalendarName(int calendarId)
2402 return GetCalendar(calendarId).sNativeName;
2405 internal CalendarData GetCalendar(int calendarId)
2407 Contract.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
2408 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
2410 // arrays are 0 based, calendarIds are 1 based
2411 int calendarIndex = calendarId - 1;
2413 // Have to have calendars
2414 if (calendars == null)
2416 calendars = new CalendarData[CalendarData.MAX_CALENDARS];
2419 // we need the following local variable to avoid returning null
2420 // when another thread creates a new array of CalendarData (above)
2421 // right after we insert the newly created CalendarData (below)
2422 CalendarData calendarData = calendars[calendarIndex];
2423 // Make sure that calendar has data
2424 if (calendarData == null
2425 #if !FEATURE_CORECLR
2430 Contract.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2431 calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
2432 #if !FEATURE_CORECLR
2433 //Work around issue where Win7 data for MonthDay contains invalid two sets of data separated by semicolon
2434 //even though MonthDay is not enumerated
2435 if (IsOsWin7OrPrior() && !IsSupplementalCustomCulture && !IsReplacementCulture)
2437 calendarData.FixupWin7MonthDaySemicolonBug();
2440 calendars[calendarIndex] = calendarData;
2443 return calendarData;
2446 internal int CurrentEra(int calendarId)
2448 return GetCalendar(calendarId).iCurrentEra;
2452 // Text Information //
2456 internal bool IsRightToLeft
2460 // Returns one of the following 4 reading layout values:
2461 // 0 - Left to right (eg en-US)
2462 // 1 - Right to left (eg arabic locales)
2463 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2464 // 3 - Vertical top to bottom with columns proceeding to the right
2465 return (this.IREADINGLAYOUT == 1);
2470 // Returns one of the following 4 reading layout values:
2471 // 0 - Left to right (eg en-US)
2472 // 1 - Right to left (eg arabic locales)
2473 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2474 // 3 - Vertical top to bottom with columns proceeding to the right
2476 // If exposed as a public API, we'd have an enum with those 4 values
2477 private int IREADINGLAYOUT
2481 if (this.iReadingLayout == undef)
2483 Contract.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2484 this.iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT);
2487 return (this.iReadingLayout);
2491 // The TextInfo name never includes that alternate sort and is always specific
2492 // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
2495 // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
2496 // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
2497 // es-ES_tradnl -> es-ES
2498 internal String STEXTINFO // Text info name to use for text information
2500 #if !FEATURE_CORECLR
2501 [System.Security.SecuritySafeCritical]
2506 // Note: Custom cultures might point at another culture's textinfo, however windows knows how
2507 // to redirect it to the desired textinfo culture, so this is OK.
2508 Contract.Assert(this.sWindowsName != null, "[CultureData.STEXTINFO] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2509 return (this.sWindowsName);
2511 if (this.sTextInfo == null)
2513 // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2514 // It is also not supported downlevel without culture.dll.
2515 if (IsNeutralCulture || IsSupplementalCustomCulture)
2517 string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2518 this.sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME;
2521 if (this.sTextInfo == null)
2523 this.sTextInfo = this.SNAME; // removes alternate sort
2527 return this.sTextInfo;
2532 // Compare info name (including sorting key) to use if custom
2533 internal String SCOMPAREINFO
2535 #if !FEATURE_CORECLR
2536 [System.Security.SecuritySafeCritical]
2541 // Note: Custom cultures might point at another culture's compareinfo, however windows knows how
2542 // to redirect it to the desired compareinfo culture, so this is OK.
2543 Contract.Assert(this.sWindowsName != null, "[CultureData.SCOMPAREINFO] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2544 return (this.sWindowsName);
2546 if (this.sCompareInfo == null)
2548 // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2549 // It is also not supported downlevel without culture.dll.
2550 // We really only need it for the custom locale case though
2551 // since for all other cases, it is the same as sWindowsName
2552 if (IsSupplementalCustomCulture)
2554 this.sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2557 if (this.sCompareInfo == null)
2559 this.sCompareInfo = this.sWindowsName;
2563 return this.sCompareInfo;
2568 internal bool IsSupplementalCustomCulture
2575 return IsCustomCultureId(this.ILANGUAGE);
2580 #if !FEATURE_CORECLR
2581 // Typical Scripts for this locale (latn;cyrl; etc)
2584 private String SSCRIPTS
2586 [System.Security.SecuritySafeCritical] // auto-generated
2589 if (this.sScripts == null)
2591 this.sScripts = DoGetLocaleInfo(LOCALE_SSCRIPTS);
2593 return this.sScripts;
2597 private String SOPENTYPELANGUAGETAG
2599 [System.Security.SecuritySafeCritical] // auto-generated
2602 return DoGetLocaleInfo(LOCALE_SOPENTYPELANGUAGETAG);
2606 private String FONTSIGNATURE
2608 [System.Security.SecuritySafeCritical] // auto-generated
2611 if (this.fontSignature == null)
2613 this.fontSignature = DoGetLocaleInfo(LOCALE_FONTSIGNATURE);
2615 return this.fontSignature;
2619 private String SKEYBOARDSTOINSTALL
2621 [System.Security.SecuritySafeCritical] // auto-generated
2624 return DoGetLocaleInfo(LOCALE_SKEYBOARDSTOINSTALL);
2630 #if !FEATURE_CORECLR
2631 internal int IDEFAULTANSICODEPAGE // default ansi code page ID (ACP)
2635 if (this.iDefaultAnsiCodePage == undef)
2637 this.iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE);
2639 return this.iDefaultAnsiCodePage;
2643 internal int IDEFAULTOEMCODEPAGE // default oem code page ID (OCP or OEM)
2647 if (this.iDefaultOemCodePage == undef)
2649 this.iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE);
2651 return this.iDefaultOemCodePage;
2655 internal int IDEFAULTMACCODEPAGE // default macintosh code page
2659 if (this.iDefaultMacCodePage == undef)
2661 this.iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE);
2663 return this.iDefaultMacCodePage;
2667 internal int IDEFAULTEBCDICCODEPAGE // default EBCDIC code page
2671 if (this.iDefaultEbcdicCodePage == undef)
2673 this.iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE);
2675 return this.iDefaultEbcdicCodePage;
2679 // Obtain locale name from LCID
2680 // NOTE: This will get neutral names, unlike the OS API
2681 [System.Security.SecuritySafeCritical] // auto-generated
2682 [ResourceExposure(ResourceScope.None)]
2683 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2684 internal static extern int LocaleNameToLCID(String localeName);
2686 // These are desktop only, not coreclr
2687 // locale ID (0409), including sort information
2688 internal int ILANGUAGE
2692 if (this.iLanguage == 0)
2694 Contract.Assert(this.sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2695 this.iLanguage = LocaleNameToLCID(this.sRealName);
2697 return this.iLanguage;
2701 internal bool IsWin32Installed
2703 get { return this.bWin32Installed; }
2706 internal bool IsFramework
2708 get { return this.bFramework; }
2710 #endif // !FEATURE_CORECLR
2712 ////////////////////
2713 // Derived properties //
2714 ////////////////////
2716 internal bool IsNeutralCulture
2720 // NlsInfo::nativeInitCultureData told us if we're neutral or not
2721 return this.bNeutral;
2725 internal bool IsInvariantCulture
2729 return String.IsNullOrEmpty(this.SNAME);
2733 // Get an instance of our default calendar
2734 internal Calendar DefaultCalendar
2738 int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2739 if (defaultCalId == 0)
2741 defaultCalId = this.CalendarIds[0];
2744 return CultureInfo.GetCalendarInstance(defaultCalId);
2748 // All of our era names
2749 internal String[] EraNames(int calendarId)
2751 Contract.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
2753 return this.GetCalendar(calendarId).saEraNames;
2756 internal String[] AbbrevEraNames(int calendarId)
2758 Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2760 return this.GetCalendar(calendarId).saAbbrevEraNames;
2763 internal String[] AbbreviatedEnglishEraNames(int calendarId)
2765 Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2767 return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2770 // String array DEFAULTS
2771 // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2774 // Time separator (derived from time format)
2775 internal String TimeSeparator
2777 [System.Security.SecuritySafeCritical] // auto-generated
2780 if (sTimeSeparator == null
2781 #if !FEATURE_CORECLR
2786 string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT));
2787 if (String.IsNullOrEmpty(longTimeFormat))
2789 longTimeFormat = LongTimes[0];
2792 // Compute STIME from time format
2793 sTimeSeparator = GetTimeSeparator(longTimeFormat);
2795 return sTimeSeparator;
2799 // Date separator (derived from short date format)
2800 internal String DateSeparator(int calendarId)
2802 return GetDateSeparator(ShortDates(calendarId)[0]);
2805 //////////////////////////////////////
2806 // Helper Functions to get derived properties //
2807 //////////////////////////////////////
2809 ////////////////////////////////////////////////////////////////////////////
2811 // Unescape a NLS style quote string
2813 // This removes single quotes:
2819 // This removes the first \ of escaped characters:
2820 // fred\'s -> fred's
2824 // We don't build the stringbuilder unless we find a ' or a \. If we find a ' or a \, we
2825 // always build a stringbuilder because we need to remove the ' or \.
2827 ////////////////////////////////////////////////////////////////////////////
2828 static private String UnescapeNlsString(String str, int start, int end)
2830 Contract.Requires(str != null);
2831 Contract.Requires(start >= 0);
2832 Contract.Requires(end >= 0);
2833 StringBuilder result = null;
2835 for (int i = start; i < str.Length && i <= end; i++)
2842 result = new StringBuilder(str, start, i - start, str.Length);
2848 result = new StringBuilder(str, start, i - start, str.Length);
2853 result.Append(str[i]);
2859 result.Append(str[i]);
2866 return (str.Substring(start, end - start + 1));
2868 return (result.ToString());
2871 ////////////////////////////////////////////////////////////////////////////
2873 // Reescape a Win32 style quote string as a NLS+ style quoted string
2875 // This is also the escaping style used by custom culture data files
2877 // NLS+ uses \ to escape the next character, whether in a quoted string or
2878 // not, so we always have to change \ to \\.
2880 // NLS+ uses \' to escape a quote inside a quoted string so we have to change
2881 // '' to \' (if inside a quoted string)
2883 // We don't build the stringbuilder unless we find something to change
2884 ////////////////////////////////////////////////////////////////////////////
2885 static internal String ReescapeWin32String(String str)
2887 // If we don't have data, then don't try anything
2891 StringBuilder result = null;
2893 bool inQuote = false;
2894 for (int i = 0; i < str.Length; i++)
2899 // Already in quote?
2902 // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
2903 if (i + 1 < str.Length && str[i + 1] == '\'')
2905 // Found another ', so we have ''. Need to add \' instead.
2906 // 1st make sure we have our stringbuilder
2908 result = new StringBuilder(str, 0, i, str.Length * 2);
2910 // Append a \' and keep going (so we don't turn off quote mode)
2911 result.Append("\\'");
2916 // Turning off quote mode, fall through to add it
2921 // Found beginning quote, fall through to add it
2925 // Is there a single \ character?
2926 else if (str[i] == '\\')
2928 // Found a \, need to change it to \\
2929 // 1st make sure we have our stringbuilder
2931 result = new StringBuilder(str, 0, i, str.Length * 2);
2933 // Append our \\ to the string & continue
2934 result.Append("\\\\");
2938 // If we have a builder we need to add our character
2940 result.Append(str[i]);
2943 // Unchanged string? , just return input string
2947 // String changed, need to use the builder
2948 return result.ToString();
2951 static internal String[] ReescapeWin32Strings(String[] array)
2955 for (int i = 0; i < array.Length; i++)
2957 array[i] = ReescapeWin32String(array[i]);
2964 // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2965 // and breaking changes here will not show up at build time, only at run time.
2966 static private String GetTimeSeparator(String format)
2968 // Time format separator (ie: : in 12:39:00)
2970 // We calculate this from the provided time format
2974 // Find the time separator so that we can pretend we know STIME.
2976 return GetSeparator(format, "Hhms");
2979 // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2980 // and breaking changes here will not show up at build time, only at run time.
2981 static private String GetDateSeparator(String format)
2983 // Date format separator (ie: / in 9/1/03)
2985 // We calculate this from the provided short date
2989 // Find the date separator so that we can pretend we know SDATE.
2991 return GetSeparator(format, "dyM");
2994 private static string GetSeparator(string format, string timeParts)
2996 int index = IndexOfTimePart(format, 0, timeParts);
3000 // Found a time part, find out when it changes
3001 char cTimePart = format[index];
3006 } while (index < format.Length && format[index] == cTimePart);
3008 int separatorStart = index;
3010 // Now we need to find the end of the separator
3011 if (separatorStart < format.Length)
3013 int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
3014 if (separatorEnd != -1)
3016 // From [separatorStart, count) is our string, except we need to unescape
3017 return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
3022 return String.Empty;
3025 private static int IndexOfTimePart(string format, int startIndex, string timeParts)
3027 Contract.Assert(startIndex >= 0, "startIndex cannot be negative");
3028 Contract.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
3029 bool inQuote = false;
3030 for (int i = startIndex; i < format.Length; ++i)
3032 // See if we have a time Part
3033 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
3040 if (i + 1 < format.Length)
3049 --i; //backup since we will move over this next
3063 [System.Security.SecurityCritical]
3064 string DoGetLocaleInfo(uint lctype)
3066 Contract.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
3067 return DoGetLocaleInfo(this.sWindowsName, lctype);
3070 // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
3071 // "windows" name, which can be specific for downlevel (< windows 7) os's.
3072 [System.Security.SecurityCritical] // auto-generated
3073 string DoGetLocaleInfo(string localeName, uint lctype)
3075 // Fix lctype if we don't want overrides
3076 if (!UseUserOverride)
3078 lctype |= LOCALE_NOUSEROVERRIDE;
3082 Contract.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null");
3083 string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
3086 // Failed, just use empty string
3087 result = String.Empty;
3093 int DoGetLocaleInfoInt(uint lctype)
3095 // Fix lctype if we don't want overrides
3096 if (!UseUserOverride)
3098 lctype |= LOCALE_NOUSEROVERRIDE;
3101 // Ask OS for data, note that we presume it returns success, so we have to know that
3102 // sWindowsName is valid before calling.
3103 Contract.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
3104 int result = CultureInfo.nativeGetLocaleInfoExInt(this.sWindowsName, lctype);
3109 String[] DoEnumTimeFormats()
3111 // Note that this gets overrides for us all the time
3112 Contract.Assert(this.sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
3113 String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, 0, UseUserOverride));
3118 String[] DoEnumShortTimeFormats()
3120 // Note that this gets overrides for us all the time
3121 Contract.Assert(this.sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
3122 String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, TIME_NOSECONDS, UseUserOverride));
3128 // Static Helpers //
3130 internal static bool IsCustomCultureId(int cultureId)
3132 if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
3138 ////////////////////////////////////////////////////////////////////////////
3141 // calendarValueOnly Retrieve the values which are affected by the calendar change of DTFI.
3142 // This will cause values like longTimePattern not be retrieved since it is
3143 // not affected by the Calendar property in DTFI.
3145 ////////////////////////////////////////////////////////////////////////////
3146 [System.Security.SecurityCritical] // auto-generated
3147 internal void GetNFIValues(NumberFormatInfo nfi)
3149 if (this.IsInvariantCulture)
3151 nfi.positiveSign = this.sPositiveSign;
3152 nfi.negativeSign = this.sNegativeSign;
3154 #if !FEATURE_CORECLR
3155 nfi.nativeDigits = this.saNativeDigits;
3156 nfi.digitSubstitution = this.iDigitSubstitution;
3157 #endif // !FEATURE_CORECLR
3159 nfi.numberGroupSeparator = this.sThousandSeparator;
3160 nfi.numberDecimalSeparator = this.sDecimalSeparator;
3161 nfi.numberDecimalDigits = this.iDigits;
3162 nfi.numberNegativePattern = this.iNegativeNumber;
3164 nfi.currencySymbol = this.sCurrency;
3165 nfi.currencyGroupSeparator = this.sMonetaryThousand;
3166 nfi.currencyDecimalSeparator = this.sMonetaryDecimal;
3167 nfi.currencyDecimalDigits = this.iCurrencyDigits;
3168 nfi.currencyNegativePattern = this.iNegativeCurrency;
3169 nfi.currencyPositivePattern = this.iCurrency;
3174 // We don't have information for the following four. All cultures use
3175 // the same value of the number formatting values.
3177 // PercentDecimalDigits
3178 // PercentDecimalSeparator
3180 // PercentGroupSeparator
3184 // Ask native side for our data.
3186 Contract.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
3187 CultureData.nativeGetNumberFormatInfoValues(this.sWindowsName, nfi, UseUserOverride);
3192 // Gather additional data
3194 nfi.numberGroupSizes = this.WAGROUPING;
3195 nfi.currencyGroupSizes = this.WAMONGROUPING;
3197 // prefer the cached value since these do not have user overrides
3198 nfi.percentNegativePattern = this.INEGATIVEPERCENT;
3199 nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
3200 nfi.percentSymbol = this.SPERCENT;
3201 nfi.perMilleSymbol = this.SPERMILLE;
3203 nfi.negativeInfinitySymbol = this.SNEGINFINITY;
3204 nfi.positiveInfinitySymbol = this.SPOSINFINITY;
3205 nfi.nanSymbol = this.SNAN;
3208 // We don't have percent values, so use the number values
3210 nfi.percentDecimalDigits = nfi.numberDecimalDigits;
3211 nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
3212 nfi.percentGroupSizes = nfi.numberGroupSizes;
3213 nfi.percentGroupSeparator = nfi.numberGroupSeparator;
3216 // Clean up a few odd values
3219 // Windows usually returns an empty positive sign, but we like it to be "+"
3220 if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
3222 //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user
3223 //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
3224 //decimal point doesn't show up. We'll just hack this here because our default currency format will never use nfi.
3225 if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
3227 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
3230 #if !FEATURE_CORECLR
3232 if ((932 == this.IDEFAULTANSICODEPAGE) ||
3233 (949 == this.IDEFAULTANSICODEPAGE))
3235 // Legacy behavior for cultures that use Japanese/Korean default ANSI code pages
3236 // Note that this is a code point, not a character. On Japanese/Korean machines this
3237 // will be rendered as their currency symbol, not rendered as a "\"
3238 nfi.ansiCurrencySymbol = "\x5c";
3240 #endif // !FEATURE_CORECLR
3243 static private int ConvertFirstDayOfWeekMonToSun(int iTemp)
3245 // Convert Mon-Sun to Sun-Sat format
3249 // Wrap Sunday and convert invalid data to Sunday
3256 // This is ONLY used for caching names and shouldn't be used for anything else
3257 internal static string AnsiToLower(string testString)
3259 StringBuilder sb = new StringBuilder(testString.Length);
3261 for (int ich = 0; ich < testString.Length; ich++)
3263 char ch = testString[ich];
3264 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
3267 return (sb.ToString());
3270 // If we get a group from windows, then its in 3;0 format with the 0 backwards
3271 // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
3272 // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
3273 static private int[] ConvertWin32GroupString(String win32Str)
3275 // None of these cases make any sense
3276 if (win32Str == null || win32Str.Length == 0)
3278 return (new int[] { 3 });
3281 if (win32Str[0] == '0')
3283 return (new int[] { 0 });
3286 // Since its in n;n;n;n;n format, we can always get the length quickly
3288 if (win32Str[win32Str.Length - 1] == '0')
3290 // Trailing 0 gets dropped. 1;0 -> 1
3291 values = new int[(win32Str.Length / 2)];
3295 // Need extra space for trailing zero 1 -> 1;0
3296 values = new int[(win32Str.Length / 2) + 2];
3297 values[values.Length - 1] = 0;
3302 for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
3304 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
3305 // But we'll test because its registry that could be anything
3306 if (win32Str[i] < '1' || win32Str[i] > '9')
3307 return new int[] { 3 };
3309 values[j] = (int)(win32Str[i] - '0');
3315 // LCTYPES for GetLocaleInfo
3316 private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; // do not use user overrides
3317 private const uint LOCALE_RETURN_NUMBER = 0x20000000; // return number instead of string
3319 // Modifier for genitive names
3320 private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000; //Flag to return the Genitive forms of month names
3323 // The following LCTypes are mutually exclusive in that they may NOT
3324 // be used in combination with each other.
3328 // These are the various forms of the name of the locale:
3330 private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002; // localized name of locale, eg "German (Germany)" in UI language
3331 private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072; // Display name (language + country usually) in English, eg "German (Germany)"
3332 private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073; // Display name in native locale language, eg "Deutsch (Deutschland)
3334 private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f; // Language Display Name for a language, eg "German" in UI language
3335 private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001; // English name of language, eg "German"
3336 private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004; // native name of language, eg "Deutsch"
3338 private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006; // localized name of country, eg "Germany" in UI language
3339 private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002; // English name of country, eg "Germany"
3340 private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008; // native name of country, eg "Deutschland"
3343 // private const uint LOCALE_ILANGUAGE =0x00000001; // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals)
3345 // private const uint LOCALE_SLANGUAGE =LOCALE_SLOCALIZEDDISPLAYNAME; // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead)
3346 // private const uint LOCALE_SENGLANGUAGE =LOCALE_SENGLISHLANGUAGENAME; // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead)
3347 private const uint LOCALE_SABBREVLANGNAME = 0x00000003; // abbreviated language name
3348 // private const uint LOCALE_SNATIVELANGNAME =LOCALE_SNATIVELANGUAGENAME; // native name of language (use LOCALE_SNATIVELANGUAGENAME instead)
3350 private const uint LOCALE_ICOUNTRY = 0x00000005; // country code
3351 // private const uint LOCALE_SCOUNTRY =LOCALE_SLOCALIZEDCOUNTRYNAME; // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead)
3352 // private const uint LOCALE_SENGCOUNTRY =LOCALE_SENGLISHCOUNTRYNAME; // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead)
3353 private const uint LOCALE_SABBREVCTRYNAME = 0x00000007; // abbreviated country name
3354 // private const uint LOCALE_SNATIVECTRYNAME =LOCALE_SNATIVECOUNTRYNAME; // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead)
3355 private const uint LOCALE_IGEOID = 0x0000005B; // geographical location id
3357 private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009; // default language id
3358 private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A; // default country code
3359 private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B; // default oem code page
3360 private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004; // default ansi code page
3361 private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011; // default mac code page
3363 private const uint LOCALE_SLIST = 0x0000000C; // list item separator
3364 private const uint LOCALE_IMEASURE = 0x0000000D; // 0 = metric, 1 = US
3366 private const uint LOCALE_SDECIMAL = 0x0000000E; // decimal separator
3367 private const uint LOCALE_STHOUSAND = 0x0000000F; // thousand separator
3368 private const uint LOCALE_SGROUPING = 0x00000010; // digit grouping
3369 private const uint LOCALE_IDIGITS = 0x00000011; // number of fractional digits
3370 private const uint LOCALE_ILZERO = 0x00000012; // leading zeros for decimal
3371 private const uint LOCALE_INEGNUMBER = 0x00001010; // negative number mode
3372 private const uint LOCALE_SNATIVEDIGITS = 0x00000013; // native digits for 0-9
3374 private const uint LOCALE_SCURRENCY = 0x00000014; // local monetary symbol
3375 private const uint LOCALE_SINTLSYMBOL = 0x00000015; // uintl monetary symbol
3376 private const uint LOCALE_SMONDECIMALSEP = 0x00000016; // monetary decimal separator
3377 private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017; // monetary thousand separator
3378 private const uint LOCALE_SMONGROUPING = 0x00000018; // monetary grouping
3379 private const uint LOCALE_ICURRDIGITS = 0x00000019; // # local monetary digits
3380 private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A; // # uintl monetary digits
3381 private const uint LOCALE_ICURRENCY = 0x0000001B; // positive currency mode
3382 private const uint LOCALE_INEGCURR = 0x0000001C; // negative currency mode
3384 private const uint LOCALE_SDATE = 0x0000001D; // date separator (derived from LOCALE_SSHORTDATE, use that instead)
3385 private const uint LOCALE_STIME = 0x0000001E; // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
3386 private const uint LOCALE_SSHORTDATE = 0x0000001F; // short date format string
3387 private const uint LOCALE_SLONGDATE = 0x00000020; // long date format string
3388 private const uint LOCALE_STIMEFORMAT = 0x00001003; // time format string
3389 private const uint LOCALE_IDATE = 0x00000021; // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead)
3390 private const uint LOCALE_ILDATE = 0x00000022; // long date format ordering (derived from LOCALE_SLONGDATE, use that instead)
3391 private const uint LOCALE_ITIME = 0x00000023; // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead)
3392 private const uint LOCALE_ITIMEMARKPOSN = 0x00001005; // time marker position (derived from LOCALE_STIMEFORMAT, use that instead)
3393 private const uint LOCALE_ICENTURY = 0x00000024; // century format specifier (short date, LOCALE_SSHORTDATE is preferred)
3394 private const uint LOCALE_ITLZERO = 0x00000025; // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead)
3395 private const uint LOCALE_IDAYLZERO = 0x00000026; // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred)
3396 private const uint LOCALE_IMONLZERO = 0x00000027; // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred)
3397 private const uint LOCALE_S1159 = 0x00000028; // AM designator
3398 private const uint LOCALE_S2359 = 0x00000029; // PM designator
3400 private const uint LOCALE_ICALENDARTYPE = 0x00001009; // type of calendar specifier
3401 private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B; // additional calendar types specifier
3402 private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; // first day of week specifier
3403 private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D; // first week of year specifier
3405 private const uint LOCALE_SDAYNAME1 = 0x0000002A; // long name for Monday
3406 private const uint LOCALE_SDAYNAME2 = 0x0000002B; // long name for Tuesday
3407 private const uint LOCALE_SDAYNAME3 = 0x0000002C; // long name for Wednesday
3408 private const uint LOCALE_SDAYNAME4 = 0x0000002D; // long name for Thursday
3409 private const uint LOCALE_SDAYNAME5 = 0x0000002E; // long name for Friday
3410 private const uint LOCALE_SDAYNAME6 = 0x0000002F; // long name for Saturday
3411 private const uint LOCALE_SDAYNAME7 = 0x00000030; // long name for Sunday
3412 private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031; // abbreviated name for Monday
3413 private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032; // abbreviated name for Tuesday
3414 private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033; // abbreviated name for Wednesday
3415 private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034; // abbreviated name for Thursday
3416 private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035; // abbreviated name for Friday
3417 private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036; // abbreviated name for Saturday
3418 private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037; // abbreviated name for Sunday
3419 private const uint LOCALE_SMONTHNAME1 = 0x00000038; // long name for January
3420 private const uint LOCALE_SMONTHNAME2 = 0x00000039; // long name for February
3421 private const uint LOCALE_SMONTHNAME3 = 0x0000003A; // long name for March
3422 private const uint LOCALE_SMONTHNAME4 = 0x0000003B; // long name for April
3423 private const uint LOCALE_SMONTHNAME5 = 0x0000003C; // long name for May
3424 private const uint LOCALE_SMONTHNAME6 = 0x0000003D; // long name for June
3425 private const uint LOCALE_SMONTHNAME7 = 0x0000003E; // long name for July
3426 private const uint LOCALE_SMONTHNAME8 = 0x0000003F; // long name for August
3427 private const uint LOCALE_SMONTHNAME9 = 0x00000040; // long name for September
3428 private const uint LOCALE_SMONTHNAME10 = 0x00000041; // long name for October
3429 private const uint LOCALE_SMONTHNAME11 = 0x00000042; // long name for November
3430 private const uint LOCALE_SMONTHNAME12 = 0x00000043; // long name for December
3431 private const uint LOCALE_SMONTHNAME13 = 0x0000100E; // long name for 13th month (if exists)
3432 private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044; // abbreviated name for January
3433 private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045; // abbreviated name for February
3434 private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046; // abbreviated name for March
3435 private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047; // abbreviated name for April
3436 private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048; // abbreviated name for May
3437 private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049; // abbreviated name for June
3438 private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A; // abbreviated name for July
3439 private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B; // abbreviated name for August
3440 private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C; // abbreviated name for September
3441 private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D; // abbreviated name for October
3442 private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E; // abbreviated name for November
3443 private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F; // abbreviated name for December
3444 private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F; // abbreviated name for 13th month (if exists)
3446 private const uint LOCALE_SPOSITIVESIGN = 0x00000050; // positive sign
3447 private const uint LOCALE_SNEGATIVESIGN = 0x00000051; // negative sign
3448 private const uint LOCALE_IPOSSIGNPOSN = 0x00000052; // positive sign position (derived from INEGCURR)
3449 private const uint LOCALE_INEGSIGNPOSN = 0x00000053; // negative sign position (derived from INEGCURR)
3450 private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054; // mon sym precedes pos amt (derived from ICURRENCY)
3451 private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055; // mon sym sep by space from pos amt (derived from ICURRENCY)
3452 private const uint LOCALE_INEGSYMPRECEDES = 0x00000056; // mon sym precedes neg amt (derived from INEGCURR)
3453 private const uint LOCALE_INEGSEPBYSPACE = 0x00000057; // mon sym sep by space from neg amt (derived from INEGCURR)
3455 private const uint LOCALE_FONTSIGNATURE = 0x00000058; // font signature
3456 private const uint LOCALE_SISO639LANGNAME = 0x00000059; // ISO abbreviated language name
3457 private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A; // ISO abbreviated country name
3459 private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012; // default ebcdic code page
3460 private const uint LOCALE_IPAPERSIZE = 0x0000100A; // 1 = letter, 5 = legal, 8 = a3, 9 = a4
3461 private const uint LOCALE_SENGCURRNAME = 0x00001007; // english name of currency
3462 private const uint LOCALE_SNATIVECURRNAME = 0x00001008; // native name of currency
3463 private const uint LOCALE_SYEARMONTH = 0x00001006; // year month format string
3464 private const uint LOCALE_SSORTNAME = 0x00001013; // sort name
3465 private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014; // 0 = context, 1 = none, 2 = national
3467 private const uint LOCALE_SNAME = 0x0000005c; // locale name (with sort info) (ie: de-DE_phoneb)
3468 private const uint LOCALE_SDURATION = 0x0000005d; // time duration format
3469 private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e; // (windows only) keyboards to install
3470 private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060; // Shortest day name for Monday
3471 private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061; // Shortest day name for Tuesday
3472 private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062; // Shortest day name for Wednesday
3473 private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063; // Shortest day name for Thursday
3474 private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064; // Shortest day name for Friday
3475 private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065; // Shortest day name for Saturday
3476 private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066; // Shortest day name for Sunday
3477 private const uint LOCALE_SISO639LANGNAME2 = 0x00000067; // 3 character ISO abbreviated language name
3478 private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068; // 3 character ISO country name
3479 private const uint LOCALE_SNAN = 0x00000069; // Not a Number
3480 private const uint LOCALE_SPOSINFINITY = 0x0000006a; // + Infinity
3481 private const uint LOCALE_SNEGINFINITY = 0x0000006b; // - Infinity
3482 private const uint LOCALE_SSCRIPTS = 0x0000006c; // Typical scripts in the locale
3483 private const uint LOCALE_SPARENT = 0x0000006d; // Fallback name for resources
3484 private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e; // Fallback name for within the console
3485 // private const uint LOCALE_SLANGDISPLAYNAME =LOCALE_SLOCALIZEDLANGUAGENAME; // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead)
3487 // Windows 7 LCTYPES
3488 private const uint LOCALE_IREADINGLAYOUT = 0x00000070; // Returns one of the following 4 reading layout values:
3489 // 0 - Left to right (eg en-US)
3490 // 1 - Right to left (eg arabic locales)
3491 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
3492 // 3 - Vertical top to bottom with columns proceeding to the right
3493 private const uint LOCALE_INEUTRAL = 0x00000071; // Returns 0 for specific cultures, 1 for neutral cultures.
3494 private const uint LOCALE_INEGATIVEPERCENT = 0x00000074; // Returns 0-11 for the negative percent format
3495 private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075; // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT
3496 private const uint LOCALE_SPERCENT = 0x00000076; // Returns the percent symbol
3497 private const uint LOCALE_SPERMILLE = 0x00000077; // Returns the permille (U+2030) symbol
3498 private const uint LOCALE_SMONTHDAY = 0x00000078; // Returns the preferred month/day format
3499 private const uint LOCALE_SSHORTTIME = 0x00000079; // Returns the preferred short time format (ie: no seconds, just h:mm)
3500 private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a; // Open type language tag, eg: "latn" or "dflt"
3501 private const uint LOCALE_SSORTLOCALE = 0x0000007b; // Name of locale to use for sorting/collation/casing behavior.
3503 // Time formats enumerations
3504 internal const uint TIME_NOSECONDS = 0x00000002; // Don't use seconds (get short time format for enumtimeformats on win7+)
3506 // Get our initial minimal culture data (name, parent, etc.)
3507 [System.Security.SecuritySafeCritical] // auto-generated
3508 [ResourceExposure(ResourceScope.None)]
3509 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3510 internal static extern bool nativeInitCultureData(CultureData cultureData);
3512 // Grab the NumberFormatInfo data
3513 [System.Security.SecuritySafeCritical] // auto-generated
3514 [ResourceExposure(ResourceScope.None)]
3515 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3516 internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride);
3518 [System.Security.SecuritySafeCritical] // auto-generated
3519 [ResourceExposure(ResourceScope.None)]
3520 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3521 private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride);
3523 [System.Security.SecurityCritical] // auto-generated
3524 [SuppressUnmanagedCodeSecurityAttribute()]
3525 [ResourceExposure(ResourceScope.None)]
3526 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
3527 internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray);