1package i18n
2
3import "strings"
4
5// Template represents a parsed template string with placeholders.
6type Template struct {
7 raw string
8 parts []templatePart
9}
10
11type templatePart struct {
12 isVar bool
13 value string
14}
15
16// NewTemplate parses a template string and returns a Template.
17func NewTemplate(s string) *Template {
18 t := &Template{
19 raw: s,
20 parts: parseTemplate(s),
21 }
22 return t
23}
24
25// Execute applies data to the template and returns the result.
26func (t *Template) Execute(data map[string]interface{}) string {
27 if len(t.parts) == 0 {
28 return t.raw
29 }
30
31 var result strings.Builder
32 for _, part := range t.parts {
33 if part.isVar {
34 if val, ok := data[part.value]; ok {
35 result.WriteString(formatValue(val))
36 } else {
37 // Keep placeholder if no value provided
38 result.WriteString("{")
39 result.WriteString(part.value)
40 result.WriteString("}")
41 }
42 } else {
43 result.WriteString(part.value)
44 }
45 }
46 return result.String()
47}
48
49// parseTemplate breaks a template string into parts (literal text and variables).
50func parseTemplate(s string) []templatePart {
51 var parts []templatePart
52 var current strings.Builder
53 inVar := false
54 var varName strings.Builder
55
56 for i := 0; i < len(s); i++ {
57 ch := s[i]
58
59 if ch == '{' && !inVar { //nolint:gocritic
60 // Start of variable
61 if current.Len() > 0 {
62 parts = append(parts, templatePart{isVar: false, value: current.String()})
63 current.Reset()
64 }
65 inVar = true
66 varName.Reset()
67 } else if ch == '}' && inVar {
68 // End of variable
69 if varName.Len() > 0 {
70 parts = append(parts, templatePart{isVar: true, value: varName.String()})
71 }
72 inVar = false
73 } else if inVar {
74 varName.WriteByte(ch)
75 } else {
76 current.WriteByte(ch)
77 }
78 }
79
80 // Add remaining text
81 if current.Len() > 0 {
82 parts = append(parts, templatePart{isVar: false, value: current.String()})
83 }
84
85 return parts
86}
87
88// String returns the raw template string.
89func (t *Template) String() string {
90 return t.raw
91}