react项目国际化实现过程以及store中如何实现多语言开发

前言

前段时间公司拓展了海外业务,因此需要前端系统的开发在完成业务需求的同时,可以实现国际化,以满足用户的使用需要。

技术挑战

之前完全没有接触相关的技术,乍一看感觉很是简单,就是翻译一下文案。但是真正着手做的时候发现面临很多技术上的挑战,随着项目上线,所以梳理一下整个开发过程中的思路。

需要进行国际化开发的内容

label、placeholder、字段校验提示信息
需要进行语言切换的内容.png
第三方插件

公司项目中用到了第三发组件:Antdesign和bootstraptable,都支持国际化开发,但是其中bootstraptable的官方文档对相关的内容较少,所以花费了较多的时间。

开发思路

了解到React有支持国际化的组件-ReactIntl,所以除第三方插件以外,项目中的文案,基本都可以实现。
项目中文案整理.png

把需要翻译的文案提取出来,放到对应的语言文件(en_US.js)中,因为项目中好多内容是重复的,所以工作量不是很大,如果内容很多的话,这种方式的工作量太大了,需要更换方案。
开发过程

  • 安装ReactR-Intl组件
    React-intl是雅虎的语言国际化开源项目FormatJS的一部分,通过其提供的组件和API可以与ReactJS绑定。上面这句话援引了官方文档的说辞,主要表达的是,这是一个很好的开源项目,有大团队支持,使用量也很大,不会有太多坑,可以放心用。
    React-intl提供了两种使用方法,一种是引用React组件,另一种是直接调取API,官方更加推荐在React项目中使用前者,只有在无法使用React组件的地方,才应该调用框架提供的API,事实上,我在项目的过程大部分都是用API的方式。
  1. 安装react-intl需要执行下面的命令:
npm install intl --save
  1. 在项目中引入react-intl
import { FormattedMessage } from 'react-intl';

该组件提供了全面的API来帮助我们进行开发,这里我们不详细讲解各个API,只是介绍如何使用,其中使用频率较高API包括:FormattedMessage(字符串格式化API)、injectIntl(高阶组件)、FormattedPlural(格式化量词)

  1. 通过代码来了解一下如何使用上述API
import { injectIntl, FormattedMessage  } from 'react-intl';
class  Mycomponent extends Reflux.Component{
render(){
      const {messages, loacal} = this.props.intl;
    return (
        <Input placeholder={messages["warehouseCodeplacehoder"]}/>
      <Button type="primary" htmlType="submit" icon="search" ><FormattedMessage id="common_SearchSubmitBtn"/></Button>
        )
    }
}
export default injectIntl(Mycomponent );

通过injectIntl 在我们在 组件的 props 上会得到一个 intl 对象,它提供formatDate、formatTime、formatPlural、formatMessage等方法和locale、formats、messages等属性,这时候我们想要显示字符串,可以使用formatMessage进行字符串的格式化。loacal可以拿到当前代码的语言环境。

可以直接利用messages['id]的方式,可以直接从语言文件中拿到当前语言环境下文档中对应id的字符串。
对于其他的字符串,可以使用formatMessageAPI进行格式化。
这种方式的应用场景还是蛮多的。

  • 配置语言文件
    语言的配置文件zh-config.js是比较关键的,我们举例说明
import appLocaleData from 'react-intl/locale-data/zh';
import messages from './zh_CN';
window.appLocale = {
  messages: Object.assign({}, messages),
  locale: 'zh-CN',
  data: appLocaleData,
  },
};
export default window.appLocale;

下面是语言文件zh.js,即key-value键值对

const zh_CN = {
            id: "中文字符串",
}
export default zh_CN;

主入口文件mian.js

import React, {Component} from 'react';
import { IntlProvider, addLocaleData } from 'react-intl';

function getLocale(lang){
  let result={};
  switch(lang){
    case 'zh_CN':
    result = require('./locale/zh-config');
    break;
    case 'en_US':
    result = require('./locale/en-US.config');
    break;
    default: result = require('./locale/zh-config');
  }
  return result.default || result;
}

