"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter_localizations/lib/src/material_localizations.dart" (13 Nov 2020, 29583 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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