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