Merge pull request #8329 from litian2025/Fix_SIMDScalarMoveEncoding
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Globalization / CultureData.cs
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.
4
5 namespace System.Globalization
6 {
7
8     using System;
9     using System.Collections;
10     using System.Collections.Generic;
11     using System.Text;
12     using System.Threading;
13     using System.Runtime.CompilerServices;
14     using System.Runtime.InteropServices;
15     using System.Runtime.Versioning;
16     using System.Diagnostics;
17     using System.Diagnostics.Contracts;
18     using System.Security;
19
20     //
21     // List of culture data
22     // Note the we cache overrides.
23     // Note that localized names (resource names) aren't available from here.
24     //
25
26     //
27     // Our names are a tad confusing.
28     //
29     // sWindowsName -- The name that windows thinks this culture is, ie:
30     //                            en-US if you pass in en-US
31     //                            de-DE_phoneb if you pass in de-DE_phoneb
32     //                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
33     //                            fj if you pass in fj (neutral, post-Windows 7 machine)
34     //
35     // sRealName -- The name you used to construct the culture, in pretty form
36     //                       en-US if you pass in EN-us
37     //                       en if you pass in en
38     //                       de-DE_phoneb if you pass in de-DE_phoneb
39     //
40     // sSpecificCulture -- The specific culture for this culture
41     //                             en-US for en-US
42     //                             en-US for en
43     //                             de-DE_phoneb for alt sort
44     //                             fj-FJ for fj (neutral)
45     //
46     // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
47     //                en-US if you pass in en-US
48     //                en if you pass in en
49     //                de-DE if you pass in de-DE_phoneb
50     //
51
52     // StructLayout is needed here otherwise compiler can re-arrange the fields.
53     // We have to keep this in-sync with the definition in comnlsinfo.h
54     //
55     // WARNING WARNING WARNING
56     //
57     // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CultureDataBaseObject)
58     // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureDataBaseObject
59     // WARNING: must be manually structured to match the true loaded class layout
60     //
61     [FriendAccessAllowed]
62     internal class CultureData
63     {
64         const int undef = -1;
65
66         // Override flag
67         private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
68         private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
69
70         // Identity
71         private String sName; // locale name (ie: en-us, NO sort info, but could be neutral)
72         private String sParent; // Parent name (which may be a custom locale/culture)
73         private String sLocalizedDisplayName; // Localized pretty name for this locale
74         private String sEnglishDisplayName; // English pretty name for this locale
75         private String sNativeDisplayName; // Native pretty name for this locale
76         private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
77
78         // Language
79         private String sISO639Language; // ISO 639 Language Name
80         private String sLocalizedLanguage; // Localized name for this language
81         private String sEnglishLanguage; // English name for this language
82         private String sNativeLanguage; // Native name of this language
83
84         // Region
85         private String sRegionName; // (RegionInfo)
86         private int iGeoId = undef; // GeoId
87         private String sLocalizedCountry; // localized country name
88         private String sEnglishCountry; // english country name (RegionInfo)
89         private String sNativeCountry; // native country name
90         private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
91
92         // Numbers
93         private String sPositiveSign; // (user can override) positive sign
94         private String sNegativeSign; // (user can override) negative sign
95         private String[] saNativeDigits; // (user can override) native characters for digits 0-9
96         // (nfi populates these 5, don't have to be = undef)
97         private int iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
98         private int iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
99         private int iDigits; // (user can override) number of fractional digits
100         private int iNegativeNumber; // (user can override) negative number format
101         private int[] waGrouping; // (user can override) grouping of digits
102         private String sDecimalSeparator; // (user can override) decimal separator
103         private String sThousandSeparator; // (user can override) thousands separator
104         private String sNaN; // Not a Number
105         private String sPositiveInfinity; // + Infinity
106         private String sNegativeInfinity; // - Infinity
107
108         // Percent
109         private int iNegativePercent = undef; // Negative Percent (0-3)
110         private int iPositivePercent = undef; // Positive Percent (0-11)
111         private String sPercent; // Percent (%) symbol
112         private String sPerMille; // PerMille (‰) symbol
113
114         // Currency
115         private String sCurrency; // (user can override) local monetary symbol
116         private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
117         private String sEnglishCurrency; // English name for this currency
118         private String sNativeCurrency; // Native name for this currency
119         // (nfi populates these 4, don't have to be = undef)
120         private int iCurrencyDigits; // (user can override) # local monetary fractional digits
121         private int iCurrency; // (user can override) positive currency format
122         private int iNegativeCurrency; // (user can override) negative currency format
123         private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
124         private String sMonetaryDecimal; // (user can override) monetary decimal separator
125         private String sMonetaryThousand; // (user can override) monetary thousands separator
126
127         // Misc
128         private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
129         private String sListSeparator; // (user can override) list separator
130         //        private int    iPaperSize               ; // default paper size (RegionInfo)
131
132         // Time
133         private String sAM1159; // (user can override) AM designator
134         private String sPM2359; // (user can override) PM designator
135         private String sTimeSeparator;
136         private volatile String[] saLongTimes; // (user can override) time format
137         private volatile String[] saShortTimes; // short time format
138         private volatile String[] saDurationFormats; // time duration format
139
140         // Calendar specific data
141         private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
142         private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
143         private volatile int[] waCalendars; // all available calendar type(s).  The first one is the default calendar
144
145         // Store for specific data about each calendar
146         private CalendarData[] calendars; // Store for specific calendar data
147
148         // Text information
149         private int iReadingLayout = undef; // Reading layout data
150         // 0 - Left to right (eg en-US)
151         // 1 - Right to left (eg arabic locales)
152         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
153         // 3 - Vertical top to bottom with columns proceeding to the right
154
155         private String sTextInfo; // Text info name to use for custom
156         private String sCompareInfo; // Compare info name (including sorting key) to use if custom
157         private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
158
159         private int iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
160         private int iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
161         private int iDefaultMacCodePage = undef; // default macintosh code page
162         private int iDefaultEbcdicCodePage = undef; // default EBCDIC code page
163
164         // These are desktop only, not coreclr
165         private int    iLanguage; // locale ID (0409) - NO sort information
166         private String sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
167         private String sAbbrevCountry; // abbreviated country name (RegionInfo) (Windows Region Name) ex: USA
168         private String sISO639Language2; // 3 char ISO 639 lang name 2 ex: eng
169         private String sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
170         private int    iInputLanguageHandle=undef;// input language handle
171         private String sConsoleFallbackName; // The culture name for the console fallback UI culture
172         private String sKeyboardsToInstall; // Keyboard installation string.
173         private String fontSignature; // Font signature (16 WORDS)
174
175         // The bools all need to be in one spot
176         private bool bUseOverrides; // use user overrides?
177         private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
178         private bool bWin32Installed; // Flags indicate if the culture is Win32 installed
179         private bool bFramework; // Flags for indicate if the culture is one of Whidbey cultures
180
181         // Region Name to Culture Name mapping table
182         // (In future would be nice to be in registry or something)
183
184         //Using a property so we avoid creating the dictionary untill we need it
185         private static Dictionary<string, string> RegionNames
186         {
187             get 
188             {
189                 if (s_RegionNames == null)
190                 {
191                     var regionNames = new Dictionary<string, string> {
192                         { "029", "en-029" },
193                         { "AE",  "ar-AE" },
194                         { "AF",  "prs-AF" },
195                         { "AL",  "sq-AL" },
196                         { "AM",  "hy-AM" },
197                         { "AR",  "es-AR" },
198                         { "AT",  "de-AT" },
199                         { "AU",  "en-AU" },
200                         { "AZ",  "az-Cyrl-AZ" },
201                         { "BA",  "bs-Latn-BA" },
202                         { "BD",  "bn-BD" },
203                         { "BE",  "nl-BE" },
204                         { "BG",  "bg-BG" },
205                         { "BH",  "ar-BH" },
206                         { "BN",  "ms-BN" },
207                         { "BO",  "es-BO" },
208                         { "BR",  "pt-BR" },
209                         { "BY",  "be-BY" },
210                         { "BZ",  "en-BZ" },
211                         { "CA",  "en-CA" },
212                         { "CH",  "it-CH" },
213                         { "CL",  "es-CL" },
214                         { "CN",  "zh-CN" },
215                         { "CO",  "es-CO" },
216                         { "CR",  "es-CR" },
217                         { "CS",  "sr-Cyrl-CS" },
218                         { "CZ",  "cs-CZ" },
219                         { "DE",  "de-DE" },
220                         { "DK",  "da-DK" },
221                         { "DO",  "es-DO" },
222                         { "DZ",  "ar-DZ" },
223                         { "EC",  "es-EC" },
224                         { "EE",  "et-EE" },
225                         { "EG",  "ar-EG" },
226                         { "ES",  "es-ES" },
227                         { "ET",  "am-ET" },
228                         { "FI",  "fi-FI" },
229                         { "FO",  "fo-FO" },
230                         { "FR",  "fr-FR" },
231                         { "GB",  "en-GB" },
232                         { "GE",  "ka-GE" },
233                         { "GL",  "kl-GL" },
234                         { "GR",  "el-GR" },
235                         { "GT",  "es-GT" },
236                         { "HK",  "zh-HK" },
237                         { "HN",  "es-HN" },
238                         { "HR",  "hr-HR" },
239                         { "HU",  "hu-HU" },
240                         { "ID",  "id-ID" },
241                         { "IE",  "en-IE" },
242                         { "IL",  "he-IL" },
243                         { "IN",  "hi-IN" },
244                         { "IQ",  "ar-IQ" },
245                         { "IR",  "fa-IR" },
246                         { "IS",  "is-IS" },
247                         { "IT",  "it-IT" },
248                         { "IV",  "" },
249                         { "JM",  "en-JM" },
250                         { "JO",  "ar-JO" },
251                         { "JP",  "ja-JP" },
252                         { "KE",  "sw-KE" },
253                         { "KG",  "ky-KG" },
254                         { "KH",  "km-KH" },
255                         { "KR",  "ko-KR" },
256                         { "KW",  "ar-KW" },
257                         { "KZ",  "kk-KZ" },
258                         { "LA",  "lo-LA" },
259                         { "LB",  "ar-LB" },
260                         { "LI",  "de-LI" },
261                         { "LK",  "si-LK" },
262                         { "LT",  "lt-LT" },
263                         { "LU",  "lb-LU" },
264                         { "LV",  "lv-LV" },
265                         { "LY",  "ar-LY" },
266                         { "MA",  "ar-MA" },
267                         { "MC",  "fr-MC" },
268                         { "ME",  "sr-Latn-ME" },
269                         { "MK",  "mk-MK" },
270                         { "MN",  "mn-MN" },
271                         { "MO",  "zh-MO" },
272                         { "MT",  "mt-MT" },
273                         { "MV",  "dv-MV" },
274                         { "MX",  "es-MX" },
275                         { "MY",  "ms-MY" },
276                         { "NG",  "ig-NG" },
277                         { "NI",  "es-NI" },
278                         { "NL",  "nl-NL" },
279                         { "NO",  "nn-NO" },
280                         { "NP",  "ne-NP" },
281                         { "NZ",  "en-NZ" },
282                         { "OM",  "ar-OM" },
283                         { "PA",  "es-PA" },
284                         { "PE",  "es-PE" },
285                         { "PH",  "en-PH" },
286                         { "PK",  "ur-PK" },
287                         { "PL",  "pl-PL" },
288                         { "PR",  "es-PR" },
289                         { "PT",  "pt-PT" },
290                         { "PY",  "es-PY" },
291                         { "QA",  "ar-QA" },
292                         { "RO",  "ro-RO" },
293                         { "RS",  "sr-Latn-RS" },
294                         { "RU",  "ru-RU" },
295                         { "RW",  "rw-RW" },
296                         { "SA",  "ar-SA" },
297                         { "SE",  "sv-SE" },
298                         { "SG",  "zh-SG" },
299                         { "SI",  "sl-SI" },
300                         { "SK",  "sk-SK" },
301                         { "SN",  "wo-SN" },
302                         { "SV",  "es-SV" },
303                         { "SY",  "ar-SY" },
304                         { "TH",  "th-TH" },
305                         { "TJ",  "tg-Cyrl-TJ" },
306                         { "TM",  "tk-TM" },
307                         { "TN",  "ar-TN" },
308                         { "TR",  "tr-TR" },
309                         { "TT",  "en-TT" },
310                         { "TW",  "zh-TW" },
311                         { "UA",  "uk-UA" },
312                         { "US",  "en-US" },
313                         { "UY",  "es-UY" },
314                         { "UZ",  "uz-Cyrl-UZ" },
315                         { "VE",  "es-VE" },
316                         { "VN",  "vi-VN" },
317                         { "YE",  "ar-YE" },
318                         { "ZA",  "af-ZA" },
319                         { "ZW",  "en-ZW" }
320                     };
321                     s_RegionNames = regionNames;
322                 }
323                 return s_RegionNames;
324             }
325         }
326         private volatile static Dictionary<string, string> s_RegionNames;
327
328         /////////////////////////////////////////////////////////////////////////
329         // Build our invariant information
330         //
331         // We need an invariant instance, which we build hard-coded
332         /////////////////////////////////////////////////////////////////////////
333         internal static CultureData Invariant  
334         {
335             get 
336             {
337                 if (s_Invariant == null)
338                 {
339                     // Make a new culturedata
340                     CultureData invariant = new CultureData();
341
342                     // Call the native code to get the value of bWin32Installed.
343                     // For versions <= Vista, we set this to false for compatibility with v2.
344                     // For Windows 7, the flag is true.
345                     invariant.bUseOverrides = false;
346                     invariant.sRealName = "";
347
348                     // Ask the native code to fill it out for us, we only need the field IsWin32Installed
349                     nativeInitCultureData(invariant);
350
351                     // Basics
352                     // Note that we override the resources since this IS NOT supposed to change (by definition)
353                     invariant.bUseOverrides = false;
354                     invariant.sRealName = "";                     // Name you passed in (ie: en-US, en, or de-DE_phoneb)
355                     invariant.sWindowsName = "";                     // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
356
357                     // Identity
358                     invariant.sName = "";                     // locale name (ie: en-us)
359                     invariant.sParent = "";                     // Parent name (which may be a custom locale/culture)
360                     invariant.bNeutral = false;                   // Flags for the culture (ie: neutral or not right now)
361                     // Don't set invariant.bWin32Installed, we used nativeInitCultureData for that.
362                     invariant.bFramework = true;
363
364                     invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
365                     invariant.sNativeDisplayName = "Invariant Language (Invariant Country)";  // Native pretty name for this locale
366                     invariant.sSpecificCulture = "";                     // The culture name to be used in CultureInfo.CreateSpecificCulture()
367
368                     // Language
369                     invariant.sISO639Language = "iv";                   // ISO 639 Language Name
370                     invariant.sLocalizedLanguage = "Invariant Language";   // Display name for this Language
371                     invariant.sEnglishLanguage = "Invariant Language";   // English name for this language
372                     invariant.sNativeLanguage = "Invariant Language";   // Native name of this language
373
374                     // Region
375                     invariant.sRegionName = "IV";                   // (RegionInfo)
376                     // Unused for now:
377                     //            invariant.iCountry              =1;                      // country code (RegionInfo)
378                     invariant.iGeoId = 244;                    // GeoId (Windows Only)
379                     invariant.sEnglishCountry = "Invariant Country";    // english country name (RegionInfo)
380                     invariant.sNativeCountry = "Invariant Country";    // native country name (Windows Only)
381                     invariant.sISO3166CountryName = "IV";                   // (RegionInfo), ie: US
382
383                     // Numbers
384                     invariant.sPositiveSign = "+";                    // positive sign
385                     invariant.sNegativeSign = "-";                    // negative sign
386                     invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9
387                     invariant.iDigitSubstitution = 1;                      // Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
388                     invariant.iLeadingZeros = 1;                      // leading zeros 0=no leading zeros, 1=leading zeros
389                     invariant.iDigits = 2;                      // number of fractional digits
390                     invariant.iNegativeNumber = 1;                      // negative number format
391                     invariant.waGrouping = new int[] { 3 };          // grouping of digits
392                     invariant.sDecimalSeparator = ".";                    // decimal separator
393                     invariant.sThousandSeparator = ",";                    // thousands separator
394                     invariant.sNaN = "NaN";                  // Not a Number
395                     invariant.sPositiveInfinity = "Infinity";             // + Infinity
396                     invariant.sNegativeInfinity = "-Infinity";            // - Infinity
397
398                     // Percent
399                     invariant.iNegativePercent = 0;                      // Negative Percent (0-3)
400                     invariant.iPositivePercent = 0;                      // Positive Percent (0-11)
401                     invariant.sPercent = "%";                    // Percent (%) symbol
402                     invariant.sPerMille = "\x2030";               // PerMille(‰) symbol
403
404                     // Currency
405                     invariant.sCurrency = "\x00a4";                // local monetary symbol "¤: for international monetary symbol
406                     invariant.sIntlMonetarySymbol = "XDR";                  // international monetary symbol (RegionInfo)
407                     invariant.sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
408                     invariant.sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
409                     invariant.iCurrencyDigits = 2;                      // # local monetary fractional digits
410                     invariant.iCurrency = 0;                      // positive currency format
411                     invariant.iNegativeCurrency = 0;                      // negative currency format
412                     invariant.waMonetaryGrouping = new int[] { 3 };          // monetary grouping of digits
413                     invariant.sMonetaryDecimal = ".";                    // monetary decimal separator
414                     invariant.sMonetaryThousand = ",";                    // monetary thousands separator
415
416                     // Misc
417                     invariant.iMeasure = 0;                      // system of measurement 0=metric, 1=US (RegionInfo)
418                     invariant.sListSeparator = ",";                    // list separator
419                     // Unused for now:
420                     //            invariant.iPaperSize            =9;                      // default paper size (RegionInfo)
421                     //            invariant.waFontSignature       ="\x0002\x0000\x0000\x0000\x0000\x0000\x0000\x8000\x0001\x0000\x0000\x8000\x0001\x0000\x0000\x8000"; // Font signature (16 WORDS) (Windows Only)
422
423                     // Time
424                     invariant.sAM1159 = "AM";                   // AM designator
425                     invariant.sPM2359 = "PM";                   // PM designator
426                     invariant.saLongTimes = new String[] { "HH:mm:ss" };                             // time format
427                     invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
428                     invariant.saDurationFormats = new String[] { "HH:mm:ss" };                             // time duration format
429
430                     // Calendar specific data
431                     invariant.iFirstDayOfWeek = 0;                      // first day of week
432                     invariant.iFirstWeekOfYear = 0;                      // first week of year
433                     invariant.waCalendars = new int[] { (int)CalendarId.GREGORIAN };       // all available calendar type(s).  The first one is the default calendar
434
435                     // Store for specific data about each calendar
436                     invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS];
437                     invariant.calendars[0] = CalendarData.Invariant;
438
439                     // Text information
440                     invariant.iReadingLayout = 0;                      // Reading Layout = RTL
441
442                     invariant.sTextInfo = "";                     // Text info name to use for custom
443                     invariant.sCompareInfo = "";                     // Compare info name (including sorting key) to use if custom
444                     invariant.sScripts = "Latn;";                // Typical Scripts for this locale (latn,cyrl, etc)
445
446                     invariant.iLanguage = 0x007f;                 // locale ID (0409) - NO sort information
447                     invariant.iDefaultAnsiCodePage = 1252;                   // default ansi code page ID (ACP)
448                     invariant.iDefaultOemCodePage = 437;                    // default oem code page ID (OCP or OEM)
449                     invariant.iDefaultMacCodePage = 10000;                  // default macintosh code page
450                     invariant.iDefaultEbcdicCodePage = 037;                    // default EBCDIC code page
451             invariant.sAbbrevLang = "IVL";                     // abbreviated language name (Windows Language Name)
452             invariant.sAbbrevCountry = "IVC";                  // abbreviated country name (RegionInfo) (Windows Region Name)
453                     invariant.sISO639Language2 = "ivl";                  // 3 char ISO 639 lang name 2
454                     invariant.sISO3166CountryName2 = "ivc";                  // 3 char ISO 3166 country name 2 2(RegionInfo)
455                     invariant.iInputLanguageHandle = 0x007f;                 // input language handle
456                     invariant.sConsoleFallbackName = "";                     // The culture name for the console fallback UI culture
457                     invariant.sKeyboardsToInstall = "0409:00000409";        // Keyboard installation string.
458                     // Remember it
459                     s_Invariant = invariant;
460                 }
461                 return s_Invariant;
462             }
463         }
464         private volatile static CultureData s_Invariant;
465
466         ///////////////
467         // Constructors //
468         ///////////////
469         // Cache of cultures we've already looked up
470         private static volatile Dictionary<String, CultureData> s_cachedCultures;
471
472         [FriendAccessAllowed]
473         internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
474         {
475             // First do a shortcut for Invariant
476             if (String.IsNullOrEmpty(cultureName))
477             {
478                 return CultureData.Invariant;
479             }
480
481             // Try the hash table first
482             String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
483             Dictionary<String, CultureData> tempHashTable = s_cachedCultures;
484             if (tempHashTable == null)
485             {
486                 // No table yet, make a new one
487                 tempHashTable = new Dictionary<String, CultureData>();
488             }
489             else
490             {
491                 // Check the hash table
492                 CultureData retVal;
493                 lock (((ICollection)tempHashTable).SyncRoot)
494                 {
495                     tempHashTable.TryGetValue(hashName, out retVal);
496                 }
497                 if (retVal != null)
498                 {
499                     return retVal;
500                 }
501             }
502
503             // Not found in the hash table, need to see if we can build one that works for us
504             CultureData culture = CreateCultureData(cultureName, useUserOverride);
505             if (culture == null)
506             {
507                 return null;
508             }
509
510             // Found one, add it to the cache
511             lock (((ICollection)tempHashTable).SyncRoot)
512             {
513                 tempHashTable[hashName] = culture;
514             }
515
516             // Copy the hashtable to the corresponding member variables.  This will potentially overwrite
517             // new tables simultaneously created by a new thread, but maximizes thread safety.
518             s_cachedCultures = tempHashTable;
519
520             return culture;
521         }
522
523         private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
524         {
525             CultureData culture = new CultureData();
526             culture.bUseOverrides = useUserOverride;
527             culture.sRealName = cultureName;
528
529             // Ask native code if that one's real
530             if (culture.InitCultureData() == false)
531             {
532                 return null;
533             }
534
535             return culture;
536         }
537
538         private bool InitCultureData()
539         {
540             if (nativeInitCultureData(this) == false)
541             {
542                 return false;
543             }
544             return true;
545         }
546
547         // Cache of regions we've already looked up
548         private static volatile Dictionary<String, CultureData> s_cachedRegions;
549
550         internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
551         {
552             // First do a shortcut for Invariant
553             if (String.IsNullOrEmpty(cultureName))
554             {
555                 return CultureData.Invariant;
556             }
557
558             //
559             // First check if GetCultureData() can find it (ie: its a real culture)
560             //
561             CultureData retVal = GetCultureData(cultureName, useUserOverride);
562             if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
563
564             //
565             // Not a specific culture, perhaps it's region-only name
566             // (Remember this isn't a core clr path where that's not supported)
567             //
568
569             // If it was neutral remember that so that RegionInfo() can throw the right exception
570             CultureData neutral = retVal;
571
572             // Try the hash table next
573             String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
574             Dictionary<String, CultureData> tempHashTable = s_cachedRegions;
575             if (tempHashTable == null)
576             {
577                 // No table yet, make a new one
578                 tempHashTable = new Dictionary<String, CultureData>();
579             }
580             else
581             {
582                 // Check the hash table
583                 lock (((ICollection)tempHashTable).SyncRoot)
584                 {
585                     tempHashTable.TryGetValue(hashName, out retVal);
586                 }
587                 if (retVal != null)
588                 {
589                     return retVal;
590                 }
591             }
592
593             //
594             // Not found in the hash table, look it up the hard way
595             //
596
597             // If not a valid mapping from the registry we'll have to try the hard coded table
598             if (retVal == null || (retVal.IsNeutralCulture == true))
599             {
600                 // Not a valid mapping, try the hard coded table
601                 if (RegionNames.ContainsKey(cultureName))
602                 {
603                     // Make sure we can get culture data for it
604                     retVal = GetCultureData(RegionNames[cultureName], useUserOverride);
605                 }
606             }
607
608             // If not found in the hard coded table we'll have to find a culture that works for us
609             if (retVal == null || (retVal.IsNeutralCulture == true))
610             {
611                 // Not found in the hard coded table, need to see if we can find a culture that works for us
612                 // Not a real culture name, see if it matches a region name
613                 // (we just return the first culture we match)
614                 CultureInfo[] specifics = SpecificCultures;
615                 for (int i = 0; i < specifics.Length; i++)
616                 {
617                     if (String.Compare(specifics[i].m_cultureData.SREGIONNAME, cultureName, StringComparison.OrdinalIgnoreCase) == 0)
618                     {
619                         // Matched, use this culture
620                         retVal = specifics[i].m_cultureData;
621                         break;
622                     }
623                 }
624             }
625
626             // If we found one we can use, then cash it for next time
627             if (retVal != null && (retVal.IsNeutralCulture == false))
628             {
629                 // first add it to the cache
630                 lock (((ICollection)tempHashTable).SyncRoot)
631                 {
632                     tempHashTable[hashName] = retVal;
633                 }
634
635                 // Copy the hashtable to the corresponding member variables.  This will potentially overwrite
636                 // new tables simultaneously created by a new thread, but maximizes thread safety.
637                 s_cachedRegions = tempHashTable;
638             }
639             else
640             {
641                 // Unable to find a matching culture/region, return null or neutral
642                 // (regionInfo throws a more specific exception on neutrals)
643                 retVal = neutral;
644             }
645
646             // Return the found culture to use, null, or the neutral culture.
647             return retVal;
648         }
649
650 #if FEATURE_USE_LCID
651         // Obtain locale name from LCID
652         // NOTE: This will get neutral names, unlike the OS API
653         [MethodImplAttribute(MethodImplOptions.InternalCall)]
654         internal static extern String LCIDToLocaleName(int lcid);
655
656         // We'd rather people use the named version since this doesn't allow custom locales
657         internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
658         {
659             String localeName = null;
660             CultureData retVal = null;
661
662             if (localeName == null)
663             {
664                 // Convert the lcid to a name, then use that
665                 // Note that this'll return neutral names (unlike Vista native API)
666                 localeName = LCIDToLocaleName(culture);
667             }
668
669             // If its not valid, then throw
670             if (String.IsNullOrEmpty(localeName))
671             {
672                 // Could be valid for Invariant
673                 if (culture == 0x007f)
674                     return Invariant;
675             }
676             else
677             {
678                 // Valid name, use it
679                 retVal = GetCultureData(localeName, bUseUserOverride);
680             }
681
682             // If not successful, throw
683             if (retVal == null)
684                 throw new CultureNotFoundException(
685                     nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported"));
686
687             // Return the one we found
688             return retVal;
689         }
690 #endif
691
692         // Clear our internal caches
693         internal static void ClearCachedData()
694         {
695             s_cachedCultures = null;
696             s_cachedRegions = null;
697             s_replacementCultureNames = null;
698         }
699
700         internal static CultureInfo[] GetCultures(CultureTypes types)
701         {
702             // Disable  warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
703 #pragma warning disable 618
704             // Validate flags
705             if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
706                                                             CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
707                                                             CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
708                                                             CultureTypes.FrameworkCultures)) != 0)
709             {
710                 throw new ArgumentOutOfRangeException(
711                                 nameof(types),
712                                 String.Format(
713                                     CultureInfo.CurrentCulture,
714                                     Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
715             }
716
717             //
718             // CHANGE FROM Whidbey
719             //
720             // We have deprecated CultureTypes.FrameworkCultures.
721             // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
722             //
723
724             // We have deprecated CultureTypes.WindowsOnlyCultures.
725             // When this enum is used, we will return an empty array for this enum.
726             if ((types & CultureTypes.WindowsOnlyCultures) != 0)
727             {
728                 // Remove the enum as it is an no-op.
729                 types &= (~CultureTypes.WindowsOnlyCultures);
730             }
731
732             String[] cultureNames = null;
733
734             //
735             // Call nativeEnumCultureNames() to get a string array of culture names based on the specified
736             // enumeration type.
737             //
738             // nativeEnumCultureNames is a QCall.  We need to use a reference to return the string array
739             // allocated from the QCall.  That ref has to be wrapped as object handle.
740             // See vm\qcall.h for details in QCall.
741             //
742
743             if (nativeEnumCultureNames((int)types, JitHelpers.GetObjectHandleOnStack(ref cultureNames)) == 0)
744             {
745                 return new CultureInfo[0];
746             }
747
748             int arrayLength = cultureNames.Length;
749
750             CultureInfo[] cultures = new CultureInfo[arrayLength];
751
752             for (int i = 0; i < cultureNames.Length; i++)
753             {
754                 cultures[i] = new CultureInfo(cultureNames[i]);
755             }
756 #pragma warning restore 618
757
758             return cultures;
759         }
760
761         internal static volatile CultureInfo[] specificCultures;
762
763         private static CultureInfo[] SpecificCultures
764         {
765             get
766             {
767                 if (specificCultures == null)
768                     specificCultures = GetCultures(CultureTypes.SpecificCultures);
769
770                 return specificCultures;
771             }
772         }
773
774         internal bool IsReplacementCulture
775         {
776             get
777             {
778                 return IsReplacementCultureName(this.SNAME);
779             }
780         }
781
782         internal static volatile String[] s_replacementCultureNames;
783
784         ////////////////////////////////////////////////////////////////////////
785         //
786         // Cache for the known replacement cultures.
787         // This is used by CultureInfo.CultureType to check if a culture is a
788         // replacement culture.
789         //
790         ////////////////////////////////////////////////////////////////////////
791
792
793         private static bool IsReplacementCultureName(String name)
794         {
795             Debug.Assert(name != null, "IsReplacementCultureName(): name should not be null");
796             String[] replacementCultureNames = s_replacementCultureNames;
797             if (replacementCultureNames == null)
798             {
799                 if (nativeEnumCultureNames((int)CultureTypes.ReplacementCultures, JitHelpers.GetObjectHandleOnStack(ref replacementCultureNames)) == 0)
800                 {
801                     return false;
802                 }
803
804                 // Even if we don't have any replacement cultures, the returned replacementCultureNames will still an empty string array, not null.
805                 Debug.Assert(name != null, "IsReplacementCultureName(): replacementCultureNames should not be null");
806                 Array.Sort(replacementCultureNames);
807                 s_replacementCultureNames = replacementCultureNames;
808             }
809             return Array.BinarySearch(replacementCultureNames, name) >= 0;
810         }
811
812         ////////////////////////////////////////////////////////////////////////
813         //
814         //  All the accessors
815         //
816         //  Accessors for our data object items
817         //
818         ////////////////////////////////////////////////////////////////////////
819
820         ///////////
821         // Identity //
822         ///////////
823
824         // The real name used to construct the locale (ie: de-DE_phoneb)
825         internal String CultureName
826         {
827             get
828             {
829                 Debug.Assert(this.sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
830                 // since windows doesn't know about zh-CHS and zh-CHT,
831                 // we leave sRealName == zh-Hanx but we still need to
832                 // pretend that it was zh-CHX.
833                 switch (this.sName)
834                 {
835                     case "zh-CHS":
836                     case "zh-CHT":
837                         return this.sName;
838                 }
839                 return this.sRealName;
840             }
841         }
842
843         // Are overrides enabled?
844         internal bool UseUserOverride
845         {
846             get
847             {
848                 return this.bUseOverrides;
849             }
850         }
851
852         // locale name (ie: de-DE, NO sort information)
853         internal String SNAME
854         {
855             get
856             {
857                 //                Debug.Assert(this.sName != null,
858                 //                    "[CultureData.SNAME] Expected this.sName to be populated by COMNlsInfo::nativeInitCultureData already");
859                 if (this.sName == null)
860                 {
861                     this.sName = String.Empty;
862                 }
863                 return this.sName;
864             }
865         }
866
867         // Parent name (which may be a custom locale/culture)
868         internal String SPARENT
869         {
870             get
871             {
872                 if (this.sParent == null)
873                 {
874                     // Ask using the real name, so that we get parents of neutrals
875                     this.sParent = DoGetLocaleInfo(this.sRealName, LOCALE_SPARENT);
876                 }
877                 return this.sParent;
878             }
879         }
880
881         // Localized pretty name for this locale (ie: Inglis (estados Unitos))
882         internal String SLOCALIZEDDISPLAYNAME
883         {
884             get
885             {
886                 if (this.sLocalizedDisplayName == null)
887                 {
888                     // If it hasn't been found (Windows 8 and up), fallback to the system
889                     if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
890                     {
891                         // If its neutral use the language name
892                         if (this.IsNeutralCulture)
893                         {
894                             this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
895                         }
896                         else
897                         {
898                             // We have to make the neutral distinction in case the OS returns a specific name
899                             if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
900                             {
901                                 this.sLocalizedDisplayName = DoGetLocaleInfo(LOCALE_SLOCALIZEDDISPLAYNAME);
902                             }
903                             if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
904                             {
905                                 this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
906                             }
907                         }
908                     }
909                 }
910                 return this.sLocalizedDisplayName;
911             }
912         }
913
914         // English pretty name for this locale (ie: English (United States))
915         internal String SENGDISPLAYNAME
916         {
917             get
918             {
919                 if (this.sEnglishDisplayName == null)
920                 {
921                     // If its neutral use the language name
922                     if (this.IsNeutralCulture)
923                     {
924                         this.sEnglishDisplayName = this.SENGLISHLANGUAGE;
925                     }
926                     else
927                     {
928                         this.sEnglishDisplayName = DoGetLocaleInfo(LOCALE_SENGLISHDISPLAYNAME);
929
930                         // if it isn't found build one:
931                         if (String.IsNullOrEmpty(this.sEnglishDisplayName))
932                         {
933                             // Our existing names mostly look like:
934                             // "English" + "United States" -> "English (United States)"
935                             // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
936                             if (this.SENGLISHLANGUAGE.EndsWith(')'))
937                             {
938                                 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
939                                 this.sEnglishDisplayName =
940                                     this.SENGLISHLANGUAGE.Substring(0, this.sEnglishLanguage.Length - 1) +
941                                     ", " + this.SENGCOUNTRY + ")";
942                             }
943                             else
944                             {
945                                 // "English" + "United States" -> "English (United States)"
946                                 this.sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
947                             }
948                         }
949                     }
950                 }
951                 return this.sEnglishDisplayName;
952             }
953         }
954
955         // Native pretty name for this locale (ie: Deutsch (Deutschland))
956         internal String SNATIVEDISPLAYNAME
957         {
958             get
959             {
960                 if (this.sNativeDisplayName == null)
961                 {
962                     // If its neutral use the language name
963                     if (this.IsNeutralCulture)
964                     {
965                         this.sNativeDisplayName = this.SNATIVELANGUAGE;
966                     }
967                     else
968                     {
969                         this.sNativeDisplayName = DoGetLocaleInfo(LOCALE_SNATIVEDISPLAYNAME);
970
971                         // if it isn't found build one:
972                         if (String.IsNullOrEmpty(this.sNativeDisplayName))
973                         {
974                             // These should primarily be "Deutsch (Deutschland)" type names
975                             this.sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
976                         }
977                     }
978                 }
979                 return this.sNativeDisplayName;
980             }
981         }
982
983         // The culture name to be used in CultureInfo.CreateSpecificCulture()
984         internal String SSPECIFICCULTURE
985         {
986             get
987             {
988                 // This got populated when ComNlsInfo::nativeInitCultureData told us we had a culture
989                 Debug.Assert(this.sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by COMNlsInfo::nativeInitCultureData already");
990                 return this.sSpecificCulture;
991             }
992         }
993
994         /////////////
995         // Language //
996         /////////////
997
998         // iso 639 language name, ie: en
999         internal String SISO639LANGNAME
1000         {
1001             get
1002             {
1003                 if (this.sISO639Language == null)
1004                 {
1005                     this.sISO639Language = DoGetLocaleInfo(LOCALE_SISO639LANGNAME);
1006                 }
1007                 return this.sISO639Language;
1008             }
1009         }
1010
1011         // iso 639 language name, ie: eng
1012         internal String SISO639LANGNAME2
1013         {
1014             get
1015             {
1016                 if (this.sISO639Language2 == null)
1017                 {
1018                     this.sISO639Language2 = DoGetLocaleInfo(LOCALE_SISO639LANGNAME2);
1019                 }
1020                 return this.sISO639Language2;
1021             }
1022         }
1023
1024         // abbreviated windows language name (ie: enu) (non-standard, avoid this)
1025         internal String SABBREVLANGNAME
1026         {
1027             get
1028             {
1029                 if (this.sAbbrevLang == null)
1030                 {
1031                     this.sAbbrevLang = DoGetLocaleInfo(LOCALE_SABBREVLANGNAME);
1032                 }
1033                 return this.sAbbrevLang;
1034             }
1035         }
1036
1037         // Localized name for this language (Windows Only) ie: Inglis
1038         // This is only valid for Windows 8 and higher neutrals:
1039         internal String SLOCALIZEDLANGUAGE
1040         {
1041             get
1042             {
1043                 if (this.sLocalizedLanguage == null)
1044                 {
1045                     if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1046                     {
1047                         this.sLocalizedLanguage = DoGetLocaleInfo(LOCALE_SLOCALIZEDLANGUAGENAME);
1048                     }
1049                     // Some OS's might not have this resource or LCTYPE
1050                     if (String.IsNullOrEmpty(this.sLocalizedLanguage))
1051                     {
1052                         this.sLocalizedLanguage = SNATIVELANGUAGE;
1053                     }
1054                 }
1055
1056                 return this.sLocalizedLanguage;
1057             }
1058         }
1059
1060         // English name for this language (Windows Only) ie: German
1061         internal String SENGLISHLANGUAGE
1062         {
1063             get
1064             {
1065                 if (this.sEnglishLanguage == null)
1066                 {
1067                     this.sEnglishLanguage = DoGetLocaleInfo(LOCALE_SENGLISHLANGUAGENAME);
1068                 }
1069                 return this.sEnglishLanguage;
1070             }
1071         }
1072
1073         // Native name of this language (Windows Only) ie: Deutsch
1074         internal String SNATIVELANGUAGE
1075         {
1076             get
1077             {
1078                 if (this.sNativeLanguage == null)
1079                 {
1080                     {
1081                         this.sNativeLanguage = DoGetLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
1082                     }
1083                 }
1084                 return this.sNativeLanguage;
1085             }
1086         }
1087
1088         ///////////
1089         // Region //
1090         ///////////
1091
1092         // region name (eg US)
1093         internal String SREGIONNAME
1094         {
1095             get
1096             {
1097                 if (this.sRegionName == null)
1098                 {
1099                     this.sRegionName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1100                 }
1101                 return this.sRegionName;
1102             }
1103         }
1104
1105         // (user can override) country code (RegionInfo)
1106         internal int ICOUNTRY
1107         {
1108             get
1109             {
1110                 return DoGetLocaleInfoInt(LOCALE_ICOUNTRY);
1111             }
1112         }
1113
1114         // GeoId
1115         internal int IGEOID
1116         {
1117             get
1118             {
1119                 if (this.iGeoId == undef)
1120                 {
1121                     this.iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID);
1122                 }
1123                 return this.iGeoId;
1124             }
1125         }
1126
1127         // localized name for the country
1128         internal string SLOCALIZEDCOUNTRY
1129         {
1130             get
1131             {
1132                 if (this.sLocalizedCountry == null)
1133                 {
1134                     // If it hasn't been found (Windows 8 and up), fallback to the system
1135                     if (String.IsNullOrEmpty(this.sLocalizedCountry))
1136                     {
1137                         // We have to make the neutral distinction in case the OS returns a specific name
1138                         if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1139                         {
1140                             this.sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
1141                         }
1142                         if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1143                         {
1144                             this.sLocalizedCountry = SNATIVECOUNTRY;
1145                         }
1146                     }
1147                 }
1148                 return this.sLocalizedCountry;
1149             }
1150         }
1151
1152         // english country name (RegionInfo) ie: Germany
1153         internal String SENGCOUNTRY
1154         {
1155             get
1156             {
1157                 if (this.sEnglishCountry == null)
1158                 {
1159                     this.sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME);
1160                 }
1161                 return this.sEnglishCountry;
1162             }
1163         }
1164
1165         // native country name (RegionInfo) ie: Deutschland
1166         internal String SNATIVECOUNTRY
1167         {
1168             get
1169             {
1170                 if (this.sNativeCountry == null)
1171                 {
1172                     this.sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
1173                 }
1174                 return this.sNativeCountry;
1175             }
1176         }
1177
1178         // ISO 3166 Country Name
1179         internal String SISO3166CTRYNAME
1180         {
1181             get
1182             {
1183                 if (this.sISO3166CountryName == null)
1184                 {
1185                     this.sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1186                 }
1187                 return this.sISO3166CountryName;
1188             }
1189         }
1190
1191         // ISO 3166 Country Name
1192         internal String SISO3166CTRYNAME2
1193         {
1194             get
1195             {
1196                 if (this.sISO3166CountryName2 == null)
1197                 {
1198                     this.sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2);
1199                 }
1200                 return this.sISO3166CountryName2;
1201             }
1202         }
1203
1204         // abbreviated Country Name (windows version, non-standard, avoid)
1205         internal String SABBREVCTRYNAME
1206         {
1207             get
1208             {
1209                 if (this.sAbbrevCountry == null)
1210                 {
1211                     this.sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME);
1212                 }
1213                 return this.sAbbrevCountry;
1214             }
1215         }
1216
1217         // Default Country
1218         private int IDEFAULTCOUNTRY
1219         {
1220             get
1221             {
1222                 return DoGetLocaleInfoInt(LOCALE_IDEFAULTCOUNTRY);
1223             }
1224         }
1225
1226         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1227         internal int IINPUTLANGUAGEHANDLE
1228         {
1229             get
1230             {
1231                 if (this.iInputLanguageHandle == undef)
1232                 {
1233                     if (IsSupplementalCustomCulture)
1234                     {
1235                         this.iInputLanguageHandle = 0x0409;
1236                     }
1237                     else
1238                     {
1239                         // Input Language is same as LCID for built-in cultures
1240                         this.iInputLanguageHandle = this.ILANGUAGE;
1241                     }
1242                 }
1243                 return this.iInputLanguageHandle;
1244             }
1245         }
1246
1247         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1248         internal String SCONSOLEFALLBACKNAME
1249         {
1250             get
1251             {
1252                 if (this.sConsoleFallbackName == null)
1253                 {
1254                     string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME);
1255                     if (consoleFallbackName == "es-ES_tradnl")
1256                     {
1257                         consoleFallbackName = "es-ES";
1258                     }
1259                     this.sConsoleFallbackName = consoleFallbackName;
1260                 }
1261                 return this.sConsoleFallbackName;
1262             }
1263         }
1264
1265         /////////////
1266         // Numbers //
1267         ////////////
1268
1269         //                internal String sPositiveSign            ; // (user can override) positive sign
1270         //                internal String sNegativeSign            ; // (user can override) negative sign
1271         //                internal String[] saNativeDigits         ; // (user can override) native characters for digits 0-9
1272         //                internal int iDigitSubstitution       ; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
1273         //                internal int iDigits                  ; // (user can override) number of fractional digits
1274         //                internal int iNegativeNumber          ; // (user can override) negative number format
1275
1276         // Leading zeroes
1277         private bool ILEADINGZEROS
1278         {
1279             get
1280             {
1281                 return (DoGetLocaleInfoInt(LOCALE_ILZERO) == 1);
1282             }
1283         }
1284
1285
1286         // (user can override) grouping of digits
1287         internal int[] WAGROUPING
1288         {
1289             get
1290             {
1291                 if (this.waGrouping == null || UseUserOverride)
1292                 {
1293                     this.waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING));
1294                 }
1295                 return this.waGrouping;
1296             }
1297         }
1298
1299
1300         //                internal String sDecimalSeparator        ; // (user can override) decimal separator
1301         //                internal String sThousandSeparator       ; // (user can override) thousands separator
1302
1303         // Not a Number
1304         internal String SNAN
1305         {
1306             get
1307             {
1308                 if (this.sNaN == null)
1309                 {
1310                     this.sNaN = DoGetLocaleInfo(LOCALE_SNAN);
1311                 }
1312                 return this.sNaN;
1313             }
1314         }
1315
1316         // + Infinity
1317         internal String SPOSINFINITY
1318         {
1319             get
1320             {
1321                 if (this.sPositiveInfinity == null)
1322                 {
1323                     this.sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY);
1324                 }
1325                 return this.sPositiveInfinity;
1326             }
1327         }
1328
1329         // - Infinity
1330         internal String SNEGINFINITY
1331         {
1332             get
1333             {
1334                 if (this.sNegativeInfinity == null)
1335                 {
1336                     this.sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY);
1337                 }
1338                 return this.sNegativeInfinity;
1339             }
1340         }
1341
1342
1343         ////////////
1344         // Percent //
1345         ///////////
1346
1347         // Negative Percent (0-3)
1348         internal int INEGATIVEPERCENT
1349         {
1350             get
1351             {
1352                 if (this.iNegativePercent == undef)
1353                 {
1354                     // Note that <= Windows Vista this is synthesized by native code
1355                     this.iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT);
1356                 }
1357                 return this.iNegativePercent;
1358             }
1359         }
1360
1361         // Positive Percent (0-11)
1362         internal int IPOSITIVEPERCENT
1363         {
1364             get
1365             {
1366                 if (this.iPositivePercent == undef)
1367                 {
1368                     // Note that <= Windows Vista this is synthesized by native code
1369                     this.iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT);
1370                 }
1371                 return this.iPositivePercent;
1372             }
1373         }
1374
1375         // Percent (%) symbol
1376         internal String SPERCENT
1377         {
1378             get
1379             {
1380                 if (this.sPercent == null)
1381                 {
1382                     // Note that <= Windows Vista this is synthesized by native code
1383                     this.sPercent = DoGetLocaleInfo(LOCALE_SPERCENT);
1384                 }
1385                 return this.sPercent;
1386             }
1387         }
1388
1389         // PerMille (‰) symbol
1390         internal String SPERMILLE
1391         {
1392             get
1393             {
1394                 if (this.sPerMille == null)
1395                 {
1396                     // Note that <= Windows Vista this is synthesized by native code
1397                     this.sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE);
1398                 }
1399                 return this.sPerMille;
1400             }
1401         }
1402
1403         /////////////
1404         // Currency //
1405         /////////////
1406
1407         // (user can override) local monetary symbol, eg: $
1408         internal String SCURRENCY
1409         {
1410             get
1411             {
1412                 if (this.sCurrency == null || UseUserOverride)
1413                 {
1414                     this.sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY);
1415                 }
1416                 return this.sCurrency;
1417             }
1418         }
1419
1420         // international monetary symbol (RegionInfo), eg: USD
1421         internal String SINTLSYMBOL
1422         {
1423             get
1424             {
1425                 if (this.sIntlMonetarySymbol == null)
1426                 {
1427                     this.sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL);
1428                 }
1429                 return this.sIntlMonetarySymbol;
1430             }
1431         }
1432
1433         // English name for this currency (RegionInfo), eg: US Dollar
1434         internal String SENGLISHCURRENCY
1435         {
1436             get
1437             {
1438                 if (this.sEnglishCurrency == null)
1439                 {
1440                     this.sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME);
1441                 }
1442                 return this.sEnglishCurrency;
1443             }
1444         }
1445
1446         // Native name for this currency (RegionInfo), eg: Schweiz Frank
1447         internal String SNATIVECURRENCY
1448         {
1449             get
1450             {
1451                 if (this.sNativeCurrency == null)
1452                 {
1453                     this.sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME);
1454                 }
1455                 return this.sNativeCurrency;
1456             }
1457         }
1458
1459         //                internal int iCurrencyDigits          ; // (user can override) # local monetary fractional digits
1460         //                internal int iCurrency                ; // (user can override) positive currency format
1461         //                internal int iNegativeCurrency        ; // (user can override) negative currency format
1462
1463         // (user can override) monetary grouping of digits
1464         internal int[] WAMONGROUPING
1465         {
1466             get
1467             {
1468                 if (this.waMonetaryGrouping == null || UseUserOverride)
1469                 {
1470                     this.waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING));
1471                 }
1472                 return this.waMonetaryGrouping;
1473             }
1474         }
1475
1476         //                internal String sMonetaryDecimal         ; // (user can override) monetary decimal separator
1477         //                internal String sMonetaryThousand        ; // (user can override) monetary thousands separator
1478
1479         /////////
1480         // Misc //
1481         /////////
1482
1483         // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1484         internal int IMEASURE
1485         {
1486             get
1487             {
1488                 if (this.iMeasure == undef || UseUserOverride)
1489                 {
1490                     this.iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE);
1491                 }
1492                 return this.iMeasure;
1493             }
1494         }
1495
1496         // (user can override) list Separator
1497         internal String SLIST
1498         {
1499             get
1500             {
1501                 if (this.sListSeparator == null || UseUserOverride)
1502                 {
1503                     this.sListSeparator = DoGetLocaleInfo(LOCALE_SLIST);
1504                 }
1505                 return this.sListSeparator;
1506             }
1507         }
1508
1509         // Paper size
1510         private int IPAPERSIZE
1511         {
1512             get
1513             {
1514                 return DoGetLocaleInfoInt(LOCALE_IPAPERSIZE);
1515             }
1516         }
1517
1518         ////////////////////////////
1519         // Calendar/Time (Gregorian) //
1520         ////////////////////////////
1521
1522         // (user can override) AM designator
1523         internal String SAM1159
1524         {
1525             get
1526             {
1527                 if (this.sAM1159 == null || UseUserOverride)
1528                 {
1529                     this.sAM1159 = DoGetLocaleInfo(LOCALE_S1159);
1530                 }
1531                 return this.sAM1159;
1532             }
1533         }
1534
1535         // (user can override) PM designator
1536         internal String SPM2359
1537         {
1538             get
1539             {
1540                 if (this.sPM2359 == null || UseUserOverride)
1541                 {
1542                     this.sPM2359 = DoGetLocaleInfo(LOCALE_S2359);
1543                 }
1544                 return this.sPM2359;
1545             }
1546         }
1547
1548         // (user can override) time format
1549         internal String[] LongTimes
1550         {
1551             get
1552             {
1553                 if (this.saLongTimes == null || UseUserOverride)
1554                 {
1555                     String[] longTimes = DoEnumTimeFormats();
1556                     if (longTimes == null || longTimes.Length == 0)
1557                     {
1558                         this.saLongTimes = Invariant.saLongTimes;
1559                     }
1560                     else
1561                     {
1562                         this.saLongTimes = longTimes;
1563                     }
1564                 }
1565                 return this.saLongTimes;
1566             }
1567         }
1568
1569         // short time format
1570         // Short times (derived from long times format)
1571         internal String[] ShortTimes
1572         {
1573             get
1574             {
1575                 if (this.saShortTimes == null || UseUserOverride)
1576                 {
1577                     // Try to get the short times from the OS/culture.dll
1578                     String[] shortTimes = DoEnumShortTimeFormats();
1579
1580                     if (shortTimes == null || shortTimes.Length == 0)
1581                     {
1582                         //
1583                         // If we couldn't find short times, then compute them from long times
1584                         // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
1585                         //
1586                         shortTimes = DeriveShortTimesFromLong();
1587                     }
1588
1589                     // Found short times, use them
1590                     this.saShortTimes = shortTimes;
1591                 }
1592                 return this.saShortTimes;
1593             }
1594         }
1595
1596         private string[] DeriveShortTimesFromLong()
1597         {
1598             // Our logic is to look for h,H,m,s,t.  If we find an s, then we check the string
1599             // between it and the previous marker, if any.  If its a short, unescaped separator,
1600             // then we don't retain that part.
1601             // We then check after the ss and remove anything before the next h,H,m,t...
1602             string[] shortTimes = new string[LongTimes.Length];
1603
1604             for (int i = 0; i < LongTimes.Length; i++)
1605             {
1606                 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
1607             }
1608             return shortTimes;
1609         }
1610
1611         private static string StripSecondsFromPattern(string time)
1612         {
1613             bool bEscape = false;
1614             int iLastToken = -1;
1615
1616             // Find the seconds
1617             for (int j = 0; j < time.Length; j++)
1618             {
1619                 // Change escape mode?
1620                 if (time[j] == '\'')
1621                 {
1622                     // Continue
1623                     bEscape = !bEscape;
1624                     continue;
1625                 }
1626
1627                 // See if there was a single \
1628                 if (time[j] == '\\')
1629                 {
1630                     // Skip next char
1631                     j++;
1632                     continue;
1633                 }
1634
1635                 if (bEscape)
1636                 {
1637                     continue;
1638                 }
1639
1640                 switch (time[j])
1641                 {
1642                     // Check for seconds
1643                     case 's':
1644                         // Found seconds, see if there was something unescaped and short between
1645                         // the last marker and the seconds.  Windows says separator can be a
1646                         // maximum of three characters (without null)
1647                         // If 1st or last characters were ', then ignore it
1648                         if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
1649                             (time[iLastToken + 1] != '\'') &&
1650                             (time[j - 1] != '\''))
1651                         {
1652                             // There was something there we want to remember
1653                             if (iLastToken >= 0)
1654                             {
1655                                 j = iLastToken + 1;
1656                             }
1657                         }
1658
1659                         bool containsSpace;
1660                         int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
1661                         StringBuilder sb = new StringBuilder(time.Substring(0, j));
1662                         if (containsSpace)
1663                         {
1664                             sb.Append(' ');
1665                         }
1666                         sb.Append(time.Substring(endIndex));
1667                         time = sb.ToString();
1668                         break;
1669                     case 'm':
1670                     case 'H':
1671                     case 'h':
1672                         iLastToken = j;
1673                         break;
1674                 }
1675             }
1676             return time;
1677         }
1678
1679         private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
1680         {
1681             bool bEscape = false;
1682             containsSpace = false;
1683             for (; index < time.Length; index++)
1684             {
1685                 switch (time[index])
1686                 {
1687                     case '\'':
1688                         bEscape = !bEscape;
1689                         continue;
1690                     case '\\':
1691                         index++;
1692                         if (time[index] == ' ')
1693                         {
1694                             containsSpace = true;
1695                         }
1696                         continue;
1697                     case ' ':
1698                         containsSpace = true;
1699                         break;
1700                     case 't':
1701                     case 'm':
1702                     case 'H':
1703                     case 'h':
1704                         if (bEscape)
1705                         {
1706                             continue;
1707                         }
1708                         return index;
1709                 }
1710             }
1711             containsSpace = false;
1712             return index;
1713         }
1714
1715         // time duration format
1716         internal String[] SADURATION
1717         {
1718             get
1719             {
1720                 if (this.saDurationFormats == null)
1721                 {
1722                     String durationFormat = DoGetLocaleInfo(LOCALE_SDURATION);
1723                     this.saDurationFormats = new String[] { ReescapeWin32String(durationFormat) };
1724                 }
1725                 return this.saDurationFormats;
1726             }
1727         }
1728
1729         // (user can override) first day of week
1730         internal int IFIRSTDAYOFWEEK
1731         {
1732             get
1733             {
1734                 if (this.iFirstDayOfWeek == undef || UseUserOverride)
1735                 {
1736                     // Have to convert it from windows to .Net formats
1737                     this.iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK));
1738                 }
1739                 return this.iFirstDayOfWeek;
1740             }
1741         }
1742
1743         // (user can override) first week of year
1744         internal int IFIRSTWEEKOFYEAR
1745         {
1746             get
1747             {
1748                 if (this.iFirstWeekOfYear == undef || UseUserOverride)
1749                 {
1750                     this.iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR);
1751                 }
1752                 return this.iFirstWeekOfYear;
1753             }
1754         }
1755
1756         // (user can override default only) short date format
1757         internal String[] ShortDates(int calendarId)
1758         {
1759             return GetCalendar(calendarId).saShortDates;
1760         }
1761
1762         // (user can override default only) long date format
1763         internal String[] LongDates(int calendarId)
1764         {
1765             return GetCalendar(calendarId).saLongDates;
1766         }
1767
1768         // (user can override) date year/month format.
1769         internal String[] YearMonths(int calendarId)
1770         {
1771             return GetCalendar(calendarId).saYearMonths;
1772         }
1773
1774         // day names
1775         internal string[] DayNames(int calendarId)
1776         {
1777             return GetCalendar(calendarId).saDayNames;
1778         }
1779
1780         // abbreviated day names
1781         internal string[] AbbreviatedDayNames(int calendarId)
1782         {
1783             // Get abbreviated day names for this calendar from the OS if necessary
1784             return GetCalendar(calendarId).saAbbrevDayNames;
1785         }
1786
1787         // The super short day names
1788         internal string[] SuperShortDayNames(int calendarId)
1789         {
1790             return GetCalendar(calendarId).saSuperShortDayNames;
1791         }
1792
1793         // month names
1794         internal string[] MonthNames(int calendarId)
1795         {
1796             return GetCalendar(calendarId).saMonthNames;
1797         }
1798
1799         // Genitive month names
1800         internal string[] GenitiveMonthNames(int calendarId)
1801         {
1802             return GetCalendar(calendarId).saMonthGenitiveNames;
1803         }
1804
1805         // month names
1806         internal string[] AbbreviatedMonthNames(int calendarId)
1807         {
1808             return GetCalendar(calendarId).saAbbrevMonthNames;
1809         }
1810
1811         // Genitive month names
1812         internal string[] AbbreviatedGenitiveMonthNames(int calendarId)
1813         {
1814             return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
1815         }
1816
1817         // Leap year month names
1818         // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
1819         // the non-leap names skip the 7th name in the normal month name array
1820         internal string[] LeapYearMonthNames(int calendarId)
1821         {
1822             return GetCalendar(calendarId).saLeapYearMonthNames;
1823         }
1824
1825         // month/day format (single string, no override)
1826         internal String MonthDay(int calendarId)
1827         {
1828             return GetCalendar(calendarId).sMonthDay;
1829         }
1830
1831
1832
1833         /////////////
1834         // Calendars //
1835         /////////////
1836
1837         // all available calendar type(s), The first one is the default calendar.
1838         internal int[] CalendarIds
1839         {
1840             get
1841             {
1842                 if (this.waCalendars == null)
1843                 {
1844                     // We pass in an array of ints, and native side fills it up with count calendars.
1845                     // We then have to copy that list to a new array of the right size.
1846                     // Default calendar should be first
1847                     int[] calendarInts = new int[23];
1848                     Debug.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1849                     int count = CalendarData.nativeGetCalendars(this.sWindowsName, this.bUseOverrides, calendarInts);
1850
1851                     // See if we had a calendar to add.
1852                     if (count == 0)
1853                     {
1854                         // Failed for some reason, just grab Gregorian from Invariant
1855                         this.waCalendars = Invariant.waCalendars;
1856                     }
1857                     else
1858                     {
1859                         // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
1860                         if (this.sWindowsName == "zh-TW")
1861                         {
1862                             bool found = false;
1863
1864                             // Do we need to insert calendar 4?
1865                             for (int i = 0; i < count; i++)
1866                             {
1867                                 // Stop if we found calendar four
1868                                 if (calendarInts[i] == Calendar.CAL_TAIWAN)
1869                                 {
1870                                     found = true;
1871                                     break;
1872                                 }
1873                             }
1874
1875                             // If not found then insert it
1876                             if (!found)
1877                             {
1878                                 // Insert it as the 2nd calendar
1879                                 count++;
1880                                 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
1881                                 Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1);
1882                                 calendarInts[1] = Calendar.CAL_TAIWAN;
1883                             }
1884                         }
1885
1886                         // It worked, remember the list
1887                         int[] temp = new int[count];
1888                         Array.Copy(calendarInts, temp, count);
1889
1890                         // Want 1st calendar to be default
1891                         // Prior to Vista the enumeration didn't have default calendar first
1892                         // Only a coreclr concern, culture.dll does the right thing.
1893                         if (temp.Length > 1)
1894                         {
1895                             int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
1896                             if (temp[1] == i)
1897                             {
1898                                 temp[1] = temp[0];
1899                                 temp[0] = i;
1900                             }
1901                         }
1902
1903                         this.waCalendars = temp;
1904                     }
1905                 }
1906
1907                 return this.waCalendars;
1908             }
1909         }
1910
1911         // Native calendar names.  index of optional calendar - 1, empty if no optional calendar at that number
1912         internal String CalendarName(int calendarId)
1913         {
1914             // Get the calendar
1915             return GetCalendar(calendarId).sNativeName;
1916         }
1917
1918         internal CalendarData GetCalendar(int calendarId)
1919         {
1920             Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
1921                 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
1922
1923             // arrays are 0 based, calendarIds are 1 based
1924             int calendarIndex = calendarId - 1;
1925
1926             // Have to have calendars
1927             if (calendars == null)
1928             {
1929                 calendars = new CalendarData[CalendarData.MAX_CALENDARS];
1930             }
1931
1932             // we need the following local variable to avoid returning null
1933             // when another thread creates a new array of CalendarData (above)
1934             // right after we insert the newly created CalendarData (below)
1935             CalendarData calendarData = calendars[calendarIndex];
1936             // Make sure that calendar has data
1937             if (calendarData == null || UseUserOverride)
1938             {
1939                 Debug.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1940                 calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
1941                 calendars[calendarIndex] = calendarData;
1942             }
1943
1944             return calendarData;
1945         }
1946
1947         internal int CurrentEra(int calendarId)
1948         {
1949             return GetCalendar(calendarId).iCurrentEra;
1950         }
1951
1952         ///////////////////
1953         // Text Information //
1954         ///////////////////
1955
1956         // IsRightToLeft
1957         internal bool IsRightToLeft
1958         {
1959             get
1960             {
1961                 // Returns one of the following 4 reading layout values:
1962                 // 0 - Left to right (eg en-US)
1963                 // 1 - Right to left (eg arabic locales)
1964                 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1965                 // 3 - Vertical top to bottom with columns proceeding to the right
1966                 return (this.IREADINGLAYOUT == 1);
1967             }
1968         }
1969
1970         // IREADINGLAYOUT
1971         // Returns one of the following 4 reading layout values:
1972         // 0 - Left to right (eg en-US)
1973         // 1 - Right to left (eg arabic locales)
1974         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1975         // 3 - Vertical top to bottom with columns proceeding to the right
1976         //
1977         // If exposed as a public API, we'd have an enum with those 4 values
1978         private int IREADINGLAYOUT
1979         {
1980             get
1981             {
1982                 if (this.iReadingLayout == undef)
1983                 {
1984                     Debug.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
1985                     this.iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT);
1986                 }
1987
1988                 return (this.iReadingLayout);
1989             }
1990         }
1991
1992         // The TextInfo name never includes that alternate sort and is always specific
1993         // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
1994         // en -> en-US
1995         // en-US -> en-US
1996         // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
1997         // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
1998         // es-ES_tradnl -> es-ES
1999         internal String STEXTINFO               // Text info name to use for text information
2000         {
2001             get
2002             {
2003                 if (this.sTextInfo == null)
2004                 {
2005                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2006                     // It is also not supported downlevel without culture.dll.
2007                     if (IsNeutralCulture || IsSupplementalCustomCulture)
2008                     {
2009                         string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2010                         this.sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME;
2011                     }
2012
2013                     if (this.sTextInfo == null)
2014                     {
2015                         this.sTextInfo = this.SNAME; // removes alternate sort
2016                     }
2017                 }
2018
2019                 return this.sTextInfo;
2020             }
2021         }
2022
2023         // Compare info name (including sorting key) to use if custom
2024         internal String SCOMPAREINFO
2025         {
2026             get
2027             {
2028                 if (this.sCompareInfo == null)
2029                 {
2030                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2031                     // It is also not supported downlevel without culture.dll.
2032                     // We really only need it for the custom locale case though
2033                     // since for all other cases, it is the same as sWindowsName
2034                     if (IsSupplementalCustomCulture)
2035                     {
2036                         this.sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2037                     }
2038
2039                     if (this.sCompareInfo == null)
2040                     {
2041                         this.sCompareInfo = this.sWindowsName;
2042                     }
2043                 }
2044
2045                 return this.sCompareInfo;
2046             }
2047         }
2048
2049         internal bool IsSupplementalCustomCulture
2050         {
2051             get
2052             {
2053                 return IsCustomCultureId(this.ILANGUAGE);
2054             }
2055         }
2056
2057         // Typical Scripts for this locale (latn;cyrl; etc)
2058
2059         private String SSCRIPTS
2060         {
2061             get
2062             {
2063                 if (this.sScripts == null)
2064                 {
2065                     this.sScripts = DoGetLocaleInfo(LOCALE_SSCRIPTS);
2066                 }
2067                 return this.sScripts;
2068             }
2069         }
2070
2071         private String SOPENTYPELANGUAGETAG
2072         {
2073             get
2074             {
2075                 return DoGetLocaleInfo(LOCALE_SOPENTYPELANGUAGETAG);
2076             }
2077         }
2078
2079         private String FONTSIGNATURE
2080         {
2081             get
2082             {
2083                 if (this.fontSignature == null)
2084                 {
2085                     this.fontSignature = DoGetLocaleInfo(LOCALE_FONTSIGNATURE);
2086                 }
2087                 return this.fontSignature;
2088             }
2089         }
2090
2091         private String SKEYBOARDSTOINSTALL
2092         {
2093             get
2094             {
2095                 return DoGetLocaleInfo(LOCALE_SKEYBOARDSTOINSTALL);
2096             }
2097         }
2098
2099
2100         internal int IDEFAULTANSICODEPAGE   // default ansi code page ID (ACP)
2101         {
2102             get
2103             {
2104                 if (this.iDefaultAnsiCodePage == undef)
2105                 {
2106                     this.iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE);
2107                 }
2108                 return this.iDefaultAnsiCodePage;
2109             }
2110         }
2111
2112         internal int IDEFAULTOEMCODEPAGE   // default oem code page ID (OCP or OEM)
2113         {
2114             get
2115             {
2116                 if (this.iDefaultOemCodePage == undef)
2117                 {
2118                     this.iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE);
2119                 }
2120                 return this.iDefaultOemCodePage;
2121             }
2122         }
2123
2124         internal int IDEFAULTMACCODEPAGE   // default macintosh code page
2125         {
2126             get
2127             {
2128                 if (this.iDefaultMacCodePage == undef)
2129                 {
2130                     this.iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE);
2131                 }
2132                 return this.iDefaultMacCodePage;
2133             }
2134         }
2135
2136         internal int IDEFAULTEBCDICCODEPAGE   // default EBCDIC code page
2137         {
2138             get
2139             {
2140                 if (this.iDefaultEbcdicCodePage == undef)
2141                 {
2142                     this.iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE);
2143                 }
2144                 return this.iDefaultEbcdicCodePage;
2145             }
2146         }
2147
2148         // Obtain locale name from LCID
2149         // NOTE: This will get neutral names, unlike the OS API
2150         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2151         internal static extern int LocaleNameToLCID(String localeName);
2152
2153         // These are desktop only, not coreclr
2154         // locale ID (0409), including sort information
2155         internal int ILANGUAGE
2156         {
2157             get
2158             {
2159                 if (this.iLanguage == 0)
2160                 {
2161                     Debug.Assert(this.sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2162                     this.iLanguage = LocaleNameToLCID(this.sRealName);
2163                 }
2164                 return this.iLanguage;
2165             }
2166         }
2167
2168         internal bool IsWin32Installed
2169         {
2170             get { return this.bWin32Installed; }
2171         }
2172
2173         internal bool IsFramework
2174         {
2175             get { return this.bFramework; }
2176         }
2177
2178         ////////////////////
2179         // Derived properties //
2180         ////////////////////
2181
2182         internal bool IsNeutralCulture
2183         {
2184             get
2185             {
2186                 // NlsInfo::nativeInitCultureData told us if we're neutral or not
2187                 return this.bNeutral;
2188             }
2189         }
2190
2191         internal bool IsInvariantCulture
2192         {
2193             get
2194             {
2195                 return String.IsNullOrEmpty(this.SNAME);
2196             }
2197         }
2198
2199         // Get an instance of our default calendar
2200         internal Calendar DefaultCalendar
2201         {
2202             get
2203             {
2204                 int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2205                 if (defaultCalId == 0)
2206                 {
2207                     defaultCalId = this.CalendarIds[0];
2208                 }
2209
2210                 return CultureInfo.GetCalendarInstance(defaultCalId);
2211             }
2212         }
2213
2214         // All of our era names
2215         internal String[] EraNames(int calendarId)
2216         {
2217             Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
2218
2219             return this.GetCalendar(calendarId).saEraNames;
2220         }
2221
2222         internal String[] AbbrevEraNames(int calendarId)
2223         {
2224             Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2225
2226             return this.GetCalendar(calendarId).saAbbrevEraNames;
2227         }
2228
2229         internal String[] AbbreviatedEnglishEraNames(int calendarId)
2230         {
2231             Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2232
2233             return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2234         }
2235
2236         // String array DEFAULTS
2237         // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2238
2239
2240         // Time separator (derived from time format)
2241         internal String TimeSeparator
2242         {
2243             get
2244             {
2245                 if (sTimeSeparator == null || UseUserOverride)
2246                 {
2247                     string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT));
2248                     if (String.IsNullOrEmpty(longTimeFormat))
2249                     {
2250                         longTimeFormat = LongTimes[0];
2251                     }
2252
2253                     // Compute STIME from time format
2254                     sTimeSeparator = GetTimeSeparator(longTimeFormat);
2255                 }
2256                 return sTimeSeparator;
2257             }
2258         }
2259
2260         // Date separator (derived from short date format)
2261         internal String DateSeparator(int calendarId)
2262         {
2263             return GetDateSeparator(ShortDates(calendarId)[0]);
2264         }
2265
2266         //////////////////////////////////////
2267         // Helper Functions to get derived properties //
2268         //////////////////////////////////////
2269
2270         ////////////////////////////////////////////////////////////////////////////
2271         //
2272         // Unescape a NLS style quote string
2273         //
2274         // This removes single quotes:
2275         //      'fred' -> fred
2276         //      'fred -> fred
2277         //      fred' -> fred
2278         //      fred's -> freds
2279         //
2280         // This removes the first \ of escaped characters:
2281         //      fred\'s -> fred's
2282         //      a\\b -> a\b
2283         //      a\b -> ab
2284         //
2285         // We don't build the stringbuilder unless we find a ' or a \.  If we find a ' or a \, we
2286         // always build a stringbuilder because we need to remove the ' or \.
2287         //
2288         ////////////////////////////////////////////////////////////////////////////
2289         static private String UnescapeNlsString(String str, int start, int end)
2290         {
2291             Contract.Requires(str != null);
2292             Contract.Requires(start >= 0);
2293             Contract.Requires(end >= 0);
2294             StringBuilder result = null;
2295
2296             for (int i = start; i < str.Length && i <= end; i++)
2297             {
2298                 switch (str[i])
2299                 {
2300                     case '\'':
2301                         if (result == null)
2302                         {
2303                             result = new StringBuilder(str, start, i - start, str.Length);
2304                         }
2305                         break;
2306                     case '\\':
2307                         if (result == null)
2308                         {
2309                             result = new StringBuilder(str, start, i - start, str.Length);
2310                         }
2311                         ++i;
2312                         if (i < str.Length)
2313                         {
2314                             result.Append(str[i]);
2315                         }
2316                         break;
2317                     default:
2318                         if (result != null)
2319                         {
2320                             result.Append(str[i]);
2321                         }
2322                         break;
2323                 }
2324             }
2325
2326             if (result == null)
2327                 return (str.Substring(start, end - start + 1));
2328
2329             return (result.ToString());
2330         }
2331
2332         ////////////////////////////////////////////////////////////////////////////
2333         //
2334         // Reescape a Win32 style quote string as a NLS+ style quoted string
2335         //
2336         // This is also the escaping style used by custom culture data files
2337         //
2338         // NLS+ uses \ to escape the next character, whether in a quoted string or
2339         // not, so we always have to change \ to \\.
2340         //
2341         // NLS+ uses \' to escape a quote inside a quoted string so we have to change
2342         // '' to \' (if inside a quoted string)
2343         //
2344         // We don't build the stringbuilder unless we find something to change
2345         ////////////////////////////////////////////////////////////////////////////
2346         static internal String ReescapeWin32String(String str)
2347         {
2348             // If we don't have data, then don't try anything
2349             if (str == null)
2350                 return null;
2351
2352             StringBuilder result = null;
2353
2354             bool inQuote = false;
2355             for (int i = 0; i < str.Length; i++)
2356             {
2357                 // Look for quote
2358                 if (str[i] == '\'')
2359                 {
2360                     // Already in quote?
2361                     if (inQuote)
2362                     {
2363                         // See another single quote.  Is this '' of 'fred''s' or '''', or is it an ending quote?
2364                         if (i + 1 < str.Length && str[i + 1] == '\'')
2365                         {
2366                             // Found another ', so we have ''.  Need to add \' instead.
2367                             // 1st make sure we have our stringbuilder
2368                             if (result == null)
2369                                 result = new StringBuilder(str, 0, i, str.Length * 2);
2370
2371                             // Append a \' and keep going (so we don't turn off quote mode)
2372                             result.Append("\\'");
2373                             i++;
2374                             continue;
2375                         }
2376
2377                         // Turning off quote mode, fall through to add it
2378                         inQuote = false;
2379                     }
2380                     else
2381                     {
2382                         // Found beginning quote, fall through to add it
2383                         inQuote = true;
2384                     }
2385                 }
2386                 // Is there a single \ character?
2387                 else if (str[i] == '\\')
2388                 {
2389                     // Found a \, need to change it to \\
2390                     // 1st make sure we have our stringbuilder
2391                     if (result == null)
2392                         result = new StringBuilder(str, 0, i, str.Length * 2);
2393
2394                     // Append our \\ to the string & continue
2395                     result.Append("\\\\");
2396                     continue;
2397                 }
2398
2399                 // If we have a builder we need to add our character
2400                 if (result != null)
2401                     result.Append(str[i]);
2402             }
2403
2404             // Unchanged string? , just return input string
2405             if (result == null)
2406                 return str;
2407
2408             // String changed, need to use the builder
2409             return result.ToString();
2410         }
2411
2412         static internal String[] ReescapeWin32Strings(String[] array)
2413         {
2414             if (array != null)
2415             {
2416                 for (int i = 0; i < array.Length; i++)
2417                 {
2418                     array[i] = ReescapeWin32String(array[i]);
2419                 }
2420             }
2421
2422             return array;
2423         }
2424
2425         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2426         // and breaking changes here will not show up at build time, only at run time.
2427         static private String GetTimeSeparator(String format)
2428         {
2429             // Time format separator (ie: : in 12:39:00)
2430             //
2431             // We calculate this from the provided time format
2432             //
2433
2434             //
2435             //  Find the time separator so that we can pretend we know STIME.
2436             //
2437             return GetSeparator(format, "Hhms");
2438         }
2439
2440         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2441         // and breaking changes here will not show up at build time, only at run time.
2442         static private String GetDateSeparator(String format)
2443         {
2444             // Date format separator (ie: / in 9/1/03)
2445             //
2446             // We calculate this from the provided short date
2447             //
2448
2449             //
2450             //  Find the date separator so that we can pretend we know SDATE.
2451             //
2452             return GetSeparator(format, "dyM");
2453         }
2454
2455         private static string GetSeparator(string format, string timeParts)
2456         {
2457             int index = IndexOfTimePart(format, 0, timeParts);
2458
2459             if (index != -1)
2460             {
2461                 // Found a time part, find out when it changes
2462                 char cTimePart = format[index];
2463
2464                 do
2465                 {
2466                     index++;
2467                 } while (index < format.Length && format[index] == cTimePart);
2468
2469                 int separatorStart = index;
2470
2471                 // Now we need to find the end of the separator
2472                 if (separatorStart < format.Length)
2473                 {
2474                     int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
2475                     if (separatorEnd != -1)
2476                     {
2477                         // From [separatorStart, count) is our string, except we need to unescape
2478                         return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
2479                     }
2480                 }
2481             }
2482
2483             return String.Empty;
2484         }
2485
2486         private static int IndexOfTimePart(string format, int startIndex, string timeParts)
2487         {
2488             Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
2489             Debug.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
2490             bool inQuote = false;
2491             for (int i = startIndex; i < format.Length; ++i)
2492             {
2493                 // See if we have a time Part
2494                 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
2495                 {
2496                     return i;
2497                 }
2498                 switch (format[i])
2499                 {
2500                     case '\\':
2501                         if (i + 1 < format.Length)
2502                         {
2503                             ++i;
2504                             switch (format[i])
2505                             {
2506                                 case '\'':
2507                                 case '\\':
2508                                     break;
2509                                 default:
2510                                     --i; //backup since we will move over this next
2511                                     break;
2512                             }
2513                         }
2514                         break;
2515                     case '\'':
2516                         inQuote = !inQuote;
2517                         break;
2518                 }
2519             }
2520
2521             return -1;
2522         }
2523
2524         string DoGetLocaleInfo(uint lctype)
2525         {
2526             Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2527             return DoGetLocaleInfo(this.sWindowsName, lctype);
2528         }
2529
2530         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
2531         // "windows" name, which can be specific for downlevel (< windows 7) os's.
2532         string DoGetLocaleInfo(string localeName, uint lctype)
2533         {
2534             // Fix lctype if we don't want overrides
2535             if (!UseUserOverride)
2536             {
2537                 lctype |= LOCALE_NOUSEROVERRIDE;
2538             }
2539
2540             // Ask OS for data
2541             Debug.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null");
2542             string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
2543             if (result == null)
2544             {
2545                 // Failed, just use empty string
2546                 result = String.Empty;
2547             }
2548
2549             return result;
2550         }
2551
2552         int DoGetLocaleInfoInt(uint lctype)
2553         {
2554             // Fix lctype if we don't want overrides
2555             if (!UseUserOverride)
2556             {
2557                 lctype |= LOCALE_NOUSEROVERRIDE;
2558             }
2559
2560             // Ask OS for data, note that we presume it returns success, so we have to know that
2561             // sWindowsName is valid before calling.
2562             Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2563             int result = CultureInfo.nativeGetLocaleInfoExInt(this.sWindowsName, lctype);
2564
2565             return result;
2566         }
2567
2568         String[] DoEnumTimeFormats()
2569         {
2570             // Note that this gets overrides for us all the time
2571             Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2572             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, 0, UseUserOverride));
2573
2574             return result;
2575         }
2576
2577         String[] DoEnumShortTimeFormats()
2578         {
2579             // Note that this gets overrides for us all the time
2580             Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2581             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, TIME_NOSECONDS, UseUserOverride));
2582
2583             return result;
2584         }
2585
2586         /////////////////
2587         // Static Helpers //
2588         ////////////////
2589         internal static bool IsCustomCultureId(int cultureId)
2590         {
2591             if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
2592                 return true;
2593
2594             return false;
2595         }
2596
2597         ////////////////////////////////////////////////////////////////////////////
2598         //
2599         // Parameters:
2600         //      calendarValueOnly   Retrieve the values which are affected by the calendar change of DTFI.
2601         //                          This will cause values like longTimePattern not be retrieved since it is
2602         //                          not affected by the Calendar property in DTFI.
2603         //
2604         ////////////////////////////////////////////////////////////////////////////
2605         internal void GetNFIValues(NumberFormatInfo nfi)
2606         {
2607             if (this.IsInvariantCulture)
2608             {
2609                 nfi.positiveSign = this.sPositiveSign;
2610                 nfi.negativeSign = this.sNegativeSign;
2611
2612                 nfi.nativeDigits = this.saNativeDigits;
2613                 nfi.digitSubstitution = this.iDigitSubstitution;
2614
2615                 nfi.numberGroupSeparator = this.sThousandSeparator;
2616                 nfi.numberDecimalSeparator = this.sDecimalSeparator;
2617                 nfi.numberDecimalDigits = this.iDigits;
2618                 nfi.numberNegativePattern = this.iNegativeNumber;
2619
2620                 nfi.currencySymbol = this.sCurrency;
2621                 nfi.currencyGroupSeparator = this.sMonetaryThousand;
2622                 nfi.currencyDecimalSeparator = this.sMonetaryDecimal;
2623                 nfi.currencyDecimalDigits = this.iCurrencyDigits;
2624                 nfi.currencyNegativePattern = this.iNegativeCurrency;
2625                 nfi.currencyPositivePattern = this.iCurrency;
2626             }
2627             else
2628             {
2629                 //
2630                 // We don't have information for the following four.  All cultures use
2631                 // the same value of the number formatting values.
2632                 //
2633                 // PercentDecimalDigits
2634                 // PercentDecimalSeparator
2635                 // PercentGroupSize
2636                 // PercentGroupSeparator
2637                 //
2638
2639                 //
2640                 // Ask native side for our data.
2641                 //
2642                 Debug.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2643                 CultureData.nativeGetNumberFormatInfoValues(this.sWindowsName, nfi, UseUserOverride);
2644             }
2645
2646
2647             //
2648             // Gather additional data
2649             //
2650             nfi.numberGroupSizes = this.WAGROUPING;
2651             nfi.currencyGroupSizes = this.WAMONGROUPING;
2652
2653             // prefer the cached value since these do not have user overrides
2654             nfi.percentNegativePattern = this.INEGATIVEPERCENT;
2655             nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
2656             nfi.percentSymbol = this.SPERCENT;
2657             nfi.perMilleSymbol = this.SPERMILLE;
2658
2659             nfi.negativeInfinitySymbol = this.SNEGINFINITY;
2660             nfi.positiveInfinitySymbol = this.SPOSINFINITY;
2661             nfi.nanSymbol = this.SNAN;
2662
2663             //
2664             // We don't have percent values, so use the number values
2665             //
2666             nfi.percentDecimalDigits = nfi.numberDecimalDigits;
2667             nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
2668             nfi.percentGroupSizes = nfi.numberGroupSizes;
2669             nfi.percentGroupSeparator = nfi.numberGroupSeparator;
2670
2671             //
2672             // Clean up a few odd values
2673             //
2674
2675             // Windows usually returns an empty positive sign, but we like it to be "+"
2676             if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
2677
2678             //Special case for Italian.  The currency decimal separator in the control panel is the empty string. When the user
2679             //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
2680             //decimal point doesn't show up.  We'll just workaround this here because our default currency format will never use nfi.
2681             if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
2682             {
2683                 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
2684             }
2685         }
2686
2687         static private int ConvertFirstDayOfWeekMonToSun(int iTemp)
2688         {
2689             // Convert Mon-Sun to Sun-Sat format
2690             iTemp++;
2691             if (iTemp > 6)
2692             {
2693                 // Wrap Sunday and convert invalid data to Sunday
2694                 iTemp = 0;
2695             }
2696             return iTemp;
2697         }
2698
2699         // Helper
2700         // This is ONLY used for caching names and shouldn't be used for anything else
2701         internal static string AnsiToLower(string testString)
2702         {
2703             StringBuilder sb = new StringBuilder(testString.Length);
2704
2705             for (int ich = 0; ich < testString.Length; ich++)
2706             {
2707                 char ch = testString[ich];
2708                 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
2709             }
2710
2711             return (sb.ToString());
2712         }
2713
2714         // If we get a group from windows, then its in 3;0 format with the 0 backwards
2715         // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
2716         // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
2717         static private int[] ConvertWin32GroupString(String win32Str)
2718         {
2719             // None of these cases make any sense
2720             if (win32Str == null || win32Str.Length == 0)
2721             {
2722                 return (new int[] { 3 });
2723             }
2724
2725             if (win32Str[0] == '0')
2726             {
2727                 return (new int[] { 0 });
2728             }
2729
2730             // Since its in n;n;n;n;n format, we can always get the length quickly
2731             int[] values;
2732             if (win32Str[win32Str.Length - 1] == '0')
2733             {
2734                 // Trailing 0 gets dropped. 1;0 -> 1
2735                 values = new int[(win32Str.Length / 2)];
2736             }
2737             else
2738             {
2739                 // Need extra space for trailing zero 1 -> 1;0
2740                 values = new int[(win32Str.Length / 2) + 2];
2741                 values[values.Length - 1] = 0;
2742             }
2743
2744             int i;
2745             int j;
2746             for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
2747             {
2748                 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
2749                 // But we'll test because its registry that could be anything
2750                 if (win32Str[i] < '1' || win32Str[i] > '9')
2751                     return new int[] { 3 };
2752
2753                 values[j] = (int)(win32Str[i] - '0');
2754             }
2755
2756             return (values);
2757         }
2758
2759         // LCTYPES for GetLocaleInfo
2760         private const uint LOCALE_NOUSEROVERRIDE = 0x80000000;   // do not use user overrides
2761         private const uint LOCALE_RETURN_NUMBER = 0x20000000;   // return number instead of string
2762
2763         // Modifier for genitive names
2764         private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000;   //Flag to return the Genitive forms of month names
2765
2766         //
2767         //  The following LCTypes are mutually exclusive in that they may NOT
2768         //  be used in combination with each other.
2769         //
2770
2771         //
2772         // These are the various forms of the name of the locale:
2773         //
2774         private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002;   // localized name of locale, eg "German (Germany)" in UI language
2775         private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072;   // Display name (language + country usually) in English, eg "German (Germany)"
2776         private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073;   // Display name in native locale language, eg "Deutsch (Deutschland)
2777
2778         private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f;   // Language Display Name for a language, eg "German" in UI language
2779         private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001;   // English name of language, eg "German"
2780         private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004;   // native name of language, eg "Deutsch"
2781
2782         private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006;   // localized name of country, eg "Germany" in UI language
2783         private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002;   // English name of country, eg "Germany"
2784         private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008;   // native name of country, eg "Deutschland"
2785
2786
2787         //        private const uint LOCALE_ILANGUAGE              =0x00000001;   // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals)
2788
2789         //        private const uint LOCALE_SLANGUAGE              =LOCALE_SLOCALIZEDDISPLAYNAME;   // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead)
2790         //        private const uint LOCALE_SENGLANGUAGE           =LOCALE_SENGLISHLANGUAGENAME;   // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead)
2791         private const uint LOCALE_SABBREVLANGNAME = 0x00000003;   // abbreviated language name
2792         //        private const uint LOCALE_SNATIVELANGNAME        =LOCALE_SNATIVELANGUAGENAME;   // native name of language (use LOCALE_SNATIVELANGUAGENAME instead)
2793
2794         private const uint LOCALE_ICOUNTRY = 0x00000005;   // country code
2795         //        private const uint LOCALE_SCOUNTRY               =LOCALE_SLOCALIZEDCOUNTRYNAME;   // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead)
2796         //        private const uint LOCALE_SENGCOUNTRY            =LOCALE_SENGLISHCOUNTRYNAME;   // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead)
2797         private const uint LOCALE_SABBREVCTRYNAME = 0x00000007;   // abbreviated country name
2798         //        private const uint LOCALE_SNATIVECTRYNAME        =LOCALE_SNATIVECOUNTRYNAME;   // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead)
2799         private const uint LOCALE_IGEOID = 0x0000005B;   // geographical location id
2800
2801         private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009;   // default language id
2802         private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A;   // default country code
2803         private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B;   // default oem code page
2804         private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004;   // default ansi code page
2805         private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011;   // default mac code page
2806
2807         private const uint LOCALE_SLIST = 0x0000000C;   // list item separator
2808         private const uint LOCALE_IMEASURE = 0x0000000D;   // 0 = metric, 1 = US
2809
2810         private const uint LOCALE_SDECIMAL = 0x0000000E;   // decimal separator
2811         private const uint LOCALE_STHOUSAND = 0x0000000F;   // thousand separator
2812         private const uint LOCALE_SGROUPING = 0x00000010;   // digit grouping
2813         private const uint LOCALE_IDIGITS = 0x00000011;   // number of fractional digits
2814         private const uint LOCALE_ILZERO = 0x00000012;   // leading zeros for decimal
2815         private const uint LOCALE_INEGNUMBER = 0x00001010;   // negative number mode
2816         private const uint LOCALE_SNATIVEDIGITS = 0x00000013;   // native digits for 0-9
2817
2818         private const uint LOCALE_SCURRENCY = 0x00000014;   // local monetary symbol
2819         private const uint LOCALE_SINTLSYMBOL = 0x00000015;   // uintl monetary symbol
2820         private const uint LOCALE_SMONDECIMALSEP = 0x00000016;   // monetary decimal separator
2821         private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017;   // monetary thousand separator
2822         private const uint LOCALE_SMONGROUPING = 0x00000018;   // monetary grouping
2823         private const uint LOCALE_ICURRDIGITS = 0x00000019;   // # local monetary digits
2824         private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A;   // # uintl monetary digits
2825         private const uint LOCALE_ICURRENCY = 0x0000001B;   // positive currency mode
2826         private const uint LOCALE_INEGCURR = 0x0000001C;   // negative currency mode
2827
2828         private const uint LOCALE_SDATE = 0x0000001D;   // date separator (derived from LOCALE_SSHORTDATE, use that instead)
2829         private const uint LOCALE_STIME = 0x0000001E;   // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
2830         private const uint LOCALE_SSHORTDATE = 0x0000001F;   // short date format string
2831         private const uint LOCALE_SLONGDATE = 0x00000020;   // long date format string
2832         private const uint LOCALE_STIMEFORMAT = 0x00001003;   // time format string
2833         private const uint LOCALE_IDATE = 0x00000021;   // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead)
2834         private const uint LOCALE_ILDATE = 0x00000022;   // long date format ordering (derived from LOCALE_SLONGDATE, use that instead)
2835         private const uint LOCALE_ITIME = 0x00000023;   // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead)
2836         private const uint LOCALE_ITIMEMARKPOSN = 0x00001005;   // time marker position (derived from LOCALE_STIMEFORMAT, use that instead)
2837         private const uint LOCALE_ICENTURY = 0x00000024;   // century format specifier (short date, LOCALE_SSHORTDATE is preferred)
2838         private const uint LOCALE_ITLZERO = 0x00000025;   // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead)
2839         private const uint LOCALE_IDAYLZERO = 0x00000026;   // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred)
2840         private const uint LOCALE_IMONLZERO = 0x00000027;   // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred)
2841         private const uint LOCALE_S1159 = 0x00000028;   // AM designator
2842         private const uint LOCALE_S2359 = 0x00000029;   // PM designator
2843
2844         private const uint LOCALE_ICALENDARTYPE = 0x00001009;   // type of calendar specifier
2845         private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B;   // additional calendar types specifier
2846         private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C;   // first day of week specifier
2847         private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D;   // first week of year specifier
2848
2849         private const uint LOCALE_SDAYNAME1 = 0x0000002A;   // long name for Monday
2850         private const uint LOCALE_SDAYNAME2 = 0x0000002B;   // long name for Tuesday
2851         private const uint LOCALE_SDAYNAME3 = 0x0000002C;   // long name for Wednesday
2852         private const uint LOCALE_SDAYNAME4 = 0x0000002D;   // long name for Thursday
2853         private const uint LOCALE_SDAYNAME5 = 0x0000002E;   // long name for Friday
2854         private const uint LOCALE_SDAYNAME6 = 0x0000002F;   // long name for Saturday
2855         private const uint LOCALE_SDAYNAME7 = 0x00000030;   // long name for Sunday
2856         private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031;   // abbreviated name for Monday
2857         private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032;   // abbreviated name for Tuesday
2858         private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033;   // abbreviated name for Wednesday
2859         private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034;   // abbreviated name for Thursday
2860         private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035;   // abbreviated name for Friday
2861         private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036;   // abbreviated name for Saturday
2862         private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037;   // abbreviated name for Sunday
2863         private const uint LOCALE_SMONTHNAME1 = 0x00000038;   // long name for January
2864         private const uint LOCALE_SMONTHNAME2 = 0x00000039;   // long name for February
2865         private const uint LOCALE_SMONTHNAME3 = 0x0000003A;   // long name for March
2866         private const uint LOCALE_SMONTHNAME4 = 0x0000003B;   // long name for April
2867         private const uint LOCALE_SMONTHNAME5 = 0x0000003C;   // long name for May
2868         private const uint LOCALE_SMONTHNAME6 = 0x0000003D;   // long name for June
2869         private const uint LOCALE_SMONTHNAME7 = 0x0000003E;   // long name for July
2870         private const uint LOCALE_SMONTHNAME8 = 0x0000003F;   // long name for August
2871         private const uint LOCALE_SMONTHNAME9 = 0x00000040;   // long name for September
2872         private const uint LOCALE_SMONTHNAME10 = 0x00000041;   // long name for October
2873         private const uint LOCALE_SMONTHNAME11 = 0x00000042;   // long name for November
2874         private const uint LOCALE_SMONTHNAME12 = 0x00000043;   // long name for December
2875         private const uint LOCALE_SMONTHNAME13 = 0x0000100E;   // long name for 13th month (if exists)
2876         private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044;   // abbreviated name for January
2877         private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045;   // abbreviated name for February
2878         private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046;   // abbreviated name for March
2879         private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047;   // abbreviated name for April
2880         private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048;   // abbreviated name for May
2881         private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049;   // abbreviated name for June
2882         private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A;   // abbreviated name for July
2883         private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B;   // abbreviated name for August
2884         private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C;   // abbreviated name for September
2885         private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D;   // abbreviated name for October
2886         private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E;   // abbreviated name for November
2887         private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F;   // abbreviated name for December
2888         private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F;   // abbreviated name for 13th month (if exists)
2889
2890         private const uint LOCALE_SPOSITIVESIGN = 0x00000050;   // positive sign
2891         private const uint LOCALE_SNEGATIVESIGN = 0x00000051;   // negative sign
2892         private const uint LOCALE_IPOSSIGNPOSN = 0x00000052;   // positive sign position (derived from INEGCURR)
2893         private const uint LOCALE_INEGSIGNPOSN = 0x00000053;   // negative sign position (derived from INEGCURR)
2894         private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054;   // mon sym precedes pos amt (derived from ICURRENCY)
2895         private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055;   // mon sym sep by space from pos amt (derived from ICURRENCY)
2896         private const uint LOCALE_INEGSYMPRECEDES = 0x00000056;   // mon sym precedes neg amt (derived from INEGCURR)
2897         private const uint LOCALE_INEGSEPBYSPACE = 0x00000057;   // mon sym sep by space from neg amt (derived from INEGCURR)
2898
2899         private const uint LOCALE_FONTSIGNATURE = 0x00000058;   // font signature
2900         private const uint LOCALE_SISO639LANGNAME = 0x00000059;   // ISO abbreviated language name
2901         private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A;   // ISO abbreviated country name
2902
2903         private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012;   // default ebcdic code page
2904         private const uint LOCALE_IPAPERSIZE = 0x0000100A;   // 1 = letter, 5 = legal, 8 = a3, 9 = a4
2905         private const uint LOCALE_SENGCURRNAME = 0x00001007;   // english name of currency
2906         private const uint LOCALE_SNATIVECURRNAME = 0x00001008;   // native name of currency
2907         private const uint LOCALE_SYEARMONTH = 0x00001006;   // year month format string
2908         private const uint LOCALE_SSORTNAME = 0x00001013;   // sort name
2909         private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014;   // 0 = context, 1 = none, 2 = national
2910
2911         private const uint LOCALE_SNAME = 0x0000005c;   // locale name (with sort info) (ie: de-DE_phoneb)
2912         private const uint LOCALE_SDURATION = 0x0000005d;   // time duration format
2913         private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e;   // (windows only) keyboards to install
2914         private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060;   // Shortest day name for Monday
2915         private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061;   // Shortest day name for Tuesday
2916         private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062;   // Shortest day name for Wednesday
2917         private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063;   // Shortest day name for Thursday
2918         private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064;   // Shortest day name for Friday
2919         private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065;   // Shortest day name for Saturday
2920         private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066;   // Shortest day name for Sunday
2921         private const uint LOCALE_SISO639LANGNAME2 = 0x00000067;   // 3 character ISO abbreviated language name
2922         private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068;   // 3 character ISO country name
2923         private const uint LOCALE_SNAN = 0x00000069;   // Not a Number
2924         private const uint LOCALE_SPOSINFINITY = 0x0000006a;   // + Infinity
2925         private const uint LOCALE_SNEGINFINITY = 0x0000006b;   // - Infinity
2926         private const uint LOCALE_SSCRIPTS = 0x0000006c;   // Typical scripts in the locale
2927         private const uint LOCALE_SPARENT = 0x0000006d;   // Fallback name for resources
2928         private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e;   // Fallback name for within the console
2929         //        private const uint LOCALE_SLANGDISPLAYNAME       =LOCALE_SLOCALIZEDLANGUAGENAME;   // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead)
2930
2931         // Windows 7 LCTYPES
2932         private const uint LOCALE_IREADINGLAYOUT = 0x00000070;   // Returns one of the following 4 reading layout values:
2933         // 0 - Left to right (eg en-US)
2934         // 1 - Right to left (eg arabic locales)
2935         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2936         // 3 - Vertical top to bottom with columns proceeding to the right
2937         private const uint LOCALE_INEUTRAL = 0x00000071;   // Returns 0 for specific cultures, 1 for neutral cultures.
2938         private const uint LOCALE_INEGATIVEPERCENT = 0x00000074;   // Returns 0-11 for the negative percent format
2939         private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075;   // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT
2940         private const uint LOCALE_SPERCENT = 0x00000076;   // Returns the percent symbol
2941         private const uint LOCALE_SPERMILLE = 0x00000077;   // Returns the permille (U+2030) symbol
2942         private const uint LOCALE_SMONTHDAY = 0x00000078;   // Returns the preferred month/day format
2943         private const uint LOCALE_SSHORTTIME = 0x00000079;   // Returns the preferred short time format (ie: no seconds, just h:mm)
2944         private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a;   // Open type language tag, eg: "latn" or "dflt"
2945         private const uint LOCALE_SSORTLOCALE = 0x0000007b;   // Name of locale to use for sorting/collation/casing behavior.
2946
2947         // Time formats enumerations
2948         internal const uint TIME_NOSECONDS = 0x00000002;   // Don't use seconds (get short time format for enumtimeformats on win7+)
2949
2950         // Get our initial minimal culture data (name, parent, etc.)
2951         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2952         internal static extern bool nativeInitCultureData(CultureData cultureData);
2953
2954         // Grab the NumberFormatInfo data
2955         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2956         internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride);
2957
2958         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2959         private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride);
2960
2961         [SuppressUnmanagedCodeSecurityAttribute()]
2962         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
2963         internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray);
2964     }
2965 }