5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-plural-rules.h" 11 #include "src/isolate-inl.h" 12 #include "src/objects/intl-objects.h" 13 #include "src/objects/js-plural-rules-inl.h" 14 #include "unicode/decimfmt.h" 15 #include "unicode/locid.h" 16 #include "unicode/numfmt.h" 17 #include "unicode/plurrule.h" 24 bool CreateICUPluralRules(Isolate* isolate,
const icu::Locale& icu_locale,
25 JSPluralRules::Type type,
26 std::unique_ptr<icu::PluralRules>* pl,
27 std::unique_ptr<icu::DecimalFormat>* nf) {
30 UErrorCode status = U_ZERO_ERROR;
32 UPluralType icu_type = UPLURAL_TYPE_CARDINAL;
33 if (type == JSPluralRules::Type::ORDINAL) {
34 icu_type = UPLURAL_TYPE_ORDINAL;
36 CHECK_EQ(JSPluralRules::Type::CARDINAL, type);
39 std::unique_ptr<icu::PluralRules> plural_rules(
40 icu::PluralRules::forLocale(icu_locale, icu_type, status));
41 if (U_FAILURE(status)) {
44 CHECK_NOT_NULL(plural_rules.get());
46 std::unique_ptr<icu::DecimalFormat> number_format(
47 static_cast<icu::DecimalFormat*>(
48 icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status)));
49 if (U_FAILURE(status)) {
52 CHECK_NOT_NULL(number_format.get());
54 *pl = std::move(plural_rules);
55 *nf = std::move(number_format);
60 void InitializeICUPluralRules(
61 Isolate* isolate,
const icu::Locale& icu_locale, JSPluralRules::Type type,
62 std::unique_ptr<icu::PluralRules>* plural_rules,
63 std::unique_ptr<icu::DecimalFormat>* number_format) {
64 bool success = CreateICUPluralRules(isolate, icu_locale, type, plural_rules,
68 icu::Locale no_extension_locale(icu_locale.getBaseName());
69 success = CreateICUPluralRules(isolate, no_extension_locale, type,
70 plural_rules, number_format);
73 FATAL(
"Failed to create ICU PluralRules, are ICU data files missing?");
77 CHECK_NOT_NULL((*plural_rules).get());
78 CHECK_NOT_NULL((*number_format).get());
83 Handle<String> JSPluralRules::TypeAsString()
const {
86 return GetReadOnlyRoots().cardinal_string_handle();
88 return GetReadOnlyRoots().ordinal_string_handle();
95 MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
96 Isolate* isolate, Handle<JSPluralRules> plural_rules,
97 Handle<Object> locales, Handle<Object> options_obj) {
98 plural_rules->set_flags(0);
100 Maybe<std::vector<std::string>> maybe_requested_locales =
101 Intl::CanonicalizeLocaleList(isolate, locales);
102 MAYBE_RETURN(maybe_requested_locales, Handle<JSPluralRules>());
103 std::vector<std::string> requested_locales =
104 maybe_requested_locales.FromJust();
107 if (options_obj->IsUndefined(isolate)) {
109 options_obj = isolate->factory()->NewJSObjectWithNullProto();
113 ASSIGN_RETURN_ON_EXCEPTION(
114 isolate, options_obj,
115 Object::ToObject(isolate, options_obj,
"Intl.PluralRules"),
120 Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
125 Maybe<Intl::MatcherOption> maybe_locale_matcher =
126 Intl::GetLocaleMatcher(isolate, options,
"Intl.PluralRules");
127 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSPluralRules>());
128 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
132 Maybe<Type> maybe_type = Intl::GetStringOption<Type>(
133 isolate, options,
"type",
"Intl.PluralRules", {
"cardinal",
"ordinal"},
134 {Type::CARDINAL, Type::ORDINAL}, Type::CARDINAL);
135 MAYBE_RETURN(maybe_type, MaybeHandle<JSPluralRules>());
136 Type type = maybe_type.FromJust();
139 plural_rules->set_type(type);
150 Intl::ResolvedLocale r =
151 Intl::ResolveLocale(isolate, JSPluralRules::GetAvailableLocales(),
152 requested_locales, matcher, {});
155 icu::Locale icu_locale = r.icu_locale;
156 DCHECK(!icu_locale.isBogus());
158 std::map<std::string, std::string> extensions = r.extensions;
161 Handle<String> locale_str =
162 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
163 plural_rules->set_locale(*locale_str);
165 std::unique_ptr<icu::PluralRules> icu_plural_rules;
166 std::unique_ptr<icu::DecimalFormat> icu_decimal_format;
167 InitializeICUPluralRules(isolate, icu_locale, type, &icu_plural_rules,
168 &icu_decimal_format);
169 CHECK_NOT_NULL(icu_plural_rules.get());
170 CHECK_NOT_NULL(icu_decimal_format.get());
173 Maybe<bool> done = Intl::SetNumberFormatDigitOptions(
174 isolate, icu_decimal_format.get(), options, 0, 3);
175 MAYBE_RETURN(done, MaybeHandle<JSPluralRules>());
177 Handle<Managed<icu::PluralRules>> managed_plural_rules =
178 Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
179 std::move(icu_plural_rules));
180 plural_rules->set_icu_plural_rules(*managed_plural_rules);
182 Handle<Managed<icu::DecimalFormat>> managed_decimal_format =
183 Managed<icu::DecimalFormat>::FromUniquePtr(isolate, 0,
184 std::move(icu_decimal_format));
185 plural_rules->set_icu_decimal_format(*managed_decimal_format);
191 MaybeHandle<String> JSPluralRules::ResolvePlural(
192 Isolate* isolate, Handle<JSPluralRules> plural_rules,
double number) {
193 icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
194 CHECK_NOT_NULL(icu_plural_rules);
196 icu::DecimalFormat* icu_decimal_format =
197 plural_rules->icu_decimal_format()->raw();
198 CHECK_NOT_NULL(icu_decimal_format);
207 icu::UnicodeString rounded_string;
208 icu_decimal_format->format(number, rounded_string);
210 icu::Formattable formattable;
211 UErrorCode status = U_ZERO_ERROR;
212 icu_decimal_format->parse(rounded_string, formattable, status);
213 CHECK(U_SUCCESS(status));
215 double rounded = formattable.getDouble(status);
216 CHECK(U_SUCCESS(status));
218 icu::UnicodeString result = icu_plural_rules->select(rounded);
219 return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
220 reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
225 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
226 Handle<Object> value,
const char* key) {
227 Handle<String> key_str = isolate->factory()->NewStringFromAsciiChecked(key);
231 CHECK(JSReceiver::CreateDataProperty(isolate, options, key_str, value,
236 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
237 int value,
const char* key) {
238 Handle<Smi> value_smi(Smi::FromInt(value), isolate);
239 CreateDataPropertyForOptions(isolate, options, value_smi, key);
244 Handle<JSObject> JSPluralRules::ResolvedOptions(
245 Isolate* isolate, Handle<JSPluralRules> plural_rules) {
246 Handle<JSObject> options =
247 isolate->factory()->NewJSObject(isolate->object_function());
249 Handle<String> locale_value(plural_rules->locale(), isolate);
250 CreateDataPropertyForOptions(isolate, options, locale_value,
"locale");
252 CreateDataPropertyForOptions(isolate, options, plural_rules->TypeAsString(),
255 icu::DecimalFormat* icu_decimal_format =
256 plural_rules->icu_decimal_format()->raw();
257 CHECK_NOT_NULL(icu_decimal_format);
261 icu::NumberFormat* icu_number_format =
262 static_cast<icu::NumberFormat*
>(icu_decimal_format);
264 int min_int_digits = icu_number_format->getMinimumIntegerDigits();
265 CreateDataPropertyForOptions(isolate, options, min_int_digits,
266 "minimumIntegerDigits");
268 int min_fraction_digits = icu_number_format->getMinimumFractionDigits();
269 CreateDataPropertyForOptions(isolate, options, min_fraction_digits,
270 "minimumFractionDigits");
272 int max_fraction_digits = icu_number_format->getMaximumFractionDigits();
273 CreateDataPropertyForOptions(isolate, options, max_fraction_digits,
274 "maximumFractionDigits");
276 if (icu_decimal_format->areSignificantDigitsUsed()) {
277 int min_significant_digits =
278 icu_decimal_format->getMinimumSignificantDigits();
279 CreateDataPropertyForOptions(isolate, options, min_significant_digits,
280 "minimumSignificantDigits");
282 int max_significant_digits =
283 icu_decimal_format->getMaximumSignificantDigits();
284 CreateDataPropertyForOptions(isolate, options, max_significant_digits,
285 "maximumSignificantDigits");
290 icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
291 CHECK_NOT_NULL(icu_plural_rules);
293 UErrorCode status = U_ZERO_ERROR;
294 std::unique_ptr<icu::StringEnumeration> categories(
295 icu_plural_rules->getKeywords(status));
296 CHECK(U_SUCCESS(status));
297 int32_t count = categories->count(status);
298 CHECK(U_SUCCESS(status));
300 Handle<FixedArray> plural_categories =
301 isolate->factory()->NewFixedArray(count);
302 for (int32_t
i = 0;
i < count;
i++) {
303 const icu::UnicodeString* category = categories->snext(status);
304 CHECK(U_SUCCESS(status));
305 if (category ==
nullptr)
break;
308 Handle<String> value = isolate->factory()->NewStringFromAsciiChecked(
309 category->toUTF8String(keyword).data());
310 plural_categories->set(
i, *value);
315 Handle<JSArray> plural_categories_value =
316 isolate->factory()->NewJSArrayWithElements(plural_categories);
317 CreateDataPropertyForOptions(isolate, options, plural_categories_value,
323 std::set<std::string> JSPluralRules::GetAvailableLocales() {
324 int32_t num_locales = 0;
330 const icu::Locale* icu_available_locales =
331 icu::Locale::getAvailableLocales(num_locales);
332 return Intl::BuildLocaleSet(icu_available_locales, num_locales);