package i18n import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "sync" ) // I18n es la estructura principal type I18n struct { current string fallback string locales map[string]map[string]string mu sync.RWMutex // protege acceso concurrente } // New crea una instancia de i18n /* func New(defaultLocale, fallback string) *I18n { return &I18n{ current: defaultLocale, fallback: fallback, locales: make(map[string]map[string]string), } } */ func New(path string, fallback string) (*I18n, error) { i := &I18n{ current: fallback, fallback: fallback, locales: make(map[string]map[string]string), } if err := i.loadDir(path); err != nil { return nil, err } if len(i.locales) == 0 { return nil, fmt.Errorf("no locales loaded from %s", path) } return i, nil } func (i *I18n) loadDir(path string) error { files, err := os.ReadDir(path) if err != nil { return err } for _, file := range files { if file.IsDir() { continue } name := file.Name() if !strings.HasSuffix(name, ".json") { continue } lang := strings.TrimSuffix(name, ".json") if err := i.loadFile(filepath.Join(path, name), lang); err != nil { return err } } return nil } // LoadLocale carga un archivo JSON en memoria func (i *I18n) LoadLocale(locale, path string) error { data, err := os.ReadFile(path) if err != nil { return err } var parsed map[string]string if err := json.Unmarshal(data, &parsed); err != nil { return err } i.mu.Lock() i.locales[locale] = parsed i.mu.Unlock() return nil } func (i *I18n) loadFile(path string, lang string) error { data, err := os.ReadFile(path) if err != nil { return err } var content map[string]string if err := json.Unmarshal(data, &content); err != nil { return err } i.mu.Lock() defer i.mu.Unlock() i.locales[lang] = content return nil } // SetLocale cambia el idioma activo func (i *I18n) SetLocale(locale string) { i.mu.Lock() i.current = locale i.mu.Unlock() } // T traduce una clave, opcionalmente interpolando variables func (i *I18n) T(key string, vars map[string]string) string { val := i.translate(key) if vars == nil { return val } // Interpolación simple: reemplaza {var} por su valor for k, v := range vars { val = strings.ReplaceAll(val, "{"+k+"}", v) } return val } // translate busca la clave en el idioma actual y fallback func (i *I18n) translate(key string) string { i.mu.RLock() defer i.mu.RUnlock() // 1. Buscar en idioma actual if localeData, ok := i.locales[i.current]; ok { if val, ok := localeData[key]; ok { return val } } // 2. Buscar en fallback if localeData, ok := i.locales[i.fallback]; ok { if val, ok := localeData[key]; ok { return val } } // 3. Si no existe → devolver clave marcada return fmt.Sprintf("??%s??", key) } // Helper para construir claves con seguridad func Key(parts ...string) string { return strings.Join(parts, ".") }