deps: cherry-pick 6989b3f6d7 from V8 upstream
Original commit message:
Fix default Intl language tag handling
With certain ICU data bundles (such as the Node.js "small-icu"),
%GetDefaultICULocale() may return a more specific language tag (e.g.
"en-US") than what's available (e.g. "en"). In those cases, consider the
more specific language tag supported.
This CL also resolves the following Node.js issue:
https://github.com/nodejs/node/issues/15223
Bug: v8:7024
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ifda0776b3418734d5caa8af4e50c17cda95add73
Reviewed-on: https://chromium-review.googlesource.com/668350
Commit-Queue: Daniel Ehrenberg <littledan@chromium.org>
Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52716}
PR-URL: https://github.com/nodejs/node/pull/20826
Fixes: https://github.com/nodejs/node/issues/15223
Refs: 6989b3f6d7
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
f781758d41
commit
a60ab57c3c
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
# Reset this number to 0 on major V8 upgrades.
|
# Reset this number to 0 on major V8 upgrades.
|
||||||
# Increment by one for each non-official patch applied to deps/v8.
|
# Increment by one for each non-official patch applied to deps/v8.
|
||||||
'v8_embedder_string': '-node.6',
|
'v8_embedder_string': '-node.7',
|
||||||
|
|
||||||
# Enable disassembler for `--print-code` v8 options
|
# Enable disassembler for `--print-code` v8 options
|
||||||
'v8_enable_disassembler': 1,
|
'v8_enable_disassembler': 1,
|
||||||
|
89
deps/v8/src/js/intl.js
vendored
89
deps/v8/src/js/intl.js
vendored
@ -152,18 +152,11 @@ var AVAILABLE_LOCALES = {
|
|||||||
*/
|
*/
|
||||||
var DEFAULT_ICU_LOCALE = UNDEFINED;
|
var DEFAULT_ICU_LOCALE = UNDEFINED;
|
||||||
|
|
||||||
function GetDefaultICULocaleJS(service) {
|
function GetDefaultICULocaleJS() {
|
||||||
if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
|
if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
|
||||||
DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
|
DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
|
||||||
}
|
}
|
||||||
// Check that this is a valid default for this service,
|
return DEFAULT_ICU_LOCALE;
|
||||||
// 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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
* Returns best matched supported locale and extension info using basic
|
||||||
* lookup algorithm.
|
* lookup algorithm.
|
||||||
@ -446,31 +481,25 @@ function lookupMatcher(service, requestedLocales) {
|
|||||||
var availableLocales = getAvailableLocalesOf(service);
|
var availableLocales = getAvailableLocalesOf(service);
|
||||||
|
|
||||||
for (var i = 0; i < requestedLocales.length; ++i) {
|
for (var i = 0; i < requestedLocales.length; ++i) {
|
||||||
// Remove all extensions.
|
var result = attemptSingleLookup(availableLocales, requestedLocales[i]);
|
||||||
var locale = %RegExpInternalReplace(
|
if (!IS_UNDEFINED(result)) {
|
||||||
GetAnyExtensionRE(), requestedLocales[i], '');
|
return result;
|
||||||
do {
|
}
|
||||||
if (!IS_UNDEFINED(availableLocales[locale])) {
|
}
|
||||||
// Return the resolved locale and extension.
|
|
||||||
var extensionMatch = %regexp_internal_match(
|
var defLocale = GetDefaultICULocaleJS();
|
||||||
GetUnicodeExtensionRE(), requestedLocales[i]);
|
|
||||||
var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
|
// While ECMA-402 returns defLocale directly, we have to check if it is
|
||||||
return {locale: locale, extension: extension, position: i};
|
// supported, as such support is not guaranteed.
|
||||||
}
|
var result = attemptSingleLookup(availableLocales, defLocale);
|
||||||
// Truncate locale if possible.
|
if (!IS_UNDEFINED(result)) {
|
||||||
var pos = %StringLastIndexOf(locale, '-');
|
return result;
|
||||||
if (pos === -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
locale = %_Call(StringSubstring, locale, 0, pos);
|
|
||||||
} while (true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't find a match, return default.
|
// Didn't find a match, return default.
|
||||||
return {
|
return {
|
||||||
locale: GetDefaultICULocaleJS(service),
|
locale: 'und',
|
||||||
extension: '',
|
extension: ''
|
||||||
position: -1
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
deps/v8/test/intl/assert.js
vendored
41
deps/v8/test/intl/assert.js
vendored
@ -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.
|
* Runs code() and asserts that it throws the specified exception.
|
||||||
*/
|
*/
|
||||||
@ -189,3 +199,34 @@ function assertInstanceof(obj, type) {
|
|||||||
(actualTypeName ? ' but of < ' + actualTypeName + '>' : ''));
|
(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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
|||||||
assertFalse(options.locale === '');
|
assertFalse(options.locale === '');
|
||||||
assertFalse(options.locale === undefined);
|
assertFalse(options.locale === undefined);
|
||||||
|
|
||||||
// Then check for equality.
|
// Then check for legitimacy.
|
||||||
assertEquals(options.locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||||||
|
|
||||||
var iteratorNone = new Intl.v8BreakIterator();
|
var iteratorNone = new Intl.v8BreakIterator();
|
||||||
assertEquals(options.locale, iteratorNone.resolvedOptions().locale);
|
assertEquals(options.locale, iteratorNone.resolvedOptions().locale);
|
||||||
|
@ -29,4 +29,4 @@
|
|||||||
|
|
||||||
var iterator = Intl.v8BreakIterator(['xx']);
|
var iterator = Intl.v8BreakIterator(['xx']);
|
||||||
|
|
||||||
assertEquals(iterator.resolvedOptions().locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), iterator.resolvedOptions().locale);
|
||||||
|
11
deps/v8/test/intl/collator/default-locale.js
vendored
11
deps/v8/test/intl/collator/default-locale.js
vendored
@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
|||||||
assertFalse(options.locale === '');
|
assertFalse(options.locale === '');
|
||||||
assertFalse(options.locale === undefined);
|
assertFalse(options.locale === undefined);
|
||||||
|
|
||||||
// Then check for equality.
|
// Then check for legitimacy.
|
||||||
assertEquals(options.locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||||||
|
|
||||||
var collatorNone = new Intl.Collator();
|
var collatorNone = new Intl.Collator();
|
||||||
assertEquals(options.locale, collatorNone.resolvedOptions().locale);
|
assertEquals(options.locale, collatorNone.resolvedOptions().locale);
|
||||||
@ -48,5 +48,8 @@ var collatorBraket = new Intl.Collator({});
|
|||||||
assertEquals(options.locale, collatorBraket.resolvedOptions().locale);
|
assertEquals(options.locale, collatorBraket.resolvedOptions().locale);
|
||||||
|
|
||||||
var collatorWithOptions = new Intl.Collator(undefined, {usage: 'search'});
|
var collatorWithOptions = new Intl.Collator(undefined, {usage: 'search'});
|
||||||
assertEquals(%GetDefaultICULocale() + '-u-co-search',
|
assertLanguageTag(%GetDefaultICULocale(),
|
||||||
collatorWithOptions.resolvedOptions().locale);
|
collatorWithOptions.resolvedOptions().locale);
|
||||||
|
assertNotNull(
|
||||||
|
%regexp_internal_match(/-u(-[a-zA-Z]+-[a-zA-Z]+)*-co-search/,
|
||||||
|
collatorWithOptions.resolvedOptions().locale));
|
||||||
|
@ -29,4 +29,4 @@
|
|||||||
|
|
||||||
var collator = Intl.Collator(['xx']);
|
var collator = Intl.Collator(['xx']);
|
||||||
|
|
||||||
assertEquals(collator.resolvedOptions().locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), collator.resolvedOptions().locale);
|
||||||
|
@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
|||||||
assertFalse(options.locale === '');
|
assertFalse(options.locale === '');
|
||||||
assertFalse(options.locale === undefined);
|
assertFalse(options.locale === undefined);
|
||||||
|
|
||||||
// Then check for equality.
|
// Then check for legitimacy.
|
||||||
assertEquals(options.locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||||||
|
|
||||||
var dtfNone = new Intl.DateTimeFormat();
|
var dtfNone = new Intl.DateTimeFormat();
|
||||||
assertEquals(options.locale, dtfNone.resolvedOptions().locale);
|
assertEquals(options.locale, dtfNone.resolvedOptions().locale);
|
||||||
|
@ -29,4 +29,4 @@
|
|||||||
|
|
||||||
var dtf = Intl.DateTimeFormat(['xx']);
|
var dtf = Intl.DateTimeFormat(['xx']);
|
||||||
|
|
||||||
assertEquals(dtf.resolvedOptions().locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), dtf.resolvedOptions().locale);
|
||||||
|
@ -37,8 +37,8 @@ assertFalse(options.locale === 'und');
|
|||||||
assertFalse(options.locale === '');
|
assertFalse(options.locale === '');
|
||||||
assertFalse(options.locale === undefined);
|
assertFalse(options.locale === undefined);
|
||||||
|
|
||||||
// Then check for equality.
|
// Then check for legitimacy.
|
||||||
assertEquals(options.locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), options.locale);
|
||||||
|
|
||||||
var nfNone = new Intl.NumberFormat();
|
var nfNone = new Intl.NumberFormat();
|
||||||
assertEquals(options.locale, nfNone.resolvedOptions().locale);
|
assertEquals(options.locale, nfNone.resolvedOptions().locale);
|
||||||
|
@ -29,4 +29,4 @@
|
|||||||
|
|
||||||
var nf = Intl.NumberFormat(['xx']);
|
var nf = Intl.NumberFormat(['xx']);
|
||||||
|
|
||||||
assertEquals(nf.resolvedOptions().locale, %GetDefaultICULocale());
|
assertLanguageTag(%GetDefaultICULocale(), nf.resolvedOptions().locale);
|
||||||
|
2
deps/v8/test/mjsunit/regress/regress-6288.js
vendored
2
deps/v8/test/mjsunit/regress/regress-6288.js
vendored
@ -8,6 +8,6 @@
|
|||||||
// DateTimeFormat but not Collation
|
// DateTimeFormat but not Collation
|
||||||
|
|
||||||
if (this.Intl) {
|
if (this.Intl) {
|
||||||
assertEquals('und', Intl.Collator().resolvedOptions().locale);
|
assertEquals('pt', Intl.Collator().resolvedOptions().locale);
|
||||||
assertEquals('pt-BR', Intl.DateTimeFormat().resolvedOptions().locale);
|
assertEquals('pt-BR', Intl.DateTimeFormat().resolvedOptions().locale);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user