You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:09:55