[CP-stable][Gen-l10n] Infer placeholder types on both templates and localizations (#166337)
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.
### Issue Link:
What is the link to the issue this cherry-pick is addressing?
https://github.com/flutter/flutter/issues/163627
### Changelog Description:
Explain this cherry pick in one line that is accessible to most Flutter developers. See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples
[flutter/163627](https://github.com/flutter/flutter/issues/163627): Fix issue where placeholder types in ARB localizations weren't used for type inference, causing a possible type mismatch with the placeholder field defined in the template.
### Impact Description:
What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)? Does it impact development (ex. flutter doctor crashes when Android Studio is installed), or the shipping production app (the app crashes on launch)
Developers are unable to successfully generate localization code without making changes across all of their ARB files.
### Workaround:
Is there a workaround for this issue?
Downgrade to 3.27 or explicitly define types for all impacted placeholders across all template and localization files.
### Risk:
What is the risk level of this cherry-pick?
### Test Coverage:
Are you confident that your fix is well-tested by automated tests?
### Validation Steps:
What are the steps to validate that this fix works?
See https://github.com/benthillerkus/i18n_repro
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0252eb2..f8dc7f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,8 +30,8 @@
- [flutter/163421](https://github.com/flutter/flutter/issues/163421) - Impeller,
Android, Fixes Android Emulator crash when navigating to routes with backdrop
blurs.
-- [flutter/165166](https://github.com/flutter/flutter/pull/165166) - Impeller,
- All platforms, Text that is scaled over 48x renders incorrectly.
+- [flutter/165166](https://github.com/flutter/flutter/pull/165166) - Impeller, All platforms, Text that is scaled over 48x renders incorrectly.
+- [flutter/163627](https://github.com/flutter/flutter/pull/163627) - Fix issue where placeholder types in ARB localizations weren't used for type inference, causing a possible type mismatch with the placeholder field defined in the template.
- [flutter/165166](https://github.com/flutter/flutter/pull/165166) - Update CI configurations and tests to use Xcode 16 and iOS 18 simulator.
### [3.29.2](https://github.com/flutter/flutter/releases/tag/3.29.2)
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 96e9451..4be34a3 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 @@
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<String, Placeholder> 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 e0a5c73..4a207ca 100644
--- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart
+++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart
@@ -1659,6 +1659,50 @@
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 `getGeneratedFileContent` 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(<String, String>{
+ '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(getGeneratedFileContent(locale: 'en'), isA<String>());
+ },
+ );
});
group('DateTime tests', () {
@@ -2128,7 +2172,7 @@
(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.',
),
),
),