2017-02-05 03:20:06 +00:00
// Copyright 2017 The Hugo Authors. All rights reserved.
2016-05-14 04:35:16 +00:00
//
// 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.
2017-02-05 03:20:06 +00:00
package i18n
2016-05-14 04:35:16 +00:00
import (
2019-06-02 09:11:46 +00:00
"reflect"
"strings"
"github.com/gohugoio/hugo/common/hreflect"
2018-10-03 12:58:09 +00:00
"github.com/gohugoio/hugo/common/loggers"
2017-06-13 16:42:45 +00:00
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
2018-10-03 12:58:09 +00:00
2019-06-02 09:11:46 +00:00
"github.com/nicksnyder/go-i18n/v2/i18n"
2016-08-09 09:41:56 +00:00
)
2019-06-02 09:11:46 +00:00
type translateFunc func ( translationID string , templateData interface { } ) string
2016-08-09 09:41:56 +00:00
var (
i18nWarningLogger = helpers . NewDistinctFeedbackLogger ( )
2016-05-14 04:35:16 +00:00
)
2017-02-05 03:20:06 +00:00
// Translator handles i18n translations.
type Translator struct {
2019-06-02 09:11:46 +00:00
translateFuncs map [ string ] translateFunc
2017-02-05 03:20:06 +00:00
cfg config . Provider
2020-10-21 09:17:48 +00:00
logger loggers . Logger
2016-07-26 12:44:37 +00:00
}
2017-02-05 03:20:06 +00:00
// NewTranslator creates a new Translator for the given language bundle and configuration.
2020-10-21 09:17:48 +00:00
func NewTranslator ( b * i18n . Bundle , cfg config . Provider , logger loggers . Logger ) Translator {
2019-06-02 09:11:46 +00:00
t := Translator { cfg : cfg , logger : logger , translateFuncs : make ( map [ string ] translateFunc ) }
2017-02-05 03:20:06 +00:00
t . initFuncs ( b )
return t
}
2016-07-26 12:44:37 +00:00
2017-02-05 03:20:06 +00:00
// Func gets the translate func for the given language, or for the default
// configured language if not found.
2019-06-02 09:11:46 +00:00
func ( t Translator ) Func ( lang string ) translateFunc {
2017-02-05 03:20:06 +00:00
if f , ok := t . translateFuncs [ lang ] ; ok {
return f
}
2020-10-21 09:17:48 +00:00
t . logger . Infof ( "Translation func for language %v not found, use default." , lang )
2017-02-05 03:20:06 +00:00
if f , ok := t . translateFuncs [ t . cfg . GetString ( "defaultContentLanguage" ) ] ; ok {
return f
}
2019-06-02 09:11:46 +00:00
2020-10-21 09:17:48 +00:00
t . logger . Infoln ( "i18n not initialized; if you need string translations, check that you have a bundle in /i18n that matches the site language or the default language." )
2019-06-02 09:11:46 +00:00
return func ( translationID string , args interface { } ) string {
2017-02-05 03:20:06 +00:00
return ""
2016-07-26 12:44:37 +00:00
}
2017-02-05 03:20:06 +00:00
2016-07-26 12:44:37 +00:00
}
2019-06-02 09:11:46 +00:00
func ( t Translator ) initFuncs ( bndl * i18n . Bundle ) {
2017-02-05 03:20:06 +00:00
enableMissingTranslationPlaceholders := t . cfg . GetBool ( "enableMissingTranslationPlaceholders" )
2016-07-26 12:44:37 +00:00
for _ , lang := range bndl . LanguageTags ( ) {
2019-06-02 09:11:46 +00:00
currentLang := lang
currentLangStr := currentLang . String ( )
2020-10-09 08:00:50 +00:00
// This may be pt-BR; make it case insensitive.
currentLangKey := strings . ToLower ( strings . TrimPrefix ( currentLangStr , artificialLangTagPrefix ) )
2019-06-02 09:11:46 +00:00
localizer := i18n . NewLocalizer ( bndl , currentLangStr )
t . translateFuncs [ currentLangKey ] = func ( translationID string , templateData interface { } ) string {
2020-10-06 18:32:52 +00:00
var pluralCount interface { }
2019-06-02 09:11:46 +00:00
if templateData != nil {
tp := reflect . TypeOf ( templateData )
if hreflect . IsNumber ( tp . Kind ( ) ) {
2020-10-06 18:32:52 +00:00
pluralCount = templateData
2019-06-02 09:11:46 +00:00
// This was how go-i18n worked in v1.
templateData = map [ string ] interface { } {
"Count" : templateData ,
}
2020-10-06 18:32:52 +00:00
2019-06-02 09:11:46 +00:00
}
2017-05-02 17:01:05 +00:00
}
2019-06-02 09:11:46 +00:00
translated , translatedLang , err := localizer . LocalizeWithTag ( & i18n . LocalizeConfig {
MessageID : translationID ,
TemplateData : templateData ,
2020-10-06 18:32:52 +00:00
PluralCount : pluralCount ,
2019-06-02 09:11:46 +00:00
} )
if err == nil && currentLang == translatedLang {
2017-05-03 07:11:14 +00:00
return translated
}
2019-06-02 09:11:46 +00:00
if _ , ok := err . ( * i18n . MessageNotFoundErr ) ; ! ok {
2020-10-21 09:17:48 +00:00
t . logger . Warnf ( "Failed to get translated string for language %q and ID %q: %s" , currentLangStr , translationID , err )
2016-08-09 09:41:56 +00:00
}
2017-05-03 07:11:14 +00:00
2017-02-05 03:20:06 +00:00
if t . cfg . GetBool ( "logI18nWarnings" ) {
2019-06-02 09:11:46 +00:00
i18nWarningLogger . Printf ( "i18n|MISSING_TRANSLATION|%s|%s" , currentLangStr , translationID )
2016-08-09 09:41:56 +00:00
}
2019-06-02 09:11:46 +00:00
2016-11-06 23:10:32 +00:00
if enableMissingTranslationPlaceholders {
2016-11-18 21:38:41 +00:00
return "[i18n] " + translationID
2016-09-18 20:18:13 +00:00
}
2019-06-02 09:11:46 +00:00
return translated
2016-08-09 09:41:56 +00:00
}
2016-05-14 04:35:16 +00:00
}
}