Skip to content

Commit 215f637

Browse files
authored
Refactor Message class to hold all translations (#115506)
* init * more fixing * finish * fix lint * address pr comments * redo checks
1 parent 4a5dd9c commit 215f637

File tree

3 files changed

+193
-213
lines changed

3 files changed

+193
-213
lines changed

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

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -92,22 +92,22 @@ String _syntheticL10nPackagePath(FileSystem fileSystem) => fileSystem.path.join(
9292
// TODO(thkim1011): Let's store the output of this function in the Message class, so that we don't
9393
// recompute this. See https://github.com/flutter/flutter/issues/112709
9494
List<String> generateMethodParameters(Message message) {
95-
return message.placeholders.map((Placeholder placeholder) {
95+
return message.placeholders.values.map((Placeholder placeholder) {
9696
return '${placeholder.type} ${placeholder.name}';
9797
}).toList();
9898
}
9999

100100
// Similar to above, but is used for passing arguments into helper functions.
101101
List<String> generateMethodArguments(Message message) {
102-
return message.placeholders.map((Placeholder placeholder) => placeholder.name).toList();
102+
return message.placeholders.values.map((Placeholder placeholder) => placeholder.name).toList();
103103
}
104104

105105
String generateDateFormattingLogic(Message message) {
106106
if (message.placeholders.isEmpty || !message.placeholdersRequireFormatting) {
107107
return '@(none)';
108108
}
109109

110-
final Iterable<String> formatStatements = message.placeholders
110+
final Iterable<String> formatStatements = message.placeholders.values
111111
.where((Placeholder placeholder) => placeholder.requiresDateFormatting)
112112
.map((Placeholder placeholder) {
113113
final String? placeholderFormat = placeholder.format;
@@ -150,7 +150,7 @@ String generateNumberFormattingLogic(Message message) {
150150
return '@(none)';
151151
}
152152

153-
final Iterable<String> formatStatements = message.placeholders
153+
final Iterable<String> formatStatements = message.placeholders.values
154154
.where((Placeholder placeholder) => placeholder.requiresNumFormatting)
155155
.map((Placeholder placeholder) {
156156
final String? placeholderFormat = placeholder.format;
@@ -502,8 +502,10 @@ class LocalizationsGenerator {
502502
final FileSystem _fs;
503503
Iterable<Message> _allMessages = <Message>[];
504504
late final AppResourceBundleCollection _allBundles = AppResourceBundleCollection(inputDirectory);
505-
506505
late final AppResourceBundle _templateBundle = AppResourceBundle(templateArbFile);
506+
late final Map<LocaleInfo, String> _inputFileNames = Map<LocaleInfo, String>.fromEntries(
507+
_allBundles.bundles.map((AppResourceBundle bundle) => MapEntry<LocaleInfo, String>(bundle.locale, bundle.file.basename))
508+
);
507509
late final LocaleInfo _templateArbLocale = _templateBundle.locale;
508510

509511
@visibleForTesting
@@ -843,7 +845,7 @@ class LocalizationsGenerator {
843845
// files in inputDirectory. Also initialized: supportedLocales.
844846
void loadResources() {
845847
_allMessages = _templateBundle.resourceIds.map((String id) => Message(
846-
_templateBundle.resources, id, areResourceAttributesRequired,
848+
_templateBundle, _allBundles, id, areResourceAttributesRequired,
847849
));
848850
for (final String resourceId in _templateBundle.resourceIds) {
849851
if (!_isValidGetterAndMethodName(resourceId)) {
@@ -891,21 +893,19 @@ class LocalizationsGenerator {
891893
String className,
892894
String fileName,
893895
String header,
894-
AppResourceBundle bundle,
895-
AppResourceBundle templateBundle,
896-
Iterable<Message> messages,
896+
final LocaleInfo locale,
897897
) {
898-
final LocaleInfo locale = bundle.locale;
899-
900-
final Iterable<String> methods = messages.map((Message message) {
901-
if (bundle.translationFor(message) == null) {
898+
final Iterable<String> methods = _allMessages.map((Message message) {
899+
if (message.messages[locale] == null) {
902900
_addUnimplementedMessage(locale, message.resourceId);
901+
return _generateMethod(
902+
message,
903+
_templateArbLocale,
904+
);
903905
}
904-
905906
return _generateMethod(
906907
message,
907-
bundle.file.basename,
908-
bundle.translationFor(message) ?? templateBundle.translationFor(message)!,
908+
locale,
909909
);
910910
});
911911

@@ -923,20 +923,19 @@ class LocalizationsGenerator {
923923
String _generateSubclass(
924924
String className,
925925
AppResourceBundle bundle,
926-
Iterable<Message> messages,
927926
) {
928927
final LocaleInfo locale = bundle.locale;
929928
final String baseClassName = '$className${LocaleInfo.fromString(locale.languageCode).camelCase()}';
930929

931-
messages
932-
.where((Message message) => bundle.translationFor(message) == null)
930+
_allMessages
931+
.where((Message message) => message.messages[locale] == null)
933932
.forEach((Message message) {
934933
_addUnimplementedMessage(locale, message.resourceId);
935934
});
936935

937-
final Iterable<String> methods = messages
938-
.where((Message message) => bundle.translationFor(message) != null)
939-
.map((Message message) => _generateMethod(message, bundle.file.basename, bundle.translationFor(message)!));
936+
final Iterable<String> methods = _allMessages
937+
.where((Message message) => message.messages[locale] != null)
938+
.map((Message message) => _generateMethod(message, locale));
940939

941940
return subclassTemplate
942941
.replaceAll('@(language)', describeLocale(locale.toString()))
@@ -1016,9 +1015,7 @@ class LocalizationsGenerator {
10161015
className,
10171016
outputFileName,
10181017
header,
1019-
_allBundles.bundleFor(locale)!,
1020-
_allBundles.bundleFor(_templateArbLocale)!,
1021-
_allMessages,
1018+
locale,
10221019
);
10231020

10241021
// Every locale for the language except the base class.
@@ -1029,7 +1026,6 @@ class LocalizationsGenerator {
10291026
return _generateSubclass(
10301027
className,
10311028
_allBundles.bundleFor(locale)!,
1032-
_allMessages,
10331029
);
10341030
});
10351031

@@ -1079,13 +1075,14 @@ class LocalizationsGenerator {
10791075
.replaceAll('\n\n\n', '\n\n');
10801076
}
10811077

1082-
String _generateMethod(Message message, String filename, String translationForMessage) {
1078+
String _generateMethod(Message message, LocaleInfo locale) {
10831079
// Determine if we must import intl for date or number formatting.
10841080
if (message.placeholdersRequireFormatting) {
10851081
requiresIntlImport = true;
10861082
}
10871083

1088-
final Node node = Parser(message.resourceId, filename, translationForMessage).parse();
1084+
final String translationForMessage = message.messages[locale]!;
1085+
final Node node = message.parsedMessages[locale]!;
10891086
// If parse tree is only a string, then return a getter method.
10901087
if (node.children.every((Node child) => child.type == ST.string)) {
10911088
// Use the parsed translation to handle escaping with the same behavior.
@@ -1150,17 +1147,16 @@ class LocalizationsGenerator {
11501147
assert(node.children[1].type == ST.identifier);
11511148
final Node identifier = node.children[1];
11521149
// Check that placeholders exist.
1153-
// TODO(thkim1011): Make message.placeholders a map so that we don't need to do linear time search.
1154-
// See https://github.com/flutter/flutter/issues/112709
1155-
final Placeholder placeholder = message.placeholders.firstWhere(
1156-
(Placeholder placeholder) => placeholder.name == identifier.value,
1157-
orElse: () {
1158-
throw L10nException('''
1159-
Make sure that the specified placeholder is defined in your arb file.
1160-
$translationForMessage
1161-
${Parser.indentForError(identifier.positionInMessage)}''');
1162-
}
1163-
);
1150+
final Placeholder? placeholder = message.placeholders[identifier.value];
1151+
if (placeholder == null) {
1152+
throw L10nParserException(
1153+
'Make sure that the specified placeholder is defined in your arb file.',
1154+
_inputFileNames[locale]!,
1155+
message.resourceId,
1156+
translationForMessage,
1157+
identifier.positionInMessage,
1158+
);
1159+
}
11641160
dependentPlaceholders.add(placeholder);
11651161
return HelperMethod(dependentPlaceholders, placeholder: placeholder);
11661162

@@ -1175,25 +1171,27 @@ ${Parser.indentForError(identifier.positionInMessage)}''');
11751171
final Node identifier = node.children[1];
11761172
final Node pluralParts = node.children[5];
11771173

1178-
// Check that identifier exists and is of type int or num.
1179-
final Placeholder placeholder = message.placeholders.firstWhere(
1180-
(Placeholder placeholder) => placeholder.name == identifier.value,
1181-
orElse: () {
1182-
throw L10nException('''
1183-
Make sure that the specified plural placeholder is defined in your arb file.
1184-
$translationForMessage
1185-
${List<String>.filled(identifier.positionInMessage, ' ').join()}^''');
1186-
}
1187-
);
1174+
// Check that placeholders exist and is of type int or num.
1175+
final Placeholder? placeholder = message.placeholders[identifier.value];
1176+
if (placeholder == null) {
1177+
throw L10nParserException(
1178+
'Make sure that the specified placeholder is defined in your arb file.',
1179+
_inputFileNames[locale]!,
1180+
message.resourceId,
1181+
translationForMessage,
1182+
identifier.positionInMessage,
1183+
);
1184+
}
1185+
if (placeholder.type != 'num' && placeholder.type != 'int') {
1186+
throw L10nParserException(
1187+
'The specified placeholder must be of type int or num.',
1188+
_inputFileNames[locale]!,
1189+
message.resourceId,
1190+
translationForMessage,
1191+
identifier.positionInMessage,
1192+
);
1193+
}
11881194
dependentPlaceholders.add(placeholder);
1189-
// TODO(thkim1011): Uncomment the following lines after Message refactor.
1190-
// See https://github.com/flutter/flutter/issues/112709.
1191-
// if (placeholder.type != 'num' && placeholder.type != 'int') {
1192-
// throw L10nException('''
1193-
// The specified placeholder must be of type int or num.
1194-
// $translationForMessage
1195-
// ${List<String>.filled(identifier.positionInMessage, ' ').join()}^''');
1196-
// }
11971195

11981196
for (final Node pluralPart in pluralParts.children.reversed) {
11991197
String pluralCase;
@@ -1239,16 +1237,17 @@ ${Parser.indentForError(pluralPart.positionInMessage)}
12391237
assert(node.children[5].type == ST.selectParts);
12401238

12411239
final Node identifier = node.children[1];
1242-
// Check that identifier exists
1243-
final Placeholder placeholder = message.placeholders.firstWhere(
1244-
(Placeholder placeholder) => placeholder.name == identifier.value,
1245-
orElse: () {
1246-
throw L10nException('''
1247-
Make sure that the specified select placeholder is defined in your arb file.
1248-
$translationForMessage
1249-
${Parser.indentForError(identifier.positionInMessage)}''');
1250-
}
1251-
);
1240+
// Check that placeholders exist.
1241+
final Placeholder? placeholder = message.placeholders[identifier.value];
1242+
if (placeholder == null) {
1243+
throw L10nParserException(
1244+
'Make sure that the specified placeholder is defined in your arb file.',
1245+
_inputFileNames[locale]!,
1246+
message.resourceId,
1247+
translationForMessage,
1248+
identifier.positionInMessage,
1249+
);
1250+
}
12521251
dependentPlaceholders.add(placeholder);
12531252
final List<String> selectLogicArgs = <String>[];
12541253
final Node selectParts = node.children[5];

0 commit comments

Comments
 (0)