1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ********************************************************************** 5 * Copyright (C) 1997-2016, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ********************************************************************** 8 * 9 * File locid.cpp 10 * 11 * Created by: Richard Gillam 12 * 13 * Modification History: 14 * 15 * Date Name Description 16 * 02/11/97 aliu Changed gLocPath to fgDataDirectory and added 17 * methods to get and set it. 18 * 04/02/97 aliu Made operator!= inline; fixed return value 19 * of getName(). 20 * 04/15/97 aliu Cleanup for AIX/Win32. 21 * 04/24/97 aliu Numerous changes per code review. 22 * 08/18/98 stephen Changed getDisplayName() 23 * Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE 24 * Added getISOCountries(), getISOLanguages(), 25 * getLanguagesForCountry() 26 * 03/16/99 bertrand rehaul. 27 * 07/21/99 stephen Added U_CFUNC setDefault 28 * 11/09/99 weiv Added const char * getName() const; 29 * 04/12/00 srl removing unicodestring api's and cached hash code 30 * 08/10/01 grhoten Change the static Locales to accessor functions 31 ****************************************************************************** 32 */ 33 34 #include <utility> 35 36 #include "unicode/bytestream.h" 37 #include "unicode/locid.h" 38 #include "unicode/strenum.h" 39 #include "unicode/stringpiece.h" 40 #include "unicode/uloc.h" 41 #include "putilimp.h" 42 #include "mutex.h" 43 #include "umutex.h" 44 #include "uassert.h" 45 #include "cmemory.h" 46 #include "cstring.h" 47 #include "uassert.h" 48 #include "uhash.h" 49 #include "ulocimp.h" 50 #include "ucln_cmn.h" 51 #include "ustr_imp.h" 52 #include "charstr.h" 53 #include "bytesinkutil.h" 54 55 U_CDECL_BEGIN 56 static UBool U_CALLCONV locale_cleanup(void); 57 U_CDECL_END 58 59 U_NAMESPACE_BEGIN 60 61 static Locale *gLocaleCache = NULL; 62 static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER; 63 64 // gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. 65 static UMutex gDefaultLocaleMutex = U_MUTEX_INITIALIZER; 66 static UHashtable *gDefaultLocalesHashT = NULL; 67 static Locale *gDefaultLocale = NULL; 68 69 /** 70 * \def ULOC_STRING_LIMIT 71 * strings beyond this value crash in CharString 72 */ 73 #define ULOC_STRING_LIMIT 357913941 74 75 U_NAMESPACE_END 76 77 typedef enum ELocalePos { 78 eENGLISH, 79 eFRENCH, 80 eGERMAN, 81 eITALIAN, 82 eJAPANESE, 83 eKOREAN, 84 eCHINESE, 85 86 eFRANCE, 87 eGERMANY, 88 eITALY, 89 eJAPAN, 90 eKOREA, 91 eCHINA, /* Alias for PRC */ 92 eTAIWAN, 93 eUK, 94 eUS, 95 eCANADA, 96 eCANADA_FRENCH, 97 eROOT, 98 99 100 //eDEFAULT, 101 eMAX_LOCALES 102 } ELocalePos; 103 104 U_CFUNC int32_t locale_getKeywords(const char *localeID, 105 char prev, 106 char *keywords, int32_t keywordCapacity, 107 char *values, int32_t valuesCapacity, int32_t *valLen, 108 UBool valuesToo, 109 UErrorCode *status); 110 111 U_CDECL_BEGIN 112 // 113 // Deleter function for Locales owned by the default Locale hash table/ 114 // 115 static void U_CALLCONV 116 deleteLocale(void *obj) { 117 delete (icu::Locale *) obj; 118 } 119 120 static UBool U_CALLCONV locale_cleanup(void) 121 { 122 U_NAMESPACE_USE 123 124 delete [] gLocaleCache; 125 gLocaleCache = NULL; 126 gLocaleCacheInitOnce.reset(); 127 128 if (gDefaultLocalesHashT) { 129 uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. 130 gDefaultLocalesHashT = NULL; 131 } 132 gDefaultLocale = NULL; 133 return TRUE; 134 } 135 136 137 static void U_CALLCONV locale_init(UErrorCode &status) { 138 U_NAMESPACE_USE 139 140 U_ASSERT(gLocaleCache == NULL); 141 gLocaleCache = new Locale[(int)eMAX_LOCALES]; 142 if (gLocaleCache == NULL) { 143 status = U_MEMORY_ALLOCATION_ERROR; 144 return; 145 } 146 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); 147 gLocaleCache[eROOT] = Locale(""); 148 gLocaleCache[eENGLISH] = Locale("en"); 149 gLocaleCache[eFRENCH] = Locale("fr"); 150 gLocaleCache[eGERMAN] = Locale("de"); 151 gLocaleCache[eITALIAN] = Locale("it"); 152 gLocaleCache[eJAPANESE] = Locale("ja"); 153 gLocaleCache[eKOREAN] = Locale("ko"); 154 gLocaleCache[eCHINESE] = Locale("zh"); 155 gLocaleCache[eFRANCE] = Locale("fr", "FR"); 156 gLocaleCache[eGERMANY] = Locale("de", "DE"); 157 gLocaleCache[eITALY] = Locale("it", "IT"); 158 gLocaleCache[eJAPAN] = Locale("ja", "JP"); 159 gLocaleCache[eKOREA] = Locale("ko", "KR"); 160 gLocaleCache[eCHINA] = Locale("zh", "CN"); 161 gLocaleCache[eTAIWAN] = Locale("zh", "TW"); 162 gLocaleCache[eUK] = Locale("en", "GB"); 163 gLocaleCache[eUS] = Locale("en", "US"); 164 gLocaleCache[eCANADA] = Locale("en", "CA"); 165 gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); 166 } 167 168 U_CDECL_END 169 170 U_NAMESPACE_BEGIN 171 172 Locale *locale_set_default_internal(const char *id, UErrorCode& status) { 173 // Synchronize this entire function. 174 Mutex lock(&gDefaultLocaleMutex); 175 176 UBool canonicalize = FALSE; 177 178 // If given a NULL string for the locale id, grab the default 179 // name from the system. 180 // (Different from most other locale APIs, where a null name means use 181 // the current ICU default locale.) 182 if (id == NULL) { 183 id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. 184 canonicalize = TRUE; // always canonicalize host ID 185 } 186 187 char localeNameBuf[512]; 188 189 if (canonicalize) { 190 uloc_canonicalize(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); 191 } else { 192 uloc_getName(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); 193 } 194 localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of 195 // a long name filling the buffer. 196 // (long names are truncated.) 197 // 198 if (U_FAILURE(status)) { 199 return gDefaultLocale; 200 } 201 202 if (gDefaultLocalesHashT == NULL) { 203 gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 204 if (U_FAILURE(status)) { 205 return gDefaultLocale; 206 } 207 uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); 208 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); 209 } 210 211 Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); 212 if (newDefault == NULL) { 213 newDefault = new Locale(Locale::eBOGUS); 214 if (newDefault == NULL) { 215 status = U_MEMORY_ALLOCATION_ERROR; 216 return gDefaultLocale; 217 } 218 newDefault->init(localeNameBuf, FALSE); 219 uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); 220 if (U_FAILURE(status)) { 221 return gDefaultLocale; 222 } 223 } 224 gDefaultLocale = newDefault; 225 return gDefaultLocale; 226 } 227 228 U_NAMESPACE_END 229 230 /* sfb 07/21/99 */ 231 U_CFUNC void 232 locale_set_default(const char *id) 233 { 234 U_NAMESPACE_USE 235 UErrorCode status = U_ZERO_ERROR; 236 locale_set_default_internal(id, status); 237 } 238 /* end */ 239 240 U_CFUNC const char * 241 locale_get_default(void) 242 { 243 U_NAMESPACE_USE 244 return Locale::getDefault().getName(); 245 } 246 247 248 U_NAMESPACE_BEGIN 249 250 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) 251 252 /*Character separating the posix id fields*/ 253 // '_' 254 // In the platform codepage. 255 #define SEP_CHAR '_' 256 257 Locale::~Locale() 258 { 259 if (baseName != fullName) { 260 uprv_free(baseName); 261 } 262 baseName = NULL; 263 /*if fullName is on the heap, we free it*/ 264 if (fullName != fullNameBuffer) 265 { 266 uprv_free(fullName); 267 fullName = NULL; 268 } 269 } 270 271 Locale::Locale() 272 : UObject(), fullName(fullNameBuffer), baseName(NULL) 273 { 274 init(NULL, FALSE); 275 } 276 277 /* 278 * Internal constructor to allow construction of a locale object with 279 * NO side effects. (Default constructor tries to get 280 * the default locale.) 281 */ 282 Locale::Locale(Locale::ELocaleType) 283 : UObject(), fullName(fullNameBuffer), baseName(NULL) 284 { 285 setToBogus(); 286 } 287 288 289 Locale::Locale( const char * newLanguage, 290 const char * newCountry, 291 const char * newVariant, 292 const char * newKeywords) 293 : UObject(), fullName(fullNameBuffer), baseName(NULL) 294 { 295 if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) ) 296 { 297 init(NULL, FALSE); /* shortcut */ 298 } 299 else 300 { 301 UErrorCode status = U_ZERO_ERROR; 302 int32_t size = 0; 303 int32_t lsize = 0; 304 int32_t csize = 0; 305 int32_t vsize = 0; 306 int32_t ksize = 0; 307 308 // Calculate the size of the resulting string. 309 310 // Language 311 if ( newLanguage != NULL ) 312 { 313 lsize = (int32_t)uprv_strlen(newLanguage); 314 if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap 315 setToBogus(); 316 return; 317 } 318 size = lsize; 319 } 320 321 CharString togo(newLanguage, lsize, status); // start with newLanguage 322 323 // _Country 324 if ( newCountry != NULL ) 325 { 326 csize = (int32_t)uprv_strlen(newCountry); 327 if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap 328 setToBogus(); 329 return; 330 } 331 size += csize; 332 } 333 334 // _Variant 335 if ( newVariant != NULL ) 336 { 337 // remove leading _'s 338 while(newVariant[0] == SEP_CHAR) 339 { 340 newVariant++; 341 } 342 343 // remove trailing _'s 344 vsize = (int32_t)uprv_strlen(newVariant); 345 if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap 346 setToBogus(); 347 return; 348 } 349 while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) 350 { 351 vsize--; 352 } 353 } 354 355 if( vsize > 0 ) 356 { 357 size += vsize; 358 } 359 360 // Separator rules: 361 if ( vsize > 0 ) 362 { 363 size += 2; // at least: __v 364 } 365 else if ( csize > 0 ) 366 { 367 size += 1; // at least: _v 368 } 369 370 if ( newKeywords != NULL) 371 { 372 ksize = (int32_t)uprv_strlen(newKeywords); 373 if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) { 374 setToBogus(); 375 return; 376 } 377 size += ksize + 1; 378 } 379 380 // NOW we have the full locale string.. 381 // Now, copy it back. 382 383 // newLanguage is already copied 384 385 if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v 386 { // ^ 387 togo.append(SEP_CHAR, status); 388 } 389 390 if ( csize != 0 ) 391 { 392 togo.append(newCountry, status); 393 } 394 395 if ( vsize != 0) 396 { 397 togo.append(SEP_CHAR, status) 398 .append(newVariant, vsize, status); 399 } 400 401 if ( ksize != 0) 402 { 403 if (uprv_strchr(newKeywords, '=')) { 404 togo.append('@', status); /* keyword parsing */ 405 } 406 else { 407 togo.append('_', status); /* Variant parsing with a script */ 408 if ( vsize == 0) { 409 togo.append('_', status); /* No country found */ 410 } 411 } 412 togo.append(newKeywords, status); 413 } 414 415 if (U_FAILURE(status)) { 416 // Something went wrong with appending, etc. 417 setToBogus(); 418 return; 419 } 420 // Parse it, because for example 'language' might really be a complete 421 // string. 422 init(togo.data(), FALSE); 423 } 424 } 425 426 Locale::Locale(const Locale &other) 427 : UObject(other), fullName(fullNameBuffer), baseName(NULL) 428 { 429 *this = other; 430 } 431 432 Locale::Locale(Locale&& other) U_NOEXCEPT 433 : UObject(other), fullName(fullNameBuffer), baseName(fullName) { 434 *this = std::move(other); 435 } 436 437 Locale& Locale::operator=(const Locale& other) { 438 if (this == &other) { 439 return *this; 440 } 441 442 setToBogus(); 443 444 if (other.fullName == other.fullNameBuffer) { 445 uprv_strcpy(fullNameBuffer, other.fullNameBuffer); 446 } else if (other.fullName == nullptr) { 447 fullName = nullptr; 448 } else { 449 fullName = uprv_strdup(other.fullName); 450 if (fullName == nullptr) return *this; 451 } 452 453 if (other.baseName == other.fullName) { 454 baseName = fullName; 455 } else if (other.baseName != nullptr) { 456 baseName = uprv_strdup(other.baseName); 457 if (baseName == nullptr) return *this; 458 } 459 460 uprv_strcpy(language, other.language); 461 uprv_strcpy(script, other.script); 462 uprv_strcpy(country, other.country); 463 464 variantBegin = other.variantBegin; 465 fIsBogus = other.fIsBogus; 466 467 return *this; 468 } 469 470 Locale& Locale::operator=(Locale&& other) U_NOEXCEPT { 471 if (baseName != fullName) uprv_free(baseName); 472 if (fullName != fullNameBuffer) uprv_free(fullName); 473 474 if (other.fullName == other.fullNameBuffer) { 475 uprv_strcpy(fullNameBuffer, other.fullNameBuffer); 476 fullName = fullNameBuffer; 477 } else { 478 fullName = other.fullName; 479 } 480 481 if (other.baseName == other.fullName) { 482 baseName = fullName; 483 } else { 484 baseName = other.baseName; 485 } 486 487 uprv_strcpy(language, other.language); 488 uprv_strcpy(script, other.script); 489 uprv_strcpy(country, other.country); 490 491 variantBegin = other.variantBegin; 492 fIsBogus = other.fIsBogus; 493 494 other.baseName = other.fullName = other.fullNameBuffer; 495 496 return *this; 497 } 498 499 Locale * 500 Locale::clone() const { 501 return new Locale(*this); 502 } 503 504 UBool 505 Locale::operator==( const Locale& other) const 506 { 507 return (uprv_strcmp(other.fullName, fullName) == 0); 508 } 509 510 #define ISASCIIALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 511 512 /*This function initializes a Locale from a C locale ID*/ 513 Locale& Locale::init(const char* localeID, UBool canonicalize) 514 { 515 fIsBogus = FALSE; 516 /* Free our current storage */ 517 if (baseName != fullName) { 518 uprv_free(baseName); 519 } 520 baseName = NULL; 521 if(fullName != fullNameBuffer) { 522 uprv_free(fullName); 523 fullName = fullNameBuffer; 524 } 525 526 // not a loop: 527 // just an easy way to have a common error-exit 528 // without goto and without another function 529 do { 530 char *separator; 531 char *field[5] = {0}; 532 int32_t fieldLen[5] = {0}; 533 int32_t fieldIdx; 534 int32_t variantField; 535 int32_t length; 536 UErrorCode err; 537 538 if(localeID == NULL) { 539 // not an error, just set the default locale 540 return *this = getDefault(); 541 } 542 543 /* preset all fields to empty */ 544 language[0] = script[0] = country[0] = 0; 545 546 // "canonicalize" the locale ID to ICU/Java format 547 err = U_ZERO_ERROR; 548 length = canonicalize ? 549 uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : 550 uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); 551 552 if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { 553 /*Go to heap for the fullName if necessary*/ 554 fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); 555 if(fullName == 0) { 556 fullName = fullNameBuffer; 557 break; // error: out of memory 558 } 559 err = U_ZERO_ERROR; 560 length = canonicalize ? 561 uloc_canonicalize(localeID, fullName, length+1, &err) : 562 uloc_getName(localeID, fullName, length+1, &err); 563 } 564 if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { 565 /* should never occur */ 566 break; 567 } 568 569 variantBegin = length; 570 571 /* after uloc_getName/canonicalize() we know that only '_' are separators */ 572 separator = field[0] = fullName; 573 fieldIdx = 1; 574 while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 && fieldIdx < UPRV_LENGTHOF(field)-1) { 575 field[fieldIdx] = separator + 1; 576 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); 577 fieldIdx++; 578 } 579 // variant may contain @foo or .foo POSIX cruft; remove it 580 separator = uprv_strchr(field[fieldIdx-1], '@'); 581 char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); 582 if (separator!=NULL || sep2!=NULL) { 583 if (separator==NULL || (sep2!=NULL && separator > sep2)) { 584 separator = sep2; 585 } 586 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); 587 } else { 588 fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); 589 } 590 591 if (fieldLen[0] >= (int32_t)(sizeof(language))) 592 { 593 break; // error: the language field is too long 594 } 595 596 variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ 597 if (fieldLen[0] > 0) { 598 /* We have a language */ 599 uprv_memcpy(language, fullName, fieldLen[0]); 600 language[fieldLen[0]] = 0; 601 } 602 if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) && 603 ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) && 604 ISASCIIALPHA(field[1][3])) { 605 /* We have at least a script */ 606 uprv_memcpy(script, field[1], fieldLen[1]); 607 script[fieldLen[1]] = 0; 608 variantField++; 609 } 610 611 if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { 612 /* We have a country */ 613 uprv_memcpy(country, field[variantField], fieldLen[variantField]); 614 country[fieldLen[variantField]] = 0; 615 variantField++; 616 } else if (fieldLen[variantField] == 0) { 617 variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ 618 } 619 620 if (fieldLen[variantField] > 0) { 621 /* We have a variant */ 622 variantBegin = (int32_t)(field[variantField] - fullName); 623 } 624 625 err = U_ZERO_ERROR; 626 initBaseName(err); 627 if (U_FAILURE(err)) { 628 break; 629 } 630 631 // successful end of init() 632 return *this; 633 } while(0); /*loop doesn't iterate*/ 634 635 // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) 636 setToBogus(); 637 638 return *this; 639 } 640 641 /* 642 * Set up the base name. 643 * If there are no key words, it's exactly the full name. 644 * If key words exist, it's the full name truncated at the '@' character. 645 * Need to set up both at init() and after setting a keyword. 646 */ 647 void 648 Locale::initBaseName(UErrorCode &status) { 649 if (U_FAILURE(status)) { 650 return; 651 } 652 U_ASSERT(baseName==NULL || baseName==fullName); 653 const char *atPtr = uprv_strchr(fullName, '@'); 654 const char *eqPtr = uprv_strchr(fullName, '='); 655 if (atPtr && eqPtr && atPtr < eqPtr) { 656 // Key words exist. 657 int32_t baseNameLength = (int32_t)(atPtr - fullName); 658 baseName = (char *)uprv_malloc(baseNameLength + 1); 659 if (baseName == NULL) { 660 status = U_MEMORY_ALLOCATION_ERROR; 661 return; 662 } 663 uprv_strncpy(baseName, fullName, baseNameLength); 664 baseName[baseNameLength] = 0; 665 666 // The original computation of variantBegin leaves it equal to the length 667 // of fullName if there is no variant. It should instead be 668 // the length of the baseName. 669 if (variantBegin > baseNameLength) { 670 variantBegin = baseNameLength; 671 } 672 } else { 673 baseName = fullName; 674 } 675 } 676 677 678 int32_t 679 Locale::hashCode() const 680 { 681 return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName))); 682 } 683 684 void 685 Locale::setToBogus() { 686 /* Free our current storage */ 687 if(baseName != fullName) { 688 uprv_free(baseName); 689 } 690 baseName = NULL; 691 if(fullName != fullNameBuffer) { 692 uprv_free(fullName); 693 fullName = fullNameBuffer; 694 } 695 *fullNameBuffer = 0; 696 *language = 0; 697 *script = 0; 698 *country = 0; 699 fIsBogus = TRUE; 700 variantBegin = 0; 701 } 702 703 const Locale& U_EXPORT2 704 Locale::getDefault() 705 { 706 { 707 Mutex lock(&gDefaultLocaleMutex); 708 if (gDefaultLocale != NULL) { 709 return *gDefaultLocale; 710 } 711 } 712 UErrorCode status = U_ZERO_ERROR; 713 return *locale_set_default_internal(NULL, status); 714 } 715 716 717 718 void U_EXPORT2 719 Locale::setDefault( const Locale& newLocale, 720 UErrorCode& status) 721 { 722 if (U_FAILURE(status)) { 723 return; 724 } 725 726 /* Set the default from the full name string of the supplied locale. 727 * This is a convenient way to access the default locale caching mechanisms. 728 */ 729 const char *localeID = newLocale.getName(); 730 locale_set_default_internal(localeID, status); 731 } 732 733 void 734 Locale::addLikelySubtags(UErrorCode& status) { 735 if (U_FAILURE(status)) { 736 return; 737 } 738 739 // The maximized locale ID string is often longer, but there is no good 740 // heuristic to estimate just how much longer. Leave that to CharString. 741 CharString maximizedLocaleID; 742 int32_t maximizedLocaleIDCapacity = static_cast<int32_t>(uprv_strlen(fullName)); 743 744 char* buffer; 745 int32_t reslen; 746 747 for (;;) { 748 buffer = maximizedLocaleID.getAppendBuffer( 749 /*minCapacity=*/maximizedLocaleIDCapacity, 750 /*desiredCapacityHint=*/maximizedLocaleIDCapacity, 751 maximizedLocaleIDCapacity, 752 status); 753 754 if (U_FAILURE(status)) { 755 return; 756 } 757 758 reslen = uloc_addLikelySubtags( 759 fullName, 760 buffer, 761 maximizedLocaleIDCapacity, 762 &status); 763 764 if (status != U_BUFFER_OVERFLOW_ERROR) { 765 break; 766 } 767 768 maximizedLocaleIDCapacity = reslen; 769 status = U_ZERO_ERROR; 770 } 771 772 if (U_FAILURE(status)) { 773 return; 774 } 775 776 maximizedLocaleID.append(buffer, reslen, status); 777 if (status == U_STRING_NOT_TERMINATED_WARNING) { 778 status = U_ZERO_ERROR; // Terminators provided by CharString. 779 } 780 781 if (U_FAILURE(status)) { 782 return; 783 } 784 785 init(maximizedLocaleID.data(), /*canonicalize=*/FALSE); 786 if (isBogus()) { 787 status = U_ILLEGAL_ARGUMENT_ERROR; 788 } 789 } 790 791 void 792 Locale::minimizeSubtags(UErrorCode& status) { 793 if (U_FAILURE(status)) { 794 return; 795 } 796 797 // Except for a few edge cases (like the empty string, that is minimized to 798 // "en__POSIX"), minimized locale ID strings will be either the same length 799 // or shorter than their input. 800 CharString minimizedLocaleID; 801 int32_t minimizedLocaleIDCapacity = static_cast<int32_t>(uprv_strlen(fullName)); 802 803 char* buffer; 804 int32_t reslen; 805 806 for (;;) { 807 buffer = minimizedLocaleID.getAppendBuffer( 808 /*minCapacity=*/minimizedLocaleIDCapacity, 809 /*desiredCapacityHint=*/minimizedLocaleIDCapacity, 810 minimizedLocaleIDCapacity, 811 status); 812 813 if (U_FAILURE(status)) { 814 return; 815 } 816 817 reslen = uloc_minimizeSubtags( 818 fullName, 819 buffer, 820 minimizedLocaleIDCapacity, 821 &status); 822 823 if (status != U_BUFFER_OVERFLOW_ERROR) { 824 break; 825 } 826 827 // Because of the internal minimal buffer size of CharString, I can't 828 // think of any input data for which this could possibly ever happen. 829 // Maybe it would be better replaced with an assertion instead? 830 minimizedLocaleIDCapacity = reslen; 831 status = U_ZERO_ERROR; 832 } 833 834 if (U_FAILURE(status)) { 835 return; 836 } 837 838 minimizedLocaleID.append(buffer, reslen, status); 839 if (status == U_STRING_NOT_TERMINATED_WARNING) { 840 status = U_ZERO_ERROR; // Terminators provided by CharString. 841 } 842 843 if (U_FAILURE(status)) { 844 return; 845 } 846 847 init(minimizedLocaleID.data(), /*canonicalize=*/FALSE); 848 if (isBogus()) { 849 status = U_ILLEGAL_ARGUMENT_ERROR; 850 } 851 } 852 853 Locale U_EXPORT2 854 Locale::forLanguageTag(StringPiece tag, UErrorCode& status) 855 { 856 Locale result(Locale::eBOGUS); 857 858 if (U_FAILURE(status)) { 859 return result; 860 } 861 862 // If a BCP-47 language tag is passed as the language parameter to the 863 // normal Locale constructor, it will actually fall back to invoking 864 // uloc_forLanguageTag() to parse it if it somehow is able to detect that 865 // the string actually is BCP-47. This works well for things like strings 866 // using BCP-47 extensions, but it does not at all work for things like 867 // BCP-47 grandfathered tags (eg. "en-GB-oed") which are possible to also 868 // interpret as ICU locale IDs and because of that won't trigger the BCP-47 869 // parsing. Therefore the code here explicitly calls uloc_forLanguageTag() 870 // and then Locale::init(), instead of just calling the normal constructor. 871 872 // All simple language tags will have the exact same length as ICU locale 873 // ID strings as they have as BCP-47 strings (like "en_US" for "en-US"). 874 CharString localeID; 875 int32_t resultCapacity = tag.size(); 876 877 char* buffer; 878 int32_t parsedLength, reslen; 879 880 for (;;) { 881 buffer = localeID.getAppendBuffer( 882 /*minCapacity=*/resultCapacity, 883 /*desiredCapacityHint=*/resultCapacity, 884 resultCapacity, 885 status); 886 887 if (U_FAILURE(status)) { 888 return result; 889 } 890 891 reslen = ulocimp_forLanguageTag( 892 tag.data(), 893 tag.length(), 894 buffer, 895 resultCapacity, 896 &parsedLength, 897 &status); 898 899 if (status != U_BUFFER_OVERFLOW_ERROR) { 900 break; 901 } 902 903 // For all BCP-47 language tags that use extensions, the corresponding 904 // ICU locale ID will be longer but uloc_forLanguageTag() does compute 905 // the exact length needed so this memory reallocation will be done at 906 // most once. 907 resultCapacity = reslen; 908 status = U_ZERO_ERROR; 909 } 910 911 if (U_FAILURE(status)) { 912 return result; 913 } 914 915 if (parsedLength != tag.size()) { 916 status = U_ILLEGAL_ARGUMENT_ERROR; 917 return result; 918 } 919 920 localeID.append(buffer, reslen, status); 921 if (status == U_STRING_NOT_TERMINATED_WARNING) { 922 status = U_ZERO_ERROR; // Terminators provided by CharString. 923 } 924 925 if (U_FAILURE(status)) { 926 return result; 927 } 928 929 result.init(localeID.data(), /*canonicalize=*/FALSE); 930 if (result.isBogus()) { 931 status = U_ILLEGAL_ARGUMENT_ERROR; 932 } 933 return result; 934 } 935 936 void 937 Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const 938 { 939 if (U_FAILURE(status)) { 940 return; 941 } 942 943 if (fIsBogus) { 944 status = U_ILLEGAL_ARGUMENT_ERROR; 945 return; 946 } 947 948 // All simple language tags will have the exact same length as BCP-47 949 // strings as they have as ICU locale IDs (like "en-US" for "en_US"). 950 LocalMemory<char> scratch; 951 int32_t scratch_capacity = static_cast<int32_t>(uprv_strlen(fullName)); 952 953 if (scratch_capacity == 0) { 954 scratch_capacity = 3; // "und" 955 } 956 957 char* buffer; 958 int32_t result_capacity, reslen; 959 960 for (;;) { 961 if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) { 962 status = U_MEMORY_ALLOCATION_ERROR; 963 return; 964 } 965 966 buffer = sink.GetAppendBuffer( 967 /*min_capacity=*/scratch_capacity, 968 /*desired_capacity_hint=*/scratch_capacity, 969 scratch.getAlias(), 970 scratch_capacity, 971 &result_capacity); 972 973 reslen = uloc_toLanguageTag( 974 fullName, 975 buffer, 976 result_capacity, 977 /*strict=*/FALSE, 978 &status); 979 980 if (status != U_BUFFER_OVERFLOW_ERROR) { 981 break; 982 } 983 984 // For some very few edge cases a language tag will be longer as a 985 // BCP-47 string than it is as an ICU locale ID. Most notoriously "C" 986 // expands to the BCP-47 tag "en-US-u-va-posix", 16 times longer, and 987 // it'll take several calls to uloc_toLanguageTag() to figure that out. 988 // https://unicode-org.atlassian.net/browse/ICU-20132 989 scratch_capacity = reslen; 990 status = U_ZERO_ERROR; 991 } 992 993 if (U_FAILURE(status)) { 994 return; 995 } 996 997 sink.Append(buffer, reslen); 998 if (status == U_STRING_NOT_TERMINATED_WARNING) { 999 status = U_ZERO_ERROR; // Terminators not used. 1000 } 1001 } 1002 1003 Locale U_EXPORT2 1004 Locale::createFromName (const char *name) 1005 { 1006 if (name) { 1007 Locale l(""); 1008 l.init(name, FALSE); 1009 return l; 1010 } 1011 else { 1012 return getDefault(); 1013 } 1014 } 1015 1016 Locale U_EXPORT2 1017 Locale::createCanonical(const char* name) { 1018 Locale loc(""); 1019 loc.init(name, TRUE); 1020 return loc; 1021 } 1022 1023 const char * 1024 Locale::getISO3Language() const 1025 { 1026 return uloc_getISO3Language(fullName); 1027 } 1028 1029 1030 const char * 1031 Locale::getISO3Country() const 1032 { 1033 return uloc_getISO3Country(fullName); 1034 } 1035 1036 /** 1037 * Return the LCID value as specified in the "LocaleID" resource for this 1038 * locale. The LocaleID must be expressed as a hexadecimal number, from 1039 * one to four digits. If the LocaleID resource is not present, or is 1040 * in an incorrect format, 0 is returned. The LocaleID is for use in 1041 * Windows (it is an LCID), but is available on all platforms. 1042 */ 1043 uint32_t 1044 Locale::getLCID() const 1045 { 1046 return uloc_getLCID(fullName); 1047 } 1048 1049 const char* const* U_EXPORT2 Locale::getISOCountries() 1050 { 1051 return uloc_getISOCountries(); 1052 } 1053 1054 const char* const* U_EXPORT2 Locale::getISOLanguages() 1055 { 1056 return uloc_getISOLanguages(); 1057 } 1058 1059 // Set the locale's data based on a posix id. 1060 void Locale::setFromPOSIXID(const char *posixID) 1061 { 1062 init(posixID, TRUE); 1063 } 1064 1065 const Locale & U_EXPORT2 1066 Locale::getRoot(void) 1067 { 1068 return getLocale(eROOT); 1069 } 1070 1071 const Locale & U_EXPORT2 1072 Locale::getEnglish(void) 1073 { 1074 return getLocale(eENGLISH); 1075 } 1076 1077 const Locale & U_EXPORT2 1078 Locale::getFrench(void) 1079 { 1080 return getLocale(eFRENCH); 1081 } 1082 1083 const Locale & U_EXPORT2 1084 Locale::getGerman(void) 1085 { 1086 return getLocale(eGERMAN); 1087 } 1088 1089 const Locale & U_EXPORT2 1090 Locale::getItalian(void) 1091 { 1092 return getLocale(eITALIAN); 1093 } 1094 1095 const Locale & U_EXPORT2 1096 Locale::getJapanese(void) 1097 { 1098 return getLocale(eJAPANESE); 1099 } 1100 1101 const Locale & U_EXPORT2 1102 Locale::getKorean(void) 1103 { 1104 return getLocale(eKOREAN); 1105 } 1106 1107 const Locale & U_EXPORT2 1108 Locale::getChinese(void) 1109 { 1110 return getLocale(eCHINESE); 1111 } 1112 1113 const Locale & U_EXPORT2 1114 Locale::getSimplifiedChinese(void) 1115 { 1116 return getLocale(eCHINA); 1117 } 1118 1119 const Locale & U_EXPORT2 1120 Locale::getTraditionalChinese(void) 1121 { 1122 return getLocale(eTAIWAN); 1123 } 1124 1125 1126 const Locale & U_EXPORT2 1127 Locale::getFrance(void) 1128 { 1129 return getLocale(eFRANCE); 1130 } 1131 1132 const Locale & U_EXPORT2 1133 Locale::getGermany(void) 1134 { 1135 return getLocale(eGERMANY); 1136 } 1137 1138 const Locale & U_EXPORT2 1139 Locale::getItaly(void) 1140 { 1141 return getLocale(eITALY); 1142 } 1143 1144 const Locale & U_EXPORT2 1145 Locale::getJapan(void) 1146 { 1147 return getLocale(eJAPAN); 1148 } 1149 1150 const Locale & U_EXPORT2 1151 Locale::getKorea(void) 1152 { 1153 return getLocale(eKOREA); 1154 } 1155 1156 const Locale & U_EXPORT2 1157 Locale::getChina(void) 1158 { 1159 return getLocale(eCHINA); 1160 } 1161 1162 const Locale & U_EXPORT2 1163 Locale::getPRC(void) 1164 { 1165 return getLocale(eCHINA); 1166 } 1167 1168 const Locale & U_EXPORT2 1169 Locale::getTaiwan(void) 1170 { 1171 return getLocale(eTAIWAN); 1172 } 1173 1174 const Locale & U_EXPORT2 1175 Locale::getUK(void) 1176 { 1177 return getLocale(eUK); 1178 } 1179 1180 const Locale & U_EXPORT2 1181 Locale::getUS(void) 1182 { 1183 return getLocale(eUS); 1184 } 1185 1186 const Locale & U_EXPORT2 1187 Locale::getCanada(void) 1188 { 1189 return getLocale(eCANADA); 1190 } 1191 1192 const Locale & U_EXPORT2 1193 Locale::getCanadaFrench(void) 1194 { 1195 return getLocale(eCANADA_FRENCH); 1196 } 1197 1198 const Locale & 1199 Locale::getLocale(int locid) 1200 { 1201 Locale *localeCache = getLocaleCache(); 1202 U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); 1203 if (localeCache == NULL) { 1204 // Failure allocating the locale cache. 1205 // The best we can do is return a NULL reference. 1206 locid = 0; 1207 } 1208 return localeCache[locid]; /*operating on NULL*/ 1209 } 1210 1211 /* 1212 This function is defined this way in order to get around static 1213 initialization and static destruction. 1214 */ 1215 Locale * 1216 Locale::getLocaleCache(void) 1217 { 1218 UErrorCode status = U_ZERO_ERROR; 1219 umtx_initOnce(gLocaleCacheInitOnce, locale_init, status); 1220 return gLocaleCache; 1221 } 1222 1223 class KeywordEnumeration : public StringEnumeration { 1224 private: 1225 char *keywords; 1226 char *current; 1227 int32_t length; 1228 UnicodeString currUSKey; 1229 static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ 1230 1231 public: 1232 static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } 1233 virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); } 1234 public: 1235 KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) 1236 : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { 1237 if(U_SUCCESS(status) && keywordLen != 0) { 1238 if(keys == NULL || keywordLen < 0) { 1239 status = U_ILLEGAL_ARGUMENT_ERROR; 1240 } else { 1241 keywords = (char *)uprv_malloc(keywordLen+1); 1242 if (keywords == NULL) { 1243 status = U_MEMORY_ALLOCATION_ERROR; 1244 } 1245 else { 1246 uprv_memcpy(keywords, keys, keywordLen); 1247 keywords[keywordLen] = 0; 1248 current = keywords + currentIndex; 1249 length = keywordLen; 1250 } 1251 } 1252 } 1253 } 1254 1255 virtual ~KeywordEnumeration(); 1256 1257 virtual StringEnumeration * clone() const 1258 { 1259 UErrorCode status = U_ZERO_ERROR; 1260 return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); 1261 } 1262 1263 virtual int32_t count(UErrorCode &/*status*/) const { 1264 char *kw = keywords; 1265 int32_t result = 0; 1266 while(*kw) { 1267 result++; 1268 kw += uprv_strlen(kw)+1; 1269 } 1270 return result; 1271 } 1272 1273 virtual const char* next(int32_t* resultLength, UErrorCode& status) { 1274 const char* result; 1275 int32_t len; 1276 if(U_SUCCESS(status) && *current != 0) { 1277 result = current; 1278 len = (int32_t)uprv_strlen(current); 1279 current += len+1; 1280 if(resultLength != NULL) { 1281 *resultLength = len; 1282 } 1283 } else { 1284 if(resultLength != NULL) { 1285 *resultLength = 0; 1286 } 1287 result = NULL; 1288 } 1289 return result; 1290 } 1291 1292 virtual const UnicodeString* snext(UErrorCode& status) { 1293 int32_t resultLength = 0; 1294 const char *s = next(&resultLength, status); 1295 return setChars(s, resultLength, status); 1296 } 1297 1298 virtual void reset(UErrorCode& /*status*/) { 1299 current = keywords; 1300 } 1301 }; 1302 1303 const char KeywordEnumeration::fgClassID = '\0'; 1304 1305 KeywordEnumeration::~KeywordEnumeration() { 1306 uprv_free(keywords); 1307 } 1308 1309 // A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in 1310 // the next() method for each keyword before returning it. 1311 class UnicodeKeywordEnumeration : public KeywordEnumeration { 1312 public: 1313 using KeywordEnumeration::KeywordEnumeration; 1314 virtual ~UnicodeKeywordEnumeration(); 1315 1316 virtual const char* next(int32_t* resultLength, UErrorCode& status) { 1317 const char* legacy_key = KeywordEnumeration::next(nullptr, status); 1318 if (U_SUCCESS(status) && legacy_key != nullptr) { 1319 const char* key = uloc_toUnicodeLocaleKey(legacy_key); 1320 if (key == nullptr) { 1321 status = U_ILLEGAL_ARGUMENT_ERROR; 1322 } else { 1323 if (resultLength != nullptr) { 1324 *resultLength = static_cast<int32_t>(uprv_strlen(key)); 1325 } 1326 return key; 1327 } 1328 } 1329 if (resultLength != nullptr) *resultLength = 0; 1330 return nullptr; 1331 } 1332 }; 1333 1334 // Out-of-line virtual destructor to serve as the "key function". 1335 UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default; 1336 1337 StringEnumeration * 1338 Locale::createKeywords(UErrorCode &status) const 1339 { 1340 char keywords[256]; 1341 int32_t keywordCapacity = sizeof keywords; 1342 StringEnumeration *result = NULL; 1343 1344 if (U_FAILURE(status)) { 1345 return result; 1346 } 1347 1348 const char* variantStart = uprv_strchr(fullName, '@'); 1349 const char* assignment = uprv_strchr(fullName, '='); 1350 if(variantStart) { 1351 if(assignment > variantStart) { 1352 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, NULL, 0, NULL, FALSE, &status); 1353 if(U_SUCCESS(status) && keyLen) { 1354 result = new KeywordEnumeration(keywords, keyLen, 0, status); 1355 if (!result) { 1356 status = U_MEMORY_ALLOCATION_ERROR; 1357 } 1358 } 1359 } else { 1360 status = U_INVALID_FORMAT_ERROR; 1361 } 1362 } 1363 return result; 1364 } 1365 1366 StringEnumeration * 1367 Locale::createUnicodeKeywords(UErrorCode &status) const 1368 { 1369 char keywords[256]; 1370 int32_t keywordCapacity = sizeof keywords; 1371 StringEnumeration *result = NULL; 1372 1373 if (U_FAILURE(status)) { 1374 return result; 1375 } 1376 1377 const char* variantStart = uprv_strchr(fullName, '@'); 1378 const char* assignment = uprv_strchr(fullName, '='); 1379 if(variantStart) { 1380 if(assignment > variantStart) { 1381 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, NULL, 0, NULL, FALSE, &status); 1382 if(U_SUCCESS(status) && keyLen) { 1383 result = new UnicodeKeywordEnumeration(keywords, keyLen, 0, status); 1384 if (!result) { 1385 status = U_MEMORY_ALLOCATION_ERROR; 1386 } 1387 } 1388 } else { 1389 status = U_INVALID_FORMAT_ERROR; 1390 } 1391 } 1392 return result; 1393 } 1394 1395 int32_t 1396 Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const 1397 { 1398 return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); 1399 } 1400 1401 void 1402 Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const { 1403 if (U_FAILURE(status)) { 1404 return; 1405 } 1406 1407 if (fIsBogus) { 1408 status = U_ILLEGAL_ARGUMENT_ERROR; 1409 return; 1410 } 1411 1412 // TODO: Remove the need for a const char* to a NUL terminated buffer. 1413 const CharString keywordName_nul(keywordName, status); 1414 if (U_FAILURE(status)) { 1415 return; 1416 } 1417 1418 LocalMemory<char> scratch; 1419 int32_t scratch_capacity = 16; // Arbitrarily chosen default size. 1420 1421 char* buffer; 1422 int32_t result_capacity, reslen; 1423 1424 for (;;) { 1425 if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) { 1426 status = U_MEMORY_ALLOCATION_ERROR; 1427 return; 1428 } 1429 1430 buffer = sink.GetAppendBuffer( 1431 /*min_capacity=*/scratch_capacity, 1432 /*desired_capacity_hint=*/scratch_capacity, 1433 scratch.getAlias(), 1434 scratch_capacity, 1435 &result_capacity); 1436 1437 reslen = uloc_getKeywordValue( 1438 fullName, 1439 keywordName_nul.data(), 1440 buffer, 1441 result_capacity, 1442 &status); 1443 1444 if (status != U_BUFFER_OVERFLOW_ERROR) { 1445 break; 1446 } 1447 1448 scratch_capacity = reslen; 1449 status = U_ZERO_ERROR; 1450 } 1451 1452 if (U_FAILURE(status)) { 1453 return; 1454 } 1455 1456 sink.Append(buffer, reslen); 1457 if (status == U_STRING_NOT_TERMINATED_WARNING) { 1458 status = U_ZERO_ERROR; // Terminators not used. 1459 } 1460 } 1461 1462 void 1463 Locale::getUnicodeKeywordValue(StringPiece keywordName, 1464 ByteSink& sink, 1465 UErrorCode& status) const { 1466 // TODO: Remove the need for a const char* to a NUL terminated buffer. 1467 const CharString keywordName_nul(keywordName, status); 1468 if (U_FAILURE(status)) { 1469 return; 1470 } 1471 1472 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); 1473 1474 if (legacy_key == nullptr) { 1475 status = U_ILLEGAL_ARGUMENT_ERROR; 1476 return; 1477 } 1478 1479 CharString legacy_value; 1480 { 1481 CharStringByteSink sink(&legacy_value); 1482 getKeywordValue(legacy_key, sink, status); 1483 } 1484 1485 if (U_FAILURE(status)) { 1486 return; 1487 } 1488 1489 const char* unicode_value = uloc_toUnicodeLocaleType( 1490 keywordName_nul.data(), legacy_value.data()); 1491 1492 if (unicode_value == nullptr) { 1493 status = U_ILLEGAL_ARGUMENT_ERROR; 1494 return; 1495 } 1496 1497 sink.Append(unicode_value, static_cast<int32_t>(uprv_strlen(unicode_value))); 1498 } 1499 1500 void 1501 Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) 1502 { 1503 uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status); 1504 if (U_SUCCESS(status) && baseName == fullName) { 1505 // May have added the first keyword, meaning that the fullName is no longer also the baseName. 1506 initBaseName(status); 1507 } 1508 } 1509 1510 void 1511 Locale::setKeywordValue(StringPiece keywordName, 1512 StringPiece keywordValue, 1513 UErrorCode& status) { 1514 // TODO: Remove the need for a const char* to a NUL terminated buffer. 1515 const CharString keywordName_nul(keywordName, status); 1516 const CharString keywordValue_nul(keywordValue, status); 1517 setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status); 1518 } 1519 1520 void 1521 Locale::setUnicodeKeywordValue(StringPiece keywordName, 1522 StringPiece keywordValue, 1523 UErrorCode& status) { 1524 // TODO: Remove the need for a const char* to a NUL terminated buffer. 1525 const CharString keywordName_nul(keywordName, status); 1526 const CharString keywordValue_nul(keywordValue, status); 1527 1528 if (U_FAILURE(status)) { 1529 return; 1530 } 1531 1532 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); 1533 1534 if (legacy_key == nullptr) { 1535 status = U_ILLEGAL_ARGUMENT_ERROR; 1536 return; 1537 } 1538 1539 const char* legacy_value = 1540 uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data()); 1541 1542 if (legacy_value == nullptr) { 1543 status = U_ILLEGAL_ARGUMENT_ERROR; 1544 return; 1545 } 1546 1547 setKeywordValue(legacy_key, legacy_value, status); 1548 } 1549 1550 const char * 1551 Locale::getBaseName() const { 1552 return baseName; 1553 } 1554 1555 //eof 1556 U_NAMESPACE_END 1557