Skip to content

Commit

Permalink
Improvements to compiled size of generated messages with dart2js, loo…
Browse files Browse the repository at this point in the history
…sen pubspec for fixnum
  • Loading branch information
alan-knight committed Nov 18, 2015
1 parent 527f470 commit d63da27
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 36 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 0.12.5
* Parse Eras in DateFormat.
* Update pubspec.yaml to allow newer version of fixnum.
* Improvements to the compiled size of generated messages code with dart2js.

## 0.12.4+2
* update analyzer to '<0.27.0'
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ the message.
name: "greetingMessage",
args: [name],
desc: "Greet the user as they first open the application",
examples: {'name': "Emily"});
examples: const {'name': "Emily"});
print(greetingMessage('Dan'));

There is one special class of complex expressions allowed in the
message string, for plurals and genders.

remainingEmailsMessage(int howMany, String userName) =>
remainingEmailsMessage(int howMany, String userName) =>
Intl.message(
"${Intl.plural(howMany,
zero: 'There are no emails left for $userName.',
Expand All @@ -138,15 +138,15 @@ message string, for plurals and genders.
name: "remainingEmailsMessage",
args: [howMany, userName],
desc: "How many emails remain after archiving.",
examples: {'howMany': 42, 'userName': 'Fred'});
examples: const {'howMany': 42, 'userName': 'Fred'});

print(remainingEmailsMessage(1, "Fred"));

However, since the typical usage for a plural or gender is for it to
be at the top-level, we can also omit the [Intl.message][Intl.message] call and
provide its parameters to the [Intl.plural][Intl.plural] call instead.

remainingEmailsMessage(int howMany, String userName) =>
remainingEmailsMessage(int howMany, String userName) =>
Intl.plural(
howMany,
zero: 'There are no emails left for $userName.',
Expand All @@ -155,12 +155,12 @@ provide its parameters to the [Intl.plural][Intl.plural] call instead.
name: "remainingEmailsMessage",
args: [howMany, userName],
desc: "How many emails remain after archiving.",
examples: {'howMany': 42, 'userName': 'Fred'});
examples: const {'howMany': 42, 'userName': 'Fred'});

Similarly, there is an [Intl.gender][Intl.gender] message, and plurals
and genders can be nested.

