Merge pull request #9338 from jkotas/against-packages
[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         // GeoId
1106         internal int IGEOID
1107         {
1108             get
1109             {
1110                 if (this.iGeoId == undef)
1111                 {
1112                     this.iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID);
1113                 }
1114                 return this.iGeoId;
1115             }
1116         }
1117
1118         // localized name for the country
1119         internal string SLOCALIZEDCOUNTRY
1120         {
1121             get
1122             {
1123                 if (this.sLocalizedCountry == null)
1124                 {
1125                     // If it hasn't been found (Windows 8 and up), fallback to the system
1126                     if (String.IsNullOrEmpty(this.sLocalizedCountry))
1127                     {
1128                         // We have to make the neutral distinction in case the OS returns a specific name
1129                         if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1130                         {
1131                             this.sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
1132                         }
1133                         if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1134                         {
1135                             this.sLocalizedCountry = SNATIVECOUNTRY;
1136                         }
1137                     }
1138                 }
1139                 return this.sLocalizedCountry;
1140             }
1141         }
1142
1143         // english country name (RegionInfo) ie: Germany
1144         internal String SENGCOUNTRY
1145         {
1146             get
1147             {
1148                 if (this.sEnglishCountry == null)
1149                 {
1150                     this.sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME);
1151                 }
1152                 return this.sEnglishCountry;
1153             }
1154         }
1155
1156         // native country name (RegionInfo) ie: Deutschland
1157         internal String SNATIVECOUNTRY
1158         {
1159             get
1160             {
1161                 if (this.sNativeCountry == null)
1162                 {
1163                     this.sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
1164                 }
1165                 return this.sNativeCountry;
1166             }
1167         }
1168
1169         // ISO 3166 Country Name
1170         internal String SISO3166CTRYNAME
1171         {
1172             get
1173             {
1174                 if (this.sISO3166CountryName == null)
1175                 {
1176                     this.sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1177                 }
1178                 return this.sISO3166CountryName;
1179             }
1180         }
1181
1182         // ISO 3166 Country Name
1183         internal String SISO3166CTRYNAME2
1184         {
1185             get
1186             {
1187                 if (this.sISO3166CountryName2 == null)
1188                 {
1189                     this.sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2);
1190                 }
1191                 return this.sISO3166CountryName2;
1192             }
1193         }
1194
1195         // abbreviated Country Name (windows version, non-standard, avoid)
1196         internal String SABBREVCTRYNAME
1197         {
1198             get
1199             {
1200                 if (this.sAbbrevCountry == null)
1201                 {
1202                     this.sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME);
1203                 }
1204                 return this.sAbbrevCountry;
1205             }
1206         }
1207
1208         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1209         internal int IINPUTLANGUAGEHANDLE
1210         {
1211             get
1212             {
1213                 if (this.iInputLanguageHandle == undef)
1214                 {
1215                     if (IsSupplementalCustomCulture)
1216                     {
1217                         this.iInputLanguageHandle = 0x0409;
1218                     }
1219                     else
1220                     {
1221                         // Input Language is same as LCID for built-in cultures
1222                         this.iInputLanguageHandle = this.ILANGUAGE;
1223                     }
1224                 }
1225                 return this.iInputLanguageHandle;
1226             }
1227         }
1228
1229         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1230         internal String SCONSOLEFALLBACKNAME
1231         {
1232             get
1233             {
1234                 if (this.sConsoleFallbackName == null)
1235                 {
1236                     string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME);
1237                     if (consoleFallbackName == "es-ES_tradnl")
1238                     {
1239                         consoleFallbackName = "es-ES";
1240                     }
1241                     this.sConsoleFallbackName = consoleFallbackName;
1242                 }
1243                 return this.sConsoleFallbackName;
1244             }
1245         }
1246
1247
1248         // (user can override) grouping of digits
1249         internal int[] WAGROUPING
1250         {
1251             get
1252             {
1253                 if (this.waGrouping == null || UseUserOverride)
1254                 {
1255                     this.waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING));
1256                 }
1257                 return this.waGrouping;
1258             }
1259         }
1260
1261
1262         //                internal String sDecimalSeparator        ; // (user can override) decimal separator
1263         //                internal String sThousandSeparator       ; // (user can override) thousands separator
1264
1265         // Not a Number
1266         internal String SNAN
1267         {
1268             get
1269             {
1270                 if (this.sNaN == null)
1271                 {
1272                     this.sNaN = DoGetLocaleInfo(LOCALE_SNAN);
1273                 }
1274                 return this.sNaN;
1275             }
1276         }
1277
1278         // + Infinity
1279         internal String SPOSINFINITY
1280         {
1281             get
1282             {
1283                 if (this.sPositiveInfinity == null)
1284                 {
1285                     this.sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY);
1286                 }
1287                 return this.sPositiveInfinity;
1288             }
1289         }
1290
1291         // - Infinity
1292         internal String SNEGINFINITY
1293         {
1294             get
1295             {
1296                 if (this.sNegativeInfinity == null)
1297                 {
1298                     this.sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY);
1299                 }
1300                 return this.sNegativeInfinity;
1301             }
1302         }
1303
1304
1305         ////////////
1306         // Percent //
1307         ///////////
1308
1309         // Negative Percent (0-3)
1310         internal int INEGATIVEPERCENT
1311         {
1312             get
1313             {
1314                 if (this.iNegativePercent == undef)
1315                 {
1316                     // Note that <= Windows Vista this is synthesized by native code
1317                     this.iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT);
1318                 }
1319                 return this.iNegativePercent;
1320             }
1321         }
1322
1323         // Positive Percent (0-11)
1324         internal int IPOSITIVEPERCENT
1325         {
1326             get
1327             {
1328                 if (this.iPositivePercent == undef)
1329                 {
1330                     // Note that <= Windows Vista this is synthesized by native code
1331                     this.iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT);
1332                 }
1333                 return this.iPositivePercent;
1334             }
1335         }
1336
1337         // Percent (%) symbol
1338         internal String SPERCENT
1339         {
1340             get
1341             {
1342                 if (this.sPercent == null)
1343                 {
1344                     // Note that <= Windows Vista this is synthesized by native code
1345                     this.sPercent = DoGetLocaleInfo(LOCALE_SPERCENT);
1346                 }
1347                 return this.sPercent;
1348             }
1349         }
1350
1351         // PerMille (‰) symbol
1352         internal String SPERMILLE
1353         {
1354             get
1355             {
1356                 if (this.sPerMille == null)
1357                 {
1358                     // Note that <= Windows Vista this is synthesized by native code
1359                     this.sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE);
1360                 }
1361                 return this.sPerMille;
1362             }
1363         }
1364
1365         /////////////
1366         // Currency //
1367         /////////////
1368
1369         // (user can override) local monetary symbol, eg: $
1370         internal String SCURRENCY
1371         {
1372             get
1373             {
1374                 if (this.sCurrency == null || UseUserOverride)
1375                 {
1376                     this.sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY);
1377                 }
1378                 return this.sCurrency;
1379             }
1380         }
1381
1382         // international monetary symbol (RegionInfo), eg: USD
1383         internal String SINTLSYMBOL
1384         {
1385             get
1386             {
1387                 if (this.sIntlMonetarySymbol == null)
1388                 {
1389                     this.sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL);
1390                 }
1391                 return this.sIntlMonetarySymbol;
1392             }
1393         }
1394
1395         // English name for this currency (RegionInfo), eg: US Dollar
1396         internal String SENGLISHCURRENCY
1397         {
1398             get
1399             {
1400                 if (this.sEnglishCurrency == null)
1401                 {
1402                     this.sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME);
1403                 }
1404                 return this.sEnglishCurrency;
1405             }
1406         }
1407
1408         // Native name for this currency (RegionInfo), eg: Schweiz Frank
1409         internal String SNATIVECURRENCY
1410         {
1411             get
1412             {
1413                 if (this.sNativeCurrency == null)
1414                 {
1415                     this.sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME);
1416                 }
1417                 return this.sNativeCurrency;
1418             }
1419         }
1420
1421         //                internal int iCurrencyDigits          ; // (user can override) # local monetary fractional digits
1422         //                internal int iCurrency                ; // (user can override) positive currency format
1423         //                internal int iNegativeCurrency        ; // (user can override) negative currency format
1424
1425         // (user can override) monetary grouping of digits
1426         internal int[] WAMONGROUPING
1427         {
1428             get
1429             {
1430                 if (this.waMonetaryGrouping == null || UseUserOverride)
1431                 {
1432                     this.waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING));
1433                 }
1434                 return this.waMonetaryGrouping;
1435             }
1436         }
1437
1438         //                internal String sMonetaryDecimal         ; // (user can override) monetary decimal separator
1439         //                internal String sMonetaryThousand        ; // (user can override) monetary thousands separator
1440
1441         /////////
1442         // Misc //
1443         /////////
1444
1445         // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1446         internal int IMEASURE
1447         {
1448             get
1449             {
1450                 if (this.iMeasure == undef || UseUserOverride)
1451                 {
1452                     this.iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE);
1453                 }
1454                 return this.iMeasure;
1455             }
1456         }
1457
1458         // (user can override) list Separator
1459         internal String SLIST
1460         {
1461             get
1462             {
1463                 if (this.sListSeparator == null || UseUserOverride)
1464                 {
1465                     this.sListSeparator = DoGetLocaleInfo(LOCALE_SLIST);
1466                 }
1467                 return this.sListSeparator;
1468             }
1469         }
1470
1471         ////////////////////////////
1472         // Calendar/Time (Gregorian) //
1473         ////////////////////////////
1474
1475         // (user can override) AM designator
1476         internal String SAM1159
1477         {
1478             get
1479             {
1480                 if (this.sAM1159 == null || UseUserOverride)
1481                 {
1482                     this.sAM1159 = DoGetLocaleInfo(LOCALE_S1159);
1483                 }
1484                 return this.sAM1159;
1485             }
1486         }
1487
1488         // (user can override) PM designator
1489         internal String SPM2359
1490         {
1491             get
1492             {
1493                 if (this.sPM2359 == null || UseUserOverride)
1494                 {
1495                     this.sPM2359 = DoGetLocaleInfo(LOCALE_S2359);
1496                 }
1497                 return this.sPM2359;
1498             }
1499         }
1500
1501         // (user can override) time format
1502         internal String[] LongTimes
1503         {
1504             get
1505             {
1506                 if (this.saLongTimes == null || UseUserOverride)
1507                 {
1508                     String[] longTimes = DoEnumTimeFormats();
1509                     if (longTimes == null || longTimes.Length == 0)
1510                     {
1511                         this.saLongTimes = Invariant.saLongTimes;
1512                     }
1513                     else
1514                     {
1515                         this.saLongTimes = longTimes;
1516                     }
1517                 }
1518                 return this.saLongTimes;
1519             }
1520         }
1521
1522         // short time format
1523         // Short times (derived from long times format)
1524         internal String[] ShortTimes
1525         {
1526             get
1527             {
1528                 if (this.saShortTimes == null || UseUserOverride)
1529                 {
1530                     // Try to get the short times from the OS/culture.dll
1531                     String[] shortTimes = DoEnumShortTimeFormats();
1532
1533                     if (shortTimes == null || shortTimes.Length == 0)
1534                     {
1535                         //
1536                         // If we couldn't find short times, then compute them from long times
1537                         // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
1538                         //
1539                         shortTimes = DeriveShortTimesFromLong();
1540                     }
1541
1542                     // Found short times, use them
1543                     this.saShortTimes = shortTimes;
1544                 }
1545                 return this.saShortTimes;
1546             }
1547         }
1548
1549         private string[] DeriveShortTimesFromLong()
1550         {
1551             // Our logic is to look for h,H,m,s,t.  If we find an s, then we check the string
1552             // between it and the previous marker, if any.  If its a short, unescaped separator,
1553             // then we don't retain that part.
1554             // We then check after the ss and remove anything before the next h,H,m,t...
1555             string[] shortTimes = new string[LongTimes.Length];
1556
1557             for (int i = 0; i < LongTimes.Length; i++)
1558             {
1559                 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
1560             }
1561             return shortTimes;
1562         }
1563
1564         private static string StripSecondsFromPattern(string time)
1565         {
1566             bool bEscape = false;
1567             int iLastToken = -1;
1568
1569             // Find the seconds
1570             for (int j = 0; j < time.Length; j++)
1571             {
1572                 // Change escape mode?
1573                 if (time[j] == '\'')
1574                 {
1575                     // Continue
1576                     bEscape = !bEscape;
1577                     continue;
1578                 }
1579
1580                 // See if there was a single \
1581                 if (time[j] == '\\')
1582                 {
1583                     // Skip next char
1584                     j++;
1585                     continue;
1586                 }
1587
1588                 if (bEscape)
1589                 {
1590                     continue;
1591                 }
1592
1593                 switch (time[j])
1594                 {
1595                     // Check for seconds
1596                     case 's':
1597                         // Found seconds, see if there was something unescaped and short between
1598                         // the last marker and the seconds.  Windows says separator can be a
1599                         // maximum of three characters (without null)
1600                         // If 1st or last characters were ', then ignore it
1601                         if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
1602                             (time[iLastToken + 1] != '\'') &&
1603                             (time[j - 1] != '\''))
1604                         {
1605                             // There was something there we want to remember
1606                             if (iLastToken >= 0)
1607                             {
1608                                 j = iLastToken + 1;
1609                             }
1610                         }
1611
1612                         bool containsSpace;
1613                         int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
1614                         StringBuilder sb = new StringBuilder(time.Substring(0, j));
1615                         if (containsSpace)
1616                         {
1617                             sb.Append(' ');
1618                         }
1619                         sb.Append(time.Substring(endIndex));
1620                         time = sb.ToString();
1621                         break;
1622                     case 'm':
1623                     case 'H':
1624                     case 'h':
1625                         iLastToken = j;
1626                         break;
1627                 }
1628             }
1629             return time;
1630         }
1631
1632         private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
1633         {
1634             bool bEscape = false;
1635             containsSpace = false;
1636             for (; index < time.Length; index++)
1637             {
1638                 switch (time[index])
1639                 {
1640                     case '\'':
1641                         bEscape = !bEscape;
1642                         continue;
1643                     case '\\':
1644                         index++;
1645                         if (time[index] == ' ')
1646                         {
1647                             containsSpace = true;
1648                         }
1649                         continue;
1650                     case ' ':
1651                         containsSpace = true;
1652                         break;
1653                     case 't':
1654                     case 'm':
1655                     case 'H':
1656                     case 'h':
1657                         if (bEscape)
1658                         {
1659                             continue;
1660                         }
1661                         return index;
1662                 }
1663             }
1664             containsSpace = false;
1665             return index;
1666         }
1667
1668         // (user can override) first day of week
1669         internal int IFIRSTDAYOFWEEK
1670         {
1671             get
1672             {
1673                 if (this.iFirstDayOfWeek == undef || UseUserOverride)
1674                 {
1675                     // Have to convert it from windows to .Net formats
1676                     this.iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK));
1677                 }
1678                 return this.iFirstDayOfWeek;
1679             }
1680         }
1681
1682         // (user can override) first week of year
1683         internal int IFIRSTWEEKOFYEAR
1684         {
1685             get
1686             {
1687                 if (this.iFirstWeekOfYear == undef || UseUserOverride)
1688                 {
1689                     this.iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR);
1690                 }
1691                 return this.iFirstWeekOfYear;
1692             }
1693         }
1694
1695         // (user can override default only) short date format
1696         internal String[] ShortDates(int calendarId)
1697         {
1698             return GetCalendar(calendarId).saShortDates;
1699         }
1700
1701         // (user can override default only) long date format
1702         internal String[] LongDates(int calendarId)
1703         {
1704             return GetCalendar(calendarId).saLongDates;
1705         }
1706
1707         // (user can override) date year/month format.
1708         internal String[] YearMonths(int calendarId)
1709         {
1710             return GetCalendar(calendarId).saYearMonths;
1711         }
1712
1713         // day names
1714         internal string[] DayNames(int calendarId)
1715         {
1716             return GetCalendar(calendarId).saDayNames;
1717         }
1718
1719         // abbreviated day names
1720         internal string[] AbbreviatedDayNames(int calendarId)
1721         {
1722             // Get abbreviated day names for this calendar from the OS if necessary
1723             return GetCalendar(calendarId).saAbbrevDayNames;
1724         }
1725
1726         // The super short day names
1727         internal string[] SuperShortDayNames(int calendarId)
1728         {
1729             return GetCalendar(calendarId).saSuperShortDayNames;
1730         }
1731
1732         // month names
1733         internal string[] MonthNames(int calendarId)
1734         {
1735             return GetCalendar(calendarId).saMonthNames;
1736         }
1737
1738         // Genitive month names
1739         internal string[] GenitiveMonthNames(int calendarId)
1740         {
1741             return GetCalendar(calendarId).saMonthGenitiveNames;
1742         }
1743
1744         // month names
1745         internal string[] AbbreviatedMonthNames(int calendarId)
1746         {
1747             return GetCalendar(calendarId).saAbbrevMonthNames;
1748         }
1749
1750         // Genitive month names
1751         internal string[] AbbreviatedGenitiveMonthNames(int calendarId)
1752         {
1753             return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
1754         }
1755
1756         // Leap year month names
1757         // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
1758         // the non-leap names skip the 7th name in the normal month name array
1759         internal string[] LeapYearMonthNames(int calendarId)
1760         {
1761             return GetCalendar(calendarId).saLeapYearMonthNames;
1762         }
1763
1764         // month/day format (single string, no override)
1765         internal String MonthDay(int calendarId)
1766         {
1767             return GetCalendar(calendarId).sMonthDay;
1768         }
1769
1770
1771
1772         /////////////
1773         // Calendars //
1774         /////////////
1775
1776         // all available calendar type(s), The first one is the default calendar.
1777         internal int[] CalendarIds
1778         {
1779             get
1780             {
1781                 if (this.waCalendars == null)
1782                 {
1783                     // We pass in an array of ints, and native side fills it up with count calendars.
1784                     // We then have to copy that list to a new array of the right size.
1785                     // Default calendar should be first
1786                     int[] calendarInts = new int[23];
1787                     Debug.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1788                     int count = CalendarData.nativeGetCalendars(this.sWindowsName, this.bUseOverrides, calendarInts);
1789
1790                     // See if we had a calendar to add.
1791                     if (count == 0)
1792                     {
1793                         // Failed for some reason, just grab Gregorian from Invariant
1794                         this.waCalendars = Invariant.waCalendars;
1795                     }
1796                     else
1797                     {
1798                         // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
1799                         if (this.sWindowsName == "zh-TW")
1800                         {
1801                             bool found = false;
1802
1803                             // Do we need to insert calendar 4?
1804                             for (int i = 0; i < count; i++)
1805                             {
1806                                 // Stop if we found calendar four
1807                                 if (calendarInts[i] == Calendar.CAL_TAIWAN)
1808                                 {
1809                                     found = true;
1810                                     break;
1811                                 }
1812                             }
1813
1814                             // If not found then insert it
1815                             if (!found)
1816                             {
1817                                 // Insert it as the 2nd calendar
1818                                 count++;
1819                                 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
1820                                 Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1);
1821                                 calendarInts[1] = Calendar.CAL_TAIWAN;
1822                             }
1823                         }
1824
1825                         // It worked, remember the list
1826                         int[] temp = new int[count];
1827                         Array.Copy(calendarInts, temp, count);
1828
1829                         // Want 1st calendar to be default
1830                         // Prior to Vista the enumeration didn't have default calendar first
1831                         // Only a coreclr concern, culture.dll does the right thing.
1832                         if (temp.Length > 1)
1833                         {
1834                             int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
1835                             if (temp[1] == i)
1836                             {
1837                                 temp[1] = temp[0];
1838                                 temp[0] = i;
1839                             }
1840                         }
1841
1842                         this.waCalendars = temp;
1843                     }
1844                 }
1845
1846                 return this.waCalendars;
1847             }
1848         }
1849
1850         // Native calendar names.  index of optional calendar - 1, empty if no optional calendar at that number
1851         internal String CalendarName(int calendarId)
1852         {
1853             // Get the calendar
1854             return GetCalendar(calendarId).sNativeName;
1855         }
1856
1857         internal CalendarData GetCalendar(int calendarId)
1858         {
1859             Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
1860                 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
1861
1862             // arrays are 0 based, calendarIds are 1 based
1863             int calendarIndex = calendarId - 1;
1864
1865             // Have to have calendars
1866             if (calendars == null)
1867             {
1868                 calendars = new CalendarData[CalendarData.MAX_CALENDARS];
1869             }
1870
1871             // we need the following local variable to avoid returning null
1872             // when another thread creates a new array of CalendarData (above)
1873             // right after we insert the newly created CalendarData (below)
1874             CalendarData calendarData = calendars[calendarIndex];
1875             // Make sure that calendar has data
1876             if (calendarData == null || UseUserOverride)
1877             {
1878                 Debug.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1879                 calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
1880                 calendars[calendarIndex] = calendarData;
1881             }
1882
1883             return calendarData;
1884         }
1885
1886         ///////////////////
1887         // Text Information //
1888         ///////////////////
1889
1890         // IsRightToLeft
1891         internal bool IsRightToLeft
1892         {
1893             get
1894             {
1895                 // Returns one of the following 4 reading layout values:
1896                 // 0 - Left to right (eg en-US)
1897                 // 1 - Right to left (eg arabic locales)
1898                 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1899                 // 3 - Vertical top to bottom with columns proceeding to the right
1900                 return (this.IREADINGLAYOUT == 1);
1901             }
1902         }
1903
1904         // IREADINGLAYOUT
1905         // Returns one of the following 4 reading layout values:
1906         // 0 - Left to right (eg en-US)
1907         // 1 - Right to left (eg arabic locales)
1908         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
1909         // 3 - Vertical top to bottom with columns proceeding to the right
1910         //
1911         // If exposed as a public API, we'd have an enum with those 4 values
1912         private int IREADINGLAYOUT
1913         {
1914             get
1915             {
1916                 if (this.iReadingLayout == undef)
1917                 {
1918                     Debug.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
1919                     this.iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT);
1920                 }
1921
1922                 return (this.iReadingLayout);
1923             }
1924         }
1925
1926         // The TextInfo name never includes that alternate sort and is always specific
1927         // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
1928         // en -> en-US
1929         // en-US -> en-US
1930         // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
1931         // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
1932         // es-ES_tradnl -> es-ES
1933         internal String STEXTINFO               // Text info name to use for text information
1934         {
1935             get
1936             {
1937                 if (this.sTextInfo == null)
1938                 {
1939                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
1940                     // It is also not supported downlevel without culture.dll.
1941                     if (IsNeutralCulture || IsSupplementalCustomCulture)
1942                     {
1943                         string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
1944                         this.sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME;
1945                     }
1946
1947                     if (this.sTextInfo == null)
1948                     {
1949                         this.sTextInfo = this.SNAME; // removes alternate sort
1950                     }
1951                 }
1952
1953                 return this.sTextInfo;
1954             }
1955         }
1956
1957         // Compare info name (including sorting key) to use if custom
1958         internal String SCOMPAREINFO
1959         {
1960             get
1961             {
1962                 if (this.sCompareInfo == null)
1963                 {
1964                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
1965                     // It is also not supported downlevel without culture.dll.
1966                     // We really only need it for the custom locale case though
1967                     // since for all other cases, it is the same as sWindowsName
1968                     if (IsSupplementalCustomCulture)
1969                     {
1970                         this.sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
1971                     }
1972
1973                     if (this.sCompareInfo == null)
1974                     {
1975                         this.sCompareInfo = this.sWindowsName;
1976                     }
1977                 }
1978
1979                 return this.sCompareInfo;
1980             }
1981         }
1982
1983         internal bool IsSupplementalCustomCulture
1984         {
1985             get
1986             {
1987                 return IsCustomCultureId(this.ILANGUAGE);
1988             }
1989         }
1990
1991
1992         internal int IDEFAULTANSICODEPAGE   // default ansi code page ID (ACP)
1993         {
1994             get
1995             {
1996                 if (this.iDefaultAnsiCodePage == undef)
1997                 {
1998                     this.iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE);
1999                 }
2000                 return this.iDefaultAnsiCodePage;
2001             }
2002         }
2003
2004         internal int IDEFAULTOEMCODEPAGE   // default oem code page ID (OCP or OEM)
2005         {
2006             get
2007             {
2008                 if (this.iDefaultOemCodePage == undef)
2009                 {
2010                     this.iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE);
2011                 }
2012                 return this.iDefaultOemCodePage;
2013             }
2014         }
2015
2016         internal int IDEFAULTMACCODEPAGE   // default macintosh code page
2017         {
2018             get
2019             {
2020                 if (this.iDefaultMacCodePage == undef)
2021                 {
2022                     this.iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE);
2023                 }
2024                 return this.iDefaultMacCodePage;
2025             }
2026         }
2027
2028         internal int IDEFAULTEBCDICCODEPAGE   // default EBCDIC code page
2029         {
2030             get
2031             {
2032                 if (this.iDefaultEbcdicCodePage == undef)
2033                 {
2034                     this.iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE);
2035                 }
2036                 return this.iDefaultEbcdicCodePage;
2037             }
2038         }
2039
2040         // Obtain locale name from LCID
2041         // NOTE: This will get neutral names, unlike the OS API
2042         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2043         internal static extern int LocaleNameToLCID(String localeName);
2044
2045         // These are desktop only, not coreclr
2046         // locale ID (0409), including sort information
2047         internal int ILANGUAGE
2048         {
2049             get
2050             {
2051                 if (this.iLanguage == 0)
2052                 {
2053                     Debug.Assert(this.sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2054                     this.iLanguage = LocaleNameToLCID(this.sRealName);
2055                 }
2056                 return this.iLanguage;
2057             }
2058         }
2059
2060         internal bool IsWin32Installed
2061         {
2062             get { return this.bWin32Installed; }
2063         }
2064
2065         internal bool IsFramework
2066         {
2067             get { return this.bFramework; }
2068         }
2069
2070         ////////////////////
2071         // Derived properties //
2072         ////////////////////
2073
2074         internal bool IsNeutralCulture
2075         {
2076             get
2077             {
2078                 // NlsInfo::nativeInitCultureData told us if we're neutral or not
2079                 return this.bNeutral;
2080             }
2081         }
2082
2083         internal bool IsInvariantCulture
2084         {
2085             get
2086             {
2087                 return String.IsNullOrEmpty(this.SNAME);
2088             }
2089         }
2090
2091         // Get an instance of our default calendar
2092         internal Calendar DefaultCalendar
2093         {
2094             get
2095             {
2096                 int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2097                 if (defaultCalId == 0)
2098                 {
2099                     defaultCalId = this.CalendarIds[0];
2100                 }
2101
2102                 return CultureInfo.GetCalendarInstance(defaultCalId);
2103             }
2104         }
2105
2106         // All of our era names
2107         internal String[] EraNames(int calendarId)
2108         {
2109             Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
2110
2111             return this.GetCalendar(calendarId).saEraNames;
2112         }
2113
2114         internal String[] AbbrevEraNames(int calendarId)
2115         {
2116             Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2117
2118             return this.GetCalendar(calendarId).saAbbrevEraNames;
2119         }
2120
2121         internal String[] AbbreviatedEnglishEraNames(int calendarId)
2122         {
2123             Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2124
2125             return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2126         }
2127
2128         // String array DEFAULTS
2129         // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2130
2131
2132         // Time separator (derived from time format)
2133         internal String TimeSeparator
2134         {
2135             get
2136             {
2137                 if (sTimeSeparator == null || UseUserOverride)
2138                 {
2139                     string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT));
2140                     if (String.IsNullOrEmpty(longTimeFormat))
2141                     {
2142                         longTimeFormat = LongTimes[0];
2143                     }
2144
2145                     // Compute STIME from time format
2146                     sTimeSeparator = GetTimeSeparator(longTimeFormat);
2147                 }
2148                 return sTimeSeparator;
2149             }
2150         }
2151
2152         // Date separator (derived from short date format)
2153         internal String DateSeparator(int calendarId)
2154         {
2155             return GetDateSeparator(ShortDates(calendarId)[0]);
2156         }
2157
2158         //////////////////////////////////////
2159         // Helper Functions to get derived properties //
2160         //////////////////////////////////////
2161
2162         ////////////////////////////////////////////////////////////////////////////
2163         //
2164         // Unescape a NLS style quote string
2165         //
2166         // This removes single quotes:
2167         //      'fred' -> fred
2168         //      'fred -> fred
2169         //      fred' -> fred
2170         //      fred's -> freds
2171         //
2172         // This removes the first \ of escaped characters:
2173         //      fred\'s -> fred's
2174         //      a\\b -> a\b
2175         //      a\b -> ab
2176         //
2177         // We don't build the stringbuilder unless we find a ' or a \.  If we find a ' or a \, we
2178         // always build a stringbuilder because we need to remove the ' or \.
2179         //
2180         ////////////////////////////////////////////////////////////////////////////
2181         static private String UnescapeNlsString(String str, int start, int end)
2182         {
2183             Contract.Requires(str != null);
2184             Contract.Requires(start >= 0);
2185             Contract.Requires(end >= 0);
2186             StringBuilder result = null;
2187
2188             for (int i = start; i < str.Length && i <= end; i++)
2189             {
2190                 switch (str[i])
2191                 {
2192                     case '\'':
2193                         if (result == null)
2194                         {
2195                             result = new StringBuilder(str, start, i - start, str.Length);
2196                         }
2197                         break;
2198                     case '\\':
2199                         if (result == null)
2200                         {
2201                             result = new StringBuilder(str, start, i - start, str.Length);
2202                         }
2203                         ++i;
2204                         if (i < str.Length)
2205                         {
2206                             result.Append(str[i]);
2207                         }
2208                         break;
2209                     default:
2210                         if (result != null)
2211                         {
2212                             result.Append(str[i]);
2213                         }
2214                         break;
2215                 }
2216             }
2217
2218             if (result == null)
2219                 return (str.Substring(start, end - start + 1));
2220
2221             return (result.ToString());
2222         }
2223
2224         ////////////////////////////////////////////////////////////////////////////
2225         //
2226         // Reescape a Win32 style quote string as a NLS+ style quoted string
2227         //
2228         // This is also the escaping style used by custom culture data files
2229         //
2230         // NLS+ uses \ to escape the next character, whether in a quoted string or
2231         // not, so we always have to change \ to \\.
2232         //
2233         // NLS+ uses \' to escape a quote inside a quoted string so we have to change
2234         // '' to \' (if inside a quoted string)
2235         //
2236         // We don't build the stringbuilder unless we find something to change
2237         ////////////////////////////////////////////////////////////////////////////
2238         static internal String ReescapeWin32String(String str)
2239         {
2240             // If we don't have data, then don't try anything
2241             if (str == null)
2242                 return null;
2243
2244             StringBuilder result = null;
2245
2246             bool inQuote = false;
2247             for (int i = 0; i < str.Length; i++)
2248             {
2249                 // Look for quote
2250                 if (str[i] == '\'')
2251                 {
2252                     // Already in quote?
2253                     if (inQuote)
2254                     {
2255                         // See another single quote.  Is this '' of 'fred''s' or '''', or is it an ending quote?
2256                         if (i + 1 < str.Length && str[i + 1] == '\'')
2257                         {
2258                             // Found another ', so we have ''.  Need to add \' instead.
2259                             // 1st make sure we have our stringbuilder
2260                             if (result == null)
2261                                 result = new StringBuilder(str, 0, i, str.Length * 2);
2262
2263                             // Append a \' and keep going (so we don't turn off quote mode)
2264                             result.Append("\\'");
2265                             i++;
2266                             continue;
2267                         }
2268
2269                         // Turning off quote mode, fall through to add it
2270                         inQuote = false;
2271                     }
2272                     else
2273                     {
2274                         // Found beginning quote, fall through to add it
2275                         inQuote = true;
2276                     }
2277                 }
2278                 // Is there a single \ character?
2279                 else if (str[i] == '\\')
2280                 {
2281                     // Found a \, need to change it to \\
2282                     // 1st make sure we have our stringbuilder
2283                     if (result == null)
2284                         result = new StringBuilder(str, 0, i, str.Length * 2);
2285
2286                     // Append our \\ to the string & continue
2287                     result.Append("\\\\");
2288                     continue;
2289                 }
2290
2291                 // If we have a builder we need to add our character
2292                 if (result != null)
2293                     result.Append(str[i]);
2294             }
2295
2296             // Unchanged string? , just return input string
2297             if (result == null)
2298                 return str;
2299
2300             // String changed, need to use the builder
2301             return result.ToString();
2302         }
2303
2304         static internal String[] ReescapeWin32Strings(String[] array)
2305         {
2306             if (array != null)
2307             {
2308                 for (int i = 0; i < array.Length; i++)
2309                 {
2310                     array[i] = ReescapeWin32String(array[i]);
2311                 }
2312             }
2313
2314             return array;
2315         }
2316
2317         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2318         // and breaking changes here will not show up at build time, only at run time.
2319         static private String GetTimeSeparator(String format)
2320         {
2321             // Time format separator (ie: : in 12:39:00)
2322             //
2323             // We calculate this from the provided time format
2324             //
2325
2326             //
2327             //  Find the time separator so that we can pretend we know STIME.
2328             //
2329             return GetSeparator(format, "Hhms");
2330         }
2331
2332         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2333         // and breaking changes here will not show up at build time, only at run time.
2334         static private String GetDateSeparator(String format)
2335         {
2336             // Date format separator (ie: / in 9/1/03)
2337             //
2338             // We calculate this from the provided short date
2339             //
2340
2341             //
2342             //  Find the date separator so that we can pretend we know SDATE.
2343             //
2344             return GetSeparator(format, "dyM");
2345         }
2346
2347         private static string GetSeparator(string format, string timeParts)
2348         {
2349             int index = IndexOfTimePart(format, 0, timeParts);
2350
2351             if (index != -1)
2352             {
2353                 // Found a time part, find out when it changes
2354                 char cTimePart = format[index];
2355
2356                 do
2357                 {
2358                     index++;
2359                 } while (index < format.Length && format[index] == cTimePart);
2360
2361                 int separatorStart = index;
2362
2363                 // Now we need to find the end of the separator
2364                 if (separatorStart < format.Length)
2365                 {
2366                     int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
2367                     if (separatorEnd != -1)
2368                     {
2369                         // From [separatorStart, count) is our string, except we need to unescape
2370                         return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
2371                     }
2372                 }
2373             }
2374
2375             return String.Empty;
2376         }
2377
2378         private static int IndexOfTimePart(string format, int startIndex, string timeParts)
2379         {
2380             Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
2381             Debug.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
2382             bool inQuote = false;
2383             for (int i = startIndex; i < format.Length; ++i)
2384             {
2385                 // See if we have a time Part
2386                 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
2387                 {
2388                     return i;
2389                 }
2390                 switch (format[i])
2391                 {
2392                     case '\\':
2393                         if (i + 1 < format.Length)
2394                         {
2395                             ++i;
2396                             switch (format[i])
2397                             {
2398                                 case '\'':
2399                                 case '\\':
2400                                     break;
2401                                 default:
2402                                     --i; //backup since we will move over this next
2403                                     break;
2404                             }
2405                         }
2406                         break;
2407                     case '\'':
2408                         inQuote = !inQuote;
2409                         break;
2410                 }
2411             }
2412
2413             return -1;
2414         }
2415
2416         string DoGetLocaleInfo(uint lctype)
2417         {
2418             Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2419             return DoGetLocaleInfo(this.sWindowsName, lctype);
2420         }
2421
2422         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
2423         // "windows" name, which can be specific for downlevel (< windows 7) os's.
2424         string DoGetLocaleInfo(string localeName, uint lctype)
2425         {
2426             // Fix lctype if we don't want overrides
2427             if (!UseUserOverride)
2428             {
2429                 lctype |= LOCALE_NOUSEROVERRIDE;
2430             }
2431
2432             // Ask OS for data
2433             Debug.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null");
2434             string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
2435             if (result == null)
2436             {
2437                 // Failed, just use empty string
2438                 result = String.Empty;
2439             }
2440
2441             return result;
2442         }
2443
2444         int DoGetLocaleInfoInt(uint lctype)
2445         {
2446             // Fix lctype if we don't want overrides
2447             if (!UseUserOverride)
2448             {
2449                 lctype |= LOCALE_NOUSEROVERRIDE;
2450             }
2451
2452             // Ask OS for data, note that we presume it returns success, so we have to know that
2453             // sWindowsName is valid before calling.
2454             Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2455             int result = CultureInfo.nativeGetLocaleInfoExInt(this.sWindowsName, lctype);
2456
2457             return result;
2458         }
2459
2460         String[] DoEnumTimeFormats()
2461         {
2462             // Note that this gets overrides for us all the time
2463             Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2464             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, 0, UseUserOverride));
2465
2466             return result;
2467         }
2468
2469         String[] DoEnumShortTimeFormats()
2470         {
2471             // Note that this gets overrides for us all the time
2472             Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2473             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, TIME_NOSECONDS, UseUserOverride));
2474
2475             return result;
2476         }
2477
2478         /////////////////
2479         // Static Helpers //
2480         ////////////////
2481         internal static bool IsCustomCultureId(int cultureId)
2482         {
2483             if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
2484                 return true;
2485
2486             return false;
2487         }
2488
2489         ////////////////////////////////////////////////////////////////////////////
2490         //
2491         // Parameters:
2492         //      calendarValueOnly   Retrieve the values which are affected by the calendar change of DTFI.
2493         //                          This will cause values like longTimePattern not be retrieved since it is
2494         //                          not affected by the Calendar property in DTFI.
2495         //
2496         ////////////////////////////////////////////////////////////////////////////
2497         internal void GetNFIValues(NumberFormatInfo nfi)
2498         {
2499             if (this.IsInvariantCulture)
2500             {
2501                 nfi.positiveSign = this.sPositiveSign;
2502                 nfi.negativeSign = this.sNegativeSign;
2503
2504                 nfi.nativeDigits = this.saNativeDigits;
2505                 nfi.digitSubstitution = this.iDigitSubstitution;
2506
2507                 nfi.numberGroupSeparator = this.sThousandSeparator;
2508                 nfi.numberDecimalSeparator = this.sDecimalSeparator;
2509                 nfi.numberDecimalDigits = this.iDigits;
2510                 nfi.numberNegativePattern = this.iNegativeNumber;
2511
2512                 nfi.currencySymbol = this.sCurrency;
2513                 nfi.currencyGroupSeparator = this.sMonetaryThousand;
2514                 nfi.currencyDecimalSeparator = this.sMonetaryDecimal;
2515                 nfi.currencyDecimalDigits = this.iCurrencyDigits;
2516                 nfi.currencyNegativePattern = this.iNegativeCurrency;
2517                 nfi.currencyPositivePattern = this.iCurrency;
2518             }
2519             else
2520             {
2521                 //
2522                 // We don't have information for the following four.  All cultures use
2523                 // the same value of the number formatting values.
2524                 //
2525                 // PercentDecimalDigits
2526                 // PercentDecimalSeparator
2527                 // PercentGroupSize
2528                 // PercentGroupSeparator
2529                 //
2530
2531                 //
2532                 // Ask native side for our data.
2533                 //
2534                 Debug.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2535                 CultureData.nativeGetNumberFormatInfoValues(this.sWindowsName, nfi, UseUserOverride);
2536             }
2537
2538
2539             //
2540             // Gather additional data
2541             //
2542             nfi.numberGroupSizes = this.WAGROUPING;
2543             nfi.currencyGroupSizes = this.WAMONGROUPING;
2544
2545             // prefer the cached value since these do not have user overrides
2546             nfi.percentNegativePattern = this.INEGATIVEPERCENT;
2547             nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
2548             nfi.percentSymbol = this.SPERCENT;
2549             nfi.perMilleSymbol = this.SPERMILLE;
2550
2551             nfi.negativeInfinitySymbol = this.SNEGINFINITY;
2552             nfi.positiveInfinitySymbol = this.SPOSINFINITY;
2553             nfi.nanSymbol = this.SNAN;
2554
2555             //
2556             // We don't have percent values, so use the number values
2557             //
2558             nfi.percentDecimalDigits = nfi.numberDecimalDigits;
2559             nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
2560             nfi.percentGroupSizes = nfi.numberGroupSizes;
2561             nfi.percentGroupSeparator = nfi.numberGroupSeparator;
2562
2563             //
2564             // Clean up a few odd values
2565             //
2566
2567             // Windows usually returns an empty positive sign, but we like it to be "+"
2568             if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
2569
2570             //Special case for Italian.  The currency decimal separator in the control panel is the empty string. When the user
2571             //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
2572             //decimal point doesn't show up.  We'll just workaround this here because our default currency format will never use nfi.
2573             if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
2574             {
2575                 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
2576             }
2577         }
2578
2579         static private int ConvertFirstDayOfWeekMonToSun(int iTemp)
2580         {
2581             // Convert Mon-Sun to Sun-Sat format
2582             iTemp++;
2583             if (iTemp > 6)
2584             {
2585                 // Wrap Sunday and convert invalid data to Sunday
2586                 iTemp = 0;
2587             }
2588             return iTemp;
2589         }
2590
2591         // Helper
2592         // This is ONLY used for caching names and shouldn't be used for anything else
2593         internal static string AnsiToLower(string testString)
2594         {
2595             StringBuilder sb = new StringBuilder(testString.Length);
2596
2597             for (int ich = 0; ich < testString.Length; ich++)
2598             {
2599                 char ch = testString[ich];
2600                 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
2601             }
2602
2603             return (sb.ToString());
2604         }
2605
2606         // If we get a group from windows, then its in 3;0 format with the 0 backwards
2607         // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
2608         // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
2609         static private int[] ConvertWin32GroupString(String win32Str)
2610         {
2611             // None of these cases make any sense
2612             if (win32Str == null || win32Str.Length == 0)
2613             {
2614                 return (new int[] { 3 });
2615             }
2616
2617             if (win32Str[0] == '0')
2618             {
2619                 return (new int[] { 0 });
2620             }
2621
2622             // Since its in n;n;n;n;n format, we can always get the length quickly
2623             int[] values;
2624             if (win32Str[win32Str.Length - 1] == '0')
2625             {
2626                 // Trailing 0 gets dropped. 1;0 -> 1
2627                 values = new int[(win32Str.Length / 2)];
2628             }
2629             else
2630             {
2631                 // Need extra space for trailing zero 1 -> 1;0
2632                 values = new int[(win32Str.Length / 2) + 2];
2633                 values[values.Length - 1] = 0;
2634             }
2635
2636             int i;
2637             int j;
2638             for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
2639             {
2640                 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
2641                 // But we'll test because its registry that could be anything
2642                 if (win32Str[i] < '1' || win32Str[i] > '9')
2643                     return new int[] { 3 };
2644
2645                 values[j] = (int)(win32Str[i] - '0');
2646             }
2647
2648             return (values);
2649         }
2650
2651         // LCTYPES for GetLocaleInfo
2652         private const uint LOCALE_NOUSEROVERRIDE = 0x80000000;   // do not use user overrides
2653         private const uint LOCALE_RETURN_NUMBER = 0x20000000;   // return number instead of string
2654
2655         // Modifier for genitive names
2656         private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000;   //Flag to return the Genitive forms of month names
2657
2658         //
2659         //  The following LCTypes are mutually exclusive in that they may NOT
2660         //  be used in combination with each other.
2661         //
2662
2663         //
2664         // These are the various forms of the name of the locale:
2665         //
2666         private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002;   // localized name of locale, eg "German (Germany)" in UI language
2667         private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072;   // Display name (language + country usually) in English, eg "German (Germany)"
2668         private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073;   // Display name in native locale language, eg "Deutsch (Deutschland)
2669
2670         private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f;   // Language Display Name for a language, eg "German" in UI language
2671         private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001;   // English name of language, eg "German"
2672         private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004;   // native name of language, eg "Deutsch"
2673
2674         private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006;   // localized name of country, eg "Germany" in UI language
2675         private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002;   // English name of country, eg "Germany"
2676         private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008;   // native name of country, eg "Deutschland"
2677
2678
2679         //        private const uint LOCALE_ILANGUAGE              =0x00000001;   // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals)
2680
2681         //        private const uint LOCALE_SLANGUAGE              =LOCALE_SLOCALIZEDDISPLAYNAME;   // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead)
2682         //        private const uint LOCALE_SENGLANGUAGE           =LOCALE_SENGLISHLANGUAGENAME;   // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead)
2683         private const uint LOCALE_SABBREVLANGNAME = 0x00000003;   // abbreviated language name
2684         //        private const uint LOCALE_SNATIVELANGNAME        =LOCALE_SNATIVELANGUAGENAME;   // native name of language (use LOCALE_SNATIVELANGUAGENAME instead)
2685
2686         private const uint LOCALE_ICOUNTRY = 0x00000005;   // country code
2687         //        private const uint LOCALE_SCOUNTRY               =LOCALE_SLOCALIZEDCOUNTRYNAME;   // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead)
2688         //        private const uint LOCALE_SENGCOUNTRY            =LOCALE_SENGLISHCOUNTRYNAME;   // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead)
2689         private const uint LOCALE_SABBREVCTRYNAME = 0x00000007;   // abbreviated country name
2690         //        private const uint LOCALE_SNATIVECTRYNAME        =LOCALE_SNATIVECOUNTRYNAME;   // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead)
2691         private const uint LOCALE_IGEOID = 0x0000005B;   // geographical location id
2692
2693         private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009;   // default language id
2694         private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A;   // default country code
2695         private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B;   // default oem code page
2696         private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004;   // default ansi code page
2697         private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011;   // default mac code page
2698
2699         private const uint LOCALE_SLIST = 0x0000000C;   // list item separator
2700         private const uint LOCALE_IMEASURE = 0x0000000D;   // 0 = metric, 1 = US
2701
2702         private const uint LOCALE_SDECIMAL = 0x0000000E;   // decimal separator
2703         private const uint LOCALE_STHOUSAND = 0x0000000F;   // thousand separator
2704         private const uint LOCALE_SGROUPING = 0x00000010;   // digit grouping
2705         private const uint LOCALE_IDIGITS = 0x00000011;   // number of fractional digits
2706         private const uint LOCALE_ILZERO = 0x00000012;   // leading zeros for decimal
2707         private const uint LOCALE_INEGNUMBER = 0x00001010;   // negative number mode
2708         private const uint LOCALE_SNATIVEDIGITS = 0x00000013;   // native digits for 0-9
2709
2710         private const uint LOCALE_SCURRENCY = 0x00000014;   // local monetary symbol
2711         private const uint LOCALE_SINTLSYMBOL = 0x00000015;   // uintl monetary symbol
2712         private const uint LOCALE_SMONDECIMALSEP = 0x00000016;   // monetary decimal separator
2713         private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017;   // monetary thousand separator
2714         private const uint LOCALE_SMONGROUPING = 0x00000018;   // monetary grouping
2715         private const uint LOCALE_ICURRDIGITS = 0x00000019;   // # local monetary digits
2716         private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A;   // # uintl monetary digits
2717         private const uint LOCALE_ICURRENCY = 0x0000001B;   // positive currency mode
2718         private const uint LOCALE_INEGCURR = 0x0000001C;   // negative currency mode
2719
2720         private const uint LOCALE_SDATE = 0x0000001D;   // date separator (derived from LOCALE_SSHORTDATE, use that instead)
2721         private const uint LOCALE_STIME = 0x0000001E;   // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
2722         private const uint LOCALE_SSHORTDATE = 0x0000001F;   // short date format string
2723         private const uint LOCALE_SLONGDATE = 0x00000020;   // long date format string
2724         private const uint LOCALE_STIMEFORMAT = 0x00001003;   // time format string
2725         private const uint LOCALE_IDATE = 0x00000021;   // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead)
2726         private const uint LOCALE_ILDATE = 0x00000022;   // long date format ordering (derived from LOCALE_SLONGDATE, use that instead)
2727         private const uint LOCALE_ITIME = 0x00000023;   // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead)
2728         private const uint LOCALE_ITIMEMARKPOSN = 0x00001005;   // time marker position (derived from LOCALE_STIMEFORMAT, use that instead)
2729         private const uint LOCALE_ICENTURY = 0x00000024;   // century format specifier (short date, LOCALE_SSHORTDATE is preferred)
2730         private const uint LOCALE_ITLZERO = 0x00000025;   // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead)
2731         private const uint LOCALE_IDAYLZERO = 0x00000026;   // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred)
2732         private const uint LOCALE_IMONLZERO = 0x00000027;   // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred)
2733         private const uint LOCALE_S1159 = 0x00000028;   // AM designator
2734         private const uint LOCALE_S2359 = 0x00000029;   // PM designator
2735
2736         private const uint LOCALE_ICALENDARTYPE = 0x00001009;   // type of calendar specifier
2737         private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B;   // additional calendar types specifier
2738         private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C;   // first day of week specifier
2739         private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D;   // first week of year specifier
2740
2741         private const uint LOCALE_SDAYNAME1 = 0x0000002A;   // long name for Monday
2742         private const uint LOCALE_SDAYNAME2 = 0x0000002B;   // long name for Tuesday
2743         private const uint LOCALE_SDAYNAME3 = 0x0000002C;   // long name for Wednesday
2744         private const uint LOCALE_SDAYNAME4 = 0x0000002D;   // long name for Thursday
2745         private const uint LOCALE_SDAYNAME5 = 0x0000002E;   // long name for Friday
2746         private const uint LOCALE_SDAYNAME6 = 0x0000002F;   // long name for Saturday
2747         private const uint LOCALE_SDAYNAME7 = 0x00000030;   // long name for Sunday
2748         private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031;   // abbreviated name for Monday
2749         private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032;   // abbreviated name for Tuesday
2750         private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033;   // abbreviated name for Wednesday
2751         private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034;   // abbreviated name for Thursday
2752         private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035;   // abbreviated name for Friday
2753         private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036;   // abbreviated name for Saturday
2754         private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037;   // abbreviated name for Sunday
2755         private const uint LOCALE_SMONTHNAME1 = 0x00000038;   // long name for January
2756         private const uint LOCALE_SMONTHNAME2 = 0x00000039;   // long name for February
2757         private const uint LOCALE_SMONTHNAME3 = 0x0000003A;   // long name for March
2758         private const uint LOCALE_SMONTHNAME4 = 0x0000003B;   // long name for April
2759         private const uint LOCALE_SMONTHNAME5 = 0x0000003C;   // long name for May
2760         private const uint LOCALE_SMONTHNAME6 = 0x0000003D;   // long name for June
2761         private const uint LOCALE_SMONTHNAME7 = 0x0000003E;   // long name for July
2762         private const uint LOCALE_SMONTHNAME8 = 0x0000003F;   // long name for August
2763         private const uint LOCALE_SMONTHNAME9 = 0x00000040;   // long name for September
2764         private const uint LOCALE_SMONTHNAME10 = 0x00000041;   // long name for October
2765         private const uint LOCALE_SMONTHNAME11 = 0x00000042;   // long name for November
2766         private const uint LOCALE_SMONTHNAME12 = 0x00000043;   // long name for December
2767         private const uint LOCALE_SMONTHNAME13 = 0x0000100E;   // long name for 13th month (if exists)
2768         private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044;   // abbreviated name for January
2769         private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045;   // abbreviated name for February
2770         private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046;   // abbreviated name for March
2771         private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047;   // abbreviated name for April
2772         private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048;   // abbreviated name for May
2773         private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049;   // abbreviated name for June
2774         private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A;   // abbreviated name for July
2775         private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B;   // abbreviated name for August
2776         private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C;   // abbreviated name for September
2777         private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D;   // abbreviated name for October
2778         private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E;   // abbreviated name for November
2779         private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F;   // abbreviated name for December
2780         private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F;   // abbreviated name for 13th month (if exists)
2781
2782         private const uint LOCALE_SPOSITIVESIGN = 0x00000050;   // positive sign
2783         private const uint LOCALE_SNEGATIVESIGN = 0x00000051;   // negative sign
2784         private const uint LOCALE_IPOSSIGNPOSN = 0x00000052;   // positive sign position (derived from INEGCURR)
2785         private const uint LOCALE_INEGSIGNPOSN = 0x00000053;   // negative sign position (derived from INEGCURR)
2786         private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054;   // mon sym precedes pos amt (derived from ICURRENCY)
2787         private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055;   // mon sym sep by space from pos amt (derived from ICURRENCY)
2788         private const uint LOCALE_INEGSYMPRECEDES = 0x00000056;   // mon sym precedes neg amt (derived from INEGCURR)
2789         private const uint LOCALE_INEGSEPBYSPACE = 0x00000057;   // mon sym sep by space from neg amt (derived from INEGCURR)
2790
2791         private const uint LOCALE_FONTSIGNATURE = 0x00000058;   // font signature
2792         private const uint LOCALE_SISO639LANGNAME = 0x00000059;   // ISO abbreviated language name
2793         private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A;   // ISO abbreviated country name
2794
2795         private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012;   // default ebcdic code page
2796         private const uint LOCALE_IPAPERSIZE = 0x0000100A;   // 1 = letter, 5 = legal, 8 = a3, 9 = a4
2797         private const uint LOCALE_SENGCURRNAME = 0x00001007;   // english name of currency
2798         private const uint LOCALE_SNATIVECURRNAME = 0x00001008;   // native name of currency
2799         private const uint LOCALE_SYEARMONTH = 0x00001006;   // year month format string
2800         private const uint LOCALE_SSORTNAME = 0x00001013;   // sort name
2801         private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014;   // 0 = context, 1 = none, 2 = national
2802
2803         private const uint LOCALE_SNAME = 0x0000005c;   // locale name (with sort info) (ie: de-DE_phoneb)
2804         private const uint LOCALE_SDURATION = 0x0000005d;   // time duration format
2805         private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e;   // (windows only) keyboards to install
2806         private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060;   // Shortest day name for Monday
2807         private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061;   // Shortest day name for Tuesday
2808         private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062;   // Shortest day name for Wednesday
2809         private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063;   // Shortest day name for Thursday
2810         private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064;   // Shortest day name for Friday
2811         private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065;   // Shortest day name for Saturday
2812         private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066;   // Shortest day name for Sunday
2813         private const uint LOCALE_SISO639LANGNAME2 = 0x00000067;   // 3 character ISO abbreviated language name
2814         private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068;   // 3 character ISO country name
2815         private const uint LOCALE_SNAN = 0x00000069;   // Not a Number
2816         private const uint LOCALE_SPOSINFINITY = 0x0000006a;   // + Infinity
2817         private const uint LOCALE_SNEGINFINITY = 0x0000006b;   // - Infinity
2818         private const uint LOCALE_SSCRIPTS = 0x0000006c;   // Typical scripts in the locale
2819         private const uint LOCALE_SPARENT = 0x0000006d;   // Fallback name for resources
2820         private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e;   // Fallback name for within the console
2821         //        private const uint LOCALE_SLANGDISPLAYNAME       =LOCALE_SLOCALIZEDLANGUAGENAME;   // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead)
2822
2823         // Windows 7 LCTYPES
2824         private const uint LOCALE_IREADINGLAYOUT = 0x00000070;   // Returns one of the following 4 reading layout values:
2825         // 0 - Left to right (eg en-US)
2826         // 1 - Right to left (eg arabic locales)
2827         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2828         // 3 - Vertical top to bottom with columns proceeding to the right
2829         private const uint LOCALE_INEUTRAL = 0x00000071;   // Returns 0 for specific cultures, 1 for neutral cultures.
2830         private const uint LOCALE_INEGATIVEPERCENT = 0x00000074;   // Returns 0-11 for the negative percent format
2831         private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075;   // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT
2832         private const uint LOCALE_SPERCENT = 0x00000076;   // Returns the percent symbol
2833         private const uint LOCALE_SPERMILLE = 0x00000077;   // Returns the permille (U+2030) symbol
2834         private const uint LOCALE_SMONTHDAY = 0x00000078;   // Returns the preferred month/day format
2835         private const uint LOCALE_SSHORTTIME = 0x00000079;   // Returns the preferred short time format (ie: no seconds, just h:mm)
2836         private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a;   // Open type language tag, eg: "latn" or "dflt"
2837         private const uint LOCALE_SSORTLOCALE = 0x0000007b;   // Name of locale to use for sorting/collation/casing behavior.
2838
2839         // Time formats enumerations
2840         internal const uint TIME_NOSECONDS = 0x00000002;   // Don't use seconds (get short time format for enumtimeformats on win7+)
2841
2842         // Get our initial minimal culture data (name, parent, etc.)
2843         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2844         internal static extern bool nativeInitCultureData(CultureData cultureData);
2845
2846         // Grab the NumberFormatInfo data
2847         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2848         internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride);
2849
2850         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2851         private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride);
2852
2853         [SuppressUnmanagedCodeSecurityAttribute()]
2854         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
2855         internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray);
2856     }
2857 }