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