spark-server/.cursor/skills/spark-im-v3/i18n.md
2026-09-22 17:07:26 +08:00

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 msg or kick/push strings in handler/* or EventLoop.
  • Localize with the sender language when notifying another user.
  • Introduce a fourth locale without adding it to i18n.Lang and every catalog entry.