A solution to translating zod error messages using next-intl [closed]
Asked Answered
W

2

6

I am looking for a way of translating Zod error messages when using the next-intl package. Perhaps an alternative to Zod-i18n, instead of i18next.

The reasons for picking next-intl instead of i18next are related to NextJS app router so switching to i18next really isn't an option in this case.

EDIT: I am really looking to translate the default Zod messages, not passing in custom translations per refinement. Bulk translations are what I'm after.

Windbreak answered 26/10, 2023 at 13:1 Comment(0)
S
14

You can pass the translation as an argument.

en.json:

{
  "home": {
    "errors": {
      "Name is required": "Name is required."
    }
  }
}

schemas/index.ts:

import * as z from "zod";

export const BaseSchema = (t: (arg: string) => string) =>
  z.object({
    name: z.string().min(1, {message: t("errors.Name is required")})
  });

in client code (form):

"use client";

import { useTranslations } from "next-intl";
import { BaseSchema } from "@/schemas";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

export const Form = ()=> {
  const t = useTranslations("home");
  const formSchema = BaseSchema(t);
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      name: ""
  });
  const onSubmit = async (values: z.infer<typeof formSchema>) => {
    // await a server action 
  };
  return(<Form></Form>);
};

in server action code:

"use server";

import { getTranslations } from "next-intl/server";
import * as z from "zod";
import { BaseSchema } from "@/schemas";

export const create = async (
  values: z.infer<ReturnType<typeof BaseSchema>>
) => {
  const t = await getTranslations("home");
  const paramsSchema = BaseSchema(t);
  const validatedFields = paramsSchema.safeParse(values);
  if (!validatedFields.success) {
    throw new Error("Invalid fields");
  }
};
Singultus answered 16/1, 2024 at 9:25 Comment(3)
Yeah, good shout. I was mainly wondering about bulk editing the pre-existing messages. Thanks for the reply.Windbreak
Probably the better answer since it doesn't take that much code and is more flexible in terms of messages then zod-i18n. I would add better typing here with export const BaseSchema = (t: ReturnType<typeof useTranslations<'home'>>) => And I'd export schema types from the same file with export type BaseSchemaType = z.infer<ReturnType<typeof BaseSchema>>;Tertial
There is one caveat, the t instance is obtained at runtime via client-side preference, while schema are defined statically. This makes all schema definition dynamic via the t instance.Innerve
A
2

I was facing the same problem and was looking into alternatives of zod-i18n that work with next-intl. After looking into the code of zod-i18n I realized that it's not that big of a deal to adapt for next-intl. You can find an example here and a blog post here. Not as convenient as having a package out of the box but it solved my problem for now.

Argentina answered 15/3, 2024 at 6:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.