-
Notifications
You must be signed in to change notification settings - Fork 3
fix(intl): delegate locale prototype methods #700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+341
−1
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b95a36a
fix(intl): delegate locale prototype methods
frostney 99f33fc
fix(intl): harden locale prototype methods
frostney 18b8577
fix(intl): delegate typed array locale strings
frostney d8a730a
Merge remote-tracking branch 'origin/main' into issue-605-intl-locale…
frostney c9df6b7
fix(intl): preserve Date locale style options
frostney File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| /*--- | ||
| description: Array.prototype.toLocaleString delegates to each element | ||
| features: [Intl, Array.prototype.toLocaleString] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe("Array.prototype.toLocaleString", () => { | ||
| test("exists on Array.prototype", () => { | ||
| expect(typeof Array.prototype.toLocaleString).toBe("function"); | ||
| }); | ||
|
|
||
| test("invokes each element toLocaleString with locales and options", () => { | ||
| const calls = []; | ||
| const options = { style: "currency", currency: "EUR" }; | ||
| const item = { | ||
| toLocaleString(locales, receivedOptions) { | ||
| calls.push([locales, receivedOptions]); | ||
| return "item"; | ||
| }, | ||
| }; | ||
|
|
||
| expect([item, null, undefined, item].toLocaleString("de-DE", options)).toBe("item,,,item"); | ||
| expect(calls.length).toBe(2); | ||
| expect(calls[0][0]).toBe("de-DE"); | ||
| expect(calls[0][1]).toBe(options); | ||
| expect(calls[1][0]).toBe("de-DE"); | ||
| expect(calls[1][1]).toBe(options); | ||
| }); | ||
|
|
||
| test("throws when an element toLocaleString is not callable", () => { | ||
| expect(() => [{ toLocaleString: 1 }].toLocaleString()).toThrow(TypeError); | ||
| }); | ||
|
|
||
| test("does not delegate to mutable Array helpers", () => { | ||
| const originalFrom = Array.from; | ||
| const originalJoin = Array.prototype.join; | ||
|
|
||
| try { | ||
| Array.from = () => ["tainted"]; | ||
| Array.prototype.join = () => "tainted"; | ||
|
|
||
| expect([1, 2].toLocaleString()).toBe("1,2"); | ||
| } finally { | ||
| Array.from = originalFrom; | ||
| Array.prototype.join = originalJoin; | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe.runIf(isIntl)("Array.prototype.toLocaleString Intl elements", () => { | ||
| test("formats number elements through Number.prototype.toLocaleString", () => { | ||
| const options = { style: "currency", currency: "EUR" }; | ||
| const expected = new Intl.NumberFormat("de-DE", options).format(1234.5); | ||
|
|
||
| expect([1234.5].toLocaleString("de-DE", options)).toBe(expected); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /*--- | ||
| description: Date.prototype.toLocaleDateString delegates to Intl.DateTimeFormat date defaults | ||
| features: [Intl, Date] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe.runIf(isIntl)("Date.prototype.toLocaleDateString", () => { | ||
| test("formats with date defaults", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC" }; | ||
|
|
||
| expect(date.toLocaleDateString("de-DE", options)).toBe(new Intl.DateTimeFormat("de-DE", { | ||
| timeZone: "UTC", | ||
| year: "numeric", | ||
| month: "numeric", | ||
| day: "numeric", | ||
| }).format(date)); | ||
| }); | ||
|
|
||
| test("preserves explicit date options", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC", year: "numeric", month: "long" }; | ||
|
|
||
| expect(date.toLocaleDateString("en-US", options)).toBe(new Intl.DateTimeFormat("en-US", options).format(date)); | ||
| }); | ||
|
|
||
| test("throws TypeError for fake Date receiver", () => { | ||
| const fakeDate = Object.create(Date.prototype); | ||
|
|
||
| expect(() => Date.prototype.toLocaleDateString.call(fakeDate)).toThrow(TypeError); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /*--- | ||
| description: Date.prototype.toLocaleString delegates to Intl.DateTimeFormat date and time defaults | ||
| features: [Intl, Date] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe.runIf(isIntl)("Date.prototype.toLocaleString", () => { | ||
| test("formats with date and time defaults", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC" }; | ||
|
|
||
| expect(date.toLocaleString("de-DE", options)).toBe(new Intl.DateTimeFormat("de-DE", { | ||
| timeZone: "UTC", | ||
| year: "numeric", | ||
| month: "numeric", | ||
| day: "numeric", | ||
| hour: "numeric", | ||
| minute: "numeric", | ||
| second: "numeric", | ||
| }).format(date)); | ||
| }); | ||
|
|
||
| test("preserves explicit date and time options", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit" }; | ||
|
|
||
| expect(date.toLocaleString("en-US", options)).toBe(new Intl.DateTimeFormat("en-US", options).format(date)); | ||
| }); | ||
|
|
||
| test("preserves style-only options without adding component defaults", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC", dateStyle: "short" }; | ||
|
|
||
| expect(date.toLocaleString("en-US", options)).toBe(new Intl.DateTimeFormat("en-US", options).format(date)); | ||
| }); | ||
|
|
||
| test("throws TypeError for fake Date receiver", () => { | ||
| const fakeDate = Object.create(Date.prototype); | ||
|
|
||
| expect(() => Date.prototype.toLocaleString.call(fakeDate)).toThrow(TypeError); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /*--- | ||
| description: Date.prototype.toLocaleTimeString delegates to Intl.DateTimeFormat time defaults | ||
| features: [Intl, Date] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe.runIf(isIntl)("Date.prototype.toLocaleTimeString", () => { | ||
| test("formats with time defaults", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC" }; | ||
|
|
||
| expect(date.toLocaleTimeString("de-DE", options)).toBe(new Intl.DateTimeFormat("de-DE", { | ||
| timeZone: "UTC", | ||
| hour: "numeric", | ||
| minute: "numeric", | ||
| second: "numeric", | ||
| }).format(date)); | ||
| }); | ||
|
|
||
| test("preserves explicit time options", () => { | ||
| const date = new Date(Date.UTC(2026, 0, 1, 15, 30, 45)); | ||
| const options = { timeZone: "UTC", hour: "2-digit", minute: "2-digit" }; | ||
|
|
||
| expect(date.toLocaleTimeString("en-US", options)).toBe(new Intl.DateTimeFormat("en-US", options).format(date)); | ||
| }); | ||
|
|
||
| test("throws TypeError for fake Date receiver", () => { | ||
| const fakeDate = Object.create(Date.prototype); | ||
|
|
||
| expect(() => Date.prototype.toLocaleTimeString.call(fakeDate)).toThrow(TypeError); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /*--- | ||
| description: Number.prototype.toLocaleString delegates to Intl.NumberFormat | ||
| features: [Intl, Number.prototype.toLocaleString] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe.runIf(isIntl)("Number.prototype.toLocaleString", () => { | ||
| test("formats with the requested locale", () => { | ||
| const value = 1234567.89; | ||
|
|
||
| expect(value.toLocaleString("de-DE")).toBe(new Intl.NumberFormat("de-DE").format(value)); | ||
| expect(value.toLocaleString("en-US")).toBe(new Intl.NumberFormat("en-US").format(value)); | ||
| }); | ||
|
|
||
| test("passes formatting options to Intl.NumberFormat", () => { | ||
| const options = { style: "currency", currency: "EUR" }; | ||
| const value = 1234.5; | ||
|
|
||
| expect(value.toLocaleString("de-DE", options)).toBe(new Intl.NumberFormat("de-DE", options).format(value)); | ||
| }); | ||
|
|
||
| test("works with Number objects", () => { | ||
| const value = new Number(42); | ||
|
|
||
| expect(value.toLocaleString("en-US")).toBe(new Intl.NumberFormat("en-US").format(42)); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| /*--- | ||
| description: TypedArray.prototype.toLocaleString delegates to each element | ||
| features: [Intl, TypedArray.prototype.toLocaleString] | ||
| ---*/ | ||
|
|
||
| const isIntl = typeof Intl !== "undefined"; | ||
|
|
||
| describe("%TypedArray%.prototype.toLocaleString", () => { | ||
| test("invokes number element toLocaleString with locales and options", () => { | ||
| const original = Number.prototype.toLocaleString; | ||
| const options = { marker: "yes" }; | ||
|
|
||
| try { | ||
| Number.prototype.toLocaleString = { | ||
| method(locales, receivedOptions) { | ||
| return "number:" + locales + ":" + receivedOptions.marker; | ||
| }, | ||
| }.method; | ||
|
|
||
| expect(new Uint8Array([0]).toLocaleString("xx", options)).toBe("number:xx:yes"); | ||
| } finally { | ||
| Number.prototype.toLocaleString = original; | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe.runIf(isIntl)("%TypedArray%.prototype.toLocaleString Intl elements", () => { | ||
| test("formats number elements through Number.prototype.toLocaleString", () => { | ||
| const options = { minimumFractionDigits: 3 }; | ||
| const expected = new Intl.NumberFormat("th-u-nu-thai", options).format(0); | ||
|
|
||
| expect(new Uint8Array([0]).toLocaleString("th-u-nu-thai", options)).toBe(expected); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.