class Lang extends React.Component{
  constructor(props){
      super(props);
      this.state={
          lan:" "
      }
      this.changeLang=this.changeLang.bind(this);
  }
  changeLang(p){
     if(p==='en-US'){
      this.setState({
        lan:"en_US"
      })
    }
   else {
      this.setState(
        {lan:"zh_CN"}
        )
    }
  }
  render(){
    const {lan} = this.state;
    const appLocale = getLocale(lan);
    addLocaleData(...appLocale.data);
    window.lang = appLocale.locale;
      return (
      <div >
      <IntlProvider 
          locale={appLocale.locale} 
          messages={appLocale.messages}
          >
           <LocaleProvider locale={appLocale.antd}>
          <Index />
          </LocaleProvider>
      </IntlProvider>
     
      <Radio.Group style={style}>
          <Radio.Button onClick={() => this.changeLang('zh-CN')} >中文</Radio.Button>
          <Radio.Button onClick={() => this.changeLang('en-US')}>En</Radio.Button>
      </Radio.Group>
      </div>
      )
  }
}
ReactDOM.render(<Lang/>,document.getElementById('root'));

当设置了上述两个文件时,我们可以根据当前locale值来找对应的配置文件,从而找到对应的语言文件,再去匹配对应的id,拿到value值。我们在主入口文件中进行设置,切换到对应语言时,导入该语言的配置文件和语言包,从而全面替换字符串,实现多语言切换。

  • bootstraptable的国际化开发
    bootstraptable的国际化开发难点在于,他的官方文档这方面写的较简洁,所以摸索了较长时间,最后也是同过injectIntl高阶组件包裹table,从而拿到当前语言,再配置表格自动导入对应的语言包。
import createReactClass from 'create-react-class';
import 'bootstrap';
import 'bootstrap-table';
import {injectIntl } from "react-intl";
import Main from './main/Main';

const Layout = createReactClass({
changeLang (p){
    switch(p){
      case "en-US": require("bootstrap-table-locale-en");
      break;
      case "th-TH":  require("bootstrap-table-locale-th");
      break;
      case "zh-CN": require("bootstrap-table-locale-zh");
      break;
      default:  require("bootstrap-table-locale-en");
    }
  },
  render(){
    this.changeLang(this.props.intl.locale)
    return (
      <div>  
        <Main {...this.props}></Main>
      </div>
    );
  },
});

module.exports = injectIntl(Layout);
  • antd国际化开发
    antd 提供了一个 React 组件 LocaleProvider 用于全局配置国际化文案。
    LocaleProvider 使用 React 的 context 特性,只需在应用外围包裹一次即可全局生效。
    mian.js中有具体的使用方法,可以参考。

解决store中的多语言实现

项目中调用接口的回调处理都在store文件中实现,会有一些提示信息。但是react-intl并不支持store中字符串的多语言,这个比较不好处理,在git上找到了一种解决方案
首先添加一个组件 CurrentLocale.js

import {injectIntl} from 'react-intl';

// Does not actually render anything visible. 
// We need it to provide access to internationalization for classes
// which are not a React component
class CurrentLocale extends React.Component {
    static CurrentLocale = null;

    componentWillMount() {
        if (!CurrentLocale.instance)
            CurrentLocale.instance = this;
    }

    render() {
        return null;
    }
}

export default injectIntl(CurrentLocale);

export function intl() {
    return CurrentLocale.instance.props.intl;
}

export function formatMessage(...args) {
    return intl().formatMessage(...args);
}

在项目入口处引入
然后在store文件中引入使用即可

import {formatMessage} from "../../../../locale/CurrentLocale";

class MyComponent extends Reflux.Store{
     onFunctionCompleted(res){
            if(res.state==='success'){
                message.success(formatMessage({id:"OperateSuccess"}));
            }else{
                Modal.error({title:formatMessage({id:"OperateFailed"}),content:res.error});
            }
        }
     }
export default MyComponent ;

写在后面

毕业后的第一个项目就要求实现国际化,对于之前没有项目经验又没有人带的我来说,挑战巨大。因为之前没有接触过,所以全部都是边学边用,发现可以参考的文章不是很多,所以总结一下自己开发过程中的难点和通点,如果这篇文章可以帮助到大家,那么也算是做了一件有意义的事情。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容

  • 开始之前,先了解目前常用的 React 国际化插件:The Best Libraries for React i1...
    Evelynzzz阅读 41,277评论 0 18
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,658评论 4 59
  • 时间过的真快啊,似乎就在转瞬间,我们天人永隔已经五载。你的弟弟也已经到了一个狗都嫌弃的年纪。前几天,有人跟妈妈聊...
    禾言_2176阅读 3,724评论 1 1
  • 我初中和高中的班主任很是巧地都姓张,都是三十岁出头的年轻人,偏偏都有点显老,同样永远模糊的镜片,同样喜欢把衬衫扎进...
    珂鸣阅读 233评论 0 1
  • 写给读者的话: 敬爱的读者,你好! 如果你是因为标题而进来的读者,那么,恭喜你被骗了,因为我写下的这篇文章,纯粹是...
    谢卓锟阅读 3,423评论 6 3