Skip to content
文档使用指南useExtracted

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 devnext build,你无需手动触发。

当上述文件被编译时,会执行以下操作:

  1. 将内联消息以自动分配的键提取到你的源语言文件中:
messages/en.json
{
  "VgH3tb": "Look ma, no keys!"
}
  1. 通过添加空条目或删除过时条目,保持目标语言文件同步:
messages/de.json
{
  "VgH3tb": ""
}
  1. 编译文件,将 useExtracted 替换成 useTranslations
import {useTranslations} from 'next-intl';
 
function InlineMessages() {
  const t = useTranslations();
  return <h1>{t('VgH3tb')}</h1>;
}

链接:

入门

该 API 当前为实验性功能,需要在 next.config.ts 中启用:

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 devnext build 时,都会根据发现的消息自动提取,并保持你的消息同步。

详情请参见 createNextIntlPlugin

我可以手动提取消息吗?

虽然消息提取设计为无缝集成你的开发工作流——基于运行 next devnext 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"
  }
}

命名空间适用于以下情况:

  1. 库: 如果你在 monorepo 中有多个包,可以将不同包的消息合并到同一个目录,避免包之间的键冲突。
  2. 拆分: 如果你只想传递部分消息给客户端,这有助于相应地分组(例如 <NextIntlClientProvider messages={messages.client}>)。

建议不要过度使用命名空间,因为如果涉及重构命名空间,移动组件间的消息可能会变得困难。

await getExtracted()

在异步函数例如服务端组件、Metadata 和服务端 Action 中,使用来自 next-intl/server 的异步版本:

page.tsx
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 的翻译管理系统自动化完成。