Files
DockerBot/internal/i18n/i18n.go
2026-04-13 21:42:04 +02:00

161 lines
2.9 KiB
Go

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, ".")
}