1package i18n
2
3import (
4 "fmt"
5 "sync"
6)
7
8// Bundle holds all translation messages for all languages.
9type Bundle struct {
10 defaultLang string
11 messages map[string]MessageMap // lang -> MessageMap
12 locales map[string]*Locale // lang -> Locale
13 mu sync.RWMutex
14}
15
16// NewBundle creates a new Bundle with a default language.
17func NewBundle(defaultLang string) *Bundle {
18 return &Bundle{
19 defaultLang: defaultLang,
20 messages: make(map[string]MessageMap),
21 locales: make(map[string]*Locale),
22 }
23}
24
25// AddMessages adds translation messages for a language.
26func (b *Bundle) AddMessages(lang string, messages MessageMap) error {
27 if lang == "" {
28 return ErrInvalidLocale
29 }
30
31 b.mu.Lock()
32 defer b.mu.Unlock()
33
34 if b.messages[lang] == nil {
35 b.messages[lang] = make(MessageMap)
36 }
37
38 // Merge messages
39 for id, msg := range messages {
40 b.messages[lang][id] = msg
41 }
42
43 return nil
44}
45
46// GetMessage retrieves a message for a specific language and ID.
47func (b *Bundle) GetMessage(lang, id string) (*Message, error) {
48 b.mu.RLock()
49 defer b.mu.RUnlock()
50
51 langMessages, ok := b.messages[lang]
52 if !ok {
53 return nil, fmt.Errorf("%w: %s", ErrLanguageNotFound, lang)
54 }
55
56 msg, ok := langMessages[id]
57 if !ok {
58 return nil, fmt.Errorf("%w: %s", ErrMessageNotFound, id)
59 }
60
61 return msg, nil
62}
63
64// RegisterLocale registers a locale configuration.
65func (b *Bundle) RegisterLocale(locale *Locale) {
66 if locale == nil || locale.Code == "" {
67 return
68 }
69
70 b.mu.Lock()
71 defer b.mu.Unlock()
72
73 b.locales[locale.Code] = locale
74}
75
76// GetLocale retrieves a registered locale.
77func (b *Bundle) GetLocale(lang string) (*Locale, bool) {
78 b.mu.RLock()
79 defer b.mu.RUnlock()
80
81 locale, ok := b.locales[lang]
82 return locale, ok
83}
84
85// AvailableLanguages returns a list of all languages with loaded messages.
86func (b *Bundle) AvailableLanguages() []string {
87 b.mu.RLock()
88 defer b.mu.RUnlock()
89
90 langs := make([]string, 0, len(b.messages))
91 for lang := range b.messages {
92 langs = append(langs, lang)
93 }
94 return langs
95}
96
97// DefaultLanguage returns the default language code.
98func (b *Bundle) DefaultLanguage() string {
99 return b.defaultLang
100}
101
102// MessageCount returns the number of messages for a language.
103func (b *Bundle) MessageCount(lang string) int {
104 b.mu.RLock()
105 defer b.mu.RUnlock()
106
107 if messages, ok := b.messages[lang]; ok {
108 return len(messages)
109 }
110 return 0
111}
112
113// HasLanguage checks if a language has been loaded.
114func (b *Bundle) HasLanguage(lang string) bool {
115 b.mu.RLock()
116 defer b.mu.RUnlock()
117
118 _, ok := b.messages[lang]
119 return ok
120}