]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix some default site redirect woes
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 11 Jan 2026 14:30:33 +0000 (15:30 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 11 Jan 2026 20:50:10 +0000 (21:50 +0100)
* The fix for #14357 yesterday sadly had the assumption that default language/version/role always was the first site in the sites matrix. This is common, but not always true.
* Also, that fix forgot to add a redirect the other way when `defaultContentLanguageInSubdir` was disabled, e.g. from `/en/` to `/`.
* This commit also renames config option `DisableDefaultDimensionRedirect` to `DisableDefaultSiteRedirect`. This is stricly a breaking change, but it's only been out for a day, and the old name didn't make much sense.

Fixes #14361

.prettierignore [new file with mode: 0644]
config/allconfig/allconfig.go
hugolib/site.go
hugolib/site_render.go
hugolib/sitesmatrix/sitematrix_integration_test.go
tpl/tplimpl/embedded/templates/alias.html

diff --git a/.prettierignore b/.prettierignore
new file mode 100644 (file)
index 0000000..838f027
--- /dev/null
@@ -0,0 +1 @@
+tpl/tplimpl/embedded/templates/**
\ No newline at end of file
index 01be23fc68503569319c872e710bceba17d37dd0..118f57d695c1a4e284fd590dad641cb17fd1fe9c 100644 (file)
@@ -607,12 +607,13 @@ type RootConfig struct {
        DefaultOutputFormat string
 
        // Disable generation of redirect to the default language when DefaultContentLanguageInSubdir is enabled.
-       // Note that this currently is an alias for DisableDefaultDimensionRedirect introduced in v0.154.4.
+       // Note that this currently is an alias for DisableDefaultSiteRedirect introduced in v0.154.5.
        // It's not obvious how a more fine grained setup would work.
        DisableDefaultLanguageRedirect bool
 
-       // Disable generation of redirect to the default dimension when DefaultContentRoleInSubdir or DefaultContentVersionInSubdir or DefaultContentLanguageInSubdir is enabled.
-       DisableDefaultDimensionRedirect bool
+       // Disable generation of redirects to the default site when DefaultContentRoleInSubdir or DefaultContentVersionInSubdir or DefaultContentLanguageInSubdir is enabled.
+       // The default site is the site is the combination of defaultContentLanguage, defaultContentVersion and defaultContentRole.
+       DisableDefaultSiteRedirect bool
 
        // Disable creation of alias redirect pages.
        DisableAliases bool
index 6dbe307bf77d0ee64b6184c1f765781f2f7a9226..849d48dcccd93d70aa01b03c5af227f29cec0821 100644 (file)
@@ -138,7 +138,8 @@ func (s *Site) resolveDimensionNames() types.Strings3 {
 }
 
 type siteLanguageVersionRole struct {
-       siteVector sitesmatrix.Vector
+       siteVector        sitesmatrix.Vector
+       isDefaultLanguage bool
 
        roleInternal roles.RoleInternal
        role         roles.Role
@@ -186,6 +187,10 @@ func (s siteLanguageVersionRole) Version() versions.Version {
        return s.version
 }
 
+func (s siteLanguageVersionRole) isDefault() bool {
+       return s.isDefaultLanguage && s.roleInternal.Default && s.versionInternal.Default
+}
+
 func (s *Site) Debug() {
        fmt.Println("Debugging site", s.Lang(), "=>")
        // fmt.Println(s.pageMap.testDump())
@@ -341,7 +346,8 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
                        frontmatterHandler: frontmatterHandler,
                        store:              hstore.NewScratch(),
                        siteLanguageVersionRole: &siteLanguageVersionRole{
-                               siteVector: sitesmatrix.Vector{i, 0, 0},
+                               isDefaultLanguage: language.IsDefault(),
+                               siteVector:        sitesmatrix.Vector{i, 0, 0},
                        },
                }
 
index 45f24a624bc27fed7008f88815979320d25605c2..0edb5632964c645de3b16cb4b69db60323f47c16 100644 (file)
@@ -203,8 +203,8 @@ func pageRenderer(
                        }
                }
 
-               if p.IsHome() && p.outputFormat().IsHTML && s.siteVector.IsFirst() {
-                       if err = s.renderDefaultDimensionRedirect(p); err != nil {
+               if p.IsHome() && p.outputFormat().IsHTML && s.isDefault() {
+                       if err = s.renderDefaultSiteRedirect(p); err != nil {
                                if sendErr(err) {
                                        continue
                                } else {
@@ -368,18 +368,17 @@ func (s *Site) renderAliases() error {
        return w.Walk(context.TODO())
 }
 
-// renderDefaultDimensionRedirect creates a redirect to the main dimension's home,
+// renderDefaultSiteRedirect creates a redirect to the default site's home,
 // depending on if it lives in sub folder (e.g. /en) or not.
-func (s *Site) renderDefaultDimensionRedirect(home *pageState) error {
-       if s.conf.DisableDefaultLanguageRedirect || s.conf.DisableDefaultDimensionRedirect {
+// The default site is the site is the combination of defaultContentLanguage,
+// defaultContentVersion and defaultContentRole.
+func (s *Site) renderDefaultSiteRedirect(home *pageState) error {
+       if s.conf.DisableDefaultLanguageRedirect || s.conf.DisableDefaultSiteRedirect {
                return nil
        }
 
-       shouldAdd := s.conf.DefaultContentLanguageInSubdir && !s.Conf.IsMultihost()
-       shouldAdd = shouldAdd || s.conf.DefaultContentVersionInSubdir || s.conf.DefaultContentRoleInSubdir
-       if !shouldAdd {
-               return nil
-       }
+       addRedirectInRoot := s.conf.DefaultContentLanguageInSubdir && !s.Conf.IsMultihost()
+       addRedirectInRoot = addRedirectInRoot || s.conf.DefaultContentVersionInSubdir || s.conf.DefaultContentRoleInSubdir
 
        homeLink := home.pageOutput.targetPaths().Link // This doesn't have any baseURL paths in it.
 
@@ -388,10 +387,20 @@ func (s *Site) renderDefaultDimensionRedirect(home *pageState) error {
 
        var ps []string
        if of.Path != "" {
-               // For OutputFormats with a path, creating more than one alias will easily create path clashes without much value.
-               ps = []string{paths.AddLeadingAndTrailingSlash(of.Path)}
+               if addRedirectInRoot {
+                       // For OutputFormats with a path, creating more than one alias will easily create path clashes without much value.
+                       ps = []string{paths.AddLeadingAndTrailingSlash(of.Path)}
+               }
        } else {
-               ps = []string{"/"}
+               if addRedirectInRoot {
+                       ps = append(ps, "/")
+               }
+
+               if s.Conf.IsMultilingual() && !s.conf.DefaultContentLanguageInSubdir && !s.Conf.IsMultihost() {
+                       // Create redirect from e.g. /en => /
+                       ps = append(ps, homeLink+s.Lang()+"/")
+               }
+
                // /guest/v1.0.0/en/
                //    /,/guest/,/guest/v1.0.0  = /guest/v1.0.0/en/
                // /guest/v1.0.0/
index c7b6a237e650cb1394ed5dacecbb0fe119507acd..60a3e8a11cffc0e1e80fe8d5ca2ac95647b8a516 100644 (file)
@@ -1281,8 +1281,23 @@ defaultContentLanguageInSubDir = true
 defaultContentVersionInSubDir = true
 defaultContentRoleInSubDir = true
 disableDefaultLanguageRedirect = false
-disableDefaultDimensionRedirect = false
+disableDefaultSiteRedirect = false
 defaultContentLanguage = "en"
+defaultContentVersion = "v1.0.0"
+defaultContentRole = "guest"
+[languages]
+[languages.en]
+weight = 2
+[languages.nn]
+weight = 1
+[versions]
+[versions."v1.0.0"]
+weight = 2
+[versions."v2.0.0"]
+weight = 1
+[roles]
+[roles.guest]
+[roles.member]
 [outputs]
 home = [ 'rss', 'amp', 'html']
 -- layouts/home.html --
@@ -1334,7 +1349,7 @@ func TestSitesMatrixRedirectsDisableDefaultLanguageRedirect(t *testing.T) {
        }
 
        runTest(strings.ReplaceAll(files, "disableDefaultLanguageRedirect = false", "disableDefaultLanguageRedirect = true"))
-       runTest(strings.ReplaceAll(files, "disableDefaultDimensionRedirect = false", "disableDefaultDimensionRedirect = true"))
+       runTest(strings.ReplaceAll(files, "disableDefaultSiteRedirect = false", "disableDefaultSiteRedirect = true"))
 }
 
 func TestSitesMatrixRedirectsDefaultContentVersionInBase(t *testing.T) {
@@ -1361,7 +1376,7 @@ defaultContentLanguageInSubDir = true
 defaultContentVersionInSubDir = true
 defaultContentRoleInSubDir = true
 disableDefaultLanguageRedirect = false
-disableDefaultDimensionRedirect = false
+disableDefaultSiteRedirect = false
 defaultContentLanguage = "en"
 [languages]
 [languages.en]
@@ -1384,3 +1399,34 @@ Home.
        b.AssertFileContent("public/en/guest/v1.0.0/index.html", "Home.")
        b.AssertFileContent("public/en/guest/v1.0.0/amp/index.html", "Home.")
 }
+
+func TestSitesMatrixRedirectsDefaultContentLanguageInBase(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.yml --
+baseURL: https://example.org
+disableKinds: [taxonomy]
+defaultContentVersionInSubDir: true
+defaultContentRoleInSubDir: true
+defaultContentLanguage: en
+languages:
+  en:
+    languageName: English
+  bn:
+    languageName: Bengali
+  fr:
+    languageName: French
+-- layouts/all.html --
+All.
+
+`
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContent("public/guest/v1.0.0/en/index.html", "url=https://example.org/guest/v1.0.0/\"", `lang="en"`)
+
+       files = strings.ReplaceAll(files, "defaultContentVersionInSubDir: true", "defaultContentVersionInSubDir: false")
+
+       b = hugolib.Test(t, files)
+       b.AssertFileContent("public/guest/en/index.html", "url=https://example.org/guest/\"")
+}
index 4b3ee33eb24b484ea268c762dad5de05da5a7d65..9d5db096262b340dfbe30d9e1e1b8442ae9dbc6d 100644 (file)
@@ -1,4 +1,4 @@
-<!doctype html>
+<!DOCTYPE html>
 <html lang="{{ site.Language.LanguageCode }}">
   <head>
     <title>{{ .Permalink }}</title>