随着全球化程度的提高,为不同地区和地域的广泛受众编写React应用程序意味着要让它可以跨语言访问。
通过国际化功能,React Intl库提供了将文件文本正确翻译成其他语言的机制。
在本指南中,我们将学习如何使用React Intl库来在React项目中设置国际化功能。我们将创建一个简单的应用程序,允许用户在查看应用程序时选择并看到他们喜欢的语言。
我们还将在浏览器的存储中持久化所选择的语言,这样在页面刷新后或后续访问时,内容仍然可用。
下面是我们将共同建立的最终产品。

与最终完成的网页互动,以熟悉界面,有了这个,我们就开始吧
国际化和本地化
如前所述,React Intl允许我们在React应用中设置国际化。但究竟什么是国际化?
国际化是指设计一个产品--在这里是指React应用--以便在不同地区使用的过程。它通常被缩写为Intl或i18n。
相比之下,本地化,缩写为l10n,重点是将国际化的应用程序从其原始语言翻译成特定的语言。
但是,嘿,翻译不仅仅是将文本或信息转换为目标语言。文化差异也必须加以考虑,比如数字和日期是如何写的,或者不同地区的单位和货币是如何放置的。
幸运的是,有了React Intl库,我们可以无缝地实现我们想要的结果。
设置React项目
让我们先从这个GitHub仓库中获取我们的简单React项目--其默认内容为英文。然后,我们可以添加对另外三种语言的支持。法语、德语和日语。
因此,前往终端,切换到一个目录来保存项目,并运行以下命令来下载启动文件。
git clone https://github.com/Ibaslogic/i18n_react_intl_starter
一旦项目文件被启动,用一个代码编辑器打开它们。我们必须在项目目录内,运行npm install ,为项目创建一个node_modules 的文件夹。
确保计算机上安装了Node.js以完成本教程。
现在,使用npm start ,启动开发服务器。我们应该在浏览器中看到我们的应用程序被加载到http://localhost:3000。

如果我们看一下项目的文件结构,我们应该有以下内容。
project_folder
├── node_modules
├── public
├── src
│ ├── components
│ │ ├── App.js
│ │ ├── Content.js
│ │ ├── Footer.js
│ │ └── Header.js
│ ├── index.css
│ └── index.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
很好。现在我们已经在同一个页面上了,让我们看看如何在我们的应用程序中使用React Intl库。
设置React Intl库
要使用这个库,我们必须通过停止开发服务器并从终端运行以下命令来安装它。
npm install react-intl
这个库为我们提供了在应用程序中实现国际化所需的所有API和组件。
让我们用库中的一个provider 组件来包装我们的顶级应用或根组件;在这种情况下,我们将使用<IntlProvider> 。
什么是IntlProvider 组件?
顾名思义,IntlProvider ,确保为树上的子组件提供或提供重要的配置。
这些来自React Intl的子组件被称为formatted 组件。它们负责在运行时进行适当的翻译和格式化。这些组件是。
FormattedMessageFormattedNumberFormattedDateFormattedPlural- 还有很多
对于使用React Intl库的国际化项目,经常使用FormattedMessage 组件,允许用户对简单到复杂的字符串和消息进行翻译和格式化。我们一会儿就会使用这个。
回到我们的项目中,让我们打开父组件文件src/components/App.js ,并用IntlProvider 来包装子组件。
确保在文件的顶部从react-intl ,导入IntlProvider 。
我们的文件应该看起来像这样。
...
import { IntlProvider } from "react-intl";
const App = () => {
return (
<IntlProvider>
<div>
<Header />
<Content />
<Footer />
</div>
</IntlProvider>
);
};
export default App;
让我们花点时间,检查一下IntlProvider 组件返回的内容。首先,记录该组件。
...
const App = () => {
console.log(IntlProvider);
return (
...
);
};
...
然后,检查浏览器的控制台。

