mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
218 lines
5.5 KiB
Go
218 lines
5.5 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"
|
||
|
|
||
|
"github.com/pkg/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 := cfg.GetString("defaultContentLanguage")
|
||
|
if defaultLang == "" {
|
||
|
defaultLang = "en"
|
||
|
cfg.Set("defaultContentLanguage", defaultLang)
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
for _, disabled := range disableLanguages {
|
||
|
if disabled == defaultLang {
|
||
|
return c, fmt.Errorf("cannot disable default language %q", defaultLang)
|
||
|
}
|
||
|
|
||
|
if strings.EqualFold(k, disabled) {
|
||
|
v.(map[string]interface{})["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, errors.Wrap(err, "Failed to parse multilingual config")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (Languages, error) {
|
||
|
languages := make(Languages, len(l))
|
||
|
i := 0
|
||
|
|
||
|
for lang, langConf := range l {
|
||
|
langsMap, err := cast.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 "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 := cast.ToStringMap(v)
|
||
|
// Needed for case insensitive fetching of params values
|
||
|
maps.ToLower(m)
|
||
|
for k, vv := range m {
|
||
|
language.SetParam(k, vv)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|