krz/skunky-art

Alternative privacy frontend for DeviantArt.

clone: git clone https://gitbay.org/krz/skunky-art.git

main: app/i18n_test.go · raw

  1package app
  2
  3import (
  4	"encoding/json"
  5	"os"
  6	"path/filepath"
  7	"strings"
  8	"testing"
  9)
 10
 11// TestEnglishCatalogueCoversEveryKeyTheTemplatesUse is the guard against a
 12// template asking for a key nobody defined: T falls back to the key itself, so
 13// the failure renders as "nav.home" on the page rather than crashing.
 14func TestEnglishCatalogueCoversEveryKeyTheTemplatesUse(t *testing.T) {
 15	body, err := os.ReadFile(filepath.Join("..", "static", "lang", "en.json"))
 16	if err != nil {
 17		t.Fatalf("read en.json: %v", err)
 18	}
 19	var cat map[string]string
 20	if err := json.Unmarshal(body, &cat); err != nil {
 21		t.Fatalf("en.json is not valid JSON: %v", err)
 22	}
 23
 24	files, err := filepath.Glob(filepath.Join("..", "static", "html", "*.htm"))
 25	if err != nil || len(files) == 0 {
 26		t.Fatalf("no templates found: %v", err)
 27	}
 28
 29	for _, f := range files {
 30		src, err := os.ReadFile(f)
 31		if err != nil {
 32			t.Fatalf("read %s: %v", f, err)
 33		}
 34		for _, key := range templateKeys(string(src)) {
 35			if _, ok := cat[key]; !ok {
 36				t.Errorf("%s uses %q, which en.json does not define", filepath.Base(f), key)
 37			}
 38		}
 39	}
 40}
 41
 42// templateKeys pulls the key out of every {{T "..."}} action in src.
 43func templateKeys(src string) []string {
 44	var out []string
 45	for rest := src; ; {
 46		i := strings.Index(rest, `{{T "`)
 47		if i < 0 {
 48			return out
 49		}
 50		rest = rest[i+len(`{{T "`):]
 51		j := strings.Index(rest, `"`)
 52		if j < 0 {
 53			return out
 54		}
 55		out = append(out, rest[:j])
 56		rest = rest[j:]
 57	}
 58}
 59
 60// TestTFallsBackRatherThanBlanking: a catalogue that has not caught up must show
 61// English, and an unknown key must show itself, because a blank label in the UI
 62// gives nobody anything to search for.
 63func TestTFallsBackRatherThanBlanking(t *testing.T) {
 64	saved := catalogues
 65	defer func() { catalogues = saved }()
 66
 67	catalogues = map[string]map[string]string{
 68		"en": {"nav.home": "HOME", "nav.about": "About"},
 69		"xx": {"nav.home": "INICIO"},
 70	}
 71
 72	if got := T("xx", "nav.home"); got != "INICIO" {
 73		t.Errorf("translated key = %q, want INICIO", got)
 74	}
 75	if got := T("xx", "nav.about"); got != "About" {
 76		t.Errorf("untranslated key = %q, want the English fallback", got)
 77	}
 78	if got := T("xx", "nav.missing"); got != "nav.missing" {
 79		t.Errorf("unknown key = %q, want the key itself", got)
 80	}
 81	if got := T("zz", "nav.home"); got != "HOME" {
 82		t.Errorf("unknown language = %q, want the English fallback", got)
 83	}
 84}
 85
 86// TestResolveLangReadsAcceptLanguage covers the header parsing: quality values,
 87// regional tags falling back to their base, and an instance that has pinned one.
 88func TestResolveLangReadsAcceptLanguage(t *testing.T) {
 89	saved, savedCfg := catalogues, CFG.Language
 90	defer func() { catalogues, CFG.Language = saved, savedCfg }()
 91
 92	catalogues = map[string]map[string]string{"en": {}, "xx": {}}
 93	CFG.Language = "auto"
 94
 95	for _, tc := range []struct{ header, want string }{
 96		{"xx", "xx"},
 97		{"xx-XX,xx;q=0.9", "xx"},
 98		{"fr-FR,fr;q=0.9,en;q=0.8", "en"},
 99		{"", "en"},
100		{"zz", "en"},
101	} {
102		if got := ResolveLang(tc.header); got != tc.want {
103			t.Errorf("ResolveLang(%q) = %q, want %q", tc.header, got, tc.want)
104		}
105	}
106
107	// A pinned language ignores the header entirely.
108	CFG.Language = "xx"
109	if got := ResolveLang("fr-FR"); got != "xx" {
110		t.Errorf("pinned language = %q, want xx", got)
111	}
112
113	// Pinned to something that did not load: English rather than nothing.
114	CFG.Language = "nope"
115	if got := ResolveLang("xx"); got != DefaultLang {
116		t.Errorf("pinned-but-missing = %q, want %q", got, DefaultLang)
117	}
118}