ref: 6413559f7575e2653d76227a8037a7edbaae82aa
parent: 322c567220aa4123a5d707629c1bebd375599912
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Thu Jan 25 12:03:29 EST 2018
Add a way to disable one or more languages This commit adds a new config setting: ```toml disableLanguages = ["fr"] ``` If this is a multilingual site: * No site for the French language will be created * French content pages will be ignored/not read * The French language configuration (menus etc.) will also be ignored This makes it possible to start translating new languages and turn it on when you're happy etc. Fixes #4297 Fixed #4329
--- a/commands/server.go
+++ b/commands/server.go
@@ -173,20 +173,23 @@
c.Set("liveReloadPort", serverPorts[0])}
- if c.languages.IsMultihost() {- for i, language := range c.languages {- baseURL, err := fixURL(language, baseURL, serverPorts[i])
- if err != nil {- return err
- }
- language.Set("baseURL", baseURL)+ isMultiHost := c.languages.IsMultihost()
+ for i, language := range c.languages {+ var serverPort int
+ if isMultiHost {+ serverPort = serverPorts[i]
+ } else {+ serverPort = serverPorts[0]
}
- } else {- baseURL, err := fixURL(c.Cfg, baseURL, serverPorts[0])
+
+ baseURL, err := fixURL(language, baseURL, serverPort)
if err != nil {return err
}
- c.Set("baseURL", baseURL)+ language.Set("baseURL", baseURL)+ if i == 0 {+ c.Set("baseURL", baseURL)+ }
}
return nil
--- a/config/configProvider.go
+++ b/config/configProvider.go
@@ -20,6 +20,7 @@
GetBool(key string) bool
GetStringMap(key string) map[string]interface{}GetStringMapString(key string) map[string]string
+ GetStringSlice(key string) []string
Get(key string) interface{} Set(key string, value interface{})IsSet(key string) bool
--- a/helpers/language.go
+++ b/helpers/language.go
@@ -140,6 +140,11 @@
return cast.ToStringMapString(l.Get(key))
}
+// returns the value associated with the key as a slice of strings.
+func (l *Language) GetStringSlice(key string) []string {+ return cast.ToStringSlice(l.Get(key))
+}
+
// Get returns a value associated with the key relying on specified language.
// Get is case-insensitive for a key.
//
--- a/hugolib/config.go
+++ b/hugolib/config.go
@@ -72,16 +72,46 @@
}
func loadLanguageSettings(cfg config.Provider, oldLangs helpers.Languages) error {- multilingual := cfg.GetStringMap("languages")+
+ defaultLang := cfg.GetString("defaultContentLanguage")+
+ var languages map[string]interface{}+
+ languagesFromConfig := cfg.GetStringMap("languages")+ disableLanguages := cfg.GetStringSlice("disableLanguages")+
+ if len(disableLanguages) == 0 {+ languages = languagesFromConfig
+ } else {+ languages = make(map[string]interface{})+ for k, v := range languagesFromConfig {+ isDisabled := false
+ for _, disabled := range disableLanguages {+ if disabled == defaultLang {+ return fmt.Errorf("cannot disable default language %q", defaultLang)+ }
+
+ if strings.EqualFold(k, disabled) {+ isDisabled = true
+ break
+ }
+ }
+ if !isDisabled {+ languages[k] = v
+ }
+
+ }
+ }
+
var (
langs helpers.Languages
err error
)
- if len(multilingual) == 0 {+ if len(languages) == 0 {langs = append(langs, helpers.NewDefaultLanguage(cfg))
} else {- langs, err = toSortedLanguages(cfg, multilingual)
+ langs, err = toSortedLanguages(cfg, languages)
if err != nil { return fmt.Errorf("Failed to parse multilingual config: %s", err)}
@@ -113,8 +143,6 @@
}
}
}
-
- defaultLang := cfg.GetString("defaultContentLanguage")// The defaultContentLanguage is something the user has to decide, but it needs
// to match a language in the language definition list.
--- a/hugolib/fileInfo.go
+++ b/hugolib/fileInfo.go
@@ -31,6 +31,9 @@
bundleTp bundleDirType
source.ReadableFile
overriddenLang string
+
+ // Set if the content language for this file is disabled.
+ disabled bool
}
func (fi *fileInfo) Lang() string {@@ -59,6 +62,9 @@
bundleTp: tp,
ReadableFile: baseFi,
}
+
+ lang := f.Lang()
+ f.disabled = lang != "" && sp.DisabledLanguages[lang]
return f
--- a/hugolib/page_bundler_capture.go
+++ b/hugolib/page_bundler_capture.go
@@ -149,8 +149,10 @@
// create the proper mapping for it.
c.getRealFileInfo(dir)
- f := c.newFileInfo(resolvedFilename, fi, tp)
- c.copyOrHandleSingle(f)
+ f, active := c.newFileInfo(resolvedFilename, fi, tp)
+ if active {+ c.copyOrHandleSingle(f)
+ }
}
}
@@ -228,7 +230,10 @@
tp, isContent := classifyBundledFile(fi.Name())
- f := c.newFileInfo(fi.filename, fi.FileInfo, tp)
+ f, active := c.newFileInfo(fi.filename, fi.FileInfo, tp)
+ if !active {+ continue
+ }
if f.isOwner() {dirs.addBundleHeader(f)
} else if !isContent {@@ -309,7 +314,7 @@
return c.handleNonBundle(dirname, files, state == dirStateSinglesOnly)
}
- var fileInfos = make([]*fileInfo, len(files))
+ var fileInfos = make([]*fileInfo, 0, len(files))
for i, fi := range files {currentType := bundleNot
@@ -324,8 +329,12 @@
if bundleType == bundleNot && currentType != bundleNot {bundleType = currentType
}
+ f, active := c.newFileInfo(fi.filename, fi.FileInfo, currentType)
+ if !active {+ continue
+ }
- fileInfos[i] = c.newFileInfo(fi.filename, fi.FileInfo, currentType)
+ fileInfos = append(fileInfos, f)
}
var todo []*fileInfo
@@ -377,8 +386,11 @@
}
} else { if singlesOnly {- file := c.newFileInfo(fi.filename, fi, bundleNot)
- c.handler.handleSingles(file)
+ f, active := c.newFileInfo(fi.filename, fi, bundleNot)
+ if !active {+ continue
+ }
+ c.handler.handleSingles(f)
} else {c.handler.handleCopyFiles(fi.filename)
}
@@ -462,7 +474,10 @@
return err
}
} else {- handleFiles(c.newFileInfo(fi.filename, fi.FileInfo, bundleNot))
+ f, active := c.newFileInfo(fi.filename, fi.FileInfo, bundleNot)
+ if active {+ handleFiles(f)
+ }
}
}
@@ -506,8 +521,9 @@
return fis, nil
}
-func (c *capturer) newFileInfo(filename string, fi os.FileInfo, tp bundleDirType) *fileInfo {- return newFileInfo(c.sourceSpec, c.baseDir, filename, fi, tp)
+func (c *capturer) newFileInfo(filename string, fi os.FileInfo, tp bundleDirType) (*fileInfo, bool) {+ f := newFileInfo(c.sourceSpec, c.baseDir, filename, fi, tp)
+ return f, !f.disabled
}
type fileInfoName struct {--- a/hugolib/page_bundler_capture_test.go
+++ b/hugolib/page_bundler_capture_test.go
@@ -174,6 +174,7 @@
expected := `
F:
/work/base/1s/mypage.md
+/work/base/1s/mypage.nn.md
/work/base/bb/_1.md
/work/base/bb/_1.nn.md
/work/base/bb/en.md
--- a/hugolib/page_bundler_test.go
+++ b/hugolib/page_bundler_test.go
@@ -192,6 +192,10 @@
s := sites.Sites[0]
+ assert.Equal(8, len(s.RegularPages))
+ assert.Equal(18, len(s.Pages))
+ assert.Equal(35, len(s.AllPages))
+
bundleWithSubPath := s.getPage(KindPage, "lb/index")
assert.NotNil(bundleWithSubPath)
@@ -214,6 +218,8 @@
assert.Equal(bfBundle, s.getPage(KindPage, "my-bf-bundle"))
nnSite := sites.Sites[1]
+ assert.Equal(7, len(nnSite.RegularPages))
+
bfBundleNN := nnSite.getPage(KindPage, "bf/my-bf-bundle/index")
assert.NotNil(bfBundleNN)
assert.Equal("nn", bfBundleNN.Lang())@@ -233,6 +239,48 @@
}
}
+func TestMultilingualDisableDefaultLanguage(t *testing.T) {+ t.Parallel()
+
+ assert := require.New(t)
+ cfg, _ := newTestBundleSourcesMultilingual(t)
+
+ cfg.Set("disableLanguages", []string{"en"})+
+ err := loadDefaultSettingsFor(cfg)
+ assert.Error(err)
+ assert.Contains(err.Error(), "cannot disable default language")
+}
+
+func TestMultilingualDisableLanguage(t *testing.T) {+ t.Parallel()
+
+ assert := require.New(t)
+ cfg, fs := newTestBundleSourcesMultilingual(t)
+ cfg.Set("disableLanguages", []string{"nn"})+
+ assert.NoError(loadDefaultSettingsFor(cfg))
+ sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})+ assert.NoError(err)
+ assert.Equal(1, len(sites.Sites))
+
+ assert.NoError(sites.Build(BuildCfg{}))+
+ s := sites.Sites[0]
+
+ assert.Equal(8, len(s.RegularPages))
+ assert.Equal(18, len(s.Pages))
+ // No nn pages
+ assert.Equal(18, len(s.AllPages))
+ for _, p := range s.rawAllPages {+ assert.True(p.Lang() != "nn")
+ }
+ for _, p := range s.AllPages {+ assert.True(p.Lang() != "nn")
+ }
+
+}
+
func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) {assert := require.New(t)
cfg, fs, workDir := newTestBundleSymbolicSources(t)
@@ -509,6 +557,7 @@
writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout)
writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.md"), pageContent)
+ writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.nn.md"), pageContent)
writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mylogo.png"), "content")
writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.md"), pageContent)
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1207,30 +1207,28 @@
}
type contentCaptureResultHandler struct {- contentProcessors map[string]*siteContentProcessor
+ defaultContentProcessor *siteContentProcessor
+ contentProcessors map[string]*siteContentProcessor
}
+func (c *contentCaptureResultHandler) getContentProcessor(lang string) *siteContentProcessor {+ proc, found := c.contentProcessors[lang]
+ if found {+ return proc
+ }
+ return c.defaultContentProcessor
+}
+
func (c *contentCaptureResultHandler) handleSingles(fis ...*fileInfo) { for _, fi := range fis {- // May be connected to a language (content files)
- proc, found := c.contentProcessors[fi.Lang()]
- if !found {- panic("proc not found")- }
+ proc := c.getContentProcessor(fi.Lang())
proc.fileSinglesChan <- fi
-
}
}
func (c *contentCaptureResultHandler) handleBundles(d *bundleDirs) { for _, b := range d.bundles {- lang := b.fi.Lang()
-
- proc, found := c.contentProcessors[lang]
- if !found {- panic("proc not found")- }
+ proc := c.getContentProcessor(b.fi.Lang())
proc.fileBundlesChan <- b
-
}
}
@@ -1247,13 +1245,17 @@
sourceSpec := source.NewSourceSpec(s.owner.Cfg, s.Fs)
baseDir := s.absContentDir()
+ defaultContentLanguage := s.SourceSpec.DefaultContentLanguage
contentProcessors := make(map[string]*siteContentProcessor)
+ var defaultContentProcessor *siteContentProcessor
sites := s.owner.langSite()
for k, v := range sites {proc := newSiteContentProcessor(baseDir, len(filenames) > 0, v)
contentProcessors[k] = proc
-
+ if k == defaultContentLanguage {+ defaultContentProcessor = proc
+ }
g.Go(func() error {return proc.process(ctx)
})
@@ -1264,7 +1266,7 @@
bundleMap *contentChangeMap
)
- mainHandler := &contentCaptureResultHandler{contentProcessors: contentProcessors}+ mainHandler := &contentCaptureResultHandler{contentProcessors: contentProcessors, defaultContentProcessor: defaultContentProcessor} if s.running() {// Need to track changes.
--- a/source/sourceSpec.go
+++ b/source/sourceSpec.go
@@ -35,6 +35,7 @@
Languages map[string]interface{}DefaultContentLanguage string
+ DisabledLanguages map[string]bool
}
// NewSourceSpec initializes SourceSpec using languages from a given configuration.
@@ -42,6 +43,12 @@
defaultLang := cfg.GetString("defaultContentLanguage") languages := cfg.GetStringMap("languages")+ disabledLangsSet := make(map[string]bool)
+
+ for _, disabledLang := range cfg.GetStringSlice("disableLanguages") {+ disabledLangsSet[disabledLang] = true
+ }
+
if len(languages) == 0 {l := helpers.NewDefaultLanguage(cfg)
languages[l.Lang] = l
@@ -62,7 +69,7 @@
}
}
- return &SourceSpec{ignoreFilesRe: regexps, Cfg: cfg, Fs: fs, Languages: languages, DefaultContentLanguage: defaultLang}+ return &SourceSpec{ignoreFilesRe: regexps, Cfg: cfg, Fs: fs, Languages: languages, DefaultContentLanguage: defaultLang, DisabledLanguages: disabledLangsSet}}
func (s *SourceSpec) IgnoreFile(filename string) bool {--
⑨