Cupertino localization step 8: create a gen_cupertino_localizations and generate one for cupertino english and french (#29824)

diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart
index 4456bdc..ffa96e2 100644
--- a/dev/bots/analyze.dart
+++ b/dev/bots/analyze.dart
@@ -91,30 +91,52 @@
 }
 
 Future<void> _verifyInternationalizations() async {
-  final EvalResult genResult = await _evalCommand(
+  final EvalResult materialGenResult = await _evalCommand(
     dart,
     <String>[
       path.join('dev', 'tools', 'localization', 'gen_localizations.dart'),
+      '--material',
+    ],
+    workingDirectory: flutterRoot,
+  );
+  final EvalResult cupertinoGenResult = await _evalCommand(
+    dart,
+    <String>[
+      path.join('dev', 'tools', 'localization', 'gen_localizations.dart'),
+      '--cupertino',
     ],
     workingDirectory: flutterRoot,
   );
 
-  final String localizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_material_localizations.dart');
-  final String expectedResult = await File(localizationsFile).readAsString();
+  final String materialLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_material_localizations.dart');
+  final String cupertinoLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_cupertino_localizations.dart');
+  final String expectedMaterialResult = await File(materialLocalizationsFile).readAsString();
+  final String expectedCupertinoResult = await File(cupertinoLocalizationsFile).readAsString();
 
-  if (genResult.stdout.trim() != expectedResult.trim()) {
+  if (materialGenResult.stdout.trim() != expectedMaterialResult.trim()) {
     stderr
-      ..writeln('<<<<<<< $localizationsFile')
-      ..writeln(expectedResult.trim())
+      ..writeln('<<<<<<< $materialLocalizationsFile')
+      ..writeln(expectedMaterialResult.trim())
       ..writeln('=======')
-      ..writeln(genResult.stdout.trim())
+      ..writeln(materialGenResult.stdout.trim())
       ..writeln('>>>>>>> gen_localizations')
-      ..writeln('The contents of $localizationsFile are different from that produced by gen_localizations.')
+      ..writeln('The contents of $materialLocalizationsFile are different from that produced by gen_localizations.')
       ..writeln()
       ..writeln('Did you forget to run gen_localizations.dart after updating a .arb file?');
     exit(1);
   }
-  print('Contents of $localizationsFile matches output of gen_localizations.dart script.');
+  if (cupertinoGenResult.stdout.trim() != expectedCupertinoResult.trim()) {
+    stderr
+      ..writeln('<<<<<<< $cupertinoLocalizationsFile')
+      ..writeln(expectedCupertinoResult.trim())
+      ..writeln('=======')
+      ..writeln(cupertinoGenResult.stdout.trim())
+      ..writeln('>>>>>>> gen_localizations')
+      ..writeln('The contents of $cupertinoLocalizationsFile are different from that produced by gen_localizations.')
+      ..writeln()
+      ..writeln('Did you forget to run gen_localizations.dart after updating a .arb file?');
+    exit(1);
+  }
 }
 
 Future<String> _getCommitRange() async {
diff --git a/dev/tools/localization/gen_cupertino_localizations.dart b/dev/tools/localization/gen_cupertino_localizations.dart
new file mode 100644
index 0000000..9ad7e10
--- /dev/null
+++ b/dev/tools/localization/gen_cupertino_localizations.dart
@@ -0,0 +1,80 @@
+import 'localizations_utils.dart';
+
+HeaderGenerator generateCupertinoHeader = (String regenerateInstructions) {
+  return '''
+// 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.
+
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use:
+// $regenerateInstructions
+
+import 'dart:collection';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:intl/intl.dart' as intl;
+
+import '../cupertino_localizations.dart';
+
+// The classes defined here encode all of the translations found in the
+// `flutter_localizations/lib/src/l10n/*.arb` files.
+//
+// These classes are constructed by the [getCupertinoTranslation] method at the
+// bottom of this file, and used by the [_GlobalCupertinoLocalizationsDelegate.load]
+// method defined in `flutter_localizations/lib/src/cupertino_localizations.dart`.''';
+};
+
+/// Returns the source of the constructor for a GlobalCupertinoLocalizations
+/// subclass.
+ConstructorGenerator generateCupertinoConstructor = (LocaleInfo locale) {
+  final String localeName = locale.originalString;
+  return '''
+  /// Create an instance of the translation bundle for ${describeLocale(localeName)}.
+  ///
+  /// For details on the meaning of the arguments, see [GlobalCupertinoLocalizations].
+  const CupertinoLocalization${camelCase(locale)}({
+    String localeName = '$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,
+  }) : super(
+    localeName: localeName,
+    fullYearFormat: fullYearFormat,
+    dayFormat: dayFormat,
+    mediumDateFormat: mediumDateFormat,
+    singleDigitHourFormat: singleDigitHourFormat,
+    singleDigitMinuteFormat: singleDigitMinuteFormat,
+    doubleDigitMinuteFormat: doubleDigitMinuteFormat,
+    singleDigitSecondFormat: singleDigitSecondFormat,
+    decimalFormat: decimalFormat,
+  );''';
+};
+
+const String cupertinoFactoryName = 'getCupertinoTranslation';
+
+const String cupertinoFactoryDeclaration = '''
+GlobalCupertinoLocalizations getCupertinoTranslation(
+  Locale locale,
+  intl.DateFormat fullYearFormat,
+  intl.DateFormat dayFormat,
+  intl.DateFormat mediumDateFormat,
+  intl.DateFormat singleDigitHourFormat,
+  intl.DateFormat singleDigitMinuteFormat,
+  intl.DateFormat doubleDigitMinuteFormat,
+  intl.DateFormat singleDigitSecondFormat,
+  intl.NumberFormat decimalFormat,
+) {''';
+
+const String cupertinoFactoryArguments =
+    'fullYearFormat: fullYearFormat, dayFormat: dayFormat, mediumDateFormat: mediumDateFormat, singleDigitHourFormat: singleDigitHourFormat, singleDigitMinuteFormat: singleDigitMinuteFormat, doubleDigitMinuteFormat: doubleDigitMinuteFormat, singleDigitSecondFormat: singleDigitSecondFormat, decimalFormat: decimalFormat';
+
+const String cupertinoSupportedLanguagesConstant = 'kCupertinoSupportedLanguages';
+
+const String cupertinoSupportedLanguagesDocMacro = 'flutter.localizations.cupertino.languages';
diff --git a/dev/tools/localization/gen_localizations.dart b/dev/tools/localization/gen_localizations.dart
index 205b5f7..bbc9170 100644
--- a/dev/tools/localization/gen_localizations.dart
+++ b/dev/tools/localization/gen_localizations.dart
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This program generates a getMaterialTranslation() function that looks up the
-// translations provided by the arb files. The returned value is a generated
-// instance of GlobalMaterialLocalizations that corresponds to a single
-// locale.
+// This program generates a getMaterialTranslation() and a
+// getCupertinoTranslation() function that look up the translations provided by
+// the arb files. The returned value is a generated instance of a
+// GlobalMaterialLocalizations or a GlobalCupertinoLocalizations that
+// corresponds to a single locale.
 //
 // The *.arb files are in packages/flutter_localizations/lib/src/l10n.
 //
@@ -13,10 +14,10 @@
 // Each map value is itself a map with resource identifier keys and localized
 // resource string values.
 //
-// The arb filenames are expected to have the form "material_(\w+)\.arb", where
-// the group following "_" identifies the language code and the country code,
-// e.g. "material_en.arb" or "material_en_GB.arb". In most cases both codes are
-// just two characters.
+// The arb filenames are expected to have the form "material_(\w+)\.arb" or
+// "cupertino_(\w+)\.arb" where the group following "_" identifies the language
+// code and the country code, e.g. "material_en.arb" or "material_en_GB.arb".
+// In most cases both codes are just two characters.
 //
 // This app is typically run by hand when a module's .arb files have been
 // updated.
@@ -32,7 +33,8 @@
 // ```
 //
 // If the data looks good, use the `-w` or `--overwrite` option to overwrite the
-// packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart file:
+// packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
+// and packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart file:
 //
 // ```
 // dart dev/tools/localization/gen_localizations.dart --overwrite
@@ -44,6 +46,7 @@
 import 'package:path/path.dart' as path;
 import 'package:meta/meta.dart';
 
+import 'gen_cupertino_localizations.dart';
 import 'gen_material_localizations.dart';
 import 'localizations_utils.dart';
 import 'localizations_validator.dart';
@@ -56,8 +59,11 @@
   @required String baseClass,
   @required HeaderGenerator generateHeader,
   @required ConstructorGenerator generateConstructor,
+  @required String factoryName,
   @required String factoryDeclaration,
   @required String factoryArguments,
+  @required String supportedLanguagesConstant,
+  @required String supportedLanguagesDocMacro,
 }) {
   assert(localeToResources != null);
   assert(localeToResourceAttributes != null);
@@ -65,8 +71,11 @@
   assert(baseClass.isNotEmpty);
   assert(generateHeader != null);
   assert(generateConstructor != null);
+  assert(factoryName.isNotEmpty);
   assert(factoryDeclaration.isNotEmpty);
   assert(factoryArguments.isNotEmpty);
+  assert(supportedLanguagesConstant.isNotEmpty);
+  assert(supportedLanguagesDocMacro.isNotEmpty);
 
   final StringBuffer output = StringBuffer();
   output.writeln(generateHeader('dart dev/tools/localization/gen_localizations.dart --overwrite'));
@@ -232,9 +241,9 @@
 ///
 /// See also:
 ///
-///  * [getMaterialTranslation], whose documentation describes these values.
-final Set<String> kSupportedLanguages = HashSet<String>.from(const <String>[
-${languageCodes.map<String>((String value) => "  '$value', // ${describeLocale(value)}").join('\n')}
+///  * [$factoryName], whose documentation describes these values.
+final Set<String> $supportedLanguagesConstant = HashSet<String>.from(const <String>[
+${languageCodes.map<String>((String value) => "  '$value', // ${describeLocale(value)}").toList().join('\n')}
 ]);
 
 /// Creates a [$baseClass] instance for the given `locale`.
@@ -246,7 +255,7 @@
 ///
 /// The following locales are supported by this package:
 ///
-/// {@template flutter.localizations.languages}
+/// {@template $supportedLanguagesDocMacro}
 $supportedLocales/// {@endtemplate}
 ///
 /// Generally speaking, this method is only intended to be used by
@@ -357,7 +366,7 @@
   }
   output.writeln('''
   }
-  assert(false, 'getMaterialTranslation() called for unsupported locale "\$locale"');
+  assert(false, '$factoryName() called for unsupported locale "\$locale"');
   return null;
 }''');
 
@@ -397,6 +406,10 @@
         return '${key}Raw';
     }
   }
+  if (key == 'datePickerDateOrder')
+    return 'datePickerDateOrderString';
+  if (key == 'datePickerDateTimeOrder')
+    return 'datePickerDateTimeOrderString';
   return key;
 }
 
