i18nucleus-transifex
Version:
Transifex translation provider middleware for i18nucleus
319 lines (289 loc) • 10.3 kB
JavaScript
var pjson = require('./package.json')
, path = require('path')
, os = require('os')
, Transifex = require('transifex')
, async = require('breeze-async')
, request = require('request')
, _ = require('underscore')
, countryLanguage = require('country-language')
, flat = require('flat')
, fs = require('node-fs')
, rmdir = require('rimraf')
, colors = require('colors')
, defineOptions = require('./options')
, baseUrl = 'https://www.transifex.com/api/2/'
, transifex = null;
_.mixin(require('underscore.string'))
function i18nucleus_transifex () {
var version = pjson.version
, type = 'translationProvider'
, name = pjson.name
, options = {}
, utils = null
, parentOptions = null
, MwareError = null
, namespaces = []
, authHeader = '';
function setOptions (_options, _utils) {
utils = _utils;
var optionObj = defineOptions(_options, utils);
if (!optionObj.err) {
options = optionObj.options;
}
return optionObj;
}
function init (_parentOptions, _MwareError, cb) {
var credential = options.username + ':' + options.password;
parentOptions = _parentOptions;
MwareError = _MwareError;
namespaces = parentOptions.namespaces;
transifex = new Transifex({
project_slug: options.projectSlug
, credential: credential
});
authHeader = 'Basic ' + new Buffer(credential).toString('base64');
cb();
}
function adaptNsForTransifex (ns) {
var trimChars = [ '\\.' ] // TODO: add other charapters need to be stripped
, reg = null
, res = ns;
trimChars.forEach(function (chr) {
reg = new RegExp(chr, 'g');
res = res.replace(reg, '');
});
return res;
}
function getLocalesSummary (cb) {
var defaultLng = parentOptions.defaultLng
, languagesInfo = []
, countriesInfo = []
, resObj = [];
function pushToResArray (language, data) {
var langInfo = _.find(languagesInfo, function (lInfo) { return lInfo.locale == language }).language
, countryInfo = _.find(countriesInfo, function (lInfo) { return lInfo.locale == language }).country
resObj.push({
locale: language
, region: countryInfo.name
, name: {
local: langInfo.nativeName[0]
, english: langInfo.name[0]
}
, translatedTerms: data[language].translated_entities
, totalTerms: data[language].translated_entities + data[language].untranslated_entities
, is_base_language: language == defaultLng
});
}
function nsIterrator (ns, next) {
var nsSlug = adaptNsForTransifex(ns)
, res = {}
, lngCountryPairs = [];
transifex.statisticsMethods(options.projectSlug, nsSlug, function (err, data) {
if (err) {
if (_.endsWith(err.message, 'returned 404')) { // namespace does not exist yet on Transifex
next();
} else {
next(err);
}
} else {
if (resObj.length == 0) {
var lngCountryPairs = utils.splitLocalesTo_lngCountries(_.keys(data));
languagesInfo = lngCountryPairs[0]
countriesInfo = lngCountryPairs[1]
pushToResArray(defaultLng, data);
_.keys(data).forEach(function (locale) {
if (locale != defaultLng) {
pushToResArray(locale, data);
}
});
} else {
_.keys(data).forEach(function (locale) {
res = _.find(resObj, function (item) { return item.locale == locale });
res.translatedTerms += data[locale].translated_entities;
res.totalTerms += data[locale].translated_entities + data[locale].untranslated_entities;
res.completeness = parseInt(res.translatedTerms / res.totalTerms * 1000) / 10;
});
}
next();
}
});
}
function finish (err) {
if (err) {
return cb(new MwareError(err, name));
} else {
resObj.forEach(function (res) {
delete res.translatedTerms;
delete res.totalTerms;
});
return cb(null, resObj);
}
}
async.forEachSeries(
namespaces
, nsIterrator
, finish
);
}
function getTranslations (locale, cb) {
var totalTerms = 0
, retObj = { translations: {} }
, missedNss = [];
function nsIterrator (ns, next) {
var nsSlug = adaptNsForTransifex(ns);
function getTermsNumber (next_int) {
var translatedTerms = [];
transifex.translationStringsMethod(options.projectSlug, nsSlug, locale, function (err, data) {
if (err) {
if (_.endsWith(err.message, 'returned 404')) {
missedNss.push(ns);
next();
} else {
next_int(err);
}
} else {
data = JSON.parse(data);
translatedTerms = _.filter(data, function (term) { return term.translation != '' });
totalTerms += translatedTerms.length;
next_int(null, true);
}
});
}
function getTerms (next_int) {
transifex.translationInstanceMethod(options.projectSlug, nsSlug, locale, function (err, data) {
if (err) {
next_int(err);
} else {
retObj.translations[ns] = JSON.parse(data);
next_int(null, true);
}
});
}
async.series([
getTermsNumber
, getTerms
], next);
}
function finish (err) {
if (err) {
return cb(new MwareError(err, name));
} else {
retObj.totalTerms = totalTerms;
if (missedNss.length) {
retObj.missingNamespaces = missedNss;
}
return cb(null, retObj);
}
}
async.forEachSeries(
namespaces
, nsIterrator
, finish
);
}
function updateResources (resources, cb) {
var totalTerms = 0
, totalAdded = 0
, totalDeleted = 0;
function resIterrator (resource, next) {
if (options.ignoredNamespaces.indexOf(resource.namespace) == -1) {
var ns = resource.namespace
, nsSlug = adaptNsForTransifex(ns)
, resourceData = resource.resources
, resourceDataStr = JSON.stringify(resource.resources)
, rand = Math.floor(Math.random() * 1000000000)
, environment = process.env.NODE_ENV
, tmpFilePath = ((environment == 'development') ? path.dirname(require.main.filename) + '/tmp' : os.tmpdir()) + '/' + rand
, filePath = tmpFilePath + '/' + ns + '.json'
, url = baseUrl + 'project/' + options.projectSlug + '/resource/' + nsSlug + '/content/'
, requestParams = {
url: url
, options: {}
, headers: {
'Authorization': authHeader
}
}
fs.mkdir(tmpFilePath, 0777, true, function (err) {
if (err) {
return next(err);
} else {
fs.writeFile(filePath, resourceDataStr, { mode: 777 }, function (err) {
if (err) {
return next(err);
} else {
var r = request.put(requestParams, function (err, incomingMsg, response) {
if (err) {
return next(err);
} else if (response.indexOf('Not Found') > -1) {
requestParams.url = baseUrl + 'project/' + options.projectSlug + '/resources/';
delete requestParams.options;
var r2 = request.post(requestParams, function (err, incomingMsg, response) {
if (err) {
return next(err);
} else {
rmdir(tmpFilePath, function (err) {
if (err) {
console.log(('WARNING: Resources temp file ' + filePath + ' could not be deleted.').magenta);
}
});
var flatResources = flat.flatten(resourceData);
totalAdded += _.size(flatResources);
totalTerms += _.size(flatResources);
next();
}
});
var form2 = r2.form();
form2.append('slug', nsSlug);
form2.append('name', ns);
form2.append('accept_translations', 'true');
form2.append('i18n_type', 'KEYVALUEJSON');
form2.append('content', fs.createReadStream(filePath));
} else {
var responseObj = JSON.parse(response);
totalAdded += responseObj.strings_added + responseObj.strings_updated;
totalDeleted += responseObj.strings_delete;
rmdir(tmpFilePath, function (err) {
if (err) {
console.log(('WARNING: Resources temp file ' + filePath + ' could not be deleted.').magenta);
}
});
var flatResources = flat.flatten(resourceData);
totalTerms += _.size(flatResources);
next();
}
});
var form = r.form();
form.append('content', fs.createReadStream(filePath));
}
});
}
});
}
}
function finish (err) {
if (err) {
return cb(new MwareError(err, name));
} else {
console.log(('Resource strings updated in Transifex. Strings added/updated: ' + totalAdded + ', strings deleted: ' + totalDeleted).green);
cb(null, { totalTerms: totalTerms });
}
}
async.forEach(
resources
, resIterrator
, finish
);
}
return {
version: version
, type: type
, name: name
, setOptions: setOptions
, init: init
, options: options
, getLocalesSummary: getLocalesSummary
, getTranslations: getTranslations
, updateResources: updateResources
}
}
module.exports = i18nucleus_transifex;