Accept the collations Intl.Locale.prototype.getCollations reports - #2974
Merged
lahma merged 1 commit intoAug 11, 2026
Merged
Conversation
ECMA-402 gives a locale one collation list and reads it from two places. https://tc39.es/ecma402/#sec-collationsoflocale (15.5.10) step 3.c takes %Intl.Collator%.[[SortLocaleData]].[[<foundLocale>]].[[co]] and reports it, and https://tc39.es/ecma402/#sec-resolvelocale (9.2.7) step 10 - reached from https://tc39.es/ecma402/#sec-intl.collator (10.1.1) through ResolveOptions - resolves a requested "co" by asking whether that same list contains it, from the -u-co- keyword and from the options bag alike. getCollations() and Intl.Collator are two views of one list, so they may not disagree. Jint's disagreed on thirteen of fifteen tags probed. GetCollationsForLanguage appends the root collations "emoji" and "eor" to every locale, which is right - CLDR's root locale carries both, and 15.5.10 step 4 hardcodes exactly that pair for a tag matching nothing - while IsCollationSupportedForLocale conceded only "eor" from that pair and otherwise allowed just what the per-language table lists. The table lists "emoji" for "en" alone, so new Intl.Locale('de').getCollations() // ["emoji", "eor", "phonebk"] new Intl.Collator('de', { collation: 'emoji' }).resolvedOptions().collation // "default" and likewise for ar, da, es, hi, ja, ko, ln, si, sv, zh, tr and fr. Only "en", whose table entry happens to list "emoji", and "und", which resolves through the default locale, were consistent. V8 accepts "emoji" on all fifteen. The acceptance predicate is the older half, but the disagreement is new: until sebastienros#2867 getCollations() answered ["default"] for every locale and so had nothing to contradict. Both views now read one derived list. LocaleCollations is built once from the CLDR table plus the root collations, in the code unit order 15.5.10 returns; GetCollationsForLanguage hands it out and IsCollationSupportedForLocale scans it. Adding a collation to the table now reports it and accepts it by the same act, which is the property that was missing. Two identifiers stay out of that list rather than being special-cased where a request is resolved. "standard" and "search" are excluded because https://tc39.es/ecma402/#sec-intl-collator-internal-slots (10.2.3) forbids either from being an element of any [[SortLocaleData]].[[<locale>]].[[co]] or [[SearchLocaleData]].[[<locale>]].[[co]] List, and keeping them out of the list makes them unreportable and unrequestable at once - the guard they used to have in GetCollationOption would have gone on rejecting them even if a table edit made getCollations() report them, which is this same bug in miniature. "default" is excluded because no locale reports it, but stays acceptable as a request: it is how Jint spells the null [[co]] that 10.1.1 turns into a resolved collation of "default" anyway. ValidCollationTypes goes with them. It was a second, wider whitelist every request had to clear first, and because every entry of every per-language list is already in it the check never changed an answer - but it was one more list to keep in step, and a collation added to the table and not to it would have been reported and refused exactly as "emoji" was. The fifteen-tag probe now finds nothing: everything getCollations() reports constructs and resolves back to itself, through the options bag and through -u-co-. The resolved locale for a -u-co-emoji tag moves with it, from "de" to "de-u-co-emoji", because 9.2.7 step 10 inserts a keyword back into the locale once its value resolves. A collation supplied through the options bag still does not appear there - the same step sets supportedKeyword to empty on the options branch - which is what intl402/Collator/prototype/resolvedOptions/resolved-collation-unicode-extensions-and-options.js pins, and is one place V8 answers differently. Left alone deliberately: whether the per-language table is accurate CLDR at all. Its "ducet", "direct", "big5han" and "reformed"-rather-than-"trad" entries are flagged as suspect and are a data question of their own. This change makes the two views agree on whatever the table says, which is a prerequisite for correcting it rather than a substitute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lahma
added a commit
to lahma/jint
that referenced
this pull request
Aug 12, 2026
sebastienros#2974 made Intl.Locale.prototype.getCollations and Intl.Collator agree on whatever LocaleCollationSupport says, and left open whether what it says is CLDR at all. It is not, in six places. The table is the one CLDR reads two ways: common/bcp47/collation.xml registers the collation identifiers and marks the dead ones deprecated, and common/collation/<language>.xml says which of them a language actually defines. identifier registry per-language file big5han deprecated="true" defined by no locale direct deprecated="true" defined by no locale gb2312 deprecated="true" defined by no locale reformed deprecated="true" defined by no locale ducet current defined by no locale Those five were reachable. "ducet" is the one that is not deprecated - it is registered and current, and CLDR simply ships no data for it, a search of common/collation for a <collation type="ducet"> finding nothing and the hi.xml hit being prose about the DUCET order rather than a type. Either way no locale's file defines it, so no locale's [[co]] list may contain it. The other four are deprecated and equally undefined; "reformed" in particular is the old name for what sv.xml has called "traditional" for many releases. What the per-language files say, minus the "standard" and "search" every one of them carries and the "private-" types CLDR keeps only to be [import]ed: ar compat unchanged de search, phonebook, eor unchanged as phonebk, eor en (no <collations> element at all) "ducet" removed, row now empty es traditional unchanged as trad fi traditional "trad" added, row is new hi standard "direct" removed, row now empty ja private-kana, unihan unchanged ko searchjl, unihan unchanged ln phonetic unchanged si dictionary unchanged as dict sv traditional "reformed" replaced by "trad" vi traditional "trad" added, row is new zh private-pinyin, pinyin, stroke, "big5han" and "gb2312" removed zhuyin, unihan root.xml defines "standard", "search", "eor", "private-unihan" and "emoji", so RootCollations stays ["emoji", "eor"] and every locale keeps getting it. That is also why the en, hi and da rows go away entirely rather than shrinking: once what CLDR does not support is gone, what is left of them is the root pair every locale already gets, which is the same reason fr and tr never needed a row. The rows that remain now say one thing - what this language adds - so the "eor" that ar, es, ja, ko, ln, si, sv and zh used to repeat is gone from them too, while de keeps its own, which de.xml really does define as a German delta on the European ordering rules. Intl.supportedValuesOf("collation") moves with them. https://tc39.es/ecma402/#sec-availablecanonicalcollations wants the collations the implementation provides Intl.Collator functionality for, and intl402/Intl/supportedValuesOf/collations-accepted-by-Collator.js checks exactly that against ten locales - so leaving the five in DefaultCldrProvider.GetSupportedCollations while no locale resolves them any more turns that test red on "big5han is supported by Collator". It is still a third hardcoded list that can drift from the table; deriving it is a separate change. Left alone: yue, which CLDR does give zh's set. It has no collation file, and supplementalData.xml's <parentLocales component="collations"> makes zh_Hant its collation parent, whose file defines only a defaultCollation - which is why node reports pinyin, stroke, unihan and zhuyin for it and ICU's coll/yue.txt is a bare %%ALIAS to zh_Hant. Jint cannot report it yet: "yue" matches no element of IntlUtilities.GetAvailableLocales(), so Intl.Collator.supportedLocalesOf (['yue']) is empty and new Intl.Collator('yue') resolves to the default locale. A row would have advertised four collations Intl.Collator answers "default" for - the disagreement sebastienros#2974 removed, restored on one tag. Reporting them waits on making yue an available Collator locale, and a test pins the current answer and the reason so a later row cannot be added without it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
added a commit
that referenced
this pull request
Aug 13, 2026
#2974 made Intl.Locale.prototype.getCollations and Intl.Collator agree on whatever LocaleCollationSupport says, and left open whether what it says is CLDR at all. It is not, in six places. The table is the one CLDR reads two ways: common/bcp47/collation.xml registers the collation identifiers and marks the dead ones deprecated, and common/collation/<language>.xml says which of them a language actually defines. identifier registry per-language file big5han deprecated="true" defined by no locale direct deprecated="true" defined by no locale gb2312 deprecated="true" defined by no locale reformed deprecated="true" defined by no locale ducet current defined by no locale Those five were reachable. "ducet" is the one that is not deprecated - it is registered and current, and CLDR simply ships no data for it, a search of common/collation for a <collation type="ducet"> finding nothing and the hi.xml hit being prose about the DUCET order rather than a type. Either way no locale's file defines it, so no locale's [[co]] list may contain it. The other four are deprecated and equally undefined; "reformed" in particular is the old name for what sv.xml has called "traditional" for many releases. What the per-language files say, minus the "standard" and "search" every one of them carries and the "private-" types CLDR keeps only to be [import]ed: ar compat unchanged de search, phonebook, eor unchanged as phonebk, eor en (no <collations> element at all) "ducet" removed, row now empty es traditional unchanged as trad fi traditional "trad" added, row is new hi standard "direct" removed, row now empty ja private-kana, unihan unchanged ko searchjl, unihan unchanged ln phonetic unchanged si dictionary unchanged as dict sv traditional "reformed" replaced by "trad" vi traditional "trad" added, row is new zh private-pinyin, pinyin, stroke, "big5han" and "gb2312" removed zhuyin, unihan root.xml defines "standard", "search", "eor", "private-unihan" and "emoji", so RootCollations stays ["emoji", "eor"] and every locale keeps getting it. That is also why the en, hi and da rows go away entirely rather than shrinking: once what CLDR does not support is gone, what is left of them is the root pair every locale already gets, which is the same reason fr and tr never needed a row. The rows that remain now say one thing - what this language adds - so the "eor" that ar, es, ja, ko, ln, si, sv and zh used to repeat is gone from them too, while de keeps its own, which de.xml really does define as a German delta on the European ordering rules. Intl.supportedValuesOf("collation") moves with them. https://tc39.es/ecma402/#sec-availablecanonicalcollations wants the collations the implementation provides Intl.Collator functionality for, and intl402/Intl/supportedValuesOf/collations-accepted-by-Collator.js checks exactly that against ten locales - so leaving the five in DefaultCldrProvider.GetSupportedCollations while no locale resolves them any more turns that test red on "big5han is supported by Collator". It is still a third hardcoded list that can drift from the table; deriving it is a separate change. Left alone: yue, which CLDR does give zh's set. It has no collation file, and supplementalData.xml's <parentLocales component="collations"> makes zh_Hant its collation parent, whose file defines only a defaultCollation - which is why node reports pinyin, stroke, unihan and zhuyin for it and ICU's coll/yue.txt is a bare %%ALIAS to zh_Hant. Jint cannot report it yet: "yue" matches no element of IntlUtilities.GetAvailableLocales(), so Intl.Collator.supportedLocalesOf (['yue']) is empty and new Intl.Collator('yue') resolves to the default locale. A row would have advertised four collations Intl.Collator answers "default" for - the disagreement #2974 removed, restored on one tag. Reporting them waits on making yue an available Collator locale, and a test pins the current answer and the reason so a later row cannot be added without it. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
lahma
added a commit
to lahma/jint
that referenced
this pull request
Aug 13, 2026
Intl.supportedValuesOf("collation") was a third hardcoded collation list, in
DefaultCldrProvider.GetSupportedCollations, next to the per-language table and
the root pair the other two views read.
https://tc39.es/ecma402/#sec-availablecanonicalcollations asks for "the
collations for which the implementation provides the functionality of
Intl.Collator objects", sorted in lexicographic code unit order and unique -
which is the union of the one [[co]] list every locale has and nothing else, so
there was never a second fact for that list to hold. It is now read off
LocaleCollations, the same derived data getCollations and Intl.Collator resolve
against.
It agreed with them when this was written, and the previous change is what made
that true again - but agreeing today is exactly what the two views did before
sebastienros#2974. The failure modes are asymmetric and only one of them is caught:
a value listed that no locale resolves turns
intl402/Intl/supportedValuesOf/collations-accepted-by-Collator.js red, which is
how the deprecated CLDR types were noticed, while a collation some locale
reports and the list omits is invisible to test262 - AvailableCollations is
implementation-defined, so nothing checks it from that side. Adding a collation
to LocaleCollationSupport used to produce exactly that silent second case; it
now shows up in all three views by the same act.
"standard", "search" and "default" need no exclusion of their own here.
IsReportableCollation already keeps them out of every list being unioned, which
is what https://tc39.es/ecma402/#sec-intl-collator-internal-slots (10.2.3)
requires of the per-locale lists and what
intl402/Intl/supportedValuesOf/collations.js checks of this one - the same
guard now answering for both. Sorting is likewise not restated: the union is
built in code unit order, which is what 15.5.10 and this clause both ask for
and what IntlInstance.SupportedValuesOf sorts by anyway.
The values are unchanged: compat, dict, emoji, eor, phonebk, phonetic, pinyin,
searchjl, stroke, trad, unihan and zhuyin, the same twelve the hardcoded list
ended at. The tests pin both the derivation - the union over every language,
built from getCollations rather than from the expected answer - and that
literal list, so a change to the table has to be a deliberate edit in two
places rather than a silent divergence in one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
added a commit
that referenced
this pull request
Aug 13, 2026
Intl.supportedValuesOf("collation") was a third hardcoded collation list, in
DefaultCldrProvider.GetSupportedCollations, next to the per-language table and
the root pair the other two views read.
https://tc39.es/ecma402/#sec-availablecanonicalcollations asks for "the
collations for which the implementation provides the functionality of
Intl.Collator objects", sorted in lexicographic code unit order and unique -
which is the union of the one [[co]] list every locale has and nothing else, so
there was never a second fact for that list to hold. It is now read off
LocaleCollations, the same derived data getCollations and Intl.Collator resolve
against.
It agreed with them when this was written, and the previous change is what made
that true again - but agreeing today is exactly what the two views did before
#2974. The failure modes are asymmetric and only one of them is caught:
a value listed that no locale resolves turns
intl402/Intl/supportedValuesOf/collations-accepted-by-Collator.js red, which is
how the deprecated CLDR types were noticed, while a collation some locale
reports and the list omits is invisible to test262 - AvailableCollations is
implementation-defined, so nothing checks it from that side. Adding a collation
to LocaleCollationSupport used to produce exactly that silent second case; it
now shows up in all three views by the same act.
"standard", "search" and "default" need no exclusion of their own here.
IsReportableCollation already keeps them out of every list being unioned, which
is what https://tc39.es/ecma402/#sec-intl-collator-internal-slots (10.2.3)
requires of the per-locale lists and what
intl402/Intl/supportedValuesOf/collations.js checks of this one - the same
guard now answering for both. Sorting is likewise not restated: the union is
built in code unit order, which is what 15.5.10 and this clause both ask for
and what IntlInstance.SupportedValuesOf sorts by anyway.
The values are unchanged: compat, dict, emoji, eor, phonebk, phonetic, pinyin,
searchjl, stroke, trad, unihan and zhuyin, the same twelve the hardcoded list
ended at. The tests pin both the derivation - the union over every language,
built from getCollations rather than from the expected answer - and that
literal list, so a change to the table has to be a deliberate edit in two
places rather than a silent divergence in one.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
#2867 made
getCollations()report real collation data — and exposed thatIntl.Collatorrefusedemojifor 13 of the locales the query advertises it for (ar da de es hi ja ko ln si sv zh tr fr): the acceptance predicate's fallback concededeorfrom the root pair and forgotemoji, and its table branch trusted a table that listsemojiforenalone.The two views may not legally disagree: 15.5.10 CollationsOfLocale reads
[[SortLocaleData]].[[<foundLocale>]].[[co]], and 9.2.7 ResolveLocale step 10 gates both the-u-co-keyword branch and the options branch on that same list. One[[co]]per locale;getCollations()and the constructor are two views of it.The fix makes them one list by construction:
LocaleCollationsis derived once from the table plus the root pair,GetCollationsForLanguagehands it out andIsCollationSupportedForLocalescans it.standard/searchstay out of the list rather than being guarded at resolution time (10.2.3 forbids them as[[co]]elements, and filtering at derivation means a future table edit cannot reintroduce this bug class);defaultis accepted as a request only — it is the spelling of the null[[co]]— and never reported.ValidCollationTypes, a second wider whitelist that never changed an answer but could drift exactly the way this bug did, is deleted.15-tag consistency matrix (everything reported must construct and echo in
resolvedOptions().collation, options bag and-u-co-): Jint before 13 inconsistent cells, after 0. One correction to the review that found this: node is not clean on the same matrix — it has 3 inconsistent cells (zh/pinyinresolves todefaultbecause pinyin is zh's default, and bothund-u-co-*forms).One deliberate non-change, now pinned so nobody "fixes" it toward V8:
resolvedOptions().localefor an options-bag collation staysde, notde-u-co-emoji— ResolveLocale setssupportedKeywordto empty on the options branch, and test262'sresolved-collation-unicode-extensions-and-options.jspins exactly that.Red 15 / green 25 on both TFMs. Test262 exactly 99,738 / 0 / 157;
Test262Harness.settings.jsonuntouched.Found and left for the backlog: an invalid
collationoption ({collation:'a'}) silently falls back where 9.2.8 step 7.d.ii wants aRangeError(node throws); theLocaleCollationSupporttable's CLDR accuracy (ducet/direct/big5han/reformed-vs-trad) — this consistency fix is the prerequisite for correcting that data, not a substitute; andIntl.supportedValuesOf("collation")is backed by a third hardcoded list that is consistent today but can drift the same way.🤖 Generated with Claude Code