Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed553f6b authored by Roozbeh Pournader's avatar Roozbeh Pournader Committed by Android (Google) Code Review
Browse files

Merge "Make default resources a better match for en-US requests"

parents 14889dd6 27953c34
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ int localeDataCompareRegions(

void localeDataComputeScript(char out[4], const char* language, const char* region);

bool localeDataIsCloseToUsEnglish(const char* region);

} // namespace android

#endif // _LIBS_UTILS_LOCALE_DATA_H
+21 −3
Original line number Diff line number Diff line
@@ -70,13 +70,17 @@ uint32_t findParent(uint32_t packed_locale, const char* script) {
//
// Returns the number of ancestors written in the output, which is always
// at least one.
//
// (If 'out' is nullptr, we do everything the same way but we simply don't write
// any results in 'out'.)
size_t findAncestors(uint32_t* out, ssize_t* stop_list_index,
                     uint32_t packed_locale, const char* script,
                     const uint32_t* stop_list, size_t stop_set_length) {
    uint32_t ancestor = packed_locale;
    size_t count = 0;
    do {
        out[count++] = ancestor;
        if (out != nullptr) out[count] = ancestor;
        count++;
        for (size_t i = 0; i < stop_set_length; i++) {
            if (stop_list[i] == ancestor) {
                *stop_list_index = (ssize_t) i;
@@ -93,10 +97,9 @@ size_t findDistance(uint32_t supported,
                    const char* script,
                    const uint32_t* request_ancestors,
                    size_t request_ancestors_count) {
    uint32_t supported_ancestors[MAX_PARENT_DEPTH+1];
    ssize_t request_ancestors_index;
    const size_t supported_ancestor_count = findAncestors(
            supported_ancestors, &request_ancestors_index,
            nullptr, &request_ancestors_index,
            supported, script,
            request_ancestors, request_ancestors_count);
    // Since both locales share the same root, there will always be a shared
@@ -198,4 +201,19 @@ void localeDataComputeScript(char out[4], const char* language, const char* regi
    }
}

const uint32_t ENGLISH_STOP_LIST[2] = {
    0x656E0000lu, // en
    0x656E8400lu, // en-001
};
const char ENGLISH_CHARS[2] = {'e', 'n'};
const char LATIN_CHARS[4] = {'L', 'a', 't', 'n'};

bool localeDataIsCloseToUsEnglish(const char* region) {
    const uint32_t locale = packLocale(ENGLISH_CHARS, region);
    ssize_t stop_list_index;
    findAncestors(nullptr, &stop_list_index, locale, LATIN_CHARS, ENGLISH_STOP_LIST, 2);
    // A locale is like US English if we see "en" before "en-001" in its ancestor list.
    return stop_list_index == 0; // 'en' is first in ENGLISH_STOP_LIST
}

} // namespace android
+26 −1
Original line number Diff line number Diff line
@@ -2196,7 +2196,32 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
        // The languages of the two resources are not the same. We can only
        // assume that one of the two resources matched the request because one
        // doesn't have a language and the other has a matching language.
        return (language[0] != 0);
        //
        // We consider the one that has the language specified a better match.
        //
        // The exception is that we consider no-language resources a better match
        // for US English and similar locales than locales that are a descendant
        // of Internatinal English (en-001), since no-language resources are
        // where the US English resource have traditionally lived for most apps.
        if (requested->language[0] == 'e' && requested->language[1] == 'n') {
            if (requested->country[0] == 'U' && requested->country[1] == 'S') {
                // For US English itself, we consider a no-locale resource a
                // better match if the other resource has a country other than
                // US specified.
                if (language[0] != '\0') {
                    return country[0] == '\0' || (country[0] == 'U' && country[1] == 'S');
                } else {
                    return !(o.country[0] == '\0' || (o.country[0] == 'U' && o.country[1] == 'S'));
                }
            } else if (localeDataIsCloseToUsEnglish(requested->country)) {
                if (language[0] != '\0') {
                    return localeDataIsCloseToUsEnglish(country);
                } else {
                    return !localeDataIsCloseToUsEnglish(o.country);
                }
            }
        }
        return (language[0] != '\0');
    }

    // If we are here, both the resources have the same non-empty language as
+48 −0
Original line number Diff line number Diff line
@@ -592,4 +592,52 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) {
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
}

// Default resources are considered better matches for US English
// and US-like English locales than International English locales
TEST(ConfigLocaleTest, isLocaleBetterThan_UsEnglishIsSpecial) {
    ResTable_config config1, config2, request;

    fillIn("en", "US", NULL, NULL, &request);
    fillIn(NULL, NULL, NULL, NULL, &config1);
    fillIn("en", "001", NULL, NULL, &config2);
    // default is better than International English
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("en", "US", NULL, NULL, &request);
    fillIn(NULL, NULL, NULL, NULL, &config1);
    fillIn("en", "GB", NULL, NULL, &config2);
    // default is better than British English
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("en", "PR", NULL, NULL, &request);
    fillIn(NULL, NULL, NULL, NULL, &config1);
    fillIn("en", "001", NULL, NULL, &config2);
    // Even for Puerto Rico, default is better than International English
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("en", "US", NULL, NULL, &request);
    fillIn("en", NULL, NULL, NULL, &config1);
    fillIn(NULL, NULL, NULL, NULL, &config2);
    // "English" is better than default, since it's a parent of US English
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("en", "PR", NULL, NULL, &request);
    fillIn("en", NULL, NULL, NULL, &config1);
    fillIn(NULL, NULL, NULL, NULL, &config2);
    // "English" is better than default, since it's a parent of Puerto Rico English
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("en", "US", NULL, NULL, &request);
    fillIn(NULL, NULL, NULL, NULL, &config1);
    fillIn("en", "PR", NULL, NULL, &config2);
    // For US English itself, we prefer default to its siblings in the parent tree
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
}

}  // namespace android