Nexus/pkg/nex/localize/bundle.go
2025-02-02 13:47:14 +08:00

140 lines
3.3 KiB
Go

package localize
import (
"errors"
"fmt"
"github.com/goccy/go-json"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/rs/zerolog/log"
"golang.org/x/text/language"
htmpl "html/template"
"os"
"path/filepath"
"strings"
"text/template"
)
type Bundle struct {
Bundle *i18n.Bundle
LocalesPath string
TemplatesPath string
}
const FallbackLanguage = "en-US"
var L *Bundle
func LoadLocalization(localesPath string, templatesPath ...string) error {
L = &Bundle{
LocalesPath: localesPath,
}
if len(templatesPath) > 0 {
L.TemplatesPath = templatesPath[0]
}
L.Bundle = i18n.NewBundle(language.AmericanEnglish)
L.Bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
var count int
basePath := localesPath
if entries, err := os.ReadDir(basePath); err != nil {
return fmt.Errorf("unable to read locales directory: %v", err)
} else {
for _, entry := range entries {
if entry.IsDir() {
continue
}
if _, err := L.Bundle.LoadMessageFile(filepath.Join(basePath, entry.Name())); err != nil {
return fmt.Errorf("unable to load localization file %s: %v", entry.Name(), err)
} else {
count++
}
}
}
log.Info().Int("locales", count).Msg("Loaded localization files...")
return nil
}
func (v *Bundle) GetLocalizer(lang string) *i18n.Localizer {
return i18n.NewLocalizer(v.Bundle, lang)
}
func (v *Bundle) GetLocalizedString(name string, lang string) string {
localizer := v.GetLocalizer(lang)
msg, err := localizer.LocalizeMessage(&i18n.Message{
ID: name,
})
if err != nil {
log.Warn().Err(err).Str("lang", lang).Str("name", name).Msg("Failed to localize string...")
return name
}
return msg
}
func (v *Bundle) GetLocalizedTemplatePath(name string, lang string) string {
basePath := v.TemplatesPath
filePath := filepath.Join(basePath, lang, name)
if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
// Fallback to English
filePath = filepath.Join(basePath, FallbackLanguage, name)
return filePath
}
return filePath
}
func (v *Bundle) GetLocalizedTemplate(name string, lang string) *template.Template {
path := v.GetLocalizedTemplatePath(name, lang)
tmpl, err := template.ParseFiles(path)
if err != nil {
log.Warn().Err(err).Str("lang", lang).Str("name", name).Msg("Failed to load localized template...")
return nil
}
return tmpl
}
func (v *Bundle) GetLocalizedTemplateHTML(name string, lang string) *htmpl.Template {
path := v.GetLocalizedTemplatePath(name, lang)
tmpl, err := htmpl.ParseFiles(path)
if err != nil {
log.Warn().Err(err).Str("lang", lang).Str("name", name).Msg("Failed to load localized template...")
return nil
}
return tmpl
}
func (v *Bundle) RenderLocalizedTemplateHTML(name string, lang string, data any) string {
tmpl := v.GetLocalizedTemplate(name, lang)
if tmpl == nil {
return ""
}
buf := new(strings.Builder)
err := tmpl.Execute(buf, data)
if err != nil {
log.Warn().Err(err).Str("lang", lang).Str("name", name).Msg("Failed to render localized template...")
return ""
}
return buf.String()
}
func (v *Bundle) RenderLocalizedTemplate(name string, lang string, data any) string {
tmpl := v.GetLocalizedTemplate(name, lang)
if tmpl == nil {
return ""
}
buf := new(strings.Builder)
err := tmpl.Execute(buf, data)
if err != nil {
log.Warn().Err(err).Str("lang", lang).Str("name", name).Msg("Failed to render localized template...")
return ""
}
return buf.String()
}