locdspnm.cpp (41456B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2010-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 10 #include "unicode/utypes.h" 11 12 #if !UCONFIG_NO_FORMATTING 13 14 #include "unicode/locdspnm.h" 15 #include "unicode/simpleformatter.h" 16 #include "unicode/ucasemap.h" 17 #include "unicode/ures.h" 18 #include "unicode/udisplaycontext.h" 19 #include "unicode/brkiter.h" 20 #include "unicode/ucurr.h" 21 #include "bytesinkutil.h" 22 #include "charstr.h" 23 #include "cmemory.h" 24 #include "cstring.h" 25 #include "mutex.h" 26 #include "uassert.h" 27 #include "ulocimp.h" 28 #include "umutex.h" 29 #include "ureslocs.h" 30 #include "uresimp.h" 31 32 U_NAMESPACE_BEGIN 33 34 //////////////////////////////////////////////////////////////////////////////////////////////////// 35 36 // Access resource data for locale components. 37 // Wrap code in uloc.c for now. 38 class ICUDataTable { 39 const char* const path; 40 Locale locale; 41 42 public: 43 // Note: path should be a pointer to a statically allocated string. 44 ICUDataTable(const char* path, const Locale& locale); 45 ~ICUDataTable() = default; 46 47 const Locale& getLocale(); 48 49 UnicodeString& get(const char* tableKey, const char* itemKey, 50 UnicodeString& result) const; 51 UnicodeString& get(const char* tableKey, const char* subTableKey, const char* itemKey, 52 UnicodeString& result) const; 53 54 UnicodeString& getNoFallback(const char* tableKey, const char* itemKey, 55 UnicodeString &result) const; 56 UnicodeString& getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, 57 UnicodeString &result) const; 58 }; 59 60 inline UnicodeString & 61 ICUDataTable::get(const char* tableKey, const char* itemKey, UnicodeString& result) const { 62 return get(tableKey, nullptr, itemKey, result); 63 } 64 65 inline UnicodeString & 66 ICUDataTable::getNoFallback(const char* tableKey, const char* itemKey, UnicodeString& result) const { 67 return getNoFallback(tableKey, nullptr, itemKey, result); 68 } 69 70 ICUDataTable::ICUDataTable(const char* path, const Locale& locale) 71 : path(path), locale(locale) 72 { 73 U_ASSERT(path != nullptr); 74 } 75 76 const Locale& 77 ICUDataTable::getLocale() { 78 return locale; 79 } 80 81 UnicodeString & 82 ICUDataTable::get(const char* tableKey, const char* subTableKey, const char* itemKey, 83 UnicodeString &result) const { 84 UErrorCode status = U_ZERO_ERROR; 85 int32_t len = 0; 86 87 const char16_t *s = uloc_getTableStringWithFallback(path, locale.getName(), 88 tableKey, subTableKey, itemKey, 89 &len, &status); 90 if (U_SUCCESS(status) && len > 0) { 91 return result.setTo(s, len); 92 } 93 return result.setTo(UnicodeString(itemKey, -1, US_INV)); 94 } 95 96 UnicodeString & 97 ICUDataTable::getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, 98 UnicodeString& result) const { 99 UErrorCode status = U_ZERO_ERROR; 100 int32_t len = 0; 101 102 const char16_t *s = uloc_getTableStringWithFallback(path, locale.getName(), 103 tableKey, subTableKey, itemKey, 104 &len, &status); 105 if (U_SUCCESS(status)) { 106 return result.setTo(s, len); 107 } 108 109 result.setToBogus(); 110 return result; 111 } 112 113 //////////////////////////////////////////////////////////////////////////////////////////////////// 114 115 LocaleDisplayNames::~LocaleDisplayNames() {} 116 117 //////////////////////////////////////////////////////////////////////////////////////////////////// 118 119 #if 0 // currently unused 120 121 class DefaultLocaleDisplayNames : public LocaleDisplayNames { 122 UDialectHandling dialectHandling; 123 124 public: 125 // constructor 126 DefaultLocaleDisplayNames(UDialectHandling dialectHandling); 127 128 virtual ~DefaultLocaleDisplayNames(); 129 130 virtual const Locale& getLocale() const; 131 virtual UDialectHandling getDialectHandling() const; 132 133 virtual UnicodeString& localeDisplayName(const Locale& locale, 134 UnicodeString& result) const; 135 virtual UnicodeString& localeDisplayName(const char* localeId, 136 UnicodeString& result) const; 137 virtual UnicodeString& languageDisplayName(const char* lang, 138 UnicodeString& result) const; 139 virtual UnicodeString& scriptDisplayName(const char* script, 140 UnicodeString& result) const; 141 virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, 142 UnicodeString& result) const; 143 virtual UnicodeString& regionDisplayName(const char* region, 144 UnicodeString& result) const; 145 virtual UnicodeString& variantDisplayName(const char* variant, 146 UnicodeString& result) const; 147 virtual UnicodeString& keyDisplayName(const char* key, 148 UnicodeString& result) const; 149 virtual UnicodeString& keyValueDisplayName(const char* key, 150 const char* value, 151 UnicodeString& result) const; 152 }; 153 154 DefaultLocaleDisplayNames::DefaultLocaleDisplayNames(UDialectHandling dialectHandling) 155 : dialectHandling(dialectHandling) { 156 } 157 158 DefaultLocaleDisplayNames::~DefaultLocaleDisplayNames() { 159 } 160 161 const Locale& 162 DefaultLocaleDisplayNames::getLocale() const { 163 return Locale::getRoot(); 164 } 165 166 UDialectHandling 167 DefaultLocaleDisplayNames::getDialectHandling() const { 168 return dialectHandling; 169 } 170 171 UnicodeString& 172 DefaultLocaleDisplayNames::localeDisplayName(const Locale& locale, 173 UnicodeString& result) const { 174 return result = UnicodeString(locale.getName(), -1, US_INV); 175 } 176 177 UnicodeString& 178 DefaultLocaleDisplayNames::localeDisplayName(const char* localeId, 179 UnicodeString& result) const { 180 return result = UnicodeString(localeId, -1, US_INV); 181 } 182 183 UnicodeString& 184 DefaultLocaleDisplayNames::languageDisplayName(const char* lang, 185 UnicodeString& result) const { 186 return result = UnicodeString(lang, -1, US_INV); 187 } 188 189 UnicodeString& 190 DefaultLocaleDisplayNames::scriptDisplayName(const char* script, 191 UnicodeString& result) const { 192 return result = UnicodeString(script, -1, US_INV); 193 } 194 195 UnicodeString& 196 DefaultLocaleDisplayNames::scriptDisplayName(UScriptCode scriptCode, 197 UnicodeString& result) const { 198 const char* name = uscript_getName(scriptCode); 199 if (name) { 200 return result = UnicodeString(name, -1, US_INV); 201 } 202 return result.remove(); 203 } 204 205 UnicodeString& 206 DefaultLocaleDisplayNames::regionDisplayName(const char* region, 207 UnicodeString& result) const { 208 return result = UnicodeString(region, -1, US_INV); 209 } 210 211 UnicodeString& 212 DefaultLocaleDisplayNames::variantDisplayName(const char* variant, 213 UnicodeString& result) const { 214 return result = UnicodeString(variant, -1, US_INV); 215 } 216 217 UnicodeString& 218 DefaultLocaleDisplayNames::keyDisplayName(const char* key, 219 UnicodeString& result) const { 220 return result = UnicodeString(key, -1, US_INV); 221 } 222 223 UnicodeString& 224 DefaultLocaleDisplayNames::keyValueDisplayName(const char* /* key */, 225 const char* value, 226 UnicodeString& result) const { 227 return result = UnicodeString(value, -1, US_INV); 228 } 229 230 #endif // currently unused class DefaultLocaleDisplayNames 231 232 //////////////////////////////////////////////////////////////////////////////////////////////////// 233 234 class LocaleDisplayNamesImpl : public LocaleDisplayNames { 235 Locale locale; 236 UDialectHandling dialectHandling; 237 ICUDataTable langData; 238 ICUDataTable regionData; 239 SimpleFormatter separatorFormat; 240 SimpleFormatter format; 241 SimpleFormatter keyTypeFormat; 242 UDisplayContext capitalizationContext; 243 #if !UCONFIG_NO_BREAK_ITERATION 244 BreakIterator* capitalizationBrkIter; 245 #else 246 UObject* capitalizationBrkIter; 247 #endif 248 UnicodeString formatOpenParen; 249 UnicodeString formatReplaceOpenParen; 250 UnicodeString formatCloseParen; 251 UnicodeString formatReplaceCloseParen; 252 UDisplayContext nameLength; 253 UDisplayContext substitute; 254 255 // Constants for capitalization context usage types. 256 enum CapContextUsage { 257 kCapContextUsageLanguage, 258 kCapContextUsageScript, 259 kCapContextUsageTerritory, 260 kCapContextUsageVariant, 261 kCapContextUsageKey, 262 kCapContextUsageKeyValue, 263 kCapContextUsageCount 264 }; 265 // Capitalization transforms. For each usage type, indicates whether to titlecase for 266 // the context specified in capitalizationContext (which we know at construction time) 267 bool fCapitalization[kCapContextUsageCount]; 268 269 public: 270 // constructor 271 LocaleDisplayNamesImpl(const Locale& locale, UDialectHandling dialectHandling); 272 LocaleDisplayNamesImpl(const Locale& locale, UDisplayContext *contexts, int32_t length); 273 virtual ~LocaleDisplayNamesImpl(); 274 275 virtual const Locale& getLocale() const override; 276 virtual UDialectHandling getDialectHandling() const override; 277 virtual UDisplayContext getContext(UDisplayContextType type) const override; 278 279 virtual UnicodeString& localeDisplayName(const Locale& locale, 280 UnicodeString& result) const override; 281 virtual UnicodeString& localeDisplayName(const char* localeId, 282 UnicodeString& result) const override; 283 virtual UnicodeString& languageDisplayName(const char* lang, 284 UnicodeString& result) const override; 285 virtual UnicodeString& scriptDisplayName(const char* script, 286 UnicodeString& result) const override; 287 virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, 288 UnicodeString& result) const override; 289 virtual UnicodeString& regionDisplayName(const char* region, 290 UnicodeString& result) const override; 291 virtual UnicodeString& variantDisplayName(const char* variant, 292 UnicodeString& result) const override; 293 virtual UnicodeString& keyDisplayName(const char* key, 294 UnicodeString& result) const override; 295 virtual UnicodeString& keyValueDisplayName(const char* key, 296 const char* value, 297 UnicodeString& result) const override; 298 private: 299 UnicodeString& localeIdName(const char* localeId, 300 UnicodeString& result, bool substitute) const; 301 UnicodeString& appendWithSep(UnicodeString& buffer, const UnicodeString& src) const; 302 UnicodeString& adjustForUsageAndContext(CapContextUsage usage, UnicodeString& result) const; 303 UnicodeString& scriptDisplayName(const char* script, UnicodeString& result, bool skipAdjust) const; 304 UnicodeString& regionDisplayName(const char* region, UnicodeString& result, bool skipAdjust) const; 305 UnicodeString& variantDisplayName(const char* variant, UnicodeString& result, bool skipAdjust) const; 306 UnicodeString& keyDisplayName(const char* key, UnicodeString& result, bool skipAdjust) const; 307 UnicodeString& keyValueDisplayName(const char* key, const char* value, 308 UnicodeString& result, bool skipAdjust) const; 309 void initialize(); 310 311 struct CapitalizationContextSink; 312 }; 313 314 LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, 315 UDialectHandling dialectHandling) 316 : dialectHandling(dialectHandling) 317 , langData(U_ICUDATA_LANG, locale) 318 , regionData(U_ICUDATA_REGION, locale) 319 , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) 320 , capitalizationBrkIter(nullptr) 321 , nameLength(UDISPCTX_LENGTH_FULL) 322 , substitute(UDISPCTX_SUBSTITUTE) 323 { 324 initialize(); 325 } 326 327 LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, 328 UDisplayContext *contexts, int32_t length) 329 : dialectHandling(ULDN_STANDARD_NAMES) 330 , langData(U_ICUDATA_LANG, locale) 331 , regionData(U_ICUDATA_REGION, locale) 332 , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) 333 , capitalizationBrkIter(nullptr) 334 , nameLength(UDISPCTX_LENGTH_FULL) 335 , substitute(UDISPCTX_SUBSTITUTE) 336 { 337 while (length-- > 0) { 338 UDisplayContext value = *contexts++; 339 UDisplayContextType selector = 340 static_cast<UDisplayContextType>(static_cast<uint32_t>(value) >> 8); 341 switch (selector) { 342 case UDISPCTX_TYPE_DIALECT_HANDLING: 343 dialectHandling = static_cast<UDialectHandling>(value); 344 break; 345 case UDISPCTX_TYPE_CAPITALIZATION: 346 capitalizationContext = value; 347 break; 348 case UDISPCTX_TYPE_DISPLAY_LENGTH: 349 nameLength = value; 350 break; 351 case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: 352 substitute = value; 353 break; 354 default: 355 break; 356 } 357 } 358 initialize(); 359 } 360 361 struct LocaleDisplayNamesImpl::CapitalizationContextSink : public ResourceSink { 362 bool hasCapitalizationUsage; 363 LocaleDisplayNamesImpl& parent; 364 365 CapitalizationContextSink(LocaleDisplayNamesImpl& _parent) 366 : hasCapitalizationUsage(false), parent(_parent) {} 367 virtual ~CapitalizationContextSink(); 368 369 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, 370 UErrorCode &errorCode) override { 371 ResourceTable contexts = value.getTable(errorCode); 372 if (U_FAILURE(errorCode)) { return; } 373 for (int i = 0; contexts.getKeyAndValue(i, key, value); ++i) { 374 375 CapContextUsage usageEnum; 376 if (uprv_strcmp(key, "key") == 0) { 377 usageEnum = kCapContextUsageKey; 378 } else if (uprv_strcmp(key, "keyValue") == 0) { 379 usageEnum = kCapContextUsageKeyValue; 380 } else if (uprv_strcmp(key, "languages") == 0) { 381 usageEnum = kCapContextUsageLanguage; 382 } else if (uprv_strcmp(key, "script") == 0) { 383 usageEnum = kCapContextUsageScript; 384 } else if (uprv_strcmp(key, "territory") == 0) { 385 usageEnum = kCapContextUsageTerritory; 386 } else if (uprv_strcmp(key, "variant") == 0) { 387 usageEnum = kCapContextUsageVariant; 388 } else { 389 continue; 390 } 391 392 int32_t len = 0; 393 const int32_t* intVector = value.getIntVector(len, errorCode); 394 if (U_FAILURE(errorCode)) { return; } 395 if (len < 2) { continue; } 396 397 int32_t titlecaseInt = (parent.capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU) ? intVector[0] : intVector[1]; 398 if (titlecaseInt == 0) { continue; } 399 400 parent.fCapitalization[usageEnum] = true; 401 hasCapitalizationUsage = true; 402 } 403 } 404 }; 405 406 // Virtual destructors must be defined out of line. 407 LocaleDisplayNamesImpl::CapitalizationContextSink::~CapitalizationContextSink() {} 408 409 void 410 LocaleDisplayNamesImpl::initialize() { 411 LocaleDisplayNamesImpl* nonConstThis = this; 412 nonConstThis->locale = langData.getLocale() == Locale::getRoot() 413 ? regionData.getLocale() 414 : langData.getLocale(); 415 416 UnicodeString sep; 417 langData.getNoFallback("localeDisplayPattern", "separator", sep); 418 if (sep.isBogus()) { 419 sep = UnicodeString("{0}, {1}", -1, US_INV); 420 } 421 UErrorCode status = U_ZERO_ERROR; 422 separatorFormat.applyPatternMinMaxArguments(sep, 2, 2, status); 423 424 UnicodeString pattern; 425 langData.getNoFallback("localeDisplayPattern", "pattern", pattern); 426 if (pattern.isBogus()) { 427 pattern = UnicodeString("{0} ({1})", -1, US_INV); 428 } 429 format.applyPatternMinMaxArguments(pattern, 2, 2, status); 430 if (pattern.indexOf(static_cast<char16_t>(0xFF08)) >= 0) { 431 formatOpenParen.setTo(static_cast<char16_t>(0xFF08)); // fullwidth ( 432 formatReplaceOpenParen.setTo(static_cast<char16_t>(0xFF3B)); // fullwidth [ 433 formatCloseParen.setTo(static_cast<char16_t>(0xFF09)); // fullwidth ) 434 formatReplaceCloseParen.setTo(static_cast<char16_t>(0xFF3D)); // fullwidth ] 435 } else { 436 formatOpenParen.setTo(static_cast<char16_t>(0x0028)); // ( 437 formatReplaceOpenParen.setTo(static_cast<char16_t>(0x005B)); // [ 438 formatCloseParen.setTo(static_cast<char16_t>(0x0029)); // ) 439 formatReplaceCloseParen.setTo(static_cast<char16_t>(0x005D)); // ] 440 } 441 442 UnicodeString ktPattern; 443 langData.get("localeDisplayPattern", "keyTypePattern", ktPattern); 444 if (ktPattern.isBogus()) { 445 ktPattern = UnicodeString("{0}={1}", -1, US_INV); 446 } 447 keyTypeFormat.applyPatternMinMaxArguments(ktPattern, 2, 2, status); 448 449 uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); 450 #if !UCONFIG_NO_BREAK_ITERATION 451 // Only get the context data if we need it! This is a const object so we know now... 452 // Also check whether we will need a break iterator (depends on the data) 453 bool needBrkIter = false; 454 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE) { 455 LocalUResourceBundlePointer resource(ures_open(nullptr, locale.getName(), &status)); 456 if (U_FAILURE(status)) { return; } 457 CapitalizationContextSink sink(*this); 458 ures_getAllItemsWithFallback(resource.getAlias(), "contextTransforms", sink, status); 459 if (status == U_MISSING_RESOURCE_ERROR) { 460 // Silently ignore. Not every locale has contextTransforms. 461 status = U_ZERO_ERROR; 462 } else if (U_FAILURE(status)) { 463 return; 464 } 465 needBrkIter = sink.hasCapitalizationUsage; 466 } 467 // Get a sentence break iterator if we will need it 468 if (needBrkIter || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { 469 status = U_ZERO_ERROR; 470 capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); 471 if (U_FAILURE(status)) { 472 delete capitalizationBrkIter; 473 capitalizationBrkIter = nullptr; 474 } 475 } 476 #endif 477 } 478 479 LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() { 480 #if !UCONFIG_NO_BREAK_ITERATION 481 delete capitalizationBrkIter; 482 #endif 483 } 484 485 const Locale& 486 LocaleDisplayNamesImpl::getLocale() const { 487 return locale; 488 } 489 490 UDialectHandling 491 LocaleDisplayNamesImpl::getDialectHandling() const { 492 return dialectHandling; 493 } 494 495 UDisplayContext 496 LocaleDisplayNamesImpl::getContext(UDisplayContextType type) const { 497 switch (type) { 498 case UDISPCTX_TYPE_DIALECT_HANDLING: 499 return static_cast<UDisplayContext>(dialectHandling); 500 case UDISPCTX_TYPE_CAPITALIZATION: 501 return capitalizationContext; 502 case UDISPCTX_TYPE_DISPLAY_LENGTH: 503 return nameLength; 504 case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: 505 return substitute; 506 default: 507 break; 508 } 509 return static_cast<UDisplayContext>(0); 510 } 511 512 UnicodeString& 513 LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage, 514 UnicodeString& result) const { 515 #if !UCONFIG_NO_BREAK_ITERATION 516 // check to see whether we need to titlecase result 517 if ( result.length() > 0 && u_islower(result.char32At(0)) && capitalizationBrkIter!= nullptr && 518 ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || fCapitalization[usage] ) ) { 519 // note fCapitalization[usage] won't be set unless capitalizationContext is UI_LIST_OR_MENU or STANDALONE 520 static UMutex capitalizationBrkIterLock; 521 Mutex lock(&capitalizationBrkIterLock); 522 result.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); 523 } 524 #endif 525 return result; 526 } 527 528 UnicodeString& 529 LocaleDisplayNamesImpl::localeDisplayName(const Locale& loc, 530 UnicodeString& result) const { 531 if (loc.isBogus()) { 532 result.setToBogus(); 533 return result; 534 } 535 UnicodeString resultName; 536 537 const char* lang = loc.getLanguage(); 538 if (uprv_strlen(lang) == 0) { 539 lang = "root"; 540 } 541 const char* script = loc.getScript(); 542 const char* country = loc.getCountry(); 543 const char* variant = loc.getVariant(); 544 545 bool hasScript = uprv_strlen(script) > 0; 546 bool hasCountry = uprv_strlen(country) > 0; 547 bool hasVariant = uprv_strlen(variant) > 0; 548 549 if (dialectHandling == ULDN_DIALECT_NAMES) { 550 UErrorCode status = U_ZERO_ERROR; 551 CharString buffer; 552 do { // loop construct is so we can break early out of search 553 if (hasScript && hasCountry) { 554 buffer.append(lang, status) 555 .append('_', status) 556 .append(script, status) 557 .append('_', status) 558 .append(country, status); 559 if (U_SUCCESS(status)) { 560 localeIdName(buffer.data(), resultName, false); 561 if (!resultName.isBogus()) { 562 hasScript = false; 563 hasCountry = false; 564 break; 565 } 566 } 567 } 568 if (hasScript) { 569 buffer.append(lang, status) 570 .append('_', status) 571 .append(script, status); 572 if (U_SUCCESS(status)) { 573 localeIdName(buffer.data(), resultName, false); 574 if (!resultName.isBogus()) { 575 hasScript = false; 576 break; 577 } 578 } 579 } 580 if (hasCountry) { 581 buffer.append(lang, status) 582 .append('_', status) 583 .append(country, status); 584 if (U_SUCCESS(status)) { 585 localeIdName(buffer.data(), resultName, false); 586 if (!resultName.isBogus()) { 587 hasCountry = false; 588 break; 589 } 590 } 591 } 592 } while (false); 593 } 594 if (resultName.isBogus() || resultName.isEmpty()) { 595 localeIdName(lang, resultName, substitute == UDISPCTX_SUBSTITUTE); 596 if (resultName.isBogus()) { 597 result.setToBogus(); 598 return result; 599 } 600 } 601 602 UnicodeString resultRemainder; 603 UnicodeString temp; 604 UErrorCode status = U_ZERO_ERROR; 605 606 if (hasScript) { 607 UnicodeString script_str = scriptDisplayName(script, temp, true); 608 if (script_str.isBogus()) { 609 result.setToBogus(); 610 return result; 611 } 612 resultRemainder.append(script_str); 613 } 614 if (hasCountry) { 615 UnicodeString region_str = regionDisplayName(country, temp, true); 616 if (region_str.isBogus()) { 617 result.setToBogus(); 618 return result; 619 } 620 appendWithSep(resultRemainder, region_str); 621 } 622 if (hasVariant) { 623 UnicodeString variant_str = variantDisplayName(variant, temp, true); 624 if (variant_str.isBogus()) { 625 result.setToBogus(); 626 return result; 627 } 628 appendWithSep(resultRemainder, variant_str); 629 } 630 resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen); 631 resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen); 632 633 LocalPointer<StringEnumeration> e(loc.createKeywords(status)); 634 if (e.isValid() && U_SUCCESS(status)) { 635 UnicodeString temp2; 636 const char* key; 637 while ((key = e->next((int32_t*)nullptr, status)) != nullptr) { 638 auto value = loc.getKeywordValue<CharString>(key, status); 639 if (U_FAILURE(status)) { 640 return result; 641 } 642 keyDisplayName(key, temp, true); 643 temp.findAndReplace(formatOpenParen, formatReplaceOpenParen); 644 temp.findAndReplace(formatCloseParen, formatReplaceCloseParen); 645 keyValueDisplayName(key, value.data(), temp2, true); 646 temp2.findAndReplace(formatOpenParen, formatReplaceOpenParen); 647 temp2.findAndReplace(formatCloseParen, formatReplaceCloseParen); 648 if (temp2 != UnicodeString(value.data(), -1, US_INV)) { 649 appendWithSep(resultRemainder, temp2); 650 } else if (temp != UnicodeString(key, -1, US_INV)) { 651 UnicodeString temp3; 652 keyTypeFormat.format(temp, temp2, temp3, status); 653 appendWithSep(resultRemainder, temp3); 654 } else { 655 appendWithSep(resultRemainder, temp) 656 .append(static_cast<char16_t>(0x3d) /* = */) 657 .append(temp2); 658 } 659 } 660 } 661 662 if (!resultRemainder.isEmpty()) { 663 format.format(resultName, resultRemainder, result.remove(), status); 664 return adjustForUsageAndContext(kCapContextUsageLanguage, result); 665 } 666 667 result = resultName; 668 return adjustForUsageAndContext(kCapContextUsageLanguage, result); 669 } 670 671 UnicodeString& 672 LocaleDisplayNamesImpl::appendWithSep(UnicodeString& buffer, const UnicodeString& src) const { 673 if (buffer.isEmpty()) { 674 buffer.setTo(src); 675 } else { 676 const UnicodeString *values[2] = { &buffer, &src }; 677 UErrorCode status = U_ZERO_ERROR; 678 separatorFormat.formatAndReplace(values, 2, buffer, nullptr, 0, status); 679 } 680 return buffer; 681 } 682 683 UnicodeString& 684 LocaleDisplayNamesImpl::localeDisplayName(const char* localeId, 685 UnicodeString& result) const { 686 return localeDisplayName(Locale(localeId), result); 687 } 688 689 // private 690 UnicodeString& 691 LocaleDisplayNamesImpl::localeIdName(const char* localeId, 692 UnicodeString& result, bool substitute) const { 693 if (nameLength == UDISPCTX_LENGTH_SHORT) { 694 langData.getNoFallback("Languages%short", localeId, result); 695 if (!result.isBogus()) { 696 return result; 697 } 698 } 699 langData.getNoFallback("Languages", localeId, result); 700 if (result.isBogus() && uprv_strchr(localeId, '_') == nullptr) { 701 // Canonicalize lang and try again, ICU-20870 702 // (only for language codes without script or region) 703 Locale canonLocale = Locale::createCanonical(localeId); 704 const char* canonLocId = canonLocale.getName(); 705 if (nameLength == UDISPCTX_LENGTH_SHORT) { 706 langData.getNoFallback("Languages%short", canonLocId, result); 707 if (!result.isBogus()) { 708 return result; 709 } 710 } 711 langData.getNoFallback("Languages", canonLocId, result); 712 } 713 if (result.isBogus() && substitute) { 714 // use key, this is what langData.get (with fallback) falls back to. 715 result.setTo(UnicodeString(localeId, -1, US_INV)); // use key ( 716 } 717 return result; 718 } 719 720 UnicodeString& 721 LocaleDisplayNamesImpl::languageDisplayName(const char* lang, 722 UnicodeString& result) const { 723 if (uprv_strcmp("root", lang) == 0 || uprv_strchr(lang, '_') != nullptr) { 724 return result = UnicodeString(lang, -1, US_INV); 725 } 726 if (nameLength == UDISPCTX_LENGTH_SHORT) { 727 langData.getNoFallback("Languages%short", lang, result); 728 if (!result.isBogus()) { 729 return adjustForUsageAndContext(kCapContextUsageLanguage, result); 730 } 731 } 732 langData.getNoFallback("Languages", lang, result); 733 if (result.isBogus()) { 734 // Canonicalize lang and try again, ICU-20870 735 Locale canonLocale = Locale::createCanonical(lang); 736 const char* canonLocId = canonLocale.getName(); 737 if (nameLength == UDISPCTX_LENGTH_SHORT) { 738 langData.getNoFallback("Languages%short", canonLocId, result); 739 if (!result.isBogus()) { 740 return adjustForUsageAndContext(kCapContextUsageLanguage, result); 741 } 742 } 743 langData.getNoFallback("Languages", canonLocId, result); 744 } 745 if (result.isBogus() && substitute == UDISPCTX_SUBSTITUTE) { 746 // use key, this is what langData.get (with fallback) falls back to. 747 result.setTo(UnicodeString(lang, -1, US_INV)); // use key ( 748 } 749 return adjustForUsageAndContext(kCapContextUsageLanguage, result); 750 } 751 752 UnicodeString& 753 LocaleDisplayNamesImpl::scriptDisplayName(const char* script, 754 UnicodeString& result, 755 bool skipAdjust) const { 756 if (nameLength == UDISPCTX_LENGTH_SHORT) { 757 langData.getNoFallback("Scripts%short", script, result); 758 if (!result.isBogus()) { 759 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); 760 } 761 } 762 if (substitute == UDISPCTX_SUBSTITUTE) { 763 langData.get("Scripts", script, result); 764 } else { 765 langData.getNoFallback("Scripts", script, result); 766 } 767 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); 768 } 769 770 UnicodeString& 771 LocaleDisplayNamesImpl::scriptDisplayName(const char* script, 772 UnicodeString& result) const { 773 return scriptDisplayName(script, result, false); 774 } 775 776 UnicodeString& 777 LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode, 778 UnicodeString& result) const { 779 return scriptDisplayName(uscript_getName(scriptCode), result, false); 780 } 781 782 UnicodeString& 783 LocaleDisplayNamesImpl::regionDisplayName(const char* region, 784 UnicodeString& result, 785 bool skipAdjust) const { 786 if (nameLength == UDISPCTX_LENGTH_SHORT) { 787 regionData.getNoFallback("Countries%short", region, result); 788 if (!result.isBogus()) { 789 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); 790 } 791 } 792 if (substitute == UDISPCTX_SUBSTITUTE) { 793 regionData.get("Countries", region, result); 794 } else { 795 regionData.getNoFallback("Countries", region, result); 796 } 797 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); 798 } 799 800 UnicodeString& 801 LocaleDisplayNamesImpl::regionDisplayName(const char* region, 802 UnicodeString& result) const { 803 return regionDisplayName(region, result, false); 804 } 805 806 807 UnicodeString& 808 LocaleDisplayNamesImpl::variantDisplayName(const char* variant, 809 UnicodeString& result, 810 bool skipAdjust) const { 811 // don't have a resource for short variant names 812 if (substitute == UDISPCTX_SUBSTITUTE) { 813 langData.get("Variants", variant, result); 814 } else { 815 langData.getNoFallback("Variants", variant, result); 816 } 817 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageVariant, result); 818 } 819 820 UnicodeString& 821 LocaleDisplayNamesImpl::variantDisplayName(const char* variant, 822 UnicodeString& result) const { 823 return variantDisplayName(variant, result, false); 824 } 825 826 UnicodeString& 827 LocaleDisplayNamesImpl::keyDisplayName(const char* key, 828 UnicodeString& result, 829 bool skipAdjust) const { 830 // don't have a resource for short key names 831 if (substitute == UDISPCTX_SUBSTITUTE) { 832 langData.get("Keys", key, result); 833 } else { 834 langData.getNoFallback("Keys", key, result); 835 } 836 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKey, result); 837 } 838 839 UnicodeString& 840 LocaleDisplayNamesImpl::keyDisplayName(const char* key, 841 UnicodeString& result) const { 842 return keyDisplayName(key, result, false); 843 } 844 845 UnicodeString& 846 LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, 847 const char* value, 848 UnicodeString& result, 849 bool skipAdjust) const { 850 if (uprv_strcmp(key, "currency") == 0) { 851 // ICU4C does not have ICU4J CurrencyDisplayInfo equivalent for now. 852 UErrorCode sts = U_ZERO_ERROR; 853 UnicodeString ustrValue(value, -1, US_INV); 854 int32_t len; 855 const char16_t *currencyName = ucurr_getName(ustrValue.getTerminatedBuffer(), 856 locale.getBaseName(), UCURR_LONG_NAME, nullptr /* isChoiceFormat */, &len, &sts); 857 if (U_FAILURE(sts)) { 858 // Return the value as is on failure 859 result = ustrValue; 860 return result; 861 } 862 result.setTo(currencyName, len); 863 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); 864 } 865 866 if (nameLength == UDISPCTX_LENGTH_SHORT) { 867 langData.getNoFallback("Types%short", key, value, result); 868 if (!result.isBogus()) { 869 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); 870 } 871 } 872 if (substitute == UDISPCTX_SUBSTITUTE) { 873 langData.get("Types", key, value, result); 874 } else { 875 langData.getNoFallback("Types", key, value, result); 876 } 877 return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); 878 } 879 880 UnicodeString& 881 LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, 882 const char* value, 883 UnicodeString& result) const { 884 return keyValueDisplayName(key, value, result, false); 885 } 886 887 //////////////////////////////////////////////////////////////////////////////////////////////////// 888 889 LocaleDisplayNames* 890 LocaleDisplayNames::createInstance(const Locale& locale, 891 UDialectHandling dialectHandling) { 892 return new LocaleDisplayNamesImpl(locale, dialectHandling); 893 } 894 895 LocaleDisplayNames* 896 LocaleDisplayNames::createInstance(const Locale& locale, 897 UDisplayContext *contexts, int32_t length) { 898 if (contexts == nullptr) { 899 length = 0; 900 } 901 return new LocaleDisplayNamesImpl(locale, contexts, length); 902 } 903 904 U_NAMESPACE_END 905 906 //////////////////////////////////////////////////////////////////////////////////////////////////// 907 908 U_NAMESPACE_USE 909 910 U_CAPI ULocaleDisplayNames * U_EXPORT2 911 uldn_open(const char * locale, 912 UDialectHandling dialectHandling, 913 UErrorCode *pErrorCode) { 914 if (U_FAILURE(*pErrorCode)) { 915 return nullptr; 916 } 917 if (locale == nullptr) { 918 locale = uloc_getDefault(); 919 } 920 return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), dialectHandling); 921 } 922 923 U_CAPI ULocaleDisplayNames * U_EXPORT2 924 uldn_openForContext(const char * locale, 925 UDisplayContext *contexts, int32_t length, 926 UErrorCode *pErrorCode) { 927 if (U_FAILURE(*pErrorCode)) { 928 return nullptr; 929 } 930 if (locale == nullptr) { 931 locale = uloc_getDefault(); 932 } 933 return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), contexts, length); 934 } 935 936 937 U_CAPI void U_EXPORT2 938 uldn_close(ULocaleDisplayNames *ldn) { 939 delete (LocaleDisplayNames *)ldn; 940 } 941 942 U_CAPI const char * U_EXPORT2 943 uldn_getLocale(const ULocaleDisplayNames *ldn) { 944 if (ldn) { 945 return ((const LocaleDisplayNames *)ldn)->getLocale().getName(); 946 } 947 return nullptr; 948 } 949 950 U_CAPI UDialectHandling U_EXPORT2 951 uldn_getDialectHandling(const ULocaleDisplayNames *ldn) { 952 if (ldn) { 953 return ((const LocaleDisplayNames *)ldn)->getDialectHandling(); 954 } 955 return ULDN_STANDARD_NAMES; 956 } 957 958 U_CAPI UDisplayContext U_EXPORT2 959 uldn_getContext(const ULocaleDisplayNames *ldn, 960 UDisplayContextType type, 961 UErrorCode *pErrorCode) { 962 if (U_FAILURE(*pErrorCode)) { 963 return (UDisplayContext)0; 964 } 965 return ((const LocaleDisplayNames *)ldn)->getContext(type); 966 } 967 968 U_CAPI int32_t U_EXPORT2 969 uldn_localeDisplayName(const ULocaleDisplayNames *ldn, 970 const char *locale, 971 char16_t *result, 972 int32_t maxResultSize, 973 UErrorCode *pErrorCode) { 974 if (U_FAILURE(*pErrorCode)) { 975 return 0; 976 } 977 if (ldn == nullptr || locale == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 978 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 979 return 0; 980 } 981 UnicodeString temp(result, 0, maxResultSize); 982 ((const LocaleDisplayNames *)ldn)->localeDisplayName(locale, temp); 983 if (temp.isBogus()) { 984 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 985 return 0; 986 } 987 return temp.extract(result, maxResultSize, *pErrorCode); 988 } 989 990 U_CAPI int32_t U_EXPORT2 991 uldn_languageDisplayName(const ULocaleDisplayNames *ldn, 992 const char *lang, 993 char16_t *result, 994 int32_t maxResultSize, 995 UErrorCode *pErrorCode) { 996 if (U_FAILURE(*pErrorCode)) { 997 return 0; 998 } 999 if (ldn == nullptr || lang == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 1000 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1001 return 0; 1002 } 1003 UnicodeString temp(result, 0, maxResultSize); 1004 ((const LocaleDisplayNames *)ldn)->languageDisplayName(lang, temp); 1005 return temp.extract(result, maxResultSize, *pErrorCode); 1006 } 1007 1008 U_CAPI int32_t U_EXPORT2 1009 uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, 1010 const char *script, 1011 char16_t *result, 1012 int32_t maxResultSize, 1013 UErrorCode *pErrorCode) { 1014 if (U_FAILURE(*pErrorCode)) { 1015 return 0; 1016 } 1017 if (ldn == nullptr || script == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 1018 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1019 return 0; 1020 } 1021 UnicodeString temp(result, 0, maxResultSize); 1022 ((const LocaleDisplayNames *)ldn)->scriptDisplayName(script, temp); 1023 return temp.extract(result, maxResultSize, *pErrorCode); 1024 } 1025 1026 U_CAPI int32_t U_EXPORT2 1027 uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, 1028 UScriptCode scriptCode, 1029 char16_t *result, 1030 int32_t maxResultSize, 1031 UErrorCode *pErrorCode) { 1032 return uldn_scriptDisplayName(ldn, uscript_getName(scriptCode), result, maxResultSize, pErrorCode); 1033 } 1034 1035 U_CAPI int32_t U_EXPORT2 1036 uldn_regionDisplayName(const ULocaleDisplayNames *ldn, 1037 const char *region, 1038 char16_t *result, 1039 int32_t maxResultSize, 1040 UErrorCode *pErrorCode) { 1041 if (U_FAILURE(*pErrorCode)) { 1042 return 0; 1043 } 1044 if (ldn == nullptr || region == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 1045 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1046 return 0; 1047 } 1048 UnicodeString temp(result, 0, maxResultSize); 1049 ((const LocaleDisplayNames *)ldn)->regionDisplayName(region, temp); 1050 return temp.extract(result, maxResultSize, *pErrorCode); 1051 } 1052 1053 U_CAPI int32_t U_EXPORT2 1054 uldn_variantDisplayName(const ULocaleDisplayNames *ldn, 1055 const char *variant, 1056 char16_t *result, 1057 int32_t maxResultSize, 1058 UErrorCode *pErrorCode) { 1059 if (U_FAILURE(*pErrorCode)) { 1060 return 0; 1061 } 1062 if (ldn == nullptr || variant == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 1063 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1064 return 0; 1065 } 1066 UnicodeString temp(result, 0, maxResultSize); 1067 ((const LocaleDisplayNames *)ldn)->variantDisplayName(variant, temp); 1068 return temp.extract(result, maxResultSize, *pErrorCode); 1069 } 1070 1071 U_CAPI int32_t U_EXPORT2 1072 uldn_keyDisplayName(const ULocaleDisplayNames *ldn, 1073 const char *key, 1074 char16_t *result, 1075 int32_t maxResultSize, 1076 UErrorCode *pErrorCode) { 1077 if (U_FAILURE(*pErrorCode)) { 1078 return 0; 1079 } 1080 if (ldn == nullptr || key == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { 1081 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1082 return 0; 1083 } 1084 UnicodeString temp(result, 0, maxResultSize); 1085 ((const LocaleDisplayNames *)ldn)->keyDisplayName(key, temp); 1086 return temp.extract(result, maxResultSize, *pErrorCode); 1087 } 1088 1089 U_CAPI int32_t U_EXPORT2 1090 uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, 1091 const char *key, 1092 const char *value, 1093 char16_t *result, 1094 int32_t maxResultSize, 1095 UErrorCode *pErrorCode) { 1096 if (U_FAILURE(*pErrorCode)) { 1097 return 0; 1098 } 1099 if (ldn == nullptr || key == nullptr || value == nullptr || (result == nullptr && maxResultSize > 0) 1100 || maxResultSize < 0) { 1101 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; 1102 return 0; 1103 } 1104 UnicodeString temp(result, 0, maxResultSize); 1105 ((const LocaleDisplayNames *)ldn)->keyValueDisplayName(key, value, temp); 1106 return temp.extract(result, maxResultSize, *pErrorCode); 1107 } 1108 1109 #endif