我是 前端多语言国际化彭于晏,我喂自己袋盐
最近和同事做了一个小游戏网站,我来处理国际化,现在很多公司产品涉及到海外用户,前端国际化(i18n) 已经从"锦上添花"变成了"必备能力"。一套良好的国际化方案,不仅能提升用户体验,还能让产品更快适应不同国家和地区。
本文将基于 React + i18next ,带你从零到一实现 6 种语言国际化 (中文、英文、葡萄牙语、法语、韩语、日语),并配合 framer-motion 动画 打造丝滑的语言切换体验。

🧪 为什么要用国际化?
场景 | 说明 |
---|---|
产品出海 | 面向不同地区(北美、巴西、印度等),需要多语言支持 |
用户良好体验 | 用户看到的是自己熟悉的语言,降低使用门槛 |
市场推广与增长 | 本地化内容更容易被接受,增加留存与转化 |
团队协作与维护 | 统一的多语言文件管理方式,开发和运营团队都能快速调整文案 |
⚙️ 国际化方案选型
在 React 生态中,国际化的主流方案是 i18next ,它配合 react-i18next
和 i18next-browser-languagedetector
可以快速完成需求。
- i18next:核心国际化引擎
- react-i18next:React 适配层
- i18next-browser-languagedetector:自动嗅探浏览器语言
- framer-motion :丝滑的切换动画库 👉 官方文档
🛠️ 初始化项目国际化配置
1️⃣ 安装依赖
r
npm install i18next react-i18next i18next-browser-languagedetector
2️⃣ 创建国际化文件
在项目根目录下新增 locales
文件夹,结构如下:
locales/
├── i18n.ts
├── en.json
├── pt-BR.json
├── zh-CN.json
├── fr.json
├── ja.json
└── ko.json
3️⃣ 配置 i18n.ts
javascript
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import en from "./en.json";
import pt_BR from "./pt-BR.json";
import zh_CN from "./zh-CN.json";
import fr from "./fr.json";
import ja from "./ja.json";
import ko from "./ko.json";
import store from "@/store";
const { app: { language } = {} } = store.getState();
const resources = {
en: { translation: en },
"pt-BR": { translation: pt_BR },
"zh-CN": { translation: zh_CN },
fr: { translation: fr },
ja: { translation: ja },
ko: { translation: ko },
};
i18n
.use(LanguageDetector) // 自动检测浏览器语言
.use(initReactI18next) // 集成到 React
.init({
resources,
fallbackLng: language || "en", // 默认语言
interpolation: { escapeValue: false }, // 不转义 HTML
});
export default i18n;
💡 小技巧 :定义语言时要遵循 BCP-47 标准 ,因为 LanguageDetector
返回的就是这种格式。例如 zh-CN
、pt-BR
。
🔄 语言切换组件
我们需要一个下拉菜单来切换语言,并且配合 framer-motion 实现动画效果。
ini
"use client";
import { changeLanguage } from "@/store/app/appSlice";
import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
import { useAppDispatch, useAppSelector } from "@/store/hooks";
import { CDN } from "@/utils/env";
import i18n from "@/locales/i18n";
export default function LangDropdown() {
const LANG_OPTIONS = [
{ code: "en", label: "English" },
{ code: "pt-BR", label: "Português" },
{ code: "fr", label: "Français" },
{ code: "ko", label: "한국어" },
{ code: "ja", label: "日本語" },
{ code: "zh-CN", label: "中文" },
];
const dispatch = useAppDispatch();
const selected = useAppSelector((state) => state.app.language);
const [open, setOpen] = useState(false);
return (
<div className="relative my-[12px]">
<div
onClick={() => setOpen((prev) => !prev)}
className="p-[8px] border border-solid border-[rgba(255,255,255,0.40)] h-[32px] rounded-[8px] hover:bg-[#8138ff27] text-white text-[14px] flex justify-between items-center cursor-pointer !select-none"
>
{LANG_OPTIONS.find((i) => i.code === selected)?.label}
<span className="ml-2">
<img
className={`size-[18px] transition-transform duration-200 ${
open ? "rotate-180" : "rotate-0"
}`}
src={`${CDN}images/arrow-down.svg`}
/>
</span>
</div>
<AnimatePresence>
{open && (
<motion.ul
initial={{ opacity: 0, scaleY: 0, originY: 1 }}
animate={{ opacity: 1, scaleY: 1, originY: 1 }}
exit={{ opacity: 0, scaleY: 0, originY: 0 }}
transition={{ duration: 0.2 }}
className="absolute bottom-full left-0 mb-2 w-full bg-[#332F48] rounded-[8px] max-h-[400px] shadow-lg overflow-y-auto z-50 "
>
{LANG_OPTIONS.map((lang) => (
<li
key={lang.code}
className={`text-[14px] p-[8px] text-white hover:bg-[#8138ff27] cursor-pointer ${
lang.code === selected ? "bg-[#8138FF]" : ""
}`}
onClick={() => {
dispatch(changeLanguage(lang.code));
i18n.changeLanguage(lang.code);
setOpen(false);
}}
>
{lang.label}
</li>
))}
</motion.ul>
)}
</AnimatePresence>
</div>
);
}
✨ 亮点:
AnimatePresence
+motion.ul
→ 丝滑展开收起rotate-180
→ 下拉箭头旋转动画- Redux 状态同步 → 全局保存语言选择
效果图

📖 文案配置与使用
所有翻译统一放在 json
文件里,不同语言使用相同的 key。
en.json
json
{
"About": "About",
"FAQ": "FAQ",
"Feedback about us": "Feedback about us"
}
zh-CN.json
json
{
"About": "关于",
"FAQ": "常见问题",
"Feedback about us": "关于我们的反馈"
}
使用方式
javascript
import { useTranslation } from "react-i18next";
const { t } = useTranslation();
function Footer() {
return (
<div>
<span>{t("About")}</span> | <span>{t("FAQ")}</span>
</div>
);
}
🔚 总结
关键点 | 描述 |
---|---|
为什么要国际化 | 产品出海、用户体验、本地化适配 |
主要依赖库 | i18next 、react-i18next 、i18next-browser-languagedetector |
语言文件规范 | 使用 BCP-47 语言码(如 zh-CN 、pt-BR ) |
动画优化 | framer-motion 提供丝滑切换体验 |
使用方式 | useTranslation() + JSON key-value |
至此,前端国际化彭于晏6 种语言国际化配置和切换功能就完成了。是不是觉得很 easy? 🚀