Next.js13/14 App目录无需i18n实现多语言方案咨询
仅2页面的Next.js 13/14 App目录站点,无需官方i18n也能实现多语言
完全可以做到,以下是针对你的场景的落地步骤,解决JSON语言包的加载与渲染问题:
步骤1:创建语言JSON文件
在app目录下新建lang文件夹,分别创建两个语言配置文件:
app/lang/en.json:
{ "home": { "title": "Welcome Home", "description": "This is the home page" }, "about": { "title": "About Us", "description": "Learn more about our site" } }
app/lang/fr.json:
{ "home": { "title": "Bienvenue à la maison", "description": "Ceci est la page d'accueil" }, "about": { "title": "À propos de nous", "description": "En savoir plus sur notre site" } }
步骤2:封装语言加载工具
在app目录下创建utils/language.ts(或.js),写一个动态加载对应语言包的函数:
export async function getLanguageStrings(lang: 'en' | 'fr') { try { const langModule = await import(`../lang/${lang}.json`); return langModule.default; } catch (error) { // 加载失败默认回退到英文 const enModule = await import('../lang/en.json'); return enModule.default; } }
步骤3:结合上下文渲染多语言文本
你的语言检测脚本是客户端运行的,所有依赖语言状态的组件都需要标记为客户端组件,以下是具体实现:
3.1 创建语言上下文组件
新建app/components/LangProvider.tsx:
'use client'; import { createContext, useContext, useEffect, useState } from 'react'; type LangContextType = { lang: 'en' | 'fr'; setLang: (lang: 'en' | 'fr') => void; }; const LangContext = createContext<LangContextType | undefined>(undefined); export function LangProvider({ children }: { children: React.ReactNode }) { const [lang, setLang] = useState<'en' | 'fr'>('en'); useEffect(() => { // 关联你根布局中的全局语言监听 window.__onThemeChange = (newLang) => { setLang(newLang as 'en' | 'fr'); }; // 初始化语言值 setLang(window.__theme as 'en' | 'fr'); }, []); const handleSetLang = (newLang: 'en' | 'fr') => { window.__setPreferredTheme(newLang); setLang(newLang); }; return ( <LangContext.Provider value={{ lang, setLang: handleSetLang }}> {children} </LangContext.Provider> ); } export function useLang() { const context = useContext(LangContext); if (!context) { throw new Error('useLang must be used within a LangProvider'); } return context; }
3.2 在根布局中引入Provider
修改app/layout.tsx,将LangProvider添加到根组件外层(根布局需标记为客户端组件):
'use client'; import { LangProvider } from './components/LangProvider'; import './globals.css'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <head> {/* 保留你原有的语言检测脚本 */} <script async dangerouslySetInnerHTML={{ __html:(function () { function setTheme(newTheme) { window.__theme = newTheme; window.__onThemeChange(newTheme); } window.__onThemeChange = function () {}; window.__setPreferredTheme = function (newTheme) { setTheme(newTheme); try { localStorage.setItem("lang", JSON.stringify(window.__theme)); } catch (err) {} }; var darkQuery = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage) darkQuery = darkQuery.split('-')[0]; var preferredTheme; try { preferredTheme = JSON.parse(localStorage.getItem("lang")); } catch (err) {} setTheme(preferredTheme || darkQuery); })() }} /> </head> <body> <LangProvider>{children}</LangProvider> </body> </html> ); }
3.3 在页面中使用语言包
以app/page.tsx(首页)为例,改成客户端组件并渲染对应语言文本:
'use client'; import { useLang } from './components/LangProvider'; import { getLanguageStrings } from './utils/language'; import { useEffect, useState } from 'react'; export default function Home() { const { lang } = useLang(); const [strings, setStrings] = useState<any>(null); useEffect(() => { const loadStrings = async () => { const langStrings = await getLanguageStrings(lang); setStrings(langStrings.home); }; loadStrings(); }, [lang]); if (!strings) return <div>Loading...</div>; return ( <div> <h1>{strings.title}</h1> <p>{strings.description}</p> {/* 可选:添加语言切换按钮 */} <button onClick={() => useLang().setLang(lang === 'en' ? 'fr' : 'en')}> Switch to {lang === 'en' ? 'French' : 'English'} </button> </div> ); }
app/about/page.tsx可复用相同逻辑,加载langStrings.about的内容即可。
关键注意事项
- 所有依赖语言状态的组件必须标记
'use client';,因为语言检测是客户端行为 - 若想避免首次加载闪烁,可在根布局脚本中同步设置HTML的
lang属性,或用Suspense包裹页面组件 - 确保JSON文件名与语言值(
en/fr)严格匹配,避免动态导入出错
内容的提问来源于stack exchange,提问作者Canadian Lonman
相关产品推荐
相关产品推荐

