mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-05-06 14:24:15 +02:00
feat(i18n): per-session i18n routing and localized scenarios
Phase C of #188 — the last phase of server-side multi-language support. Adds Session.I18n(), a cached per-session i18n table resolver built via getLangStringsFor(s.Lang()). The pointer is stable until SetLang invalidates the cache, so hot-path handlers pay zero allocations on repeated calls. All 51 s.server.i18n.* call sites across commands, guild, guild scout, cafe, and cast-binary handlers now route through s.I18n().*, so chat replies, guild invite mail templates, cafe reset notices, and quest-timer broadcasts are served in the player's preferred language instead of the server-wide default. Scenario JSON gets the same plain-or-map LocalizedString treatment that quests received in phase B: subheader Strings and inline entry Text accept either a plain string (backwards compatible) or a language-keyed object. CompileScenarioJSON takes the compiling session's language, loadScenarioBinary passes s.Lang(), and ParseScenarioBinary emits plain-string LocalizedStrings so existing .bin files round-trip byte-for-byte through the JSON path. World-wide broadcasts (Raviente siege announcements via BroadcastRaviente) intentionally stay on the server default — they have no single-session context to resolve against.
This commit is contained in:
@@ -49,6 +49,8 @@ type Session struct {
|
||||
charID uint32
|
||||
userID uint32
|
||||
clientLang string // Per-session language preference; empty = use server default
|
||||
cachedI18n *i18n // Lazily populated by I18n(); invalidated on SetLang
|
||||
cachedI18nLang string // Lang the cachedI18n was built for
|
||||
logKey []byte
|
||||
sessionStart int64
|
||||
courses []mhfcourse.Course
|
||||
@@ -126,12 +128,45 @@ func (s *Session) Lang() string {
|
||||
|
||||
// SetLang updates the session's in-memory language preference. Persistence
|
||||
// to the database is the caller's responsibility (via userRepo.SetLanguage).
|
||||
// The cached i18n table is invalidated so the next I18n() call rebuilds
|
||||
// against the new language.
|
||||
func (s *Session) SetLang(lang string) {
|
||||
s.Lock()
|
||||
s.clientLang = lang
|
||||
s.cachedI18n = nil
|
||||
s.cachedI18nLang = ""
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
// I18n returns the i18n string table resolved against this session's
|
||||
// effective language (see Lang). The first call materializes the table via
|
||||
// getLangStringsFor and the result is cached on the session so hot-path
|
||||
// handlers (chat, mail, timer tick broadcasts) do not pay the allocation on
|
||||
// every packet. SetLang invalidates the cache.
|
||||
func (s *Session) I18n() *i18n {
|
||||
s.Lock()
|
||||
if s.cachedI18n != nil && s.cachedI18nLang == s.clientLang {
|
||||
i := s.cachedI18n
|
||||
s.Unlock()
|
||||
return i
|
||||
}
|
||||
lang := s.clientLang
|
||||
s.Unlock()
|
||||
// Resolve lang (falls back to server default when empty).
|
||||
effectiveLang := lang
|
||||
if effectiveLang == "" {
|
||||
effectiveLang = s.server.erupeConfig.Language
|
||||
}
|
||||
resolved := getLangStringsFor(effectiveLang)
|
||||
s.Lock()
|
||||
// Someone may have raced us — overwrite defensively, pointer value is
|
||||
// still the one we just built so callers get a consistent view.
|
||||
s.cachedI18n = &resolved
|
||||
s.cachedI18nLang = lang
|
||||
s.Unlock()
|
||||
return &resolved
|
||||
}
|
||||
|
||||
// Start starts the session packet send and recv loop(s).
|
||||
func (s *Session) Start() {
|
||||
s.logger.Debug("New connection", zap.String("RemoteAddr", s.rawConn.RemoteAddr().String()))
|
||||
|
||||
Reference in New Issue
Block a user