end_of_eden/system/localization/localization.go
2023-12-23 13:35:44 +01:00

145 lines
3.3 KiB
Go

package localization
import (
"fmt"
"github.com/BigJk/end_of_eden/internal/fs"
"gopkg.in/yaml.v3"
"path/filepath"
"strings"
)
type Localization struct {
current string
locals map[string]map[string]string
}
func New() *Localization {
loc := &Localization{current: "en", locals: make(map[string]map[string]string)}
loc.locals["en"] = make(map[string]string)
return loc
}
// SetCurrent sets the current locale.
func (l *Localization) SetCurrent(locale string) {
l.current = locale
}
// GetCurrent returns the current locale.
func (l *Localization) GetCurrent() string {
return l.current
}
// Add adds a new locale with the given translations. If the locale
// already exists, the translations will be added to the existing ones.
func (l *Localization) Add(locale string, translations map[string]string) {
if _, ok := l.locals[locale]; !ok {
l.locals[locale] = make(map[string]string)
}
for k, v := range translations {
l.locals[locale][k] = v
}
}
// AddFolder adds all locales from the given folder. Will walk through all
// sub-folders and add all .yaml and .yml files.
func (l *Localization) AddFolder(folder string) error {
return fs.Walk(folder, func(path string, isDir bool) error {
if isDir {
return nil
}
if filepath.Ext(path) != ".yaml" && filepath.Ext(path) != ".yml" {
return nil
}
return l.AddFile(path)
})
}
// AddFile adds a new locale with the given translations from the given file.
func (l *Localization) AddFile(file string) error {
var parsed map[string]map[string]any
data, err := fs.ReadFile(file)
if err != nil {
return err
}
err = yaml.Unmarshal(data, &parsed)
if err != nil {
return err
}
var walk func(found map[string]string, m map[string]any, prefix string)
walk = func(found map[string]string, m map[string]any, prefix string) {
for k, v := range m {
if m, ok := v.(map[string]any); ok {
if prefix != "" {
walk(found, m, prefix+"."+k)
} else {
walk(found, m, k)
}
} else {
if prefix != "" {
found[prefix+"."+k] = fmt.Sprint(v)
} else {
found[k] = fmt.Sprint(v)
}
}
}
}
for local, v := range parsed {
if len(local) != 2 {
return fmt.Errorf("invalid locale: %s", local)
}
found := make(map[string]string)
walk(found, v, "")
l.Add(local, found)
}
return nil
}
// GetLocales returns a list of all available locales.
func (l *Localization) GetLocales() []string {
var locales []string
for k := range l.locals {
locales = append(locales, k)
}
return locales
}
// Get returns the translation for the given key in the given locale. If the key is not found,
// it will fall back to the "en" locale and if the key is not found there, it will return the key
// or the given defaults.
func (l *Localization) Get(locale, key string, defaults ...string) string {
if _, ok := l.locals[locale]; !ok {
if locale != "en" {
return l.Get("en", key, defaults...)
}
if len(defaults) > 0 {
return strings.Join(defaults, " ")
}
return key
}
if _, ok := l.locals[locale][key]; !ok {
if locale != "en" {
return l.Get("en", key, defaults...)
}
if len(defaults) > 0 {
return strings.Join(defaults, " ")
}
return key
}
return l.locals[locale][key]
}
// G is a shortcut for Get with the current locale.
func (l *Localization) G(key string, defaults ...string) string {
return l.Get(l.current, key, defaults...)
}