Repository: gohugoio/hugo (language: go)

Issue:
Partials does not work with multiple languages and RSS
<!--
Please do not use the issue queue for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the forum:

https://discourse.gohugo.io
-->

<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Hugo are you using (`hugo version`)?

<pre>
v0.146.3
</pre>

### Does this issue reproduce with the latest release?
Yes

### Problem

My site is in Russian and English. The default language is Russian (defaultContentLanguage = "ru"). 

Files: 
**/partials/comment.ru.html
/partials/comment.en.html
/partials/comment.ru.xml
/partials/comment.en.xml**

Fragment from the template /layouts/_default/single.html:
<pre>
{{ partial (print "comment." (default "ru" .Lang) ".html") . }}
</pre>

In version 0.145.0 everything was fine, but after the update it always displays only partial en (comment.en.html). Even in RSS I see comment.en.html (instead of comment.ru.xml or comment.en.xml).


Discussion / hints:
```go
func TestTestIssue13612(t *testing.T) {
	t.Parallel()

	files := `
-- hugo.toml --
disableKinds = ['page','section','sitemap','taxonomy','term']

defaultContentLanguage = 'ru'
defaultContentLanguageInSubdir = true

[languages.ru]
weight = 1

[languages.en]
weight = 2

[outputs]
home = ['html','rss']

-- layouts/_partials/comment.en.html --
layouts/_partials/a.en.html
-- layouts/_partials/a.en.xml --
layouts/_partials/comment.en.xml
-- layouts/_partials/a.ru.html --
layouts/_partials/a.ru.html
-- layouts/_partials/a.ru.xml --
layouts/_partials/a.ru.xml
-- layouts/home.html --
{{ partial (print "comment." (default "ru" .Lang) ".html") . }}
-- layouts/home.rss.xml --
{{ partial (print "comment." (default "ru" .Lang) ".xml") . }}
`

	b := hugolib.Test(t, files)

	b.AssertFileContent("public/en/index.html", "layouts/_partials/a.en.html")
	b.AssertFileContent("public/en/index.xml", "layouts/_partials/a.en.xml")   // fail
	b.AssertFileContent("public/ru/index.html", "layouts/_partials/a.ru.html") // fail
	b.AssertFileContent("public/ru/index.xml", "layouts/_partials/a.ru.xml")   // fail
}
```

Fix patch:
diff --git a/common/paths/pathparser.go b/common/paths/pathparser.go
index 4b3feaa1492..c04f2022e4f 100644
--- a/common/paths/pathparser.go
+++ b/common/paths/pathparser.go
@@ -724,7 +724,7 @@ func (p *Path) IsContentData() bool {
 	return p.pathType == TypeContentData
 }
 
