locdispnames.cpp (35100B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * 6 * Copyright (C) 1997-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ******************************************************************************* 10 * file name: locdispnames.cpp 11 * encoding: UTF-8 12 * tab size: 8 (not used) 13 * indentation:4 14 * 15 * created on: 2010feb25 16 * created by: Markus W. Scherer 17 * 18 * Code for locale display names, separated out from other .cpp files 19 * that then do not depend on resource bundle code and display name data. 20 */ 21 22 #include <string_view> 23 24 #include "unicode/utypes.h" 25 #include "unicode/brkiter.h" 26 #include "unicode/locid.h" 27 #include "unicode/uenum.h" 28 #include "unicode/uloc.h" 29 #include "unicode/ures.h" 30 #include "unicode/ustring.h" 31 #include "charstr.h" 32 #include "cmemory.h" 33 #include "cstring.h" 34 #include "putilimp.h" 35 #include "ulocimp.h" 36 #include "uresimp.h" 37 #include "ureslocs.h" 38 #include "ustr_imp.h" 39 40 // C++ API ----------------------------------------------------------------- *** 41 42 U_NAMESPACE_BEGIN 43 44 UnicodeString& 45 Locale::getDisplayLanguage(UnicodeString& dispLang) const 46 { 47 return this->getDisplayLanguage(getDefault(), dispLang); 48 } 49 50 /*We cannot make any assumptions on the size of the output display strings 51 * Yet, since we are calling through to a C API, we need to set limits on 52 * buffer size. For all the following getDisplay functions we first attempt 53 * to fill up a stack allocated buffer. If it is to small we heap allocated 54 * the exact buffer we need copy it to the UnicodeString and delete it*/ 55 56 UnicodeString& 57 Locale::getDisplayLanguage(const Locale &displayLocale, 58 UnicodeString &result) const { 59 char16_t *buffer; 60 UErrorCode errorCode=U_ZERO_ERROR; 61 int32_t length; 62 63 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); 64 if (buffer == nullptr) { 65 result.truncate(0); 66 return result; 67 } 68 69 length=uloc_getDisplayLanguage(getName(), displayLocale.getName(), 70 buffer, result.getCapacity(), 71 &errorCode); 72 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 73 74 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { 75 buffer=result.getBuffer(length); 76 if (buffer == nullptr) { 77 result.truncate(0); 78 return result; 79 } 80 errorCode=U_ZERO_ERROR; 81 length=uloc_getDisplayLanguage(getName(), displayLocale.getName(), 82 buffer, result.getCapacity(), 83 &errorCode); 84 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 85 } 86 87 return result; 88 } 89 90 UnicodeString& 91 Locale::getDisplayScript(UnicodeString& dispScript) const 92 { 93 return this->getDisplayScript(getDefault(), dispScript); 94 } 95 96 UnicodeString& 97 Locale::getDisplayScript(const Locale &displayLocale, 98 UnicodeString &result) const { 99 char16_t *buffer; 100 UErrorCode errorCode=U_ZERO_ERROR; 101 int32_t length; 102 103 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); 104 if (buffer == nullptr) { 105 result.truncate(0); 106 return result; 107 } 108 109 length=uloc_getDisplayScript(getName(), displayLocale.getName(), 110 buffer, result.getCapacity(), 111 &errorCode); 112 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 113 114 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { 115 buffer=result.getBuffer(length); 116 if (buffer == nullptr) { 117 result.truncate(0); 118 return result; 119 } 120 errorCode=U_ZERO_ERROR; 121 length=uloc_getDisplayScript(getName(), displayLocale.getName(), 122 buffer, result.getCapacity(), 123 &errorCode); 124 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 125 } 126 127 return result; 128 } 129 130 UnicodeString& 131 Locale::getDisplayCountry(UnicodeString& dispCntry) const 132 { 133 return this->getDisplayCountry(getDefault(), dispCntry); 134 } 135 136 UnicodeString& 137 Locale::getDisplayCountry(const Locale &displayLocale, 138 UnicodeString &result) const { 139 char16_t *buffer; 140 UErrorCode errorCode=U_ZERO_ERROR; 141 int32_t length; 142 143 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); 144 if (buffer == nullptr) { 145 result.truncate(0); 146 return result; 147 } 148 149 length=uloc_getDisplayCountry(getName(), displayLocale.getName(), 150 buffer, result.getCapacity(), 151 &errorCode); 152 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 153 154 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { 155 buffer=result.getBuffer(length); 156 if (buffer == nullptr) { 157 result.truncate(0); 158 return result; 159 } 160 errorCode=U_ZERO_ERROR; 161 length=uloc_getDisplayCountry(getName(), displayLocale.getName(), 162 buffer, result.getCapacity(), 163 &errorCode); 164 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 165 } 166 167 return result; 168 } 169 170 UnicodeString& 171 Locale::getDisplayVariant(UnicodeString& dispVar) const 172 { 173 return this->getDisplayVariant(getDefault(), dispVar); 174 } 175 176 UnicodeString& 177 Locale::getDisplayVariant(const Locale &displayLocale, 178 UnicodeString &result) const { 179 char16_t *buffer; 180 UErrorCode errorCode=U_ZERO_ERROR; 181 int32_t length; 182 183 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); 184 if (buffer == nullptr) { 185 result.truncate(0); 186 return result; 187 } 188 189 length=uloc_getDisplayVariant(getName(), displayLocale.getName(), 190 buffer, result.getCapacity(), 191 &errorCode); 192 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 193 194 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { 195 buffer=result.getBuffer(length); 196 if (buffer == nullptr) { 197 result.truncate(0); 198 return result; 199 } 200 errorCode=U_ZERO_ERROR; 201 length=uloc_getDisplayVariant(getName(), displayLocale.getName(), 202 buffer, result.getCapacity(), 203 &errorCode); 204 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 205 } 206 207 return result; 208 } 209 210 UnicodeString& 211 Locale::getDisplayName( UnicodeString& name ) const 212 { 213 return this->getDisplayName(getDefault(), name); 214 } 215 216 UnicodeString& 217 Locale::getDisplayName(const Locale &displayLocale, 218 UnicodeString &result) const { 219 char16_t *buffer; 220 UErrorCode errorCode=U_ZERO_ERROR; 221 int32_t length; 222 223 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); 224 if (buffer == nullptr) { 225 result.truncate(0); 226 return result; 227 } 228 229 length=uloc_getDisplayName(getName(), displayLocale.getName(), 230 buffer, result.getCapacity(), 231 &errorCode); 232 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 233 234 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { 235 buffer=result.getBuffer(length); 236 if (buffer == nullptr) { 237 result.truncate(0); 238 return result; 239 } 240 errorCode=U_ZERO_ERROR; 241 length=uloc_getDisplayName(getName(), displayLocale.getName(), 242 buffer, result.getCapacity(), 243 &errorCode); 244 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); 245 } 246 247 return result; 248 } 249 250 #if !UCONFIG_NO_BREAK_ITERATION 251 252 // ------------------------------------- 253 // Gets the objectLocale display name in the default locale language. 254 UnicodeString& U_EXPORT2 255 BreakIterator::getDisplayName(const Locale& objectLocale, 256 UnicodeString& name) 257 { 258 return objectLocale.getDisplayName(name); 259 } 260 261 // ------------------------------------- 262 // Gets the objectLocale display name in the displayLocale language. 263 UnicodeString& U_EXPORT2 264 BreakIterator::getDisplayName(const Locale& objectLocale, 265 const Locale& displayLocale, 266 UnicodeString& name) 267 { 268 return objectLocale.getDisplayName(displayLocale, name); 269 } 270 271 #endif 272 273 274 U_NAMESPACE_END 275 276 // C API ------------------------------------------------------------------- *** 277 278 U_NAMESPACE_USE 279 280 namespace { 281 282 /* ### Constants **************************************************/ 283 284 /* These strings describe the resources we attempt to load from 285 the locale ResourceBundle data file.*/ 286 constexpr char _kLanguages[] = "Languages"; 287 constexpr char _kScripts[] = "Scripts"; 288 constexpr char _kScriptsStandAlone[] = "Scripts%stand-alone"; 289 constexpr char _kCountries[] = "Countries"; 290 constexpr char _kVariants[] = "Variants"; 291 constexpr char _kKeys[] = "Keys"; 292 constexpr char _kTypes[] = "Types"; 293 //constexpr char _kRootName[] = "root"; 294 constexpr char _kCurrency[] = "currency"; 295 constexpr char _kCurrencies[] = "Currencies"; 296 constexpr char _kLocaleDisplayPattern[] = "localeDisplayPattern"; 297 constexpr char _kPattern[] = "pattern"; 298 constexpr char _kSeparator[] = "separator"; 299 300 /* ### Display name **************************************************/ 301 302 int32_t 303 _getStringOrCopyKey(const char *path, const char *locale, 304 const char *tableKey, 305 const char* subTableKey, 306 const char *itemKey, 307 const char *substitute, 308 char16_t *dest, int32_t destCapacity, 309 UErrorCode &errorCode) { 310 if (U_FAILURE(errorCode)) { return 0; } 311 const char16_t *s = nullptr; 312 int32_t length = 0; 313 314 if(itemKey==nullptr) { 315 /* top-level item: normal resource bundle access */ 316 icu::LocalUResourceBundlePointer rb(ures_open(path, locale, &errorCode)); 317 318 if(U_SUCCESS(errorCode)) { 319 s=ures_getStringByKey(rb.getAlias(), tableKey, &length, &errorCode); 320 /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */ 321 } 322 } else { 323 bool isLanguageCode = (uprv_strncmp(tableKey, _kLanguages, 9) == 0); 324 /* Language code should not be a number. If it is, set the error code. */ 325 if (isLanguageCode && uprv_strtol(itemKey, nullptr, 10)) { 326 errorCode = U_MISSING_RESOURCE_ERROR; 327 } else { 328 /* second-level item, use special fallback */ 329 s=uloc_getTableStringWithFallback(path, locale, 330 tableKey, 331 subTableKey, 332 itemKey, 333 &length, 334 &errorCode); 335 if (U_FAILURE(errorCode) && isLanguageCode && itemKey != nullptr) { 336 // convert itemKey locale code to canonical form and try again, ICU-20870 337 errorCode = U_ZERO_ERROR; 338 Locale canonKey = Locale::createCanonical(itemKey); 339 s=uloc_getTableStringWithFallback(path, locale, 340 tableKey, 341 subTableKey, 342 canonKey.getName(), 343 &length, 344 &errorCode); 345 } 346 } 347 } 348 349 if(U_SUCCESS(errorCode)) { 350 int32_t copyLength=uprv_min(length, destCapacity); 351 if(copyLength>0 && s != nullptr) { 352 u_memcpy(dest, s, copyLength); 353 } 354 } else { 355 /* no string from a resource bundle: convert the substitute */ 356 length = static_cast<int32_t>(uprv_strlen(substitute)); 357 u_charsToUChars(substitute, dest, uprv_min(length, destCapacity)); 358 errorCode = U_USING_DEFAULT_WARNING; 359 } 360 361 return u_terminateUChars(dest, destCapacity, length, &errorCode); 362 } 363 364 using UDisplayNameGetter = icu::CharString(std::string_view, UErrorCode&); 365 366 int32_t 367 _getDisplayNameForComponent(const char *locale, 368 const char *displayLocale, 369 char16_t *dest, int32_t destCapacity, 370 UDisplayNameGetter *getter, 371 const char *tag, 372 UErrorCode &errorCode) { 373 if (U_FAILURE(errorCode)) { return 0; } 374 UErrorCode localStatus; 375 const char* root = nullptr; 376 377 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { 378 errorCode = U_ILLEGAL_ARGUMENT_ERROR; 379 return 0; 380 } 381 382 if (locale == nullptr) { 383 locale = uloc_getDefault(); 384 } 385 386 localStatus = U_ZERO_ERROR; 387 icu::CharString localeBuffer = (*getter)(locale, localStatus); 388 if (U_FAILURE(localStatus)) { 389 errorCode = U_ILLEGAL_ARGUMENT_ERROR; 390 return 0; 391 } 392 if (localeBuffer.isEmpty()) { 393 // For the display name, we treat this as unknown language (ICU-20273). 394 if (getter == ulocimp_getLanguage) { 395 localeBuffer.append("und", errorCode); 396 } else { 397 return u_terminateUChars(dest, destCapacity, 0, &errorCode); 398 } 399 } 400 401 root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG; 402 403 return _getStringOrCopyKey(root, displayLocale, 404 tag, nullptr, localeBuffer.data(), 405 localeBuffer.data(), 406 dest, destCapacity, 407 errorCode); 408 } 409 410 } // namespace 411 412 U_CAPI int32_t U_EXPORT2 413 uloc_getDisplayLanguage(const char *locale, 414 const char *displayLocale, 415 char16_t *dest, int32_t destCapacity, 416 UErrorCode *pErrorCode) { 417 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 418 ulocimp_getLanguage, _kLanguages, *pErrorCode); 419 } 420 421 U_CAPI int32_t U_EXPORT2 422 uloc_getDisplayScript(const char* locale, 423 const char* displayLocale, 424 char16_t *dest, int32_t destCapacity, 425 UErrorCode *pErrorCode) 426 { 427 if (U_FAILURE(*pErrorCode)) { return 0; } 428 UErrorCode err = U_ZERO_ERROR; 429 int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 430 ulocimp_getScript, _kScriptsStandAlone, err); 431 432 if (destCapacity == 0 && err == U_BUFFER_OVERFLOW_ERROR) { 433 // For preflight, return the max of the value and the fallback. 434 int32_t fallback_res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 435 ulocimp_getScript, _kScripts, *pErrorCode); 436 return (fallback_res > res) ? fallback_res : res; 437 } 438 if ( err == U_USING_DEFAULT_WARNING ) { 439 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 440 ulocimp_getScript, _kScripts, *pErrorCode); 441 } else { 442 *pErrorCode = err; 443 return res; 444 } 445 } 446 447 static int32_t 448 uloc_getDisplayScriptInContext(const char* locale, 449 const char* displayLocale, 450 char16_t *dest, int32_t destCapacity, 451 UErrorCode *pErrorCode) 452 { 453 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 454 ulocimp_getScript, _kScripts, *pErrorCode); 455 } 456 457 U_CAPI int32_t U_EXPORT2 458 uloc_getDisplayCountry(const char *locale, 459 const char *displayLocale, 460 char16_t *dest, int32_t destCapacity, 461 UErrorCode *pErrorCode) { 462 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 463 ulocimp_getRegion, _kCountries, *pErrorCode); 464 } 465 466 /* 467 * TODO separate variant1_variant2_variant3... 468 * by getting each tag's display string and concatenating them with ", " 469 * in between - similar to uloc_getDisplayName() 470 */ 471 U_CAPI int32_t U_EXPORT2 472 uloc_getDisplayVariant(const char *locale, 473 const char *displayLocale, 474 char16_t *dest, int32_t destCapacity, 475 UErrorCode *pErrorCode) { 476 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, 477 ulocimp_getVariant, _kVariants, *pErrorCode); 478 } 479 480 /* Instead of having a separate pass for 'special' patterns, reintegrate the two 481 * so we don't get bitten by preflight bugs again. We can be reasonably efficient 482 * without two separate code paths, this code isn't that performance-critical. 483 * 484 * This code is general enough to deal with patterns that have a prefix or swap the 485 * language and remainder components, since we gave developers enough rope to do such 486 * things if they futz with the pattern data. But since we don't give them a way to 487 * specify a pattern for arbitrary combinations of components, there's not much use in 488 * that. I don't think our data includes such patterns, the only variable I know if is 489 * whether there is a space before the open paren, or not. Oh, and zh uses different 490 * chars than the standard open/close paren (which ja and ko use, btw). 491 */ 492 U_CAPI int32_t U_EXPORT2 493 uloc_getDisplayName(const char *locale, 494 const char *displayLocale, 495 char16_t *dest, int32_t destCapacity, 496 UErrorCode *pErrorCode) 497 { 498 static const char16_t defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */ 499 static const char16_t sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */ 500 static const char16_t sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */ 501 static const int32_t subLen = 3; 502 static const char16_t defaultPattern[10] = { 503 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000 504 }; /* {0} ({1}) */ 505 static const int32_t defaultPatLen = 9; 506 static const int32_t defaultSub0Pos = 0; 507 static const int32_t defaultSub1Pos = 5; 508 509 int32_t length; /* of formatted result */ 510 511 const char16_t *separator; 512 int32_t sepLen = 0; 513 const char16_t *pattern; 514 int32_t patLen = 0; 515 int32_t sub0Pos, sub1Pos; 516 517 char16_t formatOpenParen = 0x0028; // ( 518 char16_t formatReplaceOpenParen = 0x005B; // [ 519 char16_t formatCloseParen = 0x0029; // ) 520 char16_t formatReplaceCloseParen = 0x005D; // ] 521 522 UBool haveLang = true; /* assume true, set false if we find we don't have 523 a lang component in the locale */ 524 UBool haveRest = true; /* assume true, set false if we find we don't have 525 any other component in the locale */ 526 UBool retry = false; /* set true if we need to retry, see below */ 527 528 int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */ 529 530 if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { 531 return 0; 532 } 533 534 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { 535 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 536 return 0; 537 } 538 539 { 540 UErrorCode status = U_ZERO_ERROR; 541 542 icu::LocalUResourceBundlePointer locbundle( 543 ures_open(U_ICUDATA_LANG, displayLocale, &status)); 544 icu::LocalUResourceBundlePointer dspbundle( 545 ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, nullptr, &status)); 546 547 separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status); 548 pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status); 549 } 550 551 /* If we couldn't find any data, then use the defaults */ 552 if(sepLen == 0) { 553 separator = defaultSeparator; 554 } 555 /* #10244: Even though separator is now a pattern, it is awkward to handle it as such 556 * here since we are trying to build the display string in place in the dest buffer, 557 * and to handle it as a pattern would entail having separate storage for the 558 * substrings that need to be combined (the first of which may be the result of 559 * previous such combinations). So for now we continue to treat the portion between 560 * {0} and {1} as a string to be appended when joining substrings, ignoring anything 561 * that is before {0} or after {1} (no existing separator pattern has any such thing). 562 * This is similar to how pattern is handled below. 563 */ 564 { 565 char16_t *p0=u_strstr(separator, sub0); 566 char16_t *p1=u_strstr(separator, sub1); 567 if (p0==nullptr || p1==nullptr || p1<p0) { 568 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 569 return 0; 570 } 571 separator = (const char16_t *)p0 + subLen; 572 sepLen = static_cast<int32_t>(p1 - separator); 573 } 574 575 if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) { 576 pattern=defaultPattern; 577 patLen=defaultPatLen; 578 sub0Pos=defaultSub0Pos; 579 sub1Pos=defaultSub1Pos; 580 // use default formatOpenParen etc. set above 581 } else { /* non-default pattern */ 582 char16_t *p0=u_strstr(pattern, sub0); 583 char16_t *p1=u_strstr(pattern, sub1); 584 if (p0==nullptr || p1==nullptr) { 585 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 586 return 0; 587 } 588 sub0Pos = static_cast<int32_t>(p0-pattern); 589 sub1Pos = static_cast<int32_t>(p1-pattern); 590 if (sub1Pos < sub0Pos) { /* a very odd pattern */ 591 int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t; 592 langi=1; 593 } 594 if (u_strchr(pattern, 0xFF08) != nullptr) { 595 formatOpenParen = 0xFF08; // fullwidth ( 596 formatReplaceOpenParen = 0xFF3B; // fullwidth [ 597 formatCloseParen = 0xFF09; // fullwidth ) 598 formatReplaceCloseParen = 0xFF3D; // fullwidth ] 599 } 600 } 601 602 /* We loop here because there is one case in which after the first pass we could need to 603 * reextract the data. If there's initial padding before the first element, we put in 604 * the padding and then write that element. If it turns out there's no second element, 605 * we didn't need the padding. If we do need the data (no preflight), and the first element 606 * would have fit but for the padding, we need to reextract. In this case (only) we 607 * adjust the parameters so padding is not added, and repeat. 608 */ 609 do { 610 char16_t* p=dest; 611 int32_t patPos=0; /* position in the pattern, used for non-substitution portions */ 612 int32_t langLen=0; /* length of language substitution */ 613 int32_t langPos=0; /* position in output of language substitution */ 614 int32_t restLen=0; /* length of 'everything else' substitution */ 615 int32_t restPos=0; /* position in output of 'everything else' substitution */ 616 icu::LocalUEnumerationPointer kenum; /* keyword enumeration */ 617 618 /* prefix of pattern, extremely likely to be empty */ 619 if(sub0Pos) { 620 if(destCapacity >= sub0Pos) { 621 while (patPos < sub0Pos) { 622 *p++ = pattern[patPos++]; 623 } 624 } else { 625 patPos=sub0Pos; 626 } 627 length=sub0Pos; 628 } else { 629 length=0; 630 } 631 632 for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/ 633 UBool subdone = false; /* set true when ready to move to next substitution */ 634 635 /* prep p and cap for calls to get display components, pin cap to 0 since 636 they complain if cap is negative */ 637 int32_t cap=destCapacity-length; 638 if (cap <= 0) { 639 cap=0; 640 } else { 641 p=dest+length; 642 } 643 644 if (subi == langi) { /* {0}*/ 645 if(haveLang) { 646 langPos=length; 647 langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode); 648 length+=langLen; 649 haveLang=langLen>0; 650 } 651 subdone=true; 652 } else { /* {1} */ 653 if(!haveRest) { 654 subdone=true; 655 } else { 656 int32_t len; /* length of component (plus other stuff) we just fetched */ 657 switch(resti++) { 658 case 0: 659 restPos=length; 660 len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode); 661 break; 662 case 1: 663 len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode); 664 break; 665 case 2: 666 len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode); 667 break; 668 case 3: 669 kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode)); 670 U_FALLTHROUGH; 671 default: { 672 const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode); 673 if (kw == nullptr) { 674 len=0; /* mark that we didn't add a component */ 675 subdone=true; 676 } else { 677 /* incorporating this behavior into the loop made it even more complex, 678 so just special case it here */ 679 len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode); 680 if(len) { 681 if(len < cap) { 682 p[len]=0x3d; /* '=', assume we'll need it */ 683 } 684 len+=1; 685 686 /* adjust for call to get keyword */ 687 cap-=len; 688 if(cap <= 0) { 689 cap=0; 690 } else { 691 p+=len; 692 } 693 } 694 /* reset for call below */ 695 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { 696 *pErrorCode=U_ZERO_ERROR; 697 } 698 int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale, 699 p, cap, pErrorCode); 700 if(len) { 701 if(vlen==0) { 702 --len; /* remove unneeded '=' */ 703 } 704 /* restore cap and p to what they were at start */ 705 cap=destCapacity-length; 706 if(cap <= 0) { 707 cap=0; 708 } else { 709 p=dest+length; 710 } 711 } 712 len+=vlen; /* total we added for key + '=' + value */ 713 } 714 } break; 715 } /* end switch */ 716 717 if (len>0) { 718 /* we added a component, so add separator and write it if there's room. */ 719 if(len+sepLen<=cap) { 720 const char16_t * plimit = p + len; 721 for (; p < plimit; p++) { 722 if (*p == formatOpenParen) { 723 *p = formatReplaceOpenParen; 724 } else if (*p == formatCloseParen) { 725 *p = formatReplaceCloseParen; 726 } 727 } 728 for(int32_t i=0;i<sepLen;++i) { 729 *p++=separator[i]; 730 } 731 } 732 length+=len+sepLen; 733 } else if(subdone) { 734 /* remove separator if we added it */ 735 if (length!=restPos) { 736 length-=sepLen; 737 } 738 restLen=length-restPos; 739 haveRest=restLen>0; 740 } 741 } 742 } 743 744 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { 745 *pErrorCode=U_ZERO_ERROR; 746 } 747 748 if(subdone) { 749 if(haveLang && haveRest) { 750 /* append internal portion of pattern, the first time, 751 or last portion of pattern the second time */ 752 int32_t padLen; 753 patPos+=subLen; 754 padLen=(subi==0 ? sub1Pos : patLen)-patPos; 755 if(length+padLen <= destCapacity) { 756 p=dest+length; 757 for(int32_t i=0;i<padLen;++i) { 758 *p++=pattern[patPos++]; 759 } 760 } else { 761 patPos+=padLen; 762 } 763 length+=padLen; 764 } else if(subi==0) { 765 /* don't have first component, reset for second component */ 766 sub0Pos=0; 767 length=0; 768 } else if(length>0) { 769 /* true length is the length of just the component we got. */ 770 length=haveLang?langLen:restLen; 771 if(dest && sub0Pos!=0) { 772 if (sub0Pos+length<=destCapacity) { 773 /* first component not at start of result, 774 but we have full component in buffer. */ 775 u_memmove(dest, dest+(haveLang?langPos:restPos), length); 776 } else { 777 /* would have fit, but didn't because of pattern prefix. */ 778 sub0Pos=0; /* stops initial padding (and a second retry, 779 so we won't end up here again) */ 780 retry=true; 781 } 782 } 783 } 784 785 ++subi; /* move on to next substitution */ 786 } 787 } 788 } while(retry); 789 790 return u_terminateUChars(dest, destCapacity, length, pErrorCode); 791 } 792 793 U_CAPI int32_t U_EXPORT2 794 uloc_getDisplayKeyword(const char* keyword, 795 const char* displayLocale, 796 char16_t* dest, 797 int32_t destCapacity, 798 UErrorCode* status){ 799 800 /* argument checking */ 801 if(status==nullptr || U_FAILURE(*status)) { 802 return 0; 803 } 804 805 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { 806 *status=U_ILLEGAL_ARGUMENT_ERROR; 807 return 0; 808 } 809 810 811 /* pass itemKey=nullptr to look for a top-level item */ 812 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, 813 _kKeys, nullptr, 814 keyword, 815 keyword, 816 dest, destCapacity, 817 *status); 818 819 } 820 821 822 #define UCURRENCY_DISPLAY_NAME_INDEX 1 823 824 U_CAPI int32_t U_EXPORT2 825 uloc_getDisplayKeywordValue( const char* locale, 826 const char* keyword, 827 const char* displayLocale, 828 char16_t* dest, 829 int32_t destCapacity, 830 UErrorCode* status){ 831 832 833 /* argument checking */ 834 if(status==nullptr || U_FAILURE(*status)) { 835 return 0; 836 } 837 838 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { 839 *status=U_ILLEGAL_ARGUMENT_ERROR; 840 return 0; 841 } 842 843 /* get the keyword value */ 844 CharString keywordValue; 845 if (keyword != nullptr && *keyword != '\0') { 846 keywordValue = ulocimp_getKeywordValue(locale, keyword, *status); 847 } 848 849 /* 850 * if the keyword is equal to currency .. then to get the display name 851 * we need to do the fallback ourselves 852 */ 853 if(uprv_stricmp(keyword, _kCurrency)==0){ 854 855 int32_t dispNameLen = 0; 856 const char16_t *dispName = nullptr; 857 858 icu::LocalUResourceBundlePointer bundle( 859 ures_open(U_ICUDATA_CURR, displayLocale, status)); 860 icu::LocalUResourceBundlePointer currencies( 861 ures_getByKey(bundle.getAlias(), _kCurrencies, nullptr, status)); 862 icu::LocalUResourceBundlePointer currency( 863 ures_getByKeyWithFallback(currencies.getAlias(), keywordValue.data(), nullptr, status)); 864 865 dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status); 866 867 if(U_FAILURE(*status)){ 868 if(*status == U_MISSING_RESOURCE_ERROR){ 869 /* we just want to write the value over if nothing is available */ 870 *status = U_USING_DEFAULT_WARNING; 871 }else{ 872 return 0; 873 } 874 } 875 876 /* now copy the dispName over if not nullptr */ 877 if(dispName != nullptr){ 878 if(dispNameLen <= destCapacity){ 879 u_memcpy(dest, dispName, dispNameLen); 880 return u_terminateUChars(dest, destCapacity, dispNameLen, status); 881 }else{ 882 *status = U_BUFFER_OVERFLOW_ERROR; 883 return dispNameLen; 884 } 885 }else{ 886 /* we have not found the display name for the value .. just copy over */ 887 if(keywordValue.length() <= destCapacity){ 888 u_charsToUChars(keywordValue.data(), dest, keywordValue.length()); 889 return u_terminateUChars(dest, destCapacity, keywordValue.length(), status); 890 }else{ 891 *status = U_BUFFER_OVERFLOW_ERROR; 892 return keywordValue.length(); 893 } 894 } 895 896 897 }else{ 898 899 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, 900 _kTypes, keyword, 901 keywordValue.data(), 902 keywordValue.data(), 903 dest, destCapacity, 904 *status); 905 } 906 }