最近在项目中添加了语言国际化的功能。html
语言国际化,也有人说成是语言本地化,其实就是为Web App添加多语言,咱们的项目当前包含了中文版和英文版,按理来讲『逐字替换』也不是多大事儿,可是,这么Low的作法,有钱途吗?node
一开始的时候,我考虑的是传统的为整个项目添加config文件,根据不一样的语言和地区,加载不一样的config文件,就可以达到界面语言切换的目的。固然,也正是由于这个想法太过于幼稚,因此才被称为『一开始』的想法。语言的国际化不只仅是将界面上的UI文字翻译成另外一种语言,还包括了日期&时间显示,数字显示(英文环境下每隔3位一个逗号:1,000),量词的显示(一个苹果是apple,两个苹果就应该是apples),甚至还有一个字符串中间插了一个变量的状况("今天午餐吃了{count}个鸡腿")...react
因此这并不仅是一个简单的字符替换问题,而且,咱们要很方便的导出一个目录,放到word或者page当中,给到其余同事对照着进行翻译工做,这个很是重要!!难道你要让产品经理直接在代码里改么?或者你想一个一个搜索替换?不考虑清楚就干的话,相信我,You'll pay for this。git
做为一个有追求的代码家,你确定不但愿在index.html当中增长一行<Script>引用吧?另外,UI中的文字所有都是使用图片的那个同窗,请起立,滚。若是想要在一个React项目中,优雅的import something from somewhere,而后将界面中的文字用<首字母大写 /> 组件替代,最后经过简单的配置实现语言的国际化,那咱们就用React-intl吧。github
注意:本文说的是用法,源码我也没有拜读过,太深的东西去github给做者留言吧。web
项目地址: https://github.com/yahoo/reac...
React-intl是雅虎的语言国际化开源项目FormatJS的一部分,经过其提供的组件和API能够与ReactJS绑定。上面这句话援引了官方文档的说辞,主要表达的是,这是一个很屌的开源项目,有大团队支持,使用量也很大,不会太坑爹,大家放心用。虽然雅虎都快被收购了。npm
React-intl提供了两种使用方法,一种是引用React组建,另外一种是直接调取API,官方更加推荐在React项目中使用前者,只有在没法使用React组件的地方,才应该调用框架提供的API,事实上,我在项目的过程当中真的遇到了没法使用组件的状况,这个我会另外写一篇文章来描述。redux
React-intl提供的React组件有以下几种:浏览器
<IntlProvider />
包裹在须要语言国际化的组建的最外层,为包含在其中的全部组建提供包含id和字符串的键值对。(如:"homepage.title":"Hommily";
)微信
a. <FormattedDate />
用于格式化日期,可以将一个时间戳格式化成不一样语言中的日期格式。
传入时间戳做为参数:
<FormattedDate value={new Date(1459832991883)} />
输出结果:
<span>4/5/2016</span>
b. <FormattedTime>
用于格式化时间,效果与<FormattedDate />类似。
传入时间戳做为参数:
<FormattedTime value={new Date(1459832991883)} />
输出结果:
<span>1:09 AM</span>
c. <FormattedRelative />
经过这个组件能够显示传入组件的某个时间戳和当前时间的关系,好比 “ 10 minutes ago"。
传入时间戳做为参数:
<FormattedRelative value={Date.now()} />
输出结果:
<span>now</span>
10秒以后的输出结果:
<span>10 seconds ago</span>
1分钟以后的输出结果:
<span>1 minute ago</span>
a. <FormattedNumber />
这个组件最主要的用途是用来给一串数字标逗号,好比10000这个数字,在中文的语言环境中应该是1,0000,是每隔4位加一个逗号,而在英语的环境中是10,000,每隔3位加一个逗号。
传入数字做为参数:
<FormattedNumber value={1000} />
输出结果:
<span>1,000</span>
b. <FormattedPlural />
这个组件可用于格式化量词,在中文的语境中,其实不太会用获得,好比咱们说一个鸡腿,那么量词就是‘个’,咱们说两个鸡腿,量词仍是‘个’,不会发生变化。可是在英文的语言环境中,描述一个苹果的时候,量词是apple,当苹果数量为两个时,就会变成apples,这个组件的做用就在于此。
传入组件的参数中,value为数量,其余的为不一样数量时对应的量词,在下面的例子中,一个的时候量词为message,两个的时候量词为messages。实际上能够传入组件的量词包括 zero, one, two, few, many, other 已经涵盖了全部的状况。
<FormattedPlural value={10} one='message' other='messages'/>
传入组件的量词参数能够是一个字符串,也能够是一个组件,咱们能够选择传入<FormattedMessage />
组件,就能够实现量词的不一样语言的切换。
输出结果:
<span>messages</span>
a. <FormattedMessage />
这个组件用于格式化字符串,是全部的组件中使用频率最高的组件,由于基本上,UI上面的每个字符串都应该用这个组件替代。这个组件的功能丰富,除了能够根据配置输出不一样语言的简单字符串以外,还能够输出包含动态变化的参数的复杂字符串,具体的用法在后面的例子中会慢慢叙述。
好比咱们在locale配置文件中写了以下内容:
const app = { greeting:'Hello Howard!", } export default app;
使用这个组件的时候,咱们这么写:
<FormattedMessage id='app.greeting' description='say hello to Howard' defaultMessage='Hello, Howard!' />
id指代的是这个字符串在locale配置文件中的属性名,description指的是对于这个位置替代的字符串的描述,便于维护代码,不写的话也不会影响输出的结果,当在locale配置文件中没有找到这个id的时候,输出的结果就是defaultMessage的值。
输出的结果:
<span>Hello, Howard!</span>
b. <FormattedHTMLMessage />
这个组件的用法和<FormattedMessage />彻底相同,惟一的不一样就是输出的字符串能够包含HTML标签,可是官方不太推荐使用这个方法,若是能够想办法用<FormattedMessage />的话,就不该该使用这个组件,我揣测应该是性能方面不如<FormattedMessage />,这个组件的用法我就不举例了。
Well,到此为止,已经把React-intl提供的全部组件介绍完了,下面就给你们介绍一下具体如何去使用吧。
(本文例子运行在OSX环境,Window操做方法的终端在安装的时候要注意用管理员身份运行)
假设你已经在你的系统中安装了node.js和npm,若是你还不知道这两个是什么东西,请自行百度,对,在百度都能找到答案。
打开终端,进入项目根目录,输入如下指令安装React-intl:
npm install react-intl -save
注意:为了兼容Safari各个版本,须要同时安装 intl,intl在大部分的『现代』浏览器中是默认自带的,可是Safari和IE11如下的版本就没有了,这里须要留个心眼。
安装intl须要在终端中输入如下指令:
npm install intl --save
这里还有一个注意:因为React-intl的每个组件的使用方法大同小异,和ReactJS的语法彻底一致,因此我就仅仅描述如何使用<FormattedMessage />这个组件的用法,借此抛砖引玉,相信看完以后已经足够帮助你迅速的去使用这个开源框架了。
import { FormattedMessage } from 'react-intl';
因为我使用的是ES6 的语法,因此是支持直接引用组件的。你固然可使用ES5的方式引用,可是,这样有前途么?
require ReactIntl from 'react-intl';
这里,咱们将文件命名为zh_CN.js和en_US.js,表明中文和美式英语的配置包。
在zh_CN.js编写以下代码:
const zh_CN = { hello:"你好,方浩!", superHello:"你好,{ someone } !" } export default zh_CN;
在en_US.js编写以下代码:
const en_US = { hello:"Hello, Howard!", superHello:"Hello, { someone } !" } export default en_US;
因而,咱们就建立好了locale文件,可是,在实际的项目中配置文件不会这么简单,您可能须要根据业务需求按照不一样的页面或者不一样的功能块建立不一样的文件树,而后用模块化的方法将不一样的配置文件进行组织,已达成你的目标,这里我也就没能力逼逼太多了。
你可能想问,{ someone }
是什么鬼?其实悟性高一些的话就应该已经猜到,这个应该就是前面提到过的在字符串中插入动态参数的用法,事实上也是这样的。
<IntlProvider />
使用<IntlProvider />
组件包裹住须要您须要进行语言国际化的组件,用法和React-redux的<Provider />
差很少,当<IntlProvider />
包裹住某个组件的时候,这个组件自己和组件内部包含的子组件就能够得到全部React-intl提供的接口以及在<IntlProvider />
中引入的locale配置文件的内容。
import React from 'react'; import { render } from 'react-dom'; //引入locale配置文件,具体路径根据实际状况填写 import zh_CN from './zh_CN'; import en-US from './en-US'; //若是浏览器没有自带intl,则须要在使用npm安装intl以后添加以下代码 import intl from 'intl'; addLocaleDate([...en,...zh]); ... ... render( <IntlProvider locale={'en'} messages={en_US} > <App /> </IntlProvider>, document.getElementById('container') );
<IntlProvider />
须要传递两个参数:
locale是传递须要国际化的语言的缩写,经过这个参数能够肯定格式化日期,数字,量词的时候按照哪种语言的规则,这个是规则是intl提供的,通常浏览器会内置这个库,可是在Safari和IE11以前须要本身安装,安装的方法前面已经说起,请本身翻阅。
messages是用于传递刚刚咱们在第3步中定义的配置文件的,从示例代码中咱们能够看出,首先咱们使用Import语句引入了配置文件,而后将配置文件的内容传递给了messages这个参数,此时<App />组件中的全部组件均可以拿到配置文件中的内容了。
那个跳起来的同窗,请先坐下,我猜你是想问,是否是每次都要手动修改这两个参数以适配不一样语言呢?
其实否则,咱们彻底能够按照下面的作法自动识别当前浏览器的语言:
chooseLocale(){ switch(navigator.language.split('_')[0]){ case 'en': return 'en_US'; break; case 'zh': return 'zh_CN'; break; ... ... ... default: return 'en_US'; break; } } render( <IntlProvider locale={navigator.language} messages={chooseLocale()} > <App /> </IntlProvider>, document.getElementById('container') );
您还须要知道的是,<IntlProvider />
是能够嵌套使用的,也就是说,在一个<IntlProvider />
内部还能够有N个<IntlProvider />
,这个功能的实际意义就是能够在英文网站中嵌套一个中文的或者德语的或者法语的板块,应用起来会更加灵活一些。
<FormattedMessage />
前面的几个步骤其实都是为了这个步骤作铺垫的,在添加了<IntlProvoder />以后,咱们就能够在其包裹的<App />及<App />包含的全部组件中获取到配置文件的信息,传入<FormattedMessage />组件的id参数也能其在配置文件中对应的字符串了。
使用的方法以下:
<FormattedMessage id='hello' description='say hello to Howard.' defaultMessage='Hello, Howard' />
在Js执行的时候,组件就会找到配置文件中,‘hello'键名对应的字符串'Hello, Howard!'.
输出的结果为:
<span>Hello, Howard!</span>
那么如何输出含有动态参数的字符串呢?好比Hello,Johnson!,若是我要问候的对象是一个变量呢?
那就这么写呗。。
<FormattedMessage id='superHello' description='say hello to Howard.' defaultMessage='Hello, {someone}' values={ someone:this.props.name, } />
以上的例子中,赋给someone的就是一个变量(假设这个变量是经过参数传进这个组件的),注意,若是是这样的话,那么locale配置文件中就要这么写。
superHello:"你好,{ someone } !"
前面其实提过了,怕你忘了...我已经悄无声息的把id换成了superHello。
更牛逼的是,这个someone还能够包含HTML标签!
<FormattedMessage id='superHello' description='say hello to Howard.' defaultMessage='Hello, {someone}' values={ someone:<b>this.props.name</b>, } />
输出结果:
<span>Hello, <b>Howard</b>!</span>
因而,这个名字就被加粗了。
眼尖的同窗又要跳起来了,“webFunc,为何全部的输出都带一个<span>标签,我就不能换成别的么?”
不要着急,我正要说这个,对于这个问题,官方的文档是这么说的。
By default<formattedMessage>
will render the formatted string into
a<span>
. If you need to customize rendering, you can either wrap it
with another React element (recommended), specify a different tagName
(e.g., 'div'), or pass a function as the child.
翻译过来就是,默认的是会包裹在<span>
标签中的,若是想要让输出的字符串包裹在其余标签中的话,好比你想包裹在<div>
中,你就把<FormattedMessage />
组件包含在一对<div>
中间,这是一种官方更加推荐的作法。
<div> <FormattedMessage id='hello' description='say hello to Howard.' defaultMessage='Hello, Howard!" /> </div>
Well, that's stupid...
或者你能够给<FormattedMessage>传入一个tagName的参数。好比:
<FormattedMessage id='hello' tagName = 'div' description='say hello to Howard.' defaultMessage='Hello, Howard!' />
就会输出:
<div>Hello, Howard!</div>
比较奇葩的是,也是我揣测做者不推荐使用这种方法的缘由是...只要你高兴,tagName能够传入任意字符串,好比 shit:
<FormattedMessage id='hello' tagName = 'shit' description='say hello to Howard.' defaultMessage='Hello, Howard!' />
就会输出:
<shit>Hello, Howard!</shit>
Yes, shit happens.
看到这里,你应该已经会使用React-intl对你的项目进行语言国际化了,没有进一步描述的地方,请自行查阅官方文档(项目地址:https://github.com/yahoo/reac...,或者给我留言,虽然我不必定会及时回复。
--写在后面:
语言国际化应该是一个比较常常遇到的需求,可是我在完成项目的过程当中,看到的中文的资料却至关少,虽然这不是一篇很是牛叉的技术文章,可是可能会帮到不少人,如若如此,也便知足了。
——方浩(webFunc)
对了,你能够关注一下个人微信公众号:webcoding