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
102 changes: 30 additions & 72 deletions test/intl402/Locale/constructor-getter-order.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,92 +11,48 @@ includes: [compareArray.js]
---*/

const order = [];
function handleGet(name, value) {
order.push("get " + name);
return {
toString() {
order.push("toString " + name);
return value;
}
};
}
new Intl.Locale(
{ toString() { order.push("tag toString"); return "en"; } },
{
get language() {
order.push("get language");
return {
toString() {
order.push("toString language");
return "de";
}
}
},

get script() {
order.push("get script");
return {
toString() {
order.push("toString script");
return "Latn";
}
}
},

get region() {
order.push("get region");
return {
toString() {
order.push("toString region");
return "DE";
}
}
},

get calendar() {
order.push("get calendar");
return {
toString() {
order.push("toString calendar");
return "gregory";
}
}
return handleGet("calendar", "gregory");
},
get caseFirst() {
return handleGet("caseFirst", "upper");
},

get collation() {
order.push("get collation");
return {
toString() {
order.push("toString collation");
return "zhuyin";
}
}
return handleGet("collation", "zhuyin");
},

get hourCycle() {
order.push("get hourCycle");
return {
toString() {
order.push("toString hourCycle");
return "h24";
}
}
return handleGet("hourCycle", "h24");
},

get caseFirst() {
order.push("get caseFirst");
return {
toString() {
order.push("toString caseFirst");
return "upper";
}
}
get language() {
return handleGet("language", "de");
},
get numberingSystem() {
return handleGet("numberingSystem", "latn");
},

get numeric() {
order.push("get numeric");
return false;
},

get numberingSystem() {
order.push("get numberingSystem");
return {
toString() {
order.push("toString numberingSystem");
return "latn";
}
}
get region() {
return handleGet("region", "DE");
},
get script() {
return handleGet("script", "Latn");
},
get variants() {
return handleGet("variants", "fonipa-1996");
},
}
);
Expand All @@ -109,6 +65,8 @@ const expected_order = [
"toString script",
"get region",
"toString region",
"get variants",
"toString variants",
"get calendar",
"toString calendar",
"get collation",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const options = [
"language",
"script",
"region",
"variants",
"calendar",
"collation",
"hourCycle",
Expand Down
54 changes: 54 additions & 0 deletions test/intl402/Locale/constructor-options-variants-invalid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2025 Richard Gibson. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-intl.locale
description: >
Checks error cases for the options argument to the Locale
constructor.
info: |
Intl.Locale( tag [, options] )
12. Set _tag_ to ? UpdateLanguageId(_tag_, _options_).

UpdateLanguageId ( tag, options )
8. Let _variants_ be ? GetOption(_options_, *"variants"*, ~string~, ~empty~, GetLocaleVariants(_baseName_)).
9. If _variants_ is not *undefined*, then
a. If _variants_ cannot be matched by the <code>unicode_variant_subtag</code> Unicode locale nonterminal, throw a *RangeError* exception.

features: [Intl.Locale]
---*/

/*
unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3})
*/
const invalidVariantsOptions = [
"",
"a",
"1",
"ab",
"2x",
"abc",
"3xy",
"abcd",
"abcdefghi",

// Value contains more than just the 'variants' production.
"GB-scouse",

// Value contains duplicates.
"fonipa-fonipa",
"fonipa-valencia-Fonipa",

// Value contains out-of-place dashes.
"-",
"-spanglis",
"spanglis-",
"-spanglis-oxendict",
"spanglis-oxendict-",
"spanglis--oxendict",
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also test for leading, trailing, and consecutive "-"?

const alsoInvalid = [
  "-",
  "-spanglis",
  "spanglis-",
  "-spanglis-oxendict",
  "spanglis-oxendict-",
  "spanglis--oxendict",
];

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And test that duplicate variants throw:

js> new Intl.Locale("en", {variants: "spanglis-spanglis"})
typein:1:1 RangeError: duplicate variant subtag

for (const variants of invalidVariantsOptions) {
assert.throws(RangeError, function() {
new Intl.Locale("en", {variants});
}, `new Intl.Locale("en", {variants: "${variants}"}) throws RangeError`);
}
60 changes: 60 additions & 0 deletions test/intl402/Locale/constructor-options-variants-valid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2025 Richard Gibson. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-intl.locale
description: >
Checks error cases for the options argument to the Locale
constructor.
info: |
Intl.Locale( tag [, options] )
12. Set _tag_ to ? UpdateLanguageId(_tag_, _options_).

UpdateLanguageId ( tag, options )
8. Let _variants_ be ? GetOption(_options_, *"variants"*, ~string~, ~empty~, GetLocaleVariants(_baseName_)).
...
13. If _variants_ is not *undefined*, set _newTag_ to the string-concatenation of _newTag_, *"-"*, and _variants_.

features: [Intl.Locale]
---*/

const validVariantsOptions = [
['en', undefined, undefined],
['en', 'spanglis', 'en-spanglis'],

// unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3})
['xx', '1xyz', 'xx-1xyz'],
['xx', '1234', 'xx-1234'],
['xx', 'abcde', 'xx-abcde'],
['xx', '12345678', 'xx-12345678'],
['xx', '1xyz-1234-abcde-12345678', 'xx-1234-12345678-1xyz-abcde'],

// Canonicalization affects subtag ordering.
['en', 'spanglis-oxendict', 'en-oxendict-spanglis'],
];
for (const [lang, variants, baseName] of validVariantsOptions) {
let options = { variants };
let optionsRepr = `{variants: ${typeof variants === "string" ? `"${variants}"` : variants}}`;
let instance;
let expect;

instance = new Intl.Locale(lang, options);
expect = baseName || lang;
assert.sameValue(instance.toString(), expect,
`new Intl.Locale("${lang}", ${optionsRepr}).toString() returns "${expect}"`);

instance = new Intl.Locale(lang + '-fonipa', options);
expect = baseName || (lang + '-fonipa');
assert.sameValue(instance.toString(), expect,
`new Intl.Locale("${lang}-fonipa", ${optionsRepr}).toString() returns "${expect}"`);

instance = new Intl.Locale(lang + '-u-ca-gregory', options);
expect = (baseName || lang) + '-u-ca-gregory';
assert.sameValue(instance.toString(), expect,
`new Intl.Locale("${lang}-u-ca-gregory", ${optionsRepr}).toString() returns "${expect}"`);

instance = new Intl.Locale(lang + '-fonipa-u-ca-gregory', options);
expect = (baseName || (lang + '-fonipa')) + '-u-ca-gregory';
assert.sameValue(instance.toString(), expect,
`new Intl.Locale("${lang}-fonipa-u-ca-gregory", ${optionsRepr}).toString() returns "${expect}"`);
}
75 changes: 66 additions & 9 deletions test/intl402/Locale/getters-grandfathered.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,70 @@ description: >
Verifies getters with grandfathered tags.
info: |
get Intl.Locale.prototype.baseName
5. Return the substring of locale corresponding to the
language ["-" script] ["-" region] *("-" variant)
subsequence of the unicode_language_id grammar.
3. Return GetLocaleBaseName(_loc_.[[Locale]]).

GetLocaleBaseName
2. Return the longest prefix of _locale_ matched by the
<code>unicode_language_id</code> Unicode locale nonterminal.

get Intl.Locale.prototype.language
5. Return the substring of locale corresponding to the
unicode_language_subtag production.
3. Return GetLocaleLanguage(_loc_.[[Locale]]).

GetLocaleLanguage
1. Let _baseName_ be GetLocaleBaseName(_locale_).
2. Assert: The first subtag of _baseName_ can be matched by the
<code>unicode_language_subtag</code> Unicode locale nonterminal.
3. Return the first subtag of _baseName_.

get Intl.Locale.prototype.script
6. Return the substring of locale corresponding to the
unicode_script_subtag production.
3. Return GetLocaleScript(_loc_.[[Locale]]).

GetLocaleScript
1. Let _baseName_ be GetLocaleBaseName(_locale_).
2. Assert: _baseName_ contains at most one subtag that can be matched by the
<code>unicode_script_subtag</code> Unicode locale nonterminal.
3. If _baseName_ contains a subtag matched by the
<code>unicode_script_subtag</code> Unicode locale nonterminal, return
that subtag.
4. Return *undefined*.

get Intl.Locale.prototype.region
6. Return the substring of locale corresponding to the unicode_region_subtag
production.
3. Return GetLocaleRegion(_loc_.[[Locale]]).

GetLocaleRegion
1. Let _baseName_ be GetLocaleBaseName(_locale_).
2. NOTE: A <code>unicode_region_subtag</code> subtag is only valid
immediately after an initial <code>unicode_language_subtag</code> subtag,
optionally with a single <code>unicode_script_subtag</code> subtag
between them. In that position, <code>unicode_region_subtag</code> cannot
be confused with any other valid subtag because all their productions are
disjoint.
3. Assert: The first subtag of _baseName_ can be matched by the
<code>unicode_language_subtag</code> Unicode locale nonterminal.
4. Let _baseNameTail_ be the suffix of _baseName_ following the first
subtag.
5. Assert: _baseNameTail_ contains at most one subtag that can be matched by
the <code>unicode_region_subtag</code> Unicode locale nonterminal.
6. If _baseNameTail_ contains a subtag matched by the
<code>unicode_region_subtag</code> Unicode locale nonterminal, return
that subtag.
7. Return *undefined*.

get Intl.Locale.prototype.variants
3. Return GetLocaleVariants(_loc_.[[Locale]]).

GetLocaleVariants
1. Let _baseName_ be GetLocaleBaseName(_locale_).
2. NOTE: Each subtag in _baseName_ that is preceded by *"-"* is either a
<code>unicode_script_subtag</code>, <code>unicode_region_subtag</code>,
or <code>unicode_variant_subtag</code>, but any substring matched by
<code>unicode_variant_subtag</code> is strictly longer than any prefix
thereof which could also be matched by one of the other productions.
3. Let _variants_ be the longest suffix of _baseName_ that starts with a
*"-"* followed by a <emu-not-ref>substring</emu-not-ref> that is matched
by the <code>unicode_variant_subtag</code> Unicode locale nonterminal. If
there is no such suffix, return *undefined*.
4. Return the substring of _variants_ from 1.
features: [Intl.Locale]
---*/

Expand All @@ -31,6 +80,14 @@ assert.sameValue(loc.baseName, "xtg");
assert.sameValue(loc.language, "xtg");
assert.sameValue(loc.script, undefined);
assert.sameValue(loc.region, undefined);
assert.sameValue(loc.variants, undefined);

loc = new Intl.Locale("cel", { variants: "gaulish" });
assert.sameValue(loc.baseName, "xtg");
assert.sameValue(loc.language, "xtg");
assert.sameValue(loc.script, undefined);
assert.sameValue(loc.region, undefined);
assert.sameValue(loc.variants, undefined);

// Regular grandfathered language tag.
assert.throws(RangeError, () => new Intl.Locale("zh-min"));
Expand Down
4 changes: 4 additions & 0 deletions test/intl402/Locale/getters-missing.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,28 @@ assert.sameValue(loc.baseName, "sv");
assert.sameValue(loc.language, "sv");
assert.sameValue(loc.script, undefined);
assert.sameValue(loc.region, undefined);
assert.sameValue(loc.variants, undefined);

// 'region' subtag not present.
var loc = new Intl.Locale("sv-Latn");
assert.sameValue(loc.baseName, "sv-Latn");
assert.sameValue(loc.language, "sv");
assert.sameValue(loc.script, "Latn");
assert.sameValue(loc.region, undefined);
assert.sameValue(loc.variants, undefined);

// 'script' subtag not present.
var loc = new Intl.Locale("sv-SE");
assert.sameValue(loc.baseName, "sv-SE");
assert.sameValue(loc.language, "sv");
assert.sameValue(loc.script, undefined);
assert.sameValue(loc.region, "SE");
assert.sameValue(loc.variants, undefined);

// 'variant' subtag present.
var loc = new Intl.Locale("de-1901");
assert.sameValue(loc.baseName, "de-1901");
assert.sameValue(loc.language, "de");
assert.sameValue(loc.script, undefined);
assert.sameValue(loc.region, undefined);
assert.sameValue(loc.variants, '1901');
Loading