useExtracted(实验性功能)
作为手动管理命名空间和键的替代方案,next-intl 提供了一个额外的 API,它的工作方式类似于 useTranslations,但会自动从你的源文件中提取消息。
import {useExtracted} from 'next-intl';
function InlineMessages() {
const t = useExtracted();
return <h1>{t('Look ma, no keys!')}</h1>;
}提取会通过 Turbo 或 Webpack loader 自动集成到 next dev 和 next build,你无需手动触发。
当上述文件被编译时,会执行以下操作:
- 将内联消息以自动分配的键提取到你的源语言文件中:
{
"VgH3tb": "Look ma, no keys!"
}- 通过添加空条目或删除过时条目,保持目标语言文件同步:
{
"VgH3tb": ""
}- 编译文件,将
useExtracted替换成useTranslations:
import {useTranslations} from 'next-intl';
function InlineMessages() {
const t = useTranslations();
return <h1>{t('VgH3tb')}</h1>;
}链接:
入门
该 API 当前为实验性功能,需要在 next.config.ts 中启用:
import {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin({
experimental: {
// 源文件的相对路径
srcPath: './src',
extract: {
// 定义提取到哪个语言
sourceLocale: 'en'
},
messages: {
// 目录的相对路径
path: './messages',
// 格式为 'json'、'po' 或自定义格式(见下文)
format: 'json',
// 'infer' 表示根据 `path` 中匹配的文件自动检测语言,或显式指定语言数组
locales: 'infer'
}
}
});
const config: NextConfig = {};
export default withNextIntl(config);这样,每次运行 next dev 或 next build 时,都会根据发现的消息自动提取,并保持你的消息同步。
详情请参见 createNextIntlPlugin。
我可以手动提取消息吗?
虽然消息提取设计为无缝集成你的开发工作流——基于运行 next dev 和 next build,你也可以手动提取消息:
import {unstable_extractMessages} from 'next-intl/extractor';
await unstable_extractMessages({
srcPath: './src',
sourceLocale: 'en',
messages: {
path: './messages',
format: 'json',
locales: 'infer'
}
});
console.log('✔ 消息已提取');这在你开发包(例如组件库)时非常有用,此时你没有运行 Next.js 开发服务器,但想连同包一起提供消息。
内联消息
ICU 消息
你熟悉的 useTranslations 中所有 ICU 特性 都支持,依然可以照常使用:
// 参数插值
t('Hello {name}!', {name: 'Jane'});// 基数复数形式
t(
'You have {count, plural, =0 {no followers yet} =1 {one follower} other {# followers}}.',
{count: 3580}
);// 序数复数形式
t(
"It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!",
{year: 22}
);// 选择值
t('{gender, select, female {She is} male {He is} other {They are}} online.', {
gender: 'female'
});// 富文本
t.rich('Please refer to the <link>guidelines</link>.', {
link: (chunks) => <Link href="/guidelines">{chunks}</Link>
});唯一的例外是 t.raw,此功能不打算与消息提取一起使用。
描述信息
为了给消息(AI)译者提供更多上下文,你可以添加描述信息:
<button onClick={onSlideRight}>
{t({
message: 'Right',
description: 'Advance to the next slide'
})}
</button>显式 ID
如果你想使用显式 ID 而非自动生成的,可以选择提供:
<button onClick={onSlideRight}>
{t({
id: 'carousel.next',
message: 'Right'
})}
</button>这在标签在多个地方使用但在其它语言需要不同翻译时非常有用。这是一个较少需要使用的“逃生舱”。
命名空间
如果你希望将消息组织到特定命名空间下,可传递给 useExtracted:
function Modal() {
const t = useExtracted('design-system');
return (
<>
<button>{t('Close')}</button>
...
</>
);
}这会将调用 t 的消息提取到对应的命名空间中:
{
"design-system": {
"5VpL9Z": "Close"
}
}命名空间适用于以下情况:
- 库: 如果你在 monorepo 中有多个包,可以将不同包的消息合并到同一个目录,避免包之间的键冲突。
- 拆分: 如果你只想传递部分消息给客户端,这有助于相应地分组(例如
<NextIntlClientProvider messages={messages.client}>)。
建议不要过度使用命名空间,因为如果涉及重构命名空间,移动组件间的消息可能会变得困难。
await getExtracted()
在异步函数例如服务端组件、Metadata 和服务端 Action 中,使用来自 next-intl/server 的异步版本:
import {getExtracted} from 'next-intl/server';
export default async function ProfilePage() {
const user = await fetchUser();
const t = await getExtracted();
return (
<PageLayout title={t('Hello {name}!', {name: user.name})}>
<UserDetails user={user} />
</PageLayout>
);
}可选编译
虽然消息提取主要设计用于运行中的 Next.js 应用,useExtracted 也可以不被编译为 useTranslations,此时将直接使用内联消息而非替换成翻译键。
例如这对测试很有用:
import {expect, it} from 'vitest';
import {NextIntlClientProvider} from 'next-intl';
import {renderToString} from 'react-dom/server';
function Component() {
const t = useExtracted();
return t('Hello {name}!', {name: 'Jane'});
}
it('renders', () => {
const html = renderToString(
// 不需要传递任何消息
<NextIntlClientProvider locale="en" timeZone="UTC">
<Component />
</NextIntlClientProvider>
);
// ✅ 将使用内联消息
expect(html).toContain('Hello Jane!');
});格式
消息可被提取为 .json、.po,或 自定义文件格式—详情请见 messages.format 配置。
建议: 由于 useExtracted 会自动生成键,推荐使用 PO 文件,因为它们支持为消息提供更多上下文信息,如文件引用和描述,对(AI)译者很有帮助。
基于 AI 的翻译可以通过类似 Crowdin 的翻译管理系统自动化完成。