UNPKG

deepl-po-translate

Version:

Sponsor: https://ora.pm/ - Best agile project management, time tracking with native git integration platform for developers # Translate a .PO file using DeepL API .po file with no translation => .po file with translation! Translation is autosaved each 30

159 lines (137 loc) 5.9 kB
const inquirer = require('inquirer') const gettextParser = require("gettext-parser") const { readFileSync, writeFileSync, existsSync } = require('fs') const { program } = require('commander') const { DeepL } = require('./deepl') const ConsoleProgressBar = require('console-progress-bar') const noop = () => {} require('dotenv').config() function welcome() { console.log('Translate your app/site or service .po files? Read this https://github.com/uffou/deepl-po-translate for more info' ); console.log('------------------'); }; program .option('-d, --debug', 'output extra debugging') .option('-p, --paid-api <paidApi>', 'if to use paid api') .option('-k, --auth-key <auth_key>', 'auth_key') .option('-f, --po-file-path <po_file_path>', '.po File Path') .option('-d, --po-directory-path <po_directory_path>', '.po Folders Path') .option('-l, --target-lang <target_lang>', 'Target language') program.parse(process.argv); const options = program.opts(); let answers if (options.debug) console.log(options); if (options.api) console.log(options.api); if (options.authKey) console.log(options.authKey); if (options.targetLang) console.log(options.targetLang); if (options.poFilePath) console.log(options.poFilePath); if (options.poDirectoryPath) console.log(options.poDirectoryPath); const debug = options.debug ? console.log : noop welcome() if(options.authKey || options.poFilePath || options.poDirectoryPath){ parseAndTranslate() }else { inquirer.prompt([ { type: 'list', message: "Free or paid api?", name:'paidApi', choices:[ "DeepL Free API (api-free.deepl.com)", "DeepL Paid API (api.deepl.com)" ]}, { type:'input', message: 'auth_key', name: 'authKey', }, { type:'input', message: 'Source File Path', name: 'poFilePath', }, { type:'input', message: 'Target language', name: 'targetLang', }, ]).then(answers => { answers = answers if(answers) console.log(answers) parseAndTranslate() }) } function escapeString(string){ return string.replace(/{(.*)}/gm, '<NoTrans>$&</NoTrans>') } function unescapeString(string){ return string.replace(/<NoTrans>/gm, '').replace(/<\/NoTrans>/gm, '') } async function parseAndTranslate(answers){ const cwd = process.cwd() const poDirectoryPath = options.poDirectoryPath || (answers && answers.poDirectoryPath) const targetLang = options.targetLang || process.env.poTransTargetLang if(!targetLang) throw 'Target lang not provided. use --target-lang or .env.poTransTargetLang' const languages = targetLang.includes(',') ? targetLang.split(",") : [targetLang] for(const language of languages){ const poFilePath = poDirectoryPath ? poDirectoryPath + (poDirectoryPath.endsWith("/") ? '' : "/") + language + "/messages.po" : options.poFilePath || (answers && answers.poFilePath) if(!existsSync(poFilePath)) { console.log('poFilePath: ', poFilePath) throw 'PO file not found!' } if(!(options.authKey || process.env.deeplAuthKey)) throw 'authKey not provided. use --auth-key or .env.deeplAuthKey' const free = !options.paidApi const deepl = new DeepL(options.authKey || process.env.deeplAuthKey, language, free) var input = readFileSync(poFilePath) var po = gettextParser.po.parse(input) const transObject = po.translations[''] let keysWithoutTranslation = [] Object.keys(transObject).forEach(key => { //TODO: handle multiline texts if(transObject[key].msgstr[0] === '') { keysWithoutTranslation.push(key) debug('\x1b[33m\x1b[0m',transObject[key].msgstr) } }) if(keysWithoutTranslation.length === 0) { console.log('All strings translated', language.toUpperCase()) continue } console.log('Translating:', keysWithoutTranslation.length, ' strings') function saveFile(){ const output = gettextParser.po.compile(po) writeFileSync(poFilePath, output) } // keysWithoutTranslation = keysWithoutTranslation.slice(0,10) // TESTING const bar = new ConsoleProgressBar({ maxValue: keysWithoutTranslation.length }) index = 0 for(const key of keysWithoutTranslation){ index += 1 const sourceString = escapeString(transObject[key].msgid) const data = await deepl.translate(sourceString).catch(error => { if (error.response) { // Request made and server responded console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // The request was made but no response was received console.log(error.request); } else { // Something happened in setting up the request that triggered an Error console.log('Error', error.message); } deepl.usage() }) // console.log(data) // TODO HANDLE ARRAYS transObject[key].msgstr[0] = unescapeString(data.translations[0].text) debug('\x1b[32m%s\x1b[0m',transObject[key].msgstr[0]) bar.addValue(1) if(index % 20 === 0) { saveFile() // saving each 20 api calls just in case } } saveFile() console.log(`\n${language.toUpperCase()} Translated & Saved...\n`) deepl.usage() } }