langs/i18n: Prefer languageCode when picking translation file

Fixes #14204
Closes #14217

Co-authored-by: Patrice Chalin <pchalin@gmail.com>
This commit is contained in:
Bjørn Erik Pedersen
2025-11-28 21:24:28 +01:00
committed by GitHub
parent 9e24b5645d
commit 6be463b586
3 changed files with 76 additions and 2 deletions

View File

@@ -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 {

View File

@@ -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 --"))
}

View File

@@ -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