]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix auto-creation of root sections in multilingual sites
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 6 Apr 2026 16:01:22 +0000 (18:01 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 7 Apr 2026 11:58:06 +0000 (13:58 +0200)
Fixes #14681

Co-authored-by: Joe Mooring <joe@mooring.com>
hugolib/content_map_page_assembler.go
hugolib/language_content_dir_test.go

index 5a61795c7c274e100ad257cee97ee4bc1f8729ca..db0fb0ede7ba1e3b32d7670c5f1d9089c3dbacc4 100644 (file)
@@ -497,14 +497,14 @@ func (a *allPagesAssembler) doCreatePages(prefix string, depth int) error {
 
                isTaxonomy := !a.h.getFirstTaxonomyConfig(s).IsZero()
                isRootSection := !isTaxonomy && level == 1 && cnh.isBranchNode(n)
-
-               if isRootSection {
-                       // This is a root section.
-                       a.seenRootSections.SetIfAbsent(cnh.PathInfo(n).Section(), true)
-               } else if !isTaxonomy {
+               if !isTaxonomy {
                        p := cnh.PathInfo(n)
                        rootSection := p.Section()
                        _, err := a.seenRootSections.GetOrCreate(rootSection, func() (bool, error) {
+                               if isRootSection {
+                                       return true, nil
+                               }
+
                                // Try to preserve the original casing if possible.
                                sectionUnnormalized := p.Unnormalized().Section()
                                rootSectionPath := a.h.Conf.PathParser().Parse(files.ComponentFolderContent, "/"+sectionUnnormalized+"/_index.md")
@@ -520,6 +520,51 @@ func (a *allPagesAssembler) doCreatePages(prefix string, depth int) error {
                                        return true, err
                                }
                                treePages.InsertRaw(rootSectionPath.Base(), rootSectionPages)
+
+                               // Collect vectors from descendant pages to create section
+                               // pages for all languages that have content in this section.
+                               nm, replaced := contentNodeToContentNodesPage(rootSectionPages)
+                               if replaced {
+                                       treePages.InsertRaw(rootSectionPath.Base(), nm)
+                               }
+                               missingVectors := sitesmatrix.Vectors{}
+                               pw.WalkContext.AddEventListener("sitesmatrix", rootSectionPath.Base(),
+                                       func(e *doctree.Event[contentNode]) {
+                                               if cnh.isBranchNode(e.Source) && !a.h.getFirstTaxonomyConfig(e.Path).IsZero() {
+                                                       return
+                                               }
+                                               n := e.Source
+                                               e.StopPropagation()
+                                               n.forEeachContentNode(
+                                                       func(vec sitesmatrix.Vector, nn contentNode) bool {
+                                                               if _, found := nm[vec]; !found {
+                                                                       missingVectors[vec] = struct{}{}
+                                                               }
+                                                               return true
+                                                       })
+                                       },
+                               )
+                               pw.WalkContext.HooksPost().Push(
+                                       func() error {
+                                               if len(missingVectors) > 0 {
+                                                       vec := missingVectors.VectorSample()
+                                                       nm[vec] = &pageMetaSource{
+                                                               pathInfo:            rootSectionPath,
+                                                               sitesMatrixBase:     missingVectors,
+                                                               sitesMatrixBaseOnly: true,
+                                                               pageConfigSource: &pagemeta.PageConfigEarly{
+                                                                       Kind: kinds.KindSection,
+                                                               },
+                                                       }
+                                                       _, _, err := transformPages(rootSectionPath.Base(), nm, cascades)
+                                                       if err != nil {
+                                                               return err
+                                                       }
+                                               }
+                                               return nil
+                                       },
+                               )
+
                                return true, nil
                        })
                        if err != nil {
index 53bd8de2cb00883577bfbe103eb5173bb445b509..888f592f5c98db7046f69dd8dafc3daed6442a57 100644 (file)
@@ -171,6 +171,65 @@ title: "p4 theme (nl)"
        b.AssertFileContent("public/en/index.html", `home (en): en: p1 (en)|p2 (en)|p3 (en)|:END`)
 }
 
+// Issue 14681
+func TestPublishMultilingualSectionCreation(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+capitalizeListTitles = false
+pluralizeListTitles = false
+disableKinds = ['rss','sitemap','taxonomy','term']
+
+defaultContentLanguage = "fr"
+defaultContentLanguageInSubdir = true
+
+[languages.fr]
+contentDir = "content/fr"
+weight = 1
+
+[languages.en]
+contentDir = "content/en"
+weight = 2
+
+[languages.de]
+contentDir = "content/de"
+weight = 3
+-- layouts/home.html --
+HOME {{ .Language.Name }}
+-- layouts/page.html --
+{{ .Title }} {{ .Language.Name }}
+-- layouts/section.html --
+{{ .Title }} {{ .Language.Name }}
+-- content/de/s1/p1.md --
+---
+title: p1
+---
+-- content/en/s1/p2.md --
+---
+title: p2
+---
+-- content/fr/s1/p3.md --
+---
+title: p3
+---
+`
+
+       b := Test(t, files)
+
+       b.AssertFileContent("public/de/index.html", "HOME de")
+       b.AssertFileContent("public/de/s1/index.html", "s1 de")
+       b.AssertFileContent("public/de/s1/p1/index.html", "p1 de")
+
+       b.AssertFileContent("public/en/index.html", "HOME en")
+       b.AssertFileContent("public/en/s1/index.html", "s1 en") // fail (file does not exist)
+       b.AssertFileContent("public/en/s1/p2/index.html", "p2 en")
+
+       b.AssertFileContent("public/fr/index.html", "HOME fr")
+       b.AssertFileContent("public/fr/s1/index.html", "s1 fr") // fail (file does not exist)
+       b.AssertFileContent("public/fr/s1/p3/index.html", "p3 fr")
+}
+
 // Issue 13993
 func TestIssue13993(t *testing.T) {
        t.Parallel()