blob: f00535a1733156ad96c0e9094f776070a1906666 [file] [log] [blame]
// 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.
class L10nException implements Exception {
L10nException(this.message);
final String message;
}
class OptionalParameter {
const OptionalParameter(this.name, this.value) : assert(name != null), assert(value != null);
final String name;
final Object value;
}
class Placeholder {
Placeholder(this.resourceId, this.name, Map<String, dynamic> attributes)
: assert(resourceId != null),
assert(name != null),
example = _stringAttribute(resourceId, name, attributes, 'example'),
type = _stringAttribute(resourceId, name, attributes, 'type') ?? 'Object',
format = _stringAttribute(resourceId, name, attributes, 'format'),
optionalParameters = _optionalParameters(resourceId, name, attributes);
final String resourceId;
final String name;
final String example;
final String type;
final String format;
final List<OptionalParameter> optionalParameters;
bool get requiresFormatting => <String>['DateTime', 'double', 'int', 'num'].contains(type);
bool get isNumber => <String>['double', 'int', 'num'].contains(type);
bool get isDate => 'DateTime' == type;
static String _stringAttribute(
String resourceId,
String name,
Map<String, dynamic> attributes,
String attributeName,
) {
final dynamic value = attributes[attributeName];
if (value == null)
return null;
if (value is! String || (value as String).isEmpty) {
throw L10nException(
'The "$attributeName" value of the "$name" placeholder in message $resourceId '
'must be a non-empty string.',
);
}
return value as String;
}
static List<OptionalParameter> _optionalParameters(
String resourceId,
String name,
Map<String, dynamic> attributes
) {
final dynamic value = attributes['optionalParameters'];
if (value == null)
return <OptionalParameter>[];
if (value is! Map<String, Object>) {
throw L10nException(
'The "optionalParameters" value of the "$name" placeholder in message '
'$resourceId is not a properly formatted Map. Ensure that it is a map '
'with keys that are strings.'
);
}
final Map<String, dynamic> optionalParameterMap = value as Map<String, dynamic>;
return optionalParameterMap.keys.map<OptionalParameter>((String parameterName) {
return OptionalParameter(parameterName, optionalParameterMap[parameterName]);
}).toList();
}
}
class Message {
Message(Map<String, dynamic> bundle, this.resourceId)
: assert(bundle != null),
assert(resourceId != null && resourceId.isNotEmpty),
value = _value(bundle, resourceId),
description = _description(bundle, resourceId),
placeholders = _placeholders(bundle, resourceId),
_pluralMatch = _pluralRE.firstMatch(_value(bundle, resourceId));
static final RegExp _pluralRE = RegExp(r'\s*\{([\w\s,]*),\s*plural\s*,');
final String resourceId;
final String value;
final String description;
final List<Placeholder> placeholders;
final RegExpMatch _pluralMatch;
bool get isPlural => _pluralMatch != null && _pluralMatch.groupCount == 1;
bool get placeholdersRequireFormatting => placeholders.any((Placeholder p) => p.requiresFormatting);
Placeholder getCountPlaceholder() {
assert(isPlural);
final String countPlaceholderName = _pluralMatch[1];
return placeholders.firstWhere(
(Placeholder p) => p.name == countPlaceholderName,
orElse: () {
throw L10nException('Cannot find the $countPlaceholderName placeholder in plural message "$resourceId".');
}
);
}
static String _value(Map<String, dynamic> bundle, String resourceId) {
final dynamic value = bundle[resourceId];
if (value == null)
throw L10nException('A value for resource "$resourceId" was not found.');
if (value is! String)
throw L10nException('The value of "$resourceId" is not a string.');
return bundle[resourceId] as String;
}
static Map<String, dynamic> _attributes(Map<String, dynamic> bundle, String resourceId) {
final dynamic attributes = bundle['@$resourceId'];
if (attributes == null) {
throw L10nException(
'Resource attribute "@$resourceId" was not found. Please '
'ensure that each resource has a corresponding @resource.'
);
}
if (attributes is! Map<String, dynamic>) {
throw L10nException(
'The resource attribute "@$resourceId" is not a properly formatted Map. '
'Ensure that it is a map with keys that are strings.'
);
}
return attributes as Map<String, dynamic>;
}
static String _description(Map<String, dynamic> bundle, String resourceId) {
final dynamic value = _attributes(bundle, resourceId)['description'];
if (value == null)
return null;
if (value is! String) {
throw L10nException(
'The description for "@$resourceId" is not a properly formatted String.'
);
}
return value as String;
}
static List<Placeholder> _placeholders(Map<String, dynamic> bundle, String resourceId) {
final dynamic value = _attributes(bundle, resourceId)['placeholders'];
if (value == null)
return <Placeholder>[];
if (value is! Map<String, dynamic>) {
throw L10nException(
'The "placeholders" attribute for message $resourceId, is not '
'properly formatted. Ensure that it is a map with string valued keys.'
);
}
final Map<String, dynamic> allPlaceholdersMap = value as Map<String, dynamic>;
return allPlaceholdersMap.keys.map<Placeholder>((String placeholderName) {
final dynamic value = allPlaceholdersMap[placeholderName];
if (value is! Map<String, dynamic>) {
throw L10nException(
'The value of the "$placeholderName" placeholder attribute for message '
'"$resourceId", is not properly formatted. Ensure that it is a map '
'with string valued keys.'
);
}
return Placeholder(resourceId, placeholderName, value as Map<String, dynamic>);
}).toList();
}
}