GenerateNumberFormatData.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. /*
  2. * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "../LibUnicode/GeneratorUtil.h" // FIXME: Move this somewhere common.
  7. #include <AK/AllOf.h>
  8. #include <AK/Array.h>
  9. #include <AK/CharacterTypes.h>
  10. #include <AK/DeprecatedString.h>
  11. #include <AK/Find.h>
  12. #include <AK/Format.h>
  13. #include <AK/HashFunctions.h>
  14. #include <AK/HashMap.h>
  15. #include <AK/JsonObject.h>
  16. #include <AK/JsonParser.h>
  17. #include <AK/JsonValue.h>
  18. #include <AK/LexicalPath.h>
  19. #include <AK/QuickSort.h>
  20. #include <AK/SourceGenerator.h>
  21. #include <AK/StringBuilder.h>
  22. #include <AK/Traits.h>
  23. #include <AK/Utf8View.h>
  24. #include <LibCore/ArgsParser.h>
  25. #include <LibCore/Directory.h>
  26. #include <LibFileSystem/FileSystem.h>
  27. #include <LibJS/Runtime/Intl/SingleUnitIdentifiers.h>
  28. #include <LibLocale/Locale.h>
  29. #include <LibLocale/NumberFormat.h>
  30. #include <LibLocale/PluralRules.h>
  31. #include <math.h>
  32. enum class NumberFormatType {
  33. Standard,
  34. Compact,
  35. };
  36. struct NumberFormat : public Locale::NumberFormat {
  37. using Base = Locale::NumberFormat;
  38. unsigned hash() const
  39. {
  40. auto hash = pair_int_hash(magnitude, exponent);
  41. hash = pair_int_hash(hash, to_underlying(plurality));
  42. hash = pair_int_hash(hash, zero_format_index);
  43. hash = pair_int_hash(hash, positive_format_index);
  44. hash = pair_int_hash(hash, negative_format_index);
  45. for (auto index : identifier_indices)
  46. hash = pair_int_hash(hash, index);
  47. return hash;
  48. }
  49. bool operator==(NumberFormat const& other) const
  50. {
  51. return (magnitude == other.magnitude)
  52. && (exponent == other.exponent)
  53. && (plurality == other.plurality)
  54. && (zero_format_index == other.zero_format_index)
  55. && (positive_format_index == other.positive_format_index)
  56. && (negative_format_index == other.negative_format_index)
  57. && (identifier_indices == other.identifier_indices);
  58. }
  59. size_t zero_format_index { 0 };
  60. size_t positive_format_index { 0 };
  61. size_t negative_format_index { 0 };
  62. Vector<size_t> identifier_indices {};
  63. };
  64. template<>
  65. struct AK::Formatter<NumberFormat> : Formatter<FormatString> {
  66. ErrorOr<void> format(FormatBuilder& builder, NumberFormat const& format)
  67. {
  68. StringBuilder identifier_indices;
  69. identifier_indices.join(", "sv, format.identifier_indices);
  70. return Formatter<FormatString>::format(builder,
  71. "{{ {}, {}, {}, {}, {}, {}, {{ {} }} }}"sv,
  72. format.magnitude,
  73. format.exponent,
  74. to_underlying(format.plurality),
  75. format.zero_format_index,
  76. format.positive_format_index,
  77. format.negative_format_index,
  78. identifier_indices.to_deprecated_string());
  79. }
  80. };
  81. template<>
  82. struct AK::Traits<NumberFormat> : public GenericTraits<NumberFormat> {
  83. static unsigned hash(NumberFormat const& f) { return f.hash(); }
  84. };
  85. using NumberFormatList = Vector<size_t>;
  86. using NumericSymbolList = Vector<size_t>;
  87. struct NumberSystem {
  88. unsigned hash() const
  89. {
  90. auto hash = int_hash(symbols);
  91. hash = pair_int_hash(hash, primary_grouping_size);
  92. hash = pair_int_hash(hash, secondary_grouping_size);
  93. hash = pair_int_hash(hash, decimal_format);
  94. hash = pair_int_hash(hash, decimal_long_formats);
  95. hash = pair_int_hash(hash, decimal_short_formats);
  96. hash = pair_int_hash(hash, currency_format);
  97. hash = pair_int_hash(hash, accounting_format);
  98. hash = pair_int_hash(hash, currency_unit_formats);
  99. hash = pair_int_hash(hash, currency_short_formats);
  100. hash = pair_int_hash(hash, percent_format);
  101. hash = pair_int_hash(hash, scientific_format);
  102. return hash;
  103. }
  104. bool operator==(NumberSystem const& other) const
  105. {
  106. return (symbols == other.symbols)
  107. && (primary_grouping_size == other.primary_grouping_size)
  108. && (secondary_grouping_size == other.secondary_grouping_size)
  109. && (decimal_format == other.decimal_format)
  110. && (decimal_long_formats == other.decimal_long_formats)
  111. && (decimal_short_formats == other.decimal_short_formats)
  112. && (currency_format == other.currency_format)
  113. && (accounting_format == other.accounting_format)
  114. && (currency_unit_formats == other.currency_unit_formats)
  115. && (currency_short_formats == other.currency_short_formats)
  116. && (percent_format == other.percent_format)
  117. && (scientific_format == other.scientific_format);
  118. }
  119. size_t symbols { 0 };
  120. u8 primary_grouping_size { 0 };
  121. u8 secondary_grouping_size { 0 };
  122. size_t decimal_format { 0 };
  123. size_t decimal_long_formats { 0 };
  124. size_t decimal_short_formats { 0 };
  125. size_t currency_format { 0 };
  126. size_t accounting_format { 0 };
  127. size_t currency_unit_formats { 0 };
  128. size_t currency_short_formats { 0 };
  129. size_t percent_format { 0 };
  130. size_t scientific_format { 0 };
  131. };
  132. template<>
  133. struct AK::Formatter<NumberSystem> : Formatter<FormatString> {
  134. ErrorOr<void> format(FormatBuilder& builder, NumberSystem const& system)
  135. {
  136. return Formatter<FormatString>::format(builder,
  137. "{{ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }}"sv,
  138. system.symbols,
  139. system.primary_grouping_size,
  140. system.secondary_grouping_size,
  141. system.decimal_format,
  142. system.decimal_long_formats,
  143. system.decimal_short_formats,
  144. system.currency_format,
  145. system.accounting_format,
  146. system.currency_unit_formats,
  147. system.currency_short_formats,
  148. system.percent_format,
  149. system.scientific_format);
  150. }
  151. };
  152. template<>
  153. struct AK::Traits<NumberSystem> : public GenericTraits<NumberSystem> {
  154. static unsigned hash(NumberSystem const& s) { return s.hash(); }
  155. };
  156. struct Unit {
  157. unsigned hash() const
  158. {
  159. auto hash = int_hash(unit);
  160. hash = pair_int_hash(hash, long_formats);
  161. hash = pair_int_hash(hash, short_formats);
  162. hash = pair_int_hash(hash, narrow_formats);
  163. return hash;
  164. }
  165. bool operator==(Unit const& other) const
  166. {
  167. return (unit == other.unit)
  168. && (long_formats == other.long_formats)
  169. && (short_formats == other.short_formats)
  170. && (narrow_formats == other.narrow_formats);
  171. }
  172. size_t unit { 0 };
  173. size_t long_formats { 0 };
  174. size_t short_formats { 0 };
  175. size_t narrow_formats { 0 };
  176. };
  177. template<>
  178. struct AK::Formatter<Unit> : Formatter<FormatString> {
  179. ErrorOr<void> format(FormatBuilder& builder, Unit const& system)
  180. {
  181. return Formatter<FormatString>::format(builder,
  182. "{{ {}, {}, {}, {} }}"sv,
  183. system.unit,
  184. system.long_formats,
  185. system.short_formats,
  186. system.narrow_formats);
  187. }
  188. };
  189. template<>
  190. struct AK::Traits<Unit> : public GenericTraits<Unit> {
  191. static unsigned hash(Unit const& u) { return u.hash(); }
  192. };
  193. struct LocaleData {
  194. Vector<size_t> number_systems;
  195. HashMap<DeprecatedString, size_t> units {};
  196. u8 minimum_grouping_digits { 0 };
  197. };
  198. struct CLDR {
  199. UniqueStringStorage unique_strings;
  200. UniqueStorage<NumberFormat> unique_formats;
  201. UniqueStorage<NumberFormatList> unique_format_lists;
  202. UniqueStorage<NumericSymbolList> unique_symbols;
  203. UniqueStorage<NumberSystem> unique_systems;
  204. UniqueStorage<Unit> unique_units;
  205. HashMap<DeprecatedString, Array<u32, 10>> number_system_digits;
  206. Vector<DeprecatedString> number_systems;
  207. HashMap<DeprecatedString, LocaleData> locales;
  208. size_t max_identifier_count { 0 };
  209. };
  210. static ErrorOr<void> parse_number_system_digits(DeprecatedString core_supplemental_path, CLDR& cldr)
  211. {
  212. LexicalPath number_systems_path(move(core_supplemental_path));
  213. number_systems_path = number_systems_path.append("numberingSystems.json"sv);
  214. auto number_systems = TRY(read_json_file(number_systems_path.string()));
  215. auto const& supplemental_object = number_systems.as_object().get_object("supplemental"sv).value();
  216. auto const& number_systems_object = supplemental_object.get_object("numberingSystems"sv).value();
  217. number_systems_object.for_each_member([&](auto const& number_system, auto const& digits_object) {
  218. auto type = digits_object.as_object().get_deprecated_string("_type"sv).value();
  219. if (type != "numeric"sv)
  220. return;
  221. auto digits = digits_object.as_object().get_deprecated_string("_digits"sv).value();
  222. Utf8View utf8_digits { digits };
  223. VERIFY(utf8_digits.length() == 10);
  224. auto& number_system_digits = cldr.number_system_digits.ensure(number_system);
  225. size_t index = 0;
  226. for (u32 digit : utf8_digits)
  227. number_system_digits[index++] = digit;
  228. if (!cldr.number_systems.contains_slow(number_system))
  229. cldr.number_systems.append(number_system);
  230. });
  231. return {};
  232. }
  233. static DeprecatedString parse_identifiers(DeprecatedString pattern, StringView replacement, CLDR& cldr, NumberFormat& format)
  234. {
  235. static constexpr Utf8View whitespace { "\u0020\u00a0\u200f"sv };
  236. while (true) {
  237. Utf8View utf8_pattern { pattern };
  238. Optional<size_t> start_index;
  239. Optional<size_t> end_index;
  240. bool inside_replacement = false;
  241. for (auto it = utf8_pattern.begin(); it != utf8_pattern.end(); ++it) {
  242. if (*it == '{') {
  243. if (start_index.has_value()) {
  244. end_index = utf8_pattern.byte_offset_of(it);
  245. break;
  246. }
  247. inside_replacement = true;
  248. } else if (*it == '}') {
  249. inside_replacement = false;
  250. } else if (!inside_replacement && !start_index.has_value() && !whitespace.contains(*it)) {
  251. start_index = utf8_pattern.byte_offset_of(it);
  252. }
  253. }
  254. if (!start_index.has_value())
  255. return pattern;
  256. end_index = end_index.value_or(pattern.length());
  257. utf8_pattern = utf8_pattern.substring_view(*start_index, *end_index - *start_index);
  258. utf8_pattern = utf8_pattern.trim(whitespace);
  259. auto identifier = utf8_pattern.as_string().replace("'.'"sv, "."sv, ReplaceMode::FirstOnly);
  260. auto identifier_index = cldr.unique_strings.ensure(move(identifier));
  261. size_t replacement_index = 0;
  262. if (auto index = format.identifier_indices.find_first_index(identifier_index); index.has_value()) {
  263. replacement_index = *index;
  264. } else {
  265. replacement_index = format.identifier_indices.size();
  266. format.identifier_indices.append(identifier_index);
  267. cldr.max_identifier_count = max(cldr.max_identifier_count, format.identifier_indices.size());
  268. }
  269. pattern = DeprecatedString::formatted("{}{{{}:{}}}{}",
  270. *start_index > 0 ? pattern.substring_view(0, *start_index) : ""sv,
  271. replacement,
  272. replacement_index,
  273. pattern.substring_view(*start_index + utf8_pattern.byte_length()));
  274. }
  275. }
  276. static void parse_number_pattern(Vector<DeprecatedString> patterns, CLDR& cldr, NumberFormatType type, NumberFormat& format, NumberSystem* number_system_for_groupings = nullptr)
  277. {
  278. // https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns
  279. // https://cldr.unicode.org/translation/number-currency-formats/number-and-currency-patterns
  280. VERIFY((patterns.size() == 1) || (patterns.size() == 2));
  281. auto replace_patterns = [&](DeprecatedString pattern) {
  282. static HashMap<StringView, StringView> replacements = {
  283. { "{0}"sv, "{number}"sv },
  284. { "{1}"sv, "{currency}"sv },
  285. { "%"sv, "{percentSign}"sv },
  286. { "+"sv, "{plusSign}"sv },
  287. { "-"sv, "{minusSign}"sv },
  288. { "¤"sv, "{currency}"sv }, // U+00A4 Currency Sign
  289. { "E"sv, "{scientificSeparator}"sv },
  290. };
  291. for (auto const& replacement : replacements)
  292. pattern = pattern.replace(replacement.key, replacement.value, ReplaceMode::All);
  293. if (auto start_number_index = pattern.find_any_of("#0"sv, DeprecatedString::SearchDirection::Forward); start_number_index.has_value()) {
  294. auto end_number_index = *start_number_index + 1;
  295. for (; end_number_index < pattern.length(); ++end_number_index) {
  296. auto ch = pattern[end_number_index];
  297. if ((ch != '#') && (ch != '0') && (ch != ',') && (ch != '.'))
  298. break;
  299. }
  300. if (number_system_for_groupings) {
  301. auto number_pattern = pattern.substring_view(*start_number_index, end_number_index - *start_number_index);
  302. auto group_separators = number_pattern.find_all(","sv);
  303. VERIFY((group_separators.size() == 1) || (group_separators.size() == 2));
  304. auto decimal = number_pattern.find('.');
  305. VERIFY(decimal.has_value());
  306. if (group_separators.size() == 1) {
  307. number_system_for_groupings->primary_grouping_size = *decimal - group_separators[0] - 1;
  308. number_system_for_groupings->secondary_grouping_size = number_system_for_groupings->primary_grouping_size;
  309. } else {
  310. number_system_for_groupings->primary_grouping_size = *decimal - group_separators[1] - 1;
  311. number_system_for_groupings->secondary_grouping_size = group_separators[1] - group_separators[0] - 1;
  312. }
  313. }
  314. pattern = DeprecatedString::formatted("{}{{number}}{}",
  315. *start_number_index > 0 ? pattern.substring_view(0, *start_number_index) : ""sv,
  316. pattern.substring_view(end_number_index));
  317. // This is specifically handled here rather than in the replacements HashMap above so
  318. // that we do not errantly replace zeroes in number patterns.
  319. if (pattern.contains(*replacements.get("E"sv)))
  320. pattern = pattern.replace("0"sv, "{scientificExponent}"sv, ReplaceMode::FirstOnly);
  321. }
  322. if (type == NumberFormatType::Compact)
  323. return parse_identifiers(move(pattern), "compactIdentifier"sv, cldr, format);
  324. return pattern;
  325. };
  326. auto zero_format = replace_patterns(move(patterns[0]));
  327. format.positive_format_index = cldr.unique_strings.ensure(DeprecatedString::formatted("{{plusSign}}{}", zero_format));
  328. if (patterns.size() == 2) {
  329. auto negative_format = replace_patterns(move(patterns[1]));
  330. format.negative_format_index = cldr.unique_strings.ensure(move(negative_format));
  331. } else {
  332. format.negative_format_index = cldr.unique_strings.ensure(DeprecatedString::formatted("{{minusSign}}{}", zero_format));
  333. }
  334. format.zero_format_index = cldr.unique_strings.ensure(move(zero_format));
  335. }
  336. static void parse_number_pattern(Vector<DeprecatedString> patterns, CLDR& cldr, NumberFormatType type, size_t& format_index, NumberSystem* number_system_for_groupings = nullptr)
  337. {
  338. NumberFormat format {};
  339. parse_number_pattern(move(patterns), cldr, type, format, number_system_for_groupings);
  340. format_index = cldr.unique_formats.ensure(move(format));
  341. }
  342. static ErrorOr<void> parse_number_systems(DeprecatedString locale_numbers_path, CLDR& cldr, LocaleData& locale)
  343. {
  344. LexicalPath numbers_path(move(locale_numbers_path));
  345. numbers_path = numbers_path.append("numbers.json"sv);
  346. auto numbers = TRY(read_json_file(numbers_path.string()));
  347. auto const& main_object = numbers.as_object().get_object("main"sv).value();
  348. auto const& locale_object = main_object.get_object(numbers_path.parent().basename()).value();
  349. auto const& locale_numbers_object = locale_object.get_object("numbers"sv).value();
  350. auto const& minimum_grouping_digits = locale_numbers_object.get_deprecated_string("minimumGroupingDigits"sv).value();
  351. Vector<Optional<NumberSystem>> number_systems;
  352. number_systems.resize(cldr.number_systems.size());
  353. auto ensure_number_system = [&](auto const& system) -> NumberSystem& {
  354. auto system_index = cldr.number_systems.find_first_index(system).value();
  355. VERIFY(system_index < number_systems.size());
  356. auto& number_system = number_systems.at(system_index);
  357. if (!number_system.has_value())
  358. number_system = NumberSystem {};
  359. return number_system.value();
  360. };
  361. auto parse_number_format = [&](auto const& format_object) {
  362. Vector<size_t> result;
  363. result.ensure_capacity(format_object.size());
  364. format_object.for_each_member([&](auto const& key, JsonValue const& value) {
  365. auto split_key = key.split_view('-');
  366. if (split_key.size() != 3)
  367. return;
  368. auto patterns = value.as_string().split(';');
  369. NumberFormat format {};
  370. if (auto type = split_key[0].template to_uint<u64>(); type.has_value()) {
  371. VERIFY(*type % 10 == 0);
  372. format.magnitude = static_cast<u8>(log10(*type));
  373. if (patterns[0] != "0"sv) {
  374. auto number_of_zeroes_in_pattern = patterns[0].count("0"sv);
  375. VERIFY(format.magnitude >= number_of_zeroes_in_pattern);
  376. format.exponent = format.magnitude + 1 - number_of_zeroes_in_pattern;
  377. }
  378. } else {
  379. VERIFY(split_key[0] == "unitPattern"sv);
  380. }
  381. format.plurality = Locale::plural_category_from_string(split_key[2]);
  382. parse_number_pattern(move(patterns), cldr, NumberFormatType::Compact, format);
  383. auto format_index = cldr.unique_formats.ensure(move(format));
  384. result.append(format_index);
  385. });
  386. return cldr.unique_format_lists.ensure(move(result));
  387. };
  388. auto numeric_symbol_from_string = [&](StringView numeric_symbol) -> Optional<Locale::NumericSymbol> {
  389. if (numeric_symbol == "approximatelySign"sv)
  390. return Locale::NumericSymbol::ApproximatelySign;
  391. if (numeric_symbol == "decimal"sv)
  392. return Locale::NumericSymbol::Decimal;
  393. if (numeric_symbol == "exponential"sv)
  394. return Locale::NumericSymbol::Exponential;
  395. if (numeric_symbol == "group"sv)
  396. return Locale::NumericSymbol::Group;
  397. if (numeric_symbol == "infinity"sv)
  398. return Locale::NumericSymbol::Infinity;
  399. if (numeric_symbol == "minusSign"sv)
  400. return Locale::NumericSymbol::MinusSign;
  401. if (numeric_symbol == "nan"sv)
  402. return Locale::NumericSymbol::NaN;
  403. if (numeric_symbol == "percentSign"sv)
  404. return Locale::NumericSymbol::PercentSign;
  405. if (numeric_symbol == "plusSign"sv)
  406. return Locale::NumericSymbol::PlusSign;
  407. if (numeric_symbol == "timeSeparator"sv)
  408. return Locale::NumericSymbol::TimeSeparator;
  409. return {};
  410. };
  411. locale_numbers_object.for_each_member([&](auto const& key, JsonValue const& value) {
  412. constexpr auto symbols_prefix = "symbols-numberSystem-"sv;
  413. constexpr auto decimal_formats_prefix = "decimalFormats-numberSystem-"sv;
  414. constexpr auto currency_formats_prefix = "currencyFormats-numberSystem-"sv;
  415. constexpr auto percent_formats_prefix = "percentFormats-numberSystem-"sv;
  416. constexpr auto scientific_formats_prefix = "scientificFormats-numberSystem-"sv;
  417. constexpr auto misc_patterns_prefix = "miscPatterns-numberSystem-"sv;
  418. if (key.starts_with(symbols_prefix)) {
  419. auto system = key.substring(symbols_prefix.length());
  420. auto& number_system = ensure_number_system(system);
  421. NumericSymbolList symbols;
  422. value.as_object().for_each_member([&](auto const& symbol, JsonValue const& localization) {
  423. auto numeric_symbol = numeric_symbol_from_string(symbol);
  424. if (!numeric_symbol.has_value())
  425. return;
  426. if (to_underlying(*numeric_symbol) >= symbols.size())
  427. symbols.resize(to_underlying(*numeric_symbol) + 1);
  428. auto symbol_index = cldr.unique_strings.ensure(localization.as_string());
  429. symbols[to_underlying(*numeric_symbol)] = symbol_index;
  430. });
  431. // The range separator does not appear in the symbols list, we have to extract it from
  432. // the range pattern.
  433. auto misc_patterns_key = DeprecatedString::formatted("{}{}", misc_patterns_prefix, system);
  434. auto misc_patterns = locale_numbers_object.get_object(misc_patterns_key).value();
  435. auto range_separator = misc_patterns.get_deprecated_string("range"sv).value();
  436. auto begin_index = range_separator.find("{0}"sv).value() + "{0}"sv.length();
  437. auto end_index = range_separator.find("{1}"sv).value();
  438. range_separator = range_separator.substring(begin_index, end_index - begin_index);
  439. if (to_underlying(Locale::NumericSymbol::RangeSeparator) >= symbols.size())
  440. symbols.resize(to_underlying(Locale::NumericSymbol::RangeSeparator) + 1);
  441. auto symbol_index = cldr.unique_strings.ensure(move(range_separator));
  442. symbols[to_underlying(Locale::NumericSymbol::RangeSeparator)] = symbol_index;
  443. number_system.symbols = cldr.unique_symbols.ensure(move(symbols));
  444. } else if (key.starts_with(decimal_formats_prefix)) {
  445. auto system = key.substring(decimal_formats_prefix.length());
  446. auto& number_system = ensure_number_system(system);
  447. auto format_object = value.as_object().get_deprecated_string("standard"sv).value();
  448. parse_number_pattern(format_object.split(';'), cldr, NumberFormatType::Standard, number_system.decimal_format, &number_system);
  449. auto const& long_format = value.as_object().get_object("long"sv)->get_object("decimalFormat"sv).value();
  450. number_system.decimal_long_formats = parse_number_format(long_format);
  451. auto const& short_format = value.as_object().get_object("short"sv)->get_object("decimalFormat"sv).value();
  452. number_system.decimal_short_formats = parse_number_format(short_format);
  453. } else if (key.starts_with(currency_formats_prefix)) {
  454. auto system = key.substring(currency_formats_prefix.length());
  455. auto& number_system = ensure_number_system(system);
  456. auto format_object = value.as_object().get_deprecated_string("standard"sv).value();
  457. parse_number_pattern(format_object.split(';'), cldr, NumberFormatType::Standard, number_system.currency_format);
  458. format_object = value.as_object().get_deprecated_string("accounting"sv).value();
  459. parse_number_pattern(format_object.split(';'), cldr, NumberFormatType::Standard, number_system.accounting_format);
  460. number_system.currency_unit_formats = parse_number_format(value.as_object());
  461. if (value.as_object().has_object("short"sv)) {
  462. auto const& short_format = value.as_object().get_object("short"sv)->get_object("standard"sv).value();
  463. number_system.currency_short_formats = parse_number_format(short_format);
  464. }
  465. } else if (key.starts_with(percent_formats_prefix)) {
  466. auto system = key.substring(percent_formats_prefix.length());
  467. auto& number_system = ensure_number_system(system);
  468. auto format_object = value.as_object().get_deprecated_string("standard"sv).value();
  469. parse_number_pattern(format_object.split(';'), cldr, NumberFormatType::Standard, number_system.percent_format);
  470. } else if (key.starts_with(scientific_formats_prefix)) {
  471. auto system = key.substring(scientific_formats_prefix.length());
  472. auto& number_system = ensure_number_system(system);
  473. auto format_object = value.as_object().get_deprecated_string("standard"sv).value();
  474. parse_number_pattern(format_object.split(';'), cldr, NumberFormatType::Standard, number_system.scientific_format);
  475. }
  476. });
  477. locale.number_systems.ensure_capacity(number_systems.size());
  478. for (auto& number_system : number_systems) {
  479. size_t system_index = 0;
  480. if (number_system.has_value())
  481. system_index = cldr.unique_systems.ensure(number_system.release_value());
  482. locale.number_systems.append(system_index);
  483. }
  484. locale.minimum_grouping_digits = minimum_grouping_digits.template to_uint<u8>().value();
  485. return {};
  486. }
  487. static ErrorOr<void> parse_units(DeprecatedString locale_units_path, CLDR& cldr, LocaleData& locale)
  488. {
  489. LexicalPath units_path(move(locale_units_path));
  490. units_path = units_path.append("units.json"sv);
  491. auto locale_units = TRY(read_json_file(units_path.string()));
  492. auto const& main_object = locale_units.as_object().get_object("main"sv).value();
  493. auto const& locale_object = main_object.get_object(units_path.parent().basename()).value();
  494. auto const& locale_units_object = locale_object.get_object("units"sv).value();
  495. auto const& long_object = locale_units_object.get_object("long"sv).value();
  496. auto const& short_object = locale_units_object.get_object("short"sv).value();
  497. auto const& narrow_object = locale_units_object.get_object("narrow"sv).value();
  498. HashMap<DeprecatedString, Unit> units;
  499. auto ensure_unit = [&](auto const& unit) -> Unit& {
  500. return units.ensure(unit, [&]() {
  501. auto unit_index = cldr.unique_strings.ensure(unit);
  502. return Unit { .unit = unit_index };
  503. });
  504. };
  505. auto is_sanctioned_unit = [](StringView unit_name) {
  506. // LibUnicode generally tries to avoid being directly dependent on ECMA-402, but this rather significantly reduces the amount
  507. // of data generated here, and ECMA-402 is currently the only consumer of this data.
  508. constexpr auto sanctioned_units = JS::Intl::sanctioned_single_unit_identifiers();
  509. return find(sanctioned_units.begin(), sanctioned_units.end(), unit_name) != sanctioned_units.end();
  510. };
  511. auto parse_units_object = [&](auto const& units_object, Locale::Style style) {
  512. constexpr auto unit_pattern_prefix = "unitPattern-count-"sv;
  513. constexpr auto combined_unit_separator = "-per-"sv;
  514. units_object.for_each_member([&](auto const& key, JsonValue const& value) {
  515. auto end_of_category = key.find('-');
  516. if (!end_of_category.has_value())
  517. return;
  518. auto unit_name = key.substring(*end_of_category + 1);
  519. if (!is_sanctioned_unit(unit_name)) {
  520. auto indices = unit_name.find_all(combined_unit_separator);
  521. if (indices.size() != 1)
  522. return;
  523. auto numerator = unit_name.substring_view(0, indices[0]);
  524. auto denominator = unit_name.substring_view(indices[0] + combined_unit_separator.length());
  525. if (!is_sanctioned_unit(numerator) || !is_sanctioned_unit(denominator))
  526. return;
  527. }
  528. auto& unit = ensure_unit(unit_name);
  529. NumberFormatList formats;
  530. value.as_object().for_each_member([&](auto const& unit_key, JsonValue const& pattern_value) {
  531. if (!unit_key.starts_with(unit_pattern_prefix))
  532. return;
  533. NumberFormat format {};
  534. auto plurality = unit_key.substring_view(unit_pattern_prefix.length());
  535. format.plurality = Locale::plural_category_from_string(plurality);
  536. auto zero_format = pattern_value.as_string().replace("{0}"sv, "{number}"sv, ReplaceMode::FirstOnly);
  537. zero_format = parse_identifiers(zero_format, "unitIdentifier"sv, cldr, format);
  538. format.positive_format_index = cldr.unique_strings.ensure(zero_format.replace("{number}"sv, "{plusSign}{number}"sv, ReplaceMode::FirstOnly));
  539. format.negative_format_index = cldr.unique_strings.ensure(zero_format.replace("{number}"sv, "{minusSign}{number}"sv, ReplaceMode::FirstOnly));
  540. format.zero_format_index = cldr.unique_strings.ensure(move(zero_format));
  541. formats.append(cldr.unique_formats.ensure(move(format)));
  542. });
  543. auto number_format_list_index = cldr.unique_format_lists.ensure(move(formats));
  544. switch (style) {
  545. case Locale::Style::Long:
  546. unit.long_formats = number_format_list_index;
  547. break;
  548. case Locale::Style::Short:
  549. unit.short_formats = number_format_list_index;
  550. break;
  551. case Locale::Style::Narrow:
  552. unit.narrow_formats = number_format_list_index;
  553. break;
  554. default:
  555. VERIFY_NOT_REACHED();
  556. }
  557. });
  558. };
  559. parse_units_object(long_object, Locale::Style::Long);
  560. parse_units_object(short_object, Locale::Style::Short);
  561. parse_units_object(narrow_object, Locale::Style::Narrow);
  562. for (auto& unit : units) {
  563. auto unit_index = cldr.unique_units.ensure(move(unit.value));
  564. locale.units.set(unit.key, unit_index);
  565. }
  566. return {};
  567. }
  568. static ErrorOr<void> parse_all_locales(DeprecatedString core_path, DeprecatedString numbers_path, DeprecatedString units_path, CLDR& cldr)
  569. {
  570. LexicalPath core_supplemental_path(move(core_path));
  571. core_supplemental_path = core_supplemental_path.append("supplemental"sv);
  572. VERIFY(FileSystem::is_directory(core_supplemental_path.string()));
  573. TRY(parse_number_system_digits(core_supplemental_path.string(), cldr));
  574. auto remove_variants_from_path = [&](DeprecatedString path) -> ErrorOr<DeprecatedString> {
  575. auto parsed_locale = TRY(CanonicalLanguageID::parse(cldr.unique_strings, LexicalPath::basename(path)));
  576. StringBuilder builder;
  577. builder.append(cldr.unique_strings.get(parsed_locale.language));
  578. if (auto script = cldr.unique_strings.get(parsed_locale.script); !script.is_empty())
  579. builder.appendff("-{}", script);
  580. if (auto region = cldr.unique_strings.get(parsed_locale.region); !region.is_empty())
  581. builder.appendff("-{}", region);
  582. return builder.to_deprecated_string();
  583. };
  584. TRY(Core::Directory::for_each_entry(TRY(String::formatted("{}/main", numbers_path)), Core::DirIterator::SkipParentAndBaseDir, [&](auto& entry, auto& directory) -> ErrorOr<IterationDecision> {
  585. auto numbers_path = LexicalPath::join(directory.path().string(), entry.name).string();
  586. auto language = TRY(remove_variants_from_path(numbers_path));
  587. auto& locale = cldr.locales.ensure(language);
  588. TRY(parse_number_systems(numbers_path, cldr, locale));
  589. return IterationDecision::Continue;
  590. }));
  591. TRY(Core::Directory::for_each_entry(TRY(String::formatted("{}/main", units_path)), Core::DirIterator::SkipParentAndBaseDir, [&](auto& entry, auto& directory) -> ErrorOr<IterationDecision> {
  592. auto units_path = LexicalPath::join(directory.path().string(), entry.name).string();
  593. auto language = TRY(remove_variants_from_path(units_path));
  594. auto& locale = cldr.locales.ensure(language);
  595. TRY(parse_units(units_path, cldr, locale));
  596. return IterationDecision::Continue;
  597. }));
  598. return {};
  599. }
  600. static DeprecatedString format_identifier(StringView, DeprecatedString identifier)
  601. {
  602. return identifier.to_titlecase();
  603. }
  604. static ErrorOr<void> generate_unicode_locale_header(Core::InputBufferedFile& file, CLDR& cldr)
  605. {
  606. StringBuilder builder;
  607. SourceGenerator generator { builder };
  608. generator.append(R"~~~(
  609. #include <AK/Types.h>
  610. #pragma once
  611. namespace Locale {
  612. )~~~");
  613. generate_enum(generator, format_identifier, "NumberSystem"sv, {}, cldr.number_systems);
  614. generator.append(R"~~~(
  615. }
  616. )~~~");
  617. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  618. return {};
  619. }
  620. static ErrorOr<void> generate_unicode_locale_implementation(Core::InputBufferedFile& file, CLDR& cldr)
  621. {
  622. StringBuilder builder;
  623. SourceGenerator generator { builder };
  624. generator.set("string_index_type"sv, cldr.unique_strings.type_that_fits());
  625. generator.set("number_format_index_type"sv, cldr.unique_formats.type_that_fits());
  626. generator.set("number_format_list_index_type"sv, cldr.unique_format_lists.type_that_fits());
  627. generator.set("numeric_symbol_list_index_type"sv, cldr.unique_symbols.type_that_fits());
  628. generator.set("identifier_count", DeprecatedString::number(cldr.max_identifier_count));
  629. generator.append(R"~~~(
  630. #include <AK/Array.h>
  631. #include <AK/BinarySearch.h>
  632. #include <AK/Optional.h>
  633. #include <AK/Span.h>
  634. #include <AK/StringView.h>
  635. #include <AK/Vector.h>
  636. #include <LibLocale/Locale.h>
  637. #include <LibLocale/LocaleData.h>
  638. #include <LibLocale/NumberFormat.h>
  639. #include <LibLocale/NumberFormatData.h>
  640. #include <LibLocale/PluralRules.h>
  641. namespace Locale {
  642. )~~~");
  643. cldr.unique_strings.generate(generator);
  644. generator.append(R"~~~(
  645. struct NumberFormatImpl {
  646. ErrorOr<NumberFormat> to_unicode_number_format() const {
  647. NumberFormat number_format {};
  648. number_format.magnitude = magnitude;
  649. number_format.exponent = exponent;
  650. number_format.plurality = static_cast<PluralCategory>(plurality);
  651. number_format.zero_format = decode_string(zero_format);
  652. number_format.positive_format = decode_string(positive_format);
  653. number_format.negative_format = decode_string(negative_format);
  654. TRY(number_format.identifiers.try_ensure_capacity(identifiers.size()));
  655. for (@string_index_type@ identifier : identifiers)
  656. number_format.identifiers.unchecked_append(decode_string(identifier));
  657. return number_format;
  658. }
  659. u8 magnitude { 0 };
  660. u8 exponent { 0 };
  661. u8 plurality { 0 };
  662. @string_index_type@ zero_format { 0 };
  663. @string_index_type@ positive_format { 0 };
  664. @string_index_type@ negative_format { 0 };
  665. Array<@string_index_type@, @identifier_count@> identifiers {};
  666. };
  667. struct NumberSystemData {
  668. @numeric_symbol_list_index_type@ symbols { 0 };
  669. u8 primary_grouping_size { 0 };
  670. u8 secondary_grouping_size { 0 };
  671. @number_format_index_type@ decimal_format { 0 };
  672. @number_format_list_index_type@ decimal_long_formats { 0 };
  673. @number_format_list_index_type@ decimal_short_formats { 0 };
  674. @number_format_index_type@ currency_format { 0 };
  675. @number_format_index_type@ accounting_format { 0 };
  676. @number_format_list_index_type@ currency_unit_formats { 0 };
  677. @number_format_list_index_type@ currency_short_formats { 0 };
  678. @number_format_index_type@ percent_format { 0 };
  679. @number_format_index_type@ scientific_format { 0 };
  680. };
  681. struct Unit {
  682. @string_index_type@ unit { 0 };
  683. @number_format_list_index_type@ long_formats { 0 };
  684. @number_format_list_index_type@ short_formats { 0 };
  685. @number_format_list_index_type@ narrow_formats { 0 };
  686. };
  687. )~~~");
  688. cldr.unique_formats.generate(generator, "NumberFormatImpl"sv, "s_number_formats"sv, 10);
  689. cldr.unique_format_lists.generate(generator, cldr.unique_formats.type_that_fits(), "s_number_format_lists"sv);
  690. cldr.unique_symbols.generate(generator, cldr.unique_strings.type_that_fits(), "s_numeric_symbol_lists"sv);
  691. cldr.unique_systems.generate(generator, "NumberSystemData"sv, "s_number_systems"sv, 10);
  692. cldr.unique_units.generate(generator, "Unit"sv, "s_units"sv, 10);
  693. auto locales = cldr.locales.keys();
  694. quick_sort(locales);
  695. generator.set("size", DeprecatedString::number(locales.size()));
  696. generator.append(R"~~~(
  697. static constexpr Array<u8, @size@> s_minimum_grouping_digits { { )~~~");
  698. bool first = true;
  699. for (auto const& locale : locales) {
  700. generator.append(first ? " "sv : ", "sv);
  701. generator.append(DeprecatedString::number(cldr.locales.find(locale)->value.minimum_grouping_digits));
  702. first = false;
  703. }
  704. generator.append(" } };\n");
  705. auto append_map = [&](DeprecatedString name, auto type, auto const& map) {
  706. generator.set("name", name);
  707. generator.set("type", type);
  708. generator.set("size", DeprecatedString::number(map.size()));
  709. generator.append(R"~~~(
  710. static constexpr Array<@type@, @size@> @name@ { {)~~~");
  711. bool first = true;
  712. for (auto const& item : map) {
  713. generator.append(first ? " "sv : ", "sv);
  714. if constexpr (requires { item.value; })
  715. generator.append(DeprecatedString::number(item.value));
  716. else
  717. generator.append(DeprecatedString::number(item));
  718. first = false;
  719. }
  720. generator.append(" } };");
  721. };
  722. generate_mapping(generator, cldr.number_system_digits, "u32"sv, "s_number_systems_digits"sv, "s_number_systems_digits_{}"sv, nullptr, [&](auto const& name, auto const& value) { append_map(name, "u32"sv, value); });
  723. generate_mapping(generator, cldr.locales, cldr.unique_systems.type_that_fits(), "s_locale_number_systems"sv, "s_number_systems_{}"sv, nullptr, [&](auto const& name, auto const& value) { append_map(name, cldr.unique_systems.type_that_fits(), value.number_systems); });
  724. generate_mapping(generator, cldr.locales, cldr.unique_units.type_that_fits(), "s_locale_units"sv, "s_units_{}"sv, nullptr, [&](auto const& name, auto const& value) { append_map(name, cldr.unique_units.type_that_fits(), value.units); });
  725. generator.append(R"~~~(
  726. static Optional<NumberSystem> keyword_to_number_system(KeywordNumbers keyword)
  727. {
  728. switch (keyword) {)~~~");
  729. for (auto const& number_system : cldr.number_systems) {
  730. generator.set("name"sv, format_identifier({}, number_system));
  731. generator.append(R"~~~(
  732. case KeywordNumbers::@name@:
  733. return NumberSystem::@name@;)~~~");
  734. }
  735. generator.append(R"~~~(
  736. default:
  737. return {};
  738. }
  739. }
  740. Optional<ReadonlySpan<u32>> get_digits_for_number_system(StringView system)
  741. {
  742. auto number_system_keyword = keyword_nu_from_string(system);
  743. if (!number_system_keyword.has_value())
  744. return {};
  745. auto number_system_value = keyword_to_number_system(*number_system_keyword);
  746. if (!number_system_value.has_value())
  747. return {};
  748. auto number_system_index = to_underlying(*number_system_value);
  749. return s_number_systems_digits[number_system_index];
  750. }
  751. static NumberSystemData const* find_number_system(StringView locale, StringView system)
  752. {
  753. auto locale_value = locale_from_string(locale);
  754. if (!locale_value.has_value())
  755. return nullptr;
  756. auto locale_index = to_underlying(*locale_value) - 1; // Subtract 1 because 0 == Locale::None.
  757. auto const& number_systems = s_locale_number_systems.at(locale_index);
  758. auto lookup_number_system = [&](auto number_system) -> NumberSystemData const* {
  759. auto number_system_keyword = keyword_nu_from_string(number_system);
  760. if (!number_system_keyword.has_value())
  761. return nullptr;
  762. auto number_system_value = keyword_to_number_system(*number_system_keyword);
  763. if (!number_system_value.has_value())
  764. return nullptr;
  765. auto number_system_index = to_underlying(*number_system_value);
  766. number_system_index = number_systems.at(number_system_index);
  767. if (number_system_index == 0)
  768. return nullptr;
  769. return &s_number_systems.at(number_system_index);
  770. };
  771. if (auto const* number_system = lookup_number_system(system))
  772. return number_system;
  773. auto default_number_system = get_preferred_keyword_value_for_locale(locale, "nu"sv);
  774. if (!default_number_system.has_value())
  775. return nullptr;
  776. return lookup_number_system(*default_number_system);
  777. }
  778. Optional<StringView> get_number_system_symbol(StringView locale, StringView system, NumericSymbol symbol)
  779. {
  780. if (auto const* number_system = find_number_system(locale, system); number_system != nullptr) {
  781. auto symbols = s_numeric_symbol_lists.at(number_system->symbols);
  782. auto symbol_index = to_underlying(symbol);
  783. if (symbol_index >= symbols.size())
  784. return {};
  785. return decode_string(symbols[symbol_index]);
  786. }
  787. return {};
  788. }
  789. Optional<NumberGroupings> get_number_system_groupings(StringView locale, StringView system)
  790. {
  791. auto locale_value = locale_from_string(locale);
  792. if (!locale_value.has_value())
  793. return {};
  794. u8 minimum_grouping_digits = s_minimum_grouping_digits[to_underlying(*locale_value) - 1];
  795. if (auto const* number_system = find_number_system(locale, system); number_system != nullptr)
  796. return NumberGroupings { minimum_grouping_digits, number_system->primary_grouping_size, number_system->secondary_grouping_size };
  797. return {};
  798. }
  799. ErrorOr<Optional<NumberFormat>> get_standard_number_system_format(StringView locale, StringView system, StandardNumberFormatType type)
  800. {
  801. if (auto const* number_system = find_number_system(locale, system); number_system != nullptr) {
  802. @number_format_index_type@ format_index = 0;
  803. switch (type) {
  804. case StandardNumberFormatType::Decimal:
  805. format_index = number_system->decimal_format;
  806. break;
  807. case StandardNumberFormatType::Currency:
  808. format_index = number_system->currency_format;
  809. break;
  810. case StandardNumberFormatType::Accounting:
  811. format_index = number_system->accounting_format;
  812. break;
  813. case StandardNumberFormatType::Percent:
  814. format_index = number_system->percent_format;
  815. break;
  816. case StandardNumberFormatType::Scientific:
  817. format_index = number_system->scientific_format;
  818. break;
  819. }
  820. return TRY(s_number_formats[format_index].to_unicode_number_format());
  821. }
  822. return OptionalNone {};
  823. }
  824. ErrorOr<Vector<NumberFormat>> get_compact_number_system_formats(StringView locale, StringView system, CompactNumberFormatType type)
  825. {
  826. Vector<NumberFormat> formats;
  827. if (auto const* number_system = find_number_system(locale, system); number_system != nullptr) {
  828. @number_format_list_index_type@ number_format_list_index { 0 };
  829. switch (type) {
  830. case CompactNumberFormatType::DecimalLong:
  831. number_format_list_index = number_system->decimal_long_formats;
  832. break;
  833. case CompactNumberFormatType::DecimalShort:
  834. number_format_list_index = number_system->decimal_short_formats;
  835. break;
  836. case CompactNumberFormatType::CurrencyUnit:
  837. number_format_list_index = number_system->currency_unit_formats;
  838. break;
  839. case CompactNumberFormatType::CurrencyShort:
  840. number_format_list_index = number_system->currency_short_formats;
  841. break;
  842. }
  843. auto number_formats = s_number_format_lists.at(number_format_list_index);
  844. TRY(formats.try_ensure_capacity(number_formats.size()));
  845. for (auto number_format : number_formats)
  846. formats.unchecked_append(TRY(s_number_formats[number_format].to_unicode_number_format()));
  847. }
  848. return formats;
  849. }
  850. static Unit const* find_units(StringView locale, StringView unit)
  851. {
  852. auto locale_value = locale_from_string(locale);
  853. if (!locale_value.has_value())
  854. return nullptr;
  855. auto locale_index = to_underlying(*locale_value) - 1; // Subtract 1 because 0 == Locale::None.
  856. auto const& locale_units = s_locale_units.at(locale_index);
  857. for (auto unit_index : locale_units) {
  858. auto const& units = s_units.at(unit_index);
  859. if (unit == decode_string(units.unit))
  860. return &units;
  861. };
  862. return nullptr;
  863. }
  864. ErrorOr<Vector<NumberFormat>> get_unit_formats(StringView locale, StringView unit, Style style)
  865. {
  866. Vector<NumberFormat> formats;
  867. if (auto const* units = find_units(locale, unit); units != nullptr) {
  868. @number_format_list_index_type@ number_format_list_index { 0 };
  869. switch (style) {
  870. case Style::Long:
  871. number_format_list_index = units->long_formats;
  872. break;
  873. case Style::Short:
  874. number_format_list_index = units->short_formats;
  875. break;
  876. case Style::Narrow:
  877. number_format_list_index = units->narrow_formats;
  878. break;
  879. default:
  880. VERIFY_NOT_REACHED();
  881. }
  882. auto number_formats = s_number_format_lists.at(number_format_list_index);
  883. TRY(formats.try_ensure_capacity(number_formats.size()));
  884. for (auto number_format : number_formats)
  885. formats.unchecked_append(TRY(s_number_formats[number_format].to_unicode_number_format()));
  886. }
  887. return formats;
  888. }
  889. }
  890. )~~~");
  891. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  892. return {};
  893. }
  894. ErrorOr<int> serenity_main(Main::Arguments arguments)
  895. {
  896. StringView generated_header_path;
  897. StringView generated_implementation_path;
  898. StringView core_path;
  899. StringView numbers_path;
  900. StringView units_path;
  901. Core::ArgsParser args_parser;
  902. args_parser.add_option(generated_header_path, "Path to the Unicode locale header file to generate", "generated-header-path", 'h', "generated-header-path");
  903. args_parser.add_option(generated_implementation_path, "Path to the Unicode locale implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
  904. args_parser.add_option(core_path, "Path to cldr-core directory", "core-path", 'r', "core-path");
  905. args_parser.add_option(numbers_path, "Path to cldr-numbers directory", "numbers-path", 'n', "numbers-path");
  906. args_parser.add_option(units_path, "Path to cldr-units directory", "units-path", 'u', "units-path");
  907. args_parser.parse(arguments);
  908. auto generated_header_file = TRY(open_file(generated_header_path, Core::File::OpenMode::Write));
  909. auto generated_implementation_file = TRY(open_file(generated_implementation_path, Core::File::OpenMode::Write));
  910. CLDR cldr;
  911. TRY(parse_all_locales(core_path, numbers_path, units_path, cldr));
  912. TRY(generate_unicode_locale_header(*generated_header_file, cldr));
  913. TRY(generate_unicode_locale_implementation(*generated_implementation_file, cldr));
  914. return 0;
  915. }