资讯专栏INFORMATION COLUMN

[ 一起学React系列 -- 10 ] i18n

biaoxiaoduan / 232人阅读

摘要:假如有这么一段句子这件衣服是人民币如果我们想将一个数字以人民币的形式写进去的话可以这么做最终显示结果是这件衣服是人民币其实它做了两件事一个是加符号,另一个是加分隔符。同时表示人民币,表示美元。

今天来介绍一个非常international的东西。

i18n
国际化(internationalization)的简称。之所以叫i18n,是因为字母i和n之间有18个字母,所以才叫i18n。不要认为这是一个高大上的名词,其实就是因为懒才简写的。hiahiahia...

因为本系列是以React为中心,所以只介绍React项目中的国际化解决方案。当然还有很多很多...很多别的国际化解决方案,但是不是所有的轮子都适合React这辆开往幼儿园的车。
实际上国际化在日常项目中用的没那么频繁,除非有业务需求,比如要做一个非常international的项目。目前在React中比较热门的两个包就是react-intl-universal 和 react-intl。因为本文重点介绍对象是前者,所以我们先简单介绍下后者。当然在这里不会把它的使用方法列出来,而是把它的缺点列出来,为什么呢?因为笔者懒啊!
react-intl不足的地方主要是两个:

它只能用于视图层。举个例子,比如React.Comoponent对象,但是对于Vanilla JS就会显得很无力了,因为它无法在Vanilla JS中实例化。(这里会有人感到奇怪,Vanilla JS是什么鬼?哈哈...百gu度ge吧,不会发现新大陆)!

其次

想使用国际化方法,我们必须要利用它的一个方法将自己的组件转化成另外一个class。这就比较蛋疼了,例子如下:
import { injectIntl } from "react-intl";
class MyComponent extends Component {
  render() {
    const intl = this.props;
    const title = intl.formatMessage({ id: "title" });
    return (
{title}
); } }; export default injectIntl(MyComponent);

看我笔者第一篇文章的朋友应该有印象:所有被包裹过的组件,如果你想获得原本的组件的对象,那得调用相应的方法。这里也不例外,如果我们想获取组件的原对象,那就得这么做:

class MyComponent {...}
export default injectIntl(MyComponent, {withRef: true});
 
class App {
  render() {
    
  }
  getMyInstance() {
    console.log("getMyInstance", this.refs.my.getWrappedInstance());
  }
}

这样写会不会觉得太麻烦了...
所以Alibaba前端组就按捺不住了,然后就搞出了自己的react-intl-universal。看名字不就是在react-intl后面加个universal吗?的确是这样,不过笔者不清楚这个框架的核心逻辑是不是参考的react-intl,但是单从名字来看就有点"可疑"了,翻译就是react-intl的通用版(当然,纯属意淫,一笑而过!)。

react-intl-universal

作为一个国际化解决方案,首先实现国际化是它的基本功能。其次它还有一些别的功能,比如文本格式化、货币格式化、时间格式化等等,我相信这些都是我们页面开发经常使用到的功能。

i18n

首先来看一下它的技术功能:国际化
react-intl-universal采用了与组件无关的方法来实现国际化。国际化的本质其实就是将我们预先设置好的不同语言的句子按照语言环境显示在页面上。

import intl from "react-intl-universal";

通过intl这个对象来实现初始化和国际化处理。我们可以认为这个intl是一个单例对象。我们在App启动的时候对其进行初始化,尔后在别的地方再次导入的时候仍然是一个已经初始化过的对象。在这种情况下,国际化处理就会变得异常简单。其次就是准备多语言句子了,传统的在前端处理这个问题是将不同语言的句子放在不同的json文件中再导出,文件结构如下:

这样我们就可以在App启动或者切换语言的时候导入相应的json对象了。

首先是API介绍

intl对象主要有三个常用的用于国际化处理的API,determineLocale、init、get

determineLocale
看到方法名就应该知道它是用来干什么了。它用来确定在整个体系中使用的是哪种语言。看代码:

let currentLocale = intl.determineLocale({
    urlLocaleKey: "lang",
    cookieLocaleKey: "lang"
});

