NextJS i18n多国语言的实现代码

前面简单提供了NextJS i18n国际化多国语言的处理的一些资源,由于网上的示例代码都有些小问题,于是踩坑之后决定把完整的实现代码提供一下。
其中主要依赖next-i18next这个包,但是在按照官方的教程实现过程中发现了两个小问题并得以修复:
- 语言的切换调用只支持两个,示例代码的写法是:
locale={router.locale === 'en' ? 'de' : 'en'},而我修复了这一点,单独写了个componet用做任意语言切换和当前语言的显示 - 由于在文件
next-i18next.config.js中localePath目录配置的缺失,应用运行时会导致namespace错误
内容均在官方原来的示例代码中修改,下面直接上文件
目录结构
components ----Footer.js ----Header.js ----LocaleSwitcher.js pages ----_app.js ----index.js ----second-page.js public ----static --------locales ------------en ----------------common.json ----------------footer.json ------------fr ----------------common.json ----------------footer.json ------------zh-CN ----------------common.json ----------------footer.json next-i18next.config.js next.config.js
另外需要注意的是我使用了tailwindcss框架,不需要的删除相应的引入即可,以免报错。
配置文件
/next-i18next.config.js
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'zh-CN'],
defaultNS: 'common',
//fallbackLng: 'en',
},
localePath: 'public/static/locales',
};/next.config.js
/** @type {import('next').NextConfig} */
const { i18n } = require('./next-i18next.config');
const nextConfig = {
i18n,
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig
Pages目录
/pages/_app.js
import '../styles/globals.css';
import { appWithTranslation } from 'next-i18next';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default appWithTranslation(MyApp);
/pages/index.js
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useTranslation, Trans } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { Header } from '../components/Header'
import { Footer } from '../components/Footer'
const Homepage = () => {
const router = useRouter()
const { t } = useTranslation('common')
const { locale, locales, defaultLocale } = router
return (
<>
<main>
<Header heading={t('h1')} title={t('title')} />
<p>Current locale: {locale}</p>
<p>Default locale: {defaultLocale}</p>
<p>Configured locales: {JSON.stringify(locales)}</p>
<div style={{ display: 'inline-flex', width: '90%' }}>
<div style={{ width: '50%' }}>
<h3 style={{ minHeight: 70 }}>{t('blog.optimized.question')}</h3>
<p>
<Trans i18nKey='blog.optimized.answer'>
Then you may have a look at <a href='https://locize.com/blog/next-i18next/'>this blog post</a>.
</Trans>
</p>
<a href='https://locize.com/blog/next-i18next/'>
<img style={{ width: '50%' }} src='https://locize.com/blog/next-i18next/next-i18next.jpg' />
</a>
</div>
<div style={{ width: '50%' }}>
<h3 style={{ minHeight: 70 }}>{t('blog.ssg.question')}</h3>
<p>
<Trans i18nKey='blog.ssg.answer'>
Then you may have a look at <a href='https://locize.com/blog/next-i18n-static/'>this blog post</a>.
</Trans>
</p>
<a href='https://locize.com/blog/next-i18n-static/'>
<img style={{ width: '50%' }} src='https://locize.com/blog/next-i18n-static/title.jpg' />
</a>
</div>
</div>
<hr style={{ marginTop: 20, width: '90%' }} />
<div>
<Link href='/second-page'>
<button
type='button'
>
{t('to-second-page')}
</button>
</Link>
</div>
</main>
<Footer />
</>
)
}
export const getStaticProps = async ({ locale }) => ({
props: {
...await serverSideTranslations(locale, ['common', 'footer']),
},
})
export default Homepage/pages/second-page.js
import Link from 'next/link'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { Header } from '../components/Header'
import { Footer } from '../components/Footer'
const SecondPage = () => {
const { t } = useTranslation('second-page')
return (
<>
<main>
<Header heading={t('h1')} title={t('title')} />
<Link href='/'>
<button
type='button'
>
{t('back-to-home')}
</button>
</Link>
</main>
<Footer />
</>
)
}
export const getStaticProps = async ({ locale }) => ({
props: {
...await serverSideTranslations(locale, ['second-page', 'footer']),
},
})
export default SecondPagecomponents目录
/components/Footer.js
import { useTranslation } from 'next-i18next';
export const Footer = () => {
const { t } = useTranslation('footer');
return (
<footer>
<p>{t('description')}</p>
</footer>
);
};/components/Header.js
import Head from 'next/head'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useTranslation, Trans } from 'next-i18next'
import LocaleSwitcher from '../components/LocaleSwitcher'
export const Header = ({ heading, title }) => {
const router = useRouter()
const { t } = useTranslation('common')
return (
<>
<Head>
<title>{title}</title>
</Head>
<h2>
<Trans i18nKey='website.name'>
test <a href='https://www.saucouncil.com'>this blog post</a>
</Trans>
<hr />
</h2>
<LocaleSwitcher />
</>
)
}
/components/LocaleSwitcher.js
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function LocaleSwitcher() {
const router = useRouter()
const { locales, locale: activeLocale } = router
const otherLocales = locales.filter((locale) => locale !== activeLocale)
return (
<div>
<p>Locale switcher:</p>
<ul>
<li>Current locale: {activeLocale}</li>
{otherLocales.map((locale) => {
const { pathname, query, asPath } = router
return (
<li key={locale}>
<Link href={{ pathname, query }} as={asPath} locale={locale}>
<a>{locale}</a>
</Link>
</li>
)
})}
</ul>
</div>
)
}
public目录
注意此处的翻译文件都是乱写的,自行修改
/public/locales/en/common.json
{
"website": {
"name": "MYWEBSITE <1>http</1>"
},
"h1": "A simple example",
"change-locale": "Change locale to \"{{changeTo}}\"",
"to-second-page": "To second page",
"error-with-status": "A {{statusCode}} error occurred on server",
"error-without-status": "An error occurred on the server",
"title": "Home | next-i18next",
"blog": {
"optimized": {
"question": "Do you like to unleash some super powers to have all side optimized translations?",
"answer": "Then you may have a look at 66666<1>this blog post</1>."
},
"ssg": {
"question": "Do you want to use SSG (next export)?",
"answer": "Then you may have a look at11111111 <1>this blog post</1>."
}
}
}/public/locales/en/footer.json
{
"description": "This is a non-page component that requires its own namespace"
}/public/locales/fr/common.json
{
"website": {
"name": "SAUCouncil <1>fr http</1>"
},
"h1": "Ein einfaches Beispiel",
"change-locale": "Sprache wechseln zu \"{{changeTo}}\"",
"to-second-page": "Zur zweiten Seite",
"error-with-status": "Auf dem Server ist ein Fehler ({{statusCode}}) aufgetreten",
"error-without-status": "Auf dem Server ist ein Fehler aufgetreten",
"title": "Hauptseite | next-i18next",
"blog": {
"optimized": {
"question": "Möchtest du einige Superkräfte entfesseln, um für alle Seiten optimierte Übersetzungen zu haben?",
"answer": "Dann schaue dir vielleicht 3333333333<1>diesen Blogbeitrag</1> an."
},
"ssg": {
"question": "Möchtest du SSG (next export) verwenden?",
"answer": "Dann schaue dir vielleicht 2222222<1>diesen Blogbeitrag</1> an."
}
}
}/public/locales/fr/footer.json
{
"description": "Dies ist eine Nicht-Seitenkomponente, die einen eigenen Namespace erfordert"
}/public/locales/zh-CN/common.json
{
"website": {
"name": "我的网站 <1>http</1>"
},
"h1": "示例代码",
"change-locale": "Change locale to \"{{changeTo}}\"",
"to-second-page": "To second page",
"error-with-status": "A {{statusCode}} error occurred on server",
"error-without-status": "An error occurred on the server",
"title": "Home | next-i18next",
"blog": {
"optimized": {
"question": "Do you like to unleash some super powers to have all side optimized translations?",
"answer": "Then you may have a look at 66666<1>this blog post</1>."
},
"ssg": {
"question": "Do you want to use SSG (next export)?",
"answer": "Then you may have a look at11111111 <1>this blog post</1>."
}
}
}/public/locales/zh-CN/footer.json
{
"description": "页面底部描述"
}