Skip to content
博客next-intl 3.0

next-intl 3.0

2023年11月14日 · 作者 Jan Amann

一年多以前,在2022年10月25日,Next.js 13 发布,引入了对 App Router 和 Server Components 的测试支持。从那时起,next-intl 就开始探索利用这些新增功能来提供实现国际化(i18n)的理想体验。

今天,经过370多个提交和60多位社区成员的参与,我非常激动地宣布 next-intl 3.0 正式发布,它现已优先支持 App Router。

如果你仍然对 Pages Router 感到满意,请放心,next-intl 将持续支持这种范式,只要 Next.js 还在支持。对于已经迁移到 App Router 的用户,这意味着你可以充分利用 next-intl 带来的新能力。

新特性

  1. 支持 React 服务器组件(Server Components)useTranslationsuseFormatteruseLocaleuseNowuseTimeZone 这些 API 现在可以在服务器组件中使用(文档)。
  2. 新增异步 API:为了支持异步组件、Metadata API 和路由处理器,新增了 getTranslationsgetFormattergetNowgetTimeZone 这些异步 API(文档)。
  3. 国际化路由中间件:虽然 Next.js 在 Pages Router 内置了对此的支持,但 App Router 不再包含内置解决方案。next-intl 现在提供了一个开箱即用的解决方案来覆盖这部分需求(文档)。
  4. 国际化导航 API:类似于中间件的功能,这提供了一个可直接使用的解决方案,为 Next.js 的导航 API 添加国际化支持:LinkuseRouterusePathnameredirect。这些 API 允许你在幕后处理语言前缀,还支持路径名称的本地化(例如 /en/about/de/ueber-uns,详见文档)。

后面两个功能之前已在小版本中加入,但 3.0 版本整理了 API,并包含了许多改进和 bug 修复。

重大更改

预发布版本用户: 如果你已经尝试过预发布版本,首先,非常感谢!其次:在预发布期间一些 API 经历了迭代,请仔细查看以下重大更改,即使你已经在使用部分新 API。并且请注意将 getTranslator 改为 getTranslations 以提供更好的人机工程学支持异步服务器组件的变化。

更新的配置步骤

当你使用 App Router 时,next-intl 现在需要额外两个配置步骤:

  1. i18n.ts 模块 为服务器组件提供配置
  2. 需要在 next.config.js 中添加 next-intl/plugin,用来链接你的 i18n.ts 模块到 next-intl

App Router 的新导航 API

v2.14 中,useRouterusePathnameLink 导航 API 被添加到 next-intl,使你能够使用惯用的 Next.js API,同时自动处理后台的 locale

3.0 版本中,我们整理了这些 API,将它们移动到共享命名空间中,并且为可以传递给这些 API 的 locale 属性引入了类型安全支持。

- import Link from 'next-intl/link';
- import {useRouter, usePathname} from 'next-intl/client';
- import {redirect} from 'next-intl/server';
 
+ import {createSharedPathnamesNavigation} from 'next-intl/navigation';
+
+ const locales = ['en', 'de'] as const;
+ const {Link, useRouter, usePathname, redirect} = createSharedPathnamesNavigation({locales});

通常,你会想在应用的一个中央位置调用此工厂函数,以方便导入(参见导航文档)。

这些变化使现有 API 与新的 createLocalizedPathnamesNavigation API 保持一致,该 API 允许你对路径名称进行本地化:

navigation.ts
import {
  createLocalizedPathnamesNavigation,
  Pathnames
} from 'next-intl/navigation';
 
export const locales = ['en', 'de'] as const;
 
// `pathnames` 对象保存了内部路径和外部路径的映射,按语言区分。
export const pathnames = {
  // 如果所有语言使用同一路径,则可提供单一外部路径。
  '/': '/',
  '/blog': '/blog',
 
  // 如果不同语言使用不同路径,可以
  // 为每种语言指定外部路径。
  '/about': {
    en: '/about',
    de: '/ueber-uns'
  }
} satisfies Pathnames<typeof locales>;
 
export const {Link, redirect, usePathname, useRouter} =
  createLocalizedPathnamesNavigation({locales, pathnames});

使用类似的 API,你可以通过替换工厂函数,将共享路径名升级为本地化路径名。

中间件 localePrefix 默认值切换为 always

