template.go

 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}