You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:30:53