]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
langs/i18n: Prefer languageCode when picking translation file
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 28 Nov 2025 20:24:28 +0000 (21:24 +0100)
committerGitHub <noreply@github.com>
Fri, 28 Nov 2025 20:24:28 +0000 (21:24 +0100)
Fixes #14204
Closes #14217

Co-authored-by: Patrice Chalin <pchalin@gmail.com>
langs/i18n/i18n.go
langs/i18n/i18n_integration_test.go
langs/i18n/translationProvider.go

index db27fb997e788b23f0aaa9b543b66bc81fc23af1..3893e0a43f97d3464ab245aff237ee3634aab71b 100644 (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 {
index 781d749093bd8518f7dbc19ff6addfebfc8a09bf..af06e7b6e01f043ad54add4feec53b82dc7907bf 100644 (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 --"))
+}
index 227492794c333cb9d757e9b543f40b40aa003c99..490951f4ae8694a9f5d5f25f41ec66312e0ae1dc 100644 (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