5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-list-format.h" 14 #include "src/elements-inl.h" 15 #include "src/elements.h" 16 #include "src/heap/factory.h" 17 #include "src/isolate.h" 18 #include "src/objects-inl.h" 19 #include "src/objects/intl-objects.h" 20 #include "src/objects/js-array-inl.h" 21 #include "src/objects/js-list-format-inl.h" 22 #include "src/objects/managed.h" 23 #include "unicode/fieldpos.h" 24 #include "unicode/fpositer.h" 25 #include "unicode/listformatter.h" 26 #include "unicode/ulistformatter.h" 32 const char* kStandard =
"standard";
33 const char* kOr =
"or";
34 const char* kUnit =
"unit";
35 const char* kStandardShort =
"standard-short";
36 const char* kUnitShort =
"unit-short";
37 const char* kUnitNarrow =
"unit-narrow";
39 const char* GetIcuStyleString(JSListFormat::Style style,
40 JSListFormat::Type type) {
42 case JSListFormat::Type::CONJUNCTION:
44 case JSListFormat::Style::LONG:
46 case JSListFormat::Style::SHORT:
47 return kStandardShort;
51 case JSListFormat::Style::NARROW:
52 case JSListFormat::Style::COUNT:
55 case JSListFormat::Type::DISJUNCTION:
64 case JSListFormat::Style::LONG:
65 case JSListFormat::Style::SHORT:
70 case JSListFormat::Style::NARROW:
71 case JSListFormat::Style::COUNT:
74 case JSListFormat::Type::UNIT:
76 case JSListFormat::Style::LONG:
78 case JSListFormat::Style::SHORT:
80 case JSListFormat::Style::NARROW:
82 case JSListFormat::Style::COUNT:
85 case JSListFormat::Type::COUNT:
92 JSListFormat::Style get_style(
const char* str) {
95 if (strcmp(&str[1],
"arrow") == 0)
return JSListFormat::Style::NARROW;
98 if (strcmp(&str[1],
"ong") == 0)
return JSListFormat::Style::LONG;
101 if (strcmp(&str[1],
"hort") == 0)
return JSListFormat::Style::SHORT;
107 JSListFormat::Type get_type(
const char* str) {
110 if (strcmp(&str[1],
"onjunction") == 0)
111 return JSListFormat::Type::CONJUNCTION;
114 if (strcmp(&str[1],
"isjunction") == 0)
115 return JSListFormat::Type::DISJUNCTION;
118 if (strcmp(&str[1],
"nit") == 0)
return JSListFormat::Type::UNIT;
124 MaybeHandle<JSListFormat> JSListFormat::Initialize(
125 Isolate* isolate, Handle<JSListFormat> list_format, Handle<Object> locales,
126 Handle<Object> input_options) {
127 list_format->set_flags(0);
129 Handle<JSReceiver> options;
131 Maybe<std::vector<std::string>> maybe_requested_locales =
132 Intl::CanonicalizeLocaleList(isolate, locales);
133 MAYBE_RETURN(maybe_requested_locales, Handle<JSListFormat>());
134 std::vector<std::string> requested_locales =
135 maybe_requested_locales.FromJust();
138 if (input_options->IsUndefined(isolate)) {
140 options = isolate->factory()->NewJSObjectWithNullProto();
144 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
145 Object::ToObject(isolate, input_options),
154 Maybe<Type> maybe_type = Intl::GetStringOption<Type>(
155 isolate, options,
"type",
"Intl.ListFormat",
156 {
"conjunction",
"disjunction",
"unit"},
157 {Type::CONJUNCTION, Type::DISJUNCTION, Type::UNIT}, Type::CONJUNCTION);
158 MAYBE_RETURN(maybe_type, MaybeHandle<JSListFormat>());
159 Type type_enum = maybe_type.FromJust();
162 list_format->set_type(type_enum);
166 Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
167 isolate, options,
"style",
"Intl.ListFormat", {
"long",
"short",
"narrow"},
168 {Style::LONG, Style::SHORT, Style::NARROW}, Style::LONG);
169 MAYBE_RETURN(maybe_style, MaybeHandle<JSListFormat>());
170 Style style_enum = maybe_style.FromJust();
173 list_format->set_style(style_enum);
177 Maybe<Intl::MatcherOption> maybe_locale_matcher =
178 Intl::GetLocaleMatcher(isolate, options,
"Intl.ListFormat");
179 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSListFormat>());
180 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
184 if (style_enum == Style::NARROW && type_enum != Type::UNIT) {
186 isolate, NewRangeError(MessageTemplate::kIllegalTypeWhileStyleNarrow),
192 Intl::ResolvedLocale r =
193 Intl::ResolveLocale(isolate, JSListFormat::GetAvailableLocales(),
194 requested_locales, matcher, {});
197 Handle<String> locale_str =
198 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
199 list_format->set_locale(*locale_str);
201 icu::Locale icu_locale = r.icu_locale;
202 UErrorCode status = U_ZERO_ERROR;
203 icu::ListFormatter* formatter = icu::ListFormatter::createInstance(
204 icu_locale, GetIcuStyleString(style_enum, type_enum), status);
205 if (U_FAILURE(status)) {
207 FATAL(
"Failed to create ICU list formatter, are ICU data files missing?");
209 CHECK_NOT_NULL(formatter);
211 Handle<Managed<icu::ListFormatter>> managed_formatter =
212 Managed<icu::ListFormatter>::FromRawPtr(isolate, 0, formatter);
214 list_format->set_icu_formatter(*managed_formatter);
219 Handle<JSObject> JSListFormat::ResolvedOptions(Isolate* isolate,
220 Handle<JSListFormat> format) {
221 Factory* factory = isolate->factory();
223 Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
231 Handle<String> locale(format->locale(), isolate);
232 JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
234 JSObject::AddProperty(isolate, result, factory->type_string(),
235 format->TypeAsString(), NONE);
236 JSObject::AddProperty(isolate, result, factory->style_string(),
237 format->StyleAsString(), NONE);
242 Handle<String> JSListFormat::StyleAsString()
const {
245 return GetReadOnlyRoots().long_string_handle();
247 return GetReadOnlyRoots().short_string_handle();
249 return GetReadOnlyRoots().narrow_string_handle();
255 Handle<String> JSListFormat::TypeAsString()
const {
257 case Type::CONJUNCTION:
258 return GetReadOnlyRoots().conjunction_string_handle();
259 case Type::DISJUNCTION:
260 return GetReadOnlyRoots().disjunction_string_handle();
262 return GetReadOnlyRoots().unit_string_handle();
270 MaybeHandle<JSArray> GenerateListFormatParts(
271 Isolate* isolate,
const icu::UnicodeString& formatted,
272 const std::vector<icu::FieldPosition>& positions) {
273 Factory* factory = isolate->factory();
274 Handle<JSArray> array =
275 factory->NewJSArray(static_cast<int>(positions.size()));
277 int prev_item_end_index = 0;
278 Handle<String> substring;
279 for (
const icu::FieldPosition pos : positions) {
280 CHECK(pos.getBeginIndex() >= prev_item_end_index);
281 CHECK(pos.getField() == ULISTFMT_ELEMENT_FIELD);
282 if (pos.getBeginIndex() != prev_item_end_index) {
283 ASSIGN_RETURN_ON_EXCEPTION(
285 Intl::ToString(isolate, formatted, prev_item_end_index,
286 pos.getBeginIndex()),
288 Intl::AddElement(isolate, array, index++, factory->literal_string(),
291 ASSIGN_RETURN_ON_EXCEPTION(
293 Intl::ToString(isolate, formatted, pos.getBeginIndex(),
296 Intl::AddElement(isolate, array, index++, factory->element_string(),
298 prev_item_end_index = pos.getEndIndex();
300 if (prev_item_end_index != formatted.length()) {
301 ASSIGN_RETURN_ON_EXCEPTION(
303 Intl::ToString(isolate, formatted, prev_item_end_index,
306 Intl::AddElement(isolate, array, index++, factory->literal_string(),
314 std::vector<icu::FieldPosition> GenerateFieldPosition(
315 icu::FieldPositionIterator iter) {
316 std::vector<icu::FieldPosition> positions;
317 icu::FieldPosition pos;
318 while (iter.next(pos)) {
320 if (pos.getField() == ULISTFMT_ELEMENT_FIELD) {
321 positions.push_back(pos);
335 std::sort(positions.begin(), positions.end(),
336 [](icu::FieldPosition a, icu::FieldPosition b) {
337 return a.getBeginIndex() < b.getBeginIndex();
343 Maybe<std::vector<icu::UnicodeString>> ToUnicodeStringArray(
344 Isolate* isolate, Handle<JSArray> array) {
345 Factory* factory = isolate->factory();
349 auto* accessor = array->GetElementsAccessor();
350 uint32_t length = accessor->NumberOfElements(*array);
358 std::vector<icu::UnicodeString> result;
360 DCHECK(accessor->HasElement(*array,
i));
361 Handle<Object> item = accessor->Get(array,
i);
362 DCHECK(!item.is_null());
363 if (!item->IsString()) {
364 THROW_NEW_ERROR_RETURN_VALUE(
366 NewTypeError(MessageTemplate::kArrayItemNotType,
367 factory->list_string(),
371 factory->NewNumber(
i), factory->String_string()),
372 Nothing<std::vector<icu::UnicodeString>>());
375 Intl::ToICUUnicodeString(isolate, Handle<String>::cast(item)));
377 DCHECK(!array->HasDictionaryElements());
384 MaybeHandle<String> JSListFormat::FormatList(Isolate* isolate,
385 Handle<JSListFormat> format,
386 Handle<JSArray> list) {
387 DCHECK(!list->IsUndefined());
391 Maybe<std::vector<icu::UnicodeString>> maybe_array =
392 ToUnicodeStringArray(isolate, list);
393 MAYBE_RETURN(maybe_array, Handle<String>());
394 std::vector<icu::UnicodeString> array = maybe_array.FromJust();
396 icu::ListFormatter* formatter = format->icu_formatter()->raw();
397 CHECK_NOT_NULL(formatter);
399 UErrorCode status = U_ZERO_ERROR;
400 icu::UnicodeString formatted;
401 formatter->format(array.data(),
static_cast<int32_t
>(array.size()), formatted,
403 DCHECK(U_SUCCESS(status));
405 return Intl::ToString(isolate, formatted);
408 std::set<std::string> JSListFormat::GetAvailableLocales() {
409 int32_t num_locales = 0;
414 const icu::Locale* icu_available_locales =
415 icu::Locale::getAvailableLocales(num_locales);
416 return Intl::BuildLocaleSet(icu_available_locales, num_locales);
420 MaybeHandle<JSArray> JSListFormat::FormatListToParts(
421 Isolate* isolate, Handle<JSListFormat> format, Handle<JSArray> list) {
422 DCHECK(!list->IsUndefined());
426 Maybe<std::vector<icu::UnicodeString>> maybe_array =
427 ToUnicodeStringArray(isolate, list);
428 MAYBE_RETURN(maybe_array, Handle<JSArray>());
429 std::vector<icu::UnicodeString> array = maybe_array.FromJust();
431 icu::ListFormatter* formatter = format->icu_formatter()->raw();
432 CHECK_NOT_NULL(formatter);
434 UErrorCode status = U_ZERO_ERROR;
435 icu::UnicodeString formatted;
436 icu::FieldPositionIterator iter;
437 formatter->format(array.data(),
static_cast<int32_t
>(array.size()), formatted,
439 DCHECK(U_SUCCESS(status));
441 std::vector<icu::FieldPosition> field_positions = GenerateFieldPosition(iter);
442 return GenerateListFormatParts(isolate, formatted, field_positions);