NumberFormat.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. /*
  2. * Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #define AK_DONT_REPLACE_STD
  7. #include <AK/CharacterTypes.h>
  8. #include <AK/QuickSort.h>
  9. #include <AK/Utf8View.h>
  10. #include <LibUnicode/ICU.h>
  11. #include <LibUnicode/Locale.h>
  12. #include <LibUnicode/NumberFormat.h>
  13. #include <LibUnicode/PartitionRange.h>
  14. #include <math.h>
  15. #include <unicode/numberformatter.h>
  16. #include <unicode/numberrangeformatter.h>
  17. #include <unicode/plurrule.h>
  18. namespace Unicode {
  19. NumberFormatStyle number_format_style_from_string(StringView number_format_style)
  20. {
  21. if (number_format_style == "decimal"sv)
  22. return NumberFormatStyle::Decimal;
  23. if (number_format_style == "percent"sv)
  24. return NumberFormatStyle::Percent;
  25. if (number_format_style == "currency"sv)
  26. return NumberFormatStyle::Currency;
  27. if (number_format_style == "unit"sv)
  28. return NumberFormatStyle::Unit;
  29. VERIFY_NOT_REACHED();
  30. }
  31. StringView number_format_style_to_string(NumberFormatStyle number_format_style)
  32. {
  33. switch (number_format_style) {
  34. case NumberFormatStyle::Decimal:
  35. return "decimal"sv;
  36. case NumberFormatStyle::Percent:
  37. return "percent"sv;
  38. case NumberFormatStyle::Currency:
  39. return "currency"sv;
  40. case NumberFormatStyle::Unit:
  41. return "unit"sv;
  42. }
  43. VERIFY_NOT_REACHED();
  44. }
  45. SignDisplay sign_display_from_string(StringView sign_display)
  46. {
  47. if (sign_display == "auto"sv)
  48. return SignDisplay::Auto;
  49. if (sign_display == "never"sv)
  50. return SignDisplay::Never;
  51. if (sign_display == "always"sv)
  52. return SignDisplay::Always;
  53. if (sign_display == "exceptZero"sv)
  54. return SignDisplay::ExceptZero;
  55. if (sign_display == "negative"sv)
  56. return SignDisplay::Negative;
  57. VERIFY_NOT_REACHED();
  58. }
  59. StringView sign_display_to_string(SignDisplay sign_display)
  60. {
  61. switch (sign_display) {
  62. case SignDisplay::Auto:
  63. return "auto"sv;
  64. case SignDisplay::Never:
  65. return "never"sv;
  66. case SignDisplay::Always:
  67. return "always"sv;
  68. case SignDisplay::ExceptZero:
  69. return "exceptZero"sv;
  70. case SignDisplay::Negative:
  71. return "negative"sv;
  72. }
  73. VERIFY_NOT_REACHED();
  74. }
  75. static constexpr UNumberSignDisplay icu_sign_display(SignDisplay sign_display, Optional<CurrencySign> const& currency_sign)
  76. {
  77. switch (sign_display) {
  78. case SignDisplay::Auto:
  79. return currency_sign == CurrencySign::Standard ? UNUM_SIGN_AUTO : UNUM_SIGN_ACCOUNTING;
  80. case SignDisplay::Never:
  81. return UNUM_SIGN_NEVER;
  82. case SignDisplay::Always:
  83. return currency_sign == CurrencySign::Standard ? UNUM_SIGN_ALWAYS : UNUM_SIGN_ACCOUNTING_ALWAYS;
  84. case SignDisplay::ExceptZero:
  85. return currency_sign == CurrencySign::Standard ? UNUM_SIGN_EXCEPT_ZERO : UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
  86. case SignDisplay::Negative:
  87. return currency_sign == CurrencySign::Standard ? UNUM_SIGN_NEGATIVE : UNUM_SIGN_ACCOUNTING_NEGATIVE;
  88. }
  89. VERIFY_NOT_REACHED();
  90. }
  91. Notation notation_from_string(StringView notation)
  92. {
  93. if (notation == "standard"sv)
  94. return Notation::Standard;
  95. if (notation == "scientific"sv)
  96. return Notation::Scientific;
  97. if (notation == "engineering"sv)
  98. return Notation::Engineering;
  99. if (notation == "compact"sv)
  100. return Notation::Compact;
  101. VERIFY_NOT_REACHED();
  102. }
  103. StringView notation_to_string(Notation notation)
  104. {
  105. switch (notation) {
  106. case Notation::Standard:
  107. return "standard"sv;
  108. case Notation::Scientific:
  109. return "scientific"sv;
  110. case Notation::Engineering:
  111. return "engineering"sv;
  112. case Notation::Compact:
  113. return "compact"sv;
  114. }
  115. VERIFY_NOT_REACHED();
  116. }
  117. static icu::number::Notation icu_notation(Notation notation, Optional<CompactDisplay> const& compact_display)
  118. {
  119. switch (notation) {
  120. case Notation::Standard:
  121. return icu::number::Notation::simple();
  122. case Notation::Scientific:
  123. return icu::number::Notation::scientific();
  124. case Notation::Engineering:
  125. return icu::number::Notation::engineering();
  126. case Notation::Compact:
  127. switch (*compact_display) {
  128. case CompactDisplay::Short:
  129. return icu::number::Notation::compactShort();
  130. case CompactDisplay::Long:
  131. return icu::number::Notation::compactLong();
  132. }
  133. }
  134. VERIFY_NOT_REACHED();
  135. }
  136. CompactDisplay compact_display_from_string(StringView compact_display)
  137. {
  138. if (compact_display == "short"sv)
  139. return CompactDisplay::Short;
  140. if (compact_display == "long"sv)
  141. return CompactDisplay::Long;
  142. VERIFY_NOT_REACHED();
  143. }
  144. StringView compact_display_to_string(CompactDisplay compact_display)
  145. {
  146. switch (compact_display) {
  147. case CompactDisplay::Short:
  148. return "short"sv;
  149. case CompactDisplay::Long:
  150. return "long"sv;
  151. }
  152. VERIFY_NOT_REACHED();
  153. }
  154. Grouping grouping_from_string(StringView grouping)
  155. {
  156. if (grouping == "always"sv)
  157. return Grouping::Always;
  158. if (grouping == "auto"sv)
  159. return Grouping::Auto;
  160. if (grouping == "min2"sv)
  161. return Grouping::Min2;
  162. if (grouping == "false"sv)
  163. return Grouping::False;
  164. VERIFY_NOT_REACHED();
  165. }
  166. StringView grouping_to_string(Grouping grouping)
  167. {
  168. switch (grouping) {
  169. case Grouping::Always:
  170. return "always"sv;
  171. case Grouping::Auto:
  172. return "auto"sv;
  173. case Grouping::Min2:
  174. return "min2"sv;
  175. case Grouping::False:
  176. return "false"sv;
  177. }
  178. VERIFY_NOT_REACHED();
  179. }
  180. static constexpr UNumberGroupingStrategy icu_grouping_strategy(Grouping grouping)
  181. {
  182. switch (grouping) {
  183. case Grouping::Always:
  184. return UNUM_GROUPING_ON_ALIGNED;
  185. case Grouping::Auto:
  186. return UNUM_GROUPING_AUTO;
  187. case Grouping::Min2:
  188. return UNUM_GROUPING_MIN2;
  189. case Grouping::False:
  190. return UNUM_GROUPING_OFF;
  191. }
  192. VERIFY_NOT_REACHED();
  193. }
  194. CurrencyDisplay currency_display_from_string(StringView currency_display)
  195. {
  196. if (currency_display == "code"sv)
  197. return CurrencyDisplay::Code;
  198. if (currency_display == "symbol"sv)
  199. return CurrencyDisplay::Symbol;
  200. if (currency_display == "narrowSymbol"sv)
  201. return CurrencyDisplay::NarrowSymbol;
  202. if (currency_display == "name"sv)
  203. return CurrencyDisplay::Name;
  204. VERIFY_NOT_REACHED();
  205. }
  206. StringView currency_display_to_string(CurrencyDisplay currency_display)
  207. {
  208. switch (currency_display) {
  209. case CurrencyDisplay::Code:
  210. return "code"sv;
  211. case CurrencyDisplay::Symbol:
  212. return "symbol"sv;
  213. case CurrencyDisplay::NarrowSymbol:
  214. return "narrowSymbol"sv;
  215. case CurrencyDisplay::Name:
  216. return "name"sv;
  217. }
  218. VERIFY_NOT_REACHED();
  219. }
  220. static constexpr UNumberUnitWidth icu_currency_display(CurrencyDisplay currency_display)
  221. {
  222. switch (currency_display) {
  223. case CurrencyDisplay::Code:
  224. return UNUM_UNIT_WIDTH_ISO_CODE;
  225. case CurrencyDisplay::Symbol:
  226. return UNUM_UNIT_WIDTH_SHORT;
  227. case CurrencyDisplay::NarrowSymbol:
  228. return UNUM_UNIT_WIDTH_NARROW;
  229. case CurrencyDisplay::Name:
  230. return UNUM_UNIT_WIDTH_FULL_NAME;
  231. }
  232. VERIFY_NOT_REACHED();
  233. }
  234. CurrencySign currency_sign_from_string(StringView currency_sign)
  235. {
  236. if (currency_sign == "standard"sv)
  237. return CurrencySign::Standard;
  238. if (currency_sign == "accounting"sv)
  239. return CurrencySign::Accounting;
  240. VERIFY_NOT_REACHED();
  241. }
  242. StringView currency_sign_to_string(CurrencySign currency_sign)
  243. {
  244. switch (currency_sign) {
  245. case CurrencySign::Standard:
  246. return "standard"sv;
  247. case CurrencySign::Accounting:
  248. return "accounting"sv;
  249. }
  250. VERIFY_NOT_REACHED();
  251. }
  252. RoundingType rounding_type_from_string(StringView rounding_type)
  253. {
  254. if (rounding_type == "significantDigits"sv)
  255. return RoundingType::SignificantDigits;
  256. if (rounding_type == "fractionDigits"sv)
  257. return RoundingType::FractionDigits;
  258. if (rounding_type == "morePrecision"sv)
  259. return RoundingType::MorePrecision;
  260. if (rounding_type == "lessPrecision"sv)
  261. return RoundingType::LessPrecision;
  262. VERIFY_NOT_REACHED();
  263. }
  264. StringView rounding_type_to_string(RoundingType rounding_type)
  265. {
  266. switch (rounding_type) {
  267. case RoundingType::SignificantDigits:
  268. return "significantDigits"sv;
  269. case RoundingType::FractionDigits:
  270. return "fractionDigits"sv;
  271. case RoundingType::MorePrecision:
  272. return "morePrecision"sv;
  273. case RoundingType::LessPrecision:
  274. return "lessPrecision"sv;
  275. }
  276. VERIFY_NOT_REACHED();
  277. }
  278. RoundingMode rounding_mode_from_string(StringView rounding_mode)
  279. {
  280. if (rounding_mode == "ceil"sv)
  281. return RoundingMode::Ceil;
  282. if (rounding_mode == "expand"sv)
  283. return RoundingMode::Expand;
  284. if (rounding_mode == "floor"sv)
  285. return RoundingMode::Floor;
  286. if (rounding_mode == "halfCeil"sv)
  287. return RoundingMode::HalfCeil;
  288. if (rounding_mode == "halfEven"sv)
  289. return RoundingMode::HalfEven;
  290. if (rounding_mode == "halfExpand"sv)
  291. return RoundingMode::HalfExpand;
  292. if (rounding_mode == "halfFloor"sv)
  293. return RoundingMode::HalfFloor;
  294. if (rounding_mode == "halfTrunc"sv)
  295. return RoundingMode::HalfTrunc;
  296. if (rounding_mode == "trunc"sv)
  297. return RoundingMode::Trunc;
  298. VERIFY_NOT_REACHED();
  299. }
  300. StringView rounding_mode_to_string(RoundingMode rounding_mode)
  301. {
  302. switch (rounding_mode) {
  303. case RoundingMode::Ceil:
  304. return "ceil"sv;
  305. case RoundingMode::Expand:
  306. return "expand"sv;
  307. case RoundingMode::Floor:
  308. return "floor"sv;
  309. case RoundingMode::HalfCeil:
  310. return "halfCeil"sv;
  311. case RoundingMode::HalfEven:
  312. return "halfEven"sv;
  313. case RoundingMode::HalfExpand:
  314. return "halfExpand"sv;
  315. case RoundingMode::HalfFloor:
  316. return "halfFloor"sv;
  317. case RoundingMode::HalfTrunc:
  318. return "halfTrunc"sv;
  319. case RoundingMode::Trunc:
  320. return "trunc"sv;
  321. }
  322. VERIFY_NOT_REACHED();
  323. }
  324. static constexpr UNumberFormatRoundingMode icu_rounding_mode(RoundingMode rounding_mode)
  325. {
  326. switch (rounding_mode) {
  327. case RoundingMode::Ceil:
  328. return UNUM_ROUND_CEILING;
  329. case RoundingMode::Expand:
  330. return UNUM_ROUND_UP;
  331. case RoundingMode::Floor:
  332. return UNUM_ROUND_FLOOR;
  333. case RoundingMode::HalfCeil:
  334. return UNUM_ROUND_HALF_CEILING;
  335. case RoundingMode::HalfEven:
  336. return UNUM_ROUND_HALFEVEN;
  337. case RoundingMode::HalfExpand:
  338. return UNUM_ROUND_HALFUP;
  339. case RoundingMode::HalfFloor:
  340. return UNUM_ROUND_HALF_FLOOR;
  341. case RoundingMode::HalfTrunc:
  342. return UNUM_ROUND_HALFDOWN;
  343. case RoundingMode::Trunc:
  344. return UNUM_ROUND_DOWN;
  345. }
  346. VERIFY_NOT_REACHED();
  347. }
  348. TrailingZeroDisplay trailing_zero_display_from_string(StringView trailing_zero_display)
  349. {
  350. if (trailing_zero_display == "auto"sv)
  351. return TrailingZeroDisplay::Auto;
  352. if (trailing_zero_display == "stripIfInteger"sv)
  353. return TrailingZeroDisplay::StripIfInteger;
  354. VERIFY_NOT_REACHED();
  355. }
  356. StringView trailing_zero_display_to_string(TrailingZeroDisplay trailing_zero_display)
  357. {
  358. switch (trailing_zero_display) {
  359. case TrailingZeroDisplay::Auto:
  360. return "auto"sv;
  361. case TrailingZeroDisplay::StripIfInteger:
  362. return "stripIfInteger"sv;
  363. }
  364. VERIFY_NOT_REACHED();
  365. }
  366. static constexpr UNumberTrailingZeroDisplay icu_trailing_zero_display(TrailingZeroDisplay trailing_zero_display)
  367. {
  368. switch (trailing_zero_display) {
  369. case TrailingZeroDisplay::Auto:
  370. return UNUM_TRAILING_ZERO_AUTO;
  371. case TrailingZeroDisplay::StripIfInteger:
  372. return UNUM_TRAILING_ZERO_HIDE_IF_WHOLE;
  373. }
  374. VERIFY_NOT_REACHED();
  375. }
  376. static constexpr UNumberUnitWidth icu_unit_width(Style unit_display)
  377. {
  378. switch (unit_display) {
  379. case Style::Long:
  380. return UNUM_UNIT_WIDTH_FULL_NAME;
  381. case Style::Short:
  382. return UNUM_UNIT_WIDTH_SHORT;
  383. case Style::Narrow:
  384. return UNUM_UNIT_WIDTH_NARROW;
  385. }
  386. VERIFY_NOT_REACHED();
  387. }
  388. static constexpr UPluralType icu_plural_type(PluralForm plural_form)
  389. {
  390. switch (plural_form) {
  391. case PluralForm::Cardinal:
  392. return UPluralType::UPLURAL_TYPE_CARDINAL;
  393. case PluralForm::Ordinal:
  394. return UPluralType::UPLURAL_TYPE_ORDINAL;
  395. }
  396. VERIFY_NOT_REACHED();
  397. }
  398. static void apply_display_options(icu::number::LocalizedNumberFormatter& formatter, DisplayOptions const& display_options)
  399. {
  400. UErrorCode status = U_ZERO_ERROR;
  401. switch (display_options.style) {
  402. case NumberFormatStyle::Decimal:
  403. break;
  404. case NumberFormatStyle::Percent:
  405. formatter = formatter.unit(icu::MeasureUnit::getPercent()).scale(icu::number::Scale::byDouble(100));
  406. break;
  407. case NumberFormatStyle::Currency:
  408. formatter = formatter.unit(icu::CurrencyUnit(icu_string_piece(*display_options.currency), status));
  409. formatter = formatter.unitWidth(icu_currency_display(*display_options.currency_display));
  410. VERIFY(icu_success(status));
  411. break;
  412. case NumberFormatStyle::Unit:
  413. formatter = formatter.unit(icu::MeasureUnit::forIdentifier(icu_string_piece(*display_options.unit), status));
  414. formatter = formatter.unitWidth(icu_unit_width(*display_options.unit_display));
  415. VERIFY(icu_success(status));
  416. break;
  417. }
  418. formatter = formatter.sign(icu_sign_display(display_options.sign_display, display_options.currency_sign));
  419. formatter = formatter.notation(icu_notation(display_options.notation, display_options.compact_display));
  420. formatter = formatter.grouping(icu_grouping_strategy(display_options.grouping));
  421. }
  422. static void apply_rounding_options(icu::number::LocalizedNumberFormatter& formatter, RoundingOptions const& rounding_options)
  423. {
  424. auto precision = icu::number::Precision::unlimited();
  425. if (rounding_options.rounding_increment == 1) {
  426. switch (rounding_options.type) {
  427. case RoundingType::SignificantDigits:
  428. precision = icu::number::Precision::minMaxSignificantDigits(*rounding_options.min_significant_digits, *rounding_options.max_significant_digits);
  429. break;
  430. case RoundingType::FractionDigits:
  431. precision = icu::number::Precision::minMaxFraction(*rounding_options.min_fraction_digits, *rounding_options.max_fraction_digits);
  432. break;
  433. case RoundingType::MorePrecision:
  434. precision = icu::number::Precision::minMaxFraction(*rounding_options.min_fraction_digits, *rounding_options.max_fraction_digits)
  435. .withSignificantDigits(*rounding_options.min_significant_digits, *rounding_options.max_significant_digits, UNUM_ROUNDING_PRIORITY_RELAXED);
  436. break;
  437. case RoundingType::LessPrecision:
  438. precision = icu::number::Precision::minMaxFraction(*rounding_options.min_fraction_digits, *rounding_options.max_fraction_digits)
  439. .withSignificantDigits(*rounding_options.min_significant_digits, *rounding_options.max_significant_digits, UNUM_ROUNDING_PRIORITY_STRICT);
  440. break;
  441. }
  442. } else {
  443. auto mantissa = rounding_options.rounding_increment;
  444. auto magnitude = *rounding_options.max_fraction_digits * -1;
  445. precision = icu::number::Precision::incrementExact(mantissa, static_cast<i16>(magnitude))
  446. .withMinFraction(*rounding_options.min_fraction_digits);
  447. }
  448. formatter = formatter.precision(precision.trailingZeroDisplay(icu_trailing_zero_display(rounding_options.trailing_zero_display)));
  449. formatter = formatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(rounding_options.min_integer_digits));
  450. formatter = formatter.roundingMode(icu_rounding_mode(rounding_options.mode));
  451. }
  452. static constexpr StringView icu_number_format_field_to_string(i32 field, NumberFormat::Value const& value, bool is_unit)
  453. {
  454. switch (field) {
  455. case PartitionRange::LITERAL_FIELD:
  456. return "literal"sv;
  457. case UNUM_INTEGER_FIELD:
  458. if (auto const* number = value.get_pointer<double>()) {
  459. if (isnan(*number))
  460. return "nan"sv;
  461. if (isinf(*number))
  462. return "infinity"sv;
  463. }
  464. return "integer"sv;
  465. case UNUM_FRACTION_FIELD:
  466. return "fraction"sv;
  467. case UNUM_DECIMAL_SEPARATOR_FIELD:
  468. return "decimal"sv;
  469. case UNUM_EXPONENT_SYMBOL_FIELD:
  470. return "exponentSeparator"sv;
  471. case UNUM_EXPONENT_SIGN_FIELD:
  472. return "exponentMinusSign"sv;
  473. case UNUM_EXPONENT_FIELD:
  474. return "exponentInteger"sv;
  475. case UNUM_GROUPING_SEPARATOR_FIELD:
  476. return "group"sv;
  477. case UNUM_CURRENCY_FIELD:
  478. return "currency"sv;
  479. case UNUM_PERCENT_FIELD:
  480. return is_unit ? "unit"sv : "percentSign"sv;
  481. case UNUM_SIGN_FIELD: {
  482. auto is_negative = value.visit(
  483. [&](double number) { return signbit(number); },
  484. [&](String const& number) { return number.starts_with('-'); });
  485. return is_negative ? "minusSign"sv : "plusSign"sv;
  486. }
  487. case UNUM_MEASURE_UNIT_FIELD:
  488. return "unit"sv;
  489. case UNUM_COMPACT_FIELD:
  490. return "compact"sv;
  491. case UNUM_APPROXIMATELY_SIGN_FIELD:
  492. return "approximatelySign"sv;
  493. }
  494. VERIFY_NOT_REACHED();
  495. }
  496. // ICU will give us overlapping partitions, e.g. for the formatted result "1,234", we will get the following parts:
  497. //
  498. // part="," type=group start=1 end=2
  499. // part="1,234" type=integer start=0 end=5
  500. //
  501. // We need to massage these partitions into non-overlapping parts for ECMA-402:
  502. //
  503. // part="1" type=integer start=0 end=1
  504. // part="," type=group start=1 end=2
  505. // part="234" type=integer start=2 end=5
  506. static void flatten_partitions(Vector<PartitionRange>& partitions)
  507. {
  508. if (partitions.size() <= 1)
  509. return;
  510. quick_sort(partitions);
  511. auto subtract_range = [&](auto const& first, auto const& second) -> Vector<PartitionRange> {
  512. if (second.start > first.end || first.start > second.end)
  513. return { first };
  514. Vector<PartitionRange> result;
  515. if (second.start > first.start)
  516. result.empend(first.field, first.start, second.start);
  517. if (second.end < first.end)
  518. result.empend(first.field, second.end, first.end);
  519. return result;
  520. };
  521. for (size_t i = 0; i < partitions.size(); ++i) {
  522. for (size_t j = i + 1; j < partitions.size(); ++j) {
  523. auto& first = partitions[i];
  524. auto& second = partitions[j];
  525. auto result = subtract_range(first, second);
  526. if (result.is_empty()) {
  527. partitions.remove(i);
  528. --i;
  529. break;
  530. }
  531. first = result[0];
  532. if (result.size() == 2)
  533. partitions.insert(i + 1, result[1]);
  534. }
  535. }
  536. quick_sort(partitions);
  537. }
  538. class NumberFormatImpl : public NumberFormat {
  539. public:
  540. NumberFormatImpl(icu::Locale& locale, icu::number::LocalizedNumberFormatter formatter, bool is_unit)
  541. : m_locale(locale)
  542. , m_formatter(move(formatter))
  543. , m_is_unit(is_unit)
  544. {
  545. }
  546. virtual ~NumberFormatImpl() override = default;
  547. virtual String format(Value const& value) const override
  548. {
  549. UErrorCode status = U_ZERO_ERROR;
  550. auto formatted = format_impl(value);
  551. if (!formatted.has_value())
  552. return {};
  553. auto result = formatted->toTempString(status);
  554. if (icu_failure(status))
  555. return {};
  556. return icu_string_to_string(result);
  557. }
  558. virtual String format_to_decimal(Value const& value) const override
  559. {
  560. UErrorCode status = U_ZERO_ERROR;
  561. auto formatted = format_impl(value);
  562. if (!formatted.has_value())
  563. return {};
  564. auto result = formatted->toDecimalNumber<StringBuilder>(status);
  565. if (icu_failure(status))
  566. return {};
  567. return MUST(result.to_string());
  568. }
  569. virtual Vector<Partition> format_to_parts(Value const& value) const override
  570. {
  571. auto formatted = format_impl(value);
  572. if (!formatted.has_value())
  573. return {};
  574. return format_to_parts_impl(formatted, value, value);
  575. }
  576. virtual String format_range(Value const& start, Value const& end) const override
  577. {
  578. UErrorCode status = U_ZERO_ERROR;
  579. auto formatted = format_range_impl(start, end);
  580. if (!formatted.has_value())
  581. return {};
  582. auto result = formatted->toTempString(status);
  583. if (icu_failure(status))
  584. return {};
  585. return icu_string_to_string(result);
  586. }
  587. virtual Vector<Partition> format_range_to_parts(Value const& start, Value const& end) const override
  588. {
  589. auto formatted = format_range_impl(start, end);
  590. if (!formatted.has_value())
  591. return {};
  592. return format_to_parts_impl(formatted, start, end);
  593. }
  594. virtual void create_plural_rules(PluralForm plural_form) override
  595. {
  596. UErrorCode status = U_ZERO_ERROR;
  597. VERIFY(!m_plural_rules);
  598. m_plural_rules = adopt_own(*icu::PluralRules::forLocale(m_locale, icu_plural_type(plural_form), status));
  599. VERIFY(icu_success(status));
  600. }
  601. virtual PluralCategory select_plural(double value) const override
  602. {
  603. UErrorCode status = U_ZERO_ERROR;
  604. VERIFY(m_plural_rules);
  605. auto formatted = format_impl(value);
  606. if (!formatted.has_value())
  607. return PluralCategory::Other;
  608. auto result = m_plural_rules->select(*formatted, status);
  609. if (icu_failure(status))
  610. return PluralCategory::Other;
  611. return plural_category_from_string(icu_string_to_string(result));
  612. }
  613. virtual PluralCategory select_plural_range(double start, double end) const override
  614. {
  615. UErrorCode status = U_ZERO_ERROR;
  616. VERIFY(m_plural_rules);
  617. auto formatted = format_range_impl(start, end);
  618. if (!formatted.has_value())
  619. return PluralCategory::Other;
  620. auto [formatted_start, formatted_end] = formatted->getDecimalNumbers<StringBuilder>(status);
  621. if (icu_failure(status))
  622. return PluralCategory::Other;
  623. if (formatted_start.string_view() == formatted_end.string_view())
  624. return select_plural(start);
  625. auto result = m_plural_rules->select(*formatted, status);
  626. if (icu_failure(status))
  627. return PluralCategory::Other;
  628. return plural_category_from_string(icu_string_to_string(result));
  629. }
  630. virtual Vector<PluralCategory> available_plural_categories() const override
  631. {
  632. UErrorCode status = U_ZERO_ERROR;
  633. VERIFY(m_plural_rules);
  634. auto keywords = adopt_own_if_nonnull(m_plural_rules->getKeywords(status));
  635. if (icu_failure(status))
  636. return {};
  637. Vector<PluralCategory> result;
  638. while (true) {
  639. i32 length = 0;
  640. auto const* category = keywords->next(&length, status);
  641. if (icu_failure(status) || category == nullptr)
  642. break;
  643. result.append(plural_category_from_string({ category, static_cast<size_t>(length) }));
  644. }
  645. return result;
  646. }
  647. private:
  648. static icu::Formattable value_to_formattable(Value const& value)
  649. {
  650. UErrorCode status = U_ZERO_ERROR;
  651. auto formattable = value.visit(
  652. [&](double number) { return icu::Formattable { number }; },
  653. [&](String const& number) { return icu::Formattable(icu_string_piece(number), status); });
  654. VERIFY(icu_success(status));
  655. return formattable;
  656. }
  657. Optional<icu::number::FormattedNumber> format_impl(Value const& value) const
  658. {
  659. UErrorCode status = U_ZERO_ERROR;
  660. auto formatted = value.visit(
  661. [&](double number) {
  662. return m_formatter.formatDouble(number, status);
  663. },
  664. [&](String const& number) {
  665. return m_formatter.formatDecimal(icu_string_piece(number), status);
  666. });
  667. if (icu_failure(status))
  668. return {};
  669. return formatted;
  670. }
  671. Optional<icu::number::FormattedNumberRange> format_range_impl(Value const& start, Value const& end) const
  672. {
  673. UErrorCode status = U_ZERO_ERROR;
  674. if (!m_range_formatter.has_value()) {
  675. auto skeleton = icu::number::NumberFormatter::forSkeleton(m_formatter.toSkeleton(status), status);
  676. if (icu_failure(status))
  677. return {};
  678. auto formatter = icu::number::UnlocalizedNumberRangeFormatter().numberFormatterBoth(move(skeleton)).locale(m_locale);
  679. if (icu_failure(status))
  680. return {};
  681. m_range_formatter = move(formatter);
  682. }
  683. auto formattable_start = value_to_formattable(start);
  684. auto formattable_end = value_to_formattable(end);
  685. auto formatted = m_range_formatter->formatFormattableRange(formattable_start, formattable_end, status);
  686. if (icu_failure(status))
  687. return {};
  688. return formatted;
  689. }
  690. template<typename Formatted>
  691. Vector<Partition> format_to_parts_impl(Formatted const& formatted, Value const& start, Value const& end) const
  692. {
  693. UErrorCode status = U_ZERO_ERROR;
  694. auto formatted_number = formatted->toTempString(status);
  695. if (icu_failure(status))
  696. return {};
  697. Vector<PartitionRange> ranges;
  698. ranges.empend(PartitionRange::LITERAL_FIELD, 0, formatted_number.length());
  699. icu::ConstrainedFieldPosition position;
  700. Optional<PartitionRange> start_range;
  701. Optional<PartitionRange> end_range;
  702. while (static_cast<bool>(formatted->nextPosition(position, status)) && icu_success(status)) {
  703. if (position.getCategory() == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) {
  704. auto& range = position.getField() == 0 ? start_range : end_range;
  705. range = PartitionRange { position.getField(), position.getStart(), position.getLimit() };
  706. } else {
  707. ranges.empend(position.getField(), position.getStart(), position.getLimit());
  708. }
  709. }
  710. flatten_partitions(ranges);
  711. auto apply_to_partition = [&](Partition& partition, auto field, auto index) {
  712. if (start_range.has_value() && start_range->contains(index)) {
  713. partition.type = icu_number_format_field_to_string(field, start, m_is_unit);
  714. partition.source = "startRange"sv;
  715. return;
  716. }
  717. if (end_range.has_value() && end_range->contains(index)) {
  718. partition.type = icu_number_format_field_to_string(field, end, m_is_unit);
  719. partition.source = "endRange"sv;
  720. return;
  721. }
  722. partition.type = icu_number_format_field_to_string(field, end, m_is_unit);
  723. partition.source = "shared"sv;
  724. };
  725. Vector<Partition> result;
  726. result.ensure_capacity(ranges.size());
  727. for (auto const& range : ranges) {
  728. auto value = formatted_number.tempSubStringBetween(range.start, range.end);
  729. Partition partition;
  730. partition.value = icu_string_to_string(value);
  731. apply_to_partition(partition, range.field, range.start);
  732. result.unchecked_append(move(partition));
  733. }
  734. return result;
  735. }
  736. icu::Locale& m_locale;
  737. icu::number::LocalizedNumberFormatter m_formatter;
  738. mutable Optional<icu::number::LocalizedNumberRangeFormatter> m_range_formatter;
  739. OwnPtr<icu::PluralRules> m_plural_rules;
  740. bool m_is_unit { false };
  741. };
  742. NonnullOwnPtr<NumberFormat> NumberFormat::create(
  743. StringView locale,
  744. StringView numbering_system,
  745. DisplayOptions const& display_options,
  746. RoundingOptions const& rounding_options)
  747. {
  748. UErrorCode status = U_ZERO_ERROR;
  749. auto locale_data = LocaleData::for_locale(locale);
  750. VERIFY(locale_data.has_value());
  751. auto formatter = icu::number::NumberFormatter::withLocale(locale_data->locale());
  752. apply_display_options(formatter, display_options);
  753. apply_rounding_options(formatter, rounding_options);
  754. if (!numbering_system.is_empty()) {
  755. if (auto* symbols = icu::NumberingSystem::createInstanceByName(ByteString(numbering_system).characters(), status); symbols && icu_success(status))
  756. formatter = formatter.adoptSymbols(symbols);
  757. }
  758. bool is_unit = display_options.style == NumberFormatStyle::Unit;
  759. return adopt_own(*new NumberFormatImpl(locale_data->locale(), move(formatter), is_unit));
  760. }
  761. }