NumberFormat.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  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 <LibLocale/ICU.h>
  11. #include <LibLocale/Locale.h>
  12. #include <LibLocale/NumberFormat.h>
  13. #include <LibUnicode/CharacterTypes.h>
  14. #include <math.h>
  15. #include <unicode/numberformatter.h>
  16. #include <unicode/numberrangeformatter.h>
  17. #include <unicode/plurrule.h>
  18. namespace Locale {
  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. // ICU does not contain a field enumeration for "literal" partitions. Define a custom field so that we may provide a
  453. // type for those partitions.
  454. static constexpr i32 LITERAL_FIELD = -1;
  455. static constexpr StringView icu_number_format_field_to_string(i32 field, NumberFormat::Value const& value, bool is_unit)
  456. {
  457. switch (field) {
  458. case LITERAL_FIELD:
  459. return "literal"sv;
  460. case UNUM_INTEGER_FIELD:
  461. if (auto const* number = value.get_pointer<double>()) {
  462. if (isnan(*number))
  463. return "nan"sv;
  464. if (isinf(*number))
  465. return "infinity"sv;
  466. }
  467. return "integer"sv;
  468. case UNUM_FRACTION_FIELD:
  469. return "fraction"sv;
  470. case UNUM_DECIMAL_SEPARATOR_FIELD:
  471. return "decimal"sv;
  472. case UNUM_EXPONENT_SYMBOL_FIELD:
  473. return "exponentSeparator"sv;
  474. case UNUM_EXPONENT_SIGN_FIELD:
  475. return "exponentMinusSign"sv;
  476. case UNUM_EXPONENT_FIELD:
  477. return "exponentInteger"sv;
  478. case UNUM_GROUPING_SEPARATOR_FIELD:
  479. return "group"sv;
  480. case UNUM_CURRENCY_FIELD:
  481. return "currency"sv;
  482. case UNUM_PERCENT_FIELD:
  483. return is_unit ? "unit"sv : "percentSign"sv;
  484. case UNUM_SIGN_FIELD: {
  485. auto is_negative = value.visit(
  486. [&](double number) { return signbit(number); },
  487. [&](String const& number) { return number.starts_with('-'); });
  488. return is_negative ? "minusSign"sv : "plusSign"sv;
  489. }
  490. case UNUM_MEASURE_UNIT_FIELD:
  491. return "unit"sv;
  492. case UNUM_COMPACT_FIELD:
  493. return "compact"sv;
  494. case UNUM_APPROXIMATELY_SIGN_FIELD:
  495. return "approximatelySign"sv;
  496. }
  497. VERIFY_NOT_REACHED();
  498. }
  499. struct Range {
  500. constexpr bool contains(i32 position) const
  501. {
  502. return start <= position && position < end;
  503. }
  504. constexpr bool operator<(Range const& other) const
  505. {
  506. if (start < other.start)
  507. return true;
  508. if (start == other.start)
  509. return end > other.end;
  510. return false;
  511. }
  512. i32 field { LITERAL_FIELD };
  513. i32 start { 0 };
  514. i32 end { 0 };
  515. };
  516. // ICU will give us overlapping partitions, e.g. for the formatted result "1,234", we will get the following parts:
  517. //
  518. // part="," type=group start=1 end=2
  519. // part="1,234" type=integer start=0 end=5
  520. //
  521. // We need to massage these partitions into non-overlapping parts for ECMA-402:
  522. //
  523. // part="1" type=integer start=0 end=1
  524. // part="," type=group start=1 end=2
  525. // part="234" type=integer start=2 end=5
  526. static void flatten_partitions(Vector<Range>& partitions)
  527. {
  528. if (partitions.size() <= 1)
  529. return;
  530. quick_sort(partitions);
  531. auto subtract_range = [&](auto const& first, auto const& second) -> Vector<Range> {
  532. if (second.start > first.end || first.start > second.end)
  533. return { first };
  534. Vector<Range> result;
  535. if (second.start > first.start)
  536. result.empend(first.field, first.start, second.start);
  537. if (second.end < first.end)
  538. result.empend(first.field, second.end, first.end);
  539. return result;
  540. };
  541. for (size_t i = 0; i < partitions.size(); ++i) {
  542. for (size_t j = i + 1; j < partitions.size(); ++j) {
  543. auto& first = partitions[i];
  544. auto& second = partitions[j];
  545. auto result = subtract_range(first, second);
  546. if (result.is_empty()) {
  547. partitions.remove(i);
  548. --i;
  549. break;
  550. }
  551. first = result[0];
  552. if (result.size() == 2)
  553. partitions.insert(i + 1, result[1]);
  554. }
  555. }
  556. quick_sort(partitions);
  557. }
  558. class NumberFormatImpl : public NumberFormat {
  559. public:
  560. NumberFormatImpl(icu::Locale& locale, icu::number::LocalizedNumberFormatter formatter, bool is_unit)
  561. : m_locale(locale)
  562. , m_formatter(move(formatter))
  563. , m_is_unit(is_unit)
  564. {
  565. }
  566. virtual ~NumberFormatImpl() override = default;
  567. virtual String format(Value const& value) const override
  568. {
  569. UErrorCode status = U_ZERO_ERROR;
  570. auto formatted = format_impl(value);
  571. if (!formatted.has_value())
  572. return {};
  573. auto result = formatted->toTempString(status);
  574. if (icu_failure(status))
  575. return {};
  576. return icu_string_to_string(result);
  577. }
  578. virtual String format_to_decimal(Value const& value) const override
  579. {
  580. UErrorCode status = U_ZERO_ERROR;
  581. auto formatted = format_impl(value);
  582. if (!formatted.has_value())
  583. return {};
  584. auto result = formatted->toDecimalNumber<StringBuilder>(status);
  585. if (icu_failure(status))
  586. return {};
  587. return MUST(result.to_string());
  588. }
  589. virtual Vector<Partition> format_to_parts(Value const& value) const override
  590. {
  591. auto formatted = format_impl(value);
  592. if (!formatted.has_value())
  593. return {};
  594. return format_to_parts_impl(formatted, value, value);
  595. }
  596. virtual String format_range(Value const& start, Value const& end) const override
  597. {
  598. UErrorCode status = U_ZERO_ERROR;
  599. auto formatted = format_range_impl(start, end);
  600. if (!formatted.has_value())
  601. return {};
  602. auto result = formatted->toTempString(status);
  603. if (icu_failure(status))
  604. return {};
  605. return icu_string_to_string(result);
  606. }
  607. virtual Vector<Partition> format_range_to_parts(Value const& start, Value const& end) const override
  608. {
  609. auto formatted = format_range_impl(start, end);
  610. if (!formatted.has_value())
  611. return {};
  612. return format_to_parts_impl(formatted, start, end);
  613. }
  614. virtual void create_plural_rules(PluralForm plural_form) override
  615. {
  616. UErrorCode status = U_ZERO_ERROR;
  617. VERIFY(!m_plural_rules);
  618. m_plural_rules = adopt_own(*icu::PluralRules::forLocale(m_locale, icu_plural_type(plural_form), status));
  619. VERIFY(icu_success(status));
  620. }
  621. virtual PluralCategory select_plural(double value) const override
  622. {
  623. UErrorCode status = U_ZERO_ERROR;
  624. VERIFY(m_plural_rules);
  625. auto formatted = format_impl(value);
  626. if (!formatted.has_value())
  627. return PluralCategory::Other;
  628. auto result = m_plural_rules->select(*formatted, status);
  629. if (icu_failure(status))
  630. return PluralCategory::Other;
  631. return plural_category_from_string(icu_string_to_string(result));
  632. }
  633. virtual PluralCategory select_plural_range(double start, double end) const override
  634. {
  635. UErrorCode status = U_ZERO_ERROR;
  636. VERIFY(m_plural_rules);
  637. auto formatted = format_range_impl(start, end);
  638. if (!formatted.has_value())
  639. return PluralCategory::Other;
  640. auto [formatted_start, formatted_end] = formatted->getDecimalNumbers<StringBuilder>(status);
  641. if (icu_failure(status))
  642. return PluralCategory::Other;
  643. if (formatted_start.string_view() == formatted_end.string_view())
  644. return select_plural(start);
  645. auto result = m_plural_rules->select(*formatted, status);
  646. if (icu_failure(status))
  647. return PluralCategory::Other;
  648. return plural_category_from_string(icu_string_to_string(result));
  649. }
  650. virtual Vector<PluralCategory> available_plural_categories() const override
  651. {
  652. UErrorCode status = U_ZERO_ERROR;
  653. VERIFY(m_plural_rules);
  654. auto keywords = adopt_own_if_nonnull(m_plural_rules->getKeywords(status));
  655. if (icu_failure(status))
  656. return {};
  657. Vector<PluralCategory> result;
  658. while (true) {
  659. i32 length = 0;
  660. auto const* category = keywords->next(&length, status);
  661. if (icu_failure(status) || category == nullptr)
  662. break;
  663. result.append(plural_category_from_string({ category, static_cast<size_t>(length) }));
  664. }
  665. return result;
  666. }
  667. private:
  668. static icu::Formattable value_to_formattable(Value const& value)
  669. {
  670. UErrorCode status = U_ZERO_ERROR;
  671. auto formattable = value.visit(
  672. [&](double number) { return icu::Formattable { number }; },
  673. [&](String const& number) { return icu::Formattable(icu_string_piece(number), status); });
  674. VERIFY(icu_success(status));
  675. return formattable;
  676. }
  677. Optional<icu::number::FormattedNumber> format_impl(Value const& value) const
  678. {
  679. UErrorCode status = U_ZERO_ERROR;
  680. auto formatted = value.visit(
  681. [&](double number) {
  682. return m_formatter.formatDouble(number, status);
  683. },
  684. [&](String const& number) {
  685. return m_formatter.formatDecimal(icu_string_piece(number), status);
  686. });
  687. if (icu_failure(status))
  688. return {};
  689. return formatted;
  690. }
  691. Optional<icu::number::FormattedNumberRange> format_range_impl(Value const& start, Value const& end) const
  692. {
  693. UErrorCode status = U_ZERO_ERROR;
  694. if (!m_range_formatter.has_value()) {
  695. auto skeleton = icu::number::NumberFormatter::forSkeleton(m_formatter.toSkeleton(status), status);
  696. if (icu_failure(status))
  697. return {};
  698. auto formatter = icu::number::UnlocalizedNumberRangeFormatter().numberFormatterBoth(move(skeleton)).locale(m_locale);
  699. if (icu_failure(status))
  700. return {};
  701. m_range_formatter = move(formatter);
  702. }
  703. auto formattable_start = value_to_formattable(start);
  704. auto formattable_end = value_to_formattable(end);
  705. auto formatted = m_range_formatter->formatFormattableRange(formattable_start, formattable_end, status);
  706. if (icu_failure(status))
  707. return {};
  708. return formatted;
  709. }
  710. template<typename Formatted>
  711. Vector<Partition> format_to_parts_impl(Formatted const& formatted, Value const& start, Value const& end) const
  712. {
  713. UErrorCode status = U_ZERO_ERROR;
  714. auto formatted_number = formatted->toTempString(status);
  715. if (icu_failure(status))
  716. return {};
  717. Vector<Range> ranges;
  718. ranges.empend(LITERAL_FIELD, 0, formatted_number.length());
  719. icu::ConstrainedFieldPosition position;
  720. Optional<Range> start_range;
  721. Optional<Range> end_range;
  722. while (static_cast<bool>(formatted->nextPosition(position, status)) && icu_success(status)) {
  723. if (position.getCategory() == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) {
  724. auto& range = position.getField() == 0 ? start_range : end_range;
  725. range = Range { position.getField(), position.getStart(), position.getLimit() };
  726. } else {
  727. ranges.empend(position.getField(), position.getStart(), position.getLimit());
  728. }
  729. }
  730. flatten_partitions(ranges);
  731. auto apply_to_partition = [&](Partition& partition, auto field, auto index) {
  732. if (start_range.has_value() && start_range->contains(index)) {
  733. partition.type = icu_number_format_field_to_string(field, start, m_is_unit);
  734. partition.source = "startRange"sv;
  735. return;
  736. }
  737. if (end_range.has_value() && end_range->contains(index)) {
  738. partition.type = icu_number_format_field_to_string(field, end, m_is_unit);
  739. partition.source = "endRange"sv;
  740. return;
  741. }
  742. partition.type = icu_number_format_field_to_string(field, end, m_is_unit);
  743. partition.source = "shared"sv;
  744. };
  745. Vector<Partition> result;
  746. result.ensure_capacity(ranges.size());
  747. for (auto const& range : ranges) {
  748. auto value = formatted_number.tempSubStringBetween(range.start, range.end);
  749. Partition partition;
  750. partition.value = icu_string_to_string(value);
  751. apply_to_partition(partition, range.field, range.start);
  752. result.unchecked_append(move(partition));
  753. }
  754. return result;
  755. }
  756. icu::Locale& m_locale;
  757. icu::number::LocalizedNumberFormatter m_formatter;
  758. mutable Optional<icu::number::LocalizedNumberRangeFormatter> m_range_formatter;
  759. OwnPtr<icu::PluralRules> m_plural_rules;
  760. bool m_is_unit { false };
  761. };
  762. NonnullOwnPtr<NumberFormat> NumberFormat::create(
  763. StringView locale,
  764. StringView numbering_system,
  765. DisplayOptions const& display_options,
  766. RoundingOptions const& rounding_options)
  767. {
  768. UErrorCode status = U_ZERO_ERROR;
  769. auto locale_data = LocaleData::for_locale(locale);
  770. VERIFY(locale_data.has_value());
  771. auto formatter = icu::number::NumberFormatter::withLocale(locale_data->locale());
  772. apply_display_options(formatter, display_options);
  773. apply_rounding_options(formatter, rounding_options);
  774. if (!numbering_system.is_empty()) {
  775. if (auto* symbols = icu::NumberingSystem::createInstanceByName(ByteString(numbering_system).characters(), status); symbols && icu_success(status))
  776. formatter = formatter.adoptSymbols(symbols);
  777. }
  778. bool is_unit = display_options.style == NumberFormatStyle::Unit;
  779. return adopt_own(*new NumberFormatImpl(locale_data->locale(), move(formatter), is_unit));
  780. }
  781. }