mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 03:05:02 +03:00
fix(i18n): publish the translation bundle only once it is complete
InitLocalizer filled the shared bundle in place, and the Telegram bot runs it again while the panel is serving, so a request could read the bundle's maps while they were being written and the process died on a concurrent map access. Build the bundle privately and publish it, and the bot and web localizers, through atomic pointers; the lazy load in the middleware publishes only if nothing else did first.
This commit is contained in:
@@ -636,9 +636,8 @@ func localizeWithRealBundle(t *testing.T) {
|
||||
if _, err := bundle.ParseMessageFileBytes(data, path); err != nil {
|
||||
t.Fatalf("parse %s: %v", path, err)
|
||||
}
|
||||
orig := locale.LocalizerBot
|
||||
locale.LocalizerBot = i18n.NewLocalizer(bundle, "en-US")
|
||||
t.Cleanup(func() { locale.LocalizerBot = orig })
|
||||
orig := locale.SwapBotLocalizer(i18n.NewLocalizer(bundle, "en-US"))
|
||||
t.Cleanup(func() { locale.SwapBotLocalizer(orig) })
|
||||
}
|
||||
|
||||
// seeding a client bound to a Telegram user, which the client screens key on
|
||||
|
||||
@@ -121,9 +121,8 @@ func broadcastLocalizer(t *testing.T) {
|
||||
&i18n.Message{ID: "tgbot.messages.broadcastCanceled", Other: "canceled"},
|
||||
&i18n.Message{ID: "tgbot.messages.broadcastUnreachable", Other: "ℹ️ {{ .Count }} recipients cannot be messaged — ask them to press Start."},
|
||||
)
|
||||
orig := locale.LocalizerBot
|
||||
t.Cleanup(func() { locale.LocalizerBot = orig })
|
||||
locale.LocalizerBot = i18n.NewLocalizer(bundle, "en-US")
|
||||
orig := locale.SwapBotLocalizer(i18n.NewLocalizer(bundle, "en-US"))
|
||||
t.Cleanup(func() { locale.SwapBotLocalizer(orig) })
|
||||
}
|
||||
|
||||
func createBroadcastInbound(t *testing.T, tag, settings string) {
|
||||
|
||||
@@ -27,9 +27,8 @@ func clientInfoLocalizer(t *testing.T) {
|
||||
&i18n.Message{ID: "tgbot.messages.expire", Other: "Expire Date: {{ .Time }}\r\n"},
|
||||
&i18n.Message{ID: "tgbot.wentWrong", Other: "went wrong"},
|
||||
)
|
||||
orig := locale.LocalizerBot
|
||||
t.Cleanup(func() { locale.LocalizerBot = orig })
|
||||
locale.LocalizerBot = i18n.NewLocalizer(bundle, "en-US")
|
||||
orig := locale.SwapBotLocalizer(i18n.NewLocalizer(bundle, "en-US"))
|
||||
t.Cleanup(func() { locale.SwapBotLocalizer(orig) })
|
||||
}
|
||||
|
||||
// Regression test: a start-after-first-use client is stored as a negative duration,
|
||||
|
||||
@@ -52,9 +52,8 @@ func draftLocalizer(t *testing.T, msgs ...*i18n.Message) {
|
||||
bundle := i18n.NewBundle(language.MustParse("en-US"))
|
||||
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
|
||||
_ = bundle.AddMessages(language.MustParse("en-US"), msgs...)
|
||||
orig := locale.LocalizerBot
|
||||
t.Cleanup(func() { locale.LocalizerBot = orig })
|
||||
locale.LocalizerBot = i18n.NewLocalizer(bundle, "en-US")
|
||||
orig := locale.SwapBotLocalizer(i18n.NewLocalizer(bundle, "en-US"))
|
||||
t.Cleanup(func() { locale.SwapBotLocalizer(orig) })
|
||||
}
|
||||
|
||||
// Regression test: the wizard's own prompts are HTML-parsed as well, so the
|
||||
|
||||
@@ -291,9 +291,8 @@ func TestNotifyExhaustedLooksUpEachIDOnce(t *testing.T) {
|
||||
func TestServerUsageInfoShowsUnavailableUptime(t *testing.T) {
|
||||
bundle := i18n.NewBundle(language.English)
|
||||
_ = bundle.AddMessages(language.English, &i18n.Message{ID: "tgbot.messages.serverUpTime", Other: "Uptime: {{.UpTime}} {{.Unit}}\n"}, &i18n.Message{ID: "tgbot.days", Other: "days"})
|
||||
oldLocale := locale.LocalizerBot
|
||||
locale.LocalizerBot = i18n.NewLocalizer(bundle, "en")
|
||||
t.Cleanup(func() { locale.LocalizerBot = oldLocale })
|
||||
oldLocale := locale.SwapBotLocalizer(i18n.NewLocalizer(bundle, "en"))
|
||||
t.Cleanup(func() { locale.SwapBotLocalizer(oldLocale) })
|
||||
statusCache.mutex.Lock()
|
||||
oldStatus, oldTime := statusCache.data, statusCache.timestamp
|
||||
statusCache.mutex.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user