Skip to content

Commit b6fd532

Browse files
committed
Add tests for Intl.Locale.prototype.variants
Ref tc39/ecma402#960
1 parent bcdafc2 commit b6fd532

File tree

10 files changed

+314
-22
lines changed

10 files changed

+314
-22
lines changed

test/intl402/Locale/constructor-getter-order.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ new Intl.Locale(
4444
}
4545
},
4646

47+
get variants() {
48+
order.push("get variants");
49+
return {
50+
toString() {
51+
order.push("toString variants");
52+
return "fonipa-1996";
53+
}
54+
}
55+
},
56+
4757
get calendar() {
4858
order.push("get calendar");
4959
return {
@@ -109,6 +119,8 @@ const expected_order = [
109119
"toString script",
110120
"get region",
111121
"toString region",
122+
"get variants",
123+
"toString variants",
112124
"get calendar",
113125
"toString calendar",
114126
"get collation",

test/intl402/Locale/constructor-options-throwing-getters.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const options = [
1313
"language",
1414
"script",
1515
"region",
16+
"variants",
1617
"calendar",
1718
"collation",
1819
"hourCycle",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-intl.locale
6+
description: >
7+
Checks error cases for the options argument to the Locale
8+
constructor.
9+
info: |
10+
Intl.Locale( tag [, options] )
11+
12. Set _tag_ to ? UpdateLanguageId(_tag_, _options_).
12+
13+
UpdateLanguageId ( tag, options )
14+
8. Let _variants_ be ? GetOption(_options_, *"variants"*, ~string~, ~empty~, GetLocaleVariants(_baseName_)).
15+
9. If _variants_ is not *undefined*, then
16+
a. If _variants_ cannot be matched by the <code>unicode_variant_subtag</code> Unicode locale nonterminal, throw a *RangeError* exception.
17+
18+
features: [Intl.Locale]
19+
---*/
20+
21+
/*
22+
unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3})
23+
*/
24+
const invalidVariantsOptions = [
25+
"",
26+
"a",
27+
"1",
28+
"ab",
29+
"2x",
30+
"abc",
31+
"3xy",
32+
"abcd",
33+
"12345",
34+
"5wxyz",
35+
"abcdefghi",
36+
37+
// Value contains more than just the 'variants' production.
38+
"GB-scouse",
39+
];
40+
for (const variants of invalidVariantsOptions) {
41+
assert.throws(RangeError, function() {
42+
new Intl.Locale("en", {variants});
43+
}, `new Intl.Locale("en", {variants: "${variants}"}) throws RangeError`);
44+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2025 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-intl.locale
6+
description: >
7+
Checks error cases for the options argument to the Locale
8+
constructor.
9+
info: |
10+
Intl.Locale( tag [, options] )
11+
12. Set _tag_ to ? UpdateLanguageId(_tag_, _options_).
12+
13+
UpdateLanguageId ( tag, options )
14+
8. Let _variants_ be ? GetOption(_options_, *"variants"*, ~string~, ~empty~, GetLocaleVariants(_baseName_)).
15+
...
16+
13. If _variants_ is not *undefined*, set _newTag_ to the string-concatenation of _newTag_, *"-"*, and _variants_.
17+
18+
features: [Intl.Locale]
19+
---*/
20+
21+
const validVariantsOptions = [
22+
['en', undefined, undefined],
23+
['en', 'spanglis', 'en-spanglis'],
24+
];
25+
for (const [lang, variants, baseName] of validVariantsOptions) {
26+
let options = { variants };
27+
let optionsRepr = `{variants: ${variants == null ? variants : `"${variants}"`}}`;
28+
let instance;
29+
let expect;
30+
31+
instance = new Intl.Locale(lang, options);
32+
expect = baseName || lang;
33+
assert.sameValue(instance.toString(), expect,
34+
`new Intl.Locale("${lang}", ${optionsRepr}).toString() returns "${expect}"`);
35+
36+
instance = new Intl.Locale(lang + '-fonipa', options);
37+
expect = baseName || (lang + '-fonipa');
38+
assert.sameValue(instance.toString(), expect,
39+
`new Intl.Locale("${lang}-fonipa", ${optionsRepr}).toString() returns "${expect}"`);
40+
41+
instance = new Intl.Locale(lang + '-u-ca-gregory', options);
42+
expect = (baseName || lang) + '-u-ca-gregory';
43+
assert.sameValue(instance.toString(), expect,
44+
`new Intl.Locale("${lang}-u-ca-gregory", ${optionsRepr}).toString() returns "${expect}"`);
45+
46+
instance = new Intl.Locale(lang + '-fonipa-u-ca-gregory', options);
47+
expect = (baseName || (lang + '-fonipa')) + '-u-ca-gregory';
48+
assert.sameValue(instance.toString(), expect,
49+
`new Intl.Locale("${lang}-fonipa-u-ca-gregory", ${optionsRepr}).toString() returns "${expect}"`);
50+
}

test/intl402/Locale/getters-grandfathered.js

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,70 @@ description: >
77
Verifies getters with grandfathered tags.
88
info: |
99
get Intl.Locale.prototype.baseName
10-
5. Return the substring of locale corresponding to the
11-
language ["-" script] ["-" region] *("-" variant)
12-
subsequence of the unicode_language_id grammar.
10+
3. Return GetLocaleBaseName(_loc_.[[Locale]]).
11+
12+
GetLocaleBaseName
13+
2. Return the longest prefix of _locale_ matched by the
14+
<code>unicode_language_id</code> Unicode locale nonterminal.
1315
1416
get Intl.Locale.prototype.language
15-
5. Return the substring of locale corresponding to the
16-
unicode_language_subtag production.
17+
3. Return GetLocaleLanguage(_loc_.[[Locale]]).
18+
19+
GetLocaleLanguage
20+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
21+
2. Assert: The first subtag of _baseName_ can be matched by the
22+
<code>unicode_language_subtag</code> Unicode locale nonterminal.
23+
3. Return the first subtag of _baseName_.
1724
1825
get Intl.Locale.prototype.script
19-
6. Return the substring of locale corresponding to the
20-
unicode_script_subtag production.
26+
3. Return GetLocaleScript(_loc_.[[Locale]]).
27+
28+
GetLocaleScript
29+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
30+
2. Assert: _baseName_ contains at most one subtag that can be matched by the
31+
<code>unicode_script_subtag</code> Unicode locale nonterminal.
32+
3. If _baseName_ contains a subtag matched by the
33+
<code>unicode_script_subtag</code> Unicode locale nonterminal, return
34+
that subtag.
35+
4. Return *undefined*.
2136
2237
get Intl.Locale.prototype.region
23-
6. Return the substring of locale corresponding to the unicode_region_subtag
24-
production.
38+
3. Return GetLocaleRegion(_loc_.[[Locale]]).
39+
40+
GetLocaleRegion
41+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
42+
2. NOTE: A <code>unicode_region_subtag</code> subtag is only valid
43+
immediately after an initial <code>unicode_language_subtag</code> subtag,
44+
optionally with a single <code>unicode_script_subtag</code> subtag
45+
between them. In that position, <code>unicode_region_subtag</code> cannot
46+
be confused with any other valid subtag because all their productions are
47+
disjoint.
48+
3. Assert: The first subtag of _baseName_ can be matched by the
49+
<code>unicode_language_subtag</code> Unicode locale nonterminal.
50+
4. Let _baseNameTail_ be the suffix of _baseName_ following the first
51+
subtag.
52+
5. Assert: _baseNameTail_ contains at most one subtag that can be matched by
53+
the <code>unicode_region_subtag</code> Unicode locale nonterminal.
54+
6. If _baseNameTail_ contains a subtag matched by the
55+
<code>unicode_region_subtag</code> Unicode locale nonterminal, return
56+
that subtag.
57+
7. Return *undefined*.
58+
59+
get Intl.Locale.prototype.variants
60+
3. Return GetLocaleVariants(_loc_.[[Locale]]).
61+
62+
GetLocaleVariants
63+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
64+
2. NOTE: Each subtag in _baseName_ that is preceded by *"-"* is either a
65+
<code>unicode_script_subtag</code>, <code>unicode_region_subtag</code>,
66+
or <code>unicode_variant_subtag</code>, but any substring matched by
67+
<code>unicode_variant_subtag</code> is strictly longer than any prefix
68+
thereof which could also be matched by one of the other productions.
69+
3. Let _variants_ be the longest suffix of _baseName_ that starts with a
70+
*"-"* followed by a <emu-not-ref>substring</emu-not-ref> that is matched
71+
by the <code>unicode_variant_subtag</code> Unicode locale nonterminal. If
72+
there is no such suffix, return *undefined*.
73+
4. Return the substring of _variants_ from 1.
2574
features: [Intl.Locale]
2675
---*/
2776

@@ -31,6 +80,7 @@ assert.sameValue(loc.baseName, "xtg");
3180
assert.sameValue(loc.language, "xtg");
3281
assert.sameValue(loc.script, undefined);
3382
assert.sameValue(loc.region, undefined);
83+
assert.sameValue(loc.variants, undefined);
3484

3585
// Regular grandfathered language tag.
3686
assert.throws(RangeError, () => new Intl.Locale("zh-min"));

test/intl402/Locale/getters-missing.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,28 @@ assert.sameValue(loc.baseName, "sv");
3030
assert.sameValue(loc.language, "sv");
3131
assert.sameValue(loc.script, undefined);
3232
assert.sameValue(loc.region, undefined);
33+
assert.sameValue(loc.variants, undefined);
3334

3435
// 'region' subtag not present.
3536
var loc = new Intl.Locale("sv-Latn");
3637
assert.sameValue(loc.baseName, "sv-Latn");
3738
assert.sameValue(loc.language, "sv");
3839
assert.sameValue(loc.script, "Latn");
3940
assert.sameValue(loc.region, undefined);
41+
assert.sameValue(loc.variants, undefined);
4042

4143
// 'script' subtag not present.
4244
var loc = new Intl.Locale("sv-SE");
4345
assert.sameValue(loc.baseName, "sv-SE");
4446
assert.sameValue(loc.language, "sv");
4547
assert.sameValue(loc.script, undefined);
4648
assert.sameValue(loc.region, "SE");
49+
assert.sameValue(loc.variants, undefined);
4750

4851
// 'variant' subtag present.
4952
var loc = new Intl.Locale("de-1901");
5053
assert.sameValue(loc.baseName, "de-1901");
5154
assert.sameValue(loc.language, "de");
5255
assert.sameValue(loc.script, undefined);
5356
assert.sameValue(loc.region, undefined);
57+
assert.sameValue(loc.variants, '1901');

test/intl402/Locale/getters.js

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,70 @@ info: |
1010
3. Return loc.[[Locale]].
1111
1212
get Intl.Locale.prototype.baseName
13-
5. Return the substring of locale corresponding to the
14-
language ["-" script] ["-" region] *("-" variant)
15-
subsequence of the langtag grammar.
13+
3. Return GetLocaleBaseName(_loc_.[[Locale]]).
14+
15+
GetLocaleBaseName
16+
2. Return the longest prefix of _locale_ matched by the
17+
<code>unicode_language_id</code> Unicode locale nonterminal.
1618
1719
get Intl.Locale.prototype.language
18-
4. Return the substring of locale corresponding to the language production.
20+
3. Return GetLocaleLanguage(_loc_.[[Locale]]).
21+
22+
GetLocaleLanguage
23+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
24+
2. Assert: The first subtag of _baseName_ can be matched by the
25+
<code>unicode_language_subtag</code> Unicode locale nonterminal.
26+
3. Return the first subtag of _baseName_.
1927
2028
get Intl.Locale.prototype.script
21-
7. Return the substring of locale corresponding to the script production.
29+
3. Return GetLocaleScript(_loc_.[[Locale]]).
30+
31+
GetLocaleScript
32+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
33+
2. Assert: _baseName_ contains at most one subtag that can be matched by the
34+
<code>unicode_script_subtag</code> Unicode locale nonterminal.
35+
3. If _baseName_ contains a subtag matched by the
36+
<code>unicode_script_subtag</code> Unicode locale nonterminal, return
37+
that subtag.
38+
4. Return *undefined*.
2239
2340
get Intl.Locale.prototype.region
24-
7. Return the substring of locale corresponding to the region production.
41+
3. Return GetLocaleRegion(_loc_.[[Locale]]).
42+
43+
GetLocaleRegion
44+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
45+
2. NOTE: A <code>unicode_region_subtag</code> subtag is only valid
46+
immediately after an initial <code>unicode_language_subtag</code> subtag,
47+
optionally with a single <code>unicode_script_subtag</code> subtag
48+
between them. In that position, <code>unicode_region_subtag</code> cannot
49+
be confused with any other valid subtag because all their productions are
50+
disjoint.
51+
3. Assert: The first subtag of _baseName_ can be matched by the
52+
<code>unicode_language_subtag</code> Unicode locale nonterminal.
53+
4. Let _baseNameTail_ be the suffix of _baseName_ following the first
54+
subtag.
55+
5. Assert: _baseNameTail_ contains at most one subtag that can be matched by
56+
the <code>unicode_region_subtag</code> Unicode locale nonterminal.
57+
6. If _baseNameTail_ contains a subtag matched by the
58+
<code>unicode_region_subtag</code> Unicode locale nonterminal, return
59+
that subtag.
60+
7. Return *undefined*.
61+
62+
get Intl.Locale.prototype.variants
63+
3. Return GetLocaleVariants(_loc_.[[Locale]]).
64+
65+
GetLocaleVariants
66+
1. Let _baseName_ be GetLocaleBaseName(_locale_).
67+
2. NOTE: Each subtag in _baseName_ that is preceded by *"-"* is either a
68+
<code>unicode_script_subtag</code>, <code>unicode_region_subtag</code>,
69+
or <code>unicode_variant_subtag</code>, but any substring matched by
70+
<code>unicode_variant_subtag</code> is strictly longer than any prefix
71+
thereof which could also be matched by one of the other productions.
72+
3. Let _variants_ be the longest suffix of _baseName_ that starts with a
73+
*"-"* followed by a <emu-not-ref>substring</emu-not-ref> that is matched
74+
by the <code>unicode_variant_subtag</code> Unicode locale nonterminal. If
75+
there is no such suffix, return *undefined*.
76+
4. Return the substring of _variants_ from 1.
2577
2678
get Intl.Locale.prototype.calendar
2779
3. Return loc.[[Calendar]].
@@ -47,14 +99,15 @@ features: [Intl.Locale]
4799
---*/
48100

49101
// Test all getters return the expected results.
50-
var langtag = "de-latn-de-u-ca-gregory-co-phonebk-hc-h23-kf-true-kn-false-nu-latn";
102+
var langtag = "de-latn-de-fonipa-1996-u-ca-gregory-co-phonebk-hc-h23-kf-true-kn-false-nu-latn";
51103
var loc = new Intl.Locale(langtag);
52104

53-
assert.sameValue(loc.toString(), "de-Latn-DE-u-ca-gregory-co-phonebk-hc-h23-kf-kn-false-nu-latn");
54-
assert.sameValue(loc.baseName, "de-Latn-DE");
105+
assert.sameValue(loc.toString(), "de-Latn-DE-1996-fonipa-u-ca-gregory-co-phonebk-hc-h23-kf-kn-false-nu-latn");
106+
assert.sameValue(loc.baseName, "de-Latn-DE-1996-fonipa");
55107
assert.sameValue(loc.language, "de");
56108
assert.sameValue(loc.script, "Latn");
57109
assert.sameValue(loc.region, "DE");
110+
assert.sameValue(loc.variants, "1996-fonipa");
58111
assert.sameValue(loc.calendar, "gregory");
59112
assert.sameValue(loc.collation, "phonebk");
60113
assert.sameValue(loc.hourCycle, "h23");
@@ -72,6 +125,7 @@ var loc = new Intl.Locale(langtag, {
72125
language: "ja",
73126
script: "jpan",
74127
region: "jp",
128+
variants: "Hepburn",
75129
calendar: "japanese",
76130
collation: "search",
77131
hourCycle: "h24",
@@ -80,11 +134,12 @@ var loc = new Intl.Locale(langtag, {
80134
numberingSystem: "jpanfin",
81135
});
82136

83-
assert.sameValue(loc.toString(), "ja-Jpan-JP-u-ca-japanese-co-search-hc-h24-kf-false-kn-nu-jpanfin");
84-
assert.sameValue(loc.baseName, "ja-Jpan-JP");
137+
assert.sameValue(loc.toString(), "ja-Jpan-JP-hepburn-u-ca-japanese-co-search-hc-h24-kf-false-kn-nu-jpanfin");
138+
assert.sameValue(loc.baseName, "ja-Jpan-JP-hepburn");
85139
assert.sameValue(loc.language, "ja");
86140
assert.sameValue(loc.script, "Jpan");
87141
assert.sameValue(loc.region, "JP");
142+
assert.sameValue(loc.variants, "hepburn");
88143
assert.sameValue(loc.calendar, "japanese");
89144
assert.sameValue(loc.collation, "search");
90145
assert.sameValue(loc.hourCycle, "h24");
@@ -105,11 +160,12 @@ var loc = new Intl.Locale(langtag, {
105160
hourCycle: "h11",
106161
});
107162

108-
assert.sameValue(loc.toString(), "fr-Latn-CA-u-ca-gregory-co-standard-hc-h11-kf-kn-false-nu-latn");
109-
assert.sameValue(loc.baseName, "fr-Latn-CA");
163+
assert.sameValue(loc.toString(), "fr-Latn-CA-1996-fonipa-u-ca-gregory-co-standard-hc-h11-kf-kn-false-nu-latn");
164+
assert.sameValue(loc.baseName, "fr-Latn-CA-1996-fonipa");
110165
assert.sameValue(loc.language, "fr");
111166
assert.sameValue(loc.script, "Latn");
112167
assert.sameValue(loc.region, "CA");
168+
assert.sameValue(loc.variants, "1996-fonipa");
113169
assert.sameValue(loc.calendar, "gregory");
114170
assert.sameValue(loc.collation, "standard");
115171
assert.sameValue(loc.hourCycle, "h11");
@@ -129,6 +185,7 @@ assert.sameValue(loc.baseName, "und");
129185
assert.sameValue(loc.language, "und");
130186
assert.sameValue(loc.script, undefined);
131187
assert.sameValue(loc.region, undefined);
188+
assert.sameValue(loc.variants, undefined);
132189

133190
var loc = new Intl.Locale("und-US-u-co-emoji");
134191

@@ -137,6 +194,7 @@ assert.sameValue(loc.baseName, "und-US");
137194
assert.sameValue(loc.language, "und");
138195
assert.sameValue(loc.script, undefined);
139196
assert.sameValue(loc.region, "US");
197+
assert.sameValue(loc.variants, undefined);
140198
if ("collation" in loc) {
141199
assert.sameValue(loc.collation, "emoji");
142200
}

0 commit comments

Comments
 (0)