diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart index acbcda9a90..85dab535dc 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart @@ -229,15 +229,102 @@ String _generatePluralMethod(Message message, String translationForMessage) { final String comment = message.description ?? 'No description provided in @${message.resourceId}'; - return pluralMethodTemplate + if (translationForMessage.startsWith('{') && translationForMessage.endsWith('}')) { + return pluralMethodTemplate + .replaceAll('@(comment)', comment) + .replaceAll('@(name)', message.resourceId) + .replaceAll('@(dateFormatting)', generateDateFormattingLogic(message)) + .replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message)) + .replaceAll('@(parameters)', parameters.join(', ')) + .replaceAll('@(count)', countPlaceholder.name) + .replaceAll('@(pluralLogicArgs)', pluralLogicArgs.join(',\n')) + .replaceAll('@(none)\n', ''); + } + + const String variable = 'pluralString'; + final String string = _replaceWithVariable(translationForMessage, variable); + return pluralMethodTemplateInString .replaceAll('@(comment)', comment) .replaceAll('@(name)', message.resourceId) - .replaceAll('@(parameters)', parameters.join(', ')) .replaceAll('@(dateFormatting)', generateDateFormattingLogic(message)) .replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message)) + .replaceAll('@(parameters)', parameters.join(', ')) + .replaceAll('@(variable)', variable) .replaceAll('@(count)', countPlaceholder.name) .replaceAll('@(pluralLogicArgs)', pluralLogicArgs.join(',\n')) - .replaceAll('@(none)\n', ''); + .replaceAll('@(none)\n', '') + .replaceAll('@(string)', string); +} + +String _replaceWithVariable(String translation, String variable) { + String prefix = generateString(translation.substring(0, translation.indexOf('{'))); + prefix = prefix.substring(0, prefix.length - 1); + String suffix = generateString(translation.substring(translation.lastIndexOf('}') + 1)); + suffix = suffix.substring(1); + + // escape variable when the suffix can be combined with the variable + if (suffix.isNotEmpty && !suffix.startsWith(' ')) { + variable = '{$variable}'; + } + return prefix + r'$' + variable + suffix; +} + +String _generateSelectMethod(Message message, String translationForMessage) { + if (message.placeholders.isEmpty) { + throw L10nException( + 'Unable to find placeholders for the select message: ${message.resourceId}.\n' + 'Check to see if the select message is in the proper ICU syntax format ' + 'and ensure that placeholders are properly specified.' + ); + } + + final List cases = []; + + final RegExpMatch? selectMatch = + LocalizationsGenerator._selectRE.firstMatch(translationForMessage); + String? choice; + if (selectMatch != null && selectMatch.groupCount == 2) { + choice = selectMatch.group(1); + final String pattern = selectMatch.group(2)!; + final RegExp patternRE = RegExp(r'\s*([\w\d]+)\s*\{(.*?)\}'); + for (final RegExpMatch patternMatch in patternRE.allMatches(pattern)) { + if (patternMatch.groupCount == 2) { + final String value = patternMatch.group(2)! + .replaceAll("'", r"\'") + .replaceAll('"', r'\"'); + cases.add( + " '${patternMatch.group(1)}': '$value'", + ); + } + } + } + + final List parameters = message.placeholders.map((Placeholder placeholder) { + final String placeholderType = placeholder.type ?? 'object'; + return '$placeholderType ${placeholder.name}'; + }).toList(); + + final String description = message.description ?? 'No description provided in @${message.resourceId}'; + + if (translationForMessage.startsWith('{') && translationForMessage.endsWith('}')) { + return selectMethodTemplate + .replaceAll('@(name)', message.resourceId) + .replaceAll('@(parameters)', parameters.join(', ')) + .replaceAll('@(choice)', choice!) + .replaceAll('@(cases)', cases.join(',\n').trim()) + .replaceAll('@(description)', description); + } + + const String variable = 'selectString'; + final String string = _replaceWithVariable(translationForMessage, variable); + return selectMethodTemplateInString + .replaceAll('@(name)', message.resourceId) + .replaceAll('@(parameters)', parameters.join(', ')) + .replaceAll('@(variable)', variable) + .replaceAll('@(choice)', choice!) + .replaceAll('@(cases)', cases.join(',\n').trim()) + .replaceAll('@(description)', description) + .replaceAll('@(string)', string); } bool _needsCurlyBracketStringInterpolation(String messageString, String placeholder) { @@ -305,6 +392,10 @@ String _generateMethod(Message message, String translationForMessage) { return _generatePluralMethod(message, translationForMessage); } + if (message.isSelect) { + return _generateSelectMethod(message, translationForMessage); + } + if (message.placeholdersRequireFormatting) { return formatMethodTemplate .replaceAll('@(name)', message.resourceId) @@ -741,6 +832,8 @@ class LocalizationsGenerator { @visibleForTesting final bool areResourceAttributesRequired; + static final RegExp _selectRE = RegExp(r'\{([\w\s,]*),\s*select\s*,\s*([\w\d]+\s*\{.*\})+\s*\}'); + static bool _isNotReadable(FileStat fileStat) { final String rawStatString = fileStat.modeString(); // Removes potential prepended permission bits, such as '(suid)' and '(guid)'. @@ -1168,7 +1261,11 @@ class LocalizationsGenerator { .replaceAll('\n\n\n', '\n\n'); } - bool _requiresIntlImport() => _allMessages.any((Message message) => message.isPlural || message.placeholdersRequireFormatting); + bool _requiresIntlImport() => _allMessages.any((Message message) { + return message.isPlural + || message.isSelect + || message.placeholdersRequireFormatting; + }); void writeOutputFiles(Logger logger, { bool isFromYaml = false }) { // First, generate the string contents of all necessary files. diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart index 8da8122c8d..8075df4e3c 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n_templates.dart @@ -156,6 +156,46 @@ const String pluralMethodTemplate = ''' ); }'''; +const String pluralMethodTemplateInString = ''' + @override + String @(name)(@(parameters)) { +@(dateFormatting) +@(numberFormatting) + final String @(variable) = intl.Intl.pluralLogic( + @(count), + locale: localeName, +@(pluralLogicArgs), + ); + + return @(string); + }'''; + +const String selectMethodTemplate = ''' + @override + String @(name)(@(parameters)) { + return intl.Intl.select( + @(choice), + { + @(cases) + }, + desc: '@(description)' + ); + }'''; + +const String selectMethodTemplateInString = ''' + @override + String @(name)(@(parameters)) { + final String @(variable) = intl.Intl.select( + @(choice), + { + @(cases) + }, + desc: '@(description)' + ); + + return @(string); + }'''; + const String classFileTemplate = ''' @(header) 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 63c2f58300..46c617c6e5 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n_types.dart @@ -273,17 +273,21 @@ class Message { value = _value(bundle, resourceId), description = _description(bundle, resourceId, isResourceAttributeRequired), placeholders = _placeholders(bundle, resourceId, isResourceAttributeRequired), - _pluralMatch = _pluralRE.firstMatch(_value(bundle, resourceId)); + _pluralMatch = _pluralRE.firstMatch(_value(bundle, resourceId)), + _selectMatch = _selectRE.firstMatch(_value(bundle, resourceId)); static final RegExp _pluralRE = RegExp(r'\s*\{([\w\s,]*),\s*plural\s*,'); + static final RegExp _selectRE = RegExp(r'\s*\{([\w\s,]*),\s*select\s*,'); final String resourceId; final String value; final String? description; final List placeholders; final RegExpMatch? _pluralMatch; + final RegExpMatch? _selectMatch; bool get isPlural => _pluralMatch != null && _pluralMatch!.groupCount == 1; + bool get isSelect => _selectMatch != null && _selectMatch!.groupCount == 1; bool get placeholdersRequireFormatting => placeholders.any((Placeholder p) => p.requiresFormatting); @@ -331,13 +335,21 @@ class Message { ); } - final RegExpMatch? pluralRegExp = _pluralRE.firstMatch(_value(bundle, resourceId)); - final bool isPlural = pluralRegExp != null && pluralRegExp.groupCount == 1; - if (attributes == null && isPlural) { - throw L10nException( - 'Resource attribute "@$resourceId" was not found. Please ' - 'ensure that plural resources have a corresponding @resource.' - ); + if (attributes == null) { + + void _throwEmptyAttributes(final RegExp regExp, final String type) { + final RegExpMatch? match = regExp.firstMatch(_value(bundle, resourceId)); + final bool isMatch = match != null && match.groupCount == 1; + if (isMatch) { + throw L10nException( + 'Resource attribute "@$resourceId" was not found. Please ' + 'ensure that $type resources have a corresponding @resource.' + ); + } + } + + _throwEmptyAttributes(_pluralRE, 'plural'); + _throwEmptyAttributes(_selectRE, 'select'); } return attributes as Map?; 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 091d24dbb4..fcaa2ac553 100644 --- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart +++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart @@ -120,23 +120,17 @@ void main() { // Run localizations generator in specified absolute path. final String flutterProjectPath = fs.path.join('absolute', 'path', 'to', 'flutter_project'); - try { - LocalizationsGenerator( - fileSystem: fs, - projectPathString: flutterProjectPath, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - throw TestFailure('Unexpected failure during test setup: ${e.message}'); - } on Exception catch (e) { - throw TestFailure('Unexpected failure during test setup: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + projectPathString: flutterProjectPath, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); // Output files should be generated in the provided absolute path. expect( @@ -245,20 +239,15 @@ void main() { testWithoutContext('correctly adds a headerString when it is set', () { _standardFlutterDirectoryL10nSetup(fs); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - headerString: '/// Sample header', - ); - } on L10nException catch (e) { - fail('Setting a header through a String should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + headerString: '/// Sample header', + ); expect(generator.header, '/// Sample header'); }); @@ -270,20 +259,15 @@ void main() { ..childFile(esArbFileName).writeAsStringSync(singleEsMessageArbFileString) ..childFile('header.txt').writeAsStringSync('/// Sample header in a text file'); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - headerFile: 'header.txt', - ); - } on L10nException catch (e) { - fail('Setting a header through a file should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + headerFile: 'header.txt', + ); expect(generator.header, '/// Sample header in a text file'); }); @@ -295,19 +279,15 @@ void main() { .writeAsStringSync(singleMessageArbFileString); l10nDirectory.childFile('app_localizations_es.arb') .writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: 'app_localizations_en.arb', - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: 'app_localizations_en.arb', + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory(syntheticL10nPackagePath); expect(outputDirectory.childFile('output-localization-file.dart').existsSync(), isTrue); @@ -346,21 +326,17 @@ void main() { ..childFile(defaultTemplateArbFileName).writeAsStringSync(twoMessageArbFileString) ..childFile(esArbFileName).writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final File unimplementedOutputFile = fs.file( fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), @@ -382,22 +358,18 @@ void main() { ..childFile(defaultTemplateArbFileName).writeAsStringSync(twoMessageArbFileString) ..childFile(esArbFileName).writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final File unimplementedOutputFile = fs.file( fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), @@ -423,21 +395,17 @@ void main() { ..childFile(defaultTemplateArbFileName).writeAsStringSync(twoMessageArbFileString) ..childFile(esArbFileName).writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - ) - ..loadResources() - ..writeOutputFiles(testLogger); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + ) + ..loadResources() + ..writeOutputFiles(testLogger); expect( testLogger.statusText, @@ -460,19 +428,15 @@ void main() { ..childFile(defaultTemplateArbFileName).writeAsStringSync(twoMessageArbFileString) ..childFile(esArbFileName).writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(testLogger, isFromYaml: true); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(testLogger, isFromYaml: true); expect( testLogger.statusText, @@ -495,20 +459,16 @@ void main() { ..childFile(defaultTemplateArbFileName).writeAsStringSync(twoMessageArbFileString) ..childFile(esArbFileName).writeAsStringSync(twoMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(testLogger); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(testLogger); expect(testLogger.statusText, ''); }, @@ -517,21 +477,17 @@ void main() { testWithoutContext('untranslated messages file included in generated JSON list of outputs', () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - inputsAndOutputsListPath: syntheticL10nPackagePath, - untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + inputsAndOutputsListPath: syntheticL10nPackagePath, + untranslatedMessagesFile: fs.path.join('lib', 'l10n', 'unimplemented_message_translations.json'), + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final File inputsAndOutputsList = fs.file( fs.path.join(syntheticL10nPackagePath, 'gen_l10n_inputs_and_outputs.json'), @@ -550,21 +506,17 @@ void main() { 'null while not using the synthetic package option', () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - // outputPathString is intentionally not defined - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + // outputPathString is intentionally not defined + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory('lib').childDirectory('l10n'); expect(outputDirectory.childFile('output-localization-file.dart').existsSync(), isTrue); @@ -591,21 +543,17 @@ void main() { .childFile(esArbFileName) .writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: fs.path.join('lib', 'l10n', 'output'), - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: fs.path.join('lib', 'l10n', 'output'), + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output'); expect(outputDirectory.existsSync(), isTrue); @@ -621,21 +569,17 @@ void main() { () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: fs.path.join('lib', 'l10n', 'output'), - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: fs.path.join('lib', 'l10n', 'output'), + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output'); expect(outputDirectory.existsSync(), isTrue); @@ -651,21 +595,17 @@ void main() { () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: fs.path.join('lib', 'l10n', 'output'), - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: fs.path.join('lib', 'l10n', 'output'), + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output'); expect(outputDirectory.existsSync(), isTrue); @@ -686,22 +626,18 @@ void main() { () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: fs.path.join('lib', 'l10n', 'output'), - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useSyntheticPackage: false, - usesNullableGetter: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: fs.path.join('lib', 'l10n', 'output'), + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useSyntheticPackage: false, + usesNullableGetter: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory('lib').childDirectory('l10n').childDirectory('output'); expect(outputDirectory.existsSync(), isTrue); @@ -720,20 +656,16 @@ void main() { testWithoutContext('creates list of inputs and outputs when file path is specified', () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - inputsAndOutputsListPath: syntheticL10nPackagePath, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + inputsAndOutputsListPath: syntheticL10nPackagePath, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final File inputsAndOutputsList = fs.file( fs.path.join(syntheticL10nPackagePath, 'gen_l10n_inputs_and_outputs.json'), @@ -915,19 +847,15 @@ flutter: testWithoutContext('correctly initializes supportedLocales and supportedLanguageCodes properties', () { _standardFlutterDirectoryL10nSetup(fs); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - )..loadResources(); - } on L10nException catch (e) { - fail('Setting language and locales should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources(); expect(generator.supportedLocales.contains(LocaleInfo.fromString('en')), true); expect(generator.supportedLocales.contains(LocaleInfo.fromString('es')), true); @@ -944,20 +872,15 @@ flutter: l10nDirectory.childFile('app_en.arb') .writeAsStringSync(singleMessageArbFileString); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ); - generator.loadResources(); - } on L10nException catch (e) { - fail('Setting language and locales should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources(); expect(generator.supportedLocales.first, LocaleInfo.fromString('en')); expect(generator.supportedLocales.elementAt(1), LocaleInfo.fromString('es')); @@ -975,21 +898,16 @@ flutter: .writeAsStringSync(singleZhMessageArbFileString); const List preferredSupportedLocale = ['zh', 'es']; - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - preferredSupportedLocales: preferredSupportedLocale, - ); - generator.loadResources(); - } on L10nException catch (e) { - fail('Setting language and locales should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + preferredSupportedLocales: preferredSupportedLocale, + ) + ..loadResources(); expect(generator.supportedLocales.first, LocaleInfo.fromString('zh')); expect(generator.supportedLocales.elementAt(1), LocaleInfo.fromString('es')); @@ -1043,20 +961,15 @@ flutter: l10nDirectory.childFile('app_en.arb') .writeAsStringSync(singleMessageArbFileString); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ); - generator.loadResources(); - } on L10nException catch (e) { - fail('Setting language and locales should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources(); expect(generator.arbPathStrings.first, fs.path.join('lib', 'l10n', 'app_en.arb')); expect(generator.arbPathStrings.elementAt(1), fs.path.join('lib', 'l10n', 'app_es.arb')); @@ -1089,20 +1002,15 @@ flutter: l10nDirectory.childFile('second_file.arb') .writeAsStringSync(arbFileWithZhLocale); - LocalizationsGenerator generator; - try { - generator = LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: 'first_file.arb', - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ); - generator.loadResources(); - } on L10nException catch (e) { - fail('Setting language and locales should not fail: \n${e.message}'); - } + final LocalizationsGenerator generator = LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: 'first_file.arb', + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources(); expect(generator.supportedLocales.contains(LocaleInfo.fromString('en')), true); expect(generator.supportedLocales.contains(LocaleInfo.fromString('zh')), true); @@ -1241,20 +1149,16 @@ flutter: testWithoutContext('message without placeholders - should generate code comment with description and template message translation', () { _standardFlutterDirectoryL10nSetup(fs); final BufferLogger testLogger = BufferLogger.test(); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(testLogger); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(testLogger); final File baseLocalizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart') @@ -1286,20 +1190,16 @@ flutter: final BufferLogger testLogger = BufferLogger.test(); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(testLogger); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(testLogger); final File baseLocalizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart') @@ -1339,22 +1239,16 @@ flutter: }'''); final BufferLogger testLogger = BufferLogger.test(); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(testLogger); - } on Exception catch (e) { - final L10nException exception = e as L10nException; - print(exception.message); - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(testLogger); final File baseLocalizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart') @@ -1379,20 +1273,16 @@ flutter: ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString) ..childFile('app_en_CA.arb').writeAsStringSync(singleEnCaMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); expect(fs.isFileSync(fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart')), true); expect(fs.isFileSync(fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en_US.dart')), false); @@ -1411,21 +1301,17 @@ flutter: ..childFile('app_es.arb').writeAsStringSync(singleEsMessageArbFileString); const List preferredSupportedLocale = ['zh']; - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - preferredSupportedLocales: preferredSupportedLocale, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + preferredSupportedLocales: preferredSupportedLocale, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, defaultOutputFileString), @@ -1442,20 +1328,16 @@ import 'output-localization-file_zh.dart'; fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true) ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useDeferredLoading: true, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useDeferredLoading: true, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, defaultOutputFileString), @@ -1812,25 +1694,164 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e }); }); + group('select messages', () { + testWithoutContext('should throw attempting to generate a select message without placeholders', () { + const String selectMessageWithoutPlaceholdersAttribute = ''' +{ + "genderSelect": "{gender, select, female {She} male {He} other {they} }", + "@genderSelect": { + "description": "Improperly formatted since it has no placeholder attribute." + } +}'''; + + final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(selectMessageWithoutPlaceholdersAttribute); + + expect( + () { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + }, + throwsA(isA().having( + (L10nException e) => e.message, + 'message', + contains('Check to see if the select message is in the proper ICU syntax format'), + )), + ); + }); + + testWithoutContext('should throw attempting to generate a select message with an empty placeholders map', () { + const String selectMessageWithEmptyPlaceholdersMap = ''' +{ + "genderSelect": "{gender, select, female {She} male {He} other {they} }", + "@genderSelect": { + "description": "Improperly formatted since it has no placeholder attribute.", + "placeholders": {} + } +}'''; + + final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(selectMessageWithEmptyPlaceholdersMap); + + expect( + () { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + }, + throwsA(isA().having( + (L10nException e) => e.message, + 'message', + contains('Check to see if the select message is in the proper ICU syntax format'), + )), + ); + }); + + testWithoutContext('should throw attempting to generate a select message with no resource attributes', () { + const String selectMessageWithoutResourceAttributes = ''' +{ + "genderSelect": "{gender, select, female {She} male {He} other {they} }" +}'''; + + final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(selectMessageWithoutResourceAttributes); + + expect( + () { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + }, + throwsA(isA().having( + (L10nException e) => e.message, + 'message', + contains('Resource attribute "@genderSelect" was not found'), + )), + ); + }); + + testWithoutContext('should throw attempting to generate a select message with incorrect format for placeholders', () { + const String selectMessageWithIncorrectPlaceholderFormat = ''' +{ + "genderSelect": "{gender, select, female {She} male {He} other {they} }", + "@genderSelect": { + "placeholders": "Incorrectly a string, should be a map." + } +}'''; + + final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(selectMessageWithIncorrectPlaceholderFormat); + + expect( + () { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + }, + throwsA(isA().having( + (L10nException e) => e.message, + 'message', + allOf( + contains('is not properly formatted'), + contains('Ensure that it is a map with string valued keys'), + ), + )), + ); + }); + }); + testWithoutContext('intl package import should be omitted in subclass files when no plurals are included', () { fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true) ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString) ..childFile('app_es.arb').writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file_es.dart'), @@ -1861,20 +1882,56 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..childFile(defaultTemplateArbFileName).writeAsStringSync(pluralMessageArb) ..childFile('app_es.arb').writeAsStringSync(pluralMessageEsArb); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); + + final String localizationsFile = fs.file( + fs.path.join(syntheticL10nPackagePath, 'output-localization-file_es.dart'), + ).readAsStringSync(); + expect(localizationsFile, contains(intlImportDartCode)); + }); + + testWithoutContext('intl package import should be kept in subclass files when select is included', () { + const String selectMessageArb = ''' +{ + "genderSelect": "{gender, select, female {She} male {He} other {they} }", + "@genderSelect": { + "description": "A select message", + "placeholders": { + "gender": {} + } + } +} +'''; + + const String selectMessageEsArb = ''' +{ + "genderSelect": "{gender, select, female {ES - She} male {ES - He} other {ES - they} }" +} +'''; + + fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true) + ..childFile(defaultTemplateArbFileName).writeAsStringSync(selectMessageArb) + ..childFile('app_es.arb').writeAsStringSync(selectMessageEsArb); + + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file_es.dart'), @@ -1885,21 +1942,17 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e testWithoutContext('check indentation on generated files', () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useDeferredLoading: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useDeferredLoading: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart'), @@ -1923,21 +1976,17 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString) ..childFile('app_es.arb').writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useDeferredLoading: true, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useDeferredLoading: true, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart'), @@ -1950,21 +1999,17 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..childFile(defaultTemplateArbFileName).writeAsStringSync(singleMessageArbFileString) ..childFile('app_es.arb').writeAsStringSync(singleEsMessageArbFileString); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - useDeferredLoading: false, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + useDeferredLoading: false, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file.dart'), @@ -2079,23 +2124,16 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..childFile(defaultTemplateArbFileName).writeAsStringSync(enArbCheckList) ..childFile('app_es.arb').writeAsStringSync(esArbCheckList); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - if (e is L10nException) { - print(e.message); - } - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file_es.dart'), @@ -2145,23 +2183,16 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..childFile(defaultTemplateArbFileName).writeAsStringSync(enArbCheckList) ..childFile('app_es.arb').writeAsStringSync(esArbCheckList); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - outputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on Exception catch (e) { - if (e is L10nException) { - print(e.message); - } - fail('Generating output files should not fail: $e'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final String localizationsFile = fs.file( fs.path.join(syntheticL10nPackagePath, 'output-localization-file_es.dart'), @@ -2357,19 +2388,15 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e testWithoutContext('should generate a valid pubspec.yaml file when using synthetic package if it does not already exist', () { _standardFlutterDirectoryL10nSetup(fs); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); final Directory outputDirectory = fs.directory(syntheticPackagePath); final File pubspecFile = outputDirectory.childFile('pubspec.yaml'); @@ -2391,19 +2418,15 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ..createSync(recursive: true) ..writeAsStringSync('abcd'); - try { - LocalizationsGenerator( - fileSystem: fs, - inputPathString: defaultL10nPathString, - templateArbFileName: defaultTemplateArbFileName, - outputFileString: defaultOutputFileString, - classNameString: defaultClassNameString, - ) - ..loadResources() - ..writeOutputFiles(BufferLogger.test()); - } on L10nException catch (e) { - fail('Generating output should not fail: \n${e.message}'); - } + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ) + ..loadResources() + ..writeOutputFiles(BufferLogger.test()); // The original pubspec file should not be overwritten. expect(pubspecFile.readAsStringSync(), 'abcd'); diff --git a/packages/flutter_tools/test/integration.shard/gen_l10n_test.dart b/packages/flutter_tools/test/integration.shard/gen_l10n_test.dart index fb9d93a5d1..12f7290429 100644 --- a/packages/flutter_tools/test/integration.shard/gen_l10n_test.dart +++ b/packages/flutter_tools/test/integration.shard/gen_l10n_test.dart @@ -118,40 +118,50 @@ void main() { "#l10n 63 (Flutter's amazing, times 2!)\n" '#l10n 64 (Flutter is "amazing"!)\n' '#l10n 65 (Flutter is "amazing", times 2!)\n' - '#l10n 66 (--- es ---)\n' - '#l10n 67 (ES - Hello world)\n' - '#l10n 68 (ES - Hello _NEWLINE_ World)\n' - '#l10n 69 (ES - Hola \$ Mundo)\n' - '#l10n 70 (ES - Hello Mundo)\n' - '#l10n 71 (ES - Hola Mundo)\n' - '#l10n 72 (ES - Hello World on viernes, 1 de enero de 1960)\n' - '#l10n 73 (ES - Hello world argument on 1/1/1960 at 0:00)\n' - '#l10n 74 (ES - Hello World from 1960 to 2020)\n' - '#l10n 75 (ES - Hello for 123)\n' - '#l10n 76 (ES - Hello)\n' - '#l10n 77 (ES - Hello World)\n' - '#l10n 78 (ES - Hello two worlds)\n' - '#l10n 79 (ES - Hello)\n' - '#l10n 80 (ES - Hello nuevo World)\n' - '#l10n 81 (ES - Hello two nuevo worlds)\n' - '#l10n 82 (ES - Hello on viernes, 1 de enero de 1960)\n' - '#l10n 83 (ES - Hello World, on viernes, 1 de enero de 1960)\n' - '#l10n 84 (ES - Hello two worlds, on viernes, 1 de enero de 1960)\n' - '#l10n 85 (ES - Hello other 0 worlds, with a total of 100 citizens)\n' - '#l10n 86 (ES - Hello World of 101 citizens)\n' - '#l10n 87 (ES - Hello two worlds with 102 total citizens)\n' - '#l10n 88 (ES - [Hola] -Mundo- #123#)\n' - '#l10n 89 (ES - \$!)\n' - '#l10n 90 (ES - One \$)\n' - "#l10n 91 (ES - Flutter's amazing!)\n" - "#l10n 92 (ES - Flutter's amazing, times 2!)\n" - '#l10n 93 (ES - Flutter is "amazing"!)\n' - '#l10n 94 (ES - Flutter is "amazing", times 2!)\n' - '#l10n 95 (--- es_419 ---)\n' - '#l10n 96 (ES 419 - Hello World)\n' - '#l10n 97 (ES 419 - Hello)\n' - '#l10n 98 (ES 419 - Hello World)\n' - '#l10n 99 (ES 419 - Hello two worlds)\n' + '#l10n 66 (16 wheel truck)\n' + "#l10n 67 (Sedan's elegance)\n" + '#l10n 68 (Cabriolet has "acceleration")\n' + '#l10n 69 (Oh, she found 1 item!)\n' + '#l10n 70 (Indeed, they like Flutter!)\n' + '#l10n 71 (--- es ---)\n' + '#l10n 72 (ES - Hello world)\n' + '#l10n 73 (ES - Hello _NEWLINE_ World)\n' + '#l10n 74 (ES - Hola \$ Mundo)\n' + '#l10n 75 (ES - Hello Mundo)\n' + '#l10n 76 (ES - Hola Mundo)\n' + '#l10n 77 (ES - Hello World on viernes, 1 de enero de 1960)\n' + '#l10n 78 (ES - Hello world argument on 1/1/1960 at 0:00)\n' + '#l10n 79 (ES - Hello World from 1960 to 2020)\n' + '#l10n 80 (ES - Hello for 123)\n' + '#l10n 81 (ES - Hello)\n' + '#l10n 82 (ES - Hello World)\n' + '#l10n 83 (ES - Hello two worlds)\n' + '#l10n 84 (ES - Hello)\n' + '#l10n 85 (ES - Hello nuevo World)\n' + '#l10n 86 (ES - Hello two nuevo worlds)\n' + '#l10n 87 (ES - Hello on viernes, 1 de enero de 1960)\n' + '#l10n 88 (ES - Hello World, on viernes, 1 de enero de 1960)\n' + '#l10n 89 (ES - Hello two worlds, on viernes, 1 de enero de 1960)\n' + '#l10n 90 (ES - Hello other 0 worlds, with a total of 100 citizens)\n' + '#l10n 91 (ES - Hello World of 101 citizens)\n' + '#l10n 92 (ES - Hello two worlds with 102 total citizens)\n' + '#l10n 93 (ES - [Hola] -Mundo- #123#)\n' + '#l10n 94 (ES - \$!)\n' + '#l10n 95 (ES - One \$)\n' + "#l10n 96 (ES - Flutter's amazing!)\n" + "#l10n 97 (ES - Flutter's amazing, times 2!)\n" + '#l10n 98 (ES - Flutter is "amazing"!)\n' + '#l10n 99 (ES - Flutter is "amazing", times 2!)\n' + '#l10n 100 (ES - 16 wheel truck)\n' + "#l10n 101 (ES - Sedan's elegance)\n" + '#l10n 102 (ES - Cabriolet has "acceleration")\n' + '#l10n 103 (ES - Oh, she found ES - 1 itemES - !)\n' + '#l10n 104 (ES - Indeed, ES - they like ES - Flutter!)\n' + '#l10n 105 (--- es_419 ---)\n' + '#l10n 106 (ES 419 - Hello World)\n' + '#l10n 107 (ES 419 - Hello)\n' + '#l10n 108 (ES 419 - Hello World)\n' + '#l10n 109 (ES 419 - Hello two worlds)\n' '#l10n END\n' ); } diff --git a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart index ae6f358e47..d82ea93195 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart @@ -225,6 +225,11 @@ class Home extends StatelessWidget { '${localizations.singleQuotePlural(2)}', '${localizations.doubleQuote}', '${localizations.doubleQuotePlural(2)}', + "${localizations.vehicleSelect('truck')}", + "${localizations.singleQuoteSelect('sedan')}", + "${localizations.doubleQuoteSelect('cabriolet')}", + "${localizations.pluralInString(1)}", + "${localizations.selectInString('he')}", ]); }, ), @@ -266,6 +271,11 @@ class Home extends StatelessWidget { '${localizations.singleQuotePlural(2)}', '${localizations.doubleQuote}', '${localizations.doubleQuotePlural(2)}', + "${localizations.vehicleSelect('truck')}", + "${localizations.singleQuoteSelect('sedan')}", + "${localizations.doubleQuoteSelect('cabriolet')}", + "${localizations.pluralInString(1)}", + "${localizations.selectInString('he')}", ]); }, ), @@ -608,6 +618,46 @@ void main() { "placeholders": { "count": {} } + }, + + "vehicleSelect": "{vehicleType, select, sedan{Sedan} cabriolet{Solid roof cabriolet} truck{16 wheel truck} other{Other}}", + "@vehicleSelect": { + "description": "A select message.", + "placeholders": { + "vehicleType": {} + } + }, + + "singleQuoteSelect": "{vehicleType, select, sedan{Sedan's elegance} cabriolet{Cabriolet' acceleration} truck{truck's heavy duty} other{Other's mirrors!}}", + "@singleQuoteSelect": { + "description": "A select message with a single quote.", + "placeholders": { + "vehicleType": {} + } + }, + + "doubleQuoteSelect": "{vehicleType, select, sedan{Sedan has \"elegance\"} cabriolet{Cabriolet has \"acceleration\"} truck{truck is \"heavy duty\"} other{Other have \"mirrors\"!}}", + "@doubleQuoteSelect": { + "description": "A select message with double quote.", + "placeholders": { + "vehicleType": {} + } + }, + + "pluralInString": "Oh, she found {count, plural, =1 {1 item} other {all {count} items} }!", + "@pluralInString": { + "description": "A plural message with prefix and suffix strings.", + "placeholders": { + "count": {} + } + }, + + "selectInString": "Indeed, {gender, select, male {he likes} female {she likes} other {they like} } Flutter!", + "@selectInString": { + "description": "A select message with prefix and suffix strings.", + "placeholders": { + "gender": {} + } } } '''; @@ -646,13 +696,18 @@ void main() { "helloWorldsOn": "{count,plural, =0{ES - Hello on {date}} =1{ES - Hello World, on {date}} =2{ES - Hello two worlds, on {date}} other{ES - Hello other {count} worlds, on {date}}}", "helloWorldPopulation": "{ES - count,plural, =1{ES - Hello World of {population} citizens} =2{ES - Hello two worlds with {population} total citizens} many{ES - Hello all {count} worlds, with a total of {population} citizens} other{ES - Hello other {count} worlds, with a total of {population} citizens}}", "helloWorldInterpolation": "ES - [{hello}] #{world}#", - "helloWorldsInterpolation": "ES - {count,plural, other {ES - [{hello}] -{world}- #{count}#}}", + "helloWorldsInterpolation": "{count,plural, other {ES - [{hello}] -{world}- #{count}#}}", "dollarSign": "ES - $!", "dollarSignPlural": "{count,plural, =1{ES - One $} other{ES - Many $}}", "singleQuote": "ES - Flutter's amazing!", "singleQuotePlural": "{count,plural, =1{ES - Flutter's amazing, times 1!} other{ES - Flutter's amazing, times {count}!}}", "doubleQuote": "ES - Flutter is \"amazing\"!", - "doubleQuotePlural": "{count,plural, =1{ES - Flutter is \"amazing\", times 1!} other{ES - Flutter is \"amazing\", times {count}!}}" + "doubleQuotePlural": "{count,plural, =1{ES - Flutter is \"amazing\", times 1!} other{ES - Flutter is \"amazing\", times {count}!}}", + "vehicleSelect": "{vehicleType, select, sedan{ES - Sedan} cabriolet{ES - Solid roof cabriolet} truck{ES - 16 wheel truck} other{ES - Other}}", + "singleQuoteSelect": "{vehicleType, select, sedan{ES - Sedan's elegance} cabriolet{ES - Cabriolet' acceleration} truck{ES - truck's heavy duty} other{ES - Other's mirrors!}}", + "doubleQuoteSelect": "{vehicleType, select, sedan{ES - Sedan has \"elegance\"} cabriolet{ES - Cabriolet has \"acceleration\"} truck{ES - truck is \"heavy duty\"} other{ES - Other have \"mirrors\"!}}", + "pluralInString": "ES - Oh, she found {count, plural, =1 {ES - 1 item} other {ES - all {count} items} }ES - !", + "selectInString": "ES - Indeed, {gender, select, male {ES - he likes} female {ES - she likes} other {ES - they like} } ES - Flutter!" } ''';