5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-collator.h" 11 #include "src/isolate.h" 12 #include "src/objects-inl.h" 13 #include "src/objects/js-collator-inl.h" 14 #include "unicode/coll.h" 15 #include "unicode/locid.h" 16 #include "unicode/strenum.h" 17 #include "unicode/ucol.h" 18 #include "unicode/uloc.h" 30 enum class Sensitivity {
39 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
40 Handle<String> key,
const char* value) {
41 CHECK_NOT_NULL(value);
42 Handle<String> value_str =
43 isolate->factory()->NewStringFromAsciiChecked(value);
47 CHECK(JSReceiver::CreateDataProperty(isolate, options, key, value_str,
52 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
53 Handle<String> key,
bool value) {
54 Handle<Object> value_obj = isolate->factory()->ToBoolean(value);
58 CHECK(JSReceiver::CreateDataProperty(isolate, options, key, value_obj,
63 void toLanguageTag(
const icu::Locale& locale,
char* tag) {
64 UErrorCode status = U_ZERO_ERROR;
65 uloc_toLanguageTag(locale.getName(), tag, ULOC_FULLNAME_CAPACITY, FALSE,
67 CHECK(U_SUCCESS(status));
73 Handle<JSObject> JSCollator::ResolvedOptions(Isolate* isolate,
74 Handle<JSCollator> collator) {
75 Handle<JSObject> options =
76 isolate->factory()->NewJSObject(isolate->object_function());
78 icu::Collator* icu_collator = collator->icu_collator()->raw();
79 CHECK_NOT_NULL(icu_collator);
81 UErrorCode status = U_ZERO_ERROR;
83 icu_collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON;
84 CHECK(U_SUCCESS(status));
86 const char* case_first =
nullptr;
87 status = U_ZERO_ERROR;
88 switch (icu_collator->getAttribute(UCOL_CASE_FIRST, status)) {
89 case UCOL_LOWER_FIRST:
92 case UCOL_UPPER_FIRST:
98 CHECK(U_SUCCESS(status));
100 const char* sensitivity =
nullptr;
101 status = U_ZERO_ERROR;
102 switch (icu_collator->getAttribute(UCOL_STRENGTH, status)) {
104 CHECK(U_SUCCESS(status));
105 status = U_ZERO_ERROR;
107 if (UCOL_ON == icu_collator->getAttribute(UCOL_CASE_LEVEL, status)) {
108 sensitivity =
"case";
110 sensitivity =
"base";
112 CHECK(U_SUCCESS(status));
116 sensitivity =
"accent";
119 sensitivity =
"variant";
121 case UCOL_QUATERNARY:
124 sensitivity =
"variant";
127 sensitivity =
"variant";
129 CHECK(U_SUCCESS(status));
131 status = U_ZERO_ERROR;
132 bool ignore_punctuation = icu_collator->getAttribute(UCOL_ALTERNATE_HANDLING,
133 status) == UCOL_SHIFTED;
134 CHECK(U_SUCCESS(status));
136 status = U_ZERO_ERROR;
138 icu::Locale icu_locale(icu_collator->getLocale(ULOC_VALID_LOCALE, status));
139 CHECK(U_SUCCESS(status));
141 const char* collation =
"default";
142 const char* usage =
"sort";
143 const char* collation_key =
"co";
144 const char* legacy_collation_key = uloc_toLegacyKey(collation_key);
145 DCHECK_NOT_NULL(legacy_collation_key);
147 char bcp47_locale_tag[ULOC_FULLNAME_CAPACITY];
148 char legacy_collation_value[ULOC_FULLNAME_CAPACITY];
149 status = U_ZERO_ERROR;
151 icu_locale.getKeywordValue(legacy_collation_key, legacy_collation_value,
152 ULOC_FULLNAME_CAPACITY, status);
154 if (length > 0 && U_SUCCESS(status)) {
155 const char* collation_value =
156 uloc_toUnicodeLocaleType(collation_key, legacy_collation_value);
157 CHECK_NOT_NULL(collation_value);
159 if (strcmp(collation_value,
"search") == 0) {
166 collation =
"default";
171 icu::Locale new_icu_locale = icu_locale;
175 status = U_ZERO_ERROR;
176 new_icu_locale.setKeywordValue(legacy_collation_key,
nullptr, status);
177 CHECK(U_SUCCESS(status));
179 toLanguageTag(new_icu_locale, bcp47_locale_tag);
181 collation = collation_value;
182 toLanguageTag(icu_locale, bcp47_locale_tag);
185 toLanguageTag(icu_locale, bcp47_locale_tag);
199 CreateDataPropertyForOptions(
200 isolate, options, isolate->factory()->locale_string(), bcp47_locale_tag);
201 CreateDataPropertyForOptions(isolate, options,
202 isolate->factory()->usage_string(), usage);
203 CreateDataPropertyForOptions(
204 isolate, options, isolate->factory()->sensitivity_string(), sensitivity);
205 CreateDataPropertyForOptions(isolate, options,
206 isolate->factory()->ignorePunctuation_string(),
208 CreateDataPropertyForOptions(
209 isolate, options, isolate->factory()->collation_string(), collation);
210 CreateDataPropertyForOptions(isolate, options,
211 isolate->factory()->numeric_string(), numeric);
212 CreateDataPropertyForOptions(
213 isolate, options, isolate->factory()->caseFirst_string(), case_first);
219 Intl::CaseFirst ToCaseFirst(
const char* str) {
220 if (strcmp(str,
"upper") == 0)
return Intl::CaseFirst::kUpper;
221 if (strcmp(str,
"lower") == 0)
return Intl::CaseFirst::kLower;
222 if (strcmp(str,
"false") == 0)
return Intl::CaseFirst::kFalse;
223 return Intl::CaseFirst::kUndefined;
226 UColAttributeValue ToUColAttributeValue(Intl::CaseFirst case_first) {
227 switch (case_first) {
228 case Intl::CaseFirst::kUpper:
229 return UCOL_UPPER_FIRST;
230 case Intl::CaseFirst::kLower:
231 return UCOL_LOWER_FIRST;
232 case Intl::CaseFirst::kFalse:
233 case Intl::CaseFirst::kUndefined:
238 void SetCaseFirstOption(icu::Collator* icu_collator,
239 Intl::CaseFirst case_first) {
240 CHECK_NOT_NULL(icu_collator);
241 UErrorCode status = U_ZERO_ERROR;
242 icu_collator->setAttribute(UCOL_CASE_FIRST, ToUColAttributeValue(case_first),
244 CHECK(U_SUCCESS(status));
250 MaybeHandle<JSCollator> JSCollator::Initialize(Isolate* isolate,
251 Handle<JSCollator> collator,
252 Handle<Object> locales,
253 Handle<Object> options_obj) {
255 Maybe<std::vector<std::string>> maybe_requested_locales =
256 Intl::CanonicalizeLocaleList(isolate, locales);
257 MAYBE_RETURN(maybe_requested_locales, Handle<JSCollator>());
258 std::vector<std::string> requested_locales =
259 maybe_requested_locales.FromJust();
262 if (options_obj->IsUndefined(isolate)) {
264 options_obj = isolate->factory()->NewJSObjectWithNullProto();
268 ASSIGN_RETURN_ON_EXCEPTION(
269 isolate, options_obj,
270 Object::ToObject(isolate, options_obj,
"Intl.Collator"), JSCollator);
274 Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
278 Maybe<Usage> maybe_usage = Intl::GetStringOption<Usage>(
279 isolate, options,
"usage",
"Intl.Collator", {
"sort",
"search"},
280 {Usage::SORT, Usage::SEARCH}, Usage::SORT);
281 MAYBE_RETURN(maybe_usage, MaybeHandle<JSCollator>());
282 Usage usage = maybe_usage.FromJust();
287 Maybe<Intl::MatcherOption> maybe_locale_matcher =
288 Intl::GetLocaleMatcher(isolate, options,
"Intl.Collator");
289 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSCollator>());
290 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
303 Maybe<bool> found_numeric = Intl::GetBoolOption(isolate, options,
"numeric",
304 "Intl.Collator", &numeric);
305 MAYBE_RETURN(found_numeric, MaybeHandle<JSCollator>());
309 Maybe<Intl::CaseFirst> maybe_case_first =
310 Intl::GetCaseFirst(isolate, options,
"Intl.Collator");
311 MAYBE_RETURN(maybe_case_first, MaybeHandle<JSCollator>());
312 Intl::CaseFirst case_first = maybe_case_first.FromJust();
318 std::set<std::string> relevant_extension_keys{
"co",
"kn",
"kf"};
323 Intl::ResolvedLocale r =
324 Intl::ResolveLocale(isolate, JSCollator::GetAvailableLocales(),
325 requested_locales, matcher, relevant_extension_keys);
328 icu::Locale icu_locale = r.icu_locale;
329 DCHECK(!icu_locale.isBogus());
331 std::map<std::string, std::string> extensions = r.extensions;
344 auto co_extension_it = extensions.find(
"co");
345 if (co_extension_it != extensions.end()) {
346 const std::string& value = co_extension_it->second;
347 if ((value ==
"search") || (value ==
"standard")) {
348 UErrorCode status = U_ZERO_ERROR;
349 const char* key = uloc_toLegacyKey(
"co");
350 icu_locale.setKeywordValue(key,
nullptr, status);
351 CHECK(U_SUCCESS(status));
371 if (usage == Usage::SEARCH) {
372 const char* key = uloc_toLegacyKey(
"co");
374 const char* value = uloc_toLegacyType(key,
"search");
375 CHECK_NOT_NULL(value);
376 UErrorCode status = U_ZERO_ERROR;
377 icu_locale.setKeywordValue(key, value, status);
378 CHECK(U_SUCCESS(status));
388 UErrorCode status = U_ZERO_ERROR;
389 std::unique_ptr<icu::Collator> icu_collator(
390 icu::Collator::createInstance(icu_locale, status));
391 if (U_FAILURE(status) || icu_collator.get() ==
nullptr) {
392 status = U_ZERO_ERROR;
394 icu::Locale no_extension_locale(icu_locale.getBaseName());
396 icu::Collator::createInstance(no_extension_locale, status));
398 if (U_FAILURE(status) || icu_collator.get() ==
nullptr) {
399 FATAL(
"Failed to create ICU collator, are ICU data files missing?");
402 DCHECK(U_SUCCESS(status));
403 CHECK_NOT_NULL(icu_collator.get());
411 status = U_ZERO_ERROR;
412 if (found_numeric.FromJust()) {
413 icu_collator->setAttribute(UCOL_NUMERIC_COLLATION,
414 numeric ? UCOL_ON : UCOL_OFF, status);
415 CHECK(U_SUCCESS(status));
417 auto kn_extension_it = extensions.find(
"kn");
418 if (kn_extension_it != extensions.end()) {
419 const std::string& value = kn_extension_it->second;
421 numeric = (value ==
"true");
423 icu_collator->setAttribute(UCOL_NUMERIC_COLLATION,
424 numeric ? UCOL_ON : UCOL_OFF, status);
425 CHECK(U_SUCCESS(status));
435 if (case_first != Intl::CaseFirst::kUndefined) {
436 SetCaseFirstOption(icu_collator.get(), case_first);
438 auto kf_extension_it = extensions.find(
"kf");
439 if (kf_extension_it != extensions.end()) {
440 const std::string& value = kf_extension_it->second;
441 SetCaseFirstOption(icu_collator.get(), ToCaseFirst(value.c_str()));
448 status = U_ZERO_ERROR;
449 icu_collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
450 CHECK(U_SUCCESS(status));
454 Maybe<Sensitivity> maybe_sensitivity = Intl::GetStringOption<Sensitivity>(
455 isolate, options,
"sensitivity",
"Intl.Collator",
456 {
"base",
"accent",
"case",
"variant"},
457 {Sensitivity::kBase, Sensitivity::kAccent, Sensitivity::kCase,
458 Sensitivity::kVariant},
459 Sensitivity::kUndefined);
460 MAYBE_RETURN(maybe_sensitivity, MaybeHandle<JSCollator>());
461 Sensitivity sensitivity = maybe_sensitivity.FromJust();
464 if (sensitivity == Sensitivity::kUndefined) {
466 if (usage == Usage::SORT) {
468 sensitivity = Sensitivity::kVariant;
472 switch (sensitivity) {
473 case Sensitivity::kBase:
474 icu_collator->setStrength(icu::Collator::PRIMARY);
476 case Sensitivity::kAccent:
477 icu_collator->setStrength(icu::Collator::SECONDARY);
479 case Sensitivity::kCase:
480 icu_collator->setStrength(icu::Collator::PRIMARY);
481 status = U_ZERO_ERROR;
482 icu_collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
483 CHECK(U_SUCCESS(status));
485 case Sensitivity::kVariant:
486 icu_collator->setStrength(icu::Collator::TERTIARY);
488 case Sensitivity::kUndefined:
494 bool ignore_punctuation;
495 Maybe<bool> found_ignore_punctuation =
496 Intl::GetBoolOption(isolate, options,
"ignorePunctuation",
497 "Intl.Collator", &ignore_punctuation);
498 MAYBE_RETURN(found_ignore_punctuation, MaybeHandle<JSCollator>());
501 if (found_ignore_punctuation.FromJust() && ignore_punctuation) {
502 status = U_ZERO_ERROR;
503 icu_collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
504 CHECK(U_SUCCESS(status));
507 Handle<Managed<icu::Collator>> managed_collator =
508 Managed<icu::Collator>::FromUniquePtr(isolate, 0,
509 std::move(icu_collator));
510 collator->set_icu_collator(*managed_collator);
516 std::set<std::string> JSCollator::GetAvailableLocales() {
517 int32_t num_locales = 0;
518 const icu::Locale* icu_available_locales =
519 icu::Collator::getAvailableLocales(num_locales);
520 return Intl::BuildLocaleSet(icu_available_locales, num_locales);