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

Next.js 13 App Router嵌套布局下i18n字典传递至子组件方案

Next.js 13 App Router 国际化字典传递方案

问题背景

基于Next.js 13新/app路由的项目,根布局src/app/[lang]/layout.tsx可通过路由参数lang获取i18n字典,但无法将该字典传递给子布局(src/app/[lang]/(main)/layout.tsx)及Header等组件,需解决这一传递问题。

解决方案

方案1:React Context 全局传递(推荐)

利用React Context实现字典的全局共享,适合组件层级较多的场景。

步骤1:创建I18n Context组件

新建src/components/I18nContext.tsx:

'use client';

import { createContext, useContext } from 'react';
import type { I18nDictionary } from '@/i18n';

const I18nContext = createContext<I18nDictionary | null>(null);

export const I18nProvider = ({ children, dictionary }: { children: React.ReactNode; dictionary: I18nDictionary }) => {
  return <I18nContext.Provider value={dictionary}>{children}</I18nContext.Provider>;
};

export const useI18n = () => {
  const context = useContext(I18nContext);
  if (!context) {
    throw new Error('useI18n必须在I18nProvider内部使用');
  }
  return context;
};

步骤2:修改根布局,包裹Provider

更新src/app/[lang]/layout.tsx:

import React from 'react';
import { getI18nDictionary } from '@/i18n';
import { I18nProvider } from '@/components/I18nContext';

type Props = {
  children: React.ReactNode;
  params: {
    lang: string;
  };
};

const AppLayout = async ({ children, params }: Props) => {
  const i18nDictionary = await getI18nDictionary(params.lang);

  return (
    <html lang={params.lang}>
      <body>
        <I18nProvider dictionary={i18nDictionary}>
          {children}
        </I18nProvider>
      </body>
    </html>
  );
};

export default AppLayout;

步骤3:子组件中使用字典

修改Header组件,通过useI18n获取字典:

'use client';

// 保留原有导入
import { useI18n } from '@/components/I18nContext';

export const Header = () => {
  const i18n = useI18n();
  const { isOpen: isMobileNavOpen, onToggle } = useDisclosure();
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Box as="header">
      {/* 原有代码 */}
      <Heading as={'h1'} fontSize={'xl'} display={{ base: 'none', md: 'block' }}>
        <TextUnderline>{i18n.header.logoText}</TextUnderline>
      </Heading>
      {/* 原有代码 */}
    </Box>
  );
};

方案2:Props 逐层传递

适合组件层级较少、结构简单的项目,逻辑直观。

步骤1:修改子布局接收字典Props

更新src/app/[lang]/(main)/layout.tsx:

import React from 'react';
import type { Metadata } from 'next/types';
import type { I18nDictionary } from '@/i18n';

import { InitializeChakra } from '@/components/InitializeChakra';
import { Header } from '@/components/Header';
import { Footer } from '@/components/Footer';

export const metadata: Metadata = {
  title: 'Test',
  description: 'Test',
  keywords: 'test',
};

type Props = {
  children: React.ReactNode;
  i18nDictionary: I18nDictionary;
};

const AppLayout = ({ children, i18nDictionary }: Props) => {
  return (
    <InitializeChakra>
      <Header i18nDictionary={i18nDictionary} />
      {children}
      <Footer i18nDictionary={i18nDictionary} />
    </InitializeChakra>
  );
};

export default AppLayout;

步骤2:根布局传递字典给子布局

更新src/app/[lang]/layout.tsx:

import React from 'react';
import { getI18nDictionary } from '@/i18n';

type Props = {
  children: React.ReactNode;
  params: {
    lang: string;
  };
};

const AppLayout = async ({ children, params }: Props) => {
  const i18nDictionary = await getI18nDictionary(params.lang);

  // 将字典作为Props传递给子布局
  const childWithProps = React.cloneElement(children as React.ReactElement, {
    i18nDictionary,
  });

  return (
    <html lang={params.lang}>
      <body>
        {childWithProps}
      </body>
    </html>
  );
};

export default AppLayout;

步骤3:Header组件接收字典Props

修改Header组件:

'use client';

// 保留原有导入
import type { I18nDictionary } from '@/i18n';

type Props = {
  i18nDictionary: I18nDictionary;
};

export const Header = ({ i18nDictionary }: Props) => {
  const { isOpen: isMobileNavOpen, onToggle } = useDisclosure();
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Box as="header">
      {/* 原有代码 */}
      <Heading as={'h1'} fontSize={'xl'} display={{ base: 'none', md: 'block' }}>
        <TextUnderline>{i18nDictionary.header.logoText}</TextUnderline>
      </Heading>
      {/* 原有代码 */}
    </Box>
  );
};

方案3:组件内自行获取(搭配缓存)

适合无需全局共享字典的场景,每个组件独立获取并缓存,避免重复请求。

步骤1:安装SWR用于缓存

npm install swr

步骤2:Header组件内获取字典

修改Header组件:

'use client';

// 保留原有导入
import { useParams } from 'next/navigation';
import useSWR from 'swr';
import { getI18nDictionary } from '@/i18n';

export const Header = () => {
  const params = useParams();
  const lang = params.lang as string;

  // 用SWR缓存字典,避免重复请求
  const { data: i18nDictionary } = useSWR(
    ['i18n', lang],
    () => getI18nDictionary(lang),
    { fallbackData: null }
  );

  // 加载状态处理
  if (!i18nDictionary) return <div>加载中...</div>;

  const { isOpen: isMobileNavOpen, onToggle } = useDisclosure();
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Box as="header">
      {/* 原有代码 */}
      <Heading as={'h1'} fontSize={'xl'} display={{ base: 'none', md: 'block' }}>
        <TextUnderline>{i18nDictionary.header.logoText}</TextUnderline>
      </Heading>
      {/* 原有代码 */}
    </Box>
  );
};

方案选择建议

  • Context方案:优先选择,适合大多数项目,全局共享无需逐层传递Props。
  • Props传递:适合小型项目或组件层级简单的场景,逻辑清晰。
  • 组件内自行获取:适合独立组件或无需全局共享的场景,搭配缓存性能更优。

内容的提问来源于stack exchange,提问作者nop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 14:27:04