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

Next.js中Footer组件国际化实现报错求助

Next.js Footer组件国际化问题修复

错误分析

错误1:服务器端报错 (0 , react__WEBPACK_IMPORTED_MODULE_0__.createContext) is not a function

i18next的HttpApi和LanguageDetector是浏览器端专属插件,在Next.js服务器组件中执行时,会访问React客户端API(如createContext),导致服务器渲染环境报错。

错误2:i18n.changeLanguage() is not a function

i18n实例未在客户端环境完成初始化,或在服务器端调用了仅客户端可用的方法,导致无法找到changeLanguage函数。


修复步骤

1. 调整i18n初始化逻辑,限定仅在客户端运行

修改src/utils/i18n.js,添加环境判断避免服务器端执行浏览器代码:

// src/utils/i18n.js
let i18nInstance = null;

export const initI18n = async () => {
  if (typeof window === 'undefined') return null; // 服务器端直接返回空

  const { default: i18n } = await import('i18next');
  const { initReactI18next } = await import('react-i18next');
  const { default: HttpApi } = await import('i18next-http-backend');
  const { default: LanguageDetector } = await import('i18next-browser-languagedetector');

  i18n
    .use(HttpApi)
    .use(LanguageDetector)
    .use(initReactI18next)
    .init({
      fallbackLng: 'en',
      debug: process.env.NODE_ENV === 'development',
      interpolation: {
        escapeValue: false
      },
      backend: {
        loadPath: '/locale/{{lng}}.json'
      },
      detection: {
        order: ['queryString', 'cookie'],
        caches: ['cookie']
      }
    });

  i18nInstance = i18n;
  return i18n;
};

export const getI18nInstance = () => i18nInstance;

2. 在Providers中集成I18nextProvider

修改src/lib/providers.js,动态初始化i18n并注入上下文:

'use client';
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
// redux
import { Provider } from 'react-redux';
import { reduxStore, persistor } from 'src/lib/redux';
// react
import { PersistGate } from 'redux-persist/integration/react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { Toaster } from 'react-hot-toast';
// i18n
import { I18nextProvider } from 'react-i18next';
import { initI18n } from 'src/utils/i18n';
// components
import ThemeRegistry from 'src/theme';
import GlobalStyles from 'src/theme/globalStyles';
import { LinearProgress, Stack } from '@mui/material';
import AuthProvider from './auth';
import dynamic from 'next/dynamic';

const ProgressBar = dynamic(() => import('src/components/ProgressBar'), {
  ssr: false
});

export default function Providers(props) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            refetchOnWindowFocus: false
          }
        }
      })
  );
  const [i18n, setI18n] = useState(null);

  useEffect(() => {
    const initializeI18n = async () => {
      const instance = await initI18n();
      setI18n(instance);
    };
    initializeI18n();
  }, []);

  // 等待i18n初始化完成
  if (!i18n) {
    return (
      <Stack
        sx={{
          position: 'fixed',
          top: 'calc(50vh - 2px)',
          width: '300px',
          left: 'calc(50vw - 150px)',
          zIndex: 11
        }}
      >
        <LinearProgress />
      </Stack>
    );
  }

  return (
    <Provider store={reduxStore}>
      <AuthProvider isAuth={props.isAuth}>
        <ThemeRegistry>
          <GlobalStyles />
          <QueryClientProvider client={queryClient}>
            <Toaster position={'top-center'} />
            <I18nextProvider i18n={i18n}>
              <PersistGate
                loading={
                  <Stack
                    sx={{
                      position: 'fixed',
                      top: 'calc(50vh - 2px)',
                      width: '300px',
                      left: 'calc(50vw - 150px)',
                      zIndex: 11
                    }}
                  >
                    <LinearProgress />
                  </Stack>
                }
                persistor={persistor}
              >
                {props.children}
              </PersistGate>
            </I18nextProvider>
          </QueryClientProvider>
          <ProgressBar />
        </ThemeRegistry>
      </AuthProvider>
    </Provider>
  );
}

Providers.propTypes = {
  isAuth: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired
};

3. 修改Footer组件实现国际化文本与语言切换

