Skip to content
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

ResolvedLocale locale string manipulation is confusing and inconsistent #872

Open
gibson042 opened this issue Mar 1, 2024 · 0 comments
Open
Labels
c: spec Component: spec editorial issues s: help wanted Status: help wanted; needs proposal champion Small Smaller change solvable in a Pull Request
Milestone

Comments

@gibson042
Copy link
Contributor

ResolveLocale strives to output a record in which the string locale includes "-u-" Unicode locale extension sequence data from the matched requested locale as filtered by relevantExtensionKeys and superseded by options, but it (and implementations) do a poor job of handling keys with multiple representations for the same value data.

Examples

Calendar

"islamic-civil" vs. "islamicc"
$ eshost -sx '
const repr = val => JSON.stringify(val).replaceAll(/"([^"]*)":/g, "$1 ");
const key = "ca", propName = "calendar", values = [undefined, "islamic-civil", "islamicc"];
for (const ext of values.map(val => val === undefined ? "" : `-u-${key}-${val}`)) {
  for(const val of values) {
    const requestedLocale = `az${ext}`, options = { [propName]: val },
      resolved = new Intl.DateTimeFormat(requestedLocale, options).resolvedOptions();
    print(`${requestedLocale} ${repr(options)}`.padEnd(48) + ` => ${resolved.locale} ${repr(resolved[propName])}`);
  }
}
'
#### GraalJS, SpiderMonkey
az {}                                            => az "gregory"
az {calendar "islamic-civil"}                    => az "islamic-civil"
az {calendar "islamicc"}                         => az "islamic-civil"
az-u-ca-islamic-civil {}                         => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamic-civil"} => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamicc"}      => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {}                              => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamic-civil"}      => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamicc"}           => az-u-ca-islamic-civil "islamic-civil"

#### V8
az {}                                            => az "gregory"
az {calendar "islamic-civil"}                    => az "islamic-civil"
az {calendar "islamicc"}                         => az "islamic-civil"
az-u-ca-islamic-civil {}                         => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamic-civil"} => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamicc"}      => az "islamic-civil"
az-u-ca-islamicc {}                              => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamic-civil"}      => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamicc"}           => az "islamic-civil"

#### JavaScriptCore
az {}                                            => az "gregory"
az {calendar "islamic-civil"}                    => az "islamic-civil"
az {calendar "islamicc"}                         => az "islamicc"
az-u-ca-islamic-civil {}                         => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamic-civil"} => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamic-civil {calendar "islamicc"}      => az "islamicc"
az-u-ca-islamicc {}                              => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamic-civil"}      => az-u-ca-islamic-civil "islamic-civil"
az-u-ca-islamicc {calendar "islamicc"}           => az "islamicc"

GraalJS and SpiderMonkey seem to normalize the value from the requested locale and the value from options before doing anything with either, such that { calendar: "islamicc" } is treated as a non-override of both "-u-ca-islamic-civil" and "-u-ca-islamicc". V8 seems to normalize only the former before comparison, such that { calendar: "islamicc" } is treated as overriding both "-u-ca-islamic-civil" and "-u-ca-islamicc". And JSC seems to not normalize the value from options at all, such that { calendar: "islamicc" } overrides any value in the requested locale and also survives into resolvedOptions() output.

I prefer the GraalJS/SpiderMonkey behavior.

Numeric collation

"true" vs. empty string
$ eshost -sx '
const repr = val => JSON.stringify(val).replaceAll(/"([^"]*)":/g, "$1 ");
const key = "kn", propName = "numeric", values = [undefined, "", "true"];
for (const ext of values.map(val => val === undefined ? "" : `-u-${key}${val ? "-" + val : ""}`)) {
  for(const val of values) {
    const requestedLocale = `en${ext}`, options = { [propName]: val },
      resolved = new Intl.Collator(requestedLocale, options).resolvedOptions();
    print(`${requestedLocale} ${repr(options)}`.padEnd(29) + ` => ${resolved.locale} ${repr(resolved[propName])}`);
  }
}
'
#### GraalJS
en {}                         => en false
en {numeric ""}               => en false
en {numeric "true"}           => en true
en-u-kn {}                    => en-u-kn true
en-u-kn {numeric ""}          => en false
en-u-kn {numeric "true"}      => en true
en-u-kn-true {}               => en-u-kn true
en-u-kn-true {numeric ""}     => en false
en-u-kn-true {numeric "true"} => en true

#### V8
en {}                         => en false
en {numeric ""}               => en false
en {numeric "true"}           => en true
en-u-kn {}                    => en-u-kn true
en-u-kn {numeric ""}          => en-u-kn false
en-u-kn {numeric "true"}      => en-u-kn true
en-u-kn-true {}               => en-u-kn true
en-u-kn-true {numeric ""}     => en-u-kn false
en-u-kn-true {numeric "true"} => en-u-kn true

#### JavaScriptCore, SpiderMonkey
en {}                         => en false
en {numeric ""}               => en false
en {numeric "true"}           => en true
en-u-kn {}                    => en-u-kn true
en-u-kn {numeric ""}          => en false
en-u-kn {numeric "true"}      => en-u-kn true
en-u-kn-true {}               => en-u-kn true
en-u-kn-true {numeric ""}     => en false
en-u-kn-true {numeric "true"} => en-u-kn true

All implementations agree on output from resolvedOptions(), and that behavior is expected. As for the locale string, GraalJS overrides it when options provides any value at all, V8 always preserves it regardless of options, and JSC and SpiderMonkey preserve it if and only if options provides no value or provides a truthy value.

I prefer the JSC/SpiderMonkey behavior.

Recommendations

I would like to update the spec to produce behavior like that of SpiderMonkey, normalizing values before comparing them to determine if options constitutes an override and therefore necessitates a removal from the resolved locale string.

@sffc sffc added this to the ES 2024 milestone Mar 13, 2024
@sffc sffc added c: spec Component: spec editorial issues Small Smaller change solvable in a Pull Request s: help wanted Status: help wanted; needs proposal champion labels Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: spec Component: spec editorial issues s: help wanted Status: help wanted; needs proposal champion Small Smaller change solvable in a Pull Request
Projects
None yet
Development

No branches or pull requests

2 participants