LanguageDetector切换语言失效求助(i18next+Zod+NestJS环境)
i18next语言切换失效问题
问题现象
使用i18next-http-middleware的LanguageDetector时,无法通过请求头Accept-Language切换语言。将fallbackLng设为en或ar时提示正常,但修改请求头为ar后,仍然返回en语言的提示信息。Debug日志显示语言已切换到ar,但仍出现missingKey en zod email email的错误。
关键日志
// 设置Accept-Language: ar时的日志 i18next: languageChanged en i18next: languageChanged ar i18next::translator: missingKey en zod email email
配置代码
// configure dotenv before every thing, even imports import * as dotenv from 'dotenv'; import { join, resolve } from 'path'; dotenv.config({ path: resolve(__dirname, '.env') }); import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import i18next from 'i18next'; import enTranslations from 'zod-i18n-map/locales/en/zod.json'; import { z } from 'zod'; import { makeZodI18nMap } from 'zod-i18n-map'; import languageDetector from 'i18next-http-middleware'; import i18nextBackend from 'i18next-fs-backend'; import arTranslations from 'zod-i18n-map/locales/ar/zod.json'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(languageDetector.handle(i18next)); await i18next .use(i18nextBackend) .use(languageDetector.LanguageDetector) .init({ debug: true, supportedLngs: ['en', 'ar'], fallbackLng: 'en', resources: { ar: { zod: arTranslations }, en: { zod: enTranslations }, }, }); z.setErrorMap(makeZodI18nMap({ ns: 'zod', t: i18next.t })); app.use(compression()); await app.listen(3000); } bootstrap();
预期行为
发送Accept-Language: ar请求时,返回阿拉伯语的提示信息,而非默认的英语。
环境信息
- Runtime: 最新版NestJS
- i18next: 23.5.1
- 操作系统: Windows
- zod-i18n-map: 2.20.0
初始化Debug日志
i18next: languageChanged en i18next: initialized { debug: true, initImmediate: true, ns: [ 'translation' ], defaultNS: [ 'translation' ], fallbackLng: [ 'en' ], fallbackNS: false, supportedLngs: [ 'en', 'ar', 'cimode' ], nonExplicitSupportedLngs: false, load: 'all', preload: false, simplifyPluralSuffix: true, keySeparator: '.', nsSeparator: ':', pluralSeparator: '_', contextSeparator: '_', partialBundledLanguages: false, saveMissing: false, updateMissing: false, saveMissingTo: 'fallback', saveMissingPlurals: true, missingKeyHandler: false, missingInterpolationHandler: false, postProcess: false, postProcessPassResolved: false, returnNull: false, returnEmptyString: true, returnObjects: false, joinArrays: false, returnedObjectHandler: false, parseMissingKeyHandler: false, appendNamespaceToMissingKey: false, appendNamespaceToCIMode: false, overloadTranslationOptionHandler: [Function: handle], interpolation: { escapeValue: true, format: [Function: bound format], prefix: '{{', suffix: '}}', formatSeparator: ',', unescapePrefix: '-', nestingPrefix: '$t(', nestingSuffix: ')', nestingOptionsSeparator: ',', maxReplaces: 1000, skipOnVariables: true }, resources: { ar: { zod: [Object] }, en: { zod: [Object] } }, ignoreJSONStructure: true }
问题根源与解决方法
1. 初始化顺序错误
当前代码先挂载languageDetector中间件,再初始化i18next,导致中间件无法正确绑定已配置的i18n实例。
修复:调整顺序,先完成i18next初始化,再挂载中间件:
async function bootstrap() { const app = await NestFactory.create(AppModule); // 先初始化i18next await i18next .use(i18nextBackend) .use(languageDetector.LanguageDetector) .init({ debug: true, supportedLngs: ['en', 'ar'], fallbackLng: 'en', defaultNS: 'zod', // 设置默认命名空间为zod,避免重复指定 resources: { ar: { zod: arTranslations }, en: { zod: enTranslations }, }, detection: { order: ['header', 'querystring', 'cookie'], // 确保优先读取请求头 lookupHeader: 'Accept-Language', }, }); // 再挂载语言检测中间件 app.use(languageDetector.handle(i18next)); app.use(compression()); await app.listen(3000); }
2. Zod无法获取请求上下文的语言
全局设置的z.setErrorMap使用了i18next的全局t函数,而每个请求的语言上下文存储在req.i18n中,全局t函数不会随请求语言变化。
修复:创建请求作用域的Zod验证管道,动态获取当前请求的t函数:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { z } from 'zod'; import { makeZodI18nMap } from 'zod-i18n-map'; import { Request } from 'express'; import { REQUEST } from '@nestjs/core'; @Injectable() export class ZodValidationPipe implements PipeTransform { constructor(@Inject(REQUEST) private readonly request: Request) {} transform(value: unknown, metadata: ArgumentMetadata) { if (!('parse' in (metadata.metatype as any))) { return value; } // 获取当前请求的i18n翻译函数 const t = this.request.i18n.t; z.setErrorMap(makeZodI18nMap({ ns: 'zod', t })); try { return (metadata.metatype as any).parse(value); } catch (error) { throw new BadRequestException(error.errors); } } }
然后在控制器或模块中使用该管道:
// 控制器级别使用 import { Controller, Post, Body, UsePipes } from '@nestjs/common'; import { ZodValidationPipe } from './zod-validation.pipe'; import { CreateUserSchema } from './schemas/create-user.schema'; @Controller('users') @UsePipes(ZodValidationPipe) export class UsersController { @Post() create(@Body() body: CreateUserSchema) { // 处理请求 } }
3. 重复导入修复
删除代码中重复的enTranslations导入语句。
完成以上修改后,请求头Accept-Language: ar即可正确触发阿拉伯语的提示信息。
内容的提问来源于stack exchange,提问作者Saybers
相关产品推荐
相关产品推荐

