ref: c4e7c37055a029a26d87ebeb21614efb3f0b0040
parent: 06d12ab895a83fc8a9f94b23e533b25511bbb6d1
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Mon Jul 25 18:22:09 EDT 2016
Add Translations and AllTranslations methods to Page Will revisit Node later.
--- a/commands/multilingual.go
+++ b/commands/multilingual.go
@@ -33,6 +33,7 @@
func toSortedLanguages(l map[string]interface{}) (hugolib.Languages, error) {langs := make(hugolib.Languages, len(l))
+ i := 0
for lang, langConf := range l { langsMap, ok := langConf.(map[string]interface{})@@ -57,7 +58,8 @@
language.SetParam(loki, v)
}
- langs = append(langs, language)
+ langs[i] = language
+ i++
}
sort.Sort(langs)
--- a/hugolib/multilingual.go
+++ b/hugolib/multilingual.go
@@ -3,6 +3,7 @@
import (
"sync"
+ "sort"
"strings"
"github.com/spf13/cast"
@@ -23,16 +24,40 @@
type Languages []*Language
+func NewLanguages(l ...*Language) Languages {+ languages := make(Languages, len(l))
+ for i := 0; i < len(l); i++ {+ languages[i] = l[i]
+ }
+ sort.Sort(languages)
+ return languages
+}
+
func (l Languages) Len() int { return len(l) } func (l Languages) Less(i, j int) bool { return l[i].Weight < l[j].Weight } func (l Languages) Swap(i, j int) { l[i], l[j] = l[j], l[i] } type Multilingual struct {- enabled bool
-
Languages Languages
+
+ langMap map[string]*Language
+ langMapInit sync.Once
}
+func (ml *Multilingual) Language(lang string) *Language {+ ml.langMapInit.Do(func() {+ ml.langMap = make(map[string]*Language)
+ for _, l := range ml.Languages {+ ml.langMap[l.Lang] = l
+ }
+ })
+ return ml.langMap[lang]
+}
+
+func (ml *Multilingual) enabled() bool {+ return len(ml.Languages) > 0
+}
+
func (l *Language) Params() map[string]interface{} { l.paramsInit.Do(func() {// Merge with global config.
@@ -73,15 +98,14 @@
// TODO(bep) multilingo evaluate
viper.Set("CurrentLanguage", currentLang) ml := &Multilingual{- enabled: len(languages) > 0,
Languages: languages,
}
- viper.Set("Multilingual", ml.enabled)+ viper.Set("Multilingual", ml.enabled())s.Multilingual = ml
}
func (s *Site) multilingualEnabled() bool {- return s.Multilingual != nil && s.Multilingual.enabled
+ return s.Multilingual != nil && s.Multilingual.enabled()
}
func currentLanguageString() string {--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -14,10 +14,11 @@
package hugolib
import (
- "github.com/spf13/cast"
"html/template"
"sync"
"time"
+
+ "github.com/spf13/cast"
)
type Node struct {--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -61,10 +61,11 @@
PublishDate time.Time
ExpiryDate time.Time
Markup string
- Translations Translations
+ translations Pages
extension string
contentType string
lang string
+ language *Language
renderable bool
Layout string
layoutsCalculated []string
@@ -305,7 +306,7 @@
Source: Source{File: *source.NewFile(filename)}, Node: Node{Keywords: []string{}, Sitemap: Sitemap{Priority: -1}}, Params: make(map[string]interface{}),- Translations: make(Translations),
+ translations: make(Pages, 0),
}
jww.DEBUG.Println("Reading from", page.File.Path())@@ -466,8 +467,28 @@
return viper.GetString("DefaultExtension")}
+// TODO(bep) multilingo consolidate
+func (p *Page) Language() *Language {+ return p.language
+}
func (p *Page) Lang() string {return p.lang
+}
+
+// AllTranslations returns all translations, including the current Page.
+func (p *Page) AllTranslations() Pages {+ return p.translations
+}
+
+// Translations returns the translations excluding the current Page.
+func (p *Page) Translations() Pages {+ translations := make(Pages, 0)
+ for _, t := range p.translations {+ if t != p {+ translations = append(translations, t)
+ }
+ }
+ return translations
}
func (p *Page) LinkTitle() string {--- a/hugolib/pageSort.go
+++ b/hugolib/pageSort.go
@@ -56,6 +56,19 @@
return p1.Weight < p2.Weight
}
+var languagePageSort = func(p1, p2 *Page) bool {+ if p1.language.Weight == p2.language.Weight {+ if p1.Date.Unix() == p2.Date.Unix() {+ if p1.LinkTitle() == p2.LinkTitle() {+ return (p1.FullFilePath() < p2.FullFilePath())
+ }
+ return (p1.LinkTitle() < p2.LinkTitle())
+ }
+ return p1.Date.Unix() > p2.Date.Unix()
+ }
+ return p1.language.Weight < p2.language.Weight
+}
+
func (ps *pageSorter) Len() int { return len(ps.pages) } func (ps *pageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] }@@ -208,6 +221,20 @@
}
pages, _ := spc.get(key, p, pageBy(length).Sort)
+
+ return pages
+}
+
+// ByLanguage sorts the Pages by the language's Weight.
+//
+// Adjacent invocactions on the same receiver will return a cached result.
+//
+// This may safely be executed in parallel.
+func (p Pages) ByLanguage() Pages {+
+ key := "pageSort.ByLanguage"
+
+ pages, _ := spc.get(key, p, pageBy(languagePageSort).Sort)
return pages
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -744,10 +744,10 @@
currentLang := currentLanguageString()
- allTranslations := pagesToTranslationsMap(s.AllPages)
+ allTranslations := pagesToTranslationsMap(s.Multilingual, s.AllPages)
assignTranslationsToPages(allTranslations, s.AllPages)
- var currentLangPages []*Page
+ var currentLangPages Pages
for _, p := range s.AllPages { if p.Lang() == "" || strings.HasPrefix(currentLang, p.lang) {currentLangPages = append(currentLangPages, p)
@@ -2014,6 +2014,10 @@
err := s.render(name, d, renderBuffer, layouts...)
+ if err != nil {+ return err
+ }
+
outBuffer := bp.GetBuffer()
defer bp.PutBuffer(outBuffer)
@@ -2030,11 +2034,8 @@
transformer := transform.NewChain(transform.AbsURLInXML)
transformer.Apply(outBuffer, renderBuffer, path)
- if err == nil {- err = s.writeDestFile(dest, outBuffer)
- }
+ return s.writeDestFile(dest, outBuffer)
- return err
}
func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layouts ...string) error {@@ -2043,6 +2044,16 @@
err := s.render(name, d, renderBuffer, layouts...)
+ if err != nil {+ return err
+ }
+
+ if renderBuffer.Len() == 0 {+ if p, ok := d.(*Page); ok {+ fmt.Println(">>>>", p.Lang(), len(p.Content))+ }
+ }
+
outBuffer := bp.GetBuffer()
defer bp.PutBuffer(outBuffer)
@@ -2107,6 +2118,7 @@
}
if err == nil {+
if err = s.writeDestPage(dest, pageTarget, outBuffer); err != nil {return err
}
@@ -2122,6 +2134,7 @@
}
if err := s.renderThing(d, layout, w); err != nil {+
// Behavior here should be dependent on if running in server or watch mode.
distinctErrorLogger.Printf("Error while rendering %s: %v", name, err) if !s.running() && !testMode {@@ -2145,6 +2158,7 @@
}
func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {+
// If the template doesn't exist, then return, but leave the Writer open
if templ := s.Tmpl.Lookup(layout); templ != nil {return templ.Execute(w, d)
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -1399,12 +1399,6 @@
hugofs.InitMemFs()
- s := &Site{- Source: &source.InMemorySource{ByteSource: sources},- Multilingual: &Multilingual{- enabled: true,
- },
- }
// Multilingual settings
viper.Set("Multilingual", true) en := NewLanguage("en")@@ -1412,6 +1406,14 @@
viper.Set("DefaultContentLanguage", "fr") viper.Set("paginate", "2")+ languages := NewLanguages(en, NewLanguage("fr"))+ s := &Site{+ Source: &source.InMemorySource{ByteSource: sources},+ Multilingual: &Multilingual{+ Languages: languages,
+ },
+ }
+
s.prepTemplates()
s.initializeSiteInfo()
@@ -1425,7 +1427,7 @@
permalink, err := doc1en.Permalink()
assert.NoError(t, err, "permalink call failed")
assert.Equal(t, "http://example.com/blog/en/sect/doc1-slug", permalink, "invalid doc1.en permalink")
- assert.Len(t, doc1en.Translations, 1, "doc1-en should have one translation, excluding itself")
+ assert.Len(t, doc1en.Translations(), 1, "doc1-en should have one translation, excluding itself")
doc2 := s.Pages[1]
permalink, err = doc2.Permalink()
@@ -1440,19 +1442,20 @@
assert.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
- doc1fr := doc1en.Translations["fr"]
+ doc1fr := doc1en.Translations()[0]
permalink, err = doc1fr.Permalink()
assert.NoError(t, err, "permalink call failed")
assert.Equal(t, "http://example.com/blog/fr/sect/doc1", permalink, "invalid doc1fr permalink")
- assert.Equal(t, doc1en.Translations["fr"], doc1fr, "doc1-en should have doc1-fr as translation")
- assert.Equal(t, doc1fr.Translations["en"], doc1en, "doc1-fr should have doc1-en as translation")
+ assert.Equal(t, doc1en.Translations()[0], doc1fr, "doc1-en should have doc1-fr as translation")
+ assert.Equal(t, doc1fr.Translations()[0], doc1en, "doc1-fr should have doc1-en as translation")
+ assert.Equal(t, "fr", doc1fr.Language().Lang)
doc4 := s.AllPages[4]
permalink, err = doc4.Permalink()
assert.NoError(t, err, "permalink call failed")
assert.Equal(t, "http://example.com/blog/fr/sect/doc4", permalink, "invalid doc4 permalink")
- assert.Len(t, doc4.Translations, 0, "found translations for doc4")
+ assert.Len(t, doc4.Translations(), 0, "found translations for doc4")
doc5 := s.AllPages[5]
permalink, err = doc5.Permalink()
--- a/hugolib/translations.go
+++ b/hugolib/translations.go
@@ -13,12 +13,16 @@
package hugolib
+import (
+ "fmt"
+)
+
// Translations represent the other translations for a given page. The
// string here is the language code, as affected by the `post.LANG.md`
// filename.
type Translations map[string]*Page
-func pagesToTranslationsMap(pages []*Page) map[string]Translations {+func pagesToTranslationsMap(ml *Multilingual, pages []*Page) map[string]Translations {out := make(map[string]Translations)
for _, page := range pages {@@ -34,6 +38,14 @@
continue
}
+ language := ml.Language(pageLang)
+
+ if language == nil {+ panic(fmt.Sprintf("Page language not found in multilang setup: %s", pageLang))+ }
+
+ page.language = language
+
pageTranslation[pageLang] = page
out[base] = pageTranslation
}
@@ -49,11 +61,14 @@
continue
}
- for lang, translatedPage := range trans {+ // TODO(bep) multilingo remove lang
+ for _, translatedPage := range trans { if translatedPage == page {continue
}
- page.Translations[lang] = translatedPage
+ page.translations = append(page.translations, translatedPage)
}
+
+ pageBy(languagePageSort).Sort(page.translations)
}
}
--
⑨