locize-cli
Version: 
locize cli to import locales
269 lines (254 loc) • 8.27 kB
JavaScript
const flatten = require('flat');
const i18next2po = require('gettext-converter/cjs/i18next2po');
const csvjson = require('csvjson');
const xlsx = require('xlsx');
const yaml = require('yaml');
const js2asr = require('android-string-resource/cjs/js2asr');
const stringsFile = require('strings-file');
const createxliff = require('xliff/cjs/createxliff');
const createxliff12 = require('xliff/cjs/createxliff12');
const js2resx = require('resx/cjs/js2resx');
const js2ftl = require('fluent_conv/cjs/js2ftl');
const js2tmx = require('tmexchange/cjs/js2tmx');
const js2laravel = require('laravelphp/cjs/js2laravel');
const javaProperties = require('@js.properties/properties');
const unflatten = require('./unflatten');
const getRemoteNamespace = require('./getRemoteNamespace');
const removeUndefinedFromArrays = require('./removeUndefinedFromArrays');
const shouldUnflatten = require('./shouldUnflatten');
const prepareCombinedExport = require('./combineSubkeyPreprocessor').prepareExport;
const convertToDesiredFormat = (
  opt,
  namespace,
  lng,
  data,
  lastModified,
  cb
) => {
  opt.getNamespace = opt.getNamespace || getRemoteNamespace;
  const isEmpty = !data || Object.keys(data).length === 0;
  try {
    if (opt.format === 'json') {
      try {
        data = unflatten(data, true);
      } catch (err) {}
      cb(null, JSON.stringify(data, null, 2));
      return;
    }
    if (opt.format === 'nested') {
      try {
        data = unflatten(data);
      } catch (err) {}
      cb(null, JSON.stringify(data, null, 2));
      return;
    }
    if (opt.format === 'flat') {
      cb(null, JSON.stringify(flatten(data), null, 2));
      return;
    }
    if (opt.format === 'po' || opt.format === 'gettext') {
      const flatData = flatten(data);
      const gettextOpt = {
        project: 'locize',
        language: lng,
        potCreationDate: lastModified,
        poRevisionDate: lastModified,
        ctxSeparator: '_ is default but we set it to something that is never found!!!',
        persistMsgIdPlural: true
      };
      cb(null, i18next2po(lng, flatData, gettextOpt));
      return;
    }
    if (opt.format === 'po_i18next' || opt.format === 'gettext_i18next') {
      const flatData = flatten(data);
      const compatibilityJSON = !!Object.keys(flatData).find((k) => /_(zero|one|two|few|many|other)/.test(k)) && 'v4';
      const gettextOpt = {
        project: 'locize',
        language: lng,
        potCreationDate: lastModified,
        poRevisionDate: lastModified,
        compatibilityJSON
      };
      cb(null, i18next2po(lng, flatData, gettextOpt));
      return;
    }
    if (opt.format === 'csv') {
      opt.getNamespace(opt, opt.referenceLanguage, namespace, (err, refNs) => {
        if (err) return cb(err);
        const js2CsvData = Object.keys(flatten(data)).reduce((mem, k) => {
          const value = data[k] || '';
          const line = {
            // https://en.wikipedia.org/wiki/Delimiter-separated_values
            key: k.replace(/"/g, '""'),
            [opt.referenceLanguage]: refNs[k] || '',
            [lng]: value.replace(/"/g, '""')
          };
          line.key = line.key.replace(/\n/g, '\\NeWlInE\\');
          line[opt.referenceLanguage] = line[opt.referenceLanguage].replace(
            /\n/g,
            '\\NeWlInE\\'
          );
          line[lng] = line[lng].replace(/\n/g, '\\NeWlInE\\');
          mem.push(line);
          return mem;
        }, []);
        const options = {
          delimiter: ',',
          wrap: true,
          headers: 'relative'
          // objectDenote: '.',
          // arrayDenote: '[]'
        };
        cb(
          null,
          `\ufeff${csvjson
            .toCSV(js2CsvData, options)
            .replace(/\\NeWlInE\\/g, '\n')}`
        );
      });
      return;
    }
    if (opt.format === 'xlsx') {
      opt.getNamespace(opt, opt.referenceLanguage, namespace, (err, refNs) => {
        if (err) return cb(err);
        const js2XlsxData = Object.keys(flatten(data)).reduce((mem, k) => {
          const value = data[k] || '';
          const line = {
            key: k,
            [opt.referenceLanguage]: refNs[k] || '',
            [lng]: value
          };
          mem.push(line);
          return mem;
        }, []);
        const worksheet = xlsx.utils.json_to_sheet(js2XlsxData);
        const workbook = xlsx.utils.book_new();
        let workSheetName = namespace;
        if (workSheetName.length > 31) workSheetName = workSheetName.substring(0, 31);
        workbook.SheetNames.push(workSheetName);
        workbook.Sheets[workSheetName] = worksheet;
        const wbout = xlsx.write(workbook, { type: 'buffer' });
        cb(null, wbout);
      });
      return;
    }
    if (
      opt.format === 'yaml' ||
      opt.format === 'yml'
    ) {
      if (isEmpty) return cb(null, '');
      cb(null, yaml.stringify(flatten(data)));
      return;
    }
    if (
      opt.format === 'yaml-nested' ||
      opt.format === 'yml-nested'
    ) {
      if (isEmpty) return cb(null, '');
      cb(null, yaml.stringify(shouldUnflatten(data) ? unflatten(data) : data));
      return;
    }
    if (
      opt.format === 'yaml-rails' ||
      opt.format === 'yml-rails'
    ) {
      if (isEmpty) return cb(null, '');
      var newData = {};
      newData[lng] = shouldUnflatten(data) ? unflatten(data) : data;
      cb(null, yaml.stringify(removeUndefinedFromArrays(newData)));
      return;
    }
    if (
      opt.format === 'yaml-rails-ns' ||
      opt.format === 'yml-rails-ns'
    ) {
      if (isEmpty) return cb(null, '');
      var newDataNs = {};
      newDataNs[lng] = {};
      newDataNs[lng][namespace] = shouldUnflatten(data) ? unflatten(data) : data;
      cb(null, yaml.stringify(removeUndefinedFromArrays(newDataNs)));
      return;
    }
    if (opt.format === 'android') {
      js2asr(flatten(data), cb);
      return;
    }
    if (opt.format === 'strings') {
      Object.keys(data).forEach((k) => {
        if (data[k] === null) delete data[k];
      });
      data = stringsFile.compile(data);
      cb(null, data);
      return;
    }
    if (
      opt.format === 'xliff2' ||
      opt.format === 'xliff12' ||
      opt.format === 'xlf2' ||
      opt.format === 'xlf12'
    ) {
      const fn =
        opt.format === 'xliff12' || opt.format === 'xlf12'
          ? createxliff12
          : createxliff;
      opt.getNamespace(opt, opt.referenceLanguage, namespace, (err, refNs) => {
        if (err) return cb(err);
        const prepared = prepareCombinedExport(refNs, flatten(data));
        fn(opt.referenceLanguage, lng, prepared.ref, prepared.trg, namespace, cb);
      });
      return;
    }
    if (opt.format === 'resx') {
      js2resx(flatten(data), cb);
      return;
    }
    if (opt.format === 'fluent') {
      Object.keys(data).forEach((k) => {
        if (!data[k] || data[k] === '') delete data[k];
        data[k] = data[k].replace(
          new RegExp(String.fromCharCode(160), 'g'),
          String.fromCharCode(32)
        );
      });
      js2ftl(unflatten(data), cb);
      return;
    }
    if (opt.format === 'tmx') {
      opt.getNamespace(opt, opt.referenceLanguage, namespace, (err, refNs) => {
        if (err) return cb(err);
        const js = flatten(data);
        const js2TmxData = Object.keys(js).reduce(
          (mem, k) => {
            const refItem = refNs[k];
            if (!refItem) return mem;
            const value = js[k] || '';
            mem.resources[namespace][k] = {};
            mem.resources[namespace][k][opt.referenceLanguage] = refItem;
            mem.resources[namespace][k][lng] = value;
            return mem;
          },
          {
            resources: {
              [namespace]: {}
            },
            sourceLanguage: opt.referenceLanguage
          }
        );
        js2tmx(js2TmxData, cb);
      });
      return;
    }
    if (opt.format === 'laravel') {
      js2laravel(unflatten(data), cb);
      return;
    }
    if (opt.format === 'properties') {
      cb(null, javaProperties.stringifyFromProperties(data, { eol: '\n' }));
      return;
    }
    cb(new Error(`${opt.format} is not a valid format!`));
  } catch (err) {
    cb(err);
  }
};
module.exports = convertToDesiredFormat;