github.com/jbramsden/hugo@v0.47.1/i18n/i18n.go (about)

     1  // Copyright 2017 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package i18n
    15  
    16  import (
    17  	"github.com/gohugoio/hugo/config"
    18  	"github.com/gohugoio/hugo/helpers"
    19  	"github.com/nicksnyder/go-i18n/i18n/bundle"
    20  	jww "github.com/spf13/jwalterweatherman"
    21  )
    22  
    23  var (
    24  	i18nWarningLogger = helpers.NewDistinctFeedbackLogger()
    25  )
    26  
    27  // Translator handles i18n translations.
    28  type Translator struct {
    29  	translateFuncs map[string]bundle.TranslateFunc
    30  	cfg            config.Provider
    31  	logger         *jww.Notepad
    32  }
    33  
    34  // NewTranslator creates a new Translator for the given language bundle and configuration.
    35  func NewTranslator(b *bundle.Bundle, cfg config.Provider, logger *jww.Notepad) Translator {
    36  	t := Translator{cfg: cfg, logger: logger, translateFuncs: make(map[string]bundle.TranslateFunc)}
    37  	t.initFuncs(b)
    38  	return t
    39  }
    40  
    41  // Func gets the translate func for the given language, or for the default
    42  // configured language if not found.
    43  func (t Translator) Func(lang string) bundle.TranslateFunc {
    44  	if f, ok := t.translateFuncs[lang]; ok {
    45  		return f
    46  	}
    47  	t.logger.WARN.Printf("Translation func for language %v not found, use default.", lang)
    48  	if f, ok := t.translateFuncs[t.cfg.GetString("defaultContentLanguage")]; ok {
    49  		return f
    50  	}
    51  	t.logger.WARN.Println("i18n not initialized, check that you have language file (in i18n) that matches the site language or the default language.")
    52  	return func(translationID string, args ...interface{}) string {
    53  		return ""
    54  	}
    55  
    56  }
    57  
    58  func (t Translator) initFuncs(bndl *bundle.Bundle) {
    59  	defaultContentLanguage := t.cfg.GetString("defaultContentLanguage")
    60  
    61  	defaultT, err := bndl.Tfunc(defaultContentLanguage)
    62  	if err != nil {
    63  		jww.WARN.Printf("No translation bundle found for default language %q", defaultContentLanguage)
    64  	}
    65  
    66  	enableMissingTranslationPlaceholders := t.cfg.GetBool("enableMissingTranslationPlaceholders")
    67  	for _, lang := range bndl.LanguageTags() {
    68  		currentLang := lang
    69  
    70  		t.translateFuncs[currentLang] = func(translationID string, args ...interface{}) string {
    71  			tFunc, err := bndl.Tfunc(currentLang)
    72  			if err != nil {
    73  				jww.WARN.Printf("could not load translations for language %q (%s), will use default content language.\n", lang, err)
    74  			}
    75  
    76  			translated := tFunc(translationID, args...)
    77  			if translated != translationID {
    78  				return translated
    79  			}
    80  			// If there is no translation for translationID,
    81  			// then Tfunc returns translationID itself.
    82  			// But if user set same translationID and translation, we should check
    83  			// if it really untranslated:
    84  			if isIDTranslated(currentLang, translationID, bndl) {
    85  				return translated
    86  			}
    87  
    88  			if t.cfg.GetBool("logI18nWarnings") {
    89  				i18nWarningLogger.Printf("i18n|MISSING_TRANSLATION|%s|%s", currentLang, translationID)
    90  			}
    91  			if enableMissingTranslationPlaceholders {
    92  				return "[i18n] " + translationID
    93  			}
    94  			if defaultT != nil {
    95  				translated := defaultT(translationID, args...)
    96  				if translated != translationID {
    97  					return translated
    98  				}
    99  				if isIDTranslated(defaultContentLanguage, translationID, bndl) {
   100  					return translated
   101  				}
   102  			}
   103  			return ""
   104  		}
   105  	}
   106  }
   107  
   108  // If bndl contains the translationID for specified currentLang,
   109  // then the translationID is actually translated.
   110  func isIDTranslated(lang, id string, b *bundle.Bundle) bool {
   111  	_, contains := b.Translations()[lang][id]
   112  	return contains
   113  }