In practice, a lot of apps provide English resources as their default
resources, so assume that English is always supported.
With this change, users can safely set secondary locales after
English in their preferred locale lists. Such settings would help
the apps (and parts of the system that are aware of multiple locales)
consider the user's familiarity with the secondary locale or the
user's preference for glyphs forms in the secondary locale. This
change makes sure that no app unintentionally switches to such
secondary locales.
Note that this doesn't break from the tradition of previous releases.
Traditionally, if the user had set the system locale to English, they
would get the default resources when the app didn't explicitly
provide English resources. This will continue to be the case where
the user's locale list has English in it before another supported
language. (English will be the first locale in most cases.)
Bug:
26192817
Change-Id: I2c1a003760299157786c1b3f9fb116ce18db3b8c
|| (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) {
if (locales.size() == 1) {
// This is an optimization to avoid the JNI call(s) when the result of
- // getFirstMatch() does not depend on the supported locales.
+ // getFirstMatchWithEnglishSupported() does not depend on the supported locales.
mResolvedLocale = locales.getPrimary();
} else {
String[] supportedLocales = mAssets.getNonSystemLocales();
// their own.
supportedLocales = mAssets.getLocales();
}
- mResolvedLocale = locales.getFirstMatch(supportedLocales);
+ mResolvedLocale = locales.getFirstMatchWithEnglishSupported(supportedLocales);
}
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
return supportedScr.equals(desiredScr) ? 1 : 0;
}
- /**
- * Returns the first match in the locale list given an unordered array of supported locales
- * in BCP47 format.
- *
- * If the locale list is empty, null would be returned.
- */
- @Nullable
- public Locale getFirstMatch(String[] supportedLocales) {
+ private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn");
+
+ private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) {
if (mList.length == 1) { // just one locale, perhaps the most common scenario
return mList[0];
}
return null;
}
int bestIndex = Integer.MAX_VALUE;
- for (String tag : supportedLocales) {
- final Locale supportedLocale = Locale.forLanguageTag(tag);
+ final int numSupportedLocales =
+ supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0);
+ for (int i = 0; i < numSupportedLocales; i++) {
+ final Locale supportedLocale;
+ if (assumeEnglishIsSupported) {
+ // Try English first, so we can return early if it's in the LocaleList
+ supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]);
+ } else {
+ supportedLocale = Locale.forLanguageTag(supportedLocales[i]);
+ }
// We expect the average length of locale lists used for locale resolution to be
// smaller than three, so it's OK to do this as an O(mn) algorithm.
for (int idx = 0; idx < mList.length; idx++) {
}
/**
+ * Returns the first match in the locale list given an unordered array of supported locales
+ * in BCP47 format.
+ *
+ * If the locale list is empty, null would be returned.
+ */
+ @Nullable
+ public Locale getFirstMatch(String[] supportedLocales) {
+ return computeFirstMatch(supportedLocales, false /* assume English is not supported */);
+ }
+
+ /**
+ * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
+ * {@hide}
+ */
+ @Nullable
+ public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
+ return computeFirstMatch(supportedLocales, true /* assume English is supported */);
+ }
+
+ /**
* Returns true if the array of locale tags only contains empty locales and pseudolocales.
* Assumes that there is no repetition in the input.
* {@hide}
$(call all-java-files-under, EnabledTestApp/src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_AAPT_FLAGS = -0 dat -0 gld
+LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
LOCAL_STATIC_JAVA_LIBRARIES := \
core-tests-support \
android-common \
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="dummy_string">رشتهٔ الکی</string>
+</resources>
<!-- RestrictionsManagerTest -->
<string name="restrictionManager_title">Title</string>
<string name="restrictionManager_desc">Description</string>
+
+ <!-- ResourcesLocaleResolutionTest -->
+ <string name="dummy_string">dummy string</string>
</resources>
--- /dev/null
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.content.res;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleResolutionTest extends AndroidTestCase {
+ @SmallTest
+ public void testGetResolvedLocale_englishIsAlwaysConsideredSupported() {
+ // First make sure English has no explicit assets other than the default assets
+ final AssetManager assets = getContext().getAssets();
+ final String supportedLocales[] = assets.getNonSystemLocales();
+ for (String languageTag : supportedLocales) {
+ if ("en-XA".equals(languageTag)) {
+ continue;
+ }
+ assertFalse(
+ "supported locales: " + Arrays.toString(supportedLocales),
+ "en".equals(Locale.forLanguageTag(languageTag).getLanguage()));
+ }
+
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ final Configuration cfg = new Configuration();
+ cfg.setToDefaults();
+ // Avestan and English have no assets, but Persian does.
+ cfg.setLocales(LocaleList.forLanguageTags("ae,en,fa"));
+ Resources res = new Resources(assets, dm, cfg);
+ // We should get English, because it is always considered supported.
+ assertEquals("en", res.getResolvedLocale().toLanguageTag());
+ }
+}
+