# General Translation Knowledge Base: 如何从 next-intl 迁移到 gt-next？
URL: https://generaltranslation.com/zh/kb/migrate-from-next-intl.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 将 next-intl 的插件、中间件和提供者替换为 gt-next 的对应实现，保持相同的区域设置前缀配置，并将 messages/en.json 作为 gt-next 字典加载，让现有的 ICU 消息和译文继续可用。删除 next-intl 的路由配置之前，先映射好所有本地化路径名，然后逐个路由将文案以内联方式迁移到 `<T>` 中。
Category: 国际化
Last updated: 2026-11-11
Authors: Kevin Wu
Published: 2026-11-11

next-intl 和 gt-next 都借助带区域设置前缀的路由和服务端组件来本地化 Next.js App Router 应用，因此应用结构几乎不用改动。next-intl 的消息本身就是 ICU MessageFormat 格式，与 gt-next 字典所用的格式相同。所以在切换库期间，你可以保留 `messages/en.json` 和已翻译的消息目录，之后再按自己的节奏将文案以内联方式迁移到 [`<T>`](/docs/react/reference/components/t) 中。

如果你的消息目录用得好好的，只是产出译文太慢，那可能没必要迁移。继续使用 next-intl 的话，`gt` CLI 也可以原地翻译其消息目录。 (参见 [next-intl 与 gt-next 对比](/kb/next-intl-vs-gt-next)。)

## next-intl 的 API 如何对应到 gt-next？

| next-intl                                                                          | gt-next                                                                                                                                                                                                                                                 |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defineRouting({ locales, defaultLocale })`                                        | `gt.config.json` 中的 `defaultLocale` 和 `locales`                                                                                                                                                                                                         |
| `next.config.ts` 中的 `createNextIntlPlugin()`                                       | 来自 `gt-next/config` 的 [`withGTConfig`](/docs/react/nextjs/config)                                                                                                                                                                                       |
| `proxy.ts` 中的 `createMiddleware(routing)`                                          | 来自 `gt-next/middleware` 的 [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware)                                                                                                                                       |
| `localePrefix: 'as-needed'` / `'always'`                                           | `prefixDefaultLocale: false` (默认值) / `true`                                                                                                                                                                                                             |
| `i18n/request.ts` 中的 `getRequestConfig`                                            | 位于根目录、读取 `next/root-params` 的 `getLocale.ts`，外加 `dictionary` 选项                                                                                                                                                                                         |
| `NextIntlClientProvider`                                                           | `app/[locale]/layout.tsx` 中的 [`GTProvider`](/docs/react/reference/components/gt-provider)                                                                                                                                                               |
| `useTranslations('ns')`                                                            | 来自 `gt-next` 的 [`useTranslations`](/docs/react/reference/hooks/use-translations)`('ns')`                                                                                                                                                                |
| `await getTranslations('ns')`                                                      | 来自 `gt-next/server` 的 `await` [`getTranslations`](/docs/react/nextjs/reference/functions/get-translations)`('ns')`                                                                                                                                      |
| `t.rich` 和 `t.markup`                                                              | 直接用 [`<T>`](/docs/react/reference/components/t) 包裹实际的 JSX                                                                                                                                                                                               |
| `useFormatter` / `getFormatter`                                                    | [`<Num>`](/docs/react/reference/components/num)、[`<Currency>`](/docs/react/reference/components/currency)、[`<DateTime>`](/docs/react/reference/components/datetime) 和 [`<RelativeTime>`](/docs/react/reference/components/relative-time)，或在消息中使用 ICU 参数 |
| [`useLocale` / `getLocale`](https://next-intl.dev/docs/usage/configuration#locale) | [`useLocale`](/docs/react/reference/hooks/use-locale) / [`getLocale`](/docs/react/nextjs/reference/functions/get-locale)                                                                                                                                |
| `setRequestLocale(locale)`                                                         | 使用 `getLocale.ts` 时，页面和布局无需调用；路由处理函数和图片路由则使用 [`registerLocale`](/docs/react/nextjs/reference/functions/register-locale)                                                                                                                                 |
| 来自 `createNavigation` 的 `Link`                                                     | 来自 `gt-next/link` 的 [`Link`](/docs/react/nextjs/link)                                                                                                                                                                                                   |
| `useRouter` / `getPathname` / `redirect`                                           | Next.js 自带的 API，搭配 [`useLocalizeHref`](/docs/react/nextjs/reference/hooks/use-localize-href) 或 [`getLocalizeHref`](/docs/react/nextjs/reference/functions/get-localize-href) 使用                                                                         |
| `NEXT_LOCALE` cookie                                                               | `generaltranslation.locale`，可通过 `headersAndCookies.localeCookieName` 重命名                                                                                                                                                                                |

## 第 1 步：替换插件并保留现有消息

安装 `gt-next` 和 `gt` CLI，然后在 `gt.config.json` 中列出你的语言。将 `withGTConfig` 指向你现有的源文案目录，并与 next-intl 的 URL 结构保持一致：

```ts title="next.config.ts"
import type { NextConfig } from 'next';
import { withGTConfig } from 'gt-next/config';

