301 lines
11 KiB
Diff
301 lines
11 KiB
Diff
|
diff --git a/src/js/intl.js b/src/js/intl.js
|
||
|
index 53fbe1f947..3c7112716c 100644
|
||
|
--- a/src/js/intl.js
|
||
|
+++ b/src/js/intl.js
|
||
|
@@ -152,18 +152,11 @@ var AVAILABLE_LOCALES = {
|
||
|
*/
|
||
|
var DEFAULT_ICU_LOCALE = UNDEFINED;
|
||
|
|
||
|
-function GetDefaultICULocaleJS(service) {
|
||
|
+function GetDefaultICULocaleJS() {
|
||
|
if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
|
||
|
DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
|
||
|
}
|
||
|
- // Check that this is a valid default for this service,
|
||
|
- // otherwise fall back to "und"
|
||
|
- // TODO(littledan,jshin): AvailableLocalesOf sometimes excludes locales
|
||
|
- // which don't require tailoring, but work fine with root data. Look into
|
||
|
- // exposing this fact in ICU or the way Chrome bundles data.
|
||
|
- return (IS_UNDEFINED(service) ||
|
||
|
- HAS_OWN_PROPERTY(getAvailableLocalesOf(service), DEFAULT_ICU_LOCALE))
|
||
|
- ? DEFAULT_ICU_LOCALE : "und";
|
||
|
+ return DEFAULT_ICU_LOCALE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@@ -434,6 +427,48 @@ function resolveLocale(service, requestedLocales, options) {
|
||
|
}
|
||
|
|
||
|
|
||
|
+/**
|
||
|
+ * Look up the longest non-empty prefix of |locale| that is an element of
|
||
|
+ * |availableLocales|. Returns undefined when the |locale| is completely
|
||
|
+ * unsupported by |availableLocales|.
|
||
|
+ */
|
||
|
+function bestAvailableLocale(availableLocales, locale) {
|
||
|
+ do {
|
||
|
+ if (!IS_UNDEFINED(availableLocales[locale])) {
|
||
|
+ return locale;
|
||
|
+ }
|
||
|
+ // Truncate locale if possible.
|
||
|
+ var pos = %StringLastIndexOf(locale, '-');
|
||
|
+ if (pos === -1) {
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ locale = %_Call(StringSubstring, locale, 0, pos);
|
||
|
+ } while (true);
|
||
|
+
|
||
|
+ return UNDEFINED;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * Try to match any mutation of |requestedLocale| against |availableLocales|.
|
||
|
+ */
|
||
|
+function attemptSingleLookup(availableLocales, requestedLocale) {
|
||
|
+ // Remove all extensions.
|
||
|
+ var noExtensionsLocale = %RegExpInternalReplace(
|
||
|
+ GetAnyExtensionRE(), requestedLocale, '');
|
||
|
+ var availableLocale = bestAvailableLocale(
|
||
|
+ availableLocales, requestedLocale);
|
||
|
+ if (!IS_UNDEFINED(availableLocale)) {
|
||
|
+ // Return the resolved locale and extension.
|
||
|
+ var extensionMatch = %regexp_internal_match(
|
||
|
+ GetUnicodeExtensionRE(), requestedLocale);
|
||
|
+ var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
|
||
|
+ return {locale: availableLocale, extension: extension};
|
||
|
+ }
|
||
|
+ return UNDEFINED;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
/**
|
||
|
* Returns best matched supported locale and extension info using basic
|
||
|
* lookup algorithm.
|
||
|
@@ -446,31 +481,25 @@ function lookupMatcher(service, requestedLocales) {
|
||
|
var availableLocales = getAvailableLocalesOf(service);
|
||
|
|
||
|
for (var i = 0; i < requestedLocales.length; ++i) {
|
||
|
- // Remove all extensions.
|
||
|
- var locale = %RegExpInternalReplace(
|
||
|
- GetAnyExtensionRE(), requestedLocales[i], '');
|
||
|
- do {
|
||
|
- if (!IS_UNDEFINED(availableLocales[locale])) {
|
||
|
- // Return the resolved locale and extension.
|
||
|
- var extensionMatch = %regexp_internal_match(
|
||
|
- GetUnicodeExtensionRE(), requestedLocales[i]);
|
||
|
- var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
|
||
|
- return {locale: locale, extension: extension, position: i};
|
||
|
- }
|
||
|
- // Truncate locale if possible.
|
||
|
- var pos = %StringLastIndexOf(locale, '-');
|
||
|
- if (pos === -1) {
|
||
|
- break;
|
||
|
- }
|
||
|
- locale = %_Call(StringSubstring, locale, 0, pos);
|
||
|
- } while (true);
|
||
|
+ var result = attemptSingleLookup(availableLocales, requestedLocales[i]);
|
||
|
+ if (!IS_UNDEFINED(result)) {
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ var defLocale = GetDefaultICULocaleJS();
|
||
|
+
|
||
|
+ // While ECMA-402 returns defLocale directly, we have to check if it is
|
||
|
+ // supported, as such support is not guaranteed.
|
||
|
+ var result = attemptSingleLookup(availableLocales, defLocale);
|
||
|
+ if (!IS_UNDEFINED(result)) {
|
||
|
+ return result;
|
||
|
}
|
||
|
|
||
|
// Didn't find a match, return default.
|
||
|
return {
|
||
|
- locale: GetDefaultICULocaleJS(service),
|
||
|
- extension: '',
|
||
|
- position: -1
|
||
|
+ locale: 'und',
|
||
|
+ extension: ''
|
||
|
};
|
||
|
}
|
||
|
|
||
|
diff --git a/test/intl/assert.js b/test/intl/assert.js
|
||
|
index d8cc85849f..c11e7c0bbf 100644
|
||
|
--- a/test/intl/assert.js
|
||
|
+++ b/test/intl/assert.js
|
||
|
@@ -132,6 +132,16 @@ function assertFalse(value, user_message = '') {
|
||
|
}
|
||
|
|
||
|
|
||
|
+/**
|
||
|
+ * Throws if value is null.
|
||
|
+ */
|
||
|
+function assertNotNull(value, user_message = '') {
|
||
|
+ if (value === null) {
|
||
|
+ fail("not null", value, user_message);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
/**
|
||
|
* Runs code() and asserts that it throws the specified exception.
|
||
|
*/
|
||
|
@@ -189,3 +199,34 @@ function assertInstanceof(obj, type) {
|
||
|
(actualTypeName ? ' but of < ' + actualTypeName + '>' : ''));
|
||
|
}
|
||
|
}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * Split a BCP 47 language tag into locale and extension.
|
||
|
+ */
|
||
|
+function splitLanguageTag(tag) {
|
||
|
+ var extRe = /(-[0-9A-Za-z](-[0-9A-Za-z]{2,8})+)+$/;
|
||
|
+ var match = %regexp_internal_match(extRe, tag);
|
||
|
+ if (match) {
|
||
|
+ return { locale: tag.slice(0, match.index), extension: match[0] };
|
||
|
+ }
|
||
|
+
|
||
|
+ return { locale: tag, extension: '' };
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * Throw if |parent| is not a more general language tag of |child|, nor |child|
|
||
|
+ * itself, per BCP 47 rules.
|
||
|
+ */
|
||
|
+function assertLanguageTag(child, parent) {
|
||
|
+ var childSplit = splitLanguageTag(child);
|
||
|
+ var parentSplit = splitLanguageTag(parent);
|
||
|
+
|
||
|
+ // Do not compare extensions at this moment, as %GetDefaultICULocale()
|
||
|
+ // doesn't always output something we support.
|
||
|
+ if (childSplit.locale !== parentSplit.locale &&
|
||
|
+ !childSplit.locale.startsWith(parentSplit.locale + '-')) {
|
||
|
+ fail(child, parent, 'language tag comparison');
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/test/intl/break-iterator/default-locale.js b/test/intl/break-iterator/default-locale.js
|
||
|
index d8d5aeadb2..e1a42a100a 100644
|
||
|
--- a/test/intl/break-iterator/default-locale.js
|
||
|
+++ b/test/intl/break-iterator/default-locale.js
|
||
|
@@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
||
|
assertFalse(options.locale === '');
|
||
|
assertFalse(options.locale === undefined);
|
||
|
|
||
|
-// Then check for equality.
|
||
|
-assertEquals(options.locale, %GetDefaultICULocale());
|
||
|
+// Then check for legitimacy.
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||
|
|
||
|
var iteratorNone = new Intl.v8BreakIterator();
|
||
|
assertEquals(options.locale, iteratorNone.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/break-iterator/wellformed-unsupported-locale.js b/test/intl/break-iterator/wellformed-unsupported-locale.js
|
||
|
index 5ac8fbcd41..ffa44aef08 100644
|
||
|
--- a/test/intl/break-iterator/wellformed-unsupported-locale.js
|
||
|
+++ b/test/intl/break-iterator/wellformed-unsupported-locale.js
|
||
|
@@ -29,4 +29,4 @@
|
||
|
|
||
|
var iterator = Intl.v8BreakIterator(['xx']);
|
||
|
|
||
|
-assertEquals(iterator.resolvedOptions().locale, %GetDefaultICULocale());
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), iterator.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/collator/default-locale.js b/test/intl/collator/default-locale.js
|
||
|
index db9b1e7330..5fc6ff4665 100644
|
||
|
--- a/test/intl/collator/default-locale.js
|
||
|
+++ b/test/intl/collator/default-locale.js
|
||
|
@@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
||
|
assertFalse(options.locale === '');
|
||
|
assertFalse(options.locale === undefined);
|
||
|
|
||
|
-// Then check for equality.
|
||
|
-assertEquals(options.locale, %GetDefaultICULocale());
|
||
|
+// Then check for legitimacy.
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||
|
|
||
|
var collatorNone = new Intl.Collator();
|
||
|
assertEquals(options.locale, collatorNone.resolvedOptions().locale);
|
||
|
@@ -48,5 +48,8 @@ var collatorBraket = new Intl.Collator({});
|
||
|
assertEquals(options.locale, collatorBraket.resolvedOptions().locale);
|
||
|
|
||
|
var collatorWithOptions = new Intl.Collator(undefined, {usage: 'search'});
|
||
|
-assertEquals(%GetDefaultICULocale() + '-u-co-search',
|
||
|
- collatorWithOptions.resolvedOptions().locale);
|
||
|
+assertLanguageTag(%GetDefaultICULocale(),
|
||
|
+ collatorWithOptions.resolvedOptions().locale);
|
||
|
+assertNotNull(
|
||
|
+ %regexp_internal_match(/-u(-[a-zA-Z]+-[a-zA-Z]+)*-co-search/,
|
||
|
+ collatorWithOptions.resolvedOptions().locale));
|
||
|
diff --git a/test/intl/collator/wellformed-unsupported-locale.js b/test/intl/collator/wellformed-unsupported-locale.js
|
||
|
index 3963d47a61..ad89e3e220 100644
|
||
|
--- a/test/intl/collator/wellformed-unsupported-locale.js
|
||
|
+++ b/test/intl/collator/wellformed-unsupported-locale.js
|
||
|
@@ -29,4 +29,4 @@
|
||
|
|
||
|
var collator = Intl.Collator(['xx']);
|
||
|
|
||
|
-assertEquals(collator.resolvedOptions().locale, %GetDefaultICULocale());
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), collator.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/date-format/default-locale.js b/test/intl/date-format/default-locale.js
|
||
|
index 8e9b7fcec3..2d79e895b5 100644
|
||
|
--- a/test/intl/date-format/default-locale.js
|
||
|
+++ b/test/intl/date-format/default-locale.js
|
||
|
@@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
||
|
assertFalse(options.locale === '');
|
||
|
assertFalse(options.locale === undefined);
|
||
|
|
||
|
-// Then check for equality.
|
||
|
-assertEquals(options.locale, %GetDefaultICULocale());
|
||
|
+// Then check for legitimacy.
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||
|
|
||
|
var dtfNone = new Intl.DateTimeFormat();
|
||
|
assertEquals(options.locale, dtfNone.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/date-format/wellformed-unsupported-locale.js b/test/intl/date-format/wellformed-unsupported-locale.js
|
||
|
index 6f063abbd1..b812164832 100644
|
||
|
--- a/test/intl/date-format/wellformed-unsupported-locale.js
|
||
|
+++ b/test/intl/date-format/wellformed-unsupported-locale.js
|
||
|
@@ -29,4 +29,4 @@
|
||
|
|
||
|
var dtf = Intl.DateTimeFormat(['xx']);
|
||
|
|
||
|
-assertEquals(dtf.resolvedOptions().locale, %GetDefaultICULocale());
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), dtf.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/number-format/default-locale.js b/test/intl/number-format/default-locale.js
|
||
|
index cd67ba724f..a24aec2333 100644
|
||
|
--- a/test/intl/number-format/default-locale.js
|
||
|
+++ b/test/intl/number-format/default-locale.js
|
||
|
@@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
||
|
assertFalse(options.locale === '');
|
||
|
assertFalse(options.locale === undefined);
|
||
|
|
||
|
-// Then check for equality.
|
||
|
-assertEquals(options.locale, %GetDefaultICULocale());
|
||
|
+// Then check for legitimacy.
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||
|
|
||
|
var nfNone = new Intl.NumberFormat();
|
||
|
assertEquals(options.locale, nfNone.resolvedOptions().locale);
|
||
|
diff --git a/test/intl/number-format/wellformed-unsupported-locale.js b/test/intl/number-format/wellformed-unsupported-locale.js
|
||
|
index 195eba4c19..c51753928e 100644
|
||
|
--- a/test/intl/number-format/wellformed-unsupported-locale.js
|
||
|
+++ b/test/intl/number-format/wellformed-unsupported-locale.js
|
||
|
@@ -29,4 +29,4 @@
|
||
|
|
||
|
var nf = Intl.NumberFormat(['xx']);
|
||
|
|
||
|
-assertEquals(nf.resolvedOptions().locale, %GetDefaultICULocale());
|
||
|
+assertLanguageTag(%GetDefaultICULocale(), nf.resolvedOptions().locale);
|
||
|
diff --git a/test/mjsunit/regress/regress-6288.js b/test/mjsunit/regress/regress-6288.js
|
||
|
index 337af54c1a..5f550c31c8 100644
|
||
|
--- a/test/mjsunit/regress/regress-6288.js
|
||
|
+++ b/test/mjsunit/regress/regress-6288.js
|
||
|
@@ -8,6 +8,6 @@
|
||
|
// DateTimeFormat but not Collation
|
||
|
|
||
|
if (this.Intl) {
|
||
|
- assertEquals('und', Intl.Collator().resolvedOptions().locale);
|
||
|
+ assertEquals('pt', Intl.Collator().resolvedOptions().locale);
|
||
|
assertEquals('pt-BR', Intl.DateTimeFormat().resolvedOptions().locale);
|
||
|
}
|