summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/content/getting-started/configuration.md4
-rw-r--r--helpers/general.go25
-rw-r--r--helpers/general_test.go15
-rw-r--r--hugolib/config.go1
-rw-r--r--hugolib/site.go11
-rw-r--r--tpl/strings/init.go1
-rw-r--r--tpl/strings/init_test.go3
-rw-r--r--tpl/strings/strings.go9
-rw-r--r--tpl/strings/strings_test.go3
-rw-r--r--vendor/vendor.json12
10 files changed, 77 insertions, 7 deletions
diff --git a/docs/content/getting-started/configuration.md b/docs/content/getting-started/configuration.md
index 5aa5160e0..55e2f1cdc 100644
--- a/docs/content/getting-started/configuration.md
+++ b/docs/content/getting-started/configuration.md
@@ -156,6 +156,10 @@ themesDir: "themes"
theme: ""
title: ""
# if true, use /filename.html instead of /filename/
+# Title Case style guide for the title func and other automatic title casing in Hugo.
+// Valid values are "AP" (default), "Chicago" and "Go" (which was what you had in Hugo <= 0.25.1).
+// See https://www.apstylebook.com/ and http://www.chicagomanualofstyle.org/home.html
+titleCaseStyle: "AP"
uglyURLs: false
# verbose output
verbose: false
diff --git a/helpers/general.go b/helpers/general.go
index 552e4d0bf..a064309d3 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -26,6 +26,8 @@ import (
"unicode"
"unicode/utf8"
+ "github.com/jdkato/prose/transform"
+
bp "github.com/gohugoio/hugo/bufferpool"
"github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman"
@@ -194,6 +196,29 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
return false
}
+// GetTitleFunc returns a func that can be used to transform a string to
+// title case.
+//
+// The supported styles are
+//
+// - "Go" (strings.Title)
+// - "AP" (see https://www.apstylebook.com/)
+// - "Chicago" (see http://www.chicagomanualofstyle.org/home.html)
+//
+// If an unknown or empty style is provided, AP style is what you get.
+func GetTitleFunc(style string) func(s string) string {
+ switch strings.ToLower(style) {
+ case "go":
+ return strings.Title
+ case "chicago":
+ tc := transform.NewTitleConverter(transform.ChicagoStyle)
+ return tc.Title
+ default:
+ tc := transform.NewTitleConverter(transform.APStyle)
+ return tc.Title
+ }
+}
+
// HasStringsPrefix tests whether the string slice s begins with prefix slice s.
func HasStringsPrefix(s, prefix []string) bool {
return len(s) >= len(prefix) && compareStringSlices(s[0:len(prefix)], prefix)
diff --git a/helpers/general_test.go b/helpers/general_test.go
index 4d82bc0cf..561f59522 100644
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -19,6 +19,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGuessType(t *testing.T) {
@@ -173,6 +174,20 @@ func TestReaderContains(t *testing.T) {
assert.False(t, ReaderContains(nil, nil))
}
+func TestGetTitleFunc(t *testing.T) {
+ title := "somewhere over the rainbow"
+ assert := require.New(t)
+
+ assert.Equal("Somewhere Over The Rainbow", GetTitleFunc("go")(title))
+ assert.Equal("Somewhere over the Rainbow", GetTitleFunc("chicago")(title), "Chicago style")
+ assert.Equal("Somewhere over the Rainbow", GetTitleFunc("Chicago")(title), "Chicago style")
+ assert.Equal("Somewhere Over the Rainbow", GetTitleFunc("ap")(title), "AP style")
+ assert.Equal("Somewhere Over the Rainbow", GetTitleFunc("ap")(title), "AP style")
+ assert.Equal("Somewhere Over the Rainbow", GetTitleFunc("")(title), "AP style")
+ assert.Equal("Somewhere Over the Rainbow", GetTitleFunc("unknown")(title), "AP style")
+
+}
+
func BenchmarkReaderContains(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
diff --git a/hugolib/config.go b/hugolib/config.go
index e70d07756..8f3625331 100644
--- a/hugolib/config.go
+++ b/hugolib/config.go
@@ -101,6 +101,7 @@ func loadDefaultSettingsFor(v *viper.Viper) {
v.SetDefault("canonifyURLs", false)
v.SetDefault("relativeURLs", false)
v.SetDefault("removePathAccents", false)
+ v.SetDefault("titleCaseStyle", "AP")
v.SetDefault("taxonomies", map[string]string{"tag": "tags", "category": "categories"})
v.SetDefault("permalinks", make(PermalinkOverrides, 0))
v.SetDefault("sitemap", Sitemap{Priority: -1, Filename: "sitemap.xml"})
diff --git a/hugolib/site.go b/hugolib/site.go
index 47c2af453..629881856 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -132,6 +132,9 @@ type Site struct {
// Logger etc.
*deps.Deps `json:"-"`
+ // The func used to title case titles.
+ titleFunc func(s string) string
+
siteStats *siteStats
}
@@ -172,6 +175,7 @@ func (s *Site) reset() *Site {
return &Site{Deps: s.Deps,
layoutHandler: output.NewLayoutHandler(s.PathSpec.ThemeSet()),
disabledKinds: s.disabledKinds,
+ titleFunc: s.titleFunc,
outputFormats: s.outputFormats,
outputFormatsConfig: s.outputFormatsConfig,
mediaTypesConfig: s.mediaTypesConfig,
@@ -227,11 +231,14 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
return nil, err
}
+ titleFunc := helpers.GetTitleFunc(cfg.Language.GetString("titleCaseStyle"))
+
s := &Site{
PageCollections: c,
layoutHandler: output.NewLayoutHandler(cfg.Cfg.GetString("themesDir") != ""),
Language: cfg.Language,
disabledKinds: disabledKinds,
+ titleFunc: titleFunc,
outputFormats: outputFormats,
outputFormatsConfig: siteOutputFormatsConfig,
mediaTypesConfig: siteMediaTypesConfig,
@@ -2121,7 +2128,7 @@ func (s *Site) newTaxonomyPage(plural, key string) *Page {
p.Title = helpers.FirstUpper(key)
key = s.PathSpec.MakePathSanitized(key)
} else {
- p.Title = strings.Replace(strings.Title(key), "-", " ", -1)
+ p.Title = strings.Replace(s.titleFunc(key), "-", " ", -1)
}
return p
@@ -2141,6 +2148,6 @@ func (s *Site) newSectionPage(name string) *Page {
func (s *Site) newTaxonomyTermsPage(plural string) *Page {
p := s.newNodePage(KindTaxonomyTerm, plural)
- p.Title = strings.Title(plural)
+ p.Title = s.titleFunc(plural)
return p
}
diff --git a/tpl/strings/init.go b/tpl/strings/init.go
index 45d694b97..4f240415a 100644
--- a/tpl/strings/init.go
+++ b/tpl/strings/init.go
@@ -116,6 +116,7 @@ func init() {
[]string{"title"},
[][2]string{
{`{{title "Bat man"}}`, `Bat Man`},
+ {`{{title "somewhere over the rainbow"}}`, `Somewhere Over the Rainbow`},
},
)
diff --git a/tpl/strings/init_test.go b/tpl/strings/init_test.go
index a8ad8ffdf..904e486f7 100644
--- a/tpl/strings/init_test.go
+++ b/tpl/strings/init_test.go
@@ -18,6 +18,7 @@ import (
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/tpl/internal"
+ "github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -26,7 +27,7 @@ func TestInit(t *testing.T) {
var ns *internal.TemplateFuncsNamespace
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
- ns = nsf(&deps.Deps{})
+ ns = nsf(&deps.Deps{Cfg: viper.New()})
if ns.Name == name {
found = true
break
diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go
index ec95be730..5fe920433 100644
--- a/tpl/strings/strings.go
+++ b/tpl/strings/strings.go
@@ -27,14 +27,17 @@ import (
// New returns a new instance of the strings-namespaced template functions.
func New(d *deps.Deps) *Namespace {
- return &Namespace{deps: d}
+ titleCaseStyle := d.Cfg.GetString("titleCaseStyle")
+ titleFunc := helpers.GetTitleFunc(titleCaseStyle)
+ return &Namespace{deps: d, titleFunc: titleFunc}
}
// Namespace provides template functions for the "strings" namespace.
// Most functions mimic the Go stdlib, but the order of the parameters may be
// different to ease their use in the Go template system.
type Namespace struct {
- deps *deps.Deps
+ titleFunc func(s string) string
+ deps *deps.Deps
}
// CountRunes returns the number of runes in s, excluding whitepace.
@@ -303,7 +306,7 @@ func (ns *Namespace) Title(s interface{}) (string, error) {
return "", err
}
- return _strings.Title(ss), nil
+ return ns.titleFunc(ss), nil
}
// ToLower returns a copy of the input s with all Unicode letters mapped to their
diff --git a/tpl/strings/strings_test.go b/tpl/strings/strings_test.go
index ee10ac759..64ec0864f 100644
--- a/tpl/strings/strings_test.go
+++ b/tpl/strings/strings_test.go
@@ -19,11 +19,12 @@ import (
"testing"
"github.com/gohugoio/hugo/deps"
+ "github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-var ns = New(&deps.Deps{})
+var ns = New(&deps.Deps{Cfg: viper.New()})
type tstNoStringer struct{}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 936f3d620..8533e32d2 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -160,6 +160,18 @@
"revisionTime": "2014-10-17T20:07:13Z"
},
{
+ "checksumSHA1": "ywE9KA40kVq0qKcAIqLgpoA0su4=",
+ "path": "github.com/jdkato/prose/internal/util",
+ "revision": "c24611cae00c16858e611ef77226dd2f7502759f",
+ "revisionTime": "2017-07-29T20:17:14Z"
+ },
+ {
+ "checksumSHA1": "SpQ8EpkRvM9fAxEXQAy7Qy/L0Ig=",
+ "path": "github.com/jdkato/prose/transform",
+ "revision": "c24611cae00c16858e611ef77226dd2f7502759f",
+ "revisionTime": "2017-07-29T20:17:14Z"
+ },
+ {
"checksumSHA1": "gEjGS03N1eysvpQ+FCHTxPcbxXc=",
"path": "github.com/kardianos/osext",
"revision": "ae77be60afb1dcacde03767a8c37337fad28ac14",