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 96e9451e00..4be34a3dd4 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart @@ -584,7 +584,9 @@ class Message { return x && !y && !z || !x && y && !z || !x && !y && z || !x && !y && !z; } - for (final Placeholder placeholder in templatePlaceholders.values) { + for (final Placeholder placeholder in templatePlaceholders.values.followedBy( + localePlaceholders.values.expand((Map e) => e.values), + )) { if (!atMostOneOf(placeholder.isPlural, placeholder.isDateTime, placeholder.isSelect)) { throw L10nException('Placeholder is used as plural/select/datetime in certain languages.'); } else if (placeholder.isPlural) { diff --git a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart index 7d3832518d..05a47e334f 100644 --- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart +++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart @@ -1689,6 +1689,50 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e expect(content, contains("String get helloWorld => 'Hello {name}'")); }, ); + + // Regression test for https://github.com/flutter/flutter/issues/163627 + // + // If placeholders have no explicit type (like `int` or `String`) set + // their type can be inferred. + // + // Later in the pipeline it is ensured that each locales placeholder types + // matches the definitions in the template. + // + // If only the types of the template had been inferred, + // and not for the translation there would be a mismatch: + // in this case `num` for count and `null` (the default), which is incompatible + // and `getSyntheticGeneratedFileContent` would throw an exception. + // + // This test ensures that both template and locale can be equally partially defined + // in the arb. + testWithoutContext( + 'translation placeholder type definitions can be inferred for plurals', + () { + setupLocalizations({ + 'en': ''' +{ + "helloWorld": "{count, plural, one{Hello World!} other{Hello Worlds!}}", + "@helloWorld": { + "description": "The conventional newborn programmer greeting", + "placeholders": { + "count": {} + } + } +}''', + 'de': ''' +{ + "helloWorld": "{count, plural, one{Hallo Welt!} other{Hallo Welten!}}", + "@helloWorld": { + "description": "The conventional newborn programmer greeting", + "placeholders": { + "count": {} + } + } +}''', + }); + expect(getSyntheticGeneratedFileContent(locale: 'en'), isA()); + }, + ); }); group('DateTime tests', () { @@ -2167,7 +2211,7 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e (L10nException e) => e.message, 'message', contains( - 'The placeholder, springStartDate, has its "type" resource attribute set to the "null" type in locale "ja", but it is "DateTime" in the template placeholder.', + 'The placeholder, springStartDate, has its "type" resource attribute set to the "Object" type in locale "ja", but it is "DateTime" in the template placeholder.', ), ), ),