Zod 基于 Next.js 中活动区域设置的动态错误消息

问题描述 投票:0回答:4

我在 Zod 中遇到 i18n 问题。基本上,我有一个由react-hook-form和Zod处理的简单表单进行验证,但是当我更改区域设置时,错误消息似乎不会根据活动区域设置进行翻译。 如果有人可以帮助我使 zod 错误消息动态化。我正在使用 Next.js,next-i18next:

// my zod schema
import { z } from 'zod';
import { i18n } from 'next-i18next';

export const mailSchema = z.object({
  email: z
    .string({
      invalid_type_error: i18n?.t('zod-errors.invalid_type_error') ?? '',
      required_error: i18n?.t('zod-errors.required_error') ?? '',
    })
    .email(i18n?.t('zod-errors.email') ?? ''),
  subject: z
    .string({
      invalid_type_error: i18n?.t('zod-errors.invalid_type_error') ?? '',
      required_error: i18n?.t('zod-errors.required_error') ?? '',
    })
    .min(3, i18n?.t('zod-errors.min') ?? '')
    .max(60, i18n?.t('zod-errors.max') ?? ''),
  body: z
    .string({
      invalid_type_error: i18n?.t('zod-errors.invalid_type_error') ?? '',
      required_error: i18n?.t('zod-errors.required_error' ?? ''),
    })
    .min(10, i18n?.t('zod-errors.min') ?? '')
    .max(500, i18n?.t('zod-errors.max') ?? ''),
});
next.js i18next zod
4个回答
2
投票

我有同样的问题,我找到了一个为 zod 错误消息提供自动翻译的库:

https://github.com/aiji42/zod-i18n


2
投票

我认为问题在于消息的异步加载,这并没有真正解释,但在 i18next 的配置文档中提到过。

我想要自己的错误消息,而不是由另一个依赖项翻译的 zod。所以我这样做了:

const validationSchema = z.object({
    place: z.object({
        route: z.string().min(1, "field::Address.Req").default(""),
        streetNumber: z.string().min(1, "field::Street number.Req").default(""),
        postalCode: z.string().min(1, "field::Postal code.Req").default(""),
    })
});

所以错误消息是 i18next 翻译机的关键。当渲染/呈现给用户时,会获取实际的消息。

field
命名空间中,我得到了这个:

{
  "IsReq": "ist erforderlich",
  "Address.Req": "$t(common::Address) $t(field::IsReq)",
  "Street number.Req": "$t(common::Street number) $t(field::IsReq)",
  "Postal code.Req": "$t(common::Postal code) $t(field::IsReq)"
}

好处是它只是使用系统(zod | i18next),翻译者可以忽略插值并根据需要写出更好的东西。

(将架构添加到组件中也可以解决问题,但不知何故感觉不太对..)


0
投票

我遇到了同样的问题并且非常简单地解决了!在 Zod 对象中,作为错误消息,我只放置翻译键,实际的 t('key') 在错误消息组件中完成。不需要图书馆,根本不需要额外的工作。希望您喜欢这个简单的解决方法:)

❌错了

