locize-cli
Version:
locize cli to import locales
204 lines (190 loc) • 10.4 kB
JavaScript
const fs = require('fs');
const path = require('path');
const async = require('async');
const { mkdirp } = require('mkdirp');
const convertToFlatFormat = require('./convertToFlatFormat');
const formats = require('./formats');
const fileExtensionsMap = formats.fileExtensionsMap;
const acceptedFileExtensions = formats.acceptedFileExtensions;
const xcstrings2locize = require('locize-xcstrings/cjs/xcstrings2locize');
const getFiles = (srcpath) => {
return fs.readdirSync(srcpath).filter((file) => {
return !fs.statSync(path.join(srcpath, file)).isDirectory();
}).filter((file) => acceptedFileExtensions.indexOf(path.extname(file)) > -1);
};
const getDirectories = (srcpath) => {
return fs.readdirSync(srcpath).filter((file) => {
return fs.statSync(path.join(srcpath, file)).isDirectory();
});
};
const parseLocalLanguage = (opt, lng, cb) => {
const hasNamespaceInPath = opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) > -1;
const filledLngMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, opt.format === 'xcstrings' ? '' : lng);
var firstPartLngMask, lastPartLngMask;
if (opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`) > opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`)) {
const secondPartMask = opt.pathMask.substring(opt.pathMask.lastIndexOf(path.sep) + 1);
firstPartLngMask = secondPartMask.substring(0, secondPartMask.indexOf(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`));
lastPartLngMask = secondPartMask.substring(secondPartMask.indexOf(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`) + `${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`.length);
}
var lngPath;
if (filledLngMask.lastIndexOf(path.sep) > 0) {
lngPath = filledLngMask.substring(0, filledLngMask.lastIndexOf(path.sep));
}
if (!opt.dry && lngPath && lngPath.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) < 0) mkdirp.sync(path.join(opt.path, lngPath));
var files = [];
try {
if (lngPath) {
if (lngPath.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) > -1) {
const firstPart = lngPath.substring(0, lngPath.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`));
const lastPart = lngPath.substring(lngPath.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) + `${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`.length);
var additionalSubDirsLeft = '';
var additionalSubDirs = '';
var splittedP = lngPath.split(path.sep);
const foundSplitted = splittedP.find((s) => s.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) > -1);
const foundSplittedIndex = splittedP.indexOf(foundSplitted);
if (splittedP.length > 2) {
additionalSubDirsLeft = splittedP.slice(0, foundSplittedIndex).join(path.sep);
additionalSubDirs = splittedP.slice(foundSplittedIndex + 1).join(path.sep);
}
var dirs = getDirectories(path.join(opt.path, additionalSubDirsLeft));
if (additionalSubDirs === '') {
dirs = dirs.filter((d) => d.startsWith(firstPart) && d.endsWith(lastPart));
}
dirs.forEach((d) => {
if (additionalSubDirs && fs.statSync(path.join(opt.path, additionalSubDirsLeft, d)).isDirectory()) {
var directoryExists = false;
try {
directoryExists = fs.statSync(path.join(opt.path, additionalSubDirsLeft, d, additionalSubDirs)).isDirectory();
} catch (e) {}
if (directoryExists) {
var subFls = getFiles(path.join(opt.path, additionalSubDirsLeft, d, additionalSubDirs));
if (firstPartLngMask || lastPartLngMask) subFls = subFls.filter((f) => path.basename(f, path.extname(f)) === `${firstPartLngMask}${lng}${lastPartLngMask}`);
subFls = subFls.filter((f) => {
const a = path.join(additionalSubDirsLeft, d, additionalSubDirs, path.basename(f, path.extname(f)));
const startIndexOfNs = filledLngMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`);
if (startIndexOfNs === -1) return true;
const afterNs = filledLngMask.substring(startIndexOfNs + `${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`.length);
const nsName = a.substring(startIndexOfNs, a.indexOf(afterNs));
const b = filledLngMask.replace(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`, nsName);
return a === b;
});
files = files.concat(subFls.map((f) => `${additionalSubDirsLeft ? additionalSubDirsLeft + path.sep : ''}${d}${path.sep}${additionalSubDirs}${path.sep}${f}`));
}
} else {
const fls = getFiles(path.join(opt.path, additionalSubDirsLeft, d)).filter((f) => path.basename(f, path.extname(f)) === `${firstPartLngMask}${lng}${lastPartLngMask}`);
files = files.concat(fls.map((f) => `${additionalSubDirsLeft ? additionalSubDirsLeft + path.sep : ''}${d}${path.sep}${f}`));
}
});
} else {
files = getFiles(path.join(opt.path, lngPath));
}
} else {
files = getFiles(opt.path);
// filter lng files...
const lngIndex = filledLngMask.indexOf(lng);
const lngLeftLength = filledLngMask.length - lngIndex;
files = files.filter((f) => { // {{language}} can be left or right of {{namespace}}
if (opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`) < opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`)) {
return f.indexOf(lng) === lngIndex;
}
return (path.basename(f, path.extname(f)).length - f.indexOf(lng)) === lngLeftLength;
});
}
} catch (err) {}
async.map(files, (file, clb) => {
var dirPath;
if (file.lastIndexOf(path.sep) > 0) {
dirPath = file.substring(0, file.lastIndexOf(path.sep));
file = file.substring(file.lastIndexOf(path.sep) + 1);
}
const fExt = path.extname(file);
var namespace = path.basename(file, fExt);
const nsMask = `${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`;
const filledNsMask = lngPath && lngPath.indexOf(nsMask) > -1 ? filledLngMask : filledLngMask.substring(filledLngMask.lastIndexOf(path.sep) + 1);
const startNsIndex = filledNsMask.indexOf(nsMask);
var restNsMask = filledNsMask.substring((startNsIndex || 0) + nsMask.length);
namespace = namespace.substring(startNsIndex || 0, namespace.lastIndexOf(restNsMask));
if (lngPath && lngPath.indexOf(nsMask) > -1) {
restNsMask = restNsMask.substring(0, restNsMask.lastIndexOf(path.sep));
if (dirPath.indexOf(restNsMask) > 0) {
namespace = dirPath.substring(filledNsMask.indexOf(nsMask), dirPath.indexOf(restNsMask));
} else {
namespace = dirPath.substring(filledNsMask.indexOf(nsMask));
}
} else if (!hasNamespaceInPath && startNsIndex < 0) {
namespace = opt.namespace;
}
var fPath = path.join(opt.path, lngPath || '', file);
if (dirPath && lngPath.indexOf(nsMask) > -1) {
fPath = path.join(opt.path, dirPath.replace(nsMask, namespace), file);
}
if (!namespace) return clb(new Error(`namespace could not be found in ${fPath}`));
if (opt.namespaces && opt.namespaces.indexOf(namespace) < 0) return clb(null);
if (opt.namespace && opt.namespace !== namespace) return clb(null);
fs.readFile(fPath, (err, data) => {
if (err) return clb(err);
if (fileExtensionsMap[fExt].indexOf(opt.format) < 0) {
return clb(new Error(`Format mismatch! Found ${fileExtensionsMap[fExt][0]} but requested ${opt.format}!`));
}
if (opt.namespace) {
let hasNamespaceInPathPask = !opt.pathMask || !opt.pathMaskInterpolationPrefix || !opt.pathMaskInterpolationSuffix;
hasNamespaceInPathPask = !hasNamespaceInPathPask && opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) > -1;
if (!hasNamespaceInPathPask && namespace === lng) {
namespace = opt.namespace;
}
}
if (opt.format === 'xcstrings') { // 1 file per namespace including all languages
try {
const content = xcstrings2locize(data);
fs.stat(fPath, (err, stat) => {
if (err) return clb(err);
clb(null, Object.keys(content.resources).map((l) => ({
namespace: namespace,
path: fPath,
extension: fExt,
content: content.resources[l],
language: l,
mtime: stat.mtime
})));
});
} catch (e) {
err.message = 'Invalid content for "' + opt.format + '" format!\n' + (err.message || '');
err.message += '\n' + fPath;
return clb(err);
}
} else { // 1 file per namespace/lng
convertToFlatFormat(opt, data, lng, (err, content) => {
if (err) {
err.message = 'Invalid content for "' + opt.format + '" format!\n' + (err.message || '');
err.message += '\n' + fPath;
return clb(err);
}
fs.stat(fPath, (err, stat) => {
if (err) return clb(err);
clb(null, {
namespace: namespace,
path: fPath,
extension: fExt,
content: content,
language: lng,
mtime: stat.mtime
});
});
});
}
});
}, (err, ret) => {
if (err) return cb(err);
// xcstrings, returns array in array
const r = ret.filter((r) => r !== undefined).reduce((prev, cur) => {
if (Array.isArray(cur)) {
prev = prev.concat(cur);
} else {
prev.push(cur);
}
return prev;
}, []);
cb(null, r);
});
};
module.exports = parseLocalLanguage;