2019-10-21 14:36:09 -04:00
/ * *
* @ description MeshCentral MeshAgent
* @ author Ylian Saint - Hilaire
2020-01-02 21:30:12 -05:00
* @ copyright Intel Corporation 2019 - 2020
2019-10-21 14:36:09 -04:00
* @ license Apache - 2.0
* @ version v0 . 0.1
* /
var fs = require ( 'fs' ) ;
var path = require ( 'path' ) ;
var performCheck = false ;
var translationTable = null ;
var sourceStrings = null ;
2019-10-21 17:57:27 -04:00
var jsdom = null ; //require('jsdom');
var esprima = null ; //require('esprima'); // https://www.npmjs.com/package/esprima
2019-10-24 13:45:58 -04:00
var minifyLib = 2 ; // 0 = None, 1 = minify-js, 2 = HTMLMinifier
var minify = null ;
2019-10-21 14:36:09 -04:00
var meshCentralSourceFiles = [
"../views/agentinvite.handlebars" ,
"../views/default.handlebars" ,
"../views/default-mobile.handlebars" ,
"../views/download.handlebars" ,
"../views/error404.handlebars" ,
2019-10-24 13:45:58 -04:00
"../views/error404-mobile.handlebars" ,
2019-10-21 14:36:09 -04:00
"../views/login.handlebars" ,
"../views/login-mobile.handlebars" ,
2019-12-01 18:21:33 -05:00
"../views/terms.handlebars" ,
"../views/terms-mobile.handlebars" ,
2019-10-21 14:36:09 -04:00
"../views/message.handlebars" ,
"../views/messenger.handlebars" ,
"../public/player.htm"
] ;
2019-12-04 15:01:03 -05:00
// Check NodeJS version
if ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) < 8 ) { console . log ( "Translate.js requires Node v8 or above, current version is " + process . version + "." ) ; return ; }
2019-10-21 14:36:09 -04:00
// node translate.json CHECK ../meshcentral/views/default.handlebars
// node translate.json EXTRACT bob.json ../meshcentral/views/default.handlebars
// node translate.js TRANSLATE fr test2.json ../meshcentral/views/default.handlebars
2019-10-24 13:45:58 -04:00
var libs = [ 'jsdom' , 'esprima' , 'minify-js' ] ;
if ( minifyLib == 1 ) { libs . push ( 'minify-js' ) ; }
if ( minifyLib == 2 ) { libs . push ( 'html-minifier' ) ; }
InstallModules ( libs , start ) ;
2019-10-21 17:57:27 -04:00
function start ( ) {
2019-12-04 15:01:03 -05:00
// Load dependencies
2019-10-21 17:57:27 -04:00
jsdom = require ( 'jsdom' ) ;
esprima = require ( 'esprima' ) ; // https://www.npmjs.com/package/esprima
2019-10-24 13:45:58 -04:00
if ( minifyLib == 1 ) { minify = require ( 'minify-js' ) ; }
2019-10-24 16:13:18 -04:00
if ( minifyLib == 2 ) { minify = require ( 'html-minifier' ) . minify ; } // https://www.npmjs.com/package/html-minifier
2019-10-21 17:57:27 -04:00
var command = null ;
if ( process . argv . length > 2 ) { command = process . argv [ 2 ] . toLowerCase ( ) ; }
2019-12-09 19:34:37 -05:00
if ( [ 'check' , 'extract' , 'extractall' , 'translate' , 'translateall' , 'minifyall' , 'merge' , 'totext' , 'fromtext' ] . indexOf ( command ) == - 1 ) { command = null ; }
2019-10-21 17:57:27 -04:00
console . log ( 'MeshCentral web site translator' ) ;
if ( command == null ) {
console . log ( 'Usage "node translate.js [command] [options]' ) ;
console . log ( 'Possible commands:' ) ;
console . log ( '' ) ;
console . log ( ' CHECK [files]' ) ;
console . log ( ' Check will pull string out of a web page and display a report.' ) ;
console . log ( '' ) ;
console . log ( ' EXTRACT [languagefile] [files]' ) ;
console . log ( ' Extract strings from web pages and generate a language (.json) file.' ) ;
console . log ( '' ) ;
console . log ( ' EXTRACTALL' ) ;
console . log ( ' Extract all MeshCentral strings from web pages and generate the languages.json file.' ) ;
console . log ( '' ) ;
console . log ( ' TRANSLATE [language] [languagefile] [files]' ) ;
console . log ( ' Use a language (.json) file to translate web pages to a give language.' ) ;
console . log ( '' ) ;
2019-12-26 14:37:49 -05:00
console . log ( ' TRANSLATEALL (languagefile) (language code)' ) ;
2019-10-21 17:57:27 -04:00
console . log ( ' Translate all MeshCentral strings using the languages.json file.' ) ;
2019-10-24 13:45:58 -04:00
console . log ( '' ) ;
console . log ( ' MINIFYALL' ) ;
console . log ( ' Minify the main MeshCentral english web pages.' ) ;
2019-12-09 16:43:02 -05:00
console . log ( '' ) ;
2019-12-17 17:06:54 -05:00
console . log ( ' MERGE [sourcefile] [targetfile] [language code]' ) ;
2019-12-09 16:43:02 -05:00
console . log ( ' Merge a language from a translation file into another translation file.' ) ;
2019-12-09 19:34:37 -05:00
console . log ( '' ) ;
console . log ( ' TOTEXT [translationfile] [textfile] [language code]' ) ;
console . log ( ' Save a text for with all strings of a given language.' ) ;
console . log ( '' ) ;
console . log ( ' FROMTEXT [translationfile] [textfile] [language code]' ) ;
console . log ( ' Import raw text string as translations for a language code.' ) ;
2019-10-21 17:57:27 -04:00
process . exit ( ) ;
return ;
}
2019-10-21 14:36:09 -04:00
2019-10-21 17:57:27 -04:00
// Extract strings from web pages and display a report
if ( command == 'check' ) {
var sources = [ ] ;
for ( var i = 3 ; i < process . argv . length ; i ++ ) { if ( fs . existsSync ( process . argv [ i ] ) == false ) { console . log ( 'Missing file: ' + process . argv [ i ] ) ; process . exit ( ) ; return ; } sources . push ( process . argv [ i ] ) ; }
if ( sources . length == 0 ) { console . log ( 'No source files specified.' ) ; process . exit ( ) ; return ; }
performCheck = true ;
sourceStrings = { } ;
for ( var i = 0 ; i < sources . length ; i ++ ) { extractFromHtml ( sources [ i ] ) ; }
var count = 0 ;
for ( var i in sourceStrings ) { count ++ ; }
console . log ( 'Extracted ' + count + ' strings.' ) ;
process . exit ( ) ;
return ;
}
2019-10-21 14:36:09 -04:00
2019-10-21 17:57:27 -04:00
// Extract strings from web pages
if ( command == 'extract' ) {
if ( process . argv . length < 4 ) { console . log ( 'No language file specified.' ) ; process . exit ( ) ; return ; }
var sources = [ ] ;
for ( var i = 4 ; i < process . argv . length ; i ++ ) { if ( fs . existsSync ( process . argv [ i ] ) == false ) { console . log ( 'Missing file: ' + process . argv [ i ] ) ; process . exit ( ) ; return ; } sources . push ( process . argv [ i ] ) ; }
if ( sources . length == 0 ) { console . log ( 'No source files specified.' ) ; process . exit ( ) ; return ; }
extract ( process . argv [ 3 ] , sources ) ;
}
2019-10-21 14:36:09 -04:00
2019-12-09 19:34:37 -05:00
// Save a text file with all the strings for a given language
if ( command == 'totext' ) {
if ( ( process . argv . length == 6 ) ) {
if ( fs . existsSync ( process . argv [ 3 ] ) == false ) { console . log ( 'Unable to find: ' + process . argv [ 3 ] ) ; return ; }
totext ( process . argv [ 3 ] , process . argv [ 4 ] , process . argv [ 5 ] ) ;
} else {
console . log ( 'Usage: TOTEXT [translationfile] [textfile] [language code]' ) ;
}
return ;
}
// Read a text file and use it as translation for a given language
if ( command == 'fromtext' ) {
if ( ( process . argv . length == 6 ) ) {
if ( fs . existsSync ( process . argv [ 3 ] ) == false ) { console . log ( 'Unable to find: ' + process . argv [ 3 ] ) ; return ; }
if ( fs . existsSync ( process . argv [ 4 ] ) == false ) { console . log ( 'Unable to find: ' + process . argv [ 4 ] ) ; return ; }
fromtext ( process . argv [ 3 ] , process . argv [ 4 ] , process . argv [ 5 ] ) ;
} else {
console . log ( 'Usage: FROMTEXT [translationfile] [textfile] [language code]' ) ;
}
return ;
}
2019-12-09 16:43:02 -05:00
// Merge one language from a language file into another language file.
if ( command == 'merge' ) {
if ( ( process . argv . length == 6 ) ) {
if ( fs . existsSync ( process . argv [ 3 ] ) == false ) { console . log ( 'Unable to find: ' + process . argv [ 3 ] ) ; return ; }
if ( fs . existsSync ( process . argv [ 4 ] ) == false ) { console . log ( 'Unable to find: ' + process . argv [ 4 ] ) ; return ; }
merge ( process . argv [ 3 ] , process . argv [ 4 ] , process . argv [ 5 ] ) ;
} else {
console . log ( 'Usage: MERGE [sourcefile] [tartgetfile] [language code]' ) ;
}
return ;
}
2019-10-21 17:57:27 -04:00
// Extract or translate all MeshCentral strings
if ( command == 'extractall' ) { extract ( "translate.json" , meshCentralSourceFiles ) ; }
if ( command == 'translateall' ) {
2019-12-26 14:37:49 -05:00
if ( fs . existsSync ( '../views/translations' ) == false ) { fs . mkdirSync ( '../views/translations' ) ; }
if ( fs . existsSync ( '../public/translations' ) == false ) { fs . mkdirSync ( '../public/translations' ) ; }
var lang = null ;
if ( process . argv . length > 4 ) { lang = process . argv [ 4 ] . toLowerCase ( ) ; }
if ( process . argv . length > 3 ) {
2019-12-06 19:49:40 -05:00
if ( fs . existsSync ( process . argv [ 3 ] ) == false ) {
console . log ( 'Unable to find: ' + process . argv [ 3 ] ) ;
} else {
2019-12-26 14:37:49 -05:00
translate ( lang , process . argv [ 3 ] , meshCentralSourceFiles , 'translations' ) ;
2019-12-06 19:49:40 -05:00
}
} else {
2019-12-26 14:37:49 -05:00
if ( fs . existsSync ( 'translate.json' ) == false ) {
2019-12-06 19:49:40 -05:00
console . log ( 'Unable to find translate.json.' ) ;
} else {
2019-12-26 14:37:49 -05:00
translate ( lang , 'translate.json' , meshCentralSourceFiles , 'translations' ) ;
2019-12-06 19:49:40 -05:00
}
}
return ;
2019-10-21 17:57:27 -04:00
}
2019-10-21 14:36:09 -04:00
2019-10-21 17:57:27 -04:00
// Translate web pages to a given language given a language file
if ( command == 'translate' ) {
if ( process . argv . length < 4 ) { console . log ( "No language specified." ) ; process . exit ( ) ; return ; }
if ( process . argv . length < 5 ) { console . log ( "No language file specified." ) ; process . exit ( ) ; return ; }
var lang = process . argv [ 3 ] . toLowerCase ( ) ;
var langFile = process . argv [ 4 ] ;
if ( fs . existsSync ( langFile ) == false ) { console . log ( "Missing language file: " + langFile ) ; process . exit ( ) ; return ; }
2019-10-21 14:36:09 -04:00
2019-10-21 17:57:27 -04:00
var sources = [ ] ;
for ( var i = 5 ; i < process . argv . length ; i ++ ) { if ( fs . existsSync ( process . argv [ i ] ) == false ) { console . log ( "Missing file: " + process . argv [ i ] ) ; process . exit ( ) ; return ; } sources . push ( process . argv [ i ] ) ; }
if ( sources . length == 0 ) { console . log ( "No source files specified." ) ; process . exit ( ) ; return ; }
2019-10-21 14:36:09 -04:00
2019-10-21 17:57:27 -04:00
translate ( lang , langFile , sources , false ) ;
}
2019-10-24 13:45:58 -04:00
if ( command == 'minifyall' ) {
for ( var i in meshCentralSourceFiles ) {
var outname = meshCentralSourceFiles [ i ] ;
var outnamemin = null ;
if ( outname . endsWith ( '.handlebars' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 11 ) + '-min.handlebars' ) ;
} else if ( outname . endsWith ( '.html' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 5 ) + '-min.html' ) ;
} else if ( outname . endsWith ( '.htm' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 4 ) + '-min.htm' ) ;
} else {
outnamemin = ( outname , outname + '.min' ) ;
}
console . log ( 'Generating ' + outnamemin + '...' ) ;
// Minify the file
if ( minifyLib = 2 ) {
var minifiedOut = minify ( fs . readFileSync ( outname ) . toString ( ) , {
collapseBooleanAttributes : true ,
2019-10-25 02:58:39 -04:00
collapseInlineTagWhitespace : false , // This is not good.
2019-10-24 13:45:58 -04:00
collapseWhitespace : true ,
minifyCSS : true ,
minifyJS : true ,
removeComments : true ,
removeOptionalTags : true ,
removeEmptyAttributes : true ,
removeAttributeQuotes : true ,
removeRedundantAttributes : true ,
removeScriptTypeAttributes : true ,
removeTagWhitespace : true ,
preserveLineBreaks : false ,
useShortDoctype : true
} ) ;
fs . writeFileSync ( outnamemin , minifiedOut , { flag : 'w+' } ) ;
}
}
}
2019-10-21 14:36:09 -04:00
}
2019-12-09 19:34:37 -05:00
function totext ( source , target , lang ) {
// Load the source language file
var sourceLangFileData = null ;
try { sourceLangFileData = JSON . parse ( fs . readFileSync ( source ) ) ; } catch ( ex ) { }
if ( ( sourceLangFileData == null ) || ( sourceLangFileData . strings == null ) ) { console . log ( "Invalid source language file." ) ; process . exit ( ) ; return ; }
console . log ( 'Writing ' + lang + '...' ) ;
// Generate raw text
var output = [ ] ;
2019-12-23 17:25:27 -05:00
var outputCharCount = 0 ; // Google has a 5000 character limit
2019-12-09 19:34:37 -05:00
var splitOutput = [ ] ;
2019-12-23 17:25:27 -05:00
var splitOutputPtr = 1 ;
2019-12-09 19:34:37 -05:00
var count = 0 ;
for ( var i in sourceLangFileData . strings ) {
if ( ( sourceLangFileData . strings [ i ] [ lang ] != null ) && ( sourceLangFileData . strings [ i ] [ lang ] . indexOf ( '\r' ) == - 1 ) && ( sourceLangFileData . strings [ i ] [ lang ] . indexOf ( '\n' ) == - 1 ) ) {
output . push ( sourceLangFileData . strings [ i ] [ lang ] ) ;
2019-12-23 17:25:27 -05:00
outputCharCount += ( sourceLangFileData . strings [ i ] [ lang ] . length + 2 ) ;
if ( outputCharCount > 4500 ) { outputCharCount = 0 ; splitOutputPtr ++ ; }
if ( splitOutput [ splitOutputPtr ] == null ) { splitOutput [ splitOutputPtr ] = [ ] ; }
splitOutput [ splitOutputPtr ] . push ( sourceLangFileData . strings [ i ] [ lang ] ) ;
2019-12-09 19:34:37 -05:00
} else {
output . push ( '' ) ;
2019-12-23 17:25:27 -05:00
outputCharCount += 2 ;
if ( outputCharCount > 4500 ) { outputCharCount = 0 ; splitOutputPtr ++ ; }
if ( splitOutput [ splitOutputPtr ] == null ) { splitOutput [ splitOutputPtr ] = [ ] ; }
splitOutput [ splitOutputPtr ] . push ( '' ) ;
2019-12-09 19:34:37 -05:00
}
count ++ ;
}
2019-12-23 17:25:27 -05:00
if ( splitOutputPtr == 1 ) {
2019-12-09 19:34:37 -05:00
// Save the target back
fs . writeFileSync ( target + '-' + lang + '.txt' , output . join ( '\r\n' ) , { flag : 'w+' } ) ;
console . log ( 'Done.' ) ;
} else {
// Save the text in 1000 string bunches
for ( var i in splitOutput ) {
console . log ( 'Writing ' + target + '-' + lang + '-' + i + '.txt...' ) ;
fs . writeFileSync ( target + '-' + lang + '-' + i + '.txt' , splitOutput [ i ] . join ( '\r\n' ) , { flag : 'w+' } ) ;
}
console . log ( 'Done.' ) ;
}
}
function fromtext ( source , target , lang ) {
// Load the source language file
var sourceLangFileData = null ;
try { sourceLangFileData = JSON . parse ( fs . readFileSync ( source ) ) ; } catch ( ex ) { }
if ( ( sourceLangFileData == null ) || ( sourceLangFileData . strings == null ) ) { console . log ( "Invalid source language file." ) ; process . exit ( ) ; return ; }
console . log ( 'Updating ' + lang + '...' ) ;
// Read raw text
var rawText = fs . readFileSync ( target ) . toString ( 'utf8' ) ;
var rawTextArray = rawText . split ( '\r\n' ) ;
var rawTextPtr = 0 ;
console . log ( 'Translation file: ' + sourceLangFileData . strings . length + ' string(s)' ) ;
console . log ( 'Text file: ' + rawTextArray . length + ' string(s)' ) ;
if ( sourceLangFileData . strings . length != rawTextArray . length ) { console . log ( 'String count mismatch, unable to import.' ) ; process . exit ( 1 ) ; return ; }
var output = [ ] ;
var splitOutput = [ ] ;
for ( var i in sourceLangFileData . strings ) {
if ( ( sourceLangFileData . strings [ i ] [ 'en' ] != null ) && ( sourceLangFileData . strings [ i ] [ 'en' ] . indexOf ( '\r' ) == - 1 ) && ( sourceLangFileData . strings [ i ] [ 'en' ] . indexOf ( '\n' ) == - 1 ) ) {
2019-12-10 21:17:25 -05:00
if ( sourceLangFileData . strings [ i ] [ lang ] == null ) { sourceLangFileData . strings [ i ] [ lang ] = rawTextArray [ i ] ; }
2019-12-09 19:34:37 -05:00
}
}
2020-01-07 18:10:12 -05:00
fs . writeFileSync ( source + '-new' , translationsToJson ( sourceLangFileData ) , { flag : 'w+' } ) ;
2019-12-09 19:34:37 -05:00
console . log ( 'Done.' ) ;
}
2019-12-09 16:43:02 -05:00
function merge ( source , target , lang ) {
// Load the source language file
var sourceLangFileData = null ;
try { sourceLangFileData = JSON . parse ( fs . readFileSync ( source ) ) ; } catch ( ex ) { }
if ( ( sourceLangFileData == null ) || ( sourceLangFileData . strings == null ) ) { console . log ( "Invalid source language file." ) ; process . exit ( ) ; return ; }
// Load the target language file
var targetLangFileData = null ;
try { targetLangFileData = JSON . parse ( fs . readFileSync ( target ) ) ; } catch ( ex ) { }
if ( ( targetLangFileData == null ) || ( targetLangFileData . strings == null ) ) { console . log ( "Invalid target language file." ) ; process . exit ( ) ; return ; }
console . log ( 'Merging ' + lang + '...' ) ;
// Index the target file
var index = { } ;
for ( var i in targetLangFileData . strings ) { if ( targetLangFileData . strings [ i ] . en != null ) { index [ targetLangFileData . strings [ i ] . en ] = targetLangFileData . strings [ i ] ; } }
// Merge the translation
for ( var i in sourceLangFileData . strings ) {
if ( ( sourceLangFileData . strings [ i ] . en != null ) && ( sourceLangFileData . strings [ i ] [ lang ] != null ) && ( index [ sourceLangFileData . strings [ i ] . en ] != null ) ) {
2019-12-11 13:17:26 -05:00
//if (sourceLangFileData.strings[i][lang] == null) {
index [ sourceLangFileData . strings [ i ] . en ] [ lang ] = sourceLangFileData . strings [ i ] [ lang ] ;
//}
2019-12-09 16:43:02 -05:00
}
}
// Deindex the new target file
var targetData = { strings : [ ] } ;
for ( var i in index ) { targetData . strings . push ( index [ i ] ) ; }
// Save the target back
2020-01-07 18:10:12 -05:00
fs . writeFileSync ( target , translationsToJson ( targetData ) , { flag : 'w+' } ) ;
2019-12-09 16:43:02 -05:00
console . log ( 'Done.' ) ;
}
2019-10-21 14:36:09 -04:00
function translate ( lang , langFile , sources , createSubDir ) {
// Load the language file
var langFileData = null ;
try { langFileData = JSON . parse ( fs . readFileSync ( langFile ) ) ; } catch ( ex ) { }
if ( ( langFileData == null ) || ( langFileData . strings == null ) ) { console . log ( "Invalid language file." ) ; process . exit ( ) ; return ; }
if ( lang != null ) {
// Translate a single language
translateEx ( lang , langFileData , sources , createSubDir ) ;
} else {
// See that languages are in the translation file
langs = { } ;
2019-12-26 14:37:49 -05:00
for ( var i in langFileData . strings ) { var entry = langFileData . strings [ i ] ; for ( var j in entry ) { if ( ( j != 'en' ) && ( j != 'xloc' ) && ( j != '*' ) ) { langs [ j . toLowerCase ( ) ] = true ; } } }
2019-10-21 14:36:09 -04:00
for ( var i in langs ) { translateEx ( i , langFileData , sources , createSubDir ) ; }
}
2019-10-24 13:45:58 -04:00
//process.exit();
2019-10-21 14:36:09 -04:00
return ;
}
function translateEx ( lang , langFileData , sources , createSubDir ) {
// Build translation table, simple source->target for the given language.
translationTable = { } ;
for ( var i in langFileData . strings ) {
var entry = langFileData . strings [ i ] ;
if ( ( entry [ 'en' ] != null ) && ( entry [ lang ] != null ) ) { translationTable [ entry [ 'en' ] ] = entry [ lang ] ; }
}
// Translate the files
for ( var i = 0 ; i < sources . length ; i ++ ) { translateFromHtml ( lang , sources [ i ] , createSubDir ) ; }
}
function extract ( langFile , sources ) {
sourceStrings = { } ;
if ( fs . existsSync ( langFile ) == true ) {
var langFileData = null ;
try { langFileData = JSON . parse ( fs . readFileSync ( langFile ) ) ; } catch ( ex ) { }
if ( ( langFileData == null ) || ( langFileData . strings == null ) ) { console . log ( "Invalid language file." ) ; process . exit ( ) ; return ; }
for ( var i in langFileData . strings ) {
sourceStrings [ langFileData . strings [ i ] [ 'en' ] ] = langFileData . strings [ i ] ;
delete sourceStrings [ langFileData . strings [ i ] [ 'en' ] ] . xloc ;
}
}
for ( var i = 0 ; i < sources . length ; i ++ ) { extractFromHtml ( sources [ i ] ) ; }
var count = 0 , output = [ ] ;
2019-12-01 18:21:33 -05:00
for ( var i in sourceStrings ) {
count ++ ;
sourceStrings [ i ] [ 'en' ] = i ;
2019-12-23 17:25:27 -05:00
//if ((sourceStrings[i].xloc != null) && (sourceStrings[i].xloc.length > 0)) { output.push(sourceStrings[i]); } // Only save results that have a source location.
output . push ( sourceStrings [ i ] ) ; // Save all results
2019-12-01 18:21:33 -05:00
}
2020-01-07 18:10:12 -05:00
fs . writeFileSync ( langFile , translationsToJson ( { strings : output } ) , { flag : 'w+' } ) ;
2019-10-21 14:36:09 -04:00
console . log ( format ( "{0} strings in output file." , count ) ) ;
process . exit ( ) ;
return ;
}
function extractFromHtml ( file ) {
var data = fs . readFileSync ( file ) ;
2019-10-21 17:57:27 -04:00
var { JSDOM } = jsdom ;
2019-10-21 14:36:09 -04:00
const dom = new JSDOM ( data , { includeNodeLocations : true } ) ;
console . log ( "Processing HTML: " + path . basename ( file ) ) ;
getStrings ( path . basename ( file ) , dom . window . document . querySelector ( 'body' ) ) ;
}
function getStrings ( name , node ) {
for ( var i = 0 ; i < node . childNodes . length ; i ++ ) {
var subnode = node . childNodes [ i ] ;
// Check if the "value" attribute exists and needs to be translated
if ( ( subnode . attributes != null ) && ( subnode . attributes . length > 0 ) ) {
2019-10-21 17:57:27 -04:00
var subnodeignore = false , subnodevalue = null , subnodeplaceholder = null , subnodetitle = null ;
2019-10-21 14:36:09 -04:00
for ( var j in subnode . attributes ) {
if ( ( subnode . attributes [ j ] . name == 'type' ) && ( subnode . attributes [ j ] . value == 'hidden' ) ) { subnodeignore = true ; }
if ( subnode . attributes [ j ] . name == 'value' ) { subnodevalue = subnode . attributes [ j ] . value ; }
2019-10-21 17:57:27 -04:00
if ( subnode . attributes [ j ] . name == 'placeholder' ) { subnodeplaceholder = subnode . attributes [ j ] . value ; }
if ( subnode . attributes [ j ] . name == 'title' ) { subnodetitle = subnode . attributes [ j ] . value ; }
2019-10-21 14:36:09 -04:00
}
2019-10-21 17:57:27 -04:00
if ( ( subnodevalue != null ) && isNumber ( subnodevalue ) == true ) { subnodevalue = null ; }
if ( ( subnodeplaceholder != null ) && isNumber ( subnodeplaceholder ) == true ) { subnodeplaceholder = null ; }
if ( ( subnodetitle != null ) && isNumber ( subnodetitle ) == true ) { subnodetitle = null ; }
2019-10-21 14:36:09 -04:00
if ( ( subnodeignore == false ) && ( subnodevalue != null ) ) {
2019-10-21 17:57:27 -04:00
// Add a new string to the list (value)
2019-10-21 14:36:09 -04:00
if ( sourceStrings [ subnodevalue ] == null ) { sourceStrings [ subnodevalue ] = { en : subnodevalue , xloc : [ name ] } ; } else { if ( sourceStrings [ subnodevalue ] . xloc == null ) { sourceStrings [ subnodevalue ] . xloc = [ ] ; } sourceStrings [ subnodevalue ] . xloc . push ( name ) ; }
}
2019-10-21 17:57:27 -04:00
if ( subnodeplaceholder != null ) {
// Add a new string to the list (placeholder)
if ( sourceStrings [ subnodeplaceholder ] == null ) { sourceStrings [ subnodeplaceholder ] = { en : subnodeplaceholder , xloc : [ name ] } ; } else { if ( sourceStrings [ subnodeplaceholder ] . xloc == null ) { sourceStrings [ subnodeplaceholder ] . xloc = [ ] ; } sourceStrings [ subnodeplaceholder ] . xloc . push ( name ) ; }
}
if ( subnodetitle != null ) {
// Add a new string to the list (title)
if ( sourceStrings [ subnodetitle ] == null ) { sourceStrings [ subnodetitle ] = { en : subnodetitle , xloc : [ name ] } ; } else { if ( sourceStrings [ subnodetitle ] . xloc == null ) { sourceStrings [ subnodetitle ] . xloc = [ ] ; } sourceStrings [ subnodetitle ] . xloc . push ( name ) ; }
}
2019-10-21 14:36:09 -04:00
}
// Check the content of the element
var subname = subnode . id ;
if ( subname == null || subname == '' ) { subname = i ; }
if ( subnode . hasChildNodes ( ) ) {
getStrings ( name + '->' + subname , subnode ) ;
} else {
if ( subnode . nodeValue == null ) continue ;
var nodeValue = subnode . nodeValue . trim ( ) . split ( '\\r' ) . join ( '' ) . split ( '\\n' ) . join ( '' ) . trim ( ) ;
if ( ( nodeValue . length > 0 ) && ( subnode . nodeType == 3 ) ) {
if ( ( node . tagName != 'SCRIPT' ) && ( node . tagName != 'STYLE' ) && ( nodeValue . length < 8000 ) && ( nodeValue . startsWith ( '{{{' ) == false ) && ( nodeValue != ' ' ) ) {
if ( performCheck ) { console . log ( ' "' + nodeValue + '"' ) ; }
// Add a new string to the list
if ( sourceStrings [ nodeValue ] == null ) { sourceStrings [ nodeValue ] = { en : nodeValue , xloc : [ name ] } ; } else { if ( sourceStrings [ nodeValue ] . xloc == null ) { sourceStrings [ nodeValue ] . xloc = [ ] ; } sourceStrings [ nodeValue ] . xloc . push ( name ) ; }
} else if ( node . tagName == 'SCRIPT' ) {
// Parse JavaScript
getStringFromJavaScript ( name , subnode . nodeValue ) ;
}
}
}
}
}
function getStringFromJavaScript ( name , script ) {
if ( performCheck ) { console . log ( format ( 'Processing JavaScript of {0} bytes: {1}' , script . length , name ) ) ; }
var tokenScript = esprima . tokenize ( script ) , count = 0 ;
for ( var i in tokenScript ) {
var token = tokenScript [ i ] ;
if ( ( token . type == 'String' ) && ( token . value . length > 2 ) && ( token . value [ 0 ] == '"' ) ) {
var str = token . value . substring ( 1 , token . value . length - 1 ) ;
//if (performCheck) { console.log(' ' + name + '->' + (++count), token.value); }
if ( performCheck ) { console . log ( ' ' + token . value ) ; }
if ( sourceStrings [ str ] == null ) { sourceStrings [ str ] = { en : str , xloc : [ name + '->' + ( ++ count ) ] } ; } else { if ( sourceStrings [ str ] . xloc == null ) { sourceStrings [ str ] . xloc = [ ] ; } sourceStrings [ str ] . xloc . push ( name + '->' + ( ++ count ) ) ; }
}
}
}
function translateFromHtml ( lang , file , createSubDir ) {
var data = fs . readFileSync ( file ) ;
2019-10-21 17:57:27 -04:00
var { JSDOM } = jsdom ;
2019-10-21 14:36:09 -04:00
const dom = new JSDOM ( data , { includeNodeLocations : true } ) ;
2020-01-04 15:44:12 -05:00
console . log ( "Translating HTML (" + lang + "): " + path . basename ( file ) ) ;
2019-10-21 14:36:09 -04:00
translateStrings ( path . basename ( file ) , dom . window . document . querySelector ( 'body' ) ) ;
var out = dom . serialize ( ) ;
var outname = file ;
2019-10-24 13:45:58 -04:00
var outnamemin = null ;
2019-10-21 14:36:09 -04:00
if ( createSubDir != null ) { outname = path . join ( path . dirname ( file ) , createSubDir , path . basename ( file ) ) ; }
2019-10-24 13:45:58 -04:00
if ( outname . endsWith ( '.handlebars' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 11 ) + '-min_' + lang + '.handlebars' ) ;
outname = ( outname . substring ( 0 , outname . length - 11 ) + '_' + lang + '.handlebars' ) ;
} else if ( outname . endsWith ( '.html' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 5 ) + '-min_' + lang + '.html' ) ;
outname = ( outname . substring ( 0 , outname . length - 5 ) + '_' + lang + '.html' ) ;
} else if ( outname . endsWith ( '.htm' ) ) {
outnamemin = ( outname . substring ( 0 , outname . length - 4 ) + '-min_' + lang + '.htm' ) ;
outname = ( outname . substring ( 0 , outname . length - 4 ) + '_' + lang + '.htm' ) ;
} else {
outnamemin = ( outname + '_' + lang + '.min' ) ;
outname = ( outname + '_' + lang ) ;
}
2019-10-21 14:36:09 -04:00
fs . writeFileSync ( outname , out , { flag : 'w+' } ) ;
2019-10-24 13:45:58 -04:00
// Minify the file
if ( minifyLib == 1 ) {
minify . file ( {
file : outname ,
dist : outnamemin
} , ( e , compress ) => {
if ( e ) { console . log ( 'ERROR ' , e ) ; return done ( ) ; }
compress . run ( ( e ) => { e ? console . log ( 'Minification fail' , e ) : console . log ( 'Minification sucess' ) ; minifyDone ( ) ; } ) ;
}
) ;
}
// Minify the file
if ( minifyLib = 2 ) {
var minifiedOut = minify ( out , {
collapseBooleanAttributes : true ,
2019-10-25 02:58:39 -04:00
collapseInlineTagWhitespace : false , // This is not good.
2019-10-24 13:45:58 -04:00
collapseWhitespace : true ,
minifyCSS : true ,
minifyJS : true ,
removeComments : true ,
removeOptionalTags : true ,
removeEmptyAttributes : true ,
removeAttributeQuotes : true ,
removeRedundantAttributes : true ,
removeScriptTypeAttributes : true ,
removeTagWhitespace : true ,
preserveLineBreaks : false ,
useShortDoctype : true
} ) ;
fs . writeFileSync ( outnamemin , minifiedOut , { flag : 'w+' } ) ;
}
2019-10-21 14:36:09 -04:00
}
2019-10-24 13:45:58 -04:00
function minifyDone ( ) { console . log ( 'Completed minification.' ) ; }
2019-10-21 14:36:09 -04:00
function translateStrings ( name , node ) {
for ( var i = 0 ; i < node . childNodes . length ; i ++ ) {
var subnode = node . childNodes [ i ] ;
// Check if the "value" attribute exists and needs to be translated
if ( ( subnode . attributes != null ) && ( subnode . attributes . length > 0 ) ) {
2019-10-21 17:57:27 -04:00
var subnodeignore = false , subnodevalue = null , subnodeindex = null , subnodeplaceholder = null , subnodeplaceholderindex = null , subnodetitle = null , subnodetitleindex = null ;
2019-10-21 14:36:09 -04:00
for ( var j in subnode . attributes ) {
if ( ( subnode . attributes [ j ] . name == 'type' ) && ( subnode . attributes [ j ] . value == 'hidden' ) ) { subnodeignore = true ; }
if ( subnode . attributes [ j ] . name == 'value' ) { subnodevalue = subnode . attributes [ j ] . value ; subnodeindex = j ; }
2019-10-21 17:57:27 -04:00
if ( subnode . attributes [ j ] . name == 'placeholder' ) { subnodeplaceholder = subnode . attributes [ j ] . value ; subnodeplaceholderindex = j ; }
if ( subnode . attributes [ j ] . name == 'title' ) { subnodetitle = subnode . attributes [ j ] . value ; subnodetitleindex = j ; }
2019-10-21 14:36:09 -04:00
}
2019-10-21 17:57:27 -04:00
if ( ( subnodevalue != null ) && isNumber ( subnodevalue ) == true ) { subnodevalue = null ; }
if ( ( subnodeplaceholder != null ) && isNumber ( subnodeplaceholder ) == true ) { subnodeplaceholder = null ; }
if ( ( subnodetitle != null ) && isNumber ( subnodetitle ) == true ) { subnodetitle = null ; }
2019-10-21 14:36:09 -04:00
if ( ( subnodeignore == false ) && ( subnodevalue != null ) ) {
2019-10-21 17:57:27 -04:00
// Perform attribute translation for value
2019-10-21 14:36:09 -04:00
if ( translationTable [ subnodevalue ] != null ) { subnode . attributes [ subnodeindex ] . value = translationTable [ subnodevalue ] ; }
}
2019-10-21 17:57:27 -04:00
if ( subnodeplaceholder != null ) {
// Perform attribute translation for placeholder
if ( translationTable [ subnodeplaceholder ] != null ) { subnode . attributes [ subnodeplaceholderindex ] . value = translationTable [ subnodeplaceholder ] ; }
}
if ( subnodetitle != null ) {
// Perform attribute translation for title
if ( translationTable [ subnodetitle ] != null ) { subnode . attributes [ subnodetitleindex ] . value = translationTable [ subnodetitle ] ; }
}
2019-10-21 14:36:09 -04:00
}
var subname = subnode . id ;
if ( subname == null || subname == '' ) { subname = i ; }
if ( subnode . hasChildNodes ( ) ) {
translateStrings ( name + '->' + subname , subnode ) ;
} else {
if ( subnode . nodeValue == null ) continue ;
var nodeValue = subnode . nodeValue . trim ( ) . split ( '\\r' ) . join ( '' ) . split ( '\\n' ) . join ( '' ) . trim ( ) ;
2019-10-21 17:57:27 -04:00
// Look for the front trim
var frontTrim = '' , backTrim = '' ; ;
var x1 = subnode . nodeValue . indexOf ( nodeValue ) ;
if ( x1 > 0 ) { frontTrim = subnode . nodeValue . substring ( 0 , x1 ) ; }
if ( x1 != - 1 ) { backTrim = subnode . nodeValue . substring ( x1 + nodeValue . length ) ; }
2019-10-21 14:36:09 -04:00
if ( ( nodeValue . length > 0 ) && ( subnode . nodeType == 3 ) ) {
if ( ( node . tagName != 'SCRIPT' ) && ( node . tagName != 'STYLE' ) && ( nodeValue . length < 8000 ) && ( nodeValue . startsWith ( '{{{' ) == false ) && ( nodeValue != ' ' ) ) {
// Check if we have a translation for this string
2019-10-21 17:57:27 -04:00
if ( translationTable [ nodeValue ] ) { subnode . nodeValue = ( frontTrim + translationTable [ nodeValue ] + backTrim ) ; }
2019-10-21 14:36:09 -04:00
} else if ( node . tagName == 'SCRIPT' ) {
// Translate JavaScript
subnode . nodeValue = translateStringsFromJavaScript ( name , subnode . nodeValue ) ;
}
}
}
}
}
function translateStringsFromJavaScript ( name , script ) {
if ( performCheck ) { console . log ( format ( 'Translating JavaScript of {0} bytes: {1}' , script . length , name ) ) ; }
var tokenScript = esprima . tokenize ( script , { range : true } ) , count = 0 ;
var output = [ ] , ptr = 0 ;
for ( var i in tokenScript ) {
var token = tokenScript [ i ] ;
if ( ( token . type == 'String' ) && ( token . value . length > 2 ) && ( token . value [ 0 ] == '"' ) ) {
var str = token . value . substring ( 1 , token . value . length - 1 ) ;
if ( translationTable [ str ] ) {
output . push ( script . substring ( ptr , token . range [ 0 ] ) ) ;
output . push ( '"' + translationTable [ str ] + '"' ) ;
ptr = token . range [ 1 ] ;
}
}
}
output . push ( script . substring ( ptr ) ) ;
return output . join ( '' ) ;
}
function isNumber ( x ) { return ( ( '' + parseInt ( x ) ) === x ) || ( ( '' + parseFloat ( x ) ) === x ) ; }
2019-10-21 17:57:27 -04:00
function format ( format ) { var args = Array . prototype . slice . call ( arguments , 1 ) ; return format . replace ( /{(\d+)}/g , function ( match , number ) { return typeof args [ number ] != 'undefined' ? args [ number ] : match ; } ) ; } ;
// Check if a list of modules are present and install any missing ones
var InstallModuleChildProcess = null ;
var previouslyInstalledModules = { } ;
function InstallModules ( modules , func ) {
var missingModules = [ ] ;
if ( previouslyInstalledModules == null ) { previouslyInstalledModules = { } ; }
if ( modules . length > 0 ) {
for ( var i in modules ) {
try {
var xxmodule = require ( modules [ i ] ) ;
} catch ( e ) {
if ( previouslyInstalledModules [ modules [ i ] ] !== true ) { missingModules . push ( modules [ i ] ) ; }
}
}
if ( missingModules . length > 0 ) { InstallModule ( missingModules . shift ( ) , InstallModules , modules , func ) ; } else { func ( ) ; }
}
}
// Check if a module is present and install it if missing
function InstallModule ( modulename , func , tag1 , tag2 ) {
console . log ( 'Installing ' + modulename + '...' ) ;
var child _process = require ( 'child_process' ) ;
var parentpath = _ _dirname ;
// Get the working directory
if ( ( _ _dirname . endsWith ( '/node_modules/meshcentral' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral' ) ) || ( _ _dirname . endsWith ( '/node_modules/meshcentral/' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral\\' ) ) ) { parentpath = require ( 'path' ) . join ( _ _dirname , '../..' ) ; }
// Looks like we need to keep a global reference to the child process object for this to work correctly.
2019-11-15 17:36:41 -05:00
InstallModuleChildProcess = child _process . exec ( 'npm install --no-optional --save ' + modulename , { maxBuffer : 512000 , timeout : 120000 , cwd : parentpath } , function ( error , stdout , stderr ) {
2019-10-21 17:57:27 -04:00
InstallModuleChildProcess = null ;
if ( ( error != null ) && ( error != '' ) ) {
console . log ( 'ERROR: Unable to install required module "' + modulename + '". May not have access to npm, or npm may not have suffisent rights to load the new module. Try "npm install ' + modulename + '" to manualy install this module.\r\n' ) ;
process . exit ( ) ;
return ;
}
previouslyInstalledModules [ modulename ] = true ;
func ( tag1 , tag2 ) ;
return ;
} ) ;
2020-01-07 18:10:12 -05:00
}
// Convert the translations to a standardized JSON we can use in GitHub
// Strings are sorder by english source and object keys are sorted
function translationsToJson ( t ) {
var arr2 = [ ] , arr = t . strings ;
for ( var i in arr ) {
var names = [ ] , el = arr [ i ] , el2 = { } ;
for ( var j in el ) { names . push ( j ) ; }
names . sort ( ) ;
for ( var j in names ) { el2 [ names [ j ] ] = el [ names [ j ] ] ; }
2020-01-07 20:12:09 -05:00
if ( el2 . xloc != null ) { el2 . xloc . sort ( ) ; }
2020-01-07 18:10:12 -05:00
arr2 . push ( el2 ) ;
}
arr2 . sort ( function ( a , b ) { if ( a . en > b . en ) return 1 ; if ( a . en < b . en ) return - 1 ; return 0 ; } ) ;
return JSON . stringify ( { strings : arr2 } , null , ' ' ) ;
2019-10-21 17:57:27 -04:00
}