Unverified Commit e8436970 authored by TabooSun's avatar TabooSun Committed by GitHub

Gen l10n add named argument option (#138663)

Add an option to use named argument for generated method.

Fix #116308
parent 0d6927cb
...@@ -118,4 +118,5 @@ Kim Jiun <kkimj@hanyang.ac.kr> ...@@ -118,4 +118,5 @@ Kim Jiun <kkimj@hanyang.ac.kr>
LinXunFeng <linxunfeng@yeah.net> LinXunFeng <linxunfeng@yeah.net>
Sabin Neupane <sabin.neupane26@gmail.com> Sabin Neupane <sabin.neupane26@gmail.com>
Mahdi Bagheri <1839491@gmail.com> Mahdi Bagheri <1839491@gmail.com>
Mok Kah Wai <taboosun1996@gmail.com>
Lucas Saudon <lsaudon@gmail.com> Lucas Saudon <lsaudon@gmail.com>
...@@ -207,6 +207,10 @@ class GenerateLocalizationsCommand extends FlutterCommand { ...@@ -207,6 +207,10 @@ class GenerateLocalizationsCommand extends FlutterCommand {
'and "}" is treated as a string if it does not close any previous "{" ' 'and "}" is treated as a string if it does not close any previous "{" '
'that is treated as a special character.', 'that is treated as a special character.',
); );
argParser.addFlag(
'use-named-parameters',
help: 'Whether or not to use named parameters for the generated localization methods.',
);
} }
final FileSystem _fileSystem; final FileSystem _fileSystem;
......
...@@ -73,6 +73,7 @@ Future<LocalizationsGenerator> generateLocalizations({ ...@@ -73,6 +73,7 @@ Future<LocalizationsGenerator> generateLocalizations({
logger: logger, logger: logger,
suppressWarnings: options.suppressWarnings, suppressWarnings: options.suppressWarnings,
useRelaxedSyntax: options.relaxSyntax, useRelaxedSyntax: options.relaxSyntax,
useNamedParameters: options.useNamedParameters,
) )
..loadResources() ..loadResources()
..writeOutputFiles(isFromYaml: true, useCRLF: useCRLF); ..writeOutputFiles(isFromYaml: true, useCRLF: useCRLF);
...@@ -122,9 +123,9 @@ String _syntheticL10nPackagePath(FileSystem fileSystem) => fileSystem.path.join( ...@@ -122,9 +123,9 @@ String _syntheticL10nPackagePath(FileSystem fileSystem) => fileSystem.path.join(
// For example, if placeholders are used for plurals and no type was specified, then the type will // For example, if placeholders are used for plurals and no type was specified, then the type will
// automatically set to 'num'. Similarly, if such placeholders are used for selects, then the type // automatically set to 'num'. Similarly, if such placeholders are used for selects, then the type
// will be set to 'String'. For such placeholders that are used for both, we should throw an error. // will be set to 'String'. For such placeholders that are used for both, we should throw an error.
List<String> generateMethodParameters(Message message) { List<String> generateMethodParameters(Message message, bool useNamedParameters) {
return message.placeholders.values.map((Placeholder placeholder) { return message.placeholders.values.map((Placeholder placeholder) {
return '${placeholder.type} ${placeholder.name}'; return '${useNamedParameters ? 'required ' : ''}${placeholder.type} ${placeholder.name}';
}).toList(); }).toList();
} }
...@@ -231,7 +232,7 @@ Map<String, String> pluralCases = <String, String>{ ...@@ -231,7 +232,7 @@ Map<String, String> pluralCases = <String, String>{
'other': 'other', 'other': 'other',
}; };
String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale) { String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale, bool useNamedParameters) {
final String comment = message final String comment = message
.description .description
?.split('\n') ?.split('\n')
...@@ -242,11 +243,11 @@ String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale) { ...@@ -242,11 +243,11 @@ String generateBaseClassMethod(Message message, LocaleInfo? templateArbLocale) {
/// **'${generateString(message.value)}'**'''; /// **'${generateString(message.value)}'**''';
if (message.placeholders.isNotEmpty) { if (message.placeholders.isNotEmpty) {
return baseClassMethodTemplate return (useNamedParameters ? baseClassMethodWithNamedParameterTemplate : baseClassMethodTemplate)
.replaceAll('@(comment)', comment) .replaceAll('@(comment)', comment)
.replaceAll('@(templateLocaleTranslationComment)', templateLocaleTranslationComment) .replaceAll('@(templateLocaleTranslationComment)', templateLocaleTranslationComment)
.replaceAll('@(name)', message.resourceId) .replaceAll('@(name)', message.resourceId)
.replaceAll('@(parameters)', generateMethodParameters(message).join(', ')); .replaceAll('@(parameters)', generateMethodParameters(message, useNamedParameters).join(', '));
} }
return baseClassGetterTemplate return baseClassGetterTemplate
.replaceAll('@(comment)', comment) .replaceAll('@(comment)', comment)
...@@ -492,6 +493,7 @@ class LocalizationsGenerator { ...@@ -492,6 +493,7 @@ class LocalizationsGenerator {
required Logger logger, required Logger logger,
bool suppressWarnings = false, bool suppressWarnings = false,
bool useRelaxedSyntax = false, bool useRelaxedSyntax = false,
bool useNamedParameters = false,
}) { }) {
final Directory? projectDirectory = projectDirFromPath(fileSystem, projectPathString); final Directory? projectDirectory = projectDirFromPath(fileSystem, projectPathString);
final Directory inputDirectory = inputDirectoryFromPath(fileSystem, inputPathString, projectDirectory); final Directory inputDirectory = inputDirectoryFromPath(fileSystem, inputPathString, projectDirectory);
...@@ -516,6 +518,7 @@ class LocalizationsGenerator { ...@@ -516,6 +518,7 @@ class LocalizationsGenerator {
logger: logger, logger: logger,
suppressWarnings: suppressWarnings, suppressWarnings: suppressWarnings,
useRelaxedSyntax: useRelaxedSyntax, useRelaxedSyntax: useRelaxedSyntax,
useNamedParameters: useNamedParameters,
); );
} }
...@@ -541,6 +544,7 @@ class LocalizationsGenerator { ...@@ -541,6 +544,7 @@ class LocalizationsGenerator {
this.useEscaping = false, this.useEscaping = false,
this.suppressWarnings = false, this.suppressWarnings = false,
this.useRelaxedSyntax = false, this.useRelaxedSyntax = false,
this.useNamedParameters = false,
}); });
final FileSystem _fs; final FileSystem _fs;
...@@ -685,6 +689,14 @@ class LocalizationsGenerator { ...@@ -685,6 +689,14 @@ class LocalizationsGenerator {
/// Whether or not to suppress warnings or not. /// Whether or not to suppress warnings or not.
final bool suppressWarnings; final bool suppressWarnings;
/// Whether to generate the Dart localization methods with named parameters.
///
/// If this sets to true, the generated Dart localization methods will be:
/// ```
/// String helloWorld({required String name});
/// ```
final bool useNamedParameters;
static bool _isNotReadable(FileStat fileStat) { static bool _isNotReadable(FileStat fileStat) {
final String rawStatString = fileStat.modeString(); final String rawStatString = fileStat.modeString();
// Removes potential prepended permission bits, such as '(suid)' and '(guid)'. // Removes potential prepended permission bits, such as '(suid)' and '(guid)'.
...@@ -1124,7 +1136,7 @@ class LocalizationsGenerator { ...@@ -1124,7 +1136,7 @@ class LocalizationsGenerator {
return fileTemplate return fileTemplate
.replaceAll('@(header)', header.isEmpty ? '' : '$header\n') .replaceAll('@(header)', header.isEmpty ? '' : '$header\n')
.replaceAll('@(class)', className) .replaceAll('@(class)', className)
.replaceAll('@(methods)', _allMessages.map((Message message) => generateBaseClassMethod(message, _templateArbLocale)).join('\n')) .replaceAll('@(methods)', _allMessages.map((Message message) => generateBaseClassMethod(message, _templateArbLocale, useNamedParameters)).join('\n'))
.replaceAll('@(importFile)', '$directory/$outputFileName') .replaceAll('@(importFile)', '$directory/$outputFileName')
.replaceAll('@(supportedLocales)', supportedLocalesCode.join(',\n ')) .replaceAll('@(supportedLocales)', supportedLocalesCode.join(',\n '))
.replaceAll('@(supportedLanguageCodes)', supportedLanguageCodes.join(', ')) .replaceAll('@(supportedLanguageCodes)', supportedLanguageCodes.join(', '))
...@@ -1306,9 +1318,9 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", " ...@@ -1306,9 +1318,9 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "
} }
final String messageString = generateVariables(node, isRoot: true); final String messageString = generateVariables(node, isRoot: true);
final String tempVarLines = tempVariables.isEmpty ? '' : '${tempVariables.join('\n')}\n'; final String tempVarLines = tempVariables.isEmpty ? '' : '${tempVariables.join('\n')}\n';
return methodTemplate return (useNamedParameters ? methodWithNamedParameterTemplate : methodTemplate)
.replaceAll('@(name)', message.resourceId) .replaceAll('@(name)', message.resourceId)
.replaceAll('@(parameters)', generateMethodParameters(message).join(', ')) .replaceAll('@(parameters)', generateMethodParameters(message, useNamedParameters).join(', '))
.replaceAll('@(dateFormatting)', generateDateFormattingLogic(message)) .replaceAll('@(dateFormatting)', generateDateFormattingLogic(message))
.replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message)) .replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message))
.replaceAll('@(tempVars)', tempVarLines) .replaceAll('@(tempVars)', tempVarLines)
......
...@@ -142,6 +142,14 @@ const String methodTemplate = ''' ...@@ -142,6 +142,14 @@ const String methodTemplate = '''
@(tempVars) return @(message); @(tempVars) return @(message);
}'''; }''';
const String methodWithNamedParameterTemplate = '''
@override
String @(name)({@(parameters)}) {
@(dateFormatting)
@(numberFormatting)
@(tempVars) return @(message);
}''';
const String pluralVariableTemplate = ''' const String pluralVariableTemplate = '''
String @(varName) = intl.Intl.pluralLogic( String @(varName) = intl.Intl.pluralLogic(
@(count), @(count),
...@@ -195,6 +203,13 @@ const String baseClassMethodTemplate = ''' ...@@ -195,6 +203,13 @@ const String baseClassMethodTemplate = '''
String @(name)(@(parameters)); String @(name)(@(parameters));
'''; ''';
const String baseClassMethodWithNamedParameterTemplate = '''
@(comment)
///
@(templateLocaleTranslationComment)
String @(name)({@(parameters)});
''';
// DELEGATE CLASS TEMPLATES // DELEGATE CLASS TEMPLATES
const String delegateClassTemplate = ''' const String delegateClassTemplate = '''
......
...@@ -355,6 +355,7 @@ class LocalizationOptions { ...@@ -355,6 +355,7 @@ class LocalizationOptions {
bool? useEscaping, bool? useEscaping,
bool? suppressWarnings, bool? suppressWarnings,
bool? relaxSyntax, bool? relaxSyntax,
bool? useNamedParameters,
}) : templateArbFile = templateArbFile ?? 'app_en.arb', }) : templateArbFile = templateArbFile ?? 'app_en.arb',
outputLocalizationFile = outputLocalizationFile ?? 'app_localizations.dart', outputLocalizationFile = outputLocalizationFile ?? 'app_localizations.dart',
outputClass = outputClass ?? 'AppLocalizations', outputClass = outputClass ?? 'AppLocalizations',
...@@ -365,7 +366,8 @@ class LocalizationOptions { ...@@ -365,7 +366,8 @@ class LocalizationOptions {
format = format ?? false, format = format ?? false,
useEscaping = useEscaping ?? false, useEscaping = useEscaping ?? false,
suppressWarnings = suppressWarnings ?? false, suppressWarnings = suppressWarnings ?? false,
relaxSyntax = relaxSyntax ?? false; relaxSyntax = relaxSyntax ?? false,
useNamedParameters = useNamedParameters ?? false;
/// The `--arb-dir` argument. /// The `--arb-dir` argument.
/// ///
...@@ -467,6 +469,14 @@ class LocalizationOptions { ...@@ -467,6 +469,14 @@ class LocalizationOptions {
/// This was added in for backward compatibility and is not recommended /// This was added in for backward compatibility and is not recommended
/// as it may mask errors. /// as it may mask errors.
final bool relaxSyntax; final bool relaxSyntax;
/// The `use-named-parameters` argument.
///
/// Whether or not to use named parameters for the generated localization
/// methods.
///
/// Defaults to `false`.
final bool useNamedParameters;
} }
/// Parse the localizations configuration options from [file]. /// Parse the localizations configuration options from [file].
...@@ -511,6 +521,7 @@ LocalizationOptions parseLocalizationsOptionsFromYAML({ ...@@ -511,6 +521,7 @@ LocalizationOptions parseLocalizationsOptionsFromYAML({
useEscaping: _tryReadBool(yamlNode, 'use-escaping', logger), useEscaping: _tryReadBool(yamlNode, 'use-escaping', logger),
suppressWarnings: _tryReadBool(yamlNode, 'suppress-warnings', logger), suppressWarnings: _tryReadBool(yamlNode, 'suppress-warnings', logger),
relaxSyntax: _tryReadBool(yamlNode, 'relax-syntax', logger), relaxSyntax: _tryReadBool(yamlNode, 'relax-syntax', logger),
useNamedParameters: _tryReadBool(yamlNode, 'use-named-parameters', logger),
); );
} }
...@@ -537,6 +548,7 @@ LocalizationOptions parseLocalizationsOptionsFromCommand({ ...@@ -537,6 +548,7 @@ LocalizationOptions parseLocalizationsOptionsFromCommand({
format: command.boolArg('format'), format: command.boolArg('format'),
useEscaping: command.boolArg('use-escaping'), useEscaping: command.boolArg('use-escaping'),
suppressWarnings: command.boolArg('suppress-warnings'), suppressWarnings: command.boolArg('suppress-warnings'),
useNamedParameters: command.boolArg('use-named-parameters'),
); );
} }
......
...@@ -96,6 +96,7 @@ void main() { ...@@ -96,6 +96,7 @@ void main() {
bool areResourceAttributeRequired = false, bool areResourceAttributeRequired = false,
bool suppressWarnings = false, bool suppressWarnings = false,
bool relaxSyntax = false, bool relaxSyntax = false,
bool useNamedParameters = false,
void Function(Directory)? setup, void Function(Directory)? setup,
} }
) { ) {
...@@ -128,6 +129,7 @@ void main() { ...@@ -128,6 +129,7 @@ void main() {
areResourceAttributesRequired: areResourceAttributeRequired, areResourceAttributesRequired: areResourceAttributeRequired,
suppressWarnings: suppressWarnings, suppressWarnings: suppressWarnings,
useRelaxedSyntax: relaxSyntax, useRelaxedSyntax: relaxSyntax,
useNamedParameters: useNamedParameters,
) )
..loadResources() ..loadResources()
..writeOutputFiles(isFromYaml: isFromYaml); ..writeOutputFiles(isFromYaml: isFromYaml);
...@@ -2491,4 +2493,43 @@ NumberFormat.decimalPatternDigits( ...@@ -2491,4 +2493,43 @@ NumberFormat.decimalPatternDigits(
setupLocalizations(<String, String>{ 'en': dollarSignWithSelect }); setupLocalizations(<String, String>{ 'en': dollarSignWithSelect });
expect(getGeneratedFileContent(locale: 'en'), contains(r'\$nice_bug\nHello Bug! Manifistation #1 $_temp0')); expect(getGeneratedFileContent(locale: 'en'), contains(r'\$nice_bug\nHello Bug! Manifistation #1 $_temp0'));
}); });
testWithoutContext('can generate method with named parameter', () {
const String arbFile = '''
{
"helloName": "Hello {name}!",
"@helloName": {
"description": "A more personal greeting",
"placeholders": {
"name": {
"type": "String",
"description": "The name of the person to greet"
}
}
},
"helloNameAndAge": "Hello {name}! You are {age} years old.",
"@helloNameAndAge": {
"description": "A more personal greeting",
"placeholders": {
"name": {
"type": "String",
"description": "The name of the person to greet"
},
"age": {
"type": "int",
"description": "The age of the person to greet"
}
}
}
}
''';
setupLocalizations(<String, String>{ 'en': arbFile }, useNamedParameters: true);
final String localizationsFile = getGeneratedFileContent(locale: 'en');
expect(localizationsFile, containsIgnoringWhitespace(r'''
String helloName({required String name}) {
'''));
expect(localizationsFile, containsIgnoringWhitespace(r'''
String helloNameAndAge({required String name, required int age}) {
'''));
});
} }
...@@ -11,7 +11,12 @@ import 'test_data/gen_l10n_project.dart'; ...@@ -11,7 +11,12 @@ import 'test_data/gen_l10n_project.dart';
import 'test_driver.dart'; import 'test_driver.dart';
import 'test_utils.dart'; import 'test_utils.dart';
final GenL10nProject project = GenL10nProject(); final GenL10nProject project = GenL10nProject(
useNamedParameters: false,
);
final GenL10nProject projectWithNamedParameter = GenL10nProject(
useNamedParameters: true,
);
// Verify that the code generated by gen_l10n executes correctly. // Verify that the code generated by gen_l10n executes correctly.
// It can fail if gen_l10n produces a lib/l10n/app_localizations.dart that // It can fail if gen_l10n produces a lib/l10n/app_localizations.dart that
...@@ -180,4 +185,11 @@ void main() { ...@@ -180,4 +185,11 @@ void main() {
final StringBuffer stdout = await runApp(); final StringBuffer stdout = await runApp();
expectOutput(stdout); expectOutput(stdout);
}); });
testWithoutContext('generated l10n classes produce expected localized strings when named parameter is used', () async {
await projectWithNamedParameter.setUpIn(tempDir);
flutter = FlutterRunTestDriver(tempDir);
final StringBuffer stdout = await runApp();
expectOutput(stdout);
});
} }
...@@ -8,6 +8,8 @@ import '../test_utils.dart'; ...@@ -8,6 +8,8 @@ import '../test_utils.dart';
import 'project.dart'; import 'project.dart';
class GenL10nProject extends Project { class GenL10nProject extends Project {
GenL10nProject({required this.useNamedParameters});
@override @override
Future<void> setUpIn(Directory dir, { Future<void> setUpIn(Directory dir, {
bool useDeferredLoading = false, bool useDeferredLoading = false,
...@@ -26,10 +28,12 @@ class GenL10nProject extends Project { ...@@ -26,10 +28,12 @@ class GenL10nProject extends Project {
writeFile(fileSystem.path.join(dir.path, 'l10n.yaml'), l10nYaml( writeFile(fileSystem.path.join(dir.path, 'l10n.yaml'), l10nYaml(
useDeferredLoading: useDeferredLoading, useDeferredLoading: useDeferredLoading,
useSyntheticPackage: useSyntheticPackage, useSyntheticPackage: useSyntheticPackage,
useNamedParameters: useNamedParameters,
)); ));
return super.setUpIn(dir); return super.setUpIn(dir);
} }
@override @override
final String pubspec = ''' final String pubspec = '''
name: test_l10n_project name: test_l10n_project
...@@ -44,386 +48,109 @@ dependencies: ...@@ -44,386 +48,109 @@ dependencies:
intl: any # Pick up the pinned version from flutter_localizations intl: any # Pick up the pinned version from flutter_localizations
'''; ''';
@override String? _main;
final String main = r'''
import 'package:flutter/material.dart';
import 'l10n/app_localizations.dart'; @override
String get main =>
_main ??= (useNamedParameters ? _getMainWithNamedParameters() : _getMain());
class LocaleBuilder extends StatelessWidget { final bool useNamedParameters;
const LocaleBuilder({
Key? key,
this.locale,
this.test,
required this.callback,
}) : super(key: key);
final Locale? locale; final String appEn = r'''
final String? test; {
final void Function (BuildContext context) callback; "@@locale": "en",
@override build(BuildContext context) { "helloWorld": "Hello World",
return Localizations.override( "@helloWorld": {
locale: locale, "description": "The conventional newborn programmer greeting"
context: context, },
child: ResultBuilder(
test: test,
callback: callback,
),
);
}
}
class ResultBuilder extends StatelessWidget { "helloNewlineWorld": "Hello \n World",
const ResultBuilder({ "@helloNewlineWorld": {
Key? key, "description": "The JSON decoder should convert backslash-n to a newline character in the generated Dart string."
this.test, },
required this.callback,
}) : super(key: key);
final String? test; "testDollarSign": "Hello $ World",
final void Function (BuildContext context) callback; "@testDollarSign": {
"description": "The generated Dart String should handle the dollar sign correctly."
},
@override build(BuildContext context) { "hello": "Hello {world}",
return Builder( "@hello": {
builder: (BuildContext context) { "description": "A message with a single parameter",
try { "placeholders": {
callback(context); "world": {}
} on Exception catch (e) {
print('#l10n A(n) $e has occurred trying to generate "$test" results.');
print('#l10n END');
} }
return Container();
}, },
);
"greeting": "{hello} {world}",
"@greeting": {
"description": "A message with a two parameters",
"placeholders": {
"hello": {},
"world": {}
} }
} },
class Home extends StatelessWidget { "helloWorldOn": "Hello World on {date}",
@override "@helloWorldOn": {
Widget build(BuildContext context) { "description": "A message with a date parameter",
final List<String> results = []; "placeholders": {
return Row( "date": {
children: <Widget>[ "type": "DateTime",
LocaleBuilder( "format": "yMMMMEEEEd"
test: 'supportedLocales', }
callback: (BuildContext context) {
results.add('--- supportedLocales tests ---');
int n = 0;
for (Locale locale in AppLocalizations.supportedLocales) {
String languageCode = locale.languageCode;
String? countryCode = locale.countryCode;
String? scriptCode = locale.scriptCode;
results.add('supportedLocales[$n]: languageCode: $languageCode, countryCode: $countryCode, scriptCode: $scriptCode');
n += 1;
} }
}, },
),
LocaleBuilder( "helloWorldDuring": "Hello World from {startDate} to {endDate}",
locale: Locale('en', 'CA'), "@helloWorldDuring": {
test: 'countryCode - en_CA', "description": "A message with two date parameters",
callback: (BuildContext context) { "placeholders": {
results.add('--- countryCode (en_CA) tests ---'); "startDate": {
results.add(AppLocalizations.of(context)!.helloWorld); "type": "DateTime",
results.add(AppLocalizations.of(context)!.hello("CA fallback World")); "format": "y"
}, },
), "endDate": {
LocaleBuilder( "type": "DateTime",
locale: Locale('en', 'GB'), "format": "y"
test: 'countryCode - en_GB', }
callback: (BuildContext context) { }
results.add('--- countryCode (en_GB) tests ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.hello("GB fallback World"));
}, },
),
LocaleBuilder( "helloOn": "Hello {world} on {date} at {time}",
locale: Locale('zh'), "@helloOn": {
test: 'zh', "description": "A message with date and string parameters",
callback: (BuildContext context) { "placeholders": {
results.add('--- zh ---'); "world": {
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.helloWorlds(0));
results.add(AppLocalizations.of(context)!.helloWorlds(1));
results.add(AppLocalizations.of(context)!.helloWorlds(2));
// Should use the fallback language, in this case,
// "Hello 世界" should be displayed.
results.add(AppLocalizations.of(context)!.hello("世界"));
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
results.add(AppLocalizations.of(context)!.helloCost("价钱", 123));
}, },
), "date": {
LocaleBuilder( "type": "DateTime",
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), "format": "yMd"
test: 'zh',
callback: (BuildContext context) {
results.add('--- scriptCode: zh_Hans ---');
results.add(AppLocalizations.of(context)!.helloWorld);
}, },
), "time": {
LocaleBuilder( "type": "DateTime",
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), "format": "Hm"
test: 'scriptCode - zh_Hant', }
callback: (BuildContext context) { }
results.add('--- scriptCode - zh_Hant ---');
results.add(AppLocalizations.of(context)!.helloWorld);
}, },
),
LocaleBuilder( "helloFor": "Hello for {value}",
locale: Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW', scriptCode: 'Hant'), "@helloFor": {
test: 'scriptCode - zh_TW_Hant', "description": "A message with a double parameter",
callback: (BuildContext context) { "placeholders": {
results.add('--- scriptCode - zh_Hant_TW ---'); "value": {
results.add(AppLocalizations.of(context)!.helloWorld); "type": "double",
"format": "compact"
}
}
}, },
),
LocaleBuilder( "helloCost": "Hello for {price} {value}",
locale: Locale('en'), "@helloCost": {
test: 'General formatting', "description": "A message with string and int (currency) parameters",
callback: (BuildContext context) { "placeholders": {
results.add('--- General formatting tests ---'); "price": {
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello("World")}',
'${localizations.greeting("Hello", "World")}',
'${localizations.helloWorldOn(DateTime(1960))}',
'${localizations.helloOn("world argument", DateTime(1960), DateTime(1960))}',
'${localizations.helloWorldDuring(DateTime(1960), DateTime(2020))}',
'${localizations.helloFor(123)}',
'${localizations.helloCost("price", 123)}',
'${localizations.helloCostWithOptionalParam("price", .5)}',
'${localizations.helloCostWithSpecialCharacter1("price", .5)}',
'${localizations.helloCostWithSpecialCharacter2("price", .5)}',
'${localizations.helloCostWithSpecialCharacter3("price", .5)}',
'${localizations.helloDecimalPattern(1200000)}',
'${localizations.helloPercentPattern(1200000)}',
'${localizations.helloScientificPattern(1200000)}',
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
'${localizations.helloAdjectiveWorlds(0, "new")}',
'${localizations.helloAdjectiveWorlds(1, "new")}',
'${localizations.helloAdjectiveWorlds(2, "new")}',
'${localizations.helloWorldsOn(0, DateTime(1960))}',
'${localizations.helloWorldsOn(1, DateTime(1960))}',
'${localizations.helloWorldsOn(2, DateTime(1960))}',
'${localizations.helloWorldPopulation(0, 100)}',
'${localizations.helloWorldPopulation(1, 101)}',
'${localizations.helloWorldPopulation(2, 102)}',
'${localizations.helloWorldsInterpolation(123, "Hello", "World")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(2)}',
"${localizations.vehicleSelect('truck')}",
"${localizations.singleQuoteSelect('sedan')}",
"${localizations.doubleQuoteSelect('cabriolet')}",
"${localizations.pluralInString(1)}",
"${localizations.selectInString('he')}",
"${localizations.selectWithPlaceholder('male', 'ice cream')}",
"${localizations.selectWithPlaceholder('female', 'chocolate')}",
"${localizations.selectInPlural('male', 1)}",
"${localizations.selectInPlural('male', 2)}",
"${localizations.selectInPlural('female', 1)}",
'${localizations.datetime1(DateTime(2023, 6, 26))}',
'${localizations.datetime2(DateTime(2023, 6, 26, 5, 23))}',
]);
},
),
LocaleBuilder(
locale: Locale('es'),
test: '--- es ---',
callback: (BuildContext context) {
results.add('--- es ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello("Mundo")}',
'${localizations.greeting("Hola", "Mundo")}',
'${localizations.helloWorldOn(DateTime(1960))}',
'${localizations.helloOn("world argument", DateTime(1960), DateTime(1960))}',
'${localizations.helloWorldDuring(DateTime(1960), DateTime(2020))}',
'${localizations.helloFor(123)}',
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
'${localizations.helloAdjectiveWorlds(0, "nuevo")}',
'${localizations.helloAdjectiveWorlds(1, "nuevo")}',
'${localizations.helloAdjectiveWorlds(2, "nuevo")}',
'${localizations.helloWorldsOn(0, DateTime(1960))}',
'${localizations.helloWorldsOn(1, DateTime(1960))}',
'${localizations.helloWorldsOn(2, DateTime(1960))}',
'${localizations.helloWorldPopulation(0, 100)}',
'${localizations.helloWorldPopulation(1, 101)}',
'${localizations.helloWorldPopulation(2, 102)}',
'${localizations.helloWorldsInterpolation(123, "Hola", "Mundo")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(2)}',
"${localizations.vehicleSelect('truck')}",
"${localizations.singleQuoteSelect('sedan')}",
"${localizations.doubleQuoteSelect('cabriolet')}",
"${localizations.pluralInString(1)}",
"${localizations.selectInString('he')}",
]);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'es', countryCode: '419'),
test: 'countryCode - es_419',
callback: (BuildContext context) {
results.add('--- es_419 ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll([
'${localizations.helloWorld}',
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
]);
},
),
LocaleBuilder(
callback: (BuildContext context) {
try {
int n = 0;
for (final String result in results) {
// Newline character replacement is necessary because
// the stream breaks up stdout by new lines.
print('#l10n $n (${result.replaceAll('\n', '_NEWLINE_')})');
n += 1;
}
}
finally {
print('#l10n END');
}
},
),
],
);
}
}
void main() {
runApp(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Home(),
),
);
}
''';
final String appEn = r'''
{
"@@locale": "en",
"helloWorld": "Hello World",
"@helloWorld": {
"description": "The conventional newborn programmer greeting"
},
"helloNewlineWorld": "Hello \n World",
"@helloNewlineWorld": {
"description": "The JSON decoder should convert backslash-n to a newline character in the generated Dart string."
},
"testDollarSign": "Hello $ World",
"@testDollarSign": {
"description": "The generated Dart String should handle the dollar sign correctly."
},
"hello": "Hello {world}",
"@hello": {
"description": "A message with a single parameter",
"placeholders": {
"world": {}
}
},
"greeting": "{hello} {world}",
"@greeting": {
"description": "A message with a two parameters",
"placeholders": {
"hello": {},
"world": {}
}
},
"helloWorldOn": "Hello World on {date}",
"@helloWorldOn": {
"description": "A message with a date parameter",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMMEEEEd"
}
}
},
"helloWorldDuring": "Hello World from {startDate} to {endDate}",
"@helloWorldDuring": {
"description": "A message with two date parameters",
"placeholders": {
"startDate": {
"type": "DateTime",
"format": "y"
},
"endDate": {
"type": "DateTime",
"format": "y"
}
}
},
"helloOn": "Hello {world} on {date} at {time}",
"@helloOn": {
"description": "A message with date and string parameters",
"placeholders": {
"world": {
},
"date": {
"type": "DateTime",
"format": "yMd"
},
"time": {
"type": "DateTime",
"format": "Hm"
}
}
},
"helloFor": "Hello for {value}",
"@helloFor": {
"description": "A message with a double parameter",
"placeholders": {
"value": {
"type": "double",
"format": "compact"
}
}
},
"helloCost": "Hello for {price} {value}",
"@helloCost": {
"description": "A message with string and int (currency) parameters",
"placeholders": {
"price": {
}, },
"value": { "value": {
"type": "int", "type": "int",
...@@ -777,18 +504,590 @@ void main() { ...@@ -777,18 +504,590 @@ void main() {
} }
'''; ''';
String l10nYaml({ String _getMain() => r'''
required bool useDeferredLoading, import 'package:flutter/material.dart';
required bool useSyntheticPackage,
}) {
String l10nYamlString = '';
if (useDeferredLoading) { import 'l10n/app_localizations.dart';
l10nYamlString += 'use-deferred-loading: true\n';
class LocaleBuilder extends StatelessWidget {
const LocaleBuilder({
Key? key,
this.locale,
this.test,
required this.callback,
}) : super(key: key);
final Locale? locale;
final String? test;
final void Function (BuildContext context) callback;
@override build(BuildContext context) {
return Localizations.override(
locale: locale,
context: context,
child: ResultBuilder(
test: test,
callback: callback,
),
);
} }
}
if (!useSyntheticPackage) { class ResultBuilder extends StatelessWidget {
l10nYamlString += 'synthetic-package: false\n'; const ResultBuilder({
Key? key,
this.test,
required this.callback,
}) : super(key: key);
final String? test;
final void Function (BuildContext context) callback;
@override build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
try {
callback(context);
} on Exception catch (e) {
print('#l10n A(n) $e has occurred trying to generate "$test" results.');
print('#l10n END');
}
return Container();
},
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<String> results = [];
return Row(
children: <Widget>[
LocaleBuilder(
test: 'supportedLocales',
callback: (BuildContext context) {
results.add('--- supportedLocales tests ---');
int n = 0;
for (Locale locale in AppLocalizations.supportedLocales) {
String languageCode = locale.languageCode;
String? countryCode = locale.countryCode;
String? scriptCode = locale.scriptCode;
results.add('supportedLocales[$n]: languageCode: $languageCode, countryCode: $countryCode, scriptCode: $scriptCode');
n += 1;
}
},
),
LocaleBuilder(
locale: Locale('en', 'CA'),
test: 'countryCode - en_CA',
callback: (BuildContext context) {
results.add('--- countryCode (en_CA) tests ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.hello("CA fallback World"));
},
),
LocaleBuilder(
locale: Locale('en', 'GB'),
test: 'countryCode - en_GB',
callback: (BuildContext context) {
results.add('--- countryCode (en_GB) tests ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.hello("GB fallback World"));
},
),
LocaleBuilder(
locale: Locale('zh'),
test: 'zh',
callback: (BuildContext context) {
results.add('--- zh ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.helloWorlds(0));
results.add(AppLocalizations.of(context)!.helloWorlds(1));
results.add(AppLocalizations.of(context)!.helloWorlds(2));
// Should use the fallback language, in this case,
// "Hello 世界" should be displayed.
results.add(AppLocalizations.of(context)!.hello("世界"));
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
results.add(AppLocalizations.of(context)!.helloCost("价钱", 123));
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'),
test: 'zh',
callback: (BuildContext context) {
results.add('--- scriptCode: zh_Hans ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
test: 'scriptCode - zh_Hant',
callback: (BuildContext context) {
results.add('--- scriptCode - zh_Hant ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW', scriptCode: 'Hant'),
test: 'scriptCode - zh_TW_Hant',
callback: (BuildContext context) {
results.add('--- scriptCode - zh_Hant_TW ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale('en'),
test: 'General formatting',
callback: (BuildContext context) {
results.add('--- General formatting tests ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello("World")}',
'${localizations.greeting("Hello", "World")}',
'${localizations.helloWorldOn(DateTime(1960))}',
'${localizations.helloOn("world argument", DateTime(1960), DateTime(1960))}',
'${localizations.helloWorldDuring(DateTime(1960), DateTime(2020))}',
'${localizations.helloFor(123)}',
'${localizations.helloCost("price", 123)}',
'${localizations.helloCostWithOptionalParam("price", .5)}',
'${localizations.helloCostWithSpecialCharacter1("price", .5)}',
'${localizations.helloCostWithSpecialCharacter2("price", .5)}',
'${localizations.helloCostWithSpecialCharacter3("price", .5)}',
'${localizations.helloDecimalPattern(1200000)}',
'${localizations.helloPercentPattern(1200000)}',
'${localizations.helloScientificPattern(1200000)}',
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
'${localizations.helloAdjectiveWorlds(0, "new")}',
'${localizations.helloAdjectiveWorlds(1, "new")}',
'${localizations.helloAdjectiveWorlds(2, "new")}',
'${localizations.helloWorldsOn(0, DateTime(1960))}',
'${localizations.helloWorldsOn(1, DateTime(1960))}',
'${localizations.helloWorldsOn(2, DateTime(1960))}',
'${localizations.helloWorldPopulation(0, 100)}',
'${localizations.helloWorldPopulation(1, 101)}',
'${localizations.helloWorldPopulation(2, 102)}',
'${localizations.helloWorldsInterpolation(123, "Hello", "World")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(2)}',
"${localizations.vehicleSelect('truck')}",
"${localizations.singleQuoteSelect('sedan')}",
"${localizations.doubleQuoteSelect('cabriolet')}",
"${localizations.pluralInString(1)}",
"${localizations.selectInString('he')}",
"${localizations.selectWithPlaceholder('male', 'ice cream')}",
"${localizations.selectWithPlaceholder('female', 'chocolate')}",
"${localizations.selectInPlural('male', 1)}",
"${localizations.selectInPlural('male', 2)}",
"${localizations.selectInPlural('female', 1)}",
'${localizations.datetime1(DateTime(2023, 6, 26))}',
'${localizations.datetime2(DateTime(2023, 6, 26, 5, 23))}',
]);
},
),
LocaleBuilder(
locale: Locale('es'),
test: '--- es ---',
callback: (BuildContext context) {
results.add('--- es ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello("Mundo")}',
'${localizations.greeting("Hola", "Mundo")}',
'${localizations.helloWorldOn(DateTime(1960))}',
'${localizations.helloOn("world argument", DateTime(1960), DateTime(1960))}',
'${localizations.helloWorldDuring(DateTime(1960), DateTime(2020))}',
'${localizations.helloFor(123)}',
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
'${localizations.helloAdjectiveWorlds(0, "nuevo")}',
'${localizations.helloAdjectiveWorlds(1, "nuevo")}',
'${localizations.helloAdjectiveWorlds(2, "nuevo")}',
'${localizations.helloWorldsOn(0, DateTime(1960))}',
'${localizations.helloWorldsOn(1, DateTime(1960))}',
'${localizations.helloWorldsOn(2, DateTime(1960))}',
'${localizations.helloWorldPopulation(0, 100)}',
'${localizations.helloWorldPopulation(1, 101)}',
'${localizations.helloWorldPopulation(2, 102)}',
'${localizations.helloWorldsInterpolation(123, "Hola", "Mundo")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(2)}',
"${localizations.vehicleSelect('truck')}",
"${localizations.singleQuoteSelect('sedan')}",
"${localizations.doubleQuoteSelect('cabriolet')}",
"${localizations.pluralInString(1)}",
"${localizations.selectInString('he')}",
]);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'es', countryCode: '419'),
test: 'countryCode - es_419',
callback: (BuildContext context) {
results.add('--- es_419 ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll([
'${localizations.helloWorld}',
'${localizations.helloWorlds(0)}',
'${localizations.helloWorlds(1)}',
'${localizations.helloWorlds(2)}',
]);
},
),
LocaleBuilder(
callback: (BuildContext context) {
try {
int n = 0;
for (final String result in results) {
// Newline character replacement is necessary because
// the stream breaks up stdout by new lines.
print('#l10n $n (${result.replaceAll('\n', '_NEWLINE_')})');
n += 1;
}
}
finally {
print('#l10n END');
}
},
),
],
);
}
}
void main() {
runApp(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Home(),
),
);
}
''';
String _getMainWithNamedParameters() => r'''
import 'package:flutter/material.dart';
import 'l10n/app_localizations.dart';
class LocaleBuilder extends StatelessWidget {
const LocaleBuilder({
Key? key,
this.locale,
this.test,
required this.callback,
}) : super(key: key);
final Locale? locale;
final String? test;
final void Function (BuildContext context) callback;
@override build(BuildContext context) {
return Localizations.override(
locale: locale,
context: context,
child: ResultBuilder(
test: test,
callback: callback,
),
);
}
}
class ResultBuilder extends StatelessWidget {
const ResultBuilder({
Key? key,
this.test,
required this.callback,
}) : super(key: key);
final String? test;
final void Function (BuildContext context) callback;
@override build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
try {
callback(context);
} on Exception catch (e) {
print('#l10n A(n) $e has occurred trying to generate "$test" results.');
print('#l10n END');
}
return Container();
},
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<String> results = [];
return Row(
children: <Widget>[
LocaleBuilder(
test: 'supportedLocales',
callback: (BuildContext context) {
results.add('--- supportedLocales tests ---');
int n = 0;
for (Locale locale in AppLocalizations.supportedLocales) {
String languageCode = locale.languageCode;
String? countryCode = locale.countryCode;
String? scriptCode = locale.scriptCode;
results.add('supportedLocales[$n]: languageCode: $languageCode, countryCode: $countryCode, scriptCode: $scriptCode');
n += 1;
}
},
),
LocaleBuilder(
locale: Locale('en', 'CA'),
test: 'countryCode - en_CA',
callback: (BuildContext context) {
results.add('--- countryCode (en_CA) tests ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.hello(world: "CA fallback World"));
},
),
LocaleBuilder(
locale: Locale('en', 'GB'),
test: 'countryCode - en_GB',
callback: (BuildContext context) {
results.add('--- countryCode (en_GB) tests ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.hello(world: "GB fallback World"));
},
),
LocaleBuilder(
locale: Locale('zh'),
test: 'zh',
callback: (BuildContext context) {
results.add('--- zh ---');
results.add(AppLocalizations.of(context)!.helloWorld);
results.add(AppLocalizations.of(context)!.helloWorlds(count: 0));
results.add(AppLocalizations.of(context)!.helloWorlds(count: 1));
results.add(AppLocalizations.of(context)!.helloWorlds(count: 2));
// Should use the fallback language, in this case,
// "Hello 世界" should be displayed.
results.add(AppLocalizations.of(context)!.hello(world: "世界"));
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
results.add(AppLocalizations.of(context)!.helloCost(price: "价钱", value: 123));
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'),
test: 'zh',
callback: (BuildContext context) {
results.add('--- scriptCode: zh_Hans ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
test: 'scriptCode - zh_Hant',
callback: (BuildContext context) {
results.add('--- scriptCode - zh_Hant ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW', scriptCode: 'Hant'),
test: 'scriptCode - zh_TW_Hant',
callback: (BuildContext context) {
results.add('--- scriptCode - zh_Hant_TW ---');
results.add(AppLocalizations.of(context)!.helloWorld);
},
),
LocaleBuilder(
locale: Locale('en'),
test: 'General formatting',
callback: (BuildContext context) {
results.add('--- General formatting tests ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello(world: "World")}',
'${localizations.greeting(hello: "Hello", world: "World")}',
'${localizations.helloWorldOn(date: DateTime(1960))}',
'${localizations.helloOn(world: "world argument", date: DateTime(1960), time: DateTime(1960))}',
'${localizations.helloWorldDuring(startDate: DateTime(1960), endDate: DateTime(2020))}',
'${localizations.helloFor(value: 123)}',
'${localizations.helloCost(price: "price", value: 123)}',
'${localizations.helloCostWithOptionalParam(price: "price", value: .5)}',
'${localizations.helloCostWithSpecialCharacter1(price: "price", value: .5)}',
'${localizations.helloCostWithSpecialCharacter2(price: "price", value: .5)}',
'${localizations.helloCostWithSpecialCharacter3(price: "price", value: .5)}',
'${localizations.helloDecimalPattern(value: 1200000)}',
'${localizations.helloPercentPattern(value: 1200000)}',
'${localizations.helloScientificPattern(value: 1200000)}',
'${localizations.helloWorlds(count: 0)}',
'${localizations.helloWorlds(count: 1)}',
'${localizations.helloWorlds(count: 2)}',
'${localizations.helloAdjectiveWorlds(count: 0, adjective: "new")}',
'${localizations.helloAdjectiveWorlds(count: 1, adjective: "new")}',
'${localizations.helloAdjectiveWorlds(count: 2, adjective: "new")}',
'${localizations.helloWorldsOn(count: 0, date: DateTime(1960))}',
'${localizations.helloWorldsOn(count: 1, date: DateTime(1960))}',
'${localizations.helloWorldsOn(count: 2, date: DateTime(1960))}',
'${localizations.helloWorldPopulation(count: 0, population: 100)}',
'${localizations.helloWorldPopulation(count: 1, population: 101)}',
'${localizations.helloWorldPopulation(count: 2, population: 102)}',
'${localizations.helloWorldsInterpolation(count: 123, hello: "Hello", world: "World")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(count: 1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(count: 2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(count: 2)}',
"${localizations.vehicleSelect(vehicleType: 'truck')}",
"${localizations.singleQuoteSelect(vehicleType: 'sedan')}",
"${localizations.doubleQuoteSelect(vehicleType: 'cabriolet')}",
"${localizations.pluralInString(count: 1)}",
"${localizations.selectInString(gender: 'he')}",
"${localizations.selectWithPlaceholder(gender: 'male', preference: 'ice cream')}",
"${localizations.selectWithPlaceholder(gender: 'female', preference: 'chocolate')}",
"${localizations.selectInPlural(gender: 'male', count: 1)}",
"${localizations.selectInPlural(gender: 'male', count: 2)}",
"${localizations.selectInPlural(gender: 'female', count: 1)}",
'${localizations.datetime1(today: DateTime(2023, 6, 26))}',
'${localizations.datetime2(current: DateTime(2023, 6, 26, 5, 23))}',
]);
},
),
LocaleBuilder(
locale: Locale('es'),
test: '--- es ---',
callback: (BuildContext context) {
results.add('--- es ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll(<String>[
'${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.testDollarSign}',
'${localizations.hello(world: "Mundo")}',
'${localizations.greeting(hello: "Hola", world: "Mundo")}',
'${localizations.helloWorldOn(date: DateTime(1960))}',
'${localizations.helloOn(world: "world argument", date: DateTime(1960), time: DateTime(1960))}',
'${localizations.helloWorldDuring(startDate: DateTime(1960), endDate: DateTime(2020))}',
'${localizations.helloFor(value: 123)}',
// helloCost is tested in 'zh' because 'es' currency format contains a
// non-breaking space character (U+00A0), which if removed,
// makes it hard to decipher why the test is failing.
'${localizations.helloWorlds(count: 0)}',
'${localizations.helloWorlds(count: 1)}',
'${localizations.helloWorlds(count: 2)}',
'${localizations.helloAdjectiveWorlds(count: 0, adjective: "nuevo")}',
'${localizations.helloAdjectiveWorlds(count: 1, adjective: "nuevo")}',
'${localizations.helloAdjectiveWorlds(count: 2, adjective: "nuevo")}',
'${localizations.helloWorldsOn(count: 0, date: DateTime(1960))}',
'${localizations.helloWorldsOn(count: 1, date: DateTime(1960))}',
'${localizations.helloWorldsOn(count: 2, date: DateTime(1960))}',
'${localizations.helloWorldPopulation(count: 0, population: 100)}',
'${localizations.helloWorldPopulation(count: 1, population: 101)}',
'${localizations.helloWorldPopulation(count: 2, population: 102)}',
'${localizations.helloWorldsInterpolation(count: 123, hello: "Hola", world: "Mundo")}',
'${localizations.dollarSign}',
'${localizations.dollarSignPlural(count: 1)}',
'${localizations.singleQuote}',
'${localizations.singleQuotePlural(count: 2)}',
'${localizations.doubleQuote}',
'${localizations.doubleQuotePlural(count: 2)}',
"${localizations.vehicleSelect(vehicleType: 'truck')}",
"${localizations.singleQuoteSelect(vehicleType: 'sedan')}",
"${localizations.doubleQuoteSelect(vehicleType: 'cabriolet')}",
"${localizations.pluralInString(count: 1)}",
"${localizations.selectInString(gender: 'he')}",
]);
},
),
LocaleBuilder(
locale: Locale.fromSubtags(languageCode: 'es', countryCode: '419'),
test: 'countryCode - es_419',
callback: (BuildContext context) {
results.add('--- es_419 ---');
final AppLocalizations localizations = AppLocalizations.of(context)!;
results.addAll([
'${localizations.helloWorld}',
'${localizations.helloWorlds(count: 0)}',
'${localizations.helloWorlds(count: 1)}',
'${localizations.helloWorlds(count: 2)}',
]);
},
),
LocaleBuilder(
callback: (BuildContext context) {
try {
int n = 0;
for (final String result in results) {
// Newline character replacement is necessary because
// the stream breaks up stdout by new lines.
print('#l10n $n (${result.replaceAll('\n', '_NEWLINE_')})');
n += 1;
}
}
finally {
print('#l10n END');
}
},
),
],
);
}
}
void main() {
runApp(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Home(),
),
);
}''';
String l10nYaml({
required bool useDeferredLoading,
required bool useSyntheticPackage,
required bool useNamedParameters,
}) {
String l10nYamlString = '';
if (useDeferredLoading) {
l10nYamlString += 'use-deferred-loading: true\n';
}
if (!useSyntheticPackage) {
l10nYamlString += 'synthetic-package: false\n';
}
if (useNamedParameters) {
l10nYamlString += 'use-named-parameters: true\n';
} }
return l10nYamlString; return l10nYamlString;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment