Skip to content

Commit

Permalink
Merge pull request #13 from Download/v2
Browse files Browse the repository at this point in the history
V2
  • Loading branch information
Download authored Apr 5, 2018
2 parents 43b4e37 + f358fc9 commit d8d4f25
Show file tree
Hide file tree
Showing 18 changed files with 884 additions and 191 deletions.
408 changes: 325 additions & 83 deletions README.md

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions lib/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ var log = require('./log')('i18nline:commands');
var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var wb = chalk.white.bold, gr = chalk.gray, grb = chalk.gray.bold, g=chalk.green, gb=chalk.green.bold;
var w = chalk.white, gr = chalk.gray, grb = chalk.gray.bold, g=chalk.green, gb=chalk.green.bold;

var Utils = require('./utils');
var Check = require('./commands/check');
var Export = require('./commands/export');
var Index = require('./commands/index');
var Synch = require('./commands/synch');
var Help = require('./commands/help');

function capitalize(string) {
Expand All @@ -28,12 +30,12 @@ var Commands = {
if (Command) {
try {
log.info('');
log.info(gr(' ██╗ ███╗ ██╗██╗ ██╗███╗ ██╗███████╗ '));
log.info(gr(' ██║ ████╗ ██║██║ ██║████╗ ██║██╔════╝ '));
log.info(gr(' ██║') + wb('18') + gr(' ██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗ '));
log.info(gr(' ██║ ██║╚██╗██║██║ ██║██║╚██╗██║██╔══╝ '));
log.info(gr(' ██║ ██║ ╚████║███████╗██║██║ ╚████║███████╗ '));
log.info(gr(' ╚═╝ ╚═╝ ╚═══╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ '));
log.info(w(' ██╗ ███╗ ██╗██╗ ██╗███╗ ██╗███████╗ '));
log.info(w(' ██║ ████╗ ██║██║ ██║████╗ ██║██╔════╝ '));
log.info(w(' ██║') + gr('18') + w(' ██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗ '));
log.info(w(' ██║ ██║╚██╗██║██║ ██║██║╚██╗██║██╔══╝ '));
log.info(w(' ██║ ██║ ╚████║███████╗██║██║ ╚████║███████╗ '));
log.info(w(' ╚═╝ ╚═╝ ╚═══╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ '));
log.info(grb(' keep your translations in line '));
log.info('');
return (new Command(options)).run();
Expand All @@ -48,7 +50,9 @@ var Commands = {

Check: Check,
Export: Export,
Synch: Synch,
Help: Help,
Index: Index,
};

function autoConfigureDirectories(options) {
Expand Down
14 changes: 6 additions & 8 deletions lib/commands/base_command.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
var log = require('../log')('i18nline:commands:base');

var chalk = require("chalk");
var fs = require('fs');
var extend = require('extend');
var I18nline = require('../i18nline');

function BaseCommand(options) {
options.outputFile = options.o || options.outputFile;
if (options.silent) log.level = log.NONE;
options.out = options.o || options.out || options.outputFile;
if (options.outputFile) {
log.warn(chalk.yellow('i18nline: Option `outputFile` is deprecated. Prefer `out` instead.'));
}
options = extend({}, I18nline.config, options);
options.patterns = typeof options.patterns == 'string' ? options.patterns.split(',') : options.patterns || [];
options.ignorePatterns = typeof options.ignorePatterns == 'string' ? options.ignorePatterns.split(',') : options.ignorePatterns || [];
options.directories = typeof options.directories == 'string' ? options.directories.split(',') : options.directories;


this.options = options;
this.log = require('../log')(
'i18nline:' + this.constructor.name,
options.silent || options.s
);
}

module.exports = BaseCommand;
Expand Down
30 changes: 17 additions & 13 deletions lib/commands/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function sum(array, prop) {
}

function Check(options) {
if (options.silent) log.level = log.NONE;
BaseCommand.call(this, options);
this.errors = [];
this.translations = new this.TranslationHash();
Expand Down Expand Up @@ -54,12 +55,11 @@ Check.prototype.checkFiles = function() {
Check.prototype.checkWrapper = function(file, checker) {
try {
var found = checker(file);
if (found) {this.log.info(g("+" + found));}
else {this.log.info(gr("."));}
if (found) {log.info(g("+" + found) + (found < 10 ? ' ' : ' ') + gr(file));}
return found;
} catch (e) {
this.errors.push(e.message + "\n" + file);
this.log.error(r("ERR" + this.errors.length));
log.error(r("ERR" + this.errors.length) + (this.errors.length < 10 ? ' ' : ' ') + gr(file));
return 0;
}
};
Expand All @@ -77,26 +77,30 @@ Check.prototype.printSummary = function() {

var translationCount = sum(processors, 'translationCount');
var fileCount = sum(processors, 'fileCount');
var elapsed = (new Date()).getTime() - this.startTime;

this.log.info("\n\n");

for (i = 0; i < errorsLen; i++) {
this.log.error("ERR" + (i+1) + ")\n" + r(errors[i]) + "\n\n");
log.error("\nERR" + (i+1) + ")\n" + r(errors[i]));
}
this.log.info("Finished in " + (elapsed / 1000) + " seconds\n\n");
summary = fileCount + " files, " + translationCount + " strings, " + errorsLen + " failures";
if (this.isSuccess()) {this.log.info(g(summary));}
else {this.log.error(r(summary));}
this.log.info("\n");
summary = "\n" + fileCount + " files, " + translationCount + " strings, " + errorsLen + " failures\n";
if (this.isSuccess()) {log.info(g(summary));}
else {log.error(r(summary));}
};

Check.prototype.run = function() {
var now = new Date();
this.startTime = now.getTime();
this.log.debug(this.log.name + ': check started at ' + now);

log[!this.sub && this.constructor === Check ? 'info' : 'debug'](
'Checking source files for errors\n' +
gr('Tip: Add this task to your continuous build.\n')
);

this.checkFiles();
this.printSummary();
var elapsed = (new Date()).getTime() - this.startTime;
log[!this.sub && this.constructor === Check ? 'info' : 'debug'](
"\nCheck finished " + (this.isSuccess() ? "" : "with errors ") + "in " + (elapsed / 1000) + " seconds\n"
);
return this.isSuccess();
};

Expand Down
89 changes: 75 additions & 14 deletions lib/commands/export.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,93 @@
var log = require('../log')('i18nline:commands:export');

var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var extend = require('extend');
var chalk = require("chalk");
var r = chalk.red, g = chalk.green, gr = chalk.gray;

var I18nline = require('../i18nline');
var Check = require('./check');
var I18nline = require('../../lib/i18nline');

var template = fs.readFileSync(path.resolve(__dirname, '../template.js')).toString();

function Export(options) {
if (options.silent) log.level = log.NONE;
Check.call(this, options);
}

Export.prototype = Object.create(Check.prototype);
Export.prototype.constructor = Export;

Export.prototype.run = function() {
this.log.debug(this.log.name + ': export', this.options)
var success = Check.prototype.run.call(this);
var locale = 'en';
var translations = {};
translations[locale] = this.translations.translations;
var now = new Date();
this.startTime = now.getTime();

var locale = this.options.defaultLocale || I18nline.config.defaultLocale;
var basePath = this.options.basePath || I18nline.config.basePath;
var outputFile = this.options.outputFile || I18nline.config.outputFile;
this.outputFile = basePath + '/' + outputFile;
mkdirp.sync(this.outputFile.replace(/\/[^\/]+$/, ''));
if (success) {
fs.writeFileSync(this.outputFile, JSON.stringify(translations, null, 2));
this.log.info("Wrote default translations to " + this.outputFile + "\n");
this.out = path.resolve(basePath, this.options.out || I18nline.config.out);
var outputFolder = path.extname(this.out) == '.json' ? path.dirname(this.out) : this.out;

log[!this.sub && this.constructor === Export ? 'info' : 'debug'](
'Exporting default translations to ' + (this.out.endsWith('.json') ? this.out : path.join(this.out, 'default.json')) + '\n'
);

Check.prototype.run.call(this);

if (this.isSuccess()) {
var translations = {};
translations[locale] = Object.keys(this.translations.translations).sort()
.reduce(function(r,k){return ((r[k] = this.translations.translations[k]) && r) || r}.bind(this), {});

try {
mkdirp.sync(outputFolder);
var def = path.extname(this.out) == '.json' ? this.out : path.resolve(outputFolder, 'default.json');

this.oldDefaults = {};
if (fs.existsSync(def)) {
try {
this.oldDefaults = JSON.parse(fs.readFileSync(def));
} catch(e) {
this.errors.push(e.message + "\n" + def);
log.error(r("ERR" + this.errors.length) + (this.errors.length < 10 ? ' ' : ' '));
}
}

if (this.isSuccess()) {
var removed = Object.keys(this.oldDefaults[locale] || {})
.filter(function(k){return !(k in translations[locale])}).length;
var added = Object.keys(translations[locale])
.filter(function(k){return !(k in (this.oldDefaults[locale] || {}))}.bind(this)).length;
var status = '';
if (added || removed) {
try {
fs.writeFileSync(def, JSON.stringify(translations, null, 2), {encoding:'utf8',flag:'w'});
status += added > 0 ? (g('+' + added) + (added < 10 ? ' ' : ' ')) : ' ';
status += removed > 0 ? (r('-' + removed) + (removed < 10 ? ' ' : ' ')) : ' ';
} catch(e) {
this.errors.push(e.message + "\n" + def);
status = r("ERR" + this.errors.length) + (this.errors.length < 10 ? ' ' : ' ');
}
log.info(status + gr(def));
}
}

for (var i=0,e; e=this.errors[i]; i++) {
log.error('\nERR' + (i+1) + '\n' + e);
}
} catch(e) {
this.errors.push(r(e.message + "\n" + def));
log.error(r("ERR" + this.errors.length) + (this.errors.length < 10 ? ' ' : ' '));
for (var i=0,e; e=this.errors[i]; i++) {
log.error('\nERR' + (i+1) + '\n' + e);
}
}
}
return success;
var elapsed = (new Date()).getTime() - this.startTime;
log[!this.sub && this.constructor === Export ? 'info' : 'debug'](
"\nExport finished " + (this.isSuccess() ? "" : "with errors ") + "in " + (elapsed / 1000) + " seconds\n"
);
return this.isSuccess();
};

module.exports = Export;
Expand Down
28 changes: 15 additions & 13 deletions lib/commands/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,35 @@ var I18nline = require('../../lib/i18nline');


function Help(options) {
if (options.silent) log.level = log.NONE;
BaseCommand.call(this, options);
}

Help.prototype = Object.create(BaseCommand.prototype);
Help.prototype.constructor = Help;

Help.prototype.run = function() {
var log = this.log;
log.info(wb('Usage'));
log.info('');
log.info(gb('i18nline <command> [options]'))
log.info('');
log.info(wb('Commands'))
log.info('');
log.info('check ' + gr('Performs a dry-run with all checks, but does not write any files'));
log.info('export ' + gr('Performs a check, then writes the default translation file'));
log.info('help ' + gr('Prints this help screen'));
log.info('check ' + gr('Performs a dry-run with all checks, but does not write any files'));
log.info('export ' + gr('Performs a check, then exports the default translation file'));
log.info('index ' + gr('Generates an index file you can import in your program'));
log.info('synch ' + gr('Synchronizes all generated files with the source code'));
log.info('help ' + gr('Prints this help screen'));
log.info('');
log.info(wb('Options'));
log.info('');
log.info(gr('You can set/override all of i18nline\'s configuration options on the command line.'));
log.info(grb('SEE: ') + g('https://github.com/download/i18nline#configuration'));
log.info(gr('In addition these extra options are available in the CLI:'));
log.info('-o ' + gr('Alias for --outputFile (SEE config docs)'));
log.info('--only ' + gr('Only process a single file/directory/pattern'));
log.info('--silent ' + gr('Don\'t log any messages'));
log.info('-s ' + gr('Alias for --silent'));
log.info(gr('In addition these extra options are available in the CLI:\n'));
log.info('-o ' + gr('Alias for --out (SEE config docs)'));
log.info('--only ' + gr('Only process a single file/directory/pattern'));
log.info('--silent ' + gr('Don\'t log any messages'));
log.info('-s ' + gr('Alias for --silent'));
log.info('');
log.info(wb('Examples'));
log.info('');
Expand All @@ -45,15 +47,15 @@ Help.prototype.run = function() {
log.info('');
log.info(gr('$ ') + 'i18nline export --directory=src --patterns=**/*.js,**/*.jsx');
log.info(gr('> Export all translations in `src` directory from .js and .jsx files'));
log.info(gr('> to default output file i18n/en.json'));
log.info(gr('> to default output file src/i18n/default.json'));
log.info('');
log.info(gr('$ ') + 'i18nline export -o=translations/en.json');
log.info(gr('$ ') + 'i18nline export -o=translations');
log.info(gr('> Export all translations in any directory but the ignored ones, from'));
log.info(gr('> .js and .jsx files to the given output file translations/en.json'));
log.info(gr('> .js and .jsx files to the given output file translations/default.json'));
log.info('');
log.info(wb('See what\'s happening'));
log.info('');
log.info(gr('i18nline uses ') + wb('ulog') + gr(' for it\'s logging. The default level is info. To change it:'));
log.info(gr('i18nline uses ') + g('ulog') + gr(' for it\'s logging. The default level is info. To change it:'));
log.info(gr('$ ') + 'LOG=debug ' + gr(' (or trace, log, info, warn, error)'));
log.info(gr('Now, i18nline will log any messages at or above the set level'));
log.info('');
Expand Down
87 changes: 87 additions & 0 deletions lib/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
var log = require('../log')('i18nline:commands:index');
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var chalk = require("chalk");
var r = chalk.red, g = chalk.green, gr = chalk.grey;

var I18nline = require('../i18nline');
var Check = require('./check');

var template = fs.readFileSync(path.resolve(__dirname, '../template.js')).toString();

function Index(options) {
if (options.silent) log.level = log.NONE;
Check.call(this, options);
}

Index.prototype = Object.create(Check.prototype);
Index.prototype.constructor = Index;

Index.prototype.run = function() {
var now = new Date();
this.startTime = now.getTime();

var basePath = this.options.basePath || I18nline.config.basePath;
this.out = path.resolve(basePath, this.options.out || I18nline.config.out);

log[!this.sub && this.constructor === Index ? 'info' : 'debug'](
'Generating index file\n' +
gr('Import the generated file into your project\n')
);

mkdirp.sync(this.out);
var supportedLocales = fs.readdirSync(this.out)
.filter(function(f){return !fs.lstatSync(path.resolve(this.out, f)).isDirectory();}.bind(this))
.filter(function(f){return path.basename(f).match(/^[a-z][a-z]_?([A-Z][A-Z])?\.json$/);})
.map(function(f){return f.substring(0, f.length - 5);});
var indexFile = path.resolve(this.out, 'index.js');
var ignoredConfigKeys = ['basePath', 'directories', 'ignoreDirectories', 'patterns', 'ignorePatterns', 'autoTranslateTags', 'neverTranslateTags', 'out', 'inferredKeyFormat', 'underscoredKeyLength', 'locales'];
var configuration = [].concat(
'I18n.supportedLocales = ' + JSON.stringify(supportedLocales).replace(/"/g, "'") + ';',
Object.keys(I18nline.config)
.filter(function(k){return ignoredConfigKeys.indexOf(k) === -1})
.map(function(k){return 'I18n.' + k + ' = ' + JSON.stringify(I18nline.config[k]).replace(/"/g, "'") + ';'})
);
if (I18nline.config.locales) configuration = configuration.concat(
Object.keys(I18nline.config.locales)
.map(function(k){return 'I18n.locales.' + k + ' = ' + JSON.stringify(I18nline.config.locales[k]).replace(/"/g, "'") + ';'})
)
var imports = supportedLocales
.map(function(l){return "case '" + l + "': return import(/* webpackChunkName: 'i18n." + l + "' */ './" + l + ".json');"});
var reloads = supportedLocales
.map(function(l){return "module.hot.accept('./" + l + ".json', I18n.reload('" + l + "'));";});
var parts = template.split(/\/\*\[[A-Z_]?[A-Z0-9_]+\]\*\//);
var script = parts[0] +
configuration.join('\n') + parts[1] +
imports.join('\n\t\t') + parts[2] +
reloads.join('\n\t') + parts[3];

// allow outside code to process the generated script
// by assigning a function to indexFilehook
if (this.indexFileHook) {
script = this.indexFileHook(script)
}

try {
fs.writeFileSync(indexFile, script, {encoding:'utf8',flag:'w'});
log.info(g('index ') + gr(indexFile));
} catch(e) {
this.errors.push(e.message + "\n" + def);
log.error(r("ERR" + this.errors.length) + (this.errors.length < 10 ? ' ' : ' ') + gr(indexFile));
}
if (this.constructor === Index) {
for (var i=0,e; e=this.errors[i]; i++) {
log.error('ERR' + (i+1) + '\n' + e);
}
}
var elapsed = (new Date()).getTime() - this.startTime;
log[!this.sub && this.constructor === Index ? 'info' : 'debug'](
"\nIndex finished " + (this.isSuccess() ? "" : "with errors ") + "in " + (elapsed / 1000) + " seconds\n"
);
return this.isSuccess();
};

module.exports = Index;

log.debug('Initialized ' + log.name);
Loading

0 comments on commit d8d4f25

Please sign in to comment.