manager.go

  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}