Browse Source

chore: upload missing translations to transifex

Gerald 5 năm trước cách đây
mục cha
commit
7ff4bcef30

+ 30 - 0
.github/workflows/transifex-pull-translations.yml

@@ -0,0 +1,30 @@
+name: Download translations from Transifex
+
+on:
+  schedule:
+    - cron: '0 1 * * 1'
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+      - name: Install deps
+        run: yarn
+      - name: Update translations
+        run: node scripts/transifex pull
+        env:
+          TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}
+      - name: Create pull request
+        uses: peter-evans/create-pull-request@v2
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+          branch: i18n-patch
+          title: Update locale files from Transifex
+          body: >-
+            Violentmonkey updates translations from Transifex every week automatically.
+
+            If you want to help translate into your language, please visit [the transifex platform](https://www.transifex.com/projects/p/violentmonkey-nex/resource/messagesjson/).

+ 0 - 29
.github/workflows/transifex-pull.yml

@@ -1,29 +0,0 @@
-name: Update translations from Transifex
-
-on:
-  schedule:
-    - cron: '0 1 * * 1'
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Install deps
-      run: yarn
-    - name: Update translations
-      run: 'node scripts/transifex.js'
-      env:
-        TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}
-    - name: Create pull request
-      uses: peter-evans/create-pull-request@v2
-      with:
-        token: ${{ secrets.GITHUB_TOKEN }}
-        branch: i18n-patch
-        title: Update locale files from Transifex
-        body: >-
-          Violentmonkey updates translations from Transifex every week automatically.
-
-          If you want to help translate into your language, please visit [the transifex platform](https://www.transifex.com/projects/p/violentmonkey-nex/resource/messagesjson/).

+ 21 - 0
.github/workflows/transifex-push-resources.yml

@@ -0,0 +1,21 @@
+name: Upload resources to Transifex
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+      - name: Update locale files
+        run: yarn && yarn copyI18n
+      - name: Upload to Transifex
+        run: 'curl -i -L --user api:$TRANSIFEX_TOKEN -X PUT -F file=@dist/_locales/en/messages.json https://www.transifex.com/api/2/project/violentmonkey-nex/resource/messagesjson/content/'
+        env:
+          TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}

+ 17 - 0
.github/workflows/transifex-push-translations.yml

@@ -0,0 +1,17 @@
+name: Upload translations to Transifex
+
+on:
+  pull_request_target:
+    types:
+      - closed
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    # if PR with label `i18n` is merged
+    if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'i18n')
+    steps:
+      - name: Upload translations
+        run: node scripts/transifex push
+        env:
+          TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}

+ 0 - 20
.github/workflows/transifex-push.yml

@@ -1,20 +0,0 @@
-name: Upload resources to Transifex
-
-on:
-  push:
-    branches:
-      - master
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Update locale files
-      run: yarn && yarn copyI18n
-    - name: Upload to Transifex
-      run: 'curl -i -L --user api:$TRANSIFEX_TOKEN -X PUT -F file=@dist/_locales/en/messages.json https://www.transifex.com/api/2/project/violentmonkey-nex/resource/messagesjson/content/'
-      env:
-        TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}

+ 67 - 18
scripts/transifex.js

@@ -6,7 +6,11 @@ function delay(time) {
   return new Promise(resolve => setTimeout(resolve, time));
   return new Promise(resolve => setTimeout(resolve, time));
 }
 }
 
 
