+static icu::UInitOnce parentLocaleInitOnce = U_INITONCE_INITIALIZER;
+static UHashtable* parentLocaleTable = NULL;
+static char* parentLocaleStrings = NULL;
+static const int32_t parentLocaleStringsCapacity = 2350; // actual size is 2292-- leave a little extra space in case the table changes
+
+static void doInitParentLocaleTable() {
+ UErrorCode err = U_ZERO_ERROR;
+ parentLocaleTable = uhash_open(uhash_hashIChars, uhash_compareIChars, uhash_compareIChars, &err);
+
+ UResourceBundle* curBundle = ures_openDirect(NULL, "supplementalData", &err);
+ curBundle = ures_getByKey(curBundle, "parentLocales", curBundle, &err);
+ parentLocaleStrings = (char*)uprv_malloc(parentLocaleStringsCapacity);
+ const char* parentLocaleStringsEnd = parentLocaleStrings + parentLocaleStringsCapacity;
+ if (U_FAILURE(err) || parentLocaleStrings == NULL) {
+ U_ASSERT(FALSE); // we should never end up in here; make sure
+ uhash_close(parentLocaleTable);
+ parentLocaleTable = NULL;
+ ures_close(curBundle);
+ uprv_free(parentLocaleStrings);
+ return;
+ }
+
+ char* nextString = parentLocaleStrings;
+ UResourceBundle* localesForParent = NULL; // localesForParent in the next line is an in/out parameter
+ localesForParent = ures_getNextResource(curBundle, localesForParent, &err);
+ while (U_SUCCESS(err) && localesForParent != NULL) {
+ const char* parentID = ures_getKey(localesForParent);
+ if (nextString + uprv_strlen(parentID) + 1 >= parentLocaleStringsEnd) {
+ // if we can't build this whole table, we're in trouble
+ U_ASSERT(FALSE);
+ break;
+ }
+ uprv_strcpy(nextString, parentID);
+ nextString += uprv_strlen(parentID) + 1;
+
+ int32_t numChildIDs = ures_getSize(localesForParent);
+ for (int32_t i = 0; i < numChildIDs; i++) {
+ int32_t childLength = parentLocaleStringsEnd - nextString - 1;
+ const char* childID = ures_getUTF8StringByIndex(localesForParent, i, nextString, &childLength, TRUE, &err);
+ nextString += childLength + 1;
+
+ if (U_SUCCESS(err)) {
+ uhash_put(parentLocaleTable, (void*)childID, (void*)parentID, &err);
+ } else {
+ // again, if we can't build this whole table, we're in trouble
+ U_ASSERT(FALSE);
+ break;
+ }
+ }
+ localesForParent = ures_getNextResource(curBundle, localesForParent, &err);
+ }
+
+ ures_close(localesForParent);
+ ures_close(curBundle);
+}
+
+static void initParentLocaleTable() {
+ umtx_initOnce(parentLocaleInitOnce, &doInitParentLocaleTable);
+}
+
+/**
+ * <rdar://problem/63880069>
+ * Currently internal function which should eventually be moved (with new name) to ulocimp.h, or perhaps uloc.h.
+ * Somewhat like uloc_getParent, but only returns a parent from parentLocales data.
+ */
+U_CAPI int32_t U_EXPORT2
+ures_getLocParent(const char* localeID,
+ char* parent,
+ int32_t parentCapacity,
+ UErrorCode* err)
+{
+ if (U_FAILURE(*err))
+ return 0;
+ if (localeID == NULL)
+ localeID = uloc_getDefault();
+
+ initParentLocaleTable();
+ U_ASSERT(parentLocaleTable != NULL); // should not get NULL here
+ if (parentLocaleTable != NULL) {
+ const char* parentID = (const char*)uhash_get(parentLocaleTable, localeID);
+ if (parentID != NULL) {
+ int32_t parentLen = uprv_strlen(parentID);
+ uprv_memcpy(parent, parentID, uprv_min(parentLen, parentCapacity));
+ return u_terminateChars(parent, parentCapacity, parentLen, err);
+ }
+ }
+ return 0;
+ // A more general version of this might do the following instead:
+ // return uloc_getParent(localeID, parent, parentCapacity, err);
+}
+
+/**
+ * Internal function, determines the search path for resource bundle files.
+ * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified
+ * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to
+ * use chopLocale() below.
+ * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the
+ * requested parent locale ID.
+ * @param savedRegionCode Pointer to a character array where this function can store the region code from the input locale ID for use by future
+ * calls to this function. The caller doesn't usually use the contents of this array itself; it's just temporary storage for this function. The caller can pass NULL,
+ * but this isn't recommended.
+ */
+static UBool getParentLocaleID(char *name, char* savedRegionCode) {
+ // first look the locale ID up in the parent locale table (if it exists); if that table specifies a parent
+ // for it, return that
+ if (parentLocaleTable != NULL) {
+ const char* parentID = (const char*)uhash_get(parentLocaleTable, name);
+ if (parentID != NULL) {
+ uprv_strcpy(name, parentID);
+ return TRUE;
+ }
+ }
+
+ // if the parent locale table didn't specify a locale ID for our locale, derive it algorithmically
+ char language[ULOC_LANG_CAPACITY];
+ char script[ULOC_SCRIPT_CAPACITY];
+ char country[ULOC_COUNTRY_CAPACITY];
+ char variant[ULOC_KEYWORD_AND_VALUES_CAPACITY];
+ UErrorCode err = U_ZERO_ERROR;
+
+ uloc_getLanguage(name, language, ULOC_LANG_CAPACITY, &err);
+ uloc_getScript(name, script, ULOC_SCRIPT_CAPACITY, &err);
+ uloc_getCountry(name, country, ULOC_COUNTRY_CAPACITY, &err);
+ uloc_getVariant(name, variant, ULOC_KEYWORD_AND_VALUES_CAPACITY, &err);
+
+ if (U_SUCCESS(err)) {
+ // if the locale ID has both script and country codes (and no variant), save the country code in
+ // savedRegionField and chop it off of the locale ID
+ if (variant[0] == '\0' && script[0] != '\0' && country[0] != '\0') {
+ uprv_strcpy(savedRegionCode, country);
+ return chopLocale(name);
+ }
+
+ // if the locale ID has language and script and we have a saved region code, replace the script code
+ // in the locale ID with the saved region code
+ if (variant[0] == '\0' && country[0] == '\0' && script[0] != '\0') {
+ sprintf(name, "%s_%s", language, savedRegionCode);
+ return TRUE;
+ }
+ }
+
+ // if we have any other configuration of fields (or couldn't get fields for some reason), clear out the saved
+ // region code and just use chopLocale()
+ *savedRegionCode = '\0';
+ return chopLocale(name);
+}
+