Next.js 13配置react-i18next实现组件本地化遇错求助
问题背景
我正在用Next.js 13的App Router,想通过react-i18next实现组件的服务端+客户端双端本地化,预期组件代码如下:
import { withTranslation } from 'react-i18next'; const MyComponent = () => { return <p>{t('msg')}</p> } export default MyComponent;
想要的目录结构是:
app |------ about |------ home components |------- MyComponent locale |------- en | |---- MyComponent.json |------- de | |---- MyComponent.json
核心需求是翻译文件存在locale文件夹,不使用app/[lang]/page这种路由式结构。请问这在Next.js 13中可行吗?是不是必须用路由式结构?
另外我已经装了react-i18next和i18next,写了i18n.js配置:
import i18n from "i18next"; import { initReactI18next } from "react-i18next"; const resources = { en: { translation: { "msg": "Welcome to React and react-i18next" } }, fr: { translation: { "msg": "Bienvenue à React et react-i18next" } } }; i18n .use(initReactI18next) .init({ resources, lng: "en", interpolation: { escapeValue: false } }); export default i18n;
运行后报错:
./src/app/i18n.js ReactServerComponentsError: You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default. ,-[/Users/mnd/dev/node_modules/react-i18next/dist/es/context.js:1:1] 1 | import { createContext } from 'react'; : ^^^^^^^^^^^^^ 2 | import { getDefaults, setDefaults } from './defaults.js'; 3 | import { getI18n, setI18n } from './i18nInstance.js'; 4 | import { initReactI18next } from './initReactI18next.js'; `---- The error was caused by importing 'react-i18next/dist/es/index.js' in './src/app/i18n.js'. Maybe one of these should be marked as a client entry with "use client": ./src/app/i18n.js ./src/app/page.tsx
完全不知道怎么解决,看起来Next.js 13的服务端组件不支持context,请问怎么按我的需求用react-i18next?
解决方案
1. 可行性说明
完全可行,不需要强制使用app/[lang]/page结构。Next.js 13的本地化方案不止路由式一种,你可以通过react-i18next结合客户端状态、cookie或localStorage来管理当前语言,完全符合你的目录结构需求。
2. 解决createContext错误
Next.js 13 App Router默认所有组件都是服务端组件,而react-i18next依赖React Context,所以必须在涉及的文件顶部添加'use client'指令,标记为客户端组件。
调整i18n配置文件
修改app/i18n.js,添加'use client',并正确导入locale目录的翻译文件:
'use client'; // 必须添加这一行,标记为客户端组件 import i18n from "i18next"; import { initReactI18next } from "react-i18next"; // 导入各语言下的组件翻译文件 import enMyComponent from '../locale/en/MyComponent.json'; import deMyComponent from '../locale/de/MyComponent.json'; const resources = { en: { MyComponent: enMyComponent // 用组件名作为命名空间 }, de: { MyComponent: deMyComponent } }; i18n .use(initReactI18next) .init({ resources, lng: "en", // 默认语言 ns: ['MyComponent'], // 指定需要加载的命名空间 defaultNS: 'MyComponent', // 默认使用的命名空间 interpolation: { escapeValue: false // React已内置XSS防护 } }); export default i18n;
改造组件代码
推荐使用useTranslation钩子替代withTranslation高阶组件,同时组件必须标记为客户端组件:
'use client'; // 标记为客户端组件 import { useTranslation } from 'react-i18next'; const MyComponent = () => { const { t } = useTranslation('MyComponent'); // 指定对应命名空间 return <p>{t('msg')}</p>; }; export default MyComponent;
3. 全局注入i18n配置
在app/layout.jsx中导入i18n配置,确保全局生效:
import './i18n'; // 导入i18n配置 import './globals.css'; export default function RootLayout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> ); }
4. 实现语言切换
如果需要切换语言,在客户端组件中调用i18n.changeLanguage即可:
'use client'; import { useTranslation } from 'react-i18next'; const LanguageSwitcher = () => { const { i18n } = useTranslation(); return ( <div> <button onClick={() => i18n.changeLanguage('en')}>English</button> <button onClick={() => i18n.changeLanguage('de')}>Deutsch</button> </div> ); }; export default LanguageSwitcher;
关键注意点
- 服务端组件限制:服务端组件无法使用React Context,所以所有依赖react-i18next的组件必须标记为
'use client'。 - 命名空间优势:用组件名作为命名空间,和你
locale/[lang]/[Component].json的目录结构完美匹配,便于维护不同组件的翻译。 - 语言持久化:如果需要刷新页面后保留当前语言,可以结合localStorage或cookie,在i18n初始化时读取对应值设置
lng。
内容的提问来源于stack exchange,提问作者Mendes

