Skip to content

Commit

Permalink
Android native locale resolution algorithm (flutter#19266)
Browse files Browse the repository at this point in the history
  • Loading branch information
GaryQian committed Jun 26, 2020
1 parent 5d2a22d commit 559d93d
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 8 deletions.
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ action("robolectric_tests") {
"test/io/flutter/plugin/common/StandardMethodCodecTest.java",
"test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java",
"test/io/flutter/plugin/editing/TextInputPluginTest.java",
"test/io/flutter/plugin/localization/LocalizationPluginTest.java",
"test/io/flutter/plugin/mouse/MouseCursorPluginTest.java",
"test/io/flutter/plugin/platform/PlatformPluginTest.java",
"test/io/flutter/plugin/platform/SingleViewPresentationTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,25 @@ public LocalizationPlugin(
this.localizationChannel = localizationChannel;
}

/**
* Computes the {@link Locale} in supportedLocales that best matches the user's preferred locales.
*
* <p>FlutterEngine must be non-null when this method is invoked.
*/
@SuppressWarnings("deprecation")
public Locale resolveNativeLocale(List<Locale> supportedLocales) {
Locale platformResolvedLocale = null;
if (supportedLocales == null || supportedLocales.isEmpty()) {
return null;
}

// Android improved the localization resolution algorithms after API 24 (7.0, Nougat).
// See https://developer.android.com/guide/topics/resources/multilingual-support
//
// LanguageRange and Locale.lookup was added in API 26 and is the preferred way to
// select a locale. Pre-API 26, we implement a manual locale resolution.
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// Modern locale resolution using LanguageRange
// https://developer.android.com/guide/topics/resources/multilingual-support#postN
List<Locale.LanguageRange> languageRanges = new ArrayList<>();
LocaleList localeList = context.getResources().getConfiguration().getLocales();
int localeCount = localeList.size();
Expand All @@ -37,14 +53,60 @@ public Locale resolveNativeLocale(List<Locale> supportedLocales) {
String localeString = locale.toString();
// This string replacement converts the locale string into the ranges format.
languageRanges.add(new Locale.LanguageRange(localeString.replace("_", "-")));
languageRanges.add(new Locale.LanguageRange(locale.getLanguage()));
languageRanges.add(new Locale.LanguageRange(locale.getLanguage() + "-*"));
}
Locale platformResolvedLocale = Locale.lookup(languageRanges, supportedLocales);
if (platformResolvedLocale != null) {
return platformResolvedLocale;
}
return supportedLocales.get(0);
} else if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
// Modern locale resolution without languageRange
// https://developer.android.com/guide/topics/resources/multilingual-support#postN
LocaleList localeList = context.getResources().getConfiguration().getLocales();
for (int index = 0; index < localeList.size(); ++index) {
Locale preferredLocale = localeList.get(index);
// Look for exact match.
for (Locale locale : supportedLocales) {
if (preferredLocale.equals(locale)) {
return locale;
}
}
// Look for exact language only match.
for (Locale locale : supportedLocales) {
if (preferredLocale.getLanguage().equals(locale.toLanguageTag())) {
return locale;
}
}
// Look for any locale with matching language.
for (Locale locale : supportedLocales) {
if (preferredLocale.getLanguage().equals(locale.getLanguage())) {
return locale;
}
}
}
return supportedLocales.get(0);
}

// TODO(garyq): This should be modified to achieve Android's full
// locale resolution:
// https://developer.android.com/guide/topics/resources/multilingual-support
platformResolvedLocale = Locale.lookup(languageRanges, supportedLocales);
// Legacy locale resolution
// https://developer.android.com/guide/topics/resources/multilingual-support#preN
Locale preferredLocale = context.getResources().getConfiguration().locale;
if (preferredLocale != null) {
// Look for exact match.
for (Locale locale : supportedLocales) {
if (preferredLocale.equals(locale)) {
return locale;
}
}
// Look for exact language only match.
for (Locale locale : supportedLocales) {
if (preferredLocale.getLanguage().equals(locale.toString())) {
return locale;
}
}
}
return platformResolvedLocale;
return supportedLocales.get(0);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/android/test/io/flutter/FlutterTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.flutter.embedding.engine.FlutterEngineCacheTest;
import io.flutter.embedding.engine.FlutterEnginePluginRegistryTest;
import io.flutter.embedding.engine.FlutterJNITest;
import io.flutter.embedding.engine.LocalizationPluginTest;
import io.flutter.embedding.engine.RenderingComponentTest;
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
Expand Down Expand Up @@ -52,6 +53,7 @@
FlutterRendererTest.class,
FlutterViewTest.class,
InputConnectionAdaptorTest.class,
LocalizationPluginTest.class,
PlatformPluginTest.class,
PluginComponentTest.class,
PreconditionsTest.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ public void computePlatformResolvedLocaleCallsLocalizationPluginProperly() {
"en", "CA", ""
};
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 0); // This should change when full algo is implemented.
assertEquals(result.length, 3);
assertEquals(result[0], "en");
assertEquals(result[1], "CA");
assertEquals(result[2], "");

supportedLocales =
new String[] {
Expand Down Expand Up @@ -137,7 +140,11 @@ public void computePlatformResolvedLocaleCallsLocalizationPluginProperly() {
localeList = new LocaleList();
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 0);
// The first locale is default.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
}

public void onDisplayPlatformView__callsPlatformViewsController() {
Expand Down
Loading

0 comments on commit 559d93d

Please sign in to comment.