-function transifexRequest(url, method = 'GET') {
+function transifexRequest(url, {
+  method = 'GET',
+  responseType = 'json',
+  data = null,
+} = {}) {
   return new Promise((resolve, reject) => {
   return new Promise((resolve, reject) => {
     const child = spawn('curl', [
     const child = spawn('curl', [
       '-sSL',
       '-sSL',
@@ -14,8 +18,15 @@ function transifexRequest(url, method = 'GET') {
       `api:${process.env.TRANSIFEX_TOKEN}`,
       `api:${process.env.TRANSIFEX_TOKEN}`,
       '-X',
       '-X',
       method,
       method,
+      '-H',
+      'Content-Type: application/json',
+      ...data == null ? [] : ['-d', '@-'],
       `https://www.transifex.com${url}`,
       `https://www.transifex.com${url}`,
-    ], { stdio: ['ignore', 'pipe', 'inherit'] });
+    ], { stdio: ['pipe', 'pipe', 'inherit'] });
+    if (data != null) {
+      child.stdin.write(JSON.stringify(data));
+      child.stdin.end();
+    }
     const stdoutBuffer = [];
     const stdoutBuffer = [];
     child.stdout.on('data', chunk => {
     child.stdout.on('data', chunk => {
       stdoutBuffer.push(chunk);
       stdoutBuffer.push(chunk);
@@ -23,15 +34,19 @@ function transifexRequest(url, method = 'GET') {
     child.on('exit', (code) => {
     child.on('exit', (code) => {
       if (code) {
       if (code) {
         reject(code);
         reject(code);
-      } else {
-        const stdout = Buffer.concat(stdoutBuffer).toString('utf8');
+        return;
+      }
+      let result = Buffer.concat(stdoutBuffer).toString('utf8');
+      if (responseType === 'json') {
         try {
         try {
-          resolve(JSON.parse(stdout));
+          result = JSON.parse(result);
         } catch (err) {
         } catch (err) {
-          console.error('stdout: ' + stdout);
-          throw err;
+          console.error('stdout: ' + result);
+          reject(err);
+          return;
         }
         }
       }
       }
+      resolve(result);
     });
     });
   });
   });
 }
 }
@@ -45,23 +60,55 @@ async function loadRemote(lang) {
   // Reference: https://docs.transifex.com/api/translations#downloading-and-uploading-translations
   // Reference: https://docs.transifex.com/api/translations#downloading-and-uploading-translations
   // Use translated messages since we don't have enough reviewers
   // Use translated messages since we don't have enough reviewers
   const result = await transifexRequest(`/api/2/project/violentmonkey-nex/resource/messagesjson/translation/${lang}/?mode=onlytranslated`);
   const result = await transifexRequest(`/api/2/project/violentmonkey-nex/resource/messagesjson/translation/${lang}/?mode=onlytranslated`);
-  const reviewed = JSON.parse(result.content);
-  return reviewed;
+  const remote = JSON.parse(result.content);
+  return remote;
+}
+
+async function loadData(lang) {
+  const remote = await loadRemote(lang);
+  const filePath = `src/_locales/${lang}/messages.yml`;
+  const local = yaml.safeLoad(await fs.readFile(filePath, 'utf8'));
+  return { lang, local, remote, filePath };
+}
+
+async function pushTranslations({ local, remote, lang }) {
+  const remoteUpdate = {};
+  Object.entries(local)
+  .forEach(([key, value]) => {
+    const remoteMessage = remote[key] && remote[key].message;
+    if (value.touched !== false && value.message && value.message !== remoteMessage) remoteUpdate[key] = value;
+  });
+  if (Object.keys(remoteUpdate).length) {
+    const strings = await transifexRequest(`/api/2/project/violentmonkey-nex/resource/messagesjson/translation/${lang}/strings/`);
+    const updates = strings.filter(({ key, reviewed }) => !reviewed && remoteUpdate[key])
+      .map(({ key, string_hash }) => ({
+        source_entity_hash: string_hash,
+        translation: remoteUpdate[key].message,
+      }));
+    process.stdout.write(`\n  Uploading translations for ${lang}:\n  ${JSON.stringify(updates)}\n`);
+    await transifexRequest(`/api/2/project/violentmonkey-nex/resource/messagesjson/translation/${lang}/strings/`, {
+      method: 'PUT',
+      responseType: 'text',
+      data: updates,
+    });
+    process.stdout.write('  finished\n');
+  }
 }
 }
 
 
-async function extract(lang) {
-  const reviewed = await loadRemote(lang);
-  const fullPath = `src/_locales/${lang}/messages.yml`;
-  const existed = yaml.safeLoad(await fs.readFile(fullPath, 'utf8'));
-  Object.entries(existed)
+async function pullTranslations({ local, remote, filePath }) {
+  Object.entries(local)
   .forEach(([key, value]) => {
   .forEach(([key, value]) => {
-    const reviewedMessage = reviewed[key] && reviewed[key].message;
-    if (reviewedMessage) value.message = reviewedMessage;
+    const remoteMessage = remote[key] && remote[key].message;
+    if (remoteMessage) value.message = remoteMessage;
   });
   });
-  await fs.writeFile(fullPath, yaml.safeDump(existed), 'utf8');
+  await fs.writeFile(filePath, yaml.safeDump(local), 'utf8');
 }
 }
 
 
 async function main() {
 async function main() {
+  let handle;
+  if (process.argv.includes('push')) handle = pushTranslations;
+  else if (process.argv.includes('pull')) handle = pullTranslations;
+  else process.exit(2);
   process.stdout.write('Loading languages...');
   process.stdout.write('Loading languages...');
   const codes = await getLanguages();
   const codes = await getLanguages();
   process.stdout.write('OK\n');
   process.stdout.write('OK\n');
@@ -78,9 +125,11 @@ async function main() {
   for (const code of codes) {
   for (const code of codes) {
     await delay(500);
     await delay(500);
     try {
     try {
-      await extract(code);
+      const data = await loadData(code);
+      await handle(data);
     } catch (err) {
     } catch (err) {
       process.stderr.write(`\nError pulling ${code}\n`)
       process.stderr.write(`\nError pulling ${code}\n`)
+      console.error(err);
     }
     }
     finished += 1;
     finished += 1;
     showProgress();
     showProgress();