如何在Next.js 14 Server Actions中集成国际化?
解决Next.js 14 Server Action中Zod验证的国际化问题
核心思路
Server Action运行在服务端,无法直接使用客户端国际化hooks(如useLocale),需通过表单传递locale参数或从请求上下文提取locale获取当前语言环境,再动态生成带本地化提示的Zod校验规则。
方案一:表单传递Locale(推荐)
这种方式直接可靠,避免依赖请求头或URL解析的不确定性:
- 客户端表单添加Locale隐藏字段
在提交表单的客户端组件中,获取当前locale并作为隐藏字段传入:
'use client' import { useLocale } from 'next/navigation' import createUser from '@/actions/createUser' export default function UserForm() { const locale = useLocale() return ( <form action={createUser}> <input type="email" name="email" placeholder="Enter email" /> {/* 传递当前语言环境 */} <input type="hidden" name="locale" value={locale} /> <button type="submit">Submit</button> </form> ) }
- Server Action中读取Locale并生成本地化Zod Schema
先定义本地化错误消息文件(如locales/en.json、locales/zh.json):
// locales/en.json { "errors": { "email": { "invalid_type": "Invalid Email", "required": "Email is required" } } } // locales/zh.json { "errors": { "email": { "invalid_type": "无效的邮箱格式", "required": "邮箱不能为空" } } }
然后在Server Action中动态加载对应语言的消息,生成校验规则:
'use server' import { z } from 'zod' // 定义支持的语言列表,避免非法值 const SUPPORTED_LOCALES = ['en', 'zh'] as const export default async function createUser(formData: FormData) { // 从表单获取locale,默认用英文 const rawLocale = formData.get('locale') as string const locale = SUPPORTED_LOCALES.includes(rawLocale as any) ? rawLocale : 'en' // 加载对应语言的错误消息(服务端可直接读取文件) const messages = require(`../locales/${locale}.json`).errors // 动态生成带本地化提示的Zod Schema const schema = z.object({ email: z.string({ invalid_type_error: messages.email.invalid_type, required_error: messages.email.required }).email(messages.email.invalid_type) }) const validatedFields = schema.safeParse({ email: formData.get('email') }) if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors } } // 执行后续数据操作 }
方案二:从请求上下文提取Locale
如果不想修改表单,可以从请求头或URL解析locale(依赖Next.js官方国际化的自动配置):
- 从请求头或URL获取Locale
'use server' import { headers } from 'next/headers' import { z } from 'zod' const SUPPORTED_LOCALES = ['en', 'zh'] as const function getCurrentLocale() { // 优先从Next.js官方请求头获取 const headerLocale = headers().get('x-nextjs-locale') if (headerLocale && SUPPORTED_LOCALES.includes(headerLocale as any)) { return headerLocale } // 降级:从请求URL解析(如/en/xxx中的en) const referer = headers().get('referer') if (referer) { const url = new URL(referer) const urlLocale = url.pathname.split('/')[1] if (urlLocale && SUPPORTED_LOCALES.includes(urlLocale as any)) { return urlLocale } } return 'en' } export default async function createUser(formData: FormData) { const locale = getCurrentLocale() const messages = require(`../locales/${locale}.json`).errors const schema = z.object({ email: z.string({ invalid_type_error: messages.email.invalid_type }) }) // 后续校验逻辑同方案一 }
注意事项
- 确保本地化消息文件路径正确,服务端读取时可结合
process.cwd()使用绝对路径 - 对locale值做合法性校验,避免加载不存在的语言文件导致报错
- 如果使用TypeScript,可给本地化消息文件添加类型定义,提升类型安全性
内容的提问来源于stack exchange,提问作者proco
相关产品推荐
相关产品推荐

