From 5456cad3437bd9f3f0353bba67963a83ef5cc445 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 8 Jul 2021 15:06:04 -0700 Subject: [PATCH] Migrate gen_localizations to null-safety (#86051) --- .../localization/bin/gen_localizations.dart | 105 +++++++++--------- .../localization/localizations_validator.dart | 4 +- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/dev/tools/localization/bin/gen_localizations.dart b/dev/tools/localization/bin/gen_localizations.dart index 45db77e29b..ff3624ec95 100644 --- a/dev/tools/localization/bin/gen_localizations.dart +++ b/dev/tools/localization/bin/gen_localizations.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - // 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 @@ -44,7 +42,6 @@ import 'dart:io'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import '../gen_cupertino_localizations.dart'; @@ -55,17 +52,17 @@ import 'encode_kn_arb_files.dart'; /// This is the core of this script; it generates the code used for translations. String generateArbBasedLocalizationSubclasses({ - @required Map> localeToResources, - @required Map> localeToResourceAttributes, - @required String generatedClassPrefix, - @required String baseClass, - @required HeaderGenerator generateHeader, - @required ConstructorGenerator generateConstructor, - @required String factoryName, - @required String factoryDeclaration, - @required String factoryArguments, - @required String supportedLanguagesConstant, - @required String supportedLanguagesDocMacro, + required Map> localeToResources, + required Map> localeToResourceAttributes, + required String generatedClassPrefix, + 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); @@ -92,16 +89,16 @@ String generateArbBasedLocalizationSubclasses({ for (final LocaleInfo locale in localeToResources.keys.toList()..sort()) { if (locale.scriptCode != null) { languageToScriptCodes[locale.languageCode] ??= {}; - languageToScriptCodes[locale.languageCode].add(locale.scriptCode); + languageToScriptCodes[locale.languageCode]!.add(locale.scriptCode!); } if (locale.countryCode != null && locale.scriptCode != null) { final LocaleInfo key = LocaleInfo.fromString('${locale.languageCode}_${locale.scriptCode}'); languageAndScriptToCountryCodes[key] ??= {}; - languageAndScriptToCountryCodes[key].add(locale.countryCode); + languageAndScriptToCountryCodes[key]!.add(locale.countryCode!); } languageToLocales[locale.languageCode] ??= []; - languageToLocales[locale.languageCode].add(locale); - allResourceIdentifiers.addAll(localeToResources[locale].keys.toList()..sort()); + languageToLocales[locale.languageCode]!.add(locale); + allResourceIdentifiers.addAll(localeToResources[locale]!.keys.toList()..sort()); } // We generate one class per supported language (e.g. @@ -137,19 +134,19 @@ String generateArbBasedLocalizationSubclasses({ output.writeln(generateClassDeclaration(languageLocale, generatedClassPrefix, baseClass)); output.writeln(generateConstructor(languageLocale)); - final Map languageResources = localeToResources[languageLocale]; + final Map languageResources = localeToResources[languageLocale]!; for (final String key in allKeys) { - final Map attributes = localeToResourceAttributes[canonicalLocale][key] as Map; + final Map? attributes = localeToResourceAttributes[canonicalLocale]![key] as Map?; output.writeln(generateGetter(key, languageResources[key], attributes, languageLocale)); } output.writeln('}'); int countryCodeCount = 0; int scriptCodeCount = 0; if (languageToScriptCodes.containsKey(languageName)) { - scriptCodeCount = languageToScriptCodes[languageName].length; + scriptCodeCount = languageToScriptCodes[languageName]!.length; // Language has scriptCodes, so we need to properly fallback countries to corresponding // script default values before language default values. - for (final String scriptCode in languageToScriptCodes[languageName]) { + for (final String scriptCode in languageToScriptCodes[languageName]!) { final LocaleInfo scriptBaseLocale = LocaleInfo.fromString('${languageName}_$scriptCode'); output.writeln(generateClassDeclaration( scriptBaseLocale, @@ -157,16 +154,16 @@ String generateArbBasedLocalizationSubclasses({ '$generatedClassPrefix${languageLocale.camelCase()}', )); output.writeln(generateConstructor(scriptBaseLocale)); - final Map scriptResources = localeToResources[scriptBaseLocale]; + final Map scriptResources = localeToResources[scriptBaseLocale]!; for (final String key in scriptResources.keys.toList()..sort()) { if (languageResources[key] == scriptResources[key]) continue; - final Map attributes = localeToResourceAttributes[canonicalLocale][key] as Map; + final Map? attributes = localeToResourceAttributes[canonicalLocale]![key] as Map?; output.writeln(generateGetter(key, scriptResources[key], attributes, languageLocale)); } output.writeln('}'); - final List localeCodes = languageToLocales[languageName]..sort(); + final List localeCodes = languageToLocales[languageName]!..sort(); for (final LocaleInfo locale in localeCodes) { if (locale.originalString == languageName) continue; @@ -181,12 +178,12 @@ String generateArbBasedLocalizationSubclasses({ '$generatedClassPrefix${scriptBaseLocale.camelCase()}', )); output.writeln(generateConstructor(locale)); - final Map localeResources = localeToResources[locale]; + final Map localeResources = localeToResources[locale]!; for (final String key in localeResources.keys) { // When script fallback contains the key, we compare to it instead of language fallback. if (scriptResources.containsKey(key) ? scriptResources[key] == localeResources[key] : languageResources[key] == localeResources[key]) continue; - final Map attributes = localeToResourceAttributes[canonicalLocale][key] as Map; + final Map? attributes = localeToResourceAttributes[canonicalLocale]![key] as Map?; output.writeln(generateGetter(key, localeResources[key], attributes, languageLocale)); } output.writeln('}'); @@ -195,12 +192,12 @@ String generateArbBasedLocalizationSubclasses({ } else { // No scriptCode. Here, we do not compare against script default (because it // doesn't exist). - final List localeCodes = languageToLocales[languageName]..sort(); + final List localeCodes = languageToLocales[languageName]!..sort(); for (final LocaleInfo locale in localeCodes) { if (locale.originalString == languageName) continue; countryCodeCount += 1; - final Map localeResources = localeToResources[locale]; + final Map localeResources = localeToResources[locale]!; output.writeln(generateClassDeclaration( locale, generatedClassPrefix, @@ -210,7 +207,7 @@ String generateArbBasedLocalizationSubclasses({ for (final String key in localeResources.keys) { if (languageResources[key] == localeResources[key]) continue; - final Map attributes = localeToResourceAttributes[canonicalLocale][key] as Map; + final Map? attributes = localeToResourceAttributes[canonicalLocale]![key] as Map?; output.writeln(generateGetter(key, localeResources[key], attributes, languageLocale)); } output.writeln('}'); @@ -268,19 +265,19 @@ $factoryDeclaration switch (locale.languageCode) {'''); for (final String language in languageToLocales.keys) { // Only one instance of the language. - if (languageToLocales[language].length == 1) { + if (languageToLocales[language]!.length == 1) { output.writeln(''' case '$language': - return $generatedClassPrefix${languageToLocales[language][0].camelCase()}($factoryArguments);'''); + return $generatedClassPrefix${languageToLocales[language]![0].camelCase()}($factoryArguments);'''); } else if (!languageToScriptCodes.containsKey(language)) { // Does not distinguish between scripts. Switch on countryCode directly. output.writeln(''' case '$language': { switch (locale.countryCode) {'''); - for (final LocaleInfo locale in languageToLocales[language]) { + for (final LocaleInfo locale in languageToLocales[language]!) { if (locale.originalString == language) continue; assert(locale.length > 1); - final String countryCode = locale.countryCode; + final String countryCode = locale.countryCode!; output.writeln(''' case '$countryCode': return $generatedClassPrefix${locale.camelCase()}($factoryArguments);'''); @@ -294,14 +291,14 @@ $factoryDeclaration output.writeln(''' case '$language': { switch (locale.scriptCode) {'''); - for (final String scriptCode in languageToScriptCodes[language]) { + for (final String scriptCode in languageToScriptCodes[language]!) { final LocaleInfo scriptLocale = LocaleInfo.fromString('${language}_$scriptCode'); output.writeln(''' case '$scriptCode': {'''); if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) { output.writeln(''' switch (locale.countryCode) {'''); - for (final LocaleInfo locale in languageToLocales[language]) { + for (final LocaleInfo locale in languageToLocales[language]!) { if (locale.countryCode == null) continue; else @@ -310,7 +307,7 @@ $factoryDeclaration continue; if (locale.scriptCode != scriptCode && locale.scriptCode != null) continue; - final String countryCode = locale.countryCode; + final String countryCode = locale.countryCode!; output.writeln(''' case '$countryCode': return $generatedClassPrefix${locale.camelCase()}($factoryArguments);'''); @@ -319,7 +316,7 @@ $factoryDeclaration // Return a fallback locale that matches scriptCode, but not countryCode. // // Explicitly defined scriptCode fallback: - if (languageToLocales[language].contains(scriptLocale)) { + if (languageToLocales[language]!.contains(scriptLocale)) { if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) { output.writeln(''' }'''); @@ -330,7 +327,7 @@ $factoryDeclaration } else { // Not Explicitly defined, fallback to first locale with the same language and // script: - for (final LocaleInfo locale in languageToLocales[language]) { + for (final LocaleInfo locale in languageToLocales[language]!) { if (locale.scriptCode != scriptCode) continue; if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) { @@ -349,13 +346,13 @@ $factoryDeclaration if (hasCountryCode) { output.writeln(''' switch (locale.countryCode) {'''); - for (final LocaleInfo locale in languageToLocales[language]) { + for (final LocaleInfo locale in languageToLocales[language]!) { if (locale.originalString == language) continue; assert(locale.length > 1); if (locale.countryCode == null) continue; - final String countryCode = locale.countryCode; + final String countryCode = locale.countryCode!; output.writeln(''' case '$countryCode': return $generatedClassPrefix${locale.camelCase()}($factoryArguments);'''); @@ -382,12 +379,12 @@ $factoryDeclaration /// Typically "String", but some (e.g. "timeOfDayFormat") return enums. /// /// Used by [generateGetter] below. -String generateType(Map attributes) { +String generateType(Map? attributes) { bool optional = false; String type = 'String'; if (attributes != null) { optional = attributes.containsKey('optional'); - switch (attributes['x-flutter-type'] as String) { + switch (attributes['x-flutter-type'] as String?) { case 'icuShortTimePattern': type = 'TimeOfDayFormat'; break; @@ -406,11 +403,11 @@ String generateType(Map attributes) { /// those we have to therefore provide an alternate name. /// /// Used by [generateGetter] below. -String generateKey(String key, Map attributes) { +String generateKey(String key, Map? attributes) { if (attributes != null) { if (attributes.containsKey('parameters')) return '${key}Raw'; - switch (attributes['x-flutter-type'] as String) { + switch (attributes['x-flutter-type'] as String?) { case 'icuShortTimePattern': return '${key}Raw'; } @@ -447,12 +444,12 @@ const Map _scriptCategoryToEnum = { /// it. /// /// Used by [generateGetter] below. -String generateValue(String value, Map attributes, LocaleInfo locale) { +String? generateValue(String? value, Map? attributes, LocaleInfo locale) { if (value == null) return null; // cupertino_en.arb doesn't use x-flutter-type. if (attributes != null) { - switch (attributes['x-flutter-type'] as String) { + switch (attributes['x-flutter-type'] as String?) { case 'icuShortTimePattern': if (!_icuTimeOfDayToEnum.containsKey(value)) { throw Exception( @@ -479,14 +476,14 @@ String generateValue(String value, Map attributes, LocaleInfo l /// Combines [generateType], [generateKey], and [generateValue] to return /// the source of getters for the GlobalMaterialLocalizations subclass. /// The locale is the locale for which the getter is being generated. -String generateGetter(String key, String value, Map attributes, LocaleInfo locale) { +String generateGetter(String key, String? value, Map? attributes, LocaleInfo locale) { final String type = generateType(attributes); key = generateKey(key, attributes); - value = generateValue(value, attributes, locale); + final String? generatedValue = generateValue(value, attributes, locale); return ''' @override - $type get $key => $value;'''; + $type get $key => $generatedValue;'''; } void main(List rawArgs) { @@ -551,7 +548,7 @@ void main(List rawArgs) { exitWithError('$exception'); } - final String materialLocalizations = options.writeToFile || !options.cupertinoOnly + final String? materialLocalizations = options.writeToFile || !options.cupertinoOnly ? generateArbBasedLocalizationSubclasses( localeToResources: materialLocaleToResources, localeToResourceAttributes: materialLocaleToResourceAttributes, @@ -566,7 +563,7 @@ void main(List rawArgs) { supportedLanguagesDocMacro: materialSupportedLanguagesDocMacro, ) : null; - final String cupertinoLocalizations = options.writeToFile || !options.materialOnly + final String? cupertinoLocalizations = options.writeToFile || !options.materialOnly ? generateArbBasedLocalizationSubclasses( localeToResources: cupertinoLocaleToResources, localeToResourceAttributes: cupertinoLocaleToResourceAttributes, @@ -584,9 +581,9 @@ void main(List rawArgs) { if (options.writeToFile) { final File materialLocalizationsFile = File(path.join(directory.path, 'generated_material_localizations.dart')); - materialLocalizationsFile.writeAsStringSync(materialLocalizations, flush: true); + materialLocalizationsFile.writeAsStringSync(materialLocalizations!, flush: true); final File cupertinoLocalizationsFile = File(path.join(directory.path, 'generated_cupertino_localizations.dart')); - cupertinoLocalizationsFile.writeAsStringSync(cupertinoLocalizations, flush: true); + cupertinoLocalizationsFile.writeAsStringSync(cupertinoLocalizations!, flush: true); } else { if (!options.cupertinoOnly) { stdout.write(materialLocalizations); diff --git a/dev/tools/localization/localizations_validator.dart b/dev/tools/localization/localizations_validator.dart index 4714516730..68d34164f8 100644 --- a/dev/tools/localization/localizations_validator.dart +++ b/dev/tools/localization/localizations_validator.dart @@ -68,11 +68,11 @@ void validateEnglishLocalizations(File file) { } final bool optional = atResource.containsKey('optional'); - final String description = atResource['description'] as String; + final String? description = atResource['description'] as String?; if (description == null && !optional) errorMessages.writeln('No description specified for $atResourceId'); - final String plural = atResource['plural'] as String; + final String? plural = atResource['plural'] as String?; final String resourceId = atResourceId.substring(1); if (plural != null) { final String resourceIdOther = '${resourceId}Other';