text/currency: format currency amount according to the locale

Fixes golang/go#47623

Change-Id: Ie6be9db93bf58f597f1ea4d864fcb507235b1018
GitHub-Last-Rev: 4c8f3557daf5440390c0775ed6e71ec80f8c11e8
GitHub-Pull-Request: golang/text#27
Reviewed-on: https://go-review.googlesource.com/c/text/+/353935
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Trust: Marcel van Lohuizen <mpvl@golang.org>
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/currency/format.go b/currency/format.go
index 1115263..cc4570d 100644
--- a/currency/format.go
+++ b/currency/format.go
@@ -6,11 +6,13 @@
 
 import (
 	"fmt"
-	"io"
 	"sort"
 
 	"golang.org/x/text/internal/format"
 	"golang.org/x/text/internal/language/compact"
+	"golang.org/x/text/internal/number"
+
+	"golang.org/x/text/language"
 )
 
 // Amount is an amount-currency unit pair.
@@ -34,8 +36,6 @@
 //
 // Add/Sub/Div/Mul/Round.
 
-var space = []byte(" ")
-
 // Format implements fmt.Formatter. It accepts format.State for
 // language-specific rendering.
 func (a Amount) Format(s fmt.State, verb rune) {
@@ -58,9 +58,11 @@
 // Format implements fmt.Formatter. It accepts format.State for
 // language-specific rendering.
 func (v formattedValue) Format(s fmt.State, verb rune) {
+	var tag language.Tag
 	var lang compact.ID
 	if state, ok := s.(format.State); ok {
-		lang, _ = compact.RegionalID(compact.Tag(state.Language()))
+		tag = state.Language()
+		lang, _ = compact.RegionalID(compact.Tag(tag))
 	}
 
 	// Get the options. Use DefaultFormat if not present.
@@ -73,18 +75,22 @@
 		cur = opt.currency
 	}
 
-	// TODO: use pattern.
-	io.WriteString(s, opt.symbol(lang, cur))
+	sym := opt.symbol(lang, cur)
 	if v.amount != nil {
-		s.Write(space)
+		var f number.Formatter
+		f.InitDecimal(tag)
 
-		// TODO: apply currency-specific rounding
-		scale, _ := opt.kind.Rounding(cur)
-		if _, ok := s.Precision(); !ok {
-			fmt.Fprintf(s, "%.*f", scale, v.amount)
-		} else {
-			fmt.Fprint(s, v.amount)
-		}
+		scale, increment := opt.kind.Rounding(cur)
+		f.RoundingContext.SetScale(scale)
+		f.RoundingContext.Increment = uint32(increment)
+		f.RoundingContext.IncrementScale = uint8(scale)
+		f.RoundingContext.Mode = number.ToNearestAway
+
+		d := f.Append(nil, v.amount)
+
+		fmt.Fprint(s, sym, " ", string(d))
+	} else {
+		fmt.Fprint(s, sym)
 	}
 }
 
diff --git a/currency/format_test.go b/currency/format_test.go
index 0aa0d58..5cb11eb 100644
--- a/currency/format_test.go
+++ b/currency/format_test.go
@@ -12,8 +12,10 @@
 )
 
 var (
+	de    = language.German
 	en    = language.English
 	fr    = language.French
+	de_CH = language.MustParse("de-CH")
 	en_US = language.AmericanEnglish
 	en_GB = language.BritishEnglish
 	en_AU = language.MustParse("en-AU")
@@ -42,20 +44,35 @@
 
 		9:  {en, 9.0, Symbol.Default(EUR), "€ 9.00"},
 		10: {en, 10.123, Symbol.Default(KRW), "₩ 10"},
-		11: {fr, 11.52, Symbol.Default(TWD), "TWD 11.52"},
+		11: {fr, 11.52, Symbol.Default(TWD), "TWD 11,52"},
 		12: {en, 12.123, Symbol.Default(czk), "CZK 12.12"},
 		13: {en, 13.123, Symbol.Default(czk).Kind(Cash), "CZK 13"},
 		14: {en, 14.12345, ISO.Default(MustParseISO("CLF")), "CLF 14.1235"},
 		15: {en, USD.Amount(15.00), ISO.Default(TWD), "USD 15.00"},
 		16: {en, KRW.Amount(16.00), ISO.Kind(Cash), "KRW 16"},
 
-		// TODO: support integers as well.
-
 		17: {en, USD, nil, "USD"},
 		18: {en, USD, ISO, "USD"},
 		19: {en, USD, Symbol, "$"},
 		20: {en_GB, USD, Symbol, "US$"},
 		21: {en_AU, USD, NarrowSymbol, "$"},
+
+		// https://en.wikipedia.org/wiki/Decimal_separator
+		22: {de, EUR.Amount(1234567.89), nil, "EUR 1.234.567,89"},
+		23: {fr, EUR.Amount(1234567.89), nil, "EUR 1\u00a0234\u00a0567,89"},
+		24: {en_AU, EUR.Amount(1234567.89), nil, "EUR 1,234,567.89"},
+		25: {de_CH, EUR.Amount(1234567.89), nil, "EUR 1’234’567.89"},
+
+		// https://en.wikipedia.org/wiki/Cash_rounding
+		26: {de, NOK.Amount(2.49), ISO.Kind(Cash), "NOK 2"},
+		27: {de, NOK.Amount(2.50), ISO.Kind(Cash), "NOK 3"},
+		28: {de, DKK.Amount(0.24), ISO.Kind(Cash), "DKK 0,00"},
+		29: {de, DKK.Amount(0.25), ISO.Kind(Cash), "DKK 0,50"},
+
+		// integers
+		30: {de, EUR.Amount(1234567), nil, "EUR 1.234.567,00"},
+		31: {en, CNY.Amount(0), NarrowSymbol, "¥ 0.00"},
+		32: {en, CNY.Amount(0), Symbol, "CN¥ 0.00"},
 	}
 	for i, tc := range testCases {
 		p := message.NewPrinter(tc.tag)