Skip to content
文档环境错误文件(例如未找到)

Next.js 错误文件中的国际化

Next.js App Router 的文件约定提供了两个可用于错误处理的文件:

  1. not-found.js
  2. error.js

本页提供了这些情况的实用指南。

提示: 你可以查看 App Router 示例 来探索包含错误处理功能的示例应用。

not-found.js

💡

本节仅在你使用基于语言环境的路由时相关。

当路由段调用 notFound 函数时,Next.js 会渲染最近的 not-found 页面。我们可以利用此机制,在 [locale] 文件夹内添加一个 not-found 文件来提供本地化的 404 页面。

app/[locale]/not-found.tsx
import {useTranslations} from 'next-intl';
 
export default function NotFoundPage() {
  const t = useTranslations('NotFoundPage');
  return <h1>{t('title')}</h1>;
}

但需要注意的是,Next.js 仅当路由内部调用了 notFound 函数时才会渲染这个页面,而不是针对所有未知路由。

捕获未知路由

若要捕获未知路由,你可以定义一个捕获所有的路由,并显式调用 notFound 函数。

app/[locale]/[...rest]/page.tsx
import {notFound} from 'next/navigation';
 
export default function CatchAllPage() {
  notFound();
}

此更改后,所有 [locale] 段内匹配的请求在遇到未知路由时都会渲染 not-found 页面(例如 /en/unknown)。

捕获非本地化请求

当用户请求的路由未被 next-intl middleware 匹配时,请求没有关联的语言环境(取决于你的 matcher 配置,例如 /unknown.txt 可能不会被匹配到)。

你可以添加一个根目录的 not-found 页面来处理这些情况。

app/not-found.tsx
'use client';
 
import Error from 'next/error';
 
export default function NotFound() {
  return (
    <html lang="en">
      <body>
        <Error statusCode={404} />
      </body>
    </html>
  );
}

注意,存在 app/not-found.tsx 文件时,根布局是必需的,即使它只是简单地透传 children

app/layout.tsx
// 由于我们有根目录的 `not-found.tsx` 页面,
// 所以必须定义布局文件,即使只是简单地透传 children。
export default function RootLayout({children}) {
  return children;
}

为了使 404 页面渲染,我们需要在根布局中检测到传入的 locale 参数无效时调用 notFound 函数。

app/[locale]/layout.tsx
import {hasLocale} from 'next-intl';
import {notFound} from 'next/navigation';
import {routing} from '@/i18n/routing';
 
export default function LocaleLayout({children, params}) {
  const {locale} = await params;
  if (!hasLocale(routing.locales, locale)) {
    notFound();
  }
 
  // ...
}

error.js

当定义了 error 文件时,Next.js 会在你的布局内创建一个错误边界,以相应地包裹页面捕获运行时错误:

<RootLayout>
  <ErrorBoundary fallback={<Error />}>
    <Page />
  </ErrorBoundary>
</RootLayout>

Next.js 内部创建的组件层级示意。

由于 error 文件必须定义为客户端组件,你需要使用 NextIntlClientProvider 来在渲染时提供翻译消息:

layout.tsx
import pick from 'lodash/pick';
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
 
export default async function RootLayout(/* ... */) {
  const messages = await getMessages();
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider
          locale={locale}
          // 确保至少提供 `Error` 的翻译消息
          messages={pick(messages, 'Error')}
        >
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

一旦引入了 NextIntlClientProvider,你就可以在 error 文件中使用 next-intl 的功能:

error.tsx
'use client';
 
import {useTranslations} from 'next-intl';
 
export default function Error({error, reset}) {
  const t = useTranslations('Error');
 
  return (
    <div>
      <h1>{t('title')}</h1>
      <button onClick={reset}>{t('retry')}</button>
    </div>
  );
}

需注意,error.tsx 会在应用初始化后立即加载。如果你的应用对性能敏感,且想避免将 next-intl 的翻译功能包含进这个包中,可以从 error 文件导出一个延迟加载的引用:

error.tsx
'use client';
 
import {lazy} from 'react';
 
// 将错误内容拆分到单独的 chunk,仅在需要时加载
export default lazy(() => import('./Error'));