locize-cli 10.1.0 → 10.2.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 CHANGED
@@ -5,6 +5,15 @@ 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
+
9
+ ## [10.2.0](https://github.com/locize/locize-cli/compare/v10.1.1...v10.2.0) - 2025-07-16
10
+
11
+ - download/sync introduce `--overridden-only` option for branches or tenants
12
+
13
+ ## [10.1.1](https://github.com/locize/locize-cli/compare/v10.1.0...v10.1.1) - 2025-07-12
14
+
15
+ - create-branch: nicer feedback, if branch already exists
16
+
8
17
  ## [10.1.0](https://github.com/locize/locize-cli/compare/v10.0.3...v10.1.0) - 2025-07-10
9
18
 
10
19
  - introduce branching features
package/bin/locize CHANGED
@@ -241,6 +241,7 @@ program
241
241
  .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}}`)
242
242
  .option('-c, --clean <true|false>', 'Removes all local files by removing the whole folder (default: false)', 'false')
243
243
  .option('-up, --unpublished <true|false>', 'Downloads the current (unpublished) translations. This will generate private download costs (default: false)', 'false')
244
+ .option('-oo, --overridden-only <true|false>', 'Downloads only the current overridden (unpublished) translations of a tenant or branch project. This will generate private download costs (default: false)', 'false')
244
245
  .option('-b, --branch <branch>', 'The branch name (or id) that should be targeted')
245
246
  .option('-C, --config-path <configPath>', `Specify the path to the optional locize config file (default: ${configInWorkingDirectory} or ${configInHome})`)
246
247
  .action((options) => {
@@ -274,6 +275,8 @@ program
274
275
 
275
276
  const unpublished = options.unpublished === 'true';
276
277
 
278
+ const overriddenOnly = options.overriddenOnly === 'true';
279
+
277
280
  const languageFolderPrefix = options.languageFolderPrefix || '';
278
281
 
279
282
  const pathMask = options.pathMask;
@@ -295,7 +298,8 @@ program
295
298
  languageFolderPrefix: languageFolderPrefix,
296
299
  pathMask: pathMask,
297
300
  unpublished: unpublished,
298
- branch: branch
301
+ branch: branch,
302
+ overriddenOnly: overriddenOnly
299
303
  });
300
304
  })
301
305
  .on('--help', () => {
@@ -386,6 +390,7 @@ program
386
390
  .option('-n, --namespace <ns>', 'The namespace that should be targeted (you can also pass a comma separated list)')
387
391
  .option('-g, --get-path <url>', `Specify the get-path url that should be used (default: ${getPathUrl})`)
388
392
  .option('-up, --unpublished <true|false>', 'Downloads the current (unpublished) translations. This will generate private download costs (default: false)', 'false')
393
+ .option('-oo, --overridden-only <true|false>', 'Downloads only the current overridden (unpublished) translations of a tenant or branch project. This will generate private download costs (default: false)', 'false')
389
394
  .option('-b, --branch <branch>', 'The branch name (or id) that should be targeted')
390
395
  .option('-C, --config-path <configPath>', `Specify the path to the optional locize config file (default: ${configInWorkingDirectory} or ${configInHome})`)
391
396
  .action((options) => {
@@ -433,6 +438,7 @@ program
433
438
  const compareModificationTime = options.compareModificationTime === 'true';
434
439
  const pathMask = options.pathMask;
435
440
  const unpublished = options.unpublished === 'true';
441
+ const overriddenOnly = options.overriddenOnly === 'true';
436
442
  const autoCreatePath = options.autoCreatePath === 'true';
437
443
  const backupDeletedPath = options.backupDeletedPath;
438
444
  const branch = options.branch;
@@ -462,7 +468,8 @@ program
462
468
  unpublished: unpublished,
463
469
  autoCreatePath: autoCreatePath,
464
470
  backupDeletedPath: backupDeletedPath,
465
- branch: branch
471
+ branch: branch,
472
+ overriddenOnly: overriddenOnly
466
473
  });
467
474
  })
468
475
  .on('--help', () => {
package/createBranch.js CHANGED
@@ -1,40 +1,60 @@
1
1
  const colors = require('colors');
2
2
  const request = require('./request');
3
+ const getBranches = require('./getBranches');
4
+
5
+ const handleError = (err, cb) => {
6
+ if (!cb && err) {
7
+ console.error(colors.red(err.stack));
8
+ process.exit(1);
9
+ }
10
+ if (cb) cb(err);
11
+ };
3
12
 
4
13
  const createBranch = (opt, cb) => {
5
- request(opt.apiPath + '/branch/create/' + opt.projectId + '/' + opt.version, {
6
- method: 'post',
7
- headers: {
8
- 'Authorization': opt.apiKey
9
- },
10
- body: { name: opt.branch }
11
- }, (err, res, obj) => {
12
- if (err || (obj && (obj.errorMessage || obj.message))) {
13
- if (!cb) console.log(colors.red('creating branch failed...'));
14
+ getBranches(opt, (err, branches) => {
15
+ if (err) return handleError(err, cb);
16
+
17
+ const b = branches.find((br) => br.name === opt.branch);
18
+ if (b) {
19
+ if (!cb) console.log(colors.green('creating branch "' + b.name + '" (' + b.id + ') not necessary, because already existing'));
20
+ if (cb) cb(null, b);
21
+ return;
22
+ }
23
+
24
+ request(opt.apiPath + '/branch/create/' + opt.projectId + '/' + opt.version, {
25
+ method: 'post',
26
+ headers: {
27
+ 'Authorization': opt.apiKey
28
+ },
29
+ body: { name: opt.branch }
30
+ }, (err, res, obj) => {
31
+ if (err || (obj && (obj.errorMessage || obj.message))) {
32
+ if (!cb) console.log(colors.red('creating branch failed...'));
14
33
 
15
- if (err) {
16
- if (!cb) { console.error(colors.red(err.message)); process.exit(1); }
17
- if (cb) cb(err);
34
+ if (err) {
35
+ if (!cb) { console.error(colors.red(err.message)); process.exit(1); }
36
+ if (cb) cb(err);
37
+ return;
38
+ }
39
+ if (obj && (obj.errorMessage || obj.message)) {
40
+ if (!cb) { console.error(colors.red((obj.errorMessage || obj.message))); process.exit(1); }
41
+ if (cb) cb(new Error((obj.errorMessage || obj.message)));
42
+ return;
43
+ }
44
+ }
45
+ if (res.status === 404) {
46
+ if (!cb) { console.error(colors.yellow(res.statusText + ' (' + res.status + ')')); process.exit(1); }
47
+ if (cb) cb(null, null);
18
48
  return;
19
49
  }
20
- if (obj && (obj.errorMessage || obj.message)) {
21
- if (!cb) { console.error(colors.red((obj.errorMessage || obj.message))); process.exit(1); }
22
- if (cb) cb(new Error((obj.errorMessage || obj.message)));
50
+ if (res.status >= 300) {
51
+ if (!cb) { console.error(colors.red(res.statusText + ' (' + res.status + ')')); process.exit(1); }
52
+ if (cb) cb(new Error(res.statusText + ' (' + res.status + ')'));
23
53
  return;
24
54
  }
25
- }
26
- if (res.status === 404) {
27
- if (!cb) { console.error(colors.yellow(res.statusText + ' (' + res.status + ')')); process.exit(1); }
28
- if (cb) cb(null, null);
29
- return;
30
- }
31
- if (res.status >= 300) {
32
- if (!cb) { console.error(colors.red(res.statusText + ' (' + res.status + ')')); process.exit(1); }
33
- if (cb) cb(new Error(res.statusText + ' (' + res.status + ')'));
34
- return;
35
- }
36
- if (!cb) console.log(colors.green('creating branch "' + obj.name + '" (' + obj.id + ') successful'));
37
- if (cb) cb(null, obj);
55
+ if (!cb) console.log(colors.green('creating branch "' + obj.name + '" (' + obj.id + ') successful'));
56
+ if (cb) cb(null, obj);
57
+ });
38
58
  });
39
59
  };
40
60
 
package/download.js CHANGED
@@ -467,6 +467,9 @@ const download = (opt, cb) => {
467
467
  opt.pathMaskInterpolationSuffix = opt.pathMaskInterpolationSuffix || '}}';
468
468
  opt.pathMask = opt.pathMask || `${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}${path.sep}${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`;
469
469
  opt.pathMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, `${opt.languageFolderPrefix}${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`);
470
+ if (opt.overriddenOnly) {
471
+ opt.unpublished = true;
472
+ }
470
473
  if (opt.unpublished && !opt.apiKey) {
471
474
  return handleError(new Error('Please provide also an api-key!'), cb);
472
475
  }
@@ -23,7 +23,7 @@ function onlyKeysFlat(resources, prefix, ret) {
23
23
 
24
24
  const pullNamespacePaged = (opt, lng, ns, cb, next, retry) => {
25
25
  next = next || '';
26
- request(opt.apiPath + '/pull/' + opt.projectId + '/' + opt.version + '/' + lng + '/' + ns + '?' + 'next=' + next + (opt.raw ? '&raw=true' : '') + '&ts=' + Date.now(), {
26
+ request(opt.apiPath + '/pull/' + opt.projectId + '/' + opt.version + '/' + lng + '/' + ns + '?' + 'next=' + next + ((opt.raw || opt.overriddenOnly) ? '&raw=true' : '') + '&ts=' + Date.now(), {
27
27
  method: 'get',
28
28
  headers: {
29
29
  'Authorization': opt.apiKey
@@ -47,6 +47,20 @@ const pullNamespacePaged = (opt, lng, ns, cb, next, retry) => {
47
47
  return cb(new Error(res.statusText + ' (' + res.status + ')'));
48
48
  }
49
49
 
50
+ if (opt.overriddenOnly && obj) {
51
+ const newObj = {};
52
+ Object.keys(obj).forEach((k) => {
53
+ if (obj[k].overrides !== undefined) {
54
+ if (opt.raw) {
55
+ newObj[k] = obj[k];
56
+ } else {
57
+ newObj[k] = obj[k].value;
58
+ }
59
+ }
60
+ });
61
+ obj = newObj;
62
+ }
63
+
50
64
  cb(null, {
51
65
  result: opt.raw ? sortFlatResources(onlyKeysFlat(obj)) : sortFlatResources(flatten(obj)),
52
66
  next: res.headers.get('x-next-page'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locize-cli",
3
- "version": "10.1.0",
3
+ "version": "10.2.0",
4
4
  "description": "locize cli to import locales",
5
5
  "main": "index.js",
6
6
  "bin": {
package/sync.js CHANGED
@@ -736,6 +736,9 @@ const sync = (opt, cb) => {
736
736
  opt.pathMask = opt.pathMask || `${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}${path.sep}${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`;
737
737
  opt.languageFolderPrefix = opt.languageFolderPrefix || '';
738
738
  opt.pathMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, `${opt.languageFolderPrefix}${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`);
739
+ if (opt.overriddenOnly) {
740
+ opt.unpublished = true;
741
+ }
739
742
  if (opt.unpublished && !opt.apiKey) {
740
743
  return handleError(new Error('Please provide also an api-key!'), cb);
741
744
  }