admin/services/apiErrorLocalization.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

72 lines
2.6 KiB
TypeScript

import { texts } from '@/texts'
const PERSIAN_TEXT_PATTERN = /[\u0600-\u06ff]/
const ERROR_CODE_MESSAGES: Record<string, string> = { ...texts.errors.codes }
const STATUS_MESSAGES: Record<number, string> = Object.fromEntries(
Object.entries(texts.errors.httpStatus).map(([status, message]) => [Number(status), message])
)
const DEFAULT_API_ERROR_MESSAGE = texts.errors.defaultApi
export const containsPersianText = (value: string): boolean => PERSIAN_TEXT_PATTERN.test(value)
export const getProductApiErrorMessage = (code: string): string | undefined => ERROR_CODE_MESSAGES[code.toUpperCase()]
/**
* API error codes stay machine-readable, while every user-facing message is
* guaranteed to contain Persian texts. Unknown English server details are not
* exposed because they are often framework or database implementation text.
*/
export const ensurePersianApiErrorMessage = (message: unknown, status?: number, code?: string): string => {
if (typeof message === 'string') {
const trimmed = message.trim()
if (trimmed && containsPersianText(trimmed)) return trimmed
}
if (code) {
const localizedCodeMessage = ERROR_CODE_MESSAGES[code.toUpperCase()]
if (localizedCodeMessage) return localizedCodeMessage
}
if (status) {
return STATUS_MESSAGES[status] ?? (status >= 500 ? STATUS_MESSAGES[500] : DEFAULT_API_ERROR_MESSAGE)
}
return DEFAULT_API_ERROR_MESSAGE
}
const localizeMessageValue = (message: unknown, status?: number, code?: string): string | string[] => {
if (!Array.isArray(message)) return ensurePersianApiErrorMessage(message, status, code)
const localized = message.map((item) => ensurePersianApiErrorMessage(item, status, code))
return [...new Set(localized)]
}
/** Returns an error payload with Persian top-level and nested body messages. */
export const localizeApiErrorPayload = (data: unknown, status?: number): Record<string, unknown> => {
const source = data && typeof data === 'object' && !Array.isArray(data) ? (data as Record<string, unknown>) : {}
const code = typeof source.code === 'string' ? source.code : undefined
const body =
source.body && typeof source.body === 'object' && !Array.isArray(source.body) ? (source.body as Record<string, unknown>) : undefined
const bodyCode = typeof body?.code === 'string' ? body.code : code
const localized: Record<string, unknown> = {
...source,
success: false,
message: localizeMessageValue(source.message ?? data, status, code),
}
if (body) {
localized.body = {
...body,
message: localizeMessageValue(body.message, status, bodyCode),
}
}
return localized
}