• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6
7import 'package:flutter/foundation.dart';
8import 'package:flutter/material.dart';
9import 'package:intl/intl.dart' as intl;
10import 'package:intl/date_symbols.dart' as intl;
11
12import 'cupertino_localizations.dart';
13import 'l10n/generated_material_localizations.dart';
14import 'utils/date_localizations.dart' as util;
15import 'widgets_localizations.dart';
16
17/// Implementation of localized strings for the material widgets using the
18/// `intl` package for date and time formatting.
19///
20/// ## Supported languages
21///
22/// This class supports locales with the following [Locale.languageCode]s:
23///
24/// {@macro flutter.localizations.material.languages}
25///
26/// This list is available programatically via [kMaterialSupportedLanguages].
27///
28/// ## Sample code
29///
30/// To include the localizations provided by this class in a [MaterialApp],
31/// add [GlobalMaterialLocalizations.delegates] to
32/// [MaterialApp.localizationsDelegates], and specify the locales your
33/// app supports with [MaterialApp.supportedLocales]:
34///
35/// ```dart
36/// new MaterialApp(
37///   localizationsDelegates: GlobalMaterialLocalizations.delegates,
38///   supportedLocales: [
39///     const Locale('en', 'US'), // American English
40///     const Locale('he', 'IL'), // Israeli Hebrew
41///     // ...
42///   ],
43///   // ...
44/// )
45/// ```
46///
47/// ## Overriding translations
48///
49/// To create a translation that's similar to an existing language's translation
50/// but has slightly different strings, subclass the relevant translation
51/// directly and then create a [LocalizationsDelegate<MaterialLocalizations>]
52/// subclass to define how to load it.
53///
54/// Avoid subclassing an unrelated language (for example, subclassing
55/// [MaterialLocalizationEn] and then passing a non-English `localeName` to the
56/// constructor). Doing so will cause confusion for locale-specific behaviors;
57/// in particular, translations that use the `localeName` for determining how to
58/// pluralize will end up doing invalid things. Subclassing an existing
59/// language's translations is only suitable for making small changes to the
60/// existing strings. For providing a new language entirely, implement
61/// [MaterialLocalizations] directly.
62///
63/// See also:
64///
65///  * The Flutter Internationalization Tutorial,
66///    <https://flutter.dev/tutorials/internationalization/>.
67///  * [DefaultMaterialLocalizations], which only provides US English translations.
68abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
69  /// Initializes an object that defines the material widgets' localized strings
70  /// for the given `locale`.
71  ///
72  /// The arguments are used for further runtime localization of data,
73  /// specifically for selecting plurals, date and time formatting, and number
74  /// formatting. They correspond to the following values:
75  ///
76  ///  1. The string that would be returned by [Intl.canonicalizedLocale] for
77  ///     the locale.
78  ///  2. The [intl.DateFormat] for [formatYear].
79  ///  3. The [intl.DateFormat] for [formatMediumDate].
80  ///  4. The [intl.DateFormat] for [formatFullDate].
81  ///  5. The [intl.DateFormat] for [formatMonthYear].
82  ///  6. The [NumberFormat] for [formatDecimal] (also used by [formatHour] and
83  ///     [formatTimeOfDay] when [timeOfDayFormat] doesn't use [HourFormat.HH]).
84  ///  7. The [NumberFormat] for [formatHour] and the hour part of
85  ///     [formatTimeOfDay] when [timeOfDayFormat] uses [HourFormat.HH], and for
86  ///     [formatMinute] and the minute part of [formatTimeOfDay].
87  ///
88  /// The [narrowWeekdays] and [firstDayOfWeekIndex] properties use the values
89  /// from the [intl.DateFormat] used by [formatFullDate].
90  const GlobalMaterialLocalizations({
91    @required String localeName,
92    @required intl.DateFormat fullYearFormat,
93    @required intl.DateFormat mediumDateFormat,
94    @required intl.DateFormat longDateFormat,
95    @required intl.DateFormat yearMonthFormat,
96    @required intl.NumberFormat decimalFormat,
97    @required intl.NumberFormat twoDigitZeroPaddedFormat,
98  }) : assert(localeName != null),
99       _localeName = localeName,
100       assert(fullYearFormat != null),
101       _fullYearFormat = fullYearFormat,
102       assert(mediumDateFormat != null),
103       _mediumDateFormat = mediumDateFormat,
104       assert(longDateFormat != null),
105       _longDateFormat = longDateFormat,
106       assert(yearMonthFormat != null),
107       _yearMonthFormat = yearMonthFormat,
108       assert(decimalFormat != null),
109       _decimalFormat = decimalFormat,
110       assert(twoDigitZeroPaddedFormat != null),
111       _twoDigitZeroPaddedFormat = twoDigitZeroPaddedFormat;
112
113  final String _localeName;
114  final intl.DateFormat _fullYearFormat;
115  final intl.DateFormat _mediumDateFormat;
116  final intl.DateFormat _longDateFormat;
117  final intl.DateFormat _yearMonthFormat;
118  final intl.NumberFormat _decimalFormat;
119  final intl.NumberFormat _twoDigitZeroPaddedFormat;
120
121  @override
122  String formatHour(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) {
123    switch (hourFormat(of: timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat))) {
124      case HourFormat.HH:
125        return _twoDigitZeroPaddedFormat.format(timeOfDay.hour);
126      case HourFormat.H:
127        return formatDecimal(timeOfDay.hour);
128      case HourFormat.h:
129        final int hour = timeOfDay.hourOfPeriod;
130        return formatDecimal(hour == 0 ? 12 : hour);
131    }
132    return null;
133  }
134
135  @override
136  String formatMinute(TimeOfDay timeOfDay) {
137    return _twoDigitZeroPaddedFormat.format(timeOfDay.minute);
138  }
139
140  @override
141  String formatYear(DateTime date) {
142    return _fullYearFormat.format(date);
143  }
144
145  @override
146  String formatMediumDate(DateTime date) {
147    return _mediumDateFormat.format(date);
148  }
149
150  @override
151  String formatFullDate(DateTime date) {
152    return _longDateFormat.format(date);
153  }
154
155  @override
156  String formatMonthYear(DateTime date) {
157    return _yearMonthFormat.format(date);
158  }
159
160  @override
161  List<String> get narrowWeekdays {
162    return _longDateFormat.dateSymbols.NARROWWEEKDAYS;
163  }
164
165  @override
166  int get firstDayOfWeekIndex => (_longDateFormat.dateSymbols.FIRSTDAYOFWEEK + 1) % 7;
167
168  @override
169  String formatDecimal(int number) {
170    return _decimalFormat.format(number);
171  }
172
173  @override
174  String formatTimeOfDay(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) {
175    // Not using intl.DateFormat for two reasons:
176    //
177    // - DateFormat supports more formats than our material time picker does,
178    //   and we want to be consistent across time picker format and the string
179    //   formatting of the time of day.
180    // - DateFormat operates on DateTime, which is sensitive to time eras and
181    //   time zones, while here we want to format hour and minute within one day
182    //   no matter what date the day falls on.
183    final String hour = formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat);
184    final String minute = formatMinute(timeOfDay);
185    switch (timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat)) {
186      case TimeOfDayFormat.h_colon_mm_space_a:
187        return '$hour:$minute ${_formatDayPeriod(timeOfDay)}';
188      case TimeOfDayFormat.H_colon_mm:
189      case TimeOfDayFormat.HH_colon_mm:
190        return '$hour:$minute';
191      case TimeOfDayFormat.HH_dot_mm:
192        return '$hour.$minute';
193      case TimeOfDayFormat.a_space_h_colon_mm:
194        return '${_formatDayPeriod(timeOfDay)} $hour:$minute';
195      case TimeOfDayFormat.frenchCanadian:
196        return '$hour h $minute';
197    }
198    return null;
199  }
200
201  String _formatDayPeriod(TimeOfDay timeOfDay) {
202    switch (timeOfDay.period) {
203      case DayPeriod.am:
204        return anteMeridiemAbbreviation;
205      case DayPeriod.pm:
206        return postMeridiemAbbreviation;
207    }
208    return null;
209  }
210
211  /// The raw version of [aboutListTileTitle], with `$applicationName` verbatim
212  /// in the string.
213  @protected
214  String get aboutListTileTitleRaw;
215
216  @override
217  String aboutListTileTitle(String applicationName) {
218    final String text = aboutListTileTitleRaw;
219    return text.replaceFirst(r'$applicationName', applicationName);
220  }
221
222  /// The raw version of [pageRowsInfoTitle], with `$firstRow`, `$lastRow`' and
223  /// `$rowCount` verbatim in the string, for the case where the value is
224  /// approximate.
225  @protected
226  String get pageRowsInfoTitleApproximateRaw;
227
228  /// The raw version of [pageRowsInfoTitle], with `$firstRow`, `$lastRow`' and
229  /// `$rowCount` verbatim in the string, for the case where the value is
230  /// precise.
231  @protected
232  String get pageRowsInfoTitleRaw;
233
234  @override
235  String pageRowsInfoTitle(int firstRow, int lastRow, int rowCount, bool rowCountIsApproximate) {
236    String text = rowCountIsApproximate ? pageRowsInfoTitleApproximateRaw : null;
237    text ??= pageRowsInfoTitleRaw;
238    assert(text != null, 'A $_localeName localization was not found for pageRowsInfoTitle or pageRowsInfoTitleApproximate');
239    return text
240      .replaceFirst(r'$firstRow', formatDecimal(firstRow))
241      .replaceFirst(r'$lastRow', formatDecimal(lastRow))
242      .replaceFirst(r'$rowCount', formatDecimal(rowCount));
243  }
244
245  /// The raw version of [tabLabel], with `$tabIndex` and `$tabCount` verbatim
246  /// in the string.
247  @protected
248  String get tabLabelRaw;
249
250  @override
251  String tabLabel({ int tabIndex, int tabCount }) {
252    assert(tabIndex >= 1);
253    assert(tabCount >= 1);
254    final String template = tabLabelRaw;
255    return template
256      .replaceFirst(r'$tabIndex', formatDecimal(tabIndex))
257      .replaceFirst(r'$tabCount', formatDecimal(tabCount));
258  }
259
260  /// The "zero" form of [selectedRowCountTitle].
261  ///
262  /// This form is optional.
263  ///
264  /// See also:
265  ///
266  ///  * [Intl.plural], to which this form is passed.
267  ///  * [selectedRowCountTitleOne], the "one" form
268  ///  * [selectedRowCountTitleTwo], the "two" form
269  ///  * [selectedRowCountTitleFew], the "few" form
270  ///  * [selectedRowCountTitleMany], the "many" form
271  ///  * [selectedRowCountTitleOther], the "other" form
272  @protected
273  String get selectedRowCountTitleZero => null;
274
275  /// The "one" form of [selectedRowCountTitle].
276  ///
277  /// This form is optional.
278  ///
279  /// See also:
280  ///
281  ///  * [Intl.plural], to which this form is passed.
282  ///  * [selectedRowCountTitleZero], the "zero" form
283  ///  * [selectedRowCountTitleTwo], the "two" form
284  ///  * [selectedRowCountTitleFew], the "few" form
285  ///  * [selectedRowCountTitleMany], the "many" form
286  ///  * [selectedRowCountTitleOther], the "other" form
287  @protected
288  String get selectedRowCountTitleOne => null;
289
290  /// The "two" form of [selectedRowCountTitle].
291  ///
292  /// This form is optional.
293  ///
294  /// See also:
295  ///
296  ///  * [Intl.plural], to which this form is passed.
297  ///  * [selectedRowCountTitleZero], the "zero" form
298  ///  * [selectedRowCountTitleOne], the "one" form
299  ///  * [selectedRowCountTitleFew], the "few" form
300  ///  * [selectedRowCountTitleMany], the "many" form
301  ///  * [selectedRowCountTitleOther], the "other" form
302  @protected
303  String get selectedRowCountTitleTwo => null;
304
305  /// The "few" form of [selectedRowCountTitle].
306  ///
307  /// This form is optional.
308  ///
309  /// See also:
310  ///
311  ///  * [Intl.plural], to which this form is passed.
312  ///  * [selectedRowCountTitleZero], the "zero" form
313  ///  * [selectedRowCountTitleOne], the "one" form
314  ///  * [selectedRowCountTitleTwo], the "two" form
315  ///  * [selectedRowCountTitleMany], the "many" form
316  ///  * [selectedRowCountTitleOther], the "other" form
317  @protected
318  String get selectedRowCountTitleFew => null;
319
320  /// The "many" form of [selectedRowCountTitle].
321  ///
322  /// This form is optional.
323  ///
324  /// See also:
325  ///
326  ///  * [Intl.plural], to which this form is passed.
327  ///  * [selectedRowCountTitleZero], the "zero" form
328  ///  * [selectedRowCountTitleOne], the "one" form
329  ///  * [selectedRowCountTitleTwo], the "two" form
330  ///  * [selectedRowCountTitleFew], the "few" form
331  ///  * [selectedRowCountTitleOther], the "other" form
332  @protected
333  String get selectedRowCountTitleMany => null;
334
335  /// The "other" form of [selectedRowCountTitle].
336  ///
337  /// This form is required.
338  ///
339  /// See also:
340  ///
341  ///  * [Intl.plural], to which this form is passed.
342  ///  * [selectedRowCountTitleZero], the "zero" form
343  ///  * [selectedRowCountTitleOne], the "one" form
344  ///  * [selectedRowCountTitleTwo], the "two" form
345  ///  * [selectedRowCountTitleFew], the "few" form
346  ///  * [selectedRowCountTitleMany], the "many" form
347  @protected
348  String get selectedRowCountTitleOther;
349
350  @override
351  String selectedRowCountTitle(int selectedRowCount) {
352    return intl.Intl.pluralLogic(
353      selectedRowCount,
354      zero: selectedRowCountTitleZero,
355      one: selectedRowCountTitleOne,
356      two: selectedRowCountTitleTwo,
357      few: selectedRowCountTitleFew,
358      many: selectedRowCountTitleMany,
359      other: selectedRowCountTitleOther,
360      locale: _localeName,
361    ).replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount));
362  }
363
364  /// The format to use for [timeOfDayFormat].
365  @protected
366  TimeOfDayFormat get timeOfDayFormatRaw;
367
368  /// The [TimeOfDayFormat] corresponding to one of the following supported
369  /// patterns:
370  ///
371  ///  * `HH:mm`
372  ///  * `HH.mm`
373  ///  * `HH 'h' mm`
374  ///  * `HH:mm น.`
375  ///  * `H:mm`
376  ///  * `h:mm a`
377  ///  * `a h:mm`
378  ///  * `ah:mm`
379  ///
380  /// See also:
381  ///
382  ///  * <http://demo.icu-project.org/icu-bin/locexp?d_=en&_=en_US>, which shows
383  ///    the short time pattern used in the `en_US` locale.
384  @override
385  TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }) {
386    assert(alwaysUse24HourFormat != null);
387    if (alwaysUse24HourFormat)
388      return _get24HourVersionOf(timeOfDayFormatRaw);
389    return timeOfDayFormatRaw;
390  }
391
392  /// The "zero" form of [remainingTextFieldCharacterCount].
393  ///
394  /// This form is required.
395  ///
396  /// See also:
397  ///
398  ///  * [Intl.plural], to which this form is passed.
399  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
400  ///  * [remainingTextFieldCharacterCountOne], the "one" form
401  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
402  ///  * [remainingTextFieldCharacterCountFew], the "few" form
403  ///  * [remainingTextFieldCharacterCountMany], the "many" form
404  ///  * [remainingTextFieldCharacterCountOther], the "other" form
405  @protected
406  String get remainingTextFieldCharacterCountZero;
407
408  /// The "one" form of [remainingTextFieldCharacterCount].
409  ///
410  /// This form is optional.
411  ///
412  /// See also:
413  ///
414  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
415  ///  * [remainingTextFieldCharacterCountOne], the "one" form
416  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
417  ///  * [remainingTextFieldCharacterCountFew], the "few" form
418  ///  * [remainingTextFieldCharacterCountMany], the "many" form
419  ///  * [remainingTextFieldCharacterCountOther], the "other" form
420  @protected
421  String get remainingTextFieldCharacterCountOne => null;
422
423  /// The "two" form of [remainingTextFieldCharacterCount].
424  ///
425  /// This form is optional.
426  ///
427  /// See also:
428  ///
429  ///  * [Intl.plural], to which this form is passed.
430  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
431  ///  * [remainingTextFieldCharacterCountOne], the "one" form
432  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
433  ///  * [remainingTextFieldCharacterCountFew], the "few" form
434  ///  * [remainingTextFieldCharacterCountMany], the "many" form
435  ///  * [remainingTextFieldCharacterCountOther], the "other" form
436  @protected
437  String get remainingTextFieldCharacterCountTwo => null;
438
439  /// The "many" form of [remainingTextFieldCharacterCount].
440  ///
441  /// This form is optional.
442  ///
443  /// See also:
444  ///
445  ///  * [Intl.plural], to which this form is passed.
446  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
447  ///  * [remainingTextFieldCharacterCountOne], the "one" form
448  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
449  ///  * [remainingTextFieldCharacterCountFew], the "few" form
450  ///  * [remainingTextFieldCharacterCountMany], the "many" form
451  ///  * [remainingTextFieldCharacterCountOther], the "other" form
452  @protected
453  String get remainingTextFieldCharacterCountMany => null;
454
455  /// The "few" form of [remainingTextFieldCharacterCount].
456  ///
457  /// This form is optional.
458  ///
459  /// See also:
460  ///
461  ///  * [Intl.plural], to which this form is passed.
462  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
463  ///  * [remainingTextFieldCharacterCountOne], the "one" form
464  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
465  ///  * [remainingTextFieldCharacterCountFew], the "few" form
466  ///  * [remainingTextFieldCharacterCountMany], the "many" form
467  ///  * [remainingTextFieldCharacterCountOther], the "other" form
468  @protected
469  String get remainingTextFieldCharacterCountFew => null;
470
471  /// The "other" form of [remainingTextFieldCharacterCount].
472  ///
473  /// This form is required.
474  ///
475  /// See also:
476  ///
477  ///  * [Intl.plural], to which this form is passed.
478  ///  * [remainingTextFieldCharacterCountZero], the "zero" form
479  ///  * [remainingTextFieldCharacterCountOne], the "one" form
480  ///  * [remainingTextFieldCharacterCountTwo], the "two" form
481  ///  * [remainingTextFieldCharacterCountFew], the "few" form
482  ///  * [remainingTextFieldCharacterCountMany], the "many" form
483  ///  * [remainingTextFieldCharacterCountOther], the "other" form
484  @protected
485  String get remainingTextFieldCharacterCountOther;
486
487  @override
488  String remainingTextFieldCharacterCount(int remainingCount) {
489    return intl.Intl.pluralLogic(
490      remainingCount,
491      zero: remainingTextFieldCharacterCountZero,
492      one: remainingTextFieldCharacterCountOne,
493      two: remainingTextFieldCharacterCountTwo,
494      many: remainingTextFieldCharacterCountMany,
495      few: remainingTextFieldCharacterCountFew,
496      other: remainingTextFieldCharacterCountOther,
497      locale: _localeName,
498    ).replaceFirst(r'$remainingCount', formatDecimal(remainingCount));
499  }
500
501  @override
502  ScriptCategory get scriptCategory;
503
504  /// A [LocalizationsDelegate] that uses [GlobalMaterialLocalizations.load]
505  /// to create an instance of this class.
506  ///
507  /// Most internationalized apps will use [GlobalMaterialLocalizations.delegates]
508  /// as the value of [MaterialApp.localizationsDelegates] to include
509  /// the localizations for both the material and widget libraries.
510  static const LocalizationsDelegate<MaterialLocalizations> delegate = _MaterialLocalizationsDelegate();
511
512  /// A value for [MaterialApp.localizationsDelegates] that's typically used by
513  /// internationalized apps.
514  ///
515  /// ## Sample code
516  ///
517  /// To include the localizations provided by this class and by
518  /// [GlobalWidgetsLocalizations] in a [MaterialApp],
519  /// use [GlobalMaterialLocalizations.delegates] as the value of
520  /// [MaterialApp.localizationsDelegates], and specify the locales your
521  /// app supports with [MaterialApp.supportedLocales]:
522  ///
523  /// ```dart
524  /// new MaterialApp(
525  ///   localizationsDelegates: GlobalMaterialLocalizations.delegates,
526  ///   supportedLocales: [
527  ///     const Locale('en', 'US'), // English
528  ///     const Locale('he', 'IL'), // Hebrew
529  ///   ],
530  ///   // ...
531  /// )
532  /// ```
533  static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
534    GlobalCupertinoLocalizations.delegate,
535    GlobalMaterialLocalizations.delegate,
536    GlobalWidgetsLocalizations.delegate,
537  ];
538}
539
540/// Finds the [TimeOfDayFormat] to use instead of the `original` when the
541/// `original` uses 12-hour format and [MediaQueryData.alwaysUse24HourFormat]
542/// is true.
543TimeOfDayFormat _get24HourVersionOf(TimeOfDayFormat original) {
544  switch (original) {
545    case TimeOfDayFormat.HH_colon_mm:
546    case TimeOfDayFormat.HH_dot_mm:
547    case TimeOfDayFormat.frenchCanadian:
548    case TimeOfDayFormat.H_colon_mm:
549      return original;
550    case TimeOfDayFormat.h_colon_mm_space_a:
551    case TimeOfDayFormat.a_space_h_colon_mm:
552      return TimeOfDayFormat.HH_colon_mm;
553  }
554  return TimeOfDayFormat.HH_colon_mm;
555}
556
557class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
558  const _MaterialLocalizationsDelegate();
559
560  @override
561  bool isSupported(Locale locale) => kMaterialSupportedLanguages.contains(locale.languageCode);
562
563  static final Map<Locale, Future<MaterialLocalizations>> _loadedTranslations = <Locale, Future<MaterialLocalizations>>{};
564
565  @override
566  Future<MaterialLocalizations> load(Locale locale) {
567    assert(isSupported(locale));
568    return _loadedTranslations.putIfAbsent(locale, () {
569      util.loadDateIntlDataIfNotLoaded();
570
571      final String localeName = intl.Intl.canonicalizedLocale(locale.toString());
572      assert(
573        locale.toString() == localeName,
574        'Flutter does not support the non-standard locale form $locale (which '
575        'might be $localeName',
576      );
577
578      intl.DateFormat fullYearFormat;
579      intl.DateFormat mediumDateFormat;
580      intl.DateFormat longDateFormat;
581      intl.DateFormat yearMonthFormat;
582      if (intl.DateFormat.localeExists(localeName)) {
583        fullYearFormat = intl.DateFormat.y(localeName);
584        mediumDateFormat = intl.DateFormat.MMMEd(localeName);
585        longDateFormat = intl.DateFormat.yMMMMEEEEd(localeName);
586        yearMonthFormat = intl.DateFormat.yMMMM(localeName);
587      } else if (intl.DateFormat.localeExists(locale.languageCode)) {
588        fullYearFormat = intl.DateFormat.y(locale.languageCode);
589        mediumDateFormat = intl.DateFormat.MMMEd(locale.languageCode);
590        longDateFormat = intl.DateFormat.yMMMMEEEEd(locale.languageCode);
591        yearMonthFormat = intl.DateFormat.yMMMM(locale.languageCode);
592      } else {
593        fullYearFormat = intl.DateFormat.y();
594        mediumDateFormat = intl.DateFormat.MMMEd();
595        longDateFormat = intl.DateFormat.yMMMMEEEEd();
596        yearMonthFormat = intl.DateFormat.yMMMM();
597      }
598
599      intl.NumberFormat decimalFormat;
600      intl.NumberFormat twoDigitZeroPaddedFormat;
601      if (intl.NumberFormat.localeExists(localeName)) {
602        decimalFormat = intl.NumberFormat.decimalPattern(localeName);
603        twoDigitZeroPaddedFormat = intl.NumberFormat('00', localeName);
604      } else if (intl.NumberFormat.localeExists(locale.languageCode)) {
605        decimalFormat = intl.NumberFormat.decimalPattern(locale.languageCode);
606        twoDigitZeroPaddedFormat = intl.NumberFormat('00', locale.languageCode);
607      } else {
608        decimalFormat = intl.NumberFormat.decimalPattern();
609        twoDigitZeroPaddedFormat = intl.NumberFormat('00');
610      }
611
612      return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation(
613        locale,
614        fullYearFormat,
615        mediumDateFormat,
616        longDateFormat,
617        yearMonthFormat,
618        decimalFormat,
619        twoDigitZeroPaddedFormat,
620      ));
621    });
622  }
623
624  @override
625  bool shouldReload(_MaterialLocalizationsDelegate old) => false;
626
627  @override
628  String toString() => 'GlobalMaterialLocalizations.delegate(${kMaterialSupportedLanguages.length} locales)';
629}
630