Next.js (SSG) & i18n
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


The resources on this site come from the Internet and are used for learning and research by Internet enthusiasts. If your rights are accidentally infringed, please contact the webmaster in time to handle and delete them. Please understand!
IT Resource Hub » Next.js (SSG) & i18n
IT Resource Hub » Next.js (SSG) & i18n