flutter_localization optional package (#12410)

diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml
index 7da9207..7538d76 100644
--- a/dev/automated_tests/pubspec.yaml
+++ b/dev/automated_tests/pubspec.yaml
@@ -7,7 +7,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml
index 37c1000..dca5efa 100644
--- a/dev/benchmarks/complex_layout/pubspec.yaml
+++ b/dev/benchmarks/complex_layout/pubspec.yaml
@@ -19,7 +19,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml
index cfedc59..df3d8a2 100644
--- a/dev/benchmarks/microbenchmarks/pubspec.yaml
+++ b/dev/benchmarks/microbenchmarks/pubspec.yaml
@@ -11,7 +11,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml
index acdc100..35263c7 100644
--- a/dev/bots/pubspec.yaml
+++ b/dev/bots/pubspec.yaml
@@ -6,7 +6,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index c12ab27..c3fffa7 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -57,13 +57,13 @@
     dart,
     <String>[
       path.join('dev', 'tools', 'gen_localizations.dart'),
-      path.join('packages', 'flutter', 'lib', 'src', 'material', 'i18n'),
+      path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n'),
       'material'
     ],
     workingDirectory: flutterRoot,
   );
 
-  final String localizationsFile = path.join('packages', 'flutter', 'lib', 'src', 'material', 'i18n', 'localizations.dart');
+  final String localizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'localizations.dart');
 
   final EvalResult sourceContents = await _evalCommand(
     'cat',
@@ -156,6 +156,7 @@
 
   // Run tests.
   await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter'));
+  await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'));
   await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'));
   await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'));
   await _pubRunTest(path.join(flutterRoot, 'packages', 'flutter_tools'));
diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml
index f7618e7..62f14a4 100644
--- a/dev/devicelab/pubspec.yaml
+++ b/dev/devicelab/pubspec.yaml
@@ -24,7 +24,7 @@
 
   archive: 1.0.31 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   browser: 0.10.0+2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml
index 08af630..38da5a1 100644
--- a/dev/integration_tests/channels/pubspec.yaml
+++ b/dev/integration_tests/channels/pubspec.yaml
@@ -9,7 +9,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml
index 1d29942..1e9d71e 100644
--- a/dev/integration_tests/flavors/pubspec.yaml
+++ b/dev/integration_tests/flavors/pubspec.yaml
@@ -9,7 +9,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/integration_tests/platform_interaction/pubspec.yaml b/dev/integration_tests/platform_interaction/pubspec.yaml
index 20d2a55..6986c44 100644
--- a/dev/integration_tests/platform_interaction/pubspec.yaml
+++ b/dev/integration_tests/platform_interaction/pubspec.yaml
@@ -9,7 +9,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml
index 7b072fb..ddc6398 100644
--- a/dev/integration_tests/ui/pubspec.yaml
+++ b/dev/integration_tests/ui/pubspec.yaml
@@ -14,7 +14,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml
index 0258598..11e8a3c 100644
--- a/dev/manual_tests/pubspec.yaml
+++ b/dev/manual_tests/pubspec.yaml
@@ -10,7 +10,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml
index 2f8c3d8..d99e9c5 100644
--- a/dev/tools/pubspec.yaml
+++ b/dev/tools/pubspec.yaml
@@ -10,7 +10,7 @@
   path: 1.4.2
 
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/catalog/pubspec.yaml b/examples/catalog/pubspec.yaml
index 38befc8..08f696a 100644
--- a/examples/catalog/pubspec.yaml
+++ b/examples/catalog/pubspec.yaml
@@ -13,7 +13,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/flutter_gallery/pubspec.yaml b/examples/flutter_gallery/pubspec.yaml
index e6b6bba..07291c7 100644
--- a/examples/flutter_gallery/pubspec.yaml
+++ b/examples/flutter_gallery/pubspec.yaml
@@ -21,7 +21,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   convert: 2.0.1 # TRANSITIVE DEPENDENCY
diff --git a/examples/flutter_view/pubspec.yaml b/examples/flutter_view/pubspec.yaml
index dbf42b4..09acc0a 100644
--- a/examples/flutter_view/pubspec.yaml
+++ b/examples/flutter_view/pubspec.yaml
@@ -7,7 +7,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml
index 18731f5..26354a1 100644
--- a/examples/hello_world/pubspec.yaml
+++ b/examples/hello_world/pubspec.yaml
@@ -10,7 +10,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml
index c3838cb..5eba9e0 100644
--- a/examples/layers/pubspec.yaml
+++ b/examples/layers/pubspec.yaml
@@ -9,7 +9,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml
index d4e817c..8ee0ce4 100644
--- a/examples/platform_channel/pubspec.yaml
+++ b/examples/platform_channel/pubspec.yaml
@@ -12,7 +12,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/platform_channel_swift/pubspec.yaml b/examples/platform_channel_swift/pubspec.yaml
index 07e4e6e..db6b961 100644
--- a/examples/platform_channel_swift/pubspec.yaml
+++ b/examples/platform_channel_swift/pubspec.yaml
@@ -12,7 +12,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/platform_view/pubspec.yaml b/examples/platform_view/pubspec.yaml
index f8a36f7..a21fcf2 100644
--- a/examples/platform_view/pubspec.yaml
+++ b/examples/platform_view/pubspec.yaml
@@ -6,7 +6,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart
index 8364d46..a89769a 100644
--- a/examples/stocks/lib/main.dart
+++ b/examples/stocks/lib/main.dart
@@ -13,6 +13,7 @@
   debugPaintLayerBordersEnabled,
   debugPaintPointersEnabled,
   debugRepaintRainbowEnabled;
+import 'package:flutter_localizations/flutter_localizations.dart';
 
 import 'stock_data.dart';
 import 'stock_home.dart';
@@ -118,8 +119,10 @@
     return new MaterialApp(
       title: 'Stocks',
       theme: theme,
-      localizationsDelegates: <_StocksLocalizationsDelegate>[
+      localizationsDelegates: <LocalizationsDelegate<dynamic>>[
         new _StocksLocalizationsDelegate(),
+        GlobalMaterialLocalizations.delegate,
+        GlobalWidgetsLocalizations.delegate,
       ],
       supportedLocales: const <Locale>[
         const Locale('en', 'US'),
diff --git a/examples/stocks/pubspec.yaml b/examples/stocks/pubspec.yaml
index 7597950..ca76147 100644
--- a/examples/stocks/pubspec.yaml
+++ b/examples/stocks/pubspec.yaml
@@ -2,6 +2,8 @@
 dependencies:
   flutter:
     sdk: flutter
+  flutter_localizations:
+    sdk: flutter
   intl: 0.15.1
   intl_translation: 0.15.0
   http: 0.11.3+14
@@ -15,7 +17,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
   collection: 1.14.3 # TRANSITIVE DEPENDENCY
diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart
index 78c0a36..fbf61a3 100644
--- a/packages/flutter/lib/src/material/app.dart
+++ b/packages/flutter/lib/src/material/app.dart
@@ -2,8 +2,6 @@
 // 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/rendering.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
@@ -26,17 +24,6 @@
   decorationStyle: TextDecorationStyle.double
 );
 
-class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
-  const _MaterialLocalizationsDelegate();
-
-  @override
-  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
@@ -463,7 +450,7 @@
   Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
     if (widget.localizationsDelegates != null)
       yield* widget.localizationsDelegates;
-    yield const _MaterialLocalizationsDelegate();
+    yield DefaultMaterialLocalizations.delegate;
   }
 
   RectTween _createRectTween(Rect begin, Rect end) {
diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart
index 91c5a44..60616e0 100644
--- a/packages/flutter/lib/src/material/date_picker.dart
+++ b/packages/flutter/lib/src/material/date_picker.dart
@@ -295,8 +295,8 @@
   List<Widget> _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) {
     final List<Widget> result = <Widget>[];
     for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) {
-      final String weekDay = localizations.narrowWeekDays[i];
-      result.add(new Center(child: new Text(weekDay, style: headerStyle)));
+      final String weekday = localizations.narrowWeekdays[i];
+      result.add(new Center(child: new Text(weekday, style: headerStyle)));
       if (i == (localizations.firstDayOfWeekIndex - 1) % 7)
         break;
     }
@@ -350,19 +350,19 @@
   /// - [DateTime.weekday] provides a 1-based index into days of week, with 1
   ///   falling on Monday.
   /// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index
-  ///   into the [MaterialLocalizations.narrowWeekDays] list.
-  /// - [MaterialLocalizations.narrowWeekDays] list provides localized names of
+  ///   into the [MaterialLocalizations.narrowWeekdays] list.
+  /// - [MaterialLocalizations.narrowWeekdays] list provides localized names of
   ///   days of week, always starting with Sunday and ending with Saturday.
   int _computeFirstDayOffset(int year, int month, MaterialLocalizations localizations) {
     // 0-based day of week, with 0 representing Monday.
-    final int weekDayFromMonday = new DateTime(year, month).weekday - 1;
+    final int weekdayFromMonday = new DateTime(year, month).weekday - 1;
     // 0-based day of week, with 0 representing Sunday.
     final int firstDayOfWeekFromSunday = localizations.firstDayOfWeekIndex;
     // firstDayOfWeekFromSunday recomputed to be Monday-based
     final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7;
     // Number of days between the first day of week appearing on the calendar,
     // and the day corresponding to the 1-st of the month.
-    return (weekDayFromMonday - firstDayOfWeekFromMonday) % 7;
+    return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7;
   }
 
   @override
diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart
index 690e060..c872dbb 100644
--- a/packages/flutter/lib/src/material/material_localizations.dart
+++ b/packages/flutter/lib/src/material/material_localizations.dart
@@ -6,11 +6,7 @@
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
-import 'package:intl/intl.dart' as intl;
-import 'package:intl/date_symbols.dart' as intl;
-import 'package:intl/date_symbol_data_local.dart' as intl_local_date_data;
 
-import 'i18n/localizations.dart';
 import 'time.dart';
 import 'typography.dart';
 
@@ -18,8 +14,10 @@
 ///
 /// See also:
 ///
-///  * [DefaultMaterialLocalizations], which implements this interface
-///    and supports a variety of locales.
+///  * [DefaultMaterialLocalizations], the default, English-only, implementation
+///    of this interface.
+///  * [GlobalMaterialLocalizations], which provides material localizations for
+///    many languages.
 abstract class MaterialLocalizations {
   /// The tooltip for the leading [AppBar] menu (aka 'hamburger') button.
   String get openAppDrawerTooltip;
@@ -155,17 +153,17 @@
   /// - US English: S, M, T, W, T, F, S
   /// - Russian: вс, пн, вт, ср, чт, пт, сб - notice that the list begins with
   ///   вс (Sunday) even though the first day of week for Russian is Monday.
-  List<String> get narrowWeekDays;
+  List<String> get narrowWeekdays;
 
   /// Index of the first day of week, where 0 points to Sunday, and 6 points to
   /// Saturday.
   ///
-  /// This getter is compatible with [narrowWeekDays]. For example:
+  /// This getter is compatible with [narrowWeekdays]. For example:
   ///
   /// ```dart
   /// var localizations = MaterialLocalizations.of(context);
   /// // The name of the first day of week for the current locale.
-  /// var firstDayOfWeek = localizations.narrowWeekDays[localizations.firstDayOfWeekIndex];
+  /// var firstDayOfWeek = localizations.narrowWeekdays[localizations.firstDayOfWeekIndex];
   /// ```
   int get firstDayOfWeekIndex;
 
@@ -186,173 +184,118 @@
   }
 }
 
-/// Localized strings for the material widgets.
+class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
+  const _MaterialLocalizationsDelegate();
+
+  @override
+  Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
+
+  @override
+  bool shouldReload(_MaterialLocalizationsDelegate old) => false;
+}
+
+/// US English strings for the material widgets.
+///
+/// See also:
+///
+///  * [GlobalMaterialLocalizations], which provides material localizations for
+///    many languages.
+///  * [MaterialApp.delegates], which automatically includes
+///    [DefaultMaterialLocalizations.delegate] by default.
 class DefaultMaterialLocalizations implements MaterialLocalizations {
   /// Constructs an object that defines the material widgets' localized strings
-  /// for the given `locale`.
+  /// for US English (only).
   ///
   /// [LocalizationsDelegate] implementations typically call the static [load]
   /// function, rather than constructing this class directly.
-  DefaultMaterialLocalizations(this.locale)
-      : assert(locale != null),
-        this._localeName = _computeLocaleName(locale) {
-    _loadDateIntlDataIfNotLoaded();
+  const DefaultMaterialLocalizations();
 
-    if (localizations.containsKey(locale.languageCode))
-      _nameToValue.addAll(localizations[locale.languageCode]);
-    if (localizations.containsKey(_localeName))
-      _nameToValue.addAll(localizations[_localeName]);
 
-    const String kMediumDatePattern = 'E, MMM\u00a0d';
-    if (intl.DateFormat.localeExists(_localeName)) {
-      _fullYearFormat = new intl.DateFormat.y(_localeName);
-      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern, _localeName);
-      _yearMonthFormat = new intl.DateFormat('yMMMM', _localeName);
-    } else if (intl.DateFormat.localeExists(locale.languageCode)) {
-      _fullYearFormat = new intl.DateFormat.y(locale.languageCode);
-      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern, locale.languageCode);
-      _yearMonthFormat = new intl.DateFormat('yMMMM', locale.languageCode);
-    } else {
-      _fullYearFormat = new intl.DateFormat.y();
-      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern);
-      _yearMonthFormat = new intl.DateFormat('yMMMM');
-    }
+  // Ordered to match DateTime.MONDAY=1, DateTime.SUNDAY=6
+  static const List<String>_shortWeekdays = const <String>[
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat',
+    'Sun',
+  ];
 
-    if (intl.NumberFormat.localeExists(_localeName)) {
-      _decimalFormat = new intl.NumberFormat.decimalPattern(_localeName);
-      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00', _localeName);
-    } else if (intl.NumberFormat.localeExists(locale.languageCode)) {
-      _decimalFormat = new intl.NumberFormat.decimalPattern(locale.languageCode);
-      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00', locale.languageCode);
-    } else {
-      _decimalFormat = new intl.NumberFormat.decimalPattern();
-      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00');
-    }
-  }
+  static const List<String> _narrowWeekdays = const <String>[
+    'S',
+    'M',
+    'T',
+    'W',
+    'T',
+    'F',
+    'S',
+  ];
 
-  /// The locale for which the values of this class's localized resources
-  /// have been translated.
-  final Locale locale;
+  static const List<String> _shortMonths = const <String>[
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec',
+  ];
 
