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