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); |
} |
//////////////////////////////////////////////////////////////////////////////////////////////////// |