shithub: hugo

Download patch

ref: ae742cb1bdf35b81aa0ede5453da6b0c4a4fccf2
parent: feeed073c3320b09fb38168ce272ac88b987f1d2
author: Bjørn Erik Pedersen <[email protected]>
date: Tue Jan 30 12:51:18 EST 2018

Fix language params handling

This fixes some issues with language params handling by separating params from configuration values per language.

This means that you can now do this:

```toml
[languages]
[languages.en]
languageName = "English"
weight = 1
title = "My Cool Site"
[languages.en.params]
myParam = "Hi!"
```

This is not a breaking change, but the above is a less suprising way of configuring custom params.

It also fixes some hard-to-debug corner-cases in multilingual sites.

Fixes #4356
Fixes #4352

--- a/helpers/language.go
+++ b/helpers/language.go
@@ -16,7 +16,6 @@
 import (
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/gohugoio/hugo/config"
 	"github.com/spf13/cast"
@@ -42,9 +41,16 @@
 	Title        string
 	Weight       int
 
-	Cfg        config.Provider
-	params     map[string]interface{}
-	paramsInit sync.Once
+	Cfg config.Provider
+
+	// These are params declared in the [params] section of the language merged with the
+	// site's params, the most specific (language) wins on duplicate keys.
+	params map[string]interface{}
+
+	// These are config values, i.e. the settings declared outside of the [params] section of the language.
+	// This is the map Hugo looks in when looking for configuration values (baseURL etc.).
+	// Values in this map can also be fetched from the params map above.
+	settings map[string]interface{}
 }
 
 func (l *Language) String() string {
@@ -53,15 +59,14 @@
 
 // NewLanguage creates a new language.
 func NewLanguage(lang string, cfg config.Provider) *Language {
+	// Note that language specific params will be overridden later.
+	// We should improve that, but we need to make a copy:
 	params := make(map[string]interface{})
-	// Merge with global config.
-	globalParams := cfg.GetStringMap("params")
-	for k, v := range globalParams {
-		if _, ok := params[k]; !ok {
-			params[k] = v
-		}
+	for k, v := range cfg.GetStringMap("params") {
+		params[k] = v
 	}
-	l := &Language{Lang: lang, Cfg: cfg, params: params}
+	ToLowerMap(params)
+	l := &Language{Lang: lang, Cfg: cfg, params: params, settings: make(map[string]interface{})}
 	return l
 }
 
@@ -115,7 +120,7 @@
 	return false
 }
 
-// SetParam sets param with the given key and value.
+// SetParam sets a param with the given key and value.
 // SetParam is case-insensitive.
 func (l *Language) SetParam(k string, v interface{}) {
 	l.params[strings.ToLower(k)] = v
@@ -166,7 +171,7 @@
 	}
 	key = strings.ToLower(key)
 	if !globalOnlySettings[key] {
-		if v, ok := l.params[key]; ok {
+		if v, ok := l.settings[key]; ok {
 			return v
 		}
 	}
@@ -179,7 +184,7 @@
 		panic("language not set")
 	}
 	key = strings.ToLower(key)
-	l.params[key] = value
+	l.settings[key] = value
 }
 
 // IsSet checks whether the key is set in the language or the related config store.
@@ -188,7 +193,7 @@
 
 	key = strings.ToLower(key)
 	if !globalOnlySettings[key] {
-		if _, ok := l.params[key]; ok {
+		if _, ok := l.settings[key]; ok {
 			return true
 		}
 	}
--- a/helpers/language_test.go
+++ b/helpers/language_test.go
@@ -23,11 +23,24 @@
 func TestGetGlobalOnlySetting(t *testing.T) {
 	v := viper.New()
 	lang := NewDefaultLanguage(v)
-	lang.SetParam("defaultContentLanguageInSubdir", false)
-	lang.SetParam("paginatePath", "side")
+	lang.Set("defaultContentLanguageInSubdir", false)
+	lang.Set("paginatePath", "side")
 	v.Set("defaultContentLanguageInSubdir", true)
 	v.Set("paginatePath", "page")
 
 	require.True(t, lang.GetBool("defaultContentLanguageInSubdir"))
 	require.Equal(t, "side", lang.GetString("paginatePath"))
+}
+
+func TestLanguageParams(t *testing.T) {
+	assert := require.New(t)
+
+	v := viper.New()
+	v.Set("p1", "p1cfg")
+
+	lang := NewDefaultLanguage(v)
+	lang.SetParam("p1", "p1p")
+
+	assert.Equal("p1p", lang.Params()["p1"])
+	assert.Equal("p1cfg", lang.Get("p1"))
 }
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -141,6 +141,9 @@
 
 func TestMultiSitesWithTwoLanguages(t *testing.T) {
 	t.Parallel()
+
+	assert := require.New(t)
+
 	mm := afero.NewMemMapFs()
 
 	writeToFs(t, mm, "config.toml", `
@@ -152,11 +155,15 @@
 languageName = "Nynorsk"
 weight = 1
 title = "Tittel på Nynorsk"
+[languages.nn.params]
+p1 = "p1nn"
 
 [languages.en]
 title = "Title in English"
 languageName = "English"
 weight = 2
+[languages.en.params]
+p1 = "p1en"
 `,
 	)
 
@@ -176,15 +183,24 @@
 	// Add some data
 	writeSource(t, fs, filepath.Join("data", "hugo.toml"), "slogan = \"Hugo Rocks!\"")
 
-	require.NoError(t, sites.Build(BuildCfg{}))
-	require.Len(t, sites.Sites, 2)
+	assert.NoError(sites.Build(BuildCfg{}))
+	assert.Len(sites.Sites, 2)
 
 	nnSite := sites.Sites[0]
-	nnSiteHome := nnSite.getPage(KindHome)
-	require.Len(t, nnSiteHome.AllTranslations(), 2)
-	require.Len(t, nnSiteHome.Translations(), 1)
-	require.True(t, nnSiteHome.IsTranslated())
+	nnHome := nnSite.getPage(KindHome)
+	assert.Len(nnHome.AllTranslations(), 2)
+	assert.Len(nnHome.Translations(), 1)
+	assert.True(nnHome.IsTranslated())
 
+	enHome := sites.Sites[1].getPage(KindHome)
+
+	p1, err := enHome.Param("p1")
+	assert.NoError(err)
+	assert.Equal("p1en", p1)
+
+	p1, err = nnHome.Param("p1")
+	assert.NoError(err)
+	assert.Equal("p1nn", p1)
 }
 
 //
--- a/hugolib/multilingual.go
+++ b/hugolib/multilingual.go
@@ -111,10 +111,20 @@
 				language.LanguageName = cast.ToString(v)
 			case "weight":
 				language.Weight = cast.ToInt(v)
+			case "params":
+				m := cast.ToStringMap(v)
+				// Needed for case insensitive fetching of params values
+				helpers.ToLowerMap(m)
+				for k, vv := range m {
+					language.SetParam(k, vv)
+				}
 			}
 
 			// Put all into the Params map
 			language.SetParam(loki, v)
+
+			// Also set it in the configuration map (for baseURL etc.)
+			language.Set(loki, v)
 		}
 
 		langs[i] = language