@@ -518,21 +531,48 @@
     exitWithError('$exception');
   }
 
-  final String materialLocalizations = generateArbBasedLocalizationSubclasses(
-    localeToResources: materialLocaleToResources,
-    localeToResourceAttributes: materialLocaleToResourceAttributes,
-    generatedClassPrefix: 'MaterialLocalization',
-    baseClass: 'GlobalMaterialLocalizations',
-    generateHeader: generateMaterialHeader,
-    generateConstructor: generateMaterialConstructor,
-    factoryDeclaration: materialFactoryDeclaration,
-    factoryArguments: materialFactoryArguments,
-  );
+  final String materialLocalizations = options.writeToFile || !options.cupertinoOnly
+      ? generateArbBasedLocalizationSubclasses(
+        localeToResources: materialLocaleToResources,
+        localeToResourceAttributes: materialLocaleToResourceAttributes,
+        generatedClassPrefix: 'MaterialLocalization',
+        baseClass: 'GlobalMaterialLocalizations',
+        generateHeader: generateMaterialHeader,
+        generateConstructor: generateMaterialConstructor,
+        factoryName: materialFactoryName,
+        factoryDeclaration: materialFactoryDeclaration,
+        factoryArguments: materialFactoryArguments,
+        supportedLanguagesConstant: materialSupportedLanguagesConstant,
+        supportedLanguagesDocMacro: materialSupportedLanguagesDocMacro,
+      )
+      : null;
+  final String cupertinoLocalizations = options.writeToFile || !options.materialOnly
+      ? generateArbBasedLocalizationSubclasses(
+        localeToResources: cupertinoLocaleToResources,
+        localeToResourceAttributes: cupertinoLocaleToResourceAttributes,
+        generatedClassPrefix: 'CupertinoLocalization',
+        baseClass: 'GlobalCupertinoLocalizations',
+        generateHeader: generateCupertinoHeader,
+        generateConstructor: generateCupertinoConstructor,
+        factoryName: cupertinoFactoryName,
+        factoryDeclaration: cupertinoFactoryDeclaration,
+        factoryArguments: cupertinoFactoryArguments,
+        supportedLanguagesConstant: cupertinoSupportedLanguagesConstant,
+        supportedLanguagesDocMacro: cupertinoSupportedLanguagesDocMacro,
+      )
+      : null;
 
   if (options.writeToFile) {
-    final File localizationsFile = File(path.join(directory.path, 'generated_material_localizations.dart'));
-    localizationsFile.writeAsStringSync(materialLocalizations, flush: true);
+    final File materialLocalizationsFile = File(path.join(directory.path, 'generated_material_localizations.dart'));
+    materialLocalizationsFile.writeAsStringSync(materialLocalizations, flush: true);
+    final File cupertinoLocalizationsFile = File(path.join(directory.path, 'generated_cupertino_localizations.dart'));
+    cupertinoLocalizationsFile.writeAsStringSync(cupertinoLocalizations, flush: true);
   } else {
-    stdout.write(materialLocalizations);
+    if (!options.cupertinoOnly) {
+      stdout.write(materialLocalizations);
+    }
+    if (!options.materialOnly) {
+      stdout.write(cupertinoLocalizations);
+    }
   }
 }
