Skip to content
Open
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
25 changes: 24 additions & 1 deletion harness/testIntl.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defines:
- getLocaleSupportInfo
- getInvalidLanguageTags
- isCanonicalizedStructurallyValidLanguageTag
- getLocaleBaseName
- getInvalidLocaleArguments
- testOption
- testForUnwantedRegExpChanges
Expand Down Expand Up @@ -1813,6 +1814,28 @@ function isCanonicalizedStructurallyValidLanguageTag(locale) {
}


/**
* Returns the given language tag without any extension or private use
* subtags, like Intl.Locale.prototype.baseName. Use this before appending a
* Unicode extension sequence to a locale obtained from resolvedOptions():
* some hosts report a default locale that already carries one, such as
* "en-US-u-va-posix" under the POSIX locale, and a language tag with two "-u-"
* singletons is not structurally valid.
* @param {string} locale a canonicalized language tag
* @return {string} the language, script, region and variant subtags of locale
*/
function getLocaleBaseName(locale) {
var subtags = locale.split("-");
// Every subtag after the language is at least two characters long, except
// for the singletons that introduce extension and private use sequences.
for (var i = 1; i < subtags.length; i++) {
if (subtags[i].length === 1) {
return subtags.slice(0, i).join("-");
}
}
return locale;
}

