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