Commit 796c5439 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Language-specific LocalizationDelegates (#12645)

parent a554401e
......@@ -26,6 +26,9 @@ class _StocksLocalizationsDelegate extends LocalizationsDelegate<StockStrings> {
@override
Future<StockStrings> load(Locale locale) => StockStrings.load(locale);
@override
bool isSupported(Locale locale) => locale.languageCode == 'es' || locale.languageCode == 'en';
@override
bool shouldReload(_StocksLocalizationsDelegate old) => false;
}
......
......@@ -187,6 +187,9 @@ abstract class MaterialLocalizations {
class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
const _MaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => locale.languageCode == 'en';
@override
Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
......
......@@ -42,11 +42,12 @@ Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegat
final Map<Type, dynamic> output = <Type, dynamic>{};
List<_Pending> pendingList;
// Only load the first delegate for each delgate type.
// Only load the first delegate for each delegate type that supports
// locale.languageCode.
final Set<Type> types = new Set<Type>();
final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[];
for (LocalizationsDelegate<dynamic> delegate in allDelegates) {
if (!types.contains(delegate.type)) {
if (!types.contains(delegate.type) && delegate.isSupported(locale)) {
types.add(delegate.type);
delegates.add(delegate);
}
......@@ -97,6 +98,12 @@ abstract class LocalizationsDelegate<T> {
/// const constructors so that they can be used in const expressions.
const LocalizationsDelegate();
/// Whether resources for the given locale can be loaded by this delegate.
///
/// Return true if the instance of `T` loaded by this delegate's [load]
/// method supports the given `locale`'s language.
bool isSupported(Locale locale);
/// Start loading the resources for `locale`. The returned future completes
/// when the resources have finished loading.
///
......@@ -164,6 +171,11 @@ abstract class WidgetsLocalizations {
class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const _WidgetsLocalizationsDelegate();
// This is convenient simplification. It would be more correct test if the locale's
// text-direction is LTR.
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
......
......@@ -28,6 +28,9 @@ class MockClipboard {
}
class MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
@override
bool isSupported(Locale locale) => true;
@override
Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
......@@ -36,6 +39,9 @@ class MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocali
}
class WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
......
......@@ -423,6 +423,27 @@ void _loadDateIntlDataIfNotLoaded() {
class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
const _MaterialLocalizationsDelegate();
static const List<String> _supportedLanguages = const <String>[
'ar', // Arabic
'de', // German
'en', // English
'es', // Spanish
'fa', // Farsi
'fr', // French
'he', // Hebrew
'it', // Italian
'ja', // Japanese
'ps', // Pashto
'pt', // Portugese
'ru', // Russian
'sd', // Sindhi
'ur', // Urdu
'zh', // Simplified Chinese
];
@override
bool isSupported(Locale locale) => _supportedLanguages.contains(locale.languageCode);
@override
Future<MaterialLocalizations> load(Locale locale) => GlobalMaterialLocalizations.load(locale);
......
......@@ -67,6 +67,9 @@ class GlobalWidgetsLocalizations implements WidgetsLocalizations {
class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const _WidgetsLocalizationsDelegate();
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) => GlobalWidgetsLocalizations.load(locale);
......
......@@ -55,6 +55,9 @@ class _DummyLocalizationsDelegate extends LocalizationsDelegate<DummyLocalizatio
@override
Future<DummyLocalizations> load(Locale locale) async => new DummyLocalizations();
@override
bool isSupported(Locale locale) => true;
@override
bool shouldReload(_DummyLocalizationsDelegate old) => true;
}
......
......@@ -8,18 +8,29 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
class FooMaterialLocalizations extends GlobalMaterialLocalizations {
FooMaterialLocalizations(Locale locale) : super(locale);
FooMaterialLocalizations(Locale locale, this.backButtonTooltip) : super(locale);
@override
String get backButtonTooltip => 'foo';
final String backButtonTooltip;
}
class FooMaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
const FooMaterialLocalizationsDelegate();
const FooMaterialLocalizationsDelegate({
this.supportedLanguage: 'en',
this.backButtonTooltip: 'foo'
});
final String supportedLanguage;
final String backButtonTooltip;
@override
bool isSupported(Locale locale) {
return supportedLanguage == 'allLanguages' ? true : locale.languageCode == supportedLanguage;
}
@override
Future<FooMaterialLocalizations> load(Locale locale) {
return new SynchronousFuture<FooMaterialLocalizations>(new FooMaterialLocalizations(locale));
return new SynchronousFuture<FooMaterialLocalizations>(new FooMaterialLocalizations(locale, backButtonTooltip));
}
@override
......@@ -143,7 +154,42 @@ void main() {
expect(find.text('Back'), findsOneWidget);
});
testWidgets('MaterialApp overrides MaterialLocalizations', (WidgetTester tester) async {
testWidgets('MaterialApp adds MaterialLocalizations for additional languages', (WidgetTester tester) async {
final Key textKey = new UniqueKey();
await tester.pumpWidget(
buildFrame(
delegates: <FooMaterialLocalizationsDelegate>[
const FooMaterialLocalizationsDelegate(supportedLanguage: 'fr', backButtonTooltip: 'FR'),
const FooMaterialLocalizationsDelegate(supportedLanguage: 'de', backButtonTooltip: 'DE'),
],
supportedLocales: const <Locale>[
const Locale('en', ''),
const Locale('fr', ''),
const Locale('de', ''),
],
buildContent: (BuildContext context) {
// Should always be 'foo', no matter what the locale is
return new Text(
MaterialLocalizations.of(context).backButtonTooltip,
key: textKey,
);
}
)
);
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
await tester.binding.setLocale('fr', 'CA');
await tester.pump();
expect(find.text('FR'), findsOneWidget);
await tester.binding.setLocale('de', 'DE');
await tester.pump();
expect(find.text('DE'), findsOneWidget);
});
testWidgets('MaterialApp overrides MaterialLocalizations for all locales', (WidgetTester tester) async {
final Key textKey = new UniqueKey();
await tester.pumpWidget(
......@@ -151,7 +197,7 @@ void main() {
// Accept whatever locale we're given
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
delegates: <FooMaterialLocalizationsDelegate>[
const FooMaterialLocalizationsDelegate(),
const FooMaterialLocalizationsDelegate(supportedLanguage: 'allLanguages'),
],
buildContent: (BuildContext context) {
// Should always be 'foo', no matter what the locale is
......@@ -174,6 +220,38 @@ void main() {
expect(find.text('foo'), findsOneWidget);
});
testWidgets('MaterialApp overrides MaterialLocalizations for default locale', (WidgetTester tester) async {
final Key textKey = new UniqueKey();
await tester.pumpWidget(
buildFrame(
delegates: <FooMaterialLocalizationsDelegate>[
const FooMaterialLocalizationsDelegate(supportedLanguage: 'en'),
],
// supportedLocales not specified, so all locales resolve to 'en'
buildContent: (BuildContext context) {
return new Text(
MaterialLocalizations.of(context).backButtonTooltip,
key: textKey,
);
}
)
);
// Unsupported locale '_' (the widget tester's default) resolves to 'en'.
expect(tester.widget<Text>(find.byKey(textKey)).data, 'foo');
// Unsupported locale 'zh' resolves to 'en'.
await tester.binding.setLocale('zh', 'CN');
await tester.pump();
expect(find.text('foo'), findsOneWidget);
// Unsupported locale 'de' resolves to 'en'.
await tester.binding.setLocale('de', 'DE');
await tester.pump();
expect(find.text('foo'), findsOneWidget);
});
testWidgets('deprecated Android/Java locales are modernized', (WidgetTester tester) async {
final Key textKey = new UniqueKey();
......
......@@ -35,6 +35,9 @@ class SyncTestLocalizationsDelegate extends LocalizationsDelegate<TestLocalizati
final String prefix; // Changing this value triggers a rebuild
final List<bool> shouldReloadValues = <bool>[];
@override
bool isSupported(Locale locale) => true;
@override
Future<TestLocalizations> load(Locale locale) => TestLocalizations.loadSync(locale, prefix);
......@@ -54,6 +57,9 @@ class AsyncTestLocalizationsDelegate extends LocalizationsDelegate<TestLocalizat
final String prefix; // Changing this value triggers a rebuild
final List<bool> shouldReloadValues = <bool>[];
@override
bool isSupported(Locale locale) => true;
@override
Future<TestLocalizations> load(Locale locale) => TestLocalizations.loadAsync(locale, prefix);
......@@ -92,6 +98,9 @@ class SyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizati
@override
Future<MoreLocalizations> load(Locale locale) => MoreLocalizations.loadSync(locale);
@override
bool isSupported(Locale locale) => true;
@override
bool shouldReload(SyncMoreLocalizationsDelegate old) => false;
}
......@@ -100,6 +109,9 @@ class AsyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizat
@override
Future<MoreLocalizations> load(Locale locale) => MoreLocalizations.loadAsync(locale);
@override
bool isSupported(Locale locale) => true;
@override
bool shouldReload(AsyncMoreLocalizationsDelegate old) => false;
}
......@@ -112,6 +124,9 @@ class OnlyRTLDefaultWidgetsLocalizations extends DefaultWidgetsLocalizations {
class OnlyRTLDefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const OnlyRTLDefaultWidgetsLocalizationsDelegate();
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) {
return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment