93 lines
1.7 KiB
Go
93 lines
1.7 KiB
Go
package i18n
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
//go:embed locales/*.json
|
|
var localeFS embed.FS
|
|
|
|
var (
|
|
mu sync.RWMutex
|
|
cache = map[string]map[string]string{}
|
|
defaultLocale = "ru"
|
|
)
|
|
|
|
// T returns a localized string for the given locale and key.
|
|
// It supports printf-style formatting via args.
|
|
// Falls back: key not found -> ru -> key itself.
|
|
func T(locale, key string, args ...any) string {
|
|
mu.RLock()
|
|
catalog, ok := cache[locale]
|
|
mu.RUnlock()
|
|
if !ok {
|
|
catalog = loadLocale(locale)
|
|
}
|
|
msg, ok := catalog[key]
|
|
if !ok && locale != defaultLocale {
|
|
mu.RLock()
|
|
ruCatalog, ruOK := cache[defaultLocale]
|
|
mu.RUnlock()
|
|
if ruOK {
|
|
msg = ruCatalog[key]
|
|
}
|
|
if msg == "" {
|
|
msg = key
|
|
}
|
|
}
|
|
if msg == "" {
|
|
msg = key
|
|
}
|
|
if len(args) > 0 {
|
|
return fmt.Sprintf(msg, args...)
|
|
}
|
|
return msg
|
|
}
|
|
|
|
// TF is a shorthand for T with formatting.
|
|
func TF(locale, key string, args ...any) string {
|
|
return T(locale, key, args...)
|
|
}
|
|
|
|
// SetDefault changes the default locale.
|
|
func SetDefault(locale string) {
|
|
mu.Lock()
|
|
defaultLocale = locale
|
|
mu.Unlock()
|
|
}
|
|
|
|
// AvailableLocales returns locale names for which files exist.
|
|
func AvailableLocales() []string {
|
|
entries, err := localeFS.ReadDir("locales")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var out []string
|
|
for _, e := range entries {
|
|
name := e.Name()
|
|
name = strings.TrimSuffix(name, ".json")
|
|
out = append(out, name)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func loadLocale(locale string) map[string]string {
|
|
data, err := localeFS.ReadFile("locales/" + locale + ".json")
|
|
if err != nil {
|
|
data = []byte("{}")
|
|
}
|
|
var m map[string]string
|
|
json.Unmarshal(data, &m)
|
|
if m == nil {
|
|
m = map[string]string{}
|
|
}
|
|
mu.Lock()
|
|
cache[locale] = m
|
|
mu.Unlock()
|
|
return m
|
|
}
|