/**
* Returns an array of error cases handled by CanonicalizeLocaleList().
*/
Expand Down Expand Up @@ -2387,7 +2410,7 @@ function testNumberFormat(locales, numberingSystems, options, testData) {
locales.forEach(function (locale) {
numberingSystems.forEach(function (numbering) {
var digits = numberingSystemDigits[numbering];
var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
var format = new Intl.NumberFormat([getLocaleBaseName(locale) + "-u-nu-" + numbering], options);

function getPatternParts(positive) {
var n = positive ? 1.1 : -1.1;
Expand Down
6 changes: 3 additions & 3 deletions test/intl402/Collator/ignore-invalid-unicode-ext-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >
Tests that Intl.Collator does not accept Unicode locale extension
keys and values that are not allowed.
author: Norbert Lindenberg
includes: [compareArray.js]
includes: [compareArray.js, testIntl.js]
---*/

var testArray = [
Expand All @@ -17,10 +17,10 @@ var testArray = [
"Å", "Å", "A\u030A"
];

var defaultCollator = new Intl.Collator();
var defaultLocale = getLocaleBaseName(new Intl.Collator().resolvedOptions().locale);
var defaultCollator = new Intl.Collator([defaultLocale]);
var defaultOptions = defaultCollator.resolvedOptions();
var defaultOptionsJSON = JSON.stringify(defaultOptions);
var defaultLocale = defaultOptions.locale;
var defaultSortedArray = testArray.slice(0).sort(defaultCollator.compare);

var keyValues = {
Expand Down
3 changes: 2 additions & 1 deletion test/intl402/Collator/numeric-and-caseFirst.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description: >
Tests that the options numeric and caseFirst can be set through
either the locale or the options.
author: Norbert Lindenberg
includes: [testIntl.js]
---*/

var options = [
Expand All @@ -15,7 +16,7 @@ var options = [
];

options.forEach(function (option) {
var defaultLocale = new Intl.Collator().resolvedOptions().locale;
var defaultLocale = getLocaleBaseName(new Intl.Collator().resolvedOptions().locale);
var collator, opt, result;

// find out which values are supported for a property in the default locale
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ description: >
to lower case properly.
author: Caio Lima
features: [Array.prototype.includes]
includes: [testIntl.js]
---*/

let defaultLocale = new Intl.DateTimeFormat().resolvedOptions().locale;
let defaultLocale = getLocaleBaseName(new Intl.DateTimeFormat().resolvedOptions().locale);

let supportedNumberingSystems = ["latn", "arab"].filter(nu =>
new Intl.DateTimeFormat(defaultLocale + "-u-nu-" + nu)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ description: >
Tests that the options numberingSystem and calendar can be set through
either the locale or the options.
author: Norbert Lindenberg, Daniel Ehrenberg
includes: [testIntl.js]
---*/

let defaultLocale = new Intl.DateTimeFormat().resolvedOptions().locale;
let defaultLocale = getLocaleBaseName(new Intl.DateTimeFormat().resolvedOptions().locale);

let supportedNumberingSystems = ["latn", "arab"].filter(nu =>
new Intl.DateTimeFormat(defaultLocale + "-u-nu-" + nu)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ info: |
a. Let value be optionsValue.
b. Let supportedExtensionAddition be "".
...
includes: [testIntl.js]
---*/

var defaultLocale = new Intl.DateTimeFormat().resolvedOptions().locale;
var defaultLocale = getLocaleBaseName(new Intl.DateTimeFormat().resolvedOptions().locale);
var defaultLocaleWithHourCycle = defaultLocale + "-u-hc-h11";

function assertLocale(locale, expectedLocale, options, message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ description: >
Tests that the options numberingSystem are mapped to lower case.
author: Caio Lima
features: [Array.prototype.includes]
includes: [testIntl.js]
---*/

let defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
let defaultLocale = getLocaleBaseName(new Intl.NumberFormat().resolvedOptions().locale);

let supportedNumberingSystems = ["latn", "arab"].filter(nu =>
new Intl.NumberFormat(defaultLocale + "-u-nu-" + nu)
Expand Down
3 changes: 2 additions & 1 deletion test/intl402/NumberFormat/numbering-system-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ description: >
Tests that the options numberingSystem and calendar can be set through
either the locale or the options.
author: Norbert Lindenberg, Daniel Ehrenberg
includes: [testIntl.js]
---*/

let defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
let defaultLocale = getLocaleBaseName(new Intl.NumberFormat().resolvedOptions().locale);

let supportedNumberingSystems = ["latn", "arab"].filter(nu =>
new Intl.NumberFormat(defaultLocale + "-u-nu-" + nu)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-torawprecision
description: >
Rounding to significant digits when the rounded value reaches the next order
of magnitude
info: |
ToRawPrecision ( x, minPrecision, maxPrecision, unsignedRoundingMode )

1. Let p be maxPrecision.
...
3. Else,
a. Let n1 and e1 each be an integer and r1 a mathematical value, with
r1 = ToRawPrecisionFn(n1, e1, p), such that r1 ≤ x and r1 is maximized.
b. Let n2 and e2 each be an integer and r2 a mathematical value, with
r2 = ToRawPrecisionFn(n2, e2, p), such that r2 ≥ x and r2 is minimized.
c. Let xFinal be ApplyUnsignedRoundingMode(x, r1, r2, unsignedRoundingMode).
d. If xFinal is r1, then
i. Let n be n1.
ii. Let e be e1.
e. Else,
i. Let n be n2.
ii. Let e be e2.
f. Let m be the String consisting of the digits of the decimal
representation of n (in order, with no leading zeroes).
4. If e ≥ (p - 1), then
a. Set m to the string-concatenation of m and e - p + 1 occurrences of the
code unit 0x0030 (DIGIT ZERO).
b. Let int be e + 1.
5. Else if e ≥ 0, then
a. Set m to the string-concatenation of the first e + 1 code units of m,
the code unit 0x002E (FULL STOP), and the remaining p - (e + 1) code
units of m.
b. Let int be e + 1.
6. Else,
a. Assert: e < 0.
b. Set m to the string-concatenation of "0.", -(e + 1) occurrences of the
code unit 0x0030 (DIGIT ZERO), and m.
c. Let int be 1.
7. If m contains the code unit 0x002E (FULL STOP) and
maxPrecision > minPrecision, then
a. Let cut be maxPrecision - minPrecision.
b. Repeat, while cut > 0 and the last code unit of m is 0x0030
(DIGIT ZERO),
i. Remove the last code unit from m.
ii. Set cut to cut - 1.
c. If the last code unit of m is 0x002E (FULL STOP), then
i. Remove the last code unit from m.
includes: [testIntl.js]
---*/

var locales = [
new Intl.NumberFormat().resolvedOptions().locale,
"ar", "de", "th", "ja"
];
var numberingSystems = ["latn", "arab", "thai", "hanidec"];

// minimumSignificantDigits = maximumSignificantDigits, so no trailing zeros
// are trimmed and the digit count of the result is visible directly.
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, minimumSignificantDigits: 3, maximumSignificantDigits: 3 },
{
// step 6: e = -3 → e = -2 (still e < 0)
"0.0009999": "0.00100",
"-0.0009999": "-0.00100",
// step 6: e = -2 → e = -1
"0.09999": "0.100",
"-0.09999": "-0.100",
// step 6 (e = -1) → step 5 (e = 0)
"0.9999": "1.00",
"-0.9999": "-1.00",
// step 5: e = 0 → e = 1
"9.999": "10.0",
"-9.999": "-10.0",
// step 5 (e = 1) → step 4 (e = 2 = p - 1)
"99.99": "100",
"-99.99": "-100",
// step 4: e = 2 → e = 3
"999.9": "1000",
"-999.9": "-1000",
// step 4: e = 5 → e = 6
"999999.9": "1000000",
"-999999.9": "-1000000",
// control values that round without changing magnitude
"9.994": "9.99",
"99.94": "99.9",
"0.9994": "0.999",
});

// Default minimumSignificantDigits of 1: rollover still happens, and the
// zeros that appear after the FULL STOP are trimmed by step 7.
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, maximumSignificantDigits: 3 },
{
"0.0009999": "0.001",
"0.09999": "0.1",
"0.9999": "1",
"-0.9999": "-1",
"9.999": "10",
"-9.999": "-10",
"99.99": "100",
"-99.99": "-100",
"999.9": "1000",
"999999.9": "1000000",
});

// Lower minimum: step 7 trims at most maxPrecision - minPrecision zeros.
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, minimumSignificantDigits: 2, maximumSignificantDigits: 3 },
{
"0.9999": "1.0",
"9.999": "10",
"99.99": "100",
});

// Two significant digits
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, minimumSignificantDigits: 2, maximumSignificantDigits: 2 },
{
"0.996": "1.0",
"9.96": "10",
"99.6": "100",
"996": "1000",
});

// Larger precision
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, minimumSignificantDigits: 5, maximumSignificantDigits: 5 },
{
"9.99996": "10.000",
"99999.6": "100000",
"0.999996": "1.0000",
});

// The rounded value is always compared against x; a value just below the
// rounding boundary must not roll over.
testNumberFormat(locales, numberingSystems,
{ useGrouping: false, minimumSignificantDigits: 3, maximumSignificantDigits: 3 },
{
"9.9949": "9.99",
"99.949": "99.9",
"999.49": "999",
});
Loading