number: add plural integration

also added consideration to use only
VisibleDigits interface instead of
plural.Interface.

Change-Id: Ibb2a9a0eddc3f6fe7689b7fa3268f2bf0c8a97dc
Reviewed-on: https://go-review.googlesource.com/60771
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/feature/plural/message.go b/feature/plural/message.go
index 22f5a26..f931f8a 100755
--- a/feature/plural/message.go
+++ b/feature/plural/message.go
@@ -16,6 +16,9 @@
 	"golang.org/x/text/message/catalog"
 )
 
+// TODO: consider deleting this interface. Maybe VisibleDigits is always
+// sufficient and practical.
+
 // Interface is used for types that can determine their own plural form.
 type Interface interface {
 	// PluralForm reports the plural form for the given language of the
@@ -193,6 +196,9 @@
 	n := -1
 	if arg := d.Arg(argN); arg == nil {
 		// Default to Other.
+	} else if x, ok := arg.(number.VisibleDigits); ok {
+		d := x.Digits(nil, lang, scale)
+		form, n = cardinal.matchDisplayDigits(lang, &d)
 	} else if x, ok := arg.(Interface); ok {
 		// This covers lists and formatters from the number package.
 		form, n = x.PluralForm(lang, scale)
diff --git a/feature/plural/message_test.go b/feature/plural/message_test.go
index 1a89bd5..b5bc47e 100644
--- a/feature/plural/message_test.go
+++ b/feature/plural/message_test.go
@@ -65,7 +65,7 @@
 		},
 	}, {
 		desc: "decimal without fractions",
-		msg:  Selectf(1, "%.of", "one", "foo", "other", "bar"),
+		msg:  Selectf(1, "%.0f", "one", "foo", "other", "bar"),
 		tests: []test{
 			// fractions are always plural in english
 			{arg: 0, result: "bar"},
diff --git a/internal/number/format.go b/internal/number/format.go
index 0a5ffb5..910bdeb 100755
--- a/internal/number/format.go
+++ b/internal/number/format.go
@@ -16,6 +16,13 @@
 // - allow user-defined superscript notation (such as <sup>4</sup>)
 // - same for non-breaking spaces, like &nbsp;
 
+// A VisibleDigits computes digits, comma placement and trailing zeros as they
+// will be shown to the user.
+type VisibleDigits interface {
+	Digits(buf []byte, t language.Tag, scale int) Digits
+	// TODO: Do we also need to add the verb or pass a format.State?
+}
+
 // Formatting proceeds along the following lines:
 // 0) Compose rounding information from format and context.
 // 1) Convert a number into a Decimal.
diff --git a/number/doc.go b/number/doc.go
index 97088d1..2ad8d43 100644
--- a/number/doc.go
+++ b/number/doc.go
@@ -22,7 +22,6 @@
 //    p.Printf("There are %v bikes per household.", number.Decimal(1.2))
 //    // Prints: Er zijn 1,2 fietsen per huishouden.
 //
-// Provided that the printed translation is available.
 //
 // The width and scale specified in the formatting directives override the
 // configuration of the formatter.
diff --git a/number/format.go b/number/format.go
index 62af651..1c3d41b 100755
--- a/number/format.go
+++ b/number/format.go
@@ -104,3 +104,19 @@
 	d.Convert(p.RoundingContext, f.value)
 	state.Write(p.Format(nil, &d))
 }
+
+// Digits returns information about which logical digits will be presented to
+// the user. This information is relevant, for instance, to determine plural
+// forms.
+func (f Formatter) Digits(buf []byte, tag language.Tag, scale int) number.Digits {
+	var p number.Formatter
+	f.initFunc(&p, tag)
+	if scale >= 0 {
+		// TODO: this only works well for decimal numbers, which is generally
+		// fine.
+		p.SetScale(scale)
+	}
+	var d number.Decimal
+	d.Convert(p.RoundingContext, f.value)
+	return number.FormatDigits(&d, p.RoundingContext)
+}
diff --git a/number/format_test.go b/number/format_test.go
index 30be72d..3c67c5d 100644
--- a/number/format_test.go
+++ b/number/format_test.go
@@ -5,8 +5,10 @@
 package number
 
 import (
+	"fmt"
 	"testing"
 
+	"golang.org/x/text/feature/plural"
 	"golang.org/x/text/language"
 	"golang.org/x/text/message"
 )
@@ -43,3 +45,63 @@
 		})
 	}
 }