notOnlineMessage(String userName, String userGender) =>
notOnlineMessage(String userName, String userGender) =>
Intl.gender(
userGender,
male: '$userName is unavailable because he is not online.',
Expand All @@ -169,7 +169,7 @@ and genders can be nested.
name: "notOnlineMessage",
args: [userName, userGender],
desc: "The user is not available to hangout.",
examples: {{'userGender': 'male', 'userName': 'Fred'},
examples: const {{'userGender': 'male', 'userName': 'Fred'},
{'userGender': 'female', 'userName' : 'Alice'}});

It's recommended to use complete sentences in the sub-messages to keep
Expand All @@ -179,7 +179,7 @@ the structure as simple as possible for the translators.

When your program contains messages that need translation, these must
be extracted from the program source, sent to human translators, and the
results need to be incorporated.
results need to be incorporated.

To extract messages, run the `extract_to_arb.dart` program.

Expand All @@ -190,14 +190,14 @@ This will produce a file `intl_messages.arb` with the messages from
all of these programs. an [ARB]
(https://code.google.com/p/arb/wiki/ApplicationResourceBundleSpecification)
format file which can be used for input to translation tools like
[Google Translator Toolkit](https://translate.google.com/toolkit/)
[Google Translator Toolkit](https://translate.google.com/toolkit/)
The resulting translations can be used to generate a set of libraries
using the `generate_from_arb.dart` program.

This expects to receive a series of files, one per
locale.
locale.

pub run intl:generate_from_arb --generated_file_prefix=<prefix>
pub run intl:generate_from_arb --generated_file_prefix=<prefix>
<my_dart_files> <translated_ARB_files>

This will generate Dart libraries, one per locale, which contain the
Expand Down
42 changes: 35 additions & 7 deletions lib/generate_localized.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,29 @@ void generateIndividualMessageFile(String basicLocale,
usableTranslations.sort((a, b) =>
a.originalMessages.first.name.compareTo(b.originalMessages.first.name));
for (var translation in usableTranslations) {
for (var original in translation.originalMessages) {
// Some messages we generate as methods in this class. Simpler ones
// we inline in the map from names to messages.
var messagesThatNeedMethods =
translation.originalMessages.where((each) => _hasArguments(each));
for (var original in messagesThatNeedMethods) {
result
..write(" ")
..write(original.toCodeForLocale(locale))
..write("\n\n");
}
}
result.write("\n final messages = const {\n");
// Some gyrations to prevent parts of the deferred libraries from being
// inlined into the main one, defeating the space savings. Issue 24356
result.write(
"""
final messages = _notInlinedMessages(_notInlinedMessages);
static _notInlinedMessages(_) => {
""");
var entries = usableTranslations
.expand((translation) => translation.originalMessages)
.map((original) => original.name)
.map((name) => " \"$name\" : $name");
result
..write(entries.join(",\n"))
..write("\n };\n}");
.map((original) =>
' "${original.name}" : ${_mapReference(original, locale)}');
result..write(entries.join(",\n"))..write("\n };\n}");

// To preserve compatibility, we don't use the canonical version of the locale
// in the file name.
Expand All @@ -148,6 +156,26 @@ void generateIndividualMessageFile(String basicLocale,
new File(filename).writeAsStringSync(result.toString());
}

bool _hasArguments(MainMessage message) => message.arguments.length != 0;

/**
* Simple messages are printed directly in the map of message names to
* functions as a call that returns a lambda. e.g.
*
* "foo" : simpleMessage("This is foo"),
*
* This is helpful for the compiler.
**/
String _mapReference(MainMessage original, String locale) {
if (!_hasArguments(original)) {
// No parameters, can be printed simply.
return 'MessageLookupByLibrary.simpleMessage("'
'${original.translations[locale]}")';
} else {
return original.name;
}
}

/**
* This returns the mostly constant string used in
* [generateIndividualMessageFile] for the beginning of the file,
Expand Down
15 changes: 9 additions & 6 deletions lib/intl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ part 'src/intl/number_format.dart';
* name: 'today',
* args: [date],
* desc: 'Indicate the current date',
* examples: {'date' : 'June 8, 2012'});
* examples: const {'date' : 'June 8, 2012'});
* print(today(new DateTime.now().toString());
*
* howManyPeople(numberOfPeople, place) => Intl.plural(
Expand All @@ -60,7 +60,7 @@ part 'src/intl/number_format.dart';
* name: 'msg',
* args: [numberOfPeople, place],
* desc: 'Description of how many people are seen in a place.',
* examples: {'numberOfPeople': 3, 'place': 'London'});
* 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.
Expand Down Expand Up @@ -153,7 +153,7 @@ class Intl {
* name: "hello",
* args: [yourName],
* desc: "Say hello",
* examples = {"yourName": "Sparky"}.
* 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
Expand All @@ -172,9 +172,12 @@ class Intl {
*/
static String message(String message_str, {String desc: '',
Map<String, String> examples: const {}, String locale, String name,
List<String> args, String meaning}) {
return messageLookup.lookupMessage(
message_str, desc, examples, locale, name, args, meaning);
List<String> args, String meaning}) =>
_message(message_str, locale, name, args);

/** Omit the compile-time only parameters so dart2js can see to drop them. */
static _message(String message_str, String locale, String name, List args) {
return messageLookup.lookupMessage(message_str, locale, name, args);
}

/**
Expand Down
19 changes: 11 additions & 8 deletions lib/message_lookup_by_library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,17 @@ class CompositeMessageLookup {
* If nothing is found, return [message_str]. The [desc] and [examples]
* parameters are ignored
*/
String lookupMessage(String message_str, [final String desc = '',
final Map examples = const {}, String locale, String name,
List<String> args, String meaning]) {
String lookupMessage(String message_str, String locale,
String name, List args) {
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);
var messages = availableMessages[verifiedLocale];
if (messages == null) return message_str;
return messages.lookupMessage(
message_str, desc, examples, locale, name, args, meaning);
return messages.
lookupMessage(message_str, locale, name, args);
}

/**
Expand Down Expand Up @@ -90,9 +89,7 @@ abstract class MessageLookupByLibrary {
* 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, String meaning]) {
String lookupMessage(String message_str, String locale, String name, List args) {
if (name == null) return message_str;
var function = this[name];
return function == null ? message_str : Function.apply(function, args);
Expand All @@ -111,4 +108,10 @@ abstract class MessageLookupByLibrary {
String get localeName;

toString() => localeName;

/**
* Return a function that returns the given string.
* An optimization for dart2js, used from the generated code.
*/
static simpleMessage(translatedString) => () => translatedString;
}
5 changes: 2 additions & 3 deletions lib/src/intl_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ class UninitializedLocaleData<F> {
operator [](String key) =>
(key == 'en_US') ? fallbackData : _throwException();

String lookupMessage(String message_str, [final String desc = '',
final Map examples = const {}, String locale, String name,
List<String> args, String meaning]) => message_str;
String lookupMessage(String message_str, String locale,
String name, List args) => message_str;

List get keys => _throwException();

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies:
path: '>=0.9.0 <2.0.0'
petitparser: '>=1.1.3 <2.0.0'
dev_dependencies:
fixnum: '>=0.9.0 <0.10.0'
fixnum: '>=0.9.0 <0.11.0'
unittest: '>=0.10.0 <0.12.0'
transformers:
- $dart2js:
Expand Down

0 comments on commit d63da27

Please sign in to comment.