Skip to content

gen_l10n localizations date formatting (simple messages) #47006

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

Merged
merged 10 commits into from
Dec 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 138 additions & 4 deletions dev/tools/localization/gen_l10n.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const String getterMethodTemplate = '''
''';

const String simpleMethodTemplate = '''
String @methodName(@methodParameters) {
String @methodName(@methodParameters) {@dateFormatting
return Intl.message(
@message,
locale: _localeName,
Expand All @@ -149,6 +149,96 @@ const String pluralMethodTemplate = '''
}
''';

// The set of date formats that can be automatically localized.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE

//
// The localizations generation tool makes use of the intl library's
// DateFormat class to properly format dates based on the locale, the
// desired format, as well as the passed in [DateTime]. For example, using
// DateFormat.yMMMMd("en_US").format(DateTime.utc(1996, 7, 10)) results
// in the string "July 10, 1996".
//
// Since the tool generates code that uses DateFormat's constructor, it is
// necessary to verify that the constructor exists, or the
// tool will generate code that may cause a compile-time error.
//
// See also:
//
// * <https://pub.dev/packages/intl>
// * <https://pub.dev/documentation/intl/latest/intl/DateFormat-class.html>
// * <https://api.dartlang.org/stable/2.7.0/dart-core/DateTime-class.html>
const Set<String> allowableDateFormats = <String>{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to include a comment that points to https://pub.dev/documentation/intl/latest/intl/DateFormat-class.html and explains that these are the formats which will be automatically localized

'd',
'E',
'EEEE',
'LLL',
'LLLL',
'M',
'Md',
'MEd',
'MMM',
'MMMd',
'MMMEd',
'MMMM',
'MMMMd',
'MMMMEEEEd',
'QQQ',
'QQQQ',
'y',
'yM',
'yMd',
'yMEd',
'yMMM',
'yMMMd',
'yMMMEd',
'yMMMM',
'yMMMMd',
'yMMMMEEEEd',
'yQQQ',
'yQQQQ',
'H',
'Hm',
'Hms',
'j',
'jm',
'jms',
'jmv',
'jmz',
'jv',
'jz',
'm',
'ms',
's',
};

bool _isDateParameter(dynamic placeholderValue) {
return placeholderValue is Map<String, dynamic> &&
placeholderValue['type'] == 'DateTime';
}

bool _dateParameterIsValid(Map<String, dynamic> placeholderValue, String placeholder) {
if (allowableDateFormats.contains(placeholderValue['format']))
return true;
throw L10nException(
'Date format ${placeholderValue['format']} for the $placeholder \n'
'placeholder does not have a corresponding DateFormat \n'
'constructor. Check the intl library\'s DateFormat class \n'
'constructors for allowed date formats.'
);
}

bool _containsFormatKey(Map<String, dynamic> placeholderValue, String placeholder) {
if (placeholderValue.containsKey('format'))
return true;
throw L10nException(
'The placeholder, $placeholder, has its "type" resource attribute set to '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE

'the "DateTime" type. To properly resolve for the right DateTime format, '
'the "format" attribute needs to be set to determine which DateFormat to '
'use. \n'
'Check the intl library\'s DateFormat class constructors for allowed '
'date formats.'
);
}

List<String> genMethodParameters(Map<String, dynamic> bundle, String key, String type) {
final Map<String, dynamic> attributesMap = bundle['@$key'] as Map<String, dynamic>;
if (attributesMap != null && attributesMap.containsKey('placeholders')) {
Expand All @@ -158,6 +248,30 @@ List<String> genMethodParameters(Map<String, dynamic> bundle, String key, String
return <String>[];
}

String generateDateFormattingLogic(Map<String, dynamic> bundle, String key) {
String result = '';
final Map<String, dynamic> attributesMap = bundle['@$key'] as Map<String, dynamic>;
if (attributesMap != null && attributesMap.containsKey('placeholders')) {
final Map<String, dynamic> placeholders = attributesMap['placeholders'] as Map<String, dynamic>;
for (String placeholder in placeholders.keys) {
final dynamic value = placeholders[placeholder];
if (
_isDateParameter(value) &&
_containsFormatKey(value, placeholder) &&
_dateParameterIsValid(value, placeholder)
) {
result += '''

final DateFormat ${placeholder}DateFormat = DateFormat.${value['format']}(_localeName);
final String ${placeholder}String = ${placeholder}DateFormat.format($placeholder);
''';
}
}
}

return result;
}

List<String> genIntlMethodArgs(Map<String, dynamic> bundle, String key) {
final List<String> attributes = <String>['name: \'$key\''];
final Map<String, dynamic> attributesMap = bundle['@$key'] as Map<String, dynamic>;
Expand All @@ -169,7 +283,20 @@ List<String> genIntlMethodArgs(Map<String, dynamic> bundle, String key) {
if (attributesMap.containsKey('placeholders')) {
final Map<String, dynamic> placeholders = attributesMap['placeholders'] as Map<String, dynamic>;
if (placeholders.isNotEmpty) {
final String args = placeholders.keys.join(', ');
final List<String> argumentList = <String>[];
for (String placeholder in placeholders.keys) {
final dynamic value = placeholders[placeholder];
if (
_isDateParameter(value) &&
_containsFormatKey(value, placeholder) &&
_dateParameterIsValid(value, placeholder)
) {
argumentList.add('${placeholder}String');
} else {
argumentList.add(placeholder);
}
}
final String args = argumentList.join(', ');
attributes.add('args: <Object>[$args]');
}
}
Expand All @@ -182,8 +309,14 @@ String genSimpleMethod(Map<String, dynamic> bundle, String key) {
String message = bundle[key] as String;
final Map<String, dynamic> attributesMap = bundle['@$key'] as Map<String, dynamic>;
final Map<String, dynamic> placeholders = attributesMap['placeholders'] as Map<String, dynamic>;
for (String placeholder in placeholders.keys)
message = message.replaceAll('{$placeholder}', '\$$placeholder');
for (String placeholder in placeholders.keys) {
final dynamic value = placeholders[placeholder];
if (_isDateParameter(value)) {
message = message.replaceAll('{$placeholder}', '\$${placeholder}String');
} else {
message = message.replaceAll('{$placeholder}', '\$$placeholder');
}
}
return generateString(message);
}

Expand All @@ -198,6 +331,7 @@ String genSimpleMethod(Map<String, dynamic> bundle, String key) {
return simpleMethodTemplate
.replaceAll('@methodName', key)
.replaceAll('@methodParameters', genMethodParameters(bundle, key, 'Object').join(', '))
.replaceAll('@dateFormatting', generateDateFormattingLogic(bundle, key))
.replaceAll('@message', '${genSimpleMethodMessage(bundle, key)}')
.replaceAll('@intlMethodArgs', genIntlMethodArgs(bundle, key).join(',\n '));
}
Expand Down
Loading