diff --git a/dev/tools/localization/gen_material_localizations.dart b/dev/tools/localization/gen_material_localizations.dart
index 54a79f2..1909306 100644
--- a/dev/tools/localization/gen_material_localizations.dart
+++ b/dev/tools/localization/gen_material_localizations.dart
@@ -53,6 +53,8 @@
   );''';
 };
 
+const String materialFactoryName = 'getMaterialTranslation';
+
 const String materialFactoryDeclaration = '''
 GlobalMaterialLocalizations getMaterialTranslation(
   Locale locale,
@@ -66,3 +68,7 @@
 
 const String materialFactoryArguments =
     'fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat';
+
+const String materialSupportedLanguagesConstant = 'kMaterialSupportedLanguages';
+
+const String materialSupportedLanguagesDocMacro = 'flutter.localizations.material.languages';
diff --git a/dev/tools/localization/localizations_utils.dart b/dev/tools/localization/localizations_utils.dart
index 4d85bd5..3fc4bc8 100644
--- a/dev/tools/localization/localizations_utils.dart
+++ b/dev/tools/localization/localizations_utils.dart
@@ -231,19 +231,35 @@
       'overwrite',
       abbr: 'w',
       defaultsTo: false,
+    )
+    ..addFlag(
+      'material',
+      help: 'Whether to print the generated classes for the Material package only. Ignored when --overwrite is passed.',
+      defaultsTo: false,
+    )
+    ..addFlag(
+      'cupertino',
+      help: 'Whether to print the generated classes for the Cupertino package only. Ignored when --overwrite is passed.',
+      defaultsTo: false,
     );
   final argslib.ArgResults args = argParser.parse(rawArgs);
   final bool writeToFile = args['overwrite'];
