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