URI: 
       lang.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       lang.go (6676B)
       ---
            1 // Copyright 2017 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 // Package lang provides template functions for content internationalization.
           15 package lang
           16 
           17 import (
           18         "context"
           19         "errors"
           20         "fmt"
           21         "math"
           22         "strconv"
           23         "strings"
           24 
           25         "github.com/gohugoio/locales"
           26         translators "github.com/gohugoio/localescompressed"
           27 
           28         "github.com/gohugoio/hugo/common/hreflect"
           29         "github.com/gohugoio/hugo/deps"
           30         "github.com/spf13/cast"
           31 )
           32 
           33 // New returns a new instance of the lang-namespaced template functions.
           34 func New(deps *deps.Deps, translator locales.Translator) *Namespace {
           35         return &Namespace{
           36                 translator: translator,
           37                 deps:       deps,
           38         }
           39 }
           40 
           41 // Namespace provides template functions for the "lang" namespace.
           42 type Namespace struct {
           43         translator locales.Translator
           44         deps       *deps.Deps
           45 }
           46 
           47 // Translate returns a translated string for id.
           48 func (ns *Namespace) Translate(ctx context.Context, id any, args ...any) (string, error) {
           49         var templateData any
           50 
           51         if len(args) > 0 {
           52                 if len(args) > 1 {
           53                         return "", fmt.Errorf("wrong number of arguments, expecting at most 2, got %d", len(args)+1)
           54                 }
           55                 templateData = args[0]
           56         }
           57 
           58         sid, err := cast.ToStringE(id)
           59         if err != nil {
           60                 return "", err
           61         }
           62 
           63         return ns.deps.Translate(ctx, sid, templateData), nil
           64 }
           65 
           66 // FormatNumber formats number with the given precision for the current language.
           67 func (ns *Namespace) FormatNumber(precision, number any) (string, error) {
           68         p, n, err := ns.castPrecisionNumber(precision, number)
           69         if err != nil {
           70                 return "", err
           71         }
           72         return ns.translator.FmtNumber(n, p), nil
           73 }
           74 
           75 // FormatPercent formats number with the given precision for the current language.
           76 // Note that the number is assumed to be a percentage.
           77 func (ns *Namespace) FormatPercent(precision, number any) (string, error) {
           78         p, n, err := ns.castPrecisionNumber(precision, number)
           79         if err != nil {
           80                 return "", err
           81         }
           82         return ns.translator.FmtPercent(n, p), nil
           83 }
           84 
           85 // FormatCurrency returns the currency representation of number for the given currency and precision
           86 // for the current language.
           87 //
           88 // The return value is formatted with at least two decimal places.
           89 func (ns *Namespace) FormatCurrency(precision, currency, number any) (string, error) {
           90         p, n, err := ns.castPrecisionNumber(precision, number)
           91         if err != nil {
           92                 return "", err
           93         }
           94         c := translators.GetCurrency(cast.ToString(currency))
           95         if c < 0 {
           96                 return "", fmt.Errorf("unknown currency code: %q", currency)
           97         }
           98         return ns.translator.FmtCurrency(n, p, c), nil
           99 }
          100 
          101 // FormatAccounting returns the currency representation of number for the given currency and precision
          102 // for the current language in accounting notation.
          103 //
          104 // The return value is formatted with at least two decimal places.
          105 func (ns *Namespace) FormatAccounting(precision, currency, number any) (string, error) {
          106         p, n, err := ns.castPrecisionNumber(precision, number)
          107         if err != nil {
          108                 return "", err
          109         }
          110         c := translators.GetCurrency(cast.ToString(currency))
          111         if c < 0 {
          112                 return "", fmt.Errorf("unknown currency code: %q", currency)
          113         }
          114         return ns.translator.FmtAccounting(n, p, c), nil
          115 }
          116 
          117 func (ns *Namespace) castPrecisionNumber(precision, number any) (uint64, float64, error) {
          118         p, err := cast.ToUint64E(precision)
          119         if err != nil {
          120                 return 0, 0, err
          121         }
          122 
          123         // Sanity check.
          124         if p > 20 {
          125                 return 0, 0, fmt.Errorf("invalid precision: %d", precision)
          126         }
          127 
          128         n, err := cast.ToFloat64E(number)
          129         if err != nil {
          130                 return 0, 0, err
          131         }
          132         return p, n, nil
          133 }
          134 
          135 // FormatNumberCustom formats a number with the given precision. The first
          136 // options parameter is a space-delimited string of characters to represent
          137 // negativity, the decimal point, and grouping. The default value is `- . ,`.
          138 // The second options parameter defines an alternate delimiting character.
          139 //
          140 // Note that numbers are rounded up at 5 or greater.
          141 // So, with precision set to 0, 1.5 becomes `2`, and 1.4 becomes `1`.
          142 //
          143 // For a simpler function that adapts to the current language, see FormatNumber.
          144 func (ns *Namespace) FormatNumberCustom(precision, number any, options ...any) (string, error) {
          145         prec, err := cast.ToIntE(precision)
          146         if err != nil {
          147                 return "", err
          148         }
          149 
          150         n, err := cast.ToFloat64E(number)
          151         if err != nil {
          152                 return "", err
          153         }
          154 
          155         var neg, dec, grp string
          156 
          157         if len(options) == 0 {
          158                 // defaults
          159                 neg, dec, grp = "-", ".", ","
          160         } else {
          161                 delim := " "
          162 
          163                 if len(options) == 2 {
          164                         // custom delimiter
          165                         s, err := cast.ToStringE(options[1])
          166                         if err != nil {
          167                                 return "", err
          168                         }
          169 
          170                         delim = s
          171                 }
          172 
          173                 s, err := cast.ToStringE(options[0])
          174                 if err != nil {
          175                         return "", err
          176                 }
          177 
          178                 rs := strings.Split(s, delim)
          179                 switch len(rs) {
          180                 case 0:
          181                 case 1:
          182                         neg = rs[0]
          183                 case 2:
          184                         neg, dec = rs[0], rs[1]
          185                 case 3:
          186                         neg, dec, grp = rs[0], rs[1], rs[2]
          187                 default:
          188                         return "", errors.New("too many fields in options parameter to NumFmt")
          189                 }
          190         }
          191 
          192         exp := math.Pow(10.0, float64(prec))
          193         r := math.Round(n*exp) / exp
          194 
          195         // Logic from MIT Licensed github.com/gohugoio/locales/
          196         // Original Copyright (c) 2016 Go Playground
          197 
          198         s := strconv.FormatFloat(math.Abs(r), 'f', prec, 64)
          199         L := len(s) + 2 + len(s[:len(s)-1-prec])/3
          200 
          201         var count int
          202         inWhole := prec == 0
          203         b := make([]byte, 0, L)
          204 
          205         for i := len(s) - 1; i >= 0; i-- {
          206                 if s[i] == '.' {
          207                         for j := len(dec) - 1; j >= 0; j-- {
          208                                 b = append(b, dec[j])
          209                         }
          210                         inWhole = true
          211                         continue
          212                 }
          213 
          214                 if inWhole {
          215                         if count == 3 {
          216                                 for j := len(grp) - 1; j >= 0; j-- {
          217                                         b = append(b, grp[j])
          218                                 }
          219                                 count = 1
          220                         } else {
          221                                 count++
          222                         }
          223                 }
          224 
          225                 b = append(b, s[i])
          226         }
          227 
          228         if n < 0 {
          229                 for j := len(neg) - 1; j >= 0; j-- {
          230                         b = append(b, neg[j])
          231                 }
          232         }
          233 
          234         // reverse
          235         for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
          236                 b[i], b[j] = b[j], b[i]
          237         }
          238 
          239         return string(b), nil
          240 }
          241 
          242 type pagesLanguageMerger interface {
          243         MergeByLanguageInterface(other any) (any, error)
          244 }
          245 
          246 // Merge creates a union of pages from two languages.
          247 func (ns *Namespace) Merge(p2, p1 any) (any, error) {
          248         if !hreflect.IsTruthful(p1) {
          249                 return p2, nil
          250         }
          251         if !hreflect.IsTruthful(p2) {
          252                 return p1, nil
          253         }
          254         merger, ok := p1.(pagesLanguageMerger)
          255         if !ok {
          256                 return nil, fmt.Errorf("language merge not supported for %T", p1)
          257         }
          258         return merger.MergeByLanguageInterface(p2)
          259 }