Skip to content
文档使用指南Next.js 插件

Next.js 插件 (createNextIntlPlugin)

在为 App Router 设置 next-intl 时,您需要将 next-intl/plugin 添加到您的 Next.js 配置中。

最简单的配置示例如下:

next.config.ts
import {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
 
const nextConfig: NextConfig = {};
 
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);

若要自定义,您可以向插件传递选项。

requestConfig

默认情况下,next-intl 会查找一个名为 i18n/request.ts 的文件,该文件返回请求特定的配置。此文件会在 src 文件夹和项目根目录中以 .ts.tsx.js.jsx 扩展名进行查找。

如果您想把此文件移动到其他地方,可以向插件提供路径:

next.config.ts
const withNextIntl = createNextIntlPlugin(
  // 在此指定自定义路径
  './somewhere/else/request.ts'
);

或者,如果您同时使用其他选项,可以使用 requestConfig 选项:

next.config.ts
const withNextIntl = createNextIntlPlugin({
  requestConfig: './somewhere/else/request.ts'
});

experimental

对于早期使用者,Next.js 插件提供了各种实验性选项,让您能在新功能发布为稳定版本前尝试它们。

createMessagesDeclaration

要启用类型安全的消息参数,您可以将 createMessagesDeclaration 选项指向一个示例消息文件,以生成严格的声明文件。

next.config.ts
const withNextIntl = createNextIntlPlugin({
  experimental: {
    // 提供您在 `AppConfig` 中使用的消息路径
    createMessagesDeclaration: './messages/en.json'
  }
  // ...
});

有关详情,请参见 TypeScript 增强

注意: 当使用 useExtracted 时,无需此步骤。

srcPath

使用 next-intl 的应用源代码的相对路径(或多个相对路径)

// 使用 `src` 文件夹
srcPath: './src',
// 不使用 `src` 文件夹
srcPath: './',
// 包含多个包的 Monorepo
srcPath: ['./src', '../ui'],
// 依赖外部包
srcPath: ['./src', './node_modules/@acme/components'],

启用 extract 时,此项为必需。

extract

此选项启用使用 useExtracted,可自动从源文件中提取消息。

const withNextIntl = createNextIntlPlugin({
  experimental: {
    extract: true
    // ...
  }
});

注意: extract 选项应与 messagessrcPath 一起使用。

extract.path

定义提取消息的写入目录(默认为 messages.path):

extract: {
  path: './messages'
},

仅当 messages.path 是数组时,才需要配置此属性。

另见:Monorepo 和外部包

messages

此选项定义了消息文件的存储位置以及加载方式。

const withNextIntl = createNextIntlPlugin({
  experimental: {
    messages: {
      path: './messages',
      format: 'json',
 
      // 可选
      precompile: true
    }
  }
});

配置 experimental.messages 会设置 Turbo 或 Webpack 加载器,使消息能够以普通 JavaScript 对象的形式导入(参见 messages.format)。

messages.path

消息目录的相对路径:

// 从此处加载并转换消息
path: './messages',

如果要从外部包加载消息,可以包含多个路径:

// 包含消息的多个目录
path: ['./messages', '../packages/ui/messages'],

另见:Monorepo 和外部包

messages.locales

定义在 messages.path 中进行互译的语言区域。

使用 useExtracted 时,当检测到 messages.sourceLocale 发生更改(例如 en.jsonde.json 等)时,此选项定义哪些消息会保持同步。

您可以自动检测 path 中的所有语言区域:

// 检测 `path` 中可用的语言区域
locales: 'infer',

……也可以显式指定它们(例如,只使用其中一部分):

// 提取到这些语言区域
locales: ['en', 'de', 'fr'],

……或者传入空数组以不提取任何消息:

// 不提取任何消息
locales: [],

在后一种情况下,您可以通过 unstable_extractMessages 手动运行提取。

messages.sourceLocale

您的主要语言区域,在使用 useExtracted 时,可能用于定义源代码中的消息。

// 源消息将提取到此处
sourceLocale: 'en',

