ref: c4e7c37055a029a26d87ebeb21614efb3f0b0040
parent: 06d12ab895a83fc8a9f94b23e533b25511bbb6d1
author: Bjørn Erik Pedersen <[email protected]>
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)
}
}