Support generating translations as JSON data rather than code

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=167018221
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3ef2d7..dbf2563 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## 0.15.2
  * Group the padding digits to the left of the number, if present. e.g. 00,001.
  * Update the SDK constraint for Dart 2.0 dev versions.
+ * Tweak lookup code to support translated messages as JSON rather than code.
 
 ## 0.15.1
  * Use the platform.locale API to get the OS platform.
diff --git a/lib/intl.dart b/lib/intl.dart
index 095eb71..13b15c9 100644
--- a/lib/intl.dart
+++ b/lib/intl.dart
@@ -264,36 +264,65 @@
   /// 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 zero,
+      String one,
+      String two,
+      String few,
+      String many,
+      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(
-          plural(howMany,
-              zero: zero,
-              one: one,
-              two: two,
-              few: few,
-              many: many,
-              other: other,
-              locale: locale),
-          name: name,
-          args: args,
-          locale: locale,
-          meaning: meaning);
-    }
+    // Call our internal method, dropping examples and desc because they're not
+    // used at runtime and we want them to be optimized away.
+    return _plural(howMany,
+        zero: zero,
+        one: one,
+        two: two,
+        few: few,
+        many: many,
+        other: other,
+        locale: locale,
+        name: name,
+        args: args,
+        meaning: meaning);
+  }
+
+  static String _plural(int howMany,
+      {String zero,
+      String one,
+      String two,
+      String few,
+      String many,
+      String other,
+      String locale,
+      String name,
+      List args,
+      String meaning}) {
+    // Look up our translation, but pass in a null message so we don't have to
+    // eagerly evaluate calls that may not be necessary.
+    var translated = _message(null, locale, name, args, meaning);
+
+    /// If there's a translation, return it, otherwise evaluate with our
+    /// original text.
+    return translated ??
+        pluralLogic(howMany,
+            zero: zero,
+            one: one,
+            two: two,
+            few: few,
+            many: many,
+            other: other,
+            locale: locale);
+  }
+
+  /// Internal: Implements the logic for plural selection - use [plural] for
+  /// normal messages.
+  static pluralLogic(int howMany,
+      {zero, one, two, few, many, other, String locale, String meaning}) {
     if (other == null) {
       throw new ArgumentError("The 'other' named argument must be provided");
     }
@@ -347,11 +376,10 @@
     }
   }
 
-  /// Format a message differently depending on [targetGender]. Normally used as
-  /// part of an Intl.message message that is to be translated.
+  /// Format a message differently depending on [targetGender].
   static String gender(String targetGender,
-      {String male,
-      String female,
+      {String female,
+      String male,
       String other,
       String desc,
       Map<String, dynamic> examples,
@@ -359,18 +387,43 @@
       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);
-    }
+    // Call our internal method, dropping args and desc because they're not used
+    // at runtime and we want them to be optimized away.
+    return _gender(targetGender,
+        male: male,
+        female: female,
+        other: other,
+        locale: locale,
+        name: name,
+        args: args,
+        meaning: meaning);
+  }
 
+  static String _gender(String targetGender,
+      {String female,
+      String male,
+      String other,
+      String desc,
+      Map<String, dynamic> examples,
+      String locale,
+      String name,
+      List args,
+      String meaning}) {
+    // Look up our translation, but pass in a null message so we don't have to
+    // eagerly evaluate calls that may not be necessary.
+    var translated = _message(null, locale, name, args, meaning);
+
+    /// If there's a translation, return it, otherwise evaluate with our
+    /// original text.
+    return translated ??
+        genderLogic(targetGender,
+            female: female, male: male, other: other, locale: locale);
+  }
+
+  /// Internal: Implements the logic for gender selection - use [gender] for
+  /// normal messages.
+  static genderLogic(String targetGender,
+      {female, male, other, String locale}) {
     if (other == null) {
       throw new ArgumentError("The 'other' named argument must be specified");
     }
@@ -395,15 +448,26 @@
       String name,
       List args,
       String meaning}) {
+    return _select(choice, cases,
+        locale: locale, name: name, args: args, meaning: meaning);
+  }
+
+  static String _select(Object choice, Map<String, String> cases,
+      {String locale, String name, List args, String meaning}) {
+    // Look up our translation, but pass in a null message so we don't have to
+    // eagerly evaluate calls that may not be necessary.
+    var translated = _message(null, locale, name, args, meaning);
+
+    /// If there's a translation, return it, otherwise evaluate with our
+    /// original text.
+    return translated ?? selectLogic(choice, cases);
+  }
+
+  /// Internal: Implements the logic for select - use [select] for
+  /// normal messages.
+  static selectLogic(Object choice, Map<String, String> cases) {
     // 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.
-    if (name != null) {
-      return message(select(choice, cases),
-          name: name, args: args, locale: locale);
-    }
     var exact = cases[choice];
     if (exact != null) return exact;
     var other = cases["other"];
diff --git a/lib/message_lookup_by_library.dart b/lib/message_lookup_by_library.dart
index 66b93fd..48973fb 100644
--- a/lib/message_lookup_by_library.dart
+++ b/lib/message_lookup_by_library.dart
@@ -112,16 +112,21 @@
     var notFound = false;
     var actualName = computeMessageName(name, message_str, meaning);
     if (actualName == null) notFound = true;
-    var function = this[actualName];
-    notFound = notFound || (function == null);
+    var translation = this[actualName];
+    notFound = notFound || (translation == null);
     if (notFound) {
       return ifAbsent == null ? message_str : ifAbsent(message_str, args);
     } else {
-      args = args ?? [];
-      return Function.apply(function, args);
+      args = args ?? const [];
+      return evaluateMessage(translation, args);
     }
   }
 
+  /// Evaluate the translated message and return the translated string.
+  String evaluateMessage(translation, List args) {
+    return Function.apply(translation, args);
+  }
+
   /// Return our message with the given name
   operator [](String messageName) => messages[messageName];