-  final String _localeName;
-
-  final Map<String, String> _nameToValue = <String, String>{};
-
-  /// Formats numbers using variable length format with no zero padding.
-  ///
-  /// See also [_twoDigitZeroPaddedFormat].
-  intl.NumberFormat _decimalFormat;
-
-  /// Formats numbers as two-digits.
-  ///
-  /// If the number is less than 10, zero-pads it.
-  intl.NumberFormat _twoDigitZeroPaddedFormat;
-
-  /// Full unabbreviated year format, e.g. 2017 rather than 17.
-  intl.DateFormat _fullYearFormat;
-
-  intl.DateFormat _mediumDateFormat;
-
-  intl.DateFormat _yearMonthFormat;
-
-  static String _computeLocaleName(Locale locale) {
-    final String localeName = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
-    return intl.Intl.canonicalizedLocale(localeName);
-  }
-
-  // TODO(hmuller): the rules for mapping from an integer value to
-  // "one" or "two" etc. are locale specific and an additional "few" category
-  // is needed. See http://cldr.unicode.org/index/cldr-spec/plural-rules
-  String _nameToPluralValue(int count, String key) {
-    String text;
-    if (count == 0)
-      text = _nameToValue['${key}Zero'];
-    else if (count == 1)
-      text = _nameToValue['${key}One'];
-    else if (count == 2)
-      text = _nameToValue['${key}Two'];
-    else if (count > 2)
-      text = _nameToValue['${key}Many'];
-    text ??= _nameToValue['${key}Other'];
-    assert(text != null);
-    return text;
-  }
+  static const List<String> _months = const <String>[
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December',
+  ];
 
   @override
   String formatHour(TimeOfDay timeOfDay) {
-    switch (hourFormat(of: timeOfDayFormat)) {
-      case HourFormat.HH:
-        return _twoDigitZeroPaddedFormat.format(timeOfDay.hour);
-      case HourFormat.H:
-        return formatDecimal(timeOfDay.hour);
-      case HourFormat.h:
-        final int hour = timeOfDay.hourOfPeriod;
-        return formatDecimal(hour == 0 ? 12 : hour);
-    }
-    return null;
+    assert(hourFormat(of: timeOfDayFormat) == HourFormat.h);
+    return formatDecimal(timeOfDay.hour);
   }
 
   @override
   String formatMinute(TimeOfDay timeOfDay) {
-    return _twoDigitZeroPaddedFormat.format(timeOfDay.minute);
+    final int minute = timeOfDay.minute;
+    return minute < 10 ? '0$minute' : minute.toString();
   }
 
   @override
-  String formatYear(DateTime date) {
-    return _fullYearFormat.format(date);
-  }
+  String formatYear(DateTime date) => date.year.toString();
 
   @override
   String formatMediumDate(DateTime date) {
-    return _mediumDateFormat.format(date);
+    final String day = _shortWeekdays[date.weekday - DateTime.MONDAY];
+    final String month = _shortMonths[date.month - DateTime.JANUARY];
+    return '$day, $month ${date.day}';
   }
 
   @override
   String formatMonthYear(DateTime date) {
-    return _yearMonthFormat.format(date);
+    final String year = formatYear(date);
+    final String month = _months[date.month - DateTime.JANUARY];
+    return '$month $year';
   }
 
   @override
-  List<String> get narrowWeekDays {
-    return _fullYearFormat.dateSymbols.NARROWWEEKDAYS;
-  }
+  List<String> get narrowWeekdays => _narrowWeekdays;
 
   @override
-  int get firstDayOfWeekIndex => (_fullYearFormat.dateSymbols.FIRSTDAYOFWEEK + 1) % 7;
-
-  /// Formats a [number] using local decimal number format.
-  ///
-  /// Inserts locale-appropriate thousands separator, if necessary.
-  String formatDecimal(int number) {
-    return _decimalFormat.format(number);
-  }
-
-  @override
-  String formatTimeOfDay(TimeOfDay timeOfDay) {
-    // Not using intl.DateFormat for two reasons:
-    //
-    // - DateFormat supports more formats than our material time picker does,
-    //   and we want to be consistent across time picker format and the string
-    //   formatting of the time of day.
-    // - DateFormat operates on DateTime, which is sensitive to time eras and
-    //   time zones, while here we want to format hour and minute within one day
-    //   no matter what date the day falls on.
-    switch (timeOfDayFormat) {
-      case TimeOfDayFormat.h_colon_mm_space_a:
-        return '${formatHour(timeOfDay)}:${formatMinute(timeOfDay)} ${_formatDayPeriod(timeOfDay)}';
-      case TimeOfDayFormat.H_colon_mm:
-      case TimeOfDayFormat.HH_colon_mm:
-        return '${formatHour(timeOfDay)}:${formatMinute(timeOfDay)}';
-      case TimeOfDayFormat.HH_dot_mm:
-        return '${formatHour(timeOfDay)}.${formatMinute(timeOfDay)}';
-      case TimeOfDayFormat.a_space_h_colon_mm:
-        return '${_formatDayPeriod(timeOfDay)} ${formatHour(timeOfDay)}:${formatMinute(timeOfDay)}';
-      case TimeOfDayFormat.frenchCanadian:
-        return '${formatHour(timeOfDay)} h ${formatMinute(timeOfDay)}';
-    }
-
-    return null;
-  }
+  int get firstDayOfWeekIndex => 0; // narrowWeekdays[0] is 'S' for Sunday
 
   String _formatDayPeriod(TimeOfDay timeOfDay) {
     switch (timeOfDay.period) {
@@ -364,166 +307,142 @@
     return null;
   }
 
-  @override
-  String get openAppDrawerTooltip => _nameToValue['openAppDrawerTooltip'];
+  /// Formats an integer, inserting thousands separators as needed.
+  String formatDecimal(int number) {
+    if (number > -1000 && number < 1000)
+      return number.toString();
 
-  @override
-  String get backButtonTooltip => _nameToValue['backButtonTooltip'];
-
-  @override
-  String get closeButtonTooltip => _nameToValue['closeButtonTooltip'];
-
-  @override
-  String get nextMonthTooltip => _nameToValue['nextMonthTooltip'];
-
-  @override
-  String get previousMonthTooltip => _nameToValue['previousMonthTooltip'];
-
-  @override
-  String get nextPageTooltip => _nameToValue['nextPageTooltip'];
-
-  @override
-  String get previousPageTooltip => _nameToValue['previousPageTooltip'];
-
-  @override
-  String get showMenuTooltip => _nameToValue['showMenuTooltip'];
-
-  @override
-  String aboutListTileTitle(String applicationName) {
-    final String text = _nameToValue['aboutListTileTitle'];
-    return text.replaceFirst(r'$applicationName', applicationName);
+    final String digits = number.abs().toString();
+    final StringBuffer result = new StringBuffer(number < 0 ? '-' : '');
+    final int maxDigitIndex = digits.length - 1;
+    for (int i = 0; i <= maxDigitIndex; i += 1) {
+      result.write(digits[i]);
+      if (i < maxDigitIndex && (maxDigitIndex - i) % 3 == 0)
+        result.write(',');
+    }
+    return result.toString();
   }
 
   @override
-  String get licensesPageTitle => _nameToValue['licensesPageTitle'];
+  String formatTimeOfDay(TimeOfDay timeOfDay) {
+    assert(timeOfDayFormat == TimeOfDayFormat.h_colon_mm_space_a);
+    // Not using intl.DateFormat for two reasons:
+    //
+    // - DateFormat supports more formats than our material time picker does,
+    //   and we want to be consistent across time picker format and the string
+    //   formatting of the time of day.
+    // - DateFormat operates on DateTime, which is sensitive to time eras and
+    //   time zones, while here we want to format hour and minute within one day
+    //   no matter what date the day falls on.
+    return '${formatHour(timeOfDay)}:${formatMinute(timeOfDay)} ${_formatDayPeriod(timeOfDay)}';
+  }
+
+  @override
+  String get openAppDrawerTooltip => 'Open navigation menu';
+
+  @override
+  String get backButtonTooltip => 'Back';
+
+  @override
+  String get closeButtonTooltip => 'Close';
+
+  @override
+  String get nextMonthTooltip => 'Next month';
+
+  @override
+  String get previousMonthTooltip => 'Previous month';
+
+  @override
+  String get nextPageTooltip => 'Next page';
+
+  @override
+  String get previousPageTooltip => 'Previous page';
+
+  @override
+  String get showMenuTooltip => 'Show menu';
+
+  @override
+  String aboutListTileTitle(String applicationName) => 'About $applicationName';
+
+  @override
+  String get licensesPageTitle => 'Licenses';
 
   @override
   String pageRowsInfoTitle(int firstRow, int lastRow, int rowCount, bool rowCountIsApproximate) {
-    String text = rowCountIsApproximate ? _nameToValue['pageRowsInfoTitleApproximate'] : null;
-    text ??= _nameToValue['pageRowsInfoTitle'];
-    assert(text != null, 'A $locale localization was not found for pageRowsInfoTitle or pageRowsInfoTitleApproximate');
-    // TODO(hansmuller): this could be more efficient.
-    return text
-      .replaceFirst(r'$firstRow', formatDecimal(firstRow))
-      .replaceFirst(r'$lastRow', formatDecimal(lastRow))
-      .replaceFirst(r'$rowCount', formatDecimal(rowCount));
+    return rowCountIsApproximate
+      ? '$firstRow–$lastRow of about $rowCount'
+      : '$firstRow–$lastRow of $rowCount';
   }
 
   @override
-  String get rowsPerPageTitle => _nameToValue['rowsPerPageTitle'];
+  String get rowsPerPageTitle => 'Rows per page';
 
   @override
   String selectedRowCountTitle(int selectedRowCount) {
-    return _nameToPluralValue(selectedRowCount, 'selectedRowCountTitle') // asserts on no match
-      .replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount));
+    switch (selectedRowCount) {
+      case 0:
+        return 'No items selected';
+      case 1:
+        return '1 item selected';
+      default:
+        return '$selectedRowCount items selected';
+    }
   }
 
   @override
-  String get cancelButtonLabel => _nameToValue['cancelButtonLabel'];
+  String get cancelButtonLabel => 'CANCEL';
 
   @override
-  String get closeButtonLabel => _nameToValue['closeButtonLabel'];
+  String get closeButtonLabel => 'CLOSE';
 
   @override
-  String get continueButtonLabel => _nameToValue['continueButtonLabel'];
+  String get continueButtonLabel => 'CONTINUE';
 
   @override
-  String get copyButtonLabel => _nameToValue['copyButtonLabel'];
+  String get copyButtonLabel => 'COPY';
 
   @override
-  String get cutButtonLabel => _nameToValue['cutButtonLabel'];
+  String get cutButtonLabel => 'CUT';
 
   @override
-  String get okButtonLabel => _nameToValue['okButtonLabel'];
+  String get okButtonLabel => 'OK';
 
   @override
-  String get pasteButtonLabel => _nameToValue['pasteButtonLabel'];
+  String get pasteButtonLabel => 'PASTE';
 
   @override
-  String get selectAllButtonLabel => _nameToValue['selectAllButtonLabel'];
+  String get selectAllButtonLabel => 'SELECT ALL';
 
   @override
-  String get viewLicensesButtonLabel => _nameToValue['viewLicensesButtonLabel'];
+  String get viewLicensesButtonLabel => 'VIEW LICENSES';
 
   @override
-  String get anteMeridiemAbbreviation => _nameToValue['anteMeridiemAbbreviation'];
+  String get anteMeridiemAbbreviation => 'AM';
 
   @override
-  String get postMeridiemAbbreviation => _nameToValue['postMeridiemAbbreviation'];
+  String get postMeridiemAbbreviation => 'PM';
 
-  /// The [TimeOfDayFormat] corresponding to one of the following supported
-  /// patterns:
-  ///
-  ///  * `HH:mm`
-  ///  * `HH.mm`
-  ///  * `HH 'h' mm`
-  ///  * `HH:mm น.`
-  ///  * `H:mm`
-  ///  * `h:mm a`
-  ///  * `a h:mm`
-  ///  * `ah:mm`
-  ///
-  /// See also:
-  ///
-  ///  * http://demo.icu-project.org/icu-bin/locexp?d_=en&_=en_US shows the
-  ///    short time pattern used in locale en_US
   @override
-  TimeOfDayFormat get timeOfDayFormat {
-    final String icuShortTimePattern = _nameToValue['timeOfDayFormat'];
-
-    assert(() {
-      if (!_icuTimeOfDayToEnum.containsKey(icuShortTimePattern)) {
-        throw new FlutterError(
-          '"$icuShortTimePattern" is not one of the ICU short time patterns '
-          'supported by the material library. Here is the list of supported '
-          'patterns:\n  ' +
-          _icuTimeOfDayToEnum.keys.join('\n  ')
-        );
-      }
-      return true;
-    }());
-
-    return _icuTimeOfDayToEnum[icuShortTimePattern];
-  }
+  TimeOfDayFormat get timeOfDayFormat => TimeOfDayFormat.h_colon_mm_space_a;
 
   /// Looks up text geometry defined in [MaterialTextGeometry].
   @override
-  TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(_nameToValue["scriptCategory"]);
+  TextTheme get localTextGeometry => MaterialTextGeometry.englishLike;
 
-  /// Creates an object that provides localized resource values for the
-  /// for the widgets of the material library.
+  /// Creates an object that provides US English resource values for the material
+  /// library widgets.
+  ///
+  /// The [locale] parameter is ignored.
   ///
   /// 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));
+    return new SynchronousFuture<MaterialLocalizations>(const DefaultMaterialLocalizations());
   }
-}
 
-const Map<String, TimeOfDayFormat> _icuTimeOfDayToEnum = const <String, TimeOfDayFormat>{
-  'HH:mm': TimeOfDayFormat.HH_colon_mm,
-  'HH.mm': TimeOfDayFormat.HH_dot_mm,
-  "HH 'h' mm": TimeOfDayFormat.frenchCanadian,
-  'HH:mm น.': TimeOfDayFormat.HH_colon_mm,
-  'H:mm': TimeOfDayFormat.H_colon_mm,
-  'h:mm a': TimeOfDayFormat.h_colon_mm_space_a,
-  'a h:mm': TimeOfDayFormat.a_space_h_colon_mm,
-  'ah:mm': TimeOfDayFormat.a_space_h_colon_mm,
-};
-
-/// Tracks if date i18n data has been loaded.
-bool _dateIntlDataInitialized = false;
-
-/// Loads i18n data for dates if it hasn't be loaded yet.
-///
-/// Only the first invocation of this function has the effect of loading the
-/// data. Subsequent invocations have no effect.
-void _loadDateIntlDataIfNotLoaded() {
-  if (!_dateIntlDataInitialized) {
-    // The returned Future is intentionally dropped on the floor. The
-    // function only returns it to be compatible with the async counterparts.
-    // The Future has no value otherwise.
-    intl_local_date_data.initializeDateFormatting();
-    _dateIntlDataInitialized = true;
-  }
+  /// A [LocalizationsDelegate] that uses [DefaultMaterialLocalizations.load]
+  /// to create an instance of this class.
+  ///
+  /// [MaterialApp] automatically adds this value to [MaterialApp.localizationsDelegates].
+  static const LocalizationsDelegate<MaterialLocalizations> delegate = const _MaterialLocalizationsDelegate();
 }
diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart
index 997c2ec..f779cb5 100644
--- a/packages/flutter/lib/src/widgets/app.dart
+++ b/packages/flutter/lib/src/widgets/app.dart
@@ -44,17 +44,6 @@
 /// This function must not return null.
 typedef String GenerateAppTitle(BuildContext context);
 
-// Delegate that fetches the default (English) strings.
-class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
-  const _WidgetsLocalizationsDelegate();
-
-  @override
-  Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
-
-  @override
-  bool shouldReload(_WidgetsLocalizationsDelegate old) => false;
-}
-
 /// A convenience class that wraps a number of widgets that are commonly
 /// required for an application.
 ///
