From 790d5be6e935dd1dc95ef2904a4eddad348e5da8 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 27 Jan 2020 17:52:20 -0800 Subject: [PATCH] Added --translate to MeshCentral server and allow translated custom web pages to be served. --- meshcentral.js | 47 +++++++++++++++++++++++++++++++++++------- package.json | 2 +- translate/translate.js | 33 ++++++++++++++++++++++------- webserver.js | 22 +++++++++++++++++++- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/meshcentral.js b/meshcentral.js index 6cba1d04..b3ea2f95 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -154,19 +154,52 @@ function CreateMeshCentralServer(config, args) { // Perform web site translations into different languages if (obj.args.translate) { - // Check if translate.json is in the "meshcentral-data" folder, if so use that and translate default pages. - // TODO + // Check NodeJS version + const NodeJSVer = Number(process.version.match(/^v(\d+\.\d+)/)[1]); + if (NodeJSVer < 8) { console.log("Translation feature requires Node v8 or above, current version is " + process.version + "."); process.exit(); return; } + // Check if translate.json is in the "meshcentral-data" folder, if so use that and translate default pages. + var translationFile = null, customTranslation = false; + if (require('fs').existsSync(obj.path.join(obj.datapath, 'translate.json'))) { translationFile = obj.path.join(obj.datapath, 'translate.json'); console.log("Using translate.json in meshentral-data."); customTranslation = true; } + if (translationFile == null) { if (require('fs').existsSync(obj.path.join(__dirname, 'translate', 'translate.json'))) { translationFile = obj.path.join(__dirname, 'translate', 'translate.json'); console.log("Using default translate.json."); } } + if (translationFile == null) { console.log("Unable to find translate.json."); process.exit(); return; } + + // Perform translation operations + var didSomething = false; process.chdir('./translate'); var translateEngine = require('./translate/translate.js') - translateEngine.startEx(['', '', 'minifyall']); - translateEngine.startEx(['', '', 'translateall']); - translateEngine.startEx(['', '', 'extractall']); - process.exit(); + if (customTranslation == true) { + // Translate all of the default files using custom translation file + translateEngine.startEx(['', '', 'minifyall']); + translateEngine.startEx(['', '', 'translateall', translationFile]); + translateEngine.startEx(['', '', 'extractall', translationFile]); + didSomething = true; + } // Check is "meshcentral-web" exists, if so, translate all pages in that folder. - // TODO + if (obj.webViewsOverridePath != null) { + didSomething = true; + var files = obj.fs.readdirSync(obj.webViewsOverridePath); + for (var i in files) { + var file = obj.path.join(obj.webViewsOverridePath, files[i]); + if (file.endsWith('.handlebars') && !file.endsWith('-min.handlebars')) { + translateEngine.startEx(['', '', 'translate', '*', translationFile, file, '--subdir:translations']); + } + } + } + if (obj.webPublicOverridePath != null) { + didSomething = true; + var files = obj.fs.readdirSync(obj.webPublicOverridePath); + for (var i in files) { + var file = obj.path.join(obj.webPublicOverridePath, files[i]); + if (file.endsWith('.htm') && !file.endsWith('-min.htm')) { + translateEngine.startEx(['', '', 'translate', '*', translationFile, file, '--subdir:translations']); + } + } + } + if (didSomething == false) { console.log("Nothing to do."); } + process.exit(); return; } diff --git a/package.json b/package.json index 7e5eb30c..6b93bddd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.8-a", + "version": "0.4.8-b", "keywords": [ "Remote Management", "Intel AMT", diff --git a/translate/translate.js b/translate/translate.js index f5eab39e..e2f68143 100644 --- a/translate/translate.js +++ b/translate/translate.js @@ -112,7 +112,7 @@ function startEx(argv) { log(' EXTRACT [languagefile] [files]'); log(' Extract strings from web pages and generate a language (.json) file.'); log(''); - log(' EXTRACTALL'); + log(' EXTRACTALL (languagefile)'); log(' Extract all MeshCentral strings from web pages and generate the languages.json file.'); log(''); log(' TRANSLATE [language] [languagefile] [files]'); @@ -196,7 +196,15 @@ function startEx(argv) { } // Extract or translate all MeshCentral strings - if (command == 'extractall') { extract("translate.json", meshCentralSourceFiles); } + if (command == 'extractall') { + if (argv.length > 4) { lang = argv[4].toLowerCase(); } + var translationFile = 'translate.json'; + if (argv.length > 3) { + if (fs.existsSync(argv[3]) == false) { log('Unable to find: ' + argv[3]); return; } else { translationFile = argv[3]; } + } + extract(translationFile, meshCentralSourceFiles, translationFile); + } + if (command == 'translateall') { if (fs.existsSync('../views/translations') == false) { fs.mkdirSync('../views/translations'); } if (fs.existsSync('../public/translations') == false) { fs.mkdirSync('../public/translations'); } @@ -226,11 +234,16 @@ function startEx(argv) { var langFile = argv[4]; if (fs.existsSync(langFile) == false) { log("Missing language file: " + langFile); process.exit(); return; } - var sources = []; - for (var i = 5; i < argv.length; i++) { if (fs.existsSync(argv[i]) == false) { log("Missing file: " + argv[i]); process.exit(); return; } sources.push(argv[i]); } + var sources = [], subdir = null; + for (var i = 5; i < argv.length; i++) { + if (argv[i].startsWith('--subdir:')) { + subdir = argv[i].substring(9); + } else { + if (fs.existsSync(argv[i]) == false) { log("Missing file: " + argv[i]); process.exit(); return; } sources.push(argv[i]); + } + } if (sources.length == 0) { log("No source files specified."); process.exit(); return; } - - translate(lang, langFile, sources, false); + translate(lang, langFile, sources, subdir); } if (command == 'minifyall') { @@ -414,7 +427,7 @@ function translateSingleThreaded(lang, langFile, sources, createSubDir) { try { langFileData = JSON.parse(fs.readFileSync(langFile)); } catch (ex) { } if ((langFileData == null) || (langFileData.strings == null)) { log("Invalid language file."); process.exit(); return; } - if (lang != null) { + if ((lang != null) && (lang != '*')) { // Translate a single language translateEx(lang, langFileData, sources, createSubDir); } else { @@ -553,7 +566,11 @@ function translateFromHtml(lang, file, createSubDir) { var outname = file; var outnamemin = null; - if (createSubDir != null) { outname = path.join(path.dirname(file), createSubDir, path.basename(file)); } + if (createSubDir != null) { + var outfolder = path.join(path.dirname(file), createSubDir); + if (fs.existsSync(outfolder) == false) { fs.mkdirSync(outfolder); } + outname = path.join(path.dirname(file), createSubDir, path.basename(file)); + } if (outname.endsWith('.handlebars')) { outnamemin = (outname.substring(0, outname.length - 11) + '-min_' + lang + '.handlebars'); outname = (outname.substring(0, outname.length - 11) + '_' + lang + '.handlebars'); diff --git a/webserver.js b/webserver.js index be0941b9..d55c2ed9 100644 --- a/webserver.js +++ b/webserver.js @@ -4332,7 +4332,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Render a page using the proper language function render(req, res, filename, args) { - if ((obj.parent.webViewsOverridePath == null) && (obj.renderPages != null)) { + if (obj.renderPages != null) { // If a user set a localization, use that if ((req.query.lang == null) && (req.session != null) && (req.session.userid)) { var user = obj.users[req.session.userid]; @@ -4375,6 +4375,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Get the list of pages with different languages that can be rendered function getRenderList() { + // Fetch default rendeing pages var translateFolder = null; if (obj.fs.existsSync('views/translations')) { translateFolder = 'views/translations'; } if (obj.fs.existsSync(obj.path.join(__dirname, 'views', 'translations'))) { translateFolder = obj.path.join(__dirname, 'views', 'translations'); } @@ -4395,6 +4396,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } } } + + // See if there are any custom rending pages that will override the default ones + if (obj.parent.webViewsOverridePath != null) { + translateFolder = null; + if (obj.fs.existsSync(obj.path.join(obj.parent.webViewsOverridePath, 'translations'))) { translateFolder = obj.path.join(obj.parent.webViewsOverridePath, 'translations'); } + var files = obj.fs.readdirSync(translateFolder); + for (var i in files) { + var name = files[i]; + if (name.endsWith('.handlebars')) { + name = name.substring(0, name.length - 11); + var xname = name.split('_'); + if (xname.length == 2) { + if (obj.renderPages[xname[0]] == null) { obj.renderPages[xname[0]] = {}; } + obj.renderPages[xname[0]][xname[1]] = obj.path.join(translateFolder, name); + if (obj.renderLanguages.indexOf(xname[1]) == -1) { obj.renderLanguages.push(xname[1]); } + } + } + } + } } }