pxt-core 9.2.9 → 9.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/cli.js +56 -45
- package/built/crowdin.d.ts +2 -1
- package/built/crowdin.js +218 -213
- package/built/crowdinApi.d.ts +10 -0
- package/built/crowdinApi.js +374 -0
- package/built/pxt.js +97 -298
- package/built/pxtblockly.js +8 -6
- package/built/pxtblocks.d.ts +4 -0
- package/built/pxtblocks.js +8 -6
- package/built/pxtlib.d.ts +3 -37
- package/built/pxtlib.js +41 -253
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +1 -1
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/localtypings/pxtarget.d.ts +3 -0
- package/package.json +5 -1
package/built/crowdin.js
CHANGED
|
@@ -1,107 +1,78 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.execCrowdinAsync = exports.buildAllTranslationsAsync = exports.downloadTargetTranslationsAsync = exports.internalUploadTargetTranslationsAsync = exports.uploadTargetTranslationsAsync = void 0;
|
|
3
|
+
exports.execCrowdinAsync = exports.buildAllTranslationsAsync = exports.downloadTargetTranslationsAsync = exports.uploadBuiltStringsAsync = exports.internalUploadTargetTranslationsAsync = exports.uploadTargetTranslationsAsync = void 0;
|
|
4
4
|
const nodeutil = require("./nodeutil");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const path = require("path");
|
|
7
|
-
|
|
8
|
-
const prj = pxt.appTarget.appTheme.crowdinProject;
|
|
9
|
-
const branch = pxt.appTarget.appTheme.crowdinBranch;
|
|
10
|
-
if (!prj) {
|
|
11
|
-
pxt.log(`crowdin upload skipped, Crowdin project missing in target theme`);
|
|
12
|
-
return Promise.resolve(undefined);
|
|
13
|
-
}
|
|
14
|
-
let key;
|
|
15
|
-
if (pxt.crowdin.testMode)
|
|
16
|
-
key = pxt.crowdin.TEST_KEY;
|
|
17
|
-
else
|
|
18
|
-
key = process.env[pxt.crowdin.KEY_VARIABLE];
|
|
19
|
-
if (!key) {
|
|
20
|
-
pxt.log(`Crowdin operation skipped: '${pxt.crowdin.KEY_VARIABLE}' variable is missing`);
|
|
21
|
-
return Promise.resolve(undefined);
|
|
22
|
-
}
|
|
23
|
-
return Promise.resolve({ prj, key, branch });
|
|
24
|
-
}
|
|
7
|
+
const crowdinApi_1 = require("./crowdinApi");
|
|
25
8
|
function uploadTargetTranslationsAsync(parsed) {
|
|
26
9
|
const uploadDocs = parsed && !!parsed.flags["docs"];
|
|
27
10
|
const uploadApiStrings = parsed && !!parsed.flags["apis"];
|
|
28
|
-
if (parsed && !!parsed.flags["test"])
|
|
11
|
+
if (parsed && !!parsed.flags["test"]) {
|
|
29
12
|
pxt.crowdin.setTestMode();
|
|
13
|
+
}
|
|
30
14
|
return internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs);
|
|
31
15
|
}
|
|
32
16
|
exports.uploadTargetTranslationsAsync = uploadTargetTranslationsAsync;
|
|
33
|
-
function internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs) {
|
|
17
|
+
async function internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs) {
|
|
34
18
|
pxt.log(`uploading translations (apis ${uploadApiStrings ? "yes" : "no"}, docs ${uploadDocs ? "yes" : "no"})...`);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
19
|
+
const crowdinDir = pxt.appTarget.id;
|
|
20
|
+
if (crowdinDir == "core") {
|
|
21
|
+
if (!uploadDocs) {
|
|
22
|
+
pxt.log('missing --docs flag, skipping');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
await uploadDocsTranslationsAsync("docs", crowdinDir);
|
|
26
|
+
await uploadDocsTranslationsAsync("common-docs", crowdinDir);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
if (uploadApiStrings) {
|
|
30
|
+
await uploadBuiltStringsAsync("built/target-strings.json", crowdinDir);
|
|
31
|
+
if (fs.existsSync("built/sim-strings.json")) {
|
|
32
|
+
await uploadBuiltStringsAsync("built/sim-strings.json", crowdinDir);
|
|
45
33
|
}
|
|
46
|
-
|
|
47
|
-
.then(() => uploadDocsTranslationsAsync("common-docs", crowdinDir, cred.branch, cred.prj, cred.key));
|
|
34
|
+
await uploadBundledTranslationsAsync(crowdinDir);
|
|
48
35
|
}
|
|
49
36
|
else {
|
|
50
|
-
|
|
51
|
-
if (uploadApiStrings)
|
|
52
|
-
p = p.then(() => execCrowdinAsync("upload", "built/target-strings.json", crowdinDir))
|
|
53
|
-
.then(() => fs.existsSync("built/sim-strings.json") ? execCrowdinAsync("upload", "built/sim-strings.json", crowdinDir) : Promise.resolve())
|
|
54
|
-
.then(() => uploadBundledTranslationsAsync(crowdinDir, cred.branch, cred.prj, cred.key));
|
|
55
|
-
else
|
|
56
|
-
p = p.then(() => pxt.log(`translations: skipping api strings upload`));
|
|
57
|
-
if (uploadDocs)
|
|
58
|
-
p = p.then(() => uploadDocsTranslationsAsync("docs", crowdinDir, cred.branch, cred.prj, cred.key))
|
|
59
|
-
// scan for docs in bundled packages
|
|
60
|
-
.then(() => Promise.all(pxt.appTarget.bundleddirs
|
|
61
|
-
// there must be a folder under .../docs
|
|
62
|
-
.filter(pkgDir => nodeutil.existsDirSync(path.join(pkgDir, "docs")))
|
|
63
|
-
// upload to crowdin
|
|
64
|
-
.map(pkgDir => uploadDocsTranslationsAsync(path.join(pkgDir, "docs"), crowdinDir, cred.branch, cred.prj, cred.key))).then(() => {
|
|
65
|
-
pxt.log("docs uploaded");
|
|
66
|
-
}));
|
|
67
|
-
else
|
|
68
|
-
p = p.then(() => pxt.log(`translations: skipping docs upload`));
|
|
69
|
-
return p;
|
|
37
|
+
pxt.log(`translations: skipping api strings upload`);
|
|
70
38
|
}
|
|
71
|
-
|
|
39
|
+
if (uploadDocs) {
|
|
40
|
+
await uploadDocsTranslationsAsync("docs", crowdinDir);
|
|
41
|
+
await Promise.all(pxt.appTarget.bundleddirs
|
|
42
|
+
.filter(pkgDir => nodeutil.existsDirSync(path.join(pkgDir, "docs")))
|
|
43
|
+
.map(pkgDir => uploadDocsTranslationsAsync(path.join(pkgDir, "docs"), crowdinDir)));
|
|
44
|
+
pxt.log("docs uploaded");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
pxt.log(`translations: skipping docs upload`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
72
50
|
}
|
|
73
51
|
exports.internalUploadTargetTranslationsAsync = internalUploadTargetTranslationsAsync;
|
|
74
|
-
function
|
|
75
|
-
|
|
52
|
+
async function uploadBuiltStringsAsync(filename, crowdinDir) {
|
|
53
|
+
const baseName = path.basename(filename);
|
|
54
|
+
const crowdinFile = crowdinDir ? path.join(crowdinDir, baseName) : baseName;
|
|
55
|
+
const contents = fs.readFileSync(filename, "utf8");
|
|
56
|
+
pxt.log(`Uploading ${filename} to ${crowdinFile}`);
|
|
57
|
+
await (0, crowdinApi_1.uploadFileAsync)(crowdinFile, contents);
|
|
58
|
+
}
|
|
59
|
+
exports.uploadBuiltStringsAsync = uploadBuiltStringsAsync;
|
|
60
|
+
async function uploadDocsTranslationsAsync(srcDir, crowdinDir) {
|
|
61
|
+
pxt.log(`Uploading from ${srcDir} to ${crowdinDir}`);
|
|
76
62
|
const ignoredDirectoriesList = getIgnoredDirectories(srcDir);
|
|
77
63
|
const todo = nodeutil.allFiles(srcDir).filter(f => /\.md$/.test(f) && !/_locales/.test(f)).reverse();
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
pxt.log(`creating folder ${crowdd}`);
|
|
83
|
-
return pxt.crowdin.createDirectoryAsync(branch, prj, key, crowdd);
|
|
84
|
-
}
|
|
85
|
-
return Promise.resolve();
|
|
86
|
-
};
|
|
87
|
-
const nextFileAsync = (f) => {
|
|
88
|
-
if (!f)
|
|
89
|
-
return Promise.resolve();
|
|
90
|
-
const crowdf = path.join(crowdinDir, f);
|
|
91
|
-
const crowdd = path.dirname(crowdf);
|
|
64
|
+
for (const file of todo) {
|
|
65
|
+
if (!file)
|
|
66
|
+
continue;
|
|
67
|
+
const crowdinFile = path.join(crowdinDir, file);
|
|
92
68
|
// check if file should be ignored
|
|
93
|
-
if (ignoredDirectoriesList.filter(d => path.dirname(
|
|
94
|
-
pxt.log(`skipping ${
|
|
95
|
-
|
|
69
|
+
if (ignoredDirectoriesList.filter(d => path.dirname(file).indexOf(d) == 0).length > 0) {
|
|
70
|
+
pxt.log(`skipping ${file} because of .crowdinignore file`);
|
|
71
|
+
continue;
|
|
96
72
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.then(() => pxt.crowdin.uploadTranslationAsync(branch, prj, key, crowdf, data))
|
|
101
|
-
.then(() => nextFileAsync(todo.pop()));
|
|
102
|
-
};
|
|
103
|
-
return ensureFolderAsync(path.join(crowdinDir, srcDir))
|
|
104
|
-
.then(() => nextFileAsync(todo.pop()));
|
|
73
|
+
pxt.log(`Uploading ${file} to ${crowdinFile}`);
|
|
74
|
+
await (0, crowdinApi_1.uploadFileAsync)(crowdinFile, fs.readFileSync(file, "utf8"));
|
|
75
|
+
}
|
|
105
76
|
}
|
|
106
77
|
function getIgnoredDirectories(srcDir) {
|
|
107
78
|
const ignoredDirectories = {};
|
|
@@ -117,40 +88,39 @@ function getIgnoredDirectories(srcDir) {
|
|
|
117
88
|
});
|
|
118
89
|
return Object.keys(ignoredDirectories).filter(d => ignoredDirectories[d]);
|
|
119
90
|
}
|
|
120
|
-
function uploadBundledTranslationsAsync(crowdinDir
|
|
91
|
+
async function uploadBundledTranslationsAsync(crowdinDir) {
|
|
121
92
|
const todo = [];
|
|
122
|
-
pxt.appTarget.bundleddirs
|
|
93
|
+
for (const dir of pxt.appTarget.bundleddirs) {
|
|
123
94
|
const locdir = path.join(dir, "_locales");
|
|
124
|
-
if (fs.existsSync(locdir))
|
|
125
|
-
fs.readdirSync(locdir)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
95
|
+
if (fs.existsSync(locdir)) {
|
|
96
|
+
const stringsFiles = fs.readdirSync(locdir).filter(f => /strings\.json$/i.test(f));
|
|
97
|
+
for (const file of stringsFiles) {
|
|
98
|
+
todo.unshift(path.join(locdir, file));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
129
102
|
pxt.log(`uploading bundled translations to Crowdin (${todo.length} files)`);
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
pxt.log(`uploading ${f} to ${crowdf}`);
|
|
137
|
-
return pxt.crowdin.uploadTranslationAsync(branch, prj, key, crowdf, JSON.stringify(data))
|
|
138
|
-
.then(nextFileAsync);
|
|
139
|
-
};
|
|
140
|
-
return nextFileAsync();
|
|
103
|
+
for (const file of todo) {
|
|
104
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
105
|
+
const crowdinFile = path.join(crowdinDir, path.basename(file));
|
|
106
|
+
pxt.log(`Uploading ${file} to ${crowdinFile}`);
|
|
107
|
+
await (0, crowdinApi_1.uploadFileAsync)(crowdinFile, JSON.stringify(data));
|
|
108
|
+
}
|
|
141
109
|
}
|
|
142
110
|
async function downloadTargetTranslationsAsync(parsed) {
|
|
143
111
|
const name = parsed === null || parsed === void 0 ? void 0 : parsed.args[0];
|
|
144
|
-
const cred = await crowdinCredentialsAsync();
|
|
145
|
-
if (!cred)
|
|
146
|
-
return;
|
|
147
112
|
await buildAllTranslationsAsync(async (fileName) => {
|
|
148
113
|
pxt.log(`downloading ${fileName}`);
|
|
149
|
-
|
|
114
|
+
const translations = await (0, crowdinApi_1.downloadFileTranslationsAsync)(fileName);
|
|
115
|
+
const parsed = {};
|
|
116
|
+
for (const file of Object.keys(translations)) {
|
|
117
|
+
parsed[file] = JSON.parse(translations[file]);
|
|
118
|
+
}
|
|
119
|
+
return parsed;
|
|
150
120
|
}, name);
|
|
151
121
|
}
|
|
152
122
|
exports.downloadTargetTranslationsAsync = downloadTargetTranslationsAsync;
|
|
153
|
-
async function buildAllTranslationsAsync(
|
|
123
|
+
async function buildAllTranslationsAsync(fetchFileTranslationAsync, singleDir) {
|
|
154
124
|
await buildTranslationFilesAsync(["sim-strings.json"], "sim-strings.json");
|
|
155
125
|
await buildTranslationFilesAsync(["target-strings.json"], "target-strings.json");
|
|
156
126
|
await buildTranslationFilesAsync(["strings.json"], "strings.json", true);
|
|
@@ -176,7 +146,7 @@ async function buildAllTranslationsAsync(langToStringsHandlerAsync, singleDir) {
|
|
|
176
146
|
const locdir = path.dirname(filePath);
|
|
177
147
|
const projectdir = path.dirname(locdir);
|
|
178
148
|
pxt.debug(`projectdir: ${projectdir}`);
|
|
179
|
-
const data = await
|
|
149
|
+
const data = await fetchFileTranslationAsync(crowdf);
|
|
180
150
|
for (const lang of Object.keys(data)) {
|
|
181
151
|
const dataLang = data[lang];
|
|
182
152
|
if (!dataLang || !stringifyTranslations(dataLang))
|
|
@@ -212,120 +182,155 @@ function stringifyTranslations(strings) {
|
|
|
212
182
|
else
|
|
213
183
|
return JSON.stringify(trg, null, 2);
|
|
214
184
|
}
|
|
215
|
-
function execCrowdinAsync(cmd, ...args) {
|
|
185
|
+
async function execCrowdinAsync(cmd, ...args) {
|
|
216
186
|
pxt.log(`executing Crowdin command ${cmd}...`);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const key = crowdinCredentials.key;
|
|
228
|
-
cmd = cmd.toLowerCase();
|
|
229
|
-
if (!args[0] && (cmd != "clean" && cmd != "stats"))
|
|
230
|
-
throw new Error(cmd == "status" ? "language missing" : "filename missing");
|
|
231
|
-
switch (cmd) {
|
|
232
|
-
case "stats": return statsCrowdinAsync(prj, key, args[0]);
|
|
233
|
-
case "clean": return cleanCrowdinAsync(prj, key, args[0] || "docs");
|
|
234
|
-
case "upload": return uploadCrowdinAsync(branch, prj, key, args[0], args[1]);
|
|
235
|
-
case "download": {
|
|
236
|
-
if (!args[1])
|
|
237
|
-
throw new Error("output path missing");
|
|
238
|
-
const fn = path.basename(args[0]);
|
|
239
|
-
return pxt.crowdin.downloadTranslationsAsync(branch, prj, key, args[0], { translatedOnly: true, validatedOnly: true })
|
|
240
|
-
.then(r => {
|
|
241
|
-
Object.keys(r).forEach(k => {
|
|
242
|
-
const rtranslations = stringifyTranslations(r[k]);
|
|
243
|
-
if (!rtranslations)
|
|
244
|
-
return;
|
|
245
|
-
nodeutil.mkdirP(path.join(args[1], k));
|
|
246
|
-
const outf = path.join(args[1], k, fn);
|
|
247
|
-
console.log(`writing ${outf}`);
|
|
248
|
-
nodeutil.writeFileSync(outf, rtranslations, { encoding: "utf8" });
|
|
249
|
-
});
|
|
250
|
-
});
|
|
187
|
+
switch (cmd.toLowerCase()) {
|
|
188
|
+
case "stats":
|
|
189
|
+
execStatsAsync(args[0]);
|
|
190
|
+
break;
|
|
191
|
+
case "clean":
|
|
192
|
+
await execCleanAsync(args[0] || "docs");
|
|
193
|
+
break;
|
|
194
|
+
case "upload":
|
|
195
|
+
if (!args[0]) {
|
|
196
|
+
throw new Error("filename missing");
|
|
251
197
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
198
|
+
await uploadBuiltStringsAsync(args[0], args[1]);
|
|
199
|
+
break;
|
|
200
|
+
case "download":
|
|
201
|
+
if (!args[1]) {
|
|
202
|
+
throw new Error("output path missing");
|
|
203
|
+
}
|
|
204
|
+
await execDownloadAsync(args[0], args[1]);
|
|
205
|
+
break;
|
|
206
|
+
case "restore":
|
|
207
|
+
if (!args[0]) {
|
|
208
|
+
throw new Error("Time missing");
|
|
209
|
+
}
|
|
210
|
+
if (args[1] !== "force" && !pxt.crowdin.testMode) {
|
|
211
|
+
throw new Error(`Refusing to run restore command without 'force' argument. Re-run as 'pxt crowdin restore <date> force' to proceed or use --test flag to test.`);
|
|
212
|
+
}
|
|
213
|
+
execRestoreFiles(args[0]);
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
throw new Error("unknown command");
|
|
217
|
+
}
|
|
255
218
|
}
|
|
256
219
|
exports.execCrowdinAsync = execCrowdinAsync;
|
|
257
|
-
function
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
220
|
+
async function execDownloadAsync(filename, outputDir) {
|
|
221
|
+
const basename = path.basename(filename);
|
|
222
|
+
pxt.log("Downloading translations");
|
|
223
|
+
const translations = await (0, crowdinApi_1.downloadFileTranslationsAsync)(filename);
|
|
224
|
+
for (const language of Object.keys(translations)) {
|
|
225
|
+
const langTranslations = stringifyTranslations(JSON.parse(translations[language]));
|
|
226
|
+
if (!langTranslations)
|
|
227
|
+
continue;
|
|
228
|
+
nodeutil.mkdirP(path.join(outputDir, language));
|
|
229
|
+
const outFilename = path.join(outputDir, language, basename);
|
|
230
|
+
console.log(`Writing ${outFilename}`);
|
|
231
|
+
nodeutil.writeFileSync(outFilename, langTranslations, { encoding: "utf8" });
|
|
232
|
+
}
|
|
264
233
|
}
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const
|
|
234
|
+
async function execCleanAsync(dir) {
|
|
235
|
+
const directoryPath = pxt.appTarget.id + "/" + dir;
|
|
236
|
+
const files = await (0, crowdinApi_1.listFilesAsync)(directoryPath);
|
|
237
|
+
for (const file of files) {
|
|
238
|
+
if (!nodeutil.fileExistsSync(file.substring(pxt.appTarget.id.length + 1))) {
|
|
239
|
+
pxt.log(`crowdin: dead file: ${file}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function execStatsAsync(language) {
|
|
244
|
+
const crowdinDir = pxt.appTarget.id;
|
|
245
|
+
// If this is run inside pxt-core, give results for all targets
|
|
246
|
+
const isCore = crowdinDir === "core";
|
|
247
|
+
pxt.log(`collecting crowdin stats for ${isCore ? "all targets" : crowdinDir} ${language ? `for language ${language}` : `all languages`}`);
|
|
248
|
+
const files = await (0, crowdinApi_1.listFilesAsync)();
|
|
249
|
+
const stats = {};
|
|
250
|
+
const outputCsvFile = `crowdinstats.csv`;
|
|
269
251
|
let headers = 'sep=\t\r\n';
|
|
270
|
-
headers += `
|
|
271
|
-
nodeutil.writeFileSync(
|
|
272
|
-
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
252
|
+
headers += `file\t language\t phrases\t translated\t approved\r\n`;
|
|
253
|
+
nodeutil.writeFileSync(outputCsvFile, headers, { encoding: "utf8" });
|
|
254
|
+
for (const file of files) {
|
|
255
|
+
pxt.debug("Processing file: " + file + "...");
|
|
256
|
+
// We only care about strings files
|
|
257
|
+
if (!file.endsWith("-strings.json"))
|
|
258
|
+
continue;
|
|
259
|
+
// Files for core are in the top-level of the crowdin project
|
|
260
|
+
const isCoreFile = file.indexOf("/") === -1;
|
|
261
|
+
// Only include files for the current target and core
|
|
262
|
+
if (!isCore && !isCoreFile && !file.startsWith(crowdinDir + "/"))
|
|
263
|
+
continue;
|
|
264
|
+
pxt.debug(`Downloading progress`);
|
|
265
|
+
const progress = await (0, crowdinApi_1.getFileProgressAsync)(file, language && [language]);
|
|
266
|
+
let fileCsvRows = "";
|
|
267
|
+
for (const language of progress) {
|
|
268
|
+
if (!stats[language.languageId]) {
|
|
269
|
+
stats[language.languageId] = {
|
|
270
|
+
uiphrases: 0,
|
|
271
|
+
uitranslated: 0,
|
|
272
|
+
uiapproved: 0,
|
|
273
|
+
corephrases: 0,
|
|
274
|
+
coretranslated: 0,
|
|
275
|
+
coreapproved: 0,
|
|
276
|
+
phrases: 0,
|
|
277
|
+
translated: 0,
|
|
278
|
+
approved: 0
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const fileCsvColumns = [
|
|
282
|
+
file,
|
|
283
|
+
language.languageId,
|
|
284
|
+
language.phrases.total,
|
|
285
|
+
language.phrases.translated,
|
|
286
|
+
language.phrases.approved
|
|
287
|
+
];
|
|
288
|
+
fileCsvRows += `${fileCsvColumns.join("\t ")}\r\n`;
|
|
289
|
+
const langStats = stats[language.languageId];
|
|
290
|
+
if (file === "strings.json") {
|
|
291
|
+
langStats.uiapproved += language.phrases.approved;
|
|
292
|
+
langStats.uitranslated += language.phrases.translated;
|
|
293
|
+
langStats.uiphrases += language.phrases.total;
|
|
294
|
+
}
|
|
295
|
+
else if (/core-strings\.json$/.test(file)) {
|
|
296
|
+
langStats.coreapproved += language.phrases.approved;
|
|
297
|
+
langStats.coretranslated += language.phrases.translated;
|
|
298
|
+
langStats.corephrases += language.phrases.total;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
langStats.approved += language.phrases.approved;
|
|
302
|
+
langStats.translated += language.phrases.translated;
|
|
303
|
+
langStats.phrases += language.phrases.total;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
fs.appendFileSync(outputCsvFile, fileCsvRows, { encoding: "utf8" });
|
|
307
|
+
}
|
|
308
|
+
console.log(`context\t language\t translated%\t approved%\t phrases\t translated\t approved`);
|
|
309
|
+
for (const language of Object.keys(stats)) {
|
|
310
|
+
const { uiphrases, uitranslated, uiapproved, corephrases, coretranslated, coreapproved, phrases, translated, approved, } = stats[language];
|
|
311
|
+
console.log(`ui\t ${language}\t ${(uitranslated / uiphrases * 100) >> 0}%\t ${(uiapproved / uiphrases * 100) >> 0}%\t ${uiphrases}\t ${uitranslated}\t ${uiapproved}`);
|
|
312
|
+
console.log(`core\t ${language}\t ${(coretranslated / corephrases * 100) >> 0}%\t ${(coreapproved / corephrases * 100) >> 0}%\t ${corephrases}\t ${coretranslated}\t ${coreapproved}`);
|
|
313
|
+
console.log(`blocks\t ${language}\t ${(translated / phrases * 100) >> 0}%\t ${(approved / phrases * 100) >> 0}%\t ${phrases}\t ${translated}\t ${approved}`);
|
|
322
314
|
}
|
|
323
315
|
}
|
|
324
|
-
function
|
|
325
|
-
let
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
316
|
+
async function execRestoreFiles(time) {
|
|
317
|
+
let cutoffTime;
|
|
318
|
+
if (!isNaN(parseInt(time + ""))) {
|
|
319
|
+
cutoffTime = parseInt(time + "");
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
cutoffTime = new Date(time).getTime();
|
|
323
|
+
}
|
|
324
|
+
const crowdinDir = pxt.appTarget.id;
|
|
325
|
+
// If this is run inside pxt-core, give results for all targets
|
|
326
|
+
const isCore = crowdinDir === "core";
|
|
327
|
+
const files = await (0, crowdinApi_1.listFilesAsync)();
|
|
328
|
+
for (const file of files) {
|
|
329
|
+
pxt.debug("Processing file: " + file + "...");
|
|
330
|
+
// Files for core are in the top-level of the crowdin project
|
|
331
|
+
const isCoreFile = file.indexOf("/") === -1;
|
|
332
|
+
if ((isCore && !isCoreFile) || !file.startsWith(crowdinDir + "/"))
|
|
333
|
+
continue;
|
|
334
|
+
await (0, crowdinApi_1.restoreFileBefore)(file, cutoffTime);
|
|
335
|
+
}
|
|
331
336
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function setProjectId(id: number): void;
|
|
2
|
+
export declare function uploadFileAsync(fileName: string, fileContent: string): Promise<void>;
|
|
3
|
+
export declare function getProjectInfoAsync(): Promise<import("@crowdin/crowdin-api-client").ProjectsGroupsModel.Project | import("@crowdin/crowdin-api-client").ProjectsGroupsModel.ProjectSettings>;
|
|
4
|
+
export declare function getProjectProgressAsync(languages?: string[]): Promise<import("@crowdin/crowdin-api-client").TranslationStatusModel.LanguageProgress[]>;
|
|
5
|
+
export declare function getDirectoryProgressAsync(directory: string, languages?: string[]): Promise<import("@crowdin/crowdin-api-client").TranslationStatusModel.LanguageProgress[]>;
|
|
6
|
+
export declare function getFileProgressAsync(file: string, languages?: string[]): Promise<import("@crowdin/crowdin-api-client").TranslationStatusModel.LanguageProgress[]>;
|
|
7
|
+
export declare function listFilesAsync(directory?: string): Promise<string[]>;
|
|
8
|
+
export declare function downloadTranslationsAsync(directory?: string): Promise<pxt.Map<string>>;
|
|
9
|
+
export declare function downloadFileTranslationsAsync(fileName: string): Promise<pxt.Map<string>>;
|
|
10
|
+
export declare function restoreFileBefore(filename: string, cutoffTime: number): Promise<void>;
|