mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 15:47:45 +00:00
LibJS+LibUnicode: Fully implement currency number formatting
Currencies are a bit strange; the layout of currency data in the CLDR is not particularly compatible with what ECMA-402 expects. For example, the currency format in the "en" and "ar" locales for the Latin script are: en: "¤#,##0.00" ar: "¤\u00A0#,##0.00" Note how the "ar" locale has a non-breaking space after the currency symbol (¤), but "en" does not. This does not mean that this space will appear in the "ar"-formatted string, nor does it mean that a space won't appear in the "en"-formatted string. This is a runtime decision based on the currency display chosen by the user ("$" vs. "USD" vs. "US dollar") and other rules in the Unicode TR-35 spec. ECMA-402 shies away from the nuances here with "implementation-defined" steps. LibUnicode will store the data parsed from the CLDR however it is presented; making decisions about spacing, etc. will occur at runtime based on user input.
This commit is contained in:
parent
e9493a2cd5
commit
a701ed52fc
6 changed files with 454 additions and 30 deletions
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Intl/NumberFormat.h>
|
||||
#include <LibJS/Runtime/Intl/NumberFormatFunction.h>
|
||||
|
@ -596,7 +595,7 @@ Vector<PatternPartition> partition_number_pattern(NumberFormat& number_format, d
|
|||
Vector<PatternPartition> result;
|
||||
|
||||
// 7. Let patternParts be PartitionPattern(pattern).
|
||||
auto pattern_parts = partition_pattern(*pattern);
|
||||
auto pattern_parts = pattern->visit([](auto const& p) { return partition_pattern(p); });
|
||||
|
||||
// 8. For each Record { [[Type]], [[Value]] } patternPart of patternParts, do
|
||||
for (auto& pattern_part : pattern_parts) {
|
||||
|
@ -662,32 +661,11 @@ Vector<PatternPartition> partition_number_pattern(NumberFormat& number_format, d
|
|||
}
|
||||
|
||||
// i. Else if p is equal to "currencyCode" and numberFormat.[[Style]] is "currency", then
|
||||
else if ((part == "currencyCode"sv) && (number_format.style() == NumberFormat::Style::Currency)) {
|
||||
// i. Let currency be numberFormat.[[Currency]].
|
||||
// ii. Let cd be currency.
|
||||
// iii. Append a new Record { [[Type]]: "currency", [[Value]]: cd } as the last element of result.
|
||||
result.append({ part, number_format.currency() });
|
||||
}
|
||||
|
||||
// j. Else if p is equal to "currencyPrefix" and numberFormat.[[Style]] is "currency", then
|
||||
else if ((part == "currencyPrefix"sv) && (number_format.style() == NumberFormat::Style::Currency)) {
|
||||
// i. Let currency be numberFormat.[[Currency]].
|
||||
// ii. Let currencyDisplay be numberFormat.[[CurrencyDisplay]].
|
||||
// iii. Let cd be an ILD String value representing currency before x in currencyDisplay form, which may depend on x in languages having different plural forms.
|
||||
// iv. Append a new Record { [[Type]]: "currency", [[Value]]: cd } as the last element of result.
|
||||
|
||||
// FIXME: LibUnicode will need to parse currencies.json and the "currencySpacing/beforeCurrency" object from numbers.json.
|
||||
}
|
||||
|
||||
// k. Else if p is equal to "currencySuffix" and numberFormat.[[Style]] is "currency", then
|
||||
else if ((part == "currencySuffix"sv) && (number_format.style() == NumberFormat::Style::Currency)) {
|
||||
// i. Let currency be numberFormat.[[Currency]].
|
||||
// ii. Let currencyDisplay be numberFormat.[[CurrencyDisplay]].
|
||||
// iii. Let cd be an ILD String value representing currency after x in currencyDisplay form, which may depend on x in languages having different plural forms. If the implementation does not have such a representation of currency, use currency itself.
|
||||
// iv. Append a new Record { [[Type]]: "currency", [[Value]]: cd } as the last element of result.
|
||||
|
||||
// FIXME: LibUnicode will need to parse currencies.json and the "currencySpacing/afterCurrency" object from numbers.json.
|
||||
}
|
||||
//
|
||||
// Note: Our implementation formats currency codes during GetNumberFormatPattern so that we
|
||||
// do not have to do currency display / plurality lookups more than once.
|
||||
|
||||
// l. Else,
|
||||
else {
|
||||
|
@ -1198,7 +1176,7 @@ ThrowCompletionOr<void> set_number_format_unit_options(GlobalObject& global_obje
|
|||
}
|
||||
|
||||
// 15.1.14 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-getnumberformatpattern
|
||||
Optional<StringView> get_number_format_pattern(NumberFormat& number_format, double number)
|
||||
Optional<Variant<StringView, String>> get_number_format_pattern(NumberFormat& number_format, double number)
|
||||
{
|
||||
// 1. Let localeData be %NumberFormat%.[[LocaleData]].
|
||||
// 2. Let dataLocale be numberFormat.[[DataLocale]].
|
||||
|
@ -1239,11 +1217,20 @@ Optional<StringView> get_number_format_pattern(NumberFormat& number_format, doub
|
|||
// f. Let patterns be patterns.[[<currency>]].
|
||||
// g. Let patterns be patterns.[[<currencyDisplay>]].
|
||||
// h. Let patterns be patterns.[[<currencySign>]].
|
||||
|
||||
// Handling of other [[CurrencyDisplay]] options will occur after [[SignDisplay]].
|
||||
if (number_format.currency_display() == NumberFormat::CurrencyDisplay::Name) {
|
||||
auto maybe_patterns = Unicode::select_currency_unit_pattern(number_format.data_locale(), number_format.numbering_system(), number);
|
||||
if (maybe_patterns.has_value()) {
|
||||
patterns = maybe_patterns.release_value();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (number_format.currency_sign()) {
|
||||
case NumberFormat::CurrencySign::Standard:
|
||||
patterns = Unicode::get_standard_number_system_format(number_format.data_locale(), number_format.numbering_system(), Unicode::StandardNumberFormatType::Currency);
|
||||
break;
|
||||
|
||||
case NumberFormat::CurrencySign::Accounting:
|
||||
patterns = Unicode::get_standard_number_system_format(number_format.data_locale(), number_format.numbering_system(), Unicode::StandardNumberFormatType::Accounting);
|
||||
break;
|
||||
|
@ -1332,6 +1319,39 @@ Optional<StringView> get_number_format_pattern(NumberFormat& number_format, doub
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Handling of steps 9b/9g: Depending on the currency display and the format pattern found above,
|
||||
// we might need to mutate the format pattern to inject a space between the currency display and
|
||||
// the currency number.
|
||||
if (number_format.style() == NumberFormat::Style::Currency) {
|
||||
if (number_format.currency_display() == NumberFormat::CurrencyDisplay::Name) {
|
||||
auto maybe_currency_display = Unicode::get_locale_currency_mapping(number_format.data_locale(), number_format.currency(), Unicode::Style::Numeric);
|
||||
auto currency_display = maybe_currency_display.value_or(number_format.currency());
|
||||
|
||||
return pattern.replace("{0}"sv, "{number}"sv).replace("{1}"sv, currency_display);
|
||||
}
|
||||
|
||||
Optional<StringView> currency_display;
|
||||
|
||||
switch (number_format.currency_display()) {
|
||||
case NumberFormat::CurrencyDisplay::Code:
|
||||
currency_display = number_format.currency();
|
||||
break;
|
||||
case NumberFormat::CurrencyDisplay::Symbol:
|
||||
currency_display = Unicode::get_locale_currency_mapping(number_format.data_locale(), number_format.currency(), Unicode::Style::Short);
|
||||
break;
|
||||
case NumberFormat::CurrencyDisplay::NarrowSymbol:
|
||||
currency_display = Unicode::get_locale_currency_mapping(number_format.data_locale(), number_format.currency(), Unicode::Style::Narrow);
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!currency_display.has_value())
|
||||
currency_display = number_format.currency();
|
||||
|
||||
return Unicode::create_currency_format_pattern(*currency_display, pattern);
|
||||
}
|
||||
|
||||
// 16. Return pattern.
|
||||
return pattern;
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ String format_numeric(NumberFormat& number_format, double number);
|
|||
RawFormatResult to_raw_precision(double number, int min_precision, int max_precision);
|
||||
RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction);
|
||||
ThrowCompletionOr<void> set_number_format_unit_options(GlobalObject& global_object, NumberFormat& intl_object, Object const& options);
|
||||
Optional<StringView> get_number_format_pattern(NumberFormat& number_format, double number);
|
||||
Optional<Variant<StringView, String>> get_number_format_pattern(NumberFormat& number_format, double number);
|
||||
StringView get_notation_sub_pattern(NumberFormat& number_format, int exponent);
|
||||
int compute_exponent(NumberFormat& number_format, double number);
|
||||
int compute_exponent_for_magniude(NumberFormat& number_format, int magnitude);
|
||||
|
|
|
@ -355,3 +355,329 @@ describe("style=percent", () => {
|
|||
expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c");
|
||||
});
|
||||
});
|
||||
|
||||
describe("style=currency", () => {
|
||||
test("currencyDisplay=code", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "code",
|
||||
});
|
||||
expect(en1.format(1)).toBe("USD\u00a01.00");
|
||||
expect(en1.format(1.2)).toBe("USD\u00a01.20");
|
||||
expect(en1.format(1.23)).toBe("USD\u00a01.23");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "KHR",
|
||||
currencyDisplay: "code",
|
||||
});
|
||||
expect(en2.format(1)).toBe("KHR\u00a01.00");
|
||||
expect(en2.format(1.2)).toBe("KHR\u00a01.20");
|
||||
expect(en2.format(1.23)).toBe("KHR\u00a01.23");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "code",
|
||||
});
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0USD");
|
||||
expect(ar1.format(1.2)).toBe("\u0661\u066b\u0662\u0660\u00a0USD");
|
||||
expect(ar1.format(1.23)).toBe("\u0661\u066b\u0662\u0663\u00a0USD");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "code",
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
expect(ar2.format(1)).toBe("USD\u00a01.00");
|
||||
expect(ar2.format(1.2)).toBe("USD\u00a01.20");
|
||||
expect(ar2.format(1.23)).toBe("USD\u00a01.23");
|
||||
});
|
||||
|
||||
test("currencyDisplay=symbol", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "symbol",
|
||||
});
|
||||
expect(en1.format(1)).toBe("$1.00");
|
||||
expect(en1.format(1.2)).toBe("$1.20");
|
||||
expect(en1.format(1.23)).toBe("$1.23");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "KHR",
|
||||
currencyDisplay: "symbol",
|
||||
});
|
||||
expect(en2.format(1)).toBe("KHR\u00a01.00");
|
||||
expect(en2.format(1.2)).toBe("KHR\u00a01.20");
|
||||
expect(en2.format(1.23)).toBe("KHR\u00a01.23");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "symbol",
|
||||
});
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(1.2)).toBe("\u0661\u066b\u0662\u0660\u00a0US$");
|
||||
expect(ar1.format(1.23)).toBe("\u0661\u066b\u0662\u0663\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "symbol",
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
expect(ar2.format(1)).toBe("US$\u00a01.00");
|
||||
expect(ar2.format(1.2)).toBe("US$\u00a01.20");
|
||||
expect(ar2.format(1.23)).toBe("US$\u00a01.23");
|
||||
});
|
||||
|
||||
test("currencyDisplay=narrowSymbol", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "narrowSymbol",
|
||||
});
|
||||
expect(en1.format(1)).toBe("$1.00");
|
||||
expect(en1.format(1.2)).toBe("$1.20");
|
||||
expect(en1.format(1.23)).toBe("$1.23");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "KHR",
|
||||
currencyDisplay: "narrowSymbol",
|
||||
});
|
||||
expect(en2.format(1)).toBe("៛1.00");
|
||||
expect(en2.format(1.2)).toBe("៛1.20");
|
||||
expect(en2.format(1.23)).toBe("៛1.23");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "narrowSymbol",
|
||||
});
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(1.2)).toBe("\u0661\u066b\u0662\u0660\u00a0US$");
|
||||
expect(ar1.format(1.23)).toBe("\u0661\u066b\u0662\u0663\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "narrowSymbol",
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
expect(ar2.format(1)).toBe("US$\u00a01.00");
|
||||
expect(ar2.format(1.2)).toBe("US$\u00a01.20");
|
||||
expect(ar2.format(1.23)).toBe("US$\u00a01.23");
|
||||
});
|
||||
|
||||
test("currencyDisplay=name", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "name",
|
||||
});
|
||||
expect(en1.format(1)).toBe("1.00 US dollars");
|
||||
expect(en1.format(1.2)).toBe("1.20 US dollars");
|
||||
expect(en1.format(1.23)).toBe("1.23 US dollars");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "KHR",
|
||||
currencyDisplay: "name",
|
||||
});
|
||||
expect(en2.format(1)).toBe("1.00 Cambodian riels");
|
||||
expect(en2.format(1.2)).toBe("1.20 Cambodian riels");
|
||||
expect(en2.format(1.23)).toBe("1.23 Cambodian riels");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "name",
|
||||
});
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660 دولار أمريكي");
|
||||
expect(ar1.format(1.2)).toBe("\u0661\u066b\u0662\u0660 دولار أمريكي");
|
||||
expect(ar1.format(1.23)).toBe("\u0661\u066b\u0662\u0663 دولار أمريكي");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencyDisplay: "name",
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
expect(ar2.format(1)).toBe("1.00 دولار أمريكي");
|
||||
expect(ar2.format(1.2)).toBe("1.20 دولار أمريكي");
|
||||
expect(ar2.format(1.23)).toBe("1.23 دولار أمريكي");
|
||||
});
|
||||
|
||||
test("signDisplay=never", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "never",
|
||||
});
|
||||
expect(en1.format(1)).toBe("$1.00");
|
||||
expect(en1.format(-1)).toBe("$1.00");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "never",
|
||||
});
|
||||
expect(en2.format(1)).toBe("$1.00");
|
||||
expect(en2.format(-1)).toBe("$1.00");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "never",
|
||||
});
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "never",
|
||||
});
|
||||
expect(ar2.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
});
|
||||
|
||||
test("signDisplay=auto", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "auto",
|
||||
});
|
||||
expect(en1.format(0)).toBe("$0.00");
|
||||
expect(en1.format(1)).toBe("$1.00");
|
||||
expect(en1.format(-0)).toBe("-$0.00");
|
||||
expect(en1.format(-1)).toBe("-$1.00");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "auto",
|
||||
});
|
||||
expect(en2.format(0)).toBe("$0.00");
|
||||
expect(en2.format(1)).toBe("$1.00");
|
||||
expect(en2.format(-0)).toBe("($0.00)");
|
||||
expect(en2.format(-1)).toBe("($1.00)");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "auto",
|
||||
});
|
||||
expect(ar1.format(0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-0)).toBe("\u061c-\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "auto",
|
||||
});
|
||||
expect(ar2.format(0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(1)).toBe("\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-0)).toBe("\u061c-\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
});
|
||||
|
||||
test("signDisplay=always", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "always",
|
||||
});
|
||||
expect(en1.format(0)).toBe("+$0.00");
|
||||
expect(en1.format(1)).toBe("+$1.00");
|
||||
expect(en1.format(-0)).toBe("-$0.00");
|
||||
expect(en1.format(-1)).toBe("-$1.00");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "always",
|
||||
});
|
||||
expect(en2.format(0)).toBe("+$0.00");
|
||||
expect(en2.format(1)).toBe("+$1.00");
|
||||
expect(en2.format(-0)).toBe("($0.00)");
|
||||
expect(en2.format(-1)).toBe("($1.00)");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "always",
|
||||
});
|
||||
expect(ar1.format(0)).toBe("\u061c+\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(1)).toBe("\u061c+\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-0)).toBe("\u061c-\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "always",
|
||||
});
|
||||
expect(ar2.format(0)).toBe("\u061c+\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(1)).toBe("\u061c+\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-0)).toBe("\u061c-\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
});
|
||||
|
||||
test("signDisplay=exceptZero", () => {
|
||||
const en1 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "exceptZero",
|
||||
});
|
||||
expect(en1.format(0)).toBe("$0.00");
|
||||
expect(en1.format(1)).toBe("+$1.00");
|
||||
expect(en1.format(-0)).toBe("$0.00");
|
||||
expect(en1.format(-1)).toBe("-$1.00");
|
||||
|
||||
const en2 = new Intl.NumberFormat("en", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "exceptZero",
|
||||
});
|
||||
expect(en2.format(0)).toBe("$0.00");
|
||||
expect(en2.format(1)).toBe("+$1.00");
|
||||
expect(en2.format(-0)).toBe("$0.00");
|
||||
expect(en2.format(-1)).toBe("($1.00)");
|
||||
|
||||
const ar1 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
signDisplay: "exceptZero",
|
||||
});
|
||||
expect(ar1.format(0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(1)).toBe("\u061c+\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar1.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
|
||||
const ar2 = new Intl.NumberFormat("ar", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
currencySign: "accounting",
|
||||
signDisplay: "exceptZero",
|
||||
});
|
||||
expect(ar2.format(0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(1)).toBe("\u061c+\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$");
|
||||
expect(ar2.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue