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 using System.Collections.Generic;
6 using System.Diagnostics;
8 using System.Threading;
10 namespace System.Globalization
13 using StringStringDictionary = Dictionary<string, string>;
14 using StringCultureDataDictionary = Dictionary<string, CultureData>;
15 using LcidToCultureNameDictionary = Dictionary<int, string>;
18 using StringStringDictionary = LowLevelDictionary<string, string>;
19 using StringCultureDataDictionary = LowLevelDictionary<string, CultureData>;
20 using LcidToCultureNameDictionary = LowLevelDictionary<int, string>;
24 // List of culture data
25 // Note the we cache overrides.
26 // Note that localized names (resource names) aren't available from here.
30 // Our names are a tad confusing.
32 // sWindowsName -- The name that windows thinks this culture is, ie:
33 // en-US if you pass in en-US
34 // de-DE_phoneb if you pass in de-DE_phoneb
35 // fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
36 // fj if you pass in fj (neutral, post-Windows 7 machine)
38 // sRealName -- The name you used to construct the culture, in pretty form
39 // en-US if you pass in EN-us
40 // en if you pass in en
41 // de-DE_phoneb if you pass in de-DE_phoneb
43 // sSpecificCulture -- The specific culture for this culture
46 // de-DE_phoneb for alt sort
47 // fj-FJ for fj (neutral)
49 // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
50 // en-US if you pass in en-US
51 // en if you pass in en
52 // de-DE if you pass in de-DE_phoneb
54 internal partial class CultureData
56 private const int undef = -1;
59 private String _sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
60 private String _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
63 private String _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
64 private String _sParent; // Parent name (which may be a custom locale/culture)
65 private String _sLocalizedDisplayName; // Localized pretty name for this locale
66 private String _sEnglishDisplayName; // English pretty name for this locale
67 private String _sNativeDisplayName; // Native pretty name for this locale
68 private String _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
71 private String _sISO639Language; // ISO 639 Language Name
72 private String _sISO639Language2; // ISO 639 Language Name
73 private String _sLocalizedLanguage; // Localized name for this language
74 private String _sEnglishLanguage; // English name for this language
75 private String _sNativeLanguage; // Native name of this language
76 private String _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
77 private string _sConsoleFallbackName; // The culture name for the console fallback UI culture
78 private int _iInputLanguageHandle=undef;// input language handle
81 private String _sRegionName; // (RegionInfo)
82 private String _sLocalizedCountry; // localized country name
83 private String _sEnglishCountry; // english country name (RegionInfo)
84 private String _sNativeCountry; // native country name
85 private String _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
86 private String _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
87 private int _iGeoId = undef; // GeoId
90 private String _sPositiveSign; // (user can override) positive sign
91 private String _sNegativeSign; // (user can override) negative sign
92 // (nfi populates these 5, don't have to be = undef)
93 private int _iDigits; // (user can override) number of fractional digits
94 private int _iNegativeNumber; // (user can override) negative number format
95 private int[] _waGrouping; // (user can override) grouping of digits
96 private String _sDecimalSeparator; // (user can override) decimal separator
97 private String _sThousandSeparator; // (user can override) thousands separator
98 private String _sNaN; // Not a Number
99 private String _sPositiveInfinity; // + Infinity
100 private String _sNegativeInfinity; // - Infinity
103 private int _iNegativePercent = undef; // Negative Percent (0-3)
104 private int _iPositivePercent = undef; // Positive Percent (0-11)
105 private String _sPercent; // Percent (%) symbol
106 private String _sPerMille; // PerMille symbol
109 private String _sCurrency; // (user can override) local monetary symbol
110 private String _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
111 private String _sEnglishCurrency; // English name for this currency
112 private String _sNativeCurrency; // Native name for this currency
113 // (nfi populates these 4, don't have to be = undef)
114 private int _iCurrencyDigits; // (user can override) # local monetary fractional digits
115 private int _iCurrency; // (user can override) positive currency format
116 private int _iNegativeCurrency; // (user can override) negative currency format
117 private int[] _waMonetaryGrouping; // (user can override) monetary grouping of digits
118 private String _sMonetaryDecimal; // (user can override) monetary decimal separator
119 private String _sMonetaryThousand; // (user can override) monetary thousands separator
122 private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
123 private String _sListSeparator; // (user can override) list separator
126 private String _sAM1159; // (user can override) AM designator
127 private String _sPM2359; // (user can override) PM designator
128 private String _sTimeSeparator;
129 private volatile String[] _saLongTimes; // (user can override) time format
130 private volatile String[] _saShortTimes; // short time format
131 private volatile String[] _saDurationFormats; // time duration format
133 // Calendar specific data
134 private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
135 private int _iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
136 private volatile CalendarId[] _waCalendars; // all available calendar type(s). The first one is the default calendar
138 // Store for specific data about each calendar
139 private CalendarData[] _calendars; // Store for specific calendar data
142 private int _iReadingLayout = undef; // Reading layout data
143 // 0 - Left to right (eg en-US)
144 // 1 - Right to left (eg arabic locales)
145 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
146 // 3 - Vertical top to bottom with columns proceeding to the right
148 // CoreCLR depends on this even though its not exposed publicly.
150 private int _iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
151 private int _iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
152 private int _iDefaultMacCodePage = undef; // default macintosh code page
153 private int _iDefaultEbcdicCodePage = undef; // default EBCDIC code page
155 private int _iLanguage; // locale ID (0409) - NO sort information
156 private bool _bUseOverrides; // use user overrides?
157 private bool _bNeutral; // Flags for the culture (ie: neutral or not right now)
159 // Region Name to Culture Name mapping table
160 // (In future would be nice to be in registry or something)
162 //Using a property so we avoid creating the dictionary untill we need it
163 private static StringStringDictionary RegionNames
167 if (s_RegionNames == null)
169 StringStringDictionary regionNames = new StringStringDictionary(211 /* prime */);
171 regionNames.Add("029", "en-029");
172 regionNames.Add("AE", "ar-AE");
173 regionNames.Add("AF", "prs-AF");
174 regionNames.Add("AL", "sq-AL");
175 regionNames.Add("AM", "hy-AM");
176 regionNames.Add("AR", "es-AR");
177 regionNames.Add("AT", "de-AT");
178 regionNames.Add("AU", "en-AU");
179 regionNames.Add("AZ", "az-Cyrl-AZ");
180 regionNames.Add("BA", "bs-Latn-BA");
181 regionNames.Add("BD", "bn-BD");
182 regionNames.Add("BE", "nl-BE");
183 regionNames.Add("BG", "bg-BG");
184 regionNames.Add("BH", "ar-BH");
185 regionNames.Add("BN", "ms-BN");
186 regionNames.Add("BO", "es-BO");
187 regionNames.Add("BR", "pt-BR");
188 regionNames.Add("BY", "be-BY");
189 regionNames.Add("BZ", "en-BZ");
190 regionNames.Add("CA", "en-CA");
191 regionNames.Add("CH", "it-CH");
192 regionNames.Add("CL", "es-CL");
193 regionNames.Add("CN", "zh-CN");
194 regionNames.Add("CO", "es-CO");
195 regionNames.Add("CR", "es-CR");
196 regionNames.Add("CS", "sr-Cyrl-CS");
197 regionNames.Add("CZ", "cs-CZ");
198 regionNames.Add("DE", "de-DE");
199 regionNames.Add("DK", "da-DK");
200 regionNames.Add("DO", "es-DO");
201 regionNames.Add("DZ", "ar-DZ");
202 regionNames.Add("EC", "es-EC");
203 regionNames.Add("EE", "et-EE");
204 regionNames.Add("EG", "ar-EG");
205 regionNames.Add("ES", "es-ES");
206 regionNames.Add("ET", "am-ET");
207 regionNames.Add("FI", "fi-FI");
208 regionNames.Add("FO", "fo-FO");
209 regionNames.Add("FR", "fr-FR");
210 regionNames.Add("GB", "en-GB");
211 regionNames.Add("GE", "ka-GE");
212 regionNames.Add("GL", "kl-GL");
213 regionNames.Add("GR", "el-GR");
214 regionNames.Add("GT", "es-GT");
215 regionNames.Add("HK", "zh-HK");
216 regionNames.Add("HN", "es-HN");
217 regionNames.Add("HR", "hr-HR");
218 regionNames.Add("HU", "hu-HU");
219 regionNames.Add("ID", "id-ID");
220 regionNames.Add("IE", "en-IE");
221 regionNames.Add("IL", "he-IL");
222 regionNames.Add("IN", "hi-IN");
223 regionNames.Add("IQ", "ar-IQ");
224 regionNames.Add("IR", "fa-IR");
225 regionNames.Add("IS", "is-IS");
226 regionNames.Add("IT", "it-IT");
227 regionNames.Add("IV", "");
228 regionNames.Add("JM", "en-JM");
229 regionNames.Add("JO", "ar-JO");
230 regionNames.Add("JP", "ja-JP");
231 regionNames.Add("KE", "sw-KE");
232 regionNames.Add("KG", "ky-KG");
233 regionNames.Add("KH", "km-KH");
234 regionNames.Add("KR", "ko-KR");
235 regionNames.Add("KW", "ar-KW");
236 regionNames.Add("KZ", "kk-KZ");
237 regionNames.Add("LA", "lo-LA");
238 regionNames.Add("LB", "ar-LB");
239 regionNames.Add("LI", "de-LI");
240 regionNames.Add("LK", "si-LK");
241 regionNames.Add("LT", "lt-LT");
242 regionNames.Add("LU", "lb-LU");
243 regionNames.Add("LV", "lv-LV");
244 regionNames.Add("LY", "ar-LY");
245 regionNames.Add("MA", "ar-MA");
246 regionNames.Add("MC", "fr-MC");
247 regionNames.Add("ME", "sr-Latn-ME");
248 regionNames.Add("MK", "mk-MK");
249 regionNames.Add("MN", "mn-MN");
250 regionNames.Add("MO", "zh-MO");
251 regionNames.Add("MT", "mt-MT");
252 regionNames.Add("MV", "dv-MV");
253 regionNames.Add("MX", "es-MX");
254 regionNames.Add("MY", "ms-MY");
255 regionNames.Add("NG", "ig-NG");
256 regionNames.Add("NI", "es-NI");
257 regionNames.Add("NL", "nl-NL");
258 regionNames.Add("NO", "nn-NO");
259 regionNames.Add("NP", "ne-NP");
260 regionNames.Add("NZ", "en-NZ");
261 regionNames.Add("OM", "ar-OM");
262 regionNames.Add("PA", "es-PA");
263 regionNames.Add("PE", "es-PE");
264 regionNames.Add("PH", "en-PH");
265 regionNames.Add("PK", "ur-PK");
266 regionNames.Add("PL", "pl-PL");
267 regionNames.Add("PR", "es-PR");
268 regionNames.Add("PT", "pt-PT");
269 regionNames.Add("PY", "es-PY");
270 regionNames.Add("QA", "ar-QA");
271 regionNames.Add("RO", "ro-RO");
272 regionNames.Add("RS", "sr-Latn-RS");
273 regionNames.Add("RU", "ru-RU");
274 regionNames.Add("RW", "rw-RW");
275 regionNames.Add("SA", "ar-SA");
276 regionNames.Add("SE", "sv-SE");
277 regionNames.Add("SG", "zh-SG");
278 regionNames.Add("SI", "sl-SI");
279 regionNames.Add("SK", "sk-SK");
280 regionNames.Add("SN", "wo-SN");
281 regionNames.Add("SV", "es-SV");
282 regionNames.Add("SY", "ar-SY");
283 regionNames.Add("TH", "th-TH");
284 regionNames.Add("TJ", "tg-Cyrl-TJ");
285 regionNames.Add("TM", "tk-TM");
286 regionNames.Add("TN", "ar-TN");
287 regionNames.Add("TR", "tr-TR");
288 regionNames.Add("TT", "en-TT");
289 regionNames.Add("TW", "zh-TW");
290 regionNames.Add("UA", "uk-UA");
291 regionNames.Add("US", "en-US");
292 regionNames.Add("UY", "es-UY");
293 regionNames.Add("UZ", "uz-Cyrl-UZ");
294 regionNames.Add("VE", "es-VE");
295 regionNames.Add("VN", "vi-VN");
296 regionNames.Add("YE", "ar-YE");
297 regionNames.Add("ZA", "af-ZA");
298 regionNames.Add("ZW", "en-ZW");
300 s_RegionNames = regionNames;
303 return s_RegionNames;
307 // Cache of regions we've already looked up
308 private static volatile StringCultureDataDictionary s_cachedRegions;
309 private static volatile StringStringDictionary s_RegionNames;
311 internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
313 // First do a shortcut for Invariant
314 if (String.IsNullOrEmpty(cultureName))
316 return CultureData.Invariant;
320 // First check if GetCultureData() can find it (ie: its a real culture)
322 CultureData retVal = GetCultureData(cultureName, useUserOverride);
323 if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
326 // Not a specific culture, perhaps it's region-only name
327 // (Remember this isn't a core clr path where that's not supported)
330 // If it was neutral remember that so that RegionInfo() can throw the right exception
331 CultureData neutral = retVal;
333 // Try the hash table next
334 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
335 StringCultureDataDictionary tempHashTable = s_cachedRegions;
336 if (tempHashTable == null)
338 // No table yet, make a new one
339 tempHashTable = new StringCultureDataDictionary();
343 // Check the hash table
346 tempHashTable.TryGetValue(hashName, out retVal);
355 // Not found in the hash table, look it up the hard way
358 // If not a valid mapping from the registry we'll have to try the hard coded table
359 if (retVal == null || (retVal.IsNeutralCulture == true))
361 // Not a valid mapping, try the hard coded table
363 if (RegionNames.TryGetValue(cultureName, out name))
365 // Make sure we can get culture data for it
366 retVal = GetCultureData(name, useUserOverride);
370 // If not found in the hard coded table we'll have to find a culture that works for us
371 if (retVal == null || (retVal.IsNeutralCulture == true))
373 retVal = GetCultureDataFromRegionName(cultureName);
376 // If we found one we can use, then cache it for next time
377 if (retVal != null && (retVal.IsNeutralCulture == false))
379 // first add it to the cache
382 tempHashTable[hashName] = retVal;
385 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
386 // new tables simultaneously created by a new thread, but maximizes thread safety.
387 s_cachedRegions = tempHashTable;
391 // Unable to find a matching culture/region, return null or neutral
392 // (regionInfo throws a more specific exception on neutrals)
396 // Return the found culture to use, null, or the neutral culture.
400 // Clear our internal caches
401 internal static void ClearCachedData()
403 s_cachedCultures = null;
404 s_cachedRegions = null;
407 internal static CultureInfo[] GetCultures(CultureTypes types)
409 // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
410 #pragma warning disable 618
412 if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
413 CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
414 CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
415 CultureTypes.FrameworkCultures)) != 0)
417 throw new ArgumentOutOfRangeException(nameof(types),
418 SR.Format(SR.ArgumentOutOfRange_Range, CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
421 // We have deprecated CultureTypes.FrameworkCultures.
422 // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
424 // We have deprecated CultureTypes.WindowsOnlyCultures.
425 // When this enum is used, we will return an empty array for this enum.
426 if ((types & CultureTypes.WindowsOnlyCultures) != 0)
428 // Remove the enum as it is an no-op.
429 types &= (~CultureTypes.WindowsOnlyCultures);
432 #pragma warning restore 618
433 return EnumCultures(types);
436 /////////////////////////////////////////////////////////////////////////
437 // Build our invariant information
439 // We need an invariant instance, which we build hard-coded
440 /////////////////////////////////////////////////////////////////////////
441 internal static CultureData Invariant
445 if (s_Invariant == null)
447 // Make a new culturedata
448 CultureData invariant = new CultureData();
451 // Note that we override the resources since this IS NOT supposed to change (by definition)
452 invariant._bUseOverrides = false;
453 invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
454 invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
457 invariant._sName = ""; // locale name (ie: en-us)
458 invariant._sParent = ""; // Parent name (which may be a custom locale/culture)
459 invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now)
460 invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
461 invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
462 invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
465 invariant._sISO639Language = "iv"; // ISO 639 Language Name
466 invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
467 invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language
468 invariant._sEnglishLanguage = "Invariant Language"; // English name for this language
469 invariant._sNativeLanguage = "Invariant Language"; // Native name of this language
470 invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
471 invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
472 invariant._iInputLanguageHandle = 0x07F; // input language handle
475 invariant._sRegionName = "IV"; // (RegionInfo)
476 invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
477 invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only)
478 invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US
479 invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
480 invariant._iGeoId = 244; // GeoId (Windows Only)
483 invariant._sPositiveSign = "+"; // positive sign
484 invariant._sNegativeSign = "-"; // negative sign
485 invariant._iDigits = 2; // number of fractional digits
486 invariant._iNegativeNumber = 1; // negative number format
487 invariant._waGrouping = new int[] { 3 }; // grouping of digits
488 invariant._sDecimalSeparator = "."; // decimal separator
489 invariant._sThousandSeparator = ","; // thousands separator
490 invariant._sNaN = "NaN"; // Not a Number
491 invariant._sPositiveInfinity = "Infinity"; // + Infinity
492 invariant._sNegativeInfinity = "-Infinity"; // - Infinity
495 invariant._iNegativePercent = 0; // Negative Percent (0-3)
496 invariant._iPositivePercent = 0; // Positive Percent (0-11)
497 invariant._sPercent = "%"; // Percent (%) symbol
498 invariant._sPerMille = "\x2030"; // PerMille symbol
501 invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol
502 invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
503 invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
504 invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
505 invariant._iCurrencyDigits = 2; // # local monetary fractional digits
506 invariant._iCurrency = 0; // positive currency format
507 invariant._iNegativeCurrency = 0; // negative currency format
508 invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
509 invariant._sMonetaryDecimal = "."; // monetary decimal separator
510 invariant._sMonetaryThousand = ","; // monetary thousands separator
513 invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
514 invariant._sListSeparator = ","; // list separator
517 invariant._sAM1159 = "AM"; // AM designator
518 invariant._sPM2359 = "PM"; // PM designator
519 invariant._saLongTimes = new String[] { "HH:mm:ss" }; // time format
520 invariant._saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
521 invariant._saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format
524 // Calendar specific data
525 invariant._iFirstDayOfWeek = 0; // first day of week
526 invariant._iFirstWeekOfYear = 0; // first week of year
527 invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
529 // Store for specific data about each calendar
530 invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
531 invariant._calendars[0] = CalendarData.Invariant;
534 invariant._iReadingLayout = 0;
536 // These are desktop only, not coreclr
538 invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information
539 invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
540 invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
541 invariant._iDefaultMacCodePage = 10000; // default macintosh code page
542 invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
544 s_Invariant = invariant;
549 private volatile static CultureData s_Invariant;
554 // Cache of cultures we've already looked up
555 private static volatile StringCultureDataDictionary s_cachedCultures;
556 private static readonly Lock s_lock = new Lock();
558 internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
560 // First do a shortcut for Invariant
561 if (String.IsNullOrEmpty(cultureName))
563 return CultureData.Invariant;
566 // Try the hash table first
567 String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
568 StringCultureDataDictionary tempHashTable = s_cachedCultures;
569 if (tempHashTable == null)
571 // No table yet, make a new one
572 tempHashTable = new StringCultureDataDictionary();
576 // Check the hash table
581 ret = tempHashTable.TryGetValue(hashName, out retVal);
583 if (ret && retVal != null)
589 // Not found in the hash table, need to see if we can build one that works for us
590 CultureData culture = CreateCultureData(cultureName, useUserOverride);
596 // Found one, add it to the cache
599 tempHashTable[hashName] = culture;
602 // Copy the hashtable to the corresponding member variables. This will potentially overwrite
603 // new tables simultaneously created by a new thread, but maximizes thread safety.
604 s_cachedCultures = tempHashTable;
609 private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
611 CultureData culture = new CultureData();
612 culture._bUseOverrides = useUserOverride;
613 culture._sRealName = cultureName;
615 // Ask native code if that one's real
616 if (culture.InitCultureData() == false)
618 if (culture.InitCompatibilityCultureData() == false)
627 private bool InitCompatibilityCultureData()
629 // for compatibility handle the deprecated ids: zh-chs, zh-cht
630 string cultureName = _sRealName;
632 string fallbackCultureName;
633 string realCultureName;
634 switch (AnsiToLower(cultureName))
637 fallbackCultureName = "zh-Hans";
638 realCultureName = "zh-CHS";
641 fallbackCultureName = "zh-Hant";
642 realCultureName = "zh-CHT";
648 _sRealName = fallbackCultureName;
649 if (InitCultureData() == false)
654 _sName = realCultureName; // the name that goes back to the user
655 _sParent = fallbackCultureName;
660 // We'd rather people use the named version since this doesn't allow custom locales
661 internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
663 string localeName = null;
664 CultureData retVal = null;
666 if (culture == CultureInfo.LOCALE_INVARIANT)
669 // Convert the lcid to a name, then use that
670 // Note that this will return neutral names (unlike Vista native API)
671 localeName = LCIDToLocaleName(culture);
673 if (!String.IsNullOrEmpty(localeName))
675 // Valid name, use it
676 retVal = GetCultureData(localeName, bUseUserOverride);
679 // If not successful, throw
681 throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
683 // Return the one we found
687 ////////////////////////////////////////////////////////////////////////
691 // Accessors for our data object items
693 ////////////////////////////////////////////////////////////////////////
699 // The real name used to construct the locale (ie: de-DE_phoneb)
700 internal String CultureName
704 Debug.Assert(_sRealName != null, "[CultureData.CultureName] Expected _sRealName to be populated by already");
705 // since windows doesn't know about zh-CHS and zh-CHT,
706 // we leave sRealName == zh-Hanx but we still need to
707 // pretend that it was zh-CHX.
718 // Are overrides enabled?
719 internal bool UseUserOverride
723 return _bUseOverrides;
727 // locale name (ie: de-DE, NO sort information)
728 internal String SNAME
734 _sName = String.Empty;
740 // Parent name (which may be a custom locale/culture)
741 internal String SPARENT
745 if (_sParent == null)
747 // Ask using the real name, so that we get parents of neutrals
748 _sParent = GetLocaleInfo(_sRealName, LocaleStringData.ParentName);
754 // Localized pretty name for this locale (ie: Inglis (estados Unitos))
755 internal String SLOCALIZEDDISPLAYNAME
759 if (_sLocalizedDisplayName == null)
761 if (this.IsSupplementalCustomCulture)
763 if (this.IsNeutralCulture)
765 _sLocalizedDisplayName = this.SNATIVELANGUAGE;
769 _sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
776 const string ZH_CHT = "zh-CHT";
777 const string ZH_CHS = "zh-CHS";
779 if (SNAME.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase))
781 _sLocalizedDisplayName = GetLanguageDisplayName("zh-Hant");
783 else if (SNAME.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase))
785 _sLocalizedDisplayName = GetLanguageDisplayName("zh-Hans");
789 _sLocalizedDisplayName = GetLanguageDisplayName(SNAME);
797 // If it hasn't been found (Windows 8 and up), fallback to the system
798 if (String.IsNullOrEmpty(_sLocalizedDisplayName))
800 // If its neutral use the language name
801 if (this.IsNeutralCulture)
803 _sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
807 // Usually the UI culture shouldn't be different than what we got from WinRT except
808 // if DefaultThreadCurrentUICulture was set
811 if (CultureInfo.DefaultThreadCurrentUICulture != null &&
812 ((ci = GetUserDefaultCulture()) != null) &&
813 !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
815 _sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
819 _sLocalizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName);
825 return _sLocalizedDisplayName;
829 // English pretty name for this locale (ie: English (United States))
830 internal String SENGDISPLAYNAME
834 if (_sEnglishDisplayName == null)
836 // If its neutral use the language name
837 if (this.IsNeutralCulture)
839 _sEnglishDisplayName = this.SENGLISHLANGUAGE;
840 // differentiate the legacy display names
845 _sEnglishDisplayName += " Legacy";
851 _sEnglishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
853 // if it isn't found build one:
854 if (String.IsNullOrEmpty(_sEnglishDisplayName))
856 // Our existing names mostly look like:
857 // "English" + "United States" -> "English (United States)"
858 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
859 if (this.SENGLISHLANGUAGE[this.SENGLISHLANGUAGE.Length - 1] == ')')
861 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
862 _sEnglishDisplayName =
863 this.SENGLISHLANGUAGE.Substring(0, _sEnglishLanguage.Length - 1) +
864 ", " + this.SENGCOUNTRY + ")";
868 // "English" + "United States" -> "English (United States)"
869 _sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
874 return _sEnglishDisplayName;
878 // Native pretty name for this locale (ie: Deutsch (Deutschland))
879 internal String SNATIVEDISPLAYNAME
883 if (_sNativeDisplayName == null)
885 // If its neutral use the language name
886 if (this.IsNeutralCulture)
888 _sNativeDisplayName = this.SNATIVELANGUAGE;
889 // differentiate the legacy display names
893 _sNativeDisplayName += " \u65E7\u7248";
896 _sNativeDisplayName += " \u820A\u7248";
902 _sNativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
904 // if it isn't found build one:
905 if (String.IsNullOrEmpty(_sNativeDisplayName))
907 // These should primarily be "Deutsch (Deutschland)" type names
908 _sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
912 return _sNativeDisplayName;
916 // The culture name to be used in CultureInfo.CreateSpecificCulture()
917 internal string SSPECIFICCULTURE
921 // This got populated during the culture initialization
922 Debug.Assert(_sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by culture data initialization already");
923 return _sSpecificCulture;
931 // iso 639 language name, ie: en
932 internal String SISO639LANGNAME
936 if (_sISO639Language == null)
938 _sISO639Language = GetLocaleInfo(LocaleStringData.Iso639LanguageTwoLetterName);
940 return _sISO639Language;
944 // iso 639 language name, ie: eng
945 internal string SISO639LANGNAME2
949 if (_sISO639Language2 == null)
951 _sISO639Language2 = GetLocaleInfo(LocaleStringData.Iso639LanguageThreeLetterName);
953 return _sISO639Language2;
957 // abbreviated windows language name (ie: enu) (non-standard, avoid this)
958 internal string SABBREVLANGNAME
962 if (_sAbbrevLang == null)
964 _sAbbrevLang = GetThreeLetterWindowsLanguageName(_sRealName);
970 // Localized name for this language (Windows Only) ie: Inglis
971 // This is only valid for Windows 8 and higher neutrals:
972 internal String SLOCALIZEDLANGUAGE
976 if (_sLocalizedLanguage == null)
978 // Usually the UI culture shouldn't be different than what we got from WinRT except
979 // if DefaultThreadCurrentUICulture was set
982 if (CultureInfo.DefaultThreadCurrentUICulture != null &&
983 ((ci = GetUserDefaultCulture()) != null) &&
984 !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
986 _sLocalizedLanguage = SNATIVELANGUAGE;
990 _sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName);
994 return _sLocalizedLanguage;
998 // English name for this language (Windows Only) ie: German
999 internal String SENGLISHLANGUAGE
1003 if (_sEnglishLanguage == null)
1005 _sEnglishLanguage = GetLocaleInfo(LocaleStringData.EnglishLanguageName);
1007 return _sEnglishLanguage;
1011 // Native name of this language (Windows Only) ie: Deutsch
1012 internal String SNATIVELANGUAGE
1016 if (_sNativeLanguage == null)
1018 _sNativeLanguage = GetLocaleInfo(LocaleStringData.NativeLanguageName);
1020 return _sNativeLanguage;
1028 // region name (eg US)
1029 internal String SREGIONNAME
1033 if (_sRegionName == null)
1035 _sRegionName = GetLocaleInfo(LocaleStringData.Iso3166CountryName);
1037 return _sRegionName;
1045 if (_iGeoId == undef)
1047 _iGeoId = GetGeoId(_sRealName);
1053 // localized name for the country
1054 internal string SLOCALIZEDCOUNTRY
1058 if (_sLocalizedCountry == null)
1062 _sLocalizedCountry = GetRegionDisplayName(SISO3166CTRYNAME);
1066 // do nothing. we'll fallback
1069 if (_sLocalizedCountry == null)
1071 _sLocalizedCountry = SNATIVECOUNTRY;
1074 return _sLocalizedCountry;
1078 // english country name (RegionInfo) ie: Germany
1079 internal String SENGCOUNTRY
1083 if (_sEnglishCountry == null)
1085 _sEnglishCountry = GetLocaleInfo(LocaleStringData.EnglishCountryName);
1087 return _sEnglishCountry;
1091 // native country name (RegionInfo) ie: Deutschland
1092 internal String SNATIVECOUNTRY
1096 if (_sNativeCountry == null)
1098 _sNativeCountry = GetLocaleInfo(LocaleStringData.NativeCountryName);
1100 return _sNativeCountry;
1104 // ISO 3166 Country Name
1105 internal String SISO3166CTRYNAME
1109 if (_sISO3166CountryName == null)
1111 _sISO3166CountryName = GetLocaleInfo(LocaleStringData.Iso3166CountryName);
1113 return _sISO3166CountryName;
1117 // 3 letter ISO 3166 country code
1118 internal String SISO3166CTRYNAME2
1122 if (_sISO3166CountryName2 == null)
1124 _sISO3166CountryName2 = GetLocaleInfo(LocaleStringData.Iso3166CountryName2);
1126 return _sISO3166CountryName2;
1130 internal int IINPUTLANGUAGEHANDLE
1134 if (_iInputLanguageHandle == undef)
1136 if (IsSupplementalCustomCulture)
1138 _iInputLanguageHandle = 0x0409;
1142 // Input Language is same as LCID for built-in cultures
1143 _iInputLanguageHandle = this.ILANGUAGE;
1146 return _iInputLanguageHandle;
1150 // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1151 internal string SCONSOLEFALLBACKNAME
1155 if (_sConsoleFallbackName == null)
1157 _sConsoleFallbackName = GetConsoleFallbackName(_sRealName);
1159 return _sConsoleFallbackName;
1163 // (user can override) grouping of digits
1164 internal int[] WAGROUPING
1168 if (_waGrouping == null)
1170 _waGrouping = GetLocaleInfo(LocaleGroupingData.Digit);
1177 // internal String sDecimalSeparator ; // (user can override) decimal separator
1178 // internal String sThousandSeparator ; // (user can override) thousands separator
1181 internal String SNAN
1187 _sNaN = GetLocaleInfo(LocaleStringData.NaNSymbol);
1194 internal String SPOSINFINITY
1198 if (_sPositiveInfinity == null)
1200 _sPositiveInfinity = GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol);
1202 return _sPositiveInfinity;
1207 internal String SNEGINFINITY
1211 if (_sNegativeInfinity == null)
1213 _sNegativeInfinity = GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol);
1215 return _sNegativeInfinity;
1224 // Negative Percent (0-3)
1225 internal int INEGATIVEPERCENT
1229 if (_iNegativePercent == undef)
1231 // Note that <= Windows Vista this is synthesized by native code
1232 _iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat);
1234 return _iNegativePercent;
1238 // Positive Percent (0-11)
1239 internal int IPOSITIVEPERCENT
1243 if (_iPositivePercent == undef)
1245 // Note that <= Windows Vista this is synthesized by native code
1246 _iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat);
1248 return _iPositivePercent;
1252 // Percent (%) symbol
1253 internal String SPERCENT
1257 if (_sPercent == null)
1259 _sPercent = GetLocaleInfo(LocaleStringData.PercentSymbol);
1266 internal String SPERMILLE
1270 if (_sPerMille == null)
1272 _sPerMille = GetLocaleInfo(LocaleStringData.PerMilleSymbol);
1282 // (user can override) local monetary symbol, eg: $
1283 internal String SCURRENCY
1287 if (_sCurrency == null)
1289 _sCurrency = GetLocaleInfo(LocaleStringData.MonetarySymbol);
1295 // international monetary symbol (RegionInfo), eg: USD
1296 internal String SINTLSYMBOL
1300 if (_sIntlMonetarySymbol == null)
1302 _sIntlMonetarySymbol = GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol);
1304 return _sIntlMonetarySymbol;
1308 // English name for this currency (RegionInfo), eg: US Dollar
1309 internal String SENGLISHCURRENCY
1313 if (_sEnglishCurrency == null)
1315 _sEnglishCurrency = GetLocaleInfo(LocaleStringData.CurrencyEnglishName);
1317 return _sEnglishCurrency;
1321 // Native name for this currency (RegionInfo), eg: Schweiz Frank
1322 internal String SNATIVECURRENCY
1326 if (_sNativeCurrency == null)
1328 _sNativeCurrency = GetLocaleInfo(LocaleStringData.CurrencyNativeName);
1330 return _sNativeCurrency;
1334 // internal int iCurrencyDigits ; // (user can override) # local monetary fractional digits
1335 // internal int iCurrency ; // (user can override) positive currency format
1336 // internal int iNegativeCurrency ; // (user can override) negative currency format
1338 // (user can override) monetary grouping of digits
1339 internal int[] WAMONGROUPING
1343 if (_waMonetaryGrouping == null)
1345 _waMonetaryGrouping = GetLocaleInfo(LocaleGroupingData.Monetary);
1347 return _waMonetaryGrouping;
1351 // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1352 internal int IMEASURE
1356 if (_iMeasure == undef)
1358 _iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem);
1364 // (user can override) list Separator
1365 internal String SLIST
1369 if (_sListSeparator == null)
1371 _sListSeparator = GetLocaleInfo(LocaleStringData.ListSeparator);
1373 return _sListSeparator;
1378 ////////////////////////////
1379 // Calendar/Time (Gregorian) //
1380 ////////////////////////////
1382 // (user can override) AM designator
1383 internal String SAM1159
1387 if (_sAM1159 == null)
1389 _sAM1159 = GetLocaleInfo(LocaleStringData.AMDesignator);
1395 // (user can override) PM designator
1396 internal String SPM2359
1400 if (_sPM2359 == null)
1402 _sPM2359 = GetLocaleInfo(LocaleStringData.PMDesignator);
1408 // (user can override) time format
1409 internal String[] LongTimes
1413 if (_saLongTimes == null)
1415 String[] longTimes = GetTimeFormats();
1416 if (longTimes == null || longTimes.Length == 0)
1418 _saLongTimes = Invariant._saLongTimes;
1422 _saLongTimes = longTimes;
1425 return _saLongTimes;
1429 // short time format
1430 // Short times (derived from long times format)
1431 // TODO: NLS Arrowhead - On Windows 7 we should have short times so this isn't necessary
1432 internal String[] ShortTimes
1436 if (_saShortTimes == null)
1438 // Try to get the short times from the OS/culture.dll
1439 String[] shortTimes = null;
1440 shortTimes = GetShortTimeFormats();
1442 if (shortTimes == null || shortTimes.Length == 0)
1445 // If we couldn't find short times, then compute them from long times
1446 // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
1448 shortTimes = DeriveShortTimesFromLong();
1451 /* The above logic doesn't make sense on Mac, since the OS can provide us a "short time pattern".
1452 * currently this is the 4th element in the array returned by LongTimes. We'll add this to our array
1453 * if it doesn't exist.
1455 shortTimes = AdjustShortTimesForMac(shortTimes);
1457 // Found short times, use them
1458 _saShortTimes = shortTimes;
1460 return _saShortTimes;
1464 private string[] AdjustShortTimesForMac(string[] shortTimes)
1469 private string[] DeriveShortTimesFromLong()
1471 // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string
1472 // between it and the previous marker, if any. If its a short, unescaped separator,
1473 // then we don't retain that part.
1474 // We then check after the ss and remove anything before the next h,H,m,t...
1475 string[] shortTimes = new string[LongTimes.Length];
1477 for (int i = 0; i < LongTimes.Length; i++)
1479 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
1484 private static string StripSecondsFromPattern(string time)
1486 bool bEscape = false;
1487 int iLastToken = -1;
1490 for (int j = 0; j < time.Length; j++)
1492 // Change escape mode?
1493 if (time[j] == '\'')
1500 // See if there was a single \
1501 if (time[j] == '\\')
1515 // Check for seconds
1517 // Found seconds, see if there was something unescaped and short between
1518 // the last marker and the seconds. Windows says separator can be a
1519 // maximum of three characters (without null)
1520 // If 1st or last characters were ', then ignore it
1521 if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
1522 (time[iLastToken + 1] != '\'') &&
1523 (time[j - 1] != '\''))
1525 // There was something there we want to remember
1526 if (iLastToken >= 0)
1533 int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
1546 time = time.Substring(0, j) + sep + time.Substring(endIndex);
1558 private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
1560 bool bEscape = false;
1561 containsSpace = false;
1562 for (; index < time.Length; index++)
1564 switch (time[index])
1571 if (time[index] == ' ')
1573 containsSpace = true;
1577 containsSpace = true;
1590 containsSpace = false;
1594 // (user can override) first day of week
1595 internal int IFIRSTDAYOFWEEK
1599 if (_iFirstDayOfWeek == undef)
1601 _iFirstDayOfWeek = GetFirstDayOfWeek();
1603 return _iFirstDayOfWeek;
1607 // (user can override) first week of year
1608 internal int IFIRSTWEEKOFYEAR
1612 if (_iFirstWeekOfYear == undef)
1614 _iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear);
1616 return _iFirstWeekOfYear;
1620 // (user can override default only) short date format
1621 internal String[] ShortDates(CalendarId calendarId)
1623 return GetCalendar(calendarId).saShortDates;
1626 // (user can override default only) long date format
1627 internal String[] LongDates(CalendarId calendarId)
1629 return GetCalendar(calendarId).saLongDates;
1632 // (user can override) date year/month format.
1633 internal String[] YearMonths(CalendarId calendarId)
1635 return GetCalendar(calendarId).saYearMonths;
1639 internal string[] DayNames(CalendarId calendarId)
1641 return GetCalendar(calendarId).saDayNames;
1644 // abbreviated day names
1645 internal string[] AbbreviatedDayNames(CalendarId calendarId)
1647 // Get abbreviated day names for this calendar from the OS if necessary
1648 return GetCalendar(calendarId).saAbbrevDayNames;
1651 // The super short day names
1652 internal string[] SuperShortDayNames(CalendarId calendarId)
1654 return GetCalendar(calendarId).saSuperShortDayNames;
1658 internal string[] MonthNames(CalendarId calendarId)
1660 return GetCalendar(calendarId).saMonthNames;
1663 // Genitive month names
1664 internal string[] GenitiveMonthNames(CalendarId calendarId)
1666 return GetCalendar(calendarId).saMonthGenitiveNames;
1670 internal string[] AbbreviatedMonthNames(CalendarId calendarId)
1672 return GetCalendar(calendarId).saAbbrevMonthNames;
1675 // Genitive month names
1676 internal string[] AbbreviatedGenitiveMonthNames(CalendarId calendarId)
1678 return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
1681 // Leap year month names
1682 // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
1683 // the non-leap names skip the 7th name in the normal month name array
1684 internal string[] LeapYearMonthNames(CalendarId calendarId)
1686 return GetCalendar(calendarId).saLeapYearMonthNames;
1689 // month/day format (single string, no override)
1690 internal String MonthDay(CalendarId calendarId)
1692 return GetCalendar(calendarId).sMonthDay;
1701 // all available calendar type(s), The first one is the default calendar.
1702 internal CalendarId[] CalendarIds
1706 if (_waCalendars == null)
1708 // We pass in an array of ints, and native side fills it up with count calendars.
1709 // We then have to copy that list to a new array of the right size.
1710 // Default calendar should be first
1711 CalendarId[] calendars = new CalendarId[23];
1712 Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be populated by already");
1713 int count = CalendarData.GetCalendars(_sWindowsName, _bUseOverrides, calendars);
1715 // See if we had a calendar to add.
1718 // Failed for some reason, just grab Gregorian from Invariant
1719 _waCalendars = Invariant._waCalendars;
1723 // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
1724 // TODO: Is this hack necessary long-term?
1725 if (_sWindowsName == "zh-TW")
1729 // Do we need to insert calendar 4?
1730 for (int i = 0; i < count; i++)
1732 // Stop if we found calendar four
1733 if (calendars[i] == CalendarId.TAIWAN)
1740 // If not found then insert it
1743 // Insert it as the 2nd calendar
1745 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
1746 Array.Copy(calendars, 1, calendars, 2, 23 - 1 - 1);
1747 calendars[1] = CalendarId.TAIWAN;
1751 // It worked, remember the list
1752 CalendarId[] temp = new CalendarId[count];
1753 Array.Copy(calendars, temp, count);
1755 // Want 1st calendar to be default
1756 // Prior to Vista the enumeration didn't have default calendar first
1757 if (temp.Length > 1)
1759 CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
1767 _waCalendars = temp;
1771 return _waCalendars;
1775 // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number
1776 internal string CalendarName(CalendarId calendarId)
1779 return GetCalendar(calendarId).sNativeName;
1782 internal CalendarData GetCalendar(CalendarId calendarId)
1784 Debug.Assert(calendarId > 0 && calendarId <= CalendarId.LAST_CALENDAR,
1785 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
1787 // arrays are 0 based, calendarIds are 1 based
1788 int calendarIndex = (int)calendarId - 1;
1790 // Have to have calendars
1791 if (_calendars == null)
1793 _calendars = new CalendarData[CalendarData.MAX_CALENDARS];
1796 // we need the following local variable to avoid returning null
1797 // when another thread creates a new array of CalendarData (above)
1798 // right after we insert the newly created CalendarData (below)
1799 CalendarData calendarData = _calendars[calendarIndex];
1800 // Make sure that calendar has data
1801 if (calendarData == null)
1803 Debug.Assert(_sWindowsName != null, "[CultureData.GetCalendar] Expected _sWindowsName to be populated by already");
1804 calendarData = new CalendarData(_sWindowsName, calendarId, this.UseUserOverride);
1805 _calendars[calendarIndex] = calendarData;
1808 return calendarData;
1812 // Text Information //
1816 internal bool IsRightToLeft
1820 // Returns one of the following 4 reading layout values:
1821 // 0 - Left to right (eg en-US)
1822 // 1 - Right to left (eg arabic locales)
1823 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1824 // 3 - Vertical top to bottom with columns proceeding to the right
1825 return (this.IREADINGLAYOUT == 1);
1830 // Returns one of the following 4 reading layout values:
1831 // 0 - Left to right (eg en-US)
1832 // 1 - Right to left (eg arabic locales)
1833 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1834 // 3 - Vertical top to bottom with columns proceeding to the right
1836 // If exposed as a public API, we'd have an enum with those 4 values
1837 private int IREADINGLAYOUT
1841 if (_iReadingLayout == undef)
1843 Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by already");
1844 _iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout);
1847 return (_iReadingLayout);
1851 // The TextInfo name never includes that alternate sort and is always specific
1852 // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
1855 // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
1856 // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
1857 // es-ES_tradnl -> es-ES
1858 internal String STEXTINFO // Text info name to use for text information
1862 // Note: Custom cultures might point at another culture's textinfo, however windows knows how
1863 // to redirect it to the desired textinfo culture, so this is OK.
1864 Debug.Assert(_sRealName != null, "[CultureData.STEXTINFO] Expected _sRealName to be populated by already");
1865 return (_sRealName);
1869 // Compare info name (including sorting key) to use if custom
1870 internal String SCOMPAREINFO
1874 Debug.Assert(_sRealName != null, "[CultureData.SCOMPAREINFO] Expected _sRealName to be populated by already");
1875 return (_sRealName);
1879 internal bool IsSupplementalCustomCulture
1883 return IsCustomCultureId(this.ILANGUAGE);
1887 internal int IDEFAULTANSICODEPAGE // default ansi code page ID (ACP)
1891 if (_iDefaultAnsiCodePage == undef)
1893 _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName);
1895 return _iDefaultAnsiCodePage;
1899 internal int IDEFAULTOEMCODEPAGE // default oem code page ID (OCP or OEM)
1903 if (_iDefaultOemCodePage == undef)
1905 _iDefaultOemCodePage = GetOemCodePage(_sRealName);
1907 return _iDefaultOemCodePage;
1911 internal int IDEFAULTMACCODEPAGE // default macintosh code page
1915 if (_iDefaultMacCodePage == undef)
1917 _iDefaultMacCodePage = GetMacCodePage(_sRealName);
1919 return _iDefaultMacCodePage;
1923 internal int IDEFAULTEBCDICCODEPAGE // default EBCDIC code page
1927 if (_iDefaultEbcdicCodePage == undef)
1929 _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName);
1931 return _iDefaultEbcdicCodePage;
1935 internal int ILANGUAGE
1939 if (_iLanguage == 0)
1941 Debug.Assert(_sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated already");
1942 _iLanguage = LocaleNameToLCID(_sRealName);
1948 internal bool IsNeutralCulture
1952 // InitCultureData told us if we're neutral or not
1957 internal bool IsInvariantCulture
1961 return String.IsNullOrEmpty(this.SNAME);
1965 // Get an instance of our default calendar
1966 internal Calendar DefaultCalendar
1970 CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
1972 if (defaultCalId == 0)
1974 defaultCalId = this.CalendarIds[0];
1977 return CultureInfo.GetCalendarInstance(defaultCalId);
1981 // All of our era names
1982 internal String[] EraNames(CalendarId calendarId)
1984 Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
1986 return this.GetCalendar(calendarId).saEraNames;
1989 internal String[] AbbrevEraNames(CalendarId calendarId)
1991 Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
1993 return this.GetCalendar(calendarId).saAbbrevEraNames;
1996 internal String[] AbbreviatedEnglishEraNames(CalendarId calendarId)
1998 Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2000 return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2003 //// String array DEFAULTS
2004 //// Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2007 // Time separator (derived from time format)
2008 internal String TimeSeparator
2012 if (_sTimeSeparator == null)
2014 string longTimeFormat = GetTimeFormatString();
2015 if (String.IsNullOrEmpty(longTimeFormat))
2017 longTimeFormat = LongTimes[0];
2020 // Compute STIME from time format
2021 _sTimeSeparator = GetTimeSeparator(longTimeFormat);
2023 return _sTimeSeparator;
2027 // Date separator (derived from short date format)
2028 internal String DateSeparator(CalendarId calendarId)
2030 return GetDateSeparator(ShortDates(calendarId)[0]);
2033 //////////////////////////////////////
2034 // Helper Functions to get derived properties //
2035 //////////////////////////////////////
2037 ////////////////////////////////////////////////////////////////////////////
2039 // Unescape a NLS style quote string
2041 // This removes single quotes:
2047 // This removes the first \ of escaped characters:
2048 // fred\'s -> fred's
2052 // We don't build the stringbuilder unless we find a ' or a \. If we find a ' or a \, we
2053 // always build a stringbuilder because we need to remove the ' or \.
2055 ////////////////////////////////////////////////////////////////////////////
2056 private static String UnescapeNlsString(String str, int start, int end)
2058 Debug.Assert(str != null);
2059 Debug.Assert(start >= 0);
2060 Debug.Assert(end >= 0);
2061 StringBuilder result = null;
2063 for (int i = start; i < str.Length && i <= end; i++)
2070 result = new StringBuilder(str, start, i - start, str.Length);
2076 result = new StringBuilder(str, start, i - start, str.Length);
2081 result.Append(str[i]);
2087 result.Append(str[i]);
2094 return (str.Substring(start, end - start + 1));
2096 return (result.ToString());
2099 private static String GetTimeSeparator(String format)
2101 // Time format separator (ie: : in 12:39:00)
2103 // We calculate this from the provided time format
2107 // Find the time separator so that we can pretend we know STIME.
2109 return GetSeparator(format, "Hhms");
2112 private static String GetDateSeparator(String format)
2114 // Date format separator (ie: / in 9/1/03)
2116 // We calculate this from the provided short date
2120 // Find the date separator so that we can pretend we know SDATE.
2122 return GetSeparator(format, "dyM");
2125 private static string GetSeparator(string format, string timeParts)
2127 int index = IndexOfTimePart(format, 0, timeParts);
2131 // Found a time part, find out when it changes
2132 char cTimePart = format[index];
2137 } while (index < format.Length && format[index] == cTimePart);
2139 int separatorStart = index;
2141 // Now we need to find the end of the separator
2142 if (separatorStart < format.Length)
2144 int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
2145 if (separatorEnd != -1)
2147 // From [separatorStart, count) is our string, except we need to unescape
2148 return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
2153 return String.Empty;
2156 private static int IndexOfTimePart(string format, int startIndex, string timeParts)
2158 Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
2159 Debug.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
2160 bool inQuote = false;
2161 for (int i = startIndex; i < format.Length; ++i)
2163 // See if we have a time Part
2164 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
2171 if (i + 1 < format.Length)
2180 --i; //backup since we will move over this next
2194 internal static bool IsCustomCultureId(int cultureId)
2196 return (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED);
2199 internal void GetNFIValues(NumberFormatInfo nfi)
2201 if (this.IsInvariantCulture)
2203 // FUTURE: NumberFormatInfo already has default values for many of these fields. Can we not do this?
2204 nfi.positiveSign = _sPositiveSign;
2205 nfi.negativeSign = _sNegativeSign;
2207 nfi.numberGroupSeparator = _sThousandSeparator;
2208 nfi.numberDecimalSeparator = _sDecimalSeparator;
2209 nfi.numberDecimalDigits = _iDigits;
2210 nfi.numberNegativePattern = _iNegativeNumber;
2212 nfi.currencySymbol = _sCurrency;
2213 nfi.currencyGroupSeparator = _sMonetaryThousand;
2214 nfi.currencyDecimalSeparator = _sMonetaryDecimal;
2215 nfi.currencyDecimalDigits = _iCurrencyDigits;
2216 nfi.currencyNegativePattern = _iNegativeCurrency;
2217 nfi.currencyPositivePattern = _iCurrency;
2221 Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated by already");
2223 nfi.positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign);
2224 nfi.negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign);
2226 nfi.numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator);
2227 nfi.numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator);
2228 nfi.currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator);
2229 nfi.currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator);
2230 nfi.currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol);
2233 nfi.numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount);
2234 nfi.currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount);
2235 nfi.currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat);
2236 nfi.currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat);
2237 nfi.numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat);
2239 // LOCALE_SNATIVEDIGITS (array of 10 single character strings).
2240 string digits = GetLocaleInfo(LocaleStringData.Digits);
2241 nfi.nativeDigits = new string[10];
2242 for (int i = 0; i < nfi.nativeDigits.Length; i++)
2244 nfi.nativeDigits[i] = new string(digits[i], 1);
2247 nfi.digitSubstitution = GetDigitSubstitution(_sRealName);
2251 // Gather additional data
2253 nfi.numberGroupSizes = this.WAGROUPING;
2254 nfi.currencyGroupSizes = this.WAMONGROUPING;
2256 // prefer the cached value since these do not have user overrides
2257 nfi.percentNegativePattern = this.INEGATIVEPERCENT;
2258 nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
2259 nfi.percentSymbol = this.SPERCENT;
2260 nfi.perMilleSymbol = this.SPERMILLE;
2262 nfi.negativeInfinitySymbol = this.SNEGINFINITY;
2263 nfi.positiveInfinitySymbol = this.SPOSINFINITY;
2264 nfi.nanSymbol = this.SNAN;
2267 // We don't have percent values, so use the number values
2269 nfi.percentDecimalDigits = nfi.numberDecimalDigits;
2270 nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
2271 nfi.percentGroupSizes = nfi.numberGroupSizes;
2272 nfi.percentGroupSeparator = nfi.numberGroupSeparator;
2275 // Clean up a few odd values
2278 // Windows usually returns an empty positive sign, but we like it to be "+"
2279 if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
2281 //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user
2282 //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
2283 //decimal point doesn't show up. We'll just hack this here because our default currency format will never use nfi.
2284 if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
2286 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
2291 // This is ONLY used for caching names and shouldn't be used for anything else
2292 internal static string AnsiToLower(string testString)
2296 while (index<testString.Length && (testString[index]<'A' || testString[index]>'Z' ))
2300 if (index >= testString.Length)
2302 return testString; // we didn't really change the string
2305 StringBuilder sb = new StringBuilder(testString.Length);
2306 for (int i=0; i<index; i++)
2308 sb.Append(testString[i]);
2311 sb.Append((char) (testString[index] -'A' + 'a'));
2313 for (int ich = index+1; ich < testString.Length; ich++)
2315 char ch = testString[ich];
2316 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
2319 return (sb.ToString());
2323 /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
2324 /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
2326 private enum LocaleStringData : uint
2328 /// <summary>localized name of locale, eg "German (Germany)" in UI language (coresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
2329 LocalizedDisplayName = 0x00000002,
2330 /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (coresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
2331 EnglishDisplayName = 0x00000072,
2332 /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (coresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
2333 NativeDisplayName = 0x00000073,
2334 /// <summary>Language Display Name for a language, eg "German" in UI language (coresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
2335 LocalizedLanguageName = 0x0000006f,
2336 /// <summary>English name of language, eg "German" (coresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
2337 EnglishLanguageName = 0x00001001,
2338 /// <summary>native name of language, eg "Deutsch" (coresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
2339 NativeLanguageName = 0x00000004,
2340 /// <summary>localized name of country, eg "Germany" in UI language (coresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
2341 LocalizedCountryName = 0x00000006,
2342 /// <summary>English name of country, eg "Germany" (coresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
2343 EnglishCountryName = 0x00001002,
2344 /// <summary>native name of country, eg "Deutschland" (coresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
2345 NativeCountryName = 0x00000008,
2346 /// <summary>abbreviated language name (coresponds to LOCALE_SABBREVLANGNAME)</summary>
2347 AbbreviatedWindowsLanguageName = 0x00000003,
2348 /// <summary>list item separator (coresponds to LOCALE_SLIST)</summary>
2349 ListSeparator = 0x0000000C,
2350 /// <summary>decimal separator (coresponds to LOCALE_SDECIMAL)</summary>
2351 DecimalSeparator = 0x0000000E,
2352 /// <summary>thousand separator (coresponds to LOCALE_STHOUSAND)</summary>
2353 ThousandSeparator = 0x0000000F,
2354 /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
2355 Digits = 0x00000013,
2356 /// <summary>local monetary symbol (coresponds to LOCALE_SCURRENCY)</summary>
2357 MonetarySymbol = 0x00000014,
2358 /// <summary>English currency name (coresponds to LOCALE_SENGCURRNAME)</summary>
2359 CurrencyEnglishName = 0x00001007,
2360 /// <summary>Native currency name (coresponds to LOCALE_SNATIVECURRNAME)</summary>
2361 CurrencyNativeName = 0x00001008,
2362 /// <summary>uintl monetary symbol (coresponds to LOCALE_SINTLSYMBOL)</summary>
2363 Iso4217MonetarySymbol = 0x00000015,
2364 /// <summary>monetary decimal separator (coresponds to LOCALE_SMONDECIMALSEP)</summary>
2365 MonetaryDecimalSeparator = 0x00000016,
2366 /// <summary>monetary thousand separator (coresponds to LOCALE_SMONTHOUSANDSEP)</summary>
2367 MonetaryThousandSeparator = 0x00000017,
2368 /// <summary>AM designator (coresponds to LOCALE_S1159)</summary>
2369 AMDesignator = 0x00000028,
2370 /// <summary>PM designator (coresponds to LOCALE_S2359)</summary>
2371 PMDesignator = 0x00000029,
2372 /// <summary>positive sign (coresponds to LOCALE_SPOSITIVESIGN)</summary>
2373 PositiveSign = 0x00000050,
2374 /// <summary>negative sign (coresponds to LOCALE_SNEGATIVESIGN)</summary>
2375 NegativeSign = 0x00000051,
2376 /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
2377 Iso639LanguageTwoLetterName = 0x00000059,
2378 /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO639LANGNAME2)</summary>
2379 Iso639LanguageThreeLetterName = 0x00000067,
2380 /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
2381 Iso639LanguageName = 0x00000059,
2382 /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO3166CTRYNAME)</summary>
2383 Iso3166CountryName = 0x0000005A,
2384 /// <summary>3 letter ISO country code (coresponds to LOCALE_SISO3166CTRYNAME2)</summary>
2385 Iso3166CountryName2 = 0x00000068, // 3 character ISO country name
2386 /// <summary>Not a Number (coresponds to LOCALE_SNAN)</summary>
2387 NaNSymbol = 0x00000069,
2388 /// <summary>+ Infinity (coresponds to LOCALE_SPOSINFINITY)</summary>
2389 PositiveInfinitySymbol = 0x0000006a,
2390 /// <summary>- Infinity (coresponds to LOCALE_SNEGINFINITY)</summary>
2391 NegativeInfinitySymbol = 0x0000006b,
2392 /// <summary>Fallback name for resources (coresponds to LOCALE_SPARENT)</summary>
2393 ParentName = 0x0000006d,
2394 /// <summary>Fallback name for within the console (coresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
2395 ConsoleFallbackName = 0x0000006e,
2396 /// <summary>Returns the percent symbol (coresponds to LOCALE_SPERCENT)</summary>
2397 PercentSymbol = 0x00000076,
2398 /// <summary>Returns the permille (U+2030) symbol (coresponds to LOCALE_SPERMILLE)</summary>
2399 PerMilleSymbol = 0x00000077
2403 /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
2404 /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
2406 private enum LocaleGroupingData : uint
2408 /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
2410 /// <summary>monetary grouping (coresponds to LOCALE_SMONGROUPING)</summary>
2411 Monetary = 0x00000018,
2415 /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
2416 /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
2418 private enum LocaleNumberData : uint
2420 /// <summary>language id (coresponds to LOCALE_ILANGUAGE)</summary>
2421 LanguageId = 0x00000001,
2422 /// <summary>geographical location id, (coresponds to LOCALE_IGEOID)</summary>
2424 /// <summary>0 = context, 1 = none, 2 = national (coresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
2425 DigitSubstitution = 0x00001014,
2426 /// <summary>0 = metric, 1 = US (coresponds to LOCALE_IMEASURE)</summary>
2427 MeasurementSystem = 0x0000000D,
2428 /// <summary>number of fractional digits (coresponds to LOCALE_IDIGITS)</summary>
2429 FractionalDigitsCount = 0x00000011,
2430 /// <summary>negative number mode (coresponds to LOCALE_INEGNUMBER)</summary>
2431 NegativeNumberFormat = 0x00001010,
2432 /// <summary># local monetary digits (coresponds to LOCALE_ICURRDIGITS)</summary>
2433 MonetaryFractionalDigitsCount = 0x00000019,
2434 /// <summary>positive currency mode (coresponds to LOCALE_ICURRENCY)</summary>
2435 PositiveMonetaryNumberFormat = 0x0000001B,
2436 /// <summary>negative currency mode (coresponds to LOCALE_INEGCURR)</summary>
2437 NegativeMonetaryNumberFormat = 0x0000001C,
2438 /// <summary>type of calendar specifier (coresponds to LOCALE_ICALENDARTYPE)</summary>
2439 CalendarType = 0x00001009,
2440 /// <summary>first day of week specifier (coresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
2441 FirstDayOfWeek = 0x0000100C,
2442 /// <summary>first week of year specifier (coresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
2443 FirstWeekOfYear = 0x0000100D,
2445 /// Returns one of the following 4 reading layout values:
2446 /// 0 - Left to right (eg en-US)
2447 /// 1 - Right to left (eg arabic locales)
2448 /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2449 /// 3 - Vertical top to bottom with columns proceeding to the right
2450 /// (coresponds to LOCALE_IREADINGLAYOUT)
2452 ReadingLayout = 0x00000070,
2453 /// <summary>Returns 0-11 for the negative percent format (coresponds to LOCALE_INEGATIVEPERCENT)</summary>
2454 NegativePercentFormat = 0x00000074,
2455 /// <summary>Returns 0-3 for the positive percent format (coresponds to LOCALE_IPOSITIVEPERCENT)</summary>
2456 PositivePercentFormat = 0x00000075,
2457 /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
2458 OemCodePage = 0x0000000B,
2459 /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
2460 AnsiCodePage = 0x00001004,
2461 /// <summary>default mac code page (coresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
2462 MacCodePage = 0x00001011,
2463 /// <summary>default ebcdic code page (coresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
2464 EbcdicCodePage = 0x00001012,