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
相关产品推荐
相关产品推荐