email: z.string().email({ message:  t('emailError)})

✅正确!

email: z.string().email({ message: 'emailError'})

然后像这样使用:

<HelperText type='error'>
   {t(errors.email.message)}
</HelperText>

0
投票

大家好!

我不确定我的方法是否完全正确,但我找到了一种使用 Zod 管理 next-intl 翻译值/消息参数以解决验证错误的方法。

让我们从 zod 模式开始

export const signupFormStepUsernameSchema = z.object({
    username: z
        .string()
        .trim()
        .superRefine((val, ctx) => {
            const minChars = 4
            const maxChars = 20

            // ... other validation

            if (val.length < minChars) {
                ctx.addIssue({
                    code: z.ZodIssueCode.custom,
                    message: `errors.invalid.username.minLength|{"min": ${minChars}}`,
                })

                return z.NEVER
            }
            if (val.length > maxChars) {
                ctx.addIssue({
                    code: z.ZodIssueCode.custom,
                    message: `errors.invalid.username.maxLength|{"max": ${maxChars}}`,
                })

                return z.NEVER
            }

            // ... other validation

        }),
}).strict().required();

让我们分解一下我们传递的内容或以什么格式传递错误消息:

ctx.addIssue({
    code: z.ZodIssueCode.custom,
    message: `errors.invalid.username.maxLength|{"max": ${maxChars}}`
});

在消息中,我们定义了特定的格式,并用

|
符号分隔消息键和翻译值/消息参数。

在实现部分,我们通过以下方法提取密钥和翻译值/消息参数。

首先让我提一下必要的导入和类型声明

import { MessageKeys, TranslationValues } from "next-intl"

type ComponentSignupMessages =
  typeof import("./i18n/en-US/components/signup.json");
type Messages = ComponentSignupMessages;
type ZNIMNestedValueOf<
  T,
  K extends Extract<keyof T, string>
> = K extends keyof T
  ? T[K] extends Record<string, any>
    ? `${K}.${ZNIMNestedKeys<T[K]>}`
    : `${K}`
  : never;

type ZNIMNestedKeys<T> = NestedValueOf<T, Extract<keyof T, string>>;
interface IntlMessages extends Messages {}

好的,现在让我们开始工作吧。

  • 首先,我们将错误消息存储在变量中:
const dirtyErrorKeyAndTranslationValues: Array<string> = errors.username ? (errors.username.message as string).split("|"): "|".split("|");

上面的代码行检查是否存在与用户名字段关联的错误消息。如果存在,它将按

|
拆分错误消息,并将结果部分存储在名为 dirtyErrorKeyAndTranslationValues 的数组中。如果没有错误,它会创建一个包含空字符串的数组。

  • 然后我们从错误消息中提取消息密钥:
let refinedErrorKey = "" as MessageKeys<
  ComponentSignupMessages["Signup"]["StepUsername"],
  ZNIMNestedKeys<ComponentSignupMessages["Signup"]["StepUsername"]>
>;

if (
  (dirtyErrorKeyAndTranslationValues[0] as string) &&
  (dirtyErrorKeyAndTranslationValues[0] as string).length > 0
) {
  refinedErrorKey = dirtyErrorKeyAndTranslationValues[0] as MessageKeys<
    ComponentSignupMessages["Signup"]["StepUsername"],
    ZNIMNestedKeys<ComponentSignupMessages["Signup"]["StepUsername"]>
  >;
}

上面的代码将一个名为refineErrorKey的变量初始化为空字符串。然后,它检查 dirtyErrorKeyAndTranslationValues 数组的第一个元素(假设为字符串)是否存在并且长度是否大于零。如果为 true,则会将 dirtyErrorKeyAndTranslationValues 的第一个元素的值分配给refineErrorKey。

  • 然后我们从错误消息中提取翻译值/消息参数:
let refinedTranslationValues = {} as TranslationValues;

if (
  (dirtyErrorKeyAndTranslationValues[1] as string) &&
  (dirtyErrorKeyAndTranslationValues[1] as string).length > 0
) {
  refinedTranslationValues = JSON.parse(
    dirtyErrorKeyAndTranslationValues[1]
  ) as TranslationValues;
}

上面的代码将名为refineTranslationValues的变量初始化为空对象。然后,它检查 dirtyErrorKeyAndTranslationValues 数组的第二个元素(应为字符串)是否存在且长度是否大于零。如果为 true,则会将 dirtyErrorKeyAndTranslationValues 的第二个元素解析为 JSON,并将结果对象分配给精炼的翻译值。

  • 完成所有之前的工作后,我们可以像这样使用它:
<p>
  {errors.username
    ? t(
        refinedErrorKey,
        Object.keys(refinedTranslationValues).length === 0
          ? refinedTranslationValues
          : undefined
      )
    : " "}
</p>

在这里,如果存在与用户名 (errors.username) 关联的错误消息,它将调用带有参数精炼错误键和精炼翻译值的翻译函数 t(),除非精炼翻译值为空。如果用户名没有错误消息,它会呈现一个空白空间。

MUI React 组件
helperText
中所有上述实现的实际示例
<TextField/>

谢谢!

© www.soinside.com 2019 - 2024. All rights reserved.