通过关注defaultProps ,我们看到IntlProvider 所使用的所有默认配置道具。该组件还告诉我们要配置一个locale。
因此,让我们通过更新该组件来添加所需的道具,以识别和显示翻译到IntlProvider ,包括以下道具。
return (
<IntlProvider messages={{}} locale="en" defaultLocale="en">
<div>
<Header />
<Content />
<Footer />
</div>
</IntlProvider>
);
通过添加locale 道具并保存,错误立即消失了。
这个locale ,它接受一个字符串,决定了我们的应用程序是以什么语言呈现的。我们将根据用户在前台选择的内容动态地添加这个值。
messages 对象包含一组准备在前台显示的翻译字符串。这些也将根据当前的语言环境动态地添加。
最后,defaultLocale 道具是默认的locale,应该与应用程序的默认语言相匹配。
在我们为我们的项目加载翻译好的信息之前,让我们玩一玩,熟悉一下格式化的组件。
打开src/components/Footer.js 文件,在return 语句中添加这些格式化组件。
// ...
import { FormattedDate, FormattedNumber, FormattedPlural } from "react-intl";
const Footer = () => {
// ...
return (
<div className="container mt">
{/* ... */}
<FormattedDate value={Date.now()} />
<br />
<FormattedNumber value={2000} />
<br />
<FormattedPlural value={5} one="1 click" other="5 clicks" />
</div>
);
};
// ...
接下来,保存该文件并检查前端。我们应该有这样的显示。
5/29/2021 // your current date
2,000
5 clicks
locale 如果我们把IntlProvider 的道具改为de ,并再次保存文件,我们就有了德语格式的数据。
29.5.2021
2.000
这真是太棒了。React Intl正在通过格式化组件(FormattedDate 和FormattedNumber, 分别)根据所选区域对日期和数字进行格式化。
这些格式化组件有一个value 道具,接受要格式化的数据。
我们可以通过利用它的其他道具来进一步定制这些组件。例如,我们可以有以下内容。
return (
<div className="container mt">
{/* ... */}
<FormattedDate
value={Date.now()}
year="numeric"
month="long"
day="2-digit"
/>
<br />
<FormattedNumber value={2000} style={`currency`} currency="USD" />
<br />
{/* ... */}
</div>
);
然后,我们会得到这样的英语(en)。
May 29, 2021 // your current date
$2,000.00
然后我们会得到这样的德语(de)。
29. Mai 2021
2.000,00 $
除了这两个组件外,我们还有FormattedPlural 组件,它允许我们在我们的应用程序中处理复数。正如在上面的例子中所看到的,该组件根据它收到的值选择复数类别。
使用React Intl API
另一种方法是通过React Intl API来格式化我们的数据。虽然在React中使用组件方法是首选,因为它可以与其他React组件无缝工作,但在有些情况下需要使用API方法,例如当一个元素的placeholder,title, 或aria-label 必须被翻译。
任何时候我们使用格式化组件来渲染React元素,它们都会在幕后使用React Intl API。
让我们看看这个API是如何工作的。还是在Footer.js 文件中,从react-intl 中导入useIntl Hook 。
import { FormattedDate, FormattedNumber, FormattedPlural, useIntl } from "react-intl";
确保这是在一个功能性的React组件中完成的。
如果我们在控制台中记录useIntl Hook,我们应该看到所有需要格式化我们数据的可用函数。
// ...
const Footer = () => {
// ...
const intl = useIntl();
console.log(intl);
return (
<div className="container mt">
{/* ... */}
</div>
);
};
// ...
>
其中一个函数是formatDate ;让我们来应用它。
更新Footer.js 文件中的return 语句以包括该函数。
// ...
const Footer = () => {
// ...
const intl = useIntl();
return (
<div className="container mt">
{/* <... */}
<br />
<input placeholder={intl.formatDate(Date.now())} />
</div>
);
};
// ...
检查前台,看到输入字段。locale 记住要改变IntlProvider 的值,看看它是如何工作的。
我们可以通过添加可选的配置来进一步定制。
<input
placeholder={intl.formatDate(Date.now(), {
year: "numeric",
month: "long",
day: "2-digit",
})}
/>
现在我们已经熟悉了这些格式器,让我们回到我们的项目中。
翻译应用程序的源文本字符串
为了支持其他地区,我们必须翻译源文本。对于本教程,让我们使用谷歌翻译。
在src 目录中创建一个名为i18n 的文件夹。在这个文件夹中,创建两个不同的文件,名为locales.js 和messages.js 。locales.js 文件将保存我们支持的语言,而messages.js 文件将保存相应的翻译。
在i18n/locales.js 文件中添加这个内容。
export const LOCALES = {
ENGLISH: "en-US",
JAPANESE: "ja-JA",
FRENCH: "fr-FR",
GERMAN: "de-DE",
};
这是必要的,以避免手动添加本地化。要访问en-US ,例如,我们将使用[LOCALES.ENGLISH] 。
代码从哪里来?
代码en-US 来自于language-COUNTRY 代码,尽管我们也可以直接使用 [language](http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/gettext.html#Language-Codes)代码,而不添加 [COUNTRY](http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/gettext.html#Country-Codes)代码。
然而,在多个国家有相同语言的情况下,添加COUNTRY 澄清器可能是有用的。例如,美国的英语将是en-US ,英国的英语将是en-GB 。
React中的翻译
现在,在i18n/messages.js 文件中,添加以下翻译。
import { LOCALES } from "./locales";
export const messages = {
[LOCALES.ENGLISH]: {
learn_to: "Hello, let's learn how to use React-Intl",
price_display:
"How {n, number, ::currency/USD} is displayed in your selected language",
number_display:
"This is how {n, number} is formatted in the selected locale",
start_today: "Start Today: {d, date}",
// menu
about_project: "About the project",
contact_us: "Contact us",
},
[LOCALES.FRENCH]: {
learn_to: "Bonjour, apprenons à utiliser React-Intl",
price_display:
"Comment {n, number, ::currency/USD} $ s'affiche dans la langue sélectionnée",
number_display:
"Voici comment {n, number} sont formatés dans les paramètres régionaux sélectionnés ",
start_today: "Commencez aujourd'hui: {d, date}",
// menu
about_project: "À propos du projet",
contact_us: "Contactez-nous",
},
[LOCALES.GERMAN]: {
learn_to: "Hallo, lass uns lernen, wie man React-Intl benutzt",
price_display:
"Wie {n, number, ::currency/USD} in Ihrer ausgewählten Sprache angezeigt wird",
number_display:
"Auf diese Weise werden {n, number} im ausgewählten Gebietsschema formatiert",
start_today: "Beginnen Sie heute: {d, date}",
// menu
about_project: "Über das Projekt",
contact_us: "Kontaktiere uns",
},
[LOCALES.JAPANESE]: {
learn_to: "こんにちは、React-Intlの使い方を学びましょう",
price_display:
"選択した言語で{n, number, ::currency/USD}がどのように表示されるか",
number_display:
"これは、選択したロケールで{n, number}がフォーマットされる方法です。",
start_today: "今日から始める:{d, date}",
// menu
about_project: "プロジェクトについて",
contact_us: "お問い合わせ",
},
};
这个文件包括我们的应用程序源文本和所支持的地区的翻译。注意每一个独特的ID和键,它们可以被命名为任何东西;我们将使用它们将其相应的字符串注入我们的应用程序。
现在,让我们专注于简单的字符串,忽略大括号中的参数,也称为占位符。
接下来,让我们在IntlProvider 组件的message 道具中加载数据。
我们可以从数据中看到,我们可以像这样访问英语翻译对象。
messages[LOCALES.ENGLISH]
这同样适用于其他地区。
所以,让我们把这个英文翻译分配给provider 组件的messages 道具。在本指南的后面,我们将定义一个逻辑,根据用户选择的语言环境,动态地注入翻译的信息。
在components/App.js 文件中,在文件的顶部导入LOCALES 和messages 。
import { LOCALES } from "../i18n/locales";
import { messages } from "../i18n/messages";
然后,更新<IntlProvider> ,所以我们有以下内容。
const App = () => {
const locale = LOCALES.ENGLISH;
return (
<IntlProvider
messages={messages[locale]}
locale={locale}
defaultLocale={LOCALES.ENGLISH}
>
...
</IntlProvider>
);
};
export default App;
从代码中,我们可以推断出英语数据是在provider 组件中加载的,可以通过子组件访问。
如前所述,我们将使用FormattedMessage 来格式化这些复杂的字符串。如果我们的信息结合了日期、数字或只是简单的信息,这个组件是理想的。
我们将从转换简单的字符串开始,"你好,让我们学习如何使用React-Intl"。
前往components/Content.js 文件并导入FormattedMessage 。
import { FormattedMessage } from "react-intl";
然后,用该组件替换该字符串。
import { FormattedMessage } from "react-intl";
const Content = () => {
return (
<div className="container hero">
<h1><FormattedMessage id="learn_to" /></h1>
{/* ... */}
</div>
);
};
export default Content;
就这么简单。为了测试我们的工作,我们可以手动将App.js 文件中的locale 改为JAPANESE 。
const locale = LOCALES.JAPANESE;
保存并在前台看到变化。

上面代码中使用的FormattedMessage ,需要一个id ,其值与翻译文件中的一个特定键相匹配,然后返回当前locale的相应字符串。记住,这个id 在翻译文件中所有支持的语言中是唯一的。
使用参数
当我们看到一个包含日期、金额或数字的消息时,我们要用参数来代替它们。
早些时候,我们学习了如何使用FormattedDate 和FormattedNumber 来格式化这些类型的值。但在这里我们将使用FormattedMessage ,因为这些值是字符串的一部分。
如果我们看一下翻译文件,我们是按照这个模式来替换这些值类型的:{ key, type, format } 。
例如,我们有这样的东西。
price_display:
"How {n, number, ::currency/USD} is displayed in your selected language",
第一个元素,n ,是关键,它向数据对象查找适当的数据。第二个元素是要解释的数据类型,它符合特定的地域性。
第三个参数--是可选的--允许我们指定关于元素类型的额外信息。
在上面的案例中,我们说number 的占位符必须被格式化为美元货币。然后,React Intl定位货币。你可以在这里找到货币代码的列表。
使用FormattedMessage 来转换这种类型的字符串,我们会有这样的结果。
<FormattedMessage id="price_display" values={{ n: 59.99 }} />
正如预期的那样,id 找到了当前地区的翻译,占位符被key 的值所取代。
如果我们在我们的项目中应用这个逻辑,components/Content.js 文件现在看起来像这样。
import { FormattedMessage } from "react-intl";
const Content = () => {
return (
<div className="container hero">
<h1><FormattedMessage id="learn_to" /></h1>
<p><FormattedMessage id="price_display" values={{ n: 59.99 }} /></p>
<p><FormattedMessage id="number_display" values={{ n: 2000 }} /></p>
<p><FormattedMessage id="start_today" values={{ d: new Date() }} /></p>
</div>
);
};
export default Content;
保存该文件,并通过将component/App.js 文件中的locale改为另一种支持的语言来测试该应用程序。
const locale = LOCALES.JAPANESE;
我们的应用程序应该看起来像这样。

使用React Intl的复数化
早些时候,我们学习了如何使用<FormattedPlural> 组件来处理一个简单的复数文本。在本节中,我们将使用<FormattedMessage> 来实现富文本信息中的复数。
目前,如果我们在前台点击计数按钮,当计数为单数时,也就是在1 ,我们没有显示正确文本的逻辑。
当我们使用{ key, type, format } 模式对货币进行格式化时,我们可以使用相同的模式,但使用plural 和matches 来分别替换type 和format 。
通过应用这个模式,我们可以在我们的英语翻译对象中添加以下内容。
click_count: "You clicked {count, plural, one {# time} other {# times}}"
复数类别,one 和other ,与第一和第二个大括号内的单数和复数形式相匹配,前面是# ,代表计数编号。
如果我们将消息翻译成其他地区的语言,并应用同样的逻辑,我们会有以下的德语。
click_count:
"Sie haben {count, plural, one {# Mal} other {# Mal}} geklickt",
对于法语。
click_count:
"Vous avez cliqué {count, plural, one {# fois} other {# fois}}",
对于日语。
click_count: "{count, plural, one {# 回} other {# 回}}クリックしました",
保存文件并打开components/Footer.js 文件,使用FormattedMessage 组件渲染翻译。
然后,找到这个元素。
<p>You clicked {count} times</p>
将上述内容替换为以下内容。
<p>
<FormattedMessage id="click_count" values={{ count: count }} />
</p>
我们还必须从react-intl ,导入FormattedMessage 组件。
import {
// ...
FormattedMessage,
} from "react-intl";
再次保存该文件。让我们重新加载前端并测试我们的工作。
我们现在可以为菜单项添加翻译支持。首先,像这样导入components/Header.js 文件中的FormattedMessage 。
import { FormattedMessage } from "react-intl";
我们需要访问翻译文件中使用的各个菜单key 。要做到这一点,让我们更新menu 数组以包括key 。
const menu = [
{
key: "about_project",
// ...
},
{
key: "contact_us",
// ...
},
];
我们现在可以使用键来访问FormattedMessage 中的翻译。
首先,找到下面的代码。
{menu.map(({ title, path }) => (
<li key={title}>
<a href={path}>{title}</a>
</li>
))}
然后,用这个替换。
{menu.map(({ title, path, key }) => (
<li key={title}>
<a href={path}>
<FormattedMessage id={key} />
</a>
</li>
))}
保存该文件并测试该应用程序。
为了使前端按钮及其行动呼吁文本本地化,让我们在i18n/message.js 文件中为按钮分配键。
对于英语对象,添加以下内容。
click_button: "Please click the button below",
click_here: "click here",
对于法语,添加这个。
click_button: "Veuillez cliquer sur le bouton ci-dessous",
click_here: "Cliquez ici",
使用上述代码和谷歌翻译,为其他地区添加翻译,如日语和德语。
一旦我们完成,让我们打开components/Footer.js ,用FormattedMessage 替换文本字符串。
return (
<div className="container mt">
{/* Footer content here */}
<p><FormattedMessage id="click_button" /></p>
<button onClick={onChange}>
<FormattedMessage id="click_here" />
</button>
{/* ... */}
</div>
);
保存文件并重新加载前台。注意,按钮的描述和按钮的文字都以法语更新。

在前台添加切换语言的选项
为了在前端提供一个切换语言的选项,我们必须打开components/Header.js 文件,并在return 语句上方添加以下内容。
// Languages
const languages = [
{ name: "English", code: LOCALES.ENGLISH },
{ name: "日本語", code: LOCALES.JAPANESE },
{ name: "Français", code: LOCALES.FRENCH },
{ name: "Deutsche", code: LOCALES.GERMAN },
];
然后,在文件的顶部导入LOCALES 。
import { LOCALES } from "../i18n/locales";
接下来,我们将循环浏览languages 数组,生成我们的下拉菜单。
还是在文件中,找到这个div 容器元素。
<div className="switcher">
{/* Language switch dropdown here */}
</div>
然后,更新它,看到以下内容。
<div className="switcher">
{/* Language switch dropdown here */}
Languages{" "}
<select>
{languages.map(({ name, code }) => (
<option key={code} value={code}>
{name}
</option>
))}
</select>
</div>
保存它并在src/index.css 文件中添加这个简单的样式。
.switcher select {
width: 99px;
height: 30px;
}
再次保存该文件,并在前端查看下拉菜单。目前,在不同的语言之间切换并不改变页面内容,所以让我们来解决这个问题。
如果熟悉React的表单处理,这个过程应该是小菜一碟。如果不熟悉,那也没关系,因为我们会一起做。
在React中,表单输入应该是一个受控的输入,与HTML表单不同。
为了做到这一点,我们将在select 元素上添加一个value 的道具和一个onChange 事件。value 道具被指定为当前的语言环境,而onChange 则根据用户的选择来更新数值。
同时,更新select 元素以包括这些道具。
<select onChange="" value="">
..
</select>
由于IntlProvider 是在父组件中,App.js 也必须知道当前的locale,所以让我们在文件中设置好逻辑。
我们可以通过props将必要的数据下传到Header 子组件中。这个过程被称为道具钻取。
打开components/App.js ,添加一个默认语言为英语的状态。
import { useState } from "react";
...
const App = () => {
const locale = LOCALES.ENGLISH;
const [currentLocale, setCurrentLocale] = useState(locale);
return (
<IntlProvider
messages={messages[currentLocale]}
locale={currentLocale}
defaultLocale={LOCALES.ENGLISH}
>
...
</IntlProvider>
);
};
export default App;
不要忘记从React导入useState Hook,以获得当前的locale。
现在,让我们把locale传递给Header 子组件,并在select 元素中使用它。
在App.js 文件中,更新<IntlProvider /> 中的<Header /> 实例,所以我们有以下内容。
<Header currentLocale={currentLocale}/>
保存文件并打开Header.js ,以访问数据;将其传递给select 元素的value 道具。
...
const Header = (props) => {
...
return (
<header>
<div className="container header_content">
...
<nav>
...
</nav>
<div className="spacer"></div>
<div className="switcher">
{/* Language switch dropdown here */}
Languages{" "}
<select onChange="" value={props.currentLocale}>
...
</select>
</div>
</div>
</header>
);
};
export default Header;
调用props ,如下所示。
const Header = (props) => {
接下来,我们将使用onChange 事件来处理更新。
在App.js 文件中,在return 语句上方添加以下处理程序,并通过道具将其向下传递给Header 组件。
const handleChange = (e) => {
setCurrentLocale(e.target.value);
};
现在的代码看起来像这样。
...
const App = () => {
...
const handleChange = (e) => {
setCurrentLocale(e.target.value);
};
return (
...
<div>
<Header currentLocale={currentLocale} handleChange={handleChange} />
...
</div>
...
);
};
保存文件并访问Header 组件内的处理程序。
简单地更新select 元素,所以我们有以下内容。
<select onChange={props.handleChange} value={props.currentLocale}>
现在我们可以测试我们的应用程序了。它成功了!很好。
确保将下拉列表旁边的语言文本也翻译出来。
在浏览器的本地存储中保留所选的语言环境
目前,如果我们从默认的语言环境中切换出来并重新加载页面,内容就会恢复到默认状态。我们不希望这样。
在这一节中,我们将看到如何轻松地将所选的区域设置持久化到浏览器的存储中。这意味着,在重新加载页面时,以及在以后的访问中,用户所喜欢的语言内容仍然可以在页面上看到,从而进一步改善用户的体验。
首先,打开App.js 文件,在return 语句上方添加这段代码。
//localstorage
function getInitialLocal() {
// getting stored items
const savedLocale = localStorage.getItem("locale");
return savedLocale || LOCALES.ENGLISH;
}
然后,更新状态以使用这个函数。
const [currentLocale, setCurrentLocale] = useState(getInitialLocal());
最后,更新handleChange ,以包括存储对象。
const handleChange = (e) => {
setCurrentLocale(e.target.value);
// storing locale in the localstorage
localStorage.setItem("locale", e.target.value);
};
从App.js 文件中删除这一行,因为它现在是多余的。
const locale = LOCALES.ENGLISH;
现在,保存该文件并测试该应用程序。确保重新加载页面,看看数据是否持续存在于存储中。
因为我们开始使用getItem() 方法访问本地存储,以检查任何保存的locale或返回一个回退,返回的locale被分配到状态变量currentLocale 。
同样地,我们也使用setItem() 方法将用户选择的locale保存到Storage 对象中。
阅读关于如何在本地存储中持久化你的React组件的状态来了解更多。
总结
我们已经涵盖了关于React Intl库的几乎所有我们需要知道的东西。我们已经学会了如何将该库应用到React应用程序中,以及如何在本地存储中持久化用户选择的地区。
我希望你觉得这篇指南对你有帮助。如果你有问题或贡献,我在评论区。你也可以在这个GitHub仓库里找到整个项目的源代码。
The postReact Intl: Internationalize your React appsappeared first onLogRocket Blog.