소스 검색

LibJS: Implement compact formatting for Intl.NumberFormat

Timothy Flynn 3 년 전
부모
커밋
fdae323401

+ 11 - 12
Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp

@@ -945,18 +945,15 @@ Vector<PatternPartition> partition_notation_sub_pattern(NumberFormat& number_for
                 }
             }
             // iv. Else if p is equal to "compactSymbol", then
-            else if (part == "compactSymbol"sv) {
-                // 1. Let compactSymbol be an ILD string representing exponent in short form, which may depend on x in languages having different plural forms. The implementation must be able to provide this string, or else the pattern would not have a "{compactSymbol}" placeholder.
-                // 2. Append a new Record { [[Type]]: "compact", [[Value]]: compactSymbol } as the last element of result.
-
-                // FIXME: Implement this when GetNotationSubPattern is fully implemented.
-            }
             // v. Else if p is equal to "compactName", then
-            else if (part == "compactName"sv) {
-                // 1. Let compactName be an ILD string representing exponent in long form, which may depend on x in languages having different plural forms. The implementation must be able to provide this string, or else the pattern would not have a "{compactName}" placeholder.
-                // 2. Append a new Record { [[Type]]: "compact", [[Value]]: compactName } as the last element of result.
+            else if (part == "compactIdentifier"sv) {
+                // Note: Our implementation combines "compactSymbol" and "compactName" into one field, "compactIdentifier".
 
-                // FIXME: Implement this when GetNotationSubPattern is fully implemented.
+                // 1. Let compactSymbol be an ILD string representing exponent in short form, which may depend on x in languages having different plural forms. The implementation must be able to provide this string, or else the pattern would not have a "{compactSymbol}" placeholder.
+                auto compact_identifier = number_format.compact_format().compact_identifier;
+
+                // 2. Append a new Record { [[Type]]: "compact", [[Value]]: compactSymbol } as the last element of result.
+                result.append({ "compact"sv, compact_identifier });
             }
             // vi. Else if p is equal to "scientificSeparator", then
             else if (part == "scientificSeparator"sv) {
@@ -1491,12 +1488,14 @@ Optional<StringView> get_notation_sub_pattern(NumberFormat& number_format, int e
     }
     // 8. Else if exponent is not 0, then
     else if (exponent != 0) {
-        // FIXME: Implement this.
-
         // a. Assert: notation is "compact".
+        VERIFY(notation == NumberFormat::Notation::Compact);
+
         // b. Let compactDisplay be numberFormat.[[CompactDisplay]].
         // c. Let compactPatterns be notationSubPatterns.[[compact]].[[<compactDisplay>]].
         // d. Return compactPatterns.[[<exponent>]].
+        if (number_format.has_compact_format())
+            return number_format.compact_format().zero_format;
     }
 
     // 9. Else,

+ 48 - 0
Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.format.js

@@ -205,6 +205,54 @@ describe("style=decimal", () => {
         expect(ar.format(12.34)).toBe("\u0661\u0662");
     });
 
+    test("notation=compact, compactDisplay=long", () => {
+        const en = new Intl.NumberFormat("en", { notation: "compact", compactDisplay: "long" });
+        expect(en.format(1)).toBe("1");
+        expect(en.format(1200)).toBe("1.2 thousand");
+        expect(en.format(1290)).toBe("1.3 thousand");
+        expect(en.format(12000)).toBe("12 thousand");
+        expect(en.format(12900)).toBe("13 thousand");
+        expect(en.format(1200000)).toBe("1.2 million");
+        expect(en.format(1290000)).toBe("1.3 million");
+        expect(en.format(12000000)).toBe("12 million");
+        expect(en.format(12900000)).toBe("13 million");
+
+        const ar = new Intl.NumberFormat("ar", { notation: "compact", compactDisplay: "long" });
+        expect(ar.format(1)).toBe("\u0661");
+        expect(ar.format(1200)).toBe("\u0661\u066b\u0662 ألف");
+        expect(ar.format(1290)).toBe("\u0661\u066b\u0663 ألف");
+        expect(ar.format(12000)).toBe("\u0661\u0662 ألف");
+        expect(ar.format(12900)).toBe("\u0661\u0663 ألف");
+        expect(ar.format(1200000)).toBe("\u0661\u066b\u0662 مليون");
+        expect(ar.format(1290000)).toBe("\u0661\u066b\u0663 مليون");
+        expect(ar.format(12000000)).toBe("\u0661\u0662 مليون");
+        expect(ar.format(12900000)).toBe("\u0661\u0663 مليون");
+    });
+
+    test("notation=compact, compactDisplay=short", () => {
+        const en = new Intl.NumberFormat("en", { 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", { notation: "compact", compactDisplay: "short" });
+        expect(ar.format(1)).toBe("\u0661");
+        expect(ar.format(1200)).toBe("\u0661\u066b\u0662\u00a0ألف");
+        expect(ar.format(1290)).toBe("\u0661\u066b\u0663\u00a0ألف");
+        expect(ar.format(12000)).toBe("\u0661\u0662\u00a0ألف");
+        expect(ar.format(12900)).toBe("\u0661\u0663\u00a0ألف");
+        expect(ar.format(1200000)).toBe("\u0661\u066b\u0662\u00a0مليون");
+        expect(ar.format(1290000)).toBe("\u0661\u066b\u0663\u00a0مليون");
+        expect(ar.format(12000000)).toBe("\u0661\u0662\u00a0مليون");
+        expect(ar.format(12900000)).toBe("\u0661\u0663\u00a0مليون");
+    });
+
     test("signDisplay=never", () => {
         const en = new Intl.NumberFormat("en", { signDisplay: "never" });
         expect(en.format(1)).toBe("1");

+ 58 - 0
Userland/Libraries/LibJS/Tests/builtins/Intl/NumberFormat/NumberFormat.prototype.formatToParts.js

@@ -312,6 +312,64 @@ describe("style=decimal", () => {
             { type: "exponentInteger", value: "\u0663" },
         ]);
     });
+
+    test("notation=compact, compactDisplay=long", () => {
+        const en = new Intl.NumberFormat("en", { notation: "compact", compactDisplay: "long" });
+        expect(en.formatToParts(1200)).toEqual([
+            { type: "integer", value: "1" },
+            { type: "decimal", value: "." },
+            { type: "fraction", value: "2" },
+            { type: "literal", value: " " },
+            { type: "compact", value: "thousand" },
+        ]);
+        expect(en.formatToParts(12900000)).toEqual([
+            { type: "integer", value: "13" },
+            { type: "literal", value: " " },
+            { type: "compact", value: "million" },
+        ]);
+
+        const ar = new Intl.NumberFormat("ar", { notation: "compact", compactDisplay: "long" });
+        expect(ar.formatToParts(1200)).toEqual([
+            { type: "integer", value: "\u0661" },
+            { type: "decimal", value: "\u066b" },
+            { type: "fraction", value: "\u0662" },
+            { type: "literal", value: " " },
+            { type: "compact", value: "ألف" },
+        ]);
+        expect(ar.formatToParts(12900000)).toEqual([
+            { type: "integer", value: "\u0661\u0663" },
+            { type: "literal", value: " " },
+            { type: "compact", value: "مليون" },
+        ]);
+    });
+
+    test("notation=compact, compactDisplay=short", () => {
+        const en = new Intl.NumberFormat("en", { notation: "compact", compactDisplay: "short" });
+        expect(en.formatToParts(1200)).toEqual([
+            { type: "integer", value: "1" },
+            { type: "decimal", value: "." },
+            { type: "fraction", value: "2" },
+            { type: "compact", value: "K" },
+        ]);
+        expect(en.formatToParts(12900000)).toEqual([
+            { type: "integer", value: "13" },
+            { type: "compact", value: "M" },
+        ]);
+
+        const ar = new Intl.NumberFormat("ar", { notation: "compact", compactDisplay: "short" });
+        expect(ar.formatToParts(1200)).toEqual([
+            { type: "integer", value: "\u0661" },
+            { type: "decimal", value: "\u066b" },
+            { type: "fraction", value: "\u0662" },
+            { type: "literal", value: "\u00a0" },
+            { type: "compact", value: "ألف" },
+        ]);
+        expect(ar.formatToParts(12900000)).toEqual([
+            { type: "integer", value: "\u0661\u0663" },
+            { type: "literal", value: "\u00a0" },
+            { type: "compact", value: "مليون" },
+        ]);
+    });
 });
 
 describe("style=percent", () => {