Next.js中不使用子路径/域名路由实现i18n的方案咨询
在Next.js中实现无路由切换的i18n(解决服务端/客户端文本不匹配问题)
问题根源
你遇到的Text content does not match server-rendered HTML错误,本质是服务端渲染时用的语言和客户端hydrate时的语言不一致——服务端输出了英文"login",但客户端初始化后切换成了德语"Anmelden",导致DOM不匹配。下面是不用子路径/域名路由的完整解决方案:
1. 安装依赖
直接用i18next生态,不需要Next.js内置的i18n路由:
npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend
2. 配置i18n核心
在src/i18n.js(或项目根目录)创建配置文件,禁用路由相关逻辑,优先从localStorage/cookie读取语言:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-http-backend'; i18n .use(Backend) // 加载public文件夹下的翻译文件 .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: 'en', debug: false, interpolation: { escapeValue: false }, // React自带转义,无需额外处理 detection: { order: ['localStorage', 'cookie', 'navigator'], // 优先级:本地存储 > Cookie > 浏览器语言 caches: ['localStorage', 'cookie'], // 缓存语言选择 }, react: { useSuspense: false }, // 避免SSR时的Suspense阻塞 }); export default i18n;
3. 同步服务端与客户端语言
Next.js是SSR/SSG,服务端无法访问浏览器存储,必须通过Cookie传递用户的语言偏好,保证两端渲染一致。
3.1 改造_app.js
在pages/_app.js中处理服务端语言获取和客户端初始化:
import '../i18n'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; function MyApp({ Component, pageProps, initialLanguage }) { const { i18n } = useTranslation(); const [isHydrated, setIsHydrated] = useState(false); useEffect(() => { setIsHydrated(true); // 客户端初始化时,同步服务端传递的初始语言 if (initialLanguage) i18n.changeLanguage(initialLanguage); }, [i18n, initialLanguage]); // 未完成hydrate前不渲染内容,避免DOM不匹配 if (!isHydrated) return null; return <Component {...pageProps} />; } // 服务端获取初始语言 MyApp.getInitialProps = async ({ ctx }) => { let initialLanguage = 'en'; // 从Cookie读取用户之前的选择 if (ctx.req?.headers.cookie) { const langCookie = ctx.req.headers.cookie.split(';').find(c => c.trim().startsWith('i18next=')); if (langCookie) initialLanguage = langCookie.split('=')[1]; } // 无Cookie时,从请求头获取浏览器偏好语言 else if (ctx.req) { const acceptLang = ctx.req.headers['accept-language']; if (acceptLang) initialLanguage = acceptLang.split(',')[0].split('-')[0]; } return { initialLanguage }; }; export default MyApp;
3.2 实现语言选择器
创建语言切换组件,切换时同步更新Cookie,确保服务端下次渲染能拿到正确语言:
import { useTranslation } from 'react-i18next'; const LanguageSelector = () => { const { i18n } = useTranslation(); const switchLanguage = (lng) => { i18n.changeLanguage(lng); // 设置Cookie,有效期1年 document.cookie = `i18next=${lng}; max-age=31536000; path=/`; }; return ( <div className="language-selector"> <button onClick={() => switchLanguage('en')}>English</button> <button onClick={() => switchLanguage('de')}>Deutsch</button> </div> ); }; export default LanguageSelector;
4. 存放翻译文件
在public/locales文件夹下创建对应语言的翻译文件:
public/locales/en/translation.json
{ "login": "Login" }
public/locales/de/translation.json
{ "login": "Anmelden" }
5. 在组件中使用翻译
正常调用useTranslation获取翻译函数:
import { useTranslation } from 'react-i18next'; const LoginButton = () => { const { t } = useTranslation(); return <button>{t('login')}</button>; }; export default LoginButton;
内容的提问来源于stack exchange,提问作者Ankit Panchal
相关产品推荐
相关产品推荐