const nextConfig: NextConfig = {};

export default withGTConfig(nextConfig, {
  dictionary: './messages/en.json',
  prefixDefaultLocale: true, // 即 next-intl 的默认设置 localePrefix: 'always'
  headersAndCookies: { localeCookieName: 'NEXT_LOCALE' }, // 让回访用户沿用上次选择的语言
});
```

如果你之前使用的是 `localePrefix: 'as-needed'`，请省略 `prefixDefaultLocale`。路由选项应在 `withGTConfig` 中设置，而不能只在中间件中设置，因为 gt-next 的 `<Link>` 会从 `withGTConfig` 读取这些选项。

本示例适用于标准的区域设置前缀，例如 `/de/about`。如果你的 `defineRouting` 调用对路由做了自定义，请在步骤 2 删除该调用之前，将各项自定义逐一迁移过来：

* **本地化路径名** (`pathnames`，例如 `/de/ueber-uns`) ：使用 gt-next 的 [`pathConfig`](/docs/react/nextjs/reference/functions/create-next-middleware#path-config) 选项逐一映射。该选项的结构与之相同，即一个共享路径加上各区域设置对应的本地化路径。
* **自定义前缀、基于域名的路由或 `localePrefix: 'never'`：** gt-next 没有直接对应的功能。请先确定新的 URL，并在移除 next-intl 的路由之前，为旧 URL 添加永久重定向。

如需继续使用已审校的译文，请在项目根目录下添加 `loadDictionary.ts` 文件，gt-next 会自动检测到该文件：

```ts title="loadDictionary.ts"
export default async function loadDictionary(locale: string) {
  try {
    return (await import(`./messages/${locale}.json`)).default;
  } catch {
    return {}; // 尚无目录的新区域设置将改用 General Translation 提供的译文
  }
}
```

这些文件中的条目优先生效。若某个键在目录中缺失，则会渲染 General Translation 的翻译。如果 `gt.config.json` 已在 `files` 下列出这些消息目录，请移除该条目：gt-next 现在会将它们作为字典加载，而每个字符串只应由一个来源负责翻译。

## 第 2 步：替换中间件、布局和请求配置

替换 `proxy.ts` (Next.js 15 及更早版本中为 `middleware.ts`) 中 next-intl 的中间件：

```ts title="proxy.ts"
import { createNextMiddleware } from 'gt-next/middleware';

export default createNextMiddleware();

export const config = {
  matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};
```

完成路由映射后，删除 `i18n/request.ts` 和 `i18n/routing.ts`，并在根目录新增 `getLocale.ts` 来替代它们。这样一来，页面和布局无需调用 `setRequestLocale` 即可静态渲染：

```ts title="getLocale.ts"
import { locale } from 'next/root-params';

export default async function getLocale() {
  return await locale();
}
```

`next/root-params` 需要 Next.js 15.5 或更高版本；如果使用的是 15.2 至 15.4 版本，请参阅[中间件指南](/docs/react/nextjs/app-router-middleware#root-locale)。接着，替换区域设置布局中的提供者：

```tsx title="app/[locale]/layout.tsx"
import { GTProvider, getLocales } from 'gt-next';
import { getLocale } from 'gt-next/server';

export function generateStaticParams() {
  return getLocales().map((locale) => ({ locale }));
}

export default async function LocaleLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang={await getLocale()}>
      <body>
        <GTProvider>{children}</GTProvider>
      </body>
    </html>
  );
}
```

默认情况下，如果区域设置路由段不受支持，gt-next 会回退到 `defaultLocale`。若要像 next-intl 的 `hasLocale` 检查那样返回 404，请使用 [`isLocaleSupported`](/docs/react/nextjs/reference/functions/is-locale-supported) 检查该路由段，再调用 `notFound()`。

## 步骤 3：修改导入语句

将目录作为字典加载后，大多数组件只需修改导入那一行即可：

```tsx
// 修改前
import { useTranslations } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { Link } from '@/i18n/navigation';

