package i18n import ( "encoding/json" "fmt" "io/fs" "sync" ) var ( mu sync.RWMutex messages map[string]map[string]string fallback string ) func Init(fsys fs.FS, defaultLang string) error { mu.Lock() defer mu.Unlock() messages = make(map[string]map[string]string) fallback = defaultLang entries, err := fs.ReadDir(fsys, ".") if err != nil { return fmt.Errorf("i18n: read messages dir: %w", err) } for _, e := range entries { if e.IsDir() { continue } data, err := fs.ReadFile(fsys, e.Name()) if err != nil { return fmt.Errorf("i18n: read %s: %w", e.Name(), err) } lang := langFromFilename(e.Name()) kv := make(map[string]string) if err := json.Unmarshal(data, &kv); err != nil { return fmt.Errorf("i18n: parse %s: %w", e.Name(), err) } messages[lang] = kv } return nil } func T(lang, key string) string { mu.RLock() defer mu.RUnlock() if msgs, ok := messages[lang]; ok { if v, ok := msgs[key]; ok { return v } } if msgs, ok := messages[fallback]; ok { if v, ok := msgs[key]; ok { return v } } return key } func SupportedLanguages() []string { mu.RLock() defer mu.RUnlock() out := make([]string, 0, len(messages)) for k := range messages { out = append(out, k) } return out } func langFromFilename(name string) string { for i, c := range name { if c == '.' { return name[:i] } } return name }