diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart index 0ea3050e6152..d17988dd1e39 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart @@ -566,13 +566,18 @@ class Message { } } -// Represents the contents of one ARB file. +/// Represents the contents of one ARB file. class AppResourceBundle { + /// Assuming that the caller has verified that the file exists and is readable. factory AppResourceBundle(File file) { - // Assuming that the caller has verified that the file exists and is readable. - Map resources; + final Map resources; try { - resources = json.decode(file.readAsStringSync()) as Map; + final String content = file.readAsStringSync().trim(); + if (content.isEmpty) { + resources = {}; + } else { + resources = json.decode(content) as Map; + } } on FormatException catch (e) { throw L10nException( 'The arb file ${file.path} has the following formatting issue: \n' @@ -657,20 +662,26 @@ class AppResourceBundleCollection { final RegExp filenameRE = RegExp(r'(\w+)\.arb$'); final Map localeToBundle = {}; final Map> languageToLocales = >{}; - final List files = directory.listSync().whereType().toList()..sort(sortFilesByPath); + // We require the list of files to be sorted so that + // "languageToLocales[bundle.locale.languageCode]" is not null + // by the time we handle locales with country codes. + final List files = directory + .listSync() + .whereType() + .where((File e) => filenameRE.hasMatch(e.path)) + .toList() + ..sort(sortFilesByPath); for (final File file in files) { - if (filenameRE.hasMatch(file.path)) { - final AppResourceBundle bundle = AppResourceBundle(file); - if (localeToBundle[bundle.locale] != null) { - throw L10nException( - "Multiple arb files with the same '${bundle.locale}' locale detected. \n" - 'Ensure that there is exactly one arb file for each locale.' - ); - } - localeToBundle[bundle.locale] = bundle; - languageToLocales[bundle.locale.languageCode] ??= []; - languageToLocales[bundle.locale.languageCode]!.add(bundle.locale); + final AppResourceBundle bundle = AppResourceBundle(file); + if (localeToBundle[bundle.locale] != null) { + throw L10nException( + "Multiple arb files with the same '${bundle.locale}' locale detected. \n" + 'Ensure that there is exactly one arb file for each locale.' + ); } + localeToBundle[bundle.locale] = bundle; + languageToLocales[bundle.locale.languageCode] ??= []; + languageToLocales[bundle.locale.languageCode]!.add(bundle.locale); } languageToLocales.forEach((String language, List listOfCorrespondingLocales) { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart index e2556a6e58c7..263254ddc8bd 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart @@ -10,6 +10,7 @@ import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/targets/localizations.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/generate_localizations.dart'; +import 'package:flutter_tools/src/localizations/gen_l10n_types.dart'; import '../../integration.shard/test_data/basic_project.dart'; import '../../src/common.dart'; @@ -501,4 +502,33 @@ format: true throwsToolExit(message: 'Unexpected positional argument "false".') ); }); + + group(AppResourceBundle, () { + testWithoutContext("can be parsed without FormatException when it's content is empty", () { + final File arbFile = fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb')) + ..createSync(recursive: true); + expect(AppResourceBundle(arbFile), isA()); + }); + + testUsingContext("would not fail the gen-l10n command when it's content is empty", () async { + fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb')).createSync(recursive: true); + final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync(); + pubspecFile.writeAsStringSync(BasicProjectWithFlutterGen().pubspec); + final GenerateLocalizationsCommand command = GenerateLocalizationsCommand( + fileSystem: fileSystem, + logger: logger, + artifacts: artifacts, + processManager: processManager, + ); + await createTestCommandRunner(command).run(['gen-l10n']); + + final Directory outputDirectory = fileSystem.directory(fileSystem.path.join('.dart_tool', 'flutter_gen', 'gen_l10n')); + expect(outputDirectory.existsSync(), true); + expect(outputDirectory.childFile('app_localizations_en.dart').existsSync(), true); + expect(outputDirectory.childFile('app_localizations.dart').existsSync(), true); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.any(), + }); + }); }