mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[gen-l10n] Add nullable-getter flag (#79263)
This commit is contained in:
parent
cf6d4a35a5
commit
134aa8e9da
@ -172,6 +172,17 @@ class GenerateLocalizationsCommand extends FlutterCommand {
|
||||
'\n'
|
||||
'Resource attributes are still required for plural messages.'
|
||||
);
|
||||
argParser.addFlag(
|
||||
'nullable-getter',
|
||||
help: 'Whether or not the localizations class getter is nullable.\n'
|
||||
'\n'
|
||||
'By default, this value is set to true so that '
|
||||
'Localizations.of(context) returns a nullable value '
|
||||
'for backwards compatibility. If this value is set to true, then '
|
||||
'a null check is performed on the returned value of '
|
||||
'Localizations.of(context), removing the need for null checking in '
|
||||
'user code.'
|
||||
);
|
||||
}
|
||||
|
||||
final FileSystem _fileSystem;
|
||||
@ -220,6 +231,7 @@ class GenerateLocalizationsCommand extends FlutterCommand {
|
||||
final bool useSyntheticPackage = boolArg('synthetic-package');
|
||||
final String projectPathString = stringArg('project-dir');
|
||||
final bool areResourceAttributesRequired = boolArg('required-resource-attributes');
|
||||
final bool usesNullableGetter = boolArg('nullable-getter');
|
||||
|
||||
final LocalizationsGenerator localizationsGenerator = LocalizationsGenerator(_fileSystem);
|
||||
|
||||
@ -242,6 +254,7 @@ class GenerateLocalizationsCommand extends FlutterCommand {
|
||||
projectPathString: projectPathString,
|
||||
areResourceAttributesRequired: areResourceAttributesRequired,
|
||||
untranslatedMessagesFile: untranslatedMessagesFile,
|
||||
usesNullableGetter: usesNullableGetter,
|
||||
)
|
||||
..loadResources()
|
||||
..writeOutputFiles(_logger);
|
||||
|
||||
@ -66,6 +66,7 @@ void generateLocalizations({
|
||||
useSyntheticPackage: options.useSyntheticPackage ?? true,
|
||||
areResourceAttributesRequired: options.areResourceAttributesRequired ?? false,
|
||||
untranslatedMessagesFile: options?.untranslatedMessagesFile?.toFilePath(),
|
||||
usesNullableGetter: options?.usesNullableGetter ?? true,
|
||||
)
|
||||
..loadResources()
|
||||
..writeOutputFiles(logger, isFromYaml: true);
|
||||
@ -545,6 +546,10 @@ class LocalizationsGenerator {
|
||||
AppResourceBundleCollection _allBundles;
|
||||
LocaleInfo _templateArbLocale;
|
||||
bool _useSyntheticPackage = true;
|
||||
// Used to decide if the generated code is nullable or not
|
||||
// (whether AppLocalizations? or AppLocalizations is returned from
|
||||
// `static {name}Localizations{?} of (BuildContext context))`
|
||||
bool _usesNullableGetter = true;
|
||||
|
||||
/// The directory that contains the project's arb files, as well as the
|
||||
/// header file, if specified.
|
||||
@ -689,8 +694,10 @@ class LocalizationsGenerator {
|
||||
String projectPathString,
|
||||
bool areResourceAttributesRequired = false,
|
||||
String untranslatedMessagesFile,
|
||||
bool usesNullableGetter = true,
|
||||
}) {
|
||||
_useSyntheticPackage = useSyntheticPackage;
|
||||
_usesNullableGetter = usesNullableGetter;
|
||||
setProjectDir(projectPathString);
|
||||
setInputDirectory(inputPathString);
|
||||
setOutputDirectory(outputPathString ?? inputPathString);
|
||||
@ -1162,7 +1169,9 @@ class LocalizationsGenerator {
|
||||
.replaceAll('@(supportedLanguageCodes)', supportedLanguageCodes.join(', '))
|
||||
.replaceAll('@(messageClassImports)', sortedClassImports.join('\n'))
|
||||
.replaceAll('@(delegateClass)', delegateClass)
|
||||
.replaceAll('@(requiresIntlImport)', _containsPluralMessage() ? "import 'package:intl/intl.dart' as intl;" : '');
|
||||
.replaceAll('@(requiresIntlImport)', _containsPluralMessage() ? "import 'package:intl/intl.dart' as intl;" : '')
|
||||
.replaceAll('@(canBeNullable)', _usesNullableGetter ? '?' : '')
|
||||
.replaceAll('@(needsNullCheck)', _usesNullableGetter ? '' : '!');
|
||||
}
|
||||
|
||||
bool _containsPluralMessage() => _allMessages.any((Message message) => message.isPlural);
|
||||
|
||||
@ -77,8 +77,8 @@ abstract class @(class) {
|
||||
// ignore: unused_field
|
||||
final String localeName;
|
||||
|
||||
static @(class)? of(BuildContext context) {
|
||||
return Localizations.of<@(class)>(context, @(class));
|
||||
static @(class)@(canBeNullable) of(BuildContext context) {
|
||||
return Localizations.of<@(class)>(context, @(class))@(needsNullCheck);
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<@(class)> delegate = _@(class)Delegate();
|
||||
|
||||
@ -304,6 +304,7 @@ class LocalizationOptions {
|
||||
this.deferredLoading,
|
||||
this.useSyntheticPackage = true,
|
||||
this.areResourceAttributesRequired = false,
|
||||
this.usesNullableGetter = true,
|
||||
}) : assert(useSyntheticPackage != null);
|
||||
|
||||
/// The `--arb-dir` argument.
|
||||
@ -365,6 +366,11 @@ class LocalizationOptions {
|
||||
/// Whether to require all resource ids to contain a corresponding
|
||||
/// resource attribute.
|
||||
final bool areResourceAttributesRequired;
|
||||
|
||||
/// The `nullable-getter` argument.
|
||||
///
|
||||
/// Whether or not the localizations class getter is nullable.
|
||||
final bool usesNullableGetter;
|
||||
}
|
||||
|
||||
/// Parse the localizations configuration options from [file].
|
||||
@ -398,6 +404,7 @@ LocalizationOptions parseLocalizationsOptions({
|
||||
deferredLoading: _tryReadBool(yamlNode, 'use-deferred-loading', logger),
|
||||
useSyntheticPackage: _tryReadBool(yamlNode, 'synthetic-package', logger) ?? true,
|
||||
areResourceAttributesRequired: _tryReadBool(yamlNode, 'required-resource-attributes', logger) ?? false,
|
||||
usesNullableGetter: _tryReadBool(yamlNode, 'nullable-getter', logger) ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ void main() {
|
||||
untranslatedMessagesFile: Uri.file('untranslated'),
|
||||
useSyntheticPackage: false,
|
||||
areResourceAttributesRequired: true,
|
||||
usesNullableGetter: false,
|
||||
);
|
||||
|
||||
final LocalizationsGenerator mockLocalizationsGenerator = MockLocalizationsGenerator();
|
||||
@ -70,6 +71,7 @@ void main() {
|
||||
projectPathString: '/',
|
||||
areResourceAttributesRequired: true,
|
||||
untranslatedMessagesFile: 'untranslated',
|
||||
usesNullableGetter: false,
|
||||
),
|
||||
).called(1);
|
||||
verify(mockLocalizationsGenerator.loadResources()).called(1);
|
||||
@ -151,6 +153,9 @@ header-file: header
|
||||
header: HEADER
|
||||
use-deferred-loading: true
|
||||
preferred-supported-locales: en_US
|
||||
synthetic-package: false
|
||||
required-resource-attributes: false
|
||||
nullable-getter: false
|
||||
''');
|
||||
|
||||
final LocalizationOptions options = parseLocalizationsOptions(
|
||||
@ -167,6 +172,9 @@ preferred-supported-locales: en_US
|
||||
expect(options.header, 'HEADER');
|
||||
expect(options.deferredLoading, true);
|
||||
expect(options.preferredSupportedLocales, <String>['en_US']);
|
||||
expect(options.useSyntheticPackage, false);
|
||||
expect(options.areResourceAttributesRequired, false);
|
||||
expect(options.usesNullableGetter, false);
|
||||
});
|
||||
|
||||
testWithoutContext('parseLocalizationsOptions handles preferredSupportedLocales as list', () async {
|
||||
|
||||
@ -800,6 +800,82 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'generates nullable localizations class getter via static `of` method '
|
||||
'by default',
|
||||
() {
|
||||
_standardFlutterDirectoryL10nSetup(fs);
|
||||
|
||||
LocalizationsGenerator generator;
|
||||
try {
|
||||
generator = LocalizationsGenerator(fs);
|
||||
generator
|
||||
..initialize(
|
||||
inputPathString: defaultL10nPathString,
|
||||
outputPathString: fs.path.join('lib', 'l10n', 'output'),
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
outputFileString: defaultOutputFileString,
|
||||
classNameString: defaultClassNameString,
|
||||
useSyntheticPackage: false,
|
||||
)
|
||||
..loadResources()
|
||||
..writeOutputFiles(BufferLogger.test());
|
||||
} on L10nException catch (e) {
|
||||
fail('Generating output should not fail: \n${e.message}');
|
||||
}
|
||||
|
||||
final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output');
|
||||
expect(outputDirectory.existsSync(), isTrue);
|
||||
expect(outputDirectory.childFile('output-localization-file.dart').existsSync(), isTrue);
|
||||
expect(
|
||||
outputDirectory.childFile('output-localization-file.dart').readAsStringSync(),
|
||||
contains('static AppLocalizations? of(BuildContext context)'),
|
||||
);
|
||||
expect(
|
||||
outputDirectory.childFile('output-localization-file.dart').readAsStringSync(),
|
||||
contains('return Localizations.of<AppLocalizations>(context, AppLocalizations);'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'can generate non-nullable localizations class getter via static `of` method ',
|
||||
() {
|
||||
_standardFlutterDirectoryL10nSetup(fs);
|
||||
|
||||
LocalizationsGenerator generator;
|
||||
try {
|
||||
generator = LocalizationsGenerator(fs);
|
||||
generator
|
||||
..initialize(
|
||||
inputPathString: defaultL10nPathString,
|
||||
outputPathString: fs.path.join('lib', 'l10n', 'output'),
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
outputFileString: defaultOutputFileString,
|
||||
classNameString: defaultClassNameString,
|
||||
useSyntheticPackage: false,
|
||||
usesNullableGetter: false,
|
||||
)
|
||||
..loadResources()
|
||||
..writeOutputFiles(BufferLogger.test());
|
||||
} on L10nException catch (e) {
|
||||
fail('Generating output should not fail: \n${e.message}');
|
||||
}
|
||||
|
||||
final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output');
|
||||
expect(outputDirectory.existsSync(), isTrue);
|
||||
expect(outputDirectory.childFile('output-localization-file.dart').existsSync(), isTrue);
|
||||
expect(
|
||||
outputDirectory.childFile('output-localization-file.dart').readAsStringSync(),
|
||||
contains('static AppLocalizations of(BuildContext context)'),
|
||||
);
|
||||
expect(
|
||||
outputDirectory.childFile('output-localization-file.dart').readAsStringSync(),
|
||||
contains('return Localizations.of<AppLocalizations>(context, AppLocalizations)!;'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext('creates list of inputs and outputs when file path is specified', () {
|
||||
_standardFlutterDirectoryL10nSetup(fs);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user