shithub: hugo

Download patch

ref: 6413559f7575e2653d76227a8037a7edbaae82aa
parent: 322c567220aa4123a5d707629c1bebd375599912
author: Bjørn Erik Pedersen <[email protected]>
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 {