summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2021-07-28 12:28:52 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2021-07-29 16:40:06 +0200
commit7907d24ba16fc5a80930c1aabf5144e684ff7f29 (patch)
tree7f4a9500a0167f4ea5b9c41a1113288ffb8f1c95
parent726fe9c3c97a9c979dc7862e7f226fc5ec1341de (diff)
tpl/lang: Add new localized versions of lang.FormatNumber etc.
Fixes #8820
-rw-r--r--common/htime/time_test.go12
-rw-r--r--docs/content/en/content-management/image-processing/index.md2
-rw-r--r--docs/content/en/functions/lang.md (renamed from docs/content/en/functions/NumFmt.md)21
-rw-r--r--docs/content/en/functions/time.md1
-rw-r--r--docs/data/docs.json92
-rw-r--r--docs/layouts/template-func/page.html54
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--hugolib/language_test.go49
-rw-r--r--langs/language.go6
-rw-r--r--tpl/cast/docshelper.go5
-rw-r--r--tpl/lang/init.go44
-rw-r--r--tpl/lang/init_test.go7
-rw-r--r--tpl/lang/lang.go88
-rw-r--r--tpl/lang/lang_test.go53
-rw-r--r--tpl/time/time_test.go6
16 files changed, 385 insertions, 59 deletions
diff --git a/common/htime/time_test.go b/common/htime/time_test.go
index 383027755..e8aec0153 100644
--- a/common/htime/time_test.go
+++ b/common/htime/time_test.go
@@ -28,7 +28,7 @@ func TestTimeFormatter(t *testing.T) {
june06 = june06.Add(7777 * time.Second)
c.Run("Norsk nynorsk", func(c *qt.C) {
- f := NewTimeFormatter(translators.Get("nn"))
+ f := NewTimeFormatter(translators.GetTranslator("nn"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "onsdag juni 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "on. juni 6 2018")
@@ -36,7 +36,7 @@ func TestTimeFormatter(t *testing.T) {
})
c.Run("Custom layouts Norsk nynorsk", func(c *qt.C) {
- f := NewTimeFormatter(translators.Get("nn"))
+ f := NewTimeFormatter(translators.GetTranslator("nn"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "onsdag 6. juni 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "6. juni 2018")
@@ -51,7 +51,7 @@ func TestTimeFormatter(t *testing.T) {
})
c.Run("Custom layouts English", func(c *qt.C) {
- f := NewTimeFormatter(translators.Get("en"))
+ f := NewTimeFormatter(translators.GetTranslator("en"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "Wednesday, June 6, 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "June 6, 2018")
@@ -66,7 +66,7 @@ func TestTimeFormatter(t *testing.T) {
})
c.Run("English", func(c *qt.C) {
- f := NewTimeFormatter(translators.Get("en"))
+ f := NewTimeFormatter(translators.GetTranslator("en"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "Wednesday Jun 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "Wed June 6 2018")
@@ -88,7 +88,7 @@ func BenchmarkTimeFormatter(b *testing.B) {
})
b.Run("Localized", func(b *testing.B) {
- f := NewTimeFormatter(translators.Get("nn"))
+ f := NewTimeFormatter(translators.GetTranslator("nn"))
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := f.Format(june06, "Monday Jan 2 2006")
@@ -99,7 +99,7 @@ func BenchmarkTimeFormatter(b *testing.B) {
})
b.Run("Localized Custom", func(b *testing.B) {
- f := NewTimeFormatter(translators.Get("nn"))
+ f := NewTimeFormatter(translators.GetTranslator("nn"))
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := f.Format(june06, ":date_medium")
diff --git a/docs/content/en/content-management/image-processing/index.md b/docs/content/en/content-management/image-processing/index.md
index 76679717e..e2e964154 100644
--- a/docs/content/en/content-management/image-processing/index.md
+++ b/docs/content/en/content-management/image-processing/index.md
@@ -134,7 +134,7 @@ Or individually access EXIF data with dot access, e.g.:
{{ end }}
```
-Some fields may need to be formatted with [`lang.NumFmt`]({{< relref "functions/numfmt" >}}) function to prevent display like `Aperture: 2.278934289` instead of `Aperture: 2.28`.
+Some fields may need to be formatted with [`lang.FormatNumberCustom`]({{< relref "functions/lang" >}}) function to prevent display like `Aperture: 2.278934289` instead of `Aperture: 2.28`.
#### Exif fields
diff --git a/docs/content/en/functions/NumFmt.md b/docs/content/en/functions/lang.md
index 9b51f597f..7b810c9be 100644
--- a/docs/content/en/functions/NumFmt.md
+++ b/docs/content/en/functions/lang.md
@@ -1,10 +1,8 @@
---
-title: lang.NumFmt
-description: "Formats a number with a given precision using the requested `negative`, `decimal`, and `grouping` options. The `options` parameter is a string consisting of `<negative> <decimal> <grouping>`."
-godocref: ""
-date: 2017-02-01
-publishdate: 2017-02-01
-lastmod: 2017-08-21
+title: lang
+package: lang
+description: "TODO.."
+date: 2021-07-28
categories: [functions]
keywords: [numbers]
menu:
@@ -12,18 +10,13 @@ menu:
parent: "functions"
toc: false
signature: ["lang.NumFmt PRECISION NUMBER [OPTIONS [DELIMITER]]"]
-workson: []
-hugoversion:
-relatedfuncs: []
-deprecated: false
-draft: false
-aliases: []
-comments:
+aliases: ['/functions/numfmt/']
+type: 'template-func'
---
The default options value is `- . ,`. The default delimiter within the options
value is a space. If you need to use a space as one of the options, set a
-custom delimiter.
+custom delimiter.s
Numbers greater than or equal to 5 are rounded up. For example, if precision is set to `0`, `1.5` becomes `2`, and `1.4` becomes `1`.
diff --git a/docs/content/en/functions/time.md b/docs/content/en/functions/time.md
index 6c7f5aec6..e1f24a40b 100644
--- a/docs/content/en/functions/time.md
+++ b/docs/content/en/functions/time.md
@@ -19,6 +19,7 @@ deprecated: false
aliases: []
---
+
`time` converts a timestamp string with an optional default location into a [`time.Time`](https://godoc.org/time#Time) structure so you can access its fields:
```
diff --git a/docs/data/docs.json b/docs/data/docs.json
index d0edcb67f..9925a57e6 100644
--- a/docs/data/docs.json
+++ b/docs/data/docs.json
@@ -1677,6 +1677,9 @@
"caches": {
"_merge": "none"
},
+ "cascade": {
+ "_merge": "none"
+ },
"frontmatter": {
"_merge": "none"
},
@@ -1745,7 +1748,7 @@
"keepDocumentTags": true,
"keepEndTags": true,
"keepQuotes": false,
- "keepWhitespace": false
+ "keepWhitespace": true
},
"css": {
"keepCSS2": true,
@@ -1756,7 +1759,8 @@
"keepVarNames": false
},
"json": {
- "precision": 0
+ "precision": 0,
+ "keepNumbers": false
},
"svg": {
"precision": 0
@@ -3898,14 +3902,52 @@
}
},
"lang": {
- "Merge": {
- "Description": "",
- "Args": null,
+ "FormatAccounting": {
+ "Description": "FormatAccounting returns the currency reprecentation of number for the given currency and precision\nfor the current language in accounting notation.",
+ "Args": [
+ "precision",
+ "currency",
+ "number"
+ ],
"Aliases": null,
- "Examples": null
+ "Examples": [
+ [
+ "{{ 512.5032 | lang.FormatAccounting 2 \"NOK\" }}",
+ "NOK512.50"
+ ]
+ ]
},
- "NumFmt": {
- "Description": "NumFmt formats a number with the given precision using the\nnegative, decimal, and grouping options. The `options`\nparameter is a string consisting of `\u003cnegative\u003e \u003cdecimal\u003e \u003cgrouping\u003e`. The\ndefault `options` value is `- . ,`.\n\nNote that numbers are rounded up at 5 or greater.\nSo, with precision set to 0, 1.5 becomes `2`, and 1.4 becomes `1`.",
+ "FormatCurrency": {
+ "Description": "FormatCurrency returns the currency reprecentation of number for the given currency and precision\nfor the current language.",
+ "Args": [
+ "precision",
+ "currency",
+ "number"
+ ],
+ "Aliases": null,
+ "Examples": [
+ [
+ "{{ 512.5032 | lang.FormatCurrency 2 \"USD\" }}",
+ "$512.50"
+ ]
+ ]
+ },
+ "FormatNumber": {
+ "Description": "FormatNumber formats number with the given precision for the current language.",
+ "Args": [
+ "precision",
+ "number"
+ ],
+ "Aliases": null,
+ "Examples": [
+ [
+ "{{ 512.5032 | lang.FormatNumber 2 }}",
+ "512.50"
+ ]
+ ]
+ },
+ "FormatNumberCustom": {
+ "Description": "FormatNumberCustom formats a number with the given precision using the\nnegative, decimal, and grouping options. The `options`\nparameter is a string consisting of `\u003cnegative\u003e \u003cdecimal\u003e \u003cgrouping\u003e`. The\ndefault `options` value is `- . ,`.\n\nNote that numbers are rounded up at 5 or greater.\nSo, with precision set to 0, 1.5 becomes `2`, and 1.4 becomes `1`.\n\nFor a simpler function that adapts to the current language, see FormatNumberCustom.",
"Args": [
"precision",
"number",
@@ -3914,19 +3956,19 @@
"Aliases": null,
"Examples": [
[
- "{{ lang.NumFmt 2 12345.6789 }}",
+ "{{ lang.FormatNumberCustom 2 12345.6789 }}",
"12,345.68"
],
[
- "{{ lang.NumFmt 2 12345.6789 \"- , .\" }}",
+ "{{ lang.FormatNumberCustom 2 12345.6789 \"- , .\" }}",
"12.345,68"
],
[
- "{{ lang.NumFmt 6 -12345.6789 \"- .\" }}",
+ "{{ lang.FormatNumberCustom 6 -12345.6789 \"- .\" }}",
"-12345.678900"
],
[
- "{{ lang.NumFmt 0 -12345.6789 \"- . ,\" }}",
+ "{{ lang.FormatNumberCustom 0 -12345.6789 \"- . ,\" }}",
"-12,346"
],
[
@@ -3935,6 +3977,32 @@
]
]
},
+ "FormatPercent": {
+ "Description": "FormatPercent formats number with the given precision for the current language.\nNote that the number is assumbed to be percent.",
+ "Args": [
+ "precision",
+ "number"
+ ],
+ "Aliases": null,
+ "Examples": [
+ [
+ "{{ 512.5032 | lang.FormatPercent 2 }}",
+ "512.50%"
+ ]
+ ]
+ },
+ "Merge": {
+ "Description": "",
+ "Args": null,
+ "Aliases": null,
+ "Examples": null
+ },
+ "NumFmt": {
+ "Description": "",
+ "Args": null,
+ "Aliases": null,
+ "Examples": null
+ },
"Translate": {
"Description": "Translate returns a translated string for id.",
"Args": [
diff --git a/docs/layouts/template-func/page.html b/docs/layouts/template-func/page.html
new file mode 100644
index 000000000..f08018e4f
--- /dev/null
+++ b/docs/layouts/template-func/page.html
@@ -0,0 +1,54 @@
+{{ $pkg := .Params.package}}
+{{ $funcs := index site.Data.docs.tpl.funcs $pkg }}
+
+{{ range $k, $v := $funcs }}
+ {{ if $v.Description }}
+ {{ $func := printf "%s.%s" $pkg $k }}
+ <h2>
+ <a class="header-link" href="#{{ $func | anchorize | safeURL }}">
+ <svg class="fill-current o-60 hover-accent-color-light" height="22px" viewBox="0 0 24 24" width="22px" xmlns="http://www.w3.org/2000/svg">
+ <path d="M0 0h24v24H0z" fill="none"/>
+ <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>
+ </svg>
+ </a>
+ {{ $func }}
+ </h2>
+ {{ with $v.Description }}
+ <p class="f4 lh-copy">
+ {{ . | $.RenderString | safeHTML }}
+ </p>
+ {{ end }}
+ <h4 class="minor mb3 pt2 primary-color-dark">
+ Syntax
+ </h4>
+ <div class="f5 mb4 ph3 pv2 bg-light-gray" style="border-left:4px solid #0594CB;">
+ {{ $pkg }}.{{ $k }}
+ {{ with $v.Args }}
+ <span class="ttu">
+ {{ delimit $v.Args ", "}}
+ </span>
+ {{ end }}
+ <span></span>
+ </div>
+ {{ if $v.Examples }}
+ <h4 class="minor mb3 pt2 primary-color-dark">
+ Examples
+ </h4>
+ {{ end }}
+ {{ range $v.Examples }}
+ {{ $input := index . 0 }}
+ {{ $result := index . 1 }}
+ {{ $example := printf "%s ---> %s" $input $result }}
+
+ {{ highlight $example "go-html-template" "" }}
+ {{ end }}
+ {{ with $v.Aliases }}
+ <h4 class="minor mb3 pt2 primary-color-dark">
+ Aliases
+ </h4>
+ <p>
+ {{ delimit . ", "}}
+ </p>
+ {{ end }}
+ {{ end }}
+{{ end }}
diff --git a/go.mod b/go.mod
index aa2eb76db..5ceaaca1e 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
github.com/bep/gitmap v1.1.2
github.com/bep/godartsass v0.12.0
github.com/bep/golibsass v1.0.0
- github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80
+ github.com/bep/gotranslators v0.2.0
github.com/bep/gowebp v0.1.0
github.com/bep/tmc v0.5.1
github.com/cli/safeexec v1.0.0
diff --git a/go.sum b/go.sum
index 31c16e234..491bad20e 100644
--- a/go.sum
+++ b/go.sum
@@ -136,6 +136,8 @@ github.com/bep/golibsass v1.0.0 h1:gNguBMSDi5yZEZzVZP70YpuFQE3qogJIGUlrVILTmOw=
github.com/bep/golibsass v1.0.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80 h1:FuOr7TE02FmHwf0HbOzfN0UyQfHoZd1R3PVuYduFU6U=
github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80/go.mod h1:/tUOv4Jdczp4ZggwBAQriNN97HsQdG1Gm+yV0PsIGD8=
+github.com/bep/gotranslators v0.2.0 h1:GW0mGPivOY4drd4HwWpn44HXBo5zc5iHdDJZj3yWb/k=
+github.com/bep/gotranslators v0.2.0/go.mod h1:fbo6ptvCVYarnHjBm4BvOJX0o18VEvA0slN7xKvqXzc=
github.com/bep/gowebp v0.1.0 h1:4/iQpfnxHyXs3x/aTxMMdOpLEQQhFmF6G7EieWPTQyo=
github.com/bep/gowebp v0.1.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
diff --git a/hugolib/language_test.go b/hugolib/language_test.go
index da8ecd22b..1de422620 100644
--- a/hugolib/language_test.go
+++ b/hugolib/language_test.go
@@ -79,3 +79,52 @@ name = "foo-a"
})
}
+
+func TestLanguageNumberFormatting(t *testing.T) {
+
+ b := newTestSitesBuilder(t)
+ b.WithConfigFile("toml", `
+baseURL = "https://example.org"
+
+defaultContentLanguage = "en"
+defaultContentLanguageInSubDir = true
+
+[languages]
+[languages.en]
+timeZone="UTC"
+weight=10
+[languages.nn]
+weight=20
+
+`)
+
+ b.WithTemplates("index.html", `
+
+FormatNumber: {{ 512.5032 | lang.FormatNumber 2 }}
+FormatPercent: {{ 512.5032 | lang.FormatPercent 2 }}
+FormatCurrency: {{ 512.5032 | lang.FormatCurrency 2 "USD" }}
+FormatAccounting: {{ 512.5032 | lang.FormatAccounting 2 "NOK" }}
+FormatNumberCustom: {{ lang.FormatNumberCustom 2 12345.6789 }}
+
+# We renamed this to FormatNumberCustom in 0.87.0.
+NumFmt: {{ -98765.4321 | lang.NumFmt 2 }}
+
+
+`)
+ b.WithContent("p1.md", "")
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/en/index.html", `
+FormatNumber: 512.50
+FormatPercent: 512.50%
+FormatCurrency: $512.50
+FormatAccounting: NOK512.50
+FormatNumberCustom: 12,345.68
+
+NumFmt: -98,765.43
+`,
+ )
+
+ b.AssertFileContent("public/nn/index.html", "FormatNumber: 512,50\nFormatPercent: 512,50\u00a0%\nFormatCurrency: 512,50\u00a0USD\nFormatAccounting: 512,50\u00a0kr")
+}
diff --git a/langs/language.go b/langs/language.go
index 6f39848cf..758729c25 100644
--- a/langs/language.go
+++ b/langs/language.go
@@ -97,11 +97,11 @@ func NewLanguage(lang string, cfg config.Provider) *Language {
localCfg := config.New()
compositeConfig := config.NewCompositeConfig(cfg, localCfg)
- translator := translators.Get(lang)
+ translator := translators.GetTranslator(lang)
if translator == nil {
- translator = translators.Get(cfg.GetString("defaultContentLanguage"))
+ translator = translators.GetTranslator(cfg.GetString("defaultContentLanguage"))
if translator == nil {
- translator = translators.Get("en")
+ translator = translators.GetTranslator("en")
}
}
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.