Tracking issue for the frontend i18n rollout in octo-admin, aligned with the backend i18n contract (octo-server/.context/i18n 接入方案.draft.md). UI strings live locally in the frontend; the backend only owns server-produced messages (error envelope, emails, notifications).
Approach
- Library:
i18next + react-i18next + i18next-browser-languagedetector
- Resources:
src/i18n/locales/{en-US,zh-CN}/<namespace>.json
- Detection order (aligned with backend negotiation priority):
?lang → i18n_lang cookie → navigator.language → en-US fallback
- antd
ConfigProvider.locale follows i18n.resolvedLanguage
- axios injects
Accept-Language per request (contract §5.1)
Progress
✅ Phase 1 — Foundation + first batch (PR #56, merged)
Phase 2 — Page migrations (mostly done)
All migrated pages route aria-labels through i18n; glossary terms consistent. Only Changelog remains.
⬜ Phase 3 — Lint guard + tooling (PR4)
⬜ Phase 4 — User language preference API (PR2) — blocked on server
⬜ Phase 5 — Error envelope v2 adaptation (PR5) — intentionally last
References
Tracking issue for the frontend i18n rollout in octo-admin, aligned with the backend i18n contract (
octo-server/.context/i18n 接入方案.draft.md). UI strings live locally in the frontend; the backend only owns server-produced messages (error envelope, emails, notifications).Approach
i18next+react-i18next+i18next-browser-languagedetectorsrc/i18n/locales/{en-US,zh-CN}/<namespace>.json?lang→i18n_langcookie →navigator.language→en-USfallbackConfigProvider.localefollowsi18n.resolvedLanguageAccept-Languageper request (contract §5.1)Progress
✅ Phase 1 — Foundation + first batch (PR #56, merged)
MainLayout+Loginmigrated (namespaces:nav/layout/login)resolvedLanguage+ cookiesameSite/securePhase 2 — Page migrations (mostly done)
All migrated pages route
aria-labels through i18n; glossary terms consistent. OnlyChangelogremains.⬜ Phase 3 — Lint guard + tooling (PR4)
t()keys)⬜ Phase 4 — User language preference API (PR2) — blocked on server
GET /v1/user→ readlanguageto init i18nPUT /v1/user/language(value ornullto clear); useeffective_languagefrom response tochangeLanguage+ writei18n_langcookie⬜ Phase 5 — Error envelope v2 adaptation (PR5) — intentionally last
error.{code,message,http_status,details}, falls back tomsg/statusApiErrorgainscode/httpStatus/detailserror.code === 'err.shared.auth.required' || error.http_status === 401(not outer HTTP status — fixed 400 during compat window)err.shared.internal→ generic toast, do not exposeerror.messageerrors.jsonfallback keyed byerr.shared.*/err.server.*X-Octo-Error-Envelope: v2header (sunset stats only)References
octo-server/.context/i18n 接入方案.draft.md(DRAFT — confirm non-draft before Phase 4/5 cutover)