messages.format

定义消息目录的存储格式(如 'json''po' 或自定义格式)。

JSON 格式

若使用此选项,您的消息文件可能如下所示:

{
  "greeting": "Hello"
}

或者在使用 useExtracted 时,使用自动生成的键:

{
  "NhX4DJ": "Hello"
}

请注意,JSON 文件只能存储键值对。要为消息提供更多上下文,例如文件引用和描述,您可以使用 PO 文件,或创建自定义格式来存储其他元数据。

💡

可以使用 eloqnt/studio 等工具通过 AI 生成翻译。

PO 格式

若使用此选项,您的消息文件示例如下:

#. 转到下一张幻灯片
#: src/components/Carousel.tsx
msgid "carousel.next"
msgstr "Right"

或者在使用 useExtracted 时,使用自动生成的键:

#. 转到下一张幻灯片
#: src/components/Carousel.tsx
msgid "5VpL9Z"
msgstr "Right"

除了消息键和标签本身之外,此格式还支持可选的描述和文件引用,用于引用使用该消息的模块。

注意: 默认的 PO 格式只输出路径引用(不包含行号),以避免产生嘈杂的差异。如果您还希望存储行号,可以创建一个自定义格式来输出此格式。

💡

可以使用 eloqnt/studio 等工具通过 AI 生成翻译。

自定义格式

要配置自定义格式,需要指定一个 codec 及其对应的扩展名。

codec 可通过 next-intl/extractordefineCodec 创建:

./CustomCodec.ts
import {defineCodec} from 'next-intl/extractor';
 
export default defineCodec(() => ({
  decode(content, context) {
    // ...
  },
 
  encode(messages, context) {
    // ...
  },
 
  toJSONString(content, context) {
    // ...
  }
}));

随后,在配置中引用该 codec 和一个扩展名:

next.config.ts
const withNextIntl = createNextIntlPlugin({
  experimental: {
    messages: {
      format: {
        codec: './CustomCodec.ts',
        extension: '.json'
      }
      // ...
    }
  }
});

另外可参考内置的 codecs 以获取灵感,以及提供的类型和 JSDoc 说明。

💡

Node.js 从 v22.18 开始原生支持 TypeScript 执行,如上示例所需。如果您的版本较旧,建议将 codec 定义为 JavaScript 文件。

messages.precompile

作为性能优化,您可以在构建时提前预编译消息,从而获得更小的包体积和更快的运行时消息格式化:

const withNextIntl = createNextIntlPlugin({
  experimental: {
    messages: {
      path: './messages',
      format: 'json',
      precompile: true
    }
    // ...
  }
});

根据所提供的选项,导入消息时将自动进行预编译:

// ✅ 会被 Turbo 或 Webpack 加载器预处理
const messages = (await import(`../../messages/en.json`)).default;

另见:使用 next-intl 的预编译

注意: 预编译的消息不支持 t.raw(详见权衡

如何手动预编译消息?

对于未通过 import 导入消息(例如运行时动态获取消息)的情况,您可以使用 icu-minify/compile 手动预编译它们:

i18n/request.ts
import compile from 'icu-minify/compile';
import {getRequestConfig} from 'next-intl/server';
 
type Messages = Record<string, unknown>;
 
function compileMessages(messages: Messages): Messages {
  return Object.fromEntries(
    Object.entries(messages).map(([key, value]) => {
      if (value && typeof value === 'object') {
        return [key, compileMessages(value as Messages)];
      }
 
      if (typeof value === 'string') {
        return [key, compile(value)];
      }
 
      throw new Error(`Unexpected message: ${typeof value}`);
    })
  );
}
 
export default getRequestConfig(async () => {
  const response = await fetch('https://cdn.example.com/messages/en.json');
  const messages = (await response.json()) as Messages;
  const compiled = compileMessages(messages);
 
  return {
    messages: compiled
    // ...
  };
});

如果使用此方式,请确保 icu-minify 版本与您所使用的 next-intl 版本依赖的版本一致,以保证兼容性。