package i18n import ( "encoding/json" "fmt" "io/ioutil" "path/filepath" "sync" ) // Translator handles translation loading and retrieval. type Translator struct { mu sync.RWMutex messages map[string]map[string]string // lang -> key -> message } // NewTranslator loads JSON locale files en.json and fa.json from the i18n directory. // defaultLang sets the default fallback language (e.g., "en"). func NewTranslator(defaultLang string) (*Translator, error) { if defaultLang == "" { defaultLang = "en" } t := &Translator{ messages: make(map[string]map[string]string), } // Load known locale files: en.json and fa.json located in the same package directory. for _, lang := range []string{"en", "fa"} { filePath := filepath.Join("i18n", lang+".json") data, err := ioutil.ReadFile(filePath) if err != nil { return nil, err } var m map[string]string if err := json.Unmarshal(data, &m); err != nil { return nil, err } t.messages[lang] = m } // Ensure default language exists. if _, ok := t.messages[defaultLang]; !ok { return nil, fmt.Errorf("default language %s not found", defaultLang) } return t, nil } // Get returns the translated string for a given key and language. // Falls back to English if the key/language is missing. func (t *Translator) Get(key, lang string) string { if lang == "" { lang = "en" } t.mu.RLock() defer t.mu.RUnlock() if msgs, ok := t.messages[lang]; ok { if v, ok := msgs[key]; ok { return v } } // fallback to English messages if msgs, ok := t.messages["en"]; ok { if v, ok := msgs[key]; ok { return v } } // If not found, return the key itself. return key }