Localizations for Material (#11832)

This commit is contained in:
Hans Muller 2017-08-31 07:45:30 -07:00 committed by GitHub
parent 0323f2ddd2
commit 541afae45b
16 changed files with 627 additions and 47 deletions

View file

@ -0,0 +1,127 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Given a directory that contains localized ".arb" (application resource
// bundle) files, generates a Dart "localizations" Map definition that combines
// the contents of the arb files. The map can be used to lookup a localized
// string: localizations[localeString][resourceId].
//
// See *.arb and localizations.dart in packages/flutter/lib/src/material/i18n/.
//
// The arb (JSON) format files must contain a single map indexed by locale.
// Each map value is itself a map with resource identifier keys and localized
// resource string values.
//
// The arb filenames are assumed to end in "prefix_lc.arb" or "prefix_lc_cc.arb",
// where prefix is the 2nd command line argument, lc is a language code and cc
// is the country code. In most cases both codes are just two characters. A typical
// filename would be "material_en.arb".
//
// This app is typically run by hand when a module's .arb files have been
// updated.
//
// Usage: dart gen_localizations.dart directory prefix
import 'dart:convert' show JSON;
import 'dart:io';
const String outputHeader = '''
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file has been automatically generated. Please do not edit it manually.
// To regenerate the file, use:
// @(regenerate)
''';
final Map<String, Map<String, String>> localeToResources = <String, Map<String, String>>{};
// Return s as a Dart-parseable raw string in double quotes. Expand double quotes:
// foo => r"foo"
// foo "bar" => r"foo " '"' r"bar" '"'
String generateString(String s) {
if (!s.contains('"'))
return 'r"$s"';
final StringBuffer output = new StringBuffer();
bool started = false; // Have we started writing a raw string.
for (int i = 0; i < s.length; i++) {
if (s[i] == '"') {
if (started)
output.write('"');
output.write(' \'"\' ');
started = false;
} else if (!started) {
output.write('r"${s[i]}');
started = true;
} else {
output.write(s[i]);
}
}
if (started)
output.write('"');
return output.toString();
}
String generateLocalizationsMap() {
final StringBuffer output = new StringBuffer();
output.writeln('const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {');
final String lastLocale = localeToResources.keys.last;
for (String locale in localeToResources.keys) {
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');
}
final String comma = locale == lastLocale ? "" : ",";
output.writeln(' }$comma');
}
output.writeln('};');
return output.toString();
}
void processBundle(File file, String locale) {
localeToResources[locale] ??= <String, String>{};
final Map<String, String> resources = localeToResources[locale];
final Map<String, dynamic> bundle = JSON.decode(file.readAsStringSync());
for (String key in bundle.keys) {
// The ARB file resource "attributes" for foo are called @foo.
if (key.startsWith('@'))
continue;
resources[key] = bundle[key];
}
}
void main(List<String> args) {
if (args.length != 2)
stderr.writeln('Usage: dart gen_localizations.dart directory prefix');
// filenames are assumed to end in "prefix_lc.arb" or "prefix_lc_cc.arb", where prefix
// is the 2nd command line argument, lc is a language code and cc is the country
// code. In most cases both codes are just two characters.
final Directory directory = new Directory(args[0]);
final String prefix = args[1];
final RegExp filenameRE = new RegExp('${prefix}_(\\w+)\\.arb\$');
for (FileSystemEntity entity in directory.listSync()) {
final String path = entity.path;
if (FileSystemEntity.isFileSync(path) && filenameRE.hasMatch(path)) {
final String locale = filenameRE.firstMatch(path)[1];
processBundle(new File(path), locale);
}
}
final String regenerate = 'dart gen_localizations ${directory.path} ${args[1]}';
print(outputHeader.replaceFirst('@(regenerate)', regenerate));
print(generateLocalizationsMap());
}

View file

@ -26,19 +26,17 @@ const TextStyle _errorTextStyle = const TextStyle(
decorationStyle: TextDecorationStyle.double
);
// Delegate that fetches the default (English) strings.
class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
const _MaterialLocalizationsDelegate();
@override
Future<MaterialLocalizations> load(Locale locale) {
return new SynchronousFuture<MaterialLocalizations>(const MaterialLocalizations());
}
Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
@override
bool shouldReload(_MaterialLocalizationsDelegate old) => false;
}
/// An application that uses material design.
///
/// A convenience widget that wraps a number of widgets that are commonly

