From 6dd578fe80b21aeec133680cafbba2eab4a720ba Mon Sep 17 00:00:00 2001 From: erio Date: Mon, 20 Apr 2026 18:52:06 +0800 Subject: [PATCH] feat(payment): localize config-field names in error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前错误提示里直接显示原始字段名(certSerial / apiV3Key),用户看不懂。 在 extractI18nErrorMessage 内加 localizeMetadata:自动把 metadata.key 和 metadata.keys(斜杠分隔)对应到 admin.settings.payment.field_ 的 i18n 标签,查不到才回退原文。 - 「微信支付配置缺少必填项:certSerial」 → 「微信支付配置缺少必填项:证书序列号」 - 「publicKey/publicKeyId must be provided together」 → 「公钥 / 公钥 ID 必须同时配置」 --- frontend/src/utils/apiError.ts | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/frontend/src/utils/apiError.ts b/frontend/src/utils/apiError.ts index 384578acd2..07a17aca13 100644 --- a/frontend/src/utils/apiError.ts +++ b/frontend/src/utils/apiError.ts @@ -48,10 +48,45 @@ export function extractApiErrorMetadata(err: unknown): Record | type TranslateFn = (key: string, params?: Record) => string type TranslateWithExistsFn = TranslateFn & { te?: (key: string) => boolean } +/** + * Translate a value via i18n if a matching key exists, otherwise return the original. + * Example: "certSerial" → t('admin.settings.payment.field_certSerial') → "证书序列号". + */ +function tryTranslate(t: TranslateFn, key: string, fallback: string): string { + const translated = t(key) + if (translated === key) return fallback + const te = (t as TranslateWithExistsFn).te + if (te && !te(key)) return fallback + return translated +} + +/** + * Replace raw config field names in metadata (e.g. "certSerial") with their + * localized UI labels (e.g. "证书序列号"), using the provider-config field i18n namespace. + * Handles both single `key` and `/`-joined `keys` patterns used by wxpay errors. + */ +function localizeMetadata(metadata: Record, t: TranslateFn): Record { + const out: Record = { ...metadata } + if (typeof out.key === 'string') { + out.key = tryTranslate(t, `admin.settings.payment.field_${out.key}`, out.key) + } + if (typeof out.keys === 'string') { + out.keys = out.keys + .split('/') + .map(k => tryTranslate(t, `admin.settings.payment.field_${k}`, k)) + .join(' / ') + } + return out +} + /** * Extract a localized error message from an API error by looking up * `.` in i18n and substituting metadata as placeholders. * + * Config-field names in metadata (`key` / `keys`) are automatically translated + * to their UI labels before substitution, so error messages read like + * "缺少必填项:证书序列号" instead of "缺少必填项:certSerial". + * * @param err - The caught error * @param t - Vue i18n translate function * @param namespace- i18n key prefix, e.g. "payment.errors" @@ -66,7 +101,8 @@ export function extractI18nErrorMessage( const code = extractApiErrorCode(err) if (code) { const key = `${namespace}.${code}` - const metadata = extractApiErrorMetadata(err) ?? {} + const rawMetadata = extractApiErrorMetadata(err) ?? {} + const metadata = localizeMetadata(rawMetadata, t) const translated = t(key, metadata) // Vue i18n returns the key itself when missing; detect that and fall back. if (translated !== key) return translated