1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 namespace System.Globalization
8 using System.Collections;
9 using System.Collections.Generic;
11 using System.Threading;
12 using System.Runtime.CompilerServices;
13 using System.Runtime.InteropServices;
14 using System.Runtime.Versioning;
15 using System.Diagnostics;
16 using System.Diagnostics.Contracts;
17 using System.Security;
20 // List of culture data
21 // Note the we cache overrides.
22 // Note that localized names (resource names) aren't available from here.
26 // Our names are a tad confusing.
28 // sWindowsName -- The name that windows thinks this culture is, ie:
29 // en-US if you pass in en-US
30 // de-DE_phoneb if you pass in de-DE_phoneb
31 // fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
32 // fj if you pass in fj (neutral, post-Windows 7 machine)
34 // sRealName -- The name you used to construct the culture, in pretty form
35 // en-US if you pass in EN-us
36 // en if you pass in en
37 // de-DE_phoneb if you pass in de-DE_phoneb
39 // sSpecificCulture -- The specific culture for this culture
42 // de-DE_phoneb for alt sort
43 // fj-FJ for fj (neutral)
45 // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
46 // en-US if you pass in en-US
47 // en if you pass in en
48 // de-DE if you pass in de-DE_phoneb
51 // StructLayout is needed here otherwise compiler can re-arrange the fields.
52 // We have to keep this in-sync with the definition in comnlsinfo.h
54 // WARNING WARNING WARNING
56 // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CultureDataBaseObject)
57 // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureDataBaseObject
58 // WARNING: must be manually structured to match the true loaded class layout
61 internal class CultureData
63 private const int undef = -1;
66 private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
67 private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
70 private String sName; // locale name (ie: en-us, NO sort info, but could be neutral)
71 private String sParent; // Parent name (which may be a custom locale/culture)
72 private String sLocalizedDisplayName; // Localized pretty name for this locale
73 private String sEnglishDisplayName; // English pretty name for this locale
74 private String sNativeDisplayName; // Native pretty name for this locale
75 private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
78 private String sISO639Language; // ISO 639 Language Name
79 private String sLocalizedLanguage; // Localized name for this language
80 private String sEnglishLanguage; // English name for this language
81 private String sNativeLanguage; // Native name of this language
84 private String sRegionName; // (RegionInfo)
85 private int iGeoId = undef; // GeoId
86 private String sLocalizedCountry; // localized country name
87 private String sEnglishCountry; // english country name (RegionInfo)
88 private String sNativeCountry; // native country name
89 private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
92 private String sPositiveSign; // (user can override) positive sign
93 private String sNegativeSign; // (user can override) negative sign
94 private String[] saNativeDigits; // (user can override) native characters for digits 0-9
95 // (nfi populates these 5, don't have to be = undef)
96 private int iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
97 private int iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
98 private int iDigits; // (user can override) number of fractional digits
99 private int iNegativeNumber; // (user can override) negative number format
100 private int[] waGrouping; // (user can override) grouping of digits
101 private String sDecimalSeparator; // (user can override) decimal separator
102 private String sThousandSeparator; // (user can override) thousands separator
103 private String sNaN; // Not a Number
104 private String sPositiveInfinity; // + Infinity
105 private String sNegativeInfinity; // - Infinity
108 private int iNegativePercent = undef; // Negative Percent (0-3)
109 private int iPositivePercent = undef; // Positive Percent (0-11)
110 private String sPercent; // Percent (%) symbol
111 private String sPerMille; // PerMille (‰) symbol
114 private String sCurrency; // (user can override) local monetary symbol
115 private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
116 private String sEnglishCurrency; // English name for this currency
117 private String sNativeCurrency; // Native name for this currency
118 // (nfi populates these 4, don't have to be = undef)
119 private int iCurrencyDigits; // (user can override) # local monetary fractional digits
120 private int iCurrency; // (user can override) positive currency format
121 private int iNegativeCurrency; // (user can override) negative currency format
122 private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
123 private String sMonetaryDecimal; // (user can override) monetary decimal separator
124 private String sMonetaryThousand; // (user can override) monetary thousands separator
127 private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
128 private String sListSeparator; // (user can override) list separator
129 // private int iPaperSize ; // default paper size (RegionInfo)
132 private String sAM1159; // (user can override) AM designator
133 private String sPM2359; // (user can override) PM designator
134 private String sTimeSeparator;
135 private volatile String[] saLongTimes; // (user can override) time format
136 private volatile String[] saShortTimes; // short time format
137 private volatile String[] saDurationFormats; // time duration format
139 // Calendar specific data
140 private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
141 private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
142 private volatile int[] waCalendars; // all available calendar type(s). The first one is the default calendar
144 // Store for specific data about each calendar
145 private CalendarData[] calendars; // Store for specific calendar data
148 private int iReadingLayout = undef; // Reading layout data
149 // 0 - Left to right (eg en-US)
150 // 1 - Right to left (eg arabic locales)
151 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
152 // 3 - Vertical top to bottom with columns proceeding to the right
154 private String sTextInfo; // Text info name to use for custom
155 private String sCompareInfo; // Compare info name (including sorting key) to use if custom
156 private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
158 private int iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
159 private int iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
160 private int iDefaultMacCodePage = undef; // default macintosh code page
161 private int iDefaultEbcdicCodePage = undef; // default EBCDIC code page
163 // These are desktop only, not coreclr
164 private int iLanguage; // locale ID (0409) - NO sort information
165 private String sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
166 private String sAbbrevCountry; // abbreviated country name (RegionInfo) (Windows Region Name) ex: USA
167 private String sISO639Language2; // 3 char ISO 639 lang name 2 ex: eng
168 private String sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
169 private int iInputLanguageHandle = undef;// input language handle
170 private String sConsoleFallbackName; // The culture name for the console fallback UI culture
171 private String sKeyboardsToInstall; // Keyboard installation string.
172 private String fontSignature; // Font signature (16 WORDS)
174 // The bools all need to be in one spot
175 private bool bUseOverrides; // use user overrides?
176 private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
177 private bool bWin32Installed; // Flags indicate if the culture is Win32 installed
178 private bool bFramework; // Flags for indicate if the culture is one of Whidbey cultures
180 // Region Name to Culture Name mapping table
181 // (In future would be nice to be in registry or something)
183 //Using a property so we avoid creating the dictionary untill we need it
184 private static Dictionary<string, string> RegionNames
188 if (s_RegionNames == null)
190 var regionNames = new Dictionary<string, string> {
199 { "AZ", "az-Cyrl-AZ" },
200 { "BA", "bs-Latn-BA" },
216 { "CS", "sr-Cyrl-CS" },
267 { "ME", "sr-Latn-ME" },
292 { "RS", "sr-Latn-RS" },
304 { "TJ", "tg-Cyrl-TJ" },
313 { "UZ", "uz-Cyrl-UZ" },
320 s_RegionNames = regionNames;
322 return s_RegionNames;
325 private volatile static Dictionary<string, string> s_RegionNames;
327 /////////////////////////////////////////////////////////////////////////
328 // Build our invariant information
330 // We need an invariant instance, which we build hard-coded
331 /////////////////////////////////////////////////////////////////////////
332 internal static CultureData Invariant
336 if (s_Invariant == null)
338 // Make a new culturedata
339 CultureData invariant = new CultureData();
341 // Call the native code to get the value of bWin32Installed.
342 // For versions <= Vista, we set this to false for compatibility with v2.
343 // For Windows 7, the flag is true.
344 invariant.bUseOverrides = false;
345 invariant.sRealName = "";
347 // Ask the native code to fill it out for us, we only need the field IsWin32Installed
348 nativeInitCultureData(invariant);
351 // Note that we override the resources since this IS NOT supposed to change (by definition)
352 invariant.bUseOverrides = false;
353 invariant.sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
354 invariant.sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
357 invariant.sName = ""; // locale name (ie: en-us)
358 invariant.sParent = ""; // Parent name (which may be a custom locale/culture)
359 invariant.bNeutral = false; // Flags for the culture (ie: neutral or not right now)
360 // Don't set invariant.bWin32Installed, we used nativeInitCultureData for that.
361 invariant.bFramework = true;
363 invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
364 invariant.sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
365 invariant.sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
368 invariant.sISO639Language = "iv"; // ISO 639 Language Name
369 invariant.sLocalizedLanguage = "Invariant Language"; // Display name for this Language
370 invariant.sEnglishLanguage = "Invariant Language"; // English name for this language
371 invariant.sNativeLanguage = "Invariant Language"; // Native name of this language
374 invariant.sRegionName = "IV"; // (RegionInfo)
376 // invariant.iCountry =1; // country code (RegionInfo)
377 invariant.iGeoId = 244; // GeoId (Windows Only)
378 invariant.sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
379 invariant.sNativeCountry = "Invariant Country"; // native country name (Windows Only)
380 invariant.sISO3166CountryName = "IV"; // (RegionInfo), ie: US
383 invariant.sPositiveSign = "+"; // positive sign
384 invariant.sNegativeSign = "-"; // negative sign
385 invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9
386 invariant.iDigitSubstitution = 1; // Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
387 invariant.iLeadingZeros = 1; // leading zeros 0=no leading zeros, 1=leading zeros
388 invariant.iDigits = 2; // number of fractional digits
389 invariant.iNegativeNumber = 1; // negative number format
390 invariant.waGrouping = new int[] { 3 }; // grouping of digits
391 invariant.sDecimalSeparator = "."; // decimal separator
392 invariant.sThousandSeparator = ","; // thousands separator
393 invariant.sNaN = "NaN"; // Not a Number
394 invariant.sPositiveInfinity = "Infinity"; // + Infinity
395 invariant.sNegativeInfinity = "-Infinity"; // - Infinity
398 invariant.iNegativePercent = 0; // Negative Percent (0-3)
399 invariant.iPositivePercent = 0; // Positive Percent (0-11)
400 invariant.sPercent = "%"; // Percent (%) symbol
401 invariant.sPerMille = "\x2030"; // PerMille(‰) symbol
404 invariant.sCurrency = "\x00a4"; // local monetary symbol "¤: for international monetary symbol
405 invariant.sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
406 invariant.sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
407 invariant.sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
408 invariant.iCurrencyDigits = 2; // # local monetary fractional digits
409 invariant.iCurrency = 0; // positive currency format
410 invariant.iNegativeCurrency = 0; // negative currency format
411 invariant.waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
412 invariant.sMonetaryDecimal = "."; // monetary decimal separator
413 invariant.sMonetaryThousand = ","; // monetary thousands separator
416 invariant.iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
417 invariant.sListSeparator = ","; // list separator
419 // invariant.iPaperSize =9; // default paper size (RegionInfo)
420 // invariant.waFontSignature ="\x0002\x0000\x0000\x0000\x0000\x0000\x0000\x8000\x0001\x0000\x0000\x8000\x0001\x0000\x0000\x8000"; // Font signature (16 WORDS) (Windows Only)
423 invariant.sAM1159 = "AM"; // AM designator
424 invariant.sPM2359 = "PM"; // PM designator
425 invariant.saLongTimes = new String[] { "HH:mm:ss" }; // time format
426 invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
427 invariant.saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format
429 // Calendar specific data
430 invariant.iFirstDayOfWeek = 0; // first day of week
431 invariant.iFirstWeekOfYear = 0; // first week of year
432 invariant.waCalendars = new int[] { (int)CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
434 // Store for specific data about each calendar
435 invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS];
436 invariant.calendars[0] = CalendarData.Invariant;
439 invariant.iReadingLayout = 0; // Reading Layout = RTL
441 invariant.sTextInfo = ""; // Text info name to use for custom
442 invariant.sCompareInfo = ""; // Compare info name (including sorting key) to use if custom
443 invariant.sScripts = "Latn;"; // Typical Scripts for this locale (latn,cyrl, etc)
445 invariant.iLanguage = 0x007f; // locale ID (0409) - NO sort information
446 invariant.iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
447 invariant.iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
448 invariant.iDefaultMacCodePage = 10000; // default macintosh code page
449 invariant.iDefaultEbcdicCodePage = 037; // default EBCDIC code page
450 invariant.sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
451 invariant.sAbbrevCountry = "IVC"; // abbreviated country name (RegionInfo) (Windows Region Name)
452 invariant.sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
453 invariant.sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
454 invariant.iInputLanguageHandle = 0x007f; // input language handle
455 invariant.sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
456 invariant.sKeyboardsToInstall = "0409:00000409"; // Keyboard installation string.
458 s_Invariant = invariant;
463 private volatile static CultureData s_Invariant;
468 // Cache of cultures we've already looked up
469 private static volatile Dictionary<String, CultureData> s_cachedCultures;
471 [FriendAccessAllowed]
472 internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
474 // First do a shortcut for Invariant
475 if (String.IsNullOrEmpty(cultureName))
477 return CultureData.Invariant;
480 // Try the hash table first
481 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
482 Dictionary<String, CultureData> tempHashTable = s_cachedCultures;
483 if (tempHashTable == null)
485 // No table yet, make a new one
486 tempHashTable = new Dictionary<String, CultureData>();
490 // Check the hash table
492 lock (((ICollection)tempHashTable).SyncRoot)
494 tempHashTable.TryGetValue(hashName, out retVal);
502 // Not found in the hash table, need to see if we can build one that works for us
503 CultureData culture = CreateCultureData(cultureName, useUserOverride);
509 // Found one, add it to the cache
510 lock (((ICollection)tempHashTable).SyncRoot)
512 tempHashTable[hashName] = culture;
515 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
516 // new tables simultaneously created by a new thread, but maximizes thread safety.
517 s_cachedCultures = tempHashTable;
522 private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
524 CultureData culture = new CultureData();
525 culture.bUseOverrides = useUserOverride;
526 culture.sRealName = cultureName;
528 // Ask native code if that one's real
529 if (culture.InitCultureData() == false)
537 private bool InitCultureData()
539 if (nativeInitCultureData(this) == false)
546 // Cache of regions we've already looked up
547 private static volatile Dictionary<String, CultureData> s_cachedRegions;
549 internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
551 // First do a shortcut for Invariant
552 if (String.IsNullOrEmpty(cultureName))
554 return CultureData.Invariant;
558 // First check if GetCultureData() can find it (ie: its a real culture)
560 CultureData retVal = GetCultureData(cultureName, useUserOverride);
561 if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
564 // Not a specific culture, perhaps it's region-only name
565 // (Remember this isn't a core clr path where that's not supported)
568 // If it was neutral remember that so that RegionInfo() can throw the right exception
569 CultureData neutral = retVal;
571 // Try the hash table next
572 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
573 Dictionary<String, CultureData> tempHashTable = s_cachedRegions;
574 if (tempHashTable == null)
576 // No table yet, make a new one
577 tempHashTable = new Dictionary<String, CultureData>();
581 // Check the hash table
582 lock (((ICollection)tempHashTable).SyncRoot)
584 tempHashTable.TryGetValue(hashName, out retVal);
593 // Not found in the hash table, look it up the hard way
596 // If not a valid mapping from the registry we'll have to try the hard coded table
597 if (retVal == null || (retVal.IsNeutralCulture == true))
599 // Not a valid mapping, try the hard coded table
600 if (RegionNames.ContainsKey(cultureName))
602 // Make sure we can get culture data for it
603 retVal = GetCultureData(RegionNames[cultureName], useUserOverride);
607 // If not found in the hard coded table we'll have to find a culture that works for us
608 if (retVal == null || (retVal.IsNeutralCulture == true))
610 // Not found in the hard coded table, need to see if we can find a culture that works for us
611 // Not a real culture name, see if it matches a region name
612 // (we just return the first culture we match)
613 CultureInfo[] specifics = SpecificCultures;
614 for (int i = 0; i < specifics.Length; i++)
616 if (String.Compare(specifics[i].m_cultureData.SREGIONNAME, cultureName, StringComparison.OrdinalIgnoreCase) == 0)
618 // Matched, use this culture
619 retVal = specifics[i].m_cultureData;
625 // If we found one we can use, then cash it for next time
626 if (retVal != null && (retVal.IsNeutralCulture == false))
628 // first add it to the cache
629 lock (((ICollection)tempHashTable).SyncRoot)
631 tempHashTable[hashName] = retVal;
634 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
635 // new tables simultaneously created by a new thread, but maximizes thread safety.
636 s_cachedRegions = tempHashTable;
640 // Unable to find a matching culture/region, return null or neutral
641 // (regionInfo throws a more specific exception on neutrals)
645 // Return the found culture to use, null, or the neutral culture.
650 // Obtain locale name from LCID
651 // NOTE: This will get neutral names, unlike the OS API
652 [MethodImplAttribute(MethodImplOptions.InternalCall)]
653 internal static extern String LCIDToLocaleName(int lcid);
655 // We'd rather people use the named version since this doesn't allow custom locales
656 internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
658 String localeName = null;
659 CultureData retVal = null;
661 if (localeName == null)
663 // Convert the lcid to a name, then use that
664 // Note that this'll return neutral names (unlike Vista native API)
665 localeName = LCIDToLocaleName(culture);
668 // If its not valid, then throw
669 if (String.IsNullOrEmpty(localeName))
671 // Could be valid for Invariant
672 if (culture == 0x007f)
677 // Valid name, use it
678 retVal = GetCultureData(localeName, bUseUserOverride);
681 // If not successful, throw
683 throw new CultureNotFoundException(
684 nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported"));
686 // Return the one we found
691 // Clear our internal caches
692 internal static void ClearCachedData()
694 s_cachedCultures = null;
695 s_cachedRegions = null;
696 s_replacementCultureNames = null;
699 internal static CultureInfo[] GetCultures(CultureTypes types)
701 // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
702 #pragma warning disable 618
704 if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
705 CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
706 CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
707 CultureTypes.FrameworkCultures)) != 0)
709 throw new ArgumentOutOfRangeException(
712 CultureInfo.CurrentCulture,
713 Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
717 // CHANGE FROM Whidbey
719 // We have deprecated CultureTypes.FrameworkCultures.
720 // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
723 // We have deprecated CultureTypes.WindowsOnlyCultures.
724 // When this enum is used, we will return an empty array for this enum.
725 if ((types & CultureTypes.WindowsOnlyCultures) != 0)
727 // Remove the enum as it is an no-op.
728 types &= (~CultureTypes.WindowsOnlyCultures);
731 String[] cultureNames = null;
734 // Call nativeEnumCultureNames() to get a string array of culture names based on the specified
737 // nativeEnumCultureNames is a QCall. We need to use a reference to return the string array
738 // allocated from the QCall. That ref has to be wrapped as object handle.
739 // See vm\qcall.h for details in QCall.
742 if (nativeEnumCultureNames((int)types, JitHelpers.GetObjectHandleOnStack(ref cultureNames)) == 0)
744 return new CultureInfo[0];
747 int arrayLength = cultureNames.Length;
749 CultureInfo[] cultures = new CultureInfo[arrayLength];
751 for (int i = 0; i < cultureNames.Length; i++)
753 cultures[i] = new CultureInfo(cultureNames[i]);
755 #pragma warning restore 618
760 internal static volatile CultureInfo[] specificCultures;
762 private static CultureInfo[] SpecificCultures
766 if (specificCultures == null)
767 specificCultures = GetCultures(CultureTypes.SpecificCultures);
769 return specificCultures;
773 internal bool IsReplacementCulture
777 return IsReplacementCultureName(this.SNAME);
781 internal static volatile String[] s_replacementCultureNames;
783 ////////////////////////////////////////////////////////////////////////
785 // Cache for the known replacement cultures.
786 // This is used by CultureInfo.CultureType to check if a culture is a
787 // replacement culture.
789 ////////////////////////////////////////////////////////////////////////
792 private static bool IsReplacementCultureName(String name)
794 Debug.Assert(name != null, "IsReplacementCultureName(): name should not be null");
795 String[] replacementCultureNames = s_replacementCultureNames;
796 if (replacementCultureNames == null)
798 if (nativeEnumCultureNames((int)CultureTypes.ReplacementCultures, JitHelpers.GetObjectHandleOnStack(ref replacementCultureNames)) == 0)
803 // Even if we don't have any replacement cultures, the returned replacementCultureNames will still an empty string array, not null.
804 Debug.Assert(name != null, "IsReplacementCultureName(): replacementCultureNames should not be null");
805 Array.Sort(replacementCultureNames);
806 s_replacementCultureNames = replacementCultureNames;
808 return Array.BinarySearch(replacementCultureNames, name) >= 0;
811 ////////////////////////////////////////////////////////////////////////
815 // Accessors for our data object items
817 ////////////////////////////////////////////////////////////////////////
823 // The real name used to construct the locale (ie: de-DE_phoneb)
824 internal String CultureName
828 Debug.Assert(sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
829 // since windows doesn't know about zh-CHS and zh-CHT,
830 // we leave sRealName == zh-Hanx but we still need to
831 // pretend that it was zh-CHX.
842 // Are overrides enabled?
843 internal bool UseUserOverride
847 return bUseOverrides;
851 // locale name (ie: de-DE, NO sort information)
852 internal String SNAME
856 // Debug.Assert(this.sName != null,
857 // "[CultureData.SNAME] Expected this.sName to be populated by COMNlsInfo::nativeInitCultureData already");
860 sName = String.Empty;
866 // Parent name (which may be a custom locale/culture)
867 internal String SPARENT
873 // Ask using the real name, so that we get parents of neutrals
874 sParent = DoGetLocaleInfo(sRealName, LOCALE_SPARENT);
880 // Localized pretty name for this locale (ie: Inglis (estados Unitos))
881 internal String SLOCALIZEDDISPLAYNAME
885 if (sLocalizedDisplayName == null)
887 // If it hasn't been found (Windows 8 and up), fallback to the system
888 if (String.IsNullOrEmpty(sLocalizedDisplayName))
890 // If its neutral use the language name
891 if (this.IsNeutralCulture)
893 sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
897 // We have to make the neutral distinction in case the OS returns a specific name
898 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
900 sLocalizedDisplayName = DoGetLocaleInfo(LOCALE_SLOCALIZEDDISPLAYNAME);
902 if (String.IsNullOrEmpty(sLocalizedDisplayName))
904 sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
909 return sLocalizedDisplayName;
913 // English pretty name for this locale (ie: English (United States))
914 internal String SENGDISPLAYNAME
918 if (sEnglishDisplayName == null)
920 // If its neutral use the language name
921 if (this.IsNeutralCulture)
923 sEnglishDisplayName = this.SENGLISHLANGUAGE;
927 sEnglishDisplayName = DoGetLocaleInfo(LOCALE_SENGLISHDISPLAYNAME);
929 // if it isn't found build one:
930 if (String.IsNullOrEmpty(sEnglishDisplayName))
932 // Our existing names mostly look like:
933 // "English" + "United States" -> "English (United States)"
934 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
935 if (this.SENGLISHLANGUAGE.EndsWith(')'))
937 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
938 sEnglishDisplayName =
939 this.SENGLISHLANGUAGE.Substring(0, sEnglishLanguage.Length - 1) +
940 ", " + this.SENGCOUNTRY + ")";
944 // "English" + "United States" -> "English (United States)"
945 sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
950 return sEnglishDisplayName;
954 // Native pretty name for this locale (ie: Deutsch (Deutschland))
955 internal String SNATIVEDISPLAYNAME
959 if (sNativeDisplayName == null)
961 // If its neutral use the language name
962 if (this.IsNeutralCulture)
964 sNativeDisplayName = this.SNATIVELANGUAGE;
968 sNativeDisplayName = DoGetLocaleInfo(LOCALE_SNATIVEDISPLAYNAME);
970 // if it isn't found build one:
971 if (String.IsNullOrEmpty(sNativeDisplayName))
973 // These should primarily be "Deutsch (Deutschland)" type names
974 sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
978 return sNativeDisplayName;
982 // The culture name to be used in CultureInfo.CreateSpecificCulture()
983 internal String SSPECIFICCULTURE
987 // This got populated when ComNlsInfo::nativeInitCultureData told us we had a culture
988 Debug.Assert(sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by COMNlsInfo::nativeInitCultureData already");
989 return sSpecificCulture;
997 // iso 639 language name, ie: en
998 internal String SISO639LANGNAME
1002 if (sISO639Language == null)
1004 sISO639Language = DoGetLocaleInfo(LOCALE_SISO639LANGNAME);
1006 return sISO639Language;
1010 // iso 639 language name, ie: eng
1011 internal String SISO639LANGNAME2
1015 if (sISO639Language2 == null)
1017 sISO639Language2 = DoGetLocaleInfo(LOCALE_SISO639LANGNAME2);
1019 return sISO639Language2;
1023 // abbreviated windows language name (ie: enu) (non-standard, avoid this)
1024 internal String SABBREVLANGNAME
1028 if (sAbbrevLang == null)
1030 sAbbrevLang = DoGetLocaleInfo(LOCALE_SABBREVLANGNAME);
1036 // Localized name for this language (Windows Only) ie: Inglis
1037 // This is only valid for Windows 8 and higher neutrals:
1038 internal String SLOCALIZEDLANGUAGE
1042 if (sLocalizedLanguage == null)
1044 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1046 sLocalizedLanguage = DoGetLocaleInfo(LOCALE_SLOCALIZEDLANGUAGENAME);
1048 // Some OS's might not have this resource or LCTYPE
1049 if (String.IsNullOrEmpty(sLocalizedLanguage))
1051 sLocalizedLanguage = SNATIVELANGUAGE;
1055 return sLocalizedLanguage;
1059 // English name for this language (Windows Only) ie: German
1060 internal String SENGLISHLANGUAGE
1064 if (sEnglishLanguage == null)
1066 sEnglishLanguage = DoGetLocaleInfo(LOCALE_SENGLISHLANGUAGENAME);
1068 return sEnglishLanguage;
1072 // Native name of this language (Windows Only) ie: Deutsch
1073 internal String SNATIVELANGUAGE
1077 if (sNativeLanguage == null)
1080 sNativeLanguage = DoGetLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
1083 return sNativeLanguage;
1091 // region name (eg US)
1092 internal String SREGIONNAME
1096 if (sRegionName == null)
1098 sRegionName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1109 if (iGeoId == undef)
1111 iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID);
1117 // localized name for the country
1118 internal string SLOCALIZEDCOUNTRY
1122 if (sLocalizedCountry == null)
1124 // If it hasn't been found (Windows 8 and up), fallback to the system
1125 if (String.IsNullOrEmpty(sLocalizedCountry))
1127 // We have to make the neutral distinction in case the OS returns a specific name
1128 if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1130 sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
1132 if (String.IsNullOrEmpty(sLocalizedDisplayName))
1134 sLocalizedCountry = SNATIVECOUNTRY;
1138 return sLocalizedCountry;
1142 // english country name (RegionInfo) ie: Germany
1143 internal String SENGCOUNTRY
1147 if (sEnglishCountry == null)
1149 sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME);
1151 return sEnglishCountry;
1155 // native country name (RegionInfo) ie: Deutschland
1156 internal String SNATIVECOUNTRY
1160 if (sNativeCountry == null)
1162 sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
1164 return sNativeCountry;
1168 // ISO 3166 Country Name
1169 internal String SISO3166CTRYNAME
1173 if (sISO3166CountryName == null)
1175 sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1177 return sISO3166CountryName;
1181 // ISO 3166 Country Name
1182 internal String SISO3166CTRYNAME2
1186 if (sISO3166CountryName2 == null)
1188 sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2);
1190 return sISO3166CountryName2;
1194 // abbreviated Country Name (windows version, non-standard, avoid)
1195 internal String SABBREVCTRYNAME
1199 if (sAbbrevCountry == null)
1201 sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME);
1203 return sAbbrevCountry;
1207 // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1208 internal int IINPUTLANGUAGEHANDLE
1212 if (iInputLanguageHandle == undef)
1214 if (IsSupplementalCustomCulture)
1216 iInputLanguageHandle = 0x0409;
1220 // Input Language is same as LCID for built-in cultures
1221 iInputLanguageHandle = this.ILANGUAGE;
1224 return iInputLanguageHandle;
1228 // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1229 internal String SCONSOLEFALLBACKNAME
1233 if (sConsoleFallbackName == null)
1235 string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME);
1236 if (consoleFallbackName == "es-ES_tradnl")
1238 consoleFallbackName = "es-ES";
1240 sConsoleFallbackName = consoleFallbackName;
1242 return sConsoleFallbackName;
1247 // (user can override) grouping of digits
1248 internal int[] WAGROUPING
1252 if (waGrouping == null || UseUserOverride)
1254 waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING));
1261 // internal String sDecimalSeparator ; // (user can override) decimal separator
1262 // internal String sThousandSeparator ; // (user can override) thousands separator
1265 internal String SNAN
1271 sNaN = DoGetLocaleInfo(LOCALE_SNAN);
1278 internal String SPOSINFINITY
1282 if (sPositiveInfinity == null)
1284 sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY);
1286 return sPositiveInfinity;
1291 internal String SNEGINFINITY
1295 if (sNegativeInfinity == null)
1297 sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY);
1299 return sNegativeInfinity;
1308 // Negative Percent (0-3)
1309 internal int INEGATIVEPERCENT
1313 if (iNegativePercent == undef)
1315 // Note that <= Windows Vista this is synthesized by native code
1316 iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT);
1318 return iNegativePercent;
1322 // Positive Percent (0-11)
1323 internal int IPOSITIVEPERCENT
1327 if (iPositivePercent == undef)
1329 // Note that <= Windows Vista this is synthesized by native code
1330 iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT);
1332 return iPositivePercent;
1336 // Percent (%) symbol
1337 internal String SPERCENT
1341 if (sPercent == null)
1343 // Note that <= Windows Vista this is synthesized by native code
1344 sPercent = DoGetLocaleInfo(LOCALE_SPERCENT);
1350 // PerMille (‰) symbol
1351 internal String SPERMILLE
1355 if (sPerMille == null)
1357 // Note that <= Windows Vista this is synthesized by native code
1358 sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE);
1368 // (user can override) local monetary symbol, eg: $
1369 internal String SCURRENCY
1373 if (sCurrency == null || UseUserOverride)
1375 sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY);
1381 // international monetary symbol (RegionInfo), eg: USD
1382 internal String SINTLSYMBOL
1386 if (sIntlMonetarySymbol == null)
1388 sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL);
1390 return sIntlMonetarySymbol;
1394 // English name for this currency (RegionInfo), eg: US Dollar
1395 internal String SENGLISHCURRENCY
1399 if (sEnglishCurrency == null)
1401 sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME);
1403 return sEnglishCurrency;
1407 // Native name for this currency (RegionInfo), eg: Schweiz Frank
1408 internal String SNATIVECURRENCY
1412 if (sNativeCurrency == null)
1414 sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME);
1416 return sNativeCurrency;
1420 // internal int iCurrencyDigits ; // (user can override) # local monetary fractional digits
1421 // internal int iCurrency ; // (user can override) positive currency format
1422 // internal int iNegativeCurrency ; // (user can override) negative currency format
1424 // (user can override) monetary grouping of digits
1425 internal int[] WAMONGROUPING
1429 if (waMonetaryGrouping == null || UseUserOverride)
1431 waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING));
1433 return waMonetaryGrouping;
1437 // internal String sMonetaryDecimal ; // (user can override) monetary decimal separator
1438 // internal String sMonetaryThousand ; // (user can override) monetary thousands separator
1444 // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1445 internal int IMEASURE
1449 if (iMeasure == undef || UseUserOverride)
1451 iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE);
1457 // (user can override) list Separator
1458 internal String SLIST
1462 if (sListSeparator == null || UseUserOverride)
1464 sListSeparator = DoGetLocaleInfo(LOCALE_SLIST);
1466 return sListSeparator;
1470 ////////////////////////////
1471 // Calendar/Time (Gregorian) //
1472 ////////////////////////////
1474 // (user can override) AM designator
1475 internal String SAM1159
1479 if (sAM1159 == null || UseUserOverride)
1481 sAM1159 = DoGetLocaleInfo(LOCALE_S1159);
1487 // (user can override) PM designator
1488 internal String SPM2359
1492 if (sPM2359 == null || UseUserOverride)
1494 sPM2359 = DoGetLocaleInfo(LOCALE_S2359);
1500 // (user can override) time format
1501 internal String[] LongTimes
1505 if (saLongTimes == null || UseUserOverride)
1507 String[] longTimes = DoEnumTimeFormats();
1508 if (longTimes == null || longTimes.Length == 0)
1510 saLongTimes = Invariant.saLongTimes;
1514 saLongTimes = longTimes;
1521 // short time format
1522 // Short times (derived from long times format)
1523 internal String[] ShortTimes
1527 if (saShortTimes == null || UseUserOverride)
1529 // Try to get the short times from the OS/culture.dll
1530 String[] shortTimes = DoEnumShortTimeFormats();
1532 if (shortTimes == null || shortTimes.Length == 0)
1535 // If we couldn't find short times, then compute them from long times
1536 // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
1538 shortTimes = DeriveShortTimesFromLong();
1541 // Found short times, use them
1542 saShortTimes = shortTimes;
1544 return saShortTimes;
1548 private string[] DeriveShortTimesFromLong()
1550 // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string
1551 // between it and the previous marker, if any. If its a short, unescaped separator,
1552 // then we don't retain that part.
1553 // We then check after the ss and remove anything before the next h,H,m,t...
1554 string[] shortTimes = new string[LongTimes.Length];
1556 for (int i = 0; i < LongTimes.Length; i++)
1558 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
1563 private static string StripSecondsFromPattern(string time)
1565 bool bEscape = false;
1566 int iLastToken = -1;
1569 for (int j = 0; j < time.Length; j++)
1571 // Change escape mode?
1572 if (time[j] == '\'')
1579 // See if there was a single \
1580 if (time[j] == '\\')
1594 // Check for seconds
1596 // Found seconds, see if there was something unescaped and short between
1597 // the last marker and the seconds. Windows says separator can be a
1598 // maximum of three characters (without null)
1599 // If 1st or last characters were ', then ignore it
1600 if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
1601 (time[iLastToken + 1] != '\'') &&
1602 (time[j - 1] != '\''))
1604 // There was something there we want to remember
1605 if (iLastToken >= 0)
1612 int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
1613 StringBuilder sb = new StringBuilder(time.Substring(0, j));
1618 sb.Append(time.Substring(endIndex));
1619 time = sb.ToString();
1631 private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
1633 bool bEscape = false;
1634 containsSpace = false;
1635 for (; index < time.Length; index++)
1637 switch (time[index])
1644 if (time[index] == ' ')
1646 containsSpace = true;
1650 containsSpace = true;
1663 containsSpace = false;
1667 // (user can override) first day of week
1668 internal int IFIRSTDAYOFWEEK
1672 if (iFirstDayOfWeek == undef || UseUserOverride)
1674 // Have to convert it from windows to .Net formats
1675 iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK));
1677 return iFirstDayOfWeek;
1681 // (user can override) first week of year
1682 internal int IFIRSTWEEKOFYEAR
1686 if (iFirstWeekOfYear == undef || UseUserOverride)
1688 iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR);
1690 return iFirstWeekOfYear;
1694 // (user can override default only) short date format
1695 internal String[] ShortDates(int calendarId)
1697 return GetCalendar(calendarId).saShortDates;
1700 // (user can override default only) long date format
1701 internal String[] LongDates(int calendarId)
1703 return GetCalendar(calendarId).saLongDates;
1706 // (user can override) date year/month format.
1707 internal String[] YearMonths(int calendarId)
1709 return GetCalendar(calendarId).saYearMonths;
1713 internal string[] DayNames(int calendarId)
1715 return GetCalendar(calendarId).saDayNames;
1718 // abbreviated day names
1719 internal string[] AbbreviatedDayNames(int calendarId)
1721 // Get abbreviated day names for this calendar from the OS if necessary
1722 return GetCalendar(calendarId).saAbbrevDayNames;
1725 // The super short day names
1726 internal string[] SuperShortDayNames(int calendarId)
1728 return GetCalendar(calendarId).saSuperShortDayNames;
1732 internal string[] MonthNames(int calendarId)
1734 return GetCalendar(calendarId).saMonthNames;
1737 // Genitive month names
1738 internal string[] GenitiveMonthNames(int calendarId)
1740 return GetCalendar(calendarId).saMonthGenitiveNames;
1744 internal string[] AbbreviatedMonthNames(int calendarId)
1746 return GetCalendar(calendarId).saAbbrevMonthNames;
1749 // Genitive month names
1750 internal string[] AbbreviatedGenitiveMonthNames(int calendarId)
1752 return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
1755 // Leap year month names
1756 // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
1757 // the non-leap names skip the 7th name in the normal month name array
1758 internal string[] LeapYearMonthNames(int calendarId)
1760 return GetCalendar(calendarId).saLeapYearMonthNames;
1763 // month/day format (single string, no override)
1764 internal String MonthDay(int calendarId)
1766 return GetCalendar(calendarId).sMonthDay;
1775 // all available calendar type(s), The first one is the default calendar.
1776 internal int[] CalendarIds
1780 if (waCalendars == null)
1782 // We pass in an array of ints, and native side fills it up with count calendars.
1783 // We then have to copy that list to a new array of the right size.
1784 // Default calendar should be first
1785 int[] calendarInts = new int[23];
1786 Debug.Assert(sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1787 int count = CalendarData.nativeGetCalendars(sWindowsName, bUseOverrides, calendarInts);
1789 // See if we had a calendar to add.
1792 // Failed for some reason, just grab Gregorian from Invariant
1793 waCalendars = Invariant.waCalendars;
1797 // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
1798 if (sWindowsName == "zh-TW")
1802 // Do we need to insert calendar 4?
1803 for (int i = 0; i < count; i++)
1805 // Stop if we found calendar four
1806 if (calendarInts[i] == Calendar.CAL_TAIWAN)
1813 // If not found then insert it
1816 // Insert it as the 2nd calendar
1818 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
1819 Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1);
1820 calendarInts[1] = Calendar.CAL_TAIWAN;
1824 // It worked, remember the list
1825 int[] temp = new int[count];
1826 Array.Copy(calendarInts, temp, count);
1828 // Want 1st calendar to be default
1829 // Prior to Vista the enumeration didn't have default calendar first
1830 // Only a coreclr concern, culture.dll does the right thing.
1831 if (temp.Length > 1)
1833 int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
1849 // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number
1850 internal String CalendarName(int calendarId)
1853 return GetCalendar(calendarId).sNativeName;
1856 internal CalendarData GetCalendar(int calendarId)
1858 Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
1859 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
1861 // arrays are 0 based, calendarIds are 1 based
1862 int calendarIndex = calendarId - 1;
1864 // Have to have calendars
1865 if (calendars == null)
1867 calendars = new CalendarData[CalendarData.MAX_CALENDARS];
1870 // we need the following local variable to avoid returning null
1871 // when another thread creates a new array of CalendarData (above)
1872 // right after we insert the newly created CalendarData (below)
1873 CalendarData calendarData = calendars[calendarIndex];
1874 // Make sure that calendar has data
1875 if (calendarData == null || UseUserOverride)
1877 Debug.Assert(sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1878 calendarData = new CalendarData(sWindowsName, calendarId, this.UseUserOverride);
1879 calendars[calendarIndex] = calendarData;
1882 return calendarData;
1886 // Text Information //
1890 internal bool IsRightToLeft
1894 // Returns one of the following 4 reading layout values:
1895 // 0 - Left to right (eg en-US)
1896 // 1 - Right to left (eg arabic locales)
1897 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1898 // 3 - Vertical top to bottom with columns proceeding to the right
1899 return (this.IREADINGLAYOUT == 1);
1904 // Returns one of the following 4 reading layout values:
1905 // 0 - Left to right (eg en-US)
1906 // 1 - Right to left (eg arabic locales)
1907 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1908 // 3 - Vertical top to bottom with columns proceeding to the right
1910 // If exposed as a public API, we'd have an enum with those 4 values
1911 private int IREADINGLAYOUT
1915 if (iReadingLayout == undef)
1917 Debug.Assert(sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
1918 iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT);
1921 return (iReadingLayout);
1925 // The TextInfo name never includes that alternate sort and is always specific
1926 // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
1929 // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
1930 // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
1931 // es-ES_tradnl -> es-ES
1932 internal String STEXTINFO // Text info name to use for text information
1936 if (sTextInfo == null)
1938 // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
1939 // It is also not supported downlevel without culture.dll.
1940 if (IsNeutralCulture || IsSupplementalCustomCulture)
1942 string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
1943 sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME;
1946 if (sTextInfo == null)
1948 sTextInfo = this.SNAME; // removes alternate sort
1956 // Compare info name (including sorting key) to use if custom
1957 internal String SCOMPAREINFO
1961 if (sCompareInfo == null)
1963 // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
1964 // It is also not supported downlevel without culture.dll.
1965 // We really only need it for the custom locale case though
1966 // since for all other cases, it is the same as sWindowsName
1967 if (IsSupplementalCustomCulture)
1969 sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
1972 if (sCompareInfo == null)
1974 sCompareInfo = sWindowsName;
1978 return sCompareInfo;
1982 internal bool IsSupplementalCustomCulture
1986 return IsCustomCultureId(this.ILANGUAGE);
1991 internal int IDEFAULTANSICODEPAGE // default ansi code page ID (ACP)
1995 if (iDefaultAnsiCodePage == undef)
1997 iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE);
1999 return iDefaultAnsiCodePage;
2003 internal int IDEFAULTOEMCODEPAGE // default oem code page ID (OCP or OEM)
2007 if (iDefaultOemCodePage == undef)
2009 iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE);
2011 return iDefaultOemCodePage;
2015 internal int IDEFAULTMACCODEPAGE // default macintosh code page
2019 if (iDefaultMacCodePage == undef)
2021 iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE);
2023 return iDefaultMacCodePage;
2027 internal int IDEFAULTEBCDICCODEPAGE // default EBCDIC code page
2031 if (iDefaultEbcdicCodePage == undef)
2033 iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE);
2035 return iDefaultEbcdicCodePage;
2039 // Obtain locale name from LCID
2040 // NOTE: This will get neutral names, unlike the OS API
2041 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2042 internal static extern int LocaleNameToLCID(String localeName);
2044 // These are desktop only, not coreclr
2045 // locale ID (0409), including sort information
2046 internal int ILANGUAGE
2052 Debug.Assert(sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2053 iLanguage = LocaleNameToLCID(sRealName);
2059 internal bool IsWin32Installed
2061 get { return bWin32Installed; }
2064 internal bool IsFramework
2066 get { return bFramework; }
2069 ////////////////////
2070 // Derived properties //
2071 ////////////////////
2073 internal bool IsNeutralCulture
2077 // NlsInfo::nativeInitCultureData told us if we're neutral or not
2082 internal bool IsInvariantCulture
2086 return String.IsNullOrEmpty(this.SNAME);
2090 // Get an instance of our default calendar
2091 internal Calendar DefaultCalendar
2095 int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2096 if (defaultCalId == 0)
2098 defaultCalId = this.CalendarIds[0];
2101 return CultureInfo.GetCalendarInstance(defaultCalId);
2105 // All of our era names
2106 internal String[] EraNames(int calendarId)
2108 Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
2110 return this.GetCalendar(calendarId).saEraNames;
2113 internal String[] AbbrevEraNames(int calendarId)
2115 Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2117 return this.GetCalendar(calendarId).saAbbrevEraNames;
2120 internal String[] AbbreviatedEnglishEraNames(int calendarId)
2122 Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2124 return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2127 // String array DEFAULTS
2128 // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2131 // Time separator (derived from time format)
2132 internal String TimeSeparator
2136 if (sTimeSeparator == null || UseUserOverride)
2138 string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT));
2139 if (String.IsNullOrEmpty(longTimeFormat))
2141 longTimeFormat = LongTimes[0];
2144 // Compute STIME from time format
2145 sTimeSeparator = GetTimeSeparator(longTimeFormat);
2147 return sTimeSeparator;
2151 // Date separator (derived from short date format)
2152 internal String DateSeparator(int calendarId)
2154 return GetDateSeparator(ShortDates(calendarId)[0]);
2157 //////////////////////////////////////
2158 // Helper Functions to get derived properties //
2159 //////////////////////////////////////
2161 ////////////////////////////////////////////////////////////////////////////
2163 // Unescape a NLS style quote string
2165 // This removes single quotes:
2171 // This removes the first \ of escaped characters:
2172 // fred\'s -> fred's
2176 // We don't build the stringbuilder unless we find a ' or a \. If we find a ' or a \, we
2177 // always build a stringbuilder because we need to remove the ' or \.
2179 ////////////////////////////////////////////////////////////////////////////
2180 static private String UnescapeNlsString(String str, int start, int end)
2182 Contract.Requires(str != null);
2183 Contract.Requires(start >= 0);
2184 Contract.Requires(end >= 0);
2185 StringBuilder result = null;
2187 for (int i = start; i < str.Length && i <= end; i++)
2194 result = new StringBuilder(str, start, i - start, str.Length);
2200 result = new StringBuilder(str, start, i - start, str.Length);
2205 result.Append(str[i]);
2211 result.Append(str[i]);
2218 return (str.Substring(start, end - start + 1));
2220 return (result.ToString());
2223 ////////////////////////////////////////////////////////////////////////////
2225 // Reescape a Win32 style quote string as a NLS+ style quoted string
2227 // This is also the escaping style used by custom culture data files
2229 // NLS+ uses \ to escape the next character, whether in a quoted string or
2230 // not, so we always have to change \ to \\.
2232 // NLS+ uses \' to escape a quote inside a quoted string so we have to change
2233 // '' to \' (if inside a quoted string)
2235 // We don't build the stringbuilder unless we find something to change
2236 ////////////////////////////////////////////////////////////////////////////
2237 static internal String ReescapeWin32String(String str)
2239 // If we don't have data, then don't try anything
2243 StringBuilder result = null;
2245 bool inQuote = false;
2246 for (int i = 0; i < str.Length; i++)
2251 // Already in quote?
2254 // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
2255 if (i + 1 < str.Length && str[i + 1] == '\'')
2257 // Found another ', so we have ''. Need to add \' instead.
2258 // 1st make sure we have our stringbuilder
2260 result = new StringBuilder(str, 0, i, str.Length * 2);
2262 // Append a \' and keep going (so we don't turn off quote mode)
2263 result.Append("\\'");
2268 // Turning off quote mode, fall through to add it
2273 // Found beginning quote, fall through to add it
2277 // Is there a single \ character?
2278 else if (str[i] == '\\')
2280 // Found a \, need to change it to \\
2281 // 1st make sure we have our stringbuilder
2283 result = new StringBuilder(str, 0, i, str.Length * 2);
2285 // Append our \\ to the string & continue
2286 result.Append("\\\\");
2290 // If we have a builder we need to add our character
2292 result.Append(str[i]);
2295 // Unchanged string? , just return input string
2299 // String changed, need to use the builder
2300 return result.ToString();
2303 static internal String[] ReescapeWin32Strings(String[] array)
2307 for (int i = 0; i < array.Length; i++)
2309 array[i] = ReescapeWin32String(array[i]);
2316 // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2317 // and breaking changes here will not show up at build time, only at run time.
2318 static private String GetTimeSeparator(String format)
2320 // Time format separator (ie: : in 12:39:00)
2322 // We calculate this from the provided time format
2326 // Find the time separator so that we can pretend we know STIME.
2328 return GetSeparator(format, "Hhms");
2331 // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2332 // and breaking changes here will not show up at build time, only at run time.
2333 static private String GetDateSeparator(String format)
2335 // Date format separator (ie: / in 9/1/03)
2337 // We calculate this from the provided short date
2341 // Find the date separator so that we can pretend we know SDATE.
2343 return GetSeparator(format, "dyM");
2346 private static string GetSeparator(string format, string timeParts)
2348 int index = IndexOfTimePart(format, 0, timeParts);
2352 // Found a time part, find out when it changes
2353 char cTimePart = format[index];
2358 } while (index < format.Length && format[index] == cTimePart);
2360 int separatorStart = index;
2362 // Now we need to find the end of the separator
2363 if (separatorStart < format.Length)
2365 int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
2366 if (separatorEnd != -1)
2368 // From [separatorStart, count) is our string, except we need to unescape
2369 return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
2374 return String.Empty;
2377 private static int IndexOfTimePart(string format, int startIndex, string timeParts)
2379 Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
2380 Debug.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
2381 bool inQuote = false;
2382 for (int i = startIndex; i < format.Length; ++i)
2384 // See if we have a time Part
2385 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
2392 if (i + 1 < format.Length)
2401 --i; //backup since we will move over this next
2415 private string DoGetLocaleInfo(uint lctype)
2417 Debug.Assert(sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2418 return DoGetLocaleInfo(sWindowsName, lctype);
2421 // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
2422 // "windows" name, which can be specific for downlevel (< windows 7) os's.
2423 private string DoGetLocaleInfo(string localeName, uint lctype)
2425 // Fix lctype if we don't want overrides
2426 if (!UseUserOverride)
2428 lctype |= LOCALE_NOUSEROVERRIDE;
2432 Debug.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null");
2433 string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
2436 // Failed, just use empty string
2437 result = String.Empty;
2443 private int DoGetLocaleInfoInt(uint lctype)
2445 // Fix lctype if we don't want overrides
2446 if (!UseUserOverride)
2448 lctype |= LOCALE_NOUSEROVERRIDE;
2451 // Ask OS for data, note that we presume it returns success, so we have to know that
2452 // sWindowsName is valid before calling.
2453 Debug.Assert(sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2454 int result = CultureInfo.nativeGetLocaleInfoExInt(sWindowsName, lctype);
2459 private String[] DoEnumTimeFormats()
2461 // Note that this gets overrides for us all the time
2462 Debug.Assert(sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2463 String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(sWindowsName, 0, UseUserOverride));
2468 private String[] DoEnumShortTimeFormats()
2470 // Note that this gets overrides for us all the time
2471 Debug.Assert(sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2472 String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(sWindowsName, TIME_NOSECONDS, UseUserOverride));
2478 // Static Helpers //
2480 internal static bool IsCustomCultureId(int cultureId)
2482 if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
2488 ////////////////////////////////////////////////////////////////////////////
2491 // calendarValueOnly Retrieve the values which are affected by the calendar change of DTFI.
2492 // This will cause values like longTimePattern not be retrieved since it is
2493 // not affected by the Calendar property in DTFI.
2495 ////////////////////////////////////////////////////////////////////////////
2496 internal void GetNFIValues(NumberFormatInfo nfi)
2498 if (this.IsInvariantCulture)
2500 nfi.positiveSign = sPositiveSign;
2501 nfi.negativeSign = sNegativeSign;
2503 nfi.nativeDigits = saNativeDigits;
2504 nfi.digitSubstitution = iDigitSubstitution;
2506 nfi.numberGroupSeparator = sThousandSeparator;
2507 nfi.numberDecimalSeparator = sDecimalSeparator;
2508 nfi.numberDecimalDigits = iDigits;
2509 nfi.numberNegativePattern = iNegativeNumber;
2511 nfi.currencySymbol = sCurrency;
2512 nfi.currencyGroupSeparator = sMonetaryThousand;
2513 nfi.currencyDecimalSeparator = sMonetaryDecimal;
2514 nfi.currencyDecimalDigits = iCurrencyDigits;
2515 nfi.currencyNegativePattern = iNegativeCurrency;
2516 nfi.currencyPositivePattern = iCurrency;
2521 // We don't have information for the following four. All cultures use
2522 // the same value of the number formatting values.
2524 // PercentDecimalDigits
2525 // PercentDecimalSeparator
2527 // PercentGroupSeparator
2531 // Ask native side for our data.
2533 Debug.Assert(sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2534 CultureData.nativeGetNumberFormatInfoValues(sWindowsName, nfi, UseUserOverride);
2539 // Gather additional data
2541 nfi.numberGroupSizes = this.WAGROUPING;
2542 nfi.currencyGroupSizes = this.WAMONGROUPING;
2544 // prefer the cached value since these do not have user overrides
2545 nfi.percentNegativePattern = this.INEGATIVEPERCENT;
2546 nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
2547 nfi.percentSymbol = this.SPERCENT;
2548 nfi.perMilleSymbol = this.SPERMILLE;
2550 nfi.negativeInfinitySymbol = this.SNEGINFINITY;
2551 nfi.positiveInfinitySymbol = this.SPOSINFINITY;
2552 nfi.nanSymbol = this.SNAN;
2555 // We don't have percent values, so use the number values
2557 nfi.percentDecimalDigits = nfi.numberDecimalDigits;
2558 nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
2559 nfi.percentGroupSizes = nfi.numberGroupSizes;
2560 nfi.percentGroupSeparator = nfi.numberGroupSeparator;
2563 // Clean up a few odd values
2566 // Windows usually returns an empty positive sign, but we like it to be "+"
2567 if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
2569 //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user
2570 //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
2571 //decimal point doesn't show up. We'll just workaround this here because our default currency format will never use nfi.
2572 if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
2574 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
2578 static private int ConvertFirstDayOfWeekMonToSun(int iTemp)
2580 // Convert Mon-Sun to Sun-Sat format
2584 // Wrap Sunday and convert invalid data to Sunday
2591 // This is ONLY used for caching names and shouldn't be used for anything else
2592 internal static string AnsiToLower(string testString)
2594 StringBuilder sb = new StringBuilder(testString.Length);
2596 for (int ich = 0; ich < testString.Length; ich++)
2598 char ch = testString[ich];
2599 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
2602 return (sb.ToString());
2605 // If we get a group from windows, then its in 3;0 format with the 0 backwards
2606 // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
2607 // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
2608 static private int[] ConvertWin32GroupString(String win32Str)
2610 // None of these cases make any sense
2611 if (win32Str == null || win32Str.Length == 0)
2613 return (new int[] { 3 });
2616 if (win32Str[0] == '0')
2618 return (new int[] { 0 });
2621 // Since its in n;n;n;n;n format, we can always get the length quickly
2623 if (win32Str[win32Str.Length - 1] == '0')
2625 // Trailing 0 gets dropped. 1;0 -> 1
2626 values = new int[(win32Str.Length / 2)];
2630 // Need extra space for trailing zero 1 -> 1;0
2631 values = new int[(win32Str.Length / 2) + 2];
2632 values[values.Length - 1] = 0;
2637 for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
2639 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
2640 // But we'll test because its registry that could be anything
2641 if (win32Str[i] < '1' || win32Str[i] > '9')
2642 return new int[] { 3 };
2644 values[j] = (int)(win32Str[i] - '0');
2650 // LCTYPES for GetLocaleInfo
2651 private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; // do not use user overrides
2652 private const uint LOCALE_RETURN_NUMBER = 0x20000000; // return number instead of string
2654 // Modifier for genitive names
2655 private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000; //Flag to return the Genitive forms of month names
2658 // The following LCTypes are mutually exclusive in that they may NOT
2659 // be used in combination with each other.
2663 // These are the various forms of the name of the locale:
2665 private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002; // localized name of locale, eg "German (Germany)" in UI language
2666 private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072; // Display name (language + country usually) in English, eg "German (Germany)"
2667 private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073; // Display name in native locale language, eg "Deutsch (Deutschland)
2669 private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f; // Language Display Name for a language, eg "German" in UI language
2670 private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001; // English name of language, eg "German"
2671 private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004; // native name of language, eg "Deutsch"
2673 private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006; // localized name of country, eg "Germany" in UI language
2674 private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002; // English name of country, eg "Germany"
2675 private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008; // native name of country, eg "Deutschland"
2678 // private const uint LOCALE_ILANGUAGE =0x00000001; // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals)
2680 // private const uint LOCALE_SLANGUAGE =LOCALE_SLOCALIZEDDISPLAYNAME; // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead)
2681 // private const uint LOCALE_SENGLANGUAGE =LOCALE_SENGLISHLANGUAGENAME; // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead)
2682 private const uint LOCALE_SABBREVLANGNAME = 0x00000003; // abbreviated language name
2683 // private const uint LOCALE_SNATIVELANGNAME =LOCALE_SNATIVELANGUAGENAME; // native name of language (use LOCALE_SNATIVELANGUAGENAME instead)
2685 private const uint LOCALE_ICOUNTRY = 0x00000005; // country code
2686 // private const uint LOCALE_SCOUNTRY =LOCALE_SLOCALIZEDCOUNTRYNAME; // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead)
2687 // private const uint LOCALE_SENGCOUNTRY =LOCALE_SENGLISHCOUNTRYNAME; // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead)
2688 private const uint LOCALE_SABBREVCTRYNAME = 0x00000007; // abbreviated country name
2689 // private const uint LOCALE_SNATIVECTRYNAME =LOCALE_SNATIVECOUNTRYNAME; // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead)
2690 private const uint LOCALE_IGEOID = 0x0000005B; // geographical location id
2692 private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009; // default language id
2693 private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A; // default country code
2694 private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B; // default oem code page
2695 private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004; // default ansi code page
2696 private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011; // default mac code page
2698 private const uint LOCALE_SLIST = 0x0000000C; // list item separator
2699 private const uint LOCALE_IMEASURE = 0x0000000D; // 0 = metric, 1 = US
2701 private const uint LOCALE_SDECIMAL = 0x0000000E; // decimal separator
2702 private const uint LOCALE_STHOUSAND = 0x0000000F; // thousand separator
2703 private const uint LOCALE_SGROUPING = 0x00000010; // digit grouping
2704 private const uint LOCALE_IDIGITS = 0x00000011; // number of fractional digits
2705 private const uint LOCALE_ILZERO = 0x00000012; // leading zeros for decimal
2706 private const uint LOCALE_INEGNUMBER = 0x00001010; // negative number mode
2707 private const uint LOCALE_SNATIVEDIGITS = 0x00000013; // native digits for 0-9
2709 private const uint LOCALE_SCURRENCY = 0x00000014; // local monetary symbol
2710 private const uint LOCALE_SINTLSYMBOL = 0x00000015; // uintl monetary symbol
2711 private const uint LOCALE_SMONDECIMALSEP = 0x00000016; // monetary decimal separator
2712 private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017; // monetary thousand separator
2713 private const uint LOCALE_SMONGROUPING = 0x00000018; // monetary grouping
2714 private const uint LOCALE_ICURRDIGITS = 0x00000019; // # local monetary digits
2715 private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A; // # uintl monetary digits
2716 private const uint LOCALE_ICURRENCY = 0x0000001B; // positive currency mode
2717 private const uint LOCALE_INEGCURR = 0x0000001C; // negative currency mode
2719 private const uint LOCALE_SDATE = 0x0000001D; // date separator (derived from LOCALE_SSHORTDATE, use that instead)
2720 private const uint LOCALE_STIME = 0x0000001E; // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
2721 private const uint LOCALE_SSHORTDATE = 0x0000001F; // short date format string
2722 private const uint LOCALE_SLONGDATE = 0x00000020; // long date format string
2723 private const uint LOCALE_STIMEFORMAT = 0x00001003; // time format string
2724 private const uint LOCALE_IDATE = 0x00000021; // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead)
2725 private const uint LOCALE_ILDATE = 0x00000022; // long date format ordering (derived from LOCALE_SLONGDATE, use that instead)
2726 private const uint LOCALE_ITIME = 0x00000023; // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead)
2727 private const uint LOCALE_ITIMEMARKPOSN = 0x00001005; // time marker position (derived from LOCALE_STIMEFORMAT, use that instead)
2728 private const uint LOCALE_ICENTURY = 0x00000024; // century format specifier (short date, LOCALE_SSHORTDATE is preferred)
2729 private const uint LOCALE_ITLZERO = 0x00000025; // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead)
2730 private const uint LOCALE_IDAYLZERO = 0x00000026; // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred)
2731 private const uint LOCALE_IMONLZERO = 0x00000027; // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred)
2732 private const uint LOCALE_S1159 = 0x00000028; // AM designator
2733 private const uint LOCALE_S2359 = 0x00000029; // PM designator
2735 private const uint LOCALE_ICALENDARTYPE = 0x00001009; // type of calendar specifier
2736 private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B; // additional calendar types specifier
2737 private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; // first day of week specifier
2738 private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D; // first week of year specifier
2740 private const uint LOCALE_SDAYNAME1 = 0x0000002A; // long name for Monday
2741 private const uint LOCALE_SDAYNAME2 = 0x0000002B; // long name for Tuesday
2742 private const uint LOCALE_SDAYNAME3 = 0x0000002C; // long name for Wednesday
2743 private const uint LOCALE_SDAYNAME4 = 0x0000002D; // long name for Thursday
2744 private const uint LOCALE_SDAYNAME5 = 0x0000002E; // long name for Friday
2745 private const uint LOCALE_SDAYNAME6 = 0x0000002F; // long name for Saturday
2746 private const uint LOCALE_SDAYNAME7 = 0x00000030; // long name for Sunday
2747 private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031; // abbreviated name for Monday
2748 private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032; // abbreviated name for Tuesday
2749 private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033; // abbreviated name for Wednesday
2750 private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034; // abbreviated name for Thursday
2751 private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035; // abbreviated name for Friday
2752 private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036; // abbreviated name for Saturday
2753 private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037; // abbreviated name for Sunday
2754 private const uint LOCALE_SMONTHNAME1 = 0x00000038; // long name for January
2755 private const uint LOCALE_SMONTHNAME2 = 0x00000039; // long name for February
2756 private const uint LOCALE_SMONTHNAME3 = 0x0000003A; // long name for March
2757 private const uint LOCALE_SMONTHNAME4 = 0x0000003B; // long name for April
2758 private const uint LOCALE_SMONTHNAME5 = 0x0000003C; // long name for May
2759 private const uint LOCALE_SMONTHNAME6 = 0x0000003D; // long name for June
2760 private const uint LOCALE_SMONTHNAME7 = 0x0000003E; // long name for July
2761 private const uint LOCALE_SMONTHNAME8 = 0x0000003F; // long name for August
2762 private const uint LOCALE_SMONTHNAME9 = 0x00000040; // long name for September
2763 private const uint LOCALE_SMONTHNAME10 = 0x00000041; // long name for October
2764 private const uint LOCALE_SMONTHNAME11 = 0x00000042; // long name for November
2765 private const uint LOCALE_SMONTHNAME12 = 0x00000043; // long name for December
2766 private const uint LOCALE_SMONTHNAME13 = 0x0000100E; // long name for 13th month (if exists)
2767 private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044; // abbreviated name for January
2768 private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045; // abbreviated name for February
2769 private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046; // abbreviated name for March
2770 private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047; // abbreviated name for April
2771 private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048; // abbreviated name for May
2772 private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049; // abbreviated name for June
2773 private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A; // abbreviated name for July
2774 private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B; // abbreviated name for August
2775 private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C; // abbreviated name for September
2776 private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D; // abbreviated name for October
2777 private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E; // abbreviated name for November
2778 private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F; // abbreviated name for December
2779 private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F; // abbreviated name for 13th month (if exists)
2781 private const uint LOCALE_SPOSITIVESIGN = 0x00000050; // positive sign
2782 private const uint LOCALE_SNEGATIVESIGN = 0x00000051; // negative sign
2783 private const uint LOCALE_IPOSSIGNPOSN = 0x00000052; // positive sign position (derived from INEGCURR)
2784 private const uint LOCALE_INEGSIGNPOSN = 0x00000053; // negative sign position (derived from INEGCURR)
2785 private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054; // mon sym precedes pos amt (derived from ICURRENCY)
2786 private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055; // mon sym sep by space from pos amt (derived from ICURRENCY)
2787 private const uint LOCALE_INEGSYMPRECEDES = 0x00000056; // mon sym precedes neg amt (derived from INEGCURR)
2788 private const uint LOCALE_INEGSEPBYSPACE = 0x00000057; // mon sym sep by space from neg amt (derived from INEGCURR)
2790 private const uint LOCALE_FONTSIGNATURE = 0x00000058; // font signature
2791 private const uint LOCALE_SISO639LANGNAME = 0x00000059; // ISO abbreviated language name
2792 private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A; // ISO abbreviated country name
2794 private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012; // default ebcdic code page
2795 private const uint LOCALE_IPAPERSIZE = 0x0000100A; // 1 = letter, 5 = legal, 8 = a3, 9 = a4
2796 private const uint LOCALE_SENGCURRNAME = 0x00001007; // english name of currency
2797 private const uint LOCALE_SNATIVECURRNAME = 0x00001008; // native name of currency
2798 private const uint LOCALE_SYEARMONTH = 0x00001006; // year month format string
2799 private const uint LOCALE_SSORTNAME = 0x00001013; // sort name
2800 private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014; // 0 = context, 1 = none, 2 = national
2802 private const uint LOCALE_SNAME = 0x0000005c; // locale name (with sort info) (ie: de-DE_phoneb)
2803 private const uint LOCALE_SDURATION = 0x0000005d; // time duration format
2804 private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e; // (windows only) keyboards to install
2805 private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060; // Shortest day name for Monday
2806 private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061; // Shortest day name for Tuesday
2807 private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062; // Shortest day name for Wednesday
2808 private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063; // Shortest day name for Thursday
2809 private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064; // Shortest day name for Friday
2810 private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065; // Shortest day name for Saturday
2811 private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066; // Shortest day name for Sunday
2812 private const uint LOCALE_SISO639LANGNAME2 = 0x00000067; // 3 character ISO abbreviated language name
2813 private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068; // 3 character ISO country name
2814 private const uint LOCALE_SNAN = 0x00000069; // Not a Number
2815 private const uint LOCALE_SPOSINFINITY = 0x0000006a; // + Infinity
2816 private const uint LOCALE_SNEGINFINITY = 0x0000006b; // - Infinity
2817 private const uint LOCALE_SSCRIPTS = 0x0000006c; // Typical scripts in the locale
2818 private const uint LOCALE_SPARENT = 0x0000006d; // Fallback name for resources
2819 private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e; // Fallback name for within the console
2820 // private const uint LOCALE_SLANGDISPLAYNAME =LOCALE_SLOCALIZEDLANGUAGENAME; // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead)
2822 // Windows 7 LCTYPES
2823 private const uint LOCALE_IREADINGLAYOUT = 0x00000070; // Returns one of the following 4 reading layout values:
2824 // 0 - Left to right (eg en-US)
2825 // 1 - Right to left (eg arabic locales)
2826 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2827 // 3 - Vertical top to bottom with columns proceeding to the right
2828 private const uint LOCALE_INEUTRAL = 0x00000071; // Returns 0 for specific cultures, 1 for neutral cultures.
2829 private const uint LOCALE_INEGATIVEPERCENT = 0x00000074; // Returns 0-11 for the negative percent format
2830 private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075; // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT
2831 private const uint LOCALE_SPERCENT = 0x00000076; // Returns the percent symbol
2832 private const uint LOCALE_SPERMILLE = 0x00000077; // Returns the permille (U+2030) symbol
2833 private const uint LOCALE_SMONTHDAY = 0x00000078; // Returns the preferred month/day format
2834 private const uint LOCALE_SSHORTTIME = 0x00000079; // Returns the preferred short time format (ie: no seconds, just h:mm)
2835 private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a; // Open type language tag, eg: "latn" or "dflt"
2836 private const uint LOCALE_SSORTLOCALE = 0x0000007b; // Name of locale to use for sorting/collation/casing behavior.
2838 // Time formats enumerations
2839 internal const uint TIME_NOSECONDS = 0x00000002; // Don't use seconds (get short time format for enumtimeformats on win7+)
2841 // Get our initial minimal culture data (name, parent, etc.)
2842 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2843 internal static extern bool nativeInitCultureData(CultureData cultureData);
2845 // Grab the NumberFormatInfo data
2846 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2847 internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride);
2849 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2850 private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride);
2852 [SuppressUnmanagedCodeSecurityAttribute()]
2853 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
2854 internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray);