2 *****************************************************************************************
3 * Copyright (C) 2014-2017 Apple Inc. All Rights Reserved.
4 *****************************************************************************************
13 #include "unicode/utypes.h"
14 #include "unicode/ualoc.h"
15 #include "unicode/uloc.h"
16 #include "unicode/ures.h"
17 #include "unicode/putil.h"
18 #include "unicode/ustring.h"
24 // the following has replacements for some math.h funcs etc
28 // The numeric values in territoryInfo are in "IntF" format from LDML2ICUConverter.
29 // From its docs (adapted): [IntF is] a special integer that represents the number in
30 // normalized scientific notation.
31 // Resultant integers are in the form -?xxyyyyyy, where xx is the exponent
32 // offset by 50 and yyyyyy is the coefficient to 5 decimal places (range 1.0 to 9.99999), e.g.
33 // 14660000000000 -> 1.46600E13 -> 63146600
34 // 0.0001 -> 1.00000E-4 -> 46100000
35 // -123.456 -> -1.23456E-2 -> -48123456
37 // Here to avoid an extra division we have the max coefficient as 999999 (instead of
38 // 9.99999) and instead offset the exponent by -55.
40 static double doubleFromIntF(int32_t intF
) {
41 double coefficient
= (double)(intF
% 1000000);
42 int32_t exponent
= (intF
/ 1000000) - 55;
43 return coefficient
* uprv_pow10(exponent
);
46 static int compareLangEntries(const void * entry1
, const void * entry2
) {
47 double fraction1
= ((const UALanguageEntry
*)entry1
)->userFraction
;
48 double fraction2
= ((const UALanguageEntry
*)entry2
)->userFraction
;
49 // want descending order
50 if (fraction1
> fraction2
) return -1;
51 if (fraction1
< fraction2
) return 1;
52 // userFractions the same, sort by languageCode
53 return uprv_strcmp(((const UALanguageEntry
*)entry1
)->languageCode
,((const UALanguageEntry
*)entry2
)->languageCode
);
56 // language codes to version with default script
57 // must be sorted by language code
58 static const char * langToDefaultScript
[] = {
82 static const char * langCodeWithScriptIfAmbig(const char * langCode
) {
83 const char ** langToDefScriptPtr
= langToDefaultScript
;
84 const char * testCurLoc
;
85 while ( (testCurLoc
= *langToDefScriptPtr
++) != NULL
) {
86 int cmp
= uprv_strcmp(langCode
, testCurLoc
);
89 return *langToDefScriptPtr
;
98 static const UChar ustrLangStatusDefacto
[] = {0x64,0x65,0x5F,0x66,0x61,0x63,0x74,0x6F,0x5F,0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"de_facto_official"
99 static const UChar ustrLangStatusOfficial
[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"official"
100 static const UChar ustrLangStatusRegional
[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0x5F,0x72,0x65,0x67,0x69,0x6F,0x6E,0x61,0x6C,0}; //"official_regional"
103 kLocalLangEntriesMax
= 26, // enough for most regions to minimumFraction 0.001 except India
104 kLangEntriesFactor
= 3 // if we have to allocate, multiply existing size by this
107 U_CAPI
int32_t U_EXPORT2
108 ualoc_getLanguagesForRegion(const char *regionID
, double minimumFraction
,
109 UALanguageEntry
*entries
, int32_t entriesCapacity
,
112 if (U_FAILURE(*err
)) {
115 if ( regionID
== NULL
|| minimumFraction
< 0.0 || minimumFraction
> 1.0 ||
116 ((entries
==NULL
)? entriesCapacity
!=0: entriesCapacity
<0) ) {
117 *err
= U_ILLEGAL_ARGUMENT_ERROR
;
120 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", err
);
121 rb
= ures_getByKey(rb
, "territoryInfo", rb
, err
);
122 rb
= ures_getByKey(rb
, regionID
, rb
, err
);
123 if (U_FAILURE(*err
)) {
128 int32_t entryCount
= 0;
129 UResourceBundle
*langBund
= NULL
;
130 int32_t lbIdx
, lbCount
= ures_getSize(rb
);
131 UALanguageEntry localLangEntries
[kLocalLangEntriesMax
];
132 UALanguageEntry
* langEntries
= localLangEntries
;
133 int32_t langEntriesMax
= kLocalLangEntriesMax
;
135 for (lbIdx
= 0; lbIdx
< lbCount
; lbIdx
++) {
136 langBund
= ures_getByIndex(rb
, lbIdx
, langBund
, err
);
137 if (U_FAILURE(*err
)) {
140 const char * langCode
= ures_getKey(langBund
);
141 if (uprv_strcmp(langCode
,"territoryF") == 0) {
144 if (strnlen(langCode
, UALANGDATA_CODELEN
+1) > UALANGDATA_CODELEN
) { // no uprv_strnlen
145 continue; // a code we cannot handle
148 UErrorCode localErr
= U_ZERO_ERROR
;
149 double userFraction
= 0.0;
150 UResourceBundle
*itemBund
= ures_getByKey(langBund
, "populationShareF", NULL
, &localErr
);
151 if (U_SUCCESS(localErr
)) {
152 int32_t intF
= ures_getInt(itemBund
, &localErr
);
153 if (U_SUCCESS(localErr
)) {
154 userFraction
= doubleFromIntF(intF
);
156 ures_close(itemBund
);
158 if (userFraction
< minimumFraction
) {
161 if (entries
!= NULL
) {
162 localErr
= U_ZERO_ERROR
;
163 UALanguageStatus langStatus
= UALANGSTATUS_UNSPECIFIED
;
165 const UChar
* ustrLangStatus
= ures_getStringByKey(langBund
, "officialStatus", &ulen
, &localErr
);
166 if (U_SUCCESS(localErr
)) {
167 int32_t cmp
= u_strcmp(ustrLangStatus
, ustrLangStatusOfficial
);
169 langStatus
= UALANGSTATUS_OFFICIAL
;
170 } else if (cmp
< 0 && u_strcmp(ustrLangStatus
, ustrLangStatusDefacto
) == 0) {
171 langStatus
= UALANGSTATUS_DEFACTO_OFFICIAL
;
172 } else if (u_strcmp(ustrLangStatus
, ustrLangStatusRegional
) == 0) {
173 langStatus
= UALANGSTATUS_REGIONAL_OFFICIAL
;
176 // Now we have all of the info for our next entry
177 if (entryCount
>= langEntriesMax
) {
178 int32_t newMax
= langEntriesMax
* kLangEntriesFactor
;
179 if (langEntries
== localLangEntries
) {
180 // first allocation, copy from local buf
181 langEntries
= (UALanguageEntry
*)uprv_malloc(newMax
*sizeof(UALanguageEntry
));
182 if (langEntries
== NULL
) {
183 *err
= U_MEMORY_ALLOCATION_ERROR
;
186 uprv_memcpy(langEntries
, localLangEntries
, entryCount
*sizeof(UALanguageEntry
));
188 langEntries
= (UALanguageEntry
*)uprv_realloc(langEntries
, newMax
*sizeof(UALanguageEntry
));
189 if (langEntries
== NULL
) {
190 *err
= U_MEMORY_ALLOCATION_ERROR
;
194 langEntriesMax
= newMax
;
196 uprv_strcpy(langEntries
[entryCount
].languageCode
, langCodeWithScriptIfAmbig(langCode
));
197 langEntries
[entryCount
].userFraction
= userFraction
;
198 langEntries
[entryCount
].status
= langStatus
;
202 ures_close(langBund
);
204 if (U_FAILURE(*err
)) {
205 if (langEntries
!= localLangEntries
) {
210 if (entries
!= NULL
) {
211 // sort langEntries, copy entries that fit to provided array
212 qsort(langEntries
, entryCount
, sizeof(UALanguageEntry
), compareLangEntries
);
213 if (entryCount
> entriesCapacity
) {
214 entryCount
= entriesCapacity
;
216 uprv_memcpy(entries
, langEntries
, entryCount
*sizeof(UALanguageEntry
));
217 if (langEntries
!= localLangEntries
) {
224 static const char * forceParent
[] = {
225 "en_150", "en_GB", // en for Europe
227 "en_BD", "en_GB", // en for Bangladesh
228 "en_BE", "en_150", // en for Belgium goes to en for Europe
233 "en_HK", "en_GB", // en for Hong Kong
242 "en_MV", "en_GB", // for Maldives
243 "en_MY", "en_GB", // en for Malaysia
245 "en_PK", "en_GB", // en for Pakistan
249 "yue", "yue_CN", // yue_CN has 71M users (5.2% of 1.37G), yue_HK has 6.5M (90% of 7.17M)
261 enum { kLocBaseNameMax
= 16 };
263 U_CAPI
int32_t U_EXPORT2
264 ualoc_getAppleParent(const char* localeID
,
266 int32_t parentCapacity
,
271 UErrorCode tempStatus
;
272 char locbuf
[ULOC_FULLNAME_CAPACITY
+1];
273 char * foundDoubleUnderscore
;
275 if (U_FAILURE(*err
)) {
278 if ( (parent
==NULL
)? parentCapacity
!=0: parentCapacity
<0 ) {
279 *err
= U_ILLEGAL_ARGUMENT_ERROR
;
282 len
= uloc_getBaseName(localeID
, locbuf
, ULOC_FULLNAME_CAPACITY
, err
); /* canonicalize and strip keywords */
283 if (U_FAILURE(*err
)) {
286 if (*err
== U_STRING_NOT_TERMINATED_WARNING
) {
287 locbuf
[ULOC_FULLNAME_CAPACITY
] = 0;
290 foundDoubleUnderscore
= uprv_strstr(locbuf
, "__"); /* __ comes from bad/missing subtag or variant */
291 if (foundDoubleUnderscore
!= NULL
) {
292 *foundDoubleUnderscore
= 0; /* terminate at the __ */
293 len
= uprv_strlen(locbuf
);
295 if (len
>= 2 && (uprv_strncmp(locbuf
, "en", 2) == 0 || uprv_strncmp(locbuf
, "zh", 2) == 0)) {
296 const char ** forceParentPtr
= forceParent
;
297 const char * testCurLoc
;
298 while ( (testCurLoc
= *forceParentPtr
++) != NULL
) {
299 int cmp
= uprv_strcmp(locbuf
, testCurLoc
);
302 len
= uprv_strlen(*forceParentPtr
);
303 if (len
< parentCapacity
) {
304 uprv_strcpy(parent
, *forceParentPtr
);
306 *err
= U_BUFFER_OVERFLOW_ERROR
;
315 tempStatus
= U_ZERO_ERROR
;
316 rb
= ures_openDirect(NULL
, locbuf
, &tempStatus
);
317 if (U_SUCCESS(tempStatus
)) {
318 const char * actualLocale
= ures_getLocaleByType(rb
, ULOC_ACTUAL_LOCALE
, &tempStatus
);
320 if (U_SUCCESS(tempStatus
) && uprv_strcmp(locbuf
, actualLocale
) != 0) {
321 // we have followed an alias
322 len
= uprv_strlen(actualLocale
);
323 if (len
< parentCapacity
) {
324 uprv_strcpy(parent
, actualLocale
);
326 *err
= U_BUFFER_OVERFLOW_ERROR
;
331 tempStatus
= U_ZERO_ERROR
;
332 rb
= ures_openDirect(NULL
, "supplementalData", &tempStatus
);
333 rb
= ures_getByKey(rb
, "parentLocales", rb
, &tempStatus
);
334 if (U_SUCCESS(tempStatus
)) {
335 UResourceBundle
* parentMapBundle
= NULL
;
336 int32_t childLen
= 0;
337 while (childLen
== 0) {
338 tempStatus
= U_ZERO_ERROR
;
339 parentMapBundle
= ures_getNextResource(rb
, parentMapBundle
, &tempStatus
);
340 if (U_FAILURE(tempStatus
)) {
341 break; // no more parent bundles, normal exit
343 char childName
[kLocBaseNameMax
+ 1];
344 childName
[kLocBaseNameMax
] = 0;
345 const char * childPtr
= NULL
;
346 if (ures_getType(parentMapBundle
) == URES_STRING
) {
347 childLen
= kLocBaseNameMax
;
348 childPtr
= ures_getUTF8String(parentMapBundle
, childName
, &childLen
, FALSE
, &tempStatus
);
349 if (U_FAILURE(tempStatus
) || uprv_strncmp(locbuf
, childPtr
, kLocBaseNameMax
) != 0) {
352 } else { // should be URES_ARRAY
353 int32_t childCur
, childCount
= ures_getSize(parentMapBundle
);
354 for (childCur
= 0; childCur
< childCount
&& childLen
== 0; childCur
++) {
355 tempStatus
= U_ZERO_ERROR
;
356 childLen
= kLocBaseNameMax
;
357 childPtr
= ures_getUTF8StringByIndex(parentMapBundle
, childCur
, childName
, &childLen
, FALSE
, &tempStatus
);
358 if (U_FAILURE(tempStatus
) || uprv_strncmp(locbuf
, childPtr
, kLocBaseNameMax
) != 0) {
366 // parentMapBundle key is the parent we are looking for
367 const char * keyStr
= ures_getKey(parentMapBundle
);
368 len
= uprv_strlen(keyStr
);
369 if (len
< parentCapacity
) {
370 uprv_strcpy(parent
, keyStr
);
372 *err
= U_BUFFER_OVERFLOW_ERROR
;
374 ures_close(parentMapBundle
);
377 ures_close(parentMapBundle
);
380 len
= uloc_getParent(locbuf
, parent
, parentCapacity
, err
);
381 if (U_SUCCESS(*err
) && len
== 0) {
383 if (len
< parentCapacity
) {
384 uprv_strcpy(parent
, "root");
386 *err
= U_BUFFER_OVERFLOW_ERROR
;
393 // Data and related functions for ualoc_localizationsToUse
396 static const char * appleAliasMap
[][2] = {
397 // names are lowercase here because they are looked up after being processed by uloc_getBaseName
398 { "arabic", "ar" }, // T2
399 { "chinese", "zh_Hans" }, // T0
400 { "danish", "da" }, // T2
401 { "dutch", "nl" }, // T1, still in use
402 { "english", "en" }, // T0, still in use
403 { "finnish", "fi" }, // T2
404 { "french", "fr" }, // T0, still in use
405 { "german", "de" }, // T0, still in use
406 { "italian", "it" }, // T1, still in use
407 { "japanese", "ja" }, // T0, still in use
408 { "korean", "ko" }, // T1
409 { "no_NO", "nb_NO" }, // special
410 { "norwegian", "nb" }, // T2
411 { "polish", "pl" }, // T2
412 { "portuguese", "pt" }, // T2
413 { "russian", "ru" }, // T2
414 { "spanish", "es" }, // T1, still in use
415 { "swedish", "sv" }, // T2
416 { "thai", "th" }, // T2
417 { "turkish", "tr" }, // T2
418 { "yue", "yue_Hans"}, // special
419 { "zh", "zh_Hans" }, // special
421 enum { kAppleAliasMapCount
= UPRV_LENGTHOF(appleAliasMap
) };
423 static const char * appleParentMap
[][2] = {
424 { "en_150", "en_GB" }, // Apple custom parent
425 { "en_AD", "en_150" }, // Apple locale addition
426 { "en_AG", "en_GB" }, // Antigua & Barbuda
427 { "en_AI", "en_GB" }, // Anguilla
428 { "en_AL", "en_150" }, // Apple locale addition
429 { "en_AT", "en_150" }, // Apple locale addition
430 { "en_AU", "en_GB" }, // Apple custom parent
431 { "en_BA", "en_150" }, // Apple locale addition
432 { "en_BB", "en_GB" }, // Barbados
433 { "en_BD", "en_GB" }, // Apple custom parent
434 { "en_BE", "en_150" }, // Apple custom parent
435 { "en_BM", "en_GB" }, // Bermuda
436 { "en_BS", "en_GB" }, // Bahamas
437 { "en_BW", "en_GB" }, // Botswana
438 { "en_BZ", "en_GB" }, // Belize
439 { "en_CC", "en_AU" }, // Cocos (Keeling) Islands
440 { "en_CH", "en_150" }, // Apple locale addition
441 { "en_CK", "en_AU" }, // Cook Islands (maybe to en_NZ instead?)
442 { "en_CX", "en_AU" }, // Christmas Island
443 { "en_CY", "en_150" }, // Apple locale addition
444 { "en_CZ", "en_150" }, // Apple locale addition
445 { "en_DE", "en_150" }, // Apple locale addition
446 { "en_DG", "en_GB" },
447 { "en_DK", "en_150" }, // Apple locale addition
448 { "en_DM", "en_GB" }, // Dominica
449 { "en_EE", "en_150" }, // Apple locale addition
450 { "en_ES", "en_150" }, // Apple locale addition
451 { "en_FI", "en_150" }, // Apple locale addition
452 { "en_FJ", "en_GB" }, // Fiji
453 { "en_FK", "en_GB" },
454 { "en_FR", "en_150" }, // Apple locale addition
455 { "en_GD", "en_GB" }, // Grenada
456 { "en_GG", "en_GB" },
457 { "en_GH", "en_GB" }, // Ghana
458 { "en_GI", "en_GB" },
459 { "en_GM", "en_GB" }, // Gambia
460 { "en_GR", "en_150" }, // Apple locale addition
461 { "en_GY", "en_GB" }, // Guyana
462 { "en_HK", "en_GB" }, // Apple custom parent
463 { "en_HR", "en_150" }, // Apple locale addition
464 { "en_HU", "en_150" }, // Apple locale addition
465 { "en_IE", "en_GB" },
466 { "en_IL", "en_001" }, // Apple locale addition
467 { "en_IM", "en_GB" },
468 { "en_IN", "en_GB" }, // Apple custom parent
469 { "en_IO", "en_GB" },
470 { "en_IS", "en_150" }, // Apple locale addition
471 { "en_IT", "en_150" }, // Apple locale addition
472 { "en_JE", "en_GB" },
473 { "en_JM", "en_GB" },
474 { "en_KE", "en_GB" }, // Kenya
475 { "en_KI", "en_GB" }, // Kiribati
476 { "en_KN", "en_GB" }, // St. Kitts & Nevis
477 { "en_KY", "en_GB" }, // Cayman Islands
478 { "en_LC", "en_GB" }, // St. Lucia
479 { "en_LS", "en_GB" }, // Lesotho
480 { "en_LT", "en_150" }, // Apple locale addition
481 { "en_LU", "en_150" }, // Apple locale addition
482 { "en_LV", "en_150" }, // Apple locale addition
483 { "en_ME", "en_150" }, // Apple locale addition
484 { "en_MO", "en_GB" },
485 { "en_MS", "en_GB" }, // Montserrat
486 { "en_MT", "en_GB" },
487 { "en_MU", "en_GB" }, // Mauritius
488 { "en_MV", "en_GB" },
489 { "en_MW", "en_GB" }, // Malawi
490 { "en_MY", "en_GB" }, // Apple custom parent
491 { "en_NA", "en_GB" }, // Namibia
492 { "en_NF", "en_AU" }, // Norfolk Island
493 { "en_NG", "en_GB" }, // Nigeria
494 { "en_NL", "en_150" }, // Apple locale addition
495 { "en_NO", "en_150" }, // Apple locale addition
496 { "en_NR", "en_AU" }, // Nauru
497 { "en_NU", "en_AU" }, // Niue (maybe to en_NZ instead?)
498 { "en_NZ", "en_AU" },
499 { "en_PG", "en_AU" }, // Papua New Guinea
500 { "en_PK", "en_GB" }, // Apple custom parent
501 { "en_PL", "en_150" }, // Apple locale addition
502 { "en_PN", "en_GB" }, // Pitcairn Islands
503 { "en_PT", "en_150" }, // Apple locale addition
504 { "en_RO", "en_150" }, // Apple locale addition
505 { "en_RU", "en_150" }, // Apple locale addition
506 { "en_SB", "en_GB" }, // Solomon Islands
507 { "en_SC", "en_GB" }, // Seychelles
508 { "en_SD", "en_GB" }, // Sudan
509 { "en_SE", "en_150" }, // Apple locale addition
510 { "en_SG", "en_GB" },
511 { "en_SH", "en_GB" },
512 { "en_SI", "en_150" }, // Apple locale addition
513 { "en_SK", "en_150" }, // Apple locale addition
514 { "en_SL", "en_GB" }, // Sierra Leone
515 { "en_SS", "en_GB" }, // South Sudan
516 { "en_SZ", "en_GB" }, // Swaziland
517 { "en_TC", "en_GB" }, // Tristan da Cunha
518 { "en_TO", "en_GB" }, // Tonga
519 { "en_TT", "en_GB" }, // Trinidad & Tobago
520 { "en_TV", "en_GB" }, // Tuvalu
521 { "en_TZ", "en_GB" }, // Tanzania
522 { "en_UG", "en_GB" }, // Uganda
523 { "en_VC", "en_GB" }, // St. Vincent & Grenadines
524 { "en_VG", "en_GB" },
525 { "en_VU", "en_GB" }, // Vanuatu
526 { "en_WS", "en_AU" }, // Samoa (maybe to en_NZ instead?)
527 { "en_ZA", "en_GB" }, // South Africa
528 { "en_ZM", "en_GB" }, // Zambia
529 { "en_ZW", "en_GB" }, // Zimbabwe
531 enum { kAppleParentMapCount
= UPRV_LENGTHOF(appleParentMap
) };
537 } LocParentAndDistance
;
539 static LocParentAndDistance locParentMap
[] = {
540 // The localizations listed in the first column are in
541 // normalized form (e.g. zh_CN -> zh_Hans_CN, etc.).
542 // The distance is a rough measure of distance from
543 // the localization to its parent, used as a weight.
544 { "en_001", "en", 2 },
545 { "en_150", "en_GB", 1 },
546 { "en_AU", "en_GB", 1 },
547 { "en_GB", "en_001", 0 },
548 { "en_US", "en", 0 },
549 { "es_419", "es", 2 },
550 { "es_MX", "es_419", 0 },
551 { "pt_PT", "pt", 2 },
552 { "yue_Hans_CN","yue_Hans",0 },
553 { "yue_Hant_HK","yue_Hant",0 },
554 { "zh_Hans_CN", "zh_Hans", 0 },
555 { "zh_Hant_HK", "zh_Hant", 1 },
556 { "zh_Hant_TW", "zh_Hant", 0 },
558 enum { kLocParentMapCount
= UPRV_LENGTHOF(locParentMap
), kMaxParentDistance
= 8 };
561 kStringsAllocSize
= 4480, // cannot expand; current actual usage 4150
562 kParentMapInitCount
= 205 // can expand; current actual usage 205
566 static UBool U_CALLCONV
ualocale_cleanup(void);
571 static UInitOnce gUALocaleCacheInitOnce
= U_INITONCE_INITIALIZER
;
573 static int gMapDataState
= 0; // 0 = not initialized, 1 = initialized, -1 = failure
574 static char* gStrings
= NULL
;
575 static UHashtable
* gAliasMap
= NULL
;
576 static UHashtable
* gParentMap
= NULL
;
582 static UBool U_CALLCONV
ualocale_cleanup(void)
586 gUALocaleCacheInitOnce
.reset();
588 if (gMapDataState
> 0) {
589 uhash_close(gParentMap
);
591 uhash_close(gAliasMap
);
600 static void initializeMapData() {
603 UResourceBundle
* curBundle
;
607 int32_t entryIndex
, icuEntryCount
;
609 ucln_common_registerCleanup(UCLN_COMMON_LOCALE
, ualocale_cleanup
);
611 gStrings
= (char*)uprv_malloc(kStringsAllocSize
);
613 stringsPtr
= gStrings
;
614 stringsEnd
= gStrings
+ kStringsAllocSize
;
617 status
= U_ZERO_ERROR
;
621 curBundle
= ures_openDirect(NULL
, "metadata", &status
);
622 curBundle
= ures_getByKey(curBundle
, "alias", curBundle
, &status
);
623 curBundle
= ures_getByKey(curBundle
, "language", curBundle
, &status
); // language resource is URES_TABLE
624 if (U_SUCCESS(status
)) {
625 icuEntryCount
= ures_getSize(curBundle
); // currently 331
628 status
= U_ZERO_ERROR
;
629 gAliasMap
= uhash_openSize(uhash_hashIChars
, uhash_compareIChars
, uhash_compareIChars
,
630 kAppleAliasMapCount
+ icuEntryCount
, &status
);
631 // defaults to keyDeleter NULL
632 if (U_SUCCESS(status
)) {
633 for (entryIndex
= 0; entryIndex
< kAppleAliasMapCount
&& U_SUCCESS(status
); entryIndex
++) {
634 uhash_put(gAliasMap
, (void*)appleAliasMap
[entryIndex
][0], (void*)appleAliasMap
[entryIndex
][1], &status
);
636 status
= U_ZERO_ERROR
;
637 UResourceBundle
* aliasMapBundle
= NULL
;
638 for (entryIndex
= 0; entryIndex
< icuEntryCount
&& U_SUCCESS(status
); entryIndex
++) {
639 aliasMapBundle
= ures_getByIndex(curBundle
, entryIndex
, aliasMapBundle
, &status
);
640 if (U_FAILURE(status
)) {
643 const char * keyStr
= ures_getKey(aliasMapBundle
);
644 int32_t len
= uprv_strlen(keyStr
);
645 if (len
>= stringsEnd
- stringsPtr
) {
648 uprv_strcpy(stringsPtr
, keyStr
);
649 char * inLocStr
= stringsPtr
;
650 stringsPtr
+= len
+ 1;
652 len
= stringsEnd
- stringsPtr
- 1;
653 ures_getUTF8StringByKey(aliasMapBundle
, "replacement", stringsPtr
, &len
, TRUE
, &status
);
654 if (U_FAILURE(status
)) {
658 uhash_put(gAliasMap
, inLocStr
, stringsPtr
, &status
);
659 stringsPtr
+= len
+ 1;
661 ures_close(aliasMapBundle
);
663 ures_close(curBundle
);
665 gMapDataState
= -1; // failure
668 ures_close(curBundle
);
670 status
= U_ZERO_ERROR
;
671 gParentMap
= uhash_openSize(uhash_hashIChars
, uhash_compareIChars
, uhash_compareIChars
,
672 kParentMapInitCount
, &status
);
673 // defaults to keyDeleter NULL
674 if (U_SUCCESS(status
)) {
675 curBundle
= ures_openDirect(NULL
, "supplementalData", &status
);
676 curBundle
= ures_getByKey(curBundle
, "parentLocales", curBundle
, &status
); // parentLocales resource is URES_TABLE
677 if (U_SUCCESS(status
)) {
678 UResourceBundle
* parentMapBundle
= NULL
;
680 parentMapBundle
= ures_getNextResource(curBundle
, parentMapBundle
, &status
);
681 if (U_FAILURE(status
)) {
682 break; // no more parent bundles, normal exit
684 const char * keyStr
= ures_getKey(parentMapBundle
);
685 int32_t len
= uprv_strlen(keyStr
);
686 if (len
>= stringsEnd
- stringsPtr
) {
689 uprv_strcpy(stringsPtr
, keyStr
);
690 char * parentStr
= stringsPtr
;
691 stringsPtr
+= len
+ 1;
693 if (ures_getType(parentMapBundle
) == URES_STRING
) {
694 len
= stringsEnd
- stringsPtr
- 1;
695 ures_getUTF8String(parentMapBundle
, stringsPtr
, &len
, TRUE
, &status
);
696 if (U_FAILURE(status
)) {
700 uhash_put(gParentMap
, stringsPtr
, parentStr
, &status
);
701 stringsPtr
+= len
+ 1;
703 // should be URES_ARRAY
704 icuEntryCount
= ures_getSize(parentMapBundle
);
705 for (entryIndex
= 0; entryIndex
< icuEntryCount
&& U_SUCCESS(status
); entryIndex
++) {
706 len
= stringsEnd
- stringsPtr
- 1;
707 ures_getUTF8StringByIndex(parentMapBundle
, entryIndex
, stringsPtr
, &len
, TRUE
, &status
);
708 if (U_FAILURE(status
)) {
712 uhash_put(gParentMap
, stringsPtr
, parentStr
, &status
);
713 stringsPtr
+= len
+ 1;
717 ures_close(parentMapBundle
);
719 ures_close(curBundle
);
721 status
= U_ZERO_ERROR
;
722 for (entryIndex
= 0; entryIndex
< kAppleParentMapCount
&& U_SUCCESS(status
); entryIndex
++) {
723 uhash_put(gParentMap
, (void*)appleParentMap
[entryIndex
][0], (void*)appleParentMap
[entryIndex
][1], &status
);
726 uhash_close(gAliasMap
);
729 gMapDataState
= -1; // failure
734 printf("# gStrings size %ld\n", stringsPtr
- gStrings
);
735 printf("# gParentMap count %d\n", uhash_count(gParentMap
));
742 // The following maps aliases, etc. Ensures 0-termination if no error.
743 static void ualoc_normalize(const char *locale
, char *normalized
, int32_t normalizedCapacity
, UErrorCode
*status
)
745 if (U_FAILURE(*status
)) {
748 // uloc_minimizeSubtags(locale, normalized, normalizedCapacity, status);
750 const char *replacement
= NULL
;
751 if (icu::gMapDataState
> 0) {
752 replacement
= (const char *)uhash_get(icu::gAliasMap
, locale
);
754 if (replacement
== NULL
) {
755 replacement
= locale
;
757 int32_t len
= strnlen(replacement
, normalizedCapacity
);
758 if (len
< normalizedCapacity
) { // allow for 0 termination
759 uprv_strcpy(normalized
, replacement
);
761 *status
= U_BUFFER_OVERFLOW_ERROR
;
765 static void ualoc_getParent(const char *locale
, char *parent
, int32_t parentCapacity
, UErrorCode
*status
)
767 if (U_FAILURE(*status
)) {
770 if (icu::gMapDataState
> 0) {
771 const char *replacement
= (const char *)uhash_get(icu::gParentMap
, locale
);
773 int32_t len
= uprv_strlen(replacement
);
774 if (len
< parentCapacity
) { // allow for 0 termination
775 uprv_strcpy(parent
, replacement
);
777 *status
= U_BUFFER_OVERFLOW_ERROR
;
782 uloc_getParent(locale
, parent
, parentCapacity
- 1, status
);
783 parent
[parentCapacity
- 1] = 0; // ensure 0 termination in case of U_STRING_NOT_TERMINATED_WARNING
786 // Might do something better for this, perhaps maximizing locales then stripping
787 static const char * getLocParent(const char *locale
, int32_t* distance
)
789 int32_t locParentIndex
;
790 for (locParentIndex
= 0; locParentIndex
< kLocParentMapCount
; locParentIndex
++) {
791 if (uprv_strcmp(locale
, locParentMap
[locParentIndex
].locale
) == 0) {
792 *distance
= locParentMap
[locParentIndex
].distance
;
793 return locParentMap
[locParentIndex
].parent
;
796 if (icu::gMapDataState
> 0) {
797 const char *replacement
= (const char *)uhash_get(icu::gParentMap
, locale
);
806 // this just checks if the *pointer* value is already in the array
807 static UBool
locInArray(const char* *localizationsToUse
, int32_t locsToUseCount
, const char *locToCheck
)
810 for (locIndex
= 0; locIndex
< locsToUseCount
; locIndex
++) {
811 if (locToCheck
== localizationsToUse
[locIndex
]) {
818 enum { kLangScriptRegMaxLen
= ULOC_LANG_CAPACITY
+ ULOC_SCRIPT_CAPACITY
+ ULOC_COUNTRY_CAPACITY
}; // currently 22
821 ualoc_localizationsToUse( const char* const *preferredLanguages
,
822 int32_t preferredLanguagesCount
,
823 const char* const *availableLocalizations
,
824 int32_t availableLocalizationsCount
,
825 const char* *localizationsToUse
,
826 int32_t localizationsToUseCapacity
,
829 if (U_FAILURE(*status
)) {
832 if (preferredLanguages
== NULL
|| availableLocalizations
== NULL
|| localizationsToUse
== NULL
) {
833 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
836 // get resource data, need to protect with mutex
837 if (icu::gMapDataState
== 0) {
838 umtx_initOnce(icu::gUALocaleCacheInitOnce
, initializeMapData
);
840 int32_t locsToUseCount
= 0;
841 int32_t prefLangIndex
, availLocIndex
= 0;
842 int32_t availLocIndexBackup
= -1; // if >= 0, contains index of backup match
843 int32_t foundMatchPrefLangIndex
= 0, backupMatchPrefLangIndex
= 0;
844 char (*availLocBase
)[kLangScriptRegMaxLen
+ 1] = NULL
;
845 char (*availLocNorm
)[kLangScriptRegMaxLen
+ 1] = NULL
;
846 UBool foundMatch
= FALSE
;
847 UBool backupMatchPrefLang_pt_PT
= FALSE
;
850 if (preferredLanguagesCount
> 0 && availableLocalizationsCount
> 0) {
851 printf("\n # ualoc_localizationsToUse start, preferredLanguages %d: %s, ..., availableLocalizations %d: %s, ...\n",
852 preferredLanguagesCount
, preferredLanguages
[0], availableLocalizationsCount
, availableLocalizations
[0]);
854 printf("\n # ualoc_localizationsToUse start, preferredLanguages %d: ..., availableLocalizations %d: ...\n",
855 preferredLanguagesCount
, availableLocalizationsCount
);
859 // Part 1, find the best matching localization, if any
860 for (prefLangIndex
= 0; prefLangIndex
< preferredLanguagesCount
; prefLangIndex
++) {
861 char prefLangBaseName
[kLangScriptRegMaxLen
+ 1];
862 char prefLangNormName
[kLangScriptRegMaxLen
+ 1];
863 char prefLangParentName
[kLangScriptRegMaxLen
+ 1];
864 UErrorCode tmpStatus
= U_ZERO_ERROR
;
866 if (preferredLanguages
[prefLangIndex
] == NULL
) {
867 continue; // skip NULL preferredLanguages entry, go to next one
869 // use underscores, fix bad capitalization, delete any keywords
870 uloc_getBaseName(preferredLanguages
[prefLangIndex
], prefLangBaseName
, kLangScriptRegMaxLen
, &tmpStatus
);
871 if (U_FAILURE(tmpStatus
) || prefLangBaseName
[0] == 0 ||
872 uprv_strcmp(prefLangBaseName
, "root") == 0 || prefLangBaseName
[0] == '_') {
873 continue; // can't handle this preferredLanguages entry or it is invalid, go to next one
875 prefLangBaseName
[kLangScriptRegMaxLen
] = 0; // ensure 0 termination, could have U_STRING_NOT_TERMINATED_WARNING
877 printf(" # loop: try prefLangBaseName %s\n", prefLangBaseName
);
880 // if we have not already allocated and filled the array of
881 // base availableLocalizations, do so now.
882 if (availLocBase
== NULL
) {
883 availLocBase
= (char (*)[kLangScriptRegMaxLen
+ 1])uprv_malloc(availableLocalizationsCount
* (kLangScriptRegMaxLen
+ 1));
884 if (availLocBase
== NULL
) {
885 continue; // cannot further check this preferredLanguages entry, go to next one
888 printf(" # allocate & fill availLocBase\n");
890 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
891 tmpStatus
= U_ZERO_ERROR
;
892 if (availableLocalizations
[availLocIndex
] == NULL
) {
893 availLocBase
[availLocIndex
][0] = 0; // effectively remove this entry
896 uloc_getBaseName(availableLocalizations
[availLocIndex
], availLocBase
[availLocIndex
], kLangScriptRegMaxLen
, &tmpStatus
);
897 if (U_FAILURE(tmpStatus
) || uprv_strcmp(availLocBase
[availLocIndex
], "root") == 0 || availLocBase
[availLocIndex
][0] == '_') {
898 availLocBase
[availLocIndex
][0] = 0; // effectively remove this entry
901 availLocBase
[availLocIndex
][kLangScriptRegMaxLen
] = 0; // ensure 0 termination, could have U_STRING_NOT_TERMINATED_WARNING
903 printf(" # add availLocBase %s\n", availLocBase
[availLocIndex
]);
907 // first compare base preferredLanguage to base versions of availableLocalizations names
908 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
909 if (uprv_strcmp(prefLangBaseName
, availLocBase
[availLocIndex
]) == 0) {
910 foundMatch
= TRUE
; // availLocIndex records where
911 foundMatchPrefLangIndex
= prefLangIndex
;
913 printf(" # FOUND: matched availLocBase %s -> actualLoc %s\n", availLocBase
[availLocIndex
], availableLocalizations
[availLocIndex
]);
919 break; // found a loc for this preferredLanguages entry
922 // get normalized preferredLanguage
923 tmpStatus
= U_ZERO_ERROR
;
924 ualoc_normalize(prefLangBaseName
, prefLangNormName
, kLangScriptRegMaxLen
+ 1, &tmpStatus
);
925 if (U_FAILURE(tmpStatus
)) {
926 continue; // can't handle this preferredLanguages entry, go to next one
929 printf(" # prefLangNormName %s\n", prefLangNormName
);
931 // if we have not already allocated and filled the array of
932 // normalized availableLocalizations, do so now.
933 // Note: ualoc_normalize turns "zh_TW" into "zh_Hant_TW", zh_HK" into "zh_Hant_HK",
934 // and fixes deprecated codes "iw" > "he", "in" > "id" etc.
935 if (availLocNorm
== NULL
) {
936 availLocNorm
= (char (*)[kLangScriptRegMaxLen
+ 1])uprv_malloc(availableLocalizationsCount
* (kLangScriptRegMaxLen
+ 1));
937 if (availLocNorm
== NULL
) {
938 continue; // cannot further check this preferredLanguages entry, go to next one
941 printf(" # allocate & fill availLocNorm\n");
943 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
944 tmpStatus
= U_ZERO_ERROR
;
945 ualoc_normalize(availLocBase
[availLocIndex
], availLocNorm
[availLocIndex
], kLangScriptRegMaxLen
+ 1, &tmpStatus
);
946 if (U_FAILURE(tmpStatus
)) {
947 availLocNorm
[availLocIndex
][0] = 0; // effectively remove this entry
950 printf(" # actualLoc %-11s -> norm %s\n", availableLocalizations
[availLocIndex
], availLocNorm
[availLocIndex
]);
955 // now compare normalized preferredLanguage to normalized localization names
956 // if matches, copy *original* localization name
957 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
958 if (uprv_strcmp(prefLangNormName
, availLocNorm
[availLocIndex
]) == 0) {
959 foundMatch
= TRUE
; // availLocIndex records where
960 foundMatchPrefLangIndex
= prefLangIndex
;
962 printf(" # FOUND: matched availLocNorm %s -> actualLoc %s\n", availLocNorm
[availLocIndex
], availableLocalizations
[availLocIndex
]);
968 break; // found a loc for this preferredLanguages entry
971 // now walk up the parent chain for preferredLanguage
972 // until we find a match or hit root
973 uprv_strcpy(prefLangBaseName
, prefLangNormName
);
974 while (!foundMatch
) {
975 tmpStatus
= U_ZERO_ERROR
;
976 ualoc_getParent(prefLangBaseName
, prefLangParentName
, kLangScriptRegMaxLen
+ 1, &tmpStatus
);
977 if (U_FAILURE(tmpStatus
) || uprv_strcmp(prefLangParentName
, "root") == 0 || prefLangParentName
[0] == 0) {
978 break; // reached root or cannot proceed further
981 printf(" # prefLangParentName %s\n", prefLangParentName
);
984 // now compare this preferredLanguage parent to normalized localization names
985 // if matches, copy *original* localization name
986 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
987 if (uprv_strcmp(prefLangParentName
, availLocNorm
[availLocIndex
]) == 0) {
988 foundMatch
= TRUE
; // availLocIndex records where
989 foundMatchPrefLangIndex
= prefLangIndex
;
991 printf(" # FOUND: matched availLocNorm %s -> actualLoc %s\n", availLocNorm
[availLocIndex
], availableLocalizations
[availLocIndex
]);
996 uprv_strcpy(prefLangBaseName
, prefLangParentName
);
999 break; // found a loc for this preferredLanguages entry
1002 // last try, use parents of selected language to try for backup match
1003 // if we have not already found one
1004 if (availLocIndexBackup
< 0) {
1005 // now walk up the parent chain for preferredLanguage again
1006 // checking against parents of selected availLocNorm entries
1007 // but this time start with current prefLangNormName
1008 uprv_strcpy(prefLangBaseName
, prefLangNormName
);
1009 int32_t minDistance
= kMaxParentDistance
;
1011 // now compare this preferredLanguage to normalized localization names
1012 // parent if have one for this; if matches, copy *original* localization name
1014 printf(" # BACKUP: trying prefLangBaseName %s\n", prefLangBaseName
);
1016 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
1017 char availLocMinOrParent
[kLangScriptRegMaxLen
+ 1];
1019 // first check for special Apple parents of availLocNorm; the number
1020 // of locales with such parents is small.
1021 // If no such parent, or if parent has an intermediate numeric region,
1022 // then try stripping the original region.
1023 int32_t availLocParentLen
= 0;
1024 const char *availLocParent
= getLocParent(availLocNorm
[availLocIndex
], &distance
);
1025 if (availLocParent
) {
1027 printf(" # availLocAppleParentName %s\n", availLocParent
);
1029 if (uprv_strcmp(prefLangBaseName
, availLocParent
) == 0 && distance
< minDistance
) {
1030 availLocIndexBackup
= availLocIndex
; // records where the match occurred
1031 backupMatchPrefLangIndex
= prefLangIndex
;
1032 minDistance
= distance
;
1034 printf(" # BACKUP: LocAppleParent matched prefLangNormName with distance %d\n", distance
);
1038 availLocParentLen
= uprv_strlen(availLocParent
);
1040 if (minDistance
<= 1) {
1041 continue; // we can't get any closer in the rest of this iteration
1043 if (availLocParent
== NULL
|| (availLocParentLen
>= 6 && isdigit(availLocParent
[availLocParentLen
-1]))) {
1044 tmpStatus
= U_ZERO_ERROR
;
1045 int32_t regLen
= uloc_getCountry(availLocNorm
[availLocIndex
], availLocMinOrParent
, kLangScriptRegMaxLen
, &tmpStatus
);
1046 if (U_SUCCESS(tmpStatus
) && regLen
> 1) {
1047 uloc_addLikelySubtags(availLocNorm
[availLocIndex
], availLocMinOrParent
, kLangScriptRegMaxLen
, &tmpStatus
);
1048 if (U_SUCCESS(tmpStatus
)) {
1049 availLocMinOrParent
[kLangScriptRegMaxLen
] = 0; // ensure 0 termination, could have U_STRING_NOT_TERMINATED_WARNING
1051 printf(" # availLocRegMaxName %s\n", availLocMinOrParent
);
1053 char availLocTemp
[kLangScriptRegMaxLen
+ 1];
1054 uloc_getParent(availLocMinOrParent
, availLocTemp
, kLangScriptRegMaxLen
, &tmpStatus
);
1055 if (U_SUCCESS(tmpStatus
)) {
1056 availLocTemp
[kLangScriptRegMaxLen
] = 0;
1057 uloc_minimizeSubtags(availLocTemp
, availLocMinOrParent
, kLangScriptRegMaxLen
, &tmpStatus
);
1058 if (U_SUCCESS(tmpStatus
)) {
1059 availLocMinOrParent
[kLangScriptRegMaxLen
] = 0;
1061 printf(" # availLocNoRegParentName %s\n", availLocMinOrParent
);
1063 if (uprv_strcmp(prefLangBaseName
, availLocMinOrParent
) == 0) {
1064 availLocIndexBackup
= availLocIndex
; // records where the match occurred
1065 backupMatchPrefLangIndex
= prefLangIndex
;
1067 backupMatchPrefLang_pt_PT
= (uprv_strcmp(prefLangNormName
, "pt_PT") == 0);
1069 printf(" # BACKUP: LocNoRegParent matched prefLangNormName with distance 1\n");
1078 // then check against minimized version of availLocNorm
1079 tmpStatus
= U_ZERO_ERROR
;
1080 uloc_minimizeSubtags(availLocNorm
[availLocIndex
], availLocMinOrParent
, kLangScriptRegMaxLen
, &tmpStatus
);
1081 if (U_FAILURE(tmpStatus
)) {
1084 availLocMinOrParent
[kLangScriptRegMaxLen
] = 0; // ensure 0 termination, could have U_STRING_NOT_TERMINATED_WARNING
1086 printf(" # availLocMinimized %s\n", availLocMinOrParent
);
1088 if (uprv_strcmp(prefLangBaseName
, availLocMinOrParent
) == 0) {
1089 availLocIndexBackup
= availLocIndex
; // records where the match occurred
1090 backupMatchPrefLangIndex
= prefLangIndex
;
1093 printf(" # BACKUP: LocMinimized matched prefLangNormName with distance 1\n");
1097 if (availLocIndexBackup
>= 0) {
1100 tmpStatus
= U_ZERO_ERROR
;
1101 ualoc_getParent(prefLangBaseName
, prefLangParentName
, kLangScriptRegMaxLen
+ 1, &tmpStatus
);
1102 if (U_FAILURE(tmpStatus
) || uprv_strcmp(prefLangParentName
, "root") == 0 || prefLangParentName
[0] == 0) {
1103 break; // reached root or cannot proceed further
1105 uprv_strcpy(prefLangBaseName
, prefLangParentName
);
1109 // If we have a backup match, decide what to do
1110 if (availLocIndexBackup
>= 0) {
1112 // no main match, just use the backup
1113 availLocIndex
= availLocIndexBackup
;
1116 printf(" # no main match, have backup => use availLocIndexBackup %d\n", availLocIndexBackup
);
1118 } else if (backupMatchPrefLangIndex
< foundMatchPrefLangIndex
&& (!backupMatchPrefLang_pt_PT
|| uprv_strcmp(availLocNorm
[availLocIndexBackup
], "pt_BR") != 0)) {
1119 // have a main match but backup match was higher in the prefs, use it if for a different language
1121 printf(" # have backup match higher in prefs, comparing its language and script to main match\n");
1123 char mainLang
[ULOC_LANG_CAPACITY
+ 1];
1124 char backupLang
[ULOC_LANG_CAPACITY
+ 1];
1125 UErrorCode tmpStatus
= U_ZERO_ERROR
;
1126 uloc_getLanguage(availLocNorm
[availLocIndex
], mainLang
, ULOC_LANG_CAPACITY
, &tmpStatus
);
1127 mainLang
[ULOC_LANG_CAPACITY
] = 0; // ensure zero termination
1128 uloc_getLanguage(availLocNorm
[availLocIndexBackup
], backupLang
, ULOC_LANG_CAPACITY
, &tmpStatus
);
1129 backupLang
[ULOC_LANG_CAPACITY
] = 0; // ensure zero termination
1130 if (U_SUCCESS(tmpStatus
)) {
1131 if (uprv_strncmp(mainLang
, backupLang
, ULOC_LANG_CAPACITY
) != 0) {
1132 // backup match has different language than main match
1133 availLocIndex
= availLocIndexBackup
;
1134 // foundMatch is already TRUE
1136 printf(" # main match but backup is for a different lang higher in prefs => use availLocIndexBackup %d\n", availLocIndexBackup
);
1139 // backup match has same language as main match, check scripts too
1140 char availLocMaximized
[kLangScriptRegMaxLen
+ 1];
1142 uloc_addLikelySubtags(availLocNorm
[availLocIndex
], availLocMaximized
, kLangScriptRegMaxLen
, &tmpStatus
);
1143 availLocMaximized
[kLangScriptRegMaxLen
] = 0;
1144 uloc_getScript(availLocMaximized
, mainLang
, ULOC_LANG_CAPACITY
, &tmpStatus
);
1145 mainLang
[ULOC_LANG_CAPACITY
] = 0;
1147 uloc_addLikelySubtags(availLocNorm
[availLocIndexBackup
], availLocMaximized
, kLangScriptRegMaxLen
, &tmpStatus
);
1148 availLocMaximized
[kLangScriptRegMaxLen
] = 0;
1149 uloc_getScript(availLocMaximized
, backupLang
, ULOC_LANG_CAPACITY
, &tmpStatus
);
1150 backupLang
[ULOC_LANG_CAPACITY
] = 0;
1152 if (U_SUCCESS(tmpStatus
) && uprv_strncmp(mainLang
, backupLang
, ULOC_LANG_CAPACITY
) != 0) {
1153 // backup match has different script than main match
1154 availLocIndex
= availLocIndexBackup
;
1155 // foundMatch is already TRUE
1157 printf(" # main match but backup is for a different script higher in prefs => use availLocIndexBackup %d\n", availLocIndexBackup
);
1165 // Part 2, if we found a matching localization, then walk up its parent tree to find any fallback matches in availableLocalizations
1167 // Here availLocIndex corresponds to the first matched localization
1168 UErrorCode tmpStatus
= U_ZERO_ERROR
;
1169 int32_t availLocMatchIndex
= availLocIndex
;
1170 if (locsToUseCount
< localizationsToUseCapacity
) {
1171 localizationsToUse
[locsToUseCount
++] = availableLocalizations
[availLocMatchIndex
];
1173 // at this point we must have availLocBase, and minimally matched against that.
1174 // if we have not already allocated and filled the array of
1175 // normalized availableLocalizations, do so now, but don't require it
1176 if (availLocNorm
== NULL
) {
1177 availLocNorm
= (char (*)[kLangScriptRegMaxLen
+ 1])uprv_malloc(availableLocalizationsCount
* (kLangScriptRegMaxLen
+ 1));
1178 if (availLocNorm
!= NULL
) {
1179 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
1180 tmpStatus
= U_ZERO_ERROR
;
1181 ualoc_normalize(availLocBase
[availLocIndex
], availLocNorm
[availLocIndex
], kLangScriptRegMaxLen
+ 1, &tmpStatus
);
1182 if (U_FAILURE(tmpStatus
)) {
1183 availLocNorm
[availLocIndex
][0] = 0; // effectively remove this entry
1189 // add normalized form of matching loc, if different and in availLocBase
1190 if (locsToUseCount
< localizationsToUseCapacity
) {
1191 tmpStatus
= U_ZERO_ERROR
;
1192 char matchedLocNormName
[kLangScriptRegMaxLen
+ 1];
1193 char matchedLocParentName
[kLangScriptRegMaxLen
+ 1];
1194 // get normalized form of matching loc
1195 if (availLocNorm
!= NULL
) {
1196 uprv_strcpy(matchedLocNormName
, availLocNorm
[availLocMatchIndex
]);
1198 ualoc_normalize(availLocBase
[availLocMatchIndex
], matchedLocNormName
, kLangScriptRegMaxLen
+ 1, &tmpStatus
);
1200 if (U_SUCCESS(tmpStatus
)) {
1201 // add normalized form of matching loc, if different and in availLocBase
1202 if (uprv_strcmp(matchedLocNormName
, localizationsToUse
[0]) != 0) {
1203 // normalization of matched localization is different, see if we have the normalization in availableLocalizations
1204 // from this point on, availLocIndex no longer corresponds to the matched localization.
1205 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
1206 if ( (uprv_strcmp(matchedLocNormName
, availLocBase
[availLocIndex
]) == 0
1207 || (availLocNorm
!= NULL
&& uprv_strcmp(matchedLocNormName
, availLocNorm
[availLocIndex
]) == 0))
1208 && !locInArray(localizationsToUse
, locsToUseCount
, availableLocalizations
[availLocIndex
])) {
1209 localizationsToUse
[locsToUseCount
++] = availableLocalizations
[availLocIndex
];
1215 // now walk up the parent chain from matchedLocNormName, adding parents if they are in availLocBase
1216 while (locsToUseCount
< localizationsToUseCapacity
) {
1217 ualoc_getParent(matchedLocNormName
, matchedLocParentName
, kLangScriptRegMaxLen
+ 1, &tmpStatus
);
1218 if (U_FAILURE(tmpStatus
) || uprv_strcmp(matchedLocParentName
, "root") == 0 || matchedLocParentName
[0] == 0) {
1219 break; // reached root or cannot proceed further
1222 // now compare this matchedLocParentName parent to base localization names (and norm ones if we have them)
1223 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
1224 if ( (uprv_strcmp(matchedLocParentName
, availLocBase
[availLocIndex
]) == 0
1225 || (availLocNorm
!= NULL
&& uprv_strcmp(matchedLocParentName
, availLocNorm
[availLocIndex
]) == 0))
1226 && !locInArray(localizationsToUse
, locsToUseCount
, availableLocalizations
[availLocIndex
])) {
1227 localizationsToUse
[locsToUseCount
++] = availableLocalizations
[availLocIndex
];
1231 uprv_strcpy(matchedLocNormName
, matchedLocParentName
);
1234 // The above still fails to include "zh_TW" if it is in availLocBase and the matched localization
1235 // base name is "zh_HK" or "zh_MO". One option would be to walk up the parent chain from
1236 // matchedLocNormName again, comparing against parents of of selected availLocNorm entries.
1237 // But this picks up too many matches that are not parents of the matched localization. So
1238 // we just handle these specially.
1239 if ( locsToUseCount
< localizationsToUseCapacity
1240 && (uprv_strcmp(availLocBase
[availLocMatchIndex
], "zh_HK") == 0
1241 || uprv_strcmp(availLocBase
[availLocMatchIndex
], "zh_MO") == 0) ) {
1242 int32_t zhTW_matchIndex
= -1;
1243 UBool zhHant_found
= FALSE
;
1244 for (availLocIndex
= 0; availLocIndex
< availableLocalizationsCount
; availLocIndex
++) {
1245 if ( zhTW_matchIndex
< 0 && uprv_strcmp("zh_TW", availLocBase
[availLocIndex
]) == 0 ) {
1246 zhTW_matchIndex
= availLocIndex
;
1248 if ( !zhHant_found
&& uprv_strcmp("zh_Hant", availLocBase
[availLocIndex
]) == 0 ) {
1249 zhHant_found
= TRUE
;
1252 if (zhTW_matchIndex
>= 0 && !zhHant_found
1253 && !locInArray(localizationsToUse
, locsToUseCount
, availableLocalizations
[zhTW_matchIndex
])) {
1254 localizationsToUse
[locsToUseCount
++] = availableLocalizations
[zhTW_matchIndex
];
1261 uprv_free(availLocNorm
);
1262 uprv_free(availLocBase
);
1263 return locsToUseCount
;