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 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 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 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 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 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> 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 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 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 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 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 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 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 699 Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) { 700 return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate); 701 } 702 703 704 // static 705 Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) { 706 return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate); 707 } 708 709 710 // static 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 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> 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 777 static void DestroyGlobalHandle( 778 const v8::WeakCallbackData<v8::Value, void>& data) { 779 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); 780 } 781 782 783 void DateFormat::DeleteDateFormat( 784 const v8::WeakCallbackData<v8::Value, void>& data) { 785 DeleteNativeObjectAt<icu::SimpleDateFormat>(data, 0); 786 DestroyGlobalHandle(data); 787 } 788 789 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 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 848 void NumberFormat::DeleteNumberFormat( 849 const v8::WeakCallbackData<v8::Value, void>& data) { 850 DeleteNativeObjectAt<icu::DecimalFormat>(data, 0); 851 DestroyGlobalHandle(data); 852 } 853 854 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 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 909 void Collator::DeleteCollator( 910 const v8::WeakCallbackData<v8::Value, void>& data) { 911 DeleteNativeObjectAt<icu::Collator>(data, 0); 912 DestroyGlobalHandle(data); 913 } 914 915 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 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 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