@@ -423,11 +412,11 @@
   // 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.
+  // WidgetsLocalizations.delegate.
   Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
     if (widget.localizationsDelegates != null)
       yield* widget.localizationsDelegates;
-    yield const _WidgetsLocalizationsDelegate();
+    yield DefaultWidgetsLocalizations.delegate;
   }
 
   @override
diff --git a/packages/flutter/lib/src/widgets/localizations.dart b/packages/flutter/lib/src/widgets/localizations.dart
index bfabeb2..ace2e3b 100644
--- a/packages/flutter/lib/src/widgets/localizations.dart
+++ b/packages/flutter/lib/src/widgets/localizations.dart
@@ -161,44 +161,50 @@
   }
 }
 
-/// Localized values for widgets.
-class DefaultWidgetsLocalizations implements WidgetsLocalizations {
-  /// Construct an object that defines the localized values for the widgets
-  /// library for the given `locale`.
-  ///
-  /// [LocalizationsDelegate] implementations typically call the static [load]
-  /// function, rather than constructing this class directly.
-  DefaultWidgetsLocalizations(this.locale) {
-    final String language = locale.languageCode.toLowerCase();
-    _textDirection = _rtlLanguages.contains(language) ? TextDirection.rtl : TextDirection.ltr;
-  }
-
-  // See http://en.wikipedia.org/wiki/Right-to-left
-  static const List<String> _rtlLanguages = const <String>[
-    'ar',  // Arabic
-    'fa',  // Farsi
-    'he',  // Hebrew
-    'ps',  // Pashto
-    'sd',  // Sindhi
-    'ur',  // Urdu
-  ];
-
-  /// The locale for which the values of this class's localized resources
-  /// have been translated.
-  final Locale locale;
+class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
+  const _WidgetsLocalizationsDelegate();
 
   @override
-  TextDirection get textDirection => _textDirection;
-  TextDirection _textDirection;
+  Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
 
-  /// Creates an object that provides localized resource values for the
-  /// lowest levels of the Flutter framework.
+  @override
+  bool shouldReload(_WidgetsLocalizationsDelegate old) => false;
+}
+
+/// US English localizations for the widgets library.
+///
+/// See also:
+///
+///  * [GlobalWidgetsLocalizations], which provides widgets localizations for
+///    many languages.
+///  * [WidgetsApp.delegates], which automatically includes
+///    [DefaultWidgetsLocalizations.delegate] by default.
+class DefaultWidgetsLocalizations implements WidgetsLocalizations {
+  /// Construct an object that defines the localized values for the widgets
+  /// library for US English (only).
+  ///
+  /// [LocalizationsDelegate] implementations typically call the static [load]
+  const DefaultWidgetsLocalizations();
+
+  @override
+  TextDirection get textDirection => TextDirection.ltr;
+
+  /// Creates an object that provides US English resource values for the
+  /// lowest levels of the widgets library.
+  ///
+  /// The [locale] parameter is ignored.
   ///
   /// This method is typically used to create a [LocalizationsDelegate].
   /// The [WidgetsApp] does so by default.
   static Future<WidgetsLocalizations> load(Locale locale) {
-    return new SynchronousFuture<WidgetsLocalizations>(new DefaultWidgetsLocalizations(locale));
+    return new SynchronousFuture<WidgetsLocalizations>(const DefaultWidgetsLocalizations());
   }
+
+  /// A [LocalizationsDelegate] that uses [DefaultWidgetsLocalizations.load]
+  /// to create an instance of this class.
+  ///
+  /// [WidgetsApp] automatically adds this value to [WidgetApp.localizationsDelegates].
+  static const LocalizationsDelegate<WidgetsLocalizations> delegate = const _WidgetsLocalizationsDelegate();
 }
 
 class _LocalizationsScope extends InheritedWidget {
diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml
index 4fef675..b94a470 100644
--- a/packages/flutter/pubspec.yaml
+++ b/packages/flutter/pubspec.yaml
@@ -8,7 +8,6 @@
   # To update these, use "flutter update-packages --force-upgrade".
   collection: 1.14.3
   http: 0.11.3+14
-  intl: 0.15.1 # TODO(ianh): remove this, see https://github.com/flutter/flutter/issues/12050
   meta: 1.1.1
   typed_data: 1.1.4
   vector_math: 2.0.5
@@ -23,7 +22,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
@@ -35,6 +34,7 @@
   html: 0.13.2 # TRANSITIVE DEPENDENCY
   http_multi_server: 2.0.4 # TRANSITIVE DEPENDENCY
   http_parser: 3.1.1 # TRANSITIVE DEPENDENCY
+  intl: 0.15.1 # TRANSITIVE DEPENDENCY
   intl_translation: 0.15.0 # TRANSITIVE DEPENDENCY
   isolate: 1.1.0 # TRANSITIVE DEPENDENCY
   js: 0.6.1 # TRANSITIVE DEPENDENCY
diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart
index df9cb47..69a7016 100644
--- a/packages/flutter/test/material/date_picker_test.dart
+++ b/packages/flutter/test/material/date_picker_test.dart
@@ -4,8 +4,6 @@
 
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
 
 import 'feedback_tester.dart';
 
@@ -419,250 +417,4 @@
       expect(await date, isNull);
     });
   });
-
-  group(DayPicker, () {
-    final Map<Locale, Map<String, dynamic>> testLocales = <Locale, Map<String, dynamic>>{
-      // Tests the default.
-      const Locale('en', 'US'): <String, dynamic>{
-        'textDirection': TextDirection.ltr,
-        'expectedDaysOfWeek': <String>['S', 'M', 'T', 'W', 'T', 'F', 'S'],
-        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
-        'expectedMonthYearHeader': 'September 2017',
-      },
-      // Tests a different first day of week.
-      const Locale('ru', 'RU'): <String, dynamic>{
-        'textDirection': TextDirection.ltr,
-        'expectedDaysOfWeek': <String>['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс'],
-        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
-        'expectedMonthYearHeader': 'сентябрь 2017 г.',
-      },
-      // Tests RTL.
-      // TODO: change to Arabic numerals when these are fixed:
-      // TODO: https://github.com/dart-lang/intl/issues/143
-      // TODO: https://github.com/flutter/flutter/issues/12289
-      const Locale('ar', 'AR'): <String, dynamic>{
-        'textDirection': TextDirection.rtl,
-        'expectedDaysOfWeek': <String>['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
-        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
-        'expectedMonthYearHeader': 'سبتمبر 2017',
-      },
-    };
-
-    for (Locale locale in testLocales.keys) {
-      testWidgets('shows dates for $locale', (WidgetTester tester) async {
-        final List<String> expectedDaysOfWeek = testLocales[locale]['expectedDaysOfWeek'];
-        final List<String> expectedDaysOfMonth = testLocales[locale]['expectedDaysOfMonth'];
-        final String expectedMonthYearHeader = testLocales[locale]['expectedMonthYearHeader'];
-        final TextDirection textDirection = testLocales[locale]['textDirection'];
-        final DateTime baseDate = new DateTime(2017, 9, 27);
-
-        await _pumpBoilerplate(tester, new DayPicker(
-          selectedDate: baseDate,
-          currentDate: baseDate,
-          onChanged: (DateTime newValue) {},
-          firstDate: baseDate.subtract(const Duration(days: 90)),
-          lastDate: baseDate.add(const Duration(days: 90)),
-          displayedMonth: baseDate,
-        ), locale: locale, textDirection: textDirection);
-
-        expect(find.text(expectedMonthYearHeader), findsOneWidget);
-
-        expectedDaysOfWeek.forEach((String dayOfWeek) {
-          expect(find.text(dayOfWeek), findsWidgets);
-        });
-
-        Offset previousCellOffset;
-        expectedDaysOfMonth.forEach((String dayOfMonth) {
-          final Finder dayCell = find.descendant(of: find.byType(GridView), matching: find.text(dayOfMonth));
-          expect(dayCell, findsOneWidget);
-
-          // Check that cells are correctly positioned relative to each other,
-          // taking text direction into account.
-          final Offset offset = tester.getCenter(dayCell);
-          if (previousCellOffset != null) {
-            if (textDirection == TextDirection.ltr) {
-              expect(offset.dx > previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
-            } else {
-              expect(offset.dx < previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
-            }
-          }
-          previousCellOffset = offset;
-        });
-      });
-    }
-  });
-
-  testWidgets('locale parameter overrides ambient locale', (WidgetTester tester) async {
-    await tester.pumpWidget(new MaterialApp(
-      locale: const Locale('en', 'US'),
-      supportedLocales: const <Locale>[
-        const Locale('en', 'US'),
-        const Locale('fr', 'CA'),
-      ],
-      home: new Material(
-        child: new Builder(
-          builder: (BuildContext context) {
-            return new FlatButton(
-              onPressed: () async {
-                await showDatePicker(
-                  context: context,
-                  initialDate: initialDate,
-                  firstDate: firstDate,
-                  lastDate: lastDate,
-                  locale: const Locale('fr', 'CA'),
-                );
-              },
-              child: const Text('X'),
-            );
-          },
-        ),
-      ),
-    ));
-
-    await tester.tap(find.text('X'));
-    await tester.pumpAndSettle(const Duration(seconds: 1));
-
-    final Element dayPicker = tester.element(find.byType(DayPicker));
-    expect(
-      Localizations.localeOf(dayPicker),
-      const Locale('fr', 'CA'),
-    );
-
-    expect(
-      Directionality.of(dayPicker),
-      TextDirection.ltr,
-    );
-
-    await tester.tap(find.text('ANNULER'));
-  });
-
-  testWidgets('textDirection parameter overrides ambient textDirection', (WidgetTester tester) async {
-    await tester.pumpWidget(new MaterialApp(
-      locale: const Locale('en', 'US'),
-      supportedLocales: const <Locale>[
-        const Locale('en', 'US'),
-      ],
-      home: new Material(
-        child: new Builder(
-          builder: (BuildContext context) {
-            return new FlatButton(
-              onPressed: () async {
-                await showDatePicker(
-                  context: context,
-                  initialDate: initialDate,
-                  firstDate: firstDate,
-                  lastDate: lastDate,
-                  textDirection: TextDirection.rtl,
-                );
-              },
-              child: const Text('X'),
-            );
-          },
-        ),
-      ),
-    ));
-
-    await tester.tap(find.text('X'));
-    await tester.pumpAndSettle(const Duration(seconds: 1));
-
-    final Element dayPicker = tester.element(find.byType(DayPicker));
-    expect(
-      Directionality.of(dayPicker),
-      TextDirection.rtl,
-    );
-
-    await tester.tap(find.text('CANCEL'));
-  });
-
-  testWidgets('textDirection parameter takes precendence over locale parameter', (WidgetTester tester) async {
-    await tester.pumpWidget(new MaterialApp(
-      locale: const Locale('en', 'US'),
-      supportedLocales: const <Locale>[
-        const Locale('en', 'US'),
-        const Locale('fr', 'CA'),
-      ],
-      home: new Material(
-        child: new Builder(
-          builder: (BuildContext context) {
-            return new FlatButton(
-              onPressed: () async {
-                await showDatePicker(
-                  context: context,
-                  initialDate: initialDate,
-                  firstDate: firstDate,
-                  lastDate: lastDate,
-                  locale: const Locale('fr', 'CA'),
-                  textDirection: TextDirection.rtl,
-                );
-              },
-              child: const Text('X'),
-            );
-          },
-        ),
-      ),
-    ));
-
-    await tester.tap(find.text('X'));
-    await tester.pumpAndSettle(const Duration(seconds: 1));
-
-    final Element dayPicker = tester.element(find.byType(DayPicker));
-    expect(
-      Localizations.localeOf(dayPicker),
-      const Locale('fr', 'CA'),
-    );
-
-    expect(
-      Directionality.of(dayPicker),
-      TextDirection.rtl,
-    );
-
-    await tester.tap(find.text('ANNULER'));
-  });
-}
-
-Future<Null> _pumpBoilerplate(
-  WidgetTester tester,
-  Widget child, {
-  Locale locale = const Locale('en', 'US'),
-  TextDirection textDirection: TextDirection.ltr
-}) async {
-  await tester.pumpWidget(new Directionality(
-    textDirection: TextDirection.ltr,
-    child: new Localizations(
-      locale: locale,
-      delegates: <LocalizationsDelegate<dynamic>>[
-        new _MaterialLocalizationsDelegate(
-          new DefaultMaterialLocalizations(locale),
-        ),
-        const DefaultWidgetsLocalizationsDelegate(),
-      ],
-      child: child,
-    ),
-  ));
-}
-
-class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
-  const _MaterialLocalizationsDelegate(this.localizations);
-
-  final MaterialLocalizations localizations;
-
-  @override
-  Future<MaterialLocalizations> load(Locale locale) {
-    return new SynchronousFuture<MaterialLocalizations>(localizations);
-  }
-
-  @override
-  bool shouldReload(_MaterialLocalizationsDelegate old) => false;
-}
-
-class DefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
-  const DefaultWidgetsLocalizationsDelegate();
-
-  @override
-  Future<WidgetsLocalizations> load(Locale locale) {
-    return new SynchronousFuture<WidgetsLocalizations>(new DefaultWidgetsLocalizations(locale));
-  }
-
-  @override
-  bool shouldReload(DefaultWidgetsLocalizationsDelegate old) => false;
 }
diff --git a/packages/flutter/test/material/localizations_test.dart b/packages/flutter/test/material/localizations_test.dart
index 66366d1..a9c7f33 100644
--- a/packages/flutter/test/material/localizations_test.dart
+++ b/packages/flutter/test/material/localizations_test.dart
@@ -3,323 +3,51 @@
 // 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;
