#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif
#include "src/objects/js-locale.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "src/api/api.h"
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-locale-inl.h"
#include "src/objects/managed-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/option-utils.h"
#include "unicode/calendar.h"
#include "unicode/char16ptr.h"
#include "unicode/coll.h"
#include "unicode/dtptngen.h"
#include "unicode/localebuilder.h"
#include "unicode/locid.h"
#include "unicode/ucal.h"
#include "unicode/uloc.h"
#include "unicode/ulocdata.h"
#include "unicode/unistr.h"
namespace v8 {
namespace internal {
namespace {
struct OptionData {
Handle<String> (Factory::*object_key)();
const char* key;
const std::span<const std::string_view> possible_values;
bool is_bool_value;
};
struct ValueAndType {
const char* value;
const char* type;
};
Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
DirectHandle<JSReceiver> options,
icu::LocaleBuilder* builder) {
DCHECK(isolate);
static const auto hour_cycle_values =
std::to_array<const std::string_view>({"h11", "h12", "h23", "h24"});
static const auto case_first_values =
std::to_array<const std::string_view>({"upper", "lower", "false"});
const auto empty_values = std::span<std::string_view>();
const std::array<OptionData, 7> kOptionToUnicodeTagMap = {
{{&Factory::calendar_string, "ca", empty_values, false},
{&Factory::collation_string, "co", empty_values, false},
{&Factory::firstDayOfWeek_string, "fw", empty_values, false},
{&Factory::hourCycle_string, "hc", hour_cycle_values, false},
{&Factory::caseFirst_string, "kf", case_first_values, false},
{&Factory::numeric_string, "kn", empty_values, true},
{&Factory::numberingSystem_string, "nu", empty_values, false}}};
for (const auto& option_to_bcp47 : kOptionToUnicodeTagMap) {
bool value_bool = false;
DirectHandle<String> name =
(isolate->factory()->*option_to_bcp47.object_key)();
bool found = false;
std::string_view value_str;
std::string owned;
if (option_to_bcp47.is_bool_value) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, found,
GetBoolOption(isolate, options, name, "locale", &value_bool), {});
} else if (option_to_bcp47.possible_values.empty()) {
DirectHandle<String> output;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, found,
GetStringOption(isolate, options, name, "locale", &output), {});
if (found) {
owned = output->ToStdString();
value_str = owned;
}
} else {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value_str,
GetStringOption<std::string_view>(
isolate, options, name, "locale", option_to_bcp47.possible_values,
option_to_bcp47.possible_values, std::string_view()),
{});
if (!value_str.empty()) {
found = true;
}
}
if (!found) continue;
const char* type = value_str.data();
if (strcmp(option_to_bcp47.key, "fw") == 0) {
const std::array<ValueAndType, 8> kFirstDayValuesAndTypes = {
{{"0", "sun"},
{"1", "mon"},
{"2", "tue"},
{"3", "wed"},
{"4", "thu"},
{"5", "fri"},
{"6", "sat"},
{"7", "sun"}}};
for (const auto& value_to_type : kFirstDayValuesAndTypes) {
if (strcmp(type, value_to_type.value) == 0) {
type = value_to_type.type;
break;
}
}
} else if (option_to_bcp47.is_bool_value) {
type = value_bool ? "true" : "false";
}
DCHECK_NOT_NULL(type);
if (!uloc_toLegacyType(uloc_toLegacyKey(option_to_bcp47.key), type)) {
return Just(false);
}
builder->setUnicodeLocaleKeyword(option_to_bcp47.key, type);
}
return Just(true);
}
DirectHandle<Object> UnicodeKeywordValue(Isolate* isolate,
DirectHandle<JSLocale> locale,
const char* key) {
icu::Locale* icu_locale = locale->icu_locale()->raw();
UErrorCode status = U_ZERO_ERROR;
std::string value =
icu_locale->getUnicodeKeywordValue<std::string>(key, status);
if (status == U_ILLEGAL_ARGUMENT_ERROR || value.empty()) {
return isolate->factory()->undefined_value();
}
if (value == "yes") {
value = "true";
}
if (value == "true" && strcmp(key, "kf") == 0) {
return isolate->factory()->NewStringFromStaticChars("");
}
return isolate->factory()->NewStringFromAsciiChecked(value.c_str());
}
bool IsCheckRange(std::string_view str, size_t min, size_t max,
bool(range_check_func)(char)) {
if (!base::IsInRange(str.length(), min, max)) return false;
for (size_t i = 0; i < str.length(); i++) {
if (!range_check_func(str[i])) return false;
}
return true;
}
bool IsAlpha(std::string_view str, size_t min, size_t max) {
return IsCheckRange(str, min, max, [](char c) -> bool {
return base::IsInRange(c, 'a', 'z') || base::IsInRange(c, 'A', 'Z');
});
}
bool IsDigit(std::string_view str, size_t min, size_t max) {
return IsCheckRange(str, min, max, [](char c) -> bool {
return base::IsInRange(c, '0', '9');
});
}
bool IsAlphanum(std::string_view str, size_t min, size_t max) {
return IsCheckRange(str, min, max, [](char c) -> bool {
return base::IsInRange(c, 'a', 'z') || base::IsInRange(c, 'A', 'Z') ||
base::IsInRange(c, '0', '9');
});
}
bool IsUnicodeLanguageSubtag(std::string_view value) {
return IsAlpha(value, 2, 3) || IsAlpha(value, 5, 8);
}
bool IsUnicodeScriptSubtag(std::string_view value) {
return IsAlpha(value, 4, 4);
}
bool IsUnicodeRegionSubtag(std::string_view value) {
return IsAlpha(value, 2, 2) || IsDigit(value, 3, 3);
}
bool IsDigitAlphanum3(std::string_view value) {
return value.length() == 4 && base::IsInRange(value[0], '0', '9') &&
IsAlphanum(value.substr(1), 3, 3);
}
bool IsUnicodeVariantSubtag(std::string_view value) {
return IsAlphanum(value, 5, 8) || IsDigitAlphanum3(value);
}
bool IsExtensionSingleton(std::string_view value) {
return IsAlphanum(value, 1, 1);
}
int32_t weekdayFromEDaysOfWeek(icu::Calendar::EDaysOfWeek eDaysOfWeek) {
return (eDaysOfWeek == icu::Calendar::SUNDAY) ? 7 : eDaysOfWeek - 1;
}
}
bool JSLocale::Is38AlphaNumList(std::string_view in) {
std::string_view value = in;
while (true) {
std::size_t found_dash = value.find('-');
if (found_dash == std::string::npos) {
return IsAlphanum(value, 3, 8);
}
if (!IsAlphanum(value.substr(0, found_dash), 3, 8)) return false;
value = value.substr(found_dash + 1);
}
}
bool JSLocale::Is3Alpha(std::string_view value) { return IsAlpha(value, 3, 3); }
bool JSLocale::StartsWithUnicodeLanguageId(std::string_view value) {
if (value.empty()) return false;
std::vector<std::string_view> tokens;
size_t token_start = 0;
size_t token_end;
for (token_end = 0; token_end < value.size(); ++token_end) {
if (value[token_end] == '-') {
tokens.emplace_back(&value[token_start], token_end - token_start);
token_start = token_end + 1;
}
}
if (token_start != token_end) {
tokens.emplace_back(&value[token_start], token_end - token_start);
}
DCHECK(!tokens.empty());
if (!IsUnicodeLanguageSubtag(tokens[0])) return false;
if (tokens.size() == 1) return true;
if (IsExtensionSingleton(tokens[1])) return true;
size_t index = 1;
if (IsUnicodeScriptSubtag(tokens[index])) {
index++;
if (index == tokens.size()) return true;
}
if (IsUnicodeRegionSubtag(tokens[index])) {
index++;
}
while (index < tokens.size()) {
if (IsExtensionSingleton(tokens[index])) return true;
if (!IsUnicodeVariantSubtag(tokens[index])) return false;
index++;
}
return true;
}
namespace {
bool DuplicateVariants(std::string variants) {
if (variants.length() >= 9) {
std::transform(
variants.begin(), variants.end(), variants.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
size_t pos = 0;
size_t count = 1;
std::set<std::string> set;
while ((pos = variants.find('-')) != std::string::npos) {
set.insert(variants.substr(0, pos));
variants.erase(0, pos + 1);
count++;
}
set.insert(variants);
if (count != set.size()) {
return true;
}
}
return false;
}
Maybe<bool> ApplyOptionsToTag(Isolate* isolate, DirectHandle<String> tag,
DirectHandle<JSReceiver> options,
icu::LocaleBuilder* builder) {
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
if (tag->length() == 0) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty));
}
v8::String::Utf8Value bcp47_tag(v8_isolate, v8::Utils::ToLocal(tag));
builder->setLanguageTag(
{*bcp47_tag, static_cast<int32_t>(bcp47_tag.length())});
DCHECK_LT(0, bcp47_tag.length());
DCHECK_NOT_NULL(*bcp47_tag);
if (!JSLocale::StartsWithUnicodeLanguageId(*bcp47_tag)) {
return Just(false);
}
UErrorCode status = U_ZERO_ERROR;
icu::Locale canonicalized = builder->build(status);
canonicalized.canonicalize(status);
if (U_FAILURE(status)) {
return Just(false);
}
builder->setLocale(canonicalized);
DirectHandle<String> language_str;
Maybe<bool> maybe_language =
GetStringOption(isolate, options, isolate->factory()->language_string(),
"ApplyOptionsToTag", &language_str);
MAYBE_RETURN(maybe_language, Nothing<bool>());
if (maybe_language.FromJust()) {
std::string language_stdstr = language_str->ToStdString();
builder->setLanguage(language_stdstr);
builder->build(status);
if (U_FAILURE(status) || language_stdstr.empty() ||
IsAlpha(language_stdstr, 4, 4)) {
return Just(false);
}
}
DirectHandle<String> script_str;
Maybe<bool> maybe_script =
GetStringOption(isolate, options, isolate->factory()->script_string(),
"ApplyOptionsToTag", &script_str);
MAYBE_RETURN(maybe_script, Nothing<bool>());
if (maybe_script.FromJust()) {
std::string script_stdstr = script_str->ToStdString();
builder->setScript(script_stdstr);
builder->build(status);
if (U_FAILURE(status) || script_stdstr.empty()) {
return Just(false);
}
}
DirectHandle<String> region_str;
Maybe<bool> maybe_region =
GetStringOption(isolate, options, isolate->factory()->region_string(),
"ApplyOptionsToTag", ®ion_str);
MAYBE_RETURN(maybe_region, Nothing<bool>());
if (maybe_region.FromJust()) {
std::string region_stdstr = region_str->ToStdString();
builder->setRegion(region_stdstr);
builder->build(status);
if (U_FAILURE(status) || region_stdstr.empty()) {
return Just(false);
}
}
if (v8_flags.js_intl_locale_variants) {
DirectHandle<String> variants_str;
Maybe<bool> maybe_variants =
GetStringOption(isolate, options, isolate->factory()->variants_string(),
"ApplyOptionsToTag", &variants_str);
MAYBE_RETURN(maybe_variants, Nothing<bool>());
if (maybe_variants.FromJust()) {
std::string variants_stdstr = variants_str->ToStdString();
builder->setVariant(variants_stdstr);
builder->build(status);
if (U_FAILURE(status) || variants_stdstr.empty()) {
return Just(false);
}
if (DuplicateVariants(variants_stdstr)) {
return Just(false);
}
}
}
return Just(true);
}
}
MaybeDirectHandle<JSLocale> JSLocale::New(Isolate* isolate,
DirectHandle<Map> map,
DirectHandle<String> locale_str,
DirectHandle<JSReceiver> options) {
icu::LocaleBuilder builder;
Maybe<bool> maybe_apply =
ApplyOptionsToTag(isolate, locale_str, options, &builder);
MAYBE_RETURN(maybe_apply, MaybeDirectHandle<JSLocale>());
if (!maybe_apply.FromJust()) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters));
}
Maybe<bool> maybe_insert =
InsertOptionsIntoLocale(isolate, options, &builder);
MAYBE_RETURN(maybe_insert, MaybeDirectHandle<JSLocale>());
UErrorCode status = U_ZERO_ERROR;
icu::Locale icu_locale = builder.build(status);
icu_locale.canonicalize(status);
if (!maybe_insert.FromJust() || U_FAILURE(status)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters));
}
DirectHandle<Managed<icu::Locale>> managed_locale =
Managed<icu::Locale>::From(
isolate, 0, std::shared_ptr<icu::Locale>{icu_locale.clone()});
DirectHandle<JSLocale> locale =
Cast<JSLocale>(isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
DisallowGarbageCollection no_gc;
locale->set_icu_locale(*managed_locale);
return locale;
}
namespace {
MaybeDirectHandle<JSLocale> Construct(Isolate* isolate,
const icu::Locale& icu_locale) {
DirectHandle<Managed<icu::Locale>> managed_locale =
Managed<icu::Locale>::From(
isolate, 0, std::shared_ptr<icu::Locale>{icu_locale.clone()});
DirectHandle<JSFunction> constructor(
isolate->native_context()->intl_locale_function(), isolate);
DirectHandle<Map> map;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, map,
JSFunction::GetDerivedMap(isolate, constructor, constructor));
DirectHandle<JSLocale> locale =
Cast<JSLocale>(isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
DisallowGarbageCollection no_gc;
locale->set_icu_locale(*managed_locale);
return locale;
}
}
MaybeDirectHandle<JSLocale> JSLocale::Maximize(Isolate* isolate,
DirectHandle<JSLocale> locale) {
icu::Locale source(*(locale->icu_locale()->raw()));
icu::Locale result = icu::Locale::createFromName(source.getBaseName());
UErrorCode status = U_ZERO_ERROR;
result.addLikelySubtags(status);
if (strlen(source.getBaseName()) != strlen(result.getBaseName())) {
if (strlen(source.getBaseName()) != strlen(source.getName())) {
result = icu::LocaleBuilder()
.setLocale(source)
.setLanguage(result.getLanguage())
.setRegion(result.getCountry())
.setScript(result.getScript())
.setVariant(result.getVariant())
.build(status);
}
} else {
result = source;
}
if (U_FAILURE(status) || result.isBogus()) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters));
}
return Construct(isolate, result);
}
MaybeDirectHandle<JSLocale> JSLocale::Minimize(Isolate* isolate,
DirectHandle<JSLocale> locale) {
icu::Locale source(*(locale->icu_locale()->raw()));
icu::Locale result = icu::Locale::createFromName(source.getBaseName());
UErrorCode status = U_ZERO_ERROR;
result.minimizeSubtags(status);
if (strcmp(source.getBaseName(), result.getBaseName()) != 0) {
if (strlen(source.getBaseName()) != strlen(source.getName())) {
result = icu::LocaleBuilder()
.setLocale(source)
.setLanguage(result.getLanguage())
.setRegion(result.getCountry())
.setScript(result.getScript())
.setVariant(result.getVariant())
.build(status);
}
} else {
result = source;
}
if (U_FAILURE(status) || result.isBogus()) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters));
}
return Construct(isolate, result);
}
template <typename T>
MaybeDirectHandle<JSArray> GetKeywordValuesFromLocale(
Isolate* isolate, const char* key, const char* unicode_key,
const icu::Locale& locale, bool (*removes)(const char*), bool commonly_used,
bool sort) {
Factory* factory = isolate->factory();
UErrorCode status = U_ZERO_ERROR;
std::string ext =
locale.getUnicodeKeywordValue<std::string>(unicode_key, status);
if (!ext.empty()) {
DirectHandle<FixedArray> fixed_array = factory->NewFixedArray(1);
DirectHandle<String> str = factory->NewStringFromAsciiChecked(ext.c_str());
fixed_array->set(0, *str);
return factory->NewJSArrayWithElements(fixed_array);
}
status = U_ZERO_ERROR;
std::unique_ptr<icu::StringEnumeration> enumeration(
T::getKeywordValuesForLocale(key, locale, commonly_used, status));
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
return Intl::ToJSArray(
isolate,
[unicode_key](const char* value) {
return std::string(uloc_toUnicodeLocaleType(unicode_key, value));
},
enumeration.get(), removes, sort);
}
namespace {
MaybeDirectHandle<JSArray> CalendarsForLocale(Isolate* isolate,
const icu::Locale& icu_locale,
bool commonly_used, bool sort) {
return GetKeywordValuesFromLocale<icu::Calendar>(
isolate, "calendar", "ca", icu_locale, nullptr, commonly_used, sort);
}
}
MaybeDirectHandle<JSArray> JSLocale::GetCalendars(
Isolate* isolate, DirectHandle<JSLocale> locale) {
icu::Locale icu_locale(*(locale->icu_locale()->raw()));
return CalendarsForLocale(isolate, icu_locale, true, false);
}
MaybeDirectHandle<JSArray> Intl::AvailableCalendars(Isolate* isolate) {
icu::Locale icu_locale("und");
return CalendarsForLocale(isolate, icu_locale, false, true);
}
MaybeDirectHandle<JSArray> JSLocale::GetCollations(
Isolate* isolate, DirectHandle<JSLocale> locale) {
icu::Locale icu_locale(*(locale->icu_locale()->raw()));
return GetKeywordValuesFromLocale<icu::Collator>(
isolate, "collations", "co", icu_locale, Intl::RemoveCollation, true,
true);
}
MaybeDirectHandle<JSArray> JSLocale::GetHourCycles(
Isolate* isolate, DirectHandle<JSLocale> locale) {
icu::Locale icu_locale(*(locale->icu_locale()->raw()));
Factory* factory = isolate->factory();
DirectHandle<FixedArray> fixed_array = factory->NewFixedArray(1);
UErrorCode status = U_ZERO_ERROR;
std::string ext =
icu_locale.getUnicodeKeywordValue<std::string>("hc", status);
if (!ext.empty()) {
DirectHandle<String> str = factory->NewStringFromAsciiChecked(ext.c_str());
fixed_array->set(0, *str);
return factory->NewJSArrayWithElements(fixed_array);
}
status = U_ZERO_ERROR;
std::unique_ptr<icu::DateTimePatternGenerator> generator(
icu::DateTimePatternGenerator::createInstance(icu_locale, status));
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
UDateFormatHourCycle hc = generator->getDefaultHourCycle(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
DirectHandle<String> hour_cycle;
switch (hc) {
case UDAT_HOUR_CYCLE_11:
hour_cycle = factory->h11_string();
break;
case UDAT_HOUR_CYCLE_12:
hour_cycle = factory->h12_string();
break;
case UDAT_HOUR_CYCLE_23:
hour_cycle = factory->h23_string();
break;
case UDAT_HOUR_CYCLE_24:
hour_cycle = factory->h24_string();
break;
default:
break;
}
fixed_array->set(0, *hour_cycle);
return factory->NewJSArrayWithElements(fixed_array);
}
MaybeDirectHandle<JSArray> JSLocale::GetNumberingSystems(
Isolate* isolate, DirectHandle<JSLocale> locale) {
icu::Locale icu_locale(*(locale->icu_locale()->raw()));
Factory* factory = isolate->factory();
UErrorCode status = U_ZERO_ERROR;
DirectHandle<FixedArray> fixed_array = factory->NewFixedArray(1);
std::string numbering_system =
icu_locale.getUnicodeKeywordValue<std::string>("nu", status);
if (numbering_system.empty()) {
numbering_system = Intl::GetNumberingSystem(icu_locale);
}
DirectHandle<String> str =
factory->NewStringFromAsciiChecked(numbering_system.c_str());
fixed_array->set(0, *str);
return factory->NewJSArrayWithElements(fixed_array);
}
MaybeDirectHandle<Object> JSLocale::GetTimeZones(
Isolate* isolate, DirectHandle<JSLocale> locale) {
icu::Locale icu_locale(*(locale->icu_locale()->raw()));
Factory* factory = isolate->factory();
const char* region = icu_locale.getCountry();
if (region == nullptr || strlen(region) == 0) {
return factory->undefined_value();
}
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::StringEnumeration> enumeration(
icu::TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL,
region, nullptr, status));
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
return Intl::ToJSArray(isolate, nullptr, enumeration.get(), nullptr, true);
}
MaybeDirectHandle<JSObject> JSLocale::GetTextInfo(
Isolate* isolate, DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
DirectHandle<JSObject> info =
factory->NewJSObject(isolate->object_function());
DirectHandle<String> dir = locale->icu_locale()->raw()->isRightToLeft()
? factory->rtl_string()
: factory->ltr_string();
CHECK(JSReceiver::CreateDataProperty(
isolate, info, factory->direction_string(), dir, Just(kDontThrow))
.FromJust());
return info;
}
MaybeDirectHandle<JSObject> JSLocale::GetWeekInfo(
Isolate* isolate, DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
DirectHandle<JSObject> info =
factory->NewJSObject(isolate->object_function());
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::Calendar> calendar(
icu::Calendar::createInstance(*(locale->icu_locale()->raw()), status));
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
int32_t fd = weekdayFromEDaysOfWeek(calendar->getFirstDayOfWeek());
Handle<FixedArray> wi = Cast<FixedArray>(factory->NewFixedArray(2));
int32_t length = 0;
for (int32_t i = 1; i <= 7; i++) {
UCalendarDaysOfWeek day =
(i == 7) ? UCAL_SUNDAY : static_cast<UCalendarDaysOfWeek>(i + 1);
if (UCAL_WEEKDAY != calendar->getDayOfWeekType(day, status)) {
wi->set(length++, Smi::FromInt(i));
CHECK_LE(length, 2);
}
}
if (length != 2) {
wi = wi->RightTrimOrEmpty(isolate, wi, length);
}
DirectHandle<JSArray> we = factory->NewJSArrayWithElements(wi);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
CHECK(JSReceiver::CreateDataProperty(
isolate, info, factory->firstDay_string(),
factory->NewNumberFromInt(fd), Just(kDontThrow))
.FromJust());
CHECK(JSReceiver::CreateDataProperty(isolate, info, factory->weekend_string(),
we, Just(kDontThrow))
.FromJust());
return info;
}
DirectHandle<Object> JSLocale::Language(Isolate* isolate,
DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* language = locale->icu_locale()->raw()->getLanguage();
constexpr const char kUnd[] = "und";
if (strlen(language) == 0) {
language = kUnd;
}
return factory->NewStringFromAsciiChecked(language);
}
DirectHandle<Object> JSLocale::Script(Isolate* isolate,
DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* script = locale->icu_locale()->raw()->getScript();
if (strlen(script) == 0) return factory->undefined_value();
return factory->NewStringFromAsciiChecked(script);
}
DirectHandle<Object> JSLocale::Variants(Isolate* isolate,
DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
std::string variants = locale->icu_locale()->raw()->getVariant();
if (variants.length() == 0) return factory->undefined_value();
std::transform(variants.begin(), variants.end(), variants.begin(),
[](unsigned char c) {
if (c == '_') return '-';
return static_cast<char>(std::tolower(c));
});
return factory->NewStringFromAsciiChecked(variants.c_str());
}
DirectHandle<Object> JSLocale::Region(Isolate* isolate,
DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* region = locale->icu_locale()->raw()->getCountry();
if (strlen(region) == 0) return factory->undefined_value();
return factory->NewStringFromAsciiChecked(region);
}
DirectHandle<String> JSLocale::BaseName(Isolate* isolate,
DirectHandle<JSLocale> locale) {
icu::Locale icu_locale =
icu::Locale::createFromName(locale->icu_locale()->raw()->getBaseName());
std::string base_name = Intl::ToLanguageTag(icu_locale).FromJust();
return isolate->factory()->NewStringFromAsciiChecked(base_name.c_str());
}
DirectHandle<Object> JSLocale::Calendar(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "ca");
}
DirectHandle<Object> JSLocale::CaseFirst(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "kf");
}
DirectHandle<Object> JSLocale::Collation(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "co");
}
DirectHandle<Object> JSLocale::FirstDayOfWeek(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "fw");
}
DirectHandle<Object> JSLocale::HourCycle(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "hc");
}
DirectHandle<Object> JSLocale::Numeric(Isolate* isolate,
DirectHandle<JSLocale> locale) {
Factory* factory = isolate->factory();
icu::Locale* icu_locale = locale->icu_locale()->raw();
UErrorCode status = U_ZERO_ERROR;
std::string numeric =
icu_locale->getUnicodeKeywordValue<std::string>("kn", status);
return factory->ToBoolean(numeric == "true");
}
DirectHandle<Object> JSLocale::NumberingSystem(Isolate* isolate,
DirectHandle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "nu");
}
std::string JSLocale::ToString(DirectHandle<JSLocale> locale) {
icu::Locale* icu_locale = locale->icu_locale()->raw();
return Intl::ToLanguageTag(*icu_locale).FromJust();
}
DirectHandle<String> JSLocale::ToString(Isolate* isolate,
DirectHandle<JSLocale> locale) {
std::string locale_str = JSLocale::ToString(locale);
return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
}
}
}