chore: migrate mail themes to shared framework and locked upstream inputs
This commit is contained in:
1 parent
5c0589f6f6
commit
fec3ace600
225 files changed
+4718
-15807
No files matched your search
@@ -0,0 +1,148 @@
|
||||
package upstream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/template"
|
||||
"text/template/parse"
|
||||
)
|
||||
|
||||
// ReferencedKeys walks Go's template AST, including nested pipelines and both
|
||||
// TrN keys. Dynamic translation keys require an explicit adapter review.
|
||||
func ReferencedKeys(s *Snapshot) ([]string, error) {
|
||||
fns := template.FuncMap{}
|
||||
for _, name := range []string{"AppUrl", "AppName", "AppDomain", "DotEscape", "QueryEscape", "ShortSha", "HTMLFormat", "PathEscapeSegments", "FormatByteSize", "Dict", "dict", "StringUtils", "SliceUtils", "JsonUtils", "QueryBuild", "PathEscape", "SanitizeHTML"} {
|
||||
fns[name] = func(...any) string { return "" }
|
||||
}
|
||||
keys := map[string]bool{}
|
||||
var walk func(parse.Node) error
|
||||
walk = func(n parse.Node) error {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
switch n := n.(type) {
|
||||
case *parse.ListNode:
|
||||
if n != nil {
|
||||
for _, child := range n.Nodes {
|
||||
if err := walk(child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case *parse.ActionNode:
|
||||
return walk(n.Pipe)
|
||||
case *parse.PipeNode:
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
for _, cmd := range n.Cmds {
|
||||
if err := walk(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *parse.CommandNode:
|
||||
if len(n.Args) > 0 {
|
||||
var ident []string
|
||||
switch f := n.Args[0].(type) {
|
||||
case *parse.FieldNode:
|
||||
ident = f.Ident
|
||||
case *parse.VariableNode:
|
||||
ident = f.Ident
|
||||
}
|
||||
if len(ident) >= 2 && ident[len(ident)-2] == "locale" {
|
||||
var positions []int
|
||||
switch ident[len(ident)-1] {
|
||||
case "Tr", "TrString":
|
||||
positions = []int{1}
|
||||
case "TrN":
|
||||
positions = []int{2, 3}
|
||||
}
|
||||
for _, i := range positions {
|
||||
if i >= len(n.Args) {
|
||||
return fmt.Errorf("invalid translation call: %s", n)
|
||||
}
|
||||
key, ok := n.Args[i].(*parse.StringNode)
|
||||
if !ok {
|
||||
return fmt.Errorf("dynamic translation key needs review: %s", n)
|
||||
}
|
||||
keys[key.Text] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, arg := range n.Args {
|
||||
if err := walk(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *parse.IfNode:
|
||||
for _, child := range []parse.Node{n.Pipe, n.List, n.ElseList} {
|
||||
if err := walk(child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *parse.RangeNode:
|
||||
for _, child := range []parse.Node{n.Pipe, n.List, n.ElseList} {
|
||||
if err := walk(child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *parse.WithNode:
|
||||
for _, child := range []parse.Node{n.Pipe, n.List, n.ElseList} {
|
||||
if err := walk(child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *parse.TemplateNode:
|
||||
return walk(n.Pipe)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for p, data := range s.Templates {
|
||||
t, err := template.New(p).Funcs(fns).Parse(string(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", p, err)
|
||||
}
|
||||
for _, t := range t.Templates() {
|
||||
if t.Tree != nil {
|
||||
if err := walk(t.Tree.Root); err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var result []string
|
||||
for k := range keys {
|
||||
result = append(result, k)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ValidateKeys(s *Snapshot) error {
|
||||
keys, err := ReferencedKeys(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, k := range keys {
|
||||
if s.Locales["en-US"][k] == "" {
|
||||
return fmt.Errorf("official English catalog is missing referenced key %q", k)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MissingKeys(s *Snapshot) (map[string][]string, error) {
|
||||
keys, err := ReferencedKeys(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
missing := map[string][]string{}
|
||||
for lang, catalog := range s.Locales {
|
||||
for _, k := range keys {
|
||||
if catalog[k] == "" {
|
||||
missing[lang] = append(missing[lang], k)
|
||||
}
|
||||
}
|
||||
}
|
||||
return missing, nil
|
||||
}
|
||||
Reference in new issue
Block a user