Commit avant changement d'agent vers devstral
This commit is contained in:
139
internal/i18n/i18n.go
Normal file
139
internal/i18n/i18n.go
Normal file
@ -0,0 +1,139 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Translator manages translations for multiple languages
|
||||
type Translator struct {
|
||||
translations map[string]map[string]interface{}
|
||||
mu sync.RWMutex
|
||||
defaultLang string
|
||||
}
|
||||
|
||||
// New creates a new Translator with the specified default language
|
||||
func New(defaultLang string) *Translator {
|
||||
t := &Translator{
|
||||
translations: make(map[string]map[string]interface{}),
|
||||
defaultLang: defaultLang,
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// LoadFromDir loads all translation files from a directory
|
||||
func (t *Translator) LoadFromDir(dir string) error {
|
||||
files, err := filepath.Glob(filepath.Join(dir, "*.json"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list translation files: %w", err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
lang := strings.TrimSuffix(filepath.Base(file), ".json")
|
||||
if err := t.LoadLanguage(lang, file); err != nil {
|
||||
return fmt.Errorf("failed to load language %s: %w", lang, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLanguage loads translations for a specific language from a JSON file
|
||||
func (t *Translator) LoadLanguage(lang, filePath string) error {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read translation file: %w", err)
|
||||
}
|
||||
|
||||
var translations map[string]interface{}
|
||||
if err := json.Unmarshal(data, &translations); err != nil {
|
||||
return fmt.Errorf("failed to parse translation file: %w", err)
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
t.translations[lang] = translations
|
||||
t.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// T translates a key for the given language with optional arguments
|
||||
// Key format: "section.subsection.key" (e.g., "menu.home")
|
||||
// Arguments can be passed as a map for variable interpolation
|
||||
func (t *Translator) T(lang, key string, args ...map[string]string) string {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
// Try to get translation for specified language
|
||||
translation := t.getTranslation(lang, key)
|
||||
|
||||
// Fallback to default language if not found
|
||||
if translation == "" && lang != t.defaultLang {
|
||||
translation = t.getTranslation(t.defaultLang, key)
|
||||
}
|
||||
|
||||
// Return key if no translation found
|
||||
if translation == "" {
|
||||
return key
|
||||
}
|
||||
|
||||
// Interpolate variables if args provided
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
for k, v := range args[0] {
|
||||
placeholder := fmt.Sprintf("{{%s}}", k)
|
||||
translation = strings.ReplaceAll(translation, placeholder, v)
|
||||
}
|
||||
}
|
||||
|
||||
return translation
|
||||
}
|
||||
|
||||
// getTranslation retrieves a translation by key using dot notation
|
||||
func (t *Translator) getTranslation(lang, key string) string {
|
||||
langTranslations, ok := t.translations[lang]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := strings.Split(key, ".")
|
||||
var current interface{} = langTranslations
|
||||
|
||||
for _, part := range parts {
|
||||
if m, ok := current.(map[string]interface{}); ok {
|
||||
current = m[part]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
if str, ok := current.(string); ok {
|
||||
return str
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetAvailableLanguages returns a list of loaded languages
|
||||
func (t *Translator) GetAvailableLanguages() []string {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
langs := make([]string, 0, len(t.translations))
|
||||
for lang := range t.translations {
|
||||
langs = append(langs, lang)
|
||||
}
|
||||
return langs
|
||||
}
|
||||
|
||||
// GetTranslations returns all translations for a specific language
|
||||
func (t *Translator) GetTranslations(lang string) (map[string]interface{}, bool) {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
translations, ok := t.translations[lang]
|
||||
return translations, ok
|
||||
}
|
||||
Reference in New Issue
Block a user