♻️ Replace i18n services with nexus one

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

14
.idea/workspace.xml generated
View File

@ -4,9 +4,17 @@
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Register with preferred language">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":necktie: Limit max auth steps to 2 for normal users">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/gap/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/gap/server.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/factors.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/factors.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/i18n.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/reports.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/reports.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/ticket.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/ticket.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/tokens.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/tokens.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/main.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -159,7 +167,6 @@
</component>
<component name="VcsManagerConfiguration">
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<MESSAGE value=":boom: Passing relationship api arguments in body instead of querystring" />
<MESSAGE value=":sparkles: Better check in experience random algorithm" />
<MESSAGE value=":sparkles: Better relationships stauts query" />
<MESSAGE value=":truck: Move make friendship api" />
@ -184,7 +191,8 @@
<MESSAGE value=":bug: Bug fixes on localization" />
<MESSAGE value=":bug: Fix email html rendering" />
<MESSAGE value=":sparkles: Register with preferred language" />
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: Register with preferred language" />
<MESSAGE value=":necktie: Limit max auth steps to 2 for normal users" />
<option name="LAST_COMMIT_MESSAGE" value=":necktie: Limit max auth steps to 2 for normal users" />
<option name="GROUP_MULTIFILE_MERGE_BY_DIRECTORY" value="true" />
</component>
<component name="VgoProject">

2
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

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.")
}