Intl currency formatting can specify decimalDigits and has table of defaults
(rolling forward after Automated g4 rollback of changelist 111723849).

*** Reason for rollback ***

Rolling forward

*** Original change description ***

Automated g4 rollback of changelist 111713489.

*** Reason for rollback ***

Breaks currency tests in adsense due to upper/lower case discrepancy

*** Original change description ***

Intl currency formatting can specify decimalDigits and has table of defaults

***

***
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=111970339
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf59256..318f5a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,9 @@
   * Switch all the source to use line comments.
   * Slight improvement to the error message when parsing dates has an invalid
     value.
+  * Introduce new NumberFormat.currency constructor which can explicitly take a
+    separate currency name and symbol, as well as the number of decimal digits.
+  * Provide a default number of decimal digits per-currency.
 
 ## 0.12.5
   * Parse Eras in DateFormat.
diff --git a/lib/number_symbols_data.dart b/lib/number_symbols_data.dart
index 596acfb..9b32cbe 100644
--- a/lib/number_symbols_data.dart
+++ b/lib/number_symbols_data.dart
@@ -1949,3 +1949,72 @@
       CURRENCY_PATTERN: '\u00A4#,##0.00',
       DEF_CURRENCY_CODE: 'ZAR')
 };
+
+final currencyFractionDigits = {
+  "ADP": 0,
+  "AFN": 0,
+  "ALL": 0,
+  "AMD": 0,
+  "BHD": 3,
+  "BIF": 0,
+  "BYR": 0,
+  "CAD": 2,
+  "CHF": 2,
+  "CLF": 4,
+  "CLP": 0,
+  "COP": 0,
+  "CRC": 0,
+  "CZK": 2,
+  "DEFAULT": 2,
+  "DJF": 0,
+  "ESP": 0,
+  "GNF": 0,
+  "GYD": 0,
+  "HUF": 2,
+  "IDR": 0,
+  "IQD": 0,
+  "IRR": 0,
+  "ISK": 0,
+  "ITL": 0,
+  "JOD": 3,
+  "JPY": 0,
+  "KMF": 0,
+  "KPW": 0,
+  "KRW": 0,
+  "KWD": 3,
+  "LAK": 0,
+  "LBP": 0,
+  "LUF": 0,
+  "LYD": 3,
+  "MGA": 0,
+  "MGF": 0,
+  "MMK": 0,
+  "MNT": 0,
+  "MRO": 0,
+  "MUR": 0,
+  "OMR": 3,
+  "PKR": 0,
+  "PYG": 0,
+  "RSD": 0,
+  "RWF": 0,
+  "SLL": 0,
+  "SOS": 0,
+  "STD": 0,
+  "SYP": 0,
+  "TMM": 0,
+  "TND": 3,
+  "TRL": 0,
+  "TWD": 2,
+  "TZS": 0,
+  "UGX": 0,
+  "UYI": 0,
+  "UZS": 0,
+  "VND": 0,
+  "VUV": 0,
+  "XAF": 0,
+  "XOF": 0,
+  "XPF": 0,
+  "YER": 0,
+  "ZMK": 0,
+  "ZWD": 0,
+};
diff --git a/lib/src/intl/number_format.dart b/lib/src/intl/number_format.dart
index 5af03d4..2fcff9b 100644
--- a/lib/src/intl/number_format.dart
+++ b/lib/src/intl/number_format.dart
@@ -32,7 +32,8 @@
 /// There are also standard patterns available via the special constructors.
 /// e.g.
 ///       var percent = new NumberFormat.percentFormat("ar");
-///       var eurosInUSFormat = new NumberFormat.currencyPattern("en_US", "€");
+///       var eurosInUSFormat = new NumberFormat.currency(locale: "en_US",
+///           symbol: "€");
 /// There are four such constructors: decimalFormat, percentFormat,
 /// scientificFormat and currencyFormat. However, at the moment,
 /// scientificFormat prints only as equivalent to "#E0" and does not take
@@ -46,13 +47,16 @@
   String _positivePrefix = '';
   String _negativeSuffix = '';
   String _positiveSuffix = '';
+
   /// How many numbers in a group when using punctuation to group digits in
   /// large numbers. e.g. in en_US: "1,000,000" has a grouping size of 3 digits
   /// between commas.
   int _groupingSize = 3;
