1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif // V8_INTL_SUPPORT
8
9 #include "src/objects/js-collator.h"
10
11 #include "src/execution/isolate.h"
12 #include "src/objects/js-collator-inl.h"
13 #include "src/objects/js-locale.h"
14 #include "src/objects/objects-inl.h"
15 #include "unicode/coll.h"
16 #include "unicode/locid.h"
17 #include "unicode/strenum.h"
18 #include "unicode/ucol.h"
19 #include "unicode/udata.h"
20 #include "unicode/uloc.h"
21 #include "unicode/utypes.h"
22
23 namespace v8 {
24 namespace internal {
25
26 namespace {
27
28 enum class Usage {
29 SORT,
30 SEARCH,
31 };
32
33 enum class Sensitivity {
34 kBase,
35 kAccent,
36 kCase,
37 kVariant,
38 kUndefined,
39 };
40
41 // enum for "caseFirst" option.
42 enum class CaseFirst { kUndefined, kUpper, kLower, kFalse };
43
GetCaseFirst(Isolate * isolate,Handle<JSReceiver> options,const char * method)44 Maybe<CaseFirst> GetCaseFirst(Isolate* isolate, Handle<JSReceiver> options,
45 const char* method) {
46 return Intl::GetStringOption<CaseFirst>(
47 isolate, options, "caseFirst", method, {"upper", "lower", "false"},
48 {CaseFirst::kUpper, CaseFirst::kLower, CaseFirst::kFalse},
49 CaseFirst::kUndefined);
50 }
51
52 // TODO(gsathya): Consider internalizing the value strings.
CreateDataPropertyForOptions(Isolate * isolate,Handle<JSObject> options,Handle<String> key,const char * value)53 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
54 Handle<String> key, const char* value) {
55 DCHECK_NOT_NULL(value);
56 Handle<String> value_str =
57 isolate->factory()->NewStringFromAsciiChecked(value);
58
59 // This is a brand new JSObject that shouldn't already have the same
60 // key so this shouldn't fail.
61 Maybe<bool> maybe = JSReceiver::CreateDataProperty(
62 isolate, options, key, value_str, Just(kDontThrow));
63 DCHECK(maybe.FromJust());
64 USE(maybe);
65 }
66
CreateDataPropertyForOptions(Isolate * isolate,Handle<JSObject> options,Handle<String> key,bool value)67 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
68 Handle<String> key, bool value) {
69 Handle<Object> value_obj = isolate->factory()->ToBoolean(value);
70
71 // This is a brand new JSObject that shouldn't already have the same
72 // key so this shouldn't fail.
73 Maybe<bool> maybe = JSReceiver::CreateDataProperty(
74 isolate, options, key, value_obj, Just(kDontThrow));
75 DCHECK(maybe.FromJust());
76 USE(maybe);
77 }
78
79 } // anonymous namespace
80
81 // static
ResolvedOptions(Isolate * isolate,Handle<JSCollator> collator)82 Handle<JSObject> JSCollator::ResolvedOptions(Isolate* isolate,
83 Handle<JSCollator> collator) {
84 Handle<JSObject> options =
85 isolate->factory()->NewJSObject(isolate->object_function());
86
87 icu::Collator* icu_collator = collator->icu_collator().raw();
88 DCHECK_NOT_NULL(icu_collator);
89
90 UErrorCode status = U_ZERO_ERROR;
91 bool numeric =
92 icu_collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON;
93 DCHECK(U_SUCCESS(status));
94
95 const char* case_first = nullptr;
96 status = U_ZERO_ERROR;
97 switch (icu_collator->getAttribute(UCOL_CASE_FIRST, status)) {
98 case UCOL_LOWER_FIRST:
99 case_first = "lower";
100 break;
101 case UCOL_UPPER_FIRST:
102 case_first = "upper";
103 break;
104 default:
105 case_first = "false";
106 }
107 DCHECK(U_SUCCESS(status));
108
109 const char* sensitivity = nullptr;
110 status = U_ZERO_ERROR;
111 switch (icu_collator->getAttribute(UCOL_STRENGTH, status)) {
112 case UCOL_PRIMARY: {
113 DCHECK(U_SUCCESS(status));
114 status = U_ZERO_ERROR;
115 // case level: true + s1 -> case, s1 -> base.
116 if (UCOL_ON == icu_collator->getAttribute(UCOL_CASE_LEVEL, status)) {
117 sensitivity = "case";
118 } else {
119 sensitivity = "base";
120 }
121 DCHECK(U_SUCCESS(status));
122 break;
123 }
124 case UCOL_SECONDARY:
125 sensitivity = "accent";
126 break;
127 case UCOL_TERTIARY:
128 sensitivity = "variant";
129 break;
130 case UCOL_QUATERNARY:
131 // We shouldn't get quaternary and identical from ICU, but if we do
132 // put them into variant.
133 sensitivity = "variant";
134 break;
135 default:
136 sensitivity = "variant";
137 }
138 DCHECK(U_SUCCESS(status));
139
140 status = U_ZERO_ERROR;
141 bool ignore_punctuation = icu_collator->getAttribute(UCOL_ALTERNATE_HANDLING,
142 status) == UCOL_SHIFTED;
143 DCHECK(U_SUCCESS(status));
144
145 status = U_ZERO_ERROR;
146
147 icu::Locale icu_locale(icu_collator->getLocale(ULOC_VALID_LOCALE, status));
148 DCHECK(U_SUCCESS(status));
149
150 const char* collation = "default";
151 const char* usage = "sort";
152 const char* collation_key = "co";
153 status = U_ZERO_ERROR;
154 std::string collation_value =
155 icu_locale.getUnicodeKeywordValue<std::string>(collation_key, status);
156
157 std::string locale;
158 if (U_SUCCESS(status)) {
159 if (collation_value == "search") {
160 usage = "search";
161
162 // Search is disallowed as a collation value per spec. Let's
163 // use `default`, instead.
164 //
165 // https://tc39.github.io/ecma402/#sec-properties-of-intl-collator-instances
166 collation = "default";
167
168 // We clone the icu::Locale because we don't want the
169 // icu_collator to be affected when we remove the collation key
170 // below.
171 icu::Locale new_icu_locale = icu_locale;
172
173 // The spec forbids the search as a collation value in the
174 // locale tag, so let's filter it out.
175 status = U_ZERO_ERROR;
176 new_icu_locale.setUnicodeKeywordValue(collation_key, nullptr, status);
177 DCHECK(U_SUCCESS(status));
178
179 locale = Intl::ToLanguageTag(new_icu_locale).FromJust();
180 } else {
181 collation = collation_value.c_str();
182 locale = Intl::ToLanguageTag(icu_locale).FromJust();
183 }
184 } else {
185 locale = Intl::ToLanguageTag(icu_locale).FromJust();
186 }
187
188 // 5. For each row of Table 2, except the header row, in table order, do
189 // ...
190 // Table 2: Resolved Options of Collator Instances
191 // Internal Slot Property Extension Key
192 // [[Locale] "locale"
193 // [[Usage] "usage"
194 // [[Sensitivity]] "sensitivity"
195 // [[IgnorePunctuation]] "ignorePunctuation"
196 // [[Collation]] "collation"
197 // [[Numeric]] "numeric" kn
198 // [[CaseFirst]] "caseFirst" kf
199
200 // If the collator return the locale differ from what got requested, we stored
201 // it in the collator->locale. Otherwise, we just use the one from the
202 // collator.
203 if (collator->locale().length() != 0) {
204 // Get the locale from collator->locale() since we know in some cases
205 // collator won't be able to return the requested one, such as zh_CN.
206 Handle<String> locale_from_collator(collator->locale(), isolate);
207 Maybe<bool> maybe = JSReceiver::CreateDataProperty(
208 isolate, options, isolate->factory()->locale_string(),
209 locale_from_collator, Just(kDontThrow));
210 DCHECK(maybe.FromJust());
211 USE(maybe);
212 } else {
213 // Just return from the collator for most of the cases that we can recover
214 // from the collator.
215 CreateDataPropertyForOptions(
216 isolate, options, isolate->factory()->locale_string(), locale.c_str());
217 }
218
219 CreateDataPropertyForOptions(isolate, options,
220 isolate->factory()->usage_string(), usage);
221 CreateDataPropertyForOptions(
222 isolate, options, isolate->factory()->sensitivity_string(), sensitivity);
223 CreateDataPropertyForOptions(isolate, options,
224 isolate->factory()->ignorePunctuation_string(),
225 ignore_punctuation);
226 CreateDataPropertyForOptions(
227 isolate, options, isolate->factory()->collation_string(), collation);
228 CreateDataPropertyForOptions(isolate, options,
229 isolate->factory()->numeric_string(), numeric);
230 CreateDataPropertyForOptions(
231 isolate, options, isolate->factory()->caseFirst_string(), case_first);
232 return options;
233 }
234
235 namespace {
236
ToCaseFirst(const char * str)237 CaseFirst ToCaseFirst(const char* str) {
238 if (strcmp(str, "upper") == 0) return CaseFirst::kUpper;
239 if (strcmp(str, "lower") == 0) return CaseFirst::kLower;
240 if (strcmp(str, "false") == 0) return CaseFirst::kFalse;
241 return CaseFirst::kUndefined;
242 }
243
ToUColAttributeValue(CaseFirst case_first)244 UColAttributeValue ToUColAttributeValue(CaseFirst case_first) {
245 switch (case_first) {
246 case CaseFirst::kUpper:
247 return UCOL_UPPER_FIRST;
248 case CaseFirst::kLower:
249 return UCOL_LOWER_FIRST;
250 case CaseFirst::kFalse:
251 case CaseFirst::kUndefined:
252 return UCOL_OFF;
253 }
254 }
255
SetNumericOption(icu::Collator * icu_collator,bool numeric)256 void SetNumericOption(icu::Collator* icu_collator, bool numeric) {
257 DCHECK_NOT_NULL(icu_collator);
258 UErrorCode status = U_ZERO_ERROR;
259 icu_collator->setAttribute(UCOL_NUMERIC_COLLATION,
260 numeric ? UCOL_ON : UCOL_OFF, status);
261 DCHECK(U_SUCCESS(status));
262 }
263
SetCaseFirstOption(icu::Collator * icu_collator,CaseFirst case_first)264 void SetCaseFirstOption(icu::Collator* icu_collator, CaseFirst case_first) {
265 DCHECK_NOT_NULL(icu_collator);
266 UErrorCode status = U_ZERO_ERROR;
267 icu_collator->setAttribute(UCOL_CASE_FIRST, ToUColAttributeValue(case_first),
268 status);
269 DCHECK(U_SUCCESS(status));
270 }
271
272 } // anonymous namespace
273
274 // static
New(Isolate * isolate,Handle<Map> map,Handle<Object> locales,Handle<Object> options_obj,const char * service)275 MaybeHandle<JSCollator> JSCollator::New(Isolate* isolate, Handle<Map> map,
276 Handle<Object> locales,
277 Handle<Object> options_obj,
278 const char* service) {
279 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
280 Maybe<std::vector<std::string>> maybe_requested_locales =
281 Intl::CanonicalizeLocaleList(isolate, locales);
282 MAYBE_RETURN(maybe_requested_locales, Handle<JSCollator>());
283 std::vector<std::string> requested_locales =
284 maybe_requested_locales.FromJust();
285
286 // 2. If options is undefined, then
287 if (options_obj->IsUndefined(isolate)) {
288 // 2. a. Let options be ObjectCreate(null).
289 options_obj = isolate->factory()->NewJSObjectWithNullProto();
290 } else {
291 // 3. Else
292 // 3. a. Let options be ? ToObject(options).
293 ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
294 Object::ToObject(isolate, options_obj, service),
295 JSCollator);
296 }
297
298 // At this point, options_obj can either be a JSObject or a JSProxy only.
299 Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
300
301 // 4. Let usage be ? GetOption(options, "usage", "string", « "sort",
302 // "search" », "sort").
303 Maybe<Usage> maybe_usage = Intl::GetStringOption<Usage>(
304 isolate, options, "usage", service, {"sort", "search"},
305 {Usage::SORT, Usage::SEARCH}, Usage::SORT);
306 MAYBE_RETURN(maybe_usage, MaybeHandle<JSCollator>());
307 Usage usage = maybe_usage.FromJust();
308
309 // 9. Let matcher be ? GetOption(options, "localeMatcher", "string",
310 // « "lookup", "best fit" », "best fit").
311 // 10. Set opt.[[localeMatcher]] to matcher.
312 Maybe<Intl::MatcherOption> maybe_locale_matcher =
313 Intl::GetLocaleMatcher(isolate, options, service);
314 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSCollator>());
315 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
316
317 // x. Let _collation_ be ? GetOption(_options_, *"collation"*, *"string"*,
318 // *undefined*, *undefined*).
319 std::unique_ptr<char[]> collation_str = nullptr;
320 const std::vector<const char*> empty_values = {};
321 Maybe<bool> maybe_collation = Intl::GetStringOption(
322 isolate, options, "collation", empty_values, service, &collation_str);
323 MAYBE_RETURN(maybe_collation, MaybeHandle<JSCollator>());
324 // x. If _collation_ is not *undefined*, then
325 if (maybe_collation.FromJust() && collation_str != nullptr) {
326 // 1. If _collation_ does not match the Unicode Locale Identifier `type`
327 // nonterminal, throw a *RangeError* exception.
328 if (!JSLocale::Is38AlphaNumList(collation_str.get())) {
329 THROW_NEW_ERROR_RETURN_VALUE(
330 isolate,
331 NewRangeError(MessageTemplate::kInvalid,
332 isolate->factory()->collation_string(),
333 isolate->factory()->NewStringFromAsciiChecked(
334 collation_str.get())),
335 MaybeHandle<JSCollator>());
336 }
337 }
338 // x. Set _opt_.[[co]] to _collation_.
339
340 // 11. Let numeric be ? GetOption(options, "numeric", "boolean",
341 // undefined, undefined).
342 // 12. If numeric is not undefined, then
343 // a. Let numeric be ! ToString(numeric).
344 //
345 // Note: We omit the ToString(numeric) operation as it's not
346 // observable. Intl::GetBoolOption returns a Boolean and
347 // ToString(Boolean) is not side-effecting.
348 //
349 // 13. Set opt.[[kn]] to numeric.
350 bool numeric;
351 Maybe<bool> found_numeric =
352 Intl::GetBoolOption(isolate, options, "numeric", service, &numeric);
353 MAYBE_RETURN(found_numeric, MaybeHandle<JSCollator>());
354
355 // 14. Let caseFirst be ? GetOption(options, "caseFirst", "string",
356 // « "upper", "lower", "false" », undefined).
357 Maybe<CaseFirst> maybe_case_first = GetCaseFirst(isolate, options, service);
358 MAYBE_RETURN(maybe_case_first, MaybeHandle<JSCollator>());
359 CaseFirst case_first = maybe_case_first.FromJust();
360
361 // The relevant unicode extensions accepted by Collator as specified here:
362 // https://tc39.github.io/ecma402/#sec-intl-collator-internal-slots
363 //
364 // 16. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
365 std::set<std::string> relevant_extension_keys{"co", "kn", "kf"};
366
367 // 17. Let r be ResolveLocale(%Collator%.[[AvailableLocales]],
368 // requestedLocales, opt, %Collator%.[[RelevantExtensionKeys]],
369 // localeData).
370 Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
371 Intl::ResolveLocale(isolate, JSCollator::GetAvailableLocales(),
372 requested_locales, matcher, relevant_extension_keys);
373 if (maybe_resolve_locale.IsNothing()) {
374 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
375 JSCollator);
376 }
377 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
378
379 // 18. Set collator.[[Locale]] to r.[[locale]].
380 icu::Locale icu_locale = r.icu_locale;
381 DCHECK(!icu_locale.isBogus());
382
383 // 19. Let collation be r.[[co]].
384 UErrorCode status = U_ZERO_ERROR;
385 if (collation_str != nullptr) {
386 auto co_extension_it = r.extensions.find("co");
387 if (co_extension_it != r.extensions.end() &&
388 co_extension_it->second != collation_str.get()) {
389 icu_locale.setUnicodeKeywordValue("co", nullptr, status);
390 DCHECK(U_SUCCESS(status));
391 }
392 }
393
394 // 5. Set collator.[[Usage]] to usage.
395 //
396 // 6. If usage is "sort", then
397 // a. Let localeData be %Collator%.[[SortLocaleData]].
398 // 7. Else,
399 // a. Let localeData be %Collator%.[[SearchLocaleData]].
400 //
401 // The Intl spec doesn't allow us to use "search" as an extension
402 // value for collation as per:
403 // https://tc39.github.io/ecma402/#sec-intl-collator-internal-slots
404 //
405 // But the only way to pass the value "search" for collation from
406 // the options object to ICU is to use the 'co' extension keyword.
407 //
408 // This will need to be filtered out when creating the
409 // resolvedOptions object.
410 if (usage == Usage::SEARCH) {
411 UErrorCode status = U_ZERO_ERROR;
412 icu_locale.setUnicodeKeywordValue("co", "search", status);
413 DCHECK(U_SUCCESS(status));
414 } else {
415 if (collation_str != nullptr &&
416 Intl::IsValidCollation(icu_locale, collation_str.get())) {
417 icu_locale.setUnicodeKeywordValue("co", collation_str.get(), status);
418 DCHECK(U_SUCCESS(status));
419 }
420 }
421
422 // 20. If collation is null, let collation be "default".
423 // 21. Set collator.[[Collation]] to collation.
424 //
425 // We don't store the collation value as per the above two steps
426 // here. The collation value can be looked up from icu::Collator on
427 // demand, as part of Intl.Collator.prototype.resolvedOptions.
428
429 std::unique_ptr<icu::Collator> icu_collator(
430 icu::Collator::createInstance(icu_locale, status));
431 if (U_FAILURE(status) || icu_collator.get() == nullptr) {
432 status = U_ZERO_ERROR;
433 // Remove extensions and try again.
434 icu::Locale no_extension_locale(icu_locale.getBaseName());
435 icu_collator.reset(
436 icu::Collator::createInstance(no_extension_locale, status));
437
438 if (U_FAILURE(status) || icu_collator.get() == nullptr) {
439 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
440 JSCollator);
441 }
442 }
443 DCHECK(U_SUCCESS(status));
444
445 icu::Locale collator_locale(
446 icu_collator->getLocale(ULOC_VALID_LOCALE, status));
447
448 // 22. If relevantExtensionKeys contains "kn", then
449 // a. Set collator.[[Numeric]] to ! SameValue(r.[[kn]], "true").
450 //
451 // If the numeric value is passed in through the options object,
452 // then we use it. Otherwise, we check if the numeric value is
453 // passed in through the unicode extensions.
454 status = U_ZERO_ERROR;
455 if (found_numeric.FromJust()) {
456 SetNumericOption(icu_collator.get(), numeric);
457 } else {
458 auto kn_extension_it = r.extensions.find("kn");
459 if (kn_extension_it != r.extensions.end()) {
460 SetNumericOption(icu_collator.get(), (kn_extension_it->second == "true"));
461 }
462 }
463
464 // 23. If relevantExtensionKeys contains "kf", then
465 // a. Set collator.[[CaseFirst]] to r.[[kf]].
466 //
467 // If the caseFirst value is passed in through the options object,
468 // then we use it. Otherwise, we check if the caseFirst value is
469 // passed in through the unicode extensions.
470 if (case_first != CaseFirst::kUndefined) {
471 SetCaseFirstOption(icu_collator.get(), case_first);
472 } else {
473 auto kf_extension_it = r.extensions.find("kf");
474 if (kf_extension_it != r.extensions.end()) {
475 SetCaseFirstOption(icu_collator.get(),
476 ToCaseFirst(kf_extension_it->second.c_str()));
477 }
478 }
479
480 // Normalization is always on, by the spec. We are free to optimize
481 // if the strings are already normalized (but we don't have a way to tell
482 // that right now).
483 status = U_ZERO_ERROR;
484 icu_collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
485 DCHECK(U_SUCCESS(status));
486
487 // 24. Let sensitivity be ? GetOption(options, "sensitivity",
488 // "string", « "base", "accent", "case", "variant" », undefined).
489 Maybe<Sensitivity> maybe_sensitivity = Intl::GetStringOption<Sensitivity>(
490 isolate, options, "sensitivity", service,
491 {"base", "accent", "case", "variant"},
492 {Sensitivity::kBase, Sensitivity::kAccent, Sensitivity::kCase,
493 Sensitivity::kVariant},
494 Sensitivity::kUndefined);
495 MAYBE_RETURN(maybe_sensitivity, MaybeHandle<JSCollator>());
496 Sensitivity sensitivity = maybe_sensitivity.FromJust();
497
498 // 25. If sensitivity is undefined, then
499 if (sensitivity == Sensitivity::kUndefined) {
500 // 25. a. If usage is "sort", then
501 if (usage == Usage::SORT) {
502 // 25. a. i. Let sensitivity be "variant".
503 sensitivity = Sensitivity::kVariant;
504 }
505 }
506 // 26. Set collator.[[Sensitivity]] to sensitivity.
507 switch (sensitivity) {
508 case Sensitivity::kBase:
509 icu_collator->setStrength(icu::Collator::PRIMARY);
510 break;
511 case Sensitivity::kAccent:
512 icu_collator->setStrength(icu::Collator::SECONDARY);
513 break;
514 case Sensitivity::kCase:
515 icu_collator->setStrength(icu::Collator::PRIMARY);
516 status = U_ZERO_ERROR;
517 icu_collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
518 DCHECK(U_SUCCESS(status));
519 break;
520 case Sensitivity::kVariant:
521 icu_collator->setStrength(icu::Collator::TERTIARY);
522 break;
523 case Sensitivity::kUndefined:
524 break;
525 }
526
527 // 27.Let ignorePunctuation be ? GetOption(options,
528 // "ignorePunctuation", "boolean", undefined, false).
529 bool ignore_punctuation;
530 Maybe<bool> found_ignore_punctuation = Intl::GetBoolOption(
531 isolate, options, "ignorePunctuation", service, &ignore_punctuation);
532 MAYBE_RETURN(found_ignore_punctuation, MaybeHandle<JSCollator>());
533
534 // 28. Set collator.[[IgnorePunctuation]] to ignorePunctuation.
535 if (found_ignore_punctuation.FromJust() && ignore_punctuation) {
536 status = U_ZERO_ERROR;
537 icu_collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
538 DCHECK(U_SUCCESS(status));
539 }
540
541 Handle<Managed<icu::Collator>> managed_collator =
542 Managed<icu::Collator>::FromUniquePtr(isolate, 0,
543 std::move(icu_collator));
544
545 // We only need to do so if it is different from the collator would return.
546 Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(
547 (collator_locale != icu_locale) ? r.locale.c_str() : "");
548 // Now all properties are ready, so we can allocate the result object.
549 Handle<JSCollator> collator = Handle<JSCollator>::cast(
550 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
551 DisallowHeapAllocation no_gc;
552 collator->set_icu_collator(*managed_collator);
553 collator->set_locale(*locale_str);
554
555 // 29. Return collator.
556 return collator;
557 }
558
559 namespace {
560
561 class CollatorAvailableLocales {
562 public:
CollatorAvailableLocales()563 CollatorAvailableLocales() {
564 int32_t num_locales = 0;
565 const icu::Locale* icu_available_locales =
566 icu::Collator::getAvailableLocales(num_locales);
567 std::vector<std::string> locales;
568 for (int32_t i = 0; i < num_locales; ++i) {
569 locales.push_back(
570 Intl::ToLanguageTag(icu_available_locales[i]).FromJust());
571 }
572 #define U_ICUDATA_COLL U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll"
573 set_ = Intl::BuildLocaleSet(locales, U_ICUDATA_COLL, nullptr);
574 #undef U_ICUDATA_COLL
575 }
576 virtual ~CollatorAvailableLocales() = default;
Get() const577 const std::set<std::string>& Get() const { return set_; }
578
579 private:
580 std::set<std::string> set_;
581 };
582
583 } // namespace
584
GetAvailableLocales()585 const std::set<std::string>& JSCollator::GetAvailableLocales() {
586 static base::LazyInstance<CollatorAvailableLocales>::type available_locales =
587 LAZY_INSTANCE_INITIALIZER;
588 return available_locales.Pointer()->Get();
589 }
590
591 } // namespace internal
592 } // namespace v8
593