From 5be3e9de2dc7a943c7471f3cdbdcec09d851b4f7 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Fri, 8 Oct 2021 16:22:40 +0200 Subject: [PATCH] Add script to push translations source to transifex --- i18n/en.json | 10 ++++ package.json | 1 + scripts/i18n/transifex-push.js | 96 ++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 i18n/en.json create mode 100644 scripts/i18n/transifex-push.js diff --git a/i18n/en.json b/i18n/en.json new file mode 100644 index 00000000..08e12be2 --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,10 @@ +{ + "theia": { + "core": { + "common": { + "example": "This is an example translatable string", + "another-example": "This is another example translatable string" + } + } + } +} diff --git a/package.json b/package.json index 9ae5c687..7dcda652 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "test": "lerna run test", "download:plugins": "theia download:plugins", "update:version": "node ./scripts/update-version.js", + "i18n:push": "node ./scripts/i18n/transifex-push.js ./i18n/en.json", "i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/" }, "lint-staged": { diff --git a/scripts/i18n/transifex-push.js b/scripts/i18n/transifex-push.js new file mode 100644 index 00000000..3eb9248f --- /dev/null +++ b/scripts/i18n/transifex-push.js @@ -0,0 +1,96 @@ +// @ts-check + +const transifex = require('./transifex'); +const fetch = require('node-fetch'); +const fs = require('fs'); +const shell = require('shelljs'); +const util = require('util'); + +const uploadSourceFile = async (organization, project, resource, filePath) => { + const url = transifex.url('resource_strings_async_uploads'); + const data = { + data: { + attributes: { + callback_url: null, + content: fs.readFileSync(filePath).toString('base64'), + content_encoding: 'base64' + }, + relationships: { + resource: { + data: { + id: util.format('o:%s:p:%s:r:%s', organization, project, resource), + type: 'resources' + } + } + }, + type: 'resource_strings_async_uploads' + } + }; + + const headers = transifex.authHeader(); + headers['Content-Type'] = 'application/vnd.api+json'; + const json = await fetch(url, { method: 'POST', headers, body: JSON.stringify(data) }) + .catch(err => { + shell.echo(err); + shell.exit(1); + }) + .then(res => res.json()); + + return json['data']['id']; +}; + +const getSourceUploadStatus = async (uploadId) => { + const url = transifex.url(util.format('resource_strings_async_uploads/%s', uploadId)); + // The download request status must be asked from time to time, if it's + // still pending we try again using exponentional backoff starting from 2.5 seconds. + let backoffMs = 2500; + const headers = transifex.authHeader(); + while (true) { + const json = await fetch(url, { headers }) + .catch(err => { + shell.echo(err); + shell.exit(1); + }) + .then(res => res.json()); + + const status = json['data']['attributes']['status']; + if (status === 'succeeded') { + return + } else if (status === 'pending' || status === 'processing') { + await new Promise(r => setTimeout(r, backoffMs)); + backoffMs = backoffMs * 2; + // Retry the upload request status again + continue + } else if (status === 'failed') { + const errors = []; + json['data']['attributes']['errors'].forEach(err => { + errors.push(util.format('%s: %s', err.code, err.details)); + }); + throw util.format('Download request failed: %s', errors.join(', ')); + } + throw 'Download request failed in an unforeseen way'; + } +} + +(async () => { + const { organization, project, resource } = await transifex.credentials(); + const sourceFile = process.argv[2]; + if (!sourceFile) { + shell.echo('Translation source file not specified'); + shell.exit(1); + } + + const uploadId = await uploadSourceFile(organization, project, resource, sourceFile) + .catch(err => { + shell.echo(err); + shell.exit(1); + }); + + await getSourceUploadStatus(uploadId) + .catch(err => { + shell.echo(err); + shell.exit(1); + }); + + shell.echo("Translation source file uploaded"); +})() \ No newline at end of file