mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	* Add file context to minifier errors when publishing * Misc fixes (see issues) * Allow custom server error template in layouts/server/error.html To get to this, this commit also cleans up and simplifies the code surrounding errors and files. This also removes the usage of `github.com/pkg/errors`, mostly because of https://github.com/pkg/errors/issues/223 -- but also because most of this is now built-in to Go. Fixes #9852 Fixes #9857 Fixes #9863
		
			
				
	
	
		
			227 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2018 The Hugo Authors. All rights reserved.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
// http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package langs
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"path/filepath"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/gohugoio/hugo/common/maps"
 | 
						|
 | 
						|
	"github.com/spf13/cast"
 | 
						|
 | 
						|
	"errors"
 | 
						|
 | 
						|
	"github.com/gohugoio/hugo/config"
 | 
						|
)
 | 
						|
 | 
						|
type LanguagesConfig struct {
 | 
						|
	Languages                      Languages
 | 
						|
	Multihost                      bool
 | 
						|
	DefaultContentLanguageInSubdir bool
 | 
						|
}
 | 
						|
 | 
						|
func LoadLanguageSettings(cfg config.Provider, oldLangs Languages) (c LanguagesConfig, err error) {
 | 
						|
	defaultLang := strings.ToLower(cfg.GetString("defaultContentLanguage"))
 | 
						|
	if defaultLang == "" {
 | 
						|
		defaultLang = "en"
 | 
						|
		cfg.Set("defaultContentLanguage", defaultLang)
 | 
						|
	}
 | 
						|
 | 
						|
	var languages map[string]any
 | 
						|
 | 
						|
	languagesFromConfig := cfg.GetParams("languages")
 | 
						|
	disableLanguages := cfg.GetStringSlice("disableLanguages")
 | 
						|
 | 
						|
	if len(disableLanguages) == 0 {
 | 
						|
		languages = languagesFromConfig
 | 
						|
	} else {
 | 
						|
		languages = make(maps.Params)
 | 
						|
		for k, v := range languagesFromConfig {
 | 
						|
			for _, disabled := range disableLanguages {
 | 
						|
				if disabled == defaultLang {
 | 
						|
					return c, fmt.Errorf("cannot disable default language %q", defaultLang)
 | 
						|
				}
 | 
						|
 | 
						|
				if strings.EqualFold(k, disabled) {
 | 
						|
					v.(maps.Params)["disabled"] = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			languages[k] = v
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var languages2 Languages
 | 
						|
 | 
						|
	if len(languages) == 0 {
 | 
						|
		languages2 = append(languages2, NewDefaultLanguage(cfg))
 | 
						|
	} else {
 | 
						|
		languages2, err = toSortedLanguages(cfg, languages)
 | 
						|
		if err != nil {
 | 
						|
			return c, fmt.Errorf("Failed to parse multilingual config: %w", err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if oldLangs != nil {
 | 
						|
		// When in multihost mode, the languages are mapped to a server, so
 | 
						|
		// some structural language changes will need a restart of the dev server.
 | 
						|
		// The validation below isn't complete, but should cover the most
 | 
						|
		// important cases.
 | 
						|
		var invalid bool
 | 
						|
		if languages2.IsMultihost() != oldLangs.IsMultihost() {
 | 
						|
			invalid = true
 | 
						|
		} else {
 | 
						|
			if languages2.IsMultihost() && len(languages2) != len(oldLangs) {
 | 
						|
				invalid = true
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if invalid {
 | 
						|
			return c, errors.New("language change needing a server restart detected")
 | 
						|
		}
 | 
						|
 | 
						|
		if languages2.IsMultihost() {
 | 
						|
			// We need to transfer any server baseURL to the new language
 | 
						|
			for i, ol := range oldLangs {
 | 
						|
				nl := languages2[i]
 | 
						|
				nl.Set("baseURL", ol.GetString("baseURL"))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// The defaultContentLanguage is something the user has to decide, but it needs
 | 
						|
	// to match a language in the language definition list.
 | 
						|
	langExists := false
 | 
						|
	for _, lang := range languages2 {
 | 
						|
		if lang.Lang == defaultLang {
 | 
						|
			langExists = true
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if !langExists {
 | 
						|
		return c, fmt.Errorf("site config value %q for defaultContentLanguage does not match any language definition", defaultLang)
 | 
						|
	}
 | 
						|
 | 
						|
	c.Languages = languages2
 | 
						|
	c.Multihost = languages2.IsMultihost()
 | 
						|
	c.DefaultContentLanguageInSubdir = c.Multihost
 | 
						|
 | 
						|
	sortedDefaultFirst := make(Languages, len(c.Languages))
 | 
						|
	for i, v := range c.Languages {
 | 
						|
		sortedDefaultFirst[i] = v
 | 
						|
	}
 | 
						|
	sort.Slice(sortedDefaultFirst, func(i, j int) bool {
 | 
						|
		li, lj := sortedDefaultFirst[i], sortedDefaultFirst[j]
 | 
						|
		if li.Lang == defaultLang {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
 | 
						|
		if lj.Lang == defaultLang {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		return i < j
 | 
						|
	})
 | 
						|
 | 
						|
	cfg.Set("languagesSorted", c.Languages)
 | 
						|
	cfg.Set("languagesSortedDefaultFirst", sortedDefaultFirst)
 | 
						|
	cfg.Set("multilingual", len(languages2) > 1)
 | 
						|
 | 
						|
	multihost := c.Multihost
 | 
						|
 | 
						|
	if multihost {
 | 
						|
		cfg.Set("defaultContentLanguageInSubdir", true)
 | 
						|
		cfg.Set("multihost", true)
 | 
						|
	}
 | 
						|
 | 
						|
	if multihost {
 | 
						|
		// The baseURL may be provided at the language level. If that is true,
 | 
						|
		// then every language must have a baseURL. In this case we always render
 | 
						|
		// to a language sub folder, which is then stripped from all the Permalink URLs etc.
 | 
						|
		for _, l := range languages2 {
 | 
						|
			burl := l.GetLocal("baseURL")
 | 
						|
			if burl == nil {
 | 
						|
				return c, errors.New("baseURL must be set on all or none of the languages")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for _, language := range c.Languages {
 | 
						|
		if language.initErr != nil {
 | 
						|
			return c, language.initErr
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return c, nil
 | 
						|
}
 | 
						|
 | 
						|
func toSortedLanguages(cfg config.Provider, l map[string]any) (Languages, error) {
 | 
						|
	languages := make(Languages, len(l))
 | 
						|
	i := 0
 | 
						|
 | 
						|
	for lang, langConf := range l {
 | 
						|
		langsMap, err := maps.ToStringMapE(langConf)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("Language config is not a map: %T", langConf)
 | 
						|
		}
 | 
						|
 | 
						|
		language := NewLanguage(lang, cfg)
 | 
						|
 | 
						|
		for loki, v := range langsMap {
 | 
						|
			switch loki {
 | 
						|
			case "title":
 | 
						|
				language.Title = cast.ToString(v)
 | 
						|
			case "languagename":
 | 
						|
				language.LanguageName = cast.ToString(v)
 | 
						|
			case "languagedirection":
 | 
						|
				language.LanguageDirection = cast.ToString(v)
 | 
						|
			case "weight":
 | 
						|
				language.Weight = cast.ToInt(v)
 | 
						|
			case "contentdir":
 | 
						|
				language.ContentDir = filepath.Clean(cast.ToString(v))
 | 
						|
			case "disabled":
 | 
						|
				language.Disabled = cast.ToBool(v)
 | 
						|
			case "params":
 | 
						|
				m := maps.ToStringMap(v)
 | 
						|
				// Needed for case insensitive fetching of params values
 | 
						|
				maps.PrepareParams(m)
 | 
						|
				for k, vv := range m {
 | 
						|
					language.SetParam(k, vv)
 | 
						|
				}
 | 
						|
			case "timezone":
 | 
						|
				if err := language.loadLocation(cast.ToString(v)); err != nil {
 | 
						|
					return nil, err
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			// Put all into the Params map
 | 
						|
			language.SetParam(loki, v)
 | 
						|
 | 
						|
			// Also set it in the configuration map (for baseURL etc.)
 | 
						|
			language.Set(loki, v)
 | 
						|
		}
 | 
						|
 | 
						|
		languages[i] = language
 | 
						|
		i++
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(languages)
 | 
						|
 | 
						|
	return languages, nil
 | 
						|
}
 |