View file

@ -0,0 +1,80 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file has been automatically generated. Please do not edit it manually.
// To regenerate the file, use:
// dart gen_localizations packages/flutter/lib/src/material/i18n material
const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {
"ar": const <String, String>{
"openAppDrawerTooltip": r"افتح قائمة التنقل",
"backButtonTooltip": r"الى الخلف",
"closeButtonTooltip": r"إغلا",
"nextMonthTooltip": r"الشهر القادم",
"previousMonthTooltip": r"الشهر الماضى"
},
"it": const <String, String>{
"openAppDrawerTooltip": r"Apri il menu di navigazione",
"backButtonTooltip": r"Indietro",
"closeButtonTooltip": r"Chiudi",
"nextMonthTooltip": r"Il prossimo mese",
"previousMonthTooltip": r"Il mese scorso"
},
"pt": const <String, String>{
"openAppDrawerTooltip": r"Abrir menu de navegação",
"backButtonTooltip": r"Costas",
"closeButtonTooltip": r"Fechar",
"nextMonthTooltip": r"Próximo mês",
"previousMonthTooltip": r"Mês anterior"
},
"es": const <String, String>{
"openAppDrawerTooltip": r"Abrir el menú de navegación",
"backButtonTooltip": r"Espalda",
"closeButtonTooltip": r"Cerrar",
"nextMonthTooltip": r"Próximo mes",
"previousMonthTooltip": r"mes anterior"
},
"fr": const <String, String>{
"openAppDrawerTooltip": r"Ouvrir le menu de navigation",
"backButtonTooltip": r"Arrière",
"closeButtonTooltip": r"Fermer",
"nextMonthTooltip": r"Mois Suivant",
"previousMonthTooltip": r"Le mois précédent"
},
"zh": const <String, String>{
"openAppDrawerTooltip": r"打开导航菜单",
"backButtonTooltip": r"背部",
"closeButtonTooltip": r"关",
"nextMonthTooltip": r"-下月就29了。",
"previousMonthTooltip": r"前一个月"
},
"en": const <String, String>{
"openAppDrawerTooltip": r"Open navigation menu",
"backButtonTooltip": r"Back",
"closeButtonTooltip": r"Close",
"nextMonthTooltip": r"Next month",
"previousMonthTooltip": r"Previous month"
},
"de": const <String, String>{
"openAppDrawerTooltip": r"Navigationsmenü öffnen",
"backButtonTooltip": r"Zurück",
"closeButtonTooltip": r"Schließen ",
"nextMonthTooltip": r"Nächster Monat",
"previousMonthTooltip": r"Letzter Monat"
},
"ja": const <String, String>{
"openAppDrawerTooltip": r"ナビゲーションメニューを開く",
"backButtonTooltip": r"バック",
"closeButtonTooltip": r"閉じる",
"nextMonthTooltip": r"来月",
"previousMonthTooltip": r"前の月"
},
"ru": const <String, String>{
"openAppDrawerTooltip": r"Открыть меню навигации",
"backButtonTooltip": r"назад",
"closeButtonTooltip": r"Закрыть",
"nextMonthTooltip": r"В следующем месяце",
"previousMonthTooltip": r"Предыдущий месяц"
}
};

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "افتح قائمة التنقل",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "الى الخلف",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "إغلا",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "الشهر القادم",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "الشهر الماضى",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Navigationsmenü öffnen",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Zurück",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Schließen ",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Nächster Monat",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Letzter Monat",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,31 @@
{
"openAppDrawerTooltip": "Open navigation menu",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Back",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Close",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Next month",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Previous month",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Abrir el menú de navegación",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Espalda",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Cerrar",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Próximo mes",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "mes anterior",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Ouvrir le menu de navigation",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Arrière",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Fermer",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Mois Suivant",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Le mois précédent",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Apri il menu di navigazione",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Indietro",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Chiudi",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Il prossimo mese",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Il mese scorso",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "ナビゲーションメニューを開く",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "バック",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "閉じる",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "来月",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "前の月",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Abrir menu de navegação",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "Costas",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Fechar",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "Próximo mês",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Mês anterior",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "Открыть меню навигации",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "назад",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "Закрыть",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "В следующем месяце",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "Предыдущий месяц",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -0,0 +1,27 @@
{
"openAppDrawerTooltip": "打开导航菜单",
"@openAppDrawerTooltip": {
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
"type": "text"
},
"backButtonTooltip": "背部",
"@backButtonTooltip": {
"description": "The BackButton's tooltip",
"type": "text"
},
"closeButtonTooltip": "关",
"@closeButtonTooltip": {
"description": "The CloseButton's tooltip",
"type": "text"
},
"nextMonthTooltip": "-下月就29了。",
"@nextMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'next month' button.",
"type": "text"
},
"previousMonthTooltip": "前一个月",
"@previousMonthTooltip": {
"description": "The tooltip for the MonthPicker's 'previous month' button.",
"type": "text"
}
}

