1 // Copyright 2013 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 // limitations under the License.
5
6 #include "src/i18n.h"
7
8 #include <memory>
9
10 #include "src/api.h"
11 #include "src/factory.h"
12 #include "src/isolate.h"
13 #include "unicode/brkiter.h"
14 #include "unicode/calendar.h"
15 #include "unicode/coll.h"
16 #include "unicode/curramt.h"
17 #include "unicode/dcfmtsym.h"
18 #include "unicode/decimfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/gregocal.h"
22 #include "unicode/locid.h"
23 #include "unicode/numfmt.h"
24 #include "unicode/numsys.h"
25 #include "unicode/rbbi.h"
26 #include "unicode/smpdtfmt.h"
27 #include "unicode/timezone.h"
28 #include "unicode/uchar.h"
29 #include "unicode/ucol.h"
30 #include "unicode/ucurr.h"
31 #include "unicode/unum.h"
32 #include "unicode/uversion.h"
33
34 namespace v8 {
35 namespace internal {
36
37 namespace {
38
ExtractStringSetting(Isolate * isolate,Handle<JSObject> options,const char * key,icu::UnicodeString * setting)39 bool ExtractStringSetting(Isolate* isolate,
40 Handle<JSObject> options,
41 const char* key,
42 icu::UnicodeString* setting) {
43 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
44 Handle<Object> object =
45 JSReceiver::GetProperty(options, str).ToHandleChecked();
46 if (object->IsString()) {
47 v8::String::Utf8Value utf8_string(
48 v8::Utils::ToLocal(Handle<String>::cast(object)));
49 *setting = icu::UnicodeString::fromUTF8(*utf8_string);
50 return true;
51 }
52 return false;
53 }
54
55
ExtractIntegerSetting(Isolate * isolate,Handle<JSObject> options,const char * key,int32_t * value)56 bool ExtractIntegerSetting(Isolate* isolate,
57 Handle<JSObject> options,
58 const char* key,
59 int32_t* value) {
60 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
61 Handle<Object> object =
62 JSReceiver::GetProperty(options, str).ToHandleChecked();
63 if (object->IsNumber()) {
64 object->ToInt32(value);
65 return true;
66 }
67 return false;
68 }
69
70
ExtractBooleanSetting(Isolate * isolate,Handle<JSObject> options,const char * key,bool * value)71 bool ExtractBooleanSetting(Isolate* isolate,
72 Handle<JSObject> options,
73 const char* key,
74 bool* value) {
75 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
76 Handle<Object> object =
77 JSReceiver::GetProperty(options, str).ToHandleChecked();
78 if (object->IsBoolean()) {
79 *value = object->BooleanValue();
80 return true;
81 }
82 return false;
83 }
84
85
CreateICUDateFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)86 icu::SimpleDateFormat* CreateICUDateFormat(
87 Isolate* isolate,
88 const icu::Locale& icu_locale,
89 Handle<JSObject> options) {
90 // Create time zone as specified by the user. We have to re-create time zone
91 // since calendar takes ownership.
92 icu::TimeZone* tz = NULL;
93 icu::UnicodeString timezone;
94 if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
95 tz = icu::TimeZone::createTimeZone(timezone);
96 } else {
97 tz = icu::TimeZone::createDefault();
98 }
99
100 // Create a calendar using locale, and apply time zone to it.
101 UErrorCode status = U_ZERO_ERROR;
102 icu::Calendar* calendar =
103 icu::Calendar::createInstance(tz, icu_locale, status);
104
105 if (calendar->getDynamicClassID() ==
106 icu::GregorianCalendar::getStaticClassID()) {
107 icu::GregorianCalendar* gc = (icu::GregorianCalendar*)calendar;
108 UErrorCode status = U_ZERO_ERROR;
109 // The beginning of ECMAScript time, namely -(2**53)
110 const double start_of_time = -9007199254740992;
111 gc->setGregorianChange(start_of_time, status);
112 DCHECK(U_SUCCESS(status));
113 }
114
115 // Make formatter from skeleton. Calendar and numbering system are added
116 // to the locale as Unicode extension (if they were specified at all).
117 icu::SimpleDateFormat* date_format = NULL;
118 icu::UnicodeString skeleton;
119 if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
120 std::unique_ptr<icu::DateTimePatternGenerator> generator(
121 icu::DateTimePatternGenerator::createInstance(icu_locale, status));
122 icu::UnicodeString pattern;
123 if (U_SUCCESS(status))
124 pattern = generator->getBestPattern(skeleton, status);
125
126 date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
127 if (U_SUCCESS(status)) {
128 date_format->adoptCalendar(calendar);
129 }
130 }
131
132 if (U_FAILURE(status)) {
133 delete calendar;
134 delete date_format;
135 date_format = nullptr;
136 }
137
138 return date_format;
139 }
140
141
SetResolvedDateSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::SimpleDateFormat * date_format,Handle<JSObject> resolved)142 void SetResolvedDateSettings(Isolate* isolate,
143 const icu::Locale& icu_locale,
144 icu::SimpleDateFormat* date_format,
145 Handle<JSObject> resolved) {
146 Factory* factory = isolate->factory();
147 UErrorCode status = U_ZERO_ERROR;
148 icu::UnicodeString pattern;
149 date_format->toPattern(pattern);
150 JSObject::SetProperty(
151 resolved, factory->intl_pattern_symbol(),
152 factory->NewStringFromTwoByte(
153 Vector<const uint16_t>(
154 reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
155 pattern.length())).ToHandleChecked(),
156 SLOPPY).Assert();
157
158 // Set time zone and calendar.
159 const icu::Calendar* calendar = date_format->getCalendar();
160 // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
161 // key values. i18n.js maps them to BCP47 values for key "ca".
162 // TODO(jshin): Consider doing it here, instead.
163 const char* calendar_name = calendar->getType();
164 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("calendar"),
165 factory->NewStringFromAsciiChecked(calendar_name),
166 SLOPPY).Assert();
167
168 const icu::TimeZone& tz = calendar->getTimeZone();
169 icu::UnicodeString time_zone;
170 tz.getID(time_zone);
171
172 icu::UnicodeString canonical_time_zone;
173 icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
174 if (U_SUCCESS(status)) {
175 if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
176 JSObject::SetProperty(
177 resolved, factory->NewStringFromStaticChars("timeZone"),
178 factory->NewStringFromStaticChars("UTC"), SLOPPY).Assert();
179 } else {
180 JSObject::SetProperty(
181 resolved, factory->NewStringFromStaticChars("timeZone"),
182 factory->NewStringFromTwoByte(
183 Vector<const uint16_t>(
184 reinterpret_cast<const uint16_t*>(
185 canonical_time_zone.getBuffer()),
186 canonical_time_zone.length())).ToHandleChecked(),
187 SLOPPY).Assert();
188 }
189 }
190
191 // Ugly hack. ICU doesn't expose numbering system in any way, so we have
192 // to assume that for given locale NumberingSystem constructor produces the
193 // same digits as NumberFormat/Calendar would.
194 status = U_ZERO_ERROR;
195 icu::NumberingSystem* numbering_system =
196 icu::NumberingSystem::createInstance(icu_locale, status);
197 if (U_SUCCESS(status)) {
198 const char* ns = numbering_system->getName();
199 JSObject::SetProperty(
200 resolved, factory->NewStringFromStaticChars("numberingSystem"),
201 factory->NewStringFromAsciiChecked(ns), SLOPPY).Assert();
202 } else {
203 JSObject::SetProperty(resolved,
204 factory->NewStringFromStaticChars("numberingSystem"),
205 factory->undefined_value(), SLOPPY).Assert();
206 }
207 delete numbering_system;
208
209 // Set the locale
210 char result[ULOC_FULLNAME_CAPACITY];
211 status = U_ZERO_ERROR;
212 uloc_toLanguageTag(
213 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
214 if (U_SUCCESS(status)) {
215 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
216 factory->NewStringFromAsciiChecked(result),
217 SLOPPY).Assert();
218 } else {
219 // This would never happen, since we got the locale from ICU.
220 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
221 factory->NewStringFromStaticChars("und"),
222 SLOPPY).Assert();
223 }
224 }
225
226
227 template<int internal_fields, EternalHandles::SingletonHandle field>
GetEternal(Isolate * isolate)228 Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
229 if (isolate->eternal_handles()->Exists(field)) {
230 return Handle<ObjectTemplateInfo>::cast(
231 isolate->eternal_handles()->GetSingleton(field));
232 }
233 v8::Local<v8::ObjectTemplate> raw_template =
234 v8::ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate));
235 raw_template->SetInternalFieldCount(internal_fields);
236 return Handle<ObjectTemplateInfo>::cast(
237 isolate->eternal_handles()->CreateSingleton(
238 isolate,
239 *v8::Utils::OpenHandle(*raw_template),
240 field));
241 }
242
243
CreateICUNumberFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)244 icu::DecimalFormat* CreateICUNumberFormat(
245 Isolate* isolate,
246 const icu::Locale& icu_locale,
247 Handle<JSObject> options) {
248 // Make formatter from options. Numbering system is added
249 // to the locale as Unicode extension (if it was specified at all).
250 UErrorCode status = U_ZERO_ERROR;
251 icu::DecimalFormat* number_format = NULL;
252 icu::UnicodeString style;
253 icu::UnicodeString currency;
254 if (ExtractStringSetting(isolate, options, "style", &style)) {
255 if (style == UNICODE_STRING_SIMPLE("currency")) {
256 icu::UnicodeString display;
257 ExtractStringSetting(isolate, options, "currency", ¤cy);
258 ExtractStringSetting(isolate, options, "currencyDisplay", &display);
259
260 #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
261 icu::NumberFormat::EStyles format_style;
262 if (display == UNICODE_STRING_SIMPLE("code")) {
263 format_style = icu::NumberFormat::kIsoCurrencyStyle;
264 } else if (display == UNICODE_STRING_SIMPLE("name")) {
265 format_style = icu::NumberFormat::kPluralCurrencyStyle;
266 } else {
267 format_style = icu::NumberFormat::kCurrencyStyle;
268 }
269 #else // ICU version is 4.8 or above (we ignore versions below 4.0).
270 UNumberFormatStyle format_style;
271 if (display == UNICODE_STRING_SIMPLE("code")) {
272 format_style = UNUM_CURRENCY_ISO;
273 } else if (display == UNICODE_STRING_SIMPLE("name")) {
274 format_style = UNUM_CURRENCY_PLURAL;
275 } else {
276 format_style = UNUM_CURRENCY;
277 }
278 #endif
279
280 number_format = static_cast<icu::DecimalFormat*>(
281 icu::NumberFormat::createInstance(icu_locale, format_style, status));
282
283 if (U_FAILURE(status)) {
284 delete number_format;
285 return NULL;
286 }
287
288 UErrorCode status_digits = U_ZERO_ERROR;
289 uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
290 currency.getTerminatedBuffer(), &status_digits);
291 if (U_SUCCESS(status_digits)) {
292 number_format->setMinimumFractionDigits(fraction_digits);
293 number_format->setMaximumFractionDigits(fraction_digits);
294 } else {
295 // Set min & max to default values (previously in i18n.js)
296 number_format->setMinimumFractionDigits(0);
297 number_format->setMaximumFractionDigits(3);
298 }
299 } else if (style == UNICODE_STRING_SIMPLE("percent")) {
300 number_format = static_cast<icu::DecimalFormat*>(
301 icu::NumberFormat::createPercentInstance(icu_locale, status));
302 if (U_FAILURE(status)) {
303 delete number_format;
304 return NULL;
305 }
306 // Make sure 1.1% doesn't go into 2%.
307 number_format->setMinimumFractionDigits(1);
308 } else {
309 // Make a decimal instance by default.
310 number_format = static_cast<icu::DecimalFormat*>(
311 icu::NumberFormat::createInstance(icu_locale, status));
312 }
313 }
314
315 if (U_FAILURE(status)) {
316 delete number_format;
317 return NULL;
318 }
319
320 // Set all options.
321 if (!currency.isEmpty()) {
322 number_format->setCurrency(currency.getBuffer(), status);
323 }
324
325 int32_t digits;
326 if (ExtractIntegerSetting(
327 isolate, options, "minimumIntegerDigits", &digits)) {
328 number_format->setMinimumIntegerDigits(digits);
329 }
330
331 if (ExtractIntegerSetting(
332 isolate, options, "minimumFractionDigits", &digits)) {
333 number_format->setMinimumFractionDigits(digits);
334 }
335
336 if (ExtractIntegerSetting(
337 isolate, options, "maximumFractionDigits", &digits)) {
338 number_format->setMaximumFractionDigits(digits);
339 }
340
341 bool significant_digits_used = false;
342 if (ExtractIntegerSetting(
343 isolate, options, "minimumSignificantDigits", &digits)) {
344 number_format->setMinimumSignificantDigits(digits);
345 significant_digits_used = true;
346 }
347
348 if (ExtractIntegerSetting(
349 isolate, options, "maximumSignificantDigits", &digits)) {
350 number_format->setMaximumSignificantDigits(digits);
351 significant_digits_used = true;
352 }
353
354 number_format->setSignificantDigitsUsed(significant_digits_used);
355
356 bool grouping;
357 if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
358 number_format->setGroupingUsed(grouping);
359 }
360
361 // Set rounding mode.
362 number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
363
364 return number_format;
365 }
366
367
SetResolvedNumberSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::DecimalFormat * number_format,Handle<JSObject> resolved)368 void SetResolvedNumberSettings(Isolate* isolate,
369 const icu::Locale& icu_locale,
370 icu::DecimalFormat* number_format,
371 Handle<JSObject> resolved) {
372 Factory* factory = isolate->factory();
373 icu::UnicodeString pattern;
374 number_format->toPattern(pattern);
375 JSObject::SetProperty(
376 resolved, factory->intl_pattern_symbol(),
377 factory->NewStringFromTwoByte(
378 Vector<const uint16_t>(
379 reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
380 pattern.length())).ToHandleChecked(),
381 SLOPPY).Assert();
382
383 // Set resolved currency code in options.currency if not empty.
384 icu::UnicodeString currency(number_format->getCurrency());
385 if (!currency.isEmpty()) {
386 JSObject::SetProperty(
387 resolved, factory->NewStringFromStaticChars("currency"),
388 factory->NewStringFromTwoByte(Vector<const uint16_t>(
389 reinterpret_cast<const uint16_t*>(
390 currency.getBuffer()),
391 currency.length())).ToHandleChecked(),
392 SLOPPY).Assert();
393 }
394
395 // Ugly hack. ICU doesn't expose numbering system in any way, so we have
396 // to assume that for given locale NumberingSystem constructor produces the
397 // same digits as NumberFormat/Calendar would.
398 UErrorCode status = U_ZERO_ERROR;
399 icu::NumberingSystem* numbering_system =
400 icu::NumberingSystem::createInstance(icu_locale, status);
401 if (U_SUCCESS(status)) {
402 const char* ns = numbering_system->getName();
403 JSObject::SetProperty(
404 resolved, factory->NewStringFromStaticChars("numberingSystem"),
405 factory->NewStringFromAsciiChecked(ns), SLOPPY).Assert();
406 } else {
407 JSObject::SetProperty(resolved,
408 factory->NewStringFromStaticChars("numberingSystem"),
409 factory->undefined_value(), SLOPPY).Assert();
410 }
411 delete numbering_system;
412
413 JSObject::SetProperty(
414 resolved, factory->NewStringFromStaticChars("useGrouping"),
415 factory->ToBoolean(number_format->isGroupingUsed()), SLOPPY).Assert();
416
417 JSObject::SetProperty(
418 resolved, factory->NewStringFromStaticChars("minimumIntegerDigits"),
419 factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
420 SLOPPY).Assert();
421
422 JSObject::SetProperty(
423 resolved, factory->NewStringFromStaticChars("minimumFractionDigits"),
424 factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
425 SLOPPY).Assert();
426
427 JSObject::SetProperty(
428 resolved, factory->NewStringFromStaticChars("maximumFractionDigits"),
429 factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
430 SLOPPY).Assert();
431
432 Handle<String> key =
433 factory->NewStringFromStaticChars("minimumSignificantDigits");
434 Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key);
435 CHECK(maybe.IsJust());
436 if (maybe.FromJust()) {
437 JSObject::SetProperty(
438 resolved, factory->NewStringFromStaticChars("minimumSignificantDigits"),
439 factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()),
440 SLOPPY).Assert();
441 }
442
443 key = factory->NewStringFromStaticChars("maximumSignificantDigits");
444 maybe = JSReceiver::HasOwnProperty(resolved, key);
445 CHECK(maybe.IsJust());
446 if (maybe.FromJust()) {
447 JSObject::SetProperty(
448 resolved, factory->NewStringFromStaticChars("maximumSignificantDigits"),
449 factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()),
450 SLOPPY).Assert();
451 }
452
453 // Set the locale
454 char result[ULOC_FULLNAME_CAPACITY];
455 status = U_ZERO_ERROR;
456 uloc_toLanguageTag(
457 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
458 if (U_SUCCESS(status)) {
459 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
460 factory->NewStringFromAsciiChecked(result),
461 SLOPPY).Assert();
462 } else {
463 // This would never happen, since we got the locale from ICU.
464 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
465 factory->NewStringFromStaticChars("und"),
466 SLOPPY).Assert();
467 }
468 }
469
470
CreateICUCollator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)471 icu::Collator* CreateICUCollator(
472 Isolate* isolate,
473 const icu::Locale& icu_locale,
474 Handle<JSObject> options) {
475 // Make collator from options.
476 icu::Collator* collator = NULL;
477 UErrorCode status = U_ZERO_ERROR;
478 collator = icu::Collator::createInstance(icu_locale, status);
479
480 if (U_FAILURE(status)) {
481 delete collator;
482 return NULL;
483 }
484
485 // Set flags first, and then override them with sensitivity if necessary.
486 bool numeric;
487 if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) {
488 collator->setAttribute(
489 UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status);
490 }
491
492 // Normalization is always on, by the spec. We are free to optimize
493 // if the strings are already normalized (but we don't have a way to tell
494 // that right now).
495 collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
496
497 icu::UnicodeString case_first;
498 if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) {
499 if (case_first == UNICODE_STRING_SIMPLE("upper")) {
500 collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
501 } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
502 collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
503 } else {
504 // Default (false/off).
505 collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
506 }
507 }
508
509 icu::UnicodeString sensitivity;
510 if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) {
511 if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
512 collator->setStrength(icu::Collator::PRIMARY);
513 } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
514 collator->setStrength(icu::Collator::SECONDARY);
515 } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
516 collator->setStrength(icu::Collator::PRIMARY);
517 collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
518 } else {
519 // variant (default)
520 collator->setStrength(icu::Collator::TERTIARY);
521 }
522 }
523
524 bool ignore;
525 if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) {
526 if (ignore) {
527 collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
528 }
529 }
530
531 return collator;
532 }
533
534
SetResolvedCollatorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::Collator * collator,Handle<JSObject> resolved)535 void SetResolvedCollatorSettings(Isolate* isolate,
536 const icu::Locale& icu_locale,
537 icu::Collator* collator,
538 Handle<JSObject> resolved) {
539 Factory* factory = isolate->factory();
540 UErrorCode status = U_ZERO_ERROR;
541
542 JSObject::SetProperty(
543 resolved, factory->NewStringFromStaticChars("numeric"),
544 factory->ToBoolean(
545 collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON),
546 SLOPPY).Assert();
547
548 switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
549 case UCOL_LOWER_FIRST:
550 JSObject::SetProperty(
551 resolved, factory->NewStringFromStaticChars("caseFirst"),
552 factory->NewStringFromStaticChars("lower"), SLOPPY).Assert();
553 break;
554 case UCOL_UPPER_FIRST:
555 JSObject::SetProperty(
556 resolved, factory->NewStringFromStaticChars("caseFirst"),
557 factory->NewStringFromStaticChars("upper"), SLOPPY).Assert();
558 break;
559 default:
560 JSObject::SetProperty(
561 resolved, factory->NewStringFromStaticChars("caseFirst"),
562 factory->NewStringFromStaticChars("false"), SLOPPY).Assert();
563 }
564
565 switch (collator->getAttribute(UCOL_STRENGTH, status)) {
566 case UCOL_PRIMARY: {
567 JSObject::SetProperty(
568 resolved, factory->NewStringFromStaticChars("strength"),
569 factory->NewStringFromStaticChars("primary"), SLOPPY).Assert();
570
571 // case level: true + s1 -> case, s1 -> base.
572 if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
573 JSObject::SetProperty(
574 resolved, factory->NewStringFromStaticChars("sensitivity"),
575 factory->NewStringFromStaticChars("case"), SLOPPY).Assert();
576 } else {
577 JSObject::SetProperty(
578 resolved, factory->NewStringFromStaticChars("sensitivity"),
579 factory->NewStringFromStaticChars("base"), SLOPPY).Assert();
580 }
581 break;
582 }
583 case UCOL_SECONDARY:
584 JSObject::SetProperty(
585 resolved, factory->NewStringFromStaticChars("strength"),
586 factory->NewStringFromStaticChars("secondary"), SLOPPY).Assert();
587 JSObject::SetProperty(
588 resolved, factory->NewStringFromStaticChars("sensitivity"),
589 factory->NewStringFromStaticChars("accent"), SLOPPY).Assert();
590 break;
591 case UCOL_TERTIARY:
592 JSObject::SetProperty(
593 resolved, factory->NewStringFromStaticChars("strength"),
594 factory->NewStringFromStaticChars("tertiary"), SLOPPY).Assert();
595 JSObject::SetProperty(
596 resolved, factory->NewStringFromStaticChars("sensitivity"),
597 factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
598 break;
599 case UCOL_QUATERNARY:
600 // We shouldn't get quaternary and identical from ICU, but if we do
601 // put them into variant.
602 JSObject::SetProperty(
603 resolved, factory->NewStringFromStaticChars("strength"),
604 factory->NewStringFromStaticChars("quaternary"), SLOPPY).Assert();
605 JSObject::SetProperty(
606 resolved, factory->NewStringFromStaticChars("sensitivity"),
607 factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
608 break;
609 default:
610 JSObject::SetProperty(
611 resolved, factory->NewStringFromStaticChars("strength"),
612 factory->NewStringFromStaticChars("identical"), SLOPPY).Assert();
613 JSObject::SetProperty(
614 resolved, factory->NewStringFromStaticChars("sensitivity"),
615 factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
616 }
617
618 JSObject::SetProperty(
619 resolved, factory->NewStringFromStaticChars("ignorePunctuation"),
620 factory->ToBoolean(collator->getAttribute(UCOL_ALTERNATE_HANDLING,
621 status) == UCOL_SHIFTED),
622 SLOPPY).Assert();
623
624 // Set the locale
625 char result[ULOC_FULLNAME_CAPACITY];
626 status = U_ZERO_ERROR;
627 uloc_toLanguageTag(
628 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
629 if (U_SUCCESS(status)) {
630 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
631 factory->NewStringFromAsciiChecked(result),
632 SLOPPY).Assert();
633 } else {
634 // This would never happen, since we got the locale from ICU.
635 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
636 factory->NewStringFromStaticChars("und"),
637 SLOPPY).Assert();
638 }
639 }
640
641
CreateICUBreakIterator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)642 icu::BreakIterator* CreateICUBreakIterator(
643 Isolate* isolate,
644 const icu::Locale& icu_locale,
645 Handle<JSObject> options) {
646 UErrorCode status = U_ZERO_ERROR;
647 icu::BreakIterator* break_iterator = NULL;
648 icu::UnicodeString type;
649 if (!ExtractStringSetting(isolate, options, "type", &type)) return NULL;
650
651 if (type == UNICODE_STRING_SIMPLE("character")) {
652 break_iterator =
653 icu::BreakIterator::createCharacterInstance(icu_locale, status);
654 } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
655 break_iterator =
656 icu::BreakIterator::createSentenceInstance(icu_locale, status);
657 } else if (type == UNICODE_STRING_SIMPLE("line")) {
658 break_iterator =
659 icu::BreakIterator::createLineInstance(icu_locale, status);
660 } else {
661 // Defualt is word iterator.
662 break_iterator =
663 icu::BreakIterator::createWordInstance(icu_locale, status);
664 }
665
666 if (U_FAILURE(status)) {
667 delete break_iterator;
668 return NULL;
669 }
670
671 isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator);
672
673 return break_iterator;
674 }
675
676
SetResolvedBreakIteratorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::BreakIterator * break_iterator,Handle<JSObject> resolved)677 void SetResolvedBreakIteratorSettings(Isolate* isolate,
678 const icu::Locale& icu_locale,
679 icu::BreakIterator* break_iterator,
680 Handle<JSObject> resolved) {
681 Factory* factory = isolate->factory();
682 UErrorCode status = U_ZERO_ERROR;
683
684 // Set the locale
685 char result[ULOC_FULLNAME_CAPACITY];
686 status = U_ZERO_ERROR;
687 uloc_toLanguageTag(
688 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
689 if (U_SUCCESS(status)) {
690 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
691 factory->NewStringFromAsciiChecked(result),
692 SLOPPY).Assert();
693 } else {
694 // This would never happen, since we got the locale from ICU.
695 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
696 factory->NewStringFromStaticChars("und"),
697 SLOPPY).Assert();
698 }
699 }
700
701 } // namespace
702
703
704 // static
GetTemplate(Isolate * isolate)705 Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
706 return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
707 }
708
709
710 // static
GetTemplate2(Isolate * isolate)711 Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
712 return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
713 }
714
715
716 // static
InitializeDateTimeFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)717 icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
718 Isolate* isolate,
719 Handle<String> locale,
720 Handle<JSObject> options,
721 Handle<JSObject> resolved) {
722 // Convert BCP47 into ICU locale format.
723 UErrorCode status = U_ZERO_ERROR;
724 icu::Locale icu_locale;
725 char icu_result[ULOC_FULLNAME_CAPACITY];
726 int icu_length = 0;
727 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
728 if (bcp47_locale.length() != 0) {
729 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
730 &icu_length, &status);
731 if (U_FAILURE(status) || icu_length == 0) {
732 return NULL;
733 }
734 icu_locale = icu::Locale(icu_result);
735 }
736
737 icu::SimpleDateFormat* date_format = CreateICUDateFormat(
738 isolate, icu_locale, options);
739 if (!date_format) {
740 // Remove extensions and try again.
741 icu::Locale no_extension_locale(icu_locale.getBaseName());
742 date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
743
744 if (!date_format) {
745 FATAL("Failed to create ICU date format, are ICU data files missing?");
746 }
747
748 // Set resolved settings (pattern, numbering system, calendar).
749 SetResolvedDateSettings(
750 isolate, no_extension_locale, date_format, resolved);
751 } else {
752 SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
753 }
754
755 return date_format;
756 }
757
758
UnpackDateFormat(Isolate * isolate,Handle<JSObject> obj)759 icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
760 Isolate* isolate,
761 Handle<JSObject> obj) {
762 Handle<String> key =
763 isolate->factory()->NewStringFromStaticChars("dateFormat");
764 Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
765 CHECK(maybe.IsJust());
766 if (maybe.FromJust()) {
767 return reinterpret_cast<icu::SimpleDateFormat*>(
768 obj->GetInternalField(0));
769 }
770
771 return NULL;
772 }
773
DeleteDateFormat(const v8::WeakCallbackInfo<void> & data)774 void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) {
775 delete reinterpret_cast<icu::SimpleDateFormat*>(data.GetInternalField(0));
776 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
777 }
778
779
InitializeNumberFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)780 icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
781 Isolate* isolate,
782 Handle<String> locale,
783 Handle<JSObject> options,
784 Handle<JSObject> resolved) {
785 // Convert BCP47 into ICU locale format.
786 UErrorCode status = U_ZERO_ERROR;
787 icu::Locale icu_locale;
788 char icu_result[ULOC_FULLNAME_CAPACITY];
789 int icu_length = 0;
790 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
791 if (bcp47_locale.length() != 0) {
792 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
793 &icu_length, &status);
794 if (U_FAILURE(status) || icu_length == 0) {
795 return NULL;
796 }
797 icu_locale = icu::Locale(icu_result);
798 }
799
800 icu::DecimalFormat* number_format =
801 CreateICUNumberFormat(isolate, icu_locale, options);
802 if (!number_format) {
803 // Remove extensions and try again.
804 icu::Locale no_extension_locale(icu_locale.getBaseName());
805 number_format = CreateICUNumberFormat(
806 isolate, no_extension_locale, options);
807
808 if (!number_format) {
809 FATAL("Failed to create ICU number format, are ICU data files missing?");
810 }
811
812 // Set resolved settings (pattern, numbering system).
813 SetResolvedNumberSettings(
814 isolate, no_extension_locale, number_format, resolved);
815 } else {
816 SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
817 }
818
819 return number_format;
820 }
821
822
UnpackNumberFormat(Isolate * isolate,Handle<JSObject> obj)823 icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
824 Isolate* isolate,
825 Handle<JSObject> obj) {
826 Handle<String> key =
827 isolate->factory()->NewStringFromStaticChars("numberFormat");
828 Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
829 CHECK(maybe.IsJust());
830 if (maybe.FromJust()) {
831 return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0));
832 }
833
834 return NULL;
835 }
836
DeleteNumberFormat(const v8::WeakCallbackInfo<void> & data)837 void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) {
838 delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(0));
839 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
840 }
841
842
InitializeCollator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)843 icu::Collator* Collator::InitializeCollator(
844 Isolate* isolate,
845 Handle<String> locale,
846 Handle<JSObject> options,
847 Handle<JSObject> resolved) {
848 // Convert BCP47 into ICU locale format.
849 UErrorCode status = U_ZERO_ERROR;
850 icu::Locale icu_locale;
851 char icu_result[ULOC_FULLNAME_CAPACITY];
852 int icu_length = 0;
853 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
854 if (bcp47_locale.length() != 0) {
855 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
856 &icu_length, &status);
857 if (U_FAILURE(status) || icu_length == 0) {
858 return NULL;
859 }
860 icu_locale = icu::Locale(icu_result);
861 }
862
863 icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
864 if (!collator) {
865 // Remove extensions and try again.
866 icu::Locale no_extension_locale(icu_locale.getBaseName());
867 collator = CreateICUCollator(isolate, no_extension_locale, options);
868
869 if (!collator) {
870 FATAL("Failed to create ICU collator, are ICU data files missing?");
871 }
872
873 // Set resolved settings (pattern, numbering system).
874 SetResolvedCollatorSettings(
875 isolate, no_extension_locale, collator, resolved);
876 } else {
877 SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved);
878 }
879
880 return collator;
881 }
882
883
UnpackCollator(Isolate * isolate,Handle<JSObject> obj)884 icu::Collator* Collator::UnpackCollator(Isolate* isolate,
885 Handle<JSObject> obj) {
886 Handle<String> key = isolate->factory()->NewStringFromStaticChars("collator");
887 Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
888 CHECK(maybe.IsJust());
889 if (maybe.FromJust()) {
890 return reinterpret_cast<icu::Collator*>(obj->GetInternalField(0));
891 }
892
893 return NULL;
894 }
895
DeleteCollator(const v8::WeakCallbackInfo<void> & data)896 void Collator::DeleteCollator(const v8::WeakCallbackInfo<void>& data) {
897 delete reinterpret_cast<icu::Collator*>(data.GetInternalField(0));
898 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
899 }
900
901
InitializeBreakIterator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)902 icu::BreakIterator* BreakIterator::InitializeBreakIterator(
903 Isolate* isolate,
904 Handle<String> locale,
905 Handle<JSObject> options,
906 Handle<JSObject> resolved) {
907 // Convert BCP47 into ICU locale format.
908 UErrorCode status = U_ZERO_ERROR;
909 icu::Locale icu_locale;
910 char icu_result[ULOC_FULLNAME_CAPACITY];
911 int icu_length = 0;
912 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
913 if (bcp47_locale.length() != 0) {
914 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
915 &icu_length, &status);
916 if (U_FAILURE(status) || icu_length == 0) {
917 return NULL;
918 }
919 icu_locale = icu::Locale(icu_result);
920 }
921
922 icu::BreakIterator* break_iterator = CreateICUBreakIterator(
923 isolate, icu_locale, options);
924 if (!break_iterator) {
925 // Remove extensions and try again.
926 icu::Locale no_extension_locale(icu_locale.getBaseName());
927 break_iterator = CreateICUBreakIterator(
928 isolate, no_extension_locale, options);
929
930 if (!break_iterator) {
931 FATAL("Failed to create ICU break iterator, are ICU data files missing?");
932 }
933
934 // Set resolved settings (locale).
935 SetResolvedBreakIteratorSettings(
936 isolate, no_extension_locale, break_iterator, resolved);
937 } else {
938 SetResolvedBreakIteratorSettings(
939 isolate, icu_locale, break_iterator, resolved);
940 }
941
942 return break_iterator;
943 }
944
945
UnpackBreakIterator(Isolate * isolate,Handle<JSObject> obj)946 icu::BreakIterator* BreakIterator::UnpackBreakIterator(Isolate* isolate,
947 Handle<JSObject> obj) {
948 Handle<String> key =
949 isolate->factory()->NewStringFromStaticChars("breakIterator");
950 Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
951 CHECK(maybe.IsJust());
952 if (maybe.FromJust()) {
953 return reinterpret_cast<icu::BreakIterator*>(obj->GetInternalField(0));
954 }
955
956 return NULL;
957 }
958
DeleteBreakIterator(const v8::WeakCallbackInfo<void> & data)959 void BreakIterator::DeleteBreakIterator(
960 const v8::WeakCallbackInfo<void>& data) {
961 delete reinterpret_cast<icu::BreakIterator*>(data.GetInternalField(0));
962 delete reinterpret_cast<icu::UnicodeString*>(data.GetInternalField(1));
963 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
964 }
965
966 } // namespace internal
967 } // namespace v8
968