< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 1343
Coverable lines: 1343
Total lines: 3973
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 852
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .ctor()100%110%
File 1: GetCultureDataForRegion(...)0%32320%
File 1: ClearCachedData()100%110%
File 1: GetCultures(...)0%10100%
File 1: CreateCultureWithInvariantData()100%110%
File 1: GetCultureData(...)0%24240%
File 1: NormalizeCultureName(...)0%18180%
File 1: CreateCultureData(...)0%16160%
File 1: InitCompatibilityCultureData()0%440%
File 1: GetCultureData(...)0%14140%
File 1: GetLanguageDisplayNameCore(...)0%220%
File 1: DeriveShortTimesFromLong()0%220%
File 1: StripSecondsFromPattern(...)0%30300%
File 1: GetIndexOfNextTokenAfterSeconds(...)0%24240%
File 1: ShortDates(...)100%110%
File 1: LongDates(...)100%110%
File 1: YearMonths(...)100%110%
File 1: DayNames(...)100%110%
File 1: AbbreviatedDayNames(...)100%110%
File 1: SuperShortDayNames(...)100%110%
File 1: MonthNames(...)100%110%
File 1: GenitiveMonthNames(...)100%110%
File 1: AbbreviatedMonthNames(...)100%110%
File 1: AbbreviatedGenitiveMonthNames(...)100%110%
File 1: LeapYearMonthNames(...)100%110%
File 1: MonthDay(...)100%110%
File 1: CalendarName(...)100%110%
File 1: GetCalendar(...)0%12120%
File 1: EraNames(...)100%110%
File 1: AbbrevEraNames(...)100%110%
File 1: AbbreviatedEnglishEraNames(...)100%110%
File 1: DateSeparator(...)0%660%
File 1: UnescapeNlsString(...)0%18180%
File 1: GetTimeSeparator(...)100%110%
File 1: GetDateSeparator(...)100%110%
File 1: GetSeparator(...)0%10100%
File 1: IndexOfTimePart(...)0%16160%
File 1: IsCustomCultureId(...)0%220%
File 1: GetNativeDigits()0%22220%
File 1: GetNFIValues(...)0%10100%
File 1: AnsiToLower(...)100%110%
File 1: GetLocaleInfoCore(...)0%440%
File 1: GetLocaleInfoCoreUserOverride(...)0%440%
File 1: GetLocaleInfoCoreUserOverride(...)0%440%
File 1: GetLocaleInfoCore(...)0%440%
File 1: GetLocaleInfoCore(...)0%440%
File 1: GetLocaleInfoCoreUserOverride(...)0%440%
File 2: NormalizeCultureName(...)0%32320%
File 2: InitIcuCultureDataCore()0%16160%
File 2: GetLocaleName(...)0%220%
File 2: IcuGetLocaleInfo(...)100%110%
File 2: IcuGetLocaleInfo(...)0%440%
File 2: IcuGetLocaleInfo(...)0%440%
File 2: IcuGetLocaleInfo(...)0%440%
File 2: IcuGetTimeFormatString()100%110%
File 2: IcuGetTimeFormatString(...)0%220%
File 2: IcuGetLanguageDisplayName(...)100%110%
File 2: IcuIsEnsurePredefinedLocaleName(...)100%110%
File 2: ConvertIcuTimeFormatString(...)0%40400%
File 2: HandleQuoteLiteral(System.ReadOnlySpan`1<System.Char>,System.Int32&,System.Span`1<System.Char>,System.Int32&)0%440%
File 2: IcuLocaleNameToLCID(...)0%220%
File 2: IcuGetGeoId(...)0%220%
File 2: IcuGetDigitSubstitution(...)0%220%
File 2: IcuGetListSeparator(...)0%14140%
File 2: IcuGetThreeLetterWindowsLanguageName(...)0%220%
File 2: IcuEnumCultures(...)0%20200%
File 2: IcuGetConsoleFallbackName(...)100%110%
File 2: IsValidCultureName(...)0%48480%
File 3: GetLocaleInfoEx(...)0%220%
File 3: GetLocaleInfoExInt(...)100%110%
File 3: GetLocaleInfoEx(...)100%110%
File 3: NlsGetLocaleInfo(...)100%110%
File 3: NlsGetLocaleInfo(...)100%110%
File 3: NlsGetLocaleInfo(...)0%220%
File 3: NlsGetLocaleInfo(...)100%110%
File 3: NlsIsEnsurePredefinedLocaleName(...)100%110%
File 3: NlsGetTimeFormatString()100%110%
File 3: NlsGetFirstDayOfWeek()0%220%
File 3: NlsGetCultureDataFromRegionName(...)0%220%
File 3: NlsGetLanguageDisplayName(...)0%660%
File 3: NlsGetRegionDisplayName()0%220%
File 3: GetLocaleInfoFromLCType(...)0%440%
File 3: ReescapeWin32String(...)0%22220%
File 3: ReescapeWin32Strings(...)0%440%
File 3: ConvertWin32GroupString(...)0%12120%
File 3: ConvertFirstDayOfWeekMonToSun(...)0%220%
File 3: EnumSystemLocalesProc(...)0%440%
File 3: EnumAllSystemLocalesProc(...)100%110%
File 3: EnumTimeCallback(...)100%110%
File 3: nativeEnumTimeFormats(...)0%12120%
File 3: NlsLocaleNameToLCID(...)100%110%
File 3: NlsGetThreeLetterWindowsLanguageName(...)100%110%
File 3: NlsEnumCultures(...)0%12120%
File 3: NlsGetConsoleFallbackName(...)100%110%
File 4: InitCultureDataCore()0%18180%
File 4: InitUserOverride(...)0%220%
File 4: GetCurrentRegionData()0%660%
File 4: LCIDToLocaleName(...)0%440%
File 4: GetTimeFormatsCore(...)0%10100%
File 4: GetAnsiCodePage(...)100%110%
File 4: GetOemCodePage(...)100%110%
File 4: GetMacCodePage(...)100%110%
File 4: GetEbcdicCodePage(...)100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Text;
 7using System.Threading;
 8
 9namespace System.Globalization
 10{
 11    /// <summary>
 12    /// List of culture data
 13    /// Note the we cache overrides.
 14    /// Note that localized names (resource names) aren't available from here.
 15    /// </summary>
 16    /// <remarks>
 17    /// Our names are a tad confusing.
 18    ///
 19    /// sWindowsName -- The name that windows thinks this culture is, ie:
 20    ///                            en-US if you pass in en-US
 21    ///                            de-DE_phoneb if you pass in de-DE_phoneb
 22    ///                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
 23    ///                            fj if you pass in fj (neutral, post-Windows 7 machine)
 24    ///
 25    /// sRealName -- The name you used to construct the culture, in pretty form
 26    ///                       en-US if you pass in EN-us
 27    ///                       en if you pass in en
 28    ///                       de-DE_phoneb if you pass in de-DE_phoneb
 29    ///
 30    /// sSpecificCulture -- The specific culture for this culture
 31    ///                             en-US for en-US
 32    ///                             en-US for en
 33    ///                             de-DE_phoneb for alt sort
 34    ///                             fj-FJ for fj (neutral)
 35    ///
 36    /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
 37    ///                en-US if you pass in en-US
 38    ///                en if you pass in en
 39    ///                de-DE if you pass in de-DE_phoneb
 40    /// </remarks>
 41    internal sealed partial class CultureData
 42    {
 43        private const int LocaleNameMaxLength = 85;
 44        private const int undef = -1;
 45
 46        // Override flag
 47        private string _sRealName = null!; // Name you passed in (ie: en-US, en, or de-DE_phoneb). Initialized by helper
 48        private string? _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passe
 49
 50        // Identity
 51        private string? _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
 52        private string? _sParent; // Parent name (which may be a custom locale/culture)
 53        private string? _sEnglishDisplayName; // English pretty name for this locale
 54        private string? _sNativeDisplayName; // Native pretty name for this locale
 55        private string? _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US 
 56
 57        // Language
 58        private string? _sISO639Language; // ISO 639 Language Name
 59        private string? _sISO639Language2; // ISO 639 Language Name
 60        private string? _sEnglishLanguage; // English name for this language
 61        private string? _sNativeLanguage; // Native name of this language
 62        private string? _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
 63        private string? _sConsoleFallbackName; // The culture name for the console fallback UI culture
 064        private int _iInputLanguageHandle = undef; // input language handle
 65
 66        // Region
 67        private string? _sRegionName; // (RegionInfo)
 68        private string? _sLocalizedCountry; // localized country name
 69        private string? _sEnglishCountry; // english country name (RegionInfo)
 70        private string? _sNativeCountry; // native country name
 71        private string? _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
 72        private string? _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
 073        private int _iGeoId = undef; // GeoId
 74
 75        // Numbers
 76        private string? _sPositiveSign; // (user can override) positive sign
 77        private string? _sNegativeSign; // (user can override) negative sign
 78        // (nfi populates these 5, don't have to be = undef)
 79        private int _iDigits; // (user can override) number of fractional digits
 80        private int _iNegativeNumber; // (user can override) negative number format
 81        private int[]? _waGrouping; // (user can override) grouping of digits
 82        private string? _sDecimalSeparator; // (user can override) decimal separator
 83        private string? _sThousandSeparator; // (user can override) thousands separator
 84        private string? _sNaN; // Not a Number
 85        private string? _sPositiveInfinity; // + Infinity
 86        private string? _sNegativeInfinity; // - Infinity
 87
 88        // Percent
 089        private int _iNegativePercent = undef; // Negative Percent (0-3)
 090        private int _iPositivePercent = undef; // Positive Percent (0-11)
 91        private string? _sPercent; // Percent (%) symbol
 92        private string? _sPerMille; // PerMille symbol
 93
 94        // Currency
 95        private string? _sCurrency; // (user can override) local monetary symbol
 96        private string? _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
 97        private string? _sEnglishCurrency; // English name for this currency
 98        private string? _sNativeCurrency; // Native name for this currency
 99        // (nfi populates these 4, don't have to be = undef)
 100        private int _iCurrencyDigits; // (user can override) # local monetary fractional digits
 101        private int _iCurrency; // (user can override) positive currency format
 102        private int _iNegativeCurrency; // (user can override) negative currency format
 103        private int[]? _waMonetaryGrouping; // (user can override) monetary grouping of digits
 104        private string? _sMonetaryDecimal; // (user can override) monetary decimal separator
 105        private string? _sMonetaryThousand; // (user can override) monetary thousands separator
 106
 107        // Misc
 0108        private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
 109        private string? _sListSeparator; // (user can override) list separator
 110
 111        // Time
 112        private string? _sAM1159; // (user can override) AM designator
 113        private string? _sPM2359; // (user can override) PM designator
 114        private string? _sTimeSeparator;
 115        private string[]? _saLongTimes; // (user can override) time format
 116        private string[]? _saShortTimes; // (user can override) short time format
 117
 118        // Calendar specific data
 0119        private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
 0120        private int _iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
 121        private CalendarId[]? _waCalendars; // all available calendar type(s).  The first one is the default calendar
 122
 123        // Store for specific data about each calendar
 124        private CalendarData?[]? _calendars; // Store for specific calendar data
 125
 126        // Text information
 0127        private int _iReadingLayout = undef; // Reading layout data
 128        // 0 - Left to right (eg en-US)
 129        // 1 - Right to left (eg arabic locales)
 130        // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
 131        // 3 - Vertical top to bottom with columns proceeding to the right
 132
 133        // CoreCLR depends on this even though its not exposed publicly.
 134
 0135        private int _iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
 0136        private int _iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
 0137        private int _iDefaultMacCodePage = undef; // default macintosh code page
 0138        private int _iDefaultEbcdicCodePage = undef; // default EBCDIC code page
 139
 140        private int _iLanguage; // locale ID (0409) - NO sort information
 141        private bool _bUseOverrides; // use user overrides? this depends on user setting and if is user default locale.
 142        private bool _bUseOverridesUserSetting; // the setting the user requested for.
 143        private bool _bNeutral; // Flags for the culture (ie: neutral or not right now)
 144
 145        /// <summary>
 146        /// Region Name to Culture Name mapping table
 147        /// </summary>
 148        /// <remarks>
 149        /// Using a property so we avoid creating the dictionary until we need it
 150        /// </remarks>
 151        private static Dictionary<string, string> RegionNames =>
 0152            s_regionNames ??=
 0153            new Dictionary<string, string>(255, StringComparer.OrdinalIgnoreCase)
 0154            {
 0155                { "001", "en-001" },
 0156                { "029", "en-029" },
 0157                { "150", "en-150" },
 0158                { "419", "es-419" },
 0159                { "AD", "ca-AD" },
 0160                { "AE", "ar-AE" },
 0161                { "AF", "prs-AF" },
 0162                { "AG", "en-AG" },
 0163                { "AI", "en-AI" },
 0164                { "AL", "sq-AL" },
 0165                { "AM", "hy-AM" },
 0166                { "AO", "pt-AO" },
 0167                { "AQ", "en-AQ" },
 0168                { "AR", "es-AR" },
 0169                { "AS", "en-AS" },
 0170                { "AT", "de-AT" },
 0171                { "AU", "en-AU" },
 0172                { "AW", "nl-AW" },
 0173                { "AX", "sv-AX" },
 0174                { "AZ", "az-Cyrl-AZ" },
 0175                { "BA", "bs-Latn-BA" },
 0176                { "BB", "en-BB" },
 0177                { "BD", "bn-BD" },
 0178                { "BE", "nl-BE" },
 0179                { "BF", "fr-BF" },
 0180                { "BG", "bg-BG" },
 0181                { "BH", "ar-BH" },
 0182                { "BI", "rn-BI" },
 0183                { "BJ", "fr-BJ" },
 0184                { "BL", "fr-BL" },
 0185                { "BM", "en-BM" },
 0186                { "BN", "ms-BN" },
 0187                { "BO", "es-BO" },
 0188                { "BQ", "nl-BQ" },
 0189                { "BR", "pt-BR" },
 0190                { "BS", "en-BS" },
 0191                { "BT", "dz-BT" },
 0192                { "BV", "nb-BV" },
 0193                { "BW", "en-BW" },
 0194                { "BY", "be-BY" },
 0195                { "BZ", "en-BZ" },
 0196                { "CA", "en-CA" },
 0197                { "CC", "en-CC" },
 0198                { "CD", "fr-CD" },
 0199                { "CF", "sg-CF" },
 0200                { "CG", "fr-CG" },
 0201                { "CH", "it-CH" },
 0202                { "CI", "fr-CI" },
 0203                { "CK", "en-CK" },
 0204                { "CL", "es-CL" },
 0205                { "CM", "fr-CM" },
 0206                { "CN", "zh-CN" },
 0207                { "CO", "es-CO" },
 0208                { "CR", "es-CR" },
 0209                { "CS", "sr-Cyrl-CS" },
 0210                { "CU", "es-CU" },
 0211                { "CV", "pt-CV" },
 0212                { "CW", "nl-CW" },
 0213                { "CX", "en-CX" },
 0214                { "CY", "el-CY" },
 0215                { "CZ", "cs-CZ" },
 0216                { "DE", "de-DE" },
 0217                { "DJ", "fr-DJ" },
 0218                { "DK", "da-DK" },
 0219                { "DM", "en-DM" },
 0220                { "DO", "es-DO" },
 0221                { "DZ", "ar-DZ" },
 0222                { "EC", "es-EC" },
 0223                { "EE", "et-EE" },
 0224                { "EG", "ar-EG" },
 0225                { "ER", "tig-ER" },
 0226                { "ES", "es-ES" },
 0227                { "ET", "am-ET" },
 0228                { "FI", "fi-FI" },
 0229                { "FJ", "en-FJ" },
 0230                { "FK", "en-FK" },
 0231                { "FM", "en-FM" },
 0232                { "FO", "fo-FO" },
 0233                { "FR", "fr-FR" },
 0234                { "GA", "fr-GA" },
 0235                { "GB", "en-GB" },
 0236                { "GD", "en-GD" },
 0237                { "GE", "ka-GE" },
 0238                { "GF", "fr-GF" },
 0239                { "GG", "en-GG" },
 0240                { "GH", "en-GH" },
 0241                { "GI", "en-GI" },
 0242                { "GL", "kl-GL" },
 0243                { "GM", "en-GM" },
 0244                { "GN", "fr-GN" },
 0245                { "GP", "fr-GP" },
 0246                { "GQ", "es-GQ" },
 0247                { "GR", "el-GR" },
 0248                { "GS", "en-GS" },
 0249                { "GT", "es-GT" },
 0250                { "GU", "en-GU" },
 0251                { "GW", "pt-GW" },
 0252                { "GY", "en-GY" },
 0253                { "HK", "zh-HK" },
 0254                { "HM", "en-HM" },
 0255                { "HN", "es-HN" },
 0256                { "HR", "hr-HR" },
 0257                { "HT", "fr-HT" },
 0258                { "HU", "hu-HU" },
 0259                { "ID", "id-ID" },
 0260                { "IE", "en-IE" },
 0261                { "IL", "he-IL" },
 0262                { "IM", "gv-IM" },
 0263                { "IN", "hi-IN" },
 0264                { "IO", "en-IO" },
 0265                { "IQ", "ar-IQ" },
 0266                { "IR", "fa-IR" },
 0267                { "IS", "is-IS" },
 0268                { "IT", "it-IT" },
 0269                { "IV", "" },
 0270                { "JE", "en-JE" },
 0271                { "JM", "en-JM" },
 0272                { "JO", "ar-JO" },
 0273                { "JP", "ja-JP" },
 0274                { "KE", "sw-KE" },
 0275                { "KG", "ky-KG" },
 0276                { "KH", "km-KH" },
 0277                { "KI", "en-KI" },
 0278                { "KM", "ar-KM" },
 0279                { "KN", "en-KN" },
 0280                { "KP", "ko-KP" },
 0281                { "KR", "ko-KR" },
 0282                { "KW", "ar-KW" },
 0283                { "KY", "en-KY" },
 0284                { "KZ", "kk-KZ" },
 0285                { "LA", "lo-LA" },
 0286                { "LB", "ar-LB" },
 0287                { "LC", "en-LC" },
 0288                { "LI", "de-LI" },
 0289                { "LK", "si-LK" },
 0290                { "LR", "en-LR" },
 0291                { "LS", "st-LS" },
 0292                { "LT", "lt-LT" },
 0293                { "LU", "lb-LU" },
 0294                { "LV", "lv-LV" },
 0295                { "LY", "ar-LY" },
 0296                { "MA", "ar-MA" },
 0297                { "MC", "fr-MC" },
 0298                { "MD", "ro-MD" },
 0299                { "ME", "sr-Latn-ME" },
 0300                { "MF", "fr-MF" },
 0301                { "MG", "mg-MG" },
 0302                { "MH", "en-MH" },
 0303                { "MK", "mk-MK" },
 0304                { "ML", "fr-ML" },
 0305                { "MM", "my-MM" },
 0306                { "MN", "mn-MN" },
 0307                { "MO", "zh-MO" },
 0308                { "MP", "en-MP" },
 0309                { "MQ", "fr-MQ" },
 0310                { "MR", "ar-MR" },
 0311                { "MS", "en-MS" },
 0312                { "MT", "mt-MT" },
 0313                { "MU", "en-MU" },
 0314                { "MV", "dv-MV" },
 0315                { "MW", "en-MW" },
 0316                { "MX", "es-MX" },
 0317                { "MY", "ms-MY" },
 0318                { "MZ", "pt-MZ" },
 0319                { "NA", "en-NA" },
 0320                { "NC", "fr-NC" },
 0321                { "NE", "fr-NE" },
 0322                { "NF", "en-NF" },
 0323                { "NG", "ig-NG" },
 0324                { "NI", "es-NI" },
 0325                { "NL", "nl-NL" },
 0326                { "NO", "nn-NO" },
 0327                { "NP", "ne-NP" },
 0328                { "NR", "en-NR" },
 0329                { "NU", "en-NU" },
 0330                { "NZ", "en-NZ" },
 0331                { "OM", "ar-OM" },
 0332                { "PA", "es-PA" },
 0333                { "PE", "es-PE" },
 0334                { "PF", "fr-PF" },
 0335                { "PG", "en-PG" },
 0336                { "PH", "en-PH" },
 0337                { "PK", "ur-PK" },
 0338                { "PL", "pl-PL" },
 0339                { "PM", "fr-PM" },
 0340                { "PN", "en-PN" },
 0341                { "PR", "es-PR" },
 0342                { "PS", "ar-PS" },
 0343                { "PT", "pt-PT" },
 0344                { "PW", "en-PW" },
 0345                { "PY", "es-PY" },
 0346                { "QA", "ar-QA" },
 0347                { "RE", "fr-RE" },
 0348                { "RO", "ro-RO" },
 0349                { "RS", "sr-Latn-RS" },
 0350                { "RU", "ru-RU" },
 0351                { "RW", "rw-RW" },
 0352                { "SA", "ar-SA" },
 0353                { "SB", "en-SB" },
 0354                { "SC", "fr-SC" },
 0355                { "SD", "ar-SD" },
 0356                { "SE", "sv-SE" },
 0357                { "SG", "zh-SG" },
 0358                { "SH", "en-SH" },
 0359                { "SI", "sl-SI" },
 0360                { "SJ", "nb-SJ" },
 0361                { "SK", "sk-SK" },
 0362                { "SL", "en-SL" },
 0363                { "SM", "it-SM" },
 0364                { "SN", "wo-SN" },
 0365                { "SO", "so-SO" },
 0366                { "SR", "nl-SR" },
 0367                { "SS", "en-SS" },
 0368                { "ST", "pt-ST" },
 0369                { "SV", "es-SV" },
 0370                { "SX", "nl-SX" },
 0371                { "SY", "ar-SY" },
 0372                { "SZ", "ss-SZ" },
 0373                { "TC", "en-TC" },
 0374                { "TD", "fr-TD" },
 0375                { "TF", "fr-TF" },
 0376                { "TG", "fr-TG" },
 0377                { "TH", "th-TH" },
 0378                { "TJ", "tg-Cyrl-TJ" },
 0379                { "TK", "en-TK" },
 0380                { "TL", "pt-TL" },
 0381                { "TM", "tk-TM" },
 0382                { "TN", "ar-TN" },
 0383                { "TO", "to-TO" },
 0384                { "TR", "tr-TR" },
 0385                { "TT", "en-TT" },
 0386                { "TV", "en-TV" },
 0387                { "TW", "zh-TW" },
 0388                { "TZ", "sw-TZ" },
 0389                { "UA", "uk-UA" },
 0390                { "UG", "sw-UG" },
 0391                { "UM", "en-UM" },
 0392                { "US", "en-US" },
 0393                { "UY", "es-UY" },
 0394                { "UZ", "uz-Cyrl-UZ" },
 0395                { "VA", "it-VA" },
 0396                { "VC", "en-VC" },
 0397                { "VE", "es-VE" },
 0398                { "VG", "en-VG" },
 0399                { "VI", "en-VI" },
 0400                { "VN", "vi-VN" },
 0401                { "VU", "fr-VU" },
 0402                { "WF", "fr-WF" },
 0403                { "WS", "en-WS" },
 0404                { "XK", "sq-XK" },
 0405                { "YE", "ar-YE" },
 0406                { "YT", "fr-YT" },
 0407                { "ZA", "af-ZA" },
 0408                { "ZM", "en-ZM" },
 0409                { "ZW", "en-ZW" }
 0410            };
 411
 412        // Cache of regions we've already looked up
 413        private static Dictionary<string, CultureData>? s_cachedRegions;
 414        private static Dictionary<string, string>? s_regionNames;
 415
 416        /// <summary>
 417        /// The culture name to use to interop with the underlying native globalization libraries like ICU or Windows NL
 418        /// For example, we can have the name de_DE@collation=phonebook when using ICU for the German culture de-DE with
 419        /// </summary>
 0420        internal string? InteropName => _sWindowsName;
 421
 422        internal static CultureData? GetCultureDataForRegion(string? cultureName, bool useUserOverride)
 423        {
 424            // First do a shortcut for Invariant
 0425            if (string.IsNullOrEmpty(cultureName))
 426            {
 0427                return Invariant;
 428            }
 429
 430            // First check if GetCultureData() can find it (ie: its a real culture)
 0431            CultureData? retVal = GetCultureData(cultureName, useUserOverride);
 0432            if (retVal != null && !retVal.IsNeutralCulture)
 433            {
 0434                return retVal;
 435            }
 436
 437            // If it was neutral remember that so that RegionInfo() can throw the right exception
 0438            CultureData? neutral = retVal;
 439
 440            // Try the hash table next
 0441            string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
 0442            Dictionary<string, CultureData>? tempHashTable = s_cachedRegions;
 443
 0444            if (tempHashTable == null)
 445            {
 446                // No table yet, make a new one
 0447                var newTable = new Dictionary<string, CultureData>();
 0448                tempHashTable = Interlocked.CompareExchange(ref s_cachedRegions, newTable, null) ?? newTable;
 449            }
 450            else
 451            {
 452                // Check the hash table
 0453                lock (tempHashTable)
 454                {
 0455                    tempHashTable.TryGetValue(hashName, out retVal);
 0456                }
 0457                if (retVal != null)
 458                {
 0459                    return retVal;
 460                }
 461            }
 462
 463            // Not found in the hash table, look it up the hard way
 464
 465            // If not a valid mapping from the registry we'll have to try the hard coded table
 0466            if (retVal == null || retVal.IsNeutralCulture)
 467            {
 468                // Not a valid mapping, try the hard coded table
 0469                if (RegionNames.TryGetValue(cultureName, out string? name))
 470                {
 471                    // Make sure we can get culture data for it
 0472                    retVal = GetCultureData(name, useUserOverride);
 473                }
 474            }
 475
 476            // If not found in the hard coded table we'll have to find a culture that works for us
 0477            if (!GlobalizationMode.Invariant && (retVal == null || retVal.IsNeutralCulture))
 478            {
 0479                retVal = GlobalizationMode.UseNls ? NlsGetCultureDataFromRegionName(cultureName) : IcuGetCultureDataFrom
 480            }
 481
 482            // If we found one we can use, then cache it for next time
 0483            if (retVal != null && !retVal.IsNeutralCulture)
 484            {
 485                // first add it to the cache
 0486                lock (tempHashTable)
 487                {
 0488                    tempHashTable[hashName] = retVal;
 0489                }
 490            }
 491            else
 492            {
 493                // Unable to find a matching culture/region, return null or neutral
 494                // (regionInfo throws a more specific exception on neutrals)
 0495                retVal = neutral;
 496            }
 497
 498            // Return the found culture to use, null, or the neutral culture.
 0499            return retVal;
 500        }
 501
 502        // Clear our internal caches
 503        internal static void ClearCachedData()
 504        {
 0505            s_cachedCultures = null;
 0506            s_cachedRegions = null;
 0507        }
 508
 509        internal static CultureInfo[] GetCultures(CultureTypes types)
 510        {
 511            // Disable  warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
 512#pragma warning disable 618
 513            // Validate flags
 0514            if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
 0515                                                        CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCul
 0516                                                        CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultu
 0517                                                        CultureTypes.FrameworkCultures)) != 0)
 518            {
 0519                throw new ArgumentOutOfRangeException(nameof(types),
 0520                              SR.Format(SR.ArgumentOutOfRange_Range, CultureTypes.NeutralCultures, CultureTypes.Framewor
 521            }
 522
 523            // We have deprecated CultureTypes.FrameworkCultures.
 524            // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
 525
 526            // We have deprecated CultureTypes.WindowsOnlyCultures.
 527            // When this enum is used, we will return an empty array for this enum.
 0528            if ((types & CultureTypes.WindowsOnlyCultures) != 0)
 529            {
 530                // Remove the enum as it is an no-op.
 0531                types &= (~CultureTypes.WindowsOnlyCultures);
 532            }
 533
 0534            if (GlobalizationMode.Invariant)
 535            {
 536                // in invariant mode we always return invariant culture only from the enumeration
 0537                return [new CultureInfo("")];
 538            }
 539
 540#pragma warning restore 618
 0541            return GlobalizationMode.UseNls ? NlsEnumCultures(types) : IcuEnumCultures(types);
 542        }
 543
 544        private static CultureData CreateCultureWithInvariantData()
 545        {
 546            // Make a new culturedata
 0547            CultureData invariant = new CultureData();
 548
 549            // Basics
 550            // Note that we override the resources since this IS NOT supposed to change (by definition)
 0551            invariant._bUseOverrides = false;
 0552            invariant._bUseOverridesUserSetting = false;
 0553            invariant._sRealName = "";                     // Name you passed in (ie: en-US, en, or de-DE_phoneb)
 0554            invariant._sWindowsName = "";                     // Name OS thinks the object is (ie: de-DE_phoneb, or en-U
 555
 556            // Identity
 0557            invariant._sName = "";                     // locale name (ie: en-us)
 0558            invariant._sParent = "";                     // Parent name (which may be a custom locale/culture)
 0559            invariant._bNeutral = false;                   // Flags for the culture (ie: neutral or not right now)
 0560            invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this l
 0561            invariant._sNativeDisplayName = "Invariant Language (Invariant Country)";  // Native pretty name for this lo
 0562            invariant._sSpecificCulture = "";                     // The culture name to be used in CultureInfo.CreateSp
 563
 564            // Language
 0565            invariant._sISO639Language = "iv";                   // ISO 639 Language Name
 0566            invariant._sISO639Language2 = "ivl";                  // 3 char ISO 639 lang name 2
 0567            invariant._sEnglishLanguage = "Invariant Language";   // English name for this language
 0568            invariant._sNativeLanguage = "Invariant Language";   // Native name of this language
 0569            invariant._sAbbrevLang = "IVL";                  // abbreviated language name (Windows Language Name)
 0570            invariant._sConsoleFallbackName = "";            // The culture name for the console fallback UI culture
 0571            invariant._iInputLanguageHandle = 0x07F;         // input language handle
 572
 573            // Region
 0574            invariant._sRegionName = "IV";                    // (RegionInfo)
 0575            invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
 0576            invariant._sNativeCountry = "Invariant Country";  // native country name (Windows Only)
 0577            invariant._sISO3166CountryName = "IV";            // (RegionInfo), ie: US
 0578            invariant._sISO3166CountryName2 = "ivc";          // 3 char ISO 3166 country name 2 2(RegionInfo)
 0579            invariant._iGeoId = 244;                          // GeoId (Windows Only)
 580
 581            // Numbers
 0582            invariant._sPositiveSign = "+";                    // positive sign
 0583            invariant._sNegativeSign = "-";                    // negative sign
 0584            invariant._iDigits = 2;                      // number of fractional digits
 0585            invariant._iNegativeNumber = 1;                      // negative number format
 0586            invariant._waGrouping = [3];          // grouping of digits
 0587            invariant._sDecimalSeparator = ".";                    // decimal separator
 0588            invariant._sThousandSeparator = ",";                    // thousands separator
 0589            invariant._sNaN = "NaN";                  // Not a Number
 0590            invariant._sPositiveInfinity = "Infinity";             // + Infinity
 0591            invariant._sNegativeInfinity = "-Infinity";            // - Infinity
 592
 593            // Percent
 0594            invariant._iNegativePercent = 0;                      // Negative Percent (0-3)
 0595            invariant._iPositivePercent = 0;                      // Positive Percent (0-11)
 0596            invariant._sPercent = "%";                    // Percent (%) symbol
 0597            invariant._sPerMille = "\x2030";               // PerMille symbol
 598
 599            // Currency
 0600            invariant._sCurrency = "\x00a4";                // local monetary symbol: for international monetary symbol
 0601            invariant._sIntlMonetarySymbol = "XDR";                  // international monetary symbol (RegionInfo)
 0602            invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only
 0603            invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
 0604            invariant._iCurrencyDigits = 2;                      // # local monetary fractional digits
 0605            invariant._iCurrency = 0;                      // positive currency format
 0606            invariant._iNegativeCurrency = 0;                      // negative currency format
 0607            invariant._waMonetaryGrouping = [3];          // monetary grouping of digits
 0608            invariant._sMonetaryDecimal = ".";                    // monetary decimal separator
 0609            invariant._sMonetaryThousand = ",";                    // monetary thousands separator
 610
 611            // Misc
 0612            invariant._iMeasure = 0;                      // system of measurement 0=metric, 1=US (RegionInfo)
 0613            invariant._sListSeparator = ",";                    // list separator
 614
 615            // Time
 0616            invariant._sTimeSeparator = ":";
 0617            invariant._sAM1159 = "AM";                   // AM designator
 0618            invariant._sPM2359 = "PM";                   // PM designator
 0619            invariant._saLongTimes = ["HH:mm:ss"];                             // time format
 0620            invariant._saShortTimes = ["HH:mm", "hh:mm tt", "H:mm", "h:mm tt"]; // short time format
 621
 622            // Calendar specific data
 0623            invariant._iFirstDayOfWeek = 0;                      // first day of week
 0624            invariant._iFirstWeekOfYear = 0;                      // first week of year
 625
 626            // Calendar information is expensive size-wise, especially for AOT.
 627            // CalendarData.Invariant is special cased in _waCalendars and _calendars
 628            // accessors and gets initialized lazily.
 629            // invariant._waCalendars
 630            // invariant._calendars
 631
 632            // Text information
 0633            invariant._iReadingLayout = 0;
 634
 635            // These are .NET Framework only, not coreclr
 636
 0637            invariant._iLanguage = CultureInfo.LOCALE_INVARIANT;   // locale ID (0409) - NO sort information
 0638            invariant._iDefaultAnsiCodePage = 1252;         // default ansi code page ID (ACP)
 0639            invariant._iDefaultOemCodePage = 437;           // default oem code page ID (OCP or OEM)
 0640            invariant._iDefaultMacCodePage = 10000;         // default macintosh code page
 0641            invariant._iDefaultEbcdicCodePage = 037;        // default EBCDIC code page
 642
 643            // _sLocalizedCountry is intentionally left null here. It is populated lazily the first
 644            // time the LocalizedCountryName property is accessed. We avoid reading GlobalizationMode
 645            // here because doing so triggers ICU load in GlobalizationMode.Settings.cctor. During
 646            // runtime startup that load can fail and crash the process on some platforms such as Android.
 0647            return invariant;
 648        }
 649
 650        /// <summary>
 651        /// Build our invariant information
 652        /// We need an invariant instance, which we build hard-coded
 653        /// </summary>
 0654        internal static CultureData Invariant => field ??= CreateCultureWithInvariantData();
 655
 656        // Cache of cultures we've already looked up
 657        private static Dictionary<string, CultureData>? s_cachedCultures;
 658
 659        internal static CultureData? GetCultureData(string? cultureName, bool useUserOverride)
 660        {
 661            // The undetermined culture name "und" is not a real culture, but it resolves to the invariant culture becau
 662            const string UndeterminedCultureName = "und";
 663
 664            // First do a shortcut for Invariant
 0665            if (string.IsNullOrEmpty(cultureName) || cultureName.Equals(UndeterminedCultureName, StringComparison.Ordina
 666            {
 0667                return Invariant;
 668            }
 669
 0670            if (GlobalizationMode.PredefinedCulturesOnly)
 671            {
 0672                if (GlobalizationMode.Invariant || (GlobalizationMode.UseNls ? !NlsIsEnsurePredefinedLocaleName(cultureN
 0673                    return null;
 674            }
 675
 676            // Try the hash table first
 0677            string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
 0678            Dictionary<string, CultureData>? tempHashTable = s_cachedCultures;
 0679            if (tempHashTable == null)
 680            {
 681                // No table yet, make a new one
 0682                var newTable = new Dictionary<string, CultureData>();
 0683                tempHashTable = Interlocked.CompareExchange(ref s_cachedCultures, newTable, null) ?? newTable;
 684            }
 685            else
 686            {
 687                // Check the hash table
 688                bool ret;
 689                CultureData? retVal;
 0690                lock (tempHashTable)
 691                {
 0692                    ret = tempHashTable.TryGetValue(hashName, out retVal);
 0693                }
 0694                if (ret && retVal != null)
 695                {
 0696                    return retVal;
 697                }
 698            }
 699            // Not found in the hash table, need to see if we can build one that works for us
 0700            CultureData? culture = CreateCultureData(cultureName, useUserOverride);
 0701            if (culture == null)
 702            {
 0703                return null;
 704            }
 705
 706            // Found one, add it to the cache
 0707            lock (tempHashTable)
 708            {
 0709                tempHashTable[hashName] = culture;
 0710            }
 711
 0712            return culture;
 713        }
 714
 715        private static unsafe string NormalizeCultureName(string name, out bool isNeutralName)
 716        {
 0717            isNeutralName = true;
 0718            int i = 0;
 719
 0720            if (name.Length > LocaleNameMaxLength)
 721            {
 722                // Theoretically we shouldn't hit this exception.
 0723                throw new ArgumentException(SR.Format(SR.Argument_InvalidId, nameof(name)));
 724            }
 725
 0726            Span<char> normalizedName = stackalloc char[name.Length];
 727
 0728            bool changed = false;
 729
 0730            while (i < name.Length && name[i] != '-' && name[i] != '_')
 731            {
 0732                if (char.IsAsciiLetterUpper(name[i]))
 733                {
 734                    // lowercase characters before '-'
 0735                    normalizedName[i] = (char)(((int)name[i]) + 0x20);
 0736                    changed = true;
 737                }
 738                else
 739                {
 0740                    normalizedName[i] = name[i];
 741                }
 0742                i++;
 743            }
 744
 0745            if (i < name.Length)
 746            {
 747                // this is not perfect to detect the non neutral cultures but it is good enough when we are running in i
 0748                isNeutralName = false;
 749            }
 750
 0751            while (i < name.Length)
 752            {
 0753                if (char.IsAsciiLetterLower(name[i]))
 754                {
 0755                    normalizedName[i] = (char)(((int)name[i]) - 0x20);
 0756                    changed = true;
 757                }
 758                else
 759                {
 0760                    normalizedName[i] = name[i];
 761                }
 0762                i++;
 763            }
 764
 0765            if (changed)
 766            {
 0767                return new string(normalizedName);
 768            }
 769
 0770            return name;
 771        }
 772
 773        private static CultureData? CreateCultureData(string cultureName, bool useUserOverride)
 774        {
 0775            if (GlobalizationMode.Invariant)
 776            {
 0777                if (cultureName.Length > LocaleNameMaxLength || !CultureInfo.VerifyCultureName(cultureName, false))
 778                {
 0779                    return null;
 780                }
 0781                CultureData cd = CreateCultureWithInvariantData();
 0782                cd._sName = NormalizeCultureName(cultureName, out cd._bNeutral);
 0783                cd._bUseOverridesUserSetting = useUserOverride;
 0784                cd._sRealName = cd._sName;
 0785                cd._sWindowsName = cd._sName;
 0786                cd._iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
 787
 0788                return cd;
 789            }
 790
 0791            if (cultureName.Length == 1 && (cultureName[0] == 'C' || cultureName[0] == 'c'))
 792            {
 793                // Always map the "C" locale to Invariant to avoid mapping it to en_US_POSIX on Linux because POSIX
 794                // locale collation doesn't support case insensitive comparisons.
 795                // We do the same mapping on Windows for the sake of consistency.
 0796                return Invariant;
 797            }
 798
 0799            CultureData culture = new CultureData();
 0800            culture._sRealName = cultureName;
 0801            culture._bUseOverridesUserSetting = useUserOverride;
 802
 803            // Ask native code if that one's real, populate _sWindowsName
 0804            if (!culture.InitCultureDataCore() && !culture.InitCompatibilityCultureData())
 805            {
 0806                return null;
 807            }
 808#if TARGET_BROWSER
 809            culture.JSInitLocaleInfo();
 810#endif
 811
 812            // We need _sWindowsName to be initialized to know if we're using overrides.
 0813            culture.InitUserOverride(useUserOverride);
 0814            return culture;
 815        }
 816
 817        private bool InitCompatibilityCultureData()
 818        {
 819            // for compatibility handle the deprecated ids: zh-chs, zh-cht
 0820            string cultureName = _sRealName;
 821
 822            string fallbackCultureName;
 823            string realCultureName;
 0824            switch (AnsiToLower(cultureName))
 825            {
 826                case "zh-chs":
 0827                    fallbackCultureName = "zh-Hans";
 0828                    realCultureName = "zh-CHS";
 0829                    break;
 830                case "zh-cht":
 0831                    fallbackCultureName = "zh-Hant";
 0832                    realCultureName = "zh-CHT";
 0833                    break;
 834                default:
 0835                    return false;
 836            }
 837
 0838            _sRealName = fallbackCultureName;
 0839            if (!InitCultureDataCore())
 840            {
 0841                return false;
 842            }
 843
 844            // fixup our data
 0845            _sName = realCultureName; // the name that goes back to the user
 0846            _sParent = fallbackCultureName;
 847
 0848            return true;
 849        }
 850
 851        /// We'd rather people use the named version since this doesn't allow custom locales
 852        internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
 853        {
 0854            CultureData? retVal = null;
 855
 0856            if (culture == CultureInfo.LOCALE_INVARIANT)
 857            {
 0858                return Invariant;
 859            }
 860
 0861            if (GlobalizationMode.Invariant)
 862            {
 0863                if (!GlobalizationMode.PredefinedCulturesOnly)
 864                {
 0865                    if (culture is < 1 or > 0xf_ffff)
 866                    {
 0867                        throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
 868                    }
 869
 0870                    return Invariant;
 871                }
 872
 873                // LCID is not supported in the InvariantMode
 0874                throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupportedInInvariantM
 875            }
 876
 877            // Convert the lcid to a name, then use that
 0878            string? localeName = LCIDToLocaleName(culture);
 879
 0880            if (!string.IsNullOrEmpty(localeName))
 881            {
 882                // Valid name, use it
 0883                retVal = GetCultureData(localeName, bUseUserOverride);
 884            }
 885
 886            // If not successful, throw
 0887            if (retVal == null)
 888            {
 0889                throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
 890            }
 891
 892            // Return the one we found
 0893            return retVal;
 894        }
 895
 896        /// <summary>
 897        /// The real name used to construct the locale (ie: de-DE_phoneb)
 898        /// </summary>
 899        internal string CultureName
 900        {
 901            get
 902            {
 0903                Debug.Assert(_sRealName != null, "[CultureData.CultureName] Expected _sRealName to be populated by alrea
 904                // since windows doesn't know about zh-CHS and zh-CHT,
 905                // we leave sRealName == zh-Hanx but we still need to
 906                // pretend that it was zh-CHX.
 0907                return _sName is "zh-CHS" or "zh-CHT" ? _sName : _sRealName;
 908            }
 909        }
 910
 911        /// <summary>
 912        /// Did the user request to use overrides?
 913        /// </summary>
 0914        internal bool UseUserOverride => _bUseOverridesUserSetting;
 915
 916        /// <summary>
 917        /// locale name (ie: de-DE, NO sort information)
 918        /// </summary>
 0919        internal string Name => _sName ?? string.Empty;
 920
 921        // Parent name (which may be a custom locale/culture)
 922        // Ask using the real name, so that we get parents of neutrals
 0923        internal string ParentName => _sParent ??= GetLocaleInfoCore(_sRealName, LocaleStringData.ParentName);
 924
 925        // Localized pretty name for this locale (ie: Inglis (estados Unitos))
 926        internal string DisplayName
 927        {
 928            get
 929            {
 0930                string? localizedDisplayName = NativeName;
 0931                if (!GlobalizationMode.Invariant && Name.Length > 0)
 932                {
 933                    try
 934                    {
 0935                        localizedDisplayName = GetLanguageDisplayNameCore(
 0936                            Name.Equals("zh-CHT", StringComparison.OrdinalIgnoreCase) ? "zh-Hant" :
 0937                            Name.Equals("zh-CHS", StringComparison.OrdinalIgnoreCase) ? "zh-Hans" :
 0938                            Name);
 0939                    }
 0940                    catch
 941                    {
 942                        // do nothing
 0943                    }
 944
 945                    // If it hasn't been found (Windows 8 and up), fallback to the system
 0946                    if (string.IsNullOrEmpty(localizedDisplayName) && IsNeutralCulture)
 947                    {
 0948                        localizedDisplayName = LocalizedLanguageName;
 949                    }
 950                }
 951
 0952                return localizedDisplayName;
 953            }
 954        }
 955
 0956        private string GetLanguageDisplayNameCore(string cultureName) => GlobalizationMode.UseNls ?
 0957                                                                            NlsGetLanguageDisplayName(cultureName) :
 0958#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 0959                                                                         GlobalizationMode.Hybrid ?
 0960                                                                            GetLocaleInfoNative(cultureName, LocaleStrin
 0961#endif
 0962                                                                            IcuGetLanguageDisplayName(cultureName);
 963
 964        /// <summary>
 965        /// English pretty name for this locale (ie: English (United States))
 966        /// </summary>
 967        internal string EnglishName
 968        {
 969            get
 970            {
 0971                string? englishDisplayName = _sEnglishDisplayName;
 0972                if (englishDisplayName == null && !GlobalizationMode.Invariant)
 973                {
 974                    // If its neutral use the language name
 0975                    if (IsNeutralCulture)
 976                    {
 0977                        englishDisplayName = GetLocaleInfoCore(LocaleStringData.EnglishDisplayName);
 0978                        if (string.IsNullOrEmpty(englishDisplayName))
 979                        {
 0980                            englishDisplayName = EnglishLanguageName;
 981                        }
 982
 983                        // differentiate the legacy display names
 0984                        switch (_sName)
 985                        {
 986                            case "zh-CHS":
 987                            case "zh-CHT":
 0988                                englishDisplayName += " Legacy";
 0989                                break;
 990                        }
 991                    }
 992                    else
 993                    {
 0994                        englishDisplayName = GetLocaleInfoCore(LocaleStringData.EnglishDisplayName);
 995
 996                        // if it isn't found build one:
 0997                        if (string.IsNullOrEmpty(englishDisplayName))
 998                        {
 999                            // Our existing names mostly look like:
 1000                            // "English" + "United States" -> "English (United States)"
 1001                            // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
 01002                            if (EnglishLanguageName.EndsWith(')'))
 1003                            {
 1004                                // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
 01005                                englishDisplayName = string.Concat(
 01006                                    EnglishLanguageName.AsSpan(0, _sEnglishLanguage!.Length - 1),
 01007                                    ", ",
 01008                                    EnglishCountryName,
 01009                                    ")");
 1010                            }
 1011                            else
 1012                            {
 1013                                // "English" + "United States" -> "English (United States)"
 01014                                englishDisplayName = EnglishLanguageName + " (" + EnglishCountryName + ")";
 1015                            }
 1016                        }
 1017                    }
 1018
 01019                    _sEnglishDisplayName = englishDisplayName;
 1020                }
 1021
 01022                return englishDisplayName!;
 1023            }
 1024        }
 1025
 1026        /// <summary>
 1027        /// Native pretty name for this locale (ie: Deutsch (Deutschland))
 1028        /// </summary>
 1029        internal string NativeName
 1030        {
 1031            get
 1032            {
 01033                string? nativeDisplayName = _sNativeDisplayName;
 01034                if (nativeDisplayName == null && !GlobalizationMode.Invariant)
 1035                {
 1036                    // If its neutral use the language name
 01037                    if (IsNeutralCulture)
 1038                    {
 01039                        nativeDisplayName = GetLocaleInfoCore(LocaleStringData.NativeDisplayName);
 01040                        if (string.IsNullOrEmpty(nativeDisplayName))
 1041                        {
 01042                            nativeDisplayName = NativeLanguageName;
 1043                        }
 1044
 1045                        // differentiate the legacy display names
 01046                        switch (_sName)
 1047                        {
 1048                            case "zh-CHS":
 01049                                nativeDisplayName += " \u65E7\u7248";
 01050                                break;
 1051                            case "zh-CHT":
 01052                                nativeDisplayName += " \u820A\u7248";
 01053                                break;
 1054                        }
 1055                    }
 1056                    else
 1057                    {
 01058                        nativeDisplayName = GetLocaleInfoCore(LocaleStringData.NativeDisplayName);
 1059
 1060                        // if it isn't found build one:
 01061                        if (string.IsNullOrEmpty(nativeDisplayName))
 1062                        {
 1063                            // These should primarily be "Deutsch (Deutschland)" type names
 01064                            nativeDisplayName = NativeLanguageName + " (" + NativeCountryName + ")";
 1065                        }
 1066                    }
 1067
 01068                    _sNativeDisplayName = nativeDisplayName;
 1069                }
 1070
 01071                return nativeDisplayName!;
 1072            }
 1073        }
 1074
 1075        /// <summary>
 1076        /// The culture name to be used in CultureInfo.CreateSpecificCulture()
 1077        /// </summary>
 1078        internal string SpecificCultureName
 1079        {
 1080            get
 1081            {
 1082                // This got populated during the culture initialization
 01083                Debug.Assert(_sSpecificCulture != null, "[CultureData.SpecificCultureName] Expected this.sSpecificCultur
 01084                return _sSpecificCulture;
 1085            }
 1086        }
 1087
 1088        /// <summary>
 1089        /// iso 639 language name, ie: en
 1090        /// </summary>
 01091        internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfoCore(LocaleStringData.Iso639Langua
 1092
 1093        /// <summary>
 1094        /// iso 639 language name, ie: eng
 1095        /// </summary>
 01096        internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfoCore(LocaleStringData.Iso639Lan
 1097
 1098        /// <summary>
 1099        /// abbreviated windows language name (ie: enu) (non-standard, avoid this)
 1100        /// </summary>
 01101        internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GlobalizationMode.UseNls ?
 01102                                                                            NlsGetThreeLetterWindowsLanguageName(_sRealN
 01103                                                                            IcuGetThreeLetterWindowsLanguageName(_sRealN
 1104
 1105        /// <summary>
 1106        /// Localized name for this language
 1107        /// </summary>
 1108        private string LocalizedLanguageName
 1109        {
 1110            get
 1111            {
 01112                string localizedLanguage = NativeLanguageName;
 01113                if (!GlobalizationMode.Invariant && Name.Length > 0)
 1114                {
 1115                    // If ICU is enabled we call it anyway. If NLS, we call it only if the Windows UI language match the
 01116                    if (!GlobalizationMode.UseNls || CultureInfo.UserDefaultUICulture?.Name == CultureInfo.CurrentUICult
 1117                    {
 01118                        localizedLanguage = GetLocaleInfoCore(LocaleStringData.LocalizedLanguageName, CultureInfo.Curren
 1119                    }
 1120                }
 1121
 01122                return localizedLanguage;
 1123            }
 1124        }
 1125
 1126        /// <summary>
 1127        /// English name for this language (Windows Only) ie: German
 1128        /// </summary>
 01129        private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfoCore(LocaleStringData.EnglishLanguageNa
 1130
 1131        /// <summary>
 1132        /// Native name of this language (Windows Only) ie: Deutsch
 1133        /// </summary>
 01134        private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfoCore(LocaleStringData.NativeLanguageName)
 1135
 1136        /// <summary>
 1137        /// region name (eg US)
 1138        /// </summary>
 01139        internal string RegionName => _sRegionName ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName);
 1140
 1141        internal int GeoId
 1142        {
 1143            get
 1144            {
 01145                if (_iGeoId == undef && !GlobalizationMode.Invariant)
 1146                {
 01147                    _iGeoId = GlobalizationMode.UseNls ? NlsGetLocaleInfo(LocaleNumberData.GeoId) : IcuGetGeoId(_sRealNa
 1148                }
 01149                return _iGeoId;
 1150            }
 1151        }
 1152
 1153        /// <summary>
 1154        /// localized name for the country
 1155        /// </summary>
 1156        internal string LocalizedCountryName
 1157        {
 1158            get
 1159            {
 01160                string? localizedCountry = _sLocalizedCountry;
 01161                if (localizedCountry == null)
 1162                {
 01163                    if (!GlobalizationMode.Invariant)
 1164                    {
 1165                        try
 1166                        {
 01167                            localizedCountry = GlobalizationMode.UseNls ? NlsGetRegionDisplayName() : IcuGetRegionDispla
 01168                        }
 01169                        catch
 1170                        {
 1171                            // do nothing. we'll fallback
 01172                        }
 01173                        localizedCountry ??= NativeCountryName;
 1174                    }
 1175                    else
 1176                    {
 01177                        localizedCountry = NativeCountryName;
 1178                    }
 01179                    _sLocalizedCountry = localizedCountry;
 1180                }
 1181
 01182                return localizedCountry;
 1183            }
 1184        }
 1185
 1186        /// <summary>
 1187        /// english country name (RegionInfo) ie: Germany
 1188        /// </summary>
 01189        internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfoCore(LocaleStringData.EnglishCountryName
 1190
 1191        /// <summary>
 1192        /// native country name (RegionInfo) ie: Deutschland
 1193        /// </summary>
 01194        internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfoCore(LocaleStringData.NativeCountryName);
 1195
 1196        /// <summary>
 1197        /// ISO 3166 Country Name
 1198        /// </summary>
 01199        internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfoCore(LocaleStringData.Iso3166Co
 1200
 1201        /// <summary>
 1202        /// 3 letter ISO 3166 country code
 1203        /// </summary>
 01204        internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfoCore(LocaleStringData.Iso316
 1205
 1206        internal int KeyboardLayoutId
 1207        {
 1208            get
 1209            {
 01210                if (_iInputLanguageHandle == undef)
 1211                {
 01212                    if (IsSupplementalCustomCulture)
 1213                    {
 01214                        _iInputLanguageHandle = 0x0409;
 1215                    }
 1216                    else
 1217                    {
 1218                        // Input Language is same as LCID for built-in cultures
 01219                        _iInputLanguageHandle = LCID;
 1220                    }
 1221                }
 01222                return _iInputLanguageHandle;
 1223            }
 1224        }
 1225
 1226        /// <summary>
 1227        /// Console fallback name (ie: locale to use for console apps for unicode-only locales)
 1228        /// </summary>
 01229        internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GlobalizationMode.UseNls ?
 01230                                                                            NlsGetConsoleFallbackName(_sRealName) :
 01231                                                                            IcuGetConsoleFallbackName(_sRealName);
 1232
 1233        /// <summary>
 1234        /// grouping of digits
 1235        /// (user can override)
 1236        /// </summary>
 01237        internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfoCoreUserOverride(LocaleGroupingData.Digit);
 1238
 1239        /// <summary>
 1240        /// Not a Number
 1241        /// </summary>
 01242        private string NaNSymbol => _sNaN ??= GetLocaleInfoCore(LocaleStringData.NaNSymbol);
 1243
 1244        /// <summary>
 1245        /// + Infinity
 1246        /// </summary>
 01247        private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfoCore(LocaleStringData.PositiveInfin
 1248
 1249        /// <summary>
 1250        /// - Infinity
 1251        /// </summary>
 01252        private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfoCore(LocaleStringData.NegativeInfin
 1253
 1254        /// <summary>
 1255        /// Negative Percent (0-3)
 1256        /// </summary>
 1257        private int PercentNegativePattern
 1258        {
 1259            get
 1260            {
 01261                if (_iNegativePercent == undef)
 1262                {
 1263                    // Note that <= Windows Vista this is synthesized by native code
 01264                    _iNegativePercent = GetLocaleInfoCore(LocaleNumberData.NegativePercentFormat);
 1265                }
 01266                return _iNegativePercent;
 1267            }
 1268        }
 1269
 1270        /// <summary>
 1271        /// Positive Percent (0-11)
 1272        /// </summary>
 1273        private int PercentPositivePattern
 1274        {
 1275            get
 1276            {
 01277                if (_iPositivePercent == undef)
 1278                {
 1279                    // Note that <= Windows Vista this is synthesized by native code
 01280                    _iPositivePercent = GetLocaleInfoCore(LocaleNumberData.PositivePercentFormat);
 1281                }
 01282                return _iPositivePercent;
 1283            }
 1284        }
 1285
 1286        /// <summary>
 1287        /// Percent (%) symbol
 1288        /// </summary>
 01289        private string PercentSymbol => _sPercent ??= GetLocaleInfoCore(LocaleStringData.PercentSymbol);
 1290
 1291        /// <summary>
 1292        /// PerMille symbol
 1293        /// </summary>
 01294        private string PerMilleSymbol => _sPerMille ??= GetLocaleInfoCore(LocaleStringData.PerMilleSymbol);
 1295
 1296        /// <summary>
 1297        /// local monetary symbol, eg: $
 1298        /// (user can override)
 1299        /// </summary>
 01300        internal string CurrencySymbol => _sCurrency ??= GetLocaleInfoCoreUserOverride(LocaleStringData.MonetarySymbol);
 1301
 1302        /// <summary>
 1303        /// international monetary symbol (RegionInfo), eg: USD
 1304        /// </summary>
 01305        internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfoCore(LocaleStringData.Iso4217Monetary
 1306
 1307        /// <summary>
 1308        /// English name for this currency (RegionInfo), eg: US Dollar
 1309        /// </summary>
 01310        internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyEnglishN
 1311
 1312        /// <summary>
 1313        /// Native name for this currency (RegionInfo), eg: Schweiz Frank
 1314        /// </summary>
 01315        internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyNativeName
 1316
 1317        /// <summary>
 1318        /// monetary grouping of digits
 1319        /// (user can override)
 1320        /// </summary>
 01321        internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfoCoreUserOverride(LocaleGroupingData.Mo
 1322
 1323        /// <summary>
 1324        /// system of measurement 0=metric, 1=US (RegionInfo)
 1325        /// (user can override)
 1326        /// </summary>
 1327        internal int MeasurementSystem
 1328        {
 1329            get
 1330            {
 01331                if (_iMeasure == undef)
 1332                {
 01333                    _iMeasure = GetLocaleInfoCoreUserOverride(LocaleNumberData.MeasurementSystem);
 1334                }
 01335                return _iMeasure;
 1336            }
 1337        }
 1338
 1339        /// <summary>
 1340        /// list Separator
 1341        /// (user can override)
 1342        /// </summary>
 01343        internal string ListSeparator => _sListSeparator ??= ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(LocaleStrin
 1344
 1345        /// <summary>
 1346        /// AM designator
 1347        /// (user can override)
 1348        /// </summary>
 01349        internal string AMDesignator => _sAM1159 ??= GetLocaleInfoCoreUserOverride(LocaleStringData.AMDesignator);
 1350
 1351        /// <summary>
 1352        /// PM designator
 1353        /// (user can override)
 1354        /// </summary>
 01355        internal string PMDesignator => _sPM2359 ??= GetLocaleInfoCoreUserOverride(LocaleStringData.PMDesignator);
 1356
 1357        /// <summary>
 1358        /// time format
 1359        /// (user can override)
 1360        /// </summary>
 1361        internal string[] LongTimes
 1362        {
 1363            get
 1364            {
 01365                if (_saLongTimes == null && !GlobalizationMode.Invariant)
 1366                {
 01367                    string[]? longTimes = GetTimeFormatsCore(shortFormat: false);
 01368                    _saLongTimes = longTimes != null && longTimes.Length != 0 ? longTimes : Invariant._saLongTimes!;
 1369                }
 1370
 01371                return _saLongTimes!;
 1372            }
 1373        }
 1374
 1375        /// <summary>
 1376        /// Short times (derived from long times format)
 1377        /// (user can override)
 1378        /// </summary>
 1379        internal string[] ShortTimes
 1380        {
 1381            get
 1382            {
 01383                if (_saShortTimes == null && !GlobalizationMode.Invariant)
 1384                {
 1385                    // Try to get the short times from the OS/culture.dll
 1386                    // If we couldn't find short times, then compute them from long times
 1387                    // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
 01388                    string[]? shortTimes = GetTimeFormatsCore(shortFormat: true);
 01389                    _saShortTimes = shortTimes != null && shortTimes.Length != 0 ? shortTimes : DeriveShortTimesFromLong
 1390                }
 1391
 01392                return _saShortTimes!;
 1393            }
 1394        }
 1395
 1396        private string[] DeriveShortTimesFromLong()
 1397        {
 1398            // Our logic is to look for h,H,m,s,t.  If we find an s, then we check the string
 1399            // between it and the previous marker, if any.  If its a short, unescaped separator,
 1400            // then we don't retain that part.
 1401            // We then check after the ss and remove anything before the next h,H,m,t...
 01402            string[] longTimes = LongTimes;
 01403            string[] shortTimes = new string[longTimes.Length];
 1404
 01405            for (int i = 0; i < longTimes.Length; i++)
 1406            {
 01407                shortTimes[i] = StripSecondsFromPattern(longTimes[i]);
 1408            }
 01409            return shortTimes;
 1410        }
 1411
 1412        private static string StripSecondsFromPattern(string time)
 1413        {
 01414            bool bEscape = false;
 01415            int iLastToken = -1;
 1416
 1417            // Find the seconds
 01418            for (int j = 0; j < time.Length; j++)
 1419            {
 1420                // Change escape mode?
 01421                if (time[j] == '\'')
 1422                {
 1423                    // Continue
 01424                    bEscape = !bEscape;
 01425                    continue;
 1426                }
 1427
 1428                // See if there was a single \
 01429                if (time[j] == '\\')
 1430                {
 1431                    // Skip next char
 01432                    j++;
 01433                    continue;
 1434                }
 1435
 01436                if (bEscape)
 1437                {
 1438                    continue;
 1439                }
 1440
 01441                switch (time[j])
 1442                {
 1443                    // Check for seconds
 1444                    case 's':
 1445                        // Found seconds, see if there was something unescaped and short between
 1446                        // the last marker and the seconds.  Windows says separator can be a
 1447                        // maximum of three characters (without null)
 1448                        // If 1st or last characters were ', then ignore it
 01449                        if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
 01450                            (time[iLastToken + 1] != '\'') &&
 01451                            (time[j - 1] != '\''))
 1452                        {
 1453                            // There was something there we want to remember
 01454                            if (iLastToken >= 0)
 1455                            {
 01456                                j = iLastToken + 1;
 1457                            }
 1458                        }
 1459
 1460                        bool containsSpace;
 01461                        int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
 1462
 1463                        string sep;
 1464
 01465                        if (containsSpace)
 1466                        {
 01467                            sep = " ";
 1468                        }
 1469                        else
 1470                        {
 01471                            sep = "";
 1472                        }
 1473
 01474                        time = string.Concat(time.AsSpan(0, j), sep, time.AsSpan(endIndex));
 01475                        break;
 1476                    case 'm':
 1477                    case 'H':
 1478                    case 'h':
 01479                        iLastToken = j;
 1480                        break;
 1481                }
 1482            }
 01483            return time;
 1484        }
 1485
 1486        private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
 1487        {
 01488            bool shouldEscape = false;
 01489            containsSpace = false;
 01490            for (; index < time.Length; index++)
 1491            {
 01492                switch (time[index])
 1493                {
 1494                    case '\'':
 01495                        shouldEscape = !shouldEscape;
 01496                        continue;
 1497                    case '\\':
 01498                        index++;
 01499                        if (time[index] == ' ')
 1500                        {
 01501                            containsSpace = true;
 1502                        }
 01503                        continue;
 1504                    case ' ':
 01505                        containsSpace = true;
 01506                        break;
 1507                    case 't':
 1508                    case 'm':
 1509                    case 'H':
 1510                    case 'h':
 01511                        if (shouldEscape)
 1512                        {
 1513                            continue;
 1514                        }
 01515                        return index;
 1516                }
 1517            }
 01518            containsSpace = false;
 01519            return index;
 1520        }
 1521
 1522        /// <summary>
 1523        /// first day of week
 1524        /// (user can override)
 1525        /// </summary>
 1526        internal int FirstDayOfWeek
 1527        {
 1528            get
 1529            {
 01530                if (_iFirstDayOfWeek == undef && !GlobalizationMode.Invariant)
 1531                {
 1532#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 1533                    if (GlobalizationMode.Hybrid)
 1534                    {
 1535                        _iFirstDayOfWeek = GetLocaleInfoNative(LocaleNumberData.FirstDayOfWeek);
 1536                    }
 1537                    else
 1538#endif
 1539                    {
 01540                        _iFirstDayOfWeek = ShouldUseUserOverrideNlsData ? NlsGetFirstDayOfWeek() : IcuGetLocaleInfo(Loca
 1541                    }
 1542                }
 01543                return _iFirstDayOfWeek;
 1544            }
 1545        }
 1546
 1547        /// <summary>
 1548        /// first week of year
 1549        /// (user can override)
 1550        /// </summary>
 1551        internal int CalendarWeekRule
 1552        {
 1553            get
 1554            {
 01555                if (_iFirstWeekOfYear == undef)
 1556                {
 01557                    _iFirstWeekOfYear = GetLocaleInfoCoreUserOverride(LocaleNumberData.FirstWeekOfYear);
 1558                }
 01559                return _iFirstWeekOfYear;
 1560            }
 1561        }
 1562
 1563        /// <summary>
 1564        /// (user can override default only) short date format
 1565        /// </summary>
 1566        internal string[] ShortDates(CalendarId calendarId)
 1567        {
 01568            return GetCalendar(calendarId).saShortDates;
 1569        }
 1570
 1571        /// <summary>
 1572        /// long date format
 1573        /// (user can override default only)
 1574        /// </summary>
 1575        internal string[] LongDates(CalendarId calendarId)
 1576        {
 01577            return GetCalendar(calendarId).saLongDates;
 1578        }
 1579
 1580        /// <summary>
 1581        /// date year/month format.
 1582        /// (user can override default only)
 1583        /// </summary>
 1584        internal string[] YearMonths(CalendarId calendarId)
 1585        {
 01586            return GetCalendar(calendarId).saYearMonths;
 1587        }
 1588
 1589        internal string[] DayNames(CalendarId calendarId)
 1590        {
 01591            return GetCalendar(calendarId).saDayNames;
 1592        }
 1593
 1594        internal string[] AbbreviatedDayNames(CalendarId calendarId)
 1595        {
 01596            return GetCalendar(calendarId).saAbbrevDayNames;
 1597        }
 1598
 1599        internal string[] SuperShortDayNames(CalendarId calendarId)
 1600        {
 01601            return GetCalendar(calendarId).saSuperShortDayNames;
 1602        }
 1603
 1604        internal string[] MonthNames(CalendarId calendarId)
 1605        {
 01606            return GetCalendar(calendarId).saMonthNames;
 1607        }
 1608
 1609        internal string[] GenitiveMonthNames(CalendarId calendarId)
 1610        {
 01611            return GetCalendar(calendarId).saMonthGenitiveNames;
 1612        }
 1613
 1614        internal string[] AbbreviatedMonthNames(CalendarId calendarId)
 1615        {
 01616            return GetCalendar(calendarId).saAbbrevMonthNames;
 1617        }
 1618
 1619        internal string[] AbbreviatedGenitiveMonthNames(CalendarId calendarId)
 1620        {
 01621            return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
 1622        }
 1623
 1624        /// <remarks>>
 1625        /// Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
 1626        /// the non-leap names skip the 7th name in the normal month name array
 1627        /// </remarks>
 1628        internal string[] LeapYearMonthNames(CalendarId calendarId)
 1629        {
 01630            return GetCalendar(calendarId).saLeapYearMonthNames;
 1631        }
 1632
 1633        internal string MonthDay(CalendarId calendarId)
 1634        {
 01635            return GetCalendar(calendarId).sMonthDay;
 1636        }
 1637
 1638        /// <summary>
 1639        /// All available calendar type(s). The first one is the default calendar.
 1640        /// </summary>
 1641        internal CalendarId[] CalendarIds
 1642        {
 1643            get
 1644            {
 01645                if (_waCalendars == null)
 1646                {
 01647                    if (GlobalizationMode.Invariant || IsInvariantCulture)
 1648                    {
 1649                        // We do this lazily as opposed in CreateCultureWithInvariantData to avoid introducing
 1650                        // enum arrays into apps that otherwise wouldn't have them. This helps with size in AOT.
 01651                        _waCalendars = [CalendarId.GREGORIAN];
 1652                    }
 1653                    else
 1654                    {
 1655                        // We pass in an array of ints, and native side fills it up with count calendars.
 1656                        // We then have to copy that list to a new array of the right size.
 1657                        // Default calendar should be first
 01658                        CalendarId[] calendars = new CalendarId[23];
 01659                        Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be popu
 1660
 01661                        int count = CalendarData.GetCalendarsCore(_sWindowsName, _bUseOverrides, calendars);
 1662
 1663                        // See if we had a calendar to add.
 01664                        if (count == 0)
 1665                        {
 1666                            // Failed for some reason, just use Gregorian
 01667                            _waCalendars = Invariant.CalendarIds;
 1668                        }
 1669                        else
 1670                        {
 1671                            // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
 1672                            // TODO: Is this hack necessary long-term?
 01673                            if (_sWindowsName == "zh-TW")
 1674                            {
 01675                                bool found = false;
 1676
 1677                                // Do we need to insert calendar 4?
 01678                                for (int i = 0; i < count; i++)
 1679                                {
 1680                                    // Stop if we found calendar four
 01681                                    if (calendars[i] == CalendarId.TAIWAN)
 1682                                    {
 01683                                        found = true;
 01684                                        break;
 1685                                    }
 1686                                }
 1687
 1688                                // If not found then insert it
 01689                                if (!found)
 1690                                {
 1691                                    // Insert it as the 2nd calendar
 01692                                    count++;
 1693                                    // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being
 01694                                    Array.Copy(calendars, 1, calendars, 2, 23 - 1 - 1);
 01695                                    calendars[1] = CalendarId.TAIWAN;
 1696                                }
 1697                            }
 1698
 1699                            // It worked, remember the list
 01700                            CalendarId[] temp = new CalendarId[count];
 01701                            Array.Copy(calendars, temp, count);
 01702                            _waCalendars = temp;
 1703                        }
 1704                    }
 1705                }
 01706                return _waCalendars;
 1707            }
 1708        }
 1709
 1710        /// <summary>
 1711        /// Native calendar names. Index of optional calendar - 1, empty if
 1712        /// no optional calendar at that number
 1713        /// </summary>
 1714        internal string CalendarName(CalendarId calendarId)
 1715        {
 01716            return GetCalendar(calendarId).sNativeName;
 1717        }
 1718
 1719        internal CalendarData GetCalendar(CalendarId calendarId)
 1720        {
 01721            if (GlobalizationMode.Invariant)
 1722            {
 01723                return CalendarData.Invariant;
 1724            }
 1725
 01726            Debug.Assert(calendarId > 0 && calendarId <= CalendarId.LAST_CALENDAR,
 01727                "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
 1728
 1729            // arrays are 0 based, calendarIds are 1 based
 01730            int calendarIndex = (int)calendarId - 1;
 1731
 1732            // Have to have calendars
 01733            _calendars ??= new CalendarData[CalendarData.MAX_CALENDARS];
 1734
 1735            // we need the following local variable to avoid returning null
 1736            // when another thread creates a new array of CalendarData (above)
 1737            // right after we insert the newly created CalendarData (below)
 01738            CalendarData? calendarData = _calendars[calendarIndex];
 1739            // Make sure that calendar has data
 01740            if (calendarData == null)
 1741            {
 01742                if (calendarIndex == 0 && IsInvariantCulture)
 1743                {
 1744                    // We do this lazily as opposed in CreateCultureWithInvariantData to avoid introducing
 1745                    // invariant calendar data into apps that don't even need calendar.
 01746                    calendarData = CalendarData.Invariant;
 1747                }
 1748                else
 1749                {
 01750                    Debug.Assert(_sWindowsName != null, "[CultureData.GetCalendar] Expected _sWindowsName to be populate
 01751                    calendarData = new CalendarData(_sWindowsName, calendarId, _bUseOverrides);
 1752                }
 01753                _calendars[calendarIndex] = calendarData;
 1754            }
 1755
 01756            return calendarData;
 1757        }
 1758
 1759        internal bool IsRightToLeft =>
 1760            // Returns one of the following 4 reading layout values:
 1761            // 0 - Left to right (eg en-US)
 1762            // 1 - Right to left (eg arabic locales)
 1763            // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
 1764            // 3 - Vertical top to bottom with columns proceeding to the right
 01765            ReadingLayout == 1;
 1766
 1767        /// <summary>
 1768        /// Returns one of the following 4 reading layout values:
 1769        /// 0 - Left to right (eg en-US)
 1770        /// 1 - Right to left (eg arabic locales)
 1771        /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
 1772        /// 3 - Vertical top to bottom with columns proceeding to the right
 1773        /// </summary>
 1774        private int ReadingLayout
 1775        {
 1776            get
 1777            {
 01778                if (_iReadingLayout == undef && !GlobalizationMode.Invariant)
 1779                {
 01780                    Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by
 01781                    _iReadingLayout = GetLocaleInfoCore(LocaleNumberData.ReadingLayout);
 1782                }
 1783
 01784                return _iReadingLayout;
 1785            }
 1786        }
 1787
 1788        /// <summary>
 1789        /// // Text info name to use for text information
 1790        /// The TextInfo name never includes that alternate sort and is always specific
 1791        /// For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
 1792        /// en -> en-US
 1793        /// en-US -> en-US
 1794        /// fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
 1795        /// fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
 1796        /// es-ES_tradnl -> es-ES
 1797        /// </summary>
 1798        internal string TextInfoName
 1799        {
 1800            get
 1801            {
 1802                // Note: Custom cultures might point at another culture's textinfo, however windows knows how
 1803                // to redirect it to the desired textinfo culture, so this is OK.
 01804                Debug.Assert(_sRealName != null, "[CultureData.TextInfoName] Expected _sRealName to be populated by alre
 01805                return _sRealName;
 1806            }
 1807        }
 1808
 1809        /// <summary>
 1810        /// Compare info name (including sorting key) to use if custom
 1811        /// </summary>
 1812        internal string SortName
 1813        {
 1814            get
 1815            {
 01816                Debug.Assert(_sRealName != null, "[CultureData.SortName] Expected _sRealName to be populated by already"
 01817                return _sRealName;
 1818            }
 1819        }
 1820
 01821        internal bool IsSupplementalCustomCulture => IsCustomCultureId(LCID);
 1822
 1823        /// <summary>
 1824        /// Default ansi code page ID (ACP)
 1825        /// </summary>
 1826        internal int ANSICodePage
 1827        {
 1828            get
 1829            {
 01830                if (_iDefaultAnsiCodePage == undef && !GlobalizationMode.Invariant)
 1831                {
 01832                    _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName);
 1833                }
 01834                return _iDefaultAnsiCodePage;
 1835            }
 1836        }
 1837
 1838        /// <summary>
 1839        /// Default oem code page ID (OCP or OEM).
 1840        /// </summary>
 1841        internal int OEMCodePage
 1842        {
 1843            get
 1844            {
 01845                if (_iDefaultOemCodePage == undef && !GlobalizationMode.Invariant)
 1846                {
 01847                    _iDefaultOemCodePage = GetOemCodePage(_sRealName);
 1848                }
 01849                return _iDefaultOemCodePage;
 1850            }
 1851        }
 1852
 1853        /// <summary>
 1854        /// Default macintosh code page.
 1855        /// </summary>
 1856        internal int MacCodePage
 1857        {
 1858            get
 1859            {
 01860                if (_iDefaultMacCodePage == undef && !GlobalizationMode.Invariant)
 1861                {
 01862                    _iDefaultMacCodePage = GetMacCodePage(_sRealName);
 1863                }
 01864                return _iDefaultMacCodePage;
 1865            }
 1866        }
 1867
 1868        /// <summary>
 1869        /// Default EBCDIC code page.
 1870        /// </summary>
 1871        internal int EBCDICCodePage
 1872        {
 1873            get
 1874            {
 01875                if (_iDefaultEbcdicCodePage == undef && !GlobalizationMode.Invariant)
 1876                {
 01877                    _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName);
 1878                }
 01879                return _iDefaultEbcdicCodePage;
 1880            }
 1881        }
 1882
 1883        internal int LCID
 1884        {
 1885            get
 1886            {
 01887                if (_iLanguage == 0 && !GlobalizationMode.Invariant)
 1888                {
 01889                    Debug.Assert(_sRealName != null, "[CultureData.LCID] Expected this.sRealName to be populated already
 01890                    _iLanguage = GlobalizationMode.UseNls ? NlsLocaleNameToLCID(_sRealName) : IcuLocaleNameToLCID(_sReal
 1891                }
 01892                return _iLanguage;
 1893            }
 1894        }
 1895
 1896        internal bool IsNeutralCulture =>
 1897            // InitCultureData told us if we're neutral or not
 01898            _bNeutral;
 1899
 01900        internal bool IsInvariantCulture => string.IsNullOrEmpty(Name);
 1901
 01902        internal bool IsReplacementCulture => GlobalizationMode.UseNls ? NlsIsReplacementCulture : false;
 1903
 1904        /// <summary>
 1905        /// Get an instance of our default calendar
 1906        /// </summary>
 1907        internal Calendar DefaultCalendar
 1908        {
 1909            get
 1910            {
 01911                if (GlobalizationMode.Invariant)
 1912                {
 01913                    return new GregorianCalendar();
 1914                }
 1915
 01916                CalendarId defaultCalId = (CalendarId)GetLocaleInfoCore(LocaleNumberData.CalendarType);
 1917
 01918                if (defaultCalId == 0)
 1919                {
 01920                    defaultCalId = CalendarIds[0];
 1921                }
 1922
 01923                return CultureInfo.GetCalendarInstance(defaultCalId);
 1924            }
 1925        }
 1926
 1927        /// <summary>
 1928        /// All of our era names
 1929        /// </summary>
 1930        internal string[] EraNames(CalendarId calendarId)
 1931        {
 01932            Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
 01933            return GetCalendar(calendarId).saEraNames;
 1934        }
 1935
 1936        internal string[] AbbrevEraNames(CalendarId calendarId)
 1937        {
 01938            Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
 01939            return GetCalendar(calendarId).saAbbrevEraNames;
 1940        }
 1941
 1942        internal string[] AbbreviatedEnglishEraNames(CalendarId calendarId)
 1943        {
 01944            Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
 01945            return GetCalendar(calendarId).saAbbrevEnglishEraNames;
 1946        }
 1947
 1948        /// <summary>
 1949        /// Time separator (derived from time format)
 1950        /// </summary>
 1951        internal string TimeSeparator
 1952        {
 1953            get
 1954            {
 01955                if (_sTimeSeparator == null && !GlobalizationMode.Invariant)
 1956                {
 1957                    // fr-CA culture uses time format as "HH 'h' mm 'min' ss 's'" which we cannot derive the time separa
 1958                    // We special case such culture and force ':' as time separator.
 01959                    if (_sName == "fr-CA")
 1960                    {
 01961                        _sTimeSeparator = ":";
 1962                    }
 1963                    else
 1964                    {
 01965                        string? longTimeFormat = ShouldUseUserOverrideNlsData ? NlsGetTimeFormatString() : IcuGetTimeFor
 01966                        if (string.IsNullOrEmpty(longTimeFormat))
 1967                        {
 01968                            longTimeFormat = LongTimes[0];
 1969                        }
 1970
 1971                        // Compute STIME from time format
 01972                        _sTimeSeparator = GetTimeSeparator(longTimeFormat);
 1973                    }
 1974                }
 01975                return _sTimeSeparator!;
 1976            }
 1977        }
 1978
 1979        /// <summary>
 1980        /// Date separator (derived from short date format)
 1981        /// </summary>
 1982        internal string DateSeparator(CalendarId calendarId)
 1983        {
 01984            if (GlobalizationMode.Invariant)
 1985            {
 01986                return "/";
 1987            }
 1988
 01989            if (calendarId == CalendarId.JAPAN && !LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing)
 1990            {
 1991                // The date separator is derived from the default short date pattern. So far this pattern is using
 1992                // '/' as date separator when using the Japanese calendar which make the formatting and parsing work fin
 1993                // changing the default pattern is likely will happen in the near future which can easily break formatti
 1994                // and parsing.
 1995                // We are forcing here the date separator to '/' to ensure the parsing is not going to break when changi
 1996                // the default short date pattern. The application still can override this in the code by DateTimeFormat
 01997                return "/";
 1998            }
 1999
 02000            return GetDateSeparator(ShortDates(calendarId)[0]);
 2001        }
 2002
 2003        /// <summary>
 2004        /// Unescape a NLS style quote string
 2005        ///
 2006        /// This removes single quotes:
 2007        ///      'fred' -> fred
 2008        ///      'fred -> fred
 2009        ///      fred' -> fred
 2010        ///      fred's -> freds
 2011        ///
 2012        /// This removes the first \ of escaped characters:
 2013        ///      fred\'s -> fred's
 2014        ///      a\\b -> a\b
 2015        ///      a\b -> ab
 2016        ///
 2017        /// We don't build the stringbuilder unless we find a ' or a \.  If we find a ' or a \, we
 2018        /// always build a stringbuilder because we need to remove the ' or \.
 2019        /// </summary>
 2020        private static string UnescapeNlsString(string str, int start, int end)
 2021        {
 02022            Debug.Assert(str != null);
 02023            Debug.Assert(start >= 0);
 02024            Debug.Assert(end >= 0);
 02025            StringBuilder? result = null;
 2026
 02027            for (int i = start; i < str.Length && i <= end; i++)
 2028            {
 02029                switch (str[i])
 2030                {
 2031                    case '\'':
 02032                        result ??= new StringBuilder(str, start, i - start, str.Length);
 02033                        break;
 2034                    case '\\':
 02035                        result ??= new StringBuilder(str, start, i - start, str.Length);
 02036                        ++i;
 02037                        if (i < str.Length)
 2038                        {
 02039                            result.Append(str[i]);
 2040                        }
 02041                        break;
 2042                    default:
 02043                        result?.Append(str[i]);
 2044                        break;
 2045                }
 2046            }
 2047
 02048            if (result == null)
 2049            {
 02050                return str.Substring(start, end - start + 1);
 2051            }
 2052
 02053            return result.ToString();
 2054        }
 2055
 2056        /// <summary>
 2057        /// Time format separator (ie: : in 12:39:00)
 2058        /// We calculate this from the provided time format
 2059        /// </summary>
 2060        private static string GetTimeSeparator(string format)
 2061        {
 2062            // Find the time separator so that we can pretend we know TimeSeparator.
 02063            return GetSeparator(format, "Hhms");
 2064        }
 2065
 2066        /// <summary>
 2067        /// Date format separator (ie: / in 9/1/03)
 2068        /// We calculate this from the provided short date
 2069        /// </summary>
 2070        private static string GetDateSeparator(string format)
 2071        {
 2072            // Find the date separator so that we can pretend we know DateSeparator.
 02073            return GetSeparator(format, "dyM");
 2074        }
 2075
 2076        private static string GetSeparator(string format, string timeParts)
 2077        {
 02078            int index = IndexOfTimePart(format, 0, timeParts);
 2079
 02080            if (index != -1)
 2081            {
 2082                // Found a time part, find out when it changes
 02083                char cTimePart = format[index];
 2084
 2085                do
 2086                {
 02087                    index++;
 02088                } while (index < format.Length && format[index] == cTimePart);
 2089
 02090                int separatorStart = index;
 2091
 2092                // Now we need to find the end of the separator
 02093                if (separatorStart < format.Length)
 2094                {
 02095                    int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
 02096                    if (separatorEnd != -1)
 2097                    {
 2098                        // From [separatorStart, count) is our string, except we need to unescape
 02099                        return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
 2100                    }
 2101                }
 2102            }
 2103
 02104            return string.Empty;
 2105        }
 2106
 2107        private static int IndexOfTimePart(string format, int startIndex, string timeParts)
 2108        {
 02109            Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
 02110            Debug.Assert(!timeParts.AsSpan().ContainsAny('\'', '\\'), "timeParts cannot include quote characters");
 02111            bool inQuote = false;
 02112            for (int i = startIndex; i < format.Length; ++i)
 2113            {
 2114                // See if we have a time Part
 02115                if (!inQuote && timeParts.Contains(format[i]))
 2116                {
 02117                    return i;
 2118                }
 02119                switch (format[i])
 2120                {
 2121                    case '\\':
 02122                        if (i + 1 < format.Length)
 2123                        {
 02124                            ++i;
 02125                            switch (format[i])
 2126                            {
 2127                                case '\'':
 2128                                case '\\':
 2129                                    break;
 2130                                default:
 02131                                    --i; // backup since we will move over this next
 02132                                    break;
 2133                            }
 2134                        }
 2135                        break;
 2136                    case '\'':
 02137                        inQuote = !inQuote;
 2138                        break;
 2139                }
 2140            }
 2141
 02142            return -1;
 2143        }
 2144
 2145        internal static bool IsCustomCultureId(int cultureId)
 2146        {
 02147            return cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
 2148        }
 2149
 2150        private string[] GetNativeDigits()
 2151        {
 02152            string[] result = NumberFormatInfo.s_asciiDigits;
 2153
 2154            // NLS LOCALE_SNATIVEDIGITS (array of 10 single character strings). In case of ICU, the buffer can be longer
 02155            string digits = GetLocaleInfoCoreUserOverride(LocaleStringData.Digits);
 2156
 2157            // if digits.Length < NumberFormatInfo.s_asciiDigits.Length means the native digits setting is messed up in 
 2158            // Instead of throwing IndexOutOfRangeException that will be hard to diagnose after the fact, we'll fall bac
 02159            if (digits.Length < NumberFormatInfo.s_asciiDigits.Length)
 2160            {
 02161                return result;
 2162            }
 2163
 2164            // In ICU we separate the digits with the '\uFFFF' character
 2165
 02166            if (digits.StartsWith("0\uFFFF1\uFFFF2\uFFFF3\uFFFF4\uFFFF5\uFFFF6\uFFFF7\uFFFF8\uFFFF9\uFFFF", StringCompar
 02167                digits.StartsWith("0123456789", StringComparison.Ordinal))  // NLS common cases
 2168            {
 02169                return result;
 2170            }
 2171
 2172            // Non-ASCII digits
 2173
 2174            // Check if values coming from ICU separated by 0xFFFF
 02175            int ffffPos = digits.IndexOf('\uFFFF');
 2176
 02177            result = new string[10];
 02178            if (ffffPos < 0) // NLS case
 2179            {
 02180                for (int i = 0; i < result.Length; i++)
 2181                {
 02182                    result[i] = char.ToString(digits[i]);
 2183                }
 2184
 02185                return result;
 2186            }
 2187
 2188            // ICU case
 2189
 02190            int start = 0;
 02191            int index = 0;
 2192
 2193            do
 2194            {
 02195                result[index++] = digits.Substring(start, ffffPos - start);
 02196                start = ++ffffPos;
 02197                while ((uint)ffffPos < (uint)digits.Length && digits[ffffPos] != '\uFFFF')
 2198                {
 02199                    ffffPos++;
 2200                }
 2201
 02202            } while (ffffPos < digits.Length && index < 10);
 2203
 02204            Debug.Assert(index >= 10, $"Couldn't read native digits for '{_sWindowsName}' successfully.");
 2205
 02206            return index < 10 ? NumberFormatInfo.s_asciiDigits : result;
 2207        }
 2208
 2209        internal void GetNFIValues(NumberFormatInfo nfi)
 2210        {
 02211            if (GlobalizationMode.InvariantNoLoad || IsInvariantCulture)
 2212            {
 02213                nfi._positiveSign = _sPositiveSign!;
 02214                nfi._negativeSign = _sNegativeSign!;
 2215
 02216                nfi._numberGroupSeparator = _sThousandSeparator!;
 02217                nfi._numberDecimalSeparator = _sDecimalSeparator!;
 02218                nfi._numberDecimalDigits = _iDigits;
 02219                nfi._numberNegativePattern = _iNegativeNumber;
 2220
 02221                nfi._currencySymbol = _sCurrency!;
 02222                nfi._currencyGroupSeparator = _sMonetaryThousand!;
 02223                nfi._currencyDecimalSeparator = _sMonetaryDecimal!;
 02224                nfi._currencyDecimalDigits = _iCurrencyDigits;
 02225                nfi._currencyNegativePattern = _iNegativeCurrency;
 02226                nfi._currencyPositivePattern = _iCurrency;
 2227            }
 2228            else
 2229            {
 02230                Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated b
 2231                // String values
 02232                nfi._positiveSign = GetLocaleInfoCoreUserOverride(LocaleStringData.PositiveSign);
 02233                nfi._negativeSign = GetLocaleInfoCoreUserOverride(LocaleStringData.NegativeSign);
 2234
 02235                nfi._numberDecimalSeparator = GetLocaleInfoCoreUserOverride(LocaleStringData.DecimalSeparator);
 02236                nfi._numberGroupSeparator = GetLocaleInfoCoreUserOverride(LocaleStringData.ThousandSeparator);
 02237                nfi._currencyGroupSeparator = GetLocaleInfoCoreUserOverride(LocaleStringData.MonetaryThousandSeparator);
 02238                nfi._currencyDecimalSeparator = GetLocaleInfoCoreUserOverride(LocaleStringData.MonetaryDecimalSeparator)
 02239                nfi._currencySymbol = GetLocaleInfoCoreUserOverride(LocaleStringData.MonetarySymbol);
 2240
 2241                // Numeric values
 02242                nfi._numberDecimalDigits = GetLocaleInfoCoreUserOverride(LocaleNumberData.FractionalDigitsCount);
 02243                nfi._currencyDecimalDigits = GetLocaleInfoCoreUserOverride(LocaleNumberData.MonetaryFractionalDigitsCoun
 02244                nfi._currencyPositivePattern = GetLocaleInfoCoreUserOverride(LocaleNumberData.PositiveMonetaryNumberForm
 02245                nfi._currencyNegativePattern = GetLocaleInfoCoreUserOverride(LocaleNumberData.NegativeMonetaryNumberForm
 02246                nfi._numberNegativePattern = GetLocaleInfoCoreUserOverride(LocaleNumberData.NegativeNumberFormat);
 2247
 02248                nfi._nativeDigits = GetNativeDigits();
 2249
 02250                Debug.Assert(_sRealName != null);
 02251                nfi._digitSubstitution = ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(LocaleNumberData.DigitSubstitut
 2252            }
 2253
 2254            // Gather additional data
 02255            nfi._numberGroupSizes = NumberGroupSizes;
 02256            nfi._currencyGroupSizes = CurrencyGroupSizes;
 2257
 2258            // prefer the cached value since these do not have user overrides
 02259            nfi._percentNegativePattern = PercentNegativePattern;
 02260            nfi._percentPositivePattern = PercentPositivePattern;
 02261            nfi._percentSymbol = PercentSymbol;
 02262            nfi._perMilleSymbol = PerMilleSymbol;
 2263
 02264            nfi._negativeInfinitySymbol = NegativeInfinitySymbol;
 02265            nfi._positiveInfinitySymbol = PositiveInfinitySymbol;
 02266            nfi._nanSymbol = NaNSymbol;
 2267
 2268            // We don't have percent values, so use the number values
 02269            nfi._percentDecimalDigits = nfi._numberDecimalDigits;
 02270            nfi._percentDecimalSeparator = nfi._numberDecimalSeparator;
 02271            nfi._percentGroupSizes = nfi._numberGroupSizes;
 02272            nfi._percentGroupSeparator = nfi._numberGroupSeparator;
 2273
 2274            // Clean up a few odd values
 2275
 2276            // Windows usually returns an empty positive sign, but we like it to be "+"
 02277            if (string.IsNullOrEmpty(nfi._positiveSign))
 2278            {
 02279                nfi._positiveSign = "+";
 2280            }
 2281
 2282            // Special case for Italian.  The currency decimal separator in the control panel is the empty string. When 
 2283            // specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 be
 2284            // decimal point doesn't show up.  We'll just hack this here because our default currency format will never 
 02285            if (string.IsNullOrEmpty(nfi._currencyDecimalSeparator))
 2286            {
 02287                nfi._currencyDecimalSeparator = nfi._numberDecimalSeparator;
 2288            }
 02289        }
 2290
 2291        /// <remarks>
 2292        /// This is ONLY used for caching names and shouldn't be used for anything else
 2293        /// </remarks>
 02294        internal static string AnsiToLower(string testString) => TextInfo.ToLowerAsciiInvariant(testString);
 2295
 2296        private int GetLocaleInfoCore(LocaleNumberData type)
 2297        {
 2298            // This is never reached but helps illinker statically remove dependencies
 02299            if (GlobalizationMode.Invariant)
 02300                return 0;
 2301#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2302            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
 2303#else
 02304            return GlobalizationMode.UseNls ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
 2305#endif
 2306        }
 2307
 2308        private int GetLocaleInfoCoreUserOverride(LocaleNumberData type)
 2309        {
 2310            // This is never reached but helps illinker statically remove dependencies
 02311            if (GlobalizationMode.Invariant)
 02312                return 0;
 2313#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2314            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
 2315#else
 02316            return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
 2317#endif
 2318        }
 2319
 2320        private string GetLocaleInfoCoreUserOverride(LocaleStringData type)
 2321        {
 2322            // This is never reached but helps illinker statically remove dependencies
 02323            if (GlobalizationMode.Invariant)
 02324                return null!;
 2325
 2326#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2327            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
 2328#else
 02329            return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
 2330#endif
 2331        }
 2332
 2333        private string GetLocaleInfoCore(LocaleStringData type, string? uiCultureName = null)
 2334        {
 2335            // This is never reached but helps illinker statically remove dependencies
 02336            if (GlobalizationMode.Invariant)
 02337                return null!;
 2338
 2339#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2340            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type, uiCultureName) : IcuGetLocaleInfo(type, uiCultur
 2341#else
 02342            return GlobalizationMode.UseNls ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type, uiCultureName);
 2343#endif
 2344        }
 2345
 2346        private string GetLocaleInfoCore(string localeName, LocaleStringData type, string? uiCultureName = null)
 2347        {
 2348            // This is never reached but helps illinker statically remove dependencies
 02349            if (GlobalizationMode.Invariant)
 02350                return null!;
 2351
 2352#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2353            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(localeName, type, uiCultureName) : IcuGetLocaleInfo(lo
 2354#else
 02355            return GlobalizationMode.UseNls ? NlsGetLocaleInfo(localeName, type) : IcuGetLocaleInfo(localeName, type, ui
 2356#endif
 2357        }
 2358
 2359        private int[] GetLocaleInfoCoreUserOverride(LocaleGroupingData type)
 2360        {
 2361            // This is never reached but helps illinker statically remove dependencies
 02362            if (GlobalizationMode.Invariant)
 02363                return null!;
 2364
 2365#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 2366            return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
 2367#else
 02368            return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
 2369#endif
 2370        }
 2371
 2372        /// <remarks>
 2373        /// The numeric values of the enum members match their Win32 counterparts.  The CultureData Win32 PAL implementa
 2374        /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to L
 2375        /// </remarks>
 2376        private enum LocaleStringData : uint
 2377        {
 2378            /// <summary>localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZE
 2379            LocalizedDisplayName = 0x00000002,
 2380            /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOC
 2381            EnglishDisplayName = 0x00000072,
 2382            /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIV
 2383            NativeDisplayName = 0x00000073,
 2384            /// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALI
 2385            LocalizedLanguageName = 0x0000006f,
 2386            /// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
 2387            EnglishLanguageName = 0x00001001,
 2388            /// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
 2389            NativeLanguageName = 0x00000004,
 2390            /// <summary>localized name of country, eg "Germany" in UI language (corresponds to LOCALE_SLOCALIZEDCOUNTRY
 2391            LocalizedCountryName = 0x00000006,
 2392            /// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
 2393            EnglishCountryName = 0x00001002,
 2394            /// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
 2395            NativeCountryName = 0x00000008,
 2396            /// <summary>abbreviated language name (corresponds to LOCALE_SABBREVLANGNAME)</summary>
 2397            AbbreviatedWindowsLanguageName = 0x00000003,
 2398            /// <summary>list item separator (corresponds to LOCALE_SLIST)</summary>
 2399            ListSeparator = 0x0000000C,
 2400            /// <summary>decimal separator (corresponds to LOCALE_SDECIMAL)</summary>
 2401            DecimalSeparator = 0x0000000E,
 2402            /// <summary>thousand separator (corresponds to LOCALE_STHOUSAND)</summary>
 2403            ThousandSeparator = 0x0000000F,
 2404            /// <summary>native digits for 0-9, eg "0123456789" (corresponds to LOCALE_SNATIVEDIGITS)</summary>
 2405            Digits = 0x00000013,
 2406            /// <summary>local monetary symbol (corresponds to LOCALE_SCURRENCY)</summary>
 2407            MonetarySymbol = 0x00000014,
 2408            /// <summary>English currency name (corresponds to LOCALE_SENGCURRNAME)</summary>
 2409            CurrencyEnglishName = 0x00001007,
 2410            /// <summary>Native currency name (corresponds to LOCALE_SNATIVECURRNAME)</summary>
 2411            CurrencyNativeName = 0x00001008,
 2412            /// <summary>uintl monetary symbol (corresponds to LOCALE_SINTLSYMBOL)</summary>
 2413            Iso4217MonetarySymbol = 0x00000015,
 2414            /// <summary>monetary decimal separator (corresponds to LOCALE_SMONDECIMALSEP)</summary>
 2415            MonetaryDecimalSeparator = 0x00000016,
 2416            /// <summary>monetary thousand separator (corresponds to LOCALE_SMONTHOUSANDSEP)</summary>
 2417            MonetaryThousandSeparator = 0x00000017,
 2418            /// <summary>AM designator (corresponds to LOCALE_S1159)</summary>
 2419            AMDesignator = 0x00000028,
 2420            /// <summary>PM designator (corresponds to LOCALE_S2359)</summary>
 2421            PMDesignator = 0x00000029,
 2422            /// <summary>positive sign (corresponds to LOCALE_SPOSITIVESIGN)</summary>
 2423            PositiveSign = 0x00000050,
 2424            /// <summary>negative sign (corresponds to LOCALE_SNEGATIVESIGN)</summary>
 2425            NegativeSign = 0x00000051,
 2426            /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
 2427            Iso639LanguageTwoLetterName = 0x00000059,
 2428            /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO639LANGNAME2)</summary>
 2429            Iso639LanguageThreeLetterName = 0x00000067,
 2430            /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
 2431            Iso639LanguageName = 0x00000059,
 2432            /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO3166CTRYNAME)</summary>
 2433            Iso3166CountryName = 0x0000005A,
 2434            /// <summary>3 letter ISO country code (corresponds to LOCALE_SISO3166CTRYNAME2)</summary>
 2435            Iso3166CountryName2 = 0x00000068,   // 3 character ISO country name
 2436            /// <summary>Not a Number (corresponds to LOCALE_SNAN)</summary>
 2437            NaNSymbol = 0x00000069,
 2438            /// <summary>+ Infinity (corresponds to LOCALE_SPOSINFINITY)</summary>
 2439            PositiveInfinitySymbol = 0x0000006a,
 2440            /// <summary>- Infinity (corresponds to LOCALE_SNEGINFINITY)</summary>
 2441            NegativeInfinitySymbol = 0x0000006b,
 2442            /// <summary>Fallback name for resources (corresponds to LOCALE_SPARENT)</summary>
 2443            ParentName = 0x0000006d,
 2444            /// <summary>Fallback name for within the console (corresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
 2445            ConsoleFallbackName = 0x0000006e,
 2446            /// <summary>Returns the percent symbol (corresponds to LOCALE_SPERCENT)</summary>
 2447            PercentSymbol = 0x00000076,
 2448            /// <summary>Returns the permille (U+2030) symbol (corresponds to LOCALE_SPERMILLE)</summary>
 2449            PerMilleSymbol = 0x00000077
 2450        }
 2451
 2452        /// <remarks>
 2453        /// The numeric values of the enum members match their Win32 counterparts.  The CultureData Win32 PAL implementa
 2454        /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to L
 2455        /// </remarks>
 2456        private enum LocaleGroupingData : uint
 2457        {
 2458            /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
 2459            Digit = 0x00000010,
 2460            /// <summary>monetary grouping (corresponds to LOCALE_SMONGROUPING)</summary>
 2461            Monetary = 0x00000018,
 2462        }
 2463
 2464        /// <remarks>
 2465        /// The numeric values of the enum members match their Win32 counterparts.  The CultureData Win32 PAL implementa
 2466        /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to L
 2467        /// </remarks>
 2468        private enum LocaleNumberData : uint
 2469        {
 2470            /// <summary>language id (corresponds to LOCALE_ILANGUAGE)</summary>
 2471            LanguageId = 0x00000001,
 2472            /// <summary>geographical location id, (corresponds to LOCALE_IGEOID)</summary>
 2473            GeoId = 0x0000005B,
 2474            /// <summary>0 = context, 1 = none, 2 = national (corresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
 2475            DigitSubstitution = 0x00001014,
 2476            /// <summary>0 = metric, 1 = US (corresponds to LOCALE_IMEASURE)</summary>
 2477            MeasurementSystem = 0x0000000D,
 2478            /// <summary>number of fractional digits (corresponds to LOCALE_IDIGITS)</summary>
 2479            FractionalDigitsCount = 0x00000011,
 2480            /// <summary>negative number mode (corresponds to LOCALE_INEGNUMBER)</summary>
 2481            NegativeNumberFormat = 0x00001010,
 2482            /// <summary># local monetary digits (corresponds to LOCALE_ICURRDIGITS)</summary>
 2483            MonetaryFractionalDigitsCount = 0x00000019,
 2484            /// <summary>positive currency mode (corresponds to LOCALE_ICURRENCY)</summary>
 2485            PositiveMonetaryNumberFormat = 0x0000001B,
 2486            /// <summary>negative currency mode (corresponds to LOCALE_INEGCURR)</summary>
 2487            NegativeMonetaryNumberFormat = 0x0000001C,
 2488            /// <summary>type of calendar specifier (corresponds to LOCALE_ICALENDARTYPE)</summary>
 2489            CalendarType = 0x00001009,
 2490            /// <summary>first day of week specifier (corresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
 2491            FirstDayOfWeek = 0x0000100C,
 2492            /// <summary>first week of year specifier (corresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
 2493            FirstWeekOfYear = 0x0000100D,
 2494            /// <summary>
 2495            /// Returns one of the following 4 reading layout values:
 2496            ///  0 - Left to right (eg en-US)
 2497            ///  1 - Right to left (eg arabic locales)
 2498            ///  2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
 2499            ///  3 - Vertical top to bottom with columns proceeding to the right
 2500            /// (corresponds to LOCALE_IREADINGLAYOUT)
 2501            /// </summary>
 2502            ReadingLayout = 0x00000070,
 2503            /// <summary>Returns 0-11 for the negative percent format (corresponds to LOCALE_INEGATIVEPERCENT)</summary>
 2504            NegativePercentFormat = 0x00000074,
 2505            /// <summary>Returns 0-3 for the positive percent format (corresponds to LOCALE_IPOSITIVEPERCENT)</summary>
 2506            PositivePercentFormat = 0x00000075,
 2507            /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
 2508            OemCodePage = 0x0000000B,
 2509            /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
 2510            AnsiCodePage = 0x00001004,
 2511            /// <summary>default mac code page (corresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
 2512            MacCodePage = 0x00001011,
 2513            /// <summary>default ebcdic code page (corresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
 2514            EbcdicCodePage = 0x00001012,
 2515        }
 2516    }
 2517}
 2518

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7
 8namespace System.Globalization
 9{
 10    internal sealed partial class CultureData
 11    {
 12        // ICU constants
 13        private const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value
 14        private const int ICU_ULOC_FULLNAME_CAPACITY = 157;           // max size of locale name
 15        private const int WINDOWS_MAX_COLLATION_NAME_LENGTH = 8;      // max collation name length in the culture name
 16
 17        /// <summary>
 18        /// Process the locale name that ICU returns and convert it to the format that .NET expects.
 19        /// </summary>
 20        /// <param name="name">The locale name that ICU returns.</param>
 21        /// <param name="extension">The extension part in the original culture name.</param>
 22        /// <param name="originalName">The original input culture name.</param>
 23        /// <param name="collationStart">The index of the collation in the name.</param>
 24        /// <remarks>
 25        /// BCP 47 specifications allow for extensions in the locale name, following the format language-script-region-e
 26        /// not all extensions supported by ICU are supported in .NET. In the locale name, extensions are separated from
 27        /// In .NET, only the collation extension is supported. If the name includes a collation extension, it will be p
 28        /// For example, en-US-u-co-search would be converted to the ICU name en_US@collation=search, which would then b
 29        /// All extensions in the ICU names start with @. When normalizing the name to the .NET format, we retain the ex
 30        /// between names with extensions and those without. For example, we may have a name like en-US and en-US-u-xx. 
 31        /// we still include it in the name to distinguish it from the name without the extension.
 32        /// </remarks>
 33        private static unsafe string NormalizeCultureName(string name, ReadOnlySpan<char> extension, string originalName
 34        {
 035            Debug.Assert(name is not null);
 036            Debug.Assert(name.Length <= ICU_ULOC_FULLNAME_CAPACITY);
 37
 038            collationStart = -1;
 039            bool changed = false;
 040            Span<char> buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
 041            int bufferIndex = 0;
 42
 43            // Using `Undetermined` language (e.g. `und-US`), the returned name from ICU getName is `_US`. We return bac
 044            if (name.Length > 1 && (name[0] == '-' || name[0] == '_') && originalName.StartsWith("und", StringComparison
 45            {
 046                buffer[bufferIndex++] = 'u';
 047                buffer[bufferIndex++] = 'n';
 048                buffer[bufferIndex++] = 'd';
 49
 050                changed = true;
 51            }
 52
 053            for (int i = 0; i < name.Length && bufferIndex < ICU_ULOC_FULLNAME_CAPACITY; i++)
 54            {
 055                char c = name[i];
 056                if (c == '-' && i < name.Length - 1 && name[i + 1] == '-')
 57                {
 58                    // ICU changes names like `qps_plocm` (one underscore) to `qps__plocm` (two underscores)
 59                    // The reason this occurs is because, while ICU canonicalizing, ulocimp_getCountry returns an empty 
 60                    // But append an extra '_' thinking that country code was in-fact appended (for the empty string val
 61                    // Before processing, the name qps__plocm will be converted to its .NET name equivalent, which is qp
 062                    changed = true;
 063                    buffer[bufferIndex++] = '-';
 064                    i++;
 65                }
 066                else if (c == '@')
 67                {
 068                    changed = true;
 69
 070                    if (!extension.IsEmpty && extension.TryCopyTo(buffer.Slice(bufferIndex)))
 71                    {
 072                        bufferIndex += extension.Length;
 73                    }
 74
 075                    int collationIndex = name.IndexOf("collation=", i + 1, StringComparison.Ordinal);
 076                    if (collationIndex > 0)
 77                    {
 078                        collationIndex += "collation=".Length;
 79
 80                        // format of the locale properties is @key=value;collation=collationName;key=value;key=value
 081                        int endOfCollation = name.IndexOf(';', collationIndex);
 082                        if (endOfCollation < 0)
 83                        {
 084                            endOfCollation = name.Length;
 85                        }
 86
 087                        int length = Math.Min(WINDOWS_MAX_COLLATION_NAME_LENGTH, endOfCollation - collationIndex);  // W
 088                        if (buffer.Length - bufferIndex >= length + 1)
 89                        {
 090                            collationStart = bufferIndex;
 091                            buffer[bufferIndex++] = '_';
 092                            name.AsSpan(collationIndex, length).CopyTo(buffer.Slice(bufferIndex));
 093                            bufferIndex += length;
 94                        }
 95                    }
 96
 97                    // done getting all parts can be supported in the .NET culture names.
 098                    break;
 99                }
 100                else
 101                {
 0102                    buffer[bufferIndex++] = name[i];
 103                }
 104            }
 105
 0106            return changed ? new string(buffer.Slice(0, bufferIndex)) : name;
 107        }
 108
 109        /// <summary>
 110        /// This method uses the sRealName field (which is initialized by the constructor before this is called) to
 111        /// initialize the rest of the state of CultureData based on the underlying OS globalization library.
 112        /// </summary>
 113        private bool InitIcuCultureDataCore()
 114        {
 0115            Debug.Assert(_sRealName != null);
 0116            Debug.Assert(!GlobalizationMode.Invariant);
 117
 118            const string ICU_COLLATION_KEYWORD = "@collation=";
 0119            string realNameBuffer = _sRealName;
 120
 121            // Basic validation
 0122            if (!IsValidCultureName(realNameBuffer, out var index, out int indexOfExtensions))
 123            {
 0124                return false;
 125            }
 126
 127            // Replace _ (alternate sort) with @collation= for ICU
 0128            if (index > 0)
 129            {
 0130                ReadOnlySpan<char> alternateSortName = realNameBuffer.AsSpan(index + 1);
 0131                realNameBuffer = string.Concat(realNameBuffer.AsSpan(0, index), ICU_COLLATION_KEYWORD, alternateSortName
 132            }
 133
 134#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 135            if (GlobalizationMode.Hybrid)
 136            {
 137                _sWindowsName = GetLocaleNameNative(realNameBuffer);
 138                if (_sWindowsName == null || _sWindowsName.Length == 0)
 139                {
 140                    return false;
 141                }
 142            }
 143            else
 144#endif
 145            {
 146                // Get the locale name from ICU
 0147                if (!GetLocaleName(realNameBuffer, out _sWindowsName))
 148                {
 0149                    return false;
 150                }
 151            }
 152
 0153            Debug.Assert(_sWindowsName != null);
 154
 0155            _sRealName = NormalizeCultureName(_sWindowsName, indexOfExtensions > 0 ? _sRealName.AsSpan(indexOfExtensions
 156
 0157            _iLanguage = LCID;
 0158            if (_iLanguage == 0)
 159            {
 0160                _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
 161            }
 0162            _bNeutral = TwoLetterISOCountryName.Length == 0;
 0163            _sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName;
 164
 165            // Remove the sort from sName unless custom culture
 166            // To ensure compatibility, it is necessary to allow the creation of cultures like zh_CN (using ICU notation
 0167            _sName = collationStart < 0 || _bNeutral ? _sRealName : _sRealName.Substring(0, collationStart);
 168
 0169            return true;
 170        }
 171
 172        internal static unsafe bool GetLocaleName(string localeName, out string? windowsName)
 173        {
 174            // Get the locale name from ICU
 0175            char* buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
 0176            if (!Interop.Globalization.GetLocaleName(localeName, buffer, ICU_ULOC_FULLNAME_CAPACITY))
 177            {
 0178                windowsName = null;
 0179                return false; // fail
 180            }
 181
 182            // Success - use the locale name returned which may be different than realNameBuffer (casing)
 0183            windowsName = new string(buffer); // the name passed to subsequent ICU calls
 0184            return true;
 185        }
 186
 187        internal static unsafe bool GetDefaultLocaleName([NotNullWhen(true)] out string? windowsName)
 188        {
 189#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 190            if (GlobalizationMode.Hybrid)
 191            {
 192                windowsName = Interop.Globalization.GetDefaultLocaleNameNative();
 193                return windowsName != null && windowsName.Length > 0;
 194            }
 195            else
 196#endif
 197            {
 198                // Get the default (system) locale name from ICU
 199                char* buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
 200                if (!Interop.Globalization.GetDefaultLocaleName(buffer, ICU_ULOC_FULLNAME_CAPACITY))
 201                {
 202                    windowsName = null;
 203                    return false; // fail
 204                }
 205
 206                // Success - use the locale name returned which may be different than realNameBuffer (casing)
 207                windowsName = new string(buffer); // the name passed to subsequent ICU calls
 208                return true;
 209            }
 210        }
 211
 212        private string IcuGetLocaleInfo(LocaleStringData type, string? uiCultureName = null)
 213        {
 0214            Debug.Assert(!GlobalizationMode.Invariant);
 0215            Debug.Assert(!GlobalizationMode.UseNls);
 0216            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo] Expected _sWindowsName to be populated a
 217#if TARGET_BROWSER && !FEATURE_WASM_MANAGED_THREADS
 218            if (type == LocaleStringData.NativeDisplayName)
 219            {
 220                return JSGetNativeDisplayName(_sWindowsName, uiCultureName ?? _sWindowsName);
 221            }
 222#endif
 0223            return IcuGetLocaleInfo(_sWindowsName, type, uiCultureName);
 224        }
 225
 226        // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
 227        // "windows" name, which can be specific for downlevel (< windows 7) os's.
 228        private unsafe string IcuGetLocaleInfo(string localeName, LocaleStringData type, string? uiCultureName = null)
 229        {
 0230            Debug.Assert(!GlobalizationMode.UseNls);
 0231            Debug.Assert(localeName != null, "[CultureData.IcuGetLocaleInfo] Expected localeName to be not be null");
 232
 233            switch (type)
 234            {
 235                case LocaleStringData.NegativeInfinitySymbol:
 236                    // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign
 0237                    return IcuGetLocaleInfo(localeName, LocaleStringData.NegativeSign) +
 0238                        IcuGetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol);
 239            }
 240
 0241            char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY];
 0242            bool result = Interop.Globalization.GetLocaleInfoString(localeName, (uint)type, buffer, ICU_ULOC_KEYWORD_AND
 0243            if (!result)
 244            {
 245                // Failed, just use empty string
 0246                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed");
 247                return string.Empty;
 248            }
 0249            return new string(buffer);
 250        }
 251
 252        private int IcuGetLocaleInfo(LocaleNumberData type)
 253        {
 0254            Debug.Assert(!GlobalizationMode.UseNls);
 255
 0256            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleNumberData)] Expected _sWindowsName
 257
 258            switch (type)
 259            {
 260                case LocaleNumberData.CalendarType:
 261                    // returning 0 will cause the first supported calendar to be returned, which is the preferred calend
 0262                    return 0;
 263            }
 264
 265
 0266            int value = 0;
 0267            bool result = Interop.Globalization.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value);
 0268            if (!result)
 269            {
 270                // Failed, just use 0
 0271                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleNumberData)] failed");
 272            }
 273
 0274            return value;
 275        }
 276
 277        private int[] IcuGetLocaleInfo(LocaleGroupingData type)
 278        {
 0279            Debug.Assert(!GlobalizationMode.UseNls);
 0280            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleGroupingData)] Expected _sWindowsNa
 281
 0282            int primaryGroupingSize = 0;
 0283            int secondaryGroupingSize = 0;
 0284            bool result = Interop.Globalization.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupin
 0285            if (!result)
 286            {
 0287                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleGroupingData type)] failed");
 288            }
 289
 0290            if (secondaryGroupingSize == 0)
 291            {
 0292                return [primaryGroupingSize];
 293            }
 294
 0295            return [primaryGroupingSize, secondaryGroupingSize];
 296        }
 297
 0298        private string IcuGetTimeFormatString() => IcuGetTimeFormatString(shortFormat: false);
 299
 300        private unsafe string IcuGetTimeFormatString(bool shortFormat)
 301        {
 0302            Debug.Assert(!GlobalizationMode.UseNls);
 0303            Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsN
 304
 305            ReadOnlySpan<char> span;
 306#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 307            if (GlobalizationMode.Hybrid)
 308            {
 309                string res = Interop.Globalization.GetLocaleTimeFormatNative(_sWindowsName, shortFormat);
 310                if (string.IsNullOrEmpty(res))
 311                {
 312                    Debug.Fail("[CultureData.GetTimeFormatString(bool shortFormat)] Failed");
 313                    return string.Empty;
 314                }
 315                span = res.AsSpan();
 316            }
 317            else
 318#endif
 319            {
 0320                char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY];
 0321                bool result = Interop.Globalization.GetLocaleTimeFormat(_sWindowsName, shortFormat, buffer, ICU_ULOC_KEY
 0322                if (!result)
 323                {
 324                    // Failed, just use empty string
 0325                    Debug.Fail("[CultureData.GetTimeFormatString(bool shortFormat)] Failed");
 326                    return string.Empty;
 327                }
 0328                span = new ReadOnlySpan<char>(buffer, ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY);
 0329                span = span.Slice(0, span.IndexOf('\0'));
 330            }
 331
 0332            return ConvertIcuTimeFormatString(span);
 333        }
 334
 335        // no support to lookup by region name, other than the hard-coded list in CultureData
 336        private static CultureData? IcuGetCultureDataFromRegionName() => null;
 337
 338        private string IcuGetLanguageDisplayName(string cultureName)
 339        {
 340#if TARGET_BROWSER
 341            return JSGetNativeDisplayName(CultureInfo.CurrentUICulture.Name, cultureName);
 342#else
 0343            return IcuGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName, CultureInfo.CurrentUICulture.Nam
 344#endif
 345        }
 346
 347        // use the fallback which is to return NativeName
 348        private static string? IcuGetRegionDisplayName() => null;
 349
 350        internal static bool IcuIsEnsurePredefinedLocaleName(string name)
 351        {
 0352            Debug.Assert(!GlobalizationMode.UseNls);
 353#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 354            if (GlobalizationMode.Hybrid)
 355            {
 356                return Interop.Globalization.IsPredefinedLocaleNative(name);
 357            }
 358            else
 359#endif
 360            {
 0361                return Interop.Globalization.IsPredefinedLocale(name);
 362            }
 363        }
 364
 365        private static unsafe string ConvertIcuTimeFormatString(ReadOnlySpan<char> icuFormatString)
 366        {
 0367            Debug.Assert(icuFormatString.Length < ICU_ULOC_FULLNAME_CAPACITY);
 0368            Span<char> result = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
 369
 0370            bool amPmAdded = false;
 0371            int resultPos = 0;
 372
 0373            for (int i = 0; i < icuFormatString.Length; i++)
 374            {
 0375                char current = icuFormatString[i];
 376                switch (current)
 377                {
 378                    case '\\':
 379                        // The ICU format does not use backslashes to escape while the .NET format does
 0380                        result[resultPos++] = '\\';
 0381                        result[resultPos++] = '\\';
 0382                        break;
 383                    case '\'':
 384                        static bool HandleQuoteLiteral(ReadOnlySpan<char> icuFormatString, ref int i, Span<char> result,
 385                        {
 0386                            if (i + 1 < icuFormatString.Length && icuFormatString[i + 1] == '\'')
 387                            {
 0388                                result[resultPos++] = '\\';
 0389                                result[resultPos++] = '\'';
 0390                                i++;
 0391                                return true;
 392                            }
 0393                            result[resultPos++] = '\'';
 0394                            return false;
 395                        }
 396
 0397                        if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos))
 398                        {
 399                            break;
 400                        }
 0401                        i++;
 0402                        for (; i < icuFormatString.Length; i++)
 403                        {
 0404                            current = icuFormatString[i];
 0405                            if (current == '\'')
 406                            {
 0407                                if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos))
 408                                {
 0409                                    continue;
 410                                }
 411                                break;
 412                            }
 0413                            if (current == '\\')
 414                            {
 415                                // The same backslash escaping mentioned above
 0416                                result[resultPos++] = '\\';
 417                            }
 0418                            result[resultPos++] = current;
 419                        }
 0420                        break;
 421
 422                    case 'H':
 423                    case 'h':
 424                    case 'm':
 425                    case 's':
 0426                        result[resultPos++] = current;
 0427                        break;
 428                    case 'a': // AM/PM
 429                    case 'b': // am, pm, noon, midnight
 430                    case 'B': // flexible day periods
 0431                        if (!amPmAdded)
 432                        {
 0433                            amPmAdded = true;
 0434                            result[resultPos++] = 't';
 0435                            result[resultPos++] = 't';
 436                        }
 0437                        break;
 438
 439                    default:
 440                        // Characters that are not ASCII letters are literal texts
 0441                        if (!char.IsAsciiLetter(current))
 442                        {
 0443                            result[resultPos++] = current;
 444                        }
 445                        break;
 446                }
 447            }
 448
 0449            return result.Slice(0, resultPos).ToString();
 450        }
 451
 452        private static int IcuLocaleNameToLCID(string cultureName)
 453        {
 0454            Debug.Assert(!GlobalizationMode.Invariant);
 0455            Debug.Assert(!GlobalizationMode.UseNls);
 456
 0457            int lcid = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.Lcid);
 0458            return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid;
 459        }
 460
 461        private static int IcuGetGeoId(string cultureName)
 462        {
 0463            Debug.Assert(!GlobalizationMode.UseNls);
 0464            int geoId = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.GeoId);
 0465            return geoId == -1 ? Invariant.GeoId : geoId;
 466        }
 467
 468        private const uint DigitSubstitutionMask = 0x0000FFFF;
 469        private const uint ListSeparatorMask     = 0xFFFF0000;
 470
 471        private static int IcuGetDigitSubstitution(string cultureName)
 472        {
 0473            Debug.Assert(!GlobalizationMode.UseNls);
 0474            int digitSubstitution = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.DigitSubstitu
 0475            return digitSubstitution == -1 ? (int)DigitShapes.None : (int)(digitSubstitution & DigitSubstitutionMask);
 476        }
 477
 478        private static string IcuGetListSeparator(string? cultureName)
 479        {
 0480            Debug.Assert(!GlobalizationMode.UseNls);
 0481            Debug.Assert(cultureName != null);
 482
 0483            int separator = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.DigitSubstitutionOrLi
 0484            if (separator != -1)
 485            {
 0486                switch (separator & ListSeparatorMask)
 487                {
 488                    case IcuLocaleData.CommaSep:
 0489                        return ",";
 490
 491                    case IcuLocaleData.SemicolonSep:
 0492                        return ";";
 493
 494                    case IcuLocaleData.ArabicCommaSep:
 0495                        return "\u060C";
 496
 497                    case IcuLocaleData.ArabicSemicolonSep:
 0498                        return "\u061B";
 499
 500                    case IcuLocaleData.DoubleCommaSep:
 0501                        return ",,";
 502
 503                    default:
 0504                        Debug.Fail("[CultureData.IcuGetListSeparator] Unexpected ListSeparator value.");
 505                        break;
 506                }
 507            }
 508
 0509            return ","; // default separator
 510        }
 511
 512        private static string IcuGetThreeLetterWindowsLanguageName(string cultureName)
 513        {
 0514            Debug.Assert(!GlobalizationMode.UseNls);
 0515            return IcuLocaleData.GetThreeLetterWindowsLanguageName(cultureName) ?? "ZZZ" /* default lang name */;
 516        }
 517
 518        private static CultureInfo[] IcuEnumCultures(CultureTypes types)
 519        {
 0520            Debug.Assert(!GlobalizationMode.Invariant);
 0521            Debug.Assert(!GlobalizationMode.UseNls);
 522
 0523            if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0)
 524            {
 0525                return [];
 526            }
 527
 528            int bufferLength;
 529#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 530            if (GlobalizationMode.Hybrid)
 531            {
 532                bufferLength = Interop.Globalization.GetLocalesNative(null, 0);
 533            }
 534            else
 535#endif
 536            {
 0537                bufferLength = Interop.Globalization.GetLocales(null, 0);
 538            }
 539
 0540            if (bufferLength <= 0)
 541            {
 0542                return [];
 543            }
 544
 0545            char[] chars = new char[bufferLength];
 546
 547#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 548            if (GlobalizationMode.Hybrid)
 549            {
 550                bufferLength = Interop.Globalization.GetLocalesNative(chars, bufferLength);
 551            }
 552            else
 553#endif
 554            {
 0555                bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
 556            }
 557
 0558            if (bufferLength <= 0)
 559            {
 0560                return [];
 561            }
 562
 0563            bool enumNeutrals   = (types & CultureTypes.NeutralCultures) != 0;
 0564            bool enumSpecifics = (types & CultureTypes.SpecificCultures) != 0;
 565
 0566            List<CultureInfo> list = new List<CultureInfo>();
 0567            if (enumNeutrals)
 568            {
 0569                list.Add(CultureInfo.InvariantCulture);
 570            }
 571
 0572            int index = 0;
 0573            while (index < bufferLength)
 574            {
 0575                int length = (int)chars[index++];
 0576                if (index + length <= bufferLength)
 577                {
 0578                    CultureInfo ci = CultureInfo.GetCultureInfo(new string(chars, index, length));
 0579                    if ((enumNeutrals && ci.IsNeutralCulture) || (enumSpecifics && !ci.IsNeutralCulture))
 580                    {
 0581                        list.Add(ci);
 582                    }
 583                }
 584
 0585                index += length;
 586            }
 587
 0588            return list.ToArray();
 589        }
 590
 591        private static string IcuGetConsoleFallbackName(string cultureName)
 592        {
 0593            Debug.Assert(!GlobalizationMode.UseNls);
 0594            return IcuLocaleData.GetConsoleUICulture(cultureName);
 595        }
 596
 597        /// <summary>
 598        /// Implementation of culture name validation.
 599        /// </summary>
 600        /// <remarks>
 601        /// This is a fast approximate implementation based on BCP47 spec. It covers only parts of
 602        /// the spec; such that, when it returns false, the input is definitely in incorrect format.
 603        /// However, it returns true for some characters which are not allowed by the spec. It also
 604        /// returns true for some inputs where spec specifies the lengths of subtags, but we are not
 605        /// validating subtags individually to keep algorithm's computational complexity at O(n).
 606        ///
 607        /// Rules of implementation:
 608        /// * Allow only letters, digits, - and '_' or \0 (NULL is for backward compatibility).
 609        /// * Allow input length of zero (for invariant culture) or otherwise greater than 1 and less than or equal Loca
 610        /// * Disallow input that starts or ends with '-' or '_'.
 611        /// * Disallow input that has any combination of consecutive '-' or '_'.
 612        /// * Disallow input that has multiple '_'.
 613        ///
 614        /// The IsValidCultureName method also identifies the presence of any extensions in the name (such as -u- or -t-
 615        /// This is necessary because we need to append the extensions to the name when normalizing it to the .NET forma
 616        /// </remarks>
 617        private static bool IsValidCultureName(string subject, out int indexOfUnderscore, out int indexOfExtensions)
 618        {
 0619            indexOfUnderscore = -1;
 0620            indexOfExtensions = -1;
 621
 0622            if (subject.Length == 0) return true; // Invariant Culture
 0623            if (subject.Length == 1 || subject.Length > LocaleNameMaxLength) return false;
 624
 0625            bool seenUnderscore = false;
 0626            for (int i = 0; i < subject.Length; ++i)
 627            {
 0628                char c = subject[i];
 629
 0630                if (char.IsAsciiLetterOrDigit(c) || c == '\0')
 631                {
 632                    continue;
 633                }
 634
 0635                if (c == '_' || c == '-')
 636                {
 0637                    if (i == 0 || i == subject.Length - 1) return false;
 0638                    if (subject[i - 1] == '_' || subject[i - 1] == '-') return false;
 0639                    if (c == '_')
 640                    {
 0641                        if (seenUnderscore) return false; // only one _ is allowed
 0642                        seenUnderscore = true;
 0643                        indexOfUnderscore = i;
 644                    }
 645                    else
 646                    {
 0647                        if (indexOfExtensions < 0 && i < subject.Length - 2 && (subject[i + 1] is 'u' or 't') && subject
 648                        {
 0649                            if (subject[i + 1] == 't' || i >= subject.Length - 6 || subject[i + 3] != 'c' || subject[i +
 650                            {
 0651                                indexOfExtensions = i;
 652                            }
 653                        }
 654                    }
 655                }
 656                else
 657                {
 0658                    return false;
 659                }
 660            }
 661
 0662            return true;
 663        }
 664    }
 665}
 666

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using System.Text;
 10
 11namespace System.Globalization
 12{
 13    internal sealed partial class CultureData
 14    {
 15        // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned
 16        // data as either and Int or string.
 17        internal static unsafe string? GetLocaleInfoEx(string localeName, uint field)
 18        {
 19            // REVIEW: Determine the maximum size for the buffer
 20            const int BUFFER_SIZE = 530;
 21
 022            char* pBuffer = stackalloc char[BUFFER_SIZE];
 023            int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE);
 024            if (resultCode > 0)
 25            {
 026                return new string(pBuffer);
 27            }
 28
 029            return null;
 30        }
 31
 32        internal static unsafe int GetLocaleInfoExInt(string localeName, uint field)
 33        {
 034            field |= Interop.Kernel32.LOCALE_RETURN_NUMBER;
 035            int value = 0;
 036            GetLocaleInfoEx(localeName, field, (char*)&value, sizeof(int));
 037            return value;
 38        }
 39
 40        internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData)
 41        {
 042            Debug.Assert(!GlobalizationMode.Invariant);
 43
 044            return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData);
 45        }
 46
 47        private string NlsGetLocaleInfo(LocaleStringData type)
 48        {
 049            Debug.Assert(ShouldUseUserOverrideNlsData);
 050            Debug.Assert(_sRealName != null, "[CultureData.DoGetLocaleInfo] Expected _sRealName to be populated by alrea
 051            return NlsGetLocaleInfo(_sRealName, type);
 52        }
 53
 54        // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
 55        // "windows" name, which can be specific for downlevel (< windows 7) os's.
 56        private string NlsGetLocaleInfo(string localeName, LocaleStringData type)
 57        {
 058            Debug.Assert(ShouldUseUserOverrideNlsData);
 059            uint lctype = (uint)type;
 60
 061            return GetLocaleInfoFromLCType(localeName, lctype, _bUseOverrides);
 62        }
 63
 64        private int NlsGetLocaleInfo(LocaleNumberData type)
 65        {
 66            Debug.Assert(IsWin32Installed);
 067            uint lctype = (uint)type;
 68
 69            // Fix lctype if we don't want overrides
 070            if (!_bUseOverrides)
 71            {
 072                lctype |= Interop.Kernel32.LOCALE_NOUSEROVERRIDE;
 73            }
 74
 75            // Ask OS for data, note that we presume it returns success, so we have to know that
 76            // sWindowsName is valid before calling.
 077            Debug.Assert(_sRealName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sRealName to be populated by al
 078            return GetLocaleInfoExInt(_sRealName, lctype);
 79        }
 80
 81        private int[] NlsGetLocaleInfo(LocaleGroupingData type)
 82        {
 083            Debug.Assert(ShouldUseUserOverrideNlsData);
 084            Debug.Assert(_sRealName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sRealName to be populated by al
 085            return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sRealName, (uint)type, _bUseOverrides));
 86        }
 87
 88        internal static bool NlsIsEnsurePredefinedLocaleName(string name)
 89        {
 090            Debug.Assert(GlobalizationMode.UseNls);
 091            return GetLocaleInfoExInt(name, Interop.Kernel32.LOCALE_ICONSTRUCTEDLOCALE) != 1;
 92        }
 93
 94        private string? NlsGetTimeFormatString()
 95        {
 096            Debug.Assert(ShouldUseUserOverrideNlsData);
 097            Debug.Assert(_sRealName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sRealName to be populated by al
 098            return ReescapeWin32String(GetLocaleInfoFromLCType(_sRealName, Interop.Kernel32.LOCALE_STIMEFORMAT, _bUseOve
 99        }
 100
 101        private int NlsGetFirstDayOfWeek()
 102        {
 0103            Debug.Assert(ShouldUseUserOverrideNlsData);
 0104            Debug.Assert(_sRealName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sRealName to be populated by al
 105
 0106            int result = GetLocaleInfoExInt(_sRealName, Interop.Kernel32.LOCALE_IFIRSTDAYOFWEEK | (!_bUseOverrides ? Int
 107
 108            // Win32 and .NET disagree on the numbering for days of the week, so we have to convert.
 0109            return ConvertFirstDayOfWeekMonToSun(result);
 110        }
 111
 112        // Enumerate all system cultures and then try to find out which culture has
 113        // region name match the requested region name
 114        private static CultureData? NlsGetCultureDataFromRegionName(string regionName)
 115        {
 0116            Debug.Assert(!GlobalizationMode.Invariant);
 0117            Debug.Assert(GlobalizationMode.UseNls);
 0118            Debug.Assert(regionName != null);
 119
 120            EnumLocaleData context;
 0121            context.cultureName = null;
 0122            context.regionName = regionName;
 123
 124            unsafe
 125            {
 0126                Interop.Kernel32.EnumSystemLocalesEx(&EnumSystemLocalesProc, Interop.Kernel32.LOCALE_SPECIFICDATA | Inte
 127            }
 128
 0129            if (context.cultureName != null)
 130            {
 131                // we got a matched culture
 0132                return GetCultureData(context.cultureName, true);
 133            }
 134
 0135            return null;
 136        }
 137
 138        private string NlsGetLanguageDisplayName(string cultureName)
 139        {
 0140            Debug.Assert(GlobalizationMode.UseNls);
 141
 142            // Usually the UI culture shouldn't be different than what we got from WinRT except
 143            // if DefaultThreadCurrentUICulture was set
 144            CultureInfo? ci;
 145
 0146            if (CultureInfo.DefaultThreadCurrentUICulture != null &&
 0147                ((ci = CultureInfo.GetUserDefaultCulture()) != null) &&
 0148                !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
 149            {
 0150                return NativeName;
 151            }
 152            else
 153            {
 0154                return NlsGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
 155            }
 156        }
 157
 158        private string NlsGetRegionDisplayName()
 159        {
 0160            Debug.Assert(GlobalizationMode.UseNls);
 161
 162            // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
 163            // otherwise, we use the native name as we don't carry resources for the region display names anyway.
 0164            if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
 165            {
 0166                return NlsGetLocaleInfo(LocaleStringData.LocalizedCountryName);
 167            }
 168
 0169            return NativeCountryName;
 170        }
 171
 172        // PAL methods end here.
 173
 174        private static string GetLocaleInfoFromLCType(string localeName, uint lctype, bool useUserOverride)
 175        {
 0176            Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoFromLCType] Expected localeName to be not be nul
 177
 178            // Fix lctype if we don't want overrides
 0179            if (!useUserOverride)
 180            {
 0181                lctype |= Interop.Kernel32.LOCALE_NOUSEROVERRIDE;
 182            }
 183
 184            // Ask OS for data
 185            // Failed? Just use empty string
 0186            return GetLocaleInfoEx(localeName, lctype) ?? string.Empty;
 187        }
 188
 189        /// <summary>
 190        /// Reescape a Win32 style quote string as a NLS+ style quoted string
 191        ///
 192        /// This is also the escaping style used by custom culture data files
 193        ///
 194        /// NLS+ uses \ to escape the next character, whether in a quoted string or
 195        /// not, so we always have to change \ to \\.
 196        ///
 197        /// NLS+ uses \' to escape a quote inside a quoted string so we have to change
 198        /// '' to \' (if inside a quoted string)
 199        ///
 200        /// We don't build the stringbuilder unless we find something to change
 201        /// </summary>
 202        [return: NotNullIfNotNull(nameof(str))]
 203        internal static string? ReescapeWin32String(string? str)
 204        {
 205            // If we don't have data, then don't try anything
 0206            if (str == null)
 207            {
 0208                return null;
 209            }
 210
 0211            StringBuilder? result = null;
 212
 0213            bool inQuote = false;
 0214            for (int i = 0; i < str.Length; i++)
 215            {
 216                // Look for quote
 0217                if (str[i] == '\'')
 218                {
 219                    // Already in quote?
 0220                    if (inQuote)
 221                    {
 222                        // See another single quote.  Is this '' of 'fred''s' or '''', or is it an ending quote?
 0223                        if (i + 1 < str.Length && str[i + 1] == '\'')
 224                        {
 225                            // Found another ', so we have ''.  Need to add \' instead.
 226                            // 1st make sure we have our stringbuilder
 0227                            result ??= new StringBuilder(str, 0, i, str.Length * 2);
 228
 229                            // Append a \' and keep going (so we don't turn off quote mode)
 0230                            result.Append("\\'");
 0231                            i++;
 0232                            continue;
 233                        }
 234
 235                        // Turning off quote mode, fall through to add it
 0236                        inQuote = false;
 237                    }
 238                    else
 239                    {
 240                        // Found beginning quote, fall through to add it
 0241                        inQuote = true;
 242                    }
 243                }
 244                // Is there a single \ character?
 0245                else if (str[i] == '\\')
 246                {
 247                    // Found a \, need to change it to \\
 248                    // 1st make sure we have our stringbuilder
 0249                    result ??= new StringBuilder(str, 0, i, str.Length * 2);
 250
 251                    // Append our \\ to the string & continue
 0252                    result.Append("\\\\");
 0253                    continue;
 254                }
 255
 256                // If we have a builder we need to add our character
 0257                result?.Append(str[i]);
 258            }
 259
 260            // Unchanged string? , just return input string
 0261            if (result == null)
 0262                return str;
 263
 264            // String changed, need to use the builder
 0265            return result.ToString();
 266        }
 267
 268        [return: NotNullIfNotNull(nameof(array))]
 269        internal static string[]? ReescapeWin32Strings(string[]? array)
 270        {
 0271            if (array != null)
 272            {
 0273                for (int i = 0; i < array.Length; i++)
 274                {
 0275                    array[i] = ReescapeWin32String(array[i]);
 276                }
 277            }
 278
 0279            return array;
 280        }
 281
 282        // If we get a group from windows, then its in 3;0 format with the 0 backwards
 283        // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
 284        // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
 285        private static int[] ConvertWin32GroupString(string win32Str)
 286        {
 287            // None of these cases make any sense
 0288            if (string.IsNullOrEmpty(win32Str))
 289            {
 0290                return [3];
 291            }
 292
 0293            if (win32Str.StartsWith('0'))
 294            {
 0295                return [0];
 296            }
 297
 298            // Since its in n;n;n;n;n format, we can always get the length quickly
 299            int[] values;
 0300            if (win32Str.EndsWith('0'))
 301            {
 302                // Trailing 0 gets dropped. 1;0 -> 1
 0303                values = new int[win32Str.Length / 2];
 304            }
 305            else
 306            {
 307                // Need extra space for trailing zero 1 -> 1;0
 0308                values = new int[(win32Str.Length / 2) + 2];
 0309                values[^1] = 0;
 310            }
 311
 312            int i;
 313            int j;
 0314            for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
 315            {
 316                // Note that this # shouldn't ever be zero, 'cause 0 is only at end
 317                // But we'll test because its registry that could be anything
 0318                if (!char.IsBetween(win32Str[i], '1', '9'))
 0319                    return [3];
 320
 0321                values[j] = (int)(win32Str[i] - '0');
 322            }
 323
 0324            return values;
 325        }
 326
 327        private static int ConvertFirstDayOfWeekMonToSun(int iTemp)
 328        {
 329            // Convert Mon-Sun to Sun-Sat format
 0330            iTemp++;
 0331            if (iTemp > 6)
 332            {
 333                // Wrap Sunday and convert invalid data to Sunday
 0334                iTemp = 0;
 335            }
 0336            return iTemp;
 337        }
 338
 339        // Context for EnumCalendarInfoExEx callback.
 340        private struct EnumLocaleData
 341        {
 342            public string regionName;
 343            public string? cultureName;
 344        }
 345
 346        // EnumSystemLocaleEx callback.
 347        [UnmanagedCallersOnly]
 348        private static unsafe Interop.BOOL EnumSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
 349        {
 0350            EnumLocaleData* context = (EnumLocaleData*)contextHandle;
 351            try
 352            {
 0353                string cultureName = new string(lpLocaleString);
 0354                string? regionName = GetLocaleInfoEx(cultureName, Interop.Kernel32.LOCALE_SISO3166CTRYNAME);
 0355                if (regionName != null && regionName.Equals(context->regionName, StringComparison.OrdinalIgnoreCase))
 356                {
 0357                    context->cultureName = cultureName;
 0358                    return Interop.BOOL.FALSE; // we found a match, then stop the enumeration
 359                }
 360
 0361                return Interop.BOOL.TRUE;
 362            }
 0363            catch (Exception)
 364            {
 0365                return Interop.BOOL.FALSE;
 366            }
 0367        }
 368
 369        // EnumSystemLocaleEx callback.
 370        [UnmanagedCallersOnly]
 371        private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandl
 372        {
 373            try
 374            {
 0375                ((EnumData*)contextHandle)->strings.Add(new string(lpLocaleString));
 0376                return Interop.BOOL.TRUE;
 377            }
 0378            catch (Exception)
 379            {
 0380                return Interop.BOOL.FALSE;
 381            }
 0382        }
 383
 384        // Context for EnumTimeFormatsEx callback.
 385        private struct EnumData
 386        {
 387            public List<string> strings;
 388        }
 389
 390        // EnumTimeFormatsEx callback itself.
 391        [UnmanagedCallersOnly]
 392        private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, void* lParam)
 393        {
 394            try
 395            {
 0396                ((EnumData*)lParam)->strings.Add(new string(lpTimeFormatString));
 0397                return Interop.BOOL.TRUE;
 398            }
 0399            catch (Exception)
 400            {
 0401                return Interop.BOOL.FALSE;
 402            }
 0403        }
 404
 405        private static unsafe string[]? nativeEnumTimeFormats(string localeName, uint dwFlags, bool useUserOverride)
 406        {
 0407            EnumData data = default;
 0408            data.strings = new List<string>();
 409
 410            // Now call the enumeration API. Work is done by our callback function
 0411            Interop.Kernel32.EnumTimeFormatsEx(&EnumTimeCallback, localeName, dwFlags, &data);
 412
 0413            if (data.strings.Count > 0)
 414            {
 415                // Now we need to allocate our stringarray and populate it
 0416                string[] results = data.strings.ToArray();
 417
 0418                if (!useUserOverride && data.strings.Count > 1)
 419                {
 420                    // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
 421                    // The override is the first entry if it is overridden.
 422                    // We can check if we have overrides by checking the GetLocaleInfo with no override
 423                    // If we do have an override, we don't know if it is a user defined override or if the
 424                    // user has just selected one of the predefined formats so we can't just remove it
 425                    // but we can move it down.
 0426                    uint lcType = (dwFlags == Interop.Kernel32.TIME_NOSECONDS) ? Interop.Kernel32.LOCALE_SSHORTTIME : In
 0427                    string timeFormatNoUserOverride = GetLocaleInfoFromLCType(localeName, lcType, useUserOverride);
 0428                    if (timeFormatNoUserOverride != "")
 429                    {
 0430                        string firstTimeFormat = results[0];
 0431                        if (timeFormatNoUserOverride != firstTimeFormat)
 432                        {
 0433                            results[0] = results[1];
 0434                            results[1] = firstTimeFormat;
 435                        }
 436                    }
 437                }
 438
 0439                return results;
 440            }
 441
 0442            return null;
 443        }
 444
 445        private static int NlsLocaleNameToLCID(string cultureName)
 446        {
 0447            Debug.Assert(!GlobalizationMode.Invariant);
 448
 0449            return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
 450        }
 451
 452        private string NlsGetThreeLetterWindowsLanguageName(string cultureName)
 453        {
 0454            Debug.Assert(GlobalizationMode.UseNls);
 0455            return NlsGetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName);
 456        }
 457
 458        private static CultureInfo[] NlsEnumCultures(CultureTypes types)
 459        {
 0460            Debug.Assert(!GlobalizationMode.Invariant);
 0461            Debug.Assert(GlobalizationMode.UseNls);
 462
 0463            uint flags = 0;
 464
 465#pragma warning disable 618
 0466            if ((types & (CultureTypes.FrameworkCultures | CultureTypes.InstalledWin32Cultures | CultureTypes.Replacemen
 467            {
 0468                flags |= Interop.Kernel32.LOCALE_NEUTRALDATA | Interop.Kernel32.LOCALE_SPECIFICDATA;
 469            }
 470#pragma warning restore 618
 471
 0472            if ((types & CultureTypes.NeutralCultures) != 0)
 473            {
 0474                flags |= Interop.Kernel32.LOCALE_NEUTRALDATA;
 475            }
 476
 0477            if ((types & CultureTypes.SpecificCultures) != 0)
 478            {
 0479                flags |= Interop.Kernel32.LOCALE_SPECIFICDATA;
 480            }
 481
 0482            if ((types & CultureTypes.UserCustomCulture) != 0)
 483            {
 0484                flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
 485            }
 486
 0487            if ((types & CultureTypes.ReplacementCultures) != 0)
 488            {
 0489                flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
 490            }
 491
 0492            EnumData context = default;
 0493            context.strings = new List<string>();
 494
 495            unsafe
 496            {
 0497                Interop.Kernel32.EnumSystemLocalesEx(&EnumAllSystemLocalesProc, flags, &context, IntPtr.Zero);
 498            }
 499
 0500            CultureInfo[] cultures = new CultureInfo[context.strings.Count];
 0501            for (int i = 0; i < cultures.Length; i++)
 502            {
 0503                cultures[i] = new CultureInfo(context.strings[i]);
 504            }
 505
 0506            return cultures;
 507        }
 508
 509        private string NlsGetConsoleFallbackName(string cultureName)
 510        {
 0511            Debug.Assert(GlobalizationMode.UseNls);
 0512            return NlsGetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName);
 513        }
 514
 515        internal bool NlsIsReplacementCulture
 516        {
 517            get
 518            {
 0519                Debug.Assert(GlobalizationMode.UseNls);
 0520                EnumData context = default;
 0521                context.strings = new List<string>();
 522
 523                unsafe
 524                {
 0525                    Interop.Kernel32.EnumSystemLocalesEx(&EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT,
 526                }
 527
 0528                for (int i = 0; i < context.strings.Count; i++)
 529                {
 0530                    if (string.Equals(context.strings[i], _sRealName, StringComparison.OrdinalIgnoreCase))
 0531                        return true;
 532                }
 533
 0534                return false;
 535            }
 536        }
 537    }
 538}
 539

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs

#LineLine coverage
 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
 4using System.Diagnostics;
 5
 6namespace System.Globalization
 7{
 8    internal sealed partial class CultureData
 9    {
 10        /// <summary>
 11        /// Check with the OS to see if this is a valid culture.
 12        /// If so we populate a limited number of fields.  If its not valid we return false.
 13        ///
 14        /// The fields we populate:
 15        ///
 16        /// sWindowsName -- The name that windows thinks this culture is, ie:
 17        ///                            en-US if you pass in en-US
 18        ///                            de-DE_phoneb if you pass in de-DE_phoneb
 19        ///                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
 20        ///                            fj if you pass in fj (neutral, post-Windows 7 machine)
 21        ///
 22        /// sRealName -- The name you used to construct the culture, in pretty form
 23        ///                       en-US if you pass in EN-us
 24        ///                       en if you pass in en
 25        ///                       de-DE_phoneb if you pass in de-DE_phoneb
 26        ///
 27        /// sSpecificCulture -- The specific culture for this culture
 28        ///                             en-US for en-US
 29        ///                             en-US for en
 30        ///                             de-DE_phoneb for alt sort
 31        ///                             fj-FJ for fj (neutral)
 32        ///
 33        /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
 34        ///                en-US if you pass in en-US
 35        ///                en if you pass in en
 36        ///                de-DE if you pass in de-DE_phoneb
 37        ///
 38        /// bNeutral -- TRUE if it is a neutral locale
 39        ///
 40        /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
 41        /// windows locale that's going to provide data for us.
 42        /// </summary>
 43        private unsafe bool InitCultureDataCore()
 44        {
 045            char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
 046            if (!GlobalizationMode.UseNls)
 47            {
 048                return InitIcuCultureDataCore() &&
 049                       GetLocaleInfoEx(_sRealName, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_
 50            }
 51
 052            Debug.Assert(!GlobalizationMode.Invariant);
 53
 54            int result;
 055            string realNameBuffer = _sRealName;
 56
 057            result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAM
 58
 59            // Did it fail?
 060            if (result == 0)
 61            {
 062                return false;
 63            }
 64
 65            // It worked, note that the name is the locale name, so use that (even for neutrals)
 66            // We need to clean up our "real" name, which should look like the windows name right now
 67            // so overwrite the input with the cleaned up name
 068            _sRealName = new string(pBuffer, 0, result - 1);
 069            realNameBuffer = _sRealName;
 70
 71            // Check for neutrality, don't expect to fail
 072            result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_INEUTRAL | Interop.Kernel32.LOCALE_RETURN_N
 073            if (result == 0)
 74            {
 075                return false;
 76            }
 77
 78            // Remember our neutrality
 079            _bNeutral = *((uint*)pBuffer) != 0;
 80
 81            // Note: Parents will be set dynamically
 82
 83            // Start by assuming the windows name will be the same as the specific name since windows knows
 84            // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
 085            _sWindowsName = realNameBuffer;
 86
 87            // Neutrals and non-neutrals are slightly different
 088            if (_bNeutral)
 89            {
 90                // Neutral Locale
 91
 92                // IETF name looks like neutral name
 093                _sName = realNameBuffer;
 94
 95                // Specific locale name is whatever ResolveLocaleName (win7+) returns.
 96                // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the
 097                result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LE
 98
 99                // 0 is failure, 1 is invariant (""), which we expect
 0100                if (result < 1)
 101                {
 0102                    return false;
 103                }
 104
 105                // We found a locale name, so use it.
 106                // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the
 0107                _sSpecificCulture = new string(pBuffer, 0, result - 1);
 108            }
 109            else
 110            {
 111                // Specific Locale
 112
 113                // Specific culture's the same as the locale name since we know its not neutral
 114                // On mac we'll use this as well, even for neutrals. There's no obvious specific
 115                // culture to use and this isn't exposed, but behaviorally this is correct on mac.
 116                // Note that specifics include the sort name (de-DE_phoneb)
 0117                _sSpecificCulture = realNameBuffer;
 118
 0119                _sName = realNameBuffer;
 120
 121                // We need the IETF name (sname)
 122                // If we aren't an alt sort locale then this is the same as the windows name.
 123                // If we are an alt sort locale then this is the same as the part before the _ in the windows name
 124                // This is for like de-DE_phoneb and es-ES_tradnl that shouldn't have the _ part
 125
 0126                result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RET
 0127                if (result == 0)
 128                {
 0129                    return false;
 130                }
 131
 0132                _iLanguage = *((int*)pBuffer);
 133
 0134                if (!IsCustomCultureId(_iLanguage))
 135                {
 136                    // not custom locale
 0137                    int index = realNameBuffer.IndexOf('_');
 0138                    if (index > 0)
 139                    {
 0140                        _sName = realNameBuffer.Substring(0, index);
 141                    }
 142                }
 143            }
 144
 145            // It succeeded.
 0146            return true;
 147        }
 148
 149        private void InitUserOverride(bool useUserOverride)
 150        {
 0151            Debug.Assert(_sWindowsName != null, "[CultureData.InitUserOverride] Expected _sWindowsName to be populated b
 0152            _bUseOverrides = useUserOverride && _sWindowsName == CultureInfo.UserDefaultLocaleName;
 0153        }
 154
 155        internal static bool IsWin32Installed => true;
 156
 157        internal static unsafe CultureData GetCurrentRegionData()
 158        {
 0159            Span<char> geoIso2Letters = stackalloc char[10];
 160
 0161            int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION);
 0162            if (geoId != Interop.Kernel32.GEOID_NOT_AVAILABLE)
 163            {
 164                int geoIsoIdLength;
 0165                fixed (char* pGeoIsoId = geoIso2Letters)
 166                {
 0167                    geoIsoIdLength = Interop.Kernel32.GetGeoInfo(geoId, Interop.Kernel32.GEO_ISO2, pGeoIsoId, geoIso2Let
 168                }
 169
 0170                if (geoIsoIdLength != 0)
 171                {
 0172                    geoIsoIdLength -= geoIso2Letters[geoIsoIdLength - 1] == 0 ? 1 : 0; // handle null termination and ex
 0173                    CultureData? cd = GetCultureDataForRegion(geoIso2Letters.Slice(0, geoIsoIdLength).ToString(), true);
 0174                    if (cd != null)
 175                    {
 0176                        return cd;
 177                    }
 178                }
 179            }
 180
 181            // Fallback to current locale data.
 0182            return CultureInfo.CurrentCulture._cultureData;
 183        }
 184
 185        private static unsafe string? LCIDToLocaleName(int culture)
 186        {
 0187            Debug.Assert(!GlobalizationMode.Invariant);
 188
 189            // LOCALE_CUSTOM_UNSPECIFIED ia an unspecified custom locale, used to identify all supplemental locales.
 190            // Supplemental locales cannot be distinguished from one another by their locale identifiers, but can be dis
 0191            if (culture == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
 192            {
 0193                return null;
 194            }
 195
 0196            char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination
 0197            int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1
 198
 0199            if (length > 0)
 200            {
 0201                return new string(pBuffer);
 202            }
 203
 0204            return null;
 205        }
 206
 207        private string[]? GetTimeFormatsCore(bool shortFormat)
 208        {
 0209            Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatsCore] Expected _sWindowsName to be populated
 210
 0211            if (GlobalizationMode.UseNls)
 212            {
 0213                return ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, shortFormat ? Interop.Kernel32.TIME_NOS
 214            }
 215
 0216            string icuFormatString = IcuGetTimeFormatString(shortFormat);
 217
 0218            if (!_bUseOverrides)
 219            {
 0220                return [icuFormatString];
 221            }
 222
 223            // When using ICU and need to get user overrides, we put the user override at the beginning
 0224            string userOverride = GetLocaleInfoFromLCType(_sWindowsName, shortFormat ? Interop.Kernel32.LOCALE_SSHORTTIM
 225
 0226            Debug.Assert(!string.IsNullOrEmpty(userOverride));
 227
 0228            return userOverride != icuFormatString ?
 0229                 [userOverride, icuFormatString] : [userOverride];
 230        }
 231
 232        private int GetAnsiCodePage(string _ /*cultureName*/) =>
 0233            NlsGetLocaleInfo(LocaleNumberData.AnsiCodePage);
 234
 235        private int GetOemCodePage(string _ /*cultureName*/) =>
 0236            NlsGetLocaleInfo(LocaleNumberData.OemCodePage);
 237
 238        private int GetMacCodePage(string _ /*cultureName*/) =>
 0239            NlsGetLocaleInfo(LocaleNumberData.MacCodePage);
 240
 241        private int GetEbcdicCodePage(string _ /*cultureName*/) =>
 0242            NlsGetLocaleInfo(LocaleNumberData.EbcdicCodePage);
 243
 244        // If we are using ICU and loading the calendar data for the user's default
 245        // local, and we're using user overrides, then we use NLS to load the data
 246        // in order to get the user overrides from the OS.
 0247        private bool ShouldUseUserOverrideNlsData => GlobalizationMode.UseNls || _bUseOverrides;
 248    }
 249}
 250

Methods/Properties

.ctor()
RegionNames()
InteropName()
GetCultureDataForRegion(System.String,System.Boolean)
ClearCachedData()
GetCultures(System.Globalization.CultureTypes)
CreateCultureWithInvariantData()
Invariant()
GetCultureData(System.String,System.Boolean)
NormalizeCultureName(System.String,System.Boolean&)
CreateCultureData(System.String,System.Boolean)
InitCompatibilityCultureData()
GetCultureData(System.Int32,System.Boolean)
CultureName()
UseUserOverride()
Name()
ParentName()
DisplayName()
GetLanguageDisplayNameCore(System.String)
EnglishName()
NativeName()
SpecificCultureName()
TwoLetterISOLanguageName()
ThreeLetterISOLanguageName()
ThreeLetterWindowsLanguageName()
LocalizedLanguageName()
EnglishLanguageName()
NativeLanguageName()
RegionName()
GeoId()
LocalizedCountryName()
EnglishCountryName()
NativeCountryName()
TwoLetterISOCountryName()
ThreeLetterISOCountryName()
KeyboardLayoutId()
SCONSOLEFALLBACKNAME()
NumberGroupSizes()
NaNSymbol()
PositiveInfinitySymbol()
NegativeInfinitySymbol()
PercentNegativePattern()
PercentPositivePattern()
PercentSymbol()
PerMilleSymbol()
CurrencySymbol()
ISOCurrencySymbol()
CurrencyEnglishName()
CurrencyNativeName()
CurrencyGroupSizes()
MeasurementSystem()
ListSeparator()
AMDesignator()
PMDesignator()
LongTimes()
ShortTimes()
DeriveShortTimesFromLong()
StripSecondsFromPattern(System.String)
GetIndexOfNextTokenAfterSeconds(System.String,System.Int32,System.Boolean&)
FirstDayOfWeek()
CalendarWeekRule()
ShortDates(System.Globalization.CalendarId)
LongDates(System.Globalization.CalendarId)
YearMonths(System.Globalization.CalendarId)
DayNames(System.Globalization.CalendarId)
AbbreviatedDayNames(System.Globalization.CalendarId)
SuperShortDayNames(System.Globalization.CalendarId)
MonthNames(System.Globalization.CalendarId)
GenitiveMonthNames(System.Globalization.CalendarId)
AbbreviatedMonthNames(System.Globalization.CalendarId)
AbbreviatedGenitiveMonthNames(System.Globalization.CalendarId)
LeapYearMonthNames(System.Globalization.CalendarId)
MonthDay(System.Globalization.CalendarId)
CalendarIds()
CalendarName(System.Globalization.CalendarId)
GetCalendar(System.Globalization.CalendarId)
IsRightToLeft()
ReadingLayout()
TextInfoName()
SortName()
IsSupplementalCustomCulture()
ANSICodePage()
OEMCodePage()
MacCodePage()
EBCDICCodePage()
LCID()
IsNeutralCulture()
IsInvariantCulture()
IsReplacementCulture()
DefaultCalendar()
EraNames(System.Globalization.CalendarId)
AbbrevEraNames(System.Globalization.CalendarId)
AbbreviatedEnglishEraNames(System.Globalization.CalendarId)
TimeSeparator()
DateSeparator(System.Globalization.CalendarId)
UnescapeNlsString(System.String,System.Int32,System.Int32)
GetTimeSeparator(System.String)
GetDateSeparator(System.String)
GetSeparator(System.String,System.String)
IndexOfTimePart(System.String,System.Int32,System.String)
IsCustomCultureId(System.Int32)
GetNativeDigits()
GetNFIValues(System.Globalization.NumberFormatInfo)
AnsiToLower(System.String)
GetLocaleInfoCore(System.Globalization.CultureData/LocaleNumberData)
GetLocaleInfoCoreUserOverride(System.Globalization.CultureData/LocaleNumberData)
GetLocaleInfoCoreUserOverride(System.Globalization.CultureData/LocaleStringData)
GetLocaleInfoCore(System.Globalization.CultureData/LocaleStringData,System.String)
GetLocaleInfoCore(System.String,System.Globalization.CultureData/LocaleStringData,System.String)
GetLocaleInfoCoreUserOverride(System.Globalization.CultureData/LocaleGroupingData)
NormalizeCultureName(System.String,System.ReadOnlySpan`1<System.Char>,System.String,System.Int32&)
InitIcuCultureDataCore()
GetLocaleName(System.String,System.String&)
IcuGetLocaleInfo(System.Globalization.CultureData/LocaleStringData,System.String)
IcuGetLocaleInfo(System.String,System.Globalization.CultureData/LocaleStringData,System.String)
IcuGetLocaleInfo(System.Globalization.CultureData/LocaleNumberData)
IcuGetLocaleInfo(System.Globalization.CultureData/LocaleGroupingData)
IcuGetTimeFormatString()
IcuGetTimeFormatString(System.Boolean)
IcuGetLanguageDisplayName(System.String)
IcuIsEnsurePredefinedLocaleName(System.String)
ConvertIcuTimeFormatString(System.ReadOnlySpan`1<System.Char>)
HandleQuoteLiteral(System.ReadOnlySpan`1<System.Char>,System.Int32&,System.Span`1<System.Char>,System.Int32&)
IcuLocaleNameToLCID(System.String)
IcuGetGeoId(System.String)
IcuGetDigitSubstitution(System.String)
IcuGetListSeparator(System.String)
IcuGetThreeLetterWindowsLanguageName(System.String)
IcuEnumCultures(System.Globalization.CultureTypes)
IcuGetConsoleFallbackName(System.String)
IsValidCultureName(System.String,System.Int32&,System.Int32&)
GetLocaleInfoEx(System.String,System.UInt32)
GetLocaleInfoExInt(System.String,System.UInt32)
GetLocaleInfoEx(System.String,System.UInt32,System.Char*,System.Int32)
NlsGetLocaleInfo(System.Globalization.CultureData/LocaleStringData)
NlsGetLocaleInfo(System.String,System.Globalization.CultureData/LocaleStringData)
NlsGetLocaleInfo(System.Globalization.CultureData/LocaleNumberData)
NlsGetLocaleInfo(System.Globalization.CultureData/LocaleGroupingData)
NlsIsEnsurePredefinedLocaleName(System.String)
NlsGetTimeFormatString()
NlsGetFirstDayOfWeek()
NlsGetCultureDataFromRegionName(System.String)
NlsGetLanguageDisplayName(System.String)
NlsGetRegionDisplayName()
GetLocaleInfoFromLCType(System.String,System.UInt32,System.Boolean)
ReescapeWin32String(System.String)
ReescapeWin32Strings(System.String[])
ConvertWin32GroupString(System.String)
ConvertFirstDayOfWeekMonToSun(System.Int32)
EnumSystemLocalesProc(System.Char*,System.UInt32,System.Void*)
EnumAllSystemLocalesProc(System.Char*,System.UInt32,System.Void*)
EnumTimeCallback(System.Char*,System.Void*)
nativeEnumTimeFormats(System.String,System.UInt32,System.Boolean)
NlsLocaleNameToLCID(System.String)
NlsGetThreeLetterWindowsLanguageName(System.String)
NlsEnumCultures(System.Globalization.CultureTypes)
NlsGetConsoleFallbackName(System.String)
NlsIsReplacementCulture()
InitCultureDataCore()
InitUserOverride(System.Boolean)
GetCurrentRegionData()
LCIDToLocaleName(System.Int32)
GetTimeFormatsCore(System.Boolean)
GetAnsiCodePage(System.String)
GetOemCodePage(System.String)
GetMacCodePage(System.String)
GetEbcdicCodePage(System.String)
ShouldUseUserOverrideNlsData()