summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roozbeh Pournader <roozbeh@google.com> 2017-01-10 15:24:32 -0800
committer Roozbeh Pournader <roozbeh@google.com> 2017-01-11 15:52:08 -0800
commita192a8ced65ceea8bfe44f0eac6a394cbf80d936 (patch)
treee8188ef2b11a50e0052cb7af40749fadad32dcea
parent51d34bf06aa67ab5b29c5541e777505ad37907d0 (diff)
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
-rw-r--r--libs/androidfw/LocaleData.cpp31
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp67
2 files changed, 93 insertions, 5 deletions
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 038ef5839fe2..889d166d853b 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -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;
}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 6e998159e554..86a627e1485d 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -516,15 +516,80 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) {
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));