mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
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
This commit is contained in:
parent
322c567220
commit
6413559f75
10 changed files with 160 additions and 42 deletions
|
@ -173,20 +173,23 @@ func server(cmd *cobra.Command, args []string) error {
|
|||
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
|
||||
|
|
|
@ -20,6 +20,7 @@ type Provider interface {
|
|||
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
|
||||
|
|
|
@ -140,6 +140,11 @@ func (l *Language) GetStringMapString(key string) map[string]string {
|
|||
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.
|
||||
//
|
||||
|
|
|
@ -72,16 +72,46 @@ func LoadConfig(fs afero.Fs, relativeSourcePath, configFilename string) (*viper.
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -114,8 +144,6 @@ func loadLanguageSettings(cfg config.Provider, oldLangs helpers.Languages) error
|
|||
}
|
||||
}
|
||||
|
||||
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.
|
||||
langExists := false
|
||||
|
|
|
@ -31,6 +31,9 @@ type fileInfo struct {
|
|||
bundleTp bundleDirType
|
||||
source.ReadableFile
|
||||
overriddenLang string
|
||||
|
||||
// Set if the content language for this file is disabled.
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (fi *fileInfo) Lang() string {
|
||||
|
@ -60,6 +63,9 @@ func newFileInfo(sp *source.SourceSpec, baseDir, filename string, fi os.FileInfo
|
|||
ReadableFile: baseFi,
|
||||
}
|
||||
|
||||
lang := f.Lang()
|
||||
f.disabled = lang != "" && sp.DisabledLanguages[lang]
|
||||
|
||||
return f
|
||||
|
||||
}
|
||||
|
|
|
@ -149,8 +149,10 @@ func (c *capturer) capturePartial(filenames ...string) error {
|
|||
// 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 @@ func (c *capturer) handleBranchDir(dirname string) error {
|
|||
|
||||
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 @@ func (c *capturer) handleDir(dirname string) error {
|
|||
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 @@ func (c *capturer) handleDir(dirname string) error {
|
|||
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 @@ func (c *capturer) handleNonBundle(
|
|||
}
|
||||
} 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 @@ func (c *capturer) collectFiles(dirname string, handleFiles func(fis ...*fileInf
|
|||
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 @@ func (c *capturer) readDir(dirname string) ([]fileInfoName, error) {
|
|||
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 {
|
||||
|
|
|
@ -174,6 +174,7 @@ func TestPageBundlerCaptureMultilingual(t *testing.T) {
|
|||
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
|
||||
|
|
|
@ -192,6 +192,10 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
|
|||
|
||||
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 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
|
|||
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 TestPageBundlerSiteMultilingual(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ TheContent.
|
|||
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)
|
||||
|
|
|
@ -1207,30 +1207,28 @@ func (s *Site) checkDirectories() (err error) {
|
|||
}
|
||||
|
||||
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 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
|
|||
|
||||
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 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
|
|||
bundleMap *contentChangeMap
|
||||
)
|
||||
|
||||
mainHandler := &contentCaptureResultHandler{contentProcessors: contentProcessors}
|
||||
mainHandler := &contentCaptureResultHandler{contentProcessors: contentProcessors, defaultContentProcessor: defaultContentProcessor}
|
||||
|
||||
if s.running() {
|
||||
// Need to track changes.
|
||||
|
|
|
@ -35,6 +35,7 @@ type SourceSpec struct {
|
|||
|
||||
Languages map[string]interface{}
|
||||
DefaultContentLanguage string
|
||||
DisabledLanguages map[string]bool
|
||||
}
|
||||
|
||||
// NewSourceSpec initializes SourceSpec using languages from a given configuration.
|
||||
|
@ -42,6 +43,12 @@ func NewSourceSpec(cfg config.Provider, fs *hugofs.Fs) *SourceSpec {
|
|||
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 @@ func NewSourceSpec(cfg config.Provider, fs *hugofs.Fs) *SourceSpec {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue