func (c *Configs) Init() error {
var languages langs.Languages
- defaultContentLanguage := c.Base.DefaultContentLanguage
- for k, v := range c.LanguageConfigMap {
- languageConf := v.Languages[k]
- language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
- if err != nil {
- return err
+
+ var langKeys []string
+ var hasEn bool
+
+ const en = "en"
+
+ for k := range c.LanguageConfigMap {
+ langKeys = append(langKeys, k)
+ if k == en {
+ hasEn = true
}
- languages = append(languages, language)
}
- // Sort the sites by language weight (if set) or lang.
- sort.Slice(languages, func(i, j int) bool {
- li := languages[i]
- lj := languages[j]
+ // Sort the LanguageConfigSlice by language weight (if set) or lang.
+ sort.Slice(langKeys, func(i, j int) bool {
+ ki := langKeys[i]
+ kj := langKeys[j]
+ lki := c.LanguageConfigMap[ki]
+ lkj := c.LanguageConfigMap[kj]
+ li := lki.Languages[ki]
+ lj := lkj.Languages[kj]
if li.Weight != lj.Weight {
return li.Weight < lj.Weight
}
- return li.Lang < lj.Lang
+ return ki < kj
})
- for _, l := range languages {
- c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang])
+ // See issue #13646.
+ defaultConfigLanguageFallback := en
+ if !hasEn {
+ // Pick the first one.
+ defaultConfigLanguageFallback = langKeys[0]
+ }
+
+ if c.Base.DefaultContentLanguage == "" {
+ c.Base.DefaultContentLanguage = defaultConfigLanguageFallback
+ }
+
+ for _, k := range langKeys {
+ v := c.LanguageConfigMap[k]
+ if v.DefaultContentLanguage == "" {
+ v.DefaultContentLanguage = defaultConfigLanguageFallback
+ }
+ c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
+ languageConf := v.Languages[k]
+ language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf)
+ if err != nil {
+ return err
+ }
+ languages = append(languages, language)
}
// Filter out disabled languages.
var languagesDefaultFirst langs.Languages
for _, l := range languages {
- if l.Lang == defaultContentLanguage {
+ if l.Lang == c.Base.DefaultContentLanguage {
languagesDefaultFirst = append(languagesDefaultFirst, l)
}
}
for _, l := range languages {
- if l.Lang != defaultContentLanguage {
+ if l.Lang != c.Base.DefaultContentLanguage {
languagesDefaultFirst = append(languagesDefaultFirst, l)
}
}
return nil
}
+func newDefaultConfig() *Config {
+ return &Config{
+ Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
+ Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
+ RootConfig: RootConfig{
+ Environment: hugo.EnvironmentProduction,
+ TitleCaseStyle: "AP",
+ PluralizeListTitles: true,
+ CapitalizeListTitles: true,
+ StaticDir: []string{"static"},
+ SummaryLength: 70,
+ Timeout: "60s",
+
+ CommonDirs: config.CommonDirs{
+ ArcheTypeDir: "archetypes",
+ ContentDir: "content",
+ ResourceDir: "resources",
+ PublishDir: "public",
+ ThemesDir: "themes",
+ AssetDir: "assets",
+ LayoutDir: "layouts",
+ I18nDir: "i18n",
+ DataDir: "data",
+ },
+ },
+ }
+}
+
// fromLoadConfigResult creates a new Config from res.
func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) {
if !res.Cfg.IsSet("languages") {
// We need at least one
lang := res.Cfg.GetString("defaultContentLanguage")
+ if lang == "" {
+ lang = "en"
+ }
res.Cfg.Set("languages", maps.Params{lang: maps.Params{}})
}
bcfg := res.BaseConfig
cfg := res.Cfg
- all := &Config{}
+ all := newDefaultConfig()
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
if err != nil {
langConfigMap := make(map[string]*Config)
languagesConfig := cfg.GetStringMap("languages")
+
var isMultihost bool
if err := all.CompileConfig(logger); err != nil {
"testing"
qt "github.com/frankban/quicktest"
+ "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/media"
b.Assert(c.IsContentFile("foo.md"), qt.Equals, true)
b.Assert(len(s), qt.Equals, 6)
}
+
+func TestMergeDeep(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+theme = ["theme1", "theme2"]
+_merge = "deep"
+-- themes/theme1/hugo.toml --
+[sitemap]
+filename = 'mysitemap.xml'
+[services]
+[services.googleAnalytics]
+id = 'foo bar'
+[taxonomies]
+ foo = 'bars'
+-- themes/theme2/config/_default/hugo.toml --
+[taxonomies]
+ bar = 'baz'
+-- layouts/home.html --
+GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
+
+`
+
+ b := hugolib.Test(t, files)
+
+ conf := b.H.Configs
+ base := conf.Base
+
+ b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction)
+ b.Assert(base.BaseURL, qt.Equals, "https://example.com")
+ b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml")
+ b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"})
+
+ b.AssertFileContent("public/index.html", "GA ID: foo bar.")
+}
+
+func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+[languages]
+[languages.nn]
+weight = 20
+[languages.sv]
+weight = 10
+[languages.sv.taxonomies]
+ tag = "taggar"
+-- layouts/all.html --
+All.
+`
+
+ b := hugolib.Test(t, files)
+
+ b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
+}
key: "sitemap",
decode: func(d decodeWeight, p decodeConfig) error {
var err error
- p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key))
+ if p.p.IsSet(d.key) {
+ p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key))
+ }
return err
},
},
"taxonomies": {
key: "taxonomies",
decode: func(d decodeWeight, p decodeConfig) error {
- p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
+ if p.p.IsSet(d.key) {
+ p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
+ }
return nil
},
},
}
// Validate defaultContentLanguage.
- var found bool
- for lang := range p.c.Languages {
- if lang == p.c.DefaultContentLanguage {
- found = true
- break
+ if p.c.DefaultContentLanguage != "" {
+ var found bool
+ for lang := range p.c.Languages {
+ if lang == p.c.DefaultContentLanguage {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
}
- }
- if !found {
- return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
}
return nil
func (l configLoader) applyDefaultConfig() error {
defaultSettings := maps.Params{
- "baseURL": "",
- "cleanDestinationDir": false,
- "watch": false,
- "contentDir": "content",
- "resourceDir": "resources",
- "publishDir": "public",
- "publishDirOrig": "public",
- "themesDir": "themes",
- "assetDir": "assets",
- "layoutDir": "layouts",
- "i18nDir": "i18n",
- "dataDir": "data",
- "archetypeDir": "archetypes",
- "configDir": "config",
- "staticDir": "static",
- "buildDrafts": false,
- "buildFuture": false,
- "buildExpired": false,
- "params": maps.Params{},
- "environment": hugo.EnvironmentProduction,
- "uglyURLs": false,
- "verbose": false,
- "ignoreCache": false,
- "canonifyURLs": false,
- "relativeURLs": false,
- "removePathAccents": false,
- "titleCaseStyle": "AP",
- "taxonomies": maps.Params{"tag": "tags", "category": "categories"},
- "permalinks": maps.Params{},
- "sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"},
- "menus": maps.Params{},
- "disableLiveReload": false,
- "pluralizeListTitles": true,
- "capitalizeListTitles": true,
- "forceSyncStatic": false,
- "footnoteAnchorPrefix": "",
- "footnoteReturnLinkContents": "",
- "newContentEditor": "",
- "paginate": 0, // Moved into the paginator struct in Hugo v0.128.0.
- "paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0.
- "summaryLength": 70,
- "rssLimit": -1,
- "sectionPagesMenu": "",
- "disablePathToLower": false,
- "hasCJKLanguage": false,
- "enableEmoji": false,
- "defaultContentLanguage": "en",
- "defaultContentLanguageInSubdir": false,
- "enableMissingTranslationPlaceholders": false,
- "enableGitInfo": false,
- "ignoreFiles": make([]string, 0),
- "disableAliases": false,
- "debug": false,
- "disableFastRender": false,
- "timeout": "60s",
- "timeZone": "",
- "enableInlineShortcodes": false,
+ // These dirs are used early/before we build the config struct.
+ "themesDir": "themes",
+ "configDir": "config",
}
l.cfg.SetDefaults(defaultSettings)
"fmt"
"net/http"
"regexp"
+ "slices"
"sort"
"strings"
"github.com/gohugoio/hugo/common/herrors"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"
- "slices"
)
type BaseConfig struct {
if c.RSS.Limit == 0 {
c.RSS.Limit = cfg.GetInt(rssLimitKey)
+ if c.RSS.Limit == 0 {
+ c.RSS.Limit = -1
+ }
}
return
})
})
- // Issue #8724
+ // Issue #8724 ##13643
for _, mergeStrategy := range []string{"none", "shallow"} {
c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) {
smapConfigTempl := `[sitemap]
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
} else {
- b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"})
+ b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: 0.5, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
}
})