Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(418)

Unified Diff: src/i18n.cc

Issue 2736543002: [intl] Implement Intl.PluralRules
Patch Set: clarify comment Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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) {

Powered by Google App Engine
This is Rietveld 408576698