🔥 Do not format the messages file for `gen-l10n` (#119596)

* 🔥 Do not format the messages file

* ✅ Add test

* ✅ Add the link to the test

* ✅ Respect the path separator from the file system

* ✅ Add the untranslated messages file existence test
diff --git a/packages/flutter_tools/lib/src/commands/generate_localizations.dart b/packages/flutter_tools/lib/src/commands/generate_localizations.dart
index 0cffa44..233b693 100644
--- a/packages/flutter_tools/lib/src/commands/generate_localizations.dart
+++ b/packages/flutter_tools/lib/src/commands/generate_localizations.dart
@@ -223,7 +223,8 @@
 
   @override
   Future<FlutterCommandResult> runCommand() async {
-    List<String> outputFileList;
+    final List<String> outputFileList;
+    File? untranslatedMessagesFile;
 
     bool format = boolArg('format') ?? false;
 
@@ -238,19 +239,21 @@
         'To use the command line arguments, delete the l10n.yaml file in the '
         'Flutter project.\n\n'
       );
-      outputFileList = generateLocalizations(
+      final LocalizationsGenerator generator = generateLocalizations(
         logger: _logger,
         options: options,
         projectDir: _fileSystem.currentDirectory,
         fileSystem: _fileSystem,
-      ).outputFileList;
+      );
+      outputFileList = generator.outputFileList;
+      untranslatedMessagesFile = generator.untranslatedMessagesFile;
       format = format || options.format;
     } else {
       final String inputPathString = stringArgDeprecated('arb-dir')!; // Has default value, cannot be null.
       final String? outputPathString = stringArgDeprecated('output-dir');
       final String outputFileString = stringArgDeprecated('output-localization-file')!; // Has default value, cannot be null.
       final String templateArbFileName = stringArgDeprecated('template-arb-file')!; // Has default value, cannot be null.
-      final String? untranslatedMessagesFile = stringArgDeprecated('untranslated-messages-file');
+      final String? untranslatedMessagesFilePath = stringArgDeprecated('untranslated-messages-file');
       final String classNameString = stringArgDeprecated('output-class')!; // Has default value, cannot be null.
       final List<String> preferredSupportedLocales = stringsArg('preferred-supported-locales');
       final String? headerString = stringArgDeprecated('header');
@@ -267,7 +270,7 @@
       precacheLanguageAndRegionTags();
 
       try {
-        outputFileList = (LocalizationsGenerator(
+        final LocalizationsGenerator generator = LocalizationsGenerator(
           fileSystem: _fileSystem,
           inputPathString: inputPathString,
           outputPathString: outputPathString,
@@ -282,15 +285,16 @@
           useSyntheticPackage: useSyntheticPackage,
           projectPathString: projectPathString,
           areResourceAttributesRequired: areResourceAttributesRequired,
-          untranslatedMessagesFile: untranslatedMessagesFile,
+          untranslatedMessagesFile: untranslatedMessagesFilePath,
           usesNullableGetter: usesNullableGetter,
           useEscaping: useEscaping,
           logger: _logger,
           suppressWarnings: suppressWarnings,
         )
           ..loadResources()
-          ..writeOutputFiles())
-          .outputFileList;
+          ..writeOutputFiles();
+        outputFileList = generator.outputFileList;
+        untranslatedMessagesFile = generator.untranslatedMessagesFile;
       } on L10nException catch (e) {
         throwToolExit(e.message);
       }
@@ -301,8 +305,16 @@
       if (outputFileList.isEmpty) {
         return FlutterCommandResult.success();
       }
+      final List<String> formatFileList = outputFileList.toList();
+      if (untranslatedMessagesFile != null) {
+        // Don't format the messages file using `dart format`.
+        formatFileList.remove(untranslatedMessagesFile.absolute.path);
+      }
+      if (formatFileList.isEmpty) {
+        return FlutterCommandResult.success();
+      }
       final String dartBinary = _artifacts.getArtifactPath(Artifact.engineDartBinary);
-      final List<String> command = <String>[dartBinary, 'format', ...outputFileList];
+      final List<String> command = <String>[dartBinary, 'format', ...formatFileList];
       final ProcessResult result = await _processManager.run(command);
       if (result.exitCode != 0) {
         throwToolExit('Formatting failed: $result', exitCode: result.exitCode);
diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
index 5a31e09..820940a 100644
--- a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
+++ b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
@@ -627,7 +627,6 @@
 
   /// A generated file that will contain the list of messages for each locale
   /// that do not have a translation yet.
-  @visibleForTesting
   final File? untranslatedMessagesFile;
 
   /// The file that contains the list of inputs and outputs for generating
@@ -824,7 +823,7 @@
     if (untranslatedMessagesFileString == null || untranslatedMessagesFileString.isEmpty) {
       return null;
     }
-
+    untranslatedMessagesFileString = untranslatedMessagesFileString.replaceAll(r'\', fileSystem.path.separator);
     return fileSystem.file(untranslatedMessagesFileString);
   }
 
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 cd52d89..e57e5ec 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
@@ -265,4 +265,58 @@
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.any(),
   });
+
+  // Regression test for https://github.com/flutter/flutter/issues/119594
+  testUsingContext('dart format is working when the untranslated messages file is produced', () async {
+    final File arbFile = fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb'))
+      ..createSync(recursive: true);
+    arbFile.writeAsStringSync('''
+{
+  "helloWorld": "Hello, World!",
+  "untranslated": "Test untranslated message."
+}''');
+    fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_es.arb'))
+      ..createSync(recursive: true)
+      ..writeAsStringSync('''
+{
+  "helloWorld": "Hello, World!"
+}''');
+    final File configFile = fileSystem.file('l10n.yaml')..createSync();
+    configFile.writeAsStringSync('''
+format: true
+untranslated-messages-file: lib/l10n/untranslated.json
+''');
+    final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
+    pubspecFile.writeAsStringSync(BasicProjectWithFlutterGen().pubspec);
+    processManager.addCommand(
+      const FakeCommand(
+        command: <String>[
+          'Artifact.engineDartBinary',
+          'format',
+          '/.dart_tool/flutter_gen/gen_l10n/app_localizations_en.dart',
+          '/.dart_tool/flutter_gen/gen_l10n/app_localizations_es.dart',
+          '/.dart_tool/flutter_gen/gen_l10n/app_localizations.dart',
+        ]
+      )
+    );
+    final GenerateLocalizationsCommand command = GenerateLocalizationsCommand(
+      fileSystem: fileSystem,
+      logger: logger,
+      artifacts: artifacts,
+      processManager: processManager,
+    );
+    await createTestCommandRunner(command).run(<String>['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_es.dart').existsSync(), true);
+    expect(outputDirectory.childFile('app_localizations.dart').existsSync(), true);
+    final File untranslatedMessagesFile = fileSystem.file(fileSystem.path.join('lib', 'l10n', 'untranslated.json'));
+    expect(untranslatedMessagesFile.existsSync(), true);
+    expect(processManager, hasNoRemainingExpectations);
+  }, overrides: <Type, Generator>{
+    FileSystem: () => fileSystem,
+    ProcessManager: () => FakeProcessManager.any(),
+  });
 }