diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 115974f..5e7e42c 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,17 @@
-
+
+
+
+
+
+
+
+
+
@@ -159,7 +167,6 @@
-
@@ -184,7 +191,8 @@
-
+
+
diff --git a/go.mod b/go.mod
index 626a13e..cbe2223 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module git.solsynth.dev/hypernet/passport
go 1.23.2
require (
- git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b
+ git.solsynth.dev/hypernet/nexus v0.0.0-20250202054714-6de240179f9c
git.solsynth.dev/hypernet/pusher v0.0.0-20241228030233-50ff8304e465
git.solsynth.dev/hypernet/wallet v0.0.0-20250129150034-87b94cdb5488
github.com/dgraph-io/ristretto v0.2.0
diff --git a/go.sum b/go.sum
index 9bf6ded..e7d3df8 100644
--- a/go.sum
+++ b/go.sum
@@ -35,6 +35,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b h1:8yB9kMwEMY/nIbmDDxrhH5sTypgmK5PIIiIfP5QXx4s=
git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b/go.mod h1:PhLCv2lsNoscPVJbkWnxwQnJ141lc4RIEkVffrHwl4s=
+git.solsynth.dev/hypernet/nexus v0.0.0-20250202054714-6de240179f9c h1:z0//UGRwyZq1TIvn5/fGK5GCXr837KLFD3K0AkaKDyY=
+git.solsynth.dev/hypernet/nexus v0.0.0-20250202054714-6de240179f9c/go.mod h1:v+rpf1ZDRi8moaThTAkj5DMQU+rw96YTHcN8/7n/p2Y=
git.solsynth.dev/hypernet/pusher v0.0.0-20241228030233-50ff8304e465 h1:KFtv9lF0JMUGsq1uHwQvop8PTyqdiLuUQuRrd5WmzPk=
git.solsynth.dev/hypernet/pusher v0.0.0-20241228030233-50ff8304e465/go.mod h1:XHTqFU/vBe4JiuAjl87GUcL8+w/IizSNoqH6n3WkQFc=
git.solsynth.dev/hypernet/wallet v0.0.0-20250129150034-87b94cdb5488 h1:/9Ol+PfDQFAYtHo0kk6sxqiEsZ6epb6yUEsZJxy14Mk=
diff --git a/pkg/internal/gap/server.go b/pkg/internal/gap/server.go
index 7cb493d..b50d5a5 100644
--- a/pkg/internal/gap/server.go
+++ b/pkg/internal/gap/server.go
@@ -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"))
+}
diff --git a/pkg/internal/services/factors.go b/pkg/internal/services/factors.go
index fb41b99..2035077 100644
--- a/pkg/internal/services/factors.go
+++ b/pkg/internal/services/factors.go
@@ -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,
diff --git a/pkg/internal/services/i18n.go b/pkg/internal/services/i18n.go
deleted file mode 100644
index 891e506..0000000
--- a/pkg/internal/services/i18n.go
+++ /dev/null
@@ -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()
-}
diff --git a/pkg/internal/services/reports.go b/pkg/internal/services/reports.go
index 1b232d2..d023858 100644
--- a/pkg/internal/services/reports.go
+++ b/pkg/internal/services/reports.go
@@ -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,
})
diff --git a/pkg/internal/services/ticket.go b/pkg/internal/services/ticket.go
index 781d4d7..55da7fc 100644
--- a/pkg/internal/services/ticket.go
+++ b/pkg/internal/services/ticket.go
@@ -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,
diff --git a/pkg/internal/services/tokens.go b/pkg/internal/services/tokens.go
index 776d221..bb51caa 100644
--- a/pkg/internal/services/tokens.go
+++ b/pkg/internal/services/tokens.go
@@ -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,
})
diff --git a/pkg/main.go b/pkg/main.go
index 3a1f3e3..43c54cc 100644
--- a/pkg/main.go
+++ b/pkg/main.go
@@ -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.")
}