Localizations for Material (#11832)
diff --git a/dev/tools/gen_localizations.dart b/dev/tools/gen_localizations.dart
new file mode 100644
index 0000000..0a7a838
--- /dev/null
+++ b/dev/tools/gen_localizations.dart
@@ -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());
+}
diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart
index a114622..4d06360 100644
--- a/packages/flutter/lib/src/material/app.dart
+++ b/packages/flutter/lib/src/material/app.dart
@@ -26,19 +26,17 @@
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
diff --git a/packages/flutter/lib/src/material/i18n/localizations.dart b/packages/flutter/lib/src/material/i18n/localizations.dart
new file mode 100644
index 0000000..1808dbc
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/localizations.dart
@@ -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"Предыдущий месяц"
+ }
+};
diff --git a/packages/flutter/lib/src/material/i18n/material_ar.arb b/packages/flutter/lib/src/material/i18n/material_ar.arb
new file mode 100644
index 0000000..16745bf
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_ar.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_de.arb b/packages/flutter/lib/src/material/i18n/material_de.arb
new file mode 100644
index 0000000..e36bec6
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_de.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_en.arb b/packages/flutter/lib/src/material/i18n/material_en.arb
new file mode 100644
index 0000000..784ad6a
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_en.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_es.arb b/packages/flutter/lib/src/material/i18n/material_es.arb
new file mode 100644
index 0000000..ddb3053
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_es.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_fr.arb b/packages/flutter/lib/src/material/i18n/material_fr.arb
new file mode 100644
index 0000000..4ff5670
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_fr.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_it.arb b/packages/flutter/lib/src/material/i18n/material_it.arb
new file mode 100644
index 0000000..80030ea
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_it.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_ja.arb b/packages/flutter/lib/src/material/i18n/material_ja.arb
new file mode 100644
index 0000000..4205c64
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_ja.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_pt.arb b/packages/flutter/lib/src/material/i18n/material_pt.arb
new file mode 100644
index 0000000..a346b4b
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_pt.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_ru.arb b/packages/flutter/lib/src/material/i18n/material_ru.arb
new file mode 100644
index 0000000..c12a616
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_ru.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/i18n/material_zh.arb b/packages/flutter/lib/src/material/i18n/material_zh.arb
new file mode 100644
index 0000000..c194874
--- /dev/null
+++ b/packages/flutter/lib/src/material/i18n/material_zh.arb
@@ -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"
+ }
+}
diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart
index b13adce..6289bf4 100644
--- a/packages/flutter/lib/src/material/material_localizations.dart
+++ b/packages/flutter/lib/src/material/material_localizations.dart
@@ -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 @@
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));
+ }
+}
diff --git a/packages/flutter/lib/src/widgets/localizations.dart b/packages/flutter/lib/src/widgets/localizations.dart
index 961fbc6..5ed7d60 100644
--- a/packages/flutter/lib/src/widgets/localizations.dart
+++ b/packages/flutter/lib/src/widgets/localizations.dart
@@ -16,10 +16,17 @@
// 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 @@
// 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 @@
/// 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 @@
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 @@
T resourcesFor<T>(Type type) {
assert(type != null);
final T resources = _typeToResources[type];
- assert(resources.runtimeType == type);
return resources;
}
diff --git a/packages/flutter/test/material/localizations_test.dart b/packages/flutter/test/material/localizations_test.dart
new file mode 100644
index 0000000..d6ed0d8
--- /dev/null
+++ b/packages/flutter/test/material/localizations_test.dart
@@ -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');
+ });
+}