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