| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | /// @docImport 'package:intl/intl.dart'; |
| 6 | library; |
| 7 | |
| 8 | import 'package:flutter/cupertino.dart'; |
| 9 | import 'package:flutter/foundation.dart'; |
| 10 | import 'package:intl/intl.dart' as intl; |
| 11 | |
| 12 | import 'l10n/generated_cupertino_localizations.dart'; |
| 13 | import 'utils/date_localizations.dart' as util; |
| 14 | import 'widgets_localizations.dart'; |
| 15 | |
| 16 | // Examples can assume: |
| 17 | // import 'package:flutter_localizations/flutter_localizations.dart'; |
| 18 | // import 'package:flutter/cupertino.dart'; |
| 19 | |
| 20 | /// Implementation of localized strings for Cupertino widgets using the `intl` |
| 21 | /// package for date and time formatting. |
| 22 | /// |
| 23 | /// Further localization of strings beyond date time formatting are provided |
| 24 | /// by language specific subclasses of [GlobalCupertinoLocalizations]. |
| 25 | /// |
| 26 | /// ## Supported languages |
| 27 | /// |
| 28 | /// This class supports locales with the following [Locale.languageCode]s: |
| 29 | /// |
| 30 | /// {@macro flutter.localizations.cupertino.languages} |
| 31 | /// |
| 32 | /// This list is available programmatically via [kCupertinoSupportedLanguages]. |
| 33 | /// |
| 34 | /// ## Sample code |
| 35 | /// |
| 36 | /// To include the localizations provided by this class in a [CupertinoApp], |
| 37 | /// add [GlobalCupertinoLocalizations.delegates] to |
| 38 | /// [CupertinoApp.localizationsDelegates], and specify the locales your |
| 39 | /// app supports with [CupertinoApp.supportedLocales]: |
| 40 | /// |
| 41 | /// ```dart |
| 42 | /// const CupertinoApp( |
| 43 | /// localizationsDelegates: GlobalCupertinoLocalizations.delegates, |
| 44 | /// supportedLocales: <Locale>[ |
| 45 | /// Locale('en', 'US'), // American English |
| 46 | /// Locale('he', 'IL'), // Israeli Hebrew |
| 47 | /// // ... |
| 48 | /// ], |
| 49 | /// // ... |
| 50 | /// ) |
| 51 | /// ``` |
| 52 | /// |
| 53 | /// See also: |
| 54 | /// |
| 55 | /// * [DefaultCupertinoLocalizations], which provides US English localizations |
| 56 | /// for Cupertino widgets. |
| 57 | abstract class GlobalCupertinoLocalizations implements CupertinoLocalizations { |
| 58 | /// Initializes an object that defines the Cupertino widgets' localized |
| 59 | /// strings for the given `localeName`. |
| 60 | /// |
| 61 | /// The remaining '*Format' arguments uses the intl package to provide |
| 62 | /// [DateFormat] configurations for the `localeName`. |
| 63 | const GlobalCupertinoLocalizations({ |
| 64 | required String localeName, |
| 65 | required intl.DateFormat fullYearFormat, |
| 66 | required intl.DateFormat dayFormat, |
| 67 | required intl.DateFormat weekdayFormat, |
| 68 | required intl.DateFormat mediumDateFormat, |
| 69 | required intl.DateFormat singleDigitHourFormat, |
| 70 | required intl.DateFormat singleDigitMinuteFormat, |
| 71 | required intl.DateFormat doubleDigitMinuteFormat, |
| 72 | required intl.DateFormat singleDigitSecondFormat, |
| 73 | required intl.NumberFormat decimalFormat, |
| 74 | }) : _localeName = localeName, |
| 75 | _fullYearFormat = fullYearFormat, |
| 76 | _dayFormat = dayFormat, |
| 77 | _weekdayFormat = weekdayFormat, |
| 78 | _mediumDateFormat = mediumDateFormat, |
| 79 | _singleDigitHourFormat = singleDigitHourFormat, |
| 80 | _singleDigitMinuteFormat = singleDigitMinuteFormat, |
| 81 | _doubleDigitMinuteFormat = doubleDigitMinuteFormat, |
| 82 | _singleDigitSecondFormat = singleDigitSecondFormat, |
| 83 | _decimalFormat = decimalFormat; |
| 84 | |
| 85 | final String _localeName; |
| 86 | final intl.DateFormat _fullYearFormat; |
| 87 | final intl.DateFormat _dayFormat; |
| 88 | final intl.DateFormat _weekdayFormat; |
| 89 | final intl.DateFormat _mediumDateFormat; |
| 90 | final intl.DateFormat _singleDigitHourFormat; |
| 91 | final intl.DateFormat _singleDigitMinuteFormat; |
| 92 | final intl.DateFormat _doubleDigitMinuteFormat; |
| 93 | final intl.DateFormat _singleDigitSecondFormat; |
| 94 | final intl.NumberFormat _decimalFormat; |
| 95 | |
| 96 | @override |
| 97 | String datePickerYear(int yearIndex) { |
| 98 | return _fullYearFormat.format(DateTime.utc(yearIndex)); |
| 99 | } |
| 100 | |
| 101 | @override |
| 102 | String datePickerMonth(int monthIndex) { |
| 103 | // It doesn't actually have anything to do with _fullYearFormat. It's just |
| 104 | // taking advantage of the fact that _fullYearFormat loaded the needed |
| 105 | // locale's symbols. |
| 106 | return _fullYearFormat.dateSymbols.MONTHS[monthIndex - 1]; |
| 107 | } |
| 108 | |
| 109 | @override |
| 110 | String datePickerStandaloneMonth(int monthIndex) { |
| 111 | // It doesn't actually have anything to do with _fullYearFormat. It's just |
| 112 | // taking advantage of the fact that _fullYearFormat loaded the needed |
| 113 | // locale's symbols. |
| 114 | // |
| 115 | // Because this will be used without specifying any day of month, |
| 116 | // in most cases it should be capitalized (according to rules in specific language). |
| 117 | return intl.toBeginningOfSentenceCase( |
| 118 | _fullYearFormat.dateSymbols.STANDALONEMONTHS[monthIndex - 1], |
| 119 | ) ?? |
| 120 | _fullYearFormat.dateSymbols.STANDALONEMONTHS[monthIndex - 1]; |
| 121 | } |
| 122 | |
| 123 | @override |
| 124 | String datePickerDayOfMonth(int dayIndex, [int? weekDay]) { |
| 125 | return weekDay != null |
| 126 | ? ' ${_weekdayFormat.format(DateTime.utc(1, 1, weekDay))} ${_dayFormat.format(DateTime.utc(1, 1, dayIndex))}' |
| 127 | // Year and month doesn't matter since we just want to day formatted. |
| 128 | : _dayFormat.format(DateTime.utc(0, 0, dayIndex)); |
| 129 | } |
| 130 | |
| 131 | @override |
| 132 | String datePickerMediumDate(DateTime date) { |
| 133 | return _mediumDateFormat.format(date); |
| 134 | } |
| 135 | |
| 136 | @override |
| 137 | String datePickerHour(int hour) { |
| 138 | return _singleDigitHourFormat.format(DateTime.utc(0, 0, 0, hour)); |
| 139 | } |
| 140 | |
| 141 | @override |
| 142 | String datePickerMinute(int minute) { |
| 143 | return _doubleDigitMinuteFormat.format(DateTime.utc(0, 0, 0, 0, minute)); |
| 144 | } |
| 145 | |
| 146 | /// Subclasses should provide the optional zero pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 147 | @protected |
| 148 | String? get datePickerHourSemanticsLabelZero => null; |
| 149 | |
| 150 | /// Subclasses should provide the optional one pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 151 | @protected |
| 152 | String? get datePickerHourSemanticsLabelOne => null; |
| 153 | |
| 154 | /// Subclasses should provide the optional two pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 155 | @protected |
| 156 | String? get datePickerHourSemanticsLabelTwo => null; |
| 157 | |
| 158 | /// Subclasses should provide the optional few pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 159 | @protected |
| 160 | String? get datePickerHourSemanticsLabelFew => null; |
| 161 | |
| 162 | /// Subclasses should provide the optional many pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 163 | @protected |
| 164 | String? get datePickerHourSemanticsLabelMany => null; |
| 165 | |
| 166 | /// Subclasses should provide the required other pluralization of [datePickerHourSemanticsLabel] based on the ARB file. |
| 167 | @protected |
| 168 | String? get datePickerHourSemanticsLabelOther; |
| 169 | |
| 170 | @override |
| 171 | String? datePickerHourSemanticsLabel(int hour) { |
| 172 | return intl.Intl.pluralLogic( |
| 173 | hour, |
| 174 | zero: datePickerHourSemanticsLabelZero, |
| 175 | one: datePickerHourSemanticsLabelOne, |
| 176 | two: datePickerHourSemanticsLabelTwo, |
| 177 | few: datePickerHourSemanticsLabelFew, |
| 178 | many: datePickerHourSemanticsLabelMany, |
| 179 | other: datePickerHourSemanticsLabelOther, |
| 180 | locale: _localeName, |
| 181 | )?.replaceFirst(r'$hour' , _decimalFormat.format(hour)); |
| 182 | } |
| 183 | |
| 184 | /// Subclasses should provide the optional zero pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 185 | @protected |
| 186 | String? get datePickerMinuteSemanticsLabelZero => null; |
| 187 | |
| 188 | /// Subclasses should provide the optional one pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 189 | @protected |
| 190 | String? get datePickerMinuteSemanticsLabelOne => null; |
| 191 | |
| 192 | /// Subclasses should provide the optional two pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 193 | @protected |
| 194 | String? get datePickerMinuteSemanticsLabelTwo => null; |
| 195 | |
| 196 | /// Subclasses should provide the optional few pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 197 | @protected |
| 198 | String? get datePickerMinuteSemanticsLabelFew => null; |
| 199 | |
| 200 | /// Subclasses should provide the optional many pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 201 | @protected |
| 202 | String? get datePickerMinuteSemanticsLabelMany => null; |
| 203 | |
| 204 | /// Subclasses should provide the required other pluralization of [datePickerMinuteSemanticsLabel] based on the ARB file. |
| 205 | @protected |
| 206 | String? get datePickerMinuteSemanticsLabelOther; |
| 207 | |
| 208 | @override |
| 209 | String? datePickerMinuteSemanticsLabel(int minute) { |
| 210 | return intl.Intl.pluralLogic( |
| 211 | minute, |
| 212 | zero: datePickerMinuteSemanticsLabelZero, |
| 213 | one: datePickerMinuteSemanticsLabelOne, |
| 214 | two: datePickerMinuteSemanticsLabelTwo, |
| 215 | few: datePickerMinuteSemanticsLabelFew, |
| 216 | many: datePickerMinuteSemanticsLabelMany, |
| 217 | other: datePickerMinuteSemanticsLabelOther, |
| 218 | locale: _localeName, |
| 219 | )?.replaceFirst(r'$minute' , _decimalFormat.format(minute)); |
| 220 | } |
| 221 | |
| 222 | /// A string describing the [DatePickerDateOrder] enum value. |
| 223 | /// |
| 224 | /// Subclasses should provide this string value based on the ARB file for |
| 225 | /// the locale. |
| 226 | /// |
| 227 | /// See also: |
| 228 | /// |
| 229 | /// * [datePickerDateOrder], which provides the [DatePickerDateOrder] |
| 230 | /// enum value for [CupertinoLocalizations] based on this string value |
| 231 | @protected |
| 232 | String get datePickerDateOrderString; |
| 233 | |
| 234 | @override |
| 235 | DatePickerDateOrder get datePickerDateOrder { |
| 236 | switch (datePickerDateOrderString) { |
| 237 | case 'dmy' : |
| 238 | return DatePickerDateOrder.dmy; |
| 239 | case 'mdy' : |
| 240 | return DatePickerDateOrder.mdy; |
| 241 | case 'ymd' : |
| 242 | return DatePickerDateOrder.ymd; |
| 243 | case 'ydm' : |
| 244 | return DatePickerDateOrder.ydm; |
| 245 | default: |
| 246 | assert( |
| 247 | false, |
| 248 | 'Failed to load DatePickerDateOrder $datePickerDateOrderString for ' |
| 249 | "locale $_localeName.\nNon conforming string for $_localeName's " |
| 250 | '.arb file' , |
| 251 | ); |
| 252 | return DatePickerDateOrder.mdy; |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /// A string describing the [DatePickerDateTimeOrder] enum value. |
| 257 | /// |
| 258 | /// Subclasses should provide this string value based on the ARB file for |
| 259 | /// the locale. |
| 260 | /// |
| 261 | /// See also: |
| 262 | /// |
| 263 | /// * [datePickerDateTimeOrder], which provides the [DatePickerDateTimeOrder] |
| 264 | /// enum value for [CupertinoLocalizations] based on this string value. |
| 265 | @protected |
| 266 | String get datePickerDateTimeOrderString; |
| 267 | |
| 268 | @override |
| 269 | DatePickerDateTimeOrder get datePickerDateTimeOrder { |
| 270 | switch (datePickerDateTimeOrderString) { |
| 271 | case 'date_time_dayPeriod' : |
| 272 | return DatePickerDateTimeOrder.date_time_dayPeriod; |
| 273 | case 'date_dayPeriod_time' : |
| 274 | return DatePickerDateTimeOrder.date_dayPeriod_time; |
| 275 | case 'time_dayPeriod_date' : |
| 276 | return DatePickerDateTimeOrder.time_dayPeriod_date; |
| 277 | case 'dayPeriod_time_date' : |
| 278 | return DatePickerDateTimeOrder.dayPeriod_time_date; |
| 279 | default: |
| 280 | assert( |
| 281 | false, |
| 282 | 'Failed to load DatePickerDateTimeOrder $datePickerDateTimeOrderString ' |
| 283 | "for locale $_localeName.\nNon conforming string for $_localeName's " |
| 284 | '.arb file' , |
| 285 | ); |
| 286 | return DatePickerDateTimeOrder.date_time_dayPeriod; |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | /// The raw version of [tabSemanticsLabel], with `$tabIndex` and `$tabCount` verbatim |
| 291 | /// in the string. |
| 292 | @protected |
| 293 | String get tabSemanticsLabelRaw; |
| 294 | |
| 295 | @override |
| 296 | String tabSemanticsLabel({required int tabIndex, required int tabCount}) { |
| 297 | assert(tabIndex >= 1); |
| 298 | assert(tabCount >= 1); |
| 299 | final String template = tabSemanticsLabelRaw; |
| 300 | return template |
| 301 | .replaceFirst(r'$tabIndex' , _decimalFormat.format(tabIndex)) |
| 302 | .replaceFirst(r'$tabCount' , _decimalFormat.format(tabCount)); |
| 303 | } |
| 304 | |
| 305 | @override |
| 306 | String timerPickerHour(int hour) { |
| 307 | return _singleDigitHourFormat.format(DateTime.utc(0, 0, 0, hour)); |
| 308 | } |
| 309 | |
| 310 | @override |
| 311 | String timerPickerMinute(int minute) { |
| 312 | return _singleDigitMinuteFormat.format(DateTime.utc(0, 0, 0, 0, minute)); |
| 313 | } |
| 314 | |
| 315 | @override |
| 316 | String timerPickerSecond(int second) { |
| 317 | return _singleDigitSecondFormat.format(DateTime.utc(0, 0, 0, 0, 0, second)); |
| 318 | } |
| 319 | |
| 320 | /// Subclasses should provide the optional zero pluralization of [timerPickerHourLabel] based on the ARB file. |
| 321 | @protected |
| 322 | String? get timerPickerHourLabelZero => null; |
| 323 | |
| 324 | /// Subclasses should provide the optional one pluralization of [timerPickerHourLabel] based on the ARB file. |
| 325 | @protected |
| 326 | String? get timerPickerHourLabelOne => null; |
| 327 | |
| 328 | /// Subclasses should provide the optional two pluralization of [timerPickerHourLabel] based on the ARB file. |
| 329 | @protected |
| 330 | String? get timerPickerHourLabelTwo => null; |
| 331 | |
| 332 | /// Subclasses should provide the optional few pluralization of [timerPickerHourLabel] based on the ARB file. |
| 333 | @protected |
| 334 | String? get timerPickerHourLabelFew => null; |
| 335 | |
| 336 | /// Subclasses should provide the optional many pluralization of [timerPickerHourLabel] based on the ARB file. |
| 337 | @protected |
| 338 | String? get timerPickerHourLabelMany => null; |
| 339 | |
| 340 | /// Subclasses should provide the required other pluralization of [timerPickerHourLabel] based on the ARB file. |
| 341 | @protected |
| 342 | String? get timerPickerHourLabelOther; |
| 343 | |
| 344 | @override |
| 345 | String? timerPickerHourLabel(int hour) { |
| 346 | return intl.Intl.pluralLogic( |
| 347 | hour, |
| 348 | zero: timerPickerHourLabelZero, |
| 349 | one: timerPickerHourLabelOne, |
| 350 | two: timerPickerHourLabelTwo, |
| 351 | few: timerPickerHourLabelFew, |
| 352 | many: timerPickerHourLabelMany, |
| 353 | other: timerPickerHourLabelOther, |
| 354 | locale: _localeName, |
| 355 | )?.replaceFirst(r'$hour' , _decimalFormat.format(hour)); |
| 356 | } |
| 357 | |
| 358 | @override |
| 359 | List<String> get timerPickerHourLabels => <String>[ |
| 360 | if (timerPickerHourLabelZero != null) timerPickerHourLabelZero!, |
| 361 | if (timerPickerHourLabelOne != null) timerPickerHourLabelOne!, |
| 362 | if (timerPickerHourLabelTwo != null) timerPickerHourLabelTwo!, |
| 363 | if (timerPickerHourLabelFew != null) timerPickerHourLabelFew!, |
| 364 | if (timerPickerHourLabelMany != null) timerPickerHourLabelMany!, |
| 365 | if (timerPickerHourLabelOther != null) timerPickerHourLabelOther!, |
| 366 | ]; |
| 367 | |
| 368 | /// Subclasses should provide the optional zero pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 369 | @protected |
| 370 | String? get timerPickerMinuteLabelZero => null; |
| 371 | |
| 372 | /// Subclasses should provide the optional one pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 373 | @protected |
| 374 | String? get timerPickerMinuteLabelOne => null; |
| 375 | |
| 376 | /// Subclasses should provide the optional two pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 377 | @protected |
| 378 | String? get timerPickerMinuteLabelTwo => null; |
| 379 | |
| 380 | /// Subclasses should provide the optional few pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 381 | @protected |
| 382 | String? get timerPickerMinuteLabelFew => null; |
| 383 | |
| 384 | /// Subclasses should provide the optional many pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 385 | @protected |
| 386 | String? get timerPickerMinuteLabelMany => null; |
| 387 | |
| 388 | /// Subclasses should provide the required other pluralization of [timerPickerMinuteLabel] based on the ARB file. |
| 389 | @protected |
| 390 | String? get timerPickerMinuteLabelOther; |
| 391 | |
| 392 | @override |
| 393 | String? timerPickerMinuteLabel(int minute) { |
| 394 | return intl.Intl.pluralLogic( |
| 395 | minute, |
| 396 | zero: timerPickerMinuteLabelZero, |
| 397 | one: timerPickerMinuteLabelOne, |
| 398 | two: timerPickerMinuteLabelTwo, |
| 399 | few: timerPickerMinuteLabelFew, |
| 400 | many: timerPickerMinuteLabelMany, |
| 401 | other: timerPickerMinuteLabelOther, |
| 402 | locale: _localeName, |
| 403 | )?.replaceFirst(r'$minute' , _decimalFormat.format(minute)); |
| 404 | } |
| 405 | |
| 406 | @override |
| 407 | List<String> get timerPickerMinuteLabels => <String>[ |
| 408 | if (timerPickerMinuteLabelZero != null) timerPickerMinuteLabelZero!, |
| 409 | if (timerPickerMinuteLabelOne != null) timerPickerMinuteLabelOne!, |
| 410 | if (timerPickerMinuteLabelTwo != null) timerPickerMinuteLabelTwo!, |
| 411 | if (timerPickerMinuteLabelFew != null) timerPickerMinuteLabelFew!, |
| 412 | if (timerPickerMinuteLabelMany != null) timerPickerMinuteLabelMany!, |
| 413 | if (timerPickerMinuteLabelOther != null) timerPickerMinuteLabelOther!, |
| 414 | ]; |
| 415 | |
| 416 | /// Subclasses should provide the optional zero pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 417 | @protected |
| 418 | String? get timerPickerSecondLabelZero => null; |
| 419 | |
| 420 | /// Subclasses should provide the optional one pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 421 | @protected |
| 422 | String? get timerPickerSecondLabelOne => null; |
| 423 | |
| 424 | /// Subclasses should provide the optional two pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 425 | @protected |
| 426 | String? get timerPickerSecondLabelTwo => null; |
| 427 | |
| 428 | /// Subclasses should provide the optional few pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 429 | @protected |
| 430 | String? get timerPickerSecondLabelFew => null; |
| 431 | |
| 432 | /// Subclasses should provide the optional many pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 433 | @protected |
| 434 | String? get timerPickerSecondLabelMany => null; |
| 435 | |
| 436 | /// Subclasses should provide the required other pluralization of [timerPickerSecondLabel] based on the ARB file. |
| 437 | @protected |
| 438 | String? get timerPickerSecondLabelOther; |
| 439 | |
| 440 | @override |
| 441 | String? timerPickerSecondLabel(int second) { |
| 442 | return intl.Intl.pluralLogic( |
| 443 | second, |
| 444 | zero: timerPickerSecondLabelZero, |
| 445 | one: timerPickerSecondLabelOne, |
| 446 | two: timerPickerSecondLabelTwo, |
| 447 | few: timerPickerSecondLabelFew, |
| 448 | many: timerPickerSecondLabelMany, |
| 449 | other: timerPickerSecondLabelOther, |
| 450 | locale: _localeName, |
| 451 | )?.replaceFirst(r'$second' , _decimalFormat.format(second)); |
| 452 | } |
| 453 | |
| 454 | @override |
| 455 | List<String> get timerPickerSecondLabels => <String>[ |
| 456 | if (timerPickerSecondLabelZero != null) timerPickerSecondLabelZero!, |
| 457 | if (timerPickerSecondLabelOne != null) timerPickerSecondLabelOne!, |
| 458 | if (timerPickerSecondLabelTwo != null) timerPickerSecondLabelTwo!, |
| 459 | if (timerPickerSecondLabelFew != null) timerPickerSecondLabelFew!, |
| 460 | if (timerPickerSecondLabelMany != null) timerPickerSecondLabelMany!, |
| 461 | if (timerPickerSecondLabelOther != null) timerPickerSecondLabelOther!, |
| 462 | ]; |
| 463 | |
| 464 | /// A [LocalizationsDelegate] for [CupertinoLocalizations]. |
| 465 | /// |
| 466 | /// Most internationalized apps will use [GlobalCupertinoLocalizations.delegates] |
| 467 | /// as the value of [CupertinoApp.localizationsDelegates] to include |
| 468 | /// the localizations for both the cupertino and widget libraries. |
| 469 | static const LocalizationsDelegate<CupertinoLocalizations> delegate = |
| 470 | _GlobalCupertinoLocalizationsDelegate(); |
| 471 | |
| 472 | /// A value for [CupertinoApp.localizationsDelegates] that's typically used by |
| 473 | /// internationalized apps. |
| 474 | /// |
| 475 | /// ## Sample code |
| 476 | /// |
| 477 | /// To include the localizations provided by this class and by |
| 478 | /// [GlobalWidgetsLocalizations] in a [CupertinoApp], |
| 479 | /// use [GlobalCupertinoLocalizations.delegates] as the value of |
| 480 | /// [CupertinoApp.localizationsDelegates], and specify the locales your |
| 481 | /// app supports with [CupertinoApp.supportedLocales]: |
| 482 | /// |
| 483 | /// ```dart |
| 484 | /// const CupertinoApp( |
| 485 | /// localizationsDelegates: GlobalCupertinoLocalizations.delegates, |
| 486 | /// supportedLocales: <Locale>[ |
| 487 | /// Locale('en', 'US'), // English |
| 488 | /// Locale('he', 'IL'), // Hebrew |
| 489 | /// ], |
| 490 | /// // ... |
| 491 | /// ) |
| 492 | /// ``` |
| 493 | static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[ |
| 494 | GlobalCupertinoLocalizations.delegate, |
| 495 | GlobalWidgetsLocalizations.delegate, |
| 496 | ]; |
| 497 | } |
| 498 | |
| 499 | class _GlobalCupertinoLocalizationsDelegate extends LocalizationsDelegate<CupertinoLocalizations> { |
| 500 | const _GlobalCupertinoLocalizationsDelegate(); |
| 501 | |
| 502 | @override |
| 503 | bool isSupported(Locale locale) => kCupertinoSupportedLanguages.contains(locale.languageCode); |
| 504 | |
| 505 | static final Map<Locale, Future<CupertinoLocalizations>> _loadedTranslations = |
| 506 | <Locale, Future<CupertinoLocalizations>>{}; |
| 507 | |
| 508 | @override |
| 509 | Future<CupertinoLocalizations> load(Locale locale) { |
| 510 | assert(isSupported(locale)); |
| 511 | return _loadedTranslations.putIfAbsent(locale, () { |
| 512 | util.loadDateIntlDataIfNotLoaded(); |
| 513 | |
| 514 | final String localeName = intl.Intl.canonicalizedLocale(locale.toString()); |
| 515 | assert( |
| 516 | locale.toString() == localeName, |
| 517 | 'Flutter does not support the non-standard locale form $locale (which ' |
| 518 | 'might be $localeName' , |
| 519 | ); |
| 520 | |
| 521 | late intl.DateFormat fullYearFormat; |
| 522 | late intl.DateFormat dayFormat; |
| 523 | late intl.DateFormat weekdayFormat; |
| 524 | late intl.DateFormat mediumDateFormat; |
| 525 | // We don't want any additional decoration here. The am/pm is handled in |
| 526 | // the date picker. We just want an hour number localized. |
| 527 | late intl.DateFormat singleDigitHourFormat; |
| 528 | late intl.DateFormat singleDigitMinuteFormat; |
| 529 | late intl.DateFormat doubleDigitMinuteFormat; |
| 530 | late intl.DateFormat singleDigitSecondFormat; |
| 531 | late intl.NumberFormat decimalFormat; |
| 532 | |
| 533 | void loadFormats(String? locale) { |
| 534 | fullYearFormat = intl.DateFormat.y(locale); |
| 535 | dayFormat = intl.DateFormat.d(locale); |
| 536 | weekdayFormat = intl.DateFormat.E(locale); |
| 537 | mediumDateFormat = intl.DateFormat.MMMEd(locale); |
| 538 | // TODO(xster): fix when https://github.com/dart-lang/intl/issues/207 is resolved. |
| 539 | singleDigitHourFormat = intl.DateFormat('HH' , locale); |
| 540 | singleDigitMinuteFormat = intl.DateFormat.m(locale); |
| 541 | doubleDigitMinuteFormat = intl.DateFormat('mm' , locale); |
| 542 | singleDigitSecondFormat = intl.DateFormat.s(locale); |
| 543 | decimalFormat = intl.NumberFormat.decimalPattern(locale); |
| 544 | } |
| 545 | |
| 546 | if (intl.DateFormat.localeExists(localeName)) { |
| 547 | loadFormats(localeName); |
| 548 | } else if (intl.DateFormat.localeExists(locale.languageCode)) { |
| 549 | loadFormats(locale.languageCode); |
| 550 | } else { |
| 551 | loadFormats(null); |
| 552 | } |
| 553 | |
| 554 | return SynchronousFuture<CupertinoLocalizations>( |
| 555 | getCupertinoTranslation( |
| 556 | locale, |
| 557 | fullYearFormat, |
| 558 | dayFormat, |
| 559 | weekdayFormat, |
| 560 | mediumDateFormat, |
| 561 | singleDigitHourFormat, |
| 562 | singleDigitMinuteFormat, |
| 563 | doubleDigitMinuteFormat, |
| 564 | singleDigitSecondFormat, |
| 565 | decimalFormat, |
| 566 | )!, |
| 567 | ); |
| 568 | }); |
| 569 | } |
| 570 | |
| 571 | @override |
| 572 | bool shouldReload(_GlobalCupertinoLocalizationsDelegate old) => false; |
| 573 | |
| 574 | @override |
| 575 | String toString() => |
| 576 | 'GlobalCupertinoLocalizations.delegate( ${kCupertinoSupportedLanguages.length} locales)' ; |
| 577 | } |
| 578 | |