mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This Pull Request extends the functionality of the `flutter gen-l10n` command (and its behavior during hot restart/reload) related to `DateFormat` type placeholders and their `format`. Until now, it was impossible to take advantage of `intl`'s `DateFormat.something().add_somethingElse()`. The `.add_x()` part was impossible to achieve. This PR adds the ability to take advantage of these methods over `DateFormat`, by adding the `add_` formats after the `+` character in the `format` in placeholder configuration. You can even have multiple added format parts if needed. All within a single placeholder. <table> <tr> <th>Before the PR</th> <th>After the PR</th> </tr> <tr> <td> ```json { "bookingsPage_camo_dataLoaded": "CAMO data from {date} {time}.", "@bookingsPage_camo_dataLoaded": { "placeholders": { "date": { "type": "DateTime", "format": "yMMMd" }, "time": { "type": "DateTime", "format": "jm" } } }, } ``` </td> <td> ```json { "bookingsPage_camo_dataLoaded": "CAMO data from {date}.", "@bookingsPage_camo_dataLoaded": { "placeholders": { "date": { "type": "DateTime", "format": "yMMMd+jm" } } }, } ``` </td> </tr> </table> Resolves #155817. ## Next steps After this PR is merged, an update to [i18n | Flutter > Messages with dates](https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization#messages-with-dates) ([source](https://github.com/flutter/website/blob/main/src/content/ui/accessibility-and-internationalization/internationalization.md)) shall be made to include a mention of this new addition. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Andrew Kolos <andrewrkolos@gmail.com>
200 lines
8.2 KiB
Dart
200 lines
8.2 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
@Tags(<String>['flutter-test-driver'])
|
|
library;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/file.dart';
|
|
|
|
import '../src/common.dart';
|
|
import 'test_data/gen_l10n_project.dart';
|
|
import 'test_driver.dart';
|
|
import 'test_utils.dart';
|
|
|
|
final GenL10nProject project = GenL10nProject(
|
|
useNamedParameters: false,
|
|
);
|
|
final GenL10nProject projectWithNamedParameter = GenL10nProject(
|
|
useNamedParameters: true,
|
|
);
|
|
|
|
// Verify that the code generated by gen_l10n executes correctly.
|
|
// It can fail if gen_l10n produces a lib/l10n/app_localizations.dart that
|
|
// does not analyze cleanly.
|
|
void main() {
|
|
late Directory tempDir;
|
|
late FlutterRunTestDriver flutter;
|
|
|
|
setUp(() async {
|
|
tempDir = createResolvedTempDirectorySync('gen_l10n_test.');
|
|
});
|
|
|
|
tearDown(() async {
|
|
await flutter.stop();
|
|
tryToDelete(tempDir);
|
|
});
|
|
|
|
Future<StringBuffer> runApp() async {
|
|
// Run the app defined in GenL10nProject.main and wait for it to
|
|
// send '#l10n END' to its stdout.
|
|
final Completer<void> l10nEnd = Completer<void>();
|
|
final StringBuffer stdout = StringBuffer();
|
|
final StreamSubscription<String> subscription = flutter.stdout.listen((String line) {
|
|
if (line.contains('#l10n')) {
|
|
stdout.writeln(line.substring(line.indexOf('#l10n')));
|
|
}
|
|
if (line.contains('#l10n END')) {
|
|
l10nEnd.complete();
|
|
}
|
|
});
|
|
await flutter.run();
|
|
await l10nEnd.future;
|
|
await subscription.cancel();
|
|
return stdout;
|
|
}
|
|
|
|
void expectOutput(StringBuffer stdout) {
|
|
expect(stdout.toString(),
|
|
'#l10n 0 (--- supportedLocales tests ---)\n'
|
|
'#l10n 1 (supportedLocales[0]: languageCode: en, countryCode: null, scriptCode: null)\n'
|
|
'#l10n 2 (supportedLocales[1]: languageCode: en, countryCode: CA, scriptCode: null)\n'
|
|
'#l10n 3 (supportedLocales[2]: languageCode: en, countryCode: GB, scriptCode: null)\n'
|
|
'#l10n 4 (supportedLocales[3]: languageCode: es, countryCode: null, scriptCode: null)\n'
|
|
'#l10n 5 (supportedLocales[4]: languageCode: es, countryCode: 419, scriptCode: null)\n'
|
|
'#l10n 6 (supportedLocales[5]: languageCode: zh, countryCode: null, scriptCode: null)\n'
|
|
'#l10n 7 (supportedLocales[6]: languageCode: zh, countryCode: null, scriptCode: Hans)\n'
|
|
'#l10n 8 (supportedLocales[7]: languageCode: zh, countryCode: null, scriptCode: Hant)\n'
|
|
'#l10n 9 (supportedLocales[8]: languageCode: zh, countryCode: TW, scriptCode: Hant)\n'
|
|
'#l10n 10 (--- countryCode (en_CA) tests ---)\n'
|
|
'#l10n 11 (CA Hello World)\n'
|
|
'#l10n 12 (Hello CA fallback World)\n'
|
|
'#l10n 13 (--- countryCode (en_GB) tests ---)\n'
|
|
'#l10n 14 (GB Hello World)\n'
|
|
'#l10n 15 (Hello GB fallback World)\n'
|
|
'#l10n 16 (--- zh ---)\n'
|
|
'#l10n 17 (你好世界)\n'
|
|
'#l10n 18 (你好)\n'
|
|
'#l10n 19 (你好世界)\n'
|
|
'#l10n 20 (你好2个其他世界)\n'
|
|
'#l10n 21 (Hello 世界)\n'
|
|
'#l10n 22 (zh - Hello for 价钱 CNY123.00)\n'
|
|
'#l10n 23 (--- scriptCode: zh_Hans ---)\n'
|
|
'#l10n 24 (简体你好世界)\n'
|
|
'#l10n 25 (--- scriptCode - zh_Hant ---)\n'
|
|
'#l10n 26 (繁體你好世界)\n'
|
|
'#l10n 27 (--- scriptCode - zh_Hant_TW ---)\n'
|
|
'#l10n 28 (台灣繁體你好世界)\n'
|
|
'#l10n 29 (--- General formatting tests ---)\n'
|
|
'#l10n 30 (Hello World)\n'
|
|
'#l10n 31 (Hello _NEWLINE_ World)\n'
|
|
'#l10n 32 (Hello \$ World)\n'
|
|
'#l10n 33 (Hello World)\n'
|
|
'#l10n 34 (Hello World)\n'
|
|
'#l10n 35 (Hello World on Friday, January 1, 1960)\n'
|
|
'#l10n 36 (Hello world argument on 1/1/1960 at 00:00)\n'
|
|
'#l10n 37 (Hello World from 1960 to 2020)\n'
|
|
'#l10n 38 (Hello for 123)\n'
|
|
'#l10n 39 (Hello for price USD123.00)\n'
|
|
'#l10n 40 (Hello for price BTC0.50 (with optional param))\n'
|
|
"#l10n 41 (Hello for price BTC'0.50 (with special character))\n"
|
|
'#l10n 42 (Hello for price BTC"0.50 (with special character))\n'
|
|
'#l10n 43 (Hello for price BTC"\'0.50 (with special character))\n'
|
|
'#l10n 44 (Hello for Decimal Pattern 1,200,000)\n'
|
|
'#l10n 45 (Hello for Percent Pattern 120,000,000%)\n'
|
|
'#l10n 46 (Hello for Scientific Pattern 1E6)\n'
|
|
'#l10n 47 (Hello)\n'
|
|
'#l10n 48 (Hello World)\n'
|
|
'#l10n 49 (Hello two worlds)\n'
|
|
'#l10n 50 (Hello)\n'
|
|
'#l10n 51 (Hello new World)\n'
|
|
'#l10n 52 (Hello two new worlds)\n'
|
|
'#l10n 53 (Hello on Friday, January 1, 1960)\n'
|
|
'#l10n 54 (Hello World, on Friday, January 1, 1960)\n'
|
|
'#l10n 55 (Hello two worlds, on Friday, January 1, 1960)\n'
|
|
'#l10n 56 (Hello other 0 worlds, with a total of 100 citizens)\n'
|
|
'#l10n 57 (Hello World of 101 citizens)\n'
|
|
'#l10n 58 (Hello two worlds with 102 total citizens)\n'
|
|
'#l10n 59 ([Hello] -World- #123#)\n'
|
|
'#l10n 60 (\$!)\n'
|
|
'#l10n 61 (One \$)\n'
|
|
"#l10n 62 (Flutter's amazing!)\n"
|
|
"#l10n 63 (Flutter's amazing, times 2!)\n"
|
|
'#l10n 64 (Flutter is "amazing"!)\n'
|
|
'#l10n 65 (Flutter is "amazing", times 2!)\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 (Indeed, he likes ice cream!)\n'
|
|
'#l10n 72 (Indeed, she likes chocolate!)\n'
|
|
'#l10n 73 (he)\n'
|
|
'#l10n 74 (they)\n'
|
|
'#l10n 75 (she)\n'
|
|
'#l10n 76 (6/26/2023)\n'
|
|
'#l10n 77 (5:23:00 AM)\n'
|
|
'#l10n 78 (10/6/2024 11:29:48 PM and Tuesday, July 4, 2000 12:54:32 3rd quarter)\n'
|
|
'#l10n 79 (--- es ---)\n'
|
|
'#l10n 80 (ES - Hello world)\n'
|
|
'#l10n 81 (ES - Hello _NEWLINE_ World)\n'
|
|
'#l10n 82 (ES - Hola \$ Mundo)\n'
|
|
'#l10n 83 (ES - Hello Mundo)\n'
|
|
'#l10n 84 (ES - Hola Mundo)\n'
|
|
'#l10n 85 (ES - Hello World on viernes, 1 de enero de 1960)\n'
|
|
'#l10n 86 (ES - Hello world argument on 1/1/1960 at 0:00)\n'
|
|
'#l10n 87 (ES - Hello World from 1960 to 2020)\n'
|
|
'#l10n 88 (ES - Hello for 123)\n'
|
|
'#l10n 89 (ES - Hello)\n'
|
|
'#l10n 90 (ES - Hello World)\n'
|
|
'#l10n 91 (ES - Hello two worlds)\n'
|
|
'#l10n 92 (ES - Hello)\n'
|
|
'#l10n 93 (ES - Hello nuevo World)\n'
|
|
'#l10n 94 (ES - Hello two nuevo worlds)\n'
|
|
'#l10n 95 (ES - Hello on viernes, 1 de enero de 1960)\n'
|
|
'#l10n 96 (ES - Hello World, on viernes, 1 de enero de 1960)\n'
|
|
'#l10n 97 (ES - Hello two worlds, on viernes, 1 de enero de 1960)\n'
|
|
'#l10n 98 (ES - Hello other 0 worlds, with a total of 100 citizens)\n'
|
|
'#l10n 99 (ES - Hello World of 101 citizens)\n'
|
|
'#l10n 100 (ES - Hello two worlds with 102 total citizens)\n'
|
|
'#l10n 101 (ES - [Hola] -Mundo- #123#)\n'
|
|
'#l10n 102 (ES - \$!)\n'
|
|
'#l10n 103 (ES - One \$)\n'
|
|
"#l10n 104 (ES - Flutter's amazing!)\n"
|
|
"#l10n 105 (ES - Flutter's amazing, times 2!)\n"
|
|
'#l10n 106 (ES - Flutter is "amazing"!)\n'
|
|
'#l10n 107 (ES - Flutter is "amazing", times 2!)\n'
|
|
'#l10n 108 (ES - 16 wheel truck)\n'
|
|
"#l10n 109 (ES - Sedan's elegance)\n"
|
|
'#l10n 110 (ES - Cabriolet has "acceleration")\n'
|
|
'#l10n 111 (ES - Oh, she found ES - 1 itemES - !)\n'
|
|
'#l10n 112 (ES - Indeed, ES - they like ES - Flutter!)\n'
|
|
'#l10n 113 (--- es_419 ---)\n'
|
|
'#l10n 114 (ES 419 - Hello World)\n'
|
|
'#l10n 115 (ES 419 - Hello)\n'
|
|
'#l10n 116 (ES 419 - Hello World)\n'
|
|
'#l10n 117 (ES 419 - Hello two worlds)\n'
|
|
'#l10n END\n'
|
|
|
|
);
|
|
}
|
|
|
|
// TODO(jsimmons): need a localization test that uses deferred loading
|
|
// (see https://github.com/flutter/flutter/issues/61911)
|
|
testWithoutContext('generated l10n classes produce expected localized strings', () async {
|
|
await project.setUpIn(tempDir);
|
|
flutter = FlutterRunTestDriver(tempDir);
|
|
final StringBuffer stdout = await runApp();
|
|
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);
|
|
});
|
|
}
|