+  final bool materialOnly = args['material'];
+  final bool cupertinoOnly = args['cupertino'];
 
-  return GeneratorOptions(writeToFile: writeToFile);
+  return GeneratorOptions(writeToFile: writeToFile, materialOnly: materialOnly, cupertinoOnly: cupertinoOnly);
 }
 
 class GeneratorOptions {
   GeneratorOptions({
     @required this.writeToFile,
+    @required this.materialOnly,
+    @required this.cupertinoOnly,
   });
 
   final bool writeToFile;
+  final bool materialOnly;
+  final bool cupertinoOnly;
 }
 
 const String registry = 'https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry';
diff --git a/packages/flutter_localizations/lib/src/cupertino_localizations.dart b/packages/flutter_localizations/lib/src/cupertino_localizations.dart
index 557a934..eb31edd 100644
--- a/packages/flutter_localizations/lib/src/cupertino_localizations.dart
+++ b/packages/flutter_localizations/lib/src/cupertino_localizations.dart
@@ -9,6 +9,7 @@
 import 'package:intl/intl.dart' as intl;
 import 'package:intl/date_symbols.dart' as intl;
 
+import 'l10n/generated_cupertino_localizations.dart';
 import 'utils/date_localizations.dart' as util;
 import 'widgets_localizations.dart';
 