之前,中间件的 localePrefix 默认值是 as-needed,意味着仅为非默认语言添加语言前缀。

现在,这一默认改为 always,原因有两个:

  1. 我们可以推荐一个更安全的默认 matcher,无需特别处理包含点号的路径名(如 /users/jane.doe
  2. 避免了 Link 的一个边界情况:服务器端会为默认语言包含前缀,但在客户端侧通过移除该前缀来修复(某些 SEO 工具可能报告链接指向重定向的提示)。

如果你想继续使用 as-needed 策略,可以在中间件里配置该选项

服务器组件的静态渲染

新增的服务器组件支持带来了对静态渲染的临时解决方案。

如果你在服务器组件中调用 next-intl 的 API,页面默认将选择动态渲染。这是我们未来计划移除的限制,但作为权宜之计,我们添加了 unstable_setRequestLocale API,让你可以保持页面的完全静态渲染。

请注意,如果你仅在客户端组件中使用 next-intl,则此限制不适用,静态渲染仍按原样工作。

NextIntlClientProvider 的变更

首先引入此组件的方式变更:

- import {NextIntlClientProvider} from 'next-intl/client';
+ import {NextIntlClientProvider} from 'next-intl';

根据是否结合使用 App Router,使用该组件时还须考虑下述变更,这是为了避免服务端与客户端中的水合不匹配。

在未使用 App Router 的情况下使用 NextIntlClientProvider

如果你在 未使用 App Router 的情况下(例如使用 Pages Router)使用 NextIntlClientProvider,现在需要显式定义 locale 属性(比如通过 useRouter().locale)。同时,如果未定义 timeZone,将会产生警告。

在使用 App Router 的情况下使用 NextIntlClientProvider

如果你在 使用 App Router 时渲染 NextIntlClientProvider 于服务器组件中,组件将为客户端传递默认的 localenowtimeZone

如果你已经提供了这些选项,就无需改变。

如果没有,请注意这会默认使对应页面进入动态渲染。如果你的应用依赖静态渲染,可以通过传入所有这些属性来避免:

<NextIntlClientProvider
  messages={messages}
  // 通过显式提供这些属性,
  // provider 可以静态渲染。
  timeZone="Europe/Vienna"
  now={new Date()}
  locale={locale}
>
  ...
</NextIntlClientProvider>

另外,你也可以显式启用静态渲染——请参见前一节

其他重要变化

  1. next-intl 现在使用 package.jsonexports 明确导出模块。除非你之前导入了未文档化的内部模块,否则不会受到影响。
  2. NextIntlProvider 被移除,推荐使用 NextIntlClientProvider
  3. 中间件 现在需从 next-intl/middleware 导入,而非之前的 next-intl/server(自 v2.14 起已废弃)。
  4. 使用服务器组件相关 API 需要 next@^13.4。Pages Router 集成通过 use-intl 仍支持 Next.js 10–12(另见:旧版本 Next.js 支持)。
  5. useMessages 现在返回非空类型,便于使用,且如果未配置消息会抛出错误。
  6. createTranslator(…).rich 现在返回 ReactNode。之前存在一些混淆,因为 t.rich 根据是通过 useTranslations 还是 createTranslator 获得,接受并返回的是 React 元素或字符串。现在,添加了显式的 t.markup 函数,用于在 React 组件外生成类似 '<b>Hello</b>' 的标记字符串。
  7. useIntl 已被 useFormatter 替代(自 v2.11 起废弃)。
  8. createIntl 已被 createFormatter 替代(自 v2.11 起废弃)。
  9. useLocale 现在需要 next@>=13.3.0。如果你使用较旧版本的 Next.js 且用 Pages Router,请改用 useRouter().locale

立刻升级

npm install next-intl@latest

感谢

此次发布是团队的共同努力成果,离不开社区的积极参与。

特别感谢大家:

  • 一路上的鼓励与支持
  • 试用预发布版本
  • 提供反馈意见
  • 贡献代码
  • 质疑和讨论想法
  • 相互帮助

很高兴在此过程中与大家交流,我非常感激我们社区里大家愿意互相帮助和支持。

参与拉取请求 #149 讨论的成员们。

特别感谢 Crowdin,作为 next-intl 的主要赞助商,使我得以定期腾出时间专注于此项目。

—Jan

(本文从初次发布的 3.0 候选版本公告更新而来)


Let’s keep in touch: