| LEFT | RIGHT | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <https://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-present eyeo GmbH |    3  * Copyright (C) 2006-present eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|    6  * it under the terms of the GNU General Public License version 3 as |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
|   21  |   21  | 
|   22 const fs = require("fs"); |   22 const fs = require("fs"); | 
|   23 const path = require("path"); |   23 const path = require("path"); | 
|   24 const csv = require("csv"); |   24 const csv = require("csv"); | 
|   25 const {promisify} = require("util"); |   25 const {promisify} = require("util"); | 
|   26 const execFile = promisify(require("child_process").execFile); |   26 const execFile = promisify(require("child_process").execFile); | 
|   27 const csvParser = promisify(csv.parse); |   27 const csvParser = promisify(csv.parse); | 
|   28 const readFile = promisify(fs.readFile); |   28 const readFile = promisify(fs.readFile); | 
|   29 const writeFile = promisify(fs.writeFile); |   29 const writeFile = promisify(fs.writeFile); | 
|   30 const glob = promisify(require("glob").glob); |   30 const glob = promisify(require("glob").glob); | 
|   31 const readJsonPromised = promisify(readJson); |  | 
|   32  |   31  | 
|   33 const localesDir = "locale"; |   32 const localesDir = "locale"; | 
|   34 const defaultLocale = "en_US"; |   33 const defaultLocale = "en_US"; | 
|   35  |   34  | 
|   36 const headers = ["Filename", "StringID", "Description", "Placeholders", |   35 const headers = ["Filename", "StringID", "Description", "Placeholders", | 
|   37                  defaultLocale]; |   36                  defaultLocale]; | 
|   38 let outputFileName = "translations.csv"; |   37 let outputFileName = "translations.csv"; | 
|   39  |   38  | 
|   40 /** |   39 /** | 
|   41  * Export existing translation - files into CSV file |   40  * Export existing translation - files into CSV file | 
|   42  */ |   41  */ | 
|   43 function exportTranslations() |   42 function exportTranslations() | 
|   44 { |   43 { | 
|   45   glob(`${localesDir}/**/*.json`).then((filePaths) => |   44   glob(`${localesDir}/**/*.json`).then((filePaths) => | 
|   46   { |   45   { | 
|   47     // Reading all existing translations files |   46     // Reading all existing translations files | 
|   48     return Promise.all(filePaths.map((filePath) => readJsonPromised(filePath))); |   47     return Promise.all(filePaths.map((filePath) => readJson(filePath))); | 
|   49   }).then(csvFromJsonFileObjects); |   48   }).then(csvFromJsonFileObjects); | 
|   50 } |   49 } | 
|   51  |   50  | 
|   52 /** |   51 /** | 
|   53  * Creating Matrix which reflects output CSV file |   52  * Creating Matrix which reflects output CSV file | 
|   54  * @param  {Object[]} fileObjects - array of file objects created by readJson |   53  * @param  {Object[]} fileObjects - array of file objects created by readJson | 
|   55  */ |   54  */ | 
|   56 function csvFromJsonFileObjects(fileObjects) |   55 function csvFromJsonFileObjects(fileObjects) | 
|   57 { |   56 { | 
|   58   const locales = []; |   57   const locales = []; | 
|   59   const fileNames = []; |   58   const fileNames = []; | 
|   60   // Create Object tree from the Objects array, for easier search |   59   // Create Object tree from the Objects array, for easier search | 
|   61   // ex.: {dektop-options.json: {en_US: {...}, {de: {...}, {ru: {...}}} |   60   // ex.: {dektop-options.json: {en_US: {...}, {de: {...}, {ru: {...}}} | 
|   62   const dataTreeObj = Object.create(null); |   61   const dataTreeObj = Object.create(null); | 
|   63   for (const fileObject of fileObjects) |   62   for (const fileObject of fileObjects) | 
|   64   { |   63   { | 
|   65     const {fileName, locale, strings} = fileObject; |   64     const {fileName, locale, strings} = fileObject; | 
|   66  |   65  | 
|   67     if (locale != defaultLocale && !locales.includes(locale)) |   66     if (locale != defaultLocale && !locales.includes(locale)) | 
|   68       locales.push(locale); |   67       locales.push(locale); | 
|   69  |   68  | 
|   70     if (!filesFilter.length || filesFilter.includes(fileName)) |   69     if ((!filesFilter.length || filesFilter.includes(fileName)) && | 
 |   70          !fileNames.includes(fileName)) | 
|   71       fileNames.push(fileName); |   71       fileNames.push(fileName); | 
|   72  |   72  | 
|   73     if (!(fileName in dataTreeObj)) |   73     if (!(fileName in dataTreeObj)) | 
|   74       dataTreeObj[fileName] = Object.create(null); |   74       dataTreeObj[fileName] = Object.create(null); | 
|   75  |   75  | 
|   76     dataTreeObj[fileName][locale] = strings; |   76     dataTreeObj[fileName][locale] = strings; | 
|   77   } |   77   } | 
|   78   // Create two dimensional strings array that reflects CSV structure |   78   // Create two dimensional strings array that reflects CSV structure | 
|   79   const csvArray = [headers.concat(locales)]; |   79   const csvArray = [headers.concat(locales)]; | 
|   80   for (const fileName of fileNames) |   80   for (const fileName of fileNames) | 
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  218     }).catch((error) => |  218     }).catch((error) => | 
|  219     { |  219     { | 
|  220       console.error(error); |  220       console.error(error); | 
|  221     }); |  221     }); | 
|  222   }); |  222   }); | 
|  223 } |  223 } | 
|  224  |  224  | 
|  225 /** |  225 /** | 
|  226  * Reads JSON file and assign filename and locale to it |  226  * Reads JSON file and assign filename and locale to it | 
|  227  * @param {string} filePath - ex.: "locales/en_US/desktop-options.json" |  227  * @param {string} filePath - ex.: "locales/en_US/desktop-options.json" | 
|  228  * @param {function} callback - fileName, locale and strings of locale file |  228  * @returns {Promise<Object>} resolves fileName, locale and strings of the | 
|  229  *                   Parameters: |  229  *                            locale file | 
|  230  *                     * Error message |  230  */ | 
|  231  *                     * Object containing fileName, locale and strings |  231 function readJson(filePath) | 
|  232  */ |  232 { | 
|  233 function readJson(filePath, callback) |  233   return readFile(filePath, "utf8").then((data) => | 
|  234 { |  234   { | 
|  235   const {dir, base} = path.parse(filePath); |  235     const {dir, base} = path.parse(filePath); | 
|  236   readFile(filePath, "utf8").then((data) => |  | 
|  237   { |  | 
|  238     const locale = dir.split(path.sep).pop(); |  236     const locale = dir.split(path.sep).pop(); | 
|  239     const strings = JSON.parse(data); |  237     const strings = JSON.parse(data); | 
|  240     callback(null, {fileName: base, locale, strings}); |  238     return {fileName: base, locale, strings}; | 
|  241   }).catch((err) => |  | 
|  242   { |  | 
|  243     callback(err); |  | 
|  244   }); |  239   }); | 
|  245 } |  240 } | 
|  246  |  241  | 
|  247 /** |  242 /** | 
|  248  * Exit process and log error message |  243  * Exit process and log error message | 
|  249  * @param {String} error error message |  244  * @param {String} error error message | 
|  250  */ |  245  */ | 
|  251 function exitProcess(error) |  246 function exitProcess(error) | 
|  252 { |  247 { | 
|  253   console.error(error); |  248   console.error(error); | 
|  254   process.exit(1); |  249   process.exit(1); | 
|  255 } |  250 } | 
|  256  |  251  | 
|  257 // CLI |  252 // CLI | 
|  258 const helpText = ` |  253 const helpText = ` | 
|  259 About: Converts locale files between CSV and JSON formats |  254 About: Converts locale files between CSV and JSON formats | 
|  260 Usage: csv-export.js [option] [argument] |  255 Usage: csv-export.js [option] [argument] | 
|  261 Options: |  256 Options: | 
|  262   -f [FILENAME]         Name of the files to be exported ex.: -f firstRun.json |  257   -f [FILENAME]         Name of the files to be exported ex.: -f firstRun.json | 
|  263                         option can be used multiple times. |  258                         option can be used multiple times. | 
|  264                         If omitted all files are being exported |  259                         If omitted all files are being exported | 
|  265  |  260  | 
|  266   -o [FILENAME]         Output filename ex.: |  261   -o [FILENAME]         Output filename ex.: | 
|  267                         -f firstRun.json -o {hash}-firstRun.csv |  262                         -f firstRun.json -o firstRun.csv | 
|  268                         Placeholders: |  | 
|  269                           {hash} - Mercurial current revision hash |  | 
|  270                         If omitted the output fileName is set to |  263                         If omitted the output fileName is set to | 
|  271                         translations-{repo}-{hash}.csv |  264                         translations.csv | 
|  272  |  265  | 
|  273   -i [FILENAME]         Import file path ex: -i issue-reporter.csv |  266   -i [FILENAME]         Import file path ex: -i issue-reporter.csv | 
|  274 `; |  267 `; | 
|  275  |  268  | 
|  276 const argv = process.argv.slice(2); |  269 const argv = process.argv.slice(2); | 
|  277 let stopExportScript = false; |  270 let stopExportScript = false; | 
|  278 // Filter to be used export to the fileNames inside |  271 // Filter to be used export to the fileNames inside | 
|  279 const filesFilter = []; |  272 const filesFilter = []; | 
|  280  |  273  | 
|  281 for (let i = 0; i < argv.length; i++) |  274 for (let i = 0; i < argv.length; i++) | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
|  307       } |  300       } | 
|  308       const importFile = argv[i + 1]; |  301       const importFile = argv[i + 1]; | 
|  309       importTranslations(importFile); |  302       importTranslations(importFile); | 
|  310       stopExportScript = true; |  303       stopExportScript = true; | 
|  311       break; |  304       break; | 
|  312   } |  305   } | 
|  313 } |  306 } | 
|  314  |  307  | 
|  315 if (!stopExportScript) |  308 if (!stopExportScript) | 
|  316   exportTranslations(filesFilter); |  309   exportTranslations(filesFilter); | 
| LEFT | RIGHT |