forked from arduino/arduino-ide
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtransifex-pull.js
160 lines (146 loc) · 5.05 KB
/
transifex-pull.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// @ts-check
const transifex = require('./transifex');
const path = require('path');
const fs = require('node:fs/promises');
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 exponential 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('Translations directory not specified');
shell.exit(1);
}
const languages = await getLanguages(organization, project);
shell.echo('translations found:', languages.join(', '));
// Remove data managed on Transifex to avoid accumulation of vestigial files
const translationFilenames = await fs.readdir(translationsDirectory);
for (const filename of translationFilenames) {
if (filename === 'en.json' || !filename.endsWith('.json')) {
continue;
}
await fs.unlink(path.join(translationsDirectory, filename));
}
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.');
})();