summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-11-21 21:59:38 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-11-22 18:41:50 +0100
commita3fe5e5e35f311f22b6b4fc38abfcf64cd2c7d6f (patch)
tree06cf1f647ae026b4fb3053c85370c2b203c7a089
parentcd07e6d57b158a76f812e8c4c9567dbc84f57939 (diff)
Fix Params case handling in the index, sort and where func
This means that you can now do: ``` {{ range where .Site.Pages "Params.MYPARAM" "foo" }} ```
-rw-r--r--commands/import_jekyll.go6
-rw-r--r--common/maps/maps.go31
-rw-r--r--common/maps/maps_test.go24
-rw-r--r--common/maps/params.go109
-rw-r--r--common/maps/params_test.go2
-rw-r--r--common/para/para.go6
-rw-r--r--common/para/para_test.go4
-rw-r--r--hugolib/case_insensitive_test.go36
-rw-r--r--hugolib/page__meta.go21
-rw-r--r--hugolib/page_test.go11
-rw-r--r--hugolib/pages_map.go4
-rw-r--r--hugolib/site.go15
-rw-r--r--langs/config.go4
-rw-r--r--langs/language.go2
-rw-r--r--navigation/menu.go3
-rw-r--r--navigation/pagemenus.go6
-rw-r--r--resources/page/page_nop.go2
-rw-r--r--resources/page/site.go6
-rw-r--r--resources/page/testhelpers_test.go2
-rw-r--r--resources/resource.go4
-rw-r--r--resources/resource/params.go6
-rw-r--r--resources/resource/resourcetypes.go3
-rw-r--r--resources/resource_metadata.go2
-rw-r--r--resources/transform.go6
-rw-r--r--tpl/collections/collections_test.go11
-rw-r--r--tpl/collections/index.go10
-rw-r--r--tpl/collections/index_test.go5
-rw-r--r--tpl/collections/sort.go23
-rw-r--r--tpl/collections/sort_test.go16
-rw-r--r--tpl/collections/where.go22
-rw-r--r--tpl/collections/where_test.go49
-rw-r--r--tpl/resources/resources.go6
-rw-r--r--tpl/tplimpl/template_ast_transformers.go7
33 files changed, 313 insertions, 151 deletions
diff --git a/commands/import_jekyll.go b/commands/import_jekyll.go
index e5c39dc34..b1cc53378 100644
--- a/commands/import_jekyll.go
+++ b/commands/import_jekyll.go
@@ -30,12 +30,12 @@ import (
"github.com/gohugoio/hugo/parser/metadecoders"
+ "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/parser"
"github.com/spf13/afero"
- "github.com/spf13/cast"
"github.com/spf13/cobra"
jww "github.com/spf13/jwalterweatherman"
)
@@ -420,7 +420,7 @@ func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft b
}
func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
- metadata, err := cast.ToStringMapE(m)
+ metadata, err := maps.ToStringMapE(m)
if err != nil {
return nil, err
}
@@ -472,7 +472,7 @@ func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, d
}
func convertJekyllContent(m interface{}, content string) string {
- metadata, _ := cast.ToStringMapE(m)
+ metadata, _ := maps.ToStringMapE(m)
lines := strings.Split(content, "\n")
var resultLines []string
diff --git a/common/maps/maps.go b/common/maps/maps.go
index e0d4f964d..8b42ca764 100644
--- a/common/maps/maps.go
+++ b/common/maps/maps.go
@@ -25,26 +25,45 @@ import (
// recursively.
// Notes:
// * This will modify the map given.
-// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}.
-func ToLower(m map[string]interface{}) {
+// * Any nested map[interface{}]interface{} will be converted to Params.
+func ToLower(m Params) {
for k, v := range m {
+ var retyped bool
switch v.(type) {
case map[interface{}]interface{}:
- v = cast.ToStringMap(v)
- ToLower(v.(map[string]interface{}))
+ var p Params = cast.ToStringMap(v)
+ v = p
+ ToLower(p)
+ retyped = true
case map[string]interface{}:
- ToLower(v.(map[string]interface{}))
+ var p Params = v.(map[string]interface{})
+ v = p
+ ToLower(p)
+ retyped = true
}
lKey := strings.ToLower(k)
- if k != lKey {
+ if retyped || k != lKey {
delete(m, k)
m[lKey] = v
}
+ }
+}
+func ToStringMapE(in interface{}) (map[string]interface{}, error) {
+ switch in.(type) {
+ case Params:
+ return in.(Params), nil
+ default:
+ return cast.ToStringMapE(in)
}
}
+func ToStringMap(in interface{}) map[string]interface{} {
+ m, _ := ToStringMapE(in)
+ return m
+}
+
type keyRename struct {
pattern glob.Glob
newKey string
diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go
index 8b0aa5eb9..6e4947adb 100644
--- a/common/maps/maps_test.go
+++ b/common/maps/maps_test.go
@@ -14,6 +14,7 @@
package maps
import (
+ "fmt"
"reflect"
"testing"
@@ -21,7 +22,6 @@ import (
)
func TestToLower(t *testing.T) {
-
tests := []struct {
input map[string]interface{}
expected map[string]interface{}
@@ -30,7 +30,7 @@ func TestToLower(t *testing.T) {
map[string]interface{}{
"abC": 32,
},
- map[string]interface{}{
+ Params{
"abc": 32,
},
},
@@ -48,16 +48,16 @@ func TestToLower(t *testing.T) {
"J": 25,
},
},
- map[string]interface{}{
+ Params{
"abc": 32,
- "def": map[string]interface{}{
+ "def": Params{
"23": "A value",
- "24": map[string]interface{}{
+ "24": Params{
"abcde": "A value",
"efghi": "Another value",
},
},
- "ghi": map[string]interface{}{
+ "ghi": Params{
"j": 25,
},
},
@@ -65,11 +65,13 @@ func TestToLower(t *testing.T) {
}
for i, test := range tests {
- // ToLower modifies input.
- ToLower(test.input)
- if !reflect.DeepEqual(test.expected, test.input) {
- t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
- }
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ // ToLower modifies input.
+ ToLower(test.input)
+ if !reflect.DeepEqual(test.expected, test.input) {
+ t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
+ }
+ })
}
}
diff --git a/common/maps/params.go b/common/maps/params.go
index 2d62ad752..ecb63d7a5 100644
--- a/common/maps/params.go
+++ b/common/maps/params.go
@@ -19,76 +19,89 @@ import (
"github.com/spf13/cast"
)
+// Params is a map where all keys are lower case.
+type Params map[string]interface{}
+
+// Get does a lower case and nested search in this map.
+// It will return nil if none found.
+func (p Params) Get(indices ...string) interface{} {
+ v, _, _ := getNested(p, indices)
+ return v
+}
+
+func getNested(m map[string]interface{}, indices []string) (interface{}, string, map[string]interface{}) {
+ if len(indices) == 0 {
+ return nil, "", nil
+ }
+
+ first := indices[0]
+ v, found := m[strings.ToLower(cast.ToString(first))]
+ if !found {
+ return nil, "", nil
+ }
+
+ if len(indices) == 1 {
+ return v, first, m
+ }
+
+ switch m2 := v.(type) {
+ case Params:
+ return getNested(m2, indices[1:])
+ case map[string]interface{}:
+ return getNested(m2, indices[1:])
+ default:
+ return nil, "", nil
+ }
+}
+
// GetNestedParam gets the first match of the keyStr in the candidates given.
// It will first try the exact match and then try to find it as a nested map value,
// using the given separator, e.g. "mymap.name".
// It assumes that all the maps given have lower cased keys.
-func GetNestedParam(keyStr, separator string, candidates ...map[string]interface{}) (interface{}, error) {
+func GetNestedParam(keyStr, separator string, candidates ...Params) (interface{}, error) {
keyStr = strings.ToLower(keyStr)
- lookupFn := func(key string) interface{} {
- for _, m := range candidates {
- if v, ok := m[key]; ok {
- return v
- }
+ // Try exact match first
+ for _, m := range candidates {
+ if v, ok := m[keyStr]; ok {
+ return v, nil
}
-
- return nil
- }
-
- v, _, _, err := GetNestedParamFn(keyStr, separator, lookupFn)
- return v, err
-}
-
-func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
- result, _ := traverseDirectParams(keyStr, lookupFn)
- if result != nil {
- return result, keyStr, nil, nil
}
keySegments := strings.Split(keyStr, separator)
- if len(keySegments) == 1 {
- return nil, keyStr, nil, nil
+ for _, m := range candidates {
+ if v := m.Get(keySegments...); v != nil {
+ return v, nil
+ }
}
- return traverseNestedParams(keySegments, lookupFn)
-}
+ return nil, nil
-func traverseDirectParams(keyStr string, lookupFn func(key string) interface{}) (interface{}, error) {
- return lookupFn(keyStr), nil
}
-func traverseNestedParams(keySegments []string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
- firstKey, rest := keySegments[0], keySegments[1:]
- result := lookupFn(firstKey)
- if result == nil || len(rest) == 0 {
- return result, firstKey, nil, nil
+func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
+ keySegments := strings.Split(strings.ToLower(keyStr), separator)
+ if len(keySegments) == 0 {
+ return nil, "", nil, nil
}
- switch m := result.(type) {
- case map[string]interface{}:
- v, key, owner := traverseParams(rest, m)
- return v, key, owner, nil
- default:
+ first := lookupFn(keySegments[0])
+ if first == nil {
return nil, "", nil, nil
}
-}
-
-func traverseParams(keys []string, m map[string]interface{}) (interface{}, string, map[string]interface{}) {
- // Shift first element off.
- firstKey, rest := keys[0], keys[1:]
- result := m[firstKey]
- // No point in continuing here.
- if result == nil {
- return result, "", nil
+ if len(keySegments) == 1 {
+ return first, keySegments[0], nil, nil
}
- if len(rest) == 0 {
- // That was the last key.
- return result, firstKey, m
+ switch m := first.(type) {
+ case map[string]interface{}:
+ v, key, owner := getNested(m, keySegments[1:])
+ return v, key, owner, nil
+ case Params:
+ v, key, owner := getNested(m, keySegments[1:])
+ return v, key, owner, nil
}
- // That was not the last key.
- return traverseParams(rest, cast.ToStringMap(result))
+ return nil, "", nil, nil
}
diff --git a/common/maps/params_test.go b/common/maps/params_test.go
index 6477de6f4..8016a8bd6 100644
--- a/common/maps/params_test.go
+++ b/common/maps/params_test.go
@@ -35,7 +35,7 @@ func TestGetNestedParam(t *testing.T) {
c := qt.New(t)
- must := func(keyStr, separator string, candidates ...map[string]interface{}) interface{} {
+ must := func(keyStr, separator string, candidates ...Params) interface{} {
v, err := GetNestedParam(keyStr, separator, candidates...)
c.Assert(err, qt.IsNil)
return v
diff --git a/common/para/para.go b/common/para/para.go
index 319bdb78f..69bfc205b 100644
--- a/common/para/para.go
+++ b/common/para/para.go
@@ -37,8 +37,8 @@ type Runner interface {
type errGroupRunner struct {
*errgroup.Group
- w *Workers
- ctx context.Context
+ w *Workers
+ ctx context.Context
}
func (g *errGroupRunner) Run(fn func() error) {
@@ -68,6 +68,6 @@ func (w *Workers) Start(ctx context.Context) (Runner, context.Context) {
return &errGroupRunner{
Group: g,
ctx: ctx,
- w: w,
+ w: w,
}, ctx
}
diff --git a/common/para/para_test.go b/common/para/para_test.go
index 9f33a234c..bda7f5d27 100644
--- a/common/para/para_test.go
+++ b/common/para/para_test.go
@@ -15,6 +15,7 @@ package para
import (
"context"
+ "runtime"
"sort"
"sync"
"sync/atomic"
@@ -25,6 +26,9 @@ import (
)
func TestPara(t *testing.T) {
+ if runtime.NumCPU() < 4 {
+ t.Skipf("skip para test, CPU count is %d", runtime.NumCPU())
+ }
c := qt.New(t)
diff --git a/hugolib/case_insensitive_test.go b/hugolib/case_insensitive_test.go
index 5c7dbe073..9c2662044 100644
--- a/hugolib/case_insensitive_test.go
+++ b/hugolib/case_insensitive_test.go
@@ -61,7 +61,7 @@ angledQuotes = false
hrefTargetBlank = false
[Languages.en.Colors]
BLUE = "blues"
-yellow = "golden"
+Yellow = "golden"
`
caseMixingPage1En = `
---
@@ -137,18 +137,6 @@ func TestCaseInsensitiveConfigurationVariations(t *testing.T) {
c := qt.New(t)
- // See issues 2615, 1129, 2590 and maybe some others
- // Also see 2598
- //
- // Viper is now, at least for the Hugo part, case insensitive
- // So we need tests for all of it, with needed adjustments on the Hugo side.
- // Not sure what that will be. Let us see.
-
- // So all the below with case variations:
- // config: regular fields, blackfriday config, param with nested map
- // language: new and overridden values, in regular fields and nested paramsmap
- // page frontmatter: regular fields, blackfriday config, param with nested map
-
mm := afero.NewMemMapFs()
caseMixingTestsWriteCommonSources(t, mm)
@@ -168,17 +156,27 @@ Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
{{ define "main"}}
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }}
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
+{{ template "index-color" (dict "name" "Page" "params" .Params) }}
+{{ template "index-color" (dict "name" "Site" "params" .Site.Params) }}
+
{{ .Content }}
{{ partial "partial.html" . }}
{{ end }}
+{{ define "index-color" }}
+{{ $yellow := index .params "COLoRS" "yELLOW" }}
+{{ $colors := index .params "COLoRS" }}
+{{ $yellow2 := index $colors "yEllow" }}
+index1|{{ .name }}: {{ $yellow }}|
+index2|{{ .name }}: {{ $yellow2 }}|
+{{ end }}
`)
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `
Page Title: {{ .Title }}
Site Title: {{ .Site.Title }}
Site Lang Mood: {{ .Site.Language.Params.MOoD }}
-Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
-Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
+Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}|{{ index .Params "ColOR" }}
+Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}|{{ index .Site.Params "ColOR" }}
{{ $page2 := .Site.GetPage "/sect2/page2" }}
{{ if $page2 }}
Page2: {{ $page2.Params.ColoR }}
@@ -200,8 +198,8 @@ Page2: {{ $page2.Params.ColoR }}
}
th.assertFileContent(filepath.Join("public", "nn", "sect1", "page1", "index.html"),
- "Page Colors: red|heavenly",
- "Site Colors: green|yellow",
+ "Page Colors: red|heavenly|red",
+ "Site Colors: green|yellow|green",
"Site Lang Mood: Happy",
"Shortcode Page: red|heavenly",
"Shortcode Site: green|yellow",
@@ -230,6 +228,10 @@ Page2: {{ $page2.Params.ColoR }}
"Block Page Colors: black|sky",
"Partial Page: black|sky",
"Partial Site: green|yellow",
+ "index1|Page: flower|",
+ "index1|Site: yellow|",
+ "index2|Page: flower|",
+ "index2|Site: yellow|",
)
}
diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
index d137ac340..ca5c7007e 100644
--- a/hugolib/page__meta.go
+++ b/hugolib/page__meta.go
@@ -228,7 +228,7 @@ func (p *pageMeta) Param(key interface{}) (interface{}, error) {
return resource.Param(p, p.s.Info.Params(), key)
}
-func (p *pageMeta) Params() map[string]interface{} {
+func (p *pageMeta) Params() maps.Params {
return p.params
}
@@ -312,7 +312,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
return errors.New("missing frontmatter data")
}
- pm.params = make(map[string]interface{})
+ pm.params = make(maps.Params)
if frontmatter != nil {
// Needed for case insensitive fetching of params values
@@ -320,7 +320,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
if p.IsNode() {
// Check for any cascade define on itself.
if cv, found := frontmatter["cascade"]; found {
- cvm := cast.ToStringMap(cv)
+ cvm := maps.ToStringMap(cv)
if bucket.cascade == nil {
bucket.cascade = cvm
} else {
@@ -479,7 +479,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
}
pm.params[loki] = pm.aliases
case "sitemap":
- p.m.sitemap = config.DecodeSitemap(p.s.siteCfg.sitemap, cast.ToStringMap(v))
+ p.m.sitemap = config.DecodeSitemap(p.s.siteCfg.sitemap, maps.ToStringMap(v))
pm.params[loki] = p.m.sitemap
sitemapSet = true
case "iscjklanguage":
@@ -495,7 +495,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
switch vv := v.(type) {
case []map[interface{}]interface{}:
for _, vvv := range vv {
- resources = append(resources, cast.ToStringMap(vvv))
+ resources = append(resources, maps.ToStringMap(vvv))
}
case []map[string]interface{}:
resources = append(resources, vv...)
@@ -503,7 +503,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
for _, vvv := range vv {
switch vvvv := vvv.(type) {
case map[interface{}]interface{}:
- resources = append(resources, cast.ToStringMap(vvvv))
+ resources = append(resources, maps.ToStringMap(vvvv))
case map[string]interface{}:
resources = append(resources, vvvv)
}
@@ -642,7 +642,7 @@ func (p *pageMeta) applyDefaultValues() error {
var renderingConfigOverrides map[string]interface{}
bfParam := getParamToLower(p, "blackfriday")
if bfParam != nil {
- renderingConfigOverrides = cast.ToStringMap(bfParam)
+ renderingConfigOverrides = maps.ToStringMap(bfParam)
}
cp := p.s.ContentSpec.Converters.Get(p.markup)
@@ -705,14 +705,9 @@ func getParam(m resource.ResourceParamsProvider, key string, stringToLower bool)
return helpers.SliceToLower(val)
}
return v
- case map[string]interface{}: // JSON and TOML
- return v
- case map[interface{}]interface{}: // YAML
+ default:
return v
}
-
- //p.s.Log.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
- return nil
}
func getParamToLower(m resource.ResourceParamsProvider, key string) interface{} {
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 6b9c4193d..abceec9c6 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1573,7 +1573,8 @@ baseURL = "https://example.org"
{{ $withStringParam := .Site.GetPage "withstringparam" }}
Author page: {{ $withParam.Param "author.name" }}
-Author page string: {{ $withStringParam.Param "author.name" }}|
+Author name page string: {{ $withStringParam.Param "author.name" }}|
+Author page string: {{ $withStringParam.Param "author" }}|
Author site config: {{ $noParam.Param "author.name" }}
`,
@@ -1603,8 +1604,10 @@ author = "Jo Nesbø"
`)
b.Build(BuildCfg{})
- b.AssertFileContent("public/index.html", "Author page: Ernest Miller Hemingway")
- b.AssertFileContent("public/index.html", "Author page string: |")
- b.AssertFileContent("public/index.html", "Author site config: Kurt Vonnegut")
+ b.AssertFileContent("public/index.html",
+ "Author page: Ernest Miller Hemingway",
+ "Author name page string: Kurt Vonnegut|",
+ "Author page string: Jo Nesbø|",
+ "Author site config: Kurt Vonnegut")
}
diff --git a/hugolib/pages_map.go b/hugolib/pages_map.go
index aba1aa4bf..5af86c95d 100644
--- a/hugolib/pages_map.go
+++ b/hugolib/pages_map.go
@@ -20,6 +20,8 @@ import (
"strings"
"sync"
+ "github.com/gohugoio/hugo/common/maps"
+
radix "github.com/armon/go-radix"
"github.com/spf13/cast"
@@ -359,7 +361,7 @@ func (m *pagesMap) cleanKey(key string) string {
func (m *pagesMap) mergeCascades(b1, b2 *pagesMapBucket) {
if b1.cascade == nil {
- b1.cascade = make(map[string]interface{})
+ b1.cascade = make(maps.Params)
}
if b2 != nil && b2.cascade != nil {
for k, v := range b2.cascade {
diff --git a/hugolib/site.go b/hugolib/site.go
index db0cd2ea5..0b45c4803 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -28,6 +28,8 @@ import (
"strings"
"time"
+ "github.com/gohugoio/hugo/resources/resource"
+
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/hugofs/files"
@@ -581,7 +583,7 @@ func (s *SiteInfo) Taxonomies() interface{} {
return s.s.Taxonomies
}
-func (s *SiteInfo) Params() map[string]interface{} {
+func (s *SiteInfo) Params() maps.Params {
return s.s.Language().Params()
}
@@ -654,14 +656,9 @@ type SiteSocial map[string]string
// Param is a convenience method to do lookups in SiteInfo's Params map.
//
-// This method is also implemented on Page and Node.
+// This method is also implemented on Page.
func (s *SiteInfo) Param(key interface{}) (interface{}, error) {
- keyStr, err := cast.ToStringE(key)
- if err != nil {
- return nil, err
- }
- keyStr = strings.ToLower(keyStr)
- return s.Params()[keyStr], nil
+ return resource.Param(s, nil, key)
}
func (s *SiteInfo) IsMultiLingual() bool {
@@ -1272,7 +1269,7 @@ func (s *Site) getMenusFromConfig() navigation.Menus {
s.Log.DEBUG.Printf("found menu: %q, in site config\n", name)
menuEntry := navigation.MenuEntry{Menu: name}
- ime, err := cast.ToStringMapE(entry)
+ ime, err := maps.ToStringMapE(entry)
if err != nil {
s.Log.ERROR.Printf("unable to process menus in site config\n")
s.Log.ERROR.Println(err)
diff --git a/langs/config.go b/langs/config.go
index 927f3558f..184223650 100644
--- a/langs/config.go
+++ b/langs/config.go
@@ -171,7 +171,7 @@ func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (Languages
i := 0
for lang, langConf := range l {
- langsMap, err := cast.ToStringMapE(langConf)
+ langsMap, err := maps.ToStringMapE(langConf)
if err != nil {
return nil, fmt.Errorf("Language config is not a map: %T", langConf)
@@ -192,7 +192,7 @@ func toSortedLanguages(cfg config.Provider, l map[string]in