package discordi18n import ( "errors" "fmt" "gitea.dwysokinski.me/twhelp/dcbot/internal/domain" "github.com/bwmarrin/discordgo" lru "github.com/hashicorp/golang-lru/v2" "github.com/nicksnyder/go-i18n/v2/i18n" "golang.org/x/text/language" ) const localizerCacheSize = 128 type Localizer struct { bundle *i18n.Bundle cache *lru.Cache[string, *i18n.Localizer] defaultLanguage language.Tag } func NewLocalizer(defaultLanguage language.Tag) (*Localizer, error) { b, err := newBundle(defaultLanguage) if err != nil { return nil, err } l, err := lru.New[string, *i18n.Localizer](localizerCacheSize) if err != nil { return nil, err } return &Localizer{ bundle: b, cache: l, }, nil } func (l *Localizer) Localize(lang string, lc *i18n.LocalizeConfig) (string, error) { return l.newLocalizer(lang).Localize(lc) } func (l *Localizer) MustLocalize(lang string, lc *i18n.LocalizeConfig) string { return l.newLocalizer(lang).MustLocalize(lc) } func (l *Localizer) LocalizeWithTag(lang string, lc *i18n.LocalizeConfig) (string, language.Tag, error) { return l.newLocalizer(lang).LocalizeWithTag(lc) } func (l *Localizer) newLocalizer(lang string) *i18n.Localizer { if loc, ok := l.cache.Get(lang); ok { return loc } loc := i18n.NewLocalizer(l.bundle, lang, l.defaultLanguage.String()) _ = l.cache.Add(lang, loc) return loc } func (l *Localizer) LocalizeDiscord(messageID string) (string, map[discordgo.Locale]string, error) { return l.LocalizeDiscordWithConfig(&i18n.LocalizeConfig{ MessageID: messageID, }) } func (l *Localizer) LocalizeDiscordWithConfig(lc *i18n.LocalizeConfig) (string, map[discordgo.Locale]string, error) { m := make(map[discordgo.Locale]string) for _, tag := range l.bundle.LanguageTags() { locale := discordgo.Locale(tag.String()) if discordgo.Locales[locale] == "" { continue } msg, msgLang, err := l.LocalizeWithTag(tag.String(), lc) if err != nil { return "", nil, fmt.Errorf("couldn't lookup message in locale '%s': %w", locale, err) } if msgLang == l.defaultLanguage { continue } m[locale] = msg } defaultMsg, err := l.Localize(l.defaultLanguage.String(), lc) if err != nil { return "", nil, fmt.Errorf("couldn't lookup message in default language: %w", err) } return defaultMsg, m, nil } func (l *Localizer) LocalizeError(lang string, errToLocalize error) string { defaultErr, err := l.Localize(lang, &i18n.LocalizeConfig{MessageID: "err.default"}) if err != nil { defaultErr = "Something went wrong. Try again later." } var translatableErr domain.TranslatableError if !errors.As(errToLocalize, &translatableErr) { return defaultErr } msgID := "err." + translatableErr.Slug() msg, err := l.Localize(lang, &i18n.LocalizeConfig{ MessageID: msgID, TemplateData: translatableErr.Params(), }) if err != nil { return defaultErr } return msg } func (l *Localizer) LanguageTags() []language.Tag { return l.bundle.LanguageTags() }