di18n-translate

前端通用国际化解决方案

背景

前端技术日新月异,技术栈繁多。以前端框架来说有ReactVueAngular等等,再配以webpackgulpBrowserifyfis等等构建工具去满足日常的开发工作。同时在日常的工作当中,不同的项目使用的技术栈也会不一样。当需要对部分项目进行国际化改造时,由于技术栈的差异,这时你需要去寻找和当前项目使用的技术栈相匹配的国际化的插件工具。比如:

  • vue + vue-i18n
  • angular + angular-translate
  • react + react-intl
  • jquery + jquery.i18n.property

等等,同时可能有些页面没有使用框架,或者完全是没有进行工程化的静态前端页面。

为了减少由于不同技术栈所带来的学习相关国际化插件的成本及开发过程中可能遇到的国际化坑,在尝试着分析前端国际化所面临的主要问题及相关的解决方案后,我觉得是可以使用更加通用的技术方案去完成国际化的工作。

国际化所面临的问题

1.语言翻译

  • 静态文案翻译(前端静态模板文案)
  • 动态文案翻译(server端下发的动态数据)

2.样式

  • 不同语言文案长度不一样造成的样式错乱
  • 图片的替换

3.map表维护

4.第三方服务

  • SDK

5.本地化

  • 货币单位
  • 货币汇率
  • 时间格式

6.打包方案

  • 运行时
  • 编译后

解决方案

在日常的开发过程当中,遇到的最多的需要国际化的场景是:语言翻译,样式,map表维护打包方案。接下来针对这几块内容并结合日常的开发流程说明国际化的通用解决方案。

首先来看下当前开发环境可能用的技术栈:

1.使用了构建工具

  • webpack
  • gulp
  • fis
  • browserify
  • ...

基于这些构建工具,使用:

  • Vue
  • Angular
  • React
  • Backbone
  • ...
  • 未使用任何framework

2.未使用构建工具

  • 使用了jqueryzepto等类库
  • 原生js

其中在第一种开发流程当中,可用的国际化的工具可选方案较多:

从框架层面来看,各大框架都会有相对应的国际化插件,例如:vue-i18nangular-translatereact-intl等,这些插件可以无缝接入当前的开发环节当中。优点是这些框架层面的国际化插件使用灵活,可以进行静态文案的翻译,动态文案的翻译。缺点就是开发过程中使用不同的框架还需要去学习相对应的插件,存在一定的学习成本,同时在业务代码中可能存在不同语言包判断逻辑。

从构建工具层面来看, webpack有相对应的i18n-webpack-plugingulpgulp-static-i18n等相应的插件。这些插件的套路一般都是在你自定义map语言映射表,同时根据插件定义好的需要被编译的代码格式,然后在代码的编译阶段,通过字符串匹配的形式去完成静态文案的替换工作。这些插件仅仅解决了静态文案的问题,比如一些样式,图片替换,class属性,以及动态文案的翻译等工作并没有做。
事实上,这些插件在编译过程中对于样式图片替换class属性等替换工作是非常容易完成的,而动态文案的翻译因为缺少context,所以不会选择使用这些编译插件去完成动态文案的翻译工作。相反,将动态文案的翻译放到运行时去完成应该是更加靠谱的。

但是换个角度,抛开基于这些构建工具进行开发的框架来说,构建工具层面的国际化插件可以很好的抹平使用不同框架的差异,通过将国际化的过程从运行时转到编译时,在编译的过程中就完成大部分的国际化任务,降低学习相对应国际化插件的成本,同时在构建打包环节可实现定制化。不过也存在一定的缺点,就是这些构建工具层面的国际化插件只能完成一些基本的静态文案的翻译,因为缺少context,并不能很好的去完成动态文案的翻译工作,它比较适用于一些纯静态,偏展示性的网页。

在第二种开发流程当中,可使用的国际化工具较少,大多都会搭配jquery这些类库及相对应的jquery.i18ni18next等插件去完成国际化。

综合不同的构建工具,开发框架及类库,针对不同的开发环境似乎是可以找到一个比较通用的国际化的方案的。

这个方案的大致思路就是:通过构建工具去完成样式图片替换class属性等的替换工作,在业务代码中不会出现过多的因国际化而多出的变量名,同时使用一个通用的翻译函数去完成静态文案动态文案的翻译工作,而不用使用不同框架提供的相应的国际化插件。简单点来说就是:

  • 依据你使用的构建工具 + 一个通用的翻译函数去完成前端国际化

首先,这个通用的语言翻译函数: di18n-translate。它所提供的功能就是静态和动态文案的翻译, 不依赖开发框架及构建工具。

npm install di18n-translate
// 模块化写法const LOCALE = 'en'const DI18n = require('di18n-translate')const di18n = new DI18n({locale: LOCALE,     // 语言环境 isReplace: false,   // 是否开始运行时(适用于没有使用任何构建工具开发流程) messages: {         // 语言映射表 en: {你好: 'Hello, {person}'},zh: {你好: '你好, {person}'}}})di18n继承于一个翻译类,提供了2个方法`$t`, `$html`:di18n.$t('你好', {person: 'xl'})   // 输出: Hello, xldi18n.$html(htmlTemp)   // 传入字符串拼接的dom, 返回匹配后的字符串,具体示例可见下文// 外链形式<script src="./lib/di18n-translate/index.js"></script><script>const LOCALE = 'en'const di18n = new DI18n({locale: LOCALE,isReplace: false,messages: {// 语言包}})</script>

这个时候你只需要将这个通用的翻译函数以适当的方式集成到你的开发框架当中去。

接下来会结合具体的不同场景去说明下相应的解决方案:

使用MVVM类的framework

使用了MVVM类的framework时,可以借助framework帮你完成view层的渲染工作, 那么你可以在代码当中轻松的通过代码去控制class的内容, 以及不同语言环境下的图片替换工作.

例如vue示例(1):


main.js文件:window.LOCALE = 'en'

app.vue文件:<template><p class="desc":class="locale"   // locale这个变量去控制class的内容:style="{backgroundImage: 'url(' + bgImg + ')'}"  // bgImg去控制背景图片的路径></p><img :src="imgSrc"> // imgSrc去控制图片路径</template><script>export default {name: 'page',data () {return {locale: LOCALE,imgSrc: require(`./${LOCALE}/img/demo.png`),bgImg: require(`./${LOCALE}/img/demo.png`)}}}</script>

这个时候你再加入翻译函数,就可以满足大部分的国际化的场景了,现在在main.js中添加对翻译函数di18n-translate的引用:

