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) { |