-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
provides more styles and more control over formatting than fmt. Change-Id: Ib9d2c27db14804af911556e3caa8b35fddd32fce Reviewed-on: https://go-review.googlesource.com/59812 Run-TryBot: Marcel van Lohuizen <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Nigel Tao <[email protected]>
- Loading branch information
Showing
7 changed files
with
652 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Package number formats numbers according to the customs of different locales. | ||
// | ||
// The number formats of this package allow for greater formatting flexibility | ||
// than passing values to message.Printf calls as is. It currently supports the | ||
// builtin Go types and anything that implements the Convert interface | ||
// (currently internal). | ||
// | ||
// p := message.NewPrinter(language.English) | ||
// | ||
// p.Printf("%v bottles of beer on the wall.", number.Decimal(1234)) | ||
// // Prints: 1,234 bottles of beer on the wall. | ||
// | ||
// p.Printf("%v of gophers lose too much fur", number.Percent(0.12)) | ||
// // Prints: 12% of gophers lose too much fur. | ||
// | ||
// p := message.NewPrinter(language.Dutch) | ||
// | ||
// 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. | ||
package number |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package number_test | ||
|
||
import ( | ||
"golang.org/x/text/language" | ||
"golang.org/x/text/message" | ||
"golang.org/x/text/number" | ||
) | ||
|
||
func ExampleMaxIntegerDigits() { | ||
const year = 1999 | ||
p := message.NewPrinter(language.English) | ||
p.Println("Year:", number.Decimal(year, number.MaxIntegerDigits(2))) | ||
|
||
// Output: | ||
// Year: 99 | ||
} | ||
|
||
func ExampleIncrementString() { | ||
p := message.NewPrinter(language.English) | ||
|
||
p.Println(number.Decimal(1.33, number.IncrementString("0.50"))) | ||
|
||
// Output: 1.50 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package number | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"golang.org/x/text/feature/plural" | ||
"golang.org/x/text/internal/format" | ||
"golang.org/x/text/internal/number" | ||
"golang.org/x/text/language" | ||
) | ||
|
||
// A FormatFunc formates a number. | ||
type FormatFunc func(x interface{}, opts ...Option) Formatter | ||
|
||
// NewFormat creates a FormatFunc based on another FormatFunc and new options. | ||
// Use NewFormat to cash the creation of formatters. | ||
func NewFormat(format FormatFunc, opts ...Option) FormatFunc { | ||
o := *format(nil).options | ||
n := len(o.options) | ||
o.options = append(o.options[:n:n], opts...) | ||
return func(x interface{}, opts ...Option) Formatter { | ||
return newFormatter(&o, opts, x) | ||
} | ||
} | ||
|
||
type options struct { | ||
verbs string | ||
initFunc initFunc | ||
options []Option | ||
pluralFunc func(t language.Tag, scale int) (f plural.Form, n int) | ||
} | ||
|
||
type optionFlag uint16 | ||
|
||
const ( | ||
hasScale optionFlag = 1 << iota | ||
hasPrecision | ||
noSeparator | ||
exact | ||
) | ||
|
||
type initFunc func(f *number.Formatter, t language.Tag) | ||
|
||
func newFormatter(o *options, opts []Option, value interface{}) Formatter { | ||
if len(opts) > 0 { | ||
n := *o | ||
n.options = opts | ||
o = &n | ||
} | ||
return Formatter{o, value} | ||
} | ||
|
||
func newOptions(verbs string, f initFunc) *options { | ||
return &options{verbs: verbs, initFunc: f} | ||
} | ||
|
||
type Formatter struct { | ||
*options | ||
value interface{} | ||
} | ||
|
||
// Format implements format.Formatter. It is for internal use only for now. | ||
func (f Formatter) Format(state format.State, verb rune) { | ||
// TODO: consider implementing fmt.Formatter instead and using the following | ||
// piece of code. This allows numbers to be rendered mostly as expected | ||
// when using fmt. But it may get weird with the spellout options and we | ||
// may need more of format.State over time. | ||
// lang := language.Und | ||
// if s, ok := state.(format.State); ok { | ||
// lang = s.Language() | ||
// } | ||
|
||
lang := state.Language() | ||
if !strings.Contains(f.verbs, string(verb)) { | ||
fmt.Fprintf(state, "%%!%s(%T=%v)", string(verb), f.value, f.value) | ||
return | ||
} | ||
var p number.Formatter | ||
f.initFunc(&p, lang) | ||
for _, o := range f.options.options { | ||
o(lang, &p) | ||
} | ||
if w, ok := state.Width(); ok { | ||
p.FormatWidth = uint16(w) | ||
} | ||
if prec, ok := state.Precision(); ok { | ||
switch verb { | ||
case 'd': | ||
p.SetScale(0) | ||
case 'f': | ||
p.SetScale(prec) | ||
case 'e': | ||
p.SetPrecision(prec + 1) | ||
case 'g': | ||
p.SetPrecision(prec) | ||
} | ||
} | ||
var d number.Decimal | ||
d.Convert(p.RoundingContext, f.value) | ||
state.Write(p.Format(nil, &d)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package number | ||
|
||
import ( | ||
"testing" | ||
|
||
"golang.org/x/text/language" | ||
"golang.org/x/text/message" | ||
) | ||
|
||
func TestWrongVerb(t *testing.T) { | ||
testCases := []struct { | ||
f Formatter | ||
fmt string | ||
want string | ||
}{{ | ||
f: Decimal(12), | ||
fmt: "%e", | ||
want: "%!e(int=12)", | ||
}, { | ||
f: Scientific(12), | ||
fmt: "%f", | ||
want: "%!f(int=12)", | ||
}, { | ||
f: Engineering(12), | ||
fmt: "%f", | ||
want: "%!f(int=12)", | ||
}, { | ||
f: Percent(12), | ||
fmt: "%e", | ||
want: "%!e(int=12)", | ||
}} | ||
for _, tc := range testCases { | ||
t.Run("", func(t *testing.T) { | ||
tag := language.Und | ||
got := message.NewPrinter(tag).Sprintf(tc.fmt, tc.f) | ||
if got != tc.want { | ||
t.Errorf("got %q; want %q", got, tc.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package number | ||
|
||
// TODO: | ||
// p.Printf("The gauge was at %v.", number.Spell(number.Percent(23))) | ||
// // Prints: The gauge was at twenty-three percent. | ||
// | ||
// p.Printf("From here to %v!", number.Spell(math.Inf())) | ||
// // Prints: From here to infinity! | ||
// | ||
|
||
import ( | ||
"golang.org/x/text/internal/number" | ||
) | ||
|
||
const ( | ||
decimalVerbs = "vfgd" | ||
scientificVerbs = "veg" | ||
) | ||
|
||
// Decimal represents 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. | ||
func Scientific(x interface{}, opts ...Option) Formatter { | ||
return newFormatter(scientificOptions, opts, x) | ||
} | ||
|
||
var scientificOptions = newOptions(scientificVerbs, (*number.Formatter).InitScientific) | ||
|
||
// Engineering formats a number using engineering notation, which is like | ||
// scientific notation, but with the exponent normalized to multiples of 3. | ||
func Engineering(x interface{}, opts ...Option) Formatter { | ||
return newFormatter(engineeringOptions, opts, x) | ||
} | ||
|
||
var engineeringOptions = newOptions(scientificVerbs, (*number.Formatter).InitEngineering) | ||
|
||
// Percent formats a number as a percentage. A value of 1.0 means 100%. | ||
func Percent(x interface{}, opts ...Option) Formatter { | ||
return newFormatter(percentOptions, opts, x) | ||
} | ||
|
||
var percentOptions = newOptions(decimalVerbs, (*number.Formatter).InitPercent) | ||
|
||
// PerMille formats a number as a per mille indication. A value of 1.0 means | ||
// 1000‰. | ||
func PerMille(x interface{}, opts ...Option) Formatter { | ||
return newFormatter(perMilleOptions, opts, x) | ||
} | ||
|
||
var perMilleOptions = newOptions(decimalVerbs, (*number.Formatter).InitPerMille) | ||
|
||
// TODO: | ||
// - Shortest: akin to verb 'g' of 'G' | ||
// | ||
// TODO: RBNF forms: | ||
// - Compact: 1M 3.5T | ||
// - CompactBinary: 1Mi 3.5Ti | ||
// - Long: 1 million | ||
// - Ordinal: | ||
// - Roman: MCMIIXX | ||
// - RomanSmall: mcmiixx | ||
// - Text: numbers as it typically appears in running text, allowing | ||
// language-specific choices for when to use numbers and when to use words. | ||
// - Spell?: spelled-out number. Maybe just allow as an option? | ||
|
||
// NOTE: both spelled-out numbers and ordinals, to render correctly, need | ||
// detailed linguistic information from the translated string into which they | ||
// are substituted. We will need to implement that first. |
Oops, something went wrong.