| // Copyright 2014 The Flutter 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:intl/intl.dart' as intl; |
| |
| import 'cupertino_localizations.dart'; |
| import 'l10n/generated_material_localizations.dart'; |
| import 'utils/date_localizations.dart' as util; |
| import 'widgets_localizations.dart'; |
| |
| /// Implementation of localized strings for the material widgets using the |
| /// `intl` package for date and time formatting. |
| /// |
| /// ## Supported languages |
| /// |
| /// This class supports locales with the following [Locale.languageCode]s: |
| /// |
| /// {@macro flutter.localizations.material.languages} |
| /// |
| /// This list is available programmatically via [kMaterialSupportedLanguages]. |
| /// |
| /// ## Sample code |
| /// |
| /// To include the localizations provided by this class in a [MaterialApp], |
| /// add [GlobalMaterialLocalizations.delegates] to |
| /// [MaterialApp.localizationsDelegates], and specify the locales your |
| /// app supports with [MaterialApp.supportedLocales]: |
| /// |
| /// ```dart |
| /// MaterialApp( |
| /// localizationsDelegates: GlobalMaterialLocalizations.delegates, |
| /// supportedLocales: [ |
| /// const Locale('en', 'US'), // American English |
| /// const Locale('he', 'IL'), // Israeli Hebrew |
| /// // ... |
| /// ], |
| /// // ... |
| /// ) |
| /// ``` |
| /// |
| /// ## Overriding translations |
| /// |
| /// To create a translation that's similar to an existing language's translation |
| /// but has slightly different strings, subclass the relevant translation |
| /// directly and then create a [LocalizationsDelegate<MaterialLocalizations>] |
| /// subclass to define how to load it. |
| /// |
| /// Avoid subclassing an unrelated language (for example, subclassing |
| /// [MaterialLocalizationEn] and then passing a non-English `localeName` to the |
| /// constructor). Doing so will cause confusion for locale-specific behaviors; |
| /// in particular, translations that use the `localeName` for determining how to |
| /// pluralize will end up doing invalid things. Subclassing an existing |
| /// language's translations is only suitable for making small changes to the |
| /// existing strings. For providing a new language entirely, implement |
| /// [MaterialLocalizations] directly. |
| /// |
| /// See also: |
| /// |
| /// * The Flutter Internationalization Tutorial, |
| /// <https://flutter.dev/tutorials/internationalization/>. |
| /// * [DefaultMaterialLocalizations], which only provides US English translations. |
| abstract class GlobalMaterialLocalizations implements MaterialLocalizations { |
| /// Initializes an object that defines the material widgets' localized strings |
| /// for the given `locale`. |
| /// |
| /// The arguments are used for further runtime localization of data, |
| /// specifically for selecting plurals, date and time formatting, and number |
| /// formatting. They correspond to the following values: |
| /// |
| /// 1. The string that would be returned by [Intl.canonicalizedLocale] for |
| /// the locale. |
| /// 2. The [DateFormat] for [formatYear]. |
| /// 3. The [DateFormat] for [formatShortDate]. |
| /// 4. The [DateFormat] for [formatMediumDate]. |
| /// 5. The [DateFormat] for [formatFullDate]. |
| /// 6. The [DateFormat] for [formatMonthYear]. |
| /// 7. The [DateFormat] for [formatShortMonthDay]. |
| /// 8. The [NumberFormat] for [formatDecimal] (also used by [formatHour] and |
| /// [formatTimeOfDay] when [timeOfDayFormat] doesn't use [HourFormat.HH]). |
| /// 9. The [NumberFormat] for [formatHour] and the hour part of |
| /// [formatTimeOfDay] when [timeOfDayFormat] uses [HourFormat.HH], and for |
| /// [formatMinute] and the minute part of [formatTimeOfDay]. |
| /// |
| /// The [narrowWeekdays] and [firstDayOfWeekIndex] properties use the values |
| /// from the [intl.DateFormat] used by [formatFullDate]. |
| const GlobalMaterialLocalizations({ |
| required String localeName, |
| required intl.DateFormat fullYearFormat, |
| required intl.DateFormat compactDateFormat, |
| required intl.DateFormat shortDateFormat, |
| required intl.DateFormat mediumDateFormat, |
| required intl.DateFormat longDateFormat, |
| required intl.DateFormat yearMonthFormat, |
| required intl.DateFormat shortMonthDayFormat, |
| required intl.NumberFormat decimalFormat, |
| required intl.NumberFormat twoDigitZeroPaddedFormat, |
| }) : assert(localeName != null), |
| _localeName = localeName, |
| assert(fullYearFormat != null), |
| _fullYearFormat = fullYearFormat, |
| assert(compactDateFormat != null), |
| _compactDateFormat = compactDateFormat, |
| assert(shortDateFormat != null), |
| _shortDateFormat = shortDateFormat, |
| assert(mediumDateFormat != null), |
| _mediumDateFormat = mediumDateFormat, |
| assert(longDateFormat != null), |
| _longDateFormat = longDateFormat, |
| assert(yearMonthFormat != null), |
| _yearMonthFormat = yearMonthFormat, |
| assert(shortMonthDayFormat != null), |
| _shortMonthDayFormat = shortMonthDayFormat, |
| assert(decimalFormat != null), |
| _decimalFormat = decimalFormat, |
| assert(twoDigitZeroPaddedFormat != null), |
| _twoDigitZeroPaddedFormat = twoDigitZeroPaddedFormat; |
| |
| final String _localeName; |
| final intl.DateFormat _fullYearFormat; |
| final intl.DateFormat _compactDateFormat; |
| final intl.DateFormat _shortDateFormat; |
| final intl.DateFormat _mediumDateFormat; |
| final intl.DateFormat _longDateFormat; |
| final intl.DateFormat _yearMonthFormat; |
| final intl.DateFormat _shortMonthDayFormat; |
| final intl.NumberFormat _decimalFormat; |
| final intl.NumberFormat _twoDigitZeroPaddedFormat; |
| |
| @override |
| String formatHour(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) { |
| switch (hourFormat(of: timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat))) { |
| case HourFormat.HH: |
| return _twoDigitZeroPaddedFormat.format(timeOfDay.hour); |
| case HourFormat.H: |
| return formatDecimal(timeOfDay.hour); |
| case HourFormat.h: |
| final int hour = timeOfDay.hourOfPeriod; |
| return formatDecimal(hour == 0 ? 12 : hour); |
| } |
| } |
| |
| @override |
| String formatMinute(TimeOfDay timeOfDay) { |
| return _twoDigitZeroPaddedFormat.format(timeOfDay.minute); |
| } |
| |
| @override |
| String formatYear(DateTime date) { |
| return _fullYearFormat.format(date); |
| } |
| |
| @override |
| String formatCompactDate(DateTime date) { |
| return _compactDateFormat.format(date); |
| } |
| |
| @override |
| String formatShortDate(DateTime date) { |
| return _shortDateFormat.format(date); |
| } |
| |
| @override |
| String formatMediumDate(DateTime date) { |
| return _mediumDateFormat.format(date); |
| } |
| |
| @override |
| String formatFullDate(DateTime date) { |
| return _longDateFormat.format(date); |
| } |
| |
| @override |
| String formatMonthYear(DateTime date) { |
| return _yearMonthFormat.format(date); |
| } |
| |
| @override |
| String formatShortMonthDay(DateTime date) { |
| return _shortMonthDayFormat.format(date); |
| } |
| |
| @override |
| DateTime? parseCompactDate(String? inputString) { |
| try { |
| return inputString != null ? _compactDateFormat.parseStrict(inputString) : null; |
| } on FormatException { |
| return null; |
| } |
| } |
| |
| @override |
| List<String> get narrowWeekdays { |
| return _longDateFormat.dateSymbols.NARROWWEEKDAYS; |
| } |
| |
| @override |
| int get firstDayOfWeekIndex => (_longDateFormat.dateSymbols.FIRSTDAYOFWEEK + 1) % 7; |
| |
| @override |
| String formatDecimal(int number) { |
| return _decimalFormat.format(number); |
| } |
| |
| @override |
| String formatTimeOfDay(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) { |
| // Not using intl.DateFormat for two reasons: |
| // |
| // - DateFormat supports more formats than our material time picker does, |
| // and we want to be consistent across time picker format and the string |
| // formatting of the time of day. |
| // - DateFormat operates on DateTime, which is sensitive to time eras and |
| // time zones, while here we want to format hour and minute within one day |
| // no matter what date the day falls on. |
| final String hour = formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat); |
| final String minute = formatMinute(timeOfDay); |
| switch (timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat)) { |
| case TimeOfDayFormat.h_colon_mm_space_a: |
| return '$hour:$minute ${_formatDayPeriod(timeOfDay)!}'; |
| case TimeOfDayFormat.H_colon_mm: |
| case TimeOfDayFormat.HH_colon_mm: |
| return '$hour:$minute'; |
| case TimeOfDayFormat.HH_dot_mm: |
| return '$hour.$minute'; |
| case TimeOfDayFormat.a_space_h_colon_mm: |
| return '${_formatDayPeriod(timeOfDay)!} $hour:$minute'; |
| case TimeOfDayFormat.frenchCanadian: |
| return '$hour h $minute'; |
| } |
| } |
| |
| String? _formatDayPeriod(TimeOfDay timeOfDay) { |
| switch (timeOfDay.period) { |
| case DayPeriod.am: |
| return anteMeridiemAbbreviation; |
| case DayPeriod.pm: |
| return postMeridiemAbbreviation; |
| } |
| } |
| |
| /// The raw version of [dateRangeStartDateSemanticLabel], with `$formattedDate` verbatim |
| /// in the string. |
| @protected |
| String get dateRangeStartDateSemanticLabelRaw; |
| |
| @override |
| String dateRangeStartDateSemanticLabel(String formattedDate) { |
| return dateRangeStartDateSemanticLabelRaw.replaceFirst(r'$fullDate', formattedDate); |
| } |
| |
| /// The raw version of [dateRangeEndDateSemanticLabel], with `$fullDate` verbatim |
| /// in the string. |
| @protected |
| String get dateRangeEndDateSemanticLabelRaw; |
| |
| @override |
| String dateRangeEndDateSemanticLabel(String formattedDate) { |
| return dateRangeEndDateSemanticLabelRaw.replaceFirst(r'$fullDate', formattedDate); |
| } |
| |
| /// The raw version of [aboutListTileTitle], with `$applicationName` verbatim |
| /// in the string. |
| @protected |
| String get aboutListTileTitleRaw; |
| |
| @override |
| String aboutListTileTitle(String applicationName) { |
| final String text = aboutListTileTitleRaw; |
| return text.replaceFirst(r'$applicationName', applicationName); |
| } |
| |
| /// The raw version of [pageRowsInfoTitle], with `$firstRow`, `$lastRow`' and |
| /// `$rowCount` verbatim in the string, for the case where the value is |
| /// approximate. |
| @protected |
| String get pageRowsInfoTitleApproximateRaw; |
| |
| /// The raw version of [pageRowsInfoTitle], with `$firstRow`, `$lastRow`' and |
| /// `$rowCount` verbatim in the string, for the case where the value is |
| /// precise. |
| @protected |
| String get pageRowsInfoTitleRaw; |
| |
| @override |
| String pageRowsInfoTitle(int firstRow, int lastRow, int rowCount, bool rowCountIsApproximate) { |
| String? text = rowCountIsApproximate ? pageRowsInfoTitleApproximateRaw : null; |
| text ??= pageRowsInfoTitleRaw; |
| assert(text != null, 'A $_localeName localization was not found for pageRowsInfoTitle or pageRowsInfoTitleApproximate'); |
| return text |
| .replaceFirst(r'$firstRow', formatDecimal(firstRow)) |
| .replaceFirst(r'$lastRow', formatDecimal(lastRow)) |
| .replaceFirst(r'$rowCount', formatDecimal(rowCount)); |
| } |
| |
| /// The raw version of [tabLabel], with `$tabIndex` and `$tabCount` verbatim |
| /// in the string. |
| @protected |
| String get tabLabelRaw; |
| |
| @override |
| String tabLabel({ required int tabIndex, required int tabCount }) { |
| assert(tabIndex >= 1); |
| assert(tabCount >= 1); |
| final String template = tabLabelRaw; |
| return template |
| .replaceFirst(r'$tabIndex', formatDecimal(tabIndex)) |
| .replaceFirst(r'$tabCount', formatDecimal(tabCount)); |
| } |
| |
| /// The "zero" form of [selectedRowCountTitle]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleOne], the "one" form |
| /// * [selectedRowCountTitleTwo], the "two" form |
| /// * [selectedRowCountTitleFew], the "few" form |
| /// * [selectedRowCountTitleMany], the "many" form |
| /// * [selectedRowCountTitleOther], the "other" form |
| @protected |
| String? get selectedRowCountTitleZero => null; |
| |
| /// The "one" form of [selectedRowCountTitle]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleZero], the "zero" form |
| /// * [selectedRowCountTitleTwo], the "two" form |
| /// * [selectedRowCountTitleFew], the "few" form |
| /// * [selectedRowCountTitleMany], the "many" form |
| /// * [selectedRowCountTitleOther], the "other" form |
| @protected |
| String? get selectedRowCountTitleOne => null; |
| |
| /// The "two" form of [selectedRowCountTitle]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleZero], the "zero" form |
| /// * [selectedRowCountTitleOne], the "one" form |
| /// * [selectedRowCountTitleFew], the "few" form |
| /// * [selectedRowCountTitleMany], the "many" form |
| /// * [selectedRowCountTitleOther], the "other" form |
| @protected |
| String? get selectedRowCountTitleTwo => null; |
| |
| /// The "few" form of [selectedRowCountTitle]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleZero], the "zero" form |
| /// * [selectedRowCountTitleOne], the "one" form |
| /// * [selectedRowCountTitleTwo], the "two" form |
| /// * [selectedRowCountTitleMany], the "many" form |
| /// * [selectedRowCountTitleOther], the "other" form |
| @protected |
| String? get selectedRowCountTitleFew => null; |
| |
| /// The "many" form of [selectedRowCountTitle]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleZero], the "zero" form |
| /// * [selectedRowCountTitleOne], the "one" form |
| /// * [selectedRowCountTitleTwo], the "two" form |
| /// * [selectedRowCountTitleFew], the "few" form |
| /// * [selectedRowCountTitleOther], the "other" form |
| @protected |
| String? get selectedRowCountTitleMany => null; |
| |
| /// The "other" form of [selectedRowCountTitle]. |
| /// |
| /// This form is required. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [selectedRowCountTitleZero], the "zero" form |
| /// * [selectedRowCountTitleOne], the "one" form |
| /// * [selectedRowCountTitleTwo], the "two" form |
| /// * [selectedRowCountTitleFew], the "few" form |
| /// * [selectedRowCountTitleMany], the "many" form |
| @protected |
| String get selectedRowCountTitleOther; |
| |
| @override |
| String selectedRowCountTitle(int selectedRowCount) { |
| return intl.Intl.pluralLogic( |
| selectedRowCount, |
| zero: selectedRowCountTitleZero, |
| one: selectedRowCountTitleOne, |
| two: selectedRowCountTitleTwo, |
| few: selectedRowCountTitleFew, |
| many: selectedRowCountTitleMany, |
| other: selectedRowCountTitleOther, |
| locale: _localeName, |
| ).replaceFirst(r'$selectedRowCount', formatDecimal(selectedRowCount)); |
| } |
| |
| /// The format to use for [timeOfDayFormat]. |
| @protected |
| TimeOfDayFormat get timeOfDayFormatRaw; |
| |
| /// The [TimeOfDayFormat] corresponding to one of the following supported |
| /// patterns: |
| /// |
| /// * `HH:mm` |
| /// * `HH.mm` |
| /// * `HH 'h' mm` |
| /// * `HH:mm น.` |
| /// * `H:mm` |
| /// * `h:mm a` |
| /// * `a h:mm` |
| /// * `ah:mm` |
| /// |
| /// See also: |
| /// |
| /// * <http://demo.icu-project.org/icu-bin/locexp?d_=en&_=en_US>, which shows |
| /// the short time pattern used in the `en_US` locale. |
| @override |
| TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }) { |
| assert(alwaysUse24HourFormat != null); |
| if (alwaysUse24HourFormat) { |
| return _get24HourVersionOf(timeOfDayFormatRaw); |
| } |
| return timeOfDayFormatRaw; |
| } |
| |
| /// The "zero" form of [licensesPackageDetailText]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String? get licensesPackageDetailTextZero => null; |
| |
| /// The "one" form of [licensesPackageDetailText]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String? get licensesPackageDetailTextOne => null; |
| |
| /// The "two" form of [licensesPackageDetailText]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String? get licensesPackageDetailTextTwo => null; |
| |
| /// The "many" form of [licensesPackageDetailText]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String? get licensesPackageDetailTextMany => null; |
| |
| /// The "few" form of [licensesPackageDetailText]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String? get licensesPackageDetailTextFew => null; |
| |
| /// The "other" form of [licensesPackageDetailText]. |
| /// |
| /// This form is required. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [licensesPackageDetailTextZero], the "zero" form |
| /// * [licensesPackageDetailTextOne], the "one" form |
| /// * [licensesPackageDetailTextTwo], the "two" form |
| /// * [licensesPackageDetailTextFew], the "few" form |
| /// * [licensesPackageDetailTextMany], the "many" form |
| /// * [licensesPackageDetailTextOther], the "other" form |
| @protected |
| String get licensesPackageDetailTextOther; |
| |
| @override |
| String licensesPackageDetailText(int licenseCount) { |
| return intl.Intl.pluralLogic( |
| licenseCount, |
| zero: licensesPackageDetailTextZero, |
| one: licensesPackageDetailTextOne, |
| two: licensesPackageDetailTextTwo, |
| many: licensesPackageDetailTextMany, |
| few: licensesPackageDetailTextFew, |
| other: licensesPackageDetailTextOther, |
| locale: _localeName, |
| ).replaceFirst(r'$licenseCount', formatDecimal(licenseCount)); |
| } |
| |
| /// The "zero" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String? get remainingTextFieldCharacterCountZero => null; |
| |
| /// The "one" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String? get remainingTextFieldCharacterCountOne => null; |
| |
| /// The "two" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String? get remainingTextFieldCharacterCountTwo => null; |
| |
| /// The "many" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String? get remainingTextFieldCharacterCountMany => null; |
| |
| /// The "few" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is optional. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String? get remainingTextFieldCharacterCountFew => null; |
| |
| /// The "other" form of [remainingTextFieldCharacterCount]. |
| /// |
| /// This form is required. |
| /// |
| /// See also: |
| /// |
| /// * [Intl.plural], to which this form is passed. |
| /// * [remainingTextFieldCharacterCountZero], the "zero" form |
| /// * [remainingTextFieldCharacterCountOne], the "one" form |
| /// * [remainingTextFieldCharacterCountTwo], the "two" form |
| /// * [remainingTextFieldCharacterCountFew], the "few" form |
| /// * [remainingTextFieldCharacterCountMany], the "many" form |
| /// * [remainingTextFieldCharacterCountOther], the "other" form |
| @protected |
| String get remainingTextFieldCharacterCountOther; |
| |
| @override |
| String remainingTextFieldCharacterCount(int remaining) { |
| return intl.Intl.pluralLogic( |
| remaining, |
| zero: remainingTextFieldCharacterCountZero, |
| one: remainingTextFieldCharacterCountOne, |
| two: remainingTextFieldCharacterCountTwo, |
| many: remainingTextFieldCharacterCountMany, |
| few: remainingTextFieldCharacterCountFew, |
| other: remainingTextFieldCharacterCountOther, |
| locale: _localeName, |
| ).replaceFirst(r'$remainingCount', formatDecimal(remaining)); |
| } |
| |
| @override |
| ScriptCategory get scriptCategory; |
| |
| /// A [LocalizationsDelegate] for [MaterialLocalizations]. |
| /// |
| /// Most internationalized apps will use [GlobalMaterialLocalizations.delegates] |
| /// as the value of [MaterialApp.localizationsDelegates] to include |
| /// the localizations for both the material and widget libraries. |
| static const LocalizationsDelegate<MaterialLocalizations> delegate = _MaterialLocalizationsDelegate(); |
| |
| /// A value for [MaterialApp.localizationsDelegates] that's typically used by |
| /// internationalized apps. |
| /// |
| /// ## Sample code |
| /// |
| /// To include the localizations provided by this class and by |
| /// [GlobalWidgetsLocalizations] in a [MaterialApp], |
| /// use [GlobalMaterialLocalizations.delegates] as the value of |
| /// [MaterialApp.localizationsDelegates], and specify the locales your |
| /// app supports with [MaterialApp.supportedLocales]: |
| /// |
| /// ```dart |
| /// MaterialApp( |
| /// localizationsDelegates: GlobalMaterialLocalizations.delegates, |
| /// supportedLocales: [ |
| /// const Locale('en', 'US'), // English |
| /// const Locale('he', 'IL'), // Hebrew |
| /// ], |
| /// // ... |
| /// ) |
| /// ``` |
| static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[ |
| GlobalCupertinoLocalizations.delegate, |
| GlobalMaterialLocalizations.delegate, |
| GlobalWidgetsLocalizations.delegate, |
| ]; |
| } |
| |
| /// Finds the [TimeOfDayFormat] to use instead of the `original` when the |
| /// `original` uses 12-hour format and [MediaQueryData.alwaysUse24HourFormat] |
| /// is true. |
| TimeOfDayFormat _get24HourVersionOf(TimeOfDayFormat original) { |
| switch (original) { |
| case TimeOfDayFormat.HH_colon_mm: |
| case TimeOfDayFormat.HH_dot_mm: |
| case TimeOfDayFormat.frenchCanadian: |
| case TimeOfDayFormat.H_colon_mm: |
| return original; |
| case TimeOfDayFormat.h_colon_mm_space_a: |
| case TimeOfDayFormat.a_space_h_colon_mm: |
| return TimeOfDayFormat.HH_colon_mm; |
| } |
| } |
| |
| class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> { |
| const _MaterialLocalizationsDelegate(); |
| |
| @override |
| bool isSupported(Locale locale) => kMaterialSupportedLanguages.contains(locale.languageCode); |
| |
| static final Map<Locale, Future<MaterialLocalizations>> _loadedTranslations = <Locale, Future<MaterialLocalizations>>{}; |
| |
| @override |
| Future<MaterialLocalizations> load(Locale locale) { |
| assert(isSupported(locale)); |
| return _loadedTranslations.putIfAbsent(locale, () { |
| util.loadDateIntlDataIfNotLoaded(); |
| |
| final String localeName = intl.Intl.canonicalizedLocale(locale.toString()); |
| assert( |
| locale.toString() == localeName, |
| 'Flutter does not support the non-standard locale form $locale (which ' |
| 'might be $localeName', |
| ); |
| |
| intl.DateFormat fullYearFormat; |
| intl.DateFormat compactDateFormat; |
| intl.DateFormat shortDateFormat; |
| intl.DateFormat mediumDateFormat; |
| intl.DateFormat longDateFormat; |
| intl.DateFormat yearMonthFormat; |
| intl.DateFormat shortMonthDayFormat; |
| if (intl.DateFormat.localeExists(localeName)) { |
| fullYearFormat = intl.DateFormat.y(localeName); |
| compactDateFormat = intl.DateFormat.yMd(localeName); |
| shortDateFormat = intl.DateFormat.yMMMd(localeName); |
| mediumDateFormat = intl.DateFormat.MMMEd(localeName); |
| longDateFormat = intl.DateFormat.yMMMMEEEEd(localeName); |
| yearMonthFormat = intl.DateFormat.yMMMM(localeName); |
| shortMonthDayFormat = intl.DateFormat.MMMd(localeName); |
| } else if (intl.DateFormat.localeExists(locale.languageCode)) { |
| fullYearFormat = intl.DateFormat.y(locale.languageCode); |
| compactDateFormat = intl.DateFormat.yMd(locale.languageCode); |
| shortDateFormat = intl.DateFormat.yMMMd(locale.languageCode); |
| mediumDateFormat = intl.DateFormat.MMMEd(locale.languageCode); |
| longDateFormat = intl.DateFormat.yMMMMEEEEd(locale.languageCode); |
| yearMonthFormat = intl.DateFormat.yMMMM(locale.languageCode); |
| shortMonthDayFormat = intl.DateFormat.MMMd(locale.languageCode); |
| } else { |
| fullYearFormat = intl.DateFormat.y(); |
| compactDateFormat = intl.DateFormat.yMd(); |
| shortDateFormat = intl.DateFormat.yMMMd(); |
| mediumDateFormat = intl.DateFormat.MMMEd(); |
| longDateFormat = intl.DateFormat.yMMMMEEEEd(); |
| yearMonthFormat = intl.DateFormat.yMMMM(); |
| shortMonthDayFormat = intl.DateFormat.MMMd(); |
| } |
| |
| intl.NumberFormat decimalFormat; |
| intl.NumberFormat twoDigitZeroPaddedFormat; |
| if (intl.NumberFormat.localeExists(localeName)) { |
| decimalFormat = intl.NumberFormat.decimalPattern(localeName); |
| twoDigitZeroPaddedFormat = intl.NumberFormat('00', localeName); |
| } else if (intl.NumberFormat.localeExists(locale.languageCode)) { |
| decimalFormat = intl.NumberFormat.decimalPattern(locale.languageCode); |
| twoDigitZeroPaddedFormat = intl.NumberFormat('00', locale.languageCode); |
| } else { |
| decimalFormat = intl.NumberFormat.decimalPattern(); |
| twoDigitZeroPaddedFormat = intl.NumberFormat('00'); |
| } |
| |
| return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation( |
| locale, |
| fullYearFormat, |
| compactDateFormat, |
| shortDateFormat, |
| mediumDateFormat, |
| longDateFormat, |
| yearMonthFormat, |
| shortMonthDayFormat, |
| decimalFormat, |
| twoDigitZeroPaddedFormat, |
| )!); |
| }); |
| } |
| |
| @override |
| bool shouldReload(_MaterialLocalizationsDelegate old) => false; |
| |
| @override |
| String toString() => 'GlobalMaterialLocalizations.delegate(${kMaterialSupportedLanguages.length} locales)'; |
| } |