tc39 / ecma402

Status, process, and documents for ECMA 402
https://tc39.es/ecma402/
Other
535 stars 105 forks source link

Change Array.prototype.toLocaleString to use ListFormat #422

Open FrankYFTang opened 4 years ago

FrankYFTang commented 4 years ago

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

anba commented 4 years ago

Two potential issues which can make the transition to Intl.ListFormat a bit harder than expected:

  1. How to handle the options parameter? Should it apply only to the individual array elements or also to the implicitly constructed ListFormat?

    • I'll guess I can answer this myself: I don't think we can reuse the options parameter to apply to Intl.ListFormat object, because some property names like "style" can have different meanings for the ListFormat and for the elements, cf. [1].toLocaleString("en", {style: "percent"}).
  2. The current Array.prototype.toLocaleString specification ensures all elements are always separated with the same delimiter. This won't be the case anymore when ListFormat is used. Here's an example using English, German, and French which shows that there's no type/style combination 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"]))
    }
    }
    }

  3. ChakraCore uses LOCALE_SLIST, which can give better output for certain locales and element types (or more specifically: numbers). LOCALE_SLIST is similar to numbers/symbols/list in CLDR.

sffc commented 4 years ago

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.

anba commented 4 years ago

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.

sffc commented 4 years ago

Thanks, yes, my bad >_>

littledan commented 4 years ago

Good catch, @FrankYFTang . I agree this should be addressed. Some ideas about the issues @anba raised:

  1. I agree with your analysis. The two options I see here are A: Add an options bag entry for listOptions or something like that, which we Get out of the options bag and pass to the ListFormat constructor. 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 through toLocaleString? Though it's rather late for this kind of change.)
  2. 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...
  3. 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.
FrankYFTang commented 2 years ago

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?

FrankYFTang commented 2 years ago

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.

sffc commented 2 years ago

Discussion 2021-11-04: https://github.com/tc39/ecma402/blob/master/meetings/notes-2021-11-04.md#change-arrayprototypetolocalestring-to-use-listformat

sffc commented 2 years ago

Maybe we should consider just deprecating Array.prototype.toLocaleString.

  1. It is more clear and explicit for people to use Intl.ListFormat
  2. There are likely people who depend on Array.prototype.toLocaleString having its current behavior
  3. No great solution to the conflicting options issue discussed above
ryzokuken commented 2 years ago

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.