krz/skunky-art
Alternative privacy frontend for DeviantArt.
clone: git clone https://gitbay.org/krz/skunky-art.git
v1.4.0: 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}