Unverified Commit 5848a162 authored by Ahmed Ashour's avatar Ahmed Ashour Committed by GitHub

[gen_l10n] to handle arbitrary DateFormat patterns (#86844)

parent e1485e95
...@@ -111,17 +111,25 @@ String generateDateFormattingLogic(Message message) { ...@@ -111,17 +111,25 @@ String generateDateFormattingLogic(Message message) {
'date formats.' 'date formats.'
); );
} }
if (!placeholder.hasValidDateFormat) { final bool? isCustomDateFormat = placeholder.isCustomDateFormat;
if (!placeholder.hasValidDateFormat
&& (isCustomDateFormat == null || !isCustomDateFormat)) {
throw L10nException( throw L10nException(
'Date format "$placeholderFormat" for placeholder ' 'Date format "$placeholderFormat" for placeholder '
'${placeholder.name} does not have a corresponding DateFormat ' '${placeholder.name} does not have a corresponding DateFormat '
"constructor\n. Check the intl library's DateFormat class " "constructor\n. Check the intl library's DateFormat class "
'constructors for allowed date formats.' 'constructors for allowed date formats, or set "isCustomDateFormat" attribute '
'to "true".'
); );
} }
return dateFormatTemplate if (placeholder.hasValidDateFormat) {
return dateFormatTemplate
.replaceAll('@(placeholder)', placeholder.name)
.replaceAll('@(format)', placeholderFormat);
}
return dateFormatCustomTemplate
.replaceAll('@(placeholder)', placeholder.name) .replaceAll('@(placeholder)', placeholder.name)
.replaceAll('@(format)', placeholderFormat); .replaceAll('@(format)', generateString(placeholderFormat));
}); });
return formatStatements.isEmpty ? '@(none)' : formatStatements.join(''); return formatStatements.isEmpty ? '@(none)' : formatStatements.join('');
......
...@@ -126,6 +126,11 @@ const String dateFormatTemplate = ''' ...@@ -126,6 +126,11 @@ const String dateFormatTemplate = '''
final String @(placeholder)String = @(placeholder)DateFormat.format(@(placeholder)); final String @(placeholder)String = @(placeholder)DateFormat.format(@(placeholder));
'''; ''';
const String dateFormatCustomTemplate = '''
final intl.DateFormat @(placeholder)DateFormat = intl.DateFormat(@(format), localeName);
final String @(placeholder)String = @(placeholder)DateFormat.format(@(placeholder));
''';
const String getterTemplate = ''' const String getterTemplate = '''
@override @override
String get @(name) => @(message);'''; String get @(name) => @(message);''';
......
...@@ -193,7 +193,8 @@ class Placeholder { ...@@ -193,7 +193,8 @@ class Placeholder {
example = _stringAttribute(resourceId, name, attributes, 'example'), example = _stringAttribute(resourceId, name, attributes, 'example'),
type = _stringAttribute(resourceId, name, attributes, 'type') ?? 'Object', type = _stringAttribute(resourceId, name, attributes, 'type') ?? 'Object',
format = _stringAttribute(resourceId, name, attributes, 'format'), format = _stringAttribute(resourceId, name, attributes, 'format'),
optionalParameters = _optionalParameters(resourceId, name, attributes); optionalParameters = _optionalParameters(resourceId, name, attributes),
isCustomDateFormat = _boolAttribute(resourceId, name, attributes, 'isCustomDateFormat');
final String resourceId; final String resourceId;
final String name; final String name;
...@@ -201,6 +202,7 @@ class Placeholder { ...@@ -201,6 +202,7 @@ class Placeholder {
final String? type; final String? type;
final String? format; final String? format;
final List<OptionalParameter> optionalParameters; final List<OptionalParameter> optionalParameters;
final bool? isCustomDateFormat;
bool get requiresFormatting => <String>['DateTime', 'double', 'num'].contains(type) || (type == 'int' && format != null); bool get requiresFormatting => <String>['DateTime', 'double', 'num'].contains(type) || (type == 'int' && format != null);
bool get isNumber => <String>['double', 'int', 'num'].contains(type); bool get isNumber => <String>['double', 'int', 'num'].contains(type);
...@@ -228,6 +230,25 @@ class Placeholder { ...@@ -228,6 +230,25 @@ class Placeholder {
return value; return value;
} }
static bool? _boolAttribute(
String resourceId,
String name,
Map<String, Object?> attributes,
String attributeName,
) {
final Object? value = attributes[attributeName];
if (value == null) {
return null;
}
if (value != 'true' && value != 'false') {
throw L10nException(
'The "$attributeName" value of the "$name" placeholder in message $resourceId '
'must be a boolean value.',
);
}
return value == 'true';
}
static List<OptionalParameter> _optionalParameters( static List<OptionalParameter> _optionalParameters(
String resourceId, String resourceId,
String name, String name,
......
...@@ -1356,13 +1356,13 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ...@@ -1356,13 +1356,13 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e
"@@locale": "en", "@@locale": "en",
"springBegins": "Spring begins on {springStartDate}", "springBegins": "Spring begins on {springStartDate}",
"@springBegins": { "@springBegins": {
"description": "The first day of spring", "description": "The first day of spring",
"placeholders": { "placeholders": {
"springStartDate": { "springStartDate": {
"type": "DateTime", "type": "DateTime",
"format": "yMd" "format": "yMd"
}
} }
}
} }
}'''; }''';
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true) fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true)
...@@ -1391,13 +1391,13 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ...@@ -1391,13 +1391,13 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e
"@@locale": "en", "@@locale": "en",
"springBegins": "Spring begins on {springStartDate}", "springBegins": "Spring begins on {springStartDate}",
"@springBegins": { "@springBegins": {
"description": "The first day of spring", "description": "The first day of spring",
"placeholders": { "placeholders": {
"springStartDate": { "springStartDate": {
"type": "DateTime", "type": "DateTime",
"format": "asdf" "format": "asdf"
}
} }
}
} }
}'''; }''';
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
...@@ -1429,17 +1429,91 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ...@@ -1429,17 +1429,91 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e
); );
}); });
testWithoutContext('use standard date format whenever possible', () {
const String singleDateMessageArbFileString = '''
{
"@@locale": "en",
"springBegins": "Spring begins on {springStartDate}",
"@springBegins": {
"description": "The first day of spring",
"placeholders": {
"springStartDate": {
"type": "DateTime",
"format": "yMd",
"isCustomDateFormat": "true"
}
}
}
}''';
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
..createSync(recursive: true);
l10nDirectory.childFile(defaultTemplateArbFileName)
.writeAsStringSync(singleDateMessageArbFileString);
LocalizationsGenerator(
fileSystem: fs,
inputPathString: defaultL10nPathString,
templateArbFileName: defaultTemplateArbFileName,
outputFileString: defaultOutputFileString,
classNameString: defaultClassNameString,
)
..loadResources()
..writeOutputFiles(BufferLogger.test());
final String localizationsFile = fs.file(
fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart'),
).readAsStringSync();
expect(localizationsFile, contains('DateFormat.yMd(localeName)'));
});
testWithoutContext('handle arbitrary formatted date', () {
const String singleDateMessageArbFileString = '''
{
"@@locale": "en",
"springBegins": "Spring begins on {springStartDate}",
"@springBegins": {
"description": "The first day of spring",
"placeholders": {
"springStartDate": {
"type": "DateTime",
"format": "asdf o'clock",
"isCustomDateFormat": "true"
}
}
}
}''';
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
..createSync(recursive: true);
l10nDirectory.childFile(defaultTemplateArbFileName)
.writeAsStringSync(singleDateMessageArbFileString);
LocalizationsGenerator(
fileSystem: fs,
inputPathString: defaultL10nPathString,
templateArbFileName: defaultTemplateArbFileName,
outputFileString: defaultOutputFileString,
classNameString: defaultClassNameString,
)
..loadResources()
..writeOutputFiles(BufferLogger.test());
final String localizationsFile = fs.file(
fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart'),
).readAsStringSync();
expect(localizationsFile, contains(r"DateFormat('asdf o\'clock', localeName)"));
});
testWithoutContext('throws an exception when no format attribute is passed in', () { testWithoutContext('throws an exception when no format attribute is passed in', () {
const String singleDateMessageArbFileString = ''' const String singleDateMessageArbFileString = '''
{ {
"springBegins": "Spring begins on {springStartDate}", "springBegins": "Spring begins on {springStartDate}",
"@springBegins": { "@springBegins": {
"description": "The first day of spring", "description": "The first day of spring",
"placeholders": { "placeholders": {
"springStartDate": { "springStartDate": {
"type": "DateTime" "type": "DateTime"
}
} }
}
} }
}'''; }''';
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
......
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