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

@@ -0,0 +1,45 @@
import {
BadRequestException,
ForbiddenException,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import type { ApiErrorCode, ApiErrorParams } from '@thebet365/shared';
function body(code: ApiErrorCode, params?: ApiErrorParams) {
const clean = params
? Object.fromEntries(
Object.entries(params)
.filter(([, v]) => v !== undefined && v !== null)
.map(([k, v]) => [k, String(v)]),
)
: undefined;
return { code, params: clean };
}
export function appBadRequest(code: ApiErrorCode, params?: ApiErrorParams) {
return new BadRequestException(body(code, params));
}
export function appNotFound(code: ApiErrorCode, params?: ApiErrorParams) {
return new NotFoundException(body(code, params));
}
export function appForbidden(code: ApiErrorCode, params?: ApiErrorParams) {
return new ForbiddenException(body(code, params));
}
export function appUnauthorized(code: ApiErrorCode, params?: ApiErrorParams) {
return new UnauthorizedException(body(code, params));
}
export function isCodedExceptionResponse(
res: unknown,
): res is { code: ApiErrorCode; params?: ApiErrorParams } {
return (
typeof res === 'object' &&
res !== null &&
'code' in res &&
typeof (res as { code: unknown }).code === 'string'
);
}