Cupertino localization step 6: add a GlobalCupertinoLocalizations base class with date time formatting (#29767)
diff --git a/packages/flutter_localizations/lib/src/cupertino_localizations.dart b/packages/flutter_localizations/lib/src/cupertino_localizations.dart
new file mode 100644
index 0000000..557a934
--- /dev/null
+++ b/packages/flutter_localizations/lib/src/cupertino_localizations.dart
@@ -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.
+}
diff --git a/packages/flutter_localizations/lib/src/material_localizations.dart b/packages/flutter_localizations/lib/src/material_localizations.dart
index 1dbeaca..a0b72d2 100644
--- a/packages/flutter_localizations/lib/src/material_localizations.dart
+++ b/packages/flutter_localizations/lib/src/material_localizations.dart
@@ -8,10 +8,9 @@
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 @@
@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 @@
twoDigitZeroPaddedFormat = intl.NumberFormat('00');
}
- assert(locale.toString() == localeName, 'comparing "$locale" to "$localeName"');
-
return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation(
locale,
fullYearFormat,
diff --git a/packages/flutter_localizations/lib/src/utils/date_localizations.dart b/packages/flutter_localizations/lib/src/utils/date_localizations.dart
new file mode 100644
index 0000000..704322c
--- /dev/null
+++ b/packages/flutter_localizations/lib/src/utils/date_localizations.dart
@@ -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;
+ }
+}
\ No newline at end of file