Skip to content

Commit

Permalink
Fix language params handling
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bep committed Jan 30, 2018
1 parent feeed07 commit ae742cb
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 23 deletions.
35 changes: 20 additions & 15 deletions helpers/language.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package helpers
import (
"sort"
"strings"
"sync"

"github.com/gohugoio/hugo/config"
"github.com/spf13/cast"
Expand All @@ -42,9 +41,16 @@ type Language struct {
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 {
Expand All @@ -53,15 +59,14 @@ func (l *Language) String() string {

// 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
}

Expand Down Expand Up @@ -115,7 +120,7 @@ func (l Languages) IsMultihost() bool {
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
Expand Down Expand Up @@ -166,7 +171,7 @@ func (l *Language) GetLocal(key string) interface{} {
}
key = strings.ToLower(key)
if !globalOnlySettings[key] {
if v, ok := l.params[key]; ok {
if v, ok := l.settings[key]; ok {
return v
}
}
Expand All @@ -179,7 +184,7 @@ func (l *Language) Set(key string, value interface{}) {
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.
Expand All @@ -188,7 +193,7 @@ func (l *Language) IsSet(key string) bool {

key = strings.ToLower(key)
if !globalOnlySettings[key] {
if _, ok := l.params[key]; ok {
if _, ok := l.settings[key]; ok {
return true
}
}
Expand Down
17 changes: 15 additions & 2 deletions helpers/language_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,24 @@ import (
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"))
}
28 changes: 22 additions & 6 deletions hugolib/hugo_sites_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {

func TestMultiSitesWithTwoLanguages(t *testing.T) {
t.Parallel()

assert := require.New(t)

mm := afero.NewMemMapFs()

writeToFs(t, mm, "config.toml", `
Expand All @@ -152,11 +155,15 @@ defaultContentLanguage = "nn"
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"
`,
)

Expand All @@ -176,15 +183,24 @@ weight = 2
// 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)
}

//
Expand Down
10 changes: 10 additions & 0 deletions hugolib/multilingual.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,20 @@ func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (helpers.L
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
Expand Down

0 comments on commit ae742cb

Please sign in to comment.