mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
d953e39e63
commit
8da040342e
12 changed files with 296 additions and 129 deletions
|
@ -14,6 +14,8 @@ Hugo supports multiple languages side-by-side (added in `Hugo 0.17`). Define the
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
DefaultContentLanguage = "en"
|
||||||
|
|
||||||
Languages:
|
Languages:
|
||||||
en:
|
en:
|
||||||
weight: 1
|
weight: 1
|
||||||
|
@ -34,7 +36,9 @@ Anything not defined in a `[lang]:` block will fall back to the global
|
||||||
value for that key (like `copyright` for the English (`en`) language in this example).
|
value for that key (like `copyright` for the English (`en`) language in this example).
|
||||||
|
|
||||||
With the config above, all content, sitemap, RSS feeds, paginations
|
With the config above, all content, sitemap, RSS feeds, paginations
|
||||||
and taxonomy pages will be rendered below `/en` in English, and below `/fr` in French.
|
and taxonomy pages will be rendered below `/` in English (your default content language), and below `/fr` in French.
|
||||||
|
|
||||||
|
If you want all of the languages to be put below their respective language code, enable `DefaultContentLanguageInSubdir: true` in your configuration.
|
||||||
|
|
||||||
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
|
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,10 @@ type Language struct {
|
||||||
paramsInit sync.Once
|
paramsInit sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Language) String() string {
|
||||||
|
return l.Lang
|
||||||
|
}
|
||||||
|
|
||||||
func NewLanguage(lang string) *Language {
|
func NewLanguage(lang string) *Language {
|
||||||
return &Language{Lang: lang, params: make(map[string]interface{})}
|
return &Language{Lang: lang, params: make(map[string]interface{})}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,21 +168,32 @@ func AbsURL(in string, addLanguage bool) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
if addLanguage {
|
if addLanguage {
|
||||||
addSlash := in == "" || strings.HasSuffix(in, "/")
|
prefix := getLanguagePrefix()
|
||||||
in = path.Join(getLanguagePrefix(), in)
|
|
||||||
|
|
||||||
if addSlash {
|
if prefix != "" {
|
||||||
in += "/"
|
addSlash := in == "" || strings.HasSuffix(in, "/")
|
||||||
|
in = path.Join(prefix, in)
|
||||||
|
|
||||||
|
if addSlash {
|
||||||
|
in += "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MakePermalink(baseURL, in).String()
|
return MakePermalink(baseURL, in).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLanguagePrefix() string {
|
func getLanguagePrefix() string {
|
||||||
|
defaultLang := viper.GetString("DefaultContentLanguage")
|
||||||
|
defaultInSubDir := viper.GetBool("DefaultContentLanguageInSubdir")
|
||||||
|
|
||||||
if !viper.GetBool("Multilingual") {
|
if !viper.GetBool("Multilingual") {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return viper.Get("CurrentContentLanguage").(*Language).Lang
|
currentLang := viper.Get("CurrentContentLanguage").(*Language).Lang
|
||||||
|
if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return currentLang
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAbsURL determines whether the given path points to an absolute URL.
|
// IsAbsURL determines whether the given path points to an absolute URL.
|
||||||
|
@ -211,12 +222,15 @@ func RelURL(in string, addLanguage bool) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
if addLanguage {
|
if addLanguage {
|
||||||
hadSlash := strings.HasSuffix(u, "/")
|
prefix := getLanguagePrefix()
|
||||||
|
if prefix != "" {
|
||||||
|
hadSlash := strings.HasSuffix(u, "/")
|
||||||
|
|
||||||
u = path.Join(getLanguagePrefix(), u)
|
u = path.Join(prefix, u)
|
||||||
|
|
||||||
if hadSlash {
|
if hadSlash {
|
||||||
u += "/"
|
u += "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,19 +45,24 @@ func TestURLize(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAbsURL(t *testing.T) {
|
func TestAbsURL(t *testing.T) {
|
||||||
for _, addLanguage := range []bool{true, false} {
|
for _, defaultInSubDir := range []bool{true, false} {
|
||||||
for _, m := range []bool{true, false} {
|
for _, addLanguage := range []bool{true, false} {
|
||||||
for _, l := range []string{"en", "fr"} {
|
for _, m := range []bool{true, false} {
|
||||||
doTestAbsURL(t, addLanguage, m, l)
|
for _, l := range []string{"en", "fr"} {
|
||||||
|
doTestAbsURL(t, defaultInSubDir, addLanguage, m, l)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestAbsURL(t *testing.T, addLanguage, multilingual bool, lang string) {
|
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
|
||||||
viper.Reset()
|
viper.Reset()
|
||||||
viper.Set("Multilingual", multilingual)
|
viper.Set("Multilingual", multilingual)
|
||||||
viper.Set("CurrentContentLanguage", NewLanguage(lang))
|
viper.Set("CurrentContentLanguage", NewLanguage(lang))
|
||||||
|
viper.Set("DefaultContentLanguage", "en")
|
||||||
|
viper.Set("DefaultContentLanguageInSubdir", defaultInSubDir)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
baseURL string
|
baseURL string
|
||||||
|
@ -79,12 +84,17 @@ func doTestAbsURL(t *testing.T, addLanguage, multilingual bool, lang string) {
|
||||||
output := AbsURL(test.input, addLanguage)
|
output := AbsURL(test.input, addLanguage)
|
||||||
expected := test.expected
|
expected := test.expected
|
||||||
if multilingual && addLanguage {
|
if multilingual && addLanguage {
|
||||||
expected = strings.Replace(expected, "MULTI", lang+"/", 1)
|
if !defaultInSubDir && lang == "en" {
|
||||||
|
expected = strings.Replace(expected, "MULTI", "", 1)
|
||||||
|
} else {
|
||||||
|
expected = strings.Replace(expected, "MULTI", lang+"/", 1)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
expected = strings.Replace(expected, "MULTI", "", 1)
|
expected = strings.Replace(expected, "MULTI", "", 1)
|
||||||
}
|
}
|
||||||
if output != expected {
|
if output != expected {
|
||||||
t.Errorf("Expected %#v, got %#v\n", expected, output)
|
t.Fatalf("Expected %#v, got %#v\n", expected, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,19 +116,23 @@ func TestIsAbsURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRelURL(t *testing.T) {
|
func TestRelURL(t *testing.T) {
|
||||||
for _, addLanguage := range []bool{true, false} {
|
for _, defaultInSubDir := range []bool{true, false} {
|
||||||
for _, m := range []bool{true, false} {
|
for _, addLanguage := range []bool{true, false} {
|
||||||
for _, l := range []string{"en", "fr"} {
|
for _, m := range []bool{true, false} {
|
||||||
doTestRelURL(t, addLanguage, m, l)
|
for _, l := range []string{"en", "fr"} {
|
||||||
|
doTestRelURL(t, defaultInSubDir, addLanguage, m, l)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestRelURL(t *testing.T, addLanguage, multilingual bool, lang string) {
|
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
|
||||||
viper.Reset()
|
viper.Reset()
|
||||||
viper.Set("Multilingual", multilingual)
|
viper.Set("Multilingual", multilingual)
|
||||||
viper.Set("CurrentContentLanguage", NewLanguage(lang))
|
viper.Set("CurrentContentLanguage", NewLanguage(lang))
|
||||||
|
viper.Set("DefaultContentLanguage", "en")
|
||||||
|
viper.Set("DefaultContentLanguageInSubdir", defaultInSubDir)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
|
@ -146,7 +160,11 @@ func doTestRelURL(t *testing.T, addLanguage, multilingual bool, lang string) {
|
||||||
|
|
||||||
expected := test.expected
|
expected := test.expected
|
||||||
if multilingual && addLanguage {
|
if multilingual && addLanguage {
|
||||||
expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
|
if !defaultInSubDir && lang == "en" {
|
||||||
|
expected = strings.Replace(expected, "MULTI", "", 1)
|
||||||
|
} else {
|
||||||
|
expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
expected = strings.Replace(expected, "MULTI", "", 1)
|
expected = strings.Replace(expected, "MULTI", "", 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,4 +104,5 @@ func loadDefaultSettings() {
|
||||||
viper.SetDefault("UseModTimeAsFallback", false)
|
viper.SetDefault("UseModTimeAsFallback", false)
|
||||||
viper.SetDefault("Multilingual", false)
|
viper.SetDefault("Multilingual", false)
|
||||||
viper.SetDefault("DefaultContentLanguage", "en")
|
viper.SetDefault("DefaultContentLanguage", "en")
|
||||||
|
viper.SetDefault("DefaultContentLanguageInSubdir", false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,134 @@ func testCommonResetState() {
|
||||||
viper.SetFs(hugofs.Source())
|
viper.SetFs(hugofs.Source())
|
||||||
loadDefaultSettings()
|
loadDefaultSettings()
|
||||||
|
|
||||||
|
// Default is false, but true is easier to use as default in tests
|
||||||
|
viper.Set("DefaultContentLanguageInSubdir", true)
|
||||||
|
|
||||||
if err := hugofs.Source().Mkdir("content", 0755); err != nil {
|
if err := hugofs.Source().Mkdir("content", 0755); err != nil {
|
||||||
panic("Content folder creation failed.")
|
panic("Content folder creation failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultiSitesMainLangInRoot(t *testing.T) {
|
||||||
|
for _, b := range []bool{false, true} {
|
||||||
|
doTestMultiSitesMainLangInRoot(t, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
|
||||||
|
testCommonResetState()
|
||||||
|
viper.Set("DefaultContentLanguageInSubdir", defaultInSubDir)
|
||||||
|
|
||||||
|
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
||||||
|
|
||||||
|
err := sites.Build(BuildCfg{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build sites: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Len(t, sites.Sites, 2)
|
||||||
|
|
||||||
|
enSite := sites.Sites[0]
|
||||||
|
frSite := sites.Sites[1]
|
||||||
|
|
||||||
|
require.Equal(t, "/en", enSite.Info.LanguagePrefix)
|
||||||
|
|
||||||
|
if defaultInSubDir {
|
||||||
|
require.Equal(t, "/fr", frSite.Info.LanguagePrefix)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, "", frSite.Info.LanguagePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc1en := enSite.Pages[0]
|
||||||
|
doc1fr := frSite.Pages[0]
|
||||||
|
|
||||||
|
enPerm, _ := doc1en.Permalink()
|
||||||
|
enRelPerm, _ := doc1en.RelPermalink()
|
||||||
|
require.Equal(t, "http://example.com/blog/en/sect/doc1-slug/", enPerm)
|
||||||
|
require.Equal(t, "/blog/en/sect/doc1-slug/", enRelPerm)
|
||||||
|
|
||||||
|
frPerm, _ := doc1fr.Permalink()
|
||||||
|
frRelPerm, _ := doc1fr.RelPermalink()
|
||||||
|
// Main language in root
|
||||||
|
require.Equal(t, replaceDefaultContentLanguageValue("http://example.com/blog/fr/sect/doc1/", defaultInSubDir), frPerm)
|
||||||
|
require.Equal(t, replaceDefaultContentLanguageValue("/blog/fr/sect/doc1/", defaultInSubDir), frRelPerm)
|
||||||
|
|
||||||
|
assertFileContent(t, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour")
|
||||||
|
assertFileContent(t, "public/en/sect/doc1-slug/index.html", defaultInSubDir, "Single", "Hello")
|
||||||
|
|
||||||
|
// Check home
|
||||||
|
if defaultInSubDir {
|
||||||
|
// should have a redirect on top level.
|
||||||
|
assertFileContent(t, "public/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
|
||||||
|
}
|
||||||
|
assertFileContent(t, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour")
|
||||||
|
assertFileContent(t, "public/en/index.html", defaultInSubDir, "Home", "Hello")
|
||||||
|
|
||||||
|
// Check list pages
|
||||||
|
assertFileContent(t, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour")
|
||||||
|
assertFileContent(t, "public/en/sect/index.html", defaultInSubDir, "List", "Hello")
|
||||||
|
assertFileContent(t, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour")
|
||||||
|
assertFileContent(t, "public/en/tags/tag1/index.html", defaultInSubDir, "List", "Hello")
|
||||||
|
|
||||||
|
// Check sitemaps
|
||||||
|
// Sitemaps behaves different: In a multilanguage setup there will always be a index file and
|
||||||
|
// one sitemap in each lang folder.
|
||||||
|
assertFileContent(t, "public/sitemap.xml", true,
|
||||||
|
"<loc>http:/example.com/blog/en/sitemap.xml</loc>",
|
||||||
|
"<loc>http:/example.com/blog/fr/sitemap.xml</loc>")
|
||||||
|
|
||||||
|
if defaultInSubDir {
|
||||||
|
assertFileContent(t, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/fr/</loc>")
|
||||||
|
} else {
|
||||||
|
assertFileContent(t, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/</loc>")
|
||||||
|
}
|
||||||
|
assertFileContent(t, "public/en/sitemap.xml", true, "<loc>http://example.com/blog/en/</loc>")
|
||||||
|
|
||||||
|
// Check rss
|
||||||
|
assertFileContent(t, "public/fr/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/index.xml"`)
|
||||||
|
assertFileContent(t, "public/en/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/index.xml"`)
|
||||||
|
assertFileContent(t, "public/fr/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/sect/index.xml"`)
|
||||||
|
assertFileContent(t, "public/en/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
|
||||||
|
assertFileContent(t, "public/fr/plaques/frtag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`)
|
||||||
|
assertFileContent(t, "public/en/tags/tag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
|
||||||
|
|
||||||
|
// Check paginators
|
||||||
|
assertFileContent(t, "public/fr/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/"`)
|
||||||
|
assertFileContent(t, "public/en/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/"`)
|
||||||
|
assertFileContent(t, "public/fr/page/2/index.html", defaultInSubDir, "Home Page 2", "Bonjour", "http://example.com/blog/fr/")
|
||||||
|
assertFileContent(t, "public/en/page/2/index.html", defaultInSubDir, "Home Page 2", "Hello", "http://example.com/blog/en/")
|
||||||
|
assertFileContent(t, "public/fr/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/sect/"`)
|
||||||
|
assertFileContent(t, "public/en/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/sect/"`)
|
||||||
|
assertFileContent(t, "public/fr/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/sect/")
|
||||||
|
assertFileContent(t, "public/en/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/sect/")
|
||||||
|
assertFileContent(t, "public/fr/plaques/frtag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`)
|
||||||
|
assertFileContent(t, "public/en/tags/tag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
|
||||||
|
assertFileContent(t, "public/fr/plaques/frtag1/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/plaques/frtag1/")
|
||||||
|
assertFileContent(t, "public/en/tags/tag1/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) string {
|
||||||
|
replace := viper.GetString("DefaultContentLanguage") + "/"
|
||||||
|
if !defaultInSubDir {
|
||||||
|
value = strings.Replace(value, replace, "", 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertFileContent(t *testing.T, filename string, defaultInSubDir bool, matches ...string) {
|
||||||
|
filename = replaceDefaultContentLanguageValue(filename, defaultInSubDir)
|
||||||
|
content := readDestination(t, filename)
|
||||||
|
for _, match := range matches {
|
||||||
|
match = replaceDefaultContentLanguageValue(match, defaultInSubDir)
|
||||||
|
require.True(t, strings.Contains(content, match), fmt.Sprintf("File no match for %q in %q: %s", match, filename, content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultiSitesBuild(t *testing.T) {
|
func TestMultiSitesBuild(t *testing.T) {
|
||||||
testCommonResetState()
|
testCommonResetState()
|
||||||
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
||||||
|
@ -397,7 +519,7 @@ DisableSitemap = false
|
||||||
DisableRSS = false
|
DisableRSS = false
|
||||||
RSSUri = "index.xml"
|
RSSUri = "index.xml"
|
||||||
|
|
||||||
paginate = 2
|
paginate = 1
|
||||||
DefaultContentLanguage = "fr"
|
DefaultContentLanguage = "fr"
|
||||||
|
|
||||||
[permalinks]
|
[permalinks]
|
||||||
|
@ -435,14 +557,14 @@ func createMultiTestSites(t *testing.T, tomlConfig string) *HugoSites {
|
||||||
|
|
||||||
if err := afero.WriteFile(hugofs.Source(),
|
if err := afero.WriteFile(hugofs.Source(),
|
||||||
filepath.Join("layouts", "_default/list.html"),
|
filepath.Join("layouts", "_default/list.html"),
|
||||||
[]byte("List: {{ .Title }}"),
|
[]byte("{{ $p := .Paginator }}List Page {{ $p.PageNumber }}: {{ .Title }}|{{ i18n \"hello\" }}|{{ .Permalink }}"),
|
||||||
0755); err != nil {
|
0755); err != nil {
|
||||||
t.Fatalf("Failed to write layout file: %s", err)
|
t.Fatalf("Failed to write layout file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := afero.WriteFile(hugofs.Source(),
|
if err := afero.WriteFile(hugofs.Source(),
|
||||||
filepath.Join("layouts", "index.html"),
|
filepath.Join("layouts", "index.html"),
|
||||||
[]byte("Home: {{ .Title }}|{{ .IsHome }}"),
|
[]byte("{{ $p := .Paginator }}Home Page {{ $p.PageNumber }}: {{ .Title }}|{{ .IsHome }}|{{ i18n \"hello\" }}|{{ .Permalink }}"),
|
||||||
0755); err != nil {
|
0755); err != nil {
|
||||||
t.Fatalf("Failed to write layout file: %s", err)
|
t.Fatalf("Failed to write layout file: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -505,6 +627,7 @@ title: doc3
|
||||||
publishdate: "2000-01-03"
|
publishdate: "2000-01-03"
|
||||||
tags:
|
tags:
|
||||||
- tag2
|
- tag2
|
||||||
|
- tag1
|
||||||
url: /superbob
|
url: /superbob
|
||||||
---
|
---
|
||||||
# doc3
|
# doc3
|
||||||
|
|
|
@ -176,7 +176,7 @@ type URLPath struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) URL() string {
|
func (n *Node) URL() string {
|
||||||
return n.addMultilingualWebPrefix(n.URLPath.URL)
|
return n.addLangPathPrefix(n.URLPath.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Permalink() string {
|
func (n *Node) Permalink() string {
|
||||||
|
@ -206,8 +206,27 @@ func (n *Node) Lang() string {
|
||||||
return n.lang
|
return n.lang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Node) shouldAddLanguagePrefix() bool {
|
||||||
|
if !n.Site.IsMultiLingual() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.Lang() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !n.Site.defaultContentLanguageInSubdir && n.Lang() == n.Site.multilingual.DefaultLang.Lang {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Node) initLanguage() {
|
func (n *Node) initLanguage() {
|
||||||
n.languageInit.Do(func() {
|
n.languageInit.Do(func() {
|
||||||
|
if n.language != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
pageLang := n.lang
|
pageLang := n.lang
|
||||||
ml := n.Site.multilingual
|
ml := n.Site.multilingual
|
||||||
if ml == nil {
|
if ml == nil {
|
||||||
|
@ -278,29 +297,34 @@ func (n *Node) initTranslations() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) addMultilingualWebPrefix(outfile string) string {
|
func (n *Node) addLangPathPrefix(outfile string) string {
|
||||||
|
return n.addLangPathPrefixIfFlagSet(outfile, n.shouldAddLanguagePrefix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) addLangPathPrefixIfFlagSet(outfile string, should bool) string {
|
||||||
if helpers.IsAbsURL(outfile) {
|
if helpers.IsAbsURL(outfile) {
|
||||||
return outfile
|
return outfile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !should {
|
||||||
|
return outfile
|
||||||
|
}
|
||||||
|
|
||||||
hadSlashSuffix := strings.HasSuffix(outfile, "/")
|
hadSlashSuffix := strings.HasSuffix(outfile, "/")
|
||||||
|
|
||||||
lang := n.Lang()
|
outfile = "/" + path.Join(n.Lang(), outfile)
|
||||||
if lang == "" || !n.Site.IsMultiLingual() {
|
|
||||||
return outfile
|
|
||||||
}
|
|
||||||
outfile = "/" + path.Join(lang, outfile)
|
|
||||||
if hadSlashSuffix {
|
if hadSlashSuffix {
|
||||||
outfile += "/"
|
outfile += "/"
|
||||||
}
|
}
|
||||||
return outfile
|
return outfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) addMultilingualFilesystemPrefix(outfile string) string {
|
func (n *Node) addLangFilepathPrefix(outfile string) string {
|
||||||
lang := n.Lang()
|
if outfile == "" {
|
||||||
if lang == "" || !n.Site.IsMultiLingual() {
|
outfile = helpers.FilePathSeparator
|
||||||
|
}
|
||||||
|
if !n.shouldAddLanguagePrefix() {
|
||||||
return outfile
|
return outfile
|
||||||
}
|
}
|
||||||
return string(filepath.Separator) + filepath.Join(lang, outfile)
|
return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -514,7 +514,7 @@ func (p *Page) permalink() (*url.URL, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
permalink = p.addMultilingualWebPrefix(permalink)
|
permalink = p.addLangPathPrefix(permalink)
|
||||||
|
|
||||||
return helpers.MakePermalink(baseURL, permalink), nil
|
return helpers.MakePermalink(baseURL, permalink), nil
|
||||||
}
|
}
|
||||||
|
@ -1059,7 +1059,7 @@ func (p *Page) TargetPath() (outfile string) {
|
||||||
outfile += "index.html"
|
outfile += "index.html"
|
||||||
}
|
}
|
||||||
outfile = filepath.FromSlash(outfile)
|
outfile = filepath.FromSlash(outfile)
|
||||||
outfile = p.addMultilingualFilesystemPrefix(outfile)
|
outfile = p.addLangFilepathPrefix(outfile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1071,5 +1071,5 @@ func (p *Page) TargetPath() (outfile string) {
|
||||||
outfile = helpers.ReplaceExtension(p.Source.TranslationBaseName(), p.Extension())
|
outfile = helpers.ReplaceExtension(p.Source.TranslationBaseName(), p.Extension())
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.addMultilingualFilesystemPrefix(filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
|
return p.addLangFilepathPrefix(filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
|
||||||
}
|
}
|
||||||
|
|
133
hugolib/site.go
133
hugolib/site.go
|
@ -167,10 +167,11 @@ type SiteInfo struct {
|
||||||
paginationPageCount uint64
|
paginationPageCount uint64
|
||||||
Data *map[string]interface{}
|
Data *map[string]interface{}
|
||||||
|
|
||||||
multilingual *Multilingual
|
multilingual *Multilingual
|
||||||
Language *helpers.Language
|
Language *helpers.Language
|
||||||
LanguagePrefix string
|
LanguagePrefix string
|
||||||
Languages helpers.Languages
|
Languages helpers.Languages
|
||||||
|
defaultContentLanguageInSubdir bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used in tests.
|
// Used in tests.
|
||||||
|
@ -864,42 +865,45 @@ func (s *Site) initializeSiteInfo() {
|
||||||
permalinks[k] = pathPattern(v)
|
permalinks[k] = pathPattern(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultContentInSubDir := viper.GetBool("DefaultContentLanguageInSubdir")
|
||||||
|
defaultContentLanguage := viper.GetString("DefaultContentLanguage")
|
||||||
|
|
||||||
languagePrefix := ""
|
languagePrefix := ""
|
||||||
if s.multilingualEnabled() {
|
if s.multilingualEnabled() && (defaultContentInSubDir || lang.Lang != defaultContentLanguage) {
|
||||||
languagePrefix = "/" + lang.Lang
|
languagePrefix = "/" + lang.Lang
|
||||||
}
|
}
|
||||||
|
|
||||||
var multilingual *Multilingual
|
var multilingual *Multilingual
|
||||||
|
|
||||||
if s.owner != nil {
|
if s.owner != nil {
|
||||||
multilingual = s.owner.multilingual
|
multilingual = s.owner.multilingual
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Info = SiteInfo{
|
s.Info = SiteInfo{
|
||||||
BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),
|
BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),
|
||||||
Title: lang.GetString("Title"),
|
Title: lang.GetString("Title"),
|
||||||
Author: lang.GetStringMap("author"),
|
Author: lang.GetStringMap("author"),
|
||||||
Social: lang.GetStringMapString("social"),
|
Social: lang.GetStringMapString("social"),
|
||||||
LanguageCode: lang.GetString("languagecode"),
|
LanguageCode: lang.GetString("languagecode"),
|
||||||
Copyright: lang.GetString("copyright"),
|
Copyright: lang.GetString("copyright"),
|
||||||
DisqusShortname: lang.GetString("DisqusShortname"),
|
DisqusShortname: lang.GetString("DisqusShortname"),
|
||||||
multilingual: multilingual,
|
multilingual: multilingual,
|
||||||
Language: lang,
|
Language: lang,
|
||||||
LanguagePrefix: languagePrefix,
|
LanguagePrefix: languagePrefix,
|
||||||
Languages: languages,
|
Languages: languages,
|
||||||
GoogleAnalytics: lang.GetString("GoogleAnalytics"),
|
defaultContentLanguageInSubdir: defaultContentInSubDir,
|
||||||
RSSLink: permalinkStr(viper.GetString("RSSUri")),
|
GoogleAnalytics: lang.GetString("GoogleAnalytics"),
|
||||||
BuildDrafts: viper.GetBool("BuildDrafts"),
|
RSSLink: permalinkStr(viper.GetString("RSSUri")),
|
||||||
canonifyURLs: viper.GetBool("CanonifyURLs"),
|
BuildDrafts: viper.GetBool("BuildDrafts"),
|
||||||
preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),
|
canonifyURLs: viper.GetBool("CanonifyURLs"),
|
||||||
AllPages: &s.AllPages,
|
preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),
|
||||||
Pages: &s.Pages,
|
AllPages: &s.AllPages,
|
||||||
rawAllPages: &s.rawAllPages,
|
Pages: &s.Pages,
|
||||||
Files: &s.Files,
|
rawAllPages: &s.rawAllPages,
|
||||||
Menus: &s.Menus,
|
Files: &s.Files,
|
||||||
Params: params,
|
Menus: &s.Menus,
|
||||||
Permalinks: permalinks,
|
Params: params,
|
||||||
Data: &s.Data,
|
Permalinks: permalinks,
|
||||||
|
Data: &s.Data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1280,7 +1284,7 @@ func (s *Site) assembleMenus() {
|
||||||
if p.Section() != "" {
|
if p.Section() != "" {
|
||||||
me := MenuEntry{Identifier: p.Section(),
|
me := MenuEntry{Identifier: p.Section(),
|
||||||
Name: helpers.MakeTitle(helpers.FirstUpper(p.Section())),
|
Name: helpers.MakeTitle(helpers.FirstUpper(p.Section())),
|
||||||
URL: s.Info.createNodeMenuEntryURL(p.addMultilingualWebPrefix("/"+p.Section()) + "/")}
|
URL: s.Info.createNodeMenuEntryURL(p.addLangPathPrefix("/"+p.Section()) + "/")}
|
||||||
if _, ok := flat[twoD{sectionPagesMenu, me.KeyName()}]; ok {
|
if _, ok := flat[twoD{sectionPagesMenu, me.KeyName()}]; ok {
|
||||||
// menu with same id defined in config, let that one win
|
// menu with same id defined in config, let that one win
|
||||||
continue
|
continue
|
||||||
|
@ -1422,7 +1426,7 @@ func (s *Site) renderAliases() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.owner.multilingual.enabled() {
|
if s.owner.multilingual.enabled() && s.Info.defaultContentLanguageInSubdir {
|
||||||
mainLang := s.owner.multilingual.DefaultLang.Lang
|
mainLang := s.owner.multilingual.DefaultLang.Lang
|
||||||
mainLangURL := helpers.AbsURL(mainLang, false)
|
mainLangURL := helpers.AbsURL(mainLang, false)
|
||||||
jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
|
jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
|
||||||
|
@ -1612,19 +1616,6 @@ func (s *Site) newTaxonomyNode(t taxRenderInfo) (*Node, string) {
|
||||||
return n, base
|
return n, base
|
||||||
}
|
}
|
||||||
|
|
||||||
// addMultilingualPrefix adds the `en/` prefix to the path passed as parameter.
|
|
||||||
// `basePath` must not start with http://
|
|
||||||
func (s *Site) addMultilingualPrefix(basePath string) string {
|
|
||||||
hadPrefix := strings.HasPrefix(basePath, "/")
|
|
||||||
if s.multilingualEnabled() {
|
|
||||||
basePath = path.Join(s.Language.Lang, basePath)
|
|
||||||
if hadPrefix {
|
|
||||||
basePath = "/" + basePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return basePath
|
|
||||||
}
|
|
||||||
|
|
||||||
func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error, wg *sync.WaitGroup) {
|
func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
|
@ -1637,14 +1628,13 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
|
||||||
[]string{"taxonomy/" + t.singular + ".html", "indexes/" + t.singular + ".html", "_default/taxonomy.html", "_default/list.html"})
|
[]string{"taxonomy/" + t.singular + ".html", "indexes/" + t.singular + ".html", "_default/taxonomy.html", "_default/list.html"})
|
||||||
|
|
||||||
n, base = s.newTaxonomyNode(t)
|
n, base = s.newTaxonomyNode(t)
|
||||||
|
baseWithLanguagePrefix := n.addLangPathPrefix(base)
|
||||||
base = s.addMultilingualPrefix(base)
|
|
||||||
|
|
||||||
dest := base
|
dest := base
|
||||||
if viper.GetBool("UglyURLs") {
|
if viper.GetBool("UglyURLs") {
|
||||||
dest = helpers.Uglify(base + ".html")
|
dest = helpers.Uglify(baseWithLanguagePrefix + ".html")
|
||||||
} else {
|
} else {
|
||||||
dest = helpers.PrettifyPath(base + "/index.html")
|
dest = helpers.PrettifyPath(baseWithLanguagePrefix + "/index.html")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.renderAndWritePage("taxonomy "+t.singular, dest, n, layouts...); err != nil {
|
if err := s.renderAndWritePage("taxonomy "+t.singular, dest, n, layouts...); err != nil {
|
||||||
|
@ -1657,7 +1647,7 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
|
||||||
paginatePath := viper.GetString("paginatePath")
|
paginatePath := viper.GetString("paginatePath")
|
||||||
|
|
||||||
// write alias for page 1
|
// write alias for page 1
|
||||||
s.writeDestAlias(helpers.PaginateAliasPath(base, 1), permalink(base))
|
s.writeDestAlias(helpers.PaginateAliasPath(baseWithLanguagePrefix, 1), n.Permalink())
|
||||||
|
|
||||||
pagers := n.paginator.Pagers()
|
pagers := n.paginator.Pagers()
|
||||||
|
|
||||||
|
@ -1675,7 +1665,7 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
|
||||||
taxonomyPagerNode.Lastmod = first.Lastmod
|
taxonomyPagerNode.Lastmod = first.Lastmod
|
||||||
}
|
}
|
||||||
pageNumber := i + 1
|
pageNumber := i + 1
|
||||||
htmlBase := fmt.Sprintf("/%s/%s/%d", base, paginatePath, pageNumber)
|
htmlBase := fmt.Sprintf("/%s/%s/%d", baseWithLanguagePrefix, paginatePath, pageNumber)
|
||||||
if err := s.renderAndWritePage(fmt.Sprintf("taxonomy %s", t.singular), htmlBase, taxonomyPagerNode, layouts...); err != nil {
|
if err := s.renderAndWritePage(fmt.Sprintf("taxonomy %s", t.singular), htmlBase, taxonomyPagerNode, layouts...); err != nil {
|
||||||
results <- err
|
results <- err
|
||||||
continue
|
continue
|
||||||
|
@ -1686,11 +1676,10 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
|
||||||
if !viper.GetBool("DisableRSS") {
|
if !viper.GetBool("DisableRSS") {
|
||||||
// XML Feed
|
// XML Feed
|
||||||
rssuri := viper.GetString("RSSUri")
|
rssuri := viper.GetString("RSSUri")
|
||||||
n.URLPath.URL = permalinkStr(base + "/" + rssuri)
|
s.setURLs(n, base+"/"+rssuri)
|
||||||
n.URLPath.Permalink = permalink(base)
|
|
||||||
rssLayouts := []string{"taxonomy/" + t.singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
|
rssLayouts := []string{"taxonomy/" + t.singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
|
||||||
|
|
||||||
if err := s.renderAndWriteXML("taxonomy "+t.singular+" rss", base+"/"+rssuri, n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
if err := s.renderAndWriteXML("taxonomy "+t.singular+" rss", baseWithLanguagePrefix+"/"+rssuri, n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
||||||
results <- err
|
results <- err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1714,7 +1703,7 @@ func (s *Site) renderListsOfTaxonomyTerms() (err error) {
|
||||||
layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}
|
layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}
|
||||||
layouts = s.appendThemeTemplates(layouts)
|
layouts = s.appendThemeTemplates(layouts)
|
||||||
if s.layoutExists(layouts...) {
|
if s.layoutExists(layouts...) {
|
||||||
if err := s.renderAndWritePage("taxonomy terms for "+singular, s.addMultilingualPrefix(plural+"/index.html"), n, layouts...); err != nil {
|
if err := s.renderAndWritePage("taxonomy terms for "+singular, n.addLangPathPrefix(plural+"/index.html"), n, layouts...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1755,9 +1744,9 @@ func (s *Site) renderSectionLists() error {
|
||||||
section = helpers.MakePathSanitized(section)
|
section = helpers.MakePathSanitized(section)
|
||||||
}
|
}
|
||||||
|
|
||||||
base := s.addMultilingualPrefix(section)
|
|
||||||
|
|
||||||
n := s.newSectionListNode(sectionName, section, data)
|
n := s.newSectionListNode(sectionName, section, data)
|
||||||
|
base := n.addLangPathPrefix(section)
|
||||||
|
|
||||||
if err := s.renderAndWritePage(fmt.Sprintf("section %s", section), base, n, s.appendThemeTemplates(layouts)...); err != nil {
|
if err := s.renderAndWritePage(fmt.Sprintf("section %s", section), base, n, s.appendThemeTemplates(layouts)...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1795,10 +1784,9 @@ func (s *Site) renderSectionLists() error {
|
||||||
if !viper.GetBool("DisableRSS") && section != "" {
|
if !viper.GetBool("DisableRSS") && section != "" {
|
||||||
// XML Feed
|
// XML Feed
|
||||||
rssuri := viper.GetString("RSSUri")
|
rssuri := viper.GetString("RSSUri")
|
||||||
n.URLPath.URL = permalinkStr(base + "/" + rssuri)
|
s.setURLs(n, section+"/"+rssuri)
|
||||||
n.URLPath.Permalink = permalink(base)
|
|
||||||
rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
|
rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
|
||||||
if err := s.renderAndWriteXML("section "+section+" rss", base+"/"+rssuri, n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
if err := s.renderAndWriteXML("section "+section+" rss", n.addLangPathPrefix(section+"/"+rssuri), n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1807,19 +1795,21 @@ func (s *Site) renderSectionLists() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Site) renderHomePage() error {
|
func (s *Site) renderHomePage() error {
|
||||||
n := s.newHomeNode()
|
|
||||||
layouts := s.appendThemeTemplates([]string{"index.html", "_default/list.html"})
|
|
||||||
|
|
||||||
if err := s.renderAndWritePage("homepage", s.addMultilingualPrefix(helpers.FilePathSeparator), n, layouts...); err != nil {
|
n := s.newHomeNode()
|
||||||
|
|
||||||
|
layouts := s.appendThemeTemplates([]string{"index.html", "_default/list.html"})
|
||||||
|
base := n.addLangFilepathPrefix("")
|
||||||
|
if err := s.renderAndWritePage("homepage", base, n, layouts...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.paginator != nil {
|
if n.paginator != nil {
|
||||||
|
|
||||||
paginatePath := viper.GetString("paginatePath")
|
paginatePath := viper.GetString("paginatePath")
|
||||||
|
|
||||||
// write alias for page 1
|
// write alias for page 1
|
||||||
s.writeDestAlias(s.addMultilingualPrefix(helpers.PaginateAliasPath("", 1)), permalink("/"))
|
// TODO(bep) ml all of these n.addLang ... fix.
|
||||||
|
s.writeDestAlias(n.addLangPathPrefix(helpers.PaginateAliasPath("", 1)), n.Permalink())
|
||||||
|
|
||||||
pagers := n.paginator.Pagers()
|
pagers := n.paginator.Pagers()
|
||||||
|
|
||||||
|
@ -1838,7 +1828,7 @@ func (s *Site) renderHomePage() error {
|
||||||
}
|
}
|
||||||
pageNumber := i + 1
|
pageNumber := i + 1
|
||||||
htmlBase := fmt.Sprintf("/%s/%d", paginatePath, pageNumber)
|
htmlBase := fmt.Sprintf("/%s/%d", paginatePath, pageNumber)
|
||||||
htmlBase = s.addMultilingualPrefix(htmlBase)
|
htmlBase = n.addLangPathPrefix(htmlBase)
|
||||||
if err := s.renderAndWritePage(fmt.Sprintf("homepage"), filepath.FromSlash(htmlBase), homePagerNode, layouts...); err != nil {
|
if err := s.renderAndWritePage(fmt.Sprintf("homepage"), filepath.FromSlash(htmlBase), homePagerNode, layouts...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1847,7 +1837,7 @@ func (s *Site) renderHomePage() error {
|
||||||
|
|
||||||
if !viper.GetBool("DisableRSS") {
|
if !viper.GetBool("DisableRSS") {
|
||||||
// XML Feed
|
// XML Feed
|
||||||
n.URLPath.URL = permalinkStr(viper.GetString("RSSUri"))
|
s.setURLs(n, viper.GetString("RSSUri"))
|
||||||
n.Title = ""
|
n.Title = ""
|
||||||
high := 50
|
high := 50
|
||||||
if len(s.Pages) < high {
|
if len(s.Pages) < high {
|
||||||
|
@ -1861,7 +1851,7 @@ func (s *Site) renderHomePage() error {
|
||||||
|
|
||||||
rssLayouts := []string{"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml"}
|
rssLayouts := []string{"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml"}
|
||||||
|
|
||||||
if err := s.renderAndWriteXML("homepage rss", s.addMultilingualPrefix(viper.GetString("RSSUri")), n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
if err := s.renderAndWriteXML("homepage rss", n.addLangPathPrefix(viper.GetString("RSSUri")), n, s.appendThemeTemplates(rssLayouts)...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1911,10 +1901,11 @@ func (s *Site) renderSitemap() error {
|
||||||
pages := make(Pages, 0)
|
pages := make(Pages, 0)
|
||||||
|
|
||||||
page := &Page{}
|
page := &Page{}
|
||||||
|
page.language = s.Language
|
||||||
page.Date = s.Info.LastChange
|
page.Date = s.Info.LastChange
|
||||||
page.Lastmod = s.Info.LastChange
|
page.Lastmod = s.Info.LastChange
|
||||||
page.Site = &s.Info
|
page.Site = &s.Info
|
||||||
page.URLPath.URL = "/"
|
page.URLPath.URL = ""
|
||||||
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
|
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
|
||||||
page.Sitemap.Priority = sitemapDefault.Priority
|
page.Sitemap.Priority = sitemapDefault.Priority
|
||||||
|
|
||||||
|
@ -1938,8 +1929,8 @@ func (s *Site) renderSitemap() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
|
smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
|
||||||
|
addLanguagePrefix := n.Site.IsMultiLingual()
|
||||||
if err := s.renderAndWriteXML("sitemap", s.addMultilingualPrefix(page.Sitemap.Filename), n, s.appendThemeTemplates(smLayouts)...); err != nil {
|
if err := s.renderAndWriteXML("sitemap", n.addLangPathPrefixIfFlagSet(page.Sitemap.Filename, addLanguagePrefix), n, s.appendThemeTemplates(smLayouts)...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,7 +317,6 @@ THE END.`, refShortcode)),
|
||||||
// Issue #939
|
// Issue #939
|
||||||
// Issue #1923
|
// Issue #1923
|
||||||
func TestShouldAlwaysHaveUglyURLs(t *testing.T) {
|
func TestShouldAlwaysHaveUglyURLs(t *testing.T) {
|
||||||
hugofs.InitMemFs()
|
|
||||||
for _, uglyURLs := range []bool{true, false} {
|
for _, uglyURLs := range []bool{true, false} {
|
||||||
doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
|
doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
|
||||||
}
|
}
|
||||||
|
@ -383,12 +382,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
file, err := hugofs.Destination().Open(test.doc)
|
content := readDestination(t, test.doc)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Did not find %s in target: %s", test.doc, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
content := helpers.ReaderToString(file)
|
|
||||||
|
|
||||||
if content != test.expected {
|
if content != test.expected {
|
||||||
t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
|
t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
|
||||||
|
|
|
@ -14,13 +14,12 @@
|
||||||
package hugolib
|
package hugolib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/hugo/helpers"
|
"github.com/spf13/hugo/helpers"
|
||||||
"github.com/spf13/hugo/hugofs"
|
|
||||||
"github.com/spf13/hugo/source"
|
"github.com/spf13/hugo/source"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -50,15 +49,10 @@ func TestSitemapOutput(t *testing.T) {
|
||||||
t.Fatalf("Failed to build site: %s", err)
|
t.Fatalf("Failed to build site: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sitemapFile, err := hugofs.Destination().Open("public/sitemap.xml")
|
sitemapContent := readDestination(t, "public/sitemap.xml")
|
||||||
|
|
||||||
if err != nil {
|
if !strings.HasPrefix(sitemapContent, "<?xml") {
|
||||||
t.Fatalf("Unable to locate: sitemap.xml")
|
t.Errorf("Sitemap file should start with <?xml. %s", sitemapContent)
|
||||||
}
|
|
||||||
|
|
||||||
sitemap := helpers.ReaderToBytes(sitemapFile)
|
|
||||||
if !bytes.HasPrefix(sitemap, []byte("<?xml")) {
|
|
||||||
t.Errorf("Sitemap file should start with <?xml. %s", sitemap)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (t *GoHTMLTemplate) EmbedTemplates() {
|
||||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||||
<atom:link href="{{.URL}}" rel="self" type="application/rss+xml" />
|
<atom:link href="{{.Permalink}}" rel="self" type="application/rss+xml" />
|
||||||
{{ range first 15 .Data.Pages }}
|
{{ range first 15 .Data.Pages }}
|
||||||
<item>
|
<item>
|
||||||
<title>{{ .Title }}</title>
|
<title>{{ .Title }}</title>
|
||||||
|
|
Loading…
Reference in a new issue