This commit also consolidates URLs on Node vs Page, so now .Permalink should be interoperable.
Note that this implementations should be fairly short-livded, waiting for #2297, but the API should be stable.
s := setupMenuTests(t, menuPageSources)
home := s.newHomeNode()
- homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL}
+ homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL()}
for i, this := range []struct {
menu string
import (
"html/template"
+ "path"
+ "path/filepath"
+ "sort"
"sync"
"time"
paginator *Pager
paginatorInit sync.Once
scratch *Scratch
+
+ language *Language
+ lang string // TODO(bep) multilingo
+
+ translations Nodes
+ translationsInit sync.Once
+}
+
+// The Nodes type is temporary until we get https://github.com/spf13/hugo/issues/2297 fixed.
+type Nodes []*Node
+
+func (n Nodes) Len() int {
+ return len(n)
+}
+
+func (n Nodes) Less(i, j int) bool {
+ return n[i].language.Weight < n[j].language.Weight
+}
+
+func (n Nodes) Swap(i, j int) {
+ n[i], n[j] = n[j], n[i]
}
func (n *Node) Now() time.Time {
func (n *Node) HasMenuCurrent(menuID string, inme *MenuEntry) bool {
if inme.HasChildren() {
- me := MenuEntry{Name: n.Title, URL: n.URL}
+ me := MenuEntry{Name: n.Title, URL: n.URL()}
for _, child := range inme.Children {
if me.IsSameResource(child) {
func (n *Node) IsMenuCurrent(menuID string, inme *MenuEntry) bool {
- me := MenuEntry{Name: n.Title, URL: n.Site.createNodeMenuEntryURL(n.URL)}
+ me := MenuEntry{Name: n.Title, URL: n.Site.createNodeMenuEntryURL(n.URL())}
if !me.IsSameResource(inme) {
return false
return n.Site.RelRef(ref, nil)
}
+// TODO(bep) multilingo some of these are now hidden. Consider unexport.
type URLPath struct {
URL string
Permalink string
Section string
}
+func (n *Node) URL() string {
+ return n.addMultilingualWebPrefix(n.URLPath.URL)
+}
+
+func (n *Node) Permalink() string {
+ return permalink(n.URL())
+}
+
// Scratch returns the writable context associated with this Node.
func (n *Node) Scratch() *Scratch {
if n.scratch == nil {
}
return n.scratch
}
+
+// TODO(bep) multilingo consolidate. See Page.
+func (n *Node) Language() *Language {
+ return n.language
+}
+
+func (n *Node) Lang() string {
+ if n.Language() != nil {
+ return n.Language().Lang
+ }
+ return n.lang
+}
+
+// AllTranslations returns all translations, including the current Node.
+// Note that this and the one below is kind of a temporary hack before #2297 is solved.
+func (n *Node) AllTranslations() Nodes {
+ n.initTranslations()
+ return n.translations
+}
+
+// Translations returns the translations excluding the current Node.
+func (n *Node) Translations() Nodes {
+ n.initTranslations()
+ translations := make(Nodes, 0)
+
+ for _, t := range n.translations {
+
+ if t != n {
+ translations = append(translations, t)
+ }
+ }
+
+ return translations
+}
+
+func (n *Node) initTranslations() {
+ n.translationsInit.Do(func() {
+ if n.translations != nil {
+ return
+ }
+ n.translations = make(Nodes, 0)
+ for _, l := range n.Site.Languages {
+ if l == n.language {
+ n.translations = append(n.translations, n)
+ continue
+ }
+
+ translation := *n
+ translation.language = l
+ translation.translations = n.translations
+ n.translations = append(n.translations, &translation)
+ }
+
+ sort.Sort(n.translations)
+ })
+}
+
+func (n *Node) addMultilingualWebPrefix(outfile string) string {
+ lang := n.Lang()
+ if lang == "" || !n.Site.Multilingual {
+ return outfile
+ }
+ return "/" + path.Join(lang, outfile)
+}
+
+func (n *Node) addMultilingualFilesystemPrefix(outfile string) string {
+ lang := n.Lang()
+ if lang == "" || !n.Site.Multilingual {
+ return outfile
+ }
+ return string(filepath.Separator) + filepath.Join(lang, outfile)
+}
translations Pages
extension string
contentType string
- lang string
- language *Language
renderable bool
Layout string
layoutsCalculated []string
baseURL := string(p.Site.BaseURL)
dir := strings.TrimSpace(helpers.MakePath(filepath.ToSlash(strings.ToLower(p.Source.Dir()))))
pSlug := strings.TrimSpace(helpers.URLize(p.Slug))
- pURL := strings.TrimSpace(helpers.URLize(p.URL))
+ pURL := strings.TrimSpace(helpers.URLize(p.URLPath.URL))
var permalink string
var err error
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
if url := cast.ToString(v); strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
return fmt.Errorf("Only relative URLs are supported, %v provided", url)
}
- p.URL = cast.ToString(v)
+ p.URLPath.URL = cast.ToString(v)
case "type":
p.contentType = cast.ToString(v)
case "extension", "ext":
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)
+ if len(strings.TrimSpace(p.URLPath.URL)) > 2 {
+ outfile = strings.TrimSpace(p.URLPath.URL)
if strings.HasSuffix(outfile, "/") {
outfile = outfile + "index.html"
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)
-}
import (
"errors"
"fmt"
- "github.com/spf13/cast"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
"html/template"
"math"
"path"
"reflect"
"strings"
+
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/viper"
)
// Pager represents one of the elements in a paginator.
return
}
- pagers, err := paginatePages(n.Data["Pages"], pagerSize, n.URL)
+ pagers, err := paginatePages(n.Data["Pages"], pagerSize, n.URL())
if err != nil {
initError = err
if n.paginator != nil {
return
}
- pagers, err := paginatePages(seq, pagerSize, n.URL)
+ pagers, err := paginatePages(seq, pagerSize, n.URL())
if err != nil {
initError = err
}
type SiteInfo struct {
- BaseURL template.URL
- Taxonomies TaxonomyList
- Authors AuthorList
- Social SiteSocial
- Sections Taxonomy
- 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
- Title string
- RSSLink string
- Author map[string]interface{}
+ BaseURL template.URL
+ Taxonomies TaxonomyList
+ Authors AuthorList
+ Social SiteSocial
+ Sections Taxonomy
+ 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
+ Title string
+ RSSLink string
+ Author map[string]interface{}
+ // TODO(bep) multilingo
LanguageCode string
DisqusShortname string
GoogleAnalytics string
LanguagePrefix: languagePrefix,
Languages: languages,
GoogleAnalytics: viper.GetString("GoogleAnalytics"),
- RSSLink: s.permalinkStr(viper.GetString("RSSUri")),
+ RSSLink: permalinkStr(viper.GetString("RSSUri")),
BuildDrafts: viper.GetBool("BuildDrafts"),
canonifyURLs: viper.GetBool("CanonifyURLs"),
preserveTaxonomyNames: viper.GetBool("PreserveTaxonomyNames"),
paginatePath := viper.GetString("paginatePath")
// write alias for page 1
- s.writeDestAlias(helpers.PaginateAliasPath(base, 1), s.permalink(base))
+ s.writeDestAlias(helpers.PaginateAliasPath(base, 1), permalink(base))
pagers := n.paginator.Pagers()
if !viper.GetBool("DisableRSS") {
// XML Feed
rssuri := viper.GetString("RSSUri")
- n.URL = s.permalinkStr(base + "/" + rssuri)
- n.Permalink = s.permalink(base)
+ n.URLPath.URL = permalinkStr(base + "/" + rssuri)
+ n.URLPath.Permalink = permalink(base)
rssLayouts := []string{"taxonomy/" + t.singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
if err := s.renderAndWriteXML("taxonomy "+t.singular+" rss", base+"/"+rssuri, n, s.appendThemeTemplates(rssLayouts)...); err != nil {
paginatePath := viper.GetString("paginatePath")
// write alias for page 1
- s.writeDestAlias(helpers.PaginateAliasPath(base, 1), s.permalink(base))
+ s.writeDestAlias(helpers.PaginateAliasPath(base, 1), permalink(base))
pagers := n.paginator.Pagers()
if !viper.GetBool("DisableRSS") && section != "" {
// XML Feed
rssuri := viper.GetString("RSSUri")
- n.URL = s.permalinkStr(base + "/" + rssuri)
- n.Permalink = s.permalink(base)
+ n.URLPath.URL = permalinkStr(base + "/" + rssuri)
+ n.URLPath.Permalink = permalink(base)
rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
if err := s.renderAndWriteXML("section "+section+" rss", base+"/"+rssuri, n, s.appendThemeTemplates(rssLayouts)...); err != nil {
return err
paginatePath := viper.GetString("paginatePath")
// write alias for page 1
- s.writeDestAlias(s.addMultilingualPrefix(helpers.PaginateAliasPath("", 1)), s.permalink("/"))
+ s.writeDestAlias(s.addMultilingualPrefix(helpers.PaginateAliasPath("", 1)), permalink("/"))
pagers := n.paginator.Pagers()
if !viper.GetBool("DisableRSS") {
// XML Feed
- n.URL = s.permalinkStr(viper.GetString("RSSUri"))
+ n.URLPath.URL = permalinkStr(viper.GetString("RSSUri"))
n.Title = ""
high := 50
if len(s.Pages) < high {
}
// TODO(bep) reusing the Home Node smells trouble
- n.URL = helpers.URLize("404.html")
+ n.URLPath.URL = helpers.URLize("404.html")
n.IsHome = false
n.Title = "404 Page not found"
- n.Permalink = s.permalink("404.html")
+ n.URLPath.Permalink = permalink("404.html")
n.scratch = newScratch()
nfLayouts := []string{"404.html"}
page.Date = s.Info.LastChange
page.Lastmod = s.Info.LastChange
page.Site = &s.Info
- page.URL = "/"
+ page.URLPath.URL = "/"
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
page.Sitemap.Priority = sitemapDefault.Priority
}
func (s *Site) setURLs(n *Node, in string) {
- in = s.addMultilingualPrefix(in)
- n.URL = helpers.URLizeAndPrep(in)
- n.Permalink = s.permalink(n.URL)
- n.RSSLink = template.HTML(s.permalink(in + ".xml"))
+ n.URLPath.URL = helpers.URLizeAndPrep(in)
+ n.URLPath.Permalink = permalink(n.URLPath.URL)
+ // TODO(bep) multilingo
+ n.RSSLink = template.HTML(permalink(in + ".xml"))
}
-func (s *Site) permalink(plink string) string {
- return s.permalinkStr(plink)
+func permalink(plink string) string {
+ return permalinkStr(plink)
}
-func (s *Site) permalinkStr(plink string) string {
+func permalinkStr(plink string) string {
return helpers.MakePermalink(viper.GetString("BaseURL"), helpers.URLizeAndPrep(plink)).String()
}
func (s *Site) newNode() *Node {
return &Node{
- Data: make(map[string]interface{}),
- Site: &s.Info,
+ Data: make(map[string]interface{}),
+ Site: &s.Info,
+ language: s.Lang,
}
}
var pageTarget target.Output
- if p, ok := d.(*Page); ok && path.Ext(p.URL) != "" {
+ if p, ok := d.(*Page); ok && path.Ext(p.URLPath.URL) != "" {
// user has explicitly set a URL with extension for this page
// make sure it sticks even if "ugly URLs" are turned off.
pageTarget = s.pageUglyTarget()
permalink, err = doc3.Permalink()
assert.NoError(t, err, "permalink call failed")
assert.Equal(t, "http://example.com/blog/superbob", permalink, "invalid doc3 permalink")
- assert.Equal(t, "/superbob", doc3.URL, "invalid url, was specified on doc3")
+
+ // TODO(bep) multilingo. Check this case. This has url set in frontmatter, but we must split into lang folders
+ // The assertion below was missing the /en prefix.
+ assert.Equal(t, "/en/superbob", doc3.URL(), "invalid url, was specified on doc3 TODO(bep)")
assert.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
translater.current = f
return nil
}
- return fmt.Errorf("Translation func for language %v not found", lang)
+ jww.WARN.Printf("Translation func for language %v not found", lang)
+ return nil
}
func SetI18nTfuncs(bndl *bundle.Bundle) {
}
func I18nTranslate(id string, args ...interface{}) (string, error) {
- if translater == nil {
+ if translater == nil || translater.current == nil {
return "", fmt.Errorf("i18n not initialized, have you configured everything properly?")
}
return translater.current(id, args...), nil