+
+func TestDigits(t *testing.T) {
+	testCases := []struct {
+		f     Formatter
+		scale int
+		want  string
+	}{{
+		f:     Decimal(3),
+		scale: 0,
+		want:  "digits:[3] exp:1 comma:0 end:1",
+	}, {
+		f:     Decimal(3.1),
+		scale: 0,
+		want:  "digits:[3] exp:1 comma:0 end:1",
+	}, {
+		f:     Scientific(3.1),
+		scale: 0,
+		want:  "digits:[3] exp:1 comma:1 end:1",
+	}, {
+		f:     Scientific(3.1),
+		scale: 3,
+		want:  "digits:[3 1] exp:1 comma:1 end:4",
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			d := tc.f.Digits(nil, language.Croatian, tc.scale)
+			got := fmt.Sprintf("digits:%d exp:%d comma:%d end:%d", d.Digits, d.Exp, d.Comma, d.End)
+			if got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestPluralIntegration(t *testing.T) {
+	testCases := []struct {
+		f    Formatter
+		want string
+	}{{
+		f:    Decimal(1),
+		want: "one: 1",
+	}, {
+		f:    Decimal(5),
+		want: "other: 5",
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			message.Set(language.English, "num %f", plural.Selectf(1, "%f",
+				"one", "one: %f",
+				"other", "other: %f"))
+
+			p := message.NewPrinter(language.English)
+
+			got := p.Sprintf("num %f", tc.f)
+			if got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/number/number.go b/number/number.go
index 0a6e62f..f5ca93b 100755
--- a/number/number.go
+++ b/number/number.go
@@ -21,14 +21,14 @@
 	scientificVerbs = "veg"
 )
 
-// Decimal represents a number as a floating point decimal.
+// Decimal formats a number as a floating point decimal.
 func Decimal(x interface{}, opts ...Option) Formatter {
 	return newFormatter(decimalOptions, opts, x)
 }
 
 var decimalOptions = newOptions(decimalVerbs, (*number.Formatter).InitDecimal)
 
-// Scientific prints a values in scientific format.
+// Scientific formats a number in scientific format.
 func Scientific(x interface{}, opts ...Option) Formatter {
 	return newFormatter(scientificOptions, opts, x)
 }
diff --git a/number/number_test.go b/number/number_test.go
index f380b7e..96b1acb 100644
--- a/number/number_test.go
+++ b/number/number_test.go
@@ -80,11 +80,11 @@
 		want: "       123",
 	}, {
 		desc: "format width pad option before",
-		f:    Decimal(123, PadRune('*'), FormatWidth(10)),
+		f:    Decimal(123, Pad('*'), FormatWidth(10)),
 		want: "*******123",
 	}, {
 		desc: "format width pad option after",
-		f:    Decimal(123, FormatWidth(10), PadRune('*')),
+		f:    Decimal(123, FormatWidth(10), Pad('*')),
 		want: "*******123",
 	}, {
 		desc: "format width illegal",
diff --git a/number/option.go b/number/option.go
index 2e73525..de96f8e 100644
--- a/number/option.go
+++ b/number/option.go
@@ -48,7 +48,7 @@
 	}
 }
 
-// MaxFractionDigits specifies the maximum number of digits after the comma.
+// MaxFractionDigits specifies the maximum number of fractional digits.
 func MaxFractionDigits(max int) Option {
 	return func(t language.Tag, f *number.Formatter) {
 		if max >= 1<<15 {
@@ -58,7 +58,7 @@
 	}
 }
 
-// MinFractionDigits specifies the minimum number of digits after the comma.
+// MinFractionDigits specifies the minimum number of fractional digits.
 func MinFractionDigits(min int) Option {
 	return func(t language.Tag, f *number.Formatter) {
 		if min >= 1<<8 {
@@ -161,8 +161,8 @@
 	}
 }
 
-// PadRune sets the rune to be used for filling up to the format width.
-func PadRune(r rune) Option {
+// Pad sets the rune to be used for filling up to the format width.
+func Pad(r rune) Option {
 	return func(t language.Tag, f *number.Formatter) {
 		f.PadRune = r
 	}