diff options
author | Alexandre Bourget <alex@bourget.cc> | 2016-05-14 00:35:16 -0400 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2016-09-06 18:32:15 +0300 |
commit | ec33732fbe84f67c1164fb713d6cb738609f2e2e (patch) | |
tree | e4d361fda15e254617fb0fc2fdba275a269afc65 /hugolib | |
parent | faa3472fa299adb287d575e6d404d4ddcddbff4e (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.go | 4 | ||||
-rw-r--r-- | hugolib/i18n.go | 36 | ||||
-rw-r--r-- | hugolib/menu_test.go | 8 | ||||
-rw-r--r-- | hugolib/multilingual.go | 48 | ||||
-rw-r--r-- | hugolib/page.go | 67 | ||||
-rw-r--r-- | hugolib/permalinks.go | 2 | ||||
-rw-r--r-- | hugolib/planner.go | 2 | ||||
-rw-r--r-- | hugolib/robotstxt_test.go | 8 | ||||
-rw-r--r-- | hugolib/rss_test.go | 8 | ||||
-rw-r--r-- | hugolib/site.go | 226 | ||||
-rw-r--r-- | hugolib/site_test.go | 242 | ||||
-rw-r--r-- | hugolib/site_url_test.go | 7 | ||||
-rw-r--r-- | hugolib/sitemap_test.go | 8 | ||||
-rw-r--r-- | hugolib/taxonomy_test.go | 12 | ||||
-rw-r--r-- | hugolib/translations.go | 59 |
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 |