Repository: gohugoio/hugo (language: go)

Issue:
Revert auto-capitalization of term titles
This front matter:

```text
tags = ['tag-a','tag-b']
```

Produces this taxonomy page:

![image](https://github.com/gohugoio/hugo/assets/335669/7fd9b853-165a-4adf-9ae0-30da4ff1cec8)

And this term page:

![image](https://github.com/gohugoio/hugo/assets/335669/92829f34-90ef-4d7e-bbdf-75ab29f3fca0)

#### Alternative 1

You can change your templates from this:

```text
{{ range .Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
```

to this:

```text
{{ range .Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .Data.Term }}</a></h2>
{{ end }}
```

...but that means you need one or two more templates to handle these page kinds.

#### Alternative 2

Do this in your site configuration...

```
titleCaseStyle = 'none'
```

...but that affects how the `strings.Title` function behaves as well.

#### Alternative 3

Create a term page for every term and set the title to whatever you want...

```text
hugo new content tags/foo/_index.md
```

...but you have to do that for every term.



Discussion / hints:
<img width="202" alt="image" src="https://github.com/gohugoio/hugo/assets/394382/016299ae-f9dd-453b-af7f-ee8e1fe36b22">

What's wrong with the above? And why is the alternative better?
As the original reporter of this issue on the Hugo forum, I'll chime in here.

> What's wrong with the above? And why is the alternative better?

I personally like having my tags rendered in lowercase, so I wrote them as lowercase in my YAML front matter. When I look at my [tags page](https://lorendb.dev/tags), now I see some tags like `Ksp2`. This is clearly incorrect, since KSP2 is an abbreviation for Kerbal Space Program 2. With this update, I'm forced to either uppercase tags like `ksp2` or use a hacky workaround that may break other parts of my site.

I think auto-capitalizing things like this is also a bad idea in general. When I write text, I assume that it will be rendered without changes to its case, so if I write `tags = ["foo", "bar", "ksp2"]`, I intend for them to be rendered in all lowercase.
OK, that's fine then (but I'm not doing the code work for this one :-))

I understand that we are also auto-capitalizing automatic section pages titles, but conceptually (from a content author's standpoint) section titles and terms are different things. There's also demand for disabling auto-capitalization of automatic section page titles:

#9793

We could do something like that instead, letting it apply to section, term, and taxonomy pages.

---

EDIT: The more I think about #9793, the more I like it. It's a good compromise. Casing of automatic titles will be consistent for section, taxonomy, and term. 

Fix patch:
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index 21d12b0c78b..469a164cf64 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -538,6 +538,9 @@ type RootConfig struct {
 	// Note that this currently only works for English, but you can provide your own title in the content file's front matter.
 	PluralizeListTitles bool
 
+	// Whether to capitalize automatic page titles, applicable to section, taxonomy, and term pages.
+	CapitalizeListTitles bool
+
 	// Make all relative URLs absolute using the baseURL.
 	// <docsmeta>{"identifiers": ["baseURL"] }</docsmeta>
 	CanonifyURLs bool
diff --git a/config/allconfig/load.go b/config/allconfig/load.go
index cb267422f15..8675ced231e 100644
--- a/config/allconfig/load.go
+++ b/config/allconfig/load.go
@@ -189,6 +189,7 @@ func (l configLoader) applyDefaultConfig() error {
 		"menus":                                maps.Params{},
 		"disableLiveReload":                    false,
 		"pluralizeListTitles":                  true,
+		"CapitalizeListTitles":                 true,
 		"forceSyncStatic":                      false,
 		"footnoteAnchorPrefix":                 "",
 		"footnoteReturnLinkContents":           "",
diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
index 03f4d4d1965..082e3145a49 100644
--- a/hugolib/page__meta.go
+++ b/hugolib/page__meta.go
@@ -446,7 +446,7 @@ title: "My Title"
 params:
   build: "My Build"
 ---
-´   
+´
 
 `
 		}
@@ -746,15 +746,26 @@ func (p *pageMeta) applyDefaultValues() error {
 			if p.s.conf.PluralizeListTitles {
 				sectionName = flect.Pluralize(sectionName)
 			}
-			p.pageConfig.Title = p.s.conf.C.CreateTitle(sectionName)
+			if p.s.conf.CapitalizeListTitles {
+				sectionName = p.s.conf.C.CreateTitle(sectionName)
+			}
+			p.pageConfig.Title = sectionName
 		case kinds.KindTerm:
 			if p.term != "" {
-				p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
+				if p.s.conf.CapitalizeListTitles {
+					p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
+				} else {
+					p.pageConfig.Title = p.term
+				}
 			} else {
 				panic("term not set")
 			}
 		case kinds.KindTaxonomy:
-			p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
+			if p.s.conf.CapitalizeListTitles {
+				p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
+			} else {
+				p.pageConfig.Title = strings.Replace(p.pathInfo.Unnormalized().BaseNameNoIdentifier(), "-", " ", -1)
+			}
 		case kinds.KindStatus404:
 			p.pageConfig.Title = "404 Page not found"
 		}


Test patch:
diff --git a/hugolib/page__meta_test.go b/hugolib/page__meta_test.go
new file mode 100644
index 00000000000..d4b66ed9a29
--- /dev/null
+++ b/hugolib/page__meta_test.go
@@ -0,0 +1,59 @@
+// Copyright 2024 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib_test
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/gohugoio/hugo/hugolib"
+)
+
+// Issue 9793
+// Issue 12115
+func TestListTitles(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['home','rss','sitemap']
+capitalizeListTitles = true
+pluralizeListTitles = true
+[taxonomies]
+tag = 'tags'
+-- content/section-1/page-1.md --
+---
+title: page-1
+tags: 'tag-a'
+---
+-- layouts/_default/list.html --
+{{ .Title }}
+-- layouts/_default/single.html --
+{{ .Title }}
+	`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/section-1/index.html", "Section-1s")
+	b.AssertFileContent("public/tags/index.html", "Tags")
+	b.AssertFileContent("public/tags/tag-a/index.html", "Tag-A")
+
+	files = strings.Replace(files, "true", "false", -1)
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/section-1/index.html", "section-1")
+	b.AssertFileContent("public/tags/index.html", "tags")
+	b.AssertFileContent("public/tags/tag-a/index.html", "tag-a")
+}