Next.js 插件 (createNextIntlPlugin)
在为 App Router 设置 next-intl 时,您需要将 next-intl/plugin 添加到您的 Next.js 配置中。
最简单的配置示例如下:
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 扩展名进行查找。
如果您想把此文件移动到其他地方,可以向插件提供路径:
const withNextIntl = createNextIntlPlugin(
// 在此指定自定义路径
'./somewhere/else/request.ts'
);或者,如果您同时使用其他选项,可以使用 requestConfig 选项:
const withNextIntl = createNextIntlPlugin({
requestConfig: './somewhere/else/request.ts'
});experimental
对于早期使用者,Next.js 插件提供了各种实验性选项,让您能在新功能发布为稳定版本前尝试它们。
createMessagesDeclaration
要启用类型安全的消息参数,您可以将 createMessagesDeclaration 选项指向一个示例消息文件,以生成严格的声明文件。
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 选项应与 messages 和 srcPath 一起使用。
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'],messages.locales
定义在 messages.path 中进行互译的语言区域。
使用 useExtracted 时,当检测到 messages.sourceLocale 发生更改(例如 en.json、de.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/extractor 的 defineCodec 创建:
import {defineCodec} from 'next-intl/extractor';
export default defineCodec(() => ({
decode(content, context) {
// ...
},
encode(messages, context) {
// ...
},
toJSONString(content, context) {
// ...
}
}));随后,在配置中引用该 codec 和一个扩展名:
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;注意: 预编译的消息不支持 t.raw(详见权衡)
如何手动预编译消息?
对于未通过 import 导入消息(例如运行时动态获取消息)的情况,您可以使用 icu-minify/compile 手动预编译它们:
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 版本依赖的版本一致,以保证兼容性。