一、当前问题汇总
- 前后端国际化在同一个文件中,web的和nodejs的没有分离,造成页面需要加载更多的字典,影响首次加载性能
- 国际化文件存在大量的key冲突,重复定义
- 国际化没有分模块管理,各个模块维护自己的国际化字段太困难了,非常容易冲突或者覆盖别人的代码
- 前端源码的国际化文件暴露,点击查看源码
二、解决方法
1.前后端国际化分离
在locales目录下创建web、server目录,分别存放前后端(nodejs)的国际化文件
nodejs当前采用的是npm 的i18n库,该函数会根据lang自动加载directory配置项下的‘lang’.json;因此需重新设置directory
T.configure({
locales: ['en_US', 'zh_CN'],
defaultLocale: APP.config.app.lang,
directory: __dirname + '/locales/server', //此处将原来的locales目录修改为 '/locales/server'
updateFiles: false
});
前端Ext采用的是自定义函数_(),该函数需要读取dict进行key的匹配进行国际化;需修改读取前端国际化json的目录为/locales/web
2.分模块管理
根据现有的模块,自行创建模块目录,将自己的前后端国际化内容写在自定义json中
注:名称可以随便写,文件格式为json格式,且大模块可以在细分小模块创建对应目录
3.key冲突
国际化key需按照模块名称作为顶级作用于,采用包命名的方式,采用驼峰命名
{
"sysmng.serverList.grid.title":"服务器列表"
}
4.首页国际化内容直接暴露
创建i18n文件,自定义Ajax方法获取后端的dict字典,将字典缓存在内存中
var request = new XMLHttpRequest();
var url = '/lang/get_i18n_dict?lang=' + APP.lang
request.open("get", url);
request.send(null);
request.onload = function () {
if (request.status == 200) {
var json = JSON.parse(request.responseText);
APP.dict = json.dict;
console.log("dict", json.dict);
}
}
// i18n func
var _ = function (s) {
if (APP.dict[s] != undefined) {
return APP.dict[s];
}
return s;
};
// console.log(_('My title'));
// console.log(_('Login Successfully!'));
// console.log(_('Alarms'));
后端返回
router.get('/get_i18n_dict', async function(req, res, next) {
// req.setLocale(req.query.lang);
let dict = require(path.join(path.resolve(), '/locales/web', req.app.locals.lang + ".json"));
// let dict=await readJsonFile(localsPath);
console.log("dict", dict)
res.json(200, {
success: true,
lang: req.app.locals.lang,
// dict: req.getCatalog()
dict:dict
});
});
5.国际化文件的管理
在modules目录下创建i18n模块,用来管理前后端的国际化文件
webapp在首次运行时会加载该模块,并负责合并各个模块的语言json,创建生成制定名称的json文件。
之后nodejs的i18n模块会负责管理后端国际化
前端页面在首次加载时会调用接口请求前端国际化文件数据
var fs = require('fs');
var path = require('path');
const localsArr = ['en_US', 'zh_CN'];
exports.createLocalesFile = function () {
for(let lang of localsArr){
let srcPath = path.resolve();
let localesDir = path.join(srcPath, 'locales');
let langWebPath = path.join(localesDir, 'web',lang);
let langServerPath = path.join(localesDir, 'server', lang);
createJsonFile(lang, langWebPath);
createJsonFile(lang, langServerPath);
}
}
function getJsonFilePath(localsPath) {
let components = [];
const files = fs.readdirSync(localsPath);
files.forEach(function (item, index) {
let itemPath = path.join(localsPath, item);
let stat = fs.lstatSync(itemPath);
if (stat.isDirectory()) {
let subItemJsonFile = getJsonFilePath(itemPath);
components.push.apply(components, subItemJsonFile);
} else {
if (item.substr(item.lastIndexOf(".") + 1) === "json") {
components.push(itemPath);
}
}
})
return components;
}
async function createJsonFile(lang, langDir) {
let jsonName=lang+".json";
let jsonPath = path.join(langDir,"../", jsonName);
let json = await readJsonFile(langDir);
fs.writeFile(jsonPath, JSON.stringify(json), (err) => {
if (err) throw err;
console.log('文件已被保存');
});
}
async function readJsonFile(localsPath) {
let jsonPathArr = getJsonFilePath(localsPath);
console.log("allARR", jsonPathArr);
let allJson = {};
for (let item of jsonPathArr) {
let json = await readOneFile(item);
Object.assign(allJson, json);
}
return allJson;
}
async function readOneFile(item) {
return new Promise(function (resolve, reject) {
fs.readFile(item, "utf8", function (err, data) {
// console.log("json", data);
let json = JSON.parse(data);
// console.log("data", json);
resolve(json);
});
});
}
6.风险评估
原来的zh_CN.json的内容移植到了base.json文件中,各模块可以暂时不处理
base.json中的内容,日后项目的新增国际化在模块国际化文件中编写,有时间可以将base.json中的属于本模块的国际化内容修改并移植到本模块国际化文件中
- 该方案采用渐进式修改,兼容性强。
- 可维护性强
- 修改较少,各模块的工作量小