diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/non262/Intl | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/non262/Intl')
220 files changed, 32913 insertions, 0 deletions
diff --git a/js/src/tests/non262/Intl/Array/shell.js b/js/src/tests/non262/Intl/Array/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Array/shell.js diff --git a/js/src/tests/non262/Intl/Array/toLocaleString-date.js b/js/src/tests/non262/Intl/Array/toLocaleString-date.js new file mode 100644 index 0000000000..7711c60643 --- /dev/null +++ b/js/src/tests/non262/Intl/Array/toLocaleString-date.js @@ -0,0 +1,53 @@ +if (typeof Intl === "object") { + const localeSep = [,,].toLocaleString(); + + const date = new Date(Date.UTC(2012, 11, 12, 3, 0, 0)); + + assertEq([date].toLocaleString("en-us", {timeZone: "UTC"}), "12/12/2012, 3:00:00 AM"); + assertEq([date].toLocaleString(["de", "en"], {timeZone: "UTC"}), "12.12.2012, 03:00:00"); + assertEq([date].toLocaleString("th-th", {timeZone: "UTC"}), "12/12/2555 03:00:00"); + assertEq([date].toLocaleString("th-th-u-nu-thai", {timeZone: "UTC"}), "๑๒/๑๒/๒๕๕๕ ๐๓:๐๐:๐๐"); + + const sampleValues = [ + date, new Date(0), + ]; + const sampleLocales = [ + void 0, + "en", + "th-th-u-nu-thai", + "ja-jp", + "ar-ma-u-ca-islamicc", + ["tlh", "de"], + ]; + const numericFormatOptions = { + timeZone: "UTC", + year: "numeric", month: "numeric", day: "numeric", + hour: "numeric", minute: "numeric", second: "numeric", + }; + const longFormatOptions = { + timeZone: "UTC", + year: "numeric", month: "long", day: "numeric", + hour: "numeric", minute: "numeric", second: "numeric" + }; + const sampleOptions = [ + {timeZone: "UTC"}, + longFormatOptions, + ]; + + for (let locale of sampleLocales) { + for (let options of sampleOptions) { + let dtfOptions; + if (options === longFormatOptions) { + dtfOptions = longFormatOptions; + } else { + dtfOptions = numericFormatOptions; + } + let dtf = new Intl.DateTimeFormat(locale, dtfOptions); + let expected = sampleValues.map(dtf.format).join(localeSep); + assertEq(sampleValues.toLocaleString(locale, options), expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Array/toLocaleString-number.js b/js/src/tests/non262/Intl/Array/toLocaleString-number.js new file mode 100644 index 0000000000..6ca8a17837 --- /dev/null +++ b/js/src/tests/non262/Intl/Array/toLocaleString-number.js @@ -0,0 +1,34 @@ +if (typeof Intl === "object") { + const localeSep = [,,].toLocaleString(); + + assertEq([NaN].toLocaleString("ar"), "ليس رقم"); + assertEq([NaN].toLocaleString(["zh-hant", "ar"]), "非數值"); + assertEq([Infinity].toLocaleString("dz"), "གྲངས་མེད"); + assertEq([-Infinity].toLocaleString(["fr", "en"]), "-∞"); + + const sampleValues = [ + -0, +0, -1, +1, -2, +2, -0.5, +0.5, + ]; + const sampleLocales = [ + void 0, + "en", + "th-th-u-nu-thai", + ["tlh", "de"], + ]; + const sampleOptions = [ + void 0, + {}, + {style: "percent"}, + {style: "currency", currency: "USD", minimumIntegerDigits: 4}, + ]; + for (let locale of sampleLocales) { + for (let options of sampleOptions) { + let nf = new Intl.NumberFormat(locale, options); + let expected = sampleValues.map(nf.format).join(localeSep); + assertEq(sampleValues.toLocaleString(locale, options), expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Array/toLocaleString.js b/js/src/tests/non262/Intl/Array/toLocaleString.js new file mode 100644 index 0000000000..f94e531f89 --- /dev/null +++ b/js/src/tests/non262/Intl/Array/toLocaleString.js @@ -0,0 +1,35 @@ +if (typeof Intl === "object") { + const localeSep = [,,].toLocaleString(); + + // Missing arguments are passed as |undefined|. + const objNoArgs = { + toLocaleString() { + assertEq(arguments.length, 2); + assertEq(arguments[0], undefined); + assertEq(arguments[1], undefined); + return "pass"; + } + }; + // - Single element case. + assertEq([objNoArgs].toLocaleString(), "pass"); + // - More than one element. + assertEq([objNoArgs, objNoArgs].toLocaleString(), "pass" + localeSep + "pass"); + + // Ensure "locales" and "options" arguments are passed to the array elements. + const locales = {}, options = {}; + const objWithArgs = { + toLocaleString() { + assertEq(arguments.length, 2); + assertEq(arguments[0], locales); + assertEq(arguments[1], options); + return "pass"; + } + }; + // - Single element case. + assertEq([objWithArgs].toLocaleString(locales, options), "pass"); + // - More than one element. + assertEq([objWithArgs, objWithArgs].toLocaleString(locales, options), "pass" + localeSep + "pass"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Collator/big5han-gb2312han.js b/js/src/tests/non262/Intl/Collator/big5han-gb2312han.js new file mode 100644 index 0000000000..cf6870ff7d --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/big5han-gb2312han.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +let scrambled = ['𠙶', '𠇲', '㓙', '㑧', '假', '凷']; + +// Root or pinyin +const fallback = ["假", "凷", "㑧", "㓙", "𠇲", "𠙶"]; + +scrambled.sort(new Intl.Collator("zh-u-co-big5han").compare); +assertEqArray(scrambled, fallback); + +scrambled.sort(new Intl.Collator("zh-u-co-gb2312").compare); +assertEqArray(scrambled, fallback); + +assertEq(new Intl.Collator("zh-u-co-big5han").resolvedOptions().collation, "default"); +assertEq(new Intl.Collator("zh-u-co-gb2312").resolvedOptions().collation, "default"); + +assertEq(Intl.supportedValuesOf("collation").includes("big5han"), false); +assertEq(Intl.supportedValuesOf("collation").includes("gb2312"), false); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/Collator/browser.js b/js/src/tests/non262/Intl/Collator/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/browser.js diff --git a/js/src/tests/non262/Intl/Collator/call.js b/js/src/tests/non262/Intl/Collator/call.js new file mode 100644 index 0000000000..089764a2cb --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/call.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function IsObject(o) { + return Object(o) === o; +} + +function thisValues() { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.map(ctor => { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Object inheriting from an Intl constructor prototype. + Object.create(ctor.prototype), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; + })), + ]; +} + +// Invoking [[Call]] for Intl.Collator always returns a new Collator instance. +for (let thisValue of thisValues()) { + let obj = Intl.Collator.call(thisValue); + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.Collator, true); + + // Ensure Intl.[[FallbackSymbol]] wasn't installed on |thisValue|. + if (IsObject(thisValue)) + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); +} + +// Intl.Collator doesn't use the legacy Intl constructor compromise semantics. +for (let thisValue of thisValues()) { + // Ensure instanceof operator isn't invoked for Intl.Collator. + Object.defineProperty(Intl.Collator, Symbol.hasInstance, { + get() { + assertEq(false, true, "@@hasInstance operator called"); + }, configurable: true + }); + let obj = Intl.Collator.call(thisValue); + delete Intl.Collator[Symbol.hasInstance]; + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.Collator, true); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Collator/caseFirst.js b/js/src/tests/non262/Intl/Collator/caseFirst.js new file mode 100644 index 0000000000..d183c8fdd5 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/caseFirst.js @@ -0,0 +1,197 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Locales which use caseFirst=off for the standard (sort) collation type. +const defaultLocales = Intl.Collator.supportedLocalesOf(["en", "de", "es", "sv", "ar", "zh", "ja"]); + +// Locales which use caseFirst=upper for the standard (sort) collation type. +const upperFirstLocales = Intl.Collator.supportedLocalesOf(["cu", "da", "mt"]); + +// Default collation for zh (pinyin) reorders "á" before "a" at secondary strength level. +const accentReordered = ["zh"]; + +const allLocales = [...defaultLocales, ...upperFirstLocales]; + + +// Check default "caseFirst" option is resolved correctly. +for (let locale of defaultLocales) { + let col = new Intl.Collator(locale, {usage: "sort"}); + assertEq(col.resolvedOptions().caseFirst, "false"); +} +for (let locale of upperFirstLocales) { + let col = new Intl.Collator(locale, {usage: "sort"}); + assertEq(col.resolvedOptions().caseFirst, "upper"); +} +for (let locale of allLocales) { + let col = new Intl.Collator(locale, {usage: "search"}); + assertEq(col.resolvedOptions().caseFirst, "false"); +} + + +const collOptions = {usage: "sort"}; +const primary = {sensitivity: "base"}; +const secondary = {sensitivity: "accent"}; +const tertiary = {sensitivity: "variant"}; +const caseLevel = {sensitivity: "case"}; +const strengths = [primary, secondary, tertiary, caseLevel]; + +// "A" is sorted after "a" when caseFirst=off is the default and strength is tertiary. +for (let locale of defaultLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary)); + + assertEq(col.compare("A", "a"), 1); + assertEq(col.compare("a", "A"), -1); +} +for (let locale of defaultLocales.filter(loc => !accentReordered.includes(loc))) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary)); + + assertEq(col.compare("A", "á"), -1); + assertEq(col.compare("á", "A"), 1); +} + +// Also sorted after "a" with the sensitivity=case collator. +for (let locale of defaultLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, caseLevel)); + + assertEq(col.compare("A", "a"), 1); + assertEq(col.compare("a", "A"), -1); + + assertEq(col.compare("A", "á"), 1); + assertEq(col.compare("á", "A"), -1); +} + + +// "A" is sorted before "a" when caseFirst=upper is the default and strength is tertiary. +for (let locale of upperFirstLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary)); + + assertEq(col.compare("A", "a"), -1); + assertEq(col.compare("a", "A"), 1); + + assertEq(col.compare("A", "á"), -1); + assertEq(col.compare("á", "A"), 1); +} + +// Also sorted before "a" with the sensitivity=case collator. +for (let locale of upperFirstLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, caseLevel)); + + assertEq(col.compare("A", "a"), -1); + assertEq(col.compare("a", "A"), 1); + + assertEq(col.compare("A", "á"), -1); + assertEq(col.compare("á", "A"), 1); +} + + +// caseFirst=upper doesn't change the sort order when strength is below tertiary. +for (let locale of allLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, secondary)); + + assertEq(col.compare("A", "a"), 0); + assertEq(col.compare("a", "A"), 0); +} +for (let locale of allLocales.filter(loc => !accentReordered.includes(loc))) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, secondary)); + + assertEq(col.compare("A", "á"), -1); + assertEq(col.compare("á", "A"), 1); +} + +for (let locale of allLocales) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, primary)); + + assertEq(col.compare("A", "a"), 0); + assertEq(col.compare("a", "A"), 0); + + assertEq(col.compare("A", "á"), 0); + assertEq(col.compare("á", "A"), 0); +} + + +// caseFirst=upper doesn't change the sort order when there's a primary difference. +for (let locale of allLocales) { + for (let strength of strengths) { + let col = new Intl.Collator(locale, Object.assign({}, collOptions, strength)); + + assertEq(col.compare("A", "b"), -1); + assertEq(col.compare("a", "B"), -1); + } +} + + +// caseFirst set through Unicode extension tag. +for (let locale of allLocales) { + let colKfFalse = new Intl.Collator(locale + "-u-kf-false", {}); + let colKfLower = new Intl.Collator(locale + "-u-kf-lower", {}); + let colKfUpper = new Intl.Collator(locale + "-u-kf-upper", {}); + + assertEq(colKfFalse.resolvedOptions().caseFirst, "false"); + assertEq(colKfFalse.compare("A", "a"), 1); + assertEq(colKfFalse.compare("a", "A"), -1); + + assertEq(colKfLower.resolvedOptions().caseFirst, "lower"); + assertEq(colKfLower.compare("A", "a"), 1); + assertEq(colKfLower.compare("a", "A"), -1); + + assertEq(colKfUpper.resolvedOptions().caseFirst, "upper"); + assertEq(colKfUpper.compare("A", "a"), -1); + assertEq(colKfUpper.compare("a", "A"), 1); +} + + +// caseFirst set through options value. +for (let locale of allLocales) { + let colKfFalse = new Intl.Collator(locale, {caseFirst: "false"}); + let colKfLower = new Intl.Collator(locale, {caseFirst: "lower"}); + let colKfUpper = new Intl.Collator(locale, {caseFirst: "upper"}); + + assertEq(colKfFalse.resolvedOptions().caseFirst, "false"); + assertEq(colKfFalse.compare("A", "a"), 1); + assertEq(colKfFalse.compare("a", "A"), -1); + + assertEq(colKfLower.resolvedOptions().caseFirst, "lower"); + assertEq(colKfLower.compare("A", "a"), 1); + assertEq(colKfLower.compare("a", "A"), -1); + + assertEq(colKfUpper.resolvedOptions().caseFirst, "upper"); + assertEq(colKfUpper.compare("A", "a"), -1); + assertEq(colKfUpper.compare("a", "A"), 1); +} + + +// Test Unicode extension tag and options value, the latter should win. +for (let locale of allLocales) { + let colKfFalse = new Intl.Collator(locale + "-u-kf-upper", {caseFirst: "false"}); + let colKfLower = new Intl.Collator(locale + "-u-kf-upper", {caseFirst: "lower"}); + let colKfUpper = new Intl.Collator(locale + "-u-kf-lower", {caseFirst: "upper"}); + + assertEq(colKfFalse.resolvedOptions().caseFirst, "false"); + assertEq(colKfFalse.compare("A", "a"), 1); + assertEq(colKfFalse.compare("a", "A"), -1); + + assertEq(colKfLower.resolvedOptions().caseFirst, "lower"); + assertEq(colKfLower.compare("A", "a"), 1); + assertEq(colKfLower.compare("a", "A"), -1); + + assertEq(colKfUpper.resolvedOptions().caseFirst, "upper"); + assertEq(colKfUpper.compare("A", "a"), -1); + assertEq(colKfUpper.compare("a", "A"), 1); +} + +// Ensure languages are properly detected when additional subtags are present. +if (Intl.Collator.supportedLocalesOf("da").length !== 0) { + assertEq(new Intl.Collator("da-DK", {usage: "sort"}).resolvedOptions().caseFirst, "upper"); + assertEq(new Intl.Collator("da-Latn-DK", {usage: "sort"}).resolvedOptions().caseFirst, "upper"); +} +if (Intl.Collator.supportedLocalesOf("mt").length !== 0) { + assertEq(new Intl.Collator("mt-MT", {usage: "sort"}).resolvedOptions().caseFirst, "upper"); + assertEq(new Intl.Collator("mt-Latn-MT", {usage: "sort"}).resolvedOptions().caseFirst, "upper"); +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/Collator/collation.js b/js/src/tests/non262/Intl/Collator/collation.js new file mode 100644 index 0000000000..f73728b876 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/collation.js @@ -0,0 +1,90 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Collation can be set as Unicode locale extension or as a property. +{ + let c1 = new Intl.Collator("de", {usage: "sort"}); + let c2 = new Intl.Collator("de", {usage: "sort", collation: "phonebk"}); + let c3 = new Intl.Collator("de-u-co-phonebk", {usage: "sort"}); + + assertEq(c1.resolvedOptions().locale, "de"); + assertEq(c2.resolvedOptions().locale, "de"); + assertEq(c3.resolvedOptions().locale, "de-u-co-phonebk"); + + assertEq(c1.resolvedOptions().collation, "default"); + assertEq(c2.resolvedOptions().collation, "phonebk"); + assertEq(c3.resolvedOptions().collation, "phonebk"); + + assertEq(c1.compare("ä", "ae"), -1); + assertEq(c2.compare("ä", "ae"), 1); + assertEq(c3.compare("ä", "ae"), 1); +} + +// Collation property overrides any Unicode locale extension. +{ + let c1 = new Intl.Collator("de-u-co-eor", {usage: "sort"}); + let c2 = new Intl.Collator("de-u-co-eor", {usage: "sort", collation: "phonebk"}); + + // Ensure "eor" collation is supported. + assertEq(c1.resolvedOptions().locale, "de-u-co-eor"); + assertEq(c1.resolvedOptions().collation, "eor"); + + // "phonebk" property overrides the Unicode locale extension. + assertEq(c2.resolvedOptions().locale, "de"); + assertEq(c2.resolvedOptions().collation, "phonebk"); + + assertEq(c1.compare("ä", "ae"), -1); + assertEq(c2.compare("ä", "ae"), 1); +} + +// The default sort collation can't be requested. +{ + // The default sort collation for Swedish (sv) was "reformed" before CLDR 42. + // It wasn't possible to override this and select the default root sort + // collation. Use English (en) as a locale which uses the root sort collation + // for comparison. + let c1 = new Intl.Collator("sv", {usage: "sort"}); + let c2 = new Intl.Collator("sv-u-co-reformed", {usage: "sort"}); + let c3 = new Intl.Collator("sv-u-co-standard", {usage: "sort"}); + let c4 = new Intl.Collator("sv-u-co-default", {usage: "sort"}); + let c5 = new Intl.Collator("en", {usage: "sort"}); + + assertEq(c1.resolvedOptions().locale, "sv"); + assertEq(c2.resolvedOptions().locale, "sv"); + assertEq(c3.resolvedOptions().locale, "sv"); + assertEq(c4.resolvedOptions().locale, "sv"); + assertEq(c5.resolvedOptions().locale, "en"); + + assertEq(c1.resolvedOptions().collation, "default"); + assertEq(c2.resolvedOptions().collation, "default"); + assertEq(c3.resolvedOptions().collation, "default"); + assertEq(c4.resolvedOptions().collation, "default"); + assertEq(c5.resolvedOptions().collation, "default"); + + assertEq(c1.compare("y", "ü"), -1); + assertEq(c2.compare("y", "ü"), -1); + assertEq(c3.compare("y", "ü"), -1); + assertEq(c4.compare("y", "ü"), -1); + assertEq(c5.compare("y", "ü"), 1); +} + +// Search collations ignore any collation overrides. +{ + let c1 = new Intl.Collator("de", {usage: "search"}); + let c2 = new Intl.Collator("de", {usage: "search", collation: "phonebk"}); + let c3 = new Intl.Collator("de-u-co-phonebk", {usage: "search"}); + + assertEq(c1.resolvedOptions().locale, "de"); + assertEq(c2.resolvedOptions().locale, "de"); + assertEq(c3.resolvedOptions().locale, "de"); + + assertEq(c1.resolvedOptions().collation, "default"); + assertEq(c2.resolvedOptions().collation, "default"); + assertEq(c3.resolvedOptions().collation, "default"); + + assertEq(c1.compare("ä", "ae"), 1); + assertEq(c2.compare("ä", "ae"), 1); + assertEq(c3.compare("ä", "ae"), 1); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/Collator/compare.js b/js/src/tests/non262/Intl/Collator/compare.js new file mode 100644 index 0000000000..6f57c722dd --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/compare.js @@ -0,0 +1,137 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the compare function with a diverse set of locales and options. + +var input = [ + "Argentina", + "Oerlikon", + "Offenbach", + "Sverige", + "Vaticano", + "Zimbabwe", + "la France", + "¡viva España!", + "Österreich", + "中国", + "日本", + "한국", +]; + +var collator, expected; + +function assertEqualArray(actual, expected, collator) { + var description = JSON.stringify(collator.resolvedOptions()); + assertEq(actual.length, expected.length, "array length, " + description); + for (var i = 0; i < actual.length; i++) { + assertEq(actual[i], expected[i], "element " + i + ", " + description); + } +} + + +// Locale en-US; default options. +collator = new Intl.Collator("en-US"); +expected = [ + "¡viva España!", + "Argentina", + "la France", + "Oerlikon", + "Offenbach", + "Österreich", + "Sverige", + "Vaticano", + "Zimbabwe", + "한국", + "中国", + "日本", +]; +assertEqualArray(input.sort(collator.compare), expected, collator); + +// Locale sv-SE; default options. +// Swedish treats "Ö" as a separate character, which sorts after "Z". +collator = new Intl.Collator("sv-SE"); +expected = [ + "¡viva España!", + "Argentina", + "la France", + "Oerlikon", + "Offenbach", + "Sverige", + "Vaticano", + "Zimbabwe", + "Österreich", + "한국", + "中国", + "日本", +]; +assertEqualArray(input.sort(collator.compare), expected, collator); + +// Locale sv-SE; ignore punctuation. +collator = new Intl.Collator("sv-SE", {ignorePunctuation: true}); +expected = [ + "Argentina", + "la France", + "Oerlikon", + "Offenbach", + "Sverige", + "Vaticano", + "¡viva España!", + "Zimbabwe", + "Österreich", + "한국", + "中国", + "日本", +]; +assertEqualArray(input.sort(collator.compare), expected, collator); + +// Locale de-DE; default options. +// In German standard sorting, umlauted characters are treated as variants +// of their base characters: ä ≅ a, ö ≅ o, ü ≅ u. +collator = new Intl.Collator("de-DE"); +expected = [ + "¡viva España!", + "Argentina", + "la France", + "Oerlikon", + "Offenbach", + "Österreich", + "Sverige", + "Vaticano", + "Zimbabwe", + "한국", + "中国", + "日本", +]; +assertEqualArray(input.sort(collator.compare), expected, collator); + +// Locale de-DE; phonebook sort order. +// In German phonebook sorting, umlauted characters are expanded to two-vowel +// sequences: ä → ae, ö → oe, ü → ue. +collator = new Intl.Collator("de-DE-u-co-phonebk"); +expected = [ + "¡viva España!", + "Argentina", + "la France", + "Oerlikon", + "Österreich", + "Offenbach", + "Sverige", + "Vaticano", + "Zimbabwe", + "한국", + "中国", + "日本", +]; +assertEqualArray(input.sort(collator.compare), expected, collator); + + +// Test the .name property of the "compare" getter. +var desc = Object.getOwnPropertyDescriptor(Intl.Collator.prototype, "compare"); +assertEq(desc !== undefined, true); +assertEq(typeof desc.get, "function"); +assertEq(desc.get.name, "get compare"); + + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/Collator/construct-newtarget.js b/js/src/tests/non262/Intl/Collator/construct-newtarget.js new file mode 100644 index 0000000000..5db1abf373 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +// Test subclassing %Intl.Collator% works correctly. +class MyCollator extends Intl.Collator {} + +var obj = new MyCollator(); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, []); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, [], MyCollator); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, [], Intl.Collator); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyCollator, [], Array); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.Collator, [], Array); +assertEq(obj instanceof Intl.Collator, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %CollatorPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.Collator, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + +obj = Reflect.construct(MyCollator, [], NewTargetNullPrototype); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.Collator, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.Collator, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Collator/cross-compartment.js b/js/src/tests/non262/Intl/Collator/cross-compartment.js new file mode 100644 index 0000000000..a8cf3134ca --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/cross-compartment.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var otherGlobal = newGlobal(); + +var collator = new Intl.Collator(); +var ccwCollator = new otherGlobal.Intl.Collator(); + +// Test Intl.Collator.prototype.compare with a CCW object. +var Intl_Collator_compare_get = Object.getOwnPropertyDescriptor(Intl.Collator.prototype, "compare").get; + +assertEq(Intl_Collator_compare_get.call(ccwCollator)("a", "A"), + Intl_Collator_compare_get.call(collator)("a", "A")); + +// Test Intl.Collator.prototype.resolvedOptions with a CCW object. +var Intl_Collator_resolvedOptions = Intl.Collator.prototype.resolvedOptions; + +assertEq(deepEqual(Intl_Collator_resolvedOptions.call(ccwCollator), + Intl_Collator_resolvedOptions.call(collator)), + true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Collator/implicithan.js b/js/src/tests/non262/Intl/Collator/implicithan.js new file mode 100644 index 0000000000..518ecdb43e --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/implicithan.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +let scrambled = ['𠙶', '𠇲', '㓙', '㑧', '假', '凷']; + +// Sort first by block and then by radical-stroke inside each block. +// This matches the ICU/ICU4X implicithan root order, which is used +// by Chrome as of October 2022. (As of October 2022, Safari uses +// the unihan root order, which uses more data but uses radical-stroke +// across blocks.) +const byBlock = ['假', '凷', '㑧', '㓙', '𠇲', '𠙶']; + +scrambled.sort(new Intl.Collator().compare); +assertEqArray(scrambled, byBlock); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/Collator/shell.js b/js/src/tests/non262/Intl/Collator/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/shell.js diff --git a/js/src/tests/non262/Intl/Collator/supportedLocalesOf.js b/js/src/tests/non262/Intl/Collator/supportedLocalesOf.js new file mode 100644 index 0000000000..7fbebca094 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/supportedLocalesOf.js @@ -0,0 +1,355 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell) +// -- test in browser only that ICU has locale data for all Mozilla languages + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This array contains the locales that ICU supports in +// collation whose languages Mozilla localizes Firefox into. +// Current as of ICU 50.1.2 and Firefox March 2013. +var locales = [ + "af", + "af-NA", + "af-ZA", + "ar", + "ar-001", + "ar-AE", + "ar-BH", + "ar-DJ", + "ar-DZ", + "ar-EG", + "ar-EH", + "ar-ER", + "ar-IL", + "ar-IQ", + "ar-JO", + "ar-KM", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-MR", + "ar-OM", + "ar-PS", + "ar-QA", + "ar-SA", + "ar-SD", + "ar-SO", + "ar-SY", + "ar-TD", + "ar-TN", + "ar-YE", + "as", + "as-IN", + "be", + "be-BY", + "bg", + "bg-BG", + "bn", + "bn-BD", + "bn-IN", + "bs", + "bs-Cyrl", + "bs-Cyrl-BA", + "bs-Latn", + "bs-Latn-BA", + "ca", + "ca-AD", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-BE", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "el", + "el-CY", + "el-GR", + "en", + "en-150", + "en-AG", + "en-AS", + "en-AU", + "en-BB", + "en-BE", + "en-BM", + "en-BS", + "en-BW", + "en-BZ", + "en-CA", + "en-CM", + "en-DM", + "en-FJ", + "en-FM", + "en-GB", + "en-GD", + "en-GG", + "en-GH", + "en-GI", + "en-GM", + "en-GU", + "en-HK", + "en-IE", + "en-IM", + "en-IN", + "en-JE", + "en-JM", + "en-KE", + "en-KI", + "en-KN", + "en-KY", + "en-LC", + "en-LR", + "en-LS", + "en-MG", + "en-MH", + "en-MP", + "en-MT", + "en-MU", + "en-MW", + "en-NA", + "en-NG", + "en-NZ", + "en-PG", + "en-PH", + "en-PK", + "en-PR", + "en-PW", + "en-SB", + "en-SC", + "en-SG", + "en-SL", + "en-SS", + "en-TC", + "en-TO", + "en-TT", + "en-TZ", + "en-UG", + "en-UM", + "en-US", + "en-US-POSIX", + "en-VC", + "en-VG", + "en-VI", + "en-VU", + "en-WS", + "en-ZA", + "en-ZM", + "en-ZW", + "eo", + "es", + "es-419", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-CU", + "es-DO", + "es-EA", + "es-EC", + "es-ES", + "es-GQ", + "es-GT", + "es-HN", + "es-IC", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PH", + "es-PR", + "es-PY", + "es-SV", + "es-US", + "es-UY", + "es-VE", + "et", + "et-EE", + "fa", + "fa-AF", + "fa-IR", + "fi", + "fi-FI", + "fr", + "fr-BE", + "fr-BF", + "fr-BI", + "fr-BJ", + "fr-BL", + "fr-CA", + "fr-CD", + "fr-CF", + "fr-CG", + "fr-CH", + "fr-CI", + "fr-CM", + "fr-DJ", + "fr-DZ", + "fr-FR", + "fr-GA", + "fr-GN", + "fr-GP", + "fr-GQ", + "fr-HT", + "fr-KM", + "fr-LU", + "fr-MA", + "fr-MC", + "fr-MF", + "fr-MG", + "fr-ML", + "fr-MQ", + "fr-MR", + "fr-MU", + "fr-NC", + "fr-NE", + "fr-PF", + "fr-RE", + "fr-RW", + "fr-SC", + "fr-SN", + "fr-SY", + "fr-TD", + "fr-TG", + "fr-TN", + "fr-VU", + "ga", + "ga-IE", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "it-SM", + "ja", + "ja-JP", + "kk", + "kk-KZ", + "km", + "km-KH", + "kn", + "kn-IN", + "ko", + "ko-KP", + "ko-KR", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mk", + "mk-MK", + "ml", + "ml-IN", + "mr", + "mr-IN", + "nb", + "nb-NO", + "nl", + "nl-AW", + "nl-BE", + "nl-CW", + "nl-NL", + "nl-SR", + "nl-SX", + "nn", + "nn-NO", + "or", + "or-IN", + "pa", + "pa-Arab", + "pa-Arab-PK", + "pa-Guru", + "pa-Guru-IN", + "pl", + "pl-PL", + "pt", + "pt-AO", + "pt-BR", + "pt-CV", + "pt-GW", + "pt-MO", + "pt-MZ", + "pt-PT", + "pt-ST", + "pt-TL", + "ro", + "ro-MD", + "ro-RO", + "ru", + "ru-BY", + "ru-KG", + "ru-KZ", + "ru-MD", + "ru-RU", + "ru-UA", + "si", + "si-LK", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sq-MK", + "sr", + "sr-Cyrl", + "sr-Cyrl-BA", + "sr-Cyrl-ME", + "sr-Cyrl-RS", + "sr-Latn", + "sr-Latn-BA", + "sr-Latn-ME", + "sr-Latn-RS", + "sv", + "sv-AX", + "sv-FI", + "sv-SE", + "te", + "te-IN", + "th", + "th-TH", + "tr", + "tr-CY", + "tr-TR", + "uk", + "uk-UA", + "vi", + "vi-VN", + "zh", + "zh-Hans", + "zh-Hans-CN", + "zh-Hans-SG", + "zh-Hant", + "zh-Hant-HK", + "zh-Hant-MO", + "zh-Hant-TW", +]; + + +var count = Intl.Collator.supportedLocalesOf(locales).length; + +reportCompare(locales.length, count, "Number of supported locales in Intl.Collator"); diff --git a/js/src/tests/non262/Intl/Collator/toStringTag.js b/js/src/tests/non262/Intl/Collator/toStringTag.js new file mode 100644 index 0000000000..e092418df9 --- /dev/null +++ b/js/src/tests/non262/Intl/Collator/toStringTag.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var desc = Object.getOwnPropertyDescriptor(Intl.Collator.prototype, Symbol.toStringTag); + +assertEq(desc !== undefined, true); +assertEq(desc.value, "Intl.Collator"); +assertEq(desc.writable, false); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); + +assertEq(Object.prototype.toString.call(Intl.Collator.prototype), "[object Intl.Collator]"); +assertEq(Object.prototype.toString.call(new Intl.Collator), "[object Intl.Collator]"); + +Object.defineProperty(Intl.Collator.prototype, Symbol.toStringTag, {value: "Collator"}); + +assertEq(Object.prototype.toString.call(Intl.Collator.prototype), "[object Collator]"); +assertEq(Object.prototype.toString.call(new Intl.Collator), "[object Collator]"); + +delete Intl.Collator.prototype[Symbol.toStringTag]; + +assertEq(Object.prototype.toString.call(Intl.Collator.prototype), "[object Object]"); +assertEq(Object.prototype.toString.call(new Intl.Collator), "[object Object]"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/Date/browser.js b/js/src/tests/non262/Intl/Date/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Date/browser.js diff --git a/js/src/tests/non262/Intl/Date/shell.js b/js/src/tests/non262/Intl/Date/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Date/shell.js diff --git a/js/src/tests/non262/Intl/Date/toLocaleDateString_timeZone.js b/js/src/tests/non262/Intl/Date/toLocaleDateString_timeZone.js new file mode 100644 index 0000000000..e3c3013d30 --- /dev/null +++ b/js/src/tests/non262/Intl/Date/toLocaleDateString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = { timeZoneName: "short" }; + +const tests = [ + { + timeZone: "UTC", + result: "12/6/2012, UTC", + }, + { + timeZone: "America/Los_Angeles", + result: "12/6/2012, PST", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "6.12.2012, MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "06/12/2012 heure normale d’Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "2012/12/6 中国标准时间", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "06/12/2012, AEDT", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleDateString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleDateString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleDateString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString(<value>) +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone}); + }, RangeError); +} + +// ToString(<symbol>) throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/Date/toLocaleString_timeZone.js b/js/src/tests/non262/Intl/Date/toLocaleString_timeZone.js new file mode 100644 index 0000000000..592e55070c --- /dev/null +++ b/js/src/tests/non262/Intl/Date/toLocaleString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = {}; + +const tests = [ + { + timeZone: "UTC", + result: "12/6/2012, 12:00:00 PM", + }, + { + timeZone: "America/Los_Angeles", + result: "12/6/2012, 4:00:00 AM", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "6.12.2012, 13:00:00 MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "06/12/2012 13:00:00 heure normale d’Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "2012/12/6 中国标准时间 20:00:00", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "06/12/2012, 11:00:00 pm", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString(<value>) +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone}); + }, RangeError); +} + +// ToString(<symbol>) throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/Date/toLocaleTimeString_timeZone.js b/js/src/tests/non262/Intl/Date/toLocaleTimeString_timeZone.js new file mode 100644 index 0000000000..a9b8b5f980 --- /dev/null +++ b/js/src/tests/non262/Intl/Date/toLocaleTimeString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = {}; + +const tests = [ + { + timeZone: "UTC", + result: "12:00:00 PM", + }, + { + timeZone: "America/Los_Angeles", + result: "4:00:00 AM", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "13:00:00 MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "13:00:00 heure normale d’Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "中国标准时间 20:00:00", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "11:00:00 pm", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleTimeString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString(<value>) +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone}); + }, RangeError); +} + +// ToString(<symbol>) throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/browser.js b/js/src/tests/non262/Intl/DateTimeFormat/browser.js new file mode 100644 index 0000000000..5665e7ed44 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/browser.js @@ -0,0 +1,3 @@ +if (typeof setTimeZone === "undefined") { + var setTimeZone = SpecialPowers.Cu.getJSTestingFunctions().setTimeZone; +} diff --git a/js/src/tests/non262/Intl/DateTimeFormat/calendar-aliases.js b/js/src/tests/non262/Intl/DateTimeFormat/calendar-aliases.js new file mode 100644 index 0000000000..901adcb45f --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/calendar-aliases.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure ethiopic-amete-alem is resolved to ethioaa instead of ethiopic. +function testEthiopicAmeteAlem() { + var locale = "am-ET-u-nu-latn"; + var opts = {timeZone: "Africa/Addis_Ababa"}; + var dtfEthiopicAmeteAlem = new Intl.DateTimeFormat(`${locale}-ca-ethiopic-amete-alem`, opts); + var dtfEthioaa = new Intl.DateTimeFormat(`${locale}-ca-ethioaa`, opts); + var dtfEthiopic = new Intl.DateTimeFormat(`${locale}-ca-ethiopic`, opts); + + var date = new Date(2016, 1 - 1, 1); + + assertEq(dtfEthiopicAmeteAlem.format(date), dtfEthioaa.format(date)); + assertEq(dtfEthiopicAmeteAlem.format(date) === dtfEthiopic.format(date), false); +} + +// Ensure islamicc is resolved to islamic-civil. +function testIslamicCivil() { + var locale = "ar-SA-u-nu-latn"; + var opts = {timeZone: "Asia/Riyadh"}; + var dtfIslamicCivil = new Intl.DateTimeFormat(`${locale}-ca-islamic-civil`, opts); + var dtfIslamicc = new Intl.DateTimeFormat(`${locale}-ca-islamicc`, opts); + var dtfIslamic = new Intl.DateTimeFormat(`${locale}-ca-islamic`, opts); + + var date = new Date(2016, 1 - 1, 1); + + assertEq(dtfIslamicCivil.format(date), dtfIslamicc.format(date)); + assertEq(dtfIslamicCivil.format(date) === dtfIslamic.format(date), false); +} + +testEthiopicAmeteAlem(); +testIslamicCivil(); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js b/js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js new file mode 100644 index 0000000000..cbf73df5c9 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js @@ -0,0 +1,67 @@ +const defaultLocale = "en"; +const defaultCalendar = new Intl.DateTimeFormat(defaultLocale).resolvedOptions().calendar; + +function createWithLocale(locale, calendar) { + return new Intl.DateTimeFormat(locale, {calendar}); +} + +function create(calendar) { + return createWithLocale(defaultLocale, calendar); +} + +// Empty string should throw. +assertThrowsInstanceOf(() => create(""), RangeError); + +// Trailing \0 should throw. +assertThrowsInstanceOf(() => create("gregory\0"), RangeError); + +// Too short or too long strings should throw. +assertThrowsInstanceOf(() => create("a"), RangeError); +assertThrowsInstanceOf(() => create("toolongstring"), RangeError); + +// Throw even when prefix is valid. +assertThrowsInstanceOf(() => create("gregory-toolongstring"), RangeError); + +// |calendar| can be set to |undefined|. +let dtf = create(undefined); +assertEq(dtf.resolvedOptions().calendar, defaultCalendar); + +// Unsupported calendars are ignored. +dtf = create("xxxxxxxx"); +assertEq(dtf.resolvedOptions().calendar, defaultCalendar); + +// Calendars in options overwrite Unicode extension keyword. +dtf = createWithLocale(`${defaultLocale}-u-ca-iso8601`, "japanese"); +assertEq(dtf.resolvedOptions().locale, defaultLocale); +assertEq(dtf.resolvedOptions().calendar, "japanese"); + +// |calendar| option ignores case. +dtf = create("CHINESE"); +assertEq(dtf.resolvedOptions().locale, defaultLocale); +assertEq(dtf.resolvedOptions().calendar, "chinese"); + +const calendars = [ + "buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic-amete-alem", + "ethiopic", "gregory", "hebrew", "indian", "islamic", "islamic-umalqura", + "islamic-tbla", "islamic-civil", "islamic-rgsa", "iso8601", "japanese", + "persian", "roc", "islamicc", +]; + +// https://github.com/tc39/proposal-intl-locale/issues/96 +const canonical = { + "islamicc": "islamic-civil", + "ethiopic-amete-alem": "ethioaa", +}; + +for (let calendar of calendars) { + let dtf1 = new Intl.DateTimeFormat(`${defaultLocale}-u-ca-${calendar}`); + let dtf2 = new Intl.DateTimeFormat(defaultLocale, {calendar}); + + assertEq(dtf1.resolvedOptions().calendar, canonical[calendar] ?? calendar); + assertEq(dtf2.resolvedOptions().calendar, canonical[calendar] ?? calendar); + + assertEq(dtf2.format(0), dtf1.format(0)); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/call.js b/js/src/tests/non262/Intl/DateTimeFormat/call.js new file mode 100644 index 0000000000..752bda9c02 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/call.js @@ -0,0 +1,184 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function IsObject(o) { + return Object(o) === o; +} + +function IsPrimitive(o) { + return Object(o) !== o; +} + +function thisValues() { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.map(ctor => { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Object inheriting from an Intl constructor prototype. + Object.create(ctor.prototype), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; + })), + ]; +} + +const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.DateTimeFormat.call(Object.create(Intl.DateTimeFormat.prototype)))[0]; + +// Invoking [[Call]] for Intl.DateTimeFormat returns a new instance unless called +// with an instance inheriting from Intl.DateTimeFormat.prototype. +for (let thisValue of thisValues()) { + let obj = Intl.DateTimeFormat.call(thisValue); + + if (!Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue)) { + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.DateTimeFormat, true); + if (IsObject(thisValue)) + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); + } else { + assertEq(Object.is(obj, thisValue), true); + assertEq(obj instanceof Intl.DateTimeFormat, true); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); + } +} + +// Intl.DateTimeFormat uses the legacy Intl constructor compromise semantics. +// - Test when InstanceofOperator(thisValue, %DateTimeFormat%) returns true. +for (let thisValue of thisValues().filter(IsObject)) { + let isPrototypeOf = Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false; + Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return true; + }, configurable: true + }); + let obj = Intl.DateTimeFormat.call(thisValue); + delete Intl.DateTimeFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), isPrototypeOf); + assertEq(hasInstanceCalled, false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); +} +// - Test when InstanceofOperator(thisValue, %DateTimeFormat%) returns false. +for (let thisValue of thisValues().filter(IsObject)) { + let isPrototypeOf = Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false; + Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return false; + }, configurable: true + }); + let obj = Intl.DateTimeFormat.call(thisValue); + delete Intl.DateTimeFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), isPrototypeOf); + assertEq(obj instanceof Intl.DateTimeFormat, true); + assertEq(hasInstanceCalled, false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); +} +// - Test with primitive values. +for (let thisValue of thisValues().filter(IsPrimitive)) { + // Ensure @@hasInstance is not called. + Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, { + value() { assertEq(true, false); }, configurable: true + }); + let obj = Intl.DateTimeFormat.call(thisValue); + delete Intl.DateTimeFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.DateTimeFormat, true); +} + +// Throws an error when attempting to install [[FallbackSymbol]] twice. +{ + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); + + assertEq(Intl.DateTimeFormat.call(thisValue), thisValue); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); + + assertThrowsInstanceOf(() => Intl.DateTimeFormat.call(thisValue), TypeError); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); +} + +// Throws an error when the thisValue is non-extensible. +{ + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + Object.preventExtensions(thisValue); + + assertThrowsInstanceOf(() => Intl.DateTimeFormat.call(thisValue), TypeError); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); +} + +// [[FallbackSymbol]] is installed as a frozen property holding an Intl.DateTimeFormat instance. +{ + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + Intl.DateTimeFormat.call(thisValue); + + let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol); + assertEq(desc !== undefined, true); + assertEq(desc.writable, false); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, false); + assertEq(desc.value instanceof Intl.DateTimeFormat, true); +} + +// Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]] +// during initialization. +{ + let thisValue = {}; + let options = { + get hour12() { + Object.setPrototypeOf(thisValue, Intl.DateTimeFormat.prototype); + return false; + } + }; + let obj = Intl.DateTimeFormat.call(thisValue, undefined, options); + assertEq(Object.is(obj, thisValue), true); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); +} +{ + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + let options = { + get hour12() { + Object.setPrototypeOf(thisValue, Object.prototype); + return false; + } + }; + let obj = Intl.DateTimeFormat.call(thisValue, undefined, options); + assertEq(Object.is(obj, thisValue), false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/construct-newtarget.js b/js/src/tests/non262/Intl/DateTimeFormat/construct-newtarget.js new file mode 100644 index 0000000000..bbc08c79a1 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +// Test subclassing %Intl.DateTimeFormat% works correctly. +class MyDateTimeFormat extends Intl.DateTimeFormat {} + +var obj = new MyDateTimeFormat(); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, []); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], MyDateTimeFormat); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], Intl.DateTimeFormat); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyDateTimeFormat, [], Array); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.DateTimeFormat, [], Array); +assertEq(obj instanceof Intl.DateTimeFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %DateTimeFormatPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.DateTimeFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.DateTimeFormat, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.DateTimeFormat, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/cross-compartment.js b/js/src/tests/non262/Intl/DateTimeFormat/cross-compartment.js new file mode 100644 index 0000000000..dac99bdc19 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/cross-compartment.js @@ -0,0 +1,70 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var otherGlobal = newGlobal(); + +var dateTimeFormat = new Intl.DateTimeFormat(); +var ccwDateTimeFormat = new otherGlobal.Intl.DateTimeFormat(); + +// Test Intl.DateTimeFormat.prototype.format with a CCW object. +var Intl_DateTimeFormat_format_get = Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get; + +assertEq(Intl_DateTimeFormat_format_get.call(ccwDateTimeFormat)(0), + Intl_DateTimeFormat_format_get.call(dateTimeFormat)(0)); + +// Test Intl.DateTimeFormat.prototype.formatToParts with a CCW object. +var Intl_DateTimeFormat_formatToParts = Intl.DateTimeFormat.prototype.formatToParts; + +assertEq(deepEqual(Intl_DateTimeFormat_formatToParts.call(ccwDateTimeFormat, 0), + Intl_DateTimeFormat_formatToParts.call(dateTimeFormat, 0)), + true); + +// Test Intl.DateTimeFormat.prototype.resolvedOptions with a CCW object. +var Intl_DateTimeFormat_resolvedOptions = Intl.DateTimeFormat.prototype.resolvedOptions; + +assertEq(deepEqual(Intl_DateTimeFormat_resolvedOptions.call(ccwDateTimeFormat), + Intl_DateTimeFormat_resolvedOptions.call(dateTimeFormat)), + true); + +// Special case for Intl.DateTimeFormat: The Intl fallback symbol. + +function fallbackSymbol(global) { + var DTF = global.Intl.DateTimeFormat; + return Object.getOwnPropertySymbols(DTF.call(Object.create(DTF.prototype)))[0]; +} + +const intlFallbackSymbol = fallbackSymbol(this); +const otherIntlFallbackSymbol = fallbackSymbol(otherGlobal); +assertEq(intlFallbackSymbol === otherIntlFallbackSymbol, false); + +// Test when the fallback symbol points to a CCW DateTimeFormat object. +var objWithFallbackCCWDateTimeFormat = { + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: ccwDateTimeFormat, +}; + +assertEq(Intl_DateTimeFormat_format_get.call(objWithFallbackCCWDateTimeFormat)(0), + Intl_DateTimeFormat_format_get.call(dateTimeFormat)(0)); + +assertEq(deepEqual(Intl_DateTimeFormat_resolvedOptions.call(objWithFallbackCCWDateTimeFormat), + Intl_DateTimeFormat_resolvedOptions.call(dateTimeFormat)), + true); + +// Ensure the fallback symbol(s) are not accessed for CCW DateTimeFormat objects. +var ccwDateTimeFormatWithPoisonedFallback = new otherGlobal.Intl.DateTimeFormat(); +Object.setPrototypeOf(ccwDateTimeFormatWithPoisonedFallback, Intl.DateTimeFormat.prototype); +Object.defineProperty(ccwDateTimeFormatWithPoisonedFallback, intlFallbackSymbol, { + get() { throw new Error(); } +}); +Object.defineProperty(ccwDateTimeFormatWithPoisonedFallback, otherIntlFallbackSymbol, { + get() { throw new Error(); } +}); + +assertEq(Intl_DateTimeFormat_format_get.call(ccwDateTimeFormatWithPoisonedFallback)(0), + Intl_DateTimeFormat_format_get.call(dateTimeFormat)(0)); + +assertEq(deepEqual(Intl_DateTimeFormat_resolvedOptions.call(ccwDateTimeFormatWithPoisonedFallback), + Intl_DateTimeFormat_resolvedOptions.call(dateTimeFormat)), + true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/dateTimeStyle.js b/js/src/tests/non262/Intl/DateTimeFormat/dateTimeStyle.js new file mode 100644 index 0000000000..1dd4c35316 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/dateTimeStyle.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Date style + +var dtf = new Intl.DateTimeFormat("en-US", {dateStyle: 'long'}); +assertEq(dtf.resolvedOptions().dateStyle, 'long'); +assertEq(dtf.resolvedOptions().hasOwnProperty('year'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('month'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('day'), false); + +// Time style + +var dtf = new Intl.DateTimeFormat("en-US", {timeStyle: 'long'}); +assertEq(dtf.resolvedOptions().timeStyle, 'long'); +assertEq(dtf.resolvedOptions().hasOwnProperty('hour'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('minute'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('second'), false); + +// Date/Time style + +var dtf = new Intl.DateTimeFormat("en-US", {dateStyle: 'medium', timeStyle: 'medium'}); +assertEq(dtf.resolvedOptions().timeStyle, 'medium'); +assertEq(dtf.resolvedOptions().dateStyle, 'medium'); +assertEq(dtf.resolvedOptions().hasOwnProperty('hour'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('minute'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('second'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('year'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('month'), false); +assertEq(dtf.resolvedOptions().hasOwnProperty('day'), false); + +// Error when mixing date/timeStyle with other date-time options. + +assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat("en-US", {dateStyle: 'medium', year: 'numeric'}); +}, TypeError); + +assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat("en-US", {timeStyle: 'medium', year: 'numeric'}); +}, TypeError); + +// Error when using dateStyle in toLocaleTimeString. + +assertThrowsInstanceOf(() => { + new Date().toLocaleTimeString("en-US", {dateStyle: 'long'}); +}, TypeError); + +// Error when using dateStyle in toLocaleDateString. + +assertThrowsInstanceOf(() => { + new Date().toLocaleDateString("en-US", {timeStyle: 'long'}); +}, TypeError); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/day-period-hour-cycle.js b/js/src/tests/non262/Intl/DateTimeFormat/day-period-hour-cycle.js new file mode 100644 index 0000000000..6bfbf94df3 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/day-period-hour-cycle.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Year, Month, Day, DayPeriod, Hour, Minute, Literal +} = DateTimeFormatParts; + +// If the locale defaults to a 24-hour-cycle, the "dayPeriod" option is ignored if an "hour" option +// is also present, unless the hour-cycle is manually set to a 12-hour-cycle. + +const tests = [ + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", }, + locales: { + en: [Hour("12"), Literal(" "), DayPeriod("noon")], + de: [Hour("12"), Literal(" Uhr")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hour12: true }, + locales: { + en: [Hour("12"), Literal(" "), DayPeriod("noon")], + de: [Hour("12"), Literal(" "), DayPeriod("mittags")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hour12: false }, + locales: { + en: [Hour("12")], + de: [Hour("12"), Literal(" Uhr")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hourCycle: "h12" }, + locales: { + en: [Hour("12"), Literal(" "), DayPeriod("noon")], + de: [Hour("12"), Literal(" "), DayPeriod("mittags")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hourCycle: "h11" }, + locales: { + en: [Hour("0"), Literal(" "), DayPeriod("noon")], + de: [Hour("0"), Literal(" "), DayPeriod("mittags")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hourCycle: "h23" }, + locales: { + en: [Hour("12")], + de: [Hour("12"), Literal(" Uhr")], + }, + }, + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", hour: "numeric", hourCycle: "h24" }, + locales: { + en: [Hour("12")], + de: [Hour("12"), Literal(" Uhr")], + }, + }, + + // The default hour-cycle is irrelevant when an "hour" option isn't present. + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", day: "numeric", month: "numeric", year: "numeric" }, + locales: { + en: [Month("1"), Literal("/"), Day("1"), Literal("/"), Year("2019"), Literal(", "), DayPeriod("noon")], + de: [Day("1"), Literal("."), Month("1"), Literal("."), Year("2019"), Literal(", "), DayPeriod("mittags")], + }, + }, + + // ICU replacement pattern for missing <appendItem> entries in CLDR. + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "short", minute: "numeric" }, + locales: { + en: [Minute("0"), Literal(" (AM/PM: "), DayPeriod("noon"), Literal(")")], + de: [Minute("0"), Literal(" (Tageshälfte: "), DayPeriod("mittags"), Literal(")")], + }, + }, +]; + +for (let {date, options, locales} of tests) { + for (let [locale, parts] of Object.entries(locales)) { + let dtf = new Intl.DateTimeFormat(locale, options); + + assertEq(dtf.format(date), parts.map(({value}) => value).join(""), + `locale=${locale}, date=${date}, options=${JSON.stringify(options)}`); + + assertDeepEq(dtf.formatToParts(date), parts, + `locale=${locale}, date=${date}, options=${JSON.stringify(options)}`); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/day-period-standalone.js b/js/src/tests/non262/Intl/DateTimeFormat/day-period-standalone.js new file mode 100644 index 0000000000..203ba03766 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/day-period-standalone.js @@ -0,0 +1,160 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Tests using various locales to cover all day period types: +// "midnight", "noon", "morning1", "morning2", "afternoon1", "afternoon2", +// "evening1", "evening2", "night1", "night2". + +const tests = [ + { + // ICU doesn't support "midnight" and instead uses "night1" resp. "night2". + // ICU bug: https://unicode-org.atlassian.net/projects/ICU/issues/ICU-12278 + date: new Date("2019-01-01T00:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "nachts", short: "nachts", long: "nachts" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "夜中", short: "夜中", long: "夜中" }, + } + }, + { + date: new Date("2019-01-01T03:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "nachts", short: "nachts", long: "nachts" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "夜中", short: "夜中", long: "夜中" }, + } + }, + { + date: new Date("2019-01-01T04:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "nachts", short: "nachts", long: "nachts" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "朝", short: "朝", long: "朝" }, + } + }, + { + date: new Date("2019-01-01T05:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "morgens", short: "morgens", long: "morgens" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "朝", short: "朝", long: "朝" }, + } + }, + { + date: new Date("2019-01-01T06:00:00"), + locales: { + en: { narrow: "in the morning", short: "in the morning", long: "in the morning" }, + de: { narrow: "morgens", short: "morgens", long: "morgens" }, + th: { narrow: "เช้า", short: "ในตอนเช้า", long: "ในตอนเช้า" }, + ja: { narrow: "朝", short: "朝", long: "朝" }, + } + }, + { + date: new Date("2019-01-01T10:00:00"), + locales: { + en: { narrow: "in the morning", short: "in the morning", long: "in the morning" }, + de: { narrow: "vorm.", short: "vorm.", long: "vormittags" }, + th: { narrow: "เช้า", short: "ในตอนเช้า", long: "ในตอนเช้า" }, + ja: { narrow: "朝", short: "朝", long: "朝" }, + } + }, + { + date: new Date("2019-01-01T12:00:00"), + locales: { + en: { narrow: "n", short: "noon", long: "noon" }, + de: { narrow: "mittags", short: "mittags", long: "mittags" }, + th: { narrow: "เที่ยง", short: "เที่ยง", long: "เที่ยง" }, + ja: { narrow: "正午", short: "正午", long: "正午" }, + } + }, + { + date: new Date("2019-01-01T13:00:00"), + locales: { + en: { narrow: "in the afternoon", short: "in the afternoon", long: "in the afternoon" }, + de: { narrow: "nachm.", short: "nachm.", long: "nachmittags" }, + th: { narrow: "บ่าย", short: "บ่าย", long: "บ่าย" }, + ja: { narrow: "昼", short: "昼", long: "昼" }, + } + }, + { + date: new Date("2019-01-01T15:00:00"), + locales: { + en: { narrow: "in the afternoon", short: "in the afternoon", long: "in the afternoon" }, + de: { narrow: "nachm.", short: "nachm.", long: "nachmittags" }, + th: { narrow: "บ่าย", short: "บ่าย", long: "บ่าย" }, + ja: { narrow: "昼", short: "昼", long: "昼" }, + } + }, + { + date: new Date("2019-01-01T16:00:00"), + locales: { + en: { narrow: "in the afternoon", short: "in the afternoon", long: "in the afternoon" }, + de: { narrow: "nachm.", short: "nachm.", long: "nachmittags" }, + th: { narrow: "เย็น", short: "ในตอนเย็น", long: "ในตอนเย็น" }, + ja: { narrow: "夕方", short: "夕方", long: "夕方" }, + } + }, + { + date: new Date("2019-01-01T18:00:00"), + locales: { + en: { narrow: "in the evening", short: "in the evening", long: "in the evening" }, + de: { narrow: "abends", short: "abends", long: "abends" }, + th: { narrow: "ค่ำ", short: "ค่ำ", long: "ค่ำ" }, + ja: { narrow: "夕方", short: "夕方", long: "夕方" }, + } + }, + { + date: new Date("2019-01-01T19:00:00"), + locales: { + en: { narrow: "in the evening", short: "in the evening", long: "in the evening" }, + de: { narrow: "abends", short: "abends", long: "abends" }, + th: { narrow: "ค่ำ", short: "ค่ำ", long: "ค่ำ" }, + ja: { narrow: "夜", short: "夜", long: "夜" }, + } + }, + { + date: new Date("2019-01-01T21:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "abends", short: "abends", long: "abends" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "夜", short: "夜", long: "夜" }, + } + }, + { + date: new Date("2019-01-01T22:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "abends", short: "abends", long: "abends" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "夜", short: "夜", long: "夜" }, + } + }, + { + date: new Date("2019-01-01T23:00:00"), + locales: { + en: { narrow: "at night", short: "at night", long: "at night" }, + de: { narrow: "abends", short: "abends", long: "abends" }, + th: { narrow: "กลางคืน", short: "กลางคืน", long: "กลางคืน" }, + ja: { narrow: "夜中", short: "夜中", long: "夜中" }, + } + }, +]; + +for (let {date, locales} of tests) { + for (let [locale, formats] of Object.entries(locales)) { + for (let [dayPeriod, expected] of Object.entries(formats)) { + let dtf = new Intl.DateTimeFormat(locale, {dayPeriod}); + + assertEq(dtf.format(date), expected, + `locale=${locale}, date=${date}, dayPeriod=${dayPeriod}`); + assertDeepEq(dtf.formatToParts(date), [{type: "dayPeriod", value: expected}]); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/day-period.js b/js/src/tests/non262/Intl/DateTimeFormat/day-period.js new file mode 100644 index 0000000000..486970e519 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/day-period.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Weekday, DayPeriod, Literal +} = DateTimeFormatParts; + +const tests = [ + // https://unicode-org.atlassian.net/browse/ICU-20741 + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "long", weekday: "long", }, + locales: { + "en-001": [Weekday("Tuesday"), Literal(", "), DayPeriod("noon")], + }, + }, + + // https://unicode-org.atlassian.net/browse/ICU-20740 + { + date: new Date("2019-01-01T12:00:00"), + options: { dayPeriod: "narrow", weekday: "long", }, + locales: { + "bs-Cyrl": [Weekday("уторак"), Literal(" "), DayPeriod("подне")], + }, + }, +]; + +for (let {date, options, locales} of tests) { + for (let [locale, parts] of Object.entries(locales)) { + let dtf = new Intl.DateTimeFormat(locale, options); + + assertEq(dtf.format(date), parts.map(({value}) => value).join(""), + `locale=${locale}, date=${date}, options=${JSON.stringify(options)}`); + + assertDeepEq(dtf.formatToParts(date), parts, + `locale=${locale}, date=${date}, options=${JSON.stringify(options)}`); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/era.js b/js/src/tests/non262/Intl/DateTimeFormat/era.js new file mode 100644 index 0000000000..716fa2cf0d --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/era.js @@ -0,0 +1,245 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Era, Year, Month, Day, Literal +} = DateTimeFormatParts; + +const tests = { + "en": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Month("1"), Literal("/"), Day("1"), Literal("/"), Year("1970"), Literal(" "), Era("AD") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Month("1"), Literal("/"), Day("1"), Literal("/"), Year("1971"), Literal(" "), Era("BC") + ], + }, + ], + }, + ], + "en-001": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1970"), Literal(" "), Era("AD") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1971"), Literal(" "), Era("BC") + ], + }, + ], + }, + ], + "de": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("01"), Literal("."), Month("01"), Literal("."), Year("1970"), Literal(" "), Era("n. Chr.") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("01"), Literal("."), Month("01"), Literal("."), Year("1971"), Literal(" "), Era("v. Chr.") + ], + }, + ], + }, + ], + "fr": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("01"), Literal("/"), Month("01"), Literal("/"), Year("1970"), Literal(" "), Era("ap. J.-C.") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("01"), Literal("/"), Month("01"), Literal("/"), Year("1971"), Literal(" "), Era("av. J.-C.") + ], + }, + ], + }, + ], + "es": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1970"), Literal(" "), Era("d. C.") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1971"), Literal(" "), Era("a. C.") + ], + }, + ], + }, + ], + "nl": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1970"), Literal(" "), Era("n.Chr.") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("1"), Literal("/"), Month("1"), Literal("/"), Year("1971"), Literal(" "), Era("v.Chr.") + ], + }, + ], + }, + ], + "ja": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Era("西暦"), Year("1970"), Literal("/"), Month("1"), Literal("/"), Day("1") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Era("紀元前"), Year("1971"), Literal("/"), Month("1"), Literal("/"), Day("1") + ], + }, + ], + }, + ], + "zh": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Era("公元"), Literal(" "), Year("1970"), Literal("-"), Month("01"), Literal("-"), Day("01") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Era("公元前"), Literal(" "), Year("1971"), Literal("-"), Month("01"), Literal("-"), Day("01") + ], + }, + ], + }, + ], + "ar": [ + { + options: { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC", + }, + dates: [ + { + date: new Date("1970-01-01T00:00:00.000Z"), + parts: [ + Day("٠١"), Literal("-"), Month("٠١"), Literal("-"), Year("١٩٧٠"), Literal(" "), Era("م") + ], + }, + { + date: new Date("-001970-01-01T00:00:00.000Z"), + parts: [ + Day("٠١"), Literal("-"), Month("٠١"), Literal("-"), Year("١٩٧١"), Literal(" "), Era("ق.م") + ], + }, + ], + }, + ], +}; + +for (let [locale, inputs] of Object.entries(tests)) { + for (let {options, dates} of inputs) { + let dtf = new Intl.DateTimeFormat(locale, options); + for (let {date, parts} of dates) { + assertParts(dtf, date, parts); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/extended-time-zone-names.js b/js/src/tests/non262/Intl/DateTimeFormat/extended-time-zone-names.js new file mode 100644 index 0000000000..12090c4119 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/extended-time-zone-names.js @@ -0,0 +1,124 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const tests = { + "America/Los_Angeles": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "longGeneric", + locales: { + "en": "5/20/2021, Pacific Time", + "de": "20.5.2021, Nordamerikanische Westküstenzeit", + "fr": "20/05/2021 à heure du Pacifique nord-américain", + "ar": "٢٠/٥/٢٠٢١ توقيت المحيط الهادي", + "th": "20/5/2564 เวลาแปซิฟิกในอเมริกาเหนือ", + "zh": "2021/5/20 北美太平洋时间", + "ja": "2021/5/20 アメリカ太平洋時間", + } + }, + "America/Los_Angeles": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "shortGeneric", + locales: { + "en": "5/20/2021, PT", + "de": "20.5.2021, Los Angeles Zeit", + "fr": "20/05/2021 à heure : Los Angeles", + "ar": "٢٠/٥/٢٠٢١ توقيت Los Angeles", + "th": "20/5/2564 เวลาLos Angeles", + "zh": "2021/5/20 Los Angeles时间", + "ja": "2021/5/20 Los Angeles時間", + } + }, + "America/Los_Angeles": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "longOffset", + locales: { + "en": "5/20/2021, GMT-07:00", + "de": "20.5.2021, GMT-07:00", + "fr": "20/05/2021 à UTC−07:00", + "ar": "٢٠/٥/٢٠٢١ غرينتش-٠٧:٠٠", + "th": "20/5/2564 GMT-07:00", + "zh": "2021/5/20 GMT-07:00", + "ja": "2021/5/20 GMT-07:00", + } + }, + "America/Los_Angeles": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "shortOffset", + locales: { + "en": "5/20/2021, GMT-7", + "de": "20.5.2021, GMT-7", + "fr": "20/05/2021 UTC−7", + "ar": "٢٠/٥/٢٠٢١، غرينتش-٧", + "th": "20/5/2564 GMT-7", + "zh": "2021/5/20 GMT-7", + "ja": "2021/5/20 GMT-7", + } + }, + "Europe/Berlin": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "longGeneric", + locales: { + "en": "5/20/2021, Central European Time", + "de": "20.5.2021, Mitteleuropäische Zeit", + "fr": "20/05/2021 à heure d’Europe centrale", + "ar": "٢٠/٥/٢٠٢١ توقيت وسط أوروبا", + "th": "20/5/2564 เวลายุโรปกลาง", + "zh": "2021/5/20 中欧时间", + "ja": "2021/5/20 中央ヨーロッパ時間", + } + }, + "Europe/Berlin": { + date: Date.UTC(2021, 5-1, 20, 12, 0, 0), + timeZoneName: "shortGeneric", + locales: { + "en": "5/20/2021, Germany Time", + "de": "20.5.2021, MEZ", + "fr": "20/05/2021 heure : Allemagne", + "ar": "٢٠/٥/٢٠٢١، توقيت ألمانيا", + "th": "20/5/2564 เวลาเยอรมนี", + "zh": "2021/5/20 德国时间", + "ja": "2021/5/20 ドイツ時間", + } + }, + "Africa/Monrovia": { + date: Date.UTC(1971, 12-1, 6, 12, 0, 0), + timeZoneName: "longOffset", + locales: { + "en": "12/6/1971, GMT-00:44:30", + "de": "6.12.1971, GMT-00:44:30", + "fr": "06/12/1971 UTC−00:44:30", + "ar": "٦/١٢/١٩٧١ غرينتش-٠٠:٤٤:٣٠", + "th": "6/12/2514 GMT-00:44:30", + "zh": "1971/12/6 GMT-00:44:30", + "ja": "1971/12/6 GMT-00:44:30", + } + }, + "Africa/Monrovia": { + date: Date.UTC(1971, 12-1, 6, 12, 0, 0), + timeZoneName: "shortOffset", + locales: { + "en": "12/6/1971, GMT-0:44:30", + "de": "6.12.1971, GMT-0:44:30", + "fr": "06/12/1971 UTC−0:44:30", + "ar": "٦/١٢/١٩٧١، غرينتش-٠:٤٤:٣٠", + "th": "6/12/2514 GMT-0:44:30", + "zh": "1971/12/6 GMT-0:44:30", + "ja": "1971/12/6 GMT-0:44:30", + } + }, +}; + +for (let [timeZone, {timeZoneName, date, locales}] of Object.entries(tests)) { + for (let [locale, expected] of Object.entries(locales)) { + let dtf = new Intl.DateTimeFormat(locale, {timeZone, timeZoneName}); + assertEq(dtf.format(date), expected); + + let parts = dtf.formatToParts(date); + assertEq(parts.map(p => p.value).join(""), expected); + assertEq(parts.filter(p => p.type === "timeZoneName").length, 1); + + assertEq(dtf.resolvedOptions().timeZoneName, timeZoneName); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/field-widths.js b/js/src/tests/non262/Intl/DateTimeFormat/field-widths.js new file mode 100644 index 0000000000..789d0fdaa6 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/field-widths.js @@ -0,0 +1,225 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const date = new Date(Date.UTC(2021, 1-1, 2, 3, 4, 5, 678)); + +const tests = [ + // Standalone 'hour' field. + { + options: { + hour: "numeric", + }, + locales: { + "en": "3 AM", + "de": "03 Uhr", + }, + }, + { + options: { + hour: "2-digit", + }, + locales: { + "en": "03 AM", + "de": "03 Uhr", + }, + }, + + // Standalone 'minute' field. + { + options: { + minute: "numeric", + }, + locales: { + "en": "4", + "de": "4", + }, + }, + { + options: { + minute: "2-digit", + }, + locales: { + "en": "04", + "de": "04", + }, + }, + + // Standalone 'second' field. + { + options: { + second: "numeric", + }, + locales: { + "en": "5", + "de": "5", + }, + }, + { + options: { + second: "2-digit", + }, + locales: { + "en": "05", + "de": "05", + }, + }, + + // 'hour' and 'minute' fields with all possible field width combinations. + { + options: { + hour: "numeric", + minute: "numeric", + }, + locales: { + "en": "3:04 AM", + "de": "03:04", + }, + }, + { + options: { + hour: "numeric", + minute: "2-digit", + }, + locales: { + "en": "3:04 AM", + "de": "03:04", + }, + }, + { + options: { + hour: "2-digit", + minute: "numeric", + }, + locales: { + "en": "03:04 AM", + "de": "03:04", + }, + }, + { + options: { + hour: "2-digit", + minute: "2-digit", + }, + locales: { + "en": "03:04 AM", + "de": "03:04", + }, + }, + + // 'minute' and 'second' fields with all possible field width combinations. + { + options: { + minute: "numeric", + second: "numeric", + }, + locales: { + "en": "04:05", + "de": "04:05", + }, + }, + { + options: { + minute: "numeric", + second: "2-digit", + }, + locales: { + "en": "04:05", + "de": "04:05", + }, + }, + { + options: { + minute: "2-digit", + second: "numeric", + }, + locales: { + "en": "04:05", + "de": "04:05", + }, + }, + { + options: { + minute: "2-digit", + second: "2-digit", + }, + locales: { + "en": "04:05", + "de": "04:05", + }, + }, + + // Test 'hour' and 'minute' with 'hourCycle=h12'. + { + options: { + hour: "numeric", + minute: "numeric", + hourCycle: "h12", + }, + locales: { + "en": "3:04 AM", + "de": "3:04 AM", + }, + }, + { + options: { + hour: "2-digit", + minute: "2-digit", + hourCycle: "h12", + }, + locales: { + "en": "03:04 AM", + "de": "03:04 AM", + }, + }, + + // Test 'hour' and 'minute' with 'hourCycle=h23'. + { + options: { + hour: "numeric", + minute: "numeric", + hourCycle: "h23", + }, + locales: { + "en": "03:04", + "de": "03:04", + }, + }, + { + options: { + hour: "2-digit", + minute: "2-digit", + hourCycle: "h23", + }, + locales: { + "en": "03:04", + "de": "03:04", + }, + }, +]; + +for (let {options, locales} of tests) { + for (let [locale, expected] of Object.entries(locales)) { + let dtf = new Intl.DateTimeFormat(locale, {timeZone: "UTC", ...options}); + assertEq(dtf.format(date), expected); + } +} + +const toLocaleTests = { + "en": "1/2/2021, 3:04:05 AM", + "de": "2.1.2021, 03:04:05", +}; + +for (let [locale, expected] of Object.entries(toLocaleTests)) { + assertEq(date.toLocaleString(locale, {timeZone: "UTC"}), expected); +} + +const toLocaleTimeTests = { + "en": "3:04:05 AM", + "de": "03:04:05", +}; + +for (let [locale, expected] of Object.entries(toLocaleTimeTests)) { + assertEq(date.toLocaleTimeString(locale, {timeZone: "UTC"}), expected); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/format.js b/js/src/tests/non262/Intl/DateTimeFormat/format.js new file mode 100644 index 0000000000..224d19cfa8 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/format.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the format function with a diverse set of locales and options. +// Always use UTC to avoid dependencies on test environment. + +var format; +var date = Date.UTC(2012, 11, 12, 3, 0, 0); +var longFormatOptions = {timeZone: "UTC", + year: "numeric", month: "long", day: "numeric", + hour: "numeric", minute: "numeric", second: "numeric"}; + +// Locale en-US; default options. +format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"}); +assertEq(format.format(date), "12/12/2012"); + +// Locale th-TH; default options. +// Thailand uses the Buddhist calendar. +format = new Intl.DateTimeFormat("th-th", {timeZone: "UTC"}); +assertEq(format.format(date), "12/12/2555"); + +// Locale th-TH; long format, Thai digits. +format = new Intl.DateTimeFormat("th-th-u-nu-thai", longFormatOptions); +assertEq(format.format(date), "๑๒ ธันวาคม ๒๕๕๕ เวลา ๐๓:๐๐:๐๐"); + +// Locale ja-JP; long format. +format = new Intl.DateTimeFormat("ja-jp", longFormatOptions); +assertEq(format.format(date), "2012年12月12日 3:00:00"); + +// Locale ar-MA; long format, Islamic civilian calendar. +format = new Intl.DateTimeFormat("ar-ma-u-ca-islamicc", longFormatOptions); +assertEq(format.format(date), "28 محرم 1434 هـ في 03:00:00"); + +// Locale en-IE: timeZoneName for crash test +format = new Intl.DateTimeFormat("en-IE", {timeZone: "UTC", timeZoneName: "short"}); +assertEq(format.format(date), "12/12/2012, UTC"); + +// Test the .name property of the "format" getter. +var desc = Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format"); +assertEq(desc !== undefined, true); +assertEq(typeof desc.get, "function"); +assertEq(desc.get.name, "get format"); + + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/formatRange-gregorian-proleptic.js b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-gregorian-proleptic.js new file mode 100644 index 0000000000..9356577b47 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-gregorian-proleptic.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure formatRange() uses a proleptic Gregorian calendar. +// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20705 + +let dtf = new Intl.DateTimeFormat("en", {timeZone: "UTC"}); + +// The Gregorian calendar was first introduced in 15 October 1582. +const firstGregorian = new Date("1582-10-15"); +assertEq(dtf.formatRange(firstGregorian, firstGregorian), dtf.format(firstGregorian)); + +// To deal with the ten days' difference between the Julian and the Gregorian calendar, some days +// were skipped, so that 4 October 1582 was followed by 15 October 1582. +const lastJulian = new Date("1582-10-04"); +assertEq(dtf.formatRange(lastJulian, lastJulian), dtf.format(lastJulian)); + +function rangePart(source, parts) { + return parts.filter(x => x.source === source).map(x => x.value).join(""); +} + +// Test with non-empty range. +assertEq(rangePart("startRange", dtf.formatRangeToParts(lastJulian, firstGregorian)), + dtf.format(lastJulian)); +assertEq(rangePart("endRange", dtf.formatRangeToParts(lastJulian, firstGregorian)), + dtf.format(firstGregorian)); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/formatRange-hour-cycle.js b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-hour-cycle.js new file mode 100644 index 0000000000..7921f502cc --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-hour-cycle.js @@ -0,0 +1,450 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test formatRange supports the different hour-cycle options. +// +// ICU bugs: +// https://unicode-org.atlassian.net/browse/ICU-21154 +// https://unicode-org.atlassian.net/browse/ICU-21155 +// https://unicode-org.atlassian.net/browse/ICU-21156 +// https://unicode-org.atlassian.net/browse/ICU-21342 +// https://unicode-org.atlassian.net/browse/ICU-21343 + +// Test locales which default to a 12-hour and a 24-hour clock. + +let tests = { + "en": [ + // Midnight to morning. + { + start: 0, + end: 10, + data: [ + "12 – 10 AM", + "0 – 10 AM", + "12 – 10 AM", + "00 – 10", + "24 – 10", + ], + }, + // Midnight to noon. + { + start: 0, + end: 12, + data: [ + "12 AM – 12 PM", + "0 AM – 0 PM", + "12 AM – 12 PM", + "00 – 12", + "24 – 12", + ], + }, + // Midnight to evening. + { + start: 0, + end: 22, + data: [ + "12 AM – 10 PM", + "0 AM – 10 PM", + "12 AM – 10 PM", + "00 – 22", + "24 – 22", + ], + }, + // Midnight to midnight. + { + start: 0, + end: 24, + data: [ + "1/1/1970, 12 AM – 1/2/1970, 12 AM", + "1/1/1970, 0 AM – 1/2/1970, 0 AM", + "1/1/1970, 12 AM – 1/2/1970, 12 AM", + "1/1/1970, 00 – 1/2/1970, 00", + "1/1/1970, 24 – 1/2/1970, 24", + ], + }, + + // Morning to morning. + { + start: 1, + end: 10, + data: [ + "1 – 10 AM", + "1 – 10 AM", + "1 – 10 AM", + "01 – 10", + "01 – 10", + ], + }, + // Morning to noon. + { + start: 1, + end: 12, + data: [ + "1 AM – 12 PM", + "1 AM – 0 PM", + "1 AM – 12 PM", + "01 – 12", + "01 – 12", + ], + }, + // Morning to evening. + { + start: 1, + end: 22, + data: [ + "1 AM – 10 PM", + "1 AM – 10 PM", + "1 AM – 10 PM", + "01 – 22", + "01 – 22", + ], + }, + // Morning to midnight. + { + start: 1, + end: 24, + data: [ + "1/1/1970, 1 AM – 1/2/1970, 12 AM", + "1/1/1970, 1 AM – 1/2/1970, 0 AM", + "1/1/1970, 1 AM – 1/2/1970, 12 AM", + "1/1/1970, 01 – 1/2/1970, 00", + "1/1/1970, 01 – 1/2/1970, 24", + ], + }, + + // Noon to morning. + { + start: 12, + end: 24 + 1, + data: [ + "1/1/1970, 12 PM – 1/2/1970, 1 AM", + "1/1/1970, 0 PM – 1/2/1970, 1 AM", + "1/1/1970, 12 PM – 1/2/1970, 1 AM", + "1/1/1970, 12 – 1/2/1970, 01", + "1/1/1970, 12 – 1/2/1970, 01", + ], + }, + // Noon to noon. + { + start: 12, + end: 24 + 12, + data: [ + "1/1/1970, 12 PM – 1/2/1970, 12 PM", + "1/1/1970, 0 PM – 1/2/1970, 0 PM", + "1/1/1970, 12 PM – 1/2/1970, 12 PM", + "1/1/1970, 12 – 1/2/1970, 12", + "1/1/1970, 12 – 1/2/1970, 12", + ], + }, + // Noon to evening. + { + start: 12, + end: 22, + data: [ + "12 – 10 PM", + "0 – 10 PM", + "12 – 10 PM", + "12 – 22", + "12 – 22", + ], + }, + // Noon to midnight. + { + start: 12, + end: 24, + data: [ + "1/1/1970, 12 PM – 1/2/1970, 12 AM", + "1/1/1970, 0 PM – 1/2/1970, 0 AM", + "1/1/1970, 12 PM – 1/2/1970, 12 AM", + "1/1/1970, 12 – 1/2/1970, 00", + "1/1/1970, 12 – 1/2/1970, 24", + ], + }, + + // Evening to morning. + { + start: 22, + end: 24 + 1, + data: [ + "1/1/1970, 10 PM – 1/2/1970, 1 AM", + "1/1/1970, 10 PM – 1/2/1970, 1 AM", + "1/1/1970, 10 PM – 1/2/1970, 1 AM", + "1/1/1970, 22 – 1/2/1970, 01", + "1/1/1970, 22 – 1/2/1970, 01", + ], + }, + // Evening to noon. + { + start: 22, + end: 24 + 12, + data: [ + "1/1/1970, 10 PM – 1/2/1970, 12 PM", + "1/1/1970, 10 PM – 1/2/1970, 0 PM", + "1/1/1970, 10 PM – 1/2/1970, 12 PM", + "1/1/1970, 22 – 1/2/1970, 12", + "1/1/1970, 22 – 1/2/1970, 12", + ], + }, + // Evening to evening. + { + start: 22, + end: 23, + data: [ + "10 – 11 PM", + "10 – 11 PM", + "10 – 11 PM", + "22 – 23", + "22 – 23", + ], + }, + // Evening to midnight. + { + start: 22, + end: 24, + data: [ + "1/1/1970, 10 PM – 1/2/1970, 12 AM", + "1/1/1970, 10 PM – 1/2/1970, 0 AM", + "1/1/1970, 10 PM – 1/2/1970, 12 AM", + "1/1/1970, 22 – 1/2/1970, 00", + "1/1/1970, 22 – 1/2/1970, 24", + ], + }, + ], + + "de": [ + // Midnight to morning. + { + start: 0, + end: 10, + data: [ + "00–10 Uhr", + "0 – 10 Uhr AM", + "12 – 10 Uhr AM", + "00–10 Uhr", + "24–10 Uhr", + ], + }, + // Midnight to noon. + { + start: 0, + end: 12, + data: [ + "00–12 Uhr", + "0 Uhr AM – 0 Uhr PM", + "12 Uhr AM – 12 Uhr PM", + "00–12 Uhr", + "24–12 Uhr", + ], + }, + // Midnight to evening. + { + start: 0, + end: 22, + data: [ + "00–22 Uhr", + "0 Uhr AM – 10 Uhr PM", + "12 Uhr AM – 10 Uhr PM", + "00–22 Uhr", + "24–22 Uhr", + ], + }, + // Midnight to midnight. + { + start: 0, + end: 24, + data: [ + "1.1.1970, 00 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 0 Uhr AM – 2.1.1970, 0 Uhr AM", + "1.1.1970, 12 Uhr AM – 2.1.1970, 12 Uhr AM", + "1.1.1970, 00 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 24 Uhr – 2.1.1970, 24 Uhr", + ], + }, + + // Morning to morning. + { + start: 1, + end: 10, + data: [ + "01–10 Uhr", + "1 – 10 Uhr AM", + "1 – 10 Uhr AM", + "01–10 Uhr", + "01–10 Uhr", + ], + }, + // Morning to noon. + { + start: 1, + end: 12, + data: [ + "01–12 Uhr", + "1 Uhr AM – 0 Uhr PM", + "1 Uhr AM – 12 Uhr PM", + "01–12 Uhr", + "01–12 Uhr", + ], + }, + // Morning to evening. + { + start: 1, + end: 22, + data: [ + "01–22 Uhr", + "1 Uhr AM – 10 Uhr PM", + "1 Uhr AM – 10 Uhr PM", + "01–22 Uhr", + "01–22 Uhr", + ], + }, + // Morning to midnight. + { + start: 1, + end: 24, + data: [ + "1.1.1970, 01 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 1 Uhr AM – 2.1.1970, 0 Uhr AM", + "1.1.1970, 1 Uhr AM – 2.1.1970, 12 Uhr AM", + "1.1.1970, 01 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 01 Uhr – 2.1.1970, 24 Uhr", + ], + }, + + // Noon to morning. + { + start: 12, + end: 24 + 1, + data: [ + "1.1.1970, 12 Uhr – 2.1.1970, 01 Uhr", + "1.1.1970, 0 Uhr PM – 2.1.1970, 1 Uhr AM", + "1.1.1970, 12 Uhr PM – 2.1.1970, 1 Uhr AM", + "1.1.1970, 12 Uhr – 2.1.1970, 01 Uhr", + "1.1.1970, 12 Uhr – 2.1.1970, 01 Uhr", + ], + }, + // Noon to noon. + { + start: 12, + end: 24 + 12, + data: [ + "1.1.1970, 12 Uhr – 2.1.1970, 12 Uhr", + "1.1.1970, 0 Uhr PM – 2.1.1970, 0 Uhr PM", + "1.1.1970, 12 Uhr PM – 2.1.1970, 12 Uhr PM", + "1.1.1970, 12 Uhr – 2.1.1970, 12 Uhr", + "1.1.1970, 12 Uhr – 2.1.1970, 12 Uhr", + ], + }, + // Noon to evening. + { + start: 12, + end: 22, + data: [ + "12–22 Uhr", + "0 – 10 Uhr PM", + "12 – 10 Uhr PM", + "12–22 Uhr", + "12–22 Uhr", + ], + }, + // Noon to midnight. + { + start: 12, + end: 24, + data: [ + "1.1.1970, 12 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 0 Uhr PM – 2.1.1970, 0 Uhr AM", + "1.1.1970, 12 Uhr PM – 2.1.1970, 12 Uhr AM", + "1.1.1970, 12 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 12 Uhr – 2.1.1970, 24 Uhr", + ], + }, + + // Evening to morning. + { + start: 22, + end: 24 + 1, + data: [ + "1.1.1970, 22 Uhr – 2.1.1970, 01 Uhr", + "1.1.1970, 10 Uhr PM – 2.1.1970, 1 Uhr AM", + "1.1.1970, 10 Uhr PM – 2.1.1970, 1 Uhr AM", + "1.1.1970, 22 Uhr – 2.1.1970, 01 Uhr", + "1.1.1970, 22 Uhr – 2.1.1970, 01 Uhr", + ], + }, + // Evening to noon. + { + start: 22, + end: 24 + 12, + data: [ + "1.1.1970, 22 Uhr – 2.1.1970, 12 Uhr", + "1.1.1970, 10 Uhr PM – 2.1.1970, 0 Uhr PM", + "1.1.1970, 10 Uhr PM – 2.1.1970, 12 Uhr PM", + "1.1.1970, 22 Uhr – 2.1.1970, 12 Uhr", + "1.1.1970, 22 Uhr – 2.1.1970, 12 Uhr", + ], + }, + // Evening to evening. + { + start: 22, + end: 23, + data: [ + "22–23 Uhr", + "10 – 11 Uhr PM", + "10 – 11 Uhr PM", + "22–23 Uhr", + "22–23 Uhr", + ], + }, + // Evening to midnight. + { + start: 22, + end: 24, + data: [ + "1.1.1970, 22 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 10 Uhr PM – 2.1.1970, 0 Uhr AM", + "1.1.1970, 10 Uhr PM – 2.1.1970, 12 Uhr AM", + "1.1.1970, 22 Uhr – 2.1.1970, 00 Uhr", + "1.1.1970, 22 Uhr – 2.1.1970, 24 Uhr", + ], + }, + ], +}; + +const hourCycles = [undefined, "h11", "h12", "h23", "h24"]; +const hour12Values = [true, false]; +const options = {hour: "2-digit", timeZone: "UTC"}; +const hourToMillis = 60 * 60 * 1000; + +for (let [locale, test] of Object.entries(tests)) { + // Find the matching hourCycle for each hour12 value. + let hour12Cycles = hour12Values.map(hour12 => { + let {hourCycle} = new Intl.DateTimeFormat(locale, {...options, hour12}).resolvedOptions(); + return hourCycles.indexOf(hourCycle); + }); + + for (let {start, end, data} of Object.values(test)) { + assertEq(data.length, hourCycles.length); + + // Test all possible "hourCycle" values, including |undefined|. + for (let i = 0; i < hourCycles.length; i++) { + let hourCycle = hourCycles[i]; + let expected = data[i]; + let dtf = new Intl.DateTimeFormat(locale, {...options, hourCycle}); + + assertEq(dtf.formatRange(start * hourToMillis, end * hourToMillis), expected, + `hourCycle: ${hourCycle}`); + } + + // Test all possible "hour12" values. + for (let i = 0; i < hour12Values.length; i++) { + let hour12 = hour12Values[i]; + let dtf = new Intl.DateTimeFormat(locale, {...options, hour12}); + let expected = data[hour12Cycles[i]]; + + assertEq(dtf.formatRange(start * hourToMillis, end * hourToMillis), expected, + `hour12: ${hour12}`); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/formatRange-matches-format-output.js b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-matches-format-output.js new file mode 100644 index 0000000000..67cebd1843 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-matches-format-output.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// formatRange() returns the same output as format() when the date-time difference between +// the start and end date is too small. + +// Test case when skeleton can't be retrieved from resolved pattern (bug 1298794). +// - Best-fit pattern for the skeleton "MMMMMdd" is "M月dd日". +// - Best-fit pattern for the skeleton "Mdd" is "M/dd". +// +// So in both cases the skeleton characters in the pattern are "Mdd", which means we can't +// retrieve the original input skeleton by simply inspecting the resolved pattern. +// +// Also see: https://unicode-org.atlassian.net/browse/ICU-13518 +{ + let dtf = new Intl.DateTimeFormat("zh", {month: "narrow", day: "2-digit", timeZone: "UTC"}); + assertEq(dtf.formatRange(0, 0), dtf.format(0)); +} + +// Test that date-/time-style leads to the same output. +// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20710 +{ + let dtf = new Intl.DateTimeFormat("en", {dateStyle: "full", timeStyle: "full"}); + assertEq(dtf.formatRange(0, 0), dtf.format(0)); +} + +// Test that the hourCycle option is correctly processed (test with h24). +// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20707 +{ + let dtf = new Intl.DateTimeFormat("en-u-hc-h24", {hour: "2-digit", timeZone:"UTC"}); + assertEq(dtf.formatRange(0, 0), dtf.format(0)); +} +{ + let dtf = new Intl.DateTimeFormat("en", {hourCycle: "h24", hour: "2-digit", timeZone:"UTC"}); + assertEq(dtf.formatRange(0, 0), dtf.format(0)); +} + +// Test that the hourCycle option is correctly processed (test with h11). +// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20707 +{ + let dt = 60 * 60 * 1000; // one hour + let dtf = new Intl.DateTimeFormat("en-u-hc-h11", {hour: "2-digit", timeZone:"UTC"}); + assertEq(dtf.formatRange(dt, dt), dtf.format(dt)); +} +{ + let dt = 60 * 60 * 1000; // one hour + let dtf = new Intl.DateTimeFormat("en", {hourCycle: "h11", hour: "2-digit", timeZone:"UTC"}); + assertEq(dtf.formatRange(dt, dt), dtf.format(dt)); +} + +// Test that non-default calendars work correctly. +// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20706 +{ + let dtf = new Intl.DateTimeFormat("en-001-u-ca-hebrew"); + assertEq(dtf.formatRange(0, 0), dtf.format(0)); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/formatRange-original-skeleton.js b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-original-skeleton.js new file mode 100644 index 0000000000..c24598794e --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/formatRange-original-skeleton.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test that the interval formatting uses the original skeleton, not the skeleton +// derived from the resolved pattern. +{ + let dtf = new Intl.DateTimeFormat("zh-Hans-CN", { + formatMatcher: "best fit", + month: "narrow", + day: "2-digit", + timeZone: "UTC" + }); + + assertEq(dtf.format(Date.UTC(2016, 7, 1)), "8月01日"); + assertEq(dtf.formatRange(Date.UTC(2016, 7, 1), Date.UTC(2016, 7, 2)), "8月1日至2日"); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/formatToParts.js b/js/src/tests/non262/Intl/DateTimeFormat/formatToParts.js new file mode 100644 index 0000000000..62f0d69406 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/formatToParts.js @@ -0,0 +1,96 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +// Tests the format function with a diverse set of locales and options. +// Always use UTC to avoid dependencies on test environment. + +const { + Era, Year, Month, Weekday, Day, DayPeriod, Hour, Minute, Second, Literal +} = DateTimeFormatParts + +var format; +var date = Date.UTC(2012, 11, 17, 3, 0, 42); + +// Locale en-US; default options. +format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"}); +assertParts(format, date, [ + Month("12"), Literal("/"), Day("17"), Literal("/"), Year("2012"), +]); + +// Just date +format = new Intl.DateTimeFormat("en-us", { + year: 'numeric', + month: 'numeric', + day: 'numeric', + timeZone: "UTC"}); +assertParts(format, date, [ + Month("12"), Literal("/"), Day("17"), Literal("/"), Year("2012"), +]); + +// Just time in hour24 +format = new Intl.DateTimeFormat("en-us", { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false, + timeZone: "UTC"}); +assertParts(format, date, [ + Hour("03"), Literal(":"), Minute("00"), Literal(":"), Second("42"), +]); + +// Just time in hour12 +format = new Intl.DateTimeFormat("en-us", { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true, + timeZone: "UTC"}); +assertParts(format, date, [ + Hour("3"), Literal(":"), Minute("00"), Literal(":"), Second("42"), Literal(" "), DayPeriod("AM"), +]); + +// Just month. +format = new Intl.DateTimeFormat("en-us", { + month: "narrow", + timeZone: "UTC"}); +assertParts(format, date, [ + Month("D"), +]); + +// Just weekday. +format = new Intl.DateTimeFormat("en-us", { + weekday: "narrow", + timeZone: "UTC"}); +assertParts(format, date, [ + Weekday("M"), +]); + +// Year and era. +format = new Intl.DateTimeFormat("en-us", { + year: "numeric", + era: "short", + timeZone: "UTC"}); +assertParts(format, date, [ + Year("2012"), Literal(" "), Era("AD"), +]); + +// Time and date +format = new Intl.DateTimeFormat("en-us", { + weekday: 'long', + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true, + timeZone: "UTC"}); +assertParts(format, date, [ + Weekday("Monday"), Literal(", "), Month("12"), Literal("/"), Day("17"), Literal("/"), Year("2012"), + Literal(", "), + Hour("3"), Literal(":"), Minute("00"), Literal(":"), Second("42"), Literal(" "), DayPeriod("AM"), +]); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone-non-meta.js b/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone-non-meta.js new file mode 100644 index 0000000000..f3de06bb97 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone-non-meta.js @@ -0,0 +1,62 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure we provide 'long' and 'short' descriptive names for non-meta time +// zones. (CLDR stores names for Etc/UTC, Europe/Dublin, and Europe/London as +// non-meta zones.) + +const date = new Date(Date.UTC(2018, 7-1, 24)); +const tests = [ + { + locale: "en-US", + timeZoneName: "long", + zones: { + "Etc/UTC": "7/24/2018, Coordinated Universal Time", + "Europe/Dublin": "7/24/2018, Irish Standard Time", + "Europe/London": "7/24/2018, British Summer Time", + } + }, + { + locale: "de", + timeZoneName: "long", + zones: { + "Etc/UTC": "24.7.2018, Koordinierte Weltzeit", + "Europe/Dublin": "24.7.2018, Irische Sommerzeit", + "Europe/London": "24.7.2018, Britische Sommerzeit", + } + }, + + { + locale: "en-US", + timeZoneName: "short", + zones: { + "Europe/Dublin": "7/24/2018, GMT+1", + "Europe/London": "7/24/2018, GMT+1", + } + }, + { + locale: "en-GB", + timeZoneName: "short", + zones: { + "Europe/Dublin": "24/07/2018, GMT+1", + "Europe/London": "24/07/2018, BST", + } + }, + { + locale: "en-IE", + timeZoneName: "short", + zones: { + "Europe/Dublin": "24/7/2018, IST", + "Europe/London": "24/7/2018, GMT+1", + } + }, +]; + +for (let {locale, timeZoneName, zones} of tests) { + for (let [timeZone, expected] of Object.entries(zones)) { + assertEq(new Intl.DateTimeFormat(locale, {timeZone, timeZoneName}).format(date), + expected); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone.js b/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone.js new file mode 100644 index 0000000000..5cc3bf10cf --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/format_timeZone.js @@ -0,0 +1,104 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = { + year: "numeric", month: "numeric", day: "numeric", + hour: "numeric", minute: "numeric", second: "numeric", +}; +const longFormatOptions = Object.assign({}, defaultOptions, { + month: "long" +}); +const tzNameFormatOptions = Object.assign({}, defaultOptions, { + timeZoneName: "short" +}); + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + +const tests = [ + { + timeZone: "UTC", + result: "12/6/2012, 12:00:00 PM", + }, + { + timeZone: "America/Los_Angeles", + result: "12/6/2012, 4:00:00 AM", + }, + { + timeZone: "America/New_York", + options: tzNameFormatOptions, + result: "12/6/2012, 7:00:00 AM EST", + }, + { + timeZone: "America/Caracas", + result: "12/6/2012, 7:30:00 AM", + }, + { + timeZone: "Europe/London", + result: "12/6/2012, 12:00:00 PM", + }, + { + timeZone: "Africa/Casablanca", + locale: "ar-MA-u-ca-islamicc", options: longFormatOptions, + result: "22 محرم 1434 هـ في 12:00:00", + }, + { + timeZone: "Europe/Berlin", + locale: "de-DE", options: tzNameFormatOptions, + result: "6.12.2012, 13:00:00 MEZ", + }, + { + timeZone: "Asia/Kathmandu", + result: "12/6/2012, 5:45:00 PM", + }, + { + timeZone: "Asia/Bangkok", + locale: "th-th-u-nu-thai", options: longFormatOptions, + result: "๖ ธันวาคม ๒๕๕๕ เวลา ๑๙:๐๐:๐๐", + }, + { + timeZone: "Asia/Tokyo", + locale: "ja-JP", options: longFormatOptions, + result: "2012年12月6日 21:00:00", + }, + { + timeZone: "Australia/Lord_Howe", + result: "12/6/2012, 11:00:00 PM", + }, + { + timeZone: "Australia/Lord_Howe", + date: Date.UTC(2012, 7-1, 6, 12, 0, 0), + result: "7/6/2012, 10:30:00 PM", + }, + { + timeZone: "Pacific/Kiritimati", + date: Date.UTC(1978, 12-1, 6, 12, 0, 0), + result: "12/6/1978, 1:20:00 AM", + }, + { + timeZone: "Africa/Monrovia", + date: Date.UTC(1971, 12-1, 6, 12, 0, 0), + result: "12/6/1971, 11:15:30 AM", + }, + { + timeZone: "Asia/Riyadh", + date: Date.UTC(1946, 12-1, 6, 12, 0, 0), + result: "12/6/1946, 3:06:52 PM", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(locale, Object.assign({timeZone: map(timeZone)}, options)); + assertEq(dtf.format(date), result); + assertEq(dtf.resolvedOptions().timeZone, timeZone); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits-append-item.js b/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits-append-item.js new file mode 100644 index 0000000000..0ef5540179 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits-append-item.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + DayPeriod, Hour, Minute, Second, FractionalSecond, Literal +} = DateTimeFormatParts + +const tests = [ + // https://unicode-org.atlassian.net/browse/CLDR-13184 + // https://unicode-org.atlassian.net/browse/CLDR-13623 + { + locale: "en", + date: new Date("2020-01-01T00:00:00.123"), + options: {hour: "numeric", fractionalSecondDigits: 3}, + parts: [ + Hour("12"), + Literal(" "), + DayPeriod("AM"), + Literal(" (Fractional Second: "), + FractionalSecond("123"), + Literal(")") + ] + }, + + // https://unicode-org.atlassian.net/browse/ICU-20992 + { + locale: "ckb-IR", + date: new Date("2020-01-01T00:00:00.123"), + options: {minute: "2-digit", fractionalSecondDigits: 3}, + parts: [ + Minute("٠٠"), + Literal(":"), + Second("٠٠"), + Literal("٫"), + FractionalSecond("١٢٣"), + ] + }, + + // https://unicode-org.atlassian.net/browse/ICU-20992 + { + locale: "ckb-IR", + date: new Date("2020-01-01T00:00:00.123"), + options: {dayPeriod: "short", fractionalSecondDigits: 3}, + parts: [ + FractionalSecond("١٢٣"), + Literal(" (Dayperiod: "), + DayPeriod("ب.ن"), + Literal(")") + ] + }, +]; + +for (let {locale, date, options, parts} of tests) { + let dtf = new Intl.DateTimeFormat(locale, options); + assertParts(dtf, date, parts); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits.js b/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits.js new file mode 100644 index 0000000000..c6c29be604 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/fractional-second-digits.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Second, FractionalSecond, Literal +} = DateTimeFormatParts + +const tests = [ + { + date: new Date("2019-01-01T00:00:00.123"), + digits: { + 1: [Second("0"), Literal("."), FractionalSecond("1")], + 2: [Second("0"), Literal("."), FractionalSecond("12")], + 3: [Second("0"), Literal("."), FractionalSecond("123")], + } + }, + { + date: new Date("2019-01-01T00:00:00.023"), + digits: { + 1: [Second("0"), Literal("."), FractionalSecond("0")], + 2: [Second("0"), Literal("."), FractionalSecond("02")], + 3: [Second("0"), Literal("."), FractionalSecond("023")], + } + }, + { + date: new Date("2019-01-01T00:00:00.003"), + digits: { + 1: [Second("0"), Literal("."), FractionalSecond("0")], + 2: [Second("0"), Literal("."), FractionalSecond("00")], + 3: [Second("0"), Literal("."), FractionalSecond("003")], + } + }, +]; + +for (let {date, digits} of tests) { + for (let [fractionalSecondDigits, parts] of Object.entries(digits)) { + let dtf = new Intl.DateTimeFormat("en", {second: "numeric", fractionalSecondDigits}); + + assertParts(dtf, date, parts); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/hourCycle.js b/js/src/tests/non262/Intl/DateTimeFormat/hourCycle.js new file mode 100644 index 0000000000..21414c72cf --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/hourCycle.js @@ -0,0 +1,142 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const hourCycleToH12Map = { + "h11": true, + "h12": true, + "h23": false, + "h24": false, +}; + +for (const key of Object.keys(hourCycleToH12Map)) { + const langTag = "en-US"; + const loc = `${langTag}-u-hc-${key}`; + + const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"}); + const dtf2 = new Intl.DateTimeFormat(langTag, {hour: "numeric", hourCycle: key}); + assertEq(dtf.resolvedOptions().hourCycle, dtf2.resolvedOptions().hourCycle); +} + + +/* Legacy hour12 compatibility */ + +// When constructed with hourCycle option, resolvedOptions' hour12 is correct. +for (const key of Object.keys(hourCycleToH12Map)) { + const dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", hourCycle: key}); + assertEq(dtf.resolvedOptions().hour12, hourCycleToH12Map[key]); +} + +// When constructed with hour12 option, resolvedOptions' hourCycle is correct +for (const [key, value] of Object.entries(hourCycleToH12Map)) { + const dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", hour12: value}); + assertEq(hourCycleToH12Map[dtf.resolvedOptions().hourCycle], value); +} + +// When constructed with both hour12 and hourCycle options that don't match +// hour12 takes a precedence. +for (const [key, value] of Object.entries(hourCycleToH12Map)) { + const dtf = new Intl.DateTimeFormat("en-US", { + hour: "numeric", + hourCycle: key, + hour12: !value + }); + assertEq(hourCycleToH12Map[dtf.resolvedOptions().hourCycle], !value); + assertEq(dtf.resolvedOptions().hour12, !value); +} + +// When constructed with hourCycle as extkey, resolvedOptions' hour12 is correct. +for (const [key, value] of Object.entries(hourCycleToH12Map)) { + const langTag = "en-US"; + const loc = `${langTag}-u-hc-${key}`; + + const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"}); + assertEq(dtf.resolvedOptions().hour12, value); +} + +const expectedValuesENUS = { + h11: "0 AM", + h12: "12 AM", + h23: "00", + h24: "24" +}; + +const exampleDate = new Date(2017, 10-1, 10, 0); +for (const [key, val] of Object.entries(expectedValuesENUS)) { + assertEq( + Intl.DateTimeFormat("en-US", {hour: "numeric", hourCycle: key}).format(exampleDate), + val + ); +} + +const invalidHourCycleValues = [ + "h28", + "f28", +]; + +for (const key of invalidHourCycleValues) { + const langTag = "en-US"; + const loc = `${langTag}-u-hc-${key}`; + + const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"}); + assertEq(dtf.resolvedOptions().hour12, true); // default value for en-US + assertEq(dtf.resolvedOptions().hourCycle, "h12"); //default value for en-US +} + +{ + // hourCycle is not present in resolvedOptions when the formatter has no hour field + const options = Intl.DateTimeFormat("en-US", {hourCycle:"h11"}).resolvedOptions(); + assertEq("hourCycle" in options, false); + assertEq("hour12" in options, false); +} + +{ + // Make sure that hourCycle option overrides the unicode extension + let dtf = Intl.DateTimeFormat("en-US-u-hc-h23", {hourCycle: "h24", hour: "numeric"}); + assertEq( + dtf.resolvedOptions().hourCycle, + "h24" + ); +} + +{ + // Make sure that hour12 option overrides the unicode extension + let dtf = Intl.DateTimeFormat("en-US-u-hc-h23", {hour12: true, hour: "numeric"}); + assertEq( + dtf.resolvedOptions().hourCycle, + "h12" + ); +} + +{ + // Make sure that hour12 option overrides hourCycle options + let dtf = Intl.DateTimeFormat("en-US", + {hourCycle: "h12", hour12: false, hour: "numeric"}); + assertEq( + dtf.resolvedOptions().hourCycle, + "h23" + ); +} + +{ + // Make sure that hour12 option overrides hourCycle options + let dtf = Intl.DateTimeFormat("en-u-hc-h11", {hour: "numeric"}); + assertEq( + dtf.resolvedOptions().locale, + "en-u-hc-h11" + ); +} + +{ + // Make sure that hour12 option overrides unicode extension + let dtf = Intl.DateTimeFormat("en-u-hc-h11", {hour: "numeric", hourCycle: "h24"}); + assertEq( + dtf.resolvedOptions().locale, + "en" + ); + assertEq( + dtf.resolvedOptions().hourCycle, + "h24" + ); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/implied-script-has-consistent-output.js b/js/src/tests/non262/Intl/DateTimeFormat/implied-script-has-consistent-output.js new file mode 100644 index 0000000000..6c0b4d2f59 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/implied-script-has-consistent-output.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +// Retrieve all available locales of Intl.DateTimeFormat. +const available = getAvailableLocalesOf("DateTimeFormat"); + +const options = [ + // Include "hour" to catch hour cycle differences. + // + // For example "ff-Latn-GH" (Fulah as spoken in Ghana) uses a 12-hour clock, + // whereas "ff" (Fulah) uses a 24-hour clock. When the user creates the + // formatter for "ff-GH", it should use the same formatter data as "ff-Latn-GH" + // and it shouldn't fallback to "ff". + {hour: "2-digit", minute: "2-digit", timeZone: "UTC"}, + + // Include "timeZoneName" to catch script differences, e.g traditional or + // simplified Chinese characters. + {timeZoneName: "long", timeZone: "America/Los_Angeles"}, +]; + +// Pick a date after 12 pm to catch any hour cycle differences. +const date = Date.UTC(2021, 6-1, 7, 15, 0); + +available.map(x => { + return new Intl.Locale(x); +}).filter(loc => { + // Find all locales which have both a script and a region subtag. + return loc.script && loc.region; +}).filter(loc => { + // Skip "sd-Deva-IN" and Fulah because of <https://unicode-org.atlassian.net/browse/ICU-21974>. + return !((loc.language === "sd" && loc.script === "Deva" && loc.region === "IN") || + (loc.language === "ff" && (loc.script === "Adlm" || loc.script === "Latn") && + (loc.region === "GH" || loc.region === "GM" || loc.region === "LR" || loc.region === "SL")) + ); +}).forEach(loc => { + // Remove the script subtag from the locale. + let noScript = new Intl.Locale(`${loc.language}-${loc.region}`); + + // Add the likely script subtag. + let maximized = noScript.maximize(); + + for (let opt of options) { + // Formatter for the locale without a script subtag. + let df1 = new Intl.DateTimeFormat(noScript, opt); + + // Formatter for the locale with the likely script subtag added. + let df2 = new Intl.DateTimeFormat(maximized, opt); + + // The output for the locale without a script subtag should match the output + // with the likely script subtag added. + assertEq(df1.format(date), df2.format(date), `Mismatch for locale "${noScript}" (${maximized})`); + } +}); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/islamic.js b/js/src/tests/non262/Intl/DateTimeFormat/islamic.js new file mode 100644 index 0000000000..3a88590ea5 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/islamic.js @@ -0,0 +1,89 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function civilDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-civil-nu-latn", opts).format(date); +} + +function tabularDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-tbla-nu-latn", opts).format(date); +} + +function sightingDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-rgsa-nu-latn", opts).format(date); +} + +function ummAlQuraDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-umalqura-nu-latn", opts).format(date); +} + +// Test islamic-tbla (Tabular / Thursday epoch). +// Compare with islamic-civil (Tabular / Friday epoch). +function testIslamicTbla() { + var date = new Date(Date.UTC(2015, 1 - 1, 1)); + + // Month and year are the same. + var monthYear = {year: "numeric", month: "numeric"}; + assertEq(civilDate(monthYear, date), tabularDate(monthYear, date)); + + // Day is different by one. + var day = {day: "numeric"}; + assertEq(Number(civilDate(day, date)) - Number(tabularDate(day, date)), -1); +} + +// Test islamic-rgsa (Saudi Arabia sighting). +// Sighting of the hilal (crescent moon) in Saudi Arabia. +function testIslamicRgsa() { + var date1 = new Date(Date.UTC(1975, 5 - 1, 6)); + var date2 = new Date(Date.UTC(2015, 1 - 1, 1)); + var dayMonthYear = {year: "numeric", month: "numeric", day: "numeric"}; + + assertEq(sightingDate(dayMonthYear, date1), tabularDate(dayMonthYear, date1)); + assertEq(sightingDate(dayMonthYear, date2), civilDate(dayMonthYear, date2)); +} + +// Test islamic-umalqura (Umm al-Qura). +function testIslamicUmalqura() { + var year = {year: "numeric"}; + var month = {month: "numeric"}; + var day = {day: "numeric"}; + + // From ICU test files, which in turn was generated from: + // Official Umm-al-Qura calendar of SA: + // home, http://www.ummulqura.org.sa/default.aspx + // converter, http://www.ummulqura.org.sa/Index.aspx + var dates = [ + [ {year: 2016, month: 1, day: 11}, {year: 1437, month: 4, day: 1} ], + [ {year: 2016, month: 2, day: 10}, {year: 1437, month: 5, day: 1} ], + [ {year: 2016, month: 3, day: 10}, {year: 1437, month: 6, day: 1} ], + [ {year: 2016, month: 4, day: 8}, {year: 1437, month: 7, day: 1} ], + [ {year: 2016, month: 5, day: 8}, {year: 1437, month: 8, day: 1} ], + [ {year: 2016, month: 6, day: 6}, {year: 1437, month: 9, day: 1} ], + [ {year: 2016, month: 7, day: 6}, {year: 1437, month: 10, day: 1} ], + [ {year: 2016, month: 8, day: 4}, {year: 1437, month: 11, day: 1} ], + [ {year: 2016, month: 9, day: 2}, {year: 1437, month: 12, day: 1} ], + [ {year: 2016, month: 10, day: 2}, {year: 1438, month: 1, day: 1} ], + [ {year: 2016, month: 11, day: 1}, {year: 1438, month: 2, day: 1} ], + [ {year: 2016, month: 11, day: 30}, {year: 1438, month: 3, day: 1} ], + [ {year: 2016, month: 12, day: 30}, {year: 1438, month: 4, day: 1} ], + ]; + + for (var [gregorian, ummAlQura] of dates) { + var date = new Date(Date.UTC(gregorian.year, gregorian.month - 1, gregorian.day)); + + // Use parseInt() to remove the trailing era indicator. + assertEq(parseInt(ummAlQuraDate(year, date), 10), ummAlQura.year); + assertEq(Number(ummAlQuraDate(month, date)), ummAlQura.month); + assertEq(Number(ummAlQuraDate(day, date)), ummAlQura.day); + } +} + +testIslamicTbla(); +testIslamicRgsa(); +testIslamicUmalqura(); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/japanese-gannen-year.js b/js/src/tests/non262/Intl/DateTimeFormat/japanese-gannen-year.js new file mode 100644 index 0000000000..e972d2e55a --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/japanese-gannen-year.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var dtf = new Intl.DateTimeFormat("ja-u-ca-japanese", { + era: "short", + timeZone: "Asia/Tokyo", +}); + +var endShowa = new Date("1989-01-07T00:00:00.000Z"); +var startHeisei = new Date("1989-01-08T00:00:00.000Z"); + +assertEq(dtf.format(endShowa), "昭和64/1/7"); +assertEq(dtf.format(startHeisei), "平成1/1/8"); + +var parts = dtf.formatToParts(startHeisei); +assertEq(parts.filter(p => p.type === "era")[0].value, "平成"); +assertEq(parts.filter(p => p.type === "year")[0].value, "1"); + +var dtf = new Intl.DateTimeFormat("ja-u-ca-japanese", { + era: "short", + year: "numeric", + month: "long", + day: "numeric", + timeZone: "Asia/Tokyo", +}); + +assertEq(dtf.format(endShowa), "昭和64年1月7日"); +assertEq(dtf.format(startHeisei), "平成元年1月8日"); + +var parts = dtf.formatToParts(startHeisei); +assertEq(parts.filter(p => p.type === "era")[0].value, "平成"); +assertEq(parts.filter(p => p.type === "year")[0].value, "元"); + +// ICU returns mixed numbers when an explicit numbering system is present. + +var dtf = new Intl.DateTimeFormat("ja-u-ca-japanese-nu-arab", { + era: "short", + timeZone: "Asia/Tokyo", +}); + +assertEq(dtf.format(endShowa), "昭和٦٤/١/٧"); +assertEq(dtf.format(startHeisei), "平成١/١/٨"); + +var dtf = new Intl.DateTimeFormat("ja-u-ca-japanese-nu-arab", { + era: "short", + year: "numeric", + month: "numeric", + timeZone: "Asia/Tokyo", +}); + +assertEq(dtf.format(endShowa), "昭和64年١月"); +assertEq(dtf.format(startHeisei), "平成元年١月"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/mozExtensions.js b/js/src/tests/non262/Intl/DateTimeFormat/mozExtensions.js new file mode 100644 index 0000000000..7db314d413 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/mozExtensions.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let mozIntl = {}; +addIntlExtras(mozIntl); + +// Pattern + +var dtf = new Intl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"}); +var mozDtf = new mozIntl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"}); + +assertEq(dtf.resolvedOptions().hasOwnProperty('pattern'), false); +assertEq(mozDtf.resolvedOptions().pattern, "HH:mm MM/dd/YYYY"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/numberingSystem-option.js b/js/src/tests/non262/Intl/DateTimeFormat/numberingSystem-option.js new file mode 100644 index 0000000000..474910da47 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/numberingSystem-option.js @@ -0,0 +1,60 @@ +const defaultLocale = "en"; +const defaultNumberingSystem = new Intl.DateTimeFormat(defaultLocale).resolvedOptions().numberingSystem; + +function createWithLocale(locale, numberingSystem) { + return new Intl.DateTimeFormat(locale, {numberingSystem}); +} + +function create(numberingSystem) { + return createWithLocale(defaultLocale, numberingSystem); +} + +// Empty string should throw. +assertThrowsInstanceOf(() => create(""), RangeError); + +// Trailing \0 should throw. +assertThrowsInstanceOf(() => create("latn\0"), RangeError); + +// Too short or too long strings should throw. +assertThrowsInstanceOf(() => create("a"), RangeError); +assertThrowsInstanceOf(() => create("toolongstring"), RangeError); + +// Throw even when prefix is valid. +assertThrowsInstanceOf(() => create("latn-toolongstring"), RangeError); + +// |numberingSystem| can be set to |undefined|. +let dtf = create(undefined); +assertEq(dtf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Unsupported numbering systems are ignored. +dtf = create("xxxxxxxx"); +assertEq(dtf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Numbering system in options overwrite Unicode extension keyword. +dtf = createWithLocale(`${defaultLocale}-u-nu-thai`, "arab"); +assertEq(dtf.resolvedOptions().locale, defaultLocale); +assertEq(dtf.resolvedOptions().numberingSystem, "arab"); + +// |numberingSystem| option ignores case. +dtf = create("ARAB"); +assertEq(dtf.resolvedOptions().locale, defaultLocale); +assertEq(dtf.resolvedOptions().numberingSystem, "arab"); + +for (let [numberingSystem, {algorithmic}] of Object.entries(numberingSystems)) { + let dtf1 = new Intl.DateTimeFormat(`${defaultLocale}-u-nu-${numberingSystem}`); + let dtf2 = new Intl.DateTimeFormat(defaultLocale, {numberingSystem}); + + if (!algorithmic) { + assertEq(dtf1.resolvedOptions().numberingSystem, numberingSystem); + assertEq(dtf2.resolvedOptions().numberingSystem, numberingSystem); + } else { + // We don't yet support algorithmic numbering systems. + assertEq(dtf1.resolvedOptions().numberingSystem, defaultNumberingSystem); + assertEq(dtf2.resolvedOptions().numberingSystem, defaultNumberingSystem); + } + + assertEq(dtf2.format(0), dtf1.format(0)); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/options-property-accesses.js b/js/src/tests/non262/Intl/DateTimeFormat/options-property-accesses.js new file mode 100644 index 0000000000..e9a13c1534 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/options-property-accesses.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var log; +var proxy = new Proxy({ + year: "numeric", + hour: "numeric", +}, new Proxy({ + get(t, pk, r) { + log.push(pk); + return Reflect.get(t, pk, r); + } +}, { + get(t, pk, r) { + assertEq(pk, "get"); + return Reflect.get(t, pk, r); + } +})); + +var constructorAccesses = [ + // ToDateTimeOptions(options, "any", "date"). + "weekday", "year", "month", "day", + "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", + "dateStyle", "timeStyle", + + // InitializeDateTimeFormat + "localeMatcher", "calendar", "numberingSystem", "hour12", "hourCycle", "timeZone", + + // Table 5: Components of date and time formats + "weekday", "era", "year", "month", "day", "dayPeriod", "hour", "minute", "second", + "fractionalSecondDigits", "timeZoneName", + + // InitializeDateTimeFormat + "formatMatcher", + "dateStyle", "timeStyle", +]; + +log = []; +new Intl.DateTimeFormat(undefined, proxy); + +assertEqArray(log, constructorAccesses); + +log = []; +new Date().toLocaleString(undefined, proxy); + +assertEqArray(log, [ + // ToDateTimeOptions(options, "any", "all"). + "weekday", "year", "month", "day", + "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", + "dateStyle", "timeStyle", + + ...constructorAccesses +]); + +log = []; +new Date().toLocaleDateString(undefined, proxy); + +assertEqArray(log, [ + // ToDateTimeOptions(options, "date", "date"). + "weekday", "year", "month", "day", + "dateStyle", "timeStyle", + + ...constructorAccesses +]); + +log = []; +new Date().toLocaleTimeString(undefined, proxy); + +assertEqArray(log, [ + // ToDateTimeOptions(options, "time", "time"). + "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", + "dateStyle", "timeStyle", + + ...constructorAccesses +]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/related-year.js b/js/src/tests/non262/Intl/DateTimeFormat/related-year.js new file mode 100644 index 0000000000..296de04883 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/related-year.js @@ -0,0 +1,185 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Era, Year, YearName, RelatedYear, Month, Day, Literal +} = DateTimeFormatParts + +const tests = [ + // Test with non-leap month. + { + date: new Date("2020-04-23T00:00:00Z"), + options: {}, + calendar: "chinese", + locales: { + "en": [Month("4"), Literal("/"), Day("1"), Literal("/"), RelatedYear("2020")], + "de": [Day("1"), Literal("."), Month("4"), Literal("."), Year("37")], + "ja": [YearName("庚子"), Literal("-"), Month("4"), Literal("-"), Day("1")], + "zh": [RelatedYear("2020"), Literal("年"), Month("四月"), Day("1")], + "ar": [RelatedYear("٢٠٢٠"), Literal("-"), Month("٠٤"), Literal("-"), Day("٠١")], + } + }, + + // Test with leap month. + { + date: new Date("2020-05-23T00:00:00Z"), + options: {}, + calendar: "chinese", + locales: { + "en": [Month("4bis"), Literal("/"), Day("1"), Literal("/"), RelatedYear("2020")], + "de": [Day("1"), Literal("."), Month("4bis"), Literal("."), Year("37")], + "ja": [YearName("庚子"), Literal("-"), Month("閏4"), Literal("-"), Day("1")], + "zh": [RelatedYear("2020"), Literal("年"), Month("闰四月"), Day("1")], + "ar": [RelatedYear("٢٠٢٠"), Literal("-"), Month("٠٤bis"), Literal("-"), Day("٠١")], + } + }, + + // Display only "year" field. + { + date: new Date("2020-04-23T00:00:00Z"), + options: {year: "numeric"}, + calendar: "chinese", + locales: { + "en": [RelatedYear("2020"), Literal("("), YearName("geng-zi"), Literal(")")], + "de": [YearName("geng-zi")], + "ja": [YearName("庚子"), Literal("年")], + "zh": [RelatedYear("2020"), YearName("庚子"), Literal("年")], + "ar": [Year("٣٧")], + } + }, + + // Display only "month" field. + { + date: new Date("2020-04-23T00:00:00Z"), + options: {month: "long"}, + calendar: "chinese", + locales: { + "en": [Month("Fourth Month")], + "de": [Month("M04")], + "ja": [Month("四月")], + "zh": [Month("四月")], + "ar": [Month("M04")], + } + }, + + // Display only "month" field. (Leap month) + { + date: new Date("2020-05-23T00:00:00Z"), + options: {month: "long"}, + calendar: "chinese", + locales: { + "en": [Month("Fourth Monthbis")], + "de": [Month("M04bis")], + "ja": [Month("閏四月")], + "zh": [Month("闰四月")], + "ar": [Month("M04bis")], + } + }, + + // Display "year" and "month" fields. + { + date: new Date("2020-04-23T00:00:00Z"), + options: {year: "numeric", month: "long"}, + calendar: "chinese", + locales: { + "en": [Month("Fourth Month"), Literal(" "), RelatedYear("2020"), Literal("("), YearName("geng-zi"), Literal(")")], + "de": [Month("M04"), Literal(" "), YearName("geng-zi")], + "ja": [YearName("庚子"), Literal("年"), Month("四月")], + "zh": [RelatedYear("2020"), YearName("庚子"), Literal("年"), Month("四月")], + "ar": [RelatedYear("٢٠٢٠"), Literal("("), YearName("geng-zi"), Literal(") "), Month("M04")], + } + }, + + // Display "year" and "month" fields. (Leap month) + { + date: new Date("2020-05-23T00:00:00Z"), + options: {year: "numeric", month: "long"}, + calendar: "chinese", + locales: { + "en": [Month("Fourth Monthbis"), Literal(" "), RelatedYear("2020"), Literal("("), YearName("geng-zi"), Literal(")")], + "de": [Month("M04bis"), Literal(" "), YearName("geng-zi")], + "ja": [YearName("庚子"), Literal("年"), Month("閏四月")], + "zh": [RelatedYear("2020"), YearName("庚子"), Literal("年"), Month("闰四月")], + "ar": [RelatedYear("٢٠٢٠"), Literal("("), YearName("geng-zi"), Literal(") "), Month("M04bis")], + } + }, + + // Related year in traditional Korean calendar. + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "dangi", + locales: { + "en": [Month("11"), Literal("/"), Day("26"), Literal("/"), RelatedYear("2018")], + "ko": [RelatedYear("2018"), Literal(". "), Month("11"), Literal(". "), Day("26"), Literal(".")], + } + }, + + // Allowing the calendar to modify the pattern selection choice can result in falling back to + // the root locale patterns in more cases. That can result in displaying the era field by + // default, among other things. + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "buddhist", + locales: { + "en": [Month("1"), Literal("/"), Day("1"), Literal("/"), Year("2562"), Literal(" "), Era("BE")], + "th": [Day("1"), Literal("/"), Month("1"), Literal("/"), Year("2562")], + } + }, + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "hebrew", + locales: { + "en": [Day("24"), Literal(" "), Month("Tevet"), Literal(" "), Year("5779")], + "he": [Day("24"), Literal(" ב"), Month("טבת"), Literal(" "), Year("5779")], + "fr": [Day("24"), Literal("/"), Month("04"), Literal("/"), Year("5779"), Literal(" "), Era("A. M.")], + } + }, + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "islamic", + locales: { + "en": [Month("4"), Literal("/"), Day("25"), Literal("/"), Year("1440"), Literal(" "), Era("AH")], + "ar": [Day("٢٥"), Literal("\u200F/"), Month("٤"), Literal("\u200F/"), Year("١٤٤٠"), Literal(" "), Era("هـ")], + } + }, + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "japanese", + locales: { + "en": [Month("1"), Literal("/"), Day("1"), Literal("/"), Year("31"), Literal(" "), Era("H")], + "ja": [Era("H"), Year("31"), Literal("/"), Month("1"), Literal("/"), Day("1")], + } + }, + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "persian", + locales: { + "en": [Month("10"), Literal("/"), Day("11"), Literal("/"), Year("1397"), Literal(" "), Era("AP")], + "fa": [Year("۱۳۹۷"), Literal("/"), Month("۱۰"), Literal("/"), Day("۱۱")], + } + }, + { + date: new Date("2019-01-01T00:00:00Z"), + options: {}, + calendar: "roc", + locales: { + "en": [Month("1"), Literal("/"), Day("1"), Literal("/"), Year("108"), Literal(" "), Era("Minguo")], + "zh-Hant-TW": [Era("民國"), Year("108"), Literal("/"), Month("1"), Literal("/"), Day("1")], + } + }, +]; + +for (let {date, options, calendar, locales} of tests) { + for (let [locale, result] of Object.entries(locales)) { + let df = new Intl.DateTimeFormat(`${locale}-u-ca-${calendar}`, {timeZone: "UTC", ...options}); + assertParts(df, date, result); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/shell.js b/js/src/tests/non262/Intl/DateTimeFormat/shell.js new file mode 100644 index 0000000000..c2f9e7138b --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/shell.js @@ -0,0 +1,34 @@ +function GenericPartCreator(type) { + return str => ({ type, value: str }); +} + +const DateTimeFormatParts = { + Weekday: GenericPartCreator("weekday"), + Era: GenericPartCreator("era"), + Year: GenericPartCreator("year"), + YearName: GenericPartCreator("yearName"), + RelatedYear: GenericPartCreator("relatedYear"), + Month: GenericPartCreator("month"), + Day: GenericPartCreator("day"), + DayPeriod: GenericPartCreator("dayPeriod"), + Hour: GenericPartCreator("hour"), + Minute: GenericPartCreator("minute"), + Second: GenericPartCreator("second"), + FractionalSecond: GenericPartCreator("fractionalSecond"), + TimeZoneName: GenericPartCreator("timeZoneName"), + Unknown: GenericPartCreator("unknown"), + Literal: GenericPartCreator("literal"), +}; + +function assertParts(df, x, expected) { + var parts = df.formatToParts(x); + assertEq(parts.map(part => part.value).join(""), df.format(x), + "formatToParts and format must agree"); + + var len = parts.length; + assertEq(len, expected.length, "parts count mismatch"); + for (var i = 0; i < len; i++) { + assertEq(parts[i].type, expected[i].type, "type mismatch at " + i); + assertEq(parts[i].value, expected[i].value, "value mismatch at " + i); + } +} diff --git a/js/src/tests/non262/Intl/DateTimeFormat/standalone-month.js b/js/src/tests/non262/Intl/DateTimeFormat/standalone-month.js new file mode 100644 index 0000000000..c8aab5888e --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/standalone-month.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +for (let weekday of ["long", "short", "narrow"]) { + let dtf = new Intl.DateTimeFormat("en", {weekday}); + let options = dtf.resolvedOptions(); + + assertEq(options.weekday, weekday); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/supportedLocalesOf.js b/js/src/tests/non262/Intl/DateTimeFormat/supportedLocalesOf.js new file mode 100644 index 0000000000..b957f9cd60 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/supportedLocalesOf.js @@ -0,0 +1,371 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell) +// -- test in browser only that ICU has locale data for all Mozilla languages + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This array contains the locales that ICU supports in +// date and time formatting whose languages Mozilla localizes Firefox into. +// Current as of ICU 50.1.2 and Firefox March 2013. +var locales = [ + "af", + "af-NA", + "af-ZA", + "ar", + "ar-001", + "ar-AE", + "ar-BH", + "ar-DJ", + "ar-DZ", + "ar-EG", + "ar-EH", + "ar-ER", + "ar-IL", + "ar-IQ", + "ar-JO", + "ar-KM", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-MR", + "ar-OM", + "ar-PS", + "ar-QA", + "ar-SA", + "ar-SD", + "ar-SO", + "ar-SY", + "ar-TD", + "ar-TN", + "ar-YE", + "as", + "as-IN", + "be", + "be-BY", + "bg", + "bg-BG", + "bn", + "bn-BD", + "bn-IN", + "br", + "br-FR", + "bs", + "bs-Cyrl", + "bs-Cyrl-BA", + "bs-Latn", + "bs-Latn-BA", + "ca", + "ca-AD", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-BE", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "el", + "el-CY", + "el-GR", + "en", + "en-150", + "en-AG", + "en-AS", + "en-AU", + "en-BB", + "en-BE", + "en-BM", + "en-BS", + "en-BW", + "en-BZ", + "en-CA", + "en-CM", + "en-DM", + "en-FJ", + "en-FM", + "en-GB", + "en-GD", + "en-GG", + "en-GH", + "en-GI", + "en-GM", + "en-GU", + "en-GY", + "en-HK", + "en-IE", + "en-IM", + "en-IN", + "en-JE", + "en-JM", + "en-KE", + "en-KI", + "en-KN", + "en-KY", + "en-LC", + "en-LR", + "en-LS", + "en-MG", + "en-MH", + "en-MP", + "en-MT", + "en-MU", + "en-MW", + "en-NA", + "en-NG", + "en-NZ", + "en-PG", + "en-PH", + "en-PK", + "en-PR", + "en-PW", + "en-SB", + "en-SC", + "en-SG", + "en-SL", + "en-SS", + "en-SZ", + "en-TC", + "en-TO", + "en-TT", + "en-TZ", + "en-UG", + "en-UM", + "en-US", + "en-US-POSIX", + "en-VC", + "en-VG", + "en-VI", + "en-VU", + "en-WS", + "en-ZA", + "en-ZM", + "en-ZW", + "eo", + "es", + "es-419", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-CU", + "es-DO", + "es-EA", + "es-EC", + "es-ES", + "es-GQ", + "es-GT", + "es-HN", + "es-IC", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PH", + "es-PR", + "es-PY", + "es-SV", + "es-US", + "es-UY", + "es-VE", + "et", + "et-EE", + "eu", + "eu-ES", + "fa", + "fa-AF", + "fa-IR", + "ff", + "ff-SN", + "fi", + "fi-FI", + "fr", + "fr-BE", + "fr-BF", + "fr-BI", + "fr-BJ", + "fr-BL", + "fr-CA", + "fr-CD", + "fr-CF", + "fr-CG", + "fr-CH", + "fr-CI", + "fr-CM", + "fr-DJ", + "fr-DZ", + "fr-FR", + "fr-GA", + "fr-GF", + "fr-GN", + "fr-GP", + "fr-GQ", + "fr-HT", + "fr-KM", + "fr-LU", + "fr-MA", + "fr-MC", + "fr-MF", + "fr-MG", + "fr-ML", + "fr-MQ", + "fr-MR", + "fr-MU", + "fr-NC", + "fr-NE", + "fr-PF", + "fr-RE", + "fr-RW", + "fr-SC", + "fr-SN", + "fr-SY", + "fr-TD", + "fr-TG", + "fr-TN", + "fr-VU", + "fr-YT", + "ga", + "ga-IE", + "gl", + "gl-ES", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "it-SM", + "ja", + "ja-JP", + "kk", + "kk-Cyrl", + "kk-Cyrl-KZ", + "km", + "km-KH", + "kn", + "kn-IN", + "ko", + "ko-KP", + "ko-KR", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mk", + "mk-MK", + "ml", + "ml-IN", + "mr", + "mr-IN", + "nb", + "nb-NO", + "nl", + "nl-AW", + "nl-BE", + "nl-CW", + "nl-NL", + "nl-SR", + "nl-SX", + "nn", + "nn-NO", + "or", + "or-IN", + "pa", + "pa-Arab", + "pa-Arab-PK", + "pa-Guru", + "pa-Guru-IN", + "pl", + "pl-PL", + "pt", + "pt-AO", + "pt-BR", + "pt-CV", + "pt-GW", + "pt-MO", + "pt-MZ", + "pt-PT", + "pt-ST", + "pt-TL", + "rm", + "rm-CH", + "ro", + "ro-MD", + "ro-RO", + "ru", + "ru-BY", + "ru-KG", + "ru-KZ", + "ru-MD", + "ru-RU", + "ru-UA", + "si", + "si-LK", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sq-MK", + "sr", + "sr-Cyrl", + "sr-Cyrl-BA", + "sr-Cyrl-ME", + "sr-Cyrl-RS", + "sr-Latn", + "sr-Latn-BA", + "sr-Latn-ME", + "sr-Latn-RS", + "sv", + "sv-AX", + "sv-FI", + "sv-SE", + "te", + "te-IN", + "th", + "th-TH", + "tr", + "tr-CY", + "tr-TR", + "uk", + "uk-UA", + "vi", + "vi-VN", + "zh", + "zh-Hans", + "zh-Hans-CN", + "zh-Hans-HK", + "zh-Hans-MO", + "zh-Hans-SG", + "zh-Hant", + "zh-Hant-HK", + "zh-Hant-MO", + "zh-Hant-TW", +]; + +var count = Intl.DateTimeFormat.supportedLocalesOf(locales).length; + +reportCompare(locales.length, count, "Number of supported locales in Intl.DateTimeFormat"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone.js new file mode 100644 index 0000000000..eca6ed27ee --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone.js @@ -0,0 +1,139 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + + +const utcTimeZones = [ + // Etc/UTC and Etc/GMT are normalized to UTC. + "Etc/UTC", "Etc/GMT", + + // Links to Etc/GMT. (tzdata/etcetera) + "GMT", "Etc/Greenwich", "Etc/GMT-0", "Etc/GMT+0", "Etc/GMT0", + + // Links to Etc/UTC. (tzdata/etcetera) + "Etc/Universal", "Etc/Zulu", + + // Links to Etc/GMT. (tzdata/backward) + "GMT+0", "GMT-0", "GMT0", "Greenwich", + + // Links to Etc/UTC. (tzdata/backward) + "UTC", "Universal", "Zulu", "Etc/UCT", "UCT", +]; + +for (let timeZone of utcTimeZones) { + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)}); + assertEq(dtf.resolvedOptions().timeZone, "UTC"); + } +} + + +const invalidTimeZones = [ + "", "null", "undefined", "UTC\0", + + // ICU time zone name for invalid time zones. + "Etc/Unknown", + + // ICU custom time zones. + "GMT-1", "GMT+1", "GMT-10", "GMT+10", + "GMT-10:00", "GMT+10:00", + "GMT-1000", "GMT+1000", + + // Legacy ICU time zones. + "ACT", "AET", "AGT", "ART", "AST", "BET", "BST", "CAT", "CNT", "CST", + "CTT", "EAT", "ECT", "IET", "IST", "JST", "MIT", "NET", "NST", "PLT", + "PNT", "PRT", "PST", "SST", "VST", + + // Deprecated IANA time zones. + "SystemV/AST4ADT", "SystemV/EST5EDT", "SystemV/CST6CDT", "SystemV/MST7MDT", + "SystemV/PST8PDT", "SystemV/YST9YDT", "SystemV/AST4", "SystemV/EST5", + "SystemV/CST6", "SystemV/MST7", "SystemV/PST8", "SystemV/YST9", "SystemV/HST10", +]; + +for (let timeZone of invalidTimeZones) { + for (let map of tzMapper) { + assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)}); + }, RangeError); + } +} + + +// GMT[+-]hh is invalid, but Etc/GMT[+-]hh is a valid IANA time zone. +for (let gmtOffset = -14; gmtOffset <= 12; ++gmtOffset) { + // Skip Etc/GMT0. + if (gmtOffset === 0) + continue; + + let timeZone = `Etc/GMT${gmtOffset > 0 ? "+" : ""}${gmtOffset}`; + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)}); + assertEq(dtf.resolvedOptions().timeZone, timeZone); + } +} + + +const invalidEtcGMTNames = [ + // Out of bounds GMT offset. + "Etc/GMT-15", "Etc/GMT+13", + + // Etc/GMT[+-]hh:mm isn't a IANA time zone name. + "Etc/GMT-10:00", "Etc/GMT+10:00", + "Etc/GMT-1000", "Etc/GMT+1000", +]; + +for (let timeZone of invalidEtcGMTNames) { + for (let map of tzMapper) { + assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)}); + }, RangeError); + } +} + + +// RangeError is thrown for primitive values, because ToString(<primitive>) +// isn't a valid time zone name. +for (let nonStrings of [null, 0, 0.5, true, false]) { + assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat(undefined, {timeZone: nonStrings}); + }, RangeError); +} + +// ToString(<symbol>) throws TypeError. +assertThrowsInstanceOf(() => { + new Intl.DateTimeFormat(undefined, {timeZone: Symbol()}); +}, TypeError); + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let {timeZone: tzAbsent} = new Intl.DateTimeFormat(undefined, {}).resolvedOptions(); + let {timeZone: tzUndefined} = new Intl.DateTimeFormat(undefined, {timeZone: undefined}).resolvedOptions(); + + assertEq(typeof tzAbsent, "string"); + assertEq(typeof tzUndefined, "string"); + assertEq(tzUndefined, tzAbsent); + + // The default time zone isn't a link name. + let {timeZone: tzDefault} = new Intl.DateTimeFormat(undefined, {timeZone: tzAbsent}).resolvedOptions(); + assertEq(tzDefault, tzAbsent); +} + +// Objects are converted through ToString(). +{ + let timeZone = "Europe/Warsaw"; + let obj = { + toString() { + return timeZone; + } + }; + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: obj}); + assertEq(dtf.resolvedOptions().timeZone, timeZone); +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backward_links.js new file mode 100644 index 0000000000..234616bfd9 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backward_links.js @@ -0,0 +1,156 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + +// Link names derived from IANA Time Zone Database, backward file. +const links = { + "Africa/Asmera": "Africa/Asmara", + "America/Atka": "America/Adak", + "America/Buenos_Aires": "America/Argentina/Buenos_Aires", + "America/Catamarca": "America/Argentina/Catamarca", + "America/Cordoba": "America/Argentina/Cordoba", + "America/Fort_Wayne": "America/Indiana/Indianapolis", + "America/Godthab": "America/Nuuk", + "America/Indianapolis": "America/Indiana/Indianapolis", + "America/Jujuy": "America/Argentina/Jujuy", + "America/Knox_IN": "America/Indiana/Knox", + "America/Kralendijk": "America/Curacao", + "America/Louisville": "America/Kentucky/Louisville", + "America/Lower_Princes": "America/Curacao", + "America/Marigot": "America/Port_of_Spain", + "America/Mendoza": "America/Argentina/Mendoza", + "America/Porto_Acre": "America/Rio_Branco", + "America/Santa_Isabel": "America/Tijuana", + "America/Shiprock": "America/Denver", + "America/St_Barthelemy": "America/Port_of_Spain", + "America/Virgin": "America/St_Thomas", + "Antarctica/South_Pole": "Antarctica/McMurdo", + "Arctic/Longyearbyen": "Europe/Oslo", + "Asia/Ashkhabad": "Asia/Ashgabat", + "Asia/Calcutta": "Asia/Kolkata", + "Asia/Chungking": "Asia/Chongqing", + "Asia/Dacca": "Asia/Dhaka", + "Asia/Istanbul": "Europe/Istanbul", + "Asia/Katmandu": "Asia/Kathmandu", + "Asia/Macao": "Asia/Macau", + "Asia/Rangoon": "Asia/Yangon", + "Asia/Saigon": "Asia/Ho_Chi_Minh", + "Asia/Thimbu": "Asia/Thimphu", + "Asia/Ujung_Pandang": "Asia/Makassar", + "Asia/Ulan_Bator": "Asia/Ulaanbaatar", + "Atlantic/Faeroe": "Atlantic/Faroe", + "Australia/ACT": "Australia/Sydney", + "Australia/Canberra": "Australia/Sydney", + "Australia/LHI": "Australia/Lord_Howe", + "Australia/NSW": "Australia/Sydney", + "Australia/North": "Australia/Darwin", + "Australia/Queensland": "Australia/Brisbane", + "Australia/South": "Australia/Adelaide", + "Australia/Tasmania": "Australia/Hobart", + "Australia/Victoria": "Australia/Melbourne", + "Australia/West": "Australia/Perth", + "Australia/Yancowinna": "Australia/Broken_Hill", + "Brazil/Acre": "America/Rio_Branco", + "Brazil/DeNoronha": "America/Noronha", + "Brazil/East": "America/Sao_Paulo", + "Brazil/West": "America/Manaus", + "Canada/Atlantic": "America/Halifax", + "Canada/Central": "America/Winnipeg", + "Canada/Eastern": "America/Toronto", + "Canada/Mountain": "America/Edmonton", + "Canada/Newfoundland": "America/St_Johns", + "Canada/Pacific": "America/Vancouver", + "Canada/Saskatchewan": "America/Regina", + "Canada/Yukon": "America/Whitehorse", + "Chile/Continental": "America/Santiago", + "Chile/EasterIsland": "Pacific/Easter", + "Cuba": "America/Havana", + "Egypt": "Africa/Cairo", + "Eire": "Europe/Dublin", + "Etc/GMT+0": "Etc/GMT", + "Etc/GMT-0": "Etc/GMT", + "Etc/GMT0": "Etc/GMT", + "Etc/Greenwich": "Etc/GMT", + "Etc/UCT": "Etc/UTC", + "Etc/Universal": "Etc/UTC", + "Etc/Zulu": "Etc/UTC", + "Europe/Bratislava": "Europe/Prague", + "Europe/Busingen": "Europe/Zurich", + "Europe/Kiev": "Europe/Kyiv", + "Europe/Mariehamn": "Europe/Helsinki", + "Europe/Nicosia": "Asia/Nicosia", + "Europe/Podgorica": "Europe/Belgrade", + "Europe/San_Marino": "Europe/Rome", + "Europe/Vatican": "Europe/Rome", + "GB": "Europe/London", + "GB-Eire": "Europe/London", + "GMT+0": "Etc/GMT", + "GMT-0": "Etc/GMT", + "GMT0": "Etc/GMT", + "Greenwich": "Etc/GMT", + "Hongkong": "Asia/Hong_Kong", + "Iceland": "Atlantic/Reykjavik", + "Iran": "Asia/Tehran", + "Israel": "Asia/Jerusalem", + "Jamaica": "America/Jamaica", + "Japan": "Asia/Tokyo", + "Kwajalein": "Pacific/Kwajalein", + "Libya": "Africa/Tripoli", + "Mexico/BajaNorte": "America/Tijuana", + "Mexico/BajaSur": "America/Mazatlan", + "Mexico/General": "America/Mexico_City", + "NZ": "Pacific/Auckland", + "NZ-CHAT": "Pacific/Chatham", + "Navajo": "America/Denver", + "PRC": "Asia/Shanghai", + "Pacific/Ponape": "Pacific/Pohnpei", + "Pacific/Samoa": "Pacific/Pago_Pago", + "Pacific/Truk": "Pacific/Chuuk", + "Pacific/Yap": "Pacific/Chuuk", + "Poland": "Europe/Warsaw", + "Portugal": "Europe/Lisbon", + "ROC": "Asia/Taipei", + "ROK": "Asia/Seoul", + "Singapore": "Asia/Singapore", + "Turkey": "Europe/Istanbul", + "UCT": "Etc/UTC", + "US/Alaska": "America/Anchorage", + "US/Aleutian": "America/Adak", + "US/Arizona": "America/Phoenix", + "US/Central": "America/Chicago", + "US/East-Indiana": "America/Indiana/Indianapolis", + "US/Eastern": "America/New_York", + "US/Hawaii": "Pacific/Honolulu", + "US/Indiana-Starke": "America/Indiana/Knox", + "US/Michigan": "America/Detroit", + "US/Mountain": "America/Denver", + "US/Pacific": "America/Los_Angeles", + "US/Samoa": "Pacific/Pago_Pago", + "UTC": "Etc/UTC", + "Universal": "Etc/UTC", + "W-SU": "Europe/Moscow", + "Zulu": "Etc/UTC", +}; + +for (let [linkName, target] of Object.entries(links)) { + if (target === "Etc/UTC" || target === "Etc/GMT") + target = "UTC"; + + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)}); + let resolvedTimeZone = dtf.resolvedOptions().timeZone; + assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone.js new file mode 100644 index 0000000000..0c5882deca --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone.js @@ -0,0 +1,153 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + +// This file was generated with historical, pre-1970 backzone information +// respected. Therefore, every zone key listed below is its own Zone, not +// a Link to a modern-day target as IANA ignoring backzones would say. + +// Backzone zones derived from IANA Time Zone Database. +const links = { + "Africa/Accra": "Africa/Accra", + "Africa/Addis_Ababa": "Africa/Addis_Ababa", + "Africa/Asmara": "Africa/Asmara", + "Africa/Bamako": "Africa/Bamako", + "Africa/Bangui": "Africa/Bangui", + "Africa/Banjul": "Africa/Banjul", + "Africa/Blantyre": "Africa/Blantyre", + "Africa/Brazzaville": "Africa/Brazzaville", + "Africa/Bujumbura": "Africa/Bujumbura", + "Africa/Conakry": "Africa/Conakry", + "Africa/Dakar": "Africa/Dakar", + "Africa/Dar_es_Salaam": "Africa/Dar_es_Salaam", + "Africa/Djibouti": "Africa/Djibouti", + "Africa/Douala": "Africa/Douala", + "Africa/Freetown": "Africa/Freetown", + "Africa/Gaborone": "Africa/Gaborone", + "Africa/Harare": "Africa/Harare", + "Africa/Kampala": "Africa/Kampala", + "Africa/Kigali": "Africa/Kigali", + "Africa/Kinshasa": "Africa/Kinshasa", + "Africa/Libreville": "Africa/Libreville", + "Africa/Lome": "Africa/Lome", + "Africa/Luanda": "Africa/Luanda", + "Africa/Lubumbashi": "Africa/Lubumbashi", + "Africa/Lusaka": "Africa/Lusaka", + "Africa/Malabo": "Africa/Malabo", + "Africa/Maseru": "Africa/Maseru", + "Africa/Mbabane": "Africa/Mbabane", + "Africa/Mogadishu": "Africa/Mogadishu", + "Africa/Niamey": "Africa/Niamey", + "Africa/Nouakchott": "Africa/Nouakchott", + "Africa/Ouagadougou": "Africa/Ouagadougou", + "Africa/Porto-Novo": "Africa/Porto-Novo", + "Africa/Timbuktu": "Africa/Timbuktu", + "America/Anguilla": "America/Anguilla", + "America/Antigua": "America/Antigua", + "America/Argentina/ComodRivadavia": "America/Argentina/ComodRivadavia", + "America/Aruba": "America/Aruba", + "America/Atikokan": "America/Atikokan", + "America/Blanc-Sablon": "America/Blanc-Sablon", + "America/Cayman": "America/Cayman", + "America/Coral_Harbour": "America/Coral_Harbour", + "America/Creston": "America/Creston", + "America/Curacao": "America/Curacao", + "America/Dominica": "America/Dominica", + "America/Ensenada": "America/Ensenada", + "America/Grenada": "America/Grenada", + "America/Guadeloupe": "America/Guadeloupe", + "America/Montreal": "America/Montreal", + "America/Montserrat": "America/Montserrat", + "America/Nassau": "America/Nassau", + "America/Nipigon": "America/Nipigon", + "America/Pangnirtung": "America/Pangnirtung", + "America/Port_of_Spain": "America/Port_of_Spain", + "America/Rainy_River": "America/Rainy_River", + "America/Rosario": "America/Rosario", + "America/St_Kitts": "America/St_Kitts", + "America/St_Lucia": "America/St_Lucia", + "America/St_Thomas": "America/St_Thomas", + "America/St_Vincent": "America/St_Vincent", + "America/Thunder_Bay": "America/Thunder_Bay", + "America/Tortola": "America/Tortola", + "America/Yellowknife": "America/Yellowknife", + "Antarctica/DumontDUrville": "Antarctica/DumontDUrville", + "Antarctica/McMurdo": "Antarctica/McMurdo", + "Antarctica/Syowa": "Antarctica/Syowa", + "Antarctica/Vostok": "Antarctica/Vostok", + "Asia/Aden": "Asia/Aden", + "Asia/Bahrain": "Asia/Bahrain", + "Asia/Brunei": "Asia/Brunei", + "Asia/Chongqing": "Asia/Chongqing", + "Asia/Harbin": "Asia/Harbin", + "Asia/Kashgar": "Asia/Kashgar", + "Asia/Kuala_Lumpur": "Asia/Kuala_Lumpur", + "Asia/Kuwait": "Asia/Kuwait", + "Asia/Muscat": "Asia/Muscat", + "Asia/Phnom_Penh": "Asia/Phnom_Penh", + "Asia/Tel_Aviv": "Asia/Tel_Aviv", + "Asia/Vientiane": "Asia/Vientiane", + "Atlantic/Jan_Mayen": "Atlantic/Jan_Mayen", + "Atlantic/Reykjavik": "Atlantic/Reykjavik", + "Atlantic/St_Helena": "Atlantic/St_Helena", + "Australia/Currie": "Australia/Currie", + "Europe/Amsterdam": "Europe/Amsterdam", + "Europe/Belfast": "Europe/Belfast", + "Europe/Copenhagen": "Europe/Copenhagen", + "Europe/Guernsey": "Europe/Guernsey", + "Europe/Isle_of_Man": "Europe/Isle_of_Man", + "Europe/Jersey": "Europe/Jersey", + "Europe/Ljubljana": "Europe/Ljubljana", + "Europe/Luxembourg": "Europe/Luxembourg", + "Europe/Monaco": "Europe/Monaco", + "Europe/Oslo": "Europe/Oslo", + "Europe/Sarajevo": "Europe/Sarajevo", + "Europe/Skopje": "Europe/Skopje", + "Europe/Stockholm": "Europe/Stockholm", + "Europe/Tiraspol": "Europe/Tiraspol", + "Europe/Uzhgorod": "Europe/Uzhgorod", + "Europe/Vaduz": "Europe/Vaduz", + "Europe/Zagreb": "Europe/Zagreb", + "Europe/Zaporozhye": "Europe/Zaporozhye", + "Indian/Antananarivo": "Indian/Antananarivo", + "Indian/Christmas": "Indian/Christmas", + "Indian/Cocos": "Indian/Cocos", + "Indian/Comoro": "Indian/Comoro", + "Indian/Kerguelen": "Indian/Kerguelen", + "Indian/Mahe": "Indian/Mahe", + "Indian/Mayotte": "Indian/Mayotte", + "Indian/Reunion": "Indian/Reunion", + "Pacific/Chuuk": "Pacific/Chuuk", + "Pacific/Enderbury": "Pacific/Enderbury", + "Pacific/Funafuti": "Pacific/Funafuti", + "Pacific/Johnston": "Pacific/Johnston", + "Pacific/Majuro": "Pacific/Majuro", + "Pacific/Midway": "Pacific/Midway", + "Pacific/Pohnpei": "Pacific/Pohnpei", + "Pacific/Saipan": "Pacific/Saipan", + "Pacific/Wake": "Pacific/Wake", + "Pacific/Wallis": "Pacific/Wallis", +}; + +for (let [linkName, target] of Object.entries(links)) { + if (target === "Etc/UTC" || target === "Etc/GMT") + target = "UTC"; + + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)}); + let resolvedTimeZone = dtf.resolvedOptions().timeZone; + assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone_links.js new file mode 100644 index 0000000000..15806dd42c --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_backzone_links.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + +// This file was generated with historical, pre-1970 backzone information +// respected. Therefore, every zone key listed below points to a target +// in the backzone file and not to its modern-day target as IANA ignoring +// backzones would say. + +// Backzone links derived from IANA Time Zone Database. +const links = { + "Africa/Asmera": "Africa/Asmara", + "America/Kralendijk": "America/Curacao", + "America/Lower_Princes": "America/Curacao", + "America/Marigot": "America/Port_of_Spain", + "America/St_Barthelemy": "America/Port_of_Spain", + "America/Virgin": "America/St_Thomas", + "Antarctica/South_Pole": "Antarctica/McMurdo", + "Arctic/Longyearbyen": "Europe/Oslo", + "Asia/Chungking": "Asia/Chongqing", + "Iceland": "Atlantic/Reykjavik", + "Pacific/Ponape": "Pacific/Pohnpei", + "Pacific/Truk": "Pacific/Chuuk", + "Pacific/Yap": "Pacific/Chuuk", +}; + +for (let [linkName, target] of Object.entries(links)) { + if (target === "Etc/UTC" || target === "Etc/GMT") + target = "UTC"; + + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)}); + let resolvedTimeZone = dtf.resolvedOptions().timeZone; + assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_notbackward_links.js new file mode 100644 index 0000000000..7dbd456c2e --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_notbackward_links.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c + +const tzMapper = [ + x => x, + x => x.toUpperCase(), + x => x.toLowerCase(), +]; + +// Link names derived from IANA Time Zone Database, excluding backward file. +const links = { + "GMT": "Etc/GMT", +}; + +for (let [linkName, target] of Object.entries(links)) { + if (target === "Etc/UTC" || target === "Etc/GMT") + target = "UTC"; + + for (let map of tzMapper) { + let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)}); + let resolvedTimeZone = dtf.resolvedOptions().timeZone; + assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/DateTimeFormat/timeZone_version.js b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_version.js new file mode 100644 index 0000000000..15c7553dd8 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/timeZone_version.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c +const tzdata = "2023c"; + +if (typeof getICUOptions === "undefined") { + var getICUOptions = SpecialPowers.Cu.getJSTestingFunctions().getICUOptions; +} + +var options = getICUOptions(); + +assertEq(options.tzdata, tzdata); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/DateTimeFormat/toStringTag.js b/js/src/tests/non262/Intl/DateTimeFormat/toStringTag.js new file mode 100644 index 0000000000..136632f71f --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/toStringTag.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var desc = Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, Symbol.toStringTag); + +assertEq(desc !== undefined, true); +assertEq(desc.value, "Intl.DateTimeFormat"); +assertEq(desc.writable, false); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); + +assertEq(Object.prototype.toString.call(Intl.DateTimeFormat.prototype), "[object Intl.DateTimeFormat]"); +assertEq(Object.prototype.toString.call(new Intl.DateTimeFormat), "[object Intl.DateTimeFormat]"); + +Object.defineProperty(Intl.DateTimeFormat.prototype, Symbol.toStringTag, {value: "DateTimeFormat"}); + +assertEq(Object.prototype.toString.call(Intl.DateTimeFormat.prototype), "[object DateTimeFormat]"); +assertEq(Object.prototype.toString.call(new Intl.DateTimeFormat), "[object DateTimeFormat]"); + +delete Intl.DateTimeFormat.prototype[Symbol.toStringTag]; + +assertEq(Object.prototype.toString.call(Intl.DateTimeFormat.prototype), "[object Object]"); +assertEq(Object.prototype.toString.call(new Intl.DateTimeFormat), "[object Object]"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/tz-environment-variable.js b/js/src/tests/non262/Intl/DateTimeFormat/tz-environment-variable.js new file mode 100644 index 0000000000..6128abb9d1 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/tz-environment-variable.js @@ -0,0 +1,67 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||(xulRuntime.OS=="WINNT"&&!xulRuntime.shell)) -- Windows browser in automation doesn't pick up new time zones correctly + +// From bug 1330149: +// +// Windows only supports a very limited set of IANA time zone names for the TZ +// environment variable. +// +// TZ format supported by Windows: "TZ=tzn[+|-]hh[:mm[:ss]][dzn]". +// +// Complete list of all IANA time zone ids matching that format. +// +// From tzdata's "northamerica" file: +// EST5EDT +// CST6CDT +// MST7MDT +// PST8PDT +// +// From tzdata's "backward" file: +// GMT+0 +// GMT-0 +// GMT0 +// +// Also supported on Windows even though they don't match the format listed +// above. +// +// From tzdata's "backward" file: +// UCT +// UTC +// +// From tzdata's "etcetera" file: +// GMT + +function inTimeZone(tzname, fn) { + setTimeZone(tzname); + try { + fn(); + } finally { + setTimeZone("PST8PDT"); + } +} + +const timeZones = [ + { id: "EST5EDT" }, + { id: "CST6CDT" }, + { id: "MST7MDT" }, + { id: "PST8PDT" }, + // ICU on non-Windows platforms doesn't accept these three time zone + // identifiers, cf. isValidOlsonID in $ICU/source/common/putil.cpp. We + // could add support for them, but it seems unlikely they're used in + // practice, so we just skip over them. + // { id: "GMT+0", normalized: "UTC" }, + // { id: "GMT-0", normalized: "UTC" }, + // { id: "GMT0", normalized: "UTC" }, + { id: "UCT", normalized: "UTC" }, + { id: "UTC", normalized: "UTC" }, + { id: "GMT", normalized: "UTC" }, +]; + +for (let {id, normalized = id} of timeZones) { + inTimeZone(id, () => { + let opts = new Intl.DateTimeFormat().resolvedOptions(); + assertEq(opts.timeZone, normalized); + }); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/DateTimeFormat/unwrapping.js b/js/src/tests/non262/Intl/DateTimeFormat/unwrapping.js new file mode 100644 index 0000000000..d2fc991cb4 --- /dev/null +++ b/js/src/tests/non262/Intl/DateTimeFormat/unwrapping.js @@ -0,0 +1,248 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test UnwrapDateTimeFormat operation. + +const dateTimeFormatFunctions = []; +dateTimeFormatFunctions.push({ + function: Intl.DateTimeFormat.prototype.resolvedOptions, + unwrap: true, +}); +dateTimeFormatFunctions.push({ + function: Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get, + unwrap: true, +}); +dateTimeFormatFunctions.push({ + function: Intl.DateTimeFormat.prototype.formatToParts, + unwrap: false, +}); + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function IsObject(o) { + return Object(o) === o; +} + +function IsPrimitive(o) { + return Object(o) !== o; +} + +function intlObjects(ctor) { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; +} + +function thisValues(C) { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.filter(ctor => ctor !== C).map(intlObjects)), + + // Object inheriting from an Intl constructor prototype. + ...intlConstructors.map(ctor => Object.create(ctor.prototype)), + ]; +} + +const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.DateTimeFormat.call(Object.create(Intl.DateTimeFormat.prototype)))[0]; + +// Test Intl.DateTimeFormat.prototype methods. +for (let {function: dateTimeFormatFunction, unwrap} of dateTimeFormatFunctions) { + // Test a TypeError is thrown when the this-value isn't an initialized + // Intl.DateTimeFormat instance. + for (let thisValue of thisValues(Intl.DateTimeFormat)) { + assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError); + } + + // And test no error is thrown for initialized Intl.DateTimeFormat instances. + for (let thisValue of intlObjects(Intl.DateTimeFormat)) { + dateTimeFormatFunction.call(thisValue); + } + + // Manually add [[FallbackSymbol]] to objects and then repeat the tests from above. + for (let thisValue of thisValues(Intl.DateTimeFormat)) { + assertThrowsInstanceOf(() => dateTimeFormatFunction.call({ + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: thisValue, + }), TypeError); + } + + for (let thisValue of intlObjects(Intl.DateTimeFormat)) { + let obj = { + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: thisValue, + }; + if (unwrap) { + dateTimeFormatFunction.call(obj); + } else { + assertThrowsInstanceOf(() => dateTimeFormatFunction.call(obj), TypeError); + } + } + + // Ensure [[FallbackSymbol]] isn't retrieved for Intl.DateTimeFormat instances. + for (let thisValue of intlObjects(Intl.DateTimeFormat)) { + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { assertEq(false, true); } + }); + dateTimeFormatFunction.call(thisValue); + } + + // Ensure [[FallbackSymbol]] is only retrieved for objects inheriting from Intl.DateTimeFormat.prototype. + for (let thisValue of thisValues(Intl.DateTimeFormat).filter(IsObject)) { + if (Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue)) + continue; + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { assertEq(false, true); } + }); + assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError); + } + + // Repeat the test from above, but also change Intl.DateTimeFormat[@@hasInstance] + // so it always returns |true|. + for (let thisValue of thisValues(Intl.DateTimeFormat).filter(IsObject)) { + let isPrototypeOf = Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false, symbolGetterCalled = false; + Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return true; + }, configurable: true + }); + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { + assertEq(symbolGetterCalled, false); + symbolGetterCalled = true; + return null; + }, configurable: true + }); + + assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError); + + delete Intl.DateTimeFormat[Symbol.hasInstance]; + + assertEq(hasInstanceCalled, false); + assertEq(symbolGetterCalled, unwrap && isPrototypeOf); + } + + // Test with primitive values. + for (let thisValue of thisValues(Intl.DateTimeFormat).filter(IsPrimitive)) { + // Ensure @@hasInstance is not called. + Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, { + value() { assertEq(true, false); }, configurable: true + }); + let isUndefinedOrNull = thisValue === undefined || thisValue === null; + let symbolHolder; + if (!isUndefinedOrNull) { + // Ensure the fallback symbol isn't retrieved from the primitive wrapper prototype. + symbolHolder = Object.getPrototypeOf(thisValue); + Object.defineProperty(symbolHolder, intlFallbackSymbol, { + get() { assertEq(true, false); }, configurable: true + }); + } + + assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError); + + delete Intl.DateTimeFormat[Symbol.hasInstance]; + if (!isUndefinedOrNull) + delete symbolHolder[intlFallbackSymbol]; + } +} + +// Test format() returns the correct result for objects initialized as Intl.DateTimeFormat instances. +{ + // An actual Intl.DateTimeFormat instance. + let dateTimeFormat = new Intl.DateTimeFormat(); + + // An object initialized as a DateTimeFormat instance. + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + Intl.DateTimeFormat.call(thisValue); + + // Object with [[FallbackSymbol]] set to DateTimeFormat instance. + let fakeObj = { + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: dateTimeFormat, + }; + + for (let number of [0, Date.now(), -Date.now()]) { + let expected = dateTimeFormat.format(number); + assertEq(thisValue.format(number), expected); + assertEq(thisValue[intlFallbackSymbol].format(number), expected); + assertEq(fakeObj.format(number), expected); + } +} + +// Ensure formatToParts() doesn't use the fallback semantics. +{ + let formatToParts = Intl.DateTimeFormat.prototype.formatToParts; + + // An object initialized as a DateTimeFormat instance. + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + Intl.DateTimeFormat.call(thisValue); + assertThrowsInstanceOf(() => formatToParts.call(thisValue), TypeError); + + // Object with [[FallbackSymbol]] set to DateTimeFormat instance. + let fakeObj = { + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: new Intl.DateTimeFormat(), + }; + assertThrowsInstanceOf(() => formatToParts.call(fakeObj), TypeError); +} + +// Test resolvedOptions() returns the same results. +{ + // An actual Intl.DateTimeFormat instance. + let dateTimeFormat = new Intl.DateTimeFormat(); + + // An object initialized as a DateTimeFormat instance. + let thisValue = Object.create(Intl.DateTimeFormat.prototype); + Intl.DateTimeFormat.call(thisValue); + + // Object with [[FallbackSymbol]] set to DateTimeFormat instance. + let fakeObj = { + __proto__: Intl.DateTimeFormat.prototype, + [intlFallbackSymbol]: dateTimeFormat, + }; + + function assertEqOptions(actual, expected) { + actual = Object.entries(actual); + expected = Object.entries(expected); + + assertEq(actual.length, expected.length, "options count mismatch"); + for (var i = 0; i < expected.length; i++) { + assertEq(actual[i][0], expected[i][0], "key mismatch at " + i); + assertEq(actual[i][1], expected[i][1], "value mismatch at " + i); + } + } + + let expected = dateTimeFormat.resolvedOptions(); + assertEqOptions(thisValue.resolvedOptions(), expected); + assertEqOptions(thisValue[intlFallbackSymbol].resolvedOptions(), expected); + assertEqOptions(fakeObj.resolvedOptions(), expected); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/abbreviated.js b/js/src/tests/non262/Intl/DisplayNames/abbreviated.js new file mode 100644 index 0000000000..4203ffcbac --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/abbreviated.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +const tests = { + "en": { + long: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], + abbreviated: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], + short: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], + narrow: ["M", "T", "W", "T", "F", "S", "S"], + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, weekdays] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "weekday", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.style, style); + + for (let [day, expected] of weekdays.entries()) { + assertEq(dn.of(day + 1), expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/alias-and-parent-locales.js b/js/src/tests/non262/Intl/DisplayNames/alias-and-parent-locales.js new file mode 100644 index 0000000000..d60a1593d4 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/alias-and-parent-locales.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Ensure alias and parent locales are correctly picked up when calling into ICU. + +// "zh-HK" is an alias to "zh-Hant-HK", so display names should default to +// traditional instead of simplified characters. +{ + const zh_Hant = new Intl.DisplayNames("zh-Hant", {type: "region"}); + const zh_Hans = new Intl.DisplayNames("zh-Hans", {type: "region"}); + const zh_HK = new Intl.DisplayNames("zh-HK", {type: "region"}); + + // We assume traditional and simplified have different outputs. + assertEq(zh_Hant.of("US") === zh_Hans.of("US"), false); + + // "zh-HK" should use traditional characters. + assertEq(zh_HK.of("US"), zh_Hant.of("US")); +} + +// The parent locale of "en-AU" is "en-001" and not "en" (because "en" actually means "en-US"). +{ + const en = new Intl.DisplayNames("en", {type: "language"}); + const en_001 = new Intl.DisplayNames("en-001", {type: "language"}); + const en_AU = new Intl.DisplayNames("en-AU", {type: "language"}); + + // We assume "en" and "en-001" have different outputs. + assertEq(en.of("nds-NL") === en_001.of("nds-NL"), false); + + // "en-AU" should have the same output as "en-001". + assertEq(en_AU.of("nds-NL"), en_001.of("nds-NL")); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/browser.js b/js/src/tests/non262/Intl/DisplayNames/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/browser.js diff --git a/js/src/tests/non262/Intl/DisplayNames/calendar.js b/js/src/tests/non262/Intl/DisplayNames/calendar.js new file mode 100644 index 0000000000..0700ee2888 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/calendar.js @@ -0,0 +1,123 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "gregory": "Gregorian Calendar", + "iso8601": "ISO-8601 Calendar", + "japanese": "Japanese Calendar", + "islamic-civil": "Islamic Calendar (tabular, civil epoch)", + "islamicc": "Islamic Calendar (tabular, civil epoch)", + "ethioaa": "Ethiopic Amete Alem Calendar", + "ethiopic-amete-alem": "Ethiopic Amete Alem Calendar", + }, + short: {}, + narrow: {}, + }, + "de": { + long: { + "gregory": "Gregorianischer Kalender", + "iso8601": "ISO-8601-Kalender", + "japanese": "Japanischer Kalender", + "islamic-civil": "Bürgerlicher islamischer Kalender (tabellarisch)", + "islamicc": "Bürgerlicher islamischer Kalender (tabellarisch)", + "ethioaa": "Äthiopischer Amätä-Aläm-Kalender", + "ethiopic-amete-alem": "Äthiopischer Amätä-Aläm-Kalender", + }, + short: {}, + narrow: {}, + }, + "fr": { + long: { + "gregory": "calendrier grégorien", + "iso8601": "calendrier ISO 8601", + "japanese": "calendrier japonais", + "islamic-civil": "calendrier musulman (tabulaire, époque civile)", + "islamicc": "calendrier musulman (tabulaire, époque civile)", + "ethioaa": "calendrier éthiopien Amete Alem", + "ethiopic-amete-alem": "calendrier éthiopien Amete Alem", + }, + short: {}, + narrow: {}, + }, + "zh": { + long: { + "gregory": "公历", + "iso8601": "国际标准历法", + "japanese": "和历", + "islamic-civil": "伊斯兰希吉来日历", + "islamicc": "伊斯兰希吉来日历", + "ethioaa": "埃塞俄比亚阿米特阿莱姆日历", + "ethiopic-amete-alem": "埃塞俄比亚阿米特阿莱姆日历", + }, + short: {}, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "calendar", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "calendar"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [calendar, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(calendar), expected); + + // Also works with objects. + assertEq(dn.of(Object(calendar)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "calendar"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(undefined), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + + // Throws an error if |code| isn't a well-formed calendar type. + assertThrowsInstanceOf(() => dn.of("gregorian"), RangeError); + assertThrowsInstanceOf(() => dn.of("grëgory"), RangeError); + assertThrowsInstanceOf(() => dn.of("grēgory"), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "calendar"}); + let dn2 = new Intl.DisplayNames("en", {type: "calendar", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "calendar", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + // "invalid" isn't a known calendar type. + assertEq(dn1.of("invalid"), "invalid"); + assertEq(dn2.of("invalid"), "invalid"); + assertEq(dn3.of("invalid"), undefined); + + // The returned fallback is in canonical case. + assertEq(dn1.of("INVALID"), "invalid"); + assertEq(dn2.of("INVALID"), "invalid"); + assertEq(dn3.of("INVALID"), undefined); +} + +// Test when case isn't canonical. +{ + let dn = new Intl.DisplayNames("en", {type: "calendar", fallback: "none"}); + + assertEq(dn.of("gregory"), "Gregorian Calendar"); + assertEq(dn.of("GREGORY"), "Gregorian Calendar"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/currency.js b/js/src/tests/non262/Intl/DisplayNames/currency.js new file mode 100644 index 0000000000..1571249106 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/currency.js @@ -0,0 +1,150 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "USD": "US Dollar", + "EUR": "Euro", + "FRF": "French Franc", + "CNY": "Chinese Yuan", + "XAU": "Gold", + }, + short: { + "USD": "$", + "EUR": "€", + "FRF": "FRF", + "CNY": "CN¥", + "XAU": "XAU", + }, + narrow: { + "USD": "USD", + "EUR": "EUR", + "CNY": "CNY", + }, + }, + "de": { + long: { + "USD": "US-Dollar", + "EUR": "Euro", + "FRF": "Französischer Franc", + "CNY": "Renminbi Yuan", + "XAU": "Unze Gold", + }, + short: { + "USD": "$", + "EUR": "€", + "FRF": "FRF", + "CNY": "CN¥", + "XAU": "XAU", + }, + narrow: { + "CNY": "¥", + }, + }, + "fr": { + long: { + "USD": "dollar des États-Unis", + "EUR": "euro", + "FRF": "franc français", + "CNY": "yuan renminbi chinois", + "XAU": "or", + }, + short: { + "USD": "$US", + "EUR": "€", + "FRF": "F", + "CNY": "CNY", + "XAU": "XAU", + }, + narrow: { + "USD": "$", + "CNY": "¥", + }, + }, + "zh": { + long: { + "USD": "美元", + "EUR": "欧元", + "FRF": "法国法郎", + "CNY": "人民币", + "XAU": "黄金", + }, + short: { + "USD": "US$", + "EUR": "€", + "FRF": "FRF", + "CNY": "¥", + "XAU": "XAU", + }, + narrow: { + "USD": "$", + }, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "currency", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "currency"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [currency, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(currency), expected); + + // Also works with objects. + assertEq(dn.of(Object(currency)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "currency"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + + // Throws an error if |code| isn't a well-formed currency code. + assertThrowsInstanceOf(() => dn.of("us"), RangeError); + assertThrowsInstanceOf(() => dn.of("euro"), RangeError); + assertThrowsInstanceOf(() => dn.of("€uro"), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "currency"}); + let dn2 = new Intl.DisplayNames("en", {type: "currency", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "currency", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + // "AAA" is not a registered currency code. + assertEq(dn1.of("AAA"), "AAA"); + assertEq(dn2.of("AAA"), "AAA"); + assertEq(dn3.of("AAA"), undefined); + + // The returned fallback is in canonical case. + assertEq(dn1.of("aaa"), "AAA"); + assertEq(dn2.of("aaa"), "AAA"); + assertEq(dn3.of("aaa"), undefined); +} + +// Test when case isn't canonical. +{ + let dn = new Intl.DisplayNames("en", {type: "currency", fallback: "none"}); + + assertEq(dn.of("USD"), "US Dollar"); + assertEq(dn.of("usd"), "US Dollar"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/dateTimeField.js b/js/src/tests/non262/Intl/DisplayNames/dateTimeField.js new file mode 100644 index 0000000000..94bdf53899 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/dateTimeField.js @@ -0,0 +1,176 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "era": "era", + "year": "year", + "quarter": "quarter", + "month": "month", + "weekOfYear": "week", + "weekday": "day of the week", + "day": "day", + "dayPeriod": "AM/PM", + "hour": "hour", + "minute": "minute", + "second": "second", + "timeZoneName": "time zone", + }, + short: { + "year": "yr.", + "quarter": "qtr.", + "month": "mo.", + "weekOfYear": "wk.", + "weekday": "day of wk.", + "dayPeriod": "AM/PM", + "hour": "hr.", + "minute": "min.", + "second": "sec.", + "timeZoneName": "zone", + }, + narrow: { + "year": "yr", + "quarter": "qtr", + "month": "mo", + "weekOfYear": "wk", + "hour": "hr", + "minute": "min", + "second": "sec", + }, + }, + "de": { + long: { + "era": "Epoche", + "year": "Jahr", + "quarter": "Quartal", + "month": "Monat", + "weekOfYear": "Woche", + "weekday": "Wochentag", + "day": "Tag", + "dayPeriod": "Tageshälfte", + "hour": "Stunde", + "minute": "Minute", + "second": "Sekunde", + "timeZoneName": "Zeitzone", + }, + short: { + "era": "Epoche", + "year": "Jahr", + "quarter": "Quart.", + "month": "Monat", + "weekOfYear": "Woche", + "weekday": "Wochentag", + "day": "Tag", + "dayPeriod": "Tageshälfte", + "hour": "Std.", + "minute": "Min.", + "second": "Sek.", + "timeZoneName": "Zeitzone", + }, + narrow: { + "era": "E", + "year": "J", + "quarter": "Q", + "month": "M", + "weekOfYear": "W", + "weekday": "Wochent.", + "dayPeriod": "Tagesh.", + "timeZoneName": "Zeitz.", + }, + }, + "fr": { + long: { + "era": "ère", + "year": "année", + "quarter": "trimestre", + "month": "mois", + "weekOfYear": "semaine", + "weekday": "jour de la semaine", + "day": "jour", + "dayPeriod": "cadran", + "hour": "heure", + "minute": "minute", + "second": "seconde", + "timeZoneName": "fuseau horaire", + }, + short: { + "year": "an", + "quarter": "trim.", + "month": "m.", + "weekOfYear": "sem.", + "weekday": "j (sem.)", + "day": "j", + "hour": "h", + "minute": "min", + "second": "s", + }, + narrow: { + "year": "a", + }, + }, + "zh": { + long: { + "era": "纪元", + "year": "年", + "quarter": "季度", + "month": "月", + "weekOfYear": "周", + "weekday": "工作日", + "day": "日", + "dayPeriod": "上午/下午", + "hour": "小时", + "minute": "分钟", + "second": "秒", + "timeZoneName": "时区", + }, + short: { + "quarter": "季", + "minute": "分", + }, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "dateTimeField", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "dateTimeField"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [field, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(field), expected); + + // Also works with objects. + assertEq(dn.of(Object(field)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "dateTimeField"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + assertThrowsInstanceOf(() => dn.of(1), RangeError); + + // Throws an error if not one of ["era", "year", "quarter", "month", "weekOfYear", "weekday", + // "day", "dayPeriod", "hour", "minute", "second", "timeZoneName"]. + assertThrowsInstanceOf(() => dn.of(""), RangeError); + assertThrowsInstanceOf(() => dn.of("ERA"), RangeError); + assertThrowsInstanceOf(() => dn.of("Era"), RangeError); + assertThrowsInstanceOf(() => dn.of("era\0"), RangeError); + assertThrowsInstanceOf(() => dn.of("dayperiod"), RangeError); + assertThrowsInstanceOf(() => dn.of("day-period"), RangeError); + assertThrowsInstanceOf(() => dn.of("timezoneName"), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/dayPeriod.js b/js/src/tests/non262/Intl/DisplayNames/dayPeriod.js new file mode 100644 index 0000000000..cfeafdf5ee --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/dayPeriod.js @@ -0,0 +1,80 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +const tests = { + "en": { + long: { + "am": "AM", + "pm": "PM", + }, + short: {}, + narrow: {}, + }, + "de": { + long: { + "am": "AM", + "pm": "PM", + }, + short: {}, + narrow: {}, + }, + "fr": { + long: { + "am": "AM", + "pm": "PM", + }, + short: {}, + narrow: {}, + }, + "zh": { + long: { + "am": "上午", + "pm": "下午", + }, + short: {}, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + let defaultCalendar = new Intl.DateTimeFormat(locale).resolvedOptions().calendar; + + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "dayPeriod", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.calendar, defaultCalendar); + assertEq(resolved.style, style); + assertEq(resolved.type, "dayPeriod"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [dayPeriod, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(dayPeriod), expected); + + // Also works with objects. + assertEq(dn.of(Object(dayPeriod)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "dayPeriod"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + assertThrowsInstanceOf(() => dn.of(1), RangeError); + + // Throws an error if not one of ["am", "pm"]. + assertThrowsInstanceOf(() => dn.of(""), RangeError); + assertThrowsInstanceOf(() => dn.of("AM"), RangeError); + assertThrowsInstanceOf(() => dn.of("PM"), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/language-dialect.js b/js/src/tests/non262/Intl/DisplayNames/language-dialect.js new file mode 100644 index 0000000000..c2999bd673 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/language-dialect.js @@ -0,0 +1,104 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "de": "German", + "de-AT": "Austrian German", + "de-1996": "German (German orthography of 1996)", + "en": "English", + "en-Hant-GB": "British English (Traditional)", + "en-Hans-US": "American English (Simplified)", + "fr": "French", + "nl-BE": "Flemish", + "cr-Cans": "Cree (Unified Canadian Aboriginal Syllabics)", + }, + short: { + "en-Hant-GB": "UK English (Traditional)", + "en-Hans-US": "US English (Simplified)", + "cr-Cans": "Cree (UCAS)", + }, + narrow: {}, + }, + "de": { + long: { + "de": "Deutsch", + "de-AT": "Österreichisches Deutsch", + "de-1996": "Deutsch (Neue deutsche Rechtschreibung)", + "en": "Englisch", + "en-Hant-GB": "Englisch (Traditionell, Vereinigtes Königreich)", + "en-Hans-US": "Englisch (Vereinfacht, Vereinigte Staaten)", + "fr": "Französisch", + "nl-BE": "Flämisch", + }, + short: { + "en-Hant-GB": "Englisch (GB) (Traditionell)", + "en-Hans-US": "Englisch (USA) (Vereinfacht)", + }, + narrow: {}, + }, + "fr": { + long: { + "de": "allemand", + "de-AT": "allemand autrichien", + "de-1996": "allemand (orthographe allemande de 1996)", + "en": "anglais", + "en-Hant-GB": "anglais britannique (traditionnel)", + "en-Hans-US": "anglais américain (simplifié)", + "fr": "français", + "nl-BE": "flamand", + }, + short: { + "en-Hant-GB": "anglais britannique (traditionnel)", + "en-Hans-US": "anglais américain (simplifié)", + }, + narrow: {}, + }, + "zh": { + long: { + "zh": "中文", + "zh-Hant": "繁体中文", + "zh-Hant-CN": "繁体中文(中国)", + "zh-Hans-HK": "简体中文(中国香港特别行政区)", + }, + short: { + "zh-Hans-HK": "简体中文(香港)" + }, + narrow: {}, + }, + "ar": { + long: { + "ar": "العربية", + "ar-SA": "العربية (المملكة العربية السعودية)", + "zh-MO": "الصينية (منطقة ماكاو الإدارية الخاصة)", + }, + short: { + "zh-MO": "الصينية (مكاو)", + }, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "language", languageDisplay: "dialect", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "language"); + assertEq(resolved.languageDisplay, "dialect"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [language, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(language), expected); + + // Also works with objects. + assertEq(dn.of(Object(language)), expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/language.js b/js/src/tests/non262/Intl/DisplayNames/language.js new file mode 100644 index 0000000000..48fb72056e --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/language.js @@ -0,0 +1,205 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "de": "German", + "de-AT": "German (Austria)", + "de-1996": "German (German orthography of 1996)", + "en": "English", + "en-Hant-GB": "English (Traditional, United Kingdom)", + "en-Hans-US": "English (Simplified, United States)", + "fr": "French", + "nl-BE": "Dutch (Belgium)", + "cr-Cans": "Cree (Unified Canadian Aboriginal Syllabics)", + }, + short: { + "en-Hant-GB": "English (Traditional, UK)", + "en-Hans-US": "English (Simplified, US)", + "cr-Cans": "Cree (UCAS)", + }, + narrow: {}, + }, + "de": { + long: { + "de": "Deutsch", + "de-AT": "Deutsch (Österreich)", + "de-1996": "Deutsch (Neue deutsche Rechtschreibung)", + "en": "Englisch", + "en-Hant-GB": "Englisch (Traditionell, Vereinigtes Königreich)", + "en-Hans-US": "Englisch (Vereinfacht, Vereinigte Staaten)", + "fr": "Französisch", + "nl-BE": "Niederländisch (Belgien)", + }, + short: { + "en-Hant-GB": "Englisch (Traditionell, UK)", + "en-Hans-US": "Englisch (Vereinfacht, USA)", + }, + narrow: {}, + }, + "fr": { + long: { + "de": "allemand", + "de-AT": "allemand (Autriche)", + "de-1996": "allemand (orthographe allemande de 1996)", + "en": "anglais", + "en-Hant-GB": "anglais (traditionnel, Royaume-Uni)", + "en-Hans-US": "anglais (simplifié, États-Unis)", + "fr": "français", + "nl-BE": "néerlandais (Belgique)", + }, + short: { + "en-Hant-GB": "anglais (traditionnel, R.-U.)", + "en-Hans-US": "anglais (simplifié, É.-U.)", + }, + narrow: {}, + }, + "zh": { + long: { + "zh": "中文", + "zh-Hant": "中文(繁体)", + "zh-Hant-CN": "中文(繁体,中国)", + "zh-Hans-HK": "中文(简体,中国香港特别行政区)", + }, + short: { + "zh-Hans-HK": "中文(简体,香港)" + }, + narrow: {}, + }, + "ar": { + long: { + "ar": "العربية", + "ar-SA": "العربية (المملكة العربية السعودية)", + "zh-MO": "الصينية (منطقة ماكاو الإدارية الخاصة)", + }, + short: { + "zh-MO": "الصينية (مكاو)", + }, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "language", languageDisplay: "standard", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "language"); + assertEq(resolved.languageDisplay, "standard"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [language, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(language), expected); + + // Also works with objects. + assertEq(dn.of(Object(language)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "language"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + + // Throws an error if |code| can't be parsed as a `unicode_language_id` production. + assertThrowsInstanceOf(() => dn.of("en-"), RangeError); + assertThrowsInstanceOf(() => dn.of("en-u-ca-gregory"), RangeError); + assertThrowsInstanceOf(() => dn.of("en-x-private"), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "language"}); + let dn2 = new Intl.DisplayNames("en", {type: "language", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "language", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + // "aaa" is not a registered language code. + assertEq(dn1.of("aaa"), "aaa"); + assertEq(dn2.of("aaa"), "aaa"); + assertEq(dn3.of("aaa"), undefined); + + // "aaa" is not a registered language code. + assertEq(dn1.of("aaa-Latn"), "aaa-Latn"); + assertEq(dn2.of("aaa-Latn"), "aaa-Latn"); + assertEq(dn3.of("aaa-Latn"), undefined); + + // "Aaaa" is not a registered script code. + assertEq(dn1.of("en-Aaaa"), "en-Aaaa"); + assertEq(dn2.of("en-Aaaa"), "en-Aaaa"); + assertEq(dn3.of("en-Aaaa"), undefined); + + // "AA" is not a registered region code. + assertEq(dn1.of("en-AA"), "en-AA"); + assertEq(dn2.of("en-AA"), "en-AA"); + assertEq(dn3.of("en-AA"), undefined); + + // "XZ" doesn't have any localised names. + assertEq(dn1.of("en-XZ"), "en-XZ"); + assertEq(dn2.of("en-XZ"), "en-XZ"); + assertEq(dn3.of("en-XZ"), undefined); + + // "998" is canonicalised to "XZ". + assertEq(dn1.of("en-998"), "en-XZ"); + assertEq(dn2.of("en-998"), "en-XZ"); + assertEq(dn3.of("en-998"), undefined); + + // The returned fallback is in canonical case. + assertEq(dn1.of("AAA"), "aaa"); + assertEq(dn2.of("AAA"), "aaa"); + assertEq(dn3.of("AAA"), undefined); + + assertEq(dn1.of("En-aaaa"), "en-Aaaa"); + assertEq(dn2.of("En-aaaa"), "en-Aaaa"); + assertEq(dn3.of("En-aaaa"), undefined); + + assertEq(dn1.of("EN-aa"), "en-AA"); + assertEq(dn2.of("EN-aa"), "en-AA"); + assertEq(dn3.of("EN-aa"), undefined); +} + +// Ensure language tag canonicalisation is performed. +{ + let dn = new Intl.DisplayNames("en", {type: "language", fallback: "none"}); + + assertEq(dn.of("ru-RU"), "Russian (Russia)"); + + // ICU's canonicalisation supports "SU" -> "RU". + assertEq(Intl.getCanonicalLocales("ru-SU")[0], "ru-RU"); + assertEq(dn.of("ru-SU"), "Russian (Russia)"); + + // ICU's canonicalisation doesn't support "172" -> "RU". + assertEq(Intl.getCanonicalLocales("ru-172")[0], "ru-RU"); + assertEq(dn.of("ru-172"), "Russian (Russia)"); +} + +// Test when case isn't canonical. +{ + let dn = new Intl.DisplayNames("en", {type: "language", fallback: "none"}); + + assertEq(dn.of("IT-LATN-IT"), "Italian (Latin, Italy)"); + assertEq(dn.of("it-latn-it"), "Italian (Latin, Italy)"); +} + +// resolvedOptions() only outputs "languageDisplay" when the type is "language". +{ + let dn1 = new Intl.DisplayNames("en", {type: "language"}); + let dn2 = new Intl.DisplayNames("en", {type: "script"}); + + assertEq(dn1.resolvedOptions().languageDisplay, "dialect"); + assertEq(dn2.resolvedOptions().hasOwnProperty("languageDisplay"), false); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/month-calendar.js b/js/src/tests/non262/Intl/DisplayNames/month-calendar.js new file mode 100644 index 0000000000..9a7d9abdaa --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/month-calendar.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +let dn1 = new Intl.DisplayNames("en", {type: "month", calendar: "gregory"}); +assertEq(dn1.of(1), "January"); +assertEq(dn1.resolvedOptions().calendar, "gregory"); + +let dn2 = new Intl.DisplayNames("en", {type: "month", calendar: "hebrew"}); +assertEq(dn2.of(1), "Tishri"); +assertEq(dn2.resolvedOptions().calendar, "hebrew"); + +let dn3 = new Intl.DisplayNames("en", {type: "month", calendar: "islamicc"}); +assertEq(dn3.of(1), "Muharram"); +assertEq(dn3.resolvedOptions().calendar, "islamic-civil"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/month.js b/js/src/tests/non262/Intl/DisplayNames/month.js new file mode 100644 index 0000000000..0bd7a02a80 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/month.js @@ -0,0 +1,104 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +const tests = { + "en": { + long: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", 13], + short: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 13], + narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D", 13], + }, + "de": { + long: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember", 13], + short: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez", 13], + narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D", 13], + }, + "fr": { + long: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre", 13], + short: ["janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc.", 13], + narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D", 13], + }, + "zh": { + long: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月", 13], + short: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月", 13], + narrow: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", 13], + }, + "zh-u-ca-chinese": { + long: ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "腊月", 13], + short: ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "腊月", 13], + narrow: ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊", 13], + }, + "en-u-ca-hebrew": { + long: ["Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "Adar I", "Adar", "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul"], + short: ["Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "Adar I", "Adar", "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul"], + narrow: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"], + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + let defaultCalendar = new Intl.DateTimeFormat(locale).resolvedOptions().calendar; + + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "month", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.calendar, defaultCalendar); + assertEq(resolved.style, style); + assertEq(resolved.type, "month"); + assertEq(resolved.fallback, "code"); + + for (let i = 0; i < 13; i++) { + assertEq(dn.of(i + 1), String(styleTests[i])); + + // Also works with strings. + assertEq(dn.of(String(i + 1)), String(styleTests[i])); + + // Also works with objects. + assertEq(dn.of(Object(i + 1)), String(styleTests[i])); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "month"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + + // Throws an error if |code| isn't an integer. + assertThrowsInstanceOf(() => dn.of(1.5), RangeError); + assertThrowsInstanceOf(() => dn.of(-Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(NaN), RangeError); + + // Throws an error if outside of [1, 13]. + assertThrowsInstanceOf(() => dn.of(-1), RangeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + assertThrowsInstanceOf(() => dn.of(14), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "month"}); + let dn2 = new Intl.DisplayNames("en", {type: "month", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "month", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + assertEq(dn1.resolvedOptions().calendar, "gregory"); + assertEq(dn2.resolvedOptions().calendar, "gregory"); + assertEq(dn3.resolvedOptions().calendar, "gregory"); + + // The Gregorian calendar doesn't have a thirteenth month. + assertEq(dn1.of("13"), "13"); + assertEq(dn2.of("13"), "13"); + assertEq(dn3.of("13"), undefined); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/quarter.js b/js/src/tests/non262/Intl/DisplayNames/quarter.js new file mode 100644 index 0000000000..f74b3b439a --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/quarter.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +const tests = { + "en": { + long: ["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"], + short: ["Q1", "Q2", "Q3", "Q4"], + narrow: ["1", "2", "3", "4"], + }, + "de": { + long: ["1. Quartal", "2. Quartal", "3. Quartal", "4. Quartal"], + short: ["Q1", "Q2", "Q3", "Q4"], + narrow: ["1", "2", "3", "4"], + }, + "fr": { + long: ["1er trimestre", "2e trimestre", "3e trimestre", "4e trimestre"], + short: ["T1", "T2", "T3", "T4"], + narrow: ["1", "2", "3", "4"], + }, + "zh": { + long: ["第一季度", "第二季度", "第三季度", "第四季度"], + short: ["1季度", "2季度", "3季度", "4季度"], + narrow: ["1", "2", "3", "4"], + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + let defaultCalendar = new Intl.DateTimeFormat(locale).resolvedOptions().calendar; + + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "quarter", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.calendar, defaultCalendar); + assertEq(resolved.style, style); + assertEq(resolved.type, "quarter"); + assertEq(resolved.fallback, "code"); + + for (let i = 0; i < 4; i++) { + assertEq(dn.of(i + 1), styleTests[i]); + + // Also works with strings. + assertEq(dn.of(String(i + 1)), styleTests[i]); + + // Also works with objects. + assertEq(dn.of(Object(i + 1)), styleTests[i]); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "quarter"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + + // Throws an error if |code| isn't an integer. + assertThrowsInstanceOf(() => dn.of(1.5), RangeError); + assertThrowsInstanceOf(() => dn.of(-Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(NaN), RangeError); + + // Throws an error if outside of [1, 4]. + assertThrowsInstanceOf(() => dn.of(-1), RangeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + assertThrowsInstanceOf(() => dn.of(5), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/region.js b/js/src/tests/non262/Intl/DisplayNames/region.js new file mode 100644 index 0000000000..2d1bfe58d8 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/region.js @@ -0,0 +1,157 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "DE": "Germany", + "GB": "United Kingdom", + "US": "United States", + "FR": "France", + }, + short: { + "GB": "UK", + "US": "US", + }, + narrow: {}, + }, + "de": { + long: { + "DE": "Deutschland", + "GB": "Vereinigtes Königreich", + "US": "Vereinigte Staaten", + "FR": "Frankreich", + }, + short: { + "GB": "UK", + "US": "USA", + }, + narrow: {}, + }, + "fr": { + long: { + "DE": "Allemagne", + "GB": "Royaume-Uni", + "US": "États-Unis", + "FR": "France", + }, + short: { + "GB": "R.-U.", + "US": "É.-U.", + }, + narrow: {}, + }, + "zh": { + long: { + "CN": "中国", + "HK": "中国香港特别行政区", + }, + short: { + "HK": "香港" + }, + narrow: {}, + }, + "ar": { + long: { + "SA": "المملكة العربية السعودية", + "MO": "منطقة ماكاو الإدارية الخاصة", + }, + short: { + "MO": "مكاو", + }, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "region", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "region"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [region, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(region), expected); + + // Also works with objects. + assertEq(dn.of(Object(region)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "region"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + + // Throws an error if |code| can't be parsed as a `unicode_region_subtag` production. + assertThrowsInstanceOf(() => dn.of("CA-"), RangeError); + assertThrowsInstanceOf(() => dn.of("en-CA"), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "region"}); + let dn2 = new Intl.DisplayNames("en", {type: "region", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "region", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + // "AA" is not a registered region code. + assertEq(dn1.of("AA"), "AA"); + assertEq(dn2.of("AA"), "AA"); + assertEq(dn3.of("AA"), undefined); + + // The returned fallback is in canonical case. + assertEq(dn1.of("aa"), "AA"); + assertEq(dn2.of("aa"), "AA"); + assertEq(dn3.of("aa"), undefined); + + // "998" is canonicalised to "XZ", but "XZ" has no localised names. + assertEq(new Intl.Locale("und-998").region, "XZ"); + + // Ensure we return the input and not the canonicalised input. + assertEq(dn1.of("998"), "998"); + assertEq(dn2.of("998"), "998"); + assertEq(dn3.of("998"), undefined); + + // "XZ" should be consistent with "998". + assertEq(dn1.of("XZ"), "XZ"); + assertEq(dn2.of("XZ"), "XZ"); + assertEq(dn3.of("XZ"), undefined); +} + +// Ensure language tag canonicalisation is performed. +{ + let dn = new Intl.DisplayNames("en", {type: "region", fallback: "none"}); + + assertEq(dn.of("RU"), "Russia"); + + // ICU's canonicalisation supports "SU" -> "RU". + assertEq(Intl.getCanonicalLocales("ru-SU")[0], "ru-RU"); + assertEq(dn.of("SU"), "Russia"); + + // ICU's canonicalisation doesn't support "172" -> "RU". + assertEq(Intl.getCanonicalLocales("ru-172")[0], "ru-RU"); + assertEq(dn.of("172"), "Russia"); +} + +// Test when case isn't canonical. +{ + let dn = new Intl.DisplayNames("en", {type: "region", fallback: "none"}); + + assertEq(dn.of("IT"), "Italy"); + assertEq(dn.of("it"), "Italy"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/script.js b/js/src/tests/non262/Intl/DisplayNames/script.js new file mode 100644 index 0000000000..43b3ad5308 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/script.js @@ -0,0 +1,134 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +const tests = { + "en": { + long: { + "Latn": "Latin", + "Hant": "Traditional Han", + "Hans": "Simplified Han", + "Cans": "Unified Canadian Aboriginal Syllabics", + }, + short: { + "Hant": "Traditional", + "Hans": "Simplified", + "Cans": "UCAS", + }, + narrow: {}, + }, + "de": { + long: { + "Latn": "Lateinisch", + "Hant": "Traditionelles Chinesisch", + "Hans": "Vereinfachtes Chinesisch", + }, + short: { + "Hant": "Traditionell", + "Hans": "Vereinfacht", + }, + narrow: {}, + }, + "fr": { + long: { + "Latn": "latin", + "Hant": "sinogrammes traditionnels", + "Hans": "sinogrammes simplifiés", + }, + short: { + "Hant": "traditionnel", + "Hans": "simplifié", + }, + narrow: {}, + }, + "zh": { + long: { + "Latn": "拉丁文", + "Hant": "繁体中文", + "Hans": "简体中文", + }, + short: { + "Hant": "繁体", + "Hans": "简体", + }, + narrow: {}, + }, + "ar": { + long: { + "Latn": "اللاتينية", + "Arab": "العربية", + "Hant": "الهان التقليدية", + "Hans": "الهان المبسطة", + }, + short: { + "Hant": "التقليدية", + "Hans": "المبسطة", + }, + narrow: {}, + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "script", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.style, style); + assertEq(resolved.type, "script"); + assertEq(resolved.fallback, "code"); + + let inheritedTests = {...localeTests.long, ...localeTests.short, ...localeTests.narrow}; + for (let [script, expected] of Object.entries({...inheritedTests, ...styleTests})) { + assertEq(dn.of(script), expected); + + // Also works with objects. + assertEq(dn.of(Object(script)), expected); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "script"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + + // ToString(null) = "null", which passes `unicode_script_subtag`. + dn.of(null); // no error + + // Throws an error if |code| can't be parsed as a `unicode_script_subtag` production. + assertThrowsInstanceOf(() => dn.of("latn-"), RangeError); + assertThrowsInstanceOf(() => dn.of("en-latn"), RangeError); +} + +// Test fallback behaviour. +{ + let dn1 = new Intl.DisplayNames("en", {type: "script"}); + let dn2 = new Intl.DisplayNames("en", {type: "script", fallback: "code"}); + let dn3 = new Intl.DisplayNames("en", {type: "script", fallback: "none"}); + + assertEq(dn1.resolvedOptions().fallback, "code"); + assertEq(dn2.resolvedOptions().fallback, "code"); + assertEq(dn3.resolvedOptions().fallback, "none"); + + // "Aaaa" is not a registered script code. + assertEq(dn1.of("Aaaa"), "Aaaa"); + assertEq(dn2.of("Aaaa"), "Aaaa"); + assertEq(dn3.of("Aaaa"), undefined); + + // The returned fallback is in canonical case. + assertEq(dn1.of("aaaa"), "Aaaa"); + assertEq(dn2.of("aaaa"), "Aaaa"); + assertEq(dn3.of("aaaa"), undefined); +} + +// Test when case isn't canonical. +{ + let dn = new Intl.DisplayNames("en", {type: "script", fallback: "none"}); + + assertEq(dn.of("LATN"), "Latin"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/DisplayNames/shell.js b/js/src/tests/non262/Intl/DisplayNames/shell.js new file mode 100644 index 0000000000..20326b0254 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/shell.js @@ -0,0 +1,6 @@ +// Add |Intl.MozDisplayNames| to the Intl object. +function addMozIntlDisplayNames(global) { + let obj = {}; + global.addIntlExtras(obj); + global.Intl.DisplayNames = obj.DisplayNames; +} diff --git a/js/src/tests/non262/Intl/DisplayNames/weekday.js b/js/src/tests/non262/Intl/DisplayNames/weekday.js new file mode 100644 index 0000000000..fd19f24bb1 --- /dev/null +++ b/js/src/tests/non262/Intl/DisplayNames/weekday.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) + +addMozIntlDisplayNames(this); + +const tests = { + "en": { + long: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], + // short: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], + short: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], + narrow: ["M", "T", "W", "T", "F", "S", "S"], + }, + "de": { + long: ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], + // short: ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], + short: ["Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.", "So."], + narrow: ["M", "D", "M", "D", "F", "S", "S"], + }, + "fr": { + long: ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"], + // short: ["lun.", "mar.", "mer.", "jeu.", "ven.", "sam.", "dim."], + short: ["lu", "ma", "me", "je", "ve", "sa", "di"], + narrow: ["L", "M", "M", "J", "V", "S", "D"], + }, + "zh": { + long: ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], + short: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"], + narrow: ["一", "二", "三", "四", "五", "六", "日"], + }, +}; + +for (let [locale, localeTests] of Object.entries(tests)) { + let defaultCalendar = new Intl.DateTimeFormat(locale).resolvedOptions().calendar; + + for (let [style, styleTests] of Object.entries(localeTests)) { + let dn = new Intl.DisplayNames(locale, {type: "weekday", style}); + + let resolved = dn.resolvedOptions(); + assertEq(resolved.locale, locale); + assertEq(resolved.calendar, defaultCalendar); + assertEq(resolved.style, style); + assertEq(resolved.type, "weekday"); + assertEq(resolved.fallback, "code"); + + for (let i = 0; i < 7; i++) { + assertEq(dn.of(i + 1), styleTests[i]); + + // Also works with strings. + assertEq(dn.of(String(i + 1)), styleTests[i]); + + // Also works with objects. + assertEq(dn.of(Object(i + 1)), styleTests[i]); + } + } +} + +{ + let dn = new Intl.DisplayNames("en", {type: "weekday"}); + + // Performs ToString on the input and then validates the stringified result. + assertThrowsInstanceOf(() => dn.of(), RangeError); + assertThrowsInstanceOf(() => dn.of(null), RangeError); + assertThrowsInstanceOf(() => dn.of(Symbol()), TypeError); + + // Throws an error if |code| isn't an integer. + assertThrowsInstanceOf(() => dn.of(1.5), RangeError); + assertThrowsInstanceOf(() => dn.of(-Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(Infinity), RangeError); + assertThrowsInstanceOf(() => dn.of(NaN), RangeError); + + // Throws an error if outside of [1, 7]. + assertThrowsInstanceOf(() => dn.of(-1), RangeError); + assertThrowsInstanceOf(() => dn.of(0), RangeError); + assertThrowsInstanceOf(() => dn.of(8), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/ListFormat/browser.js b/js/src/tests/non262/Intl/ListFormat/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/browser.js diff --git a/js/src/tests/non262/Intl/ListFormat/conjunction-type.js b/js/src/tests/non262/Intl/ListFormat/conjunction-type.js new file mode 100644 index 0000000000..320a235271 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/conjunction-type.js @@ -0,0 +1,116 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Note: Use the same test locales as used in unit-type.js + +const {Element, Literal} = ListFormatParts; +const styles = ["long", "short", "narrow"]; + +// Test with zero elements. +{ + const list = []; + const expected = []; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "conjunction", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with one element. +{ + const list = ["A"]; + const expected = [Element(list[0])]; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "conjunction", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with two elements to cover the [[Template2]] case. +{ + const list = ["A", "B"]; + + const testData = { + "ar": { long: [Element("A"), Literal(" و"), Element("B")] }, + "de": { long: [Element("A"), Literal(" und "), Element("B")] }, + "en": { + long: [Element("A"), Literal(" and "), Element("B")], + short: [Element("A"), Literal(" & "), Element("B")], + narrow: [Element("A"), Literal(", "), Element("B")], + }, + "es": { long: [Element("A"), Literal(" y "), Element("B")] }, + "ja": { long: [Element("A"), Literal("、"), Element("B")] }, + "nl": { long: [Element("A"), Literal(" en "), Element("B")] }, + "th": { long: [Element("A"), Literal("และ"), Element("B")] }, + "zh": { + long: [Element("A"), Literal("和"), Element("B")], + narrow: [Element("A"), Literal("、"), Element("B")], + }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "conjunction", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +// Test with more than two elements. +// +// Use four elements to cover all template parts ([[TemplateStart]], [[TemplateMiddle]], and +// [[TemplateEnd]]). +{ + const list = ["A", "B", "C", "D"]; + + const testData = { + "ar": { + long: [Element("A"), Literal(" و"), Element("B"), Literal(" و"), Element("C"), Literal(" و"), Element("D")], + short: [Element("A"), Literal(" و"), Element("B"), Literal(" و"), Element("C"), Literal(" و"), Element("D")], + narrow: [Element("A"), Literal(" و"), Element("B"), Literal(" و"), Element("C"), Literal(" و"), Element("D")], + }, + "de": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" und "), Element("D")], + }, + "en": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", and "), Element("D")], + short: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", & "), Element("D")], + narrow: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", "), Element("D")], + }, + "es": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" y "), Element("D")], + }, + "ja": { + long: [Element("A"), Literal("、"), Element("B"), Literal("、"), Element("C"), Literal("、"), Element("D")], + }, + "nl": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" en "), Element("D")], + }, + "th": { + long: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" และ"), Element("D")], + }, + "zh": { + long: [Element("A"), Literal("、"), Element("B"), Literal("、"), Element("C"), Literal("和"), Element("D")], + narrow: [Element("A"), Literal("、"), Element("B"), Literal("、"), Element("C"), Literal("、"), Element("D")], + }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "conjunction", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/ListFormat/cross-compartment.js b/js/src/tests/non262/Intl/ListFormat/cross-compartment.js new file mode 100644 index 0000000000..37c45ef75c --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/cross-compartment.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +var g = newGlobal(); + +var locale = "en"; +var list = ["a", "b", "c"]; + +var listFormat = new Intl.ListFormat(locale); +var ccwListFormat = new g.Intl.ListFormat(locale); + +// Intl.ListFormat.prototype.format +{ + var fn = Intl.ListFormat.prototype.format; + + var expectedValue = fn.call(listFormat, list); + var actualValue = fn.call(ccwListFormat, list); + + assertEq(actualValue, expectedValue); +} + +// Intl.ListFormat.prototype.formatToParts +{ + var fn = Intl.ListFormat.prototype.formatToParts; + + var expectedValue = fn.call(listFormat, list); + var actualValue = fn.call(ccwListFormat, list); + + assertDeepEq(actualValue, expectedValue); +} + +// Intl.ListFormat.prototype.resolvedOptions +{ + var fn = Intl.ListFormat.prototype.resolvedOptions; + + var expectedValue = fn.call(listFormat); + var actualValue = fn.call(ccwListFormat); + + assertDeepEq(actualValue, expectedValue); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/ListFormat/disjunction-type.js b/js/src/tests/non262/Intl/ListFormat/disjunction-type.js new file mode 100644 index 0000000000..2713a8ae07 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/disjunction-type.js @@ -0,0 +1,108 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Note: Use the same test locales as used in unit-type.js + +const {Element, Literal} = ListFormatParts; +const styles = ["long", "short", "narrow"]; + +// Test with zero elements. +{ + const list = []; + const expected = []; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "disjunction", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with one element. +{ + const list = ["A"]; + const expected = [Element(list[0])]; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "disjunction", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with two elements to cover the [[Template2]] case. +{ + const list = ["A", "B"]; + + const testData = { + "ar": { long: [Element("A"), Literal(" أو "), Element("B")] }, + "de": { long: [Element("A"), Literal(" oder "), Element("B")] }, + "en": { long: [Element("A"), Literal(" or "), Element("B")] }, + "es": { long: [Element("A"), Literal(" o "), Element("B")] }, + "ja": { long: [Element("A"), Literal("または"), Element("B")] }, + "nl": { long: [Element("A"), Literal(" of "), Element("B")] }, + "th": { + long: [Element("A"), Literal(" หรือ "), Element("B")], + short: [Element("A"), Literal("หรือ"), Element("B")], + narrow: [Element("A"), Literal("หรือ"), Element("B")], + }, + "zh": { long: [Element("A"), Literal("或"), Element("B")] }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "disjunction", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +// Test with more than two elements. +// +// Use four elements to cover all template parts ([[TemplateStart]], [[TemplateMiddle]], and +// [[TemplateEnd]]). +{ + const list = ["A", "B", "C", "D"]; + + const testData = { + "ar": { + long: [Element("A"), Literal(" أو "), Element("B"), Literal(" أو "), Element("C"), Literal(" أو "), Element("D")], + }, + "de": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" oder "), Element("D")], + }, + "en": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", or "), Element("D")], + }, + "es": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" o "), Element("D")], + }, + "ja": { + long: [Element("A"), Literal("、"), Element("B"), Literal("、"), Element("C"), Literal("、または"), Element("D")], + }, + "nl": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" of "), Element("D")], + }, + "th": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" หรือ "), Element("D")], + }, + "zh": { + long: [Element("A"), Literal("、"), Element("B"), Literal("、"), Element("C"), Literal("或"), Element("D")], + }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "disjunction", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/ListFormat/same-compartment.js b/js/src/tests/non262/Intl/ListFormat/same-compartment.js new file mode 100644 index 0000000000..a51a041a08 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/same-compartment.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.wrapWithProto) + +var locale = "en"; +var list = ["a", "b", "c"]; + +var listFormat = new Intl.ListFormat(locale); +var scwListFormat = wrapWithProto(listFormat, Intl.ListFormat.prototype); + +// Intl.ListFormat.prototype.format +{ + var fn = Intl.ListFormat.prototype.format; + + var expectedValue = fn.call(listFormat, list); + var actualValue = fn.call(scwListFormat, list); + + assertEq(actualValue, expectedValue); +} + +// Intl.ListFormat.prototype.formatToParts +{ + var fn = Intl.ListFormat.prototype.formatToParts; + + var expectedValue = fn.call(listFormat, list); + var actualValue = fn.call(scwListFormat, list); + + assertDeepEq(actualValue, expectedValue); +} + +// Intl.ListFormat.prototype.resolvedOptions +{ + var fn = Intl.ListFormat.prototype.resolvedOptions; + + var expectedValue = fn.call(listFormat); + var actualValue = fn.call(scwListFormat); + + assertDeepEq(actualValue, expectedValue); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/ListFormat/shell.js b/js/src/tests/non262/Intl/ListFormat/shell.js new file mode 100644 index 0000000000..70056a579e --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/shell.js @@ -0,0 +1,21 @@ +function GenericPartCreator(type) { + return str => ({ type, value: str }); +} + +const ListFormatParts = { + Element: GenericPartCreator("element"), + Literal: GenericPartCreator("literal"), +}; + +function assertParts(lf, x, expected) { + var parts = lf.formatToParts(x); + assertEq(parts.map(part => part.value).join(""), lf.format(x), + "formatToParts and format must agree"); + + var len = parts.length; + assertEq(len, expected.length, "parts count mismatch"); + for (var i = 0; i < len; i++) { + assertEq(parts[i].type, expected[i].type, "type mismatch at " + i); + assertEq(parts[i].value, expected[i].value, "value mismatch at " + i); + } +} diff --git a/js/src/tests/non262/Intl/ListFormat/supported-locales.js b/js/src/tests/non262/Intl/ListFormat/supported-locales.js new file mode 100644 index 0000000000..f5d05e2da5 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/supported-locales.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Intl.ListFormat.supportedLocalesOf returns an empty array for unsupported locales. +assertEq(Intl.ListFormat.supportedLocalesOf("art-lobjan").length, 0); + +// And a non-empty array for supported locales. +assertEq(Intl.ListFormat.supportedLocalesOf("en").length, 1); +assertEq(Intl.ListFormat.supportedLocalesOf("en")[0], "en"); + +// If the locale is supported per |Intl.ListFormat.supportedLocalesOf|, the resolved locale +// should reflect this. +for (let locale of Intl.ListFormat.supportedLocalesOf(["en", "de", "th", "ar"])) { + let lf = new Intl.ListFormat(locale); + assertEq(lf.resolvedOptions().locale, locale); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/ListFormat/unit-type.js b/js/src/tests/non262/Intl/ListFormat/unit-type.js new file mode 100644 index 0000000000..8c76677865 --- /dev/null +++ b/js/src/tests/non262/Intl/ListFormat/unit-type.js @@ -0,0 +1,149 @@ +// |reftest| skip -- "unit" type currently not supported + +const {Element, Literal} = ListFormatParts; +const styles = ["long", "short", "narrow"]; + +// Test with zero elements. +{ + const list = []; + const expected = []; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "unit", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with one element. +{ + const list = ["A"]; + const expected = [Element(list[0])]; + const locales = ["ar", "de", "en", "es", "ja", "nl", "th", "zh"]; + + for (let locale of locales) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "unit", style}); + assertParts(lf, list, expected); + } + } +} + +// Test with two elements to cover the [[Template2]] case. +{ + const list = ["A", "B"]; + + const testData = { + "ar": { + long: [Element("A"), Literal(" و"), Element("B")], + narrow: [Element("A"), Literal("، "), Element("B")], + }, + "de": { + long: [Element("A"), Literal(", "), Element("B")], + }, + "en": { + long: [Element("A"), Literal(", "), Element("B")], + narrow: [Element("A"), Literal(" "), Element("B")], + }, + "es": { + long: [Element("A"), Literal(" y "), Element("B")], + narrow: [Element("A"), Literal(" "), Element("B")], + }, + "ja": { + long: [Element("A"), Literal(" "), Element("B")], + narrow: [Element("A"), Element("B")], + }, + "nl": { + long: [Element("A"), Literal(" en "), Element("B")], + short: [Element("A"), Literal(", "), Element("B")], + narrow: [Element("A"), Literal(", "), Element("B")], + }, + "th": { + long: [Element("A"), Literal(" และ "), Element("B")], + short: [Element("A"), Literal(" "), Element("B")], + narrow: [Element("A"), Literal(" "), Element("B")], + }, + "zh": { + long: [Element("A"), Element("B")], + }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "unit", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +// Test with more than two elements. +// +// Use four elements to cover all template parts ([[TemplateStart]], [[TemplateMiddle]], and +// [[TemplateEnd]]). +{ + const list = ["A", "B", "C", "D"]; + + const testData = { + // non-ASCII case + "ar": { + long: [Element("A"), Literal("، و"), Element("B"), Literal("، و"), Element("C"), Literal("، و"), Element("D")], + narrow: [Element("A"), Literal("، "), Element("B"), Literal("، "), Element("C"), Literal("، "), Element("D")], + }, + + // all values are equal + "de": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" und "), Element("D")], + }, + + // long and short values are equal + "en": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", "), Element("D")], + narrow: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" "), Element("D")], + }, + + // all values are different + "es": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" y "), Element("D")], + short: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", "), Element("D")], + narrow: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" "), Element("D")], + }, + + // no spacing for narrow case + "ja": { + long: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" "), Element("D")], + narrow: [Element("A"), Element("B"), Element("C"), Element("D")], + }, + + // short and narrow values are equal + "nl": { + long: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(" en "), Element("D")], + short: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", "), Element("D")], + narrow: [Element("A"), Literal(", "), Element("B"), Literal(", "), Element("C"), Literal(", "), Element("D")], + }, + + // another non-ASCII case + "th": { + long: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" และ "), Element("D")], + narrow: [Element("A"), Literal(" "), Element("B"), Literal(" "), Element("C"), Literal(" "), Element("D")], + }, + + // no whitespace at all + "zh": { + long: [Element("A"), Element("B"), Element("C"), Element("D")], + }, + }; + + for (let [locale, localeData] of Object.entries(testData)) { + for (let style of styles) { + let lf = new Intl.ListFormat(locale, {type: "unit", style}); + let {[style]: expected = localeData.long} = localeData; + assertParts(lf, list, expected); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/apply-options-to-tag-canonicalize-twice.js b/js/src/tests/non262/Intl/Locale/apply-options-to-tag-canonicalize-twice.js new file mode 100644 index 0000000000..2a96200757 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/apply-options-to-tag-canonicalize-twice.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// ApplyOptionsToTag canonicalises the locale identifier before applying the +// options. That means "und-Armn-SU" is first canonicalised to "und-Armn-AM", +// then the language is changed to "ru". If "ru" were applied first, the result +// would be "ru-Armn-RU" instead. +assertEq(new Intl.Locale("und-Armn-SU", {language:"ru"}).toString(), + "ru-Armn-AM"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/browser.js b/js/src/tests/non262/Intl/Locale/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/browser.js diff --git a/js/src/tests/non262/Intl/Locale/coerce-options-before-validating-tag.js b/js/src/tests/non262/Intl/Locale/coerce-options-before-validating-tag.js new file mode 100644 index 0000000000..7f57a61132 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/coerce-options-before-validating-tag.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Throw a TypeError when the |options| argument is null before validating the +// language tag argument. +assertThrowsInstanceOf(() => { + new Intl.Locale("invalid_tag", null); +}, TypeError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/cross-compartment.js b/js/src/tests/non262/Intl/Locale/cross-compartment.js new file mode 100644 index 0000000000..d3788e3eb7 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/cross-compartment.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +var g = newGlobal(); + +var tag = "de-Latn-AT-u-ca-gregory-nu-latn-co-phonebk-kf-false-kn-hc-h23"; +var locale = new Intl.Locale(tag); +var ccwLocale = new g.Intl.Locale(tag); + +for (var [key, {get, value = get}] of Object.entries(Object.getOwnPropertyDescriptors(Intl.Locale.prototype))) { + if (typeof value === "function") { + if (key !== "constructor") { + var expectedValue = value.call(locale); + + if (typeof expectedValue === "string" || typeof expectedValue === "boolean") { + assertEq(value.call(ccwLocale), expectedValue, key); + } else if (expectedValue instanceof Intl.Locale) { + assertEq(value.call(ccwLocale).toString(), expectedValue.toString(), key); + } else { + throw new Error("unexpected result value"); + } + } else { + assertEq(new value(ccwLocale).toString(), new value(locale).toString(), key); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/legacy.js b/js/src/tests/non262/Intl/Locale/legacy.js new file mode 100644 index 0000000000..835e886122 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/legacy.js @@ -0,0 +1,75 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +var testData = [ + { + tag: "cel-gaulish", + options: { + numberingSystem: "latn", + }, + canonical: "xtg-u-nu-latn", + extensions: { + numberingSystem: "latn", + }, + }, + + { + tag: "cel-gaulish", + options: { + region: "FR", + numberingSystem: "latn", + }, + canonical: "xtg-FR-u-nu-latn", + extensions: { + numberingSystem: "latn", + }, + }, + + { + tag: "art-lojban", + options: { + numberingSystem: "latn", + }, + canonical: "jbo-u-nu-latn", + extensions: { + numberingSystem: "latn", + }, + }, + + { + tag: "art-lojban", + options: { + region: "ZZ", + numberingSystem: "latn", + }, + canonical: "jbo-ZZ-u-nu-latn", + extensions: { + numberingSystem: "latn", + }, + }, +]; + +for (var {tag, options, canonical, extensions} of testData) { + var loc = new Intl.Locale(tag, options); + assertEq(loc.toString(), canonical); + + for (var [name, value] of Object.entries(extensions)) { + assertEq(loc[name], value); + } +} + +var errorTestData = [ + "en-gb-oed", + "i-default", + "sgn-ch-de", + "zh-min", + "zh-min-nan", + "zh-hakka-hakka", +]; + +for (var tag of errorTestData) { + assertThrowsInstanceOf(() => new Intl.Locale(tag), RangeError); + assertThrowsInstanceOf(() => new Intl.Locale(tag, {}), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/likely-subtags-generated.js b/js/src/tests/non262/Intl/Locale/likely-subtags-generated.js new file mode 100644 index 0000000000..91d23ae663 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/likely-subtags-generated.js @@ -0,0 +1,15797 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) +// Generated by make_intl_data.py. DO NOT EDIT. + +// Extracted from likelySubtags.xml. +// Derived from CLDR Supplemental Data, version 43. +// https://unicode.org/Public/cldr/43/cldr-common-43.0.zip +var maxLikelySubtags = { + "aa": "aa-Latn-ET", + "aaa": "aaa-Latn-NG", + "aab": "aab-Latn-NG", + "aac": "aac-Latn-PG", + "aad": "aad-Latn-PG", + "aae": "aae-Latn-IT", + "aae-Grek": "aae-Grek-IT", + "aaf": "aaf-Mlym-IN", + "aaf-Arab": "aaf-Arab-IN", + "aag": "aag-Latn-PG", + "aah": "aah-Latn-PG", + "aai": "aai-Latn-ZZ", + "aak": "aak-Latn-ZZ", + "aal": "aal-Latn-CM", + "aan": "aan-Latn-BR", + "aao": "aao-Arab-DZ", + "aap": "aap-Latn-BR", + "aaq": "aaq-Latn-US", + "aas": "aas-Latn-TZ", + "aat": "aat-Grek-GR", + "aau": "aau-Latn-ZZ", + "aaw": "aaw-Latn-PG", + "aax": "aax-Latn-ID", + "aaz": "aaz-Latn-ID", + "ab": "ab-Cyrl-GE", + "aba": "aba-Latn-CI", + "abb": "abb-Latn-CM", + "abc": "abc-Latn-PH", + "abd": "abd-Latn-PH", + "abe": "abe-Latn-CA", + "abf": "abf-Latn-MY", + "abg": "abg-Latn-PG", + "abh": "abh-Arab-TJ", + "abi": "abi-Latn-ZZ", + "abl": "abl-Rjng-ID", + "abl-Latn": "abl-Latn-ID", + "abm": "abm-Latn-NG", + "abn": "abn-Latn-NG", + "abo": "abo-Latn-NG", + "abp": "abp-Latn-PH", + "abq": "abq-Cyrl-ZZ", + "abr": "abr-Latn-GH", + "abs": "abs-Latn-ID", + "abt": "abt-Latn-ZZ", + "abu": "abu-Latn-CI", + "abv": "abv-Arab-BH", + "abw": "abw-Latn-PG", + "abx": "abx-Latn-PH", + "aby": "aby-Latn-ZZ", + "abz": "abz-Latn-ID", + "aca": "aca-Latn-CO", + "acb": "acb-Latn-NG", + "acd": "acd-Latn-ZZ", + "ace": "ace-Latn-ID", + "acf": "acf-Latn-LC", + "ach": "ach-Latn-UG", + "acm": "acm-Arab-IQ", + "acn": "acn-Latn-CN", + "acp": "acp-Latn-NG", + "acq": "acq-Arab-YE", + "acr": "acr-Latn-GT", + "acs": "acs-Latn-BR", + "act": "act-Latn-NL", + "acu": "acu-Latn-EC", + "acv": "acv-Latn-US", + "acw": "acw-Arab-SA", + "acx": "acx-Arab-OM", + "acy": "acy-Latn-CY", + "acy-Arab": "acy-Arab-CY", + "acy-Grek": "acy-Grek-CY", + "acz": "acz-Latn-SD", + "ada": "ada-Latn-GH", + "adb": "adb-Latn-TL", + "add": "add-Latn-CM", + "ade": "ade-Latn-ZZ", + "adf": "adf-Arab-OM", + "adg": "adg-Latn-AU", + "adh": "adh-Latn-UG", + "adi": "adi-Latn-IN", + "adi-Tibt": "adi-Tibt-CN", + "adj": "adj-Latn-ZZ", + "adl": "adl-Latn-IN", + "adn": "adn-Latn-ID", + "ado": "ado-Latn-PG", + "adp": "dz-Tibt-BT", + "adq": "adq-Latn-GH", + "adr": "adr-Latn-ID", + "adt": "adt-Latn-AU", + "adu": "adu-Latn-NG", + "adw": "adw-Latn-BR", + "adx": "adx-Tibt-CN", + "ady": "ady-Cyrl-RU", + "adz": "adz-Latn-ZZ", + "ae": "ae-Avst-IR", + "aea": "aea-Latn-AU", + "aeb": "aeb-Arab-TN", + "aec": "aec-Arab-EG", + "aee": "aee-Arab-AF", + "aek": "aek-Latn-NC", + "ael": "ael-Latn-CM", + "aem": "aem-Latn-VN", + "aeq": "aeq-Arab-PK", + "aer": "aer-Latn-AU", + "aeu": "aeu-Latn-CN", + "aew": "aew-Latn-PG", + "aey": "aey-Latn-ZZ", + "aez": "aez-Latn-PG", + "af": "af-Latn-ZA", + "afb": "afb-Arab-KW", + "afd": "afd-Latn-PG", + "afe": "afe-Latn-NG", + "afh": "afh-Latn-GH", + "afi": "afi-Latn-PG", + "afk": "afk-Latn-PG", + "afn": "afn-Latn-NG", + "afo": "afo-Latn-NG", + "afp": "afp-Latn-PG", + "afs": "afs-Latn-MX", + "afu": "afu-Latn-GH", + "afz": "afz-Latn-ID", + "aga": "aga-Latn-PE", + "agb": "agb-Latn-NG", + "agc": "agc-Latn-ZZ", + "agd": "agd-Latn-ZZ", + "age": "age-Latn-PG", + "agf": "agf-Latn-ID", + "agg": "agg-Latn-ZZ", + "agh": "agh-Latn-CD", + "agi": "agi-Deva-IN", + "agj": "agj-Ethi-ET", + "agj-Arab": "agj-Arab-ET", + "agk": "agk-Latn-PH", + "agl": "agl-Latn-PG", + "agm": "agm-Latn-ZZ", + "agn": "agn-Latn-PH", + "ago": "ago-Latn-ZZ", + "agq": "agq-Latn-CM", + "agr": "agr-Latn-PE", + "ags": "ags-Latn-CM", + "agt": "agt-Latn-PH", + "agu": "agu-Latn-GT", + "agv": "agv-Latn-PH", + "agw": "agw-Latn-SB", + "agx": "agx-Cyrl-RU", + "agy": "agy-Latn-PH", + "agz": "agz-Latn-PH", + "aha": "aha-Latn-ZZ", + "ahb": "ahb-Latn-VU", + "ahg": "ahg-Ethi-ET", + "ahh": "ahh-Latn-ID", + "ahi": "ahi-Latn-CI", + "ahk": "ahk-Latn-MM", + "ahk-Mymr": "ahk-Mymr-MM", + "ahk-TH": "ahk-Latn-TH", + "ahk-Thai": "ahk-Thai-TH", + "ahl": "ahl-Latn-ZZ", + "ahm": "ahm-Latn-CI", + "ahn": "ahn-Latn-NG", + "aho": "aho-Ahom-IN", + "ahp": "ahp-Latn-CI", + "ahr": "ahr-Deva-IN", + "ahs": "ahs-Latn-NG", + "aht": "aht-Latn-US", + "aia": "aia-Latn-SB", + "aib": "aib-Arab-CN", + "aic": "aic-Latn-PG", + "aid": "aid-Latn-AU", + "aie": "aie-Latn-PG", + "aif": "aif-Latn-PG", + "aig": "aig-Latn-AG", + "aij": "aij-Hebr-IL", + "aik": "aik-Latn-NG", + "ail": "ail-Latn-PG", + "aim": "aim-Latn-IN", + "ain": "ain-Kana-JP", + "ain-Latn": "ain-Latn-JP", + "aio": "aio-Mymr-IN", + "aip": "aip-Latn-ID", + "aiq": "aiq-Arab-AF", + "air": "air-Latn-ID", + "ait": "ait-Latn-BR", + "aiw": "aiw-Latn-ET", + "aiw-Arab": "aiw-Arab-ET", + "aiw-Ethi": "aiw-Ethi-ET", + "aix": "aix-Latn-PG", + "aiy": "aiy-Latn-CF", + "aja": "aja-Latn-SS", + "ajg": "ajg-Latn-ZZ", + "aji": "aji-Latn-NC", + "ajn": "ajn-Latn-AU", + "ajp": "ajp-Arab-JO", + "ajt": "aeb-Arab-TN", + "ajw": "ajw-Latn-NG", + "ajz": "ajz-Latn-IN", + "ak": "ak-Latn-GH", + "akb": "akb-Latn-ID", + "akb-Batk": "akb-Batk-ID", + "akc": "akc-Latn-ID", + "akd": "akd-Latn-NG", + "ake": "ake-Latn-GY", + "akf": "akf-Latn-NG", + "akg": "akg-Latn-ID", + "akh": "akh-Latn-PG", + "aki": "aki-Latn-PG", + "akk": "akk-Xsux-IQ", + "akl": "akl-Latn-PH", + "ako": "ako-Latn-SR", + "akp": "akp-Latn-GH", + "akq": "akq-Latn-PG", + "akr": "akr-Latn-VU", + "aks": "aks-Latn-TG", + "akt": "akt-Latn-PG", + "aku": "aku-Latn-CM", + "akv": "akv-Cyrl-RU", + "akw": "akw-Latn-CG", + "akz": "akz-Latn-US", + "ala": "ala-Latn-ZZ", + "alc": "alc-Latn-CL", + "ald": "ald-Latn-CI", + "ale": "ale-Latn-US", + "alf": "alf-Latn-NG", + "alh": "alh-Latn-AU", + "ali": "ali-Latn-ZZ", + "alj": "alj-Latn-PH", + "alk": "alk-Laoo-LA", + "all": "all-Mlym-IN", + "alm": "alm-Latn-VU", + "aln": "aln-Latn-XK", + "alo": "alo-Latn-ID", + "alp": "alp-Latn-ID", + "alq": "alq-Latn-CA", + "alr": "alr-Cyrl-RU", + "alt": "alt-Cyrl-RU", + "alu": "alu-Latn-SB", + "alw": "alw-Ethi-ET", + "alx": "alx-Latn-PG", + "aly": "aly-Latn-AU", + "alz": "alz-Latn-CD", + "am": "am-Ethi-ET", + "ama": "ama-Latn-BR", + "amb": "amb-Latn-NG", + "amc": "amc-Latn-PE", + "ame": "ame-Latn-PE", + "amf": "amf-Latn-ET", + "amf-Ethi": "amf-Ethi-ET", + "amg": "amg-Latn-AU", + "ami": "ami-Latn-TW", + "amj": "amj-Latn-TD", + "amk": "amk-Latn-ID", + "amm": "amm-Latn-ZZ", + "amn": "amn-Latn-ZZ", + "amo": "amo-Latn-NG", + "amp": "amp-Latn-ZZ", + "amq": "amq-Latn-ID", + "amr": "amr-Latn-PE", + "ams": "ams-Jpan-JP", + "amt": "amt-Latn-PG", + "amu": "amu-Latn-MX", + "amv": "amv-Latn-ID", + "amw": "amw-Syrc-SY", + "amw-Arab": "amw-Arab-SY", + "amw-Armi": "amw-Armi-SY", + "amw-Latn": "amw-Latn-SY", + "amx": "amx-Latn-AU", + "amy": "amy-Latn-AU", + "amz": "amz-Latn-AU", + "an": "an-Latn-ES", + "ana": "ana-Latn-CO", + "anb": "anb-Latn-PE", + "anc": "anc-Latn-ZZ", + "and": "and-Latn-ID", + "ane": "ane-Latn-NC", + "anf": "anf-Latn-GH", + "ang": "ang-Latn-GB", + "anh": "anh-Latn-PG", + "ani": "ani-Cyrl-RU", + "anj": "anj-Latn-PG", + "ank": "ank-Latn-ZZ", + "anl": "anl-Latn-MM", + "anm": "anm-Latn-IN", + "ann": "ann-Latn-NG", + "ano": "ano-Latn-CO", + "anp": "anp-Deva-IN", + "anr": "anr-Deva-IN", + "ans": "ans-Latn-CO", + "ant": "ant-Latn-AU", + "anu": "anu-Ethi-ET", + "anu-Arab": "anu-Arab-SS", + "anu-Latn": "anu-Latn-SS", + "anv": "anv-Latn-CM", + "anw": "anw-Latn-NG", + "anx": "anx-Latn-PG", + "any": "any-Latn-ZZ", + "anz": "anz-Latn-PG", + "aoa": "aoa-Latn-ST", + "aob": "aob-Latn-PG", + "aoc": "aoc-Latn-VE", + "aod": "aod-Latn-PG", + "aoe": "aoe-Latn-PG", + "aof": "aof-Latn-PG", + "aog": "aog-Latn-PG", + "aoi": "aoi-Latn-AU", + "aoj": "aoj-Latn-ZZ", + "aok": "aok-Latn-NC", + "aol": "aol-Latn-ID", + "aom": "aom-Latn-ZZ", + "aon": "aon-Latn-PG", + "aor": "aor-Latn-VU", + "aos": "aos-Latn-ID", + "aot": "aot-Beng-BD", + "aot-Latn": "aot-Latn-IN", + "aox": "aox-Latn-GY", + "aoz": "aoz-Latn-ID", + "apb": "apb-Latn-SB", + "apc": "apc-Arab-SY", + "apd": "apd-Arab-TG", + "ape": "ape-Latn-ZZ", + "apf": "apf-Latn-PH", + "apg": "apg-Latn-ID", + "aph": "aph-Deva-NP", + "api": "api-Latn-BR", + "apj": "apj-Latn-US", + "apk": "apk-Latn-US", + "apl": "apl-Latn-US", + "apm": "apm-Latn-US", + "apn": "apn-Latn-BR", + "apo": "apo-Latn-PG", + "app": "app-Latn-VU", + "apr": "apr-Latn-ZZ", + "aps": "aps-Latn-ZZ", + "apt": "apt-Latn-IN", + "apu": "apu-Latn-BR", + "apv": "apv-Latn-BR", + "apw": "apw-Latn-US", + "apx": "apx-Latn-ID", + "apy": "apy-Latn-BR", + "apz": "apz-Latn-ZZ", + "aqc": "aqc-Cyrl-RU", + "aqd": "aqd-Latn-ML", + "aqg": "aqg-Latn-NG", + "aqk": "aqk-Latn-NG", + "aqm": "aqm-Latn-ID", + "aqn": "aqn-Latn-PH", + "aqr": "aqr-Latn-NC", + "aqt": "aqt-Latn-PY", + "aqz": "aqz-Latn-BR", + "ar": "ar-Arab-EG", + "arc": "arc-Armi-IR", + "arc-Nbat": "arc-Nbat-JO", + "arc-Palm": "arc-Palm-SY", + "ard": "ard-Latn-AU", + "are": "are-Latn-AU", + "arh": "arh-Latn-ZZ", + "ari": "ari-Latn-US", + "arj": "arj-Latn-BR", + "ark": "ark-Latn-BR", + "arl": "arl-Latn-PE", + "arn": "arn-Latn-CL", + "aro": "aro-Latn-BO", + "arp": "arp-Latn-US", + "arq": "arq-Arab-DZ", + "arr": "arr-Latn-BR", + "ars": "ars-Arab-SA", + "aru": "aru-Latn-BR", + "arw": "arw-Latn-SR", + "arx": "arx-Latn-BR", + "ary": "ary-Arab-MA", + "arz": "arz-Arab-EG", + "as": "as-Beng-IN", + "asa": "asa-Latn-TZ", + "asb": "asb-Latn-CA", + "asc": "asc-Latn-ID", + "ase": "ase-Sgnw-US", + "asg": "asg-Latn-ZZ", + "ash": "ash-Latn-PE", + "asi": "asi-Latn-ID", + "asj": "asj-Latn-CM", + "ask": "ask-Arab-AF", + "asl": "asl-Latn-ID", + "asn": "asn-Latn-BR", + "aso": "aso-Latn-ZZ", + "ass": "ass-Latn-CM", + "ast": "ast-Latn-ES", + "asu": "asu-Latn-BR", + "asv": "asv-Latn-CD", + "asx": "asx-Latn-PG", + "asy": "asy-Latn-ID", + "asz": "asz-Latn-ID", + "ata": "ata-Latn-ZZ", + "atb": "atb-Latn-CN", + "atb-Lisu": "atb-Lisu-CN", + "atc": "atc-Latn-PE", + "atd": "atd-Latn-PH", + "ate": "ate-Latn-PG", + "atg": "atg-Latn-ZZ", + "ati": "ati-Latn-CI", + "atj": "atj-Latn-CA", + "atk": "atk-Latn-PH", + "atl": "atl-Latn-PH", + "atm": "atm-Latn-PH", + "atn": "atn-Arab-IR", + "ato": "ato-Latn-CM", + "atp": "atp-Latn-PH", + "atq": "atq-Latn-ID", + "atr": "atr-Latn-BR", + "ats": "ats-Latn-US", + "att": "att-Latn-PH", + "atu": "atu-Latn-SS", + "atv": "atv-Cyrl-RU", + "atw": "atw-Latn-US", + "atx": "atx-Latn-BR", + "aty": "aty-Latn-VU", + "atz": "atz-Latn-PH", + "aua": "aua-Latn-SB", + "auc": "auc-Latn-EC", + "aud": "aud-Latn-SB", + "aug": "aug-Latn-BJ", + "auh": "auh-Latn-ZM", + "aui": "aui-Latn-PG", + "auj": "auj-Arab-LY", + "auj-Latn": "auj-Latn-LY", + "auj-Tfng": "auj-Tfng-LY", + "auk": "auk-Latn-PG", + "aul": "aul-Latn-VU", + "aum": "aum-Latn-NG", + "aun": "aun-Latn-PG", + "auo": "auo-Latn-NG", + "aup": "aup-Latn-PG", + "auq": "auq-Latn-ID", + "aur": "aur-Latn-PG", + "aut": "aut-Latn-PF", + "auu": "auu-Latn-ID", + "auw": "auw-Latn-ID", + "auy": "auy-Latn-ZZ", + "auz": "auz-Arab-UZ", + "av": "av-Cyrl-RU", + "avb": "avb-Latn-PG", + "avd": "avd-Arab-IR", + "avi": "avi-Latn-CI", + "avk": "avk-Latn-001", + "avl": "avl-Arab-ZZ", + "avm": "avm-Latn-AU", + "avn": "avn-Latn-ZZ", + "avo": "avo-Latn-BR", + "avs": "avs-Latn-PE", + "avt": "avt-Latn-ZZ", + "avu": "avu-Latn-ZZ", + "avv": "avv-Latn-BR", + "awa": "awa-Deva-IN", + "awb": "awb-Latn-ZZ", + "awc": "awc-Latn-NG", + "awe": "awe-Latn-BR", + "awg": "awg-Latn-AU", + "awh": "awh-Latn-ID", + "awi": "awi-Latn-PG", + "awk": "awk-Latn-AU", + "awm": "awm-Latn-PG", + "awn": "awn-Ethi-ET", + "awo": "awo-Latn-ZZ", + "awr": "awr-Latn-ID", + "aws": "aws-Latn-ID", + "awt": "awt-Latn-BR", + "awu": "awu-Latn-ID", + "awv": "awv-Latn-ID", + "aww": "aww-Latn-PG", + "awx": "awx-Latn-ZZ", + "awy": "awy-Latn-ID", + "axb": "axb-Latn-AR", + "axe": "axe-Latn-AU", + "axg": "axg-Latn-BR", + "axk": "axk-Latn-CF", + "axl": "axl-Latn-AU", + "axm": "axm-Armn-AM", + "axx": "axx-Latn-NC", + "ay": "ay-Latn-BO", + "aya": "aya-Latn-PG", + "ayb": "ayb-Latn-ZZ", + "ayc": "ayc-Latn-PE", + "ayd": "ayd-Latn-AU", + "aye": "aye-Latn-NG", + "ayg": "ayg-Latn-TG", + "ayh": "ayh-Arab-YE", + "ayi": "ayi-Latn-NG", + "ayk": "ayk-Latn-NG", + "ayl": "ayl-Arab-LY", + "ayn": "ayn-Arab-YE", + "ayo": "ayo-Latn-PY", + "ayp": "ayp-Arab-IQ", + "ayq": "ayq-Latn-PG", + "ays": "ays-Latn-PH", + "ayt": "ayt-Latn-PH", + "ayu": "ayu-Latn-NG", + "ayz": "ayz-Latn-ID", + "az": "az-Latn-AZ", + "az-Arab": "az-Arab-IR", + "az-IQ": "az-Arab-IQ", + "az-IR": "az-Arab-IR", + "az-RU": "az-Cyrl-RU", + "azb": "azb-Arab-IR", + "azb-Cyrl": "azb-Cyrl-AZ", + "azb-Latn": "azb-Latn-AZ", + "azd": "azd-Latn-MX", + "azg": "azg-Latn-MX", + "azm": "azm-Latn-MX", + "azn": "azn-Latn-MX", + "azo": "azo-Latn-CM", + "azt": "azt-Latn-PH", + "azz": "azz-Latn-MX", + "ba": "ba-Cyrl-RU", + "baa": "baa-Latn-SB", + "bab": "bab-Latn-GW", + "bac": "bac-Latn-ID", + "bae": "bae-Latn-VE", + "baf": "baf-Latn-CM", + "bag": "bag-Latn-CM", + "bah": "bah-Latn-BS", + "baj": "baj-Latn-ID", + "bal": "bal-Arab-PK", + "ban": "ban-Latn-ID", + "bao": "bao-Latn-CO", + "bap": "bap-Deva-NP", + "bar": "bar-Latn-AT", + "bas": "bas-Latn-CM", + "bau": "bau-Latn-NG", + "bav": "bav-Latn-ZZ", + "baw": "baw-Latn-CM", + "bax": "bax-Bamu-CM", + "bay": "bay-Latn-ID", + "bba": "bba-Latn-ZZ", + "bbb": "bbb-Latn-ZZ", + "bbc": "bbc-Latn-ID", + "bbd": "bbd-Latn-ZZ", + "bbe": "bbe-Latn-CD", + "bbf": "bbf-Latn-PG", + "bbg": "bbg-Latn-GA", + "bbi": "bbi-Latn-CM", + "bbj": "bbj-Latn-CM", + "bbk": "bbk-Latn-CM", + "bbl": "bbl-Geor-GE", + "bbm": "bbm-Latn-CD", + "bbn": "bbn-Latn-PG", + "bbo": "bbo-Latn-BF", + "bbp": "bbp-Latn-ZZ", + "bbq": "bbq-Latn-CM", + "bbr": "bbr-Latn-ZZ", + "bbs": "bbs-Latn-NG", + "bbt": "bbt-Latn-NG", + "bbu": "bbu-Latn-NG", + "bbv": "bbv-Latn-PG", + "bbw": "bbw-Latn-CM", + "bbx": "bbx-Latn-CM", + "bby": "bby-Latn-CM", + "bca": "bca-Latn-CN", + "bca-Hani": "bca-Hani-CN", + "bcb": "bcb-Latn-SN", + "bcd": "bcd-Latn-ID", + "bce": "bce-Latn-CM", + "bcf": "bcf-Latn-ZZ", + "bcg": "bcg-Latn-GN", + "bch": "bch-Latn-ZZ", + "bci": "bci-Latn-CI", + "bcj": "bcj-Latn-AU", + "bck": "bck-Latn-AU", + "bcm": "bcm-Latn-ZZ", + "bcn": "bcn-Latn-ZZ", + "bco": "bco-Latn-ZZ", + "bcp": "bcp-Latn-CD", + "bcq": "bcq-Ethi-ZZ", + "bcr": "bcr-Latn-CA", + "bcs": "bcs-Latn-NG", + "bct": "bct-Latn-CD", + "bcu": "bcu-Latn-ZZ", + "bcv": "bcv-Latn-NG", + "bcw": "bcw-Latn-CM", + "bcy": "bcy-Latn-NG", + "bcz": "bcz-Latn-SN", + "bda": "bda-Latn-SN", + "bdb": "bdb-Latn-ID", + "bdc": "bdc-Latn-CO", + "bdd": "bdd-Latn-ZZ", + "bde": "bde-Latn-NG", + "bdf": "bdf-Latn-PG", + "bdg": "bdg-Latn-MY", + "bdh": "bdh-Latn-SS", + "bdi": "bdi-Latn-SD", + "bdj": "bdj-Latn-SS", + "bdk": "bdk-Latn-AZ", + "bdl": "bdl-Latn-ID", + "bdm": "bdm-Latn-TD", + "bdn": "bdn-Latn-CM", + "bdo": "bdo-Latn-TD", + "bdp": "bdp-Latn-TZ", + "bdq": "bdq-Latn-VN", + "bdr": "bdr-Latn-MY", + "bds": "bds-Latn-TZ", + "bdt": "bdt-Latn-CF", + "bdu": "bdu-Latn-CM", + "bdv": "bdv-Orya-IN", + "bdw": "bdw-Latn-ID", + "bdx": "bdx-Latn-ID", + "bdy": "bdy-Latn-AU", + "bdz": "bdz-Arab-PK", + "be": "be-Cyrl-BY", + "bea": "bea-Latn-CA", + "bea-Cans": "bea-Cans-CA", + "beb": "beb-Latn-CM", + "bec": "bec-Latn-CM", + "bed": "bed-Latn-ID", + "bee": "bee-Deva-IN", + "bef": "bef-Latn-ZZ", + "beh": "beh-Latn-ZZ", + "bei": "bei-Latn-ID", + "bej": "bej-Arab-SD", + "bek": "bek-Latn-PG", + "bem": "bem-Latn-ZM", + "beo": "beo-Latn-PG", + "bep": "bep-Latn-ID", + "beq": "beq-Latn-CG", + "bes": "bes-Latn-TD", + "bet": "bet-Latn-ZZ", + "beu": "beu-Latn-ID", + "bev": "bev-Latn-CI", + "bew": "bew-Latn-ID", + "bex": "bex-Latn-ZZ", + "bey": "bey-Latn-PG", + "bez": "bez-Latn-TZ", + "bfa": "bfa-Latn-SS", + "bfa-Arab": "bfa-Arab-SS", + "bfb": "bfb-Deva-IN", + "bfc": "bfc-Latn-CN", + "bfd": "bfd-Latn-CM", + "bfe": "bfe-Latn-ID", + "bff": "bff-Latn-CF", + "bfg": "bfg-Latn-ID", + "bfh": "bfh-Latn-PG", + "bfj": "bfj-Latn-CM", + "bfl": "bfl-Latn-CF", + "bfm": "bfm-Latn-CM", + "bfn": "bfn-Latn-TL", + "bfo": "bfo-Latn-BF", + "bfp": "bfp-Latn-CM", + "bfq": "bfq-Taml-IN", + "bfs": "bfs-Latn-CN", + "bfs-Hani": "bfs-Hani-CN", + "bft": "bft-Arab-PK", + "bfu": "bfu-Tibt-IN", + "bfu-Takr": "bfu-Takr-IN", + "bfw": "bfw-Orya-IN", + "bfx": "bfx-Latn-PH", + "bfy": "bfy-Deva-IN", + "bfz": "bfz-Deva-IN", + "bg": "bg-Cyrl-BG", + "bga": "bga-Latn-NG", + "bgb": "bgb-Latn-ID", + "bgc": "bgc-Deva-IN", + "bgd": "bgd-Deva-IN", + "bgf": "bgf-Latn-CM", + "bgg": "bgg-Latn-IN", + "bgi": "bgi-Latn-PH", + "bgj": "bgj-Latn-CM", + "bgn": "bgn-Arab-PK", + "bgo": "bgo-Latn-GN", + "bgp": "bgp-Arab-PK", + "bgq": "bgq-Deva-IN", + "bgr": "bgr-Latn-IN", + "bgs": "bgs-Latn-PH", + "bgt": "bgt-Latn-SB", + "bgu": "bgu-Latn-NG", + "bgv": "bgv-Latn-ID", + "bgw": "bgw-Deva-IN", + "bgx": "bgx-Grek-TR", + "bgy": "bgy-Latn-ID", + "bgz": "bgz-Latn-ID", + "bha": "bha-Deva-IN", + "bhb": "bhb-Deva-IN", + "bhc": "bhc-Latn-ID", + "bhd": "bhd-Deva-IN", + "bhd-Arab": "bhd-Arab-IN", + "bhd-Takr": "bhd-Takr-IN", + "bhe": "bhe-Arab-PK", + "bhf": "bhf-Latn-PG", + "bhg": "bhg-Latn-ZZ", + "bhh": "bhh-Cyrl-IL", + "bhh-Hebr": "bhh-Hebr-IL", + "bhh-Latn": "bhh-Latn-IL", + "bhi": "bhi-Deva-IN", + "bhj": "bhj-Deva-NP", + "bhl": "bhl-Latn-ZZ", + "bhm": "bhm-Arab-OM", + "bhn": "bhn-Syrc-GE", + "bho": "bho-Deva-IN", + "bhp": "bhp-Latn-ID", + "bhq": "bhq-Latn-ID", + "bhr": "bhr-Latn-MG", + "bhs": "bhs-Latn-CM", + "bht": "bht-Takr-IN", + "bht-Deva": "bht-Deva-IN", + "bht-Latn": "bht-Latn-IN", + "bhu": "bhu-Deva-IN", + "bhv": "bhv-Latn-ID", + "bhw": "bhw-Latn-ID", + "bhy": "bhy-Latn-ZZ", + "bhz": "bhz-Latn-ID", + "bi": "bi-Latn-VU", + "bia": "bia-Latn-AU", + "bib": "bib-Latn-ZZ", + "bid": "bid-Latn-TD", + "bie": "bie-Latn-PG", + "bif": "bif-Latn-GW", + "big": "big-Latn-ZZ", + "bik": "bik-Latn-PH", + "bil": "bil-Latn-NG", + "bim": "bim-Latn-ZZ", + "bin": "bin-Latn-NG", + "bio": "bio-Latn-ZZ", + "bip": "bip-Latn-CD", + "biq": "biq-Latn-ZZ", + "bir": "bir-Latn-PG", + "bit": "bit-Latn-PG", + "biu": "biu-Latn-IN", + "biv": "biv-Latn-GH", + "biw": "biw-Latn-CM", + "biy": "biy-Deva-IN", + "biz": "biz-Latn-CD", + "bja": "bja-Latn-CD", + "bjb": "bjb-Latn-AU", + "bjc": "bjc-Latn-PG", + "bjf": "bjf-Syrc-IL", + "bjg": "bjg-Latn-GW", + "bjh": "bjh-Latn-ZZ", + "bji": "bji-Ethi-ZZ", + "bjj": "bjj-Deva-IN", + "bjk": "bjk-Latn-PG", + "bjl": "bjl-Latn-PG", + "bjm": "bjm-Arab-IQ", + "bjn": "bjn-Latn-ID", + "bjo": "bjo-Latn-ZZ", + "bjp": "bjp-Latn-PG", + "bjr": "bjr-Latn-ZZ", + "bjs": "bjs-Latn-BB", + "bjt": "bjt-Latn-SN", + "bju": "bju-Latn-CM", + "bjv": "bjv-Latn-TD", + "bjw": "bjw-Latn-CI", + "bjx": "bjx-Latn-PH", + "bjy": "bjy-Latn-AU", + "bjz": "bjz-Latn-ZZ", + "bka": "bka-Latn-NG", + "bkc": "bkc-Latn-ZZ", + "bkd": "bkd-Latn-PH", + "bkf": "bkf-Latn-CD", + "bkg": "bkg-Latn-CF", + "bkh": "bkh-Latn-CM", + "bki": "bki-Latn-VU", + "bkj": "bkj-Latn-CF", + "bkl": "bkl-Latn-ID", + "bkm": "bkm-Latn-CM", + "bkn": "bkn-Latn-ID", + "bko": "bko-Latn-CM", + "bkp": "bkp-Latn-CD", + "bkq": "bkq-Latn-ZZ", + "bkr": "bkr-Latn-ID", + "bks": "bks-Latn-PH", + "bkt": "bkt-Latn-CD", + "bku": "bku-Latn-PH", + "bkv": "bkv-Latn-ZZ", + "bkw": "bkw-Latn-CG", + "bkx": "bkx-Latn-TL", + "bky": "bky-Latn-NG", + "bkz": "bkz-Latn-ID", + "bla": "bla-Latn-CA", + "blb": "blb-Latn-SB", + "blc": "blc-Latn-CA", + "bld": "bld-Latn-ID", + "ble": "ble-Latn-GW", + "blf": "blf-Latn-ID", + "blg": "iba-Latn-MY", + "blh": "blh-Latn-LR", + "bli": "bli-Latn-CD", + "blj": "blj-Latn-ID", + "blk": "blk-Mymr-MM", + "blm": "blm-Latn-SS", + "bln": "bln-Latn-PH", + "blo": "blo-Latn-BJ", + "blp": "blp-Latn-SB", + "blq": "blq-Latn-PG", + "blr": "blr-Latn-CN", + "blr-Tale": "blr-Tale-CN", + "blr-Thai": "blr-Thai-TH", + "bls": "bls-Latn-ID", + "blt": "blt-Tavt-VN", + "blv": "blv-Latn-AO", + "blw": "blw-Latn-PH", + "blx": "blx-Latn-PH", + "bly": "bly-Latn-BJ", + "blz": "blz-Latn-ID", + "bm": "bm-Latn-ML", + "bma": "bma-Latn-NG", + "bmb": "bmb-Latn-CD", + "bmc": "bmc-Latn-PG", + "bmd": "bmd-Latn-GN", + "bme": "bme-Latn-CF", + "bmf": "bmf-Latn-SL", + "bmg": "bmg-Latn-CD", + "bmh": "bmh-Latn-ZZ", + "bmi": "bmi-Latn-TD", + "bmj": "bmj-Deva-NP", + "bmk": "bmk-Latn-ZZ", + "bml": "bml-Latn-CD", + "bmm": "bmm-Latn-MG", + "bmn": "bmn-Latn-PG", + "bmo": "bmo-Latn-CM", + "bmp": "bmp-Latn-PG", + "bmq": "bmq-Latn-ML", + "bmr": "bmr-Latn-CO", + "bms": "bms-Latn-NE", + "bmu": "bmu-Latn-ZZ", + "bmv": "bmv-Latn-CM", + "bmw": "bmw-Latn-CG", + "bmx": "bmx-Latn-PG", + "bmz": "bmz-Latn-PG", + "bn": "bn-Beng-BD", + "bna": "bna-Latn-ID", + "bnb": "bnb-Latn-MY", + "bnc": "bnc-Latn-PH", + "bnd": "bnd-Latn-ID", + "bne": "bne-Latn-ID", + "bnf": "bnf-Latn-ID", + "bng": "bng-Latn-ZZ", + "bni": "bni-Latn-CD", + "bnj": "bnj-Latn-PH", + "bnk": "bnk-Latn-VU", + "bnm": "bnm-Latn-ZZ", + "bnn": "bnn-Latn-TW", + "bno": "bno-Latn-PH", + "bnp": "bnp-Latn-ZZ", + "bnq": "bnq-Latn-ID", + "bnr": "bnr-Latn-VU", + "bns": "bns-Deva-IN", + "bnu": "bnu-Latn-ID", + "bnv": "bnv-Latn-ID", + "bnw": "bnw-Latn-PG", + "bnx": "bnx-Latn-CD", + "bny": "bny-Latn-MY", + "bnz": "bnz-Latn-CM", + "bo": "bo-Tibt-CN", + "boa": "boa-Latn-PE", + "bob": "bob-Latn-KE", + "boe": "boe-Latn-CM", + "bof": "bof-Latn-BF", + "boh": "boh-Latn-CD", + "boj": "boj-Latn-ZZ", + "bok": "bok-Latn-CG", + "bol": "bol-Latn-NG", + "bom": "bom-Latn-ZZ", + "bon": "bon-Latn-ZZ", + "boo": "boo-Latn-ML", + "bop": "bop-Latn-PG", + "boq": "boq-Latn-PG", + "bor": "bor-Latn-BR", + "bot": "bot-Latn-SS", + "bou": "bou-Latn-TZ", + "bov": "bov-Latn-GH", + "bow": "bow-Latn-PG", + "box": "box-Latn-BF", + "boy": "boy-Latn-CF", + "boz": "boz-Latn-ML", + "boz-Arab": "boz-Arab-ML", + "bpa": "bpa-Latn-VU", + "bpc": "bpc-Latn-CM", + "bpd": "bpd-Latn-CF", + "bpe": "bpe-Latn-PG", + "bpg": "bpg-Latn-ID", + "bph": "bph-Cyrl-RU", + "bpi": "bpi-Latn-PG", + "bpj": "bpj-Latn-CD", + "bpk": "bpk-Latn-NC", + "bpl": "bpl-Latn-AU", + "bpm": "bpm-Latn-PG", + "bpo": "bpo-Latn-ID", + "bpp": "bpp-Latn-ID", + "bpq": "bpq-Latn-ID", + "bpr": "bpr-Latn-PH", + "bps": "bps-Latn-PH", + "bpt": "bpt-Latn-AU", + "bpu": "bpu-Latn-PG", + "bpv": "bpv-Latn-ID", + "bpw": "bpw-Latn-PG", + "bpx": "bpx-Deva-IN", + "bpy": "bpy-Beng-IN", + "bpz": "bpz-Latn-ID", + "bqa": "bqa-Latn-BJ", + "bqb": "bqb-Latn-ID", + "bqc": "bqc-Latn-ZZ", + "bqd": "bqd-Latn-CM", + "bqf": "bqf-Latn-GN", + "bqf-Arab": "bqf-Arab-GN", + "bqg": "bqg-Latn-TG", + "bqi": "bqi-Arab-IR", + "bqj": "bqj-Latn-SN", + "bqk": "bqk-Latn-CF", + "bql": "bql-Latn-PG", + "bqm": "bqm-Latn-CM", + "bqo": "bqo-Latn-CM", + "bqp": "bqp-Latn-ZZ", + "bqq": "bqq-Latn-ID", + "bqr": "bqr-Latn-ID", + "bqs": "bqs-Latn-PG", + "bqt": "bqt-Latn-CM", + "bqu": "bqu-Latn-CD", + "bqv": "bqv-Latn-CI", + "bqw": "bqw-Latn-NG", + "bqx": "bqx-Latn-NG", + "bqz": "bqz-Latn-CM", + "br": "br-Latn-FR", + "bra": "bra-Deva-IN", + "brb": "brb-Khmr-KH", + "brb-Laoo": "brb-Laoo-LA", + "brb-Latn": "brb-Latn-VN", + "brc": "brc-Latn-GY", + "brd": "brd-Deva-NP", + "brf": "brf-Latn-CD", + "brg": "brg-Latn-BO", + "brh": "brh-Arab-PK", + "bri": "bri-Latn-CM", + "brj": "brj-Latn-VU", + "brk": "brk-Arab-SD", + "brl": "brl-Latn-BW", + "brm": "brm-Latn-CD", + "brn": "brn-Latn-CR", + "brp": "brp-Latn-ID", + "brq": "brq-Latn-PG", + "brr": "brr-Latn-SB", + "brs": "brs-Latn-ID", + "brt": "brt-Latn-NG", + "bru": "bru-Latn-VN", + "bru-Laoo": "bru-Laoo-LA", + "bru-Thai": "bru-Thai-LA", + "brv": "brv-Laoo-LA", + "brx": "brx-Deva-IN", + "bry": "bry-Latn-PG", + "brz": "brz-Latn-ZZ", + "bs": "bs-Latn-BA", + "bsa": "bsa-Latn-ID", + "bsb": "bsb-Latn-BN", + "bsc": "bsc-Latn-SN", + "bse": "bse-Latn-CM", + "bsf": "bsf-Latn-NG", + "bsh": "bsh-Arab-AF", + "bsi": "bsi-Latn-CM", + "bsj": "bsj-Latn-ZZ", + "bsk": "bsk-Arab-PK", + "bsk-Latn": "bsk-Latn-PK", + "bsl": "bsl-Latn-NG", + "bsm": "bsm-Latn-ID", + "bsn": "bsn-Latn-CO", + "bso": "bso-Latn-TD", + "bsp": "bsp-Latn-GN", + "bsq": "bsq-Bass-LR", + "bsr": "bsr-Latn-NG", + "bss": "bss-Latn-CM", + "bst": "bst-Ethi-ZZ", + "bsu": "bsu-Latn-ID", + "bsv": "bsv-Latn-GN", + "bsv-Arab": "bsv-Arab-GN", + "bsw": "bsw-Latn-ET", + "bsw-Ethi": "bsw-Ethi-ET", + "bsx": "bsx-Latn-NG", + "bsy": "bsy-Latn-MY", + "bta": "bta-Latn-NG", + "btc": "btc-Latn-CM", + "btd": "btd-Batk-ID", + "bte": "bte-Latn-NG", + "btf": "btf-Latn-TD", + "btg": "btg-Latn-CI", + "bth": "bth-Latn-MY", + "bti": "bti-Latn-ID", + "btj": "btj-Latn-ID", + "btm": "btm-Batk-ID", + "btn": "btn-Latn-PH", + "bto": "bto-Latn-PH", + "btp": "btp-Latn-PG", + "btq": "btq-Latn-MY", + "btr": "btr-Latn-VU", + "bts": "bts-Latn-ID", + "bts-Batk": "bts-Batk-ID", + "btt": "btt-Latn-ZZ", + "btu": "btu-Latn-NG", + "btv": "btv-Deva-PK", + "btw": "btw-Latn-PH", + "btx": "btx-Latn-ID", + "btx-Batk": "btx-Batk-ID", + "bty": "bty-Latn-ID", + "btz": "btz-Latn-ID", + "bua": "bua-Cyrl-RU", + "bub": "bub-Latn-TD", + "buc": "buc-Latn-YT", + "bud": "bud-Latn-ZZ", + "bue": "bue-Latn-CA", + "buf": "buf-Latn-CD", + "bug": "bug-Latn-ID", + "buh": "buh-Latn-CN", + "bui": "bui-Latn-CG", + "buj": "buj-Latn-NG", + "buk": "buk-Latn-ZZ", + "bum": "bum-Latn-CM", + "bun": "bun-Latn-SL", + "buo": "buo-Latn-ZZ", + "bup": "bup-Latn-ID", + "buq": "buq-Latn-PG", + "bus": "bus-Latn-ZZ", + "but": "but-Latn-PG", + "buu": "buu-Latn-ZZ", + "buv": "buv-Latn-PG", + "buw": "buw-Latn-GA", + "bux": "bux-Latn-NG", + "buy": "buy-Latn-SL", + "buz": "buz-Latn-NG", + "bva": "bva-Latn-TD", + "bvb": "bvb-Latn-GQ", + "bvc": "bvc-Latn-SB", + "bvd": "bvd-Latn-SB", + "bve": "bve-Latn-ID", + "bvf": "bvf-Latn-TD", + "bvg": "bvg-Latn-CM", + "bvh": "bvh-Latn-NG", + "bvi": "bvi-Latn-SS", + "bvj": "bvj-Latn-NG", + "bvk": "bvk-Latn-ID", + "bvm": "bvm-Latn-CM", + "bvn": "bvn-Latn-PG", + "bvo": "bvo-Latn-TD", + "bvq": "bvq-Latn-CF", + "bvr": "bvr-Latn-AU", + "bvt": "bvt-Latn-ID", + "bvu": "bvu-Latn-ID", + "bvv": "bvv-Latn-VE", + "bvw": "bvw-Latn-NG", + "bvx": "bvx-Latn-CG", + "bvy": "bvy-Latn-PH", + "bvz": "bvz-Latn-ID", + "bwa": "bwa-Latn-NC", + "bwb": "bwb-Latn-FJ", + "bwc": "bwc-Latn-ZM", + "bwd": "bwd-Latn-ZZ", + "bwe": "bwe-Mymr-MM", + "bwe-Latn": "bwe-Latn-MM", + "bwf": "bwf-Latn-PG", + "bwg": "bwg-Latn-MZ", + "bwh": "bwh-Latn-CM", + "bwi": "bwi-Latn-VE", + "bwj": "bwj-Latn-BF", + "bwk": "bwk-Latn-PG", + "bwl": "bwl-Latn-CD", + "bwm": "bwm-Latn-PG", + "bwo": "bwo-Latn-ET", + "bwo-Ethi": "bwo-Ethi-ET", + "bwp": "bwp-Latn-ID", + "bwq": "bwq-Latn-BF", + "bwr": "bwr-Latn-ZZ", + "bws": "bws-Latn-CD", + "bwt": "bwt-Latn-CM", + "bwu": "bwu-Latn-GH", + "bww": "bww-Latn-CD", + "bwx": "bwx-Latn-CN", + "bwy": "bwy-Latn-BF", + "bwz": "bwz-Latn-CG", + "bxa": "bxa-Latn-SB", + "bxb": "bxb-Latn-SS", + "bxc": "bxc-Latn-GQ", + "bxf": "bxf-Latn-PG", + "bxg": "bxg-Latn-CD", + "bxh": "bxh-Latn-ZZ", + "bxi": "bxi-Latn-AU", + "bxj": "bxj-Latn-AU", + "bxl": "bxl-Latn-BF", + "bxm": "bxm-Cyrl-MN", + "bxm-Latn": "bxm-Latn-MN", + "bxm-Mong": "bxm-Mong-MN", + "bxn": "bxn-Latn-AU", + "bxo": "bxo-Latn-NG", + "bxp": "bxp-Latn-CM", + "bxq": "bxq-Latn-NG", + "bxs": "bxs-Latn-CM", + "bxu": "bxu-Mong-CN", + "bxu-Cyrl": "bxu-Cyrl-CN", + "bxu-Latn": "bxu-Latn-CN", + "bxv": "bxv-Latn-TD", + "bxw": "bxw-Latn-ML", + "bxz": "bxz-Latn-PG", + "bya": "bya-Latn-PH", + "byb": "byb-Latn-CM", + "byc": "byc-Latn-NG", + "byd": "byd-Latn-ID", + "bye": "bye-Latn-ZZ", + "byf": "byf-Latn-NG", + "byh": "byh-Deva-NP", + "byi": "byi-Latn-CD", + "byj": "byj-Latn-NG", + "byk": "byk-Latn-CN", + "byl": "byl-Latn-ID", + "bym": "bym-Latn-AU", + "byn": "byn-Ethi-ER", + "byp": "byp-Latn-NG", + "byr": "byr-Latn-ZZ", + "bys": "bys-Latn-ZZ", + "byv": "byv-Latn-CM", + "byw": "byw-Deva-NP", + "byx": "byx-Latn-ZZ", + "byz": "byz-Latn-PG", + "bza": "bza-Latn-ZZ", + "bzb": "bzb-Latn-ID", + "bzc": "bzc-Latn-MG", + "bzd": "bzd-Latn-CR", + "bze": "bze-Latn-ML", + "bzf": "bzf-Latn-ZZ", + "bzh": "bzh-Latn-ZZ", + "bzi": "bzi-Thai-TH", + "bzj": "bzj-Latn-BZ", + "bzk": "bzk-Latn-NI", + "bzl": "bzl-Latn-ID", + "bzm": "bzm-Latn-CD", + "bzn": "bzn-Latn-ID", + "bzo": "bzo-Latn-CD", + "bzp": "bzp-Latn-ID", + "bzq": "bzq-Latn-ID", + "bzr": "bzr-Latn-AU", + "bzt": "bzt-Latn-001", + "bzu": "bzu-Latn-ID", + "bzv": "bzv-Latn-CM", + "bzw": "bzw-Latn-ZZ", + "bzx": "bzx-Latn-ML", + "bzy": "bzy-Latn-NG", + "bzz": "bzz-Latn-NG", + "ca": "ca-Latn-ES", + "caa": "caa-Latn-GT", + "cab": "cab-Latn-HN", + "cac": "cac-Latn-GT", + "cad": "cad-Latn-US", + "cae": "cae-Latn-SN", + "caf": "caf-Latn-CA", + "caf-Cans": "caf-Cans-CA", + "cag": "cag-Latn-PY", + "cah": "cah-Latn-PE", + "caj": "caj-Latn-AR", + "cak": "cak-Latn-GT", + "cal": "cal-Latn-MP", + "cam": "cam-Latn-NC", + "can": "can-Latn-ZZ", + "cao": "cao-Latn-BO", + "cap": "cap-Latn-BO", + "caq": "caq-Latn-IN", + "car": "car-Latn-VE", + "cas": "cas-Latn-BO", + "cav": "cav-Latn-BO", + "caw": "caw-Latn-BO", + "cax": "cax-Latn-BO", + "cay": "cay-Latn-CA", + "caz": "caz-Latn-BO", + "cbb": "cbb-Latn-CO", + "cbc": "cbc-Latn-CO", + "cbd": "cbd-Latn-CO", + "cbg": "cbg-Latn-CO", + "cbi": "cbi-Latn-EC", + "cbj": "cbj-Latn-ZZ", + "cbk": "cbk-Latn-PH", + "cbk-Brai": "cbk-Brai-PH", + "cbl": "cbl-Latn-MM", + "cbn": "cbn-Thai-TH", + "cbo": "cbo-Latn-NG", + "cbq": "cbq-Latn-NG", + "cbr": "cbr-Latn-PE", + "cbs": "cbs-Latn-PE", + "cbt": "cbt-Latn-PE", + "cbu": "cbu-Latn-PE", + "cbv": "cbv-Latn-CO", + "cbw": "cbw-Latn-PH", + "cby": "cby-Latn-CO", + "ccc": "ccc-Latn-PE", + "ccd": "ccd-Latn-BR", + "cce": "cce-Latn-MZ", + "ccg": "ccg-Latn-NG", + "cch": "cch-Latn-NG", + "ccj": "ccj-Latn-GW", + "ccl": "ccl-Latn-TZ", + "ccm": "ccm-Latn-MY", + "cco": "cco-Latn-MX", + "ccp": "ccp-Cakm-BD", + "ccr": "ccr-Latn-SV", + "cde": "cde-Telu-IN", + "cdf": "cdf-Latn-IN", + "cdf-Beng": "cdf-Beng-IN", + "cdh": "cdh-Deva-IN", + "cdh-Takr": "cdh-Takr-IN", + "cdi": "cdi-Gujr-IN", + "cdj": "cdj-Deva-IN", + "cdm": "cdm-Deva-NP", + "cdm-Latn": "cdm-Latn-NP", + "cdo": "cdo-Hans-CN", + "cdo-Hant": "cdo-Hant-CN", + "cdo-Latn": "cdo-Latn-CN", + "cdr": "cdr-Latn-NG", + "cdz": "cdz-Beng-IN", + "ce": "ce-Cyrl-RU", + "cea": "cea-Latn-US", + "ceb": "ceb-Latn-PH", + "ceg": "ceg-Latn-PY", + "cek": "cek-Latn-MM", + "cen": "cen-Latn-NG", + "cet": "cet-Latn-NG", + "cey": "cey-Latn-MM", + "cfa": "cfa-Latn-ZZ", + "cfd": "cfd-Latn-NG", + "cfg": "cfg-Latn-NG", + "cfm": "cfm-Latn-MM", + "cfm-Beng": "cfm-Beng-IN", + "cga": "cga-Latn-PG", + "cgc": "cgc-Latn-PH", + "cgg": "cgg-Latn-UG", + "cgk": "cgk-Tibt-BT", + "ch": "ch-Latn-GU", + "chb": "chb-Latn-CO", + "chd": "chd-Latn-MX", + "chf": "chf-Latn-MX", + "chg": "chg-Arab-TM", + "chh": "chh-Latn-US", + "chj": "chj-Latn-MX", + "chk": "chk-Latn-FM", + "chl": "chl-Latn-US", + "chm": "chm-Cyrl-RU", + "chn": "chn-Latn-US", + "chn-Dupl": "chn-Dupl-US", + "cho": "cho-Latn-US", + "chp": "chp-Latn-CA", + "chq": "chq-Latn-MX", + "chr": "chr-Cher-US", + "cht": "cht-Latn-PE", + "chw": "chw-Latn-MZ", + "chx": "chx-Deva-NP", + "chy": "chy-Latn-US", + "chz": "chz-Latn-MX", + "cia": "cia-Latn-ID", + "cia-Arab": "cia-Arab-ID", + "cia-Hang": "cia-Hang-ID", + "cib": "cib-Latn-BJ", + "cic": "cic-Latn-US", + "cie": "cie-Latn-NG", + "cih": "cih-Deva-IN", + "cim": "cim-Latn-IT", + "cin": "cin-Latn-BR", + "cip": "cip-Latn-MX", + "cir": "cir-Latn-NC", + "ciw": "ciw-Latn-US", + "ciw-Cans": "ciw-Cans-US", + "ciy": "ciy-Latn-VE", + "cja": "cja-Arab-KH", + "cje": "cje-Latn-VN", + "cjh": "cjh-Latn-US", + "cji": "cji-Cyrl-RU", + "cjk": "cjk-Latn-AO", + "cjm": "cjm-Cham-VN", + "cjn": "cjn-Latn-PG", + "cjo": "cjo-Latn-PE", + "cjp": "cjp-Latn-CR", + "cjs": "cjs-Latn-RU", + "cjs-Cyrl": "cjs-Cyrl-RU", + "cjv": "cjv-Latn-ZZ", + "cjy": "cjy-Hans-CN", + "cjy-Hant": "cjy-Hant-CN", + "ckb": "ckb-Arab-IQ", + "ckl": "ckl-Latn-ZZ", + "ckm": "ckm-Latn-HR", + "ckm-Glag": "ckm-Glag-HR", + "ckn": "ckn-Latn-MM", + "cko": "cko-Latn-ZZ", + "ckq": "ckq-Latn-TD", + "ckr": "ckr-Latn-PG", + "cks": "cks-Latn-NC", + "ckt": "ckt-Cyrl-RU", + "cku": "cku-Latn-US", + "ckv": "ckv-Latn-TW", + "ckx": "ckx-Latn-CM", + "cky": "cky-Latn-ZZ", + "ckz": "ckz-Latn-GT", + "cla": "cla-Latn-ZZ", + "clc": "clc-Latn-CA", + "cle": "cle-Latn-MX", + "clh": "clh-Arab-PK", + "cli": "cli-Latn-GH", + "clj": "clj-Latn-MM", + "clk": "clk-Latn-IN", + "clk-Tibt": "clk-Tibt-CN", + "cll": "cll-Latn-GH", + "clm": "clm-Latn-US", + "clo": "clo-Latn-MX", + "clt": "clt-Latn-MM", + "clu": "clu-Latn-PH", + "clw": "clw-Cyrl-RU", + "cly": "cly-Latn-MX", + "cma": "cma-Latn-VN", + "cme": "cme-Latn-ZZ", + "cmg": "cmg-Soyo-MN", + "cmi": "cmi-Latn-CO", + "cml": "cml-Latn-ID", + "cmo": "cmo-Latn-VN", + "cmo-KH": "cmo-Latn-KH", + "cmo-Khmr": "cmo-Khmr-KH", + "cmr": "cmr-Latn-MM", + "cms": "cms-Latn-IT", + "cmt": "cmt-Latn-ZA", + "cna": "cna-Tibt-IN", + "cnb": "cnb-Latn-MM", + "cnc": "cnc-Latn-VN", + "cng": "cng-Latn-CN", + "cnh": "cnh-Latn-MM", + "cni": "cni-Latn-PE", + "cnk": "cnk-Latn-MM", + "cnl": "cnl-Latn-MX", + "cnp": "cnp-Hans-CN", + "cnp-Hant": "cnp-Hant-CN", + "cnq": "cnq-Latn-CM", + "cns": "cns-Latn-ID", + "cnt": "cnt-Latn-MX", + "cnw": "cnw-Latn-MM", + "cnx": "cnx-Latn-GB", + "co": "co-Latn-FR", + "coa": "coa-Latn-AU", + "cob": "cob-Latn-MX", + "coc": "coc-Latn-MX", + "cod": "cod-Latn-PE", + "coe": "coe-Latn-CO", + "cof": "cof-Latn-EC", + "cog": "cog-Thai-TH", + "coh": "coh-Latn-KE", + "coj": "coj-Latn-MX", + "cok": "cok-Latn-MX", + "col": "col-Latn-US", + "com": "com-Latn-US", + "coo": "coo-Latn-CA", + "cop": "cop-Copt-EG", + "coq": "coq-Latn-US", + "cot": "cot-Latn-PE", + "cou": "cou-Latn-SN", + "cox": "cox-Latn-PE", + "coz": "coz-Latn-MX", + "cpa": "cpa-Latn-MX", + "cpb": "cpb-Latn-PE", + "cpc": "cpc-Latn-PE", + "cpg": "cpg-Grek-GR", + "cpi": "cpi-Latn-NR", + "cpn": "cpn-Latn-GH", + "cpo": "cpo-Latn-BF", + "cps": "cps-Latn-PH", + "cpu": "cpu-Latn-PE", + "cpx": "cpx-Latn-CN", + "cpy": "cpy-Latn-PE", + "cqd": "cqd-Latn-CN", + "cr": "cr-Cans-CA", + "crb": "crb-Latn-VC", + "crc": "crc-Latn-VU", + "crd": "crd-Latn-US", + "crf": "crf-Latn-CO", + "crg": "crg-Latn-CA", + "crh": "crh-Cyrl-UA", + "cri": "cri-Latn-ST", + "crj": "crj-Cans-CA", + "crj-Latn": "crj-Latn-CA", + "crk": "crk-Cans-CA", + "crl": "crl-Cans-CA", + "crm": "crm-Cans-CA", + "crn": "crn-Latn-MX", + "cro": "cro-Latn-US", + "crq": "crq-Latn-AR", + "crs": "crs-Latn-SC", + "crt": "crt-Latn-AR", + "crv": "crv-Latn-IN", + "crw": "crw-Latn-VN", + "crx": "crx-Latn-CA", + "crx-Cans": "crx-Cans-CA", + "cry": "cry-Latn-NG", + "crz": "crz-Latn-US", + "cs": "cs-Latn-CZ", + "csa": "csa-Latn-MX", + "csb": "csb-Latn-PL", + "csh": "csh-Mymr-MM", + "csh-Latn": "csh-Latn-MM", + "csj": "csj-Latn-MM", + "csk": "csk-Latn-SN", + "csm": "csm-Latn-US", + "cso": "cso-Latn-MX", + "csp": "csp-Hans-CN", + "csp-Hant": "csp-Hant-CN", + "css": "css-Latn-US", + "cst": "cst-Latn-US", + "csv": "csv-Latn-MM", + "csw": "csw-Cans-CA", + "csy": "csy-Latn-MM", + "csz": "csz-Latn-US", + "cta": "cta-Latn-MX", + "ctc": "ctc-Latn-US", + "ctd": "ctd-Pauc-MM", + "cte": "cte-Latn-MX", + "ctg": "ctg-Beng-BD", + "ctg-Arab": "ctg-Arab-BD", + "ctg-Latn": "ctg-Latn-BD", + "cth": "cth-Latn-MM", + "ctl": "ctl-Latn-MX", + "ctm": "ctm-Latn-US", + "ctn": "ctn-Deva-NP", + "cto": "cto-Latn-CO", + "ctp": "ctp-Latn-MX", + "cts": "cts-Latn-PH", + "ctt": "ctt-Taml-IN", + "ctu": "ctu-Latn-MX", + "ctz": "ctz-Latn-MX", + "cu": "cu-Cyrl-RU", + "cu-Glag": "cu-Glag-BG", + "cua": "cua-Latn-VN", + "cub": "cub-Latn-CO", + "cuc": "cuc-Latn-MX", + "cuh": "cuh-Latn-KE", + "cui": "cui-Latn-CO", + "cuj": "cuj-Latn-PE", + "cuk": "cuk-Latn-PA", + "cul": "cul-Latn-BR", + "cuo": "cuo-Latn-VE", + "cup": "cup-Latn-US", + "cut": "cut-Latn-MX", + "cuu": "cuu-Lana-CN", + "cuv": "cuv-Latn-CM", + "cux": "cux-Latn-MX", + "cv": "cv-Cyrl-RU", + "cvg": "cvg-Latn-IN", + "cvg-Tibt": "cvg-Tibt-IN", + "cvn": "cvn-Latn-MX", + "cwa": "cwa-Latn-TZ", + "cwb": "cwb-Latn-MZ", + "cwe": "cwe-Latn-TZ", + "cwg": "cwg-Latn-MY", + "cwt": "cwt-Latn-SN", + "cy": "cy-Latn-GB", + "cya": "cya-Latn-MX", + "cyb": "cyb-Latn-BO", + "cyo": "cyo-Latn-PH", + "czh": "czh-Hans-CN", + "czh-Hant": "czh-Hant-CN", + "czk": "czk-Hebr-CZ", + "czn": "czn-Latn-MX", + "czt": "czt-Latn-MM", + "da": "da-Latn-DK", + "daa": "daa-Latn-TD", + "dac": "dac-Latn-PG", + "dad": "dad-Latn-ZZ", + "dae": "dae-Latn-CM", + "daf": "dnj-Latn-CI", + "dag": "dag-Latn-ZZ", + "dah": "dah-Latn-ZZ", + "dai": "dai-Latn-TD", + "daj": "daj-Latn-SD", + "dak": "dak-Latn-US", + "dal": "dal-Latn-KE", + "dam": "dam-Latn-NG", + "dao": "dao-Latn-MM", + "daq": "daq-Deva-IN", + "dar": "dar-Cyrl-RU", + "das": "das-Latn-CI", + "dau": "dau-Latn-TD", + "dav": "dav-Latn-KE", + "daw": "daw-Latn-PH", + "dax": "dax-Latn-AU", + "daz": "daz-Latn-ID", + "dba": "dba-Latn-ML", + "dbb": "dbb-Latn-NG", + "dbd": "dbd-Latn-ZZ", + "dbe": "dbe-Latn-ID", + "dbf": "dbf-Latn-ID", + "dbg": "dbg-Latn-ML", + "dbi": "dbi-Latn-NG", + "dbj": "dbj-Latn-MY", + "dbj-Arab": "dbj-Arab-MY", + "dbl": "dbl-Latn-AU", + "dbm": "dbm-Latn-NG", + "dbn": "dbn-Latn-ID", + "dbo": "dbo-Latn-NG", + "dbp": "dbp-Latn-NG", + "dbq": "dbq-Latn-ZZ", + "dbt": "dbt-Latn-ML", + "dbu": "dbu-Latn-ML", + "dbv": "dbv-Latn-NG", + "dbw": "dbw-Latn-ML", + "dby": "dby-Latn-PG", + "dcc": "dcc-Arab-IN", + "dcr": "dcr-Latn-VI", + "dda": "dda-Latn-AU", + "ddd": "ddd-Latn-SS", + "dde": "dde-Latn-CG", + "ddg": "ddg-Latn-TL", + "ddi": "ddi-Latn-PG", + "ddj": "ddj-Latn-AU", + "ddn": "ddn-Latn-ZZ", + "ddo": "ddo-Cyrl-RU", + "ddr": "ddr-Latn-AU", + "dds": "dds-Latn-ML", + "ddw": "ddw-Latn-ID", + "de": "de-Latn-DE", + "dec": "dec-Latn-SD", + "ded": "ded-Latn-ZZ", + "dee": "dee-Latn-LR", + "def": "def-Arab-IR", + "deg": "deg-Latn-NG", + "deh": "deh-Arab-PK", + "dei": "dei-Latn-ID", + "dek": "dek-Latn-CM", + "del": "del-Latn-US", + "dem": "dem-Latn-ID", + "den": "den-Latn-CA", + "deq": "deq-Latn-CF", + "der": "der-Beng-IN", + "der-Latn": "der-Latn-IN", + "des": "des-Latn-BR", + "dev": "dev-Latn-PG", + "dez": "dez-Latn-CD", + "dga": "dga-Latn-ZZ", + "dgb": "dgb-Latn-ML", + "dgc": "dgc-Latn-PH", + "dgd": "dgd-Latn-BF", + "dge": "dge-Latn-PG", + "dgg": "dgg-Latn-PG", + "dgh": "dgh-Latn-ZZ", + "dgi": "dgi-Latn-ZZ", + "dgk": "dgk-Latn-CF", + "dgl": "dgl-Arab-ZZ", + "dgn": "dgn-Latn-AU", + "dgr": "dgr-Latn-CA", + "dgs": "dgs-Latn-BF", + "dgt": "dgt-Latn-AU", + "dgw": "dgw-Latn-AU", + "dgx": "dgx-Latn-PG", + "dgz": "dgz-Latn-ZZ", + "dhg": "dhg-Latn-AU", + "dhi": "dhi-Deva-NP", + "dhl": "dhl-Latn-AU", + "dhm": "dhm-Latn-AO", + "dhn": "dhn-Gujr-IN", + "dho": "dho-Deva-IN", + "dhr": "dhr-Latn-AU", + "dhs": "dhs-Latn-TZ", + "dhu": "dhu-Latn-AU", + "dhv": "dhv-Latn-NC", + "dhw": "dhw-Deva-NP", + "dhx": "dhx-Latn-AU", + "dia": "dia-Latn-ZZ", + "dib": "dib-Latn-SS", + "dic": "dic-Latn-CI", + "did": "did-Latn-SS", + "dif": "dif-Latn-AU", + "dig": "dig-Latn-KE", + "dih": "dih-Latn-MX", + "dii": "dii-Latn-CM", + "dij": "dij-Latn-ID", + "dil": "dil-Latn-SD", + "din": "din-Latn-SS", + "din-Arab": "din-Arab-SS", + "dio": "dio-Latn-NG", + "dip": "dip-Latn-SS", + "dir": "dir-Latn-NG", + "dis": "dis-Latn-IN", + "dis-Beng": "dis-Beng-IN", + "diu": "diu-Latn-NA", + "diw": "diw-Latn-SS", + "dix": "dix-Latn-VU", + "diy": "diy-Latn-ID", + "diz": "diz-Latn-CD", + "dja": "dja-Latn-AU", + "djb": "djb-Latn-AU", + "djc": "djc-Latn-TD", + "djd": "djd-Latn-AU", + "dje": "dje-Latn-NE", + "djf": "djf-Latn-AU", + "dji": "dji-Latn-AU", + "djj": "djj-Latn-AU", + "djk": "djk-Latn-SR", + "djm": "djm-Latn-ML", + "djn": "djn-Latn-AU", + "djo": "djo-Latn-ID", + "djr": "djr-Latn-AU", + "dju": "dju-Latn-PG", + "djw": "djw-Latn-AU", + "dka": "dka-Tibt-BT", + "dkg": "dkg-Latn-NG", + "dkk": "dkk-Latn-ID", + "dkr": "dkr-Latn-MY", + "dks": "dks-Latn-SS", + "dkx": "dkx-Latn-CM", + "dlg": "dlg-Cyrl-RU", + "dlm": "dlm-Latn-HR", + "dln": "dln-Latn-IN", + "dma": "dma-Latn-GA", + "dmb": "dmb-Latn-ML", + "dmc": "dmc-Latn-PG", + "dmd": "dmd-Latn-AU", + "dme": "dme-Latn-CM", + "dmf": "dmf-Medf-NG", + "dmg": "dmg-Latn-MY", + "dmk": "dmk-Arab-PK", + "dml": "dml-Arab-PK", + "dmm": "dmm-Latn-CM", + "dmo": "dmo-Latn-CM", + "dmr": "dmr-Latn-ID", + "dms": "dms-Latn-ID", + "dmu": "dmu-Latn-ID", + "dmv": "dmv-Latn-MY", + "dmw": "dmw-Latn-AU", + "dmx": "dmx-Latn-MZ", + "dmy": "dmy-Latn-ID", + "dna": "dna-Latn-ID", + "dnd": "dnd-Latn-PG", + "dne": "dne-Latn-TZ", + "dng": "dng-Cyrl-KG", + "dng-Arab": "dng-Arab-KG", + "dni": "dni-Latn-ID", + "dnj": "dnj-Latn-CI", + "dnk": "dnk-Latn-ID", + "dnn": "dnn-Latn-BF", + "dno": "dno-Latn-CD", + "dnr": "dnr-Latn-PG", + "dnt": "dnt-Latn-ID", + "dnu": "dnu-Mymr-MM", + "dnv": "dnv-Mymr-MM", + "dnw": "dnw-Latn-ID", + "dny": "dny-Latn-BR", + "doa": "doa-Latn-PG", + "dob": "dob-Latn-ZZ", + "doc": "doc-Latn-CN", + "doe": "doe-Latn-TZ", + "dof": "dof-Latn-PG", + "doh": "doh-Latn-NG", + "doi": "doi-Deva-IN", + "dok": "dok-Latn-ID", + "dol": "dol-Latn-PG", + "don": "don-Latn-PG", + "doo": "doo-Latn-CD", + "dop": "dop-Latn-ZZ", + "dor": "dor-Latn-SB", + "dos": "dos-Latn-BF", + "dot": "dot-Latn-NG", + "dov": "dov-Latn-ZW", + "dow": "dow-Latn-ZZ", + "dox": "dox-Ethi-ET", + "doy": "doy-Latn-GH", + "dpp": "dpp-Latn-MY", + "drc": "drc-Latn-PT", + "dre": "dre-Tibt-NP", + "drg": "drg-Latn-MY", + "drh": "mn-Cyrl-MN", + "dri": "dri-Latn-ZZ", + "drl": "drl-Latn-AU", + "drn": "drn-Latn-ID", + "dro": "dro-Latn-MY", + "drq": "drq-Deva-NP", + "drs": "drs-Ethi-ZZ", + "drt": "drt-Latn-NL", + "dru": "dru-Latn-TW", + "dry": "dry-Deva-NP", + "dsb": "dsb-Latn-DE", + "dsh": "dsh-Latn-KE", + "dsi": "dsi-Latn-TD", + "dsn": "dsn-Latn-ID", + "dso": "dso-Orya-IN", + "dsq": "dsq-Latn-ML", + "dsq-Arab": "dsq-Arab-ML", + "dta": "dta-Latn-CN", + "dta-Cyrl": "dta-Cyrl-CN", + "dta-Hans": "dta-Hans-CN", + "dtb": "dtb-Latn-MY", + "dtd": "dtd-Latn-CA", + "dth": "dth-Latn-AU", + "dti": "dti-Latn-ML", + "dtk": "dtk-Latn-ML", + "dtm": "dtm-Latn-ML", + "dto": "dto-Latn-ML", + "dtp": "dtp-Latn-MY", + "dtr": "dtr-Latn-MY", + "dts": "dts-Latn-ZZ", + "dtt": "dtt-Latn-ML", + "dtu": "dtu-Latn-ML", + "dty": "dty-Deva-NP", + "dua": "dua-Latn-CM", + "dub": "dub-Gujr-IN", + "duc": "duc-Latn-ZZ", + "dud": "uth-Latn-ZZ", + "due": "due-Latn-PH", + "duf": "duf-Latn-NC", + "dug": "dug-Latn-ZZ", + "duh": "duh-Deva-IN", + "duh-Gujr": "duh-Gujr-IN", + "dui": "dui-Latn-PG", + "duk": "duk-Latn-PG", + "dul": "dul-Latn-PH", + "dum": "dum-Latn-NL", + "dun": "dun-Latn-ID", + "duo": "duo-Latn-PH", + "dup": "dup-Latn-ID", + "duq": "duq-Latn-ID", + "dur": "dur-Latn-CM", + "dus": "dus-Deva-NP", + "duu": "duu-Latn-CN", + "duv": "duv-Latn-ID", + "duw": "duw-Latn-ID", + "dux": "dux-Latn-ML", + "duy": "duy-Latn-PH", + "duz": "duz-Latn-CM", + "dv": "dv-Thaa-MV", + "dva": "dva-Latn-ZZ", + "dwa": "dwa-Latn-NG", + "dwk": "dwk-Orya-IN", + "dwr": "dwr-Latn-ET", + "dwr-Ethi": "dwr-Ethi-ET", + "dws": "dws-Latn-001", + "dwu": "dwu-Latn-AU", + "dww": "dww-Latn-ZZ", + "dwy": "dwy-Latn-AU", + "dwz": "dwz-Deva-NP", + "dya": "dya-Latn-BF", + "dyb": "dyb-Latn-AU", + "dyd": "dyd-Latn-AU", + "dyg": "dyg-Latn-PH", + "dyi": "dyi-Latn-CI", + "dym": "dym-Latn-ML", + "dyn": "dyn-Latn-AU", + "dyo": "dyo-Latn-SN", + "dyu": "dyu-Latn-BF", + "dyy": "dyy-Latn-AU", + "dz": "dz-Tibt-BT", + "dza": "dza-Latn-NG", + "dze": "dze-Latn-AU", + "dzg": "dzg-Latn-ZZ", + "dzl": "dzl-Tibt-BT", + "dzn": "dzn-Latn-CD", + "eaa": "eaa-Latn-AU", + "ebc": "ebc-Latn-ID", + "ebg": "ebg-Latn-NG", + "ebk": "ebk-Latn-PH", + "ebo": "ebo-Latn-CG", + "ebr": "ebr-Latn-CI", + "ebu": "ebu-Latn-KE", + "ecr": "ecr-Grek-GR", + "ecy": "ecy-Cprt-CY", + "ee": "ee-Latn-GH", + "efa": "efa-Latn-NG", + "efe": "efe-Latn-CD", + "efi": "efi-Latn-NG", + "ega": "ega-Latn-CI", + "egl": "egl-Latn-IT", + "egm": "egm-Latn-TZ", + "ego": "ego-Latn-NG", + "egy": "egy-Egyp-EG", + "ehu": "ehu-Latn-NG", + "eip": "eip-Latn-ID", + "eit": "eit-Latn-PG", + "eiv": "eiv-Latn-PG", + "eja": "eja-Latn-GW", + "eka": "eka-Latn-ZZ", + "eke": "eke-Latn-NG", + "ekg": "ekg-Latn-ID", + "eki": "eki-Latn-NG", + "ekl": "ekl-Latn-BD", + "ekm": "ekm-Latn-CM", + "eko": "eko-Latn-MZ", + "eko-Arab": "eko-Arab-MZ", + "ekp": "ekp-Latn-NG", + "ekr": "ekr-Latn-NG", + "eky": "eky-Kali-MM", + "el": "el-Grek-GR", + "ele": "ele-Latn-PG", + "elk": "elk-Latn-PG", + "elm": "elm-Latn-NG", + "elo": "elo-Latn-KE", + "elu": "elu-Latn-PG", + "ema": "ema-Latn-ZZ", + "emb": "emb-Latn-ID", + "eme": "eme-Latn-GF", + "emg": "emg-Deva-NP", + "emi": "emi-Latn-ZZ", + "emm": "emm-Latn-MX", + "emn": "emn-Latn-CM", + "emp": "emp-Latn-PA", + "ems": "ems-Latn-US", + "ems-Cyrl": "ems-Cyrl-US", + "emu": "emu-Deva-IN", + "emw": "emw-Latn-ID", + "emx": "emx-Latn-FR", + "emz": "emz-Latn-CM", + "en": "en-Latn-US", + "en-Shaw": "en-Shaw-GB", + "ena": "ena-Latn-PG", + "enb": "enb-Latn-KE", + "enc": "enc-Latn-VN", + "end": "end-Latn-ID", + "enf": "enf-Cyrl-RU", + "enh": "enh-Cyrl-RU", + "enl": "enl-Latn-PY", + "enm": "enm-Latn-GB", + "enn": "enn-Latn-ZZ", + "eno": "eno-Latn-ID", + "enq": "enq-Latn-ZZ", + "enr": "enr-Latn-ID", + "env": "env-Latn-NG", + "enw": "enw-Latn-NG", + "enx": "enx-Latn-PY", + "eo": "eo-Latn-001", + "eot": "eot-Latn-CI", + "epi": "epi-Latn-NG", + "era": "era-Taml-IN", + "erg": "erg-Latn-VU", + "erh": "erh-Latn-NG", + "eri": "eri-Latn-ZZ", + "erk": "erk-Latn-VU", + "err": "err-Latn-AU", + "ert": "ert-Latn-ID", + "erw": "erw-Latn-ID", + "es": "es-Latn-ES", + "ese": "ese-Latn-BO", + "esg": "esg-Gonm-IN", + "esh": "esh-Arab-IR", + "esi": "esi-Latn-US", + "esm": "esm-Latn-CI", + "ess": "ess-Latn-US", + "ess-Cyrl": "ess-Cyrl-US", + "esu": "esu-Latn-US", + "esy": "esy-Latn-PH", + "et": "et-Latn-EE", + "etb": "etb-Latn-NG", + "etn": "etn-Latn-VU", + "eto": "eto-Latn-CM", + "etr": "etr-Latn-ZZ", + "ets": "ets-Latn-NG", + "ett": "ett-Ital-IT", + "etu": "etu-Latn-ZZ", + "etx": "etx-Latn-ZZ", + "etz": "etz-Latn-ID", + "eu": "eu-Latn-ES", + "eve": "eve-Cyrl-RU", + "evh": "evh-Latn-NG", + "evn": "evn-Cyrl-RU", + "evn-Latn": "evn-Latn-CN", + "evn-Mong": "evn-Mong-CN", + "ewo": "ewo-Latn-CM", + "ext": "ext-Latn-ES", + "eya": "eya-Latn-US", + "eyo": "eyo-Latn-KE", + "eza": "eza-Latn-ZZ", + "eze": "eze-Latn-NG", + "fa": "fa-Arab-IR", + "faa": "faa-Latn-ZZ", + "fab": "fab-Latn-ZZ", + "fad": "fad-Latn-PG", + "faf": "faf-Latn-SB", + "fag": "fag-Latn-ZZ", + "fah": "fah-Latn-NG", + "fai": "fai-Latn-ZZ", + "faj": "faj-Latn-PG", + "fak": "fak-Latn-CM", + "fal": "fal-Latn-CM", + "fam": "fam-Latn-NG", + "fan": "fan-Latn-GQ", + "fap": "fap-Latn-SN", + "far": "far-Latn-SB", + "fau": "fau-Latn-ID", + "fax": "fax-Latn-ES", + "fay": "fay-Arab-IR", + "faz": "faz-Arab-IR", + "fbl": "fbl-Latn-PH", + "fer": "fer-Latn-SS", + "ff": "ff-Latn-SN", + "ff-Adlm": "ff-Adlm-GN", + "ffi": "ffi-Latn-ZZ", + "ffm": "ffm-Latn-ML", + "fgr": "fgr-Latn-TD", + "fi": "fi-Latn-FI", + "fia": "fia-Arab-SD", + "fie": "fie-Latn-NG", + "fif": "fif-Latn-SA", + "fil": "fil-Latn-PH", + "fip": "fip-Latn-TZ", + "fir": "fir-Latn-NG", + "fit": "fit-Latn-SE", + "fiw": "fiw-Latn-PG", + "fj": "fj-Latn-FJ", + "fkk": "fkk-Latn-NG", + "fkv": "fkv-Latn-NO", + "fla": "fla-Latn-US", + "flh": "flh-Latn-ID", + "fli": "fli-Latn-NG", + "fll": "fll-Latn-CM", + "fln": "fln-Latn-AU", + "flr": "flr-Latn-ZZ", + "fly": "fly-Latn-ZA", + "fmp": "fmp-Latn-ZZ", + "fmu": "fmu-Deva-IN", + "fnb": "fnb-Latn-VU", + "fng": "fng-Latn-ZA", + "fni": "fni-Latn-TD", + "fo": "fo-Latn-FO", + "fod": "fod-Latn-ZZ", + "foi": "foi-Latn-PG", + "fom": "fom-Latn-CD", + "fon": "fon-Latn-BJ", + "for": "for-Latn-ZZ", + "fos": "fos-Latn-TW", + "fpe": "fpe-Latn-ZZ", + "fqs": "fqs-Latn-ZZ", + "fr": "fr-Latn-FR", + "frc": "frc-Latn-US", + "frd": "frd-Latn-ID", + "frk": "frk-Latn-DE", + "frm": "frm-Latn-FR", + "fro": "fro-Latn-FR", + "frp": "frp-Latn-FR", + "frq": "frq-Latn-PG", + "frr": "frr-Latn-DE", + "frs": "frs-Latn-DE", + "frt": "frt-Latn-VU", + "fub": "fub-Arab-CM", + "fud": "fud-Latn-WF", + "fue": "fue-Latn-ZZ", + "fuf": "fuf-Latn-GN", + "fuh": "fuh-Latn-ZZ", + "fui": "fui-Latn-TD", + "fum": "fum-Latn-NG", + "fun": "fun-Latn-BR", + "fuq": "fuq-Latn-NE", + "fur": "fur-Latn-IT", + "fut": "fut-Latn-VU", + "fuu": "fuu-Latn-CD", + "fuv": "fuv-Latn-NG", + "fuy": "fuy-Latn-ZZ", + "fvr": "fvr-Latn-SD", + "fwa": "fwa-Latn-NC", + "fwe": "fwe-Latn-NA", + "fy": "fy-Latn-NL", + "ga": "ga-Latn-IE", + "gaa": "gaa-Latn-GH", + "gab": "gab-Latn-TD", + "gac": "gac-Latn-IN", + "gac-Deva": "gac-Deva-IN", + "gad": "gad-Latn-PH", + "gae": "gae-Latn-VE", + "gaf": "gaf-Latn-ZZ", + "gag": "gag-Latn-MD", + "gah": "gah-Latn-ZZ", + "gai": "gai-Latn-PG", + "gaj": "gaj-Latn-ZZ", + "gak": "gak-Latn-ID", + "gal": "gal-Latn-TL", + "gam": "gam-Latn-ZZ", + "gan": "gan-Hans-CN", + "gao": "gao-Latn-PG", + "gap": "gap-Latn-PG", + "gaq": "gaq-Orya-IN", + "gar": "gar-Latn-PG", + "gas": "gas-Gujr-IN", + "gat": "gat-Latn-PG", + "gau": "gau-Telu-IN", + "gaw": "gaw-Latn-ZZ", + "gax": "gax-Latn-ET", + "gax-Ethi": "gax-Ethi-ET", + "gay": "gay-Latn-ID", + "gba": "gba-Latn-ZZ", + "gbb": "gbb-Latn-AU", + "gbd": "gbd-Latn-AU", + "gbe": "gbe-Latn-PG", + "gbf": "gbf-Latn-ZZ", + "gbg": "gbg-Latn-CF", + "gbh": "gbh-Latn-BJ", + "gbi": "gbi-Latn-ID", + "gbj": "gbj-Orya-IN", + "gbk": "gbk-Deva-IN", + "gbk-Takr": "gbk-Takr-IN", + "gbl": "gbl-Gujr-IN", + "gbl-Deva": "gbl-Deva-IN", + "gbm": "gbm-Deva-IN", + "gbn": "gbn-Latn-SS", + "gbp": "gbp-Latn-CF", + "gbq": "gbq-Latn-CF", + "gbr": "gbr-Latn-NG", + "gbs": "gbs-Latn-BJ", + "gbu": "gbu-Latn-AU", + "gbv": "gbv-Latn-CF", + "gbw": "gbw-Latn-AU", + "gbx": "gbx-Latn-BJ", + "gby": "gby-Latn-ZZ", + "gbz": "gbz-Arab-IR", + "gcc": "gcc-Latn-PG", + "gcd": "gcd-Latn-AU", + "gcf": "gcf-Latn-GP", + "gcl": "gcl-Latn-GD", + "gcn": "gcn-Latn-PG", + "gcr": "gcr-Latn-GF", + "gct": "gct-Latn-VE", + "gd": "gd-Latn-GB", + "gdb": "gdb-Orya-IN", + "gdb-Telu": "gdb-Telu-IN", + "gdc": "gdc-Latn-AU", + "gdd": "gdd-Latn-PG", + "gde": "gde-Latn-ZZ", + "gdf": "gdf-Latn-NG", + "gdg": "gdg-Latn-PH", + "gdh": "gdh-Latn-AU", + "gdi": "gdi-Latn-CF", + "gdj": "gdj-Latn-AU", + "gdk": "gdk-Latn-TD", + "gdl": "gdl-Latn-ET", + "gdl-Ethi": "gdl-Ethi-ET", + "gdm": "gdm-Latn-TD", + "gdn": "gdn-Latn-ZZ", + "gdo": "gdo-Cyrl-RU", + "gdq": "gdq-Latn-YE", + "gdr": "gdr-Latn-ZZ", + "gdt": "gdt-Latn-AU", + "gdu": "gdu-Latn-NG", + "gdx": "gdx-Deva-IN", + "gea": "gea-Latn-NG", + "geb": "geb-Latn-ZZ", + "gec": "gec-Latn-LR", + "ged": "ged-Latn-NG", + "gef": "gef-Latn-ID", + "geg": "geg-Latn-NG", + "geh": "geh-Latn-CA", + "gei": "gei-Latn-ID", + "gej": "gej-Latn-ZZ", + "gek": "gek-Latn-NG", + "gel": "gel-Latn-ZZ", + "geq": "geq-Latn-CF", + "ges": "ges-Latn-ID", + "gev": "gev-Latn-GA", + "gew": "gew-Latn-NG", + "gex": "gex-Latn-SO", + "gey": "gey-Latn-CD", + "gez": "gez-Ethi-ET", + "gfk": "gfk-Latn-ZZ", + "gga": "gga-Latn-SB", + "ggb": "ggb-Latn-LR", + "ggd": "ggd-Latn-AU", + "gge": "gge-Latn-AU", + "ggg": "ggg-Arab-PK", + "ggk": "ggk-Latn-AU", + "ggl": "ggl-Latn-PG", + "ggn": "gvr-Deva-NP", + "ggt": "ggt-Latn-PG", + "ggu": "ggu-Latn-CI", + "ggw": "ggw-Latn-PG", + "gha": "gha-Arab-LY", + "gha-Latn": "gha-Latn-LY", + "gha-Tfng": "gha-Tfng-LY", + "ghc": "ghc-Latn-GB", + "ghe": "ghe-Deva-NP", + "ghk": "ghk-Latn-MM", + "ghn": "ghn-Latn-SB", + "ghr": "ghr-Arab-PK", + "ghs": "ghs-Latn-ZZ", + "ght": "ght-Tibt-NP", + "gia": "gia-Latn-AU", + "gib": "gib-Latn-NG", + "gic": "gic-Latn-ZA", + "gid": "gid-Latn-CM", + "gie": "gie-Latn-CI", + "gig": "gig-Arab-PK", + "gih": "gih-Latn-AU", + "gil": "gil-Latn-KI", + "gim": "gim-Latn-ZZ", + "gin": "gin-Cyrl-RU", + "gip": "gip-Latn-PG", + "giq": "giq-Latn-VN", + "gir": "gir-Latn-VN", + "gis": "gis-Latn-CM", + "git": "git-Latn-CA", + "gix": "gix-Latn-CD", + "giy": "giy-Latn-AU", + "giz": "giz-Latn-CM", + "gjk": "gjk-Arab-PK", + "gjm": "gjm-Latn-AU", + "gjn": "gjn-Latn-ZZ", + "gjr": "gjr-Latn-AU", + "gju": "gju-Arab-PK", + "gka": "gka-Latn-PG", + "gkd": "gkd-Latn-PG", + "gke": "gke-Latn-CM", + "gkn": "gkn-Latn-ZZ", + "gko": "gko-Latn-AU", + "gkp": "gkp-Latn-ZZ", + "gku": "gku-Latn-ZA", + "gl": "gl-Latn-ES", + "glb": "glb-Latn-NG", + "glc": "glc-Latn-TD", + "gld": "gld-Cyrl-RU", + "glh": "glh-Arab-AF", + "glj": "glj-Latn-TD", + "glk": "glk-Arab-IR", + "gll": "gll-Latn-AU", + "glo": "glo-Latn-NG", + "glr": "glr-Latn-LR", + "glu": "glu-Latn-TD", + "glw": "glw-Latn-NG", + "gma": "gma-Latn-AU", + "gmb": "gmb-Latn-SB", + "gmd": "gmd-Latn-NG", + "gmg": "gmg-Latn-PG", + "gmh": "gmh-Latn-DE", + "gmm": "gmm-Latn-ZZ", + "gmn": "gmn-Latn-CM", + "gmr": "gmr-Latn-AU", + "gmu": "gmu-Latn-PG", + "gmv": "gmv-Ethi-ZZ", + "gmx": "gmx-Latn-TZ", + "gmy": "gmy-Linb-GR", + "gmz": "gmz-Latn-NG", + "gn": "gn-Latn-PY", + "gna": "gna-Latn-BF", + "gnb": "gnb-Latn-IN", + "gnc": "gnc-Latn-ES", + "gnd": "gnd-Latn-ZZ", + "gne": "gne-Latn-NG", + "gng": "gng-Latn-ZZ", + "gnh": "gnh-Latn-NG", + "gni": "gni-Latn-AU", + "gnj": "gnj-Latn-CI", + "gnk": "gnk-Latn-BW", + "gnl": "gnl-Latn-AU", + "gnm": "gnm-Latn-PG", + "gnn": "gnn-Latn-AU", + "gnq": "gnq-Latn-MY", + "gnr": "gnr-Latn-AU", + "gnt": "gnt-Latn-PG", + "gnu": "gnu-Latn-PG", + "gnw": "gnw-Latn-BO", + "gnz": "gnz-Latn-CF", + "goa": "goa-Latn-CI", + "gob": "gob-Latn-CO", + "goc": "goc-Latn-PG", + "god": "god-Latn-ZZ", + "goe": "goe-Tibt-BT", + "gof": "gof-Ethi-ZZ", + "gog": "gog-Latn-TZ", + "goh": "goh-Latn-DE", + "goi": "goi-Latn-ZZ", + "gok": "gok-Deva-IN", + "gol": "gol-Latn-LR", + "gom": "gom-Deva-IN", + "gon": "gon-Telu-IN", + "goo": "goo-Latn-FJ", + "gop": "gop-Latn-ID", + "goq": "goq-Latn-ID", + "gor": "gor-Latn-ID", + "gos": "gos-Latn-NL", + "got": "got-Goth-UA", + "gou": "gou-Latn-CM", + "gov": "gov-Latn-CI", + "gow": "gow-Latn-TZ", + "gox": "gox-Latn-CD", + "goy": "goy-Latn-TD", + "gpa": "gpa-Latn-NG", + "gpe": "gpe-Latn-GH", + "gpn": "gpn-Latn-PG", + "gqa": "gqa-Latn-NG", + "gqn": "gqn-Latn-BR", + "gqr": "gqr-Latn-TD", + "gra": "gra-Deva-IN", + "gra-Gujr": "gra-Gujr-IN", + "grb": "grb-Latn-ZZ", + "grc": "grc-Cprt-CY", + "grc-Linb": "grc-Linb-GR", + "grd": "grd-Latn-NG", + "grg": "grg-Latn-PG", + "grh": "grh-Latn-NG", + "gri": "gri-Latn-SB", + "grj": "grj-Latn-LR", + "grm": "grm-Latn-MY", + "grq": "grq-Latn-PG", + "grs": "grs-Latn-ID", + "grt": "grt-Beng-IN", + "gru": "gru-Ethi-ET", + "gru-Latn": "gru-Latn-ET", + "grv": "grv-Latn-LR", + "grw": "grw-Latn-ZZ", + "grx": "grx-Latn-PG", + "gry": "gry-Latn-LR", + "grz": "grz-Latn-PG", + "gsl": "gsl-Latn-SN", + "gsn": "gsn-Latn-PG", + "gso": "gso-Latn-CF", + "gsp": "gsp-Latn-PG", + "gsw": "gsw-Latn-CH", + "gta": "gta-Latn-BR", + "gtu": "gtu-Latn-AU", + "gu": "gu-Gujr-IN", + "gua": "gua-Latn-NG", + "gub": "gub-Latn-BR", + "guc": "guc-Latn-CO", + "gud": "gud-Latn-ZZ", + "gue": "gue-Latn-AU", + "guf": "guf-Latn-AU", + "guh": "guh-Latn-CO", + "gui": "gui-Latn-BO", + "guk": "guk-Latn-ET", + "guk-Ethi": "guk-Ethi-ET", + "gul": "gul-Latn-US", + "gum": "gum-Latn-CO", + "gun": "gun-Latn-BR", + "guo": "guo-Latn-CO", + "gup": "gup-Latn-AU", + "guq": "guq-Latn-PY", + "gur": "gur-Latn-GH", + "gut": "gut-Latn-CR", + "guu": "guu-Latn-VE", + "guw": "guw-Latn-ZZ", + "gux": "gux-Latn-ZZ", + "guz": "guz-Latn-KE", + "gv": "gv-Latn-IM", + "gva": "gva-Latn-PY", + "gvc": "gvc-Latn-BR", + "gve": "gve-Latn-PG", + "gvf": "gvf-Latn-ZZ", + "gvj": "gvj-Latn-BR", + "gvl": "gvl-Latn-TD", + "gvm": "gvm-Latn-NG", + "gvn": "gvn-Latn-AU", + "gvo": "gvo-Latn-BR", + "gvp": "gvp-Latn-BR", + "gvr": "gvr-Deva-NP", + "gvs": "gvs-Latn-ZZ", + "gvy": "gvy-Latn-AU", + "gwa": "gwa-Latn-CI", + "gwb": "gwb-Latn-NG", + "gwc": "gwc-Arab-ZZ", + "gwd": "gwd-Latn-ET", + "gwe": "gwe-Latn-TZ", + "gwf": "gwf-Arab-PK", + "gwg": "gwg-Latn-NG", + "gwi": "gwi-Latn-CA", + "gwj": "gwj-Latn-BW", + "gwm": "gwm-Latn-AU", + "gwn": "gwn-Latn-NG", + "gwr": "gwr-Latn-UG", + "gwt": "gwt-Arab-ZZ", + "gwu": "gwu-Latn-AU", + "gww": "gww-Latn-AU", + "gwx": "gwx-Latn-GH", + "gxx": "gxx-Latn-CI", + "gyb": "gyb-Latn-PG", + "gyd": "gyd-Latn-AU", + "gye": "gye-Latn-NG", + "gyf": "gyf-Latn-AU", + "gyg": "gyg-Latn-CF", + "gyi": "gyi-Latn-ZZ", + "gyl": "gyl-Latn-ET", + "gyl-Ethi": "gyl-Ethi-ET", + "gym": "gym-Latn-PA", + "gyn": "gyn-Latn-GY", + "gyo": "gyo-Deva-NP", + "gyr": "gyr-Latn-BO", + "gyy": "gyy-Latn-AU", + "gyz": "gyz-Latn-NG", + "gza": "gza-Latn-SD", + "gzi": "gzi-Arab-IR", + "gzn": "gzn-Latn-ID", + "ha": "ha-Latn-NG", + "ha-CM": "ha-Arab-CM", + "ha-SD": "ha-Arab-SD", + "haa": "haa-Latn-US", + "hac": "hac-Arab-IR", + "had": "had-Latn-ID", + "hae": "hae-Latn-ET", + "hag": "hag-Latn-ZZ", + "hah": "hah-Latn-PG", + "hai": "hai-Latn-CA", + "haj": "haj-Latn-IN", + "haj-Beng": "haj-Beng-IN", + "hak": "hak-Hans-CN", + "hal": "hal-Latn-VN", + "ham": "ham-Latn-ZZ", + "han": "han-Latn-TZ", + "hao": "hao-Latn-PG", + "hap": "hap-Latn-ID", + "haq": "haq-Latn-TZ", + "har": "har-Ethi-ET", + "har-Arab": "har-Arab-ET", + "har-Latn": "har-Latn-ET", + "has": "has-Latn-CA", + "hav": "hav-Latn-CD", + "haw": "haw-Latn-US", + "hax": "hax-Latn-CA", + "hay": "hay-Latn-TZ", + "haz": "haz-Arab-AF", + "hba": "hba-Latn-CD", + "hbb": "hbb-Latn-ZZ", + "hbn": "hbn-Latn-SD", + "hbo": "hbo-Hebr-IL", + "hbu": "hbu-Latn-TL", + "hch": "hch-Latn-MX", + "hdy": "hdy-Ethi-ZZ", + "he": "he-Hebr-IL", + "hed": "hed-Latn-TD", + "heg": "heg-Latn-ID", + "heh": "heh-Latn-TZ", + "hei": "hei-Latn-CA", + "hem": "hem-Latn-CD", + "hgm": "hgm-Latn-NA", + "hgw": "hgw-Latn-PG", + "hhi": "hhi-Latn-PG", + "hhr": "hhr-Latn-SN", + "hhy": "hhy-Latn-ZZ", + "hi": "hi-Deva-IN", + "hi-Latn": "hi-Latn-IN", + "hia": "hia-Latn-ZZ", + "hib": "hib-Latn-PE", + "hid": "hid-Latn-US", + "hif": "hif-Latn-FJ", + "hig": "hig-Latn-ZZ", + "hih": "hih-Latn-ZZ", + "hii": "hii-Takr-IN", + "hii-Deva": "hii-Deva-IN", + "hij": "hij-Latn-CM", + "hik": "hik-Latn-ID", + "hil": "hil-Latn-PH", + "hio": "hio-Latn-BW", + "hir": "hir-Latn-BR", + "hit": "hit-Xsux-TR", + "hiw": "hiw-Latn-VU", + "hix": "hix-Latn-BR", + "hji": "hji-Latn-ID", + "hka": "hka-Latn-TZ", + "hke": "hke-Latn-CD", + "hkh": "hkh-Arab-IN", + "hkh-Deva": "hkh-Deva-IN", + "hkh-Latn": "hkh-Latn-IN", + "hkk": "hkk-Latn-PG", + "hla": "hla-Latn-ZZ", + "hlb": "hlb-Deva-IN", + "hld": "hld-Latn-VN", + "hlt": "hlt-Latn-MM", + "hlu": "hlu-Hluw-TR", + "hma": "hma-Latn-CN", + "hmb": "hmb-Latn-ML", + "hmd": "hmd-Plrd-CN", + "hmf": "hmf-Latn-VN", + "hmj": "hmj-Bopo-CN", + "hmm": "hmm-Latn-CN", + "hmn": "hmn-Latn-CN", + "hmn-Bopo": "hmn-Bopo-CN", + "hmn-Hmng": "hmn-Hmng-CN", + "hmp": "hmp-Latn-CN", + "hmq": "hmq-Bopo-CN", + "hmr": "hmr-Latn-IN", + "hms": "hms-Latn-CN", + "hmt": "hmt-Latn-ZZ", + "hmu": "hmu-Latn-ID", + "hmv": "hmv-Latn-VN", + "hmw": "hmw-Latn-CN", + "hmy": "hmy-Latn-CN", + "hmz": "hmz-Latn-CN", + "hmz-Plrd": "hmz-Plrd-CN", + "hna": "hna-Latn-CM", + "hnd": "hnd-Arab-PK", + "hne": "hne-Deva-IN", + "hng": "hng-Latn-AO", + "hnh": "hnh-Latn-BW", + "hni": "hni-Latn-CN", + "hnj": "hnj-Hmnp-US", + "hnj-AU": "hnj-Laoo-AU", + "hnj-CN": "hnj-Laoo-CN", + "hnj-FR": "hnj-Laoo-FR", + "hnj-GF": "hnj-Laoo-GF", + "hnj-LA": "hnj-Laoo-LA", + "hnj-Laoo": "hnj-Laoo-LA", + "hnj-MM": "hnj-Laoo-MM", + "hnj-SR": "hnj-Laoo-SR", + "hnj-TH": "hnj-Laoo-TH", + "hnj-US": "hnj-Hmnp-US", + "hnj-VN": "hnj-Laoo-VN", + "hnn": "hnn-Latn-PH", + "hno": "hno-Arab-PK", + "hns": "hns-Latn-SR", + "ho": "ho-Latn-PG", + "hoa": "hoa-Latn-SB", + "hob": "hob-Latn-PG", + "hoc": "hoc-Deva-IN", + "hod": "hod-Latn-NG", + "hoe": "hoe-Latn-NG", + "hoh": "hoh-Arab-OM", + "hoi": "hoi-Latn-US", + "hoj": "hoj-Deva-IN", + "hol": "hol-Latn-AO", + "hom": "hom-Latn-SS", + "hoo": "hoo-Latn-CD", + "hop": "hop-Latn-US", + "hor": "hor-Latn-TD", + "hot": "hot-Latn-ZZ", + "hov": "hov-Latn-ID", + "how": "how-Hani-CN", + "hoy": "hoy-Deva-IN", + "hpo": "hpo-Mymr-MM", + "hr": "hr-Latn-HR", + "hra": "hra-Latn-IN", + "hrc": "hrc-Latn-PG", + "hre": "hre-Latn-VN", + "hrk": "hrk-Latn-ID", + "hrm": "hrm-Latn-CN", + "hrm-Hmng": "hrm-Hmng-CN", + "hro": "hro-Latn-VN", + "hrp": "hrp-Latn-AU", + "hrt": "hrt-Syrc-TR", + "hru": "hru-Latn-IN", + "hrw": "hrw-Latn-PG", + "hrx": "hrx-Latn-BR", + "hrz": "hrz-Arab-IR", + "hsb": "hsb-Latn-DE", + "hsn": "hsn-Hans-CN", + "hss": "hss-Arab-OM", + "ht": "ht-Latn-HT", + "hti": "hti-Latn-ID", + "hto": "hto-Latn-CO", + "hts": "hts-Latn-TZ", + "htu": "htu-Latn-ID", + "htx": "htx-Xsux-TR", + "hu": "hu-Latn-HU", + "hub": "hub-Latn-PE", + "huc": "huc-Latn-BW", + "hud": "hud-Latn-ID", + "hue": "hue-Latn-MX", + "huf": "huf-Latn-PG", + "hug": "hug-Latn-PE", + "huh": "huh-Latn-CL", + "hui": "hui-Latn-ZZ", + "huk": "huk-Latn-ID", + "hul": "hul-Latn-PG", + "hum": "hum-Latn-CD", + "hup": "hup-Latn-US", + "hur": "hur-Latn-CA", + "hus": "hus-Latn-MX", + "hut": "hut-Deva-NP", + "hut-Tibt": "hut-Tibt-NP", + "huu": "huu-Latn-PE", + "huv": "huv-Latn-MX", + "huw": "huw-Latn-ID", + "hux": "hux-Latn-PE", + "huy": "huy-Hebr-IL", + "huz": "huz-Cyrl-RU", + "hvc": "hvc-Latn-HT", + "hve": "hve-Latn-MX", + "hvk": "hvk-Latn-NC", + "hvn": "hvn-Latn-ID", + "hvv": "hvv-Latn-MX", + "hwa": "hwa-Latn-CI", + "hwc": "hwc-Latn-US", + "hwo": "hwo-Latn-NG", + "hy": "hy-Armn-AM", + "hya": "hya-Latn-CM", + "hyw": "hyw-Armn-AM", + "hz": "hz-Latn-NA", + "ia": "ia-Latn-001", + "iai": "iai-Latn-NC", + "ian": "ian-Latn-ZZ", + "iar": "iar-Latn-ZZ", + "iba": "iba-Latn-MY", + "ibb": "ibb-Latn-NG", + "ibd": "ibd-Latn-AU", + "ibe": "ibe-Latn-NG", + "ibg": "ibg-Latn-PH", + "ibh": "ibh-Latn-VN", + "ibl": "ibl-Latn-PH", + "ibm": "ibm-Latn-NG", + "ibn": "ibn-Latn-NG", + "ibr": "ibr-Latn-NG", + "ibu": "ibu-Latn-ID", + "iby": "iby-Latn-ZZ", + "ica": "ica-Latn-ZZ", + "ich": "ich-Latn-ZZ", + "icr": "icr-Latn-CO", + "id": "id-Latn-ID", + "ida": "ida-Latn-KE", + "idb": "idb-Latn-IN", + "idc": "idc-Latn-NG", + "idd": "idd-Latn-ZZ", + "ide": "ide-Latn-NG", + "idi": "idi-Latn-ZZ", + "idr": "idr-Latn-SS", + "ids": "ids-Latn-NG", + "idt": "idt-Latn-TL", + "idu": "idu-Latn-ZZ", + "ie": "ie-Latn-001", + "ifa": "ifa-Latn-PH", + "ifb": "ifb-Latn-PH", + "ife": "ife-Latn-TG", + "iff": "iff-Latn-VU", + "ifk": "ifk-Latn-PH", + "ifm": "ifm-Latn-CG", + "ifu": "ifu-Latn-PH", + "ify": "ify-Latn-PH", + "ig": "ig-Latn-NG", + "igb": "igb-Latn-ZZ", + "ige": "ige-Latn-ZZ", + "igg": "igg-Latn-PG", + "igl": "igl-Latn-NG", + "igm": "igm-Latn-PG", + "ign": "ign-Latn-BO", + "igo": "igo-Latn-PG", + "igs": "igs-Latn-001", + "igs-Grek": "igs-Grek-001", + "igw": "igw-Latn-NG", + "ihb": "ihb-Latn-ID", + "ihi": "ihi-Latn-NG", + "ihp": "ihp-Latn-ID", + "ihw": "ihw-Latn-AU", + "ii": "ii-Yiii-CN", + "iin": "iin-Latn-AU", + "ijc": "ijc-Latn-NG", + "ije": "ije-Latn-NG", + "ijj": "ijj-Latn-ZZ", + "ijn": "ijn-Latn-NG", + "ijs": "ijs-Latn-NG", + "ik": "ik-Latn-US", + "iki": "iki-Latn-NG", + "ikk": "ikk-Latn-ZZ", + "ikl": "ikl-Latn-NG", + "iko": "iko-Latn-NG", + "ikp": "ikp-Latn-NG", + "ikr": "ikr-Latn-AU", + "ikt": "ikt-Latn-CA", + "ikt-Cans": "ikt-Cans-CA", + "ikv": "ikv-Latn-NG", + "ikw": "ikw-Latn-ZZ", + "ikx": "ikx-Latn-ZZ", + "ikz": "ikz-Latn-TZ", + "ila": "ila-Latn-ID", + "ilb": "ilb-Latn-ZM", + "ilg": "ilg-Latn-AU", + "ili": "ili-Latn-CN", + "ili-Arab": "ili-Arab-CN", + "ili-Cyrl": "ili-Cyrl-KZ", + "ilk": "ilk-Latn-PH", + "ilm": "ilm-Latn-MY", + "ilo": "ilo-Latn-PH", + "ilp": "ilp-Latn-PH", + "ilu": "ilu-Latn-ID", + "ilv": "ilv-Latn-NG", + "imi": "imi-Latn-PG", + "iml": "iml-Latn-US", + "imn": "imn-Latn-PG", + "imo": "imo-Latn-ZZ", + "imr": "imr-Latn-ID", + "ims": "ims-Latn-IT", + "imt": "imt-Latn-SS", + "imy": "imy-Lyci-TR", + "in": "id-Latn-ID", + "inb": "inb-Latn-CO", + "ing": "ing-Latn-US", + "inh": "inh-Cyrl-RU", + "inj": "inj-Latn-CO", + "inn": "inn-Latn-PH", + "ino": "ino-Latn-PG", + "inp": "inp-Latn-PE", + "int": "int-Mymr-MM", + "io": "io-Latn-001", + "ior": "ior-Ethi-ET", + "iou": "iou-Latn-ZZ", + "iow": "iow-Latn-US", + "ipi": "ipi-Latn-PG", + "ipo": "ipo-Latn-PG", + "iqu": "iqu-Latn-PE", + "iqw": "iqw-Latn-NG", + "ire": "ire-Latn-ID", + "irh": "irh-Latn-ID", + "iri": "iri-Latn-ZZ", + "irk": "irk-Latn-TZ", + "irn": "irn-Latn-BR", + "iru": "iru-Taml-IN", + "iru-Mlym": "iru-Mlym-IN", + "irx": "irx-Latn-ID", + "iry": "iry-Latn-PH", + "is": "is-Latn-IS", + "isa": "isa-Latn-PG", + "isc": "isc-Latn-PE", + "isd": "isd-Latn-PH", + "ish": "ish-Latn-NG", + "isi": "isi-Latn-NG", + "isk": "isk-Arab-AF", + "isk-Cyrl": "isk-Cyrl-TJ", + "ism": "ism-Latn-ID", + "isn": "isn-Latn-TZ", + "iso": "iso-Latn-NG", + "ist": "ist-Latn-HR", + "isu": "isu-Latn-CM", + "it": "it-Latn-IT", + "itb": "itb-Latn-PH", + "itd": "itd-Latn-ID", + "ite": "ite-Latn-BO", + "iti": "iti-Latn-PH", + "itk": "itk-Hebr-IT", + "itl": "itl-Cyrl-RU", + "itm": "itm-Latn-NG", + "ito": "ito-Latn-BO", + "itr": "itr-Latn-PG", + "its": "its-Latn-NG", + "itt": "itt-Latn-PH", + "itv": "itv-Latn-PH", + "itw": "itw-Latn-NG", + "itx": "itx-Latn-ID", + "ity": "ity-Latn-PH", + "itz": "itz-Latn-GT", + "iu": "iu-Cans-CA", + "ium": "ium-Latn-CN", + "ium-Hani": "ium-Hani-CN", + "ium-Laoo": "ium-Laoo-LA", + "ium-Thai": "ium-Thai-TH", + "ivb": "ivb-Latn-PH", + "ivv": "ivv-Latn-PH", + "iw": "he-Hebr-IL", + "iwk": "iwk-Latn-PH", + "iwm": "iwm-Latn-ZZ", + "iwo": "iwo-Latn-ID", + "iws": "iws-Latn-ZZ", + "ixc": "ixc-Latn-MX", + "ixl": "ixl-Latn-GT", + "iya": "iya-Latn-NG", + "iyo": "iyo-Latn-CM", + "iyx": "iyx-Latn-CG", + "izh": "izh-Latn-RU", + "izi": "eza-Latn-ZZ", + "izr": "izr-Latn-NG", + "izz": "izz-Latn-NG", + "ja": "ja-Jpan-JP", + "jaa": "jaa-Latn-BR", + "jab": "jab-Latn-ZZ", + "jac": "jac-Latn-GT", + "jad": "jad-Arab-GN", + "jae": "jae-Latn-PG", + "jaf": "jaf-Latn-NG", + "jah": "jah-Latn-MY", + "jaj": "jaj-Latn-SB", + "jak": "jak-Latn-MY", + "jal": "jal-Latn-ID", + "jam": "jam-Latn-JM", + "jan": "jan-Latn-AU", + "jao": "jao-Latn-AU", + "jaq": "jaq-Latn-ID", + "jar": "jgk-Latn-ZZ", + "jas": "jas-Latn-NC", + "jat": "jat-Arab-AF", + "jau": "jau-Latn-ID", + "jax": "jax-Latn-ID", + "jay": "jay-Latn-AU", + "jaz": "jaz-Latn-NC", + "jbe": "jbe-Hebr-IL", + "jbi": "jbi-Latn-AU", + "jbj": "jbj-Latn-ID", + "jbk": "jbk-Latn-PG", + "jbm": "jbm-Latn-NG", + "jbn": "jbn-Arab-LY", + "jbo": "jbo-Latn-001", + "jbr": "jbr-Latn-ID", + "jbt": "jbt-Latn-BR", + "jbu": "jbu-Latn-ZZ", + "jbw": "jbw-Latn-AU", + "jct": "jct-Cyrl-UA", + "jct-Latn": "jct-Latn-UA", + "jda": "jda-Tibt-IN", + "jdg": "jdg-Arab-PK", + "jdt": "jdt-Cyrl-RU", + "jdt-Hebr": "jdt-Hebr-RU", + "jdt-Latn": "jdt-Latn-AZ", + "jeb": "jeb-Latn-PE", + "jee": "jee-Deva-NP", + "jeh": "jeh-Latn-VN", + "jeh-Laoo": "jeh-Laoo-LA", + "jei": "jei-Latn-ID", + "jek": "jek-Latn-CI", + "jel": "jel-Latn-ID", + "jen": "jen-Latn-ZZ", + "jer": "jer-Latn-NG", + "jet": "jet-Latn-PG", + "jeu": "jeu-Latn-TD", + "jgb": "jgb-Latn-CD", + "jge": "jge-Geor-GE", + "jge-Hebr": "jge-Hebr-IL", + "jgk": "jgk-Latn-ZZ", + "jgo": "jgo-Latn-CM", + "jhi": "jhi-Latn-MY", + "ji": "yi-Hebr-001", + "jia": "jia-Latn-CM", + "jib": "jib-Latn-ZZ", + "jic": "jic-Latn-HN", + "jid": "jid-Latn-NG", + "jie": "jie-Latn-NG", + "jig": "jig-Latn-AU", + "jil": "jil-Latn-PG", + "jim": "jim-Latn-CM", + "jit": "jit-Latn-TZ", + "jiu": "jiu-Latn-CN", + "jiv": "jiv-Latn-EC", + "jiy": "jiy-Latn-CN", + "jje": "jje-Hang-KR", + "jjr": "jjr-Latn-NG", + "jka": "jka-Latn-ID", + "jkm": "jkm-Mymr-MM", + "jkm-Brai": "jkm-Brai-MM", + "jkm-Latn": "jkm-Latn-MM", + "jko": "jko-Latn-PG", + "jku": "jku-Latn-NG", + "jle": "jle-Latn-SD", + "jma": "jma-Latn-PG", + "jmb": "jmb-Latn-NG", + "jmc": "jmc-Latn-TZ", + "jmd": "jmd-Latn-ID", + "jmi": "jmi-Latn-NG", + "jml": "jml-Deva-NP", + "jmn": "jmn-Latn-MM", + "jmr": "jmr-Latn-GH", + "jms": "jms-Latn-NG", + "jmw": "jmw-Latn-PG", + "jmx": "jmx-Latn-MX", + "jna": "jna-Takr-IN", + "jnd": "jnd-Arab-PK", + "jng": "jng-Latn-AU", + "jni": "jni-Latn-NG", + "jnj": "jnj-Latn-ET", + "jnj-Ethi": "jnj-Ethi-ET", + "jnl": "jnl-Deva-IN", + "jns": "jns-Deva-IN", + "jns-Latn": "jns-Latn-IN", + "jns-Takr": "jns-Takr-IN", + "job": "job-Latn-CD", + "jod": "jod-Latn-CI", + "jog": "jog-Arab-PK", + "jor": "jor-Latn-BO", + "jow": "jow-Latn-ML", + "jpa": "jpa-Hebr-PS", + "jpr": "jpr-Hebr-IL", + "jqr": "jqr-Latn-PE", + "jra": "jra-Latn-ZZ", + "jrr": "jrr-Latn-NG", + "jrt": "jrt-Latn-NG", + "jru": "jru-Latn-VE", + "jua": "jua-Latn-BR", + "jub": "jub-Latn-NG", + "jud": "jud-Latn-CI", + "juh": "juh-Latn-NG", + "jui": "jui-Latn-AU", + "juk": "juk-Latn-NG", + "jul": "jul-Deva-NP", + "jum": "jum-Latn-SD", + "jun": "jun-Orya-IN", + "juo": "juo-Latn-NG", + "jup": "jup-Latn-BR", + "jur": "jur-Latn-BR", + "jut": "jut-Latn-DK", + "juu": "juu-Latn-NG", + "juw": "juw-Latn-NG", + "juy": "juy-Orya-IN", + "jv": "jv-Latn-ID", + "jvd": "jvd-Latn-ID", + "jvn": "jvn-Latn-SR", + "jw": "jv-Latn-ID", + "jwi": "jwi-Latn-GH", + "jya": "jya-Tibt-CN", + "jye": "jye-Hebr-IL", + "jyy": "jyy-Latn-TD", + "ka": "ka-Geor-GE", + "kaa": "kaa-Cyrl-UZ", + "kab": "kab-Latn-DZ", + "kac": "kac-Latn-MM", + "kad": "kad-Latn-ZZ", + "kag": "kag-Latn-MY", + "kah": "kah-Latn-CF", + "kai": "kai-Latn-ZZ", + "kaj": "kaj-Latn-NG", + "kak": "kak-Latn-PH", + "kam": "kam-Latn-KE", + "kao": "kao-Latn-ML", + "kap": "kap-Cyrl-RU", + "kaq": "kaq-Latn-PE", + "kav": "kav-Latn-BR", + "kaw": "kaw-Kawi-ID", + "kax": "kax-Latn-ID", + "kay": "kay-Latn-BR", + "kba": "kba-Latn-AU", + "kbb": "kbb-Latn-BR", + "kbc": "kbc-Latn-BR", + "kbd": "kbd-Cyrl-RU", + "kbe": "kbe-Latn-AU", + "kbh": "kbh-Latn-CO", + "kbi": "kbi-Latn-ID", + "kbj": "kbj-Latn-CD", + "kbk": "kbk-Latn-PG", + "kbl": "kbl-Latn-TD", + "kbm": "kbm-Latn-ZZ", + "kbn": "kbn-Latn-CF", + "kbo": "kbo-Latn-SS", + "kbp": "kbp-Latn-ZZ", + "kbq": "kbq-Latn-ZZ", + "kbr": "kbr-Latn-ET", + "kbr-Ethi": "kbr-Ethi-ET", + "kbs": "kbs-Latn-GA", + "kbt": "kbt-Latn-PG", + "kbu": "kbu-Arab-PK", + "kbv": "kbv-Latn-ID", + "kbw": "kbw-Latn-PG", + "kbx": "kbx-Latn-ZZ", + "kby": "kby-Arab-NE", + "kbz": "kbz-Latn-NG", + "kca": "kca-Cyrl-RU", + "kcb": "kcb-Latn-PG", + "kcc": "kcc-Latn-NG", + "kcd": "kcd-Latn-ID", + "kce": "kce-Latn-NG", + "kcf": "kcf-Latn-NG", + "kcg": "kcg-Latn-NG", + "kch": "kch-Latn-NG", + "kci": "kci-Latn-NG", + "kcj": "kcj-Latn-GW", + "kck": "kck-Latn-ZW", + "kcl": "kcl-Latn-ZZ", + "kcm": "kcm-Latn-CF", + "kcn": "kcn-Latn-UG", + "kco": "kco-Latn-PG", + "kcp": "kcp-Latn-SD", + "kcq": "kcq-Latn-NG", + "kcs": "kcs-Latn-NG", + "kct": "kct-Latn-ZZ", + "kcu": "kcu-Latn-TZ", + "kcv": "kcv-Latn-CD", + "kcw": "kcw-Latn-CD", + "kcz": "kcz-Latn-TZ", + "kda": "kda-Latn-AU", + "kdc": "kdc-Latn-TZ", + "kdd": "kdd-Latn-AU", + "kde": "kde-Latn-TZ", + "kdf": "kdf-Latn-PG", + "kdg": "kdg-Latn-CD", + "kdh": "kdh-Latn-TG", + "kdi": "kdi-Latn-UG", + "kdj": "kdj-Latn-UG", + "kdk": "kdk-Latn-NC", + "kdl": "kdl-Latn-ZZ", + "kdm": "kdm-Latn-NG", + "kdn": "kdn-Latn-ZW", + "kdp": "kdp-Latn-NG", + "kdq": "kdq-Beng-IN", + "kdr": "kdr-Latn-LT", + "kdr-Cyrl": "kdr-Cyrl-UA", + "kdt": "kdt-Thai-TH", + "kdw": "kdw-Latn-ID", + "kdx": "kdx-Latn-NG", + "kdy": "kdy-Latn-ID", + "kdz": "kdz-Latn-CM", + "kea": "kea-Latn-CV", + "keb": "keb-Latn-GA", + "kec": "kec-Latn-SD", + "ked": "ked-Latn-TZ", + "kee": "kee-Latn-US", + "kef": "kef-Latn-TG", + "keg": "keg-Latn-SD", + "keh": "keh-Latn-PG", + "kei": "kei-Latn-ID", + "kek": "kek-Latn-GT", + "kel": "kel-Latn-CD", + "kem": "kem-Latn-TL", + "ken": "ken-Latn-CM", + "keo": "keo-Latn-UG", + "ker": "ker-Latn-TD", + "kes": "kes-Latn-NG", + "ket": "ket-Cyrl-RU", + "keu": "keu-Latn-TG", + "kew": "kew-Latn-PG", + "kex": "kex-Deva-IN", + "kex-Gujr": "kex-Gujr-IN", + "key": "key-Telu-IN", + "kez": "kez-Latn-ZZ", + "kfa": "kfa-Knda-IN", + "kfb": "kfb-Deva-IN", + "kfc": "kfc-Telu-IN", + "kfd": "kfd-Knda-IN", + "kfe": "kfe-Taml-IN", + "kff": "kff-Latn-IN", + "kff-Deva": "kff-Deva-IN", + "kff-Orya": "kff-Orya-IN", + "kff-Telu": "kff-Telu-IN", + "kfh": "kfh-Mlym-IN", + "kfi": "kfi-Taml-IN", + "kfi-Knda": "kfi-Knda-IN", + "kfk": "kfk-Deva-IN", + "kfk-Takr": "kfk-Takr-IN", + "kfl": "kfl-Latn-CM", + "kfm": "kfm-Arab-IR", + "kfn": "kfn-Latn-CM", + "kfo": "kfo-Latn-CI", + "kfp": "kfp-Deva-IN", + "kfq": "kfq-Deva-IN", + "kfr": "kfr-Deva-IN", + "kfs": "kfs-Deva-IN", + "kfv": "kfv-Latn-IN", + "kfw": "kfw-Latn-IN", + "kfx": "kfx-Deva-IN", + "kfx-Takr": "kfx-Takr-IN", + "kfy": "kfy-Deva-IN", + "kfz": "kfz-Latn-BF", + "kg": "kg-Latn-CD", + "kga": "kga-Latn-CI", + "kgb": "kgb-Latn-ID", + "kge": "kge-Latn-ID", + "kgf": "kgf-Latn-ZZ", + "kgj": "kgj-Deva-NP", + "kgk": "kgk-Latn-BR", + "kgl": "kgl-Latn-AU", + "kgm": "kgm-Latn-BR", + "kgo": "kgo-Latn-SD", + "kgp": "kgp-Latn-BR", + "kgq": "kgq-Latn-ID", + "kgr": "kgr-Latn-ID", + "kgs": "kgs-Latn-AU", + "kgt": "kgt-Latn-NG", + "kgu": "kgu-Latn-PG", + "kgv": "kgv-Latn-ID", + "kgw": "kgw-Latn-ID", + "kgx": "kgx-Latn-ID", + "kgy": "kgy-Deva-NP", + "kha": "kha-Latn-IN", + "khb": "khb-Talu-CN", + "khc": "khc-Latn-ID", + "khd": "khd-Latn-ID", + "khe": "khe-Latn-ID", + "khf": "khf-Thai-LA", + "khg": "khg-Tibt-CN", + "khh": "khh-Latn-ID", + "khj": "khj-Latn-NG", + "khl": "khl-Latn-PG", + "khn": "khn-Deva-IN", + "khp": "khp-Latn-ID", + "khq": "khq-Latn-ML", + "khr": "khr-Latn-IN", + "khr-Deva": "khr-Deva-IN", + "khs": "khs-Latn-ZZ", + "kht": "kht-Mymr-IN", + "khu": "khu-Latn-AO", + "khv": "khv-Cyrl-RU", + "khw": "khw-Arab-PK", + "khx": "khx-Latn-CD", + "khy": "khy-Latn-CD", + "khz": "khz-Latn-ZZ", + "ki": "ki-Latn-KE", + "kia": "kia-Latn-TD", + "kib": "kib-Latn-SD", + "kic": "kic-Latn-US", + "kid": "kid-Latn-CM", + "kie": "kie-Latn-TD", + "kif": "kif-Deva-NP", + "kig": "kig-Latn-ID", + "kih": "kih-Latn-PG", + "kij": "kij-Latn-ZZ", + "kil": "kil-Latn-NG", + "kim": "kim-Cyrl-RU", + "kio": "kio-Latn-US", + "kip": "kip-Deva-NP", + "kiq": "kiq-Latn-ID", + "kis": "kis-Latn-PG", + "kit": "kit-Latn-PG", + "kiu": "kiu-Latn-TR", + "kiv": "kiv-Latn-TZ", + "kiw": "kiw-Latn-ZZ", + "kix": "kix-Latn-IN", + "kiy": "kiy-Latn-ID", + "kiz": "kiz-Latn-TZ", + "kj": "kj-Latn-NA", + "kja": "kja-Latn-ID", + "kjb": "kjb-Latn-GT", + "kjc": "kjc-Latn-ID", + "kjd": "kjd-Latn-ZZ", + "kje": "kje-Latn-ID", + "kjg": "kjg-Laoo-LA", + "kjh": "kjh-Cyrl-RU", + "kji": "kji-Latn-SB", + "kjj": "kjj-Latn-AZ", + "kjk": "kjk-Latn-ID", + "kjl": "kjl-Deva-NP", + "kjm": "kjm-Latn-VN", + "kjn": "kjn-Latn-AU", + "kjo": "kjo-Deva-IN", + "kjp": "kjp-Mymr-MM", + "kjp-Thai": "kjp-Thai-TH", + "kjq": "kjq-Latn-US", + "kjr": "kjr-Latn-ID", + "kjs": "kjs-Latn-ZZ", + "kjt": "kjt-Thai-TH", + "kju": "kju-Latn-US", + "kjx": "kjx-Latn-PG", + "kjy": "kjy-Latn-ZZ", + "kk": "kk-Cyrl-KZ", + "kk-AF": "kk-Arab-AF", + "kk-Arab": "kk-Arab-CN", + "kk-CN": "kk-Arab-CN", + "kk-IR": "kk-Arab-IR", + "kk-MN": "kk-Arab-MN", + "kka": "kka-Latn-NG", + "kkb": "kkb-Latn-ID", + "kkc": "kkc-Latn-ZZ", + "kkd": "kkd-Latn-NG", + "kke": "kke-Latn-GN", + "kke-Arab": "kke-Arab-GN", + "kkf": "kkf-Tibt-IN", + "kkg": "kkg-Latn-PH", + "kkh": "kkh-Lana-MM", + "kki": "kki-Latn-TZ", + "kkj": "kkj-Latn-CM", + "kkk": "kkk-Latn-SB", + "kkl": "kkl-Latn-ID", + "kkm": "kkm-Latn-NG", + "kko": "kko-Latn-SD", + "kkp": "kkp-Latn-AU", + "kkq": "kkq-Latn-CD", + "kkr": "kkr-Latn-NG", + "kks": "kks-Latn-NG", + "kkt": "kkt-Deva-NP", + "kku": "kku-Latn-NG", + "kkv": "kkv-Latn-ID", + "kkw": "kkw-Latn-CG", + "kkx": "kkx-Latn-ID", + "kky": "kky-Latn-AU", + "kkz": "kkz-Latn-CA", + "kl": "kl-Latn-GL", + "kla": "kla-Latn-US", + "klb": "klb-Latn-MX", + "klc": "klc-Latn-CM", + "kld": "kld-Latn-AU", + "kle": "kle-Deva-NP", + "klf": "klf-Latn-TD", + "klg": "klg-Latn-PH", + "klh": "klh-Latn-PG", + "kli": "kli-Latn-ID", + "klj": "klj-Arab-IR", + "klk": "klk-Latn-NG", + "kll": "kll-Latn-PH", + "klm": "klm-Latn-PG", + "kln": "kln-Latn-KE", + "klo": "klo-Latn-NG", + "klp": "klp-Latn-PG", + "klq": "klq-Latn-ZZ", + "klr": "klr-Deva-NP", + "kls": "kls-Latn-PK", + "kls-Arab": "kls-Arab-PK", + "klt": "klt-Latn-ZZ", + "klu": "klu-Latn-LR", + "klv": "klv-Latn-VU", + "klw": "klw-Latn-ID", + "klx": "klx-Latn-ZZ", + "kly": "kly-Latn-ID", + "klz": "klz-Latn-ID", + "km": "km-Khmr-KH", + "kma": "kma-Latn-GH", + "kmb": "kmb-Latn-AO", + "kmc": "kmc-Latn-CN", + "kmc-Hani": "kmc-Hani-CN", + "kmd": "kmd-Latn-PH", + "kme": "kme-Latn-CM", + "kmf": "kmf-Latn-PG", + "kmg": "kmg-Latn-PG", + "kmh": "kmh-Latn-ZZ", + "kmi": "kmi-Latn-NG", + "kmj": "kmj-Deva-IN", + "kmk": "kmk-Latn-PH", + "kml": "kml-Latn-PH", + "kmm": "kmm-Latn-IN", + "kmn": "kmn-Latn-PG", + "kmo": "kmo-Latn-ZZ", + "kmp": "kmp-Latn-CM", + "kmq": "kmq-Latn-ET", + "kms": "kms-Latn-ZZ", + "kmt": "kmt-Latn-ID", + "kmu": "kmu-Latn-ZZ", + "kmv": "kmv-Latn-BR", + "kmw": "kmw-Latn-ZZ", + "kmx": "kmx-Latn-PG", + "kmy": "kmy-Latn-NG", + "kmz": "kmz-Arab-IR", + "kn": "kn-Knda-IN", + "kna": "kna-Latn-NG", + "knb": "knb-Latn-PH", + "knd": "knd-Latn-ID", + "kne": "kne-Latn-PH", + "knf": "knf-Latn-GW", + "kni": "kni-Latn-NG", + "knj": "knj-Latn-GT", + "knk": "knk-Latn-SL", + "knk-Arab": "knk-Arab-SL", + "knl": "knl-Latn-ID", + "knm": "knm-Latn-BR", + "kno": "kno-Latn-SL", + "knp": "knp-Latn-ZZ", + "knq": "knq-Latn-MY", + "knr": "knr-Latn-PG", + "kns": "kns-Latn-MY", + "kns-Thai": "kns-Thai-TH", + "knt": "knt-Latn-BR", + "knu": "knu-Latn-GN", + "knv": "knv-Latn-PG", + "knw": "knw-Latn-NA", + "knx": "knx-Latn-ID", + "kny": "kny-Latn-CD", + "knz": "knz-Latn-BF", + "ko": "ko-Kore-KR", + "koa": "koa-Latn-PG", + "koc": "koc-Latn-NG", + "kod": "kod-Latn-ID", + "koe": "koe-Latn-SS", + "kof": "kof-Latn-NG", + "kog": "kog-Latn-CO", + "koh": "koh-Latn-CG", + "koi": "koi-Cyrl-RU", + "kok": "kok-Deva-IN", + "kol": "kol-Latn-ZZ", + "koo": "koo-Latn-UG", + "kop": "kop-Latn-PG", + "koq": "koq-Latn-GA", + "kos": "kos-Latn-FM", + "kot": "kot-Latn-CM", + "kou": "kou-Latn-TD", + "kov": "kov-Latn-NG", + "kow": "kow-Latn-NG", + "koy": "koy-Latn-US", + "koz": "koz-Latn-ZZ", + "kpa": "kpa-Latn-NG", + "kpc": "kpc-Latn-CO", + "kpd": "kpd-Latn-ID", + "kpe": "kpe-Latn-LR", + "kpf": "kpf-Latn-ZZ", + "kpg": "kpg-Latn-FM", + "kph": "kph-Latn-GH", + "kpi": "kpi-Latn-ID", + "kpj": "kpj-Latn-BR", + "kpk": "kpk-Latn-NG", + "kpl": "kpl-Latn-CD", + "kpm": "kpm-Latn-VN", + "kpn": "kpn-Latn-BR", + "kpo": "kpo-Latn-ZZ", + "kpq": "kpq-Latn-ID", + "kpr": "kpr-Latn-ZZ", + "kps": "kps-Latn-ID", + "kpt": "kpt-Cyrl-RU", + "kpu": "kpu-Latn-ID", + "kpw": "kpw-Latn-PG", + "kpx": "kpx-Latn-ZZ", + "kpy": "kpy-Cyrl-RU", + "kpz": "kpz-Latn-UG", + "kqa": "kqa-Latn-PG", + "kqb": "kqb-Latn-ZZ", + "kqc": "kqc-Latn-PG", + "kqd": "kqd-Syrc-IQ", + "kqe": "kqe-Latn-PH", + "kqf": "kqf-Latn-ZZ", + "kqg": "kqg-Latn-BF", + "kqh": "kqh-Latn-TZ", + "kqi": "kqi-Latn-PG", + "kqj": "kqj-Latn-PG", + "kqk": "kqk-Latn-BJ", + "kql": "kql-Latn-PG", + "kqm": "kqm-Latn-CI", + "kqn": "kqn-Latn-ZM", + "kqo": "kqo-Latn-LR", + "kqp": "kqp-Latn-TD", + "kqq": "kqq-Latn-BR", + "kqr": "kqr-Latn-MY", + "kqs": "kqs-Latn-ZZ", + "kqt": "kqt-Latn-MY", + "kqu": "kqu-Latn-ZA", + "kqv": "kqv-Latn-ID", + "kqw": "kqw-Latn-PG", + "kqx": "kqx-Latn-CM", + "kqy": "kqy-Ethi-ZZ", + "kqz": "kqz-Latn-ZA", + "kr": "kr-Latn-ZZ", + "kra": "kra-Deva-NP", + "krb": "krb-Latn-US", + "krc": "krc-Cyrl-RU", + "krd": "krd-Latn-TL", + "kre": "kre-Latn-BR", + "krf": "krf-Latn-VU", + "krh": "krh-Latn-NG", + "kri": "kri-Latn-SL", + "krj": "krj-Latn-PH", + "krk": "krk-Cyrl-RU", + "krl": "krl-Latn-RU", + "krn": "krn-Latn-LR", + "krp": "krp-Latn-NG", + "krr": "krr-Khmr-KH", + "krs": "krs-Latn-ZZ", + "krt": "krt-Latn-NE", + "kru": "kru-Deva-IN", + "krv": "krv-Khmr-KH", + "krw": "krw-Latn-LR", + "krx": "krx-Latn-SN", + "kry": "kry-Latn-AZ", + "krz": "krz-Latn-ID", + "ks": "ks-Arab-IN", + "ksa": "ksa-Latn-NG", + "ksb": "ksb-Latn-TZ", + "ksc": "ksc-Latn-PH", + "ksd": "ksd-Latn-ZZ", + "kse": "kse-Latn-PG", + "ksf": "ksf-Latn-CM", + "ksg": "ksg-Latn-SB", + "ksh": "ksh-Latn-DE", + "ksi": "ksi-Latn-PG", + "ksj": "ksj-Latn-ZZ", + "ksk": "ksk-Latn-US", + "ksl": "ksl-Latn-PG", + "ksm": "ksm-Latn-NG", + "ksn": "ksn-Latn-PH", + "kso": "kso-Latn-NG", + "ksp": "ksp-Latn-CF", + "ksq": "ksq-Latn-NG", + "ksr": "ksr-Latn-ZZ", + "kss": "kss-Latn-LR", + "kst": "kst-Latn-BF", + "ksu": "ksu-Mymr-IN", + "ksv": "ksv-Latn-CD", + "ksw": "ksw-Mymr-MM", + "ksw-Latn": "ksw-Latn-MM", + "ksx": "ksx-Latn-ID", + "ksz": "ksz-Deva-IN", + "kta": "kta-Latn-VN", + "ktb": "ktb-Ethi-ZZ", + "ktc": "ktc-Latn-NG", + "ktd": "ktd-Latn-AU", + "ktf": "ktf-Latn-CD", + "ktg": "ktg-Latn-AU", + "kth": "kth-Latn-TD", + "kti": "kti-Latn-ID", + "ktj": "ktj-Latn-CI", + "ktk": "ktk-Latn-PG", + "ktl": "ktl-Arab-IR", + "ktm": "ktm-Latn-ZZ", + "ktn": "ktn-Latn-BR", + "kto": "kto-Latn-ZZ", + "ktp": "ktp-Plrd-CN", + "ktq": "ktq-Latn-PH", + "ktr": "dtp-Latn-MY", + "kts": "kts-Latn-ID", + "ktt": "ktt-Latn-ID", + "ktu": "ktu-Latn-CD", + "ktv": "ktv-Latn-VN", + "ktw": "ktw-Latn-US", + "ktx": "ktx-Latn-BR", + "kty": "kty-Latn-CD", + "ktz": "ktz-Latn-NA", + "ku": "ku-Latn-TR", + "ku-Arab": "ku-Arab-IQ", + "ku-LB": "ku-Arab-LB", + "ku-Yezi": "ku-Yezi-GE", + "kub": "kub-Latn-ZZ", + "kuc": "kuc-Latn-ID", + "kud": "kud-Latn-ZZ", + "kue": "kue-Latn-ZZ", + "kuf": "kuf-Laoo-LA", + "kug": "kug-Latn-NG", + "kuh": "kuh-Latn-NG", + "kui": "kui-Latn-BR", + "kuj": "kuj-Latn-ZZ", + "kuk": "kuk-Latn-ID", + "kul": "kul-Latn-NG", + "kum": "kum-Cyrl-RU", + "kun": "kun-Latn-ZZ", + "kuo": "kuo-Latn-PG", + "kup": "kup-Latn-ZZ", + "kuq": "kuq-Latn-BR", + "kus": "kus-Latn-ZZ", + "kut": "kut-Latn-CA", + "kuu": "kuu-Latn-US", + "kuv": "kuv-Latn-ID", + "kuw": "kuw-Latn-CF", + "kux": "kux-Latn-AU", + "kuy": "kuy-Latn-AU", + "kuz": "kuz-Latn-CL", + "kv": "kv-Cyrl-RU", + "kva": "kva-Cyrl-RU", + "kvb": "kvb-Latn-ID", + "kvc": "kvc-Latn-PG", + "kvd": "kvd-Latn-ID", + "kve": "kve-Latn-MY", + "kvf": "kvf-Latn-TD", + "kvg": "kvg-Latn-ZZ", + "kvh": "kvh-Latn-ID", + "kvi": "kvi-Latn-TD", + "kvj": "kvj-Latn-CM", + "kvl": "kvl-Latn-MM", + "kvm": "kvm-Latn-CM", + "kvn": "kvn-Latn-CO", + "kvo": "kvo-Latn-ID", + "kvp": "kvp-Latn-ID", + "kvq": "kvq-Mymr-MM", + "kvq-Latn": "kvq-Latn-MM", + "kvr": "kvr-Latn-ID", + "kvt": "kvt-Mymr-MM", + "kvv": "kvv-Latn-ID", + "kvw": "kvw-Latn-ID", + "kvx": "kvx-Arab-PK", + "kvy": "kvy-Kali-MM", + "kvz": "kvz-Latn-ID", + "kw": "kw-Latn-GB", + "kwa": "kwa-Latn-BR", + "kwb": "kwb-Latn-NG", + "kwc": "kwc-Latn-CG", + "kwd": "kwd-Latn-SB", + "kwe": "kwe-Latn-ID", + "kwf": "kwf-Latn-SB", + "kwg": "kwg-Latn-TD", + "kwh": "kwh-Latn-ID", + "kwi": "kwi-Latn-CO", + "kwj": "kwj-Latn-ZZ", + "kwk": "kwk-Latn-CA", + "kwl": "kwl-Latn-NG", + "kwm": "kwm-Latn-NA", + "kwn": "kwn-Latn-NA", + "kwo": "kwo-Latn-ZZ", + "kwp": "kwp-Latn-CI", + "kwq": "yam-Latn-ZZ", + "kwr": "kwr-Latn-ID", + "kws": "kws-Latn-CD", + "kwt": "kwt-Latn-ID", + "kwu": "kwu-Latn-CM", + "kwv": "kwv-Latn-TD", + "kww": "kww-Latn-SR", + "kwy": "kwy-Latn-CD", + "kwz": "kwz-Latn-AO", + "kxa": "kxa-Latn-ZZ", + "kxb": "kxb-Latn-CI", + "kxc": "kxc-Ethi-ZZ", + "kxd": "kxd-Latn-BN", + "kxd-Arab": "kxd-Arab-BN", + "kxe": "tvd-Latn-ZZ", + "kxf": "kxf-Mymr-MM", + "kxf-Latn": "kxf-Latn-MM", + "kxi": "kxi-Latn-MY", + "kxj": "kxj-Latn-TD", + "kxk": "kxk-Mymr-MM", + "kxl": "kru-Deva-IN", + "kxm": "kxm-Thai-TH", + "kxn": "kxn-Latn-MY", + "kxo": "kxo-Latn-BR", + "kxp": "kxp-Arab-PK", + "kxq": "kxq-Latn-ID", + "kxr": "kxr-Latn-PG", + "kxt": "kxt-Latn-PG", + "kxv": "kxv-Orya-IN", + "kxv-Latn": "kxv-Latn-IN", + "kxv-Telu": "kxv-Telu-IN", + "kxw": "kxw-Latn-ZZ", + "kxx": "kxx-Latn-CG", + "kxy": "kxy-Latn-VN", + "kxz": "kxz-Latn-ZZ", + "ky": "ky-Cyrl-KG", + "ky-Arab": "ky-Arab-CN", + "ky-CN": "ky-Arab-CN", + "ky-Latn": "ky-Latn-TR", + "ky-TR": "ky-Latn-TR", + "kya": "kya-Latn-TZ", + "kyb": "kyb-Latn-PH", + "kyc": "kyc-Latn-PG", + "kyd": "kyd-Latn-ID", + "kye": "kye-Latn-ZZ", + "kyf": "kyf-Latn-CI", + "kyg": "kyg-Latn-PG", + "kyh": "kyh-Latn-US", + "kyi": "kyi-Latn-MY", + "kyj": "kyj-Latn-PH", + "kyk": "kyk-Latn-PH", + "kyl": "kyl-Latn-US", + "kym": "kym-Latn-CF", + "kyn": "kyn-Latn-PH", + "kyo": "kyo-Latn-ID", + "kyq": "kyq-Latn-TD", + "kyr": "kyr-Latn-BR", + "kys": "kys-Latn-MY", + "kyt": "kyt-Latn-ID", + "kyu": "kyu-Kali-MM", + "kyu-Latn": "kyu-Latn-MM", + "kyu-Mymr": "kyu-Mymr-MM", + "kyv": "kyv-Deva-NP", + "kyw": "kyw-Deva-IN", + "kyw-Beng": "kyw-Beng-IN", + "kyw-Orya": "kyw-Orya-IN", + "kyx": "kyx-Latn-ZZ", + "kyy": "kyy-Latn-PG", + "kyz": "kyz-Latn-BR", + "kza": "kza-Latn-BF", + "kzb": "kzb-Latn-ID", + "kzc": "kzc-Latn-CI", + "kzd": "kzd-Latn-ID", + "kze": "kze-Latn-PG", + "kzf": "kzf-Latn-ID", + "kzh": "dgl-Arab-ZZ", + "kzi": "kzi-Latn-MY", + "kzj": "dtp-Latn-MY", + "kzk": "kzk-Latn-SB", + "kzl": "kzl-Latn-ID", + "kzm": "kzm-Latn-ID", + "kzn": "kzn-Latn-MW", + "kzo": "kzo-Latn-GA", + "kzp": "kzp-Latn-ID", + "kzr": "kzr-Latn-ZZ", + "kzs": "kzs-Latn-MY", + "kzt": "dtp-Latn-MY", + "kzu": "kzu-Latn-ID", + "kzv": "kzv-Latn-ID", + "kzw": "kzw-Latn-BR", + "kzx": "kzx-Latn-ID", + "kzy": "kzy-Latn-CD", + "kzz": "kzz-Latn-ID", + "la": "la-Latn-VA", + "laa": "laa-Latn-PH", + "lab": "lab-Lina-GR", + "lac": "lac-Latn-MX", + "lad": "lad-Hebr-IL", + "lae": "lae-Deva-IN", + "lae-Tibt": "lae-Tibt-IN", + "lag": "lag-Latn-TZ", + "lah": "lah-Arab-PK", + "lai": "lai-Latn-MW", + "laj": "laj-Latn-UG", + "lal": "lal-Latn-CD", + "lam": "lam-Latn-ZM", + "lan": "lan-Latn-NG", + "lap": "lap-Latn-TD", + "laq": "laq-Latn-VN", + "lar": "lar-Latn-GH", + "las": "las-Latn-ZZ", + "lau": "lau-Latn-ID", + "law": "law-Latn-ID", + "lax": "lax-Latn-IN", + "lax-Beng": "lax-Beng-IN", + "laz": "laz-Latn-PG", + "lb": "lb-Latn-LU", + "lbb": "lbb-Latn-PG", + "lbc": "lbc-Lisu-CN", + "lbe": "lbe-Cyrl-RU", + "lbf": "lbf-Deva-IN", + "lbf-Tibt": "lbf-Tibt-CN", + "lbi": "lbi-Latn-CM", + "lbj": "lbj-Tibt-IN", + "lbj-Arab": "lbj-Arab-IN", + "lbl": "lbl-Latn-PH", + "lbm": "lbm-Deva-IN", + "lbn": "lbn-Latn-LA", + "lbn-Laoo": "lbn-Laoo-LA", + "lbo": "lbo-Laoo-LA", + "lbo-Latn": "lbo-Latn-US", + "lbq": "lbq-Latn-PG", + "lbr": "lbr-Deva-NP", + "lbt": "lbt-Latn-VN", + "lbu": "lbu-Latn-ZZ", + "lbv": "lbv-Latn-PG", + "lbw": "lbw-Latn-ID", + "lbx": "lbx-Latn-ID", + "lby": "lby-Latn-AU", + "lbz": "lbz-Latn-AU", + "lcc": "lcc-Latn-ID", + "lcd": "lcd-Latn-ID", + "lce": "lce-Latn-ID", + "lcf": "lcf-Latn-ID", + "lch": "lch-Latn-AO", + "lcl": "lcl-Latn-ID", + "lcm": "lcm-Latn-ZZ", + "lcp": "lcp-Thai-CN", + "lcq": "lcq-Latn-ID", + "lcs": "lcs-Latn-ID", + "lda": "lda-Latn-CI", + "ldb": "ldb-Latn-ZZ", + "ldd": "ldd-Latn-NG", + "ldg": "ldg-Latn-NG", + "ldh": "ldh-Latn-NG", + "ldi": "ldi-Latn-CG", + "ldj": "ldj-Latn-NG", + "ldk": "ldk-Latn-NG", + "ldl": "ldl-Latn-NG", + "ldm": "ldm-Latn-GN", + "ldn": "ldn-Latn-001", + "ldo": "ldo-Latn-NG", + "ldp": "ldp-Latn-NG", + "ldq": "ldq-Latn-NG", + "lea": "lea-Latn-CD", + "leb": "leb-Latn-ZM", + "lec": "lec-Latn-BO", + "led": "led-Latn-ZZ", + "lee": "lee-Latn-ZZ", + "lef": "lef-Latn-GH", + "leh": "leh-Latn-ZM", + "lei": "lei-Latn-PG", + "lej": "lej-Latn-CD", + "lek": "lek-Latn-PG", + "lel": "lel-Latn-CD", + "lem": "lem-Latn-ZZ", + "len": "len-Latn-HN", + "leo": "leo-Latn-CM", + "lep": "lep-Lepc-IN", + "leq": "leq-Latn-ZZ", + "ler": "ler-Latn-PG", + "les": "les-Latn-CD", + "let": "let-Latn-PG", + "leu": "leu-Latn-ZZ", + "lev": "lev-Latn-ID", + "lew": "lew-Latn-ID", + "lex": "lex-Latn-ID", + "ley": "ley-Latn-ID", + "lez": "lez-Cyrl-RU", + "lfa": "lfa-Latn-CM", + "lfn": "lfn-Latn-001", + "lfn-Cyrl": "lfn-Cyrl-001", + "lg": "lg-Latn-UG", + "lga": "lga-Latn-SB", + "lgb": "lgb-Latn-SB", + "lgg": "lgg-Latn-ZZ", + "lgh": "lgh-Latn-VN", + "lgi": "lgi-Latn-ID", + "lgk": "lgk-Latn-VU", + "lgl": "lgl-Latn-SB", + "lgm": "lgm-Latn-CD", + "lgn": "lgn-Latn-ET", + "lgo": "lgo-Latn-SS", + "lgq": "lgq-Latn-GH", + "lgr": "lgr-Latn-SB", + "lgt": "lgt-Latn-PG", + "lgu": "lgu-Latn-SB", + "lgz": "lgz-Latn-CD", + "lha": "lha-Latn-VN", + "lhh": "lhh-Latn-ID", + "lhi": "lhi-Latn-CN", + "lhm": "lhm-Deva-NP", + "lhn": "lhn-Latn-MY", + "lhs": "lhs-Syrc-SY", + "lht": "lht-Latn-VU", + "lhu": "lhu-Latn-CN", + "li": "li-Latn-NL", + "lia": "lia-Latn-ZZ", + "lib": "lib-Latn-PG", + "lic": "lic-Latn-CN", + "lid": "lid-Latn-ZZ", + "lie": "lie-Latn-CD", + "lif": "lif-Deva-NP", + "lif-Limb": "lif-Limb-IN", + "lig": "lig-Latn-ZZ", + "lih": "lih-Latn-ZZ", + "lij": "lij-Latn-IT", + "lik": "lik-Latn-CD", + "lil": "lil-Latn-CA", + "lio": "lio-Latn-ID", + "lip": "lip-Latn-GH", + "liq": "liq-Latn-ET", + "lir": "lir-Latn-LR", + "lis": "lis-Lisu-CN", + "liu": "liu-Latn-SD", + "liv": "liv-Latn-LV", + "liw": "liw-Latn-ID", + "lix": "lix-Latn-ID", + "liy": "liy-Latn-CF", + "liz": "liz-Latn-CD", + "lja": "lja-Latn-AU", + "lje": "lje-Latn-ID", + "lji": "lji-Latn-ID", + "ljl": "ljl-Latn-ID", + "ljp": "ljp-Latn-ID", + "ljw": "ljw-Latn-AU", + "ljx": "ljx-Latn-AU", + "lka": "lka-Latn-TL", + "lkb": "lkb-Latn-KE", + "lkc": "lkc-Latn-VN", + "lkd": "lkd-Latn-BR", + "lke": "lke-Latn-UG", + "lkh": "lkh-Tibt-BT", + "lki": "lki-Arab-IR", + "lkj": "lkj-Latn-MY", + "lkl": "lkl-Latn-PG", + "lkm": "lkm-Latn-AU", + "lkn": "lkn-Latn-VU", + "lko": "lko-Latn-KE", + "lkr": "lkr-Latn-SS", + "lks": "lks-Latn-KE", + "lkt": "lkt-Latn-US", + "lku": "lku-Latn-AU", + "lky": "lky-Latn-SS", + "lla": "lla-Latn-NG", + "llb": "llb-Latn-MZ", + "llc": "llc-Latn-GN", + "lld": "lld-Latn-IT", + "lle": "lle-Latn-ZZ", + "llf": "llf-Latn-PG", + "llg": "llg-Latn-ID", + "lli": "lli-Latn-CG", + "llj": "llj-Latn-AU", + "llk": "llk-Latn-MY", + "lll": "lll-Latn-PG", + "llm": "llm-Latn-ID", + "lln": "lln-Latn-ZZ", + "llp": "llp-Latn-VU", + "llq": "llq-Latn-ID", + "llu": "llu-Latn-SB", + "llx": "llx-Latn-FJ", + "lma": "lma-Latn-GN", + "lmb": "lmb-Latn-VU", + "lmc": "lmc-Latn-AU", + "lmd": "lmd-Latn-SD", + "lme": "lme-Latn-TD", + "lmf": "lmf-Latn-ID", + "lmg": "lmg-Latn-PG", + "lmh": "lmh-Deva-NP", + "lmi": "lmi-Latn-CD", + "lmj": "lmj-Latn-ID", + "lmk": "lmk-Latn-IN", + "lmk-Mymr": "lmk-Mymr-IN", + "lml": "lml-Latn-VU", + "lmn": "lmn-Telu-IN", + "lmo": "lmo-Latn-IT", + "lmp": "lmp-Latn-ZZ", + "lmq": "lmq-Latn-ID", + "lmr": "lmr-Latn-ID", + "lmu": "lmu-Latn-VU", + "lmv": "lmv-Latn-FJ", + "lmw": "lmw-Latn-US", + "lmx": "lmx-Latn-CM", + "lmy": "lmy-Latn-ID", + "ln": "ln-Latn-CD", + "lna": "lna-Latn-CF", + "lnb": "lnb-Latn-NA", + "lnd": "lnd-Latn-ID", + "lnh": "lnh-Latn-MY", + "lni": "lni-Latn-PG", + "lnj": "lnj-Latn-AU", + "lnl": "lnl-Latn-CF", + "lnm": "lnm-Latn-PG", + "lnn": "lnn-Latn-VU", + "lns": "lns-Latn-ZZ", + "lnu": "lnu-Latn-ZZ", + "lnw": "lnw-Latn-AU", + "lnz": "lnz-Latn-CD", + "lo": "lo-Laoo-LA", + "loa": "loa-Latn-ID", + "lob": "lob-Latn-BF", + "loc": "loc-Latn-PH", + "loe": "loe-Latn-ID", + "log": "log-Latn-CD", + "loh": "loh-Latn-SS", + "loi": "loi-Latn-CI", + "loj": "loj-Latn-ZZ", + "lok": "lok-Latn-ZZ", + "lol": "lol-Latn-CD", + "lom": "lom-Latn-LR", + "lon": "lon-Latn-MW", + "loo": "loo-Latn-CD", + "lop": "lop-Latn-NG", + "loq": "loq-Latn-CD", + "lor": "lor-Latn-ZZ", + "los": "los-Latn-ZZ", + "lot": "lot-Latn-SS", + "lot-Arab": "lot-Arab-SS", + "lou": "lou-Latn-US", + "low": "low-Latn-MY", + "lox": "lox-Latn-ID", + "loy": "loy-Deva-NP", + "loy-Tibt": "loy-Tibt-NP", + "loz": "loz-Latn-ZM", + "lpa": "lpa-Latn-VU", + "lpe": "lpe-Latn-ID", + "lpn": "lpn-Latn-MM", + "lpo": "lpo-Plrd-CN", + "lpo-Lisu": "lpo-Lisu-CN", + "lpx": "lpx-Latn-SS", + "lqr": "lqr-Latn-SS", + "lra": "lra-Latn-MY", + "lrc": "lrc-Arab-IR", + "lrg": "lrg-Latn-AU", + "lri": "lri-Latn-KE", + "lrk": "lrk-Arab-PK", + "lrl": "lrl-Arab-IR", + "lrm": "lrm-Latn-KE", + "lrn": "lrn-Latn-ID", + "lro": "lro-Latn-SD", + "lrt": "lrt-Latn-ID", + "lrv": "lrv-Latn-VU", + "lrz": "lrz-Latn-VU", + "lsa": "lsa-Arab-IR", + "lsd": "lsd-Hebr-IL", + "lse": "lse-Latn-CD", + "lsi": "lsi-Latn-MM", + "lsm": "lsm-Latn-UG", + "lsr": "lsr-Latn-PG", + "lss": "lss-Arab-PK", + "lt": "lt-Latn-LT", + "ltg": "ltg-Latn-LV", + "lth": "lth-Latn-UG", + "lti": "lti-Latn-ID", + "ltn": "ltn-Latn-BR", + "lto": "lto-Latn-KE", + "lts": "lts-Latn-KE", + "ltu": "ltu-Latn-ID", + "lu": "lu-Latn-CD", + "lua": "lua-Latn-CD", + "luc": "luc-Latn-UG", + "lud": "lud-Latn-RU", + "lue": "lue-Latn-ZM", + "luf": "luf-Latn-PG", + "lui": "lui-Latn-US", + "luj": "luj-Latn-CD", + "luk": "luk-Tibt-BT", + "lul": "lul-Latn-SS", + "lum": "lum-Latn-AO", + "lun": "lun-Latn-ZM", + "luo": "luo-Latn-KE", + "lup": "lup-Latn-GA", + "luq": "luq-Latn-CU", + "lur": "lur-Latn-ID", + "lus": "lus-Latn-IN", + "lus-Beng": "lus-Beng-BD", + "lus-Brai": "lus-Brai-IN", + "lut": "lut-Latn-US", + "luu": "luu-Deva-NP", + "luv": "luv-Arab-OM", + "luw": "luw-Latn-CM", + "luy": "luy-Latn-KE", + "luz": "luz-Arab-IR", + "lv": "lv-Latn-LV", + "lva": "lva-Latn-TL", + "lvi": "lvi-Latn-LA", + "lvk": "lvk-Latn-SB", + "lvu": "lvu-Latn-ID", + "lwa": "lwa-Latn-CD", + "lwe": "lwe-Latn-ID", + "lwg": "lwg-Latn-KE", + "lwh": "lwh-Latn-VN", + "lwl": "lwl-Thai-TH", + "lwm": "lwm-Thai-CN", + "lwo": "lwo-Latn-SS", + "lwo-ZA": "lwo-Latn-ZA", + "lwt": "lwt-Latn-ID", + "lww": "lww-Latn-VU", + "lxm": "lxm-Latn-PG", + "lya": "lya-Tibt-BT", + "lyn": "lyn-Latn-ZM", + "lzh": "lzh-Hans-CN", + "lzl": "lzl-Latn-VU", + "lzn": "lzn-Latn-MM", + "lzz": "lzz-Latn-TR", + "maa": "maa-Latn-MX", + "mab": "mab-Latn-MX", + "mad": "mad-Latn-ID", + "mae": "mae-Latn-NG", + "maf": "maf-Latn-CM", + "mag": "mag-Deva-IN", + "mai": "mai-Deva-IN", + "maj": "maj-Latn-MX", + "mak": "mak-Latn-ID", + "mam": "mam-Latn-GT", + "man": "man-Latn-GM", + "man-GN": "man-Nkoo-GN", + "man-Nkoo": "man-Nkoo-GN", + "maq": "maq-Latn-MX", + "mas": "mas-Latn-KE", + "mat": "mat-Latn-MX", + "mau": "mau-Latn-MX", + "mav": "mav-Latn-BR", + "maw": "maw-Latn-ZZ", + "max": "max-Latn-ID", + "maz": "maz-Latn-MX", + "mba": "mba-Latn-PH", + "mbb": "mbb-Latn-PH", + "mbc": "mbc-Latn-BR", + "mbd": "mbd-Latn-PH", + "mbf": "mbf-Latn-SG", + "mbh": "mbh-Latn-ZZ", + "mbi": "mbi-Latn-PH", + "mbj": "mbj-Latn-BR", + "mbk": "mbk-Latn-PG", + "mbl": "mbl-Latn-BR", + "mbm": "mbm-Latn-CG", + "mbn": "mbn-Latn-CO", + "mbo": "mbo-Latn-ZZ", + "mbp": "mbp-Latn-CO", + "mbq": "mbq-Latn-ZZ", + "mbr": "mbr-Latn-CO", + "mbs": "mbs-Latn-PH", + "mbt": "mbt-Latn-PH", + "mbu": "mbu-Latn-ZZ", + "mbv": "mbv-Latn-GN", + "mbw": "mbw-Latn-ZZ", + "mbx": "mbx-Latn-PG", + "mby": "mby-Arab-PK", + "mbz": "mbz-Latn-MX", + "mca": "mca-Latn-PY", + "mcb": "mcb-Latn-PE", + "mcc": "mcc-Latn-PG", + "mcd": "mcd-Latn-PE", + "mce": "mce-Latn-MX", + "mcf": "mcf-Latn-PE", + "mcg": "mcg-Latn-VE", + "mch": "mch-Latn-VE", + "mci": "mci-Latn-ZZ", + "mcj": "mcj-Latn-NG", + "mck": "mck-Latn-AO", + "mcl": "mcl-Latn-CO", + "mcm": "mcm-Latn-MY", + "mcn": "mcn-Latn-TD", + "mco": "mco-Latn-MX", + "mcp": "mcp-Latn-ZZ", + "mcq": "mcq-Latn-ZZ", + "mcr": "mcr-Latn-ZZ", + "mcs": "mcs-Latn-CM", + "mct": "mct-Latn-CM", + "mcu": "mcu-Latn-ZZ", + "mcv": "mcv-Latn-PG", + "mcw": "mcw-Latn-TD", + "mcx": "mcx-Latn-CF", + "mcy": "mcy-Latn-PG", + "mcz": "mcz-Latn-PG", + "mda": "mda-Latn-ZZ", + "mdb": "mdb-Latn-PG", + "mdc": "mdc-Latn-PG", + "mdd": "mdd-Latn-CM", + "mde": "mde-Arab-ZZ", + "mdf": "mdf-Cyrl-RU", + "mdg": "mdg-Latn-TD", + "mdh": "mdh-Latn-PH", + "mdi": "mdi-Latn-CD", + "mdj": "mdj-Latn-ZZ", + "mdk": "mdk-Latn-CD", + "mdm": "mdm-Latn-CD", + "mdn": "mdn-Latn-CF", + "mdp": "mdp-Latn-CD", + "mdq": "mdq-Latn-CD", + "mdr": "mdr-Latn-ID", + "mds": "mds-Latn-PG", + "mdt": "mdt-Latn-CG", + "mdu": "mdu-Latn-CG", + "mdv": "mdv-Latn-MX", + "mdw": "mdw-Latn-CG", + "mdx": "mdx-Ethi-ZZ", + "mdy": "mdy-Ethi-ET", + "mdy-Latn": "mdy-Latn-ET", + "mdz": "mdz-Latn-BR", + "mea": "mea-Latn-CM", + "meb": "meb-Latn-PG", + "mec": "mec-Latn-AU", + "med": "med-Latn-ZZ", + "mee": "mee-Latn-ZZ", + "meh": "meh-Latn-MX", + "mej": "mej-Latn-ID", + "mek": "mek-Latn-ZZ", + "mel": "mel-Latn-MY", + "mem": "mem-Latn-AU", + "men": "men-Latn-SL", + "meo": "meo-Latn-MY", + "meo-Arab": "meo-Arab-MY", + "mep": "mep-Latn-AU", + "meq": "meq-Latn-CM", + "mer": "mer-Latn-KE", + "mes": "mes-Latn-TD", + "met": "met-Latn-ZZ", + "meu": "meu-Latn-ZZ", + "mev": "mev-Latn-LR", + "mew": "mew-Latn-NG", + "mey": "mey-Latn-MR", + "mey-Arab": "mey-Arab-MR", + "mez": "mez-Latn-US", + "mfa": "mfa-Arab-TH", + "mfb": "mfb-Latn-ID", + "mfc": "mfc-Latn-CD", + "mfd": "mfd-Latn-CM", + "mfe": "mfe-Latn-MU", + "mff": "mff-Latn-CM", + "mfg": "mfg-Latn-GN", + "mfg-Arab": "mfg-Arab-GN", + "mfh": "mfh-Latn-CM", + "mfi": "mfi-Arab-CM", + "mfi-Latn": "mfi-Latn-CM", + "mfj": "mfj-Latn-CM", + "mfk": "mfk-Latn-CM", + "mfl": "mfl-Latn-NG", + "mfm": "mfm-Latn-NG", + "mfn": "mfn-Latn-ZZ", + "mfo": "mfo-Latn-ZZ", + "mfp": "mfp-Latn-ID", + "mfq": "mfq-Latn-ZZ", + "mfr": "mfr-Latn-AU", + "mft": "mft-Latn-PG", + "mfu": "mfu-Latn-AO", + "mfv": "mfv-Latn-GW", + "mfw": "mfw-Latn-PG", + "mfx": "mfx-Latn-ET", + "mfx-Ethi": "mfx-Ethi-ET", + "mfy": "mfy-Latn-MX", + "mfz": "mfz-Latn-SS", + "mg": "mg-Latn-MG", + "mgb": "mgb-Latn-TD", + "mgc": "mgc-Latn-SS", + "mgd": "mgd-Latn-SS", + "mgd-Arab": "mgd-Arab-SS", + "mge": "mge-Latn-TD", + "mgf": "mgf-Latn-ID", + "mgg": "mgg-Latn-CM", + "mgh": "mgh-Latn-MZ", + "mgi": "mgi-Latn-NG", + "mgj": "mgj-Latn-NG", + "mgk": "mgk-Latn-ID", + "mgl": "mgl-Latn-ZZ", + "mgm": "mgm-Latn-TL", + "mgn": "mgn-Latn-CF", + "mgo": "mgo-Latn-CM", + "mgp": "mgp-Deva-NP", + "mgq": "mgq-Latn-TZ", + "mgr": "mgr-Latn-ZM", + "mgs": "mgs-Latn-TZ", + "mgt": "mgt-Latn-PG", + "mgu": "mgu-Latn-PG", + "mgv": "mgv-Latn-TZ", + "mgw": "mgw-Latn-TZ", + "mgy": "mgy-Latn-TZ", + "mgz": "mgz-Latn-TZ", + "mh": "mh-Latn-MH", + "mhb": "mhb-Latn-GA", + "mhc": "mhc-Latn-MX", + "mhd": "mhd-Latn-TZ", + "mhe": "mhe-Latn-MY", + "mhf": "mhf-Latn-PG", + "mhg": "mhg-Latn-AU", + "mhi": "mhi-Latn-ZZ", + "mhj": "mhj-Arab-AF", + "mhk": "mhk-Latn-CM", + "mhl": "mhl-Latn-ZZ", + "mhm": "mhm-Latn-MZ", + "mhn": "mhn-Latn-IT", + "mho": "mho-Latn-ZM", + "mhp": "mhp-Latn-ID", + "mhq": "mhq-Latn-US", + "mhs": "mhs-Latn-ID", + "mht": "mht-Latn-VE", + "mhu": "mhu-Latn-IN", + "mhw": "mhw-Latn-BW", + "mhx": "mhx-Latn-MM", + "mhy": "mhy-Latn-ID", + "mhz": "mhz-Latn-ID", + "mi": "mi-Latn-NZ", + "mia": "mia-Latn-US", + "mib": "mib-Latn-MX", + "mic": "mic-Latn-CA", + "mid": "mid-Mand-IQ", + "mie": "mie-Latn-MX", + "mif": "mif-Latn-ZZ", + "mig": "mig-Latn-MX", + "mih": "mih-Latn-MX", + "mii": "mii-Latn-MX", + "mij": "mij-Latn-CM", + "mik": "mik-Latn-US", + "mil": "mil-Latn-MX", + "mim": "mim-Latn-MX", + "min": "min-Latn-ID", + "mio": "mio-Latn-MX", + "mip": "mip-Latn-MX", + "miq": "miq-Latn-NI", + "mir": "mir-Latn-MX", + "mit": "mit-Latn-MX", + "miu": "miu-Latn-MX", + "miw": "miw-Latn-ZZ", + "mix": "mix-Latn-MX", + "miy": "miy-Latn-MX", + "miz": "miz-Latn-MX", + "mjb": "mjb-Latn-TL", + "mjc": "mjc-Latn-MX", + "mjd": "mjd-Latn-US", + "mje": "mje-Latn-TD", + "mjg": "mjg-Latn-CN", + "mjh": "mjh-Latn-TZ", + "mji": "mji-Latn-CN", + "mjj": "mjj-Latn-PG", + "mjk": "mjk-Latn-PG", + "mjl": "mjl-Deva-IN", + "mjl-Takr": "mjl-Takr-IN", + "mjm": "mjm-Latn-PG", + "mjn": "mjn-Latn-PG", + "mjq": "mjq-Mlym-IN", + "mjr": "mjr-Mlym-IN", + "mjs": "mjs-Latn-NG", + "mjt": "mjt-Deva-IN", + "mjt-Beng": "mjt-Beng-BD", + "mju": "mju-Telu-IN", + "mjv": "mjv-Mlym-IN", + "mjw": "mjw-Latn-IN", + "mjx": "mjx-Latn-BD", + "mjx-Beng": "mjx-Beng-BD", + "mjy": "mjy-Latn-US", + "mjz": "mjz-Deva-NP", + "mk": "mk-Cyrl-MK", + "mka": "mka-Latn-CI", + "mkb": "mkb-Deva-IN", + "mkc": "mkc-Latn-PG", + "mke": "mke-Deva-IN", + "mkf": "mkf-Latn-NG", + "mki": "mki-Arab-ZZ", + "mkj": "mkj-Latn-FM", + "mkk": "mkk-Latn-CM", + "mkl": "mkl-Latn-ZZ", + "mkm": "mkm-Thai-TH", + "mkn": "mkn-Latn-ID", + "mko": "mko-Latn-NG", + "mkp": "mkp-Latn-ZZ", + "mkr": "mkr-Latn-PG", + "mks": "mks-Latn-MX", + "mkt": "mkt-Latn-NC", + "mku": "mku-Latn-GN", + "mkv": "mkv-Latn-VU", + "mkw": "mkw-Latn-ZZ", + "mkx": "mkx-Latn-PH", + "mky": "mky-Latn-ID", + "mkz": "mkz-Latn-TL", + "ml": "ml-Mlym-IN", + "mla": "mla-Latn-VU", + "mlb": "mlb-Latn-CM", + "mlc": "mlc-Latn-VN", + "mle": "mle-Latn-ZZ", + "mlf": "mlf-Thai-LA", + "mlf-Latn": "mlf-Latn-LA", + "mlh": "mlh-Latn-PG", + "mli": "mli-Latn-ID", + "mlj": "mlj-Latn-TD", + "mlk": "mlk-Latn-KE", + "mll": "mll-Latn-VU", + "mln": "mln-Latn-SB", + "mlo": "mlo-Latn-SN", + "mlp": "mlp-Latn-ZZ", + "mlq": "mlq-Latn-SN", + "mlq-Arab": "mlq-Arab-SN", + "mlr": "mlr-Latn-CM", + "mls": "mls-Latn-SD", + "mlu": "mlu-Latn-SB", + "mlv": "mlv-Latn-VU", + "mlw": "mlw-Latn-CM", + "mlx": "mlx-Latn-VU", + "mlz": "mlz-Latn-PH", + "mma": "mma-Latn-NG", + "mmb": "mmb-Latn-ID", + "mmc": "mmc-Latn-MX", + "mmd": "mmd-Latn-CN", + "mmd-Hans": "mmd-Hans-CN", + "mmd-Hant": "mmd-Hant-CN", + "mme": "mme-Latn-VU", + "mmf": "mmf-Latn-NG", + "mmg": "mmg-Latn-VU", + "mmh": "mmh-Latn-BR", + "mmi": "mmi-Latn-PG", + "mmm": "mmm-Latn-VU", + "mmn": "mmn-Latn-PH", + "mmo": "mmo-Latn-ZZ", + "mmp": "mmp-Latn-PG", + "mmq": "mmq-Latn-PG", + "mmr": "mmr-Latn-CN", + "mmt": "mmt-Latn-PG", + "mmu": "mmu-Latn-ZZ", + "mmv": "mmv-Latn-BR", + "mmw": "mmw-Latn-VU", + "mmx": "mmx-Latn-ZZ", + "mmy": "mmy-Latn-TD", + "mmz": "mmz-Latn-CD", + "mn": "mn-Cyrl-MN", + "mn-CN": "mn-Mong-CN", + "mn-Mong": "mn-Mong-CN", + "mna": "mna-Latn-ZZ", + "mnb": "mnb-Latn-ID", + "mnd": "mnd-Latn-BR", + "mne": "mne-Latn-TD", + "mnf": "mnf-Latn-ZZ", + "mng": "mng-Latn-VN", + "mnh": "mnh-Latn-CD", + "mni": "mni-Beng-IN", + "mnj": "mnj-Arab-AF", + "mnl": "mnl-Latn-VU", + "mnm": "mnm-Latn-PG", + "mnn": "mnn-Latn-VN", + "mnp": "mnp-Latn-CN", + "mnq": "mnq-Latn-MY", + "mnr": "mnr-Latn-US", + "mns": "mns-Cyrl-RU", + "mnu": "mnu-Latn-ID", + "mnv": "mnv-Latn-SB", + "mnw": "mnw-Mymr-MM", + "mnx": "mnx-Latn-ID", + "mny": "mny-Latn-MZ", + "mnz": "mnz-Latn-ID", + "mo": "ro-Latn-RO", + "moa": "moa-Latn-ZZ", + "moc": "moc-Latn-AR", + "mod": "mod-Latn-US", + "moe": "moe-Latn-CA", + "mog": "mog-Latn-ID", + "moh": "moh-Latn-CA", + "moi": "moi-Latn-NG", + "moj": "moj-Latn-CG", + "mok": "mok-Latn-ID", + "mom": "mom-Latn-NI", + "moo": "moo-Latn-VN", + "mop": "mop-Latn-BZ", + "moq": "moq-Latn-ID", + "mor": "mor-Latn-SD", + "mos": "mos-Latn-BF", + "mot": "mot-Latn-CO", + "mou": "mou-Latn-TD", + "mov": "mov-Latn-US", + "mow": "mow-Latn-CG", + "mox": "mox-Latn-ZZ", + "moy": "moy-Latn-ET", + "moy-Ethi": "moy-Ethi-ET", + "moz": "moz-Latn-TD", + "mpa": "mpa-Latn-TZ", + "mpb": "mpb-Latn-AU", + "mpc": "mpc-Latn-AU", + "mpd": "mpd-Latn-BR", + "mpe": "mpe-Latn-ET", + "mpe-Ethi": "mpe-Ethi-ET", + "mpg": "mpg-Latn-TD", + "mph": "mph-Latn-AU", + "mpi": "mpi-Latn-CM", + "mpj": "mpj-Latn-AU", + "mpk": "mpk-Latn-TD", + "mpl": "mpl-Latn-PG", + "mpm": "mpm-Latn-MX", + "mpn": "mpn-Latn-PG", + "mpo": "mpo-Latn-PG", + "mpp": "mpp-Latn-ZZ", + "mpq": "mpq-Latn-BR", + "mpr": "mpr-Latn-SB", + "mps": "mps-Latn-ZZ", + "mpt": "mpt-Latn-ZZ", + "mpu": "mpu-Latn-BR", + "mpv": "mpv-Latn-PG", + "mpw": "mpw-Latn-BR", + "mpx": "mpx-Latn-ZZ", + "mpy": "mpy-Latn-ID", + "mpz": "mpz-Thai-TH", + "mqa": "mqa-Latn-ID", + "mqb": "mqb-Latn-CM", + "mqc": "mqc-Latn-ID", + "mqe": "mqe-Latn-PG", + "mqf": "mqf-Latn-ID", + "mqg": "mqg-Latn-ID", + "mqh": "mqh-Latn-MX", + "mqi": "mqi-Latn-ID", + "mqj": "mqj-Latn-ID", + "mqk": "mqk-Latn-PH", + "mql": "mql-Latn-ZZ", + "mqm": "mqm-Latn-PF", + "mqn": "mqn-Latn-ID", + "mqo": "mqo-Latn-ID", + "mqp": "mqp-Latn-ID", + "mqq": "mqq-Latn-MY", + "mqr": "mqr-Latn-ID", + "mqs": "mqs-Latn-ID", + "mqu": "mqu-Latn-SS", + "mqv": "mqv-Latn-PG", + "mqw": "mqw-Latn-PG", + "mqx": "mqx-Latn-ID", + "mqx-Bugi": "mqx-Bugi-ID", + "mqy": "mqy-Latn-ID", + "mqz": "mqz-Latn-PG", + "mr": "mr-Deva-IN", + "mra": "mra-Thai-TH", + "mrb": "mrb-Latn-VU", + "mrc": "mrc-Latn-US", + "mrd": "mrd-Deva-NP", + "mrf": "mrf-Latn-ID", + "mrg": "mrg-Latn-IN", + "mrg-Beng": "mrg-Beng-IN", + "mrg-Deva": "mrg-Deva-IN", + "mrh": "mrh-Latn-IN", + "mrj": "mrj-Cyrl-RU", + "mrk": "mrk-Latn-NC", + "mrl": "mrl-Latn-FM", + "mrm": "mrm-Latn-VU", + "mrn": "mrn-Latn-SB", + "mro": "mro-Mroo-BD", + "mrp": "mrp-Latn-VU", + "mrq": "mrq-Latn-PF", + "mrr": "mrr-Deva-IN", + "mrs": "mrs-Latn-VU", + "mrt": "mrt-Latn-NG", + "mru": "mru-Latn-CM", + "mrv": "mrv-Latn-PF", + "mrw": "mrw-Latn-PH", + "mrw-Arab": "mrw-Arab-PH", + "mrx": "mrx-Latn-ID", + "mry": "mry-Latn-PH", + "mrz": "mrz-Latn-ID", + "ms": "ms-Latn-MY", + "ms-CC": "ms-Arab-CC", + "msb": "msb-Latn-PH", + "msc": "msc-Latn-GN", + "mse": "mse-Latn-TD", + "msf": "msf-Latn-ID", + "msg": "msg-Latn-ID", + "msh": "msh-Latn-MG", + "msi": "msi-Latn-MY", + "msj": "msj-Latn-CD", + "msk": "msk-Latn-PH", + "msl": "msl-Latn-ID", + "msm": "msm-Latn-PH", + "msn": "msn-Latn-VU", + "mso": "mso-Latn-ID", + "msp": "msp-Latn-BR", + "msq": "msq-Latn-NC", + "mss": "mss-Latn-ID", + "msu": "msu-Latn-PG", + "msv": "msv-Latn-CM", + "msw": "msw-Latn-GW", + "msx": "msx-Latn-PG", + "msy": "msy-Latn-PG", + "msz": "msz-Latn-PG", + "mt": "mt-Latn-MT", + "mta": "mta-Latn-PH", + "mtb": "mtb-Latn-CI", + "mtc": "mtc-Latn-ZZ", + "mtd": "mtd-Latn-ID", + "mte": "mte-Latn-SB", + "mtf": "mtf-Latn-ZZ", + "mtg": "mtg-Latn-ID", + "mth": "mth-Latn-ID", + "mti": "mti-Latn-ZZ", + "mtj": "mtj-Latn-ID", + "mtk": "mtk-Latn-CM", + "mtl": "mtl-Latn-NG", + "mtm": "mtm-Cyrl-RU", + "mtn": "mtn-Latn-NI", + "mto": "mto-Latn-MX", + "mtp": "mtp-Latn-BO", + "mtq": "mtq-Latn-VN", + "mtr": "mtr-Deva-IN", + "mts": "mts-Latn-PE", + "mtt": "mtt-Latn-VU", + "mtu": "mtu-Latn-MX", + "mtv": "mtv-Latn-PG", + "mtw": "mtw-Latn-PH", + "mtx": "mtx-Latn-MX", + "mty": "mty-Latn-PG", + "mua": "mua-Latn-CM", + "mub": "mub-Latn-TD", + "muc": "muc-Latn-CM", + "mud": "mud-Cyrl-RU", + "mue": "mue-Latn-EC", + "mug": "mug-Latn-CM", + "muh": "muh-Latn-SS", + "mui": "mui-Latn-ID", + "muj": "muj-Latn-TD", + "muk": "muk-Tibt-NP", + "mum": "mum-Latn-PG", + "muo": "muo-Latn-CM", + "muq": "muq-Latn-CN", + "mur": "mur-Latn-ZZ", + "mus": "mus-Latn-US", + "mut": "mut-Deva-IN", + "muu": "muu-Latn-KE", + "muv": "muv-Taml-IN", + "mux": "mux-Latn-PG", + "muy": "muy-Latn-CM", + "muz": "muz-Ethi-ET", + "muz-Latn": "muz-Latn-ET", + "mva": "mva-Latn-ZZ", + "mvd": "mvd-Latn-ID", + "mvf": "mvf-Mong-CN", + "mvf-Phag": "mvf-Phag-CN", + "mvg": "mvg-Latn-MX", + "mvh": "mvh-Latn-TD", + "mvk": "mvk-Latn-PG", + "mvl": "mvl-Latn-AU", + "mvn": "mvn-Latn-ZZ", + "mvo": "mvo-Latn-SB", + "mvp": "mvp-Latn-ID", + "mvq": "mvq-Latn-PG", + "mvr": "mvr-Latn-ID", + "mvs": "mvs-Latn-ID", + "mvt": "mvt-Latn-VU", + "mvu": "mvu-Latn-TD", + "mvv": "mvv-Latn-MY", + "mvw": "mvw-Latn-TZ", + "mvx": "mvx-Latn-ID", + "mvy": "mvy-Arab-PK", + "mvz": "mvz-Ethi-ET", + "mvz-Arab": "mvz-Arab-ET", + "mwa": "mwa-Latn-PG", + "mwb": "mwb-Latn-PG", + "mwc": "mwc-Latn-PG", + "mwe": "mwe-Latn-TZ", + "mwf": "mwf-Latn-AU", + "mwg": "mwg-Latn-PG", + "mwh": "mwh-Latn-PG", + "mwi": "mwi-Latn-VU", + "mwk": "mwk-Latn-ML", + "mwl": "mwl-Latn-PT", + "mwm": "mwm-Latn-TD", + "mwn": "mwn-Latn-ZM", + "mwo": "mwo-Latn-VU", + "mwp": "mwp-Latn-AU", + "mwq": "mwq-Latn-MM", + "mwr": "mwr-Deva-IN", + "mws": "mws-Latn-KE", + "mwt": "mwt-Mymr-MM", + "mwt-Thai": "mwt-Thai-TH", + "mwu": "mwu-Latn-SS", + "mwv": "mwv-Latn-ID", + "mww": "mww-Hmnp-US", + "mwz": "mwz-Latn-CD", + "mxa": "mxa-Latn-MX", + "mxb": "mxb-Latn-MX", + "mxc": "mxc-Latn-ZW", + "mxd": "mxd-Latn-ID", + "mxe": "mxe-Latn-VU", + "mxf": "mxf-Latn-CM", + "mxg": "mxg-Latn-AO", + "mxh": "mxh-Latn-CD", + "mxi": "mxi-Latn-ES", + "mxj": "mxj-Latn-IN", + "mxk": "mxk-Latn-PG", + "mxl": "mxl-Latn-BJ", + "mxm": "mxm-Latn-ZZ", + "mxn": "mxn-Latn-ID", + "mxo": "mxo-Latn-ZM", + "mxp": "mxp-Latn-MX", + "mxq": "mxq-Latn-MX", + "mxr": "mxr-Latn-MY", + "mxs": "mxs-Latn-MX", + "mxt": "mxt-Latn-MX", + "mxu": "mxu-Latn-CM", + "mxv": "mxv-Latn-MX", + "mxw": "mxw-Latn-PG", + "mxx": "mxx-Latn-CI", + "mxy": "mxy-Latn-MX", + "mxz": "mxz-Latn-ID", + "my": "my-Mymr-MM", + "myb": "myb-Latn-TD", + "myc": "myc-Latn-CD", + "mye": "mye-Latn-GA", + "myf": "myf-Latn-ET", + "myg": "myg-Latn-CM", + "myh": "myh-Latn-US", + "myj": "myj-Latn-SS", + "myk": "myk-Latn-ZZ", + "myl": "myl-Latn-ID", + "mym": "mym-Ethi-ZZ", + "myp": "myp-Latn-BR", + "myr": "myr-Latn-PE", + "myu": "myu-Latn-BR", + "myv": "myv-Cyrl-RU", + "myw": "myw-Latn-ZZ", + "myx": "myx-Latn-UG", + "myy": "myy-Latn-CO", + "myz": "myz-Mand-IR", + "mza": "mza-Latn-MX", + "mzd": "mzd-Latn-CM", + "mze": "mze-Latn-PG", + "mzh": "mzh-Latn-AR", + "mzi": "mzi-Latn-MX", + "mzj": "mzj-Latn-LR", + "mzk": "mzk-Latn-ZZ", + "mzl": "mzl-Latn-MX", + "mzm": "mzm-Latn-ZZ", + "mzn": "mzn-Arab-IR", + "mzo": "mzo-Latn-BR", + "mzp": "mzp-Latn-ZZ", + "mzq": "mzq-Latn-ID", + "mzr": "mzr-Latn-BR", + "mzt": "mzt-Latn-MY", + "mzu": "mzu-Latn-PG", + "mzv": "mzv-Latn-CF", + "mzw": "mzw-Latn-ZZ", + "mzx": "mzx-Latn-GY", + "mzz": "mzz-Latn-ZZ", + "na": "na-Latn-NR", + "naa": "naa-Latn-ID", + "nab": "nab-Latn-BR", + "nac": "nac-Latn-ZZ", + "nae": "nae-Latn-ID", + "naf": "naf-Latn-ZZ", + "nag": "nag-Latn-IN", + "naj": "naj-Latn-GN", + "nak": "nak-Latn-ZZ", + "nal": "nal-Latn-PG", + "nam": "nam-Latn-AU", + "nan": "nan-Hans-CN", + "nao": "nao-Deva-NP", + "nap": "nap-Latn-IT", + "naq": "naq-Latn-NA", + "nar": "nar-Latn-NG", + "nas": "nas-Latn-ZZ", + "nat": "nat-Latn-NG", + "naw": "naw-Latn-GH", + "nax": "nax-Latn-PG", + "nay": "nay-Latn-AU", + "naz": "naz-Latn-MX", + "nb": "nb-Latn-NO", + "nba": "nba-Latn-AO", + "nbb": "nbb-Latn-NG", + "nbc": "nbc-Latn-IN", + "nbd": "nbd-Latn-CD", + "nbe": "nbe-Latn-IN", + "nbh": "nbh-Latn-NG", + "nbi": "nbi-Latn-IN", + "nbj": "nbj-Latn-AU", + "nbk": "nbk-Latn-PG", + "nbm": "nbm-Latn-CF", + "nbn": "nbn-Latn-ID", + "nbo": "nbo-Latn-NG", + "nbp": "nbp-Latn-NG", + "nbq": "nbq-Latn-ID", + "nbr": "nbr-Latn-NG", + "nbt": "nbt-Latn-IN", + "nbt-Deva": "nbt-Deva-IN", + "nbu": "nbu-Latn-IN", + "nbv": "nbv-Latn-CM", + "nbw": "nbw-Latn-CD", + "nby": "nby-Latn-PG", + "nca": "nca-Latn-ZZ", + "ncb": "ncb-Latn-IN", + "ncb-Deva": "ncb-Deva-IN", + "ncc": "ncc-Latn-PG", + "ncd": "ncd-Deva-NP", + "nce": "nce-Latn-ZZ", + "ncf": "ncf-Latn-ZZ", + "ncg": "ncg-Latn-CA", + "nch": "nch-Latn-MX", + "nci": "nci-Latn-MX", + "ncj": "ncj-Latn-MX", + "nck": "nck-Latn-AU", + "ncl": "ncl-Latn-MX", + "ncm": "ncm-Latn-PG", + "ncn": "ncn-Latn-PG", + "nco": "nco-Latn-ZZ", + "ncq": "ncq-Laoo-LA", + "ncq-Thai": "ncq-Thai-LA", + "ncr": "ncr-Latn-CM", + "nct": "nct-Latn-IN", + "nct-Beng": "nct-Beng-IN", + "ncu": "ncu-Latn-ZZ", + "ncx": "ncx-Latn-MX", + "ncz": "ncz-Latn-US", + "nd": "nd-Latn-ZW", + "nda": "nda-Latn-CG", + "ndb": "ndb-Latn-CM", + "ndc": "ndc-Latn-MZ", + "ndd": "ndd-Latn-NG", + "ndf": "ndf-Cyrl-RU", + "ndg": "ndg-Latn-TZ", + "ndh": "ndh-Latn-TZ", + "ndi": "ndi-Latn-NG", + "ndj": "ndj-Latn-TZ", + "ndk": "ndk-Latn-CD", + "ndl": "ndl-Latn-CD", + "ndm": "ndm-Latn-TD", + "ndn": "ndn-Latn-CG", + "ndp": "ndp-Latn-UG", + "ndq": "ndq-Latn-AO", + "ndr": "ndr-Latn-NG", + "nds": "nds-Latn-DE", + "ndt": "ndt-Latn-CD", + "ndu": "ndu-Latn-CM", + "ndv": "ndv-Latn-SN", + "ndw": "ndw-Latn-CD", + "ndx": "ndx-Latn-ID", + "ndy": "ndy-Latn-CF", + "ndy-TD": "ndy-Latn-TD", + "ndz": "ndz-Latn-SS", + "ne": "ne-Deva-NP", + "nea": "nea-Latn-ID", + "neb": "neb-Latn-ZZ", + "nec": "nec-Latn-ID", + "ned": "ned-Latn-NG", + "nee": "nee-Latn-NC", + "neg": "neg-Cyrl-RU", + "neh": "neh-Tibt-BT", + "nei": "nei-Xsux-TR", + "nej": "nej-Latn-PG", + "nek": "nek-Latn-NC", + "nem": "nem-Latn-NC", + "nen": "nen-Latn-NC", + "neo": "neo-Latn-VN", + "neq": "neq-Latn-MX", + "ner": "ner-Latn-ID", + "net": "net-Latn-PG", + "neu": "neu-Latn-001", + "new": "new-Deva-NP", + "nex": "nex-Latn-ZZ", + "ney": "ney-Latn-CI", + "nez": "nez-Latn-US", + "nfa": "nfa-Latn-ID", + "nfd": "nfd-Latn-NG", + "nfl": "nfl-Latn-SB", + "nfr": "nfr-Latn-ZZ", + "nfu": "nfu-Latn-CM", + "ng": "ng-Latn-NA", + "nga": "nga-Latn-ZZ", + "ngb": "ngb-Latn-ZZ", + "ngc": "ngc-Latn-CD", + "ngd": "ngd-Latn-CF", + "nge": "nge-Latn-CM", + "ngg": "ngg-Latn-CF", + "ngh": "ngh-Latn-ZA", + "ngi": "ngi-Latn-NG", + "ngj": "ngj-Latn-CM", + "ngk": "ngk-Latn-AU", + "ngl": "ngl-Latn-MZ", + "ngm": "ngm-Latn-FM", + "ngn": "ngn-Latn-CM", + "ngp": "ngp-Latn-TZ", + "ngq": "ngq-Latn-TZ", + "ngr": "ngr-Latn-SB", + "ngs": "ngs-Latn-NG", + "ngt": "ngt-Laoo-LA", + "ngu": "ngu-Latn-MX", + "ngv": "ngv-Latn-CM", + "ngw": "ngw-Latn-NG", + "ngx": "ngx-Latn-NG", + "ngy": "ngy-Latn-CM", + "ngz": "ngz-Latn-CG", + "nha": "nha-Latn-AU", + "nhb": "nhb-Latn-ZZ", + "nhc": "nhc-Latn-MX", + "nhd": "nhd-Latn-PY", + "nhe": "nhe-Latn-MX", + "nhf": "nhf-Latn-AU", + "nhg": "nhg-Latn-MX", + "nhi": "nhi-Latn-MX", + "nhk": "nhk-Latn-MX", + "nhm": "nhm-Latn-MX", + "nhn": "nhn-Latn-MX", + "nho": "nho-Latn-PG", + "nhp": "nhp-Latn-MX", + "nhq": "nhq-Latn-MX", + "nhr": "nhr-Latn-BW", + "nht": "nht-Latn-MX", + "nhu": "nhu-Latn-CM", + "nhv": "nhv-Latn-MX", + "nhw": "nhw-Latn-MX", + "nhx": "nhx-Latn-MX", + "nhy": "nhy-Latn-MX", + "nhz": "nhz-Latn-MX", + "nia": "nia-Latn-ID", + "nib": "nib-Latn-PG", + "nid": "nid-Latn-AU", + "nie": "nie-Latn-TD", + "nif": "nif-Latn-ZZ", + "nig": "nig-Latn-AU", + "nih": "nih-Latn-TZ", + "nii": "nii-Latn-ZZ", + "nij": "nij-Latn-ID", + "nil": "nil-Latn-ID", + "nim": "nim-Latn-TZ", + "nin": "nin-Latn-ZZ", + "nio": "nio-Cyrl-RU", + "niq": "niq-Latn-KE", + "nir": "nir-Latn-ID", + "nis": "nis-Latn-PG", + "nit": "nit-Telu-IN", + "niu": "niu-Latn-NU", + "niv": "niv-Cyrl-RU", + "niv-Latn": "niv-Latn-RU", + "niw": "niw-Latn-PG", + "nix": "nix-Latn-CD", + "niy": "niy-Latn-ZZ", + "niz": "niz-Latn-ZZ", + "nja": "nja-Latn-NG", + "njb": "njb-Latn-IN", + "njd": "njd-Latn-TZ", + "njh": "njh-Latn-IN", + "nji": "nji-Latn-AU", + "njj": "njj-Latn-CM", + "njl": "njl-Latn-SS", + "njm": "njm-Latn-IN", + "njn": "njn-Latn-IN", + "njo": "njo-Latn-IN", + "njr": "njr-Latn-NG", + "njs": "njs-Latn-ID", + "njt": "njt-Latn-SR", + "nju": "nju-Latn-AU", + "njx": "njx-Latn-CG", + "njy": "njy-Latn-CM", + "njz": "njz-Latn-IN", + "njz-Beng": "njz-Beng-IN", + "nka": "nka-Latn-ZM", + "nkb": "nkb-Latn-IN", + "nkc": "nkc-Latn-CM", + "nkd": "nkd-Latn-IN", + "nke": "nke-Latn-SB", + "nkf": "nkf-Latn-IN", + "nkg": "nkg-Latn-ZZ", + "nkh": "nkh-Latn-IN", + "nki": "nki-Latn-IN", + "nki-Beng": "nki-Beng-IN", + "nkj": "nkj-Latn-ID", + "nkk": "nkk-Latn-VU", + "nkm": "nkm-Latn-PG", + "nkn": "nkn-Latn-AO", + "nko": "nko-Latn-ZZ", + "nkq": "nkq-Latn-GH", + "nkr": "nkr-Latn-FM", + "nks": "nks-Latn-ID", + "nkt": "nkt-Latn-TZ", + "nku": "nku-Latn-CI", + "nkv": "nkv-Latn-MW", + "nkw": "nkw-Latn-CD", + "nkx": "nkx-Latn-NG", + "nkz": "nkz-Latn-NG", + "nl": "nl-Latn-NL", + "nla": "nla-Latn-CM", + "nlc": "nlc-Latn-ID", + "nle": "nle-Latn-KE", + "nlg": "nlg-Latn-SB", + "nli": "nli-Arab-AF", + "nlj": "nlj-Latn-CD", + "nlk": "nlk-Latn-ID", + "nlm": "nlm-Arab-PK", + "nlo": "nlo-Latn-CD", + "nlq": "nlq-Latn-MM", + "nlu": "nlu-Latn-GH", + "nlv": "nlv-Latn-MX", + "nlw": "nlw-Latn-AU", + "nlx": "nlx-Deva-IN", + "nly": "nly-Latn-AU", + "nlz": "nlz-Latn-SB", + "nma": "nma-Latn-IN", + "nmb": "nmb-Latn-VU", + "nmc": "nmc-Latn-TD", + "nmd": "nmd-Latn-GA", + "nme": "nme-Latn-IN", + "nmf": "nmf-Latn-IN", + "nmg": "nmg-Latn-CM", + "nmh": "nmh-Latn-IN", + "nmi": "nmi-Latn-NG", + "nmj": "nmj-Latn-CF", + "nmk": "nmk-Latn-VU", + "nml": "nml-Latn-CM", + "nmm": "nmm-Deva-NP", + "nmm-Tibt": "nmm-Tibt-NP", + "nmn": "nmn-Latn-BW", + "nmo": "nmo-Latn-IN", + "nmo-Beng": "nmo-Beng-IN", + "nmp": "nmp-Latn-AU", + "nmq": "nmq-Latn-ZW", + "nmr": "nmr-Latn-CM", + "nms": "nms-Latn-VU", + "nmt": "nmt-Latn-FM", + "nmu": "nmu-Latn-US", + "nmv": "nmv-Latn-AU", + "nmw": "nmw-Latn-PG", + "nmx": "nmx-Latn-PG", + "nmz": "nmz-Latn-ZZ", + "nn": "nn-Latn-NO", + "nna": "nna-Latn-AU", + "nnb": "nnb-Latn-CD", + "nnc": "nnc-Latn-TD", + "nnd": "nnd-Latn-VU", + "nne": "nne-Latn-AO", + "nnf": "nnf-Latn-ZZ", + "nng": "nng-Latn-IN", + "nng-Beng": "nng-Beng-IN", + "nnh": "nnh-Latn-CM", + "nni": "nni-Latn-ID", + "nnj": "nnj-Latn-ET", + "nnk": "nnk-Latn-ZZ", + "nnl": "nnl-Latn-IN", + "nnm": "nnm-Latn-ZZ", + "nnn": "nnn-Latn-TD", + "nnp": "nnp-Wcho-IN", + "nnq": "nnq-Latn-TZ", + "nnr": "nnr-Latn-AU", + "nnt": "nnt-Latn-US", + "nnu": "nnu-Latn-GH", + "nnv": "nnv-Latn-AU", + "nnw": "nnw-Latn-BF", + "nny": "nny-Latn-AU", + "nnz": "nnz-Latn-CM", + "no": "no-Latn-NO", + "noa": "noa-Latn-CO", + "noc": "noc-Latn-PG", + "nod": "nod-Lana-TH", + "noe": "noe-Deva-IN", + "nof": "nof-Latn-PG", + "nog": "nog-Cyrl-RU", + "noh": "noh-Latn-PG", + "noi": "noi-Deva-IN", + "noj": "noj-Latn-CO", + "nok": "nok-Latn-US", + "nom": "nom-Latn-PE", + "non": "non-Runr-SE", + "nop": "nop-Latn-ZZ", + "noq": "noq-Latn-CD", + "nos": "nos-Yiii-CN", + "not": "not-Latn-PE", + "nou": "nou-Latn-ZZ", + "nov": "nov-Latn-001", + "now": "now-Latn-TZ", + "noy": "noy-Latn-TD", + "npb": "npb-Tibt-BT", + "npg": "npg-Latn-MM", + "nph": "nph-Latn-IN", + "npl": "npl-Latn-MX", + "npn": "npn-Latn-PG", + "npo": "npo-Latn-IN", + "nps": "nps-Latn-ID", + "npu": "npu-Latn-IN", + "npx": "npx-Latn-SB", + "npy": "npy-Latn-ID", + "nqg": "nqg-Latn-BJ", + "nqk": "nqk-Latn-BJ", + "nql": "nql-Latn-AO", + "nqm": "nqm-Latn-ID", + "nqn": "nqn-Latn-PG", + "nqo": "nqo-Nkoo-GN", + "nqq": "nqq-Latn-MM", + "nqt": "nqt-Latn-NG", + "nqy": "nqy-Latn-MM", + "nr": "nr-Latn-ZA", + "nra": "nra-Latn-GA", + "nrb": "nrb-Latn-ZZ", + "nre": "nre-Latn-IN", + "nrf": "nrf-Latn-JE", + "nrg": "nrg-Latn-VU", + "nri": "nri-Latn-IN", + "nrk": "nrk-Latn-AU", + "nrl": "nrl-Latn-AU", + "nrm": "nrm-Latn-MY", + "nrp": "nrp-Latn-IT", + "nru": "nru-Latn-CN", + "nru-Hans": "nru-Hans-CN", + "nru-Hant": "nru-Hant-CN", + "nrx": "nrx-Latn-AU", + "nrz": "nrz-Latn-PG", + "nsa": "nsa-Latn-IN", + "nsb": "nsb-Latn-ZA", + "nsc": "nsc-Latn-NG", + "nsd": "nsd-Yiii-CN", + "nse": "nse-Latn-ZM", + "nsf": "nsf-Yiii-CN", + "nsg": "nsg-Latn-TZ", + "nsh": "nsh-Latn-CM", + "nsk": "nsk-Cans-CA", + "nsm": "nsm-Latn-IN", + "nsn": "nsn-Latn-ZZ", + "nso": "nso-Latn-ZA", + "nsq": "nsq-Latn-US", + "nss": "nss-Latn-ZZ", + "nst": "nst-Tnsa-IN", + "nsu": "nsu-Latn-MX", + "nsv": "nsv-Yiii-CN", + "nsw": "nsw-Latn-VU", + "nsx": "nsx-Latn-AO", + "nsy": "nsy-Latn-ID", + "nsz": "nsz-Latn-US", + "ntd": "ntd-Latn-MY", + "nte": "nte-Latn-MZ", + "ntg": "ntg-Latn-AU", + "nti": "nti-Latn-BF", + "ntj": "ntj-Latn-AU", + "ntk": "ntk-Latn-TZ", + "ntm": "ntm-Latn-ZZ", + "nto": "nto-Latn-CD", + "ntp": "ntp-Latn-MX", + "ntr": "ntr-Latn-ZZ", + "ntu": "ntu-Latn-SB", + "ntx": "ntx-Latn-MM", + "nty": "nty-Yiii-VN", + "ntz": "ntz-Arab-IR", + "nua": "nua-Latn-NC", + "nuc": "nuc-Latn-BR", + "nud": "nud-Latn-PG", + "nue": "nue-Latn-CD", + "nuf": "nuf-Latn-CN", + "nug": "nug-Latn-AU", + "nuh": "nuh-Latn-NG", + "nui": "nui-Latn-ZZ", + "nuj": "nuj-Latn-UG", + "nuk": "nuk-Latn-CA", + "num": "num-Latn-TO", + "nun": "nun-Latn-MM", + "nuo": "nuo-Latn-VN", + "nup": "nup-Latn-ZZ", + "nuq": "nuq-Latn-PG", + "nur": "nur-Latn-PG", + "nus": "nus-Latn-SS", + "nut": "nut-Latn-VN", + "nuu": "nuu-Latn-CD", + "nuv": "nuv-Latn-ZZ", + "nuw": "nuw-Latn-FM", + "nux": "nux-Latn-ZZ", + "nuy": "nuy-Latn-AU", + "nuz": "nuz-Latn-MX", + "nv": "nv-Latn-US", + "nvh": "nvh-Latn-VU", + "nvm": "nvm-Latn-PG", + "nvo": "nvo-Latn-CM", + "nwb": "nwb-Latn-ZZ", + "nwc": "nwc-Newa-NP", + "nwc-Brah": "nwc-Brah-NP", + "nwc-Deva": "nwc-Deva-NP", + "nwc-Sidd": "nwc-Sidd-NP", + "nwe": "nwe-Latn-CM", + "nwg": "nwg-Latn-AU", + "nwi": "nwi-Latn-VU", + "nwm": "nwm-Latn-SS", + "nwo": "nwo-Latn-AU", + "nwr": "nwr-Latn-PG", + "nww": "nww-Latn-TZ", + "nwx": "nwx-Deva-NP", + "nxa": "nxa-Latn-TL", + "nxd": "nxd-Latn-CD", + "nxe": "nxe-Latn-ID", + "nxg": "nxg-Latn-ID", + "nxi": "nxi-Latn-TZ", + "nxl": "nxl-Latn-ID", + "nxn": "nxn-Latn-AU", + "nxo": "nxo-Latn-GA", + "nxq": "nxq-Latn-CN", + "nxr": "nxr-Latn-ZZ", + "nxx": "nxx-Latn-ID", + "ny": "ny-Latn-MW", + "nyb": "nyb-Latn-GH", + "nyc": "nyc-Latn-CD", + "nyd": "nyd-Latn-KE", + "nye": "nye-Latn-AO", + "nyf": "nyf-Latn-KE", + "nyg": "nyg-Latn-CD", + "nyh": "nyh-Latn-AU", + "nyi": "nyi-Latn-SD", + "nyj": "nyj-Latn-CD", + "nyk": "nyk-Latn-AO", + "nyl": "nyl-Thai-TH", + "nym": "nym-Latn-TZ", + "nyn": "nyn-Latn-UG", + "nyo": "nyo-Latn-UG", + "nyp": "nyp-Latn-UG", + "nyq": "nyq-Arab-IR", + "nyr": "nyr-Latn-MW", + "nys": "nys-Latn-AU", + "nyt": "nyt-Latn-AU", + "nyu": "nyu-Latn-MZ", + "nyv": "nyv-Latn-AU", + "nyx": "nyx-Latn-AU", + "nyy": "nyy-Latn-TZ", + "nza": "nza-Latn-CM", + "nzb": "nzb-Latn-GA", + "nzd": "nzd-Latn-CD", + "nzi": "nzi-Latn-GH", + "nzk": "nzk-Latn-CF", + "nzm": "nzm-Latn-IN", + "nzu": "nzu-Latn-CG", + "nzy": "nzy-Latn-TD", + "nzz": "nzz-Latn-ML", + "oaa": "oaa-Cyrl-RU", + "oac": "oac-Cyrl-RU", + "oar": "oar-Syrc-SY", + "oav": "oav-Geor-GE", + "obi": "obi-Latn-US", + "obk": "obk-Latn-PH", + "obl": "obl-Latn-CM", + "obm": "obm-Phnx-JO", + "obo": "obo-Latn-PH", + "obr": "obr-Mymr-MM", + "obt": "obt-Latn-FR", + "obu": "obu-Latn-NG", + "oc": "oc-Latn-FR", + "oca": "oca-Latn-PE", + "oco": "oco-Latn-GB", + "ocu": "ocu-Latn-MX", + "oda": "oda-Latn-NG", + "odk": "odk-Arab-PK", + "odt": "odt-Latn-NL", + "odu": "odu-Latn-NG", + "ofu": "ofu-Latn-NG", + "ogb": "ogb-Latn-NG", + "ogc": "ogc-Latn-ZZ", + "ogg": "ogg-Latn-NG", + "ogo": "ogo-Latn-NG", + "ogu": "ogu-Latn-NG", + "oht": "oht-Xsux-TR", + "oia": "oia-Latn-ID", + "oie": "oie-Latn-SS", + "oin": "oin-Latn-PG", + "oj": "oj-Cans-CA", + "ojb": "ojb-Latn-CA", + "ojb-Cans": "ojb-Cans-CA", + "ojc": "ojc-Latn-CA", + "ojs": "ojs-Cans-CA", + "ojv": "ojv-Latn-SB", + "ojw": "ojw-Latn-CA", + "ojw-Cans": "ojw-Cans-CA", + "oka": "oka-Latn-CA", + "okb": "okb-Latn-NG", + "okc": "okc-Latn-CD", + "okd": "okd-Latn-NG", + "oke": "oke-Latn-NG", + "okg": "okg-Latn-AU", + "oki": "oki-Latn-KE", + "okk": "okk-Latn-PG", + "okm": "okm-Hang-KR", + "oko": "oko-Hani-KR", + "okr": "okr-Latn-ZZ", + "oks": "oks-Latn-NG", + "oku": "oku-Latn-CM", + "okv": "okv-Latn-ZZ", + "okx": "okx-Latn-NG", + "okz": "okz-Khmr-KH", + "ola": "ola-Deva-NP", + "ola-Tibt": "ola-Tibt-CN", + "old": "old-Latn-TZ", + "ole": "ole-Tibt-BT", + "olk": "olk-Latn-AU", + "olm": "olm-Latn-NG", + "olo": "olo-Latn-RU", + "olr": "olr-Latn-VU", + "olt": "olt-Latn-LT", + "olu": "olu-Latn-AO", + "om": "om-Latn-ET", + "oma": "oma-Latn-US", + "omb": "omb-Latn-VU", + "omc": "omc-Latn-PE", + "omg": "omg-Latn-PE", + "omi": "omi-Latn-CD", + "omk": "omk-Cyrl-RU", + "oml": "oml-Latn-CD", + "omo": "omo-Latn-PG", + "omp": "omp-Mtei-IN", + "omr": "omr-Modi-IN", + "omt": "omt-Latn-KE", + "omu": "omu-Latn-PE", + "omw": "omw-Latn-PG", + "ona": "ona-Latn-AR", + "one": "one-Latn-CA", + "ong": "ong-Latn-ZZ", + "oni": "oni-Latn-ID", + "onj": "onj-Latn-PG", + "onk": "onk-Latn-PG", + "onn": "onn-Latn-ZZ", + "ono": "ono-Latn-CA", + "onp": "onp-Latn-IN", + "onp-Deva": "onp-Deva-IN", + "onr": "onr-Latn-PG", + "ons": "ons-Latn-ZZ", + "ont": "ont-Latn-PG", + "onu": "onu-Latn-VU", + "onx": "onx-Latn-ID", + "ood": "ood-Latn-US", + "oon": "oon-Deva-IN", + "oor": "oor-Latn-ZA", + "opa": "opa-Latn-NG", + "opk": "opk-Latn-ID", + "opm": "opm-Latn-ZZ", + "opo": "opo-Latn-PG", + "opt": "opt-Latn-MX", + "opy": "opy-Latn-BR", + "or": "or-Orya-IN", + "ora": "ora-Latn-SB", + "orc": "orc-Latn-KE", + "ore": "ore-Latn-PE", + "org": "org-Latn-NG", + "orn": "orn-Latn-MY", + "oro": "oro-Latn-ZZ", + "orr": "orr-Latn-NG", + "ors": "ors-Latn-MY", + "ort": "ort-Telu-IN", + "oru": "oru-Arab-ZZ", + "orv": "orv-Cyrl-RU", + "orw": "orw-Latn-BR", + "orx": "orx-Latn-NG", + "orz": "orz-Latn-ID", + "os": "os-Cyrl-GE", + "osa": "osa-Osge-US", + "osc": "osc-Ital-IT", + "osc-Latn": "osc-Latn-IT", + "osi": "osi-Java-ID", + "oso": "oso-Latn-NG", + "osp": "osp-Latn-ES", + "ost": "ost-Latn-CM", + "osu": "osu-Latn-PG", + "osx": "osx-Latn-DE", + "ota": "ota-Arab-ZZ", + "otb": "otb-Tibt-CN", + "otd": "otd-Latn-ID", + "ote": "ote-Latn-MX", + "oti": "oti-Latn-BR", + "otk": "otk-Orkh-MN", + "otl": "otl-Latn-MX", + "otm": "otm-Latn-MX", + "otn": "otn-Latn-MX", + "otq": "otq-Latn-MX", + "otr": "otr-Latn-SD", + "ots": "ots-Latn-MX", + "ott": "ott-Latn-MX", + "otu": "otu-Latn-BR", + "otw": "otw-Latn-CA", + "otx": "otx-Latn-MX", + "oty": "oty-Gran-IN", + "otz": "otz-Latn-MX", + "oub": "oub-Latn-LR", + "oue": "oue-Latn-PG", + "oui": "oui-Ougr-143", + "oum": "oum-Latn-PG", + "ovd": "ovd-Latn-SE", + "owi": "owi-Latn-PG", + "owl": "owl-Latn-GB", + "oyd": "oyd-Latn-ET", + "oym": "oym-Latn-BR", + "oyy": "oyy-Latn-PG", + "ozm": "ozm-Latn-ZZ", + "pa": "pa-Guru-IN", + "pa-Arab": "pa-Arab-PK", + "pa-PK": "pa-Arab-PK", + "pab": "pab-Latn-BR", + "pac": "pac-Latn-VN", + "pad": "pad-Latn-BR", + "pae": "pae-Latn-CD", + "paf": "paf-Latn-BR", + "pag": "pag-Latn-PH", + "pah": "pah-Latn-BR", + "pai": "pai-Latn-NG", + "pak": "pak-Latn-BR", + "pal": "pal-Phli-IR", + "pal-Phlp": "pal-Phlp-CN", + "pam": "pam-Latn-PH", + "pao": "pao-Latn-US", + "pap": "pap-Latn-CW", + "paq": "paq-Cyrl-TJ", + "par": "par-Latn-US", + "pas": "pas-Latn-ID", + "pau": "pau-Latn-PW", + "pav": "pav-Latn-BR", + "paw": "paw-Latn-US", + "pax": "pax-Latn-BR", + "pay": "pay-Latn-HN", + "paz": "paz-Latn-BR", + "pbb": "pbb-Latn-CO", + "pbc": "pbc-Latn-GY", + "pbe": "pbe-Latn-MX", + "pbf": "pbf-Latn-MX", + "pbg": "pbg-Latn-VE", + "pbh": "pbh-Latn-VE", + "pbi": "pbi-Latn-ZZ", + "pbl": "pbl-Latn-NG", + "pbm": "pbm-Latn-MX", + "pbn": "pbn-Latn-NG", + "pbo": "pbo-Latn-GW", + "pbp": "pbp-Latn-GN", + "pbr": "pbr-Latn-TZ", + "pbs": "pbs-Latn-MX", + "pbt": "pbt-Arab-AF", + "pbv": "pbv-Latn-IN", + "pby": "pby-Latn-PG", + "pca": "pca-Latn-MX", + "pcb": "pcb-Khmr-KH", + "pcc": "pcc-Latn-CN", + "pcc-Hani": "pcc-Hani-CN", + "pcd": "pcd-Latn-FR", + "pce": "pce-Mymr-MM", + "pce-Thai": "pce-Thai-TH", + "pcf": "pcf-Mlym-IN", + "pcg": "pcg-Mlym-IN", + "pcg-Knda": "pcg-Knda-IN", + "pcg-Taml": "pcg-Taml-IN", + "pch": "pch-Deva-IN", + "pci": "pci-Deva-IN", + "pci-Orya": "pci-Orya-IN", + "pcj": "pcj-Telu-IN", + "pck": "pck-Latn-IN", + "pcm": "pcm-Latn-NG", + "pcn": "pcn-Latn-NG", + "pcp": "pcp-Latn-BO", + "pcw": "pcw-Latn-NG", + "pda": "pda-Latn-PG", + "pdc": "pdc-Latn-US", + "pdn": "pdn-Latn-ID", + "pdo": "pdo-Latn-ID", + "pdt": "pdt-Latn-CA", + "pdu": "pdu-Latn-MM", + "pdu-Mymr": "pdu-Mymr-MM", + "pea": "pea-Latn-ID", + "peb": "peb-Latn-US", + "ped": "ped-Latn-ZZ", + "pee": "pee-Latn-ID", + "peg": "peg-Orya-IN", + "pei": "pei-Latn-MX", + "pek": "pek-Latn-PG", + "pel": "pel-Latn-ID", + "pem": "pem-Latn-CD", + "peo": "peo-Xpeo-IR", + "pep": "pep-Latn-PG", + "peq": "peq-Latn-US", + "pev": "pev-Latn-VE", + "pex": "pex-Latn-ZZ", + "pey": "pey-Latn-ID", + "pez": "pez-Latn-MY", + "pfa": "pfa-Latn-FM", + "pfe": "pfe-Latn-CM", + "pfl": "pfl-Latn-DE", + "pga": "pga-Latn-SS", + "pgd": "pgd-Khar-PK", + "pgg": "pgg-Deva-IN", + "pgi": "pgi-Latn-PG", + "pgk": "pgk-Latn-VU", + "pgl": "pgl-Ogam-IE", + "pgn": "pgn-Ital-IT", + "pgs": "pgs-Latn-NG", + "pgu": "pgu-Latn-ID", + "phd": "phd-Deva-IN", + "phg": "phg-Latn-VN", + "phh": "phh-Latn-VN", + "phk": "phk-Mymr-IN", + "phl": "phl-Arab-ZZ", + "phm": "phm-Latn-MZ", + "phn": "phn-Phnx-LB", + "pho": "pho-Laoo-LA", + "phr": "phr-Arab-PK", + "pht": "pht-Thai-TH", + "phv": "phv-Arab-AF", + "phw": "phw-Deva-NP", + "pi": "pi-Sinh-IN", + "pi-Brah": "pi-Brah-IN", + "pi-Deva": "pi-Deva-IN", + "pi-Khar": "pi-Khar-IN", + "pi-Khmr": "pi-Khmr-IN", + "pi-Mymr": "pi-Mymr-IN", + "pi-Thai": "pi-Thai-IN", + "pia": "pia-Latn-MX", + "pib": "pib-Latn-PE", + "pic": "pic-Latn-GA", + "pid": "pid-Latn-VE", + "pif": "pif-Latn-FM", + "pig": "pig-Latn-PE", + "pih": "pih-Latn-NF", + "pij": "pij-Latn-CO", + "pil": "pil-Latn-ZZ", + "pim": "pim-Latn-US", + "pin": "pin-Latn-PG", + "pio": "pio-Latn-CO", + "pip": "pip-Latn-ZZ", + "pir": "pir-Latn-BR", + "pis": "pis-Latn-SB", + "pit": "pit-Latn-AU", + "piu": "piu-Latn-AU", + "piv": "piv-Latn-SB", + "piw": "piw-Latn-TZ", + "pix": "pix-Latn-PG", + "piy": "piy-Latn-NG", + "piz": "piz-Latn-NC", + "pjt": "pjt-Latn-AU", + "pka": "pka-Brah-IN", + "pkb": "pkb-Latn-KE", + "pkg": "pkg-Latn-PG", + "pkh": "pkh-Latn-BD", + "pkh-Deva": "pkh-Deva-BD", + "pkn": "pkn-Latn-AU", + "pko": "pko-Latn-KE", + "pkp": "pkp-Latn-CK", + "pkr": "pkr-Mlym-IN", + "pku": "pku-Latn-ID", + "pl": "pl-Latn-PL", + "pla": "pla-Latn-ZZ", + "plb": "plb-Latn-VU", + "plc": "plc-Latn-PH", + "pld": "pld-Latn-GB", + "ple": "ple-Latn-ID", + "plg": "plg-Latn-AR", + "plh": "plh-Latn-ID", + "plj": "plj-Latn-NG", + "plk": "plk-Arab-PK", + "pll": "pll-Mymr-MM", + "pln": "pln-Latn-CO", + "plo": "plo-Latn-MX", + "plr": "plr-Latn-CI", + "pls": "pls-Latn-MX", + "plu": "plu-Latn-BR", + "plv": "plv-Latn-PH", + "plw": "plw-Latn-PH", + "plz": "plz-Latn-MY", + "pma": "pma-Latn-VU", + "pmb": "pmb-Latn-CD", + "pmd": "pmd-Latn-AU", + "pme": "pme-Latn-NC", + "pmf": "pmf-Latn-ID", + "pmh": "pmh-Brah-IN", + "pmi": "pmi-Latn-CN", + "pmj": "pmj-Latn-CN", + "pml": "pml-Latn-TN", + "pmm": "pmm-Latn-CM", + "pmn": "pmn-Latn-CM", + "pmo": "pmo-Latn-ID", + "pmq": "pmq-Latn-MX", + "pmr": "pmr-Latn-PG", + "pms": "pms-Latn-IT", + "pmt": "pmt-Latn-PF", + "pmw": "pmw-Latn-US", + "pmx": "pmx-Latn-IN", + "pmy": "pmy-Latn-ID", + "pmz": "pmz-Latn-MX", + "pna": "pna-Latn-MY", + "pnc": "pnc-Latn-ID", + "pnd": "pnd-Latn-AO", + "pne": "pne-Latn-MY", + "png": "png-Latn-ZZ", + "pnh": "pnh-Latn-CK", + "pni": "pni-Latn-ID", + "pnj": "pnj-Latn-AU", + "pnk": "pnk-Latn-BO", + "pnl": "pnl-Latn-BF", + "pnm": "pnm-Latn-MY", + "pnn": "pnn-Latn-ZZ", + "pno": "pno-Latn-PE", + "pnp": "pnp-Latn-ID", + "pnq": "pnq-Latn-BF", + "pnr": "pnr-Latn-PG", + "pns": "pns-Latn-ID", + "pnt": "pnt-Grek-GR", + "pnv": "pnv-Latn-AU", + "pnw": "pnw-Latn-AU", + "pny": "pny-Latn-CM", + "pnz": "pnz-Latn-CF", + "poc": "poc-Latn-GT", + "poe": "poe-Latn-MX", + "pof": "pof-Latn-CD", + "pog": "pog-Latn-BR", + "poh": "poh-Latn-GT", + "poi": "poi-Latn-MX", + "pok": "pok-Latn-BR", + "pom": "pom-Latn-US", + "pon": "pon-Latn-FM", + "poo": "poo-Latn-US", + "pop": "pop-Latn-NC", + "poq": "poq-Latn-MX", + "pos": "pos-Latn-MX", + "pot": "pot-Latn-US", + "pov": "pov-Latn-GW", + "pow": "pow-Latn-MX", + "poy": "poy-Latn-TZ", + "ppa": "bfy-Deva-IN", + "ppe": "ppe-Latn-PG", + "ppi": "ppi-Latn-MX", + "ppk": "ppk-Latn-ID", + "ppl": "ppl-Latn-SV", + "ppm": "ppm-Latn-ID", + "ppn": "ppn-Latn-PG", + "ppo": "ppo-Latn-ZZ", + "ppp": "ppp-Latn-CD", + "ppq": "ppq-Latn-PG", + "pps": "pps-Latn-MX", + "ppt": "ppt-Latn-PG", + "pqa": "pqa-Latn-NG", + "pqm": "pqm-Latn-CA", + "pra": "pra-Khar-PK", + "prc": "prc-Arab-AF", + "prd": "prd-Arab-IR", + "pre": "pre-Latn-ST", + "prf": "prf-Latn-PH", + "prg": "prg-Latn-001", + "prh": "prh-Latn-PH", + "pri": "pri-Latn-NC", + "prk": "prk-Latn-MM", + "prm": "prm-Latn-PG", + "pro": "pro-Latn-FR", + "prp": "prp-Gujr-IN", + "prq": "prq-Latn-PE", + "prr": "prr-Latn-BR", + "prt": "prt-Thai-TH", + "pru": "pru-Latn-ID", + "prw": "prw-Latn-PG", + "prx": "prx-Arab-IN", + "prx-Tibt": "prx-Tibt-IN", + "ps": "ps-Arab-AF", + "psa": "psa-Latn-ID", + "pse": "pse-Latn-ID", + "psh": "psh-Arab-AF", + "psi": "psi-Arab-AF", + "psm": "psm-Latn-BO", + "psn": "psn-Latn-ID", + "psq": "psq-Latn-PG", + "pss": "pss-Latn-ZZ", + "pst": "pst-Arab-PK", + "psw": "psw-Latn-VU", + "pt": "pt-Latn-BR", + "pta": "pta-Latn-PY", + "pth": "pth-Latn-BR", + "pti": "pti-Latn-AU", + "ptn": "ptn-Latn-ID", + "pto": "pto-Latn-BR", + "ptp": "ptp-Latn-ZZ", + "ptr": "ptr-Latn-VU", + "ptt": "ptt-Latn-ID", + "ptu": "ptu-Latn-ID", + "ptv": "ptv-Latn-VU", + "pua": "pua-Latn-MX", + "pub": "pub-Latn-IN", + "puc": "puc-Latn-ID", + "pud": "pud-Latn-ID", + "pue": "pue-Latn-AR", + "puf": "puf-Latn-ID", + "pug": "pug-Latn-BF", + "pui": "pui-Latn-CO", + "puj": "puj-Latn-ID", + "pum": "pum-Deva-NP", + "puo": "puo-Latn-VN", + "pup": "pup-Latn-PG", + "puq": "puq-Latn-PE", + "pur": "pur-Latn-BR", + "put": "put-Latn-ID", + "puu": "puu-Latn-GA", + "puw": "puw-Latn-FM", + "pux": "pux-Latn-PG", + "puy": "puy-Latn-US", + "pwa": "pwa-Latn-ZZ", + "pwb": "pwb-Latn-NG", + "pwg": "pwg-Latn-PG", + "pwm": "pwm-Latn-PH", + "pwn": "pwn-Latn-TW", + "pwo": "pwo-Mymr-MM", + "pwr": "pwr-Deva-IN", + "pww": "pww-Thai-TH", + "pxm": "pxm-Latn-MX", + "pye": "pye-Latn-CI", + "pym": "pym-Latn-NG", + "pyn": "pyn-Latn-BR", + "pyu": "pyu-Latn-TW", + "pyu-Hani": "pyu-Hani-TW", + "pyx": "pyx-Mymr-MM", + "pyy": "pyy-Latn-MM", + "pzh": "pzh-Latn-TW", + "pzn": "pzn-Latn-MM", + "qu": "qu-Latn-PE", + "qua": "qua-Latn-US", + "qub": "qub-Latn-PE", + "quc": "quc-Latn-GT", + "qud": "qud-Latn-EC", + "quf": "quf-Latn-PE", + "qug": "qug-Latn-EC", + "qui": "qui-Latn-US", + "quk": "quk-Latn-PE", + "qul": "qul-Latn-BO", + "qum": "qum-Latn-GT", + "qun": "qun-Latn-US", + "qup": "qup-Latn-PE", + "quq": "quq-Latn-ES", + "qur": "qur-Latn-PE", + "qus": "qus-Latn-AR", + "quv": "quv-Latn-GT", + "quw": "quw-Latn-EC", + "qux": "qux-Latn-PE", + "quy": "quy-Latn-PE", + "qva": "qva-Latn-PE", + "qvc": "qvc-Latn-PE", + "qve": "qve-Latn-PE", + "qvh": "qvh-Latn-PE", + "qvi": "qvi-Latn-EC", + "qvj": "qvj-Latn-EC", + "qvl": "qvl-Latn-PE", + "qvm": "qvm-Latn-PE", + "qvn": "qvn-Latn-PE", + "qvo": "qvo-Latn-PE", + "qvp": "qvp-Latn-PE", + "qvs": "qvs-Latn-PE", + "qvw": "qvw-Latn-PE", + "qvz": "qvz-Latn-EC", + "qwa": "qwa-Latn-PE", + "qwc": "qwc-Latn-PE", + "qwh": "qwh-Latn-PE", + "qwm": "qwm-Latn-RU", + "qwm-Cyrl": "qwm-Cyrl-RU", + "qwm-Runr": "qwm-Runr-RU", + "qws": "qws-Latn-PE", + "qwt": "qwt-Latn-US", + "qxa": "qxa-Latn-PE", + "qxc": "qxc-Latn-PE", + "qxh": "qxh-Latn-PE", + "qxl": "qxl-Latn-EC", + "qxn": "qxn-Latn-PE", + "qxo": "qxo-Latn-PE", + "qxp": "qxp-Latn-PE", + "qxq": "qxq-Arab-IR", + "qxr": "qxr-Latn-EC", + "qxt": "qxt-Latn-PE", + "qxu": "qxu-Latn-PE", + "qxw": "qxw-Latn-PE", + "qya": "qya-Latn-001", + "qyp": "qyp-Latn-US", + "raa": "raa-Deva-NP", + "rab": "rab-Deva-NP", + "rac": "rac-Latn-ID", + "rad": "rad-Latn-VN", + "raf": "raf-Deva-NP", + "rag": "rag-Latn-KE", + "rah": "rah-Beng-IN", + "rah-Latn": "rah-Latn-IN", + "rai": "rai-Latn-ZZ", + "raj": "raj-Deva-IN", + "rak": "rak-Latn-PG", + "ram": "ram-Latn-BR", + "ran": "ran-Latn-ID", + "rao": "rao-Latn-ZZ", + "rap": "rap-Latn-CL", + "rar": "rar-Latn-CK", + "rav": "rav-Deva-NP", + "raw": "raw-Latn-MM", + "rax": "rax-Latn-NG", + "ray": "ray-Latn-PF", + "raz": "raz-Latn-ID", + "rbb": "rbb-Mymr-MM", + "rbk": "rbk-Latn-PH", + "rbl": "rbl-Latn-PH", + "rbp": "rbp-Latn-AU", + "rcf": "rcf-Latn-RE", + "rdb": "rdb-Arab-IR", + "rea": "rea-Latn-PG", + "reb": "reb-Latn-ID", + "ree": "ree-Latn-MY", + "reg": "reg-Latn-TZ", + "rei": "rei-Orya-IN", + "rei-Telu": "rei-Telu-IN", + "rej": "rej-Latn-ID", + "rel": "rel-Latn-ZZ", + "rem": "rem-Latn-PE", + "ren": "ren-Latn-VN", + "res": "res-Latn-ZZ", + "ret": "ret-Latn-ID", + "rey": "rey-Latn-BO", + "rga": "rga-Latn-VU", + "rgn": "rgn-Latn-IT", + "rgr": "rgr-Latn-PE", + "rgs": "rgs-Latn-VN", + "rgu": "rgu-Latn-ID", + "rhg": "rhg-Rohg-MM", + "rhp": "rhp-Latn-PG", + "ria": "ria-Latn-IN", + "rif": "rif-Latn-MA", + "ril": "ril-Latn-MM", + "rim": "rim-Latn-TZ", + "rin": "rin-Latn-NG", + "rir": "rir-Latn-ID", + "rit": "rit-Latn-AU", + "riu": "riu-Latn-ID", + "rjg": "rjg-Latn-ID", + "rji": "rji-Deva-NP", + "rjs": "rjs-Deva-NP", + "rka": "rka-Khmr-KH", + "rkb": "rkb-Latn-BR", + "rkh": "rkh-Latn-CK", + "rki": "rki-Mymr-MM", + "rkm": "rkm-Latn-BF", + "rkt": "rkt-Beng-BD", + "rkw": "rkw-Latn-AU", + "rm": "rm-Latn-CH", + "rma": "rma-Latn-NI", + "rmb": "rmb-Latn-AU", + "rmc": "rmc-Latn-SK", + "rmd": "rmd-Latn-DK", + "rme": "rme-Latn-GB", + "rmf": "rmf-Latn-FI", + "rmg": "rmg-Latn-NO", + "rmh": "rmh-Latn-ID", + "rmi": "rmi-Armn-AM", + "rmk": "rmk-Latn-PG", + "rml": "rml-Latn-PL", + "rml-Cyrl": "rml-Cyrl-BY", + "rmm": "rmm-Latn-ID", + "rmn": "rmn-Latn-RS", + "rmn-Cyrl": "rmn-Cyrl-BG", + "rmn-Grek": "rmn-Grek-GR", + "rmo": "rmo-Latn-CH", + "rmp": "rmp-Latn-PG", + "rmq": "rmq-Latn-ES", + "rmt": "rmt-Arab-IR", + "rmu": "rmu-Latn-SE", + "rmw": "rmw-Latn-GB", + "rmx": "rmx-Latn-VN", + "rmz": "rmz-Mymr-IN", + "rn": "rn-Latn-BI", + "rna": "rna-Latn-ZZ", + "rnd": "rnd-Latn-CD", + "rng": "rng-Latn-MZ", + "rnl": "rnl-Latn-IN", + "rnn": "rnn-Latn-ID", + "rnr": "rnr-Latn-AU", + "rnw": "rnw-Latn-TZ", + "ro": "ro-Latn-RO", + "rob": "rob-Latn-ID", + "roc": "roc-Latn-VN", + "rod": "rod-Latn-NG", + "roe": "roe-Latn-PG", + "rof": "rof-Latn-TZ", + "rog": "rog-Latn-VN", + "rol": "rol-Latn-PH", + "rom": "rom-Latn-RO", + "rom-Cyrl": "rom-Cyrl-RO", + "roo": "roo-Latn-ZZ", + "rop": "rop-Latn-AU", + "ror": "ror-Latn-ID", + "rou": "rou-Latn-TD", + "row": "row-Latn-ID", + "rpn": "rpn-Latn-VU", + "rpt": "rpt-Latn-PG", + "rri": "rri-Latn-SB", + "rro": "rro-Latn-ZZ", + "rrt": "rrt-Latn-AU", + "rsk": "rsk-Cyrl-RS", + "rtc": "rtc-Latn-MM", + "rth": "rth-Latn-ID", + "rtm": "rtm-Latn-FJ", + "rtw": "rtw-Deva-IN", + "ru": "ru-Cyrl-RU", + "rub": "rub-Latn-UG", + "ruc": "ruc-Latn-UG", + "rue": "rue-Cyrl-UA", + "ruf": "ruf-Latn-TZ", + "rug": "rug-Latn-SB", + "rui": "rui-Latn-TZ", + "ruk": "ruk-Latn-NG", + "ruo": "ruo-Latn-HR", + "rup": "rup-Latn-RO", + "rup-Grek": "rup-Grek-GR", + "ruq": "ruq-Latn-GR", + "rut": "rut-Cyrl-RU", + "rut-Latn": "rut-Latn-AZ", + "ruu": "ruu-Latn-MY", + "ruy": "ruy-Latn-NG", + "ruz": "ruz-Latn-NG", + "rw": "rw-Latn-RW", + "rwa": "rwa-Latn-PG", + "rwk": "rwk-Latn-TZ", + "rwl": "rwl-Latn-TZ", + "rwm": "rwm-Latn-UG", + "rwo": "rwo-Latn-ZZ", + "rwr": "rwr-Deva-IN", + "rxd": "rxd-Latn-AU", + "rxw": "rxw-Latn-AU", + "ryu": "ryu-Kana-JP", + "sa": "sa-Deva-IN", + "saa": "saa-Latn-TD", + "sab": "sab-Latn-PA", + "sac": "sac-Latn-US", + "sad": "sad-Latn-TZ", + "sae": "sae-Latn-BR", + "saf": "saf-Latn-GH", + "sah": "sah-Cyrl-RU", + "saj": "saj-Latn-ID", + "sak": "sak-Latn-GA", + "sam": "sam-Samr-PS", + "sam-Hebr": "sam-Hebr-PS", + "sam-Syrc": "sam-Syrc-PS", + "sao": "sao-Latn-ID", + "saq": "saq-Latn-KE", + "sar": "sar-Latn-BO", + "sas": "sas-Latn-ID", + "sat": "sat-Olck-IN", + "sau": "sau-Latn-ID", + "sav": "sav-Latn-SN", + "saw": "saw-Latn-ID", + "sax": "sax-Latn-VU", + "say": "say-Latn-NG", + "saz": "saz-Saur-IN", + "sba": "sba-Latn-ZZ", + "sbb": "sbb-Latn-SB", + "sbc": "sbc-Latn-PG", + "sbd": "sbd-Latn-BF", + "sbe": "sbe-Latn-ZZ", + "sbg": "sbg-Latn-ID", + "sbh": "sbh-Latn-PG", + "sbi": "sbi-Latn-PG", + "sbj": "sbj-Latn-TD", + "sbk": "sbk-Latn-TZ", + "sbl": "sbl-Latn-PH", + "sbm": "sbm-Latn-TZ", + "sbn": "sbn-Arab-PK", + "sbo": "sbo-Latn-MY", + "sbp": "sbp-Latn-TZ", + "sbq": "sbq-Latn-PG", + "sbr": "sbr-Latn-ID", + "sbs": "sbs-Latn-NA", + "sbt": "sbt-Latn-ID", + "sbu": "sbu-Tibt-IN", + "sbu-Deva": "sbu-Deva-IN", + "sbv": "sbv-Latn-IT", + "sbw": "sbw-Latn-GA", + "sbx": "sbx-Latn-ID", + "sby": "sby-Latn-ZM", + "sbz": "sbz-Latn-CF", + "sc": "sc-Latn-IT", + "scb": "scb-Latn-VN", + "sce": "sce-Latn-CN", + "sce-Arab": "sce-Arab-CN", + "scf": "scf-Latn-PA", + "scg": "scg-Latn-ID", + "sch": "sch-Latn-IN", + "sci": "sci-Latn-LK", + "sck": "sck-Deva-IN", + "scl": "scl-Arab-ZZ", + "scn": "scn-Latn-IT", + "sco": "sco-Latn-GB", + "scp": "scp-Deva-NP", + "scs": "scs-Latn-CA", + "scs-Cans": "scs-Cans-CA", + "sct": "sct-Laoo-LA", + "scu": "scu-Takr-IN", + "scv": "scv-Latn-NG", + "scw": "scw-Latn-NG", + "scx": "scx-Grek-IT", + "sd": "sd-Arab-PK", + "sd-Deva": "sd-Deva-IN", + "sd-IN": "sd-Deva-IN", + "sd-Khoj": "sd-Khoj-IN", + "sd-Sind": "sd-Sind-IN", + "sda": "sda-Latn-ID", + "sdb": "sdb-Arab-IQ", + "sdc": "sdc-Latn-IT", + "sde": "sde-Latn-NG", + "sdf": "sdf-Arab-IQ", + "sdg": "sdg-Arab-AF", + "sdh": "sdh-Arab-IR", + "sdj": "sdj-Latn-CG", + "sdk": "sdk-Latn-PG", + "sdn": "sdn-Latn-IT", + "sdo": "sdo-Latn-MY", + "sdq": "sdq-Latn-ID", + "sds": "sds-Arab-TN", + "sdu": "sdu-Latn-ID", + "sdx": "sdx-Latn-MY", + "se": "se-Latn-NO", + "sea": "sea-Latn-MY", + "seb": "seb-Latn-CI", + "sec": "sec-Latn-CA", + "sed": "sed-Latn-VN", + "see": "see-Latn-US", + "sef": "sef-Latn-CI", + "seg": "seg-Latn-TZ", + "seh": "seh-Latn-MZ", + "sei": "sei-Latn-MX", + "sej": "sej-Latn-PG", + "sek": "sek-Latn-CA", + "sek-Cans": "sek-Cans-CA", + "sel": "sel-Cyrl-RU", + "sen": "sen-Latn-BF", + "seo": "seo-Latn-PG", + "sep": "sep-Latn-BF", + "seq": "seq-Latn-BF", + "ser": "ser-Latn-US", + "ses": "ses-Latn-ML", + "set": "set-Latn-ID", + "seu": "seu-Latn-ID", + "sev": "sev-Latn-CI", + "sew": "sew-Latn-PG", + "sey": "sey-Latn-EC", + "sez": "sez-Latn-MM", + "sfe": "sfe-Latn-PH", + "sfm": "sfm-Plrd-CN", + "sfw": "sfw-Latn-GH", + "sg": "sg-Latn-CF", + "sga": "sga-Ogam-IE", + "sgb": "sgb-Latn-PH", + "sgc": "sgc-Latn-KE", + "sgd": "sgd-Latn-PH", + "sge": "sge-Latn-ID", + "sgh": "sgh-Cyrl-TJ", + "sgh-Arab": "sgh-Arab-AF", + "sgh-Latn": "sgh-Latn-TJ", + "sgi": "sgi-Latn-CM", + "sgj": "sgj-Deva-IN", + "sgm": "sgm-Latn-KE", + "sgp": "sgp-Latn-IN", + "sgr": "sgr-Arab-IR", + "sgs": "sgs-Latn-LT", + "sgt": "sgt-Tibt-BT", + "sgu": "sgu-Latn-ID", + "sgw": "sgw-Ethi-ZZ", + "sgy": "sgy-Arab-AF", + "sgz": "sgz-Latn-ZZ", + "sha": "sha-Latn-NG", + "shb": "shb-Latn-BR", + "shc": "shc-Latn-CD", + "shd": "shd-Arab-PK", + "she": "she-Latn-ET", + "shg": "shg-Latn-BW", + "shh": "shh-Latn-US", + "shi": "shi-Tfng-MA", + "shj": "shj-Latn-SD", + "shk": "shk-Latn-ZZ", + "shm": "shm-Arab-IR", + "shn": "shn-Mymr-MM", + "sho": "sho-Latn-NG", + "shp": "shp-Latn-PE", + "shq": "shq-Latn-ZM", + "shr": "shr-Latn-CD", + "shs": "shs-Latn-CA", + "sht": "sht-Latn-US", + "shu": "shu-Arab-ZZ", + "shv": "shv-Arab-OM", + "shw": "shw-Latn-SD", + "shy": "shy-Latn-DZ", + "shy-Arab": "shy-Arab-DZ", + "shy-Tfng": "shy-Tfng-DZ", + "shz": "shz-Latn-ML", + "si": "si-Sinh-LK", + "sia": "sia-Cyrl-RU", + "sib": "sib-Latn-MY", + "sid": "sid-Latn-ET", + "sie": "sie-Latn-ZM", + "sif": "sif-Latn-BF", + "sig": "sig-Latn-ZZ", + "sih": "sih-Latn-NC", + "sii": "sii-Latn-IN", + "sij": "sij-Latn-PG", + "sik": "sik-Latn-BR", + "sil": "sil-Latn-ZZ", + "sim": "sim-Latn-ZZ", + "sip": "sip-Tibt-IN", + "siq": "siq-Latn-PG", + "sir": "sir-Latn-NG", + "sis": "sis-Latn-US", + "siu": "siu-Latn-PG", + "siv": "siv-Latn-PG", + "siw": "siw-Latn-PG", + "six": "six-Latn-PG", + "siy": "siy-Arab-IR", + "siz": "siz-Arab-EG", + "sja": "sja-Latn-CO", + "sjb": "sjb-Latn-ID", + "sjd": "sjd-Cyrl-RU", + "sje": "sje-Latn-SE", + "sjg": "sjg-Latn-TD", + "sjl": "sjl-Latn-IN", + "sjm": "sjm-Latn-PH", + "sjp": "sjp-Deva-IN", + "sjp-Beng": "sjp-Beng-IN", + "sjr": "sjr-Latn-ZZ", + "sjt": "sjt-Cyrl-RU", + "sju": "sju-Latn-SE", + "sjw": "sjw-Latn-US", + "sk": "sk-Latn-SK", + "ska": "ska-Latn-US", + "skb": "skb-Thai-TH", + "skc": "skc-Latn-ZZ", + "skd": "skd-Latn-US", + "ske": "ske-Latn-VU", + "skf": "skf-Latn-BR", + "skg": "skg-Latn-MG", + "skh": "skh-Latn-ID", + "ski": "ski-Latn-ID", + "skj": "skj-Deva-NP", + "skm": "skm-Latn-PG", + "skn": "skn-Latn-PH", + "sko": "sko-Latn-ID", + "skp": "skp-Latn-MY", + "skq": "skq-Latn-BF", + "skr": "skr-Arab-PK", + "sks": "sks-Latn-ZZ", + "skt": "skt-Latn-CD", + "sku": "sku-Latn-VU", + "skv": "skv-Latn-ID", + "skw": "skw-Latn-GY", + "skx": "skx-Latn-ID", + "sky": "sky-Latn-SB", + "skz": "skz-Latn-ID", + "sl": "sl-Latn-SI", + "slc": "slc-Latn-CO", + "sld": "sld-Latn-ZZ", + "slg": "slg-Latn-ID", + "slh": "slh-Latn-US", + "sli": "sli-Latn-PL", + "slj": "slj-Latn-BR", + "sll": "sll-Latn-ZZ", + "slm": "slm-Latn-PH", + "sln": "sln-Latn-US", + "slp": "slp-Latn-ID", + "slq": "slq-Arab-IR", + "slr": "slr-Latn-CN", + "slu": "slu-Latn-ID", + "slw": "slw-Latn-PG", + "slx": "slx-Latn-CD", + "sly": "sly-Latn-ID", + "slz": "slz-Latn-ID", + "sm": "sm-Latn-WS", + "sma": "sma-Latn-SE", + "smb": "smb-Latn-PG", + "smc": "smc-Latn-PG", + "smd": "kmb-Latn-AO", + "smf": "smf-Latn-PG", + "smg": "smg-Latn-PG", + "smh": "smh-Yiii-CN", + "smj": "smj-Latn-SE", + "smk": "smk-Latn-PH", + "sml": "sml-Latn-PH", + "smn": "smn-Latn-FI", + "smp": "smp-Samr-IL", + "smq": "smq-Latn-ZZ", + "smr": "smr-Latn-ID", + "sms": "sms-Latn-FI", + "smt": "smt-Latn-IN", + "smu": "smu-Khmr-KH", + "smw": "smw-Latn-ID", + "smx": "smx-Latn-CD", + "smy": "smy-Arab-IR", + "smz": "smz-Latn-PG", + "sn": "sn-Latn-ZW", + "snb": "iba-Latn-MY", + "snc": "snc-Latn-ZZ", + "sne": "sne-Latn-MY", + "snf": "snf-Latn-SN", + "sng": "sng-Latn-CD", + "sng-Brai": "sng-Brai-CD", + "sni": "sni-Latn-PE", + "snj": "snj-Latn-CF", + "snk": "snk-Latn-ML", + "snl": "snl-Latn-PH", + "snm": "snm-Latn-UG", + "snn": "snn-Latn-CO", + "sno": "sno-Latn-US", + "snp": "snp-Latn-ZZ", + "snq": "snq-Latn-GA", + "snr": "snr-Latn-PG", + "sns": "sns-Latn-VU", + "snu": "snu-Latn-ID", + "snv": "snv-Latn-MY", + "snw": "snw-Latn-GH", + "snx": "snx-Latn-ZZ", + "sny": "sny-Latn-ZZ", + "snz": "snz-Latn-PG", + "so": "so-Latn-SO", + "soa": "soa-Tavt-TH", + "soa-Thai": "soa-Thai-TH", + "sob": "sob-Latn-ID", + "soc": "soc-Latn-CD", + "sod": "sod-Latn-CD", + "soe": "soe-Latn-CD", + "sog": "sog-Sogd-UZ", + "soi": "soi-Deva-NP", + "sok": "sok-Latn-ZZ", + "sol": "sol-Latn-PG", + "soo": "soo-Latn-CD", + "sop": "sop-Latn-CD", + "soq": "soq-Latn-ZZ", + "sor": "sor-Latn-TD", + "sos": "sos-Latn-BF", + "sou": "sou-Thai-TH", + "sov": "sov-Latn-PW", + "sow": "sow-Latn-PG", + "sox": "sox-Latn-CM", + "soy": "soy-Latn-ZZ", + "soz": "soz-Latn-TZ", + "spb": "spb-Latn-ID", + "spc": "spc-Latn-VE", + "spd": "spd-Latn-ZZ", + "spe": "spe-Latn-PG", + "spg": "spg-Latn-MY", + "spi": "spi-Latn-ID", + "spk": "spk-Latn-PG", + "spl": "spl-Latn-ZZ", + "spm": "spm-Latn-PG", + "spn": "spn-Latn-PY", + "spo": "spo-Latn-US", + "spp": "spp-Latn-ML", + "spq": "spq-Latn-PE", + "spr": "spr-Latn-ID", + "sps": "sps-Latn-ZZ", + "spt": "spt-Tibt-IN", + "spv": "spv-Orya-IN", + "sq": "sq-Latn-AL", + "sqa": "sqa-Latn-NG", + "sqh": "sqh-Latn-NG", + "sqm": "sqm-Latn-CF", + "sqo": "sqo-Arab-IR", + "sqq": "sqq-Laoo-LA", + "sqt": "sqt-Arab-YE", + "sqt-Latn": "sqt-Latn-YE", + "squ": "squ-Latn-CA", + "sr": "sr-Cyrl-RS", + "sr-ME": "sr-Latn-ME", + "sr-RO": "sr-Latn-RO", + "sr-RU": "sr-Latn-RU", + "sr-TR": "sr-Latn-TR", + "sra": "sra-Latn-PG", + "srb": "srb-Sora-IN", + "sre": "sre-Latn-ID", + "srf": "srf-Latn-PG", + "srg": "srg-Latn-PH", + "srh": "srh-Arab-CN", + "sri": "sri-Latn-CO", + "srk": "srk-Latn-MY", + "srl": "srl-Latn-ID", + "srm": "srm-Latn-SR", + "srn": "srn-Latn-SR", + "sro": "sro-Latn-IT", + "srq": "srq-Latn-BO", + "srr": "srr-Latn-SN", + "srs": "srs-Latn-CA", + "srt": "srt-Latn-ID", + "sru": "sru-Latn-BR", + "srv": "srv-Latn-PH", + "srw": "srw-Latn-ID", + "srx": "srx-Deva-IN", + "sry": "sry-Latn-PG", + "srz": "srz-Arab-IR", + "ss": "ss-Latn-ZA", + "ssb": "ssb-Latn-PH", + "ssc": "ssc-Latn-TZ", + "ssd": "ssd-Latn-ZZ", + "sse": "sse-Latn-PH", + "sse-Arab": "sse-Arab-PH", + "ssf": "ssf-Latn-TW", + "ssg": "ssg-Latn-ZZ", + "ssh": "ssh-Arab-AE", + "ssj": "ssj-Latn-PG", + "ssl": "ssl-Latn-GH", + "ssm": "ssm-Latn-MY", + "ssn": "ssn-Latn-KE", + "sso": "sso-Latn-PG", + "ssq": "ssq-Latn-ID", + "sss": "sss-Laoo-LA", + "sss-Thai": "sss-Thai-TH", + "sst": "sst-Latn-PG", + "ssu": "ssu-Latn-PG", + "ssv": "ssv-Latn-VU", + "ssx": "ssx-Latn-PG", + "ssy": "ssy-Latn-ER", + "ssz": "ssz-Latn-PG", + "st": "st-Latn-ZA", + "sta": "sta-Latn-ZM", + "stb": "stb-Latn-PH", + "ste": "ste-Latn-ID", + "stf": "stf-Latn-PG", + "stg": "stg-Latn-VN", + "sth": "sth-Latn-IE", + "sti": "sti-Latn-VN", + "sti-KH": "sti-Latn-KH", + "stj": "stj-Latn-BF", + "stk": "stk-Latn-ZZ", + "stl": "stl-Latn-NL", + "stm": "stm-Latn-PG", + "stn": "stn-Latn-SB", + "sto": "sto-Latn-CA", + "stp": "stp-Latn-MX", + "stq": "stq-Latn-DE", + "str": "str-Latn-CA", + "sts": "sts-Arab-AF", + "stt": "stt-Latn-VN", + "stv": "stv-Ethi-ET", + "stv-Arab": "stv-Arab-ET", + "stw": "stw-Latn-FM", + "sty": "sty-Cyrl-RU", + "su": "su-Latn-ID", + "sua": "sua-Latn-ZZ", + "sub": "sub-Latn-CD", + "suc": "suc-Latn-PH", + "sue": "sue-Latn-ZZ", + "sug": "sug-Latn-PG", + "sui": "sui-Latn-PG", + "suj": "suj-Latn-TZ", + "suk": "suk-Latn-TZ", + "suo": "suo-Latn-PG", + "suq": "suq-Latn-ET", + "suq-Ethi": "suq-Ethi-ET", + "sur": "sur-Latn-ZZ", + "sus": "sus-Latn-GN", + "sut": "sut-Latn-NI", + "suv": "suv-Latn-IN", + "suv-Beng": "suv-Beng-IN", + "suv-Deva": "suv-Deva-IN", + "suw": "suw-Latn-TZ", + "suy": "suy-Latn-BR", + "suz": "suz-Deva-NP", + "sv": "sv-Latn-SE", + "sva": "sva-Geor-GE", + "sva-Cyrl": "sva-Cyrl-GE", + "sva-Latn": "sva-Latn-GE", + "svb": "svb-Latn-PG", + "svc": "svc-Latn-VC", + "sve": "sve-Latn-ID", + "svm": "svm-Latn-IT", + "svs": "svs-Latn-SB", + "sw": "sw-Latn-TZ", + "swb": "swb-Arab-YT", + "swc": "sw-Latn-CD", + "swf": "swf-Latn-CD", + "swg": "swg-Latn-DE", + "swi": "swi-Hani-CN", + "swj": "swj-Latn-GA", + "swk": "swk-Latn-MW", + "swm": "swm-Latn-PG", + "swo": "swo-Latn-BR", + "swp": "swp-Latn-ZZ", + "swq": "swq-Latn-CM", + "swr": "swr-Latn-ID", + "sws": "sws-Latn-ID", + "swt": "swt-Latn-ID", + "swu": "swu-Latn-ID", + "swv": "swv-Deva-IN", + "sww": "sww-Latn-VU", + "swx": "swx-Latn-BR", + "swy": "swy-Latn-TD", + "sxb": "sxb-Latn-KE", + "sxe": "sxe-Latn-GA", + "sxn": "sxn-Latn-ID", + "sxr": "sxr-Latn-TW", + "sxs": "sxs-Latn-NG", + "sxu": "sxu-Latn-DE", + "sxu-Runr": "sxu-Runr-DE", + "sxw": "sxw-Latn-ZZ", + "sya": "sya-Latn-ID", + "syb": "syb-Latn-PH", + "syc": "syc-Syrc-TR", + "syi": "syi-Latn-GA", + "syk": "syk-Latn-NG", + "syl": "syl-Beng-BD", + "sym": "sym-Latn-BF", + "syn": "syn-Syrc-IR", + "syo": "syo-Latn-KH", + "syr": "syr-Syrc-IQ", + "sys": "sys-Latn-TD", + "syw": "syw-Deva-NP", + "syx": "syx-Latn-GA", + "sza": "sza-Latn-MY", + "szb": "szb-Latn-ID", + "szc": "szc-Latn-MY", + "szd": "szd-Latn-MY", + "szg": "szg-Latn-CD", + "szl": "szl-Latn-PL", + "szn": "szn-Latn-ID", + "szp": "szp-Latn-ID", + "szv": "szv-Latn-CM", + "szw": "szw-Latn-ID", + "szy": "szy-Latn-TW", + "ta": "ta-Taml-IN", + "taa": "taa-Latn-US", + "tab": "tab-Cyrl-RU", + "tac": "tac-Latn-MX", + "tad": "tad-Latn-ID", + "tae": "tae-Latn-BR", + "taf": "taf-Latn-BR", + "tag": "tag-Latn-SD", + "taj": "taj-Deva-NP", + "tak": "tak-Latn-NG", + "tal": "tal-Latn-ZZ", + "tan": "tan-Latn-ZZ", + "tao": "tao-Latn-TW", + "tap": "tap-Latn-CD", + "taq": "taq-Latn-ZZ", + "tar": "tar-Latn-MX", + "tas": "tas-Latn-VN", + "tau": "tau-Latn-US", + "tav": "tav-Latn-CO", + "taw": "taw-Latn-PG", + "tax": "tax-Latn-TD", + "tay": "tay-Latn-TW", + "tay-Hans": "tay-Hans-TW", + "tay-Hant": "tay-Hant-TW", + "taz": "taz-Latn-SD", + "tba": "tba-Latn-BR", + "tbc": "tbc-Latn-ZZ", + "tbd": "tbd-Latn-ZZ", + "tbe": "tbe-Latn-SB", + "tbf": "tbf-Latn-ZZ", + "tbg": "tbg-Latn-ZZ", + "tbh": "tbh-Latn-AU", + "tbi": "tbi-Latn-SD", + "tbj": "tbj-Latn-PG", + "tbk": "tbk-Tagb-PH", + "tbk-Hano": "tbk-Hano-PH", + "tbk-Latn": "tbk-Latn-PH", + "tbl": "tbl-Latn-PH", + "tbm": "tbm-Latn-CD", + "tbn": "tbn-Latn-CO", + "tbo": "tbo-Latn-ZZ", + "tbp": "tbp-Latn-ID", + "tbs": "tbs-Latn-PG", + "tbt": "tbt-Latn-CD", + "tbu": "tbu-Latn-MX", + "tbv": "tbv-Latn-PG", + "tbw": "tbw-Latn-PH", + "tbx": "tbx-Latn-PG", + "tby": "tby-Latn-ID", + "tbz": "tbz-Latn-ZZ", + "tca": "tca-Latn-BR", + "tcb": "tcb-Latn-US", + "tcc": "tcc-Latn-TZ", + "tcd": "tcd-Latn-GH", + "tce": "tce-Latn-CA", + "tcf": "tcf-Latn-MX", + "tcg": "tcg-Latn-ID", + "tch": "tch-Latn-TC", + "tci": "tci-Latn-ZZ", + "tck": "tck-Latn-GA", + "tcm": "tcm-Latn-ID", + "tcn": "tcn-Tibt-NP", + "tco": "tco-Mymr-MM", + "tcp": "tcp-Latn-MM", + "tcq": "tcq-Latn-ID", + "tcs": "tcs-Latn-AU", + "tcu": "tcu-Latn-MX", + "tcw": "tcw-Latn-MX", + "tcx": "tcx-Taml-IN", + "tcy": "tcy-Knda-IN", + "tcz": "tcz-Latn-IN", + "tda": "tda-Tfng-NE", + "tda-Arab": "tda-Arab-NE", + "tda-Latn": "tda-Latn-NE", + "tdb": "tdb-Deva-IN", + "tdb-Beng": "tdb-Beng-IN", + "tdb-Kthi": "tdb-Kthi-IN", + "tdc": "tdc-Latn-CO", + "tdd": "tdd-Tale-CN", + "tde": "tde-Latn-ML", + "tdg": "tdg-Deva-NP", + "tdh": "tdh-Deva-NP", + "tdi": "tdi-Latn-ID", + "tdj": "tdj-Latn-ID", + "tdk": "tdk-Latn-NG", + "tdl": "tdl-Latn-NG", + "tdm": "tdm-Latn-GY", + "tdn": "tdn-Latn-ID", + "tdo": "tdo-Latn-NG", + "tdq": "tdq-Latn-NG", + "tdr": "tdr-Latn-VN", + "tds": "tds-Latn-ID", + "tdt": "tdt-Latn-TL", + "tdu": "dtp-Latn-MY", + "tdv": "tdv-Latn-NG", + "tdx": "tdx-Latn-MG", + "tdy": "tdy-Latn-PH", + "te": "te-Telu-IN", + "tea": "tea-Latn-MY", + "teb": "teb-Latn-EC", + "tec": "tec-Latn-KE", + "ted": "ted-Latn-ZZ", + "tee": "tee-Latn-MX", + "teg": "teg-Latn-GA", + "teh": "teh-Latn-AR", + "tei": "tei-Latn-PG", + "tek": "tek-Latn-CD", + "tem": "tem-Latn-SL", + "ten": "ten-Latn-CO", + "teo": "teo-Latn-UG", + "tep": "tep-Latn-MX", + "teq": "teq-Latn-SD", + "ter": "ter-Latn-BR", + "tes": "tes-Java-ID", + "tet": "tet-Latn-TL", + "teu": "teu-Latn-UG", + "tev": "tev-Latn-ID", + "tew": "tew-Latn-US", + "tex": "tex-Latn-SS", + "tey": "tey-Latn-SD", + "tfi": "tfi-Latn-ZZ", + "tfn": "tfn-Latn-US", + "tfo": "tfo-Latn-ID", + "tfr": "tfr-Latn-PA", + "tft": "tft-Latn-ID", + "tg": "tg-Cyrl-TJ", + "tg-Arab": "tg-Arab-PK", + "tg-PK": "tg-Arab-PK", + "tga": "tga-Latn-KE", + "tgb": "tgb-Latn-MY", + "tgc": "tgc-Latn-ZZ", + "tgd": "tgd-Latn-NG", + "tge": "tge-Deva-NP", + "tgf": "tgf-Tibt-BT", + "tgh": "tgh-Latn-TT", + "tgi": "tgi-Latn-PG", + "tgj": "tgj-Latn-IN", + "tgn": "tgn-Latn-PH", + "tgo": "tgo-Latn-ZZ", + "tgp": "tgp-Latn-VU", + "tgq": "tgq-Latn-MY", + "tgs": "tgs-Latn-VU", + "tgt": "tgt-Latn-PH", + "tgt-Hano": "tgt-Hano-PH", + "tgt-Tagb": "tgt-Tagb-PH", + "tgu": "tgu-Latn-ZZ", + "tgv": "tgv-Latn-BR", + "tgw": "tgw-Latn-CI", + "tgx": "tgx-Latn-CA", + "tgy": "tgy-Latn-SS", + "tgz": "tgz-Latn-AU", + "th": "th-Thai-TH", + "thd": "thd-Latn-AU", + "the": "the-Deva-NP", + "thf": "thf-Deva-NP", + "thh": "thh-Latn-MX", + "thi": "thi-Tale-LA", + "thk": "thk-Latn-KE", + "thl": "thl-Deva-NP", + "thm": "thm-Thai-TH", + "thp": "thp-Latn-CA", + "thp-Dupl": "thp-Dupl-CA", + "thq": "thq-Deva-NP", + "thr": "thr-Deva-NP", + "ths": "ths-Deva-NP", + "tht": "tht-Latn-CA", + "thu": "thu-Latn-SS", + "thv": "thv-Latn-DZ", + "thv-Arab": "thv-Arab-DZ", + "thv-Tfng": "thv-Tfng-DZ", + "thy": "thy-Latn-NG", + "thz": "thz-Latn-NE", + "thz-Tfng": "thz-Tfng-NE", + "ti": "ti-Ethi-ET", + "tic": "tic-Latn-SD", + "tif": "tif-Latn-ZZ", + "tig": "tig-Ethi-ER", + "tih": "tih-Latn-MY", + "tii": "tii-Latn-CD", + "tij": "tij-Deva-NP", + "tik": "tik-Latn-ZZ", + "til": "til-Latn-US", + "tim": "tim-Latn-ZZ", + "tin": "tin-Cyrl-RU", + "tio": "tio-Latn-ZZ", + "tip": "tip-Latn-ID", + "tiq": "tiq-Latn-BF", + "tis": "tis-Latn-PH", + "tit": "tit-Latn-CO", + "tiu": "tiu-Latn-PH", + "tiv": "tiv-Latn-NG", + "tiw": "tiw-Latn-AU", + "tix": "tix-Latn-US", + "tiy": "tiy-Latn-PH", + "tja": "tja-Latn-LR", + "tjg": "tjg-Latn-ID", + "tji": "tji-Latn-CN", + "tjj": "tjj-Latn-AU", + "tjl": "tjl-Mymr-MM", + "tjn": "tjn-Latn-CI", + "tjo": "tjo-Arab-DZ", + "tjp": "tjp-Latn-AU", + "tjs": "tjs-Latn-CN", + "tju": "tju-Latn-AU", + "tjw": "tjw-Latn-AU", + "tk": "tk-Latn-TM", + "tka": "tka-Latn-BR", + "tkb": "tkb-Deva-IN", + "tkd": "tkd-Latn-TL", + "tke": "tke-Latn-MZ", + "tkf": "tkf-Latn-BR", + "tkg": "tkg-Latn-MG", + "tkl": "tkl-Latn-TK", + "tkp": "tkp-Latn-SB", + "tkq": "tkq-Latn-NG", + "tkr": "tkr-Latn-AZ", + "tks": "tks-Arab-IR", + "tkt": "tkt-Deva-NP", + "tku": "tku-Latn-MX", + "tkv": "tkv-Latn-PG", + "tkw": "tkw-Latn-SB", + "tkx": "tkx-Latn-ID", + "tkz": "tkz-Latn-VN", + "tl": "fil-Latn-PH", + "tla": "tla-Latn-MX", + "tlb": "tlb-Latn-ID", + "tlc": "tlc-Latn-MX", + "tld": "tld-Latn-ID", + "tlf": "tlf-Latn-ZZ", + "tlg": "tlg-Latn-ID", + "tli": "tli-Latn-US", + "tli-Cyrl": "tli-Cyrl-US", + "tlj": "tlj-Latn-UG", + "tlk": "tlk-Latn-ID", + "tll": "tll-Latn-CD", + "tlm": "tlm-Latn-VU", + "tln": "tln-Latn-ID", + "tlp": "tlp-Latn-MX", + "tlq": "tlq-Latn-MM", + "tlr": "tlr-Latn-SB", + "tls": "tls-Latn-VU", + "tlt": "tlt-Latn-ID", + "tlu": "tlu-Latn-ID", + "tlv": "tlv-Latn-ID", + "tlx": "tlx-Latn-ZZ", + "tly": "tly-Latn-AZ", + "tma": "tma-Latn-TD", + "tmb": "tmb-Latn-VU", + "tmc": "tmc-Latn-TD", + "tmd": "tmd-Latn-PG", + "tme": "tme-Latn-BR", + "tmf": "tmf-Latn-PY", + "tmg": "tmg-Latn-ID", + "tmh": "tmh-Latn-NE", + "tmi": "tmi-Latn-VU", + "tmj": "tmj-Latn-ID", + "tmk": "tmk-Deva-NP", + "tml": "tml-Latn-ID", + "tmm": "tmm-Latn-VN", + "tmn": "tmn-Latn-ID", + "tmo": "tmo-Latn-MY", + "tmq": "tmq-Latn-PG", + "tmr": "tmr-Syrc-IL", + "tmt": "tmt-Latn-VU", + "tmu": "tmu-Latn-ID", + "tmv": "tmv-Latn-CD", + "tmw": "tmw-Latn-MY", + "tmy": "tmy-Latn-ZZ", + "tmz": "tmz-Latn-VE", + "tn": "tn-Latn-ZA", + "tna": "tna-Latn-BO", + "tnb": "tnb-Latn-CO", + "tnc": "tnc-Latn-CO", + "tnd": "tnd-Latn-CO", + "tng": "tng-Latn-TD", + "tnh": "tnh-Latn-ZZ", + "tni": "tni-Latn-ID", + "tnk": "tnk-Latn-VU", + "tnl": "tnl-Latn-VU", + "tnm": "tnm-Latn-ID", + "tnn": "tnn-Latn-VU", + "tno": "tno-Latn-BO", + "tnp": "tnp-Latn-VU", + "tnq": "tnq-Latn-PR", + "tnr": "tnr-Latn-SN", + "tns": "tns-Latn-PG", + "tnt": "tnt-Latn-ID", + "tnv": "tnv-Cakm-BD", + "tnw": "tnw-Latn-ID", + "tnx": "tnx-Latn-SB", + "tny": "tny-Latn-TZ", + "to": "to-Latn-TO", + "tob": "tob-Latn-AR", + "toc": "toc-Latn-MX", + "tod": "tod-Latn-GN", + "tof": "tof-Latn-ZZ", + "tog": "tog-Latn-MW", + "toh": "toh-Latn-MZ", + "toi": "toi-Latn-ZM", + "toj": "toj-Latn-MX", + "tok": "tok-Latn-001", + "tol": "tol-Latn-US", + "tom": "tom-Latn-ID", + "too": "too-Latn-MX", + "top": "top-Latn-MX", + "toq": "toq-Latn-ZZ", + "tor": "tor-Latn-CD", + "tos": "tos-Latn-MX", + "tou": "tou-Latn-VN", + "tov": "tov-Arab-IR", + "tow": "tow-Latn-US", + "tox": "tox-Latn-PW", + "toy": "toy-Latn-ID", + "toz": "toz-Latn-CM", + "tpa": "tpa-Latn-PG", + "tpc": "tpc-Latn-MX", + "tpe": "tpe-Latn-BD", + "tpe-Beng": "tpe-Beng-BD", + "tpf": "tpf-Latn-ID", + "tpg": "tpg-Latn-ID", + "tpi": "tpi-Latn-PG", + "tpj": "tpj-Latn-PY", + "tpk": "tpk-Latn-BR", + "tpl": "tpl-Latn-MX", + "tpm": "tpm-Latn-ZZ", + "tpn": "tpn-Latn-BR", + "tpp": "tpp-Latn-MX", + "tpr": "tpr-Latn-BR", + "tpt": "tpt-Latn-MX", + "tpu": "tpu-Khmr-KH", + "tpv": "tpv-Latn-MP", + "tpx": "tpx-Latn-MX", + "tpy": "tpy-Latn-BR", + "tpz": "tpz-Latn-ZZ", + "tqb": "tqb-Latn-BR", + "tql": "tql-Latn-VU", + "tqm": "tqm-Latn-PG", + "tqn": "tqn-Latn-US", + "tqo": "tqo-Latn-ZZ", + "tqp": "tqp-Latn-PG", + "tqt": "tqt-Latn-MX", + "tqu": "tqu-Latn-SB", + "tqw": "tqw-Latn-US", + "tr": "tr-Latn-TR", + "tra": "tra-Arab-AF", + "trb": "trb-Latn-PG", + "trc": "trc-Latn-MX", + "tre": "tre-Latn-ID", + "trf": "trf-Latn-TT", + "trg": "trg-Hebr-IL", + "trh": "trh-Latn-PG", + "tri": "tri-Latn-SR", + "trj": "trj-Latn-TD", + "trl": "trl-Latn-GB", + "trm": "trm-Arab-AF", + "trn": "trn-Latn-BO", + "tro": "tro-Latn-IN", + "trp": "trp-Latn-IN", + "trp-Beng": "trp-Beng-IN", + "trq": "trq-Latn-MX", + "trr": "trr-Latn-PE", + "trs": "trs-Latn-MX", + "trt": "trt-Latn-ID", + "tru": "tru-Latn-TR", + "trv": "trv-Latn-TW", + "trw": "trw-Arab-PK", + "trx": "trx-Latn-MY", + "try": "try-Latn-IN", + "trz": "trz-Latn-BR", + "ts": "ts-Latn-ZA", + "tsa": "tsa-Latn-CG", + "tsb": "tsb-Latn-ET", + "tsc": "tsc-Latn-MZ", + "tsd": "tsd-Grek-GR", + "tsf": "taj-Deva-NP", + "tsg": "tsg-Latn-PH", + "tsh": "tsh-Latn-CM", + "tsi": "tsi-Latn-CA", + "tsj": "tsj-Tibt-BT", + "tsl": "tsl-Latn-VN", + "tsp": "tsp-Latn-BF", + "tsr": "tsr-Latn-VU", + "tst": "tst-Latn-ML", + "tsu": "tsu-Latn-TW", + "tsv": "tsv-Latn-GA", + "tsw": "tsw-Latn-ZZ", + "tsx": "tsx-Latn-PG", + "tsz": "tsz-Latn-MX", + "tt": "tt-Cyrl-RU", + "ttb": "ttb-Latn-NG", + "ttc": "ttc-Latn-GT", + "ttd": "ttd-Latn-ZZ", + "tte": "tte-Latn-ZZ", + "ttf": "ttf-Latn-CM", + "tth": "tth-Laoo-LA", + "tti": "tti-Latn-ID", + "ttj": "ttj-Latn-UG", + "ttk": "ttk-Latn-CO", + "ttl": "ttl-Latn-ZM", + "ttm": "ttm-Latn-CA", + "ttn": "ttn-Latn-ID", + "tto": "tto-Laoo-LA", + "ttp": "ttp-Latn-ID", + "ttr": "ttr-Latn-ZZ", + "tts": "tts-Thai-TH", + "ttt": "ttt-Latn-AZ", + "ttu": "ttu-Latn-PG", + "ttv": "ttv-Latn-PG", + "ttw": "ttw-Latn-MY", + "tty": "tty-Latn-ID", + "tua": "tua-Latn-PG", + "tub": "tub-Latn-US", + "tuc": "tuc-Latn-PG", + "tud": "tud-Latn-BR", + "tue": "tue-Latn-CO", + "tuf": "tuf-Latn-CO", + "tug": "tug-Latn-TD", + "tuh": "tuh-Latn-ZZ", + "tui": "tui-Latn-CM", + "tuj": "tuj-Latn-ID", + "tul": "tul-Latn-ZZ", + "tum": "tum-Latn-MW", + "tun": "tun-Latn-US", + "tuo": "tuo-Latn-BR", + "tuq": "tuq-Latn-ZZ", + "tus": "tus-Latn-CA", + "tuu": "tuu-Latn-US", + "tuv": "tuv-Latn-KE", + "tux": "tux-Latn-BR", + "tuy": "tuy-Latn-KE", + "tuz": "tuz-Latn-BF", + "tva": "tva-Latn-SB", + "tvd": "tvd-Latn-ZZ", + "tve": "tve-Latn-ID", + "tvk": "tvk-Latn-VU", + "tvl": "tvl-Latn-TV", + "tvm": "tvm-Latn-ID", + "tvn": "tvn-Mymr-MM", + "tvo": "tvo-Latn-ID", + "tvs": "tvs-Latn-KE", + "tvt": "tvt-Latn-IN", + "tvu": "tvu-Latn-ZZ", + "tvw": "tvw-Latn-ID", + "tvx": "tvx-Latn-TW", + "twa": "twa-Latn-US", + "twb": "twb-Latn-PH", + "twd": "twd-Latn-NL", + "twe": "twe-Latn-ID", + "twf": "twf-Latn-US", + "twg": "twg-Latn-ID", + "twh": "twh-Latn-ZZ", + "twl": "twl-Latn-MZ", + "twm": "twm-Deva-IN", + "twn": "twn-Latn-CM", + "two": "two-Latn-BW", + "twp": "twp-Latn-PG", + "twq": "twq-Latn-NE", + "twr": "twr-Latn-MX", + "twt": "twt-Latn-BR", + "twu": "twu-Latn-ID", + "tww": "tww-Latn-PG", + "twx": "twx-Latn-MZ", + "twy": "twy-Latn-ID", + "txa": "txa-Latn-MY", + "txe": "txe-Latn-ID", + "txg": "txg-Tang-CN", + "txi": "txi-Latn-BR", + "txj": "txj-Latn-NG", + "txm": "txm-Latn-ID", + "txn": "txn-Latn-ID", + "txo": "txo-Toto-IN", + "txq": "txq-Latn-ID", + "txs": "txs-Latn-ID", + "txt": "txt-Latn-ID", + "txu": "txu-Latn-BR", + "txx": "txx-Latn-MY", + "txy": "txy-Latn-MG", + "ty": "ty-Latn-PF", + "tya": "tya-Latn-ZZ", + "tye": "tye-Latn-NG", + "tyh": "tyh-Latn-VN", + "tyi": "tyi-Latn-CG", + "tyj": "tyj-Latn-VN", + "tyl": "tyl-Latn-VN", + "tyn": "tyn-Latn-ID", + "typ": "typ-Latn-AU", + "tyr": "tyr-Tavt-VN", + "tys": "tys-Latn-VN", + "tyt": "tyt-Latn-VN", + "tyt-Tavt": "tyt-Tavt-VN", + "tyu": "tyu-Latn-BW", + "tyv": "tyv-Cyrl-RU", + "tyx": "tyx-Latn-CG", + "tyy": "tyy-Latn-NG", + "tyz": "tyz-Latn-VN", + "tzh": "tzh-Latn-MX", + "tzj": "tzj-Latn-GT", + "tzl": "tzl-Latn-001", + "tzm": "tzm-Latn-MA", + "tzn": "tzn-Latn-ID", + "tzo": "tzo-Latn-MX", + "tzx": "tzx-Latn-PG", + "uam": "uam-Latn-BR", + "uar": "uar-Latn-PG", + "uba": "uba-Latn-NG", + "ubi": "ubi-Latn-TD", + "ubl": "ubl-Latn-PH", + "ubr": "ubr-Latn-PG", + "ubu": "ubu-Latn-ZZ", + "uda": "uda-Latn-NG", + "ude": "ude-Cyrl-RU", + "udg": "udg-Mlym-IN", + "udi": "udi-Aghb-RU", + "udj": "udj-Latn-ID", + "udl": "udl-Latn-CM", + "udm": "udm-Cyrl-RU", + "udu": "udu-Latn-SD", + "ues": "ues-Latn-ID", + "ufi": "ufi-Latn-PG", + "ug": "ug-Arab-CN", + "ug-Cyrl": "ug-Cyrl-KZ", + "ug-KZ": "ug-Cyrl-KZ", + "ug-MN": "ug-Cyrl-MN", + "uga": "uga-Ugar-SY", + "ugb": "ugb-Latn-AU", + "uge": "uge-Latn-SB", + "ugh": "ugh-Cyrl-RU", + "ugo": "ugo-Thai-TH", + "uha": "uha-Latn-NG", + "uhn": "uhn-Latn-ID", + "uis": "uis-Latn-PG", + "uiv": "uiv-Latn-CM", + "uji": "uji-Latn-NG", + "uk": "uk-Cyrl-UA", + "uka": "uka-Latn-ID", + "ukg": "ukg-Latn-PG", + "ukh": "ukh-Latn-CF", + "uki": "uki-Orya-IN", + "ukk": "ukk-Latn-MM", + "ukp": "ukp-Latn-NG", + "ukq": "ukq-Latn-NG", + "uku": "uku-Latn-NG", + "ukv": "ukv-Latn-SS", + "ukw": "ukw-Latn-NG", + "uky": "uky-Latn-AU", + "ula": "ula-Latn-NG", + "ulb": "ulb-Latn-NG", + "ulc": "ulc-Cyrl-RU", + "ule": "ule-Latn-AR", + "ulf": "ulf-Latn-ID", + "uli": "uli-Latn-FM", + "ulk": "ulk-Latn-AU", + "ulm": "ulm-Latn-ID", + "uln": "uln-Latn-PG", + "ulu": "ulu-Latn-ID", + "ulw": "ulw-Latn-NI", + "uma": "uma-Latn-US", + "umb": "umb-Latn-AO", + "umd": "umd-Latn-AU", + "umg": "umg-Latn-AU", + "umi": "umi-Latn-MY", + "umm": "umm-Latn-NG", + "umn": "umn-Latn-MM", + "umo": "umo-Latn-BR", + "ump": "ump-Latn-AU", + "umr": "umr-Latn-AU", + "ums": "ums-Latn-ID", + "una": "una-Latn-PG", + "und": "en-Latn-US", + "und-002": "en-Latn-NG", + "und-003": "en-Latn-US", + "und-005": "pt-Latn-BR", + "und-009": "en-Latn-AU", + "und-011": "en-Latn-NG", + "und-013": "es-Latn-MX", + "und-014": "sw-Latn-TZ", + "und-015": "ar-Arab-EG", + "und-017": "sw-Latn-CD", + "und-018": "en-Latn-ZA", + "und-019": "en-Latn-US", + "und-021": "en-Latn-US", + "und-029": "es-Latn-CU", + "und-030": "zh-Hans-CN", + "und-034": "hi-Deva-IN", + "und-035": "id-Latn-ID", + "und-039": "it-Latn-IT", + "und-053": "en-Latn-AU", + "und-054": "en-Latn-PG", + "und-057": "en-Latn-GU", + "und-061": "sm-Latn-WS", + "und-142": "zh-Hans-CN", + "und-143": "uz-Latn-UZ", + "und-145": "ar-Arab-SA", + "und-150": "ru-Cyrl-RU", + "und-151": "ru-Cyrl-RU", + "und-154": "en-Latn-GB", + "und-155": "de-Latn-DE", + "und-202": "en-Latn-NG", + "und-419": "es-Latn-419", + "und-AD": "ca-Latn-AD", + "und-AE": "ar-Arab-AE", + "und-AF": "fa-Arab-AF", + "und-AL": "sq-Latn-AL", + "und-AM": "hy-Armn-AM", + "und-AO": "pt-Latn-AO", + "und-AQ": "und-Latn-AQ", + "und-AR": "es-Latn-AR", + "und-AS": "sm-Latn-AS", + "und-AT": "de-Latn-AT", + "und-AW": "nl-Latn-AW", + "und-AX": "sv-Latn-AX", + "und-AZ": "az-Latn-AZ", + "und-Adlm": "ff-Adlm-GN", + "und-Aghb": "udi-Aghb-RU", + "und-Ahom": "aho-Ahom-IN", + "und-Arab": "ar-Arab-EG", + "und-Arab-CC": "ms-Arab-CC", + "und-Arab-CN": "ug-Arab-CN", + "und-Arab-GB": "ur-Arab-GB", + "und-Arab-ID": "ms-Arab-ID", + "und-Arab-IN": "ur-Arab-IN", + "und-Arab-KH": "cja-Arab-KH", + "und-Arab-MM": "rhg-Arab-MM", + "und-Arab-MN": "kk-Arab-MN", + "und-Arab-MU": "ur-Arab-MU", + "und-Arab-NG": "ha-Arab-NG", + "und-Arab-PK": "ur-Arab-PK", + "und-Arab-TG": "apd-Arab-TG", + "und-Arab-TH": "mfa-Arab-TH", + "und-Arab-TJ": "fa-Arab-TJ", + "und-Arab-TR": "apc-Arab-TR", + "und-Arab-YT": "swb-Arab-YT", + "und-Armi": "arc-Armi-IR", + "und-Armn": "hy-Armn-AM", + "und-Avst": "ae-Avst-IR", + "und-BA": "bs-Latn-BA", + "und-BD": "bn-Beng-BD", + "und-BE": "nl-Latn-BE", + "und-BF": "fr-Latn-BF", + "und-BG": "bg-Cyrl-BG", + "und-BH": "ar-Arab-BH", + "und-BI": "rn-Latn-BI", + "und-BJ": "fr-Latn-BJ", + "und-BL": "fr-Latn-BL", + "und-BN": "ms-Latn-BN", + "und-BO": "es-Latn-BO", + "und-BQ": "pap-Latn-BQ", + "und-BR": "pt-Latn-BR", + "und-BT": "dz-Tibt-BT", + "und-BV": "und-Latn-BV", + "und-BY": "be-Cyrl-BY", + "und-Bali": "ban-Bali-ID", + "und-Bamu": "bax-Bamu-CM", + "und-Bass": "bsq-Bass-LR", + "und-Batk": "bbc-Batk-ID", + "und-Beng": "bn-Beng-BD", + "und-Bhks": "sa-Bhks-IN", + "und-Bopo": "zh-Bopo-TW", + "und-Brah": "pka-Brah-IN", + "und-Brai": "fr-Brai-FR", + "und-Bugi": "bug-Bugi-ID", + "und-Buhd": "bku-Buhd-PH", + "und-CD": "sw-Latn-CD", + "und-CF": "fr-Latn-CF", + "und-CG": "fr-Latn-CG", + "und-CH": "de-Latn-CH", + "und-CI": "fr-Latn-CI", + "und-CL": "es-Latn-CL", + "und-CM": "fr-Latn-CM", + "und-CN": "zh-Hans-CN", + "und-CO": "es-Latn-CO", + "und-CP": "und-Latn-CP", + "und-CR": "es-Latn-CR", + "und-CU": "es-Latn-CU", + "und-CV": "pt-Latn-CV", + "und-CW": "pap-Latn-CW", + "und-CY": "el-Grek-CY", + "und-CZ": "cs-Latn-CZ", + "und-Cakm": "ccp-Cakm-BD", + "und-Cans": "iu-Cans-CA", + "und-Cari": "xcr-Cari-TR", + "und-Cham": "cjm-Cham-VN", + "und-Cher": "chr-Cher-US", + "und-Chrs": "xco-Chrs-UZ", + "und-Copt": "cop-Copt-EG", + "und-Cpmn": "und-Cpmn-CY", + "und-Cpmn-CY": "und-Cpmn-CY", + "und-Cprt": "grc-Cprt-CY", + "und-Cyrl": "ru-Cyrl-RU", + "und-Cyrl-AL": "mk-Cyrl-AL", + "und-Cyrl-BA": "sr-Cyrl-BA", + "und-Cyrl-GE": "ab-Cyrl-GE", + "und-Cyrl-GR": "mk-Cyrl-GR", + "und-Cyrl-MD": "uk-Cyrl-MD", + "und-Cyrl-RO": "bg-Cyrl-RO", + "und-Cyrl-SK": "uk-Cyrl-SK", + "und-Cyrl-TR": "kbd-Cyrl-TR", + "und-Cyrl-XK": "sr-Cyrl-XK", + "und-DE": "de-Latn-DE", + "und-DJ": "aa-Latn-DJ", + "und-DK": "da-Latn-DK", + "und-DO": "es-Latn-DO", + "und-DZ": "ar-Arab-DZ", + "und-Deva": "hi-Deva-IN", + "und-Deva-BT": "ne-Deva-BT", + "und-Deva-FJ": "hif-Deva-FJ", + "und-Deva-MU": "bho-Deva-MU", + "und-Deva-PK": "btv-Deva-PK", + "und-Diak": "dv-Diak-MV", + "und-Dogr": "doi-Dogr-IN", + "und-Dupl": "fr-Dupl-FR", + "und-EA": "es-Latn-EA", + "und-EC": "es-Latn-EC", + "und-EE": "et-Latn-EE", + "und-EG": "ar-Arab-EG", + "und-EH": "ar-Arab-EH", + "und-ER": "ti-Ethi-ER", + "und-ES": "es-Latn-ES", + "und-ET": "am-Ethi-ET", + "und-EU": "en-Latn-IE", + "und-EZ": "de-Latn-EZ", + "und-Egyp": "egy-Egyp-EG", + "und-Elba": "sq-Elba-AL", + "und-Elym": "arc-Elym-IR", + "und-Ethi": "am-Ethi-ET", + "und-FI": "fi-Latn-FI", + "und-FO": "fo-Latn-FO", + "und-FR": "fr-Latn-FR", + "und-GA": "fr-Latn-GA", + "und-GE": "ka-Geor-GE", + "und-GF": "fr-Latn-GF", + "und-GH": "ak-Latn-GH", + "und-GL": "kl-Latn-GL", + "und-GN": "fr-Latn-GN", + "und-GP": "fr-Latn-GP", + "und-GQ": "es-Latn-GQ", + "und-GR": "el-Grek-GR", + "und-GS": "und-Latn-GS", + "und-GT": "es-Latn-GT", + "und-GW": "pt-Latn-GW", + "und-Geor": "ka-Geor-GE", + "und-Glag": "cu-Glag-BG", + "und-Gong": "wsg-Gong-IN", + "und-Gonm": "esg-Gonm-IN", + "und-Goth": "got-Goth-UA", + "und-Gran": "sa-Gran-IN", + "und-Grek": "el-Grek-GR", + "und-Grek-TR": "bgx-Grek-TR", + "und-Gujr": "gu-Gujr-IN", + "und-Guru": "pa-Guru-IN", + "und-HK": "zh-Hant-HK", + "und-HM": "und-Latn-HM", + "und-HN": "es-Latn-HN", + "und-HR": "hr-Latn-HR", + "und-HT": "ht-Latn-HT", + "und-HU": "hu-Latn-HU", + "und-Hanb": "zh-Hanb-TW", + "und-Hang": "ko-Hang-KR", + "und-Hani": "zh-Hani-CN", + "und-Hano": "hnn-Hano-PH", + "und-Hans": "zh-Hans-CN", + "und-Hant": "zh-Hant-TW", + "und-Hant-CA": "yue-Hant-CA", + "und-Hebr": "he-Hebr-IL", + "und-Hebr-SE": "yi-Hebr-SE", + "und-Hebr-UA": "yi-Hebr-UA", + "und-Hebr-US": "yi-Hebr-US", + "und-Hira": "ja-Hira-JP", + "und-Hluw": "hlu-Hluw-TR", + "und-Hmng": "hnj-Hmng-LA", + "und-Hmnp": "hnj-Hmnp-US", + "und-Hung": "hu-Hung-HU", + "und-IC": "es-Latn-IC", + "und-ID": "id-Latn-ID", + "und-IL": "he-Hebr-IL", + "und-IN": "hi-Deva-IN", + "und-IQ": "ar-Arab-IQ", + "und-IR": "fa-Arab-IR", + "und-IS": "is-Latn-IS", + "und-IT": "it-Latn-IT", + "und-Ital": "ett-Ital-IT", + "und-JO": "ar-Arab-JO", + "und-JP": "ja-Jpan-JP", + "und-Jamo": "ko-Jamo-KR", + "und-Java": "jv-Java-ID", + "und-Jpan": "ja-Jpan-JP", + "und-KE": "sw-Latn-KE", + "und-KG": "ky-Cyrl-KG", + "und-KH": "km-Khmr-KH", + "und-KM": "ar-Arab-KM", + "und-KP": "ko-Kore-KP", + "und-KR": "ko-Kore-KR", + "und-KW": "ar-Arab-KW", + "und-KZ": "ru-Cyrl-KZ", + "und-Kali": "eky-Kali-MM", + "und-Kana": "ja-Kana-JP", + "und-Kawi": "kaw-Kawi-ID", + "und-Khar": "pra-Khar-PK", + "und-Khmr": "km-Khmr-KH", + "und-Khoj": "sd-Khoj-IN", + "und-Kits": "zkt-Kits-CN", + "und-Knda": "kn-Knda-IN", + "und-Kore": "ko-Kore-KR", + "und-Kthi": "bho-Kthi-IN", + "und-LA": "lo-Laoo-LA", + "und-LB": "ar-Arab-LB", + "und-LI": "de-Latn-LI", + "und-LK": "si-Sinh-LK", + "und-LS": "st-Latn-LS", + "und-LT": "lt-Latn-LT", + "und-LU": "fr-Latn-LU", + "und-LV": "lv-Latn-LV", + "und-LY": "ar-Arab-LY", + "und-Lana": "nod-Lana-TH", + "und-Laoo": "lo-Laoo-LA", + "und-Laoo-AU": "hnj-Laoo-AU", + "und-Laoo-CN": "hnj-Laoo-CN", + "und-Laoo-FR": "hnj-Laoo-FR", + "und-Laoo-GF": "hnj-Laoo-GF", + "und-Laoo-MM": "hnj-Laoo-MM", + "und-Laoo-SR": "hnj-Laoo-SR", + "und-Laoo-TH": "hnj-Laoo-TH", + "und-Laoo-US": "hnj-Laoo-US", + "und-Laoo-VN": "hnj-Laoo-VN", + "und-Latn-AF": "tk-Latn-AF", + "und-Latn-AM": "ku-Latn-AM", + "und-Latn-CN": "za-Latn-CN", + "und-Latn-CY": "tr-Latn-CY", + "und-Latn-DZ": "fr-Latn-DZ", + "und-Latn-ET": "en-Latn-ET", + "und-Latn-GE": "ku-Latn-GE", + "und-Latn-IR": "tk-Latn-IR", + "und-Latn-KM": "fr-Latn-KM", + "und-Latn-MA": "fr-Latn-MA", + "und-Latn-MK": "sq-Latn-MK", + "und-Latn-MM": "kac-Latn-MM", + "und-Latn-MO": "pt-Latn-MO", + "und-Latn-MR": "fr-Latn-MR", + "und-Latn-RU": "krl-Latn-RU", + "und-Latn-SY": "fr-Latn-SY", + "und-Latn-TN": "fr-Latn-TN", + "und-Latn-TW": "trv-Latn-TW", + "und-Latn-UA": "pl-Latn-UA", + "und-Lepc": "lep-Lepc-IN", + "und-Limb": "lif-Limb-IN", + "und-Lina": "lab-Lina-GR", + "und-Linb": "grc-Linb-GR", + "und-Lisu": "lis-Lisu-CN", + "und-Lyci": "xlc-Lyci-TR", + "und-Lydi": "xld-Lydi-TR", + "und-MA": "ar-Arab-MA", + "und-MC": "fr-Latn-MC", + "und-MD": "ro-Latn-MD", + "und-ME": "sr-Latn-ME", + "und-MF": "fr-Latn-MF", + "und-MG": "mg-Latn-MG", + "und-MK": "mk-Cyrl-MK", + "und-ML": "bm-Latn-ML", + "und-MM": "my-Mymr-MM", + "und-MN": "mn-Cyrl-MN", + "und-MO": "zh-Hant-MO", + "und-MQ": "fr-Latn-MQ", + "und-MR": "ar-Arab-MR", + "und-MT": "mt-Latn-MT", + "und-MU": "mfe-Latn-MU", + "und-MV": "dv-Thaa-MV", + "und-MX": "es-Latn-MX", + "und-MY": "ms-Latn-MY", + "und-MZ": "pt-Latn-MZ", + "und-Mahj": "hi-Mahj-IN", + "und-Maka": "mak-Maka-ID", + "und-Mand": "myz-Mand-IR", + "und-Mani": "xmn-Mani-CN", + "und-Marc": "bo-Marc-CN", + "und-Medf": "dmf-Medf-NG", + "und-Mend": "men-Mend-SL", + "und-Merc": "xmr-Merc-SD", + "und-Mero": "xmr-Mero-SD", + "und-Mlym": "ml-Mlym-IN", + "und-Modi": "mr-Modi-IN", + "und-Mong": "mn-Mong-CN", + "und-Mroo": "mro-Mroo-BD", + "und-Mtei": "mni-Mtei-IN", + "und-Mult": "skr-Mult-PK", + "und-Mymr": "my-Mymr-MM", + "und-Mymr-IN": "kht-Mymr-IN", + "und-Mymr-TH": "mnw-Mymr-TH", + "und-NA": "af-Latn-NA", + "und-NC": "fr-Latn-NC", + "und-NE": "ha-Latn-NE", + "und-NI": "es-Latn-NI", + "und-NL": "nl-Latn-NL", + "und-NO": "nb-Latn-NO", + "und-NP": "ne-Deva-NP", + "und-Nagm": "unr-Nagm-IN", + "und-Nand": "sa-Nand-IN", + "und-Narb": "xna-Narb-SA", + "und-Nbat": "arc-Nbat-JO", + "und-Newa": "new-Newa-NP", + "und-Nkoo": "man-Nkoo-GN", + "und-Nshu": "zhx-Nshu-CN", + "und-OM": "ar-Arab-OM", + "und-Ogam": "sga-Ogam-IE", + "und-Olck": "sat-Olck-IN", + "und-Orkh": "otk-Orkh-MN", + "und-Orya": "or-Orya-IN", + "und-Osge": "osa-Osge-US", + "und-Osma": "so-Osma-SO", + "und-Ougr": "oui-Ougr-143", + "und-PA": "es-Latn-PA", + "und-PE": "es-Latn-PE", + "und-PF": "fr-Latn-PF", + "und-PG": "tpi-Latn-PG", + "und-PH": "fil-Latn-PH", + "und-PK": "ur-Arab-PK", + "und-PL": "pl-Latn-PL", + "und-PM": "fr-Latn-PM", + "und-PR": "es-Latn-PR", + "und-PS": "ar-Arab-PS", + "und-PT": "pt-Latn-PT", + "und-PW": "pau-Latn-PW", + "und-PY": "gn-Latn-PY", + "und-Palm": "arc-Palm-SY", + "und-Pauc": "ctd-Pauc-MM", + "und-Perm": "kv-Perm-RU", + "und-Phag": "lzh-Phag-CN", + "und-Phli": "pal-Phli-IR", + "und-Phlp": "pal-Phlp-CN", + "und-Phnx": "phn-Phnx-LB", + "und-Plrd": "hmd-Plrd-CN", + "und-Prti": "xpr-Prti-IR", + "und-QA": "ar-Arab-QA", + "und-QO": "en-Latn-DG", + "und-RE": "fr-Latn-RE", + "und-RO": "ro-Latn-RO", + "und-RS": "sr-Cyrl-RS", + "und-RU": "ru-Cyrl-RU", + "und-RW": "rw-Latn-RW", + "und-Rjng": "rej-Rjng-ID", + "und-Rohg": "rhg-Rohg-MM", + "und-Runr": "non-Runr-SE", + "und-SA": "ar-Arab-SA", + "und-SC": "fr-Latn-SC", + "und-SD": "ar-Arab-SD", + "und-SE": "sv-Latn-SE", + "und-SI": "sl-Latn-SI", + "und-SJ": "nb-Latn-SJ", + "und-SK": "sk-Latn-SK", + "und-SM": "it-Latn-SM", + "und-SN": "fr-Latn-SN", + "und-SO": "so-Latn-SO", + "und-SR": "nl-Latn-SR", + "und-ST": "pt-Latn-ST", + "und-SV": "es-Latn-SV", + "und-SY": "ar-Arab-SY", + "und-Samr": "smp-Samr-IL", + "und-Sarb": "xsa-Sarb-YE", + "und-Saur": "saz-Saur-IN", + "und-Sgnw": "ase-Sgnw-US", + "und-Shaw": "en-Shaw-GB", + "und-Shrd": "sa-Shrd-IN", + "und-Sidd": "sa-Sidd-IN", + "und-Sind": "sd-Sind-IN", + "und-Sinh": "si-Sinh-LK", + "und-Sogd": "sog-Sogd-UZ", + "und-Sogo": "sog-Sogo-UZ", + "und-Sora": "srb-Sora-IN", + "und-Soyo": "cmg-Soyo-MN", + "und-Sund": "su-Sund-ID", + "und-Sylo": "syl-Sylo-BD", + "und-Syrc": "syr-Syrc-IQ", + "und-TD": "fr-Latn-TD", + "und-TF": "fr-Latn-TF", + "und-TG": "fr-Latn-TG", + "und-TH": "th-Thai-TH", + "und-TJ": "tg-Cyrl-TJ", + "und-TK": "tkl-Latn-TK", + "und-TL": "pt-Latn-TL", + "und-TM": "tk-Latn-TM", + "und-TN": "ar-Arab-TN", + "und-TO": "to-Latn-TO", + "und-TR": "tr-Latn-TR", + "und-TV": "tvl-Latn-TV", + "und-TW": "zh-Hant-TW", + "und-TZ": "sw-Latn-TZ", + "und-Tagb": "tbw-Tagb-PH", + "und-Takr": "doi-Takr-IN", + "und-Tale": "tdd-Tale-CN", + "und-Talu": "khb-Talu-CN", + "und-Taml": "ta-Taml-IN", + "und-Tang": "txg-Tang-CN", + "und-Tavt": "blt-Tavt-VN", + "und-Telu": "te-Telu-IN", + "und-Tfng": "zgh-Tfng-MA", + "und-Tglg": "fil-Tglg-PH", + "und-Thaa": "dv-Thaa-MV", + "und-Thai": "th-Thai-TH", + "und-Thai-CN": "lcp-Thai-CN", + "und-Thai-KH": "kdt-Thai-KH", + "und-Thai-LA": "kdt-Thai-LA", + "und-Tibt": "bo-Tibt-CN", + "und-Tirh": "mai-Tirh-IN", + "und-Tnsa": "nst-Tnsa-IN", + "und-Toto": "txo-Toto-IN", + "und-UA": "uk-Cyrl-UA", + "und-UG": "sw-Latn-UG", + "und-UY": "es-Latn-UY", + "und-UZ": "uz-Latn-UZ", + "und-Ugar": "uga-Ugar-SY", + "und-VA": "it-Latn-VA", + "und-VE": "es-Latn-VE", + "und-VN": "vi-Latn-VN", + "und-VU": "bi-Latn-VU", + "und-Vaii": "vai-Vaii-LR", + "und-Vith": "sq-Vith-AL", + "und-WF": "fr-Latn-WF", + "und-WS": "sm-Latn-WS", + "und-Wara": "hoc-Wara-IN", + "und-Wcho": "nnp-Wcho-IN", + "und-XK": "sq-Latn-XK", + "und-Xpeo": "peo-Xpeo-IR", + "und-Xsux": "akk-Xsux-IQ", + "und-YE": "ar-Arab-YE", + "und-YT": "fr-Latn-YT", + "und-Yezi": "ku-Yezi-GE", + "und-Yiii": "ii-Yiii-CN", + "und-ZW": "sn-Latn-ZW", + "und-Zanb": "cmg-Zanb-MN", + "une": "une-Latn-NG", + "ung": "ung-Latn-AU", + "uni": "uni-Latn-PG", + "unk": "unk-Latn-BR", + "unm": "unm-Latn-US", + "unn": "unn-Latn-AU", + "unr": "unr-Beng-IN", + "unr-Deva": "unr-Deva-NP", + "unr-NP": "unr-Deva-NP", + "unu": "unu-Latn-PG", + "unx": "unx-Beng-IN", + "unz": "unz-Latn-ID", + "uok": "ema-Latn-ZZ", + "uon": "uon-Latn-TW", + "upi": "upi-Latn-PG", + "upv": "upv-Latn-VU", + "ur": "ur-Arab-PK", + "ura": "ura-Latn-PE", + "urb": "urb-Latn-BR", + "urc": "urc-Latn-AU", + "ure": "ure-Latn-BO", + "urf": "urf-Latn-AU", + "urg": "urg-Latn-PG", + "urh": "urh-Latn-NG", + "uri": "uri-Latn-ZZ", + "urk": "urk-Thai-TH", + "urm": "urm-Latn-PG", + "urn": "urn-Latn-ID", + "uro": "uro-Latn-PG", + "urp": "urp-Latn-BR", + "urr": "urr-Latn-VU", + "urt": "urt-Latn-ZZ", + "uru": "uru-Latn-BR", + "urv": "urv-Latn-PG", + "urw": "urw-Latn-ZZ", + "urx": "urx-Latn-PG", + "ury": "ury-Latn-ID", + "urz": "urz-Latn-BR", + "usa": "usa-Latn-ZZ", + "ush": "ush-Arab-PK", + "usi": "usi-Latn-BD", + "usi-Beng": "usi-Beng-BD", + "usk": "usk-Latn-CM", + "usp": "usp-Latn-GT", + "uss": "uss-Latn-NG", + "usu": "usu-Latn-PG", + "uta": "uta-Latn-NG", + "ute": "ute-Latn-US", + "uth": "uth-Latn-ZZ", + "utp": "utp-Latn-SB", + "utr": "utr-Latn-ZZ", + "utu": "utu-Latn-PG", + "uum": "uum-Grek-GE", + "uum-Cyrl": "uum-Cyrl-GE", + "uur": "uur-Latn-VU", + "uve": "uve-Latn-NC", + "uvh": "uvh-Latn-ZZ", + "uvl": "uvl-Latn-ZZ", + "uwa": "uwa-Latn-AU", + "uya": "uya-Latn-NG", + "uz": "uz-Latn-UZ", + "uz-AF": "uz-Arab-AF", + "uz-Arab": "uz-Arab-AF", + "uz-CN": "uz-Cyrl-CN", + "uzs": "uzs-Arab-AF", + "vaa": "vaa-Taml-IN", + "vae": "vae-Latn-CF", + "vaf": "vaf-Arab-IR", + "vag": "vag-Latn-ZZ", + "vah": "vah-Deva-IN", + "vai": "vai-Vaii-LR", + "vaj": "vaj-Latn-NA", + "val": "val-Latn-PG", + "vam": "vam-Latn-PG", + "van": "van-Latn-ZZ", + "vao": "vao-Latn-VU", + "vap": "vap-Latn-IN", + "var": "var-Latn-MX", + "vas": "vas-Deva-IN", + "vas-Gujr": "vas-Gujr-IN", + "vau": "vau-Latn-CD", + "vav": "vav-Deva-IN", + "vav-Gujr": "vav-Gujr-IN", + "vay": "vay-Deva-NP", + "vbb": "vbb-Latn-ID", + "vbk": "vbk-Latn-PH", + "ve": "ve-Latn-ZA", + "vec": "vec-Latn-IT", + "vem": "vem-Latn-NG", + "veo": "veo-Latn-US", + "vep": "vep-Latn-RU", + "ver": "ver-Latn-NG", + "vgr": "vgr-Arab-PK", + "vi": "vi-Latn-VN", + "vic": "vic-Latn-SX", + "vid": "vid-Latn-TZ", + "vif": "vif-Latn-CG", + "vig": "vig-Latn-BF", + "vil": "vil-Latn-AR", + "vin": "vin-Latn-TZ", + "vit": "vit-Latn-NG", + "viv": "viv-Latn-ZZ", + "vka": "vka-Latn-AU", + "vkj": "vkj-Latn-TD", + "vkk": "vkk-Latn-ID", + "vkl": "vkl-Latn-ID", + "vkm": "vkm-Latn-BR", + "vkn": "vkn-Latn-NG", + "vko": "vko-Latn-ID", + "vkp": "vkp-Latn-IN", + "vkp-Deva": "vkp-Deva-IN", + "vkt": "vkt-Latn-ID", + "vku": "vku-Latn-AU", + "vkz": "vkz-Latn-NG", + "vlp": "vlp-Latn-VU", + "vls": "vls-Latn-BE", + "vma": "vma-Latn-AU", + "vmb": "vmb-Latn-AU", + "vmc": "vmc-Latn-MX", + "vmd": "vmd-Knda-IN", + "vme": "vme-Latn-ID", + "vmf": "vmf-Latn-DE", + "vmg": "vmg-Latn-PG", + "vmh": "vmh-Arab-IR", + "vmi": "vmi-Latn-AU", + "vmj": "vmj-Latn-MX", + "vmk": "vmk-Latn-MZ", + "vml": "vml-Latn-AU", + "vmm": "vmm-Latn-MX", + "vmp": "vmp-Latn-MX", + "vmq": "vmq-Latn-MX", + "vmr": "vmr-Latn-MZ", + "vms": "vms-Latn-ID", + "vmu": "vmu-Latn-AU", + "vmw": "vmw-Latn-MZ", + "vmx": "vmx-Latn-MX", + "vmy": "vmy-Latn-MX", + "vmz": "vmz-Latn-MX", + "vnk": "vnk-Latn-SB", + "vnm": "vnm-Latn-VU", + "vnp": "vnp-Latn-VU", + "vo": "vo-Latn-001", + "vor": "vor-Latn-NG", + "vot": "vot-Latn-RU", + "vra": "vra-Latn-VU", + "vro": "vro-Latn-EE", + "vrs": "vrs-Latn-SB", + "vrt": "vrt-Latn-VU", + "vto": "vto-Latn-ID", + "vum": "vum-Latn-GA", + "vun": "vun-Latn-TZ", + "vut": "vut-Latn-ZZ", + "vwa": "vwa-Latn-CN", + "vwa-Mymr": "vwa-Mymr-CN", + "wa": "wa-Latn-BE", + "waa": "waa-Latn-US", + "wab": "wab-Latn-PG", + "wac": "wac-Latn-US", + "wad": "wad-Latn-ID", + "wae": "wae-Latn-CH", + "waf": "waf-Latn-BR", + "wag": "wag-Latn-PG", + "wah": "wah-Latn-ID", + "wai": "wai-Latn-ID", + "waj": "waj-Latn-ZZ", + "wal": "wal-Ethi-ET", + "wam": "wam-Latn-US", + "wan": "wan-Latn-ZZ", + "wap": "wap-Latn-GY", + "waq": "waq-Latn-AU", + "war": "war-Latn-PH", + "was": "was-Latn-US", + "wat": "wat-Latn-PG", + "wau": "wau-Latn-BR", + "wav": "wav-Latn-NG", + "waw": "waw-Latn-BR", + "wax": "wax-Latn-PG", + "way": "way-Latn-SR", + "waz": "waz-Latn-PG", + "wba": "wba-Latn-VE", + "wbb": "wbb-Latn-ID", + "wbe": "wbe-Latn-ID", + "wbf": "wbf-Latn-BF", + "wbh": "wbh-Latn-TZ", + "wbi": "wbi-Latn-TZ", + "wbj": "wbj-Latn-TZ", + "wbk": "wbk-Arab-AF", + "wbl": "wbl-Latn-PK", + "wbl-Arab": "wbl-Arab-AF", + "wbl-Cyrl": "wbl-Cyrl-TJ", + "wbm": "wbm-Latn-CN", + "wbp": "wbp-Latn-AU", + "wbq": "wbq-Telu-IN", + "wbr": "wbr-Deva-IN", + "wbt": "wbt-Latn-AU", + "wbv": "wbv-Latn-AU", + "wbw": "wbw-Latn-ID", + "wca": "wca-Latn-BR", + "wci": "wci-Latn-ZZ", + "wdd": "wdd-Latn-GA", + "wdg": "wdg-Latn-PG", + "wdj": "wdj-Latn-AU", + "wdk": "wdk-Latn-AU", + "wdt": "wdt-Latn-CA", + "wdu": "wdu-Latn-AU", + "wdy": "wdy-Latn-AU", + "wec": "wec-Latn-CI", + "wed": "wed-Latn-PG", + "weg": "weg-Latn-AU", + "weh": "weh-Latn-CM", + "wei": "wei-Latn-PG", + "wem": "wem-Latn-BJ", + "weo": "weo-Latn-ID", + "wep": "wep-Latn-DE", + "wer": "wer-Latn-ZZ", + "wes": "wes-Latn-CM", + "wet": "wet-Latn-ID", + "weu": "weu-Latn-MM", + "wew": "wew-Latn-ID", + "wfg": "wfg-Latn-ID", + "wga": "wga-Latn-AU", + "wgb": "wgb-Latn-PG", + "wgg": "wgg-Latn-AU", + "wgi": "wgi-Latn-ZZ", + "wgo": "wgo-Latn-ID", + "wgu": "wgu-Latn-AU", + "wgy": "wgy-Latn-AU", + "wha": "wha-Latn-ID", + "whg": "whg-Latn-ZZ", + "whk": "whk-Latn-ID", + "whu": "whu-Latn-ID", + "wib": "wib-Latn-ZZ", + "wic": "wic-Latn-US", + "wie": "wie-Latn-AU", + "wif": "wif-Latn-AU", + "wig": "wig-Latn-AU", + "wih": "wih-Latn-AU", + "wii": "wii-Latn-PG", + "wij": "wij-Latn-AU", + "wik": "wik-Latn-AU", + "wil": "wil-Latn-AU", + "wim": "wim-Latn-AU", + "win": "win-Latn-US", + "wir": "wir-Latn-BR", + "wiu": "wiu-Latn-ZZ", + "wiv": "wiv-Latn-ZZ", + "wiy": "wiy-Latn-US", + "wja": "wja-Latn-ZZ", + "wji": "wji-Latn-ZZ", + "wka": "wka-Latn-TZ", + "wkd": "wkd-Latn-ID", + "wkr": "wkr-Latn-AU", + "wkw": "wkw-Latn-AU", + "wky": "wky-Latn-AU", + "wla": "wla-Latn-PG", + "wlg": "wlg-Latn-AU", + "wlh": "wlh-Latn-TL", + "wli": "wli-Latn-ID", + "wlm": "wlm-Latn-GB", + "wlo": "wlo-Arab-ID", + "wlr": "wlr-Latn-VU", + "wls": "wls-Latn-WF", + "wlu": "wlu-Latn-AU", + "wlv": "wlv-Latn-AR", + "wlw": "wlw-Latn-ID", + "wlx": "wlx-Latn-GH", + "wma": "wma-Latn-NG", + "wmb": "wmb-Latn-AU", + "wmc": "wmc-Latn-PG", + "wmd": "wmd-Latn-BR", + "wme": "wme-Deva-NP", + "wmh": "wmh-Latn-TL", + "wmi": "wmi-Latn-AU", + "wmm": "wmm-Latn-ID", + "wmn": "wmn-Latn-NC", + "wmo": "wmo-Latn-ZZ", + "wms": "wms-Latn-ID", + "wmt": "wmt-Latn-AU", + "wmw": "wmw-Latn-MZ", + "wmw-Arab": "wmw-Arab-MZ", + "wmx": "wmx-Latn-PG", + "wnb": "wnb-Latn-PG", + "wnc": "wnc-Latn-ZZ", + "wnd": "wnd-Latn-AU", + "wne": "wne-Arab-PK", + "wng": "wng-Latn-ID", + "wni": "wni-Arab-KM", + "wnk": "wnk-Latn-ID", + "wnm": "wnm-Latn-AU", + "wnn": "wnn-Latn-AU", + "wno": "wno-Latn-ID", + "wnp": "wnp-Latn-PG", + "wnu": "wnu-Latn-ZZ", + "wnw": "wnw-Latn-US", + "wny": "wny-Latn-AU", + "wo": "wo-Latn-SN", + "woa": "woa-Latn-AU", + "wob": "wob-Latn-ZZ", + "woc": "woc-Latn-PG", + "wod": "wod-Latn-ID", + "woe": "woe-Latn-FM", + "wof": "wof-Latn-GM", + "wof-Arab": "wof-Arab-GM", + "wog": "wog-Latn-PG", + "woi": "woi-Latn-ID", + "wok": "wok-Latn-CM", + "wom": "wom-Latn-NG", + "won": "won-Latn-CD", + "woo": "woo-Latn-ID", + "wor": "wor-Latn-ID", + "wos": "wos-Latn-ZZ", + "wow": "wow-Latn-ID", + "wpc": "wpc-Latn-VE", + "wrb": "wrb-Latn-AU", + "wrg": "wrg-Latn-AU", + "wrh": "wrh-Latn-AU", + "wri": "wri-Latn-AU", + "wrk": "wrk-Latn-AU", + "wrl": "wrl-Latn-AU", + "wrm": "wrm-Latn-AU", + "wro": "wro-Latn-AU", + "wrp": "wrp-Latn-ID", + "wrr": "wrr-Latn-AU", + "wrs": "wrs-Latn-ZZ", + "wru": "wru-Latn-ID", + "wrv": "wrv-Latn-PG", + "wrw": "wrw-Latn-AU", + "wrx": "wrx-Latn-ID", + "wrz": "wrz-Latn-AU", + "wsa": "wsa-Latn-ID", + "wsg": "wsg-Gong-IN", + "wsi": "wsi-Latn-VU", + "wsk": "wsk-Latn-ZZ", + "wsr": "wsr-Latn-PG", + "wss": "wss-Latn-GH", + "wsu": "wsu-Latn-BR", + "wsv": "wsv-Arab-AF", + "wtf": "wtf-Latn-PG", + "wth": "wth-Latn-AU", + "wti": "wti-Latn-ET", + "wtk": "wtk-Latn-PG", + "wtm": "wtm-Deva-IN", + "wtw": "wtw-Latn-ID", + "wtw-Bugi": "wtw-Bugi-ID", + "wua": "wua-Latn-AU", + "wub": "wub-Latn-AU", + "wud": "wud-Latn-TG", + "wul": "wul-Latn-ID", + "wum": "wum-Latn-GA", + "wun": "wun-Latn-TZ", + "wur": "wur-Latn-AU", + "wut": "wut-Latn-PG", + "wuu": "wuu-Hans-CN", + "wuv": "wuv-Latn-ZZ", + "wux": "wux-Latn-AU", + "wuy": "wuy-Latn-ID", + "wwa": "wwa-Latn-ZZ", + "wwb": "wwb-Latn-AU", + "wwo": "wwo-Latn-VU", + "wwr": "wwr-Latn-AU", + "www": "www-Latn-CM", + "wxw": "wxw-Latn-AU", + "wyb": "wyb-Latn-AU", + "wyi": "wyi-Latn-AU", + "wym": "wym-Latn-PL", + "wyn": "wyn-Latn-US", + "wyr": "wyr-Latn-BR", + "wyy": "wyy-Latn-FJ", + "xaa": "xaa-Latn-ES", + "xab": "xab-Latn-NG", + "xai": "xai-Latn-BR", + "xaj": "xaj-Latn-BR", + "xak": "xak-Latn-VE", + "xal": "xal-Cyrl-RU", + "xam": "xam-Latn-ZA", + "xan": "xan-Ethi-ET", + "xao": "xao-Latn-VN", + "xar": "xar-Latn-PG", + "xas": "xas-Cyrl-RU", + "xat": "xat-Latn-BR", + "xau": "xau-Latn-ID", + "xav": "xav-Latn-BR", + "xaw": "xaw-Latn-US", + "xay": "xay-Latn-ID", + "xbb": "xbb-Latn-AU", + "xbd": "xbd-Latn-AU", + "xbe": "xbe-Latn-AU", + "xbg": "xbg-Latn-AU", + "xbi": "xbi-Latn-ZZ", + "xbj": "xbj-Latn-AU", + "xbm": "xbm-Latn-FR", + "xbn": "xbn-Latn-MY", + "xbp": "xbp-Latn-AU", + "xbr": "xbr-Latn-ID", + "xbw": "xbw-Latn-BR", + "xby": "xby-Latn-AU", + "xch": "xch-Latn-US", + "xco": "xco-Chrs-UZ", + "xcr": "xcr-Cari-TR", + "xda": "xda-Latn-AU", + "xdk": "xdk-Latn-AU", + "xdo": "xdo-Latn-AO", + "xdq": "xdq-Cyrl-RU", + "xdy": "xdy-Latn-ID", + "xed": "xed-Latn-CM", + "xeg": "xeg-Latn-ZA", + "xem": "xem-Latn-ID", + "xer": "xer-Latn-BR", + "xes": "xes-Latn-ZZ", + "xet": "xet-Latn-BR", + "xeu": "xeu-Latn-PG", + "xgb": "xgb-Latn-CI", + "xgd": "xgd-Latn-AU", + "xgg": "xgg-Latn-AU", + "xgi": "xgi-Latn-AU", + "xgm": "xgm-Latn-AU", + "xgu": "xgu-Latn-AU", + "xgw": "xgw-Latn-AU", + "xh": "xh-Latn-ZA", + "xhe": "xhe-Arab-PK", + "xhm": "xhm-Khmr-KH", + "xhv": "xhv-Latn-VN", + "xii": "xii-Latn-ZA", + "xin": "xin-Latn-GT", + "xir": "xir-Latn-BR", + "xis": "xis-Orya-IN", + "xiy": "xiy-Latn-BR", + "xjb": "xjb-Latn-AU", + "xjt": "xjt-Latn-AU", + "xka": "xka-Arab-PK", + "xkb": "xkb-Latn-BJ", + "xkc": "xkc-Arab-IR", + "xkd": "xkd-Latn-ID", + "xke": "xke-Latn-ID", + "xkg": "xkg-Latn-ML", + "xkj": "xkj-Arab-IR", + "xkl": "xkl-Latn-ID", + "xkn": "xkn-Latn-ID", + "xkp": "xkp-Arab-IR", + "xkq": "xkq-Latn-ID", + "xkr": "xkr-Latn-BR", + "xks": "xks-Latn-ID", + "xkt": "xkt-Latn-GH", + "xku": "xku-Latn-CG", + "xkv": "xkv-Latn-BW", + "xkw": "xkw-Latn-ID", + "xkx": "xkx-Latn-PG", + "xky": "xky-Latn-MY", + "xkz": "xkz-Latn-BT", + "xla": "xla-Latn-ZZ", + "xlc": "xlc-Lyci-TR", + "xld": "xld-Lydi-TR", + "xly": "xly-Elym-IR", + "xma": "xma-Latn-SO", + "xmb": "xmb-Latn-CM", + "xmc": "xmc-Latn-MZ", + "xmd": "xmd-Latn-CM", + "xmf": "xmf-Geor-GE", + "xmg": "xmg-Latn-CM", + "xmh": "xmh-Latn-AU", + "xmj": "xmj-Latn-CM", + "xmm": "xmm-Latn-ID", + "xmn": "xmn-Mani-CN", + "xmo": "xmo-Latn-BR", + "xmp": "xmp-Latn-AU", + "xmq": "xmq-Latn-AU", + "xmr": "xmr-Merc-SD", + "xmt": "xmt-Latn-ID", + "xmu": "xmu-Latn-AU", + "xmv": "xmv-Latn-MG", + "xmw": "xmw-Latn-MG", + "xmx": "xmx-Latn-ID", + "xmy": "xmy-Latn-AU", + "xmz": "xmz-Latn-ID", + "xna": "xna-Narb-SA", + "xnb": "xnb-Latn-TW", + "xni": "xni-Latn-AU", + "xnj": "xnj-Latn-TZ", + "xnk": "xnk-Latn-AU", + "xnm": "xnm-Latn-AU", + "xnn": "xnn-Latn-PH", + "xnq": "xnq-Latn-MZ", + "xnr": "xnr-Deva-IN", + "xnt": "xnt-Latn-US", + "xnu": "xnu-Latn-AU", + "xny": "xny-Latn-AU", + "xnz": "xnz-Latn-EG", + "xnz-Arab": "xnz-Arab-EG", + "xoc": "xoc-Latn-NG", + "xod": "xod-Latn-ID", + "xog": "xog-Latn-UG", + "xoi": "xoi-Latn-PG", + "xok": "xok-Latn-BR", + "xom": "xom-Latn-SD", + "xom-Ethi": "xom-Ethi-ET", + "xon": "xon-Latn-ZZ", + "xoo": "xoo-Latn-BR", + "xop": "xop-Latn-PG", + "xor": "xor-Latn-BR", + "xow": "xow-Latn-PG", + "xpa": "xpa-Latn-AU", + "xpb": "xpb-Latn-AU", + "xpd": "xpd-Latn-AU", + "xpf": "xpf-Latn-AU", + "xpg": "xpg-Grek-TR", + "xph": "xph-Latn-AU", + "xpi": "xpi-Ogam-GB", + "xpj": "xpj-Latn-AU", + "xpk": "xpk-Latn-BR", + "xpl": "xpl-Latn-AU", + "xpm": "xpm-Cyrl-RU", + "xpn": "xpn-Latn-BR", + "xpo": "xpo-Latn-MX", + "xpq": "xpq-Latn-US", + "xpr": "xpr-Prti-IR", + "xpt": "xpt-Latn-AU", + "xpv": "xpv-Latn-AU", + "xpw": "xpw-Latn-AU", + "xpx": "xpx-Latn-AU", + "xpz": "xpz-Latn-AU", + "xra": "xra-Latn-BR", + "xrb": "xrb-Latn-ZZ", + "xrd": "xrd-Latn-AU", + "xre": "xre-Latn-BR", + "xrg": "xrg-Latn-AU", + "xri": "xri-Latn-BR", + "xrm": "xrm-Cyrl-RU", + "xrn": "xrn-Cyrl-RU", + "xrr": "xrr-Latn-IT", + "xru": "xru-Latn-AU", + "xrw": "xrw-Latn-PG", + "xsa": "xsa-Sarb-YE", + "xsb": "xsb-Latn-PH", + "xse": "xse-Latn-ID", + "xsh": "xsh-Latn-NG", + "xsi": "xsi-Latn-ZZ", + "xsm": "xsm-Latn-ZZ", + "xsn": "xsn-Latn-NG", + "xsp": "xsp-Latn-PG", + "xsq": "xsq-Latn-MZ", + "xsr": "xsr-Deva-NP", + "xss": "xss-Cyrl-RU", + "xsu": "xsu-Latn-VE", + "xsy": "xsy-Latn-TW", + "xta": "xta-Latn-MX", + "xtb": "xtb-Latn-MX", + "xtc": "xtc-Latn-SD", + "xtd": "xtd-Latn-MX", + "xte": "xte-Latn-ID", + "xth": "xth-Latn-AU", + "xti": "xti-Latn-MX", + "xtj": "xtj-Latn-MX", + "xtl": "xtl-Latn-MX", + "xtm": "xtm-Latn-MX", + "xtn": "xtn-Latn-MX", + "xtp": "xtp-Latn-MX", + "xts": "xts-Latn-MX", + "xtt": "xtt-Latn-MX", + "xtu": "xtu-Latn-MX", + "xtv": "xtv-Latn-AU", + "xtw": "xtw-Latn-BR", + "xty": "xty-Latn-MX", + "xub": "xub-Taml-IN", + "xub-Knda": "xub-Knda-IN", + "xub-Mlym": "xub-Mlym-IN", + "xud": "xud-Latn-AU", + "xuj": "xuj-Taml-IN", + "xul": "xul-Latn-AU", + "xum": "xum-Latn-IT", + "xum-Ital": "xum-Ital-IT", + "xun": "xun-Latn-AU", + "xuo": "xuo-Latn-TD", + "xut": "xut-Latn-AU", + "xuu": "xuu-Latn-NA", + "xve": "xve-Ital-IT", + "xvi": "xvi-Arab-AF", + "xvn": "xvn-Latn-ES", + "xvo": "xvo-Latn-IT", + "xvs": "xvs-Latn-IT", + "xwa": "xwa-Latn-BR", + "xwd": "xwd-Latn-AU", + "xwe": "xwe-Latn-ZZ", + "xwj": "xwj-Latn-AU", + "xwk": "xwk-Latn-AU", + "xwl": "xwl-Latn-BJ", + "xwo": "xwo-Cyrl-RU", + "xwr": "xwr-Latn-ID", + "xwt": "xwt-Latn-AU", + "xww": "xww-Latn-AU", + "xxb": "xxb-Latn-GH", + "xxk": "xxk-Latn-ID", + "xxm": "xxm-Latn-AU", + "xxr": "xxr-Latn-BR", + "xxt": "xxt-Latn-ID", + "xya": "xya-Latn-AU", + "xyb": "xyb-Latn-AU", + "xyj": "xyj-Latn-AU", + "xyk": "xyk-Latn-AU", + "xyl": "xyl-Latn-BR", + "xyt": "xyt-Latn-AU", + "xyy": "xyy-Latn-AU", + "xzh": "xzh-Marc-CN", + "xzp": "xzp-Latn-MX", + "yaa": "yaa-Latn-PE", + "yab": "yab-Latn-BR", + "yac": "yac-Latn-ID", + "yad": "yad-Latn-PE", + "yae": "yae-Latn-VE", + "yaf": "yaf-Latn-CD", + "yag": "yag-Latn-CL", + "yai": "yai-Cyrl-TJ", + "yaj": "yaj-Latn-CF", + "yak": "yak-Latn-US", + "yal": "yal-Latn-GN", + "yal-Arab": "yal-Arab-GN", + "yam": "yam-Latn-ZZ", + "yan": "yan-Latn-NI", + "yao": "yao-Latn-MZ", + "yap": "yap-Latn-FM", + "yaq": "yaq-Latn-MX", + "yar": "yar-Latn-VE", + "yas": "yas-Latn-ZZ", + "yat": "yat-Latn-ZZ", + "yau": "yau-Latn-VE", + "yav": "yav-Latn-CM", + "yaw": "yaw-Latn-BR", + "yax": "yax-Latn-AO", + "yay": "yay-Latn-ZZ", + "yaz": "yaz-Latn-ZZ", + "yba": "yba-Latn-ZZ", + "ybb": "ybb-Latn-CM", + "ybe": "ybe-Latn-CN", + "ybe-Ougr": "ybe-Ougr-CN", + "ybh": "ybh-Deva-NP", + "ybi": "ybi-Deva-NP", + "ybj": "ybj-Latn-NG", + "ybl": "ybl-Latn-NG", + "ybm": "ybm-Latn-PG", + "ybn": "ybn-Latn-BR", + "ybo": "ybo-Latn-PG", + "ybx": "ybx-Latn-PG", + "yby": "yby-Latn-ZZ", + "ycl": "ycl-Latn-CN", + "ycn": "ycn-Latn-CO", + "yda": "yda-Latn-AU", + "yde": "yde-Latn-PG", + "ydg": "ydg-Arab-PK", + "ydk": "ydk-Latn-PG", + "yea": "yea-Mlym-IN", + "yea-Knda": "yea-Knda-IN", + "yec": "yec-Latn-DE", + "yee": "yee-Latn-PG", + "yei": "yei-Latn-CM", + "yej": "yej-Grek-IL", + "yel": "yel-Latn-CD", + "yer": "yer-Latn-ZZ", + "yes": "yes-Latn-NG", + "yet": "yet-Latn-ID", + "yeu": "yeu-Telu-IN", + "yev": "yev-Latn-PG", + "yey": "yey-Latn-BW", + "yga": "yga-Latn-AU", + "ygi": "ygi-Latn-AU", + "ygl": "ygl-Latn-PG", + "ygm": "ygm-Latn-PG", + "ygp": "ygp-Plrd-CN", + "ygr": "ygr-Latn-ZZ", + "ygu": "ygu-Latn-AU", + "ygw": "ygw-Latn-ZZ", + "yhd": "yhd-Hebr-IL", + "yi": "yi-Hebr-001", + "yia": "yia-Latn-AU", + "yig": "yig-Yiii-CN", + "yih": "yih-Hebr-DE", + "yii": "yii-Latn-AU", + "yij": "yij-Latn-AU", + "yil": "yil-Latn-AU", + "yim": "yim-Latn-IN", + "yir": "yir-Latn-ID", + "yis": "yis-Latn-PG", + "yiv": "yiv-Yiii-CN", + "yka": "yka-Latn-PH", + "yka-Arab": "yka-Arab-PH", + "ykg": "ykg-Cyrl-RU", + "yki": "yki-Latn-ID", + "ykk": "ykk-Latn-PG", + "ykm": "ykm-Latn-PG", + "yko": "yko-Latn-ZZ", + "ykr": "ykr-Latn-PG", + "yky": "yky-Latn-CF", + "yla": "yla-Latn-PG", + "ylb": "ylb-Latn-PG", + "yle": "yle-Latn-ZZ", + "ylg": "ylg-Latn-ZZ", + "yli": "yli-Latn-ID", + "yll": "yll-Latn-ZZ", + "ylr": "ylr-Latn-AU", + "ylu": "ylu-Latn-PG", + "yly": "yly-Latn-NC", + "ymb": "ymb-Latn-PG", + "yme": "yme-Latn-PE", + "ymg": "ymg-Latn-CD", + "ymk": "ymk-Latn-MZ", + "ymk-Arab": "ymk-Arab-MZ", + "yml": "yml-Latn-ZZ", + "ymm": "ymm-Latn-SO", + "ymn": "ymn-Latn-ID", + "ymo": "ymo-Latn-PG", + "ymp": "ymp-Latn-PG", + "yna": "yna-Plrd-CN", + "ynd": "ynd-Latn-AU", + "yng": "yng-Latn-CD", + "ynk": "ynk-Cyrl-RU", + "ynl": "ynl-Latn-PG", + "ynq": "ynq-Latn-NG", + "yns": "yns-Latn-CD", + "ynu": "ynu-Latn-CO", + "yo": "yo-Latn-NG", + "yob": "yob-Latn-PG", + "yog": "yog-Latn-PH", + "yoi": "yoi-Jpan-JP", + "yok": "yok-Latn-US", + "yol": "yol-Latn-GB", + "yom": "yom-Latn-CD", + "yon": "yon-Latn-ZZ", + "yot": "yot-Latn-NG", + "yoy": "yoy-Thai-TH", + "yra": "yra-Latn-PG", + "yrb": "yrb-Latn-ZZ", + "yre": "yre-Latn-ZZ", + "yrk": "yrk-Cyrl-RU", + "yrl": "yrl-Latn-BR", + "yrm": "yrm-Latn-AU", + "yro": "yro-Latn-BR", + "yrs": "yrs-Latn-ID", + "yrw": "yrw-Latn-PG", + "yry": "yry-Latn-AU", + "ysd": "ysd-Yiii-CN", + "ysn": "ysn-Yiii-CN", + "ysp": "ysp-Yiii-CN", + "ysr": "ysr-Cyrl-RU", + "yss": "yss-Latn-ZZ", + "ysy": "ysy-Plrd-CN", + "ytw": "ytw-Latn-PG", + "yty": "yty-Latn-AU", + "yua": "yua-Latn-MX", + "yub": "yub-Latn-AU", + "yuc": "yuc-Latn-US", + "yud": "yud-Hebr-IL", + "yue": "yue-Hant-HK", + "yue-CN": "yue-Hans-CN", + "yue-Hans": "yue-Hans-CN", + "yuf": "yuf-Latn-US", + "yug": "yug-Cyrl-RU", + "yui": "yui-Latn-CO", + "yuj": "yuj-Latn-ZZ", + "yul": "yul-Latn-CF", + "yum": "yum-Latn-US", + "yun": "yun-Latn-NG", + "yup": "yup-Latn-CO", + "yuq": "yuq-Latn-BO", + "yur": "yur-Latn-US", + "yut": "yut-Latn-ZZ", + "yuw": "yuw-Latn-ZZ", + "yux": "yux-Cyrl-RU", + "yuz": "yuz-Latn-BO", + "yva": "yva-Latn-ID", + "yvt": "yvt-Latn-VE", + "ywa": "ywa-Latn-PG", + "ywg": "ywg-Latn-AU", + "ywn": "ywn-Latn-BR", + "ywq": "ywq-Plrd-CN", + "ywq-Yiii": "ywq-Yiii-CN", + "ywr": "ywr-Latn-AU", + "ywu": "ywu-Plrd-CN", + "ywu-Yiii": "ywu-Yiii-CN", + "yww": "yww-Latn-AU", + "yxa": "yxa-Latn-AU", + "yxg": "yxg-Latn-AU", + "yxl": "yxl-Latn-AU", + "yxm": "yxm-Latn-AU", + "yxu": "yxu-Latn-AU", + "yxy": "yxy-Latn-AU", + "yyr": "yyr-Latn-AU", + "yyu": "yyu-Latn-PG", + "za": "za-Latn-CN", + "zaa": "zaa-Latn-MX", + "zab": "zab-Latn-MX", + "zac": "zac-Latn-MX", + "zad": "zad-Latn-MX", + "zae": "zae-Latn-MX", + "zaf": "zaf-Latn-MX", + "zag": "zag-Latn-SD", + "zah": "zah-Latn-NG", + "zaj": "zaj-Latn-TZ", + "zak": "zak-Latn-TZ", + "zam": "zam-Latn-MX", + "zao": "zao-Latn-MX", + "zap": "zap-Latn-MX", + "zaq": "zaq-Latn-MX", + "zar": "zar-Latn-MX", + "zas": "zas-Latn-MX", + "zat": "zat-Latn-MX", + "zau": "zau-Tibt-IN", + "zau-Arab": "zau-Arab-IN", + "zav": "zav-Latn-MX", + "zaw": "zaw-Latn-MX", + "zax": "zax-Latn-MX", + "zay": "zay-Latn-ET", + "zay-Ethi": "zay-Ethi-ET", + "zaz": "zaz-Latn-NG", + "zba": "zba-Arab-001", + "zbc": "zbc-Latn-MY", + "zbe": "zbe-Latn-MY", + "zbt": "zbt-Latn-ID", + "zbu": "zbu-Latn-NG", + "zbw": "zbw-Latn-MY", + "zca": "zca-Latn-MX", + "zch": "zch-Hani-CN", + "zdj": "zdj-Arab-KM", + "zea": "zea-Latn-NL", + "zeg": "zeg-Latn-PG", + "zeh": "zeh-Hani-CN", + "zen": "zen-Tfng-MR", + "zen-Arab": "zen-Arab-MR", + "zga": "zga-Latn-TZ", + "zgb": "zgb-Hani-CN", + "zgh": "zgh-Tfng-MA", + "zgm": "zgm-Hani-CN", + "zgn": "zgn-Hani-CN", + "zgr": "zgr-Latn-PG", + "zh": "zh-Hans-CN", + "zh-AU": "zh-Hant-AU", + "zh-BN": "zh-Hant-BN", + "zh-Bopo": "zh-Bopo-TW", + "zh-GB": "zh-Hant-GB", + "zh-GF": "zh-Hant-GF", + "zh-HK": "zh-Hant-HK", + "zh-Hanb": "zh-Hanb-TW", + "zh-Hant": "zh-Hant-TW", + "zh-ID": "zh-Hant-ID", + "zh-MO": "zh-Hant-MO", + "zh-PA": "zh-Hant-PA", + "zh-PF": "zh-Hant-PF", + "zh-PH": "zh-Hant-PH", + "zh-SR": "zh-Hant-SR", + "zh-TH": "zh-Hant-TH", + "zh-TW": "zh-Hant-TW", + "zh-US": "zh-Hant-US", + "zh-VN": "zh-Hant-VN", + "zhd": "zhd-Hani-CN", + "zhd-Latn": "zhd-Latn-VN", + "zhi": "zhi-Latn-NG", + "zhn": "zhn-Latn-CN", + "zhn-Hani": "zhn-Hani-CN", + "zhw": "zhw-Latn-CM", + "zhx": "zhx-Nshu-CN", + "zia": "zia-Latn-ZZ", + "zik": "zik-Latn-PG", + "zil": "zil-Latn-GN", + "zim": "zim-Latn-TD", + "zin": "zin-Latn-TZ", + "ziw": "ziw-Latn-TZ", + "ziz": "ziz-Latn-NG", + "zka": "zka-Latn-ID", + "zkb": "zkb-Cyrl-RU", + "zkd": "zkd-Latn-MM", + "zko": "zko-Cyrl-RU", + "zkp": "zkp-Latn-BR", + "zkt": "zkt-Kits-CN", + "zku": "zku-Latn-AU", + "zkz": "zkz-Cyrl-RU", + "zla": "zla-Latn-CD", + "zlj": "zlj-Hani-CN", + "zlj-Latn": "zlj-Latn-CN", + "zlm": "zlm-Latn-TG", + "zln": "zln-Hani-CN", + "zlq": "zlq-Hani-CN", + "zma": "zma-Latn-AU", + "zmb": "zmb-Latn-CD", + "zmc": "zmc-Latn-AU", + "zmd": "zmd-Latn-AU", + "zme": "zme-Latn-AU", + "zmf": "zmf-Latn-CD", + "zmg": "zmg-Latn-AU", + "zmh": "zmh-Latn-PG", + "zmi": "zmi-Latn-MY", + "zmj": "zmj-Latn-AU", + "zmk": "zmk-Latn-AU", + "zml": "zml-Latn-AU", + "zmm": "zmm-Latn-AU", + "zmn": "zmn-Latn-GA", + "zmo": "zmo-Latn-SD", + "zmp": "zmp-Latn-CD", + "zmq": "zmq-Latn-CD", + "zmr": "zmr-Latn-AU", + "zms": "zms-Latn-CD", + "zmt": "zmt-Latn-AU", + "zmu": "zmu-Latn-AU", + "zmv": "zmv-Latn-AU", + "zmw": "zmw-Latn-CD", + "zmx": "zmx-Latn-CG", + "zmy": "zmy-Latn-AU", + "zmz": "zmz-Latn-CD", + "zna": "zna-Latn-TD", + "zne": "zne-Latn-ZZ", + "zng": "zng-Latn-VN", + "znk": "znk-Latn-AU", + "zns": "zns-Latn-NG", + "zoc": "zoc-Latn-MX", + "zoh": "zoh-Latn-MX", + "zom": "zom-Latn-IN", + "zoo": "zoo-Latn-MX", + "zoq": "zoq-Latn-MX", + "zor": "zor-Latn-MX", + "zos": "zos-Latn-MX", + "zpa": "zpa-Latn-MX", + "zpb": "zpb-Latn-MX", + "zpc": "zpc-Latn-MX", + "zpd": "zpd-Latn-MX", + "zpe": "zpe-Latn-MX", + "zpf": "zpf-Latn-MX", + "zpg": "zpg-Latn-MX", + "zph": "zph-Latn-MX", + "zpi": "zpi-Latn-MX", + "zpj": "zpj-Latn-MX", + "zpk": "zpk-Latn-MX", + "zpl": "zpl-Latn-MX", + "zpm": "zpm-Latn-MX", + "zpn": "zpn-Latn-MX", + "zpo": "zpo-Latn-MX", + "zpp": "zpp-Latn-MX", + "zpq": "zpq-Latn-MX", + "zpr": "zpr-Latn-MX", + "zps": "zps-Latn-MX", + "zpt": "zpt-Latn-MX", + "zpu": "zpu-Latn-MX", + "zpv": "zpv-Latn-MX", + "zpw": "zpw-Latn-MX", + "zpx": "zpx-Latn-MX", + "zpy": "zpy-Latn-MX", + "zpz": "zpz-Latn-MX", + "zqe": "zqe-Hani-CN", + "zqe-Latn": "zqe-Latn-CN", + "zrn": "zrn-Latn-TD", + "zro": "zro-Latn-EC", + "zrp": "zrp-Hebr-FR", + "zrs": "zrs-Latn-ID", + "zsa": "zsa-Latn-PG", + "zsr": "zsr-Latn-MX", + "zsu": "zsu-Latn-PG", + "zte": "zte-Latn-MX", + "ztg": "ztg-Latn-MX", + "ztl": "ztl-Latn-MX", + "ztm": "ztm-Latn-MX", + "ztn": "ztn-Latn-MX", + "ztp": "ztp-Latn-MX", + "ztq": "ztq-Latn-MX", + "zts": "zts-Latn-MX", + "ztt": "ztt-Latn-MX", + "ztu": "ztu-Latn-MX", + "ztx": "ztx-Latn-MX", + "zty": "zty-Latn-MX", + "zu": "zu-Latn-ZA", + "zua": "zua-Latn-NG", + "zuh": "zuh-Latn-PG", + "zum": "zum-Arab-OM", + "zun": "zun-Latn-US", + "zuy": "zuy-Latn-CM", + "zyg": "zyg-Hani-CN", + "zyj": "zyj-Latn-CN", + "zyj-Hani": "zyj-Hani-CN", + "zyn": "zyn-Hani-CN", + "zyp": "zyp-Latn-MM", + "zza": "zza-Latn-TR", + "zzj": "zzj-Hani-CN", +}; + +// Extracted from likelySubtags.xml. +// Derived from CLDR Supplemental Data, version 43. +// https://unicode.org/Public/cldr/43/cldr-common-43.0.zip +var minLikelySubtags = { + "aa-Latn-DJ": "aa-DJ", + "aa-Latn-ET": "aa", + "aaa-Latn-NG": "aaa", + "aab-Latn-NG": "aab", + "aac-Latn-PG": "aac", + "aad-Latn-PG": "aad", + "aae-Grek-IT": "aae-Grek", + "aae-Latn-IT": "aae", + "aaf-Arab-IN": "aaf-Arab", + "aaf-Mlym-IN": "aaf", + "aag-Latn-PG": "aag", + "aah-Latn-PG": "aah", + "aai-Latn-ZZ": "aai", + "aak-Latn-ZZ": "aak", + "aal-Latn-CM": "aal", + "aan-Latn-BR": "aan", + "aao-Arab-DZ": "aao", + "aap-Latn-BR": "aap", + "aaq-Latn-US": "aaq", + "aas-Latn-TZ": "aas", + "aat-Grek-GR": "aat", + "aau-Latn-ZZ": "aau", + "aaw-Latn-PG": "aaw", + "aax-Latn-ID": "aax", + "aaz-Latn-ID": "aaz", + "ab-Cyrl-GE": "ab", + "aba-Latn-CI": "aba", + "abb-Latn-CM": "abb", + "abc-Latn-PH": "abc", + "abd-Latn-PH": "abd", + "abe-Latn-CA": "abe", + "abf-Latn-MY": "abf", + "abg-Latn-PG": "abg", + "abh-Arab-TJ": "abh", + "abi-Latn-ZZ": "abi", + "abl-Latn-ID": "abl-Latn", + "abl-Rjng-ID": "abl", + "abm-Latn-NG": "abm", + "abn-Latn-NG": "abn", + "abo-Latn-NG": "abo", + "abp-Latn-PH": "abp", + "abq-Cyrl-ZZ": "abq", + "abr-Latn-GH": "abr", + "abs-Latn-ID": "abs", + "abt-Latn-ZZ": "abt", + "abu-Latn-CI": "abu", + "abv-Arab-BH": "abv", + "abw-Latn-PG": "abw", + "abx-Latn-PH": "abx", + "aby-Latn-ZZ": "aby", + "abz-Latn-ID": "abz", + "aca-Latn-CO": "aca", + "acb-Latn-NG": "acb", + "acd-Latn-ZZ": "acd", + "ace-Latn-ID": "ace", + "acf-Latn-LC": "acf", + "ach-Latn-UG": "ach", + "acm-Arab-IQ": "acm", + "acn-Latn-CN": "acn", + "acp-Latn-NG": "acp", + "acq-Arab-YE": "acq", + "acr-Latn-GT": "acr", + "acs-Latn-BR": "acs", + "act-Latn-NL": "act", + "acu-Latn-EC": "acu", + "acv-Latn-US": "acv", + "acw-Arab-SA": "acw", + "acx-Arab-OM": "acx", + "acy-Arab-CY": "acy-Arab", + "acy-Grek-CY": "acy-Grek", + "acy-Latn-CY": "acy", + "acz-Latn-SD": "acz", + "ada-Latn-GH": "ada", + "adb-Latn-TL": "adb", + "add-Latn-CM": "add", + "ade-Latn-ZZ": "ade", + "adf-Arab-OM": "adf", + "adg-Latn-AU": "adg", + "adh-Latn-UG": "adh", + "adi-Latn-IN": "adi", + "adi-Tibt-CN": "adi-Tibt", + "adj-Latn-ZZ": "adj", + "adl-Latn-IN": "adl", + "adn-Latn-ID": "adn", + "ado-Latn-PG": "ado", + "adq-Latn-GH": "adq", + "adr-Latn-ID": "adr", + "adt-Latn-AU": "adt", + "adu-Latn-NG": "adu", + "adw-Latn-BR": "adw", + "adx-Tibt-CN": "adx", + "ady-Cyrl-RU": "ady", + "adz-Latn-ZZ": "adz", + "ae-Avst-IR": "ae", + "aea-Latn-AU": "aea", + "aeb-Arab-TN": "aeb", + "aec-Arab-EG": "aec", + "aee-Arab-AF": "aee", + "aek-Latn-NC": "aek", + "ael-Latn-CM": "ael", + "aem-Latn-VN": "aem", + "aeq-Arab-PK": "aeq", + "aer-Latn-AU": "aer", + "aeu-Latn-CN": "aeu", + "aew-Latn-PG": "aew", + "aey-Latn-ZZ": "aey", + "aez-Latn-PG": "aez", + "af-Latn-NA": "af-NA", + "af-Latn-ZA": "af", + "afb-Arab-KW": "afb", + "afd-Latn-PG": "afd", + "afe-Latn-NG": "afe", + "afh-Latn-GH": "afh", + "afi-Latn-PG": "afi", + "afk-Latn-PG": "afk", + "afn-Latn-NG": "afn", + "afo-Latn-NG": "afo", + "afp-Latn-PG": "afp", + "afs-Latn-MX": "afs", + "afu-Latn-GH": "afu", + "afz-Latn-ID": "afz", + "aga-Latn-PE": "aga", + "agb-Latn-NG": "agb", + "agc-Latn-ZZ": "agc", + "agd-Latn-ZZ": "agd", + "age-Latn-PG": "age", + "agf-Latn-ID": "agf", + "agg-Latn-ZZ": "agg", + "agh-Latn-CD": "agh", + "agi-Deva-IN": "agi", + "agj-Arab-ET": "agj-Arab", + "agj-Ethi-ET": "agj", + "agk-Latn-PH": "agk", + "agl-Latn-PG": "agl", + "agm-Latn-ZZ": "agm", + "agn-Latn-PH": "agn", + "ago-Latn-ZZ": "ago", + "agq-Latn-CM": "agq", + "agr-Latn-PE": "agr", + "ags-Latn-CM": "ags", + "agt-Latn-PH": "agt", + "agu-Latn-GT": "agu", + "agv-Latn-PH": "agv", + "agw-Latn-SB": "agw", + "agx-Cyrl-RU": "agx", + "agy-Latn-PH": "agy", + "agz-Latn-PH": "agz", + "aha-Latn-ZZ": "aha", + "ahb-Latn-VU": "ahb", + "ahg-Ethi-ET": "ahg", + "ahh-Latn-ID": "ahh", + "ahi-Latn-CI": "ahi", + "ahk-Latn-MM": "ahk", + "ahk-Latn-TH": "ahk-TH", + "ahk-Mymr-MM": "ahk-Mymr", + "ahk-Thai-TH": "ahk-Thai", + "ahl-Latn-ZZ": "ahl", + "ahm-Latn-CI": "ahm", + "ahn-Latn-NG": "ahn", + "aho-Ahom-IN": "aho", + "ahp-Latn-CI": "ahp", + "ahr-Deva-IN": "ahr", + "ahs-Latn-NG": "ahs", + "aht-Latn-US": "aht", + "aia-Latn-SB": "aia", + "aib-Arab-CN": "aib", + "aic-Latn-PG": "aic", + "aid-Latn-AU": "aid", + "aie-Latn-PG": "aie", + "aif-Latn-PG": "aif", + "aig-Latn-AG": "aig", + "aij-Hebr-IL": "aij", + "aik-Latn-NG": "aik", + "ail-Latn-PG": "ail", + "aim-Latn-IN": "aim", + "ain-Kana-JP": "ain", + "ain-Latn-JP": "ain-Latn", + "aio-Mymr-IN": "aio", + "aip-Latn-ID": "aip", + "aiq-Arab-AF": "aiq", + "air-Latn-ID": "air", + "ait-Latn-BR": "ait", + "aiw-Arab-ET": "aiw-Arab", + "aiw-Ethi-ET": "aiw-Ethi", + "aiw-Latn-ET": "aiw", + "aix-Latn-PG": "aix", + "aiy-Latn-CF": "aiy", + "aja-Latn-SS": "aja", + "ajg-Latn-ZZ": "ajg", + "aji-Latn-NC": "aji", + "ajn-Latn-AU": "ajn", + "ajp-Arab-JO": "ajp", + "ajw-Latn-NG": "ajw", + "ajz-Latn-IN": "ajz", + "ak-Latn-GH": "ak", + "akb-Batk-ID": "akb-Batk", + "akb-Latn-ID": "akb", + "akc-Latn-ID": "akc", + "akd-Latn-NG": "akd", + "ake-Latn-GY": "ake", + "akf-Latn-NG": "akf", + "akg-Latn-ID": "akg", + "akh-Latn-PG": "akh", + "aki-Latn-PG": "aki", + "akk-Xsux-IQ": "akk", + "akl-Latn-PH": "akl", + "ako-Latn-SR": "ako", + "akp-Latn-GH": "akp", + "akq-Latn-PG": "akq", + "akr-Latn-VU": "akr", + "aks-Latn-TG": "aks", + "akt-Latn-PG": "akt", + "aku-Latn-CM": "aku", + "akv-Cyrl-RU": "akv", + "akw-Latn-CG": "akw", + "akz-Latn-US": "akz", + "ala-Latn-ZZ": "ala", + "alc-Latn-CL": "alc", + "ald-Latn-CI": "ald", + "ale-Latn-US": "ale", + "alf-Latn-NG": "alf", + "alh-Latn-AU": "alh", + "ali-Latn-ZZ": "ali", + "alj-Latn-PH": "alj", + "alk-Laoo-LA": "alk", + "all-Mlym-IN": "all", + "alm-Latn-VU": "alm", + "aln-Latn-XK": "aln", + "alo-Latn-ID": "alo", + "alp-Latn-ID": "alp", + "alq-Latn-CA": "alq", + "alr-Cyrl-RU": "alr", + "alt-Cyrl-RU": "alt", + "alu-Latn-SB": "alu", + "alw-Ethi-ET": "alw", + "alx-Latn-PG": "alx", + "aly-Latn-AU": "aly", + "alz-Latn-CD": "alz", + "am-Ethi-ET": "am", + "ama-Latn-BR": "ama", + "amb-Latn-NG": "amb", + "amc-Latn-PE": "amc", + "ame-Latn-PE": "ame", + "amf-Ethi-ET": "amf-Ethi", + "amf-Latn-ET": "amf", + "amg-Latn-AU": "amg", + "ami-Latn-TW": "ami", + "amj-Latn-TD": "amj", + "amk-Latn-ID": "amk", + "amm-Latn-ZZ": "amm", + "amn-Latn-ZZ": "amn", + "amo-Latn-NG": "amo", + "amp-Latn-ZZ": "amp", + "amq-Latn-ID": "amq", + "amr-Latn-PE": "amr", + "ams-Jpan-JP": "ams", + "amt-Latn-PG": "amt", + "amu-Latn-MX": "amu", + "amv-Latn-ID": "amv", + "amw-Arab-SY": "amw-Arab", + "amw-Armi-SY": "amw-Armi", + "amw-Latn-SY": "amw-Latn", + "amw-Syrc-SY": "amw", + "amx-Latn-AU": "amx", + "amy-Latn-AU": "amy", + "amz-Latn-AU": "amz", + "an-Latn-ES": "an", + "ana-Latn-CO": "ana", + "anb-Latn-PE": "anb", + "anc-Latn-ZZ": "anc", + "and-Latn-ID": "and", + "ane-Latn-NC": "ane", + "anf-Latn-GH": "anf", + "ang-Latn-GB": "ang", + "anh-Latn-PG": "anh", + "ani-Cyrl-RU": "ani", + "anj-Latn-PG": "anj", + "ank-Latn-ZZ": "ank", + "anl-Latn-MM": "anl", + "anm-Latn-IN": "anm", + "ann-Latn-NG": "ann", + "ano-Latn-CO": "ano", + "anp-Deva-IN": "anp", + "anr-Deva-IN": "anr", + "ans-Latn-CO": "ans", + "ant-Latn-AU": "ant", + "anu-Arab-SS": "anu-Arab", + "anu-Ethi-ET": "anu", + "anu-Latn-SS": "anu-Latn", + "anv-Latn-CM": "anv", + "anw-Latn-NG": "anw", + "anx-Latn-PG": "anx", + "any-Latn-ZZ": "any", + "anz-Latn-PG": "anz", + "aoa-Latn-ST": "aoa", + "aob-Latn-PG": "aob", + "aoc-Latn-VE": "aoc", + "aod-Latn-PG": "aod", + "aoe-Latn-PG": "aoe", + "aof-Latn-PG": "aof", + "aog-Latn-PG": "aog", + "aoi-Latn-AU": "aoi", + "aoj-Latn-ZZ": "aoj", + "aok-Latn-NC": "aok", + "aol-Latn-ID": "aol", + "aom-Latn-ZZ": "aom", + "aon-Latn-PG": "aon", + "aor-Latn-VU": "aor", + "aos-Latn-ID": "aos", + "aot-Beng-BD": "aot", + "aot-Latn-IN": "aot-Latn", + "aox-Latn-GY": "aox", + "aoz-Latn-ID": "aoz", + "apb-Latn-SB": "apb", + "apc-Arab-SY": "apc", + "apc-Arab-TR": "apc-TR", + "apd-Arab-TG": "apd", + "ape-Latn-ZZ": "ape", + "apf-Latn-PH": "apf", + "apg-Latn-ID": "apg", + "aph-Deva-NP": "aph", + "api-Latn-BR": "api", + "apj-Latn-US": "apj", + "apk-Latn-US": "apk", + "apl-Latn-US": "apl", + "apm-Latn-US": "apm", + "apn-Latn-BR": "apn", + "apo-Latn-PG": "apo", + "app-Latn-VU": "app", + "apr-Latn-ZZ": "apr", + "aps-Latn-ZZ": "aps", + "apt-Latn-IN": "apt", + "apu-Latn-BR": "apu", + "apv-Latn-BR": "apv", + "apw-Latn-US": "apw", + "apx-Latn-ID": "apx", + "apy-Latn-BR": "apy", + "apz-Latn-ZZ": "apz", + "aqc-Cyrl-RU": "aqc", + "aqd-Latn-ML": "aqd", + "aqg-Latn-NG": "aqg", + "aqk-Latn-NG": "aqk", + "aqm-Latn-ID": "aqm", + "aqn-Latn-PH": "aqn", + "aqr-Latn-NC": "aqr", + "aqt-Latn-PY": "aqt", + "aqz-Latn-BR": "aqz", + "ar-Arab-AE": "ar-AE", + "ar-Arab-BH": "ar-BH", + "ar-Arab-DZ": "ar-DZ", + "ar-Arab-EG": "ar", + "ar-Arab-EH": "ar-EH", + "ar-Arab-IQ": "ar-IQ", + "ar-Arab-JO": "ar-JO", + "ar-Arab-KM": "ar-KM", + "ar-Arab-KW": "ar-KW", + "ar-Arab-LB": "ar-LB", + "ar-Arab-LY": "ar-LY", + "ar-Arab-MA": "ar-MA", + "ar-Arab-MR": "ar-MR", + "ar-Arab-OM": "ar-OM", + "ar-Arab-PS": "ar-PS", + "ar-Arab-QA": "ar-QA", + "ar-Arab-SA": "ar-SA", + "ar-Arab-SD": "ar-SD", + "ar-Arab-SY": "ar-SY", + "ar-Arab-TN": "ar-TN", + "ar-Arab-YE": "ar-YE", + "arc-Armi-IR": "arc", + "arc-Elym-IR": "arc-Elym", + "arc-Nbat-JO": "arc-Nbat", + "arc-Palm-SY": "arc-Palm", + "ard-Latn-AU": "ard", + "are-Latn-AU": "are", + "arh-Latn-ZZ": "arh", + "ari-Latn-US": "ari", + "arj-Latn-BR": "arj", + "ark-Latn-BR": "ark", + "arl-Latn-PE": "arl", + "arn-Latn-CL": "arn", + "aro-Latn-BO": "aro", + "arp-Latn-US": "arp", + "arq-Arab-DZ": "arq", + "arr-Latn-BR": "arr", + "ars-Arab-SA": "ars", + "aru-Latn-BR": "aru", + "arw-Latn-SR": "arw", + "arx-Latn-BR": "arx", + "ary-Arab-MA": "ary", + "arz-Arab-EG": "arz", + "as-Beng-IN": "as", + "asa-Latn-TZ": "asa", + "asb-Latn-CA": "asb", + "asc-Latn-ID": "asc", + "ase-Sgnw-US": "ase", + "asg-Latn-ZZ": "asg", + "ash-Latn-PE": "ash", + "asi-Latn-ID": "asi", + "asj-Latn-CM": "asj", + "ask-Arab-AF": "ask", + "asl-Latn-ID": "asl", + "asn-Latn-BR": "asn", + "aso-Latn-ZZ": "aso", + "ass-Latn-CM": "ass", + "ast-Latn-ES": "ast", + "asu-Latn-BR": "asu", + "asv-Latn-CD": "asv", + "asx-Latn-PG": "asx", + "asy-Latn-ID": "asy", + "asz-Latn-ID": "asz", + "ata-Latn-ZZ": "ata", + "atb-Latn-CN": "atb", + "atb-Lisu-CN": "atb-Lisu", + "atc-Latn-PE": "atc", + "atd-Latn-PH": "atd", + "ate-Latn-PG": "ate", + "atg-Latn-ZZ": "atg", + "ati-Latn-CI": "ati", + "atj-Latn-CA": "atj", + "atk-Latn-PH": "atk", + "atl-Latn-PH": "atl", + "atm-Latn-PH": "atm", + "atn-Arab-IR": "atn", + "ato-Latn-CM": "ato", + "atp-Latn-PH": "atp", + "atq-Latn-ID": "atq", + "atr-Latn-BR": "atr", + "ats-Latn-US": "ats", + "att-Latn-PH": "att", + "atu-Latn-SS": "atu", + "atv-Cyrl-RU": "atv", + "atw-Latn-US": "atw", + "atx-Latn-BR": "atx", + "aty-Latn-VU": "aty", + "atz-Latn-PH": "atz", + "aua-Latn-SB": "aua", + "auc-Latn-EC": "auc", + "aud-Latn-SB": "aud", + "aug-Latn-BJ": "aug", + "auh-Latn-ZM": "auh", + "aui-Latn-PG": "aui", + "auj-Arab-LY": "auj", + "auj-Latn-LY": "auj-Latn", + "auj-Tfng-LY": "auj-Tfng", + "auk-Latn-PG": "auk", + "aul-Latn-VU": "aul", + "aum-Latn-NG": "aum", + "aun-Latn-PG": "aun", + "auo-Latn-NG": "auo", + "aup-Latn-PG": "aup", + "auq-Latn-ID": "auq", + "aur-Latn-PG": "aur", + "aut-Latn-PF": "aut", + "auu-Latn-ID": "auu", + "auw-Latn-ID": "auw", + "auy-Latn-ZZ": "auy", + "auz-Arab-UZ": "auz", + "av-Cyrl-RU": "av", + "avb-Latn-PG": "avb", + "avd-Arab-IR": "avd", + "avi-Latn-CI": "avi", + "avk-Latn-001": "avk", + "avl-Arab-ZZ": "avl", + "avm-Latn-AU": "avm", + "avn-Latn-ZZ": "avn", + "avo-Latn-BR": "avo", + "avs-Latn-PE": "avs", + "avt-Latn-ZZ": "avt", + "avu-Latn-ZZ": "avu", + "avv-Latn-BR": "avv", + "awa-Deva-IN": "awa", + "awb-Latn-ZZ": "awb", + "awc-Latn-NG": "awc", + "awe-Latn-BR": "awe", + "awg-Latn-AU": "awg", + "awh-Latn-ID": "awh", + "awi-Latn-PG": "awi", + "awk-Latn-AU": "awk", + "awm-Latn-PG": "awm", + "awn-Ethi-ET": "awn", + "awo-Latn-ZZ": "awo", + "awr-Latn-ID": "awr", + "aws-Latn-ID": "aws", + "awt-Latn-BR": "awt", + "awu-Latn-ID": "awu", + "awv-Latn-ID": "awv", + "aww-Latn-PG": "aww", + "awx-Latn-ZZ": "awx", + "awy-Latn-ID": "awy", + "axb-Latn-AR": "axb", + "axe-Latn-AU": "axe", + "axg-Latn-BR": "axg", + "axk-Latn-CF": "axk", + "axl-Latn-AU": "axl", + "axm-Armn-AM": "axm", + "axx-Latn-NC": "axx", + "ay-Latn-BO": "ay", + "aya-Latn-PG": "aya", + "ayb-Latn-ZZ": "ayb", + "ayc-Latn-PE": "ayc", + "ayd-Latn-AU": "ayd", + "aye-Latn-NG": "aye", + "ayg-Latn-TG": "ayg", + "ayh-Arab-YE": "ayh", + "ayi-Latn-NG": "ayi", + "ayk-Latn-NG": "ayk", + "ayl-Arab-LY": "ayl", + "ayn-Arab-YE": "ayn", + "ayo-Latn-PY": "ayo", + "ayp-Arab-IQ": "ayp", + "ayq-Latn-PG": "ayq", + "ays-Latn-PH": "ays", + "ayt-Latn-PH": "ayt", + "ayu-Latn-NG": "ayu", + "ayz-Latn-ID": "ayz", + "az-Arab-IQ": "az-IQ", + "az-Arab-IR": "az-IR", + "az-Cyrl-RU": "az-RU", + "az-Latn-AZ": "az", + "azb-Arab-IR": "azb", + "azb-Cyrl-AZ": "azb-Cyrl", + "azb-Latn-AZ": "azb-Latn", + "azd-Latn-MX": "azd", + "azg-Latn-MX": "azg", + "azm-Latn-MX": "azm", + "azn-Latn-MX": "azn", + "azo-Latn-CM": "azo", + "azt-Latn-PH": "azt", + "azz-Latn-MX": "azz", + "ba-Cyrl-RU": "ba", + "baa-Latn-SB": "baa", + "bab-Latn-GW": "bab", + "bac-Latn-ID": "bac", + "bae-Latn-VE": "bae", + "baf-Latn-CM": "baf", + "bag-Latn-CM": "bag", + "bah-Latn-BS": "bah", + "baj-Latn-ID": "baj", + "bal-Arab-PK": "bal", + "ban-Bali-ID": "ban-Bali", + "ban-Latn-ID": "ban", + "bao-Latn-CO": "bao", + "bap-Deva-NP": "bap", + "bar-Latn-AT": "bar", + "bas-Latn-CM": "bas", + "bau-Latn-NG": "bau", + "bav-Latn-ZZ": "bav", + "baw-Latn-CM": "baw", + "bax-Bamu-CM": "bax", + "bay-Latn-ID": "bay", + "bba-Latn-ZZ": "bba", + "bbb-Latn-ZZ": "bbb", + "bbc-Batk-ID": "bbc-Batk", + "bbc-Latn-ID": "bbc", + "bbd-Latn-ZZ": "bbd", + "bbe-Latn-CD": "bbe", + "bbf-Latn-PG": "bbf", + "bbg-Latn-GA": "bbg", + "bbi-Latn-CM": "bbi", + "bbj-Latn-CM": "bbj", + "bbk-Latn-CM": "bbk", + "bbl-Geor-GE": "bbl", + "bbm-Latn-CD": "bbm", + "bbn-Latn-PG": "bbn", + "bbo-Latn-BF": "bbo", + "bbp-Latn-ZZ": "bbp", + "bbq-Latn-CM": "bbq", + "bbr-Latn-ZZ": "bbr", + "bbs-Latn-NG": "bbs", + "bbt-Latn-NG": "bbt", + "bbu-Latn-NG": "bbu", + "bbv-Latn-PG": "bbv", + "bbw-Latn-CM": "bbw", + "bbx-Latn-CM": "bbx", + "bby-Latn-CM": "bby", + "bca-Hani-CN": "bca-Hani", + "bca-Latn-CN": "bca", + "bcb-Latn-SN": "bcb", + "bcd-Latn-ID": "bcd", + "bce-Latn-CM": "bce", + "bcf-Latn-ZZ": "bcf", + "bcg-Latn-GN": "bcg", + "bch-Latn-ZZ": "bch", + "bci-Latn-CI": "bci", + "bcj-Latn-AU": "bcj", + "bck-Latn-AU": "bck", + "bcm-Latn-ZZ": "bcm", + "bcn-Latn-ZZ": "bcn", + "bco-Latn-ZZ": "bco", + "bcp-Latn-CD": "bcp", + "bcq-Ethi-ZZ": "bcq", + "bcr-Latn-CA": "bcr", + "bcs-Latn-NG": "bcs", + "bct-Latn-CD": "bct", + "bcu-Latn-ZZ": "bcu", + "bcv-Latn-NG": "bcv", + "bcw-Latn-CM": "bcw", + "bcy-Latn-NG": "bcy", + "bcz-Latn-SN": "bcz", + "bda-Latn-SN": "bda", + "bdb-Latn-ID": "bdb", + "bdc-Latn-CO": "bdc", + "bdd-Latn-ZZ": "bdd", + "bde-Latn-NG": "bde", + "bdf-Latn-PG": "bdf", + "bdg-Latn-MY": "bdg", + "bdh-Latn-SS": "bdh", + "bdi-Latn-SD": "bdi", + "bdj-Latn-SS": "bdj", + "bdk-Latn-AZ": "bdk", + "bdl-Latn-ID": "bdl", + "bdm-Latn-TD": "bdm", + "bdn-Latn-CM": "bdn", + "bdo-Latn-TD": "bdo", + "bdp-Latn-TZ": "bdp", + "bdq-Latn-VN": "bdq", + "bdr-Latn-MY": "bdr", + "bds-Latn-TZ": "bds", + "bdt-Latn-CF": "bdt", + "bdu-Latn-CM": "bdu", + "bdv-Orya-IN": "bdv", + "bdw-Latn-ID": "bdw", + "bdx-Latn-ID": "bdx", + "bdy-Latn-AU": "bdy", + "bdz-Arab-PK": "bdz", + "be-Cyrl-BY": "be", + "bea-Cans-CA": "bea-Cans", + "bea-Latn-CA": "bea", + "beb-Latn-CM": "beb", + "bec-Latn-CM": "bec", + "bed-Latn-ID": "bed", + "bee-Deva-IN": "bee", + "bef-Latn-ZZ": "bef", + "beh-Latn-ZZ": "beh", + "bei-Latn-ID": "bei", + "bej-Arab-SD": "bej", + "bek-Latn-PG": "bek", + "bem-Latn-ZM": "bem", + "beo-Latn-PG": "beo", + "bep-Latn-ID": "bep", + "beq-Latn-CG": "beq", + "bes-Latn-TD": "bes", + "bet-Latn-ZZ": "bet", + "beu-Latn-ID": "beu", + "bev-Latn-CI": "bev", + "bew-Latn-ID": "bew", + "bex-Latn-ZZ": "bex", + "bey-Latn-PG": "bey", + "bez-Latn-TZ": "bez", + "bfa-Arab-SS": "bfa-Arab", + "bfa-Latn-SS": "bfa", + "bfb-Deva-IN": "bfb", + "bfc-Latn-CN": "bfc", + "bfd-Latn-CM": "bfd", + "bfe-Latn-ID": "bfe", + "bff-Latn-CF": "bff", + "bfg-Latn-ID": "bfg", + "bfh-Latn-PG": "bfh", + "bfj-Latn-CM": "bfj", + "bfl-Latn-CF": "bfl", + "bfm-Latn-CM": "bfm", + "bfn-Latn-TL": "bfn", + "bfo-Latn-BF": "bfo", + "bfp-Latn-CM": "bfp", + "bfq-Taml-IN": "bfq", + "bfs-Hani-CN": "bfs-Hani", + "bfs-Latn-CN": "bfs", + "bft-Arab-PK": "bft", + "bfu-Takr-IN": "bfu-Takr", + "bfu-Tibt-IN": "bfu", + "bfw-Orya-IN": "bfw", + "bfx-Latn-PH": "bfx", + "bfy-Deva-IN": "bfy", + "bfz-Deva-IN": "bfz", + "bg-Cyrl-BG": "bg", + "bg-Cyrl-RO": "bg-RO", + "bga-Latn-NG": "bga", + "bgb-Latn-ID": "bgb", + "bgc-Deva-IN": "bgc", + "bgd-Deva-IN": "bgd", + "bgf-Latn-CM": "bgf", + "bgg-Latn-IN": "bgg", + "bgi-Latn-PH": "bgi", + "bgj-Latn-CM": "bgj", + "bgn-Arab-PK": "bgn", + "bgo-Latn-GN": "bgo", + "bgp-Arab-PK": "bgp", + "bgq-Deva-IN": "bgq", + "bgr-Latn-IN": "bgr", + "bgs-Latn-PH": "bgs", + "bgt-Latn-SB": "bgt", + "bgu-Latn-NG": "bgu", + "bgv-Latn-ID": "bgv", + "bgw-Deva-IN": "bgw", + "bgx-Grek-TR": "bgx", + "bgy-Latn-ID": "bgy", + "bgz-Latn-ID": "bgz", + "bha-Deva-IN": "bha", + "bhb-Deva-IN": "bhb", + "bhc-Latn-ID": "bhc", + "bhd-Arab-IN": "bhd-Arab", + "bhd-Deva-IN": "bhd", + "bhd-Takr-IN": "bhd-Takr", + "bhe-Arab-PK": "bhe", + "bhf-Latn-PG": "bhf", + "bhg-Latn-ZZ": "bhg", + "bhh-Cyrl-IL": "bhh", + "bhh-Hebr-IL": "bhh-Hebr", + "bhh-Latn-IL": "bhh-Latn", + "bhi-Deva-IN": "bhi", + "bhj-Deva-NP": "bhj", + "bhl-Latn-ZZ": "bhl", + "bhm-Arab-OM": "bhm", + "bhn-Syrc-GE": "bhn", + "bho-Deva-IN": "bho", + "bho-Deva-MU": "bho-MU", + "bho-Kthi-IN": "bho-Kthi", + "bhp-Latn-ID": "bhp", + "bhq-Latn-ID": "bhq", + "bhr-Latn-MG": "bhr", + "bhs-Latn-CM": "bhs", + "bht-Deva-IN": "bht-Deva", + "bht-Latn-IN": "bht-Latn", + "bht-Takr-IN": "bht", + "bhu-Deva-IN": "bhu", + "bhv-Latn-ID": "bhv", + "bhw-Latn-ID": "bhw", + "bhy-Latn-ZZ": "bhy", + "bhz-Latn-ID": "bhz", + "bi-Latn-VU": "bi", + "bia-Latn-AU": "bia", + "bib-Latn-ZZ": "bib", + "bid-Latn-TD": "bid", + "bie-Latn-PG": "bie", + "bif-Latn-GW": "bif", + "big-Latn-ZZ": "big", + "bik-Latn-PH": "bik", + "bil-Latn-NG": "bil", + "bim-Latn-ZZ": "bim", + "bin-Latn-NG": "bin", + "bio-Latn-ZZ": "bio", + "bip-Latn-CD": "bip", + "biq-Latn-ZZ": "biq", + "bir-Latn-PG": "bir", + "bit-Latn-PG": "bit", + "biu-Latn-IN": "biu", + "biv-Latn-GH": "biv", + "biw-Latn-CM": "biw", + "biy-Deva-IN": "biy", + "biz-Latn-CD": "biz", + "bja-Latn-CD": "bja", + "bjb-Latn-AU": "bjb", + "bjc-Latn-PG": "bjc", + "bjf-Syrc-IL": "bjf", + "bjg-Latn-GW": "bjg", + "bjh-Latn-ZZ": "bjh", + "bji-Ethi-ZZ": "bji", + "bjj-Deva-IN": "bjj", + "bjk-Latn-PG": "bjk", + "bjl-Latn-PG": "bjl", + "bjm-Arab-IQ": "bjm", + "bjn-Latn-ID": "bjn", + "bjo-Latn-ZZ": "bjo", + "bjp-Latn-PG": "bjp", + "bjr-Latn-ZZ": "bjr", + "bjs-Latn-BB": "bjs", + "bjt-Latn-SN": "bjt", + "bju-Latn-CM": "bju", + "bjv-Latn-TD": "bjv", + "bjw-Latn-CI": "bjw", + "bjx-Latn-PH": "bjx", + "bjy-Latn-AU": "bjy", + "bjz-Latn-ZZ": "bjz", + "bka-Latn-NG": "bka", + "bkc-Latn-ZZ": "bkc", + "bkd-Latn-PH": "bkd", + "bkf-Latn-CD": "bkf", + "bkg-Latn-CF": "bkg", + "bkh-Latn-CM": "bkh", + "bki-Latn-VU": "bki", + "bkj-Latn-CF": "bkj", + "bkl-Latn-ID": "bkl", + "bkm-Latn-CM": "bkm", + "bkn-Latn-ID": "bkn", + "bko-Latn-CM": "bko", + "bkp-Latn-CD": "bkp", + "bkq-Latn-ZZ": "bkq", + "bkr-Latn-ID": "bkr", + "bks-Latn-PH": "bks", + "bkt-Latn-CD": "bkt", + "bku-Buhd-PH": "bku-Buhd", + "bku-Latn-PH": "bku", + "bkv-Latn-ZZ": "bkv", + "bkw-Latn-CG": "bkw", + "bkx-Latn-TL": "bkx", + "bky-Latn-NG": "bky", + "bkz-Latn-ID": "bkz", + "bla-Latn-CA": "bla", + "blb-Latn-SB": "blb", + "blc-Latn-CA": "blc", + "bld-Latn-ID": "bld", + "ble-Latn-GW": "ble", + "blf-Latn-ID": "blf", + "blh-Latn-LR": "blh", + "bli-Latn-CD": "bli", + "blj-Latn-ID": "blj", + "blk-Mymr-MM": "blk", + "blm-Latn-SS": "blm", + "bln-Latn-PH": "bln", + "blo-Latn-BJ": "blo", + "blp-Latn-SB": "blp", + "blq-Latn-PG": "blq", + "blr-Latn-CN": "blr", + "blr-Tale-CN": "blr-Tale", + "blr-Thai-TH": "blr-Thai", + "bls-Latn-ID": "bls", + "blt-Tavt-VN": "blt", + "blv-Latn-AO": "blv", + "blw-Latn-PH": "blw", + "blx-Latn-PH": "blx", + "bly-Latn-BJ": "bly", + "blz-Latn-ID": "blz", + "bm-Latn-ML": "bm", + "bma-Latn-NG": "bma", + "bmb-Latn-CD": "bmb", + "bmc-Latn-PG": "bmc", + "bmd-Latn-GN": "bmd", + "bme-Latn-CF": "bme", + "bmf-Latn-SL": "bmf", + "bmg-Latn-CD": "bmg", + "bmh-Latn-ZZ": "bmh", + "bmi-Latn-TD": "bmi", + "bmj-Deva-NP": "bmj", + "bmk-Latn-ZZ": "bmk", + "bml-Latn-CD": "bml", + "bmm-Latn-MG": "bmm", + "bmn-Latn-PG": "bmn", + "bmo-Latn-CM": "bmo", + "bmp-Latn-PG": "bmp", + "bmq-Latn-ML": "bmq", + "bmr-Latn-CO": "bmr", + "bms-Latn-NE": "bms", + "bmu-Latn-ZZ": "bmu", + "bmv-Latn-CM": "bmv", + "bmw-Latn-CG": "bmw", + "bmx-Latn-PG": "bmx", + "bmz-Latn-PG": "bmz", + "bn-Beng-BD": "bn", + "bna-Latn-ID": "bna", + "bnb-Latn-MY": "bnb", + "bnc-Latn-PH": "bnc", + "bnd-Latn-ID": "bnd", + "bne-Latn-ID": "bne", + "bnf-Latn-ID": "bnf", + "bng-Latn-ZZ": "bng", + "bni-Latn-CD": "bni", + "bnj-Latn-PH": "bnj", + "bnk-Latn-VU": "bnk", + "bnm-Latn-ZZ": "bnm", + "bnn-Latn-TW": "bnn", + "bno-Latn-PH": "bno", + "bnp-Latn-ZZ": "bnp", + "bnq-Latn-ID": "bnq", + "bnr-Latn-VU": "bnr", + "bns-Deva-IN": "bns", + "bnu-Latn-ID": "bnu", + "bnv-Latn-ID": "bnv", + "bnw-Latn-PG": "bnw", + "bnx-Latn-CD": "bnx", + "bny-Latn-MY": "bny", + "bnz-Latn-CM": "bnz", + "bo-Marc-CN": "bo-Marc", + "bo-Tibt-CN": "bo", + "boa-Latn-PE": "boa", + "bob-Latn-KE": "bob", + "boe-Latn-CM": "boe", + "bof-Latn-BF": "bof", + "boh-Latn-CD": "boh", + "boj-Latn-ZZ": "boj", + "bok-Latn-CG": "bok", + "bol-Latn-NG": "bol", + "bom-Latn-ZZ": "bom", + "bon-Latn-ZZ": "bon", + "boo-Latn-ML": "boo", + "bop-Latn-PG": "bop", + "boq-Latn-PG": "boq", + "bor-Latn-BR": "bor", + "bot-Latn-SS": "bot", + "bou-Latn-TZ": "bou", + "bov-Latn-GH": "bov", + "bow-Latn-PG": "bow", + "box-Latn-BF": "box", + "boy-Latn-CF": "boy", + "boz-Arab-ML": "boz-Arab", + "boz-Latn-ML": "boz", + "bpa-Latn-VU": "bpa", + "bpc-Latn-CM": "bpc", + "bpd-Latn-CF": "bpd", + "bpe-Latn-PG": "bpe", + "bpg-Latn-ID": "bpg", + "bph-Cyrl-RU": "bph", + "bpi-Latn-PG": "bpi", + "bpj-Latn-CD": "bpj", + "bpk-Latn-NC": "bpk", + "bpl-Latn-AU": "bpl", + "bpm-Latn-PG": "bpm", + "bpo-Latn-ID": "bpo", + "bpp-Latn-ID": "bpp", + "bpq-Latn-ID": "bpq", + "bpr-Latn-PH": "bpr", + "bps-Latn-PH": "bps", + "bpt-Latn-AU": "bpt", + "bpu-Latn-PG": "bpu", + "bpv-Latn-ID": "bpv", + "bpw-Latn-PG": "bpw", + "bpx-Deva-IN": "bpx", + "bpy-Beng-IN": "bpy", + "bpz-Latn-ID": "bpz", + "bqa-Latn-BJ": "bqa", + "bqb-Latn-ID": "bqb", + "bqc-Latn-ZZ": "bqc", + "bqd-Latn-CM": "bqd", + "bqf-Arab-GN": "bqf-Arab", + "bqf-Latn-GN": "bqf", + "bqg-Latn-TG": "bqg", + "bqi-Arab-IR": "bqi", + "bqj-Latn-SN": "bqj", + "bqk-Latn-CF": "bqk", + "bql-Latn-PG": "bql", + "bqm-Latn-CM": "bqm", + "bqo-Latn-CM": "bqo", + "bqp-Latn-ZZ": "bqp", + "bqq-Latn-ID": "bqq", + "bqr-Latn-ID": "bqr", + "bqs-Latn-PG": "bqs", + "bqt-Latn-CM": "bqt", + "bqu-Latn-CD": "bqu", + "bqv-Latn-CI": "bqv", + "bqw-Latn-NG": "bqw", + "bqx-Latn-NG": "bqx", + "bqz-Latn-CM": "bqz", + "br-Latn-FR": "br", + "bra-Deva-IN": "bra", + "brb-Khmr-KH": "brb", + "brb-Laoo-LA": "brb-Laoo", + "brb-Latn-VN": "brb-Latn", + "brc-Latn-GY": "brc", + "brd-Deva-NP": "brd", + "brf-Latn-CD": "brf", + "brg-Latn-BO": "brg", + "brh-Arab-PK": "brh", + "bri-Latn-CM": "bri", + "brj-Latn-VU": "brj", + "brk-Arab-SD": "brk", + "brl-Latn-BW": "brl", + "brm-Latn-CD": "brm", + "brn-Latn-CR": "brn", + "brp-Latn-ID": "brp", + "brq-Latn-PG": "brq", + "brr-Latn-SB": "brr", + "brs-Latn-ID": "brs", + "brt-Latn-NG": "brt", + "bru-Laoo-LA": "bru-Laoo", + "bru-Latn-VN": "bru", + "bru-Thai-LA": "bru-Thai", + "brv-Laoo-LA": "brv", + "brx-Deva-IN": "brx", + "bry-Latn-PG": "bry", + "brz-Latn-ZZ": "brz", + "bs-Latn-BA": "bs", + "bsa-Latn-ID": "bsa", + "bsb-Latn-BN": "bsb", + "bsc-Latn-SN": "bsc", + "bse-Latn-CM": "bse", + "bsf-Latn-NG": "bsf", + "bsh-Arab-AF": "bsh", + "bsi-Latn-CM": "bsi", + "bsj-Latn-ZZ": "bsj", + "bsk-Arab-PK": "bsk", + "bsk-Latn-PK": "bsk-Latn", + "bsl-Latn-NG": "bsl", + "bsm-Latn-ID": "bsm", + "bsn-Latn-CO": "bsn", + "bso-Latn-TD": "bso", + "bsp-Latn-GN": "bsp", + "bsq-Bass-LR": "bsq", + "bsr-Latn-NG": "bsr", + "bss-Latn-CM": "bss", + "bst-Ethi-ZZ": "bst", + "bsu-Latn-ID": "bsu", + "bsv-Arab-GN": "bsv-Arab", + "bsv-Latn-GN": "bsv", + "bsw-Ethi-ET": "bsw-Ethi", + "bsw-Latn-ET": "bsw", + "bsx-Latn-NG": "bsx", + "bsy-Latn-MY": "bsy", + "bta-Latn-NG": "bta", + "btc-Latn-CM": "btc", + "btd-Batk-ID": "btd", + "bte-Latn-NG": "bte", + "btf-Latn-TD": "btf", + "btg-Latn-CI": "btg", + "bth-Latn-MY": "bth", + "bti-Latn-ID": "bti", + "btj-Latn-ID": "btj", + "btm-Batk-ID": "btm", + "btn-Latn-PH": "btn", + "bto-Latn-PH": "bto", + "btp-Latn-PG": "btp", + "btq-Latn-MY": "btq", + "btr-Latn-VU": "btr", + "bts-Batk-ID": "bts-Batk", + "bts-Latn-ID": "bts", + "btt-Latn-ZZ": "btt", + "btu-Latn-NG": "btu", + "btv-Deva-PK": "btv", + "btw-Latn-PH": "btw", + "btx-Batk-ID": "btx-Batk", + "btx-Latn-ID": "btx", + "bty-Latn-ID": "bty", + "btz-Latn-ID": "btz", + "bua-Cyrl-RU": "bua", + "bub-Latn-TD": "bub", + "buc-Latn-YT": "buc", + "bud-Latn-ZZ": "bud", + "bue-Latn-CA": "bue", + "buf-Latn-CD": "buf", + "bug-Bugi-ID": "bug-Bugi", + "bug-Latn-ID": "bug", + "buh-Latn-CN": "buh", + "bui-Latn-CG": "bui", + "buj-Latn-NG": "buj", + "buk-Latn-ZZ": "buk", + "bum-Latn-CM": "bum", + "bun-Latn-SL": "bun", + "buo-Latn-ZZ": "buo", + "bup-Latn-ID": "bup", + "buq-Latn-PG": "buq", + "bus-Latn-ZZ": "bus", + "but-Latn-PG": "but", + "buu-Latn-ZZ": "buu", + "buv-Latn-PG": "buv", + "buw-Latn-GA": "buw", + "bux-Latn-NG": "bux", + "buy-Latn-SL": "buy", + "buz-Latn-NG": "buz", + "bva-Latn-TD": "bva", + "bvb-Latn-GQ": "bvb", + "bvc-Latn-SB": "bvc", + "bvd-Latn-SB": "bvd", + "bve-Latn-ID": "bve", + "bvf-Latn-TD": "bvf", + "bvg-Latn-CM": "bvg", + "bvh-Latn-NG": "bvh", + "bvi-Latn-SS": "bvi", + "bvj-Latn-NG": "bvj", + "bvk-Latn-ID": "bvk", + "bvm-Latn-CM": "bvm", + "bvn-Latn-PG": "bvn", + "bvo-Latn-TD": "bvo", + "bvq-Latn-CF": "bvq", + "bvr-Latn-AU": "bvr", + "bvt-Latn-ID": "bvt", + "bvu-Latn-ID": "bvu", + "bvv-Latn-VE": "bvv", + "bvw-Latn-NG": "bvw", + "bvx-Latn-CG": "bvx", + "bvy-Latn-PH": "bvy", + "bvz-Latn-ID": "bvz", + "bwa-Latn-NC": "bwa", + "bwb-Latn-FJ": "bwb", + "bwc-Latn-ZM": "bwc", + "bwd-Latn-ZZ": "bwd", + "bwe-Latn-MM": "bwe-Latn", + "bwe-Mymr-MM": "bwe", + "bwf-Latn-PG": "bwf", + "bwg-Latn-MZ": "bwg", + "bwh-Latn-CM": "bwh", + "bwi-Latn-VE": "bwi", + "bwj-Latn-BF": "bwj", + "bwk-Latn-PG": "bwk", + "bwl-Latn-CD": "bwl", + "bwm-Latn-PG": "bwm", + "bwo-Ethi-ET": "bwo-Ethi", + "bwo-Latn-ET": "bwo", + "bwp-Latn-ID": "bwp", + "bwq-Latn-BF": "bwq", + "bwr-Latn-ZZ": "bwr", + "bws-Latn-CD": "bws", + "bwt-Latn-CM": "bwt", + "bwu-Latn-GH": "bwu", + "bww-Latn-CD": "bww", + "bwx-Latn-CN": "bwx", + "bwy-Latn-BF": "bwy", + "bwz-Latn-CG": "bwz", + "bxa-Latn-SB": "bxa", + "bxb-Latn-SS": "bxb", + "bxc-Latn-GQ": "bxc", + "bxf-Latn-PG": "bxf", + "bxg-Latn-CD": "bxg", + "bxh-Latn-ZZ": "bxh", + "bxi-Latn-AU": "bxi", + "bxj-Latn-AU": "bxj", + "bxl-Latn-BF": "bxl", + "bxm-Cyrl-MN": "bxm", + "bxm-Latn-MN": "bxm-Latn", + "bxm-Mong-MN": "bxm-Mong", + "bxn-Latn-AU": "bxn", + "bxo-Latn-NG": "bxo", + "bxp-Latn-CM": "bxp", + "bxq-Latn-NG": "bxq", + "bxs-Latn-CM": "bxs", + "bxu-Cyrl-CN": "bxu-Cyrl", + "bxu-Latn-CN": "bxu-Latn", + "bxu-Mong-CN": "bxu", + "bxv-Latn-TD": "bxv", + "bxw-Latn-ML": "bxw", + "bxz-Latn-PG": "bxz", + "bya-Latn-PH": "bya", + "byb-Latn-CM": "byb", + "byc-Latn-NG": "byc", + "byd-Latn-ID": "byd", + "bye-Latn-ZZ": "bye", + "byf-Latn-NG": "byf", + "byh-Deva-NP": "byh", + "byi-Latn-CD": "byi", + "byj-Latn-NG": "byj", + "byk-Latn-CN": "byk", + "byl-Latn-ID": "byl", + "bym-Latn-AU": "bym", + "byn-Ethi-ER": "byn", + "byp-Latn-NG": "byp", + "byr-Latn-ZZ": "byr", + "bys-Latn-ZZ": "bys", + "byv-Latn-CM": "byv", + "byw-Deva-NP": "byw", + "byx-Latn-ZZ": "byx", + "byz-Latn-PG": "byz", + "bza-Latn-ZZ": "bza", + "bzb-Latn-ID": "bzb", + "bzc-Latn-MG": "bzc", + "bzd-Latn-CR": "bzd", + "bze-Latn-ML": "bze", + "bzf-Latn-ZZ": "bzf", + "bzh-Latn-ZZ": "bzh", + "bzi-Thai-TH": "bzi", + "bzj-Latn-BZ": "bzj", + "bzk-Latn-NI": "bzk", + "bzl-Latn-ID": "bzl", + "bzm-Latn-CD": "bzm", + "bzn-Latn-ID": "bzn", + "bzo-Latn-CD": "bzo", + "bzp-Latn-ID": "bzp", + "bzq-Latn-ID": "bzq", + "bzr-Latn-AU": "bzr", + "bzt-Latn-001": "bzt", + "bzu-Latn-ID": "bzu", + "bzv-Latn-CM": "bzv", + "bzw-Latn-ZZ": "bzw", + "bzx-Latn-ML": "bzx", + "bzy-Latn-NG": "bzy", + "bzz-Latn-NG": "bzz", + "ca-Latn-AD": "ca-AD", + "ca-Latn-ES": "ca", + "caa-Latn-GT": "caa", + "cab-Latn-HN": "cab", + "cac-Latn-GT": "cac", + "cad-Latn-US": "cad", + "cae-Latn-SN": "cae", + "caf-Cans-CA": "caf-Cans", + "caf-Latn-CA": "caf", + "cag-Latn-PY": "cag", + "cah-Latn-PE": "cah", + "caj-Latn-AR": "caj", + "cak-Latn-GT": "cak", + "cal-Latn-MP": "cal", + "cam-Latn-NC": "cam", + "can-Latn-ZZ": "can", + "cao-Latn-BO": "cao", + "cap-Latn-BO": "cap", + "caq-Latn-IN": "caq", + "car-Latn-VE": "car", + "cas-Latn-BO": "cas", + "cav-Latn-BO": "cav", + "caw-Latn-BO": "caw", + "cax-Latn-BO": "cax", + "cay-Latn-CA": "cay", + "caz-Latn-BO": "caz", + "cbb-Latn-CO": "cbb", + "cbc-Latn-CO": "cbc", + "cbd-Latn-CO": "cbd", + "cbg-Latn-CO": "cbg", + "cbi-Latn-EC": "cbi", + "cbj-Latn-ZZ": "cbj", + "cbk-Brai-PH": "cbk-Brai", + "cbk-Latn-PH": "cbk", + "cbl-Latn-MM": "cbl", + "cbn-Thai-TH": "cbn", + "cbo-Latn-NG": "cbo", + "cbq-Latn-NG": "cbq", + "cbr-Latn-PE": "cbr", + "cbs-Latn-PE": "cbs", + "cbt-Latn-PE": "cbt", + "cbu-Latn-PE": "cbu", + "cbv-Latn-CO": "cbv", + "cbw-Latn-PH": "cbw", + "cby-Latn-CO": "cby", + "ccc-Latn-PE": "ccc", + "ccd-Latn-BR": "ccd", + "cce-Latn-MZ": "cce", + "ccg-Latn-NG": "ccg", + "cch-Latn-NG": "cch", + "ccj-Latn-GW": "ccj", + "ccl-Latn-TZ": "ccl", + "ccm-Latn-MY": "ccm", + "cco-Latn-MX": "cco", + "ccp-Cakm-BD": "ccp", + "ccr-Latn-SV": "ccr", + "cde-Telu-IN": "cde", + "cdf-Beng-IN": "cdf-Beng", + "cdf-Latn-IN": "cdf", + "cdh-Deva-IN": "cdh", + "cdh-Takr-IN": "cdh-Takr", + "cdi-Gujr-IN": "cdi", + "cdj-Deva-IN": "cdj", + "cdm-Deva-NP": "cdm", + "cdm-Latn-NP": "cdm-Latn", + "cdo-Hans-CN": "cdo", + "cdo-Hant-CN": "cdo-Hant", + "cdo-Latn-CN": "cdo-Latn", + "cdr-Latn-NG": "cdr", + "cdz-Beng-IN": "cdz", + "ce-Cyrl-RU": "ce", + "cea-Latn-US": "cea", + "ceb-Latn-PH": "ceb", + "ceg-Latn-PY": "ceg", + "cek-Latn-MM": "cek", + "cen-Latn-NG": "cen", + "cet-Latn-NG": "cet", + "cey-Latn-MM": "cey", + "cfa-Latn-ZZ": "cfa", + "cfd-Latn-NG": "cfd", + "cfg-Latn-NG": "cfg", + "cfm-Beng-IN": "cfm-Beng", + "cfm-Latn-MM": "cfm", + "cga-Latn-PG": "cga", + "cgc-Latn-PH": "cgc", + "cgg-Latn-UG": "cgg", + "cgk-Tibt-BT": "cgk", + "ch-Latn-GU": "ch", + "chb-Latn-CO": "chb", + "chd-Latn-MX": "chd", + "chf-Latn-MX": "chf", + "chg-Arab-TM": "chg", + "chh-Latn-US": "chh", + "chj-Latn-MX": "chj", + "chk-Latn-FM": "chk", + "chl-Latn-US": "chl", + "chm-Cyrl-RU": "chm", + "chn-Dupl-US": "chn-Dupl", + "chn-Latn-US": "chn", + "cho-Latn-US": "cho", + "chp-Latn-CA": "chp", + "chq-Latn-MX": "chq", + "chr-Cher-US": "chr", + "cht-Latn-PE": "cht", + "chw-Latn-MZ": "chw", + "chx-Deva-NP": "chx", + "chy-Latn-US": "chy", + "chz-Latn-MX": "chz", + "cia-Arab-ID": "cia-Arab", + "cia-Hang-ID": "cia-Hang", + "cia-Latn-ID": "cia", + "cib-Latn-BJ": "cib", + "cic-Latn-US": "cic", + "cie-Latn-NG": "cie", + "cih-Deva-IN": "cih", + "cim-Latn-IT": "cim", + "cin-Latn-BR": "cin", + "cip-Latn-MX": "cip", + "cir-Latn-NC": "cir", + "ciw-Cans-US": "ciw-Cans", + "ciw-Latn-US": "ciw", + "ciy-Latn-VE": "ciy", + "cja-Arab-KH": "cja", + "cje-Latn-VN": "cje", + "cjh-Latn-US": "cjh", + "cji-Cyrl-RU": "cji", + "cjk-Latn-AO": "cjk", + "cjm-Cham-VN": "cjm", + "cjn-Latn-PG": "cjn", + "cjo-Latn-PE": "cjo", + "cjp-Latn-CR": "cjp", + "cjs-Cyrl-RU": "cjs-Cyrl", + "cjs-Latn-RU": "cjs", + "cjv-Latn-ZZ": "cjv", + "cjy-Hans-CN": "cjy", + "cjy-Hant-CN": "cjy-Hant", + "ckb-Arab-IQ": "ckb", + "ckl-Latn-ZZ": "ckl", + "ckm-Glag-HR": "ckm-Glag", + "ckm-Latn-HR": "ckm", + "ckn-Latn-MM": "ckn", + "cko-Latn-ZZ": "cko", + "ckq-Latn-TD": "ckq", + "ckr-Latn-PG": "ckr", + "cks-Latn-NC": "cks", + "ckt-Cyrl-RU": "ckt", + "cku-Latn-US": "cku", + "ckv-Latn-TW": "ckv", + "ckx-Latn-CM": "ckx", + "cky-Latn-ZZ": "cky", + "ckz-Latn-GT": "ckz", + "cla-Latn-ZZ": "cla", + "clc-Latn-CA": "clc", + "cle-Latn-MX": "cle", + "clh-Arab-PK": "clh", + "cli-Latn-GH": "cli", + "clj-Latn-MM": "clj", + "clk-Latn-IN": "clk", + "clk-Tibt-CN": "clk-Tibt", + "cll-Latn-GH": "cll", + "clm-Latn-US": "clm", + "clo-Latn-MX": "clo", + "clt-Latn-MM": "clt", + "clu-Latn-PH": "clu", + "clw-Cyrl-RU": "clw", + "cly-Latn-MX": "cly", + "cma-Latn-VN": "cma", + "cme-Latn-ZZ": "cme", + "cmg-Soyo-MN": "cmg", + "cmg-Zanb-MN": "cmg-Zanb", + "cmi-Latn-CO": "cmi", + "cml-Latn-ID": "cml", + "cmo-Khmr-KH": "cmo-Khmr", + "cmo-Latn-KH": "cmo-KH", + "cmo-Latn-VN": "cmo", + "cmr-Latn-MM": "cmr", + "cms-Latn-IT": "cms", + "cmt-Latn-ZA": "cmt", + "cna-Tibt-IN": "cna", + "cnb-Latn-MM": "cnb", + "cnc-Latn-VN": "cnc", + "cng-Latn-CN": "cng", + "cnh-Latn-MM": "cnh", + "cni-Latn-PE": "cni", + "cnk-Latn-MM": "cnk", + "cnl-Latn-MX": "cnl", + "cnp-Hans-CN": "cnp", + "cnp-Hant-CN": "cnp-Hant", + "cnq-Latn-CM": "cnq", + "cns-Latn-ID": "cns", + "cnt-Latn-MX": "cnt", + "cnw-Latn-MM": "cnw", + "cnx-Latn-GB": "cnx", + "co-Latn-FR": "co", + "coa-Latn-AU": "coa", + "cob-Latn-MX": "cob", + "coc-Latn-MX": "coc", + "cod-Latn-PE": "cod", + "coe-Latn-CO": "coe", + "cof-Latn-EC": "cof", + "cog-Thai-TH": "cog", + "coh-Latn-KE": "coh", + "coj-Latn-MX": "coj", + "cok-Latn-MX": "cok", + "col-Latn-US": "col", + "com-Latn-US": "com", + "coo-Latn-CA": "coo", + "cop-Copt-EG": "cop", + "coq-Latn-US": "coq", + "cot-Latn-PE": "cot", + "cou-Latn-SN": "cou", + "cox-Latn-PE": "cox", + "coz-Latn-MX": "coz", + "cpa-Latn-MX": "cpa", + "cpb-Latn-PE": "cpb", + "cpc-Latn-PE": "cpc", + "cpg-Grek-GR": "cpg", + "cpi-Latn-NR": "cpi", + "cpn-Latn-GH": "cpn", + "cpo-Latn-BF": "cpo", + "cps-Latn-PH": "cps", + "cpu-Latn-PE": "cpu", + "cpx-Latn-CN": "cpx", + "cpy-Latn-PE": "cpy", + "cqd-Latn-CN": "cqd", + "cr-Cans-CA": "cr", + "crb-Latn-VC": "crb", + "crc-Latn-VU": "crc", + "crd-Latn-US": "crd", + "crf-Latn-CO": "crf", + "crg-Latn-CA": "crg", + "crh-Cyrl-UA": "crh", + "cri-Latn-ST": "cri", + "crj-Cans-CA": "crj", + "crj-Latn-CA": "crj-Latn", + "crk-Cans-CA": "crk", + "crl-Cans-CA": "crl", + "crm-Cans-CA": "crm", + "crn-Latn-MX": "crn", + "cro-Latn-US": "cro", + "crq-Latn-AR": "crq", + "crs-Latn-SC": "crs", + "crt-Latn-AR": "crt", + "crv-Latn-IN": "crv", + "crw-Latn-VN": "crw", + "crx-Cans-CA": "crx-Cans", + "crx-Latn-CA": "crx", + "cry-Latn-NG": "cry", + "crz-Latn-US": "crz", + "cs-Latn-CZ": "cs", + "csa-Latn-MX": "csa", + "csb-Latn-PL": "csb", + "csh-Latn-MM": "csh-Latn", + "csh-Mymr-MM": "csh", + "csj-Latn-MM": "csj", + "csk-Latn-SN": "csk", + "csm-Latn-US": "csm", + "cso-Latn-MX": "cso", + "csp-Hans-CN": "csp", + "csp-Hant-CN": "csp-Hant", + "css-Latn-US": "css", + "cst-Latn-US": "cst", + "csv-Latn-MM": "csv", + "csw-Cans-CA": "csw", + "csy-Latn-MM": "csy", + "csz-Latn-US": "csz", + "cta-Latn-MX": "cta", + "ctc-Latn-US": "ctc", + "ctd-Pauc-MM": "ctd", + "cte-Latn-MX": "cte", + "ctg-Arab-BD": "ctg-Arab", + "ctg-Beng-BD": "ctg", + "ctg-Latn-BD": "ctg-Latn", + "cth-Latn-MM": "cth", + "ctl-Latn-MX": "ctl", + "ctm-Latn-US": "ctm", + "ctn-Deva-NP": "ctn", + "cto-Latn-CO": "cto", + "ctp-Latn-MX": "ctp", + "cts-Latn-PH": "cts", + "ctt-Taml-IN": "ctt", + "ctu-Latn-MX": "ctu", + "ctz-Latn-MX": "ctz", + "cu-Cyrl-RU": "cu", + "cu-Glag-BG": "cu-Glag", + "cua-Latn-VN": "cua", + "cub-Latn-CO": "cub", + "cuc-Latn-MX": "cuc", + "cuh-Latn-KE": "cuh", + "cui-Latn-CO": "cui", + "cuj-Latn-PE": "cuj", + "cuk-Latn-PA": "cuk", + "cul-Latn-BR": "cul", + "cuo-Latn-VE": "cuo", + "cup-Latn-US": "cup", + "cut-Latn-MX": "cut", + "cuu-Lana-CN": "cuu", + "cuv-Latn-CM": "cuv", + "cux-Latn-MX": "cux", + "cv-Cyrl-RU": "cv", + "cvg-Latn-IN": "cvg", + "cvg-Tibt-IN": "cvg-Tibt", + "cvn-Latn-MX": "cvn", + "cwa-Latn-TZ": "cwa", + "cwb-Latn-MZ": "cwb", + "cwe-Latn-TZ": "cwe", + "cwg-Latn-MY": "cwg", + "cwt-Latn-SN": "cwt", + "cy-Latn-GB": "cy", + "cya-Latn-MX": "cya", + "cyb-Latn-BO": "cyb", + "cyo-Latn-PH": "cyo", + "czh-Hans-CN": "czh", + "czh-Hant-CN": "czh-Hant", + "czk-Hebr-CZ": "czk", + "czn-Latn-MX": "czn", + "czt-Latn-MM": "czt", + "da-Latn-DK": "da", + "daa-Latn-TD": "daa", + "dac-Latn-PG": "dac", + "dad-Latn-ZZ": "dad", + "dae-Latn-CM": "dae", + "dag-Latn-ZZ": "dag", + "dah-Latn-ZZ": "dah", + "dai-Latn-TD": "dai", + "daj-Latn-SD": "daj", + "dak-Latn-US": "dak", + "dal-Latn-KE": "dal", + "dam-Latn-NG": "dam", + "dao-Latn-MM": "dao", + "daq-Deva-IN": "daq", + "dar-Cyrl-RU": "dar", + "das-Latn-CI": "das", + "dau-Latn-TD": "dau", + "dav-Latn-KE": "dav", + "daw-Latn-PH": "daw", + "dax-Latn-AU": "dax", + "daz-Latn-ID": "daz", + "dba-Latn-ML": "dba", + "dbb-Latn-NG": "dbb", + "dbd-Latn-ZZ": "dbd", + "dbe-Latn-ID": "dbe", + "dbf-Latn-ID": "dbf", + "dbg-Latn-ML": "dbg", + "dbi-Latn-NG": "dbi", + "dbj-Arab-MY": "dbj-Arab", + "dbj-Latn-MY": "dbj", + "dbl-Latn-AU": "dbl", + "dbm-Latn-NG": "dbm", + "dbn-Latn-ID": "dbn", + "dbo-Latn-NG": "dbo", + "dbp-Latn-NG": "dbp", + "dbq-Latn-ZZ": "dbq", + "dbt-Latn-ML": "dbt", + "dbu-Latn-ML": "dbu", + "dbv-Latn-NG": "dbv", + "dbw-Latn-ML": "dbw", + "dby-Latn-PG": "dby", + "dcc-Arab-IN": "dcc", + "dcr-Latn-VI": "dcr", + "dda-Latn-AU": "dda", + "ddd-Latn-SS": "ddd", + "dde-Latn-CG": "dde", + "ddg-Latn-TL": "ddg", + "ddi-Latn-PG": "ddi", + "ddj-Latn-AU": "ddj", + "ddn-Latn-ZZ": "ddn", + "ddo-Cyrl-RU": "ddo", + "ddr-Latn-AU": "ddr", + "dds-Latn-ML": "dds", + "ddw-Latn-ID": "ddw", + "de-Latn-AT": "de-AT", + "de-Latn-CH": "de-CH", + "de-Latn-DE": "de", + "de-Latn-EZ": "de-EZ", + "de-Latn-LI": "de-LI", + "dec-Latn-SD": "dec", + "ded-Latn-ZZ": "ded", + "dee-Latn-LR": "dee", + "def-Arab-IR": "def", + "deg-Latn-NG": "deg", + "deh-Arab-PK": "deh", + "dei-Latn-ID": "dei", + "dek-Latn-CM": "dek", + "del-Latn-US": "del", + "dem-Latn-ID": "dem", + "den-Latn-CA": "den", + "deq-Latn-CF": "deq", + "der-Beng-IN": "der", + "der-Latn-IN": "der-Latn", + "des-Latn-BR": "des", + "dev-Latn-PG": "dev", + "dez-Latn-CD": "dez", + "dga-Latn-ZZ": "dga", + "dgb-Latn-ML": "dgb", + "dgc-Latn-PH": "dgc", + "dgd-Latn-BF": "dgd", + "dge-Latn-PG": "dge", + "dgg-Latn-PG": "dgg", + "dgh-Latn-ZZ": "dgh", + "dgi-Latn-ZZ": "dgi", + "dgk-Latn-CF": "dgk", + "dgl-Arab-ZZ": "dgl", + "dgn-Latn-AU": "dgn", + "dgr-Latn-CA": "dgr", + "dgs-Latn-BF": "dgs", + "dgt-Latn-AU": "dgt", + "dgw-Latn-AU": "dgw", + "dgx-Latn-PG": "dgx", + "dgz-Latn-ZZ": "dgz", + "dhg-Latn-AU": "dhg", + "dhi-Deva-NP": "dhi", + "dhl-Latn-AU": "dhl", + "dhm-Latn-AO": "dhm", + "dhn-Gujr-IN": "dhn", + "dho-Deva-IN": "dho", + "dhr-Latn-AU": "dhr", + "dhs-Latn-TZ": "dhs", + "dhu-Latn-AU": "dhu", + "dhv-Latn-NC": "dhv", + "dhw-Deva-NP": "dhw", + "dhx-Latn-AU": "dhx", + "dia-Latn-ZZ": "dia", + "dib-Latn-SS": "dib", + "dic-Latn-CI": "dic", + "did-Latn-SS": "did", + "dif-Latn-AU": "dif", + "dig-Latn-KE": "dig", + "dih-Latn-MX": "dih", + "dii-Latn-CM": "dii", + "dij-Latn-ID": "dij", + "dil-Latn-SD": "dil", + "din-Arab-SS": "din-Arab", + "din-Latn-SS": "din", + "dio-Latn-NG": "dio", + "dip-Latn-SS": "dip", + "dir-Latn-NG": "dir", + "dis-Beng-IN": "dis-Beng", + "dis-Latn-IN": "dis", + "diu-Latn-NA": "diu", + "diw-Latn-SS": "diw", + "dix-Latn-VU": "dix", + "diy-Latn-ID": "diy", + "diz-Latn-CD": "diz", + "dja-Latn-AU": "dja", + "djb-Latn-AU": "djb", + "djc-Latn-TD": "djc", + "djd-Latn-AU": "djd", + "dje-Latn-NE": "dje", + "djf-Latn-AU": "djf", + "dji-Latn-AU": "dji", + "djj-Latn-AU": "djj", + "djk-Latn-SR": "djk", + "djm-Latn-ML": "djm", + "djn-Latn-AU": "djn", + "djo-Latn-ID": "djo", + "djr-Latn-AU": "djr", + "dju-Latn-PG": "dju", + "djw-Latn-AU": "djw", + "dka-Tibt-BT": "dka", + "dkg-Latn-NG": "dkg", + "dkk-Latn-ID": "dkk", + "dkr-Latn-MY": "dkr", + "dks-Latn-SS": "dks", + "dkx-Latn-CM": "dkx", + "dlg-Cyrl-RU": "dlg", + "dlm-Latn-HR": "dlm", + "dln-Latn-IN": "dln", + "dma-Latn-GA": "dma", + "dmb-Latn-ML": "dmb", + "dmc-Latn-PG": "dmc", + "dmd-Latn-AU": "dmd", + "dme-Latn-CM": "dme", + "dmf-Medf-NG": "dmf", + "dmg-Latn-MY": "dmg", + "dmk-Arab-PK": "dmk", + "dml-Arab-PK": "dml", + "dmm-Latn-CM": "dmm", + "dmo-Latn-CM": "dmo", + "dmr-Latn-ID": "dmr", + "dms-Latn-ID": "dms", + "dmu-Latn-ID": "dmu", + "dmv-Latn-MY": "dmv", + "dmw-Latn-AU": "dmw", + "dmx-Latn-MZ": "dmx", + "dmy-Latn-ID": "dmy", + "dna-Latn-ID": "dna", + "dnd-Latn-PG": "dnd", + "dne-Latn-TZ": "dne", + "dng-Arab-KG": "dng-Arab", + "dng-Cyrl-KG": "dng", + "dni-Latn-ID": "dni", + "dnj-Latn-CI": "dnj", + "dnk-Latn-ID": "dnk", + "dnn-Latn-BF": "dnn", + "dno-Latn-CD": "dno", + "dnr-Latn-PG": "dnr", + "dnt-Latn-ID": "dnt", + "dnu-Mymr-MM": "dnu", + "dnv-Mymr-MM": "dnv", + "dnw-Latn-ID": "dnw", + "dny-Latn-BR": "dny", + "doa-Latn-PG": "doa", + "dob-Latn-ZZ": "dob", + "doc-Latn-CN": "doc", + "doe-Latn-TZ": "doe", + "dof-Latn-PG": "dof", + "doh-Latn-NG": "doh", + "doi-Deva-IN": "doi", + "doi-Dogr-IN": "doi-Dogr", + "doi-Takr-IN": "doi-Takr", + "dok-Latn-ID": "dok", + "dol-Latn-PG": "dol", + "don-Latn-PG": "don", + "doo-Latn-CD": "doo", + "dop-Latn-ZZ": "dop", + "dor-Latn-SB": "dor", + "dos-Latn-BF": "dos", + "dot-Latn-NG": "dot", + "dov-Latn-ZW": "dov", + "dow-Latn-ZZ": "dow", + "dox-Ethi-ET": "dox", + "doy-Latn-GH": "doy", + "dpp-Latn-MY": "dpp", + "drc-Latn-PT": "drc", + "dre-Tibt-NP": "dre", + "drg-Latn-MY": "drg", + "dri-Latn-ZZ": "dri", + "drl-Latn-AU": "drl", + "drn-Latn-ID": "drn", + "dro-Latn-MY": "dro", + "drq-Deva-NP": "drq", + "drs-Ethi-ZZ": "drs", + "drt-Latn-NL": "drt", + "dru-Latn-TW": "dru", + "dry-Deva-NP": "dry", + "dsb-Latn-DE": "dsb", + "dsh-Latn-KE": "dsh", + "dsi-Latn-TD": "dsi", + "dsn-Latn-ID": "dsn", + "dso-Orya-IN": "dso", + "dsq-Arab-ML": "dsq-Arab", + "dsq-Latn-ML": "dsq", + "dta-Cyrl-CN": "dta-Cyrl", + "dta-Hans-CN": "dta-Hans", + "dta-Latn-CN": "dta", + "dtb-Latn-MY": "dtb", + "dtd-Latn-CA": "dtd", + "dth-Latn-AU": "dth", + "dti-Latn-ML": "dti", + "dtk-Latn-ML": "dtk", + "dtm-Latn-ML": "dtm", + "dto-Latn-ML": "dto", + "dtp-Latn-MY": "dtp", + "dtr-Latn-MY": "dtr", + "dts-Latn-ZZ": "dts", + "dtt-Latn-ML": "dtt", + "dtu-Latn-ML": "dtu", + "dty-Deva-NP": "dty", + "dua-Latn-CM": "dua", + "dub-Gujr-IN": "dub", + "duc-Latn-ZZ": "duc", + "due-Latn-PH": "due", + "duf-Latn-NC": "duf", + "dug-Latn-ZZ": "dug", + "duh-Deva-IN": "duh", + "duh-Gujr-IN": "duh-Gujr", + "dui-Latn-PG": "dui", + "duk-Latn-PG": "duk", + "dul-Latn-PH": "dul", + "dum-Latn-NL": "dum", + "dun-Latn-ID": "dun", + "duo-Latn-PH": "duo", + "dup-Latn-ID": "dup", + "duq-Latn-ID": "duq", + "dur-Latn-CM": "dur", + "dus-Deva-NP": "dus", + "duu-Latn-CN": "duu", + "duv-Latn-ID": "duv", + "duw-Latn-ID": "duw", + "dux-Latn-ML": "dux", + "duy-Latn-PH": "duy", + "duz-Latn-CM": "duz", + "dv-Diak-MV": "dv-Diak", + "dv-Thaa-MV": "dv", + "dva-Latn-ZZ": "dva", + "dwa-Latn-NG": "dwa", + "dwk-Orya-IN": "dwk", + "dwr-Ethi-ET": "dwr-Ethi", + "dwr-Latn-ET": "dwr", + "dws-Latn-001": "dws", + "dwu-Latn-AU": "dwu", + "dww-Latn-ZZ": "dww", + "dwy-Latn-AU": "dwy", + "dwz-Deva-NP": "dwz", + "dya-Latn-BF": "dya", + "dyb-Latn-AU": "dyb", + "dyd-Latn-AU": "dyd", + "dyg-Latn-PH": "dyg", + "dyi-Latn-CI": "dyi", + "dym-Latn-ML": "dym", + "dyn-Latn-AU": "dyn", + "dyo-Latn-SN": "dyo", + "dyu-Latn-BF": "dyu", + "dyy-Latn-AU": "dyy", + "dz-Tibt-BT": "dz", + "dza-Latn-NG": "dza", + "dze-Latn-AU": "dze", + "dzg-Latn-ZZ": "dzg", + "dzl-Tibt-BT": "dzl", + "dzn-Latn-CD": "dzn", + "eaa-Latn-AU": "eaa", + "ebc-Latn-ID": "ebc", + "ebg-Latn-NG": "ebg", + "ebk-Latn-PH": "ebk", + "ebo-Latn-CG": "ebo", + "ebr-Latn-CI": "ebr", + "ebu-Latn-KE": "ebu", + "ecr-Grek-GR": "ecr", + "ecy-Cprt-CY": "ecy", + "ee-Latn-GH": "ee", + "efa-Latn-NG": "efa", + "efe-Latn-CD": "efe", + "efi-Latn-NG": "efi", + "ega-Latn-CI": "ega", + "egl-Latn-IT": "egl", + "egm-Latn-TZ": "egm", + "ego-Latn-NG": "ego", + "egy-Egyp-EG": "egy", + "ehu-Latn-NG": "ehu", + "eip-Latn-ID": "eip", + "eit-Latn-PG": "eit", + "eiv-Latn-PG": "eiv", + "eja-Latn-GW": "eja", + "eka-Latn-ZZ": "eka", + "eke-Latn-NG": "eke", + "ekg-Latn-ID": "ekg", + "eki-Latn-NG": "eki", + "ekl-Latn-BD": "ekl", + "ekm-Latn-CM": "ekm", + "eko-Arab-MZ": "eko-Arab", + "eko-Latn-MZ": "eko", + "ekp-Latn-NG": "ekp", + "ekr-Latn-NG": "ekr", + "eky-Kali-MM": "eky", + "el-Grek-CY": "el-CY", + "el-Grek-GR": "el", + "ele-Latn-PG": "ele", + "elk-Latn-PG": "elk", + "elm-Latn-NG": "elm", + "elo-Latn-KE": "elo", + "elu-Latn-PG": "elu", + "ema-Latn-ZZ": "ema", + "emb-Latn-ID": "emb", + "eme-Latn-GF": "eme", + "emg-Deva-NP": "emg", + "emi-Latn-ZZ": "emi", + "emm-Latn-MX": "emm", + "emn-Latn-CM": "emn", + "emp-Latn-PA": "emp", + "ems-Cyrl-US": "ems-Cyrl", + "ems-Latn-US": "ems", + "emu-Deva-IN": "emu", + "emw-Latn-ID": "emw", + "emx-Latn-FR": "emx", + "emz-Latn-CM": "emz", + "en-Latn-AU": "en-AU", + "en-Latn-DG": "en-DG", + "en-Latn-ET": "en-ET", + "en-Latn-GB": "en-GB", + "en-Latn-GU": "en-GU", + "en-Latn-IE": "en-IE", + "en-Latn-NG": "en-NG", + "en-Latn-PG": "en-PG", + "en-Latn-US": "en", + "en-Latn-ZA": "en-ZA", + "en-Shaw-GB": "en-Shaw", + "ena-Latn-PG": "ena", + "enb-Latn-KE": "enb", + "enc-Latn-VN": "enc", + "end-Latn-ID": "end", + "enf-Cyrl-RU": "enf", + "enh-Cyrl-RU": "enh", + "enl-Latn-PY": "enl", + "enm-Latn-GB": "enm", + "enn-Latn-ZZ": "enn", + "eno-Latn-ID": "eno", + "enq-Latn-ZZ": "enq", + "enr-Latn-ID": "enr", + "env-Latn-NG": "env", + "enw-Latn-NG": "enw", + "enx-Latn-PY": "enx", + "eo-Latn-001": "eo", + "eot-Latn-CI": "eot", + "epi-Latn-NG": "epi", + "era-Taml-IN": "era", + "erg-Latn-VU": "erg", + "erh-Latn-NG": "erh", + "eri-Latn-ZZ": "eri", + "erk-Latn-VU": "erk", + "err-Latn-AU": "err", + "ert-Latn-ID": "ert", + "erw-Latn-ID": "erw", + "es-Latn-419": "es-419", + "es-Latn-AR": "es-AR", + "es-Latn-BO": "es-BO", + "es-Latn-CL": "es-CL", + "es-Latn-CO": "es-CO", + "es-Latn-CR": "es-CR", + "es-Latn-CU": "es-CU", + "es-Latn-DO": "es-DO", + "es-Latn-EA": "es-EA", + "es-Latn-EC": "es-EC", + "es-Latn-ES": "es", + "es-Latn-GQ": "es-GQ", + "es-Latn-GT": "es-GT", + "es-Latn-HN": "es-HN", + "es-Latn-IC": "es-IC", + "es-Latn-MX": "es-MX", + "es-Latn-NI": "es-NI", + "es-Latn-PA": "es-PA", + "es-Latn-PE": "es-PE", + "es-Latn-PR": "es-PR", + "es-Latn-SV": "es-SV", + "es-Latn-UY": "es-UY", + "es-Latn-VE": "es-VE", + "ese-Latn-BO": "ese", + "esg-Gonm-IN": "esg", + "esh-Arab-IR": "esh", + "esi-Latn-US": "esi", + "esm-Latn-CI": "esm", + "ess-Cyrl-US": "ess-Cyrl", + "ess-Latn-US": "ess", + "esu-Latn-US": "esu", + "esy-Latn-PH": "esy", + "et-Latn-EE": "et", + "etb-Latn-NG": "etb", + "etn-Latn-VU": "etn", + "eto-Latn-CM": "eto", + "etr-Latn-ZZ": "etr", + "ets-Latn-NG": "ets", + "ett-Ital-IT": "ett", + "etu-Latn-ZZ": "etu", + "etx-Latn-ZZ": "etx", + "etz-Latn-ID": "etz", + "eu-Latn-ES": "eu", + "eve-Cyrl-RU": "eve", + "evh-Latn-NG": "evh", + "evn-Cyrl-RU": "evn", + "evn-Latn-CN": "evn-Latn", + "evn-Mong-CN": "evn-Mong", + "ewo-Latn-CM": "ewo", + "ext-Latn-ES": "ext", + "eya-Latn-US": "eya", + "eyo-Latn-KE": "eyo", + "eza-Latn-ZZ": "eza", + "eze-Latn-NG": "eze", + "fa-Arab-AF": "fa-AF", + "fa-Arab-IR": "fa", + "fa-Arab-TJ": "fa-TJ", + "faa-Latn-ZZ": "faa", + "fab-Latn-ZZ": "fab", + "fad-Latn-PG": "fad", + "faf-Latn-SB": "faf", + "fag-Latn-ZZ": "fag", + "fah-Latn-NG": "fah", + "fai-Latn-ZZ": "fai", + "faj-Latn-PG": "faj", + "fak-Latn-CM": "fak", + "fal-Latn-CM": "fal", + "fam-Latn-NG": "fam", + "fan-Latn-GQ": "fan", + "fap-Latn-SN": "fap", + "far-Latn-SB": "far", + "fau-Latn-ID": "fau", + "fax-Latn-ES": "fax", + "fay-Arab-IR": "fay", + "faz-Arab-IR": "faz", + "fbl-Latn-PH": "fbl", + "fer-Latn-SS": "fer", + "ff-Adlm-GN": "ff-Adlm", + "ff-Latn-SN": "ff", + "ffi-Latn-ZZ": "ffi", + "ffm-Latn-ML": "ffm", + "fgr-Latn-TD": "fgr", + "fi-Latn-FI": "fi", + "fia-Arab-SD": "fia", + "fie-Latn-NG": "fie", + "fif-Latn-SA": "fif", + "fil-Latn-PH": "fil", + "fil-Tglg-PH": "fil-Tglg", + "fip-Latn-TZ": "fip", + "fir-Latn-NG": "fir", + "fit-Latn-SE": "fit", + "fiw-Latn-PG": "fiw", + "fj-Latn-FJ": "fj", + "fkk-Latn-NG": "fkk", + "fkv-Latn-NO": "fkv", + "fla-Latn-US": "fla", + "flh-Latn-ID": "flh", + "fli-Latn-NG": "fli", + "fll-Latn-CM": "fll", + "fln-Latn-AU": "fln", + "flr-Latn-ZZ": "flr", + "fly-Latn-ZA": "fly", + "fmp-Latn-ZZ": "fmp", + "fmu-Deva-IN": "fmu", + "fnb-Latn-VU": "fnb", + "fng-Latn-ZA": "fng", + "fni-Latn-TD": "fni", + "fo-Latn-FO": "fo", + "fod-Latn-ZZ": "fod", + "foi-Latn-PG": "foi", + "fom-Latn-CD": "fom", + "fon-Latn-BJ": "fon", + "for-Latn-ZZ": "for", + "fos-Latn-TW": "fos", + "fpe-Latn-ZZ": "fpe", + "fqs-Latn-ZZ": "fqs", + "fr-Brai-FR": "fr-Brai", + "fr-Dupl-FR": "fr-Dupl", + "fr-Latn-BF": "fr-BF", + "fr-Latn-BJ": "fr-BJ", + "fr-Latn-BL": "fr-BL", + "fr-Latn-CF": "fr-CF", + "fr-Latn-CG": "fr-CG", + "fr-Latn-CI": "fr-CI", + "fr-Latn-CM": "fr-CM", + "fr-Latn-DZ": "fr-DZ", + "fr-Latn-FR": "fr", + "fr-Latn-GA": "fr-GA", + "fr-Latn-GF": "fr-GF", + "fr-Latn-GN": "fr-GN", + "fr-Latn-GP": "fr-GP", + "fr-Latn-KM": "fr-KM", + "fr-Latn-LU": "fr-LU", + "fr-Latn-MA": "fr-MA", + "fr-Latn-MC": "fr-MC", + "fr-Latn-MF": "fr-MF", + "fr-Latn-MQ": "fr-MQ", + "fr-Latn-MR": "fr-MR", + "fr-Latn-NC": "fr-NC", + "fr-Latn-PF": "fr-PF", + "fr-Latn-PM": "fr-PM", + "fr-Latn-RE": "fr-RE", + "fr-Latn-SC": "fr-SC", + "fr-Latn-SN": "fr-SN", + "fr-Latn-SY": "fr-SY", + "fr-Latn-TD": "fr-TD", + "fr-Latn-TF": "fr-TF", + "fr-Latn-TG": "fr-TG", + "fr-Latn-TN": "fr-TN", + "fr-Latn-WF": "fr-WF", + "fr-Latn-YT": "fr-YT", + "frc-Latn-US": "frc", + "frd-Latn-ID": "frd", + "frk-Latn-DE": "frk", + "frm-Latn-FR": "frm", + "fro-Latn-FR": "fro", + "frp-Latn-FR": "frp", + "frq-Latn-PG": "frq", + "frr-Latn-DE": "frr", + "frs-Latn-DE": "frs", + "frt-Latn-VU": "frt", + "fub-Arab-CM": "fub", + "fud-Latn-WF": "fud", + "fue-Latn-ZZ": "fue", + "fuf-Latn-GN": "fuf", + "fuh-Latn-ZZ": "fuh", + "fui-Latn-TD": "fui", + "fum-Latn-NG": "fum", + "fun-Latn-BR": "fun", + "fuq-Latn-NE": "fuq", + "fur-Latn-IT": "fur", + "fut-Latn-VU": "fut", + "fuu-Latn-CD": "fuu", + "fuv-Latn-NG": "fuv", + "fuy-Latn-ZZ": "fuy", + "fvr-Latn-SD": "fvr", + "fwa-Latn-NC": "fwa", + "fwe-Latn-NA": "fwe", + "fy-Latn-NL": "fy", + "ga-Latn-IE": "ga", + "gaa-Latn-GH": "gaa", + "gab-Latn-TD": "gab", + "gac-Deva-IN": "gac-Deva", + "gac-Latn-IN": "gac", + "gad-Latn-PH": "gad", + "gae-Latn-VE": "gae", + "gaf-Latn-ZZ": "gaf", + "gag-Latn-MD": "gag", + "gah-Latn-ZZ": "gah", + "gai-Latn-PG": "gai", + "gaj-Latn-ZZ": "gaj", + "gak-Latn-ID": "gak", + "gal-Latn-TL": "gal", + "gam-Latn-ZZ": "gam", + "gan-Hans-CN": "gan", + "gao-Latn-PG": "gao", + "gap-Latn-PG": "gap", + "gaq-Orya-IN": "gaq", + "gar-Latn-PG": "gar", + "gas-Gujr-IN": "gas", + "gat-Latn-PG": "gat", + "gau-Telu-IN": "gau", + "gaw-Latn-ZZ": "gaw", + "gax-Ethi-ET": "gax-Ethi", + "gax-Latn-ET": "gax", + "gay-Latn-ID": "gay", + "gba-Latn-ZZ": "gba", + "gbb-Latn-AU": "gbb", + "gbd-Latn-AU": "gbd", + "gbe-Latn-PG": "gbe", + "gbf-Latn-ZZ": "gbf", + "gbg-Latn-CF": "gbg", + "gbh-Latn-BJ": "gbh", + "gbi-Latn-ID": "gbi", + "gbj-Orya-IN": "gbj", + "gbk-Deva-IN": "gbk", + "gbk-Takr-IN": "gbk-Takr", + "gbl-Deva-IN": "gbl-Deva", + "gbl-Gujr-IN": "gbl", + "gbm-Deva-IN": "gbm", + "gbn-Latn-SS": "gbn", + "gbp-Latn-CF": "gbp", + "gbq-Latn-CF": "gbq", + "gbr-Latn-NG": "gbr", + "gbs-Latn-BJ": "gbs", + "gbu-Latn-AU": "gbu", + "gbv-Latn-CF": "gbv", + "gbw-Latn-AU": "gbw", + "gbx-Latn-BJ": "gbx", + "gby-Latn-ZZ": "gby", + "gbz-Arab-IR": "gbz", + "gcc-Latn-PG": "gcc", + "gcd-Latn-AU": "gcd", + "gcf-Latn-GP": "gcf", + "gcl-Latn-GD": "gcl", + "gcn-Latn-PG": "gcn", + "gcr-Latn-GF": "gcr", + "gct-Latn-VE": "gct", + "gd-Latn-GB": "gd", + "gdb-Orya-IN": "gdb", + "gdb-Telu-IN": "gdb-Telu", + "gdc-Latn-AU": "gdc", + "gdd-Latn-PG": "gdd", + "gde-Latn-ZZ": "gde", + "gdf-Latn-NG": "gdf", + "gdg-Latn-PH": "gdg", + "gdh-Latn-AU": "gdh", + "gdi-Latn-CF": "gdi", + "gdj-Latn-AU": "gdj", + "gdk-Latn-TD": "gdk", + "gdl-Ethi-ET": "gdl-Ethi", + "gdl-Latn-ET": "gdl", + "gdm-Latn-TD": "gdm", + "gdn-Latn-ZZ": "gdn", + "gdo-Cyrl-RU": "gdo", + "gdq-Latn-YE": "gdq", + "gdr-Latn-ZZ": "gdr", + "gdt-Latn-AU": "gdt", + "gdu-Latn-NG": "gdu", + "gdx-Deva-IN": "gdx", + "gea-Latn-NG": "gea", + "geb-Latn-ZZ": "geb", + "gec-Latn-LR": "gec", + "ged-Latn-NG": "ged", + "gef-Latn-ID": "gef", + "geg-Latn-NG": "geg", + "geh-Latn-CA": "geh", + "gei-Latn-ID": "gei", + "gej-Latn-ZZ": "gej", + "gek-Latn-NG": "gek", + "gel-Latn-ZZ": "gel", + "geq-Latn-CF": "geq", + "ges-Latn-ID": "ges", + "gev-Latn-GA": "gev", + "gew-Latn-NG": "gew", + "gex-Latn-SO": "gex", + "gey-Latn-CD": "gey", + "gez-Ethi-ET": "gez", + "gfk-Latn-ZZ": "gfk", + "gga-Latn-SB": "gga", + "ggb-Latn-LR": "ggb", + "ggd-Latn-AU": "ggd", + "gge-Latn-AU": "gge", + "ggg-Arab-PK": "ggg", + "ggk-Latn-AU": "ggk", + "ggl-Latn-PG": "ggl", + "ggt-Latn-PG": "ggt", + "ggu-Latn-CI": "ggu", + "ggw-Latn-PG": "ggw", + "gha-Arab-LY": "gha", + "gha-Latn-LY": "gha-Latn", + "gha-Tfng-LY": "gha-Tfng", + "ghc-Latn-GB": "ghc", + "ghe-Deva-NP": "ghe", + "ghk-Latn-MM": "ghk", + "ghn-Latn-SB": "ghn", + "ghr-Arab-PK": "ghr", + "ghs-Latn-ZZ": "ghs", + "ght-Tibt-NP": "ght", + "gia-Latn-AU": "gia", + "gib-Latn-NG": "gib", + "gic-Latn-ZA": "gic", + "gid-Latn-CM": "gid", + "gie-Latn-CI": "gie", + "gig-Arab-PK": "gig", + "gih-Latn-AU": "gih", + "gil-Latn-KI": "gil", + "gim-Latn-ZZ": "gim", + "gin-Cyrl-RU": "gin", + "gip-Latn-PG": "gip", + "giq-Latn-VN": "giq", + "gir-Latn-VN": "gir", + "gis-Latn-CM": "gis", + "git-Latn-CA": "git", + "gix-Latn-CD": "gix", + "giy-Latn-AU": "giy", + "giz-Latn-CM": "giz", + "gjk-Arab-PK": "gjk", + "gjm-Latn-AU": "gjm", + "gjn-Latn-ZZ": "gjn", + "gjr-Latn-AU": "gjr", + "gju-Arab-PK": "gju", + "gka-Latn-PG": "gka", + "gkd-Latn-PG": "gkd", + "gke-Latn-CM": "gke", + "gkn-Latn-ZZ": "gkn", + "gko-Latn-AU": "gko", + "gkp-Latn-ZZ": "gkp", + "gku-Latn-ZA": "gku", + "gl-Latn-ES": "gl", + "glb-Latn-NG": "glb", + "glc-Latn-TD": "glc", + "gld-Cyrl-RU": "gld", + "glh-Arab-AF": "glh", + "glj-Latn-TD": "glj", + "glk-Arab-IR": "glk", + "gll-Latn-AU": "gll", + "glo-Latn-NG": "glo", + "glr-Latn-LR": "glr", + "glu-Latn-TD": "glu", + "glw-Latn-NG": "glw", + "gma-Latn-AU": "gma", + "gmb-Latn-SB": "gmb", + "gmd-Latn-NG": "gmd", + "gmg-Latn-PG": "gmg", + "gmh-Latn-DE": "gmh", + "gmm-Latn-ZZ": "gmm", + "gmn-Latn-CM": "gmn", + "gmr-Latn-AU": "gmr", + "gmu-Latn-PG": "gmu", + "gmv-Ethi-ZZ": "gmv", + "gmx-Latn-TZ": "gmx", + "gmy-Linb-GR": "gmy", + "gmz-Latn-NG": "gmz", + "gn-Latn-PY": "gn", + "gna-Latn-BF": "gna", + "gnb-Latn-IN": "gnb", + "gnc-Latn-ES": "gnc", + "gnd-Latn-ZZ": "gnd", + "gne-Latn-NG": "gne", + "gng-Latn-ZZ": "gng", + "gnh-Latn-NG": "gnh", + "gni-Latn-AU": "gni", + "gnj-Latn-CI": "gnj", + "gnk-Latn-BW": "gnk", + "gnl-Latn-AU": "gnl", + "gnm-Latn-PG": "gnm", + "gnn-Latn-AU": "gnn", + "gnq-Latn-MY": "gnq", + "gnr-Latn-AU": "gnr", + "gnt-Latn-PG": "gnt", + "gnu-Latn-PG": "gnu", + "gnw-Latn-BO": "gnw", + "gnz-Latn-CF": "gnz", + "goa-Latn-CI": "goa", + "gob-Latn-CO": "gob", + "goc-Latn-PG": "goc", + "god-Latn-ZZ": "god", + "goe-Tibt-BT": "goe", + "gof-Ethi-ZZ": "gof", + "gog-Latn-TZ": "gog", + "goh-Latn-DE": "goh", + "goi-Latn-ZZ": "goi", + "gok-Deva-IN": "gok", + "gol-Latn-LR": "gol", + "gom-Deva-IN": "gom", + "gon-Telu-IN": "gon", + "goo-Latn-FJ": "goo", + "gop-Latn-ID": "gop", + "goq-Latn-ID": "goq", + "gor-Latn-ID": "gor", + "gos-Latn-NL": "gos", + "got-Goth-UA": "got", + "gou-Latn-CM": "gou", + "gov-Latn-CI": "gov", + "gow-Latn-TZ": "gow", + "gox-Latn-CD": "gox", + "goy-Latn-TD": "goy", + "gpa-Latn-NG": "gpa", + "gpe-Latn-GH": "gpe", + "gpn-Latn-PG": "gpn", + "gqa-Latn-NG": "gqa", + "gqn-Latn-BR": "gqn", + "gqr-Latn-TD": "gqr", + "gra-Deva-IN": "gra", + "gra-Gujr-IN": "gra-Gujr", + "grb-Latn-ZZ": "grb", + "grc-Cprt-CY": "grc", + "grc-Linb-GR": "grc-Linb", + "grd-Latn-NG": "grd", + "grg-Latn-PG": "grg", + "grh-Latn-NG": "grh", + "gri-Latn-SB": "gri", + "grj-Latn-LR": "grj", + "grm-Latn-MY": "grm", + "grq-Latn-PG": "grq", + "grs-Latn-ID": "grs", + "grt-Beng-IN": "grt", + "gru-Ethi-ET": "gru", + "gru-Latn-ET": "gru-Latn", + "grv-Latn-LR": "grv", + "grw-Latn-ZZ": "grw", + "grx-Latn-PG": "grx", + "gry-Latn-LR": "gry", + "grz-Latn-PG": "grz", + "gsl-Latn-SN": "gsl", + "gsn-Latn-PG": "gsn", + "gso-Latn-CF": "gso", + "gsp-Latn-PG": "gsp", + "gsw-Latn-CH": "gsw", + "gta-Latn-BR": "gta", + "gtu-Latn-AU": "gtu", + "gu-Gujr-IN": "gu", + "gua-Latn-NG": "gua", + "gub-Latn-BR": "gub", + "guc-Latn-CO": "guc", + "gud-Latn-ZZ": "gud", + "gue-Latn-AU": "gue", + "guf-Latn-AU": "guf", + "guh-Latn-CO": "guh", + "gui-Latn-BO": "gui", + "guk-Ethi-ET": "guk-Ethi", + "guk-Latn-ET": "guk", + "gul-Latn-US": "gul", + "gum-Latn-CO": "gum", + "gun-Latn-BR": "gun", + "guo-Latn-CO": "guo", + "gup-Latn-AU": "gup", + "guq-Latn-PY": "guq", + "gur-Latn-GH": "gur", + "gut-Latn-CR": "gut", + "guu-Latn-VE": "guu", + "guw-Latn-ZZ": "guw", + "gux-Latn-ZZ": "gux", + "guz-Latn-KE": "guz", + "gv-Latn-IM": "gv", + "gva-Latn-PY": "gva", + "gvc-Latn-BR": "gvc", + "gve-Latn-PG": "gve", + "gvf-Latn-ZZ": "gvf", + "gvj-Latn-BR": "gvj", + "gvl-Latn-TD": "gvl", + "gvm-Latn-NG": "gvm", + "gvn-Latn-AU": "gvn", + "gvo-Latn-BR": "gvo", + "gvp-Latn-BR": "gvp", + "gvr-Deva-NP": "gvr", + "gvs-Latn-ZZ": "gvs", + "gvy-Latn-AU": "gvy", + "gwa-Latn-CI": "gwa", + "gwb-Latn-NG": "gwb", + "gwc-Arab-ZZ": "gwc", + "gwd-Latn-ET": "gwd", + "gwe-Latn-TZ": "gwe", + "gwf-Arab-PK": "gwf", + "gwg-Latn-NG": "gwg", + "gwi-Latn-CA": "gwi", + "gwj-Latn-BW": "gwj", + "gwm-Latn-AU": "gwm", + "gwn-Latn-NG": "gwn", + "gwr-Latn-UG": "gwr", + "gwt-Arab-ZZ": "gwt", + "gwu-Latn-AU": "gwu", + "gww-Latn-AU": "gww", + "gwx-Latn-GH": "gwx", + "gxx-Latn-CI": "gxx", + "gyb-Latn-PG": "gyb", + "gyd-Latn-AU": "gyd", + "gye-Latn-NG": "gye", + "gyf-Latn-AU": "gyf", + "gyg-Latn-CF": "gyg", + "gyi-Latn-ZZ": "gyi", + "gyl-Ethi-ET": "gyl-Ethi", + "gyl-Latn-ET": "gyl", + "gym-Latn-PA": "gym", + "gyn-Latn-GY": "gyn", + "gyo-Deva-NP": "gyo", + "gyr-Latn-BO": "gyr", + "gyy-Latn-AU": "gyy", + "gyz-Latn-NG": "gyz", + "gza-Latn-SD": "gza", + "gzi-Arab-IR": "gzi", + "gzn-Latn-ID": "gzn", + "ha-Arab-CM": "ha-CM", + "ha-Arab-NG": "ha-Arab", + "ha-Arab-SD": "ha-SD", + "ha-Latn-NE": "ha-NE", + "ha-Latn-NG": "ha", + "haa-Latn-US": "haa", + "hac-Arab-IR": "hac", + "had-Latn-ID": "had", + "hae-Latn-ET": "hae", + "hag-Latn-ZZ": "hag", + "hah-Latn-PG": "hah", + "hai-Latn-CA": "hai", + "haj-Beng-IN": "haj-Beng", + "haj-Latn-IN": "haj", + "hak-Hans-CN": "hak", + "hal-Latn-VN": "hal", + "ham-Latn-ZZ": "ham", + "han-Latn-TZ": "han", + "hao-Latn-PG": "hao", + "hap-Latn-ID": "hap", + "haq-Latn-TZ": "haq", + "har-Arab-ET": "har-Arab", + "har-Ethi-ET": "har", + "har-Latn-ET": "har-Latn", + "has-Latn-CA": "has", + "hav-Latn-CD": "hav", + "haw-Latn-US": "haw", + "hax-Latn-CA": "hax", + "hay-Latn-TZ": "hay", + "haz-Arab-AF": "haz", + "hba-Latn-CD": "hba", + "hbb-Latn-ZZ": "hbb", + "hbn-Latn-SD": "hbn", + "hbo-Hebr-IL": "hbo", + "hbu-Latn-TL": "hbu", + "hch-Latn-MX": "hch", + "hdy-Ethi-ZZ": "hdy", + "he-Hebr-IL": "he", + "hed-Latn-TD": "hed", + "heg-Latn-ID": "heg", + "heh-Latn-TZ": "heh", + "hei-Latn-CA": "hei", + "hem-Latn-CD": "hem", + "hgm-Latn-NA": "hgm", + "hgw-Latn-PG": "hgw", + "hhi-Latn-PG": "hhi", + "hhr-Latn-SN": "hhr", + "hhy-Latn-ZZ": "hhy", + "hi-Deva-IN": "hi", + "hi-Latn-IN": "hi-Latn", + "hi-Mahj-IN": "hi-Mahj", + "hia-Latn-ZZ": "hia", + "hib-Latn-PE": "hib", + "hid-Latn-US": "hid", + "hif-Deva-FJ": "hif-Deva", + "hif-Latn-FJ": "hif", + "hig-Latn-ZZ": "hig", + "hih-Latn-ZZ": "hih", + "hii-Deva-IN": "hii-Deva", + "hii-Takr-IN": "hii", + "hij-Latn-CM": "hij", + "hik-Latn-ID": "hik", + "hil-Latn-PH": "hil", + "hio-Latn-BW": "hio", + "hir-Latn-BR": "hir", + "hit-Xsux-TR": "hit", + "hiw-Latn-VU": "hiw", + "hix-Latn-BR": "hix", + "hji-Latn-ID": "hji", + "hka-Latn-TZ": "hka", + "hke-Latn-CD": "hke", + "hkh-Arab-IN": "hkh", + "hkh-Deva-IN": "hkh-Deva", + "hkh-Latn-IN": "hkh-Latn", + "hkk-Latn-PG": "hkk", + "hla-Latn-ZZ": "hla", + "hlb-Deva-IN": "hlb", + "hld-Latn-VN": "hld", + "hlt-Latn-MM": "hlt", + "hlu-Hluw-TR": "hlu", + "hma-Latn-CN": "hma", + "hmb-Latn-ML": "hmb", + "hmd-Plrd-CN": "hmd", + "hmf-Latn-VN": "hmf", + "hmj-Bopo-CN": "hmj", + "hmm-Latn-CN": "hmm", + "hmn-Bopo-CN": "hmn-Bopo", + "hmn-Hmng-CN": "hmn-Hmng", + "hmn-Latn-CN": "hmn", + "hmp-Latn-CN": "hmp", + "hmq-Bopo-CN": "hmq", + "hmr-Latn-IN": "hmr", + "hms-Latn-CN": "hms", + "hmt-Latn-ZZ": "hmt", + "hmu-Latn-ID": "hmu", + "hmv-Latn-VN": "hmv", + "hmw-Latn-CN": "hmw", + "hmy-Latn-CN": "hmy", + "hmz-Latn-CN": "hmz", + "hmz-Plrd-CN": "hmz-Plrd", + "hna-Latn-CM": "hna", + "hnd-Arab-PK": "hnd", + "hne-Deva-IN": "hne", + "hng-Latn-AO": "hng", + "hnh-Latn-BW": "hnh", + "hni-Latn-CN": "hni", + "hnj-Hmng-LA": "hnj-Hmng-LA", + "hnj-Hmnp-US": "hnj", + "hnj-Laoo-AU": "hnj-AU", + "hnj-Laoo-CN": "hnj-CN", + "hnj-Laoo-FR": "hnj-FR", + "hnj-Laoo-GF": "hnj-GF", + "hnj-Laoo-LA": "hnj-LA", + "hnj-Laoo-MM": "hnj-MM", + "hnj-Laoo-SR": "hnj-SR", + "hnj-Laoo-TH": "hnj-TH", + "hnj-Laoo-US": "hnj-Laoo-US", + "hnj-Laoo-VN": "hnj-VN", + "hnn-Hano-PH": "hnn-Hano", + "hnn-Latn-PH": "hnn", + "hno-Arab-PK": "hno", + "hns-Latn-SR": "hns", + "ho-Latn-PG": "ho", + "hoa-Latn-SB": "hoa", + "hob-Latn-PG": "hob", + "hoc-Deva-IN": "hoc", + "hoc-Wara-IN": "hoc-Wara", + "hod-Latn-NG": "hod", + "hoe-Latn-NG": "hoe", + "hoh-Arab-OM": "hoh", + "hoi-Latn-US": "hoi", + "hoj-Deva-IN": "hoj", + "hol-Latn-AO": "hol", + "hom-Latn-SS": "hom", + "hoo-Latn-CD": "hoo", + "hop-Latn-US": "hop", + "hor-Latn-TD": "hor", + "hot-Latn-ZZ": "hot", + "hov-Latn-ID": "hov", + "how-Hani-CN": "how", + "hoy-Deva-IN": "hoy", + "hpo-Mymr-MM": "hpo", + "hr-Latn-HR": "hr", + "hra-Latn-IN": "hra", + "hrc-Latn-PG": "hrc", + "hre-Latn-VN": "hre", + "hrk-Latn-ID": "hrk", + "hrm-Hmng-CN": "hrm-Hmng", + "hrm-Latn-CN": "hrm", + "hro-Latn-VN": "hro", + "hrp-Latn-AU": "hrp", + "hrt-Syrc-TR": "hrt", + "hru-Latn-IN": "hru", + "hrw-Latn-PG": "hrw", + "hrx-Latn-BR": "hrx", + "hrz-Arab-IR": "hrz", + "hsb-Latn-DE": "hsb", + "hsn-Hans-CN": "hsn", + "hss-Arab-OM": "hss", + "ht-Latn-HT": "ht", + "hti-Latn-ID": "hti", + "hto-Latn-CO": "hto", + "hts-Latn-TZ": "hts", + "htu-Latn-ID": "htu", + "htx-Xsux-TR": "htx", + "hu-Hung-HU": "hu-Hung", + "hu-Latn-HU": "hu", + "hub-Latn-PE": "hub", + "huc-Latn-BW": "huc", + "hud-Latn-ID": "hud", + "hue-Latn-MX": "hue", + "huf-Latn-PG": "huf", + "hug-Latn-PE": "hug", + "huh-Latn-CL": "huh", + "hui-Latn-ZZ": "hui", + "huk-Latn-ID": "huk", + "hul-Latn-PG": "hul", + "hum-Latn-CD": "hum", + "hup-Latn-US": "hup", + "hur-Latn-CA": "hur", + "hus-Latn-MX": "hus", + "hut-Deva-NP": "hut", + "hut-Tibt-NP": "hut-Tibt", + "huu-Latn-PE": "huu", + "huv-Latn-MX": "huv", + "huw-Latn-ID": "huw", + "hux-Latn-PE": "hux", + "huy-Hebr-IL": "huy", + "huz-Cyrl-RU": "huz", + "hvc-Latn-HT": "hvc", + "hve-Latn-MX": "hve", + "hvk-Latn-NC": "hvk", + "hvn-Latn-ID": "hvn", + "hvv-Latn-MX": "hvv", + "hwa-Latn-CI": "hwa", + "hwc-Latn-US": "hwc", + "hwo-Latn-NG": "hwo", + "hy-Armn-AM": "hy", + "hya-Latn-CM": "hya", + "hyw-Armn-AM": "hyw", + "hz-Latn-NA": "hz", + "ia-Latn-001": "ia", + "iai-Latn-NC": "iai", + "ian-Latn-ZZ": "ian", + "iar-Latn-ZZ": "iar", + "iba-Latn-MY": "iba", + "ibb-Latn-NG": "ibb", + "ibd-Latn-AU": "ibd", + "ibe-Latn-NG": "ibe", + "ibg-Latn-PH": "ibg", + "ibh-Latn-VN": "ibh", + "ibl-Latn-PH": "ibl", + "ibm-Latn-NG": "ibm", + "ibn-Latn-NG": "ibn", + "ibr-Latn-NG": "ibr", + "ibu-Latn-ID": "ibu", + "iby-Latn-ZZ": "iby", + "ica-Latn-ZZ": "ica", + "ich-Latn-ZZ": "ich", + "icr-Latn-CO": "icr", + "id-Latn-ID": "id", + "ida-Latn-KE": "ida", + "idb-Latn-IN": "idb", + "idc-Latn-NG": "idc", + "idd-Latn-ZZ": "idd", + "ide-Latn-NG": "ide", + "idi-Latn-ZZ": "idi", + "idr-Latn-SS": "idr", + "ids-Latn-NG": "ids", + "idt-Latn-TL": "idt", + "idu-Latn-ZZ": "idu", + "ie-Latn-001": "ie", + "ifa-Latn-PH": "ifa", + "ifb-Latn-PH": "ifb", + "ife-Latn-TG": "ife", + "iff-Latn-VU": "iff", + "ifk-Latn-PH": "ifk", + "ifm-Latn-CG": "ifm", + "ifu-Latn-PH": "ifu", + "ify-Latn-PH": "ify", + "ig-Latn-NG": "ig", + "igb-Latn-ZZ": "igb", + "ige-Latn-ZZ": "ige", + "igg-Latn-PG": "igg", + "igl-Latn-NG": "igl", + "igm-Latn-PG": "igm", + "ign-Latn-BO": "ign", + "igo-Latn-PG": "igo", + "igs-Grek-001": "igs-Grek", + "igs-Latn-001": "igs", + "igw-Latn-NG": "igw", + "ihb-Latn-ID": "ihb", + "ihi-Latn-NG": "ihi", + "ihp-Latn-ID": "ihp", + "ihw-Latn-AU": "ihw", + "ii-Yiii-CN": "ii", + "iin-Latn-AU": "iin", + "ijc-Latn-NG": "ijc", + "ije-Latn-NG": "ije", + "ijj-Latn-ZZ": "ijj", + "ijn-Latn-NG": "ijn", + "ijs-Latn-NG": "ijs", + "ik-Latn-US": "ik", + "iki-Latn-NG": "iki", + "ikk-Latn-ZZ": "ikk", + "ikl-Latn-NG": "ikl", + "iko-Latn-NG": "iko", + "ikp-Latn-NG": "ikp", + "ikr-Latn-AU": "ikr", + "ikt-Cans-CA": "ikt-Cans", + "ikt-Latn-CA": "ikt", + "ikv-Latn-NG": "ikv", + "ikw-Latn-ZZ": "ikw", + "ikx-Latn-ZZ": "ikx", + "ikz-Latn-TZ": "ikz", + "ila-Latn-ID": "ila", + "ilb-Latn-ZM": "ilb", + "ilg-Latn-AU": "ilg", + "ili-Arab-CN": "ili-Arab", + "ili-Cyrl-KZ": "ili-Cyrl", + "ili-Latn-CN": "ili", + "ilk-Latn-PH": "ilk", + "ilm-Latn-MY": "ilm", + "ilo-Latn-PH": "ilo", + "ilp-Latn-PH": "ilp", + "ilu-Latn-ID": "ilu", + "ilv-Latn-NG": "ilv", + "imi-Latn-PG": "imi", + "iml-Latn-US": "iml", + "imn-Latn-PG": "imn", + "imo-Latn-ZZ": "imo", + "imr-Latn-ID": "imr", + "ims-Latn-IT": "ims", + "imt-Latn-SS": "imt", + "imy-Lyci-TR": "imy", + "inb-Latn-CO": "inb", + "ing-Latn-US": "ing", + "inh-Cyrl-RU": "inh", + "inj-Latn-CO": "inj", + "inn-Latn-PH": "inn", + "ino-Latn-PG": "ino", + "inp-Latn-PE": "inp", + "int-Mymr-MM": "int", + "io-Latn-001": "io", + "ior-Ethi-ET": "ior", + "iou-Latn-ZZ": "iou", + "iow-Latn-US": "iow", + "ipi-Latn-PG": "ipi", + "ipo-Latn-PG": "ipo", + "iqu-Latn-PE": "iqu", + "iqw-Latn-NG": "iqw", + "ire-Latn-ID": "ire", + "irh-Latn-ID": "irh", + "iri-Latn-ZZ": "iri", + "irk-Latn-TZ": "irk", + "irn-Latn-BR": "irn", + "iru-Mlym-IN": "iru-Mlym", + "iru-Taml-IN": "iru", + "irx-Latn-ID": "irx", + "iry-Latn-PH": "iry", + "is-Latn-IS": "is", + "isa-Latn-PG": "isa", + "isc-Latn-PE": "isc", + "isd-Latn-PH": "isd", + "ish-Latn-NG": "ish", + "isi-Latn-NG": "isi", + "isk-Arab-AF": "isk", + "isk-Cyrl-TJ": "isk-Cyrl", + "ism-Latn-ID": "ism", + "isn-Latn-TZ": "isn", + "iso-Latn-NG": "iso", + "ist-Latn-HR": "ist", + "isu-Latn-CM": "isu", + "it-Latn-IT": "it", + "it-Latn-SM": "it-SM", + "it-Latn-VA": "it-VA", + "itb-Latn-PH": "itb", + "itd-Latn-ID": "itd", + "ite-Latn-BO": "ite", + "iti-Latn-PH": "iti", + "itk-Hebr-IT": "itk", + "itl-Cyrl-RU": "itl", + "itm-Latn-NG": "itm", + "ito-Latn-BO": "ito", + "itr-Latn-PG": "itr", + "its-Latn-NG": "its", + "itt-Latn-PH": "itt", + "itv-Latn-PH": "itv", + "itw-Latn-NG": "itw", + "itx-Latn-ID": "itx", + "ity-Latn-PH": "ity", + "itz-Latn-GT": "itz", + "iu-Cans-CA": "iu", + "ium-Hani-CN": "ium-Hani", + "ium-Laoo-LA": "ium-Laoo", + "ium-Latn-CN": "ium", + "ium-Thai-TH": "ium-Thai", + "ivb-Latn-PH": "ivb", + "ivv-Latn-PH": "ivv", + "iwk-Latn-PH": "iwk", + "iwm-Latn-ZZ": "iwm", + "iwo-Latn-ID": "iwo", + "iws-Latn-ZZ": "iws", + "ixc-Latn-MX": "ixc", + "ixl-Latn-GT": "ixl", + "iya-Latn-NG": "iya", + "iyo-Latn-CM": "iyo", + "iyx-Latn-CG": "iyx", + "izh-Latn-RU": "izh", + "izr-Latn-NG": "izr", + "izz-Latn-NG": "izz", + "ja-Hira-JP": "ja-Hira", + "ja-Jpan-JP": "ja", + "ja-Kana-JP": "ja-Kana", + "jaa-Latn-BR": "jaa", + "jab-Latn-ZZ": "jab", + "jac-Latn-GT": "jac", + "jad-Arab-GN": "jad", + "jae-Latn-PG": "jae", + "jaf-Latn-NG": "jaf", + "jah-Latn-MY": "jah", + "jaj-Latn-SB": "jaj", + "jak-Latn-MY": "jak", + "jal-Latn-ID": "jal", + "jam-Latn-JM": "jam", + "jan-Latn-AU": "jan", + "jao-Latn-AU": "jao", + "jaq-Latn-ID": "jaq", + "jas-Latn-NC": "jas", + "jat-Arab-AF": "jat", + "jau-Latn-ID": "jau", + "jax-Latn-ID": "jax", + "jay-Latn-AU": "jay", + "jaz-Latn-NC": "jaz", + "jbe-Hebr-IL": "jbe", + "jbi-Latn-AU": "jbi", + "jbj-Latn-ID": "jbj", + "jbk-Latn-PG": "jbk", + "jbm-Latn-NG": "jbm", + "jbn-Arab-LY": "jbn", + "jbo-Latn-001": "jbo", + "jbr-Latn-ID": "jbr", + "jbt-Latn-BR": "jbt", + "jbu-Latn-ZZ": "jbu", + "jbw-Latn-AU": "jbw", + "jct-Cyrl-UA": "jct", + "jct-Latn-UA": "jct-Latn", + "jda-Tibt-IN": "jda", + "jdg-Arab-PK": "jdg", + "jdt-Cyrl-RU": "jdt", + "jdt-Hebr-RU": "jdt-Hebr", + "jdt-Latn-AZ": "jdt-Latn", + "jeb-Latn-PE": "jeb", + "jee-Deva-NP": "jee", + "jeh-Laoo-LA": "jeh-Laoo", + "jeh-Latn-VN": "jeh", + "jei-Latn-ID": "jei", + "jek-Latn-CI": "jek", + "jel-Latn-ID": "jel", + "jen-Latn-ZZ": "jen", + "jer-Latn-NG": "jer", + "jet-Latn-PG": "jet", + "jeu-Latn-TD": "jeu", + "jgb-Latn-CD": "jgb", + "jge-Geor-GE": "jge", + "jge-Hebr-IL": "jge-Hebr", + "jgk-Latn-ZZ": "jgk", + "jgo-Latn-CM": "jgo", + "jhi-Latn-MY": "jhi", + "jia-Latn-CM": "jia", + "jib-Latn-ZZ": "jib", + "jic-Latn-HN": "jic", + "jid-Latn-NG": "jid", + "jie-Latn-NG": "jie", + "jig-Latn-AU": "jig", + "jil-Latn-PG": "jil", + "jim-Latn-CM": "jim", + "jit-Latn-TZ": "jit", + "jiu-Latn-CN": "jiu", + "jiv-Latn-EC": "jiv", + "jiy-Latn-CN": "jiy", + "jje-Hang-KR": "jje", + "jjr-Latn-NG": "jjr", + "jka-Latn-ID": "jka", + "jkm-Brai-MM": "jkm-Brai", + "jkm-Latn-MM": "jkm-Latn", + "jkm-Mymr-MM": "jkm", + "jko-Latn-PG": "jko", + "jku-Latn-NG": "jku", + "jle-Latn-SD": "jle", + "jma-Latn-PG": "jma", + "jmb-Latn-NG": "jmb", + "jmc-Latn-TZ": "jmc", + "jmd-Latn-ID": "jmd", + "jmi-Latn-NG": "jmi", + "jml-Deva-NP": "jml", + "jmn-Latn-MM": "jmn", + "jmr-Latn-GH": "jmr", + "jms-Latn-NG": "jms", + "jmw-Latn-PG": "jmw", + "jmx-Latn-MX": "jmx", + "jna-Takr-IN": "jna", + "jnd-Arab-PK": "jnd", + "jng-Latn-AU": "jng", + "jni-Latn-NG": "jni", + "jnj-Ethi-ET": "jnj-Ethi", + "jnj-Latn-ET": "jnj", + "jnl-Deva-IN": "jnl", + "jns-Deva-IN": "jns", + "jns-Latn-IN": "jns-Latn", + "jns-Takr-IN": "jns-Takr", + "job-Latn-CD": "job", + "jod-Latn-CI": "jod", + "jog-Arab-PK": "jog", + "jor-Latn-BO": "jor", + "jow-Latn-ML": "jow", + "jpa-Hebr-PS": "jpa", + "jpr-Hebr-IL": "jpr", + "jqr-Latn-PE": "jqr", + "jra-Latn-ZZ": "jra", + "jrr-Latn-NG": "jrr", + "jrt-Latn-NG": "jrt", + "jru-Latn-VE": "jru", + "jua-Latn-BR": "jua", + "jub-Latn-NG": "jub", + "jud-Latn-CI": "jud", + "juh-Latn-NG": "juh", + "jui-Latn-AU": "jui", + "juk-Latn-NG": "juk", + "jul-Deva-NP": "jul", + "jum-Latn-SD": "jum", + "jun-Orya-IN": "jun", + "juo-Latn-NG": "juo", + "jup-Latn-BR": "jup", + "jur-Latn-BR": "jur", + "jut-Latn-DK": "jut", + "juu-Latn-NG": "juu", + "juw-Latn-NG": "juw", + "juy-Orya-IN": "juy", + "jv-Java-ID": "jv-Java", + "jv-Latn-ID": "jv", + "jvd-Latn-ID": "jvd", + "jvn-Latn-SR": "jvn", + "jwi-Latn-GH": "jwi", + "jya-Tibt-CN": "jya", + "jye-Hebr-IL": "jye", + "jyy-Latn-TD": "jyy", + "ka-Geor-GE": "ka", + "kaa-Cyrl-UZ": "kaa", + "kab-Latn-DZ": "kab", + "kac-Latn-MM": "kac", + "kad-Latn-ZZ": "kad", + "kag-Latn-MY": "kag", + "kah-Latn-CF": "kah", + "kai-Latn-ZZ": "kai", + "kaj-Latn-NG": "kaj", + "kak-Latn-PH": "kak", + "kam-Latn-KE": "kam", + "kao-Latn-ML": "kao", + "kap-Cyrl-RU": "kap", + "kaq-Latn-PE": "kaq", + "kav-Latn-BR": "kav", + "kaw-Kawi-ID": "kaw", + "kax-Latn-ID": "kax", + "kay-Latn-BR": "kay", + "kba-Latn-AU": "kba", + "kbb-Latn-BR": "kbb", + "kbc-Latn-BR": "kbc", + "kbd-Cyrl-RU": "kbd", + "kbd-Cyrl-TR": "kbd-TR", + "kbe-Latn-AU": "kbe", + "kbh-Latn-CO": "kbh", + "kbi-Latn-ID": "kbi", + "kbj-Latn-CD": "kbj", + "kbk-Latn-PG": "kbk", + "kbl-Latn-TD": "kbl", + "kbm-Latn-ZZ": "kbm", + "kbn-Latn-CF": "kbn", + "kbo-Latn-SS": "kbo", + "kbp-Latn-ZZ": "kbp", + "kbq-Latn-ZZ": "kbq", + "kbr-Ethi-ET": "kbr-Ethi", + "kbr-Latn-ET": "kbr", + "kbs-Latn-GA": "kbs", + "kbt-Latn-PG": "kbt", + "kbu-Arab-PK": "kbu", + "kbv-Latn-ID": "kbv", + "kbw-Latn-PG": "kbw", + "kbx-Latn-ZZ": "kbx", + "kby-Arab-NE": "kby", + "kbz-Latn-NG": "kbz", + "kca-Cyrl-RU": "kca", + "kcb-Latn-PG": "kcb", + "kcc-Latn-NG": "kcc", + "kcd-Latn-ID": "kcd", + "kce-Latn-NG": "kce", + "kcf-Latn-NG": "kcf", + "kcg-Latn-NG": "kcg", + "kch-Latn-NG": "kch", + "kci-Latn-NG": "kci", + "kcj-Latn-GW": "kcj", + "kck-Latn-ZW": "kck", + "kcl-Latn-ZZ": "kcl", + "kcm-Latn-CF": "kcm", + "kcn-Latn-UG": "kcn", + "kco-Latn-PG": "kco", + "kcp-Latn-SD": "kcp", + "kcq-Latn-NG": "kcq", + "kcs-Latn-NG": "kcs", + "kct-Latn-ZZ": "kct", + "kcu-Latn-TZ": "kcu", + "kcv-Latn-CD": "kcv", + "kcw-Latn-CD": "kcw", + "kcz-Latn-TZ": "kcz", + "kda-Latn-AU": "kda", + "kdc-Latn-TZ": "kdc", + "kdd-Latn-AU": "kdd", + "kde-Latn-TZ": "kde", + "kdf-Latn-PG": "kdf", + "kdg-Latn-CD": "kdg", + "kdh-Latn-TG": "kdh", + "kdi-Latn-UG": "kdi", + "kdj-Latn-UG": "kdj", + "kdk-Latn-NC": "kdk", + "kdl-Latn-ZZ": "kdl", + "kdm-Latn-NG": "kdm", + "kdn-Latn-ZW": "kdn", + "kdp-Latn-NG": "kdp", + "kdq-Beng-IN": "kdq", + "kdr-Cyrl-UA": "kdr-Cyrl", + "kdr-Latn-LT": "kdr", + "kdt-Thai-KH": "kdt-KH", + "kdt-Thai-LA": "kdt-LA", + "kdt-Thai-TH": "kdt", + "kdw-Latn-ID": "kdw", + "kdx-Latn-NG": "kdx", + "kdy-Latn-ID": "kdy", + "kdz-Latn-CM": "kdz", + "kea-Latn-CV": "kea", + "keb-Latn-GA": "keb", + "kec-Latn-SD": "kec", + "ked-Latn-TZ": "ked", + "kee-Latn-US": "kee", + "kef-Latn-TG": "kef", + "keg-Latn-SD": "keg", + "keh-Latn-PG": "keh", + "kei-Latn-ID": "kei", + "kek-Latn-GT": "kek", + "kel-Latn-CD": "kel", + "kem-Latn-TL": "kem", + "ken-Latn-CM": "ken", + "keo-Latn-UG": "keo", + "ker-Latn-TD": "ker", + "kes-Latn-NG": "kes", + "ket-Cyrl-RU": "ket", + "keu-Latn-TG": "keu", + "kew-Latn-PG": "kew", + "kex-Deva-IN": "kex", + "kex-Gujr-IN": "kex-Gujr", + "key-Telu-IN": "key", + "kez-Latn-ZZ": "kez", + "kfa-Knda-IN": "kfa", + "kfb-Deva-IN": "kfb", + "kfc-Telu-IN": "kfc", + "kfd-Knda-IN": "kfd", + "kfe-Taml-IN": "kfe", + "kff-Deva-IN": "kff-Deva", + "kff-Latn-IN": "kff", + "kff-Orya-IN": "kff-Orya", + "kff-Telu-IN": "kff-Telu", + "kfh-Mlym-IN": "kfh", + "kfi-Knda-IN": "kfi-Knda", + "kfi-Taml-IN": "kfi", + "kfk-Deva-IN": "kfk", + "kfk-Takr-IN": "kfk-Takr", + "kfl-Latn-CM": "kfl", + "kfm-Arab-IR": "kfm", + "kfn-Latn-CM": "kfn", + "kfo-Latn-CI": "kfo", + "kfp-Deva-IN": "kfp", + "kfq-Deva-IN": "kfq", + "kfr-Deva-IN": "kfr", + "kfs-Deva-IN": "kfs", + "kfv-Latn-IN": "kfv", + "kfw-Latn-IN": "kfw", + "kfx-Deva-IN": "kfx", + "kfx-Takr-IN": "kfx-Takr", + "kfy-Deva-IN": "kfy", + "kfz-Latn-BF": "kfz", + "kg-Latn-CD": "kg", + "kga-Latn-CI": "kga", + "kgb-Latn-ID": "kgb", + "kge-Latn-ID": "kge", + "kgf-Latn-ZZ": "kgf", + "kgj-Deva-NP": "kgj", + "kgk-Latn-BR": "kgk", + "kgl-Latn-AU": "kgl", + "kgm-Latn-BR": "kgm", + "kgo-Latn-SD": "kgo", + "kgp-Latn-BR": "kgp", + "kgq-Latn-ID": "kgq", + "kgr-Latn-ID": "kgr", + "kgs-Latn-AU": "kgs", + "kgt-Latn-NG": "kgt", + "kgu-Latn-PG": "kgu", + "kgv-Latn-ID": "kgv", + "kgw-Latn-ID": "kgw", + "kgx-Latn-ID": "kgx", + "kgy-Deva-NP": "kgy", + "kha-Latn-IN": "kha", + "khb-Talu-CN": "khb", + "khc-Latn-ID": "khc", + "khd-Latn-ID": "khd", + "khe-Latn-ID": "khe", + "khf-Thai-LA": "khf", + "khg-Tibt-CN": "khg", + "khh-Latn-ID": "khh", + "khj-Latn-NG": "khj", + "khl-Latn-PG": "khl", + "khn-Deva-IN": "khn", + "khp-Latn-ID": "khp", + "khq-Latn-ML": "khq", + "khr-Deva-IN": "khr-Deva", + "khr-Latn-IN": "khr", + "khs-Latn-ZZ": "khs", + "kht-Mymr-IN": "kht", + "khu-Latn-AO": "khu", + "khv-Cyrl-RU": "khv", + "khw-Arab-PK": "khw", + "khx-Latn-CD": "khx", + "khy-Latn-CD": "khy", + "khz-Latn-ZZ": "khz", + "ki-Latn-KE": "ki", + "kia-Latn-TD": "kia", + "kib-Latn-SD": "kib", + "kic-Latn-US": "kic", + "kid-Latn-CM": "kid", + "kie-Latn-TD": "kie", + "kif-Deva-NP": "kif", + "kig-Latn-ID": "kig", + "kih-Latn-PG": "kih", + "kij-Latn-ZZ": "kij", + "kil-Latn-NG": "kil", + "kim-Cyrl-RU": "kim", + "kio-Latn-US": "kio", + "kip-Deva-NP": "kip", + "kiq-Latn-ID": "kiq", + "kis-Latn-PG": "kis", + "kit-Latn-PG": "kit", + "kiu-Latn-TR": "kiu", + "kiv-Latn-TZ": "kiv", + "kiw-Latn-ZZ": "kiw", + "kix-Latn-IN": "kix", + "kiy-Latn-ID": "kiy", + "kiz-Latn-TZ": "kiz", + "kj-Latn-NA": "kj", + "kja-Latn-ID": "kja", + "kjb-Latn-GT": "kjb", + "kjc-Latn-ID": "kjc", + "kjd-Latn-ZZ": "kjd", + "kje-Latn-ID": "kje", + "kjg-Laoo-LA": "kjg", + "kjh-Cyrl-RU": "kjh", + "kji-Latn-SB": "kji", + "kjj-Latn-AZ": "kjj", + "kjk-Latn-ID": "kjk", + "kjl-Deva-NP": "kjl", + "kjm-Latn-VN": "kjm", + "kjn-Latn-AU": "kjn", + "kjo-Deva-IN": "kjo", + "kjp-Mymr-MM": "kjp", + "kjp-Thai-TH": "kjp-Thai", + "kjq-Latn-US": "kjq", + "kjr-Latn-ID": "kjr", + "kjs-Latn-ZZ": "kjs", + "kjt-Thai-TH": "kjt", + "kju-Latn-US": "kju", + "kjx-Latn-PG": "kjx", + "kjy-Latn-ZZ": "kjy", + "kk-Arab-AF": "kk-AF", + "kk-Arab-CN": "kk-CN", + "kk-Arab-IR": "kk-IR", + "kk-Arab-MN": "kk-MN", + "kk-Cyrl-KZ": "kk", + "kka-Latn-NG": "kka", + "kkb-Latn-ID": "kkb", + "kkc-Latn-ZZ": "kkc", + "kkd-Latn-NG": "kkd", + "kke-Arab-GN": "kke-Arab", + "kke-Latn-GN": "kke", + "kkf-Tibt-IN": "kkf", + "kkg-Latn-PH": "kkg", + "kkh-Lana-MM": "kkh", + "kki-Latn-TZ": "kki", + "kkj-Latn-CM": "kkj", + "kkk-Latn-SB": "kkk", + "kkl-Latn-ID": "kkl", + "kkm-Latn-NG": "kkm", + "kko-Latn-SD": "kko", + "kkp-Latn-AU": "kkp", + "kkq-Latn-CD": "kkq", + "kkr-Latn-NG": "kkr", + "kks-Latn-NG": "kks", + "kkt-Deva-NP": "kkt", + "kku-Latn-NG": "kku", + "kkv-Latn-ID": "kkv", + "kkw-Latn-CG": "kkw", + "kkx-Latn-ID": "kkx", + "kky-Latn-AU": "kky", + "kkz-Latn-CA": "kkz", + "kl-Latn-GL": "kl", + "kla-Latn-US": "kla", + "klb-Latn-MX": "klb", + "klc-Latn-CM": "klc", + "kld-Latn-AU": "kld", + "kle-Deva-NP": "kle", + "klf-Latn-TD": "klf", + "klg-Latn-PH": "klg", + "klh-Latn-PG": "klh", + "kli-Latn-ID": "kli", + "klj-Arab-IR": "klj", + "klk-Latn-NG": "klk", + "kll-Latn-PH": "kll", + "klm-Latn-PG": "klm", + "kln-Latn-KE": "kln", + "klo-Latn-NG": "klo", + "klp-Latn-PG": "klp", + "klq-Latn-ZZ": "klq", + "klr-Deva-NP": "klr", + "kls-Arab-PK": "kls-Arab", + "kls-Latn-PK": "kls", + "klt-Latn-ZZ": "klt", + "klu-Latn-LR": "klu", + "klv-Latn-VU": "klv", + "klw-Latn-ID": "klw", + "klx-Latn-ZZ": "klx", + "kly-Latn-ID": "kly", + "klz-Latn-ID": "klz", + "km-Khmr-KH": "km", + "kma-Latn-GH": "kma", + "kmb-Latn-AO": "kmb", + "kmc-Hani-CN": "kmc-Hani", + "kmc-Latn-CN": "kmc", + "kmd-Latn-PH": "kmd", + "kme-Latn-CM": "kme", + "kmf-Latn-PG": "kmf", + "kmg-Latn-PG": "kmg", + "kmh-Latn-ZZ": "kmh", + "kmi-Latn-NG": "kmi", + "kmj-Deva-IN": "kmj", + "kmk-Latn-PH": "kmk", + "kml-Latn-PH": "kml", + "kmm-Latn-IN": "kmm", + "kmn-Latn-PG": "kmn", + "kmo-Latn-ZZ": "kmo", + "kmp-Latn-CM": "kmp", + "kmq-Latn-ET": "kmq", + "kms-Latn-ZZ": "kms", + "kmt-Latn-ID": "kmt", + "kmu-Latn-ZZ": "kmu", + "kmv-Latn-BR": "kmv", + "kmw-Latn-ZZ": "kmw", + "kmx-Latn-PG": "kmx", + "kmy-Latn-NG": "kmy", + "kmz-Arab-IR": "kmz", + "kn-Knda-IN": "kn", + "kna-Latn-NG": "kna", + "knb-Latn-PH": "knb", + "knd-Latn-ID": "knd", + "kne-Latn-PH": "kne", + "knf-Latn-GW": "knf", + "kni-Latn-NG": "kni", + "knj-Latn-GT": "knj", + "knk-Arab-SL": "knk-Arab", + "knk-Latn-SL": "knk", + "knl-Latn-ID": "knl", + "knm-Latn-BR": "knm", + "kno-Latn-SL": "kno", + "knp-Latn-ZZ": "knp", + "knq-Latn-MY": "knq", + "knr-Latn-PG": "knr", + "kns-Latn-MY": "kns", + "kns-Thai-TH": "kns-Thai", + "knt-Latn-BR": "knt", + "knu-Latn-GN": "knu", + "knv-Latn-PG": "knv", + "knw-Latn-NA": "knw", + "knx-Latn-ID": "knx", + "kny-Latn-CD": "kny", + "knz-Latn-BF": "knz", + "ko-Hang-KR": "ko-Hang", + "ko-Jamo-KR": "ko-Jamo", + "ko-Kore-KP": "ko-KP", + "ko-Kore-KR": "ko", + "koa-Latn-PG": "koa", + "koc-Latn-NG": "koc", + "kod-Latn-ID": "kod", + "koe-Latn-SS": "koe", + "kof-Latn-NG": "kof", + "kog-Latn-CO": "kog", + "koh-Latn-CG": "koh", + "koi-Cyrl-RU": "koi", + "kok-Deva-IN": "kok", + "kol-Latn-ZZ": "kol", + "koo-Latn-UG": "koo", + "kop-Latn-PG": "kop", + "koq-Latn-GA": "koq", + "kos-Latn-FM": "kos", + "kot-Latn-CM": "kot", + "kou-Latn-TD": "kou", + "kov-Latn-NG": "kov", + "kow-Latn-NG": "kow", + "koy-Latn-US": "koy", + "koz-Latn-ZZ": "koz", + "kpa-Latn-NG": "kpa", + "kpc-Latn-CO": "kpc", + "kpd-Latn-ID": "kpd", + "kpe-Latn-LR": "kpe", + "kpf-Latn-ZZ": "kpf", + "kpg-Latn-FM": "kpg", + "kph-Latn-GH": "kph", + "kpi-Latn-ID": "kpi", + "kpj-Latn-BR": "kpj", + "kpk-Latn-NG": "kpk", + "kpl-Latn-CD": "kpl", + "kpm-Latn-VN": "kpm", + "kpn-Latn-BR": "kpn", + "kpo-Latn-ZZ": "kpo", + "kpq-Latn-ID": "kpq", + "kpr-Latn-ZZ": "kpr", + "kps-Latn-ID": "kps", + "kpt-Cyrl-RU": "kpt", + "kpu-Latn-ID": "kpu", + "kpw-Latn-PG": "kpw", + "kpx-Latn-ZZ": "kpx", + "kpy-Cyrl-RU": "kpy", + "kpz-Latn-UG": "kpz", + "kqa-Latn-PG": "kqa", + "kqb-Latn-ZZ": "kqb", + "kqc-Latn-PG": "kqc", + "kqd-Syrc-IQ": "kqd", + "kqe-Latn-PH": "kqe", + "kqf-Latn-ZZ": "kqf", + "kqg-Latn-BF": "kqg", + "kqh-Latn-TZ": "kqh", + "kqi-Latn-PG": "kqi", + "kqj-Latn-PG": "kqj", + "kqk-Latn-BJ": "kqk", + "kql-Latn-PG": "kql", + "kqm-Latn-CI": "kqm", + "kqn-Latn-ZM": "kqn", + "kqo-Latn-LR": "kqo", + "kqp-Latn-TD": "kqp", + "kqq-Latn-BR": "kqq", + "kqr-Latn-MY": "kqr", + "kqs-Latn-ZZ": "kqs", + "kqt-Latn-MY": "kqt", + "kqu-Latn-ZA": "kqu", + "kqv-Latn-ID": "kqv", + "kqw-Latn-PG": "kqw", + "kqx-Latn-CM": "kqx", + "kqy-Ethi-ZZ": "kqy", + "kqz-Latn-ZA": "kqz", + "kr-Latn-ZZ": "kr", + "kra-Deva-NP": "kra", + "krb-Latn-US": "krb", + "krc-Cyrl-RU": "krc", + "krd-Latn-TL": "krd", + "kre-Latn-BR": "kre", + "krf-Latn-VU": "krf", + "krh-Latn-NG": "krh", + "kri-Latn-SL": "kri", + "krj-Latn-PH": "krj", + "krk-Cyrl-RU": "krk", + "krl-Latn-RU": "krl", + "krn-Latn-LR": "krn", + "krp-Latn-NG": "krp", + "krr-Khmr-KH": "krr", + "krs-Latn-ZZ": "krs", + "krt-Latn-NE": "krt", + "kru-Deva-IN": "kru", + "krv-Khmr-KH": "krv", + "krw-Latn-LR": "krw", + "krx-Latn-SN": "krx", + "kry-Latn-AZ": "kry", + "krz-Latn-ID": "krz", + "ks-Arab-IN": "ks", + "ksa-Latn-NG": "ksa", + "ksb-Latn-TZ": "ksb", + "ksc-Latn-PH": "ksc", + "ksd-Latn-ZZ": "ksd", + "kse-Latn-PG": "kse", + "ksf-Latn-CM": "ksf", + "ksg-Latn-SB": "ksg", + "ksh-Latn-DE": "ksh", + "ksi-Latn-PG": "ksi", + "ksj-Latn-ZZ": "ksj", + "ksk-Latn-US": "ksk", + "ksl-Latn-PG": "ksl", + "ksm-Latn-NG": "ksm", + "ksn-Latn-PH": "ksn", + "kso-Latn-NG": "kso", + "ksp-Latn-CF": "ksp", + "ksq-Latn-NG": "ksq", + "ksr-Latn-ZZ": "ksr", + "kss-Latn-LR": "kss", + "kst-Latn-BF": "kst", + "ksu-Mymr-IN": "ksu", + "ksv-Latn-CD": "ksv", + "ksw-Latn-MM": "ksw-Latn", + "ksw-Mymr-MM": "ksw", + "ksx-Latn-ID": "ksx", + "ksz-Deva-IN": "ksz", + "kta-Latn-VN": "kta", + "ktb-Ethi-ZZ": "ktb", + "ktc-Latn-NG": "ktc", + "ktd-Latn-AU": "ktd", + "ktf-Latn-CD": "ktf", + "ktg-Latn-AU": "ktg", + "kth-Latn-TD": "kth", + "kti-Latn-ID": "kti", + "ktj-Latn-CI": "ktj", + "ktk-Latn-PG": "ktk", + "ktl-Arab-IR": "ktl", + "ktm-Latn-ZZ": "ktm", + "ktn-Latn-BR": "ktn", + "kto-Latn-ZZ": "kto", + "ktp-Plrd-CN": "ktp", + "ktq-Latn-PH": "ktq", + "kts-Latn-ID": "kts", + "ktt-Latn-ID": "ktt", + "ktu-Latn-CD": "ktu", + "ktv-Latn-VN": "ktv", + "ktw-Latn-US": "ktw", + "ktx-Latn-BR": "ktx", + "kty-Latn-CD": "kty", + "ktz-Latn-NA": "ktz", + "ku-Arab-IQ": "ku-Arab", + "ku-Arab-LB": "ku-LB", + "ku-Latn-AM": "ku-AM", + "ku-Latn-GE": "ku-GE", + "ku-Latn-TR": "ku", + "ku-Yezi-GE": "ku-Yezi", + "kub-Latn-ZZ": "kub", + "kuc-Latn-ID": "kuc", + "kud-Latn-ZZ": "kud", + "kue-Latn-ZZ": "kue", + "kuf-Laoo-LA": "kuf", + "kug-Latn-NG": "kug", + "kuh-Latn-NG": "kuh", + "kui-Latn-BR": "kui", + "kuj-Latn-ZZ": "kuj", + "kuk-Latn-ID": "kuk", + "kul-Latn-NG": "kul", + "kum-Cyrl-RU": "kum", + "kun-Latn-ZZ": "kun", + "kuo-Latn-PG": "kuo", + "kup-Latn-ZZ": "kup", + "kuq-Latn-BR": "kuq", + "kus-Latn-ZZ": "kus", + "kut-Latn-CA": "kut", + "kuu-Latn-US": "kuu", + "kuv-Latn-ID": "kuv", + "kuw-Latn-CF": "kuw", + "kux-Latn-AU": "kux", + "kuy-Latn-AU": "kuy", + "kuz-Latn-CL": "kuz", + "kv-Cyrl-RU": "kv", + "kv-Perm-RU": "kv-Perm", + "kva-Cyrl-RU": "kva", + "kvb-Latn-ID": "kvb", + "kvc-Latn-PG": "kvc", + "kvd-Latn-ID": "kvd", + "kve-Latn-MY": "kve", + "kvf-Latn-TD": "kvf", + "kvg-Latn-ZZ": "kvg", + "kvh-Latn-ID": "kvh", + "kvi-Latn-TD": "kvi", + "kvj-Latn-CM": "kvj", + "kvl-Latn-MM": "kvl", + "kvm-Latn-CM": "kvm", + "kvn-Latn-CO": "kvn", + "kvo-Latn-ID": "kvo", + "kvp-Latn-ID": "kvp", + "kvq-Latn-MM": "kvq-Latn", + "kvq-Mymr-MM": "kvq", + "kvr-Latn-ID": "kvr", + "kvt-Mymr-MM": "kvt", + "kvv-Latn-ID": "kvv", + "kvw-Latn-ID": "kvw", + "kvx-Arab-PK": "kvx", + "kvy-Kali-MM": "kvy", + "kvz-Latn-ID": "kvz", + "kw-Latn-GB": "kw", + "kwa-Latn-BR": "kwa", + "kwb-Latn-NG": "kwb", + "kwc-Latn-CG": "kwc", + "kwd-Latn-SB": "kwd", + "kwe-Latn-ID": "kwe", + "kwf-Latn-SB": "kwf", + "kwg-Latn-TD": "kwg", + "kwh-Latn-ID": "kwh", + "kwi-Latn-CO": "kwi", + "kwj-Latn-ZZ": "kwj", + "kwk-Latn-CA": "kwk", + "kwl-Latn-NG": "kwl", + "kwm-Latn-NA": "kwm", + "kwn-Latn-NA": "kwn", + "kwo-Latn-ZZ": "kwo", + "kwp-Latn-CI": "kwp", + "kwr-Latn-ID": "kwr", + "kws-Latn-CD": "kws", + "kwt-Latn-ID": "kwt", + "kwu-Latn-CM": "kwu", + "kwv-Latn-TD": "kwv", + "kww-Latn-SR": "kww", + "kwy-Latn-CD": "kwy", + "kwz-Latn-AO": "kwz", + "kxa-Latn-ZZ": "kxa", + "kxb-Latn-CI": "kxb", + "kxc-Ethi-ZZ": "kxc", + "kxd-Arab-BN": "kxd-Arab", + "kxd-Latn-BN": "kxd", + "kxf-Latn-MM": "kxf-Latn", + "kxf-Mymr-MM": "kxf", + "kxi-Latn-MY": "kxi", + "kxj-Latn-TD": "kxj", + "kxk-Mymr-MM": "kxk", + "kxm-Thai-TH": "kxm", + "kxn-Latn-MY": "kxn", + "kxo-Latn-BR": "kxo", + "kxp-Arab-PK": "kxp", + "kxq-Latn-ID": "kxq", + "kxr-Latn-PG": "kxr", + "kxt-Latn-PG": "kxt", + "kxv-Latn-IN": "kxv-Latn", + "kxv-Orya-IN": "kxv", + "kxv-Telu-IN": "kxv-Telu", + "kxw-Latn-ZZ": "kxw", + "kxx-Latn-CG": "kxx", + "kxy-Latn-VN": "kxy", + "kxz-Latn-ZZ": "kxz", + "ky-Arab-CN": "ky-CN", + "ky-Cyrl-KG": "ky", + "ky-Latn-TR": "ky-TR", + "kya-Latn-TZ": "kya", + "kyb-Latn-PH": "kyb", + "kyc-Latn-PG": "kyc", + "kyd-Latn-ID": "kyd", + "kye-Latn-ZZ": "kye", + "kyf-Latn-CI": "kyf", + "kyg-Latn-PG": "kyg", + "kyh-Latn-US": "kyh", + "kyi-Latn-MY": "kyi", + "kyj-Latn-PH": "kyj", + "kyk-Latn-PH": "kyk", + "kyl-Latn-US": "kyl", + "kym-Latn-CF": "kym", + "kyn-Latn-PH": "kyn", + "kyo-Latn-ID": "kyo", + "kyq-Latn-TD": "kyq", + "kyr-Latn-BR": "kyr", + "kys-Latn-MY": "kys", + "kyt-Latn-ID": "kyt", + "kyu-Kali-MM": "kyu", + "kyu-Latn-MM": "kyu-Latn", + "kyu-Mymr-MM": "kyu-Mymr", + "kyv-Deva-NP": "kyv", + "kyw-Beng-IN": "kyw-Beng", + "kyw-Deva-IN": "kyw", + "kyw-Orya-IN": "kyw-Orya", + "kyx-Latn-ZZ": "kyx", + "kyy-Latn-PG": "kyy", + "kyz-Latn-BR": "kyz", + "kza-Latn-BF": "kza", + "kzb-Latn-ID": "kzb", + "kzc-Latn-CI": "kzc", + "kzd-Latn-ID": "kzd", + "kze-Latn-PG": "kze", + "kzf-Latn-ID": "kzf", + "kzi-Latn-MY": "kzi", + "kzk-Latn-SB": "kzk", + "kzl-Latn-ID": "kzl", + "kzm-Latn-ID": "kzm", + "kzn-Latn-MW": "kzn", + "kzo-Latn-GA": "kzo", + "kzp-Latn-ID": "kzp", + "kzr-Latn-ZZ": "kzr", + "kzs-Latn-MY": "kzs", + "kzu-Latn-ID": "kzu", + "kzv-Latn-ID": "kzv", + "kzw-Latn-BR": "kzw", + "kzx-Latn-ID": "kzx", + "kzy-Latn-CD": "kzy", + "kzz-Latn-ID": "kzz", + "la-Latn-VA": "la", + "laa-Latn-PH": "laa", + "lab-Lina-GR": "lab", + "lac-Latn-MX": "lac", + "lad-Hebr-IL": "lad", + "lae-Deva-IN": "lae", + "lae-Tibt-IN": "lae-Tibt", + "lag-Latn-TZ": "lag", + "lah-Arab-PK": "lah", + "lai-Latn-MW": "lai", + "laj-Latn-UG": "laj", + "lal-Latn-CD": "lal", + "lam-Latn-ZM": "lam", + "lan-Latn-NG": "lan", + "lap-Latn-TD": "lap", + "laq-Latn-VN": "laq", + "lar-Latn-GH": "lar", + "las-Latn-ZZ": "las", + "lau-Latn-ID": "lau", + "law-Latn-ID": "law", + "lax-Beng-IN": "lax-Beng", + "lax-Latn-IN": "lax", + "laz-Latn-PG": "laz", + "lb-Latn-LU": "lb", + "lbb-Latn-PG": "lbb", + "lbc-Lisu-CN": "lbc", + "lbe-Cyrl-RU": "lbe", + "lbf-Deva-IN": "lbf", + "lbf-Tibt-CN": "lbf-Tibt", + "lbi-Latn-CM": "lbi", + "lbj-Arab-IN": "lbj-Arab", + "lbj-Tibt-IN": "lbj", + "lbl-Latn-PH": "lbl", + "lbm-Deva-IN": "lbm", + "lbn-Laoo-LA": "lbn-Laoo", + "lbn-Latn-LA": "lbn", + "lbo-Laoo-LA": "lbo", + "lbo-Latn-US": "lbo-Latn", + "lbq-Latn-PG": "lbq", + "lbr-Deva-NP": "lbr", + "lbt-Latn-VN": "lbt", + "lbu-Latn-ZZ": "lbu", + "lbv-Latn-PG": "lbv", + "lbw-Latn-ID": "lbw", + "lbx-Latn-ID": "lbx", + "lby-Latn-AU": "lby", + "lbz-Latn-AU": "lbz", + "lcc-Latn-ID": "lcc", + "lcd-Latn-ID": "lcd", + "lce-Latn-ID": "lce", + "lcf-Latn-ID": "lcf", + "lch-Latn-AO": "lch", + "lcl-Latn-ID": "lcl", + "lcm-Latn-ZZ": "lcm", + "lcp-Thai-CN": "lcp", + "lcq-Latn-ID": "lcq", + "lcs-Latn-ID": "lcs", + "lda-Latn-CI": "lda", + "ldb-Latn-ZZ": "ldb", + "ldd-Latn-NG": "ldd", + "ldg-Latn-NG": "ldg", + "ldh-Latn-NG": "ldh", + "ldi-Latn-CG": "ldi", + "ldj-Latn-NG": "ldj", + "ldk-Latn-NG": "ldk", + "ldl-Latn-NG": "ldl", + "ldm-Latn-GN": "ldm", + "ldn-Latn-001": "ldn", + "ldo-Latn-NG": "ldo", + "ldp-Latn-NG": "ldp", + "ldq-Latn-NG": "ldq", + "lea-Latn-CD": "lea", + "leb-Latn-ZM": "leb", + "lec-Latn-BO": "lec", + "led-Latn-ZZ": "led", + "lee-Latn-ZZ": "lee", + "lef-Latn-GH": "lef", + "leh-Latn-ZM": "leh", + "lei-Latn-PG": "lei", + "lej-Latn-CD": "lej", + "lek-Latn-PG": "lek", + "lel-Latn-CD": "lel", + "lem-Latn-ZZ": "lem", + "len-Latn-HN": "len", + "leo-Latn-CM": "leo", + "lep-Lepc-IN": "lep", + "leq-Latn-ZZ": "leq", + "ler-Latn-PG": "ler", + "les-Latn-CD": "les", + "let-Latn-PG": "let", + "leu-Latn-ZZ": "leu", + "lev-Latn-ID": "lev", + "lew-Latn-ID": "lew", + "lex-Latn-ID": "lex", + "ley-Latn-ID": "ley", + "lez-Cyrl-RU": "lez", + "lfa-Latn-CM": "lfa", + "lfn-Cyrl-001": "lfn-Cyrl", + "lfn-Latn-001": "lfn", + "lg-Latn-UG": "lg", + "lga-Latn-SB": "lga", + "lgb-Latn-SB": "lgb", + "lgg-Latn-ZZ": "lgg", + "lgh-Latn-VN": "lgh", + "lgi-Latn-ID": "lgi", + "lgk-Latn-VU": "lgk", + "lgl-Latn-SB": "lgl", + "lgm-Latn-CD": "lgm", + "lgn-Latn-ET": "lgn", + "lgo-Latn-SS": "lgo", + "lgq-Latn-GH": "lgq", + "lgr-Latn-SB": "lgr", + "lgt-Latn-PG": "lgt", + "lgu-Latn-SB": "lgu", + "lgz-Latn-CD": "lgz", + "lha-Latn-VN": "lha", + "lhh-Latn-ID": "lhh", + "lhi-Latn-CN": "lhi", + "lhm-Deva-NP": "lhm", + "lhn-Latn-MY": "lhn", + "lhs-Syrc-SY": "lhs", + "lht-Latn-VU": "lht", + "lhu-Latn-CN": "lhu", + "li-Latn-NL": "li", + "lia-Latn-ZZ": "lia", + "lib-Latn-PG": "lib", + "lic-Latn-CN": "lic", + "lid-Latn-ZZ": "lid", + "lie-Latn-CD": "lie", + "lif-Deva-NP": "lif", + "lif-Limb-IN": "lif-Limb", + "lig-Latn-ZZ": "lig", + "lih-Latn-ZZ": "lih", + "lij-Latn-IT": "lij", + "lik-Latn-CD": "lik", + "lil-Latn-CA": "lil", + "lio-Latn-ID": "lio", + "lip-Latn-GH": "lip", + "liq-Latn-ET": "liq", + "lir-Latn-LR": "lir", + "lis-Lisu-CN": "lis", + "liu-Latn-SD": "liu", + "liv-Latn-LV": "liv", + "liw-Latn-ID": "liw", + "lix-Latn-ID": "lix", + "liy-Latn-CF": "liy", + "liz-Latn-CD": "liz", + "lja-Latn-AU": "lja", + "lje-Latn-ID": "lje", + "lji-Latn-ID": "lji", + "ljl-Latn-ID": "ljl", + "ljp-Latn-ID": "ljp", + "ljw-Latn-AU": "ljw", + "ljx-Latn-AU": "ljx", + "lka-Latn-TL": "lka", + "lkb-Latn-KE": "lkb", + "lkc-Latn-VN": "lkc", + "lkd-Latn-BR": "lkd", + "lke-Latn-UG": "lke", + "lkh-Tibt-BT": "lkh", + "lki-Arab-IR": "lki", + "lkj-Latn-MY": "lkj", + "lkl-Latn-PG": "lkl", + "lkm-Latn-AU": "lkm", + "lkn-Latn-VU": "lkn", + "lko-Latn-KE": "lko", + "lkr-Latn-SS": "lkr", + "lks-Latn-KE": "lks", + "lkt-Latn-US": "lkt", + "lku-Latn-AU": "lku", + "lky-Latn-SS": "lky", + "lla-Latn-NG": "lla", + "llb-Latn-MZ": "llb", + "llc-Latn-GN": "llc", + "lld-Latn-IT": "lld", + "lle-Latn-ZZ": "lle", + "llf-Latn-PG": "llf", + "llg-Latn-ID": "llg", + "lli-Latn-CG": "lli", + "llj-Latn-AU": "llj", + "llk-Latn-MY": "llk", + "lll-Latn-PG": "lll", + "llm-Latn-ID": "llm", + "lln-Latn-ZZ": "lln", + "llp-Latn-VU": "llp", + "llq-Latn-ID": "llq", + "llu-Latn-SB": "llu", + "llx-Latn-FJ": "llx", + "lma-Latn-GN": "lma", + "lmb-Latn-VU": "lmb", + "lmc-Latn-AU": "lmc", + "lmd-Latn-SD": "lmd", + "lme-Latn-TD": "lme", + "lmf-Latn-ID": "lmf", + "lmg-Latn-PG": "lmg", + "lmh-Deva-NP": "lmh", + "lmi-Latn-CD": "lmi", + "lmj-Latn-ID": "lmj", + "lmk-Latn-IN": "lmk", + "lmk-Mymr-IN": "lmk-Mymr", + "lml-Latn-VU": "lml", + "lmn-Telu-IN": "lmn", + "lmo-Latn-IT": "lmo", + "lmp-Latn-ZZ": "lmp", + "lmq-Latn-ID": "lmq", + "lmr-Latn-ID": "lmr", + "lmu-Latn-VU": "lmu", + "lmv-Latn-FJ": "lmv", + "lmw-Latn-US": "lmw", + "lmx-Latn-CM": "lmx", + "lmy-Latn-ID": "lmy", + "ln-Latn-CD": "ln", + "lna-Latn-CF": "lna", + "lnb-Latn-NA": "lnb", + "lnd-Latn-ID": "lnd", + "lnh-Latn-MY": "lnh", + "lni-Latn-PG": "lni", + "lnj-Latn-AU": "lnj", + "lnl-Latn-CF": "lnl", + "lnm-Latn-PG": "lnm", + "lnn-Latn-VU": "lnn", + "lns-Latn-ZZ": "lns", + "lnu-Latn-ZZ": "lnu", + "lnw-Latn-AU": "lnw", + "lnz-Latn-CD": "lnz", + "lo-Laoo-LA": "lo", + "loa-Latn-ID": "loa", + "lob-Latn-BF": "lob", + "loc-Latn-PH": "loc", + "loe-Latn-ID": "loe", + "log-Latn-CD": "log", + "loh-Latn-SS": "loh", + "loi-Latn-CI": "loi", + "loj-Latn-ZZ": "loj", + "lok-Latn-ZZ": "lok", + "lol-Latn-CD": "lol", + "lom-Latn-LR": "lom", + "lon-Latn-MW": "lon", + "loo-Latn-CD": "loo", + "lop-Latn-NG": "lop", + "loq-Latn-CD": "loq", + "lor-Latn-ZZ": "lor", + "los-Latn-ZZ": "los", + "lot-Arab-SS": "lot-Arab", + "lot-Latn-SS": "lot", + "lou-Latn-US": "lou", + "low-Latn-MY": "low", + "lox-Latn-ID": "lox", + "loy-Deva-NP": "loy", + "loy-Tibt-NP": "loy-Tibt", + "loz-Latn-ZM": "loz", + "lpa-Latn-VU": "lpa", + "lpe-Latn-ID": "lpe", + "lpn-Latn-MM": "lpn", + "lpo-Lisu-CN": "lpo-Lisu", + "lpo-Plrd-CN": "lpo", + "lpx-Latn-SS": "lpx", + "lqr-Latn-SS": "lqr", + "lra-Latn-MY": "lra", + "lrc-Arab-IR": "lrc", + "lrg-Latn-AU": "lrg", + "lri-Latn-KE": "lri", + "lrk-Arab-PK": "lrk", + "lrl-Arab-IR": "lrl", + "lrm-Latn-KE": "lrm", + "lrn-Latn-ID": "lrn", + "lro-Latn-SD": "lro", + "lrt-Latn-ID": "lrt", + "lrv-Latn-VU": "lrv", + "lrz-Latn-VU": "lrz", + "lsa-Arab-IR": "lsa", + "lsd-Hebr-IL": "lsd", + "lse-Latn-CD": "lse", + "lsi-Latn-MM": "lsi", + "lsm-Latn-UG": "lsm", + "lsr-Latn-PG": "lsr", + "lss-Arab-PK": "lss", + "lt-Latn-LT": "lt", + "ltg-Latn-LV": "ltg", + "lth-Latn-UG": "lth", + "lti-Latn-ID": "lti", + "ltn-Latn-BR": "ltn", + "lto-Latn-KE": "lto", + "lts-Latn-KE": "lts", + "ltu-Latn-ID": "ltu", + "lu-Latn-CD": "lu", + "lua-Latn-CD": "lua", + "luc-Latn-UG": "luc", + "lud-Latn-RU": "lud", + "lue-Latn-ZM": "lue", + "luf-Latn-PG": "luf", + "lui-Latn-US": "lui", + "luj-Latn-CD": "luj", + "luk-Tibt-BT": "luk", + "lul-Latn-SS": "lul", + "lum-Latn-AO": "lum", + "lun-Latn-ZM": "lun", + "luo-Latn-KE": "luo", + "lup-Latn-GA": "lup", + "luq-Latn-CU": "luq", + "lur-Latn-ID": "lur", + "lus-Beng-BD": "lus-Beng", + "lus-Brai-IN": "lus-Brai", + "lus-Latn-IN": "lus", + "lut-Latn-US": "lut", + "luu-Deva-NP": "luu", + "luv-Arab-OM": "luv", + "luw-Latn-CM": "luw", + "luy-Latn-KE": "luy", + "luz-Arab-IR": "luz", + "lv-Latn-LV": "lv", + "lva-Latn-TL": "lva", + "lvi-Latn-LA": "lvi", + "lvk-Latn-SB": "lvk", + "lvu-Latn-ID": "lvu", + "lwa-Latn-CD": "lwa", + "lwe-Latn-ID": "lwe", + "lwg-Latn-KE": "lwg", + "lwh-Latn-VN": "lwh", + "lwl-Thai-TH": "lwl", + "lwm-Thai-CN": "lwm", + "lwo-Latn-SS": "lwo", + "lwo-Latn-ZA": "lwo-ZA", + "lwt-Latn-ID": "lwt", + "lww-Latn-VU": "lww", + "lxm-Latn-PG": "lxm", + "lya-Tibt-BT": "lya", + "lyn-Latn-ZM": "lyn", + "lzh-Hans-CN": "lzh", + "lzh-Phag-CN": "lzh-Phag", + "lzl-Latn-VU": "lzl", + "lzn-Latn-MM": "lzn", + "lzz-Latn-TR": "lzz", + "maa-Latn-MX": "maa", + "mab-Latn-MX": "mab", + "mad-Latn-ID": "mad", + "mae-Latn-NG": "mae", + "maf-Latn-CM": "maf", + "mag-Deva-IN": "mag", + "mai-Deva-IN": "mai", + "mai-Tirh-IN": "mai-Tirh", + "maj-Latn-MX": "maj", + "mak-Latn-ID": "mak", + "mak-Maka-ID": "mak-Maka", + "mam-Latn-GT": "mam", + "man-Latn-GM": "man", + "man-Nkoo-GN": "man-GN", + "maq-Latn-MX": "maq", + "mas-Latn-KE": "mas", + "mat-Latn-MX": "mat", + "mau-Latn-MX": "mau", + "mav-Latn-BR": "mav", + "maw-Latn-ZZ": "maw", + "max-Latn-ID": "max", + "maz-Latn-MX": "maz", + "mba-Latn-PH": "mba", + "mbb-Latn-PH": "mbb", + "mbc-Latn-BR": "mbc", + "mbd-Latn-PH": "mbd", + "mbf-Latn-SG": "mbf", + "mbh-Latn-ZZ": "mbh", + "mbi-Latn-PH": "mbi", + "mbj-Latn-BR": "mbj", + "mbk-Latn-PG": "mbk", + "mbl-Latn-BR": "mbl", + "mbm-Latn-CG": "mbm", + "mbn-Latn-CO": "mbn", + "mbo-Latn-ZZ": "mbo", + "mbp-Latn-CO": "mbp", + "mbq-Latn-ZZ": "mbq", + "mbr-Latn-CO": "mbr", + "mbs-Latn-PH": "mbs", + "mbt-Latn-PH": "mbt", + "mbu-Latn-ZZ": "mbu", + "mbv-Latn-GN": "mbv", + "mbw-Latn-ZZ": "mbw", + "mbx-Latn-PG": "mbx", + "mby-Arab-PK": "mby", + "mbz-Latn-MX": "mbz", + "mca-Latn-PY": "mca", + "mcb-Latn-PE": "mcb", + "mcc-Latn-PG": "mcc", + "mcd-Latn-PE": "mcd", + "mce-Latn-MX": "mce", + "mcf-Latn-PE": "mcf", + "mcg-Latn-VE": "mcg", + "mch-Latn-VE": "mch", + "mci-Latn-ZZ": "mci", + "mcj-Latn-NG": "mcj", + "mck-Latn-AO": "mck", + "mcl-Latn-CO": "mcl", + "mcm-Latn-MY": "mcm", + "mcn-Latn-TD": "mcn", + "mco-Latn-MX": "mco", + "mcp-Latn-ZZ": "mcp", + "mcq-Latn-ZZ": "mcq", + "mcr-Latn-ZZ": "mcr", + "mcs-Latn-CM": "mcs", + "mct-Latn-CM": "mct", + "mcu-Latn-ZZ": "mcu", + "mcv-Latn-PG": "mcv", + "mcw-Latn-TD": "mcw", + "mcx-Latn-CF": "mcx", + "mcy-Latn-PG": "mcy", + "mcz-Latn-PG": "mcz", + "mda-Latn-ZZ": "mda", + "mdb-Latn-PG": "mdb", + "mdc-Latn-PG": "mdc", + "mdd-Latn-CM": "mdd", + "mde-Arab-ZZ": "mde", + "mdf-Cyrl-RU": "mdf", + "mdg-Latn-TD": "mdg", + "mdh-Latn-PH": "mdh", + "mdi-Latn-CD": "mdi", + "mdj-Latn-ZZ": "mdj", + "mdk-Latn-CD": "mdk", + "mdm-Latn-CD": "mdm", + "mdn-Latn-CF": "mdn", + "mdp-Latn-CD": "mdp", + "mdq-Latn-CD": "mdq", + "mdr-Latn-ID": "mdr", + "mds-Latn-PG": "mds", + "mdt-Latn-CG": "mdt", + "mdu-Latn-CG": "mdu", + "mdv-Latn-MX": "mdv", + "mdw-Latn-CG": "mdw", + "mdx-Ethi-ZZ": "mdx", + "mdy-Ethi-ET": "mdy", + "mdy-Latn-ET": "mdy-Latn", + "mdz-Latn-BR": "mdz", + "mea-Latn-CM": "mea", + "meb-Latn-PG": "meb", + "mec-Latn-AU": "mec", + "med-Latn-ZZ": "med", + "mee-Latn-ZZ": "mee", + "meh-Latn-MX": "meh", + "mej-Latn-ID": "mej", + "mek-Latn-ZZ": "mek", + "mel-Latn-MY": "mel", + "mem-Latn-AU": "mem", + "men-Latn-SL": "men", + "men-Mend-SL": "men-Mend", + "meo-Arab-MY": "meo-Arab", + "meo-Latn-MY": "meo", + "mep-Latn-AU": "mep", + "meq-Latn-CM": "meq", + "mer-Latn-KE": "mer", + "mes-Latn-TD": "mes", + "met-Latn-ZZ": "met", + "meu-Latn-ZZ": "meu", + "mev-Latn-LR": "mev", + "mew-Latn-NG": "mew", + "mey-Arab-MR": "mey-Arab", + "mey-Latn-MR": "mey", + "mez-Latn-US": "mez", + "mfa-Arab-TH": "mfa", + "mfb-Latn-ID": "mfb", + "mfc-Latn-CD": "mfc", + "mfd-Latn-CM": "mfd", + "mfe-Latn-MU": "mfe", + "mff-Latn-CM": "mff", + "mfg-Arab-GN": "mfg-Arab", + "mfg-Latn-GN": "mfg", + "mfh-Latn-CM": "mfh", + "mfi-Arab-CM": "mfi", + "mfi-Latn-CM": "mfi-Latn", + "mfj-Latn-CM": "mfj", + "mfk-Latn-CM": "mfk", + "mfl-Latn-NG": "mfl", + "mfm-Latn-NG": "mfm", + "mfn-Latn-ZZ": "mfn", + "mfo-Latn-ZZ": "mfo", + "mfp-Latn-ID": "mfp", + "mfq-Latn-ZZ": "mfq", + "mfr-Latn-AU": "mfr", + "mft-Latn-PG": "mft", + "mfu-Latn-AO": "mfu", + "mfv-Latn-GW": "mfv", + "mfw-Latn-PG": "mfw", + "mfx-Ethi-ET": "mfx-Ethi", + "mfx-Latn-ET": "mfx", + "mfy-Latn-MX": "mfy", + "mfz-Latn-SS": "mfz", + "mg-Latn-MG": "mg", + "mgb-Latn-TD": "mgb", + "mgc-Latn-SS": "mgc", + "mgd-Arab-SS": "mgd-Arab", + "mgd-Latn-SS": "mgd", + "mge-Latn-TD": "mge", + "mgf-Latn-ID": "mgf", + "mgg-Latn-CM": "mgg", + "mgh-Latn-MZ": "mgh", + "mgi-Latn-NG": "mgi", + "mgj-Latn-NG": "mgj", + "mgk-Latn-ID": "mgk", + "mgl-Latn-ZZ": "mgl", + "mgm-Latn-TL": "mgm", + "mgn-Latn-CF": "mgn", + "mgo-Latn-CM": "mgo", + "mgp-Deva-NP": "mgp", + "mgq-Latn-TZ": "mgq", + "mgr-Latn-ZM": "mgr", + "mgs-Latn-TZ": "mgs", + "mgt-Latn-PG": "mgt", + "mgu-Latn-PG": "mgu", + "mgv-Latn-TZ": "mgv", + "mgw-Latn-TZ": "mgw", + "mgy-Latn-TZ": "mgy", + "mgz-Latn-TZ": "mgz", + "mh-Latn-MH": "mh", + "mhb-Latn-GA": "mhb", + "mhc-Latn-MX": "mhc", + "mhd-Latn-TZ": "mhd", + "mhe-Latn-MY": "mhe", + "mhf-Latn-PG": "mhf", + "mhg-Latn-AU": "mhg", + "mhi-Latn-ZZ": "mhi", + "mhj-Arab-AF": "mhj", + "mhk-Latn-CM": "mhk", + "mhl-Latn-ZZ": "mhl", + "mhm-Latn-MZ": "mhm", + "mhn-Latn-IT": "mhn", + "mho-Latn-ZM": "mho", + "mhp-Latn-ID": "mhp", + "mhq-Latn-US": "mhq", + "mhs-Latn-ID": "mhs", + "mht-Latn-VE": "mht", + "mhu-Latn-IN": "mhu", + "mhw-Latn-BW": "mhw", + "mhx-Latn-MM": "mhx", + "mhy-Latn-ID": "mhy", + "mhz-Latn-ID": "mhz", + "mi-Latn-NZ": "mi", + "mia-Latn-US": "mia", + "mib-Latn-MX": "mib", + "mic-Latn-CA": "mic", + "mid-Mand-IQ": "mid", + "mie-Latn-MX": "mie", + "mif-Latn-ZZ": "mif", + "mig-Latn-MX": "mig", + "mih-Latn-MX": "mih", + "mii-Latn-MX": "mii", + "mij-Latn-CM": "mij", + "mik-Latn-US": "mik", + "mil-Latn-MX": "mil", + "mim-Latn-MX": "mim", + "min-Latn-ID": "min", + "mio-Latn-MX": "mio", + "mip-Latn-MX": "mip", + "miq-Latn-NI": "miq", + "mir-Latn-MX": "mir", + "mit-Latn-MX": "mit", + "miu-Latn-MX": "miu", + "miw-Latn-ZZ": "miw", + "mix-Latn-MX": "mix", + "miy-Latn-MX": "miy", + "miz-Latn-MX": "miz", + "mjb-Latn-TL": "mjb", + "mjc-Latn-MX": "mjc", + "mjd-Latn-US": "mjd", + "mje-Latn-TD": "mje", + "mjg-Latn-CN": "mjg", + "mjh-Latn-TZ": "mjh", + "mji-Latn-CN": "mji", + "mjj-Latn-PG": "mjj", + "mjk-Latn-PG": "mjk", + "mjl-Deva-IN": "mjl", + "mjl-Takr-IN": "mjl-Takr", + "mjm-Latn-PG": "mjm", + "mjn-Latn-PG": "mjn", + "mjq-Mlym-IN": "mjq", + "mjr-Mlym-IN": "mjr", + "mjs-Latn-NG": "mjs", + "mjt-Beng-BD": "mjt-Beng", + "mjt-Deva-IN": "mjt", + "mju-Telu-IN": "mju", + "mjv-Mlym-IN": "mjv", + "mjw-Latn-IN": "mjw", + "mjx-Beng-BD": "mjx-Beng", + "mjx-Latn-BD": "mjx", + "mjy-Latn-US": "mjy", + "mjz-Deva-NP": "mjz", + "mk-Cyrl-AL": "mk-AL", + "mk-Cyrl-GR": "mk-GR", + "mk-Cyrl-MK": "mk", + "mka-Latn-CI": "mka", + "mkb-Deva-IN": "mkb", + "mkc-Latn-PG": "mkc", + "mke-Deva-IN": "mke", + "mkf-Latn-NG": "mkf", + "mki-Arab-ZZ": "mki", + "mkj-Latn-FM": "mkj", + "mkk-Latn-CM": "mkk", + "mkl-Latn-ZZ": "mkl", + "mkm-Thai-TH": "mkm", + "mkn-Latn-ID": "mkn", + "mko-Latn-NG": "mko", + "mkp-Latn-ZZ": "mkp", + "mkr-Latn-PG": "mkr", + "mks-Latn-MX": "mks", + "mkt-Latn-NC": "mkt", + "mku-Latn-GN": "mku", + "mkv-Latn-VU": "mkv", + "mkw-Latn-ZZ": "mkw", + "mkx-Latn-PH": "mkx", + "mky-Latn-ID": "mky", + "mkz-Latn-TL": "mkz", + "ml-Mlym-IN": "ml", + "mla-Latn-VU": "mla", + "mlb-Latn-CM": "mlb", + "mlc-Latn-VN": "mlc", + "mle-Latn-ZZ": "mle", + "mlf-Latn-LA": "mlf-Latn", + "mlf-Thai-LA": "mlf", + "mlh-Latn-PG": "mlh", + "mli-Latn-ID": "mli", + "mlj-Latn-TD": "mlj", + "mlk-Latn-KE": "mlk", + "mll-Latn-VU": "mll", + "mln-Latn-SB": "mln", + "mlo-Latn-SN": "mlo", + "mlp-Latn-ZZ": "mlp", + "mlq-Arab-SN": "mlq-Arab", + "mlq-Latn-SN": "mlq", + "mlr-Latn-CM": "mlr", + "mls-Latn-SD": "mls", + "mlu-Latn-SB": "mlu", + "mlv-Latn-VU": "mlv", + "mlw-Latn-CM": "mlw", + "mlx-Latn-VU": "mlx", + "mlz-Latn-PH": "mlz", + "mma-Latn-NG": "mma", + "mmb-Latn-ID": "mmb", + "mmc-Latn-MX": "mmc", + "mmd-Hans-CN": "mmd-Hans", + "mmd-Hant-CN": "mmd-Hant", + "mmd-Latn-CN": "mmd", + "mme-Latn-VU": "mme", + "mmf-Latn-NG": "mmf", + "mmg-Latn-VU": "mmg", + "mmh-Latn-BR": "mmh", + "mmi-Latn-PG": "mmi", + "mmm-Latn-VU": "mmm", + "mmn-Latn-PH": "mmn", + "mmo-Latn-ZZ": "mmo", + "mmp-Latn-PG": "mmp", + "mmq-Latn-PG": "mmq", + "mmr-Latn-CN": "mmr", + "mmt-Latn-PG": "mmt", + "mmu-Latn-ZZ": "mmu", + "mmv-Latn-BR": "mmv", + "mmw-Latn-VU": "mmw", + "mmx-Latn-ZZ": "mmx", + "mmy-Latn-TD": "mmy", + "mmz-Latn-CD": "mmz", + "mn-Cyrl-MN": "mn", + "mn-Mong-CN": "mn-CN", + "mna-Latn-ZZ": "mna", + "mnb-Latn-ID": "mnb", + "mnd-Latn-BR": "mnd", + "mne-Latn-TD": "mne", + "mnf-Latn-ZZ": "mnf", + "mng-Latn-VN": "mng", + "mnh-Latn-CD": "mnh", + "mni-Beng-IN": "mni", + "mni-Mtei-IN": "mni-Mtei", + "mnj-Arab-AF": "mnj", + "mnl-Latn-VU": "mnl", + "mnm-Latn-PG": "mnm", + "mnn-Latn-VN": "mnn", + "mnp-Latn-CN": "mnp", + "mnq-Latn-MY": "mnq", + "mnr-Latn-US": "mnr", + "mns-Cyrl-RU": "mns", + "mnu-Latn-ID": "mnu", + "mnv-Latn-SB": "mnv", + "mnw-Mymr-MM": "mnw", + "mnw-Mymr-TH": "mnw-TH", + "mnx-Latn-ID": "mnx", + "mny-Latn-MZ": "mny", + "mnz-Latn-ID": "mnz", + "moa-Latn-ZZ": "moa", + "moc-Latn-AR": "moc", + "mod-Latn-US": "mod", + "moe-Latn-CA": "moe", + "mog-Latn-ID": "mog", + "moh-Latn-CA": "moh", + "moi-Latn-NG": "moi", + "moj-Latn-CG": "moj", + "mok-Latn-ID": "mok", + "mom-Latn-NI": "mom", + "moo-Latn-VN": "moo", + "mop-Latn-BZ": "mop", + "moq-Latn-ID": "moq", + "mor-Latn-SD": "mor", + "mos-Latn-BF": "mos", + "mot-Latn-CO": "mot", + "mou-Latn-TD": "mou", + "mov-Latn-US": "mov", + "mow-Latn-CG": "mow", + "mox-Latn-ZZ": "mox", + "moy-Ethi-ET": "moy-Ethi", + "moy-Latn-ET": "moy", + "moz-Latn-TD": "moz", + "mpa-Latn-TZ": "mpa", + "mpb-Latn-AU": "mpb", + "mpc-Latn-AU": "mpc", + "mpd-Latn-BR": "mpd", + "mpe-Ethi-ET": "mpe-Ethi", + "mpe-Latn-ET": "mpe", + "mpg-Latn-TD": "mpg", + "mph-Latn-AU": "mph", + "mpi-Latn-CM": "mpi", + "mpj-Latn-AU": "mpj", + "mpk-Latn-TD": "mpk", + "mpl-Latn-PG": "mpl", + "mpm-Latn-MX": "mpm", + "mpn-Latn-PG": "mpn", + "mpo-Latn-PG": "mpo", + "mpp-Latn-ZZ": "mpp", + "mpq-Latn-BR": "mpq", + "mpr-Latn-SB": "mpr", + "mps-Latn-ZZ": "mps", + "mpt-Latn-ZZ": "mpt", + "mpu-Latn-BR": "mpu", + "mpv-Latn-PG": "mpv", + "mpw-Latn-BR": "mpw", + "mpx-Latn-ZZ": "mpx", + "mpy-Latn-ID": "mpy", + "mpz-Thai-TH": "mpz", + "mqa-Latn-ID": "mqa", + "mqb-Latn-CM": "mqb", + "mqc-Latn-ID": "mqc", + "mqe-Latn-PG": "mqe", + "mqf-Latn-ID": "mqf", + "mqg-Latn-ID": "mqg", + "mqh-Latn-MX": "mqh", + "mqi-Latn-ID": "mqi", + "mqj-Latn-ID": "mqj", + "mqk-Latn-PH": "mqk", + "mql-Latn-ZZ": "mql", + "mqm-Latn-PF": "mqm", + "mqn-Latn-ID": "mqn", + "mqo-Latn-ID": "mqo", + "mqp-Latn-ID": "mqp", + "mqq-Latn-MY": "mqq", + "mqr-Latn-ID": "mqr", + "mqs-Latn-ID": "mqs", + "mqu-Latn-SS": "mqu", + "mqv-Latn-PG": "mqv", + "mqw-Latn-PG": "mqw", + "mqx-Bugi-ID": "mqx-Bugi", + "mqx-Latn-ID": "mqx", + "mqy-Latn-ID": "mqy", + "mqz-Latn-PG": "mqz", + "mr-Deva-IN": "mr", + "mr-Modi-IN": "mr-Modi", + "mra-Thai-TH": "mra", + "mrb-Latn-VU": "mrb", + "mrc-Latn-US": "mrc", + "mrd-Deva-NP": "mrd", + "mrf-Latn-ID": "mrf", + "mrg-Beng-IN": "mrg-Beng", + "mrg-Deva-IN": "mrg-Deva", + "mrg-Latn-IN": "mrg", + "mrh-Latn-IN": "mrh", + "mrj-Cyrl-RU": "mrj", + "mrk-Latn-NC": "mrk", + "mrl-Latn-FM": "mrl", + "mrm-Latn-VU": "mrm", + "mrn-Latn-SB": "mrn", + "mro-Mroo-BD": "mro", + "mrp-Latn-VU": "mrp", + "mrq-Latn-PF": "mrq", + "mrr-Deva-IN": "mrr", + "mrs-Latn-VU": "mrs", + "mrt-Latn-NG": "mrt", + "mru-Latn-CM": "mru", + "mrv-Latn-PF": "mrv", + "mrw-Arab-PH": "mrw-Arab", + "mrw-Latn-PH": "mrw", + "mrx-Latn-ID": "mrx", + "mry-Latn-PH": "mry", + "mrz-Latn-ID": "mrz", + "ms-Arab-CC": "ms-CC", + "ms-Arab-ID": "ms-Arab-ID", + "ms-Latn-BN": "ms-BN", + "ms-Latn-MY": "ms", + "msb-Latn-PH": "msb", + "msc-Latn-GN": "msc", + "mse-Latn-TD": "mse", + "msf-Latn-ID": "msf", + "msg-Latn-ID": "msg", + "msh-Latn-MG": "msh", + "msi-Latn-MY": "msi", + "msj-Latn-CD": "msj", + "msk-Latn-PH": "msk", + "msl-Latn-ID": "msl", + "msm-Latn-PH": "msm", + "msn-Latn-VU": "msn", + "mso-Latn-ID": "mso", + "msp-Latn-BR": "msp", + "msq-Latn-NC": "msq", + "mss-Latn-ID": "mss", + "msu-Latn-PG": "msu", + "msv-Latn-CM": "msv", + "msw-Latn-GW": "msw", + "msx-Latn-PG": "msx", + "msy-Latn-PG": "msy", + "msz-Latn-PG": "msz", + "mt-Latn-MT": "mt", + "mta-Latn-PH": "mta", + "mtb-Latn-CI": "mtb", + "mtc-Latn-ZZ": "mtc", + "mtd-Latn-ID": "mtd", + "mte-Latn-SB": "mte", + "mtf-Latn-ZZ": "mtf", + "mtg-Latn-ID": "mtg", + "mth-Latn-ID": "mth", + "mti-Latn-ZZ": "mti", + "mtj-Latn-ID": "mtj", + "mtk-Latn-CM": "mtk", + "mtl-Latn-NG": "mtl", + "mtm-Cyrl-RU": "mtm", + "mtn-Latn-NI": "mtn", + "mto-Latn-MX": "mto", + "mtp-Latn-BO": "mtp", + "mtq-Latn-VN": "mtq", + "mtr-Deva-IN": "mtr", + "mts-Latn-PE": "mts", + "mtt-Latn-VU": "mtt", + "mtu-Latn-MX": "mtu", + "mtv-Latn-PG": "mtv", + "mtw-Latn-PH": "mtw", + "mtx-Latn-MX": "mtx", + "mty-Latn-PG": "mty", + "mua-Latn-CM": "mua", + "mub-Latn-TD": "mub", + "muc-Latn-CM": "muc", + "mud-Cyrl-RU": "mud", + "mue-Latn-EC": "mue", + "mug-Latn-CM": "mug", + "muh-Latn-SS": "muh", + "mui-Latn-ID": "mui", + "muj-Latn-TD": "muj", + "muk-Tibt-NP": "muk", + "mum-Latn-PG": "mum", + "muo-Latn-CM": "muo", + "muq-Latn-CN": "muq", + "mur-Latn-ZZ": "mur", + "mus-Latn-US": "mus", + "mut-Deva-IN": "mut", + "muu-Latn-KE": "muu", + "muv-Taml-IN": "muv", + "mux-Latn-PG": "mux", + "muy-Latn-CM": "muy", + "muz-Ethi-ET": "muz", + "muz-Latn-ET": "muz-Latn", + "mva-Latn-ZZ": "mva", + "mvd-Latn-ID": "mvd", + "mvf-Mong-CN": "mvf", + "mvf-Phag-CN": "mvf-Phag", + "mvg-Latn-MX": "mvg", + "mvh-Latn-TD": "mvh", + "mvk-Latn-PG": "mvk", + "mvl-Latn-AU": "mvl", + "mvn-Latn-ZZ": "mvn", + "mvo-Latn-SB": "mvo", + "mvp-Latn-ID": "mvp", + "mvq-Latn-PG": "mvq", + "mvr-Latn-ID": "mvr", + "mvs-Latn-ID": "mvs", + "mvt-Latn-VU": "mvt", + "mvu-Latn-TD": "mvu", + "mvv-Latn-MY": "mvv", + "mvw-Latn-TZ": "mvw", + "mvx-Latn-ID": "mvx", + "mvy-Arab-PK": "mvy", + "mvz-Arab-ET": "mvz-Arab", + "mvz-Ethi-ET": "mvz", + "mwa-Latn-PG": "mwa", + "mwb-Latn-PG": "mwb", + "mwc-Latn-PG": "mwc", + "mwe-Latn-TZ": "mwe", + "mwf-Latn-AU": "mwf", + "mwg-Latn-PG": "mwg", + "mwh-Latn-PG": "mwh", + "mwi-Latn-VU": "mwi", + "mwk-Latn-ML": "mwk", + "mwl-Latn-PT": "mwl", + "mwm-Latn-TD": "mwm", + "mwn-Latn-ZM": "mwn", + "mwo-Latn-VU": "mwo", + "mwp-Latn-AU": "mwp", + "mwq-Latn-MM": "mwq", + "mwr-Deva-IN": "mwr", + "mws-Latn-KE": "mws", + "mwt-Mymr-MM": "mwt", + "mwt-Thai-TH": "mwt-Thai", + "mwu-Latn-SS": "mwu", + "mwv-Latn-ID": "mwv", + "mww-Hmnp-US": "mww", + "mwz-Latn-CD": "mwz", + "mxa-Latn-MX": "mxa", + "mxb-Latn-MX": "mxb", + "mxc-Latn-ZW": "mxc", + "mxd-Latn-ID": "mxd", + "mxe-Latn-VU": "mxe", + "mxf-Latn-CM": "mxf", + "mxg-Latn-AO": "mxg", + "mxh-Latn-CD": "mxh", + "mxi-Latn-ES": "mxi", + "mxj-Latn-IN": "mxj", + "mxk-Latn-PG": "mxk", + "mxl-Latn-BJ": "mxl", + "mxm-Latn-ZZ": "mxm", + "mxn-Latn-ID": "mxn", + "mxo-Latn-ZM": "mxo", + "mxp-Latn-MX": "mxp", + "mxq-Latn-MX": "mxq", + "mxr-Latn-MY": "mxr", + "mxs-Latn-MX": "mxs", + "mxt-Latn-MX": "mxt", + "mxu-Latn-CM": "mxu", + "mxv-Latn-MX": "mxv", + "mxw-Latn-PG": "mxw", + "mxx-Latn-CI": "mxx", + "mxy-Latn-MX": "mxy", + "mxz-Latn-ID": "mxz", + "my-Mymr-MM": "my", + "myb-Latn-TD": "myb", + "myc-Latn-CD": "myc", + "mye-Latn-GA": "mye", + "myf-Latn-ET": "myf", + "myg-Latn-CM": "myg", + "myh-Latn-US": "myh", + "myj-Latn-SS": "myj", + "myk-Latn-ZZ": "myk", + "myl-Latn-ID": "myl", + "mym-Ethi-ZZ": "mym", + "myp-Latn-BR": "myp", + "myr-Latn-PE": "myr", + "myu-Latn-BR": "myu", + "myv-Cyrl-RU": "myv", + "myw-Latn-ZZ": "myw", + "myx-Latn-UG": "myx", + "myy-Latn-CO": "myy", + "myz-Mand-IR": "myz", + "mza-Latn-MX": "mza", + "mzd-Latn-CM": "mzd", + "mze-Latn-PG": "mze", + "mzh-Latn-AR": "mzh", + "mzi-Latn-MX": "mzi", + "mzj-Latn-LR": "mzj", + "mzk-Latn-ZZ": "mzk", + "mzl-Latn-MX": "mzl", + "mzm-Latn-ZZ": "mzm", + "mzn-Arab-IR": "mzn", + "mzo-Latn-BR": "mzo", + "mzp-Latn-ZZ": "mzp", + "mzq-Latn-ID": "mzq", + "mzr-Latn-BR": "mzr", + "mzt-Latn-MY": "mzt", + "mzu-Latn-PG": "mzu", + "mzv-Latn-CF": "mzv", + "mzw-Latn-ZZ": "mzw", + "mzx-Latn-GY": "mzx", + "mzz-Latn-ZZ": "mzz", + "na-Latn-NR": "na", + "naa-Latn-ID": "naa", + "nab-Latn-BR": "nab", + "nac-Latn-ZZ": "nac", + "nae-Latn-ID": "nae", + "naf-Latn-ZZ": "naf", + "nag-Latn-IN": "nag", + "naj-Latn-GN": "naj", + "nak-Latn-ZZ": "nak", + "nal-Latn-PG": "nal", + "nam-Latn-AU": "nam", + "nan-Hans-CN": "nan", + "nao-Deva-NP": "nao", + "nap-Latn-IT": "nap", + "naq-Latn-NA": "naq", + "nar-Latn-NG": "nar", + "nas-Latn-ZZ": "nas", + "nat-Latn-NG": "nat", + "naw-Latn-GH": "naw", + "nax-Latn-PG": "nax", + "nay-Latn-AU": "nay", + "naz-Latn-MX": "naz", + "nb-Latn-NO": "nb", + "nb-Latn-SJ": "nb-SJ", + "nba-Latn-AO": "nba", + "nbb-Latn-NG": "nbb", + "nbc-Latn-IN": "nbc", + "nbd-Latn-CD": "nbd", + "nbe-Latn-IN": "nbe", + "nbh-Latn-NG": "nbh", + "nbi-Latn-IN": "nbi", + "nbj-Latn-AU": "nbj", + "nbk-Latn-PG": "nbk", + "nbm-Latn-CF": "nbm", + "nbn-Latn-ID": "nbn", + "nbo-Latn-NG": "nbo", + "nbp-Latn-NG": "nbp", + "nbq-Latn-ID": "nbq", + "nbr-Latn-NG": "nbr", + "nbt-Deva-IN": "nbt-Deva", + "nbt-Latn-IN": "nbt", + "nbu-Latn-IN": "nbu", + "nbv-Latn-CM": "nbv", + "nbw-Latn-CD": "nbw", + "nby-Latn-PG": "nby", + "nca-Latn-ZZ": "nca", + "ncb-Deva-IN": "ncb-Deva", + "ncb-Latn-IN": "ncb", + "ncc-Latn-PG": "ncc", + "ncd-Deva-NP": "ncd", + "nce-Latn-ZZ": "nce", + "ncf-Latn-ZZ": "ncf", + "ncg-Latn-CA": "ncg", + "nch-Latn-MX": "nch", + "nci-Latn-MX": "nci", + "ncj-Latn-MX": "ncj", + "nck-Latn-AU": "nck", + "ncl-Latn-MX": "ncl", + "ncm-Latn-PG": "ncm", + "ncn-Latn-PG": "ncn", + "nco-Latn-ZZ": "nco", + "ncq-Laoo-LA": "ncq", + "ncq-Thai-LA": "ncq-Thai", + "ncr-Latn-CM": "ncr", + "nct-Beng-IN": "nct-Beng", + "nct-Latn-IN": "nct", + "ncu-Latn-ZZ": "ncu", + "ncx-Latn-MX": "ncx", + "ncz-Latn-US": "ncz", + "nd-Latn-ZW": "nd", + "nda-Latn-CG": "nda", + "ndb-Latn-CM": "ndb", + "ndc-Latn-MZ": "ndc", + "ndd-Latn-NG": "ndd", + "ndf-Cyrl-RU": "ndf", + "ndg-Latn-TZ": "ndg", + "ndh-Latn-TZ": "ndh", + "ndi-Latn-NG": "ndi", + "ndj-Latn-TZ": "ndj", + "ndk-Latn-CD": "ndk", + "ndl-Latn-CD": "ndl", + "ndm-Latn-TD": "ndm", + "ndn-Latn-CG": "ndn", + "ndp-Latn-UG": "ndp", + "ndq-Latn-AO": "ndq", + "ndr-Latn-NG": "ndr", + "nds-Latn-DE": "nds", + "ndt-Latn-CD": "ndt", + "ndu-Latn-CM": "ndu", + "ndv-Latn-SN": "ndv", + "ndw-Latn-CD": "ndw", + "ndx-Latn-ID": "ndx", + "ndy-Latn-CF": "ndy", + "ndy-Latn-TD": "ndy-TD", + "ndz-Latn-SS": "ndz", + "ne-Deva-BT": "ne-BT", + "ne-Deva-NP": "ne", + "nea-Latn-ID": "nea", + "neb-Latn-ZZ": "neb", + "nec-Latn-ID": "nec", + "ned-Latn-NG": "ned", + "nee-Latn-NC": "nee", + "neg-Cyrl-RU": "neg", + "neh-Tibt-BT": "neh", + "nei-Xsux-TR": "nei", + "nej-Latn-PG": "nej", + "nek-Latn-NC": "nek", + "nem-Latn-NC": "nem", + "nen-Latn-NC": "nen", + "neo-Latn-VN": "neo", + "neq-Latn-MX": "neq", + "ner-Latn-ID": "ner", + "net-Latn-PG": "net", + "neu-Latn-001": "neu", + "new-Deva-NP": "new", + "new-Newa-NP": "new-Newa", + "nex-Latn-ZZ": "nex", + "ney-Latn-CI": "ney", + "nez-Latn-US": "nez", + "nfa-Latn-ID": "nfa", + "nfd-Latn-NG": "nfd", + "nfl-Latn-SB": "nfl", + "nfr-Latn-ZZ": "nfr", + "nfu-Latn-CM": "nfu", + "ng-Latn-NA": "ng", + "nga-Latn-ZZ": "nga", + "ngb-Latn-ZZ": "ngb", + "ngc-Latn-CD": "ngc", + "ngd-Latn-CF": "ngd", + "nge-Latn-CM": "nge", + "ngg-Latn-CF": "ngg", + "ngh-Latn-ZA": "ngh", + "ngi-Latn-NG": "ngi", + "ngj-Latn-CM": "ngj", + "ngk-Latn-AU": "ngk", + "ngl-Latn-MZ": "ngl", + "ngm-Latn-FM": "ngm", + "ngn-Latn-CM": "ngn", + "ngp-Latn-TZ": "ngp", + "ngq-Latn-TZ": "ngq", + "ngr-Latn-SB": "ngr", + "ngs-Latn-NG": "ngs", + "ngt-Laoo-LA": "ngt", + "ngu-Latn-MX": "ngu", + "ngv-Latn-CM": "ngv", + "ngw-Latn-NG": "ngw", + "ngx-Latn-NG": "ngx", + "ngy-Latn-CM": "ngy", + "ngz-Latn-CG": "ngz", + "nha-Latn-AU": "nha", + "nhb-Latn-ZZ": "nhb", + "nhc-Latn-MX": "nhc", + "nhd-Latn-PY": "nhd", + "nhe-Latn-MX": "nhe", + "nhf-Latn-AU": "nhf", + "nhg-Latn-MX": "nhg", + "nhi-Latn-MX": "nhi", + "nhk-Latn-MX": "nhk", + "nhm-Latn-MX": "nhm", + "nhn-Latn-MX": "nhn", + "nho-Latn-PG": "nho", + "nhp-Latn-MX": "nhp", + "nhq-Latn-MX": "nhq", + "nhr-Latn-BW": "nhr", + "nht-Latn-MX": "nht", + "nhu-Latn-CM": "nhu", + "nhv-Latn-MX": "nhv", + "nhw-Latn-MX": "nhw", + "nhx-Latn-MX": "nhx", + "nhy-Latn-MX": "nhy", + "nhz-Latn-MX": "nhz", + "nia-Latn-ID": "nia", + "nib-Latn-PG": "nib", + "nid-Latn-AU": "nid", + "nie-Latn-TD": "nie", + "nif-Latn-ZZ": "nif", + "nig-Latn-AU": "nig", + "nih-Latn-TZ": "nih", + "nii-Latn-ZZ": "nii", + "nij-Latn-ID": "nij", + "nil-Latn-ID": "nil", + "nim-Latn-TZ": "nim", + "nin-Latn-ZZ": "nin", + "nio-Cyrl-RU": "nio", + "niq-Latn-KE": "niq", + "nir-Latn-ID": "nir", + "nis-Latn-PG": "nis", + "nit-Telu-IN": "nit", + "niu-Latn-NU": "niu", + "niv-Cyrl-RU": "niv", + "niv-Latn-RU": "niv-Latn", + "niw-Latn-PG": "niw", + "nix-Latn-CD": "nix", + "niy-Latn-ZZ": "niy", + "niz-Latn-ZZ": "niz", + "nja-Latn-NG": "nja", + "njb-Latn-IN": "njb", + "njd-Latn-TZ": "njd", + "njh-Latn-IN": "njh", + "nji-Latn-AU": "nji", + "njj-Latn-CM": "njj", + "njl-Latn-SS": "njl", + "njm-Latn-IN": "njm", + "njn-Latn-IN": "njn", + "njo-Latn-IN": "njo", + "njr-Latn-NG": "njr", + "njs-Latn-ID": "njs", + "njt-Latn-SR": "njt", + "nju-Latn-AU": "nju", + "njx-Latn-CG": "njx", + "njy-Latn-CM": "njy", + "njz-Beng-IN": "njz-Beng", + "njz-Latn-IN": "njz", + "nka-Latn-ZM": "nka", + "nkb-Latn-IN": "nkb", + "nkc-Latn-CM": "nkc", + "nkd-Latn-IN": "nkd", + "nke-Latn-SB": "nke", + "nkf-Latn-IN": "nkf", + "nkg-Latn-ZZ": "nkg", + "nkh-Latn-IN": "nkh", + "nki-Beng-IN": "nki-Beng", + "nki-Latn-IN": "nki", + "nkj-Latn-ID": "nkj", + "nkk-Latn-VU": "nkk", + "nkm-Latn-PG": "nkm", + "nkn-Latn-AO": "nkn", + "nko-Latn-ZZ": "nko", + "nkq-Latn-GH": "nkq", + "nkr-Latn-FM": "nkr", + "nks-Latn-ID": "nks", + "nkt-Latn-TZ": "nkt", + "nku-Latn-CI": "nku", + "nkv-Latn-MW": "nkv", + "nkw-Latn-CD": "nkw", + "nkx-Latn-NG": "nkx", + "nkz-Latn-NG": "nkz", + "nl-Latn-AW": "nl-AW", + "nl-Latn-BE": "nl-BE", + "nl-Latn-NL": "nl", + "nl-Latn-SR": "nl-SR", + "nla-Latn-CM": "nla", + "nlc-Latn-ID": "nlc", + "nle-Latn-KE": "nle", + "nlg-Latn-SB": "nlg", + "nli-Arab-AF": "nli", + "nlj-Latn-CD": "nlj", + "nlk-Latn-ID": "nlk", + "nlm-Arab-PK": "nlm", + "nlo-Latn-CD": "nlo", + "nlq-Latn-MM": "nlq", + "nlu-Latn-GH": "nlu", + "nlv-Latn-MX": "nlv", + "nlw-Latn-AU": "nlw", + "nlx-Deva-IN": "nlx", + "nly-Latn-AU": "nly", + "nlz-Latn-SB": "nlz", + "nma-Latn-IN": "nma", + "nmb-Latn-VU": "nmb", + "nmc-Latn-TD": "nmc", + "nmd-Latn-GA": "nmd", + "nme-Latn-IN": "nme", + "nmf-Latn-IN": "nmf", + "nmg-Latn-CM": "nmg", + "nmh-Latn-IN": "nmh", + "nmi-Latn-NG": "nmi", + "nmj-Latn-CF": "nmj", + "nmk-Latn-VU": "nmk", + "nml-Latn-CM": "nml", + "nmm-Deva-NP": "nmm", + "nmm-Tibt-NP": "nmm-Tibt", + "nmn-Latn-BW": "nmn", + "nmo-Beng-IN": "nmo-Beng", + "nmo-Latn-IN": "nmo", + "nmp-Latn-AU": "nmp", + "nmq-Latn-ZW": "nmq", + "nmr-Latn-CM": "nmr", + "nms-Latn-VU": "nms", + "nmt-Latn-FM": "nmt", + "nmu-Latn-US": "nmu", + "nmv-Latn-AU": "nmv", + "nmw-Latn-PG": "nmw", + "nmx-Latn-PG": "nmx", + "nmz-Latn-ZZ": "nmz", + "nn-Latn-NO": "nn", + "nna-Latn-AU": "nna", + "nnb-Latn-CD": "nnb", + "nnc-Latn-TD": "nnc", + "nnd-Latn-VU": "nnd", + "nne-Latn-AO": "nne", + "nnf-Latn-ZZ": "nnf", + "nng-Beng-IN": "nng-Beng", + "nng-Latn-IN": "nng", + "nnh-Latn-CM": "nnh", + "nni-Latn-ID": "nni", + "nnj-Latn-ET": "nnj", + "nnk-Latn-ZZ": "nnk", + "nnl-Latn-IN": "nnl", + "nnm-Latn-ZZ": "nnm", + "nnn-Latn-TD": "nnn", + "nnp-Wcho-IN": "nnp", + "nnq-Latn-TZ": "nnq", + "nnr-Latn-AU": "nnr", + "nnt-Latn-US": "nnt", + "nnu-Latn-GH": "nnu", + "nnv-Latn-AU": "nnv", + "nnw-Latn-BF": "nnw", + "nny-Latn-AU": "nny", + "nnz-Latn-CM": "nnz", + "no-Latn-NO": "no", + "noa-Latn-CO": "noa", + "noc-Latn-PG": "noc", + "nod-Lana-TH": "nod", + "noe-Deva-IN": "noe", + "nof-Latn-PG": "nof", + "nog-Cyrl-RU": "nog", + "noh-Latn-PG": "noh", + "noi-Deva-IN": "noi", + "noj-Latn-CO": "noj", + "nok-Latn-US": "nok", + "nom-Latn-PE": "nom", + "non-Runr-SE": "non", + "nop-Latn-ZZ": "nop", + "noq-Latn-CD": "noq", + "nos-Yiii-CN": "nos", + "not-Latn-PE": "not", + "nou-Latn-ZZ": "nou", + "nov-Latn-001": "nov", + "now-Latn-TZ": "now", + "noy-Latn-TD": "noy", + "npb-Tibt-BT": "npb", + "npg-Latn-MM": "npg", + "nph-Latn-IN": "nph", + "npl-Latn-MX": "npl", + "npn-Latn-PG": "npn", + "npo-Latn-IN": "npo", + "nps-Latn-ID": "nps", + "npu-Latn-IN": "npu", + "npx-Latn-SB": "npx", + "npy-Latn-ID": "npy", + "nqg-Latn-BJ": "nqg", + "nqk-Latn-BJ": "nqk", + "nql-Latn-AO": "nql", + "nqm-Latn-ID": "nqm", + "nqn-Latn-PG": "nqn", + "nqo-Nkoo-GN": "nqo", + "nqq-Latn-MM": "nqq", + "nqt-Latn-NG": "nqt", + "nqy-Latn-MM": "nqy", + "nr-Latn-ZA": "nr", + "nra-Latn-GA": "nra", + "nrb-Latn-ZZ": "nrb", + "nre-Latn-IN": "nre", + "nrf-Latn-JE": "nrf", + "nrg-Latn-VU": "nrg", + "nri-Latn-IN": "nri", + "nrk-Latn-AU": "nrk", + "nrl-Latn-AU": "nrl", + "nrm-Latn-MY": "nrm", + "nrp-Latn-IT": "nrp", + "nru-Hans-CN": "nru-Hans", + "nru-Hant-CN": "nru-Hant", + "nru-Latn-CN": "nru", + "nrx-Latn-AU": "nrx", + "nrz-Latn-PG": "nrz", + "nsa-Latn-IN": "nsa", + "nsb-Latn-ZA": "nsb", + "nsc-Latn-NG": "nsc", + "nsd-Yiii-CN": "nsd", + "nse-Latn-ZM": "nse", + "nsf-Yiii-CN": "nsf", + "nsg-Latn-TZ": "nsg", + "nsh-Latn-CM": "nsh", + "nsk-Cans-CA": "nsk", + "nsm-Latn-IN": "nsm", + "nsn-Latn-ZZ": "nsn", + "nso-Latn-ZA": "nso", + "nsq-Latn-US": "nsq", + "nss-Latn-ZZ": "nss", + "nst-Tnsa-IN": "nst", + "nsu-Latn-MX": "nsu", + "nsv-Yiii-CN": "nsv", + "nsw-Latn-VU": "nsw", + "nsx-Latn-AO": "nsx", + "nsy-Latn-ID": "nsy", + "nsz-Latn-US": "nsz", + "ntd-Latn-MY": "ntd", + "nte-Latn-MZ": "nte", + "ntg-Latn-AU": "ntg", + "nti-Latn-BF": "nti", + "ntj-Latn-AU": "ntj", + "ntk-Latn-TZ": "ntk", + "ntm-Latn-ZZ": "ntm", + "nto-Latn-CD": "nto", + "ntp-Latn-MX": "ntp", + "ntr-Latn-ZZ": "ntr", + "ntu-Latn-SB": "ntu", + "ntx-Latn-MM": "ntx", + "nty-Yiii-VN": "nty", + "ntz-Arab-IR": "ntz", + "nua-Latn-NC": "nua", + "nuc-Latn-BR": "nuc", + "nud-Latn-PG": "nud", + "nue-Latn-CD": "nue", + "nuf-Latn-CN": "nuf", + "nug-Latn-AU": "nug", + "nuh-Latn-NG": "nuh", + "nui-Latn-ZZ": "nui", + "nuj-Latn-UG": "nuj", + "nuk-Latn-CA": "nuk", + "num-Latn-TO": "num", + "nun-Latn-MM": "nun", + "nuo-Latn-VN": "nuo", + "nup-Latn-ZZ": "nup", + "nuq-Latn-PG": "nuq", + "nur-Latn-PG": "nur", + "nus-Latn-SS": "nus", + "nut-Latn-VN": "nut", + "nuu-Latn-CD": "nuu", + "nuv-Latn-ZZ": "nuv", + "nuw-Latn-FM": "nuw", + "nux-Latn-ZZ": "nux", + "nuy-Latn-AU": "nuy", + "nuz-Latn-MX": "nuz", + "nv-Latn-US": "nv", + "nvh-Latn-VU": "nvh", + "nvm-Latn-PG": "nvm", + "nvo-Latn-CM": "nvo", + "nwb-Latn-ZZ": "nwb", + "nwc-Brah-NP": "nwc-Brah", + "nwc-Deva-NP": "nwc-Deva", + "nwc-Newa-NP": "nwc", + "nwc-Sidd-NP": "nwc-Sidd", + "nwe-Latn-CM": "nwe", + "nwg-Latn-AU": "nwg", + "nwi-Latn-VU": "nwi", + "nwm-Latn-SS": "nwm", + "nwo-Latn-AU": "nwo", + "nwr-Latn-PG": "nwr", + "nww-Latn-TZ": "nww", + "nwx-Deva-NP": "nwx", + "nxa-Latn-TL": "nxa", + "nxd-Latn-CD": "nxd", + "nxe-Latn-ID": "nxe", + "nxg-Latn-ID": "nxg", + "nxi-Latn-TZ": "nxi", + "nxl-Latn-ID": "nxl", + "nxn-Latn-AU": "nxn", + "nxo-Latn-GA": "nxo", + "nxq-Latn-CN": "nxq", + "nxr-Latn-ZZ": "nxr", + "nxx-Latn-ID": "nxx", + "ny-Latn-MW": "ny", + "nyb-Latn-GH": "nyb", + "nyc-Latn-CD": "nyc", + "nyd-Latn-KE": "nyd", + "nye-Latn-AO": "nye", + "nyf-Latn-KE": "nyf", + "nyg-Latn-CD": "nyg", + "nyh-Latn-AU": "nyh", + "nyi-Latn-SD": "nyi", + "nyj-Latn-CD": "nyj", + "nyk-Latn-AO": "nyk", + "nyl-Thai-TH": "nyl", + "nym-Latn-TZ": "nym", + "nyn-Latn-UG": "nyn", + "nyo-Latn-UG": "nyo", + "nyp-Latn-UG": "nyp", + "nyq-Arab-IR": "nyq", + "nyr-Latn-MW": "nyr", + "nys-Latn-AU": "nys", + "nyt-Latn-AU": "nyt", + "nyu-Latn-MZ": "nyu", + "nyv-Latn-AU": "nyv", + "nyx-Latn-AU": "nyx", + "nyy-Latn-TZ": "nyy", + "nza-Latn-CM": "nza", + "nzb-Latn-GA": "nzb", + "nzd-Latn-CD": "nzd", + "nzi-Latn-GH": "nzi", + "nzk-Latn-CF": "nzk", + "nzm-Latn-IN": "nzm", + "nzu-Latn-CG": "nzu", + "nzy-Latn-TD": "nzy", + "nzz-Latn-ML": "nzz", + "oaa-Cyrl-RU": "oaa", + "oac-Cyrl-RU": "oac", + "oar-Syrc-SY": "oar", + "oav-Geor-GE": "oav", + "obi-Latn-US": "obi", + "obk-Latn-PH": "obk", + "obl-Latn-CM": "obl", + "obm-Phnx-JO": "obm", + "obo-Latn-PH": "obo", + "obr-Mymr-MM": "obr", + "obt-Latn-FR": "obt", + "obu-Latn-NG": "obu", + "oc-Latn-FR": "oc", + "oca-Latn-PE": "oca", + "oco-Latn-GB": "oco", + "ocu-Latn-MX": "ocu", + "oda-Latn-NG": "oda", + "odk-Arab-PK": "odk", + "odt-Latn-NL": "odt", + "odu-Latn-NG": "odu", + "ofu-Latn-NG": "ofu", + "ogb-Latn-NG": "ogb", + "ogc-Latn-ZZ": "ogc", + "ogg-Latn-NG": "ogg", + "ogo-Latn-NG": "ogo", + "ogu-Latn-NG": "ogu", + "oht-Xsux-TR": "oht", + "oia-Latn-ID": "oia", + "oie-Latn-SS": "oie", + "oin-Latn-PG": "oin", + "oj-Cans-CA": "oj", + "ojb-Cans-CA": "ojb-Cans", + "ojb-Latn-CA": "ojb", + "ojc-Latn-CA": "ojc", + "ojs-Cans-CA": "ojs", + "ojv-Latn-SB": "ojv", + "ojw-Cans-CA": "ojw-Cans", + "ojw-Latn-CA": "ojw", + "oka-Latn-CA": "oka", + "okb-Latn-NG": "okb", + "okc-Latn-CD": "okc", + "okd-Latn-NG": "okd", + "oke-Latn-NG": "oke", + "okg-Latn-AU": "okg", + "oki-Latn-KE": "oki", + "okk-Latn-PG": "okk", + "okm-Hang-KR": "okm", + "oko-Hani-KR": "oko", + "okr-Latn-ZZ": "okr", + "oks-Latn-NG": "oks", + "oku-Latn-CM": "oku", + "okv-Latn-ZZ": "okv", + "okx-Latn-NG": "okx", + "okz-Khmr-KH": "okz", + "ola-Deva-NP": "ola", + "ola-Tibt-CN": "ola-Tibt", + "old-Latn-TZ": "old", + "ole-Tibt-BT": "ole", + "olk-Latn-AU": "olk", + "olm-Latn-NG": "olm", + "olo-Latn-RU": "olo", + "olr-Latn-VU": "olr", + "olt-Latn-LT": "olt", + "olu-Latn-AO": "olu", + "om-Latn-ET": "om", + "oma-Latn-US": "oma", + "omb-Latn-VU": "omb", + "omc-Latn-PE": "omc", + "omg-Latn-PE": "omg", + "omi-Latn-CD": "omi", + "omk-Cyrl-RU": "omk", + "oml-Latn-CD": "oml", + "omo-Latn-PG": "omo", + "omp-Mtei-IN": "omp", + "omr-Modi-IN": "omr", + "omt-Latn-KE": "omt", + "omu-Latn-PE": "omu", + "omw-Latn-PG": "omw", + "ona-Latn-AR": "ona", + "one-Latn-CA": "one", + "ong-Latn-ZZ": "ong", + "oni-Latn-ID": "oni", + "onj-Latn-PG": "onj", + "onk-Latn-PG": "onk", + "onn-Latn-ZZ": "onn", + "ono-Latn-CA": "ono", + "onp-Deva-IN": "onp-Deva", + "onp-Latn-IN": "onp", + "onr-Latn-PG": "onr", + "ons-Latn-ZZ": "ons", + "ont-Latn-PG": "ont", + "onu-Latn-VU": "onu", + "onx-Latn-ID": "onx", + "ood-Latn-US": "ood", + "oon-Deva-IN": "oon", + "oor-Latn-ZA": "oor", + "opa-Latn-NG": "opa", + "opk-Latn-ID": "opk", + "opm-Latn-ZZ": "opm", + "opo-Latn-PG": "opo", + "opt-Latn-MX": "opt", + "opy-Latn-BR": "opy", + "or-Orya-IN": "or", + "ora-Latn-SB": "ora", + "orc-Latn-KE": "orc", + "ore-Latn-PE": "ore", + "org-Latn-NG": "org", + "orn-Latn-MY": "orn", + "oro-Latn-ZZ": "oro", + "orr-Latn-NG": "orr", + "ors-Latn-MY": "ors", + "ort-Telu-IN": "ort", + "oru-Arab-ZZ": "oru", + "orv-Cyrl-RU": "orv", + "orw-Latn-BR": "orw", + "orx-Latn-NG": "orx", + "orz-Latn-ID": "orz", + "os-Cyrl-GE": "os", + "osa-Osge-US": "osa", + "osc-Ital-IT": "osc", + "osc-Latn-IT": "osc-Latn", + "osi-Java-ID": "osi", + "oso-Latn-NG": "oso", + "osp-Latn-ES": "osp", + "ost-Latn-CM": "ost", + "osu-Latn-PG": "osu", + "osx-Latn-DE": "osx", + "ota-Arab-ZZ": "ota", + "otb-Tibt-CN": "otb", + "otd-Latn-ID": "otd", + "ote-Latn-MX": "ote", + "oti-Latn-BR": "oti", + "otk-Orkh-MN": "otk", + "otl-Latn-MX": "otl", + "otm-Latn-MX": "otm", + "otn-Latn-MX": "otn", + "otq-Latn-MX": "otq", + "otr-Latn-SD": "otr", + "ots-Latn-MX": "ots", + "ott-Latn-MX": "ott", + "otu-Latn-BR": "otu", + "otw-Latn-CA": "otw", + "otx-Latn-MX": "otx", + "oty-Gran-IN": "oty", + "otz-Latn-MX": "otz", + "oub-Latn-LR": "oub", + "oue-Latn-PG": "oue", + "oui-Ougr-143": "oui", + "oum-Latn-PG": "oum", + "ovd-Latn-SE": "ovd", + "owi-Latn-PG": "owi", + "owl-Latn-GB": "owl", + "oyd-Latn-ET": "oyd", + "oym-Latn-BR": "oym", + "oyy-Latn-PG": "oyy", + "ozm-Latn-ZZ": "ozm", + "pa-Arab-PK": "pa-PK", + "pa-Guru-IN": "pa", + "pab-Latn-BR": "pab", + "pac-Latn-VN": "pac", + "pad-Latn-BR": "pad", + "pae-Latn-CD": "pae", + "paf-Latn-BR": "paf", + "pag-Latn-PH": "pag", + "pah-Latn-BR": "pah", + "pai-Latn-NG": "pai", + "pak-Latn-BR": "pak", + "pal-Phli-IR": "pal", + "pal-Phlp-CN": "pal-Phlp", + "pam-Latn-PH": "pam", + "pao-Latn-US": "pao", + "pap-Latn-BQ": "pap-BQ", + "pap-Latn-CW": "pap", + "paq-Cyrl-TJ": "paq", + "par-Latn-US": "par", + "pas-Latn-ID": "pas", + "pau-Latn-PW": "pau", + "pav-Latn-BR": "pav", + "paw-Latn-US": "paw", + "pax-Latn-BR": "pax", + "pay-Latn-HN": "pay", + "paz-Latn-BR": "paz", + "pbb-Latn-CO": "pbb", + "pbc-Latn-GY": "pbc", + "pbe-Latn-MX": "pbe", + "pbf-Latn-MX": "pbf", + "pbg-Latn-VE": "pbg", + "pbh-Latn-VE": "pbh", + "pbi-Latn-ZZ": "pbi", + "pbl-Latn-NG": "pbl", + "pbm-Latn-MX": "pbm", + "pbn-Latn-NG": "pbn", + "pbo-Latn-GW": "pbo", + "pbp-Latn-GN": "pbp", + "pbr-Latn-TZ": "pbr", + "pbs-Latn-MX": "pbs", + "pbt-Arab-AF": "pbt", + "pbv-Latn-IN": "pbv", + "pby-Latn-PG": "pby", + "pca-Latn-MX": "pca", + "pcb-Khmr-KH": "pcb", + "pcc-Hani-CN": "pcc-Hani", + "pcc-Latn-CN": "pcc", + "pcd-Latn-FR": "pcd", + "pce-Mymr-MM": "pce", + "pce-Thai-TH": "pce-Thai", + "pcf-Mlym-IN": "pcf", + "pcg-Knda-IN": "pcg-Knda", + "pcg-Mlym-IN": "pcg", + "pcg-Taml-IN": "pcg-Taml", + "pch-Deva-IN": "pch", + "pci-Deva-IN": "pci", + "pci-Orya-IN": "pci-Orya", + "pcj-Telu-IN": "pcj", + "pck-Latn-IN": "pck", + "pcm-Latn-NG": "pcm", + "pcn-Latn-NG": "pcn", + "pcp-Latn-BO": "pcp", + "pcw-Latn-NG": "pcw", + "pda-Latn-PG": "pda", + "pdc-Latn-US": "pdc", + "pdn-Latn-ID": "pdn", + "pdo-Latn-ID": "pdo", + "pdt-Latn-CA": "pdt", + "pdu-Latn-MM": "pdu", + "pdu-Mymr-MM": "pdu-Mymr", + "pea-Latn-ID": "pea", + "peb-Latn-US": "peb", + "ped-Latn-ZZ": "ped", + "pee-Latn-ID": "pee", + "peg-Orya-IN": "peg", + "pei-Latn-MX": "pei", + "pek-Latn-PG": "pek", + "pel-Latn-ID": "pel", + "pem-Latn-CD": "pem", + "peo-Xpeo-IR": "peo", + "pep-Latn-PG": "pep", + "peq-Latn-US": "peq", + "pev-Latn-VE": "pev", + "pex-Latn-ZZ": "pex", + "pey-Latn-ID": "pey", + "pez-Latn-MY": "pez", + "pfa-Latn-FM": "pfa", + "pfe-Latn-CM": "pfe", + "pfl-Latn-DE": "pfl", + "pga-Latn-SS": "pga", + "pgd-Khar-PK": "pgd", + "pgg-Deva-IN": "pgg", + "pgi-Latn-PG": "pgi", + "pgk-Latn-VU": "pgk", + "pgl-Ogam-IE": "pgl", + "pgn-Ital-IT": "pgn", + "pgs-Latn-NG": "pgs", + "pgu-Latn-ID": "pgu", + "phd-Deva-IN": "phd", + "phg-Latn-VN": "phg", + "phh-Latn-VN": "phh", + "phk-Mymr-IN": "phk", + "phl-Arab-ZZ": "phl", + "phm-Latn-MZ": "phm", + "phn-Phnx-LB": "phn", + "pho-Laoo-LA": "pho", + "phr-Arab-PK": "phr", + "pht-Thai-TH": "pht", + "phv-Arab-AF": "phv", + "phw-Deva-NP": "phw", + "pi-Brah-IN": "pi-Brah", + "pi-Deva-IN": "pi-Deva", + "pi-Khar-IN": "pi-Khar", + "pi-Khmr-IN": "pi-Khmr", + "pi-Mymr-IN": "pi-Mymr", + "pi-Sinh-IN": "pi", + "pi-Thai-IN": "pi-Thai", + "pia-Latn-MX": "pia", + "pib-Latn-PE": "pib", + "pic-Latn-GA": "pic", + "pid-Latn-VE": "pid", + "pif-Latn-FM": "pif", + "pig-Latn-PE": "pig", + "pih-Latn-NF": "pih", + "pij-Latn-CO": "pij", + "pil-Latn-ZZ": "pil", + "pim-Latn-US": "pim", + "pin-Latn-PG": "pin", + "pio-Latn-CO": "pio", + "pip-Latn-ZZ": "pip", + "pir-Latn-BR": "pir", + "pis-Latn-SB": "pis", + "pit-Latn-AU": "pit", + "piu-Latn-AU": "piu", + "piv-Latn-SB": "piv", + "piw-Latn-TZ": "piw", + "pix-Latn-PG": "pix", + "piy-Latn-NG": "piy", + "piz-Latn-NC": "piz", + "pjt-Latn-AU": "pjt", + "pka-Brah-IN": "pka", + "pkb-Latn-KE": "pkb", + "pkg-Latn-PG": "pkg", + "pkh-Deva-BD": "pkh-Deva", + "pkh-Latn-BD": "pkh", + "pkn-Latn-AU": "pkn", + "pko-Latn-KE": "pko", + "pkp-Latn-CK": "pkp", + "pkr-Mlym-IN": "pkr", + "pku-Latn-ID": "pku", + "pl-Latn-PL": "pl", + "pl-Latn-UA": "pl-UA", + "pla-Latn-ZZ": "pla", + "plb-Latn-VU": "plb", + "plc-Latn-PH": "plc", + "pld-Latn-GB": "pld", + "ple-Latn-ID": "ple", + "plg-Latn-AR": "plg", + "plh-Latn-ID": "plh", + "plj-Latn-NG": "plj", + "plk-Arab-PK": "plk", + "pll-Mymr-MM": "pll", + "pln-Latn-CO": "pln", + "plo-Latn-MX": "plo", + "plr-Latn-CI": "plr", + "pls-Latn-MX": "pls", + "plu-Latn-BR": "plu", + "plv-Latn-PH": "plv", + "plw-Latn-PH": "plw", + "plz-Latn-MY": "plz", + "pma-Latn-VU": "pma", + "pmb-Latn-CD": "pmb", + "pmd-Latn-AU": "pmd", + "pme-Latn-NC": "pme", + "pmf-Latn-ID": "pmf", + "pmh-Brah-IN": "pmh", + "pmi-Latn-CN": "pmi", + "pmj-Latn-CN": "pmj", + "pml-Latn-TN": "pml", + "pmm-Latn-CM": "pmm", + "pmn-Latn-CM": "pmn", + "pmo-Latn-ID": "pmo", + "pmq-Latn-MX": "pmq", + "pmr-Latn-PG": "pmr", + "pms-Latn-IT": "pms", + "pmt-Latn-PF": "pmt", + "pmw-Latn-US": "pmw", + "pmx-Latn-IN": "pmx", + "pmy-Latn-ID": "pmy", + "pmz-Latn-MX": "pmz", + "pna-Latn-MY": "pna", + "pnc-Latn-ID": "pnc", + "pnd-Latn-AO": "pnd", + "pne-Latn-MY": "pne", + "png-Latn-ZZ": "png", + "pnh-Latn-CK": "pnh", + "pni-Latn-ID": "pni", + "pnj-Latn-AU": "pnj", + "pnk-Latn-BO": "pnk", + "pnl-Latn-BF": "pnl", + "pnm-Latn-MY": "pnm", + "pnn-Latn-ZZ": "pnn", + "pno-Latn-PE": "pno", + "pnp-Latn-ID": "pnp", + "pnq-Latn-BF": "pnq", + "pnr-Latn-PG": "pnr", + "pns-Latn-ID": "pns", + "pnt-Grek-GR": "pnt", + "pnv-Latn-AU": "pnv", + "pnw-Latn-AU": "pnw", + "pny-Latn-CM": "pny", + "pnz-Latn-CF": "pnz", + "poc-Latn-GT": "poc", + "poe-Latn-MX": "poe", + "pof-Latn-CD": "pof", + "pog-Latn-BR": "pog", + "poh-Latn-GT": "poh", + "poi-Latn-MX": "poi", + "pok-Latn-BR": "pok", + "pom-Latn-US": "pom", + "pon-Latn-FM": "pon", + "poo-Latn-US": "poo", + "pop-Latn-NC": "pop", + "poq-Latn-MX": "poq", + "pos-Latn-MX": "pos", + "pot-Latn-US": "pot", + "pov-Latn-GW": "pov", + "pow-Latn-MX": "pow", + "poy-Latn-TZ": "poy", + "ppe-Latn-PG": "ppe", + "ppi-Latn-MX": "ppi", + "ppk-Latn-ID": "ppk", + "ppl-Latn-SV": "ppl", + "ppm-Latn-ID": "ppm", + "ppn-Latn-PG": "ppn", + "ppo-Latn-ZZ": "ppo", + "ppp-Latn-CD": "ppp", + "ppq-Latn-PG": "ppq", + "pps-Latn-MX": "pps", + "ppt-Latn-PG": "ppt", + "pqa-Latn-NG": "pqa", + "pqm-Latn-CA": "pqm", + "pra-Khar-PK": "pra", + "prc-Arab-AF": "prc", + "prd-Arab-IR": "prd", + "pre-Latn-ST": "pre", + "prf-Latn-PH": "prf", + "prg-Latn-001": "prg", + "prh-Latn-PH": "prh", + "pri-Latn-NC": "pri", + "prk-Latn-MM": "prk", + "prm-Latn-PG": "prm", + "pro-Latn-FR": "pro", + "prp-Gujr-IN": "prp", + "prq-Latn-PE": "prq", + "prr-Latn-BR": "prr", + "prt-Thai-TH": "prt", + "pru-Latn-ID": "pru", + "prw-Latn-PG": "prw", + "prx-Arab-IN": "prx", + "prx-Tibt-IN": "prx-Tibt", + "ps-Arab-AF": "ps", + "psa-Latn-ID": "psa", + "pse-Latn-ID": "pse", + "psh-Arab-AF": "psh", + "psi-Arab-AF": "psi", + "psm-Latn-BO": "psm", + "psn-Latn-ID": "psn", + "psq-Latn-PG": "psq", + "pss-Latn-ZZ": "pss", + "pst-Arab-PK": "pst", + "psw-Latn-VU": "psw", + "pt-Latn-AO": "pt-AO", + "pt-Latn-BR": "pt", + "pt-Latn-CV": "pt-CV", + "pt-Latn-GW": "pt-GW", + "pt-Latn-MO": "pt-MO", + "pt-Latn-MZ": "pt-MZ", + "pt-Latn-PT": "pt-PT", + "pt-Latn-ST": "pt-ST", + "pt-Latn-TL": "pt-TL", + "pta-Latn-PY": "pta", + "pth-Latn-BR": "pth", + "pti-Latn-AU": "pti", + "ptn-Latn-ID": "ptn", + "pto-Latn-BR": "pto", + "ptp-Latn-ZZ": "ptp", + "ptr-Latn-VU": "ptr", + "ptt-Latn-ID": "ptt", + "ptu-Latn-ID": "ptu", + "ptv-Latn-VU": "ptv", + "pua-Latn-MX": "pua", + "pub-Latn-IN": "pub", + "puc-Latn-ID": "puc", + "pud-Latn-ID": "pud", + "pue-Latn-AR": "pue", + "puf-Latn-ID": "puf", + "pug-Latn-BF": "pug", + "pui-Latn-CO": "pui", + "puj-Latn-ID": "puj", + "pum-Deva-NP": "pum", + "puo-Latn-VN": "puo", + "pup-Latn-PG": "pup", + "puq-Latn-PE": "puq", + "pur-Latn-BR": "pur", + "put-Latn-ID": "put", + "puu-Latn-GA": "puu", + "puw-Latn-FM": "puw", + "pux-Latn-PG": "pux", + "puy-Latn-US": "puy", + "pwa-Latn-ZZ": "pwa", + "pwb-Latn-NG": "pwb", + "pwg-Latn-PG": "pwg", + "pwm-Latn-PH": "pwm", + "pwn-Latn-TW": "pwn", + "pwo-Mymr-MM": "pwo", + "pwr-Deva-IN": "pwr", + "pww-Thai-TH": "pww", + "pxm-Latn-MX": "pxm", + "pye-Latn-CI": "pye", + "pym-Latn-NG": "pym", + "pyn-Latn-BR": "pyn", + "pyu-Hani-TW": "pyu-Hani", + "pyu-Latn-TW": "pyu", + "pyx-Mymr-MM": "pyx", + "pyy-Latn-MM": "pyy", + "pzh-Latn-TW": "pzh", + "pzn-Latn-MM": "pzn", + "qu-Latn-PE": "qu", + "qua-Latn-US": "qua", + "qub-Latn-PE": "qub", + "quc-Latn-GT": "quc", + "qud-Latn-EC": "qud", + "quf-Latn-PE": "quf", + "qug-Latn-EC": "qug", + "qui-Latn-US": "qui", + "quk-Latn-PE": "quk", + "qul-Latn-BO": "qul", + "qum-Latn-GT": "qum", + "qun-Latn-US": "qun", + "qup-Latn-PE": "qup", + "quq-Latn-ES": "quq", + "qur-Latn-PE": "qur", + "qus-Latn-AR": "qus", + "quv-Latn-GT": "quv", + "quw-Latn-EC": "quw", + "qux-Latn-PE": "qux", + "quy-Latn-PE": "quy", + "qva-Latn-PE": "qva", + "qvc-Latn-PE": "qvc", + "qve-Latn-PE": "qve", + "qvh-Latn-PE": "qvh", + "qvi-Latn-EC": "qvi", + "qvj-Latn-EC": "qvj", + "qvl-Latn-PE": "qvl", + "qvm-Latn-PE": "qvm", + "qvn-Latn-PE": "qvn", + "qvo-Latn-PE": "qvo", + "qvp-Latn-PE": "qvp", + "qvs-Latn-PE": "qvs", + "qvw-Latn-PE": "qvw", + "qvz-Latn-EC": "qvz", + "qwa-Latn-PE": "qwa", + "qwc-Latn-PE": "qwc", + "qwh-Latn-PE": "qwh", + "qwm-Cyrl-RU": "qwm-Cyrl", + "qwm-Latn-RU": "qwm", + "qwm-Runr-RU": "qwm-Runr", + "qws-Latn-PE": "qws", + "qwt-Latn-US": "qwt", + "qxa-Latn-PE": "qxa", + "qxc-Latn-PE": "qxc", + "qxh-Latn-PE": "qxh", + "qxl-Latn-EC": "qxl", + "qxn-Latn-PE": "qxn", + "qxo-Latn-PE": "qxo", + "qxp-Latn-PE": "qxp", + "qxq-Arab-IR": "qxq", + "qxr-Latn-EC": "qxr", + "qxt-Latn-PE": "qxt", + "qxu-Latn-PE": "qxu", + "qxw-Latn-PE": "qxw", + "qya-Latn-001": "qya", + "qyp-Latn-US": "qyp", + "raa-Deva-NP": "raa", + "rab-Deva-NP": "rab", + "rac-Latn-ID": "rac", + "rad-Latn-VN": "rad", + "raf-Deva-NP": "raf", + "rag-Latn-KE": "rag", + "rah-Beng-IN": "rah", + "rah-Latn-IN": "rah-Latn", + "rai-Latn-ZZ": "rai", + "raj-Deva-IN": "raj", + "rak-Latn-PG": "rak", + "ram-Latn-BR": "ram", + "ran-Latn-ID": "ran", + "rao-Latn-ZZ": "rao", + "rap-Latn-CL": "rap", + "rar-Latn-CK": "rar", + "rav-Deva-NP": "rav", + "raw-Latn-MM": "raw", + "rax-Latn-NG": "rax", + "ray-Latn-PF": "ray", + "raz-Latn-ID": "raz", + "rbb-Mymr-MM": "rbb", + "rbk-Latn-PH": "rbk", + "rbl-Latn-PH": "rbl", + "rbp-Latn-AU": "rbp", + "rcf-Latn-RE": "rcf", + "rdb-Arab-IR": "rdb", + "rea-Latn-PG": "rea", + "reb-Latn-ID": "reb", + "ree-Latn-MY": "ree", + "reg-Latn-TZ": "reg", + "rei-Orya-IN": "rei", + "rei-Telu-IN": "rei-Telu", + "rej-Latn-ID": "rej", + "rej-Rjng-ID": "rej-Rjng", + "rel-Latn-ZZ": "rel", + "rem-Latn-PE": "rem", + "ren-Latn-VN": "ren", + "res-Latn-ZZ": "res", + "ret-Latn-ID": "ret", + "rey-Latn-BO": "rey", + "rga-Latn-VU": "rga", + "rgn-Latn-IT": "rgn", + "rgr-Latn-PE": "rgr", + "rgs-Latn-VN": "rgs", + "rgu-Latn-ID": "rgu", + "rhg-Arab-MM": "rhg-Arab", + "rhg-Rohg-MM": "rhg", + "rhp-Latn-PG": "rhp", + "ria-Latn-IN": "ria", + "rif-Latn-MA": "rif", + "ril-Latn-MM": "ril", + "rim-Latn-TZ": "rim", + "rin-Latn-NG": "rin", + "rir-Latn-ID": "rir", + "rit-Latn-AU": "rit", + "riu-Latn-ID": "riu", + "rjg-Latn-ID": "rjg", + "rji-Deva-NP": "rji", + "rjs-Deva-NP": "rjs", + "rka-Khmr-KH": "rka", + "rkb-Latn-BR": "rkb", + "rkh-Latn-CK": "rkh", + "rki-Mymr-MM": "rki", + "rkm-Latn-BF": "rkm", + "rkt-Beng-BD": "rkt", + "rkw-Latn-AU": "rkw", + "rm-Latn-CH": "rm", + "rma-Latn-NI": "rma", + "rmb-Latn-AU": "rmb", + "rmc-Latn-SK": "rmc", + "rmd-Latn-DK": "rmd", + "rme-Latn-GB": "rme", + "rmf-Latn-FI": "rmf", + "rmg-Latn-NO": "rmg", + "rmh-Latn-ID": "rmh", + "rmi-Armn-AM": "rmi", + "rmk-Latn-PG": "rmk", + "rml-Cyrl-BY": "rml-Cyrl", + "rml-Latn-PL": "rml", + "rmm-Latn-ID": "rmm", + "rmn-Cyrl-BG": "rmn-Cyrl", + "rmn-Grek-GR": "rmn-Grek", + "rmn-Latn-RS": "rmn", + "rmo-Latn-CH": "rmo", + "rmp-Latn-PG": "rmp", + "rmq-Latn-ES": "rmq", + "rmt-Arab-IR": "rmt", + "rmu-Latn-SE": "rmu", + "rmw-Latn-GB": "rmw", + "rmx-Latn-VN": "rmx", + "rmz-Mymr-IN": "rmz", + "rn-Latn-BI": "rn", + "rna-Latn-ZZ": "rna", + "rnd-Latn-CD": "rnd", + "rng-Latn-MZ": "rng", + "rnl-Latn-IN": "rnl", + "rnn-Latn-ID": "rnn", + "rnr-Latn-AU": "rnr", + "rnw-Latn-TZ": "rnw", + "ro-Latn-MD": "ro-MD", + "ro-Latn-RO": "ro", + "rob-Latn-ID": "rob", + "roc-Latn-VN": "roc", + "rod-Latn-NG": "rod", + "roe-Latn-PG": "roe", + "rof-Latn-TZ": "rof", + "rog-Latn-VN": "rog", + "rol-Latn-PH": "rol", + "rom-Cyrl-RO": "rom-Cyrl", + "rom-Latn-RO": "rom", + "roo-Latn-ZZ": "roo", + "rop-Latn-AU": "rop", + "ror-Latn-ID": "ror", + "rou-Latn-TD": "rou", + "row-Latn-ID": "row", + "rpn-Latn-VU": "rpn", + "rpt-Latn-PG": "rpt", + "rri-Latn-SB": "rri", + "rro-Latn-ZZ": "rro", + "rrt-Latn-AU": "rrt", + "rsk-Cyrl-RS": "rsk", + "rtc-Latn-MM": "rtc", + "rth-Latn-ID": "rth", + "rtm-Latn-FJ": "rtm", + "rtw-Deva-IN": "rtw", + "ru-Cyrl-KZ": "ru-KZ", + "ru-Cyrl-RU": "ru", + "rub-Latn-UG": "rub", + "ruc-Latn-UG": "ruc", + "rue-Cyrl-UA": "rue", + "ruf-Latn-TZ": "ruf", + "rug-Latn-SB": "rug", + "rui-Latn-TZ": "rui", + "ruk-Latn-NG": "ruk", + "ruo-Latn-HR": "ruo", + "rup-Grek-GR": "rup-Grek", + "rup-Latn-RO": "rup", + "ruq-Latn-GR": "ruq", + "rut-Cyrl-RU": "rut", + "rut-Latn-AZ": "rut-Latn", + "ruu-Latn-MY": "ruu", + "ruy-Latn-NG": "ruy", + "ruz-Latn-NG": "ruz", + "rw-Latn-RW": "rw", + "rwa-Latn-PG": "rwa", + "rwk-Latn-TZ": "rwk", + "rwl-Latn-TZ": "rwl", + "rwm-Latn-UG": "rwm", + "rwo-Latn-ZZ": "rwo", + "rwr-Deva-IN": "rwr", + "rxd-Latn-AU": "rxd", + "rxw-Latn-AU": "rxw", + "ryu-Kana-JP": "ryu", + "sa-Bhks-IN": "sa-Bhks", + "sa-Deva-IN": "sa", + "sa-Gran-IN": "sa-Gran", + "sa-Nand-IN": "sa-Nand", + "sa-Shrd-IN": "sa-Shrd", + "sa-Sidd-IN": "sa-Sidd", + "saa-Latn-TD": "saa", + "sab-Latn-PA": "sab", + "sac-Latn-US": "sac", + "sad-Latn-TZ": "sad", + "sae-Latn-BR": "sae", + "saf-Latn-GH": "saf", + "sah-Cyrl-RU": "sah", + "saj-Latn-ID": "saj", + "sak-Latn-GA": "sak", + "sam-Hebr-PS": "sam-Hebr", + "sam-Samr-PS": "sam", + "sam-Syrc-PS": "sam-Syrc", + "sao-Latn-ID": "sao", + "saq-Latn-KE": "saq", + "sar-Latn-BO": "sar", + "sas-Latn-ID": "sas", + "sat-Olck-IN": "sat", + "sau-Latn-ID": "sau", + "sav-Latn-SN": "sav", + "saw-Latn-ID": "saw", + "sax-Latn-VU": "sax", + "say-Latn-NG": "say", + "saz-Saur-IN": "saz", + "sba-Latn-ZZ": "sba", + "sbb-Latn-SB": "sbb", + "sbc-Latn-PG": "sbc", + "sbd-Latn-BF": "sbd", + "sbe-Latn-ZZ": "sbe", + "sbg-Latn-ID": "sbg", + "sbh-Latn-PG": "sbh", + "sbi-Latn-PG": "sbi", + "sbj-Latn-TD": "sbj", + "sbk-Latn-TZ": "sbk", + "sbl-Latn-PH": "sbl", + "sbm-Latn-TZ": "sbm", + "sbn-Arab-PK": "sbn", + "sbo-Latn-MY": "sbo", + "sbp-Latn-TZ": "sbp", + "sbq-Latn-PG": "sbq", + "sbr-Latn-ID": "sbr", + "sbs-Latn-NA": "sbs", + "sbt-Latn-ID": "sbt", + "sbu-Deva-IN": "sbu-Deva", + "sbu-Tibt-IN": "sbu", + "sbv-Latn-IT": "sbv", + "sbw-Latn-GA": "sbw", + "sbx-Latn-ID": "sbx", + "sby-Latn-ZM": "sby", + "sbz-Latn-CF": "sbz", + "sc-Latn-IT": "sc", + "scb-Latn-VN": "scb", + "sce-Arab-CN": "sce-Arab", + "sce-Latn-CN": "sce", + "scf-Latn-PA": "scf", + "scg-Latn-ID": "scg", + "sch-Latn-IN": "sch", + "sci-Latn-LK": "sci", + "sck-Deva-IN": "sck", + "scl-Arab-ZZ": "scl", + "scn-Latn-IT": "scn", + "sco-Latn-GB": "sco", + "scp-Deva-NP": "scp", + "scs-Cans-CA": "scs-Cans", + "scs-Latn-CA": "scs", + "sct-Laoo-LA": "sct", + "scu-Takr-IN": "scu", + "scv-Latn-NG": "scv", + "scw-Latn-NG": "scw", + "scx-Grek-IT": "scx", + "sd-Arab-PK": "sd", + "sd-Deva-IN": "sd-IN", + "sd-Khoj-IN": "sd-Khoj", + "sd-Sind-IN": "sd-Sind", + "sda-Latn-ID": "sda", + "sdb-Arab-IQ": "sdb", + "sdc-Latn-IT": "sdc", + "sde-Latn-NG": "sde", + "sdf-Arab-IQ": "sdf", + "sdg-Arab-AF": "sdg", + "sdh-Arab-IR": "sdh", + "sdj-Latn-CG": "sdj", + "sdk-Latn-PG": "sdk", + "sdn-Latn-IT": "sdn", + "sdo-Latn-MY": "sdo", + "sdq-Latn-ID": "sdq", + "sds-Arab-TN": "sds", + "sdu-Latn-ID": "sdu", + "sdx-Latn-MY": "sdx", + "se-Latn-NO": "se", + "sea-Latn-MY": "sea", + "seb-Latn-CI": "seb", + "sec-Latn-CA": "sec", + "sed-Latn-VN": "sed", + "see-Latn-US": "see", + "sef-Latn-CI": "sef", + "seg-Latn-TZ": "seg", + "seh-Latn-MZ": "seh", + "sei-Latn-MX": "sei", + "sej-Latn-PG": "sej", + "sek-Cans-CA": "sek-Cans", + "sek-Latn-CA": "sek", + "sel-Cyrl-RU": "sel", + "sen-Latn-BF": "sen", + "seo-Latn-PG": "seo", + "sep-Latn-BF": "sep", + "seq-Latn-BF": "seq", + "ser-Latn-US": "ser", + "ses-Latn-ML": "ses", + "set-Latn-ID": "set", + "seu-Latn-ID": "seu", + "sev-Latn-CI": "sev", + "sew-Latn-PG": "sew", + "sey-Latn-EC": "sey", + "sez-Latn-MM": "sez", + "sfe-Latn-PH": "sfe", + "sfm-Plrd-CN": "sfm", + "sfw-Latn-GH": "sfw", + "sg-Latn-CF": "sg", + "sga-Ogam-IE": "sga", + "sgb-Latn-PH": "sgb", + "sgc-Latn-KE": "sgc", + "sgd-Latn-PH": "sgd", + "sge-Latn-ID": "sge", + "sgh-Arab-AF": "sgh-Arab", + "sgh-Cyrl-TJ": "sgh", + "sgh-Latn-TJ": "sgh-Latn", + "sgi-Latn-CM": "sgi", + "sgj-Deva-IN": "sgj", + "sgm-Latn-KE": "sgm", + "sgp-Latn-IN": "sgp", + "sgr-Arab-IR": "sgr", + "sgs-Latn-LT": "sgs", + "sgt-Tibt-BT": "sgt", + "sgu-Latn-ID": "sgu", + "sgw-Ethi-ZZ": "sgw", + "sgy-Arab-AF": "sgy", + "sgz-Latn-ZZ": "sgz", + "sha-Latn-NG": "sha", + "shb-Latn-BR": "shb", + "shc-Latn-CD": "shc", + "shd-Arab-PK": "shd", + "she-Latn-ET": "she", + "shg-Latn-BW": "shg", + "shh-Latn-US": "shh", + "shi-Tfng-MA": "shi", + "shj-Latn-SD": "shj", + "shk-Latn-ZZ": "shk", + "shm-Arab-IR": "shm", + "shn-Mymr-MM": "shn", + "sho-Latn-NG": "sho", + "shp-Latn-PE": "shp", + "shq-Latn-ZM": "shq", + "shr-Latn-CD": "shr", + "shs-Latn-CA": "shs", + "sht-Latn-US": "sht", + "shu-Arab-ZZ": "shu", + "shv-Arab-OM": "shv", + "shw-Latn-SD": "shw", + "shy-Arab-DZ": "shy-Arab", + "shy-Latn-DZ": "shy", + "shy-Tfng-DZ": "shy-Tfng", + "shz-Latn-ML": "shz", + "si-Sinh-LK": "si", + "sia-Cyrl-RU": "sia", + "sib-Latn-MY": "sib", + "sid-Latn-ET": "sid", + "sie-Latn-ZM": "sie", + "sif-Latn-BF": "sif", + "sig-Latn-ZZ": "sig", + "sih-Latn-NC": "sih", + "sii-Latn-IN": "sii", + "sij-Latn-PG": "sij", + "sik-Latn-BR": "sik", + "sil-Latn-ZZ": "sil", + "sim-Latn-ZZ": "sim", + "sip-Tibt-IN": "sip", + "siq-Latn-PG": "siq", + "sir-Latn-NG": "sir", + "sis-Latn-US": "sis", + "siu-Latn-PG": "siu", + "siv-Latn-PG": "siv", + "siw-Latn-PG": "siw", + "six-Latn-PG": "six", + "siy-Arab-IR": "siy", + "siz-Arab-EG": "siz", + "sja-Latn-CO": "sja", + "sjb-Latn-ID": "sjb", + "sjd-Cyrl-RU": "sjd", + "sje-Latn-SE": "sje", + "sjg-Latn-TD": "sjg", + "sjl-Latn-IN": "sjl", + "sjm-Latn-PH": "sjm", + "sjp-Beng-IN": "sjp-Beng", + "sjp-Deva-IN": "sjp", + "sjr-Latn-ZZ": "sjr", + "sjt-Cyrl-RU": "sjt", + "sju-Latn-SE": "sju", + "sjw-Latn-US": "sjw", + "sk-Latn-SK": "sk", + "ska-Latn-US": "ska", + "skb-Thai-TH": "skb", + "skc-Latn-ZZ": "skc", + "skd-Latn-US": "skd", + "ske-Latn-VU": "ske", + "skf-Latn-BR": "skf", + "skg-Latn-MG": "skg", + "skh-Latn-ID": "skh", + "ski-Latn-ID": "ski", + "skj-Deva-NP": "skj", + "skm-Latn-PG": "skm", + "skn-Latn-PH": "skn", + "sko-Latn-ID": "sko", + "skp-Latn-MY": "skp", + "skq-Latn-BF": "skq", + "skr-Arab-PK": "skr", + "skr-Mult-PK": "skr-Mult", + "sks-Latn-ZZ": "sks", + "skt-Latn-CD": "skt", + "sku-Latn-VU": "sku", + "skv-Latn-ID": "skv", + "skw-Latn-GY": "skw", + "skx-Latn-ID": "skx", + "sky-Latn-SB": "sky", + "skz-Latn-ID": "skz", + "sl-Latn-SI": "sl", + "slc-Latn-CO": "slc", + "sld-Latn-ZZ": "sld", + "slg-Latn-ID": "slg", + "slh-Latn-US": "slh", + "sli-Latn-PL": "sli", + "slj-Latn-BR": "slj", + "sll-Latn-ZZ": "sll", + "slm-Latn-PH": "slm", + "sln-Latn-US": "sln", + "slp-Latn-ID": "slp", + "slq-Arab-IR": "slq", + "slr-Latn-CN": "slr", + "slu-Latn-ID": "slu", + "slw-Latn-PG": "slw", + "slx-Latn-CD": "slx", + "sly-Latn-ID": "sly", + "slz-Latn-ID": "slz", + "sm-Latn-AS": "sm-AS", + "sm-Latn-WS": "sm", + "sma-Latn-SE": "sma", + "smb-Latn-PG": "smb", + "smc-Latn-PG": "smc", + "smf-Latn-PG": "smf", + "smg-Latn-PG": "smg", + "smh-Yiii-CN": "smh", + "smj-Latn-SE": "smj", + "smk-Latn-PH": "smk", + "sml-Latn-PH": "sml", + "smn-Latn-FI": "smn", + "smp-Samr-IL": "smp", + "smq-Latn-ZZ": "smq", + "smr-Latn-ID": "smr", + "sms-Latn-FI": "sms", + "smt-Latn-IN": "smt", + "smu-Khmr-KH": "smu", + "smw-Latn-ID": "smw", + "smx-Latn-CD": "smx", + "smy-Arab-IR": "smy", + "smz-Latn-PG": "smz", + "sn-Latn-ZW": "sn", + "snc-Latn-ZZ": "snc", + "sne-Latn-MY": "sne", + "snf-Latn-SN": "snf", + "sng-Brai-CD": "sng-Brai", + "sng-Latn-CD": "sng", + "sni-Latn-PE": "sni", + "snj-Latn-CF": "snj", + "snk-Latn-ML": "snk", + "snl-Latn-PH": "snl", + "snm-Latn-UG": "snm", + "snn-Latn-CO": "snn", + "sno-Latn-US": "sno", + "snp-Latn-ZZ": "snp", + "snq-Latn-GA": "snq", + "snr-Latn-PG": "snr", + "sns-Latn-VU": "sns", + "snu-Latn-ID": "snu", + "snv-Latn-MY": "snv", + "snw-Latn-GH": "snw", + "snx-Latn-ZZ": "snx", + "sny-Latn-ZZ": "sny", + "snz-Latn-PG": "snz", + "so-Latn-SO": "so", + "so-Osma-SO": "so-Osma", + "soa-Tavt-TH": "soa", + "soa-Thai-TH": "soa-Thai", + "sob-Latn-ID": "sob", + "soc-Latn-CD": "soc", + "sod-Latn-CD": "sod", + "soe-Latn-CD": "soe", + "sog-Sogd-UZ": "sog", + "sog-Sogo-UZ": "sog-Sogo", + "soi-Deva-NP": "soi", + "sok-Latn-ZZ": "sok", + "sol-Latn-PG": "sol", + "soo-Latn-CD": "soo", + "sop-Latn-CD": "sop", + "soq-Latn-ZZ": "soq", + "sor-Latn-TD": "sor", + "sos-Latn-BF": "sos", + "sou-Thai-TH": "sou", + "sov-Latn-PW": "sov", + "sow-Latn-PG": "sow", + "sox-Latn-CM": "sox", + "soy-Latn-ZZ": "soy", + "soz-Latn-TZ": "soz", + "spb-Latn-ID": "spb", + "spc-Latn-VE": "spc", + "spd-Latn-ZZ": "spd", + "spe-Latn-PG": "spe", + "spg-Latn-MY": "spg", + "spi-Latn-ID": "spi", + "spk-Latn-PG": "spk", + "spl-Latn-ZZ": "spl", + "spm-Latn-PG": "spm", + "spn-Latn-PY": "spn", + "spo-Latn-US": "spo", + "spp-Latn-ML": "spp", + "spq-Latn-PE": "spq", + "spr-Latn-ID": "spr", + "sps-Latn-ZZ": "sps", + "spt-Tibt-IN": "spt", + "spv-Orya-IN": "spv", + "sq-Elba-AL": "sq-Elba", + "sq-Latn-AL": "sq", + "sq-Latn-MK": "sq-MK", + "sq-Latn-XK": "sq-XK", + "sq-Vith-AL": "sq-Vith", + "sqa-Latn-NG": "sqa", + "sqh-Latn-NG": "sqh", + "sqm-Latn-CF": "sqm", + "sqo-Arab-IR": "sqo", + "sqq-Laoo-LA": "sqq", + "sqt-Arab-YE": "sqt", + "sqt-Latn-YE": "sqt-Latn", + "squ-Latn-CA": "squ", + "sr-Cyrl-BA": "sr-BA", + "sr-Cyrl-RS": "sr", + "sr-Cyrl-XK": "sr-XK", + "sr-Latn-ME": "sr-ME", + "sr-Latn-RO": "sr-RO", + "sr-Latn-RU": "sr-RU", + "sr-Latn-TR": "sr-TR", + "sra-Latn-PG": "sra", + "srb-Sora-IN": "srb", + "sre-Latn-ID": "sre", + "srf-Latn-PG": "srf", + "srg-Latn-PH": "srg", + "srh-Arab-CN": "srh", + "sri-Latn-CO": "sri", + "srk-Latn-MY": "srk", + "srl-Latn-ID": "srl", + "srm-Latn-SR": "srm", + "srn-Latn-SR": "srn", + "sro-Latn-IT": "sro", + "srq-Latn-BO": "srq", + "srr-Latn-SN": "srr", + "srs-Latn-CA": "srs", + "srt-Latn-ID": "srt", + "sru-Latn-BR": "sru", + "srv-Latn-PH": "srv", + "srw-Latn-ID": "srw", + "srx-Deva-IN": "srx", + "sry-Latn-PG": "sry", + "srz-Arab-IR": "srz", + "ss-Latn-ZA": "ss", + "ssb-Latn-PH": "ssb", + "ssc-Latn-TZ": "ssc", + "ssd-Latn-ZZ": "ssd", + "sse-Arab-PH": "sse-Arab", + "sse-Latn-PH": "sse", + "ssf-Latn-TW": "ssf", + "ssg-Latn-ZZ": "ssg", + "ssh-Arab-AE": "ssh", + "ssj-Latn-PG": "ssj", + "ssl-Latn-GH": "ssl", + "ssm-Latn-MY": "ssm", + "ssn-Latn-KE": "ssn", + "sso-Latn-PG": "sso", + "ssq-Latn-ID": "ssq", + "sss-Laoo-LA": "sss", + "sss-Thai-TH": "sss-Thai", + "sst-Latn-PG": "sst", + "ssu-Latn-PG": "ssu", + "ssv-Latn-VU": "ssv", + "ssx-Latn-PG": "ssx", + "ssy-Latn-ER": "ssy", + "ssz-Latn-PG": "ssz", + "st-Latn-LS": "st-LS", + "st-Latn-ZA": "st", + "sta-Latn-ZM": "sta", + "stb-Latn-PH": "stb", + "ste-Latn-ID": "ste", + "stf-Latn-PG": "stf", + "stg-Latn-VN": "stg", + "sth-Latn-IE": "sth", + "sti-Latn-KH": "sti-KH", + "sti-Latn-VN": "sti", + "stj-Latn-BF": "stj", + "stk-Latn-ZZ": "stk", + "stl-Latn-NL": "stl", + "stm-Latn-PG": "stm", + "stn-Latn-SB": "stn", + "sto-Latn-CA": "sto", + "stp-Latn-MX": "stp", + "stq-Latn-DE": "stq", + "str-Latn-CA": "str", + "sts-Arab-AF": "sts", + "stt-Latn-VN": "stt", + "stv-Arab-ET": "stv-Arab", + "stv-Ethi-ET": "stv", + "stw-Latn-FM": "stw", + "sty-Cyrl-RU": "sty", + "su-Latn-ID": "su", + "su-Sund-ID": "su-Sund", + "sua-Latn-ZZ": "sua", + "sub-Latn-CD": "sub", + "suc-Latn-PH": "suc", + "sue-Latn-ZZ": "sue", + "sug-Latn-PG": "sug", + "sui-Latn-PG": "sui", + "suj-Latn-TZ": "suj", + "suk-Latn-TZ": "suk", + "suo-Latn-PG": "suo", + "suq-Ethi-ET": "suq-Ethi", + "suq-Latn-ET": "suq", + "sur-Latn-ZZ": "sur", + "sus-Latn-GN": "sus", + "sut-Latn-NI": "sut", + "suv-Beng-IN": "suv-Beng", + "suv-Deva-IN": "suv-Deva", + "suv-Latn-IN": "suv", + "suw-Latn-TZ": "suw", + "suy-Latn-BR": "suy", + "suz-Deva-NP": "suz", + "sv-Latn-AX": "sv-AX", + "sv-Latn-SE": "sv", + "sva-Cyrl-GE": "sva-Cyrl", + "sva-Geor-GE": "sva", + "sva-Latn-GE": "sva-Latn", + "svb-Latn-PG": "svb", + "svc-Latn-VC": "svc", + "sve-Latn-ID": "sve", + "svm-Latn-IT": "svm", + "svs-Latn-SB": "svs", + "sw-Latn-CD": "sw-CD", + "sw-Latn-KE": "sw-KE", + "sw-Latn-TZ": "sw", + "sw-Latn-UG": "sw-UG", + "swb-Arab-YT": "swb", + "swf-Latn-CD": "swf", + "swg-Latn-DE": "swg", + "swi-Hani-CN": "swi", + "swj-Latn-GA": "swj", + "swk-Latn-MW": "swk", + "swm-Latn-PG": "swm", + "swo-Latn-BR": "swo", + "swp-Latn-ZZ": "swp", + "swq-Latn-CM": "swq", + "swr-Latn-ID": "swr", + "sws-Latn-ID": "sws", + "swt-Latn-ID": "swt", + "swu-Latn-ID": "swu", + "swv-Deva-IN": "swv", + "sww-Latn-VU": "sww", + "swx-Latn-BR": "swx", + "swy-Latn-TD": "swy", + "sxb-Latn-KE": "sxb", + "sxe-Latn-GA": "sxe", + "sxn-Latn-ID": "sxn", + "sxr-Latn-TW": "sxr", + "sxs-Latn-NG": "sxs", + "sxu-Latn-DE": "sxu", + "sxu-Runr-DE": "sxu-Runr", + "sxw-Latn-ZZ": "sxw", + "sya-Latn-ID": "sya", + "syb-Latn-PH": "syb", + "syc-Syrc-TR": "syc", + "syi-Latn-GA": "syi", + "syk-Latn-NG": "syk", + "syl-Beng-BD": "syl", + "syl-Sylo-BD": "syl-Sylo", + "sym-Latn-BF": "sym", + "syn-Syrc-IR": "syn", + "syo-Latn-KH": "syo", + "syr-Syrc-IQ": "syr", + "sys-Latn-TD": "sys", + "syw-Deva-NP": "syw", + "syx-Latn-GA": "syx", + "sza-Latn-MY": "sza", + "szb-Latn-ID": "szb", + "szc-Latn-MY": "szc", + "szd-Latn-MY": "szd", + "szg-Latn-CD": "szg", + "szl-Latn-PL": "szl", + "szn-Latn-ID": "szn", + "szp-Latn-ID": "szp", + "szv-Latn-CM": "szv", + "szw-Latn-ID": "szw", + "szy-Latn-TW": "szy", + "ta-Taml-IN": "ta", + "taa-Latn-US": "taa", + "tab-Cyrl-RU": "tab", + "tac-Latn-MX": "tac", + "tad-Latn-ID": "tad", + "tae-Latn-BR": "tae", + "taf-Latn-BR": "taf", + "tag-Latn-SD": "tag", + "taj-Deva-NP": "taj", + "tak-Latn-NG": "tak", + "tal-Latn-ZZ": "tal", + "tan-Latn-ZZ": "tan", + "tao-Latn-TW": "tao", + "tap-Latn-CD": "tap", + "taq-Latn-ZZ": "taq", + "tar-Latn-MX": "tar", + "tas-Latn-VN": "tas", + "tau-Latn-US": "tau", + "tav-Latn-CO": "tav", + "taw-Latn-PG": "taw", + "tax-Latn-TD": "tax", + "tay-Hans-TW": "tay-Hans", + "tay-Hant-TW": "tay-Hant", + "tay-Latn-TW": "tay", + "taz-Latn-SD": "taz", + "tba-Latn-BR": "tba", + "tbc-Latn-ZZ": "tbc", + "tbd-Latn-ZZ": "tbd", + "tbe-Latn-SB": "tbe", + "tbf-Latn-ZZ": "tbf", + "tbg-Latn-ZZ": "tbg", + "tbh-Latn-AU": "tbh", + "tbi-Latn-SD": "tbi", + "tbj-Latn-PG": "tbj", + "tbk-Hano-PH": "tbk-Hano", + "tbk-Latn-PH": "tbk-Latn", + "tbk-Tagb-PH": "tbk", + "tbl-Latn-PH": "tbl", + "tbm-Latn-CD": "tbm", + "tbn-Latn-CO": "tbn", + "tbo-Latn-ZZ": "tbo", + "tbp-Latn-ID": "tbp", + "tbs-Latn-PG": "tbs", + "tbt-Latn-CD": "tbt", + "tbu-Latn-MX": "tbu", + "tbv-Latn-PG": "tbv", + "tbw-Latn-PH": "tbw", + "tbw-Tagb-PH": "tbw-Tagb", + "tbx-Latn-PG": "tbx", + "tby-Latn-ID": "tby", + "tbz-Latn-ZZ": "tbz", + "tca-Latn-BR": "tca", + "tcb-Latn-US": "tcb", + "tcc-Latn-TZ": "tcc", + "tcd-Latn-GH": "tcd", + "tce-Latn-CA": "tce", + "tcf-Latn-MX": "tcf", + "tcg-Latn-ID": "tcg", + "tch-Latn-TC": "tch", + "tci-Latn-ZZ": "tci", + "tck-Latn-GA": "tck", + "tcm-Latn-ID": "tcm", + "tcn-Tibt-NP": "tcn", + "tco-Mymr-MM": "tco", + "tcp-Latn-MM": "tcp", + "tcq-Latn-ID": "tcq", + "tcs-Latn-AU": "tcs", + "tcu-Latn-MX": "tcu", + "tcw-Latn-MX": "tcw", + "tcx-Taml-IN": "tcx", + "tcy-Knda-IN": "tcy", + "tcz-Latn-IN": "tcz", + "tda-Arab-NE": "tda-Arab", + "tda-Latn-NE": "tda-Latn", + "tda-Tfng-NE": "tda", + "tdb-Beng-IN": "tdb-Beng", + "tdb-Deva-IN": "tdb", + "tdb-Kthi-IN": "tdb-Kthi", + "tdc-Latn-CO": "tdc", + "tdd-Tale-CN": "tdd", + "tde-Latn-ML": "tde", + "tdg-Deva-NP": "tdg", + "tdh-Deva-NP": "tdh", + "tdi-Latn-ID": "tdi", + "tdj-Latn-ID": "tdj", + "tdk-Latn-NG": "tdk", + "tdl-Latn-NG": "tdl", + "tdm-Latn-GY": "tdm", + "tdn-Latn-ID": "tdn", + "tdo-Latn-NG": "tdo", + "tdq-Latn-NG": "tdq", + "tdr-Latn-VN": "tdr", + "tds-Latn-ID": "tds", + "tdt-Latn-TL": "tdt", + "tdv-Latn-NG": "tdv", + "tdx-Latn-MG": "tdx", + "tdy-Latn-PH": "tdy", + "te-Telu-IN": "te", + "tea-Latn-MY": "tea", + "teb-Latn-EC": "teb", + "tec-Latn-KE": "tec", + "ted-Latn-ZZ": "ted", + "tee-Latn-MX": "tee", + "teg-Latn-GA": "teg", + "teh-Latn-AR": "teh", + "tei-Latn-PG": "tei", + "tek-Latn-CD": "tek", + "tem-Latn-SL": "tem", + "ten-Latn-CO": "ten", + "teo-Latn-UG": "teo", + "tep-Latn-MX": "tep", + "teq-Latn-SD": "teq", + "ter-Latn-BR": "ter", + "tes-Java-ID": "tes", + "tet-Latn-TL": "tet", + "teu-Latn-UG": "teu", + "tev-Latn-ID": "tev", + "tew-Latn-US": "tew", + "tex-Latn-SS": "tex", + "tey-Latn-SD": "tey", + "tfi-Latn-ZZ": "tfi", + "tfn-Latn-US": "tfn", + "tfo-Latn-ID": "tfo", + "tfr-Latn-PA": "tfr", + "tft-Latn-ID": "tft", + "tg-Arab-PK": "tg-PK", + "tg-Cyrl-TJ": "tg", + "tga-Latn-KE": "tga", + "tgb-Latn-MY": "tgb", + "tgc-Latn-ZZ": "tgc", + "tgd-Latn-NG": "tgd", + "tge-Deva-NP": "tge", + "tgf-Tibt-BT": "tgf", + "tgh-Latn-TT": "tgh", + "tgi-Latn-PG": "tgi", + "tgj-Latn-IN": "tgj", + "tgn-Latn-PH": "tgn", + "tgo-Latn-ZZ": "tgo", + "tgp-Latn-VU": "tgp", + "tgq-Latn-MY": "tgq", + "tgs-Latn-VU": "tgs", + "tgt-Hano-PH": "tgt-Hano", + "tgt-Latn-PH": "tgt", + "tgt-Tagb-PH": "tgt-Tagb", + "tgu-Latn-ZZ": "tgu", + "tgv-Latn-BR": "tgv", + "tgw-Latn-CI": "tgw", + "tgx-Latn-CA": "tgx", + "tgy-Latn-SS": "tgy", + "tgz-Latn-AU": "tgz", + "th-Thai-TH": "th", + "thd-Latn-AU": "thd", + "the-Deva-NP": "the", + "thf-Deva-NP": "thf", + "thh-Latn-MX": "thh", + "thi-Tale-LA": "thi", + "thk-Latn-KE": "thk", + "thl-Deva-NP": "thl", + "thm-Thai-TH": "thm", + "thp-Dupl-CA": "thp-Dupl", + "thp-Latn-CA": "thp", + "thq-Deva-NP": "thq", + "thr-Deva-NP": "thr", + "ths-Deva-NP": "ths", + "tht-Latn-CA": "tht", + "thu-Latn-SS": "thu", + "thv-Arab-DZ": "thv-Arab", + "thv-Latn-DZ": "thv", + "thv-Tfng-DZ": "thv-Tfng", + "thy-Latn-NG": "thy", + "thz-Latn-NE": "thz", + "thz-Tfng-NE": "thz-Tfng", + "ti-Ethi-ER": "ti-ER", + "ti-Ethi-ET": "ti", + "tic-Latn-SD": "tic", + "tif-Latn-ZZ": "tif", + "tig-Ethi-ER": "tig", + "tih-Latn-MY": "tih", + "tii-Latn-CD": "tii", + "tij-Deva-NP": "tij", + "tik-Latn-ZZ": "tik", + "til-Latn-US": "til", + "tim-Latn-ZZ": "tim", + "tin-Cyrl-RU": "tin", + "tio-Latn-ZZ": "tio", + "tip-Latn-ID": "tip", + "tiq-Latn-BF": "tiq", + "tis-Latn-PH": "tis", + "tit-Latn-CO": "tit", + "tiu-Latn-PH": "tiu", + "tiv-Latn-NG": "tiv", + "tiw-Latn-AU": "tiw", + "tix-Latn-US": "tix", + "tiy-Latn-PH": "tiy", + "tja-Latn-LR": "tja", + "tjg-Latn-ID": "tjg", + "tji-Latn-CN": "tji", + "tjj-Latn-AU": "tjj", + "tjl-Mymr-MM": "tjl", + "tjn-Latn-CI": "tjn", + "tjo-Arab-DZ": "tjo", + "tjp-Latn-AU": "tjp", + "tjs-Latn-CN": "tjs", + "tju-Latn-AU": "tju", + "tjw-Latn-AU": "tjw", + "tk-Latn-AF": "tk-AF", + "tk-Latn-IR": "tk-IR", + "tk-Latn-TM": "tk", + "tka-Latn-BR": "tka", + "tkb-Deva-IN": "tkb", + "tkd-Latn-TL": "tkd", + "tke-Latn-MZ": "tke", + "tkf-Latn-BR": "tkf", + "tkg-Latn-MG": "tkg", + "tkl-Latn-TK": "tkl", + "tkp-Latn-SB": "tkp", + "tkq-Latn-NG": "tkq", + "tkr-Latn-AZ": "tkr", + "tks-Arab-IR": "tks", + "tkt-Deva-NP": "tkt", + "tku-Latn-MX": "tku", + "tkv-Latn-PG": "tkv", + "tkw-Latn-SB": "tkw", + "tkx-Latn-ID": "tkx", + "tkz-Latn-VN": "tkz", + "tla-Latn-MX": "tla", + "tlb-Latn-ID": "tlb", + "tlc-Latn-MX": "tlc", + "tld-Latn-ID": "tld", + "tlf-Latn-ZZ": "tlf", + "tlg-Latn-ID": "tlg", + "tli-Cyrl-US": "tli-Cyrl", + "tli-Latn-US": "tli", + "tlj-Latn-UG": "tlj", + "tlk-Latn-ID": "tlk", + "tll-Latn-CD": "tll", + "tlm-Latn-VU": "tlm", + "tln-Latn-ID": "tln", + "tlp-Latn-MX": "tlp", + "tlq-Latn-MM": "tlq", + "tlr-Latn-SB": "tlr", + "tls-Latn-VU": "tls", + "tlt-Latn-ID": "tlt", + "tlu-Latn-ID": "tlu", + "tlv-Latn-ID": "tlv", + "tlx-Latn-ZZ": "tlx", + "tly-Latn-AZ": "tly", + "tma-Latn-TD": "tma", + "tmb-Latn-VU": "tmb", + "tmc-Latn-TD": "tmc", + "tmd-Latn-PG": "tmd", + "tme-Latn-BR": "tme", + "tmf-Latn-PY": "tmf", + "tmg-Latn-ID": "tmg", + "tmh-Latn-NE": "tmh", + "tmi-Latn-VU": "tmi", + "tmj-Latn-ID": "tmj", + "tmk-Deva-NP": "tmk", + "tml-Latn-ID": "tml", + "tmm-Latn-VN": "tmm", + "tmn-Latn-ID": "tmn", + "tmo-Latn-MY": "tmo", + "tmq-Latn-PG": "tmq", + "tmr-Syrc-IL": "tmr", + "tmt-Latn-VU": "tmt", + "tmu-Latn-ID": "tmu", + "tmv-Latn-CD": "tmv", + "tmw-Latn-MY": "tmw", + "tmy-Latn-ZZ": "tmy", + "tmz-Latn-VE": "tmz", + "tn-Latn-ZA": "tn", + "tna-Latn-BO": "tna", + "tnb-Latn-CO": "tnb", + "tnc-Latn-CO": "tnc", + "tnd-Latn-CO": "tnd", + "tng-Latn-TD": "tng", + "tnh-Latn-ZZ": "tnh", + "tni-Latn-ID": "tni", + "tnk-Latn-VU": "tnk", + "tnl-Latn-VU": "tnl", + "tnm-Latn-ID": "tnm", + "tnn-Latn-VU": "tnn", + "tno-Latn-BO": "tno", + "tnp-Latn-VU": "tnp", + "tnq-Latn-PR": "tnq", + "tnr-Latn-SN": "tnr", + "tns-Latn-PG": "tns", + "tnt-Latn-ID": "tnt", + "tnv-Cakm-BD": "tnv", + "tnw-Latn-ID": "tnw", + "tnx-Latn-SB": "tnx", + "tny-Latn-TZ": "tny", + "to-Latn-TO": "to", + "tob-Latn-AR": "tob", + "toc-Latn-MX": "toc", + "tod-Latn-GN": "tod", + "tof-Latn-ZZ": "tof", + "tog-Latn-MW": "tog", + "toh-Latn-MZ": "toh", + "toi-Latn-ZM": "toi", + "toj-Latn-MX": "toj", + "tok-Latn-001": "tok", + "tol-Latn-US": "tol", + "tom-Latn-ID": "tom", + "too-Latn-MX": "too", + "top-Latn-MX": "top", + "toq-Latn-ZZ": "toq", + "tor-Latn-CD": "tor", + "tos-Latn-MX": "tos", + "tou-Latn-VN": "tou", + "tov-Arab-IR": "tov", + "tow-Latn-US": "tow", + "tox-Latn-PW": "tox", + "toy-Latn-ID": "toy", + "toz-Latn-CM": "toz", + "tpa-Latn-PG": "tpa", + "tpc-Latn-MX": "tpc", + "tpe-Beng-BD": "tpe-Beng", + "tpe-Latn-BD": "tpe", + "tpf-Latn-ID": "tpf", + "tpg-Latn-ID": "tpg", + "tpi-Latn-PG": "tpi", + "tpj-Latn-PY": "tpj", + "tpk-Latn-BR": "tpk", + "tpl-Latn-MX": "tpl", + "tpm-Latn-ZZ": "tpm", + "tpn-Latn-BR": "tpn", + "tpp-Latn-MX": "tpp", + "tpr-Latn-BR": "tpr", + "tpt-Latn-MX": "tpt", + "tpu-Khmr-KH": "tpu", + "tpv-Latn-MP": "tpv", + "tpx-Latn-MX": "tpx", + "tpy-Latn-BR": "tpy", + "tpz-Latn-ZZ": "tpz", + "tqb-Latn-BR": "tqb", + "tql-Latn-VU": "tql", + "tqm-Latn-PG": "tqm", + "tqn-Latn-US": "tqn", + "tqo-Latn-ZZ": "tqo", + "tqp-Latn-PG": "tqp", + "tqt-Latn-MX": "tqt", + "tqu-Latn-SB": "tqu", + "tqw-Latn-US": "tqw", + "tr-Latn-CY": "tr-CY", + "tr-Latn-TR": "tr", + "tra-Arab-AF": "tra", + "trb-Latn-PG": "trb", + "trc-Latn-MX": "trc", + "tre-Latn-ID": "tre", + "trf-Latn-TT": "trf", + "trg-Hebr-IL": "trg", + "trh-Latn-PG": "trh", + "tri-Latn-SR": "tri", + "trj-Latn-TD": "trj", + "trl-Latn-GB": "trl", + "trm-Arab-AF": "trm", + "trn-Latn-BO": "trn", + "tro-Latn-IN": "tro", + "trp-Beng-IN": "trp-Beng", + "trp-Latn-IN": "trp", + "trq-Latn-MX": "trq", + "trr-Latn-PE": "trr", + "trs-Latn-MX": "trs", + "trt-Latn-ID": "trt", + "tru-Latn-TR": "tru", + "trv-Latn-TW": "trv", + "trw-Arab-PK": "trw", + "trx-Latn-MY": "trx", + "try-Latn-IN": "try", + "trz-Latn-BR": "trz", + "ts-Latn-ZA": "ts", + "tsa-Latn-CG": "tsa", + "tsb-Latn-ET": "tsb", + "tsc-Latn-MZ": "tsc", + "tsd-Grek-GR": "tsd", + "tsg-Latn-PH": "tsg", + "tsh-Latn-CM": "tsh", + "tsi-Latn-CA": "tsi", + "tsj-Tibt-BT": "tsj", + "tsl-Latn-VN": "tsl", + "tsp-Latn-BF": "tsp", + "tsr-Latn-VU": "tsr", + "tst-Latn-ML": "tst", + "tsu-Latn-TW": "tsu", + "tsv-Latn-GA": "tsv", + "tsw-Latn-ZZ": "tsw", + "tsx-Latn-PG": "tsx", + "tsz-Latn-MX": "tsz", + "tt-Cyrl-RU": "tt", + "ttb-Latn-NG": "ttb", + "ttc-Latn-GT": "ttc", + "ttd-Latn-ZZ": "ttd", + "tte-Latn-ZZ": "tte", + "ttf-Latn-CM": "ttf", + "tth-Laoo-LA": "tth", + "tti-Latn-ID": "tti", + "ttj-Latn-UG": "ttj", + "ttk-Latn-CO": "ttk", + "ttl-Latn-ZM": "ttl", + "ttm-Latn-CA": "ttm", + "ttn-Latn-ID": "ttn", + "tto-Laoo-LA": "tto", + "ttp-Latn-ID": "ttp", + "ttr-Latn-ZZ": "ttr", + "tts-Thai-TH": "tts", + "ttt-Latn-AZ": "ttt", + "ttu-Latn-PG": "ttu", + "ttv-Latn-PG": "ttv", + "ttw-Latn-MY": "ttw", + "tty-Latn-ID": "tty", + "tua-Latn-PG": "tua", + "tub-Latn-US": "tub", + "tuc-Latn-PG": "tuc", + "tud-Latn-BR": "tud", + "tue-Latn-CO": "tue", + "tuf-Latn-CO": "tuf", + "tug-Latn-TD": "tug", + "tuh-Latn-ZZ": "tuh", + "tui-Latn-CM": "tui", + "tuj-Latn-ID": "tuj", + "tul-Latn-ZZ": "tul", + "tum-Latn-MW": "tum", + "tun-Latn-US": "tun", + "tuo-Latn-BR": "tuo", + "tuq-Latn-ZZ": "tuq", + "tus-Latn-CA": "tus", + "tuu-Latn-US": "tuu", + "tuv-Latn-KE": "tuv", + "tux-Latn-BR": "tux", + "tuy-Latn-KE": "tuy", + "tuz-Latn-BF": "tuz", + "tva-Latn-SB": "tva", + "tvd-Latn-ZZ": "tvd", + "tve-Latn-ID": "tve", + "tvk-Latn-VU": "tvk", + "tvl-Latn-TV": "tvl", + "tvm-Latn-ID": "tvm", + "tvn-Mymr-MM": "tvn", + "tvo-Latn-ID": "tvo", + "tvs-Latn-KE": "tvs", + "tvt-Latn-IN": "tvt", + "tvu-Latn-ZZ": "tvu", + "tvw-Latn-ID": "tvw", + "tvx-Latn-TW": "tvx", + "twa-Latn-US": "twa", + "twb-Latn-PH": "twb", + "twd-Latn-NL": "twd", + "twe-Latn-ID": "twe", + "twf-Latn-US": "twf", + "twg-Latn-ID": "twg", + "twh-Latn-ZZ": "twh", + "twl-Latn-MZ": "twl", + "twm-Deva-IN": "twm", + "twn-Latn-CM": "twn", + "two-Latn-BW": "two", + "twp-Latn-PG": "twp", + "twq-Latn-NE": "twq", + "twr-Latn-MX": "twr", + "twt-Latn-BR": "twt", + "twu-Latn-ID": "twu", + "tww-Latn-PG": "tww", + "twx-Latn-MZ": "twx", + "twy-Latn-ID": "twy", + "txa-Latn-MY": "txa", + "txe-Latn-ID": "txe", + "txg-Tang-CN": "txg", + "txi-Latn-BR": "txi", + "txj-Latn-NG": "txj", + "txm-Latn-ID": "txm", + "txn-Latn-ID": "txn", + "txo-Toto-IN": "txo", + "txq-Latn-ID": "txq", + "txs-Latn-ID": "txs", + "txt-Latn-ID": "txt", + "txu-Latn-BR": "txu", + "txx-Latn-MY": "txx", + "txy-Latn-MG": "txy", + "ty-Latn-PF": "ty", + "tya-Latn-ZZ": "tya", + "tye-Latn-NG": "tye", + "tyh-Latn-VN": "tyh", + "tyi-Latn-CG": "tyi", + "tyj-Latn-VN": "tyj", + "tyl-Latn-VN": "tyl", + "tyn-Latn-ID": "tyn", + "typ-Latn-AU": "typ", + "tyr-Tavt-VN": "tyr", + "tys-Latn-VN": "tys", + "tyt-Latn-VN": "tyt", + "tyt-Tavt-VN": "tyt-Tavt", + "tyu-Latn-BW": "tyu", + "tyv-Cyrl-RU": "tyv", + "tyx-Latn-CG": "tyx", + "tyy-Latn-NG": "tyy", + "tyz-Latn-VN": "tyz", + "tzh-Latn-MX": "tzh", + "tzj-Latn-GT": "tzj", + "tzl-Latn-001": "tzl", + "tzm-Latn-MA": "tzm", + "tzn-Latn-ID": "tzn", + "tzo-Latn-MX": "tzo", + "tzx-Latn-PG": "tzx", + "uam-Latn-BR": "uam", + "uar-Latn-PG": "uar", + "uba-Latn-NG": "uba", + "ubi-Latn-TD": "ubi", + "ubl-Latn-PH": "ubl", + "ubr-Latn-PG": "ubr", + "ubu-Latn-ZZ": "ubu", + "uda-Latn-NG": "uda", + "ude-Cyrl-RU": "ude", + "udg-Mlym-IN": "udg", + "udi-Aghb-RU": "udi", + "udj-Latn-ID": "udj", + "udl-Latn-CM": "udl", + "udm-Cyrl-RU": "udm", + "udu-Latn-SD": "udu", + "ues-Latn-ID": "ues", + "ufi-Latn-PG": "ufi", + "ug-Arab-CN": "ug", + "ug-Cyrl-KZ": "ug-KZ", + "ug-Cyrl-MN": "ug-MN", + "uga-Ugar-SY": "uga", + "ugb-Latn-AU": "ugb", + "uge-Latn-SB": "uge", + "ugh-Cyrl-RU": "ugh", + "ugo-Thai-TH": "ugo", + "uha-Latn-NG": "uha", + "uhn-Latn-ID": "uhn", + "uis-Latn-PG": "uis", + "uiv-Latn-CM": "uiv", + "uji-Latn-NG": "uji", + "uk-Cyrl-MD": "uk-MD", + "uk-Cyrl-SK": "uk-SK", + "uk-Cyrl-UA": "uk", + "uka-Latn-ID": "uka", + "ukg-Latn-PG": "ukg", + "ukh-Latn-CF": "ukh", + "uki-Orya-IN": "uki", + "ukk-Latn-MM": "ukk", + "ukp-Latn-NG": "ukp", + "ukq-Latn-NG": "ukq", + "uku-Latn-NG": "uku", + "ukv-Latn-SS": "ukv", + "ukw-Latn-NG": "ukw", + "uky-Latn-AU": "uky", + "ula-Latn-NG": "ula", + "ulb-Latn-NG": "ulb", + "ulc-Cyrl-RU": "ulc", + "ule-Latn-AR": "ule", + "ulf-Latn-ID": "ulf", + "uli-Latn-FM": "uli", + "ulk-Latn-AU": "ulk", + "ulm-Latn-ID": "ulm", + "uln-Latn-PG": "uln", + "ulu-Latn-ID": "ulu", + "ulw-Latn-NI": "ulw", + "uma-Latn-US": "uma", + "umb-Latn-AO": "umb", + "umd-Latn-AU": "umd", + "umg-Latn-AU": "umg", + "umi-Latn-MY": "umi", + "umm-Latn-NG": "umm", + "umn-Latn-MM": "umn", + "umo-Latn-BR": "umo", + "ump-Latn-AU": "ump", + "umr-Latn-AU": "umr", + "ums-Latn-ID": "ums", + "una-Latn-PG": "una", + "und-Cpmn-CY": "und-Cpmn", + "und-Latn-AQ": "und-AQ", + "und-Latn-BV": "und-BV", + "und-Latn-CP": "und-CP", + "und-Latn-GS": "und-GS", + "und-Latn-HM": "und-HM", + "une-Latn-NG": "une", + "ung-Latn-AU": "ung", + "uni-Latn-PG": "uni", + "unk-Latn-BR": "unk", + "unm-Latn-US": "unm", + "unn-Latn-AU": "unn", + "unr-Beng-IN": "unr", + "unr-Deva-NP": "unr-NP", + "unr-Nagm-IN": "unr-Nagm", + "unu-Latn-PG": "unu", + "unx-Beng-IN": "unx", + "unz-Latn-ID": "unz", + "uon-Latn-TW": "uon", + "upi-Latn-PG": "upi", + "upv-Latn-VU": "upv", + "ur-Arab-GB": "ur-GB", + "ur-Arab-IN": "ur-IN", + "ur-Arab-MU": "ur-MU", + "ur-Arab-PK": "ur", + "ura-Latn-PE": "ura", + "urb-Latn-BR": "urb", + "urc-Latn-AU": "urc", + "ure-Latn-BO": "ure", + "urf-Latn-AU": "urf", + "urg-Latn-PG": "urg", + "urh-Latn-NG": "urh", + "uri-Latn-ZZ": "uri", + "urk-Thai-TH": "urk", + "urm-Latn-PG": "urm", + "urn-Latn-ID": "urn", + "uro-Latn-PG": "uro", + "urp-Latn-BR": "urp", + "urr-Latn-VU": "urr", + "urt-Latn-ZZ": "urt", + "uru-Latn-BR": "uru", + "urv-Latn-PG": "urv", + "urw-Latn-ZZ": "urw", + "urx-Latn-PG": "urx", + "ury-Latn-ID": "ury", + "urz-Latn-BR": "urz", + "usa-Latn-ZZ": "usa", + "ush-Arab-PK": "ush", + "usi-Beng-BD": "usi-Beng", + "usi-Latn-BD": "usi", + "usk-Latn-CM": "usk", + "usp-Latn-GT": "usp", + "uss-Latn-NG": "uss", + "usu-Latn-PG": "usu", + "uta-Latn-NG": "uta", + "ute-Latn-US": "ute", + "uth-Latn-ZZ": "uth", + "utp-Latn-SB": "utp", + "utr-Latn-ZZ": "utr", + "utu-Latn-PG": "utu", + "uum-Cyrl-GE": "uum-Cyrl", + "uum-Grek-GE": "uum", + "uur-Latn-VU": "uur", + "uve-Latn-NC": "uve", + "uvh-Latn-ZZ": "uvh", + "uvl-Latn-ZZ": "uvl", + "uwa-Latn-AU": "uwa", + "uya-Latn-NG": "uya", + "uz-Arab-AF": "uz-AF", + "uz-Cyrl-CN": "uz-CN", + "uz-Latn-UZ": "uz", + "uzs-Arab-AF": "uzs", + "vaa-Taml-IN": "vaa", + "vae-Latn-CF": "vae", + "vaf-Arab-IR": "vaf", + "vag-Latn-ZZ": "vag", + "vah-Deva-IN": "vah", + "vai-Vaii-LR": "vai", + "vaj-Latn-NA": "vaj", + "val-Latn-PG": "val", + "vam-Latn-PG": "vam", + "van-Latn-ZZ": "van", + "vao-Latn-VU": "vao", + "vap-Latn-IN": "vap", + "var-Latn-MX": "var", + "vas-Deva-IN": "vas", + "vas-Gujr-IN": "vas-Gujr", + "vau-Latn-CD": "vau", + "vav-Deva-IN": "vav", + "vav-Gujr-IN": "vav-Gujr", + "vay-Deva-NP": "vay", + "vbb-Latn-ID": "vbb", + "vbk-Latn-PH": "vbk", + "ve-Latn-ZA": "ve", + "vec-Latn-IT": "vec", + "vem-Latn-NG": "vem", + "veo-Latn-US": "veo", + "vep-Latn-RU": "vep", + "ver-Latn-NG": "ver", + "vgr-Arab-PK": "vgr", + "vi-Latn-VN": "vi", + "vic-Latn-SX": "vic", + "vid-Latn-TZ": "vid", + "vif-Latn-CG": "vif", + "vig-Latn-BF": "vig", + "vil-Latn-AR": "vil", + "vin-Latn-TZ": "vin", + "vit-Latn-NG": "vit", + "viv-Latn-ZZ": "viv", + "vka-Latn-AU": "vka", + "vkj-Latn-TD": "vkj", + "vkk-Latn-ID": "vkk", + "vkl-Latn-ID": "vkl", + "vkm-Latn-BR": "vkm", + "vkn-Latn-NG": "vkn", + "vko-Latn-ID": "vko", + "vkp-Deva-IN": "vkp-Deva", + "vkp-Latn-IN": "vkp", + "vkt-Latn-ID": "vkt", + "vku-Latn-AU": "vku", + "vkz-Latn-NG": "vkz", + "vlp-Latn-VU": "vlp", + "vls-Latn-BE": "vls", + "vma-Latn-AU": "vma", + "vmb-Latn-AU": "vmb", + "vmc-Latn-MX": "vmc", + "vmd-Knda-IN": "vmd", + "vme-Latn-ID": "vme", + "vmf-Latn-DE": "vmf", + "vmg-Latn-PG": "vmg", + "vmh-Arab-IR": "vmh", + "vmi-Latn-AU": "vmi", + "vmj-Latn-MX": "vmj", + "vmk-Latn-MZ": "vmk", + "vml-Latn-AU": "vml", + "vmm-Latn-MX": "vmm", + "vmp-Latn-MX": "vmp", + "vmq-Latn-MX": "vmq", + "vmr-Latn-MZ": "vmr", + "vms-Latn-ID": "vms", + "vmu-Latn-AU": "vmu", + "vmw-Latn-MZ": "vmw", + "vmx-Latn-MX": "vmx", + "vmy-Latn-MX": "vmy", + "vmz-Latn-MX": "vmz", + "vnk-Latn-SB": "vnk", + "vnm-Latn-VU": "vnm", + "vnp-Latn-VU": "vnp", + "vo-Latn-001": "vo", + "vor-Latn-NG": "vor", + "vot-Latn-RU": "vot", + "vra-Latn-VU": "vra", + "vro-Latn-EE": "vro", + "vrs-Latn-SB": "vrs", + "vrt-Latn-VU": "vrt", + "vto-Latn-ID": "vto", + "vum-Latn-GA": "vum", + "vun-Latn-TZ": "vun", + "vut-Latn-ZZ": "vut", + "vwa-Latn-CN": "vwa", + "vwa-Mymr-CN": "vwa-Mymr", + "wa-Latn-BE": "wa", + "waa-Latn-US": "waa", + "wab-Latn-PG": "wab", + "wac-Latn-US": "wac", + "wad-Latn-ID": "wad", + "wae-Latn-CH": "wae", + "waf-Latn-BR": "waf", + "wag-Latn-PG": "wag", + "wah-Latn-ID": "wah", + "wai-Latn-ID": "wai", + "waj-Latn-ZZ": "waj", + "wal-Ethi-ET": "wal", + "wam-Latn-US": "wam", + "wan-Latn-ZZ": "wan", + "wap-Latn-GY": "wap", + "waq-Latn-AU": "waq", + "war-Latn-PH": "war", + "was-Latn-US": "was", + "wat-Latn-PG": "wat", + "wau-Latn-BR": "wau", + "wav-Latn-NG": "wav", + "waw-Latn-BR": "waw", + "wax-Latn-PG": "wax", + "way-Latn-SR": "way", + "waz-Latn-PG": "waz", + "wba-Latn-VE": "wba", + "wbb-Latn-ID": "wbb", + "wbe-Latn-ID": "wbe", + "wbf-Latn-BF": "wbf", + "wbh-Latn-TZ": "wbh", + "wbi-Latn-TZ": "wbi", + "wbj-Latn-TZ": "wbj", + "wbk-Arab-AF": "wbk", + "wbl-Arab-AF": "wbl-Arab", + "wbl-Cyrl-TJ": "wbl-Cyrl", + "wbl-Latn-PK": "wbl", + "wbm-Latn-CN": "wbm", + "wbp-Latn-AU": "wbp", + "wbq-Telu-IN": "wbq", + "wbr-Deva-IN": "wbr", + "wbt-Latn-AU": "wbt", + "wbv-Latn-AU": "wbv", + "wbw-Latn-ID": "wbw", + "wca-Latn-BR": "wca", + "wci-Latn-ZZ": "wci", + "wdd-Latn-GA": "wdd", + "wdg-Latn-PG": "wdg", + "wdj-Latn-AU": "wdj", + "wdk-Latn-AU": "wdk", + "wdt-Latn-CA": "wdt", + "wdu-Latn-AU": "wdu", + "wdy-Latn-AU": "wdy", + "wec-Latn-CI": "wec", + "wed-Latn-PG": "wed", + "weg-Latn-AU": "weg", + "weh-Latn-CM": "weh", + "wei-Latn-PG": "wei", + "wem-Latn-BJ": "wem", + "weo-Latn-ID": "weo", + "wep-Latn-DE": "wep", + "wer-Latn-ZZ": "wer", + "wes-Latn-CM": "wes", + "wet-Latn-ID": "wet", + "weu-Latn-MM": "weu", + "wew-Latn-ID": "wew", + "wfg-Latn-ID": "wfg", + "wga-Latn-AU": "wga", + "wgb-Latn-PG": "wgb", + "wgg-Latn-AU": "wgg", + "wgi-Latn-ZZ": "wgi", + "wgo-Latn-ID": "wgo", + "wgu-Latn-AU": "wgu", + "wgy-Latn-AU": "wgy", + "wha-Latn-ID": "wha", + "whg-Latn-ZZ": "whg", + "whk-Latn-ID": "whk", + "whu-Latn-ID": "whu", + "wib-Latn-ZZ": "wib", + "wic-Latn-US": "wic", + "wie-Latn-AU": "wie", + "wif-Latn-AU": "wif", + "wig-Latn-AU": "wig", + "wih-Latn-AU": "wih", + "wii-Latn-PG": "wii", + "wij-Latn-AU": "wij", + "wik-Latn-AU": "wik", + "wil-Latn-AU": "wil", + "wim-Latn-AU": "wim", + "win-Latn-US": "win", + "wir-Latn-BR": "wir", + "wiu-Latn-ZZ": "wiu", + "wiv-Latn-ZZ": "wiv", + "wiy-Latn-US": "wiy", + "wja-Latn-ZZ": "wja", + "wji-Latn-ZZ": "wji", + "wka-Latn-TZ": "wka", + "wkd-Latn-ID": "wkd", + "wkr-Latn-AU": "wkr", + "wkw-Latn-AU": "wkw", + "wky-Latn-AU": "wky", + "wla-Latn-PG": "wla", + "wlg-Latn-AU": "wlg", + "wlh-Latn-TL": "wlh", + "wli-Latn-ID": "wli", + "wlm-Latn-GB": "wlm", + "wlo-Arab-ID": "wlo", + "wlr-Latn-VU": "wlr", + "wls-Latn-WF": "wls", + "wlu-Latn-AU": "wlu", + "wlv-Latn-AR": "wlv", + "wlw-Latn-ID": "wlw", + "wlx-Latn-GH": "wlx", + "wma-Latn-NG": "wma", + "wmb-Latn-AU": "wmb", + "wmc-Latn-PG": "wmc", + "wmd-Latn-BR": "wmd", + "wme-Deva-NP": "wme", + "wmh-Latn-TL": "wmh", + "wmi-Latn-AU": "wmi", + "wmm-Latn-ID": "wmm", + "wmn-Latn-NC": "wmn", + "wmo-Latn-ZZ": "wmo", + "wms-Latn-ID": "wms", + "wmt-Latn-AU": "wmt", + "wmw-Arab-MZ": "wmw-Arab", + "wmw-Latn-MZ": "wmw", + "wmx-Latn-PG": "wmx", + "wnb-Latn-PG": "wnb", + "wnc-Latn-ZZ": "wnc", + "wnd-Latn-AU": "wnd", + "wne-Arab-PK": "wne", + "wng-Latn-ID": "wng", + "wni-Arab-KM": "wni", + "wnk-Latn-ID": "wnk", + "wnm-Latn-AU": "wnm", + "wnn-Latn-AU": "wnn", + "wno-Latn-ID": "wno", + "wnp-Latn-PG": "wnp", + "wnu-Latn-ZZ": "wnu", + "wnw-Latn-US": "wnw", + "wny-Latn-AU": "wny", + "wo-Latn-SN": "wo", + "woa-Latn-AU": "woa", + "wob-Latn-ZZ": "wob", + "woc-Latn-PG": "woc", + "wod-Latn-ID": "wod", + "woe-Latn-FM": "woe", + "wof-Arab-GM": "wof-Arab", + "wof-Latn-GM": "wof", + "wog-Latn-PG": "wog", + "woi-Latn-ID": "woi", + "wok-Latn-CM": "wok", + "wom-Latn-NG": "wom", + "won-Latn-CD": "won", + "woo-Latn-ID": "woo", + "wor-Latn-ID": "wor", + "wos-Latn-ZZ": "wos", + "wow-Latn-ID": "wow", + "wpc-Latn-VE": "wpc", + "wrb-Latn-AU": "wrb", + "wrg-Latn-AU": "wrg", + "wrh-Latn-AU": "wrh", + "wri-Latn-AU": "wri", + "wrk-Latn-AU": "wrk", + "wrl-Latn-AU": "wrl", + "wrm-Latn-AU": "wrm", + "wro-Latn-AU": "wro", + "wrp-Latn-ID": "wrp", + "wrr-Latn-AU": "wrr", + "wrs-Latn-ZZ": "wrs", + "wru-Latn-ID": "wru", + "wrv-Latn-PG": "wrv", + "wrw-Latn-AU": "wrw", + "wrx-Latn-ID": "wrx", + "wrz-Latn-AU": "wrz", + "wsa-Latn-ID": "wsa", + "wsg-Gong-IN": "wsg", + "wsi-Latn-VU": "wsi", + "wsk-Latn-ZZ": "wsk", + "wsr-Latn-PG": "wsr", + "wss-Latn-GH": "wss", + "wsu-Latn-BR": "wsu", + "wsv-Arab-AF": "wsv", + "wtf-Latn-PG": "wtf", + "wth-Latn-AU": "wth", + "wti-Latn-ET": "wti", + "wtk-Latn-PG": "wtk", + "wtm-Deva-IN": "wtm", + "wtw-Bugi-ID": "wtw-Bugi", + "wtw-Latn-ID": "wtw", + "wua-Latn-AU": "wua", + "wub-Latn-AU": "wub", + "wud-Latn-TG": "wud", + "wul-Latn-ID": "wul", + "wum-Latn-GA": "wum", + "wun-Latn-TZ": "wun", + "wur-Latn-AU": "wur", + "wut-Latn-PG": "wut", + "wuu-Hans-CN": "wuu", + "wuv-Latn-ZZ": "wuv", + "wux-Latn-AU": "wux", + "wuy-Latn-ID": "wuy", + "wwa-Latn-ZZ": "wwa", + "wwb-Latn-AU": "wwb", + "wwo-Latn-VU": "wwo", + "wwr-Latn-AU": "wwr", + "www-Latn-CM": "www", + "wxw-Latn-AU": "wxw", + "wyb-Latn-AU": "wyb", + "wyi-Latn-AU": "wyi", + "wym-Latn-PL": "wym", + "wyn-Latn-US": "wyn", + "wyr-Latn-BR": "wyr", + "wyy-Latn-FJ": "wyy", + "xaa-Latn-ES": "xaa", + "xab-Latn-NG": "xab", + "xai-Latn-BR": "xai", + "xaj-Latn-BR": "xaj", + "xak-Latn-VE": "xak", + "xal-Cyrl-RU": "xal", + "xam-Latn-ZA": "xam", + "xan-Ethi-ET": "xan", + "xao-Latn-VN": "xao", + "xar-Latn-PG": "xar", + "xas-Cyrl-RU": "xas", + "xat-Latn-BR": "xat", + "xau-Latn-ID": "xau", + "xav-Latn-BR": "xav", + "xaw-Latn-US": "xaw", + "xay-Latn-ID": "xay", + "xbb-Latn-AU": "xbb", + "xbd-Latn-AU": "xbd", + "xbe-Latn-AU": "xbe", + "xbg-Latn-AU": "xbg", + "xbi-Latn-ZZ": "xbi", + "xbj-Latn-AU": "xbj", + "xbm-Latn-FR": "xbm", + "xbn-Latn-MY": "xbn", + "xbp-Latn-AU": "xbp", + "xbr-Latn-ID": "xbr", + "xbw-Latn-BR": "xbw", + "xby-Latn-AU": "xby", + "xch-Latn-US": "xch", + "xco-Chrs-UZ": "xco", + "xcr-Cari-TR": "xcr", + "xda-Latn-AU": "xda", + "xdk-Latn-AU": "xdk", + "xdo-Latn-AO": "xdo", + "xdq-Cyrl-RU": "xdq", + "xdy-Latn-ID": "xdy", + "xed-Latn-CM": "xed", + "xeg-Latn-ZA": "xeg", + "xem-Latn-ID": "xem", + "xer-Latn-BR": "xer", + "xes-Latn-ZZ": "xes", + "xet-Latn-BR": "xet", + "xeu-Latn-PG": "xeu", + "xgb-Latn-CI": "xgb", + "xgd-Latn-AU": "xgd", + "xgg-Latn-AU": "xgg", + "xgi-Latn-AU": "xgi", + "xgm-Latn-AU": "xgm", + "xgu-Latn-AU": "xgu", + "xgw-Latn-AU": "xgw", + "xh-Latn-ZA": "xh", + "xhe-Arab-PK": "xhe", + "xhm-Khmr-KH": "xhm", + "xhv-Latn-VN": "xhv", + "xii-Latn-ZA": "xii", + "xin-Latn-GT": "xin", + "xir-Latn-BR": "xir", + "xis-Orya-IN": "xis", + "xiy-Latn-BR": "xiy", + "xjb-Latn-AU": "xjb", + "xjt-Latn-AU": "xjt", + "xka-Arab-PK": "xka", + "xkb-Latn-BJ": "xkb", + "xkc-Arab-IR": "xkc", + "xkd-Latn-ID": "xkd", + "xke-Latn-ID": "xke", + "xkg-Latn-ML": "xkg", + "xkj-Arab-IR": "xkj", + "xkl-Latn-ID": "xkl", + "xkn-Latn-ID": "xkn", + "xkp-Arab-IR": "xkp", + "xkq-Latn-ID": "xkq", + "xkr-Latn-BR": "xkr", + "xks-Latn-ID": "xks", + "xkt-Latn-GH": "xkt", + "xku-Latn-CG": "xku", + "xkv-Latn-BW": "xkv", + "xkw-Latn-ID": "xkw", + "xkx-Latn-PG": "xkx", + "xky-Latn-MY": "xky", + "xkz-Latn-BT": "xkz", + "xla-Latn-ZZ": "xla", + "xlc-Lyci-TR": "xlc", + "xld-Lydi-TR": "xld", + "xly-Elym-IR": "xly", + "xma-Latn-SO": "xma", + "xmb-Latn-CM": "xmb", + "xmc-Latn-MZ": "xmc", + "xmd-Latn-CM": "xmd", + "xmf-Geor-GE": "xmf", + "xmg-Latn-CM": "xmg", + "xmh-Latn-AU": "xmh", + "xmj-Latn-CM": "xmj", + "xmm-Latn-ID": "xmm", + "xmn-Mani-CN": "xmn", + "xmo-Latn-BR": "xmo", + "xmp-Latn-AU": "xmp", + "xmq-Latn-AU": "xmq", + "xmr-Merc-SD": "xmr", + "xmr-Mero-SD": "xmr-Mero", + "xmt-Latn-ID": "xmt", + "xmu-Latn-AU": "xmu", + "xmv-Latn-MG": "xmv", + "xmw-Latn-MG": "xmw", + "xmx-Latn-ID": "xmx", + "xmy-Latn-AU": "xmy", + "xmz-Latn-ID": "xmz", + "xna-Narb-SA": "xna", + "xnb-Latn-TW": "xnb", + "xni-Latn-AU": "xni", + "xnj-Latn-TZ": "xnj", + "xnk-Latn-AU": "xnk", + "xnm-Latn-AU": "xnm", + "xnn-Latn-PH": "xnn", + "xnq-Latn-MZ": "xnq", + "xnr-Deva-IN": "xnr", + "xnt-Latn-US": "xnt", + "xnu-Latn-AU": "xnu", + "xny-Latn-AU": "xny", + "xnz-Arab-EG": "xnz-Arab", + "xnz-Latn-EG": "xnz", + "xoc-Latn-NG": "xoc", + "xod-Latn-ID": "xod", + "xog-Latn-UG": "xog", + "xoi-Latn-PG": "xoi", + "xok-Latn-BR": "xok", + "xom-Ethi-ET": "xom-Ethi", + "xom-Latn-SD": "xom", + "xon-Latn-ZZ": "xon", + "xoo-Latn-BR": "xoo", + "xop-Latn-PG": "xop", + "xor-Latn-BR": "xor", + "xow-Latn-PG": "xow", + "xpa-Latn-AU": "xpa", + "xpb-Latn-AU": "xpb", + "xpd-Latn-AU": "xpd", + "xpf-Latn-AU": "xpf", + "xpg-Grek-TR": "xpg", + "xph-Latn-AU": "xph", + "xpi-Ogam-GB": "xpi", + "xpj-Latn-AU": "xpj", + "xpk-Latn-BR": "xpk", + "xpl-Latn-AU": "xpl", + "xpm-Cyrl-RU": "xpm", + "xpn-Latn-BR": "xpn", + "xpo-Latn-MX": "xpo", + "xpq-Latn-US": "xpq", + "xpr-Prti-IR": "xpr", + "xpt-Latn-AU": "xpt", + "xpv-Latn-AU": "xpv", + "xpw-Latn-AU": "xpw", + "xpx-Latn-AU": "xpx", + "xpz-Latn-AU": "xpz", + "xra-Latn-BR": "xra", + "xrb-Latn-ZZ": "xrb", + "xrd-Latn-AU": "xrd", + "xre-Latn-BR": "xre", + "xrg-Latn-AU": "xrg", + "xri-Latn-BR": "xri", + "xrm-Cyrl-RU": "xrm", + "xrn-Cyrl-RU": "xrn", + "xrr-Latn-IT": "xrr", + "xru-Latn-AU": "xru", + "xrw-Latn-PG": "xrw", + "xsa-Sarb-YE": "xsa", + "xsb-Latn-PH": "xsb", + "xse-Latn-ID": "xse", + "xsh-Latn-NG": "xsh", + "xsi-Latn-ZZ": "xsi", + "xsm-Latn-ZZ": "xsm", + "xsn-Latn-NG": "xsn", + "xsp-Latn-PG": "xsp", + "xsq-Latn-MZ": "xsq", + "xsr-Deva-NP": "xsr", + "xss-Cyrl-RU": "xss", + "xsu-Latn-VE": "xsu", + "xsy-Latn-TW": "xsy", + "xta-Latn-MX": "xta", + "xtb-Latn-MX": "xtb", + "xtc-Latn-SD": "xtc", + "xtd-Latn-MX": "xtd", + "xte-Latn-ID": "xte", + "xth-Latn-AU": "xth", + "xti-Latn-MX": "xti", + "xtj-Latn-MX": "xtj", + "xtl-Latn-MX": "xtl", + "xtm-Latn-MX": "xtm", + "xtn-Latn-MX": "xtn", + "xtp-Latn-MX": "xtp", + "xts-Latn-MX": "xts", + "xtt-Latn-MX": "xtt", + "xtu-Latn-MX": "xtu", + "xtv-Latn-AU": "xtv", + "xtw-Latn-BR": "xtw", + "xty-Latn-MX": "xty", + "xub-Knda-IN": "xub-Knda", + "xub-Mlym-IN": "xub-Mlym", + "xub-Taml-IN": "xub", + "xud-Latn-AU": "xud", + "xuj-Taml-IN": "xuj", + "xul-Latn-AU": "xul", + "xum-Ital-IT": "xum-Ital", + "xum-Latn-IT": "xum", + "xun-Latn-AU": "xun", + "xuo-Latn-TD": "xuo", + "xut-Latn-AU": "xut", + "xuu-Latn-NA": "xuu", + "xve-Ital-IT": "xve", + "xvi-Arab-AF": "xvi", + "xvn-Latn-ES": "xvn", + "xvo-Latn-IT": "xvo", + "xvs-Latn-IT": "xvs", + "xwa-Latn-BR": "xwa", + "xwd-Latn-AU": "xwd", + "xwe-Latn-ZZ": "xwe", + "xwj-Latn-AU": "xwj", + "xwk-Latn-AU": "xwk", + "xwl-Latn-BJ": "xwl", + "xwo-Cyrl-RU": "xwo", + "xwr-Latn-ID": "xwr", + "xwt-Latn-AU": "xwt", + "xww-Latn-AU": "xww", + "xxb-Latn-GH": "xxb", + "xxk-Latn-ID": "xxk", + "xxm-Latn-AU": "xxm", + "xxr-Latn-BR": "xxr", + "xxt-Latn-ID": "xxt", + "xya-Latn-AU": "xya", + "xyb-Latn-AU": "xyb", + "xyj-Latn-AU": "xyj", + "xyk-Latn-AU": "xyk", + "xyl-Latn-BR": "xyl", + "xyt-Latn-AU": "xyt", + "xyy-Latn-AU": "xyy", + "xzh-Marc-CN": "xzh", + "xzp-Latn-MX": "xzp", + "yaa-Latn-PE": "yaa", + "yab-Latn-BR": "yab", + "yac-Latn-ID": "yac", + "yad-Latn-PE": "yad", + "yae-Latn-VE": "yae", + "yaf-Latn-CD": "yaf", + "yag-Latn-CL": "yag", + "yai-Cyrl-TJ": "yai", + "yaj-Latn-CF": "yaj", + "yak-Latn-US": "yak", + "yal-Arab-GN": "yal-Arab", + "yal-Latn-GN": "yal", + "yam-Latn-ZZ": "yam", + "yan-Latn-NI": "yan", + "yao-Latn-MZ": "yao", + "yap-Latn-FM": "yap", + "yaq-Latn-MX": "yaq", + "yar-Latn-VE": "yar", + "yas-Latn-ZZ": "yas", + "yat-Latn-ZZ": "yat", + "yau-Latn-VE": "yau", + "yav-Latn-CM": "yav", + "yaw-Latn-BR": "yaw", + "yax-Latn-AO": "yax", + "yay-Latn-ZZ": "yay", + "yaz-Latn-ZZ": "yaz", + "yba-Latn-ZZ": "yba", + "ybb-Latn-CM": "ybb", + "ybe-Latn-CN": "ybe", + "ybe-Ougr-CN": "ybe-Ougr", + "ybh-Deva-NP": "ybh", + "ybi-Deva-NP": "ybi", + "ybj-Latn-NG": "ybj", + "ybl-Latn-NG": "ybl", + "ybm-Latn-PG": "ybm", + "ybn-Latn-BR": "ybn", + "ybo-Latn-PG": "ybo", + "ybx-Latn-PG": "ybx", + "yby-Latn-ZZ": "yby", + "ycl-Latn-CN": "ycl", + "ycn-Latn-CO": "ycn", + "yda-Latn-AU": "yda", + "yde-Latn-PG": "yde", + "ydg-Arab-PK": "ydg", + "ydk-Latn-PG": "ydk", + "yea-Knda-IN": "yea-Knda", + "yea-Mlym-IN": "yea", + "yec-Latn-DE": "yec", + "yee-Latn-PG": "yee", + "yei-Latn-CM": "yei", + "yej-Grek-IL": "yej", + "yel-Latn-CD": "yel", + "yer-Latn-ZZ": "yer", + "yes-Latn-NG": "yes", + "yet-Latn-ID": "yet", + "yeu-Telu-IN": "yeu", + "yev-Latn-PG": "yev", + "yey-Latn-BW": "yey", + "yga-Latn-AU": "yga", + "ygi-Latn-AU": "ygi", + "ygl-Latn-PG": "ygl", + "ygm-Latn-PG": "ygm", + "ygp-Plrd-CN": "ygp", + "ygr-Latn-ZZ": "ygr", + "ygu-Latn-AU": "ygu", + "ygw-Latn-ZZ": "ygw", + "yhd-Hebr-IL": "yhd", + "yi-Hebr-001": "yi", + "yi-Hebr-SE": "yi-SE", + "yi-Hebr-UA": "yi-UA", + "yi-Hebr-US": "yi-US", + "yia-Latn-AU": "yia", + "yig-Yiii-CN": "yig", + "yih-Hebr-DE": "yih", + "yii-Latn-AU": "yii", + "yij-Latn-AU": "yij", + "yil-Latn-AU": "yil", + "yim-Latn-IN": "yim", + "yir-Latn-ID": "yir", + "yis-Latn-PG": "yis", + "yiv-Yiii-CN": "yiv", + "yka-Arab-PH": "yka-Arab", + "yka-Latn-PH": "yka", + "ykg-Cyrl-RU": "ykg", + "yki-Latn-ID": "yki", + "ykk-Latn-PG": "ykk", + "ykm-Latn-PG": "ykm", + "yko-Latn-ZZ": "yko", + "ykr-Latn-PG": "ykr", + "yky-Latn-CF": "yky", + "yla-Latn-PG": "yla", + "ylb-Latn-PG": "ylb", + "yle-Latn-ZZ": "yle", + "ylg-Latn-ZZ": "ylg", + "yli-Latn-ID": "yli", + "yll-Latn-ZZ": "yll", + "ylr-Latn-AU": "ylr", + "ylu-Latn-PG": "ylu", + "yly-Latn-NC": "yly", + "ymb-Latn-PG": "ymb", + "yme-Latn-PE": "yme", + "ymg-Latn-CD": "ymg", + "ymk-Arab-MZ": "ymk-Arab", + "ymk-Latn-MZ": "ymk", + "yml-Latn-ZZ": "yml", + "ymm-Latn-SO": "ymm", + "ymn-Latn-ID": "ymn", + "ymo-Latn-PG": "ymo", + "ymp-Latn-PG": "ymp", + "yna-Plrd-CN": "yna", + "ynd-Latn-AU": "ynd", + "yng-Latn-CD": "yng", + "ynk-Cyrl-RU": "ynk", + "ynl-Latn-PG": "ynl", + "ynq-Latn-NG": "ynq", + "yns-Latn-CD": "yns", + "ynu-Latn-CO": "ynu", + "yo-Latn-NG": "yo", + "yob-Latn-PG": "yob", + "yog-Latn-PH": "yog", + "yoi-Jpan-JP": "yoi", + "yok-Latn-US": "yok", + "yol-Latn-GB": "yol", + "yom-Latn-CD": "yom", + "yon-Latn-ZZ": "yon", + "yot-Latn-NG": "yot", + "yoy-Thai-TH": "yoy", + "yra-Latn-PG": "yra", + "yrb-Latn-ZZ": "yrb", + "yre-Latn-ZZ": "yre", + "yrk-Cyrl-RU": "yrk", + "yrl-Latn-BR": "yrl", + "yrm-Latn-AU": "yrm", + "yro-Latn-BR": "yro", + "yrs-Latn-ID": "yrs", + "yrw-Latn-PG": "yrw", + "yry-Latn-AU": "yry", + "ysd-Yiii-CN": "ysd", + "ysn-Yiii-CN": "ysn", + "ysp-Yiii-CN": "ysp", + "ysr-Cyrl-RU": "ysr", + "yss-Latn-ZZ": "yss", + "ysy-Plrd-CN": "ysy", + "ytw-Latn-PG": "ytw", + "yty-Latn-AU": "yty", + "yua-Latn-MX": "yua", + "yub-Latn-AU": "yub", + "yuc-Latn-US": "yuc", + "yud-Hebr-IL": "yud", + "yue-Hans-CN": "yue-CN", + "yue-Hant-CA": "yue-CA", + "yue-Hant-HK": "yue", + "yuf-Latn-US": "yuf", + "yug-Cyrl-RU": "yug", + "yui-Latn-CO": "yui", + "yuj-Latn-ZZ": "yuj", + "yul-Latn-CF": "yul", + "yum-Latn-US": "yum", + "yun-Latn-NG": "yun", + "yup-Latn-CO": "yup", + "yuq-Latn-BO": "yuq", + "yur-Latn-US": "yur", + "yut-Latn-ZZ": "yut", + "yuw-Latn-ZZ": "yuw", + "yux-Cyrl-RU": "yux", + "yuz-Latn-BO": "yuz", + "yva-Latn-ID": "yva", + "yvt-Latn-VE": "yvt", + "ywa-Latn-PG": "ywa", + "ywg-Latn-AU": "ywg", + "ywn-Latn-BR": "ywn", + "ywq-Plrd-CN": "ywq", + "ywq-Yiii-CN": "ywq-Yiii", + "ywr-Latn-AU": "ywr", + "ywu-Plrd-CN": "ywu", + "ywu-Yiii-CN": "ywu-Yiii", + "yww-Latn-AU": "yww", + "yxa-Latn-AU": "yxa", + "yxg-Latn-AU": "yxg", + "yxl-Latn-AU": "yxl", + "yxm-Latn-AU": "yxm", + "yxu-Latn-AU": "yxu", + "yxy-Latn-AU": "yxy", + "yyr-Latn-AU": "yyr", + "yyu-Latn-PG": "yyu", + "za-Latn-CN": "za", + "zaa-Latn-MX": "zaa", + "zab-Latn-MX": "zab", + "zac-Latn-MX": "zac", + "zad-Latn-MX": "zad", + "zae-Latn-MX": "zae", + "zaf-Latn-MX": "zaf", + "zag-Latn-SD": "zag", + "zah-Latn-NG": "zah", + "zaj-Latn-TZ": "zaj", + "zak-Latn-TZ": "zak", + "zam-Latn-MX": "zam", + "zao-Latn-MX": "zao", + "zap-Latn-MX": "zap", + "zaq-Latn-MX": "zaq", + "zar-Latn-MX": "zar", + "zas-Latn-MX": "zas", + "zat-Latn-MX": "zat", + "zau-Arab-IN": "zau-Arab", + "zau-Tibt-IN": "zau", + "zav-Latn-MX": "zav", + "zaw-Latn-MX": "zaw", + "zax-Latn-MX": "zax", + "zay-Ethi-ET": "zay-Ethi", + "zay-Latn-ET": "zay", + "zaz-Latn-NG": "zaz", + "zba-Arab-001": "zba", + "zbc-Latn-MY": "zbc", + "zbe-Latn-MY": "zbe", + "zbt-Latn-ID": "zbt", + "zbu-Latn-NG": "zbu", + "zbw-Latn-MY": "zbw", + "zca-Latn-MX": "zca", + "zch-Hani-CN": "zch", + "zdj-Arab-KM": "zdj", + "zea-Latn-NL": "zea", + "zeg-Latn-PG": "zeg", + "zeh-Hani-CN": "zeh", + "zen-Arab-MR": "zen-Arab", + "zen-Tfng-MR": "zen", + "zga-Latn-TZ": "zga", + "zgb-Hani-CN": "zgb", + "zgh-Tfng-MA": "zgh", + "zgm-Hani-CN": "zgm", + "zgn-Hani-CN": "zgn", + "zgr-Latn-PG": "zgr", + "zh-Bopo-TW": "zh-Bopo", + "zh-Hanb-TW": "zh-Hanb", + "zh-Hani-CN": "zh-Hani", + "zh-Hans-CN": "zh", + "zh-Hant-AU": "zh-AU", + "zh-Hant-BN": "zh-BN", + "zh-Hant-GB": "zh-GB", + "zh-Hant-GF": "zh-GF", + "zh-Hant-HK": "zh-HK", + "zh-Hant-ID": "zh-ID", + "zh-Hant-MO": "zh-MO", + "zh-Hant-PA": "zh-PA", + "zh-Hant-PF": "zh-PF", + "zh-Hant-PH": "zh-PH", + "zh-Hant-SR": "zh-SR", + "zh-Hant-TH": "zh-TH", + "zh-Hant-TW": "zh-TW", + "zh-Hant-US": "zh-US", + "zh-Hant-VN": "zh-VN", + "zhd-Hani-CN": "zhd", + "zhd-Latn-VN": "zhd-Latn", + "zhi-Latn-NG": "zhi", + "zhn-Hani-CN": "zhn-Hani", + "zhn-Latn-CN": "zhn", + "zhw-Latn-CM": "zhw", + "zhx-Nshu-CN": "zhx", + "zia-Latn-ZZ": "zia", + "zik-Latn-PG": "zik", + "zil-Latn-GN": "zil", + "zim-Latn-TD": "zim", + "zin-Latn-TZ": "zin", + "ziw-Latn-TZ": "ziw", + "ziz-Latn-NG": "ziz", + "zka-Latn-ID": "zka", + "zkb-Cyrl-RU": "zkb", + "zkd-Latn-MM": "zkd", + "zko-Cyrl-RU": "zko", + "zkp-Latn-BR": "zkp", + "zkt-Kits-CN": "zkt", + "zku-Latn-AU": "zku", + "zkz-Cyrl-RU": "zkz", + "zla-Latn-CD": "zla", + "zlj-Hani-CN": "zlj", + "zlj-Latn-CN": "zlj-Latn", + "zlm-Latn-TG": "zlm", + "zln-Hani-CN": "zln", + "zlq-Hani-CN": "zlq", + "zma-Latn-AU": "zma", + "zmb-Latn-CD": "zmb", + "zmc-Latn-AU": "zmc", + "zmd-Latn-AU": "zmd", + "zme-Latn-AU": "zme", + "zmf-Latn-CD": "zmf", + "zmg-Latn-AU": "zmg", + "zmh-Latn-PG": "zmh", + "zmi-Latn-MY": "zmi", + "zmj-Latn-AU": "zmj", + "zmk-Latn-AU": "zmk", + "zml-Latn-AU": "zml", + "zmm-Latn-AU": "zmm", + "zmn-Latn-GA": "zmn", + "zmo-Latn-SD": "zmo", + "zmp-Latn-CD": "zmp", + "zmq-Latn-CD": "zmq", + "zmr-Latn-AU": "zmr", + "zms-Latn-CD": "zms", + "zmt-Latn-AU": "zmt", + "zmu-Latn-AU": "zmu", + "zmv-Latn-AU": "zmv", + "zmw-Latn-CD": "zmw", + "zmx-Latn-CG": "zmx", + "zmy-Latn-AU": "zmy", + "zmz-Latn-CD": "zmz", + "zna-Latn-TD": "zna", + "zne-Latn-ZZ": "zne", + "zng-Latn-VN": "zng", + "znk-Latn-AU": "znk", + "zns-Latn-NG": "zns", + "zoc-Latn-MX": "zoc", + "zoh-Latn-MX": "zoh", + "zom-Latn-IN": "zom", + "zoo-Latn-MX": "zoo", + "zoq-Latn-MX": "zoq", + "zor-Latn-MX": "zor", + "zos-Latn-MX": "zos", + "zpa-Latn-MX": "zpa", + "zpb-Latn-MX": "zpb", + "zpc-Latn-MX": "zpc", + "zpd-Latn-MX": "zpd", + "zpe-Latn-MX": "zpe", + "zpf-Latn-MX": "zpf", + "zpg-Latn-MX": "zpg", + "zph-Latn-MX": "zph", + "zpi-Latn-MX": "zpi", + "zpj-Latn-MX": "zpj", + "zpk-Latn-MX": "zpk", + "zpl-Latn-MX": "zpl", + "zpm-Latn-MX": "zpm", + "zpn-Latn-MX": "zpn", + "zpo-Latn-MX": "zpo", + "zpp-Latn-MX": "zpp", + "zpq-Latn-MX": "zpq", + "zpr-Latn-MX": "zpr", + "zps-Latn-MX": "zps", + "zpt-Latn-MX": "zpt", + "zpu-Latn-MX": "zpu", + "zpv-Latn-MX": "zpv", + "zpw-Latn-MX": "zpw", + "zpx-Latn-MX": "zpx", + "zpy-Latn-MX": "zpy", + "zpz-Latn-MX": "zpz", + "zqe-Hani-CN": "zqe", + "zqe-Latn-CN": "zqe-Latn", + "zrn-Latn-TD": "zrn", + "zro-Latn-EC": "zro", + "zrp-Hebr-FR": "zrp", + "zrs-Latn-ID": "zrs", + "zsa-Latn-PG": "zsa", + "zsr-Latn-MX": "zsr", + "zsu-Latn-PG": "zsu", + "zte-Latn-MX": "zte", + "ztg-Latn-MX": "ztg", + "ztl-Latn-MX": "ztl", + "ztm-Latn-MX": "ztm", + "ztn-Latn-MX": "ztn", + "ztp-Latn-MX": "ztp", + "ztq-Latn-MX": "ztq", + "zts-Latn-MX": "zts", + "ztt-Latn-MX": "ztt", + "ztu-Latn-MX": "ztu", + "ztx-Latn-MX": "ztx", + "zty-Latn-MX": "zty", + "zu-Latn-ZA": "zu", + "zua-Latn-NG": "zua", + "zuh-Latn-PG": "zuh", + "zum-Arab-OM": "zum", + "zun-Latn-US": "zun", + "zuy-Latn-CM": "zuy", + "zyg-Hani-CN": "zyg", + "zyj-Hani-CN": "zyj-Hani", + "zyj-Latn-CN": "zyj", + "zyn-Hani-CN": "zyn", + "zyp-Latn-MM": "zyp", + "zza-Latn-TR": "zza", + "zzj-Hani-CN": "zzj", +}; + +for (let [tag, maximal] of Object.entries(maxLikelySubtags)) { + assertEq(new Intl.Locale(tag).maximize().toString(), maximal); +} + +for (let [tag, minimal] of Object.entries(minLikelySubtags)) { + assertEq(new Intl.Locale(tag).minimize().toString(), minimal); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/likely-subtags.js b/js/src/tests/non262/Intl/Locale/likely-subtags.js new file mode 100644 index 0000000000..3b3e4bd905 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/likely-subtags.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +var testDataMaximal = { + // Keeps "und" primary language. + "und-AQ": "und-Latn-AQ", + + // Modifies primary language. + "und-Cyrl-RO": "bg-Cyrl-RO", +} + +var testDataMinimal = { + // Undefined primary language. + "und": "en", + "und-Thai": "th", + "und-419": "es-419", + "und-150": "ru", + "und-AT": "de-AT", + + // https://ssl.icu-project.org/trac/ticket/13786 + "aae-Latn-IT": "aae", + "aae-Thai-CO": "aae-Thai-CO", + + // https://ssl.icu-project.org/trac/ticket/10220 + // https://ssl.icu-project.org/trac/ticket/12345 + "und-CW": "pap", + "und-US": "en", + "zh-Hant": "zh-TW", + "zh-Hani": "zh-Hani", +}; + +// Add variants, extensions, and privateuse subtags and ensure they don't +// modify the result of the likely subtags algorithms. +var extras = [ + "fonipa", + "a-not-assigned", + "u-attr", + "u-co", + "u-co-phonebk", + "x-private", +]; + +for (var [tag, maximal] of Object.entries(testDataMaximal)) { + assertEq(new Intl.Locale(tag).maximize().toString(), maximal); + assertEq(new Intl.Locale(maximal).maximize().toString(), maximal); + + for (var extra of extras) { + assertEq(new Intl.Locale(tag + "-" + extra).maximize().toString(), maximal + "-" + extra); + } +} + +for (var [tag, minimal] of Object.entries(testDataMinimal)) { + assertEq(new Intl.Locale(tag).minimize().toString(), minimal); + assertEq(new Intl.Locale(minimal).minimize().toString(), minimal); + + for (var extra of extras) { + assertEq(new Intl.Locale(tag + "-" + extra).minimize().toString(), minimal + "-" + extra); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/same-compartment.js b/js/src/tests/non262/Intl/Locale/same-compartment.js new file mode 100644 index 0000000000..fb71c85f6c --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/same-compartment.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.wrapWithProto) + +var tag = "de-Latn-AT-u-ca-gregory-nu-latn-co-phonebk-kf-false-kn-hc-h23"; +var locale = new Intl.Locale(tag); +var scwLocale = wrapWithProto(locale, Intl.Locale.prototype); + +for (var [key, {get, value = get}] of Object.entries(Object.getOwnPropertyDescriptors(Intl.Locale.prototype))) { + if (typeof value === "function") { + if (key !== "constructor") { + var expectedValue = value.call(locale); + + if (typeof expectedValue === "string" || typeof expectedValue === "boolean") { + assertEq(value.call(scwLocale), expectedValue, key); + } else if (expectedValue instanceof Intl.Locale) { + assertEq(value.call(scwLocale).toString(), expectedValue.toString(), key); + } else { + throw new Error("unexpected result value"); + } + } else { + assertEq(new value(scwLocale).toString(), new value(locale).toString(), key); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/Locale/shell.js b/js/src/tests/non262/Intl/Locale/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/shell.js diff --git a/js/src/tests/non262/Intl/Locale/surface.js b/js/src/tests/non262/Intl/Locale/surface.js new file mode 100644 index 0000000000..ba90182436 --- /dev/null +++ b/js/src/tests/non262/Intl/Locale/surface.js @@ -0,0 +1,98 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +function assertProperty(object, name, desc) { + assertEq(desc === undefined || (typeof desc === "object" && desc !== null), true, + "desc is a property descriptor"); + + var actual = Object.getOwnPropertyDescriptor(object, name); + if (desc === undefined) { + assertEq(actual, desc, `property ${String(name)} is absent`); + return; + } + assertEq(actual !== undefined, true, `property ${String(name)} is present`); + + var fields = ["value", "writable", "enumerable", "configurable", "get", "set"]; + for (var field of fields) { + if (Object.prototype.hasOwnProperty.call(desc, field)) { + assertEq(actual[field], desc[field], `unexpected value for ${field}`); + } + } +} + +function assertBuiltinFunction(fn, length, name) { + assertProperty(fn, "length", { + value: length, writable: false, enumerable: false, configurable: true, + }); +} + +function assertBuiltinMethod(object, propName, length, name) { + var desc = Object.getOwnPropertyDescriptor(object, propName); + assertProperty(object, propName, { + value: desc.value, writable: true, enumerable: false, configurable: true + }); + assertBuiltinFunction(desc.value, length, name); +} + +function assertBuiltinGetter(object, propName, length, name) { + var desc = Object.getOwnPropertyDescriptor(object, propName); + + assertBuiltinFunction(desc.get, length, name); +} + +// Intl.Locale( tag[, options] ) +assertBuiltinFunction(Intl.Locale, 1, "Locale"); + +// Properties of the Intl.Locale Constructor + +// Intl.Locale.prototype +assertProperty(Intl.Locale, "prototype", { + value: Intl.Locale.prototype, writable: false, enumerable: false, configurable: false, +}); + +// Properties of the Intl.Locale Prototype Object + +// Intl.Locale.prototype.constructor +assertProperty(Intl.Locale.prototype, "constructor", { + value: Intl.Locale, writable: true, enumerable: false, configurable: true, +}); + +// Intl.Locale.prototype[ @@toStringTag ] +assertProperty(Intl.Locale.prototype, Symbol.toStringTag, { + value: "Intl.Locale", writable: false, enumerable: false, configurable: true, +}); + +// Intl.Locale.prototype.toString () +assertBuiltinMethod(Intl.Locale.prototype, "toString", 0, "toString"); + +// get Intl.Locale.prototype.baseName +assertBuiltinGetter(Intl.Locale.prototype, "baseName", 0, "get baseName"); + +// get Intl.Locale.prototype.calendar +assertBuiltinGetter(Intl.Locale.prototype, "calendar", 0, "get calendar"); + +// get Intl.Locale.prototype.collation +assertBuiltinGetter(Intl.Locale.prototype, "collation", 0, "get collation"); + +// get Intl.Locale.prototype.hourCycle +assertBuiltinGetter(Intl.Locale.prototype, "hourCycle", 0, "get hourCycle"); + +// get Intl.Locale.prototype.caseFirst +assertBuiltinGetter(Intl.Locale.prototype, "caseFirst", 0, "get caseFirst"); + +// get Intl.Locale.prototype.numeric +assertBuiltinGetter(Intl.Locale.prototype, "numeric", 0, "get numeric"); + +// get Intl.Locale.prototype.numberingSystem +assertBuiltinGetter(Intl.Locale.prototype, "numberingSystem", 0, "get numberingSystem"); + +// get Intl.Locale.prototype.language +assertBuiltinGetter(Intl.Locale.prototype, "language", 0, "get language"); + +// get Intl.Locale.prototype.script +assertBuiltinGetter(Intl.Locale.prototype, "script", 0, "get script"); + +// get Intl.Locale.prototype.region +assertBuiltinGetter(Intl.Locale.prototype, "region", 0, "get region"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js b/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js new file mode 100644 index 0000000000..70aaa7bd75 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// The implementation of the format function uses the C++ StringBuffer class, +// which changes its storage model at 32 characters, and uses it in a way which +// also means that there's no room for null-termination at this limit. +// This test makes sure that none of this affects the output. + +var format = new Intl.NumberFormat("it-IT", {minimumFractionDigits: 1}); + +assertEq(format.format(1123123123123123123123.1), "1.123.123.123.123.123.100.000,0"); +assertEq(format.format(12123123123123123123123.1), "12.123.123.123.123.122.000.000,0"); +assertEq(format.format(123123123123123123123123.1), "123.123.123.123.123.120.000.000,0"); + +// Ensure the ICU output matches Number.prototype.toFixed. +function formatToFixed(x) { + var mfd = format.resolvedOptions().maximumFractionDigits; + var s = x.toFixed(mfd); + + // To keep it simple we assume |s| is always in exponential form. + var m = s.match(/^(\d)\.(\d+)e\+(\d+)$/); + assertEq(m !== null, true); + s = m[1] + m[2].padEnd(m[3], "0"); + + // Group digits and append fractional part. + m = s.match(/\d{1,3}(?=(?:\d{3})*$)/g); + assertEq(m !== null, true); + return m.join(".") + ",0"; +} + +assertEq(formatToFixed(1123123123123123123123.1), "1.123.123.123.123.123.100.000,0"); +assertEq(formatToFixed(12123123123123123123123.1), "12.123.123.123.123.122.000.000,0"); +assertEq(formatToFixed(123123123123123123123123.1), "123.123.123.123.123.120.000.000,0"); + +reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js b/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js new file mode 100644 index 0000000000..7b670d0873 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure the int64_t optimization when formatting a BigInt value works correctly by testing with +// various integers around the (u)int[32,64] limits. + +const limits = { + int32: { + min: -0x80000000n, + max: 0x7FFFFFFFn, + }, + uint32: { + min: 0n, + max: 0xFFFFFFFFn + }, + int64: { + min: -0x8000000000000000n, + max: 0x7FFFFFFFFFFFFFFFn, + }, + uint64: { + min: 0n, + max: 0xFFFFFFFFFFFFFFFFn + }, +}; + +const nf = new Intl.NumberFormat("en", {useGrouping: false}); + +const diff = 10n; + +for (const int of Object.values(limits)) { + for (let i = -diff; i <= diff; ++i) { + let n = int.min + i; + assertEq(nf.format(n), n.toString()); + + let m = int.max + i; + assertEq(nf.format(m), m.toString()); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/browser.js b/js/src/tests/non262/Intl/NumberFormat/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/browser.js diff --git a/js/src/tests/non262/Intl/NumberFormat/call.js b/js/src/tests/non262/Intl/NumberFormat/call.js new file mode 100644 index 0000000000..14ecf99eb5 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/call.js @@ -0,0 +1,184 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function IsObject(o) { + return Object(o) === o; +} + +function IsPrimitive(o) { + return Object(o) !== o; +} + +function thisValues() { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.map(ctor => { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Object inheriting from an Intl constructor prototype. + Object.create(ctor.prototype), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; + })), + ]; +} + +const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0]; + +// Invoking [[Call]] for Intl.NumberFormat returns a new instance unless called +// with an instance inheriting from Intl.NumberFormat.prototype. +for (let thisValue of thisValues()) { + let obj = Intl.NumberFormat.call(thisValue); + + if (!Intl.NumberFormat.prototype.isPrototypeOf(thisValue)) { + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.NumberFormat, true); + if (IsObject(thisValue)) + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); + } else { + assertEq(Object.is(obj, thisValue), true); + assertEq(obj instanceof Intl.NumberFormat, true); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); + } +} + +// Intl.NumberFormat uses the legacy Intl constructor compromise semantics. +// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns true. +for (let thisValue of thisValues().filter(IsObject)) { + let isPrototypeOf = Intl.NumberFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false; + Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return true; + }, configurable: true + }); + let obj = Intl.NumberFormat.call(thisValue); + delete Intl.NumberFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), isPrototypeOf); + assertEq(hasInstanceCalled, false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); +} +// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns false. +for (let thisValue of thisValues().filter(IsObject)) { + let isPrototypeOf = Intl.NumberFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false; + Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return false; + }, configurable: true + }); + let obj = Intl.NumberFormat.call(thisValue); + delete Intl.NumberFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), isPrototypeOf); + assertEq(obj instanceof Intl.NumberFormat, true); + assertEq(hasInstanceCalled, false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); +} +// - Test with primitive values. +for (let thisValue of thisValues().filter(IsPrimitive)) { + // Ensure @@hasInstance is not called. + Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { + value() { assertEq(true, false); }, configurable: true + }); + let obj = Intl.NumberFormat.call(thisValue); + delete Intl.NumberFormat[Symbol.hasInstance]; + + assertEq(Object.is(obj, thisValue), false); + assertEq(obj instanceof Intl.NumberFormat, true); +} + +// Throws an error when attempting to install [[FallbackSymbol]] twice. +{ + let thisValue = Object.create(Intl.NumberFormat.prototype); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); + + assertEq(Intl.NumberFormat.call(thisValue), thisValue); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); + + assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); +} + +// Throws an error when the thisValue is non-extensible. +{ + let thisValue = Object.create(Intl.NumberFormat.prototype); + Object.preventExtensions(thisValue); + + assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); +} + +// [[FallbackSymbol]] is installed as a frozen property holding an Intl.NumberFormat instance. +{ + let thisValue = Object.create(Intl.NumberFormat.prototype); + Intl.NumberFormat.call(thisValue); + + let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol); + assertEq(desc !== undefined, true); + assertEq(desc.writable, false); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, false); + assertEq(desc.value instanceof Intl.NumberFormat, true); +} + +// Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]] +// during initialization. +{ + let thisValue = {}; + let options = { + get useGrouping() { + Object.setPrototypeOf(thisValue, Intl.NumberFormat.prototype); + return false; + } + }; + let obj = Intl.NumberFormat.call(thisValue, undefined, options); + assertEq(Object.is(obj, thisValue), true); + assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); +} +{ + let thisValue = Object.create(Intl.NumberFormat.prototype); + let options = { + get useGrouping() { + Object.setPrototypeOf(thisValue, Object.prototype); + return false; + } + }; + let obj = Intl.NumberFormat.call(thisValue, undefined, options); + assertEq(Object.is(obj, thisValue), false); + assertEqArray(Object.getOwnPropertySymbols(thisValue), []); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js b/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js new file mode 100644 index 0000000000..0053b2737e --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +// Test subclassing %Intl.NumberFormat% works correctly. +class MyNumberFormat extends Intl.NumberFormat {} + +var obj = new MyNumberFormat(); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, []); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], MyNumberFormat); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], Intl.NumberFormat); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyNumberFormat, [], Array); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.NumberFormat, [], Array); +assertEq(obj instanceof Intl.NumberFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %NumberFormatPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.NumberFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.NumberFormat, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.NumberFormat, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js b/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js new file mode 100644 index 0000000000..806f889d6f --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js @@ -0,0 +1,70 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var otherGlobal = newGlobal(); + +var numberFormat = new Intl.NumberFormat(); +var ccwNumberFormat = new otherGlobal.Intl.NumberFormat(); + +// Test Intl.NumberFormat.prototype.format with a CCW object. +var Intl_NumberFormat_format_get = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get; + +assertEq(Intl_NumberFormat_format_get.call(ccwNumberFormat)(0), + Intl_NumberFormat_format_get.call(numberFormat)(0)); + +// Test Intl.NumberFormat.prototype.formatToParts with a CCW object. +var Intl_NumberFormat_formatToParts = Intl.NumberFormat.prototype.formatToParts; + +assertEq(deepEqual(Intl_NumberFormat_formatToParts.call(ccwNumberFormat, 0), + Intl_NumberFormat_formatToParts.call(numberFormat, 0)), + true); + +// Test Intl.NumberFormat.prototype.resolvedOptions with a CCW object. +var Intl_NumberFormat_resolvedOptions = Intl.NumberFormat.prototype.resolvedOptions; + +assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(ccwNumberFormat), + Intl_NumberFormat_resolvedOptions.call(numberFormat)), + true); + +// Special case for Intl.NumberFormat: The Intl fallback symbol. + +function fallbackSymbol(global) { + var NF = global.Intl.NumberFormat; + return Object.getOwnPropertySymbols(NF.call(Object.create(NF.prototype)))[0]; +} + +const intlFallbackSymbol = fallbackSymbol(this); +const otherIntlFallbackSymbol = fallbackSymbol(otherGlobal); +assertEq(intlFallbackSymbol === otherIntlFallbackSymbol, false); + +// Test when the fallback symbol points to a CCW NumberFormat object. +var objWithFallbackCCWNumberFormat = { + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: ccwNumberFormat, +}; + +assertEq(Intl_NumberFormat_format_get.call(objWithFallbackCCWNumberFormat)(0), + Intl_NumberFormat_format_get.call(numberFormat)(0)); + +assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(objWithFallbackCCWNumberFormat), + Intl_NumberFormat_resolvedOptions.call(numberFormat)), + true); + +// Ensure the fallback symbol(s) are not accessed for CCW NumberFormat objects. +var ccwNumberFormatWithPoisonedFallback = new otherGlobal.Intl.NumberFormat(); +Object.setPrototypeOf(ccwNumberFormatWithPoisonedFallback, Intl.NumberFormat.prototype); +Object.defineProperty(ccwNumberFormatWithPoisonedFallback, intlFallbackSymbol, { + get() { throw new Error(); } +}); +Object.defineProperty(ccwNumberFormatWithPoisonedFallback, otherIntlFallbackSymbol, { + get() { throw new Error(); } +}); + +assertEq(Intl_NumberFormat_format_get.call(ccwNumberFormatWithPoisonedFallback)(0), + Intl_NumberFormat_format_get.call(numberFormat)(0)); + +assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(ccwNumberFormatWithPoisonedFallback), + Intl_NumberFormat_resolvedOptions.call(numberFormat)), + true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js b/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js new file mode 100644 index 0000000000..bf2b9adcd8 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Integer, Decimal, Fraction, Currency, Literal, +} = NumberFormatParts; + +const testcases = [ + { + locale: "en-CA", + options: { + style: "currency", + currency: "USD", + currencyDisplay: "narrowSymbol", + }, + values: [ + {value: 123, string: "US$123.00", + parts: [Currency("US$"), Integer("123"), Decimal("."), Fraction("00")]}, + ], + }, + + // And for comparison "symbol" currency-display. + + { + locale: "en-CA", + options: { + style: "currency", + currency: "USD", + currencyDisplay: "symbol", + }, + values: [ + {value: 123, string: "US$123.00", + parts: [Currency("US$"), Integer("123"), Decimal("."), Fraction("00")]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js b/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js new file mode 100644 index 0000000000..6f91d6edd4 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js @@ -0,0 +1,192 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, + Currency, Literal, +} = NumberFormatParts; + +const testcases = [ + // "auto": Show the sign on negative numbers only. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "auto", + }, + values: [ + {value: +0, string: "$0.00", + parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + {value: -0, string: "($0.00)", + parts: [Literal("("), Currency("$"), Integer("0"), Decimal("."), Fraction("00"), Literal(")")]}, + + {value: 1, string: "$1.00", + parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]}, + {value: -1, string: "($1.00)", + parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]}, + + {value: Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]}, + {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]}, + + {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + ], + }, + + // "never": Show the sign on neither positive nor negative numbers. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "never", + }, + values: [ + {value: +0, string: "$0.00", parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + {value: -0, string: "$0.00", parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + + {value: 1, string: "$1.00", parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]}, + {value: -1, string: "$1.00", parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]}, + + {value: Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]}, + {value: -Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]}, + + {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + ], + }, + + // "always": Show the sign on positive and negative numbers including zero. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "always", + }, + values: [ + {value: +0, string: "+$0.00", + parts: [PlusSign("+"), Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + {value: -0, string: "($0.00)", + parts: [Literal("("), Currency("$"), Integer("0"), Decimal("."), Fraction("00"), Literal(")")]}, + + {value: 1, string: "+$1.00", + parts: [PlusSign("+"), Currency("$"), Integer("1"), Decimal("."), Fraction("00")]}, + {value: -1, string: "($1.00)", + parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]}, + + {value: Infinity, string: "+$∞", parts: [PlusSign("+"), Currency("$"), Inf("∞")]}, + {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]}, + + {value: NaN, string: "+$NaN", parts: [PlusSign("+"), Currency("$"), Nan("NaN")]}, + {value: -NaN, string: "+$NaN", parts: [PlusSign("+"), Currency("$"), Nan("NaN")]}, + ], + }, + + // "exceptZero": Show the sign on positive and negative numbers but not zero. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "exceptZero", + }, + values: [ + {value: +0, string: "$0.00", + parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + {value: -0, string: "$0.00", + parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]}, + + {value: 1, string: "+$1.00", + parts: [PlusSign("+"), Currency("$"), Integer("1"), Decimal("."), Fraction("00")]}, + {value: -1, string: "($1.00)", + parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]}, + + {value: Infinity, string: "+$∞", parts: [PlusSign("+"), Currency("$"), Inf("∞")]}, + {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]}, + + {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]}, + ], + }, + + // Tests with suppressed fractional digits. + + // "auto": Show the sign on negative numbers only. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "auto", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]}, + {value: -0.1, string: "($0)", parts: [Literal("("), Currency("$"), Integer("0"), Literal(")")]}, + ], + }, + + // "never": Show the sign on neither positive nor negative numbers. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "never", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]}, + {value: -0.1, string: "$0", parts: [Currency("$"), Integer("0")]}, + ], + }, + + // "always": Show the sign on positive and negative numbers including zero. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "always", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "+$0", parts: [PlusSign("+"), Currency("$"), Integer("0")]}, + {value: -0.1, string: "($0)", parts: [Literal("("), Currency("$"), Integer("0"), Literal(")")]}, + ], + }, + + // "exceptZero": Show the sign on positive and negative numbers but not zero. + { + locale: "en", + options: { + style: "currency", + currency: "USD", + currencySign: "accounting", + signDisplay: "exceptZero", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }, + + values: [ + {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]}, + {value: -0.1, string: "$0", parts: [Currency("$"), Integer("0")]}, + ], + } +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js b/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js new file mode 100644 index 0000000000..42bf78c3fe --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Check for duplicate variants and singletons case-insensitively, but don't +// check in privateuse components. + +function checkInvalidLocale(locale) +{ + try + { + new Intl.NumberFormat(locale); + throw new Error("didn't throw"); + } + catch (e) + { + assertEq(e instanceof RangeError, true, + "expected RangeError for locale '" + locale + "', got " + e); + } +} + +var badLocales = + [ + "en-u-foo-U-foo", + "en-tester-Tester", + "en-tesTER-TESter", + "de-DE-u-kn-true-U-kn-true", + "ar-u-foo-q-bar-u-baz", + "ar-z-moo-u-foo-q-bar-z-eit-u-baz", + ]; + +for (var locale of badLocales) + checkInvalidLocale(locale); + +// Fully-privateuse locales are rejected. +for (var locale of badLocales) + assertThrowsInstanceOf(() => new Intl.NumberFormat("x-" + locale), RangeError); + +// Locales with trailing privateuse also okay. +for (var locale of badLocales) +{ + new Intl.NumberFormat("en-x-" + locale).format(5); + new Intl.NumberFormat("en-u-foo-x-u-" + locale).format(5); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js b/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js new file mode 100644 index 0000000000..ba257ecd71 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js @@ -0,0 +1,75 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1093421; +var summary = + "new Intl.NumberFormat(..., { style: 'currency', currency: '...', " + + "currencyDisplay: 'name' or 'code' }) should have behavior other than " + + "throwing"; + +print(BUGNUMBER + ": " + summary); + +//----------------------------------------------------------------------------- + +// Test that currencyDisplay: "code" behaves correctly and doesn't throw. + +var usdCodeOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "code", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var usDollarsCode = new Intl.NumberFormat("en-US", usdCodeOptions); +assertEq(/USD/.test(usDollarsCode.format(25)), true); + +// ISO 4217 currency codes are formed from an ISO 3166-1 alpha-2 country code +// followed by a third letter. ISO 3166 guarantees that no country code +// starting with "X" will ever be assigned. Stepping carefully around a few +// 4217-designated special "currencies", XQQ will never have a representation. +// Thus, yes: this really is specified to work, as unrecognized or unsupported +// codes pass into the string unmodified. +var xqqCodeOptions = + { + style: "currency", + currency: "XQQ", + currencyDisplay: "code", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var xqqMoneyCode = new Intl.NumberFormat("en-US", xqqCodeOptions); +assertEq(/XQQ/.test(xqqMoneyCode.format(25)), true); + +// Test that currencyDisplay: "name" behaves without throwing. (Unlike the two +// above tests, the results here aren't guaranteed as the name is +// implementation-defined.) +var usdNameOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "name", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var usDollarsName = new Intl.NumberFormat("en-US", usdNameOptions); +assertEq(usDollarsName.format(25), "25 US dollars"); + +// But if the implementation doesn't recognize the currency, the provided code +// is used in place of a proper name, unmolested. +var xqqNameOptions = + { + style: "currency", + currency: "XQQ", + currencyDisplay: "name", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var xqqMoneyName = new Intl.NumberFormat("en-US", xqqNameOptions); +assertEq(/XQQ/.test(xqqMoneyName.format(25)), true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/format-string.js b/js/src/tests/non262/Intl/NumberFormat/format-string.js new file mode 100644 index 0000000000..88b11008d9 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/format-string.js @@ -0,0 +1,150 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +function groupByThree(s) { + return String(s).split("").reduceRight((acc, x) => x + (acc.match(/^\d{3}/) ? "," : "") + acc, ""); +} + +const tests = [ + {value: "", expected: "0"}, + {value: "0", expected: "0"}, + {value: "+0", expected: "0"}, + {value: "-0", expected: "-0"}, + + {value: "Infinity", expected: "∞"}, + {value: "+Infinity", expected: "∞"}, + {value: "-Infinity", expected: "-∞"}, + + {value: "NaN", expected: "NaN"}, + {value: "invalid", expected: "NaN"}, + + // Integer 1 with and without fractional/exponent part. + {value: "1", expected: "1"}, + {value: "1.", expected: "1"}, + {value: "1.0", expected: "1"}, + {value: "1.00", expected: "1"}, + {value: "1e0", expected: "1"}, + {value: "1e+0", expected: "1"}, + {value: "1e-0", expected: "1"}, + + // Leading zeros. + {value: "01", expected: "1"}, + {value: "01.", expected: "1"}, + {value: "01.0", expected: "1"}, + {value: "01.00", expected: "1"}, + {value: "01e0", expected: "1"}, + {value: "01e+0", expected: "1"}, + {value: "01e-0", expected: "1"}, + + // Large values. + {value: "1e300", expected: "1" + ",000".repeat(100)}, + {value: "1e3000", expected: "1" + ",000".repeat(1000)}, + {value: "9007199254740991", expected: "9,007,199,254,740,991"}, + {value: "9007199254740992", expected: "9,007,199,254,740,992"}, + {value: "9007199254740993", expected: "9,007,199,254,740,993"}, + + {value: "-1e300", expected: "-1" + ",000".repeat(100)}, + {value: "-1e3000", expected: "-1" + ",000".repeat(1000)}, + {value: "-9007199254740991", expected: "-9,007,199,254,740,991"}, + {value: "-9007199254740992", expected: "-9,007,199,254,740,992"}, + {value: "-9007199254740993", expected: "-9,007,199,254,740,993"}, + + // Small values. + {value: "0.10000000000000000001", expected: "0.10000000000000000001"}, + {value: "0.00000000000000000001", expected: "0.00000000000000000001"}, + {value: "1e-20", expected: "0.00000000000000000001"}, + {value: "1e-30", expected: "0"}, + + {value: "-0.10000000000000000001", expected: "-0.10000000000000000001"}, + {value: "-0.00000000000000000001", expected: "-0.00000000000000000001"}, + {value: "-1e-20", expected: "-0.00000000000000000001"}, + {value: "-1e-30", expected: "-0"}, + + // Non-standard exponent notation. + {value: ".001e-2", expected: "0.00001"}, + {value: "123.001e-2", expected: "1.23001"}, + {value: "1000e-2", expected: "10"}, + {value: "1000e+2", expected: "100,000"}, + {value: "1000e-0", expected: "1,000"}, + + // Non-decimal strings. + {value: "0b101", expected: "5"}, + {value: "0o377", expected: "255"}, + {value: "0xdeadBEEF", expected: "3,735,928,559"}, + {value: "0B0011", expected: "3"}, + {value: "0O0777", expected: "511"}, + {value: "0X0ABC", expected: "2,748"}, + {value: "0b" + "1".repeat(1000), expected: groupByThree((2n ** 1000n) - 1n)}, + {value: "0o1" + "7".repeat(333), expected: groupByThree((2n ** 1000n) - 1n)}, + {value: "0x" + "f".repeat(250), expected: groupByThree((2n ** 1000n) - 1n)}, + + // Non-decimal strings don't accept a sign. + {value: "+0xbad", expected: "NaN"}, + {value: "-0xbad", expected: "NaN"}, +]; + +// https://tc39.es/ecma262/#prod-StrWhiteSpaceChar +const strWhiteSpaceChar = [ + "", + + // https://tc39.es/ecma262/#sec-white-space + "\t", "\v", "\f", " ", "\u00A0", "\uFEFF", + "\u1680", "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", + "\u2007", "\u2008", "\u2009", "\u200A", "\u202F", "\u205F", "\u3000", + + // https://tc39.es/ecma262/#sec-line-terminators + "\n", "\r", "\u2028", "\u2029", +]; + +let nf = new Intl.NumberFormat("en", {maximumFractionDigits: 20}); +for (let {value, expected} of tests) { + for (let ws of strWhiteSpaceChar) { + assertEq(nf.format(ws + value), expected); + assertEq(nf.format(value + ws), expected); + assertEq(nf.format(ws + value + ws), expected); + } +} + +// The specification doesn't impose any limits for the exponent, but we throw +// an error if the exponent is too large. +{ + let nf = new Intl.NumberFormat("en", {useGrouping: false}); + for (let value of [ + // ICU limit is 999'999'999 (exclusive). + ".1e-999999999", + ".1e+999999999", + + // We limit positive exponents to 9'999'999 (inclusive). + "1e+9999999", + "1e+99999999", + + // Int32 overflow when computing the exponent. + ".1e-2147483649", + ".1e-2147483648", + ".1e-2147483647", + ".1e+2147483647", + ".1e+2147483648", + ".1e+2147483649", + ]) { + assertThrowsInstanceOf(() => nf.format(value), RangeError); + } + + // We allow up to ±9'999'999. + assertEq(nf.format(".1e-9999999"), "0"); + assertEq(nf.format(".1e+9999999"), "1" + "0".repeat(9_999_999 - 1)); + + // Negative exponents are even valid up to -999'999'998 + assertEq(nf.format(".1e-999999998"), "0"); +} + +// Combine extreme values with other rounding modes. +{ + let nf = new Intl.NumberFormat("en", { + minimumFractionDigits: 20, + roundingMode: "ceil", + roundingIncrement: 5000, + }); + assertEq(nf.format(".1e-999999998"), "0.00000000000000005000"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/format.js b/js/src/tests/non262/Intl/NumberFormat/format.js new file mode 100644 index 0000000000..db35251b20 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/format.js @@ -0,0 +1,55 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the format function with a diverse set of locales and options. + +var format; + +// Locale en-US; default options. +format = new Intl.NumberFormat("en-us"); +assertEq(format.format(0), "0"); +assertEq(format.format(-1), "-1"); +assertEq(format.format(123456789.123456789), "123,456,789.123"); + +// Locale en-US; currency USD. +// The US dollar uses two fractional digits, and negative values are commonly +// parenthesized. +format = new Intl.NumberFormat("en-us", {style: "currency", currency: "USD"}); +assertEq(format.format(0), "$0.00"); +assertEq(format.format(-1), "-$1.00"); +assertEq(format.format(123456789.123456789), "$123,456,789.12"); + +// Locale ja-JP; currency JPY. +// The Japanese yen has no subunit in real life. +format = new Intl.NumberFormat("ja-jp", {style: "currency", currency: "JPY"}); +assertEq(format.format(0), "¥0"); +assertEq(format.format(-1), "-¥1"); +assertEq(format.format(123456789.123456789), "¥123,456,789"); + +// Locale ar-JO; currency JOD. +// The Jordanian Dinar divides into 1000 fils. Jordan uses (real) Arabic digits. +format = new Intl.NumberFormat("ar-jo", {style: "currency", currency: "JOD"}); +assertEq(format.format(0), "\u{200F}٠٫٠٠٠ د.أ.\u{200F}"); +assertEq(format.format(-1), "\u{061C}-\u{200F}١٫٠٠٠ د.أ.\u{200F}"); +assertEq(format.format(123456789.123456789), "\u{200F}١٢٣٬٤٥٦٬٧٨٩٫١٢٣ د.أ.\u{200F}"); + +// Locale th-TH; Thai digits, percent, two significant digits. +format = new Intl.NumberFormat("th-th-u-nu-thai", + {style: "percent", + minimumSignificantDigits: 2, + maximumSignificantDigits: 2}); +assertEq(format.format(0), "๐.๐%"); +assertEq(format.format(-0.01), "-๑.๐%"); +assertEq(format.format(1.10), "๑๑๐%"); + + +// Test the .name property of the "format" getter. +var desc = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format"); +assertEq(desc !== undefined, true); +assertEq(typeof desc.get, "function"); +assertEq(desc.get.name, "get format"); + + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRange-BigInt.js b/js/src/tests/non262/Intl/NumberFormat/formatRange-BigInt.js new file mode 100644 index 0000000000..36ccdf59e3 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRange-BigInt.js @@ -0,0 +1,150 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +// Int64-BigInts which can be exactly represented as doubles receive a fast-path. + +const tests = { + "en": { + options: {}, + ranges: [ + // BigInt around Number.MIN_SAFE_INTEGER. + { + start: -0x20000000000001n, + end: -0x20000000000000n, + result: "-9,007,199,254,740,993 – -9,007,199,254,740,992", + }, + { + start: -0x20000000000000n, + end: -0x20000000000000n, + result: "~-9,007,199,254,740,992", + }, + { + start: -0x20000000000000n, + end: -0x1fffffffffffffn, + result: "-9,007,199,254,740,992 – -9,007,199,254,740,991", + }, + { + start: -0x1fffffffffffffn, + end: -0x1fffffffffffffn, + result: "~-9,007,199,254,740,991", + }, + { + start: -0x1fffffffffffffn, + end: -0x1ffffffffffffen, + result: "-9,007,199,254,740,991 – -9,007,199,254,740,990", + }, + { + start: -0x1ffffffffffffen, + end: -0x1ffffffffffffen, + result: "~-9,007,199,254,740,990", + }, + + // BigInt around Number.MAX_SAFE_INTEGER. + { + start: 0x1ffffffffffffen, + end: 0x1ffffffffffffen, + result: "~9,007,199,254,740,990", + }, + { + start: 0x1ffffffffffffen, + end: 0x1fffffffffffffn, + result: "9,007,199,254,740,990–9,007,199,254,740,991", + }, + { + start: 0x1fffffffffffffn, + end: 0x1fffffffffffffn, + result: "~9,007,199,254,740,991", + }, + { + start: 0x1fffffffffffffn, + end: 0x20000000000000n, + result: "9,007,199,254,740,991–9,007,199,254,740,992", + }, + { + start: 0x20000000000000n, + end: 0x20000000000000n, + result: "~9,007,199,254,740,992", + }, + { + start: 0x20000000000000n, + end: 0x20000000000001n, + result: "9,007,199,254,740,992–9,007,199,254,740,993", + }, + + // BigInt around INT64_MIN. + { + start: -0x8000000000000002n, + end: -0x8000000000000001n, + result: "-9,223,372,036,854,775,810 – -9,223,372,036,854,775,809", + }, + { + start: -0x8000000000000001n, + end: -0x8000000000000001n, + result: "~-9,223,372,036,854,775,809", + }, + { + start: -0x8000000000000001n, + end: -0x8000000000000000n, + result: "-9,223,372,036,854,775,809 – -9,223,372,036,854,775,808", + }, + { + start: -0x8000000000000000n, + end: -0x8000000000000000n, + result: "~-9,223,372,036,854,775,808", + }, + { + start: -0x8000000000000000n, + end: -0x7fffffffffffffffn, + result: "-9,223,372,036,854,775,808 – -9,223,372,036,854,775,807", + }, + { + start: -0x7fffffffffffffffn, + end: -0x7fffffffffffffffn, + result: "~-9,223,372,036,854,775,807", + }, + + // BigInt around INT64_MAX. + { + start: 0x7ffffffffffffffen, + end: 0x7ffffffffffffffen, + result: "~9,223,372,036,854,775,806", + }, + { + start: 0x7ffffffffffffffen, + end: 0x7fffffffffffffffn, + result: "9,223,372,036,854,775,806–9,223,372,036,854,775,807", + }, + { + start: 0x7fffffffffffffffn, + end: 0x7fffffffffffffffn, + result: "~9,223,372,036,854,775,807", + }, + { + start: 0x7fffffffffffffffn, + end: 0x8000000000000000n, + result: "9,223,372,036,854,775,807–9,223,372,036,854,775,808", + }, + { + start: 0x8000000000000000n, + end: 0x8000000000000000n, + result: "~9,223,372,036,854,775,808", + }, + { + start: 0x8000000000000000n, + end: 0x8000000000000001n, + result: "9,223,372,036,854,775,808–9,223,372,036,854,775,809", + }, + ], + }, +}; + +for (let [locale, {options, ranges}] of Object.entries(tests)) { + let nf = new Intl.NumberFormat(locale, options); + for (let {start, end, result} of ranges) { + assertEq(nf.formatRange(start, end), result, `${start}-${end}`); + assertEq(nf.formatRangeToParts(start, end).reduce((acc, part) => acc + part.value, ""), + result, `${start}-${end}`); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRange.js b/js/src/tests/non262/Intl/NumberFormat/formatRange.js new file mode 100644 index 0000000000..da5a940ca8 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRange.js @@ -0,0 +1,296 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +// String representation for Number.MAX_VALUE. +const en_Number_MAX_VALUE = "179,769,313,486,231,570" + ",000".repeat(97); +const de_Number_MAX_VALUE = en_Number_MAX_VALUE.replaceAll(",", "."); +const fr_Number_MAX_VALUE = en_Number_MAX_VALUE.replaceAll(",", " "); + +const tests = { + "en": { + options: {}, + ranges: [ + // Values around zero. + {start: 0, end: 0, result: "~0"}, + {start: 0, end: -0, result: "0–-0"}, + {start: -0, end: 0, result: "-0 – 0"}, + {start: -0, end: 0.1e-3, result: "-0 – 0"}, + {start: -0, end: "0.1e-3", result: "-0 – 0"}, + {start: "-0", end: 0.1e-3, result: "-0 – 0"}, + {start: -0, end: -0, result: "~-0"}, + {start: -0, end: -0.1, result: "-0 – -0.1"}, + + // Values starting at negative infinity. + {start: -Infinity, end: -Infinity, result: "~-∞"}, + {start: -Infinity, end: -0, result: "-∞ – -0"}, + {start: -Infinity, end: +0, result: "-∞ – 0"}, + {start: -Infinity, end: +Infinity, result: "-∞ – ∞"}, + + // Values ending at negative infinity. + {start: -Number.MAX_VALUE, end: -Infinity, result: "-" + en_Number_MAX_VALUE + " – -∞"}, + {start: -0, end: -Infinity, result: "-0 – -∞"}, + {start: 0, end: -Infinity, result: "0–-∞"}, + {start: Number.MAX_VALUE, end: -Infinity, result: en_Number_MAX_VALUE + "–-∞"}, + + // Values starting at positive infinity. + {start: Infinity, end: Number.MAX_VALUE, result: "∞–" + en_Number_MAX_VALUE}, + {start: Infinity, end: 0, result: "∞–0"}, + {start: Infinity, end: -0, result: "∞–-0"}, + {start: Infinity, end: -Number.MAX_VALUE, result: "∞–-" + en_Number_MAX_VALUE}, + {start: Infinity, end: -Infinity, result: "∞–-∞"}, + + // Values ending at positive infinity. + {start: Infinity, end: Infinity, result: "~∞"}, + + // Non-special cases. + {start: 1, end: 100, result: "1–100"}, + {start: -100, end: 100, result: "-100 – 100"}, + {start: -1000, end: -100, result: "-1,000 – -100"}, + {start: Math.PI, end: 123_456.789, result: "3.142–123,456.789"}, + {start: -Math.PI, end: Math.E, result: "-3.142 – 2.718"}, + { + start: Number.MAX_SAFE_INTEGER, + end: 9007199254740993, + result: "9,007,199,254,740,991–9,007,199,254,740,992", + }, + { + start: Number.MAX_SAFE_INTEGER, + end: "9007199254740993", + result: "9,007,199,254,740,991–9,007,199,254,740,993", + }, + + // Start value is larger than end value. + {start: -0, end: -0.1, result: "-0 – -0.1"}, + {start: -0, end: -Number.MAX_VALUE, result: "-0 – -" + en_Number_MAX_VALUE}, + {start: 1, end: 0, result: "1–0"}, + {start: 0, end: -1, result: "0–-1"}, + {start: 1, end: -1, result: "1–-1"}, + {start: -1, end: -2, result: "-1 – -2"}, + {start: "10e2", end: "1e-3", result: "1,000–0.001"}, + {start: "0x100", end: "1e1", result: "256–10"}, + {start: ".1e-999999", end: ".01e-999999", result: "~0"}, + {start: ".1e99999", end: "0", result: "100" + ",000".repeat(33332) + "–0"}, + // Number.MAX_VALUE is 1.7976931348623157e+308. + { + start: "1.7976931348623158e+308", + end: Number.MAX_VALUE, + result: "179,769,313,486,231,580" + ",000".repeat(97) + "–" + en_Number_MAX_VALUE, + }, + // Number.MIN_VALUE is 5e-324. + {start: "6e-324", end: Number.MIN_VALUE, result: "~0"}, + ], + }, + "de": { + options: {style: "currency", currency: "EUR"}, + ranges: [ + // Values around zero. + {start: 0, end: 0, result: "≈0,00 €"}, + {start: 0, end: -0, result: "0,00 € – -0,00 €"}, + {start: -0, end: 0, result: "-0,00 € – 0,00 €"}, + {start: -0, end: 0.1e-3, result: "-0,00 € – 0,00 €"}, + {start: -0, end: "0.1e-3", result: "-0,00 € – 0,00 €"}, + {start: "-0", end: 0.1e-3, result: "-0,00 € – 0,00 €"}, + {start: -0, end: -0, result: "≈-0,00 €"}, + {start: -0, end: -0.1, result: "-0,00–0,10 €"}, + + // Values starting at negative infinity. + {start: -Infinity, end: -Infinity, result: "≈-∞ €"}, + {start: -Infinity, end: -0, result: "-∞–0,00 €"}, + {start: -Infinity, end: +0, result: "-∞ € – 0,00 €"}, + {start: -Infinity, end: +Infinity, result: "-∞ € – ∞ €"}, + + // Values ending at negative infinity. + {start: -Number.MAX_VALUE, end: -Infinity, result: "-" + de_Number_MAX_VALUE + ",00–∞ €"}, + {start: -0, end: -Infinity, result: "-0,00–∞ €"}, + {start: 0, end: -Infinity, result: "0,00 € – -∞ €"}, + {start: Number.MAX_VALUE, end: -Infinity, result: de_Number_MAX_VALUE + ",00 € – -∞ €"}, + + // Values starting at positive infinity. + {start: Infinity, end: Number.MAX_VALUE, result: "∞–" + de_Number_MAX_VALUE + ",00 €"}, + {start: Infinity, end: 0, result: "∞–0,00 €"}, + {start: Infinity, end: -0, result: "∞ € – -0,00 €"}, + {start: Infinity, end: -Number.MAX_VALUE, result: "∞ € – -" + de_Number_MAX_VALUE + ",00 €"}, + {start: Infinity, end: -Infinity, result: "∞ € – -∞ €"}, + + // Values ending at positive infinity. + {start: Infinity, end: Infinity, result: "≈∞ €"}, + + // Non-special cases. + {start: 1, end: 100, result: "1,00–100,00 €"}, + {start: -100, end: 100, result: "-100,00 € – 100,00 €"}, + {start: -1000, end: -100, result: "-1.000,00–100,00 €"}, + {start: Math.PI, end: 123_456.789, result: "3,14–123.456,79 €"}, + {start: -Math.PI, end: Math.E, result: "-3,14 € – 2,72 €"}, + { + start: Number.MAX_SAFE_INTEGER, + end: 9007199254740993, + result: "9.007.199.254.740.991,00–9.007.199.254.740.992,00 €", + }, + { + start: Number.MAX_SAFE_INTEGER, + end: "9007199254740993", + result: "9.007.199.254.740.991,00–9.007.199.254.740.993,00 €", + }, + + // Start value is larger than end value. + {start: -0, end: -0.1, result: "-0,00–0,10 €"}, + {start: -0, end: -Number.MAX_VALUE, result: "-0,00–" + de_Number_MAX_VALUE + ",00 €"}, + {start: 1, end: 0, result: "1,00–0,00 €"}, + {start: 0, end: -1, result: "0,00 € – -1,00 €"}, + {start: 1, end: -1, result: "1,00 € – -1,00 €"}, + {start: -1, end: -2, result: "-1,00–2,00 €"}, + {start: "10e2", end: "1e-3", result: "1.000,00–0,00 €"}, + {start: "0x100", end: "1e1", result: "256,00–10,00 €"}, + {start: ".1e-999999", end: ".01e-999999", result: "≈0,00 €"}, + {start: ".1e99999", end: "0", result: "100" + ".000".repeat(33332) + ",00–0,00 €"}, + // Number.MAX_VALUE is 1.7976931348623157e+308. + { + start: "1.7976931348623158e+308", + end: Number.MAX_VALUE, + result: "179.769.313.486.231.580" + ".000".repeat(97) + ",00–" + de_Number_MAX_VALUE + ",00 €", + }, + // Number.MIN_VALUE is 5e-324. + {start: "6e-324", end: Number.MIN_VALUE, result: "≈0,00 €"}, + ], + }, + "fr": { + options: {style: "unit", unit: "meter"}, + ranges: [ + // Values around zero. + {start: 0, end: 0, result: "≃0 m"}, + {start: -0, end: 0, result: "-0 – 0 m"}, + {start: -0, end: 0, result: "-0 – 0 m"}, + {start: -0, end: 0.1e-3, result: "-0 – 0 m"}, + {start: -0, end: "0.1e-3", result: "-0 – 0 m"}, + {start: "-0", end: 0.1e-3, result: "-0 – 0 m"}, + {start: -0, end: -0, result: "≃-0 m"}, + {start: -0, end: -0.1, result: "-0 – -0,1 m"}, + + // Values starting at negative infinity. + {start: -Infinity, end: -Infinity, result: "≃-∞ m"}, + {start: -Infinity, end: -0, result: "-∞ – -0 m"}, + {start: -Infinity, end: +0, result: "-∞ – 0 m"}, + {start: -Infinity, end: +Infinity, result: "-∞ – ∞ m"}, + + // Values ending at negative infinity. + {start: -Number.MAX_VALUE, end: -Infinity, result: "-" + fr_Number_MAX_VALUE + " – -∞ m"}, + {start: -0, end: -Infinity, result: "-0 – -∞ m"}, + {start: 0, end: -Infinity, result: "0–-∞ m"}, + {start: Number.MAX_VALUE, end: -Infinity, result: fr_Number_MAX_VALUE + "–-∞ m"}, + + // Values starting at positive infinity. + {start: Infinity, end: Number.MAX_VALUE, result: "∞–" + fr_Number_MAX_VALUE + " m"}, + {start: Infinity, end: 0, result: "∞–0 m"}, + {start: Infinity, end: -0, result: "∞–-0 m"}, + {start: Infinity, end: -Number.MAX_VALUE, result: "∞–-" + fr_Number_MAX_VALUE + " m"}, + {start: Infinity, end: -Infinity, result: "∞–-∞ m"}, + + // Values ending at positive infinity. + {start: Infinity, end: Infinity, result: "≃∞ m"}, + + // Non-special cases. + {start: 1, end: 100, result: "1–100 m"}, + {start: -100, end: 100, result: "-100 – 100 m"}, + {start: -1000, end: -100, result: "-1 000 – -100 m"}, + {start: Math.PI, end: 123_456.789, result: "3,142–123 456,789 m"}, + {start: -Math.PI, end: Math.E, result: "-3,142 – 2,718 m"}, + { + start: Number.MAX_SAFE_INTEGER, + end: 9007199254740993, + result: "9 007 199 254 740 991–9 007 199 254 740 992 m", + }, + { + start: Number.MAX_SAFE_INTEGER, + end: "9007199254740993", + result: "9 007 199 254 740 991–9 007 199 254 740 993 m", + }, + + // Start value is larger than end value. + {start: -0, end: -0.1, result: "-0 – -0,1 m"}, + {start: -0, end: -Number.MAX_VALUE, result: "-0 – -" + fr_Number_MAX_VALUE + " m"}, + {start: 1, end: 0, result: "1–0 m"}, + {start: 0, end: -1, result: "0–-1 m"}, + {start: 1, end: -1, result: "1–-1 m"}, + {start: -1, end: -2, result: "-1 – -2 m"}, + {start: "10e2", end: "1e-3", result: "1 000–0,001 m"}, + {start: "0x100", end: "1e1", result: "256–10 m"}, + {start: ".1e-999999", end: ".01e-999999", result: "≃0 m"}, + {start: ".1e99999", end: "0", result: "100" + " 000".repeat(33332) + "–0 m"}, + // Number.MAX_VALUE is 1.7976931348623157e+308. + { + start: "1.7976931348623158e+308", + end: Number.MAX_VALUE, + result: "179 769 313 486 231 580" + " 000".repeat(97) + "–" + fr_Number_MAX_VALUE + " m", + }, + // Number.MIN_VALUE is 5e-324. + {start: "6e-324", end: Number.MIN_VALUE, result: "≃0 m"}, + ], + }, + // Non-ASCII digits. + "ar": { + options: {}, + ranges: [ + {start: -2, end: -1, result: "-٢–١"}, + {start: -1, end: -1, result: "~-١"}, + {start: -1, end: 0, result: "-١ – ٠"}, + {start: 0, end: 0, result: "~٠"}, + {start: 0, end: 1, result: "٠–١"}, + {start: 1, end: 1, result: "~١"}, + {start: 1, end: 2, result: "١–٢"}, + ], + }, + "th-u-nu-thai": { + options: {}, + ranges: [ + {start: -2, end: -1, result: "-๒ - -๑"}, + {start: -1, end: -1, result: "~-๑"}, + {start: -1, end: 0, result: "-๑ - ๐"}, + {start: 0, end: 0, result: "~๐"}, + {start: 0, end: 1, result: "๐-๑"}, + {start: 1, end: 1, result: "~๑"}, + {start: 1, end: 2, result: "๑-๒"}, + ], + }, + // Approximation sign may consist of multiple characters. + "no": { + options: {}, + ranges: [ + {start: 1, end: 1, result: "ca.1"}, + ], + }, + // Approximation sign can be a word. + "ja": { + options: {}, + ranges: [ + {start: 1, end: 1, result: "約1"}, + ], + }, +}; + +for (let [locale, {options, ranges}] of Object.entries(tests)) { + let nf = new Intl.NumberFormat(locale, options); + for (let {start, end, result} of ranges) { + assertEq(nf.formatRange(start, end), result, `${start}-${end}`); + assertEq(nf.formatRangeToParts(start, end).reduce((acc, part) => acc + part.value, ""), + result, `${start}-${end}`); + } +} + +// Throws an error if either value is NaN. +{ + const errorTests = [ + {start: NaN, end: NaN}, + {start: 0, end: NaN}, + {start: NaN, end: 0}, + {start: Infinity, end: NaN}, + {start: NaN, end: Infinity}, + ]; + + let nf = new Intl.NumberFormat("en"); + for (let {start, end} of errorTests) { + assertThrowsInstanceOf(() => nf.formatRange(start, end), RangeError); + assertThrowsInstanceOf(() => nf.formatRangeToParts(start, end), RangeError); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-compact.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-compact.js new file mode 100644 index 0000000000..b3ee5aedf3 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-compact.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {notation: "compact"}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-currency.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-currency.js new file mode 100644 index 0000000000..72722b3b18 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-currency.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {style: "currency", currency: "EUR"}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-percent.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-percent.js new file mode 100644 index 0000000000..f93ae25de8 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-percent.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {style: "percent"}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-signDisplay.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-signDisplay.js new file mode 100644 index 0000000000..197f6f9f0d --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-signDisplay.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {signDisplay: "always"}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-unit.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-unit.js new file mode 100644 index 0000000000..51507e57df --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign-unit.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {style: "unit", unit: "meter"}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + // Known failure case. + // - https://github.com/tc39/proposal-intl-numberformat-v3/issues/64 + // - https://unicode-org.atlassian.net/browse/CLDR-14918 + if (approx.length === 0 && new Intl.Locale(locale).language === "ar") { + continue; + } + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign.js new file mode 100644 index 0000000000..edb63c2b00 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts-approximately-sign.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +const numbers = [ + 0, 1, 2, 5, 10, 100, 1000, 10_000, 100_000, 1_000_000, + 0.1, 0.2, 0.5, 1.5, + -0, -1, -2, -5, + Infinity, -Infinity, +]; + +const options = {}; + +// List of known approximately sign in CLDR 40. +const approximatelySigns = [ + "~", "∼", "≈", "≃", "ca.", "約", "dáàṣì", "dáàshì", +]; + +// Iterate over all locales and ensure we find exactly one approximately sign. +for (let locale of getAvailableLocalesOf("NumberFormat").sort()) { + let nf = new Intl.NumberFormat(locale, options); + for (let number of numbers) { + let parts = nf.formatRangeToParts(number, number); + let approx = parts.filter(part => part.type === "approximatelySign"); + + assertEq(approx.length, 1); + assertEq(approximatelySigns.some(approxSign => approx[0].value.includes(approxSign)), true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts.js b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts.js new file mode 100644 index 0000000000..264dab910c --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatRangeToParts.js @@ -0,0 +1,174 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +const Start = NumberRangeFormatParts("startRange"); +const End = NumberRangeFormatParts("endRange"); +const Shared = NumberRangeFormatParts("shared"); + +const tests = { + "en": { + options: {}, + ranges: [ + // Approximation sign present. + { + start: 0, + end: 0, + result: [Shared.Approx("~"), Shared.Integer("0")], + }, + { + start: -0, + end: -0, + result: [Shared.Approx("~"), Shared.MinusSign("-"), Shared.Integer("0")], + }, + { + start: -1, + end: -1, + result: [Shared.Approx("~"), Shared.MinusSign("-"), Shared.Integer("1")], + }, + { + start: 0.5, + end: 0.5, + result: [Shared.Approx("~"), Shared.Integer("0"), Shared.Decimal("."), Shared.Fraction("5")], + }, + { + start: Infinity, + end: Infinity, + result: [Shared.Approx("~"), Shared.Inf("∞")], + }, + { + start: -Infinity, + end: -Infinity, + result: [Shared.Approx("~"), Shared.MinusSign("-"), Shared.Inf("∞")], + }, + + // Proper ranges. + { + start: -2, + end: -1, + result: [Start.MinusSign("-"), Start.Integer("2"), Shared.Literal(" – "), End.MinusSign("-"), End.Integer("1")], + }, + { + start: -1, + end: 1, + result: [Start.MinusSign("-"), Start.Integer("1"), Shared.Literal(" – "), End.Integer("1")], + }, + { + start: 1, + end: 2, + result: [Start.Integer("1"), Shared.Literal("–"), End.Integer("2")], + }, + ], + }, + // Non-ASCII digits. + "ar": { + options: {}, + ranges: [ + { + start: -2, + end: -1, + result: [Shared.Literal("\u061C"), Shared.MinusSign("-"), Start.Integer("٢"), Shared.Literal("–"), End.Integer("١")], + }, + { + start: -1, + end: -1, + result: [Shared.Approx("~"), Shared.Literal("\u061C"), Shared.MinusSign("-"), Shared.Integer("١")], + }, + { + start: -1, + end: 0, + result: [Start.Literal("\u061C"), Start.MinusSign("-"), Start.Integer("١"), Shared.Literal(" – "), End.Integer("٠")], + }, + { + start: 0, + end: 0, + result: [Shared.Approx("~"), Shared.Integer("٠")], + }, + { + start: 0, + end: 1, + result: [Start.Integer("٠"), Shared.Literal("–"), End.Integer("١")], + }, + { + start: 1, + end: 1, + result: [Shared.Approx("~"), Shared.Integer("١")], + }, + { + start: 1, + end: 2, + result: [Start.Integer("١"), Shared.Literal("–"), End.Integer("٢")], + }, + ], + }, + "th-u-nu-thai": { + options: {}, + ranges: [ + { + start: -2, + end: -1, + result: [Start.MinusSign("-"), Start.Integer("๒"), Shared.Literal(" - "), End.MinusSign("-"), End.Integer("๑")], + }, + { + start: -1, + end: -1, + result: [Shared.Approx("~"), Shared.MinusSign("-"), Shared.Integer("๑")], + }, + { + start: -1, + end: 0, + result: [Start.MinusSign("-"), Start.Integer("๑"), Shared.Literal(" - "), End.Integer("๐")], + }, + { + start: 0, + end: 0, + result: [Shared.Approx("~"), Shared.Integer("๐")], + }, + { + start: 0, + end: 1, + result: [Start.Integer("๐"), Shared.Literal("-"), End.Integer("๑")], + }, + { + start: 1, + end: 1, + result: [Shared.Approx("~"), Shared.Integer("๑")], + }, + { + start: 1, + end: 2, + result: [Start.Integer("๑"), Shared.Literal("-"), End.Integer("๒")], + }, + ], + }, + // Approximation sign may consist of multiple characters. + "no": { + options: {}, + ranges: [ + { + start: 1, + end: 1, + result: [Shared.Approx("ca."), Shared.Integer("1")], + }, + ], + }, + // Approximation sign can be a word. + "ja": { + options: {}, + ranges: [ + { + start: 1, + end: 1, + result: [Shared.Approx("約"), Shared.Integer("1")], + }, + ], + }, +}; + +for (let [locale, {options, ranges}] of Object.entries(tests)) { + let nf = new Intl.NumberFormat(locale, options); + for (let {start, end, result} of ranges) { + assertRangeParts(nf, start, end, result); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatToParts.js b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js new file mode 100644 index 0000000000..3ac7c3f4d3 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js @@ -0,0 +1,357 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1289882; +var summary = "Implement Intl.NumberFormat.prototype.formatToParts"; + +print(BUGNUMBER + ": " + summary); + +//----------------------------------------------------------------------------- + +assertEq("formatToParts" in Intl.NumberFormat(), true); + +// NOTE: Some of these tests exercise standard behavior (e.g. that format and +// formatToParts expose the same formatted string). But much of this, +// like the exact-formatted-string expectations, is technically +// implementation-dependent. This is necessary as a practical matter to +// properly test the conversion from ICU's nested-field exposure to +// ECMA-402's sequential-parts exposure. + +var { + Nan, Inf, Integer, Group, Decimal, Fraction, + MinusSign, PlusSign, PercentSign, Currency, Literal, +} = NumberFormatParts; + +//----------------------------------------------------------------------------- + +// Test -0's partitioning now that it's not treated like +0. +// https://github.com/tc39/ecma402/pull/232 + +var deadSimpleFormatter = new Intl.NumberFormat("en-US"); + +assertParts(deadSimpleFormatter, -0, + [MinusSign("-"), Integer("0")]); + +// Test behavior of a currency with code formatting. +var usdCodeOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "code", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var usDollarsCode = new Intl.NumberFormat("en-US", usdCodeOptions); + +assertParts(usDollarsCode, 25, + [Currency("USD"), Literal("\xA0"), Integer("25")]); + +// ISO 4217 currency codes are formed from an ISO 3166-1 alpha-2 country code +// followed by a third letter. ISO 3166 guarantees that no country code +// starting with "X" will ever be assigned. Stepping carefully around a few +// 4217-designated special "currencies", XQQ will never have a representation. +// Thus, yes: this really is specified to work, as unrecognized or unsupported +// codes pass into the string unmodified. +var xqqCodeOptions = + { + style: "currency", + currency: "XQQ", + currencyDisplay: "code", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var xqqMoneyCode = new Intl.NumberFormat("en-US", xqqCodeOptions); + +assertParts(xqqMoneyCode, 25, + [Currency("XQQ"), Literal("\xA0"), Integer("25")]); + +// Test currencyDisplay: "name". +var usdNameOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "name", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var usDollarsName = new Intl.NumberFormat("en-US", usdNameOptions); + +assertParts(usDollarsName, 25, + [Integer("25"), Literal(" "), Currency("US dollars")]); + +var usdNameGroupingOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "name", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var usDollarsNameGrouping = + new Intl.NumberFormat("en-US", usdNameGroupingOptions); + +assertParts(usDollarsNameGrouping, 12345678, + [Integer("12"), + Group(","), + Integer("345"), + Group(","), + Integer("678"), + Literal(" "), + Currency("US dollars")]); + +// But if the implementation doesn't recognize the currency, the provided code +// is used in place of a proper name, unmolested. +var xqqNameOptions = + { + style: "currency", + currency: "XQQ", + currencyDisplay: "name", + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; +var xqqMoneyName = new Intl.NumberFormat("en-US", xqqNameOptions); + +assertParts(xqqMoneyName, 25, + [Integer("25"), Literal(" "), Currency("XQQ")]); + +// Test some currencies with fractional components. + +var usdNameFractionOptions = + { + style: "currency", + currency: "USD", + currencyDisplay: "name", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }; +var usdNameFractionFormatter = + new Intl.NumberFormat("en-US", usdNameFractionOptions); + +// The US national surplus (i.e. debt) as of October 18, 2016. (Replicating +// data from a comment in builtin/Intl/NumberFormat.cpp.) +var usNationalSurplus = -19766580028249.41; + +assertParts(usdNameFractionFormatter, usNationalSurplus, + [MinusSign("-"), + Integer("19"), + Group(","), + Integer("766"), + Group(","), + Integer("580"), + Group(","), + Integer("028"), + Group(","), + Integer("249"), + Decimal("."), + Fraction("41"), + Literal(" "), + Currency("US dollars")]); + +// Percents in various forms. + +var usPercentOptions = + { + style: "percent", + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }; +var usPercentFormatter = + new Intl.NumberFormat("en-US", usPercentOptions); + +assertParts(usPercentFormatter, 0.375, + [Integer("37"), Decimal("."), Fraction("5"), PercentSign("%")]); + +assertParts(usPercentFormatter, -1284.375, + [MinusSign("-"), + Integer("128"), + Group(","), + Integer("437"), + Decimal("."), + Fraction("5"), + PercentSign("%")]); + +assertParts(usPercentFormatter, NaN, + [Nan("NaN"), PercentSign("%")]); + +assertParts(usPercentFormatter, Infinity, + [Inf("∞"), PercentSign("%")]); + +assertParts(usPercentFormatter, -Infinity, + [MinusSign("-"), Inf("∞"), PercentSign("%")]); + +var dePercentOptions = + { + style: "percent", + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }; +var dePercentFormatter = + new Intl.NumberFormat("de", dePercentOptions); + +assertParts(dePercentFormatter, 0.375, + [Integer("37"), Decimal(","), Fraction("5"), Literal("\xA0"), PercentSign("%")]); + +assertParts(dePercentFormatter, -1284.375, + [MinusSign("-"), + Integer("128"), + Group("."), + Integer("437"), + Decimal(","), + Fraction("5"), + Literal("\xA0"), + PercentSign("%")]); + +assertParts(dePercentFormatter, NaN, + [Nan("NaN"), Literal("\xA0"), PercentSign("%")]); + +assertParts(dePercentFormatter, Infinity, + [Inf("∞"), Literal("\xA0"), PercentSign("%")]); + +assertParts(dePercentFormatter, -Infinity, + [MinusSign("-"), Inf("∞"), Literal("\xA0"), PercentSign("%")]); + +var arPercentOptions = + { + style: "percent", + minimumFractionDigits: 2, + }; +var arPercentFormatter = + new Intl.NumberFormat("ar-IQ", arPercentOptions); + +assertParts(arPercentFormatter, -135.32, + [Literal("\u{061C}"), + MinusSign("-"), + Integer("١٣"), + Group("٬"), + Integer("٥٣٢"), + Decimal("٫"), + Fraction("٠٠"), + PercentSign("٪"), + Literal("\u{061C}")]); + +// Decimals. + +var usDecimalOptions = + { + style: "decimal", + maximumFractionDigits: 7 // minimum defaults to 0 + }; +var usDecimalFormatter = + new Intl.NumberFormat("en-US", usDecimalOptions); + +assertParts(usDecimalFormatter, 42, + [Integer("42")]); + +assertParts(usDecimalFormatter, 1337, + [Integer("1"), Group(","), Integer("337")]); + +assertParts(usDecimalFormatter, -6.25, + [MinusSign("-"), Integer("6"), Decimal("."), Fraction("25")]); + +assertParts(usDecimalFormatter, -1376.25, + [MinusSign("-"), + Integer("1"), + Group(","), + Integer("376"), + Decimal("."), + Fraction("25")]); + +assertParts(usDecimalFormatter, 124816.8359375, + [Integer("124"), + Group(","), + Integer("816"), + Decimal("."), + Fraction("8359375")]); + +var usNoGroupingDecimalOptions = + { + style: "decimal", + useGrouping: false, + maximumFractionDigits: 7 // minimum defaults to 0 + }; +var usNoGroupingDecimalFormatter = + new Intl.NumberFormat("en-US", usNoGroupingDecimalOptions); + +assertParts(usNoGroupingDecimalFormatter, 1337, + [Integer("1337")]); + +assertParts(usNoGroupingDecimalFormatter, -6.25, + [MinusSign("-"), Integer("6"), Decimal("."), Fraction("25")]); + +assertParts(usNoGroupingDecimalFormatter, -1376.25, + [MinusSign("-"), + Integer("1376"), + Decimal("."), + Fraction("25")]); + +assertParts(usNoGroupingDecimalFormatter, 124816.8359375, + [Integer("124816"), + Decimal("."), + Fraction("8359375")]); + +var deDecimalOptions = + { + style: "decimal", + maximumFractionDigits: 7 // minimum defaults to 0 + }; +var deDecimalFormatter = + new Intl.NumberFormat("de-DE", deDecimalOptions); + +assertParts(deDecimalFormatter, 42, + [Integer("42")]); + +assertParts(deDecimalFormatter, 1337, + [Integer("1"), Group("."), Integer("337")]); + +assertParts(deDecimalFormatter, -6.25, + [MinusSign("-"), Integer("6"), Decimal(","), Fraction("25")]); + +assertParts(deDecimalFormatter, -1376.25, + [MinusSign("-"), + Integer("1"), + Group("."), + Integer("376"), + Decimal(","), + Fraction("25")]); + +assertParts(deDecimalFormatter, 124816.8359375, + [Integer("124"), + Group("."), + Integer("816"), + Decimal(","), + Fraction("8359375")]); + +var deNoGroupingDecimalOptions = + { + style: "decimal", + useGrouping: false, + maximumFractionDigits: 7 // minimum defaults to 0 + }; +var deNoGroupingDecimalFormatter = + new Intl.NumberFormat("de-DE", deNoGroupingDecimalOptions); + +assertParts(deNoGroupingDecimalFormatter, 1337, + [Integer("1337")]); + +assertParts(deNoGroupingDecimalFormatter, -6.25, + [MinusSign("-"), Integer("6"), Decimal(","), Fraction("25")]); + +assertParts(deNoGroupingDecimalFormatter, -1376.25, + [MinusSign("-"), + Integer("1376"), + Decimal(","), + Fraction("25")]); + +assertParts(deNoGroupingDecimalFormatter, 124816.8359375, + [Integer("124816"), + Decimal(","), + Fraction("8359375")]); + +//----------------------------------------------------------------------------- + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); + +print("Tests complete"); diff --git a/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js new file mode 100644 index 0000000000..430d74222e --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1484943; +var summary = "Don't crash doing format/formatToParts on -NaN"; + +print(BUGNUMBER + ": " + summary); + +//----------------------------------------------------------------------------- + +assertEq("formatToParts" in Intl.NumberFormat(), true); + +var nf = new Intl.NumberFormat("en-US"); +var parts; + +var values = [NaN, -NaN]; + +for (var v of values) +{ + assertEq(nf.format(v), "NaN"); + + parts = nf.formatToParts(v); + assertEq(parts.length, 1); + assertEq(parts[0].type, "nan"); + assertEq(parts[0].value, "NaN"); +} + +//----------------------------------------------------------------------------- + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); + +print("Tests complete"); diff --git a/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js b/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js new file mode 100644 index 0000000000..0db461f50e --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const optionsList = [ + {minimumFractionDigits: -0, maximumFractionDigits: -0}, + {minimumFractionDigits: -0, maximumFractionDigits: +0}, + {minimumFractionDigits: +0, maximumFractionDigits: -0}, + {minimumFractionDigits: +0, maximumFractionDigits: +0}, +]; + +for (let options of optionsList) { + let numberFormat = new Intl.NumberFormat("en-US", options); + + let {minimumFractionDigits, maximumFractionDigits} = numberFormat.resolvedOptions(); + assertEq(minimumFractionDigits, +0); + assertEq(maximumFractionDigits, +0); + + assertEq(numberFormat.format(123), "123"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js b/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js new file mode 100644 index 0000000000..80c3416afb --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js @@ -0,0 +1,139 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal, + Compact, +} = NumberFormatParts; + +const testcases = [ + { + locale: "en", + options: { + notation: "compact", + compactDisplay: "long", + }, + values: [ + {value: +0, string: "0", parts: [Integer("0")]}, + {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + {value: 0n, string: "0", parts: [Integer("0")]}, + + {value: 1, string: "1", parts: [Integer("1")]}, + {value: 10, string: "10", parts: [Integer("10")]}, + {value: 100, string: "100", parts: [Integer("100")]}, + {value: 1000, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]}, + {value: 10000, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]}, + {value: 100000, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]}, + {value: 1000000, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]}, + {value: 10000000, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]}, + {value: 100000000, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]}, + {value: 1000000000, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]}, + {value: 10000000000, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]}, + {value: 100000000000, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]}, + {value: 1000000000000, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]}, + {value: 10000000000000, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]}, + {value: 100000000000000, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]}, + {value: 1000000000000000, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]}, + {value: 10000000000000000, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]}, + {value: 100000000000000000, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]}, + + {value: 1n, string: "1", parts: [Integer("1")]}, + {value: 10n, string: "10", parts: [Integer("10")]}, + {value: 100n, string: "100", parts: [Integer("100")]}, + {value: 1000n, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]}, + {value: 10000n, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]}, + {value: 100000n, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]}, + {value: 1000000n, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]}, + {value: 10000000n, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]}, + {value: 100000000n, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]}, + {value: 1000000000n, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]}, + {value: 10000000000n, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]}, + {value: 100000000000n, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]}, + {value: 1000000000000n, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]}, + {value: 10000000000000n, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]}, + {value: 100000000000000n, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]}, + {value: 1000000000000000n, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]}, + {value: 10000000000000000n, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]}, + {value: 100000000000000000n, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]}, + + {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]}, + {value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]}, + {value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]}, + {value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]}, + {value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]}, + {value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]}, + {value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]}, + + {value: 12, string: "12", parts: [Integer("12")]}, + {value: 123, string: "123", parts: [Integer("123")]}, + {value: 1234, string: "1.2 thousand", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("thousand")]}, + {value: 12345, string: "12 thousand", parts: [Integer("12"), Literal(" "), Compact("thousand")]}, + {value: 123456, string: "123 thousand", parts: [Integer("123"), Literal(" "), Compact("thousand")]}, + {value: 1234567, string: "1.2 million", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("million")]}, + {value: 12345678, string: "12 million", parts: [Integer("12"), Literal(" "), Compact("million")]}, + {value: 123456789, string: "123 million", parts: [Integer("123"), Literal(" "), Compact("million")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // The "{compactName}" placeholder may have different plural forms. + { + locale: "de", + options: { + notation: "compact", + compactDisplay: "long", + }, + values: [ + {value: 1e6, string: "1 Million", parts: [Integer("1"), Literal(" "), Compact("Million")]}, + {value: 2e6, string: "2 Millionen", parts: [Integer("2"), Literal(" "), Compact("Millionen")]}, + {value: 1e9, string: "1 Milliarde", parts: [Integer("1"), Literal(" "), Compact("Milliarde")]}, + {value: 2e9, string: "2 Milliarden", parts: [Integer("2"), Literal(" "), Compact("Milliarden")]}, + ], + }, + + // Digits are grouped in myriads (every 10,000) in Japanese. + { + locale: "ja", + options: { + notation: "compact", + compactDisplay: "long", + }, + values: [ + {value: 1, string: "1", parts: [Integer("1")]}, + {value: 10, string: "10", parts: [Integer("10")]}, + {value: 100, string: "100", parts: [Integer("100")]}, + {value: 1000, string: "1000", parts: [Integer("1000")]}, + + {value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]}, + {value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]}, + {value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]}, + {value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]}, + + {value: 10e7, string: "1\u5104", parts: [Integer("1"), Compact("\u5104")]}, + {value: 100e7, string: "10\u5104", parts: [Integer("10"), Compact("\u5104")]}, + {value: 1000e7, string: "100\u5104", parts: [Integer("100"), Compact("\u5104")]}, + {value: 10000e7, string: "1000\u5104", parts: [Integer("1000"), Compact("\u5104")]}, + + {value: 10e11, string: "1\u5146", parts: [Integer("1"), Compact("\u5146")]}, + {value: 100e11, string: "10\u5146", parts: [Integer("10"), Compact("\u5146")]}, + {value: 1000e11, string: "100\u5146", parts: [Integer("100"), Compact("\u5146")]}, + {value: 10000e11, string: "1000\u5146", parts: [Integer("1000"), Compact("\u5146")]}, + + {value: 10e15, string: "1\u4EAC", parts: [Integer("1"), Compact("\u4EAC")]}, + {value: 100e15, string: "10\u4EAC", parts: [Integer("10"), Compact("\u4EAC")]}, + {value: 1000e15, string: "100\u4EAC", parts: [Integer("100"), Compact("\u4EAC")]}, + {value: 10000e15, string: "1000\u4EAC", parts: [Integer("1000"), Compact("\u4EAC")]}, + + {value: 100000e15, string: "10,000\u4EAC", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u4EAC")]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js b/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js new file mode 100644 index 0000000000..f3a546294d --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js @@ -0,0 +1,136 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal, + Compact, +} = NumberFormatParts; + +const testcases = [ + { + locale: "en", + options: { + notation: "compact", + compactDisplay: "short", + }, + values: [ + {value: +0, string: "0", parts: [Integer("0")]}, + {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + {value: 0n, string: "0", parts: [Integer("0")]}, + + {value: 1, string: "1", parts: [Integer("1")]}, + {value: 10, string: "10", parts: [Integer("10")]}, + {value: 100, string: "100", parts: [Integer("100")]}, + {value: 1000, string: "1K", parts: [Integer("1"), Compact("K")]}, + {value: 10000, string: "10K", parts: [Integer("10"), Compact("K")]}, + {value: 100000, string: "100K", parts: [Integer("100"), Compact("K")]}, + {value: 1000000, string: "1M", parts: [Integer("1"), Compact("M")]}, + {value: 10000000, string: "10M", parts: [Integer("10"), Compact("M")]}, + {value: 100000000, string: "100M", parts: [Integer("100"), Compact("M")]}, + {value: 1000000000, string: "1B", parts: [Integer("1"), Compact("B")]}, + {value: 10000000000, string: "10B", parts: [Integer("10"), Compact("B")]}, + {value: 100000000000, string: "100B", parts: [Integer("100"), Compact("B")]}, + {value: 1000000000000, string: "1T", parts: [Integer("1"), Compact("T")]}, + {value: 10000000000000, string: "10T", parts: [Integer("10"), Compact("T")]}, + {value: 100000000000000, string: "100T", parts: [Integer("100"), Compact("T")]}, + {value: 1000000000000000, string: "1000T", parts: [Integer("1000"), Compact("T")]}, + {value: 10000000000000000, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]}, + {value: 100000000000000000, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]}, + + {value: 1n, string: "1", parts: [Integer("1")]}, + {value: 10n, string: "10", parts: [Integer("10")]}, + {value: 100n, string: "100", parts: [Integer("100")]}, + {value: 1000n, string: "1K", parts: [Integer("1"), Compact("K")]}, + {value: 10000n, string: "10K", parts: [Integer("10"), Compact("K")]}, + {value: 100000n, string: "100K", parts: [Integer("100"), Compact("K")]}, + {value: 1000000n, string: "1M", parts: [Integer("1"), Compact("M")]}, + {value: 10000000n, string: "10M", parts: [Integer("10"), Compact("M")]}, + {value: 100000000n, string: "100M", parts: [Integer("100"), Compact("M")]}, + {value: 1000000000n, string: "1B", parts: [Integer("1"), Compact("B")]}, + {value: 10000000000n, string: "10B", parts: [Integer("10"), Compact("B")]}, + {value: 100000000000n, string: "100B", parts: [Integer("100"), Compact("B")]}, + {value: 1000000000000n, string: "1T", parts: [Integer("1"), Compact("T")]}, + {value: 10000000000000n, string: "10T", parts: [Integer("10"), Compact("T")]}, + {value: 100000000000000n, string: "100T", parts: [Integer("100"), Compact("T")]}, + {value: 1000000000000000n, string: "1000T", parts: [Integer("1000"), Compact("T")]}, + {value: 10000000000000000n, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]}, + {value: 100000000000000000n, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]}, + + {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]}, + {value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]}, + {value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]}, + {value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]}, + {value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]}, + {value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]}, + {value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]}, + + {value: 12, string: "12", parts: [Integer("12")]}, + {value: 123, string: "123", parts: [Integer("123")]}, + {value: 1234, string: "1.2K", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("K")]}, + {value: 12345, string: "12K", parts: [Integer("12"), Compact("K")]}, + {value: 123456, string: "123K", parts: [Integer("123"), Compact("K")]}, + {value: 1234567, string: "1.2M", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("M")]}, + {value: 12345678, string: "12M", parts: [Integer("12"), Compact("M")]}, + {value: 123456789, string: "123M", parts: [Integer("123"), Compact("M")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // Compact symbol can consist of multiple characters, e.g. when it's an abbreviation. + { + locale: "de", + options: { + notation: "compact", + compactDisplay: "short", + }, + values: [ + {value: 1e6, string: "1\u00A0Mio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mio.")]}, + {value: 1e9, string: "1\u00A0Mrd.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mrd.")]}, + {value: 1e12, string: "1\u00A0Bio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Bio.")]}, + + // CLDR doesn't support "Billiarde" (Brd.), Trillion (Trill.), etc. + {value: 1e15, string: "1000\u00A0Bio.", parts: [Integer("1000"), Literal("\u00A0"), Compact("Bio.")]}, + ], + }, + + // Digits are grouped in myriads (every 10,000) in Chinese. + { + locale: "zh", + options: { + notation: "compact", + compactDisplay: "short", + }, + values: [ + {value: 1, string: "1", parts: [Integer("1")]}, + {value: 10, string: "10", parts: [Integer("10")]}, + {value: 100, string: "100", parts: [Integer("100")]}, + {value: 1000, string: "1000", parts: [Integer("1000")]}, + + {value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]}, + {value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]}, + {value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]}, + {value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]}, + + {value: 10e7, string: "1\u4EBF", parts: [Integer("1"), Compact("\u4EBF")]}, + {value: 100e7, string: "10\u4EBF", parts: [Integer("10"), Compact("\u4EBF")]}, + {value: 1000e7, string: "100\u4EBF", parts: [Integer("100"), Compact("\u4EBF")]}, + {value: 10000e7, string: "1000\u4EBF", parts: [Integer("1000"), Compact("\u4EBF")]}, + + {value: 10e11, string: "1\u4E07\u4EBF", parts: [Integer("1"), Compact("\u4E07\u4EBF")]}, + {value: 100e11, string: "10\u4E07\u4EBF", parts: [Integer("10"), Compact("\u4E07\u4EBF")]}, + {value: 1000e11, string: "100\u4E07\u4EBF", parts: [Integer("100"), Compact("\u4E07\u4EBF")]}, + {value: 10000e11, string: "1000\u4E07\u4EBF", parts: [Integer("1000"), Compact("\u4E07\u4EBF")]}, + + {value: 100000e11, string: "10,000\u4E07\u4EBF", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u4E07\u4EBF")]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-compact-with-fraction-digits.js b/js/src/tests/non262/Intl/NumberFormat/notation-compact-with-fraction-digits.js new file mode 100644 index 0000000000..4bdf26b9ad --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/notation-compact-with-fraction-digits.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +let nf = new Intl.NumberFormat("en", { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + notation: "compact", +}); + +assertEq(nf.format(1500), "1.5K"); +assertEq(nf.format(1520), "1.52K"); +assertEq(nf.format(1540), "1.54K"); +assertEq(nf.format(1550), "1.55K"); +assertEq(nf.format(1560), "1.56K"); +assertEq(nf.format(1580), "1.58K"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js b/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js new file mode 100644 index 0000000000..b4c0e19e53 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js @@ -0,0 +1,136 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, + ExponentSeparator, ExponentInteger, ExponentMinusSign, +} = NumberFormatParts; + +const testcases = [ + { + locale: "en", + options: { + notation: "engineering", + }, + values: [ + {value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + + {value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 10, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 100, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 10000, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 100000, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]}, + + {value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 10n, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 100n, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 10000n, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 100000n, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]}, + + {value: 0.1, string: "100E-3", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]}, + {value: 0.01, string: "10E-3", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]}, + {value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]}, + {value: 0.0001, string: "100E-6", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]}, + {value: 0.00001, string: "10E-6", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]}, + {value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]}, + {value: 0.0000001, string: "100E-9", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("9")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // Exponent modifications take place early, so while in the "standard" notation + // `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for + // "engineering" notation the result string is not "0", but instead "100E-3". + + { + locale: "en", + options: { + notation: "engineering", + maximumFractionDigits: 0, + }, + values: [ + {value: 0.1, string: "100E-3", parts: [ + Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "engineering", + minimumFractionDigits: 4, + }, + values: [ + {value: 10, string: "10.0000E0", parts: [ + Integer("10"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("0") + ]}, + {value: 0.1, string: "100.0000E-3", parts: [ + Integer("100"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "engineering", + minimumIntegerDigits: 4, + }, + values: [ + {value: 10, string: "0,010E0", parts: [ + Integer("0"), Group(","), Integer("010"), ExponentSeparator("E"), ExponentInteger("0") + ]}, + {value: 0.1, string: "0,100E-3", parts: [ + Integer("0"), Group(","), Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "engineering", + minimumSignificantDigits: 4, + }, + values: [ + {value: 10, string: "10.00E0", parts: [ + Integer("10"), Decimal("."), Fraction("00"), ExponentSeparator("E"), ExponentInteger("0") + ]}, + {value: 0.1, string: "100.0E-3", parts: [ + Integer("100"), Decimal("."), Fraction("0"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "engineering", + maximumSignificantDigits: 1, + }, + values: [ + {value: 12, string: "10E0", parts: [ + Integer("10"), ExponentSeparator("E"), ExponentInteger("0") + ]}, + {value: 0.12, string: "100E-3", parts: [ + Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3") + ]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js b/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js new file mode 100644 index 0000000000..f14a660129 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js @@ -0,0 +1,136 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, + ExponentSeparator, ExponentInteger, ExponentMinusSign, +} = NumberFormatParts; + +const testcases = [ + { + locale: "en", + options: { + notation: "scientific", + }, + values: [ + {value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]}, + + {value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 10, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]}, + {value: 100, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]}, + {value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 10000, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]}, + {value: 100000, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]}, + {value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]}, + + {value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]}, + {value: 10n, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]}, + {value: 100n, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]}, + {value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]}, + {value: 10000n, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]}, + {value: 100000n, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]}, + {value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]}, + + {value: 0.1, string: "1E-1", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")]}, + {value: 0.01, string: "1E-2", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("2")]}, + {value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]}, + {value: 0.0001, string: "1E-4", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("4")]}, + {value: 0.00001, string: "1E-5", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("5")]}, + {value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]}, + {value: 0.0000001, string: "1E-7", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("7")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // Exponent modifications take place early, so while in the "standard" notation + // `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for + // "scientific" notation the result string is not "0", but instead "1E-1". + + { + locale: "en", + options: { + notation: "scientific", + maximumFractionDigits: 0, + }, + values: [ + {value: 0.1, string: "1E-1", parts: [ + Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "scientific", + minimumFractionDigits: 4, + }, + values: [ + {value: 10, string: "1.0000E1", parts: [ + Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("1") + ]}, + {value: 0.1, string: "1.0000E-1", parts: [ + Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "scientific", + minimumIntegerDigits: 4, + }, + values: [ + {value: 10, string: "0,001E1", parts: [ + Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentInteger("1") + ]}, + {value: 0.1, string: "0,001E-1", parts: [ + Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "scientific", + minimumSignificantDigits: 4, + }, + values: [ + {value: 10, string: "1.000E1", parts: [ + Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentInteger("1") + ]}, + {value: 0.1, string: "1.000E-1", parts: [ + Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1") + ]}, + ], + }, + + { + locale: "en", + options: { + notation: "scientific", + maximumSignificantDigits: 1, + }, + values: [ + {value: 12, string: "1E1", parts: [ + Integer("1"), ExponentSeparator("E"), ExponentInteger("1") + ]}, + {value: 0.12, string: "1E-1", parts: [ + Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1") + ]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js new file mode 100644 index 0000000000..c0b9ba78ed --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js @@ -0,0 +1,18 @@ +for (let [numberingSystem, {digits, algorithmic}] of Object.entries(numberingSystems)) { + if (algorithmic) { + // We don't yet support algorithmic numbering systems. + continue; + } + + let nf = new Intl.NumberFormat("en", {numberingSystem}); + + assertEq([...digits].length, 10, "expect exactly ten digits for each numbering system"); + + let i = 0; + for (let digit of digits) { + assertEq(nf.format(i++), digit); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js new file mode 100644 index 0000000000..4624ee5192 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js @@ -0,0 +1,60 @@ +const defaultLocale = "en"; +const defaultNumberingSystem = new Intl.NumberFormat(defaultLocale).resolvedOptions().numberingSystem; + +function createWithLocale(locale, numberingSystem) { + return new Intl.NumberFormat(locale, {numberingSystem}); +} + +function create(numberingSystem) { + return createWithLocale(defaultLocale, numberingSystem); +} + +// Empty string should throw. +assertThrowsInstanceOf(() => create(""), RangeError); + +// Trailing \0 should throw. +assertThrowsInstanceOf(() => create("latn\0"), RangeError); + +// Too short or too long strings should throw. +assertThrowsInstanceOf(() => create("a"), RangeError); +assertThrowsInstanceOf(() => create("toolongstring"), RangeError); + +// Throw even when prefix is valid. +assertThrowsInstanceOf(() => create("latn-toolongstring"), RangeError); + +// |numberingSystem| can be set to |undefined|. +let nf = create(undefined); +assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Unsupported numbering systems are ignored. +nf = create("xxxxxxxx"); +assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Numbering system in options overwrite Unicode extension keyword. +nf = createWithLocale(`${defaultLocale}-u-nu-thai`, "arab"); +assertEq(nf.resolvedOptions().locale, defaultLocale); +assertEq(nf.resolvedOptions().numberingSystem, "arab"); + +// |numberingSystem| option ignores case. +nf = create("ARAB"); +assertEq(nf.resolvedOptions().locale, defaultLocale); +assertEq(nf.resolvedOptions().numberingSystem, "arab"); + +for (let [numberingSystem, {algorithmic}] of Object.entries(numberingSystems)) { + let nf1 = new Intl.NumberFormat(`${defaultLocale}-u-nu-${numberingSystem}`); + let nf2 = new Intl.NumberFormat(defaultLocale, {numberingSystem}); + + if (!algorithmic) { + assertEq(nf1.resolvedOptions().numberingSystem, numberingSystem); + assertEq(nf2.resolvedOptions().numberingSystem, numberingSystem); + } else { + // We don't yet support algorithmic numbering systems. + assertEq(nf1.resolvedOptions().numberingSystem, defaultNumberingSystem); + assertEq(nf2.resolvedOptions().numberingSystem, defaultNumberingSystem); + } + + assertEq(nf2.format(0), nf1.format(0)); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js b/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js new file mode 100644 index 0000000000..ddb2d61350 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// createIsHTMLDDA is only available when running tests in the shell, +// not the browser +if (typeof createIsHTMLDDA === "function") { + let nf = new Intl.NumberFormat('en-US', createIsHTMLDDA()); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js b/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js new file mode 100644 index 0000000000..87cfc317cb --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Locale processing is supposed to internally remove any Unicode extension +// sequences in the locale. Test that various weird testcases invoking +// algorithmic edge cases don't assert or throw exceptions. + +var weirdCases = + [ + "en-x-u-foo", + "en-a-bar-x-u-foo", + "en-x-u-foo-a-bar", + "en-a-bar-u-baz-x-u-foo", + ]; + +for (var locale of weirdCases) + Intl.NumberFormat(locale).format(5); + +assertThrowsInstanceOf(() => Intl.NumberFormat("x-u-foo"), RangeError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/rounding-increment.js b/js/src/tests/non262/Intl/NumberFormat/rounding-increment.js new file mode 100644 index 0000000000..84d4480227 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/rounding-increment.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +// Nickel rounding. +{ + let nf = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + roundingIncrement: 5, + }); + + assertEq(nf.format(1.22), "1.20"); + assertEq(nf.format(1.224), "1.20"); + assertEq(nf.format(1.225), "1.25"); + assertEq(nf.format(1.23), "1.25"); +} + +// Dime rounding. +{ + let nf = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + roundingIncrement: 10, + }); + + assertEq(nf.format(1.24), "1.20"); + assertEq(nf.format(1.249), "1.20"); + assertEq(nf.format(1.250), "1.30"); + assertEq(nf.format(1.25), "1.30"); +} + +// Rounding increment option is rounded down. +{ + let nf1 = new Intl.NumberFormat("en", { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + roundingIncrement: 10, + }); + + let nf2 = new Intl.NumberFormat("en", { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + roundingIncrement: 10.1, + }); + + let nf3 = new Intl.NumberFormat("en", { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + roundingIncrement: 10.9, + }); + + assertEq(nf1.resolvedOptions().roundingIncrement, 10); + assertEq(nf2.resolvedOptions().roundingIncrement, 10); + assertEq(nf3.resolvedOptions().roundingIncrement, 10); + + assertEq(nf1.format(123), "120"); + assertEq(nf2.format(123), "120"); + assertEq(nf3.format(123), "120"); +} + +// |minimumFractionDigits| must be equal to |maximumFractionDigits| when +// |roundingIncrement| is used. +// +// |minimumFractionDigits| defaults to zero. +{ + let nf = new Intl.NumberFormat("en", { + roundingIncrement: 10, + // minimumFractionDigits: 0, (default) + maximumFractionDigits: 0, + }); + + let resolved = nf.resolvedOptions(); + assertEq(resolved.minimumFractionDigits, 0); + assertEq(resolved.maximumFractionDigits, 0); + assertEq(resolved.roundingIncrement, 10); + + assertEq(nf.format(123), "120"); + assertEq(nf.format(123.456), "120"); +} + +// |maximumFractionDigits| defaults to three. And because |0 !== 3|, a +// RangeError is thrown. +{ + let options = { + roundingIncrement: 10, + // minimumFractionDigits: 0, (default) + // maximumFractionDigits: 3, (default) + }; + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", options), RangeError); +} + +// Invalid values. +for (let roundingIncrement of [-1, 0, Infinity, NaN]){ + let options = { + roundingIncrement, + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", options), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/rounding-mode.js b/js/src/tests/non262/Intl/NumberFormat/rounding-mode.js new file mode 100644 index 0000000000..e4b85b0172 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/rounding-mode.js @@ -0,0 +1,287 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +const tests = [ + // Special values: Zeros and non-finite values. + { + value: 0, + options: {}, + roundingModes: { + ceil: "0", + floor: "0", + expand: "0", + trunc: "0", + halfCeil: "0", + halfFloor: "0", + halfExpand: "0", + halfTrunc: "0", + halfEven: "0", + }, + }, + { + value: -0, + options: {}, + roundingModes: { + ceil: "-0", + floor: "-0", + expand: "-0", + trunc: "-0", + halfCeil: "-0", + halfFloor: "-0", + halfExpand: "-0", + halfTrunc: "-0", + halfEven: "-0", + }, + }, + { + value: -Infinity, + options: {}, + roundingModes: { + ceil: "-∞", + floor: "-∞", + expand: "-∞", + trunc: "-∞", + halfCeil: "-∞", + halfFloor: "-∞", + halfExpand: "-∞", + halfTrunc: "-∞", + halfEven: "-∞", + }, + }, + { + value: Infinity, + options: {}, + roundingModes: { + ceil: "∞", + floor: "∞", + expand: "∞", + trunc: "∞", + halfCeil: "∞", + halfFloor: "∞", + halfExpand: "∞", + halfTrunc: "∞", + halfEven: "∞", + }, + }, + { + value: NaN, + options: {}, + roundingModes: { + ceil: "NaN", + floor: "NaN", + expand: "NaN", + trunc: "NaN", + halfCeil: "NaN", + halfFloor: "NaN", + halfExpand: "NaN", + halfTrunc: "NaN", + halfEven: "NaN", + }, + }, + + // Integer rounding with positive values. + { + value: 0.4, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "1", + floor: "0", + expand: "1", + trunc: "0", + halfCeil: "0", + halfFloor: "0", + halfExpand: "0", + halfTrunc: "0", + halfEven: "0", + }, + }, + { + value: 0.5, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "1", + floor: "0", + expand: "1", + trunc: "0", + halfCeil: "1", + halfFloor: "0", + halfExpand: "1", + halfTrunc: "0", + halfEven: "0", + }, + }, + { + value: 0.6, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "1", + floor: "0", + expand: "1", + trunc: "0", + halfCeil: "1", + halfFloor: "1", + halfExpand: "1", + halfTrunc: "1", + halfEven: "1", + }, + }, + + // Integer rounding with negative values. + { + value: -0.4, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "-0", + floor: "-1", + expand: "-1", + trunc: "-0", + halfCeil: "-0", + halfFloor: "-0", + halfExpand: "-0", + halfTrunc: "-0", + halfEven: "-0", + }, + }, + { + value: -0.5, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "-0", + floor: "-1", + expand: "-1", + trunc: "-0", + halfCeil: "-0", + halfFloor: "-1", + halfExpand: "-1", + halfTrunc: "-0", + halfEven: "-0", + }, + }, + { + value: -0.6, + options: {maximumFractionDigits: 0}, + roundingModes: { + ceil: "-0", + floor: "-1", + expand: "-1", + trunc: "-0", + halfCeil: "-1", + halfFloor: "-1", + halfExpand: "-1", + halfTrunc: "-1", + halfEven: "-1", + }, + }, + + // Fractional digits rounding with positive values. + { + value: 0.04, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "0.1", + floor: "0", + expand: "0.1", + trunc: "0", + halfCeil: "0", + halfFloor: "0", + halfExpand: "0", + halfTrunc: "0", + halfEven: "0", + }, + }, + { + value: 0.05, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "0.1", + floor: "0", + expand: "0.1", + trunc: "0", + halfCeil: "0.1", + halfFloor: "0", + halfExpand: "0.1", + halfTrunc: "0", + halfEven: "0", + }, + }, + { + value: 0.06, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "0.1", + floor: "0", + expand: "0.1", + trunc: "0", + halfCeil: "0.1", + halfFloor: "0.1", + halfExpand: "0.1", + halfTrunc: "0.1", + halfEven: "0.1", + }, + }, + + // Fractional digits rounding with negative values. + { + value: -0.04, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "-0", + floor: "-0.1", + expand: "-0.1", + trunc: "-0", + halfCeil: "-0", + halfFloor: "-0", + halfExpand: "-0", + halfTrunc: "-0", + halfEven: "-0", + }, + }, + { + value: -0.05, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "-0", + floor: "-0.1", + expand: "-0.1", + trunc: "-0", + halfCeil: "-0", + halfFloor: "-0.1", + halfExpand: "-0.1", + halfTrunc: "-0", + halfEven: "-0", + }, + }, + { + value: -0.06, + options: {maximumFractionDigits: 1}, + roundingModes: { + ceil: "-0", + floor: "-0.1", + expand: "-0.1", + trunc: "-0", + halfCeil: "-0.1", + halfFloor: "-0.1", + halfExpand: "-0.1", + halfTrunc: "-0.1", + halfEven: "-0.1", + }, + }, +]; + +for (let {value, options, roundingModes} of tests) { + for (let [roundingMode, expected] of Object.entries(roundingModes)) { + let nf = new Intl.NumberFormat("en", {...options, roundingMode}); + assertEq(nf.format(value), expected, `value=${value}, roundingMode=${roundingMode}`); + assertEq(nf.resolvedOptions().roundingMode, roundingMode); + } +} + +// Default value is "halfExpand". +assertEq(new Intl.NumberFormat().resolvedOptions().roundingMode, "halfExpand"); + +// Invalid values. +for (let roundingMode of ["", null, "halfOdd", "halfUp", "Up", "up"]){ + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {roundingMode}), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/rounding-priority.js b/js/src/tests/non262/Intl/NumberFormat/rounding-priority.js new file mode 100644 index 0000000000..49c9e1b274 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/rounding-priority.js @@ -0,0 +1,132 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +const tests = [ + // Rounding conflict with maximum fraction/significand digits. + { + value: 4.321, + options: { + maximumFractionDigits: 2, + maximumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "4.3", + lessPrecision: "4.3", + morePrecision: "4.32", + }, + }, + { + value: 4.321, + options: { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + maximumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "4.3", + lessPrecision: "4.3", + morePrecision: "4.32", + }, + }, + { + value: 4.321, + options: { + maximumFractionDigits: 2, + maximumSignificantDigits: 2, + minimumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "4.3", + lessPrecision: "4.3", + morePrecision: "4.32", + }, + }, + { + value: 4.321, + options: { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + maximumSignificantDigits: 2, + minimumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "4.3", + lessPrecision: "4.3", + morePrecision: "4.32", + }, + }, + + // Rounding conflict with minimum fraction/significand digits. + { + value: 1.0, + options: { + minimumFractionDigits: 2, + minimumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "1.0", + // Returning "1.00" for both options seems unexpected. Also filed at + // <https://github.com/tc39/proposal-intl-numberformat-v3/issues/52>. + lessPrecision: "1.00", + morePrecision: "1.0", + }, + }, + { + value: 1.0, + options: { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + minimumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "1.0", + lessPrecision: "1.00", + morePrecision: "1.0", + }, + }, + { + value: 1.0, + options: { + minimumFractionDigits: 2, + minimumSignificantDigits: 2, + maximumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "1.0", + lessPrecision: "1.0", + morePrecision: "1.00", + }, + }, + { + value: 1.0, + options: { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + minimumSignificantDigits: 2, + maximumSignificantDigits: 2, + }, + roundingPriorities: { + auto: "1.0", + lessPrecision: "1.0", + morePrecision: "1.00", + }, + }, +]; + +for (let {value, options, roundingPriorities} of tests) { + for (let [roundingPriority, expected] of Object.entries(roundingPriorities)) { + let nf = new Intl.NumberFormat("en", {...options, roundingPriority}); + assertEq(nf.resolvedOptions().roundingPriority, roundingPriority); + assertEq(nf.format(value), expected, `value=${value}, roundingPriority=${roundingPriority}`); + } +} + +// Default value of "auto". +assertEq(new Intl.NumberFormat().resolvedOptions().roundingPriority, "auto"); + +// Invalid values. +for (let roundingPriority of ["", null, "more", "less", "never"]){ + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {roundingPriority}), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/shell.js b/js/src/tests/non262/Intl/NumberFormat/shell.js new file mode 100644 index 0000000000..471fdad71b --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/shell.js @@ -0,0 +1,77 @@ +function GenericPartCreator(type) { + return str => ({ type, value: str }); +} + +const NumberFormatParts = { + Nan: GenericPartCreator("nan"), + Inf: GenericPartCreator("infinity"), + Integer: GenericPartCreator("integer"), + Group: GenericPartCreator("group"), + Decimal: GenericPartCreator("decimal"), + Fraction: GenericPartCreator("fraction"), + MinusSign: GenericPartCreator("minusSign"), + PlusSign: GenericPartCreator("plusSign"), + PercentSign: GenericPartCreator("percentSign"), + Currency: GenericPartCreator("currency"), + Literal: GenericPartCreator("literal"), + ExponentSeparator: GenericPartCreator("exponentSeparator"), + ExponentMinusSign: GenericPartCreator("exponentMinusSign"), + ExponentInteger: GenericPartCreator("exponentInteger"), + Compact: GenericPartCreator("compact"), + Unit: GenericPartCreator("unit"), +}; + +function NumberRangeFormatParts(source) { + let entries = Object.entries(NumberFormatParts) + + entries.push(["Approx", GenericPartCreator("approximatelySign")]); + + return Object.fromEntries(entries.map(([key, part]) => { + let partWithSource = str => { + return Object.defineProperty(part(str), "source", { + value: source, writable: true, enumerable: true, configurable: true + }); + }; + return [key, partWithSource]; + })); +} + +function assertParts(nf, x, expected) { + var parts = nf.formatToParts(x); + assertEq(parts.map(part => part.value).join(""), nf.format(x), + "formatToParts and format must agree"); + + var len = parts.length; + assertEq(len, expected.length, "parts count mismatch"); + for (var i = 0; i < len; i++) { + assertEq(parts[i].type, expected[i].type, "type mismatch at " + i); + assertEq(parts[i].value, expected[i].value, "value mismatch at " + i); + } +} + +function assertRangeParts(nf, start, end, expected) { + var parts = nf.formatRangeToParts(start, end); + assertEq(parts.map(part => part.value).join(""), nf.formatRange(start, end), + "formatRangeToParts and formatRange must agree"); + + var len = parts.length; + assertEq(len, expected.length, "parts count mismatch"); + for (var i = 0; i < len; i++) { + assertEq(parts[i].type, expected[i].type, "type mismatch at " + i); + assertEq(parts[i].value, expected[i].value, "value mismatch at " + i); + assertEq(parts[i].source, expected[i].source, "source mismatch at " + i); + } +} + +function runNumberFormattingTestcases(testcases) { + for (let {locale, options, values} of testcases) { + let nf = new Intl.NumberFormat(locale, options); + + for (let {value, string, parts} of values) { + assertEq(nf.format(value), string, + `locale=${locale}, options=${JSON.stringify(options)}, value=${value}`); + + assertParts(nf, value, parts); + } + } +} diff --git a/js/src/tests/non262/Intl/NumberFormat/sign-display.js b/js/src/tests/non262/Intl/NumberFormat/sign-display.js new file mode 100644 index 0000000000..b18517d847 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/sign-display.js @@ -0,0 +1,187 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction +} = NumberFormatParts; + +const testcases = [ + // "auto": Show the sign on negative numbers only. + { + locale: "en", + options: { + signDisplay: "auto", + }, + values: [ + {value: +0, string: "0", parts: [Integer("0")]}, + {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + {value: 0n, string: "0", parts: [Integer("0")]}, + + {value: 1, string: "1", parts: [Integer("1")]}, + {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + {value: 1n, string: "1", parts: [Integer("1")]}, + {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + + {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]}, + {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]}, + + {value: 0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]}, + {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // "never": Show the sign on neither positive nor negative numbers. + { + locale: "en", + options: { + signDisplay: "never", + }, + values: [ + {value: +0, string: "0", parts: [Integer("0")]}, + {value: -0, string: "0", parts: [Integer("0")]}, + {value: 0n, string: "0", parts: [Integer("0")]}, + + {value: 1, string: "1", parts: [Integer("1")]}, + {value: -1, string: "1", parts: [Integer("1")]}, + {value: 1n, string: "1", parts: [Integer("1")]}, + {value: -1n, string: "1", parts: [Integer("1")]}, + + {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]}, + {value: -0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]}, + + {value: 0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]}, + {value: -0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]}, + + {value: Infinity, string: "∞", parts: [Inf("∞")]}, + {value: -Infinity, string: "∞", parts: [Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // "always": Show the sign on positive and negative numbers including zero. + { + locale: "en", + options: { + signDisplay: "always", + }, + values: [ + {value: +0, string: "+0", parts: [PlusSign("+"), Integer("0")]}, + {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + {value: 0n, string: "+0", parts: [PlusSign("+"), Integer("0")]}, + + {value: 1, string: "+1", parts: [PlusSign("+"), Integer("1")]}, + {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + {value: 1n, string: "+1", parts: [PlusSign("+"), Integer("1")]}, + {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + + {value: 0.1, string: "+0.1", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("1")]}, + {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]}, + + {value: 0.9, string: "+0.9", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("9")]}, + {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]}, + + {value: Infinity, string: "+∞", parts: [PlusSign("+"), Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "+NaN", parts: [PlusSign("+"), Nan("NaN")]}, + {value: -NaN, string: "+NaN", parts: [PlusSign("+"), Nan("NaN")]}, + ], + }, + + // "exceptZero": Show the sign on positive and negative numbers but not zero. + { + locale: "en", + options: { + signDisplay: "exceptZero", + }, + values: [ + {value: +0, string: "0", parts: [Integer("0")]}, + {value: -0, string: "0", parts: [Integer("0")]}, + {value: 0n, string: "0", parts: [Integer("0")]}, + + {value: 1, string: "+1", parts: [PlusSign("+"), Integer("1")]}, + {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + {value: 1n, string: "+1", parts: [PlusSign("+"), Integer("1")]}, + {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]}, + + {value: 0.1, string: "+0.1", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("1")]}, + {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]}, + + {value: 0.9, string: "+0.9", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("9")]}, + {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]}, + + {value: Infinity, string: "+∞", parts: [PlusSign("+"), Inf("∞")]}, + {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]}, + + {value: NaN, string: "NaN", parts: [Nan("NaN")]}, + {value: -NaN, string: "NaN", parts: [Nan("NaN")]}, + ], + }, + + // Tests with suppressed fractional digits. + + // "auto": Show the sign on negative numbers only. + { + locale: "en", + options: { + signDisplay: "auto", + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "0", parts: [Integer("0")]}, + {value: -0.1, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + ], + }, + + // "never": Show the sign on neither positive nor negative numbers. + { + locale: "en", + options: { + signDisplay: "never", + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "0", parts: [Integer("0")]}, + {value: -0.1, string: "0", parts: [Integer("0")]}, + ], + }, + + // "always": Show the sign on positive and negative numbers including zero. + { + locale: "en", + options: { + signDisplay: "always", + maximumFractionDigits: 0, + }, + values: [ + {value: +0.1, string: "+0", parts: [PlusSign("+"), Integer("0")]}, + {value: -0.1, string: "-0", parts: [MinusSign("-"), Integer("0")]}, + ], + }, + + // "exceptZero": Show the sign on positive and negative numbers but not zero. + { + locale: "en", + options: { + signDisplay: "exceptZero", + maximumFractionDigits: 0, + }, + + values: [ + {value: +0.1, string: "0", parts: [Integer("0")]}, + {value: -0.1, string: "0", parts: [Integer("0")]}, + ], + } +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js b/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js new file mode 100644 index 0000000000..c569313266 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +// -- test that NumberFormat correctly formats 0 with various numbers of significant digits + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var testData = [ + {minimumSignificantDigits: 1, maximumSignificantDigits: 1, expected: "0"}, + {minimumSignificantDigits: 1, maximumSignificantDigits: 2, expected: "0"}, + {minimumSignificantDigits: 1, maximumSignificantDigits: 3, expected: "0"}, + {minimumSignificantDigits: 1, maximumSignificantDigits: 4, expected: "0"}, + {minimumSignificantDigits: 1, maximumSignificantDigits: 5, expected: "0"}, + {minimumSignificantDigits: 2, maximumSignificantDigits: 2, expected: "0.0"}, + {minimumSignificantDigits: 2, maximumSignificantDigits: 3, expected: "0.0"}, + {minimumSignificantDigits: 2, maximumSignificantDigits: 4, expected: "0.0"}, + {minimumSignificantDigits: 2, maximumSignificantDigits: 5, expected: "0.0"}, + {minimumSignificantDigits: 3, maximumSignificantDigits: 3, expected: "0.00"}, + {minimumSignificantDigits: 3, maximumSignificantDigits: 4, expected: "0.00"}, + {minimumSignificantDigits: 3, maximumSignificantDigits: 5, expected: "0.00"}, +]; + +for (var i = 0; i < testData.length; i++) { + var min = testData[i].minimumSignificantDigits; + var max = testData[i].maximumSignificantDigits; + var options = {minimumSignificantDigits: min, maximumSignificantDigits: max}; + var format = new Intl.NumberFormat("en-US", options); + var actual = format.format(0); + var expected = testData[i].expected; + assertEq(actual, expected, + "Wrong formatted string for 0 with " + + "minimumSignificantDigits " + min + + ", maximumSignificantDigits " + max + + ": expected \"" + expected + + "\", actual \"" + actual + "\""); +} + +reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js b/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js new file mode 100644 index 0000000000..5ba4470a98 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js @@ -0,0 +1,371 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell) +// -- test in browser only that ICU has locale data for all Mozilla languages + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This array contains the locales that ICU supports in +// number formatting whose languages Mozilla localizes Firefox into. +// Current as of ICU 50.1.2 and Firefox March 2013. +var locales = [ + "af", + "af-NA", + "af-ZA", + "ar", + "ar-001", + "ar-AE", + "ar-BH", + "ar-DJ", + "ar-DZ", + "ar-EG", + "ar-EH", + "ar-ER", + "ar-IL", + "ar-IQ", + "ar-JO", + "ar-KM", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-MR", + "ar-OM", + "ar-PS", + "ar-QA", + "ar-SA", + "ar-SD", + "ar-SO", + "ar-SY", + "ar-TD", + "ar-TN", + "ar-YE", + "as", + "as-IN", + "be", + "be-BY", + "bg", + "bg-BG", + "bn", + "bn-BD", + "bn-IN", + "br", + "br-FR", + "bs", + "bs-Cyrl", + "bs-Cyrl-BA", + "bs-Latn", + "bs-Latn-BA", + "ca", + "ca-AD", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-BE", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "el", + "el-CY", + "el-GR", + "en", + "en-150", + "en-AG", + "en-AS", + "en-AU", + "en-BB", + "en-BE", + "en-BM", + "en-BS", + "en-BW", + "en-BZ", + "en-CA", + "en-CM", + "en-DM", + "en-FJ", + "en-FM", + "en-GB", + "en-GD", + "en-GG", + "en-GH", + "en-GI", + "en-GM", + "en-GU", + "en-GY", + "en-HK", + "en-IE", + "en-IM", + "en-IN", + "en-JE", + "en-JM", + "en-KE", + "en-KI", + "en-KN", + "en-KY", + "en-LC", + "en-LR", + "en-LS", + "en-MG", + "en-MH", + "en-MP", + "en-MT", + "en-MU", + "en-MW", + "en-NA", + "en-NG", + "en-NZ", + "en-PG", + "en-PH", + "en-PK", + "en-PR", + "en-PW", + "en-SB", + "en-SC", + "en-SG", + "en-SL", + "en-SS", + "en-SZ", + "en-TC", + "en-TO", + "en-TT", + "en-TZ", + "en-UG", + "en-UM", + "en-US", + "en-US-POSIX", + "en-VC", + "en-VG", + "en-VI", + "en-VU", + "en-WS", + "en-ZA", + "en-ZM", + "en-ZW", + "eo", + "es", + "es-419", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-CU", + "es-DO", + "es-EA", + "es-EC", + "es-ES", + "es-GQ", + "es-GT", + "es-HN", + "es-IC", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PH", + "es-PR", + "es-PY", + "es-SV", + "es-US", + "es-UY", + "es-VE", + "et", + "et-EE", + "eu", + "eu-ES", + "fa", + "fa-AF", + "fa-IR", + "ff", + "ff-SN", + "fi", + "fi-FI", + "fr", + "fr-BE", + "fr-BF", + "fr-BI", + "fr-BJ", + "fr-BL", + "fr-CA", + "fr-CD", + "fr-CF", + "fr-CG", + "fr-CH", + "fr-CI", + "fr-CM", + "fr-DJ", + "fr-DZ", + "fr-FR", + "fr-GA", + "fr-GF", + "fr-GN", + "fr-GP", + "fr-GQ", + "fr-HT", + "fr-KM", + "fr-LU", + "fr-MA", + "fr-MC", + "fr-MF", + "fr-MG", + "fr-ML", + "fr-MQ", + "fr-MR", + "fr-MU", + "fr-NC", + "fr-NE", + "fr-PF", + "fr-RE", + "fr-RW", + "fr-SC", + "fr-SN", + "fr-SY", + "fr-TD", + "fr-TG", + "fr-TN", + "fr-VU", + "fr-YT", + "ga", + "ga-IE", + "gl", + "gl-ES", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "it-SM", + "ja", + "ja-JP", + "kk", + "kk-Cyrl", + "kk-Cyrl-KZ", + "km", + "km-KH", + "kn", + "kn-IN", + "ko", + "ko-KP", + "ko-KR", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mk", + "mk-MK", + "ml", + "ml-IN", + "mr", + "mr-IN", + "nb", + "nb-NO", + "nl", + "nl-AW", + "nl-BE", + "nl-CW", + "nl-NL", + "nl-SR", + "nl-SX", + "nn", + "nn-NO", + "or", + "or-IN", + "pa", + "pa-Arab", + "pa-Arab-PK", + "pa-Guru", + "pa-Guru-IN", + "pl", + "pl-PL", + "pt", + "pt-AO", + "pt-BR", + "pt-CV", + "pt-GW", + "pt-MO", + "pt-MZ", + "pt-PT", + "pt-ST", + "pt-TL", + "rm", + "rm-CH", + "ro", + "ro-MD", + "ro-RO", + "ru", + "ru-BY", + "ru-KG", + "ru-KZ", + "ru-MD", + "ru-RU", + "ru-UA", + "si", + "si-LK", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sq-MK", + "sr", + "sr-Cyrl", + "sr-Cyrl-BA", + "sr-Cyrl-ME", + "sr-Cyrl-RS", + "sr-Latn", + "sr-Latn-BA", + "sr-Latn-ME", + "sr-Latn-RS", + "sv", + "sv-AX", + "sv-FI", + "sv-SE", + "te", + "te-IN", + "th", + "th-TH", + "tr", + "tr-CY", + "tr-TR", + "uk", + "uk-UA", + "vi", + "vi-VN", + "zh", + "zh-Hans", + "zh-Hans-CN", + "zh-Hans-HK", + "zh-Hans-MO", + "zh-Hans-SG", + "zh-Hant", + "zh-Hant-HK", + "zh-Hant-MO", + "zh-Hant-TW", +]; + +var count = Intl.NumberFormat.supportedLocalesOf(locales).length; + +reportCompare(locales.length, count, "Number of supported locales in Intl.NumberFormat"); diff --git a/js/src/tests/non262/Intl/NumberFormat/toStringTag.js b/js/src/tests/non262/Intl/NumberFormat/toStringTag.js new file mode 100644 index 0000000000..9ad0901a0b --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/toStringTag.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var desc = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, Symbol.toStringTag); + +assertEq(desc !== undefined, true); +assertEq(desc.value, "Intl.NumberFormat"); +assertEq(desc.writable, false); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); + +assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object Intl.NumberFormat]"); +assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object Intl.NumberFormat]"); + +Object.defineProperty(Intl.NumberFormat.prototype, Symbol.toStringTag, {value: "NumberFormat"}); + +assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object NumberFormat]"); +assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object NumberFormat]"); + +delete Intl.NumberFormat.prototype[Symbol.toStringTag]; + +assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object Object]"); +assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object Object]"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/trailing-zero-display.js b/js/src/tests/non262/Intl/NumberFormat/trailing-zero-display.js new file mode 100644 index 0000000000..5e3113285a --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/trailing-zero-display.js @@ -0,0 +1,98 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +// "stripIfInteger" with fractional digits. +{ + let nf1 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + }); + + let nf2 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + trailingZeroDisplay: "auto", + }); + + let nf3 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + trailingZeroDisplay: "stripIfInteger", + }); + + assertEq(nf1.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf2.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf3.resolvedOptions().trailingZeroDisplay, "stripIfInteger"); + + assertEq(nf1.format(123), "123.00"); + assertEq(nf2.format(123), "123.00"); + assertEq(nf3.format(123), "123"); +} + +// "stripIfInteger" with significand digits. +{ + let nf1 = new Intl.NumberFormat("en", { + minimumSignificantDigits: 2, + }); + + let nf2 = new Intl.NumberFormat("en", { + minimumSignificantDigits: 2, + trailingZeroDisplay: "auto", + }); + + let nf3 = new Intl.NumberFormat("en", { + minimumSignificantDigits: 2, + trailingZeroDisplay: "stripIfInteger", + }); + + assertEq(nf1.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf2.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf3.resolvedOptions().trailingZeroDisplay, "stripIfInteger"); + + assertEq(nf1.format(1), "1.0"); + assertEq(nf2.format(1), "1.0"); + assertEq(nf3.format(1), "1"); +} + +// "stripIfInteger" with rounding increment. +{ + let nf1 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + roundingIncrement: 5, + }); + let nf2 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + roundingIncrement: 5, + trailingZeroDisplay: "auto", + }); + let nf3 = new Intl.NumberFormat("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + roundingIncrement: 5, + trailingZeroDisplay: "stripIfInteger", + }); + + assertEq(nf1.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf2.resolvedOptions().trailingZeroDisplay, "auto"); + assertEq(nf3.resolvedOptions().trailingZeroDisplay, "stripIfInteger"); + + // NB: Tests 1.975 twice b/c of <https://unicode-org.atlassian.net/browse/ICU-21674>. + + assertEq(nf1.format(1.975), "2.00"); + assertEq(nf1.format(1.97), "1.95"); + assertEq(nf1.format(1.975), "2.00"); + + assertEq(nf2.format(1.975), "2.00"); + assertEq(nf2.format(1.97), "1.95"); + assertEq(nf2.format(1.975), "2.00"); + + assertEq(nf3.format(1.975), "2"); + assertEq(nf3.format(1.97), "1.95"); + assertEq(nf3.format(1.975), "2"); +} + +// Invalid values. +for (let trailingZeroDisplay of ["", "true", true, "none", "yes", "no"]){ + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {trailingZeroDisplay}), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js b/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js new file mode 100644 index 0000000000..2aad94b98e --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. + +const sanctionedSimpleUnitIdentifiers = [ + "acre", + "bit", + "byte", + "celsius", + "centimeter", + "day", + "degree", + "fahrenheit", + "fluid-ounce", + "foot", + "gallon", + "gigabit", + "gigabyte", + "gram", + "hectare", + "hour", + "inch", + "kilobit", + "kilobyte", + "kilogram", + "kilometer", + "liter", + "megabit", + "megabyte", + "meter", + "microsecond", + "mile", + "mile-scandinavian", + "milliliter", + "millimeter", + "millisecond", + "minute", + "month", + "nanosecond", + "ounce", + "percent", + "petabyte", + "pound", + "second", + "stone", + "terabit", + "terabyte", + "week", + "yard", + "year" +]; + +// Test all simple unit identifier combinations are allowed. + +for (const numerator of sanctionedSimpleUnitIdentifiers) { + for (const denominator of sanctionedSimpleUnitIdentifiers) { + const unit = `${numerator}-per-${denominator}`; + const nf = new Intl.NumberFormat("en", {style: "unit", unit}); + + assertEq(nf.format(1), nf.formatToParts(1).map(p => p.value).join("")); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js b/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js new file mode 100644 index 0000000000..9292c8ac73 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js @@ -0,0 +1,90 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. + +const sanctionedSimpleUnitIdentifiers = [ + "acre", + "bit", + "byte", + "celsius", + "centimeter", + "day", + "degree", + "fahrenheit", + "fluid-ounce", + "foot", + "gallon", + "gigabit", + "gigabyte", + "gram", + "hectare", + "hour", + "inch", + "kilobit", + "kilobyte", + "kilogram", + "kilometer", + "liter", + "megabit", + "megabyte", + "meter", + "microsecond", + "mile", + "mile-scandinavian", + "milliliter", + "millimeter", + "millisecond", + "minute", + "month", + "nanosecond", + "ounce", + "percent", + "petabyte", + "pound", + "second", + "stone", + "terabit", + "terabyte", + "week", + "yard", + "year" +]; + +// Test only English and Chinese to keep the overall runtime reasonable. +// +// Chinese is included because it contains more than one "unit" element for +// certain unit combinations. +const locales = ["en", "zh"]; + +// Plural rules for English only differentiate between "one" and "other". Plural +// rules for Chinese only use "other". That means we only need to test two values +// per unit. +const values = [0, 1]; + +// Ensure unit formatters contain at least one "unit" element. + +for (const locale of locales) { + for (const unit of sanctionedSimpleUnitIdentifiers) { + const nf = new Intl.NumberFormat(locale, {style: "unit", unit}); + + for (const value of values) { + assertEq(nf.formatToParts(value).some(e => e.type === "unit"), true, + `locale=${locale}, unit=${unit}`); + } + } + + for (const numerator of sanctionedSimpleUnitIdentifiers) { + for (const denominator of sanctionedSimpleUnitIdentifiers) { + const unit = `${numerator}-per-${denominator}`; + const nf = new Intl.NumberFormat(locale, {style: "unit", unit}); + + for (const value of values) { + assertEq(nf.formatToParts(value).some(e => e.type === "unit"), true, + `locale=${locale}, unit=${unit}`); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js b/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js new file mode 100644 index 0000000000..8eca3b58cf --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js @@ -0,0 +1,267 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. + +const sanctionedSimpleUnitIdentifiers = [ + "acre", + "bit", + "byte", + "celsius", + "centimeter", + "day", + "degree", + "fahrenheit", + "fluid-ounce", + "foot", + "gallon", + "gigabit", + "gigabyte", + "gram", + "hectare", + "hour", + "inch", + "kilobit", + "kilobyte", + "kilogram", + "kilometer", + "liter", + "megabit", + "megabyte", + "meter", + "microsecond", + "mile", + "mile-scandinavian", + "milliliter", + "millimeter", + "millisecond", + "minute", + "month", + "nanosecond", + "ounce", + "percent", + "petabyte", + "pound", + "second", + "stone", + "terabit", + "terabyte", + "week", + "yard", + "year" +]; + +const allUnits = [ + "acceleration-g-force", + "acceleration-meter-per-square-second", + "angle-arc-minute", + "angle-arc-second", + "angle-degree", + "angle-radian", + "angle-revolution", + "area-acre", + "area-dunam", + "area-hectare", + "area-square-centimeter", + "area-square-foot", + "area-square-inch", + "area-square-kilometer", + "area-square-meter", + "area-square-mile", + "area-square-yard", + "concentr-item", + "concentr-karat", + "concentr-milligram-ofglucose-per-deciliter", + "concentr-millimole-per-liter", + "concentr-mole", + "concentr-percent", + "concentr-permille", + "concentr-permillion", + "concentr-permyriad", + "consumption-liter-per-100-kilometer", + "consumption-liter-per-kilometer", + "consumption-mile-per-gallon", + "consumption-mile-per-gallon-imperial", + "digital-bit", + "digital-byte", + "digital-gigabit", + "digital-gigabyte", + "digital-kilobit", + "digital-kilobyte", + "digital-megabit", + "digital-megabyte", + "digital-petabyte", + "digital-terabit", + "digital-terabyte", + "duration-century", + "duration-day", + "duration-day-person", + "duration-decade", + "duration-hour", + "duration-microsecond", + "duration-millisecond", + "duration-minute", + "duration-month", + "duration-month-person", + "duration-nanosecond", + "duration-quarter", + "duration-second", + "duration-week", + "duration-week-person", + "duration-year", + "duration-year-person", + "electric-ampere", + "electric-milliampere", + "electric-ohm", + "electric-volt", + "energy-british-thermal-unit", + "energy-calorie", + "energy-electronvolt", + "energy-foodcalorie", + "energy-joule", + "energy-kilocalorie", + "energy-kilojoule", + "energy-kilowatt-hour", + "energy-therm-us", + "force-kilowatt-hour-per-100-kilometer", + "force-newton", + "force-pound-force", + "frequency-gigahertz", + "frequency-hertz", + "frequency-kilohertz", + "frequency-megahertz", + "graphics-dot", + "graphics-dot-per-centimeter", + "graphics-dot-per-inch", + "graphics-em", + "graphics-megapixel", + "graphics-pixel", + "graphics-pixel-per-centimeter", + "graphics-pixel-per-inch", + "length-astronomical-unit", + "length-centimeter", + "length-decimeter", + "length-earth-radius", + "length-fathom", + "length-foot", + "length-furlong", + "length-inch", + "length-kilometer", + "length-light-year", + "length-meter", + "length-micrometer", + "length-mile", + "length-mile-scandinavian", + "length-millimeter", + "length-nanometer", + "length-nautical-mile", + "length-parsec", + "length-picometer", + "length-point", + "length-solar-radius", + "length-yard", + "light-candela", + "light-lumen", + "light-lux", + "light-solar-luminosity", + "mass-carat", + "mass-dalton", + "mass-earth-mass", + "mass-grain", + "mass-gram", + "mass-kilogram", + "mass-microgram", + "mass-milligram", + "mass-ounce", + "mass-ounce-troy", + "mass-pound", + "mass-solar-mass", + "mass-stone", + "mass-ton", + "mass-tonne", + "power-gigawatt", + "power-horsepower", + "power-kilowatt", + "power-megawatt", + "power-milliwatt", + "power-watt", + "pressure-atmosphere", + "pressure-bar", + "pressure-hectopascal", + "pressure-inch-ofhg", + "pressure-kilopascal", + "pressure-megapascal", + "pressure-millibar", + "pressure-millimeter-ofhg", + "pressure-pascal", + "pressure-pound-force-per-square-inch", + "speed-kilometer-per-hour", + "speed-knot", + "speed-meter-per-second", + "speed-mile-per-hour", + "temperature-celsius", + "temperature-fahrenheit", + "temperature-generic", + "temperature-kelvin", + "torque-newton-meter", + "torque-pound-force-foot", + "volume-acre-foot", + "volume-barrel", + "volume-bushel", + "volume-centiliter", + "volume-cubic-centimeter", + "volume-cubic-foot", + "volume-cubic-inch", + "volume-cubic-kilometer", + "volume-cubic-meter", + "volume-cubic-mile", + "volume-cubic-yard", + "volume-cup", + "volume-cup-metric", + "volume-deciliter", + "volume-dessert-spoon", + "volume-dessert-spoon-imperial", + "volume-dram", + "volume-drop", + "volume-fluid-ounce", + "volume-fluid-ounce-imperial", + "volume-gallon", + "volume-gallon-imperial", + "volume-hectoliter", + "volume-jigger", + "volume-liter", + "volume-megaliter", + "volume-milliliter", + "volume-pinch", + "volume-pint", + "volume-pint-metric", + "volume-quart", + "volume-quart-imperial", + "volume-tablespoon", + "volume-teaspoon" +]; + +// Test only sanctioned unit identifiers are allowed. + +for (const typeAndUnit of allUnits) { + const [_, type, unit] = typeAndUnit.match(/(\w+)-(.+)/); + + let allowed; + if (unit.includes("-per-")) { + const [numerator, denominator] = unit.split("-per-"); + allowed = sanctionedSimpleUnitIdentifiers.includes(numerator) && + sanctionedSimpleUnitIdentifiers.includes(denominator); + } else { + allowed = sanctionedSimpleUnitIdentifiers.includes(unit); + } + + if (allowed) { + const nf = new Intl.NumberFormat("en", {style: "unit", unit}); + assertEq(nf.format(1), nf.formatToParts(1).map(p => p.value).join("")); + } else { + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {style: "unit", unit}), + RangeError, `Missing error for "${typeAndUnit}"`); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/unit.js b/js/src/tests/non262/Intl/NumberFormat/unit.js new file mode 100644 index 0000000000..7abc5142e0 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/unit.js @@ -0,0 +1,104 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const { + Nan, Inf, Integer, MinusSign, PlusSign, Literal, + Unit +} = NumberFormatParts; + +const testcases = [ + { + locale: "en", + options: { + style: "unit", + unit: "meter", + unitDisplay: "short", + }, + values: [ + {value: +0, string: "0 m", parts: [Integer("0"), Literal(" "), Unit("m")]}, + {value: -0, string: "-0 m", parts: [MinusSign("-"), Integer("0"), Literal(" "), Unit("m")]}, + {value: 0n, string: "0 m", parts: [Integer("0"), Literal(" "), Unit("m")]}, + + {value: 1, string: "1 m", parts: [Integer("1"), Literal(" "), Unit("m")]}, + {value: -1, string: "-1 m", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("m")]}, + {value: 1n, string: "1 m", parts: [Integer("1"), Literal(" "), Unit("m")]}, + {value: -1n, string: "-1 m", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("m")]}, + + {value: Infinity, string: "∞ m", parts: [Inf("∞"), Literal(" "), Unit("m")]}, + {value: -Infinity, string: "-∞ m", parts: [MinusSign("-"), Inf("∞"), Literal(" "), Unit("m")]}, + + {value: NaN, string: "NaN m", parts: [Nan("NaN"), Literal(" "), Unit("m")]}, + {value: -NaN, string: "NaN m", parts: [Nan("NaN"), Literal(" "), Unit("m")]}, + ], + }, + + { + locale: "en", + options: { + style: "unit", + unit: "meter", + unitDisplay: "narrow", + }, + values: [ + {value: +0, string: "0m", parts: [Integer("0"), Unit("m")]}, + {value: -0, string: "-0m", parts: [MinusSign("-"), Integer("0"), Unit("m")]}, + {value: 0n, string: "0m", parts: [Integer("0"), Unit("m")]}, + + {value: 1, string: "1m", parts: [Integer("1"), Unit("m")]}, + {value: -1, string: "-1m", parts: [MinusSign("-"), Integer("1"), Unit("m")]}, + {value: 1n, string: "1m", parts: [Integer("1"), Unit("m")]}, + {value: -1n, string: "-1m", parts: [MinusSign("-"), Integer("1"), Unit("m")]}, + + {value: Infinity, string: "∞m", parts: [Inf("∞"), Unit("m")]}, + {value: -Infinity, string: "-∞m", parts: [MinusSign("-"), Inf("∞"), Unit("m")]}, + + {value: NaN, string: "NaNm", parts: [Nan("NaN"), Unit("m")]}, + {value: -NaN, string: "NaNm", parts: [Nan("NaN"), Unit("m")]}, + ], + }, + + { + locale: "en", + options: { + style: "unit", + unit: "meter", + unitDisplay: "long", + }, + values: [ + {value: +0, string: "0 meters", parts: [Integer("0"), Literal(" "), Unit("meters")]}, + {value: -0, string: "-0 meters", parts: [MinusSign("-"), Integer("0"), Literal(" "), Unit("meters")]}, + {value: 0n, string: "0 meters", parts: [Integer("0"), Literal(" "), Unit("meters")]}, + + {value: 1, string: "1 meter", parts: [Integer("1"), Literal(" "), Unit("meter")]}, + {value: -1, string: "-1 meter", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("meter")]}, + {value: 1n, string: "1 meter", parts: [Integer("1"), Literal(" "), Unit("meter")]}, + {value: -1n, string: "-1 meter", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("meter")]}, + + {value: Infinity, string: "∞ meters", parts: [Inf("∞"), Literal(" "), Unit("meters")]}, + {value: -Infinity, string: "-∞ meters", parts: [MinusSign("-"), Inf("∞"), Literal(" "), Unit("meters")]}, + + {value: NaN, string: "NaN meters", parts: [Nan("NaN"), Literal(" "), Unit("meters")]}, + {value: -NaN, string: "NaN meters", parts: [Nan("NaN"), Literal(" "), Unit("meters")]}, + ], + }, + + // Ensure the correct compound unit is automatically selected by ICU. For + // example instead of "50 chilometri al orari", 50 km/h should return + // "50 chilometri orari" in Italian. + + { + locale: "it", + options: { + style: "unit", + unit: "kilometer-per-hour", + unitDisplay: "long", + }, + values: [ + {value: 50, string: "50 chilometri orari", parts: [Integer("50"), Literal(" "), Unit("chilometri orari")]}, + ], + }, +]; + +runNumberFormattingTestcases(testcases); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/unwrapping.js b/js/src/tests/non262/Intl/NumberFormat/unwrapping.js new file mode 100644 index 0000000000..df0b15d6fc --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/unwrapping.js @@ -0,0 +1,248 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test UnwrapNumberFormat operation. + +const numberFormatFunctions = []; +numberFormatFunctions.push({ + function: Intl.NumberFormat.prototype.resolvedOptions, + unwrap: true, +}); +numberFormatFunctions.push({ + function: Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get, + unwrap: true, +}); +numberFormatFunctions.push({ + function: Intl.NumberFormat.prototype.formatToParts, + unwrap: false, +}); + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function IsObject(o) { + return Object(o) === o; +} + +function IsPrimitive(o) { + return Object(o) !== o; +} + +function intlObjects(ctor) { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; +} + +function thisValues(C) { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.filter(ctor => ctor !== C).map(intlObjects)), + + // Object inheriting from an Intl constructor prototype. + ...intlConstructors.map(ctor => Object.create(ctor.prototype)), + ]; +} + +const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0]; + +// Test Intl.NumberFormat.prototype methods. +for (let {function: numberFormatFunction, unwrap} of numberFormatFunctions) { + // Test a TypeError is thrown when the this-value isn't an initialized + // Intl.NumberFormat instance. + for (let thisValue of thisValues(Intl.NumberFormat)) { + assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError); + } + + // And test no error is thrown for initialized Intl.NumberFormat instances. + for (let thisValue of intlObjects(Intl.NumberFormat)) { + numberFormatFunction.call(thisValue); + } + + // Manually add [[FallbackSymbol]] to objects and then repeat the tests from above. + for (let thisValue of thisValues(Intl.NumberFormat)) { + assertThrowsInstanceOf(() => numberFormatFunction.call({ + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: thisValue, + }), TypeError); + } + + for (let thisValue of intlObjects(Intl.NumberFormat)) { + let obj = { + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: thisValue, + }; + if (unwrap) { + numberFormatFunction.call(obj); + } else { + assertThrowsInstanceOf(() => numberFormatFunction.call(obj), TypeError); + } + } + + // Ensure [[FallbackSymbol]] isn't retrieved for Intl.NumberFormat instances. + for (let thisValue of intlObjects(Intl.NumberFormat)) { + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { assertEq(false, true); } + }); + numberFormatFunction.call(thisValue); + } + + // Ensure [[FallbackSymbol]] is only retrieved for objects inheriting from Intl.NumberFormat.prototype. + for (let thisValue of thisValues(Intl.NumberFormat).filter(IsObject)) { + if (Intl.NumberFormat.prototype.isPrototypeOf(thisValue)) + continue; + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { assertEq(false, true); } + }); + assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError); + } + + // Repeat the test from above, but also change Intl.NumberFormat[@@hasInstance] + // so it always returns |true|. + for (let thisValue of thisValues(Intl.NumberFormat).filter(IsObject)) { + let isPrototypeOf = Intl.NumberFormat.prototype.isPrototypeOf(thisValue); + let hasInstanceCalled = false, symbolGetterCalled = false; + Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { + value() { + assertEq(hasInstanceCalled, false); + hasInstanceCalled = true; + return true; + }, configurable: true + }); + Object.defineProperty(thisValue, intlFallbackSymbol, { + get() { + assertEq(symbolGetterCalled, false); + symbolGetterCalled = true; + return null; + }, configurable: true + }); + + assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError); + + delete Intl.NumberFormat[Symbol.hasInstance]; + + assertEq(hasInstanceCalled, false); + assertEq(symbolGetterCalled, unwrap && isPrototypeOf); + } + + // Test with primitive values. + for (let thisValue of thisValues(Intl.NumberFormat).filter(IsPrimitive)) { + // Ensure @@hasInstance is not called. + Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { + value() { assertEq(true, false); }, configurable: true + }); + let isUndefinedOrNull = thisValue === undefined || thisValue === null; + let symbolHolder; + if (!isUndefinedOrNull) { + // Ensure the fallback symbol isn't retrieved from the primitive wrapper prototype. + symbolHolder = Object.getPrototypeOf(thisValue); + Object.defineProperty(symbolHolder, intlFallbackSymbol, { + get() { assertEq(true, false); }, configurable: true + }); + } + + assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError); + + delete Intl.NumberFormat[Symbol.hasInstance]; + if (!isUndefinedOrNull) + delete symbolHolder[intlFallbackSymbol]; + } +} + +// Test format() returns the correct result for objects initialized as Intl.NumberFormat instances. +{ + // An actual Intl.NumberFormat instance. + let numberFormat = new Intl.NumberFormat(); + + // An object initialized as a NumberFormat instance. + let thisValue = Object.create(Intl.NumberFormat.prototype); + Intl.NumberFormat.call(thisValue); + + // Object with [[FallbackSymbol]] set to NumberFormat instance. + let fakeObj = { + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: numberFormat, + }; + + for (let number of [0, 1, 1.5, Infinity, NaN]) { + let expected = numberFormat.format(number); + assertEq(thisValue.format(number), expected); + assertEq(thisValue[intlFallbackSymbol].format(number), expected); + assertEq(fakeObj.format(number), expected); + } +} + +// Ensure formatToParts() doesn't use the fallback semantics. +{ + let formatToParts = Intl.NumberFormat.prototype.formatToParts; + + // An object initialized as a NumberFormat instance. + let thisValue = Object.create(Intl.NumberFormat.prototype); + Intl.NumberFormat.call(thisValue); + assertThrowsInstanceOf(() => formatToParts.call(thisValue), TypeError); + + // Object with [[FallbackSymbol]] set to NumberFormat instance. + let fakeObj = { + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: new Intl.NumberFormat(), + }; + assertThrowsInstanceOf(() => formatToParts.call(fakeObj), TypeError); +} + +// Test resolvedOptions() returns the same results. +{ + // An actual Intl.NumberFormat instance. + let numberFormat = new Intl.NumberFormat(); + + // An object initialized as a NumberFormat instance. + let thisValue = Object.create(Intl.NumberFormat.prototype); + Intl.NumberFormat.call(thisValue); + + // Object with [[FallbackSymbol]] set to NumberFormat instance. + let fakeObj = { + __proto__: Intl.NumberFormat.prototype, + [intlFallbackSymbol]: numberFormat, + }; + + function assertEqOptions(actual, expected) { + actual = Object.entries(actual); + expected = Object.entries(expected); + + assertEq(actual.length, expected.length, "options count mismatch"); + for (var i = 0; i < expected.length; i++) { + assertEq(actual[i][0], expected[i][0], "key mismatch at " + i); + assertEq(actual[i][1], expected[i][1], "value mismatch at " + i); + } + } + + let expected = numberFormat.resolvedOptions(); + assertEqOptions(thisValue.resolvedOptions(), expected); + assertEqOptions(thisValue[intlFallbackSymbol].resolvedOptions(), expected); + assertEqOptions(fakeObj.resolvedOptions(), expected); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/use-grouping-bool-string.js b/js/src/tests/non262/Intl/NumberFormat/use-grouping-bool-string.js new file mode 100644 index 0000000000..4216c874f0 --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/use-grouping-bool-string.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +let nf1 = new Intl.NumberFormat("en", {useGrouping: "true"}); +assertEq(nf1.format(1000), "1,000"); + +let nf2 = new Intl.NumberFormat("en", {useGrouping: "false"}); +assertEq(nf2.format(1000), "1,000"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/NumberFormat/use-grouping.js b/js/src/tests/non262/Intl/NumberFormat/use-grouping.js new file mode 100644 index 0000000000..8fcb2fb51e --- /dev/null +++ b/js/src/tests/non262/Intl/NumberFormat/use-grouping.js @@ -0,0 +1,97 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta) + +const tests = { + // minimumGroupingDigits is one for "en" (English). + "en": { + values: [ + { + value: 1000, + useGroupings: { + auto: "1,000", + always: "1,000", + min2: "1000", + "": "1000", + }, + }, + { + value: 10000, + useGroupings: { + auto: "10,000", + always: "10,000", + min2: "10,000", + "": "10000", + }, + }, + ], + }, + + // minimumGroupingDigits is two for "pl" (Polish). + "pl": { + values: [ + { + value: 1000, + useGroupings: { + auto: "1000", + always: "1 000", + min2: "1000", + "": "1000", + }, + }, + { + value: 10000, + useGroupings: { + auto: "10 000", + always: "10 000", + min2: "10 000", + "": "10000", + }, + }, + ], + }, +}; + +for (let [locale, {options = {}, values}] of Object.entries(tests)) { + for (let {value, useGroupings} of values) { + for (let [useGrouping, expected] of Object.entries(useGroupings)) { + let nf = new Intl.NumberFormat(locale, {...options, useGrouping}); + assertEq(nf.format(value), expected, `locale=${locale}, value=${value}, useGrouping=${useGrouping}`); + } + } +} + +// Resolved options. +for (let [useGrouping, expected] of [ + [false, false], + ["", false], + [0, false], + [null, false], + + ["auto", "auto"], + [undefined, "auto"], + + ["always", "always"], + [true, "always"], + + ["min2", "min2"], + + // Unsupported values fallback to "auto" + ["true", "auto"], + ["false", "auto"], + ["none", "auto"], + ["yes", "auto"], + ["no", "auto"], + [{}, "auto"], + [123, "auto"], + [123n, "auto"], +]) { + let nf = new Intl.NumberFormat("en", {useGrouping}); + assertEq(nf.resolvedOptions().useGrouping , expected); +} + +// Throws a TypeError if ToString fails. +for (let useGrouping of [Object.create(null), Symbol()]) { + assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {useGrouping}), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/PluralRules/browser.js b/js/src/tests/non262/Intl/PluralRules/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/browser.js diff --git a/js/src/tests/non262/Intl/PluralRules/call.js b/js/src/tests/non262/Intl/PluralRules/call.js new file mode 100644 index 0000000000..9412fa44d0 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/call.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +function thisValues() { + const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + + return [ + // Primitive values. + ...[undefined, null, true, "abc", Symbol(), 123], + + // Object values. + ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], + + // Intl objects. + ...[].concat(...intlConstructors.map(ctor => { + let args = []; + if (ctor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without any arguments. + args = [undefined, {type: "language"}]; + } + + return [ + // Instance of an Intl constructor. + new ctor(...args), + + // Instance of a subclassed Intl constructor. + new class extends ctor {}(...args), + + // Object inheriting from an Intl constructor prototype. + Object.create(ctor.prototype), + + // Intl object not inheriting from its default prototype. + Object.setPrototypeOf(new ctor(...args), Object.prototype), + ]; + })), + ]; +} + +// Intl.PluralRules cannot be invoked as a function. +assertThrowsInstanceOf(() => Intl.PluralRules(), TypeError); + +// Also test with explicit this-value. +for (let thisValue of thisValues()) { + assertThrowsInstanceOf(() => Intl.PluralRules.call(thisValue), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/PluralRules/construct-newtarget.js b/js/src/tests/non262/Intl/PluralRules/construct-newtarget.js new file mode 100644 index 0000000000..356c2dd221 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/construct-newtarget.js @@ -0,0 +1,76 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test subclassing %Intl.PluralRules% works correctly. +class MyPluralRules extends Intl.PluralRules {} + +var obj = new MyPluralRules(); +assertEq(obj instanceof MyPluralRules, true); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), MyPluralRules.prototype); + +obj = Reflect.construct(MyPluralRules, []); +assertEq(obj instanceof MyPluralRules, true); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), MyPluralRules.prototype); + +obj = Reflect.construct(MyPluralRules, [], MyPluralRules); +assertEq(obj instanceof MyPluralRules, true); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), MyPluralRules.prototype); + +obj = Reflect.construct(MyPluralRules, [], Intl.PluralRules); +assertEq(obj instanceof MyPluralRules, false); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), Intl.PluralRules.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyPluralRules, [], Array); +assertEq(obj instanceof MyPluralRules, false); +assertEq(obj instanceof Intl.PluralRules, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.PluralRules, [], Array); +assertEq(obj instanceof Intl.PluralRules, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %PluralRulesPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.PluralRules, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), Intl.PluralRules.prototype); + +obj = Reflect.construct(MyPluralRules, [], NewTargetNullPrototype); +assertEq(obj instanceof MyPluralRules, false); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), Intl.PluralRules.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.PluralRules, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.PluralRules, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.PluralRules, true); +assertEq(Object.getPrototypeOf(obj), Intl.PluralRules.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/PluralRules/cross-compartment.js b/js/src/tests/non262/Intl/PluralRules/cross-compartment.js new file mode 100644 index 0000000000..ff59f35957 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/cross-compartment.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var otherGlobal = newGlobal(); + +var pluralRules = new Intl.PluralRules(); +var ccwPluralRules = new otherGlobal.Intl.PluralRules(); + +// Test Intl.PluralRules.prototype.select with a CCW object. +var Intl_PluralRules_select = Intl.PluralRules.prototype.select; + +assertEq(Intl_PluralRules_select.call(ccwPluralRules, 0), + Intl_PluralRules_select.call(pluralRules, 0)); + +// Test Intl.PluralRules.prototype.resolvedOptions with a CCW object. +var Intl_PluralRules_resolvedOptions = Intl.PluralRules.prototype.resolvedOptions; + +assertEq(deepEqual(Intl_PluralRules_resolvedOptions.call(ccwPluralRules), + Intl_PluralRules_resolvedOptions.call(pluralRules)), + true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/PluralRules/negativeZeroFractionDigits.js b/js/src/tests/non262/Intl/PluralRules/negativeZeroFractionDigits.js new file mode 100644 index 0000000000..615e533466 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/negativeZeroFractionDigits.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const optionsList = [ + {minimumFractionDigits: -0, maximumFractionDigits: -0}, + {minimumFractionDigits: -0, maximumFractionDigits: +0}, + {minimumFractionDigits: +0, maximumFractionDigits: -0}, + {minimumFractionDigits: +0, maximumFractionDigits: +0}, +]; + +for (let options of optionsList) { + let pluralRules = new Intl.PluralRules("en-US", options); + + let {minimumFractionDigits, maximumFractionDigits} = pluralRules.resolvedOptions(); + assertEq(minimumFractionDigits, +0); + assertEq(maximumFractionDigits, +0); + + assertEq(pluralRules.select(123), "other"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/PluralRules/pluralrules.js b/js/src/tests/non262/Intl/PluralRules/pluralrules.js new file mode 100644 index 0000000000..20d6117b3b --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/pluralrules.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Tests the format function with a diverse set of locales and options. + +var pr; + +pr = new Intl.PluralRules("en-us"); +assertEq(pr.resolvedOptions().locale, "en-US"); +assertEq(pr.resolvedOptions().type, "cardinal"); +assertEq(pr.resolvedOptions().pluralCategories.length, 2); + +pr = new Intl.PluralRules("de", {type: 'cardinal'}); +assertEq(pr.resolvedOptions().pluralCategories.length, 2); + +pr = new Intl.PluralRules("de", {type: 'ordinal'}); +assertEq(pr.resolvedOptions().pluralCategories.length, 1); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/PluralRules/resolvedOptions-overridden-species.js b/js/src/tests/non262/Intl/PluralRules/resolvedOptions-overridden-species.js new file mode 100644 index 0000000000..3591aba32b --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/resolvedOptions-overridden-species.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Tests the PluralRules.resolvedOptions function for overriden Array[Symbol.species]. + +var pl = new Intl.PluralRules("de"); + +Object.defineProperty(Array, Symbol.species, { + value: function() { + return new Proxy(["?"], { + get(t, pk, r) { + return Reflect.get(t, pk, r); + }, + defineProperty(t, pk) { + return true; + } + }); + } +}); + +var pluralCategories = pl.resolvedOptions().pluralCategories; + +assertEqArray(pluralCategories, ["one", "other"]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/PluralRules/rounding.js b/js/src/tests/non262/Intl/PluralRules/rounding.js new file mode 100644 index 0000000000..6f9b5f9936 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/rounding.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// The rounding mode defaults to half-up for both NumberFormat and PluralRules. + +var locale = "en"; +var options = {maximumFractionDigits: 0}; + +assertEq(new Intl.NumberFormat(locale, options).format(0), "0"); +assertEq(new Intl.NumberFormat(locale, options).format(0.5), "1"); +assertEq(new Intl.NumberFormat(locale, options).format(1), "1"); + +assertEq(new Intl.PluralRules(locale, options).select(0), "other"); +assertEq(new Intl.PluralRules(locale, options).select(0.5), "one"); +assertEq(new Intl.PluralRules(locale, options).select(1), "one"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/PluralRules/select.js b/js/src/tests/non262/Intl/PluralRules/select.js new file mode 100644 index 0000000000..dcc057ec3c --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/select.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +// Tests the format function with a diverse set of locales and options. + +var pr; + +pr = new Intl.PluralRules("en-us"); +assertEq(pr.select(0), "other"); +assertEq(pr.select(0.5), "other"); +assertEq(pr.select(1.2), "other"); +assertEq(pr.select(1.5), "other"); +assertEq(pr.select(1.7), "other"); +assertEq(pr.select(-1), "one"); +assertEq(pr.select(1), "one"); +assertEq(pr.select("1"), "one"); +assertEq(pr.select(123456789.123456789), "other"); + +pr = new Intl.PluralRules("de", {type: "cardinal"}); +assertEq(pr.select(0), "other"); +assertEq(pr.select(0.5), "other"); +assertEq(pr.select(1.2), "other"); +assertEq(pr.select(1.5), "other"); +assertEq(pr.select(1.7), "other"); +assertEq(pr.select(-1), "one"); + +pr = new Intl.PluralRules("de", {type: "ordinal"}); +assertEq(pr.select(0), "other"); +assertEq(pr.select(0.5), "other"); +assertEq(pr.select(1.2), "other"); +assertEq(pr.select(1.5), "other"); +assertEq(pr.select(1.7), "other"); +assertEq(pr.select(-1), "other"); + +pr = new Intl.PluralRules("pl", {type: "cardinal"}); +assertEq(pr.select(0), "many"); +assertEq(pr.select(0.5), "other"); +assertEq(pr.select(1), "one"); + +pr = new Intl.PluralRules("pl", {type: "cardinal", maximumFractionDigits: 0}); +assertEq(pr.select(1.1), "one"); + +pr = new Intl.PluralRules("pl", {type: "cardinal", maximumFractionDigits: 1}); +assertEq(pr.select(1.1), "other"); + +pr = new Intl.PluralRules("en", {type: "cardinal", minimumFractionDigits: 0}); +assertEq(pr.select(1), "one"); + +pr = new Intl.PluralRules("en", {type: "cardinal", minimumFractionDigits: 2}); +assertEq(pr.select(1), "other"); + +var weirdCases = [ + NaN, + Infinity, + "word", + [0,2], + {}, +]; + +for (let c of weirdCases) { + assertEq(pr.select(c), "other"); +}; + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/PluralRules/selectRange.js b/js/src/tests/non262/Intl/PluralRules/selectRange.js new file mode 100644 index 0000000000..59faf621e2 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/selectRange.js @@ -0,0 +1,84 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||release_or_beta) + +// Any combination returns "other" for "en-US". +{ + let numbers = [0, 0.5, 1.2, 1.5, 1.7, -1, 1, "1", 123456789.123456789, Infinity, -Infinity]; + + const weirdCases = [ + NaN, + "word", + [0, 2], + {}, + ]; + + for (let type of ["ordinal", "cardinal"]) { + let pr = new Intl.PluralRules("en-US", {type}); + for (let start of numbers) { + for (let end of numbers) { + assertEq(pr.selectRange(start, end), "other"); + } + } + + for (let c of weirdCases) { + assertThrowsInstanceOf(() => pr.selectRange(c, 0), RangeError); + assertThrowsInstanceOf(() => pr.selectRange(0, c), RangeError); + assertThrowsInstanceOf(() => pr.selectRange(c, c), RangeError); + } + } +} + +// fr (French) returns different results. +{ + let ordinal = new Intl.PluralRules("fr", {type: "ordinal"}); + let cardinal = new Intl.PluralRules("fr", {type: "cardinal"}); + + assertEq(ordinal.selectRange(1, 1), "one"); + assertEq(ordinal.selectRange(0, 1), "other"); + + assertEq(cardinal.selectRange(1, 1), "one"); + assertEq(cardinal.selectRange(0, 1), "one"); +} + +// cy (Cymraeg) can return any combination. +{ + let ordinal = new Intl.PluralRules("cy", {type: "ordinal"}); + + assertEq(ordinal.selectRange(0, 0), "other"); + assertEq(ordinal.selectRange(0, 1), "one"); + assertEq(ordinal.selectRange(0, 2), "two"); + assertEq(ordinal.selectRange(0, 3), "few"); + assertEq(ordinal.selectRange(0, 5), "many"); + assertEq(ordinal.selectRange(0, 10), "other"); + + assertEq(ordinal.selectRange(1, 1), "other"); + assertEq(ordinal.selectRange(1, 2), "two"); + assertEq(ordinal.selectRange(1, 3), "few"); + assertEq(ordinal.selectRange(1, 5), "many"); + assertEq(ordinal.selectRange(1, 10), "other"); + + assertEq(ordinal.selectRange(2, 2), "other"); + assertEq(ordinal.selectRange(2, 3), "few"); + assertEq(ordinal.selectRange(2, 5), "many"); + assertEq(ordinal.selectRange(2, 10), "other"); + + assertEq(ordinal.selectRange(3, 3), "other"); + assertEq(ordinal.selectRange(3, 5), "many"); + assertEq(ordinal.selectRange(3, 10), "other"); + + assertEq(ordinal.selectRange(5, 5), "other"); + assertEq(ordinal.selectRange(5, 10), "other"); + + assertEq(ordinal.selectRange(10, 10), "other"); +} + +// BigInt inputs aren't allowed. +{ + let pr = new Intl.PluralRules("en-US"); + + assertThrowsInstanceOf(() => pr.selectRange(0, 0n), TypeError); + assertThrowsInstanceOf(() => pr.selectRange(0n, 0), TypeError); + assertThrowsInstanceOf(() => pr.selectRange(0n, 0n), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/PluralRules/shell.js b/js/src/tests/non262/Intl/PluralRules/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/shell.js diff --git a/js/src/tests/non262/Intl/PluralRules/supportedLocalesOf.js b/js/src/tests/non262/Intl/PluralRules/supportedLocalesOf.js new file mode 100644 index 0000000000..7c6ebe8c45 --- /dev/null +++ b/js/src/tests/non262/Intl/PluralRules/supportedLocalesOf.js @@ -0,0 +1,369 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||xulRuntime.shell) +// -- test in browser only that ICU has locale data for all Mozilla languages + +// This array contains the locales that ICU supports in +// number formatting whose languages Mozilla localizes Firefox into. +// Current as of ICU 50.1.2 and Firefox March 2013. +var locales = [ + "af", + "af-NA", + "af-ZA", + "ar", + "ar-001", + "ar-AE", + "ar-BH", + "ar-DJ", + "ar-DZ", + "ar-EG", + "ar-EH", + "ar-ER", + "ar-IL", + "ar-IQ", + "ar-JO", + "ar-KM", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-MR", + "ar-OM", + "ar-PS", + "ar-QA", + "ar-SA", + "ar-SD", + "ar-SO", + "ar-SY", + "ar-TD", + "ar-TN", + "ar-YE", + "as", + "as-IN", + "be", + "be-BY", + "bg", + "bg-BG", + "bn", + "bn-BD", + "bn-IN", + "br", + "br-FR", + "bs", + "bs-Cyrl", + "bs-Cyrl-BA", + "bs-Latn", + "bs-Latn-BA", + "ca", + "ca-AD", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-BE", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "el", + "el-CY", + "el-GR", + "en", + "en-150", + "en-AG", + "en-AS", + "en-AU", + "en-BB", + "en-BE", + "en-BM", + "en-BS", + "en-BW", + "en-BZ", + "en-CA", + "en-CM", + "en-DM", + "en-FJ", + "en-FM", + "en-GB", + "en-GD", + "en-GG", + "en-GH", + "en-GI", + "en-GM", + "en-GU", + "en-GY", + "en-HK", + "en-IE", + "en-IM", + "en-IN", + "en-JE", + "en-JM", + "en-KE", + "en-KI", + "en-KN", + "en-KY", + "en-LC", + "en-LR", + "en-LS", + "en-MG", + "en-MH", + "en-MP", + "en-MT", + "en-MU", + "en-MW", + "en-NA", + "en-NG", + "en-NZ", + "en-PG", + "en-PH", + "en-PK", + "en-PR", + "en-PW", + "en-SB", + "en-SC", + "en-SG", + "en-SL", + "en-SS", + "en-SZ", + "en-TC", + "en-TO", + "en-TT", + "en-TZ", + "en-UG", + "en-UM", + "en-US", + "en-US-posix", + "en-VC", + "en-VG", + "en-VI", + "en-VU", + "en-WS", + "en-ZA", + "en-ZM", + "en-ZW", + "eo", + "es", + "es-419", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-CU", + "es-DO", + "es-EA", + "es-EC", + "es-ES", + "es-GQ", + "es-GT", + "es-HN", + "es-IC", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PH", + "es-PR", + "es-PY", + "es-SV", + "es-US", + "es-UY", + "es-VE", + "et", + "et-EE", + "eu", + "eu-ES", + "fa", + "fa-AF", + "fa-IR", + "ff", + "ff-SN", + "fi", + "fi-FI", + "fr", + "fr-BE", + "fr-BF", + "fr-BI", + "fr-BJ", + "fr-BL", + "fr-CA", + "fr-CD", + "fr-CF", + "fr-CG", + "fr-CH", + "fr-CI", + "fr-CM", + "fr-DJ", + "fr-DZ", + "fr-FR", + "fr-GA", + "fr-GF", + "fr-GN", + "fr-GP", + "fr-GQ", + "fr-HT", + "fr-KM", + "fr-LU", + "fr-MA", + "fr-MC", + "fr-MF", + "fr-MG", + "fr-ML", + "fr-MQ", + "fr-MR", + "fr-MU", + "fr-NC", + "fr-NE", + "fr-PF", + "fr-RE", + "fr-RW", + "fr-SC", + "fr-SN", + "fr-SY", + "fr-TD", + "fr-TG", + "fr-TN", + "fr-VU", + "fr-YT", + "ga", + "ga-IE", + "gl", + "gl-ES", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "it-SM", + "ja", + "ja-JP", + "kk", + "kk-Cyrl", + "kk-Cyrl-KZ", + "km", + "km-KH", + "kn", + "kn-IN", + "ko", + "ko-KP", + "ko-KR", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mk", + "mk-MK", + "ml", + "ml-IN", + "mr", + "mr-IN", + "nb", + "nb-NO", + "nl", + "nl-AW", + "nl-BE", + "nl-CW", + "nl-NL", + "nl-SR", + "nl-SX", + "nn", + "nn-NO", + "or", + "or-IN", + "pa", + "pa-Arab", + "pa-Arab-PK", + "pa-Guru", + "pa-Guru-IN", + "pl", + "pl-PL", + "pt", + "pt-AO", + "pt-BR", + "pt-CV", + "pt-GW", + "pt-MO", + "pt-MZ", + "pt-PT", + "pt-ST", + "pt-TL", + "rm", + "rm-CH", + "ro", + "ro-MD", + "ro-RO", + "ru", + "ru-BY", + "ru-KG", + "ru-KZ", + "ru-MD", + "ru-RU", + "ru-UA", + "si", + "si-LK", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sq-MK", + "sr", + "sr-Cyrl", + "sr-Cyrl-BA", + "sr-Cyrl-ME", + "sr-Cyrl-RS", + "sr-Latn", + "sr-Latn-BA", + "sr-Latn-ME", + "sr-Latn-RS", + "sv", + "sv-AX", + "sv-FI", + "sv-SE", + "te", + "te-IN", + "th", + "th-TH", + "tr", + "tr-CY", + "tr-TR", + "uk", + "uk-UA", + "vi", + "vi-VN", + "zh", + "zh-Hans", + "zh-Hans-CN", + "zh-Hans-HK", + "zh-Hans-MO", + "zh-Hans-SG", + "zh-Hant", + "zh-Hant-HK", + "zh-Hant-MO", + "zh-Hant-TW", +]; + +const result = Intl.PluralRules.supportedLocalesOf(locales); + +assertEqArray(locales, result); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/README.txt b/js/src/tests/non262/Intl/README.txt new file mode 100644 index 0000000000..797b49ab5c --- /dev/null +++ b/js/src/tests/non262/Intl/README.txt @@ -0,0 +1,27 @@ +Integration Tests for ECMAScript Internationalization API +========================================================= + +The tests in this directory test the integration of the ICU library +(Internationalization Components for Unicode) into the implementation of the +ECMAScript Internationalization API in SpiderMonkey. + +These integration tests are complementary to: + +- The Test402 test suite maintained by Ecma TC39, which tests conformance of + an implementation to standard ECMA-402, ECMAScript Internationalization API + Specification. Test402 is currently maintained as part of Test262, the overall + conformance test suite for ECMAScript; for more information, see + http://wiki.ecmascript.org/doku.php?id=test262:test262 + +- The test suite of the ICU library, which tests the implementation of ICU + itself and correct interpretation of the locale data it obtains from CLDR + (Common Locale Data Repository). For information on ICU, see + http://site.icu-project.org + +The integration tests check for a variety of locales and options whether the +results are localized in a way that indicates correct integration with ICU. +Such tests are somewhat fragile because the underlying locale data reflects +real world usage and is therefore subject to change. When the ICU library used +by Mozilla is upgraded, it is likely that some of the integration tests will +fail because of locale data changes; however, others might fail because of +actual software bugs. Failures therefore have to be examined carefully. diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/browser.js b/js/src/tests/non262/Intl/RelativeTimeFormat/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/browser.js diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/construct-newtarget.js b/js/src/tests/non262/Intl/RelativeTimeFormat/construct-newtarget.js new file mode 100644 index 0000000000..c20c07434e --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/construct-newtarget.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var obj = new Intl.RelativeTimeFormat(); + +// Test that new RTF produces an object with the right prototype. +assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype); + +// Test subclassing %Intl.RelativeTimeFormat% works correctly. +class MyRelativeTimeFormat extends Intl.RelativeTimeFormat {} + +var obj = new MyRelativeTimeFormat(); +assertEq(obj instanceof MyRelativeTimeFormat, true); +assertEq(obj instanceof Intl.RelativeTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyRelativeTimeFormat.prototype); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/cross-compartment.js b/js/src/tests/non262/Intl/RelativeTimeFormat/cross-compartment.js new file mode 100644 index 0000000000..c826538f39 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/cross-compartment.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +var otherGlobal = newGlobal(); + +var relativeTimeFormat = new Intl.RelativeTimeFormat(); +var ccwRelativeTimeFormat = new otherGlobal.Intl.RelativeTimeFormat(); + +// Test Intl.RelativeTimeFormat.prototype.format with a CCW object. +var Intl_RelativeTimeFormat_format = Intl.RelativeTimeFormat.prototype.format; + +assertEq(Intl_RelativeTimeFormat_format.call(ccwRelativeTimeFormat, 0, "hour"), + Intl_RelativeTimeFormat_format.call(relativeTimeFormat, 0, "hour")); + +// Test Intl.RelativeTimeFormat.prototype.resolvedOptions with a CCW object. +var Intl_RelativeTimeFormat_resolvedOptions = Intl.RelativeTimeFormat.prototype.resolvedOptions; + +assertEq(deepEqual(Intl_RelativeTimeFormat_resolvedOptions.call(ccwRelativeTimeFormat), + Intl_RelativeTimeFormat_resolvedOptions.call(relativeTimeFormat)), + true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/format.js b/js/src/tests/non262/Intl/RelativeTimeFormat/format.js new file mode 100644 index 0000000000..244dbb5c56 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/format.js @@ -0,0 +1,145 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the format function with a diverse set of locales and options. + +var rtf; + +{ + // Numeric format + rtf = new Intl.RelativeTimeFormat("en-US"); + assertEq(rtf.format(0, "second"), "in 0 seconds"); + assertEq(rtf.format(-0, "second"), "0 seconds ago"); + assertEq(rtf.format(-1, "second"), "1 second ago"); + assertEq(rtf.format(1, "second"), "in 1 second"); + + assertEq(rtf.format(0, "minute"), "in 0 minutes"); + assertEq(rtf.format(-0, "minute"), "0 minutes ago"); + assertEq(rtf.format(-1, "minute"), "1 minute ago"); + assertEq(rtf.format(1, "minute"), "in 1 minute"); + + assertEq(rtf.format(0, "hour"), "in 0 hours"); + assertEq(rtf.format(-0, "hour"), "0 hours ago"); + assertEq(rtf.format(-1, "hour"), "1 hour ago"); + assertEq(rtf.format(1, "hour"), "in 1 hour"); + + assertEq(rtf.format(0, "day"), "in 0 days"); + assertEq(rtf.format(-0, "day"), "0 days ago"); + assertEq(rtf.format(-1, "day"), "1 day ago"); + assertEq(rtf.format(1, "day"), "in 1 day"); + + assertEq(rtf.format(0, "week"), "in 0 weeks"); + assertEq(rtf.format(-0, "week"), "0 weeks ago"); + assertEq(rtf.format(-1, "week"), "1 week ago"); + assertEq(rtf.format(1, "week"), "in 1 week"); + + assertEq(rtf.format(0, "month"), "in 0 months"); + assertEq(rtf.format(-0, "month"), "0 months ago"); + assertEq(rtf.format(-1, "month"), "1 month ago"); + assertEq(rtf.format(1, "month"), "in 1 month"); + + assertEq(rtf.format(0, "year"), "in 0 years"); + assertEq(rtf.format(-0, "year"), "0 years ago"); + assertEq(rtf.format(-1, "year"), "1 year ago"); + assertEq(rtf.format(1, "year"), "in 1 year"); +} + +{ + // Text format + rtf = new Intl.RelativeTimeFormat("en-US", { + numeric: "auto" + }); + assertEq(rtf.format(0, "second"), "now"); + assertEq(rtf.format(-0, "second"), "now"); + assertEq(rtf.format(-1, "second"), "1 second ago"); + assertEq(rtf.format(1, "second"), "in 1 second"); + + assertEq(rtf.format(0, "minute"), "this minute"); + assertEq(rtf.format(-0, "minute"), "this minute"); + assertEq(rtf.format(-1, "minute"), "1 minute ago"); + assertEq(rtf.format(1, "minute"), "in 1 minute"); + + assertEq(rtf.format(0, "hour"), "this hour"); + assertEq(rtf.format(-0, "hour"), "this hour"); + assertEq(rtf.format(-1, "hour"), "1 hour ago"); + assertEq(rtf.format(1, "hour"), "in 1 hour"); + + assertEq(rtf.format(0, "day"), "today"); + assertEq(rtf.format(-0, "day"), "today"); + assertEq(rtf.format(-1, "day"), "yesterday"); + assertEq(rtf.format(1, "day"), "tomorrow"); + + assertEq(rtf.format(0, "week"), "this week"); + assertEq(rtf.format(-0, "week"), "this week"); + assertEq(rtf.format(-1, "week"), "last week"); + assertEq(rtf.format(1, "week"), "next week"); + + assertEq(rtf.format(0, "month"), "this month"); + assertEq(rtf.format(-0, "month"), "this month"); + assertEq(rtf.format(-1, "month"), "last month"); + assertEq(rtf.format(1, "month"), "next month"); + + assertEq(rtf.format(0, "year"), "this year"); + assertEq(rtf.format(-0, "year"), "this year"); + assertEq(rtf.format(-1, "year"), "last year"); + assertEq(rtf.format(1, "year"), "next year"); +} + +{ + // Plural specifier + rtf = new Intl.RelativeTimeFormat("en-US"); + assertEq(rtf.format(1, "seconds"), "in 1 second"); + assertEq(rtf.format(1, "minutes"), "in 1 minute"); + assertEq(rtf.format(1, "hours"), "in 1 hour"); + assertEq(rtf.format(1, "days"), "in 1 day"); + assertEq(rtf.format(1, "weeks"), "in 1 week"); + assertEq(rtf.format(1, "months"), "in 1 month"); + assertEq(rtf.format(1, "years"), "in 1 year"); +} + +rtf = new Intl.RelativeTimeFormat("de", {numeric: "auto"}); +assertEq(rtf.format(-1, "day"), "gestern"); +assertEq(rtf.format(1, "day"), "morgen"); + +rtf = new Intl.RelativeTimeFormat("ar", {numeric: "auto"}); +assertEq(rtf.format(-1, "day"), "أمس"); +assertEq(rtf.format(1, "day"), "غدًا"); + + +rtf = new Intl.RelativeTimeFormat("en-US"); + +var weirdValueCases = [ + Infinity, + -Infinity, + NaN, + "word", + [0,2], + {}, +]; + +for (let c of weirdValueCases) { + assertThrowsInstanceOf(() => rtf.format(c, "year"), RangeError); +}; + +var weirdUnitCases = [ + "test", + "SECOND", + "sEcOnD", + 1, + NaN, + undefined, + null, + {}, +]; + +for (let u of weirdUnitCases) { + assertThrowsInstanceOf(function() { + var rtf = new Intl.RelativeTimeFormat("en-US"); + rtf.format(1, u); + }, RangeError); +}; + + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/locale-fallback-handling.js b/js/src/tests/non262/Intl/RelativeTimeFormat/locale-fallback-handling.js new file mode 100644 index 0000000000..76917c6fa0 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/locale-fallback-handling.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// In locales that don't have a relative-date/time formatter -- and presently +// "ak" is such a locale -- behavior is expected to fall back to the root-locale +// formatter. This test verifies such fallback works as long as "ak" satisfies +// these properties; "ak" may safely be changed to a different locale if that +// ever changes. See bug 1504656. +assertEq(new Intl.RelativeTimeFormat("ak").format(1, "second"), + "+1 s"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/numbering-system.js b/js/src/tests/non262/Intl/RelativeTimeFormat/numbering-system.js new file mode 100644 index 0000000000..d9a860bb01 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/numbering-system.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure passing the default numbering system leads to the same result as when +// no explicit numbering system is present. +// +// This is a regression test for the ICU issue reported at +// <https://unicode-org.atlassian.net/browse/ICU-20280>. + +for (var requestedLocale of [undefined, "en", "de", "fr"]) { + var rtf = new Intl.RelativeTimeFormat(requestedLocale); + var {locale, numberingSystem} = rtf.resolvedOptions(); + var rtfNu = new Intl.RelativeTimeFormat(`${locale}-u-nu-${numberingSystem}`); + + for (var unit of ["year", "quarter", "month", "week", "day", "hour", "minute", "second"]) { + for (var value of [-10, -3, -2, -1, 0, 1, 2, 3, 10]) { + assertEq(rtfNu.format(value, unit), rtf.format(value, unit)); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/numberingSystem-option.js b/js/src/tests/non262/Intl/RelativeTimeFormat/numberingSystem-option.js new file mode 100644 index 0000000000..bbde290dfa --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/numberingSystem-option.js @@ -0,0 +1,60 @@ +const defaultLocale = "en"; +const defaultNumberingSystem = new Intl.RelativeTimeFormat(defaultLocale).resolvedOptions().numberingSystem; + +function createWithLocale(locale, numberingSystem) { + return new Intl.RelativeTimeFormat(locale, {numberingSystem}); +} + +function create(numberingSystem) { + return createWithLocale(defaultLocale, numberingSystem); +} + +// Empty string should throw. +assertThrowsInstanceOf(() => create(""), RangeError); + +// Trailing \0 should throw. +assertThrowsInstanceOf(() => create("latn\0"), RangeError); + +// Too short or too long strings should throw. +assertThrowsInstanceOf(() => create("a"), RangeError); +assertThrowsInstanceOf(() => create("toolongstring"), RangeError); + +// Throw even when prefix is valid. +assertThrowsInstanceOf(() => create("latn-toolongstring"), RangeError); + +// |numberingSystem| can be set to |undefined|. +let nf = create(undefined); +assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Unsupported numbering systems are ignored. +nf = create("xxxxxxxx"); +assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem); + +// Numbering system in options overwrite Unicode extension keyword. +nf = createWithLocale(`${defaultLocale}-u-nu-thai`, "arab"); +assertEq(nf.resolvedOptions().locale, defaultLocale); +assertEq(nf.resolvedOptions().numberingSystem, "arab"); + +// |numberingSystem| option ignores case. +nf = create("ARAB"); +assertEq(nf.resolvedOptions().locale, defaultLocale); +assertEq(nf.resolvedOptions().numberingSystem, "arab"); + +for (let [numberingSystem, {algorithmic}] of Object.entries(numberingSystems)) { + let nf1 = new Intl.RelativeTimeFormat(`${defaultLocale}-u-nu-${numberingSystem}`); + let nf2 = new Intl.RelativeTimeFormat(defaultLocale, {numberingSystem}); + + if (!algorithmic) { + assertEq(nf1.resolvedOptions().numberingSystem, numberingSystem); + assertEq(nf2.resolvedOptions().numberingSystem, numberingSystem); + } else { + // We don't yet support algorithmic numbering systems. + assertEq(nf1.resolvedOptions().numberingSystem, defaultNumberingSystem); + assertEq(nf2.resolvedOptions().numberingSystem, defaultNumberingSystem); + } + + assertEq(nf2.format(0, "second"), nf1.format(0, "second")); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/relativetimeformat.js b/js/src/tests/non262/Intl/RelativeTimeFormat/relativetimeformat.js new file mode 100644 index 0000000000..fb30e5a9ce --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/relativetimeformat.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the format function with a diverse set of locales and options. + +var rtf; + +rtf = new Intl.RelativeTimeFormat("en-us"); +assertEq(rtf.resolvedOptions().locale, "en-US"); +assertEq(rtf.resolvedOptions().style, "long"); +assertEq(rtf.resolvedOptions().numeric, "always"); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/shell.js b/js/src/tests/non262/Intl/RelativeTimeFormat/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/shell.js diff --git a/js/src/tests/non262/Intl/RelativeTimeFormat/supportedLocalesOf.js b/js/src/tests/non262/Intl/RelativeTimeFormat/supportedLocalesOf.js new file mode 100644 index 0000000000..214b12570e --- /dev/null +++ b/js/src/tests/non262/Intl/RelativeTimeFormat/supportedLocalesOf.js @@ -0,0 +1,373 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||xulRuntime.shell) +// -- test in browser only that ICU has locale data for all Mozilla languages + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This array contains the locales that ICU supports in +// number formatting whose languages Mozilla localizes Firefox into. +// Current as of ICU 50.1.2 and Firefox March 2013. +var locales = [ + "af", + "af-NA", + "af-ZA", + "ar", + "ar-001", + "ar-AE", + "ar-BH", + "ar-DJ", + "ar-DZ", + "ar-EG", + "ar-EH", + "ar-ER", + "ar-IL", + "ar-IQ", + "ar-JO", + "ar-KM", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-MR", + "ar-OM", + "ar-PS", + "ar-QA", + "ar-SA", + "ar-SD", + "ar-SO", + "ar-SY", + "ar-TD", + "ar-TN", + "ar-YE", + "as", + "as-IN", + "be", + "be-BY", + "bg", + "bg-BG", + "bn", + "bn-BD", + "bn-IN", + "br", + "br-FR", + "bs", + "bs-Cyrl", + "bs-Cyrl-BA", + "bs-Latn", + "bs-Latn-BA", + "ca", + "ca-AD", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-BE", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "el", + "el-CY", + "el-GR", + "en", + "en-150", + "en-AG", + "en-AS", + "en-AU", + "en-BB", + "en-BE", + "en-BM", + "en-BS", + "en-BW", + "en-BZ", + "en-CA", + "en-CM", + "en-DM", + "en-FJ", + "en-FM", + "en-GB", + "en-GD", + "en-GG", + "en-GH", + "en-GI", + "en-GM", + "en-GU", + "en-GY", + "en-HK", + "en-IE", + "en-IM", + "en-IN", + "en-JE", + "en-JM", + "en-KE", + "en-KI", + "en-KN", + "en-KY", + "en-LC", + "en-LR", + "en-LS", + "en-MG", + "en-MH", + "en-MP", + "en-MT", + "en-MU", + "en-MW", + "en-NA", + "en-NG", + "en-NZ", + "en-PG", + "en-PH", + "en-PK", + "en-PR", + "en-PW", + "en-SB", + "en-SC", + "en-SG", + "en-SL", + "en-SS", + "en-SZ", + "en-TC", + "en-TO", + "en-TT", + "en-TZ", + "en-UG", + "en-UM", + "en-US", + "en-US-posix", + "en-VC", + "en-VG", + "en-VI", + "en-VU", + "en-WS", + "en-ZA", + "en-ZM", + "en-ZW", + "eo", + "es", + "es-419", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-CU", + "es-DO", + "es-EA", + "es-EC", + "es-ES", + "es-GQ", + "es-GT", + "es-HN", + "es-IC", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PH", + "es-PR", + "es-PY", + "es-SV", + "es-US", + "es-UY", + "es-VE", + "et", + "et-EE", + "eu", + "eu-ES", + "fa", + "fa-AF", + "fa-IR", + "ff", + "ff-SN", + "fi", + "fi-FI", + "fr", + "fr-BE", + "fr-BF", + "fr-BI", + "fr-BJ", + "fr-BL", + "fr-CA", + "fr-CD", + "fr-CF", + "fr-CG", + "fr-CH", + "fr-CI", + "fr-CM", + "fr-DJ", + "fr-DZ", + "fr-FR", + "fr-GA", + "fr-GF", + "fr-GN", + "fr-GP", + "fr-GQ", + "fr-HT", + "fr-KM", + "fr-LU", + "fr-MA", + "fr-MC", + "fr-MF", + "fr-MG", + "fr-ML", + "fr-MQ", + "fr-MR", + "fr-MU", + "fr-NC", + "fr-NE", + "fr-PF", + "fr-RE", + "fr-RW", + "fr-SC", + "fr-SN", + "fr-SY", + "fr-TD", + "fr-TG", + "fr-TN", + "fr-VU", + "fr-YT", + "ga", + "ga-IE", + "gl", + "gl-ES", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "it-SM", + "ja", + "ja-JP", + "kk", + "kk-Cyrl", + "kk-Cyrl-KZ", + "km", + "km-KH", + "kn", + "kn-IN", + "ko", + "ko-KP", + "ko-KR", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mk", + "mk-MK", + "ml", + "ml-IN", + "mr", + "mr-IN", + "nb", + "nb-NO", + "nl", + "nl-AW", + "nl-BE", + "nl-CW", + "nl-NL", + "nl-SR", + "nl-SX", + "nn", + "nn-NO", + "or", + "or-IN", + "pa", + "pa-Arab", + "pa-Arab-PK", + "pa-Guru", + "pa-Guru-IN", + "pl", + "pl-PL", + "pt", + "pt-AO", + "pt-BR", + "pt-CV", + "pt-GW", + "pt-MO", + "pt-MZ", + "pt-PT", + "pt-ST", + "pt-TL", + "rm", + "rm-CH", + "ro", + "ro-MD", + "ro-RO", + "ru", + "ru-BY", + "ru-KG", + "ru-KZ", + "ru-MD", + "ru-RU", + "ru-UA", + "si", + "si-LK", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sq-MK", + "sr", + "sr-Cyrl", + "sr-Cyrl-BA", + "sr-Cyrl-ME", + "sr-Cyrl-RS", + "sr-Latn", + "sr-Latn-BA", + "sr-Latn-ME", + "sr-Latn-RS", + "sv", + "sv-AX", + "sv-FI", + "sv-SE", + "te", + "te-IN", + "th", + "th-TH", + "tr", + "tr-CY", + "tr-TR", + "uk", + "uk-UA", + "vi", + "vi-VN", + "zh", + "zh-Hans", + "zh-Hans-CN", + "zh-Hans-HK", + "zh-Hans-MO", + "zh-Hans-SG", + "zh-Hant", + "zh-Hant-HK", + "zh-Hant-MO", + "zh-Hant-TW", +]; + +const result = Intl.RelativeTimeFormat.supportedLocalesOf(locales); + +assertEqArray(locales, result); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/non262/Intl/String/shell.js b/js/src/tests/non262/Intl/String/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/String/shell.js diff --git a/js/src/tests/non262/Intl/String/toLocaleLowerCase.js b/js/src/tests/non262/Intl/String/toLocaleLowerCase.js new file mode 100644 index 0000000000..bd81717d22 --- /dev/null +++ b/js/src/tests/non262/Intl/String/toLocaleLowerCase.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test language dependent special casing with different language tags. +for (let locale of ["tr", "TR", "tr-TR", "tr-u-co-search", "tr-x-turkish"]) { + assertEq("\u0130".toLocaleLowerCase(locale), "i"); + assertEq("\u0130".toLocaleLowerCase([locale]), "i"); + + // Additional language tags are ignored. + assertEq("\u0130".toLocaleLowerCase([locale, "und"]), "i"); + assertEq("\u0130".toLocaleLowerCase(["und", locale]), "\u0069\u0307"); +} + +// Ensure "trl" (Traveller Scottish) isn't misrecognized as "tr", even though +// both share the same prefix. +assertEq("\u0130".toLocaleLowerCase("trl"), "\u0069\u0307"); +assertEq("\u0130".toLocaleLowerCase(["trl"]), "\u0069\u0307"); + +// Language tag is always verified. +for (let locale of ["no_locale", "tr-invalid_ext", ["no_locale"], ["en", "no_locale"]]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleLowerCase(locale), RangeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "x".toLocaleLowerCase(locale), RangeError); +} + +// No locale argument, undefined as locale, and empty array or array-like all +// return the same result. Testing with "a/A" because it has only simple case +// mappings. +assertEq("A".toLocaleLowerCase(), "a"); +assertEq("A".toLocaleLowerCase(undefined), "a"); +assertEq("A".toLocaleLowerCase([]), "a"); +assertEq("A".toLocaleLowerCase({}), "a"); +assertEq("A".toLocaleLowerCase({length: 0}), "a"); +assertEq("A".toLocaleLowerCase({length: -1}), "a"); + +// Test with incorrect locale type. +for (let locale of [null, 0, Math.PI, NaN, Infinity, true, false, Symbol()]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleLowerCase([locale]), TypeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "A".toLocaleLowerCase([locale]), TypeError); +} + +// Primitives are converted with ToObject and then queried for .length property. +for (let locale of [null]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleLowerCase([locale]), TypeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "A".toLocaleLowerCase([locale]), TypeError); +} +// ToLength(ToObject(<primitive>)) returns 0. +for (let locale of [0, Math.PI, NaN, Infinity, true, false, Symbol()]) { + // Empty input string. + assertEq("".toLocaleLowerCase(locale), ""); + + // Non-empty input string. + assertEq("A".toLocaleLowerCase(locale), "a"); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/String/toLocaleUpperCase.js b/js/src/tests/non262/Intl/String/toLocaleUpperCase.js new file mode 100644 index 0000000000..0a33320dc7 --- /dev/null +++ b/js/src/tests/non262/Intl/String/toLocaleUpperCase.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Test language dependent special casing with different language tags. +for (let locale of ["lt", "LT", "lt-LT", "lt-u-co-phonebk", "lt-x-lietuva"]) { + assertEq("i\u0307".toLocaleUpperCase(locale), "I"); + assertEq("i\u0307".toLocaleUpperCase([locale]), "I"); + + // Additional language tags are ignored. + assertEq("i\u0307".toLocaleUpperCase([locale, "und"]), "I"); + assertEq("i\u0307".toLocaleUpperCase(["und", locale]), "I\u0307"); +} + +// Ensure "lti" (Leti) isn't misrecognized as "lt", even though both share the +// same prefix. +assertEq("i\u0307".toLocaleUpperCase("lti"), "I\u0307"); +assertEq("i\u0307".toLocaleUpperCase(["lti"]), "I\u0307"); + +// Language tag is always verified. +for (let locale of ["no_locale", "lt-invalid_ext", ["no_locale"], ["en", "no_locale"]]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleUpperCase(locale), RangeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "a".toLocaleUpperCase(locale), RangeError); +} + +// No locale argument, undefined as locale, and empty array or array-like all +// return the same result. Testing with "a/A" because it has only simple case +// mappings. +assertEq("a".toLocaleUpperCase(), "A"); +assertEq("a".toLocaleUpperCase(undefined), "A"); +assertEq("a".toLocaleUpperCase([]), "A"); +assertEq("a".toLocaleUpperCase({}), "A"); +assertEq("a".toLocaleUpperCase({length: 0}), "A"); +assertEq("a".toLocaleUpperCase({length: -1}), "A"); + +// Test with incorrect locale type. +for (let locale of [null, 0, Math.PI, NaN, Infinity, true, false, Symbol()]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleUpperCase([locale]), TypeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "a".toLocaleUpperCase([locale]), TypeError); +} + +// Primitives are converted with ToObject and then queried for .length property. +for (let locale of [null]) { + // Empty input string. + assertThrowsInstanceOf(() => "".toLocaleUpperCase([locale]), TypeError); + + // Non-empty input string. + assertThrowsInstanceOf(() => "a".toLocaleUpperCase([locale]), TypeError); +} +// ToLength(ToObject(<primitive>)) returns 0. +for (let locale of [0, Math.PI, NaN, Infinity, true, false, Symbol()]) { + // Empty input string. + assertEq("".toLocaleUpperCase(locale), ""); + + // Non-empty input string. + assertEq("a".toLocaleUpperCase(locale), "A"); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/Intl/TypedArray/shell.js b/js/src/tests/non262/Intl/TypedArray/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/TypedArray/shell.js diff --git a/js/src/tests/non262/Intl/TypedArray/toLocaleString.js b/js/src/tests/non262/Intl/TypedArray/toLocaleString.js new file mode 100644 index 0000000000..7a5d0be30a --- /dev/null +++ b/js/src/tests/non262/Intl/TypedArray/toLocaleString.js @@ -0,0 +1,103 @@ +if (typeof Intl === "object") { + const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + const localeSep = [,,].toLocaleString(); + + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Missing arguments are passed as |undefined|. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], undefined); + assertEq(arguments[1], undefined); + return "pass"; + }; + + // Single element case. + assertEq(new constructor(1).toLocaleString(), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Missing options is passed as |undefined|. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], locales); + assertEq(arguments[1], undefined); + return "pass"; + }; + let locales = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Ensure "locales" and "options" arguments are passed to the array elements. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], locales); + assertEq(arguments[1], options); + return "pass"; + }; + let locales = {}; + let options = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales, options), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales, options), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + assertEq(new Float32Array([NaN]).toLocaleString("ar"), "ليس رقم"); + assertEq(new Float64Array([NaN]).toLocaleString(["zh-hant", "ar"]), "非數值"); + assertEq(new Float32Array([Infinity]).toLocaleString(["dz"]), "གྲངས་མེད"); + assertEq(new Float64Array([-Infinity]).toLocaleString(["fr", "en"]), "-∞"); + + const sampleValues = [-0, +0, -1, +1, -2, +2, -0.5, +0.5]; + const sampleLocales = [ + void 0, + "en", + "th-th-u-nu-thai", + ["tlh", "de"], + ]; + const sampleOptions = [ + void 0, + {}, + {style: "percent"}, + {style: "currency", currency: "USD", minimumIntegerDigits: 4}, + ]; + for (let locale of sampleLocales) { + for (let options of sampleOptions) { + let nf = new Intl.NumberFormat(locale, options); + for (let constructor of constructors) { + let typedArray = new constructor(sampleValues); + let expected = [].map.call(typedArray, nf.format).join(localeSep); + assertEq(typedArray.toLocaleString(locale, options), expected); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/available-locales-implied-script.js b/js/src/tests/non262/Intl/available-locales-implied-script.js new file mode 100644 index 0000000000..d9abbebf49 --- /dev/null +++ b/js/src/tests/non262/Intl/available-locales-implied-script.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + +// Test all Intl service constructors. +for (let intlConstructor of intlConstructors) { + // Retrieve all available locales of the given Intl service constructor. + let available = getAvailableLocalesOf(intlConstructor.name); + let availableSet = new Set(available); + + available.filter(x => { + return new Intl.Locale(x); + }).filter(loc => { + // Find all locales which have both a script and a region subtag. + return loc.script && loc.region; + }).forEach(loc => { + let noScript = `${loc.language}-${loc.region}`; + + // The locale without a script subtag must also be available. + assertEq(availableSet.has(noScript), true, `Missing locale ${noScript} for ${loc}`); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/available-locales-resolved.js b/js/src/tests/non262/Intl/available-locales-resolved.js new file mode 100644 index 0000000000..76b144d275 --- /dev/null +++ b/js/src/tests/non262/Intl/available-locales-resolved.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + +// Test all Intl service constructors. +for (let intlConstructor of intlConstructors) { + // Retrieve all available locales of the given Intl service constructor. + let available = getAvailableLocalesOf(intlConstructor.name); + + // "best fit" matchers could potentially return a different locale, so we only + // test with "lookup" locale matchers. (NB: We don't yet support "best fit" + // matchers.) + let options = {localeMatcher: "lookup"}; + + if (intlConstructor === Intl.DisplayNames) { + // Intl.DisplayNames can't be constructed without the "type" option. + options.type = "language"; + } + + for (let locale of available) { + let obj = new intlConstructor(locale, options); + let resolved = obj.resolvedOptions(); + + // If |locale| is an available locale, the "lookup" locale matcher should + // pick exactly that locale. + assertEq(resolved.locale, locale); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/available-locales-supported.js b/js/src/tests/non262/Intl/available-locales-supported.js new file mode 100644 index 0000000000..d11b5d714f --- /dev/null +++ b/js/src/tests/non262/Intl/available-locales-supported.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +if (typeof getAvailableLocalesOf === "undefined") { + var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf; +} + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + +// Test all Intl service constructors. +for (let intlConstructor of intlConstructors) { + // Retrieve all available locales of the given Intl service constructor. + let available = getAvailableLocalesOf(intlConstructor.name); + + // All available locales must be reported as supported. + let supported = intlConstructor.supportedLocalesOf(available); + assertEqArray(supported, available); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/best-available-locale-from-default-locale.js b/js/src/tests/non262/Intl/best-available-locale-from-default-locale.js new file mode 100644 index 0000000000..16a0e0348f --- /dev/null +++ b/js/src/tests/non262/Intl/best-available-locale-from-default-locale.js @@ -0,0 +1,107 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +if (typeof getDefaultLocale === "undefined") { + var getDefaultLocale = SpecialPowers.Cu.getJSTestingFunctions().getDefaultLocale; +} +if (typeof setDefaultLocale === "undefined") { + var setDefaultLocale = SpecialPowers.Cu.getJSTestingFunctions().setDefaultLocale; +} + +let defaultLocale = null; + +function withLocale(locale, fn) { + if (defaultLocale === null) + defaultLocale = getDefaultLocale(); + + setDefaultLocale(locale); + try { + fn(); + } finally { + setDefaultLocale(defaultLocale); + } +} + +// This test assumes Azerbaijani ("az") is a supported locale. +const supported = Intl.Collator.supportedLocalesOf("az"); +assertEq(supported.length, 1); +assertEq(supported[0], "az"); + +withLocale("az", () => { + // Ensure the new default locale is now active. + assertEq(new Intl.Collator().resolvedOptions().locale, "az"); + + // "az" is the active default locale, so explicitly requesting "az" should succeed. + assertEq(new Intl.Collator("az").resolvedOptions().locale, "az"); + + // ICU doesn't provide a specialised "az-Cyrl" locale, so we fallback to "az". + assertEq(new Intl.Collator("az-Cyrl").resolvedOptions().locale, "az"); + + // ICU doesn't provide a specialised "az-Cyrl-AZ" locale, so we fallback to "az". + assertEq(new Intl.Collator("az-Cyrl-AZ").resolvedOptions().locale, "az"); +}); + +// As demonstrated above, "az-Cyrl-AZ" normally isn't a supported Intl.Collator locale. But when +// used as the default locale, it gets promoted to being supported, because its parent locale "az" +// is supported and can act as a fallback. +// +// This works as follows: +// We accept any default locale as long as it can be supported either explicitly or implicitly +// through a fallback. But when we claim a default locale is supported, we also need to make sure +// we report any parent locale as being supported. So when "az-Cyrl-AZ" is accepted as the +// default locale, we also need to report its parent locale "az-Cyrl" as a supported locale. +// +// The reason we're doing this, is to make sure we aren't limiting the supported default locale to +// the intersection of the sets of supported locales for each Intl service constructor. Also see +// the requirements in <https://tc39.es/ecma402/#sec-internal-slots>, which state that the default +// locale must be a member of [[AvailableLocales]] for every Intl service constructor. +// +// So the following statement must hold: +// +// ∀ Constructor ∈ IntlConstructors: DefaultLocale ∈ Constructor.[[AvailableLocales]] +// +// This can trivially be achieved when we restrict the default locale to: +// +// { RequestedLocale if RequestedLocale ∈ (∩ C.[[AvailableLocales]]) +// { C ∈ IntlConstructors +// { +// DefaultLocale = { Fallback(RequestedLocale) if Fallback(RequestedLocale) ∈ (∩ C.[[AvailableLocales]]) +// { C ∈ IntlConstructors +// { +// { LastDitchLocale otherwise +// +// But that severely restricts the possible default locales. For example, "az-Cyrl-AZ" is supported +// by all Intl constructors except Intl.Collator. Intl.Collator itself only provides explicit +// support for the parent locale "az". So with the trivial solution we'd need to mark "az-Cyrl-AZ" +// as an invalid default locale and instead use its fallback locale "az". +// +// So instead of that we're using the following approach: +// +// { RequestedLocale if RequestedLocale ∈ (∩ C.[[AvailableLocales]]) +// { C ∈ IntlConstructors +// { +// DefaultLocale = { RequestedLocale if Fallback(RequestedLocale) ∈ (∩ C.[[AvailableLocales]]) +// { C ∈ IntlConstructors +// { +// { LastDitchLocale otherwise +// +// So even when the requested default locale is only implicitly supported through a fallback, we +// still accept it as a valid default locale. +withLocale("az-Cyrl-AZ", () => { + // Ensure the new default locale is now active. + assertEq(new Intl.Collator().resolvedOptions().locale, "az-Cyrl-AZ"); + + // "az-Cyrl-AZ" is the active default locale, so explicitly requesting the parent locale + // "az" should succeed. + assertEq(new Intl.Collator("az").resolvedOptions().locale, "az"); + + // "az-Cyrl-AZ" is the active default locale, so explicitly requesting the parent locale + // "az-Cyrl" should succeed. + assertEq(new Intl.Collator("az-Cyrl").resolvedOptions().locale, "az-Cyrl"); + + // "az-Cyrl-AZ" is the active default locale, so explicitly requesting "az-Cyrl-AZ" + // should succeed. + assertEq(new Intl.Collator("az-Cyrl-AZ").resolvedOptions().locale, "az-Cyrl-AZ"); +}); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/browser.js b/js/src/tests/non262/Intl/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/browser.js diff --git a/js/src/tests/non262/Intl/default-locale-shell.js b/js/src/tests/non262/Intl/default-locale-shell.js new file mode 100644 index 0000000000..ea3f6d17db --- /dev/null +++ b/js/src/tests/non262/Intl/default-locale-shell.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!xulRuntime.shell) + +// js/src/tests/lib/tests.py sets the default locale to "en-US" for shell tests. +// Ensure it's correctly set in the runtime and for the Intl service constructors. +const defaultLocale = "en-US"; + +assertEq(getDefaultLocale(), defaultLocale); + +assertEq(new Intl.Collator().resolvedOptions().locale, defaultLocale); +assertEq(new Intl.DateTimeFormat().resolvedOptions().locale, defaultLocale); +assertEq(new Intl.NumberFormat().resolvedOptions().locale, defaultLocale); +assertEq(new Intl.PluralRules().resolvedOptions().locale, defaultLocale); +assertEq(new Intl.RelativeTimeFormat().resolvedOptions().locale, defaultLocale); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/duplicate-variants.js b/js/src/tests/non262/Intl/duplicate-variants.js new file mode 100644 index 0000000000..5d07a9b109 --- /dev/null +++ b/js/src/tests/non262/Intl/duplicate-variants.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// RFC 5646 section 2.1 +// variant = 5*8alphanum ; registered variants +// / (DIGIT 3alphanum) + +// Duplicate variants are forbidden. +assertEqArray(Intl.getCanonicalLocales("de-1996"), ["de-1996"]); +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("de-1996-1996"), RangeError); + +// Multiple different variants are allowed. +assertEqArray(Intl.getCanonicalLocales("sl-rozaj-solba"), ["sl-rozaj-solba"]); + +// Variants can have the same prefix. +assertEqArray(Intl.getCanonicalLocales("zh-Latn-pinyin-pinyin2"), ["zh-Latn-pinyin-pinyin2"]); + +// Values in extension sequences are not counted as variants. +assertEqArray(Intl.getCanonicalLocales("en-u-kf-false-kn-false"), ["en-u-kf-false-kn-false"]); + +// Also test duplicates in Unicode extension keywords and attributes. +// From https://tools.ietf.org/html/rfc6067#section-2.1 +// +// An 'attribute' is a subtag with a length of three to eight +// characters following the singleton and preceding any 'keyword' +// sequences. No attributes were defined at the time of this +// document's publication. +// +// A 'keyword' is a sequence of subtags consisting of a 'key' subtag, +// followed by zero or more 'type' subtags (so a 'key' might appear +// alone and not be accompanied by a 'type' subtag). A 'key' MUST +// NOT appear more than once in a language tag's extension string. +// +// ... +// +// Only the first occurrence of an attribute or key conveys meaning in a +// language tag. When interpreting tags containing the Unicode locale +// extension, duplicate attributes or keywords are ignored in the +// following way: ignore any attribute that has already appeared in the +// tag and ignore any keyword whose key has already occurred in the tag. +// +// The duplicates itself are removed in CanonicalizeUnicodeLocaleId, step 2-3. +assertEqArray(Intl.getCanonicalLocales("en-u-kn-false-kn-false"), ["en-u-kn-false"]); +assertEqArray(Intl.getCanonicalLocales("en-u-attr1-attr2-attr2"), ["en-u-attr1-attr2"]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/extensions/browser.js b/js/src/tests/non262/Intl/extensions/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/extensions/browser.js diff --git a/js/src/tests/non262/Intl/extensions/options-value-emulates-undefined.js b/js/src/tests/non262/Intl/extensions/options-value-emulates-undefined.js new file mode 100644 index 0000000000..5b2ee124aa --- /dev/null +++ b/js/src/tests/non262/Intl/extensions/options-value-emulates-undefined.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!xulRuntime.shell||!this.hasOwnProperty('Intl')) +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 843004; +var summary = + "Use of an object that emulates |undefined| as the sole option must " + + "preclude imputing default values"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var opt = createIsHTMLDDA(); +opt.toString = function() { return "long"; }; + +var str = new Date(2013, 12 - 1, 14).toLocaleString("en-US", { weekday: opt }); + +// Because "weekday" was present and not undefined (stringifying to "long"), +// this must be a string like "Saturday" (in this implementation, that is). +assertEq(str, "Saturday"); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Intl/extensions/shell.js b/js/src/tests/non262/Intl/extensions/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Intl/extensions/shell.js diff --git a/js/src/tests/non262/Intl/extensions/unicode-extension-sequences.js b/js/src/tests/non262/Intl/extensions/unicode-extension-sequences.js new file mode 100644 index 0000000000..ead69a4632 --- /dev/null +++ b/js/src/tests/non262/Intl/extensions/unicode-extension-sequences.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.getSelfHostedValue) + +const startOfUnicodeExtensions = getSelfHostedValue("startOfUnicodeExtensions"); +const endOfUnicodeExtensions = getSelfHostedValue("endOfUnicodeExtensions"); + +const testcases = [ + // Language tag without Unicode extension. + { locale: "en", start: -1, end: 0 }, + { locale: "en-Latn", start: -1, end: 0 }, + { locale: "en-x-y", start: -1, end: 0 }, + { locale: "en-x-yz", start: -1, end: 0 }, + { locale: "en-x-u-kf", start: -1, end: 0 }, + + // Unicode extension sequence starts with key subtag. + // - no suceeding key or type subtags. + { locale: "en-u-ab", start: 2, end: 7 }, + { locale: "en-u-ab-x-y", start: 2, end: 7 }, + { locale: "en-u-ab-x-yz", start: 2, end: 7 }, + { locale: "en-u-ab-x-u-kn", start: 2, end: 7 }, + // - followed by key subtag. + { locale: "en-u-ab-cd", start: 2, end: 10 }, + { locale: "en-u-ab-cd-x-y", start: 2, end: 10 }, + { locale: "en-u-ab-cd-x-yz", start: 2, end: 10 }, + { locale: "en-u-ab-cd-x-u-kn", start: 2, end: 10 }, + // - followed by type subtag. + { locale: "en-u-ab-cdef", start: 2, end: 12 }, + { locale: "en-u-ab-cdef-x-y", start: 2, end: 12 }, + { locale: "en-u-ab-cdef-x-yz", start: 2, end: 12 }, + { locale: "en-u-ab-cdef-x-y-u-kn", start: 2, end: 12 }, + + // Unicode extension sequence starts with attribute subtag. + // - no suceeding attribute or key subtags. + { locale: "en-u-abc", start: 2, end: 8 }, + { locale: "en-u-abc-x-y", start: 2, end: 8 }, + { locale: "en-u-abc-x-yz", start: 2, end: 8 }, + { locale: "en-u-abc-x-y-u-kn", start: 2, end: 8 }, + // - followed by attribute subtag. + { locale: "en-u-abc-def", start: 2, end: 12 }, + { locale: "en-u-abc-def-x-y", start: 2, end: 12 }, + { locale: "en-u-abc-def-x-yz", start: 2, end: 12 }, + { locale: "en-u-abc-def-x-y-u-kn", start: 2, end: 12 }, + // - followed by key subtag. + { locale: "en-u-abc-de", start: 2, end: 11 }, + { locale: "en-u-abc-de-x-y", start: 2, end: 11 }, + { locale: "en-u-abc-de-x-yz", start: 2, end: 11 }, + { locale: "en-u-abc-de-x-y-u-kn", start: 2, end: 11 }, + // - followed by two key subtags. + { locale: "en-u-abc-de-fg", start: 2, end: 14 }, + { locale: "en-u-abc-de-fg-x-y", start: 2, end: 14 }, + { locale: "en-u-abc-de-fg-x-yz", start: 2, end: 14 }, + { locale: "en-u-abc-de-fg-x-y-u-kn", start: 2, end: 14 }, + // - followed by key and type subtag. + { locale: "en-u-abc-de-fgh", start: 2, end: 15 }, + { locale: "en-u-abc-de-fgh-x-y", start: 2, end: 15 }, + { locale: "en-u-abc-de-fgh-x-yz", start: 2, end: 15 }, + { locale: "en-u-abc-de-fgh-x-y-u-kn", start: 2, end: 15 }, + + // Also test when the Unicode extension doesn't start at index 2. + { locale: "en-Latn-u-kf", start: 7, end: 12 }, + { locale: "und-u-kf", start: 3, end: 8 }, +]; + +for (const {locale, start, end} of testcases) { + // Ensure the input is a valid language tag. + assertEqArray(Intl.getCanonicalLocales(locale), [locale]); + + assertEq(startOfUnicodeExtensions(locale), start); + + if (start >= 0) + assertEq(endOfUnicodeExtensions(locale, start), end); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/fallback-symbol.js b/js/src/tests/non262/Intl/fallback-symbol.js new file mode 100644 index 0000000000..87943257ce --- /dev/null +++ b/js/src/tests/non262/Intl/fallback-symbol.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')) + +function IntlFallbackSymbol(constructor) { + return Object.getOwnPropertySymbols(constructor.call(Object.create(constructor.prototype)))[0]; +} + +const dateTimeFormatIntlFallbackSymbol = IntlFallbackSymbol(Intl.DateTimeFormat); +const numberFormatIntlFallbackSymbol = IntlFallbackSymbol(Intl.NumberFormat); + +// Intl.DateTimeFormat and Intl.NumberFormat both use the same fallback symbol. +assertEq(dateTimeFormatIntlFallbackSymbol, numberFormatIntlFallbackSymbol); + +const intlFallbackSymbol = dateTimeFormatIntlFallbackSymbol; + +// The fallback symbol is a Symbol value. +assertEq(typeof intlFallbackSymbol, "symbol"); + +// Test the description of the fallback symbol. +assertEq(Symbol.prototype.toString.call(intlFallbackSymbol), "Symbol(IntlLegacyConstructedSymbol)"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/four-letter-language-codes.js b/js/src/tests/non262/Intl/four-letter-language-codes.js new file mode 100644 index 0000000000..394efacf29 --- /dev/null +++ b/js/src/tests/non262/Intl/four-letter-language-codes.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Four letter language subtags are not allowed. +const languageTags = [ + "root", // Special meaning in Unicode CLDR locale identifiers. + "Latn", // Unicode CLDR locale identifiers can start with a script subtag. + "Flob", // And now some non-sense input. + "ZORK", + "Blah-latn", + "QuuX-latn-us", + "SPAM-gb-x-Sausages-BACON-eggs", +]; + +for (let tag of languageTags) { + assertThrowsInstanceOf(() => Intl.getCanonicalLocales(tag), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCalendarInfo.js b/js/src/tests/non262/Intl/getCalendarInfo.js new file mode 100644 index 0000000000..5c9a8e24da --- /dev/null +++ b/js/src/tests/non262/Intl/getCalendarInfo.js @@ -0,0 +1,76 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCalendarInfo function with a diverse set of arguments. + +function checkCalendarInfo(info, expected) +{ + assertEq(Object.getPrototypeOf(info), Object.prototype); + + assertEq(info.firstDayOfWeek, expected.firstDayOfWeek); + assertEq(info.minDays, expected.minDays); + assertDeepEq(info.weekend, expected.weekend); + assertEq(info.calendar, expected.calendar); + assertEq(info.locale, expected.locale); +} + +addIntlExtras(Intl); + +let gCI = Intl.getCalendarInfo; + +assertEq(gCI.length, 1); + +checkCalendarInfo(gCI('en-US'), { + firstDayOfWeek: 7, + minDays: 1, + weekend: [6, 7], + calendar: "gregory", + locale: "en-US" +}); + +checkCalendarInfo(gCI('en-IL'), { + firstDayOfWeek: 7, + minDays: 1, + weekend: [5, 6], + calendar: "gregory", + locale: "en-IL" +}); + + +checkCalendarInfo(gCI('en-GB'), { + firstDayOfWeek: 1, + minDays: 4, + weekend: [6, 7], + calendar: "gregory", + locale: "en-GB" +}); + + +checkCalendarInfo(gCI('pl'), { + firstDayOfWeek: 1, + minDays: 4, + weekend: [6, 7], + calendar: "gregory", + locale: "pl" +}); + +checkCalendarInfo(gCI('ar-IQ'), { + firstDayOfWeek: 6, + minDays: 1, + weekend: [5, 6], + calendar: "gregory", + locale: "ar-IQ" +}); + +checkCalendarInfo(gCI('fa-IR'), { + firstDayOfWeek: 6, + minDays: 1, + weekend: [5], + calendar: "persian", + locale: "fa-IR" +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-overridden-arg-length.js b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-arg-length.js new file mode 100644 index 0000000000..f0cb3d4ee3 --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-arg-length.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCanonicalLocales function for overridden argument's length. + +var count = 0; +var locs = { get length() { if (count++ > 0) throw 42; return 0; } }; +var locales = Intl.getCanonicalLocales(locs); // shouldn't throw 42 +assertEq(locales.length, 0); + + +var obj = { get 0() { throw new Error("must not be gotten!"); }, + length: -Math.pow(2, 32) + 1 }; + +assertEq(Intl.getCanonicalLocales(obj).length, 0); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-overridden-push.js b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-push.js new file mode 100644 index 0000000000..36f53ebf06 --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-push.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCanonicalLocales function for overriden Array.push. + +Array.prototype.push = () => { throw 42; }; + +// must not throw 42, might if push is used +var arr = Intl.getCanonicalLocales(["en-US"]); + +assertEqArray(arr, ["en-US"]); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-overridden-set.js b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-set.js new file mode 100644 index 0000000000..396f3a9483 --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-set.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCanonicalLocales function for overridden set(). + +Object.defineProperty(Array.prototype, 0, { set() { throw 42; } }); + +// must not throw 42, might if push is used +var arr = Intl.getCanonicalLocales(["en-US"]); + +assertEqArray(arr, ["en-US"]); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-overridden-species.js b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-species.js new file mode 100644 index 0000000000..858735b581 --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-overridden-species.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Tests the getCanonicalLocales function for overriden Array[Symbol.species]. + +Object.defineProperty(Array, Symbol.species, { + value: function() { + return new Proxy(["?"], { + get(t, pk, r) { + return Reflect.get(t, pk, r); + }, + defineProperty(t, pk) { + return true; + } + }); + } +}); + +var arr = Intl.getCanonicalLocales("de-x-private"); + +assertEqArray(arr, ["de-x-private"]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-weird-cases.js b/js/src/tests/non262/Intl/getCanonicalLocales-weird-cases.js new file mode 100644 index 0000000000..d67fda355f --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-weird-cases.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Locale processing is supposed to internally remove any Unicode extension +// sequences in the locale. Test that various weird testcases invoking +// algorithmic edge cases don't assert or throw exceptions. + +var weirdCases = + [ + "en-x-u-foo", + "en-a-bar-x-u-foo", + "en-x-u-foo-a-bar", + "en-a-bar-u-baz-x-u-foo", + ]; + +for (let weird of weirdCases) + assertEqArray(Intl.getCanonicalLocales(weird), [weird]); + +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("x-u-foo"), RangeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Intl/getCanonicalLocales-with-duplicates.js b/js/src/tests/non262/Intl/getCanonicalLocales-with-duplicates.js new file mode 100644 index 0000000000..c029569482 --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales-with-duplicates.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCanonicalLocales function for duplicate locales scenario. + +assertEqArray(Intl.getCanonicalLocales(['ab-cd', 'ff', 'de-rt', 'ab-Cd']), + ['ab-CD', 'ff', 'de-RT']); + +var locales = Intl.getCanonicalLocales(["en-US", "en-US"]); +assertEqArray(locales, ['en-US']); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/getCanonicalLocales.js b/js/src/tests/non262/Intl/getCanonicalLocales.js new file mode 100644 index 0000000000..66e9b87c5c --- /dev/null +++ b/js/src/tests/non262/Intl/getCanonicalLocales.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests the getCanonicalLocales function with a diverse set of arguments. + +let gCL = Intl.getCanonicalLocales; + +assertEq(Intl.getCanonicalLocales.length, 1); + +assertEqArray(gCL(), []); + +assertEqArray(gCL('ab-cd'), ['ab-CD']); + +assertEqArray(gCL(['ab-cd']), ['ab-CD']); + +assertEqArray(gCL(['ab-cd', 'FF']), ['ab-CD', 'ff']); + +assertEqArray(gCL({'a': 0}), []); + +assertEqArray(gCL({}), []); + +assertEqArray(gCL(['ar-ma-u-ca-islamicc']), ['ar-MA-u-ca-islamic-civil']); + +assertEqArray(gCL(['th-th-u-nu-thai']), ['th-TH-u-nu-thai']); + +assertEqArray(gCL(NaN), []); + +assertEqArray(gCL(1), []); + +Number.prototype[0] = "en-US"; +Number.prototype.length = 1; +assertEqArray(gCL(NaN), ["en-US"]); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/legacy-and-sign-locales-with-unicode-extensions.js b/js/src/tests/non262/Intl/legacy-and-sign-locales-with-unicode-extensions.js new file mode 100644 index 0000000000..2421d9bb09 --- /dev/null +++ b/js/src/tests/non262/Intl/legacy-and-sign-locales-with-unicode-extensions.js @@ -0,0 +1,79 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const languageTags = { + // Case 1: legacy, regular tags. + "art-lojban": "jbo", + "cel-gaulish": "xtg", + "zh-guoyu": "zh", + "zh-hakka": "hak", + "zh-xiang": "hsn", + + // Case 2: redundant language tags. + "sgn-BR": "bzs", + "sgn-CO": "csn", + "sgn-DE": "gsg", + "sgn-DK": "dsl", + "sgn-ES": "ssp", + "sgn-FR": "fsl", + "sgn-GB": "bfi", + "sgn-GR": "gss", + "sgn-IE": "isg", + "sgn-IT": "ise", + "sgn-JP": "jsl", + "sgn-MX": "mfs", + "sgn-NI": "ncs", + "sgn-NL": "dse", + "sgn-NO": "nsi", + "sgn-PT": "psr", + "sgn-SE": "swl", + "sgn-US": "ase", + "sgn-ZA": "sfs", + + // Case 3: the special case. + "ja-Latn-hepburn-heploc": "ja-Latn-alalc97", +}; +const extensions = ["-u-attr", "-u-nu-latn"]; +const intlConstructors = [Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat]; +const options = [{localeMatcher: "best fit"}, {localeMatcher: "lookup"}]; + +for (let [tag, canonical] of Object.entries(languageTags)) { + // Ensure Intl.getCanonicalLocales canonicalizes the language tag correctly. + assertEq(Intl.getCanonicalLocales(tag)[0], canonical); + + // Unicode extensions don't change the canonicalization result. + for (let ext of extensions) { + assertEq(Intl.getCanonicalLocales(tag + ext)[0], canonical + ext); + assertEq(Intl.getCanonicalLocales(canonical + ext)[0], canonical + ext); + } + + for (let intlCtor of intlConstructors) { + for (let opt of options) { + // The non-canonical tag is supported iff the canonical tag is supported. + let supported = intlCtor.supportedLocalesOf(tag, opt); + let supportedCanonical = intlCtor.supportedLocalesOf(canonical, opt); + assertEq(supported.length, supportedCanonical.length); + + let isSupported = supported.length > 0; + if (isSupported) { + assertEq(supported[0], canonical); + assertEq(supportedCanonical[0], canonical); + } + + // Unicode extensions don't change the result of supportedLocalesOf(). + for (let ext of extensions) { + let supportedExt = intlCtor.supportedLocalesOf(tag + ext, opt); + let supportedCanonicalExt = intlCtor.supportedLocalesOf(canonical + ext, opt); + assertEq(supportedExt.length, supportedCanonical.length); + assertEq(supportedCanonicalExt.length, supportedCanonical.length); + + if (isSupported) { + assertEq(supportedExt[0], canonical + ext); + assertEq(supportedCanonicalExt[0], canonical + ext); + } + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/resolved-locale-sorted-unicode-extension-keys.js b/js/src/tests/non262/Intl/resolved-locale-sorted-unicode-extension-keys.js new file mode 100644 index 0000000000..400f7f0f63 --- /dev/null +++ b/js/src/tests/non262/Intl/resolved-locale-sorted-unicode-extension-keys.js @@ -0,0 +1,109 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function IsIntlService(c) { + return typeof c === "function" && + c.hasOwnProperty("prototype") && + c.prototype.hasOwnProperty("resolvedOptions"); +} + +const IntlServices = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); + +// Examples for all possible Unicode extension keys (with the exception of deprecated "vt"). +const unicodeExtensions = [ + // calendar + "ca-gregory", + "fw-mon", + "hc-h23", + + // collation + "co-phonebk", + "ka-noignore", + "kb-false", + "kc-false", + "kf-false", + "kh-false", + "kk-false", + "kn-false", + "kr-space", + "ks-level1", + "kv-space", + + // currency + "cf-standard", + "cu-eur", + + // measure + "ms-metric", + + // number + "nu-latn", + + // segmentation + "lb-strict", + "lw-normal", + "ss-none", + + // timezone + "tz-atvie", + + // variant + "em-default", + "rg-atzzzz", + "sd-atat1", + "va-posix", +]; + +function reverse(a, b) { + if (a < b) { + return 1; + } + if (a > b) { + return -1; + } + return 0; +} + +function findUnicodeExtensionKeys(locale) { + // Find the Unicode extension key subtag. + var extension = locale.match(/.*-u-(.*)/); + if (extension === null) { + return []; + } + + // Replace all types in the Unicode extension keywords. + return extension[1].replace(/-\w{3,}/g, "").split("-"); +} + +// Test all Intl service constructors and ensure the Unicode extension keys in +// the resolved locale are sorted alphabetically. + +for (let IntlService of IntlServices) { + let options = undefined; + if (IntlService === Intl.DisplayNames) { + // Intl.DisplayNames requires the "type" option to be set. + options = {type: "language"}; + } + + // sort() modifies the input array, so create a copy. + let ext = unicodeExtensions.slice(0); + + let locale, keys; + + // Input keys unsorted. + locale = new IntlService(`de-u-${ext.join("-")}`, options).resolvedOptions().locale; + keys = findUnicodeExtensionKeys(locale); + assertEqArray(keys, keys.slice(0).sort()); + + // Input keys sorted alphabetically. + locale = new IntlService(`de-u-${ext.sort().join("-")}`, options).resolvedOptions().locale; + keys = findUnicodeExtensionKeys(locale); + assertEqArray(keys, keys.slice(0).sort()); + + // Input keys sorted alphabetically in reverse order. + locale = new IntlService(`de-u-${ext.sort(reverse).join("-")}`, options).resolvedOptions().locale; + keys = findUnicodeExtensionKeys(locale); + assertEqArray(keys, keys.slice(0).sort()); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/shell.js b/js/src/tests/non262/Intl/shell.js new file mode 100644 index 0000000000..1659e39e12 --- /dev/null +++ b/js/src/tests/non262/Intl/shell.js @@ -0,0 +1,340 @@ +// Generated by make_intl_data.py. DO NOT EDIT. + +// source: CLDR file common/bcp47/number.xml; version CLDR 43. +// https://github.com/unicode-org/cldr/blob/master/common/bcp47/number.xml +// https://github.com/unicode-org/cldr/blob/master/common/supplemental/numberingSystems.xml +const numberingSystems = { + "adlm": { + "algorithmic": false, + "digits": "𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙" + }, + "ahom": { + "algorithmic": false, + "digits": "𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹" + }, + "arab": { + "algorithmic": false, + "digits": "٠١٢٣٤٥٦٧٨٩" + }, + "arabext": { + "algorithmic": false, + "digits": "۰۱۲۳۴۵۶۷۸۹" + }, + "armn": { + "algorithmic": true + }, + "armnlow": { + "algorithmic": true + }, + "bali": { + "algorithmic": false, + "digits": "᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙" + }, + "beng": { + "algorithmic": false, + "digits": "০১২৩৪৫৬৭৮৯" + }, + "bhks": { + "algorithmic": false, + "digits": "𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙" + }, + "brah": { + "algorithmic": false, + "digits": "𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯" + }, + "cakm": { + "algorithmic": false, + "digits": "𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿" + }, + "cham": { + "algorithmic": false, + "digits": "꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙" + }, + "cyrl": { + "algorithmic": true + }, + "deva": { + "algorithmic": false, + "digits": "०१२३४५६७८९" + }, + "diak": { + "algorithmic": false, + "digits": "𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙" + }, + "ethi": { + "algorithmic": true + }, + "fullwide": { + "algorithmic": false, + "digits": "0123456789" + }, + "geor": { + "algorithmic": true + }, + "gong": { + "algorithmic": false, + "digits": "𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩" + }, + "gonm": { + "algorithmic": false, + "digits": "𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙" + }, + "grek": { + "algorithmic": true + }, + "greklow": { + "algorithmic": true + }, + "gujr": { + "algorithmic": false, + "digits": "૦૧૨૩૪૫૬૭૮૯" + }, + "guru": { + "algorithmic": false, + "digits": "੦੧੨੩੪੫੬੭੮੯" + }, + "hanidays": { + "algorithmic": true + }, + "hanidec": { + "algorithmic": false, + "digits": "〇一二三四五六七八九" + }, + "hans": { + "algorithmic": true + }, + "hansfin": { + "algorithmic": true + }, + "hant": { + "algorithmic": true + }, + "hantfin": { + "algorithmic": true + }, + "hebr": { + "algorithmic": true + }, + "hmng": { + "algorithmic": false, + "digits": "𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙" + }, + "hmnp": { + "algorithmic": false, + "digits": "𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉" + }, + "java": { + "algorithmic": false, + "digits": "꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙" + }, + "jpan": { + "algorithmic": true + }, + "jpanfin": { + "algorithmic": true + }, + "jpanyear": { + "algorithmic": true + }, + "kali": { + "algorithmic": false, + "digits": "꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉" + }, + "kawi": { + "algorithmic": false, + "digits": "𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙" + }, + "khmr": { + "algorithmic": false, + "digits": "០១២៣៤៥៦៧៨៩" + }, + "knda": { + "algorithmic": false, + "digits": "೦೧೨೩೪೫೬೭೮೯" + }, + "lana": { + "algorithmic": false, + "digits": "᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉" + }, + "lanatham": { + "algorithmic": false, + "digits": "᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙" + }, + "laoo": { + "algorithmic": false, + "digits": "໐໑໒໓໔໕໖໗໘໙" + }, + "latn": { + "algorithmic": false, + "digits": "0123456789" + }, + "lepc": { + "algorithmic": false, + "digits": "᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉" + }, + "limb": { + "algorithmic": false, + "digits": "᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏" + }, + "mathbold": { + "algorithmic": false, + "digits": "𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗" + }, + "mathdbl": { + "algorithmic": false, + "digits": "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡" + }, + "mathmono": { + "algorithmic": false, + "digits": "𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿" + }, + "mathsanb": { + "algorithmic": false, + "digits": "𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵" + }, + "mathsans": { + "algorithmic": false, + "digits": "𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫" + }, + "mlym": { + "algorithmic": false, + "digits": "൦൧൨൩൪൫൬൭൮൯" + }, + "modi": { + "algorithmic": false, + "digits": "𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙" + }, + "mong": { + "algorithmic": false, + "digits": "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙" + }, + "mroo": { + "algorithmic": false, + "digits": "𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩" + }, + "mtei": { + "algorithmic": false, + "digits": "꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹" + }, + "mymr": { + "algorithmic": false, + "digits": "၀၁၂၃၄၅၆၇၈၉" + }, + "mymrshan": { + "algorithmic": false, + "digits": "႐႑႒႓႔႕႖႗႘႙" + }, + "mymrtlng": { + "algorithmic": false, + "digits": "꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹" + }, + "nagm": { + "algorithmic": false, + "digits": "𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹" + }, + "newa": { + "algorithmic": false, + "digits": "𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙" + }, + "nkoo": { + "algorithmic": false, + "digits": "߀߁߂߃߄߅߆߇߈߉" + }, + "olck": { + "algorithmic": false, + "digits": "᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙" + }, + "orya": { + "algorithmic": false, + "digits": "୦୧୨୩୪୫୬୭୮୯" + }, + "osma": { + "algorithmic": false, + "digits": "𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩" + }, + "rohg": { + "algorithmic": false, + "digits": "𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹" + }, + "roman": { + "algorithmic": true + }, + "romanlow": { + "algorithmic": true + }, + "saur": { + "algorithmic": false, + "digits": "꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙" + }, + "segment": { + "algorithmic": false, + "digits": "🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹" + }, + "shrd": { + "algorithmic": false, + "digits": "𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙" + }, + "sind": { + "algorithmic": false, + "digits": "𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹" + }, + "sinh": { + "algorithmic": false, + "digits": "෦෧෨෩෪෫෬෭෮෯" + }, + "sora": { + "algorithmic": false, + "digits": "𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹" + }, + "sund": { + "algorithmic": false, + "digits": "᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹" + }, + "takr": { + "algorithmic": false, + "digits": "𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉" + }, + "talu": { + "algorithmic": false, + "digits": "᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙" + }, + "taml": { + "algorithmic": true + }, + "tamldec": { + "algorithmic": false, + "digits": "௦௧௨௩௪௫௬௭௮௯" + }, + "telu": { + "algorithmic": false, + "digits": "౦౧౨౩౪౫౬౭౮౯" + }, + "thai": { + "algorithmic": false, + "digits": "๐๑๒๓๔๕๖๗๘๙" + }, + "tibt": { + "algorithmic": false, + "digits": "༠༡༢༣༤༥༦༧༨༩" + }, + "tirh": { + "algorithmic": false, + "digits": "𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙" + }, + "tnsa": { + "algorithmic": false, + "digits": "𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉" + }, + "vaii": { + "algorithmic": false, + "digits": "꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩" + }, + "wara": { + "algorithmic": false, + "digits": "𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩" + }, + "wcho": { + "algorithmic": false, + "digits": "𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹" + } +}; diff --git a/js/src/tests/non262/Intl/supportedValuesOf-calendar.js b/js/src/tests/non262/Intl/supportedValuesOf-calendar.js new file mode 100644 index 0000000000..24a4ac3a57 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-calendar.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const calendars = Intl.supportedValuesOf("calendar"); + +assertEq(new Set(calendars).size, calendars.length, "No duplicates are present"); +assertEqArray(calendars, [...calendars].sort(), "The list is sorted"); + +const typeRE = /^[a-z0-9]{3,8}(-[a-z0-9]{3,8})*$/; +for (let calendar of calendars) { + assertEq(typeRE.test(calendar), true, `${calendar} matches the 'type' production`); +} + +for (let calendar of calendars) { + assertEq(new Intl.Locale("und", {calendar}).calendar, calendar, `${calendar} is canonicalised`); +} + +for (let calendar of calendars) { + let obj = new Intl.DateTimeFormat("en", {calendar}); + assertEq(obj.resolvedOptions().calendar, calendar, `${calendar} is supported by DateTimeFormat`); +} + +assertEq(calendars.includes("gregory"), true, `Includes the Gregorian calendar`); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/supportedValuesOf-collation.js b/js/src/tests/non262/Intl/supportedValuesOf-collation.js new file mode 100644 index 0000000000..626bb28c20 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-collation.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const collations = Intl.supportedValuesOf("collation"); + +assertEq(new Set(collations).size, collations.length, "No duplicates are present"); +assertEqArray(collations, [...collations].sort(), "The list is sorted"); + +const typeRE = /^[a-z0-9]{3,8}(-[a-z0-9]{3,8})*$/; +for (let collation of collations) { + assertEq(typeRE.test(collation), true, `${collation} matches the 'type' production`); +} + +for (let collation of collations) { + assertEq(new Intl.Locale("und", {collation}).collation, collation, `${collation} is canonicalised`); +} + +// Not all locales support all possible collations, so test the minimal set to +// cover all supported collations. +const locales = [ + "en", // "emoji", "eor" + "ar", // compat + "de", // phonebk + "es", // trad + "ko", // searchjl + "ln", // phonetic + "si", // dict + "sv", // reformed + "zh", // big5han, gb2312, pinyin, stroke, unihan, zhuyin +]; + +for (let collation of collations) { + let supported = false; + for (let locale of locales) { + let obj = new Intl.Collator(locale, {collation}); + if (obj.resolvedOptions().collation === collation) { + supported = true; + } + } + + assertEq(supported, true, `${collation} is supported by Collator`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/supportedValuesOf-currency.js b/js/src/tests/non262/Intl/supportedValuesOf-currency.js new file mode 100644 index 0000000000..e2bf659e49 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-currency.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const currencies = Intl.supportedValuesOf("currency"); + +assertEq(new Set(currencies).size, currencies.length, "No duplicates are present"); +assertEqArray(currencies, [...currencies].sort(), "The list is sorted"); + +const codeRE = /^[A-Z]{3}$/; +for (let currency of currencies) { + assertEq(codeRE.test(currency), true, `${currency} is a 3-letter ISO 4217 currency code`); +} + +for (let currency of currencies) { + let obj = new Intl.NumberFormat("en", {style: "currency", currency}); + assertEq(obj.resolvedOptions().currency, currency, `${currency} is supported by NumberFormat`); +} + +for (let currency of currencies) { + let obj = new Intl.DisplayNames("en", {type: "currency", fallback: "none"}); + assertEq(typeof obj.of(currency), "string", `${currency} is supported by DisplayNames`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/supportedValuesOf-numberingSystem.js b/js/src/tests/non262/Intl/supportedValuesOf-numberingSystem.js new file mode 100644 index 0000000000..dd04094976 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-numberingSystem.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const numSystems = Intl.supportedValuesOf("numberingSystem"); + +assertEq(new Set(numSystems).size, numSystems.length, "No duplicates are present"); +assertEqArray(numSystems, [...numSystems].sort(), "The list is sorted"); + +const typeRE = /^[a-z0-9]{3,8}(-[a-z0-9]{3,8})*$/; +for (let numberingSystem of numSystems) { + assertEq(typeRE.test(numberingSystem), true, `${numberingSystem} matches the 'type' production`); +} + +for (let numberingSystem of numSystems) { + assertEq(new Intl.Locale("und", {numberingSystem}).numberingSystem, numberingSystem, + `${numberingSystem} is canonicalised`); +} + +for (let numberingSystem of numSystems) { + let obj = new Intl.DateTimeFormat("en", {numberingSystem}); + assertEq(obj.resolvedOptions().numberingSystem, numberingSystem, + `${numberingSystem} is supported by DateTimeFormat`); +} + +for (let numberingSystem of numSystems) { + let obj = new Intl.NumberFormat("en", {numberingSystem}); + assertEq(obj.resolvedOptions().numberingSystem, numberingSystem, + `${numberingSystem} is supported by NumberFormat`); +} + +for (let numberingSystem of numSystems) { + let obj = new Intl.RelativeTimeFormat("en", {numberingSystem}); + assertEq(obj.resolvedOptions().numberingSystem, numberingSystem, + `${numberingSystem} is supported by RelativeTimeFormat`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/supportedValuesOf-timeZones-canonical.js b/js/src/tests/non262/Intl/supportedValuesOf-timeZones-canonical.js new file mode 100644 index 0000000000..333f220038 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-timeZones-canonical.js @@ -0,0 +1,487 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Generated by make_intl_data.py. DO NOT EDIT. +// tzdata version = 2023c + +// This file was generated with historical, pre-1970 backzone information +// respected. + +const zones = [ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmara", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Timbuktu", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Atikokan", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Ciudad_Juarez", + "America/Coral_Harbour", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Ensenada", + "America/Fort_Nelson", + "America/Fortaleza", + "America/Glace_Bay", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montreal", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Nuuk", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Port_of_Spain", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Rosario", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Sitka", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Chongqing", + "Asia/Colombo", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Harbin", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kashgar", + "Asia/Kathmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Riyadh", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Tel_Aviv", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ulaanbaatar", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faroe", + "Atlantic/Jan_Mayen", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/St_Helena", + "Atlantic/Stanley", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/Perth", + "Australia/Sydney", + "CET", + "CST6CDT", + "EET", + "EST", + "EST5EDT", + "Etc/GMT+1", + "Etc/GMT+10", + "Etc/GMT+11", + "Etc/GMT+12", + "Etc/GMT+2", + "Etc/GMT+3", + "Etc/GMT+4", + "Etc/GMT+5", + "Etc/GMT+6", + "Etc/GMT+7", + "Etc/GMT+8", + "Etc/GMT+9", + "Etc/GMT-1", + "Etc/GMT-10", + "Etc/GMT-11", + "Etc/GMT-12", + "Etc/GMT-13", + "Etc/GMT-14", + "Etc/GMT-2", + "Etc/GMT-3", + "Etc/GMT-4", + "Etc/GMT-5", + "Etc/GMT-6", + "Etc/GMT-7", + "Etc/GMT-8", + "Etc/GMT-9", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belfast", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kirov", + "Europe/Kyiv", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Oslo", + "Europe/Paris", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Tiraspol", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vaduz", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zaporozhye", + "Europe/Zurich", + "Factory", + "HST", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "MET", + "MST", + "MST7MDT", + "PST8PDT", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Kanton", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Wake", + "Pacific/Wallis", + "UTC", + "WET", +]; + +let supported = Intl.supportedValuesOf("timeZone"); + +assertEqArray(supported, zones); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); + diff --git a/js/src/tests/non262/Intl/supportedValuesOf-timeZones.js b/js/src/tests/non262/Intl/supportedValuesOf-timeZones.js new file mode 100644 index 0000000000..6a200cd199 --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-timeZones.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const timeZones = Intl.supportedValuesOf("timeZone"); + +assertEq(new Set(timeZones).size, timeZones.length, "No duplicates are present"); +assertEqArray(timeZones, [...timeZones].sort(), "The list is sorted"); + +// The pattern doesn't cover the complete time zone syntax, but gives a good first approximation. +const timeZoneRE = /^[a-z0-9_+-]+(\/[a-z0-9_+-]+)*$/i; +for (let timeZone of timeZones) { + assertEq(timeZoneRE.test(timeZone), true, `${timeZone} is ASCII`); +} + +for (let timeZone of timeZones) { + let obj = new Intl.DateTimeFormat("en", {timeZone}); + assertEq(obj.resolvedOptions().timeZone, timeZone, `${timeZone} is supported by DateTimeFormat`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/supportedValuesOf-unit.js b/js/src/tests/non262/Intl/supportedValuesOf-unit.js new file mode 100644 index 0000000000..01dc5bb05c --- /dev/null +++ b/js/src/tests/non262/Intl/supportedValuesOf-unit.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const units = Intl.supportedValuesOf("unit"); + +assertEq(new Set(units).size, units.length, "No duplicates are present"); +assertEqArray(units, [...units].sort(), "The list is sorted"); + +const unitRE = /^[a-z]+(-[a-z]+)*$/; +for (let unit of units) { + assertEq(unitRE.test(unit), true, `${unit} is ASCII lower-case, separated by hyphens`); + assertEq(unit.includes("-per-"), false, `${unit} is a simple unit identifier`); +} + +for (let unit of units) { + let obj = new Intl.NumberFormat("en", {style: "unit", unit}); + assertEq(obj.resolvedOptions().unit, unit, `${unit} is supported by NumberFormat`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/tolower-ascii-equivalent.js b/js/src/tests/non262/Intl/tolower-ascii-equivalent.js new file mode 100644 index 0000000000..ac3cbfb7d3 --- /dev/null +++ b/js/src/tests/non262/Intl/tolower-ascii-equivalent.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Language tags are processed case-insensitive, but unconditionally calling +// the built-in String.prototype.toLowerCase() or toUpperCase() function +// before parsing a language tag can map non-ASCII characters into the ASCII +// range. +// +// Validate the Unicode BCP 47 locale identifier parser handles this case +// (pun intended) correctly by passing language tags which contain +// U+212A (KELVIN SIGN) and U+0131 (LATIN SMALL LETTER DOTLESS I) to +// Intl.getCanonicalLocales(). + +// The lower-case form of "i-ha\u212A" is "i-hak". +assertEq("i-hak", "i-ha\u212A".toLowerCase()); + +// The upper-case form of "\u0131-hak" is "I-HAK". +assertEq("I-HAK", "\u0131-hak".toUpperCase()); + +// "i-hak" is not a valid Unicode BCP 47 locale identifier. +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("i-hak"), RangeError); + +// And neither is "i-ha\u212A". +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("i-ha\u212A"), RangeError); + +// And also "\u0131-hak" isn't valid. +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("\u0131-hak"), RangeError); + +// The lower-case form of "zh-ha\u212A\u212Aa" is "zh-hakka". +assertEq("zh-hakka", "zh-ha\u212A\u212Aa".toLowerCase()); + +// "zh-hakka" is a valid Unicode BCP 47 locale identifier. +assertEqArray(Intl.getCanonicalLocales("zh-hakka"), ["hak"]); + +// But "zh-ha\u212A\u212Aa" is not a valid locale identifier. +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("zh-ha\u212A\u212Aa"), RangeError); + +// The lower-case form of "zh-x\u0131ang" is "ZH-XIANG". +assertEq("ZH-XIANG", "zh-x\u0131ang".toUpperCase()); + +// "zh-xiang" is a valid Unicode BCP 47 locale identifier. +assertEqArray(Intl.getCanonicalLocales("zh-xiang"), ["hsn"]); + +// But "zh-x\u0131ang" is not a valid locale identifier. +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("zh-x\u0131ang"), RangeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-extlangs.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-extlangs.js new file mode 100644 index 0000000000..7e9aae5519 --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-extlangs.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Unicode BCP 47 locale identifiers don't support extlang subtags. +const invalid = [ + // Two letter language code followed by extlang subtags. + "en-abc", + "en-abc-def", + "en-abc-def-ghi", + + // Three letter language code followed by extlang subtags. + "und-abc", + "und-abc-def", + "und-abc-def-ghi", +]; + +for (let locale of invalid) { + assertThrowsInstanceOf(() => Intl.getCanonicalLocales(locale), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-language-mappings.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-language-mappings.js new file mode 100644 index 0000000000..4f0a01a9cb --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-language-mappings.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// For the most part the mappings from IANA are a subset of the CLDR mappings. +// So there are mappings which are consistent across both databases. +assertEq(Intl.getCanonicalLocales("iw")[0], "he"); + +// But some languages are mapped differently. +// +// From the IANA language data registry: +// Type: language +// Subtag: drh +// Description: Darkhat +// Added: 2009-07-29 +// Deprecated: 2010-03-11 +// Preferred-Value: khk +// +// From CLDR: +// <languageAlias type="drh" replacement="mn" reason="deprecated"/> +// +// because CLDR also maps macro-languages: +// <languageAlias type="khk" replacement="mn" reason="macrolanguage"/> +assertEq(Intl.getCanonicalLocales("drh")[0], "mn"); + +// CLDR maps macro-languages: +// <languageAlias type="cmn" replacement="zh" reason="macrolanguage"/> +assertEq(Intl.getCanonicalLocales("cmn")[0], "zh"); + +// CLDR also contains mappings from ISO-639-2 (B/T) to 639-1 codes: +// <languageAlias type="dut" replacement="nl" reason="bibliographic"/> +// <languageAlias type="nld" replacement="nl" reason="overlong"/> +assertEq(Intl.getCanonicalLocales("dut")[0], "nl"); +assertEq(Intl.getCanonicalLocales("nld")[0], "nl"); + +// CLDR has additional mappings for legacy language codes. +// <languageAlias type="tl" replacement="fil" reason="legacy"/> +assertEq(Intl.getCanonicalLocales("tl")[0], "fil"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-languages-mappings-complex.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-languages-mappings-complex.js new file mode 100644 index 0000000000..320e1b02c0 --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-languages-mappings-complex.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// CLDR contains language mappings where in addition to the language subtag also +// the script or region subtag is modified, unless they're already present. + +// <languageAlias type="sh" replacement="sr_Latn" reason="legacy"/> +assertEq(Intl.getCanonicalLocales("sh")[0], "sr-Latn"); +assertEq(Intl.getCanonicalLocales("sh-RS")[0], "sr-Latn-RS"); +assertEq(Intl.getCanonicalLocales("sh-Cyrl")[0], "sr-Cyrl"); + +// <languageAlias type="cnr" replacement="sr_ME" reason="legacy"/> +assertEq(Intl.getCanonicalLocales("cnr")[0], "sr-ME"); +assertEq(Intl.getCanonicalLocales("cnr-Latn")[0], "sr-Latn-ME"); +assertEq(Intl.getCanonicalLocales("cnr-RS")[0], "sr-RS"); + +// Aliases where more than just a language subtag are present are ignored. +// <languageAlias type="sr_RS" replacement="sr_Cyrl_RS" reason="legacy"/> +assertEq(Intl.getCanonicalLocales("sr-RS")[0], "sr-RS"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-legacy.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-legacy.js new file mode 100644 index 0000000000..b7eefe2b75 --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-legacy.js @@ -0,0 +1,52 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Unicode BCP 47 locale identifiers don't support irregular legacy tags. +var irregularLegacy = [ + "en-gb-oed", + "i-ami", + "i-bnn", + "i-default", + "i-enochian", + "i-hak", + "i-klingon", + "i-lux", + "i-mingo", + "i-navajo", + "i-pwn", + "i-tao", + "i-tay", + "i-tsu", + "sgn-be-fr", + "sgn-be-nl", + "sgn-ch-de", +]; + +// Unicode BCP 47 locale identifiers don't support regular legacy tags +// which contain an extlang-like subtag. +var regularLegacyWithExtlangLike = [ + "no-bok", + "no-nyn", + "zh-min", + "zh-min-nan", +]; + +// Unicode BCP 47 locale identifiers do support regular legacy tags +// which contain a variant-like subtag. +var regularLegacyWithVariantLike = { + "art-lojban": "jbo", + "cel-gaulish": "xtg", + "zh-guoyu": "zh", + "zh-hakka": "hak", + "zh-xiang": "hsn", +}; + +for (let locale of [...irregularLegacy, ...regularLegacyWithExtlangLike]) { + assertThrowsInstanceOf(() => Intl.getCanonicalLocales(locale), RangeError); +} + +for (let [locale, canonical] of Object.entries(regularLegacyWithVariantLike)) { + assertEq(Intl.getCanonicalLocales(locale)[0], canonical); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-and-subdivision.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-and-subdivision.js new file mode 100644 index 0000000000..c9e1bd3951 --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-and-subdivision.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// <subdivisionAlias type="frg" replacement="frges" reason="deprecated"/> +assertEq(Intl.getCanonicalLocales("fr-u-rg-frg")[0], "fr-u-rg-frges"); +assertEq(Intl.getCanonicalLocales("fr-u-sd-frg")[0], "fr-u-sd-frges"); + +// <subdivisionAlias type="frgf" replacement="GF" reason="overlong"/> +assertEq(Intl.getCanonicalLocales("fr-u-rg-frgf")[0], "fr-u-rg-gfzzzz"); +assertEq(Intl.getCanonicalLocales("fr-u-sd-frgf")[0], "fr-u-sd-gfzzzz"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings-complex.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings-complex.js new file mode 100644 index 0000000000..22dbd17a1d --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings-complex.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// CLDR contains region mappings where the replacement region depends on the +// likely subtags from the language and script subtags. +// +// For example, the breakup of the Soviet Union ("SU") means that the region of +// the Soviet Union ("SU") is replaced by Russia ("RU"), Armenia ("AM"), or +// many others -- depending on the specified (or merely likely) language and +// script subtags: +// +// <territoryAlias type="SU" replacement="RU AM AZ BY EE GE KZ KG LV LT MD TJ TM UA UZ" reason="deprecated"/> +// +// Armenia can be the preferred region when the language is "hy" (Armenian) or +// the script is "Armn" (Armenian). +// +// <likelySubtag from="hy" to="hy_Armn_AM"/> +// <likelySubtag from="und_Armn" to="hy_Armn_AM"/> +assertEq(Intl.getCanonicalLocales("ru-SU")[0], "ru-RU"); +assertEq(Intl.getCanonicalLocales("en-SU")[0], "en-RU"); +assertEq(Intl.getCanonicalLocales("und-SU")[0], "und-RU"); +assertEq(Intl.getCanonicalLocales("und-Latn-SU")[0], "und-Latn-RU"); +assertEq(Intl.getCanonicalLocales("hy-SU")[0], "hy-AM"); +assertEq(Intl.getCanonicalLocales("und-Armn-SU")[0], "und-Armn-AM"); + +// <territoryAlias type="CS" replacement="RS ME" reason="deprecated"/> +// +// The following likely-subtags entries contain "RS" and "ME": +// +// <likelySubtag from="sr" to="sr_Cyrl_RS"/> +// <likelySubtag from="sr_ME" to="sr_Latn_ME"/> +// <likelySubtag from="und_RS" to="sr_Cyrl_RS"/> +// <likelySubtag from="und_ME" to="sr_Latn_ME"/> +// +// In this case there is no language/script combination (without a region +// subtag) where "ME" is ever chosen, so the replacement is always "RS". +assertEq(Intl.getCanonicalLocales("sr-CS")[0], "sr-RS"); +assertEq(Intl.getCanonicalLocales("sr-Latn-CS")[0], "sr-Latn-RS"); +assertEq(Intl.getCanonicalLocales("sr-Cyrl-CS")[0], "sr-Cyrl-RS"); + +// The existing region in the source locale identifier is ignored when selecting +// the likely replacement region. For example take "az-NT", which is Azerbaijani +// spoken in the Neutral Zone. The replacement region for "NT" is either +// "SA" (Saudi-Arabia) or "IQ" (Iraq), and there is also a likely subtags entry +// for "az-IQ". But when only looking at the language subtag in "az-NT", "az" is +// always resolved to "az-Latn-AZ", and because "AZ" is not in the list ["SA", +// "IQ"], the final replacement region is the default for "NT", namely "SA". +// That means "az-NT" will be canonicalised to "az-SA" and not "az-IQ", even +// though the latter may be a more sensible candidate based on the actual usage +// of the target locales. +// +// <territoryAlias type="NT" replacement="SA IQ" reason="deprecated"/> +// <likelySubtag from="az_IQ" to="az_Arab_IQ"/> +// <likelySubtag from="az" to="az_Latn_AZ"/> +assertEq(Intl.getCanonicalLocales("az-NT")[0], "az-SA"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings.js new file mode 100644 index 0000000000..affebb33b8 --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-region-mappings.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// For the most part the mappings from IANA are a subset of the CLDR mappings. +// So there are mappings which are consistent across both databases. +assertEq(Intl.getCanonicalLocales("de-DD")[0], "de-DE"); + +// CLDR contains additional mappings: +// <territoryAlias type="QU" replacement="EU" reason="deprecated"/> +// <territoryAlias type="UK" replacement="GB" reason="deprecated"/> +assertEq(Intl.getCanonicalLocales("und-QU")[0], "und-EU"); +assertEq(Intl.getCanonicalLocales("en-UK")[0], "en-GB"); + +// CLDR additional maps ISO 3166-1 numeric to ISO 3166-1 alpha-2 codes: +// <territoryAlias type="280" replacement="DE" reason="deprecated"/> +// <territoryAlias type="278" replacement="DE" reason="overlong"/> +// <territoryAlias type="276" replacement="DE" reason="overlong"/> +assertEq(Intl.getCanonicalLocales("de-280")[0], "de-DE"); +assertEq(Intl.getCanonicalLocales("de-278")[0], "de-DE"); +assertEq(Intl.getCanonicalLocales("de-276")[0], "de-DE"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-sign-languages.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-sign-languages.js new file mode 100644 index 0000000000..9f7b01c69e --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-sign-languages.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// The IANA language subtag registry contains replacements for sign language +// codes marked as redundant. For example: +// +// Type: redundant +// Tag: sgn-DE +// Description: German Sign Language +// Added: 2001-11-11 +// Deprecated: 2009-07-29 +// Preferred-Value: gsg +// +// CLDR 38 added these mappings to CLDR, too. + +assertEq(Intl.getCanonicalLocales("sgn-DE")[0], "gsg"); +assertEq(Intl.getCanonicalLocales("sgn-DD")[0], "gsg"); + +assertEq(Intl.getCanonicalLocales("sgn-276")[0], "gsg"); +assertEq(Intl.getCanonicalLocales("sgn-278")[0], "gsg"); +assertEq(Intl.getCanonicalLocales("sgn-280")[0], "gsg"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-transformed-ext.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-transformed-ext.js new file mode 100644 index 0000000000..50ac20e58b --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-transformed-ext.js @@ -0,0 +1,71 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const invalid = [ + // empty + "en-t", + "en-t-a", + "en-t-x", + "en-t-0", + + // incomplete + "en-t-", + "en-t-en-", + "en-t-0x-", + + // tlang: unicode_language_subtag must be 2-3 or 5-8 characters and mustn't + // contain extlang subtags. + "en-t-root", + "en-t-abcdefghi", + "en-t-ar-aao", + + // tlang: unicode_script_subtag must be 4 alphabetical characters, can't + // be repeated. + "en-t-en-lat0", + "en-t-en-latn-latn", + + // tlang: unicode_region_subtag must either be 2 alpha characters or a three + // digit code. + "en-t-en-0", + "en-t-en-00", + "en-t-en-0x", + "en-t-en-x0", + "en-t-en-latn-0", + "en-t-en-latn-00", + "en-t-en-latn-xyz", + + // tlang: unicode_variant_subtag is either 5-8 alphanum characters or 4 + // characters starting with a digit. + "en-t-en-abcdefghi", + "en-t-en-latn-gb-ab", + "en-t-en-latn-gb-abc", + "en-t-en-latn-gb-abcd", + "en-t-en-latn-gb-abcdefghi", +]; + +// Canonicalisation also applies for the transformation extension. But also +// see <https://github.com/tc39/ecma402/issues/330>. +const valid = [ + {locale: "en-t-en", canonical: "en-t-en"}, + {locale: "en-t-en-latn", canonical: "en-t-en-latn"}, + {locale: "en-t-en-ca", canonical: "en-t-en-ca"}, + {locale: "en-t-en-latn-ca", canonical: "en-t-en-latn-ca"}, + {locale: "en-t-en-emodeng", canonical: "en-t-en-emodeng"}, + {locale: "en-t-en-latn-emodeng", canonical: "en-t-en-latn-emodeng"}, + {locale: "en-t-en-latn-ca-emodeng", canonical: "en-t-en-latn-ca-emodeng"}, + {locale: "sl-t-sl-rozaj-biske-1994", canonical: "sl-t-sl-1994-biske-rozaj"}, + {locale: "DE-T-M0-DIN-K0-QWERTZ", canonical: "de-t-k0-qwertz-m0-din"}, + {locale: "en-t-m0-true", canonical: "en-t-m0-true"}, + {locale: "en-t-iw", canonical: "en-t-he"}, + {locale: "und-Latn-t-und-hani-m0-names", canonical: "und-Latn-t-und-hani-m0-prprname"}, +]; + +for (let locale of invalid) { + assertThrowsInstanceOf(() => Intl.getCanonicalLocales(locale), RangeError); +} + +for (let {locale, canonical} of valid) { + assertEq(Intl.getCanonicalLocales(locale)[0], canonical); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-unicode-ext.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-unicode-ext.js new file mode 100644 index 0000000000..a46eba475e --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-unicode-ext.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Unicode locale extension sequences don't allow keys with a digit as their +// second character. +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("en-u-c0"), RangeError); +assertThrowsInstanceOf(() => Intl.getCanonicalLocales("en-u-00"), RangeError); + +// The first character is allowed to be a digit. +assertEq(Intl.getCanonicalLocales("en-u-0c")[0], "en-u-0c"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-legacy-mappings.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-legacy-mappings.js new file mode 100644 index 0000000000..41b2a9a5cb --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-legacy-mappings.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// ECMA-402 includes mapping of legacy variants, as long as they're also present +// in <variantAlias> in CLDR's supplementalMetadata.xml +// <https://www.unicode.org/reports/tr35/#Legacy_Variants> + +assertEq(Intl.getCanonicalLocales("sv-AALAND")[0], "sv-AX"); +assertEq(Intl.getCanonicalLocales("no-BOKMAL")[0], "nb"); +assertEq(Intl.getCanonicalLocales("no-NYNORSK")[0], "nn"); +assertEq(Intl.getCanonicalLocales("en-POSIX")[0], "en-posix"); +assertEq(Intl.getCanonicalLocales("el-POLYTONI")[0], "el-polyton"); +assertEq(Intl.getCanonicalLocales("aa-SAAHO")[0], "ssy"); + +// Additional cases from CLDR, version 38. + +assertEq(Intl.getCanonicalLocales("aar-saaho")[0], "ssy"); +assertEq(Intl.getCanonicalLocales("arm-arevmda")[0], "hyw"); +assertEq(Intl.getCanonicalLocales("hy-arevmda")[0], "hyw"); +assertEq(Intl.getCanonicalLocales("hye-arevmda")[0], "hyw"); + +for (let language of ["chi", "cmn", "zh", "zho"]) { + assertEq(Intl.getCanonicalLocales(language)[0], "zh"); + + let mapping = { + "guoyu-hakka": "hak", + "guoyu-xiang": "hsn", + "guoyu": "zh", + "hakka": "hak", + "xiang": "hsn", + }; + for (let [variant, canonical] of Object.entries(mapping)) { + assertEq(Intl.getCanonicalLocales(`${language}-${variant}`)[0], canonical); + } +} + +// Most legacy variant subtags are simply removed in contexts they don't apply. +for (let variant of ["arevela", "arevmda", "bokmal", "hakka", "lojban", "nynorsk", "saaho", "xiang"]) { + assertEq(Intl.getCanonicalLocales(`en-${variant}`)[0], "en"); +} + +// Except these three, whose replacement is always applied. +assertEq(Intl.getCanonicalLocales("en-aaland")[0], "en-AX"); +assertEq(Intl.getCanonicalLocales("en-heploc")[0], "en-alalc97"); +assertEq(Intl.getCanonicalLocales("en-polytoni")[0], "en-polyton"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-sorted.js b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-sorted.js new file mode 100644 index 0000000000..e50436a83c --- /dev/null +++ b/js/src/tests/non262/Intl/unicode-bcp47-locale-ids-variants-sorted.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// UTS 35, 3.2.1 Canonical Unicode Locale Identifiers: +// - Any variants are in alphabetical order. + +assertEq(Intl.getCanonicalLocales("en-scouse-fonipa")[0], "en-fonipa-scouse"); + +// Sorting in alphabetical order may turn a valid BCP 47 language tag into a +// BCP 47 language tag which is only well-formed, but no longer valid. This +// means there are potential compatibility issues when converting between +// Unicode BCP 47 locale identifiers and BCP 47 language tags. +// +// Spec: https://tools.ietf.org/html/rfc5646#section-2.2.9 + +// <https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry> +// +// Type: variant +// Subtag: 1994 +// Description: Standardized Resian orthography +// Added: 2007-07-28 +// Prefix: sl-rozaj +// Prefix: sl-rozaj-biske +// Prefix: sl-rozaj-njiva +// Prefix: sl-rozaj-osojs +// Prefix: sl-rozaj-solba +// Comments: For standardized Resian an orthography was published in 1994. + +assertEq(Intl.getCanonicalLocales("sl-rozaj-biske-1994")[0], "sl-1994-biske-rozaj"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Intl/variant-with-preferred-value.js b/js/src/tests/non262/Intl/variant-with-preferred-value.js new file mode 100644 index 0000000000..751649a750 --- /dev/null +++ b/js/src/tests/non262/Intl/variant-with-preferred-value.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Per UTS 35, computing the canonical form for Unicode BCP 47 locale identifiers +// includes replacing deprecated variant mappings. The other UTS 35 canonicalisation +// algorithm ("BCP 47 Language Tag to Unicode BCP 47 Locale Identifier") doesn't +// support deprecated variant mappings. +// https://github.com/tc39/ecma402/issues/330 + +const languageTags = { + // The preferred value of "hy-arevela" is "hy" per CLDR. + "hy-arevela": "hy", + "hy-Armn-arevela": "hy-Armn", + "hy-AM-arevela": "hy-AM", + "hy-arevela-fonipa": "hy-fonipa", + "hy-fonipa-arevela": "hy-fonipa", + + // The preferred value of "hy-arevmda" is "hyw" per CLDR. + "hy-arevmda": "hyw", + "hy-Armn-arevmda": "hyw-Armn", + "hy-AM-arevmda": "hyw-AM", + "hy-arevmda-fonipa": "hyw-fonipa", + "hy-fonipa-arevmda": "hyw-fonipa", + + // The preferred value of "ja-Latn-hepburn-heploc" is "ja-Latn-alalc97" per CLDR and + // IANA. + "ja-Latn-hepburn-heploc": "ja-Latn-alalc97", + "ja-Latn-JP-hepburn-heploc": "ja-Latn-JP-alalc97", + + // Variant subtag replacements not present in IANA. + "sv-aaland": "sv-AX", + "el-polytoni": "el-polyton", + + // Additional cases when more variant subtags are present. + + // 1. The preferred variant is already present. + "ja-Latn-alalc97-hepburn-heploc": "ja-Latn-alalc97", + "ja-Latn-hepburn-alalc97-heploc": "ja-Latn-alalc97", + "ja-Latn-hepburn-heploc-alalc97": "ja-Latn-alalc97", + + // 2. The variant subtags aren't in the expected order per IANA. (CLDR doesn't care + // about the order of variant subtags.) + "ja-Latn-heploc-hepburn": "ja-Latn-alalc97", + + // 3. IANA expects both variant subtags to be present, CLDR also accepts single "heploc". + "ja-Latn-heploc": "ja-Latn-alalc97", + + // 4. Test for cases when the same variant subtag position needs to be checked more + // than once when replacing deprecated variant subtags. + "ja-Latn-aaland-heploc": "ja-Latn-AX-alalc97", + "ja-Latn-heploc-polytoni": "ja-Latn-alalc97-polyton", +}; + +for (let [tag, canonical] of Object.entries(languageTags)) { + assertEq(Intl.getCanonicalLocales(tag)[0], canonical); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); |