Commit c3d56b1d authored by Hans Muller's avatar Hans Muller Committed by GitHub

flutter_localization optional package (#12410)

parent f07170b4
...@@ -7,7 +7,7 @@ dependencies: ...@@ -7,7 +7,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -19,7 +19,7 @@ dev_dependencies: ...@@ -19,7 +19,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -11,7 +11,7 @@ dependencies: ...@@ -11,7 +11,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -6,7 +6,7 @@ dependencies: ...@@ -6,7 +6,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -57,13 +57,13 @@ Future<Null> _verifyInternationalizations() async { ...@@ -57,13 +57,13 @@ Future<Null> _verifyInternationalizations() async {
dart, dart,
<String>[ <String>[
path.join('dev', 'tools', 'gen_localizations.dart'), path.join('dev', 'tools', 'gen_localizations.dart'),
path.join('packages', 'flutter', 'lib', 'src', 'material', 'i18n'), path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n'),
'material' 'material'
], ],
workingDirectory: flutterRoot, 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( final EvalResult sourceContents = await _evalCommand(
'cat', 'cat',
...@@ -156,6 +156,7 @@ Future<Null> _runTests() async { ...@@ -156,6 +156,7 @@ Future<Null> _runTests() async {
// Run tests. // Run tests.
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter')); 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_driver'));
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test')); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'));
await _pubRunTest(path.join(flutterRoot, 'packages', 'flutter_tools')); await _pubRunTest(path.join(flutterRoot, 'packages', 'flutter_tools'));
......
...@@ -24,7 +24,7 @@ dev_dependencies: ...@@ -24,7 +24,7 @@ dev_dependencies:
archive: 1.0.31 # TRANSITIVE DEPENDENCY archive: 1.0.31 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
browser: 0.10.0+2 # TRANSITIVE DEPENDENCY browser: 0.10.0+2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
......
...@@ -9,7 +9,7 @@ dependencies: ...@@ -9,7 +9,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -9,7 +9,7 @@ dependencies: ...@@ -9,7 +9,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -9,7 +9,7 @@ dependencies: ...@@ -9,7 +9,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -14,7 +14,7 @@ dev_dependencies: ...@@ -14,7 +14,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
......
...@@ -10,7 +10,7 @@ dev_dependencies: ...@@ -10,7 +10,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -10,7 +10,7 @@ dependencies: ...@@ -10,7 +10,7 @@ dependencies:
path: 1.4.2 path: 1.4.2
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -13,7 +13,7 @@ dev_dependencies: ...@@ -13,7 +13,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -21,7 +21,7 @@ dev_dependencies: ...@@ -21,7 +21,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
convert: 2.0.1 # TRANSITIVE DEPENDENCY convert: 2.0.1 # TRANSITIVE DEPENDENCY
......
...@@ -7,7 +7,7 @@ dependencies: ...@@ -7,7 +7,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -10,7 +10,7 @@ dev_dependencies: ...@@ -10,7 +10,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -9,7 +9,7 @@ dev_dependencies: ...@@ -9,7 +9,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -12,7 +12,7 @@ dev_dependencies: ...@@ -12,7 +12,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -12,7 +12,7 @@ dev_dependencies: ...@@ -12,7 +12,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -6,7 +6,7 @@ dependencies: ...@@ -6,7 +6,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart' show ...@@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart' show
debugPaintLayerBordersEnabled, debugPaintLayerBordersEnabled,
debugPaintPointersEnabled, debugPaintPointersEnabled,
debugRepaintRainbowEnabled; debugRepaintRainbowEnabled;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'stock_data.dart'; import 'stock_data.dart';
import 'stock_home.dart'; import 'stock_home.dart';
...@@ -118,8 +119,10 @@ class StocksAppState extends State<StocksApp> { ...@@ -118,8 +119,10 @@ class StocksAppState extends State<StocksApp> {
return new MaterialApp( return new MaterialApp(
title: 'Stocks', title: 'Stocks',
theme: theme, theme: theme,
localizationsDelegates: <_StocksLocalizationsDelegate>[ localizationsDelegates: <LocalizationsDelegate<dynamic>>[
new _StocksLocalizationsDelegate(), new _StocksLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
], ],
supportedLocales: const <Locale>[ supportedLocales: const <Locale>[
const Locale('en', 'US'), const Locale('en', 'US'),
......
...@@ -2,6 +2,8 @@ name: stocks ...@@ -2,6 +2,8 @@ name: stocks
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations:
sdk: flutter
intl: 0.15.1 intl: 0.15.1
intl_translation: 0.15.0 intl_translation: 0.15.0
http: 0.11.3+14 http: 0.11.3+14
...@@ -15,7 +17,7 @@ dev_dependencies: ...@@ -15,7 +17,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
collection: 1.14.3 # TRANSITIVE DEPENDENCY collection: 1.14.3 # TRANSITIVE DEPENDENCY
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -26,17 +24,6 @@ const TextStyle _errorTextStyle = const TextStyle( ...@@ -26,17 +24,6 @@ const TextStyle _errorTextStyle = const TextStyle(
decorationStyle: TextDecorationStyle.double 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. /// An application that uses material design.
/// ///
/// A convenience widget that wraps a number of widgets that are commonly /// A convenience widget that wraps a number of widgets that are commonly
...@@ -463,7 +450,7 @@ class _MaterialAppState extends State<MaterialApp> { ...@@ -463,7 +450,7 @@ class _MaterialAppState extends State<MaterialApp> {
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
if (widget.localizationsDelegates != null) if (widget.localizationsDelegates != null)
yield* widget.localizationsDelegates; yield* widget.localizationsDelegates;
yield const _MaterialLocalizationsDelegate(); yield DefaultMaterialLocalizations.delegate;
} }
RectTween _createRectTween(Rect begin, Rect end) { RectTween _createRectTween(Rect begin, Rect end) {
......
...@@ -295,8 +295,8 @@ class DayPicker extends StatelessWidget { ...@@ -295,8 +295,8 @@ class DayPicker extends StatelessWidget {
List<Widget> _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) { List<Widget> _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) {
final List<Widget> result = <Widget>[]; final List<Widget> result = <Widget>[];
for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) {
final String weekDay = localizations.narrowWeekDays[i]; final String weekday = localizations.narrowWeekdays[i];
result.add(new Center(child: new Text(weekDay, style: headerStyle))); result.add(new Center(child: new Text(weekday, style: headerStyle)));
if (i == (localizations.firstDayOfWeekIndex - 1) % 7) if (i == (localizations.firstDayOfWeekIndex - 1) % 7)
break; break;
} }
...@@ -350,19 +350,19 @@ class DayPicker extends StatelessWidget { ...@@ -350,19 +350,19 @@ class DayPicker extends StatelessWidget {
/// - [DateTime.weekday] provides a 1-based index into days of week, with 1 /// - [DateTime.weekday] provides a 1-based index into days of week, with 1
/// falling on Monday. /// falling on Monday.
/// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index /// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index
/// into the [MaterialLocalizations.narrowWeekDays] list. /// into the [MaterialLocalizations.narrowWeekdays] list.
/// - [MaterialLocalizations.narrowWeekDays] list provides localized names of /// - [MaterialLocalizations.narrowWeekdays] list provides localized names of
/// days of week, always starting with Sunday and ending with Saturday. /// days of week, always starting with Sunday and ending with Saturday.
int _computeFirstDayOffset(int year, int month, MaterialLocalizations localizations) { int _computeFirstDayOffset(int year, int month, MaterialLocalizations localizations) {
// 0-based day of week, with 0 representing Monday. // 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. // 0-based day of week, with 0 representing Sunday.
final int firstDayOfWeekFromSunday = localizations.firstDayOfWeekIndex; final int firstDayOfWeekFromSunday = localizations.firstDayOfWeekIndex;
// firstDayOfWeekFromSunday recomputed to be Monday-based // firstDayOfWeekFromSunday recomputed to be Monday-based
final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7;
// Number of days between the first day of week appearing on the calendar, // Number of days between the first day of week appearing on the calendar,
// and the day corresponding to the 1-st of the month. // and the day corresponding to the 1-st of the month.
return (weekDayFromMonday - firstDayOfWeekFromMonday) % 7; return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7;
} }
@override @override
......
...@@ -44,17 +44,6 @@ typedef Locale LocaleResolutionCallback(Locale locale, Iterable<Locale> supporte ...@@ -44,17 +44,6 @@ typedef Locale LocaleResolutionCallback(Locale locale, Iterable<Locale> supporte
/// This function must not return null. /// This function must not return null.
typedef String GenerateAppTitle(BuildContext context); 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 /// A convenience class that wraps a number of widgets that are commonly
/// required for an application. /// required for an application.
/// ///
...@@ -423,11 +412,11 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -423,11 +412,11 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
// by the localizationsDelegates parameter, if any. Only the first delegate // by the localizationsDelegates parameter, if any. Only the first delegate
// of a particular LocalizationsDelegate.type is loaded so the // of a particular LocalizationsDelegate.type is loaded so the
// localizationsDelegate parameter can be used to override // localizationsDelegate parameter can be used to override
// _WidgetsLocalizationsDelegate. // WidgetsLocalizations.delegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
if (widget.localizationsDelegates != null) if (widget.localizationsDelegates != null)
yield* widget.localizationsDelegates; yield* widget.localizationsDelegates;
yield const _WidgetsLocalizationsDelegate(); yield DefaultWidgetsLocalizations.delegate;
} }
@override @override
......
...@@ -161,44 +161,50 @@ abstract class WidgetsLocalizations { ...@@ -161,44 +161,50 @@ abstract class WidgetsLocalizations {
} }
} }
/// Localized values for widgets. class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const _WidgetsLocalizationsDelegate();
@override
Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
@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 { class DefaultWidgetsLocalizations implements WidgetsLocalizations {
/// Construct an object that defines the localized values for the widgets /// Construct an object that defines the localized values for the widgets
/// library for the given `locale`. /// library for US English (only).
/// ///
/// [LocalizationsDelegate] implementations typically call the static [load] /// [LocalizationsDelegate] implementations typically call the static [load]
/// function, rather than constructing this class directly. const DefaultWidgetsLocalizations();
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;
@override @override
TextDirection get textDirection => _textDirection; TextDirection get textDirection => TextDirection.ltr;
TextDirection _textDirection;
/// Creates an object that provides localized resource values for the /// Creates an object that provides US English resource values for the
/// lowest levels of the Flutter framework. /// lowest levels of the widgets library.
///
/// The [locale] parameter is ignored.
/// ///
/// This method is typically used to create a [LocalizationsDelegate]. /// This method is typically used to create a [LocalizationsDelegate].
/// The [WidgetsApp] does so by default. /// The [WidgetsApp] does so by default.
static Future<WidgetsLocalizations> load(Locale locale) { 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 { class _LocalizationsScope extends InheritedWidget {
......
...@@ -8,7 +8,6 @@ dependencies: ...@@ -8,7 +8,6 @@ dependencies:
# To update these, use "flutter update-packages --force-upgrade". # To update these, use "flutter update-packages --force-upgrade".
collection: 1.14.3 collection: 1.14.3
http: 0.11.3+14 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 meta: 1.1.1
typed_data: 1.1.4 typed_data: 1.1.4
vector_math: 2.0.5 vector_math: 2.0.5
...@@ -23,7 +22,7 @@ dev_dependencies: ...@@ -23,7 +22,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
...@@ -35,6 +34,7 @@ dev_dependencies: ...@@ -35,6 +34,7 @@ dev_dependencies:
html: 0.13.2 # TRANSITIVE DEPENDENCY html: 0.13.2 # TRANSITIVE DEPENDENCY
http_multi_server: 2.0.4 # TRANSITIVE DEPENDENCY http_multi_server: 2.0.4 # TRANSITIVE DEPENDENCY
http_parser: 3.1.1 # TRANSITIVE DEPENDENCY http_parser: 3.1.1 # TRANSITIVE DEPENDENCY
intl: 0.15.1 # TRANSITIVE DEPENDENCY
intl_translation: 0.15.0 # TRANSITIVE DEPENDENCY intl_translation: 0.15.0 # TRANSITIVE DEPENDENCY
isolate: 1.1.0 # TRANSITIVE DEPENDENCY isolate: 1.1.0 # TRANSITIVE DEPENDENCY
js: 0.6.1 # TRANSITIVE DEPENDENCY js: 0.6.1 # TRANSITIVE DEPENDENCY
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'feedback_tester.dart'; import 'feedback_tester.dart';
...@@ -419,250 +417,4 @@ void main() { ...@@ -419,250 +417,4 @@ void main() {
expect(await date, isNull); 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;
} }
...@@ -205,78 +205,4 @@ void main() { ...@@ -205,78 +205,4 @@ void main() {
expect(feedback.hapticCount, 3); 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)));
}
}
});
});
} }
...@@ -27,7 +27,7 @@ dev_dependencies: ...@@ -27,7 +27,7 @@ dev_dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
......
// 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;
# Material Library Internationalization # Material Library Localizations
The `.arb` files in this directory contain localized values (primarily The `.arb` files in this directory contain localized values (primarily
strings) used by the material library. The `localizations.dart` file strings) used by the material library. The `localizations.dart` file
combines all of the localizations into a single Dart Map that is combines all of the localizations into a single Map that is
linked with the rest of the material library. linked with the rest of flutter_localizations package.
If you're looking for information about internationalizing Flutter 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. [Internationalizing Flutter Apps](https://flutter.io/tutorials/internationalization/) tutorial.
...@@ -54,7 +54,7 @@ translation of "CANCEL" which is defined for the `cancelButtonLabel` ...@@ -54,7 +54,7 @@ translation of "CANCEL" which is defined for the `cancelButtonLabel`
resource ID. resource ID.
Each of the language-specific .arb files contains an entry for 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 generated `localizations.dart` file. The Map is used by the
MaterialLocalizations class. MaterialLocalizations class.
...@@ -123,18 +123,18 @@ the "Other" suffix. For example the English translations ...@@ -123,18 +123,18 @@ the "Other" suffix. For example the English translations
``` ```
### 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 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 see that it was manually generated using a `dev/tools` app called
`gen_localizations` roughly like this: `gen_localizations` roughly like this:
```dart ```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 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 file's locale. The `MaterialLocalizations` class implementation uses
this Map to implement the methods that lookup localized resource this Map to implement the methods that lookup localized resource
values. values.
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// This file has been automatically generated. Please do not edit it manually. // This file has been automatically generated. Please do not edit it manually.
// To regenerate the file, use: // 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 /// Maps from [Locale.languageCode] to a map that contains the localized strings
/// for that locale. /// for that locale.
...@@ -452,4 +452,3 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String ...@@ -452,4 +452,3 @@ const Map<String, Map<String, String>> localizations = const <String, Map<String
"postMeridiemAbbreviation": r"下午", "postMeridiemAbbreviation": r"下午",
}, },
}; };
// 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;
}
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
// 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();
}
}
// 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,
),
));
}
// 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');
});
}
// 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)));
}
}
});
}
// 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');
});
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class TestLocalizations { class TestLocalizations {
TestLocalizations(this.locale, this.prefix); TestLocalizations(this.locale, this.prefix);
...@@ -103,20 +104,7 @@ class AsyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizat ...@@ -103,20 +104,7 @@ class AsyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizat
bool shouldReload(AsyncMoreLocalizationsDelegate old) => false; 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 { class OnlyRTLDefaultWidgetsLocalizations extends DefaultWidgetsLocalizations {
OnlyRTLDefaultWidgetsLocalizations(Locale locale) : super(locale);
@override @override
TextDirection get textDirection => TextDirection.rtl; TextDirection get textDirection => TextDirection.rtl;
} }
...@@ -126,7 +114,7 @@ class OnlyRTLDefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<W ...@@ -126,7 +114,7 @@ class OnlyRTLDefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<W
@override @override
Future<WidgetsLocalizations> load(Locale locale) { Future<WidgetsLocalizations> load(Locale locale) {
return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations(locale)); return new SynchronousFuture<WidgetsLocalizations>(new OnlyRTLDefaultWidgetsLocalizations());
} }
@override @override
...@@ -224,7 +212,7 @@ void main() { ...@@ -224,7 +212,7 @@ void main() {
testWidgets('Synchronously loaded localizations in a WidgetsApp', (WidgetTester tester) async { testWidgets('Synchronously loaded localizations in a WidgetsApp', (WidgetTester tester) async {
final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[ final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
new SyncTestLocalizationsDelegate(), new SyncTestLocalizationsDelegate(),
const DefaultWidgetsLocalizationsDelegate(), DefaultWidgetsLocalizations.delegate,
]; ];
Future<Null> pumpTest(Locale locale) async { Future<Null> pumpTest(Locale locale) async {
...@@ -349,7 +337,7 @@ void main() { ...@@ -349,7 +337,7 @@ void main() {
locale: const Locale('en', 'GB'), locale: const Locale('en', 'GB'),
delegates: <LocalizationsDelegate<dynamic>>[ delegates: <LocalizationsDelegate<dynamic>>[
new SyncTestLocalizationsDelegate(), new SyncTestLocalizationsDelegate(),
const DefaultWidgetsLocalizationsDelegate(), DefaultWidgetsLocalizations.delegate,
], ],
// Create a new context within the en_GB Localization // Create a new context within the en_GB Localization
child: new Builder( child: new Builder(
...@@ -476,6 +464,9 @@ void main() { ...@@ -476,6 +464,9 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
buildFrame( buildFrame(
delegates: const <LocalizationsDelegate<dynamic>>[
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const <Locale>[ supportedLocales: const <Locale>[
const Locale('en', 'GB'), const Locale('en', 'GB'),
const Locale('ar', 'EG'), const Locale('ar', 'EG'),
...@@ -557,6 +548,9 @@ void main() { ...@@ -557,6 +548,9 @@ void main() {
buildFrame( buildFrame(
// Accept whatever locale we're given // Accept whatever locale we're given
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale, localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) => locale,
delegates: const <LocalizationsDelegate<dynamic>>[
GlobalWidgetsLocalizations.delegate,
],
buildContent: (BuildContext context) { buildContent: (BuildContext context) {
return new Localizations.override( return new Localizations.override(
context: context, context: context,
......
...@@ -27,7 +27,7 @@ dependencies: ...@@ -27,7 +27,7 @@ dependencies:
args: 0.13.7 # TRANSITIVE DEPENDENCY args: 0.13.7 # TRANSITIVE DEPENDENCY
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY cli_util: 0.1.2+1 # TRANSITIVE DEPENDENCY
......
...@@ -48,7 +48,7 @@ dev_dependencies: ...@@ -48,7 +48,7 @@ dev_dependencies:
mockito: 2.2.0 mockito: 2.2.0
async: 1.13.3 # 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 boolean_selector: 1.0.2 # TRANSITIVE DEPENDENCY
charcode: 1.1.1 # TRANSITIVE DEPENDENCY charcode: 1.1.1 # TRANSITIVE DEPENDENCY
convert: 2.0.1 # TRANSITIVE DEPENDENCY convert: 2.0.1 # TRANSITIVE DEPENDENCY
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment