From c4e7c37055a029a26d87ebeb21614efb3f0b0040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 25 Jul 2016 22:22:09 +0200 Subject: [PATCH] Add Translations and AllTranslations methods to Page Will revisit Node later. --- commands/multilingual.go | 4 +++- hugolib/multilingual.go | 34 +++++++++++++++++++++++++++++----- hugolib/node.go | 3 ++- hugolib/page.go | 25 +++++++++++++++++++++++-- hugolib/pageSort.go | 27 +++++++++++++++++++++++++++ hugolib/site.go | 26 ++++++++++++++++++++------ hugolib/site_test.go | 25 ++++++++++++++----------- hugolib/translations.go | 21 ++++++++++++++++++--- 8 files changed, 136 insertions(+), 29 deletions(-) diff --git a/commands/multilingual.go b/commands/multilingual.go index 983dc756d..3f813474d 100644 --- a/commands/multilingual.go +++ b/commands/multilingual.go @@ -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) diff --git a/hugolib/multilingual.go b/hugolib/multilingual.go index eebd43e3d..becdd5ba1 100644 --- a/hugolib/multilingual.go +++ b/hugolib/multilingual.go @@ -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 { diff --git a/hugolib/node.go b/hugolib/node.go index 86ba841c6..c9e60701e 100644 --- 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 { diff --git a/hugolib/page.go b/hugolib/page.go index 5c281bb96..f28482c57 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -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 diff --git a/hugolib/pageSort.go b/hugolib/pageSort.go index 248e3ed5d..ec5e2c36d 100644 --- a/hugolib/pageSort.go +++ b/hugolib/pageSort.go @@ -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. diff --git a/hugolib/site.go b/hugolib/site.go index 92ce48535..ff67eb48a 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -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) diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 9f29bf376..78004aac4 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -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() diff --git a/hugolib/translations.go b/hugolib/translations.go index b503071d8..7caa6b436 100644 --- 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 @@ 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) } }