// 修改后
import { useTranslations } from 'gt-next';
import { getTranslations } from 'gt-next/server';
import Link from 'gt-next/link';
```

`t('heading')`、命名空间、`{name}` 占位符，以及 ICU `plural` 和 `select` 消息均可原样使用，在 `generateMetadata` 中也不例外。以下三种模式需要修改：

* **`t.rich` 和 `t.markup`。** gt-next 的 [`t()`](/docs/react/reference/hooks/use-translations) 返回的是字符串。请将富文本以内联方式移入 [`<T>`](/docs/react/reference/components/t)，并改用真实元素。
* **配合数组使用的 `t.raw`。** gt-next 没有 `t.raw`，而且只会把字典数组当作带选项的消息来读取。请将列表改写为嵌套对象，然后用 [`t.obj()`](/docs/react/guides/translating-with-dictionaries#objects) 读取，或逐个键读取。
* **缺失的键。** next-intl 会记录错误并渲染后备内容；gt-next 则会抛出异常。因此，从目录中删除某个键之前，请先搜索确认它是否仍在使用。

然后运行 [`npx gt validate`](/docs/cli/reference/commands/validate) 检查不符合 ICU 规范的条目，再运行 [`npx gt translate --dictionary ./messages/en.json`](/docs/cli/reference/commands/translate)，为消息目录中缺少的键以及新增的区域设置生成译文。只有当字典位于项目根目录或 `src/` 下，且命名为 `dictionary.json`、`.ts` 或 `.js` 时，CLI 才能自动找到它。因此，只要仍保留该目录，就需要在本地和 CI 中为这两个命令都传入 `--dictionary ./messages/en.json`：

```bash
npx gt validate --dictionary ./messages/en.json
npx gt translate --dictionary ./messages/en.json
```

## 第 4 步：逐个路由将文案改为内联

基于键的方式仍可正常使用，因此可以到此为止。如果希望文案与渲染它的 JSX 放在一起，可以逐个路由将其迁移到 [`<T>`](/docs/react/reference/components/t) 中，先从 `t.rich` 消息开始：

```tsx
// 迁移前："terms": "By continuing you accept the <link>terms</link>."
t.rich('terms', { link: (chunks) => <a href="/terms">{chunks}</a> });

// 迁移后
<T>
  By continuing you accept the <a href="/terms">terms</a>.
</T>
```

在异步服务端组件中，请使用 [`getGT`](/docs/react/nextjs/reference/functions/get-gt) 处理普通字符串。对于改为内联的文案，同样运行 [`npx gt translate`](/docs/cli/reference/commands/translate) 命令即可生成新的译文，该命令会将周围的 JSX 用作上下文。审校人员可以在[仪表板](/docs/platform/dashboard/guides/reviewing-translations)中修正这些译文，[上下文分组](/docs/platform/dashboard/guides/defining-context-for-translations)中的词汇表则能确保术语保持一致。某个键一旦不再被任何代码读取，即可将其从消息目录中删除。

## 第 5 步：移除 next-intl

当搜索 `next-intl` 已找不到任何结果时，移除该依赖和 `createNextIntlPlugin`，并在每个区域设置下测试生产环境构建。然后将 [`npx gt translate`](/docs/cli/reference/commands/translate) 添加到你的构建流程或 [CI](/kb/translations-in-ci) 中，确保新增文案发布时已完成翻译。如果你保留了目录，请加上 `--dictionary ./messages/en.json` 参数。

## 常见问题

### 我的 URL 会变吗？

如果你沿用 next-intl 的对应设置，标准区域设置前缀的 URL 不会改变：`localePrefix: 'as-needed'` 对应 gt-next 的默认行为，`'always'` 对应 `prefixDefaultLocale: true`。gt-next 同样使用 `app/[locale]` 路由段。本地化路径名需要显式配置 `pathConfig` 映射；自定义前缀和基于域名的路由在 gt-next 中没有直接对应的功能，因此需要为这些 URL 设置重定向，并在移除 next-intl 的路由之前逐一测试所有路由。

### 我现有的译文会丢失吗？

不会。用 [`loadDictionary`](/docs/react/reference/functions/load-dictionary) 加载你的 next-intl 消息目录后，它们会照常渲染。General Translation 只会翻译其中缺失的键，以及你之后以内联方式移入 [`<T>`](/docs/react/reference/components/t) 的文案。

### gt-next 是否支持静态渲染？

支持。在根目录添加 `getLocale.ts` 并配置 `generateStaticParams` 即可。 (详见[静态站点生成](/docs/react/nextjs/app-router-static-site-generation)。)

### gt-next 免费吗？

gt-next 采用 MIT 许可证，无需账户即可渲染随应用打包的译文。使用 General Translation 生成译文则[按用量计费](https://generaltranslation.com/pricing)。

## 要点总结

* 将 next-intl 的插件、中间件、请求配置和提供者分别替换为 `withGTConfig`、[`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware)、`getLocale.ts` 和 [`GTProvider`](/docs/react/reference/components/gt-provider)。
* 将 `messages/en.json` 保留为 gt-next 字典，已翻译的消息目录则通过 [`loadDictionary`](/docs/react/reference/functions/load-dictionary) 继续使用；ICU 消息无需任何修改。
* 用 `prefixDefaultLocale` 匹配 `localePrefix` 的设置，并保留 `NEXT_LOCALE` cookie，确保标准的区域设置 URL 和已保存的偏好设置得以沿用；删除 next-intl 的路由之前，先用 `pathConfig` 映射本地化路径名。
* 将 `t.rich` 改写为 [`<T>`](/docs/react/reference/components/t)，再按路由逐步将其他文案改为内联形式。

