Next.js 错误文件中的国际化
Next.js App Router 的文件约定提供了两个可用于错误处理的文件:
本页提供了这些情况的实用指南。
提示: 你可以查看 App Router 示例 来探索包含错误处理功能的示例应用。
not-found.js
本节仅在你使用基于语言环境的路由时相关。
当路由段调用 notFound 函数时,Next.js 会渲染最近的 not-found 页面。我们可以利用此机制,在 [locale] 文件夹内添加一个 not-found 文件来提供本地化的 404 页面。
import {useTranslations} from 'next-intl';
export default function NotFoundPage() {
const t = useTranslations('NotFoundPage');
return <h1>{t('title')}</h1>;
}但需要注意的是,Next.js 仅当路由内部调用了 notFound 函数时才会渲染这个页面,而不是针对所有未知路由。
捕获未知路由
若要捕获未知路由,你可以定义一个捕获所有的路由,并显式调用 notFound 函数。
import {notFound} from 'next/navigation';
export default function CatchAllPage() {
notFound();
}此更改后,所有 [locale] 段内匹配的请求在遇到未知路由时都会渲染 not-found 页面(例如 /en/unknown)。
捕获非本地化请求
当用户请求的路由未被 next-intl middleware 匹配时,请求没有关联的语言环境(取决于你的 matcher 配置,例如 /unknown.txt 可能不会被匹配到)。
你可以添加一个根目录的 not-found 页面来处理这些情况。
'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。
// 由于我们有根目录的 `not-found.tsx` 页面,
// 所以必须定义布局文件,即使只是简单地透传 children。
export default function RootLayout({children}) {
return children;
}为了使 404 页面渲染,我们需要在根布局中检测到传入的 locale 参数无效时调用 notFound 函数。
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 来在渲染时提供翻译消息:
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 的功能:
'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 文件导出一个延迟加载的引用:
'use client';
import {lazy} from 'react';
// 将错误内容拆分到单独的 chunk,仅在需要时加载
export default lazy(() => import('./Error'));