Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
2.5 KiB
i18n (zh-CN / en / zh-TW)
User-visible error msg and notification copy are never hardcoded in handlers. They come from internal/pkg/i18n using the request language.
Language on every request
Canonical tags: zh-CN (default), en, zh-TW. Parse with i18n.Parse; store on reqctx.RequestContext.Language. Read with reqctx.Lang(ctx) / reqctx.Language(ctx).
| Transport | Client sends | Server stores |
|---|---|---|
| HTTP | X-Language if set, else Accept-Language |
Trace middleware, every request |
| TCP | AUTH JSON field language |
session.Conn.SetLanguage for the connection; copied into each command's RequestContext |
Missing or unknown tags → zh-CN. zh / zh-Hans → zh-CN. zh-TW / zh-HK / zh-Hant → zh-TW. en* → en.
Do not put language in JWT. Rate limit still uses reqctx.RealIP, not language.
Errors
Sentinel AppError keeps an English Msg for logs. Envelope msg is localized at the transport boundary:
- HTTP:
reply.FromContext(ctx, err) - TCP:
protocol.EncodeErrLang(i18n.Parse(conn.Language()), err)
WithMsg is only for already-localized text (validator). Do not WithMsg("unauthorized") in a handler.
Add a new business code: register err.{code} in internal/pkg/i18n/catalog_err.go for all three languages.
Validation
label and errmsg are catalog keys, not Chinese sentences:
Email string `json:"email" validate:"required,email" label:"field.email" errmsg:"required:validate.login.email_required"`
validate.StructLang(reqctx.Lang(ctx), in). Default templates are validate.required etc. with {label} / {param}.
Notification types
Push and in-app system notices use type keys, then i18n.Notify(lang, typ, args) for title/body. Recipient language is the target device session language (TCP) or the user's stored lang — never the sender's.
| Type | Key |
|---|---|
| Same-device kick | notify.kick.replaced |
| Friend request | notify.friend.request |
| Group invite | notify.group.invite |
| Removed from group | notify.group.removed |
| Message recall | notify.message.recall |
push.Notification.Type holds the key. Fill Title/Body with i18n.Notify before Notifier.Notify. Add new types in catalog_notify.go (.title / .body, {name} placeholders).
Do not
- Hardcode
msgor kick/push strings inhandler/*or EventLoop. - Localize with the sender language when notifying another user.
- Introduce a fourth locale without adding it to
i18n.Langand every catalog entry.