Localizations overrides (#12094)
This commit is contained in:
parent
dc47238602
commit
9fb6fd81b9
@ -75,19 +75,15 @@ String generateLocalizationsMap() {
|
||||
/// This variable is used by [MaterialLocalizations].
|
||||
const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {''');
|
||||
|
||||
final String lastLocale = localeToResources.keys.last;
|
||||
for (String locale in localeToResources.keys.toList()..sort()) {
|
||||
output.writeln(' "$locale": const <String, String>{');
|
||||
|
||||
final Map<String, String> resources = localeToResources[locale];
|
||||
final String lastName = resources.keys.last;
|
||||
for (String name in resources.keys) {
|
||||
final String comma = name == lastName ? "" : ",";
|
||||
final String value = generateString(resources[name]);
|
||||
output.writeln(' "$name": $value$comma');
|
||||
output.writeln(' "$name": $value,');
|
||||
}
|
||||
final String comma = locale == lastLocale ? "" : ",";
|
||||
output.writeln(' }$comma');
|
||||
output.writeln(' },');
|
||||
}
|
||||
|
||||
output.writeln('};');
|
||||
|
@ -234,6 +234,61 @@ class MaterialApp extends StatefulWidget {
|
||||
///
|
||||
/// The delegates collectively define all of the localized resources
|
||||
/// for this application's [Localizations] widget.
|
||||
///
|
||||
/// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations]
|
||||
/// are included automatically. Apps can provide their own versions of these
|
||||
/// localizations by creating implementations of
|
||||
/// [LocalizationsDelegate<WidgetLocalizations>] or
|
||||
/// [LocalizationsDelegate<MaterialLocalizations>] whose load methods return
|
||||
/// custom versions of [WidgetLocalizations] or [MaterialLocalizations].
|
||||
///
|
||||
/// For example: to add support to [MaterialLocalizations] for a
|
||||
/// locale it doesn't already support, say `const Locale('foo', 'BR')`,
|
||||
/// one could just extend [DefaultMaterialLocalizations]:
|
||||
///
|
||||
/// ```dart
|
||||
/// class FooLocalizations extends DefaultMaterialLocalizations {
|
||||
/// FooLocalizations(Locale locale) : super(locale);
|
||||
/// @override
|
||||
/// String get okButtonLabel {
|
||||
/// if (locale == const Locale('foo', 'BR'))
|
||||
/// return 'foo';
|
||||
/// return super.okButtonLabel;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// A `FooLocalizationsDelegate` is essentially just a method that constructs
|
||||
/// a `FooLocalizations` object. We return a [SynchronousFuture] here because
|
||||
/// no asynchronous work takes place upon "loading" the localizations object.
|
||||
///
|
||||
/// ```dart
|
||||
/// class FooLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
|
||||
/// const FooLocalizationsDelegate();
|
||||
/// @override
|
||||
/// Future<FooLocalizations> load(Locale locale) {
|
||||
/// return new SynchronousFuture(new FooLocalizations(locale));
|
||||
/// }
|
||||
/// @override
|
||||
/// bool shouldReload(FooLocalizationsDelegate old) => false;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Constructing a [MaterialApp] with a `FooLocalizationsDelegate` overrides
|
||||
/// the automatically included delegate for [MaterialLocalizations] because
|
||||
/// only the first delegate of each [LocalizationsDelegate.type] is used and
|
||||
/// the automatically included delegates are added to the end of the app's
|
||||
/// [localizationsDelegates] list.
|
||||
///
|
||||
/// ```dart
|
||||
/// new MaterialApp(
|
||||
/// localizationsDelegates: [
|
||||
/// const FooLocalizationsDelegate(),
|
||||
/// ],
|
||||
/// // ...
|
||||
/// )
|
||||
/// ```
|
||||
final Iterable<LocalizationsDelegate<dynamic>> localizationsDelegates;
|
||||
|
||||
/// This callback is responsible for choosing the app's locale
|
||||
@ -379,11 +434,14 @@ class _MaterialAppState extends State<MaterialApp> {
|
||||
}
|
||||
|
||||
// Combine the Localizations for Material with the ones contributed
|
||||
// by the localizationsDelegates parameter, if any.
|
||||
// by the localizationsDelegates parameter, if any. Only the first delegate
|
||||
// of a particular LocalizationsDelegate.type is loaded so the
|
||||
// localizationsDelegate parameter can be used to override
|
||||
// _MaterialLocalizationsDelegate.
|
||||
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
|
||||
yield const _MaterialLocalizationsDelegate(); // TODO(ianh): make this configurable
|
||||
if (widget.localizationsDelegates != null)
|
||||
yield* widget.localizationsDelegates;
|
||||
yield const _MaterialLocalizationsDelegate();
|
||||
}
|
||||
|
||||
RectTween _createRectTween(Rect begin, Rect end) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
// This file has been automatically generated. Please do not edit it manually.
|
||||
// To regenerate the file, use:
|
||||
// dart dev/tools/gen_localizations.dart lib/src/material/i18n material
|
||||
// dart dev/tools/gen_localizations.dart packages/flutter/lib/src/material/i18n material
|
||||
|
||||
/// Maps from [Locale.languageCode] to a map that contains the localized strings
|
||||
/// for that locale.
|
||||
@ -36,7 +36,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"selectAllButtonLabel": r"اختر الكل",
|
||||
"viewLicensesButtonLabel": r"عرض التراخيص",
|
||||
"anteMeridiemAbbreviation": r"ص",
|
||||
"postMeridiemAbbreviation": r"م"
|
||||
"postMeridiemAbbreviation": r"م",
|
||||
},
|
||||
"de": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -63,7 +63,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"EINFÜGEN",
|
||||
"selectAllButtonLabel": r"ALLES AUSWÄHLEN",
|
||||
"viewLicensesButtonLabel": r"LIZENZEN ANZEIGEN"
|
||||
"viewLicensesButtonLabel": r"LIZENZEN ANZEIGEN",
|
||||
},
|
||||
"en": const <String, String>{
|
||||
"timeOfDayFormat": r"h:mm a",
|
||||
@ -92,16 +92,16 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"selectAllButtonLabel": r"SELECT ALL",
|
||||
"viewLicensesButtonLabel": r"VIEW LICENSES",
|
||||
"anteMeridiemAbbreviation": r"AM",
|
||||
"postMeridiemAbbreviation": r"PM"
|
||||
"postMeridiemAbbreviation": r"PM",
|
||||
},
|
||||
"en_GB": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm"
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
},
|
||||
"en_IE": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm"
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
},
|
||||
"en_ZA": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm"
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
},
|
||||
"es": const <String, String>{
|
||||
"timeOfDayFormat": r"H:mm",
|
||||
@ -128,10 +128,10 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"PEGAR",
|
||||
"selectAllButtonLabel": r"SELECCIONAR TODO",
|
||||
"viewLicensesButtonLabel": r"VER LICENCIAS"
|
||||
"viewLicensesButtonLabel": r"VER LICENCIAS",
|
||||
},
|
||||
"es_US": const <String, String>{
|
||||
"timeOfDayFormat": r"h:mm a"
|
||||
"timeOfDayFormat": r"h:mm a",
|
||||
},
|
||||
"fa": const <String, String>{
|
||||
"timeOfDayFormat": r"H:mm",
|
||||
@ -156,7 +156,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"تایید",
|
||||
"pasteButtonLabel": r"چسباندن",
|
||||
"selectAllButtonLabel": r"انتخاب همه",
|
||||
"viewLicensesButtonLabel": r"مشاهده مجوز"
|
||||
"viewLicensesButtonLabel": r"مشاهده مجوز",
|
||||
},
|
||||
"fr": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -183,10 +183,10 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"COLLER",
|
||||
"selectAllButtonLabel": r"TOUT SÉLECTIONNER",
|
||||
"viewLicensesButtonLabel": r"AFFICHER LES LICENCES"
|
||||
"viewLicensesButtonLabel": r"AFFICHER LES LICENCES",
|
||||
},
|
||||
"fr_CA": const <String, String>{
|
||||
"timeOfDayFormat": r"HH 'h' mm"
|
||||
"timeOfDayFormat": r"HH 'h' mm",
|
||||
},
|
||||
"he": const <String, String>{
|
||||
"timeOfDayFormat": r"H:mm",
|
||||
@ -211,7 +211,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"בסדר",
|
||||
"pasteButtonLabel": r"הדבק",
|
||||
"selectAllButtonLabel": r"בחר הכל",
|
||||
"viewLicensesButtonLabel": r"ראה רישיונות"
|
||||
"viewLicensesButtonLabel": r"ראה רישיונות",
|
||||
},
|
||||
"it": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -236,7 +236,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"INCOLLA",
|
||||
"selectAllButtonLabel": r"SELEZIONA TUTTO",
|
||||
"viewLicensesButtonLabel": r"VEDI LE LICENZE"
|
||||
"viewLicensesButtonLabel": r"VEDI LE LICENZE",
|
||||
},
|
||||
"ja": const <String, String>{
|
||||
"timeOfDayFormat": r"H:mm",
|
||||
@ -261,7 +261,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"貼付け",
|
||||
"selectAllButtonLabel": r"全選択",
|
||||
"viewLicensesButtonLabel": r"ライセンス表記"
|
||||
"viewLicensesButtonLabel": r"ライセンス表記",
|
||||
},
|
||||
"ps": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -286,7 +286,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"سمه ده",
|
||||
"pasteButtonLabel": r"پیټ کړئ",
|
||||
"selectAllButtonLabel": r"غوره کړئ",
|
||||
"viewLicensesButtonLabel": r"لیدلس وګورئ"
|
||||
"viewLicensesButtonLabel": r"لیدلس وګورئ",
|
||||
},
|
||||
"pt": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -311,7 +311,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"OK",
|
||||
"pasteButtonLabel": r"COLAR",
|
||||
"selectAllButtonLabel": r"SELECIONAR TUDO",
|
||||
"viewLicensesButtonLabel": r"VER LICENÇAS"
|
||||
"viewLicensesButtonLabel": r"VER LICENÇAS",
|
||||
},
|
||||
"ru": const <String, String>{
|
||||
"timeOfDayFormat": r"H:mm",
|
||||
@ -336,7 +336,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"ОК",
|
||||
"pasteButtonLabel": r"Паст",
|
||||
"selectAllButtonLabel": r"Выбрать все",
|
||||
"viewLicensesButtonLabel": r"ПРОСМОТРЕТЬ ЛИЦЕНЗИИ"
|
||||
"viewLicensesButtonLabel": r"ПРОСМОТРЕТЬ ЛИЦЕНЗИИ",
|
||||
},
|
||||
"sd": const <String, String>{
|
||||
"timeOfDayFormat": r"HH:mm",
|
||||
@ -361,7 +361,7 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"okButtonLabel": r"ٺيڪ آهي",
|
||||
"pasteButtonLabel": r"پيسٽ ڪريو",
|
||||
"selectAllButtonLabel": r"سڀ چونڊيو",
|
||||
"viewLicensesButtonLabel": r"لائسنس ڏسو"
|
||||
"viewLicensesButtonLabel": r"لائسنس ڏسو",
|
||||
},
|
||||
"ur": const <String, String>{
|
||||
"timeOfDayFormat": r"h:mm a",
|
||||
@ -388,12 +388,12 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"selectAllButtonLabel": r"تکاپیمام منتخب کریں",
|
||||
"viewLicensesButtonLabel": r"لائسنس دیکھیں",
|
||||
"anteMeridiemAbbreviation": r"AM",
|
||||
"postMeridiemAbbreviation": r"PM"
|
||||
"postMeridiemAbbreviation": r"PM",
|
||||
},
|
||||
"zh": const <String, String>{
|
||||
"timeOfDayFormat": r"ah:mm",
|
||||
"openAppDrawerTooltip": r"打开导航菜单",
|
||||
"backButtonTooltip": r"背部",
|
||||
"backButtonTooltip": r"返回",
|
||||
"closeButtonTooltip": r"关",
|
||||
"nextMonthTooltip": r"-下月就29了。",
|
||||
"previousMonthTooltip": r"前一个月",
|
||||
@ -415,7 +415,6 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
|
||||
"selectAllButtonLabel": r"全选",
|
||||
"viewLicensesButtonLabel": r"查看许可证",
|
||||
"anteMeridiemAbbreviation": r"上午",
|
||||
"postMeridiemAbbreviation": r"下午"
|
||||
}
|
||||
"postMeridiemAbbreviation": r"下午",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
"pasteButtonLabel": "粘贴",
|
||||
"selectAllButtonLabel": "全选",
|
||||
"viewLicensesButtonLabel": "查看许可证",
|
||||
"backButtonTooltip": "背部",
|
||||
"closeButtonTooltip": "关",
|
||||
"nextMonthTooltip": "-下月就29了。",
|
||||
"previousMonthTooltip": "前一个月",
|
||||
|
@ -116,24 +116,20 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
|
||||
///
|
||||
/// [LocalizationsDelegate] implementations typically call the static [load]
|
||||
/// function, rather than constructing this class directly.
|
||||
factory DefaultMaterialLocalizations(Locale locale) {
|
||||
DefaultMaterialLocalizations(this.locale) {
|
||||
assert(locale != null);
|
||||
|
||||
final Map<String, String> result = <String, String>{};
|
||||
if (localizations.containsKey(locale.languageCode))
|
||||
result.addAll(localizations[locale.languageCode]);
|
||||
if (localizations.containsKey(locale.toString()))
|
||||
result.addAll(localizations[locale.toString()]);
|
||||
return new DefaultMaterialLocalizations._(locale, result);
|
||||
_nameToValue.addAll(localizations[locale.languageCode]);
|
||||
if (localizations.containsKey(_localeName))
|
||||
_nameToValue.addAll(localizations[_localeName]);
|
||||
}
|
||||
|
||||
DefaultMaterialLocalizations._(this.locale, this._nameToValue);
|
||||
|
||||
/// The locale for which the values of this class's localized resources
|
||||
/// have been translated.
|
||||
final Locale locale;
|
||||
|
||||
final Map<String, String> _nameToValue;
|
||||
final Map<String, String> _nameToValue = <String, String>{};
|
||||
|
||||
String get _localeName {
|
||||
final String localeName = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
|
||||
|
@ -383,11 +383,14 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
}
|
||||
|
||||
// Combine the Localizations for Widgets with the ones contributed
|
||||
// by the localizationsDelegates parameter, if any.
|
||||
// by the localizationsDelegates parameter, if any. Only the first delegate
|
||||
// of a particular LocalizationsDelegate.type is loaded so the
|
||||
// localizationsDelegate parameter can be used to override
|
||||
// _WidgetsLocalizationsDelegate.
|
||||
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
|
||||
yield const _WidgetsLocalizationsDelegate(); // TODO(ianh): make this configurable
|
||||
if (widget.localizationsDelegates != null)
|
||||
yield* widget.localizationsDelegates;
|
||||
yield const _WidgetsLocalizationsDelegate();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -38,10 +38,20 @@ class _Pending {
|
||||
// This is more complicated than just applying Future.wait to input
|
||||
// because some of the input.values may be SynchronousFutures. We don't want
|
||||
// to Future.wait for the synchronous futures.
|
||||
Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegate<dynamic>> delegates) {
|
||||
Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegate<dynamic>> allDelegates) {
|
||||
final Map<Type, dynamic> output = <Type, dynamic>{};
|
||||
List<_Pending> pendingList;
|
||||
|
||||
// Only load the first delegate for each delgate type.
|
||||
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)) {
|
||||
types.add(delegate.type);
|
||||
delegates.add(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalizationsDelegate<dynamic> delegate in delegates) {
|
||||
final Future<dynamic> inputValue = delegate.load(locale);
|
||||
dynamic completedValue;
|
||||
@ -227,6 +237,9 @@ class _LocalizationsScope extends InheritedWidget {
|
||||
/// class _MyDelegate extends LocalizationsDelegate<MyLocalizations> {
|
||||
/// @override
|
||||
/// Future<MyLocalizations> load(Locale locale) => MyLocalizations.load(locale);
|
||||
///
|
||||
/// @override
|
||||
/// bool shouldReload(MyLocalizationsDelegate old) => false;
|
||||
///}
|
||||
/// ```
|
||||
///
|
||||
@ -298,8 +311,7 @@ class _LocalizationsScope extends InheritedWidget {
|
||||
/// One could choose another approach for loading localized resources and looking them up while
|
||||
/// still conforming to the structure of this example.
|
||||
class Localizations extends StatefulWidget {
|
||||
/// Create a widget from which ambient localizations (translated strings)
|
||||
/// can be obtained.
|
||||
/// Create a widget from which localizations (like translated strings) can be obtained.
|
||||
Localizations({
|
||||
Key key,
|
||||
@required this.locale,
|
||||
@ -311,6 +323,51 @@ class Localizations extends StatefulWidget {
|
||||
assert(delegates.any((LocalizationsDelegate<dynamic> delegate) => delegate is LocalizationsDelegate<WidgetsLocalizations>));
|
||||
}
|
||||
|
||||
/// Overrides the inherited [Locale] or [LocalizationsDelegate]s for `child`.
|
||||
///
|
||||
/// This factory constructor is used for the (usually rare) situtation where part
|
||||
/// of an app should be localized for a different locale than the one defined
|
||||
/// for the device, or if its localizations should come from a different list
|
||||
/// of [LocalizationsDelegate]s than the list defined by
|
||||
/// [WidgetsApp.localizationsDelegates].
|
||||
///
|
||||
/// For example you could specify that `myWidget` was only to be localized for
|
||||
/// the US English locale:
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return new Localizations.override(
|
||||
/// context: context,
|
||||
/// locale: const Locale('en', 'US'),
|
||||
/// child: myWidget,
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `locale` and `delegates` parameters default to the [Localizations.locale]
|
||||
/// and [Localizations.delegates] values from the nearest [Localizations] ancestor.
|
||||
///
|
||||
/// To override the [Localizations.locale] or [Localizations.delegates] for an
|
||||
/// entire app, specify [WidgetsApp.locale] or [WidgetsApp.localizationsDelegates]
|
||||
/// (or specify the same parameters for [MaterialApp]).
|
||||
factory Localizations.override({
|
||||
Key key,
|
||||
@required BuildContext context,
|
||||
Locale locale,
|
||||
List<LocalizationsDelegate<dynamic>> delegates,
|
||||
Widget child,
|
||||
}) {
|
||||
final List<LocalizationsDelegate<dynamic>> mergedDelegates = Localizations._delegatesOf(context);
|
||||
if (delegates != null)
|
||||
mergedDelegates.insertAll(0, delegates);
|
||||
return new Localizations(
|
||||
key: key,
|
||||
locale: locale ?? Localizations.localeOf(context),
|
||||
delegates: mergedDelegates,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
/// The resources returned by [Localizations.of] will be specific to this locale.
|
||||
final Locale locale;
|
||||
|
||||
@ -326,9 +383,19 @@ class Localizations extends StatefulWidget {
|
||||
static Locale localeOf(BuildContext context) {
|
||||
assert(context != null);
|
||||
final _LocalizationsScope scope = context.inheritFromWidgetOfExactType(_LocalizationsScope);
|
||||
assert(scope != null, 'a Localizations ancestor was not found');
|
||||
return scope.localizationsState.locale;
|
||||
}
|
||||
|
||||
// There doesn't appear to be a need to make this public. See the
|
||||
// Localizations.override factory constructor.
|
||||
static List<LocalizationsDelegate<dynamic>> _delegatesOf(BuildContext context) {
|
||||
assert(context != null);
|
||||
final _LocalizationsScope scope = context.inheritFromWidgetOfExactType(_LocalizationsScope);
|
||||
assert(scope != null, 'a Localizations ancestor was not found');
|
||||
return new List<LocalizationsDelegate<dynamic>>.from(scope.localizationsState.widget.delegates);
|
||||
}
|
||||
|
||||
/// Returns the 'type' localized resources for the widget tree that
|
||||
/// corresponds to [BuildContext] `context`.
|
||||
///
|
||||
@ -345,6 +412,7 @@ class Localizations extends StatefulWidget {
|
||||
assert(context != null);
|
||||
assert(type != null);
|
||||
final _LocalizationsScope scope = context.inheritFromWidgetOfExactType(_LocalizationsScope);
|
||||
assert(scope != null, 'a Localizations ancestor was not found');
|
||||
return scope.localizationsState.resourcesFor<T>(type);
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,33 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class FooMaterialLocalizations extends DefaultMaterialLocalizations {
|
||||
FooMaterialLocalizations(Locale locale) : super(locale);
|
||||
|
||||
@override
|
||||
String get backButtonTooltip => 'foo';
|
||||
}
|
||||
|
||||
class FooMaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
|
||||
const FooMaterialLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<FooMaterialLocalizations> load(Locale locale) {
|
||||
return new SynchronousFuture<FooMaterialLocalizations>(new FooMaterialLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(FooMaterialLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
Widget buildFrame({
|
||||
Locale locale,
|
||||
Iterable<LocalizationsDelegate<dynamic>> delegates,
|
||||
WidgetBuilder buildContent,
|
||||
LocaleResolutionCallback localeResolutionCallback,
|
||||
Iterable<Locale> supportedLocales: const <Locale>[
|
||||
const Locale('en', 'US'),
|
||||
const Locale('es', 'es'),
|
||||
@ -16,6 +38,8 @@ Widget buildFrame({
|
||||
return new MaterialApp(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
locale: locale,
|
||||
localizationsDelegates: delegates,
|
||||
localeResolutionCallback: localeResolutionCallback,
|
||||
supportedLocales: supportedLocales,
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return new MaterialPageRoute<Null>(
|
||||
@ -133,6 +157,101 @@ void main() {
|
||||
expect(localizations.selectedRowCountTitle(123456789), '123.456.789 artículos seleccionados');
|
||||
});
|
||||
|
||||
testWidgets('Localizations.override widget tracks parent\'s locale', (WidgetTester tester) async {
|
||||
Widget buildLocaleFrame(Locale locale) {
|
||||
return buildFrame(
|
||||
locale: locale,
|
||||
buildContent: (BuildContext context) {
|
||||
return new Localizations.override(
|
||||
context: context,
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
// No MaterialLocalizations are defined for the first Localizations
|
||||
// ancestor, so we should get the values from the default one, i.e.
|
||||
// the one created by WidgetsApp via the LocalizationsDelegate
|
||||
// provided by MaterialApp.
|
||||
return new Text(MaterialLocalizations.of(context).backButtonTooltip);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US')));
|
||||
expect(find.text('Back'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('de', 'DE')));
|
||||
expect(find.text('Zurück'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('zh', 'CN')));
|
||||
expect(find.text('返回'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Localizations.override widget with hardwired locale', (WidgetTester tester) async {
|
||||
Widget buildLocaleFrame(Locale locale) {
|
||||
return buildFrame(
|
||||
locale: locale,
|
||||
buildContent: (BuildContext context) {
|
||||
return new Localizations.override(
|
||||
context: context,
|
||||
locale: const Locale('en', 'US'),
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
// No MaterialLocalizations are defined for the Localizations.override
|
||||
// ancestor, so we should get all values from the default one, i.e.
|
||||
// the one created by WidgetsApp via the LocalizationsDelegate
|
||||
// provided by MaterialApp.
|
||||
return new Text(MaterialLocalizations.of(context).backButtonTooltip);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US')));
|
||||
expect(find.text('Back'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('de', 'DE')));
|
||||
expect(find.text('Back'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(buildLocaleFrame(const Locale('zh', 'CN')));
|
||||
expect(find.text('Back'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('MaterialApp overrides MaterialLocalizations', (WidgetTester tester) async {
|
||||
final Key textKey = new UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildFrame(
|
||||
// Accept whatever locale we're given
|
||||
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
|
||||
delegates: <FooMaterialLocalizationsDelegate>[
|
||||
const FooMaterialLocalizationsDelegate(),
|
||||
],
|
||||
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, 'foo');
|
||||
|
||||
await tester.binding.setLocale('zh', 'CN');
|
||||
await tester.pump();
|
||||
expect(find.text('foo'), findsOneWidget);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -103,6 +103,36 @@ class AsyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizat
|
||||
bool shouldReload(AsyncMoreLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
// Same as _WidgetsLocalizationsDelegate in widgets/app.dart
|
||||
class DefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
|
||||
const DefaultWidgetsLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
|
||||
|
||||
@override
|
||||
bool shouldReload(DefaultWidgetsLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
class OnlyRTLDefaultWidgetsLocalizations extends DefaultWidgetsLocalizations {
|
||||
OnlyRTLDefaultWidgetsLocalizations(Locale locale) : super(locale);
|
||||
|
||||
@override
|
||||
TextDirection get textDirection => TextDirection.rtl;
|
||||
}
|
||||
|
||||
class OnlyRTLDefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
|
||||
const OnlyRTLDefaultWidgetsLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<WidgetsLocalizations> load(Locale locale) {
|
||||
return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(OnlyRTLDefaultWidgetsLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
Widget buildFrame({
|
||||
Locale locale,
|
||||
Iterable<LocalizationsDelegate<dynamic>> delegates,
|
||||
@ -504,15 +534,116 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('zh_CN'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Localizations.override widget tracks parent\'s locale and delegates', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildFrame(
|
||||
// Accept whatever locale we're given
|
||||
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
|
||||
buildContent: (BuildContext context) {
|
||||
return new Localizations.override(
|
||||
context: context,
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
final Locale locale = Localizations.localeOf(context);
|
||||
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
|
||||
return new Text('$locale $direction');
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Same as _WidgetsLocalizationsDelegate in widgets/app.dart
|
||||
class DefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
|
||||
const DefaultWidgetsLocalizationsDelegate();
|
||||
// Initial WidgetTester locale is new Locale('', '')
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('_ TextDirection.ltr'), findsOneWidget);
|
||||
|
||||
@override
|
||||
Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
|
||||
await tester.binding.setLocale('en', 'CA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('en_CA TextDirection.ltr'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('ar', 'EG');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('da', 'DA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('da_DA TextDirection.ltr'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Localizations.override widget overrides parent\'s DefaultWidgetLocalizations', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildFrame(
|
||||
// Accept whatever locale we're given
|
||||
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
|
||||
buildContent: (BuildContext context) {
|
||||
return new Localizations.override(
|
||||
context: context,
|
||||
delegates: <OnlyRTLDefaultWidgetsLocalizationsDelegate>[
|
||||
// Override: no matter what the locale, textDirection is always RTL.
|
||||
const OnlyRTLDefaultWidgetsLocalizationsDelegate(),
|
||||
],
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
final Locale locale = Localizations.localeOf(context);
|
||||
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
|
||||
return new Text('$locale $direction');
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Initial WidgetTester locale is new Locale('', '')
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('_ TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('en', 'CA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('en_CA TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('ar', 'EG');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('da', 'DA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('da_DA TextDirection.rtl'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('WidgetsApp overrides DefaultWidgetLocalizations', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildFrame(
|
||||
// Accept whatever locale we're given
|
||||
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
|
||||
delegates: <OnlyRTLDefaultWidgetsLocalizationsDelegate>[
|
||||
const OnlyRTLDefaultWidgetsLocalizationsDelegate(),
|
||||
],
|
||||
buildContent: (BuildContext context) {
|
||||
final Locale locale = Localizations.localeOf(context);
|
||||
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
|
||||
return new Text('$locale $direction');
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Initial WidgetTester locale is new Locale('', '')
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('_ TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('en', 'CA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('en_CA TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('ar', 'EG');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
|
||||
|
||||
await tester.binding.setLocale('da', 'DA');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('da_DA TextDirection.rtl'), findsOneWidget);
|
||||
});
|
||||
|
||||
@override
|
||||
bool shouldReload(DefaultWidgetsLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user