1package web
2
3import (
4 "errors"
5 "fmt"
6 "html/template"
7 "strings"
8
9 "tug/internal/transport/web/text"
10)
11
12type Ident string
13
14const identMarker = "{ident}"
15
16var errNoMarker = errors.New("catalog sentence has no identifier marker")
17
18func identHTML(v Ident) template.HTML {
19 return template.HTML(`<code class="ident">` + template.HTMLEscapeString(string(v)) + `</code>`)
20}
21
22func interpolate(sentence string, v Ident) (template.HTML, error) {
23 before, after, ok := strings.Cut(sentence, identMarker)
24 if !ok || strings.Contains(after, identMarker) {
25 return "", errNoMarker
26 }
27 return template.HTML(template.HTMLEscapeString(before)) + identHTML(v) + template.HTML(template.HTMLEscapeString(after)), nil
28}
29
30func lookup(k text.Key) (string, error) {
31 s, ok := text.Lookup(k)
32 if !ok {
33 return "", fmt.Errorf("unknown catalog key %q", k)
34 }
35 return s, nil
36}
37
38func translate(k text.Key, v Ident) (template.HTML, error) {
39 s, err := lookup(k)
40 if err != nil {
41 return "", err
42 }
43 out, err := interpolate(s, v)
44 if err != nil {
45 return "", fmt.Errorf("%w: %q", err, k)
46 }
47 return out, nil
48}
49
50func translateText(k text.Key, v Ident) (string, error) {
51 s, err := lookup(k)
52 if err != nil {
53 return "", err
54 }
55 before, after, ok := strings.Cut(s, identMarker)
56 if !ok || strings.Contains(after, identMarker) {
57 return "", fmt.Errorf("%w: %q", errNoMarker, k)
58 }
59 return before + string(v) + after, nil
60}
61
62func funcs() template.FuncMap {
63 return template.FuncMap{
64 "t": lookup,
65 "tr": translate,
66 "trt": translateText,
67 "id": identHTML,
68 }
69}