tpl/lang: Add new localized versions of lang.FormatNumber etc.
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 28 Jul 2021 10:28:52 +0000 (12:28 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 29 Jul 2021 14:40:06 +0000 (16:40 +0200)
Fixes #8820

17 files changed:
common/htime/time_test.go
docs/content/en/content-management/image-processing/index.md
docs/content/en/functions/NumFmt.md [deleted file]
docs/content/en/functions/lang.md [new file with mode: 0644]
docs/content/en/functions/time.md
docs/data/docs.json
docs/layouts/template-func/page.html [new file with mode: 0644]
go.mod
go.sum
hugolib/language_test.go
langs/language.go
tpl/cast/docshelper.go
tpl/lang/init.go
tpl/lang/init_test.go
tpl/lang/lang.go
tpl/lang/lang_test.go
tpl/time/time_test.go

index 38302775577d4de28c7a0308aa10b849f473c9ea..e8aec0153012352d647e7150dfbc10654f78839f 100644 (file)
@@ -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")
index 76679717e42373e09ef470adc1c180e3ceba1d8a..e2e9641545beed3a3d5dcea1591fe6e23d425c6f 100644 (file)
@@ -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/NumFmt.md
deleted file mode 100644 (file)
index 9b51f59..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
----
-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
-categories: [functions]
-keywords: [numbers]
-menu:
-  docs:
-    parent: "functions"
-toc: false
-signature: ["lang.NumFmt PRECISION NUMBER [OPTIONS [DELIMITER]]"]
-workson: []
-hugoversion:
-relatedfuncs: []
-deprecated: false
-draft: false
-aliases: []
-comments:
----
-
-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.
-
-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`.
-
-```
-{{ lang.NumFmt 2 12345.6789 }} → 12,345.68
-{{ lang.NumFmt 2 12345.6789 "- , ." }} → 12.345,68
-{{ lang.NumFmt 0 -12345.6789 "- . ," }} → -12,346
-{{ lang.NumFmt 6 -12345.6789 "- ." }} → -12345.678900
-{{ lang.NumFmt 6 -12345.6789 "-|.| " "|" }} → -1 2345.678900
-{{ -98765.4321 | lang.NumFmt 2 }} → -98,765.43
-```
diff --git a/docs/content/en/functions/lang.md b/docs/content/en/functions/lang.md
new file mode 100644 (file)
index 0000000..7b810c9
--- /dev/null
@@ -0,0 +1,30 @@
+---
+title: lang
+package: lang
+description: "TODO.."
+date: 2021-07-28
+categories: [functions]
+keywords: [numbers]
+menu:
+  docs:
+    parent: "functions"
+toc: false
+signature: ["lang.NumFmt PRECISION NUMBER [OPTIONS [DELIMITER]]"]
+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.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`.
+
+```
+{{ lang.NumFmt 2 12345.6789 }} → 12,345.68
+{{ lang.NumFmt 2 12345.6789 "- , ." }} → 12.345,68
+{{ lang.NumFmt 0 -12345.6789 "- . ," }} → -12,346
+{{ lang.NumFmt 6 -12345.6789 "- ." }} → -12345.678900
+{{ lang.NumFmt 6 -12345.6789 "-|.| " "|" }} → -1 2345.678900
+{{ -98765.4321 | lang.NumFmt 2 }} → -98,765.43
+```
index 6c7f5aec6a31b9b777bb68245dacafc92fedc7ab..e1f24a40b64063a2fc329ff80ec9e4ce6118c1f0 100644 (file)
@@ -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:
 
 ```
index d0edcb67fe1f2a5f152b4207ae03786173c508fc..9925a57e6e606697b1d203960715b638b373eb25 100644 (file)
       "caches": {
         "_merge": "none"
       },
+      "cascade": {
+        "_merge": "none"
+      },
       "frontmatter": {
         "_merge": "none"
       },
           "keepDocumentTags": true,
           "keepEndTags": true,
           "keepQuotes": false,
-          "keepWhitespace": false
+          "keepWhitespace": true
         },
         "css": {
           "keepCSS2": true,
           "keepVarNames": false
         },
         "json": {
-          "precision": 0
+          "precision": 0,
+          "keepNumbers": false
         },
         "svg": {
           "precision": 0
         }
       },
       "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",
           "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"
             ],
             [
             ]
           ]
         },
+        "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 (file)
index 0000000..f08018e
--- /dev/null
@@ -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 aa2eb76dbaa6febfafdf89e78321ddc0de78785e..5ceaaca1e911c9d75eb70607d0fd6149f48ece06 100644 (file)
--- 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 31c16e234910aae40b36b496666a1cf5b2cd9888..491bad20e8b8c99962a698da3d92a191b85eca0b 100644 (file)
--- 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=
index da8ecd22b1bc210ad359d7d41d0edf57490dffbb..1de42262063361733e9b4c5cd348c5ca129af1b1 100644 (file)
@@ -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")
+}
index 6f39848cfc4e127e8620112f078064a3730cd7c8..758729c2523357ffd6447aa56c56345c58535fe3 100644 (file)
@@ -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")
                }
        }
 
index a3cc26de5e092ee5088c045bd978af7fecb9196b..9a5d55b3db0edcf41604c4ffdcfe946de1324f77 100644 (file)
@@ -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()),
                }
 
index 520eccb88711b4ecd1bec530080b2ebf07571dd9..beb148ff628203406b1678806d19fcd9231989fc 100644 (file)
@@ -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
        }
 
index 82def5523de0e60cacfeae80ff0f42a653efc234..61d7b504715edd5e32241a70298047fe4bef0a40 100644 (file)
@@ -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
index 4e6c9c70a9109846c10cce17886039c7524ffa83..0cf448caa79127b091509cc99e30869554dbb954 100644 (file)
@@ -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)
 }
index 3b3caeb625472c1cd23649144b48654992823c3c..782a0a69a114165b36e9b15d0e0f8f6e2d251d72 100644 (file)
@@ -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")
+       })
+
+}
index 71899cc6516eb36606b3ae18e6bf91842a21c0bc..22cbc9bcc9e1e5fc6d89d41536cf487939ab6a6f 100644 (file)
@@ -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{}