WEBSITE:www.jessieontheroad.com/ GitHub:github.com/Jessie-jzn
在开发个人网站的时候,实现国际化几乎是必不可少的功能,今天给大家分享一下如何使用i18n在Next项目中实现国际化。
安装依赖
npm install next react-i18next i18next next-i18next
项目结构
.
├── next.config.js
├── next-i18next.config.js
├── i18n.js
├── package.json
├── pages
│ ├── _app.js
│ └── index.js
└── public
└── locales
├── en
│ └── common.json
└── zh
└── common.json
配置文件
next-i18next.config.js
// next-i18next.config.js
const path = require("path");
module.exports = {
i18n: {
defaultLocale: "zh",
locales: ["en", "zh"],
},
localePath: path.resolve("./public/locales"),
// react: { useSuspense: false },
};
next.config.js
const { i18n } = require('./next-i18next.config');
module.exports = {
i18n,
};
i18n.js
// i18n.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import nextI18NextConfig from "./next-i18next.config";
import en from "./public/locales/en/common.json";
import zh from "./public/locales/zh/common.json";
i18n.use(initReactI18next).init({
resources: {
en: { translation: en },
zh: { translation: zh },
},
fallbackLng: nextI18NextConfig.i18n.defaultLocale,
lng: nextI18NextConfig.i18n.defaultLocale,
keySeparator: false,
interpolation: {
escapeValue: false,
},
});
export default i18n;
应用程序入口
pages/_app.js
import { appWithTranslation } from 'next-i18next';
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default appWithTranslation(MyApp);
pages/index.js
import { GetStaticProps } from "next";
import NotionService from "@/lib/notion/NotionServer";
import { NOTION_HOME_ID } from "@/lib/constants";
import NotionPage from "@/components/NotionPage";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
const notionService = new NotionService();
export const getStaticProps: GetStaticProps = async ({ locale }: any) => {
const post = await notionService.getPage(NOTION_HOME_ID);
return {
props: {
post,
...(await serverSideTranslations(locale, ["common"])),
},
revalidate: 10,
};
};
const Home = ({ post }: any) => {
return (
<>
<NotionPage recordMap={post} />
</>
);
};
export default Home;
国际化文件
public/locales/en/common.json
{
"home": "Home",
"post": "Post",
"tags": "Tags",
"projects": "Projects",
"about": "About",
"contact": "Contact Me"
}
public/locales/zh/common.json
{
"home": "首页",
"post": "文章",
"tags": "标签",
"projects": "项目",
"about": "关于我",
"contact": "联系"
}
组件使用
示例组件 ExampleComponent.js
import { useTranslation } from 'react-i18next';
const ExampleComponent = () => {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
};
export default ExampleComponent;
语言切换器 LanguageSwitch.tsx:
import { useState } from "react";
import { useRouter } from "next/router";
const LanguageSwitcher = () => {
const [dropdownVisible, setDropdownVisible] = useState(false);
const router = useRouter();
const { locale, locales, pathname, asPath, query } = router;
const toggleDropdown = () => {
setDropdownVisible(!dropdownVisible);
};
const changeLanguage = (e) => {
const selectedLocale = e.target.getAttribute("data-lang");
router.push({ pathname, query }, asPath, { locale: selectedLocale });
setDropdownVisible(false); // Hide dropdown after selecting a language
};
return (
<div className="relative inline-block text-left">
<button
className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onClick={toggleDropdown}
>
<span className="mr-2">{locale === "en" ? "🇺🇸" : "🇨🇳"}</span>
{locale === "en" ? "English" : "中文"}
</button>
{dropdownVisible && (
<div className="origin-top-right z-10 absolute right-0 mt-2 w-30 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
{locales?.map((loc) => (
<div
key={loc}
data-lang={loc}
onClick={changeLanguage}
className="flex items-center px-4 py-2 text-sm text-gray-700 cursor-pointer hover:bg-gray-100"
>
<span className="mr-2">{loc === "en" ? "🇺🇸" : "🇨🇳"}</span>
{loc === "en" ? "English" : "中文"}
</div>
))}
</div>
)}
</div>
);
};
export default LanguageSwitcher;
踩坑
在实际开发的时候会遇到这样的报错
Error: Text content does not match server-rendered HTML.
Text content did not match. Server: "欢迎" Client: "welcome"
这个错误通常是因为在服务器端渲染(SSR)期间生成的内容与在客户端渲染(CSR)期间生成的内容不匹配。可能的原因是 i18next 在服务器端和客户端之间没有正确同步语言设置。
为了解决这个问题,可以尝试以下步骤:
- 确保服务器端和客户端的语言设置一致: 确保在服务器端和客户端都设置了正确的语言。
- 使用
next-i18next提供的serverSideTranslations函数: 在页面组件中使用serverSideTranslations函数来获取翻译文件。 - 确保翻译文件的路径正确: 确保翻译文件路径和内容没有错误。
如果以上几个步骤都没有解决,在home.tsx中的getStaticProps加入...(await serverSideTranslations(locale, ["common"]))
import { GetStaticProps } from "next";
import NotionService from "@/lib/notion/NotionServer";
import { NOTION_HOME_ID } from "@/lib/constants";
import NotionPage from "@/components/NotionPage";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
const notionService = new NotionService();
export const getStaticProps: GetStaticProps = async ({ locale }: any) => {
const post = await notionService.getPage(NOTION_HOME_ID);
return {
props: {
post,
...(await serverSideTranslations(locale, ["common"])),
},
revalidate: 10,
};
};
const Home = ({ post }: any) => {
return (
<>
<NotionPage recordMap={post} />
</>
);
};
export default Home;