开始使用 Intlayer 和 Next.js 的页面路由进行国际化 (i18n)

    什么是 Intlayer?

    Intlayer 是一个创新的开源国际化 (i18n) 库,旨在简化现代 Web 应用程序中的多语言支持。Intlayer 无缝集成到最新的 Next.js 框架中,包括其传统的 页面路由

    使用 Intlayer,您可以:

    • 轻松管理翻译,在组件级别使用声明式字典。
    • 动态本地化元数据、路由和内容。
    • 确保 TypeScript 支持,通过自动生成类型提高自动补全和错误检测。
    • 享受高级功能,如动态语言检测和切换。

    Intlayer 兼容 Next.js 12、13、14 和 15。如果您使用的是 Next.js App Router,请参考 App Router 指南。对于 Next.js 15,请遵循此 指南


    使用页面路由在 Next.js 应用程序中设置 Intlayer 的分步指南

    第一步:安装依赖

    使用您喜欢的包管理器安装必要的包:

    bash
    npm install intlayer next-intlayer
    • intlayer

      提供国际化工具的核心包,用于配置管理、翻译、内容声明、转译和 CLI 命令

    • next-intlayer

      将 Intlayer 与 Next.js 集成的包。它提供了用于 Next.js 国际化的上下文提供者和钩子。此外,它还包括用于将 Intlayer 集成到 WebpackTurbopack 的 Next.js 插件,以及用于检测用户首选语言、管理 Cookie 和处理 URL 重定向的中间件。

    第二步:配置您的项目

    创建一个配置文件来定义您的应用程序支持的语言:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // 在此添加您的其他语言    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

    通过此配置文件,您可以设置本地化 URL、中间件重定向、Cookie 名称、内容声明的位置和扩展名、禁用控制台中的 Intlayer 日志等。有关可用参数的完整列表,请参考 配置文档

    第三步:将 Intlayer 集成到 Next.js 配置中

    修改您的 Next.js 配置以集成 Intlayer:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {  // 您现有的 Next.js 配置};export default withIntlayer(nextConfig);

    withIntlayer() Next.js 插件用于将 Intlayer 集成到 Next.js 中。它确保构建内容声明文件并在开发模式下监视它们。它在 WebpackTurbopack 环境中定义 Intlayer 环境变量。此外,它提供了优化性能的别名,并确保与服务器组件的兼容性。

    第四步:配置用于语言检测的中间件

    设置中间件以自动检测和处理用户的首选语言:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = {  matcher:    "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};

    根据您的应用程序路由调整 matcher 参数。有关更多详细信息,请参考 Next.js 文档关于配置 matcher 的部分

    第五步:定义动态语言路由

    实现动态路由以根据用户的语言提供本地化内容。

    1. 创建特定语言的页面:

      将您的主页面文件重命名为包含 [locale] 动态段。

      bash
      mv src/pages/index.tsx src/pages/[locale]/index.tsx
    2. 更新 _app.tsx 以处理本地化:

      修改您的 _app.tsx 以包含 Intlayer 提供者。

      src/pages/_app.tsx
      import type { FC } from "react";import type { AppProps } from "next/app";import { IntlayerClientProvider } from "next-intlayer";const App = FC<AppProps>({ Component, pageProps }) => {  const { locale } = pageProps;  return (    <IntlayerClientProvider locale={locale}>      <Component {...pageProps} />    </IntlayerClientProvider>  );}export default MyApp;
    3. 设置 getStaticPathsgetStaticProps

      在您的 [locale]/index.tsx 中,定义路径和属性以处理不同的语言。

      src/pages/[locale]/index.tsx
      import type { FC } from "react";import type { GetStaticPaths, GetStaticProps } from "next";import { type Locales, getConfiguration } from "intlayer";

    const HomePage: FC = () =>

    {/ 您的内容在这里 /}
    ;

    export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, }; }; export default HomePage; ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getConfiguration } from "intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () => <div>{/* 您的内容在这里 */}</div>; export const getStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps = ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getConfiguration } = require("intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () => <div>{/* 您的内容在这里 */}</div>; const getStaticPaths = async () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; const getStaticProps = async ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; ```

    getStaticPathsgetStaticProps 确保您的应用程序为 Next.js 页面路由中的所有语言环境预先构建必要的页面。这种方法减少了运行时计算,并改善了用户体验。有关更多详细信息,请参阅 Next.js 文档中的 getStaticPathsgetStaticProps

    第 6 步:声明您的内容

    创建和管理您的内容声明以存储翻译内容。

    src/pages/[locale]/home.content.ts
    import { t, type Dictionary } from "intlayer";const homeContent = {  key: "home",  content: {    title: t({      zh: "欢迎来到我的网站",      en: "Welcome to My Website",      fr: "Bienvenue sur mon site Web",      es: "Bienvenido a mi sitio web",    }),    description: t({      zh: "通过编辑此页面开始。",      en: "Get started by editing this page.",      fr: "Commencez par éditer cette page.",      es: "Comience por editar esta página.",    }),  },} satisfies Dictionary;export default homeContent;

    有关声明内容的更多信息,请参阅 内容声明指南

    第 7 步:在代码中使用内容

    在整个应用程序中访问您的内容字典以显示翻译内容。

    src/pages/[locale]/index.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => {  const content = useIntlayer("home");  return (    <div>      <h1>{content.title}</h1>      <p>{content.description}</p>      <ComponentExample />      {/* 其他组件 */}    </div>  );};// ... 其余代码,包括 getStaticPaths 和 getStaticPropsexport default HomePage;
    src/components/ComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ComponentExample: FC = () => {  const content = useIntlayer("component-example"); // 确保您有相应的内容声明  return (    <div>      <h2>{content.title}</h2>      <p>{content.content}</p>    </div>  );};

    (可选)步骤 9:更改内容的语言

    要允许用户动态切换语言,请使用 useLocale 钩子提供的 setLocale 函数。

    src/components/LanguageSwitcher.tsx
    import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocalePageRouter();  return (    <div>      <button popoverTarget="localePopover">{getLocaleName(locale)}</button>      <div id="localePopover" popover="auto">        {availableLocales.map((localeItem) => (          <Link            href={getLocalizedUrl(pathWithoutLocale, localeItem)}            hrefLang={localeItem}            key={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);            }}          >            <span>              {/* 语言代码 - 例如 FR */}              {localeItem}            </span>            <span>              {/* 语言名称(本地化) - 例如 Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* 当前语言环境下的语言名称 - 例如 Francés(当前语言环境为 Locales.SPANISH) */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* 英文中的语言名称 - 例如 French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

    useLocalePageRouter API 与 useLocale 相同。要了解更多关于 useLocale 钩子的内容,请参考文档

    文档参考:

    (可选)步骤 10:创建一个本地化的链接组件

    为了确保您的应用程序导航符合当前语言环境,您可以创建一个自定义的 Link 组件。此组件会自动为内部 URL 添加当前语言的前缀。例如,当一个讲法语的用户点击指向“关于”页面的链接时,他们会被重定向到 /fr/about 而不是 /about

    这种行为有以下几个好处:

    • SEO 和用户体验:本地化的 URL 有助于搜索引擎正确索引特定语言的页面,并为用户提供其首选语言的内容。
    • 一致性:通过在整个应用程序中使用本地化链接,可以确保导航保持在当前语言环境中,避免意外的语言切换。
    • 可维护性:将本地化逻辑集中在一个组件中,简化了 URL 的管理,使您的代码库更易于维护和扩展。

    以下是一个使用 TypeScript 实现的本地化 Link 组件:

    src/components/Link.tsx
    "use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * 检查给定 URL 是否为外部链接的实用函数。 * 如果 URL 以 http:// 或 https:// 开头,则被视为外部链接。 */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:///.test(href ?? "");/** * 自定义 Link 组件,根据当前语言环境调整 href 属性。 * 对于内部链接,它使用 `getLocalizedUrl` 为 URL 添加语言前缀(例如 /fr/about)。 * 这确保了导航保持在相同的语言环境中。 */export const Link = forwardRef<  HTMLAnchorElement,  PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {  const { locale } = useLocale();  const isExternalLink = checkIsExternalLink(href.toString());  // 如果链接是内部链接且提供了有效的 href,则获取本地化的 URL。  const hrefI18n: NextLinkProps["href"] =    href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;  return (    <NextLink href={hrefI18n} ref={ref} {...props}>      {children}    </NextLink>  );});Link.displayName = "Link";

    工作原理

    • 检测外部链接
      辅助函数 checkIsExternalLink 用于判断 URL 是否为外部链接。外部链接保持不变,因为它们不需要本地化。

    • 获取当前语言环境
      useLocale 钩子提供当前语言环境(例如,法语为 fr)。

    • 本地化 URL
      对于内部链接(即非外部链接),使用 getLocalizedUrl 自动为 URL 添加当前语言的前缀。这意味着如果用户使用法语,传递 /about 作为 href 会被转换为 /fr/about

    • 返回链接
      组件返回一个带有本地化 URL 的 <a> 元素,确保导航与语言环境一致。

    配置 TypeScript

    Intlayer 使用模块增强来利用 TypeScript 的优势,使您的代码库更强大。

    alt text

    alt text

    确保您的 TypeScript 配置包含自动生成的类型。

    tsconfig.json
    {  // ... 您现有的 TypeScript 配置  "include": [    // ... 您现有的 TypeScript 配置    ".intlayer/**/*.ts", // 包含自动生成的类型  ],}

    Git 配置

    为了保持您的代码库整洁并避免提交生成的文件,建议忽略由 Intlayer 创建的文件。

    将以下内容添加到您的 .gitignore 文件中:

    .gitignore
    # 忽略由 Intlayer 生成的文件.intlayer

    其他资源

    通过遵循本指南,您可以将 Intlayer 有效集成到您的 Next.js 应用程序中,使用 Page Router 实现强大且可扩展的国际化支持,为您的 Web 项目提供支持。

    更进一步

    若要更进一步,您可以实现 可视化编辑器 或通过 CMS 外部化您的内容。

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

    文档的 GitHub 链接