让前端飞程序员

react-intl 实现多语言

2018-11-05  本文已影响10人  旧丶时候

0. 前言

timg.jpeg

最近在项目中添加了语言国际化,多语言的功能。

React-intl是雅虎的语言国际化开源项目FormatJS的一部分,通过其提供的组件和API可以与ReactJS绑定。



React-intl提供了两种使用方法,一种是引用React组建,另一种是直接调取API。

React-intl提供的React组件有如下几种:

<IntlProvider />包裹在需要语言国际化的组建的最外层,为包含在其中的所有组建提供包含id和字符串的键值对。

日期时间

1.<FormattedDate /> 用于格式化日期,能够将一个时间戳格式化成不同语言中的日期格式。

传入时间戳作为参数:

<FormattedDate 
    value={new Date(1459832991883)}
/>

输出结果:

<span>4/5/2016</span>

2.<FormattedTime> 用于格式化时间,效果与<FormattedDate />相似。

传入时间戳作为参数:

<FormattedTime 
   value={new Date(1459832991883)}
/>

输出结果:

<span>1:09 AM</span>

3.<FormattedRelative /> 通过这个组件可以显示传入组件的某个时间戳和当前时间的关系,比如 "10 minutes ago" 。

传入时间戳作为参数:

<FormattedRelative 
    value={Date.now()}
/>

输出结果:

<span>now</span>

10秒之后的输出结果:

<span>10 seconds ago</span>

1分钟之后的输出结果:

<span>1 minute ago</span>
数字量词

1.<FormattedNumber />这个组件最主要的用途是用来给一串数字标逗号,比如10000这个数字,在中文的语言环境中应该是1,0000,是每隔4位加一个逗号,而在英语的环境中是10,000,每隔3位加一个逗号。

传入数字作为参数:

<FormattedNumber 
    value={1000}
/>

输出结果:

<span>1,000</span>

2.<FormattedPlural /> 这个组件可用于格式化量词,在中文的语境中,其实不太会用得到,比如我们说一个鸡腿,那么量词就是‘个’,我们说两个鸡腿,量词还是‘个’,不会发生变化。但是在英文的语言环境中,描述一个苹果的时候,量词是apple,当苹果数量为两个时,就会变成apples,这个组件的作用就在于此。

传入组件的参数中,value为数量,其他的为不同数量时对应的量词,在下面的例子中,一个的时候量词为message,两个的时候量词为messages。实际上可以传入组件的量词包括 zero, one, two, few, many, other 已经涵盖了所有的情况。

<FormattedPlural
    value={10}
    one='message'
    other='messages'/>

传入组件的量词参数可以是一个字符串,也可以是一个组件,我们可以选择传入<FormattedMessage />组件,就可以实现量词的不同语言的切换。
输出结果:

<span>messages</span>
字符串的格式化

1.<FormattedMessage /> 这个组件用于格式化字符串,是所有的组件中使用频率最高的组件,因为基本上,UI上面的每一个字符串都应该用这个组件替代。

比如我们在locale配置文件中写了如下内容:

const zhCn = {
  hello:"你好,世界!",
};
export default zhCn;

使用这个组件的时候,我们这么写:

<FormattedMessage
    id=hello'
    description=问好'
    defaultMessage='你好,世界!'
    />

id指代的是这个字符串在locale配置文件中的属性名,description指的是对于这个位置替代的字符串的描述,便于维护代码,不写的话也不会影响输出的结果,当在locale配置文件中没有找到这个id的时候,输出的结果就是defaultMessage的值。

输出的结果:

<span>你好,世界!</span>

2.<FormattedHTMLMessage />这个组件的用法和<FormattedMessage />完全相同,唯一的不同就是输出的字符串可以包含HTML标签,但是官方不太推荐使用这个方法,这个组件的用法我就不举例了。

1. 安装

假设你已经在你的系统中安装了node.js和npm。

打开终端,进入项目根目录,输入以下指令安装React-intl:

npm install react-intl -save

注意:为了兼容Safari各个版本,需要同时安装 intl,intl在大部分的『现代』浏览器中是默认自带的,但是Safari和IE11以下的版本就没有了,这里需要注意一下。

安装intl需要在终端中输入以下指令:

npm install intl --save

2. 引用

import { FormattedMessage } from 'react-intl'

由于我使用的是ES6 的语法,所以是支持直接引用组件的。你当然可以使用ES5的方式引用。