-}
-
-/// A localizations delegate that does not contain any useful data, and is only
-/// used to trigger didChangeDependencies upon locale change.
-class _DummyLocalizationsDelegate extends LocalizationsDelegate<DummyLocalizations> {
-  @override
-  Future<DummyLocalizations> load(Locale locale) async => new DummyLocalizations();
-
-  @override
-  bool shouldReload(_DummyLocalizationsDelegate old) => true;
-}
-
-class DummyLocalizations {}
-
-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'),
-  ],
-}) {
-  return new MaterialApp(
-    color: const Color(0xFFFFFFFF),
-    locale: locale,
-    localizationsDelegates: delegates,
-    localeResolutionCallback: localeResolutionCallback,
-    supportedLocales: supportedLocales,
-    onGenerateRoute: (RouteSettings settings) {
-      return new MaterialPageRoute<Null>(
-        builder: (BuildContext context) {
-          return buildContent(context);
-        }
-      );
-    },
-  );
-}
-
 void main() {
-  testWidgets('sanity check', (WidgetTester tester) async {
-    final Key textKey = new UniqueKey();
+  testWidgets('English translations exist for all MaterialLocalizations properties', (WidgetTester tester) async {
+    final MaterialLocalizations localizations = const DefaultMaterialLocalizations();
 
-    await tester.pumpWidget(
-      buildFrame(
-        buildContent: (BuildContext context) {
-          return new Text(
-            MaterialLocalizations.of(context).backButtonTooltip,
-            key: textKey,
-          );
-        }
-      )
-    );
+    expect(localizations.openAppDrawerTooltip, isNotNull);
+    expect(localizations.backButtonTooltip, isNotNull);
+    expect(localizations.closeButtonTooltip, isNotNull);
+    expect(localizations.nextMonthTooltip, isNotNull);
+    expect(localizations.previousMonthTooltip, isNotNull);
+    expect(localizations.nextPageTooltip, isNotNull);
+    expect(localizations.previousPageTooltip, isNotNull);
+    expect(localizations.showMenuTooltip, isNotNull);
+    expect(localizations.licensesPageTitle, isNotNull);
+    expect(localizations.rowsPerPageTitle, isNotNull);
+    expect(localizations.cancelButtonLabel, isNotNull);
+    expect(localizations.closeButtonLabel, isNotNull);
+    expect(localizations.continueButtonLabel, isNotNull);
+    expect(localizations.copyButtonLabel, isNotNull);
+    expect(localizations.cutButtonLabel, isNotNull);
+    expect(localizations.okButtonLabel, isNotNull);
+    expect(localizations.pasteButtonLabel, isNotNull);
+    expect(localizations.selectAllButtonLabel, isNotNull);
+    expect(localizations.viewLicensesButtonLabel, isNotNull);
 
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
+    expect(localizations.aboutListTileTitle('FOO'), isNotNull);
+    expect(localizations.aboutListTileTitle('FOO'), contains('FOO'));
 
-    // Unrecognized locale falls back to 'en'
-    await tester.binding.setLocale('foo', 'bar');
-    await tester.pump();
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
+    expect(localizations.selectedRowCountTitle(0), isNotNull);
+    expect(localizations.selectedRowCountTitle(1), isNotNull);
+    expect(localizations.selectedRowCountTitle(2), isNotNull);
+    expect(localizations.selectedRowCountTitle(100), isNotNull);
+    expect(localizations.selectedRowCountTitle(0).contains(r'$selectedRowCount'), isFalse);
+    expect(localizations.selectedRowCountTitle(1).contains(r'$selectedRowCount'), isFalse);
+    expect(localizations.selectedRowCountTitle(2).contains(r'$selectedRowCount'), isFalse);
+    expect(localizations.selectedRowCountTitle(100).contains(r'$selectedRowCount'), isFalse);
 
-    // 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');
-
-  });
-
-  testWidgets('translations exist for all materia/i18n languages', (WidgetTester tester) async {
-    final List<String> languages = <String>[
-      'ar', // Arabic
-      'de', // German
-      'en', // English
-      'es', // Spanish
-      'fa', // Farsi (Persian)
-      'fr', // French
-      'he', // Hebrew
-      'it', // Italian
-      'ja', // Japanese
-      'ps', // Pashto
-      'pt', // Portugese
-      'ru', // Russian
-      'sd', // Sindhi
-      'ur', // Urdu
-      'zh', // Chinese (simplified)
-    ];
-
-    for (String language in languages) {
-      final Locale locale = new Locale(language, '');
-      final MaterialLocalizations localizations = new DefaultMaterialLocalizations(locale);
-
-      expect(localizations.openAppDrawerTooltip, isNotNull);
-      expect(localizations.backButtonTooltip, isNotNull);
-      expect(localizations.closeButtonTooltip, isNotNull);
-      expect(localizations.nextMonthTooltip, isNotNull);
-      expect(localizations.previousMonthTooltip, isNotNull);
-      expect(localizations.nextPageTooltip, isNotNull);
-      expect(localizations.previousPageTooltip, isNotNull);
-      expect(localizations.showMenuTooltip, isNotNull);
-      expect(localizations.licensesPageTitle, isNotNull);
-      expect(localizations.rowsPerPageTitle, isNotNull);
-      expect(localizations.cancelButtonLabel, isNotNull);
-      expect(localizations.closeButtonLabel, isNotNull);
-      expect(localizations.continueButtonLabel, isNotNull);
-      expect(localizations.copyButtonLabel, isNotNull);
-      expect(localizations.cutButtonLabel, isNotNull);
-      expect(localizations.okButtonLabel, isNotNull);
-      expect(localizations.pasteButtonLabel, isNotNull);
-      expect(localizations.selectAllButtonLabel, isNotNull);
-      expect(localizations.viewLicensesButtonLabel, isNotNull);
-
-      expect(localizations.aboutListTileTitle('FOO'), isNotNull);
-      expect(localizations.aboutListTileTitle('FOO'), contains('FOO'));
-
-      expect(localizations.selectedRowCountTitle(0), isNotNull);
-      expect(localizations.selectedRowCountTitle(1), isNotNull);
-      expect(localizations.selectedRowCountTitle(2), isNotNull);
-      expect(localizations.selectedRowCountTitle(100), isNotNull);
-      expect(localizations.selectedRowCountTitle(0).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(1).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(2).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(100).contains(r'$selectedRowCount'), isFalse);
-
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNotNull);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNotNull);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$firstRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$lastRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$rowCount'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$firstRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$lastRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$rowCount'), isFalse);
-    }
-  });
-
-  testWidgets('spot check selectedRowCount translations', (WidgetTester tester) async {
-    MaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('en', ''));
-    expect(localizations.selectedRowCountTitle(0), 'No items selected');
-    expect(localizations.selectedRowCountTitle(1), '1 item selected');
-    expect(localizations.selectedRowCountTitle(2), '2 items selected');
-    expect(localizations.selectedRowCountTitle(123456789), '123,456,789 items selected');
-
-    localizations = new DefaultMaterialLocalizations(const Locale('es', ''));
-    expect(localizations.selectedRowCountTitle(0), 'No se han seleccionado elementos');
-    expect(localizations.selectedRowCountTitle(1), '1 artículo seleccionado');
-    expect(localizations.selectedRowCountTitle(2), '2 artículos seleccionados');
-    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();
-
-    await tester.pumpWidget(
-      buildFrame(
-        supportedLocales: <Locale>[
-          const Locale('en', 'US'),
-          const Locale('he', 'IL'),
-          const Locale('yi', 'IL'),
-          const Locale('id', 'JV'),
-        ],
-        buildContent: (BuildContext context) {
-          return new Text(
-            '${Localizations.localeOf(context)}',
-            key: textKey,
-          );
-        },
-      )
-    );
-
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'en_US');
-
-    // Hebrew was iw (ISO-639) is he (ISO-639-1)
-    await tester.binding.setLocale('iw', 'IL');
-    await tester.pump();
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'he_IL');
-
-    // Yiddish was ji (ISO-639) is yi (ISO-639-1)
-    await tester.binding.setLocale('ji', 'IL');
-    await tester.pump();
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'yi_IL');
-
-    // Indonesian was in (ISO-639) is id (ISO-639-1)
-    await tester.binding.setLocale('in', 'JV');
-    await tester.pump();
-    expect(tester.widget<Text>(find.byKey(textKey)).data, 'id_JV');
-  });
-
-  testWidgets('Localizations is compatible with ChangeNotifier.dispose() called during didChangeDependencies', (WidgetTester tester) async {
-    // PageView calls ScrollPosition.dispose() during didChangeDependencies.
-    await tester.pumpWidget(new MaterialApp(
-      supportedLocales: const <Locale>[
-        const Locale('en', 'US'),
-        const Locale('es', 'ES'),
-      ],
-      localizationsDelegates: <_DummyLocalizationsDelegate>[
-        new _DummyLocalizationsDelegate(),
-      ],
-      home: new PageView(),
-    ));
-
-    await tester.binding.setLocale('es', 'US');
-    await tester.pump();
-    await tester.pumpWidget(new Container());
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNotNull);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNotNull);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$firstRow'), isFalse);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$lastRow'), isFalse);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$rowCount'), isFalse);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$firstRow'), isFalse);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$lastRow'), isFalse);
+    expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$rowCount'), isFalse);
   });
 }
diff --git a/packages/flutter/test/material/material_localizations_test.dart b/packages/flutter/test/material/material_localizations_test.dart
deleted file mode 100644
index b4eba2d..0000000
--- a/packages/flutter/test/material/material_localizations_test.dart
+++ /dev/null
@@ -1,193 +0,0 @@
-// 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.
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:flutter/widgets.dart';
-
-void main() {
-  testWidgets('$MaterialLocalizations localizes text inside the tree', (WidgetTester tester) async {
-    await tester.pumpWidget(new MaterialApp(
-      home: new ListView(
-        children: <Widget>[
-          new LocalizationTracker(key: const ValueKey<String>('outer')),
-          new Localizations(
-            locale: const Locale('zh', 'CN'),
-            delegates: <LocalizationsDelegate<dynamic>>[
-              new _MaterialLocalizationsDelegate(
-                new DefaultMaterialLocalizations(const Locale('zh', 'CN')),
-              ),
-              const DefaultWidgetsLocalizationsDelegate(),
-            ],
-            child: new LocalizationTracker(key: const ValueKey<String>('inner')),
-          ),
-        ],
-      ),
-    ));
-
-    final LocalizationTrackerState outerTracker = tester.state(find.byKey(const ValueKey<String>('outer')));
-    expect(outerTracker.captionFontSize, 12.0);
-    final LocalizationTrackerState innerTracker = tester.state(find.byKey(const ValueKey<String>('inner')));
-    expect(innerTracker.captionFontSize, 13.0);
-  });
-
-  group(DefaultMaterialLocalizations, () {
-    test('uses exact locale when exists', () {
-      final DefaultMaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('pt', 'PT'));
-      expect(localizations.formatDecimal(10000), '10\u00A0000');
-    });
-
-    test('falls back to language code when exact locale is missing', () {
-      final DefaultMaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('pt', 'XX'));
-      expect(localizations.formatDecimal(10000), '10.000');
-    });
-
-    test('falls back to default format when neither language code nor exact locale are available', () {
-      final DefaultMaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('xx', 'XX'));
-      expect(localizations.formatDecimal(10000), '10,000');
-    });
-
-    group('formatHour', () {
-      test('formats h', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('en', 'US'));
-        expect(localizations.formatHour(const TimeOfDay(hour: 10, minute: 0)), '10');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '8');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('ar', ''));
-        expect(localizations.formatHour(const TimeOfDay(hour: 10, minute: 0)), '١٠');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '٨');
-      });
-
-      test('formats HH', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('de', ''));
-        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '09');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('en', 'GB'));
-        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '09');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
-      });
-
-      test('formats H', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('es', ''));
-        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '9');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('fa', ''));
-        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '۹');
-        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '۲۰');
-      });
-    });
-
-    group('formatMinute', () {
-      test('formats English', () {
-        final DefaultMaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('en', 'US'));
-        expect(localizations.formatMinute(const TimeOfDay(hour: 1, minute: 32)), '32');
-      });
-
-      test('formats Arabic', () {
-        final DefaultMaterialLocalizations localizations = new DefaultMaterialLocalizations(const Locale('ar', ''));
-        expect(localizations.formatMinute(const TimeOfDay(hour: 1, minute: 32)), '٣٢');
-      });
-    });
-
-    group('formatTimeOfDay', () {
-      test('formats ${TimeOfDayFormat.h_colon_mm_space_a}', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('ar', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '٩:٣٢ ص');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('en', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32 AM');
-      });
-
-      test('formats ${TimeOfDayFormat.HH_colon_mm}', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('de', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09:32');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('en', 'ZA'));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09:32');
-      });
-
-      test('formats ${TimeOfDayFormat.H_colon_mm}', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('es', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32');
-
-        localizations = new DefaultMaterialLocalizations(const Locale('ja', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32');
-      });
-
-      test('formats ${TimeOfDayFormat.frenchCanadian}', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('fr', 'CA'));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09 h 32');
-      });
-
-      test('formats ${TimeOfDayFormat.a_space_h_colon_mm}', () {
-        DefaultMaterialLocalizations localizations;
-
-        localizations = new DefaultMaterialLocalizations(const Locale('zh', ''));
-        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '上午 9:32');
-      });
-    });
-  });
-}
-
-class LocalizationTracker extends StatefulWidget {
-  LocalizationTracker({Key key}) : super(key: key);
-
-  @override
-  State<StatefulWidget> createState() => new LocalizationTrackerState();
-}
-
-class LocalizationTrackerState extends State<LocalizationTracker> {
-  double captionFontSize;
-
-  @override
-  Widget build(BuildContext context) {
-    captionFontSize = Theme.of(context).textTheme.caption.fontSize;
-    return new Container();
-  }
-}
-
-// Same as _MaterialLocalizationsDelegate in widgets/app.dart
-class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
-  const _MaterialLocalizationsDelegate(this.localizations);
-
-  final MaterialLocalizations localizations;
-
-  @override
-  Future<MaterialLocalizations> load(Locale locale) {
-    return new SynchronousFuture<MaterialLocalizations>(localizations);
-  }
-
-  @override
-  bool shouldReload(_MaterialLocalizationsDelegate old) => false;
-}
-
-// Same as _WidgetsLocalizationsDelegate in widgets/app.dart
-class DefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
-  const DefaultWidgetsLocalizationsDelegate();
-
-  @override
-  Future<WidgetsLocalizations> load(Locale locale) {
-    return new SynchronousFuture<WidgetsLocalizations>(new DefaultWidgetsLocalizations(locale));
-  }
-
-  @override
-  bool shouldReload(DefaultWidgetsLocalizationsDelegate old) => false;
-}
diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart
index 09a0491..863dcef 100644
--- a/packages/flutter/test/material/time_picker_test.dart
+++ b/packages/flutter/test/material/time_picker_test.dart
@@ -205,78 +205,4 @@
       expect(feedback.hapticCount, 3);
     });
   });
-
-  group('localization', () {
-    testWidgets('can localize the header in all known formats', (WidgetTester tester) async {
-      // TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
-      final Map<Locale, List<String>> locales = <Locale, List<String>>{
-        const Locale('en', 'US'): const <String>['hour h', 'string :', 'minute', 'period'], //'h:mm a'
-        const Locale('en', 'GB'): const <String>['hour HH', 'string :', 'minute'], //'HH:mm'
-        const Locale('es', 'ES'): const <String>['hour H', 'string :', 'minute'], //'H:mm'
-        const Locale('fr', 'CA'): const <String>['hour HH', 'string h', 'minute'], //'HH \'h\' mm'
-        const Locale('zh', 'ZH'): const <String>['period', 'hour h', 'string :', 'minute'], //'ah:mm'
-      };
-
-      for (Locale locale in locales.keys) {
-        final Offset center = await startPicker(tester, (TimeOfDay time) { }, locale: locale);
-        final List<String> actual = <String>[];
-        tester.element(find.byType(CustomMultiChildLayout)).visitChildren((Element child) {
-          final LayoutId layout = child.widget;
-          final String fragmentType = '${layout.child.runtimeType}';
-          final dynamic widget = layout.child;
-          if (fragmentType == '_MinuteControl') {
-            actual.add('minute');
-          } else if (fragmentType == '_DayPeriodControl') {
-            actual.add('period');
-          } else if (fragmentType == '_HourControl') {
-            actual.add('hour ${widget.hourFormat.toString().split('.').last}');
-          } else if (fragmentType == '_StringFragment') {
-            actual.add('string ${widget.value}');
-          } else {
-            fail('Unsupported fragment type: $fragmentType');
-          }
-        });
-        expect(actual, locales[locale]);
-        await tester.tapAt(new Offset(center.dx, center.dy - 50.0));
-        await finishPicker(tester);
-      }
-    });
-
-    testWidgets('uses single-ring 12-hour dial for h hour format', (WidgetTester tester) async {
-      // Tap along the segment stretching from the center to the edge at
-      // 12:00 AM position. Because there's only one ring, no matter where you
-      // tap the time will be the same. See the 24-hour dial test that behaves
-      // differently.
-      for (int i = 1; i < 10; i++) {
-        TimeOfDay result;
-        final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; });
-        final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
-        final double dy = (size.height / 2.0 / 10) * i;
-        await tester.tapAt(new Offset(center.dx, center.dy - dy));
-        await finishPicker(tester);
-        expect(result, equals(const TimeOfDay(hour: 0, minute: 0)));
-      }
-    });
-
-    testWidgets('uses two-ring 24-hour dial for H and HH hour formats', (WidgetTester tester) async {
-      const List<Locale> locales = const <Locale>[
-        const Locale('en', 'GB'), // HH
-        const Locale('es', 'ES'), // H
-      ];
-      for (Locale locale in locales) {
-        // Tap along the segment stretching from the center to the edge at
-        // 12:00 AM position. There are two rings. At ~70% mark, the ring
-        // switches between inner ring and outer ring.
-        for (int i = 1; i < 10; i++) {
-          TimeOfDay result;
-          final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; }, locale: locale);
-          final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
-          final double dy = (size.height / 2.0 / 10) * i;
-          await tester.tapAt(new Offset(center.dx, center.dy - dy));
-          await finishPicker(tester);
-          expect(result, equals(new TimeOfDay(hour: i < 7 ? 12 : 0, minute: 0)));
-        }
-      }
-    });
-  });
 }
diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml
index 227a644..ffa29d6 100644
--- a/packages/flutter_driver/pubspec.yaml
+++ b/packages/flutter_driver/pubspec.yaml
@@ -27,7 +27,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
diff --git a/packages/flutter_localizations/lib/flutter_localizations.dart b/packages/flutter_localizations/lib/flutter_localizations.dart
new file mode 100644
index 0000000..934f31c
--- /dev/null
+++ b/packages/flutter_localizations/lib/flutter_localizations.dart
@@ -0,0 +1,9 @@
+// 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.
+
+/// Localizations for the Flutter library
+library flutter_localizations;
+
+export 'src/material_localizations.dart' show GlobalMaterialLocalizations;
+export 'src/widgets_localizations.dart' show GlobalWidgetsLocalizations;
diff --git a/packages/flutter/lib/src/material/i18n/README.md b/packages/flutter_localizations/lib/src/l10n/README.md
similarity index 93%
rename from packages/flutter/lib/src/material/i18n/README.md
rename to packages/flutter_localizations/lib/src/l10n/README.md
index e1d030e..ff2ef14 100644
--- a/packages/flutter/lib/src/material/i18n/README.md
+++ b/packages/flutter_localizations/lib/src/l10n/README.md
@@ -1,12 +1,12 @@
-# Material Library Internationalization
+# Material Library Localizations
 
 The `.arb` files in this directory contain localized values (primarily
 strings) used by the material library.  The `localizations.dart` file
-combines all of the localizations into a single Dart Map that is
-linked with the rest of the material library.
+combines all of the localizations into a single Map that is
+linked with the rest of flutter_localizations package.
 
 If you're looking for information about internationalizing Flutter
