ecma402
ecma402 copied to clipboard
Change Array.prototype.toLocaleString to use ListFormat
Currently, there is a section in ECMA402 about Array.prototype.toLocaleString https://ecma-international.org/ecma-402/#sup-array.prototype.tolocalestring
With the newly Stage 3 Intl.ListFormat moving to Stage 4, we should consider to rewrite that section to delegate the behavior to Intl.ListFormat
Two potential issues which can make the transition to Intl.ListFormat a bit harder than expected:
- How to handle the
optionsparameter? Should it apply only to the individual array elements or also to the implicitly constructedListFormat?- I'll guess I can answer this myself: I don't think we can reuse the
optionsparameter to apply toIntl.ListFormatobject, because some property names like"style"can have different meanings for theListFormatand for the elements, cf.[1].toLocaleString("en", {style: "percent"}).
- I'll guess I can answer this myself: I don't think we can reuse the
- The current
Array.prototype.toLocaleStringspecification ensures all elements are always separated with the same delimiter. This won't be the case anymore whenListFormatis used. Here's an example using English, German, and French which shows that there's notype/stylecombination which ensures only a single delimiter is used between elements for all locales.
for (let locale of ["en", "de", "fr"]) {
for (let type of ["conjunction", "unit"]) {
for (let style of ["long", "short", "narrow"]) {
print(`${locale} (${type}-${style}):`, new Intl.ListFormat(locale, {type, style}).format(["1", "2"]))
}
}
}
- ChakraCore uses LOCALE_SLIST, which can give better output for certain locales and element types (or more specifically: numbers).
LOCALE_SLISTis similar tonumbers/symbols/listin CLDR.- This applies to locales where the list separator is the same character as the decimal separator.
- Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=1388777 and https://bugs.chromium.org/p/chromium/issues/detail?id=753841
- (But I guess if we want to support number list formatting, it should happen in a follow-up proposal to
Intl.ListFormat.)
How to handle the options parameter?
This is a great question that I don't have an immediate answer to. One possible solution: namespace the child options like so:
[1].toLocaleString("en", {
numberOptions: {
style: "percent"
}
});
~Note: although the spec currently says that the options should be passed down verbatim, it seems that web reality at least in Chrome 80 is that we don't actually pass any options into the child toLocaleString calls. [9999].toLocaleString({ style: "percent" }) => "9,999" // Chrome 80~
The current Array.prototype.toLocaleString specification ensures all elements are always separated with the same delimiter.
Right; this would be a normative change that we would just need to make sure everyone can agree to.
Note: although the spec currently says that the options should be passed down verbatim, it seems that web reality at least in Chrome 80 is that we don't actually pass any options into the child toLocaleString calls.
The example is missing the locales argument. When using [9999].toLocaleString("en", { style: "percent" }), I get a percent formatted result in V8, JSC, and SpiderMonkey.
Thanks, yes, my bad >_>
Good catch, @FrankYFTang . I agree this should be addressed. Some ideas about the issues @anba raised:
- I agree with your analysis. The two options I see here are
A: Add an options bag entry for
listOptionsor something like that, which weGetout of the options bag and pass to theListFormatconstructor. B: Add a second options argument. (This could be pretty hard to read since they're distinguished only positionally, and also it's not analogous to anything else.) Subjectively, I prefer the A. (This whole path feels funny to me given https://github.com/tc39/proposal-intl-list-format/pull/7 , and I wish I had considered it when making that decision... maybe Intl.ListFormat should always do options processing this way, and send each element throughtoLocaleString? Though it's rather late for this kind of change.) - Yes, this would be a change. For better or worse, the current browser engines agree that the separator should be
",". I want to just cross my fingers and hope that this change is web-compatible enough... - I like the idea of adding an option to Intl.ListFormat to ask for a number list mode; I'm skeptical of deciding on this mode automatically.
How about this?
locale = "en";
opt = { style: "currency", currency: "TWD",
timeZone: "Asia/Taipei", dateStyle: "long",
listStyle: "short", type: "conjunction" };
[new Date(), 1234, 567, new Date()].toLocaleString(locale, opt)
currently, in Chrome we got 'October 9, 2021,NT$1,234.00,NT$567.00,October 9, 2021'
so the above is equlivent to
(new Intl.ListFormat(locale, {style: opt.listStyle, type: opt.type})).format(
[(new Date()).toLocaleString(locale, opt),
Number(1234).toLocaleString(locale, opt),
Number(567).toLocaleString(locale, opt),
new Date().toLocaleString(locale, opt)]
)
the above right now in Chrome output
'October 9, 2021, NT$1,234.00, NT$567.00, & October 9, 2021'
and we construct a listOption as {type: type, style: listStyle} to pass to ListFormat?
We discuss this in TG2 today (2021-11-04). I feel it might be an issue much more complicated than I expected and would like to park this issue and let other drive it they feel interest. People can still use Intl.ListFormat to do what they like to do and I think the possibility option conclit between different types of items is way too complicated and probably a good idea to just keep the status quo.
Discussion 2021-11-04: https://github.com/tc39/ecma402/blob/master/meetings/notes-2021-11-04.md#change-arrayprototypetolocalestring-to-use-listformat
Maybe we should consider just deprecating Array.prototype.toLocaleString.
- It is more clear and explicit for people to use Intl.ListFormat
- There are likely people who depend on Array.prototype.toLocaleString having its current behavior
- No great solution to the conflicting options issue discussed above
I think we have a general agreement that the output of toLocaleString is locale and platform dependent and should not be depended upon? Due to this assumption I'm still in favor of doing this at some point.