This article will introduce how to use multiple languages in the nextjs-ssg framework with examples.
At present, NextJS’s support for SSG is not perfect, and there are still many unsupported functions, including multi-language routing. In SSR mode, you can distinguish page languages by path through simple configuration. However, SSG lacks corresponding support.
Next, we will start to implement the steps:
Install related dependent libraries
#npm
npm i i18next i18next-ssg next-i18next
#yarn
yarn add i18next i18next-ssg next-i18next
Create configuration file
//Create the project root directory: next-i18next.config.js
module.exports = {
i18n: {
locales: ['en', 'zh'],//多语言配置
defaultLocale: 'en',//默认语言
},
localePath: './public/locales', // 语言文件存放位置
};
Modify configuration file
//next.config.ts
import type { NextConfig } from "next";
const { i18n } = require('./next-i18next.config');
const nextConfig: NextConfig = {
//...Add the following configuration
env: {
NEXT_PUBLIC_I18N: i18n
},
};
export default nextConfig;
Create translation resources
//Create translation resources for each language in the public/locales directory /public/locales/[language]/common.json
{
"home_text_title":"Online Tools-It Resource Hub",
"welcome": "Welcome to My Site",
"description": "Static Site with i18n"
}
Add default language build configuration
// Create [[...paths]].tsx in the same directory as _app.tsx
export { getStaticProps, getStaticPaths } from "i18next-ssg/Redirect";
import { useRootPathRedirect } from "i18next-ssg";
export default function Page() {
useRootPathRedirect();
return <div>Redirecting...</div>;
}
The final directory structure is as follows
-public
--locales
---en
----common.json
---zh
----common.json
-src
--pages
---_app.tsx
---_document.tsx
---[[...paths]].tsx
---[locale]
----index.tsx
----Other pages
Translate the page
I will just post one of my layout files for reference, including language switching.
const {t} = useTranslation("common")
{t("home_text_title")}
import { AppBar, Toolbar, Typography, Box, Container, Button, Menu, MenuItem, IconButton } from '@mui/material'
import { Language as LanguageIcon, KeyboardArrowDown } from '@mui/icons-material'
import Link from 'next/link'
import { useState } from 'react'
import { I18NLink, setUserLocale, useLocaleSwitcher, useTranslation } from 'i18next-ssg';
const localeMap: Record<Locale, string> = {
en: "English",
zh: " 中文 ",
};
const LanguageDropdown = ({
options,
currentLabel
}: {
options: {
label: string;
path: string;
locale: Locale;
}[];
currentLabel: string;
}) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
<Button
onClick={handleClick}
startIcon={<LanguageIcon />}
endIcon={<KeyboardArrowDown />}
sx={{
color: 'white',
textTransform: 'none',
borderRadius: 2,
px: 2,
py: 1,
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)',
}
}}
>
{currentLabel}
</Button>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
sx: {
backgroundColor: 'rgba(255, 255, 255, 0.95)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: 2,
mt: 1,
minWidth: 120,
}
}}
>
{options.map(({ label, path, locale }) => (
<MenuItem
key={path}
onClick={() => {
setUserLocale(locale);
handleClose();
}}
sx={{
'&:hover': {
backgroundColor: 'rgba(99, 102, 241, 0.1)',
}
}}
>
<Link href={path} style={{ textDecoration: 'none', color: 'inherit' }}>
{label}
</Link>
</MenuItem>
))}
</Menu>
</>
);
};
export default function Layout({ children }: { children: React.ReactNode }) {
const { label, options } = useLocaleSwitcher({ localeMap });
const { t } = useTranslation("common")
return (
<>
<AppBar
position="fixed"
sx={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
backdropFilter: 'blur(10px)',
backgroundColor: 'rgba(102, 126, 234, 0.9)',
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)',
zIndex: 1100,
}}
>
<Toolbar sx={{ minHeight: 64 }}>
<Typography
variant="h5"
component="div"
sx={{
flexGrow: 1,
fontWeight: 600,
background: 'linear-gradient(45deg, #ffffff 30%, #f0f9ff 90%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
textShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
}}
>
{t("home_text_title")}
</Typography>
<LanguageDropdown options={options} currentLabel={label} />
</Toolbar>
</AppBar>
{/* 添加顶部间距以避免内容被固定导航栏遮挡 */}
<Box sx={{ mt: 8 }} />
<Container
maxWidth="md"
sx={{
mt: 4,
mb: 4,
px: { xs: 2, sm: 3 },
}}
>
{children}
</Container>
<Box
sx={{
textAlign: 'center',
py: 4,
background: 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)',
borderTop: '1px solid rgba(0, 0, 0, 0.05)',
mt: 'auto',
}}
>
<I18NLink href="/">
<Typography
variant="body2"
sx={{
color: '#64748b',
fontWeight: 500,
}}
>
© 2025 IT 资源网 - 专业的技术资源平台
</Typography>
</I18NLink>
</Box>
</>
)
}
Final effect


END
Posted to: Framework Documentation
2025-07-08