CultureInfo.cs 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  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. //
  6. //
  7. //
  8. // Purpose: This class represents the software preferences of a particular
  9. // culture or community. It includes information such as the
  10. // language, writing system, and a calendar used by the culture
  11. // as well as methods for common operations such as printing
  12. // dates and sorting strings.
  13. //
  14. //
  15. //
  16. // !!!! NOTE WHEN CHANGING THIS CLASS !!!!
  17. //
  18. // If adding or removing members to this class, please update CultureInfoBaseObject
  19. // in ndp/clr/src/vm/object.h. Note, the "actual" layout of the class may be
  20. // different than the order in which members are declared. For instance, all
  21. // reference types will come first in the class before value types (like ints, bools, etc)
  22. // regardless of the order in which they are declared. The best way to see the
  23. // actual order of the class is to do a !dumpobj on an instance of the managed
  24. // object inside of the debugger.
  25. //
  26. ////////////////////////////////////////////////////////////////////////////
  27. using System.Collections.Generic;
  28. using System.Diagnostics;
  29. using System.Threading;
  30. #if ENABLE_WINRT
  31. using Internal.Runtime.Augments;
  32. #endif
  33. namespace System.Globalization
  34. {
  35. /// <summary>
  36. /// This class represents the software preferences of a particular culture
  37. /// or community. It includes information such as the language, writing
  38. /// system and a calendar used by the culture as well as methods for
  39. /// common operations such as printing dates and sorting strings.
  40. /// </summary>
  41. /// <remarks>
  42. /// !!!! NOTE WHEN CHANGING THIS CLASS !!!!
  43. /// If adding or removing members to this class, please update
  44. /// CultureInfoBaseObject in ndp/clr/src/vm/object.h. Note, the "actual"
  45. /// layout of the class may be different than the order in which members
  46. /// are declared. For instance, all reference types will come first in the
  47. /// class before value types (like ints, bools, etc) regardless of the
  48. /// order in which they are declared. The best way to see the actual
  49. /// order of the class is to do a !dumpobj on an instance of the managed
  50. /// object inside of the debugger.
  51. /// </remarks>
  52. public partial class CultureInfo : IFormatProvider, ICloneable
  53. {
  54. // We use an RFC4646 type string to construct CultureInfo.
  55. // This string is stored in _name and is authoritative.
  56. // We use the _cultureData to get the data for our object
  57. private bool _isReadOnly;
  58. private CompareInfo? _compareInfo;
  59. private TextInfo? _textInfo;
  60. internal NumberFormatInfo? _numInfo;
  61. internal DateTimeFormatInfo? _dateTimeInfo;
  62. private Calendar? _calendar;
  63. //
  64. // The CultureData instance that we are going to read data from.
  65. // For supported culture, this will be the CultureData instance that read data from mscorlib assembly.
  66. // For customized culture, this will be the CultureData instance that read data from user customized culture binary file.
  67. //
  68. internal CultureData _cultureData;
  69. internal bool _isInherited;
  70. private CultureInfo? _consoleFallbackCulture;
  71. // Names are confusing. Here are 3 names we have:
  72. //
  73. // new CultureInfo() _name _nonSortName _sortName
  74. // en-US en-US en-US en-US
  75. // de-de_phoneb de-DE_phoneb de-DE de-DE_phoneb
  76. // fj-fj (custom) fj-FJ fj-FJ en-US (if specified sort is en-US)
  77. // en en
  78. //
  79. // Note that in Silverlight we ask the OS for the text and sort behavior, so the
  80. // textinfo and compareinfo names are the same as the name
  81. // This has a de-DE, de-DE_phoneb or fj-FJ style name
  82. internal string _name;
  83. // This will hold the non sorting name to be returned from CultureInfo.Name property.
  84. // This has a de-DE style name even for de-DE_phoneb type cultures
  85. private string? _nonSortName;
  86. // This will hold the sorting name to be returned from CultureInfo.SortName property.
  87. // This might be completely unrelated to the culture name if a custom culture. Ie en-US for fj-FJ.
  88. // Otherwise its the sort name, ie: de-DE or de-DE_phoneb
  89. private string? _sortName;
  90. // Get the current user default culture. This one is almost always used, so we create it by default.
  91. private static volatile CultureInfo? s_userDefaultCulture;
  92. //The culture used in the user interface. This is mostly used to load correct localized resources.
  93. private static volatile CultureInfo? s_userDefaultUICulture;
  94. // WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture)
  95. // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
  96. // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
  97. // Get in touch with the diagnostics team if you have questions.
  98. // The Invariant culture;
  99. private static readonly CultureInfo s_InvariantCultureInfo = new CultureInfo(CultureData.Invariant, isReadOnly: true);
  100. // These are defaults that we use if a thread has not opted into having an explicit culture
  101. private static volatile CultureInfo? s_DefaultThreadCurrentUICulture;
  102. private static volatile CultureInfo? s_DefaultThreadCurrentCulture;
  103. [ThreadStatic]
  104. private static CultureInfo? s_currentThreadCulture;
  105. [ThreadStatic]
  106. private static CultureInfo? s_currentThreadUICulture;
  107. private static AsyncLocal<CultureInfo>? s_asyncLocalCurrentCulture;
  108. private static AsyncLocal<CultureInfo>? s_asyncLocalCurrentUICulture;
  109. private static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args)
  110. {
  111. s_currentThreadCulture = args.CurrentValue;
  112. }
  113. private static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args)
  114. {
  115. s_currentThreadUICulture = args.CurrentValue;
  116. }
  117. private static readonly object _lock = new object();
  118. private static volatile Dictionary<string, CultureInfo>? s_NameCachedCultures;
  119. private static volatile Dictionary<int, CultureInfo>? s_LcidCachedCultures;
  120. // The parent culture.
  121. private CultureInfo? _parent;
  122. // LOCALE constants of interest to us internally and privately for LCID functions
  123. // (ie: avoid using these and use names if possible)
  124. internal const int LOCALE_NEUTRAL = 0x0000;
  125. private const int LOCALE_USER_DEFAULT = 0x0400;
  126. private const int LOCALE_SYSTEM_DEFAULT = 0x0800;
  127. internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000;
  128. internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00;
  129. internal const int LOCALE_INVARIANT = 0x007F;
  130. private static CultureInfo InitializeUserDefaultCulture()
  131. {
  132. Interlocked.CompareExchange(ref s_userDefaultCulture, GetUserDefaultCulture(), null);
  133. return s_userDefaultCulture!;
  134. }
  135. private static CultureInfo InitializeUserDefaultUICulture()
  136. {
  137. Interlocked.CompareExchange(ref s_userDefaultUICulture, GetUserDefaultUICulture(), null);
  138. return s_userDefaultUICulture!;
  139. }
  140. public CultureInfo(string name) : this(name, true)
  141. {
  142. }
  143. public CultureInfo(string name, bool useUserOverride)
  144. {
  145. if (name == null)
  146. {
  147. throw new ArgumentNullException(nameof(name));
  148. }
  149. // Get our data providing record
  150. CultureData? cultureData = CultureData.GetCultureData(name, useUserOverride);
  151. if (cultureData == null)
  152. {
  153. throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported);
  154. }
  155. _cultureData = cultureData;
  156. _name = _cultureData.CultureName;
  157. _isInherited = GetType() != typeof(CultureInfo);
  158. }
  159. private CultureInfo(CultureData cultureData, bool isReadOnly = false)
  160. {
  161. Debug.Assert(cultureData != null);
  162. _cultureData = cultureData;
  163. _name = cultureData.CultureName;
  164. _isInherited = false;
  165. _isReadOnly = isReadOnly;
  166. }
  167. private static CultureInfo? CreateCultureInfoNoThrow(string name, bool useUserOverride)
  168. {
  169. Debug.Assert(name != null);
  170. CultureData? cultureData = CultureData.GetCultureData(name, useUserOverride);
  171. if (cultureData == null)
  172. {
  173. return null;
  174. }
  175. return new CultureInfo(cultureData);
  176. }
  177. public CultureInfo(int culture) : this(culture, true)
  178. {
  179. }
  180. public CultureInfo(int culture, bool useUserOverride)
  181. {
  182. // We don't check for other invalid LCIDS here...
  183. if (culture < 0)
  184. {
  185. throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
  186. }
  187. switch (culture)
  188. {
  189. case LOCALE_CUSTOM_DEFAULT:
  190. case LOCALE_SYSTEM_DEFAULT:
  191. case LOCALE_NEUTRAL:
  192. case LOCALE_USER_DEFAULT:
  193. case LOCALE_CUSTOM_UNSPECIFIED:
  194. // Can't support unknown custom cultures and we do not support neutral or
  195. // non-custom user locales.
  196. throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
  197. default:
  198. // Now see if this LCID is supported in the system default CultureData table.
  199. _cultureData = CultureData.GetCultureData(culture, useUserOverride);
  200. break;
  201. }
  202. _isInherited = GetType() != typeof(CultureInfo);
  203. _name = _cultureData.CultureName;
  204. }
  205. /// <summary>
  206. /// Constructor called by SQL Server's special munged culture - creates a culture with
  207. /// a TextInfo and CompareInfo that come from a supplied alternate source. This object
  208. /// is ALWAYS read-only.
  209. /// Note that we really cannot use an LCID version of this override as the cached
  210. /// name we create for it has to include both names, and the logic for this is in
  211. /// the GetCultureInfo override *only*.
  212. /// </summary>
  213. internal CultureInfo(string cultureName, string textAndCompareCultureName)
  214. {
  215. if (cultureName == null)
  216. {
  217. throw new ArgumentNullException(nameof(cultureName), SR.ArgumentNull_String);
  218. }
  219. CultureData? cultureData = CultureData.GetCultureData(cultureName, false);
  220. if (cultureData == null)
  221. {
  222. throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported);
  223. }
  224. _cultureData = cultureData;
  225. _name = _cultureData.CultureName;
  226. CultureInfo altCulture = GetCultureInfo(textAndCompareCultureName);
  227. _compareInfo = altCulture.CompareInfo;
  228. _textInfo = altCulture.TextInfo;
  229. }
  230. /// <summary>
  231. /// We do this to try to return the system UI language and the default user languages
  232. /// This method will fallback if this fails (like Invariant)
  233. /// </summary>
  234. private static CultureInfo GetCultureByName(string name)
  235. {
  236. try
  237. {
  238. return new CultureInfo(name)
  239. {
  240. _isReadOnly = true
  241. };
  242. }
  243. catch (ArgumentException)
  244. {
  245. return InvariantCulture;
  246. }
  247. }
  248. /// <summary>
  249. /// Return a specific culture. A tad irrelevent now since we always
  250. /// return valid data for neutral locales.
  251. ///
  252. /// Note that there's interesting behavior that tries to find a
  253. /// smaller name, ala RFC4647, if we can't find a bigger name.
  254. /// That doesn't help with things like "zh" though, so the approach
  255. /// is of questionable value
  256. /// </summary>
  257. public static CultureInfo CreateSpecificCulture(string name)
  258. {
  259. CultureInfo? culture;
  260. try
  261. {
  262. culture = new CultureInfo(name);
  263. }
  264. catch (ArgumentException)
  265. {
  266. // When CultureInfo throws this exception, it may be because someone passed the form
  267. // like "az-az" because it came out of an http accept lang. We should try a little
  268. // parsing to perhaps fall back to "az" here and use *it* to create the neutral.
  269. culture = null;
  270. for (int idx = 0; idx < name.Length; idx++)
  271. {
  272. if ('-' == name[idx])
  273. {
  274. try
  275. {
  276. culture = new CultureInfo(name.Substring(0, idx));
  277. break;
  278. }
  279. catch (ArgumentException)
  280. {
  281. // throw the original exception so the name in the string will be right
  282. throw;
  283. }
  284. }
  285. }
  286. if (culture == null)
  287. {
  288. // nothing to save here; throw the original exception
  289. throw;
  290. }
  291. }
  292. // In the most common case, they've given us a specific culture, so we'll just return that.
  293. if (!(culture.IsNeutralCulture))
  294. {
  295. return culture;
  296. }
  297. return new CultureInfo(culture._cultureData.SpecificCultureName);
  298. }
  299. internal static bool VerifyCultureName(string cultureName, bool throwException)
  300. {
  301. // This function is used by ResourceManager.GetResourceFileName().
  302. // ResourceManager searches for resource using CultureInfo.Name,
  303. // so we should check against CultureInfo.Name.
  304. for (int i = 0; i < cultureName.Length; i++)
  305. {
  306. char c = cultureName[i];
  307. // TODO: Names can only be RFC4646 names (ie: a-zA-Z0-9) while this allows any unicode letter/digit
  308. if (char.IsLetterOrDigit(c) || c == '-' || c == '_')
  309. {
  310. continue;
  311. }
  312. if (throwException)
  313. {
  314. throw new ArgumentException(SR.Format(SR.Argument_InvalidResourceCultureName, cultureName));
  315. }
  316. return false;
  317. }
  318. return true;
  319. }
  320. internal static bool VerifyCultureName(CultureInfo culture, bool throwException)
  321. {
  322. // If we have an instance of one of our CultureInfos, the user can't have changed the
  323. // name and we know that all names are valid in files.
  324. if (!culture._isInherited)
  325. {
  326. return true;
  327. }
  328. return VerifyCultureName(culture.Name, throwException);
  329. }
  330. /// <summary>
  331. /// This instance provides methods based on the current user settings.
  332. /// These settings are volatile and may change over the lifetime of the
  333. /// thread.
  334. /// </summary>
  335. /// <remarks>
  336. /// We use the following order to return CurrentCulture and CurrentUICulture
  337. /// o Use WinRT to return the current user profile language
  338. /// o use current thread culture if the user already set one using CurrentCulture/CurrentUICulture
  339. /// o use thread culture if the user already set one using DefaultThreadCurrentCulture
  340. /// or DefaultThreadCurrentUICulture
  341. /// o Use NLS default user culture
  342. /// o Use NLS default system culture
  343. /// o Use Invariant culture
  344. /// </remarks>
  345. public static CultureInfo CurrentCulture
  346. {
  347. get
  348. {
  349. #if ENABLE_WINRT
  350. WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks;
  351. if (callbacks != null && callbacks.IsAppxModel())
  352. {
  353. return (CultureInfo)callbacks.GetUserDefaultCulture();
  354. }
  355. #endif
  356. #if FEATURE_APPX
  357. if (ApplicationModel.IsUap)
  358. {
  359. CultureInfo? culture = GetCultureInfoForUserPreferredLanguageInAppX();
  360. if (culture != null)
  361. return culture;
  362. }
  363. #endif
  364. return s_currentThreadCulture ??
  365. s_DefaultThreadCurrentCulture ??
  366. s_userDefaultCulture ??
  367. InitializeUserDefaultCulture();
  368. }
  369. set
  370. {
  371. if (value == null)
  372. {
  373. throw new ArgumentNullException(nameof(value));
  374. }
  375. #if ENABLE_WINRT
  376. WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks;
  377. if (callbacks != null && callbacks.IsAppxModel())
  378. {
  379. callbacks.SetGlobalDefaultCulture(value);
  380. return;
  381. }
  382. #endif
  383. #if FEATURE_APPX
  384. if (ApplicationModel.IsUap)
  385. {
  386. if (SetCultureInfoForUserPreferredLanguageInAppX(value))
  387. {
  388. // successfully set the culture, otherwise fallback to legacy path
  389. return;
  390. }
  391. }
  392. #endif
  393. if (s_asyncLocalCurrentCulture == null)
  394. {
  395. Interlocked.CompareExchange(ref s_asyncLocalCurrentCulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentCulture), null);
  396. }
  397. s_asyncLocalCurrentCulture!.Value = value;
  398. }
  399. }
  400. public static CultureInfo CurrentUICulture
  401. {
  402. get
  403. {
  404. #if ENABLE_WINRT
  405. WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks;
  406. if (callbacks != null && callbacks.IsAppxModel())
  407. {
  408. return (CultureInfo)callbacks.GetUserDefaultCulture();
  409. }
  410. #endif
  411. #if FEATURE_APPX
  412. if (ApplicationModel.IsUap)
  413. {
  414. CultureInfo? culture = GetCultureInfoForUserPreferredLanguageInAppX();
  415. if (culture != null)
  416. return culture;
  417. }
  418. #endif
  419. return s_currentThreadUICulture ??
  420. s_DefaultThreadCurrentUICulture ??
  421. UserDefaultUICulture;
  422. }
  423. set
  424. {
  425. if (value == null)
  426. {
  427. throw new ArgumentNullException(nameof(value));
  428. }
  429. CultureInfo.VerifyCultureName(value, true);
  430. #if ENABLE_WINRT
  431. WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks;
  432. if (callbacks != null && callbacks.IsAppxModel())
  433. {
  434. callbacks.SetGlobalDefaultCulture(value);
  435. return;
  436. }
  437. #endif
  438. #if FEATURE_APPX
  439. if (ApplicationModel.IsUap)
  440. {
  441. if (SetCultureInfoForUserPreferredLanguageInAppX(value))
  442. {
  443. // successfully set the culture, otherwise fallback to legacy path
  444. return;
  445. }
  446. }
  447. #endif
  448. if (s_asyncLocalCurrentUICulture == null)
  449. {
  450. Interlocked.CompareExchange(ref s_asyncLocalCurrentUICulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentUICulture), null);
  451. }
  452. // this one will set s_currentThreadUICulture too
  453. s_asyncLocalCurrentUICulture!.Value = value;
  454. }
  455. }
  456. internal static CultureInfo UserDefaultUICulture => s_userDefaultUICulture ?? InitializeUserDefaultUICulture();
  457. public static CultureInfo InstalledUICulture => s_userDefaultCulture ?? InitializeUserDefaultCulture();
  458. public static CultureInfo? DefaultThreadCurrentCulture
  459. {
  460. get => s_DefaultThreadCurrentCulture;
  461. set
  462. {
  463. // If you add pre-conditions to this method, check to see if you also need to
  464. // add them to Thread.CurrentCulture.set.
  465. s_DefaultThreadCurrentCulture = value;
  466. }
  467. }
  468. public static CultureInfo? DefaultThreadCurrentUICulture
  469. {
  470. get => s_DefaultThreadCurrentUICulture;
  471. set
  472. {
  473. // If they're trying to use a Culture with a name that we can't use in resource lookup,
  474. // don't even let them set it on the thread.
  475. // If you add more pre-conditions to this method, check to see if you also need to
  476. // add them to Thread.CurrentUICulture.set.
  477. if (value != null)
  478. {
  479. CultureInfo.VerifyCultureName(value, true);
  480. }
  481. s_DefaultThreadCurrentUICulture = value;
  482. }
  483. }
  484. /// <summary>
  485. /// This instance provides methods, for example for casing and sorting,
  486. /// that are independent of the system and current user settings. It
  487. /// should be used only by processes such as some system services that
  488. /// require such invariant results (eg. file systems). In general,
  489. /// the results are not linguistically correct and do not match any
  490. /// culture info.
  491. /// </summary>
  492. public static CultureInfo InvariantCulture
  493. {
  494. get
  495. {
  496. Debug.Assert(s_InvariantCultureInfo != null);
  497. return s_InvariantCultureInfo;
  498. }
  499. }
  500. /// <summary>
  501. /// Return the parent CultureInfo for the current instance.
  502. /// </summary>
  503. public virtual CultureInfo Parent
  504. {
  505. get
  506. {
  507. if (_parent == null)
  508. {
  509. CultureInfo culture;
  510. string parentName = _cultureData.ParentName;
  511. if (string.IsNullOrEmpty(parentName))
  512. {
  513. culture = InvariantCulture;
  514. }
  515. else
  516. {
  517. culture = CreateCultureInfoNoThrow(parentName, _cultureData.UseUserOverride) ??
  518. // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant
  519. // We can't allow ourselves to fail. In case of custom cultures the parent of the
  520. // current custom culture isn't installed.
  521. InvariantCulture;
  522. }
  523. Interlocked.CompareExchange<CultureInfo?>(ref _parent, culture, null);
  524. }
  525. return _parent!;
  526. }
  527. }
  528. public virtual int LCID => _cultureData.LCID;
  529. public virtual int KeyboardLayoutId => _cultureData.KeyboardLayoutId;
  530. public static CultureInfo[] GetCultures(CultureTypes types)
  531. {
  532. // internally we treat UserCustomCultures as Supplementals but v2
  533. // treats as Supplementals and Replacements
  534. if ((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture)
  535. {
  536. types |= CultureTypes.ReplacementCultures;
  537. }
  538. return CultureData.GetCultures(types);
  539. }
  540. /// <summary>
  541. /// Returns the full name of the CultureInfo. The name is in format like
  542. /// "en-US" This version does NOT include sort information in the name.
  543. /// </summary>
  544. public virtual string Name
  545. {
  546. get
  547. {
  548. // We return non sorting name here.
  549. if (_nonSortName == null)
  550. {
  551. _nonSortName = _cultureData.Name ?? string.Empty;
  552. }
  553. return _nonSortName;
  554. }
  555. }
  556. /// <summary>
  557. /// This one has the sort information (ie: de-DE_phoneb)
  558. /// </summary>
  559. internal string SortName
  560. {
  561. get
  562. {
  563. if (_sortName == null)
  564. {
  565. _sortName = _cultureData.SortName;
  566. }
  567. return _sortName;
  568. }
  569. }
  570. public string IetfLanguageTag
  571. {
  572. get
  573. {
  574. // special case the compatibility cultures
  575. switch (this.Name)
  576. {
  577. case "zh-CHT":
  578. return "zh-Hant";
  579. case "zh-CHS":
  580. return "zh-Hans";
  581. default:
  582. return this.Name;
  583. }
  584. }
  585. }
  586. /// <summary>
  587. /// Returns the full name of the CultureInfo in the localized language.
  588. /// For example, if the localized language of the runtime is Spanish and the CultureInfo is
  589. /// US English, "Ingles (Estados Unidos)" will be returned.
  590. /// </summary>
  591. public virtual string DisplayName
  592. {
  593. get
  594. {
  595. Debug.Assert(_name != null, "[CultureInfo.DisplayName] Always expect _name to be set");
  596. return _cultureData.DisplayName;
  597. }
  598. }
  599. /// <summary>
  600. /// Returns the full name of the CultureInfo in the native language.
  601. /// For example, if the CultureInfo is US English, "English
  602. /// (United States)" will be returned.
  603. /// </summary>
  604. public virtual string NativeName => _cultureData.NativeName;
  605. /// <summary>
  606. /// Returns the full name of the CultureInfo in English.
  607. /// For example, if the CultureInfo is US English, "English
  608. /// (United States)" will be returned.
  609. /// </summary>
  610. public virtual string EnglishName => _cultureData.EnglishName;
  611. /// <summary>
  612. /// ie: en
  613. /// </summary>
  614. public virtual string TwoLetterISOLanguageName => _cultureData.TwoLetterISOLanguageName;
  615. /// <summary>
  616. /// ie: eng
  617. /// </summary>
  618. public virtual string ThreeLetterISOLanguageName => _cultureData.ThreeLetterISOLanguageName;
  619. /// <summary>
  620. /// Returns the 3 letter windows language name for the current instance. eg: "ENU"
  621. /// The ISO names are much preferred
  622. /// </summary>
  623. public virtual string ThreeLetterWindowsLanguageName => _cultureData.ThreeLetterWindowsLanguageName;
  624. // CompareInfo Read-Only Property
  625. /// <summary>
  626. /// Gets the CompareInfo for this culture.
  627. /// </summary>
  628. public virtual CompareInfo CompareInfo
  629. {
  630. get
  631. {
  632. if (_compareInfo == null)
  633. {
  634. // Since CompareInfo's don't have any overrideable properties, get the CompareInfo from
  635. // the Non-Overridden CultureInfo so that we only create one CompareInfo per culture
  636. _compareInfo = UseUserOverride
  637. ? GetCultureInfo(_name).CompareInfo
  638. : new CompareInfo(this);
  639. }
  640. return _compareInfo;
  641. }
  642. }
  643. /// <summary>
  644. /// Gets the TextInfo for this culture.
  645. /// </summary>
  646. public virtual TextInfo TextInfo
  647. {
  648. get
  649. {
  650. if (_textInfo == null)
  651. {
  652. // Make a new textInfo
  653. TextInfo tempTextInfo = new TextInfo(_cultureData);
  654. tempTextInfo.SetReadOnlyState(_isReadOnly);
  655. _textInfo = tempTextInfo;
  656. }
  657. return _textInfo;
  658. }
  659. }
  660. public override bool Equals(object? value)
  661. {
  662. if (object.ReferenceEquals(this, value))
  663. {
  664. return true;
  665. }
  666. if (value is CultureInfo that)
  667. {
  668. // using CompareInfo to verify the data passed through the constructor
  669. // CultureInfo(String cultureName, String textAndCompareCultureName)
  670. return Name.Equals(that.Name) && CompareInfo.Equals(that.CompareInfo);
  671. }
  672. return false;
  673. }
  674. public override int GetHashCode()
  675. {
  676. return Name.GetHashCode() + CompareInfo.GetHashCode();
  677. }
  678. /// <summary>
  679. /// Implements object.ToString(). Returns the name of the CultureInfo,
  680. /// eg. "de-DE_phoneb", "en-US", or "fj-FJ".
  681. /// </summary>
  682. public override string ToString() => _name;
  683. public virtual object? GetFormat(Type? formatType)
  684. {
  685. if (formatType == typeof(NumberFormatInfo))
  686. {
  687. return NumberFormat;
  688. }
  689. if (formatType == typeof(DateTimeFormatInfo))
  690. {
  691. return DateTimeFormat;
  692. }
  693. return null;
  694. }
  695. public virtual bool IsNeutralCulture => _cultureData.IsNeutralCulture;
  696. public CultureTypes CultureTypes
  697. {
  698. get
  699. {
  700. CultureTypes types = 0;
  701. if (_cultureData.IsNeutralCulture)
  702. {
  703. types |= CultureTypes.NeutralCultures;
  704. }
  705. else
  706. {
  707. types |= CultureTypes.SpecificCultures;
  708. }
  709. types |= _cultureData.IsWin32Installed ? CultureTypes.InstalledWin32Cultures : 0;
  710. // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
  711. #pragma warning disable 618
  712. types |= _cultureData.IsFramework ? CultureTypes.FrameworkCultures : 0;
  713. #pragma warning restore 618
  714. types |= _cultureData.IsSupplementalCustomCulture ? CultureTypes.UserCustomCulture : 0;
  715. types |= _cultureData.IsReplacementCulture ? CultureTypes.ReplacementCultures | CultureTypes.UserCustomCulture : 0;
  716. return types;
  717. }
  718. }
  719. public virtual NumberFormatInfo NumberFormat
  720. {
  721. get
  722. {
  723. if (_numInfo == null)
  724. {
  725. NumberFormatInfo temp = new NumberFormatInfo(_cultureData);
  726. temp._isReadOnly = _isReadOnly;
  727. Interlocked.CompareExchange(ref _numInfo, temp, null);
  728. }
  729. return _numInfo!;
  730. }
  731. set
  732. {
  733. if (value == null)
  734. {
  735. throw new ArgumentNullException(nameof(value));
  736. }
  737. VerifyWritable();
  738. _numInfo = value;
  739. }
  740. }
  741. /// <summary>
  742. /// Create a DateTimeFormatInfo, and fill in the properties according to
  743. /// the CultureID.
  744. /// </summary>
  745. public virtual DateTimeFormatInfo DateTimeFormat
  746. {
  747. get
  748. {
  749. if (_dateTimeInfo == null)
  750. {
  751. // Change the calendar of DTFI to the specified calendar of this CultureInfo.
  752. DateTimeFormatInfo temp = new DateTimeFormatInfo(_cultureData, this.Calendar);
  753. temp._isReadOnly = _isReadOnly;
  754. Interlocked.CompareExchange(ref _dateTimeInfo, temp, null);
  755. }
  756. return _dateTimeInfo!;
  757. }
  758. set
  759. {
  760. if (value == null)
  761. {
  762. throw new ArgumentNullException(nameof(value));
  763. }
  764. VerifyWritable();
  765. _dateTimeInfo = value;
  766. }
  767. }
  768. public void ClearCachedData()
  769. {
  770. // reset the default culture values
  771. s_userDefaultCulture = GetUserDefaultCulture();
  772. s_userDefaultUICulture = GetUserDefaultUICulture();
  773. RegionInfo.s_currentRegionInfo = null;
  774. #pragma warning disable 0618 // disable the obsolete warning
  775. TimeZone.ResetTimeZone();
  776. #pragma warning restore 0618
  777. TimeZoneInfo.ClearCachedData();
  778. s_LcidCachedCultures = null;
  779. s_NameCachedCultures = null;
  780. CultureData.ClearCachedData();
  781. }
  782. /// <summary>
  783. /// Map a Win32 CALID to an instance of supported calendar.
  784. /// </summary>
  785. /// <remarks>
  786. /// Shouldn't throw exception since the calType value is from our data
  787. /// table or from Win32 registry.
  788. /// If we are in trouble (like getting a weird value from Win32
  789. /// registry), just return the GregorianCalendar.
  790. /// </remarks>
  791. internal static Calendar GetCalendarInstance(CalendarId calType)
  792. {
  793. if (calType == CalendarId.GREGORIAN)
  794. {
  795. return new GregorianCalendar();
  796. }
  797. return GetCalendarInstanceRare(calType);
  798. }
  799. /// <summary>
  800. /// This function exists as a shortcut to prevent us from loading all of the non-gregorian
  801. /// calendars unless they're required.
  802. /// </summary>
  803. internal static Calendar GetCalendarInstanceRare(CalendarId calType)
  804. {
  805. Debug.Assert(calType != CalendarId.GREGORIAN, "calType!=CalendarId.GREGORIAN");
  806. switch (calType)
  807. {
  808. case CalendarId.GREGORIAN_US: // Gregorian (U.S.) calendar
  809. case CalendarId.GREGORIAN_ME_FRENCH: // Gregorian Middle East French calendar
  810. case CalendarId.GREGORIAN_ARABIC: // Gregorian Arabic calendar
  811. case CalendarId.GREGORIAN_XLIT_ENGLISH: // Gregorian Transliterated English calendar
  812. case CalendarId.GREGORIAN_XLIT_FRENCH: // Gregorian Transliterated French calendar
  813. return new GregorianCalendar((GregorianCalendarTypes)calType);
  814. case CalendarId.TAIWAN: // Taiwan Era calendar
  815. return new TaiwanCalendar();
  816. case CalendarId.JAPAN: // Japanese Emperor Era calendar
  817. return new JapaneseCalendar();
  818. case CalendarId.KOREA: // Korean Tangun Era calendar
  819. return new KoreanCalendar();
  820. case CalendarId.THAI: // Thai calendar
  821. return new ThaiBuddhistCalendar();
  822. case CalendarId.HIJRI: // Hijri (Arabic Lunar) calendar
  823. return new HijriCalendar();
  824. case CalendarId.HEBREW: // Hebrew (Lunar) calendar
  825. return new HebrewCalendar();
  826. case CalendarId.UMALQURA:
  827. return new UmAlQuraCalendar();
  828. case CalendarId.PERSIAN:
  829. return new PersianCalendar();
  830. }
  831. return new GregorianCalendar();
  832. }
  833. /// <summary>
  834. /// Return/set the default calendar used by this culture.
  835. /// This value can be overridden by regional option if this is a current culture.
  836. /// </summary>
  837. public virtual Calendar Calendar
  838. {
  839. get
  840. {
  841. if (_calendar == null)
  842. {
  843. Debug.Assert(_cultureData.CalendarIds.Length > 0, "_cultureData.CalendarIds.Length > 0");
  844. // Get the default calendar for this culture. Note that the value can be
  845. // from registry if this is a user default culture.
  846. Calendar newObj = _cultureData.DefaultCalendar;
  847. Interlocked.MemoryBarrier();
  848. newObj.SetReadOnlyState(_isReadOnly);
  849. _calendar = newObj;
  850. }
  851. return _calendar;
  852. }
  853. }
  854. /// <summary>
  855. /// Return an array of the optional calendar for this culture.
  856. /// </summary>
  857. public virtual Calendar[] OptionalCalendars
  858. {
  859. get
  860. {
  861. // This property always returns a new copy of the calendar array.
  862. CalendarId[] calID = _cultureData.CalendarIds;
  863. Calendar[] cals = new Calendar[calID.Length];
  864. for (int i = 0; i < cals.Length; i++)
  865. {
  866. cals[i] = GetCalendarInstance(calID[i]);
  867. }
  868. return cals;
  869. }
  870. }
  871. public bool UseUserOverride => _cultureData.UseUserOverride;
  872. public CultureInfo GetConsoleFallbackUICulture()
  873. {
  874. CultureInfo? temp = _consoleFallbackCulture;
  875. if (temp == null)
  876. {
  877. temp = CreateSpecificCulture(_cultureData.SCONSOLEFALLBACKNAME);
  878. temp._isReadOnly = true;
  879. _consoleFallbackCulture = temp;
  880. }
  881. return temp;
  882. }
  883. public virtual object Clone()
  884. {
  885. CultureInfo ci = (CultureInfo)MemberwiseClone();
  886. ci._isReadOnly = false;
  887. // If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
  888. // they've already been allocated. If this is a derived type, we'll take a more generic codepath.
  889. if (!_isInherited)
  890. {
  891. if (_dateTimeInfo != null)
  892. {
  893. ci._dateTimeInfo = (DateTimeFormatInfo)_dateTimeInfo.Clone();
  894. }
  895. if (_numInfo != null)
  896. {
  897. ci._numInfo = (NumberFormatInfo)_numInfo.Clone();
  898. }
  899. }
  900. else
  901. {
  902. ci.DateTimeFormat = (DateTimeFormatInfo)this.DateTimeFormat.Clone();
  903. ci.NumberFormat = (NumberFormatInfo)this.NumberFormat.Clone();
  904. }
  905. if (_textInfo != null)
  906. {
  907. ci._textInfo = (TextInfo)_textInfo.Clone();
  908. }
  909. if (_calendar != null)
  910. {
  911. ci._calendar = (Calendar)_calendar.Clone();
  912. }
  913. return ci;
  914. }
  915. public static CultureInfo ReadOnly(CultureInfo ci)
  916. {
  917. if (ci == null)
  918. {
  919. throw new ArgumentNullException(nameof(ci));
  920. }
  921. if (ci.IsReadOnly)
  922. {
  923. return ci;
  924. }
  925. CultureInfo newInfo = (CultureInfo)(ci.MemberwiseClone());
  926. if (!ci.IsNeutralCulture)
  927. {
  928. // If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
  929. // they've already been allocated. If this is a derived type, we'll take a more generic codepath.
  930. if (!ci._isInherited)
  931. {
  932. if (ci._dateTimeInfo != null)
  933. {
  934. newInfo._dateTimeInfo = DateTimeFormatInfo.ReadOnly(ci._dateTimeInfo);
  935. }
  936. if (ci._numInfo != null)
  937. {
  938. newInfo._numInfo = NumberFormatInfo.ReadOnly(ci._numInfo);
  939. }
  940. }
  941. else
  942. {
  943. newInfo.DateTimeFormat = DateTimeFormatInfo.ReadOnly(ci.DateTimeFormat);
  944. newInfo.NumberFormat = NumberFormatInfo.ReadOnly(ci.NumberFormat);
  945. }
  946. }
  947. if (ci._textInfo != null)
  948. {
  949. newInfo._textInfo = TextInfo.ReadOnly(ci._textInfo);
  950. }
  951. if (ci._calendar != null)
  952. {
  953. newInfo._calendar = Calendar.ReadOnly(ci._calendar);
  954. }
  955. // Don't set the read-only flag too early.
  956. // We should set the read-only flag here. Otherwise, info.DateTimeFormat will not be able to set.
  957. newInfo._isReadOnly = true;
  958. return newInfo;
  959. }
  960. public bool IsReadOnly => _isReadOnly;
  961. private void VerifyWritable()
  962. {
  963. if (_isReadOnly)
  964. {
  965. throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
  966. }
  967. }
  968. /// <summary>
  969. /// For resource lookup, we consider a culture the invariant culture by name equality.
  970. /// We perform this check frequently during resource lookup, so adding a property for
  971. /// improved readability.
  972. /// </summary>
  973. internal bool HasInvariantCultureName
  974. {
  975. get => Name == CultureInfo.InvariantCulture.Name;
  976. }
  977. /// <summary>
  978. /// Helper function overloads of GetCachedReadOnlyCulture. If lcid is 0, we use the name.
  979. /// If lcid is -1, use the altName and create one of those special SQL cultures.
  980. /// </summary>
  981. internal static CultureInfo? GetCultureInfoHelper(int lcid, string? name, string? altName)
  982. {
  983. // retval is our return value.
  984. CultureInfo? retval;
  985. // Temporary hashtable for the names.
  986. Dictionary<string, CultureInfo>? tempNameHT = s_NameCachedCultures;
  987. if (name != null)
  988. {
  989. name = CultureData.AnsiToLower(name);
  990. }
  991. if (altName != null)
  992. {
  993. altName = CultureData.AnsiToLower(altName);
  994. }
  995. // We expect the same result for both hashtables, but will test individually for added safety.
  996. if (tempNameHT == null)
  997. {
  998. tempNameHT = new Dictionary<string, CultureInfo>();
  999. }
  1000. else
  1001. {
  1002. // If we are called by name, check if the object exists in the hashtable. If so, return it.
  1003. if (lcid == -1 || lcid == 0)
  1004. {
  1005. Debug.Assert(name != null && (lcid != -1 || altName != null));
  1006. bool ret;
  1007. lock (_lock)
  1008. {
  1009. ret = tempNameHT.TryGetValue(lcid == 0 ? name! : name! + '\xfffd' + altName!, out retval);
  1010. }
  1011. if (ret && retval != null)
  1012. {
  1013. return retval;
  1014. }
  1015. }
  1016. }
  1017. // Next, the Lcid table.
  1018. Dictionary<int, CultureInfo>? tempLcidHT = s_LcidCachedCultures;
  1019. if (tempLcidHT == null)
  1020. {
  1021. // Case insensitive is not an issue here, save the constructor call.
  1022. tempLcidHT = new Dictionary<int, CultureInfo>();
  1023. }
  1024. else
  1025. {
  1026. // If we were called by Lcid, check if the object exists in the table. If so, return it.
  1027. if (lcid > 0)
  1028. {
  1029. bool ret;
  1030. lock (_lock)
  1031. {
  1032. ret = tempLcidHT.TryGetValue(lcid, out retval);
  1033. }
  1034. if (ret && retval != null)
  1035. {
  1036. return retval;
  1037. }
  1038. }
  1039. }
  1040. // We now have two temporary hashtables and the desired object was not found.
  1041. // We'll construct it. We catch any exceptions from the constructor call and return null.
  1042. try
  1043. {
  1044. switch (lcid)
  1045. {
  1046. case -1:
  1047. // call the private constructor
  1048. Debug.Assert(name != null && altName != null);
  1049. retval = new CultureInfo(name!, altName!);
  1050. break;
  1051. case 0:
  1052. Debug.Assert(name != null);
  1053. retval = new CultureInfo(name!, false);
  1054. break;
  1055. default:
  1056. retval = new CultureInfo(lcid, false);
  1057. break;
  1058. }
  1059. }
  1060. catch (ArgumentException)
  1061. {
  1062. return null;
  1063. }
  1064. // Set it to read-only
  1065. retval._isReadOnly = true;
  1066. if (lcid == -1)
  1067. {
  1068. lock (_lock)
  1069. {
  1070. // This new culture will be added only to the name hash table.
  1071. tempNameHT[name + '\xfffd' + altName] = retval;
  1072. }
  1073. // when lcid == -1 then TextInfo object is already get created and we need to set it as read only.
  1074. retval.TextInfo.SetReadOnlyState(true);
  1075. }
  1076. else if (lcid == 0)
  1077. {
  1078. // Remember our name (as constructed). Do NOT use alternate sort name versions because
  1079. // we have internal state representing the sort. (So someone would get the wrong cached version)
  1080. string newName = CultureData.AnsiToLower(retval._name);
  1081. // We add this new culture info object to both tables.
  1082. lock (_lock)
  1083. {
  1084. tempNameHT[newName] = retval;
  1085. }
  1086. }
  1087. else
  1088. {
  1089. lock (_lock)
  1090. {
  1091. tempLcidHT[lcid] = retval;
  1092. }
  1093. }
  1094. // Copy the two hashtables to the corresponding member variables. This will potentially overwrite
  1095. // new tables simultaneously created by a new thread, but maximizes thread safety.
  1096. if (-1 != lcid)
  1097. {
  1098. // Only when we modify the lcid hash table, is there a need to overwrite.
  1099. s_LcidCachedCultures = tempLcidHT;
  1100. }
  1101. s_NameCachedCultures = tempNameHT;
  1102. // Finally, return our new CultureInfo object.
  1103. return retval;
  1104. }
  1105. /// <summary>
  1106. /// Gets a cached copy of the specified culture from an internal
  1107. /// hashtable (or creates it if not found). (LCID version)
  1108. /// </summary>
  1109. public static CultureInfo GetCultureInfo(int culture)
  1110. {
  1111. // Must check for -1 now since the helper function uses the value to signal
  1112. // the altCulture code path for SQL Server.
  1113. // Also check for zero as this would fail trying to add as a key to the hash.
  1114. if (culture <= 0)
  1115. {
  1116. throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
  1117. }
  1118. CultureInfo? retval = GetCultureInfoHelper(culture, null, null);
  1119. if (null == retval)
  1120. {
  1121. throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
  1122. }
  1123. return retval;
  1124. }
  1125. /// <summary>
  1126. /// Gets a cached copy of the specified culture from an internal
  1127. /// hashtable (or creates it if not found). (Named version)
  1128. /// </summary>
  1129. public static CultureInfo GetCultureInfo(string name)
  1130. {
  1131. // Make sure we have a valid, non-zero length string as name
  1132. if (name == null)
  1133. {
  1134. throw new ArgumentNullException(nameof(name));
  1135. }
  1136. CultureInfo? retval = GetCultureInfoHelper(0, name, null);
  1137. if (retval == null)
  1138. {
  1139. throw new CultureNotFoundException(
  1140. nameof(name), name, SR.Argument_CultureNotSupported);
  1141. }
  1142. return retval;
  1143. }
  1144. /// <summary>
  1145. /// Gets a cached copy of the specified culture from an internal
  1146. /// hashtable (or creates it if not found).
  1147. /// </summary>
  1148. public static CultureInfo GetCultureInfo(string name, string altName)
  1149. {
  1150. if (name == null)
  1151. {
  1152. throw new ArgumentNullException(nameof(name));
  1153. }
  1154. if (altName == null)
  1155. {
  1156. throw new ArgumentNullException(nameof(altName));
  1157. }
  1158. CultureInfo? retval = GetCultureInfoHelper(-1, name, altName);
  1159. if (retval == null)
  1160. {
  1161. throw new CultureNotFoundException("name or altName",
  1162. SR.Format(SR.Argument_OneOfCulturesNotSupported, name, altName));
  1163. }
  1164. return retval;
  1165. }
  1166. public static CultureInfo GetCultureInfoByIetfLanguageTag(string name)
  1167. {
  1168. // Disallow old zh-CHT/zh-CHS names
  1169. if (name == "zh-CHT" || name == "zh-CHS")
  1170. {
  1171. throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));
  1172. }
  1173. CultureInfo ci = GetCultureInfo(name);
  1174. // Disallow alt sorts and es-es_TS
  1175. if (ci.LCID > 0xffff || ci.LCID == 0x040a)
  1176. {
  1177. throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));
  1178. }
  1179. return ci;
  1180. }
  1181. }
  1182. }