更新src/components/layout/_main/footer.jsx,使用useTranslation钩子并添加语言选择下拉框:

'use client';
// react
import React from 'react';
import { useTranslation } from 'react-i18next';

// mui
import { alpha } from '@mui/material/styles';
import { Typography, Container, Stack, Box, IconButton, Select, MenuItem } from '@mui/material';
// next
import NextLink from 'next/link';
// components
import NewsLetter from './newsletter';

// icons
import { IoLogoInstagram } from 'react-icons/io5';
import { FaFacebook, FaLinkedin } from 'react-icons/fa';
import { IoLogoYoutube } from 'react-icons/io';

const SOCIAL_MEDIA_LINK = [
  {
    name: 'facebook',
    linkPath: 'https://www.facebook.com/techgater',
    icon: <FaFacebook className="facebook" />
  },
  {
    name: 'instagram',
    linkPath: 'https://www.instagram.com/techgater',
    icon: <IoLogoInstagram className="insta" />
  },
  {
    name: 'linkedin',
    linkPath: 'https://www.linkedin.com/company/89683736/admin',
    icon: <FaLinkedin className="linkedin" />
  },
  {
    name: 'youtube',
    linkPath: 'https://www.youtube.com/@techgaterwebsolutions7394',
    icon: <IoLogoYoutube className="youtube" />
  }
];

export default function Footer() {
  const { t, i18n } = useTranslation();

  const handleLanguageChange = (event) => {
    i18n.changeLanguage(event.target.value);
  };

  return (
    <Box
      sx={{
        bgcolor: 'background.paper',
        borderTop: (theme) => `1px solid ${theme.palette.divider}`,
        py: 3,
        overflow: 'hidden',
        position: 'relative',
        display: {
          md: 'block',
          xs: 'none'
        }
      }}
    >
      <Container fixed>
        {/* 语言切换下拉框 */}
        <Stack direction="row" justifyContent="flex-end" mb={2}>
          <Select
            value={i18n.language}
            onChange={handleLanguageChange}
            sx={{ minWidth: 100 }}
          >
            <MenuItem value="en">English</MenuItem>
            <MenuItem value="fr">Français</MenuItem>
          </Select>
        </Stack>

        <Stack
          className="newsletter"
          spacing={1}
          sx={{
            textAlign: 'center',
            justifyContent: 'center',
            position: 'relative',
            width: '100%',
            maxWidth: 600,
            margin: 'auto'
          }}
        >
          <Typography variant="h2" color="text.primary" fontWeight={700}>
            {t('newsletter')}
          </Typography>
          <Typography variant="body1" color="text.secondary">
            {t('newsletterDescription')}
          </Typography>
          <NewsLetter />
          <Stack direction="row" spacing={3} justifyContent="center">
            {SOCIAL_MEDIA_LINK.map((item, index) => (
              <React.Fragment key={index}>
                <IconButton
                  aria-label=""
                  name={item.name}
                  component={NextLink}
                  href={item.linkPath}
                  target="_blank"
                  sx={{
                    color: (theme) =>
                      index === 0 || index === 2 ? theme.palette.primary.main : theme.palette.error.main,
                    fontSize: 28
                  }}
                >
                  {item.icon}
                </IconButton>
              </React.Fragment>
            ))}
          </Stack>
          <Typography variant="body1" color="text.primary" textAlign="center">
            {t('allRightsReserved')}
          </Typography>
        </Stack>
      </Container>
    </Box>
  );
}

4. 同步服务器端HTML的lang属性

修改src/app/(user)/layout.jsx,从Cookie获取当前语言并设置到html标签:

import * as React from 'react';
import Providers from 'src/lib/providers';
import { cookies } from 'next/headers';

export default async function RootLayout({ children }) {
  const cookiesList = cookies();
  const hasCookie = cookiesList.get('token');
  // 从i18next cookie读取语言,默认值为en
  const lang = cookiesList.get('i18next')?.value || 'en';

  return (
    <html lang={lang}>
      <body>
        <Providers isAuth={hasCookie}>{children}</Providers>
      </body>
    </html>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:14:55