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