Browse Source

chore: update only changed locale by reading DIFF_URL

Gerald 5 years ago
parent
commit
3dbb0ea067
2 changed files with 108 additions and 43 deletions
  1. 1 0
      .github/workflows/transifex-push-translations.yml
  2. 107 43
      scripts/transifex.js

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

@@ -15,3 +15,4 @@ jobs:
         run: node scripts/transifex push
         env:
           TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}
+          DIFF_URL: ${{ github.event.pull_request.diff_url }}

+ 107 - 43
scripts/transifex.js

@@ -6,25 +6,39 @@ function delay(time) {
   return new Promise(resolve => setTimeout(resolve, time));
 }
 
-function transifexRequest(url, {
-  method = 'GET',
-  responseType = 'json',
-  data = null,
-} = {}) {
+function defer() {
+  const deferred = {};
+  deferred.promise = new Promise((resolve, reject) => {
+    deferred.resolve = resolve;
+    deferred.reject = reject;
+  });
+  return deferred;
+}
+
+function memoize(fn) {
+  const cache = {};
+  function wrapped(...args) {
+    const key = args.toString();
+    let result = cache[key];
+    if (!result) {
+      result = { data: fn(...args) };
+      cache[key] = result;
+    }
+    return result.data;
+  }
+  return wrapped;
+}
+
+function exec(cmd, args, options) {
   return new Promise((resolve, reject) => {
-    const child = spawn('curl', [
-      '-sSL',
-      '--user',
-      `api:${process.env.TRANSIFEX_TOKEN}`,
-      '-X',
-      method,
-      '-H',
-      'Content-Type: application/json',
-      ...data == null ? [] : ['-d', '@-'],
-      `https://www.transifex.com${url}`,
-    ], { stdio: ['pipe', 'pipe', 'inherit'] });
-    if (data != null) {
-      child.stdin.write(JSON.stringify(data));
+    const { stdin, ...rest } = options || {};
+    const child = spawn(
+      cmd,
+      args,
+      { ...rest, stdio: ['pipe', 'pipe', 'inherit'] },
+    );
+    if (stdin != null) {
+      child.stdin.write(stdin);
       child.stdin.end();
     }
     const stdoutBuffer = [];
@@ -36,21 +50,51 @@ function transifexRequest(url, {
         reject(code);
         return;
       }
-      let result = Buffer.concat(stdoutBuffer).toString('utf8');
-      if (responseType === 'json') {
-        try {
-          result = JSON.parse(result);
-        } catch (err) {
-          console.error('stdout: ' + result);
-          reject(err);
-          return;
-        }
-      }
+      const result = Buffer.concat(stdoutBuffer).toString('utf8');
       resolve(result);
     });
   });
 }
 
+let lastRequest;
+async function transifexRequest(url, {
+  method = 'GET',
+  responseType = 'json',
+  data = null,
+} = {}) {
+  const deferred = defer();
+  const prevRequest = lastRequest;
+  lastRequest = deferred.promise;
+  try {
+    await prevRequest;
+    let result = await exec(
+      'curl',
+      [
+        '-sSL',
+        '--user',
+        `api:${process.env.TRANSIFEX_TOKEN}`,
+        '-X',
+        method,
+        '-H',
+        'Content-Type: application/json',
+        ...data == null ? [] : ['-d', '@-'],
+        `https://www.transifex.com${url}`,
+      ],
+      {
+        stdin: data ? JSON.stringify(data) : null,
+      },
+    );
+    if (responseType === 'json') {
+      result = JSON.parse(result);
+    }
+    deferred.resolve(delay(500));
+    return result;
+  } catch (err) {
+    deferred.reject(err);
+    throw err;
+  }
+}
+
 async function getLanguages() {
   const result = await transifexRequest('/api/2/project/violentmonkey-nex/?details');
   return result.teams;
@@ -64,14 +108,33 @@ async function loadRemote(lang) {
   return remote;
 }
 
-async function loadData(lang) {
+const loadData = memoize(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 };
-}
+  return { local, remote, filePath };
+});
 
-async function pushTranslations({ local, remote, lang }) {
+const loadUpdatedLocales = memoize(async function loadUpdatedLocales() {
+  const diffUrl = process.env.DIFF_URL;
+  const result = await exec('curl', ['-sSL', diffUrl]);
+  // Example:
+  // diff --git a/src/_locales/ko/messages.yml b/src/_locales/ko/messages.yml
+  const codes = result.split('\n')
+    .map(line => {
+      const matches = line.match(/^diff --git a\/src\/_locales\/([^/]+)\/messages.yml b\/src\/_locales\/([^/]+)\/messages.yml$/);
+      const [, code1, code2] = matches || [];
+      return code1 === code2 && code1;
+    })
+    .filter(Boolean);
+  return codes;
+});
+
+async function pushTranslations(lang) {
+  const codes = await loadUpdatedLocales();
+  // Limit to languages changed in this PR only
+  if (!codes.includes(lang)) return;
+  const { local, remote } = await loadData(lang);
   const remoteUpdate = {};
   Object.entries(local)
   .forEach(([key, value]) => {
@@ -92,10 +155,13 @@ async function pushTranslations({ local, remote, lang }) {
       data: updates,
     });
     process.stdout.write('  finished\n');
+  } else {
+    process.stdout.write('up to date\n');
   }
 }
 
-async function pullTranslations({ local, remote, filePath }) {
+async function pullTranslations(code) {
+  const { local, remote, filePath } = await loadData(code);
   Object.entries(local)
   .forEach(([key, value]) => {
     const remoteMessage = remote[key] && remote[key].message;
@@ -117,24 +183,22 @@ async function main() {
     await fs.mkdir(`src/_locales/${code}`, { recursive: true });
   }
   spawn.sync('yarn', ['i18n'], { stdio: 'inherit' });
-  let finished = 0;
-  const showProgress = () => {
-    process.stdout.write(`\rLoading translations (${finished}/${codes.length})...`);
+  let current = 0;
+  const showProgress = (lang) => {
+    process.stdout.write(`\rLoading translations ${lang} (${current}/${codes.length})...`);
   };
-  showProgress();
   for (const code of codes) {
-    await delay(500);
+    current += 1;
+    showProgress(code);
     try {
-      const data = await loadData(code);
-      await handle(data);
+      await handle(code);
     } catch (err) {
       process.stderr.write(`\nError pulling ${code}\n`)
       console.error(err);
     }
-    finished += 1;
-    showProgress();
   }
-  process.stdout.write('OK\n');
+  showProgress('OK');
+  process.stdout.write('\n');
 }
 
 main();