mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-29 17:02:10 -05:00
parent
36f2a1f676
commit
90de511017
9 changed files with 150 additions and 113 deletions
|
@ -493,12 +493,12 @@ func InitializeConfig(subCmdVs ...*cobra.Command) error {
|
||||||
helpers.HugoReleaseVersion(), minVersion)
|
helpers.HugoReleaseVersion(), minVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
h, err := readMultilingualConfiguration()
|
h, err := hugolib.NewHugoSitesFromConfiguration()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
//TODO(bep) refactor ...
|
//TODO(bep) ml refactor ...
|
||||||
Hugo = h
|
Hugo = h
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
|
||||||
"github.com/spf13/hugo/hugolib"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readMultilingualConfiguration() (*hugolib.HugoSites, error) {
|
|
||||||
sites := make([]*hugolib.Site, 0)
|
|
||||||
multilingual := viper.GetStringMap("Languages")
|
|
||||||
if len(multilingual) == 0 {
|
|
||||||
// TODO(bep) multilingo langConfigsList = append(langConfigsList, hugolib.NewLanguage("en"))
|
|
||||||
sites = append(sites, hugolib.NewSite(hugolib.NewLanguage("en")))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(multilingual) > 0 {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
languages, err := toSortedLanguages(multilingual)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to parse multilingual config: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, lang := range languages {
|
|
||||||
sites = append(sites, hugolib.NewSite(lang))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return hugolib.NewHugoSites(sites...)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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{})
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Language config is not a map: %v", langsMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
language := hugolib.NewLanguage(lang)
|
|
||||||
|
|
||||||
for k, v := range langsMap {
|
|
||||||
loki := strings.ToLower(k)
|
|
||||||
switch loki {
|
|
||||||
case "title":
|
|
||||||
language.Title = cast.ToString(v)
|
|
||||||
case "weight":
|
|
||||||
language.Weight = cast.ToInt(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put all into the Params map
|
|
||||||
// TODO(bep) reconsile with the type handling etc. from other params handlers.
|
|
||||||
language.SetParam(loki, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
langs[i] = language
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(langs)
|
|
||||||
|
|
||||||
return langs, nil
|
|
||||||
}
|
|
|
@ -38,6 +38,25 @@ and taxonomy pages will be rendered below `/en` in English, and below `/fr` in F
|
||||||
|
|
||||||
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
|
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
|
||||||
|
|
||||||
|
Taxonomies configuration can also be set per language, example:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
[Languages.en]
|
||||||
|
weight = 1
|
||||||
|
title = "English"
|
||||||
|
|
||||||
|
[Languages.fr]
|
||||||
|
weight = 2
|
||||||
|
title = "Français"
|
||||||
|
[Languages.fr.Taxonomies]
|
||||||
|
plaque = "plaques"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Translating your content
|
### Translating your content
|
||||||
|
|
||||||
Translated articles are identified by the name of the content file.
|
Translated articles are identified by the name of the content file.
|
||||||
|
|
|
@ -28,9 +28,9 @@ func TestLoadGlobalConfig(t *testing.T) {
|
||||||
PaginatePath = "side"
|
PaginatePath = "side"
|
||||||
`
|
`
|
||||||
|
|
||||||
writeSource(t, "config.toml", configContent)
|
writeSource(t, "hugo.toml", configContent)
|
||||||
|
|
||||||
require.NoError(t, LoadGlobalConfig("", "config.toml"))
|
require.NoError(t, LoadGlobalConfig("", "hugo.toml"))
|
||||||
assert.Equal(t, "side", viper.GetString("PaginatePath"))
|
assert.Equal(t, "side", viper.GetString("PaginatePath"))
|
||||||
// default
|
// default
|
||||||
assert.Equal(t, "layouts", viper.GetString("LayoutDir"))
|
assert.Equal(t, "layouts", viper.GetString("LayoutDir"))
|
||||||
|
|
|
@ -15,6 +15,7 @@ package hugolib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -53,6 +54,34 @@ func NewHugoSites(sites ...*Site) (*HugoSites, error) {
|
||||||
return &HugoSites{Multilingual: langConfig, Sites: sites}, nil
|
return &HugoSites{Multilingual: langConfig, Sites: sites}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
|
||||||
|
func NewHugoSitesFromConfiguration() (*HugoSites, error) {
|
||||||
|
sites := make([]*Site, 0)
|
||||||
|
multilingual := viper.GetStringMap("Languages")
|
||||||
|
if len(multilingual) == 0 {
|
||||||
|
// TODO(bep) multilingo langConfigsList = append(langConfigsList, NewLanguage("en"))
|
||||||
|
sites = append(sites, NewSite(NewLanguage("en")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(multilingual) > 0 {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
languages, err := toSortedLanguages(multilingual)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse multilingual config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lang := range languages {
|
||||||
|
sites = append(sites, NewSite(lang))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHugoSites(sites...)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Reset resets the sites, making it ready for a full rebuild.
|
// Reset resets the sites, making it ready for a full rebuild.
|
||||||
// TODO(bep) multilingo
|
// TODO(bep) multilingo
|
||||||
func (h HugoSites) Reset() {
|
func (h HugoSites) Reset() {
|
||||||
|
@ -61,7 +90,7 @@ func (h HugoSites) Reset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HugoSites) siteInfos() []*SiteInfo {
|
func (h HugoSites) toSiteInfos() []*SiteInfo {
|
||||||
infos := make([]*SiteInfo, len(h.Sites))
|
infos := make([]*SiteInfo, len(h.Sites))
|
||||||
for i, s := range h.Sites {
|
for i, s := range h.Sites {
|
||||||
infos[i] = &s.Info
|
infos[i] = &s.Info
|
||||||
|
@ -220,7 +249,7 @@ func (h *HugoSites) render() error {
|
||||||
smLayouts := []string{"sitemapindex.xml", "_default/sitemapindex.xml", "_internal/_default/sitemapindex.xml"}
|
smLayouts := []string{"sitemapindex.xml", "_default/sitemapindex.xml", "_internal/_default/sitemapindex.xml"}
|
||||||
|
|
||||||
if err := s.renderAndWriteXML("sitemapindex", sitemapDefault.Filename,
|
if err := s.renderAndWriteXML("sitemapindex", sitemapDefault.Filename,
|
||||||
h.siteInfos(), s.appendThemeTemplates(smLayouts)...); err != nil {
|
h.toSiteInfos(), s.appendThemeTemplates(smLayouts)...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,7 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
testCommonResetState()
|
testCommonResetState()
|
||||||
jww.SetStdoutThreshold(jww.LevelError)
|
jww.SetStdoutThreshold(jww.LevelCritical)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCommonResetState() {
|
func testCommonResetState() {
|
||||||
|
@ -38,8 +37,8 @@ func testCommonResetState() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiSites(t *testing.T) {
|
func TestMultiSitesBuild(t *testing.T) {
|
||||||
|
testCommonResetState()
|
||||||
sites := createMultiTestSites(t)
|
sites := createMultiTestSites(t)
|
||||||
|
|
||||||
err := sites.Build(BuildCfg{})
|
err := sites.Build(BuildCfg{})
|
||||||
|
@ -128,10 +127,20 @@ func TestMultiSites(t *testing.T) {
|
||||||
sitemapFr := readDestination(t, "public/fr/sitemap.xml")
|
sitemapFr := readDestination(t, "public/fr/sitemap.xml")
|
||||||
require.True(t, strings.Contains(sitemapEn, "http://example.com/blog/en/sect/doc2/"), sitemapEn)
|
require.True(t, strings.Contains(sitemapEn, "http://example.com/blog/en/sect/doc2/"), sitemapEn)
|
||||||
require.True(t, strings.Contains(sitemapFr, "http://example.com/blog/fr/sect/doc1/"), sitemapFr)
|
require.True(t, strings.Contains(sitemapFr, "http://example.com/blog/fr/sect/doc1/"), sitemapFr)
|
||||||
|
|
||||||
|
// Check taxonomies
|
||||||
|
enTags := enSite.Taxonomies["tags"]
|
||||||
|
frTags := frSite.Taxonomies["plaques"]
|
||||||
|
require.Len(t, enTags, 2, fmt.Sprintf("Tags in en: %=v", enTags))
|
||||||
|
require.Len(t, frTags, 2, fmt.Sprintf("Tags in fr: %=v", frTags))
|
||||||
|
require.NotNil(t, enTags["tag1"])
|
||||||
|
require.NotNil(t, frTags["frtag1"])
|
||||||
|
readDestination(t, "public/fr/plaques/frtag1/index.html")
|
||||||
|
readDestination(t, "public/en/tags/tag1/index.html")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiSitesRebuild(t *testing.T) {
|
func TestMultiSitesRebuild(t *testing.T) {
|
||||||
|
testCommonResetState()
|
||||||
sites := createMultiTestSites(t)
|
sites := createMultiTestSites(t)
|
||||||
cfg := BuildCfg{}
|
cfg := BuildCfg{}
|
||||||
|
|
||||||
|
@ -294,16 +303,6 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMultiTestSites(t *testing.T) *HugoSites {
|
func createMultiTestSites(t *testing.T) *HugoSites {
|
||||||
// General settings
|
|
||||||
hugofs.InitMemFs()
|
|
||||||
|
|
||||||
viper.Set("DefaultExtension", "html")
|
|
||||||
viper.Set("baseurl", "http://example.com/blog")
|
|
||||||
viper.Set("DisableSitemap", false)
|
|
||||||
viper.Set("DisableRSS", false)
|
|
||||||
viper.Set("RSSUri", "index.xml")
|
|
||||||
viper.Set("Taxonomies", map[string]string{"tag": "tags"})
|
|
||||||
viper.Set("Permalinks", map[string]string{"other": "/somewhere/else/:filename"})
|
|
||||||
|
|
||||||
// Add some layouts
|
// Add some layouts
|
||||||
if err := afero.WriteFile(hugofs.Source(),
|
if err := afero.WriteFile(hugofs.Source(),
|
||||||
|
@ -362,9 +361,9 @@ NOTE: slug should be used as URL
|
||||||
`)},
|
`)},
|
||||||
{filepath.FromSlash("sect/doc1.fr.md"), []byte(`---
|
{filepath.FromSlash("sect/doc1.fr.md"), []byte(`---
|
||||||
title: doc1
|
title: doc1
|
||||||
tags:
|
plaques:
|
||||||
- tag1
|
- frtag1
|
||||||
- tag2
|
- frtag2
|
||||||
publishdate: "2000-01-04"
|
publishdate: "2000-01-04"
|
||||||
---
|
---
|
||||||
# doc1
|
# doc1
|
||||||
|
@ -393,8 +392,8 @@ NOTE: third 'en' doc, should trigger pagination on home page.
|
||||||
`)},
|
`)},
|
||||||
{filepath.FromSlash("sect/doc4.md"), []byte(`---
|
{filepath.FromSlash("sect/doc4.md"), []byte(`---
|
||||||
title: doc4
|
title: doc4
|
||||||
tags:
|
plaques:
|
||||||
- tag1
|
- frtag1
|
||||||
publishdate: "2000-01-05"
|
publishdate: "2000-01-05"
|
||||||
---
|
---
|
||||||
# doc4
|
# doc4
|
||||||
|
@ -446,13 +445,39 @@ draft: true
|
||||||
`)},
|
`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multilingual settings
|
tomlConfig := `
|
||||||
viper.Set("Multilingual", true)
|
DefaultExtension = "html"
|
||||||
en := NewLanguage("en")
|
baseurl = "http://example.com/blog"
|
||||||
viper.Set("DefaultContentLanguage", "fr")
|
DisableSitemap = false
|
||||||
viper.Set("paginate", "2")
|
DisableRSS = false
|
||||||
|
RSSUri = "index.xml"
|
||||||
|
|
||||||
languages := NewLanguages(en, NewLanguage("fr"))
|
paginate = 2
|
||||||
|
DefaultContentLanguage = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
[permalinks]
|
||||||
|
other = "/somewhere/else/:filename"
|
||||||
|
|
||||||
|
[Taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
[Languages.en]
|
||||||
|
weight = 1
|
||||||
|
title = "English"
|
||||||
|
|
||||||
|
[Languages.fr]
|
||||||
|
weight = 2
|
||||||
|
title = "Français"
|
||||||
|
[Languages.fr.Taxonomies]
|
||||||
|
plaque = "plaques"
|
||||||
|
`
|
||||||
|
|
||||||
|
writeSource(t, "multilangconfig.toml", tomlConfig)
|
||||||
|
if err := LoadGlobalConfig("", "multilangconfig.toml"); err != nil {
|
||||||
|
t.Fatalf("Failed to load config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Hugo support using ByteSource's directly (for testing),
|
// Hugo support using ByteSource's directly (for testing),
|
||||||
// but to make it more real, we write them to the mem file system.
|
// but to make it more real, we write them to the mem file system.
|
||||||
|
@ -466,7 +491,8 @@ draft: true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to locate file")
|
t.Fatalf("Unable to locate file")
|
||||||
}
|
}
|
||||||
sites, err := newHugoSitesFromLanguages(languages)
|
|
||||||
|
sites, err := NewHugoSitesFromConfiguration()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create sites: %s", err)
|
t.Fatalf("Failed to create sites: %s", err)
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -112,3 +114,39 @@ func (s *Site) currentLanguageString() string {
|
||||||
func (s *Site) currentLanguage() *Language {
|
func (s *Site) currentLanguage() *Language {
|
||||||
return s.Language
|
return s.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toSortedLanguages(l map[string]interface{}) (Languages, error) {
|
||||||
|
langs := make(Languages, len(l))
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for lang, langConf := range l {
|
||||||
|
langsMap, ok := langConf.(map[string]interface{})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Language config is not a map: %v", langsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
language := NewLanguage(lang)
|
||||||
|
|
||||||
|
for k, v := range langsMap {
|
||||||
|
loki := strings.ToLower(k)
|
||||||
|
switch loki {
|
||||||
|
case "title":
|
||||||
|
language.Title = cast.ToString(v)
|
||||||
|
case "weight":
|
||||||
|
language.Weight = cast.ToInt(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put all into the Params map
|
||||||
|
// TODO(bep) reconsile with the type handling etc. from other params handlers.
|
||||||
|
language.SetParam(loki, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
langs[i] = language
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(langs)
|
||||||
|
|
||||||
|
return langs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1325,7 +1325,7 @@ func (s *Site) assembleMenus() {
|
||||||
func (s *Site) assembleTaxonomies() {
|
func (s *Site) assembleTaxonomies() {
|
||||||
s.Taxonomies = make(TaxonomyList)
|
s.Taxonomies = make(TaxonomyList)
|
||||||
|
|
||||||
taxonomies := viper.GetStringMapString("Taxonomies")
|
taxonomies := s.Language.GetStringMapString("Taxonomies")
|
||||||
jww.INFO.Printf("found taxonomies: %#v\n", taxonomies)
|
jww.INFO.Printf("found taxonomies: %#v\n", taxonomies)
|
||||||
|
|
||||||
for _, plural := range taxonomies {
|
for _, plural := range taxonomies {
|
||||||
|
@ -1563,7 +1563,7 @@ func (s *Site) renderTaxonomiesLists() error {
|
||||||
|
|
||||||
go errorCollator(results, errs)
|
go errorCollator(results, errs)
|
||||||
|
|
||||||
taxonomies := viper.GetStringMapString("Taxonomies")
|
taxonomies := s.Language.GetStringMapString("Taxonomies")
|
||||||
for singular, plural := range taxonomies {
|
for singular, plural := range taxonomies {
|
||||||
for key, pages := range s.Taxonomies[plural] {
|
for key, pages := range s.Taxonomies[plural] {
|
||||||
taxes <- taxRenderInfo{key, pages, singular, plural}
|
taxes <- taxRenderInfo{key, pages, singular, plural}
|
||||||
|
@ -1693,7 +1693,7 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
|
||||||
|
|
||||||
// renderListsOfTaxonomyTerms renders a page per taxonomy that lists the terms for that taxonomy
|
// renderListsOfTaxonomyTerms renders a page per taxonomy that lists the terms for that taxonomy
|
||||||
func (s *Site) renderListsOfTaxonomyTerms() (err error) {
|
func (s *Site) renderListsOfTaxonomyTerms() (err error) {
|
||||||
taxonomies := viper.GetStringMapString("Taxonomies")
|
taxonomies := s.Language.GetStringMapString("Taxonomies")
|
||||||
for singular, plural := range taxonomies {
|
for singular, plural := range taxonomies {
|
||||||
n := s.newNode()
|
n := s.newNode()
|
||||||
n.Title = strings.Title(plural)
|
n.Title = strings.Title(plural)
|
||||||
|
@ -1969,7 +1969,7 @@ func (s *Site) Stats() {
|
||||||
jww.FEEDBACK.Printf("%d pages created\n", len(s.Pages))
|
jww.FEEDBACK.Printf("%d pages created\n", len(s.Pages))
|
||||||
jww.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
|
jww.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
|
||||||
jww.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
|
jww.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
|
||||||
taxonomies := viper.GetStringMapString("Taxonomies")
|
taxonomies := s.Language.GetStringMapString("Taxonomies")
|
||||||
|
|
||||||
for _, pl := range taxonomies {
|
for _, pl := range taxonomies {
|
||||||
jww.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
|
jww.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
|
||||||
|
|
|
@ -30,7 +30,7 @@ func TestByCountOrderOfTaxonomies(t *testing.T) {
|
||||||
|
|
||||||
viper.Set("taxonomies", taxonomies)
|
viper.Set("taxonomies", taxonomies)
|
||||||
|
|
||||||
site := new(Site)
|
site := newSiteDefaultLang()
|
||||||
page, _ := NewPageFrom(strings.NewReader(pageYamlWithTaxonomiesA), "path/to/page")
|
page, _ := NewPageFrom(strings.NewReader(pageYamlWithTaxonomiesA), "path/to/page")
|
||||||
site.Pages = append(site.Pages, page)
|
site.Pages = append(site.Pages, page)
|
||||||
site.assembleTaxonomies()
|
site.assembleTaxonomies()
|
||||||
|
|
Loading…
Reference in a new issue