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

Commit a192a8ce authored by Roozbeh Pournader's avatar Roozbeh Pournader
Browse files

Treat Latin American locales specially

Due to legacy reasons, Android translations of European Spanish were
kept under 'es', while Latin American Spanish translations were kept
under 'es-US'. The combination of this, and the new locale
preference rules in Nougat, resulted in 'es' winning over 'es-US' for
all Latin American locales, since 'es' was a direct ancestor, while
'es-US' was just a fallback.

The changes in Nougat had assumed that app developers would put Latin
American Spanish translations under 'es-419', but that could create a
backward-compatibility problem under older Android versions that did
not support three-digit region codes properly.

This CL keeps the Nougat logic and its locale parent tree, but
special-cases es-US and es-MX to be treated as equivalents of es-419
in cases where they are present and es-419 is not.

Bug: 31545805
Bug: 34126460
Test: unit tests are included
Change-Id: Iab26f41294587ee044685a5a6560520c7cbb06f7
parent 51d34bf0
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -121,6 +122,16 @@ inline bool isRepresentative(uint32_t language_and_region, const char* script) {
    return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
}

const uint32_t US_SPANISH = 0x65735553lu; // es-US
const uint32_t MEXICAN_SPANISH = 0x65734D58lu; // es-MX
const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424lu; // es-419

// The two locales es-US and es-MX are treated as special fallbacks for es-419.
// If there is no es-419, they are considered its equivalent.
inline bool isSpecialSpanish(uint32_t language_and_region) {
    return (language_and_region == US_SPANISH || language_and_region == MEXICAN_SPANISH);
}

int localeDataCompareRegions(
        const char* left_region, const char* right_region,
        const char* requested_language, const char* requested_script,
@@ -129,18 +140,30 @@ int localeDataCompareRegions(
    if (left_region[0] == right_region[0] && left_region[1] == right_region[1]) {
        return 0;
    }
    const uint32_t left = packLocale(requested_language, left_region);
    const uint32_t right = packLocale(requested_language, right_region);
    uint32_t left = packLocale(requested_language, left_region);
    uint32_t right = packLocale(requested_language, right_region);
    const uint32_t request = packLocale(requested_language, requested_region);

    // If one and only one of the two locales is a special Spanish locale, we
    // replace it with es-419. We don't do the replacement if the other locale
    // is already es-419, or both locales are special Spanish locales (when
    // es-US is being compared to es-MX).
    const bool leftIsSpecialSpanish = isSpecialSpanish(left);
    const bool rightIsSpecialSpanish = isSpecialSpanish(right);
    if (leftIsSpecialSpanish && !rightIsSpecialSpanish && right != LATIN_AMERICAN_SPANISH) {
        left = LATIN_AMERICAN_SPANISH;
    } else if (rightIsSpecialSpanish && !leftIsSpecialSpanish && left != LATIN_AMERICAN_SPANISH) {
        right = LATIN_AMERICAN_SPANISH;
    }

    uint32_t request_ancestors[MAX_PARENT_DEPTH+1];
    ssize_t left_right_index;
    // Find the parents of the request, but stop as soon as we saw left or right
    const uint32_t left_and_right[] = {left, right};
    const std::array<uint32_t, 2> left_and_right = {{left, right}};
    const size_t ancestor_count = findAncestors(
            request_ancestors, &left_right_index,
            request, requested_script,
            left_and_right, sizeof(left_and_right)/sizeof(left_and_right[0]));
            left_and_right.data(), left_and_right.size());
    if (left_right_index == 0) { // We saw left earlier
        return 1;
    }
+66 −1
Original line number Diff line number Diff line
@@ -515,16 +515,81 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) {
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "AR", NULL, NULL, &request);
    fillIn("es", "US", NULL, NULL, &config1);
    fillIn("es", NULL, NULL, NULL, &config2);
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "MX", NULL, NULL, &request);
    fillIn("es", "US", NULL, NULL, &config1);
    fillIn("es", NULL, NULL, NULL, &config2);
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "AR", NULL, NULL, &request);
    fillIn("es", "MX", NULL, NULL, &config1);
    fillIn("es", NULL, NULL, NULL, &config2);
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "US", NULL, NULL, &request);
    fillIn("es", "MX", NULL, NULL, &config1);
    fillIn("es", NULL, NULL, NULL, &config2);
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "AR", NULL, NULL, &request);
    fillIn("es", "419", NULL, NULL, &config1);
    fillIn("es", "MX", NULL, NULL, &config2);
    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
    // Spanish locales, es-419 is a closer parent.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "US", NULL, NULL, &request);
    fillIn("es", "419", NULL, NULL, &config1);
    fillIn("es", "MX", NULL, NULL, &config2);
    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
    // Spanish locales, es-419 is a closer parent.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "MX", NULL, NULL, &request);
    fillIn("es", "419", NULL, NULL, &config1);
    fillIn("es", "US", NULL, NULL, &config2);
    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
    // Spanish locales, es-419 is a closer parent.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "AR", NULL, NULL, &request);
    fillIn("es", "MX", NULL, NULL, &config1);
    fillIn("es", "BO", NULL, NULL, &config2);
    // A representative locale is better if they are equidistant.
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "AR", NULL, NULL, &request);
    fillIn("es", "US", NULL, NULL, &config1);
    fillIn("es", "BO", NULL, NULL, &config2);
    // Special case for Latin American Spanish: es-MX and es-US are
    // pseudo-parents of all Latin Ameircan Spanish locales.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));

    fillIn("es", "IC", NULL, NULL, &request);
    fillIn("es", "ES", NULL, NULL, &config1);
    fillIn("es", "GQ", NULL, NULL, &config2);
    // A representative locale is better if they are equidistant.
    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));