Next.js 国际化 (i18n) 与 next-intl 和 Intlayer

    next-intl 和 Intlayer 是为 Next.js 应用程序设计的开源国际化 (i18n) 框架。它们广泛用于管理软件项目中的翻译、本地化和语言切换。

    它们共享三个主要概念:

    1. 内容声明: 定义应用程序可翻译内容的方法。

      • 在 Intlayer 中称为 content declaration file,可以是导出结构化数据的 JSON、JS 或 TS 文件。有关更多信息,请参见 Intlayer 文档
      • 在 next-intl 中称为 messageslocale messages,通常在 JSON 文件中。有关更多信息,请参见 next-intl 文档
    2. 工具: 用于构建和解释应用程序中的内容声明的工具,例如 Intlayer 的 useIntlayer()useLocale(),以及 next-intl 的 useTranslations()

    3. 插件和中间件: 用于管理 URL 重定向、打包优化等功能——例如,Intlayer 的 intlayerMiddleware 或 next-intl 的 createMiddleware

    Intlayer 与 next-intl: 关键区别

    要深入分析 Intlayer 如何与其他 i18n 库(如 next-intl)进行比较,请查看 next-i18next vs. next-intl vs. Intlayer 博客文章

    如何生成 next-intl 消息与 Intlayer

    为什么使用 Intlayer 与 next-intl?

    Intlayer 内容声明文件通常提供更好的开发者体验。由于两个主要优点,它们更加灵活和易于维护:

    1. 灵活的位置: 你可以将 Intlayer 内容声明文件放置在应用程序文件树的任何位置。这使得重命名或删除组件变得简单,而不会留下未使用或悬挂的消息文件。

      示例文件结构:

      bash
      .└── src    └── components        └── MyComponent            ├── index.content.ts # 内容声明文件            └── index.tsx
    2. 集中翻译: Intlayer 将所有翻译存储在一个内容声明中,确保没有翻译被遗漏。在 TypeScript 项目中,缺失翻译会自动被标记为类型错误,为开发者提供即时反馈。

    安装

    要一起使用 Intlayer 和 next-intl,请安装这两个库:

    bash
    npm install intlayer next-intl

    配置 Intlayer 导出 next-intl 消息

    注意: 从 Intlayer 导出消息到 next-intl 可能会引入结构上的细微差异。如果可能,保持 Intlayer 或 next-intl 单独的流程以简化集成。如果确实需要从 Intlayer 生成 next-intl 消息,请遵循以下步骤。

    在项目根目录中创建或更新一个 intlayer.config.ts 文件(或 .mjs / .cjs):

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  content: {    dictionaryOutput: ["next-intl"], // 使用 next-intl 输出    nextIntlMessagesDir: "./intl/messages", // 保存 next-intl 消息的位置  },};export default config;

    内容声明

    以下是多个格式的内容声明文件示例。Intlayer 将把这些编译成 next-intl 可以使用的消息文件。

    **/*.content.ts
    import { t, type DeclarationContent } from "intlayer";const content = {  key: "my-component",  content: {    helloWorld: t({      en: "Hello World",      fr: "Bonjour le monde",      es: "Hola Mundo",    }),  },} satisfies DeclarationContent;export default content;

    构建 next-intl 消息

    要构建 next-intl 的消息文件,请运行:

    bash
    npx intlayer build

    这将在 ./intl/messages 目录中生成资源(如 intlayer.config.* 中配置的)。预期输出:

    bash
    .└── intl    └── messages       └── zh           └── my-content.json       └── fr           └── my-content.json       └── es           └── my-content.json

    每个文件包含来自所有 Intlayer 内容声明的编译消息。顶级键通常与 content.key 字段匹配。

    在你的 Next.js 应用中使用 next-intl

    欲了解更多详细信息,请参阅官方 next-intl 使用文档

    1. 创建中间件 (可选):
      如果你想管理自动语言检测或重定向,请使用 next-intl 的 createMiddleware

      middleware.ts
      import createMiddleware from "next-intl/middleware";import { NextResponse } from "next/server";export default createMiddleware({  locales: ["en", "fr", "es"],  defaultLocale: "en",});export const config = {  matcher: ["/((?!api|_next|.*\\..*).*)"],};
    2. 创建一个 layout.tsx_app.tsx 来加载消息:
      如果你正在使用 App Router(Next.js 13+),请创建一个布局:

      app/[locale]/layout.tsx
      import { NextIntlClientProvider } from 'next-intl';import { notFound } from 'next/navigation';import React, { ReactNode } from 'react';export const dynamic = 'force-dynamic';export default async function RootLayout({  children,  params}: {  children: ReactNode;  params: { locale: string };}) {  let messages;  try {    messages = (await import(`../../intl/messages/${params.locale}.json`)).default;  } catch (error) {    notFound();  }  return (    <html lang={params.locale}>      <body>        <NextIntlClientProvider locale={params.locale} messages={messages}>          {children}        </NextIntlClientProvider>      </body>    </html>  );}

      如果你正在使用 Pages Router(Next.js 12 或更早),请在 _app.tsx 中加载消息:

      pages/_app.tsx
      import type { AppProps } from 'next/app';import { NextIntlProvider } from 'next-intl';function MyApp({ Component, pageProps }: AppProps) {  return (    <NextIntlProvider locale={pageProps.locale} messages={pageProps.messages}>      <Component {...pageProps} />    </NextIntlProvider>  );}export default MyApp;
    3. 在服务器端获取消息 (Pages Router 示例):

      pages/index.tsx
      import { GetServerSideProps } from "next";import HomePage from "../components/HomePage";export default HomePage;export const getServerSideProps: GetServerSideProps = async ({ locale }) => {  const messages = (await import(`../intl/messages/${locale}.json`)).default;  return {    props: {      locale,      messages,    },  };};

    在 Next.js 组件中使用内容

    一旦消息被加载到 next-intl 中,你可以通过 useTranslations() 钩子在组件中使用它们:

    src/components/MyComponent/index.tsx
    import type { FC } from "react";import { useTranslations } from 'next-intl';const MyComponent: FC = () => {  const t = useTranslations('my-component');  // 'my-component' 对应于 Intlayer 中的内容键  return (    <div>      <h1>{t('helloWorld')}</h1>    </div>  );};export default MyComponent;

    就这些! 每当你更新或添加新的 Intlayer 内容声明文件时,重新运行 intlayer build 命令以重新生成你的 next-intl JSON 消息。next-intl 将自动获取更新的内容。

    如果您有改善此文档的想法,请随时通过在GitHub上提交拉取请求来贡献。

    博客的 GitHub 链接