View file

@ -2,35 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
/// Interface for localized resource values for the material widgets.
import 'i18n/localizations.dart';
/// Defines the localized resource values used by the Material widgts.
///
/// This class provides a default placeholder implementation that returns
/// hard-coded American English values.
class MaterialLocalizations {
/// Create a placeholder object for the localized resources of material widgets
/// which only provides American English strings.
const MaterialLocalizations();
/// The locale for which the values of this class's localized resources
/// have been translated.
Locale get locale => const Locale('en', 'US');
/// See also:
///
/// * [DefaultMaterialLocalizations], which implements this interface and
/// and supports a variety of locales.
abstract class MaterialLocalizations {
/// The tooltip for the leading [AppBar] menu (aka 'hamburger') button
String get openAppDrawerTooltip => 'Open navigation menu';
String get openAppDrawerTooltip;
/// The [BackButton]'s tooltip.
String get backButtonTooltip => 'Back';
String get backButtonTooltip;
/// The [CloseButton]'s tooltip.
String get closeButtonTooltip => 'Close';
String get closeButtonTooltip;
/// The tooltip for the [MonthPicker]'s "next month" button.
String get nextMonthTooltip => 'Next month';
String get nextMonthTooltip;
/// The tooltip for the [MonthPicker]'s "previous month" button.
String get previousMonthTooltip => 'Previous month';
String get previousMonthTooltip;
/// The `MaterialLocalizations` from the closest [Localizations] instance
/// that encloses the given context.
@ -48,3 +47,46 @@ class MaterialLocalizations {
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
}
/// Localized strings for the material widgets.
class DefaultMaterialLocalizations implements MaterialLocalizations {
/// Construct an object that defines the material widgets' localized strings
/// for the given `locale`.
DefaultMaterialLocalizations(this.locale) {
assert(locale != null);
_nameToValue = localizations[locale.toString()]
?? localizations[locale.languageCode]
?? localizations['en']
?? <String, String>{};
}
Map<String, String> _nameToValue;
/// The locale for which the values of this class's localized resources
/// have been translated.
final Locale locale;
@override
String get openAppDrawerTooltip => _nameToValue["openAppDrawerTooltip"];
@override
String get backButtonTooltip => _nameToValue["backButtonTooltip"];
@override
String get closeButtonTooltip => _nameToValue["closeButtonTooltip"];
@override
String get nextMonthTooltip => _nameToValue["nextMonthTooltip"];
@override
String get previousMonthTooltip => _nameToValue["previousMonthTooltip"];
/// Creates an object that provides localized resource values for the
/// for the widgets of the material library.
///
/// This method is typically used to create a [LocalizationsDelegate].
/// The [MaterialApp] does so by default.
static Future<MaterialLocalizations> load(Locale locale) {
return new SynchronousFuture<MaterialLocalizations>(new DefaultMaterialLocalizations(locale));
}
}

View file

@ -16,10 +16,17 @@ import 'framework.dart';
// class Intl { static String message(String s, { String name, String locale }) => ''; }
// Future<Null> initializeMessages(String locale) => null;
// Used by loadAll() to record LocalizationsDelegate.load() futures we're
// waiting for.
class _Pending {
_Pending(this.delegate, this.futureValue);
final LocalizationsDelegate<dynamic> delegate;
final Future<dynamic> futureValue;
}
// A utility function used by Localizations to generate one future
// that completes when all of the LocalizationsDelegate.load() futures
// complete. The returned map is indexed by the type of each input
// future's value.
// complete. The returned map is indexed by each delegate's type.
//
// The input future values must have distinct types.
//
@ -31,38 +38,41 @@ import 'framework.dart';
// 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(Iterable<Future<dynamic>> inputValues) {
Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegate<dynamic>> delegates) {
final Map<Type, dynamic> output = <Type, dynamic>{};
List<Future<dynamic>> outputFutures;
List<_Pending> pendingList;
for (Future<dynamic> inputValue in inputValues) {
for (LocalizationsDelegate<dynamic> delegate in delegates) {
final Future<dynamic> inputValue = delegate.load(locale);
dynamic completedValue;
final Future<dynamic> futureValue = inputValue.then<dynamic>((dynamic value) {
return completedValue = value;
});
if (completedValue != null) { // inputValue was a SynchronousFuture
final Type type = completedValue.runtimeType;
final Type type = delegate.type;
assert(!output.containsKey(type));
output[type] = completedValue;
} else {
outputFutures ??= <Future<dynamic>>[];
outputFutures.add(futureValue);
pendingList ??= <_Pending>[];
pendingList.add(new _Pending(delegate, futureValue));
}
}
// All of the input.values were synchronous futures, we're done.
if (outputFutures == null)
// All of the delegate.load() values were synchronous futures, we're done.
if (pendingList == null)
return new SynchronousFuture<Map<Type, dynamic>>(output);
// Some of input.values were asynchronous futures. Wait for them.
return Future.wait<dynamic>(outputFutures).then<Map<Type, dynamic>>((List<dynamic> values) {
for (dynamic value in values) {
final Type type = value.runtimeType;
assert(!output.containsKey(type));
output[type] = value;
}
return output;
});
// Some of delegate.load() values were asynchronous futures. Wait for them.
return Future.wait<dynamic>(pendingList.map((_Pending p) => p.futureValue))
.then<Map<Type, dynamic>>((List<dynamic> values) {
assert(values.length == pendingList.length);
for (int i = 0; i < values.length; i += 1) {
final Type type = pendingList[i].delegate.type;
assert(!output.containsKey(type));
output[type] = values[i];
}
return output;
});
}
/// A factory for a set of localized resources of type `T`, to be loaded by a
@ -92,8 +102,10 @@ abstract class LocalizationsDelegate<T> {
/// after [load] has completed.
bool shouldReload(covariant LocalizationsDelegate<T> old);
Type get type => T;
@override
String toString() => '$runtimeType';
String toString() => '$runtimeType[$type]';
}
/// Interface for localized resource values for the lowest levels of the Flutter
@ -346,12 +358,8 @@ class _LocalizationsState extends State<Localizations> {
return;
}
final Iterable<Future<dynamic>> allResources = delegates.map((LocalizationsDelegate<dynamic> delegate) {
return delegate.load(locale);
});
Map<Type, dynamic> typeToResources;
final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(allResources)
final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(locale, delegates)
.then((Map<Type, dynamic> value) {
return typeToResources = value;
});
@ -383,7 +391,6 @@ class _LocalizationsState extends State<Localizations> {
T resourcesFor<T>(Type type) {
assert(type != null);
final T resources = _typeToResources[type];
assert(resources.runtimeType == type);
return resources;
}

View file

@ -0,0 +1,52 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
Widget buildFrame({
Locale locale,
WidgetBuilder buildContent,
}) {
return new MaterialApp(
color: const Color(0xFFFFFFFF),
locale: locale,
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<Null>(
builder: (BuildContext context) {
return buildContent(context);
}
);
},
);
}
void main() {
final Key textKey = new UniqueKey();
testWidgets('sanity check', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
buildContent: (BuildContext context) {
return new Text(
MaterialLocalizations.of(context).backButtonTooltip,
key: textKey,
);
}
)
);
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
// Spanish Bolivia locale, falls back to just 'es'
await tester.binding.setLocale('es', 'bo');
await tester.pump();
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Espalda');
// Unrecognized locale falls back to 'en'
await tester.binding.setLocale('foo', 'bar');
await tester.pump();
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
});
}