加载翻译
使用 provider 管理消息
问题
将诸如 'Hello World' 这样的文本直接硬编码到应用组件中,会导致内容与代码耦合。要显示不同的语言,开发者必须复制组件或添加 if/else 逻辑,这使得翻译难以扩展,并且每增加一段新文本都需要修改代码。
解决方案
使用 react-intl 的 IntlProvider 提供翻译。在服务端的根布局中加载翻译消息,将其传递给客户端的 provider 组件,然后在其他客户端组件中通过 useIntl hook 获取。
步骤
1. 安装 react-intl
首先,将 react-intl 作为依赖添加到你的项目中。
npm install react-intl
2. 创建扁平化翻译文件
创建一个 dictionaries 文件夹。在其中为每种语言添加一个 JSON 文件。react-intl 最适合使用扁平的键值结构。
// dictionaries/en.json
{
"home.title": "Home Page",
"home.welcome": "Hello, welcome to our site!",
"about.title": "About Us"
}
// dictionaries/es.json
{
"home.title": "Página de Inicio",
"home.welcome": "¡Hola, bienvenido a nuestro sitio!",
"about.title": "Sobre Nosotros"
}
3. 创建加载字典的函数
创建一个辅助函数,根据 lang 参数在服务端加载对应的字典文件。
// app/get-dictionary.ts
import 'server-only';
// Define the type for our flat message object
type Messages = Record<string, string>;
const dictionaries: { [key: string]: () => Promise<Messages> } = {
en: () => import('@/dictionaries/en.json').then((module) => module.default),
es: () => import('@/dictionaries/es.json').then((module) => module.default),
// fr: () => import('@/dictionaries/fr.json').then((module) => module.default),
};
export const getDictionary = async (lang: string) => {
const load = dictionaries[lang];
if (load) {
return load();
}
// Fallback to English
return dictionaries.en();
};
4. 创建客户端 provider
IntlProvider 是一个使用 React Context 的客户端组件。我们需要为其创建一个包装器,以便能够接收从服务端加载的消息。
// app/components/IntlClientProvider.tsx
'use client';
import { IntlProvider } from 'react-intl';
type Props = {
children: React.ReactNode;
locale: string;
messages: Record<string, string>; // Flat messages object
};
export default function IntlClientProvider({
children,
locale,
messages,
}: Props) {
return (
<IntlProvider messages={messages} locale={locale} defaultLocale="en">
{children}
</IntlProvider>
);
}
5. 更新根布局
将 app/[lang]/layout.tsx 修改为 async 组件。它会加载消息并将其传递给 IntlClientProvider。
// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import IntlClientProvider from '@/app/components/IntlClientProvider';
export async function generateStaticParams() {
return [{ lang: 'en' }, { lang: 'es' }];
}
export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: string };
}) {
// Load messages on the server
const messages = await getDictionary(params.lang);
return (
<html lang={params.lang}>
<body>
{/* Pass messages to the client provider */}
<IntlClientProvider locale={params.lang} messages={messages}>
{children}
</IntlClientProvider>
</body>
</html>
);
}
6. 在客户端组件中使用翻译
现在,您可以在任何客户端组件中使用 useIntl hook。服务端组件无法使用此 hook。
创建一个新的客户端组件以显示翻译文本:
// app/components/HomePageContent.tsx
'use client';
import { useIntl } from 'react-intl';
export default function HomePageContent() {
const intl = useIntl();
return (
<div>
<h1>{intl.formatMessage({ id: 'home.title' })}</h1>
<p>{intl.formatMessage({ id: 'home.welcome' })}</p>
</div>
);
}
7. 将组件添加到页面中
最后,将新的客户端组件添加到您的页面。
// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';
export default function Home() {
// This page is a Server Component
return (
<div>
{/* It renders the Client Component */}
<HomePageContent />
</div>
);
}