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-13 01:54:01 +0000
commitcf246af30d007050f8b16bbbae6fbdf8d0559cca (patch)
tree57b8756537cb6f80af07c44e04602975511dff8c
parent5ee87b5b6a5e4ec1ba0272b50e3dfeef8a8382ab (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 (cherry picked from commit a192a8ced65ceea8bfe44f0eac6a394cbf80d936)
-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 2bf9b12b6ce5..10f4d46058ec 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -470,15 +470,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));