2
0

CultureData.cs 93 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252
  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. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Text;
  7. namespace System.Globalization
  8. {
  9. /// <summary>
  10. /// List of culture data
  11. /// Note the we cache overrides.
  12. /// Note that localized names (resource names) aren't available from here.
  13. /// </summary>
  14. /// <remarks>
  15. /// Our names are a tad confusing.
  16. ///
  17. /// sWindowsName -- The name that windows thinks this culture is, ie:
  18. /// en-US if you pass in en-US
  19. /// de-DE_phoneb if you pass in de-DE_phoneb
  20. /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
  21. /// fj if you pass in fj (neutral, post-Windows 7 machine)
  22. ///
  23. /// sRealName -- The name you used to construct the culture, in pretty form
  24. /// en-US if you pass in EN-us
  25. /// en if you pass in en
  26. /// de-DE_phoneb if you pass in de-DE_phoneb
  27. ///
  28. /// sSpecificCulture -- The specific culture for this culture
  29. /// en-US for en-US
  30. /// en-US for en
  31. /// de-DE_phoneb for alt sort
  32. /// fj-FJ for fj (neutral)
  33. ///
  34. /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
  35. /// en-US if you pass in en-US
  36. /// en if you pass in en
  37. /// de-DE if you pass in de-DE_phoneb
  38. /// </remarks>
  39. internal partial class CultureData
  40. {
  41. private const int LocaleNameMaxLength = 85;
  42. private const int undef = -1;
  43. // Override flag
  44. private string _sRealName = null!; // Name you passed in (ie: en-US, en, or de-DE_phoneb). Initialized by helper called during initialization.
  45. private string? _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
  46. // Identity
  47. private string? _sName; // locale name (ie: en-us, NO sort info, but could be neutral)
  48. private string? _sParent; // Parent name (which may be a custom locale/culture)
  49. private string? _sLocalizedDisplayName; // Localized pretty name for this locale
  50. private string? _sEnglishDisplayName; // English pretty name for this locale
  51. private string? _sNativeDisplayName; // Native pretty name for this locale
  52. private string? _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
  53. // Language
  54. private string? _sISO639Language; // ISO 639 Language Name
  55. private string? _sISO639Language2; // ISO 639 Language Name
  56. private string? _sLocalizedLanguage; // Localized name for this language
  57. private string? _sEnglishLanguage; // English name for this language
  58. private string? _sNativeLanguage; // Native name of this language
  59. private string? _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
  60. private string? _sConsoleFallbackName; // The culture name for the console fallback UI culture
  61. private int _iInputLanguageHandle = undef; // input language handle
  62. // Region
  63. private string? _sRegionName; // (RegionInfo)
  64. private string? _sLocalizedCountry; // localized country name
  65. private string? _sEnglishCountry; // english country name (RegionInfo)
  66. private string? _sNativeCountry; // native country name
  67. private string? _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
  68. private string? _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
  69. private int _iGeoId = undef; // GeoId
  70. // Numbers
  71. private string? _sPositiveSign; // (user can override) positive sign
  72. private string? _sNegativeSign; // (user can override) negative sign
  73. // (nfi populates these 5, don't have to be = undef)
  74. private int _iDigits; // (user can override) number of fractional digits
  75. private int _iNegativeNumber; // (user can override) negative number format
  76. private int[]? _waGrouping; // (user can override) grouping of digits
  77. private string? _sDecimalSeparator; // (user can override) decimal separator
  78. private string? _sThousandSeparator; // (user can override) thousands separator
  79. private string? _sNaN; // Not a Number
  80. private string? _sPositiveInfinity; // + Infinity
  81. private string? _sNegativeInfinity; // - Infinity
  82. // Percent
  83. private int _iNegativePercent = undef; // Negative Percent (0-3)
  84. private int _iPositivePercent = undef; // Positive Percent (0-11)
  85. private string? _sPercent; // Percent (%) symbol
  86. private string? _sPerMille; // PerMille symbol
  87. // Currency
  88. private string? _sCurrency; // (user can override) local monetary symbol
  89. private string? _sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
  90. private string? _sEnglishCurrency; // English name for this currency
  91. private string? _sNativeCurrency; // Native name for this currency
  92. // (nfi populates these 4, don't have to be = undef)
  93. private int _iCurrencyDigits; // (user can override) # local monetary fractional digits
  94. private int _iCurrency; // (user can override) positive currency format
  95. private int _iNegativeCurrency; // (user can override) negative currency format
  96. private int[]? _waMonetaryGrouping; // (user can override) monetary grouping of digits
  97. private string? _sMonetaryDecimal; // (user can override) monetary decimal separator
  98. private string? _sMonetaryThousand; // (user can override) monetary thousands separator
  99. // Misc
  100. private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
  101. private string? _sListSeparator; // (user can override) list separator
  102. // Time
  103. private string? _sAM1159; // (user can override) AM designator
  104. private string? _sPM2359; // (user can override) PM designator
  105. private string? _sTimeSeparator;
  106. private volatile string[]? _saLongTimes; // (user can override) time format
  107. private volatile string[]? _saShortTimes; // short time format
  108. private volatile string[]? _saDurationFormats; // time duration format
  109. // Calendar specific data
  110. private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
  111. private int _iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
  112. private volatile CalendarId[]? _waCalendars; // all available calendar type(s). The first one is the default calendar
  113. // Store for specific data about each calendar
  114. private CalendarData?[]? _calendars; // Store for specific calendar data
  115. // Text information
  116. private int _iReadingLayout = undef; // Reading layout data
  117. // 0 - Left to right (eg en-US)
  118. // 1 - Right to left (eg arabic locales)
  119. // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
  120. // 3 - Vertical top to bottom with columns proceeding to the right
  121. // CoreCLR depends on this even though its not exposed publicly.
  122. private int _iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
  123. private int _iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
  124. private int _iDefaultMacCodePage = undef; // default macintosh code page
  125. private int _iDefaultEbcdicCodePage = undef; // default EBCDIC code page
  126. private int _iLanguage; // locale ID (0409) - NO sort information
  127. private bool _bUseOverrides; // use user overrides?
  128. private bool _bNeutral; // Flags for the culture (ie: neutral or not right now)
  129. /// <summary>
  130. /// Region Name to Culture Name mapping table
  131. /// </summary>
  132. /// <remarks>
  133. /// Using a property so we avoid creating the dictionary until we need it
  134. /// </remarks>
  135. private static Dictionary<string, string> RegionNames =>
  136. s_regionNames ??=
  137. new Dictionary<string, string>(211 /* prime */)
  138. {
  139. { "029", "en-029" },
  140. { "AE", "ar-AE" },
  141. { "AF", "prs-AF" },
  142. { "AL", "sq-AL" },
  143. { "AM", "hy-AM" },
  144. { "AR", "es-AR" },
  145. { "AT", "de-AT" },
  146. { "AU", "en-AU" },
  147. { "AZ", "az-Cyrl-AZ" },
  148. { "BA", "bs-Latn-BA" },
  149. { "BD", "bn-BD" },
  150. { "BE", "nl-BE" },
  151. { "BG", "bg-BG" },
  152. { "BH", "ar-BH" },
  153. { "BN", "ms-BN" },
  154. { "BO", "es-BO" },
  155. { "BR", "pt-BR" },
  156. { "BY", "be-BY" },
  157. { "BZ", "en-BZ" },
  158. { "CA", "en-CA" },
  159. { "CH", "it-CH" },
  160. { "CL", "es-CL" },
  161. { "CN", "zh-CN" },
  162. { "CO", "es-CO" },
  163. { "CR", "es-CR" },
  164. { "CS", "sr-Cyrl-CS" },
  165. { "CZ", "cs-CZ" },
  166. { "DE", "de-DE" },
  167. { "DK", "da-DK" },
  168. { "DO", "es-DO" },
  169. { "DZ", "ar-DZ" },
  170. { "EC", "es-EC" },
  171. { "EE", "et-EE" },
  172. { "EG", "ar-EG" },
  173. { "ES", "es-ES" },
  174. { "ET", "am-ET" },
  175. { "FI", "fi-FI" },
  176. { "FO", "fo-FO" },
  177. { "FR", "fr-FR" },
  178. { "GB", "en-GB" },
  179. { "GE", "ka-GE" },
  180. { "GL", "kl-GL" },
  181. { "GR", "el-GR" },
  182. { "GT", "es-GT" },
  183. { "HK", "zh-HK" },
  184. { "HN", "es-HN" },
  185. { "HR", "hr-HR" },
  186. { "HU", "hu-HU" },
  187. { "ID", "id-ID" },
  188. { "IE", "en-IE" },
  189. { "IL", "he-IL" },
  190. { "IN", "hi-IN" },
  191. { "IQ", "ar-IQ" },
  192. { "IR", "fa-IR" },
  193. { "IS", "is-IS" },
  194. { "IT", "it-IT" },
  195. { "IV", "" },
  196. { "JM", "en-JM" },
  197. { "JO", "ar-JO" },
  198. { "JP", "ja-JP" },
  199. { "KE", "sw-KE" },
  200. { "KG", "ky-KG" },
  201. { "KH", "km-KH" },
  202. { "KR", "ko-KR" },
  203. { "KW", "ar-KW" },
  204. { "KZ", "kk-KZ" },
  205. { "LA", "lo-LA" },
  206. { "LB", "ar-LB" },
  207. { "LI", "de-LI" },
  208. { "LK", "si-LK" },
  209. { "LT", "lt-LT" },
  210. { "LU", "lb-LU" },
  211. { "LV", "lv-LV" },
  212. { "LY", "ar-LY" },
  213. { "MA", "ar-MA" },
  214. { "MC", "fr-MC" },
  215. { "ME", "sr-Latn-ME" },
  216. { "MK", "mk-MK" },
  217. { "MN", "mn-MN" },
  218. { "MO", "zh-MO" },
  219. { "MT", "mt-MT" },
  220. { "MV", "dv-MV" },
  221. { "MX", "es-MX" },
  222. { "MY", "ms-MY" },
  223. { "NG", "ig-NG" },
  224. { "NI", "es-NI" },
  225. { "NL", "nl-NL" },
  226. { "NO", "nn-NO" },
  227. { "NP", "ne-NP" },
  228. { "NZ", "en-NZ" },
  229. { "OM", "ar-OM" },
  230. { "PA", "es-PA" },
  231. { "PE", "es-PE" },
  232. { "PH", "en-PH" },
  233. { "PK", "ur-PK" },
  234. { "PL", "pl-PL" },
  235. { "PR", "es-PR" },
  236. { "PT", "pt-PT" },
  237. { "PY", "es-PY" },
  238. { "QA", "ar-QA" },
  239. { "RO", "ro-RO" },
  240. { "RS", "sr-Latn-RS" },
  241. { "RU", "ru-RU" },
  242. { "RW", "rw-RW" },
  243. { "SA", "ar-SA" },
  244. { "SE", "sv-SE" },
  245. { "SG", "zh-SG" },
  246. { "SI", "sl-SI" },
  247. { "SK", "sk-SK" },
  248. { "SN", "wo-SN" },
  249. { "SV", "es-SV" },
  250. { "SY", "ar-SY" },
  251. { "TH", "th-TH" },
  252. { "TJ", "tg-Cyrl-TJ" },
  253. { "TM", "tk-TM" },
  254. { "TN", "ar-TN" },
  255. { "TR", "tr-TR" },
  256. { "TT", "en-TT" },
  257. { "TW", "zh-TW" },
  258. { "UA", "uk-UA" },
  259. { "US", "en-US" },
  260. { "UY", "es-UY" },
  261. { "UZ", "uz-Cyrl-UZ" },
  262. { "VE", "es-VE" },
  263. { "VN", "vi-VN" },
  264. { "YE", "ar-YE" },
  265. { "ZA", "af-ZA" },
  266. { "ZW", "en-ZW" }
  267. };
  268. // Cache of regions we've already looked up
  269. private static volatile Dictionary<string, CultureData>? s_cachedRegions;
  270. private static volatile Dictionary<string, string>? s_regionNames;
  271. internal static CultureData? GetCultureDataForRegion(string? cultureName, bool useUserOverride)
  272. {
  273. // First do a shortcut for Invariant
  274. if (string.IsNullOrEmpty(cultureName))
  275. {
  276. return CultureData.Invariant;
  277. }
  278. // First check if GetCultureData() can find it (ie: its a real culture)
  279. CultureData? retVal = GetCultureData(cultureName, useUserOverride);
  280. if (retVal != null && !retVal.IsNeutralCulture)
  281. {
  282. return retVal;
  283. }
  284. // Not a specific culture, perhaps it's region-only name
  285. // (Remember this isn't a core clr path where that's not supported)
  286. // If it was neutral remember that so that RegionInfo() can throw the right exception
  287. CultureData? neutral = retVal;
  288. // Try the hash table next
  289. string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
  290. Dictionary<string, CultureData>? tempHashTable = s_cachedRegions;
  291. if (tempHashTable == null)
  292. {
  293. // No table yet, make a new one
  294. tempHashTable = new Dictionary<string, CultureData>();
  295. }
  296. else
  297. {
  298. // Check the hash table
  299. lock (s_lock)
  300. {
  301. tempHashTable.TryGetValue(hashName, out retVal);
  302. }
  303. if (retVal != null)
  304. {
  305. return retVal;
  306. }
  307. }
  308. // Not found in the hash table, look it up the hard way
  309. // If not a valid mapping from the registry we'll have to try the hard coded table
  310. if (retVal == null || retVal.IsNeutralCulture)
  311. {
  312. // Not a valid mapping, try the hard coded table
  313. string? name;
  314. if (RegionNames.TryGetValue(cultureName, out name))
  315. {
  316. // Make sure we can get culture data for it
  317. retVal = GetCultureData(name, useUserOverride);
  318. }
  319. }
  320. // If not found in the hard coded table we'll have to find a culture that works for us
  321. if (!GlobalizationMode.Invariant && (retVal == null || retVal.IsNeutralCulture))
  322. {
  323. retVal = GetCultureDataFromRegionName(cultureName);
  324. }
  325. // If we found one we can use, then cache it for next time
  326. if (retVal != null && !retVal.IsNeutralCulture)
  327. {
  328. // first add it to the cache
  329. lock (s_lock)
  330. {
  331. tempHashTable[hashName] = retVal;
  332. }
  333. // Copy the hashtable to the corresponding member variables. This will potentially overwrite
  334. // new tables simultaneously created by a new thread, but maximizes thread safety.
  335. s_cachedRegions = tempHashTable;
  336. }
  337. else
  338. {
  339. // Unable to find a matching culture/region, return null or neutral
  340. // (regionInfo throws a more specific exception on neutrals)
  341. retVal = neutral;
  342. }
  343. // Return the found culture to use, null, or the neutral culture.
  344. return retVal;
  345. }
  346. // Clear our internal caches
  347. internal static void ClearCachedData()
  348. {
  349. s_cachedCultures = null;
  350. s_cachedRegions = null;
  351. }
  352. internal static CultureInfo[] GetCultures(CultureTypes types)
  353. {
  354. // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
  355. #pragma warning disable 618
  356. // Validate flags
  357. if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
  358. CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
  359. CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
  360. CultureTypes.FrameworkCultures)) != 0)
  361. {
  362. throw new ArgumentOutOfRangeException(nameof(types),
  363. SR.Format(SR.ArgumentOutOfRange_Range, CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
  364. }
  365. // We have deprecated CultureTypes.FrameworkCultures.
  366. // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
  367. // We have deprecated CultureTypes.WindowsOnlyCultures.
  368. // When this enum is used, we will return an empty array for this enum.
  369. if ((types & CultureTypes.WindowsOnlyCultures) != 0)
  370. {
  371. // Remove the enum as it is an no-op.
  372. types &= (~CultureTypes.WindowsOnlyCultures);
  373. }
  374. if (GlobalizationMode.Invariant)
  375. {
  376. // in invariant mode we always return invariant culture only from the enumeration
  377. return new CultureInfo[] { new CultureInfo("") };
  378. }
  379. #pragma warning restore 618
  380. return EnumCultures(types);
  381. }
  382. private static CultureData CreateCultureWithInvariantData()
  383. {
  384. // Make a new culturedata
  385. CultureData invariant = new CultureData();
  386. // Basics
  387. // Note that we override the resources since this IS NOT supposed to change (by definition)
  388. invariant._bUseOverrides = false;
  389. invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
  390. invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
  391. // Identity
  392. invariant._sName = ""; // locale name (ie: en-us)
  393. invariant._sParent = ""; // Parent name (which may be a custom locale/culture)
  394. invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now)
  395. invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
  396. invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale
  397. invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture()
  398. // Language
  399. invariant._sISO639Language = "iv"; // ISO 639 Language Name
  400. invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2
  401. invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language
  402. invariant._sEnglishLanguage = "Invariant Language"; // English name for this language
  403. invariant._sNativeLanguage = "Invariant Language"; // Native name of this language
  404. invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name)
  405. invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture
  406. invariant._iInputLanguageHandle = 0x07F; // input language handle
  407. // Region
  408. invariant._sRegionName = "IV"; // (RegionInfo)
  409. invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo)
  410. invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only)
  411. invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US
  412. invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo)
  413. invariant._iGeoId = 244; // GeoId (Windows Only)
  414. // Numbers
  415. invariant._sPositiveSign = "+"; // positive sign
  416. invariant._sNegativeSign = "-"; // negative sign
  417. invariant._iDigits = 2; // number of fractional digits
  418. invariant._iNegativeNumber = 1; // negative number format
  419. invariant._waGrouping = new int[] { 3 }; // grouping of digits
  420. invariant._sDecimalSeparator = "."; // decimal separator
  421. invariant._sThousandSeparator = ","; // thousands separator
  422. invariant._sNaN = "NaN"; // Not a Number
  423. invariant._sPositiveInfinity = "Infinity"; // + Infinity
  424. invariant._sNegativeInfinity = "-Infinity"; // - Infinity
  425. // Percent
  426. invariant._iNegativePercent = 0; // Negative Percent (0-3)
  427. invariant._iPositivePercent = 0; // Positive Percent (0-11)
  428. invariant._sPercent = "%"; // Percent (%) symbol
  429. invariant._sPerMille = "\x2030"; // PerMille symbol
  430. // Currency
  431. invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol
  432. invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo)
  433. invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
  434. invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
  435. invariant._iCurrencyDigits = 2; // # local monetary fractional digits
  436. invariant._iCurrency = 0; // positive currency format
  437. invariant._iNegativeCurrency = 0; // negative currency format
  438. invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits
  439. invariant._sMonetaryDecimal = "."; // monetary decimal separator
  440. invariant._sMonetaryThousand = ","; // monetary thousands separator
  441. // Misc
  442. invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo)
  443. invariant._sListSeparator = ","; // list separator
  444. // Time
  445. invariant._sTimeSeparator = ":";
  446. invariant._sAM1159 = "AM"; // AM designator
  447. invariant._sPM2359 = "PM"; // PM designator
  448. invariant._saLongTimes = new string[] { "HH:mm:ss" }; // time format
  449. invariant._saShortTimes = new string[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
  450. invariant._saDurationFormats = new string[] { "HH:mm:ss" }; // time duration format
  451. // Calendar specific data
  452. invariant._iFirstDayOfWeek = 0; // first day of week
  453. invariant._iFirstWeekOfYear = 0; // first week of year
  454. invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar
  455. // Store for specific data about each calendar
  456. invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
  457. invariant._calendars[0] = CalendarData.Invariant;
  458. // Text information
  459. invariant._iReadingLayout = 0;
  460. // These are desktop only, not coreclr
  461. invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information
  462. invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP)
  463. invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM)
  464. invariant._iDefaultMacCodePage = 10000; // default macintosh code page
  465. invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
  466. if (GlobalizationMode.Invariant)
  467. {
  468. invariant._sLocalizedDisplayName = invariant._sNativeDisplayName;
  469. invariant._sLocalizedCountry = invariant._sNativeCountry;
  470. }
  471. return invariant;
  472. }
  473. /// <summary>
  474. /// Build our invariant information
  475. /// We need an invariant instance, which we build hard-coded
  476. /// </summary>
  477. internal static CultureData Invariant => s_Invariant ??= CreateCultureWithInvariantData();
  478. private static volatile CultureData? s_Invariant;
  479. // Cache of cultures we've already looked up
  480. private static volatile Dictionary<string, CultureData>? s_cachedCultures;
  481. private static readonly object s_lock = new object();
  482. internal static CultureData? GetCultureData(string? cultureName, bool useUserOverride)
  483. {
  484. // First do a shortcut for Invariant
  485. if (string.IsNullOrEmpty(cultureName))
  486. {
  487. return CultureData.Invariant;
  488. }
  489. // Try the hash table first
  490. string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
  491. Dictionary<string, CultureData>? tempHashTable = s_cachedCultures;
  492. if (tempHashTable == null)
  493. {
  494. // No table yet, make a new one
  495. tempHashTable = new Dictionary<string, CultureData>();
  496. }
  497. else
  498. {
  499. // Check the hash table
  500. bool ret;
  501. CultureData? retVal;
  502. lock (s_lock)
  503. {
  504. ret = tempHashTable.TryGetValue(hashName, out retVal);
  505. }
  506. if (ret && retVal != null)
  507. {
  508. return retVal;
  509. }
  510. }
  511. // Not found in the hash table, need to see if we can build one that works for us
  512. CultureData? culture = CreateCultureData(cultureName, useUserOverride);
  513. if (culture == null)
  514. {
  515. return null;
  516. }
  517. // Found one, add it to the cache
  518. lock (s_lock)
  519. {
  520. tempHashTable[hashName] = culture;
  521. }
  522. // Copy the hashtable to the corresponding member variables. This will potentially overwrite
  523. // new tables simultaneously created by a new thread, but maximizes thread safety.
  524. s_cachedCultures = tempHashTable;
  525. return culture;
  526. }
  527. private static string NormalizeCultureName(string name, out bool isNeutralName)
  528. {
  529. isNeutralName = true;
  530. int i = 0;
  531. if (name.Length > LocaleNameMaxLength)
  532. {
  533. // Theoretically we shouldn't hit this exception.
  534. throw new ArgumentException(SR.Format(SR.Argument_InvalidId, nameof(name)));
  535. }
  536. Span<char> normalizedName = stackalloc char[name.Length];
  537. bool changed = false;
  538. while (i < name.Length && name[i] != '-' && name[i] != '_')
  539. {
  540. if (name[i] >= 'A' && name[i] <= 'Z')
  541. {
  542. // lowercase characters before '-'
  543. normalizedName[i] = (char)(((int)name[i]) + 0x20);
  544. changed = true;
  545. }
  546. else
  547. {
  548. normalizedName[i] = name[i];
  549. }
  550. i++;
  551. }
  552. if (i < name.Length)
  553. {
  554. // this is not perfect to detect the non neutral cultures but it is good enough when we are running in invariant mode
  555. isNeutralName = false;
  556. }
  557. while (i < name.Length)
  558. {
  559. if (name[i] >= 'a' && name[i] <= 'z')
  560. {
  561. normalizedName[i] = (char)(((int)name[i]) - 0x20);
  562. changed = true;
  563. }
  564. else
  565. {
  566. normalizedName[i] = name[i];
  567. }
  568. i++;
  569. }
  570. if (changed)
  571. {
  572. return new string(normalizedName);
  573. }
  574. return name;
  575. }
  576. private static CultureData? CreateCultureData(string cultureName, bool useUserOverride)
  577. {
  578. if (GlobalizationMode.Invariant)
  579. {
  580. if (cultureName.Length > LocaleNameMaxLength || !CultureInfo.VerifyCultureName(cultureName, false))
  581. {
  582. return null;
  583. }
  584. CultureData cd = CreateCultureWithInvariantData();
  585. cd._bUseOverrides = useUserOverride;
  586. cd._sName = NormalizeCultureName(cultureName, out cd._bNeutral);
  587. cd._sRealName = cd._sName;
  588. cd._sWindowsName = cd._sName;
  589. cd._iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
  590. return cd;
  591. }
  592. if (cultureName.Length == 1 && (cultureName[0] == 'C' || cultureName[0] == 'c'))
  593. {
  594. // Always map the "C" locale to Invariant to avoid mapping it to en_US_POSIX on Linux because POSIX
  595. // locale collation doesn't support case insensitive comparisons.
  596. // We do the same mapping on Windows for the sake of consistency.
  597. return CultureData.Invariant;
  598. }
  599. CultureData culture = new CultureData();
  600. culture._bUseOverrides = useUserOverride;
  601. culture._sRealName = cultureName;
  602. // Ask native code if that one's real
  603. if (!culture.InitCultureData() && !culture.InitCompatibilityCultureData())
  604. {
  605. return null;
  606. }
  607. return culture;
  608. }
  609. private bool InitCompatibilityCultureData()
  610. {
  611. // for compatibility handle the deprecated ids: zh-chs, zh-cht
  612. string cultureName = _sRealName!;
  613. string fallbackCultureName;
  614. string realCultureName;
  615. switch (AnsiToLower(cultureName))
  616. {
  617. case "zh-chs":
  618. fallbackCultureName = "zh-Hans";
  619. realCultureName = "zh-CHS";
  620. break;
  621. case "zh-cht":
  622. fallbackCultureName = "zh-Hant";
  623. realCultureName = "zh-CHT";
  624. break;
  625. default:
  626. return false;
  627. }
  628. _sRealName = fallbackCultureName;
  629. if (!InitCultureData())
  630. {
  631. return false;
  632. }
  633. // fixup our data
  634. _sName = realCultureName; // the name that goes back to the user
  635. _sParent = fallbackCultureName;
  636. return true;
  637. }
  638. /// We'd rather people use the named version since this doesn't allow custom locales
  639. internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
  640. {
  641. string? localeName = null;
  642. CultureData? retVal = null;
  643. if (culture == CultureInfo.LOCALE_INVARIANT)
  644. {
  645. return Invariant;
  646. }
  647. if (GlobalizationMode.Invariant)
  648. {
  649. // LCID is not supported in the InvariantMode
  650. throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
  651. }
  652. // Convert the lcid to a name, then use that
  653. // Note that this will return neutral names (unlike Vista native API)
  654. localeName = LCIDToLocaleName(culture);
  655. if (!string.IsNullOrEmpty(localeName))
  656. {
  657. // Valid name, use it
  658. retVal = GetCultureData(localeName, bUseUserOverride);
  659. }
  660. // If not successful, throw
  661. if (retVal == null)
  662. {
  663. throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
  664. }
  665. // Return the one we found
  666. return retVal;
  667. }
  668. /// <summary>
  669. /// The real name used to construct the locale (ie: de-DE_phoneb)
  670. /// </summary>
  671. internal string CultureName
  672. {
  673. get
  674. {
  675. Debug.Assert(_sRealName != null, "[CultureData.CultureName] Expected _sRealName to be populated by already");
  676. // since windows doesn't know about zh-CHS and zh-CHT,
  677. // we leave sRealName == zh-Hanx but we still need to
  678. // pretend that it was zh-CHX.
  679. switch (_sName)
  680. {
  681. case "zh-CHS":
  682. case "zh-CHT":
  683. return _sName;
  684. }
  685. return _sRealName;
  686. }
  687. }
  688. /// <summary>
  689. /// Are overrides enabled?
  690. /// </summary>
  691. internal bool UseUserOverride => _bUseOverrides;
  692. /// <summary>
  693. /// locale name (ie: de-DE, NO sort information)
  694. /// </summary>
  695. internal string Name => _sName ?? string.Empty;
  696. // Parent name (which may be a custom locale/culture)
  697. // Ask using the real name, so that we get parents of neutrals
  698. internal string ParentName => _sParent ??= GetLocaleInfo(_sRealName!, LocaleStringData.ParentName);
  699. // Localized pretty name for this locale (ie: Inglis (estados Unitos))
  700. internal string DisplayName
  701. {
  702. get
  703. {
  704. string? localizedDisplayName = _sLocalizedDisplayName;
  705. if (localizedDisplayName == null)
  706. {
  707. if (IsSupplementalCustomCulture)
  708. {
  709. if (IsNeutralCulture)
  710. {
  711. localizedDisplayName = NativeLanguageName;
  712. }
  713. else
  714. {
  715. localizedDisplayName = NativeName;
  716. }
  717. }
  718. else
  719. {
  720. try
  721. {
  722. const string ZH_CHT = "zh-CHT";
  723. const string ZH_CHS = "zh-CHS";
  724. if (Name.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase))
  725. {
  726. localizedDisplayName = GetLanguageDisplayName("zh-Hant");
  727. }
  728. else if (Name.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase))
  729. {
  730. localizedDisplayName = GetLanguageDisplayName("zh-Hans");
  731. }
  732. else
  733. {
  734. localizedDisplayName = GetLanguageDisplayName(Name);
  735. }
  736. }
  737. catch
  738. {
  739. // do nothing
  740. }
  741. }
  742. // If it hasn't been found (Windows 8 and up), fallback to the system
  743. if (string.IsNullOrEmpty(localizedDisplayName))
  744. {
  745. // If its neutral use the language name
  746. if (IsNeutralCulture)
  747. {
  748. localizedDisplayName = LocalizedLanguageName;
  749. }
  750. else
  751. {
  752. // Usually the UI culture shouldn't be different than what we got from WinRT except
  753. // if DefaultThreadCurrentUICulture was set
  754. CultureInfo ci;
  755. if (CultureInfo.DefaultThreadCurrentUICulture != null &&
  756. ((ci = CultureInfo.GetUserDefaultCulture()) != null) &&
  757. !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
  758. {
  759. localizedDisplayName = NativeName;
  760. }
  761. else
  762. {
  763. localizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName);
  764. }
  765. }
  766. }
  767. _sLocalizedDisplayName = localizedDisplayName;
  768. }
  769. return localizedDisplayName;
  770. }
  771. }
  772. /// <summary>
  773. /// English pretty name for this locale (ie: English (United States))
  774. /// </summary>
  775. internal string EnglishName
  776. {
  777. get
  778. {
  779. string? englishDisplayName = _sEnglishDisplayName;
  780. if (englishDisplayName == null)
  781. {
  782. // If its neutral use the language name
  783. if (IsNeutralCulture)
  784. {
  785. englishDisplayName = EnglishLanguageName;
  786. // differentiate the legacy display names
  787. switch (_sName)
  788. {
  789. case "zh-CHS":
  790. case "zh-CHT":
  791. englishDisplayName += " Legacy";
  792. break;
  793. }
  794. }
  795. else
  796. {
  797. englishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
  798. // if it isn't found build one:
  799. if (string.IsNullOrEmpty(englishDisplayName))
  800. {
  801. // Our existing names mostly look like:
  802. // "English" + "United States" -> "English (United States)"
  803. // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
  804. if (EnglishLanguageName[^1] == ')')
  805. {
  806. // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
  807. englishDisplayName = string.Concat(
  808. EnglishLanguageName.AsSpan(0, _sEnglishLanguage!.Length - 1),
  809. ", ",
  810. EnglishCountryName,
  811. ")");
  812. }
  813. else
  814. {
  815. // "English" + "United States" -> "English (United States)"
  816. englishDisplayName = EnglishLanguageName + " (" + EnglishCountryName + ")";
  817. }
  818. }
  819. }
  820. _sEnglishDisplayName = englishDisplayName;
  821. }
  822. return englishDisplayName;
  823. }
  824. }
  825. /// <summary>
  826. /// Native pretty name for this locale (ie: Deutsch (Deutschland))
  827. /// </summary>
  828. internal string NativeName
  829. {
  830. get
  831. {
  832. string? nativeDisplayName = _sNativeDisplayName;
  833. if (nativeDisplayName == null)
  834. {
  835. // If its neutral use the language name
  836. if (IsNeutralCulture)
  837. {
  838. nativeDisplayName = NativeLanguageName;
  839. // differentiate the legacy display names
  840. switch (_sName)
  841. {
  842. case "zh-CHS":
  843. nativeDisplayName += " \u65E7\u7248";
  844. break;
  845. case "zh-CHT":
  846. nativeDisplayName += " \u820A\u7248";
  847. break;
  848. }
  849. }
  850. else
  851. {
  852. nativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
  853. // if it isn't found build one:
  854. if (string.IsNullOrEmpty(nativeDisplayName))
  855. {
  856. // These should primarily be "Deutsch (Deutschland)" type names
  857. nativeDisplayName = NativeLanguageName + " (" + NativeCountryName + ")";
  858. }
  859. }
  860. _sNativeDisplayName = nativeDisplayName;
  861. }
  862. return nativeDisplayName;
  863. }
  864. }
  865. /// <summary>
  866. /// The culture name to be used in CultureInfo.CreateSpecificCulture()
  867. /// </summary>
  868. internal string SpecificCultureName
  869. {
  870. get
  871. {
  872. // This got populated during the culture initialization
  873. Debug.Assert(_sSpecificCulture != null, "[CultureData.SpecificCultureName] Expected this.sSpecificCulture to be populated by culture data initialization already");
  874. return _sSpecificCulture;
  875. }
  876. }
  877. /// <summary>
  878. /// iso 639 language name, ie: en
  879. /// </summary>
  880. internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfo(LocaleStringData.Iso639LanguageTwoLetterName);
  881. /// <summary>
  882. /// iso 639 language name, ie: eng
  883. /// </summary>
  884. internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfo(LocaleStringData.Iso639LanguageThreeLetterName);
  885. /// <summary>
  886. /// abbreviated windows language name (ie: enu) (non-standard, avoid this)
  887. /// </summary>
  888. internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GetThreeLetterWindowsLanguageName(_sRealName!);
  889. /// <summary>
  890. /// Localized name for this language (Windows Only) ie: Inglis
  891. /// This is only valid for Windows 8 and higher neutrals:
  892. /// </summary>
  893. private string LocalizedLanguageName
  894. {
  895. get
  896. {
  897. if (_sLocalizedLanguage == null)
  898. {
  899. // Usually the UI culture shouldn't be different than what we got from WinRT except
  900. // if DefaultThreadCurrentUICulture was set
  901. CultureInfo ci;
  902. if (CultureInfo.DefaultThreadCurrentUICulture != null &&
  903. ((ci = CultureInfo.GetUserDefaultCulture()) != null) &&
  904. !CultureInfo.DefaultThreadCurrentUICulture!.Name.Equals(ci.Name))
  905. {
  906. _sLocalizedLanguage = NativeLanguageName;
  907. }
  908. else
  909. {
  910. _sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName);
  911. }
  912. }
  913. return _sLocalizedLanguage;
  914. }
  915. }
  916. /// <summary>
  917. /// English name for this language (Windows Only) ie: German
  918. /// </summary>
  919. private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfo(LocaleStringData.EnglishLanguageName);
  920. /// <summary>
  921. /// Native name of this language (Windows Only) ie: Deutsch
  922. /// </summary>
  923. private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfo(LocaleStringData.NativeLanguageName);
  924. /// <summary>
  925. /// region name (eg US)
  926. /// </summary>
  927. internal string RegionName => _sRegionName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName);
  928. internal int GeoId
  929. {
  930. get
  931. {
  932. if (_iGeoId == undef)
  933. {
  934. _iGeoId = GetGeoId(_sRealName!);
  935. }
  936. return _iGeoId;
  937. }
  938. }
  939. /// <summary>
  940. /// localized name for the country
  941. /// </summary>
  942. internal string LocalizedCountryName
  943. {
  944. get
  945. {
  946. string? localizedCountry = _sLocalizedCountry;
  947. if (localizedCountry == null)
  948. {
  949. try
  950. {
  951. localizedCountry = GetRegionDisplayName();
  952. }
  953. catch
  954. {
  955. // do nothing. we'll fallback
  956. }
  957. localizedCountry ??= NativeCountryName;
  958. _sLocalizedCountry = localizedCountry;
  959. }
  960. return localizedCountry;
  961. }
  962. }
  963. /// <summary>
  964. /// english country name (RegionInfo) ie: Germany
  965. /// </summary>
  966. internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfo(LocaleStringData.EnglishCountryName);
  967. /// <summary>
  968. /// native country name (RegionInfo) ie: Deutschland
  969. /// </summary>
  970. internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfo(LocaleStringData.NativeCountryName);
  971. /// <summary>
  972. /// ISO 3166 Country Name
  973. /// </summary>
  974. internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName);
  975. /// <summary>
  976. /// 3 letter ISO 3166 country code
  977. /// </summary>
  978. internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName2);
  979. internal int KeyboardLayoutId
  980. {
  981. get
  982. {
  983. if (_iInputLanguageHandle == undef)
  984. {
  985. if (IsSupplementalCustomCulture)
  986. {
  987. _iInputLanguageHandle = 0x0409;
  988. }
  989. else
  990. {
  991. // Input Language is same as LCID for built-in cultures
  992. _iInputLanguageHandle = LCID;
  993. }
  994. }
  995. return _iInputLanguageHandle;
  996. }
  997. }
  998. /// <summary>
  999. /// Console fallback name (ie: locale to use for console apps for unicode-only locales)
  1000. /// </summary>
  1001. internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GetConsoleFallbackName(_sRealName!);
  1002. /// <summary>
  1003. /// (user can override) grouping of digits
  1004. /// </summary>
  1005. internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfo(LocaleGroupingData.Digit);
  1006. /// <summary>
  1007. /// Not a Number
  1008. /// </summary>
  1009. private string NaNSymbol => _sNaN ??= GetLocaleInfo(LocaleStringData.NaNSymbol);
  1010. /// <summary>
  1011. /// + Infinity
  1012. /// </summary>
  1013. private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol);
  1014. /// <summary>
  1015. /// - Infinity
  1016. /// </summary>
  1017. private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol);
  1018. /// <summary>
  1019. /// Negative Percent (0-3)
  1020. /// </summary>
  1021. private int PercentNegativePattern
  1022. {
  1023. get
  1024. {
  1025. if (_iNegativePercent == undef)
  1026. {
  1027. // Note that <= Windows Vista this is synthesized by native code
  1028. _iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat);
  1029. }
  1030. return _iNegativePercent;
  1031. }
  1032. }
  1033. /// <summary>
  1034. /// Positive Percent (0-11)
  1035. /// </summary>
  1036. private int PercentPositivePattern
  1037. {
  1038. get
  1039. {
  1040. if (_iPositivePercent == undef)
  1041. {
  1042. // Note that <= Windows Vista this is synthesized by native code
  1043. _iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat);
  1044. }
  1045. return _iPositivePercent;
  1046. }
  1047. }
  1048. /// <summary>
  1049. /// Percent (%) symbol
  1050. /// </summary>
  1051. private string PercentSymbol => _sPercent ??= GetLocaleInfo(LocaleStringData.PercentSymbol);
  1052. /// <summary>
  1053. /// PerMille symbol
  1054. /// </summary>
  1055. private string PerMilleSymbol => _sPerMille ??= GetLocaleInfo(LocaleStringData.PerMilleSymbol);
  1056. /// <summary>
  1057. /// (user can override) local monetary symbol, eg: $
  1058. /// </summary>
  1059. internal string CurrencySymbol => _sCurrency ??= GetLocaleInfo(LocaleStringData.MonetarySymbol);
  1060. /// <summary>
  1061. /// international monetary symbol (RegionInfo), eg: USD
  1062. /// </summary>
  1063. internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol);
  1064. /// <summary>
  1065. /// English name for this currency (RegionInfo), eg: US Dollar
  1066. /// </summary>
  1067. internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyEnglishName);
  1068. /// <summary>
  1069. /// Native name for this currency (RegionInfo), eg: Schweiz Frank
  1070. /// </summary>
  1071. internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyNativeName);
  1072. /// <summary>
  1073. /// (user can override) monetary grouping of digits
  1074. /// </summary>
  1075. internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfo(LocaleGroupingData.Monetary);
  1076. /// <summary>
  1077. /// (user can override) system of measurement 0=metric, 1=US (RegionInfo)
  1078. /// </summary>
  1079. internal int MeasurementSystem
  1080. {
  1081. get
  1082. {
  1083. if (_iMeasure == undef)
  1084. {
  1085. _iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem);
  1086. }
  1087. return _iMeasure;
  1088. }
  1089. }
  1090. /// <summary>
  1091. /// (user can override) list Separator
  1092. /// </summary>
  1093. internal string ListSeparator => _sListSeparator ??= GetLocaleInfo(LocaleStringData.ListSeparator);
  1094. /// <summary>
  1095. /// (user can override) AM designator
  1096. /// </summary>
  1097. internal string AMDesignator => _sAM1159 ??= GetLocaleInfo(LocaleStringData.AMDesignator);
  1098. /// <summary>
  1099. /// (user can override) PM designator
  1100. /// </summary>
  1101. internal string PMDesignator => _sPM2359 ??= GetLocaleInfo(LocaleStringData.PMDesignator);
  1102. /// <summary>
  1103. /// (user can override) time format
  1104. /// </summary>
  1105. internal string[] LongTimes
  1106. {
  1107. get
  1108. {
  1109. if (_saLongTimes == null)
  1110. {
  1111. Debug.Assert(!GlobalizationMode.Invariant);
  1112. string[]? longTimes = GetTimeFormats();
  1113. if (longTimes == null || longTimes.Length == 0)
  1114. {
  1115. _saLongTimes = Invariant._saLongTimes!;
  1116. }
  1117. else
  1118. {
  1119. _saLongTimes = longTimes;
  1120. }
  1121. }
  1122. return _saLongTimes;
  1123. }
  1124. }
  1125. /// <summary>
  1126. /// short time format
  1127. /// Short times (derived from long times format)
  1128. /// </summary>
  1129. internal string[] ShortTimes
  1130. {
  1131. get
  1132. {
  1133. if (_saShortTimes == null)
  1134. {
  1135. Debug.Assert(!GlobalizationMode.Invariant);
  1136. // Try to get the short times from the OS/culture.dll
  1137. string[]? shortTimes = GetShortTimeFormats();
  1138. if (shortTimes == null || shortTimes.Length == 0)
  1139. {
  1140. //
  1141. // If we couldn't find short times, then compute them from long times
  1142. // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
  1143. //
  1144. shortTimes = DeriveShortTimesFromLong();
  1145. }
  1146. // Found short times, use them
  1147. _saShortTimes = shortTimes;
  1148. }
  1149. return _saShortTimes;
  1150. }
  1151. }
  1152. private string[] DeriveShortTimesFromLong()
  1153. {
  1154. // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string
  1155. // between it and the previous marker, if any. If its a short, unescaped separator,
  1156. // then we don't retain that part.
  1157. // We then check after the ss and remove anything before the next h,H,m,t...
  1158. string[] longTimes = LongTimes;
  1159. string[] shortTimes = new string[longTimes.Length];
  1160. for (int i = 0; i < longTimes.Length; i++)
  1161. {
  1162. shortTimes[i] = StripSecondsFromPattern(longTimes[i]);
  1163. }
  1164. return shortTimes;
  1165. }
  1166. private static string StripSecondsFromPattern(string time)
  1167. {
  1168. bool bEscape = false;
  1169. int iLastToken = -1;
  1170. // Find the seconds
  1171. for (int j = 0; j < time.Length; j++)
  1172. {
  1173. // Change escape mode?
  1174. if (time[j] == '\'')
  1175. {
  1176. // Continue
  1177. bEscape = !bEscape;
  1178. continue;
  1179. }
  1180. // See if there was a single \
  1181. if (time[j] == '\\')
  1182. {
  1183. // Skip next char
  1184. j++;
  1185. continue;
  1186. }
  1187. if (bEscape)
  1188. {
  1189. continue;
  1190. }
  1191. switch (time[j])
  1192. {
  1193. // Check for seconds
  1194. case 's':
  1195. // Found seconds, see if there was something unescaped and short between
  1196. // the last marker and the seconds. Windows says separator can be a
  1197. // maximum of three characters (without null)
  1198. // If 1st or last characters were ', then ignore it
  1199. if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
  1200. (time[iLastToken + 1] != '\'') &&
  1201. (time[j - 1] != '\''))
  1202. {
  1203. // There was something there we want to remember
  1204. if (iLastToken >= 0)
  1205. {
  1206. j = iLastToken + 1;
  1207. }
  1208. }
  1209. bool containsSpace;
  1210. int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
  1211. string sep;
  1212. if (containsSpace)
  1213. {
  1214. sep = " ";
  1215. }
  1216. else
  1217. {
  1218. sep = "";
  1219. }
  1220. time = string.Concat(time.AsSpan(0, j), sep, time.AsSpan(endIndex));
  1221. break;
  1222. case 'm':
  1223. case 'H':
  1224. case 'h':
  1225. iLastToken = j;
  1226. break;
  1227. }
  1228. }
  1229. return time;
  1230. }
  1231. private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
  1232. {
  1233. bool shouldEscape = false;
  1234. containsSpace = false;
  1235. for (; index < time.Length; index++)
  1236. {
  1237. switch (time[index])
  1238. {
  1239. case '\'':
  1240. shouldEscape = !shouldEscape;
  1241. continue;
  1242. case '\\':
  1243. index++;
  1244. if (time[index] == ' ')
  1245. {
  1246. containsSpace = true;
  1247. }
  1248. continue;
  1249. case ' ':
  1250. containsSpace = true;
  1251. break;
  1252. case 't':
  1253. case 'm':
  1254. case 'H':
  1255. case 'h':
  1256. if (shouldEscape)
  1257. {
  1258. continue;
  1259. }
  1260. return index;
  1261. }
  1262. }
  1263. containsSpace = false;
  1264. return index;
  1265. }
  1266. // (user can override) first day of week
  1267. internal int FirstDayOfWeek
  1268. {
  1269. get
  1270. {
  1271. if (_iFirstDayOfWeek == undef)
  1272. {
  1273. _iFirstDayOfWeek = GetFirstDayOfWeek();
  1274. }
  1275. return _iFirstDayOfWeek;
  1276. }
  1277. }
  1278. // (user can override) first week of year
  1279. internal int CalendarWeekRule
  1280. {
  1281. get
  1282. {
  1283. if (_iFirstWeekOfYear == undef)
  1284. {
  1285. _iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear);
  1286. }
  1287. return _iFirstWeekOfYear;
  1288. }
  1289. }
  1290. /// <summary>
  1291. /// (user can override default only) short date format
  1292. /// </summary>
  1293. internal string[] ShortDates(CalendarId calendarId)
  1294. {
  1295. return GetCalendar(calendarId).saShortDates;
  1296. }
  1297. /// <summary>
  1298. /// (user can override default only) long date format
  1299. /// </summary>
  1300. internal string[] LongDates(CalendarId calendarId)
  1301. {
  1302. return GetCalendar(calendarId).saLongDates;
  1303. }
  1304. /// <summary>
  1305. /// (user can override) date year/month format.
  1306. /// </summary>
  1307. internal string[] YearMonths(CalendarId calendarId)
  1308. {
  1309. return GetCalendar(calendarId).saYearMonths;
  1310. }
  1311. internal string[] DayNames(CalendarId calendarId)
  1312. {
  1313. return GetCalendar(calendarId).saDayNames;
  1314. }
  1315. internal string[] AbbreviatedDayNames(CalendarId calendarId)
  1316. {
  1317. return GetCalendar(calendarId).saAbbrevDayNames;
  1318. }
  1319. internal string[] SuperShortDayNames(CalendarId calendarId)
  1320. {
  1321. return GetCalendar(calendarId).saSuperShortDayNames;
  1322. }
  1323. internal string[] MonthNames(CalendarId calendarId)
  1324. {
  1325. return GetCalendar(calendarId).saMonthNames;
  1326. }
  1327. internal string[] GenitiveMonthNames(CalendarId calendarId)
  1328. {
  1329. return GetCalendar(calendarId).saMonthGenitiveNames;
  1330. }
  1331. internal string[] AbbreviatedMonthNames(CalendarId calendarId)
  1332. {
  1333. return GetCalendar(calendarId).saAbbrevMonthNames;
  1334. }
  1335. internal string[] AbbreviatedGenitiveMonthNames(CalendarId calendarId)
  1336. {
  1337. return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
  1338. }
  1339. /// <remarks>>
  1340. /// Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
  1341. /// the non-leap names skip the 7th name in the normal month name array
  1342. /// </remarks>
  1343. internal string[] LeapYearMonthNames(CalendarId calendarId)
  1344. {
  1345. return GetCalendar(calendarId).saLeapYearMonthNames;
  1346. }
  1347. internal string MonthDay(CalendarId calendarId)
  1348. {
  1349. return GetCalendar(calendarId).sMonthDay;
  1350. }
  1351. /// <summary>
  1352. /// All available calendar type(s). The first one is the default calendar.
  1353. /// </summary>
  1354. internal CalendarId[] CalendarIds
  1355. {
  1356. get
  1357. {
  1358. if (_waCalendars == null)
  1359. {
  1360. // We pass in an array of ints, and native side fills it up with count calendars.
  1361. // We then have to copy that list to a new array of the right size.
  1362. // Default calendar should be first
  1363. CalendarId[] calendars = new CalendarId[23];
  1364. Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be populated by already");
  1365. int count = CalendarData.GetCalendars(_sWindowsName, _bUseOverrides, calendars);
  1366. // See if we had a calendar to add.
  1367. if (count == 0)
  1368. {
  1369. // Failed for some reason, just grab Gregorian from Invariant
  1370. _waCalendars = Invariant._waCalendars!;
  1371. }
  1372. else
  1373. {
  1374. // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
  1375. // TODO: Is this hack necessary long-term?
  1376. if (_sWindowsName == "zh-TW")
  1377. {
  1378. bool found = false;
  1379. // Do we need to insert calendar 4?
  1380. for (int i = 0; i < count; i++)
  1381. {
  1382. // Stop if we found calendar four
  1383. if (calendars[i] == CalendarId.TAIWAN)
  1384. {
  1385. found = true;
  1386. break;
  1387. }
  1388. }
  1389. // If not found then insert it
  1390. if (!found)
  1391. {
  1392. // Insert it as the 2nd calendar
  1393. count++;
  1394. // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
  1395. Array.Copy(calendars, 1, calendars, 2, 23 - 1 - 1);
  1396. calendars[1] = CalendarId.TAIWAN;
  1397. }
  1398. }
  1399. // It worked, remember the list
  1400. CalendarId[] temp = new CalendarId[count];
  1401. Array.Copy(calendars, temp, count);
  1402. // Want 1st calendar to be default
  1403. // Prior to Vista the enumeration didn't have default calendar first
  1404. if (temp.Length > 1)
  1405. {
  1406. CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
  1407. if (temp[1] == i)
  1408. {
  1409. temp[1] = temp[0];
  1410. temp[0] = i;
  1411. }
  1412. }
  1413. _waCalendars = temp;
  1414. }
  1415. }
  1416. return _waCalendars;
  1417. }
  1418. }
  1419. /// <summary>
  1420. /// Native calendar names. Index of optional calendar - 1, empty if
  1421. /// no optional calendar at that number
  1422. /// </summary>
  1423. internal string CalendarName(CalendarId calendarId)
  1424. {
  1425. return GetCalendar(calendarId).sNativeName;
  1426. }
  1427. internal CalendarData GetCalendar(CalendarId calendarId)
  1428. {
  1429. Debug.Assert(calendarId > 0 && calendarId <= CalendarId.LAST_CALENDAR,
  1430. "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
  1431. // arrays are 0 based, calendarIds are 1 based
  1432. int calendarIndex = (int)calendarId - 1;
  1433. // Have to have calendars
  1434. _calendars ??= new CalendarData[CalendarData.MAX_CALENDARS];
  1435. // we need the following local variable to avoid returning null
  1436. // when another thread creates a new array of CalendarData (above)
  1437. // right after we insert the newly created CalendarData (below)
  1438. CalendarData? calendarData = _calendars[calendarIndex];
  1439. // Make sure that calendar has data
  1440. if (calendarData == null)
  1441. {
  1442. Debug.Assert(_sWindowsName != null, "[CultureData.GetCalendar] Expected _sWindowsName to be populated by already");
  1443. calendarData = new CalendarData(_sWindowsName, calendarId, UseUserOverride);
  1444. _calendars[calendarIndex] = calendarData;
  1445. }
  1446. return calendarData;
  1447. }
  1448. internal bool IsRightToLeft =>
  1449. // Returns one of the following 4 reading layout values:
  1450. // 0 - Left to right (eg en-US)
  1451. // 1 - Right to left (eg arabic locales)
  1452. // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
  1453. // 3 - Vertical top to bottom with columns proceeding to the right
  1454. ReadingLayout == 1;
  1455. /// <summary>
  1456. /// Returns one of the following 4 reading layout values:
  1457. /// 0 - Left to right (eg en-US)
  1458. /// 1 - Right to left (eg arabic locales)
  1459. /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
  1460. /// 3 - Vertical top to bottom with columns proceeding to the right
  1461. /// </summary>
  1462. private int ReadingLayout
  1463. {
  1464. get
  1465. {
  1466. if (_iReadingLayout == undef)
  1467. {
  1468. Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by already");
  1469. _iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout);
  1470. }
  1471. return _iReadingLayout;
  1472. }
  1473. }
  1474. /// <summary>
  1475. /// // Text info name to use for text information
  1476. /// The TextInfo name never includes that alternate sort and is always specific
  1477. /// For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
  1478. /// en -> en-US
  1479. /// en-US -> en-US
  1480. /// fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
  1481. /// fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
  1482. /// es-ES_tradnl -> es-ES
  1483. /// </summary>
  1484. internal string TextInfoName
  1485. {
  1486. get
  1487. {
  1488. // Note: Custom cultures might point at another culture's textinfo, however windows knows how
  1489. // to redirect it to the desired textinfo culture, so this is OK.
  1490. Debug.Assert(_sRealName != null, "[CultureData.TextInfoName] Expected _sRealName to be populated by already");
  1491. return _sRealName;
  1492. }
  1493. }
  1494. /// <summary>
  1495. /// Compare info name (including sorting key) to use if custom
  1496. /// </summary>
  1497. internal string SortName
  1498. {
  1499. get
  1500. {
  1501. Debug.Assert(_sRealName != null, "[CultureData.SortName] Expected _sRealName to be populated by already");
  1502. return _sRealName;
  1503. }
  1504. }
  1505. internal bool IsSupplementalCustomCulture => IsCustomCultureId(LCID);
  1506. /// <summary>
  1507. /// Default ansi code page ID (ACP)
  1508. /// </summary>
  1509. internal int ANSICodePage
  1510. {
  1511. get
  1512. {
  1513. if (_iDefaultAnsiCodePage == undef)
  1514. {
  1515. _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName!);
  1516. }
  1517. return _iDefaultAnsiCodePage;
  1518. }
  1519. }
  1520. /// <summary>
  1521. /// Default oem code page ID (OCP or OEM).
  1522. /// </summary>
  1523. internal int OEMCodePage
  1524. {
  1525. get
  1526. {
  1527. if (_iDefaultOemCodePage == undef)
  1528. {
  1529. _iDefaultOemCodePage = GetOemCodePage(_sRealName!);
  1530. }
  1531. return _iDefaultOemCodePage;
  1532. }
  1533. }
  1534. /// <summary>
  1535. /// Default macintosh code page.
  1536. /// </summary>
  1537. internal int MacCodePage
  1538. {
  1539. get
  1540. {
  1541. if (_iDefaultMacCodePage == undef)
  1542. {
  1543. _iDefaultMacCodePage = GetMacCodePage(_sRealName!);
  1544. }
  1545. return _iDefaultMacCodePage;
  1546. }
  1547. }
  1548. /// <summary>
  1549. /// Default EBCDIC code page.
  1550. /// </summary>
  1551. internal int EBCDICCodePage
  1552. {
  1553. get
  1554. {
  1555. if (_iDefaultEbcdicCodePage == undef)
  1556. {
  1557. _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName!);
  1558. }
  1559. return _iDefaultEbcdicCodePage;
  1560. }
  1561. }
  1562. internal int LCID
  1563. {
  1564. get
  1565. {
  1566. if (_iLanguage == 0)
  1567. {
  1568. Debug.Assert(_sRealName != null, "[CultureData.LCID] Expected this.sRealName to be populated already");
  1569. _iLanguage = LocaleNameToLCID(_sRealName);
  1570. }
  1571. return _iLanguage;
  1572. }
  1573. }
  1574. internal bool IsNeutralCulture =>
  1575. // InitCultureData told us if we're neutral or not
  1576. _bNeutral;
  1577. internal bool IsInvariantCulture => string.IsNullOrEmpty(Name);
  1578. /// <summary>
  1579. /// Get an instance of our default calendar
  1580. /// </summary>
  1581. internal Calendar DefaultCalendar
  1582. {
  1583. get
  1584. {
  1585. if (GlobalizationMode.Invariant)
  1586. {
  1587. return CultureInfo.GetCalendarInstance(CalendarIds[0]);
  1588. }
  1589. CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
  1590. if (defaultCalId == 0)
  1591. {
  1592. defaultCalId = CalendarIds[0];
  1593. }
  1594. return CultureInfo.GetCalendarInstance(defaultCalId);
  1595. }
  1596. }
  1597. /// <summary>
  1598. /// All of our era names
  1599. /// </summary>
  1600. internal string[] EraNames(CalendarId calendarId)
  1601. {
  1602. Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
  1603. return GetCalendar(calendarId).saEraNames;
  1604. }
  1605. internal string[] AbbrevEraNames(CalendarId calendarId)
  1606. {
  1607. Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
  1608. return GetCalendar(calendarId).saAbbrevEraNames;
  1609. }
  1610. internal string[] AbbreviatedEnglishEraNames(CalendarId calendarId)
  1611. {
  1612. Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
  1613. return GetCalendar(calendarId).saAbbrevEnglishEraNames;
  1614. }
  1615. /// <summary>
  1616. /// Time separator (derived from time format)
  1617. /// </summary>
  1618. internal string TimeSeparator
  1619. {
  1620. get
  1621. {
  1622. if (_sTimeSeparator == null)
  1623. {
  1624. string? longTimeFormat = GetTimeFormatString();
  1625. if (string.IsNullOrEmpty(longTimeFormat))
  1626. {
  1627. longTimeFormat = LongTimes[0];
  1628. }
  1629. // Compute STIME from time format
  1630. _sTimeSeparator = GetTimeSeparator(longTimeFormat);
  1631. }
  1632. return _sTimeSeparator;
  1633. }
  1634. }
  1635. /// <summary>
  1636. /// Date separator (derived from short date format)
  1637. /// </summary>
  1638. internal string DateSeparator(CalendarId calendarId)
  1639. {
  1640. if (calendarId == CalendarId.JAPAN && !LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing)
  1641. {
  1642. // The date separator is derived from the default short date pattern. So far this pattern is using
  1643. // '/' as date separator when using the Japanese calendar which make the formatting and parsing work fine.
  1644. // changing the default pattern is likely will happen in the near future which can easily break formatting
  1645. // and parsing.
  1646. // We are forcing here the date separator to '/' to ensure the parsing is not going to break when changing
  1647. // the default short date pattern. The application still can override this in the code by DateTimeFormatInfo.DateSeparartor.
  1648. return "/";
  1649. }
  1650. return GetDateSeparator(ShortDates(calendarId)[0]);
  1651. }
  1652. /// <summary>
  1653. /// Unescape a NLS style quote string
  1654. ///
  1655. /// This removes single quotes:
  1656. /// 'fred' -> fred
  1657. /// 'fred -> fred
  1658. /// fred' -> fred
  1659. /// fred's -> freds
  1660. ///
  1661. /// This removes the first \ of escaped characters:
  1662. /// fred\'s -> fred's
  1663. /// a\\b -> a\b
  1664. /// a\b -> ab
  1665. ///
  1666. /// We don't build the stringbuilder unless we find a ' or a \. If we find a ' or a \, we
  1667. /// always build a stringbuilder because we need to remove the ' or \.
  1668. /// </summary>
  1669. private static string UnescapeNlsString(string str, int start, int end)
  1670. {
  1671. Debug.Assert(str != null);
  1672. Debug.Assert(start >= 0);
  1673. Debug.Assert(end >= 0);
  1674. StringBuilder? result = null;
  1675. for (int i = start; i < str.Length && i <= end; i++)
  1676. {
  1677. switch (str[i])
  1678. {
  1679. case '\'':
  1680. result ??= new StringBuilder(str, start, i - start, str.Length);
  1681. break;
  1682. case '\\':
  1683. result ??= new StringBuilder(str, start, i - start, str.Length);
  1684. ++i;
  1685. if (i < str.Length)
  1686. {
  1687. result.Append(str[i]);
  1688. }
  1689. break;
  1690. default:
  1691. result?.Append(str[i]);
  1692. break;
  1693. }
  1694. }
  1695. if (result == null)
  1696. {
  1697. return str.Substring(start, end - start + 1);
  1698. }
  1699. return result.ToString();
  1700. }
  1701. /// <summary>
  1702. /// Time format separator (ie: : in 12:39:00)
  1703. /// We calculate this from the provided time format
  1704. /// </summary>
  1705. private static string GetTimeSeparator(string format)
  1706. {
  1707. // Find the time separator so that we can pretend we know TimeSeparator.
  1708. return GetSeparator(format, "Hhms");
  1709. }
  1710. /// <summary>
  1711. /// Date format separator (ie: / in 9/1/03)
  1712. /// We calculate this from the provided short date
  1713. /// </summary>
  1714. private static string GetDateSeparator(string format)
  1715. {
  1716. // Find the date separator so that we can pretend we know DateSeparator.
  1717. return GetSeparator(format, "dyM");
  1718. }
  1719. private static string GetSeparator(string format, string timeParts)
  1720. {
  1721. int index = IndexOfTimePart(format, 0, timeParts);
  1722. if (index != -1)
  1723. {
  1724. // Found a time part, find out when it changes
  1725. char cTimePart = format[index];
  1726. do
  1727. {
  1728. index++;
  1729. } while (index < format.Length && format[index] == cTimePart);
  1730. int separatorStart = index;
  1731. // Now we need to find the end of the separator
  1732. if (separatorStart < format.Length)
  1733. {
  1734. int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
  1735. if (separatorEnd != -1)
  1736. {
  1737. // From [separatorStart, count) is our string, except we need to unescape
  1738. return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
  1739. }
  1740. }
  1741. }
  1742. return string.Empty;
  1743. }
  1744. private static int IndexOfTimePart(string format, int startIndex, string timeParts)
  1745. {
  1746. Debug.Assert(startIndex >= 0, "startIndex cannot be negative");
  1747. Debug.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
  1748. bool inQuote = false;
  1749. for (int i = startIndex; i < format.Length; ++i)
  1750. {
  1751. // See if we have a time Part
  1752. if (!inQuote && timeParts.Contains(format[i]))
  1753. {
  1754. return i;
  1755. }
  1756. switch (format[i])
  1757. {
  1758. case '\\':
  1759. if (i + 1 < format.Length)
  1760. {
  1761. ++i;
  1762. switch (format[i])
  1763. {
  1764. case '\'':
  1765. case '\\':
  1766. break;
  1767. default:
  1768. --i; // backup since we will move over this next
  1769. break;
  1770. }
  1771. }
  1772. break;
  1773. case '\'':
  1774. inQuote = !inQuote;
  1775. break;
  1776. }
  1777. }
  1778. return -1;
  1779. }
  1780. internal static bool IsCustomCultureId(int cultureId)
  1781. {
  1782. return cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
  1783. }
  1784. internal void GetNFIValues(NumberFormatInfo nfi)
  1785. {
  1786. if (GlobalizationMode.Invariant || IsInvariantCulture)
  1787. {
  1788. nfi._positiveSign = _sPositiveSign!;
  1789. nfi._negativeSign = _sNegativeSign!;
  1790. nfi._numberGroupSeparator = _sThousandSeparator!;
  1791. nfi._numberDecimalSeparator = _sDecimalSeparator!;
  1792. nfi._numberDecimalDigits = _iDigits;
  1793. nfi._numberNegativePattern = _iNegativeNumber;
  1794. nfi._currencySymbol = _sCurrency!;
  1795. nfi._currencyGroupSeparator = _sMonetaryThousand!;
  1796. nfi._currencyDecimalSeparator = _sMonetaryDecimal!;
  1797. nfi._currencyDecimalDigits = _iCurrencyDigits;
  1798. nfi._currencyNegativePattern = _iNegativeCurrency;
  1799. nfi._currencyPositivePattern = _iCurrency;
  1800. }
  1801. else
  1802. {
  1803. Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated by already");
  1804. // String values
  1805. nfi._positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign);
  1806. nfi._negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign);
  1807. nfi._numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator);
  1808. nfi._numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator);
  1809. nfi._currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator);
  1810. nfi._currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator);
  1811. nfi._currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol);
  1812. // Numeric values
  1813. nfi._numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount);
  1814. nfi._currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount);
  1815. nfi._currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat);
  1816. nfi._currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat);
  1817. nfi._numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat);
  1818. // LOCALE_SNATIVEDIGITS (array of 10 single character strings).
  1819. string digits = GetLocaleInfo(LocaleStringData.Digits);
  1820. nfi._nativeDigits = new string[10];
  1821. for (int i = 0; i < nfi._nativeDigits.Length; i++)
  1822. {
  1823. nfi._nativeDigits[i] = char.ToString(digits[i]);
  1824. }
  1825. Debug.Assert(_sRealName != null);
  1826. nfi._digitSubstitution = GetDigitSubstitution(_sRealName);
  1827. }
  1828. // Gather additional data
  1829. nfi._numberGroupSizes = NumberGroupSizes;
  1830. nfi._currencyGroupSizes = CurrencyGroupSizes;
  1831. // prefer the cached value since these do not have user overrides
  1832. nfi._percentNegativePattern = PercentNegativePattern;
  1833. nfi._percentPositivePattern = PercentPositivePattern;
  1834. nfi._percentSymbol = PercentSymbol;
  1835. nfi._perMilleSymbol = PerMilleSymbol;
  1836. nfi._negativeInfinitySymbol = NegativeInfinitySymbol;
  1837. nfi._positiveInfinitySymbol = PositiveInfinitySymbol;
  1838. nfi._nanSymbol = NaNSymbol;
  1839. // We don't have percent values, so use the number values
  1840. nfi._percentDecimalDigits = nfi._numberDecimalDigits;
  1841. nfi._percentDecimalSeparator = nfi._numberDecimalSeparator;
  1842. nfi._percentGroupSizes = nfi._numberGroupSizes;
  1843. nfi._percentGroupSeparator = nfi._numberGroupSeparator;
  1844. // Clean up a few odd values
  1845. // Windows usually returns an empty positive sign, but we like it to be "+"
  1846. if (string.IsNullOrEmpty(nfi._positiveSign))
  1847. {
  1848. nfi._positiveSign = "+";
  1849. }
  1850. // Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user
  1851. // specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
  1852. // decimal point doesn't show up. We'll just hack this here because our default currency format will never use nfi.
  1853. if (string.IsNullOrEmpty(nfi._currencyDecimalSeparator))
  1854. {
  1855. nfi._currencyDecimalSeparator = nfi._numberDecimalSeparator;
  1856. }
  1857. }
  1858. /// <remarks>
  1859. /// This is ONLY used for caching names and shouldn't be used for anything else
  1860. /// </remarks>
  1861. internal static string AnsiToLower(string testString) => TextInfo.ToLowerAsciiInvariant(testString);
  1862. /// <remarks>
  1863. /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
  1864. /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
  1865. /// </remarks>
  1866. private enum LocaleStringData : uint
  1867. {
  1868. /// <summary>localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
  1869. LocalizedDisplayName = 0x00000002,
  1870. /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
  1871. EnglishDisplayName = 0x00000072,
  1872. /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
  1873. NativeDisplayName = 0x00000073,
  1874. /// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
  1875. LocalizedLanguageName = 0x0000006f,
  1876. /// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
  1877. EnglishLanguageName = 0x00001001,
  1878. /// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
  1879. NativeLanguageName = 0x00000004,
  1880. /// <summary>localized name of country, eg "Germany" in UI language (corresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
  1881. LocalizedCountryName = 0x00000006,
  1882. /// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
  1883. EnglishCountryName = 0x00001002,
  1884. /// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
  1885. NativeCountryName = 0x00000008,
  1886. /// <summary>abbreviated language name (corresponds to LOCALE_SABBREVLANGNAME)</summary>
  1887. AbbreviatedWindowsLanguageName = 0x00000003,
  1888. /// <summary>list item separator (corresponds to LOCALE_SLIST)</summary>
  1889. ListSeparator = 0x0000000C,
  1890. /// <summary>decimal separator (corresponds to LOCALE_SDECIMAL)</summary>
  1891. DecimalSeparator = 0x0000000E,
  1892. /// <summary>thousand separator (corresponds to LOCALE_STHOUSAND)</summary>
  1893. ThousandSeparator = 0x0000000F,
  1894. /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
  1895. Digits = 0x00000013,
  1896. /// <summary>local monetary symbol (corresponds to LOCALE_SCURRENCY)</summary>
  1897. MonetarySymbol = 0x00000014,
  1898. /// <summary>English currency name (corresponds to LOCALE_SENGCURRNAME)</summary>
  1899. CurrencyEnglishName = 0x00001007,
  1900. /// <summary>Native currency name (corresponds to LOCALE_SNATIVECURRNAME)</summary>
  1901. CurrencyNativeName = 0x00001008,
  1902. /// <summary>uintl monetary symbol (corresponds to LOCALE_SINTLSYMBOL)</summary>
  1903. Iso4217MonetarySymbol = 0x00000015,
  1904. /// <summary>monetary decimal separator (corresponds to LOCALE_SMONDECIMALSEP)</summary>
  1905. MonetaryDecimalSeparator = 0x00000016,
  1906. /// <summary>monetary thousand separator (corresponds to LOCALE_SMONTHOUSANDSEP)</summary>
  1907. MonetaryThousandSeparator = 0x00000017,
  1908. /// <summary>AM designator (corresponds to LOCALE_S1159)</summary>
  1909. AMDesignator = 0x00000028,
  1910. /// <summary>PM designator (corresponds to LOCALE_S2359)</summary>
  1911. PMDesignator = 0x00000029,
  1912. /// <summary>positive sign (corresponds to LOCALE_SPOSITIVESIGN)</summary>
  1913. PositiveSign = 0x00000050,
  1914. /// <summary>negative sign (corresponds to LOCALE_SNEGATIVESIGN)</summary>
  1915. NegativeSign = 0x00000051,
  1916. /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
  1917. Iso639LanguageTwoLetterName = 0x00000059,
  1918. /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO639LANGNAME2)</summary>
  1919. Iso639LanguageThreeLetterName = 0x00000067,
  1920. /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
  1921. Iso639LanguageName = 0x00000059,
  1922. /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO3166CTRYNAME)</summary>
  1923. Iso3166CountryName = 0x0000005A,
  1924. /// <summary>3 letter ISO country code (corresponds to LOCALE_SISO3166CTRYNAME2)</summary>
  1925. Iso3166CountryName2 = 0x00000068, // 3 character ISO country name
  1926. /// <summary>Not a Number (corresponds to LOCALE_SNAN)</summary>
  1927. NaNSymbol = 0x00000069,
  1928. /// <summary>+ Infinity (corresponds to LOCALE_SPOSINFINITY)</summary>
  1929. PositiveInfinitySymbol = 0x0000006a,
  1930. /// <summary>- Infinity (corresponds to LOCALE_SNEGINFINITY)</summary>
  1931. NegativeInfinitySymbol = 0x0000006b,
  1932. /// <summary>Fallback name for resources (corresponds to LOCALE_SPARENT)</summary>
  1933. ParentName = 0x0000006d,
  1934. /// <summary>Fallback name for within the console (corresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
  1935. ConsoleFallbackName = 0x0000006e,
  1936. /// <summary>Returns the percent symbol (corresponds to LOCALE_SPERCENT)</summary>
  1937. PercentSymbol = 0x00000076,
  1938. /// <summary>Returns the permille (U+2030) symbol (corresponds to LOCALE_SPERMILLE)</summary>
  1939. PerMilleSymbol = 0x00000077
  1940. }
  1941. /// <remarks>
  1942. /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
  1943. /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
  1944. /// </remarks>
  1945. private enum LocaleGroupingData : uint
  1946. {
  1947. /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
  1948. Digit = 0x00000010,
  1949. /// <summary>monetary grouping (corresponds to LOCALE_SMONGROUPING)</summary>
  1950. Monetary = 0x00000018,
  1951. }
  1952. /// <remarks>
  1953. /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation
  1954. /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
  1955. /// </remarks>
  1956. private enum LocaleNumberData : uint
  1957. {
  1958. /// <summary>language id (corresponds to LOCALE_ILANGUAGE)</summary>
  1959. LanguageId = 0x00000001,
  1960. /// <summary>geographical location id, (corresponds to LOCALE_IGEOID)</summary>
  1961. GeoId = 0x0000005B,
  1962. /// <summary>0 = context, 1 = none, 2 = national (corresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
  1963. DigitSubstitution = 0x00001014,
  1964. /// <summary>0 = metric, 1 = US (corresponds to LOCALE_IMEASURE)</summary>
  1965. MeasurementSystem = 0x0000000D,
  1966. /// <summary>number of fractional digits (corresponds to LOCALE_IDIGITS)</summary>
  1967. FractionalDigitsCount = 0x00000011,
  1968. /// <summary>negative number mode (corresponds to LOCALE_INEGNUMBER)</summary>
  1969. NegativeNumberFormat = 0x00001010,
  1970. /// <summary># local monetary digits (corresponds to LOCALE_ICURRDIGITS)</summary>
  1971. MonetaryFractionalDigitsCount = 0x00000019,
  1972. /// <summary>positive currency mode (corresponds to LOCALE_ICURRENCY)</summary>
  1973. PositiveMonetaryNumberFormat = 0x0000001B,
  1974. /// <summary>negative currency mode (corresponds to LOCALE_INEGCURR)</summary>
  1975. NegativeMonetaryNumberFormat = 0x0000001C,
  1976. /// <summary>type of calendar specifier (corresponds to LOCALE_ICALENDARTYPE)</summary>
  1977. CalendarType = 0x00001009,
  1978. /// <summary>first day of week specifier (corresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
  1979. FirstDayOfWeek = 0x0000100C,
  1980. /// <summary>first week of year specifier (corresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
  1981. FirstWeekOfYear = 0x0000100D,
  1982. /// <summary>
  1983. /// Returns one of the following 4 reading layout values:
  1984. /// 0 - Left to right (eg en-US)
  1985. /// 1 - Right to left (eg arabic locales)
  1986. /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
  1987. /// 3 - Vertical top to bottom with columns proceeding to the right
  1988. /// (corresponds to LOCALE_IREADINGLAYOUT)
  1989. /// </summary>
  1990. ReadingLayout = 0x00000070,
  1991. /// <summary>Returns 0-11 for the negative percent format (corresponds to LOCALE_INEGATIVEPERCENT)</summary>
  1992. NegativePercentFormat = 0x00000074,
  1993. /// <summary>Returns 0-3 for the positive percent format (corresponds to LOCALE_IPOSITIVEPERCENT)</summary>
  1994. PositivePercentFormat = 0x00000075,
  1995. /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
  1996. OemCodePage = 0x0000000B,
  1997. /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
  1998. AnsiCodePage = 0x00001004,
  1999. /// <summary>default mac code page (corresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
  2000. MacCodePage = 0x00001011,
  2001. /// <summary>default ebcdic code page (corresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
  2002. EbcdicCodePage = 0x00001012,
  2003. }
  2004. }
  2005. }