Initial commit
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Assisted-by: GLM 5.3
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
||||
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"cs": "cs",
|
||||
"cs-CZ": "cs",
|
||||
"cs-CZ-x-praha": "cs",
|
||||
"EN": "en",
|
||||
"en-GB": "en",
|
||||
"de": "",
|
||||
"": "",
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := Normalize(in); got != want {
|
||||
t.Errorf("Normalize(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleFallback(t *testing.T) {
|
||||
// English is the key: it always renders as itself.
|
||||
if got := Admin.T("en", "New post"); got != "New post" {
|
||||
t.Errorf("en simple = %q", got)
|
||||
}
|
||||
// A key the Czech table misses falls back to English, not to noise.
|
||||
if got := Admin.T("cs", "No such string in the catalogue"); got != "No such string in the catalogue" {
|
||||
t.Errorf("cs fallback = %q", got)
|
||||
}
|
||||
if got := Admin.T("cs", "New post"); got != "Nová publikace" {
|
||||
t.Errorf("cs New post = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlural(t *testing.T) {
|
||||
csCases := []struct {
|
||||
n int
|
||||
want string
|
||||
}{
|
||||
{1, "Celkem 1 publikace"},
|
||||
{2, "Celkem 2 publikace"},
|
||||
{4, "Celkem 4 publikace"},
|
||||
{5, "Celkem 5 publikací"},
|
||||
{11, "Celkem 11 publikací"},
|
||||
{12, "Celkem 12 publikací"},
|
||||
{14, "Celkem 14 publikací"},
|
||||
{22, "Celkem 22 publikace"},
|
||||
{24, "Celkem 24 publikace"},
|
||||
{25, "Celkem 25 publikací"},
|
||||
{111, "Celkem 111 publikací"},
|
||||
{124, "Celkem 124 publikace"},
|
||||
}
|
||||
for _, c := range csCases {
|
||||
if got := Admin.N("cs", "posts.total", c.n); got != c.want {
|
||||
t.Errorf("cs posts.total(%d) = %q, want %q", c.n, got, c.want)
|
||||
}
|
||||
}
|
||||
if got := Admin.N("en", "posts.total", 1); got != "1 post in total" {
|
||||
t.Errorf("en posts.total(1) = %q", got)
|
||||
}
|
||||
if got := Admin.N("en", "posts.total", 3); got != "3 posts in total" {
|
||||
t.Errorf("en posts.total(3) = %q", got)
|
||||
}
|
||||
if got := Admin.N("cs", "no.such.id", 2); got != "no.such.id" {
|
||||
t.Errorf("missing plural id = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
if got := Admin.Tf("cs", "Scheduled for %s", "2026-09-19"); got != "Naplánováno na 2026-09-19" {
|
||||
t.Errorf("cs Tf = %q", got)
|
||||
}
|
||||
if got := Admin.Tf("en", "No such string %s", "x"); got != "No such string x" {
|
||||
t.Errorf("en Tf fallback = %q", got)
|
||||
}
|
||||
if got := Admin.Tf2("cs", "Upgrade to %s failed: %s", "v1.1", "boom"); got != "Aktualizace na v1.1 selhala: boom" {
|
||||
t.Errorf("cs Tf2 = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTML(t *testing.T) {
|
||||
en := Admin.TH("en", "Download a <code>.tar.gz</code> archive of your entire site.")
|
||||
if en != "Download a <code>.tar.gz</code> archive of your entire site." {
|
||||
t.Errorf("en TH = %q", en)
|
||||
}
|
||||
csOut := Admin.TH("cs", "Download a <code>.tar.gz</code> archive of your entire site.")
|
||||
if !strings.Contains(csOut, "<code>.tar.gz</code>") {
|
||||
t.Errorf("cs TH lost the markup: %q", csOut)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTemplatesAreTranslated walks the admin templates and asserts every
|
||||
// tr, trh and trf key exists in the Czech catalogue, and every trn id is a
|
||||
// known plural message. A string edited in a template without its Czech
|
||||
// counterpart fails here rather than silently rendering English.
|
||||
func TestTemplatesAreTranslated(t *testing.T) {
|
||||
root := filepath.Join("..", "..", "internal", "web", "templates")
|
||||
entries, err := os.ReadDir(root)
|
||||
if err != nil {
|
||||
t.Fatalf("read templates: %v", err)
|
||||
}
|
||||
callRe := regexp.MustCompile(`\b(tr|trh|trf)\s+"((?:[^"\\]|\\.)*)"`)
|
||||
// trn takes the count first: "trn .Count \"id\"" or
|
||||
// "trn (len .Posts) \"id\"", so the id is the first quoted string
|
||||
// after a run of non-quote characters on the same line.
|
||||
idRe := regexp.MustCompile(`\btrn\s+[^\n"]*"((?:[^"\\]|\\.)*)"`)
|
||||
for _, entry := range entries {
|
||||
if !strings.HasSuffix(entry.Name(), ".html") {
|
||||
continue
|
||||
}
|
||||
src, err := os.ReadFile(filepath.Join(root, entry.Name()))
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", entry.Name(), err)
|
||||
}
|
||||
for _, m := range callRe.FindAllStringSubmatch(string(src), -1) {
|
||||
fn, key := m[1], m[2]
|
||||
switch fn {
|
||||
case "tr", "trf":
|
||||
if _, ok := cs[key]; !ok {
|
||||
t.Errorf("%s: tr key %q has no Czech entry", entry.Name(), key)
|
||||
}
|
||||
case "trh":
|
||||
if _, ok := csHTML[key]; !ok {
|
||||
t.Errorf("%s: trh key %q has no Czech entry", entry.Name(), key)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, m := range idRe.FindAllStringSubmatch(string(src), -1) {
|
||||
if _, ok := pluralForms[m[1]]; !ok {
|
||||
t.Errorf("%s: trn id %q is not a plural message", entry.Name(), m[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user