| Index: source/i18n/locdspnm.cpp
|
| diff --git a/source/i18n/locdspnm.cpp b/source/i18n/locdspnm.cpp
|
| index 30fb17db63db1732cad2f15a95eae39c6f2f4589..65ac4a3d90cd8a08335fd0d3042286dea1cd21e0 100644
|
| --- a/source/i18n/locdspnm.cpp
|
| +++ b/source/i18n/locdspnm.cpp
|
| @@ -1,6 +1,6 @@
|
| /*
|
| *******************************************************************************
|
| -* Copyright (C) 2010-2013, International Business Machines Corporation and
|
| +* Copyright (C) 2010-2014, International Business Machines Corporation and
|
| * others. All Rights Reserved.
|
| *******************************************************************************
|
| */
|
| @@ -12,11 +12,14 @@
|
| #include "unicode/locdspnm.h"
|
| #include "unicode/msgfmt.h"
|
| #include "unicode/ures.h"
|
| +#include "unicode/udisplaycontext.h"
|
| #include "unicode/brkiter.h"
|
|
|
| #include "cmemory.h"
|
| #include "cstring.h"
|
| +#include "mutex.h"
|
| #include "ulocimp.h"
|
| +#include "umutex.h"
|
| #include "ureslocs.h"
|
| #include "uresimp.h"
|
|
|
| @@ -275,10 +278,13 @@ class LocaleDisplayNamesImpl : public LocaleDisplayNames {
|
| MessageFormat *format;
|
| MessageFormat *keyTypeFormat;
|
| UDisplayContext capitalizationContext;
|
| + BreakIterator* capitalizationBrkIter;
|
| + static UMutex capitalizationBrkIterLock;
|
| UnicodeString formatOpenParen;
|
| UnicodeString formatReplaceOpenParen;
|
| UnicodeString formatCloseParen;
|
| UnicodeString formatReplaceCloseParen;
|
| + UDisplayContext nameLength;
|
|
|
| // Constants for capitalization context usage types.
|
| enum CapContextUsage {
|
| @@ -287,13 +293,12 @@ class LocaleDisplayNamesImpl : public LocaleDisplayNames {
|
| kCapContextUsageTerritory,
|
| kCapContextUsageVariant,
|
| kCapContextUsageKey,
|
| - kCapContextUsageType,
|
| + kCapContextUsageKeyValue,
|
| kCapContextUsageCount
|
| };
|
| - // Capitalization transforms. For each usage type, the first array element indicates
|
| - // whether to titlecase for uiListOrMenu context, the second indicates whether to
|
| - // titlecase for stand-alone context.
|
| - UBool fCapitalization[kCapContextUsageCount][2];
|
| + // Capitalization transforms. For each usage type, indicates whether to titlecase for
|
| + // the context specified in capitalizationContext (which we know at construction time)
|
| + UBool fCapitalization[kCapContextUsageCount];
|
|
|
| public:
|
| // constructor
|
| @@ -332,6 +337,8 @@ private:
|
| void initialize(void);
|
| };
|
|
|
| +UMutex LocaleDisplayNamesImpl::capitalizationBrkIterLock = U_MUTEX_INITIALIZER;
|
| +
|
| LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale,
|
| UDialectHandling dialectHandling)
|
| : dialectHandling(dialectHandling)
|
| @@ -341,6 +348,8 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale,
|
| , format(NULL)
|
| , keyTypeFormat(NULL)
|
| , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
|
| + , capitalizationBrkIter(NULL)
|
| + , nameLength(UDISPCTX_LENGTH_FULL)
|
| {
|
| initialize();
|
| }
|
| @@ -354,6 +363,8 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale,
|
| , format(NULL)
|
| , keyTypeFormat(NULL)
|
| , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
|
| + , capitalizationBrkIter(NULL)
|
| + , nameLength(UDISPCTX_LENGTH_FULL)
|
| {
|
| while (length-- > 0) {
|
| UDisplayContext value = *contexts++;
|
| @@ -365,6 +376,9 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale,
|
| case UDISPCTX_TYPE_CAPITALIZATION:
|
| capitalizationContext = value;
|
| break;
|
| + case UDISPCTX_TYPE_DISPLAY_LENGTH:
|
| + nameLength = value;
|
| + break;
|
| default:
|
| break;
|
| }
|
| @@ -422,42 +436,59 @@ LocaleDisplayNamesImpl::initialize(void) {
|
| const ContextUsageNameToEnum contextUsageTypeMap[] = {
|
| // Entries must be sorted by usageTypeName; entry with NULL name terminates list.
|
| { "key", kCapContextUsageKey },
|
| + { "keyValue", kCapContextUsageKeyValue },
|
| { "languages", kCapContextUsageLanguage },
|
| { "script", kCapContextUsageScript },
|
| { "territory", kCapContextUsageTerritory },
|
| - { "type", kCapContextUsageType },
|
| { "variant", kCapContextUsageVariant },
|
| { NULL, (CapContextUsage)0 },
|
| };
|
| - int32_t len = 0;
|
| - UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
|
| - if (U_SUCCESS(status)) {
|
| - UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, "contextTransforms", NULL, &status);
|
| + // Only get the context data if we need it! This is a const object so we know now...
|
| + // Also check whether we will need a break iterator (depends on the data)
|
| + UBool needBrkIter = FALSE;
|
| + if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE) {
|
| + int32_t len = 0;
|
| + UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
|
| if (U_SUCCESS(status)) {
|
| - UResourceBundle *contextTransformUsage;
|
| - while ( (contextTransformUsage = ures_getNextResource(contextTransforms, NULL, &status)) != NULL ) {
|
| - const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status);
|
| - if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
|
| - const char* usageKey = ures_getKey(contextTransformUsage);
|
| - if (usageKey != NULL) {
|
| - const ContextUsageNameToEnum * typeMapPtr = contextUsageTypeMap;
|
| - int32_t compResult = 0;
|
| - // linear search; list is short and we cannot be sure that bsearch is available
|
| - while ( typeMapPtr->usageName != NULL && (compResult = uprv_strcmp(usageKey, typeMapPtr->usageName)) > 0 ) {
|
| - ++typeMapPtr;
|
| - }
|
| - if (typeMapPtr->usageName != NULL && compResult == 0) {
|
| - fCapitalization[typeMapPtr->usageEnum][0] = intVector[0];
|
| - fCapitalization[typeMapPtr->usageEnum][1] = intVector[1];
|
| + UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, "contextTransforms", NULL, &status);
|
| + if (U_SUCCESS(status)) {
|
| + UResourceBundle *contextTransformUsage;
|
| + while ( (contextTransformUsage = ures_getNextResource(contextTransforms, NULL, &status)) != NULL ) {
|
| + const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status);
|
| + if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
|
| + const char* usageKey = ures_getKey(contextTransformUsage);
|
| + if (usageKey != NULL) {
|
| + const ContextUsageNameToEnum * typeMapPtr = contextUsageTypeMap;
|
| + int32_t compResult = 0;
|
| + // linear search; list is short and we cannot be sure that bsearch is available
|
| + while ( typeMapPtr->usageName != NULL && (compResult = uprv_strcmp(usageKey, typeMapPtr->usageName)) > 0 ) {
|
| + ++typeMapPtr;
|
| + }
|
| + if (typeMapPtr->usageName != NULL && compResult == 0) {
|
| + int32_t titlecaseInt = (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU)? intVector[0]: intVector[1];
|
| + if (titlecaseInt != 0) {
|
| + fCapitalization[typeMapPtr->usageEnum] = TRUE;;
|
| + needBrkIter = TRUE;
|
| + }
|
| + }
|
| }
|
| }
|
| + status = U_ZERO_ERROR;
|
| + ures_close(contextTransformUsage);
|
| }
|
| - status = U_ZERO_ERROR;
|
| - ures_close(contextTransformUsage);
|
| + ures_close(contextTransforms);
|
| }
|
| - ures_close(contextTransforms);
|
| + ures_close(localeBundle);
|
| + }
|
| + }
|
| + // Get a sentence break iterator if we will need it
|
| + if (needBrkIter || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
|
| + status = U_ZERO_ERROR;
|
| + capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status);
|
| + if (U_FAILURE(status)) {
|
| + delete capitalizationBrkIter;
|
| + capitalizationBrkIter = NULL;
|
| }
|
| - ures_close(localeBundle);
|
| }
|
| #endif
|
| }
|
| @@ -466,6 +497,7 @@ LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() {
|
| delete separatorFormat;
|
| delete format;
|
| delete keyTypeFormat;
|
| + delete capitalizationBrkIter;
|
| }
|
|
|
| const Locale&
|
| @@ -485,6 +517,8 @@ LocaleDisplayNamesImpl::getContext(UDisplayContextType type) const {
|
| return (UDisplayContext)dialectHandling;
|
| case UDISPCTX_TYPE_CAPITALIZATION:
|
| return capitalizationContext;
|
| + case UDISPCTX_TYPE_DISPLAY_LENGTH:
|
| + return nameLength;
|
| default:
|
| break;
|
| }
|
| @@ -496,51 +530,11 @@ LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage,
|
| UnicodeString& result) const {
|
| #if !UCONFIG_NO_BREAK_ITERATION
|
| // check to see whether we need to titlecase result
|
| - UBool titlecase = FALSE;
|
| - switch (capitalizationContext) {
|
| - case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
|
| - titlecase = TRUE;
|
| - break;
|
| - case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
|
| - titlecase = fCapitalization[usage][0];
|
| - break;
|
| - case UDISPCTX_CAPITALIZATION_FOR_STANDALONE:
|
| - titlecase = fCapitalization[usage][1];
|
| - break;
|
| - default:
|
| - // titlecase = FALSE;
|
| - break;
|
| - }
|
| - if (titlecase) {
|
| - // TODO: Fix this titlecase hack when we figure out something better to do.
|
| - // We don't want to titlecase the whole text, only something like the first word,
|
| - // of the first segment long enough to have a complete cluster, whichever is
|
| - // shorter. We could have keep a word break iterator around, but I am not sure
|
| - // that will do the ight thing for the purposes here. For now we assume that in
|
| - // languages for which titlecasing makes a difference, we can stop at non-letter
|
| - // characters in 0x0000-0x00FF and only titlecase up to the first occurrence of
|
| - // any of those, or to a small number of chars, whichever comes first.
|
| - int32_t stopPos, stopPosLimit = 8, len = result.length();
|
| - if ( stopPosLimit > len ) {
|
| - stopPosLimit = len;
|
| - }
|
| - for ( stopPos = 0; stopPos < stopPosLimit; stopPos++ ) {
|
| - UChar32 ch = result.char32At(stopPos);
|
| - if ( (ch < 0x41) || (ch > 0x5A && ch < 0x61) || (ch > 0x7A && ch < 0xC0) ) {
|
| - break;
|
| - }
|
| - if (ch >= 0x10000) {
|
| - stopPos++;
|
| - }
|
| - }
|
| - if ( stopPos > 0 && stopPos < len ) {
|
| - UnicodeString firstWord(result, 0, stopPos);
|
| - firstWord.toTitle(NULL, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
|
| - result.replaceBetween(0, stopPos, firstWord);
|
| - } else {
|
| - // no stopPos, titlecase the whole text
|
| - result.toTitle(NULL, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
|
| - }
|
| + if ( result.length() > 0 && u_islower(result.char32At(0)) && capitalizationBrkIter!= NULL &&
|
| + ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || fCapitalization[usage] ) ) {
|
| + // note fCapitalization[usage] won't be set unless capitalizationContext is UI_LIST_OR_MENU or STANDALONE
|
| + Mutex lock(&capitalizationBrkIterLock);
|
| + result.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
|
| }
|
| #endif
|
| return result;
|
| @@ -693,6 +687,12 @@ LocaleDisplayNamesImpl::localeDisplayName(const char* localeId,
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::localeIdName(const char* localeId,
|
| UnicodeString& result) const {
|
| + if (nameLength == UDISPCTX_LENGTH_SHORT) {
|
| + langData.getNoFallback("LanguagesShort", localeId, result);
|
| + if (!result.isBogus()) {
|
| + return result;
|
| + }
|
| + }
|
| return langData.getNoFallback("Languages", localeId, result);
|
| }
|
|
|
| @@ -702,6 +702,12 @@ LocaleDisplayNamesImpl::languageDisplayName(const char* lang,
|
| if (uprv_strcmp("root", lang) == 0 || uprv_strchr(lang, '_') != NULL) {
|
| return result = UnicodeString(lang, -1, US_INV);
|
| }
|
| + if (nameLength == UDISPCTX_LENGTH_SHORT) {
|
| + langData.get("LanguagesShort", lang, result);
|
| + if (!result.isBogus()) {
|
| + return adjustForUsageAndContext(kCapContextUsageLanguage, result);
|
| + }
|
| + }
|
| langData.get("Languages", lang, result);
|
| return adjustForUsageAndContext(kCapContextUsageLanguage, result);
|
| }
|
| @@ -709,6 +715,12 @@ LocaleDisplayNamesImpl::languageDisplayName(const char* lang,
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::scriptDisplayName(const char* script,
|
| UnicodeString& result) const {
|
| + if (nameLength == UDISPCTX_LENGTH_SHORT) {
|
| + langData.get("Scripts%short", script, result);
|
| + if (!result.isBogus()) {
|
| + return adjustForUsageAndContext(kCapContextUsageScript, result);
|
| + }
|
| + }
|
| langData.get("Scripts", script, result);
|
| return adjustForUsageAndContext(kCapContextUsageScript, result);
|
| }
|
| @@ -716,14 +728,18 @@ LocaleDisplayNamesImpl::scriptDisplayName(const char* script,
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode,
|
| UnicodeString& result) const {
|
| - const char* name = uscript_getName(scriptCode);
|
| - langData.get("Scripts", name, result);
|
| - return adjustForUsageAndContext(kCapContextUsageScript, result);
|
| + return scriptDisplayName(uscript_getName(scriptCode), result);
|
| }
|
|
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::regionDisplayName(const char* region,
|
| UnicodeString& result) const {
|
| + if (nameLength == UDISPCTX_LENGTH_SHORT) {
|
| + regionData.get("CountriesShort", region, result);
|
| + if (!result.isBogus()) {
|
| + return adjustForUsageAndContext(kCapContextUsageTerritory, result);
|
| + }
|
| + }
|
| regionData.get("Countries", region, result);
|
| return adjustForUsageAndContext(kCapContextUsageTerritory, result);
|
| }
|
| @@ -731,6 +747,7 @@ LocaleDisplayNamesImpl::regionDisplayName(const char* region,
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::variantDisplayName(const char* variant,
|
| UnicodeString& result) const {
|
| + // don't have a resource for short variant names
|
| langData.get("Variants", variant, result);
|
| return adjustForUsageAndContext(kCapContextUsageVariant, result);
|
| }
|
| @@ -738,6 +755,7 @@ LocaleDisplayNamesImpl::variantDisplayName(const char* variant,
|
| UnicodeString&
|
| LocaleDisplayNamesImpl::keyDisplayName(const char* key,
|
| UnicodeString& result) const {
|
| + // don't have a resource for short key names
|
| langData.get("Keys", key, result);
|
| return adjustForUsageAndContext(kCapContextUsageKey, result);
|
| }
|
| @@ -746,8 +764,14 @@ UnicodeString&
|
| LocaleDisplayNamesImpl::keyValueDisplayName(const char* key,
|
| const char* value,
|
| UnicodeString& result) const {
|
| + if (nameLength == UDISPCTX_LENGTH_SHORT) {
|
| + langData.get("Types%short", key, value, result);
|
| + if (!result.isBogus()) {
|
| + return adjustForUsageAndContext(kCapContextUsageKeyValue, result);
|
| + }
|
| + }
|
| langData.get("Types", key, value, result);
|
| - return adjustForUsageAndContext(kCapContextUsageType, result);
|
| + return adjustForUsageAndContext(kCapContextUsageKeyValue, result);
|
| }
|
|
|
| ////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|