-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New Simplified Localization Workflow #43934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… and output class prefix
HansMuller
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good. Mostly just some suggestions about error detection.
dev/tools/localization/gen_l10n.dart
Outdated
| parser.addOption('dir-path', defaultsTo: path.join('lib', 'l10n')); | ||
| parser.addOption('input-arb-file', defaultsTo: 'app_en.arb'); | ||
| parser.addOption('output-file-prefix', defaultsTo: 'app'); | ||
| parser.addOption('output-class-prefix', defaultsTo: 'App'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be better to just make this 'output-class' and default to 'AppLocalizations'. No reason to require developers to use 'Localizations' as a class name suffix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
dev/tools/localization/gen_l10n.dart
Outdated
|
|
||
| Future<void> main(List<String> args) async { | ||
| final argslib.ArgParser parser = argslib.ArgParser(); | ||
| parser.addOption('dir-path', defaultsTo: path.join('lib', 'l10n')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most flutter commands options that name a directory use "-dir" as a suffix. Maybe just 'arb-dir' here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| final File inputArbFile = File(path.join(l10nDirectory.path, results['input-arb-file'])); | ||
| final File outputFile = File(path.join(l10nDirectory.path, '${results['output-file-prefix']}_localizations.dart')); | ||
| final String stringsClassName = '${results['output-class-prefix']}Localizations'; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a good place to check everything:
- The l10nDirectory exists and supports read/write.
- The inputArbFile is readable
- The outputFile is writable or doesn't exist.
- The stringClassName is a legit Dart class name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to add checks for the following:
- If l10nDirectory exists
- The stringClassName is a legit Dart class name (check for first character being uppercase alphabet and for non alphanumeric characters in the string.
I'm not quite sure how to check if a file is readable/writable. Is there an example I can reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you can use the FileSytemEntity stat method to check the value of the FileSystemStat's mode string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Let me know if it looks good, I'm not terribly familiar with the stat() method and file system permissions, so let me know if there's something we should change here.
dev/tools/localization/gen_l10n.dart
Outdated
| final String entityPath = entity.path; | ||
|
|
||
| if (FileSystemEntity.isFileSync(entityPath)) { | ||
| // TODO: what if there are multiple files with the exact same locale? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NICE. If we detect as much, we might as well abort, since it's likely to be a mistake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a check here to abort if there is two arb files with the same locale
dev/tools/localization/gen_l10n.dart
Outdated
| Future<void> main(List<String> args) async { | ||
| final argslib.ArgParser parser = argslib.ArgParser(); | ||
| parser.addOption('dir-path', defaultsTo: path.join('lib', 'l10n')); | ||
| parser.addOption('input-arb-file', defaultsTo: 'app_en.arb'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little (just a little) odd to call this the input arb file, since they're all input files. Maybe we should call this one template-arb-file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| final RegExp arbFilenameRE = RegExp(r'(\w+)\.arb$'); | ||
| if (arbFilenameRE.hasMatch(entityPath)) { | ||
| final File arbFile = File(entityPath); | ||
| final Map<String, dynamic> arbContents = json.decode(arbFile.readAsStringSync()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably this can fail. If it does we should probably abort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
dev/tools/localization/gen_l10n.dart
Outdated
| if (arbFilenameRE.hasMatch(entityPath)) { | ||
| final File arbFile = File(entityPath); | ||
| final Map<String, dynamic> arbContents = json.decode(arbFile.readAsStringSync()); | ||
| final RegExp arbFilenameLocaleRE = RegExp(r'^[^_]*_(\w+)\.arb$'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest only RE matching the filename if that's needed:
String localeString = arbContents['@@locale'];
if (localeString == null) {
final RegExp arbFilenameLocaleRE = RegExp(r'^[^_]*_(\w+)\.arb$');
localeString = arbFilenameLocaleRE.firstMatch(entityPath)[1];
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
|
||
| if (arbFilenameLocaleRE.hasMatch(entityPath) && localeString == null) | ||
| localeString = arbFilenameLocaleRE.firstMatch(entityPath)[1]; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If localeString is null at this point then we found an arb file but couldn't determine what locale it was for. That seems bad; probably abort here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| parser.addOption('input-arb-file', defaultsTo: 'app_en.arb'); | ||
| parser.addOption('output-file-prefix', defaultsTo: 'app'); | ||
| parser.addOption('output-class-prefix', defaultsTo: 'App'); | ||
| final argslib.ArgResults results = parser.parse(args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually we should add support for a --verbose option.
| /// supportedLocales list. For example: | ||
| /// | ||
| /// ``` | ||
| /// return MaterialApp( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe show the import here as well (assuming that the app is defined in lib/).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| } | ||
| '''; | ||
|
|
||
| const String simpleMethodTemplate = ''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could map no-parameter lookups to a getter. Would need a getterMethodTemplate .. and some other changes :-).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
|
It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie. Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
HansMuller
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM; just needs a few small changes
dev/tools/localization/gen_l10n.dart
Outdated
| 'Please ensure that the user has read permissions.' | ||
| ); | ||
| final String outputFileStatModeString = outputFile.statSync().modeString(); | ||
| if (outputFileStatModeString[1] == '-') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK if the file doesn't exist (we're generating it for the first time). It looks like we would fail if the output file doesn't exist yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, that makes sense. I realized this a few minutes ago and removed this check
dev/tools/localization/gen_l10n.dart
Outdated
| final String l10nDirectoryStatModeString = l10nDirectory.statSync().modeString(); | ||
| if (!_isDirectoryReadableAndWritable(l10nDirectoryStatModeString)) | ||
| exitWithError( | ||
| "The 'arb-dir' directory, $l10nDirectory, is not readable or writable.\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is not readable or writable => doesn't allow reading and writing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
dev/tools/localization/gen_l10n.dart
Outdated
| final argslib.ArgParser parser = argslib.ArgParser(); | ||
| parser.addOption('arb-dir', defaultsTo: path.join('lib', 'l10n')); | ||
| parser.addOption('template-arb-file', defaultsTo: 'app_en.arb'); | ||
| parser.addOption('output-localization-file', defaultsTo: 'app_localizations'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be less confusing if this defaulted to app_localizations.dart; if we didn't add the .dart suffix to the output file parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
…#43934) *Starting with arb files in lib/l10n, the tool creates the following by default: 1. An AppLocalizations class containing: - an `AppLocalizationsDelegate` - a `supportedLocales` property that returns a list of all supported locales. - a `localizationsDelegate` property that returns a list containing this delegate along with `GlobalMaterialLocalizations.delegate`, `GlobalCupertinoLocalizations.delegate`, and `GlobalWidgetsLocalizations.delegate`. 2. One Dart `message_<locale>.dart` file for each arb file, as well as a `messages_all.dart` file that performs the locale message lookup. The tool infers the locale of each arb file from the @@Locale property or the arb file's name.
Description
Builds on #41432 to simplify and improve the i18n process. This assumes that the developer has the necessary .arb files.
It can be test run on https://github.com/shihaohong/i18n_exploration.
Steps:
i18n_explorationproject.dart ${FLUTTER_DIR}/dev/tools/localization/gen_l10n.dart.In its current state, this proposal should generate the following:
app_localizations.dart:AppLocalizations.localizationsDelegate- Helper that returns list containing the demo localizations' delegates, along with the other default delegates needed for the app (GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate and GlobalWidgetsLocalizations.delegate)AppLocalizations.supportedLocales- Helper that returns a list of all the supported locales:messages_en.dart,messages_es.dart,messages_zh_CN.dart, andmessages_zh_TW.dart, which provide the messages for their respective localesmessages_all.dart, which looks up the desired locale's translations.Specs
Related Issues
#41437
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).and newtests are passing.flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?