+
   /// In some formats the last grouping size may be different than previous
   /// ones, e.g. Hindi.
   int _finalGroupingSize = 3;
+
   /// Set to true if the format has explicitly set the grouping size.
   bool _groupingSizeSetExplicitly = false;
   bool _decimalSeparatorAlwaysShown = false;
@@ -88,9 +92,50 @@
   /// Caches the symbols used for our locale.
   NumberSymbols _symbols;
 
-  /// The name (or symbol) of the currency to print.
+  /// The name of the currency to print, in ISO 4217 form.
   String currencyName;
 
+  /// The symbol to be used when formatting this as currency.
+  ///
+  /// For example, "$", "US$", or "€".
+  String _currencySymbol;
+
+  /// The symbol to be used when formatting this as currency.
+  ///
+  /// For example, "$", "US$", or "€".
+  String get currencySymbol => _currencySymbol ?? currencyName;
+
+  /// The number of decimal places to use when formatting.
+  ///
+  /// If this is not explicitly specified in the constructor, then for
+  /// currencies we use the default value for the currency if the name is given,
+  /// otherwise we use the value from the pattern for the locale.
+  ///
+  /// So, for example,
+  ///      new NumberFormat.currency(name: 'USD', decimalDigits: 7)
+  /// will format with 7 decimal digits, because that's what we asked for. But
+  ///       new NumberFormat.currency(locale: 'en_US', name: 'JPY')
+  /// will format with zero, because that's the default for JPY, and the
+  /// currency's default takes priority over the locale's default.
+  ///       new NumberFormat.currency(locale: 'en_US')
+  /// will format with two, which is the default for that locale.
+  ///
+  int get decimalDigits => _decimalDigits;
+
+  int _decimalDigits;
+
+  /// For currencies, the default number of decimal places to use in
+  /// formatting. Defaults to two for non-currencies or currencies where it's
+  /// not specified.
+  int get _defaultDecimalDigits =>
+      currencyFractionDigits[currencyName.toUpperCase()] ??
+          currencyFractionDigits['DEFAULT'];
+
+  /// If we have a currencyName, use that currencies decimal digits, unless
+  /// we've explicitly specified some other number.
+  bool get _overridesDecimalDigits =>
+      decimalDigits != null || currencyName != symbols.DEF_CURRENCY_CODE;
+
   /// Transient internal state in which to build up the result of the format
   /// operation. We can have this be just an instance variable because Dart is
   /// single-threaded and unless we do an asynchronous operation in the process
@@ -115,22 +160,77 @@
   NumberFormat.scientificPattern([String locale])
       : this._forPattern(locale, (x) => x.SCIENTIFIC_PATTERN);
 
-  /// Create a number format that prints as CURRENCY_PATTERN. If provided,
+  /// A regular expression to validate currency names are exactly three
+  /// alphabetic characters.
+  static final _checkCurrencyName = new RegExp(r'^[a-zA-Z]{3}$');
+
+  /// Create a number format that prints as CURRENCY_PATTERN. (Deprecated:
+  /// prefer NumberFormat.currency)
+  ///
+  /// If provided,
   /// use [nameOrSymbol] in place of the default currency name. e.g.
   ///        var eurosInCurrentLocale = new NumberFormat
   ///            .currencyPattern(Intl.defaultLocale, "€");
