Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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];
}
Comment thread
gibson042 marked this conversation as resolved.
}

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 test/intl402/Locale/prototype/getWeekInfo/region-override.js
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 test/intl402/Locale/prototype/getWeekInfo/region-priority.js
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 test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js
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`
);
Loading