diff --git a/test/intl402/Locale/prototype/getWeekInfo/likely-subtags-region.js b/test/intl402/Locale/prototype/getWeekInfo/likely-subtags-region.js new file mode 100644 index 00000000000..0f7669a75f1 --- /dev/null +++ b/test/intl402/Locale/prototype/getWeekInfo/likely-subtags-region.js @@ -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` +); diff --git a/test/intl402/Locale/prototype/getWeekInfo/region-override.js b/test/intl402/Locale/prototype/getWeekInfo/region-override.js new file mode 100644 index 00000000000..3a5e78ce887 --- /dev/null +++ b/test/intl402/Locale/prototype/getWeekInfo/region-override.js @@ -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` +); diff --git a/test/intl402/Locale/prototype/getWeekInfo/region-priority.js b/test/intl402/Locale/prototype/getWeekInfo/region-priority.js new file mode 100644 index 00000000000..0b4cb8fb832 --- /dev/null +++ b/test/intl402/Locale/prototype/getWeekInfo/region-priority.js @@ -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` + ); +} diff --git a/test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js b/test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js new file mode 100644 index 00000000000..4722902e8e2 --- /dev/null +++ b/test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js @@ -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` +);