#define DEBUG_UALOC 0
#if DEBUG_UALOC
#include <stdio.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "unicode/utypes.h"
#include "unicode/ualoc.h"
#include "unicode/uloc.h"
#include "unicode/ures.h"
#include "unicode/putil.h"
#include "unicode/ustring.h"
#include "cstring.h"
#include "cmemory.h"
#include "uhash.h"
#include "umutex.h"
#include "ucln_cmn.h"
#include "putilimp.h"
#include "uresimp.h"
static double doubleFromIntF(int32_t intF) {
double coefficient = (double)(intF % 1000000);
int32_t exponent = (intF / 1000000) - 55;
return coefficient * uprv_pow10(exponent);
}
static int compareLangEntries(const void * entry1, const void * entry2) {
double fraction1 = ((const UALanguageEntry *)entry1)->userFraction;
double fraction2 = ((const UALanguageEntry *)entry2)->userFraction;
if (fraction1 > fraction2) return -1;
if (fraction1 < fraction2) return 1;
return uprv_strcmp(((const UALanguageEntry *)entry1)->languageCode,((const UALanguageEntry *)entry2)->languageCode);
}
static const char * langToDefaultScript[] = {
"az", "az_Latn",
"bm", "bm_Latn", "bs", "bs_Latn",
"byn", "byn_Ethi", "cu", "cu_Cyrl", "ff", "ff_Latn", "ha", "ha_Latn", "iu", "iu_Cans",
"kk", "kk_Cyrl", "ks", "ks_Arab", "ku", "ku_Latn",
"ky", "ky_Cyrl",
"mn", "mn_Cyrl",
"ms", "ms_Latn",
"pa", "pa_Guru",
"rif", "rif_Tfng", "sd", "sd_Arab", "shi", "shi_Tfng",
"sr", "sr_Cyrl",
"tg", "tg_Cyrl",
"tk", "tk_Latn", "ug", "ug_Arab",
"uz", "uz_Latn",
"vai", "vai_Vaii",
"yue", "yue_Hant", "zh", "zh_Hans",
NULL
};
static const char * langCodeWithScriptIfAmbig(const char * langCode) {
const char ** langToDefScriptPtr = langToDefaultScript;
const char * testCurLoc;
while ( (testCurLoc = *langToDefScriptPtr++) != NULL ) {
int cmp = uprv_strcmp(langCode, testCurLoc);
if (cmp <= 0) {
if (cmp == 0) {
return *langToDefScriptPtr;
}
break;
}
langToDefScriptPtr++;
}
return langCode;
}
static const UChar ustrLangStatusDefacto[] = {0x64,0x65,0x5F,0x66,0x61,0x63,0x74,0x6F,0x5F,0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; static const UChar ustrLangStatusOfficial[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; static const UChar ustrLangStatusRegional[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0x5F,0x72,0x65,0x67,0x69,0x6F,0x6E,0x61,0x6C,0};
enum {
kLocalLangEntriesMax = 26, kLangEntriesFactor = 3 };
U_CAPI int32_t U_EXPORT2
ualoc_getLanguagesForRegion(const char *regionID, double minimumFraction,
UALanguageEntry *entries, int32_t entriesCapacity,
UErrorCode *err)
{
if (U_FAILURE(*err)) {
return 0;
}
if ( regionID == NULL || minimumFraction < 0.0 || minimumFraction > 1.0 ||
((entries==NULL)? entriesCapacity!=0: entriesCapacity<0) ) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", err);
rb = ures_getByKey(rb, "territoryInfo", rb, err);
rb = ures_getByKey(rb, regionID, rb, err);
if (U_FAILURE(*err)) {
ures_close(rb);
return 0;
}
int32_t entryCount = 0;
UResourceBundle *langBund = NULL;
int32_t lbIdx, lbCount = ures_getSize(rb);
UALanguageEntry localLangEntries[kLocalLangEntriesMax];
UALanguageEntry * langEntries = localLangEntries;
int32_t langEntriesMax = kLocalLangEntriesMax;
for (lbIdx = 0; lbIdx < lbCount; lbIdx++) {
langBund = ures_getByIndex(rb, lbIdx, langBund, err);
if (U_FAILURE(*err)) {
break;
}
const char * langCode = ures_getKey(langBund);
if (uprv_strcmp(langCode,"territoryF") == 0) {
continue;
}
if (strnlen(langCode, UALANGDATA_CODELEN+1) > UALANGDATA_CODELEN) { continue; }
UErrorCode localErr = U_ZERO_ERROR;
double userFraction = 0.0;
UResourceBundle *itemBund = ures_getByKey(langBund, "populationShareF", NULL, &localErr);
if (U_SUCCESS(localErr)) {
int32_t intF = ures_getInt(itemBund, &localErr);
if (U_SUCCESS(localErr)) {
userFraction = doubleFromIntF(intF);
}
ures_close(itemBund);
}
if (userFraction < minimumFraction) {
continue;
}
if (entries != NULL) {
localErr = U_ZERO_ERROR;
UALanguageStatus langStatus = UALANGSTATUS_UNSPECIFIED;
int32_t ulen;
const UChar * ustrLangStatus = ures_getStringByKey(langBund, "officialStatus", &ulen, &localErr);
if (U_SUCCESS(localErr)) {
int32_t cmp = u_strcmp(ustrLangStatus, ustrLangStatusOfficial);
if (cmp == 0) {
langStatus = UALANGSTATUS_OFFICIAL;
} else if (cmp < 0 && u_strcmp(ustrLangStatus, ustrLangStatusDefacto) == 0) {
langStatus = UALANGSTATUS_DEFACTO_OFFICIAL;
} else if (u_strcmp(ustrLangStatus, ustrLangStatusRegional) == 0) {
langStatus = UALANGSTATUS_REGIONAL_OFFICIAL;
}
}
if (entryCount >= langEntriesMax) {
int32_t newMax = langEntriesMax * kLangEntriesFactor;
if (langEntries == localLangEntries) {
langEntries = (UALanguageEntry*)uprv_malloc(newMax*sizeof(UALanguageEntry));
if (langEntries == NULL) {
*err = U_MEMORY_ALLOCATION_ERROR;
break;
}
uprv_memcpy(langEntries, localLangEntries, entryCount*sizeof(UALanguageEntry));
} else {
langEntries = (UALanguageEntry*)uprv_realloc(langEntries, newMax*sizeof(UALanguageEntry));
if (langEntries == NULL) {
*err = U_MEMORY_ALLOCATION_ERROR;
break;
}
}
langEntriesMax = newMax;
}
uprv_strcpy(langEntries[entryCount].languageCode, langCodeWithScriptIfAmbig(langCode));
langEntries[entryCount].userFraction = userFraction;
langEntries[entryCount].status = langStatus;
}
entryCount++;
}
ures_close(langBund);
ures_close(rb);
if (U_FAILURE(*err)) {
if (langEntries != localLangEntries) {
free(langEntries);
}
return 0;
}
if (entries != NULL) {
qsort(langEntries, entryCount, sizeof(UALanguageEntry), compareLangEntries);
if (entryCount > entriesCapacity) {
entryCount = entriesCapacity;
}
uprv_memcpy(entries, langEntries, entryCount*sizeof(UALanguageEntry));
if (langEntries != localLangEntries) {
free(langEntries);
}
}
return entryCount;
}
static const char * forceParent[] = { "en_150", "en_GB", "en_AU", "en_GB",
"en_BD", "en_GB", "en_BE", "en_150", "en_DG", "en_GB",
"en_FK", "en_GB",
"en_GG", "en_GB",
"en_GI", "en_GB",
"en_HK", "en_GB", "en_IE", "en_GB",
"en_IM", "en_GB",
"en_IN", "en_GB",
"en_IO", "en_GB",
"en_JE", "en_GB",
"en_JM", "en_GB",
"en_LK", "en_GB",
"en_MO", "en_GB",
"en_MT", "en_GB",
"en_MV", "en_GB", "en_MY", "en_GB", "en_NZ", "en_AU",
"en_PK", "en_GB", "en_SG", "en_GB",
"en_SH", "en_GB",
"en_VG", "en_GB",
"yue", "yue_CN", "yue_CN", "root", "yue_HK", "root", "yue_Hans","yue_CN",
"yue_Hant","yue_HK",
"zh", "zh_CN",
"zh_CN", "root",
"zh_Hant", "zh_TW",
"zh_TW", "root",
NULL
};
enum { kLocBaseNameMax = 16 };
U_CAPI int32_t U_EXPORT2
ualoc_getAppleParent(const char* localeID,
char * parent,
int32_t parentCapacity,
UErrorCode* err)
{
UResourceBundle *rb;
int32_t len;
UErrorCode tempStatus;
char locbuf[ULOC_FULLNAME_CAPACITY+1];
char * foundDoubleUnderscore;
if (U_FAILURE(*err)) {
return 0;
}
if ( (parent==NULL)? parentCapacity!=0: parentCapacity<0 ) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
len = uloc_getBaseName(localeID, locbuf, ULOC_FULLNAME_CAPACITY, err);
if (U_FAILURE(*err)) {
return 0;
}
if (*err == U_STRING_NOT_TERMINATED_WARNING) {
locbuf[ULOC_FULLNAME_CAPACITY] = 0;
*err = U_ZERO_ERROR;
}
foundDoubleUnderscore = uprv_strstr(locbuf, "__");
if (foundDoubleUnderscore != NULL) {
*foundDoubleUnderscore = 0;
len = uprv_strlen(locbuf);
}
if (len >= 2 && (uprv_strncmp(locbuf, "en", 2) == 0 || uprv_strncmp(locbuf, "zh", 2) == 0)) {
const char ** forceParentPtr = forceParent;
const char * testCurLoc;
while ( (testCurLoc = *forceParentPtr++) != NULL ) {
int cmp = uprv_strcmp(locbuf, testCurLoc);
if (cmp <= 0) {
if (cmp == 0) {
len = uprv_strlen(*forceParentPtr);
if (len < parentCapacity) {
uprv_strcpy(parent, *forceParentPtr);
} else {
*err = U_BUFFER_OVERFLOW_ERROR;
}
return len;
}
break;
}
forceParentPtr++;
}
}
tempStatus = U_ZERO_ERROR;
rb = ures_openDirect(NULL, locbuf, &tempStatus);
if (U_SUCCESS(tempStatus)) {
const char * actualLocale = ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &tempStatus);
ures_close(rb);
if (U_SUCCESS(tempStatus) && uprv_strcmp(locbuf, actualLocale) != 0) {
len = uprv_strlen(actualLocale);
if (len < parentCapacity) {
uprv_strcpy(parent, actualLocale);
} else {
*err = U_BUFFER_OVERFLOW_ERROR;
}
return len;
}
}
tempStatus = U_ZERO_ERROR;
rb = ures_openDirect(NULL, "supplementalData", &tempStatus);
rb = ures_getByKey(rb, "parentLocales", rb, &tempStatus);
if (U_SUCCESS(tempStatus)) {
UResourceBundle * parentMapBundle = NULL;
int32_t childLen = 0;
while (childLen == 0) {
tempStatus = U_ZERO_ERROR;
parentMapBundle = ures_getNextResource(rb, parentMapBundle, &tempStatus);
if (U_FAILURE(tempStatus)) {
break; }
char childName[kLocBaseNameMax + 1];
childName[kLocBaseNameMax] = 0;
const char * childPtr = NULL;
if (ures_getType(parentMapBundle) == URES_STRING) {
childLen = kLocBaseNameMax;
childPtr = ures_getUTF8String(parentMapBundle, childName, &childLen, FALSE, &tempStatus);
if (U_FAILURE(tempStatus) || uprv_strncmp(locbuf, childPtr, kLocBaseNameMax) != 0) {
childLen = 0;
}
} else { int32_t childCur, childCount = ures_getSize(parentMapBundle);
for (childCur = 0; childCur < childCount && childLen == 0; childCur++) {
tempStatus = U_ZERO_ERROR;
childLen = kLocBaseNameMax;
childPtr = ures_getUTF8StringByIndex(parentMapBundle, childCur, childName, &childLen, FALSE, &tempStatus);
if (U_FAILURE(tempStatus) || uprv_strncmp(locbuf, childPtr, kLocBaseNameMax) != 0) {
childLen = 0;
}
}
}
}
ures_close(rb);
if (childLen > 0) {
const char * keyStr = ures_getKey(parentMapBundle);
len = uprv_strlen(keyStr);
if (len < parentCapacity) {
uprv_strcpy(parent, keyStr);
} else {
*err = U_BUFFER_OVERFLOW_ERROR;
}
ures_close(parentMapBundle);
return len;
}
ures_close(parentMapBundle);
}
len = uloc_getParent(locbuf, parent, parentCapacity, err);
if (U_SUCCESS(*err) && len == 0) {
len = 4;
if (len < parentCapacity) {
uprv_strcpy(parent, "root");
} else {
*err = U_BUFFER_OVERFLOW_ERROR;
}
}
return len;
}
static const char * appleAliasMap[][2] = {
{ "arabic", "ar" }, { "chinese", "zh_Hans" }, { "danish", "da" }, { "dutch", "nl" }, { "english", "en" }, { "finnish", "fi" }, { "french", "fr" }, { "german", "de" }, { "italian", "it" }, { "japanese", "ja" }, { "korean", "ko" }, { "no_NO", "nb_NO" }, { "norwegian", "nb" }, { "polish", "pl" }, { "portuguese", "pt" }, { "russian", "ru" }, { "spanish", "es" }, { "swedish", "sv" }, { "thai", "th" }, { "turkish", "tr" }, };
enum { kAppleAliasMapCount = UPRV_LENGTHOF(appleAliasMap) };
static const char * appleParentMap[][2] = {
{ "ars", "ar" }, { "en_150", "en_GB" }, { "en_AG", "en_GB" }, { "en_AI", "en_GB" }, { "en_AU", "en_GB" }, { "en_BB", "en_GB" }, { "en_BD", "en_GB" }, { "en_BM", "en_GB" }, { "en_BN", "en_GB" }, { "en_BS", "en_GB" }, { "en_BW", "en_GB" }, { "en_BZ", "en_GB" }, { "en_CC", "en_AU" }, { "en_CK", "en_AU" }, { "en_CX", "en_AU" }, { "en_CY", "en_150" }, { "en_DG", "en_GB" },
{ "en_DM", "en_GB" }, { "en_FJ", "en_GB" }, { "en_FK", "en_GB" },
{ "en_GB", "en_001" }, { "en_GD", "en_GB" }, { "en_GG", "en_GB" },
{ "en_GH", "en_GB" }, { "en_GI", "en_GB" },
{ "en_GM", "en_GB" }, { "en_GY", "en_GB" }, { "en_HK", "en_GB" }, { "en_IE", "en_GB" },
{ "en_IM", "en_GB" },
{ "en_IN", "en_GB" }, { "en_IO", "en_GB" },
{ "en_JE", "en_GB" },
{ "en_JM", "en_GB" },
{ "en_KE", "en_GB" }, { "en_KI", "en_GB" }, { "en_KN", "en_GB" }, { "en_KY", "en_GB" }, { "en_LC", "en_GB" }, { "en_LK", "en_GB" }, { "en_LS", "en_GB" }, { "en_MO", "en_GB" },
{ "en_MS", "en_GB" }, { "en_MT", "en_GB" },
{ "en_MU", "en_GB" }, { "en_MV", "en_GB" },
{ "en_MW", "en_GB" }, { "en_MY", "en_GB" }, { "en_NA", "en_GB" }, { "en_NF", "en_AU" }, { "en_NG", "en_GB" }, { "en_NR", "en_AU" }, { "en_NU", "en_AU" }, { "en_NZ", "en_AU" },
{ "en_PG", "en_AU" }, { "en_PK", "en_GB" }, { "en_PN", "en_GB" }, { "en_SB", "en_GB" }, { "en_SC", "en_GB" }, { "en_SD", "en_GB" }, { "en_SG", "en_GB" },
{ "en_SH", "en_GB" },
{ "en_SL", "en_GB" }, { "en_SS", "en_GB" }, { "en_SZ", "en_GB" }, { "en_TC", "en_GB" }, { "en_TO", "en_GB" }, { "en_TT", "en_GB" }, { "en_TV", "en_GB" }, { "en_TZ", "en_GB" }, { "en_UG", "en_GB" }, { "en_VC", "en_GB" }, { "en_VG", "en_GB" },
{ "en_VU", "en_GB" }, { "en_WS", "en_AU" }, { "en_ZA", "en_GB" }, { "en_ZM", "en_GB" }, { "en_ZW", "en_GB" }, { "es_MX", "es_419" }, { "wuu", "wuu_Hans"}, { "wuu_Hans", "zh_Hans" }, { "wuu_Hant", "zh_Hant" }, { "yue", "yue_Hant"},
{ "yue_Hans", "zh_Hans" }, { "yue_Hant", "zh_Hant" }, { "zh_Hant", "root" }, };
enum { kAppleParentMapCount = UPRV_LENGTHOF(appleParentMap) };
U_CDECL_BEGIN
static UBool U_CALLCONV ualocale_cleanup(void);
U_CDECL_END
U_NAMESPACE_BEGIN
static UInitOnce gUALocaleCacheInitOnce = U_INITONCE_INITIALIZER;
static int gMapDataState = 0; static UResourceBundle* gLanguageAliasesBundle = NULL;
U_NAMESPACE_END
U_CDECL_BEGIN
static UBool U_CALLCONV ualocale_cleanup(void)
{
U_NAMESPACE_USE
if (gMapDataState > 0) {
ures_close(gLanguageAliasesBundle);
gLanguageAliasesBundle = NULL;
}
gMapDataState = 0;
gUALocaleCacheInitOnce.reset();
return TRUE;
}
static void initializeMapData() {
U_NAMESPACE_USE
ucln_common_registerCleanup(UCLN_COMMON_LOCALE, ualocale_cleanup);
UResourceBundle * curBundle;
UErrorCode status = U_ZERO_ERROR;
curBundle = ures_openDirect(NULL, "metadata", &status);
curBundle = ures_getByKey(curBundle, "alias", curBundle, &status);
curBundle = ures_getByKey(curBundle, "language", curBundle, &status);
if (U_FAILURE(status)) {
gMapDataState = -1; return;
}
gLanguageAliasesBundle = curBundle; #if DEBUG_UALOC
printf("# metadata/alias/language size %d\n", ures_getSize(curBundle));
#endif
gMapDataState = 1;
}
U_CDECL_END
static int compareAppleMapElements(const void *key, const void *entry) {
return uprv_strcmp((const char *)key, ((const char **)entry)[0]);
}
static void ualoc_normalize(const char *locale, char *normalized, int32_t normalizedCapacity, UErrorCode *status)
{
if (U_FAILURE(*status)) {
return;
}
const char *replacement = locale; int32_t len;
const char** entry = (const char**)bsearch(locale, appleAliasMap, kAppleAliasMapCount, sizeof(appleAliasMap[0]), compareAppleMapElements);
if (entry != NULL) {
replacement = entry[1];
} else if (icu::gMapDataState > 0) {
UErrorCode localStatus = U_ZERO_ERROR;
UResourceBundle * aliasMapBundle = ures_getByKey(icu::gLanguageAliasesBundle, locale, NULL, &localStatus);
if (U_SUCCESS(localStatus) && aliasMapBundle != NULL) {
len = normalizedCapacity;
ures_getUTF8StringByKey(aliasMapBundle, "replacement", normalized, &len, TRUE, status);
if (U_SUCCESS(*status) && len >= normalizedCapacity) {
*status = U_BUFFER_OVERFLOW_ERROR; }
ures_close(aliasMapBundle);
return;
}
}
len = strnlen(replacement, normalizedCapacity);
if (len < normalizedCapacity) { uprv_strcpy(normalized, replacement);
} else {
*status = U_BUFFER_OVERFLOW_ERROR;
}
}
static void ualoc_getParent(const char *locale, char *parent, int32_t parentCapacity, UErrorCode *status)
{
if (U_FAILURE(*status)) {
return;
}
int32_t len;
const char** entry = (const char**)bsearch(locale, appleParentMap, kAppleParentMapCount, sizeof(appleParentMap[0]), compareAppleMapElements);
if (entry != NULL) {
const char* replacement = entry[1];
len = uprv_strlen(replacement);
if (len < parentCapacity) { uprv_strcpy(parent, replacement);
} else {
*status = U_BUFFER_OVERFLOW_ERROR;
}
return;
}
len = ures_getLocParent(locale, parent, parentCapacity - 1, status);
if (len > 0 || U_FAILURE(*status)) {
parent[parentCapacity - 1] = 0; return;
}
uloc_getParent(locale, parent, parentCapacity - 1, status);
parent[parentCapacity - 1] = 0; }
enum { kLangScriptRegMaxLen = ULOC_LANG_CAPACITY + ULOC_SCRIPT_CAPACITY + ULOC_COUNTRY_CAPACITY };
const int32_t kMaxLocaleIDLength = 58; const int32_t kMaxParentChainLength = 7;
const int32_t kCharStorageBlockSize = 650;
struct LocIDCharStorage {
char chars[kCharStorageBlockSize];
char* curTop;
char* limit;
LocIDCharStorage* nextBlock;
LocIDCharStorage() : chars(), curTop(chars), limit(curTop + kCharStorageBlockSize), nextBlock(NULL) {}
~LocIDCharStorage() { delete nextBlock; }
char* nextPtr() {
if (nextBlock == NULL) {
if (limit - curTop > kMaxLocaleIDLength) {
return curTop;
} else {
nextBlock = new LocIDCharStorage();
}
}
return nextBlock->nextPtr();
}
void advance(int32_t charsUsed) {
if (nextBlock == NULL) {
curTop += charsUsed;
*curTop++ = '\0'; } else {
nextBlock->advance(charsUsed);
}
}
};
struct LocaleIDInfo {
const char* original; const char* base; const char* normalized; const char* language; const char* languageGroup; const char* parentChain[kMaxParentChainLength];
LocaleIDInfo();
void initBaseNames(const char* originalID, LocIDCharStorage& charStorage, UErrorCode* err);
void calcParentChain(LocIDCharStorage& charStorage, UBool penalizeNonDefaultCountry, UErrorCode* err);
UBool specifiesCountry();
#if DEBUG_UALOC
void dump(const char *originalID, LocIDCharStorage& charStorage, UBool penalizeNonDefaultCountry, UErrorCode *err);
#endif
};
LocaleIDInfo::LocaleIDInfo() {
original = NULL;
parentChain[0] = NULL;
}
void LocaleIDInfo::initBaseNames(const char *originalID, LocIDCharStorage& charStorage, UErrorCode *err) {
if (original == NULL) {
original = originalID;
base = charStorage.nextPtr();
int32_t length = uloc_getBaseName(original, const_cast<char*>(base), kMaxLocaleIDLength, err);
charStorage.advance(length);
normalized = charStorage.nextPtr();
ualoc_normalize(base, const_cast<char*>(normalized), kMaxLocaleIDLength, err);
charStorage.advance(uprv_strlen(normalized));
language = charStorage.nextPtr();
length = uloc_getLanguage(normalized, const_cast<char*>(language), kMaxLocaleIDLength, err);
charStorage.advance(length);
languageGroup = language;
static const char* likeLanguages[] = {
"ars", "ar",
"no", "nb",
"wuu", "zh",
"yue", "zh"
};
for (int32_t i = 0; i < UPRV_LENGTHOF(likeLanguages); i += 2) {
if (uprv_strcmp(language, likeLanguages[i]) == 0) {
languageGroup = likeLanguages[i + 1];
break;
}
}
}
}
void LocaleIDInfo::calcParentChain(LocIDCharStorage& charStorage, UBool penalizeNonDefaultCountry, UErrorCode *err) {
if (parentChain[0] != NULL) {
return;
}
int32_t index = 0;
parentChain[index] = normalized;
if (penalizeNonDefaultCountry) {
UErrorCode dummyErr = U_ZERO_ERROR;
if (uloc_getCountry(normalized, NULL, 0, &dummyErr) > 0) {
if (uprv_strcmp(normalized, "es_MX") != 0 && uprv_strcmp(normalized, "zh_Hant_TW") != 0) {
dummyErr = U_ZERO_ERROR;
char minimizedLocale[kLocBaseNameMax];
uloc_minimizeSubtags(normalized, minimizedLocale, kLocBaseNameMax, &dummyErr);
if (uloc_getCountry(minimizedLocale, NULL, 0, &dummyErr)) {
parentChain[++index] = normalized;
}
}
}
}
while (index < kMaxParentChainLength && parentChain[index] != NULL) {
char* textPtr = charStorage.nextPtr();
ualoc_getParent(parentChain[index], textPtr, kMaxLocaleIDLength, err);
++index;
if (textPtr[0] == '\0' || uprv_strcmp(textPtr, "root") == 0) {
parentChain[index] = NULL;
} else {
parentChain[index] = textPtr;
charStorage.advance(uprv_strlen(textPtr));
}
}
}
UBool LocaleIDInfo::specifiesCountry() {
UErrorCode err = U_ZERO_ERROR;
int32_t countryLength = uloc_getCountry(normalized, NULL, 0, &err);
return countryLength != 0;
}
#if DEBUG_UALOC
void LocaleIDInfo::dump(const char *originalID, LocIDCharStorage& charStorage, UBool penalizeNonDefaultCountry, UErrorCode *err) {
initBaseNames(originalID, charStorage, err);
calcParentChain(charStorage, penalizeNonDefaultCountry, err);
printf("[ %s -> %s -> %s ]", original, base, normalized);
for (int32_t i = 1; parentChain[i] != NULL; i++) {
printf(" -> %s", parentChain[i]);
}
printf("\n");
}
#endif // DEBUG_UALOC
int32_t
ualoc_localizationsToUse( const char* const *preferredLanguages,
int32_t preferredLanguagesCount,
const char* const *availableLocalizations,
int32_t availableLocalizationsCount,
const char* *localizationsToUse,
int32_t localizationsToUseCapacity,
UErrorCode *status )
{
if (U_FAILURE(*status)) {
return -1;
}
if (preferredLanguages == NULL || availableLocalizations == NULL || localizationsToUse == NULL) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return -1;
}
if (icu::gMapDataState == 0) {
umtx_initOnce(icu::gUALocaleCacheInitOnce, initializeMapData);
}
#if DEBUG_UALOC
printf("--------------------------------------------------------------------------------\n");
printf("Preferred languages: ");
for (int32_t i = 0; i < preferredLanguagesCount; i++) {
printf("%s ", preferredLanguages[i]);
}
printf("\nAvailable localizations: ");
for (int32_t i = 0; i < availableLocalizationsCount; i++) {
printf("%s ", availableLocalizations[i]);
}
printf("\n\n");
#endif // DEBUG_UALOC
LocaleIDInfo prefLangInfos[preferredLanguagesCount];
LocaleIDInfo availLocInfos[availableLocalizationsCount];
LocIDCharStorage charStorage;
LocaleIDInfo* result = NULL;
LocaleIDInfo* portugueseResult = NULL;
int32_t resultScore = 999;
#if DEBUG_UALOC
for (int32_t i = 0; i < preferredLanguagesCount; i++) {
prefLangInfos[i].dump(preferredLanguages[i], charStorage, FALSE, status);
}
printf("\n");
for (int32_t i = 0; i < availableLocalizationsCount; i++) {
availLocInfos[i].dump(availableLocalizations[i], charStorage, TRUE, status);
}
printf("\n");
#endif // DEBUG_UALOC
for (int32_t prefLangIndex = 0; result == NULL && prefLangIndex < preferredLanguagesCount; ++prefLangIndex) {
LocaleIDInfo* prefLangInfo = &prefLangInfos[prefLangIndex];
prefLangInfo->initBaseNames(preferredLanguages[prefLangIndex], charStorage, status);
for (int32_t availLocIndex = 0; availLocIndex < availableLocalizationsCount; ++availLocIndex) {
LocaleIDInfo* availLocInfo = &availLocInfos[availLocIndex];
availLocInfo->initBaseNames(availableLocalizations[availLocIndex], charStorage, status);
if (resultScore > -1 && uprv_strcmp(prefLangInfo->base, availLocInfo->base) == 0) {
result = availLocInfo;
resultScore = -1;
} else if (resultScore > 0 && uprv_strcmp(prefLangInfo->normalized, availLocInfo->normalized) == 0) {
result = availLocInfo;
resultScore = 0;
} else if (resultScore > 0 && uprv_strcmp(prefLangInfo->languageGroup, availLocInfo->languageGroup) == 0) {
prefLangInfo->calcParentChain(charStorage, FALSE, status);
availLocInfo->calcParentChain(charStorage, TRUE, status);
if (U_SUCCESS(*status)) {
for (int32_t prefLangParentIndex = 0; prefLangInfo->parentChain[prefLangParentIndex] != NULL; ++prefLangParentIndex) {
for (int32_t availLocParentIndex = 0; availLocInfo->parentChain[availLocParentIndex] != NULL; ++availLocParentIndex) {
if (uprv_strcmp(prefLangInfo->parentChain[prefLangParentIndex], availLocInfo->parentChain[availLocParentIndex]) == 0) {
if (uprv_strcmp(prefLangInfo->normalized, "pt_PT") == 0 && uprv_strcmp(availLocInfo->normalized, "pt_BR") == 0) {
portugueseResult = availLocInfo;
} else {
int32_t score = prefLangParentIndex + availLocParentIndex;
if (uprv_strcmp(prefLangInfo->language, availLocInfo->language) != 0) {
++score;
}
if (score < resultScore) {
resultScore = score;
result = availLocInfo;
}
}
}
}
}
}
}
}
}
if (result != NULL && resultScore > 0 && result->specifiesCountry() && uprv_strcmp(result->language, "zh") != 0) {
for (int32_t prefLangIndex = 0; prefLangIndex < preferredLanguagesCount; ++prefLangIndex) {
LocaleIDInfo* prefLangInfo = &prefLangInfos[prefLangIndex];
prefLangInfo->initBaseNames(preferredLanguages[prefLangIndex], charStorage, status);
if (uprv_strcmp(prefLangInfo->language, result->language) == 0) {
for (int32_t availLocIndex = 0; availLocIndex < availableLocalizationsCount; ++availLocIndex) {
LocaleIDInfo* availLocInfo = &availLocInfos[availLocIndex];
if (uprv_strcmp(prefLangInfo->base, availLocInfo->base) == 0 || uprv_strcmp(prefLangInfo->normalized, availLocInfo->normalized) == 0) {
result = &availLocInfos[availLocIndex];
break;
}
}
}
}
}
int32_t locsToUseCount = 0;
if (result == NULL && portugueseResult != NULL) {
result = portugueseResult;
}
if (result != NULL) {
localizationsToUse[locsToUseCount++] = result->original;
result->calcParentChain(charStorage, TRUE, status);
for (int32_t parentChainIndex = 0; result->parentChain[parentChainIndex] != NULL; ++parentChainIndex) {
if (parentChainIndex > 0 && result->parentChain[parentChainIndex - 1] == result->parentChain[parentChainIndex]) {
continue;
}
for (int32_t availLocIndex = 0; availLocIndex < availableLocalizationsCount; ++availLocIndex) {
LocaleIDInfo* availLocInfo = &availLocInfos[availLocIndex];
if (result->original == availLocInfo->original) {
continue;
} else if (locsToUseCount < localizationsToUseCapacity && uprv_strcmp(result->parentChain[parentChainIndex], "zh_Hant_HK") == 0 && uprv_strcmp(availLocInfo->normalized, "zh_Hant_TW") == 0) {
localizationsToUse[locsToUseCount++] = availLocInfo->original;
} else if (locsToUseCount < localizationsToUseCapacity && uprv_strcmp(result->parentChain[parentChainIndex], availLocInfo->normalized) == 0) {
localizationsToUse[locsToUseCount++] = availLocInfo->original;
}
}
}
}
if (locsToUseCount == 0) {
int32_t zxxPos = -1;
for (int32_t i = 0; i < availableLocalizationsCount; i++) {
if (uprv_strcmp(availableLocalizations[i], "zxx") == 0) {
zxxPos = i;
break;
}
}
if (zxxPos >= 0) {
localizationsToUse[locsToUseCount++] = availableLocalizations[zxxPos];
}
}
#if DEBUG_UALOC
printf("Localizations to use: ");
for (int32_t i = 0; i < locsToUseCount; i++) {
printf("%s ", localizationsToUse[i]);
}
printf("\n\n");
#endif // DEBUG_UALOC
return locsToUseCount;
}