From 3c97d2a2337e9dd82bca59000dffdbbf581e99f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Bustarret?= Date: Thu, 11 Dec 2014 11:42:25 +0100 Subject: [PATCH] Preliminary i18n support --- bigbytes.go | 29 ++++++++++++++++++++++++++--- comma.go | 40 ++++++++++++++++++++++++++++++++-------- english.go | 8 ++++++++ ftoa.go | 7 ++++++- humanize.go | 2 ++ ordinals.go | 9 ++++++++- si.go | 32 +++++++++++++++++++++++++++++--- times.go | 22 ++++++++++++++++++++-- 8 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 english.go diff --git a/bigbytes.go b/bigbytes.go index 007dc97..1fc4916 100644 --- a/bigbytes.go +++ b/bigbytes.go @@ -112,25 +112,39 @@ func humanateBigBytes(s, base *big.Int, sizes []string) string { // BigBytes produces a human readable representation of an SI size. // // BigBytes(82854982) -> 83MB -func BigBytes(s *big.Int) string { +func (h *BaseHumanizer) BigBytes(s *big.Int) string { sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} return humanateBigBytes(s, bigSIExp, sizes) } +// BigBytes produces a human readable representation of an SI size. +// +// BigBytes(82854982) -> 83MB +func BigBytes(s *big.Int) string { + return Default.BigBytes(s) +} + // BigIBytes produces a human readable representation of an IEC size. // // BigIBytes(82854982) -> 79MiB -func BigIBytes(s *big.Int) string { +func (h *BaseHumanizer) BigIBytes(s *big.Int) string { sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} return humanateBigBytes(s, bigIECExp, sizes) } +// BigIBytes produces a human readable representation of an IEC size. +// +// BigIBytes(82854982) -> 79MiB +func BigIBytes(s *big.Int) string { + return Default.BigIBytes(s) +} + // ParseBigBytes parses a string representation of bytes into the number // of bytes it represents. // // ParseBigBytes("42MB") -> 42000000, nil // ParseBigBytes("42mib") -> 44040192, nil -func ParseBigBytes(s string) (*big.Int, error) { +func (h *BaseHumanizer) ParseBigBytes(s string) (*big.Int, error) { lastDigit := 0 for _, r := range s { if !(unicode.IsDigit(r) || r == '.') { @@ -156,3 +170,12 @@ func ParseBigBytes(s string) (*big.Int, error) { return nil, fmt.Errorf("unhandled size name: %v", extra) } + +// ParseBigBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// ParseBigBytes("42MB") -> 42000000, nil +// ParseBigBytes("42mib") -> 44040192, nil +func ParseBigBytes(s string) (*big.Int, error) { + return Default.ParseBigBytes(s) +} diff --git a/comma.go b/comma.go index 9efc682..5a4c521 100644 --- a/comma.go +++ b/comma.go @@ -6,11 +6,7 @@ import ( "strings" ) -// Comma produces a string form of the given number in base 10 with -// commas after every three orders of magnitude. -// -// e.g. Comma(834142) -> 834,142 -func Comma(v int64) string { +func (h *BaseHumanizer) commaWithSeparator(v int64, separator string) string { sign := "" if v < 0 { sign = "-" @@ -32,12 +28,28 @@ func Comma(v int64) string { j-- } parts[j] = strconv.Itoa(int(v)) - return sign + strings.Join(parts[j:len(parts)], ",") + return sign + strings.Join(parts[j:len(parts)], separator) +} + +// Comma produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Comma(834142) -> 834,142 +func (h *EnglishHumanizer) Comma(v int64) string { + return h.commaWithSeparator(v, ",") +} + +// Comma produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Comma(834142) -> 834,142 +func Comma(v int64) string { + return Default.Comma(v) } // BigComma produces a string form of the given big.Int in base 10 // with commas after every three orders of magnitude. -func BigComma(b *big.Int) string { +func (h *BaseHumanizer) bigCommaWithSeparator(b *big.Int, separator string) string { sign := "" if b.Sign() < 0 { sign = "-" @@ -63,5 +75,17 @@ func BigComma(b *big.Int) string { j-- } parts[j] = strconv.Itoa(int(b.Int64())) - return sign + strings.Join(parts[j:len(parts)], ",") + return sign + strings.Join(parts[j:len(parts)], separator) +} + +// BigComma produces a string form of the given big.Int in base 10 +// with commas after every three orders of magnitude. +func (h *EnglishHumanizer) BigComma(b *big.Int) string { + return h.bigCommaWithSeparator(b, ",") +} + +// BigComma produces a string form of the given big.Int in base 10 +// with commas after every three orders of magnitude. +func BigComma(b *big.Int) string { + return Default.BigComma(b) } diff --git a/english.go b/english.go new file mode 100644 index 0000000..4f17437 --- /dev/null +++ b/english.go @@ -0,0 +1,8 @@ +package humanize + +type EnglishHumanizer struct { + BaseHumanizer +} + +var English EnglishHumanizer +var Default = English diff --git a/ftoa.go b/ftoa.go index c76190b..bdba5f4 100644 --- a/ftoa.go +++ b/ftoa.go @@ -18,6 +18,11 @@ func stripTrailingZeros(s string) string { } // Ftoa converts a float to a string with no trailing zeros. -func Ftoa(num float64) string { +func (h *BaseHumanizer) Ftoa(num float64) string { return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) } + +// Ftoa converts a float to a string with no trailing zeros. +func Ftoa(num float64) string { + return Default.Ftoa(num) +} diff --git a/humanize.go b/humanize.go index 74142c2..9f2f471 100644 --- a/humanize.go +++ b/humanize.go @@ -6,3 +6,5 @@ representing sizes like 82854982 into useful strings like, "83MB" or "79MiB" (whichever you prefer). */ package humanize + +type BaseHumanizer struct{} diff --git a/ordinals.go b/ordinals.go index 43d88a8..434649f 100644 --- a/ordinals.go +++ b/ordinals.go @@ -5,7 +5,7 @@ import "strconv" // Ordinal gives you the input number in a rank/ordinal format. // // Ordinal(3) -> 3rd -func Ordinal(x int) string { +func (h *EnglishHumanizer) Ordinal(x int) string { suffix := "th" switch x % 10 { case 1: @@ -23,3 +23,10 @@ func Ordinal(x int) string { } return strconv.Itoa(x) + suffix } + +// Ordinal gives you the input number in a rank/ordinal format. +// +// Ordinal(3) -> 3rd +func Ordinal(x int) string { + return Default.Ordinal(x) +} diff --git a/si.go b/si.go index e4cdcea..6b8500c 100644 --- a/si.go +++ b/si.go @@ -55,7 +55,7 @@ func init() { // that prefix. // // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") -func ComputeSI(input float64) (float64, string) { +func (h *BaseHumanizer) ComputeSI(input float64) (float64, string) { if input == 0 { return 0, "" } @@ -75,23 +75,42 @@ func ComputeSI(input float64) (float64, string) { return value, prefix } +// ComputeSI finds the most appropriate SI prefix for the given number +// and returns the prefix along with the value adjusted to be within +// that prefix. +// +// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") +func ComputeSI(input float64) (float64, string) { + return Default.ComputeSI(input) +} + // SI returns a string with default formatting. // // SI uses Ftoa to format float value, removing trailing zeros. // // e.g. SI(1000000, B) -> 1MB // e.g. SI(2.2345e-12, "F") -> 2.2345pF -func SI(input float64, unit string) string { +func (h *BaseHumanizer) SI(input float64, unit string) string { value, prefix := ComputeSI(input) return Ftoa(value) + prefix + unit } +// SI returns a string with default formatting. +// +// SI uses Ftoa to format float value, removing trailing zeros. +// +// e.g. SI(1000000, B) -> 1MB +// e.g. SI(2.2345e-12, "F") -> 2.2345pF +func SI(input float64, unit string) string { + return Default.SI(input, unit) +} + var errInvalid = errors.New("invalid input") // ParseSI parses an SI string back into the number and unit. // // e.g. ParseSI(2.2345pF) -> (2.2345e-12, "F", nil) -func ParseSI(input string) (float64, string, error) { +func (h *BaseHumanizer) ParseSI(input string) (float64, string, error) { found := riParseRegex.FindStringSubmatch(input) if len(found) != 4 { return 0, "", errInvalid @@ -102,3 +121,10 @@ func ParseSI(input string) (float64, string, error) { base, err := strconv.ParseFloat(found[1], 64) return base * mag, unit, err } + +// ParseSI parses an SI string back into the number and unit. +// +// e.g. ParseSI(2.2345pF) -> (2.2345e-12, "F", nil) +func ParseSI(input string) (float64, string, error) { + return Default.ParseSI(input) +} diff --git a/times.go b/times.go index 592ebe1..d7ca734 100644 --- a/times.go +++ b/times.go @@ -21,10 +21,17 @@ const ( // Time formats a time into a relative string. // // Time(someT) -> "3 weeks ago" -func Time(then time.Time) string { +func (h *EnglishHumanizer) Time(then time.Time) string { return RelTime(then, time.Now(), "ago", "from now") } +// Time formats a time into a relative string. +// +// Time(someT) -> "3 weeks ago" +func Time(then time.Time) string { + return Default.Time(then) +} + var magnitudes = []struct { d int64 format string @@ -56,7 +63,7 @@ var magnitudes = []struct { // the label corresponding to the smaller time is applied. // // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" -func RelTime(a, b time.Time, albl, blbl string) string { +func (h *EnglishHumanizer) RelTime(a, b time.Time, albl, blbl string) string { lbl := albl diff := b.Unix() - a.Unix() @@ -89,3 +96,14 @@ func RelTime(a, b time.Time, albl, blbl string) string { } return fmt.Sprintf(mag.format, args...) } + +// RelTime formats a time into a relative string. +// +// It takes two times and two labels. In addition to the generic time +// delta string (e.g. 5 minutes), the labels are used applied so that +// the label corresponding to the smaller time is applied. +// +// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" +func RelTime(a, b time.Time, albl, blbl string) string { + return Default.RelTime(a, b, albl, blbl) +}