@@ -16,7 +17,34 @@
 /// package for date and time formatting.
 ///
 /// Further localization of strings beyond date time formatting are provided
-/// by language specific subclasses of [GlobalCupertinoLocalizations]
+/// by language specific subclasses of [GlobalCupertinoLocalizations].
+///
+/// ## Supported languages
+///
+/// This class supports locales with the following [Locale.languageCode]s:
+///
+/// {@macro flutter.localizations.cupertino.languages}
+///
+/// This list is available programatically via [kCupertinoSupportedLanguages].
+///
+/// ## Sample code
+///
+/// To include the localizations provided by this class in a [CupertinoApp],
+/// add [GlobalCupertinoLocalizations.delegates] to
+/// [CupertinoApp.localizationsDelegates], and specify the locales your
+/// app supports with [CupertinoApp.supportedLocales]:
+///
+/// ```dart
+/// new CupertinoApp(
+///   localizationsDelegates: GlobalCupertinoLocalizations.delegates,
+///   supportedLocales: [
+///     const Locale('en', 'US'), // American English
+///     const Locale('he', 'IL'), // Israeli Hebrew
+///     // ...
+///   ],
+///   // ...
+/// )
+/// ```
 ///
 /// See also:
 ///
@@ -358,13 +386,13 @@
   const _GlobalCupertinoLocalizationsDelegate();
 
   @override
