blob: 9c62fb33f4bf3b387338933eeff39a2cc1dbe7a9 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// 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.
/**
* Message/plural format library with locale support. This can have different
* implementations based on the mechanism for finding the localized versions
* of messages. This version expects them to be in a library named e.g.
* 'messages_en_US'. The prefix is set in the [initializeMessages] call, which
* must be made for a locale before any lookups can be done.
*
* See Intl class comment or `tests/message_format_test.dart` for more examples.
*/
//TODO(efortuna): documentation example involving the offset parameter?
library message_lookup_local;
import 'intl.dart';
import 'src/intl_helpers.dart';
import 'dart:mirrors';
/**
* Initialize the user messages for [localeName]. Note that this is an ASYNC
* operation. This must be called before attempting to use messages in
* [localeName].
*/
Future initializeMessages(localeName, [String source = 'messages_']) {
initializeInternalMessageLookup(
() => new MessageLookupLocal(localeName, source));
_initializeMessagesForLocale(localeName);
return new Future.immediate(null);
}
void _initializeMessagesForLocale(String localeName) {}
class MessageLookupLocal {
/** Prevent infinite recursion when looking up the message. */
bool _lookupInProgress = false;
/** The libraries we can look in for internationalization messages. */
Map<String, LibraryMirror> _libraries;
/** The prefix used to find libraries that contain localized messages.
* So, if this is 'messages_' we would look for messages for the locale
* 'pt_BR' in a library named 'messages_pt_BR'.
*/
String _sourcePrefix;
/**
* Constructor. The [localeName] is of the form 'en' or 'en_US'.
*The [source] parameter defines the prefix that is used to find
* libraries that contain localized messages. So with the default value of
* 'messages_', we would look for messages for the locale 'pt_BR' in a library
* named 'messages_pt_BR'.
*/
MessageLookupLocal(String localeName, this._sourcePrefix) {
_libraries = currentMirrorSystem().libraries;
}
/**
* Return true if the locale exists, or if it is null. The null case
* is interpreted to mean that we use the default locale.
*/
bool localeExists(localeName) {
if (localeName == null) return false;
return _libraries['$_sourcePrefix$localeName'] != null;
}
/**
* Return the localized version of a message. We are passed the original
* version of the message, which consists of a
* [message_str] that will be translated, and which may be interpolated
* based on one or more variables, a [desc] providing a description of usage
* for the [message_str], and a map of [examples] for each data element to be
* substituted into the message.
*
* For example, if message="Hello, $name", then
* examples = {'name': 'Sparky'}. If not using the user's default locale, or
* if the locale is not easily detectable, explicitly pass [locale].
*
* The values of [desc] and [examples] are not used at run-time but are only
* made available to the translators, so they MUST be simple Strings available
* at compile time: no String interpolation or concatenation.
* The expected usage of this is inside a function that takes as parameters
* the variables used in the interpolated string.
*
* Ultimately, the information about the enclosing function and its arguments
* will be extracted automatically but for the time being it must be passed
* explicitly in the [name] and [args] arguments.
*/
String lookupMessage(String message_str, [final String desc='',
final Map examples=const {}, String locale,
String name, List<String> args]) {
if (name == null) return message_str;
// The translations also make use of Intl.message, so we need to not
// recurse and just stop when we find the first substitution.
if (_lookupInProgress) return message_str;
_lookupInProgress = true;
var result;
try {
var actualLocale = (locale == null) ? Intl.getCurrentLocale() : locale;
// For this usage, if the locale doesn't exist for messages, just return
// it and we'll fall back to the original version.
var verifiedLocale =
Intl.verifiedLocale(
actualLocale,
localeExists,
onFailure: (locale)=>locale);
LibraryMirror messagesForThisLocale =
_libraries['$_sourcePrefix$verifiedLocale'];
if (messagesForThisLocale == null) return message_str;
MethodMirror localized = messagesForThisLocale.functions[name];
if (localized == null) return message_str;
result = messagesForThisLocale.invoke(localized.simpleName, args);
}
finally {
_lookupInProgress = false;
}
return result.value.reflectee;
}
}