-  NumberFormat.currencyPattern([String locale, String nameOrSymbol])
-      : this._forPattern(locale, (x) => x.CURRENCY_PATTERN, nameOrSymbol);
+  factory NumberFormat.currencyPattern(
+      [String locale, String currencyNameOrSymbol]) {
+    // If it looks like an iso4217 name, pass as name, otherwise as symbol.
+    if (currencyNameOrSymbol != null &&
+        _checkCurrencyName.hasMatch(currencyNameOrSymbol)) {
+      return new NumberFormat.currency(
+          locale: locale, name: currencyNameOrSymbol);
+    } else {
+      return new NumberFormat.currency(
+          locale: locale, symbol: currencyNameOrSymbol);
+    }
+  }
+
+  /// Create a [NumberFormat] that formats using the locale's CURRENCY_PATTERN.
+  ///
+  /// If [locale] is not specified, it will use the current default locale.
+  ///
+  /// If [name] is specified, the currency with that ISO 4217 name will be used.
+  /// Otherwise we will use the default currency name for the current locale. If
+  /// no [symbol] is specified, we will use the currency name in the formatted
+  /// result. e.g.
+  ///      var f = new NumberFormat.currency(locale: 'en_US', name: 'EUR')
+  /// will format currency like "EUR1.23". If we did not specify the name, it
+  /// would format like "USD1.23".
+  ///
+  /// If [symbol] is used, then that symbol will be used in formatting instead
+  /// of the name. e.g.
+  ///      var eurosInCurrentLocale = new NumberFormat.currency(symbol: "€");
+  /// will format like "€1.23". Otherwise it will use the currency name.
+  /// If this is not explicitly specified in the constructor, then for
+  /// currencies we use the default value for the currency if the name is given,
+  ///  otherwise we use the value from the pattern for the locale.
+  ///
+  /// If [decimalDigits] is specified, numbers will format with that many digits
+  /// after the decimal place. If it's not, they will use the default for the
+  /// currency in [name], and the default currency for [locale] if the currency
+  /// name is not specified. e.g.
+  ///       new NumberFormat.currency(name: 'USD', decimalDigits: 7)
+  /// will format with 7 decimal digits, because that's what we asked for. But
+  ///       new NumberFormat.currency(locale: 'en_US', name: 'JPY')
+  /// will format with zero, because that's the default for JPY, and the
+  /// currency's default takes priority over the locale's default.
+  ///       new NumberFormat.currency(locale: 'en_US')
+  /// will format with two, which is the default for that locale.
+  // TODO(alanknight): Should we allow decimalDigits on other numbers.
+  NumberFormat.currency(
+      {String locale, String name, String symbol, int decimalDigits})
+      : this._forPattern(locale, (x) => x.CURRENCY_PATTERN,
+            name: name, currencySymbol: symbol, decimalDigits: decimalDigits);
 
   /// Create a number format that prints in a pattern we get from
   /// the [getPattern] function using the locale [locale].
   NumberFormat._forPattern(String locale, Function getPattern,
-      [this.currencyName])
+      {name, currencySymbol, decimalDigits})
       : _locale = Intl.verifiedLocale(locale, localeExists) {
+    this._currencySymbol = currencySymbol;
+    this._decimalDigits = decimalDigits;
     _symbols = numberFormatSymbols[_locale];
-    if (currencyName == null) {
-      currencyName = _symbols.DEF_CURRENCY_CODE;
-    }
+    currencyName = name ?? _symbols.DEF_CURRENCY_CODE;
+
     _setPattern(getPattern(_symbols));
   }
 
@@ -437,8 +537,14 @@
     if (newPattern == null) return;
     // Make spaces non-breaking
     _pattern = newPattern.replaceAll(' ', '\u00a0');
-    var parser = new _NumberFormatParser(this, newPattern, currencyName);
+    var parser = new _NumberFormatParser(
+        this, newPattern, currencySymbol, decimalDigits);
     parser.parse();
+    if (_overridesDecimalDigits) {
+      var digits = decimalDigits ?? _defaultDecimalDigits;
+      minimumFractionDigits = digits;
+      maximumFractionDigits = digits;
+    }
   }
 
   String toString() => "NumberFormat($_locale, $_pattern)";
@@ -475,9 +581,11 @@
   /// Did we see something that indicates this is, or at least might be,
   /// a negative number.
   bool gotNegative = false;
+
   /// Did we see the required positive suffix at the end. Should
   /// match [gotPositive].
   bool gotPositiveSuffix = false;
+
   /// Did we see the required negative suffix at the end. Should
   /// match [gotNegative].
   bool gotNegativeSuffix = false;
@@ -695,11 +803,16 @@
   final _StringIterator pattern;
 
   /// We can be passed a specific currency symbol, regardless of the locale.
-  String currencyName;
+  String currencySymbol;
+
+  /// We can be given a specific number of decimal places, overriding the
+  /// default.
+  final int decimalDigits;
 
   /// Create a new [_NumberFormatParser] for a particular [NumberFormat] and
   /// [input] pattern.