-  bool isSupported(Locale locale) => false; // TODO(xster): implement.
+  bool isSupported(Locale locale) => kCupertinoSupportedLanguages.contains(locale.languageCode);
 
   static final Map<Locale, Future<CupertinoLocalizations>> _loadedTranslations = <Locale, Future<CupertinoLocalizations>>{};
 
   @override
   Future<CupertinoLocalizations> load(Locale locale) {
-    assert(isSupported(locale)); // TODO(xster): implement.
+    assert(isSupported(locale));
     return _loadedTranslations.putIfAbsent(locale, () {
       util.loadDateIntlDataIfNotLoaded();
 
@@ -406,8 +434,8 @@
         loadFormats(null);
       }
 
-      return SynchronousFuture<CupertinoLocalizations>(_getCupertinoTranslation(
-        localeName,
+      return SynchronousFuture<CupertinoLocalizations>(getCupertinoTranslation(
+        locale,
         fullYearFormat,
         dayFormat,
         mediumDateFormat,
@@ -422,18 +450,7 @@
 
   @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.
+  @override
+  String toString() => 'GlobalCupertinoLocalizations.delegate(${kCupertinoSupportedLanguages.length} locales)';
 }
diff --git a/packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart b/packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart
new file mode 100644
index 0000000..0114931
--- /dev/null
+++ b/packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart
@@ -0,0 +1,245 @@
+// 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.
+
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use:
+// dart dev/tools/localization/gen_localizations.dart --overwrite
+
+import 'dart:collection';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:intl/intl.dart' as intl;
+
+import '../cupertino_localizations.dart';
+
+// The classes defined here encode all of the translations found in the
+// `flutter_localizations/lib/src/l10n/*.arb` files.
+//
+// These classes are constructed by the [getCupertinoTranslation] method at the
+// bottom of this file, and used by the [_GlobalCupertinoLocalizationsDelegate.load]
+// method defined in `flutter_localizations/lib/src/cupertino_localizations.dart`.
+
+/// The translations for English (`en`).
+class CupertinoLocalizationEn extends GlobalCupertinoLocalizations {
+  /// Create an instance of the translation bundle for English.
+  ///
+  /// For details on the meaning of the arguments, see [GlobalCupertinoLocalizations].
+  const CupertinoLocalizationEn({
+    String localeName = 'en',
+    @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,
+  }) : super(
+    localeName: localeName,
+    fullYearFormat: fullYearFormat,
+    dayFormat: dayFormat,
+    mediumDateFormat: mediumDateFormat,
+    singleDigitHourFormat: singleDigitHourFormat,
+    singleDigitMinuteFormat: singleDigitMinuteFormat,
+    doubleDigitMinuteFormat: doubleDigitMinuteFormat,
+    singleDigitSecondFormat: singleDigitSecondFormat,
+    decimalFormat: decimalFormat,
+  );
+
+  @override
+  String get alertDialogLabel => r'Alert';
+
+  @override
+  String get anteMeridiemAbbreviation => r'AM';
+
+  @override
+  String get copyButtonLabel => r'Copy';
+
+  @override
+  String get cutButtonLabel => r'Cut';
+
+  @override
+  String get datePickerDateOrderString => r'mdy';
+
+  @override
+  String get datePickerDateTimeOrderString => r'date_time_dayPeriod';
+
+  @override
+  String get datePickerHourSemanticsLabelOne => r'$hour o' "'" r'clock';
+
+  @override
+  String get datePickerHourSemanticsLabelOther => r'$hour o' "'" r'clock';
+
+  @override
+  String get datePickerMinuteSemanticsLabelOne => r'1 minute';
+
+  @override
+  String get datePickerMinuteSemanticsLabelOther => r'$minute minutes';
+
+  @override
+  String get pasteButtonLabel => r'Paste';
+
+  @override
+  String get postMeridiemAbbreviation => r'PM';
+
+  @override
+  String get selectAllButtonLabel => r'Select All';
+
+  @override
+  String get timerPickerHourLabelOne => r'hour';
+
+  @override
+  String get timerPickerHourLabelOther => r'hours';
+
+  @override
+  String get timerPickerMinuteLabelOne => r'min';
+
+  @override
+  String get timerPickerMinuteLabelOther => r'min';
+
+  @override
+  String get timerPickerSecondLabelOne => r'sec';
+
+  @override
+  String get timerPickerSecondLabelOther => r'sec';
+}
+
+/// The translations for French (`fr`).
+class CupertinoLocalizationFr extends GlobalCupertinoLocalizations {
+  /// Create an instance of the translation bundle for French.
+  ///
+  /// For details on the meaning of the arguments, see [GlobalCupertinoLocalizations].
+  const CupertinoLocalizationFr({
+    String localeName = 'fr',
+    @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,
+  }) : super(
+    localeName: localeName,
+    fullYearFormat: fullYearFormat,
+    dayFormat: dayFormat,
+    mediumDateFormat: mediumDateFormat,
+    singleDigitHourFormat: singleDigitHourFormat,
+    singleDigitMinuteFormat: singleDigitMinuteFormat,
+    doubleDigitMinuteFormat: doubleDigitMinuteFormat,
+    singleDigitSecondFormat: singleDigitSecondFormat,
+    decimalFormat: decimalFormat,
+  );
+
+  @override
+  String get alertDialogLabel => r'Alerte';
+
+  @override
+  String get anteMeridiemAbbreviation => r'AM';
+
+  @override
+  String get copyButtonLabel => r'Copier';
+
+  @override
+  String get cutButtonLabel => r'Couper';
+
+  @override
+  String get datePickerDateOrderString => r'dmy';
+
+  @override
+  String get datePickerDateTimeOrderString => r'date_time_dayPeriod';
+
+  @override
+  String get datePickerHourSemanticsLabelOne => r'une heure';
+
+  @override
+  String get datePickerHourSemanticsLabelOther => r'$hour heures';
+
+  @override
+  String get datePickerMinuteSemanticsLabelOne => r'une minute';
+
+  @override
+  String get datePickerMinuteSemanticsLabelOther => r'$minute minutes';
+
+  @override
+  String get pasteButtonLabel => r'Coller';
+
+  @override
+  String get postMeridiemAbbreviation => r'PM';
+
+  @override
+  String get selectAllButtonLabel => r'Tout sélect.';
+
+  @override
+  String get timerPickerHourLabelOne => r'heure';
+
+  @override
+  String get timerPickerHourLabelOther => r'heures';
+
+  @override
+  String get timerPickerMinuteLabelOne => r'minute';
+
+  @override
+  String get timerPickerMinuteLabelOther => r'minutes';
+
+  @override
+  String get timerPickerSecondLabelOne => null;
+
+  @override
+  String get timerPickerSecondLabelOther => r's';
+}
+
+/// The set of supported languages, as language code strings.
+///
+/// The [GlobalCupertinoLocalizations.delegate] can generate localizations for
+/// any [Locale] with a language code from this set, regardless of the region.
+/// Some regions have specific support (e.g. `de` covers all forms of German,
+/// but there is support for `de-CH` specifically to override some of the
+/// translations for Switzerland).
+///
+/// See also:
+///
+///  * [getCupertinoTranslation], whose documentation describes these values.
+final Set<String> kCupertinoSupportedLanguages = HashSet<String>.from(const <String>[
+  'en', // English
+  'fr', // French
+]);
+
+/// Creates a [GlobalCupertinoLocalizations] instance for the given `locale`.
+///
+/// All of the function's arguments except `locale` will be passed to the [
+/// GlobalCupertinoLocalizations] constructor. (The `localeName` argument of that
+/// constructor is specified by the actual subclass constructor by this
+/// function.)
+///
+/// The following locales are supported by this package:
+///
+/// {@template flutter.localizations.cupertino.languages}
+///  * `en` - English
+///  * `fr` - French
+/// {@endtemplate}
+///
+/// Generally speaking, this method is only intended to be used by
+/// [GlobalCupertinoLocalizations.delegate].
+GlobalCupertinoLocalizations getCupertinoTranslation(
+  Locale locale,
+  intl.DateFormat fullYearFormat,
+  intl.DateFormat dayFormat,
+  intl.DateFormat mediumDateFormat,
+  intl.DateFormat singleDigitHourFormat,
+  intl.DateFormat singleDigitMinuteFormat,
+  intl.DateFormat doubleDigitMinuteFormat,
+  intl.DateFormat singleDigitSecondFormat,
+  intl.NumberFormat decimalFormat,
+) {
+  switch (locale.languageCode) {
+    case 'en':
+      return CupertinoLocalizationEn(fullYearFormat: fullYearFormat, dayFormat: dayFormat, mediumDateFormat: mediumDateFormat, singleDigitHourFormat: singleDigitHourFormat, singleDigitMinuteFormat: singleDigitMinuteFormat, doubleDigitMinuteFormat: doubleDigitMinuteFormat, singleDigitSecondFormat: singleDigitSecondFormat, decimalFormat: decimalFormat);
+    case 'fr':
+      return CupertinoLocalizationFr(fullYearFormat: fullYearFormat, dayFormat: dayFormat, mediumDateFormat: mediumDateFormat, singleDigitHourFormat: singleDigitHourFormat, singleDigitMinuteFormat: singleDigitMinuteFormat, doubleDigitMinuteFormat: doubleDigitMinuteFormat, singleDigitSecondFormat: singleDigitSecondFormat, decimalFormat: decimalFormat);
+  }
+  assert(false, 'getCupertinoTranslation() called for unsupported locale "$locale"');
+  return null;
+}
diff --git a/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart b/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
index 254d823..a373dbe 100644
--- a/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
+++ b/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
@@ -13145,7 +13145,7 @@
 /// See also:
 ///
 ///  * [getMaterialTranslation], whose documentation describes these values.
-final Set<String> kSupportedLanguages = HashSet<String>.from(const <String>[
+final Set<String> kMaterialSupportedLanguages = HashSet<String>.from(const <String>[
   'ar', // Arabic
   'bg', // Bulgarian
   'bs', // Bosnian
@@ -13209,7 +13209,7 @@
 ///
 /// The following locales are supported by this package:
 ///
-/// {@template flutter.localizations.languages}
+/// {@template flutter.localizations.material.languages}
 ///  * `ar` - Arabic
 ///  * `bg` - Bulgarian
 ///  * `bs` - Bosnian
diff --git a/packages/flutter_localizations/lib/src/material_localizations.dart b/packages/flutter_localizations/lib/src/material_localizations.dart
index a0b72d2..c1f57fd 100644
--- a/packages/flutter_localizations/lib/src/material_localizations.dart
+++ b/packages/flutter_localizations/lib/src/material_localizations.dart
@@ -9,6 +9,7 @@
 import 'package:intl/intl.dart' as intl;
 import 'package:intl/date_symbols.dart' as intl;
 
+import 'cupertino_localizations.dart';
 import 'l10n/generated_material_localizations.dart';
 import 'utils/date_localizations.dart' as util;
 import 'widgets_localizations.dart';
@@ -20,9 +21,9 @@
 ///
 /// This class supports locales with the following [Locale.languageCode]s:
 ///
-/// {@macro flutter.localizations.languages}
+/// {@macro flutter.localizations.material.languages}
 ///
-/// This list is available programatically via [kSupportedLanguages].
+/// This list is available programatically via [kMaterialSupportedLanguages].
 ///
 /// ## Sample code
 ///
@@ -530,6 +531,7 @@
   /// )
   /// ```
   static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
+    GlobalCupertinoLocalizations.delegate,
     GlobalMaterialLocalizations.delegate,
     GlobalWidgetsLocalizations.delegate,
   ];
@@ -556,7 +558,7 @@
   const _MaterialLocalizationsDelegate();
 
   @override
-  bool isSupported(Locale locale) => kSupportedLanguages.contains(locale.languageCode);
+  bool isSupported(Locale locale) => kMaterialSupportedLanguages.contains(locale.languageCode);
 
   static final Map<Locale, Future<MaterialLocalizations>> _loadedTranslations = <Locale, Future<MaterialLocalizations>>{};
 
@@ -623,5 +625,5 @@
   bool shouldReload(_MaterialLocalizationsDelegate old) => false;
 
   @override
-  String toString() => 'GlobalMaterialLocalizations.delegate(${kSupportedLanguages.length} locales)';
+  String toString() => 'GlobalMaterialLocalizations.delegate(${kMaterialSupportedLanguages.length} locales)';
 }
diff --git a/packages/flutter_localizations/test/translations_test.dart b/packages/flutter_localizations/test/translations_test.dart
index 7d69158..00f5a0a 100644
--- a/packages/flutter_localizations/test/translations_test.dart
+++ b/packages/flutter_localizations/test/translations_test.dart
@@ -7,7 +7,7 @@
 import 'package:flutter_test/flutter_test.dart';
 
 void main() {
-  for (String language in kSupportedLanguages) {
+  for (String language in kMaterialSupportedLanguages) {
     testWidgets('translations exist for $language', (WidgetTester tester) async {
       final Locale locale = Locale(language);