NextJS i18n多国语言的实现代码
前面简单提供了NextJS i18n国际化多国语言的处理的一些资源,由于网上的示例代码都有些小问题,于是踩坑之后决定把完整的实现代码提供一下。
其中主要依赖next-i18next这个包,但是在按照官方的教程实现过程中发现了两个小问题并得以修复:
- 语言的切换调用只支持两个,示例代码的写法是:
locale={router.locale === 'en' ? 'de' : 'en'}
,而我修复了这一点,单独写了个componet用做任意语言切换和当前语言的显示 - 由于在文件
next-i18next.config.j
s中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 SecondPage
components目录
/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": "页面底部描述" }