-  _NumberFormatParser(this.format, input, this.currencyName)
+  _NumberFormatParser(
+      this.format, input, this.currencySymbol, this.decimalDigits)
       : pattern = _iterator(input) {
     pattern.moveNext();
   }
@@ -775,7 +888,7 @@
           return false;
         case _PATTERN_CURRENCY_SIGN:
           // TODO(alanknight): Handle the local/global/portable currency signs
-          affix.write(currencyName);
+          affix.write(currencySymbol);
           break;
         case _PATTERN_PERCENT:
           if (format._multiplier != 1 && format._multiplier != _PERCENT_SCALE) {
@@ -1034,16 +1147,16 @@
 
   _MicroMoney operator ~/(divisor) {
     if (divisor is! int) {
-      throw new ArgumentError.value(divisor, 'divisor',
-          '_MicroMoney ~/ only supports int arguments.');
+      throw new ArgumentError.value(
+          divisor, 'divisor', '_MicroMoney ~/ only supports int arguments.');
     }
     return new _MicroMoney((_integerPart ~/ divisor) * _multiplier);
   }
 
   _MicroMoney operator *(other) {
     if (other is! int) {
-    throw new ArgumentError.value(other, 'other',
-        '_MicroMoney * only supports int arguments.');
+      throw new ArgumentError.value(
+          other, 'other', '_MicroMoney * only supports int arguments.');
     }
     return new _MicroMoney(
         (_integerPart * other) * _multiplier + (_fractionPart * other));
@@ -1053,8 +1166,8 @@
   /// not division by another MicroMoney
   _MicroMoney remainder(other) {
     if (other is! int) {
-      throw new ArgumentError.value(other, 'other',
-          '_MicroMoney.remainder only supports int arguments.');
+      throw new ArgumentError.value(
+          other, 'other', '_MicroMoney.remainder only supports int arguments.');
     }
     return new _MicroMoney(_micros.remainder(other * _multiplier));
   }
diff --git a/test/fixnum_test.dart b/test/fixnum_test.dart
index ea634c8..9a8c346 100644
--- a/test/fixnum_test.dart
+++ b/test/fixnum_test.dart
@@ -62,7 +62,7 @@
 main() {
   test('int64', () {
     int64Values.forEach((number, expected) {
-      var currency = new NumberFormat.currencyPattern().format(number);
+      var currency = new NumberFormat.currency().format(number);
       expect(currency, expected.first);
       var percent = new NumberFormat.percentPattern().format(number);
       expect(percent, expected[1]);
@@ -71,7 +71,7 @@
 
   test('int32', () {
     int32Values.forEach((number, expected) {
-      var currency = new NumberFormat.currencyPattern().format(number);
+      var currency = new NumberFormat.currency().format(number);
       expect(currency, expected.first);
       var percent = new NumberFormat.percentPattern().format(number);
       expect(percent, expected[1]);
diff --git a/test/number_format_test.dart b/test/number_format_test.dart
index df41c41..652cc14 100644
--- a/test/number_format_test.dart
+++ b/test/number_format_test.dart
@@ -81,35 +81,11 @@
     var testFormats = standardFormats(locale);
     var testLength = (testFormats.length * 3) + 1;
     var list = mainList.take(testLength).iterator;
+    list.moveNext();
     mainList = mainList.skip(testLength);
-    var nextLocaleFromList = (list..moveNext()).current;
-    test("Test against ICU data for $locale", () {
-      expect(locale, nextLocaleFromList);
-      for (var format in testFormats) {
-        var formatted = format.format(123);
-        var negative = format.format(-12.3);
-        var large = format.format(1234567890);
-        var expected = (list..moveNext()).current;
-        expect(formatted, expected);
-        var expectedNegative = (list..moveNext()).current;
-        // Some of these results from CLDR have a leading LTR/RTL indicator,
-        // which we don't want. We also treat the difference between Unicode
-        // minus sign (2212) and hyphen-minus (45) as not significant.
-        expectedNegative = expectedNegative
-            .replaceAll("\u200e", "")
-            .replaceAll("\u200f", "")
-            .replaceAll("\u2212", "-");
-        expect(negative, expectedNegative);
-        var expectedLarge = (list..moveNext()).current;
-        expect(large, expectedLarge);
-        var readBack = format.parse(formatted);
-        expect(readBack, 123);
-        var readBackNegative = format.parse(negative);
-        expect(readBackNegative, -12.3);
-        var readBackLarge = format.parse(large);
-        expect(readBackLarge, 1234567890);
-      }
-    });
+    if (locale == list.current) {
+      testAgainstIcu(locale, testFormats, list);
+    }
   }
 
   test('Simple set of numbers', () {
@@ -162,7 +138,7 @@
 
   test('Explicit currency name', () {
     var amount = 1000000.32;
-    var usConvention = new NumberFormat.currencyPattern('en_US', '€');
+    var usConvention = new NumberFormat.currency(locale: 'en_US', symbol: '€');
     var formatted = usConvention.format(amount);
     expect(formatted, '€1,000,000.32');
     var readBack = usConvention.parse(formatted);
@@ -175,7 +151,7 @@
     expect(readBack, amount);
 
     /// Verify we can leave off the currency and it gets filled in.
-    var plainSwiss = new NumberFormat.currencyPattern('de_CH');
+    var plainSwiss = new NumberFormat.currency(locale: 'de_CH');
     formatted = plainSwiss.format(amount);
     expect(formatted, r"CHF" + nbsp + "1'000'000.32");
     readBack = plainSwiss.parse(formatted);
@@ -198,10 +174,74 @@
   });
 
   test('Unparseable', () {
-    var format = new NumberFormat.currencyPattern();
+    var format = new NumberFormat.currency();
     expect(() => format.parse("abcdefg"), throwsFormatException);
     expect(() => format.parse(""), throwsFormatException);
     expect(() => format.parse("1.0zzz"), throwsFormatException);
     expect(() => format.parse("-∞+1"), throwsFormatException);
   });
+
+  var digitsCheck = {
+    0: "@4",
+    1: "@4.3",
+    2: "@4.32",
+    3: "@4.322",
+    4: "@4.3220",
+  };
+
+  test('Decimal digits', () {
+    var amount = 4.3219876;
+    for (var digits in digitsCheck.keys) {
+      var f = new NumberFormat.currency(
+          locale: 'en_US', symbol: '@', decimalDigits: digits);
+      var formatted = f.format(amount);
+      expect(formatted, digitsCheck[digits]);
+    }
+    var defaultFormat = new NumberFormat.currency(locale: 'en_US', symbol: '@');
+    var formatted = defaultFormat.format(amount);
+    expect(formatted, digitsCheck[2]);
+
+    var jpy =
+        new NumberFormat.currency(locale: 'en_US', name: 'JPY', symbol: '@');
+    formatted = jpy.format(amount);
+    expect(formatted, digitsCheck[0]);
+
+    var jpyLower =
+        new NumberFormat.currency(locale: 'en_US', name: 'jpy', symbol: '@');
+    formatted = jpyLower.format(amount);
+    expect(formatted, digitsCheck[0]);
+
+    var tnd = new NumberFormat.currency(name: 'TND', symbol: '@');
+    formatted = tnd.format(amount);
+    expect(formatted, digitsCheck[3]);
+  });
+}
+
+void testAgainstIcu(locale, List<NumberFormat> testFormats, list) {
+  test("Test against ICU data for $locale", () {
+    for (var format in testFormats) {
+      var formatted = format.format(123);
+      var negative = format.format(-12.3);
+      var large = format.format(1234567890);
+      var expected = (list..moveNext()).current;
+      expect(formatted, expected);
+      var expectedNegative = (list..moveNext()).current;
+      // Some of these results from CLDR have a leading LTR/RTL indicator,
+      // which we don't want. We also treat the difference between Unicode
+      // minus sign (2212) and hyphen-minus (45) as not significant.
+      expectedNegative = expectedNegative
+          .replaceAll("\u200e", "")
+          .replaceAll("\u200f", "")
+          .replaceAll("\u2212", "-");
+      expect(negative, expectedNegative);
+      var expectedLarge = (list..moveNext()).current;
+      expect(large, expectedLarge);
+      var readBack = format.parse(formatted);
+      expect(readBack, 123);
+      var readBackNegative = format.parse(negative);
+      expect(readBackNegative, -12.3);
+      var readBackLarge = format.parse(large);
+      expect(readBackLarge, 1234567890);
+    }
+  });
 }