Cupertino localization step 6: add a GlobalCupertinoLocalizations base class with date time formatting (#29767)
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
// Copyright 2019 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/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'package:intl/date_symbols.dart' as intl;
|
||||
|
||||
import 'utils/date_localizations.dart' as util;
|
||||
import 'widgets_localizations.dart';
|
||||
|
||||
/// Implementation of localized strings for Cupertino widgets using the `intl`
|
||||
/// package for date and time formatting.
|
||||
///
|
||||
/// Further localization of strings beyond date time formatting are provided
|
||||
/// by language specific subclasses of [GlobalCupertinoLocalizations]
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DefaultCupertinoLocalizations], which provides US English localizations
|
||||
/// for Cupertino widgets.
|
||||
abstract class GlobalCupertinoLocalizations implements CupertinoLocalizations {
|
||||
/// Initializes an object that defines the Cupertino widgets' localized
|
||||
/// strings for the given `localeName`.
|
||||
///
|
||||
/// The remaining '*Format' arguments uses the intl package to provide
|
||||
/// [DateFormat] configurations for the `localeName`.
|
||||
const GlobalCupertinoLocalizations({
|
||||
@required String localeName,
|
||||
@required intl.DateFormat fullYearFormat,
|
||||
@required intl.DateFormat dayFormat,
|
||||
@required intl.DateFormat mediumDateFormat,
|
||||
@required intl.DateFormat singleDigitHourFormat,
|
||||
@required intl.DateFormat singleDigitMinuteFormat,
|
||||
@required intl.DateFormat doubleDigitMinuteFormat,
|
||||
@required intl.DateFormat singleDigitSecondFormat,
|
||||
@required intl.NumberFormat decimalFormat,
|
||||
}) : assert(localeName != null),
|
||||
_localeName = localeName,
|
||||
assert(fullYearFormat != null),
|
||||
_fullYearFormat = fullYearFormat,
|
||||
assert(dayFormat != null),
|
||||
_dayFormat = dayFormat,
|
||||
assert(mediumDateFormat != null),
|
||||
_mediumDateFormat = mediumDateFormat,
|
||||
assert(singleDigitHourFormat != null),
|
||||
_singleDigitHourFormat = singleDigitHourFormat,
|
||||
assert(singleDigitMinuteFormat != null),
|
||||
_singleDigitMinuteFormat = singleDigitMinuteFormat,
|
||||
assert(doubleDigitMinuteFormat != null),
|
||||
_doubleDigitMinuteFormat = doubleDigitMinuteFormat,
|
||||
assert(singleDigitSecondFormat != null),
|
||||
_singleDigitSecondFormat = singleDigitSecondFormat,
|
||||
assert(decimalFormat != null),
|
||||
_decimalFormat =decimalFormat;
|
||||
|
||||
final String _localeName;
|
||||
final intl.DateFormat _fullYearFormat;
|
||||
final intl.DateFormat _dayFormat;
|
||||
final intl.DateFormat _mediumDateFormat;
|
||||
final intl.DateFormat _singleDigitHourFormat;
|
||||
final intl.DateFormat _singleDigitMinuteFormat;
|
||||
final intl.DateFormat _doubleDigitMinuteFormat;
|
||||
final intl.DateFormat _singleDigitSecondFormat;
|
||||
final intl.NumberFormat _decimalFormat;
|
||||
|
||||
@override
|
||||
String datePickerYear(int yearIndex) {
|
||||
return _fullYearFormat.format(DateTime.utc(yearIndex));
|
||||
}
|
||||
|
||||
@override
|
||||
String datePickerMonth(int monthIndex) {
|
||||
// It doesn't actually have anything to do with _fullYearFormat. It's just
|
||||
// taking advantage of the fact that _fullYearFormat loaded the needed
|
||||
// locale's symbols.
|
||||
return _fullYearFormat.dateSymbols.MONTHS[monthIndex - 1];
|
||||
}
|
||||
|
||||
@override
|
||||
String datePickerDayOfMonth(int dayIndex) {
|
||||
// Year and month doesn't matter since we just want to day formatted.
|
||||
return _dayFormat.format(DateTime.utc(0, 0, dayIndex));
|
||||
}
|
||||
|
||||
@override
|
||||
String datePickerMediumDate(DateTime date) {
|
||||
return _mediumDateFormat.format(date);
|
||||
}
|
||||
|
||||
@override
|
||||
String datePickerHour(int hour) {
|
||||
return _singleDigitHourFormat.format(DateTime.utc(0, 0, 0, hour));
|
||||
}
|
||||
|
||||
@override
|
||||
String datePickerMinute(int minute) {
|
||||
return _doubleDigitMinuteFormat.format(DateTime.utc(0, 0, 0, 0, minute));
|
||||
}
|
||||
|
||||
/// Subclasses should provide the optional zero pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelZero => null;
|
||||
/// Subclasses should provide the optional one pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelOne => null;
|
||||
/// Subclasses should provide the optional two pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelTwo => null;
|
||||
/// Subclasses should provide the optional few pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelFew => null;
|
||||
/// Subclasses should provide the optional many pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelMany => null;
|
||||
/// Subclasses should provide the required other pluralization of [datePickerHourSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerHourSemanticsLabelOther;
|
||||
|
||||
@override
|
||||
String datePickerHourSemanticsLabel(int hour) {
|
||||
return intl.Intl.pluralLogic(
|
||||
hour,
|
||||
zero: datePickerHourSemanticsLabelZero,
|
||||
one: datePickerHourSemanticsLabelOne,
|
||||
two: datePickerHourSemanticsLabelTwo,
|
||||
few: datePickerHourSemanticsLabelFew,
|
||||
many: datePickerHourSemanticsLabelMany,
|
||||
other: datePickerHourSemanticsLabelOther,
|
||||
locale: _localeName,
|
||||
).replaceFirst(r'$hour', _decimalFormat.format(hour));
|
||||
}
|
||||
|
||||
/// Subclasses should provide the optional zero pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelZero => null;
|
||||
/// Subclasses should provide the optional one pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelOne => null;
|
||||
/// Subclasses should provide the optional two pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelTwo => null;
|
||||
/// Subclasses should provide the optional few pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelFew => null;
|
||||
/// Subclasses should provide the optional many pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelMany => null;
|
||||
/// Subclasses should provide the required other pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file.
|
||||
@protected String get datePickerMinuteSemanticsLabelOther;
|
||||
|
||||
@override
|
||||
String datePickerMinuteSemanticsLabel(int minute) {
|
||||
return intl.Intl.pluralLogic(
|
||||
minute,
|
||||
zero: datePickerMinuteSemanticsLabelZero,
|
||||
one: datePickerMinuteSemanticsLabelOne,
|
||||
two: datePickerMinuteSemanticsLabelTwo,
|
||||
few: datePickerMinuteSemanticsLabelFew,
|
||||
many: datePickerMinuteSemanticsLabelMany,
|
||||
other: datePickerMinuteSemanticsLabelOther,
|
||||
locale: _localeName,
|
||||
).replaceFirst(r'$minute', _decimalFormat.format(minute));
|
||||
}
|
||||
|
||||
/// A string describing the [DatePickerDateOrder] enum value.
|
||||
///
|
||||
/// Subclasses should provide this string value based on the ARB file for
|
||||
/// the locale.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [datePickerDateOrder], which provides the [DatePickerDateOrder]
|
||||
/// enum value for [CupertinoLocalizations] based on this string value
|
||||
@protected
|
||||
String get datePickerDateOrderString;
|
||||
|
||||
@override
|
||||
DatePickerDateOrder get datePickerDateOrder {
|
||||
switch (datePickerDateOrderString) {
|
||||
case 'dmy':
|
||||
return DatePickerDateOrder.dmy;
|
||||
case 'mdy':
|
||||
return DatePickerDateOrder.mdy;
|
||||
case 'ymd':
|
||||
return DatePickerDateOrder.ymd;
|
||||
case 'ydm':
|
||||
return DatePickerDateOrder.ydm;
|
||||
default:
|
||||
assert(
|
||||
false,
|
||||
'Failed to load DatePickerDateOrder $datePickerDateOrderString for '
|
||||
'locale $_localeName.\nNon conforming string for $_localeName\'s '
|
||||
'.arb file',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A string describing the [DatePickerDateTimeOrder] enum value.
|
||||
///
|
||||
/// Subclasses should provide this string value based on the ARB file for
|
||||
/// the locale.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [datePickerDateTimeOrder], which provides the [DatePickerDateTimeOrder]
|
||||
/// enum value for [CupertinoLocalizations] based on this string value.
|
||||
@protected
|
||||
String get datePickerDateTimeOrderString;
|
||||
|
||||
@override
|
||||
DatePickerDateTimeOrder get datePickerDateTimeOrder {
|
||||
switch (datePickerDateTimeOrderString) {
|
||||
case 'date_time_dayPeriod':
|
||||
return DatePickerDateTimeOrder.date_time_dayPeriod;
|
||||
case 'date_dayPeriod_time':
|
||||
return DatePickerDateTimeOrder.date_dayPeriod_time;
|
||||
case 'time_dayPeriod_date':
|
||||
return DatePickerDateTimeOrder.time_dayPeriod_date;
|
||||
case 'dayPeriod_time_date':
|
||||
return DatePickerDateTimeOrder.dayPeriod_time_date;
|
||||
default:
|
||||
assert(
|
||||
false,
|
||||
'Failed to load DatePickerDateTimeOrder $datePickerDateTimeOrderString '
|
||||
'for locale $_localeName.\nNon conforming string for $_localeName\'s '
|
||||
'.arb file',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String timerPickerHour(int hour) {
|
||||
return _singleDigitHourFormat.format(DateTime.utc(0, 0, 0, hour));
|
||||
}
|
||||
|
||||
@override
|
||||
String timerPickerMinute(int minute) {
|
||||
return _singleDigitMinuteFormat.format(DateTime.utc(0, 0, 0, 0, minute));
|
||||
}
|
||||
|
||||
@override
|
||||
String timerPickerSecond(int second) {
|
||||
return _singleDigitSecondFormat.format(DateTime.utc(0, 0, 0, 0, 0, second));
|
||||
}
|
||||
|
||||
/// Subclasses should provide the optional zero pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelZero => null;
|
||||
/// Subclasses should provide the optional one pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelOne => null;
|
||||
/// Subclasses should provide the optional two pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelTwo => null;
|
||||
/// Subclasses should provide the optional few pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelFew => null;
|
||||
/// Subclasses should provide the optional many pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelMany => null;
|
||||
/// Subclasses should provide the required other pluralization of [timerPickerHourLabel] based on the ARB file.
|
||||
@protected String get timerPickerHourLabelOther;
|
||||
|
||||
@override
|
||||
String timerPickerHourLabel(int hour) {
|
||||
return intl.Intl.pluralLogic(
|
||||
hour,
|
||||
zero: timerPickerHourLabelZero,
|
||||
one: timerPickerHourLabelOne,
|
||||
two: timerPickerHourLabelTwo,
|
||||
few: timerPickerHourLabelFew,
|
||||
many: timerPickerHourLabelMany,
|
||||
other: timerPickerHourLabelOther,
|
||||
locale: _localeName,
|
||||
).replaceFirst(r'$hour', _decimalFormat.format(hour));
|
||||
}
|
||||
|
||||
/// Subclasses should provide the optional zero pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelZero => null;
|
||||
/// Subclasses should provide the optional one pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelOne => null;
|
||||
/// Subclasses should provide the optional two pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelTwo => null;
|
||||
/// Subclasses should provide the optional few pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelFew => null;
|
||||
/// Subclasses should provide the optional many pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelMany => null;
|
||||
/// Subclasses should provide the required other pluralization of [timerPickerMinuteLabel] based on the ARB file.
|
||||
@protected String get timerPickerMinuteLabelOther;
|
||||
|
||||
@override
|
||||
String timerPickerMinuteLabel(int minute) {
|
||||
return intl.Intl.pluralLogic(
|
||||
minute,
|
||||
zero: timerPickerMinuteLabelZero,
|
||||
one: timerPickerMinuteLabelOne,
|
||||
two: timerPickerMinuteLabelTwo,
|
||||
few: timerPickerMinuteLabelFew,
|
||||
many: timerPickerMinuteLabelMany,
|
||||
other: timerPickerMinuteLabelOther,
|
||||
locale: _localeName,
|
||||
).replaceFirst(r'$minute', _decimalFormat.format(minute));
|
||||
}
|
||||
|
||||
/// Subclasses should provide the optional zero pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelZero => null;
|
||||
/// Subclasses should provide the optional one pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelOne => null;
|
||||
/// Subclasses should provide the optional two pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelTwo => null;
|
||||
/// Subclasses should provide the optional few pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelFew => null;
|
||||
/// Subclasses should provide the optional many pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelMany => null;
|
||||
/// Subclasses should provide the required other pluralization of [timerPickerSecondLabel] based on the ARB file.
|
||||
@protected String get timerPickerSecondLabelOther;
|
||||
|
||||
@override
|
||||
String timerPickerSecondLabel(int second) {
|
||||
return intl.Intl.pluralLogic(
|
||||
second,
|
||||
zero: timerPickerSecondLabelZero,
|
||||
one: timerPickerSecondLabelOne,
|
||||
two: timerPickerSecondLabelTwo,
|
||||
few: timerPickerSecondLabelFew,
|
||||
many: timerPickerSecondLabelMany,
|
||||
other: timerPickerSecondLabelOther,
|
||||
locale: _localeName,
|
||||
).replaceFirst(r'$second', _decimalFormat.format(second));
|
||||
}
|
||||
|
||||
/// A [LocalizationsDelegate] that uses [GlobalCupertinoLocalizations.load]
|
||||
/// to create an instance of this class.
|
||||
///
|
||||
/// Most internationalized apps will use [GlobalCupertinoLocalizations.delegates]
|
||||
/// as the value of [CupertinoApp.localizationsDelegates] to include
|
||||
/// the localizations for both the cupertino and widget libraries.
|
||||
static const LocalizationsDelegate<CupertinoLocalizations> delegate = _GlobalCupertinoLocalizationsDelegate();
|
||||
|
||||
/// A value for [CupertinoApp.localizationsDelegates] that's typically used by
|
||||
/// internationalized apps.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// To include the localizations provided by this class and by
|
||||
/// [GlobalWidgetsLocalizations] in a [CupertinoApp],
|
||||
/// use [GlobalCupertinoLocalizations.delegates] as the value of
|
||||
/// [CupertinoApp.localizationsDelegates], and specify the locales your
|
||||
/// app supports with [CupertinoApp.supportedLocales]:
|
||||
///
|
||||
/// ```dart
|
||||
/// new CupertinoApp(
|
||||
/// localizationsDelegates: GlobalCupertinoLocalizations.delegates,
|
||||
/// supportedLocales: [
|
||||
/// const Locale('en', 'US'), // English
|
||||
/// const Locale('he', 'IL'), // Hebrew
|
||||
/// ],
|
||||
/// // ...
|
||||
/// )
|
||||
/// ```
|
||||
static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
];
|
||||
}
|
||||
|
||||
class _GlobalCupertinoLocalizationsDelegate extends LocalizationsDelegate<CupertinoLocalizations> {
|
||||
const _GlobalCupertinoLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) => false; // TODO(xster): implement.
|
||||
|
||||
static final Map<Locale, Future<CupertinoLocalizations>> _loadedTranslations = <Locale, Future<CupertinoLocalizations>>{};
|
||||
|
||||
@override
|
||||
Future<CupertinoLocalizations> load(Locale locale) {
|
||||
assert(isSupported(locale)); // TODO(xster): implement.
|
||||
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 dayFormat;
|
||||
intl.DateFormat mediumDateFormat;
|
||||
// We don't want any additional decoration here. The am/pm is handled in
|
||||
// the date picker. We just want an hour number localized.
|
||||
intl.DateFormat singleDigitHourFormat;
|
||||
intl.DateFormat singleDigitMinuteFormat;
|
||||
intl.DateFormat doubleDigitMinuteFormat;
|
||||
intl.DateFormat singleDigitSecondFormat;
|
||||
intl.NumberFormat decimalFormat;
|
||||
|
||||
void loadFormats(String locale) {
|
||||
fullYearFormat = intl.DateFormat.y(locale);
|
||||
dayFormat = intl.DateFormat.d(locale);
|
||||
mediumDateFormat = intl.DateFormat.MMMEd(locale);
|
||||
// TODO(xster): fix when https://github.com/dart-lang/intl/issues/207 is resolved.
|
||||
singleDigitHourFormat = intl.DateFormat('HH', locale);
|
||||
singleDigitMinuteFormat = intl.DateFormat.m(locale);
|
||||
doubleDigitMinuteFormat = intl.DateFormat('mm', locale);
|
||||
singleDigitSecondFormat = intl.DateFormat.s(locale);
|
||||
decimalFormat = intl.NumberFormat(locale);
|
||||
}
|
||||
|
||||
if (intl.DateFormat.localeExists(localeName)) {
|
||||
loadFormats(localeName);
|
||||
} else if (intl.DateFormat.localeExists(locale.languageCode)) {
|
||||
loadFormats(locale.languageCode);
|
||||
} else {
|
||||
loadFormats(null);
|
||||
}
|
||||
|
||||
return SynchronousFuture<CupertinoLocalizations>(_getCupertinoTranslation(
|
||||
localeName,
|
||||
fullYearFormat,
|
||||
dayFormat,
|
||||
mediumDateFormat,
|
||||
singleDigitHourFormat,
|
||||
singleDigitMinuteFormat,
|
||||
doubleDigitMinuteFormat,
|
||||
singleDigitSecondFormat,
|
||||
decimalFormat,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(_GlobalCupertinoLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
CupertinoLocalizations _getCupertinoTranslation(
|
||||
String localeName,
|
||||
intl.DateFormat fullYearFormat,
|
||||
intl.DateFormat dayFormat,
|
||||
intl.DateFormat mediumDateFormat,
|
||||
intl.DateFormat singleDigitHourFormat,
|
||||
intl.DateFormat singleDigitMinuteFormat,
|
||||
intl.DateFormat doubleDigitMinuteFormat,
|
||||
intl.DateFormat singleDigitSecondFormat,
|
||||
intl.NumberFormat decimalFormat,
|
||||
) {
|
||||
return null; // TODO(xster): implement in generated subclass.
|
||||
}
|
||||
@@ -8,10 +8,9 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'package:intl/date_symbols.dart' as intl;
|
||||
import 'package:intl/date_symbol_data_custom.dart' as date_symbol_data_custom;
|
||||
import 'l10n/generated_date_localizations.dart' as date_localizations;
|
||||
|
||||
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
|
||||
@@ -559,57 +558,20 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal
|
||||
@override
|
||||
bool isSupported(Locale locale) => kSupportedLanguages.contains(locale.languageCode);
|
||||
|
||||
/// Tracks if date i18n data has been loaded.
|
||||
static bool _dateIntlDataInitialized = false;
|
||||
|
||||
/// Loads i18n data for dates if it hasn't be loaded yet.
|
||||
///
|
||||
/// Only the first invocation of this function has the effect of loading the
|
||||
/// data. Subsequent invocations have no effect.
|
||||
static void _loadDateIntlDataIfNotLoaded() {
|
||||
if (!_dateIntlDataInitialized) {
|
||||
// TODO(garyq): Add support for scriptCodes. Do not strip scriptCode from string.
|
||||
|
||||
// Keep track of initialzed locales, or will fail on attempted double init.
|
||||
// This can only happen if a locale with a stripped scriptCode has already
|
||||
// been initialzed. This should be removed when scriptCode stripping is removed.
|
||||
final Set<String> initializedLocales = <String>{};
|
||||
date_localizations.dateSymbols.forEach((String locale, dynamic data) {
|
||||
// Strip scriptCode from the locale, as we do not distinguish between scripts
|
||||
// for dates.
|
||||
final List<String> codes = locale.split('_');
|
||||
String countryCode;
|
||||
if (codes.length == 2) {
|
||||
countryCode = codes[1].length < 4 ? codes[1] : null;
|
||||
} else if (codes.length == 3) {
|
||||
countryCode = codes[1].length < codes[2].length ? codes[1] : codes[2];
|
||||
}
|
||||
locale = codes[0] + (countryCode != null ? '_' + countryCode : '');
|
||||
if (initializedLocales.contains(locale))
|
||||
return;
|
||||
initializedLocales.add(locale);
|
||||
// Perform initialization.
|
||||
assert(date_localizations.datePatterns.containsKey(locale));
|
||||
final intl.DateSymbols symbols = intl.DateSymbols.deserializeFromMap(data);
|
||||
date_symbol_data_custom.initializeDateFormattingCustom(
|
||||
locale: locale,
|
||||
symbols: symbols,
|
||||
patterns: date_localizations.datePatterns[locale],
|
||||
);
|
||||
});
|
||||
_dateIntlDataInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static final Map<Locale, Future<MaterialLocalizations>> _loadedTranslations = <Locale, Future<MaterialLocalizations>>{};
|
||||
|
||||
@override
|
||||
Future<MaterialLocalizations> load(Locale locale) {
|
||||
assert(isSupported(locale));
|
||||
return _loadedTranslations.putIfAbsent(locale, () {
|
||||
_loadDateIntlDataIfNotLoaded();
|
||||
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 mediumDateFormat;
|
||||
@@ -645,8 +607,6 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal
|
||||
twoDigitZeroPaddedFormat = intl.NumberFormat('00');
|
||||
}
|
||||
|
||||
assert(locale.toString() == localeName, 'comparing "$locale" to "$localeName"');
|
||||
|
||||
return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation(
|
||||
locale,
|
||||
fullYearFormat,
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2019 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:intl/date_symbols.dart' as intl;
|
||||
import 'package:intl/date_symbol_data_custom.dart' as date_symbol_data_custom;
|
||||
import '../l10n/generated_date_localizations.dart' as date_localizations;
|
||||
|
||||
/// Tracks if date i18n data has been loaded.
|
||||
bool _dateIntlDataInitialized = false;
|
||||
|
||||
/// Loads i18n data for dates if it hasn't be loaded yet.
|
||||
///
|
||||
/// Only the first invocation of this function has the effect of loading the
|
||||
/// data. Subsequent invocations have no effect.
|
||||
void loadDateIntlDataIfNotLoaded() {
|
||||
if (!_dateIntlDataInitialized) {
|
||||
// TODO(garyq): Add support for scriptCodes. Do not strip scriptCode from string.
|
||||
|
||||
// Keep track of initialzed locales, or will fail on attempted double init.
|
||||
// This can only happen if a locale with a stripped scriptCode has already
|
||||
// been initialzed. This should be removed when scriptCode stripping is removed.
|
||||
final Set<String> initializedLocales = <String>{};
|
||||
date_localizations.dateSymbols.forEach((String locale, dynamic data) {
|
||||
// Strip scriptCode from the locale, as we do not distinguish between scripts
|
||||
// for dates.
|
||||
final List<String> codes = locale.split('_');
|
||||
String countryCode;
|
||||
if (codes.length == 2) {
|
||||
countryCode = codes[1].length < 4 ? codes[1] : null;
|
||||
} else if (codes.length == 3) {
|
||||
countryCode = codes[1].length < codes[2].length ? codes[1] : codes[2];
|
||||
}
|
||||
locale = codes[0] + (countryCode != null ? '_' + countryCode : '');
|
||||
if (initializedLocales.contains(locale))
|
||||
return;
|
||||
initializedLocales.add(locale);
|
||||
// Perform initialization.
|
||||
assert(date_localizations.datePatterns.containsKey(locale));
|
||||
final intl.DateSymbols symbols = intl.DateSymbols.deserializeFromMap(data);
|
||||
date_symbol_data_custom.initializeDateFormattingCustom(
|
||||
locale: locale,
|
||||
symbols: symbols,
|
||||
patterns: date_localizations.datePatterns[locale],
|
||||
);
|
||||
});
|
||||
_dateIntlDataInitialized = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user