| Index: packages/intl/lib/intl.dart
|
| diff --git a/packages/intl/lib/intl.dart b/packages/intl/lib/intl.dart
|
| index d952691ddc2a84d759f77c50d0b6d207de198863..095eb710c1b6097ad50d8a22c57ddc35ee2a91f5 100644
|
| --- a/packages/intl/lib/intl.dart
|
| +++ b/packages/intl/lib/intl.dart
|
| @@ -2,22 +2,20 @@
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| -/**
|
| - * This library provides internationalization and localization. This includes
|
| - * message formatting and replacement, date and number formatting and parsing,
|
| - * and utilities for working with Bidirectional text.
|
| - *
|
| - * This is part of the [intl package]
|
| - * (https://pub.dartlang.org/packages/intl).
|
| - *
|
| - * For things that require locale or other data, there are multiple different
|
| - * ways of making that data available, which may require importing different
|
| - * libraries. See the class comments for more details.
|
| - *
|
| - * There is also a simple example application that can be found in the
|
| - * [example/basic](https://github.com/dart-lang/intl/tree/master/example/basic)
|
| - * directory.
|
| - */
|
| +/// This library provides internationalization and localization. This includes
|
| +/// message formatting and replacement, date and number formatting and parsing,
|
| +/// and utilities for working with Bidirectional text.
|
| +///
|
| +/// This is part of the [intl package]
|
| +/// (https://pub.dartlang.org/packages/intl).
|
| +///
|
| +/// For things that require locale or other data, there are multiple different
|
| +/// ways of making that data available, which may require importing different
|
| +/// libraries. See the class comments for more details.
|
| +///
|
| +/// There is also a simple example application that can be found in the
|
| +/// [example/basic](https://github.com/dart-lang/intl/tree/master/example/basic)
|
| +/// directory.
|
| library intl;
|
|
|
| import 'dart:async';
|
| @@ -30,182 +28,177 @@ import 'number_symbols.dart';
|
| import 'number_symbols_data.dart';
|
| import 'src/date_format_internal.dart';
|
| import 'src/intl_helpers.dart';
|
| +import 'package:intl/src/plural_rules.dart' as plural_rules;
|
|
|
| part 'src/intl/bidi_formatter.dart';
|
| part 'src/intl/bidi_utils.dart';
|
| +
|
| +part 'src/intl/compact_number_format.dart';
|
| part 'src/intl/date_format.dart';
|
| part 'src/intl/date_format_field.dart';
|
| part 'src/intl/date_format_helpers.dart';
|
| part 'src/intl/number_format.dart';
|
|
|
| -/**
|
| - * The Intl class provides a common entry point for internationalization
|
| - * related tasks. An Intl instance can be created for a particular locale
|
| - * and used to create a date format via `anIntl.date()`. Static methods
|
| - * on this class are also used in message formatting.
|
| - *
|
| - * Examples:
|
| - * today(date) => Intl.message(
|
| - * "Today's date is $date",
|
| - * name: 'today',
|
| - * args: [date],
|
| - * desc: 'Indicate the current date',
|
| - * examples: {'date' : 'June 8, 2012'});
|
| - * print(today(new DateTime.now().toString());
|
| - *
|
| - * howManyPeople(numberOfPeople, place) => Intl.plural(
|
| - * zero: 'I see no one at all',
|
| - * one: 'I see one other person',
|
| - * other: 'I see $numberOfPeople other people')} in $place.''',
|
| - * name: 'msg',
|
| - * args: [numberOfPeople, place],
|
| - * desc: 'Description of how many people are seen in a place.',
|
| - * examples: {'numberOfPeople': 3, 'place': 'London'});
|
| - *
|
| - * Calling `howManyPeople(2, 'Athens');` would
|
| - * produce "I see 2 other people in Athens." as output in the default locale.
|
| - * If run in a different locale it would produce appropriately translated
|
| - * output.
|
| - *
|
| - * For more detailed information on messages and localizing them see
|
| - * the main [package documentation](https://pub.dartlang.org/packages/intl)
|
| - *
|
| - * You can set the default locale.
|
| - * Intl.defaultLocale = "pt_BR";
|
| - *
|
| - * To temporarily use a locale other than the default, use the `withLocale`
|
| - * function.
|
| - * var todayString = new DateFormat("pt_BR").format(new DateTime.now());
|
| - * print(withLocale("pt_BR", () => today(todayString));
|
| - *
|
| - * See `tests/message_format_test.dart` for more examples.
|
| - */
|
| +/// The Intl class provides a common entry point for internationalization
|
| +/// related tasks. An Intl instance can be created for a particular locale
|
| +/// and used to create a date format via `anIntl.date()`. Static methods
|
| +/// on this class are also used in message formatting.
|
| +///
|
| +/// Examples:
|
| +/// today(date) => Intl.message(
|
| +/// "Today's date is $date",
|
| +/// name: 'today',
|
| +/// args: [date],
|
| +/// desc: 'Indicate the current date',
|
| +/// examples: const {'date' : 'June 8, 2012'});
|
| +/// print(today(new DateTime.now().toString());
|
| +///
|
| +/// howManyPeople(numberOfPeople, place) => Intl.plural(
|
| +/// zero: 'I see no one at all',
|
| +/// one: 'I see one other person',
|
| +/// other: 'I see $numberOfPeople other people')} in $place.''',
|
| +/// name: 'msg',
|
| +/// args: [numberOfPeople, place],
|
| +/// desc: 'Description of how many people are seen in a place.',
|
| +/// examples: const {'numberOfPeople': 3, 'place': 'London'});
|
| +///
|
| +/// Calling `howManyPeople(2, 'Athens');` would
|
| +/// produce "I see 2 other people in Athens." as output in the default locale.
|
| +/// If run in a different locale it would produce appropriately translated
|
| +/// output.
|
| +///
|
| +/// For more detailed information on messages and localizing them see
|
| +/// the main [package documentation](https://pub.dartlang.org/packages/intl)
|
| +///
|
| +/// You can set the default locale.
|
| +/// Intl.defaultLocale = "pt_BR";
|
| +///
|
| +/// To temporarily use a locale other than the default, use the `withLocale`
|
| +/// function.
|
| +/// var todayString = new DateFormat("pt_BR").format(new DateTime.now());
|
| +/// print(withLocale("pt_BR", () => today(todayString));
|
| +///
|
| +/// See `tests/message_format_test.dart` for more examples.
|
| //TODO(efortuna): documentation example involving the offset parameter?
|
|
|
| class Intl {
|
| - /**
|
| - * String indicating the locale code with which the message is to be
|
| - * formatted (such as en-CA).
|
| - */
|
| + /// String indicating the locale code with which the message is to be
|
| + /// formatted (such as en-CA).
|
| String _locale;
|
|
|
| - /**
|
| - * The default locale. This defaults to being set from systemLocale, but
|
| - * can also be set explicitly, and will then apply to any new instances where
|
| - * the locale isn't specified. Note that a locale parameter to
|
| - * [Intl.withLocale]
|
| - * will supercede this value while that operation is active. Using
|
| - * [Intl.withLocale] may be preferable if you are using different locales
|
| - * in the same application.
|
| - */
|
| + /// The default locale. This defaults to being set from systemLocale, but
|
| + /// can also be set explicitly, and will then apply to any new instances where
|
| + /// the locale isn't specified. Note that a locale parameter to
|
| + /// [Intl.withLocale]
|
| + /// will supercede this value while that operation is active. Using
|
| + /// [Intl.withLocale] may be preferable if you are using different locales
|
| + /// in the same application.
|
| static String get defaultLocale {
|
| var zoneLocale = Zone.current[#Intl.locale];
|
| return zoneLocale == null ? _defaultLocale : zoneLocale;
|
| }
|
| - static set defaultLocale(String newLocale) => _defaultLocale = newLocale;
|
| +
|
| + static set defaultLocale(String newLocale) {
|
| + _defaultLocale = newLocale;
|
| + }
|
| +
|
| static String _defaultLocale;
|
|
|
| - /**
|
| - * The system's locale, as obtained from the window.navigator.language
|
| - * or other operating system mechanism. Note that due to system limitations
|
| - * this is not automatically set, and must be set by importing one of
|
| - * intl_browser.dart or intl_standalone.dart and calling findSystemLocale().
|
| - */
|
| + /// The system's locale, as obtained from the window.navigator.language
|
| + /// or other operating system mechanism. Note that due to system limitations
|
| + /// this is not automatically set, and must be set by importing one of
|
| + /// intl_browser.dart or intl_standalone.dart and calling findSystemLocale().
|
| static String systemLocale = 'en_US';
|
|
|
| - /**
|
| - * Return a new date format using the specified [pattern].
|
| - * If [desiredLocale] is not specified, then we default to [locale].
|
| - */
|
| + /// Return a new date format using the specified [pattern].
|
| + /// If [desiredLocale] is not specified, then we default to [locale].
|
| DateFormat date([String pattern, String desiredLocale]) {
|
| var actualLocale = (desiredLocale == null) ? locale : desiredLocale;
|
| return new DateFormat(pattern, actualLocale);
|
| }
|
|
|
| - /**
|
| - * Constructor optionally [aLocale] for specifics of the language
|
| - * locale to be used, otherwise, we will attempt to infer it (acceptable if
|
| - * Dart is running on the client, we can infer from the browser/client
|
| - * preferences).
|
| - */
|
| + /// Constructor optionally [aLocale] for specifics of the language
|
| + /// locale to be used, otherwise, we will attempt to infer it (acceptable if
|
| + /// Dart is running on the client, we can infer from the browser/client
|
| + /// preferences).
|
| Intl([String aLocale]) {
|
| _locale = aLocale != null ? aLocale : getCurrentLocale();
|
| }
|
|
|
| - /**
|
| - * Use this for a message that will be translated for different locales. The
|
| - * expected usage is that this is inside an enclosing function that only
|
| - * returns the value of this call and provides a scope for the variables that
|
| - * will be substituted in the message.
|
| - *
|
| - * The [message_str] is the string to be translated, which may be interpolated
|
| - * based on one or more variables. The [name] of the message must
|
| - * match the enclosing function name. For methods, it can also be
|
| - * className_methodName. So for a method hello in class Simple, the name
|
| - * can be either "hello" or "Simple_hello". The name must also be globally
|
| - * unique in the program, so the second form can make it easier to distinguish
|
| - * messages with the same name but in different classes.
|
| - * The [args] repeats the arguments of the enclosing
|
| - * function, [desc] provides a description of usage,
|
| - * [examples] is a Map of exmaples for each interpolated variable. For example
|
| - * hello(yourName) => Intl.message(
|
| - * "Hello, $yourName",
|
| - * name: "hello",
|
| - * args: [yourName],
|
| - * desc: "Say hello",
|
| - * examples = {"yourName": "Sparky"}.
|
| - * The source code will be processed via the analyzer to extract out the
|
| - * message data, so only a subset of valid Dart code is accepted. In
|
| - * particular, everything must be literal and cannot refer to variables
|
| - * outside the scope of the enclosing function. The [examples] map must
|
| - * be a valid const literal map. Similarly, the [desc] argument must
|
| - * be a single, simple string. These two arguments will not be used at runtime
|
| - * but will be extracted from
|
| - * the source code and used as additional data for translators. For more
|
| - * information see the "Messages" section of the main [package documentation]
|
| - * (https://pub.dartlang.org/packages/intl).
|
| - *
|
| - * The [name] and [args] arguments are required, and are used at runtime
|
| - * to look up the localized version and pass the appropriate arguments to it.
|
| - * We may in the future modify the code during compilation to make manually
|
| - * passing those arguments unnecessary.
|
| - */
|
| - static String message(String message_str, {String desc: '',
|
| - Map<String, String> examples: const {}, String locale, String name,
|
| - List<String> args, String meaning}) {
|
| + /// Use this for a message that will be translated for different locales. The
|
| + /// expected usage is that this is inside an enclosing function that only
|
| + /// returns the value of this call and provides a scope for the variables that
|
| + /// will be substituted in the message.
|
| + ///
|
| + /// The [message_str] is the string to be translated, which may be
|
| + /// interpolated based on one or more variables. The [name] of the message
|
| + /// must match the enclosing function name. For methods, it can also be
|
| + /// className_methodName. So for a method hello in class Simple, the name can
|
| + /// be either "hello" or "Simple_hello". The name must also be globally unique
|
| + /// in the program, so the second form can make it easier to distinguish
|
| + /// messages with the same name but in different classes.
|
| + ///
|
| + /// The [args] repeats the arguments of the enclosing
|
| + /// function, [desc] provides a description of usage,
|
| + /// [examples] is a Map of examples for each interpolated variable.
|
| + /// For example
|
| + ///
|
| + /// hello(yourName) => Intl.message(
|
| + /// "Hello, $yourName",
|
| + /// name: "hello",
|
| + /// args: [yourName],
|
| + /// desc: "Say hello",
|
| + /// examples = const {"yourName": "Sparky"}.
|
| + ///
|
| + /// The source code will be processed via the analyzer to extract out the
|
| + /// message data, so only a subset of valid Dart code is accepted. In
|
| + /// particular, everything must be literal and cannot refer to variables
|
| + /// outside the scope of the enclosing function. The [examples] map must be a
|
| + /// valid const literal map. Similarly, the [desc] argument must be a single,
|
| + /// simple string. These two arguments will not be used at runtime but will be
|
| + /// extracted from the source code and used as additional data for
|
| + /// translators. For more information see the "Messages" section of the main
|
| + /// [package documentation] (https://pub.dartlang.org/packages/intl).
|
| + ///
|
| + /// The [name] and [args] arguments are required, and are used at runtime
|
| + /// to look up the localized version and pass the appropriate arguments to it.
|
| + /// We may in the future modify the code during compilation to make manually
|
| + /// passing those arguments unnecessary.
|
| + static String message(String message_str,
|
| + {String desc: '',
|
| + Map<String, dynamic> examples: const {},
|
| + String locale,
|
| + String name,
|
| + List args,
|
| + String meaning}) =>
|
| + _message(message_str, locale, name, args, meaning);
|
| +
|
| + /// Omit the compile-time only parameters so dart2js can see to drop them.
|
| + static _message(String message_str, String locale, String name, List args,
|
| + String meaning) {
|
| return messageLookup.lookupMessage(
|
| - message_str, desc, examples, locale, name, args, meaning);
|
| + message_str, locale, name, args, meaning);
|
| }
|
|
|
| - /**
|
| - * Return the locale for this instance. If none was set, the locale will
|
| - * be the default.
|
| - */
|
| + /// Return the locale for this instance. If none was set, the locale will
|
| + /// be the default.
|
| String get locale => _locale;
|
|
|
| - /**
|
| - * Return true if the locale exists, or if it is null. The null case
|
| - * is interpreted to mean that we use the default locale.
|
| - */
|
| - static bool _localeExists(localeName) => DateFormat.localeExists(localeName);
|
| -
|
| - /**
|
| - * Given [newLocale] return a locale that we have data for that is similar
|
| - * to it, if possible.
|
| - *
|
| - * If [newLocale] is found directly, return it. If it can't be found, look up
|
| - * based on just the language (e.g. 'en_CA' -> 'en'). Also accepts '-'
|
| - * as a separator and changes it into '_' for lookup, and changes the
|
| - * country to uppercase.
|
| - *
|
| - * There is a special case that if a locale named "fallback" is present
|
| - * and has been initialized, this will return that name. This can be useful
|
| - * for messages where you don't want to just use the text from the original
|
| - * source code, but wish to have a universal fallback translation.
|
| - *
|
| - * Note that null is interpreted as meaning the default locale, so if
|
| - * [newLocale] is null it will be returned.
|
| - */
|
| + /// Given [newLocale] return a locale that we have data for that is similar
|
| + /// to it, if possible.
|
| + ///
|
| + /// If [newLocale] is found directly, return it. If it can't be found, look up
|
| + /// based on just the language (e.g. 'en_CA' -> 'en'). Also accepts '-'
|
| + /// as a separator and changes it into '_' for lookup, and changes the
|
| + /// country to uppercase.
|
| + ///
|
| + /// There is a special case that if a locale named "fallback" is present
|
| + /// and has been initialized, this will return that name. This can be useful
|
| + /// for messages where you don't want to just use the text from the original
|
| + /// source code, but wish to have a universal fallback translation.
|
| + ///
|
| + /// Note that null is interpreted as meaning the default locale, so if
|
| + /// [newLocale] is null the default locale will be returned.
|
| static String verifiedLocale(String newLocale, Function localeExists,
|
| {Function onFailure: _throwLocaleError}) {
|
| // TODO(alanknight): Previously we kept a single verified locale on the Intl
|
| @@ -220,8 +213,11 @@ class Intl {
|
| if (localeExists(newLocale)) {
|
| return newLocale;
|
| }
|
| - for (var each in
|
| - [canonicalizedLocale(newLocale), shortLocale(newLocale), "fallback"]) {
|
| + for (var each in [
|
| + canonicalizedLocale(newLocale),
|
| + shortLocale(newLocale),
|
| + "fallback"
|
| + ]) {
|
| if (localeExists(each)) {
|
| return each;
|
| }
|
| @@ -229,26 +225,22 @@ class Intl {
|
| return onFailure(newLocale);
|
| }
|
|
|
| - /**
|
| - * The default action if a locale isn't found in verifiedLocale. Throw
|
| - * an exception indicating the locale isn't correct.
|
| - */
|
| + /// The default action if a locale isn't found in verifiedLocale. Throw
|
| + /// an exception indicating the locale isn't correct.
|
| static String _throwLocaleError(String localeName) {
|
| throw new ArgumentError("Invalid locale '$localeName'");
|
| }
|
|
|
| - /** Return the short version of a locale name, e.g. 'en_US' => 'en' */
|
| + /// Return the short version of a locale name, e.g. 'en_US' => 'en'
|
| static String shortLocale(String aLocale) {
|
| if (aLocale.length < 2) return aLocale;
|
| return aLocale.substring(0, 2).toLowerCase();
|
| }
|
|
|
| - /**
|
| - * Return the name [aLocale] turned into xx_YY where it might possibly be
|
| - * in the wrong case or with a hyphen instead of an underscore. If
|
| - * [aLocale] is null, for example, if you tried to get it from IE,
|
| - * return the current system locale.
|
| - */
|
| + /// Return the name [aLocale] turned into xx_YY where it might possibly be
|
| + /// in the wrong case or with a hyphen instead of an underscore. If
|
| + /// [aLocale] is null, for example, if you tried to get it from IE,
|
| + /// return the current system locale.
|
| static String canonicalizedLocale(String aLocale) {
|
| // Locales of length < 5 are presumably two-letter forms, or else malformed.
|
| // We return them unmodified and if correct they will be found.
|
| @@ -267,61 +259,116 @@ class Intl {
|
| return '${aLocale[0]}${aLocale[1]}_$region';
|
| }
|
|
|
| - /**
|
| - * Format a message differently depending on [howMany]. Normally used
|
| - * as part of an `Intl.message` text that is to be translated.
|
| - * Selects the correct plural form from
|
| - * the provided alternatives. The [other] named argument is mandatory.
|
| - */
|
| - static String plural(int howMany, {zero, one, two, few, many, other,
|
| - String desc, Map<String, String> examples, String locale, String name,
|
| - List<String> args, String meaning}) {
|
| + /// Format a message differently depending on [howMany]. Normally used
|
| + /// as part of an `Intl.message` text that is to be translated.
|
| + /// Selects the correct plural form from
|
| + /// the provided alternatives. The [other] named argument is mandatory.
|
| + static String plural(int howMany,
|
| + {zero,
|
| + one,
|
| + two,
|
| + few,
|
| + many,
|
| + other,
|
| + String desc,
|
| + Map<String, dynamic> examples,
|
| + String locale,
|
| + String name,
|
| + List args,
|
| + String meaning}) {
|
| // If we are passed a name and arguments, then we are operating as a
|
| // top-level message, so look up our translation by calling Intl.message
|
| // with ourselves as an argument.
|
| if (name != null) {
|
| - return message(plural(howMany,
|
| + return message(
|
| + plural(howMany,
|
| zero: zero,
|
| one: one,
|
| two: two,
|
| few: few,
|
| many: many,
|
| - other: other),
|
| - name: name, args: args, locale: locale, meaning: meaning);
|
| + other: other,
|
| + locale: locale),
|
| + name: name,
|
| + args: args,
|
| + locale: locale,
|
| + meaning: meaning);
|
| }
|
| if (other == null) {
|
| throw new ArgumentError("The 'other' named argument must be provided");
|
| }
|
| - // TODO(alanknight): This algorithm needs to be locale-dependent.
|
| - switch (howMany) {
|
| - case 0:
|
| - return (zero == null) ? other : zero;
|
| - case 1:
|
| - return (one == null) ? other : one;
|
| - case 2:
|
| - return (two == null) ? ((few == null) ? other : few) : two;
|
| - default:
|
| - if ((howMany == 3 || howMany == 4) && few != null) return few;
|
| - if (howMany > 10 && howMany < 100 && many != null) return many;
|
| + if (howMany == null) {
|
| + throw new ArgumentError("The howMany argument to plural cannot be null");
|
| + }
|
| + // If there's an explicit case for the exact number, we use it. This is not
|
| + // strictly in accord with the CLDR rules, but it seems to be the
|
| + // expectation. At least I see e.g. Russian translations that have a zero
|
| + // case defined. The rule for that locale will never produce a zero, and
|
| + // treats it as other. But it seems reasonable that, even if the language
|
| + // rules treat zero as other, we might want a special message for zero.
|
| + if (howMany == 0 && zero != null) return zero;
|
| + if (howMany == 1 && one != null) return one;
|
| + if (howMany == 2 && two != null) return two;
|
| + var pluralRule = _pluralRule(locale, howMany);
|
| + var pluralCase = pluralRule();
|
| + switch (pluralCase) {
|
| + case plural_rules.PluralCase.ZERO:
|
| + return zero ?? other;
|
| + case plural_rules.PluralCase.ONE:
|
| + return one ?? other;
|
| + case plural_rules.PluralCase.TWO:
|
| + return two ?? few ?? other;
|
| + case plural_rules.PluralCase.FEW:
|
| + return few ?? other;
|
| + case plural_rules.PluralCase.MANY:
|
| + return many ?? other;
|
| + case plural_rules.PluralCase.OTHER:
|
| return other;
|
| + default:
|
| + throw new ArgumentError.value(
|
| + howMany, "howMany", "Invalid plural argument");
|
| }
|
| - throw new ArgumentError("Invalid plural usage for $howMany");
|
| }
|
|
|
| - /**
|
| - * Format a message differently depending on [targetGender]. Normally used as
|
| - * part of an Intl.message message that is to be translated.
|
| - */
|
| - static String gender(String targetGender, {String male, String female,
|
| - String other, String desc, Map<String, String> examples, String locale,
|
| - String name, List<String> args, String meaning}) {
|
| + static var _cachedPluralRule;
|
| + static String _cachedPluralLocale;
|
| +
|
| + static _pluralRule(String locale, int howMany) {
|
| + plural_rules.startRuleEvaluation(howMany);
|
| + var verifiedLocale = Intl.verifiedLocale(
|
| + locale, plural_rules.localeHasPluralRules,
|
| + onFailure: (locale) => 'default');
|
| + if (_cachedPluralLocale == verifiedLocale) {
|
| + return _cachedPluralRule;
|
| + } else {
|
| + _cachedPluralRule = plural_rules.pluralRules[verifiedLocale];
|
| + _cachedPluralLocale = verifiedLocale;
|
| + return _cachedPluralRule;
|
| + }
|
| + }
|
| +
|
| + /// Format a message differently depending on [targetGender]. Normally used as
|
| + /// part of an Intl.message message that is to be translated.
|
| + static String gender(String targetGender,
|
| + {String male,
|
| + String female,
|
| + String other,
|
| + String desc,
|
| + Map<String, dynamic> examples,
|
| + String locale,
|
| + String name,
|
| + List args,
|
| + String meaning}) {
|
| // If we are passed a name and arguments, then we are operating as a
|
| // top-level message, so look up our translation by calling Intl.message
|
| // with ourselves as an argument.
|
| if (name != null) {
|
| return message(
|
| gender(targetGender, male: male, female: female, other: other),
|
| - name: name, args: args, locale: locale, meaning: meaning);
|
| + name: name,
|
| + args: args,
|
| + locale: locale,
|
| + meaning: meaning);
|
| }
|
|
|
| if (other == null) {
|
| @@ -337,15 +384,19 @@ class Intl {
|
| }
|
| }
|
|
|
| - /**
|
| - * Format a message differently depending on [choice]. We look up the value
|
| - * of [choice] in [cases] and return the result, or an empty string if
|
| - * it is not found. Normally used as part
|
| - * of an Intl.message message that is to be translated.
|
| - */
|
| - static String select(String choice, Map<String, String> cases, {String desc,
|
| - Map<String, String> examples, String locale, String name,
|
| - List<String> args, String meaning}) {
|
| + /// Format a message differently depending on [choice]. We look up the value
|
| + /// of [choice] in [cases] and return the result, or an empty string if
|
| + /// it is not found. Normally used as part
|
| + /// of an Intl.message message that is to be translated.
|
| + static String select(Object choice, Map<String, String> cases,
|
| + {String desc,
|
| + Map<String, dynamic> examples,
|
| + String locale,
|
| + String name,
|
| + List args,
|
| + String meaning}) {
|
| + // Allow passing non-strings, e.g. enums to a select.
|
| + choice = "$choice";
|
| // If we are passed a name and arguments, then we are operating as a
|
| // top-level message, so look up our translation by calling Intl.message
|
| // with ourselves as an argument.
|
| @@ -356,46 +407,42 @@ class Intl {
|
| var exact = cases[choice];
|
| if (exact != null) return exact;
|
| var other = cases["other"];
|
| - if (other ==
|
| - null) throw new ArgumentError("The 'other' case must be specified");
|
| + if (other == null)
|
| + throw new ArgumentError("The 'other' case must be specified");
|
| return other;
|
| }
|
|
|
| - /**
|
| - * Run [function] with the default locale set to [locale] and
|
| - * return the result.
|
| - *
|
| - * This is run in a zone, so async operations invoked
|
| - * from within [function] will still have the locale set.
|
| - *
|
| - * In simple usage [function] might be a single
|
| - * `Intl.message()` call or number/date formatting operation. But it can
|
| - * also be an arbitrary function that calls multiple Intl operations.
|
| - *
|
| - * For example
|
| - *
|
| - * Intl.withLocale("fr", () => new NumberFormat.format(123456));
|
| - *
|
| - * or
|
| - *
|
| - * hello(name) => Intl.message(
|
| - * "Hello $name.",
|
| - * name: 'hello',
|
| - * args: [name],
|
| - * desc: 'Say Hello');
|
| - * Intl.withLocale("zh", new Timer(new Duration(milliseconds:10),
|
| - * () => print(hello("World")));
|
| - */
|
| + /// Run [function] with the default locale set to [locale] and
|
| + /// return the result.
|
| + ///
|
| + /// This is run in a zone, so async operations invoked
|
| + /// from within [function] will still have the locale set.
|
| + ///
|
| + /// In simple usage [function] might be a single
|
| + /// `Intl.message()` call or number/date formatting operation. But it can
|
| + /// also be an arbitrary function that calls multiple Intl operations.
|
| + ///
|
| + /// For example
|
| + ///
|
| + /// Intl.withLocale("fr", () => new NumberFormat.format(123456));
|
| + ///
|
| + /// or
|
| + ///
|
| + /// hello(name) => Intl.message(
|
| + /// "Hello $name.",
|
| + /// name: 'hello',
|
| + /// args: [name],
|
| + /// desc: 'Say Hello');
|
| + /// Intl.withLocale("zh", new Timer(new Duration(milliseconds:10),
|
| + /// () => print(hello("World")));
|
| static withLocale(String locale, function()) {
|
| var canonical = Intl.canonicalizedLocale(locale);
|
| return runZoned(function, zoneValues: {#Intl.locale: canonical});
|
| }
|
|
|
| - /**
|
| - * Accessor for the current locale. This should always == the default locale,
|
| - * unless for some reason this gets called inside a message that resets the
|
| - * locale.
|
| - */
|
| + /// Accessor for the current locale. This should always == the default locale,
|
| + /// unless for some reason this gets called inside a message that resets the
|
| + /// locale.
|
| static String getCurrentLocale() {
|
| if (defaultLocale == null) defaultLocale = systemLocale;
|
| return defaultLocale;
|
| @@ -403,3 +450,34 @@ class Intl {
|
|
|
| toString() => "Intl($locale)";
|
| }
|
| +
|
| +/// Convert a string to beginning of sentence case, in a way appropriate to the
|
| +/// locale.
|
| +///
|
| +/// Currently this just converts the first letter to uppercase, which works for
|
| +/// many locales, and we have the option to extend this to handle more cases
|
| +/// without changing the API for clients. It also hard-codes the case of
|
| +/// dotted i in Turkish and Azeri.
|
| +String toBeginningOfSentenceCase(String input, [String locale]) {
|
| + if (input == null || input.isEmpty) return input;
|
| + return "${_upperCaseLetter(input[0], locale)}${input.substring(1)}";
|
| +}
|
| +
|
| +/// Convert the input single-letter string to upper case. A trivial
|
| +/// hard-coded implementation that only handles simple upper case
|
| +/// and the dotted i in Turkish/Azeri.
|
| +///
|
| +/// Private to the implementation of [toBeginningOfSentenceCase].
|
| +// TODO(alanknight): Consider hard-coding other important cases.
|
| +// See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt
|
| +// TODO(alanknight): Alternatively, consider toLocaleUpperCase in browsers.
|
| +// See also https://github.com/dart-lang/sdk/issues/6706
|
| +String _upperCaseLetter(String input, String locale) {
|
| + // Hard-code the important edge case of i->İ
|
| + if (locale != null) {
|
| + if (input == "i" && locale.startsWith("tr") || locale.startsWith("az")) {
|
| + return "\u0130";
|
| + }
|
| + }
|
| + return input.toUpperCase();
|
| +}
|
|
|