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