-apps in general, see th
+apps in general, see the
 [Internationalizing Flutter Apps](https://flutter.io/tutorials/internationalization/) tutorial.
 
 
@@ -54,7 +54,7 @@
 resource ID.
 
 Each of the language-specific .arb files contains an entry for
-`cancelButtonLabel`. They're all represented by the Dart `Map` in the
+`cancelButtonLabel`. They're all represented by the `Map` in the
 generated `localizations.dart` file. The Map is used by the
 MaterialLocalizations class.
 
@@ -123,18 +123,18 @@
 ```
 
 
-### Generated file localizations.dart: all of the localizations as a Dart Map
+### Generated file localizations.dart: all of the localizations as a Map
 
 If you look at the comment at the top of `localizations.dart` you'll
 see that it was manually generated using a `dev/tools` app called
 `gen_localizations` roughly like this:
 
 ```dart
-dev/tools/gen_localizations.dart packages/flutter/lib/src/material/i18n material
+dart dev/tools/gen_localizations.dart packages/flutter_localizations/lib/src/l10n material
 ```
 
 The gen_localizations app just combines the contents of all of the
-.arb files into a single Dart `Map` that has entries for each .arb
+.arb files into a single `Map` that has entries for each .arb
 file's locale. The `MaterialLocalizations` class implementation uses
 this Map to implement the methods that lookup localized resource
 values.
diff --git a/packages/flutter/lib/src/material/i18n/localizations.dart b/packages/flutter_localizations/lib/src/l10n/localizations.dart
similarity index 99%
rename from packages/flutter/lib/src/material/i18n/localizations.dart
rename to packages/flutter_localizations/lib/src/l10n/localizations.dart
index 09ccef5..987e53f 100644
--- a/packages/flutter/lib/src/material/i18n/localizations.dart
+++ b/packages/flutter_localizations/lib/src/l10n/localizations.dart
@@ -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 packages/flutter/lib/src/material/i18n material
+// dart dev/tools/gen_localizations.dart packages/flutter_localizations/lib/src/l10n material
 
 /// Maps from [Locale.languageCode] to a map that contains the localized strings
 /// for that locale.
@@ -452,4 +452,3 @@
     "postMeridiemAbbreviation": r"下午",
   },
 };
-
diff --git a/packages/flutter/lib/src/material/i18n/material_ar.arb b/packages/flutter_localizations/lib/src/l10n/material_ar.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_ar.arb
rename to packages/flutter_localizations/lib/src/l10n/material_ar.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_de.arb b/packages/flutter_localizations/lib/src/l10n/material_de.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_de.arb
rename to packages/flutter_localizations/lib/src/l10n/material_de.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_en.arb b/packages/flutter_localizations/lib/src/l10n/material_en.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_en.arb
rename to packages/flutter_localizations/lib/src/l10n/material_en.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_en_GB.arb b/packages/flutter_localizations/lib/src/l10n/material_en_GB.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_en_GB.arb
rename to packages/flutter_localizations/lib/src/l10n/material_en_GB.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_en_IE.arb b/packages/flutter_localizations/lib/src/l10n/material_en_IE.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_en_IE.arb
rename to packages/flutter_localizations/lib/src/l10n/material_en_IE.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_en_ZA.arb b/packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_en_ZA.arb
rename to packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_es.arb b/packages/flutter_localizations/lib/src/l10n/material_es.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_es.arb
rename to packages/flutter_localizations/lib/src/l10n/material_es.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_es_US.arb b/packages/flutter_localizations/lib/src/l10n/material_es_US.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_es_US.arb
rename to packages/flutter_localizations/lib/src/l10n/material_es_US.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_fa.arb b/packages/flutter_localizations/lib/src/l10n/material_fa.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_fa.arb
rename to packages/flutter_localizations/lib/src/l10n/material_fa.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_fr.arb b/packages/flutter_localizations/lib/src/l10n/material_fr.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_fr.arb
rename to packages/flutter_localizations/lib/src/l10n/material_fr.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_fr_CA.arb b/packages/flutter_localizations/lib/src/l10n/material_fr_CA.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_fr_CA.arb
rename to packages/flutter_localizations/lib/src/l10n/material_fr_CA.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_he.arb b/packages/flutter_localizations/lib/src/l10n/material_he.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_he.arb
rename to packages/flutter_localizations/lib/src/l10n/material_he.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_it.arb b/packages/flutter_localizations/lib/src/l10n/material_it.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_it.arb
rename to packages/flutter_localizations/lib/src/l10n/material_it.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_ja.arb b/packages/flutter_localizations/lib/src/l10n/material_ja.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_ja.arb
rename to packages/flutter_localizations/lib/src/l10n/material_ja.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_ps.arb b/packages/flutter_localizations/lib/src/l10n/material_ps.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_ps.arb
rename to packages/flutter_localizations/lib/src/l10n/material_ps.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_pt.arb b/packages/flutter_localizations/lib/src/l10n/material_pt.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_pt.arb
rename to packages/flutter_localizations/lib/src/l10n/material_pt.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_ru.arb b/packages/flutter_localizations/lib/src/l10n/material_ru.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_ru.arb
rename to packages/flutter_localizations/lib/src/l10n/material_ru.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_sd.arb b/packages/flutter_localizations/lib/src/l10n/material_sd.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_sd.arb
rename to packages/flutter_localizations/lib/src/l10n/material_sd.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_ur.arb b/packages/flutter_localizations/lib/src/l10n/material_ur.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_ur.arb
rename to packages/flutter_localizations/lib/src/l10n/material_ur.arb
diff --git a/packages/flutter/lib/src/material/i18n/material_zh.arb b/packages/flutter_localizations/lib/src/l10n/material_zh.arb
similarity index 100%
rename from packages/flutter/lib/src/material/i18n/material_zh.arb
rename to packages/flutter_localizations/lib/src/l10n/material_zh.arb
diff --git a/packages/flutter_localizations/lib/src/material_localizations.dart b/packages/flutter_localizations/lib/src/material_localizations.dart
new file mode 100644
index 0000000..a48dd7a
--- /dev/null
+++ b/packages/flutter_localizations/lib/src/material_localizations.dart
@@ -0,0 +1,431 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart' as intl;
+import 'package:intl/date_symbol_data_local.dart' as intl_local_date_data;
+
+import 'l10n/localizations.dart';
+import 'widgets_localizations.dart';
+
+/// Localized strings for the material widgets.
+///
+/// To include the localizations provided by this class in a [MaterialApp],
+/// add [GlobalMaterialLocalizations.delegates] to
+/// [MaterialApp.localizationsDelegates], and specify the locales your
+/// app supports with [MaterialApp.supportedLocales]:
+///
+/// ```dart
+/// new MaterialApp(
+///   localizationsDelegates: GlobalMaterialLocalizations.delegates,
+///   supportedLocales: [
+///     const Locale('en', 'US'), // English
+///     const Locale('he', 'IL'), // Hebrew
+///     // ...
+///   ],
+///   // ...
+/// )
+/// ```
+///
+/// This class supports locales with the following [Locale.languageCode]s:
+///
+///   * 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
+///
+/// See also:
+///
+///  * The Flutter Internationalization Tutorial,
+///    <https://flutter.io/tutorials/internationalization/>.
+///  * [DefaultMaterialLocalizations], which only provides US English translations.
+class GlobalMaterialLocalizations implements MaterialLocalizations {
+  /// Constructs an object that defines the material widgets' localized strings
+  /// for the given `locale`.
+  ///
+  /// [LocalizationsDelegate] implementations typically call the static [load]
+  /// function, rather than constructing this class directly.
+  GlobalMaterialLocalizations(this.locale)
+      : assert(locale != null),
+        this._localeName = _computeLocaleName(locale) {
+    _loadDateIntlDataIfNotLoaded();
+
+    if (localizations.containsKey(locale.languageCode))
+      _nameToValue.addAll(localizations[locale.languageCode]);
+    if (localizations.containsKey(_localeName))
+      _nameToValue.addAll(localizations[_localeName]);
+
+    const String kMediumDatePattern = 'E, MMM\u00a0d';
+    if (intl.DateFormat.localeExists(_localeName)) {
+      _fullYearFormat = new intl.DateFormat.y(_localeName);
+      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern, _localeName);
+      _yearMonthFormat = new intl.DateFormat('yMMMM', _localeName);
+    } else if (intl.DateFormat.localeExists(locale.languageCode)) {
+      _fullYearFormat = new intl.DateFormat.y(locale.languageCode);
+      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern, locale.languageCode);
+      _yearMonthFormat = new intl.DateFormat('yMMMM', locale.languageCode);
+    } else {
+      _fullYearFormat = new intl.DateFormat.y();
+      _mediumDateFormat = new intl.DateFormat(kMediumDatePattern);
+      _yearMonthFormat = new intl.DateFormat('yMMMM');
+    }
+
+    if (intl.NumberFormat.localeExists(_localeName)) {
+      _decimalFormat = new intl.NumberFormat.decimalPattern(_localeName);
+      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00', _localeName);
+    } else if (intl.NumberFormat.localeExists(locale.languageCode)) {
+      _decimalFormat = new intl.NumberFormat.decimalPattern(locale.languageCode);
+      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00', locale.languageCode);
+    } else {
+      _decimalFormat = new intl.NumberFormat.decimalPattern();
+      _twoDigitZeroPaddedFormat = new intl.NumberFormat('00');
+    }
+  }
+
+  /// The locale for which the values of this class's localized resources
+  /// have been translated.
+  final Locale locale;
+
+  final String _localeName;
+
+  final Map<String, String> _nameToValue = <String, String>{};
+
+  intl.NumberFormat _decimalFormat;
+
+  intl.NumberFormat _twoDigitZeroPaddedFormat;
+
+  intl.DateFormat _fullYearFormat;
+
+  intl.DateFormat _mediumDateFormat;
+
+  intl.DateFormat _yearMonthFormat;
+
+  static String _computeLocaleName(Locale locale) {
+    final String localeName = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
+    return intl.Intl.canonicalizedLocale(localeName);
+  }
+
+  // TODO(hmuller): the rules for mapping from an integer value to
+  // "one" or "two" etc. are locale specific and an additional "few" category
+  // is needed. See http://cldr.unicode.org/index/cldr-spec/plural-rules
+  String _nameToPluralValue(int count, String key) {
+    String text;
+    if (count == 0)
+      text = _nameToValue['${key}Zero'];
+    else if (count == 1)
+      text = _nameToValue['${key}One'];
+    else if (count == 2)
+      text = _nameToValue['${key}Two'];
+    else if (count > 2)
+      text = _nameToValue['${key}Many'];
+    text ??= _nameToValue['${key}Other'];
+    assert(text != null);
+    return text;
+  }
+
+  @override
+  String formatHour(TimeOfDay timeOfDay) {
+    switch (hourFormat(of: timeOfDayFormat)) {
+      case HourFormat.HH:
+        return _twoDigitZeroPaddedFormat.format(timeOfDay.hour);
+      case HourFormat.H:
+        return formatDecimal(timeOfDay.hour);
+      case HourFormat.h:
+        final int hour = timeOfDay.hourOfPeriod;
+        return formatDecimal(hour == 0 ? 12 : hour);
+    }
+    return null;
+  }
+
+  @override
+  String formatMinute(TimeOfDay timeOfDay) {
+    return _twoDigitZeroPaddedFormat.format(timeOfDay.minute);
+  }
+
+  @override
+  String formatYear(DateTime date) {
+    return _fullYearFormat.format(date);
+  }
+
+  @override
+  String formatMediumDate(DateTime date) {
+    return _mediumDateFormat.format(date);
+  }
+
+  @override
+  String formatMonthYear(DateTime date) {
+    return _yearMonthFormat.format(date);
+  }
+
+  @override
+  List<String> get narrowWeekdays {
+    return _fullYearFormat.dateSymbols.NARROWWEEKDAYS;
+  }
+
+  @override
+  int get firstDayOfWeekIndex => (_fullYearFormat.dateSymbols.FIRSTDAYOFWEEK + 1) % 7;
+
+  /// Formats a [number] using local decimal number format.
+  ///
+  /// Inserts locale-appropriate thousands separator, if necessary.
+  String formatDecimal(int number) {
+    return _decimalFormat.format(number);
+  }
+
+  @override
+  String formatTimeOfDay(TimeOfDay timeOfDay) {
+    // Not using intl.DateFormat for two reasons:
+    //
+    // - DateFormat supports more formats than our material time picker does,
+    //   and we want to be consistent across time picker format and the string
+    //   formatting of the time of day.
+    // - DateFormat operates on DateTime, which is sensitive to time eras and
+    //   time zones, while here we want to format hour and minute within one day
+    //   no matter what date the day falls on.
+    switch (timeOfDayFormat) {
+      case TimeOfDayFormat.h_colon_mm_space_a:
+        return '${formatHour(timeOfDay)}:${formatMinute(timeOfDay)} ${_formatDayPeriod(timeOfDay)}';
+      case TimeOfDayFormat.H_colon_mm:
+      case TimeOfDayFormat.HH_colon_mm:
+        return '${formatHour(timeOfDay)}:${formatMinute(timeOfDay)}';
+      case TimeOfDayFormat.HH_dot_mm:
+        return '${formatHour(timeOfDay)}.${formatMinute(timeOfDay)}';
+      case TimeOfDayFormat.a_space_h_colon_mm:
+        return '${_formatDayPeriod(timeOfDay)} ${formatHour(timeOfDay)}:${formatMinute(timeOfDay)}';
+      case TimeOfDayFormat.frenchCanadian:
+        return '${formatHour(timeOfDay)} h ${formatMinute(timeOfDay)}';
+    }
+
+    return null;
+  }
+
+  String _formatDayPeriod(TimeOfDay timeOfDay) {
+    switch (timeOfDay.period) {
+      case DayPeriod.am:
+        return anteMeridiemAbbreviation;
+      case DayPeriod.pm:
+        return postMeridiemAbbreviation;
+    }
+    return null;
+  }
+
+  @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'];
+
+  @override
+  String get nextPageTooltip => _nameToValue['nextPageTooltip'];
+
+  @override
+  String get previousPageTooltip => _nameToValue['previousPageTooltip'];
+
+  @override
+  String get showMenuTooltip => _nameToValue['showMenuTooltip'];
+
+  @override
+  String aboutListTileTitle(String applicationName) {
+    final String text = _nameToValue['aboutListTileTitle'];
+    return text.replaceFirst(r'$applicationName', applicationName);
+  }
+
+  @override
+  String get licensesPageTitle => _nameToValue['licensesPageTitle'];
+
+  @override
+  String pageRowsInfoTitle(int firstRow, int lastRow, int rowCount, bool rowCountIsApproximate) {
+    String text = rowCountIsApproximate ? _nameToValue['pageRowsInfoTitleApproximate'] : null;
+    text ??= _nameToValue['pageRowsInfoTitle'];
+    assert(text != null, 'A $locale localization was not found for pageRowsInfoTitle or pageRowsInfoTitleApproximate');
+    // TODO(hansmuller): this could be more efficient.
+    return text
+      .replaceFirst(r'$firstRow', formatDecimal(firstRow))
+      .replaceFirst(r'$lastRow', formatDecimal(lastRow))
+      .replaceFirst(r'$rowCount', formatDecimal(rowCount));
+  }
+
+  @override
+  String get rowsPerPageTitle => _nameToValue['rowsPerPageTitle'];
+
+  @override
+  String selectedRowCountTitle(int selectedRowCount) {
+    return _nameToPluralValue(selectedRowCount, 'selectedRowCountTitle') // asserts on no match
+      .replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount));
+  }
+
+  @override
+  String get cancelButtonLabel => _nameToValue['cancelButtonLabel'];
+
+  @override
+  String get closeButtonLabel => _nameToValue['closeButtonLabel'];
+
+  @override
+  String get continueButtonLabel => _nameToValue['continueButtonLabel'];
+
+  @override
+  String get copyButtonLabel => _nameToValue['copyButtonLabel'];
+
+  @override
+  String get cutButtonLabel => _nameToValue['cutButtonLabel'];
+
+  @override
+  String get okButtonLabel => _nameToValue['okButtonLabel'];
+
+  @override
+  String get pasteButtonLabel => _nameToValue['pasteButtonLabel'];
+
+  @override
+  String get selectAllButtonLabel => _nameToValue['selectAllButtonLabel'];
+
+  @override
+  String get viewLicensesButtonLabel => _nameToValue['viewLicensesButtonLabel'];
+
+  @override
+  String get anteMeridiemAbbreviation => _nameToValue['anteMeridiemAbbreviation'];
+
+  @override
+  String get postMeridiemAbbreviation => _nameToValue['postMeridiemAbbreviation'];
+
+  /// The [TimeOfDayFormat] corresponding to one of the following supported
+  /// patterns:
+  ///
+  ///  * `HH:mm`
+  ///  * `HH.mm`
+  ///  * `HH 'h' mm`
+  ///  * `HH:mm น.`
+  ///  * `H:mm`
+  ///  * `h:mm a`
+  ///  * `a h:mm`
+  ///  * `ah:mm`
+  ///
+  /// See also:
+  ///
+  ///  * http://demo.icu-project.org/icu-bin/locexp?d_=en&_=en_US shows the
+  ///    short time pattern used in locale en_US
+  @override
+  TimeOfDayFormat get timeOfDayFormat {
+    final String icuShortTimePattern = _nameToValue['timeOfDayFormat'];
+
+    assert(() {
+      if (!_icuTimeOfDayToEnum.containsKey(icuShortTimePattern)) {
+        throw new FlutterError(
+          '"$icuShortTimePattern" is not one of the ICU short time patterns '
+          'supported by the material library. Here is the list of supported '
+          'patterns:\n  ' +
+          _icuTimeOfDayToEnum.keys.join('\n  ')
+        );
+      }
+      return true;
+    }());
+
+    return _icuTimeOfDayToEnum[icuShortTimePattern];
+  }
+
+  /// Looks up text geometry defined in [MaterialTextGeometry].
+  @override
+  TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(_nameToValue["scriptCategory"]);
+
+  /// 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 GlobalMaterialLocalizations(locale));
+  }
+
+  /// A [LocalizationsDelegate] that uses [GlobalMaterialLocalizations.load]
+  /// to create an instance of this class.
+  ///
+  /// Most internationlized apps will use [GlobalMaterialLocalizations.delegates]
+  /// as the value of [MaterialApp.localizationsDelegates] to include
+  /// the localizations for both the material and widget libraries.
+  static const LocalizationsDelegate<MaterialLocalizations> delegate = const _MaterialLocalizationsDelegate();
+
+  /// A value for [MaterialApp.localizationsDelegates] that's typically used by
+  /// internationalized apps.
+  ///
+  /// To include the localizations provided by this class and by
+  /// [GlobalWidgetsLocalizations] in a [MaterialApp],
+  /// use [GlobalMaterialLocalizations.delegates] as the value of
+  /// [MaterialApp.localizationsDelegates], and specify the locales your
+  /// app supports with [MaterialApp.supportedLocales]:
+  ///
+  /// ```dart
+  /// new MaterialApp(
+  ///   localizationsDelegates: GlobalMaterialLocalizations.delegates,
+  ///   supportedLocales: [
+  ///     const Locale('en', 'US'), // English
+  ///     const Locale('he', 'IL'), // Hebrew
+  ///   ],
+  ///   // ...
+  /// )
+  /// ```
+  static const List<LocalizationsDelegate<dynamic>> delegates = const <LocalizationsDelegate<dynamic>>[
+    GlobalMaterialLocalizations.delegate,
+    GlobalWidgetsLocalizations.delegate,
+  ];
+}
+
+const Map<String, TimeOfDayFormat> _icuTimeOfDayToEnum = const <String, TimeOfDayFormat>{
+  'HH:mm': TimeOfDayFormat.HH_colon_mm,
+  'HH.mm': TimeOfDayFormat.HH_dot_mm,
+  "HH 'h' mm": TimeOfDayFormat.frenchCanadian,
+  'HH:mm น.': TimeOfDayFormat.HH_colon_mm,
+  'H:mm': TimeOfDayFormat.H_colon_mm,
+  'h:mm a': TimeOfDayFormat.h_colon_mm_space_a,
+  'a h:mm': TimeOfDayFormat.a_space_h_colon_mm,
+  'ah:mm': TimeOfDayFormat.a_space_h_colon_mm,
+};
+
+/// Tracks if date i18n data has been loaded.
+bool _dateIntlDataInitialized = false;
+
+/// Loads i18n data for dates if it hasn't be loaded yet.
+///
+/// Only the first invocation of this function has the effect of loading the
+/// data. Subsequent invocations have no effect.
+void _loadDateIntlDataIfNotLoaded() {
+  if (!_dateIntlDataInitialized) {
+    // The returned Future is intentionally dropped on the floor. The
+    // function only returns it to be compatible with the async counterparts.
+    // The Future has no value otherwise.
+    intl_local_date_data.initializeDateFormatting();
+    _dateIntlDataInitialized = true;
+  }
+}
+
+class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
+  const _MaterialLocalizationsDelegate();
+
+  @override
+  Future<MaterialLocalizations> load(Locale locale) => GlobalMaterialLocalizations.load(locale);
+
+  @override
+  bool shouldReload(_MaterialLocalizationsDelegate old) => false;
+}
diff --git a/packages/flutter_localizations/lib/src/widgets_localizations.dart b/packages/flutter_localizations/lib/src/widgets_localizations.dart
new file mode 100644
index 0000000..9670af2
--- /dev/null
+++ b/packages/flutter_localizations/lib/src/widgets_localizations.dart
@@ -0,0 +1,75 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+/// Localized values for widgets.
+///
+/// Currently this class just maps [locale] to [textDirection]. All locales
+/// are [TextDirection.ltr] except for locales with the following
+/// [Locale.languageCode] values, which are [TextDirection.rtl]:
+///
+///   * ar - Arabic
+///   * fa - Farsi
+///   * he - Hebrew
+///   * ps - Pashto
+///   * sd - Sindhi
+///   * ur - Urdu
+class GlobalWidgetsLocalizations implements WidgetsLocalizations {
+  /// Construct an object that defines the localized values for the widgets
+  /// library for the given `locale`.
+  ///
+  /// [LocalizationsDelegate] implementations typically call the static [load]
+  /// function, rather than constructing this class directly.
+  GlobalWidgetsLocalizations(this.locale) {
+    final String language = locale.languageCode.toLowerCase();
+    _textDirection = _rtlLanguages.contains(language) ? TextDirection.rtl : TextDirection.ltr;
+  }
+
+  // See http://en.wikipedia.org/wiki/Right-to-left
+  static const List<String> _rtlLanguages = const <String>[
+    'ar',  // Arabic
+    'fa',  // Farsi
+    'he',  // Hebrew
+    'ps',  // Pashto
+    'sd',  // Sindhi
+    'ur',  // Urdu
+  ];
+
+  /// The locale for which the values of this class's localized resources
+  /// have been translated.
+  final Locale locale;
+
+  @override
+  TextDirection get textDirection => _textDirection;
+  TextDirection _textDirection;
+
+  /// Creates an object that provides localized resource values for the
+  /// lowest levels of the Flutter framework.
+  ///
+  /// This method is typically used to create a [LocalizationsDelegate].
+  /// The [WidgetsApp] does so by default.
+  static Future<WidgetsLocalizations> load(Locale locale) {
+    return new SynchronousFuture<WidgetsLocalizations>(new GlobalWidgetsLocalizations(locale));
+  }
+
+  /// A [LocalizationsDelegate] that uses [GlobalWidgetsLocalizations.load]
+  /// to create an instance of this class.
+  ///
+  /// [WidgetsApp] automatically adds this value to [WidgetApp.localizationsDelegates].
+  static const LocalizationsDelegate<WidgetsLocalizations> delegate = const _WidgetsLocalizationsDelegate();
+}
+
+class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
+  const _WidgetsLocalizationsDelegate();
+
+  @override
+  Future<WidgetsLocalizations> load(Locale locale) => GlobalWidgetsLocalizations.load(locale);
+
+  @override
+  bool shouldReload(_WidgetsLocalizationsDelegate old) => false;
+}
diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml
new file mode 100644
index 0000000..70fd20c
--- /dev/null
+++ b/packages/flutter_localizations/pubspec.yaml
@@ -0,0 +1,63 @@
+name: flutter_localizations
+version: 0.0.1-dev
+dependencies:
+  # To update these, use "flutter update-packages --force-upgrade".
+
+  flutter:
+    sdk: flutter
+  intl: 0.15.1
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  mockito: 2.2.0
+
+  args: 0.13.7 # TRANSITIVE DEPENDENCY
+  async: 1.13.3 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
+  boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
+  charcode: 1.1.1 # TRANSITIVE DEPENDENCY
+  cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
+  collection: 1.14.3 # TRANSITIVE DEPENDENCY
+  convert: 2.0.1 # TRANSITIVE DEPENDENCY
+  crypto: 2.0.2+1 # TRANSITIVE DEPENDENCY
+  csslib: 0.14.1 # TRANSITIVE DEPENDENCY
+  dart_style: 1.0.8 # TRANSITIVE DEPENDENCY
+  glob: 1.1.5 # TRANSITIVE DEPENDENCY
+  html: 0.13.2 # TRANSITIVE DEPENDENCY
+  http: 0.11.3+14 # TRANSITIVE DEPENDENCY
+  http_multi_server: 2.0.4 # TRANSITIVE DEPENDENCY
+  http_parser: 3.1.1 # TRANSITIVE DEPENDENCY
+  intl_translation: 0.15.0 # TRANSITIVE DEPENDENCY
+  isolate: 1.1.0 # TRANSITIVE DEPENDENCY
+  js: 0.6.1 # TRANSITIVE DEPENDENCY
+  logging: 0.11.3+1 # TRANSITIVE DEPENDENCY
+  matcher: 0.12.1+4 # TRANSITIVE DEPENDENCY
+  meta: 1.1.1 # TRANSITIVE DEPENDENCY
+  mime: 0.9.3 # TRANSITIVE DEPENDENCY
+  node_preamble: 1.4.0 # TRANSITIVE DEPENDENCY
+  package_config: 1.0.3 # TRANSITIVE DEPENDENCY
+  package_resolver: 1.0.2 # TRANSITIVE DEPENDENCY
+  path: 1.4.2 # TRANSITIVE DEPENDENCY
+  petitparser: 1.6.1 # TRANSITIVE DEPENDENCY
+  plugin: 0.2.0+2 # TRANSITIVE DEPENDENCY
+  pool: 1.3.3 # TRANSITIVE DEPENDENCY
+  pub_semver: 1.3.2 # TRANSITIVE DEPENDENCY
+  shelf: 0.7.0 # TRANSITIVE DEPENDENCY
+  shelf_packages_handler: 1.0.3 # TRANSITIVE DEPENDENCY
+  shelf_static: 0.2.5 # TRANSITIVE DEPENDENCY
+  shelf_web_socket: 0.2.2 # TRANSITIVE DEPENDENCY
+  source_map_stack_trace: 1.1.4 # TRANSITIVE DEPENDENCY
+  source_maps: 0.10.4 # TRANSITIVE DEPENDENCY
+  source_span: 1.4.0 # TRANSITIVE DEPENDENCY
+  stack_trace: 1.8.2 # TRANSITIVE DEPENDENCY
+  stream_channel: 1.6.2 # TRANSITIVE DEPENDENCY
+  string_scanner: 1.0.2 # TRANSITIVE DEPENDENCY
+  term_glyph: 1.0.0 # TRANSITIVE DEPENDENCY
+  test: 0.12.24+8 # TRANSITIVE DEPENDENCY
+  typed_data: 1.1.4 # TRANSITIVE DEPENDENCY
+  utf: 0.9.0+3 # TRANSITIVE DEPENDENCY
+  vector_math: 2.0.5 # TRANSITIVE DEPENDENCY
+  watcher: 0.9.7+4 # TRANSITIVE DEPENDENCY
+  web_socket_channel: 1.0.6 # TRANSITIVE DEPENDENCY
+  yaml: 2.1.13 # TRANSITIVE DEPENDENCY
diff --git a/packages/flutter_localizations/test/basics_test.dart b/packages/flutter_localizations/test/basics_test.dart
new file mode 100644
index 0000000..f6f7883
--- /dev/null
+++ b/packages/flutter_localizations/test/basics_test.dart
@@ -0,0 +1,79 @@
+// 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_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('Nested Localizations', (WidgetTester tester) async {
+    await tester.pumpWidget(new MaterialApp( // Creates the outer Localizations widget.
+      home: new ListView(
+        children: <Widget>[
+          new LocalizationTracker(key: const ValueKey<String>('outer')),
+          new Localizations(
+            locale: const Locale('zh', 'CN'),
+            delegates: GlobalMaterialLocalizations.delegates,
+            child: new LocalizationTracker(key: const ValueKey<String>('inner')),
+          ),
+        ],
+      ),
+    ));
+
+    final LocalizationTrackerState outerTracker = tester.state(find.byKey(const ValueKey<String>('outer')));
+    expect(outerTracker.captionFontSize, 12.0);
+    final LocalizationTrackerState innerTracker = tester.state(find.byKey(const ValueKey<String>('inner')));
+    expect(innerTracker.captionFontSize, 13.0);
+  });
+
+  testWidgets('Localizations is compatible with ChangeNotifier.dispose() called during didChangeDependencies', (WidgetTester tester) async {
+    // PageView calls ScrollPosition.dispose() during didChangeDependencies.
+    await tester.pumpWidget(
+      new MaterialApp(
+        supportedLocales: const <Locale>[
+          const Locale('en', 'US'),
+          const Locale('es', 'ES'),
+        ],
+        localizationsDelegates: <LocalizationsDelegate<dynamic>>[
+          new _DummyLocalizationsDelegate(),
+          GlobalMaterialLocalizations.delegate,
+        ],
+        home: new PageView(),
+      )
+    );
+
+    await tester.binding.setLocale('es', 'US');
+    await tester.pump();
+    await tester.pumpWidget(new Container());
+  });
+}
+
+/// A localizations delegate that does not contain any useful data, and is only
+/// used to trigger didChangeDependencies upon locale change.
+class _DummyLocalizationsDelegate extends LocalizationsDelegate<DummyLocalizations> {
+  @override
+  Future<DummyLocalizations> load(Locale locale) async => new DummyLocalizations();
+
+  @override
+  bool shouldReload(_DummyLocalizationsDelegate old) => true;
+}
+
+class DummyLocalizations {}
+
+class LocalizationTracker extends StatefulWidget {
+  LocalizationTracker({Key key}) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => new LocalizationTrackerState();
+}
+
+class LocalizationTrackerState extends State<LocalizationTracker> {
+  double captionFontSize;
+
+  @override
+  Widget build(BuildContext context) {
+    captionFontSize = Theme.of(context).textTheme.caption.fontSize;
+    return new Container();
+  }
+}
diff --git a/packages/flutter_localizations/test/date_picker_test.dart b/packages/flutter_localizations/test/date_picker_test.dart
new file mode 100644
index 0000000..123ffaf
--- /dev/null
+++ b/packages/flutter_localizations/test/date_picker_test.dart
@@ -0,0 +1,236 @@
+// 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_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  DateTime firstDate;
+  DateTime lastDate;
+  DateTime initialDate;
+
+  setUp(() {
+    firstDate = new DateTime(2001, DateTime.JANUARY, 1);
+    lastDate = new DateTime(2031, DateTime.DECEMBER, 31);
+    initialDate = new DateTime(2016, DateTime.JANUARY, 15);
+  });
+
+  group(DayPicker, () {
+    final Map<Locale, Map<String, dynamic>> testLocales = <Locale, Map<String, dynamic>>{
+      // Tests the default.
+      const Locale('en', 'US'): <String, dynamic>{
+        'textDirection': TextDirection.ltr,
+        'expectedDaysOfWeek': <String>['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
+        'expectedMonthYearHeader': 'September 2017',
+      },
+      // Tests a different first day of week.
+      const Locale('ru', 'RU'): <String, dynamic>{
+        'textDirection': TextDirection.ltr,
+        'expectedDaysOfWeek': <String>['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс'],
+        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
+        'expectedMonthYearHeader': 'сентябрь 2017 г.',
+      },
+      // Tests RTL.
+      // TODO: change to Arabic numerals when these are fixed:
+      // TODO: https://github.com/dart-lang/intl/issues/143
+      // TODO: https://github.com/flutter/flutter/issues/12289
+      const Locale('ar', 'AR'): <String, dynamic>{
+        'textDirection': TextDirection.rtl,
+        'expectedDaysOfWeek': <String>['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+        'expectedDaysOfMonth': new List<String>.generate(30, (int i) => '${i + 1}'),
+        'expectedMonthYearHeader': 'سبتمبر 2017',
+      },
+    };
+
+    for (Locale locale in testLocales.keys) {
+      testWidgets('shows dates for $locale', (WidgetTester tester) async {
+        final List<String> expectedDaysOfWeek = testLocales[locale]['expectedDaysOfWeek'];
+        final List<String> expectedDaysOfMonth = testLocales[locale]['expectedDaysOfMonth'];
+        final String expectedMonthYearHeader = testLocales[locale]['expectedMonthYearHeader'];
+        final TextDirection textDirection = testLocales[locale]['textDirection'];
+        final DateTime baseDate = new DateTime(2017, 9, 27);
+
+        await _pumpBoilerplate(tester, new DayPicker(
+          selectedDate: baseDate,
+          currentDate: baseDate,
+          onChanged: (DateTime newValue) {},
+          firstDate: baseDate.subtract(const Duration(days: 90)),
+          lastDate: baseDate.add(const Duration(days: 90)),
+          displayedMonth: baseDate,
+        ), locale: locale, textDirection: textDirection);
+
+        expect(find.text(expectedMonthYearHeader), findsOneWidget);
+
+        expectedDaysOfWeek.forEach((String dayOfWeek) {
+          expect(find.text(dayOfWeek), findsWidgets);
+        });
+
+        Offset previousCellOffset;
+        expectedDaysOfMonth.forEach((String dayOfMonth) {
+          final Finder dayCell = find.descendant(of: find.byType(GridView), matching: find.text(dayOfMonth));
+          expect(dayCell, findsOneWidget);
+
+          // Check that cells are correctly positioned relative to each other,
+          // taking text direction into account.
+          final Offset offset = tester.getCenter(dayCell);
+          if (previousCellOffset != null) {
+            if (textDirection == TextDirection.ltr) {
+              expect(offset.dx > previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
+            } else {
+              expect(offset.dx < previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
+            }
+          }
+          previousCellOffset = offset;
+        });
+      });
+    }
+  });
+
+  testWidgets('locale parameter overrides ambient locale', (WidgetTester tester) async {
+    await tester.pumpWidget(new MaterialApp(
+      locale: const Locale('en', 'US'),
+      supportedLocales: const <Locale>[
+        const Locale('en', 'US'),
+        const Locale('fr', 'CA'),
+      ],
+      localizationsDelegates: GlobalMaterialLocalizations.delegates,
+      home: new Material(
+        child: new Builder(
+          builder: (BuildContext context) {
+            return new FlatButton(
+              onPressed: () async {
+                await showDatePicker(
+                  context: context,
+                  initialDate: initialDate,
+                  firstDate: firstDate,
+                  lastDate: lastDate,
+                  locale: const Locale('fr', 'CA'),
+                );
+              },
+              child: const Text('X'),
+            );
+          },
+        ),
+      ),
+    ));
+
+    await tester.tap(find.text('X'));
+    await tester.pumpAndSettle(const Duration(seconds: 1));
+
+    final Element dayPicker = tester.element(find.byType(DayPicker));
+    expect(
+      Localizations.localeOf(dayPicker),
+      const Locale('fr', 'CA'),
+    );
+
+    expect(
+      Directionality.of(dayPicker),
+      TextDirection.ltr,
+    );
+
+    await tester.tap(find.text('ANNULER'));
+  });
+
+  testWidgets('textDirection parameter overrides ambient textDirection', (WidgetTester tester) async {
+    await tester.pumpWidget(new MaterialApp(
+      locale: const Locale('en', 'US'),
+      supportedLocales: const <Locale>[
+        const Locale('en', 'US'),
+      ],
+      home: new Material(
+        child: new Builder(
+          builder: (BuildContext context) {
+            return new FlatButton(
+              onPressed: () async {
+                await showDatePicker(
+                  context: context,
+                  initialDate: initialDate,
+                  firstDate: firstDate,
+                  lastDate: lastDate,
+                  textDirection: TextDirection.rtl,
+                );
+              },
+              child: const Text('X'),
+            );
+          },
+        ),
+      ),
+    ));
+
+    await tester.tap(find.text('X'));
+    await tester.pumpAndSettle(const Duration(seconds: 1));
+
+    final Element dayPicker = tester.element(find.byType(DayPicker));
+    expect(
+      Directionality.of(dayPicker),
+      TextDirection.rtl,
+    );
+
+    await tester.tap(find.text('CANCEL'));
+  });
+
+  testWidgets('textDirection parameter takes precendence over locale parameter', (WidgetTester tester) async {
+    await tester.pumpWidget(new MaterialApp(
+      locale: const Locale('en', 'US'),
+      supportedLocales: const <Locale>[
+        const Locale('en', 'US'),
+        const Locale('fr', 'CA'),
+      ],
+      localizationsDelegates: GlobalMaterialLocalizations.delegates,
+      home: new Material(
+        child: new Builder(
+          builder: (BuildContext context) {
+            return new FlatButton(
+              onPressed: () async {
+                await showDatePicker(
+                  context: context,
+                  initialDate: initialDate,
+                  firstDate: firstDate,
+                  lastDate: lastDate,
+                  locale: const Locale('fr', 'CA'),
+                  textDirection: TextDirection.rtl,
+                );
+              },
+              child: const Text('X'),
+            );
+          },
+        ),
+      ),
+    ));
+
+    await tester.tap(find.text('X'));
+    await tester.pumpAndSettle(const Duration(seconds: 1));
+
+    final Element dayPicker = tester.element(find.byType(DayPicker));
+    expect(
+      Localizations.localeOf(dayPicker),
+      const Locale('fr', 'CA'),
+    );
+
+    expect(
+      Directionality.of(dayPicker),
+      TextDirection.rtl,
+    );
+
+    await tester.tap(find.text('ANNULER'));
+  });
+}
+
+Future<Null> _pumpBoilerplate(
+  WidgetTester tester,
+  Widget child, {
+  Locale locale = const Locale('en', 'US'),
+  TextDirection textDirection: TextDirection.ltr
+}) async {
+  await tester.pumpWidget(new Directionality(
+    textDirection: TextDirection.ltr,
+    child: new Localizations(
+      locale: locale,
+      delegates: GlobalMaterialLocalizations.delegates,
+      child: child,
+    ),
+  ));
+}
diff --git a/packages/flutter_localizations/test/date_time_test.dart b/packages/flutter_localizations/test/date_time_test.dart
new file mode 100644
index 0000000..a1caf27
--- /dev/null
+++ b/packages/flutter_localizations/test/date_time_test.dart
@@ -0,0 +1,122 @@
+// 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.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  group(GlobalMaterialLocalizations, () {
+    test('uses exact locale when exists', () {
+      final GlobalMaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('pt', 'PT'));
+      expect(localizations.formatDecimal(10000), '10\u00A0000');
+    });
+
+    test('falls back to language code when exact locale is missing', () {
+      final GlobalMaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('pt', 'XX'));
+      expect(localizations.formatDecimal(10000), '10.000');
+    });
+
+    test('falls back to default format when neither language code nor exact locale are available', () {
+      final GlobalMaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('xx', 'XX'));
+      expect(localizations.formatDecimal(10000), '10,000');
+    });
+
+    group('formatHour', () {
+      test('formats h', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('en', 'US'));
+        expect(localizations.formatHour(const TimeOfDay(hour: 10, minute: 0)), '10');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '8');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('ar', ''));
+        expect(localizations.formatHour(const TimeOfDay(hour: 10, minute: 0)), '١٠');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '٨');
+      });
+
+      test('formats HH', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('de', ''));
+        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '09');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('en', 'GB'));
+        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '09');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
+      });
+
+      test('formats H', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('es', ''));
+        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '9');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '20');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('fa', ''));
+        expect(localizations.formatHour(const TimeOfDay(hour: 9, minute: 0)), '۹');
+        expect(localizations.formatHour(const TimeOfDay(hour: 20, minute: 0)), '۲۰');
+      });
+    });
+
+    group('formatMinute', () {
+      test('formats English', () {
+        final GlobalMaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('en', 'US'));
+        expect(localizations.formatMinute(const TimeOfDay(hour: 1, minute: 32)), '32');
+      });
+
+      test('formats Arabic', () {
+        final GlobalMaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('ar', ''));
+        expect(localizations.formatMinute(const TimeOfDay(hour: 1, minute: 32)), '٣٢');
+      });
+    });
+
+    group('formatTimeOfDay', () {
+      test('formats ${TimeOfDayFormat.h_colon_mm_space_a}', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('ar', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '٩:٣٢ ص');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('en', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32 AM');
+      });
+
+      test('formats ${TimeOfDayFormat.HH_colon_mm}', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('de', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09:32');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('en', 'ZA'));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09:32');
+      });
+
+      test('formats ${TimeOfDayFormat.H_colon_mm}', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('es', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32');
+
+        localizations = new GlobalMaterialLocalizations(const Locale('ja', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '9:32');
+      });
+
+      test('formats ${TimeOfDayFormat.frenchCanadian}', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('fr', 'CA'));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '09 h 32');
+      });
+
+      test('formats ${TimeOfDayFormat.a_space_h_colon_mm}', () {
+        GlobalMaterialLocalizations localizations;
+
+        localizations = new GlobalMaterialLocalizations(const Locale('zh', ''));
+        expect(localizations.formatTimeOfDay(const TimeOfDay(hour: 9, minute: 32)), '上午 9:32');
+      });
+    });
+  });
+}
diff --git a/packages/flutter_localizations/test/override_test.dart b/packages/flutter_localizations/test/override_test.dart
new file mode 100644
index 0000000..16cc58d
--- /dev/null
+++ b/packages/flutter_localizations/test/override_test.dart
@@ -0,0 +1,214 @@
+// 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/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+class FooMaterialLocalizations extends GlobalMaterialLocalizations {
+  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: GlobalMaterialLocalizations.delegates,
+  WidgetBuilder buildContent,
+  LocaleResolutionCallback localeResolutionCallback,
+  Iterable<Locale> supportedLocales: const <Locale>[
+    const Locale('en', 'US'),
+    const Locale('es', 'es'),
+  ],
+}) {
+  return new MaterialApp(
+    color: const Color(0xFFFFFFFF),
+    locale: locale,
+    supportedLocales: supportedLocales,
+    localizationsDelegates: delegates,
+    localeResolutionCallback: localeResolutionCallback,
+    onGenerateRoute: (RouteSettings settings) {
+      return new MaterialPageRoute<Null>(
+        builder: (BuildContext context) {
+          return buildContent(context);
+        }
+      );
+    },
+  );
+}
+
+void main() {
+  testWidgets('Locale fallbacks', (WidgetTester tester) async {
+    final Key textKey = new UniqueKey();
+
+    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');
+
+    // Unrecognized locale falls back to 'en'
+    await tester.binding.setLocale('foo', 'bar');
+    await tester.pump();
+    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');
+  });
+
+  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();
+
+    await tester.pumpWidget(
+      buildFrame(
+        supportedLocales: <Locale>[
+          const Locale('en', 'US'),
+          const Locale('he', 'IL'),
+          const Locale('yi', 'IL'),
+          const Locale('id', 'JV'),
+        ],
+        buildContent: (BuildContext context) {
+          return new Text(
+            '${Localizations.localeOf(context)}',
+            key: textKey,
+          );
+        },
+      )
+    );
+
+    expect(tester.widget<Text>(find.byKey(textKey)).data, 'en_US');
+
+    // Hebrew was iw (ISO-639) is he (ISO-639-1)
+    await tester.binding.setLocale('iw', 'IL');
+    await tester.pump();
+    expect(tester.widget<Text>(find.byKey(textKey)).data, 'he_IL');
+
+    // Yiddish was ji (ISO-639) is yi (ISO-639-1)
+    await tester.binding.setLocale('ji', 'IL');
+    await tester.pump();
+    expect(tester.widget<Text>(find.byKey(textKey)).data, 'yi_IL');
+
+    // Indonesian was in (ISO-639) is id (ISO-639-1)
+    await tester.binding.setLocale('in', 'JV');
+    await tester.pump();
+    expect(tester.widget<Text>(find.byKey(textKey)).data, 'id_JV');
+  });
+}
diff --git a/packages/flutter_localizations/test/time_picker_test.dart b/packages/flutter_localizations/test/time_picker_test.dart
new file mode 100644
index 0000000..8e770d2
--- /dev/null
+++ b/packages/flutter_localizations/test/time_picker_test.dart
@@ -0,0 +1,129 @@
+// 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/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+class _TimePickerLauncher extends StatelessWidget {
+  const _TimePickerLauncher({ Key key, this.onChanged, this.locale }) : super(key: key);
+
+  final ValueChanged<TimeOfDay> onChanged;
+  final Locale locale;
+
+  @override
+  Widget build(BuildContext context) {
+    return new MaterialApp(
+      locale: locale,
+      localizationsDelegates: GlobalMaterialLocalizations.delegates,
+      home: new Material(
+        child: new Center(
+          child: new Builder(
+            builder: (BuildContext context) {
+              return new RaisedButton(
+                child: const Text('X'),
+                onPressed: () async {
+                  onChanged(await showTimePicker(
+                    context: context,
+                    initialTime: const TimeOfDay(hour: 7, minute: 0)
+                  ));
+                }
+              );
+            }
+          )
+        )
+      )
+    );
+  }
+}
+
+Future<Offset> startPicker(WidgetTester tester, ValueChanged<TimeOfDay> onChanged,
+    { Locale locale: const Locale('en', 'US') }) async {
+  await tester.pumpWidget(new _TimePickerLauncher(onChanged: onChanged, locale: locale,));
+  await tester.tap(find.text('X'));
+  await tester.pumpAndSettle(const Duration(seconds: 1));
+  return tester.getCenter(find.byKey(const Key('time-picker-dial')));
+}
+
+Future<Null> finishPicker(WidgetTester tester) async {
+  final Element timePickerElement = tester.element(find.byElementPredicate((Element element) => element.widget.runtimeType.toString() == '_TimePickerDialog'));
+  final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(timePickerElement);
+  await tester.tap(find.text(materialLocalizations.okButtonLabel));
+  await tester.pumpAndSettle(const Duration(seconds: 1));
+}
+
+void main() {
+  testWidgets('can localize the header in all known formats', (WidgetTester tester) async {
+    // TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
+    final Map<Locale, List<String>> locales = <Locale, List<String>>{
+      const Locale('en', 'US'): const <String>['hour h', 'string :', 'minute', 'period'], //'h:mm a'
+      const Locale('en', 'GB'): const <String>['hour HH', 'string :', 'minute'], //'HH:mm'
+      const Locale('es', 'ES'): const <String>['hour H', 'string :', 'minute'], //'H:mm'
+      const Locale('fr', 'CA'): const <String>['hour HH', 'string h', 'minute'], //'HH \'h\' mm'
+      const Locale('zh', 'ZH'): const <String>['period', 'hour h', 'string :', 'minute'], //'ah:mm'
+    };
+
+    for (Locale locale in locales.keys) {
+      final Offset center = await startPicker(tester, (TimeOfDay time) { }, locale: locale);
+      final List<String> actual = <String>[];
+      tester.element(find.byType(CustomMultiChildLayout)).visitChildren((Element child) {
+        final LayoutId layout = child.widget;
+        final String fragmentType = '${layout.child.runtimeType}';
+        final dynamic widget = layout.child;
+        if (fragmentType == '_MinuteControl') {
+          actual.add('minute');
+        } else if (fragmentType == '_DayPeriodControl') {
+          actual.add('period');
+        } else if (fragmentType == '_HourControl') {
+          actual.add('hour ${widget.hourFormat.toString().split('.').last}');
+        } else if (fragmentType == '_StringFragment') {
+          actual.add('string ${widget.value}');
+        } else {
+          fail('Unsupported fragment type: $fragmentType');
+        }
+      });
+      expect(actual, locales[locale]);
+      await tester.tapAt(new Offset(center.dx, center.dy - 50.0));
+      await finishPicker(tester);
+    }
+  });
+
+  testWidgets('uses single-ring 12-hour dial for h hour format', (WidgetTester tester) async {
+    // Tap along the segment stretching from the center to the edge at
+    // 12:00 AM position. Because there's only one ring, no matter where you
+    // tap the time will be the same. See the 24-hour dial test that behaves
+    // differently.
+    for (int i = 1; i < 10; i++) {
+      TimeOfDay result;
+      final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; });
+      final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
+      final double dy = (size.height / 2.0 / 10) * i;
+      await tester.tapAt(new Offset(center.dx, center.dy - dy));
+      await finishPicker(tester);
+      expect(result, equals(const TimeOfDay(hour: 0, minute: 0)));
+    }
+  });
+
+  testWidgets('uses two-ring 24-hour dial for H and HH hour formats', (WidgetTester tester) async {
+    const List<Locale> locales = const <Locale>[
+      const Locale('en', 'GB'), // HH
+      const Locale('es', 'ES'), // H
+    ];
+    for (Locale locale in locales) {
+      // Tap along the segment stretching from the center to the edge at
+      // 12:00 AM position. There are two rings. At ~70% mark, the ring
+      // switches between inner ring and outer ring.
+      for (int i = 1; i < 10; i++) {
+        TimeOfDay result;
+        final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; }, locale: locale);
+        final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
+        final double dy = (size.height / 2.0 / 10) * i;
+        await tester.tapAt(new Offset(center.dx, center.dy - dy));
+        await finishPicker(tester);
+        expect(result, equals(new TimeOfDay(hour: i < 7 ? 12 : 0, minute: 0)));
+      }
+    }
+  });
+}
diff --git a/packages/flutter_localizations/test/translations_test.dart b/packages/flutter_localizations/test/translations_test.dart
new file mode 100644
index 0000000..81c2c4c
--- /dev/null
+++ b/packages/flutter_localizations/test/translations_test.dart
@@ -0,0 +1,89 @@
+// 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.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  final List<String> languages = <String>[
+    'ar', // Arabic
+    'de', // German
+    'en', // English
+    'es', // Spanish
+    'fa', // Farsi (Persian)
+    'fr', // French
+    'he', // Hebrew
+    'it', // Italian
+    'ja', // Japanese
+    'ps', // Pashto
+    'pt', // Portugese
+    'ru', // Russian
+    'sd', // Sindhi
+    'ur', // Urdu
+    'zh', // Chinese (simplified)
+  ];
+
+  for (String language in languages) {
+    testWidgets('translations exist for $language', (WidgetTester tester) async {
+      final Locale locale = new Locale(language, '');
+      final MaterialLocalizations localizations = new GlobalMaterialLocalizations(locale);
+
+      expect(localizations.openAppDrawerTooltip, isNotNull);
+      expect(localizations.backButtonTooltip, isNotNull);
+      expect(localizations.closeButtonTooltip, isNotNull);
+      expect(localizations.nextMonthTooltip, isNotNull);
+      expect(localizations.previousMonthTooltip, isNotNull);
+      expect(localizations.nextPageTooltip, isNotNull);
+      expect(localizations.previousPageTooltip, isNotNull);
+      expect(localizations.showMenuTooltip, isNotNull);
+      expect(localizations.licensesPageTitle, isNotNull);
+      expect(localizations.rowsPerPageTitle, isNotNull);
+      expect(localizations.cancelButtonLabel, isNotNull);
+      expect(localizations.closeButtonLabel, isNotNull);
+      expect(localizations.continueButtonLabel, isNotNull);
+      expect(localizations.copyButtonLabel, isNotNull);
+      expect(localizations.cutButtonLabel, isNotNull);
+      expect(localizations.okButtonLabel, isNotNull);
+      expect(localizations.pasteButtonLabel, isNotNull);
+      expect(localizations.selectAllButtonLabel, isNotNull);
+      expect(localizations.viewLicensesButtonLabel, isNotNull);
+
+      expect(localizations.aboutListTileTitle('FOO'), isNotNull);
+      expect(localizations.aboutListTileTitle('FOO'), contains('FOO'));
+
+      expect(localizations.selectedRowCountTitle(0), isNotNull);
+      expect(localizations.selectedRowCountTitle(1), isNotNull);
+      expect(localizations.selectedRowCountTitle(2), isNotNull);
+      expect(localizations.selectedRowCountTitle(100), isNotNull);
+      expect(localizations.selectedRowCountTitle(0).contains(r'$selectedRowCount'), isFalse);
+      expect(localizations.selectedRowCountTitle(1).contains(r'$selectedRowCount'), isFalse);
+      expect(localizations.selectedRowCountTitle(2).contains(r'$selectedRowCount'), isFalse);
+      expect(localizations.selectedRowCountTitle(100).contains(r'$selectedRowCount'), isFalse);
+
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNotNull);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNotNull);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$firstRow'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$lastRow'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$rowCount'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$firstRow'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$lastRow'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$rowCount'), isFalse);
+    });
+  }
+
+  testWidgets('spot check selectedRowCount translations', (WidgetTester tester) async {
+    MaterialLocalizations localizations = new GlobalMaterialLocalizations(const Locale('en', ''));
+    expect(localizations.selectedRowCountTitle(0), 'No items selected');
+    expect(localizations.selectedRowCountTitle(1), '1 item selected');
+    expect(localizations.selectedRowCountTitle(2), '2 items selected');
+    expect(localizations.selectedRowCountTitle(123456789), '123,456,789 items selected');
+
+    localizations = new GlobalMaterialLocalizations(const Locale('es', ''));
+    expect(localizations.selectedRowCountTitle(0), 'No se han seleccionado elementos');
+    expect(localizations.selectedRowCountTitle(1), '1 artículo seleccionado');
+    expect(localizations.selectedRowCountTitle(2), '2 artículos seleccionados');
+    expect(localizations.selectedRowCountTitle(123456789), '123.456.789 artículos seleccionados');
+  });
+}
diff --git a/packages/flutter/test/widgets/localizations_test.dart b/packages/flutter_localizations/test/widgets_test.dart
similarity index 96%
rename from packages/flutter/test/widgets/localizations_test.dart
rename to packages/flutter_localizations/test/widgets_test.dart
index 489947a..e5ca628 100644
--- a/packages/flutter/test/widgets/localizations_test.dart
+++ b/packages/flutter_localizations/test/widgets_test.dart
@@ -5,6 +5,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
 
 class TestLocalizations {
   TestLocalizations(this.locale, this.prefix);
@@ -103,20 +104,7 @@
   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;
 }
