locize-cli 11.0.0 → 12.0.0
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/CHANGELOG.md +12 -0
- package/LICENSE +1 -1
- package/README.md +1 -0
- package/dist/cjs/add.js +90 -0
- package/{bin/locize → dist/cjs/cli.js} +390 -670
- package/dist/cjs/combineSubkeyPreprocessor.js +155 -0
- package/dist/cjs/convertToDesiredFormat.js +205 -0
- package/dist/cjs/convertToFlatFormat.js +231 -0
- package/dist/cjs/copyVersion.js +60 -0
- package/dist/cjs/createBranch.js +59 -0
- package/dist/cjs/deleteBranch.js +89 -0
- package/dist/cjs/deleteNamespace.js +37 -0
- package/dist/cjs/download.js +376 -0
- package/dist/cjs/filterNamespaces.js +13 -0
- package/dist/cjs/format.js +156 -0
- package/dist/cjs/formats.js +33 -0
- package/dist/cjs/get.js +66 -0
- package/dist/cjs/getBranches.js +37 -0
- package/dist/cjs/getJob.js +37 -0
- package/dist/cjs/getProjectStats.js +37 -0
- package/dist/cjs/getRemoteLanguages.js +38 -0
- package/dist/cjs/getRemoteNamespace.js +125 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/isValidUuid.js +6 -0
- package/dist/cjs/lngs.js +215 -0
- package/dist/cjs/mapLimit.js +22 -0
- package/dist/cjs/mergeBranch.js +80 -0
- package/dist/cjs/migrate.js +239 -0
- package/dist/cjs/missing.js +162 -0
- package/dist/cjs/package.json +5 -0
- package/{parseLocalLanguage.js → dist/cjs/parseLocalLanguage.js} +135 -142
- package/dist/cjs/parseLocalLanguages.js +18 -0
- package/dist/cjs/parseLocalReference.js +11 -0
- package/dist/cjs/publishVersion.js +42 -0
- package/dist/cjs/removeUndefinedFromArrays.js +19 -0
- package/dist/cjs/removeVersion.js +42 -0
- package/dist/cjs/request.js +66 -0
- package/dist/cjs/shouldUnflatten.js +21 -0
- package/dist/cjs/sortFlatResources.js +13 -0
- package/dist/cjs/sync.js +772 -0
- package/dist/cjs/unflatten.js +81 -0
- package/dist/esm/add.js +88 -0
- package/dist/esm/cli.js +1020 -0
- package/{combineSubkeyPreprocessor.js → dist/esm/combineSubkeyPreprocessor.js} +70 -73
- package/dist/esm/convertToDesiredFormat.js +203 -0
- package/dist/esm/convertToFlatFormat.js +229 -0
- package/dist/esm/copyVersion.js +58 -0
- package/dist/esm/createBranch.js +57 -0
- package/dist/esm/deleteBranch.js +87 -0
- package/dist/esm/deleteNamespace.js +35 -0
- package/dist/esm/download.js +374 -0
- package/{filterNamespaces.js → dist/esm/filterNamespaces.js} +4 -4
- package/dist/esm/format.js +154 -0
- package/{formats.js → dist/esm/formats.js} +7 -11
- package/dist/esm/get.js +64 -0
- package/dist/esm/getBranches.js +35 -0
- package/dist/esm/getJob.js +35 -0
- package/dist/esm/getProjectStats.js +35 -0
- package/dist/esm/getRemoteLanguages.js +36 -0
- package/dist/esm/getRemoteNamespace.js +123 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/isValidUuid.js +4 -0
- package/dist/esm/lngs.js +213 -0
- package/dist/esm/mapLimit.js +20 -0
- package/dist/esm/mergeBranch.js +78 -0
- package/dist/esm/migrate.js +237 -0
- package/dist/esm/missing.js +160 -0
- package/dist/esm/parseLocalLanguage.js +194 -0
- package/dist/esm/parseLocalLanguages.js +16 -0
- package/dist/esm/parseLocalReference.js +9 -0
- package/dist/esm/publishVersion.js +40 -0
- package/{removeUndefinedFromArrays.js → dist/esm/removeUndefinedFromArrays.js} +5 -5
- package/dist/esm/removeVersion.js +40 -0
- package/dist/esm/request.js +64 -0
- package/{shouldUnflatten.js → dist/esm/shouldUnflatten.js} +7 -7
- package/dist/esm/sortFlatResources.js +11 -0
- package/dist/esm/sync.js +770 -0
- package/{unflatten.js → dist/esm/unflatten.js} +36 -34
- package/package.json +39 -18
- package/rollup.config.js +57 -0
- package/add.js +0 -105
- package/convertToDesiredFormat.js +0 -268
- package/convertToFlatFormat.js +0 -322
- package/copyVersion.js +0 -69
- package/createBranch.js +0 -61
- package/deleteBranch.js +0 -97
- package/deleteNamespace.js +0 -39
- package/download.js +0 -516
- package/format.js +0 -206
- package/get.js +0 -81
- package/getBranches.js +0 -40
- package/getJob.js +0 -40
- package/getProjectStats.js +0 -40
- package/getRemoteLanguages.js +0 -40
- package/getRemoteNamespace.js +0 -122
- package/index.js +0 -9
- package/isValidUuid.js +0 -2
- package/lngs.json +0 -211
- package/mergeBranch.js +0 -102
- package/migrate.js +0 -314
- package/missing.js +0 -169
- package/parseLocalLanguages.js +0 -22
- package/parseLocalReference.js +0 -10
- package/publishVersion.js +0 -64
- package/removeVersion.js +0 -64
- package/request.js +0 -64
- package/sortFlatResources.js +0 -9
- package/sync.js +0 -786
package/dist/esm/lngs.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const lngCodes = [
|
|
2
|
+
'dev',
|
|
3
|
+
'ach',
|
|
4
|
+
'arn',
|
|
5
|
+
'ast',
|
|
6
|
+
'cgg',
|
|
7
|
+
'csb',
|
|
8
|
+
'dz',
|
|
9
|
+
'es_ar',
|
|
10
|
+
'es-ar',
|
|
11
|
+
'fil',
|
|
12
|
+
'fur',
|
|
13
|
+
'gun',
|
|
14
|
+
'jbo',
|
|
15
|
+
'mai',
|
|
16
|
+
'mfe',
|
|
17
|
+
'mnk',
|
|
18
|
+
'nah',
|
|
19
|
+
'nap',
|
|
20
|
+
'nso',
|
|
21
|
+
'pap',
|
|
22
|
+
'pms',
|
|
23
|
+
'pt_br',
|
|
24
|
+
'pt-br',
|
|
25
|
+
'sah',
|
|
26
|
+
'sco',
|
|
27
|
+
'son',
|
|
28
|
+
'ab',
|
|
29
|
+
'aa',
|
|
30
|
+
'af',
|
|
31
|
+
'ak',
|
|
32
|
+
'sq',
|
|
33
|
+
'am',
|
|
34
|
+
'ar',
|
|
35
|
+
'an',
|
|
36
|
+
'hy',
|
|
37
|
+
'as',
|
|
38
|
+
'av',
|
|
39
|
+
'ae',
|
|
40
|
+
'ay',
|
|
41
|
+
'az',
|
|
42
|
+
'bm',
|
|
43
|
+
'ba',
|
|
44
|
+
'eu',
|
|
45
|
+
'be',
|
|
46
|
+
'bn',
|
|
47
|
+
'bh',
|
|
48
|
+
'bi',
|
|
49
|
+
'bs',
|
|
50
|
+
'br',
|
|
51
|
+
'bg',
|
|
52
|
+
'my',
|
|
53
|
+
'ca',
|
|
54
|
+
'ch',
|
|
55
|
+
'ce',
|
|
56
|
+
'ny',
|
|
57
|
+
'zh',
|
|
58
|
+
'cv',
|
|
59
|
+
'kw',
|
|
60
|
+
'co',
|
|
61
|
+
'cr',
|
|
62
|
+
'hr',
|
|
63
|
+
'cs',
|
|
64
|
+
'da',
|
|
65
|
+
'dv',
|
|
66
|
+
'nl',
|
|
67
|
+
'en',
|
|
68
|
+
'eo',
|
|
69
|
+
'et',
|
|
70
|
+
'ee',
|
|
71
|
+
'fo',
|
|
72
|
+
'fj',
|
|
73
|
+
'fi',
|
|
74
|
+
'fr',
|
|
75
|
+
'ff',
|
|
76
|
+
'gl',
|
|
77
|
+
'ka',
|
|
78
|
+
'de',
|
|
79
|
+
'el',
|
|
80
|
+
'gn',
|
|
81
|
+
'gu',
|
|
82
|
+
'ht',
|
|
83
|
+
'ha',
|
|
84
|
+
'he',
|
|
85
|
+
'hz',
|
|
86
|
+
'hi',
|
|
87
|
+
'ho',
|
|
88
|
+
'hu',
|
|
89
|
+
'ia',
|
|
90
|
+
'id',
|
|
91
|
+
'ie',
|
|
92
|
+
'ga',
|
|
93
|
+
'ig',
|
|
94
|
+
'ik',
|
|
95
|
+
'io',
|
|
96
|
+
'is',
|
|
97
|
+
'it',
|
|
98
|
+
'iu',
|
|
99
|
+
'ja',
|
|
100
|
+
'jv',
|
|
101
|
+
'kl',
|
|
102
|
+
'kn',
|
|
103
|
+
'kr',
|
|
104
|
+
'ks',
|
|
105
|
+
'kk',
|
|
106
|
+
'km',
|
|
107
|
+
'ki',
|
|
108
|
+
'rw',
|
|
109
|
+
'ky',
|
|
110
|
+
'kv',
|
|
111
|
+
'kg',
|
|
112
|
+
'ko',
|
|
113
|
+
'ku',
|
|
114
|
+
'ckb',
|
|
115
|
+
'kj',
|
|
116
|
+
'la',
|
|
117
|
+
'lb',
|
|
118
|
+
'lg',
|
|
119
|
+
'li',
|
|
120
|
+
'ln',
|
|
121
|
+
'lo',
|
|
122
|
+
'lt',
|
|
123
|
+
'lu',
|
|
124
|
+
'lv',
|
|
125
|
+
'gv',
|
|
126
|
+
'mk',
|
|
127
|
+
'mg',
|
|
128
|
+
'ms',
|
|
129
|
+
'ml',
|
|
130
|
+
'mt',
|
|
131
|
+
'mi',
|
|
132
|
+
'mr',
|
|
133
|
+
'mh',
|
|
134
|
+
'mn',
|
|
135
|
+
'na',
|
|
136
|
+
'nv',
|
|
137
|
+
'nb',
|
|
138
|
+
'nd',
|
|
139
|
+
'ne',
|
|
140
|
+
'ng',
|
|
141
|
+
'nn',
|
|
142
|
+
'no',
|
|
143
|
+
'ii',
|
|
144
|
+
'nr',
|
|
145
|
+
'oc',
|
|
146
|
+
'oj',
|
|
147
|
+
'cu',
|
|
148
|
+
'om',
|
|
149
|
+
'or',
|
|
150
|
+
'os',
|
|
151
|
+
'pa',
|
|
152
|
+
'pi',
|
|
153
|
+
'fa',
|
|
154
|
+
'pl',
|
|
155
|
+
'ps',
|
|
156
|
+
'pt',
|
|
157
|
+
'qu',
|
|
158
|
+
'rm',
|
|
159
|
+
'rn',
|
|
160
|
+
'ro',
|
|
161
|
+
'ru',
|
|
162
|
+
'sa',
|
|
163
|
+
'sc',
|
|
164
|
+
'sd',
|
|
165
|
+
'se',
|
|
166
|
+
'sm',
|
|
167
|
+
'sg',
|
|
168
|
+
'sr',
|
|
169
|
+
'gd',
|
|
170
|
+
'sn',
|
|
171
|
+
'si',
|
|
172
|
+
'sk',
|
|
173
|
+
'sl',
|
|
174
|
+
'so',
|
|
175
|
+
'st',
|
|
176
|
+
'es',
|
|
177
|
+
'su',
|
|
178
|
+
'sw',
|
|
179
|
+
'ss',
|
|
180
|
+
'sv',
|
|
181
|
+
'ta',
|
|
182
|
+
'te',
|
|
183
|
+
'tg',
|
|
184
|
+
'th',
|
|
185
|
+
'ti',
|
|
186
|
+
'bo',
|
|
187
|
+
'tk',
|
|
188
|
+
'tl',
|
|
189
|
+
'tn',
|
|
190
|
+
'to',
|
|
191
|
+
'tr',
|
|
192
|
+
'ts',
|
|
193
|
+
'tt',
|
|
194
|
+
'tw',
|
|
195
|
+
'ty',
|
|
196
|
+
'ug',
|
|
197
|
+
'uk',
|
|
198
|
+
'ur',
|
|
199
|
+
'uz',
|
|
200
|
+
've',
|
|
201
|
+
'vi',
|
|
202
|
+
'vo',
|
|
203
|
+
'wa',
|
|
204
|
+
'cy',
|
|
205
|
+
'wo',
|
|
206
|
+
'fy',
|
|
207
|
+
'xh',
|
|
208
|
+
'yi',
|
|
209
|
+
'yo',
|
|
210
|
+
'za'
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
export { lngCodes as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
async function mapLimit (arr, limit, asyncFn) {
|
|
2
|
+
const ret = [];
|
|
3
|
+
let i = 0;
|
|
4
|
+
let active = 0;
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
function next () {
|
|
7
|
+
if (i === arr.length && active === 0) return resolve(ret)
|
|
8
|
+
while (active < limit && i < arr.length) {
|
|
9
|
+
const cur = i++;
|
|
10
|
+
active++;
|
|
11
|
+
Promise.resolve(asyncFn(arr[cur], cur, arr))
|
|
12
|
+
.then((res) => { ret[cur] = res; active--; next(); })
|
|
13
|
+
.catch(reject);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
next();
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { mapLimit as default };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import colors from 'colors';
|
|
2
|
+
import request from './request.js';
|
|
3
|
+
import getBranches from './getBranches.js';
|
|
4
|
+
import isValidUuid from './isValidUuid.js';
|
|
5
|
+
import getJob from './getJob.js';
|
|
6
|
+
|
|
7
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
8
|
+
|
|
9
|
+
const merge = async (opt) => {
|
|
10
|
+
const queryParams = new URLSearchParams();
|
|
11
|
+
if (opt.delete) {
|
|
12
|
+
queryParams.append('delete', 'true');
|
|
13
|
+
}
|
|
14
|
+
const queryString = queryParams.size > 0 ? '?' + queryParams.toString() : '';
|
|
15
|
+
const { res, obj, err } = await request(opt.apiEndpoint + '/branch/merge/' + opt.branch + queryString, {
|
|
16
|
+
method: 'post',
|
|
17
|
+
headers: {
|
|
18
|
+
Authorization: opt.apiKey
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
if (err || (obj && (obj.errorMessage || obj.message))) {
|
|
22
|
+
console.log(colors.red('merging branch failed...'));
|
|
23
|
+
if (err) {
|
|
24
|
+
console.error(colors.red(err.message));
|
|
25
|
+
throw err
|
|
26
|
+
}
|
|
27
|
+
if (obj && (obj.errorMessage || obj.message)) {
|
|
28
|
+
console.error(colors.red((obj.errorMessage || obj.message)));
|
|
29
|
+
throw new Error((obj.errorMessage || obj.message))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (res.status === 404) {
|
|
33
|
+
console.error(colors.yellow(res.statusText + ' (' + res.status + ')'));
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
if (res.status >= 300) {
|
|
37
|
+
console.error(colors.red(res.statusText + ' (' + res.status + ')'));
|
|
38
|
+
throw new Error(res.statusText + ' (' + res.status + ')')
|
|
39
|
+
}
|
|
40
|
+
if (!obj || !obj.jobId) {
|
|
41
|
+
console.error(colors.red('No jobId! Something went wrong!'));
|
|
42
|
+
throw new Error('No jobId! Something went wrong!')
|
|
43
|
+
}
|
|
44
|
+
let job;
|
|
45
|
+
while (true) {
|
|
46
|
+
job = await getJob({
|
|
47
|
+
apiEndpoint: opt.apiEndpoint,
|
|
48
|
+
apiKey: opt.apiKey,
|
|
49
|
+
projectId: opt.branch
|
|
50
|
+
}, obj.jobId);
|
|
51
|
+
if (job && !job.timeouted) {
|
|
52
|
+
await sleep(2000);
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if (job && job.timeouted) {
|
|
56
|
+
console.error(colors.red('Job timeouted!'));
|
|
57
|
+
throw new Error('Job timeouted!')
|
|
58
|
+
}
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
console.log(colors.green('merging branch successful'));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// handleError removed (unused)
|
|
65
|
+
|
|
66
|
+
const mergeBranch = async (opt) => {
|
|
67
|
+
const branches = await getBranches(opt);
|
|
68
|
+
let b;
|
|
69
|
+
if (isValidUuid(opt.branch)) b = branches.find((br) => br.id === opt.branch);
|
|
70
|
+
if (!b) b = branches.find((br) => br.name === opt.branch);
|
|
71
|
+
if (!b) {
|
|
72
|
+
throw new Error(`Branch ${opt.branch} not found!`)
|
|
73
|
+
}
|
|
74
|
+
opt.branch = b.id;
|
|
75
|
+
await merge(opt);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export { mergeBranch as default };
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import flatten from 'flat';
|
|
4
|
+
import colors from 'colors';
|
|
5
|
+
import request from './request.js';
|
|
6
|
+
import getRemoteLanguages from './getRemoteLanguages.js';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import mapLimit from './mapLimit.js';
|
|
9
|
+
|
|
10
|
+
const getDirectories = (srcpath) => {
|
|
11
|
+
return fs.readdirSync(srcpath).filter(function (file) {
|
|
12
|
+
return fs.statSync(path.join(srcpath, file)).isDirectory()
|
|
13
|
+
})
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getFiles = (srcpath) => {
|
|
17
|
+
return fs.readdirSync(srcpath).filter(function (file) {
|
|
18
|
+
return !fs.statSync(path.join(srcpath, file)).isDirectory()
|
|
19
|
+
})
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const load = async (namespaces) => {
|
|
23
|
+
await Promise.all(namespaces.map(async (ns) => {
|
|
24
|
+
try {
|
|
25
|
+
const data = await fs.promises.readFile(ns.path, 'utf8');
|
|
26
|
+
ns.value = flatten(JSON.parse(data));
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(colors.red(err.stack));
|
|
29
|
+
ns.value = {};
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
return namespaces
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const parseLanguage = async (p) => {
|
|
36
|
+
const dirs = getDirectories(p);
|
|
37
|
+
const namespaces = [];
|
|
38
|
+
dirs.forEach((lng) => {
|
|
39
|
+
const files = getFiles(path.join(p, lng));
|
|
40
|
+
files.forEach((file) => {
|
|
41
|
+
if (path.extname(file) !== '.json') return
|
|
42
|
+
namespaces.push({
|
|
43
|
+
language: lng,
|
|
44
|
+
namespace: path.basename(file, '.json'),
|
|
45
|
+
path: path.join(p, lng, file)
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
return await load(namespaces)
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const transfer = async (opt, ns) => {
|
|
53
|
+
let url = `${opt.apiEndpoint}/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}`
|
|
54
|
+
.replace('{{projectId}}', opt.projectId)
|
|
55
|
+
.replace('{{ver}}', opt.version)
|
|
56
|
+
.replace('{{version}}', opt.version)
|
|
57
|
+
.replace('{{language}}', ns.language)
|
|
58
|
+
.replace('{{lng}}', ns.language)
|
|
59
|
+
.replace('{{ns}}', ns.namespace)
|
|
60
|
+
.replace('{{namespace}}', ns.namespace);
|
|
61
|
+
|
|
62
|
+
console.log(colors.yellow(`transfering ${opt.version}/${ns.language}/${ns.namespace}...`));
|
|
63
|
+
|
|
64
|
+
if (!opt.replace) url = url.replace('/update/', '/missing/');
|
|
65
|
+
|
|
66
|
+
const data = ns.value;
|
|
67
|
+
const keysToSend = Object.keys(data).length;
|
|
68
|
+
if (keysToSend === 0) return
|
|
69
|
+
|
|
70
|
+
const payloadKeysLimit = 1000;
|
|
71
|
+
|
|
72
|
+
async function send (d, so, isFirst, isRetrying = false) {
|
|
73
|
+
const queryParams = new URLSearchParams();
|
|
74
|
+
if (so) {
|
|
75
|
+
queryParams.append('omitstatsgeneration', 'true');
|
|
76
|
+
}
|
|
77
|
+
if (isFirst && opt.replace) {
|
|
78
|
+
queryParams.append('replace', 'true');
|
|
79
|
+
}
|
|
80
|
+
const queryString = queryParams.size > 0 ? '?' + queryParams.toString() : '';
|
|
81
|
+
try {
|
|
82
|
+
const { res, obj } = await request(url + queryString, {
|
|
83
|
+
method: 'post',
|
|
84
|
+
body: d,
|
|
85
|
+
headers: {
|
|
86
|
+
Authorization: opt.apiKey
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
if (url.indexOf('/missing/') > -1 && res.status === 412) {
|
|
90
|
+
console.log(colors.green(`transfered ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace} (but all keys already existed)...`));
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
if (res.status === 504 && !isRetrying) {
|
|
94
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
95
|
+
return send(d, so, isFirst, true)
|
|
96
|
+
}
|
|
97
|
+
if (res.status >= 300 && res.status !== 412) {
|
|
98
|
+
if (obj && (obj.errorMessage || obj.message)) {
|
|
99
|
+
throw new Error((obj.errorMessage || obj.message))
|
|
100
|
+
}
|
|
101
|
+
throw new Error(res.statusText + ' (' + res.status + ')')
|
|
102
|
+
}
|
|
103
|
+
console.log(colors.green(`transfered ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace}...`));
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.log(colors.red(`transfer failed for ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace}...`));
|
|
106
|
+
throw err
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (keysToSend > payloadKeysLimit) {
|
|
111
|
+
const keysInObj = Object.keys(data);
|
|
112
|
+
let isFirst = true;
|
|
113
|
+
while (keysInObj.length > payloadKeysLimit) {
|
|
114
|
+
const pagedData = {};
|
|
115
|
+
keysInObj.splice(0, payloadKeysLimit).forEach((k) => { pagedData[k] = data[k]; });
|
|
116
|
+
const hasMoreKeys = keysInObj.length > 0;
|
|
117
|
+
await send(pagedData, hasMoreKeys, isFirst);
|
|
118
|
+
isFirst = false;
|
|
119
|
+
}
|
|
120
|
+
if (keysInObj.length === 0) return
|
|
121
|
+
const finalPagedData = {};
|
|
122
|
+
keysInObj.splice(0, keysInObj.length).forEach((k) => { finalPagedData[k] = data[k]; });
|
|
123
|
+
await send(finalPagedData, false, isFirst);
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await send(data, false, true);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const upload = async (opt, nss) => {
|
|
131
|
+
const concurrency = os.cpus().length;
|
|
132
|
+
if (!opt.referenceLanguage) {
|
|
133
|
+
await mapLimit(nss, concurrency, async (ns) => transfer(opt, ns));
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const nssRefLng = nss.filter((n) => n.language === opt.referenceLanguage);
|
|
138
|
+
const nssNonRefLng = nss.filter((n) => n.language !== opt.referenceLanguage);
|
|
139
|
+
|
|
140
|
+
// Reference language first, then others, but each group in parallel
|
|
141
|
+
await mapLimit(nssRefLng, concurrency, async (ns) => transfer(opt, ns));
|
|
142
|
+
await mapLimit(nssNonRefLng, concurrency, async (ns) => transfer(opt, ns));
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const addLanguage = async (opt, l) => {
|
|
146
|
+
const url = opt.apiEndpoint + '/language/' + opt.projectId + '/' + l;
|
|
147
|
+
try {
|
|
148
|
+
const { res } = await request(url, {
|
|
149
|
+
method: 'post',
|
|
150
|
+
headers: {
|
|
151
|
+
Authorization: opt.apiKey
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (res.status >= 300 && res.status !== 412) throw new Error(res.statusText + ' (' + res.status + ')')
|
|
155
|
+
console.log(colors.green(`added language ${l}...`));
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.log(colors.red(`failed to add language ${l}...`));
|
|
158
|
+
throw err
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const migrate = async (opt) => {
|
|
163
|
+
if (opt.format !== 'json') {
|
|
164
|
+
throw new Error(`Format ${opt.format} is not accepted!`)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
opt.apiEndpoint = opt.apiEndpoint || 'https://api.locize.app';
|
|
168
|
+
|
|
169
|
+
if (opt.language) {
|
|
170
|
+
const files = getFiles(opt.path);
|
|
171
|
+
const namespaces = files.map((file) => ({
|
|
172
|
+
language: opt.language,
|
|
173
|
+
namespace: path.basename(file, '.json'),
|
|
174
|
+
path: path.join(opt.path, file)
|
|
175
|
+
}));
|
|
176
|
+
let nss;
|
|
177
|
+
try {
|
|
178
|
+
nss = await load(namespaces);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error(colors.red(err.stack));
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
await upload(opt, nss);
|
|
185
|
+
console.log(colors.green('FINISHED'));
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(colors.red(err.stack));
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (opt.parseLanguage) {
|
|
194
|
+
let nss;
|
|
195
|
+
try {
|
|
196
|
+
nss = await parseLanguage(opt.path);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error(colors.red(err.stack));
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
let remoteLanguages;
|
|
202
|
+
try {
|
|
203
|
+
remoteLanguages = await getRemoteLanguages(opt);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error(colors.red(err.stack));
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
const localLanguages = [];
|
|
209
|
+
nss.forEach((n) => {
|
|
210
|
+
if (localLanguages.indexOf(n.language) < 0) localLanguages.push(n.language);
|
|
211
|
+
});
|
|
212
|
+
const notExistingLanguages = localLanguages.filter((l) => remoteLanguages.indexOf(l) < 0);
|
|
213
|
+
if (notExistingLanguages.length === 0) {
|
|
214
|
+
try {
|
|
215
|
+
await upload(opt, nss);
|
|
216
|
+
console.log(colors.green('FINISHED'));
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.error(colors.red(err.stack));
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
for (const l of notExistingLanguages) {
|
|
225
|
+
await addLanguage(opt, l);
|
|
226
|
+
}
|
|
227
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
228
|
+
await upload(opt, nss);
|
|
229
|
+
console.log(colors.green('FINISHED'));
|
|
230
|
+
} catch (err) {
|
|
231
|
+
console.error(colors.red(err.stack));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export { migrate as default };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import colors from 'colors';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import request from './request.js';
|
|
4
|
+
import { reversedFileExtensionsMap as reversedFileExtensionsMap$1 } from './formats.js';
|
|
5
|
+
import getRemoteLanguages from './getRemoteLanguages.js';
|
|
6
|
+
import parseLocalReference from './parseLocalReference.js';
|
|
7
|
+
import parseLocalLanguages from './parseLocalLanguages.js';
|
|
8
|
+
import getRemoteNamespace from './getRemoteNamespace.js';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import mapLimit from './mapLimit.js';
|
|
11
|
+
|
|
12
|
+
const reversedFileExtensionsMap = reversedFileExtensionsMap$1;
|
|
13
|
+
|
|
14
|
+
const compareNamespace = (local, remote) => {
|
|
15
|
+
const diff = {
|
|
16
|
+
toAdd: []
|
|
17
|
+
};
|
|
18
|
+
local = local || {};
|
|
19
|
+
remote = remote || {};
|
|
20
|
+
Object.keys(local).forEach((k) => {
|
|
21
|
+
if (remote[k] === '' && local[k] === '') return
|
|
22
|
+
if (!remote[k]) {
|
|
23
|
+
diff.toAdd.push(k);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return diff
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const compareNamespaces = async (opt, localNamespaces) => {
|
|
30
|
+
return await Promise.all(localNamespaces.map(async (ns) => {
|
|
31
|
+
const { result: remoteNamespace } = await getRemoteNamespace(opt, ns.language, ns.namespace);
|
|
32
|
+
const diff = compareNamespace(ns.content, remoteNamespace);
|
|
33
|
+
ns.diff = diff;
|
|
34
|
+
ns.remoteContent = remoteNamespace;
|
|
35
|
+
return ns
|
|
36
|
+
}))
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const saveMissing = async (opt, lng, ns) => {
|
|
40
|
+
const data = {};
|
|
41
|
+
ns.diff.toAdd.forEach((k) => { data[k] = ns.content[k]; });
|
|
42
|
+
if (Object.keys(data).length === 0 || opt.dry) return
|
|
43
|
+
const payloadKeysLimit = 1000;
|
|
44
|
+
async function send (d, isRetrying = false) {
|
|
45
|
+
const { res, obj } = await request(opt.apiEndpoint + '/missing/' + opt.projectId + '/' + opt.version + '/' + lng + '/' + ns.namespace, {
|
|
46
|
+
method: 'post',
|
|
47
|
+
body: d,
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: opt.apiKey
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (res.status === 504 && !isRetrying) {
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
54
|
+
return send(d, true)
|
|
55
|
+
}
|
|
56
|
+
if (res.status >= 300 && res.status !== 412) {
|
|
57
|
+
if (obj && (obj.errorMessage || obj.message)) {
|
|
58
|
+
throw new Error((obj.errorMessage || obj.message))
|
|
59
|
+
}
|
|
60
|
+
throw new Error(res.statusText + ' (' + res.status + ')')
|
|
61
|
+
}
|
|
62
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
63
|
+
}
|
|
64
|
+
if (Object.keys(data).length > payloadKeysLimit) {
|
|
65
|
+
const keysInObj = Object.keys(data);
|
|
66
|
+
while (keysInObj.length > payloadKeysLimit) {
|
|
67
|
+
const pagedData = {};
|
|
68
|
+
keysInObj.splice(0, payloadKeysLimit).forEach((k) => { pagedData[k] = data[k]; });
|
|
69
|
+
await send(pagedData);
|
|
70
|
+
}
|
|
71
|
+
if (keysInObj.length === 0) return
|
|
72
|
+
const finalPagedData = {};
|
|
73
|
+
keysInObj.splice(0, keysInObj.length).forEach((k) => { finalPagedData[k] = data[k]; });
|
|
74
|
+
await send(finalPagedData);
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
await send(data);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleError = (err) => {
|
|
81
|
+
if (err) {
|
|
82
|
+
console.error(colors.red(err.stack));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const handleMissing = async (opt, localNamespaces) => {
|
|
88
|
+
if (!localNamespaces || localNamespaces.length === 0) {
|
|
89
|
+
handleError(new Error('No local namespaces found!'));
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
let compared;
|
|
93
|
+
try {
|
|
94
|
+
compared = await compareNamespaces(opt, localNamespaces);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
handleError(err);
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const concurrency = os.cpus().length;
|
|
100
|
+
await mapLimit(compared, concurrency, async (ns) => {
|
|
101
|
+
if (ns.diff.toAdd.length > 0) {
|
|
102
|
+
console.log(colors.green(`adding ${ns.diff.toAdd.length} keys in ${ns.language}/${ns.namespace}...`));
|
|
103
|
+
if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAdd.join(', ')} in ${ns.language}/${ns.namespace}...`));
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
await saveMissing(opt, ns.language, ns);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
handleError(err);
|
|
109
|
+
// Don't return here, continue with others
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
console.log(colors.green('FINISHED'));
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const missing = async (opt) => {
|
|
116
|
+
if (!reversedFileExtensionsMap[opt.format]) {
|
|
117
|
+
handleError(new Error(`${opt.format} is not a valid format!`));
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if (opt.namespace && opt.namespace.indexOf(',') > 0) {
|
|
121
|
+
opt.namespaces = opt.namespace.split(',');
|
|
122
|
+
delete opt.namespace;
|
|
123
|
+
}
|
|
124
|
+
opt.pathMaskInterpolationPrefix = opt.pathMaskInterpolationPrefix || '{{';
|
|
125
|
+
opt.pathMaskInterpolationSuffix = opt.pathMaskInterpolationSuffix || '}}';
|
|
126
|
+
opt.pathMask = opt.pathMask || `${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}${path.sep}${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`;
|
|
127
|
+
opt.languageFolderPrefix = opt.languageFolderPrefix || '';
|
|
128
|
+
opt.pathMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, `${opt.languageFolderPrefix}${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`);
|
|
129
|
+
let remoteLanguages;
|
|
130
|
+
try {
|
|
131
|
+
remoteLanguages = await getRemoteLanguages(opt);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
handleError(err);
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
if (opt.referenceLanguageOnly && opt.language && opt.referenceLanguage !== opt.language) {
|
|
137
|
+
opt.referenceLanguage = opt.language;
|
|
138
|
+
}
|
|
139
|
+
if (opt.referenceLanguageOnly) {
|
|
140
|
+
let localNamespaces;
|
|
141
|
+
try {
|
|
142
|
+
localNamespaces = await parseLocalReference(opt);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
handleError(err);
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
await handleMissing(opt, localNamespaces);
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
let localNamespaces;
|
|
151
|
+
try {
|
|
152
|
+
localNamespaces = await parseLocalLanguages(opt, remoteLanguages);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
handleError(err);
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
await handleMissing(opt, localNamespaces);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export { missing as default };
|