LibJS: Use decimal compact patterns for currency style sub-patterns

When formatting a currency style pattern with compact notation, we were
(trying to) doubly insert the currency symbol into the formatted string.
We would first look up the currency pattern in GetNumberFormatPattern
(for the en locale, this is "¤#,##0.00", which our generator transforms
to "{currency}{number}").

When we hit the "{number}" field, NumberFormat will do a second lookup
for the compact pattern to use for the number being formatted. By using
the currency compact patterns, we receive a second pattern that also has
the currency symbol (for the en locale, if formatting the number 1000,
this is "¤0K", which our generator transforms to
"{currency}{number}{compactIdentifier:0}". This second lookup is not
supposed to have currency symbols (or any other symbols), thus we hit a
VERIFY_NOT_REACHED().

Instead, we are meant to use the decimal compact pattern, and allow the
currency symbol to be handled by only the outer currency pattern.
This commit is contained in:
Timothy Flynn 2023-09-04 09:15:02 -04:00 committed by Andreas Kling
parent e3f693ca7c
commit ca0d926036
Notes: sideshowbarker 2024-07-17 08:35:21 +09:00
2 changed files with 144 additions and 8 deletions

View File

@ -1634,15 +1634,11 @@ int compute_exponent_for_magnitude(NumberFormat& number_format, int magnitude)
// b. Let exponent be an implementation- and locale-dependent (ILD) integer by which to scale a number of the given magnitude in compact notation for the current locale.
// c. Return exponent.
Vector<::Locale::NumberFormat> format_rules;
if (number_format.style() == NumberFormat::Style::Currency)
format_rules = ::Locale::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), ::Locale::CompactNumberFormatType::CurrencyShort);
else if (number_format.compact_display() == NumberFormat::CompactDisplay::Long)
format_rules = ::Locale::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), ::Locale::CompactNumberFormatType::DecimalLong);
else
format_rules = ::Locale::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), ::Locale::CompactNumberFormatType::DecimalShort);
auto compact_format_type = number_format.compact_display() == NumberFormat::CompactDisplay::Short || number_format.style() == NumberFormat::Style::Currency
? ::Locale::CompactNumberFormatType::DecimalShort
: ::Locale::CompactNumberFormatType::DecimalLong;
auto format_rules = ::Locale::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), compact_format_type);
::Locale::NumberFormat const* best_number_format = nullptr;
for (auto const& format_rule : format_rules) {

View File

@ -1298,6 +1298,146 @@ describe("style=currency", () => {
expect(ar2.format(1.23)).toBe("1.23 دولار أمريكي");
});
test("notation=compact, compactDisplay=long", () => {
const en = new Intl.NumberFormat("en", {
style: "currency",
currency: "USD",
notation: "compact",
compactDisplay: "long",
});
expect(en.format(1)).toBe("$1");
expect(en.format(1200)).toBe("$1.2K");
expect(en.format(1290)).toBe("$1.3K");
expect(en.format(12000)).toBe("$12K");
expect(en.format(12900)).toBe("$13K");
expect(en.format(1200000)).toBe("$1.2M");
expect(en.format(1290000)).toBe("$1.3M");
expect(en.format(12000000)).toBe("$12M");
expect(en.format(12900000)).toBe("$13M");
const ar = new Intl.NumberFormat("ar", {
style: "currency",
currency: "USD",
notation: "compact",
compactDisplay: "long",
});
expect(ar.format(1)).toBe("١ US$");
expect(ar.format(1200)).toBe("١٫٢ ألف US$");
expect(ar.format(1290)).toBe("١٫٣ ألف US$");
expect(ar.format(12000)).toBe("١٢ ألف US$");
expect(ar.format(12900)).toBe("١٣ ألف US$");
expect(ar.format(1200000)).toBe("١٫٢ مليون US$");
expect(ar.format(1290000)).toBe("١٫٣ مليون US$");
expect(ar.format(12000000)).toBe("١٢ مليون US$");
expect(ar.format(12900000)).toBe("١٣ مليون US$");
const ja = new Intl.NumberFormat("ja", {
style: "currency",
currency: "JPY",
notation: "compact",
compactDisplay: "long",
});
expect(ja.format(1)).toBe("¥1");
expect(ja.format(1200)).toBe("¥1200");
expect(ja.format(1290)).toBe("¥1290");
expect(ja.format(12000)).toBe("¥1.2万");
expect(ja.format(12900)).toBe("¥1.3万");
expect(ja.format(1200000)).toBe("¥120万");
expect(ja.format(1290000)).toBe("¥129万");
expect(ja.format(12000000)).toBe("¥1200万");
expect(ja.format(12900000)).toBe("¥1290万");
expect(ja.format(120000000)).toBe("¥1.2億");
expect(ja.format(129000000)).toBe("¥1.3億");
expect(ja.format(12000000000)).toBe("¥120億");
expect(ja.format(12900000000)).toBe("¥129億");
const de = new Intl.NumberFormat("de", {
style: "currency",
currency: "EUR",
notation: "compact",
compactDisplay: "long",
});
expect(de.format(1)).toBe("1 €");
expect(de.format(1200)).toBe("1200 €");
expect(de.format(1290)).toBe("1290 €");
expect(de.format(12000)).toBe("12.000 €");
expect(de.format(12900)).toBe("12.900 €");
expect(de.format(1200000)).toBe("1,2 Mio. €");
expect(de.format(1290000)).toBe("1,3 Mio. €");
expect(de.format(12000000)).toBe("12 Mio. €");
expect(de.format(12900000)).toBe("13 Mio. €");
});
test("notation=compact, compactDisplay=short", () => {
const en = new Intl.NumberFormat("en", {
style: "currency",
currency: "USD",
notation: "compact",
compactDisplay: "short",
});
expect(en.format(1)).toBe("$1");
expect(en.format(1200)).toBe("$1.2K");
expect(en.format(1290)).toBe("$1.3K");
expect(en.format(12000)).toBe("$12K");
expect(en.format(12900)).toBe("$13K");
expect(en.format(1200000)).toBe("$1.2M");
expect(en.format(1290000)).toBe("$1.3M");
expect(en.format(12000000)).toBe("$12M");
expect(en.format(12900000)).toBe("$13M");
const ar = new Intl.NumberFormat("ar", {
style: "currency",
currency: "USD",
notation: "compact",
compactDisplay: "short",
});
expect(ar.format(1)).toBe("١ US$");
expect(ar.format(1200)).toBe("١٫٢ ألف US$");
expect(ar.format(1290)).toBe("١٫٣ ألف US$");
expect(ar.format(12000)).toBe("١٢ ألف US$");
expect(ar.format(12900)).toBe("١٣ ألف US$");
expect(ar.format(1200000)).toBe("١٫٢ مليون US$");
expect(ar.format(1290000)).toBe("١٫٣ مليون US$");
expect(ar.format(12000000)).toBe("١٢ مليون US$");
expect(ar.format(12900000)).toBe("١٣ مليون US$");
const ja = new Intl.NumberFormat("ja", {
style: "currency",
currency: "JPY",
notation: "compact",
compactDisplay: "short",
});
expect(ja.format(1)).toBe("¥1");
expect(ja.format(1200)).toBe("¥1200");
expect(ja.format(1290)).toBe("¥1290");
expect(ja.format(12000)).toBe("¥1.2万");
expect(ja.format(12900)).toBe("¥1.3万");
expect(ja.format(1200000)).toBe("¥120万");
expect(ja.format(1290000)).toBe("¥129万");
expect(ja.format(12000000)).toBe("¥1200万");
expect(ja.format(12900000)).toBe("¥1290万");
expect(ja.format(120000000)).toBe("¥1.2億");
expect(ja.format(129000000)).toBe("¥1.3億");
expect(ja.format(12000000000)).toBe("¥120億");
expect(ja.format(12900000000)).toBe("¥129億");
const de = new Intl.NumberFormat("de", {
style: "currency",
currency: "EUR",
notation: "compact",
compactDisplay: "short",
});
expect(de.format(1)).toBe("1 €");
expect(de.format(1200)).toBe("1200 €");
expect(de.format(1290)).toBe("1290 €");
expect(de.format(12000)).toBe("12.000 €");
expect(de.format(12900)).toBe("12.900 €");
expect(de.format(1200000)).toBe("1,2 Mio. €");
expect(de.format(1290000)).toBe("1,3 Mio. €");
expect(de.format(12000000)).toBe("12 Mio. €");
expect(de.format(12900000)).toBe("13 Mio. €");
});
test("signDisplay=never", () => {
const en1 = new Intl.NumberFormat("en", {
style: "currency",