Chromium Code Reviews| Index: src/i18n.cc |
| diff --git a/src/i18n.cc b/src/i18n.cc |
| index 18a9dfdf306e71eca6eb598bd218e85b652b832c..4547c6a123c8bf6625016880003b17b5e0cf2c9c 100644 |
| --- a/src/i18n.cc |
| +++ b/src/i18n.cc |
| @@ -11,6 +11,7 @@ |
| #include "src/factory.h" |
| #include "src/isolate.h" |
| #include "src/objects-inl.h" |
| +#include "src/property-descriptor.h" |
| #include "unicode/brkiter.h" |
| #include "unicode/calendar.h" |
| #include "unicode/coll.h" |
| @@ -23,6 +24,7 @@ |
| #include "unicode/locid.h" |
| #include "unicode/numfmt.h" |
| #include "unicode/numsys.h" |
| +#include "unicode/plurrule.h" |
| #include "unicode/rbbi.h" |
| #include "unicode/smpdtfmt.h" |
| #include "unicode/timezone.h" |
| @@ -30,6 +32,7 @@ |
| #include "unicode/ucol.h" |
| #include "unicode/ucurr.h" |
| #include "unicode/unum.h" |
| +#include "unicode/upluralrules.h" |
| #include "unicode/uversion.h" |
| namespace v8 { |
| @@ -224,6 +227,41 @@ void SetResolvedDateSettings(Isolate* isolate, |
| } |
| } |
| +void SetNumericSettings(Isolate* isolate, icu::DecimalFormat* number_format, |
| + Handle<JSObject> options) { |
| + int32_t digits; |
| + if (ExtractIntegerSetting(isolate, options, "minimumIntegerDigits", |
| + &digits)) { |
| + number_format->setMinimumIntegerDigits(digits); |
| + } |
| + |
| + if (ExtractIntegerSetting(isolate, options, "minimumFractionDigits", |
| + &digits)) { |
| + number_format->setMinimumFractionDigits(digits); |
| + } |
| + |
| + if (ExtractIntegerSetting(isolate, options, "maximumFractionDigits", |
| + &digits)) { |
| + number_format->setMaximumFractionDigits(digits); |
| + } |
| + |
| + bool significant_digits_used = false; |
| + if (ExtractIntegerSetting(isolate, options, "minimumSignificantDigits", |
| + &digits)) { |
| + number_format->setMinimumSignificantDigits(digits); |
| + significant_digits_used = true; |
| + } |
| + |
| + if (ExtractIntegerSetting(isolate, options, "maximumSignificantDigits", |
| + &digits)) { |
| + number_format->setMaximumSignificantDigits(digits); |
| + significant_digits_used = true; |
| + } |
| + |
| + number_format->setSignificantDigitsUsed(significant_digits_used); |
| + |
| + number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); |
| +} |
| icu::DecimalFormat* CreateICUNumberFormat( |
| Isolate* isolate, |
| @@ -294,46 +332,77 @@ icu::DecimalFormat* CreateICUNumberFormat( |
| number_format->setCurrency(currency.getBuffer(), status); |
| } |
| - int32_t digits; |
| - if (ExtractIntegerSetting( |
| - isolate, options, "minimumIntegerDigits", &digits)) { |
| - number_format->setMinimumIntegerDigits(digits); |
| - } |
| + SetNumericSettings(isolate, number_format, options); |
| - if (ExtractIntegerSetting( |
| - isolate, options, "minimumFractionDigits", &digits)) { |
| - number_format->setMinimumFractionDigits(digits); |
| + bool grouping; |
| + if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) { |
| + number_format->setGroupingUsed(grouping); |
| } |
| - if (ExtractIntegerSetting( |
| - isolate, options, "maximumFractionDigits", &digits)) { |
| - number_format->setMaximumFractionDigits(digits); |
| - } |
| + return number_format; |
| +} |
| - bool significant_digits_used = false; |
| - if (ExtractIntegerSetting( |
| - isolate, options, "minimumSignificantDigits", &digits)) { |
| - number_format->setMinimumSignificantDigits(digits); |
| - significant_digits_used = true; |
| - } |
| +void SetResolvedNumericSettings(Isolate* isolate, const icu::Locale& icu_locale, |
| + icu::DecimalFormat* number_format, |
| + Handle<JSObject> resolved) { |
| + Factory* factory = isolate->factory(); |
| - if (ExtractIntegerSetting( |
| - isolate, options, "maximumSignificantDigits", &digits)) { |
| - number_format->setMaximumSignificantDigits(digits); |
| - significant_digits_used = true; |
| - } |
| + JSObject::SetProperty( |
| + resolved, factory->NewStringFromStaticChars("minimumIntegerDigits"), |
| + factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()), |
| + SLOPPY) |
| + .Assert(); |
| - number_format->setSignificantDigitsUsed(significant_digits_used); |
| + JSObject::SetProperty( |
| + resolved, factory->NewStringFromStaticChars("minimumFractionDigits"), |
| + factory->NewNumberFromInt(number_format->getMinimumFractionDigits()), |
| + SLOPPY) |
| + .Assert(); |
| - bool grouping; |
| - if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) { |
| - number_format->setGroupingUsed(grouping); |
| + JSObject::SetProperty( |
| + resolved, factory->NewStringFromStaticChars("maximumFractionDigits"), |
| + factory->NewNumberFromInt(number_format->getMaximumFractionDigits()), |
| + SLOPPY) |
| + .Assert(); |
| + |
| + Handle<String> key = |
| + factory->NewStringFromStaticChars("minimumSignificantDigits"); |
| + Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key); |
| + CHECK(maybe.IsJust()); |
| + if (maybe.FromJust()) { |
| + JSObject::SetProperty( |
| + resolved, factory->NewStringFromStaticChars("minimumSignificantDigits"), |
| + factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()), |
| + SLOPPY) |
| + .Assert(); |
| } |
| - // Set rounding mode. |
| - number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); |
| + key = factory->NewStringFromStaticChars("maximumSignificantDigits"); |
| + maybe = JSReceiver::HasOwnProperty(resolved, key); |
| + CHECK(maybe.IsJust()); |
| + if (maybe.FromJust()) { |
| + JSObject::SetProperty( |
| + resolved, factory->NewStringFromStaticChars("maximumSignificantDigits"), |
| + factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()), |
| + SLOPPY) |
| + .Assert(); |
| + } |
| - return number_format; |
| + // Set the locale |
| + char result[ULOC_FULLNAME_CAPACITY]; |
| + UErrorCode status = U_ZERO_ERROR; |
| + uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, |
| + FALSE, &status); |
| + if (U_SUCCESS(status)) { |
| + JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), |
| + factory->NewStringFromAsciiChecked(result), SLOPPY) |
| + .Assert(); |
| + } else { |
| + // This would never happen, since we got the locale from ICU. |
| + JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), |
| + factory->NewStringFromStaticChars("und"), SLOPPY) |
| + .Assert(); |
| + } |
| } |
| @@ -342,15 +411,6 @@ void SetResolvedNumberSettings(Isolate* isolate, |
| icu::DecimalFormat* number_format, |
| Handle<JSObject> resolved) { |
| Factory* factory = isolate->factory(); |
| - icu::UnicodeString pattern; |
| - number_format->toPattern(pattern); |
| - JSObject::SetProperty( |
| - resolved, factory->intl_pattern_symbol(), |
| - factory->NewStringFromTwoByte( |
| - Vector<const uint16_t>( |
| - reinterpret_cast<const uint16_t*>(pattern.getBuffer()), |
| - pattern.length())).ToHandleChecked(), |
| - SLOPPY).Assert(); |
| // Set resolved currency code in options.currency if not empty. |
| icu::UnicodeString currency(number_format->getCurrency()); |
| @@ -386,57 +446,7 @@ void SetResolvedNumberSettings(Isolate* isolate, |
| resolved, factory->NewStringFromStaticChars("useGrouping"), |
| factory->ToBoolean(number_format->isGroupingUsed()), SLOPPY).Assert(); |
| - JSObject::SetProperty( |
| - resolved, factory->NewStringFromStaticChars("minimumIntegerDigits"), |
| - factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()), |
| - SLOPPY).Assert(); |
| - |
| - JSObject::SetProperty( |
| - resolved, factory->NewStringFromStaticChars("minimumFractionDigits"), |
| - factory->NewNumberFromInt(number_format->getMinimumFractionDigits()), |
| - SLOPPY).Assert(); |
| - |
| - JSObject::SetProperty( |
| - resolved, factory->NewStringFromStaticChars("maximumFractionDigits"), |
| - factory->NewNumberFromInt(number_format->getMaximumFractionDigits()), |
| - SLOPPY).Assert(); |
| - |
| - Handle<String> key = |
| - factory->NewStringFromStaticChars("minimumSignificantDigits"); |
| - Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key); |
| - CHECK(maybe.IsJust()); |
| - if (maybe.FromJust()) { |
| - JSObject::SetProperty( |
| - resolved, factory->NewStringFromStaticChars("minimumSignificantDigits"), |
| - factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()), |
| - SLOPPY).Assert(); |
| - } |
| - |
| - key = factory->NewStringFromStaticChars("maximumSignificantDigits"); |
| - maybe = JSReceiver::HasOwnProperty(resolved, key); |
| - CHECK(maybe.IsJust()); |
| - if (maybe.FromJust()) { |
| - JSObject::SetProperty( |
| - resolved, factory->NewStringFromStaticChars("maximumSignificantDigits"), |
| - factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()), |
| - SLOPPY).Assert(); |
| - } |
| - |
| - // Set the locale |
| - char result[ULOC_FULLNAME_CAPACITY]; |
| - status = U_ZERO_ERROR; |
| - uloc_toLanguageTag( |
| - icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); |
| - if (U_SUCCESS(status)) { |
| - JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), |
| - factory->NewStringFromAsciiChecked(result), |
| - SLOPPY).Assert(); |
| - } else { |
| - // This would never happen, since we got the locale from ICU. |
| - JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), |
| - factory->NewStringFromStaticChars("und"), |
| - SLOPPY).Assert(); |
| - } |
| + SetResolvedNumericSettings(isolate, icu_locale, number_format, resolved); |
| } |
| @@ -610,6 +620,91 @@ void SetResolvedCollatorSettings(Isolate* isolate, |
| } |
| } |
| +bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale, |
| + Handle<JSObject> options, icu::PluralRules** pl, |
| + icu::DecimalFormat** nf) { |
| + // Make formatter from options. Numbering system is added |
| + // to the locale as Unicode extension (if it was specified at all). |
| + UErrorCode status = U_ZERO_ERROR; |
| + |
| + UPluralType type = UPLURAL_TYPE_CARDINAL; |
| + |
| + icu::UnicodeString type_string; |
| + if (ExtractStringSetting(isolate, options, "type", &type_string)) { |
| + if (type_string == UNICODE_STRING_SIMPLE("ordinal")) { |
| + type = UPLURAL_TYPE_ORDINAL; |
| + } else { |
| + CHECK(type_string == UNICODE_STRING_SIMPLE("cardinal")); |
| + } |
| + } |
| + |
| + icu::PluralRules* plural_rules = |
| + icu::PluralRules::forLocale(icu_locale, type, status); |
| + |
| + if (U_FAILURE(status)) { |
| + delete plural_rules; |
| + return false; |
| + } |
|
jungshik at Google
2017/05/08 19:16:54
nit:
NumberFormat::createInstance(....) will retu
|
| + |
| + icu::DecimalFormat* number_format = static_cast<icu::DecimalFormat*>( |
| + icu::NumberFormat::createInstance(icu_locale, status)); |
| + |
| + if (U_FAILURE(status)) { |
| + delete plural_rules; |
| + delete number_format; |
| + return false; |
| + } |
| + |
| + *pl = plural_rules; |
| + *nf = number_format; |
| + |
| + SetNumericSettings(isolate, number_format, options); |
| + |
| + // Set rounding mode. |
| + |
| + return true; |
| +} |
| + |
| +bool SetResolvedPluralRulesSettings(Isolate* isolate, |
| + const icu::Locale& icu_locale, |
| + icu::PluralRules* plural_rules, |
| + icu::DecimalFormat* number_format, |
| + Handle<JSObject> resolved) { |
| + SetResolvedNumericSettings(isolate, icu_locale, number_format, resolved); |
| + |
| + Factory* factory = isolate->factory(); |
| + |
| + Handle<JSObject> pluralCategories = Handle<JSObject>::cast( |
| + JSObject::GetProperty( |
| + resolved, factory->NewStringFromStaticChars("pluralCategories")) |
| + .ToHandleChecked()); |
| + |
| + UErrorCode status = U_ZERO_ERROR; |
| + std::unique_ptr<icu::StringEnumeration> categories( |
| + plural_rules->getKeywords(status)); |
| + if (!U_SUCCESS(status)) return false; |
|
jungshik at Google
2017/05/08 19:16:54
nit: U_FAILURE(status) ?
Again, checking at line
|
| + |
| + int32_t count = categories->count(status); |
| + if (!U_SUCCESS(status)) return false; |
|
jungshik at Google
2017/05/08 19:16:54
nit: U_FAILURE(status)
|
| + |
| + for (int32_t i = 0; i < count; i++) { |
| + const icu::UnicodeString* category = categories->snext(status); |
| + if (!U_SUCCESS(status)) return false; |
| + |
| + Handle<String> value = |
| + factory |
| + ->NewStringFromTwoByte(Vector<const uint16_t>( |
| + reinterpret_cast<const uint16_t*>(category->getBuffer()), |
| + category->length())) |
| + .ToHandleChecked(); |
| + LookupIterator it(isolate, pluralCategories, i, LookupIterator::OWN); |
| + JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, |
| + PropertyAttributes::NONE) |
| + .ToHandleChecked(); |
| + } |
| + |
| + return true; |
| +} |
| icu::BreakIterator* CreateICUBreakIterator( |
| Isolate* isolate, |
| @@ -834,6 +929,65 @@ void Collator::DeleteCollator(const v8::WeakCallbackInfo<void>& data) { |
| GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); |
| } |
| +bool PluralRules::InitializePluralRules(Isolate* isolate, Handle<String> locale, |
| + Handle<JSObject> options, |
| + Handle<JSObject> resolved, |
| + icu::PluralRules** plural_rules, |
| + icu::DecimalFormat** number_format) { |
| + // Convert BCP47 into ICU locale format. |
|
jungshik at Google
2017/05/08 19:16:54
Need to refactor (may be in another CL if you want
|
| + UErrorCode status = U_ZERO_ERROR; |
| + icu::Locale icu_locale; |
| + char icu_result[ULOC_FULLNAME_CAPACITY]; |
| + int icu_length = 0; |
| + v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); |
| + if (bcp47_locale.length() != 0) { |
| + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, |
| + &icu_length, &status); |
| + if (U_FAILURE(status) || icu_length == 0) { |
| + return false; |
| + } |
| + icu_locale = icu::Locale(icu_result); |
| + } |
| + |
| + bool success = CreateICUPluralRules(isolate, icu_locale, options, |
| + plural_rules, number_format); |
| + if (!success) { |
| + // Remove extensions and try again. |
| + icu::Locale no_extension_locale(icu_locale.getBaseName()); |
| + success = CreateICUPluralRules(isolate, no_extension_locale, options, |
| + plural_rules, number_format); |
| + |
| + if (!success) { |
| + FATAL("Failed to create ICU PluralRules, are ICU data files missing?"); |
| + } |
| + |
| + // Set resolved settings (pattern, numbering system). |
| + success = SetResolvedPluralRulesSettings( |
| + isolate, no_extension_locale, *plural_rules, *number_format, resolved); |
| + } else { |
| + success = SetResolvedPluralRulesSettings(isolate, icu_locale, *plural_rules, |
| + *number_format, resolved); |
| + } |
| + |
| + return success; |
| +} |
| + |
| +icu::PluralRules* PluralRules::UnpackPluralRules(Isolate* isolate, |
| + Handle<JSObject> obj) { |
| + return reinterpret_cast<icu::PluralRules*>(obj->GetInternalField(0)); |
| +} |
| + |
| +icu::DecimalFormat* PluralRules::UnpackNumberFormat(Isolate* isolate, |
| + Handle<JSObject> obj) { |
| + return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(1)); |
| +} |
| + |
| +void PluralRules::DeletePluralRules(const v8::WeakCallbackInfo<void>& data) { |
| + delete reinterpret_cast<icu::PluralRules*>(data.GetInternalField(0)); |
| + delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(1)); |
| + GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); |
| +} |
| + |
| icu::BreakIterator* V8BreakIterator::InitializeBreakIterator( |
| Isolate* isolate, Handle<String> locale, Handle<JSObject> options, |
| Handle<JSObject> resolved) { |