github.com/SDLMoe/hugo@v0.47.1/tpl/lang/lang.go (about)

     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
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"math"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/gohugoio/hugo/deps"
    24  	"github.com/spf13/cast"
    25  )
    26  
    27  // New returns a new instance of the lang-namespaced template functions.
    28  func New(deps *deps.Deps) *Namespace {
    29  	return &Namespace{
    30  		deps: deps,
    31  	}
    32  }
    33  
    34  // Namespace provides template functions for the "lang" namespace.
    35  type Namespace struct {
    36  	deps *deps.Deps
    37  }
    38  
    39  // Translate ...
    40  func (ns *Namespace) Translate(id interface{}, args ...interface{}) (string, error) {
    41  	sid, err := cast.ToStringE(id)
    42  	if err != nil {
    43  		return "", nil
    44  	}
    45  
    46  	return ns.deps.Translate(sid, args...), nil
    47  }
    48  
    49  // NumFmt formats a number with the given precision using the
    50  // negative, decimal, and grouping options.  The `options`
    51  // parameter is a string consisting of `<negative> <decimal> <grouping>`.  The
    52  // default `options` value is `- . ,`.
    53  //
    54  // Note that numbers are rounded up at 5 or greater.
    55  // So, with precision set to 0, 1.5 becomes `2`, and 1.4 becomes `1`.
    56  func (ns *Namespace) NumFmt(precision, number interface{}, options ...interface{}) (string, error) {
    57  	prec, err := cast.ToIntE(precision)
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	n, err := cast.ToFloat64E(number)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	var neg, dec, grp string
    68  
    69  	if len(options) == 0 {
    70  		// TODO(moorereason): move to site config
    71  		neg, dec, grp = "-", ".", ","
    72  	} else {
    73  		s, err := cast.ToStringE(options[0])
    74  		if err != nil {
    75  			return "", nil
    76  		}
    77  
    78  		rs := strings.Fields(s)
    79  		switch len(rs) {
    80  		case 0:
    81  		case 1:
    82  			neg = rs[0]
    83  		case 2:
    84  			neg, dec = rs[0], rs[1]
    85  		case 3:
    86  			neg, dec, grp = rs[0], rs[1], rs[2]
    87  		default:
    88  			return "", errors.New("too many fields in options parameter to NumFmt")
    89  		}
    90  	}
    91  
    92  	// Logic from MIT Licensed github.com/go-playground/locales/
    93  	// Original Copyright (c) 2016 Go Playground
    94  
    95  	s := strconv.FormatFloat(math.Abs(n), 'f', prec, 64)
    96  	L := len(s) + 2 + len(s[:len(s)-1-prec])/3
    97  
    98  	var count int
    99  	inWhole := prec == 0
   100  	b := make([]byte, 0, L)
   101  
   102  	for i := len(s) - 1; i >= 0; i-- {
   103  		if s[i] == '.' {
   104  			for j := len(dec) - 1; j >= 0; j-- {
   105  				b = append(b, dec[j])
   106  			}
   107  			inWhole = true
   108  			continue
   109  		}
   110  
   111  		if inWhole {
   112  			if count == 3 {
   113  				for j := len(grp) - 1; j >= 0; j-- {
   114  					b = append(b, grp[j])
   115  				}
   116  				count = 1
   117  			} else {
   118  				count++
   119  			}
   120  		}
   121  
   122  		b = append(b, s[i])
   123  	}
   124  
   125  	if n < 0 {
   126  		for j := len(neg) - 1; j >= 0; j-- {
   127  			b = append(b, neg[j])
   128  		}
   129  	}
   130  
   131  	// reverse
   132  	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
   133  		b[i], b[j] = b[j], b[i]
   134  	}
   135  
   136  	return string(b), nil
   137  }
   138  
   139  type pagesLanguageMerger interface {
   140  	MergeByLanguageInterface(other interface{}) (interface{}, error)
   141  }
   142  
   143  func (ns *Namespace) Merge(p2, p1 interface{}) (interface{}, error) {
   144  	merger, ok := p1.(pagesLanguageMerger)
   145  	if !ok {
   146  		return nil, fmt.Errorf("language merge not supported for %T", p1)
   147  	}
   148  	return merger.MergeByLanguageInterface(p2)
   149  }