♻️ Replace i18n services with nexus one

This commit is contained in:
2025-02-02 14:28:03 +08:00
parent ec0048042a
commit eaa8fb5225
10 changed files with 38 additions and 119 deletions

View File

@ -3,6 +3,7 @@ package gap
import (
"errors"
"fmt"
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
"strings"
"time"
@ -81,3 +82,7 @@ func InitializeToNexus() error {
return err
}
func LoadLocalization() error {
return localize.LoadLocalization(viper.GetString("locales_path"), viper.GetString("templates_path"))
}

View File

@ -2,6 +2,7 @@ package services
import (
"fmt"
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
"strings"
"time"
@ -76,8 +77,8 @@ func GetFactorCode(factor models.AuthFactor, ip string) (bool, error) {
err = PushNotification(models.Notification{
Topic: "passport.security.otp",
Title: GetLocalizedString("subjectLoginOneTimePassword", user.Language),
Body: fmt.Sprintf(GetLocalizedString("shortBodyLoginOneTimePassword", user.Language), secret),
Title: localize.L.GetLocalizedString("subjectLoginOneTimePassword", user.Language),
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyLoginOneTimePassword", user.Language), secret),
Account: user,
AccountID: user.ID,
Metadata: map[string]any{"secret": secret},
@ -105,9 +106,9 @@ func GetFactorCode(factor models.AuthFactor, ip string) (bool, error) {
log.Info().Uint("factor", factor.ID).Str("secret", secret).Msg("Published one-time-password to JetStream...")
}
subject := fmt.Sprintf("[%s] %s", viper.GetString("name"), GetLocalizedString("subjectLoginOneTimePassword", user.Language))
subject := fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectLoginOneTimePassword", user.Language))
content := RenderLocalizedTemplateHTML("email-otp.tmpl", user.Language, map[string]any{
content := localize.L.RenderLocalizedTemplateHTML("email-otp.tmpl", user.Language, map[string]any{
"Code": secret,
"User": user,
"IP": ip,

View File

@ -1,100 +0,0 @@
package services
import (
"errors"
"fmt"
"github.com/goccy/go-json"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"golang.org/x/text/language"
htmpl "html/template"
"os"
"path/filepath"
"strings"
)
const FallbackLanguage = "en-US"
var LocaleBundle *i18n.Bundle
func LoadLocalization() error {
LocaleBundle = i18n.NewBundle(language.AmericanEnglish)
LocaleBundle.RegisterUnmarshalFunc("json", json.Unmarshal)
var count int
basePath := viper.GetString("locales_dir")
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 := LocaleBundle.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 GetLocalizer(lang string) *i18n.Localizer {
return i18n.NewLocalizer(LocaleBundle, lang)
}
func GetLocalizedString(name string, lang string) string {
localizer := 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 GetLocalizedTemplatePath(name string, lang string) string {
basePath := viper.GetString("templates_dir")
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 GetLocalizedTemplateHTML(name string, lang string) *htmpl.Template {
path := 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 RenderLocalizedTemplateHTML(name string, lang string, data any) string {
tmpl := GetLocalizedTemplateHTML(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()
}

View File

@ -2,6 +2,7 @@ package services
import (
"fmt"
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
)
@ -42,8 +43,8 @@ func UpdateAbuseReportStatus(id uint, status, message string) error {
_ = NewNotification(models.Notification{
Topic: "reports.feedback",
Title: GetLocalizedString("subjectAbuseReportUpdated", account.Language),
Body: fmt.Sprintf(GetLocalizedString("shortBodyAbuseReportUpdated", account.Language), id, status, message),
Title: localize.L.GetLocalizedString("subjectAbuseReportUpdated", account.Language),
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyAbuseReportUpdated", account.Language), id, status, message),
Account: account,
AccountID: account.ID,
})

View File

@ -2,6 +2,7 @@ package services
import (
"fmt"
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
"time"
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
@ -137,8 +138,8 @@ func ActiveTicket(ticket models.AuthTicket) (models.AuthTicket, error) {
_ = NewNotification(models.Notification{
Topic: "passport.security.alert",
Title: GetLocalizedString("subjectLoginAlert", account.Language),
Body: fmt.Sprintf(GetLocalizedString("shortBodyLoginAlert", account.Language), ticket.IpAddress),
Title: localize.L.GetLocalizedString("subjectLoginAlert", account.Language),
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyLoginAlert", account.Language), ticket.IpAddress),
Metadata: datatypes.JSONMap{
"ip_address": ticket.IpAddress,
"created_at": ticket.CreatedAt,

View File

@ -2,6 +2,7 @@ package services
import (
"fmt"
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
"strings"
"time"
@ -62,22 +63,22 @@ func NotifyMagicToken(token models.MagicToken) error {
switch token.Type {
case models.ConfirmMagicToken:
link := fmt.Sprintf("%s/flow/accounts/confirm?code=%s", viper.GetString("frontend_app"), token.Code)
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), GetLocalizedString("subjectConfirmRegistration", user.Language))
content = RenderLocalizedTemplateHTML("register-confirm.tmpl", user.Language, map[string]any{
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectConfirmRegistration", user.Language))
content = localize.L.RenderLocalizedTemplateHTML("register-confirm.tmpl", user.Language, map[string]any{
"User": user,
"Link": link,
})
case models.ResetPasswordMagicToken:
link := fmt.Sprintf("%s/flow/accounts/password-reset?code=%s", viper.GetString("frontend_app"), token.Code)
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), GetLocalizedString("subjectResetPassword", user.Language))
content = RenderLocalizedTemplateHTML("reset-password.tmpl", user.Language, map[string]any{
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectResetPassword", user.Language))
content = localize.L.RenderLocalizedTemplateHTML("reset-password.tmpl", user.Language, map[string]any{
"User": user,
"Link": link,
})
case models.DeleteAccountMagicToken:
link := fmt.Sprintf("%s/flow/accounts/deletion?code=%s", viper.GetString("frontend_app"), token.Code)
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), GetLocalizedString("subjectDeleteAccount", user.Language))
content = RenderLocalizedTemplateHTML("confirm-deletion.tmpl", user.Language, map[string]any{
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectDeleteAccount", user.Language))
content = localize.L.RenderLocalizedTemplateHTML("confirm-deletion.tmpl", user.Language, map[string]any{
"User": user,
"Link": link,
})

View File

@ -72,7 +72,7 @@ func main() {
}
// Load localization
if err := services.LoadLocalization(); err != nil {
if err := gap.LoadLocalization(); err != nil {
log.Fatal().Err(err).Msg("An error occurred when loading localization.")
}