本文概述
i18n在开发人员之间广为人知, 因为它是一个难题, 而且尚未解决。你的应用程序的国际化是使它在与你说不同语言的其他地区或星球上成功的第一步, 它实际上应该支持任何语言或本地语言。
在本文中, 你将学习如何本地化React应用程序。
1.安装i18n模块
为了处理你的应用程序中的国际化, 我们将使用i18n-react模块。此模块可轻松处理文本国际化和外部化。在命令行中使用以下命令安装模块:
npm install i18n-react --save
该模块运行良好, 非常易于理解和使用。有关更多信息和文档, 请在此处访问官方的Github存储库。
2.使用模块
使该模块正常工作的逻辑很简单:
- 在应用程序顶部(主要组件)需要或导入模块。
- 在应用程序顶部(主要组件)添加应用程序文本。不必从主要组件执行此操作, 也不必从子组件执行该操作。
- 使用该模块并翻译其他组件中的标签。
翻译系统使用的对象包含一些属性和字符串作为值(这些字符串包含一种语言的标签)。
提供文字
首先, 你需要提供一些文本, 这些文本将通过setTexts方法进行翻译。显然, 需要根据你使用的EcmaScript版本导入或需要该模块:
/* ES6 & TS */
import T from 'i18n-react';
/* commonJS */
var T = require('i18n-react').default;
/* when using UMD version w/o modules */
var T = window['i18n-react'].default;
// Set some texts
T.setTexts({
welcome: "Bienvenido {username}!", buttons: {
exit: "Salir", start: "Iniciar"
}
});
请注意, i18n支持插值以动态地修改翻译中的某些值(因为并非每种语言都遵循相同的结构)。实现国际化模块的基本特征之一是, 它不应仅支持简单的字符串到字符串的转换, 而应支持复数。 i18n-react支持其他本地化, 多元化, 对正式和非正式的支持, 请在此处阅读有关这些功能的更多信息。
该方法可能会初始化一次, 并可能在你的应用程序入口点执行。
正在翻译
一旦有一些可用的文本, 就可以使用它们来翻译具有相同模块的应用中的标签。你可以使用单一转换方法通过键检索标签, 也可以直接呈现不同类型的元素, 例如a, p或span元素:
import React from 'react';
import {render} from 'react-dom';
// Our custom react component with some elements inside
import Main from './Main';
// Import the translation module
import T from 'i18n-react';
// link
<T.a text="buttons.exit" href="a's href"/>
// Text
<T.text tag='h1' text="buttons.start" context: "context-if-any"/>
// Paragraph
<T.p text={{ key: "path.to.string", var1: "string", var2: 2}} anyValidHtmlAttribute="p.will.have.it"/>
// Span
<T.span text={{ key: "path.to.string", context: "context-if-any", var1: "string", var2: 2, var3: <span className="c">X</span>}}/>
/* Or translate without using an Element with the T.translate method */
<h1>
{T.translate("welcome", { username: "Bruce Wayne" })}
</h1>
默认情况下, 如果不存在指定键的翻译, 则返回键本身, 以帮助你查找丢失的翻译。通过在选项或MDText对象中提供notFound属性, 可以增强此行为。
例子
我们有以下app.js初始化一个React应用程序。在这里, 我们将使用i18n模块, 我们将提供一个对象, 其中包含一些西班牙语按钮的文本。它们将在你的应用程序初始化时声明:
import React from 'react';
import {render} from 'react-dom';
// Our custom react component with some elements inside
import Main from './Main';
// Import the translation module
import T from 'i18n-react';
// Add the texts for your APP
T.setTexts({
labels: {
accept: "Aceptar", decline: "Declinar"
}
});
// Render the Main app react component into the app div.
// For more details see: https://facebook.github.io/react/docs/top-level-api.html#react.render
render(<Main />, document.getElementById('app'));
现在, 有了一些标签, 我们可以从组件中使用它了。在我们的主要组件中, 我们将仅添加2个按钮, 这些按钮将根据单击的项目触发警报, 这些按钮的标签将通过i18n模块获得:
import React from 'react';
// Some button component example
import FlatButton from 'material-ui/FlatButton';
// Import Internationalization
import T from 'i18n-react';
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
actionAccepted = () => {
alert("You declined this");
}
actionDeclined = () => {
alert("You declined this");
}
render() {
return (
<div>
<FlatButton
label={<T.span text="labels.accept" />}
onTouchTap={this.actionAccepted}
/>
<FlatButton
label={T.translate("labels.decline")}
onTouchTap={this.actionDeclined}
/>
</div>
);
}
}
注意, text参数或translate函数的第一个参数期望对象的名称为” Javascript样式”(对象的结构)。上一个示例毫不奇怪地呈现:
如你所见, 你唯一需要担心的就是如何提供标签。请注意, 我们仅针对西班牙语执行此操作, 因此, 如果你有另一个对象使用不同的语言, 则需要以新语言的标签作为第一个参数来执行setTexts方法。
建议将翻译存储在JSON或YAML等外部文件中(而不是直接在你的代码中混合使用), 然后再以对象格式在你的代码中要求它们。
3.从文件加载区域设置数据
如前所述, 最好将语言环境存储在JSON之类的特殊文件中, 这样可以轻松管理应用中不同语言的标签。这一步完全取决于你, 是从浏览器中检索语言环境的方式, 还是将语言环境存储在文件中的方式, 等等。但是, 我们将向你展示一种可行的方法, 并且易于实现。
首先, 我们需要知道如何将语言环境存储在外部文件中, 我们将使用JSON文件。例如, 西班牙语标签将存储在具有以下内容的JSON文件(es.json)中:
{
"labels": {
"accept": "Aceptar", "decline": "Declinar"
}
}
然后, 一旦为任何一种语言添加了一些标签, 就需要在浏览器中确定用户当前正在使用哪种语言。这随你的项目而变化很大, 你可以将这种语言存储在数据库中或其他内容中, 但是为了说明这一点, 我们将使用浏览器的语言。将使用以下代码进行检索:
var language = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage;
// language would be something like es-ES or es_ES
// However we store our files with format es.json or en.json
// therefore retrieve only the first 2 digits
if (language.length > 2) {
language = language.split("-")[0];
language = language.split("_")[0];
}
// language would be now only "es"
由于我们项目的翻译文件非常简单, 因此它们将具有名称结构(仅2位数字):es, de, en, ru, jp, zh等。我们将使用它来请求正确的翻译文件。
根据你的工作方式以及应用程序语言环境的大小(和数量), 你可以从静态JSON文件中加载语言环境。
A.请求语言环境文件
如你所知, 你可以自由使用任何第三方库在React中请求文件。你可以使用本机浏览器实现的fetch(建议也包括针对过时的浏览器的whatwg-fetch之类的polyfill), 也可以使用axios之类的特殊库(或jQuery ajax, 这里没有人判断没有人)。
使它工作的逻辑非常简单, 因为你知道setTexts方法需要一个对象, 因此, 这是你唯一需要担心的事情, 为对象提供国际化所需的结构, 你就可以开始使用。以下示例(我们将使用每个开发人员都可能知道的jQuery ajax的getJSON方法)下载一个JSON文件并将其转换为Javascript对象, 该对象作为第一个参数自动传递给我们所使用的国际化库的setTexts方法刚刚安装:
$.getJSON("path-to/locales/"+ language +".json", function( data ) {
T.setTexts(data);
});
请注意, 你将需要在语言环境更新后更新组件。可以从任何更改应用程序语言的表单元素(例如, Dropdown, Select等)触发此功能。你可以在本地存储已使用的语言环境(使用localStorage或webStorage), 以防止对服务器的不必要请求执行。
B.使用webpack加载器
如果你的应用程序使用webpack并且你的语言环境文件不是那么大(只有3种语言, 并且它们不会显着增加app.min.js的文件大小), 或者它可以脱机工作(不用担心app.min.js文件大小)(例如在Electron或Cordova下运行的React应用程序), 你可以使用json文件的特殊加载程序(json-loader)将json格式的语言环境文件添加到你的应用程序中。
注意
从webpack V2开始, JSON文件将默认运行。如果使用自定义文件扩展名, 则可能仍要使用此功能。
使用以下命令安装json-loader模块:
npm install --save-dev json-loader
安装模块后, 你需要修改webpack- <env> .config.js(生产和开发)文件并添加自定义加载程序:
{
test: /\.json$/, loader: "json", include: "path/to/locales/json-files"
}
完成此操作后, 你将可以要求区域设置并将其包含在你的构建文件中。
注意
即使你的应用使用多种语言, 也建议至少在默认语言环境(例如英语)中使用此方法。这样, 你的第一页将已经具有所需的基本消息, 从而避免了使用默认标签的GET请求, 直到用户显式更改语言环境为止:
T.setTexts(require(`!json!./path-to-locales/${language}.json`));
请注意, 必须使用webpack来添加!json!字符串以使其正常工作。你可以创建一个包装所有前面步骤的自定义方法
动态更新标签
如果你已经在项目中使用本教程实现了本地化, 则可能已经注意到, 如果使用T.setTexts更新文本, 则显然没有任何反应(尽管确实发生了, 但项目中的标签并未更新, 它们仍然与你第一次执行setTexts的内容相同)。让我告诉你, 这种行为不是错误。当前, 没有模块可以自动在每个组件上动态更新元素。因此, 你将需要弄清楚(根据你使用React的方式, 通常当某人更改应用程序的语言时, 在React的情况下应重新启动或重新渲染它)如何动态地更新它们, 例如设置语言环境在组件状态下。
如果你不想花时间在上面, 那么还有其他简单的解决方案可能会根据你的项目的体系结构工作, 例如, 仅重新渲染组件就可以解决某些项目的问题:
// The app starts normally with english locale ...
// Then set it to spanish
T.setTexts(require('!json!./locales/es.json'));
// Render component again ... voila in Spanish
this.forceUpdate();
// Wait 5 seconds ...
// Now change it to German
T.setTexts(require('!json!./locales/de.json'));
// Render component again ... voila in german
this.forceUpdate();
如果你选择简单的方法, 请阅读有关如何重新渲染React组件的更多信息, 因为这里有关于如何重新渲染组件的讨论。显然应该避免使用forceUpdate, 因为它偏离了React的思维方式, 因此你可以使用另一种方式, 例如从组件重置状态:
// The app starts normally with english locale ...
// Then set it to spanish
T.setTexts(require('!json!./locales/es.json'));
// Render component again ... voila in Spanish
this.setState(this.state);
// Wait 5 seconds ...
// Now change it to German
T.setTexts(require('!json!./locales/de.json'));
// Render component again ... voila in german
this.setState(this.state);
请注意, 更新将影响单个组件(仅在将应用程序与React Router或类似的东西一起使用时有用)。
编码愉快!
评论前必须登录!
注册