-
Notifications
You must be signed in to change notification settings - Fork 549
Test RegionPreference fallbacks in Intl.Locale.p.getWeekInfo #5090
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
gibson042
merged 2 commits into
tc39:main
from
ptomato:ecma402-1051-region-preference-locale-getweekinfo
Jul 23, 2026
Merged
Changes from all commits
Commits
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
75 changes: 75 additions & 0 deletions
75
test/intl402/Locale/prototype/getWeekInfo/likely-subtags-region.js
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,75 @@ | ||
| // Copyright 2026 Igalia, S.L. All rights reserved. | ||
| // This code is governed by the BSD license found in the LICENSE file. | ||
|
|
||
| /*--- | ||
| esid: sec-intl.locale.prototype.getWeekInfo | ||
| description: > | ||
| When the locale has no region, the Add Likely Subtags algorithm is used to | ||
| derive the region whose week info is returned. | ||
| info: | | ||
| RegionPreference ( locale ) | ||
| ... | ||
| 2. If _region_ is *undefined*, then | ||
| a. Set _region_ to CanonicalUnicodeSubdivision(_locale_, *"sd"*). | ||
| b. If _region_ is *undefined*, then | ||
| i. Let _maximal_ be the result of the Add Likely Subtags algorithm applied | ||
| to _locale_. If an error is signaled, set _maximal_ to _locale_. | ||
| ii. Set _maximal_ to CanonicalizeUnicodeLocaleId(_maximal_). | ||
| iii. Set _region_ to GetLocaleRegion(_maximal_). | ||
| iv. If _region_ is *undefined*, then | ||
| 1. Set _region_ to *"001"*. | ||
| ... | ||
| features: [Intl.Locale, Intl.Locale-info] | ||
| ---*/ | ||
|
|
||
| // In order not to rely on a particular implementation's locale data, this test | ||
| // searches for a suitable locale without a region subtag, but where the likely | ||
| // region would influence the supported week info. | ||
| // | ||
| // Each candidate below is a language subtag together with the region that the | ||
| // Add Likely Subtags algorithm derives for it (e.g. "th" maximizes to | ||
| // "th-Thai-TH", so "th-TH"). A language tag without a region, subdivision ("sd" | ||
| // keyword), or region override ("rg" keyword) passed to RegionPreference must | ||
| // apply Add Likely Subtags and arrive at the paired region. Its week info must | ||
| // therefore be identical to those of the candidate locale. An incorrect | ||
| // implementation might instead fall back to the region "001" in step 2.b.iv. | ||
| // | ||
| // We could assume even less about the locale data by not hardcoding these | ||
| // likely regions and instead checking all available locales with the result of | ||
| // maximize() on each one, but that assumes the implementation has a working | ||
| // maximize(), and if that's the case then it probably implements this step | ||
| // correctly as well. | ||
| function weekInfoEqual(a, b) { | ||
| return a.firstDay === b.firstDay && compareArray(a.weekend, b.weekend); | ||
| } | ||
|
|
||
| function findSuitableTestData() { | ||
| const candidates = ["th-TH", "fa-IR", "ja-JP", "sa-IN", "ps-AF"]; | ||
|
|
||
| for (const regionTag of candidates) { | ||
| const weekInfoWithLikelyRegion = new Intl.Locale(regionTag).getWeekInfo(); | ||
|
|
||
| const bareLanguage = regionTag.replace(/-.*/, ""); | ||
| const fallbackLocale = new Intl.Locale(`${bareLanguage}-001`); | ||
| if (!weekInfoEqual(fallbackLocale.getWeekInfo(), weekInfoWithLikelyRegion)) { | ||
| return [bareLanguage, weekInfoWithLikelyRegion]; | ||
| } | ||
| } | ||
|
|
||
| assert(false, "No suitable test data found in this implementation. Consider updating the candidate list"); | ||
| } | ||
|
|
||
| const [language, expectedWeekInfo] = findSuitableTestData(); | ||
|
|
||
| const locale = new Intl.Locale(language); | ||
| const weekInfo = locale.getWeekInfo(); | ||
| assert.sameValue( | ||
| weekInfo.firstDay, | ||
| expectedWeekInfo.firstDay, | ||
| `getWeekInfo() for "${language}" should return firstDay that matches the likely region` | ||
| ); | ||
| assert.compareArray( | ||
| weekInfo.weekend, | ||
| expectedWeekInfo.weekend, | ||
| `getWeekInfo() for "${language}" should return weekend that matches the likely region` | ||
| ); | ||
79 changes: 79 additions & 0 deletions
79
test/intl402/Locale/prototype/getWeekInfo/region-override.js
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,79 @@ | ||
| // Copyright 2026 Igalia, S.L. All rights reserved. | ||
| // This code is governed by the BSD license found in the LICENSE file. | ||
|
|
||
| /*--- | ||
| esid: sec-intl.locale.prototype.getWeekInfo | ||
| description: > | ||
| When the locale has a region override ("rg") keyword, its region overrides | ||
| the region subtag when deriving the week info. | ||
| info: | | ||
| WeekInfoOfLocale ( loc ) | ||
| ... | ||
| 2. Let _preference_ be RegionPreference(_loc_.[[Locale]]). | ||
| 3. Let _region_ be _preference_.[[Region]]. | ||
| 4. Let _regionOverride_ be _preference_.[[RegionOverride]]. | ||
| 5. If _regionOverride_ is not *undefined* and week data for _regionOverride_ | ||
| are available, then | ||
| a. Let _lookupRegion_ be _regionOverride_. | ||
| 6. Else, | ||
| a. Let _lookupRegion_ be _region_. | ||
| ... | ||
|
|
||
| RegionPreference ( locale ) | ||
| ... | ||
| 3. Let _regionOverride_ be CanonicalUnicodeSubdivision(_locale_, *"rg"*). | ||
| 4. Return { [[Region]]: _region_, [[RegionOverride]]: _regionOverride_ }. | ||
| features: [Intl.Locale, Intl.Locale-info] | ||
| ---*/ | ||
|
|
||
| // In order not to rely on a particular implementation's locale data, this test | ||
| // searches for a suitable locale with a region subtag together with a region | ||
| // override ("rg" extension) whose region would influence the supported week | ||
| // info. | ||
| // | ||
| // Each candidate below is a locale with a region override extension, together | ||
| // with a locale that names the override's region explicitly (e.g. | ||
| // "en-US-u-rg-inzzzz" overrides the region with "IN", so "en-IN"). Because the | ||
| // region override is set, WeekInfoOfLocale must use it as the lookup region | ||
| // instead of the region subtag. Its week info must therefore be identical to | ||
| // those of the paired override-region locale. An incorrect implementation might | ||
| // instead ignore the "rg" keyword and use the region subtag. | ||
| function weekInfoEqual(a, b) { | ||
| return a.firstDay === b.firstDay && compareArray(a.weekend, b.weekend); | ||
| } | ||
|
|
||
| function findSuitableTestData() { | ||
| const candidates = [ | ||
| ["en-US-u-rg-dezzzz", "en-DE"], | ||
| ["en-US-u-rg-inzzzz", "en-IN"], | ||
| ["en-US-u-rg-irzzzz", "en-IR"], | ||
| ["en-US-u-rg-afzzzz", "en-AF"], | ||
| ]; | ||
|
|
||
| for (const [overrideTag, regionTag] of candidates) { | ||
| const weekInfoWithOverrideRegion = new Intl.Locale(regionTag).getWeekInfo(); | ||
|
|
||
| const baseName = overrideTag.replace(/-u-.*/, ""); | ||
| const baseLocale = new Intl.Locale(baseName); | ||
| if (!weekInfoEqual(baseLocale.getWeekInfo(), weekInfoWithOverrideRegion)) { | ||
| return [overrideTag, weekInfoWithOverrideRegion]; | ||
| } | ||
| } | ||
|
|
||
| assert(false, "No suitable test data found in this implementation. Consider updating the candidate list"); | ||
| } | ||
|
|
||
| const [overrideTag, expectedWeekInfo] = findSuitableTestData(); | ||
|
|
||
| const locale = new Intl.Locale(overrideTag); | ||
| const weekInfo = locale.getWeekInfo(); | ||
| assert.sameValue( | ||
| weekInfo.firstDay, | ||
| expectedWeekInfo.firstDay, | ||
| `getWeekInfo() for "${overrideTag}" should return firstDay that matches the override region` | ||
| ); | ||
| assert.compareArray( | ||
| weekInfo.weekend, | ||
| expectedWeekInfo.weekend, | ||
| `getWeekInfo() for "${overrideTag}" should return weekend that matches the override region` | ||
| ); |
97 changes: 97 additions & 0 deletions
97
test/intl402/Locale/prototype/getWeekInfo/region-priority.js
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,97 @@ | ||
| // Copyright 2026 Igalia, S.L. All rights reserved. | ||
| // This code is governed by the BSD license found in the LICENSE file. | ||
|
|
||
| /*--- | ||
| esid: sec-intl.locale.prototype.getWeekInfo | ||
| description: > | ||
| The region used to look up the week info is chosen from the available signals | ||
| in the correct priority order. | ||
| info: | | ||
| WeekInfoOfLocale ( loc ) | ||
| ... | ||
| 2. Let _preference_ be RegionPreference(_loc_.[[Locale]]). | ||
| 3. Let _region_ be _preference_.[[Region]]. | ||
| 4. Let _regionOverride_ be _preference_.[[RegionOverride]]. | ||
| 5. If _regionOverride_ is not *undefined* and week data for _regionOverride_ | ||
| are available, then | ||
| a. Let _lookupRegion_ be _regionOverride_. | ||
| 6. Else, | ||
| a. Let _lookupRegion_ be _region_. | ||
| ... | ||
|
|
||
| RegionPreference ( locale ) | ||
| 1. Let _region_ be GetLocaleRegion(_locale_). | ||
| 2. If _region_ is *undefined*, then | ||
| a. Set _region_ to CanonicalUnicodeSubdivision(_locale_, *"sd"*). | ||
| b. If _region_ is *undefined*, then | ||
| i. Let _maximal_ be the result of the Add Likely Subtags algorithm applied | ||
| to _locale_. If an error is signaled, set _maximal_ to _locale_. | ||
| ii. Set _maximal_ to CanonicalizeUnicodeLocaleId(_maximal_). | ||
| iii. Set _region_ to GetLocaleRegion(_maximal_). | ||
| iv. If _region_ is *undefined*, then | ||
| 1. Set _region_ to *"001"*. | ||
| 3. Let _regionOverride_ be CanonicalUnicodeSubdivision(_locale_, *"rg"*). | ||
| 4. Return { [[Region]]: _region_, [[RegionOverride]]: _regionOverride_ }. | ||
| features: [Intl.Locale, Intl.Locale-info] | ||
| ---*/ | ||
|
|
||
| // Together, WeekInfoOfLocale and RegionPreference select the region used to | ||
| // look up the week info from the available signals in this priority order | ||
| // (highest first): | ||
| // | ||
| // 1. the "rg" region override keyword (when week data for it exists) | ||
| // 2. the region subtag | ||
| // 3. the "sd" subdivision keyword | ||
| // 4. the region computed by the Add Likely Subtags algorithm | ||
| // 5. the "001" (world) region | ||
| // | ||
| // Each entry below is a locale that carries the signal for its priority level | ||
| // on top of all lower-priority signals, paired with a locale that names the | ||
| // region that signal must resolve to. The paired locale uses the same base | ||
| // language so that any language-dependent locale data is held constant. For | ||
| // example, "fa-JP-u-sd-inka-rg-afzzzz" carries an "rg" override of region "AF", | ||
| // a region subtag "JP", an "sd" subdivision in region "IN", and a base language | ||
| // "fa" whose likely region is "IR"; the "rg" override has the highest priority, | ||
| // so the locale must behave like "fa-AF". | ||
| // | ||
| // The last entry has no lower-priority signals: the language "eo" (Esperanto) | ||
| // has no likely region, so its region falls back to "001". | ||
| const levels = [ | ||
| { description: 'the "rg" region override', locale: "fa-JP-u-sd-inka-rg-afzzzz", region: "fa-AF" }, | ||
| { description: "the region subtag", locale: "fa-JP-u-sd-inka", region: "fa-JP" }, | ||
| { description: 'the "sd" subdivision', locale: "fa-u-sd-inka", region: "fa-IN" }, | ||
| { description: "the Add Likely Subtags region", locale: "fa", region: "fa-IR" }, | ||
| { description: 'the "001" region', locale: "eo", region: "eo-001" }, | ||
| ]; | ||
|
|
||
| function weekInfoEqual(a, b) { | ||
| return a.firstDay === b.firstDay && compareArray(a.weekend, b.weekend); | ||
| } | ||
|
|
||
| // For each priority level to be observable, the region it selects must have | ||
| // different week info than the region selected by the next lower level in this | ||
| // implementation; otherwise the test could not tell them apart. | ||
| for (let i = 0; i < levels.length - 1; i++) { | ||
| const higher = new Intl.Locale(levels[i].region).getWeekInfo(); | ||
| const lower = new Intl.Locale(levels[i + 1].region).getWeekInfo(); | ||
| assert( | ||
| !weekInfoEqual(higher, lower), | ||
| `Inconclusive: ${levels[i].description} and ${levels[i + 1].description} ` + | ||
| "selected regions with identical week info in this implementation. Consider updating the test data" | ||
| ); | ||
| } | ||
|
|
||
| for (const { description, locale, region } of levels) { | ||
| const weekInfo = new Intl.Locale(locale).getWeekInfo(); | ||
| const expected = new Intl.Locale(region).getWeekInfo(); | ||
| assert.sameValue( | ||
| weekInfo.firstDay, | ||
| expected.firstDay, | ||
| `getWeekInfo() for "${locale}" should use ${description}, like "${region}": firstDay mismatch` | ||
| ); | ||
| assert.compareArray( | ||
| weekInfo.weekend, | ||
| expected.weekend, | ||
| `getWeekInfo() for "${locale}" should use ${description}, like "${region}": weekend mismatch` | ||
| ); | ||
| } |
72 changes: 72 additions & 0 deletions
72
test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js
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,72 @@ | ||
| // Copyright 2026 Igalia, S.L. All rights reserved. | ||
| // This code is governed by the BSD license found in the LICENSE file. | ||
|
|
||
| /*--- | ||
| esid: sec-intl.locale.prototype.getWeekInfo | ||
| description: > | ||
| When the locale has no region but has a subdivision ("sd") keyword, the | ||
| subdivision's region is used to derive the week info. | ||
| info: | | ||
| RegionPreference ( locale ) | ||
| 1. Let _region_ be GetLocaleRegion(_locale_). | ||
| 2. If _region_ is *undefined*, then | ||
| a. Set _region_ to CanonicalUnicodeSubdivision(_locale_, *"sd"*). | ||
| b. If _region_ is *undefined*, then | ||
| i. Let _maximal_ be the result of the Add Likely Subtags algorithm applied | ||
| to _locale_. If an error is signaled, set _maximal_ to _locale_. | ||
| ... | ||
| ... | ||
| features: [Intl.Locale, Intl.Locale-info] | ||
| ---*/ | ||
|
|
||
| // In order not to rely on a particular implementation's locale data, this test | ||
| // searches for a suitable locale without a region subtag, but with a | ||
| // subdivision ("sd" extension) whose region would influence the supported week | ||
| // info. | ||
| // | ||
| // Each candidate below is a locale with a subdivision extension but no region | ||
| // subtag, together with a locale that names that subdivision's region | ||
| // explicitly (e.g. "en-u-sd-inka" has subdivision "inka", whose region is "IN", | ||
| // so "en-IN"). Because such a locale has no region subtag, RegionPreference | ||
| // must derive its region from the subdivision. Its week info must therefore be | ||
| // identical to those of the paired region locale. An incorrect implementation | ||
| // might instead ignore the "sd" extension and apply Add Likely Subtags to the | ||
| // bare language, giving "en-Latn-US". | ||
| function weekInfoEqual(a, b) { | ||
| return a.firstDay === b.firstDay && compareArray(a.weekend, b.weekend); | ||
| } | ||
|
|
||
| function findSuitableTestData() { | ||
| const candidates = [ | ||
| ["en-u-sd-inka", "en-IN"], | ||
| ["en-u-sd-irthr", "en-IR"], | ||
| ["en-u-sd-afgh", "en-AF"], | ||
| ]; | ||
|
|
||
| for (const [subdivisionTag, regionTag] of candidates) { | ||
| const weekInfoWithSubdivisionRegion = new Intl.Locale(regionTag).getWeekInfo(); | ||
|
|
||
| const baseName = subdivisionTag.replace(/-u-.*/, ""); | ||
| const fallbackLocale = new Intl.Locale(baseName).maximize(); | ||
| if (!weekInfoEqual(fallbackLocale.getWeekInfo(), weekInfoWithSubdivisionRegion)) { | ||
| return [subdivisionTag, weekInfoWithSubdivisionRegion]; | ||
| } | ||
| } | ||
|
|
||
| assert(false, "No suitable test data found in this implementation. Consider updating the candidate list"); | ||
| } | ||
|
|
||
| const [subdivisionTag, expectedWeekInfo] = findSuitableTestData(); | ||
|
|
||
| const locale = new Intl.Locale(subdivisionTag); | ||
| const weekInfo = locale.getWeekInfo(); | ||
| assert.sameValue( | ||
| weekInfo.firstDay, | ||
| expectedWeekInfo.firstDay, | ||
| `getWeekInfo() for "${subdivisionTag}" should return firstDay that matches the subdivision's region` | ||
| ); | ||
| assert.compareArray( | ||
| weekInfo.weekend, | ||
| expectedWeekInfo.weekend, | ||
| `getWeekInfo() for "${subdivisionTag}" should return weekend that matches the subdivision's region` | ||
| ); |
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.