2.7 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, 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 → en (default). zh / zh-Hans → zh-CN. zh-TW / zh-HK / zh-Hant → zh-TW. en* → en.
POST /v1/sms/codes requires X-Language explicitly (i18n.ParseExplicit); Accept-Language alone is not enough.
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.