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