diff --git a/langs/i18n/i18n.go b/langs/i18n/i18n.go index db27fb997..3893e0a43 100644 --- a/langs/i18n/i18n.go +++ b/langs/i18n/i18n.go @@ -45,6 +45,12 @@ func NewTranslator(b *i18n.Bundle, cfg config.AllProvider, logger loggers.Logger return t } +// Lookup looks up the translate func for the given language. +func (t Translator) Lookup(lang string) (translateFunc, bool) { + f, ok := t.translateFuncs[lang] + return f, ok +} + // Func gets the translate func for the given language, or for the default // configured language if not found. func (t Translator) Func(lang string) translateFunc { diff --git a/langs/i18n/i18n_integration_test.go b/langs/i18n/i18n_integration_test.go index 781d74909..af06e7b6e 100644 --- a/langs/i18n/i18n_integration_test.go +++ b/langs/i18n/i18n_integration_test.go @@ -14,6 +14,7 @@ package i18n_test import ( + "strings" "testing" qt "github.com/frankban/quicktest" @@ -165,3 +166,57 @@ b = 'b translated' b.Assert(err, qt.IsNotNil) b.Assert(err.Error(), qt.Contains, "failed to load translations: reserved keys [description] mixed with unreserved keys [a b]: see the lang.Translate documentation for a list of reserved keys") } + +func TestI18nUseLanguageCodeWhenBothTranslationFilesArePresent(t *testing.T) { + t.Parallel() + + filesTemplate := ` +-- hugo.yaml -- +languages: + en: + languageCode: en-us +-- i18n/en.yml -- +hello: Greetings from en! +-- i18n/en-us.yml -- +hello: Greetings from en-us! +-- layouts/all.html -- +{{ T "hello" }} +` + + runTest := func(s string) { + b := hugolib.Test(t, s) + b.AssertFileContent("public/index.html", `Greetings from en-us!`) + } + + runTest(filesTemplate) + runTest(strings.ReplaceAll(filesTemplate, "languageCode: en-us", "languageCode: En-US")) + runTest(strings.ReplaceAll(filesTemplate, "-- i18n/en-us.yml --", "-- i18n/en-US.yml --")) +} + +func TestI18nUseLangWhenLanguageCodeFileIsMissing(t *testing.T) { + t.Parallel() + + filesTemplate := ` +-- hugo.yaml -- +languages: + en: + title: English + pt: + languageCode: pt-br +-- i18n/en.yml -- +hello: Greetings from en! +-- i18n/pt.yml -- +hello: Greetings from pt! +-- layouts/all.html -- +{{ T "hello" }} +` + + runTest := func(s string) { + b := hugolib.Test(t, s) + b.AssertFileContent("public/pt/index.html", `Greetings from pt!`) + } + + runTest(filesTemplate) + runTest(strings.ReplaceAll(filesTemplate, "pt:", "PT:")) + runTest(strings.ReplaceAll(filesTemplate, "-- i18n/pt.yml --", "-- i18n/pT.yml --")) +} diff --git a/langs/i18n/translationProvider.go b/langs/i18n/translationProvider.go index 227492794..490951f4a 100644 --- a/langs/i18n/translationProvider.go +++ b/langs/i18n/translationProvider.go @@ -78,7 +78,7 @@ func (tp *TranslationProvider) NewResource(dst *deps.Deps) error { tp.t = NewTranslator(bundle, dst.Conf, dst.Log) - dst.Translate = tp.t.Func(dst.Conf.Language().(*langs.Language).Lang) + dst.Translate = tp.getTranslateFunc(dst) return nil } @@ -128,10 +128,23 @@ func addTranslationFile(bundle *i18n.Bundle, r *source.File) error { // CloneResource sets the language func for the new language. func (tp *TranslationProvider) CloneResource(dst, src *deps.Deps) error { - dst.Translate = tp.t.Func(dst.Conf.Language().(*langs.Language).Lang) + dst.Translate = tp.getTranslateFunc(dst) return nil } +// getTranslateFunc returns the translation function for the language in Deps. +// We first try the language code (e.g. "en-US"), then the language key (e.g. "en"). +func (tp *TranslationProvider) getTranslateFunc(dst *deps.Deps) func(ctx context.Context, translationID string, templateData any) string { + l := dst.Conf.Language().(*langs.Language) + if lc := l.LanguageCode(); lc != "" { + if fn, ok := tp.t.Lookup(strings.ToLower(lc)); ok { + return fn + } + } + // Func will fall back to the default language if not found. + return tp.t.Func(l.Lang) +} + func errWithFileContext(inerr error, r *source.File) error { meta := r.FileInfo().Meta() realFilename := meta.Filename