summaryrefslogtreecommitdiffstats
path: root/hugolib
diff options
context:
space:
mode:
authorAlexandre Bourget <alex@bourget.cc>2016-05-14 00:35:16 -0400
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2016-09-06 18:32:15 +0300
commitec33732fbe84f67c1164fb713d6cb738609f2e2e (patch)
treee4d361fda15e254617fb0fc2fdba275a269afc65 /hugolib
parentfaa3472fa299adb287d575e6d404d4ddcddbff4e (diff)
Add multilingual support in Hugo
Implements: * support to render: * content/post/whatever.en.md to /en/2015/12/22/whatever/index.html * content/post/whatever.fr.md to /fr/2015/12/22/whatever/index.html * gets enabled when `Multilingual:` is specified in config. * support having language switchers in templates, that know where the translated page is (with .Page.Translations) (when you're on /en/about/, you can have a "Francais" link pointing to /fr/a-propos/) * all translations are in the `.Page.Translations` map, including the current one. * easily tweak themes to support Multilingual mode * renders in a single swift, no need for two config files. Adds a couple of variables useful for multilingual sites Adds documentation (content/multilingual.md) Added language prefixing for all URL generation/permalinking see in the code base. Implements i18n. Leverages the great github.com/nicksnyder/go-i18n lib.. thanks Nick. * Adds "i18n" and "T" template functions..
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/embedded_shortcodes_test.go4
-rw-r--r--hugolib/i18n.go36
-rw-r--r--hugolib/menu_test.go8
-rw-r--r--hugolib/multilingual.go48
-rw-r--r--hugolib/page.go67
-rw-r--r--hugolib/permalinks.go2
-rw-r--r--hugolib/planner.go2
-rw-r--r--hugolib/robotstxt_test.go8
-rw-r--r--hugolib/rss_test.go8
-rw-r--r--hugolib/site.go226
-rw-r--r--hugolib/site_test.go242
-rw-r--r--hugolib/site_url_test.go7
-rw-r--r--hugolib/sitemap_test.go8
-rw-r--r--hugolib/taxonomy_test.go12
-rw-r--r--hugolib/translations.go59
15 files changed, 538 insertions, 199 deletions
diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go
index 360ec1209..18f807fbf 100644
--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -56,8 +56,8 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
templ := tpl.New()
p, _ := pageFromString(simplePageWithURL, path)
p.Node.Site = &SiteInfo{
- Pages: &(Pages{p}),
- BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(baseURL)),
+ AllPages: &(Pages{p}),
+ BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(baseURL)),
}
output, err := HandleShortcodes(in, p, templ)
diff --git a/hugolib/i18n.go b/hugolib/i18n.go
new file mode 100644
index 000000000..cb5061501
--- /dev/null
+++ b/hugolib/i18n.go
@@ -0,0 +1,36 @@
+// Copyright 2016 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
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/tpl"
+)
+
+func loadI18n(sources []source.Input, lang string) (err error) {
+ i18nBundle := bundle.New()
+ for _, currentSource := range sources {
+ for _, r := range currentSource.Files() {
+ err = i18nBundle.ParseTranslationFileBytes(r.LogicalName(), r.Bytes())
+ if err != nil {
+ return
+ }
+ }
+ }
+
+ tpl.SetI18nTfunc(lang, i18nBundle)
+
+ return nil
+}
diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go
index df6ccf629..602775a4b 100644
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -691,13 +691,7 @@ func testSiteSetup(s *Site, t *testing.T) {
s.Menus = Menus{}
s.initializeSiteInfo()
- if err := s.createPages(); err != nil {
- t.Fatalf("Unable to create pages: %s", err)
- }
-
- if err := s.buildSiteMeta(); err != nil {
- t.Fatalf("Unable to build site metadata: %s", err)
- }
+ createPagesAndMeta(t, s)
}
func tomlToMap(s string) (map[string]interface{}, error) {
diff --git a/hugolib/multilingual.go b/hugolib/multilingual.go
new file mode 100644
index 000000000..2552bad4c
--- /dev/null
+++ b/hugolib/multilingual.go
@@ -0,0 +1,48 @@
+package hugolib
+
+import (
+ "github.com/spf13/cast"
+ "github.com/spf13/viper"
+)
+
+type Multilingual struct {
+ enabled bool
+ config *viper.Viper
+
+ Languages []string
+}
+
+func (ml *Multilingual) GetString(key string) string { return cast.ToString(ml.Get(key)) }
+func (ml *Multilingual) GetStringMap(key string) map[string]interface{} {
+ return cast.ToStringMap(ml.Get(key))
+}
+
+func (ml *Multilingual) GetStringMapString(key string) map[string]string {
+ return cast.ToStringMapString(ml.Get(key))
+}
+
+func (ml *Multilingual) Get(key string) interface{} {
+ if ml != nil && ml.config != nil && ml.config.IsSet(key) {
+ return ml.config.Get(key)
+ }
+ return viper.Get(key)
+}
+
+func (s *Site) SetMultilingualConfig(currentLang string, orderedLanguages []string, langConfigs map[string]interface{}) {
+ conf := viper.New()
+ for k, val := range cast.ToStringMap(langConfigs[currentLang]) {
+ conf.Set(k, val)
+ }
+ conf.Set("CurrentLanguage", currentLang)
+ ml := &Multilingual{
+ enabled: len(langConfigs) > 0,
+ config: conf,
+ Languages: orderedLanguages,
+ }
+ viper.Set("Multilingual", ml.enabled)
+ s.Multilingual = ml
+}
+
+func (s *Site) multilingualEnabled() bool {
+ return s.Multilingual != nil && s.Multilingual.enabled
+}
diff --git a/hugolib/page.go b/hugolib/page.go
index f64cdf95f..5c281bb96 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -61,8 +61,10 @@ type Page struct {
PublishDate time.Time
ExpiryDate time.Time
Markup string
+ Translations Translations
extension string
contentType string
+ lang string
renderable bool
Layout string
layoutsCalculated []string
@@ -300,9 +302,11 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
func newPage(filename string) *Page {
page := Page{contentType: "",
- Source: Source{File: *source.NewFile(filename)},
- Node: Node{Keywords: []string{}, Sitemap: Sitemap{Priority: -1}},
- Params: make(map[string]interface{})}
+ Source: Source{File: *source.NewFile(filename)},
+ Node: Node{Keywords: []string{}, Sitemap: Sitemap{Priority: -1}},
+ Params: make(map[string]interface{}),
+ Translations: make(Translations),
+ }
jww.DEBUG.Println("Reading from", page.File.Path())
return &page
@@ -445,11 +449,13 @@ func (p *Page) permalink() (*url.URL, error) {
if len(pSlug) > 0 {
permalink = helpers.URLPrep(viper.GetBool("UglyURLs"), path.Join(dir, p.Slug+"."+p.Extension()))
} else {
- _, t := filepath.Split(p.Source.LogicalName())
+ t := p.Source.TranslationBaseName()
permalink = helpers.URLPrep(viper.GetBool("UglyURLs"), path.Join(dir, helpers.ReplaceExtension(strings.TrimSpace(t), p.Extension())))
}
}
+ permalink = p.addMultilingualWebPrefix(permalink)
+
return helpers.MakePermalink(baseURL, permalink), nil
}
@@ -460,6 +466,10 @@ func (p *Page) Extension() string {
return viper.GetString("DefaultExtension")
}
+func (p *Page) Lang() string {
+ return p.lang
+}
+
func (p *Page) LinkTitle() string {
if len(p.linkTitle) > 0 {
return p.linkTitle
@@ -699,29 +709,29 @@ func (p *Page) getParam(key string, stringToLower bool) interface{} {
return nil
}
- switch v.(type) {
+ switch val := v.(type) {
case bool:
- return v
- case time.Time:
- return v
+ return val
+ case string:
+ if stringToLower {
+ return strings.ToLower(val)
+ }
+ return val
case int64, int32, int16, int8, int:
return cast.ToInt(v)
case float64, float32:
return cast.ToFloat64(v)
- case map[string]interface{}: // JSON and TOML
- return v
- case map[interface{}]interface{}: // YAML
- return v
- case string:
- if stringToLower {
- return strings.ToLower(v.(string))
- }
- return v
+ case time.Time:
+ return val
case []string:
if stringToLower {
- return helpers.SliceToLower(v.([]string))
+ return helpers.SliceToLower(val)
}
return v
+ case map[string]interface{}: // JSON and TOML
+ return v
+ case map[interface{}]interface{}: // YAML
+ return v
}
jww.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
@@ -851,6 +861,7 @@ func (p *Page) parse(reader io.Reader) error {
p.renderable = psr.IsRenderable()
p.frontmatter = psr.FrontMatter()
p.rawContent = psr.Content()
+ p.lang = p.Source.File.Lang()
meta, err := psr.Metadata()
if meta != nil {
@@ -975,7 +986,6 @@ func (p *Page) FullFilePath() string {
}
func (p *Page) TargetPath() (outfile string) {
-
// Always use URL if it's specified
if len(strings.TrimSpace(p.URL)) > 2 {
outfile = strings.TrimSpace(p.URL)
@@ -997,6 +1007,7 @@ func (p *Page) TargetPath() (outfile string) {
outfile += "index.html"
}
outfile = filepath.FromSlash(outfile)
+ outfile = p.addMultilingualFilesystemPrefix(outfile)
return
}
}
@@ -1005,8 +1016,22 @@ func (p *Page) TargetPath() (outfile string) {
outfile = strings.TrimSpace(p.Slug) + "." + p.Extension()
} else {
// Fall back to filename
- outfile = helpers.ReplaceExtension(p.Source.LogicalName(), p.Extension())
+ outfile = helpers.ReplaceExtension(p.Source.TranslationBaseName(), p.Extension())
}
- return filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile))
+ return p.addMultilingualFilesystemPrefix(filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
+}
+
+func (p *Page) addMultilingualWebPrefix(outfile string) string {
+ if p.Lang() == "" {
+ return outfile
+ }
+ return "/" + path.Join(p.Lang(), outfile)
+}
+
+func (p *Page) addMultilingualFilesystemPrefix(outfile string) string {
+ if p.Lang() == "" {
+ return outfile
+ }
+ return string(filepath.Separator) + filepath.Join(p.Lang(), outfile)
}
diff --git a/hugolib/permalinks.go b/hugolib/permalinks.go
index 497fc10b2..581f8740f 100644
--- a/hugolib/permalinks.go
+++ b/hugolib/permalinks.go
@@ -159,7 +159,7 @@ func pageToPermalinkTitle(p *Page, _ string) (string, error) {
func pageToPermalinkFilename(p *Page, _ string) (string, error) {
//var extension = p.Source.Ext
//var name = p.Source.Path()[0 : len(p.Source.Path())-len(extension)]
- return helpers.URLize(p.Source.BaseFileName()), nil
+ return helpers.URLize(p.Source.TranslationBaseName()), nil
}
// if the page has a slug, return the slug, else return the title
diff --git a/hugolib/planner.go b/hugolib/planner.go
index 22dd2586b..6086718c0 100644
--- a/hugolib/planner.go
+++ b/hugolib/planner.go
@@ -25,7 +25,7 @@ func (s *Site) ShowPlan(out io.Writer) (err error) {
fmt.Fprintf(out, "No source files provided.\n")
}
- for _, p := range s.Pages {
+ for _, p := range s.AllPages {
fmt.Fprintf(out, "%s", p.Source.Path())
if p.IsRenderable() {
fmt.Fprintf(out, " (renderer: markdown)")
diff --git a/hugolib/robotstxt_test.go b/hugolib/robotstxt_test.go
index 776ff3aca..461d04e5d 100644
--- a/hugolib/robotstxt_test.go
+++ b/hugolib/robotstxt_test.go
@@ -46,13 +46,7 @@ func TestRobotsTXTOutput(t *testing.T) {
s.prepTemplates("robots.txt", robotTxtTemplate)
- if err := s.createPages(); err != nil {
- t.Fatalf("Unable to create pages: %s", err)
- }
-
- if err := s.buildSiteMeta(); err != nil {
- t.Fatalf("Unable to build site metadata: %s", err)
- }
+ createPagesAndMeta(t, s)
if err := s.renderHomePage(); err != nil {
t.Fatalf("Unable to RenderHomePage: %s", err)
diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go
index 5eca01143..ec6ee87bd 100644
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_test.go
@@ -59,13 +59,7 @@ func TestRSSOutput(t *testing.T) {
s.initializeSiteInfo()
s.prepTemplates("rss.xml", rssTemplate)
- if err := s.createPages(); err != nil {
- t.Fatalf("Unable to create pages: %s", err)
- }
-
- if err := s.buildSiteMeta(); err != nil {
- t.Fatalf("Unable to build site metadata: %s", err)
- }
+ createPagesAndMeta(t, s)
if err := s.renderHomePage(); err != nil {
t.Fatalf("Unable to RenderHomePage: %s", err)
diff --git a/hugolib/site.go b/hugolib/site.go
index c5851e813..308e35c90 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -20,6 +20,7 @@ import (
"io"
"net/url"
"os"
+ "path"
"path/filepath"
"sort"
"strconv"
@@ -29,8 +30,6 @@ import (
"sync/atomic"
- "path"
-
"github.com/bep/inflect"
"github.com/fsnotify/fsnotify"
"github.com/spf13/afero"
@@ -76,6 +75,7 @@ var distinctErrorLogger = helpers.NewDistinctErrorLogger()
// 5. The entire collection of files is written to disk.
type Site struct {
Pages Pages
+ AllPages Pages
Files []*source.File
Tmpl tpl.Template
Taxonomies TaxonomyList
@@ -87,6 +87,7 @@ type Site struct {
targets targetList
targetListInit sync.Once
RunMode runmode
+ Multilingual *Multilingual
draftCount int
futureCount int
expiredCount int
@@ -106,7 +107,8 @@ type SiteInfo struct {
Authors AuthorList
Social SiteSocial
Sections Taxonomy
- Pages *Pages
+ Pages *Pages // Includes only pages in this language
+ AllPages *Pages // Includes other translated pages, excluding those in this language.
Files *[]*source.File
Menus *Menus
Hugo *HugoInfo
@@ -125,6 +127,11 @@ type SiteInfo struct {
preserveTaxonomyNames bool
paginationPageCount uint64
Data *map[string]interface{}
+
+ Multilingual bool
+ CurrentLanguage string
+ LanguagePrefix string
+ Languages []string
}
// SiteSocial is a place to put social details on a site level. These are the
@@ -150,17 +157,17 @@ func (s *SiteInfo) GetParam(key string) interface{} {
return nil
}
- switch v.(type) {
+ switch val := v.(type) {
case bool:
- return cast.ToBool(v)
+ return val
case string:
- return cast.ToString(v)
+ return val
case int64, int32, int16, int8, int:
return cast.ToInt(v)
case float64, float32:
return cast.ToFloat64(v)
case time.Time:
- return cast.ToTime(v)
+ return val
case []string:
return v
}
@@ -181,7 +188,7 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error
var link string
if refURL.Path != "" {
- for _, page := range []*Page(*s.Pages) {
+ for _, page := range []*Page(*s.AllPages) {
refPath := filepath.FromSlash(refURL.Path)
if page.Source.Path() == refPath || page.Source.LogicalName() == refPath {
target = page
@@ -256,7 +263,7 @@ func (s *SiteInfo) SourceRelativeLink(ref string, currentPage *Page) (string, er
}
}
- for _, page := range []*Page(*s.Pages) {
+ for _, page := range []*Page(*s.AllPages) {
if page.Source.Path() == refPath {
target = page
break
@@ -265,14 +272,14 @@ func (s *SiteInfo) SourceRelativeLink(ref string, currentPage *Page) (string, er
// need to exhaust the test, then try with the others :/
// if the refPath doesn't end in a filename with extension `.md`, then try with `.md` , and then `/index.md`
mdPath := strings.TrimSuffix(refPath, string(os.PathSeparator)) + ".md"
- for _, page := range []*Page(*s.Pages) {
+ for _, page := range []*Page(*s.AllPages) {
if page.Source.Path() == mdPath {
target = page
break
}
}
indexPath := filepath.Join(refPath, "index.md")
- for _, page := range []*Page(*s.Pages) {
+ for _, page := range []*Page(*s.AllPages) {
if page.Source.Path() == indexPath {
target = page
break
@@ -443,7 +450,7 @@ func (s *Site) ReBuild(events []fsnotify.Event) error {
// If a content file changes, we need to reload only it and re-render the entire site.
- // First step is to read the changed files and (re)place them in site.Pages
+ // First step is to read the changed files and (re)place them in site.AllPages
// This includes processing any meta-data for that content
// The second step is to convert the content into HTML
@@ -479,7 +486,7 @@ func (s *Site) ReBuild(events []fsnotify.Event) error {
if len(tmplChanged) > 0 || len(dataChanged) > 0 {
// Do not need to read the files again, but they need conversion
// for shortocde re-rendering.
- for _, p := range s.Pages {
+ for _, p := range s.AllPages {
pageChan <- p
}
}
@@ -538,6 +545,9 @@ func (s *Site) ReBuild(events []fsnotify.Event) error {
s.timerStep("read & convert pages from source")
+ // FIXME: does this go inside the next `if` statement ?
+ s.setupTranslations()
+
if len(sourceChanged) > 0 {
s.setupPrevNext()
if err = s.buildSiteMeta(); err != nil {
@@ -665,9 +675,9 @@ func (s *Site) readDataFromSourceFS() error {
dataSources = append(dataSources, &source.Filesystem{Base: s.absDataDir()})
// have to be last - duplicate keys in earlier entries will win
- themeStaticDir, err := helpers.GetThemeDataDirPath()
+ themeDataDir, err := helpers.GetThemeDataDirPath()
if err == nil {
- dataSources = append(dataSources, &source.Filesystem{Base: themeStaticDir})
+ dataSources = append(dataSources, &source.Filesystem{Base: themeDataDir})
}
err = s.loadData(dataSources)
@@ -688,10 +698,25 @@ func (s *Site) Process() (err error) {
return
}
+ i18nSources := []source.Input{&source.Filesystem{Base: s.absI18nDir()}}
+
+ themeI18nDir, err := helpers.GetThemeI18nDirPath()
+ if err == nil {
+ i18nSources = []source.Input{&source.Filesystem{Base: themeI18nDir}, i18nSources[0]}
+ }
+
+ if err = loadI18n(i18nSources, s.Multilingual.GetString("CurrentLanguage")); err != nil {
+ return
+ }
+ s.timerStep("load i18n")
+
if err = s.createPages(); err != nil {
return
}
+
+ s.setupTranslations()
s.setupPrevNext()
+
if err = s.buildSiteMeta(); err != nil {
return
}
@@ -711,6 +736,27 @@ func (s *Site) setupPrevNext() {
}
}
+func (s *Site) setupTranslations() {
+ if !s.multilingualEnabled() {
+ s.Pages = s.AllPages
+ return
+ }
+
+ currentLang := s.Multilingual.GetString("CurrentLanguage")
+
+ allTranslations := pagesToTranslationsMap(s.AllPages)
+ assignTranslationsToPages(allTranslations, s.AllPages)
+
+ var currentLangPages []*Page
+ for _, p := range s.AllPages {
+ if p.Lang() == "" || strings.HasPrefix(currentLang, p.lang) {
+ currentLangPages = append(currentLangPages, p)
+ }
+ }
+
+ s.Pages = currentLangPages
+}
+
func (s *Site) Render() (err error) {
if err = s.renderAliases(); err != nil {
return
@@ -771,32 +817,47 @@ func (s *Site) initialize() (err error) {
}
func (s *Site) initializeSiteInfo() {
- params := viper.GetStringMap("Params")
+ params := s.Multilingual.GetStringMap("Params")
permalinks := make(PermalinkOverrides)
for k, v := range viper.GetStringMapString("Permalinks") {
permalinks[k] = pathPattern(v)
}
+ languagePrefix := ""
+ if s.multilingualEnabled() {
+ languagePrefix = "/" + s.Multilingual.GetString("CurrentLanguage")
+ }
+
+ languages := []string{}
+ if s.Multilingual != nil {
+ languages = s.Multilingual.Languages
+ }
+
s.Info = SiteInfo{
BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),
- Title: viper.GetString("Title"),
- Author: viper.GetStringMap("author"),
- Social: viper.GetStringMapString("social"),
- LanguageCode: viper.GetString("languagecode"),
- Copyright: viper.GetString("copyright"),
- DisqusShortname: viper.GetString("DisqusShortname"),
+ Title: s.Multilingual.GetString("Title"),
+ Author: s.Multilingual.GetStringMap("author"),
+ Social: s.Multilingual.GetStringMapString("social"),
+ LanguageCode: s.Multilingual.GetString("languagecode"),
+ Copyright: s.Multilingual.GetString("copyright"),
+ DisqusShortname: s.Multilingual.GetString("DisqusShortname"),
+ Multilingual: s.multilingualEnabled(),
+ CurrentLanguage: s.Multilingual.GetString("CurrentLanguage"),
+ LanguagePrefix: languagePrefix,
+ Languages: languages,
GoogleAnalytics: viper.GetString("GoogleAnalytics"),
RSSLink: s.permalinkStr(viper.GetString("RSSUri")),
BuildDrafts: viper.GetBool("BuildDrafts"),
canonifyURLs: viper.GetBool("CanonifyURLs"),
preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),
- Pages: &s.Pages,
- Files: &s.Files,
- Menus: &s.Menus,
- Params: params,
- Permalinks: permalinks,
- Data: &s.Data,
+ AllPages: &s.AllPages,
+ Pages: &s.Pages,
+ Files: &s.Files,
+ Menus: &s.Menus,
+ Params: params,
+ Permalinks: permalinks,
+ Data: &s.Data,
}
}
@@ -808,6 +869,10 @@ func (s *Site) absDataDir() string {
return helpers.AbsPathify(viper.GetString("DataDir"))
}
+func (s *Site) absI18nDir() string {
+ return helpers.AbsPathify(viper.GetString("I18nDir"))
+}
+
func (s *Site) absThemeDir() string {
return helpers.AbsPathify(viper.GetString("themesDir") + "/" + viper.GetString("theme"))
}
@@ -903,7 +968,7 @@ func (s *Site) convertSource() chan error {
go converterCollator(s, results, errs)
- for _, p := range s.Pages {
+ for _, p := range s.AllPages {
pageChan <- p
}
@@ -997,7 +1062,7 @@ func converterCollator(s *Site, results <-chan HandledResult, errs chan<- error)
func (s *Site) addPage(page *Page) {
if page.shouldBuild() {
- s.Pages = append(s.Pages, page)
+ s.AllPages = append(s.AllPages, page)
}
if page.IsDraft() {
@@ -1014,8 +1079,8 @@ func (s *Site) addPage(page *Page) {
}
func (s *Site) removePageByPath(path string) {
- if i := s.Pages.FindPagePosByFilePath(path); i >= 0 {
- page := s.Pages[i]
+ if i := s.AllPages.FindPagePosByFilePath(path); i >= 0 {
+ page := s.AllPages[i]
if page.IsDraft() {
s.draftCount--
@@ -1029,12 +1094,12 @@ func (s *Site) removePageByPath(path string) {
s.expiredCount--
}
- s.Pages = append(s.Pages[:i], s.Pages[i+1:]...)
+ s.AllPages = append(s.AllPages[:i], s.AllPages[i+1:]...)
}
}
func (s *Site) removePage(page *Page) {
- if i := s.Pages.FindPagePos(page); i >= 0 {
+ if i := s.AllPages.FindPagePos(page); i >= 0 {
if page.IsDraft() {
s.draftCount--
}
@@ -1047,7 +1112,7 @@ func (s *Site) removePage(page *Page) {
s.expiredCount--
}
- s.Pages = append(s.Pages[:i], s.Pages[i+1:]...)
+ s.AllPages = append(s.AllPages[:i], s.AllPages[i+1:]...)
}
}
@@ -1086,7 +1151,7 @@ func incrementalReadCollator(s *Site, results <-chan HandledResult, pageChan cha
}
}
- s.Pages.Sort()
+ s.AllPages.Sort()
close(coordinator)
if len(errMsgs) == 0 {
@@ -1112,7 +1177,7 @@ func readCollator(s *Site, results <-chan HandledResult, errs chan<- error) {
}
}
- s.Pages.Sort()
+ s.AllPages.Sort()
if len(errMsgs) == 0 {
errs <- nil
return
@@ -1298,9 +1363,8 @@ func (s *Site) resetPageBuildState() {
s.Info.paginationPageCount = 0
- for _, p := range s.Pages {
+ for _, p := range s.AllPages {
p.scratch = newScratch()
-
}
}
@@ -1326,17 +1390,6 @@ func (s *Site) assembleSections() {
}
}
-func (s *Site) possibleTaxonomies() (taxonomies []string) {
- for _, p := range s.Pages {
- for k := range p.Params {
- if !helpers.InStringArray(taxonomies, k) {
- taxonomies = append(taxonomies, k)
- }
- }
- }
- return
-}
-
// renderAliases renders shell pages that simply have a redirect in the header.
func (s *Site) renderAliases() error {
for _, p := range s.Pages {
@@ -1536,6 +1589,19 @@ func (s *Site) newTaxonomyNode(t taxRenderInfo) (*Node, string) {
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.Multilingual.GetString("CurrentLanguage"), basePath)
+ if hadPrefix {
+ basePath = "/" + basePath
+ }
+ }
+ return basePath
+}
+
func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
@@ -1549,6 +1615,8 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
n, base = s.newTaxonomyNode(t)
+ base = s.addMultilingualPrefix(base)
+
dest := base
if viper.GetBool("UglyURLs") {
dest = helpers.Uglify(base + ".html")
@@ -1623,7 +1691,7 @@ func (s *Site) renderListsOfTaxonomyTerms() (err error) {
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, plural+"/index.html", n, layouts...); err != nil {
+ if err := s.renderAndWritePage("taxonomy terms for "+singular, s.addMultilingualPrefix(plural+"/index.html"), n, layouts...); err != nil {
return err
}
}
@@ -1664,8 +1732,10 @@ func (s *Site) renderSectionLists() error {
section = helpers.MakePathSanitized(section)
}
+ base := s.addMultilingualPrefix(section)
+
n := s.newSectionLi