|
|
@ -12,12 +12,13 @@ import common from './common.mjs'; |
|
|
|
|
|
|
|
|
|
|
|
class I18N { |
|
|
|
class I18N { |
|
|
|
|
|
|
|
|
|
|
|
//构造函数,设置默认配置
|
|
|
|
//构造函数,设置默认配置
|
|
|
|
constructor(defaultLang, templateDir, langDir, buildDir) { |
|
|
|
constructor(defaultLang, templateDir, langDir, buildDir, configFilename) { |
|
|
|
this.defaultLang = typeof(defaultLang) != 'undefined' && defaultLang ? defaultLang : 'en'; |
|
|
|
this.defaultLang = typeof(defaultLang) != 'undefined' && defaultLang ? defaultLang : 'en'; |
|
|
|
this.templateDir = typeof(templateDir) != 'undefined' && templateDir ? templateDir : './public/template/'; |
|
|
|
this.templateDir = typeof(templateDir) != 'undefined' && templateDir ? templateDir : './public/template/'; |
|
|
|
this.langDir = typeof(langDir) != 'undefined' && langDir ? langDir : './i18n/'; |
|
|
|
this.langDir = typeof(langDir) != 'undefined' && langDir ? langDir : './i18n/'; |
|
|
|
this.buildDir = typeof(buildDir) != 'undefined' && buildDir ? buildDir : './public/'; |
|
|
|
this.buildDir = typeof(buildDir) != 'undefined' && buildDir ? buildDir : './public/'; |
|
|
|
|
|
|
|
this.configFile = typeof(configFilename) != 'undefined' && configFilename ? configFilename : 'config.json'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//从模板文件中解析语言占位变量,并生成语言包文件
|
|
|
|
//从模板文件中解析语言占位变量,并生成语言包文件
|
|
|
@ -25,20 +26,20 @@ class I18N { |
|
|
|
const _self = this; |
|
|
|
const _self = this; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const files = await readdir(_self.templateDir); |
|
|
|
const files = await readdir(_self.templateDir); |
|
|
|
let parseLangRes = null; |
|
|
|
let parseLangRes = null; |
|
|
|
for (const file of files) { |
|
|
|
for (const file of files) { |
|
|
|
parseLangRes = await _self.parseLangFromTemplate(_self.templateDir + file); |
|
|
|
parseLangRes = await _self.parseLangFromTemplate(_self.templateDir + file); |
|
|
|
if (parseLangRes) { |
|
|
|
if (parseLangRes) { |
|
|
|
console.log('Template file [%s] parse lang config done', file); |
|
|
|
console.log('Template file [%s] parse lang config done', file); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('Read dir in function init failed', err); |
|
|
|
console.error('Read dir in function init failed', err); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//根据语言包文件以及模板文件,生成对应语言的html文件
|
|
|
|
//根据语言包文件以及模板文件,生成对应语言的html文件
|
|
|
@ -49,29 +50,30 @@ class I18N { |
|
|
|
console.log('Lang json files', langFiles); |
|
|
|
console.log('Lang json files', langFiles); |
|
|
|
|
|
|
|
|
|
|
|
if (langFiles && langFiles.length > 0) { |
|
|
|
if (langFiles && langFiles.length > 0) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
let langData = null; |
|
|
|
let langData = null; |
|
|
|
let newHtml = '', saved = false; |
|
|
|
let newHtml = '', saved = false; |
|
|
|
for (const langFile of langFiles) { |
|
|
|
for (const langFile of langFiles) { |
|
|
|
const langJson = await readFile(_self.langDir + langFile, { encoding: 'utf8' }); |
|
|
|
const langJson = await readFile(_self.langDir + langFile, { encoding: 'utf8' }); |
|
|
|
if (!langJson) { |
|
|
|
if (!langJson) { |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
langData = JSON.parse(langJson); |
|
|
|
langData = JSON.parse(langJson); |
|
|
|
|
|
|
|
|
|
|
|
const files = await readdir(_self.templateDir); |
|
|
|
const files = await readdir(_self.templateDir); |
|
|
|
for (const file of files) { |
|
|
|
for (const file of files) { |
|
|
|
newHtml = await _self.replaceLangToTemplate(_self.templateDir + file, langData); |
|
|
|
newHtml = await _self.replaceLangToTemplate(_self.templateDir + file, langData); |
|
|
|
if (newHtml) { |
|
|
|
if (newHtml) { |
|
|
|
saved = await _self.saveBuildHtml(langFile.replace('.json', ''), file, newHtml); |
|
|
|
newHtml = await _self.replaceConfigToTemplate(newHtml); //replace configs
|
|
|
|
console.log('Template file [%s] lang data replace with [%s] done, html build [%s].', file, langFile, (saved ? 'success' : 'failed')); |
|
|
|
saved = await _self.saveBuildHtml(langFile.replace('.json', ''), file, newHtml); |
|
|
|
} |
|
|
|
console.log('Template file [%s] lang data replace with [%s] done, html build [%s].', file, langFile, (saved ? 'success' : 'failed')); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} |
|
|
|
console.error('Read dir in function build failed', err); |
|
|
|
} catch (err) { |
|
|
|
return false; |
|
|
|
console.error('Read dir in function build failed', err); |
|
|
|
} |
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
@ -79,38 +81,38 @@ class I18N { |
|
|
|
|
|
|
|
|
|
|
|
//判断语言代码格式是否符合国际标准
|
|
|
|
//判断语言代码格式是否符合国际标准
|
|
|
|
isIosLangCode(lang) { |
|
|
|
isIosLangCode(lang) { |
|
|
|
return /^[a-z]{2}(\-[a-z]{2})?$/i.test(lang); |
|
|
|
return /^[a-z]{2}(\-[a-z]{2})?$/i.test(lang); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//判断是否是语言包文件
|
|
|
|
//判断是否是语言包文件
|
|
|
|
isIosLangFile(filename) { |
|
|
|
isIosLangFile(filename) { |
|
|
|
return /^[a-z]{2}(\-[a-z]{2})?\.json$/i.test(filename); |
|
|
|
return /^[a-z]{2}(\-[a-z]{2})?\.json$/i.test(filename); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//更新语言包文件内容,合并新的数据到已有内容中
|
|
|
|
//更新语言包文件内容,合并新的数据到已有内容中
|
|
|
|
async updateLangFile(langFile, langJson) { |
|
|
|
async updateLangFile(langFile, langJson) { |
|
|
|
const _self = this; |
|
|
|
const _self = this; |
|
|
|
let updated = false; |
|
|
|
let updated = false; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
let json = await readFile(langFile, { encoding: 'utf8'}); |
|
|
|
let json = await readFile(langFile, { encoding: 'utf8'}); |
|
|
|
if (json) { |
|
|
|
if (json) { |
|
|
|
let data = JSON.parse(json); |
|
|
|
let data = JSON.parse(json); |
|
|
|
for (const key in langJson) { |
|
|
|
for (const key in langJson) { |
|
|
|
data[key] = langJson[key]; |
|
|
|
data[key] = langJson[key]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
await writeFile(langFile, JSON.stringify(data, null, 4)); |
|
|
|
await writeFile(langFile, JSON.stringify(data, null, 4)); |
|
|
|
}else { |
|
|
|
}else { |
|
|
|
await writeFile(langFile, JSON.stringify(langJson, null, 4)); |
|
|
|
await writeFile(langFile, JSON.stringify(langJson, null, 4)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
updated = true; |
|
|
|
updated = true; |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('updateLangFile failed', err); |
|
|
|
console.error('updateLangFile failed', err); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return updated; |
|
|
|
return updated; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//解析单个模板文件,并生成语言包文件
|
|
|
|
//解析单个模板文件,并生成语言包文件
|
|
|
@ -118,66 +120,66 @@ class I18N { |
|
|
|
const _self = this; |
|
|
|
const _self = this; |
|
|
|
|
|
|
|
|
|
|
|
let langJson = {}, |
|
|
|
let langJson = {}, |
|
|
|
total = 0; |
|
|
|
total = 0; |
|
|
|
try { |
|
|
|
try { |
|
|
|
const html_template = await readFile(templateFilepath, { encoding: 'utf8' }); |
|
|
|
const html_template = await readFile(templateFilepath, { encoding: 'utf8' }); |
|
|
|
|
|
|
|
|
|
|
|
const regHtmlLang = /[\s\S]*<html lang="([^"]+)">[\s\S]*/i; |
|
|
|
const regHtmlLang = /[\s\S]*<html lang="([^"]+)">[\s\S]*/i; |
|
|
|
let htmlLang = html_template.replace(regHtmlLang, "$1"); |
|
|
|
let htmlLang = html_template.replace(regHtmlLang, "$1"); |
|
|
|
if (htmlLang == html_template || _self.isIosLangCode(htmlLang) == false) { |
|
|
|
if (htmlLang == html_template || _self.isIosLangCode(htmlLang) == false) { |
|
|
|
htmlLang = _self.defaultLang; |
|
|
|
htmlLang = _self.defaultLang; |
|
|
|
}else { |
|
|
|
}else { |
|
|
|
htmlLang = htmlLang.toLowerCase(); |
|
|
|
htmlLang = htmlLang.toLowerCase(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//模版语法不能包含:换行符、英文冒号、英文分号
|
|
|
|
//模版语法不能包含:换行符、英文冒号、英文分号
|
|
|
|
const regLang = /\{([^\}\r\n:;]+)\}/ig; |
|
|
|
const regLang = /\{([^\}\r\n:;]+)\}/ig; |
|
|
|
const matches = html_template.matchAll(regLang); |
|
|
|
const matches = html_template.matchAll(regLang); |
|
|
|
for (const match of matches) { |
|
|
|
for (const match of matches) { |
|
|
|
langJson[match[1]] = match[1]; |
|
|
|
langJson[match[1]] = match[1]; |
|
|
|
total ++; |
|
|
|
total ++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//更新语言包文件
|
|
|
|
//更新语言包文件
|
|
|
|
if (total > 0) { |
|
|
|
if (total > 0) { |
|
|
|
let langFile = _self.langDir + `${htmlLang}.json`; |
|
|
|
let langFile = _self.langDir + `${htmlLang}.json`; |
|
|
|
const saved = await _self.updateLangFile(langFile, langJson); |
|
|
|
const saved = await _self.updateLangFile(langFile, langJson); |
|
|
|
if (!saved) { |
|
|
|
if (!saved) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('parseLangFromTemplate failed', err); |
|
|
|
console.error('parseLangFromTemplate failed', err); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return langJson; |
|
|
|
return langJson; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//获取所有语言包json文件
|
|
|
|
//获取所有语言包json文件
|
|
|
|
async getLangFiles(lang) { |
|
|
|
async getLangFiles(lang) { |
|
|
|
const _self = this; |
|
|
|
const _self = this; |
|
|
|
|
|
|
|
|
|
|
|
let langFiles = []; |
|
|
|
let langFiles = []; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const files = await readdir(_self.langDir); |
|
|
|
const files = await readdir(_self.langDir); |
|
|
|
let parseLangRes = null; |
|
|
|
let parseLangRes = null; |
|
|
|
for (const file of files) { |
|
|
|
for (const file of files) { |
|
|
|
if (_self.isIosLangFile(file)) { |
|
|
|
if (_self.isIosLangFile(file)) { |
|
|
|
if (typeof(lang) == 'undefined') { |
|
|
|
if (typeof(lang) == 'undefined') { |
|
|
|
langFiles.push(file); |
|
|
|
langFiles.push(file); |
|
|
|
}else if (file.indexOf(lang) > -1) { |
|
|
|
}else if (file.indexOf(lang) > -1) { |
|
|
|
langFiles.push(file); |
|
|
|
langFiles.push(file); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('Read dir in function getLangFiles failed', err); |
|
|
|
console.error('Read dir in function getLangFiles failed', err); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return langFiles; |
|
|
|
return langFiles; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//根据语言包替换单个模板文件,返回新的html代码
|
|
|
|
//根据语言包替换单个模板文件,返回新的html代码
|
|
|
@ -186,39 +188,60 @@ class I18N { |
|
|
|
|
|
|
|
|
|
|
|
let html = ''; |
|
|
|
let html = ''; |
|
|
|
try { |
|
|
|
try { |
|
|
|
const html_template = await readFile(templateFilepath, { encoding: 'utf8' }); |
|
|
|
const html_template = await readFile(templateFilepath, { encoding: 'utf8' }); |
|
|
|
|
|
|
|
|
|
|
|
html = html_template; |
|
|
|
html = html_template; |
|
|
|
for (let key in langData) { |
|
|
|
for (let key in langData) { |
|
|
|
html = html.replaceAll(`{${key}}`, langData[key]); |
|
|
|
html = html.replaceAll(`{${key}}`, langData[key]); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('replaceLangToTemplate failed', err); |
|
|
|
console.error('replaceLangToTemplate failed', err); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return html; |
|
|
|
return html; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//根据配置文件,替换配置项
|
|
|
|
|
|
|
|
async replaceConfigToTemplate(html_template) { |
|
|
|
|
|
|
|
const _self = this; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let html = html_template; |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
let config = await common.getConfigFromJsonFile(_self.configFile); |
|
|
|
|
|
|
|
if (typeof(config['templateConfigs']) == 'undefined' || !config['templateConfigs']) {return html;} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let tempConfigs = config['templateConfigs']; |
|
|
|
|
|
|
|
for (let key in tempConfigs) { |
|
|
|
|
|
|
|
html = html.replaceAll(`{config:${key}}`, tempConfigs[key]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (err) { |
|
|
|
|
|
|
|
console.error('replaceConfigToTemplate failed', err); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return html; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//保存替换了语言包的html文件
|
|
|
|
//保存替换了语言包的html文件
|
|
|
|
async saveBuildHtml(lang, htmlFilename, htmlContent) { |
|
|
|
async saveBuildHtml(lang, htmlFilename, htmlContent) { |
|
|
|
const _self = this; |
|
|
|
const _self = this; |
|
|
|
let saved = false; |
|
|
|
let saved = false; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
let langDir = _self.buildDir + lang; |
|
|
|
let langDir = _self.buildDir + lang; |
|
|
|
if (!fs.existsSync(langDir)) { |
|
|
|
if (!fs.existsSync(langDir)) { |
|
|
|
fs.mkdirSync(langDir); |
|
|
|
fs.mkdirSync(langDir); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let htmlFile = `${langDir}/${htmlFilename}`; |
|
|
|
let htmlFile = `${langDir}/${htmlFilename}`; |
|
|
|
await writeFile(htmlFile, htmlContent); |
|
|
|
await writeFile(htmlFile, htmlContent); |
|
|
|
saved = true; |
|
|
|
saved = true; |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('saveBuildHtml failed', err); |
|
|
|
console.error('saveBuildHtml failed', err); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return saved; |
|
|
|
return saved; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|