@lingui/cli 3.10.2 → 3.12.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/api/catalog.js CHANGED
@@ -146,8 +146,8 @@ var Catalog = /*#__PURE__*/function () {
146
146
  paths.forEach(function (filename) {
147
147
  return (0, _extractors.default)(filename, tmpDir, {
148
148
  verbose: options.verbose,
149
+ configPath: options.configPath,
149
150
  babelOptions: _this.config.extractBabelOptions,
150
- // @ts-ignore
151
151
  extractors: options.extractors,
152
152
  projectType: options.projectType
153
153
  });
@@ -255,7 +255,7 @@ var Catalog = /*#__PURE__*/function () {
255
255
  };
256
256
 
257
257
  var getMultipleFallbacks = function getMultipleFallbacks(locale) {
258
- var fL = fallbackLocales[locale]; // some probably the fallback will be undefined, so just search by locale
258
+ var fL = fallbackLocales && fallbackLocales[locale]; // some probably the fallback will be undefined, so just search by locale
259
259
 
260
260
  if (!fL) return null;
261
261
 
@@ -284,7 +284,7 @@ var Catalog = /*#__PURE__*/function () {
284
284
  return (// Get translation in target locale
285
285
  getTranslation(locale) || // We search in fallbackLocales as dependent of each locale
286
286
  getMultipleFallbacks(locale) || // Get translation in fallbackLocales.default (if any)
287
- fallbackLocales.default && getTranslation(fallbackLocales.default) || // Get message default
287
+ fallbackLocales && fallbackLocales.default && getTranslation(fallbackLocales.default) || // Get message default
288
288
  catalogs[locale][key].defaults || // If sourceLocale is either target locale of fallback one, use key
289
289
  sourceLocale && sourceLocale === locale && key || sourceLocale && fallbackLocales.default && sourceLocale === fallbackLocales.default && key || // Otherwise no translation is available
290
290
  undefined
@@ -333,8 +333,7 @@ var Catalog = /*#__PURE__*/function () {
333
333
  locale: undefined
334
334
  });
335
335
 
336
- var poFormat = (0, _formats.default)("po");
337
- poFormat.write(filename, messages, options);
336
+ this.format.write(filename, messages, options);
338
337
  }
339
338
  }, {
340
339
  key: "writeCompiled",
@@ -481,7 +480,7 @@ function getCatalogs(config) {
481
480
  return path.replace(NAME, "*");
482
481
  });
483
482
 
484
- var candidates = _glob.default.sync(patterns.length > 1 ? "{".concat(patterns.join(",")) : patterns[0], {
483
+ var candidates = _glob.default.sync(patterns.length > 1 ? "{".concat(patterns.join(","), "}") : patterns[0], {
485
484
  ignore: exclude,
486
485
  mark: true
487
486
  });
package/api/compile.js CHANGED
@@ -26,6 +26,8 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
26
26
 
27
27
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
28
28
 
29
+ var INVALID_OBJECT_KEY_REGEX = /^(\d+[a-zA-Z]|[a-zA-Z]+\d)(\d|[a-zA-Z])*/;
30
+
29
31
  function createCompiledCatalog(locale, messages, options) {
30
32
  var _options$strict = options.strict,
31
33
  strict = _options$strict === void 0 ? false : _options$strict,
@@ -133,7 +135,8 @@ function processTokens(tokens) {
133
135
 
134
136
  token.cases.forEach(function (item) {
135
137
  var inlineTokens = processTokens(item.tokens);
136
- formatProps.push(t.objectProperty(t.identifier(item.key), isString(inlineTokens) ? t.stringLiteral(inlineTokens) : inlineTokens));
138
+ formatProps.push(t.objectProperty( // if starts with number must be wrapped with quotes
139
+ INVALID_OBJECT_KEY_REGEX.test(item.key) ? t.stringLiteral(item.key) : t.identifier(item.key), isString(inlineTokens) ? t.stringLiteral(inlineTokens) : inlineTokens));
137
140
  });
138
141
  var params = [t.stringLiteral(token.arg), t.stringLiteral(token.type), t.objectExpression(formatProps)];
139
142
  return t.arrayExpression(params);
@@ -34,7 +34,8 @@ var extractor = {
34
34
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
35
35
 
36
36
  var _options$babelOptions = options.babelOptions,
37
- _babelOptions = _options$babelOptions === void 0 ? {} : _options$babelOptions;
37
+ _babelOptions = _options$babelOptions === void 0 ? {} : _options$babelOptions,
38
+ configPath = options.configPath;
38
39
 
39
40
  var _babelOptions$plugins = _babelOptions.plugins,
40
41
  plugins = _babelOptions$plugins === void 0 ? [] : _babelOptions$plugins,
@@ -50,7 +51,8 @@ var extractor = {
50
51
  // https://github.com/lingui/js-lingui/issues/952
51
52
  envName: "development",
52
53
  plugins: ["macros", [_babelPluginExtractMessages.default, {
53
- localeDir: localeDir
54
+ localeDir: localeDir,
55
+ configPath: configPath
54
56
  }]].concat((0, _toConsumableArray2.default)(plugins))
55
57
  }));
56
58
  }
@@ -17,7 +17,18 @@ function extract(filename, targetPath, options) {
17
17
  var _options$extractors;
18
18
 
19
19
  var extractorsToExtract = (_options$extractors = options.extractors) !== null && _options$extractors !== void 0 ? _options$extractors : DEFAULT_EXTRACTORS;
20
- return extractorsToExtract.some(function (ext) {
20
+ return extractorsToExtract.some(function (e) {
21
+ var ext = e;
22
+
23
+ if (typeof e === "string") {
24
+ // in case of the user using require.resolve in their extractors, we require that module
25
+ ext = require(e);
26
+ }
27
+
28
+ if (ext.default) {
29
+ ext = ext.default;
30
+ }
31
+
21
32
  if (!ext.match(filename)) return false;
22
33
  var spinner;
23
34
  if (options.verbose) spinner = (0, _ora.default)().start(filename);
@@ -56,9 +56,11 @@ var extractor = {
56
56
  }
57
57
 
58
58
  var _options$babelOptions = options.babelOptions,
59
- babelOptions = _options$babelOptions === void 0 ? {} : _options$babelOptions;
59
+ babelOptions = _options$babelOptions === void 0 ? {} : _options$babelOptions,
60
+ configPath = options.configPath;
60
61
  var plugins = ["macros", [_babelPluginExtractMessages.default, {
61
- localeDir: localeDir
62
+ localeDir: localeDir,
63
+ configPath: configPath
62
64
  }]].concat((0, _toConsumableArray2.default)(babelOptions.plugins || []));
63
65
 
64
66
  if (isTsx) {
@@ -196,9 +196,14 @@ var deserialize = R.map(R.applySpec({
196
196
  */
197
197
 
198
198
  var getPluralCases = function getPluralCases(lang) {
199
- var gettextPluralsInfo = _plurals.default[lang];
199
+ // If users uses locale with underscore or slash, es-ES, es_ES, gettextplural is "es" not es-ES.
200
+ var _lang$split = lang.split(/[-_]/g),
201
+ _lang$split2 = (0, _slicedToArray2.default)(_lang$split, 1),
202
+ correctLang = _lang$split2[0];
203
+
204
+ var gettextPluralsInfo = _plurals.default[correctLang];
200
205
  return gettextPluralsInfo === null || gettextPluralsInfo === void 0 ? void 0 : gettextPluralsInfo.examples.map(function (pluralCase) {
201
- return (0, _pluralsCldr.default)(lang, pluralCase.sample);
206
+ return (0, _pluralsCldr.default)(correctLang, pluralCase.sample);
202
207
  });
203
208
  };
204
209
 
@@ -213,7 +218,7 @@ var convertPluralsToICU = function convertPluralsToICU(items, lang) {
213
218
  var translationCount = getTranslationCount(item);
214
219
  var messageKey = getMessageKey(item); // Messages without multiple translations (= plural cases) need no further processing.
215
220
 
216
- if (translationCount <= 1) {
221
+ if (translationCount <= 1 && !item.msgid_plural) {
217
222
  return;
218
223
  } // msgid_plural must be set, but its actual value is not important.
219
224
 
package/lingui-compile.js CHANGED
@@ -28,6 +28,8 @@ var _compile = require("./api/compile");
28
28
 
29
29
  var _help = require("./api/help");
30
30
 
31
+ var _api = require("./api");
32
+
31
33
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
32
34
 
33
35
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
@@ -110,7 +112,7 @@ function command(config, options) {
110
112
  }
111
113
 
112
114
  if (require.main === module) {
113
- _commander.default.description("Add compile message catalogs and add language data (plurals) to compiled bundle.").option("--config <path>", "Path to the config file").option("--strict", "Disable defaults for missing translations").option("--verbose", "Verbose output").option("--format <format>", "Format of message catalog").option("--typescript", "Create Typescript definition for compiled bundle").option("--namespace <namespace>", "Specify namespace for compiled bundle. Ex: cjs(default) -> module.exports, es -> export, window.test -> window.test").option("--watch", "Enables Watch Mode").on("--help", function () {
115
+ _commander.default.description("Add compile message catalogs and add language data (plurals) to compiled bundle.").option("--config <path>", "Path to the config file").option("--strict", "Disable defaults for missing translations").option("--verbose", "Verbose output").option("--format <format>", "Format of message catalog").option("--typescript", "Create Typescript definition for compiled bundle").option("--namespace <namespace>", "Specify namespace for compiled bundle. Ex: cjs(default) -> module.exports, es -> export, window.test -> window.test").option("--watch", "Enables Watch Mode").option("--debounce <delay>", "Debounces compilation for given amount of milliseconds").on("--help", function () {
114
116
  console.log("\n Examples:\n");
115
117
  console.log(" # Compile translations and use defaults or message IDs for missing translations");
116
118
  console.log(" $ ".concat((0, _help.helpRun)("compile")));
@@ -139,6 +141,18 @@ if (require.main === module) {
139
141
  namespace: _commander.default.namespace // we want this to be undefined if user does not specify so default can be used
140
142
 
141
143
  });
144
+ };
145
+
146
+ var debounceTimer;
147
+
148
+ var dispatchCompile = function dispatchCompile() {
149
+ // Skip debouncing if not enabled
150
+ if (!_commander.default.debounce) return compile(); // CLear the previous timer if there is any, and schedule the next
151
+
152
+ debounceTimer && clearTimeout(debounceTimer);
153
+ debounceTimer = setTimeout(function () {
154
+ return compile();
155
+ }, _commander.default.debounce);
142
156
  }; // Check if Watch Mode is enabled
143
157
 
144
158
 
@@ -150,9 +164,10 @@ if (require.main === module) {
150
164
  var _catalogs = (0, _catalog.getCatalogs)(config);
151
165
 
152
166
  var paths = [];
167
+ var catalogExtension = (0, _api.getFormat)(config.format).catalogExtension;
153
168
  config.locales.forEach(function (locale) {
154
169
  _catalogs.forEach(function (catalog) {
155
- paths.push("".concat(catalog.path.replace(LOCALE, locale).replace(NAME, "*"), ".").concat(config.format));
170
+ paths.push("".concat(catalog.path.replace(LOCALE, locale).replace(NAME, "*")).concat(catalogExtension));
156
171
  });
157
172
  });
158
173
 
@@ -162,14 +177,14 @@ if (require.main === module) {
162
177
 
163
178
  var onReady = function onReady() {
164
179
  console.info(_chalk.default.green.bold("Watcher is ready!"));
165
- watcher.on('add', function () {
166
- return compile();
167
- }).on('change', function () {
168
- return compile();
180
+ watcher.on("add", function () {
181
+ return dispatchCompile();
182
+ }).on("change", function () {
183
+ return dispatchCompile();
169
184
  });
170
185
  };
171
186
 
172
- watcher.on('ready', function () {
187
+ watcher.on("ready", function () {
173
188
  return onReady();
174
189
  });
175
190
  } else {
@@ -61,10 +61,11 @@ if (require.main === module) {
61
61
  _commander.default.option("--config <path>", "Path to the config file").option("--verbose", "Verbose output").parse(process.argv);
62
62
 
63
63
  var config = (0, _conf.getConfig)({
64
- configPath: _commander.default.config
64
+ configPath: _commander.default.config || process.env.LINGUI_CONFIG
65
65
  });
66
66
  var result = command(config, {
67
- verbose: _commander.default.verbose || false
67
+ verbose: _commander.default.verbose || false,
68
+ configPath: _commander.default.config || process.env.LINGUI_CONFIG
68
69
  });
69
70
  if (!result) process.exit(1);
70
71
  }
package/lingui-extract.js CHANGED
@@ -9,6 +9,10 @@ exports.default = command;
9
9
 
10
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
11
 
12
+ var _interopRequireWildcard2 = _interopRequireDefault(require("@babel/runtime/helpers/interopRequireWildcard"));
13
+
14
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
15
+
12
16
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
17
 
14
18
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
@@ -68,13 +72,24 @@ function command(config, options) {
68
72
  if (!options.watch) {
69
73
  console.error("(use \"".concat(_chalk.default.yellow((0, _help.helpRun)("extract")), "\" to update catalogs with new messages)"));
70
74
  console.error("(use \"".concat(_chalk.default.yellow((0, _help.helpRun)("compile")), "\" to compile catalogs for production)"));
75
+ } // If service key is present in configuration, synchronize with cloud translation platform
76
+
77
+
78
+ if ((0, _typeof2.default)(config.service) === 'object' && config.service.name && config.service.name.length) {
79
+ Promise.resolve("./services/".concat(config.service.name)).then(function (s) {
80
+ return (0, _interopRequireWildcard2.default)(require(s));
81
+ }).then(function (module) {
82
+ return module.default(config, options);
83
+ }).catch(function (err) {
84
+ return console.error("Can't load service module ".concat(config.service.name), err);
85
+ });
71
86
  }
72
87
 
73
88
  return true;
74
89
  }
75
90
 
76
91
  if (require.main === module) {
77
- _commander.default.option("--config <path>", "Path to the config file").option("--locale <locale>", "Only extract the specified locale").option("--overwrite", "Overwrite translations for source locale").option("--clean", "Remove obsolete translations").option("--verbose", "Verbose output").option("--convert-from <format>", "Convert from previous format of message catalogs").option("--watch", "Enables Watch Mode") // Obsolete options
92
+ _commander.default.option("--config <path>", "Path to the config file").option("--locale <locale>", "Only extract the specified locale").option("--overwrite", "Overwrite translations for source locale").option("--clean", "Remove obsolete translations").option("--debounce <delay>", "Debounces extraction for given amount of milliseconds").option("--verbose", "Verbose output").option("--convert-from <format>", "Convert from previous format of message catalogs").option("--watch", "Enables Watch Mode") // Obsolete options
78
93
  .option("--babelOptions", "Babel options passed to transform/extract plugins").option("--format <format>", "Format of message catalogs").parse(process.argv);
79
94
 
80
95
  var config = (0, _conf.getConfig)({
@@ -124,10 +139,29 @@ if (require.main === module) {
124
139
  clean: _commander.default.watch ? false : _commander.default.clean || false,
125
140
  overwrite: _commander.default.watch || _commander.default.overwrite || false,
126
141
  locale: _commander.default.locale,
142
+ configPath: _commander.default.config || process.env.LINGUI_CONFIG,
127
143
  watch: _commander.default.watch || false,
128
144
  files: (filePath === null || filePath === void 0 ? void 0 : filePath.length) ? filePath : undefined,
129
145
  prevFormat: prevFormat
130
146
  });
147
+ };
148
+
149
+ var changedPaths = new Set();
150
+ var debounceTimer;
151
+
152
+ var dispatchExtract = function dispatchExtract(filePath) {
153
+ // Skip debouncing if not enabled
154
+ if (!_commander.default.debounce) return extract(filePath);
155
+ filePath === null || filePath === void 0 ? void 0 : filePath.forEach(function (path) {
156
+ return changedPaths.add(path);
157
+ }); // CLear the previous timer if there is any, and schedule the next
158
+
159
+ debounceTimer && clearTimeout(debounceTimer);
160
+ debounceTimer = setTimeout(function () {
161
+ var filePath = (0, _toConsumableArray2.default)(changedPaths);
162
+ changedPaths.clear();
163
+ extract(filePath);
164
+ }, _commander.default.debounce);
131
165
  }; // Check if Watch Mode is enabled
132
166
 
133
167
 
@@ -142,20 +176,20 @@ if (require.main === module) {
142
176
  });
143
177
 
144
178
  var watcher = _chokidar.default.watch(paths, {
145
- ignored: ['/(^|[\/\\])\../'].concat(ignored),
179
+ ignored: ["/(^|[/\\])../"].concat(ignored),
146
180
  persistent: true
147
181
  });
148
182
 
149
183
  var onReady = function onReady() {
150
184
  console.info(_chalk.default.green.bold("Watcher is ready!"));
151
- watcher.on('add', function (path) {
152
- return extract([path]);
153
- }).on('change', function (path) {
154
- return extract([path]);
185
+ watcher.on("add", function (path) {
186
+ return dispatchExtract([path]);
187
+ }).on("change", function (path) {
188
+ return dispatchExtract([path]);
155
189
  });
156
190
  };
157
191
 
158
- watcher.on('ready', function () {
192
+ watcher.on("ready", function () {
159
193
  return onReady();
160
194
  });
161
195
  } else if (_commander.default.args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingui/cli",
3
- "version": "3.10.2",
3
+ "version": "3.12.1",
4
4
  "description": "CLI for working wit message catalogs",
5
5
  "keywords": [
6
6
  "cli",
@@ -29,6 +29,7 @@
29
29
  "LICENSE",
30
30
  "README.md",
31
31
  "api",
32
+ "services",
32
33
  "lingui.js",
33
34
  "lingui-*.js"
34
35
  ],
@@ -38,8 +39,8 @@
38
39
  "@babel/plugin-syntax-jsx": "^7.10.4",
39
40
  "@babel/runtime": "^7.11.2",
40
41
  "@babel/types": "^7.11.5",
41
- "@lingui/babel-plugin-extract-messages": "^3.10.2",
42
- "@lingui/conf": "^3.10.2",
42
+ "@lingui/babel-plugin-extract-messages": "^3.12.1",
43
+ "@lingui/conf": "^3.12.1",
43
44
  "babel-plugin-macros": "^3.0.1",
44
45
  "bcp-47": "^1.0.7",
45
46
  "chalk": "^4.1.0",
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = syncProcess;
9
+
10
+ var _fs = _interopRequireDefault(require("fs"));
11
+
12
+ var _path = require("path");
13
+
14
+ var _pofile = _interopRequireDefault(require("pofile"));
15
+
16
+ var _https = _interopRequireDefault(require("https"));
17
+
18
+ var _glob = _interopRequireDefault(require("glob"));
19
+
20
+ var _dateFns = require("date-fns");
21
+
22
+ var getCreateHeaders = function getCreateHeaders(language) {
23
+ return {
24
+ "POT-Creation-Date": (0, _dateFns.format)(new Date(), "yyyy-MM-dd HH:mmxxxx"),
25
+ "MIME-Version": "1.0",
26
+ "Content-Type": "text/plain; charset=utf-8",
27
+ "Content-Transfer-Encoding": "8bit",
28
+ "X-Generator": "@lingui/cli",
29
+ Language: language
30
+ };
31
+ }; // Main sync method, call "Init" or "Sync" depending on the project context
32
+
33
+
34
+ function syncProcess(config, options) {
35
+ if (config.format != 'po') {
36
+ console.error("\n----------\nTranslation.io service is only compatible with the \"po\" format. Please update your Lingui configuration accordingly.\n----------");
37
+ process.exit(1);
38
+ }
39
+
40
+ var successCallback = function successCallback(project) {
41
+ console.log("\n----------\nProject successfully synchronized. Please use this URL to translate: ".concat(project.url, "\n----------"));
42
+ };
43
+
44
+ var failCallback = function failCallback(errors) {
45
+ console.error("\n----------\nSynchronization with Translation.io failed: ".concat(errors.join(', '), "\n----------"));
46
+ };
47
+
48
+ init(config, options, successCallback, function (errors) {
49
+ if (errors.length && errors[0] === 'This project has already been initialized.') {
50
+ sync(config, options, successCallback, failCallback);
51
+ } else {
52
+ failCallback(errors);
53
+ }
54
+ });
55
+ } // Initialize project with source and existing translations (only first time!)
56
+ // Cf. https://translation.io/docs/create-library#initialization
57
+
58
+
59
+ function init(config, options, successCallback, failCallback) {
60
+ var sourceLocale = config.sourceLocale || 'en';
61
+ var targetLocales = config.locales.filter(function (value) {
62
+ return value != sourceLocale;
63
+ });
64
+ var paths = poPathsPerLocale(config);
65
+ var segments = {};
66
+ targetLocales.forEach(function (targetLocale) {
67
+ segments[targetLocale] = [];
68
+ }); // Create segments from source locale PO items
69
+
70
+ paths[sourceLocale].forEach(function (path) {
71
+ var raw = _fs.default.readFileSync(path).toString();
72
+
73
+ var po = _pofile.default.parse(raw);
74
+
75
+ po.items.filter(function (item) {
76
+ return !item['obsolete'];
77
+ }).forEach(function (item) {
78
+ targetLocales.forEach(function (targetLocale) {
79
+ var newSegment = createSegmentFromPoItem(item);
80
+ segments[targetLocale].push(newSegment);
81
+ });
82
+ });
83
+ }); // Add translations to segments from target locale PO items
84
+
85
+ targetLocales.forEach(function (targetLocale) {
86
+ paths[targetLocale].forEach(function (path) {
87
+ var raw = _fs.default.readFileSync(path).toString();
88
+
89
+ var po = _pofile.default.parse(raw);
90
+
91
+ po.items.filter(function (item) {
92
+ return !item['obsolete'];
93
+ }).forEach(function (item, index) {
94
+ segments[targetLocale][index].target = item.msgstr[0];
95
+ });
96
+ });
97
+ });
98
+ var request = {
99
+ "client": "lingui",
100
+ "version": require('@lingui/core/package.json').version,
101
+ "source_language": sourceLocale,
102
+ "target_languages": targetLocales,
103
+ "segments": segments
104
+ };
105
+ postTio("init", request, config.service.apiKey, function (response) {
106
+ if (response.errors) {
107
+ failCallback(response.errors);
108
+ } else {
109
+ saveSegmentsToTargetPos(config, paths, response.segments);
110
+ successCallback(response.project);
111
+ }
112
+ }, function (error) {
113
+ console.error("\n----------\nSynchronization with Translation.io failed: ".concat(error, "\n----------"));
114
+ });
115
+ } // Send all source text from PO to Translation.io and create new PO based on received translations
116
+ // Cf. https://translation.io/docs/create-library#synchronization
117
+
118
+
119
+ function sync(config, options, successCallback, failCallback) {
120
+ var sourceLocale = config.sourceLocale || 'en';
121
+ var targetLocales = config.locales.filter(function (value) {
122
+ return value != sourceLocale;
123
+ });
124
+ var paths = poPathsPerLocale(config);
125
+ var segments = []; // Create segments with correct source
126
+
127
+ paths[sourceLocale].forEach(function (path) {
128
+ var raw = _fs.default.readFileSync(path).toString();
129
+
130
+ var po = _pofile.default.parse(raw);
131
+
132
+ po.items.filter(function (item) {
133
+ return !item['obsolete'];
134
+ }).forEach(function (item) {
135
+ var newSegment = createSegmentFromPoItem(item);
136
+ segments.push(newSegment);
137
+ });
138
+ });
139
+ var request = {
140
+ "client": "lingui",
141
+ "version": require('@lingui/core/package.json').version,
142
+ "source_language": sourceLocale,
143
+ "target_languages": targetLocales,
144
+ "segments": segments
145
+ }; // Sync and then remove unused segments (not present in the local application) from Translation.io
146
+
147
+ if (options.clean) {
148
+ request['purge'] = true;
149
+ }
150
+
151
+ postTio("sync", request, config.service.apiKey, function (response) {
152
+ if (response.errors) {
153
+ failCallback(response.errors);
154
+ } else {
155
+ saveSegmentsToTargetPos(config, paths, response.segments);
156
+ successCallback(response.project);
157
+ }
158
+ }, function (error) {
159
+ console.error("\n----------\nSynchronization with Translation.io failed: ".concat(error, "\n----------"));
160
+ });
161
+ }
162
+
163
+ function createSegmentFromPoItem(item) {
164
+ var itemHasId = item.msgid != item.msgstr[0] && item.msgstr[0].length;
165
+ var segment = {
166
+ type: 'source',
167
+ // No way to edit text for source language (inside code), so not using "key" here
168
+ source: itemHasId ? item.msgstr[0] : item.msgid,
169
+ // msgstr may be empty if --overwrite is used and no ID is used
170
+ context: '',
171
+ references: [],
172
+ comment: ''
173
+ };
174
+
175
+ if (itemHasId) {
176
+ segment.context = item.msgid;
177
+ }
178
+
179
+ if (item.references.length) {
180
+ segment.references = item.references;
181
+ }
182
+
183
+ if (item.extractedComments.length) {
184
+ segment.comment = item.extractedComments.join(' | ');
185
+ }
186
+
187
+ return segment;
188
+ }
189
+
190
+ function createPoItemFromSegment(segment) {
191
+ var item = new _pofile.default.Item();
192
+ item.msgid = segment.context ? segment.context : segment.source;
193
+ item.msgstr = [segment.target];
194
+ item.references = segment.references && segment.references.length ? segment.references : [];
195
+ item.extractedComments = segment.comment ? segment.comment.split(' | ') : [];
196
+ return item;
197
+ }
198
+
199
+ function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) {
200
+ var NAME = "{name}";
201
+ var LOCALE = "{locale}";
202
+ Object.keys(segmentsPerLocale).forEach(function (targetLocale) {
203
+ // Remove existing target POs and JS for this target locale
204
+ paths[targetLocale].forEach(function (path) {
205
+ var jsPath = path.replace(/\.po?$/, "") + ".js";
206
+ var dirPath = (0, _path.dirname)(path); // Remove PO, JS and empty dir
207
+
208
+ if (_fs.default.existsSync(path)) {
209
+ _fs.default.unlinkSync(path);
210
+ }
211
+
212
+ if (_fs.default.existsSync(jsPath)) {
213
+ _fs.default.unlinkSync(jsPath);
214
+ }
215
+
216
+ if (_fs.default.existsSync(dirPath) && _fs.default.readdirSync(dirPath).length === 0) {
217
+ _fs.default.rmdirSync(dirPath);
218
+ }
219
+ }); // Find target path (ignoring {name})
220
+
221
+ var localePath = "".concat(config.catalogs[0].path.replace(LOCALE, targetLocale).replace(NAME, ''), ".po");
222
+ var segments = segmentsPerLocale[targetLocale];
223
+ var po = new _pofile.default();
224
+ po.headers = getCreateHeaders(targetLocale);
225
+ var items = [];
226
+ segments.forEach(function (segment) {
227
+ var item = createPoItemFromSegment(segment);
228
+ items.push(item);
229
+ }); // Sort items by messageId
230
+
231
+ po.items = items.sort(function (a, b) {
232
+ if (a.msgid < b.msgid) {
233
+ return -1;
234
+ }
235
+
236
+ if (a.msgid > b.msgid) {
237
+ return 1;
238
+ }
239
+
240
+ return 0;
241
+ }); // Check that localePath directory exists and save PO file
242
+
243
+ _fs.default.promises.mkdir((0, _path.dirname)(localePath), {
244
+ recursive: true
245
+ }).then(function () {
246
+ po.save(localePath, function (err) {
247
+ if (err) {
248
+ console.error('Error while saving target PO files:');
249
+ console.error(err);
250
+ process.exit(1);
251
+ }
252
+ });
253
+ });
254
+ });
255
+ }
256
+
257
+ function poPathsPerLocale(config) {
258
+ var NAME = "{name}";
259
+ var LOCALE = "{locale}";
260
+ var paths = [];
261
+ config.locales.forEach(function (locale) {
262
+ paths[locale] = [];
263
+ config.catalogs.forEach(function (catalog) {
264
+ var path = "".concat(catalog.path.replace(LOCALE, locale).replace(NAME, "*"), ".po"); // If {name} is present (replaced by *), list all the existing POs
265
+
266
+ if (path.includes('*')) {
267
+ paths[locale] = paths[locale].concat(_glob.default.sync(path));
268
+ } else {
269
+ paths[locale].push(path);
270
+ }
271
+ });
272
+ });
273
+ return paths;
274
+ }
275
+
276
+ function postTio(action, request, apiKey, successCallback, failCallback) {
277
+ var jsonRequest = JSON.stringify(request);
278
+ var options = {
279
+ hostname: 'translation.io',
280
+ path: '/api/v1/segments/' + action + '.json?api_key=' + apiKey,
281
+ method: 'POST',
282
+ headers: {
283
+ 'Content-Type': 'application/json'
284
+ }
285
+ };
286
+
287
+ var req = _https.default.request(options, function (res) {
288
+ res.setEncoding('utf8');
289
+ var body = "";
290
+ res.on('data', function (chunk) {
291
+ body = body.concat(chunk);
292
+ });
293
+ res.on('end', function () {
294
+ var response = JSON.parse(body);
295
+ successCallback(response);
296
+ });
297
+ });
298
+
299
+ req.on('error', function (e) {
300
+ failCallback(e);
301
+ });
302
+ req.write(jsonRequest);
303
+ req.end();
304
+ }