nodejs和Ext双端国际化

一、当前问题汇总

  • 前后端国际化在同一个文件中,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中

自定义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);
        });
    });
}
根据语言种类生成对应的合并json文件

6.风险评估

原来的zh_CN.json的内容移植到了base.json文件中,各模块可以暂时不处理base.json中的内容,日后项目的新增国际化在模块国际化文件中编写,有时间可以将base.json中的属于本模块的国际化内容修改并移植到本模块国际化文件中

image.png

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

推荐阅读更多精彩内容