mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
633cc772e0
The fallback order for translations is now: 1. Current language's locale (e.g., pt-BR → pt-br.toml) 2. Current language's key (e.g., pt → pt.toml) 3. Default language's locale (e.g., es-AR → es-ar.toml) ← new 4. Default language's key (e.g., es → es.toml) Closes #14243 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
381 lines
9.5 KiB
Go
381 lines
9.5 KiB
Go
// Copyright 2025 The Hugo Authors. All rights reserved.
|
|
//
|
|
// 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.
|
|
|
|
package langs_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gohugoio/hugo/hugolib"
|
|
)
|
|
|
|
func TestLanguagesContentSimple(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
baseURL = "https://example.org/"
|
|
defaultContentLanguage = "en"
|
|
defaultContentLanguageInSubdir = true
|
|
[languages]
|
|
[languages.en]
|
|
weight = 2
|
|
[languages.nn]
|
|
weight = 1
|
|
-- content/_index.md --
|
|
---
|
|
title: "Home"
|
|
---
|
|
|
|
Welcome to the home page.
|
|
-- content/_index.nn.md --
|
|
---
|
|
title: "Heim"
|
|
---
|
|
Welkomen heim!
|
|
-- layouts/all.html --
|
|
title: {{ .Title }}|
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/en/index.html", `title: Home|`)
|
|
b.AssertFileContent("public/nn/index.html", `title: Heim|`)
|
|
}
|
|
|
|
func TestLanguagesContentSharedResource(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ["taxonomy", "term", "sitemap", "404"]
|
|
baseURL = "https://example.org/"
|
|
defaultContentLanguage = "en"
|
|
defaultContentLanguageInSubdir = true
|
|
[languages]
|
|
[languages.en]
|
|
weight = 2
|
|
[languages.nn]
|
|
weight = 1
|
|
-- content/mytext.txt --
|
|
This is a shared text file.
|
|
-- content/_index.md --
|
|
---
|
|
title: "Home"
|
|
---
|
|
|
|
Welcome to the home page.
|
|
-- content/_index.nn.md --
|
|
---
|
|
title: "Heim"
|
|
---
|
|
Welkomen heim!
|
|
-- layouts/home.html --
|
|
{{ $text := .Resources.Get "mytext.txt" }}
|
|
title: {{ .Title }}|text: {{ with $text }}{{ .Content | safeHTML }}{{ end }}|{{ .Resources | len}}
|
|
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/nn/index.html", `title: Heim|text: This is a shared text file.|1`)
|
|
b.AssertFileContent("public/en/index.html", `title: Home|text: This is a shared text file.|1`)
|
|
}
|
|
|
|
// Issue 14031
|
|
func TestDraftTermIssue14031(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['home','rss','section','sitemap']
|
|
capitalizeListTitles = false
|
|
pluralizeListTitles = false
|
|
defaultContentLanguage = 'en'
|
|
defaultContentLanguageInSubdir = true
|
|
[languages.en]
|
|
weight = 1
|
|
[languages.fr]
|
|
weight = 2
|
|
[taxonomies]
|
|
tag = 'tags'
|
|
-- content/p1.en.md --
|
|
---
|
|
title: P1 (en)
|
|
tags: [a,b]
|
|
---
|
|
-- content/p1.fr.md --
|
|
---
|
|
title: P1 (fr)
|
|
tags: [a,b]
|
|
---
|
|
-- content/tags/a/_index.en.md --
|
|
---
|
|
title: a (en)
|
|
---
|
|
-- content/tags/a/_index.fr.md --
|
|
---
|
|
title: a (fr)
|
|
---
|
|
-- content/tags/b/_index.en.md --
|
|
---
|
|
title: b (en)
|
|
---
|
|
-- content/tags/b/_index.fr.md --
|
|
---
|
|
title: b (fr)
|
|
draft: false
|
|
---
|
|
-- layouts/list.html --
|
|
{{- .Title -}}|
|
|
{{- range .Pages -}}
|
|
TITLE: {{ .Title }} RELPERMALINK: {{ .RelPermalink }}|
|
|
{{- end -}}
|
|
-- layouts/page.html --
|
|
{{- .Title -}}|
|
|
{{- range .GetTerms "tags" -}}
|
|
TITLE: {{ .Title }} RELPERMALINK: {{ .RelPermalink }}|
|
|
{{- end -}}
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileExists("public/en/tags/a/index.html", true)
|
|
b.AssertFileExists("public/fr/tags/a/index.html", true)
|
|
b.AssertFileExists("public/en/tags/b/index.html", true)
|
|
b.AssertFileExists("public/fr/tags/b/index.html", true)
|
|
|
|
b.AssertFileContentEquals("public/en/p1/index.html",
|
|
"P1 (en)|TITLE: a (en) RELPERMALINK: /en/tags/a/|TITLE: b (en) RELPERMALINK: /en/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/p1/index.html",
|
|
"P1 (fr)|TITLE: a (fr) RELPERMALINK: /fr/tags/a/|TITLE: b (fr) RELPERMALINK: /fr/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/en/tags/index.html",
|
|
"tags|TITLE: a (en) RELPERMALINK: /en/tags/a/|TITLE: b (en) RELPERMALINK: /en/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/tags/index.html",
|
|
"tags|TITLE: a (fr) RELPERMALINK: /fr/tags/a/|TITLE: b (fr) RELPERMALINK: /fr/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/en/tags/a/index.html",
|
|
"a (en)|TITLE: P1 (en) RELPERMALINK: /en/p1/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/tags/a/index.html",
|
|
"a (fr)|TITLE: P1 (fr) RELPERMALINK: /fr/p1/|",
|
|
)
|
|
b.AssertFileContentEquals("public/en/tags/b/index.html",
|
|
"b (en)|TITLE: P1 (en) RELPERMALINK: /en/p1/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/tags/b/index.html",
|
|
"b (fr)|TITLE: P1 (fr) RELPERMALINK: /fr/p1/|",
|
|
)
|
|
|
|
// Set draft to true on content/tags/b/_index.fr.md.
|
|
files = strings.ReplaceAll(files, "draft: false", "draft: true")
|
|
|
|
b = hugolib.Test(t, files)
|
|
|
|
b.AssertFileExists("public/en/tags/a/index.html", true)
|
|
b.AssertFileExists("public/fr/tags/a/index.html", true)
|
|
b.AssertFileExists("public/en/tags/b/index.html", true)
|
|
b.AssertFileExists("public/fr/tags/b/index.html", false)
|
|
|
|
// The assertion below fails.
|
|
// Got: P1 (en)|TITLE: a (en) RELPERMALINK: /en/tags/a/|
|
|
b.AssertFileContentEquals("public/en/p1/index.html",
|
|
"P1 (en)|TITLE: a (en) RELPERMALINK: /en/tags/a/|TITLE: b (en) RELPERMALINK: /en/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/p1/index.html",
|
|
"P1 (fr)|TITLE: a (fr) RELPERMALINK: /fr/tags/a/|",
|
|
)
|
|
b.AssertFileContentEquals("public/en/tags/index.html",
|
|
"tags|TITLE: a (en) RELPERMALINK: /en/tags/a/|TITLE: b (en) RELPERMALINK: /en/tags/b/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/tags/index.html",
|
|
"tags|TITLE: a (fr) RELPERMALINK: /fr/tags/a/|",
|
|
)
|
|
b.AssertFileContentEquals("public/en/tags/a/index.html",
|
|
"a (en)|TITLE: P1 (en) RELPERMALINK: /en/p1/|",
|
|
)
|
|
b.AssertFileContentEquals("public/fr/tags/a/index.html",
|
|
"a (fr)|TITLE: P1 (fr) RELPERMALINK: /fr/p1/|",
|
|
)
|
|
// The assertion below fails.
|
|
// Got: b (en)|
|
|
b.AssertFileContentEquals("public/en/tags/b/index.html",
|
|
"b (en)|TITLE: P1 (en) RELPERMALINK: /en/p1/|",
|
|
)
|
|
}
|
|
|
|
func TestLocaleAsLocalizationKeyIssue9109(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
defaultContentLanguage = 'de'
|
|
defaultContentLanguageInSubdir = true
|
|
|
|
[languages.de]
|
|
weight = 1
|
|
|
|
[languages.en]
|
|
locale = 'en-US'
|
|
weight = 2
|
|
|
|
[languages.fr]
|
|
locale = 'bogus'
|
|
weight = 3
|
|
|
|
[languages.nn]
|
|
weight = 4
|
|
|
|
[languages.xx]
|
|
locale = 'bogus'
|
|
weight = 5
|
|
|
|
[languages.zh-cn]
|
|
locale = 'zh-Hans'
|
|
weight = 6
|
|
-- layouts/home.html --
|
|
Time: {{ "2026-04-01T20:11:31+08:00" | time.Format ":date_long" }}|
|
|
FormatAccounting: {{ 512.5032 | lang.FormatAccounting 2 "USD" }}|
|
|
FormatCurrency: {{ 512.5032 | lang.FormatCurrency 2 "USD" }}|
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
// de: no locale => localize with lang.
|
|
b.AssertFileContent("public/de/index.html",
|
|
`Time: 1. April 2026|`,
|
|
"FormatAccounting: 512,50\u00a0$|",
|
|
"FormatCurrency: 512,50\u00a0$|",
|
|
)
|
|
|
|
// en: valid locale => localize with locale.
|
|
b.AssertFileContent("public/en/index.html",
|
|
`Time: April 1, 2026|`,
|
|
`FormatAccounting: $512.50|`,
|
|
`FormatCurrency: $512.50|`,
|
|
)
|
|
|
|
// fr: invalid locale => localize with lang.
|
|
b.AssertFileContent("public/fr/index.html",
|
|
`Time: 1 avril 2026|`,
|
|
"FormatAccounting: 512,50\u00a0$US|",
|
|
"FormatCurrency: 512,50\u00a0$US|",
|
|
)
|
|
|
|
// nn: no locale => localize with lang.
|
|
b.AssertFileContent("public/nn/index.html",
|
|
`Time: 1. april 2026|`,
|
|
"FormatAccounting: 512,50\u00a0USD|",
|
|
"FormatCurrency: 512,50\u00a0USD|",
|
|
)
|
|
|
|
// xx: invalid locale + invalid lang => localize with defaultContentLanguage.
|
|
b.AssertFileContent("public/xx/index.html",
|
|
`Time: 1. April 2026|`,
|
|
"FormatAccounting: 512,50\u00a0$|",
|
|
"FormatCurrency: 512,50\u00a0$|",
|
|
)
|
|
|
|
// zh-cn: invalid lang, but valid locale => localize with locale.
|
|
b.AssertFileContent("public/zh-cn/index.html",
|
|
`Time: 2026年4月1日|`,
|
|
`FormatAccounting: US$512.50|`,
|
|
`FormatCurrency: US$512.50|`,
|
|
)
|
|
}
|
|
|
|
func TestMultipleLanguageVariants7982(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
|
defaultContentLanguageInSubdir = true
|
|
[languages.en]
|
|
weight = 1
|
|
[languages.de]
|
|
weight = 2
|
|
[languages.de-de]
|
|
weight = 3
|
|
-- i18n/en.toml --
|
|
file = 'en'
|
|
-- i18n/de.toml --
|
|
file = 'de'
|
|
-- i18n/de-de.toml --
|
|
file = 'de-de'
|
|
-- layouts/index.html --
|
|
language: {{ site.Language.Name }} file: {{ T "file" }}|
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/en/index.html", "language: en file: en|")
|
|
b.AssertFileContent("public/de/index.html", "language: de file: de|")
|
|
b.AssertFileContent("public/de-de/index.html", "language: de-de file: de-de|")
|
|
}
|
|
|
|
func TestDefaultContentLanguageFallback14243(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
defaultContentLanguage = 'es'
|
|
defaultContentLanguageInSubdir = true
|
|
|
|
[languages.es]
|
|
locale = 'es-AR'
|
|
weight = 1
|
|
|
|
[languages.pt]
|
|
locale = 'pt-BR'
|
|
weight = 2
|
|
-- layouts/home.html --
|
|
{{ T "foo"}}|
|
|
-- i18n/es-ar.toml --
|
|
foo = 'foo es-ar'
|
|
-- i18n/es.toml --
|
|
foo = 'foo es'
|
|
-- i18n/pt-br.toml --
|
|
foo = 'foo pt-br'
|
|
-- i18n/pt.toml --
|
|
foo = 'foo pt'
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
b.AssertFileContent("public/pt/index.html", "foo pt-br|")
|
|
|
|
files = strings.ReplaceAll(files, "i18n/pt-br.toml", "unused-a.toml")
|
|
|
|
b = hugolib.Test(t, files)
|
|
b.AssertFileContent("public/pt/index.html", "foo pt|")
|
|
|
|
files = strings.ReplaceAll(files, "i18n/pt.toml", "unused-b.toml")
|
|
|
|
b = hugolib.Test(t, files)
|
|
b.AssertFileContent("public/pt/index.html", "foo es-ar|")
|
|
|
|
files = strings.ReplaceAll(files, "i18n/es-ar.toml", "unused-c.toml")
|
|
|
|
b = hugolib.Test(t, files)
|
|
b.AssertFileContent("public/pt/index.html", "foo es|")
|
|
|
|
files = strings.ReplaceAll(files, "i18n/es.toml", "unused-d.toml")
|
|
|
|
b = hugolib.Test(t, files)
|
|
b.AssertFileContent("public/pt/index.html", "|")
|
|
}
|