## Related questions

- [next-intl 替代方案：gt-next 与 next-intl 对比](https://generaltranslation.com/zh/kb/next-intl-vs-gt-next.mdx): next-intl 负责渲染你在各语言消息目录中提供的译文。gt-next 同样会渲染译文，此外，General Translation 还能根据你在 JSX 中内联编写的文案生成译文。两者均采用 MIT 许可证，并支持 App Router 和服务端组件。如果你现有的 next-intl 消息目录运行良好，可以继续保留，并直接用 General Translation 翻译，无需迁移。
- [如何为 Next.js App Router 应用添加 i18n？](https://generaltranslation.com/zh/kb/add-i18n-to-nextjs.mdx): 首先在受支持的 App Router 项目中运行 gt init 来配置 General Translation，然后标记源内容、生成译文并验证语言切换。gt-next 将文案与翻译上下文一同保留在 JSX 中；CLI、仪表板编辑器和 Locadex 负责生成、审校和持续更新。本指南还演示了如何显式配置本地文件。
- [如何为多语言 Next.js 应用设置 SEO？](https://generaltranslation.com/zh/kb/multilingual-nextjs-seo.mdx): 为每种语言提供带前缀的独立 URL，例如 /en/about 和 /es/about，然后为每种已发布的语言提供 html lang、自引用的规范网址、包含 x-default 的 hreflang 替代项、翻译后的 title 和 description，以及站点地图条目。将未发布的版本标记为 noindex。General Translation 的 gt-next 会将每种语言路由到各自的 URL，并通过 getGT 翻译标题。
- [语言重定向与 SEO：URL、cookie 还是 Accept-Language？](https://generaltranslation.com/zh/kb/locale-routing-seo.mdx): 凡是希望被搜索引擎收录的页面，都应由 URL 决定其语言，因为 Googlebot 不会发送 Accept-Language 请求头，而且大多从美国 IP 地址抓取。只有在决定 / 这类中性根路径重定向到哪里时，才应优先参考 cookie 中保存的用户选择，其次是 Accept-Language，并使用临时的 302 或 307 重定向。
- [如何在每次部署时保持译文同步更新？](https://generaltranslation.com/zh/kb/translations-in-ci.mdx): 在每次生产环境构建之前，将翻译作为流水线中的一个步骤运行，这样新增文案就会在引入它的那次部署中以翻译好的形式一同上线。General Translation 的 gt CLI 通过 npx gt translate 实现这一点，所需的项目 API 密钥和项目 ID 以 CI 密钥的形式存储。

## Previous and next questions

- Previous question: [哪些翻译工具按用量而非席位收费？](https://generaltranslation.com/zh/kb/translation-pricing-without-seat-fees.mdx)
- Next question: [如何从 react-i18next 迁移到 gt-react？](https://generaltranslation.com/zh/kb/migrate-from-react-i18next.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
