feat: internationalize API error responses by locale

Add shared error codes with zh/en/ms messages, coded app exceptions,
and locale-aware global filter. Frontends send X-Locale so error text
matches the active UI language.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-10 13:36:38 +08:00
parent 03f54ca689
commit 641c92a5f5
23 changed files with 1059 additions and 234 deletions

View File

@@ -2,13 +2,14 @@ import axios from 'axios';
import router from './router';
import { clearStaffSession, reconcileStaffSessionFromToken, useAuthStore } from './stores/auth';
import { ensureStaffSession, resetStaffSessionHydration } from './utils/session-hydrate';
import { ADMIN_LOCALE_STORAGE_KEY } from './i18n';
const api = axios.create({ baseURL: '/api' });
let handling401 = false;
let handling403Portal = false;
const PORTAL_MISMATCH_MESSAGES = new Set(['Admin access only', 'Agent access only']);
const PORTAL_MISMATCH_CODES = new Set(['ADMIN_ACCESS_ONLY', 'AGENT_ACCESS_ONLY']);
function requestPath(config: { url?: string; baseURL?: string } | undefined): string {
if (!config?.url) return '';
@@ -20,6 +21,9 @@ api.interceptors.request.use((config) => {
const t = localStorage.getItem('manage_token');
if (t) config.headers.Authorization = `Bearer ${t}`;
const locale = localStorage.getItem(ADMIN_LOCALE_STORAGE_KEY) || 'zh-CN';
config.headers['X-Locale'] = locale;
reconcileStaffSessionFromToken();
const auth = useAuthStore();
const path = requestPath(config);
@@ -68,7 +72,7 @@ api.interceptors.response.use(
if (
err.response?.status === 403 &&
!handling403Portal &&
PORTAL_MISMATCH_MESSAGES.has(err.response?.data?.message)
PORTAL_MISMATCH_CODES.has(err.response?.data?.code)
) {
handling403Portal = true;
await ensureStaffSession();