diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-07-28 12:28:52 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-07-29 16:40:06 +0200 |
commit | 7907d24ba16fc5a80930c1aabf5144e684ff7f29 (patch) | |
tree | 7f4a9500a0167f4ea5b9c41a1113288ffb8f1c95 /tpl | |
parent | 726fe9c3c97a9c979dc7862e7f226fc5ec1341de (diff) |
tpl/lang: Add new localized versions of lang.FormatNumber etc.
Fixes #8820
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/cast/docshelper.go | 5 | ||||
-rw-r--r-- | tpl/lang/init.go | 44 | ||||
-rw-r--r-- | tpl/lang/init_test.go | 7 | ||||
-rw-r--r-- | tpl/lang/lang.go | 88 | ||||
-rw-r--r-- | tpl/lang/lang_test.go | 53 | ||||
-rw-r--r-- | tpl/time/time_test.go | 6 |
6 files changed, 181 insertions, 22 deletions
diff --git a/tpl/cast/docshelper.go b/tpl/cast/docshelper.go index a3cc26de5..9a5d55b3d 100644 --- a/tpl/cast/docshelper.go +++ b/tpl/cast/docshelper.go @@ -18,6 +18,7 @@ import ( "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/docshelper" + "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/tpl/internal" ) @@ -25,10 +26,12 @@ import ( // This file provides documentation support and is randomly put into this package. func init() { docsProvider := func() docshelper.DocProvider { + cfg := config.New() d := &deps.Deps{ - Cfg: config.New(), + Cfg: cfg, Log: loggers.NewErrorLogger(), BuildStartListeners: &deps.Listeners{}, + Language: langs.NewDefaultLanguage(cfg), Site: page.NewDummyHugoSite(newTestConfig()), } diff --git a/tpl/lang/init.go b/tpl/lang/init.go index 520eccb88..beb148ff6 100644 --- a/tpl/lang/init.go +++ b/tpl/lang/init.go @@ -15,6 +15,7 @@ package lang import ( "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/tpl/internal" ) @@ -22,7 +23,7 @@ const name = "lang" func init() { f := func(d *deps.Deps) *internal.TemplateFuncsNamespace { - ctx := New(d) + ctx := New(d, langs.GetTranslator(d.Language)) ns := &internal.TemplateFuncsNamespace{ Name: name, @@ -34,16 +35,45 @@ func init() { [][2]string{}, ) - ns.AddMethodMapping(ctx.NumFmt, + ns.AddMethodMapping(ctx.FormatNumber, nil, [][2]string{ - {`{{ lang.NumFmt 2 12345.6789 }}`, `12,345.68`}, - {`{{ lang.NumFmt 2 12345.6789 "- , ." }}`, `12.345,68`}, - {`{{ lang.NumFmt 6 -12345.6789 "- ." }}`, `-12345.678900`}, - {`{{ lang.NumFmt 0 -12345.6789 "- . ," }}`, `-12,346`}, - {`{{ -98765.4321 | lang.NumFmt 2 }}`, `-98,765.43`}, + {`{{ 512.5032 | lang.FormatNumber 2 }}`, `512.50`}, }, ) + + ns.AddMethodMapping(ctx.FormatPercent, + nil, + [][2]string{ + {`{{ 512.5032 | lang.FormatPercent 2 }}`, `512.50%`}, + }, + ) + + ns.AddMethodMapping(ctx.FormatCurrency, + nil, + [][2]string{ + {`{{ 512.5032 | lang.FormatCurrency 2 "USD" }}`, `$512.50`}, + }, + ) + + ns.AddMethodMapping(ctx.FormatAccounting, + nil, + [][2]string{ + {`{{ 512.5032 | lang.FormatAccounting 2 "NOK" }}`, `NOK512.50`}, + }, + ) + + ns.AddMethodMapping(ctx.FormatNumberCustom, + nil, + [][2]string{ + {`{{ lang.FormatNumberCustom 2 12345.6789 }}`, `12,345.68`}, + {`{{ lang.FormatNumberCustom 2 12345.6789 "- , ." }}`, `12.345,68`}, + {`{{ lang.FormatNumberCustom 6 -12345.6789 "- ." }}`, `-12345.678900`}, + {`{{ lang.FormatNumberCustom 0 -12345.6789 "- . ," }}`, `-12,346`}, + {`{{ -98765.4321 | lang.FormatNumberCustom 2 }}`, `-98,765.43`}, + }, + ) + return ns } diff --git a/tpl/lang/init_test.go b/tpl/lang/init_test.go index 82def5523..61d7b5047 100644 --- a/tpl/lang/init_test.go +++ b/tpl/lang/init_test.go @@ -16,6 +16,9 @@ package lang import ( "testing" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/langs" + "github.com/gohugoio/hugo/htesting/hqt" qt "github.com/frankban/quicktest" @@ -29,7 +32,9 @@ func TestInit(t *testing.T) { var ns *internal.TemplateFuncsNamespace for _, nsf := range internal.TemplateFuncsNamespaceRegistry { - ns = nsf(&deps.Deps{}) + ns = nsf(&deps.Deps{ + Language: langs.NewDefaultLanguage(config.New()), + }) if ns.Name == name { found = true break diff --git a/tpl/lang/lang.go b/tpl/lang/lang.go index 4e6c9c70a..0cf448caa 100644 --- a/tpl/lang/lang.go +++ b/tpl/lang/lang.go @@ -20,6 +20,8 @@ import ( "strconv" "strings" + translators "github.com/bep/gotranslators" + "github.com/go-playground/locales" "github.com/pkg/errors" "github.com/gohugoio/hugo/deps" @@ -27,15 +29,17 @@ import ( ) // New returns a new instance of the lang-namespaced template functions. -func New(deps *deps.Deps) *Namespace { +func New(deps *deps.Deps, translator locales.Translator) *Namespace { return &Namespace{ - deps: deps, + translator: translator, + deps: deps, } } // Namespace provides template functions for the "lang" namespace. type Namespace struct { - deps *deps.Deps + translator locales.Translator + deps *deps.Deps } // Translate returns a translated string for id. @@ -57,14 +61,81 @@ func (ns *Namespace) Translate(id interface{}, args ...interface{}) (string, err return ns.deps.Translate(sid, templateData), nil } -// NumFmt formats a number with the given precision using the +// FormatNumber formats number with the given precision for the current language. +func (ns *Namespace) FormatNumber(precision, number interface{}) (string, error) { + p, n, err := ns.castPrecisionNumber(precision, number) + if err != nil { + return "", err + } + return ns.translator.FmtNumber(n, p), nil +} + +// FormatPercent formats number with the given precision for the current language. +// Note that the number is assumbed to be percent. +func (ns *Namespace) FormatPercent(precision, number interface{}) (string, error) { + p, n, err := ns.castPrecisionNumber(precision, number) + if err != nil { + return "", err + } + return ns.translator.FmtPercent(n, p), nil +} + +// FormatCurrency returns the currency reprecentation of number for the given currency and precision +// for the current language. +func (ns *Namespace) FormatCurrency(precision, currency, number interface{}) (string, error) { + p, n, err := ns.castPrecisionNumber(precision, number) + if err != nil { + return "", err + } + c := translators.GetCurrency(cast.ToString(currency)) + if c < 0 { + return "", fmt.Errorf("unknown currency code: %q", currency) + } + return ns.translator.FmtCurrency(n, p, c), nil +} + +// FormatAccounting returns the currency reprecentation of number for the given currency and precision +// for the current language in accounting notation. +func (ns *Namespace) FormatAccounting(precision, currency, number interface{}) (string, error) { + p, n, err := ns.castPrecisionNumber(precision, number) + if err != nil { + return "", err + } + c := translators.GetCurrency(cast.ToString(currency)) + if c < 0 { + return "", fmt.Errorf("unknown currency code: %q", currency) + } + return ns.translator.FmtAccounting(n, p, c), nil +} + +func (ns *Namespace) castPrecisionNumber(precision, number interface{}) (uint64, float64, error) { + p, err := cast.ToUint64E(precision) + if err != nil { + return 0, 0, err + } + + // Sanity check. + if p > 20 { + return 0, 0, fmt.Errorf("invalid precision: %d", precision) + } + + n, err := cast.ToFloat64E(number) + if err != nil { + return 0, 0, err + } + return p, n, nil +} + +// FormatNumberCustom formats a number with the given precision using the // negative, decimal, and grouping options. The `options` // parameter is a string consisting of `<negative> <decimal> <grouping>`. The // default `options` value is `- . ,`. // // Note that numbers are rounded up at 5 or greater. // So, with precision set to 0, 1.5 becomes `2`, and 1.4 becomes `1`. -func (ns *Namespace) NumFmt(precision, number interface{}, options ...interface{}) (string, error) { +// +// For a simpler function that adapts to the current language, see FormatNumberCustom. +func (ns *Namespace) FormatNumberCustom(precision, number interface{}, options ...interface{}) (string, error) { prec, err := cast.ToIntE(precision) if err != nil { return "", err @@ -162,6 +233,13 @@ func (ns *Namespace) NumFmt(precision, number interface{}, options ...interface{ return string(b), nil } +// NumFmt is deprecated, use FormatNumberCustom. +// We renamed this in Hugo 0.87. +// Deprecated: Use FormatNumberCustom +func (ns *Namespace) NumFmt(precision, number interface{}, options ...interface{}) (string, error) { + return ns.FormatNumberCustom(precision, number, options...) +} + type pagesLanguageMerger interface { MergeByLanguageInterface(other interface{}) (interface{}, error) } diff --git a/tpl/lang/lang_test.go b/tpl/lang/lang_test.go index 3b3caeb62..782a0a69a 100644 --- a/tpl/lang/lang_test.go +++ b/tpl/lang/lang_test.go @@ -3,15 +3,16 @@ package lang import ( "testing" + translators "github.com/bep/gotranslators" qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/deps" ) -func TestNumFormat(t *testing.T) { +func TestNumFmt(t *testing.T) { t.Parallel() c := qt.New(t) - ns := New(&deps.Deps{}) + ns := New(&deps.Deps{}, nil) cases := []struct { prec int @@ -49,12 +50,12 @@ func TestNumFormat(t *testing.T) { var err error if len(cas.runes) == 0 { - s, err = ns.NumFmt(cas.prec, cas.n) + s, err = ns.FormatNumberCustom(cas.prec, cas.n) } else { if cas.delim == "" { - s, err = ns.NumFmt(cas.prec, cas.n, cas.runes) + s, err = ns.FormatNumberCustom(cas.prec, cas.n, cas.runes) } else { - s, err = ns.NumFmt(cas.prec, cas.n, cas.runes, cas.delim) + s, err = ns.FormatNumberCustom(cas.prec, cas.n, cas.runes, cas.delim) } } @@ -62,3 +63,45 @@ func TestNumFormat(t *testing.T) { c.Assert(s, qt.Equals, cas.want) } } + +func TestFormatNumbers(t *testing.T) { + + c := qt.New(t) + + nsNn := New(&deps.Deps{}, translators.GetTranslator("nn")) + nsEn := New(&deps.Deps{}, translators.GetTranslator("en")) + pi := 3.14159265359 + + c.Run("FormatNumber", func(c *qt.C) { + c.Parallel() + got, err := nsNn.FormatNumber(3, pi) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, "3,142") + + got, err = nsEn.FormatNumber(3, pi) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, "3.142") + }) + + c.Run("FormatPercent", func(c *qt.C) { + c.Parallel() + got, err := nsEn.FormatPercent(3, 67.33333) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, "67.333%") + }) + + c.Run("FormatCurrency", func(c *qt.C) { + c.Parallel() + got, err := nsEn.FormatCurrency(2, "USD", 20000) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, "$20,000.00") + }) + + c.Run("FormatAccounting", func(c *qt.C) { + c.Parallel() + got, err := nsEn.FormatAccounting(2, "USD", 20000) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, "$20,000.00") + }) + +} diff --git a/tpl/time/time_test.go b/tpl/time/time_test.go index 71899cc65..22cbc9bcc 100644 --- a/tpl/time/time_test.go +++ b/tpl/time/time_test.go @@ -24,7 +24,7 @@ func TestTimeLocation(t *testing.T) { t.Parallel() loc, _ := time.LoadLocation("America/Antigua") - ns := New(translators.Get("en"), loc) + ns := New(translators.GetTranslator("en"), loc) for i, test := range []struct { value string @@ -67,7 +67,7 @@ func TestTimeLocation(t *testing.T) { func TestFormat(t *testing.T) { t.Parallel() - ns := New(translators.Get("en"), time.UTC) + ns := New(translators.GetTranslator("en"), time.UTC) for i, test := range []struct { layout string @@ -107,7 +107,7 @@ func TestFormat(t *testing.T) { func TestDuration(t *testing.T) { t.Parallel() - ns := New(translators.Get("en"), time.UTC) + ns := New(translators.GetTranslator("en"), time.UTC) for i, test := range []struct { unit interface{} |