Skip to content

Commit f2db93d

Browse files
authored
🐛 Treat empty ARB content as empty map when decoding (#131242)
Fixes #128932.
1 parent d250fa6 commit f2db93d

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -566,13 +566,18 @@ class Message {
566566
}
567567
}
568568

569-
// Represents the contents of one ARB file.
569+
/// Represents the contents of one ARB file.
570570
class AppResourceBundle {
571+
/// Assuming that the caller has verified that the file exists and is readable.
571572
factory AppResourceBundle(File file) {
572-
// Assuming that the caller has verified that the file exists and is readable.
573-
Map<String, Object?> resources;
573+
final Map<String, Object?> resources;
574574
try {
575-
resources = json.decode(file.readAsStringSync()) as Map<String, Object?>;
575+
final String content = file.readAsStringSync().trim();
576+
if (content.isEmpty) {
577+
resources = <String, Object?>{};
578+
} else {
579+
resources = json.decode(content) as Map<String, Object?>;
580+
}
576581
} on FormatException catch (e) {
577582
throw L10nException(
578583
'The arb file ${file.path} has the following formatting issue: \n'
@@ -657,20 +662,26 @@ class AppResourceBundleCollection {
657662
final RegExp filenameRE = RegExp(r'(\w+)\.arb$');
658663
final Map<LocaleInfo, AppResourceBundle> localeToBundle = <LocaleInfo, AppResourceBundle>{};
659664
final Map<String, List<LocaleInfo>> languageToLocales = <String, List<LocaleInfo>>{};
660-
final List<File> files = directory.listSync().whereType<File>().toList()..sort(sortFilesByPath);
665+
// We require the list of files to be sorted so that
666+
// "languageToLocales[bundle.locale.languageCode]" is not null
667+
// by the time we handle locales with country codes.
668+
final List<File> files = directory
669+
.listSync()
670+
.whereType<File>()
671+
.where((File e) => filenameRE.hasMatch(e.path))
672+
.toList()
673+
..sort(sortFilesByPath);
661674
for (final File file in files) {
662-
if (filenameRE.hasMatch(file.path)) {
663-
final AppResourceBundle bundle = AppResourceBundle(file);
664-
if (localeToBundle[bundle.locale] != null) {
665-
throw L10nException(
666-
"Multiple arb files with the same '${bundle.locale}' locale detected. \n"
667-
'Ensure that there is exactly one arb file for each locale.'
668-
);
669-
}
670-
localeToBundle[bundle.locale] = bundle;
671-
languageToLocales[bundle.locale.languageCode] ??= <LocaleInfo>[];
672-
languageToLocales[bundle.locale.languageCode]!.add(bundle.locale);
675+
final AppResourceBundle bundle = AppResourceBundle(file);
676+
if (localeToBundle[bundle.locale] != null) {
677+
throw L10nException(
678+
"Multiple arb files with the same '${bundle.locale}' locale detected. \n"
679+
'Ensure that there is exactly one arb file for each locale.'
680+
);
673681
}
682+
localeToBundle[bundle.locale] = bundle;
683+
languageToLocales[bundle.locale.languageCode] ??= <LocaleInfo>[];
684+
languageToLocales[bundle.locale.languageCode]!.add(bundle.locale);
674685
}
675686

676687
languageToLocales.forEach((String language, List<LocaleInfo> listOfCorrespondingLocales) {

packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/build_system/build_system.dart';
1010
import 'package:flutter_tools/src/build_system/targets/localizations.dart';
1111
import 'package:flutter_tools/src/cache.dart';
1212
import 'package:flutter_tools/src/commands/generate_localizations.dart';
13+
import 'package:flutter_tools/src/localizations/gen_l10n_types.dart';
1314

1415
import '../../integration.shard/test_data/basic_project.dart';
1516
import '../../src/common.dart';
@@ -501,4 +502,33 @@ format: true
501502
throwsToolExit(message: 'Unexpected positional argument "false".')
502503
);
503504
});
505+
506+
group(AppResourceBundle, () {
507+
testWithoutContext("can be parsed without FormatException when it's content is empty", () {
508+
final File arbFile = fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb'))
509+
..createSync(recursive: true);
510+
expect(AppResourceBundle(arbFile), isA<AppResourceBundle>());
511+
});
512+
513+
testUsingContext("would not fail the gen-l10n command when it's content is empty", () async {
514+
fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb')).createSync(recursive: true);
515+
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
516+
pubspecFile.writeAsStringSync(BasicProjectWithFlutterGen().pubspec);
517+
final GenerateLocalizationsCommand command = GenerateLocalizationsCommand(
518+
fileSystem: fileSystem,
519+
logger: logger,
520+
artifacts: artifacts,
521+
processManager: processManager,
522+
);
523+
await createTestCommandRunner(command).run(<String>['gen-l10n']);
524+
525+
final Directory outputDirectory = fileSystem.directory(fileSystem.path.join('.dart_tool', 'flutter_gen', 'gen_l10n'));
526+
expect(outputDirectory.existsSync(), true);
527+
expect(outputDirectory.childFile('app_localizations_en.dart').existsSync(), true);
528+
expect(outputDirectory.childFile('app_localizations.dart').existsSync(), true);
529+
}, overrides: <Type, Generator>{
530+
FileSystem: () => fileSystem,
531+
ProcessManager: () => FakeProcessManager.any(),
532+
});
533+
});
504534
}

0 commit comments

Comments
 (0)