-func (p Path) ForBundleType(t Type) *Path {
+func (p Path) ForType(t Type) *Path {
 	p.pathType = t
 	return &p
 }
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index b501cd9ea94..c51b4a17ad3 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -180,7 +180,7 @@ func (t *pageTrees) collectAndMarkStaleIdentities(p *paths.Path) []identity.Iden
 
 	if p.Component() == files.ComponentFolderContent {
 		// It may also be a bundled content resource.
-		key := p.ForBundleType(paths.TypeContentResource).Base()
+		key := p.ForType(paths.TypeContentResource).Base()
 		tree = t.treeResources
 		nCount = 0
 		tree.ForEeachInDimension(key, doctree.DimensionLanguage.Index(),


Test patch:
diff --git a/tpl/tplimpl/templatestore.go b/tpl/tplimpl/templatestore.go
index bb58c90831e..4266e274aba 100644
--- a/tpl/tplimpl/templatestore.go
+++ b/tpl/tplimpl/templatestore.go
@@ -564,14 +564,21 @@ func (s *TemplateStore) LookupPagesLayout(q TemplateQuery) *TemplInfo {
 
 func (s *TemplateStore) LookupPartial(pth string) *TemplInfo {
 	ti, _ := s.cacheLookupPartials.GetOrCreate(pth, func() (*TemplInfo, error) {
-		d := s.templateDescriptorFromPath(pth)
-		desc := d.Desc
-		if desc.LayoutFromTemplate != "" {
-			panic("shortcode template descriptor must not have a layout")
+		pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, pth).ForType(paths.TypePartial)
+		k1, _, _, desc, err := s.toKeyCategoryAndDescriptor(pi)
+		if err != nil {
+			return nil, err
+		}
+		if desc.OutputFormat == "" && desc.MediaType == "" {
+			// Assume HTML.
+			desc.OutputFormat = s.htmlFormat.Name
+			desc.MediaType = s.htmlFormat.MediaType.Type
+			desc.IsPlainText = s.htmlFormat.IsPlainText
 		}
+
 		best := s.getBest()
 		defer s.putBest(best)
-		s.findBestMatchGet(s.key(path.Join(containerPartials, d.Path)), CategoryPartial, nil, desc, best)
+		s.findBestMatchGet(s.key(path.Join(containerPartials, k1)), CategoryPartial, nil, desc, best)
 		return best.templ, nil
 	})
 
@@ -1486,43 +1493,6 @@ type PathTemplateDescriptor struct {
 	Desc TemplateDescriptor
 }
 
-// templateDescriptorFromPath returns a template descriptor from the given path.
-// This is currently used in partial lookups only.
-func (s *TemplateStore) templateDescriptorFromPath(pth string) PathTemplateDescriptor {
-	var (
-		mt media.Type
-		of output.Format
-	)
-
-	// Common cases.
-	dotCount := strings.Count(pth, ".")
-	if dotCount <= 1 {
-		if dotCount == 0 {
-			// Asume HTML.
-			of, mt = s.resolveOutputFormatAndOrMediaType("html", "")
-		} else {
-			pth = strings.TrimPrefix(pth, "/")
-			ext := path.Ext(pth)
-			pth = strings.TrimSuffix(pth, ext)
-			ext = ext[1:]
-			of, mt = s.resolveOutputFormatAndOrMediaType("", ext)
-		}
-	} else {
-		path := s.opts.PathParser.Parse(files.ComponentFolderLayouts, pth)
-		pth = path.PathNoIdentifier()
-		of, mt = s.resolveOutputFormatAndOrMediaType(path.OutputFormat(), path.Ext())
-	}
-
-	return PathTemplateDescriptor{
-		Path: pth,
-		Desc: TemplateDescriptor{
-			OutputFormat: of.Name,
-			MediaType:    mt.Type,
-			IsPlainText:  of.IsPlainText,
-		},
-	}
-}
-
 // resolveOutputFormatAndOrMediaType resolves the output format and/or media type
 // based on the given output format suffix and media type suffix.
 // Either of the suffixes can be empty, and the function will try to find a match
diff --git a/tpl/tplimpl/templatestore_integration_test.go b/tpl/tplimpl/templatestore_integration_test.go
index b302f5bb305..32e1248fe27 100644
--- a/tpl/tplimpl/templatestore_integration_test.go
+++ b/tpl/tplimpl/templatestore_integration_test.go
@@ -1163,3 +1163,44 @@ All.
 	// Just make sure it doesn't fail.
 	hugolib.Test(t, files)
 }
+
+func TestPartialsLangIssue13612(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','section','sitemap','taxonomy','term']
+
+defaultContentLanguage = 'ru'
+defaultContentLanguageInSubdir = true
+
+[languages.ru]
+weight = 1
+
+[languages.en]
+weight = 2
+
+[outputs]
+home = ['html','rss']
+
+-- layouts/_partials/comment.en.html --
+layouts/_partials/comment.en.html
+-- layouts/_partials/comment.en.xml --
+layouts/_partials/comment.en.xml
+-- layouts/_partials/comment.ru.html --
+layouts/_partials/comment.ru.html
+-- layouts/_partials/comment.ru.xml --
+layouts/_partials/comment.ru.xml
+-- layouts/home.html --
+{{ partial (print "comment." (default "ru" .Lang) ".html") . }}
+-- layouts/home.rss.xml --
+{{ partial (print "comment." (default "ru" .Lang) ".xml") . }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/en/index.html", "layouts/_partials/comment.en.html")
+	b.AssertFileContent("public/en/index.xml", "layouts/_partials/comment.en.xml")   // fail
+	b.AssertFileContent("public/ru/index.html", "layouts/_partials/comment.ru.html") // fail
+	b.AssertFileContent("public/ru/index.xml", "layouts/_partials/comment.ru.xml")   // fail
+}