This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/
CultureData.cs
2564 lines (2318 loc) · 101 KB
/
CultureData.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
namespace System.Globalization
{
#if CORERT
using StringStringDictionary = LowLevelDictionary<string, string>;
using StringCultureDataDictionary = LowLevelDictionary<string, CultureData>;
using LcidToCultureNameDictionary = LowLevelDictionary<int, string>;
#else
using StringStringDictionary = Dictionary<string, string>;
using StringCultureDataDictionary = Dictionary<string, CultureData>;
using LcidToCultureNameDictionary = Dictionary<int, string>;
#endif
/// <summary>
/// List of culture data
/// Note the we cache overrides.
/// Note that localized names (resource names) aren't available from here.
/// </summary>
/// <remarks>
/// Our names are a tad confusing.
///
/// sWindowsName -- The name that windows thinks this culture is, ie:
/// en-US if you pass in en-US
/// de-DE_phoneb if you pass in de-DE_phoneb
/// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
/// fj if you pass in fj (neutral, post-Windows 7 machine)
///
/// sRealName -- The name you used to construct the culture, in pretty form
/// en-US if you pass in EN-us
/// en if you pass in en
/// de-DE_phoneb if you pass in de-DE_phoneb
///
/// sSpecificCulture -- The specific culture for this culture
/// en-US for en-US
/// en-US for en
/// de-DE_phoneb for alt sort
/// fj-FJ for fj (neutral)
///
/// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
/// en-US if you pass in en-US
/// en if you pass in en
/// de-DE if you pass in de-DE_phoneb
/// </remarks>
internal partial class CultureData
{
private const int LocaleNameMaxLength = 85;
private const int undef = -1;
// Override flag
private string _sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
private string _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
// Identity
private string _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
private string _sParent; // Parent name (which may be a custom locale/culture)
private string _sLocalizedDisplayName; // Localized pretty name for this locale
private string _sEnglishDisplayName; // English pretty name for this locale
private string _sNativeDisplayName; // Native pretty name for this locale
private string _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
// Language
private string _sISO639Language; // ISO 639 Language Name
private string _sISO639Language2; // ISO 639 Language Name
private string _sLocalizedLanguage; // Localized name for this language
private string _sEnglishLanguage; // English name for this language
private string _sNativeLanguage; // Native name of this language
private string _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
private string _sConsoleFallbackName; // The culture name for the console fallback UI culture
private int _iInputLanguageHandle=undef;// input language handle
// Region
private string _sRegionName; // (RegionInfo)
private string _sLocalizedCountry; // localized country name
private string _sEnglishCountry; // english country name (RegionInfo)
private string _sNativeCountry; // native country name
private string _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
private string _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
private int _iGeoId = undef; // GeoId
// Numbers
private string _sPositiveSign; // (user can override) positive sign
private string _sNegativeSign; // (user can override) negative sign
// (nfi populates these 5, don't have to be = undef)
private int _iDigits; // (user can override) number of fractional digits
private int _iNegativeNumber; // (user can override) negative number format
private int[] _waGrouping; // (user can override) grouping of digits
private string _sDecimalSeparator; // (user can override) decimal separator
private string _sThousandSeparator; // (user can override) thousands separator
private string _sNaN; // Not a Number
private string _sPositiveInfinity; // + Infinity
private string _sNegativeInfinity; // - Infinity
// Percent
private int _iNegativePercent = undef; // Negative Percent (0-3)
private int _iPositivePercent = undef; // Positive Percent (0-11)
private string _sPercent; // Percent (%) symbol
private string _sPerMille; // PerMille symbol
// Currency
private string _sCurrency; // (user can override) local monetary symbol
private string _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
private string _sEnglishCurrency; // English name for this currency
private string _sNativeCurrency; // Native name for this currency
// (nfi populates these 4, don't have to be = undef)
private int _iCurrencyDigits; // (user can override) # local monetary fractional digits
private int _iCurrency; // (user can override) positive currency format
private int _iNegativeCurrency; // (user can override) negative currency format
private int[] _waMonetaryGrouping; // (user can override) monetary grouping of digits
private string _sMonetaryDecimal; // (user can override) monetary decimal separator
private string _sMonetaryThousand; // (user can override) monetary thousands separator
// Misc
private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
private string _sListSeparator; // (user can override) list separator
// Time
private string _sAM1159; // (user can override) AM designator
private string _sPM2359; // (user can override) PM designator
private string _sTimeSeparator;
private volatile string[] _saLongTimes; // (user can override) time format
private volatile string[] _saShortTimes; // short time format
private volatile string[] _saDurationFormats; // time duration format
// Calendar specific data
private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
private int _iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
private volatile CalendarId[] _waCalendars; // all available calendar type(s). The first one is the default calendar
// Store for specific data about each calendar
private CalendarData[] _calendars; // Store for specific calendar data
// Text information
private int _iReadingLayout = undef; // Reading layout data
// 0 - Left to right (eg en-US)
// 1 - Right to left (eg arabic locales)
// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
// 3 - Vertical top to bottom with columns proceeding to the right
// CoreCLR depends on this even though its not exposed publicly.
private int _iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
private int _iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
private int _iDefaultMacCodePage = undef; // default macintosh code page
private int _iDefaultEbcdicCodePage = undef; // default EBCDIC code page
private int _iLanguage; // locale ID (0409) - NO sort information
private bool _bUseOverrides; // use user overrides?
private bool _bNeutral; // Flags for the culture (ie: neutral or not right now)
/// <summary>
/// Region Name to Culture Name mapping table
/// </summary>
/// <remarks>
/// Using a property so we avoid creating the dictionary until we need it
/// </remarks>
private static StringStringDictionary RegionNames
{
get
{
if (s_regionNames == null)
{
StringStringDictionary regionNames = new StringStringDictionary(211 /* prime */);
regionNames.Add("029", "en-029");
regionNames.Add("AE", "ar-AE");
regionNames.Add("AF", "prs-AF");
regionNames.Add("AL", "sq-AL");
regionNames.Add("AM", "hy-AM");
regionNames.Add("AR", "es-AR");
regionNames.Add("AT", "de-AT");
regionNames.Add("AU", "en-AU");
regionNames.Add("AZ", "az-Cyrl-AZ");
regionNames.Add("BA", "bs-Latn-BA");
regionNames.Add("BD", "bn-BD");
regionNames.Add("BE", "nl-BE");
regionNames.Add("BG", "bg-BG");
regionNames.Add("BH", "ar-BH");
regionNames.Add("BN", "ms-BN");
regionNames.Add("BO", "es-BO");
regionNames.Add("BR", "pt-BR");
regionNames.Add("BY", "be-BY");
regionNames.Add("BZ", "en-BZ");
regionNames.Add("CA", "en-CA");
regionNames.Add("CH", "it-CH");
regionNames.Add("CL", "es-CL");
regionNames.Add("CN", "zh-CN");
regionNames.Add("CO", "es-CO");
regionNames.Add("CR", "es-CR");
regionNames.Add("CS", "sr-Cyrl-CS");
regionNames.Add("CZ", "cs-CZ");
regionNames.Add("DE", "de-DE");
regionNames.Add("DK", "da-DK");
regionNames.Add("DO", "es-DO");
regionNames.Add("DZ", "ar-DZ");
regionNames.Add("EC", "es-EC");
regionNames.Add("EE", "et-EE");
regionNames.Add("EG", "ar-EG");
regionNames.Add("ES", "es-ES");
regionNames.Add("ET", "am-ET");
regionNames.Add("FI", "fi-FI");
regionNames.Add("FO", "fo-FO");
regionNames.Add("FR", "fr-FR");
regionNames.Add("GB", "en-GB");
regionNames.Add("GE", "ka-GE");
regionNames.Add("GL", "kl-GL");
regionNames.Add("GR", "el-GR");
regionNames.Add("GT", "es-GT");
regionNames.Add("HK", "zh-HK");
regionNames.Add("HN", "es-HN");
regionNames.Add("HR", "hr-HR");
regionNames.Add("HU", "hu-HU");
regionNames.Add("ID", "id-ID");
regionNames.Add("IE", "en-IE");
regionNames.Add("IL", "he-IL");
regionNames.Add("IN", "hi-IN");
regionNames.Add("IQ", "ar-IQ");
regionNames.Add("IR", "fa-IR");
regionNames.Add("IS", "is-IS");
regionNames.Add("IT", "it-IT");
regionNames.Add("IV", "");
regionNames.Add("JM", "en-JM");
regionNames.Add("JO", "ar-JO");
regionNames.Add("JP", "ja-JP");
regionNames.Add("KE", "sw-KE");
regionNames.Add("KG", "ky-KG");
regionNames.Add("KH", "km-KH");
regionNames.Add("KR", "ko-KR");
regionNames.Add("KW", "ar-KW");
regionNames.Add("KZ", "kk-KZ");
regionNames.Add("LA", "lo-LA");
regionNames.Add("LB", "ar-LB");
regionNames.Add("LI", "de-LI");
regionNames.Add("LK", "si-LK");
regionNames.Add("LT", "lt-LT");
regionNames.Add("LU", "lb-LU");
regionNames.Add("LV", "lv-LV");
regionNames.Add("LY", "ar-LY");
regionNames.Add("MA", "ar-MA");
regionNames.Add("MC", "fr-MC");
regionNames.Add("ME", "sr-Latn-ME");
regionNames.Add("MK", "mk-MK");
regionNames.Add("MN", "mn-MN");
regionNames.Add("MO", "zh-MO");
regionNames.Add("MT", "mt-MT");
regionNames.Add("MV", "dv-MV");
regionNames.Add("MX", "es-MX");
regionNames.Add("MY", "ms-MY");
regionNames.Add("NG", "ig-NG");
regionNames.Add("NI", "es-NI");
regionNames.Add("NL", "nl-NL");
regionNames.Add("NO", "nn-NO");
regionNames.Add("NP", "ne-NP");
regionNames.Add("NZ", "en-NZ");
regionNames.Add("OM", "ar-OM");
regionNames.Add("PA", "es-PA");
regionNames.Add("PE", "es-PE");
regionNames.Add("PH", "en-PH");
regionNames.Add("PK", "ur-PK");
regionNames.Add("PL", "pl-PL");
regionNames.Add("PR", "es-PR");
regionNames.Add("PT", "pt-PT");
regionNames.Add("PY", "es-PY");
regionNames.Add("QA", "ar-QA");
regionNames.Add("RO", "ro-RO");
regionNames.Add("RS", "sr-Latn-RS");
regionNames.Add("RU", "ru-RU");
regionNames.Add("RW", "rw-RW");
regionNames.Add("SA", "ar-SA");
regionNames.Add("SE", "sv-SE");
regionNames.Add("SG", "zh-SG");
regionNames.Add("SI", "sl-SI");
regionNames.Add("SK", "sk-SK");
regionNames.Add("SN", "wo-SN");
regionNames.Add("SV", "es-SV");
regionNames.Add("SY", "ar-SY");
regionNames.Add("TH", "th-TH");
regionNames.Add("TJ", "tg-Cyrl-TJ");
regionNames.Add("TM", "tk-TM");
regionNames.Add("TN", "ar-TN");
regionNames.Add("TR", "tr-TR");
regionNames.Add("TT", "en-TT");
regionNames.Add("TW", "zh-TW");
regionNames.Add("UA", "uk-UA");
regionNames.Add("US", "en-US");
regionNames.Add("UY", "es-UY");
regionNames.Add("UZ", "uz-Cyrl-UZ");
regionNames.Add("VE", "es-VE");
regionNames.Add("VN", "vi-VN");
regionNames.Add("YE", "ar-YE");
regionNames.Add("ZA", "af-ZA");
regionNames.Add("ZW", "en-ZW");
s_regionNames = regionNames;
}
return s_regionNames;
}
}
// Cache of regions we've already looked up
private static volatile StringCultureDataDictionary s_cachedRegions;
private static volatile StringStringDictionary s_regionNames;
internal static CultureData GetCultureDataForRegion(string cultureName, bool useUserOverride)
{
// First do a shortcut for Invariant
if (string.IsNullOrEmpty(cultureName))
{
return CultureData.Invariant;
}
// First check if GetCultureData() can find it (ie: its a real culture)
CultureData retVal = GetCultureData(cultureName, useUserOverride);
if (retVal != null && !retVal.IsNeutralCulture)
{
return retVal;
}
// Not a specific culture, perhaps it's region-only name
// (Remember this isn't a core clr path where that's not supported)
// If it was neutral remember that so that RegionInfo() can throw the right exception
CultureData neutral = retVal;
// Try the hash table next
string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
StringCultureDataDictionary tempHashTable = s_cachedRegions;
if (tempHashTable == null)
{
// No table yet, make a new one
tempHashTable = new StringCultureDataDictionary();
}
else
{
// Check the hash table
lock (s_lock)
{
tempHashTable.TryGetValue(hashName, out retVal);
}
if (retVal != null)
{
return retVal;
}
}
// Not found in the hash table, look it up the hard way
// If not a valid mapping from the registry we'll have to try the hard coded table
if (retVal == null || (retVal.IsNeutralCulture == true))
{
// Not a valid mapping, try the hard coded table
string name;
if (RegionNames.TryGetValue(cultureName, out name))
{
// Make sure we can get culture data for it
retVal = GetCultureData(name, useUserOverride);
}
}
// If not found in the hard coded table we'll have to find a culture that works for us
if (!GlobalizationMode.Invariant && (retVal == null || (retVal.IsNeutralCulture == true)))
{
retVal = GetCultureDataFromRegionName(cultureName);
}
// If we found one we can use, then cache it for next time
if (retVal != null && (retVal.IsNeutralCulture == false))
{
// first add it to the cache
lock (s_lock)
{
tempHashTable[hashName] = retVal;
}
// Copy the hashtable to the corresponding member variables. This will potentially overwrite
// new tables simultaneously created by a new thread, but maximizes thread safety.
s_cachedRegions = tempHashTable;
}
else
{
// Unable to find a matching culture/region, return null or neutral
// (regionInfo throws a more specific exception on neutrals)
retVal = neutral;
}
// Return the found culture to use, null, or the neutral culture.
return retVal;
}
// Clear our internal caches
internal static void ClearCachedData()
{
s_cachedCultures = null;
s_cachedRegions = null;
}
internal static CultureInfo[] GetCultures(CultureTypes types)
{
// Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
#pragma warning disable 618
// Validate flags
if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
CultureTypes.FrameworkCultures)) != 0)
{
throw new ArgumentOutOfRangeException(nameof(types),
SR.Format(SR.ArgumentOutOfRange_Range, CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
}
// We have deprecated CultureTypes.FrameworkCultures.
// When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
// We have deprecated CultureTypes.WindowsOnlyCultures.
// When this enum is used, we will return an empty array for this enum.
if ((types & CultureTypes.WindowsOnlyCultures) != 0)
{
// Remove the enum as it is an no-op.
types &= (~CultureTypes.WindowsOnlyCultures);
}
if (GlobalizationMode.Invariant)
{
// in invariant mode we always return invariant culture only from the enumeration
return new CultureInfo[] { new CultureInfo("") };
}
#pragma warning restore 618
return EnumCultures(types);
}
private static CultureData CreateCultureWithInvariantData()
{
// Make a new culturedata
CultureData invariant = new CultureData();
// Basics
// Note that we override the resources since this IS NOT supposed to change (by definition)
invariant._bUseOverrides = false;
invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
// Identity
invariant._sName = ""; // locale name (ie: en-us)
invariant._sParent = ""; // Parent name (which may be a custom locale/culture)
invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now)
invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
// Language
invariant._sISO639Language = "iv"; // ISO 639 Language Name
invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language
invariant._sEnglishLanguage = "Invariant Language"; // English name for this language
invariant._sNativeLanguage = "Invariant Language"; // Native name of this language
invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
invariant._iInputLanguageHandle = 0x07F; // input language handle
// Region
invariant._sRegionName = "IV"; // (RegionInfo)
invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only)
invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US
invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
invariant._iGeoId = 244; // GeoId (Windows Only)
// Numbers
invariant._sPositiveSign = "+"; // positive sign
invariant._sNegativeSign = "-"; // negative sign
invariant._iDigits = 2; // number of fractional digits
invariant._iNegativeNumber = 1; // negative number format
invariant._waGrouping = new int[] { 3 }; // grouping of digits
invariant._sDecimalSeparator = "."; // decimal separator
invariant._sThousandSeparator = ","; // thousands separator
invariant._sNaN = "NaN"; // Not a Number
invariant._sPositiveInfinity = "Infinity"; // + Infinity
invariant._sNegativeInfinity = "-Infinity"; // - Infinity
// Percent
invariant._iNegativePercent = 0; // Negative Percent (0-3)
invariant._iPositivePercent = 0; // Positive Percent (0-11)
invariant._sPercent = "%"; // Percent (%) symbol
invariant._sPerMille = "\x2030"; // PerMille symbol
// Currency
invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol
invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
invariant._iCurrencyDigits = 2; // # local monetary fractional digits
invariant._iCurrency = 0; // positive currency format
invariant._iNegativeCurrency = 0; // negative currency format
invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
invariant._sMonetaryDecimal = "."; // monetary decimal separator
invariant._sMonetaryThousand = ","; // monetary thousands separator
// Misc
invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
invariant._sListSeparator = ","; // list separator
// Time
invariant._sTimeSeparator = ":";
invariant._sAM1159 = "AM"; // AM designator
invariant._sPM2359 = "PM"; // PM designator
invariant._saLongTimes = new string[] { "HH:mm:ss" }; // time format
invariant._saShortTimes = new string[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
invariant._saDurationFormats = new string[] { "HH:mm:ss" }; // time duration format
// Calendar specific data
invariant._iFirstDayOfWeek = 0; // first day of week
invariant._iFirstWeekOfYear = 0; // first week of year
invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
// Store for specific data about each calendar
invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
invariant._calendars[0] = CalendarData.Invariant;
// Text information
invariant._iReadingLayout = 0;
// These are desktop only, not coreclr
invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information
invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
invariant._iDefaultMacCodePage = 10000; // default macintosh code page
invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
if (GlobalizationMode.Invariant)
{
invariant._sLocalizedDisplayName = invariant._sNativeDisplayName;
invariant._sLocalizedCountry = invariant._sNativeCountry;
}
return invariant;
}
/// <summary>
/// Build our invariant information
/// We need an invariant instance, which we build hard-coded
/// </summary>
internal static CultureData Invariant
{
get
{
if (s_Invariant == null)
{
// Remember it
s_Invariant = CreateCultureWithInvariantData();
}
return s_Invariant;
}
}
private volatile static CultureData s_Invariant;
// Cache of cultures we've already looked up
private static volatile StringCultureDataDictionary s_cachedCultures;
private static readonly object s_lock = new object();
internal static CultureData GetCultureData(string cultureName, bool useUserOverride)
{
// First do a shortcut for Invariant
if (string.IsNullOrEmpty(cultureName))
{
return CultureData.Invariant;
}
// Try the hash table first
string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
StringCultureDataDictionary tempHashTable = s_cachedCultures;
if (tempHashTable == null)
{
// No table yet, make a new one
tempHashTable = new StringCultureDataDictionary();
}
else
{
// Check the hash table
bool ret;
CultureData retVal;
lock (s_lock)
{
ret = tempHashTable.TryGetValue(hashName, out retVal);
}
if (ret && retVal != null)
{
return retVal;
}
}
// Not found in the hash table, need to see if we can build one that works for us
CultureData culture = CreateCultureData(cultureName, useUserOverride);
if (culture == null)
{
return null;
}
// Found one, add it to the cache
lock (s_lock)
{
tempHashTable[hashName] = culture;
}
// Copy the hashtable to the corresponding member variables. This will potentially overwrite
// new tables simultaneously created by a new thread, but maximizes thread safety.
s_cachedCultures = tempHashTable;
return culture;
}
private static string NormalizeCultureName(string name, out bool isNeutralName)
{
isNeutralName = true;
int i = 0;
if (name.Length > LocaleNameMaxLength)
{
// Theoretically we shouldn't hit this exception.
throw new ArgumentException(SR.Format(SR.Argument_InvalidId, nameof(name)));
}
Span<char> normalizedName = stackalloc char[name.Length];
bool changed = false;
while (i < name.Length && name[i] != '-' && name[i] != '_')
{
if (name[i] >= 'A' && name[i] <= 'Z')
{
// lowercase characters before '-'
normalizedName[i] = (char) (((int)name[i]) + 0x20);
changed = true;
}
else
{
normalizedName[i] = name[i];
}
i++;
}
if (i < name.Length)
{
// this is not perfect to detect the non neutral cultures but it is good enough when we are running in invariant mode
isNeutralName = false;
}
while (i < name.Length)
{
if (name[i] >= 'a' && name[i] <= 'z')
{
normalizedName[i] = (char) (((int)name[i]) - 0x20);
changed = true;
}
else
{
normalizedName[i] = name[i];
}
i++;
}
if (changed)
{
return new string(normalizedName);
}
return name;
}
private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
{
if (GlobalizationMode.Invariant)
{
if (cultureName.Length > LocaleNameMaxLength || !CultureInfo.VerifyCultureName(cultureName, false))
{
return null;
}
CultureData cd = CreateCultureWithInvariantData();
cd._bUseOverrides = useUserOverride;
cd._sName = NormalizeCultureName(cultureName, out cd._bNeutral);
cd._sRealName = cd._sName;
cd._sWindowsName = cd._sName;
cd._iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
return cd;
}
CultureData culture = new CultureData();
culture._bUseOverrides = useUserOverride;
culture._sRealName = cultureName;
// Ask native code if that one's real
if (!culture.InitCultureData() && !culture.InitCompatibilityCultureData())
{
return null;
}
return culture;
}
private bool InitCompatibilityCultureData()
{
// for compatibility handle the deprecated ids: zh-chs, zh-cht
string cultureName = _sRealName;
string fallbackCultureName;
string realCultureName;
switch (AnsiToLower(cultureName))
{
case "zh-chs":
fallbackCultureName = "zh-Hans";
realCultureName = "zh-CHS";
break;
case "zh-cht":
fallbackCultureName = "zh-Hant";
realCultureName = "zh-CHT";
break;
default:
return false;
}
_sRealName = fallbackCultureName;
if (!InitCultureData())
{
return false;
}
// fixup our data
_sName = realCultureName; // the name that goes back to the user
_sParent = fallbackCultureName;
return true;
}
/// We'd rather people use the named version since this doesn't allow custom locales
internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
{
string localeName = null;
CultureData retVal = null;
if (culture == CultureInfo.LOCALE_INVARIANT)
{
return Invariant;
}
if (GlobalizationMode.Invariant)
{
// LCID is not supported in the InvariantMode
throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
}
// Convert the lcid to a name, then use that
// Note that this will return neutral names (unlike Vista native API)
localeName = LCIDToLocaleName(culture);
if (!string.IsNullOrEmpty(localeName))
{
// Valid name, use it
retVal = GetCultureData(localeName, bUseUserOverride);
}
// If not successful, throw
if (retVal == null)
{
throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
}
// Return the one we found
return retVal;
}
/// <summary>
/// The real name used to construct the locale (ie: de-DE_phoneb)
/// </summary>
internal string CultureName
{
get
{
Debug.Assert(_sRealName != null, "[CultureData.CultureName] Expected _sRealName to be populated by already");
// since windows doesn't know about zh-CHS and zh-CHT,
// we leave sRealName == zh-Hanx but we still need to
// pretend that it was zh-CHX.
switch (_sName)
{
case "zh-CHS":
case "zh-CHT":
return _sName;
}
return _sRealName;
}
}
/// <summary>
/// Are overrides enabled?
/// </summary>
internal bool UseUserOverride => _bUseOverrides;
/// <summary>
/// locale name (ie: de-DE, NO sort information)
/// </summary>
internal string Name => _sName ?? string.Empty;
// Parent name (which may be a custom locale/culture)
internal string ParentName
{
get
{
if (_sParent == null)
{
// Ask using the real name, so that we get parents of neutrals
_sParent = GetLocaleInfo(_sRealName, LocaleStringData.ParentName);
}
return _sParent;
}
}
// Localized pretty name for this locale (ie: Inglis (estados Unitos))
internal string DisplayName
{
get
{
if (_sLocalizedDisplayName == null)
{
if (IsSupplementalCustomCulture)
{
if (IsNeutralCulture)
{
_sLocalizedDisplayName = NativeLanguageName;
}
else
{
_sLocalizedDisplayName = NativeName;
}
}
else
{
try
{
const string ZH_CHT = "zh-CHT";
const string ZH_CHS = "zh-CHS";
if (Name.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase))
{
_sLocalizedDisplayName = GetLanguageDisplayName("zh-Hant");
}
else if (Name.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase))
{
_sLocalizedDisplayName = GetLanguageDisplayName("zh-Hans");
}
else
{
_sLocalizedDisplayName = GetLanguageDisplayName(Name);
}
}
catch (Exception)
{
// do nothing
}
}
// If it hasn't been found (Windows 8 and up), fallback to the system
if (string.IsNullOrEmpty(_sLocalizedDisplayName))
{
// If its neutral use the language name
if (IsNeutralCulture)
{
_sLocalizedDisplayName = LocalizedLanguageName;
}
else
{
// Usually the UI culture shouldn't be different than what we got from WinRT except
// if DefaultThreadCurrentUICulture was set
CultureInfo ci;
if (CultureInfo.DefaultThreadCurrentUICulture != null &&
((ci = GetUserDefaultCulture()) != null) &&
!CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
{
_sLocalizedDisplayName = NativeName;
}
else
{
_sLocalizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName);
}
}
}
}
return _sLocalizedDisplayName;
}
}
/// <summary>
/// English pretty name for this locale (ie: English (United States))
/// </summary>
internal string EnglishName
{
get
{
if (_sEnglishDisplayName == null)
{
// If its neutral use the language name
if (IsNeutralCulture)
{
_sEnglishDisplayName = EnglishLanguageName;
// differentiate the legacy display names
switch (_sName)
{
case "zh-CHS":
case "zh-CHT":
_sEnglishDisplayName += " Legacy";
break;
}
}
else
{
_sEnglishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
// if it isn't found build one:
if (string.IsNullOrEmpty(_sEnglishDisplayName))
{
// Our existing names mostly look like:
// "English" + "United States" -> "English (United States)"
// "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
if (EnglishLanguageName[EnglishLanguageName.Length - 1] == ')')
{
// "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
_sEnglishDisplayName = string.Concat(
EnglishLanguageName.AsSpan(0, _sEnglishLanguage.Length - 1),
", ",
EnglishCountryName,
")");
}
else
{
// "English" + "United States" -> "English (United States)"
_sEnglishDisplayName = EnglishLanguageName + " (" + EnglishCountryName + ")";
}
}
}
}
return _sEnglishDisplayName;
}
}
/// <summary>
/// Native pretty name for this locale (ie: Deutsch (Deutschland))
/// </summary>
internal string NativeName
{
get
{
if (_sNativeDisplayName == null)
{
// If its neutral use the language name
if (IsNeutralCulture)
{
_sNativeDisplayName = NativeLanguageName;
// differentiate the legacy display names
switch (_sName)
{
case "zh-CHS":
_sNativeDisplayName += " \u65E7\u7248";
break;
case "zh-CHT":
_sNativeDisplayName += " \u820A\u7248";
break;
}
}
else
{
_sNativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
// if it isn't found build one:
if (string.IsNullOrEmpty(_sNativeDisplayName))
{
// These should primarily be "Deutsch (Deutschland)" type names
_sNativeDisplayName = NativeLanguageName + " (" + NativeCountryName + ")";
}
}
}
return _sNativeDisplayName;
}
}
/// <summary>
/// The culture name to be used in CultureInfo.CreateSpecificCulture()
/// </summary>
internal string SpecificCultureName
{
get