Migrate gen_l10n to null safety (#80763)

This commit is contained in:
Jenn Magder 2021-04-20 11:09:03 -07:00 committed by GitHub
parent 4cceeaa075
commit 728a193383
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 263 deletions

View file

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
@ -18,20 +16,20 @@ import 'localizations_utils.dart';
/// Run the localizations generation script with the configuration [options]. /// Run the localizations generation script with the configuration [options].
LocalizationsGenerator generateLocalizations({ LocalizationsGenerator generateLocalizations({
@required Directory projectDir, required Directory projectDir,
@required Directory dependenciesDir, Directory? dependenciesDir,
@required LocalizationOptions options, required LocalizationOptions options,
@required Logger logger, required Logger logger,
@required FileSystem fileSystem, required FileSystem fileSystem,
}) { }) {
// If generating a synthetic package, generate a warning if // If generating a synthetic package, generate a warning if
// flutter: generate is not set. // flutter: generate is not set.
final FlutterManifest flutterManifest = FlutterManifest.createFromPath( final FlutterManifest? flutterManifest = FlutterManifest.createFromPath(
projectDir.childFile('pubspec.yaml').path, projectDir.childFile('pubspec.yaml').path,
fileSystem: projectDir.fileSystem, fileSystem: projectDir.fileSystem,
logger: logger, logger: logger,
); );
if (options.useSyntheticPackage && !flutterManifest.generateSyntheticPackage) { if (options.useSyntheticPackage && (flutterManifest == null || !flutterManifest.generateSyntheticPackage)) {
throwToolExit( throwToolExit(
'Attempted to generate localizations code without having ' 'Attempted to generate localizations code without having '
'the flutter: generate flag turned on.' 'the flutter: generate flag turned on.'
@ -44,9 +42,9 @@ LocalizationsGenerator generateLocalizations({
precacheLanguageAndRegionTags(); precacheLanguageAndRegionTags();
final String inputPathString = options?.arbDirectory?.path ?? fileSystem.path.join('lib', 'l10n'); final String inputPathString = options.arbDirectory?.path ?? fileSystem.path.join('lib', 'l10n');
final String templateArbFileName = options?.templateArbFile?.toFilePath() ?? 'app_en.arb'; final String templateArbFileName = options.templateArbFile?.toFilePath() ?? 'app_en.arb';
final String outputFileString = options?.outputLocalizationsFile?.toFilePath() ?? 'app_localizations.dart'; final String outputFileString = options.outputLocalizationsFile?.toFilePath() ?? 'app_localizations.dart';
LocalizationsGenerator generator; LocalizationsGenerator generator;
try { try {
generator = LocalizationsGenerator( generator = LocalizationsGenerator(
@ -56,16 +54,16 @@ LocalizationsGenerator generateLocalizations({
inputPathString: inputPathString, inputPathString: inputPathString,
templateArbFileName: templateArbFileName, templateArbFileName: templateArbFileName,
outputFileString: outputFileString, outputFileString: outputFileString,
outputPathString: options?.outputDirectory?.path, outputPathString: options.outputDirectory?.path,
classNameString: options.outputClass ?? 'AppLocalizations', classNameString: options.outputClass ?? 'AppLocalizations',
preferredSupportedLocales: options.preferredSupportedLocales, preferredSupportedLocales: options.preferredSupportedLocales,
headerString: options.header, headerString: options.header,
headerFile: options?.headerFile?.toFilePath(), headerFile: options.headerFile?.toFilePath(),
useDeferredLoading: options.deferredLoading ?? false, useDeferredLoading: options.deferredLoading ?? false,
useSyntheticPackage: options.useSyntheticPackage ?? true, useSyntheticPackage: options.useSyntheticPackage,
areResourceAttributesRequired: options.areResourceAttributesRequired ?? false, areResourceAttributesRequired: options.areResourceAttributesRequired,
untranslatedMessagesFile: options?.untranslatedMessagesFile?.toFilePath(), untranslatedMessagesFile: options.untranslatedMessagesFile?.toFilePath(),
usesNullableGetter: options?.usesNullableGetter ?? true, usesNullableGetter: options.usesNullableGetter,
) )
..loadResources() ..loadResources()
..writeOutputFiles(logger, isFromYaml: true); ..writeOutputFiles(logger, isFromYaml: true);
@ -87,9 +85,9 @@ String _syntheticL10nPackagePath(FileSystem fileSystem) => fileSystem.path.join(
List<String> generateMethodParameters(Message message) { List<String> generateMethodParameters(Message message) {
assert(message.placeholders.isNotEmpty); assert(message.placeholders.isNotEmpty);
final Placeholder countPlaceholder = message.isPlural ? message.getCountPlaceholder() : null; final Placeholder? countPlaceholder = message.isPlural ? message.getCountPlaceholder() : null;
return message.placeholders.map((Placeholder placeholder) { return message.placeholders.map((Placeholder placeholder) {
final String type = placeholder == countPlaceholder ? 'int' : placeholder.type; final String? type = placeholder == countPlaceholder ? 'int' : placeholder.type;
return '$type ${placeholder.name}'; return '$type ${placeholder.name}';
}).toList(); }).toList();
} }
@ -102,7 +100,8 @@ String generateDateFormattingLogic(Message message) {
final Iterable<String> formatStatements = message.placeholders final Iterable<String> formatStatements = message.placeholders
.where((Placeholder placeholder) => placeholder.isDate) .where((Placeholder placeholder) => placeholder.isDate)
.map((Placeholder placeholder) { .map((Placeholder placeholder) {
if (placeholder.format == null) { final String? placeholderFormat = placeholder.format;
if (placeholderFormat == null) {
throw L10nException( throw L10nException(
'The placeholder, ${placeholder.name}, has its "type" resource attribute set to ' 'The placeholder, ${placeholder.name}, has its "type" resource attribute set to '
'the "${placeholder.type}" type. To properly resolve for the right ' 'the "${placeholder.type}" type. To properly resolve for the right '
@ -114,7 +113,7 @@ String generateDateFormattingLogic(Message message) {
} }
if (!placeholder.hasValidDateFormat) { if (!placeholder.hasValidDateFormat) {
throw L10nException( throw L10nException(
'Date format "${placeholder.format}" for placeholder ' 'Date format "$placeholderFormat" for placeholder '
'${placeholder.name} does not have a corresponding DateFormat ' '${placeholder.name} does not have a corresponding DateFormat '
'constructor\n. Check the intl library\'s DateFormat class ' 'constructor\n. Check the intl library\'s DateFormat class '
'constructors for allowed date formats.' 'constructors for allowed date formats.'
@ -122,7 +121,7 @@ String generateDateFormattingLogic(Message message) {
} }
return dateFormatTemplate return dateFormatTemplate
.replaceAll('@(placeholder)', placeholder.name) .replaceAll('@(placeholder)', placeholder.name)
.replaceAll('@(format)', placeholder.format); .replaceAll('@(format)', placeholderFormat);
}); });
return formatStatements.isEmpty ? '@(none)' : formatStatements.join(''); return formatStatements.isEmpty ? '@(none)' : formatStatements.join('');
@ -136,9 +135,10 @@ String generateNumberFormattingLogic(Message message) {
final Iterable<String> formatStatements = message.placeholders final Iterable<String> formatStatements = message.placeholders
.where((Placeholder placeholder) => placeholder.isNumber) .where((Placeholder placeholder) => placeholder.isNumber)
.map((Placeholder placeholder) { .map((Placeholder placeholder) {
if (!placeholder.hasValidNumberFormat) { final String? placeholderFormat = placeholder.format;
if (!placeholder.hasValidNumberFormat || placeholderFormat == null) {
throw L10nException( throw L10nException(
'Number format ${placeholder.format} for the ${placeholder.name} ' 'Number format $placeholderFormat for the ${placeholder.name} '
'placeholder does not have a corresponding NumberFormat constructor.\n' 'placeholder does not have a corresponding NumberFormat constructor.\n'
'Check the intl library\'s NumberFormat class constructors for allowed ' 'Check the intl library\'s NumberFormat class constructors for allowed '
'number formats.' 'number formats.'
@ -157,19 +157,19 @@ String generateNumberFormattingLogic(Message message) {
if (placeholder.hasNumberFormatWithParameters) { if (placeholder.hasNumberFormatWithParameters) {
return numberFormatNamedTemplate return numberFormatNamedTemplate
.replaceAll('@(placeholder)', placeholder.name) .replaceAll('@(placeholder)', placeholder.name)
.replaceAll('@(format)', placeholder.format) .replaceAll('@(format)', placeholderFormat)
.replaceAll('@(parameters)', parameters.join(',\n ')); .replaceAll('@(parameters)', parameters.join(',\n '));
} else { } else {
return numberFormatPositionalTemplate return numberFormatPositionalTemplate
.replaceAll('@(placeholder)', placeholder.name) .replaceAll('@(placeholder)', placeholder.name)
.replaceAll('@(format)', placeholder.format); .replaceAll('@(format)', placeholderFormat);
} }
}); });
return formatStatements.isEmpty ? '@(none)' : formatStatements.join(''); return formatStatements.isEmpty ? '@(none)' : formatStatements.join('');
} }
String generatePluralMethod(Message message, AppResourceBundle bundle) { String _generatePluralMethod(Message message, String translationForMessage) {
if (message.placeholders.isEmpty) { if (message.placeholders.isEmpty) {
throw L10nException( throw L10nException(
'Unable to find placeholders for the plural message: ${message.resourceId}.\n' 'Unable to find placeholders for the plural message: ${message.resourceId}.\n'
@ -180,20 +180,12 @@ String generatePluralMethod(Message message, AppResourceBundle bundle) {
// To make it easier to parse the plurals message, temporarily replace each // To make it easier to parse the plurals message, temporarily replace each
// "{placeholder}" parameter with "#placeholder#". // "{placeholder}" parameter with "#placeholder#".
String easyMessage = bundle.translationFor(message); String easyMessage = translationForMessage;
for (final Placeholder placeholder in message.placeholders) { for (final Placeholder placeholder in message.placeholders) {
easyMessage = easyMessage.replaceAll('{${placeholder.name}}', '#${placeholder.name}#'); easyMessage = easyMessage.replaceAll('{${placeholder.name}}', '#${placeholder.name}#');
} }
final Placeholder countPlaceholder = message.getCountPlaceholder(); final Placeholder countPlaceholder = message.getCountPlaceholder();
if (countPlaceholder == null) {
throw L10nException(
'Unable to find the count placeholder for the plural message: ${message.resourceId}.\n'
'Check to see if the plural message is in the proper ICU syntax format '
'and ensure that placeholders are properly specified.'
);
}
const Map<String, String> pluralIds = <String, String>{ const Map<String, String> pluralIds = <String, String>{
'=0': 'zero', '=0': 'zero',
'=1': 'one', '=1': 'one',
@ -206,9 +198,9 @@ String generatePluralMethod(Message message, AppResourceBundle bundle) {
final List<String> pluralLogicArgs = <String>[]; final List<String> pluralLogicArgs = <String>[];
for (final String pluralKey in pluralIds.keys) { for (final String pluralKey in pluralIds.keys) {
final RegExp expRE = RegExp('($pluralKey)\\s*{([^}]+)}'); final RegExp expRE = RegExp('($pluralKey)\\s*{([^}]+)}');
final RegExpMatch match = expRE.firstMatch(easyMessage); final RegExpMatch? match = expRE.firstMatch(easyMessage);
if (match != null && match.groupCount == 2) { if (match != null && match.groupCount == 2) {
String argValue = generateString(match.group(2)); String argValue = generateString(match.group(2)!);
for (final Placeholder placeholder in message.placeholders) { for (final Placeholder placeholder in message.placeholders) {
if (placeholder != countPlaceholder && placeholder.requiresFormatting) { if (placeholder != countPlaceholder && placeholder.requiresFormatting) {
argValue = argValue.replaceAll( argValue = argValue.replaceAll(
@ -231,7 +223,7 @@ String generatePluralMethod(Message message, AppResourceBundle bundle) {
} }
final List<String> parameters = message.placeholders.map((Placeholder placeholder) { final List<String> parameters = message.placeholders.map((Placeholder placeholder) {
final String placeholderType = placeholder == countPlaceholder ? 'int' : placeholder.type; final String? placeholderType = placeholder == countPlaceholder ? 'int' : placeholder.type;
return '$placeholderType ${placeholder.name}'; return '$placeholderType ${placeholder.name}';
}).toList(); }).toList();
@ -285,9 +277,9 @@ bool _needsCurlyBracketStringInterpolation(String messageString, String placehol
} }
} }
String generateMethod(Message message, AppResourceBundle bundle) { String _generateMethod(Message message, String translationForMessage) {
String generateMessage() { String generateMessage() {
String messageValue = generateString(bundle.translationFor(message)); String messageValue = generateString(translationForMessage);
for (final Placeholder placeholder in message.placeholders) { for (final Placeholder placeholder in message.placeholders) {
if (placeholder.requiresFormatting) { if (placeholder.requiresFormatting) {
messageValue = messageValue.replaceAll( messageValue = messageValue.replaceAll(
@ -310,7 +302,7 @@ String generateMethod(Message message, AppResourceBundle bundle) {
} }
if (message.isPlural) { if (message.isPlural) {
return generatePluralMethod(message, bundle); return _generatePluralMethod(message, translationForMessage);
} }
if (message.placeholdersRequireFormatting) { if (message.placeholdersRequireFormatting) {
@ -335,7 +327,7 @@ String generateMethod(Message message, AppResourceBundle bundle) {
.replaceAll('@(message)', generateMessage()); .replaceAll('@(message)', generateMessage());
} }
String generateBaseClassMethod(Message message, LocaleInfo templateArbLocale) { String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale) {
final String comment = message.description ?? 'No description provided for @${message.resourceId}.'; final String comment = message.description ?? 'No description provided for @${message.resourceId}.';
final String templateLocaleTranslationComment = ''' final String templateLocaleTranslationComment = '''
/// In $templateArbLocale, this message translates to: /// In $templateArbLocale, this message translates to:
@ -396,9 +388,9 @@ String _generateLookupByScriptCode(
.replaceAll('@(code)', 'scriptCode') .replaceAll('@(code)', 'scriptCode')
.replaceAll('@(switchClauses)', localesWithScriptCodes.map((LocaleInfo locale) { .replaceAll('@(switchClauses)', localesWithScriptCodes.map((LocaleInfo locale) {
return generateSwitchClauseTemplate(locale) return generateSwitchClauseTemplate(locale)
.replaceAll('@(case)', locale.scriptCode); .replaceAll('@(case)', locale.scriptCode!);
}).join('\n ')); }).join('\n '));
}).where((String switchClause) => switchClause != null); }).whereType<String>();
if (switchClauses.isEmpty) { if (switchClauses.isEmpty) {
return ''; return '';
@ -429,9 +421,9 @@ String _generateLookupByCountryCode(
.replaceAll('@(code)', 'countryCode') .replaceAll('@(code)', 'countryCode')
.replaceAll('@(switchClauses)', localesWithCountryCodes.map((LocaleInfo locale) { .replaceAll('@(switchClauses)', localesWithCountryCodes.map((LocaleInfo locale) {
return generateSwitchClauseTemplate(locale) return generateSwitchClauseTemplate(locale)
.replaceAll('@(case)', locale.countryCode); .replaceAll('@(case)', locale.countryCode!);
}).join('\n ')); }).join('\n '));
}).where((String switchClause) => switchClause != null); }).whereType<String>();
if (switchClauses.isEmpty) { if (switchClauses.isEmpty) {
return ''; return '';
@ -460,7 +452,7 @@ String _generateLookupByLanguageCode(
return generateSwitchClauseTemplate(locale) return generateSwitchClauseTemplate(locale)
.replaceAll('@(case)', locale.languageCode); .replaceAll('@(case)', locale.languageCode);
}).join('\n '); }).join('\n ');
}).where((String switchClause) => switchClause != null); }).whereType<String>();
if (switchClauses.isEmpty) { if (switchClauses.isEmpty) {
return ''; return '';
@ -504,11 +496,11 @@ String _generateLookupBody(
} }
String _generateDelegateClass({ String _generateDelegateClass({
AppResourceBundleCollection allBundles, required AppResourceBundleCollection allBundles,
String className, required String className,
Set<String> supportedLanguageCodes, required Set<String> supportedLanguageCodes,
bool useDeferredLoading, required bool useDeferredLoading,
String fileName, required String fileName,
}) { }) {
final String lookupBody = _generateLookupBody( final String lookupBody = _generateLookupBody(
@ -544,24 +536,24 @@ class LocalizationsGenerator {
/// Throws a [FileSystemException] when a file operation necessary for setting /// Throws a [FileSystemException] when a file operation necessary for setting
/// up the [LocalizationsGenerator] cannot be completed. /// up the [LocalizationsGenerator] cannot be completed.
factory LocalizationsGenerator({ factory LocalizationsGenerator({
@required FileSystem fileSystem, required FileSystem fileSystem,
@required String inputPathString, required String inputPathString,
String outputPathString, String? outputPathString,
@required String templateArbFileName, required String templateArbFileName,
String outputFileString, required String outputFileString,
@required String classNameString, required String classNameString,
List<String> preferredSupportedLocales, List<String>? preferredSupportedLocales,
String headerString, String? headerString,
String headerFile, String? headerFile,
bool useDeferredLoading = false, bool useDeferredLoading = false,
String inputsAndOutputsListPath, String? inputsAndOutputsListPath,
bool useSyntheticPackage = true, bool useSyntheticPackage = true,
String projectPathString, String? projectPathString,
bool areResourceAttributesRequired = false, bool areResourceAttributesRequired = false,
String untranslatedMessagesFile, String? untranslatedMessagesFile,
bool usesNullableGetter = true, bool usesNullableGetter = true,
}) { }) {
final Directory projectDirectory = projectDirFromPath(fileSystem, projectPathString); final Directory? projectDirectory = projectDirFromPath(fileSystem, projectPathString);
final Directory inputDirectory = inputDirectoryFromPath(fileSystem, inputPathString, projectDirectory); final Directory inputDirectory = inputDirectoryFromPath(fileSystem, inputPathString, projectDirectory);
final Directory outputDirectory = outputDirectoryFromPath(fileSystem, outputPathString ?? inputPathString, useSyntheticPackage, projectDirectory); final Directory outputDirectory = outputDirectoryFromPath(fileSystem, outputPathString ?? inputPathString, useSyntheticPackage, projectDirectory);
return LocalizationsGenerator._( return LocalizationsGenerator._(
@ -576,7 +568,7 @@ class LocalizationsGenerator {
baseOutputFile: outputDirectory.childFile(outputFileString), baseOutputFile: outputDirectory.childFile(outputFileString),
preferredSupportedLocales: preferredSupportedLocalesFromLocales(preferredSupportedLocales), preferredSupportedLocales: preferredSupportedLocalesFromLocales(preferredSupportedLocales),
header: headerFromFile(headerString, headerFile, inputDirectory), header: headerFromFile(headerString, headerFile, inputDirectory),
useDeferredLoading: useDeferredLoading ?? false, useDeferredLoading: useDeferredLoading,
untranslatedMessagesFile: _untranslatedMessagesFileFromPath(fileSystem, untranslatedMessagesFile), untranslatedMessagesFile: _untranslatedMessagesFileFromPath(fileSystem, untranslatedMessagesFile),
inputsAndOutputsListFile: _inputsAndOutputsListFileFromPath(fileSystem, inputsAndOutputsListPath), inputsAndOutputsListFile: _inputsAndOutputsListFileFromPath(fileSystem, inputsAndOutputsListPath),
areResourceAttributesRequired: areResourceAttributesRequired, areResourceAttributesRequired: areResourceAttributesRequired,
@ -586,17 +578,16 @@ class LocalizationsGenerator {
/// Creates an instance of the localizations generator class. /// Creates an instance of the localizations generator class.
/// ///
/// It takes in a [FileSystem] representation that the class will act upon. /// It takes in a [FileSystem] representation that the class will act upon.
LocalizationsGenerator._( LocalizationsGenerator._(this._fs, {
this._fs, { required this.inputDirectory,
this.inputDirectory, required this.outputDirectory,
@required this.outputDirectory, required this.templateArbFile,
@required this.templateArbFile, required this.baseOutputFile,
@required this.baseOutputFile, required this.className,
@required this.className,
this.preferredSupportedLocales = const <LocaleInfo>[], this.preferredSupportedLocales = const <LocaleInfo>[],
this.header = '', this.header = '',
this.useDeferredLoading = false, this.useDeferredLoading = false,
this.inputsAndOutputsListFile, required this.inputsAndOutputsListFile,
this.useSyntheticPackage = true, this.useSyntheticPackage = true,
this.projectDirectory, this.projectDirectory,
this.areResourceAttributesRequired = false, this.areResourceAttributesRequired = false,
@ -605,9 +596,11 @@ class LocalizationsGenerator {
}); });
final FileSystem _fs; final FileSystem _fs;
Iterable<Message> _allMessages; Iterable<Message> _allMessages = <Message>[];
AppResourceBundleCollection _allBundles; late final AppResourceBundleCollection _allBundles = AppResourceBundleCollection(inputDirectory);
LocaleInfo _templateArbLocale;
late final AppResourceBundle _templateBundle = AppResourceBundle(templateArbFile);
late final LocaleInfo _templateArbLocale = _templateBundle.locale;
@visibleForTesting @visibleForTesting
final bool useSyntheticPackage; final bool useSyntheticPackage;
@ -623,44 +616,32 @@ class LocalizationsGenerator {
/// ///
/// It is assumed that all input files (e.g. [templateArbFile], arb files /// It is assumed that all input files (e.g. [templateArbFile], arb files
/// for translated messages, header file templates) will reside here. /// for translated messages, header file templates) will reside here.
///
/// This directory is specified with the [initialize] method.
final Directory inputDirectory; final Directory inputDirectory;
/// The Flutter project's root directory. /// The Flutter project's root directory.
/// final Directory? projectDirectory;
/// This directory is specified with the [initialize] method.
final Directory projectDirectory;
/// The directory to generate the project's localizations files in. /// The directory to generate the project's localizations files in.
/// ///
/// It is assumed that all output files (e.g. The localizations /// It is assumed that all output files (e.g. The localizations
/// [outputFile], `messages_<locale>.dart` and `messages_all.dart`) /// [outputFile], `messages_<locale>.dart` and `messages_all.dart`)
/// will reside here. /// will reside here.
///
/// This directory is specified with the [initialize] method.
final Directory outputDirectory; final Directory outputDirectory;
/// The input arb file which defines all of the messages that will be /// The input arb file which defines all of the messages that will be
/// exported by the generated class that's written to [outputFile]. /// exported by the generated class that's written to [outputFile].
///
/// This file is specified with the [initialize] method.
final File templateArbFile; final File templateArbFile;
/// The file to write the generated abstract localizations and /// The file to write the generated abstract localizations and
/// localizations delegate classes to. Separate localizations /// localizations delegate classes to. Separate localizations
/// files will also be generated for each language using this /// files will also be generated for each language using this
/// filename as a prefix and the locale as the suffix. /// filename as a prefix and the locale as the suffix.
///
/// This file is specified with the [initialize] method.
final File baseOutputFile; final File baseOutputFile;
/// The class name to be used for the localizations class in [outputFile]. /// The class name to be used for the localizations class in [outputFile].
/// ///
/// For example, if 'AppLocalizations' is passed in, a class named /// For example, if 'AppLocalizations' is passed in, a class named
/// AppLocalizations will be used for localized message lookups. /// AppLocalizations will be used for localized message lookups.
///
/// The class name is specified with the [initialize] method.
final String className; final String className;
/// The list of preferred supported locales. /// The list of preferred supported locales.
@ -673,8 +654,6 @@ class LocalizationsGenerator {
/// The order of locales in this list will also be the order of locale /// The order of locales in this list will also be the order of locale
/// priority. For example, if a device supports 'en' and 'es' and /// priority. For example, if a device supports 'en' and 'es' and
/// ['es', 'en'] is passed in, the 'es' locale will take priority over 'en'. /// ['es', 'en'] is passed in, the 'es' locale will take priority over 'en'.
///
/// The list of preferred locales is specified with the [initialize] method.
final List<LocaleInfo> preferredSupportedLocales; final List<LocaleInfo> preferredSupportedLocales;
/// The list of all arb path strings in [inputDirectory]. /// The list of all arb path strings in [inputDirectory].
@ -715,19 +694,15 @@ class LocalizationsGenerator {
/// string format. /// string format.
final Map<File, String> _languageFileMap = <File, String>{}; final Map<File, String> _languageFileMap = <File, String>{};
/// Contains the generated application's localizations and localizations delegate
/// classes.
String _generatedLocalizationsFile;
/// A generated file that will contain the list of messages for each locale /// A generated file that will contain the list of messages for each locale
/// that do not have a translation yet. /// that do not have a translation yet.
@visibleForTesting @visibleForTesting
final File untranslatedMessagesFile; final File? untranslatedMessagesFile;
/// The file that contains the list of inputs and outputs for generating /// The file that contains the list of inputs and outputs for generating
/// localizations. /// localizations.
@visibleForTesting @visibleForTesting
final File inputsAndOutputsListFile; final File? inputsAndOutputsListFile;
final List<String> _inputFileList = <String>[]; final List<String> _inputFileList = <String>[];
final List<String> _outputFileList = <String>[]; final List<String> _outputFileList = <String>[];
@ -753,7 +728,7 @@ class LocalizationsGenerator {
} }
@visibleForTesting @visibleForTesting
static Directory projectDirFromPath(FileSystem fileSystem, String projectPathString) { static Directory? projectDirFromPath(FileSystem fileSystem, String? projectPathString) {
if (projectPathString == null) { if (projectPathString == null) {
return null; return null;
} }
@ -771,10 +746,7 @@ class LocalizationsGenerator {
/// Sets the reference [Directory] for [inputDirectory]. /// Sets the reference [Directory] for [inputDirectory].
@visibleForTesting @visibleForTesting
static Directory inputDirectoryFromPath(FileSystem fileSystem, String inputPathString, Directory projectDirectory) { static Directory inputDirectoryFromPath(FileSystem fileSystem, String inputPathString, Directory? projectDirectory) {
if (inputPathString == null) {
throw L10nException('inputPathString argument cannot be null');
}
final Directory inputDirectory = fileSystem.directory( final Directory inputDirectory = fileSystem.directory(
projectDirectory != null projectDirectory != null
? _getAbsoluteProjectPath(inputPathString, projectDirectory) ? _getAbsoluteProjectPath(inputPathString, projectDirectory)
@ -800,7 +772,7 @@ class LocalizationsGenerator {
/// Sets the reference [Directory] for [outputDirectory]. /// Sets the reference [Directory] for [outputDirectory].
@visibleForTesting @visibleForTesting
static Directory outputDirectoryFromPath(FileSystem fileSystem, String outputPathString, bool useSyntheticPackage, Directory projectDirectory) { static Directory outputDirectoryFromPath(FileSystem fileSystem, String outputPathString, bool useSyntheticPackage, Directory? projectDirectory) {
Directory outputDirectory; Directory outputDirectory;
if (useSyntheticPackage) { if (useSyntheticPackage) {
outputDirectory = fileSystem.directory( outputDirectory = fileSystem.directory(
@ -809,13 +781,6 @@ class LocalizationsGenerator {
: _syntheticL10nPackagePath(fileSystem) : _syntheticL10nPackagePath(fileSystem)
); );
} else { } else {
if (outputPathString == null) {
throw L10nException(
'outputPathString argument cannot be null if not using '
'synthetic package option.'
);
}
outputDirectory = fileSystem.directory( outputDirectory = fileSystem.directory(
projectDirectory != null projectDirectory != null
? _getAbsoluteProjectPath(outputPathString, projectDirectory) ? _getAbsoluteProjectPath(outputPathString, projectDirectory)
@ -828,13 +793,6 @@ class LocalizationsGenerator {
/// Sets the reference [File] for [templateArbFile]. /// Sets the reference [File] for [templateArbFile].
@visibleForTesting @visibleForTesting
static File templateArbFileFromFileName(String templateArbFileName, Directory inputDirectory) { static File templateArbFileFromFileName(String templateArbFileName, Directory inputDirectory) {
if (templateArbFileName == null) {
throw L10nException('templateArbFileName argument cannot be null');
}
if (inputDirectory == null) {
throw L10nException('inputDirectory cannot be null when setting template arb file');
}
final File templateArbFile = inputDirectory.childFile(templateArbFileName); final File templateArbFile = inputDirectory.childFile(templateArbFileName);
final String templateArbFileStatModeString = templateArbFile.statSync().modeString(); final String templateArbFileStatModeString = templateArbFile.statSync().modeString();
if (templateArbFileStatModeString[0] == '-' && templateArbFileStatModeString[3] == '-') { if (templateArbFileStatModeString[0] == '-' && templateArbFileStatModeString[3] == '-') {
@ -846,15 +804,6 @@ class LocalizationsGenerator {
return templateArbFile; return templateArbFile;
} }
/// Sets the reference [File] for the localizations delegate [outputFile].
@visibleForTesting
static File baseOutputFileFromOutputFile(FileSystem fileSystem, String outputFileString, Directory outputDirectory) {
if (outputDirectory == null) {
throw L10nException('outputFileString argument cannot be null');
}
return outputDirectory.childFile(outputFileString);
}
static bool _isValidClassName(String className) { static bool _isValidClassName(String className) {
// Public Dart class name cannot begin with an underscore // Public Dart class name cannot begin with an underscore
if (className[0] == '_') { if (className[0] == '_') {
@ -879,8 +828,8 @@ class LocalizationsGenerator {
/// classes. /// classes.
@visibleForTesting @visibleForTesting
static String classNameFromString(String classNameString) { static String classNameFromString(String classNameString) {
if (classNameString == null || classNameString.isEmpty) { if (classNameString.isEmpty) {
throw L10nException('classNameString argument cannot be null or empty'); throw L10nException('classNameString argument cannot be empty');
} }
if (!_isValidClassName(classNameString)) { if (!_isValidClassName(classNameString)) {
throw L10nException( throw L10nException(
@ -893,7 +842,7 @@ class LocalizationsGenerator {
/// Sets [preferredSupportedLocales] so that this particular list of locales /// Sets [preferredSupportedLocales] so that this particular list of locales
/// will take priority over the other locales. /// will take priority over the other locales.
@visibleForTesting @visibleForTesting
static List<LocaleInfo> preferredSupportedLocalesFromLocales(List<String> inputLocales) { static List<LocaleInfo> preferredSupportedLocalesFromLocales(List<String>? inputLocales) {
if (inputLocales == null || inputLocales.isEmpty) { if (inputLocales == null || inputLocales.isEmpty) {
return const <LocaleInfo>[]; return const <LocaleInfo>[];
} }
@ -902,7 +851,7 @@ class LocalizationsGenerator {
}).toList(); }).toList();
} }
static String headerFromFile(String headerString, String headerFile, Directory inputDirectory) { static String headerFromFile(String? headerString, String? headerFile, Directory inputDirectory) {
if (headerString != null && headerFile != null) { if (headerString != null && headerFile != null) {
throw L10nException( throw L10nException(
'Cannot accept both header and header file arguments. \n' 'Cannot accept both header and header file arguments. \n'
@ -928,7 +877,7 @@ class LocalizationsGenerator {
static String _getAbsoluteProjectPath(String relativePath, Directory projectDirectory) => static String _getAbsoluteProjectPath(String relativePath, Directory projectDirectory) =>
projectDirectory.fileSystem.path.join(projectDirectory.path, relativePath); projectDirectory.fileSystem.path.join(projectDirectory.path, relativePath);
static File _untranslatedMessagesFileFromPath(FileSystem fileSystem, String untranslatedMessagesFileString) { static File? _untranslatedMessagesFileFromPath(FileSystem fileSystem, String? untranslatedMessagesFileString) {
if (untranslatedMessagesFileString == null || untranslatedMessagesFileString.isEmpty) { if (untranslatedMessagesFileString == null || untranslatedMessagesFileString.isEmpty) {
return null; return null;
} }
@ -936,7 +885,7 @@ class LocalizationsGenerator {
return fileSystem.file(untranslatedMessagesFileString); return fileSystem.file(untranslatedMessagesFileString);
} }
static File _inputsAndOutputsListFileFromPath(FileSystem fileSystem, String inputsAndOutputsListPath) { static File? _inputsAndOutputsListFileFromPath(FileSystem fileSystem, String? inputsAndOutputsListPath) {
if (inputsAndOutputsListPath == null) { if (inputsAndOutputsListPath == null) {
return null; return null;
} }
@ -969,12 +918,10 @@ class LocalizationsGenerator {
// Load _allMessages from templateArbFile and _allBundles from all of the ARB // Load _allMessages from templateArbFile and _allBundles from all of the ARB
// files in inputDirectory. Also initialized: supportedLocales. // files in inputDirectory. Also initialized: supportedLocales.
void loadResources() { void loadResources() {
final AppResourceBundle templateBundle = AppResourceBundle(templateArbFile); _allMessages = _templateBundle.resourceIds.map((String id) => Message(
_templateArbLocale = templateBundle.locale; _templateBundle.resources, id, areResourceAttributesRequired,
_allMessages = templateBundle.resourceIds.map((String id) => Message(
templateBundle.resources, id, areResourceAttributesRequired,
)); ));
for (final String resourceId in templateBundle.resourceIds) { for (final String resourceId in _templateBundle.resourceIds) {
if (!_isValidGetterAndMethodName(resourceId)) { if (!_isValidGetterAndMethodName(resourceId)) {
throw L10nException( throw L10nException(
'Invalid ARB resource name "$resourceId" in $templateArbFile.\n' 'Invalid ARB resource name "$resourceId" in $templateArbFile.\n'
@ -985,7 +932,6 @@ class LocalizationsGenerator {
} }
} }
_allBundles = AppResourceBundleCollection(inputDirectory);
if (inputsAndOutputsListFile != null) { if (inputsAndOutputsListFile != null) {
_inputFileList.addAll(_allBundles.bundles.map((AppResourceBundle bundle) { _inputFileList.addAll(_allBundles.bundles.map((AppResourceBundle bundle) {
return bundle.file.absolute.path; return bundle.file.absolute.path;
@ -1011,7 +957,7 @@ class LocalizationsGenerator {
void _addUnimplementedMessage(LocaleInfo locale, String message) { void _addUnimplementedMessage(LocaleInfo locale, String message) {
if (_unimplementedMessages.containsKey(locale)) { if (_unimplementedMessages.containsKey(locale)) {
_unimplementedMessages[locale].add(message); _unimplementedMessages[locale]!.add(message);
} else { } else {
_unimplementedMessages.putIfAbsent(locale, () => <String>[message]); _unimplementedMessages.putIfAbsent(locale, () => <String>[message]);
} }
@ -1032,9 +978,9 @@ class LocalizationsGenerator {
_addUnimplementedMessage(locale, message.resourceId); _addUnimplementedMessage(locale, message.resourceId);
} }
return generateMethod( return _generateMethod(
message, message,
bundle.translationFor(message) == null ? templateBundle : bundle, bundle.translationFor(message) ?? templateBundle.translationFor(message)!,
); );
}); });
@ -1065,7 +1011,7 @@ class LocalizationsGenerator {
final Iterable<String> methods = messages final Iterable<String> methods = messages
.where((Message message) => bundle.translationFor(message) != null) .where((Message message) => bundle.translationFor(message) != null)
.map((Message message) => generateMethod(message, bundle)); .map((Message message) => _generateMethod(message, bundle.translationFor(message)!));
return subclassTemplate return subclassTemplate
.replaceAll('@(language)', describeLocale(locale.toString())) .replaceAll('@(language)', describeLocale(locale.toString()))
@ -1078,7 +1024,7 @@ class LocalizationsGenerator {
// Generate the AppLocalizations class, its LocalizationsDelegate subclass, // Generate the AppLocalizations class, its LocalizationsDelegate subclass,
// and all AppLocalizations subclasses for every locale. This method by // and all AppLocalizations subclasses for every locale. This method by
// itself does not generate the output files. // itself does not generate the output files.
void _generateCode() { String _generateCode() {
bool isBaseClassLocale(LocaleInfo locale, String language) { bool isBaseClassLocale(LocaleInfo locale, String language) {
return locale.languageCode == language return locale.languageCode == language
&& locale.countryCode == null && locale.countryCode == null
@ -1100,8 +1046,8 @@ class LocalizationsGenerator {
final Iterable<String> supportedLocalesCode = supportedLocales.map((LocaleInfo locale) { final Iterable<String> supportedLocalesCode = supportedLocales.map((LocaleInfo locale) {
final String languageCode = locale.languageCode; final String languageCode = locale.languageCode;
final String countryCode = locale.countryCode; final String? countryCode = locale.countryCode;
final String scriptCode = locale.scriptCode; final String? scriptCode = locale.scriptCode;
if (countryCode == null && scriptCode == null) { if (countryCode == null && scriptCode == null) {
return 'Locale(\'$languageCode\')'; return 'Locale(\'$languageCode\')';
@ -1131,8 +1077,8 @@ class LocalizationsGenerator {
className, className,
outputFileName, outputFileName,
header, header,
_allBundles.bundleFor(locale), _allBundles.bundleFor(locale)!,
_allBundles.bundleFor(_templateArbLocale), _allBundles.bundleFor(_templateArbLocale)!,
_allMessages, _allMessages,
); );
@ -1143,7 +1089,7 @@ class LocalizationsGenerator {
final Iterable<String> subclasses = localesForLanguage.map<String>((LocaleInfo locale) { final Iterable<String> subclasses = localesForLanguage.map<String>((LocaleInfo locale) {
return _generateSubclass( return _generateSubclass(
className, className,
_allBundles.bundleFor(locale), _allBundles.bundleFor(locale)!,
_allMessages, _allMessages,
); );
}); });
@ -1175,7 +1121,7 @@ class LocalizationsGenerator {
fileName: fileName, fileName: fileName,
); );
_generatedLocalizationsFile = fileTemplate return fileTemplate
.replaceAll('@(header)', header) .replaceAll('@(header)', header)
.replaceAll('@(class)', className) .replaceAll('@(class)', className)
.replaceAll('@(methods)', _allMessages.map((Message message) => generateBaseClassMethod(message, _templateArbLocale)).join('\n')) .replaceAll('@(methods)', _allMessages.map((Message message) => generateBaseClassMethod(message, _templateArbLocale)).join('\n'))
@ -1194,7 +1140,7 @@ class LocalizationsGenerator {
void writeOutputFiles(Logger logger, { bool isFromYaml = false }) { void writeOutputFiles(Logger logger, { bool isFromYaml = false }) {
// First, generate the string contents of all necessary files. // First, generate the string contents of all necessary files.
_generateCode(); final String generatedLocalizationsFile = _generateCode();
// A pubspec.yaml file is required when using a synthetic package. If it does not // A pubspec.yaml file is required when using a synthetic package. If it does not
// exist, create a blank one. // exist, create a blank one.
@ -1228,10 +1174,10 @@ class LocalizationsGenerator {
} }
}); });
baseOutputFile.writeAsStringSync(_generatedLocalizationsFile); baseOutputFile.writeAsStringSync(generatedLocalizationsFile);
final File? messagesFile = untranslatedMessagesFile;
if (untranslatedMessagesFile != null) { if (messagesFile != null) {
_generateUntranslatedMessagesFile(logger); _generateUntranslatedMessagesFile(logger, messagesFile);
} else if (_unimplementedMessages.isNotEmpty) { } else if (_unimplementedMessages.isNotEmpty) {
_unimplementedMessages.forEach((LocaleInfo locale, List<String> messages) { _unimplementedMessages.forEach((LocaleInfo locale, List<String> messages) {
logger.printStatus('"$locale": ${messages.length} untranslated message(s).'); logger.printStatus('"$locale": ${messages.length} untranslated message(s).');
@ -1257,16 +1203,16 @@ class LocalizationsGenerator {
'need to be translated.' 'need to be translated.'
); );
} }
final File? inputsAndOutputsListFileLocal = inputsAndOutputsListFile;
if (inputsAndOutputsListFile != null) { if (inputsAndOutputsListFileLocal != null) {
_outputFileList.add(baseOutputFile.absolute.path); _outputFileList.add(baseOutputFile.absolute.path);
// Generate a JSON file containing the inputs and outputs of the gen_l10n script. // Generate a JSON file containing the inputs and outputs of the gen_l10n script.
if (!inputsAndOutputsListFile.existsSync()) { if (!inputsAndOutputsListFileLocal.existsSync()) {
inputsAndOutputsListFile.createSync(recursive: true); inputsAndOutputsListFileLocal.createSync(recursive: true);
} }
inputsAndOutputsListFile.writeAsStringSync( inputsAndOutputsListFileLocal.writeAsStringSync(
json.encode(<String, Object> { json.encode(<String, Object> {
'inputs': _inputFileList, 'inputs': _inputFileList,
'outputs': _outputFileList, 'outputs': _outputFileList,
@ -1275,13 +1221,7 @@ class LocalizationsGenerator {
} }
} }
void _generateUntranslatedMessagesFile(Logger logger) { void _generateUntranslatedMessagesFile(Logger logger, File untranslatedMessagesFile) {
if (logger == null) {
throw L10nException(
'Logger must be defined when generating untranslated messages file.'
);
}
if (_unimplementedMessages.isEmpty) { if (_unimplementedMessages.isEmpty) {
untranslatedMessagesFile.writeAsStringSync('{}'); untranslatedMessagesFile.writeAsStringSync('{}');
if (inputsAndOutputsListFile != null) { if (inputsAndOutputsListFile != null) {

View file

@ -187,7 +187,7 @@ class OptionalParameter {
// } // }
// //
class Placeholder { class Placeholder {
Placeholder(this.resourceId, this.name, Map<String, dynamic> attributes) Placeholder(this.resourceId, this.name, Map<String, Object?> attributes)
: assert(resourceId != null), : assert(resourceId != null),
assert(name != null), assert(name != null),
example = _stringAttribute(resourceId, name, attributes, 'example'), example = _stringAttribute(resourceId, name, attributes, 'example'),
@ -212,10 +212,10 @@ class Placeholder {
static String? _stringAttribute( static String? _stringAttribute(
String resourceId, String resourceId,
String name, String name,
Map<String, dynamic> attributes, Map<String, Object?> attributes,
String attributeName, String attributeName,
) { ) {
final dynamic value = attributes[attributeName]; final Object? value = attributes[attributeName];
if (value == null) { if (value == null) {
return null; return null;
} }
@ -231,9 +231,9 @@ class Placeholder {
static List<OptionalParameter> _optionalParameters( static List<OptionalParameter> _optionalParameters(
String resourceId, String resourceId,
String name, String name,
Map<String, dynamic> attributes Map<String, Object?> attributes
) { ) {
final dynamic value = attributes['optionalParameters']; final Object? value = attributes['optionalParameters'];
if (value == null) { if (value == null) {
return <OptionalParameter>[]; return <OptionalParameter>[];
} }
@ -267,7 +267,7 @@ class Placeholder {
// localized string to be shown for the template ARB file's locale. // localized string to be shown for the template ARB file's locale.
// The docs for the Placeholder explain how placeholder entries are defined. // The docs for the Placeholder explain how placeholder entries are defined.
class Message { class Message {
Message(Map<String, dynamic> bundle, this.resourceId, bool isResourceAttributeRequired) Message(Map<String, Object?> bundle, this.resourceId, bool isResourceAttributeRequired)
: assert(bundle != null), : assert(bundle != null),
assert(resourceId != null && resourceId.isNotEmpty), assert(resourceId != null && resourceId.isNotEmpty),
value = _value(bundle, resourceId), value = _value(bundle, resourceId),
@ -298,23 +298,23 @@ class Message {
); );
} }
static String _value(Map<String, dynamic> bundle, String resourceId) { static String _value(Map<String, Object?> bundle, String resourceId) {
final dynamic value = bundle[resourceId]; final Object? value = bundle[resourceId];
if (value == null) { if (value == null) {
throw L10nException('A value for resource "$resourceId" was not found.'); throw L10nException('A value for resource "$resourceId" was not found.');
} }
if (value is! String) { if (value is! String) {
throw L10nException('The value of "$resourceId" is not a string.'); throw L10nException('The value of "$resourceId" is not a string.');
} }
return bundle[resourceId] as String; return value;
} }
static Map<String, dynamic> _attributes( static Map<String, Object?>? _attributes(
Map<String, dynamic> bundle, Map<String, Object?> bundle,
String resourceId, String resourceId,
bool isResourceAttributeRequired, bool isResourceAttributeRequired,
) { ) {
final dynamic attributes = bundle['@$resourceId']; final Object? attributes = bundle['@$resourceId'];
if (isResourceAttributeRequired) { if (isResourceAttributeRequired) {
if (attributes == null) { if (attributes == null) {
throw L10nException( throw L10nException(
@ -324,7 +324,7 @@ class Message {
} }
} }
if (attributes != null && attributes is! Map<String, dynamic>) { if (attributes != null && attributes is! Map<String, Object?>) {
throw L10nException( throw L10nException(
'The resource attribute "@$resourceId" is not a properly formatted Map. ' 'The resource attribute "@$resourceId" is not a properly formatted Map. '
'Ensure that it is a map with keys that are strings.' 'Ensure that it is a map with keys that are strings.'
@ -340,20 +340,20 @@ class Message {
); );
} }
return attributes as Map<String, dynamic>; return attributes as Map<String, Object?>?;
} }
static String? _description( static String? _description(
Map<String, dynamic> bundle, Map<String, Object?> bundle,
String resourceId, String resourceId,
bool isResourceAttributeRequired, bool isResourceAttributeRequired,
) { ) {
final Map<String, dynamic> resourceAttributes = _attributes(bundle, resourceId, isResourceAttributeRequired); final Map<String, Object?>? resourceAttributes = _attributes(bundle, resourceId, isResourceAttributeRequired);
if (resourceAttributes == null) { if (resourceAttributes == null) {
return null; return null;
} }
final dynamic value = resourceAttributes['description']; final Object? value = resourceAttributes['description'];
if (value == null) { if (value == null) {
return null; return null;
} }
@ -366,27 +366,27 @@ class Message {
} }
static List<Placeholder> _placeholders( static List<Placeholder> _placeholders(
Map<String, dynamic> bundle, Map<String, Object?> bundle,
String resourceId, String resourceId,
bool isResourceAttributeRequired, bool isResourceAttributeRequired,
) { ) {
final Map<String, dynamic> resourceAttributes = _attributes(bundle, resourceId, isResourceAttributeRequired); final Map<String, Object?>? resourceAttributes = _attributes(bundle, resourceId, isResourceAttributeRequired);
if (resourceAttributes == null) { if (resourceAttributes == null) {
return <Placeholder>[]; return <Placeholder>[];
} }
final dynamic allPlaceholdersMap = resourceAttributes['placeholders']; final Object? allPlaceholdersMap = resourceAttributes['placeholders'];
if (allPlaceholdersMap == null) { if (allPlaceholdersMap == null) {
return <Placeholder>[]; return <Placeholder>[];
} }
if (allPlaceholdersMap is! Map<String, dynamic>) { if (allPlaceholdersMap is! Map<String, Object?>) {
throw L10nException( throw L10nException(
'The "placeholders" attribute for message $resourceId, is not ' 'The "placeholders" attribute for message $resourceId, is not '
'properly formatted. Ensure that it is a map with string valued keys.' 'properly formatted. Ensure that it is a map with string valued keys.'
); );
} }
return allPlaceholdersMap.keys.map<Placeholder>((String placeholderName) { return allPlaceholdersMap.keys.map<Placeholder>((String placeholderName) {
final dynamic value = allPlaceholdersMap[placeholderName]; final Object? value = allPlaceholdersMap[placeholderName];
if (value is! Map<String, dynamic>) { if (value is! Map<String, Object?>) {
throw L10nException( throw L10nException(
'The value of the "$placeholderName" placeholder attribute for message ' 'The value of the "$placeholderName" placeholder attribute for message '
'"$resourceId", is not properly formatted. Ensure that it is a map ' '"$resourceId", is not properly formatted. Ensure that it is a map '
@ -403,9 +403,9 @@ class AppResourceBundle {
factory AppResourceBundle(File file) { factory AppResourceBundle(File file) {
assert(file != null); assert(file != null);
// Assuming that the caller has verified that the file exists and is readable. // Assuming that the caller has verified that the file exists and is readable.
Map<String, dynamic> resources; Map<String, Object?> resources;
try { try {
resources = json.decode(file.readAsStringSync()) as Map<String, dynamic>; resources = json.decode(file.readAsStringSync()) as Map<String, Object?>;
} on FormatException catch (e) { } on FormatException catch (e) {
throw L10nException( throw L10nException(
'The arb file ${file.path} has the following formatting issue: \n' 'The arb file ${file.path} has the following formatting issue: \n'
@ -413,7 +413,7 @@ class AppResourceBundle {
); );
} }
String localeString = resources['@@locale'] as String; String? localeString = resources['@@locale'] as String?;
// Look for the first instance of an ISO 639-1 language code, matching exactly. // Look for the first instance of an ISO 639-1 language code, matching exactly.
final String fileName = file.fileSystem.path.basenameWithoutExtension(file.path); final String fileName = file.fileSystem.path.basenameWithoutExtension(file.path);
@ -470,10 +470,10 @@ class AppResourceBundle {
final File file; final File file;
final LocaleInfo locale; final LocaleInfo locale;
final Map<String, dynamic> resources; final Map<String, Object?> resources;
final Iterable<String> resourceIds; final Iterable<String> resourceIds;
String translationFor(Message message) => resources[message.resourceId] as String; String? translationFor(Message message) => resources[message.resourceId] as String?;
@override @override
String toString() { String toString() {

View file

@ -98,75 +98,12 @@ void main() {
); );
}); });
testWithoutContext('setInputDirectory fails if input string is null', () { testWithoutContext('setting className fails if input string is empty', () {
_standardFlutterDirectoryL10nSetup(fs); _standardFlutterDirectoryL10nSetup(fs);
try { try {
LocalizationsGenerator.inputDirectoryFromPath(fs, null, fs.directory('bogus')); LocalizationsGenerator.classNameFromString('');
} on L10nException catch (e) { } on L10nException catch (e) {
expect(e.message, contains('cannot be null')); expect(e.message, contains('cannot be empty'));
return;
}
fail(
'LocalizationsGenerator.setInputDirectory should fail if the '
'input string is null.'
);
});
testWithoutContext(
'setOutputDirectory fails if output string is null while not using the '
'synthetic package option',
() {
_standardFlutterDirectoryL10nSetup(fs);
try {
LocalizationsGenerator.outputDirectoryFromPath(fs, null, false, null);
} on L10nException catch (e) {
expect(e.message, contains('cannot be null'));
return;
}
fail(
'LocalizationsGenerator.setOutputDirectory should fail if the '
'input string is null.'
);
},
);
testWithoutContext('setTemplateArbFile fails if inputDirectory is null', () {
try {
LocalizationsGenerator.templateArbFileFromFileName(defaultTemplateArbFileName, null);
} on L10nException catch (e) {
expect(e.message, contains('cannot be null'));
return;
}
fail(
'LocalizationsGenerator.setTemplateArbFile should fail if the '
'inputDirectory is not specified.'
);
});
testWithoutContext('setTemplateArbFile fails if templateArbFileName is null', () {
_standardFlutterDirectoryL10nSetup(fs);
try {
LocalizationsGenerator.templateArbFileFromFileName(null, fs.directory('bogus'));
} on L10nException catch (e) {
expect(e.message, contains('cannot be null'));
return;
}
fail(
'LocalizationsGenerator.setTemplateArbFile should fail if the '
'templateArbFileName passed in is null.'
);
});
testWithoutContext('setting className fails if input string is null', () {
_standardFlutterDirectoryL10nSetup(fs);
try {
LocalizationsGenerator.classNameFromString(null);
} on L10nException catch (e) {
expect(e.message, contains('cannot be null'));
return; return;
} }