1package i18n
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8)
9
10// LoadTranslations loads all translation files into a bundle.
11// First attempts to load from embedded files, then checks for external files.
12func LoadTranslations(bundle *Bundle) error {
13 // Load from embedded files
14 if err := loadFromEmbedded(bundle); err != nil {
15 return fmt.Errorf("%w: embedded load failed: %v", ErrLoadFailed, err)
16 }
17
18 return nil
19}
20
21// loadFromEmbedded loads translation files from the embedded filesystem.
22func loadFromEmbedded(bundle *Bundle) error {
23 entries, err := localeFS.ReadDir("locales")
24 if err != nil {
25 return err
26 }
27
28 for _, entry := range entries {
29 if entry.IsDir() {
30 continue
31 }
32
33 filename := entry.Name()
34 if !strings.HasSuffix(filename, ".json") {
35 continue
36 }
37
38 // Read file
39 data, err := localeFS.ReadFile(filepath.Join("locales", filename))
40 if err != nil {
41 continue
42 }
43
44 // Extract language code from filename (e.g., "en.json" -> "en")
45 lang := strings.TrimSuffix(filename, ".json")
46
47 // Load into bundle
48 if err := loadLanguageFile(bundle, lang, data); err != nil {
49 return err
50 }
51 }
52
53 return nil
54}
55
56// LoadFromDirectory loads translation files from a directory on disk.
57// This allows overriding embedded translations with external files.
58func LoadFromDirectory(bundle *Bundle, dir string) error {
59 entries, err := os.ReadDir(dir)
60 if err != nil {
61 return fmt.Errorf("%w: %v", ErrLoadFailed, err)
62 }
63
64 for _, entry := range entries {
65 if entry.IsDir() {
66 continue
67 }
68
69 filename := entry.Name()
70 if !strings.HasSuffix(filename, ".json") {
71 continue
72 }
73
74 // Read file
75 path := filepath.Join(dir, filename)
76 data, err := os.ReadFile(path)
77 if err != nil {
78 continue
79 }
80
81 // Extract language code
82 lang := strings.TrimSuffix(filename, ".json")
83
84 // Load into bundle
85 if err := loadLanguageFile(bundle, lang, data); err != nil {
86 return err
87 }
88 }
89
90 return nil
91}
92
93// loadLanguageFile parses and loads a single language file into the bundle.
94func loadLanguageFile(bundle *Bundle, lang string, data []byte) error {
95 messages, err := ParseJSON(data)
96 if err != nil {
97 return fmt.Errorf("%w: language %s: %v", ErrParseFailed, lang, err)
98 }
99
100 return bundle.AddMessages(lang, messages)
101}