1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections.Generic; 7 using System.Diagnostics; 8 using System.Runtime.InteropServices; 9 using System.Security; 10 using System.Text; 11 12 namespace System.Globalization 13 { 14 internal partial class CultureData 15 { 16 // ICU constants 17 const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value 18 const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name 19 const string ICU_COLLATION_KEYWORD = "@collation="; 20 21 22 /// <summary> 23 /// This method uses the sRealName field (which is initialized by the constructor before this is called) to 24 /// initialize the rest of the state of CultureData based on the underlying OS globalization library. 25 /// </summary> InitCultureData()26 private unsafe bool InitCultureData() 27 { 28 Debug.Assert(_sRealName != null); 29 30 Debug.Assert(!GlobalizationMode.Invariant); 31 32 string alternateSortName = string.Empty; 33 string realNameBuffer = _sRealName; 34 35 // Basic validation 36 if (realNameBuffer.Contains("@")) 37 { 38 return false; // don't allow ICU variants to come in directly 39 } 40 41 // Replace _ (alternate sort) with @collation= for ICU 42 int index = realNameBuffer.IndexOf('_'); 43 if (index > 0) 44 { 45 if (index >= (realNameBuffer.Length - 1) // must have characters after _ 46 || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed 47 { 48 return false; // fail 49 } 50 alternateSortName = realNameBuffer.Substring(index + 1); 51 realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName; 52 } 53 54 // Get the locale name from ICU 55 if (!GetLocaleName(realNameBuffer, out _sWindowsName)) 56 { 57 return false; // fail 58 } 59 60 // Replace the ICU collation keyword with an _ 61 index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); 62 if (index >= 0) 63 { 64 _sName = _sWindowsName.Substring(0, index) + "_" + alternateSortName; 65 } 66 else 67 { 68 _sName = _sWindowsName; 69 } 70 _sRealName = _sName; 71 72 _iLanguage = this.ILANGUAGE; 73 if (_iLanguage == 0) 74 { 75 _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED; 76 } 77 78 _bNeutral = (this.SISO3166CTRYNAME.Length == 0); 79 80 _sSpecificCulture = _bNeutral ? LocaleData.GetSpecificCultureName(_sRealName) : _sRealName; 81 82 // Remove the sort from sName unless custom culture 83 if (index>0 && !_bNeutral && !IsCustomCultureId(_iLanguage)) 84 { 85 _sName = _sWindowsName.Substring(0, index); 86 } 87 return true; 88 } 89 GetLocaleName(string localeName, out string windowsName)90 internal static bool GetLocaleName(string localeName, out string windowsName) 91 { 92 // Get the locale name from ICU 93 StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); 94 if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity)) 95 { 96 StringBuilderCache.Release(sb); 97 windowsName = null; 98 return false; // fail 99 } 100 101 // Success - use the locale name returned which may be different than realNameBuffer (casing) 102 windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls 103 return true; 104 } 105 GetDefaultLocaleName(out string windowsName)106 internal static bool GetDefaultLocaleName(out string windowsName) 107 { 108 // Get the default (system) locale name from ICU 109 StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); 110 if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity)) 111 { 112 StringBuilderCache.Release(sb); 113 windowsName = null; 114 return false; // fail 115 } 116 117 // Success - use the locale name returned which may be different than realNameBuffer (casing) 118 windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls 119 return true; 120 } 121 GetLocaleInfo(LocaleStringData type)122 private string GetLocaleInfo(LocaleStringData type) 123 { 124 Debug.Assert(!GlobalizationMode.Invariant); 125 126 Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo] Expected _sWindowsName to be populated already"); 127 return GetLocaleInfo(_sWindowsName, type); 128 } 129 130 // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the 131 // "windows" name, which can be specific for downlevel (< windows 7) os's. GetLocaleInfo(string localeName, LocaleStringData type)132 private string GetLocaleInfo(string localeName, LocaleStringData type) 133 { 134 Debug.Assert(localeName != null, "[CultureData.GetLocaleInfo] Expected localeName to be not be null"); 135 136 switch (type) 137 { 138 case LocaleStringData.NegativeInfinitySymbol: 139 // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign 140 return GetLocaleInfo(localeName, LocaleStringData.NegativeSign) + 141 GetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol); 142 } 143 144 StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); 145 146 bool result = Interop.GlobalizationInterop.GetLocaleInfoString(localeName, (uint)type, sb, sb.Capacity); 147 if (!result) 148 { 149 // Failed, just use empty string 150 StringBuilderCache.Release(sb); 151 Debug.Fail("[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); 152 return String.Empty; 153 } 154 return StringBuilderCache.GetStringAndRelease(sb); 155 } 156 GetLocaleInfo(LocaleNumberData type)157 private int GetLocaleInfo(LocaleNumberData type) 158 { 159 Debug.Assert(!GlobalizationMode.Invariant); 160 161 Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already"); 162 163 switch (type) 164 { 165 case LocaleNumberData.CalendarType: 166 // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar 167 return 0; 168 } 169 170 171 int value = 0; 172 bool result = Interop.GlobalizationInterop.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value); 173 if (!result) 174 { 175 // Failed, just use 0 176 Debug.Fail("[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); 177 } 178 179 return value; 180 } 181 GetLocaleInfo(LocaleGroupingData type)182 private int[] GetLocaleInfo(LocaleGroupingData type) 183 { 184 Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already"); 185 186 int primaryGroupingSize = 0; 187 int secondaryGroupingSize = 0; 188 bool result = Interop.GlobalizationInterop.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize); 189 if (!result) 190 { 191 Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); 192 } 193 194 if (secondaryGroupingSize == 0) 195 { 196 return new int[] { primaryGroupingSize }; 197 } 198 199 return new int[] { primaryGroupingSize, secondaryGroupingSize }; 200 } 201 GetTimeFormatString()202 private string GetTimeFormatString() 203 { 204 return GetTimeFormatString(false); 205 } 206 GetTimeFormatString(bool shortFormat)207 private string GetTimeFormatString(bool shortFormat) 208 { 209 Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsName to be populated already"); 210 211 StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); 212 213 bool result = Interop.GlobalizationInterop.GetLocaleTimeFormat(_sWindowsName, shortFormat, sb, sb.Capacity); 214 if (!result) 215 { 216 // Failed, just use empty string 217 StringBuilderCache.Release(sb); 218 Debug.Fail("[CultureData.GetTimeFormatString(bool shortFormat)] Failed"); 219 return String.Empty; 220 } 221 222 return ConvertIcuTimeFormatString(StringBuilderCache.GetStringAndRelease(sb)); 223 } 224 GetFirstDayOfWeek()225 private int GetFirstDayOfWeek() 226 { 227 return this.GetLocaleInfo(LocaleNumberData.FirstDayOfWeek); 228 } 229 GetTimeFormats()230 private String[] GetTimeFormats() 231 { 232 string format = GetTimeFormatString(false); 233 return new string[] { format }; 234 } 235 GetShortTimeFormats()236 private String[] GetShortTimeFormats() 237 { 238 string format = GetTimeFormatString(true); 239 return new string[] { format }; 240 } 241 GetCultureDataFromRegionName(String regionName)242 private static CultureData GetCultureDataFromRegionName(String regionName) 243 { 244 // no support to lookup by region name, other than the hard-coded list in CultureData 245 return null; 246 } 247 GetLanguageDisplayName(string cultureName)248 private static string GetLanguageDisplayName(string cultureName) 249 { 250 return new CultureInfo(cultureName)._cultureData.GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); 251 } 252 GetRegionDisplayName(string isoCountryCode)253 private static string GetRegionDisplayName(string isoCountryCode) 254 { 255 // use the fallback which is to return NativeName 256 return null; 257 } 258 GetUserDefaultCulture()259 private static CultureInfo GetUserDefaultCulture() 260 { 261 return CultureInfo.GetUserDefaultCulture(); 262 } 263 ConvertIcuTimeFormatString(string icuFormatString)264 private static string ConvertIcuTimeFormatString(string icuFormatString) 265 { 266 StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); 267 bool amPmAdded = false; 268 269 for (int i = 0; i < icuFormatString.Length; i++) 270 { 271 switch(icuFormatString[i]) 272 { 273 case ':': 274 case '.': 275 case 'H': 276 case 'h': 277 case 'm': 278 case 's': 279 sb.Append(icuFormatString[i]); 280 break; 281 282 case ' ': 283 case '\u00A0': 284 // Convert nonbreaking spaces into regular spaces 285 sb.Append(' '); 286 break; 287 288 case 'a': // AM/PM 289 if (!amPmAdded) 290 { 291 amPmAdded = true; 292 sb.Append("tt"); 293 } 294 break; 295 296 } 297 } 298 299 return StringBuilderCache.GetStringAndRelease(sb); 300 } 301 LCIDToLocaleName(int culture)302 private static string LCIDToLocaleName(int culture) 303 { 304 Debug.Assert(!GlobalizationMode.Invariant); 305 306 return LocaleData.LCIDToLocaleName(culture); 307 } 308 LocaleNameToLCID(string cultureName)309 private static int LocaleNameToLCID(string cultureName) 310 { 311 Debug.Assert(!GlobalizationMode.Invariant); 312 313 int lcid = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.Lcid); 314 return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid; 315 } 316 GetAnsiCodePage(string cultureName)317 private static int GetAnsiCodePage(string cultureName) 318 { 319 int ansiCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.AnsiCodePage); 320 return ansiCodePage == -1 ? CultureData.Invariant.IDEFAULTANSICODEPAGE : ansiCodePage; 321 } 322 GetOemCodePage(string cultureName)323 private static int GetOemCodePage(string cultureName) 324 { 325 int oemCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.OemCodePage); 326 return oemCodePage == -1 ? CultureData.Invariant.IDEFAULTOEMCODEPAGE : oemCodePage; 327 } 328 GetMacCodePage(string cultureName)329 private static int GetMacCodePage(string cultureName) 330 { 331 int macCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.MacCodePage); 332 return macCodePage == -1 ? CultureData.Invariant.IDEFAULTMACCODEPAGE : macCodePage; 333 } 334 GetEbcdicCodePage(string cultureName)335 private static int GetEbcdicCodePage(string cultureName) 336 { 337 int ebcdicCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.EbcdicCodePage); 338 return ebcdicCodePage == -1 ? CultureData.Invariant.IDEFAULTEBCDICCODEPAGE : ebcdicCodePage; 339 } 340 GetGeoId(string cultureName)341 private static int GetGeoId(string cultureName) 342 { 343 int geoId = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.GeoId); 344 return geoId == -1 ? CultureData.Invariant.IGEOID : geoId; 345 } 346 GetDigitSubstitution(string cultureName)347 private static int GetDigitSubstitution(string cultureName) 348 { 349 int digitSubstitution = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.DigitSubstitution); 350 return digitSubstitution == -1 ? (int) DigitShapes.None : digitSubstitution; 351 } 352 GetThreeLetterWindowsLanguageName(string cultureName)353 private static string GetThreeLetterWindowsLanguageName(string cultureName) 354 { 355 string langName = LocaleData.GetThreeLetterWindowsLangageName(cultureName); 356 return langName == null ? "ZZZ" /* default lang name */ : langName; 357 } 358 EnumCultures(CultureTypes types)359 private static CultureInfo[] EnumCultures(CultureTypes types) 360 { 361 Debug.Assert(!GlobalizationMode.Invariant); 362 363 if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0) 364 { 365 return Array.Empty<CultureInfo>(); 366 } 367 368 int bufferLength = Interop.GlobalizationInterop.GetLocales(null, 0); 369 if (bufferLength <= 0) 370 { 371 return Array.Empty<CultureInfo>(); 372 } 373 374 Char [] chars = new Char[bufferLength]; 375 376 bufferLength = Interop.GlobalizationInterop.GetLocales(chars, bufferLength); 377 if (bufferLength <= 0) 378 { 379 return Array.Empty<CultureInfo>(); 380 } 381 382 bool enumNeutrals = (types & CultureTypes.NeutralCultures) != 0; 383 bool enumSpecificss = (types & CultureTypes.SpecificCultures) != 0; 384 385 List<CultureInfo> list = new List<CultureInfo>(); 386 if (enumNeutrals) 387 { 388 list.Add(CultureInfo.InvariantCulture); 389 } 390 391 int index = 0; 392 while (index < bufferLength) 393 { 394 int length = (int) chars[index++]; 395 if (index + length <= bufferLength) 396 { 397 CultureInfo ci = CultureInfo.GetCultureInfo(new String(chars, index, length)); 398 if ((enumNeutrals && ci.IsNeutralCulture) || (enumSpecificss && !ci.IsNeutralCulture)) 399 { 400 list.Add(ci); 401 } 402 } 403 404 index += length; 405 } 406 407 return list.ToArray(); 408 } 409 GetConsoleFallbackName(string cultureName)410 private static string GetConsoleFallbackName(string cultureName) 411 { 412 return LocaleData.GetConsoleUICulture(cultureName); 413 } 414 415 internal bool IsFramework // not applicable on Linux based systems 416 { 417 get { return false; } 418 } 419 420 internal bool IsWin32Installed // not applicable on Linux based systems 421 { 422 get { return false; } 423 } 424 425 internal bool IsReplacementCulture // not applicable on Linux based systems 426 { 427 get { return false; } 428 } 429 } 430 } 431