1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 // limitations under the License. 5 6 #include "src/i18n.h" 7 8 #include <memory> 9 10 #include "src/api.h" 11 #include "src/factory.h" 12 #include "src/isolate.h" 13 #include "src/objects-inl.h" 14 #include "unicode/brkiter.h" 15 #include "unicode/calendar.h" 16 #include "unicode/coll.h" 17 #include "unicode/curramt.h" 18 #include "unicode/dcfmtsym.h" 19 #include "unicode/decimfmt.h" 20 #include "unicode/dtfmtsym.h" 21 #include "unicode/dtptngen.h" 22 #include "unicode/gregocal.h" 23 #include "unicode/locid.h" 24 #include "unicode/numfmt.h" 25 #include "unicode/numsys.h" 26 #include "unicode/rbbi.h" 27 #include "unicode/smpdtfmt.h" 28 #include "unicode/timezone.h" 29 #include "unicode/uchar.h" 30 #include "unicode/ucol.h" 31 #include "unicode/ucurr.h" 32 #include "unicode/unum.h" 33 #include "unicode/uvernum.h" 34 #include "unicode/uversion.h" 35 36 #if U_ICU_VERSION_MAJOR_NUM >= 59 37 #include "unicode/char16ptr.h" 38 #endif 39 40 namespace v8 { 41 namespace internal { 42 43 namespace { 44 45 bool ExtractStringSetting(Isolate* isolate, 46 Handle<JSObject> options, 47 const char* key, 48 icu::UnicodeString* setting) { 49 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); 50 Handle<Object> object = 51 JSReceiver::GetProperty(options, str).ToHandleChecked(); 52 if (object->IsString()) { 53 v8::String::Utf8Value utf8_string( 54 v8::Utils::ToLocal(Handle<String>::cast(object))); 55 *setting = icu::UnicodeString::fromUTF8(*utf8_string); 56 return true; 57 } 58 return false; 59 } 60 61 62 bool ExtractIntegerSetting(Isolate* isolate, 63 Handle<JSObject> options, 64 const char* key, 65 int32_t* value) { 66 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); 67 Handle<Object> object = 68 JSReceiver::GetProperty(options, str).ToHandleChecked(); 69 if (object->IsNumber()) { 70 object->ToInt32(value); 71 return true; 72 } 73 return false; 74 } 75 76 77 bool ExtractBooleanSetting(Isolate* isolate, 78 Handle<JSObject> options, 79 const char* key, 80 bool* value) { 81 Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); 82 Handle<Object> object = 83 JSReceiver::GetProperty(options, str).ToHandleChecked(); 84 if (object->IsBoolean()) { 85 *value = object->BooleanValue(); 86 return true; 87 } 88 return false; 89 } 90 91 92 icu::SimpleDateFormat* CreateICUDateFormat( 93 Isolate* isolate, 94 const icu::Locale& icu_locale, 95 Handle<JSObject> options) { 96 // Create time zone as specified by the user. We have to re-create time zone 97 // since calendar takes ownership. 98 icu::TimeZone* tz = NULL; 99 icu::UnicodeString timezone; 100 if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) { 101 tz = icu::TimeZone::createTimeZone(timezone); 102 } else { 103 tz = icu::TimeZone::createDefault(); 104 } 105 106 // Create a calendar using locale, and apply time zone to it. 107 UErrorCode status = U_ZERO_ERROR; 108 icu::Calendar* calendar = 109 icu::Calendar::createInstance(tz, icu_locale, status); 110 111 if (calendar->getDynamicClassID() == 112 icu::GregorianCalendar::getStaticClassID()) { 113 icu::GregorianCalendar* gc = (icu::GregorianCalendar*)calendar; 114 UErrorCode status = U_ZERO_ERROR; 115 // The beginning of ECMAScript time, namely -(2**53) 116 const double start_of_time = -9007199254740992; 117 gc->setGregorianChange(start_of_time, status); 118 DCHECK(U_SUCCESS(status)); 119 } 120 121 // Make formatter from skeleton. Calendar and numbering system are added 122 // to the locale as Unicode extension (if they were specified at all). 123 icu::SimpleDateFormat* date_format = NULL; 124 icu::UnicodeString skeleton; 125 if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) { 126 std::unique_ptr<icu::DateTimePatternGenerator> generator( 127 icu::DateTimePatternGenerator::createInstance(icu_locale, status)); 128 icu::UnicodeString pattern; 129 if (U_SUCCESS(status)) 130 pattern = generator->getBestPattern(skeleton, status); 131 132 date_format = new icu::SimpleDateFormat(pattern, icu_locale, status); 133 if (U_SUCCESS(status)) { 134 date_format->adoptCalendar(calendar); 135 } 136 } 137 138 if (U_FAILURE(status)) { 139 delete calendar; 140 delete date_format; 141 date_format = nullptr; 142 } 143 144 return date_format; 145 } 146 147 148 void SetResolvedDateSettings(Isolate* isolate, 149 const icu::Locale& icu_locale, 150 icu::SimpleDateFormat* date_format, 151 Handle<JSObject> resolved) { 152 Factory* factory = isolate->factory(); 153 UErrorCode status = U_ZERO_ERROR; 154 icu::UnicodeString pattern; 155 date_format->toPattern(pattern); 156 JSObject::SetProperty( 157 resolved, factory->intl_pattern_symbol(), 158 factory->NewStringFromTwoByte( 159 Vector<const uint16_t>( 160 reinterpret_cast<const uint16_t*>(pattern.getBuffer()), 161 pattern.length())).ToHandleChecked(), 162 SLOPPY).Assert(); 163 164 // Set time zone and calendar. 165 const icu::Calendar* calendar = date_format->getCalendar(); 166 // getType() returns legacy calendar type name instead of LDML/BCP47 calendar 167 // key values. i18n.js maps them to BCP47 values for key "ca". 168 // TODO(jshin): Consider doing it here, instead. 169 const char* calendar_name = calendar->getType(); 170 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("calendar"), 171 factory->NewStringFromAsciiChecked(calendar_name), 172 SLOPPY).Assert(); 173 174 const icu::TimeZone& tz = calendar->getTimeZone(); 175 icu::UnicodeString time_zone; 176 tz.getID(time_zone); 177 178 icu::UnicodeString canonical_time_zone; 179 icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status); 180 if (U_SUCCESS(status)) { 181 if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) { 182 JSObject::SetProperty( 183 resolved, factory->NewStringFromStaticChars("timeZone"), 184 factory->NewStringFromStaticChars("UTC"), SLOPPY).Assert(); 185 } else { 186 JSObject::SetProperty( 187 resolved, factory->NewStringFromStaticChars("timeZone"), 188 factory->NewStringFromTwoByte( 189 Vector<const uint16_t>( 190 reinterpret_cast<const uint16_t*>( 191 canonical_time_zone.getBuffer()), 192 canonical_time_zone.length())).ToHandleChecked(), 193 SLOPPY).Assert(); 194 } 195 } 196 197 // Ugly hack. ICU doesn't expose numbering system in any way, so we have 198 // to assume that for given locale NumberingSystem constructor produces the 199 // same digits as NumberFormat/Calendar would. 200 status = U_ZERO_ERROR; 201 icu::NumberingSystem* numbering_system = 202 icu::NumberingSystem::createInstance(icu_locale, status); 203 if (U_SUCCESS(status)) { 204 const char* ns = numbering_system->getName(); 205 JSObject::SetProperty( 206 resolved, factory->NewStringFromStaticChars("numberingSystem"), 207 factory->NewStringFromAsciiChecked(ns), SLOPPY).Assert(); 208 } else { 209 JSObject::SetProperty(resolved, 210 factory->NewStringFromStaticChars("numberingSystem"), 211 factory->undefined_value(), SLOPPY).Assert(); 212 } 213 delete numbering_system; 214 215 // Set the locale 216 char result[ULOC_FULLNAME_CAPACITY]; 217 status = U_ZERO_ERROR; 218 uloc_toLanguageTag( 219 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); 220 if (U_SUCCESS(status)) { 221 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), 222 factory->NewStringFromAsciiChecked(result), 223 SLOPPY).Assert(); 224 } else { 225 // This would never happen, since we got the locale from ICU. 226 JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"), 227 factory->NewStringFromStaticChars("und"), 228 SLOPPY).Assert(); 229 } 230 } 231 232 233 icu::DecimalFormat* CreateICUNumberFormat( 234 Isolate* isolate, 235 const icu::Locale& icu_locale, 236 Handle<JSObject> options) { 237 // Make formatter from options. Numbering system is added 238 // to the locale as Unicode extension (if it was specified at all). 239 UErrorCode status = U_ZERO_ERROR; 240 icu::DecimalFormat* number_format = NULL; 241 icu::UnicodeString style; 242 icu::UnicodeString currency; 243 if (ExtractStringSetting(isolate, options, "style", &style)) { 244 if (style == UNICODE_STRING_SIMPLE("currency")) { 245 icu::UnicodeString display; 246 ExtractStringSetting(isolate, options, "currency", ¤cy); 247 ExtractStringSetting(isolate, options, "currencyDisplay", &display); 248 249 #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) 250 icu::NumberFormat::EStyles format_style; 251 if (display == UNICODE_STRING_SIMPLE("code")) { 252 format_style = icu::NumberFormat::kIsoCurrencyStyle; 253 } else if (display == UNICODE_STRING_SIMPLE("name")) { 254 format_style = icu::NumberFormat::kPluralCurrencyStyle; 255 } else { 256 format_style = icu::NumberFormat::kCurrencyStyle; 257 } 258 #else // ICU version is 4.8 or above (we ignore versions below 4.0). 259 UNumberFormatStyle format_style; 260 if (display == UNICODE_STRING_SIMPLE("code")) { 261 format_style = UNUM_CURRENCY_ISO; 262 } else if (display == UNICODE_STRING_SIMPLE("name")) { 263 format_style = UNUM_CURRENCY_PLURAL; 264 } else { 265 format_style = UNUM_CURRENCY; 266 } 267 #endif 268 269 number_format = static_cast<icu::DecimalFormat*>( 270 icu::NumberFormat::createInstance(icu_locale, format_style, status)); 271 272 if (U_FAILURE(status)) { 273 delete number_format; 274 return NULL; 275 } 276 277 UErrorCode status_digits = U_ZERO_ERROR; 278 #if U_ICU_VERSION_MAJOR_NUM >= 59 279 uint32_t fraction_digits = ucurr_getDefaultFractionDigits( 280 icu::toUCharPtr(currency.getTerminatedBuffer()), &status_digits); 281 #else 282 uint32_t fraction_digits = ucurr_getDefaultFractionDigits( 283 currency.getTerminatedBuffer(), &status_digits); 284 #endif 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 icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat( 700 Isolate* isolate, 701 Handle<String> locale, 702 Handle<JSObject> options, 703 Handle<JSObject> resolved) { 704 // Convert BCP47 into ICU locale format. 705 UErrorCode status = U_ZERO_ERROR; 706 icu::Locale icu_locale; 707 char icu_result[ULOC_FULLNAME_CAPACITY]; 708 int icu_length = 0; 709 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); 710 if (bcp47_locale.length() != 0) { 711 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, 712 &icu_length, &status); 713 if (U_FAILURE(status) || icu_length == 0) { 714 return NULL; 715 } 716 icu_locale = icu::Locale(icu_result); 717 } 718 719 icu::SimpleDateFormat* date_format = CreateICUDateFormat( 720 isolate, icu_locale, options); 721 if (!date_format) { 722 // Remove extensions and try again. 723 icu::Locale no_extension_locale(icu_locale.getBaseName()); 724 date_format = CreateICUDateFormat(isolate, no_extension_locale, options); 725 726 if (!date_format) { 727 FATAL("Failed to create ICU date format, are ICU data files missing?"); 728 } 729 730 // Set resolved settings (pattern, numbering system, calendar). 731 SetResolvedDateSettings( 732 isolate, no_extension_locale, date_format, resolved); 733 } else { 734 SetResolvedDateSettings(isolate, icu_locale, date_format, resolved); 735 } 736 737 return date_format; 738 } 739 740 741 icu::SimpleDateFormat* DateFormat::UnpackDateFormat( 742 Isolate* isolate, 743 Handle<JSObject> obj) { 744 return reinterpret_cast<icu::SimpleDateFormat*>(obj->GetInternalField(0)); 745 } 746 747 void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) { 748 delete reinterpret_cast<icu::SimpleDateFormat*>(data.GetInternalField(0)); 749 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); 750 } 751 752 753 icu::DecimalFormat* NumberFormat::InitializeNumberFormat( 754 Isolate* isolate, 755 Handle<String> locale, 756 Handle<JSObject> options, 757 Handle<JSObject> resolved) { 758 // Convert BCP47 into ICU locale format. 759 UErrorCode status = U_ZERO_ERROR; 760 icu::Locale icu_locale; 761 char icu_result[ULOC_FULLNAME_CAPACITY]; 762 int icu_length = 0; 763 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); 764 if (bcp47_locale.length() != 0) { 765 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, 766 &icu_length, &status); 767 if (U_FAILURE(status) || icu_length == 0) { 768 return NULL; 769 } 770 icu_locale = icu::Locale(icu_result); 771 } 772 773 icu::DecimalFormat* number_format = 774 CreateICUNumberFormat(isolate, icu_locale, options); 775 if (!number_format) { 776 // Remove extensions and try again. 777 icu::Locale no_extension_locale(icu_locale.getBaseName()); 778 number_format = CreateICUNumberFormat( 779 isolate, no_extension_locale, options); 780 781 if (!number_format) { 782 FATAL("Failed to create ICU number format, are ICU data files missing?"); 783 } 784 785 // Set resolved settings (pattern, numbering system). 786 SetResolvedNumberSettings( 787 isolate, no_extension_locale, number_format, resolved); 788 } else { 789 SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved); 790 } 791 792 return number_format; 793 } 794 795 796 icu::DecimalFormat* NumberFormat::UnpackNumberFormat( 797 Isolate* isolate, 798 Handle<JSObject> obj) { 799 return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0)); 800 } 801 802 void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) { 803 delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(0)); 804 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); 805 } 806 807 808 icu::Collator* Collator::InitializeCollator( 809 Isolate* isolate, 810 Handle<String> locale, 811 Handle<JSObject> options, 812 Handle<JSObject> resolved) { 813 // Convert BCP47 into ICU locale format. 814 UErrorCode status = U_ZERO_ERROR; 815 icu::Locale icu_locale; 816 char icu_result[ULOC_FULLNAME_CAPACITY]; 817 int icu_length = 0; 818 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); 819 if (bcp47_locale.length() != 0) { 820 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, 821 &icu_length, &status); 822 if (U_FAILURE(status) || icu_length == 0) { 823 return NULL; 824 } 825 icu_locale = icu::Locale(icu_result); 826 } 827 828 icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options); 829 if (!collator) { 830 // Remove extensions and try again. 831 icu::Locale no_extension_locale(icu_locale.getBaseName()); 832 collator = CreateICUCollator(isolate, no_extension_locale, options); 833 834 if (!collator) { 835 FATAL("Failed to create ICU collator, are ICU data files missing?"); 836 } 837 838 // Set resolved settings (pattern, numbering system). 839 SetResolvedCollatorSettings( 840 isolate, no_extension_locale, collator, resolved); 841 } else { 842 SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved); 843 } 844 845 return collator; 846 } 847 848 849 icu::Collator* Collator::UnpackCollator(Isolate* isolate, 850 Handle<JSObject> obj) { 851 return reinterpret_cast<icu::Collator*>(obj->GetInternalField(0)); 852 } 853 854 void Collator::DeleteCollator(const v8::WeakCallbackInfo<void>& data) { 855 delete reinterpret_cast<icu::Collator*>(data.GetInternalField(0)); 856 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); 857 } 858 859 icu::BreakIterator* V8BreakIterator::InitializeBreakIterator( 860 Isolate* isolate, Handle<String> locale, Handle<JSObject> options, 861 Handle<JSObject> resolved) { 862 // Convert BCP47 into ICU locale format. 863 UErrorCode status = U_ZERO_ERROR; 864 icu::Locale icu_locale; 865 char icu_result[ULOC_FULLNAME_CAPACITY]; 866 int icu_length = 0; 867 v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); 868 if (bcp47_locale.length() != 0) { 869 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, 870 &icu_length, &status); 871 if (U_FAILURE(status) || icu_length == 0) { 872 return NULL; 873 } 874 icu_locale = icu::Locale(icu_result); 875 } 876 877 icu::BreakIterator* break_iterator = CreateICUBreakIterator( 878 isolate, icu_locale, options); 879 if (!break_iterator) { 880 // Remove extensions and try again. 881 icu::Locale no_extension_locale(icu_locale.getBaseName()); 882 break_iterator = CreateICUBreakIterator( 883 isolate, no_extension_locale, options); 884 885 if (!break_iterator) { 886 FATAL("Failed to create ICU break iterator, are ICU data files missing?"); 887 } 888 889 // Set resolved settings (locale). 890 SetResolvedBreakIteratorSettings( 891 isolate, no_extension_locale, break_iterator, resolved); 892 } else { 893 SetResolvedBreakIteratorSettings( 894 isolate, icu_locale, break_iterator, resolved); 895 } 896 897 return break_iterator; 898 } 899 900 icu::BreakIterator* V8BreakIterator::UnpackBreakIterator(Isolate* isolate, 901 Handle<JSObject> obj) { 902 return reinterpret_cast<icu::BreakIterator*>(obj->GetInternalField(0)); 903 } 904 905 void V8BreakIterator::DeleteBreakIterator( 906 const v8::WeakCallbackInfo<void>& data) { 907 delete reinterpret_cast<icu::BreakIterator*>(data.GetInternalField(0)); 908 delete reinterpret_cast<icu::UnicodeString*>(data.GetInternalField(1)); 909 GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); 910 } 911 912 } // namespace internal 913 } // namespace v8 914