国际化原因
为了更加方便切换版本,让代码应该一次完成,多国使用,除了使用英语外,还要可以进行单独语言包的一个添加,文章就是这样的一个例子.
公司接到一个国外的项目,需要法文版本的,但是公司通晓法文的基本没有,于是商量降低要求之后开始国际化采用英文展示就行,于是任务就开始了.
目录
[TOC]
需求分析
项目的代码全部国际化任务量不小,公司基本没有用什么框架,基本采用的是js,html实现数据的展示,没有采用框架,只是有一些简单的逻辑分层,加大了不少国际化的难度.
但是针对java部分的代码,虽说稍微熟悉一些,但是国际化就暂时不需要我负责了,虽说我也只是之前弄过纯java的一些简单的国际化.
我的话暂时只是需要复杂光伏预测系统的web展示界面.其他的国际化暂时不需要修改,毕竟最为直接的只是能够看到的部分.计算过程中的一些说明日志什么的,可以后面再继续更改.
准备工作
开始准手国际化之前,先查阅了一下相关的文章,在前端方面需要修改的部分方法有如下几种:
- vue + vue-i18n
- angular + angular-translate
- react + react-intl
- jquery.i18n.properties
由于考虑到公司原本的代码没有使用什么现在的最新框架,只是采用的最后一个,也就是jquery.i18n.properties这样子的一个解决方案了,目前方案就暂时这么定来下了,然后下载对应的jQuery文件和jQuery.i18n文件了.
由于文件更新比较快,这里就不单独提供下载连接了,自行搜索,测试版本的时候使用的是3.3的版本.对应的浏览器需要ie10及以上,谷歌基本没问题.
开始着手
引入jQuery相关js
首先是引入jQuery的文件.有两种方案了,一种是在每个页面一个个添加,这样子效率不高,但是稳定,还有一种是采用js直接内部引入外部的js的方法,两种方法的代码如下:
<!-- 页面添加连接 -->
<script type="text/javascript" language="JavaScript"
src="../jsCommon/jquery-3.3.1.js"></script>
<script type="text/javascript" language="JavaScript"
src="../jsCommon/jquery.i18n.properties.min.js"></script>
<!-- <script type="text/javascript" language="JavaScript"
src="../jsCommon/jquery.i18n.properties.js"></script> -->
这里其实是指需要两个,多的一个min的包,只是为了后面加速加载之用,开发测试的话,一般用不压缩的js包,因为看起来有些难度.
//js内部添加外部js
document.write('<script type="text/javascript" language="JavaScript" src="../jsCommon/jquery-3.3.1.js"></script>');
document.write('<!-- <script type="text/javascript" language="JavaScript" src="../jsCommon/jquery.i18n.properties.min.js"> </script> -->');
document.write('<script type="text/javascript" language="JavaScript" src="../jsCommon/jquery.i18n.properties.js"></script>');
这里需要注意一下的是,添加这个之后,才能使用这个引入的js,并且不能在这个js里面使用引入的一些方法,因为会找不到,你可以尝试一下.
采用第二种方法的话,需要找一个所有页面都存在的js文件才可以,可以根据自己的需要选择,没有这样的一个js的话,就差不多只能选择第一个方法,或者自己写这么一个js了.
初始化参数
引入之后js就需要初始化了,代码以及参数如下:
var currentLang = navigator.language; //判断除IE外其他浏览器使用语言
if(!currentLang){//判断IE浏览器使用语言
currentLang = navigator.browserLanguage;
}
console.info("Browser: "+ currentLang);
//currentLang = 'en-US';
//currentLang = 'zh_CN';
jQuery.i18n.properties({
name: 'nari', //必须和配置文件一致
path : '../jsCommon/', // 资源文件路径
mode : 'map', // 用 Map 的方式使用资源文件中的值
// language : 'en_US',
language : currentLang,
// cache:false,
// encoding: 'UTF-8',
callback: function(){
console.info( $.i18n.prop('info'));
console.info(currentLang);
}
这里的话分为两个部分了,前面部分只是为了自动识别浏览器的语言,做的一个判断,可以不要,然后下面的参数的话,没有注释的部分,一般就是都需要填写的了,除了那个language除外,那个也是可以省略.
国际化页面
上面的那个初始化的代码可以放在页面加载完成之前,也可以放在页面加载完成之后.加载的方法有两种,一种是统一加载,一种是按需加载.
统一加载
放在页面加载完成之后的话,这个部分的代码就需要放在js的最后面了,然后在callback方法里面,通过id号或者其他的标志进行国际化操作了. 网络上面多是这么一个案例,找到应该不难,这里就copy一份例子,没有经过实际代码验证:
jQuery.i18n.properties({
name: 'common',
path: '/Content/i18n/' + i18nLanguage + '/', //资源文件路径
mode: 'map', //用Map的方式使用资源文件中的值
language: i18nLanguage,
callback: function () {//加载成功后设置显示内容
console.log("i18n赋值中...");
try {
//初始化页面元素
$('[data-i18n-placeholder]').each(function () {
$(this).attr('placeholder', $.i18n.prop($(this).data('i18n-placeholder')));
});
$('[data-i18n-text]').each(function () {
//如果text里面还有html需要过滤掉
var html = $(this).html();
var reg = /<(.*)>/;
if (reg.test(html)) {
var htmlValue = reg.exec(html)[0];
$(this).html(htmlValue + $.i18n.prop($(this).data('i18n-text')));
}
else {
$(this).text($.i18n.prop($(this).data('i18n-text')));
}
});
$('[data-i18n-value]').each(function () {
$(this).val($.i18n.prop($(this).data('i18n-value')));
});
}
catch(ex){ }
console.log("i18n写入完毕");
}
});
这里需要注意的是,这里需要每一个需要更改的位置都添加类似如下的标记:
<input class="typeahead" type="text" id="menu_search" data-i18n-placeholder = "searchPlaceholder"/>
<span data-i18n-text="setting"></span>
这里的话如果是开始开发或者只是短小一些的英文的话,还是可以考虑的,但是如果是完整的项目的话,任务栏就不小了.自行选择处理.下面介绍第二种方法.
按需加载
前面的那段初始化代码如果放在页面加载之前的话,就是放在引入jQuery之后,直接初始化,然后在其他位置,需要替换成国际化的位置,按照如下的格式替换就行.
//old
alert("hello");
//new
alert($.i18n.prop('hello'));
配置文件
能够知道国际化,上面的那个替换位置的用处自然不用多说,还是提一句吧 ,这样子看似复杂了,但是后面需要修改的话, 不需要改动这里的js,而是只需要更改对应的nari.properties(具体名字根据自己的配置来)就行了,还可以根据需要自动判断语言包.
对应的nari.properties文件中文文件包的名字是nari_zh.properties和nari_zh-CN.properties, 也可以多一个繁体字的nari_zh-TW.properties,英文的对应的是nari_en.properties, nari_en-US.properties,nari_en-UK.properties,其他国家和语言的就自己查询了,然后里面的内容大致如下格式:
#nari_zh.properties
#Chinese character need transfer to unicode
hello = \u4F60\u597D
#nari_en.properties
hello = hello
其他国际化内容
上面基本上可以完成大部分页面的处理了,但是还是有一些其他的需要注意一下.
标题的国际化
这里由于标题在最先加载,不管上面的那种方法,都是在这个标题之后,只能在加载之后,通过js的方法进行标题更改了.这里简单罗列一下几个有趣的方法,作用看名字就懂了.
document.getElementsByTagName("title")[0].innerText = '需要设置的值';
window.onfocus = function () {
document.title = '恢复正常了...';
};
window.onblur = function () {
document.title = '快回来~页面崩溃了';
};
$('title').html('')
需要修改的位置,只是需要把对应的字符串替换成$.i18n.prop('Title')
就行了.
这里还有个问题是,如果你的浏览器加载比较慢的话,在浏览英文时候,可能会看到标题先是英文,之后才变成中文,这是应为标题加载是在js运行之前的缘故了,这个暂时没有找到好的解决办法, 一般浏览器的加载速度基本都发现不了这个现象,如果非要解决的话,就把原先的html里面的标题设置成空格把,这样就看不到中文了.
一些未启用的界面的国际化
这个部分,emmmmm,既然没启用,也没有直接的连接跳转过去,我也不启用吧.这次里面有两个不知道干嘛的页面FSDayGrid和OverhaulCapacity.
针对一些插件的处理
针对一些插件的处理的话,如果插件本身没有国际化的功能,就直接按照上面的那个按需国际化加载就行了,比方说根据自身需要引入的一些数据表格之类的插件.还有一些固定的菜单之类的等等, 当你需要同样的差不多的插件,但是有不完全相同时候,需要全部国际化是多么令人头疼的一件事情.
你能猜猜下面三个文件的作用吗?
//差不多的功能,就不能放一个文件里面处理吗?
// /PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/MyEWISUtil.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/Mcommand/MyEWISUtil.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/Mcommand/MyEWISUtil1.js//runalarm
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command_cx/MyEWISUtil.js//StationRTList
//page部分的双引用已经更改为一个引用,两个部分完全一致,但是不能自动国际化,则选择性修改后者.
<!--<script type="text/javascript" src="../jsCommon/ext/build/locale/ext-lang-zh_CN.js"></script> -->
//index StationRTLists PlanCapacity
<script type="text/javascript" src="../jsCommon/ext/source/locale/ext-lang-zh_CN.js"></script>
//RunAlarm SolRadFigureF PowerFigureF ContrastPowerFigureF ContrastUShortRealtimeTheory ContrastLists
<script type="text/javascript" src="../Datetime/ext-lang-zh_CN.js"></script>
/PVForecast(s)1.24/WebRoot/PVForecast/Datetime/ext-lang-zh_CN.js
//针对一些表格位置的中文,发现是createGrid这个函数加载的,主要是针对xml文档的一个解析分析了.
//这个如果是返回或得到的数据的话,就不是很好处理了,需要在发送请求时候特殊处理.
// 数据显示的空间,针对角标的显示,有四处不同的引用,修改需要针对不同的部分进行修改.文件名至少都是GeneralGrid.js类似的名字.
//时间选择控件针对google的兼容性不是很好,需要进行处理,
//同时,日期对应的格式需要转化为国际化的形式,注意文件的位置,和时间控件有三个
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/EditorGrid.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/GeneralGrid.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/GeneralGrid1.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/GeneralGrid2.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/GeneralGrid3.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command/GeneralGrid4.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command_cx/GeneralGrid.js
/PVForecast(s)1.24/WebRoot/PVForecast/jsCommon/command_cx/GeneralGrid1.js
如果一些插件本身是带有国际化的,一般通过一些方法引入就行了.下面的一个例子可以参考一下:
var userLanguage = getlanguageCookie("userLanguage");
//如果cookie里面没有,则使用默认值
if (!userLanguage) {
userLanguage = 'zh-CN';
}
if (userLanguage == 'zh-CN') {
var script = $('<script><\/script>');
script.attr('src', '/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js');
$('body').append(script);
}
else if (userLanguage == 'en') {
var script = $('<script><\/script>');
script.attr('src', '/Content/bootstrap-table/locale/bootstrap-table-en-US.js');
$('body').append(script);
}
这个是自动判断中英文然后加载了,代码没有验证成功,getlanguageCookie函数加载失败了,可能要自己写.不过有一个自动化一点的可以参考,代码已经经过验证:
console.info(currentLang);
currentLang=currentLang.replace('-','_');
var scr= '<script type="text/javascript" src="../jsCommon/ext/source/locale/ext-lang-'+currentLang+'.js"></script>';
document.write(scr);
代码是可以正常运行了,但是需要注意一下文件的名字和路径的问题.
针对返回数据的处理
有些位置通过定时查询数据库是否有异常数据之后直接alert获取到的数据,你就没办法了,如果是java代码还好,还可以采用java代码的国际化,这里后面有机会再提了.方法和这个类似.
如果你硬是要通过解析判断获取到数据的中文,然后写一个替换的过程的话,我在精神上面支持你,也欢迎你把写好的代码评论一番.
针对xml文件的配置国际化
针对一些通过xml动态配置页面的部分,解决方案就太多了,一个是在所有的节点旁边配置一个英文节点,但是后面维护,估计不好添加其他的语言.
那就再重写一套英文的?暂时就这么处理吧,复制一份为英文,中文的还是保留,可以在需要中文时候加载中文的配置文件,这里暂时就先自动化处理了,等后面有时间的话再添加.
复制之后,基本把所有的中文,改成英文,就完成了.
配置文件的双语处理
在处理国际化时候,为了方便对比中英文,我是专门坐了一个中英文的文件的,但是分开时候就很伤脑经了,因为有六百多行的中文英文都是如下的一个格式:
index = index 主页
help = help 帮助
想要分开,有些难度,索性好在有正则表达式这个东西,大部分的数据是可以处理了,少部分带标点的就自行修改了.
寿险,复制一份之后(你不想失误操作之后就全没了吧?),通过浏览器转换成unicode的形式,然后代码成这个样子了:
index = index \u4E3B\u9875
help = help \u5E2E\u52A9
然后在能够使用正则表达式的位置查找替换\=[\s\w+]+\\
这个就行了,这个是将=
之后和\
之前的英文选中.全部处理完差不多就是中文的了,可能要注意一些带有符号的中英文了.
再复制一份之后通过这个\\[\w\S+]+$\r
,选中所有的中文,替换为空格就解决了,这个就是英文的配置文档了.
网上了解到有直接通过unicode查找汉字的,但是通过notepad+没有测试成功,就没有实际使用了,而且还要主意一下中文之间的符号问题,虽说是少数了.有更好的解决方案欢迎点评指正.
驼峰式样转换为正常式样
本来,现在的配置样式是这个样子的
hello = NiceToMeetYou
good = ItIsGood
nice = ItVeryNice
这个样子的,应为是菜单选项,所以就直接连在一块了,但是现在要分开的话,全部行数太多,只能寻求高效方法,最后通过正则替换实现了,当然,有大佬造了更为方便的轮子的话,欢迎分享.
我的话,使用的查找目标是\=([\s\w]+[a-z])([A-Z])
,这里的括号就是两个变量了,替换时候直接使用=\1 \2
就行了.
不过需要注意的是,这里只是每一行的匹配,每一行最多有多少个连在一块的单词,就需要执行多少次,下面的例子的话,需要3次全部替换才能完成了.
最后的结果如下:
hello = Nice To Meet You
good = It Is Good
nice = It Very Nice
有其他有用的正则也欢迎点评.
其他异常
PowerFigureF.html等页面的数据类型字符存在重叠,分析之后发现是因为长度设置的问题,直接延长宽度可以解决.改正后代码如下:
//创建数据类型面板
function createDataTypePanel() {
for (var i = 0; i < 3; i++) {
var obj = new Ext.form.Checkbox({
name: 'type',
boxLabel: LineType[i],
//width: 90,//old
width: 120,
height: 20
});
obj.checked = true;
contrastLines.push(obj);
}
MyEWISUtilObj.CreateTypePanel($.i18n.prop('all.DataType'), "west", onRadioRegionButton, contrastLines);
}
SolRadFigureF.html等页面的图表类型数据显示本应该一行,实际分为两行,分析之后是上面同样问题,解决代码如下:
var QueryTypePanel = new Ext.Panel({
title: $.i18n.prop('all.ChartConfig'),
labelWidth: 200, // label settings here cascade unless
id: 'QueryTypePanel',
frame: true,
bodyStyle: 'padding:5px 5px 0',
width: 200,
defaults: {
//width: 70
width: 90
},
buttonAlign: 'center',
layout: 'table',
layoutConfig: {
columns: 2
},
viewConfig: {
forceFit: true
},
items: [LinePlotRadio, BarPlotRadio, LabelCheck],
renderTo: 'west'
});
PowerFigureF.html等页面的数据图标显示只是部分数据选项,没有全部选择,分析之后发现被人添加了一个异常的高度,最后注释即可.
var QueryTypePanel = new Ext.Panel({
title: $.i18n.prop('all.ChartConfig'),
labelWidth: 60, // label settings here cascade unless
// overridden
id: 'QueryTypePanel',
frame: true,
bodyStyle: 'padding:5px 5px 0',
width: 200,
defaults: {
width: 100
},
buttonAlign: 'center',
layout: 'table',
layoutConfig: {
columns: 2
},
viewConfig: {
forceFit: true
},
items: [LinePlotRadio, BarPlotRadio, LabelCheck],
renderTo: 'west',
//height: 20//old
});
说明
参考博客
前端系列_jquery.i18n.properties前端国际化解决方案“填坑日记”
CommanderXL/di18n_translate: di18n_translate
JavaScript进阶(二)在一个JS文件中引用另一个JS文件
版本记录
20180606 起草任务
20180627 完成翻译
20180628 完成文章
20190528 更新文章格式