twee
Version:
Twee Framework - the most powerful, elegant and extensive framework for Node.js based on Express.js
260 lines (224 loc) • 9.52 kB
JavaScript
var extend = require('../utils/extend')
, fs = require('fs')
, path = require('path')
, commander = require('commander')
, sha1 = require('sha1');
var processLoadCharLast = '';
/**
* Return progress char
* @returns {string}
*/
function getProcessLoadChar() {
processLoadCharLast = processLoadCharLast || '[-]';
if (processLoadCharLast == '[-]') {
processLoadCharLast = '[\\]';
} else if (processLoadCharLast == '[\\]') {
processLoadCharLast = '[|]';
} else if (processLoadCharLast == '[|]') {
processLoadCharLast = '[/]';
} else if (processLoadCharLast == '[/]') {
processLoadCharLast = '[-]';
}
return processLoadCharLast;
}
/**
* Translation instructions regex
* @type {RegExp}
*/
var trRegEx = /(tr\('([^'](\\\'){0,})+'\))|(tr\("([^\"](\\\"){0,})+"\))/gi;
/**
* Search for all the entries of tr() in provided directories/files
* @param directories
* @param safe
* @returns {{}}
*/
function findTranslations(directories, safe) {
var translations = {};
if (typeof directories == 'string') {
directories = [directories];
}
if (!directories instanceof Array) {
throw new Error('Directories should be string or array');
}
try {
directories.forEach(function(directory){
process.stdout.write('\rCollecting new translations from code.. ' + getProcessLoadChar());
if (!fs.existsSync(directory)) {
return;
}
var fst = fs.statSync(directory);
if (fst.isDirectory()) {
var files = fs.readdirSync(directory);
if (files) {
files.forEach(function(_file){
var subtrans = findTranslations(path.join(directory, _file));
if (typeof subtrans == 'object') {
translations = extend(true, translations, subtrans);
}
});
}
} else if (fst.isFile()) {
var contents = fs.readFileSync(directory).toString();
var matches = contents.match(trRegEx);
var reserved = false;
if (matches) {
//console.log(matches, directory);
matches.forEach(function(tr){
//console.log(tr);
var openStr = ''
, translation = ''
, prevChr;
for (var i = 0; i < tr.length; i++){
prevChr = chr;
var chr = tr[i];
if (!openStr && (chr == '"' || chr == "'")) {
openStr = chr;
} else if (openStr
&& (chr == openStr)
&& prevChr != "\\"
&& tr[i+1] && tr[i+1] === ')'
) {
if (translation.indexOf('TT#') === 0) {
return;
}
if (!reserved && safe) {
fs.copySync(directory, directory + '.tr.src');
reserved = true;
}
translation = translation.replace("\\\'", "\'").replace("\\\"", "\"");
translation = translation.replace("\\\'", "\'");
var translationKey = 'TT#' + parseInt(sha1(translation), 16).toString(36).replace('.', '').replace('e+', '');
translations[translationKey] = translation;
contents = contents.replace(tr, "tr('" + translationKey + "')");
return;
} else if (openStr) {
translation += chr;
}
}
});
fs.writeFileSync(directory, contents);
}
}
});
} catch (e) {
console.log(e.stack || e.toString());
process.exit(1);
}
return translations;
}
/**
* Returning array from comma-separated list
* @param strList String
* @returns {Array|*}
*/
function list(strList) {
return String(strList).split(',');
}
/**
* Getting all the modules translations from all the modules for all the locales
* @returns {{}}
*/
function getMergedModulesTranslations() {
var modulesDirName = path.join(process.cwd(), 'modules');
var modulesNames = fs.readdirSync(modulesDirName);
var translations = {};
modulesNames.forEach(function(moduleName){
var moduleI18nFolder = path.join(process.cwd(), 'modules', moduleName, 'i18n');
if (!fs.existsSync(moduleI18nFolder)) {
return;
}
var moduleI18nFiles = fs.readdirSync(moduleI18nFolder);
if (moduleI18nFiles.length) {
moduleI18nFiles.forEach(function(moduleI18nFile){
var fst = fs.statSync(moduleI18nFolder + '/' + moduleI18nFile);
if (!fst.isFile()) {
return;
}
var locale = moduleI18nFile.replace('.json', '');
moduleI18nFile = moduleI18nFolder + '/' + moduleI18nFile;
translations[locale] = translations[locale] || {};
translations[locale] = extend(true, translations[locale], require(moduleI18nFile));
});
}
});
return translations;
}
/**
* Returning all the application translations as locale hash
* @returns {{}}
*/
function getApplicationTranslations() {
var applicationI18nFolder = path.join(process.cwd(), 'i18n')
, applicationTranslations = {};
if (!fs.existsSync(applicationI18nFolder)) {
fs.mkdirSync(applicationI18nFolder);
// No translations was in application folder. Just return empty hash
return {};
}
// Loading translations
var translationFiles = fs.readdirSync(applicationI18nFolder);
if (translationFiles.length) {
translationFiles.forEach(function(translationFile){
var fst = fs.statSync(applicationI18nFolder + '/' + translationFile);
if (fst.isFile()) {
var locale = translationFile.replace('.json', '');
translationFile = applicationI18nFolder + '/' + translationFile;
applicationTranslations[locale] = require(translationFile);
}
});
}
return applicationTranslations;
}
commander
//.version(require('../package.json').version)
.option('-d, --directory <dir[,dir2[,dir3[...]]]>', 'Scan list of folders for `tr(..)` instructions (default: ["modules", "views"])', ['modules', 'views'])
.option('-l, --locale <locale>', 'Default locale (default: en)', 'en')
.option('-s, --safe <safe>', 'Copy original file to *.tr.src before changing it', true)
.parse(process.argv);
// ----------------------------------------------------------------------------
var modulesTranslations = {};
// First of all merge all the modules translations into application translations
console.log('Loading all the translates from all modules..');
modulesTranslations = getMergedModulesTranslations();
console.log('Done.');
console.log('Collecting main application translations..');
var applicationTranslations = getApplicationTranslations();
console.log('Done.');
var codeTranslations = findTranslations(list(commander.directory), commander.safe);
console.log();
console.log('Done.');
console.log('Processing everything..');
var translations = {};
translations[commander.locale] = codeTranslations;
translations = extend(true, translations, applicationTranslations);
// Extending application translations with modules translations (which was ready long before)
translations = extend(true, translations, modulesTranslations);
// Extending all the other locales with main locale
for (l in translations) {
if (l == commander.locale) {
continue;
}
//translations[l] = extend(true, translations[l], translations[commander.locale]);
for (tr in translations[commander.locale]) {
if (!translations[l][tr]) {
translations[l][tr] = translations[commander.locale][tr];
}
}
}
// Order all the hashes for all locales by ASC for hash searching optimization
/*var sortedTranslations = {};
for (l in translations) {
sortedTranslations[l] = {};
var sortedKeys = Object.keys(translations[l]).sort(function(a, b) {return (a < b)});
for (var i = 0; i < sortedKeys.length; i++) {
sortedTranslations[l][sortedKeys[i]] = translations[l][sortedKeys[i]];
}
}*/
//translations = sortedTranslations;
// Saving all the translations into application folder
for (var l in translations) {
var applicationI18nFile = path.join(process.cwd(), 'i18n', l + '.json');
fs.writeFileSync(applicationI18nFile, JSON.stringify(translations[l], null, '\t'));
}
console.log('Done.');