react-intl-universal确定语言的方式有三种,一个是通过urlLocaleKey,即lang关键字从url中获取是哪种语言。比如:http://localhost?lang=en-US,因为lang对应的值是en_US,所以语言为英文。其次是从Cookie获取,因为Cookie也是以键值对形式存储的,所以会检查当前域下的Cookie是否有对应的lang。如果上述两种都没有,那么会默认使用浏览器当前的语言类型。当然上述的urlLocaleKey和cookieLocaleKey是可以自定义的,不是固定的lang.

init

init方法即用来初始化intl对象。初始化参数主要是两个,一个是currentLocale即当前的语言,另一个是locales即当前语言对应的json对象,比如{"en-US":{"key1":"value1"} 或者 {"zh-CN":{"key1":"值1"}}

get
get方法就相对简单,就是根据键去intl中获取对应的值,这里不做过多解释。

完整的初始化过程如下:

class App extends Component{
    ....
    
    componentDidMount() {
        this.loadLocales();
    }

    loadLocales() {
        const _self = this;
        let currentLocale = intl.determineLocale({  //如果cookie和url中均没有相关参数,那么以浏览器语言为准
            urlLocaleKey: "lang",
            cookieLocaleKey: "lang"
        });

        http
            .get(`locales/${currentLocale}.json`)   //理解为按需加载并且locales文件夹需要放在public文件下供http访问
            .then(res => {
                return intl.init({
                    currentLocale,
                    locales: {
                        [currentLocale]: res.data //如果key是变量,那么需要用[]包一下
                    }
                });
            })
            .then(() => {
                _self.setState({initDone: true});
            });
    }
    
    ....
}

然后在需要国际化的地方这么使用

import intl from "react-intl-universal";

{intl.get("name")}

是不是很简单? 而且完全避免了react-intl的两个缺点。

格式化工具

前面说了react-intl-universal不仅仅可以用来做国际化处理,还可以用来做简单的文本格式化处理。下面我们列举几个常用的。

Html Snippet

假如我们的json文件中有这么一段

...
"red": "

红色

", ...

如果我们直接用get方法获取的话,那么会直接把

红色

给打印出来。如果我们想将它以html片段的形式打印出来的话,就使用getHTML方法,它在获取到句子的时候会进行解析并生成最终的Html Snippet。

Default Message

缺省值其实就是默认值,是对于json键值对的默认值。将入我们去获取一个json中没有的键值对那么系统就会报错。如何去规避这个问题呢?react-intl-universal给我们提供了这样一个方法:

intl.get("not-exist-key").defaultMessage("default message")

这是一个链式调用。如果json中没有not-exist-key这个键,那就会默认返回defaultMessage的参数。简写是intl.get("not-exist-key").d("default message")

Message With Variables

假如某个句子包含了一个变量怎么办?比如一个用户名,我们只有在用户登录的时候才知道他的用户名。

{
    "me": "你好,我是{me}"
}

此时就用到了get放的第二个参数。对于上面的例子,我们可以这样处理:

{intl.get("me", {"me": "皮卡丘"})}

get方法会找出句子中被{}包住的变量me,然后在第二个参数(json对象)找出me对应的值皮卡丘并将{me}整个用皮卡丘替换。另外需要注意的是,json对象只能为一层,不可嵌套

Display Currency

它还可以用来格式化货币。假如有这么一段句子

{
  "price": "这件衣服是 {price,number,CNY} 人民币",
}

如果我们想将一个数字以人民币的形式写进去的话可以这么做:

{intl.get("price", {"price": 1000})}

最终显示结果是:这件衣服是 ¥1,000 人民币
其实它做了两件事:一个是加符号,另一个是加分隔符。同时CNY表示人民币,USD表示美元

Display Dates

然后是日期的处理。假如有这么一段话:

{
    "date": "今天是{date,date,full}"
}

然后我们这么使用它的话:

{intl.get("date",{"date":new Date()})}

显示结果是今天是2018年12月3日星期一。其实{date,date,full}这段指令就是将date变量替换成对应日期(new Date())并以long形式展示。

同时日期展示形式有四种

short: shows date as shortest as possible

medium: shows short textual representation of the month

long: shows long textual representation of the month

full: shows dates with the most detail

他们之间有什么不同呢?我们用刚刚的例子做个展示:

short: 今天是18/12/3

medium: 今天是2018年12月3日

long: 今天是2018年12月3日

full: 今天是2018年12月3日星期一

Display Times

最后是时间。我们按部就班来。假如有这么一段话:

{
    "time": "现在时间是{time,time,short}"
}

然后我们这么使用它的话:

 

{intl.get("time",{"time":new Date()})}

显示结果是现在时间是下午5:54。其实{time,time,short}这段指令就是将time变量替换成对应日期(new Date())并以short形式展示。

但是时间展示形式只有三种,它没有full

short: shows date as shortest as possible

medium: shows short textual representation of the month

long: shows long textual representation of the month

他们之间有什么不同呢?我们用刚刚的例子做个展示:

short: 现在时间是下午5:54

medium: 现在时间是下午5:58:22

long: 现在时间是GMT+8 下午5:58:50

上述贴出来的示例都是在中文环境下。如果有兴趣的朋友可以把整个例子download下来本地运行下,边看边写,受益匪浅。好了,收拾下班咯...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99683.html

相关文章

  • [ 一起React系列 -- 12 ] React-Router4 (2)

    摘要:验证路由所谓的验证路由其实就是该路由的外层加了一层验证机制,有授权的用户才能进入,反之都无法进入。一起学系列也随着这篇的结束而告一段落了。大家一起加油最后再献上和本篇博文有关的代码链接和示例页面 时隔那么久,博主终于从睡梦中醒来开始更新博客啦!为自己的勤劳欢呼...(pia pia pia打脸)!本次我们接着上一篇博客继续聊React-Router4。上篇我们主要了解了React-Ro...

    chaos_G 评论0 收藏0
  • [ 一起React系列 -- 0 ] React技术栈习路线

    摘要:的出现真可谓是前端界的福音,正与之宗旨所说,。据统计,目前世界上有的项目使用了。技术栈学习路线直到前段时间笔者的朋友给推荐了一个,真是欣喜若狂也更加坚定了自己在继续前进的想法。这是一个外国友人总结的一套技术栈学习路线,先给传送门。 我相信点进来的同学都是冲着标题来的,当然本文也不会让各位失望。不过在正式介绍标题所述的内容之前,我们不妨先放下技术,一起回顾下自己做前端技术的心路历程。 前...

    Java3y 评论0 收藏0
  • 实战React App的i18n

    摘要:而就是产品具体实现某一种语言和文化的过程。货币的符号,以及数字分割方式各个国家都存在不同。那么有没有其他的复数形式回答当然是肯定的,比如波兰语。但这个是自己的语法,并非标准,同时这个语法还会破坏的测试,并不是一个很好的选择。 记得我刚来我们公司的时候,接手现在负责的项目的时候,我就发觉了一个问题:所有的文本资源都是硬编码在代码里面。这当然会带来很多问题。但考虑到我负责的这个项目是公司内...

    arashicage 评论0 收藏0
  • [ 一起React系列 -- 3 ] UI的扩展数据源Props以及Props约束

    摘要:所以还是印证那句话是组件渲染的唯一依据。所以对组件的进行约束是创建一个健康组件的必要条件。这里我们约束属性类型为。使用方式运行结果没有错误假如我们再加入一个子组件控制台如预期报错自定义约束万物皆有其局限性。 日常扯淡前的废话 上一篇我们介绍了React中State对象,说到它是组件渲染的唯一依据;当然我们也可以认为State是组件中的数据源之一,它保存着组件渲染的所有数据并且可以直接作...

    kumfo 评论0 收藏0
  • [ 一起React系列 -- 9 ] React中的文件下载

    摘要:本篇所说的文件下载也是基于和或者都行。的返回值是一个有意思的对象,它包含了很多方法,其中一个方法就是。通过的响应头获取到文件名。接下来就是对标签的一系列操作,然后模拟点击事件触发下载动作。 距离上次博文更新已经快一个月了,期间忙于各种事情无法脱身。今天难得闲暇 and then 就来更新啦...上篇中我们了解了下载React中如何实现文件的上传,虽然不算什么高大上的技术但实际开发的时候...

    Jacendfeng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<