使用 React JS 构建多语言应用程序🌐
如今,开发一款支持多种语言的应用程序对于触达更广泛的用户群体变得越来越重要。因此,这次我们将借助 React 来构建这样一个应用程序。
目录。
📌使用的技术。📌 创建
项目。📌
初步步骤。📌配置国际化 (i18n)。📌 使用
useTranslation。📌将翻译移至单独的文件。📌总结 。
🈵 将要使用的技术。
- ▶️ React JS 18.2.0
- ▶️ i18next 22.4.9
- ▶️ Vite JS 4.0.0
- ▶️ TypeScript 4.9.3
- ▶️ 原生 CSS(您可以在本文末尾的仓库中找到样式)
🈵 创建项目。
我们将为项目命名:(multi-lang-app可选,您可以随意命名)。
npm init vite@latest
我们使用 Vite JS 创建项目,并选择 React 和 TypeScript。
然后运行以下命令导航到刚刚创建的目录。
cd multi-lang-app
然后我们安装依赖项。
npm install
然后我们在代码编辑器(我使用的是 VS Code)中打开项目。
code .
🈵 第一步。
首先,我们将安装一个库,以便在应用程序中创建路由。这里我们将使用react-router-dom。
npm install react-router-dom
创建一个名为 src/pages 的文件夹,并在其中创建两个文件,这两个文件将作为我们的页面,内容非常简单。
- 首页.tsx
export const Home = () => {
return (
<main>
<h1>Multi-language app</h1>
<span>Select another language!</span>
</main>
);
};
- 关于.tsx
export const About = () => {
return (
<main>
<h1>About</h1>
</main>
);
};
我们还将创建一个简单的菜单组件,以便您可以在不同路径之间切换,并从任何路径更改语言。
但首先,让我们在一个单独的文件中定义要使用的语言。在我的例子中,我会在src/constants文件夹中创建它们,我们创建一个名为index.ts的文件并添加:
export const LANGUAGES = [
{ label: "Spanish", code: "es" },
{ label: "English", code: "en" },
{ label: "Italian", code: "it" },
];
现在我们创建一个名为src/components 的文件夹,并在其中创建文件Menu.tsx,并添加以下内容:
import { NavLink } from "react-router-dom";
import { LANGUAGES } from "../constants";
const isActive = ({ isActive }: any) => `link ${isActive ? "active" : ""}`;
export const Menu = () => {
return (
<nav>
<div>
<NavLink className={isActive} to="/">
Home
</NavLink>
<NavLink className={isActive} to="/about">
About
</NavLink>
</div>
<select defaultValue={"es"}>
{LANGUAGES.map(({ code, label }) => (
<option key={code} value={code}>
{label}
</option>
))}
</select>
</nav>
);
};
最后,我们将在src/App.tsx文件中创建路由器,并添加页面和菜单组件。
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { Menu } from "./components/Menu";
import { About } from "./pages/About";
import { Home } from "./pages/Home";
const App = () => {
return (
<BrowserRouter>
<Menu />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
};
export default App;
就这样,我们得到了一个简单的双路由应用程序。
🈵 配置 i18n。
首先,我们要安装这些依赖项。
npm install i18next react-i18next
react-i18next是一个可以帮助我们更轻松地翻译 React 项目中页面的软件包,但为此,您还需要另一个软件包i18next来进行国际化配置。
所以基本上,i18next 是生态系统本身,而 react-i18next 是与之配套的插件。
现在我们创建一个名为i18n.ts的新文件,它位于src文件夹下(src/i18n.ts)。
在这个文件中,我们将导入 i18next 包,并调用use方法,因为我们需要加载 initReactI18next 插件,以便更轻松地在 React 中使用国际化功能。
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n.use(initReactI18next);
export default i18n;
现在我们将访问它的 init 方法来添加一个配置对象。
- lng:默认语言。
- fallbackLng:如果用户正在查找的翻译不可用,则加载此语言。
- resources:一个包含应用程序中要使用的翻译的对象。
- interpolation.escapeValue:用于转义值并避免 XSS 攻击,我们将把它设置为 false,因为 React 默认已经这样做了。
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18.use(initReactI18next).init({
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
resources: {},
});
export default i18n;
在资源部分,需要按如下方式创建:
对象的键必须是语言代码,在本例中是“en”代表“英语”,然后在对象翻译中,所有翻译都将通过键值对进行标识。
重要的是,要保持对象键名相同,唯一改变的是它的值。请注意,两个翻译对象内部的 title 键都是相同的。
resources:{
en: {
translation: {
title: 'Multi-language app',
}
},
es: {
translation: {
title: 'Aplicación en varios idiomas',
}
},
}
这就是添加翻译后文件的样子。
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n
.use(i18nBackend)
.use(initReactI18next)
.init({
fallbackLng: "en",
lng: getCurrentLang(),
interpolation: {
escapeValue: false,
},
resources: {
en: {
translation: {
title: "Multi-language app",
label: "Select another language!",
about: "About",
home: "Home",
},
},
es: {
translation: {
title: "Aplicación en varios idiomas",
label: "Selecciona otro lenguaje!",
about: "Sobre mí",
home: "Inicio",
},
},
it: {
translation: {
title: "Applicazione multilingue",
label: "Selezionare un'altra lingua ",
about: "Su di me",
home: "Casa",
},
},
},
});
export default i18n;
最后,该文件只会导入到src/main.tsx文件中。
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./i18n";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
🈵 使用 useTranslation。
现在我们已经完成了 i18n 配置,接下来让我们使用创建的翻译。因此,在src/components/Menu.tsx文件中,我们将使用react-i18next 提供的useTranslation hook。
我们将使用 react-i18next 提供的 hook,即useTranslation。
从这个钩子中,我们检索对象i18nm和函数t。
const { i18n, t } = useTranslation();
使用翻译的方法如下:
通过括号,我们执行函数t,该函数接收一个字符串作为参数,该字符串引用了我们之前配置的翻译对象中某个值的键。(请验证您的 i18n.ts 文件配置中是否存在键为 home 且包含值的对象)。
根据您设置的默认语言,将显示相应内容。
<NavLink className={isActive} to="/">
{t("home")}
</NavLink>
好了,现在我们来切换语言。
- 首先是一个每次选择框内容改变时都会执行的函数。
- 我们评估该事件的价值。
- 我们通过对象 i18n 访问 changeLanguage 方法,并将值作为参数传递。
const onChangeLang = (e: React.ChangeEvent<HTMLSelectElement>) => {
const lang_code = e.target.value;
i18n.changeLanguage(lang_code);
};
现在,如果您切换语言,您会看到应用程序中的文本如何变化。
Menu.tsx 文件看起来会像这样。
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
import { LANGUAGES } from "../constants/index";
const isActive = ({ isActive }: any) => `link ${isActive ? "active" : ""}`;
export const Menu = () => {
const { i18n, t } = useTranslation();
const onChangeLang = (e: React.ChangeEvent<HTMLSelectElement>) => {
const lang_code = e.target.value;
i18n.changeLanguage(lang_code);
};
return (
<nav>
<div>
<NavLink className={isActive} to="/">
{t("home")}
</NavLink>
<NavLink className={isActive} to="/about">
{t("about")}
</NavLink>
</div>
<select defaultValue={i18n.language} onChange={onChangeLang}>
{LANGUAGES.map(({ code, label }) => (
<option key={code} value={code}>
{label}
</option>
))}
</select>
</nav>
);
};
现在我们去其他页面,把翻译添加到文本中。
首页.tsx
import { useTranslation } from "react-i18next";
export const Home = () => {
const { t } = useTranslation();
return (
<main>
<h1>{t("title")}</h1>
<span>{t("label")} </span>
</main>
);
};
关于.tsx
import { useTranslation } from "react-i18next";
export const About = () => {
const { t } = useTranslation();
return (
<main>
<h1>{t("about")}</h1>
</main>
);
};
好,现在我们快速演示一下如何对变量进行插值。
在 t 函数内部,第二个参数是一个对象,你可以指定要插值的变量。
请注意,我添加了属性名称。那么,这个属性名称,我必须认真考虑。
import { useTranslation } from "react-i18next";
export const About = () => {
const { t } = useTranslation();
return (
<main>
<h1>{t("about")}</h1>
<span>{t("user", { name: "Bruce Wayne 🦇" })}</span>
</main>
);
};
现在我们来看一个 json 文件(但是我在一个文件中所做的任何操作都必须在所有翻译 json 文件中复制)。
- 首先,我添加了新的属性“用户”,因为之前我没有这个属性。
- 然后,我用双括号添加了我之前分配的属性的名称,即name。
{
"title": "Multi-language app",
"label": "Select another language!",
"about": "About me",
"home": "Home",
"user": "My name is: {{name}}"
}
通过这种方式,我们可以对数值进行插值。
🈵 将翻译移至单独的文件中。
但如果翻译过多,i18n.ts 文件就会变得难以管理。最好的办法是将它们移到单独的文件中。
为此,我们需要安装另一个插件。
npm install i18next-http-backend
该插件将从服务器加载资源,因此是按需提供的。
现在,我们将在 public 文件夹内创建一个名为 i18n 的文件夹(public/i18n)。
然后在该文件夹内创建 .json 文件,并根据其对应的语言进行命名,例如,
es.json 文件用于存储西班牙语翻译,it.json 文件用于存储意大利语翻译,以此类推。
最终,我们将创建 3 个文件,因为此应用仅支持 3 种语言。
然后,我们将每个翻译对象的内容从 i18n.ts 文件移动到其对应的 JSON 文件,
例如 en.json 文件。
{
"title": "Multi-language app",
"label": "Select another language!",
"about": "About",
"home": "Home"
}
完成这 3 个文件的操作后,我们转到 i18n.ts 文件,我们将修改一些内容。
- 首先,我们要删除 resources 属性。
- 我们将导入 i18next-http-backend 包,并通过 use 方法将其作为参数传递,以便执行该插件。
import i18n from "i18next";
import i18nBackend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
i18n
.use(i18nBackend)
.use(initReactI18next)
.init({
fallbackLng: "en",
lng: "en",
interpolation: {
escapeValue: false,
},
});
export default i18n;
最后,我们需要添加一个新的属性,即后端,它接收一个对象,我们将通过loadPath属性访问该对象。
loadPath属性接收一个包含语言的函数,该函数必须返回一个字符串。但
更简单的方法是对lng变量进行插值。
这样我们就能找到获取翻译文件的路径,请注意,我指的是公共文件夹。
现在,如果您想添加一种新语言,只需将 json 文件添加到 public 目录下的 i18n 文件夹中即可。
import i18n from "i18next";
import i18nBackend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
i18n
.use(i18nBackend)
.use(initReactI18next)
.init({
fallbackLng: "en",
lng: "en",
interpolation: {
escapeValue: false,
},
backend: {
loadPath: "http://localhost:5173/i18n/{{lng}}.json",
},
});
export default i18n;
但是还有一步要做,如果您注意到 loadedPath 属性,主机是http://localhost:5173,当您将其上传到生产环境时,翻译将无法工作,因此我们必须验证我们是否处于开发模式,以便添加正确的主机。
import i18n from "i18next";
import i18nBackend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
const getCurrentHost =
import.meta.env.MODE === "development"
? "http://localhost:5173"
: "LINK TO PROD";
i18n
.use(i18nBackend)
.use(initReactI18next)
.init({
fallbackLng: "en",
lng: "en",
interpolation: {
escapeValue: false,
},
backend: {
loadPath: `${getCurrentHost}/i18n/{{lng}}.json`,
},
});
export default i18n;
还有一个提示是,后端中的翻译内容可能在页面准备就绪时仍然会加载,因此建议在应用程序中管理 Suspense。
import { Suspense } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { Menu } from "./components/Menu";
import { About } from "./pages/About";
import { Home } from "./pages/Home";
const App = () => {
return (
<Suspense fallback="loading">
<BrowserRouter>
<Menu />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
</Suspense>
);
};
export default App;
Suspense组件会暂停应用程序,直到它准备就绪。fallback 属性用于显示等待应用程序准备就绪期间向用户显示的内容,这里是放置加载指示器或旋转指示器的完美位置。
由于我们的翻译数量很少,你可能不会注意到明显的改进。但这确实是一个很好的做法。
🈵 结论。
借助 i18n 及其插件,创建多语言应用程序变得更加容易。
希望你喜欢这篇文章,也希望它能帮助你更轻松地理解如何制作这类应用程序。🙌
如果您知道其他更好或不同的实现方法,欢迎留言提出您的意见和建议,我将不胜感激。
如果您有兴趣与我合作项目,欢迎查看我的作品集!富兰克林·马丁内斯·卢卡斯
🔵 别忘了在推特上关注我:@Frankomtz361
🈵 演示。
https://multi-lang-app-react.netlify.app/
🈵 源代码。
https://github.com/Franklin361/multi-lang-app
文章来源:https://dev.to/franklin030601/building-a-multi-language-app-with-react-js-2och