require ReactIntl from 'react-intl'

3. 创建locale配置文件

这里,我们将文件命名为zh_CN.js、en_US.js、ja_JP.js、ko_KR.js,分别代表中文、美式英语、日语、韩语的配置包。

在zh_CN.js编写如下代码:

const zhCn = {
  hello:"你好,世界!",
};
export default zhCn;

在en_US.js编写如下代码:

const enUs = {
  hello:"Hello, world!",
};
export default enUs;

在ja_JP.js编写如下代码:

const jaJp = {
  hello:"こんにちは、世界!",
};
export default jaJp;

在ko_KR.js编写如下代码:

const koKr = {
  hello:"안녕, 세계!",
};
export default koKr;

我们就创建好了locale文件。

4. 使用<IntlProvider />

使用<IntlProvider />组件包裹住需要您需要进行语言国际化的组件,用法和React-redux的<Provider />差不多,当<IntlProvider />包裹住某个组件的时候,这个组件本身和组件内部包含的子组件就可以获得所有React-intl提供的接口以及在<IntlProvider />中引入的locale配置文件的内容。

addLocaleData:引入本地的 localedata
IntlProvider:包裹需要翻译的组件,用来传递给子类语言信息
FormattedMessage :包裹需要实现多国语言的文字

image.png
locale是传递需要国际化的语言的缩写,通过这个参数可以确定格式化日期,数字,量词的时候按照哪一种语言的规则,这个是规则是intl提供的,一般浏览器会内置这个库,但是在Safari和IE11之前需要自己安装,安装的方法前面已经提及,请自己翻阅。

messages是用于传递刚刚我们在第3步中定义的配置文件的,从示例代码中我们可以看出,首先我们使用Import语句引入了配置文件,然后将配置文件的内容传递给了messages这个参数,此时<App />组件中的所有组件都可以拿到配置文件中的内容了。

我是把每次选择的语言存贮在本地localStorage中,这样下次进来就是你上次选中的语言,如果没有选中那就是默认中文。

image.png

5. 实现效果

好了,看一下实现效果


GIF.gif

6. 配置文件代码

一、

import React from "react";
import PropTypes from "prop-types";
import { IntlProvider, addLocaleData } from "react-intl";
import zh from "react-intl/locale-data/zh";
import en from "react-intl/locale-data/en";
import ja from "react-intl/locale-data/ja";
import ko from "react-intl/locale-data/ko";
import { chooseLocale, getLanguage, setLanguage } from "./../../lib/tools/utils.js";

addLocaleData([...zh, ...en, ...ja, ...ko]);


const Intl = ({ children }) => {
  const defaultLang = getLanguage();

  if (!defaultLang) {
    setLanguage("zh");
  }

  if (defaultLang === "zh") {
    setLanguage("zh");
  }

  if (defaultLang === "en") {
    setLanguage("en");
  }

  if (defaultLang === "ja") {
    setLanguage("ja");
  }

  if (defaultLang === "ko") {
    setLanguage("ko");
  }
  return (
    <IntlProvider locale={defaultLang} messages={chooseLocale()}>
      {children}
    </IntlProvider>
  );
};

Intl.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Intl;

二、

import enUs from "./../locale/en_US";
import zhCn from "./../locale/zh_CN";
import jaJp from "./../locale/ja_JP";
import koKr from "./../locale/ko_KR";

// 设置语言
export function setLanguage(lang) {
  const defaultLang = localStorage.setItem("lang", lang);
  return defaultLang;
}

// 获取语言
export function getLanguage() {
  const defaultLang = localStorage.getItem("lang");
  return defaultLang;
}

// 修改html.lang显示
export function changeHtmlLang(lang) {
  return document.getElementById("lang").lang = lang;
}

// 匹配语言
export function chooseLocale() {
  switch (getLanguage()) {
  case "en":
    changeHtmlLang(getLanguage());
    return enUs;
  case "zh":
    changeHtmlLang(getLanguage());
    return zhCn;
  case "ja":
    changeHtmlLang(getLanguage());
    return jaJp;
  case "ko":
    changeHtmlLang(getLanguage());
    return koKr;
  default:
    changeHtmlLang(getLanguage());
    return zhCn;
  }
}

7. 结束语

好了,国际化已经over了,你可以试一试了!!!

上一篇 下一篇

猜你喜欢

热点阅读