locize-cli 7.7.0 → 7.8.1

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 CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  Project versioning adheres to [Semantic Versioning](http://semver.org/).
6
6
  Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
7
7
 
8
+ ## [7.8.1](https://github.com/locize/locize-cli/compare/v7.8.0...v7.8.1) - 2022-02-10
9
+
10
+ - sync: optimize handling if pathmask does not include namespace
11
+
12
+
13
+ ## [7.8.0](https://github.com/locize/locize-cli/compare/v7.7.2...v7.8.0) - 2022-01-13
14
+
15
+ - sync: introduce --delete-remote-namespace option
16
+
17
+
18
+ ## [7.7.2](https://github.com/locize/locize-cli/compare/v7.7.1...v7.7.2) - 2021-12-10
19
+
20
+ - update dependencies
21
+
22
+
23
+ ## [7.7.1](https://github.com/locize/locize-cli/compare/v7.7.0...v7.7.1) - 2021-12-10
24
+
25
+ - new binary build (x64)
26
+
27
+
8
28
  ## [7.7.0](https://github.com/locize/locize-cli/compare/v7.6.17...v7.7.0) - 2021-12-08
9
29
 
10
30
  - sync: optimize api requests on bigger projects
package/README.md CHANGED
@@ -91,7 +91,7 @@ locize download --project-id my-project-id-93e1-442a-ab35-24331fa294ba --ver lat
91
91
  By using the sync command, you can keep your existing code setup and synchronize the translations with locize.
92
92
  An example on how this could look like can be seen in [this tutorial](https://github.com/locize/react-tutorial#step-1---keep-existing-code-setup-but-synchronize-with-locize).
93
93
 
94
- **⚠️ Since the remote source are the published translations, make sure the desired version is set to standard publish mode, or alternatively [publish](#publish-version) that version before you execute the cli command. ⚠️**
94
+ **⚠️ Since the remote source are the published translations, make sure the desired version is set to standard/auto publish mode, or alternatively [publish](#publish-version) that version before you execute the cli command. ⚠️**
95
95
 
96
96
  ### Step 1: Go near to your translation files
97
97
 
package/bin/locize CHANGED
@@ -368,6 +368,7 @@ program
368
368
  .option('-cf, --clean-local-files <true|false>', 'Removes all local files without removing any folder (default: false)', 'false')
369
369
  .option('-u, --update-values <true|false>', 'This will update values of existing translations. (default: false)', 'false')
370
370
  .option('-S, --skip-delete <true|false>', 'This will skip the removal of keys on locize. (default: false)', 'false')
371
+ .option('-D, --delete-remote-namespace <true|false>', 'This will delete a complete namespace on locize, if a local file in reference language was deleted. (default: false)', 'false')
371
372
  .option('-m, --path-mask <mask>', 'This will define the folder and file structure; do not add a file extension (default: {{language}}/{{namespace}})', `{{language}}${path.sep}{{namespace}}`)
372
373
  .option('-P, --language-folder-prefix <prefix>', 'This will be added as a local folder name prefix in front of the language.', '')
373
374
  .option('-d, --dry <true|false>', 'Dry run (default: false)', 'false')
@@ -417,6 +418,7 @@ program
417
418
  const dry = options.dry === 'true';
418
419
  const updateValues = options.updateValues === 'true';
419
420
  const skipDelete = options.skipDelete === 'true';
421
+ const deleteRemoteNamespace = options.deleteRemoteNamespace === 'true';
420
422
  const languageFolderPrefix = options.languageFolderPrefix || '';
421
423
  const skipEmpty = options.skipEmpty === 'true';
422
424
  const referenceLanguageOnly = options.referenceLanguageOnly === 'false' ? false : true;
@@ -432,6 +434,7 @@ program
432
434
  format: options.format,
433
435
  updateValues: updateValues,
434
436
  skipDelete: skipDelete,
437
+ deleteRemoteNamespace: deleteRemoteNamespace,
435
438
  languageFolderPrefix: languageFolderPrefix,
436
439
  clean: clean,
437
440
  cleanLocalFiles: cleanLocalFiles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locize-cli",
3
- "version": "7.7.0",
3
+ "version": "7.8.1",
4
4
  "description": "locize cli to import locales",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,37 +9,37 @@
9
9
  "dependencies": {
10
10
  "@js.properties/properties": "0.5.4",
11
11
  "android-string-resource": "2.3.4",
12
- "async": "3.2.1",
12
+ "async": "3.2.3",
13
13
  "colors": "1.4.0",
14
14
  "commander": "7.2.0",
15
15
  "csvjson": "5.1.0",
16
16
  "diff": "5.0.0",
17
17
  "flat": "5.0.2",
18
18
  "fluent_conv": "3.1.0",
19
- "gettext-converter": "1.1.0",
19
+ "gettext-converter": "1.2.0",
20
20
  "https-proxy-agent": "5.0.0",
21
21
  "ini": "2.0.0",
22
22
  "js-yaml": "4.1.0",
23
23
  "laravelphp": "2.0.3",
24
24
  "lodash.clonedeep": "4.5.0",
25
25
  "mkdirp": "1.0.4",
26
- "node-fetch": "2.6.2",
26
+ "node-fetch": "2.6.7",
27
27
  "resx": "2.0.3",
28
28
  "rimraf": "3.0.2",
29
29
  "strings-file": "0.0.5",
30
30
  "tmexchange": "2.0.4",
31
- "xliff": "5.6.2",
32
- "xlsx": "0.17.2"
31
+ "xliff": "6.0.1",
32
+ "xlsx": "0.18.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "eslint": "7.32.0",
36
- "gh-release": "6.0.0",
37
- "pkg": "5.3.2"
36
+ "gh-release": "6.0.1",
37
+ "pkg": "5.5.2"
38
38
  },
39
39
  "scripts": {
40
40
  "lint": "eslint .",
41
41
  "test": "npm run lint",
42
- "pkg": "mkdir -p ./bins && pkg ./bin/locize --out-path ./bins",
42
+ "pkg": "mkdir -p ./bins && pkg ./bin/locize --out-path ./bins --targets node16-linux-x64,node16-macos-x64,node16-win-x64",
43
43
  "release": "gh-release --assets ./bins/locize-linux,./bins/locize-macos,./bins/locize-win.exe",
44
44
  "version": "npm run pkg",
45
45
  "postversion": "git push && npm run release"
@@ -20,6 +20,7 @@ const getDirectories = (srcpath) => {
20
20
  };
21
21
 
22
22
  const parseLocalLanguage = (opt, lng, cb) => {
23
+ const hasNamespaceInPath = opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`) > -1;
23
24
  const filledLngMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, lng);
24
25
  var firstPartLngMask, lastPartLngMask;
25
26
  if (opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`) > opt.pathMask.indexOf(`${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`)) {
@@ -114,6 +115,8 @@ const parseLocalLanguage = (opt, lng, cb) => {
114
115
  } else {
115
116
  namespace = dirPath.substring(filledNsMask.indexOf(nsMask));
116
117
  }
118
+ } else if (!hasNamespaceInPath && startNsIndex < 0) {
119
+ namespace = opt.namespace;
117
120
  }
118
121
  var fPath = path.join(opt.path, lngPath || '', file);
119
122
  if (dirPath && lngPath.indexOf(nsMask) > -1) {
package/sync.js CHANGED
@@ -14,6 +14,7 @@ const parseLocalLanguages = require('./parseLocalLanguages');
14
14
  const parseLocalReference = require('./parseLocalReference');
15
15
  const formats = require('./formats');
16
16
  const lngCodes = require('./lngs.json');
17
+ const deleteNamespace = require('./deleteNamespace');
17
18
  const reversedFileExtensionsMap = formats.reversedFileExtensionsMap;
18
19
 
19
20
  const getDirectories = (srcpath) => {
@@ -303,6 +304,26 @@ const handleError = (err, cb) => {
303
304
  if (cb) cb(err);
304
305
  };
305
306
 
307
+ const checkForMissingLocalNamespaces = (downloads, localNamespaces, opt) => {
308
+ const localMissingNamespaces = [];
309
+ downloads.forEach((d) => {
310
+ const splitted = d.url.split('/');
311
+ const namespace = splitted.pop();
312
+ const language = splitted.pop();
313
+ // if (!opt.referenceLanguageOnly || (opt.referenceLanguageOnly && language === opt.referenceLanguage)) {
314
+ if (language === opt.referenceLanguage) {
315
+ const foundLocalNamespace = localNamespaces.find((n) => n.namespace === namespace && n.language === language);
316
+ if (!foundLocalNamespace) {
317
+ localMissingNamespaces.push({
318
+ language,
319
+ namespace
320
+ });
321
+ }
322
+ }
323
+ });
324
+ return localMissingNamespaces;
325
+ };
326
+
306
327
  const handleSync = (opt, remoteLanguages, localNamespaces, cb) => {
307
328
  if (!localNamespaces || localNamespaces.length === 0) {
308
329
  downloadAll(opt, remoteLanguages, (err) => {
@@ -318,6 +339,8 @@ const handleSync = (opt, remoteLanguages, localNamespaces, cb) => {
318
339
 
319
340
  opt.isPrivate = downloads.length > 0 && downloads[0].isPrivate;
320
341
 
342
+ const localMissingNamespaces = checkForMissingLocalNamespaces(downloads, localNamespaces, opt);
343
+
321
344
  compareNamespaces(opt, localNamespaces, (err, compared) => {
322
345
  if (err) return handleError(err);
323
346
 
@@ -336,105 +359,131 @@ const handleSync = (opt, remoteLanguages, localNamespaces, cb) => {
336
359
  const shouldOmit = lngsInReqs.length > 5 || nsInReqs.length > 5;
337
360
 
338
361
  var wasThereSomethingToUpdate = false;
339
- async.eachLimit(compared, Math.round(require('os').cpus().length / 2), (ns, clb) => {
340
- if (!cb) {
341
- if (ns.diff.toRemove.length > 0) {
342
- if (opt.skipDelete) {
343
- console.log(colors.bgRed(`skipping the removal of ${ns.diff.toRemove.length} keys in ${ns.language}/${ns.namespace}...`));
344
- if (opt.dry) console.log(colors.bgRed(`skipped to remove ${ns.diff.toRemove.join(', ')} in ${ns.language}/${ns.namespace}...`));
345
- } else {
346
- console.log(colors.red(`removing ${ns.diff.toRemove.length} keys in ${ns.language}/${ns.namespace}...`));
347
- if (opt.dry) console.log(colors.red(`would remove ${ns.diff.toRemove.join(', ')} in ${ns.language}/${ns.namespace}...`));
362
+
363
+ function updateComparedNamespaces() {
364
+ async.eachLimit(compared, Math.round(require('os').cpus().length / 2), (ns, clb) => {
365
+ if (!cb) {
366
+ if (ns.diff.toRemove.length > 0) {
367
+ if (opt.skipDelete) {
368
+ console.log(colors.bgRed(`skipping the removal of ${ns.diff.toRemove.length} keys in ${ns.language}/${ns.namespace}...`));
369
+ if (opt.dry) console.log(colors.bgRed(`skipped to remove ${ns.diff.toRemove.join(', ')} in ${ns.language}/${ns.namespace}...`));
370
+ } else {
371
+ console.log(colors.red(`removing ${ns.diff.toRemove.length} keys in ${ns.language}/${ns.namespace}...`));
372
+ if (opt.dry) console.log(colors.red(`would remove ${ns.diff.toRemove.join(', ')} in ${ns.language}/${ns.namespace}...`));
373
+ }
348
374
  }
349
- }
350
- if (ns.diff.toRemoveLocally.length > 0) {
351
- console.log(colors.red(`removing ${ns.diff.toRemoveLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
352
- if (opt.dry) console.log(colors.red(`would remove ${ns.diff.toRemoveLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
353
- }
354
- if (ns.diff.toAdd.length > 0) {
355
- console.log(colors.green(`adding ${ns.diff.toAdd.length} keys in ${ns.language}/${ns.namespace}...`));
356
- if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAdd.join(', ')} in ${ns.language}/${ns.namespace}...`));
357
- }
358
- if (ns.diff.toAddLocally.length > 0) {
359
- if (opt.skipDelete) {
360
- console.log(colors.bgGreen(`skipping the addition of ${ns.diff.toAddLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
361
- if (opt.dry) console.log(colors.bgGreen(`skipped the addition of ${ns.diff.toAddLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
362
- } else {
363
- console.log(colors.green(`adding ${ns.diff.toAddLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
364
- if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAddLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
375
+ if (ns.diff.toRemoveLocally.length > 0) {
376
+ console.log(colors.red(`removing ${ns.diff.toRemoveLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
377
+ if (opt.dry) console.log(colors.red(`would remove ${ns.diff.toRemoveLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
365
378
  }
366
- }
367
- if (opt.updateValues) {
368
- if (ns.diff.toUpdate.length > 0) {
369
- console.log(colors.yellow(`updating ${ns.diff.toUpdate.length} keys in ${ns.language}/${ns.namespace}...`));
370
- if (opt.dry) console.log(colors.yellow(`would update ${ns.diff.toUpdate.join(', ')} in ${ns.language}/${ns.namespace}...`));
379
+ if (ns.diff.toAdd.length > 0) {
380
+ console.log(colors.green(`adding ${ns.diff.toAdd.length} keys in ${ns.language}/${ns.namespace}...`));
381
+ if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAdd.join(', ')} in ${ns.language}/${ns.namespace}...`));
371
382
  }
372
- if (ns.diff.toUpdateLocally.length > 0) {
373
- console.log(colors.yellow(`updating ${ns.diff.toUpdateLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
374
- if (opt.dry) console.log(colors.yellow(`would update ${ns.diff.toUpdateLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
383
+ if (ns.diff.toAddLocally.length > 0) {
384
+ if (opt.skipDelete) {
385
+ console.log(colors.bgGreen(`skipping the addition of ${ns.diff.toAddLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
386
+ if (opt.dry) console.log(colors.bgGreen(`skipped the addition of ${ns.diff.toAddLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
387
+ } else {
388
+ console.log(colors.green(`adding ${ns.diff.toAddLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
389
+ if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAddLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
390
+ }
391
+ }
392
+ if (opt.updateValues) {
393
+ if (ns.diff.toUpdate.length > 0) {
394
+ console.log(colors.yellow(`updating ${ns.diff.toUpdate.length} keys in ${ns.language}/${ns.namespace}...`));
395
+ if (opt.dry) console.log(colors.yellow(`would update ${ns.diff.toUpdate.join(', ')} in ${ns.language}/${ns.namespace}...`));
396
+ }
397
+ if (ns.diff.toUpdateLocally.length > 0) {
398
+ console.log(colors.yellow(`updating ${ns.diff.toUpdateLocally.length} keys in ${ns.language}/${ns.namespace} locally...`));
399
+ if (opt.dry) console.log(colors.yellow(`would update ${ns.diff.toUpdateLocally.join(', ')} in ${ns.language}/${ns.namespace} locally...`));
400
+ }
375
401
  }
402
+ const somethingToUpdate = ns.diff.toAdd.concat(opt.skipDelete ? [] : ns.diff.toRemove)/*.concat(ns.diff.toUpdate)*/.length > 0;
403
+ if (!somethingToUpdate) console.log(colors.grey(`nothing to update for ${ns.language}/${ns.namespace}`));
404
+ if (!wasThereSomethingToUpdate && somethingToUpdate) wasThereSomethingToUpdate = true;
376
405
  }
377
- const somethingToUpdate = ns.diff.toAdd.concat(opt.skipDelete ? [] : ns.diff.toRemove)/*.concat(ns.diff.toUpdate)*/.length > 0;
378
- if (!somethingToUpdate) console.log(colors.grey(`nothing to update for ${ns.language}/${ns.namespace}`));
379
- if (!wasThereSomethingToUpdate && somethingToUpdate) wasThereSomethingToUpdate = true;
380
- }
381
- update(opt, ns.language, ns, shouldOmit, (err) => {
382
- if (err) return clb(err);
383
- if (ns.diff.toRemove.length === 0 || ns.language !== opt.referenceLanguage) return clb();
384
- const nsOnlyRemove = cloneDeep(ns);
385
- nsOnlyRemove.diff.toAdd = [];
386
- nsOnlyRemove.diff.toUpdate = [];
387
- async.eachLimit(remoteLanguages, Math.round(require('os').cpus().length / 2), (lng, clb) => update(opt, lng, nsOnlyRemove, shouldOmit, clb), clb);
388
- });
389
- }, (err) => {
390
- if (err) return handleError(err);
391
- if (!cb) console.log(colors.grey('syncing...'));
392
-
393
- function down() {
394
- setTimeout(() => { // wait a bit before downloading... just to have a chance to get the newly published files
395
- downloadAll(opt, remoteLanguages, false, opt.skipDelete ? (lng, namespace, ns) => {
396
- const found = compared.find((n) => n.namespace === namespace && n.language === lng);
397
- if (found && found.diff) {
398
- if (found.diff.toAddLocally && found.diff.toAddLocally.length > 0) {
399
- found.diff.toAddLocally.forEach((k) => {
400
- delete ns[k];
401
- });
402
- }
403
- if (found.diff.toRemove && found.diff.toRemove.length > 0) {
404
- found.diff.toRemove.forEach((k) => {
405
- delete ns[k];
406
- });
406
+ update(opt, ns.language, ns, shouldOmit, (err) => {
407
+ if (err) return clb(err);
408
+ if (ns.diff.toRemove.length === 0 || ns.language !== opt.referenceLanguage) return clb();
409
+ const nsOnlyRemove = cloneDeep(ns);
410
+ nsOnlyRemove.diff.toAdd = [];
411
+ nsOnlyRemove.diff.toUpdate = [];
412
+ async.eachLimit(remoteLanguages, Math.round(require('os').cpus().length / 2), (lng, clb) => update(opt, lng, nsOnlyRemove, shouldOmit, clb), clb);
413
+ });
414
+ }, (err) => {
415
+ if (err) return handleError(err);
416
+ if (!cb) console.log(colors.grey('syncing...'));
417
+
418
+ function down() {
419
+ setTimeout(() => { // wait a bit before downloading... just to have a chance to get the newly published files
420
+ downloadAll(opt, remoteLanguages, false, opt.skipDelete ? (lng, namespace, ns) => {
421
+ const found = compared.find((n) => n.namespace === namespace && n.language === lng);
422
+ if (found && found.diff) {
423
+ if (found.diff.toAddLocally && found.diff.toAddLocally.length > 0) {
424
+ found.diff.toAddLocally.forEach((k) => {
425
+ delete ns[k];
426
+ });
427
+ }
428
+ if (found.diff.toRemove && found.diff.toRemove.length > 0) {
429
+ found.diff.toRemove.forEach((k) => {
430
+ delete ns[k];
431
+ });
432
+ }
407
433
  }
408
- }
409
- } : undefined, (err) => {
410
- if (err) return handleError(err);
411
- if (!cb) console.log(colors.green('FINISHED'));
412
- if (cb) cb(null);
413
- });
414
- }, wasThereSomethingToUpdate && !opt.dry ? 5000 : 0);
415
- }
434
+ } : undefined, (err) => {
435
+ if (err) return handleError(err);
436
+ if (!cb) console.log(colors.green('FINISHED'));
437
+ if (cb) cb(null);
438
+ });
439
+ }, wasThereSomethingToUpdate && !opt.dry ? 5000 : 0);
440
+ }
416
441
 
417
- if (!shouldOmit) return down();
418
- if (opt.dry) return down();
442
+ if (!shouldOmit) return down();
443
+ if (opt.dry) return down();
419
444
 
420
- // optimize stats generation...
421
- request(opt.apiPath + '/stats/project/regenerate/' + opt.projectId + '/' + opt.version + (lngsInReqs.length === 1 ? `/${lngsInReqs[0]}` : '') + (nsInReqs.length === 1 ? `?namespace=${nsInReqs[0]}` : ''), {
422
- method: 'post',
423
- body: {},
424
- headers: {
425
- 'Authorization': opt.apiKey
426
- }
427
- }, (err, res, obj) => {
428
- if (err) return handleError(err);
429
- if (res.status >= 300 && res.status !== 412) {
430
- if (obj && (obj.errorMessage || obj.message)) {
431
- return handleError(new Error((obj.errorMessage || obj.message)));
445
+ // optimize stats generation...
446
+ request(opt.apiPath + '/stats/project/regenerate/' + opt.projectId + '/' + opt.version + (lngsInReqs.length === 1 ? `/${lngsInReqs[0]}` : '') + (nsInReqs.length === 1 ? `?namespace=${nsInReqs[0]}` : ''), {
447
+ method: 'post',
448
+ body: {},
449
+ headers: {
450
+ 'Authorization': opt.apiKey
451
+ }
452
+ }, (err, res, obj) => {
453
+ if (err) return handleError(err);
454
+ if (res.status >= 300 && res.status !== 412) {
455
+ if (obj && (obj.errorMessage || obj.message)) {
456
+ return handleError(new Error((obj.errorMessage || obj.message)));
457
+ }
458
+ return handleError(new Error(res.statusText + ' (' + res.status + ')'));
432
459
  }
433
- return handleError(new Error(res.statusText + ' (' + res.status + ')'));
460
+ down();
461
+ });
462
+ });
463
+ }
464
+
465
+ if (opt.deleteRemoteNamespace && localMissingNamespaces.length > 0) {
466
+ wasThereSomethingToUpdate = true;
467
+ async.each(localMissingNamespaces, (n, clb) => {
468
+ if (opt.dry) {
469
+ console.log(colors.red(`would delete complete namespace ${n.namespace}...`));
470
+ return clb();
434
471
  }
435
- down();
472
+ console.log(colors.red(`deleting complete namespace ${n.namespace}...`));
473
+ deleteNamespace({
474
+ apiPath: opt.apiPath,
475
+ apiKey: opt.apiKey,
476
+ projectId: opt.projectId,
477
+ version: opt.version,
478
+ namespace: n.namespace
479
+ }, clb);
480
+ }, (err) => {
481
+ if (err) return handleError(err);
482
+ updateComparedNamespaces();
436
483
  });
437
- });
484
+ return;
485
+ }
486
+ updateComparedNamespaces();
438
487
  });
439
488
  });
440
489
  };