国际化对于某些大型公司很常见的业务,他们基本也都有本身的一套国际化规范,但对于小公司而言可能并无这些需求,平时也不太注重。若是有需求,一般解决的方法就是使用 react-intl
,react-i18next
等一些现有的npm包。如下是我大体了解的方案,以react-intl
为例,以下:node
参考文档:react-intl 国际化 文档指引react
import React, {Component} from 'react'; import ReactDOM from 'react-dom'; import {IntlProvider, FormattedMessage} from 'react-intl'; ... ReactDOM.render( <IntlProvider locale="en"> <App /> </IntlProvider>, document.getElementById('container') );)
App.jswebpack
import {FormattedMessage} from 'react-intl'; ... class App extends Component { constructor(props) { super(props); this.state = { name: 'Eric', unreadCount: 1000, }; } render() { const {name, unreadCount} = this.state; return <p> <FormattedMessage id="welcome" defaultMessage={ `Hello {name}, you have {unreadCount, number} {unreadCount, plural, one {message} other {messages}}` } values={{name: <b>{name}</b>, unreadCount}}/> </p> } }
看出来了吗?若是你没有学习过,你根本不会知道上面的属性表明着什么,这样的国际化已经变了原有的页面结构,并且每用到一处就得改动对应的代码,及其繁琐。git
import React, {Component} from 'react'; import locale from './resources/locale'; import t from 'xxx'; ... class App extends Component { render() { return <div> <header>{ t.i18n('我是要翻译的内容') }</header> <div>{ t.text(locale.a.b) }</div> //这是我另外一处要翻译的内容 </div> } }
// resources语言包结构 const locale = { a:{ b:'这是我另外一处要翻译的内容', c:'ccc', d:'xxx', ... } ... }
这是经过函数调用的思路来实现的,这种不会对react
的层级结构有太大影响,也相对灵活,其痛点在于语言包文件管理时命名让人很头痛,几十个翻译字段还好。想想你有个项目有几千个翻译字段,并且仍是一个老项目,一个个字段取名,而后在翻译到对应的页面上,香不香?这种方法不只有可能会遗漏,还可能会出错。github
对于目前已有的国际化方案,先抛开学习成本不说,立立刻手,作一个国际化项目大概须要多久呢?一天?一周?仍是一个月?,个人答案是基本在一周以上,对于紧急的项目时间是极其重要的。web
因为现有的国际化方案都极其繁琐、枯燥,且随着项目的进展愈来愈难以维护,尤为是对于多人开发的大型项目。既然不尽如人意,咱们不妨探讨一下方便快速的国际化方案。npm
对于国际化这种重复性的工做,最合适的就是自动化,一切交给程序来解决,岂不是更香?segmentfault
自动化的大体思路就是经过代码自动实现上面的方案二,具体作法:babel
为了实现自动化,最早想到的是webpack,网上搜索了一遍发现并无现成的工具可用,只能本身手写一个插件,学习了一番怎么写插件以后,发现webpack文档并无面面俱到,都只给你个大概,想了解更详细的信息只能本身去看源码,学习成本陡然增长。dom
ps:学习无果以后,我还试着想能不能经过正则来实现呢,都是字符串处理啊。想一想仍是太天真了,这估计和写个js解析引擎也差很少了。
因为webpack的构建都是基于babel实现的,直接撸一个babel插件是否是会更好呢?因而着手学习babel,发现文档更齐全,更加规范。学习babel以前,要先了解AST语法树请参考:AST Explorer(https://astexplorer.net),AST很早以前就有了,但不多用于普通开发者,直到nodejs
,react
,webpack
等技术的兴起,才被普遍使用。
babel根据AST规范将咱们的js代码解析成树状结构,如同咱们常见的dom树同样,用不一样的标签,表示不一样的语义。AST则定义了不一样的类型来表示不一样js代码的语义,具体可参考@babel/typesAPI,列出了全部已实现的语法类型及其用法。
关于babel的更多详细解析请移步至:剖析 Babel——Babel 总览 http://www.alloyteam.com/2017/04/analysis-of-babel-babel-overview/
有了babel的认知基础以后咱们就能够开始写babel插件了。它可以原子级别的控制咱们的代码,轻松实现咱们想要的功能。
export default function() { return { visitor: { Identifier(path) { // 全部类型为Identifier的AST树,都会经过此方法进行处理 const name = path.node.name; // reverse the name: JavaScript -> tpircSavaJ // 编译前 var JavaScript; // 编译后 var tpircSavaJ; path.node.name = name .split("") .reverse() .join(""); }, StringLiteral(path) { // 处理StringLiteral(字符串)类型 let {node} = path; ... }, }, }; }
实现方式:react国际化自动化插件 react-i18n-auto
使用安装
npm install react-i18n-auto --save-dev
添加babel插件配置项
{ "plugins": [ ... "react-i18n-auto" ... ] }
编译前效果:
export default class App extends React.Comment{ render(){ let title = '这是翻译的文字' return <div title={title}> <div title='这是要翻译的标题'> 我是要翻译的内容 </div> </div> } }
编译后效果:
export default class App extends React.Comment{ render(){ let title = $T('aa8ds','这是翻译的文字') return <div title={title}> <div title={$T('see23','这是要翻译的标题')}> {$T('ae22s','我是要翻译的内容')} </div> </div> } }
自动生成的语言包配置
locale.js
export let locale = { "aa8ds":"这是翻译的文字", "see23":"这是要翻译的标题", "ae22s":"我是要翻译的内容", }
react-i18n-auto实现了对源码的无污染,开发时无感知,只影响打包后的代码,实现所有自动化。只需将locale.js翻译成对应的语言包文件,根据语言加载对应的语言包便可。对于React项目可实现快速完成国际化开发任务。
更多使用详情请参考:react-i18n-auto github主页