diff --git a/.gitignore b/.gitignore index 73f63fc..b258c97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist/ -node_modules \ No newline at end of file +node_modules +.env \ No newline at end of file diff --git a/package.json b/package.json index 46f4149..9120627 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "lint": "eslint --ext .js common/ && pnpm --filter injector lint && pnpm --filter preload lint && pnpm --filter renderer lint-js", "test": "mocha --require @babel/register --recursive \"./tests/renderer/*.js\"", "dist": "pnpm run build-prod && node scripts/pack.js", - "api": "jsdoc -X -r renderer/src/modules/api/ > jsdoc-ast.json" + "api": "jsdoc -X -r renderer/src/modules/api/ > jsdoc-ast.json", + "translations": "node -r dotenv/config scripts/translations.js" }, "devDependencies": { "asar": "^3.2.0", + "dotenv": "^16.0.3", "eslint": "^8.23.0", "eslint-plugin-react": "^7.31.6", "mocha": "^10.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a001d9..5841966 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ importers: .: specifiers: asar: ^3.2.0 + dotenv: ^16.0.3 eslint: ^8.23.0 eslint-plugin-react: ^7.31.6 mocha: ^10.0.0 @@ -12,6 +13,7 @@ importers: webpack-cli: ^4.10.0 devDependencies: asar: 3.2.0 + dotenv: 16.0.3 eslint: 8.23.0 eslint-plugin-react: 7.31.6_eslint@8.23.0 mocha: 10.0.0 @@ -2232,6 +2234,11 @@ packages: esutils: 2.0.3 dev: true + /dotenv/16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + dev: true + /electron-to-chromium/1.4.185: resolution: {integrity: sha512-9kV/isoOGpKkBt04yYNaSWIBn3187Q5VZRtoReq8oz5NY/A4XmU6cAoqgQlDp7kKJCZMRjWZ8nsQyxfpFHvfyw==} dev: true diff --git a/scripts/translations.js b/scripts/translations.js new file mode 100644 index 0000000..6a0a345 --- /dev/null +++ b/scripts/translations.js @@ -0,0 +1,113 @@ +const fs = require("fs"); +const path = require("path"); +const localeFolder = path.join(__dirname, "..", "assets", "locales"); + +const https = require("https"); +const qs = require("querystring"); + +const get = (opts, postData) => { + if (postData) { + postData = qs.stringify(postData); + opts.headers = { + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": postData.length + }; + } + + if (!opts.method) opts.method = "GET"; + return new Promise(resolve => { + const req = https.request(opts, res => { + let data = ""; + res.on("data", chunk => data += chunk); + res.on("end", () => resolve(data)); + }); + if (postData) req.write(postData); + req.end(); + }); +}; + +const HOST = "api.poeditor.com"; +const LIST = "/v2/languages/list"; +const EXPORT = "/v2/projects/export"; + +/* eslint-disable no-multi-spaces */ +const editorMap = { + "en-us": "en-us.json", // English, US + "en": "en-gb.json", // English, UK + "zh-Hans": "zh-cn.json", // Chinese, Simplified + "zh-Hant": "zh-tw.json", // Chinese, Traditional + "cs": "cs.json", // Czech + "da": "da.json", // Danish + "nl": "nl.json", // Dutch + "fr": "fr.json", // French + "de": "de.json", // German + "el": "el.json", // Greek + "hu": "hu.json", // Hungarian + "it": "it.json", // Italian + "ja": "ja.json", // Japanese + "ko": "ko.json", // Korean + "pl": "pl.json", // Polish + "pt": "pt-pt.json", // Portuguese, Portugal + "pt-br": "pt-br.json", // Protuguese, Brazil + "ru": "ru.json", // Russian + "sk": "sk.json", // Slovak + "es": "es-es.json", // Spanish (Spain) + "sv": "sv-se.json", // Swedish + "tr": "tr.json", // Turkish + "bg": "bg.json", // Bulgarian + "uk": "uk.json", // Ukrainian + "fi": "fi.json", // Finnish + "no": "no.json", // Norwegian + "hr": "hr.json", // Croation + "ro": "ro.json", // Romanian + "lt": "lt.json", // Lithuanian + "th": "th.json", // Thai + "vi": "vi.json", // Vietnamese + "hi": "hi.json", // Hindi +}; +/* eslint-enable no-multi-spaces */ + +const mo = opts => Object.assign(opts ?? {}, {api_token: process.env.POEDITOR_API_KEY, id: process.env.POEDITOR_PROJECT_ID}); + +const getAvailableLanguages = async () => { + const response = await get({method: "POST", host: HOST, path: LIST}, mo()); + const respJson = JSON.parse(response); + return respJson.result.languages; +}; + +const getTranslationUrl = async (code) => { + const response = await get({method: "POST", host: HOST, path: EXPORT}, mo({language: code, type: "key_value_json", filters: "translated"})); + const respJson = JSON.parse(response); + return new URL(respJson.result.url); +}; + +const getTranslationString = async (hostname, pathname) => { + const response = await get({host: hostname, path: pathname}); + return response ? response : "{}"; +}; + +const saveTranslationFile = (code, fileString) => { + const filename = editorMap[code]; + const filePath = path.join(localeFolder, filename); + fs.writeFileSync(filePath, fileString); +}; + +const updateTranslations = async () => { + console.log("Getting all available languages..."); + const languages = await getAvailableLanguages(); + console.log("Acquired all available languages!"); + console.log(""); + // const lang = {name: "English, UK", code: "en"}; + for (const lang of languages) { + console.log("Getting translation for " + lang.name); + const langUrl = await getTranslationUrl(lang.code); + const resultString = await getTranslationString(langUrl.hostname, langUrl.pathname); + saveTranslationFile(lang.code, resultString); + console.log("Saved translation for " + lang.name); + console.log(""); + } + console.log(""); + console.log("Successfully updated all translations!"); +}; + +updateTranslations(); \ No newline at end of file