@@ -126,7 +114,7 @@
 
   @override
   Future<WidgetsLocalizations> load(Locale locale) {
-    return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations(locale));
+    return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations());
   }
 
   @override
@@ -224,7 +212,7 @@
   testWidgets('Synchronously loaded localizations in a WidgetsApp', (WidgetTester tester) async {
     final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
       new SyncTestLocalizationsDelegate(),
-      const DefaultWidgetsLocalizationsDelegate(),
+      DefaultWidgetsLocalizations.delegate,
     ];
 
     Future<Null> pumpTest(Locale locale) async {
@@ -349,7 +337,7 @@
                 locale: const Locale('en', 'GB'),
                 delegates: <LocalizationsDelegate<dynamic>>[
                   new SyncTestLocalizationsDelegate(),
-                  const DefaultWidgetsLocalizationsDelegate(),
+                  DefaultWidgetsLocalizations.delegate,
                 ],
                 // Create a new context within the en_GB Localization
                 child: new Builder(
@@ -476,6 +464,9 @@
 
     await tester.pumpWidget(
       buildFrame(
+        delegates: const <LocalizationsDelegate<dynamic>>[
+          GlobalWidgetsLocalizations.delegate,
+        ],
         supportedLocales: const <Locale>[
           const Locale('en', 'GB'),
           const Locale('ar', 'EG'),
@@ -557,6 +548,9 @@
       buildFrame(
         // Accept whatever locale we're given
         localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
+        delegates: const <LocalizationsDelegate<dynamic>>[
+          GlobalWidgetsLocalizations.delegate,
+        ],
         buildContent: (BuildContext context) {
           return new Localizations.override(
             context: context,
diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml
index 1c7bbb9..45e6f89 100644
--- a/packages/flutter_test/pubspec.yaml
+++ b/packages/flutter_test/pubspec.yaml
@@ -27,7 +27,7 @@
 
   args: 0.13.7 # TRANSITIVE DEPENDENCY
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 3f14f1b..f6c7329 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -48,7 +48,7 @@
   mockito: 2.2.0
 
   async: 1.13.3 # TRANSITIVE DEPENDENCY
-  barback: 0.15.2+12 # TRANSITIVE DEPENDENCY
+  barback: 0.15.2+13 # TRANSITIVE DEPENDENCY
   boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
   charcode: 1.1.1 # TRANSITIVE DEPENDENCY
   convert: 2.0.1 # TRANSITIVE DEPENDENCY