ref: 8da040342eb0a3098e54dc6ed2cb10bac6173230
parent: d953e39e63deac608e7f1442a6c0d5b2fe56180e
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Mon Aug 8 09:55:18 EDT 2016
Render main content language in root by default Fixes #2312
--- a/docs/content/content/multilingual.md
+++ b/docs/content/content/multilingual.md
@@ -14,6 +14,8 @@
Example:
```
+DefaultContentLanguage = "en"
+
Languages:
en:
weight: 1
@@ -34,7 +36,9 @@
value for that key (like `copyright` for the English (`en`) language in this example).
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.
--- a/helpers/language.go
+++ b/helpers/language.go
@@ -31,6 +31,10 @@
paramsInit sync.Once
}
+func (l *Language) String() string {+ return l.Lang
+}
+
func NewLanguage(lang string) *Language { return &Language{Lang: lang, params: make(map[string]interface{})}}
--- a/helpers/url.go
+++ b/helpers/url.go
@@ -168,11 +168,15 @@
}
if addLanguage {- addSlash := in == "" || strings.HasSuffix(in, "/")
- in = path.Join(getLanguagePrefix(), in)
+ prefix := getLanguagePrefix()
- if addSlash {- in += "/"
+ if prefix != "" {+ addSlash := in == "" || strings.HasSuffix(in, "/")
+ in = path.Join(prefix, in)
+
+ if addSlash {+ in += "/"
+ }
}
}
return MakePermalink(baseURL, in).String()
@@ -179,10 +183,17 @@
}
func getLanguagePrefix() string {+ defaultLang := viper.GetString("DefaultContentLanguage")+ defaultInSubDir := viper.GetBool("DefaultContentLanguageInSubdir")+
if !viper.GetBool("Multilingual") {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.
@@ -211,12 +222,15 @@
}
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 {- u += "/"
+ if hadSlash {+ u += "/"
+ }
}
}
--- a/helpers/url_test.go
+++ b/helpers/url_test.go
@@ -45,19 +45,24 @@
}
func TestAbsURL(t *testing.T) {- for _, addLanguage := range []bool{true, false} {- for _, m := range []bool{true, false} {- for _, l := range []string{"en", "fr"} {- doTestAbsURL(t, addLanguage, m, l)
+ for _, defaultInSubDir := range []bool{true, false} {+ for _, addLanguage := range []bool{true, false} {+ for _, m := range []bool{true, false} {+ 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.Set("Multilingual", multilingual) viper.Set("CurrentContentLanguage", NewLanguage(lang))+ viper.Set("DefaultContentLanguage", "en")+ viper.Set("DefaultContentLanguageInSubdir", defaultInSubDir)+
tests := []struct {input string
baseURL string
@@ -79,12 +84,17 @@
output := AbsURL(test.input, addLanguage)
expected := test.expected
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 {expected = strings.Replace(expected, "MULTI", "", 1)
}
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 TestRelURL(t *testing.T) {- for _, addLanguage := range []bool{true, false} {- for _, m := range []bool{true, false} {- for _, l := range []string{"en", "fr"} {- doTestRelURL(t, addLanguage, m, l)
+ for _, defaultInSubDir := range []bool{true, false} {+ for _, addLanguage := range []bool{true, false} {+ for _, m := range []bool{true, false} {+ 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.Set("Multilingual", multilingual) viper.Set("CurrentContentLanguage", NewLanguage(lang))+ viper.Set("DefaultContentLanguage", "en")+ viper.Set("DefaultContentLanguageInSubdir", defaultInSubDir) tests := []struct {input string
@@ -146,7 +160,11 @@
expected := test.expected
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 {expected = strings.Replace(expected, "MULTI", "", 1)
}
--- a/hugolib/config.go
+++ b/hugolib/config.go
@@ -104,4 +104,5 @@
viper.SetDefault("UseModTimeAsFallback", false) viper.SetDefault("Multilingual", false) viper.SetDefault("DefaultContentLanguage", "en")+ viper.SetDefault("DefaultContentLanguageInSubdir", false)}
--- a/hugolib/hugo_sites_test.go
+++ b/hugolib/hugo_sites_test.go
@@ -32,6 +32,9 @@
viper.SetFs(hugofs.Source())
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 { panic("Content folder creation failed.")}
@@ -38,6 +41,125 @@
}
+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) {testCommonResetState()
sites := createMultiTestSites(t, multiSiteTomlConfig)
@@ -397,7 +519,7 @@
DisableRSS = false
RSSUri = "index.xml"
-paginate = 2
+paginate = 1
DefaultContentLanguage = "fr"
[permalinks]
@@ -435,7 +557,7 @@
if err := afero.WriteFile(hugofs.Source(),
filepath.Join("layouts", "_default/list.html"),- []byte("List: {{ .Title }}"),+ []byte("{{ $p := .Paginator }}List Page {{ $p.PageNumber }}: {{ .Title }}|{{ i18n \"hello\" }}|{{ .Permalink }}"), 0755); err != nil { t.Fatalf("Failed to write layout file: %s", err)}
@@ -442,7 +564,7 @@
if err := afero.WriteFile(hugofs.Source(),
filepath.Join("layouts", "index.html"),- []byte("Home: {{ .Title }}|{{ .IsHome }}"),+ []byte("{{ $p := .Paginator }}Home Page {{ $p.PageNumber }}: {{ .Title }}|{{ .IsHome }}|{{ i18n \"hello\" }}|{{ .Permalink }}"), 0755); err != nil { t.Fatalf("Failed to write layout file: %s", err)}
@@ -505,6 +627,7 @@
publishdate: "2000-01-03"
tags:
- tag2
+ - tag1
url: /superbob
---
# doc3
--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -176,7 +176,7 @@
}
func (n *Node) URL() string {- return n.addMultilingualWebPrefix(n.URLPath.URL)
+ return n.addLangPathPrefix(n.URLPath.URL)
}
func (n *Node) Permalink() string {@@ -206,8 +206,27 @@
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() { n.languageInit.Do(func() {+ if n.language != nil {+ return
+ }
pageLang := n.lang
ml := n.Site.multilingual
if ml == nil {@@ -278,19 +297,22 @@
})
}
-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) {return outfile
}
- hadSlashSuffix := strings.HasSuffix(outfile, "/")
-
- lang := n.Lang()
- if lang == "" || !n.Site.IsMultiLingual() {+ if !should {return outfile
}
- outfile = "/" + path.Join(lang, outfile)
+
+ hadSlashSuffix := strings.HasSuffix(outfile, "/")
+
+ outfile = "/" + path.Join(n.Lang(), outfile)
if hadSlashSuffix {outfile += "/"
}
@@ -297,10 +319,12 @@
return outfile
}
-func (n *Node) addMultilingualFilesystemPrefix(outfile string) string {- lang := n.Lang()
- if lang == "" || !n.Site.IsMultiLingual() {+func (n *Node) addLangFilepathPrefix(outfile string) string {+ if outfile == "" {+ outfile = helpers.FilePathSeparator
+ }
+ if !n.shouldAddLanguagePrefix() {return outfile
}
- return string(filepath.Separator) + filepath.Join(lang, outfile)
+ return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
}
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -514,7 +514,7 @@
}
}
- permalink = p.addMultilingualWebPrefix(permalink)
+ permalink = p.addLangPathPrefix(permalink)
return helpers.MakePermalink(baseURL, permalink), nil
}
@@ -1059,7 +1059,7 @@
outfile += "index.html"
}
outfile = filepath.FromSlash(outfile)
- outfile = p.addMultilingualFilesystemPrefix(outfile)
+ outfile = p.addLangFilepathPrefix(outfile)
return
}
}
@@ -1071,5 +1071,5 @@
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)))
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -167,10 +167,11 @@
paginationPageCount uint64
Data *map[string]interface{}- multilingual *Multilingual
- Language *helpers.Language
- LanguagePrefix string
- Languages helpers.Languages
+ multilingual *Multilingual
+ Language *helpers.Language
+ LanguagePrefix string
+ Languages helpers.Languages
+ defaultContentLanguageInSubdir bool
}
// Used in tests.
@@ -864,42 +865,45 @@
permalinks[k] = pathPattern(v)
}
+ defaultContentInSubDir := viper.GetBool("DefaultContentLanguageInSubdir")+ defaultContentLanguage := viper.GetString("DefaultContentLanguage")+
languagePrefix := ""
- if s.multilingualEnabled() {+ if s.multilingualEnabled() && (defaultContentInSubDir || lang.Lang != defaultContentLanguage) {languagePrefix = "/" + lang.Lang
}
var multilingual *Multilingual
-
if s.owner != nil {multilingual = s.owner.multilingual
}
s.Info = SiteInfo{- BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),- Title: lang.GetString("Title"),- Author: lang.GetStringMap("author"),- Social: lang.GetStringMapString("social"),- LanguageCode: lang.GetString("languagecode"),- Copyright: lang.GetString("copyright"),- DisqusShortname: lang.GetString("DisqusShortname"),- multilingual: multilingual,
- Language: lang,
- LanguagePrefix: languagePrefix,
- Languages: languages,
- GoogleAnalytics: lang.GetString("GoogleAnalytics"),- RSSLink: permalinkStr(viper.GetString("RSSUri")),- BuildDrafts: viper.GetBool("BuildDrafts"),- canonifyURLs: viper.GetBool("CanonifyURLs"),- preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),- AllPages: &s.AllPages,
- Pages: &s.Pages,
- rawAllPages: &s.rawAllPages,
- Files: &s.Files,
- Menus: &s.Menus,
- Params: params,
- Permalinks: permalinks,
- Data: &s.Data,
+ BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),+ Title: lang.GetString("Title"),+ Author: lang.GetStringMap("author"),+ Social: lang.GetStringMapString("social"),+ LanguageCode: lang.GetString("languagecode"),+ Copyright: lang.GetString("copyright"),+ DisqusShortname: lang.GetString("DisqusShortname"),+ multilingual: multilingual,
+ Language: lang,
+ LanguagePrefix: languagePrefix,
+ Languages: languages,
+ defaultContentLanguageInSubdir: defaultContentInSubDir,
+ GoogleAnalytics: lang.GetString("GoogleAnalytics"),+ RSSLink: permalinkStr(viper.GetString("RSSUri")),+ BuildDrafts: viper.GetBool("BuildDrafts"),+ canonifyURLs: viper.GetBool("CanonifyURLs"),+ preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),+ AllPages: &s.AllPages,
+ Pages: &s.Pages,
+ rawAllPages: &s.rawAllPages,
+ Files: &s.Files,
+ Menus: &s.Menus,
+ Params: params,
+ Permalinks: permalinks,
+ Data: &s.Data,
}
}
@@ -1280,7 +1284,7 @@
if p.Section() != "" { me := MenuEntry{Identifier: 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 {// menu with same id defined in config, let that one win
continue
@@ -1422,7 +1426,7 @@
}
}
- if s.owner.multilingual.enabled() {+ if s.owner.multilingual.enabled() && s.Info.defaultContentLanguageInSubdir {mainLang := s.owner.multilingual.DefaultLang.Lang
mainLangURL := helpers.AbsURL(mainLang, false)
jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)@@ -1612,19 +1616,6 @@
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) {defer wg.Done()
@@ -1637,14 +1628,13 @@
[]string{"taxonomy/" + t.singular + ".html", "indexes/" + t.singular + ".html", "_default/taxonomy.html", "_default/list.html"})n, base = s.newTaxonomyNode(t)
+ baseWithLanguagePrefix := n.addLangPathPrefix(base)
- base = s.addMultilingualPrefix(base)
-
dest := base
if viper.GetBool("UglyURLs") {- dest = helpers.Uglify(base + ".html")
+ dest = helpers.Uglify(baseWithLanguagePrefix + ".html")
} 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 {@@ -1657,7 +1647,7 @@
paginatePath := viper.GetString("paginatePath")// 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()
@@ -1675,7 +1665,7 @@
taxonomyPagerNode.Lastmod = first.Lastmod
}
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 {results <- err
continue
@@ -1686,11 +1676,10 @@
if !viper.GetBool("DisableRSS") {// XML Feed
rssuri := viper.GetString("RSSUri")- n.URLPath.URL = permalinkStr(base + "/" + rssuri)
- n.URLPath.Permalink = permalink(base)
+ s.setURLs(n, base+"/"+rssuri)
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
continue
}
@@ -1714,7 +1703,7 @@
layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}layouts = s.appendThemeTemplates(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
}
}
@@ -1755,9 +1744,9 @@
section = helpers.MakePathSanitized(section)
}
- base := s.addMultilingualPrefix(section)
-
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 {return err
}
@@ -1795,10 +1784,9 @@
if !viper.GetBool("DisableRSS") && section != "" {// XML Feed
rssuri := viper.GetString("RSSUri")- n.URLPath.URL = permalinkStr(base + "/" + rssuri)
- n.URLPath.Permalink = permalink(base)
+ s.setURLs(n, section+"/"+rssuri)
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
}
}
@@ -1807,19 +1795,21 @@
}
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 {+ layouts := s.appendThemeTemplates([]string{"index.html", "_default/list.html"})+ base := n.addLangFilepathPrefix("")+ if err := s.renderAndWritePage("homepage", base, n, layouts...); err != nil {return err
}
if n.paginator != nil {-
paginatePath := viper.GetString("paginatePath")// 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()
@@ -1838,7 +1828,7 @@
}
pageNumber := i + 1
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 {return err
}
@@ -1847,7 +1837,7 @@
if !viper.GetBool("DisableRSS") {// XML Feed
- n.URLPath.URL = permalinkStr(viper.GetString("RSSUri"))+ s.setURLs(n, viper.GetString("RSSUri"))n.Title = ""
high := 50
if len(s.Pages) < high {@@ -1861,7 +1851,7 @@
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
}
}
@@ -1911,10 +1901,11 @@
pages := make(Pages, 0)
page := &Page{}+ page.language = s.Language
page.Date = s.Info.LastChange
page.Lastmod = s.Info.LastChange
page.Site = &s.Info
- page.URLPath.URL = "/"
+ page.URLPath.URL = ""
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
page.Sitemap.Priority = sitemapDefault.Priority
@@ -1938,8 +1929,8 @@
}
smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}-
- if err := s.renderAndWriteXML("sitemap", s.addMultilingualPrefix(page.Sitemap.Filename), n, s.appendThemeTemplates(smLayouts)...); err != nil {+ addLanguagePrefix := n.Site.IsMultiLingual()
+ if err := s.renderAndWriteXML("sitemap", n.addLangPathPrefixIfFlagSet(page.Sitemap.Filename, addLanguagePrefix), n, s.appendThemeTemplates(smLayouts)...); err != nil {return err
}
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -317,7 +317,6 @@
// Issue #939
// Issue #1923
func TestShouldAlwaysHaveUglyURLs(t *testing.T) {- hugofs.InitMemFs()
for _, uglyURLs := range []bool{true, false} {doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
}
@@ -383,12 +382,7 @@
}
for _, test := range tests {- file, err := hugofs.Destination().Open(test.doc)
- if err != nil {- t.Fatalf("Did not find %s in target: %s", test.doc, err)- }
-
- content := helpers.ReaderToString(file)
+ content := readDestination(t, test.doc)
if content != test.expected { t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -14,13 +14,12 @@
package hugolib
import (
- "bytes"
"testing"
"reflect"
+ "strings"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
)
@@ -50,15 +49,10 @@
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 {- t.Fatalf("Unable to locate: sitemap.xml")- }
-
- sitemap := helpers.ReaderToBytes(sitemapFile)
- if !bytes.HasPrefix(sitemap, []byte("<?xml")) {- t.Errorf("Sitemap file should start with <?xml. %s", sitemap)+ if !strings.HasPrefix(sitemapContent, "<?xml") {+ t.Errorf("Sitemap file should start with <?xml. %s", sitemapContent)}
}
--- a/tpl/template_embedded.go
+++ b/tpl/template_embedded.go
@@ -75,7 +75,7 @@
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }} <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }} <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 }}<item>
<title>{{ .Title }}</title>--
⑨