1package i18n
2
3import (
4 "fmt"
5 "sync"
6)
7
8var (
9 globalManager *Manager
10 managerOnce sync.Once
11)
12
13// Manager is the global translation manager.
14type Manager struct {
15 bundle *Bundle
16 currentLang string
17 localizers map[string]*Localizer
18 formatters map[string]*NumberFormatter
19 cache *Cache
20 mu sync.RWMutex
21}
22
23// Init initializes the global translation manager with a default language.
24func Init(defaultLang string) error {
25 var initErr error
26
27 managerOnce.Do(func() {
28 bundle := NewBundle(defaultLang)
29
30 // Load all embedded translations
31 if err := LoadTranslations(bundle); err != nil {
32 initErr = err
33 return
34 }
35
36 // Register locales from registry into bundle
37 for _, locale := range AvailableLanguages() {
38 bundle.RegisterLocale(locale)
39 }
40
41 globalManager = &Manager{
42 bundle: bundle,
43 currentLang: defaultLang,
44 localizers: make(map[string]*Localizer),
45 formatters: make(map[string]*NumberFormatter),
46 cache: NewCache(),
47 }
48
49 // Create default localizer
50 globalManager.localizers[defaultLang] = NewLocalizer(defaultLang, bundle)
51 globalManager.formatters[defaultLang] = NewNumberFormatter(globalManager.localizers[defaultLang].Locale())
52 })
53
54 return initErr
55}
56
57// GetManager returns the global manager instance.
58func GetManager() *Manager {
59 if globalManager == nil {
60 // Auto-initialize with English if not yet initialized
61 _ = Init("en")
62 }
63 return globalManager
64}
65
66// SetLanguage changes the current language.
67func (m *Manager) SetLanguage(lang string) error {
68 if lang == "" {
69 return ErrInvalidLocale
70 }
71
72 lang = normalizeLanguageCode(lang)
73
74 m.mu.Lock()
75 defer m.mu.Unlock()
76
77 // Check if language is available
78 if !m.bundle.HasLanguage(lang) {
79 return fmt.Errorf("%w: %s", ErrLanguageNotFound, lang)
80 }
81
82 // Create localizer if not exists
83 if _, ok := m.localizers[lang]; !ok {
84 m.localizers[lang] = NewLocalizer(lang, m.bundle)
85 }
86
87 // Create formatter if not exists
88 if _, ok := m.formatters[lang]; !ok {
89 m.formatters[lang] = NewNumberFormatter(m.getLocaleLocked(lang))
90 }
91
92 m.currentLang = lang
93 m.cache.Clear() // Clear cache when switching languages
94
95 return nil
96}
97
98// GetLanguage returns the current language code.
99func (m *Manager) GetLanguage() string {
100 m.mu.RLock()
101 defer m.mu.RUnlock()
102
103 return m.currentLang
104}
105
106// T translates a message key using the current language.
107func (m *Manager) T(key string) string {
108 m.mu.RLock()
109 localizer := m.localizers[m.currentLang]
110 m.mu.RUnlock()
111
112 if localizer == nil {
113 return key
114 }
115
116 return localizer.Localize(key)
117}
118
119// Tn translates a message with plural support.
120func (m *Manager) Tn(key string, count int, data map[string]interface{}) string {
121 m.mu.RLock()
122 localizer := m.localizers[m.currentLang]
123 m.mu.RUnlock()
124
125 if localizer == nil {
126 return key
127 }
128
129 // Ensure count is in data
130 if data == nil {
131 data = make(map[string]interface{})
132 }
133 if _, ok := data["count"]; !ok {
134 data["count"] = count
135 }
136
137 return localizer.LocalizePlural(key, count, data)
138}
139
140// Tpl translates a message and applies template variables.
141func (m *Manager) Tpl(key string, data map[string]interface{}) string {
142 m.mu.RLock()
143 localizer := m.localizers[m.currentLang]
144 m.mu.RUnlock()
145
146 if localizer == nil {
147 return key
148 }
149
150 return localizer.LocalizeTemplate(key, data)
151}
152
153// AvailableLanguages returns all loaded languages.
154func (m *Manager) AvailableLanguages() []string {
155 return m.bundle.AvailableLanguages()
156}
157
158// GetLocale returns the current locale.
159func (m *Manager) GetLocale() *Locale {
160 m.mu.RLock()
161 defer m.mu.RUnlock()
162
163 return m.getLocaleLocked(m.currentLang)
164}
165
166// GetNumberFormatter returns the number formatter for the current language.
167func (m *Manager) GetNumberFormatter() *NumberFormatter {
168 m.mu.Lock()
169 defer m.mu.Unlock()
170
171 if formatter, ok := m.formatters[m.currentLang]; ok {
172 return formatter
173 }
174
175 formatter := NewNumberFormatter(m.getLocaleLocked(m.currentLang))
176 m.formatters[m.currentLang] = formatter
177 return formatter
178}
179
180// ClearCache clears all translation caches.
181func (m *Manager) ClearCache() {
182 m.cache.Clear()
183
184 m.mu.Lock()
185 defer m.mu.Unlock()
186
187 for _, localizer := range m.localizers {
188 localizer.ClearCache()
189 }
190}
191
192func (m *Manager) getLocaleLocked(lang string) *Locale {
193 if localizer, ok := m.localizers[lang]; ok {
194 return localizer.Locale()
195 }
196
197 locale, _ := ParseLocale(lang)
198 return locale
199}