diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-07-12 15:07:52 -0400 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-07-13 19:22:26 +0100 |
commit | cff2d631daf16522bb83c60706b4dd8faf9e59d1 (patch) | |
tree | 0653995308f424a973da6d5cc94e80d03abc70b9 | |
parent | 733192089fac01ac9f4128bdc393a72812317766 (diff) | |
download | serenity-cff2d631daf16522bb83c60706b4dd8faf9e59d1.zip |
LibJS: Implement Intl.NumberFormat V3's [[SignDisplay]] changes
Intl.NumberFormat V3 adds a "negative" option for [[SignDisplay]] to
only use the locale's signed pattern for negative numbers.
3 files changed, 254 insertions, 7 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index 0fc47fb469..9ebdd27d5d 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -1139,6 +1139,7 @@ RawFormatResult to_raw_fixed(GlobalObject& global_object, Value number, int min_ } // 15.5.11 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-getnumberformatpattern +// 1.1.14 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-getnumberformatpattern Optional<Variant<StringView, String>> get_number_format_pattern(GlobalObject& global_object, NumberFormat& number_format, Value number, Unicode::NumberFormat& found_pattern) { // 1. Let localeData be %NumberFormat%.[[LocaleData]]. @@ -1264,20 +1265,34 @@ Optional<Variant<StringView, String>> get_number_format_pattern(GlobalObject& gl } break; - // 15. Else, + // 15. Else if signDisplay is "exceptZero", then case NumberFormat::SignDisplay::ExceptZero: - // a. Assert: signDisplay is "exceptZero". - // b. If x is NaN, or if x is finite and β(x) is 0, then + // a. If x is NaN, or if x is finite and β(x) is 0, then if (is_positive_zero || is_negative_zero || is_nan) { // i. Let pattern be patterns.[[zeroPattern]]. pattern = patterns->zero_format; } - // c. Else if β(x) > 0, then + // b. Else if β(x) > 0, then else if (is_greater_than(number, 0)) { // i. Let pattern be patterns.[[positivePattern]]. pattern = patterns->positive_format; } - // d. Else, + // c. Else, + else { + // i. Let pattern be patterns.[[negativePattern]]. + pattern = patterns->negative_format; + } + break; + + // 16. Else, + case NumberFormat::SignDisplay::Negative: + // a. Assert: signDisplay is "negative". + // b. If x is 0 or x is -0 or x > 0 or x is NaN, then + if (is_positive_zero || is_negative_zero || is_greater_than(number, 0) || is_nan) { + // i. Let pattern be patterns.[[zeroPattern]]. + pattern = patterns->zero_format; + } + // c. Else, else { // i. Let pattern be patterns.[[negativePattern]]. pattern = patterns->negative_format; @@ -1285,8 +1300,7 @@ Optional<Variant<StringView, String>> get_number_format_pattern(GlobalObject& gl break; default: - // FIXME: Handle all NumberFormat V3 [[SignDisplay]] options. - return {}; + VERIFY_NOT_REACHED(); } found_pattern = patterns.release_value(); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.format.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.format.js index 50709781bc..1d1d7d3c9c 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.format.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.format.js @@ -347,6 +347,20 @@ describe("style=decimal", () => { expect(ar.format(-1)).toBe("\u061c-\u0661"); }); + test("signDisplay=negative", () => { + const en = new Intl.NumberFormat("en", { signDisplay: "negative" }); + expect(en.format(0)).toBe("0"); + expect(en.format(1)).toBe("1"); + expect(en.format(-0)).toBe("0"); + expect(en.format(-1)).toBe("-1"); + + const ar = new Intl.NumberFormat("ar", { signDisplay: "negative" }); + expect(ar.format(0)).toBe("\u0660"); + expect(ar.format(1)).toBe("\u0661"); + expect(ar.format(-0)).toBe("\u0660"); + expect(ar.format(-1)).toBe("\u061c-\u0661"); + }); + test("useGrouping=always", () => { const en = new Intl.NumberFormat("en", { useGrouping: "always" }); expect(en.format(123)).toBe("123"); @@ -626,6 +640,20 @@ describe("style=percent", () => { expect(ar.format(-0.0)).toBe("\u0660\u066a\u061c"); expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c"); }); + + test("signDisplay=negative", () => { + const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "negative" }); + expect(en.format(0.0)).toBe("0%"); + expect(en.format(0.01)).toBe("1%"); + expect(en.format(-0.0)).toBe("0%"); + expect(en.format(-0.01)).toBe("-1%"); + + const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "negative" }); + expect(ar.format(0.0)).toBe("\u0660\u066a\u061c"); + expect(ar.format(0.01)).toBe("\u0661\u066a\u061c"); + expect(ar.format(-0.0)).toBe("\u0660\u066a\u061c"); + expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c"); + }); }); describe("style=currency", () => { @@ -952,6 +980,50 @@ describe("style=currency", () => { expect(ar2.format(-0)).toBe("\u0660\u066b\u0660\u0660\u00a0US$"); expect(ar2.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$"); }); + + test("signDisplay=negative", () => { + const en1 = new Intl.NumberFormat("en", { + style: "currency", + currency: "USD", + signDisplay: "negative", + }); + 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: "negative", + }); + 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: "negative", + }); + 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("\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: "negative", + }); + 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("\u0660\u066b\u0660\u0660\u00a0US$"); + expect(ar2.format(-1)).toBe("\u061c-\u0661\u066b\u0660\u0660\u00a0US$"); + }); }); describe("style=unit", () => { diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.formatToParts.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.formatToParts.js index ab6adba25f..f5c9529057 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.formatToParts.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.formatToParts.js @@ -171,6 +171,26 @@ describe("style=decimal", () => { ]); }); + test("signDisplay=negative", () => { + const en = new Intl.NumberFormat("en", { signDisplay: "negative" }); + expect(en.formatToParts(0)).toEqual([{ type: "integer", value: "0" }]); + expect(en.formatToParts(1)).toEqual([{ type: "integer", value: "1" }]); + expect(en.formatToParts(-0)).toEqual([{ type: "integer", value: "0" }]); + expect(en.formatToParts(-1)).toEqual([ + { type: "minusSign", value: "-" }, + { type: "integer", value: "1" }, + ]); + + const ar = new Intl.NumberFormat("ar", { signDisplay: "negative" }); + expect(ar.formatToParts(0)).toEqual([{ type: "integer", value: "\u0660" }]); + expect(ar.formatToParts(1)).toEqual([{ type: "integer", value: "\u0661" }]); + expect(ar.formatToParts(-0)).toEqual([{ type: "integer", value: "\u0660" }]); + expect(ar.formatToParts(-1)).toEqual([ + { type: "minusSign", value: "\u061c-" }, + { type: "integer", value: "\u0661" }, + ]); + }); + test("useGrouping=always", () => { const en = new Intl.NumberFormat("en", { useGrouping: "always" }); expect(en.formatToParts(1234)).toEqual([ @@ -588,6 +608,46 @@ describe("style=percent", () => { { type: "percentSign", value: "\u066a\u061c" }, ]); }); + + test("signDisplay=negative", () => { + const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "negative" }); + expect(en.formatToParts(0.0)).toEqual([ + { type: "integer", value: "0" }, + { type: "percentSign", value: "%" }, + ]); + expect(en.formatToParts(0.01)).toEqual([ + { type: "integer", value: "1" }, + { type: "percentSign", value: "%" }, + ]); + expect(en.formatToParts(-0.0)).toEqual([ + { type: "integer", value: "0" }, + { type: "percentSign", value: "%" }, + ]); + expect(en.formatToParts(-0.01)).toEqual([ + { type: "minusSign", value: "-" }, + { type: "integer", value: "1" }, + { type: "percentSign", value: "%" }, + ]); + + const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "negative" }); + expect(ar.formatToParts(0.0)).toEqual([ + { type: "integer", value: "\u0660" }, + { type: "percentSign", value: "\u066a\u061c" }, + ]); + expect(ar.formatToParts(0.01)).toEqual([ + { type: "integer", value: "\u0661" }, + { type: "percentSign", value: "\u066a\u061c" }, + ]); + expect(ar.formatToParts(-0.0)).toEqual([ + { type: "integer", value: "\u0660" }, + { type: "percentSign", value: "\u066a\u061c" }, + ]); + expect(ar.formatToParts(-0.01)).toEqual([ + { type: "minusSign", value: "\u061c-" }, + { type: "integer", value: "\u0661" }, + { type: "percentSign", value: "\u066a\u061c" }, + ]); + }); }); describe("style=currency", () => { @@ -1133,6 +1193,107 @@ describe("style=currency", () => { { type: "literal", value: ")" }, ]); }); + + test("signDisplay=negative", () => { + const en = new Intl.NumberFormat("en", { + style: "currency", + currency: "USD", + signDisplay: "negative", + }); + expect(en.formatToParts(0)).toEqual([ + { type: "currency", value: "$" }, + { type: "integer", value: "0" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "00" }, + ]); + expect(en.formatToParts(1)).toEqual([ + { type: "currency", value: "$" }, + { type: "integer", value: "1" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "00" }, + ]); + expect(en.formatToParts(-0)).toEqual([ + { type: "currency", value: "$" }, + { type: "integer", value: "0" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "00" }, + ]); + expect(en.formatToParts(-1)).toEqual([ + { type: "minusSign", value: "-" }, + { type: "currency", value: "$" }, + { type: "integer", value: "1" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "00" }, + ]); + + const ar = new Intl.NumberFormat("ar", { + style: "currency", + currency: "USD", + signDisplay: "negative", + }); + expect(ar.formatToParts(0)).toEqual([ + { type: "integer", value: "\u0660" }, + { type: "decimal", value: "\u066b" }, + { type: "fraction", value: "\u0660\u0660" }, + { type: "literal", value: "\u00a0" }, + { type: "currency", value: "US$" }, + ]); + expect(ar.formatToParts(1)).toEqual([ + { type: "integer", value: "\u0661" }, + { type: "decimal", value: "\u066b" }, + { type: "fraction", value: "\u0660\u0660" }, + { type: "literal", value: "\u00a0" }, + { type: "currency", value: "US$" }, + ]); + expect(ar.formatToParts(-0)).toEqual([ + { type: "integer", value: "\u0660" }, + { type: "decimal", value: "\u066b" }, + { type: "fraction", value: "\u0660\u0660" }, + { type: "literal", value: "\u00a0" }, + { type: "currency", value: "US$" }, + ]); + expect(ar.formatToParts(-1)).toEqual([ + { type: "minusSign", value: "\u061c-" }, + { type: "integer", value: "\u0661" }, + { type: "decimal", value: "\u066b" }, + { type: "fraction", value: "\u0660\u0660" }, + { type: "literal", value: "\u00a0" }, + { type: "currency", value: "US$" }, + ]); + + const zh = new Intl.NumberFormat("zh-Hant-u-nu-hanidec", { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "negative", + }); + expect(zh.formatToParts(0)).toEqual([ + { type: "currency", value: "US$" }, + { type: "integer", value: "γ" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "γγ" }, + ]); + expect(zh.formatToParts(1)).toEqual([ + { type: "currency", value: "US$" }, + { type: "integer", value: "δΈ" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "γγ" }, + ]); + expect(zh.formatToParts(-0)).toEqual([ + { type: "currency", value: "US$" }, + { type: "integer", value: "γ" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "γγ" }, + ]); + expect(zh.formatToParts(-1)).toEqual([ + { type: "literal", value: "(" }, + { type: "currency", value: "US$" }, + { type: "integer", value: "δΈ" }, + { type: "decimal", value: "." }, + { type: "fraction", value: "γγ" }, + { type: "literal", value: ")" }, + ]); + }); }); describe("style=unit", () => { |