Add Translations and AllTranslations methods to Page
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 25 Jul 2016 20:22:09 +0000 (22:22 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 6 Sep 2016 15:32:15 +0000 (18:32 +0300)
Will revisit Node later.

commands/multilingual.go
hugolib/multilingual.go
hugolib/node.go
hugolib/page.go
hugolib/pageSort.go
hugolib/site.go
hugolib/site_test.go
hugolib/translations.go

index 983dc756dc8c8af810fe2c5a676b1ce417edf0e7..3f813474dbe3f77f15dc9ca2852a510d04c03bfe 100644 (file)
@@ -33,6 +33,7 @@ func readMultilingualConfiguration() error {
 
 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 @@ func toSortedLanguages(l map[string]interface{}) (hugolib.Languages, error) {
                        language.SetParam(loki, v)
                }
 
-               langs = append(langs, language)
+               langs[i] = language
+               i++
        }
 
        sort.Sort(langs)
index eebd43e3dcf7eb58d72a40f783fb23dae9fdae46..becdd5ba1a4238518737b1f2e0ed348c6f0ea710 100644 (file)
@@ -3,6 +3,7 @@ package hugolib
 import (
        "sync"
 
+       "sort"
        "strings"
 
        "github.com/spf13/cast"
@@ -23,14 +24,38 @@ func NewLanguage(lang string) *Language {
 
 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{} {
@@ -73,15 +98,14 @@ func (s *Site) SetMultilingualConfig(currentLang *Language, languages Languages)
        // 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 {
index 86ba841c61a2cd37bca42f720fa1d7bf147f6948..c9e60701e03684829622044088f59d3cfa03987b 100644 (file)
 package hugolib
 
 import (
-       "github.com/spf13/cast"
        "html/template"
        "sync"
        "time"
+
+       "github.com/spf13/cast"
 )
 
 type Node struct {
index 5c281bb967590743d54d196dcb0dbe4b73946a07..f28482c57a94debd4d56e8c1e9565784f7264fcc 100644 (file)
@@ -61,10 +61,11 @@ type Page struct {
        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 @@ func newPage(filename string) *Page {
                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,10 +467,30 @@ func (p *Page) Extension() string {
        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 {
        if len(p.linkTitle) > 0 {
                return p.linkTitle
index 248e3ed5dd1a76315017a059d1983f5b75bf3288..ec5e2c36da81092c9b0f2f3ded553f7b8ad5ec04 100644 (file)
@@ -56,6 +56,19 @@ var defaultPageSort = func(p1, p2 *Page) bool {
        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] }
 
@@ -212,6 +225,20 @@ func (p Pages) ByLength() Pages {
        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
+}
+
 // Reverse reverses the order in Pages and returns a copy.
 //
 // Adjacent invocactions on the same receiver will return a cached result.
index 92ce485358b5335e773391712bb6c4922c96c757..ff67eb48ab421a7880dac1db2439ebc43be798a5 100644 (file)
@@ -744,10 +744,10 @@ func (s *Site) setupTranslations() {
 
        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 @@ func (s *Site) renderAndWriteXML(name string, dest string, d interface{}, layout
 
        err := s.render(name, d, renderBuffer, layouts...)
 
+       if err != nil {
+               return err
+       }
+
        outBuffer := bp.GetBuffer()
        defer bp.PutBuffer(outBuffer)
 
@@ -2030,11 +2034,8 @@ func (s *Site) renderAndWriteXML(name string, dest string, d interface{}, layout
        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 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
 
        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 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
        }
 
        if err == nil {
+
                if err = s.writeDestPage(dest, pageTarget, outBuffer); err != nil {
                        return err
                }
@@ -2122,6 +2134,7 @@ func (s *Site) render(name string, d interface{}, w io.Writer, layouts ...string
        }
 
        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) findFirstLayout(layouts ...string) (string, bool) {
 }
 
 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)
index 9f29bf376ac850e6dcf65e7eac227d7ea56acbd6..78004aac4e82191cf4f5e9b633eb85d2b298b2bc 100644 (file)
@@ -1399,12 +1399,6 @@ NOTE: should use the "permalinks" configuration with :filename
 
        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 @@ NOTE: should use the "permalinks" configuration with :filename
        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 @@ NOTE: should use the "permalinks" configuration with :filename
        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 @@ NOTE: should use the "permalinks" configuration with :filename
 
        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()
index b503071d821b4188a33ca0ff26f5ac0904061353..7caa6b4364ac7a21484f0f74f9396f94ae4bd7d1 100644 (file)
 
 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 @@ func pagesToTranslationsMap(pages []*Page) map[string]Translations {
                        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 @@ func assignTranslationsToPages(allTranslations map[string]Translations, pages []
                        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)
        }
 }