main.js文件:import Vue from 'vue'window.LOCALE = 'en'
const DI18n = require('di18n-translate')
const di18n = new DI18n({locale: LOCALE,       // 语言环境isReplace: false,   // 是否进行替换(适用于没有使用任何构建工具开发流程)messages: {         // 语言映射表en: {你好: 'Hello, {person}'},zh: {你好: '你好, {person}'}}})Vue.prototype.d18n = di18n

翻译函数的基本使用, 当然你还可以使用其他的方式集成到你的开发环境当中去:

app.vue文件:<template><p class="desc":class="locale"   // locale这个变量去控制class的内容:style="{backgroundImage: 'url(' + bgImg + ')'}"  // bgImg去控制背景图片的路径></p><img :src="imgSrc"> // imgSrc去控制图片路径<p>{{title}}</p></template><script>export default {name: 'page',data () {return {locale: LOCALE,imgSrc: require(`./${LOCALE}/img/demo.png`),bgImg: require(`./${LOCALE}/img/demo.png`),title: this.di18n.$t('你好')}}}</script>

使用mvvm framework进行国际化,上述方式应该是较为合适的,主要是借助了framework帮你完成view层的渲染工作, 然后再引入一个翻译函数去完成一些动态文案的翻译工作

这种国际化的方式算是运行时处理,不管是开发还是最终上线都只需要一份代码。

当然在使用mvvm framework的情况下也是可以不借助framework帮我们完成的view层的这部分的功能,而通过构建工具去完成, 这部分的套路可以参见下午的示例3

未使用mvvm框架,使用了构建工具(如webpack/gulp/browserify/fis)

使用了前端模板

国际化的方式和上面说的使用mvvm框架的方式一致,因为有模板引擎帮你完成了view层的渲染.所以对于样式图片class属性的处理可以和上述方式一致, 动态文案的翻译需引入翻译函数。

这种国际化的方式也算是运行时处理,开发和最终上线都只需要一份代码。

没有使用前端模板

因为没用使用前端模板,便少了对于view层的处理。这个时候你的DOM结构可能是在html文件中一开始就定义好的了,也可能是借助于webpack这样能允许你使用模块化进行开发,通过js动态插入DOM的方式。

接下来我们先说说没有借助webpack这样允许你进行模块化开发的构建工具,DOM结构直接是在html文件中写死的项目。这种情况下你失去了对view层渲染能力。那么这种情况下有2种方式去处理这种情况。

第一种方式就是可以在你自己的代码中添加运行时的代码。大致的思路就是在DOM层面添加属性,这些属性及你需要翻译的map表所对应的key值:

示例(2):

html文件:

  <div class="wrapper" i18n-class="${locale}"><img i18n-img="/images/${locale}/test.png"><input i18n-placeholder="你好"><p i18n-content="你好"></p></div>

运行时:

  <script src="[PATH]/di18-translate/index.js"></script><script>const LOCALE = 'en'const di18n = new DI18n({locale: LOCALE,isReplace: true,   // 开启运行时messages: {en: {你好: 'Hello'},zh: {你好: '你好'}}})</script>

最后html会转化为:

  <div class="wrapper en"><img src="/images/en/test.png"><input placeholder="Hello"><p>Hello</p></div>

第二种方式就是借助于构建工具在代码编译的环节就完成国际化的工作,以webpack为例:

示例(3):

html文件:

  <div class="wrapper ${locale}"><img src="/images/${locale}/test.png"><p>$t('你好')</p></div>

这个时候使用了一个webpackpreloader: locale-path-loader,它的作用就是在编译编译前,就通过webpack完成语言环境的配置工作,在你的业务代码中不会出现过多的关于语言环境变量以及很好的解决了运行时作为cssbackground的图片替换工作, 具体的locale-path-loader的文档请戳我

使用方法:

npm install locale-path-loader

webpack 1.x 配置:

  module.exports = {....preLoaders: [{test: /\.*$/,exclude: /node_modules/,loaders: ['eslint','locale-path?outputDir=./src/common&locale=en&inline=true']} ]....}

webpack 2 配置:

  module.exports = {....module: {rules: [{test: /\.*$/,enforce: 'pre',exclude: /node_modules/,use: [{loader: 'locale-path-loader',options: {locale: 'en',outputDir: './src/common',inline: true}}]}]}....}

经过webpackpreloader处理后,被插入到页面中的DOM最后成为:

  <div class="wrapper en"><img src="/images/en/test.png"><p>Hello</p></div>

但是使用这种方案需要在最后的打包环节做下处理,因为通过preloader的处理,页面已经被翻译成相对应的语言版本了,所以需要通过构建工具以及改变preloader的参数去输出不同的语言版本文件。当然构建工具不止webpack这一种,不过这种方式处理的思路是一致的。
这种方式属于编译时处理,开发时只需要维护一份代码,但是最后输出的时候会输出不同语言包的代码。当然这个方案还需要服务端的支持,根据不同语言环境请求,返回相对应的入口文件。关于这里使用webpack搭配locale-path-loader进行分包的内容可参见vue-demo:

|--deploy|   ||   |---en|   |    |--app.js|   |    |--vendor.js|   |    |--index.html|   |---zh|   |    |--app.js|   |    |--vendor.js|   |    |--index.html|   |---jp|   |    |--app.js|   |    |--vendor.js|   |    |--index.html|   |----lang.json

接下来继续说下借助构建工具进行模块化开发的项目, 这些项目可能最后页面上的DOM都是通过js去动态插入到页面当中的。那么,很显然,可以在DOM被插入到页面前即可以完成静态文案翻译样式图片替换class属性等替换的工作。

示例(4):
html文件:

  <div class="wrapper ${locale}"><img src="/images/${locale}/test.png"><p>$t('你好')</p></div>

js文件:

  let tpl = require('html!./index.html')let wrapper = document.querySelector('.box-wrapper')// di18n.$html方法即对你所加载的html字符串进行replace,最后相对应的语言版本wrapper.innerHTML = di18n.$html(tpl)

最后插入到的页面当中的DOM为:

  <div class="wrapper en"><img src="/images/en/test.png"><p>Hello</p></div>

这个时候动态翻译再借助引入的di18n上的$t方法

di18n.$t('你好')

这种开发方式也属于运行时处理,开发和上线后只需要维护一份代码。

没有使用任何framework构建工具的纯静态,偏展示性的网页

这类网页的国际化,可以用上面提到的通过在代码中注入运行时来完成基本的国际化的工作, 具体内容可以参见示例(2)以及仓库中的html-demo文件夹。

前端通用国际化解决方案 di18n-translate相关推荐

  1. 基于指令和混合的前端通用埋点方案

    https://zhuanlan.zhihu.com/p/27659302 摘要 本文介绍了一种通用的前端埋点方案的设计和实现,具有适配项目广泛,易于使用,与业务逻辑解耦等优点,已经在外卖商业平台进行 ...

  2. JEECG 前端JS国际化实现,采用i18n新技术方案

    JEECG 前端JS国际化实现,采用i18n新技术方案 JEECG平台已全面实现了国际化,从后台代码提示到页面表单,使用方法简单易用: 后台配置国际化语言,java或者jsp层通过 引用国际化标签,即 ...

  3. 【Web技术】1431- 总结前端主题切换的思考和现代前端样式的解决方案落地

    关于本文 来自:codercao https://juejin.cn/post/7106702604024938503 demo在线体验地址:https://hongqingcao.github.io ...

  4. 服务端渲染(SSR) 通用技术解决方案

    项目背景 服务端渲染(SSR) 通用技术解决方案的诞生来源于对 360搜索百科移动端项目的一次重构实践.而当时决定重构该项目的主要原因有以下几点: 1.  技术栈陈旧,熟悉.开发以及维护成本都较高 项 ...

  5. 使用 jQuery.i18n.properties 实现 Web 前端的国际化

    jQuery.i18n.properties 简介 在介绍 jQuery.i18n.properties 之前,我们先来看一下什么是国际化.国际化英文单词为:Internationalization, ...

  6. 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML

    基于webpack的前端工程化开发解决方案探索(一):动态生成HTML 参考文章: (1)基于webpack的前端工程化开发解决方案探索(一):动态生成HTML (2)https://www.cnbl ...

  7. 基于FastJson的通用泛型解决方案

    基于FastJson的通用泛型解决方案 参考文章: (1)基于FastJson的通用泛型解决方案 (2)https://www.cnblogs.com/scy251147/p/9451879.html ...

  8. 程序员过关斩将--应对高并发系统有没有通用的解决方案呢?

    " 灵魂拷问: 应对高并发系统有没有一些通用的解决方案呢? 这些方案解决了什么问题呢? 这些方案有那些优势和劣势呢? 对性能孜孜不倦的追求是互联网技术不断发展的根本驱动力,从最初的大型机到现 ...

  9. java mockserver搭建_mockjs,json-server一起搭建前端通用的数据模拟框架教程

    无论是在工作,还是在业余时间做前端开发的时候,难免出现后端团队还没完成接口的开发,而前端团队却需要实现对应的功能,不要问为什么,这是肯定存在的.本篇文章就是基于此原因而产出的.希望对有这方面的需求的同 ...

最新文章

  1. 万字总结,体系化带你全面认识 Nginx
  2. 计算机游戏的英语怎么写,电脑游戏英语怎么写
  3. 使用wireshark抓取wcf生成的soap消息
  4. python3爬虫初探(七)使用MySQL
  5. mfc指示灯报警显示_奔驰车辆常用指示灯功能讲解
  6. 挑战练习6.4 报告编译版本
  7. 手把手打造开源新监控利器check_mk
  8. 学大数据要学哪些算法_大数据学习之不得不知的八大算法
  9. 浅谈JavaScript函数重载
  10. 轻松几步完成cisco交换机配置全是干货!
  11. 合宙Air720U724U722U Lua 固件更新说明
  12. 上海市计算机三级 是什么水平,上海计算机等级考试跟全国计算机等级考试有什么区别吗?...
  13. IDEA 使用破解补丁激活
  14. 十二星座物语,女生最喜欢的星座性格【1】
  15. ensp路由器注册_使用ensp进行简单的路由器互连实验
  16. MacOS git配置本地ssh
  17. HomeKit、米家、智汀智能家居应该如何确保用户的安全和隐私
  18. 各省绿色金融指数(2001-2020年)
  19. 在线直播网站源码开发,视频的采集如何实现?
  20. win10(1903)JVM虚拟机启动无法进入桌面解决办法汇总

热门文章

  1. Python学习笔记——如何获得数值类型的最大值和最小值(表示范围)
  2. [Ubuntu16.04]Anaconda下运行jupyter notebook出现Running as root is not recommended.
  3. 东大OJ-1391-Big big Power
  4. git submodule add子模块的添加,--recurse-submodules递归克隆子项目
  5. 物联网设备获取北京时间、年月日、星期几,GMT格林威治标准时间转换为北京时间,授时api Fiddler抓包调试
  6. 【lucene】高级搜索篇
  7. Java多线程编程(3)--线程安全性
  8. 利用 PIL模块实现生成动态验证码
  9. 利用Python工具进行打包功能
  10. BUG缺陷管理工具--测试用例执行