Commit 7539826d authored by Hans Muller's avatar Hans Muller Committed by Flutter GitHub Bot

Update gen_l10n handling of plurals, numbers, and dates (#49357)

parent 1a3379d5
......@@ -10,6 +10,7 @@ import 'package:file/local.dart' as local;
import 'package:path/path.dart' as path;
import '../gen_l10n.dart';
import '../gen_l10n_types.dart';
import '../localizations_utils.dart';
Future<void> main(List<String> arguments) async {
......
This diff is collapsed.
// 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.
const String getterMethodTemplate = '''
String get @(methodName) {
return Intl.message(
@(message),
locale: _localeName,
@(intlMethodArgs)
);
}
''';
const String simpleMethodTemplate = '''
String @(methodName)(@(methodParameters)) {
return Intl.message(
@(message),
locale: _localeName,
@(intlMethodArgs)
);
}
''';
const String formatMethodTemplate = '''
String @(methodName)(@(methodParameters)) {@(dateFormatting)@(numberFormatting)
String @(methodName)(@(innerMethodParameters)) {
return Intl.message(
@(message),
locale: _localeName,
@(intlMethodArgs)
);
}
return @(methodName)(@(innerMethodArgs));
}
''';
const String pluralMethodTemplate = '''
String @(methodName)(@(methodParameters)) {@(dateFormatting)@(numberFormatting)
return Intl.plural(
@(intlMethodArgs)
);
}
''';
const String pluralFormatMethodTemplate = '''
String @(methodName)(@(methodParameters)) {@(dateFormatting)@(numberFormatting)
String @(methodName)(@(innerMethodParameters)) {
return Intl.plural(
@(intlMethodArgs)
);
}
return @(methodName)(@(innerMethodArgs));
}
''';
const String defaultFileTemplate = '''
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart';
/// Callers can lookup localized strings with an instance of @(className) returned
/// by `@(className).of(context)`.
///
/// Applications need to include `@(className).delegate()` in their app\'s
/// localizationDelegates list, and the locales they support in the app\'s
/// supportedLocales list. For example:
///
/// ```
/// import '@(importFile)';
///
/// return MaterialApp(
/// localizationsDelegates: @(className).localizationsDelegates,
/// supportedLocales: @(className).supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: 0.16.0
/// intl_translation: 0.17.7
///
/// # rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ll need to edit this
/// file.
///
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the @className.supportedLocales
/// property.
class @(className) {
@(className)(Locale locale) : _localeName = Intl.canonicalizedLocale(locale.toString());
final String _localeName;
static Future<@(className)> load(Locale locale) {
return initializeMessages(locale.toString())
.then<@(className)>((_) => @(className)(locale));
}
static @(className) of(BuildContext context) {
return Localizations.of<@(className)>(context, @(className));
}
static const LocalizationsDelegate<@(className)> delegate = _@(classNameDelegate)();
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
/// A list of this localizations delegate's supported locales.
@(supportedLocales)
@(classMethods)
}
class _@(className)Delegate extends LocalizationsDelegate<@(className)> {
const _@(className)Delegate();
@override
Future<@(className)> load(Locale locale) => @(className).load(locale);
@override
bool isSupported(Locale locale) => <String>[@(supportedLanguageCodes)].contains(locale.languageCode);
@override
bool shouldReload(_@(className)Delegate old) => false;
}
''';
// 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);
final String resourceId;
final String value;
final String description;
final List<Placeholder> placeholders;
bool get placeholdersRequireFormatting => placeholders.any((Placeholder p) => p.requiresFormatting);
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();
}
}
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