From f8400c5a48f15277e47c14239b8854b0569346ed Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 7 Oct 2021 11:13:05 +0200 Subject: [PATCH 1/9] Add script to download translations from transifex --- package.json | 5 +- scripts/i18n/transifex-pull.js | 149 +++++++++++++++++++++++++++++++++ scripts/i18n/transifex.js | 52 ++++++++++++ 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 scripts/i18n/transifex-pull.js create mode 100644 scripts/i18n/transifex.js diff --git a/package.json b/package.json index 278cee1e5..9ae5c687b 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,15 @@ }, "scripts": { "prepare": "cross-env THEIA_ELECTRON_SKIP_REPLACE_FFMPEG=1 lerna run prepare && yarn download:plugins", - "cleanup": "npx rimraf ./**/node_modules && rm -rf ./node_modules ./.browser_modules ./arduino-ide-extension/build ./arduino-ide-extension/downloads ./arduino-ide-extension/Examples ./arduino-ide-extension/lib ./browser-app/lib ./browser-app/src-gen ./browser-app/gen-webpack.config.js ./electron-app/lib ./electron-app/src-gen ./electron-app/gen-webpack.config.js", + "cleanup": "npx rimraf ./**/node_modules && rm -rf ./node_modules ./.browser_modules ./arduino-ide-extension/build ./arduino-ide-extension/downloads ./arduino-ide-extension/Examples ./arduino-ide-extension/lib ./browser-app/lib ./browser-app/src-gen ./browser-app/gen-webpack.config.js ./electron-app/lib ./electron-app/src-gen ./electron-app/gen-webpack.config.js", "rebuild:browser": "theia rebuild:browser", "rebuild:electron": "theia rebuild:electron", "start": "yarn --cwd ./electron-app start", "watch": "lerna run watch --parallel", "test": "lerna run test", "download:plugins": "theia download:plugins", - "update:version": "node ./scripts/update-version.js" + "update:version": "node ./scripts/update-version.js", + "i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/" }, "lint-staged": { "./arduino-ide-extension/**/*.{ts,tsx}": [ diff --git a/scripts/i18n/transifex-pull.js b/scripts/i18n/transifex-pull.js new file mode 100644 index 000000000..986df91ea --- /dev/null +++ b/scripts/i18n/transifex-pull.js @@ -0,0 +1,149 @@ +// @ts-check + +const transifex = require('./transifex'); +const util = require('util'); +const shell = require('shelljs'); +const fetch = require('node-fetch'); +const download = require('download'); + +const getLanguages = async (organization, project) => { + const url = transifex.url( + util.format('projects/o:%s:p:%s/languages', organization, project) + ); + const json = await fetch(url, { headers: transifex.authHeader() }) + .catch(err => { + shell.echo(err); + shell.exit(1); + }) + .then(res => res.json()); + let languages = []; + json['data'].forEach(e => { + const languageCode = e['attributes']['code']; + // Skip english since it's the one we generate + if (languageCode === 'en') { + return; + } + languages.push(languageCode); + }); + return languages; +}; + +const requestTranslationDownload = async (relationships) => { + let url = transifex.url('resource_translations_async_downloads'); + const data = { + data: { + relationships, + type: 'resource_translations_async_downloads' + } + }; + 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 getTranslationDownloadStatus = async (language, downloadRequestId) => { + // 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; + while (true) { + const url = transifex.url( + util.format('resource_translations_async_downloads/%s', downloadRequestId) + ); + const options = { + headers: transifex.authHeader(), + redirect: 'manual' + }; + const res = await fetch(url, options).catch(err => { + shell.echo(err); + shell.exit(1); + }); + + if (res.status === 303) { + // When the file to download is ready we get redirected + return { + language, + downloadUrl: res.headers.get('location') + }; + } + + const json = await res.json(); + const downloadStatus = json['data']['attributes']['status']; + if (downloadStatus == 'pending' || downloadStatus == 'processing') { + await new Promise(r => setTimeout(r, backoffMs)); + backoffMs = backoffMs * 2; + // Retry the download request status again + continue; + } else if (downloadStatus == '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 translationsDirectory = process.argv[2]; + if (!translationsDirectory) { + shell.echo('Traslations directory not specified'); + shell.exit(1); + } + + const languages = await getLanguages(organization, project); + shell.echo('translations found:', languages.join(', ')); + + let downloadIds = []; + for (const language of languages) { + downloadIds.push({ + language, + id: await requestTranslationDownload({ + language: { + data: { + id: util.format('l:%s', language), + type: 'languages' + } + }, + resource: { + data: { + id: util.format('o:%s:p:%s:r:%s', organization, project, resource), + type: 'resources' + } + } + }) + }); + } + + const res = await Promise.all( + downloadIds.map(d => getTranslationDownloadStatus(d['language'], d['id'])) + ).catch(err => { + shell.echo(err); + shell.exit(1); + }); + + await Promise.all( + res.map(r => { + return download(r['downloadUrl'], translationsDirectory, { + filename: r['language'] + '.json' + }); + }) + ).catch(err => { + shell.echo(err); + shell.exit(1); + }); + + shell.echo('Translation files downloaded.'); +})(); diff --git a/scripts/i18n/transifex.js b/scripts/i18n/transifex.js new file mode 100644 index 000000000..b174feb26 --- /dev/null +++ b/scripts/i18n/transifex.js @@ -0,0 +1,52 @@ +// @ts-check + +const shell = require('shelljs'); +const util = require('util'); + +const TRANSIFEX_ENDPOINT = 'https://rest.api.transifex.com/'; + +const apiKey = () => { + const apiKey = process.env.TRANSIFEX_API_KEY; + if (apiKey === '') { + shell.echo('missing TRANSIFEX_API_KEY environment variable'); + shell.exit(1) + } + return apiKey +} + +exports.credentials = async () => { + const organization = process.env.TRANSIFEX_ORGANIZATION; + const project = process.env.TRANSIFEX_PROJECT; + const resource = process.env.TRANSIFEX_RESOURCE; + + if (organization === '') { + shell.echo('missing TRANSIFEX_ORGANIZATION environment variable'); + shell.exit(1) + } + + if (project === '') { + shell.echo('missing TRANSIFEX_PROJECT environment variable'); + shell.exit(1) + } + + if (resource === '') { + shell.echo('missing TRANSIFEX_RESOURCE environment variable'); + shell.exit(1) + } + + return { organization, project, resource } +} + +exports.url = (path, queryParameters) => { + let url = util.format('%s%s', TRANSIFEX_ENDPOINT, path); + if (queryParameters) { + url = util.format('%s?%s', url, queryParameters); + } + return url +} + +exports.authHeader = () => { + return { + 'Authorization': util.format("Bearer %s", apiKey()), + } +} From 491860adc8760cfc3a8ef431ddbcf35127cd1d72 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Fri, 8 Oct 2021 16:22:40 +0200 Subject: [PATCH 2/9] 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 000000000..08e12be21 --- /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 9ae5c687b..7dcda6522 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 000000000..3eb9248f9 --- /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 From 87f3e5285b7744887d849b41095d428d17d49ab4 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Mon, 11 Oct 2021 12:36:06 +0200 Subject: [PATCH 3/9] Add commands to generate translation file and check they're updated --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 7dcda6522..c927327e1 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "test": "lerna run test", "download:plugins": "theia download:plugins", "update:version": "node ./scripts/update-version.js", + "i18n:generate": "theia extract -e vscode -p '+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts' -o ./i18n/en.json", + "i18n:check": "yarn i18n:generate && git add -N ./i18n && git diff --exit-code ./i18n", "i18n:push": "node ./scripts/i18n/transifex-push.js ./i18n/en.json", "i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/" }, From d4ec473f05af16910f1d4a56826dad76d5f903a2 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Mon, 11 Oct 2021 12:37:26 +0200 Subject: [PATCH 4/9] Add workflows to push and pull translations from Transifex and check source file is updated when necessary --- .github/workflows/check-i18n-task.yml | 67 +++++++++++++++++++++++++ .github/workflows/i18n-nightly-push.yml | 30 +++++++++++ .github/workflows/i18n-weekly-pull.yml | 38 ++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 .github/workflows/check-i18n-task.yml create mode 100644 .github/workflows/i18n-nightly-push.yml create mode 100644 .github/workflows/i18n-weekly-pull.yml diff --git a/.github/workflows/check-i18n-task.yml b/.github/workflows/check-i18n-task.yml new file mode 100644 index 000000000..8735ed1da --- /dev/null +++ b/.github/workflows/check-i18n-task.yml @@ -0,0 +1,67 @@ +name: Check Internationalization + +env: + # See: https://github.com/actions/setup-go/tree/v2#readme + GO_VERSION: '1.16' + +# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows +on: + create: + push: + paths: + - '.github/workflows/check-i18n-task.ya?ml' + - 'package.json' + - '**/package.json' + - '**.ts' + - 'i18n/**' + pull_request: + paths: + - '.github/workflows/check-i18n-task.ya?ml' + - 'package.json' + - '**/package.json' + - '**.ts' + - 'i18n/**' + workflow_dispatch: + repository_dispatch: + +jobs: + run-determination: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.determination.outputs.result }} + steps: + - name: Determine if the rest of the workflow should run + id: determination + run: | + RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x" + # The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead. + if [[ \ + "${{ github.event_name }}" != "create" || \ + "${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX \ + ]]; then + # Run the other jobs. + RESULT="true" + else + # There is no need to run the other jobs. + RESULT="false" + fi + + echo "::set-output name=result::$RESULT" + + check: + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: '12.14.1' + registry-url: 'https://registry.npmjs.org' + + - name: Check for errors + run: yarn i18n:check diff --git a/.github/workflows/i18n-nightly-push.yml b/.github/workflows/i18n-nightly-push.yml new file mode 100644 index 000000000..942a3f6dc --- /dev/null +++ b/.github/workflows/i18n-nightly-push.yml @@ -0,0 +1,30 @@ +name: i18n-nightly-push + +on: + schedule: + # run every day at 1AM + - cron: '0 1 * * *' + +jobs: + push-to-transifex: + # This workflow is only of value to the arduino/arduino-ide repository and + # would always fail in forks + if: github.repository == 'arduino/arduino-ide' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: '12.14.1' + registry-url: 'https://registry.npmjs.org' + + - name: Run i18n:push script + run: yarn run i18n:push + env: + TRANSIFEX_ORGANIZATION: ${{ secrets.TRANSIFEX_ORGANIZATION }} + TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }} + TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }} + TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }} diff --git a/.github/workflows/i18n-weekly-pull.yml b/.github/workflows/i18n-weekly-pull.yml new file mode 100644 index 000000000..e97fc213c --- /dev/null +++ b/.github/workflows/i18n-weekly-pull.yml @@ -0,0 +1,38 @@ +name: i18n-weekly-pull + +on: + schedule: + # run every monday at 2AM + - cron: '0 2 * * 1' + +jobs: + pull-from-transifex: + # This workflow is only of value to the arduino/arduino-ide repository and + # would always fail in forks + if: github.repository == 'arduino/arduino-ide' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: '12.14.1' + registry-url: 'https://registry.npmjs.org' + + - name: Run i18n:pull script + run: yarn run i18n:pull + env: + TRANSIFEX_ORGANIZATION: ${{ secrets.TRANSIFEX_ORGANIZATION }} + TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }} + TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }} + TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Updated translation files + title: Updated translation files + branch: i18n/translations-update + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> From 4f63a886e4322bcb4c94eef677b22338658e0fe0 Mon Sep 17 00:00:00 2001 From: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:49:47 +0200 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: per1234 --- .github/workflows/check-i18n-task.yml | 30 +------------------------ .github/workflows/i18n-nightly-push.yml | 5 +---- .github/workflows/i18n-weekly-pull.yml | 7 ++---- 3 files changed, 4 insertions(+), 38 deletions(-) diff --git a/.github/workflows/check-i18n-task.yml b/.github/workflows/check-i18n-task.yml index 8735ed1da..d58cc9fa1 100644 --- a/.github/workflows/check-i18n-task.yml +++ b/.github/workflows/check-i18n-task.yml @@ -6,18 +6,15 @@ env: # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows on: - create: push: paths: - '.github/workflows/check-i18n-task.ya?ml' - - 'package.json' - '**/package.json' - '**.ts' - 'i18n/**' pull_request: paths: - '.github/workflows/check-i18n-task.ya?ml' - - 'package.json' - '**/package.json' - '**.ts' - 'i18n/**' @@ -25,32 +22,7 @@ on: repository_dispatch: jobs: - run-determination: - runs-on: ubuntu-latest - outputs: - result: ${{ steps.determination.outputs.result }} - steps: - - name: Determine if the rest of the workflow should run - id: determination - run: | - RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x" - # The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead. - if [[ \ - "${{ github.event_name }}" != "create" || \ - "${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX \ - ]]; then - # Run the other jobs. - RESULT="true" - else - # There is no need to run the other jobs. - RESULT="false" - fi - - echo "::set-output name=result::$RESULT" - check: - needs: run-determination - if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest steps: @@ -58,7 +30,7 @@ jobs: uses: actions/checkout@v2 - name: Install Node.js 12.x - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/i18n-nightly-push.yml b/.github/workflows/i18n-nightly-push.yml index 942a3f6dc..a519bec83 100644 --- a/.github/workflows/i18n-nightly-push.yml +++ b/.github/workflows/i18n-nightly-push.yml @@ -7,16 +7,13 @@ on: jobs: push-to-transifex: - # This workflow is only of value to the arduino/arduino-ide repository and - # would always fail in forks - if: github.repository == 'arduino/arduino-ide' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Install Node.js 12.x - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/i18n-weekly-pull.yml b/.github/workflows/i18n-weekly-pull.yml index e97fc213c..0497410be 100644 --- a/.github/workflows/i18n-weekly-pull.yml +++ b/.github/workflows/i18n-weekly-pull.yml @@ -7,16 +7,13 @@ on: jobs: pull-from-transifex: - # This workflow is only of value to the arduino/arduino-ide repository and - # would always fail in forks - if: github.repository == 'arduino/arduino-ide' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Install Node.js 12.x - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' @@ -33,6 +30,6 @@ jobs: uses: peter-evans/create-pull-request@v3 with: commit-message: Updated translation files - title: Updated translation files + title: Update translation files branch: i18n/translations-update author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> From f763158acde855c2e78c34b05b32feaae90f4a9f Mon Sep 17 00:00:00 2001 From: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:16:53 +0200 Subject: [PATCH 6/9] Update .github/workflows/check-i18n-task.yml Co-authored-by: per1234 --- .github/workflows/check-i18n-task.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/check-i18n-task.yml b/.github/workflows/check-i18n-task.yml index d58cc9fa1..83f3d5a6b 100644 --- a/.github/workflows/check-i18n-task.yml +++ b/.github/workflows/check-i18n-task.yml @@ -1,9 +1,5 @@ name: Check Internationalization -env: - # See: https://github.com/actions/setup-go/tree/v2#readme - GO_VERSION: '1.16' - # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows on: push: From 3fea5b51430837526252018163972c4817f44fdc Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 4 Nov 2021 14:48:54 +0100 Subject: [PATCH 7/9] Update theia/cli version --- package.json | 4 +- yarn.lock | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c927327e1..87a46a8b2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "node": ">=12.14.1 <13" }, "devDependencies": { - "@theia/cli": "1.17.2", + "@theia/cli": "1.19.0", "@types/sinon": "^2.3.5", "@types/jsdom": "^11.0.4", "@typescript-eslint/eslint-plugin": "^4.27.0", @@ -43,7 +43,7 @@ "test": "lerna run test", "download:plugins": "theia download:plugins", "update:version": "node ./scripts/update-version.js", - "i18n:generate": "theia extract -e vscode -p '+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts' -o ./i18n/en.json", + "i18n:generate": "theia nls-extract -e vscode -f '+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts' -o ./i18n/en.json", "i18n:check": "yarn i18n:generate && git add -N ./i18n && git diff --exit-code ./i18n", "i18n:push": "node ./scripts/i18n/transifex-push.js ./i18n/en.json", "i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/" diff --git a/yarn.lock b/yarn.lock index 9764f514f..ee92004da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2136,6 +2136,38 @@ worker-loader "^3.0.8" yargs "^15.3.1" +"@theia/application-manager@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@theia/application-manager/-/application-manager-1.19.0.tgz#e608e55e1bc5252b185e6f8917ea274de2a8dc7a" + integrity sha512-h4PspeMtDyHnpz3qELTKwUeWTFmpXe4OzDmET6u6NgXh93ba94gnIh0OZg7xjDD/3vrJLz1yQhVc263FioxBjw== + dependencies: + "@babel/core" "^7.10.0" + "@babel/plugin-transform-classes" "^7.10.0" + "@babel/plugin-transform-runtime" "^7.10.0" + "@babel/preset-env" "^7.10.0" + "@theia/application-package" "1.19.0" + "@theia/compression-webpack-plugin" "^3.0.0" + "@types/fs-extra" "^4.0.2" + babel-loader "^8.2.2" + buffer "^6.0.3" + circular-dependency-plugin "^5.2.2" + copy-webpack-plugin "^8.1.1" + css-loader "^6.2.0" + electron-rebuild "^1.8.6" + font-awesome-webpack "0.0.5-beta.2" + fs-extra "^4.0.2" + ignore-loader "^0.1.2" + less "^3.0.3" + setimmediate "^1.0.5" + source-map-loader "^2.0.1" + source-map-support "^0.5.19" + style-loader "^2.0.0" + umd-compat-loader "^2.1.2" + webpack "^5.48.0" + webpack-cli "4.7.0" + worker-loader "^3.0.8" + yargs "^15.3.1" + "@theia/application-package@1.17.2": version "1.17.2" resolved "https://registry.yarnpkg.com/@theia/application-package/-/application-package-1.17.2.tgz#05b1f2c749bbd693013f17cdf8b57d5789cb70fb" @@ -2170,6 +2202,23 @@ semver "^5.4.1" write-json-file "^2.2.0" +"@theia/application-package@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@theia/application-package/-/application-package-1.19.0.tgz#a6045bec3ebcc7acb80dbfbb644c7fd429799aeb" + integrity sha512-+WToWAofmvFafuY6kmsNAKz1IRwWwurHmj4U7JYpJ2jgey3H7lcO6qUMd8ZfWt3wK9ckPqlnuasuI7nHTZgcrA== + dependencies: + "@types/fs-extra" "^4.0.2" + "@types/request" "^2.0.3" + "@types/semver" "^5.4.0" + "@types/write-json-file" "^2.2.1" + changes-stream "^2.2.0" + deepmerge "^4.2.2" + fs-extra "^4.0.2" + is-electron "^2.1.0" + request "^2.82.0" + semver "^5.4.1" + write-json-file "^2.2.0" + "@theia/bulk-edit@1.18.0": version "1.18.0" resolved "https://registry.yarnpkg.com/@theia/bulk-edit/-/bulk-edit-1.18.0.tgz#bf24abd1fb25085e0c763beacfb13c2f084641dd" @@ -2218,6 +2267,34 @@ temp "^0.9.1" yargs "^15.3.1" +"@theia/cli@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@theia/cli/-/cli-1.19.0.tgz#d9316b5e294ad5e7c4f66fd6d44efd9419d60955" + integrity sha512-qMpI4cjVWzYbStN/+9wxyWCimKCVmrP+uSykRqlRFnRJcsLQC1om8ED98//9BgwYW5y3zSwB7rmBd/S0Io8tsQ== + dependencies: + "@theia/application-manager" "1.19.0" + "@theia/application-package" "1.19.0" + "@theia/localization-manager" "1.19.0" + "@theia/ovsx-client" "1.19.0" + "@types/chai" "^4.2.7" + "@types/mkdirp" "^0.5.2" + "@types/mocha" "^5.2.7" + "@types/node-fetch" "^2.5.7" + "@types/puppeteer" "^2.0.0" + "@types/requestretry" "^1.12.3" + "@types/tar" "^4.0.3" + chai "^4.2.0" + colors "^1.4.0" + decompress "^4.2.1" + https-proxy-agent "^5.0.0" + mocha "^7.0.0" + node-fetch "^2.6.0" + proxy-from-env "^1.1.0" + puppeteer "^2.0.0" + puppeteer-to-istanbul "^1.2.2" + temp "^0.9.1" + yargs "^15.3.1" + "@theia/compression-webpack-plugin@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@theia/compression-webpack-plugin/-/compression-webpack-plugin-3.0.0.tgz#3d1b932327caf33b218fd5d3d1a64a5dbee4324a" @@ -2440,6 +2517,17 @@ ajv "^6.5.3" jsonc-parser "^2.2.0" +"@theia/localization-manager@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@theia/localization-manager/-/localization-manager-1.19.0.tgz#6b6eab185c032b376f72c0ddc2a07e6a51ebd4ea" + integrity sha512-bzVZNraNU181CyRI7EVKbkFSM7Q/gZ79avXoZISTWNQSZBdU7mHJiSDDZIIl9qzJdjo5fxtml5oUI98TRw0lnA== + dependencies: + "@types/fs-extra" "^4.0.2" + deepmerge "^4.2.2" + fs-extra "^4.0.2" + glob "^7.2.0" + typescript "^4.4.3" + "@theia/markers@1.18.0": version "1.18.0" resolved "https://registry.yarnpkg.com/@theia/markers/-/markers-1.18.0.tgz#8e1b0c1c55727915f4ea9c384dcf0c7d250774b8" @@ -2528,6 +2616,15 @@ bent "^7.1.0" semver "^5.4.1" +"@theia/ovsx-client@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@theia/ovsx-client/-/ovsx-client-1.19.0.tgz#6d350c831c7e3280a10269b1ce72f4312896d441" + integrity sha512-TCdEURZTywMv7TbvFZpYjCF/mrB2ltu+9gVIk49eGAQISbmyWOoq3h9D4p+r+HTC+9TvBwAP6LSkvfHjuu+3tw== + dependencies: + "@types/bent" "^7.0.1" + bent "^7.1.0" + semver "^5.4.1" + "@theia/plugin-ext-vscode@1.18.0": version "1.18.0" resolved "https://registry.yarnpkg.com/@theia/plugin-ext-vscode/-/plugin-ext-vscode-1.18.0.tgz#ef1e44992c0fb5b52d02a4c9b93b1da94f3f9a94" @@ -7571,7 +7668,7 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.5: +glob@^7.0.5, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -13992,6 +14089,11 @@ typescript@^3.9.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== +typescript@^4.4.3: + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" From bc0177dddcb77c6d7db0c49caac949bf9407e35e Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 4 Nov 2021 14:53:17 +0100 Subject: [PATCH 8/9] Add step to install dependencies in i18n workflows --- .github/workflows/check-i18n-task.yml | 3 +++ .github/workflows/i18n-nightly-push.yml | 3 +++ .github/workflows/i18n-weekly-pull.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/check-i18n-task.yml b/.github/workflows/check-i18n-task.yml index 83f3d5a6b..121a9a844 100644 --- a/.github/workflows/check-i18n-task.yml +++ b/.github/workflows/check-i18n-task.yml @@ -31,5 +31,8 @@ jobs: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: yarn + - name: Check for errors run: yarn i18n:check diff --git a/.github/workflows/i18n-nightly-push.yml b/.github/workflows/i18n-nightly-push.yml index a519bec83..c62f16a7f 100644 --- a/.github/workflows/i18n-nightly-push.yml +++ b/.github/workflows/i18n-nightly-push.yml @@ -18,6 +18,9 @@ jobs: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: yarn + - name: Run i18n:push script run: yarn run i18n:push env: diff --git a/.github/workflows/i18n-weekly-pull.yml b/.github/workflows/i18n-weekly-pull.yml index 0497410be..1a361febe 100644 --- a/.github/workflows/i18n-weekly-pull.yml +++ b/.github/workflows/i18n-weekly-pull.yml @@ -18,6 +18,9 @@ jobs: node-version: '12.14.1' registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: yarn + - name: Run i18n:pull script run: yarn run i18n:pull env: From 675ff59214ba05624b5ae0599d8d578037b0b740 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 4 Nov 2021 15:07:11 +0100 Subject: [PATCH 9/9] Update translation source file --- i18n/en.json | 225 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 218 insertions(+), 7 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 08e12be21..77201b48c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,10 +1,221 @@ { - "theia": { - "core": { - "common": { - "example": "This is an example translatable string", - "another-example": "This is another example translatable string" - } + "arduino": { + "common": { + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost.", + "noBoardSelected": "No board selected" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user." + }, + "cloud": { + "signIn": "Sign in", + "signOut": "Sign Out", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’." + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Board Info", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Boards Manager", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Add File", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Add .ZIP Library...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Archive Sketch", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "A folder named \"{0}\" already exists. Can't open sketch.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "doneUploading": "Done uploading.", + "couldNotConnectToMonitor": "Could not reconnect to serial monitor. {0}", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Burn Bootloader", + "doneBurningBootloader": "Done burning bootloader." + }, + "debug": { + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "editor": { + "copyForForum": "Copy for Forum (Markdown)", + "commentUncomment": "Comment/Uncomment", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Decrease Font Size", + "autoFormat": "Auto Format" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Built-in examples", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Upload" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater" + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "monitor": { + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to the the serial-monitor after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2] seconds..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": ".{0} is not a valid extension", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } } - } }