Line data Source code
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-locale.h"
10 :
11 : #include <map>
12 : #include <memory>
13 : #include <string>
14 : #include <vector>
15 :
16 : #include "src/api.h"
17 : #include "src/global-handles.h"
18 : #include "src/heap/factory.h"
19 : #include "src/isolate.h"
20 : #include "src/objects-inl.h"
21 : #include "src/objects/intl-objects.h"
22 : #include "src/objects/js-locale-inl.h"
23 : #include "unicode/char16ptr.h"
24 : #include "unicode/locid.h"
25 : #include "unicode/uloc.h"
26 : #include "unicode/unistr.h"
27 :
28 : namespace v8 {
29 : namespace internal {
30 :
31 : namespace {
32 :
33 : // Helper function to check a locale is valid. It will return false if
34 : // the length of the extension fields are incorrect. For example, en-u-a or
35 : // en-u-co-b will return false.
36 52821 : bool IsValidLocale(const icu::Locale& locale) {
37 : // icu::Locale::toLanguageTag won't return U_STRING_NOT_TERMINATED_WARNING for
38 : // incorrect locale yet. So we still need the following uloc_toLanguageTag
39 : // TODO(ftang): Change to use icu::Locale::toLanguageTag once it indicate
40 : // error.
41 : char result[ULOC_FULLNAME_CAPACITY];
42 52821 : UErrorCode status = U_ZERO_ERROR;
43 : uloc_toLanguageTag(locale.getName(), result, ULOC_FULLNAME_CAPACITY, true,
44 52821 : &status);
45 52821 : return U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING;
46 : }
47 :
48 : struct OptionData {
49 : const char* name;
50 : const char* key;
51 : const std::vector<const char*>* possible_values;
52 : bool is_bool_value;
53 : };
54 :
55 : // Inserts tags from options into locale string.
56 52884 : Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
57 : Handle<JSReceiver> options,
58 : icu::Locale* icu_locale) {
59 52884 : CHECK(isolate);
60 52884 : CHECK(!icu_locale->isBogus());
61 :
62 : const std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23",
63 : "h24"};
64 : const std::vector<const char*> case_first_values = {"upper", "lower",
65 : "false"};
66 : const std::vector<const char*> empty_values = {};
67 : const std::array<OptionData, 6> kOptionToUnicodeTagMap = {
68 : {{"calendar", "ca", &empty_values, false},
69 : {"collation", "co", &empty_values, false},
70 : {"hourCycle", "hc", &hour_cycle_values, false},
71 : {"caseFirst", "kf", &case_first_values, false},
72 : {"numeric", "kn", &empty_values, true},
73 52884 : {"numberingSystem", "nu", &empty_values, false}}};
74 :
75 : // TODO(cira): Pass in values as per the spec to make this to be
76 : // spec compliant.
77 :
78 52884 : UErrorCode status = U_ZERO_ERROR;
79 687096 : for (const auto& option_to_bcp47 : kOptionToUnicodeTagMap) {
80 : std::unique_ptr<char[]> value_str = nullptr;
81 317169 : bool value_bool = false;
82 : Maybe<bool> maybe_found =
83 317169 : option_to_bcp47.is_bool_value
84 52848 : ? Intl::GetBoolOption(isolate, options, option_to_bcp47.name,
85 : "locale", &value_bool)
86 264321 : : Intl::GetStringOption(isolate, options, option_to_bcp47.name,
87 264321 : *(option_to_bcp47.possible_values),
88 1162980 : "locale", &value_str);
89 317169 : MAYBE_RETURN(maybe_found, Nothing<bool>());
90 :
91 : // TODO(cira): Use fallback value if value is not found to make
92 : // this spec compliant.
93 317115 : if (!maybe_found.FromJust()) continue;
94 :
95 162 : if (option_to_bcp47.is_bool_value) {
96 81 : value_str = value_bool ? isolate->factory()->true_string()->ToCString()
97 : : isolate->factory()->false_string()->ToCString();
98 : }
99 : DCHECK_NOT_NULL(value_str.get());
100 :
101 : // Convert bcp47 key and value into legacy ICU format so we can use
102 : // uloc_setKeywordValue.
103 162 : const char* key = uloc_toLegacyKey(option_to_bcp47.key);
104 : DCHECK_NOT_NULL(key);
105 :
106 : // Overwrite existing, or insert new key-value to the locale string.
107 162 : const char* value = uloc_toLegacyType(key, value_str.get());
108 162 : if (value) {
109 162 : icu_locale->setKeywordValue(key, value, status);
110 162 : if (U_FAILURE(status)) {
111 : return Just(false);
112 : }
113 : } else {
114 : return Just(false);
115 : }
116 : }
117 :
118 : // Check all the unicode extension fields are in the right length.
119 52821 : if (!IsValidLocale(*icu_locale)) {
120 0 : THROW_NEW_ERROR_RETURN_VALUE(
121 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
122 : Nothing<bool>());
123 : }
124 :
125 : return Just(true);
126 : }
127 :
128 90 : Handle<Object> UnicodeKeywordValue(Isolate* isolate, Handle<JSLocale> locale,
129 : const char* key) {
130 : icu::Locale* icu_locale = locale->icu_locale()->raw();
131 90 : UErrorCode status = U_ZERO_ERROR;
132 : std::string value =
133 90 : icu_locale->getUnicodeKeywordValue<std::string>(key, status);
134 135 : if (status == U_ILLEGAL_ARGUMENT_ERROR || value == "") {
135 45 : return isolate->factory()->undefined_value();
136 : }
137 45 : if (value == "yes") {
138 : value = "true";
139 : }
140 45 : return isolate->factory()->NewStringFromAsciiChecked(value.c_str());
141 : }
142 :
143 : bool InRange(size_t value, size_t start, size_t end) {
144 172521 : return (start <= value) && (value <= end);
145 : }
146 :
147 : bool InRange(char value, char start, char end) {
148 394362 : return (start <= value) && (value <= end);
149 : }
150 :
151 : bool IsCheckRange(const std::string& str, size_t min, size_t max,
152 : bool(range_check_func)(char)) {
153 172521 : if (!InRange(str.length(), min, max)) return false;
154 703368 : for (size_t i = 0; i < str.length(); i++) {
155 293310 : if (!range_check_func(str[i])) return false;
156 : }
157 : return true;
158 : }
159 131661 : bool IsAlpha(const std::string& str, size_t min, size_t max) {
160 291042 : return IsCheckRange(str, min, max, [](char c) -> bool {
161 392094 : return InRange(c, 'a', 'z') || InRange(c, 'A', 'Z');
162 422703 : });
163 : }
164 :
165 621 : bool IsDigit(const std::string& str, size_t min, size_t max) {
166 : return IsCheckRange(str, min, max,
167 4077 : [](char c) -> bool { return InRange(c, '0', '9'); });
168 : }
169 :
170 40239 : bool IsAlphanum(const std::string& str, size_t min, size_t max) {
171 540 : return IsCheckRange(str, min, max, [](char c) -> bool {
172 540 : return InRange(c, 'a', 'z') || InRange(c, 'A', 'Z') || InRange(c, '0', '9');
173 40779 : });
174 : }
175 :
176 52920 : bool IsUnicodeLanguageSubtag(const std::string& value) {
177 : // unicode_language_subtag = alpha{2,3} | alpha{5,8};
178 52920 : return IsAlpha(value, 2, 3) || IsAlpha(value, 5, 8);
179 : }
180 :
181 : bool IsUnicodeScriptSubtag(const std::string& value) {
182 : // unicode_script_subtag = alpha{4} ;
183 39699 : return IsAlpha(value, 4, 4);
184 : }
185 :
186 39033 : bool IsUnicodeRegionSubtag(const std::string& value) {
187 : // unicode_region_subtag = (alpha{2} | digit{3});
188 39033 : return IsAlpha(value, 2, 2) || IsDigit(value, 3, 3);
189 : }
190 :
191 0 : bool IsDigitAlphanum3(const std::string& value) {
192 0 : return value.length() == 4 && InRange(value[0], '0', '9') &&
193 0 : IsAlphanum(value.substr(1), 3, 3);
194 : }
195 :
196 0 : bool IsUnicodeVariantSubtag(const std::string& value) {
197 : // unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ;
198 0 : return IsAlphanum(value, 5, 8) || IsDigitAlphanum3(value);
199 : }
200 :
201 : bool IsExtensionSingleton(const std::string& value) {
202 40239 : return IsAlphanum(value, 1, 1);
203 : }
204 :
205 : // TODO(ftang) Replace the following check w/ icu::LocaleBuilder
206 : // once ICU64 land in March 2019.
207 52920 : bool StartsWithUnicodeLanguageId(const std::string& value) {
208 : // unicode_language_id =
209 : // unicode_language_subtag (sep unicode_script_subtag)?
210 : // (sep unicode_region_subtag)? (sep unicode_variant_subtag)* ;
211 52920 : std::vector<std::string> tokens;
212 : std::string token;
213 105840 : std::istringstream token_stream(value);
214 459783 : while (std::getline(token_stream, token, '-')) {
215 117981 : tokens.push_back(token);
216 : }
217 52920 : if (tokens.size() == 0) return false;
218 :
219 : // length >= 1
220 52920 : if (!IsUnicodeLanguageSubtag(tokens[0])) return false;
221 :
222 52911 : if (tokens.size() == 1) return true;
223 :
224 : // length >= 2
225 39789 : if (IsExtensionSingleton(tokens[1])) return true;
226 :
227 : size_t index = 1;
228 39699 : if (IsUnicodeScriptSubtag(tokens[index])) {
229 : index++;
230 24309 : if (index == tokens.size()) return true;
231 : }
232 39033 : if (IsUnicodeRegionSubtag(tokens[index])) {
233 38988 : index++;
234 : }
235 39033 : while (index < tokens.size()) {
236 450 : if (IsExtensionSingleton(tokens[index])) return true;
237 0 : if (!IsUnicodeVariantSubtag(tokens[index])) return false;
238 0 : index++;
239 : }
240 : return true;
241 : }
242 :
243 52929 : Maybe<std::string> ApplyOptionsToTag(Isolate* isolate, Handle<String> tag,
244 : Handle<JSReceiver> options) {
245 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
246 52929 : if (tag->length() == 0) {
247 18 : THROW_NEW_ERROR_RETURN_VALUE(
248 : isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty),
249 : Nothing<std::string>());
250 : }
251 :
252 105840 : v8::String::Utf8Value bcp47_tag(v8_isolate, v8::Utils::ToLocal(tag));
253 52920 : CHECK_LT(0, bcp47_tag.length());
254 52920 : CHECK_NOT_NULL(*bcp47_tag);
255 : // 2. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError
256 : // exception.
257 105840 : if (!StartsWithUnicodeLanguageId(*bcp47_tag)) {
258 18 : THROW_NEW_ERROR_RETURN_VALUE(
259 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
260 : Nothing<std::string>());
261 : }
262 52911 : UErrorCode status = U_ZERO_ERROR;
263 : icu::Locale icu_locale =
264 105822 : icu::Locale::forLanguageTag({*bcp47_tag, bcp47_tag.length()}, status);
265 52911 : if (U_FAILURE(status)) {
266 0 : THROW_NEW_ERROR_RETURN_VALUE(
267 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
268 : Nothing<std::string>());
269 : }
270 :
271 : // 3. Let language be ? GetOption(options, "language", "string", undefined,
272 : // undefined).
273 : const std::vector<const char*> empty_values = {};
274 : std::unique_ptr<char[]> language_str = nullptr;
275 : Maybe<bool> maybe_language =
276 : Intl::GetStringOption(isolate, options, "language", empty_values,
277 105822 : "ApplyOptionsToTag", &language_str);
278 52911 : MAYBE_RETURN(maybe_language, Nothing<std::string>());
279 : // 4. If language is not undefined, then
280 52902 : if (maybe_language.FromJust()) {
281 : // a. If language does not match the unicode_language_subtag production,
282 : // throw a RangeError exception.
283 0 : if (!IsUnicodeLanguageSubtag(language_str.get())) {
284 0 : THROW_NEW_ERROR_RETURN_VALUE(
285 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
286 : Nothing<std::string>());
287 : }
288 : }
289 : // 5. Let script be ? GetOption(options, "script", "string", undefined,
290 : // undefined).
291 : std::unique_ptr<char[]> script_str = nullptr;
292 : Maybe<bool> maybe_script =
293 : Intl::GetStringOption(isolate, options, "script", empty_values,
294 105804 : "ApplyOptionsToTag", &script_str);
295 52902 : MAYBE_RETURN(maybe_script, Nothing<std::string>());
296 : // 6. If script is not undefined, then
297 52893 : if (maybe_script.FromJust()) {
298 : // a. If script does not match the unicode_script_subtag production, throw
299 : // a RangeError exception.
300 0 : if (!IsUnicodeScriptSubtag(script_str.get())) {
301 0 : THROW_NEW_ERROR_RETURN_VALUE(
302 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
303 : Nothing<std::string>());
304 : }
305 : }
306 : // 7. Let region be ? GetOption(options, "region", "string", undefined,
307 : // undefined).
308 : std::unique_ptr<char[]> region_str = nullptr;
309 : Maybe<bool> maybe_region =
310 : Intl::GetStringOption(isolate, options, "region", empty_values,
311 105786 : "ApplyOptionsToTag", ®ion_str);
312 52893 : MAYBE_RETURN(maybe_region, Nothing<std::string>());
313 : // 8. If region is not undefined, then
314 52884 : if (maybe_region.FromJust()) {
315 : // a. If region does not match the region production, throw a RangeError
316 : // exception.
317 0 : if (!IsUnicodeRegionSubtag(region_str.get())) {
318 0 : THROW_NEW_ERROR_RETURN_VALUE(
319 : isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
320 : Nothing<std::string>());
321 : }
322 : }
323 : // 9. Set tag to CanonicalizeLanguageTag(tag).
324 : Maybe<std::string> maybe_canonicalized =
325 52884 : Intl::CanonicalizeLanguageTag(isolate, tag);
326 52884 : MAYBE_RETURN(maybe_canonicalized, Nothing<std::string>());
327 :
328 52884 : std::vector<std::string> tokens;
329 : std::string token;
330 158652 : std::istringstream token_stream(maybe_canonicalized.FromJust());
331 459522 : while (std::getline(token_stream, token, '-')) {
332 117918 : tokens.push_back(token);
333 : }
334 :
335 : // 10. If language is not undefined,
336 : std::string locale_str;
337 52884 : if (maybe_language.FromJust()) {
338 : // a. Assert: tag matches the unicode_locale_id production.
339 : // b. Set tag to tag with the substring corresponding to the
340 : // unicode_language_subtag production replaced by the string language.
341 : tokens[0] = language_str.get();
342 : }
343 :
344 : // 11. If script is not undefined, then
345 52884 : if (maybe_script.FromJust()) {
346 : // a. If tag does not contain a unicode_script_subtag production, then
347 0 : if (tokens.size() < 2 || !IsUnicodeScriptSubtag(tokens[1])) {
348 : // i. Set tag to the concatenation of the unicode_language_subtag
349 : // production of tag, "-", script, and the rest of tag.
350 0 : tokens.insert(tokens.begin() + 1, script_str.get());
351 : // b. Else,
352 : } else {
353 : // i. Set tag to tag with the substring corresponding to the
354 : // unicode_script_subtag production replaced by the string script.
355 : tokens[1] = script_str.get();
356 : }
357 : }
358 : // 12. If region is not undefined, then
359 52884 : if (maybe_region.FromJust()) {
360 : // a. If tag does not contain a unicode_region_subtag production, then
361 : // i. Set tag to the concatenation of the unicode_language_subtag
362 : // production of tag, the substring corresponding to the "-"
363 : // unicode_script_subtag production if present, "-", region, and
364 : // the rest of tag.
365 : // b. Else,
366 : // i. Set tag to tag with the substring corresponding to the
367 : // unicode_region_subtag production replaced by the string region.
368 0 : if (tokens.size() > 1 && IsUnicodeRegionSubtag(tokens[1])) {
369 : tokens[1] = region_str.get();
370 0 : } else if (tokens.size() > 1 && IsUnicodeScriptSubtag(tokens[1])) {
371 0 : if (tokens.size() > 2 && IsUnicodeRegionSubtag(tokens[2])) {
372 : tokens[2] = region_str.get();
373 : } else {
374 0 : tokens.insert(tokens.begin() + 2, region_str.get());
375 : }
376 : } else {
377 0 : tokens.insert(tokens.begin() + 1, region_str.get());
378 : }
379 : }
380 :
381 : std::string replaced;
382 170802 : for (auto it = tokens.begin(); it != tokens.end(); it++) {
383 : replaced += *it;
384 117918 : if (it + 1 != tokens.end()) {
385 : replaced += '-';
386 : }
387 : }
388 :
389 : // 13. Return CanonicalizeLanguageTag(tag).
390 52884 : return Intl::CanonicalizeLanguageTag(isolate, replaced);
391 : }
392 :
393 : } // namespace
394 :
395 52929 : MaybeHandle<JSLocale> JSLocale::Initialize(Isolate* isolate,
396 : Handle<JSLocale> locale,
397 : Handle<String> locale_str,
398 : Handle<JSReceiver> options) {
399 : Maybe<std::string> maybe_locale =
400 52929 : ApplyOptionsToTag(isolate, locale_str, options);
401 52929 : MAYBE_RETURN(maybe_locale, MaybeHandle<JSLocale>());
402 52884 : UErrorCode status = U_ZERO_ERROR;
403 : icu::Locale icu_locale =
404 158652 : icu::Locale::forLanguageTag(maybe_locale.FromJust().c_str(), status);
405 52884 : if (U_FAILURE(status)) {
406 0 : THROW_NEW_ERROR(isolate,
407 : NewRangeError(MessageTemplate::kLocaleBadParameters),
408 : JSLocale);
409 : }
410 :
411 52884 : Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, &icu_locale);
412 52884 : MAYBE_RETURN(error, MaybeHandle<JSLocale>());
413 52830 : if (!error.FromJust()) {
414 18 : THROW_NEW_ERROR(isolate,
415 : NewRangeError(MessageTemplate::kLocaleBadParameters),
416 : JSLocale);
417 : }
418 :
419 : // 31. Set locale.[[Locale]] to r.[[locale]].
420 : Handle<Managed<icu::Locale>> managed_locale =
421 52821 : Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
422 52821 : locale->set_icu_locale(*managed_locale);
423 :
424 52821 : return locale;
425 : }
426 :
427 : namespace {
428 45837 : Handle<String> MorphLocale(Isolate* isolate, String locale,
429 : void (*morph_func)(icu::Locale*, UErrorCode*)) {
430 45837 : UErrorCode status = U_ZERO_ERROR;
431 : icu::Locale icu_locale =
432 183348 : icu::Locale::forLanguageTag(locale.ToCString().get(), status);
433 : // TODO(ftang): Remove the following lines after ICU-8420 fixed.
434 : // Due to ICU-8420 "und" is turn into "" by forLanguageTag,
435 : // we have to work around to use icu::Locale("und") directly
436 45837 : if (icu_locale.getName()[0] == '\0') icu_locale = icu::Locale("und");
437 45837 : CHECK(U_SUCCESS(status));
438 45837 : CHECK(!icu_locale.isBogus());
439 45837 : (*morph_func)(&icu_locale, &status);
440 45837 : CHECK(U_SUCCESS(status));
441 45837 : CHECK(!icu_locale.isBogus());
442 91674 : std::string locale_str = Intl::ToLanguageTag(icu_locale).FromJust();
443 91674 : return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
444 : }
445 :
446 : } // namespace
447 :
448 22905 : Handle<String> JSLocale::Maximize(Isolate* isolate, String locale) {
449 : return MorphLocale(isolate, locale,
450 22905 : [](icu::Locale* icu_locale, UErrorCode* status) {
451 22905 : icu_locale->addLikelySubtags(*status);
452 45810 : });
453 : }
454 :
455 22932 : Handle<String> JSLocale::Minimize(Isolate* isolate, String locale) {
456 : return MorphLocale(isolate, locale,
457 22932 : [](icu::Locale* icu_locale, UErrorCode* status) {
458 22932 : icu_locale->minimizeSubtags(*status);
459 45864 : });
460 : }
461 :
462 18 : Handle<Object> JSLocale::Language(Isolate* isolate, Handle<JSLocale> locale) {
463 : Factory* factory = isolate->factory();
464 36 : const char* language = locale->icu_locale()->raw()->getLanguage();
465 18 : if (strlen(language) == 0) return factory->undefined_value();
466 18 : return factory->NewStringFromAsciiChecked(language);
467 : }
468 :
469 27 : Handle<Object> JSLocale::Script(Isolate* isolate, Handle<JSLocale> locale) {
470 : Factory* factory = isolate->factory();
471 54 : const char* script = locale->icu_locale()->raw()->getScript();
472 45 : if (strlen(script) == 0) return factory->undefined_value();
473 9 : return factory->NewStringFromAsciiChecked(script);
474 : }
475 :
476 18 : Handle<Object> JSLocale::Region(Isolate* isolate, Handle<JSLocale> locale) {
477 : Factory* factory = isolate->factory();
478 36 : const char* region = locale->icu_locale()->raw()->getCountry();
479 27 : if (strlen(region) == 0) return factory->undefined_value();
480 9 : return factory->NewStringFromAsciiChecked(region);
481 : }
482 :
483 18 : Handle<String> JSLocale::BaseName(Isolate* isolate, Handle<JSLocale> locale) {
484 : icu::Locale icu_locale =
485 36 : icu::Locale::createFromName(locale->icu_locale()->raw()->getBaseName());
486 36 : std::string base_name = Intl::ToLanguageTag(icu_locale).FromJust();
487 36 : return isolate->factory()->NewStringFromAsciiChecked(base_name.c_str());
488 : }
489 :
490 18 : Handle<Object> JSLocale::Calendar(Isolate* isolate, Handle<JSLocale> locale) {
491 18 : return UnicodeKeywordValue(isolate, locale, "ca");
492 : }
493 :
494 18 : Handle<Object> JSLocale::CaseFirst(Isolate* isolate, Handle<JSLocale> locale) {
495 18 : return UnicodeKeywordValue(isolate, locale, "kf");
496 : }
497 :
498 18 : Handle<Object> JSLocale::Collation(Isolate* isolate, Handle<JSLocale> locale) {
499 18 : return UnicodeKeywordValue(isolate, locale, "co");
500 : }
501 :
502 18 : Handle<Object> JSLocale::HourCycle(Isolate* isolate, Handle<JSLocale> locale) {
503 18 : return UnicodeKeywordValue(isolate, locale, "hc");
504 : }
505 :
506 18 : Handle<Object> JSLocale::Numeric(Isolate* isolate, Handle<JSLocale> locale) {
507 : Factory* factory = isolate->factory();
508 : icu::Locale* icu_locale = locale->icu_locale()->raw();
509 18 : UErrorCode status = U_ZERO_ERROR;
510 : std::string numeric =
511 18 : icu_locale->getUnicodeKeywordValue<std::string>("kn", status);
512 54 : return (numeric == "true") ? factory->true_value() : factory->false_value();
513 : }
514 :
515 18 : Handle<Object> JSLocale::NumberingSystem(Isolate* isolate,
516 : Handle<JSLocale> locale) {
517 18 : return UnicodeKeywordValue(isolate, locale, "nu");
518 : }
519 :
520 119961 : std::string JSLocale::ToString(Handle<JSLocale> locale) {
521 : icu::Locale* icu_locale = locale->icu_locale()->raw();
522 239922 : return Intl::ToLanguageTag(*icu_locale).FromJust();
523 : }
524 :
525 119889 : Handle<String> JSLocale::ToString(Isolate* isolate, Handle<JSLocale> locale) {
526 119889 : std::string locale_str = JSLocale::ToString(locale);
527 239778 : return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
528 : }
529 :
530 : } // namespace internal
531 120216 : } // namespace v8
|