@lingui/cli 5.8.0 → 5.9.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.
@@ -32,7 +32,8 @@ function mergeCatalog(prevCatalog, nextCatalog, forSourceLocale, options) {
32
32
  ? nextCatalog[key].message || key
33
33
  : prevCatalog[key].translation;
34
34
  const _a = nextCatalog[key], { obsolete } = _a, rest = __rest(_a, ["obsolete"]);
35
- return [key, Object.assign(Object.assign({}, rest), { translation })];
35
+ const { extra } = prevCatalog[key];
36
+ return [key, Object.assign(Object.assign(Object.assign({}, extra), rest), { translation })];
36
37
  }));
37
38
  // Mark all remaining translations as obsolete
38
39
  // Only if *options.files* is not provided
@@ -34,11 +34,13 @@ async function command(config, options) {
34
34
  workerPool = (0, extractWorkerPool_1.createExtractWorkerPool)(options.workersOptions);
35
35
  }
36
36
  spinner.start();
37
+ let extractionResult;
37
38
  try {
38
- await Promise.all(catalogs.map(async (catalog) => {
39
+ extractionResult = await Promise.all(catalogs.map(async (catalog) => {
39
40
  const result = await catalog.make(Object.assign(Object.assign({}, options), { orderBy: config.orderBy, workerPool }));
40
41
  catalogStats[(0, normalize_path_1.default)(path_1.default.relative(config.rootDir, catalog.path))] = result || {};
41
42
  commandSuccess && (commandSuccess = Boolean(result));
43
+ return { catalog, messagesByLocale: result };
42
44
  }));
43
45
  }
44
46
  finally {
@@ -70,7 +72,7 @@ async function command(config, options) {
70
72
  try {
71
73
  const module = require(`./services/${moduleName}`);
72
74
  await module
73
- .default(config, options)
75
+ .default(config, options, extractionResult)
74
76
  .then(console.log)
75
77
  .catch(console.error);
76
78
  }
@@ -0,0 +1,4 @@
1
+ import { MessageType } from "@lingui/conf";
2
+ import { TranslationIoSegment } from "./translationio-api";
3
+ export declare function createSegmentFromLinguiItem(key: string, item: MessageType): TranslationIoSegment;
4
+ export declare function createLinguiItemFromSegment(segment: TranslationIoSegment): readonly [string, MessageType];
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSegmentFromLinguiItem = createSegmentFromLinguiItem;
4
+ exports.createLinguiItemFromSegment = createLinguiItemFromSegment;
5
+ const generateMessageId_1 = require("@lingui/message-utils/generateMessageId");
6
+ const EXPLICIT_ID_FLAG = "js-lingui-explicit-id";
7
+ const EXPLICIT_ID_AND_CONTEXT_FLAG = "js-lingui-explicit-id-and-context";
8
+ function isGeneratedId(id, message) {
9
+ return id === (0, generateMessageId_1.generateMessageId)(message.message, message.context);
10
+ }
11
+ const joinOrigin = (origin) => origin.join(":");
12
+ const splitOrigin = (origin) => {
13
+ const [file, line] = origin.split(":");
14
+ return [file, line ? Number(line) : null];
15
+ };
16
+ function createSegmentFromLinguiItem(key, item) {
17
+ const itemHasExplicitId = !isGeneratedId(key, item);
18
+ const itemHasContext = !!item.context;
19
+ const segment = {
20
+ type: "source", // No way to edit text for source language (inside code), so not using "key" here
21
+ source: "",
22
+ context: "",
23
+ references: [],
24
+ comment: "",
25
+ };
26
+ // For segment.source & segment.context, we must remain compatible with projects created/synced before Lingui V4
27
+ if (itemHasExplicitId) {
28
+ segment.source = item.message || item.translation;
29
+ segment.context = key;
30
+ }
31
+ else {
32
+ segment.source = item.message || item.translation;
33
+ if (itemHasContext) {
34
+ segment.context = item.context;
35
+ }
36
+ }
37
+ if (item.origin) {
38
+ segment.references = item.origin.map(joinOrigin);
39
+ }
40
+ // Since Lingui v4, when using explicit IDs, Lingui automatically adds 'js-lingui-explicit-id' to the extractedComments array
41
+ const comments = [];
42
+ if (itemHasExplicitId) {
43
+ if (itemHasContext) {
44
+ // segment.context is already used for the explicit ID, so we need to pass the context (for translators) in segment.comment
45
+ comments.push(item.context, EXPLICIT_ID_AND_CONTEXT_FLAG);
46
+ }
47
+ else {
48
+ comments.push(EXPLICIT_ID_FLAG);
49
+ }
50
+ }
51
+ segment.comment = [...comments, ...(item.comments || [])].join(" | ");
52
+ return segment;
53
+ }
54
+ function createLinguiItemFromSegment(segment) {
55
+ var _a, _b, _c;
56
+ const segmentHasExplicitId = (_a = segment.comment) === null || _a === void 0 ? void 0 : _a.includes(EXPLICIT_ID_FLAG);
57
+ const segmentHasExplicitIdAndContext = (_b = segment.comment) === null || _b === void 0 ? void 0 : _b.includes(EXPLICIT_ID_AND_CONTEXT_FLAG);
58
+ const item = {
59
+ translation: segment.target,
60
+ origin: ((_c = segment.references) === null || _c === void 0 ? void 0 : _c.length)
61
+ ? segment.references.map(splitOrigin)
62
+ : [],
63
+ message: segment.source,
64
+ comments: [],
65
+ };
66
+ let id = null;
67
+ if (segmentHasExplicitId || segmentHasExplicitIdAndContext) {
68
+ id = segment.context;
69
+ }
70
+ else {
71
+ id = (0, generateMessageId_1.generateMessageId)(segment.source, segment.context);
72
+ item.context = segment.context;
73
+ }
74
+ if (segment.comment) {
75
+ item.comments = segment.comment.split(" | ").filter(
76
+ // drop flags from comments
77
+ (comment) => comment !== EXPLICIT_ID_AND_CONTEXT_FLAG && comment !== EXPLICIT_ID_FLAG);
78
+ // We recompose a target PO Item that is consistent with the source PO Item
79
+ if (segmentHasExplicitIdAndContext) {
80
+ item.context = item.comments.shift();
81
+ }
82
+ }
83
+ return [id, item];
84
+ }
@@ -0,0 +1,48 @@
1
+ export type TranslationIoSyncRequest = {
2
+ client: "lingui";
3
+ version: string;
4
+ source_language: string;
5
+ target_languages: string[];
6
+ segments: TranslationIoSegment[];
7
+ purge?: boolean;
8
+ };
9
+ export type TranslationIoInitRequest = {
10
+ client: "lingui";
11
+ version: string;
12
+ source_language: string;
13
+ target_languages: string[];
14
+ segments: {
15
+ [locale: string]: TranslationIoSegment[];
16
+ };
17
+ };
18
+ export type TranslationIoSegment = {
19
+ type: string;
20
+ source: string;
21
+ target?: string;
22
+ context?: string;
23
+ references?: string[];
24
+ comment?: string;
25
+ };
26
+ export type TranslationIoProject = {
27
+ name: string;
28
+ url: string;
29
+ };
30
+ export type TranslationIoResponse = {
31
+ errors?: string[];
32
+ project?: TranslationIoProject;
33
+ segments?: {
34
+ [locale: string]: TranslationIoSegment[];
35
+ };
36
+ };
37
+ export type FetchResult<T> = {
38
+ data: T;
39
+ error: undefined;
40
+ } | {
41
+ error: {
42
+ response: Response;
43
+ message: string;
44
+ };
45
+ data: undefined;
46
+ };
47
+ export declare function tioSync(request: TranslationIoSyncRequest, apiKey: string): Promise<FetchResult<TranslationIoResponse>>;
48
+ export declare function tioInit(request: TranslationIoInitRequest, apiKey: string): Promise<FetchResult<TranslationIoResponse>>;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tioSync = tioSync;
4
+ exports.tioInit = tioInit;
5
+ // todo: need to enable strictNullChecks to support this kind of type narrowing
6
+ // export type TranslationIoResponse =
7
+ // | TranslationIoErrorResponse
8
+ // | TranslationProjectResponse
9
+ //
10
+ // type TranslationIoErrorResponse = {
11
+ // errors: string[]
12
+ // }
13
+ // type TranslationProjectResponse = {
14
+ // errors: null
15
+ // project: TranslationIoProject
16
+ // segments: { [locale: string]: TranslationIoSegment[] }
17
+ // }
18
+ async function post(url, request) {
19
+ const response = await fetch(url, {
20
+ method: "POST",
21
+ headers: {
22
+ "Content-Type": "application/json",
23
+ },
24
+ body: JSON.stringify(request),
25
+ });
26
+ if (!response.ok && response.status !== 400) {
27
+ return {
28
+ error: {
29
+ response,
30
+ message: `Request failed with ${response.status} ${response.statusText} status. Body: ${await response.text()}`,
31
+ },
32
+ data: undefined,
33
+ };
34
+ }
35
+ return { data: await response.json(), error: undefined };
36
+ }
37
+ async function tioSync(request, apiKey) {
38
+ return post(`https://translation.io/api/v1/segments/sync.json?api_key=${apiKey}`, request);
39
+ }
40
+ async function tioInit(request, apiKey) {
41
+ return post(`https://translation.io/api/v1/segments/init.json?api_key=${apiKey}`, request);
42
+ }
@@ -1,3 +1,32 @@
1
1
  import { LinguiConfigNormalized } from "@lingui/conf";
2
2
  import { CliExtractOptions } from "../lingui-extract";
3
- export default function syncProcess(config: LinguiConfigNormalized, options: CliExtractOptions): Promise<string>;
3
+ import { TranslationIoProject, TranslationIoSegment } from "./translationIO/translationio-api";
4
+ import { Catalog } from "../api/catalog";
5
+ import { AllCatalogsType } from "../api/types";
6
+ type ExtractionResult = {
7
+ catalog: Catalog;
8
+ messagesByLocale: AllCatalogsType;
9
+ }[];
10
+ export default function syncProcess(config: LinguiConfigNormalized, options: CliExtractOptions, extractionResult: ExtractionResult): Promise<string>;
11
+ export declare function init(config: LinguiConfigNormalized, extractionResult: ExtractionResult): Promise<{
12
+ readonly success: false;
13
+ readonly errors: string[];
14
+ readonly project?: undefined;
15
+ } | {
16
+ readonly success: true;
17
+ readonly project: TranslationIoProject;
18
+ readonly errors?: string[];
19
+ }>;
20
+ export declare function sync(config: LinguiConfigNormalized, options: CliExtractOptions, extractionResult: ExtractionResult): Promise<{
21
+ readonly success: false;
22
+ readonly errors: string[];
23
+ readonly project?: undefined;
24
+ } | {
25
+ readonly success: true;
26
+ readonly project: TranslationIoProject;
27
+ readonly errors?: string[];
28
+ }>;
29
+ export declare function writeSegmentsToCatalogs(config: LinguiConfigNormalized, sourceLocale: string, extractionResult: ExtractionResult, segmentsPerLocale: {
30
+ [locale: string]: TranslationIoSegment[];
31
+ }): Promise<void>;
32
+ export {};
@@ -4,130 +4,103 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = syncProcess;
7
+ exports.init = init;
8
+ exports.sync = sync;
9
+ exports.writeSegmentsToCatalogs = writeSegmentsToCatalogs;
7
10
  const fs_1 = __importDefault(require("fs"));
8
11
  const path_1 = require("path");
9
- const pofile_1 = __importDefault(require("pofile"));
10
- const https_1 = __importDefault(require("https"));
11
- const glob_1 = require("glob");
12
- const date_fns_1 = require("date-fns");
13
- const EXPLICIT_ID_FLAG = "js-lingui-explicit-id";
14
- const EXPLICIT_ID_AND_CONTEXT_FLAG = "js-lingui-explicit-id-and-context";
15
- const getCreateHeaders = (language) => ({
16
- "POT-Creation-Date": (0, date_fns_1.format)(new Date(), "yyyy-MM-dd HH:mmxxxx"),
17
- "MIME-Version": "1.0",
18
- "Content-Type": "text/plain; charset=utf-8",
19
- "Content-Transfer-Encoding": "8bit",
20
- "X-Generator": "@lingui/cli",
21
- Language: language,
22
- });
12
+ const translationio_api_1 = require("./translationIO/translationio-api");
13
+ const catalog_1 = require("../api/catalog");
14
+ const segment_converters_1 = require("./translationIO/segment-converters");
23
15
  const getTargetLocales = (config) => {
24
16
  const sourceLocale = config.sourceLocale || "en";
25
17
  const pseudoLocale = config.pseudoLocale || "pseudo";
26
18
  return config.locales.filter((value) => value != sourceLocale && value != pseudoLocale);
27
19
  };
28
- const validCatalogFormat = (config) => {
29
- if (typeof config.format === "string") {
30
- return config.format === "po";
31
- }
32
- return config.format.catalogExtension === ".po";
33
- };
34
20
  // Main sync method, call "Init" or "Sync" depending on the project context
35
- async function syncProcess(config, options) {
36
- if (!validCatalogFormat(config)) {
37
- console.error(`\n----------\nTranslation.io service is only compatible with the "po" format. Please update your Lingui configuration accordingly.\n----------`);
38
- process.exit(1);
21
+ async function syncProcess(config, options, extractionResult) {
22
+ const reportSuccess = (project) => {
23
+ return `\n----------\nProject successfully synchronized. Please use this URL to translate: ${project.url}\n----------`;
24
+ };
25
+ const reportError = (errors) => {
26
+ throw `\n----------\nSynchronization with Translation.io failed: ${errors.join(", ")}\n----------`;
27
+ };
28
+ const { success, project, errors } = await init(config, extractionResult);
29
+ if (success) {
30
+ return reportSuccess(project);
39
31
  }
40
- return await new Promise((resolve, reject) => {
41
- const successCallback = (project) => {
42
- resolve(`\n----------\nProject successfully synchronized. Please use this URL to translate: ${project.url}\n----------`);
43
- };
44
- const failCallback = (errors) => {
45
- reject(`\n----------\nSynchronization with Translation.io failed: ${errors.join(", ")}\n----------`);
46
- };
47
- init(config, options, successCallback, (errors) => {
48
- if (errors.length &&
49
- errors[0] === "This project has already been initialized.") {
50
- sync(config, options, successCallback, failCallback);
51
- }
52
- else {
53
- failCallback(errors);
54
- }
55
- });
56
- });
32
+ if (errors[0] === "This project has already been initialized.") {
33
+ const { success, project, errors } = await sync(config, options, extractionResult);
34
+ if (success) {
35
+ return reportSuccess(project);
36
+ }
37
+ return reportError(errors);
38
+ }
39
+ return reportError(errors);
57
40
  }
58
41
  // Initialize project with source and existing translations (only first time!)
59
42
  // Cf. https://translation.io/docs/create-library#initialization
60
- function init(config, options, successCallback, failCallback) {
43
+ async function init(config, extractionResult) {
61
44
  const sourceLocale = config.sourceLocale || "en";
62
45
  const targetLocales = getTargetLocales(config);
63
- const paths = poPathsPerLocale(config);
64
46
  const segments = {};
65
47
  targetLocales.forEach((targetLocale) => {
66
48
  segments[targetLocale] = [];
67
49
  });
68
50
  // Create segments from source locale PO items
69
- paths[sourceLocale].forEach((path) => {
70
- const raw = fs_1.default.readFileSync(path).toString();
71
- const po = pofile_1.default.parse(raw);
72
- po.items
73
- .filter((item) => !item["obsolete"])
74
- .forEach((item) => {
51
+ for (const { messagesByLocale } of extractionResult) {
52
+ const messages = messagesByLocale[sourceLocale];
53
+ Object.entries(messages).forEach(([key, entry]) => {
54
+ if (entry.obsolete)
55
+ return;
75
56
  targetLocales.forEach((targetLocale) => {
76
- const newSegment = createSegmentFromPoItem(item);
77
- segments[targetLocale].push(newSegment);
57
+ segments[targetLocale].push((0, segment_converters_1.createSegmentFromLinguiItem)(key, entry));
78
58
  });
79
59
  });
80
- });
60
+ }
81
61
  // Add translations to segments from target locale PO items
82
- targetLocales.forEach((targetLocale) => {
83
- paths[targetLocale].forEach((path) => {
84
- const raw = fs_1.default.readFileSync(path).toString();
85
- const po = pofile_1.default.parse(raw);
86
- po.items
87
- .filter((item) => !item["obsolete"])
88
- .forEach((item, index) => {
89
- segments[targetLocale][index].target = item.msgstr[0];
62
+ for (const { messagesByLocale } of extractionResult) {
63
+ for (const targetLocale of targetLocales) {
64
+ const messages = messagesByLocale[targetLocale];
65
+ Object.entries(messages)
66
+ .filter(([, entry]) => !entry.obsolete)
67
+ .forEach(([, entry], index) => {
68
+ segments[targetLocale][index].target = entry.translation;
90
69
  });
91
- });
92
- });
93
- const request = {
70
+ }
71
+ }
72
+ const { data, error } = await (0, translationio_api_1.tioInit)({
94
73
  client: "lingui",
95
74
  version: require("@lingui/core/package.json").version,
96
75
  source_language: sourceLocale,
97
76
  target_languages: targetLocales,
98
77
  segments: segments,
99
- };
100
- postTio("init", request, config.service.apiKey, (response) => {
101
- if (response.errors) {
102
- failCallback(response.errors);
103
- }
104
- else {
105
- saveSegmentsToTargetPos(config, paths, response.segments);
106
- successCallback(response.project);
107
- }
108
- }, (error) => {
109
- console.error(`\n----------\nSynchronization with Translation.io failed: ${error}\n----------`);
110
- });
78
+ }, config.service.apiKey);
79
+ if (error) {
80
+ return { success: false, errors: [error.message] };
81
+ }
82
+ if (data.errors) {
83
+ return { success: false, errors: data.errors };
84
+ }
85
+ await writeSegmentsToCatalogs(config, sourceLocale, extractionResult, data.segments);
86
+ return { success: true, project: data.project };
111
87
  }
112
88
  // Send all source text from PO to Translation.io and create new PO based on received translations
113
89
  // Cf. https://translation.io/docs/create-library#synchronization
114
- function sync(config, options, successCallback, failCallback) {
90
+ async function sync(config, options, extractionResult) {
115
91
  const sourceLocale = config.sourceLocale || "en";
116
92
  const targetLocales = getTargetLocales(config);
117
- const paths = poPathsPerLocale(config);
118
93
  const segments = [];
119
94
  // Create segments with correct source
120
- paths[sourceLocale].forEach((path) => {
121
- const raw = fs_1.default.readFileSync(path).toString();
122
- const po = pofile_1.default.parse(raw);
123
- po.items
124
- .filter((item) => !item["obsolete"])
125
- .forEach((item) => {
126
- const newSegment = createSegmentFromPoItem(item);
127
- segments.push(newSegment);
95
+ for (const { messagesByLocale } of extractionResult) {
96
+ const messages = messagesByLocale[sourceLocale];
97
+ Object.entries(messages).forEach(([key, entry]) => {
98
+ if (entry.obsolete)
99
+ return;
100
+ segments.push((0, segment_converters_1.createSegmentFromLinguiItem)(key, entry));
128
101
  });
129
- });
130
- const request = {
102
+ }
103
+ const { data, error } = await (0, translationio_api_1.tioSync)({
131
104
  client: "lingui",
132
105
  version: require("@lingui/core/package.json").version,
133
106
  source_language: sourceLocale,
@@ -135,172 +108,47 @@ function sync(config, options, successCallback, failCallback) {
135
108
  segments: segments,
136
109
  // Sync and then remove unused segments (not present in the local application) from Translation.io
137
110
  purge: Boolean(options.clean),
138
- };
139
- postTio("sync", request, config.service.apiKey, (response) => {
140
- if (response.errors) {
141
- failCallback(response.errors);
142
- }
143
- else {
144
- saveSegmentsToTargetPos(config, paths, response.segments);
145
- successCallback(response.project);
146
- }
147
- }, (error) => {
148
- console.error(`\n----------\nSynchronization with Translation.io failed: ${error}\n----------`);
149
- });
150
- }
151
- function createSegmentFromPoItem(item) {
152
- const itemHasExplicitId = item.extractedComments.includes(EXPLICIT_ID_FLAG);
153
- const itemHasContext = item.msgctxt != null;
154
- const segment = {
155
- type: "source", // No way to edit text for source language (inside code), so not using "key" here
156
- source: "",
157
- context: "",
158
- references: [],
159
- comment: "",
160
- };
161
- // For segment.source & segment.context, we must remain compatible with projects created/synced before Lingui V4
162
- if (itemHasExplicitId) {
163
- segment.source = item.msgstr[0];
164
- segment.context = item.msgid;
165
- }
166
- else {
167
- segment.source = item.msgid;
168
- if (itemHasContext) {
169
- segment.context = item.msgctxt;
170
- }
111
+ }, config.service.apiKey);
112
+ if (error) {
113
+ return { success: false, errors: [error.message] };
171
114
  }
172
- if (item.references.length) {
173
- segment.references = item.references;
115
+ if (data.errors) {
116
+ return { success: false, errors: data.errors };
174
117
  }
175
- // Since Lingui v4, when using explicit IDs, Lingui automatically adds 'js-lingui-explicit-id' to the extractedComments array
176
- if (item.extractedComments.length) {
177
- segment.comment = item.extractedComments.join(" | ");
178
- if (itemHasExplicitId && itemHasContext) {
179
- // segment.context is already used for the explicit ID, so we need to pass the context (for translators) in segment.comment
180
- segment.comment = `${item.msgctxt} | ${segment.comment}`;
181
- // Replace the flag to let us know how to recompose a target PO Item that is consistent with the source PO Item
182
- segment.comment = segment.comment.replace(EXPLICIT_ID_FLAG, EXPLICIT_ID_AND_CONTEXT_FLAG);
183
- }
184
- }
185
- return segment;
118
+ await writeSegmentsToCatalogs(config, sourceLocale, extractionResult, data.segments);
119
+ return { success: true, project: data.project };
186
120
  }
187
- function createPoItemFromSegment(segment) {
188
- var _a, _b;
189
- const segmentHasExplicitId = (_a = segment.comment) === null || _a === void 0 ? void 0 : _a.includes(EXPLICIT_ID_FLAG);
190
- const segmentHasExplicitIdAndContext = (_b = segment.comment) === null || _b === void 0 ? void 0 : _b.includes(EXPLICIT_ID_AND_CONTEXT_FLAG);
191
- const item = new pofile_1.default.Item();
192
- if (segmentHasExplicitId || segmentHasExplicitIdAndContext) {
193
- item.msgid = segment.context;
194
- }
195
- else {
196
- item.msgid = segment.source;
197
- item.msgctxt = segment.context;
198
- }
199
- item.msgstr = [segment.target];
200
- item.references =
201
- segment.references && segment.references.length ? segment.references : [];
202
- if (segment.comment) {
203
- segment.comment = segment.comment.replace(EXPLICIT_ID_AND_CONTEXT_FLAG, EXPLICIT_ID_FLAG);
204
- item.extractedComments = segment.comment ? segment.comment.split(" | ") : [];
205
- // We recompose a target PO Item that is consistent with the source PO Item
206
- if (segmentHasExplicitIdAndContext) {
207
- item.msgctxt = item.extractedComments.shift();
208
- }
209
- }
210
- return item;
211
- }
212
- function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) {
213
- Object.keys(segmentsPerLocale).forEach((targetLocale) => {
214
- // Remove existing target POs and JS for this target locale
215
- paths[targetLocale].forEach((path) => {
216
- const jsPath = path.replace(/\.po?$/, "") + ".js";
217
- const dirPath = (0, path_1.dirname)(path);
218
- // Remove PO, JS and empty dir
219
- if (fs_1.default.existsSync(path)) {
220
- fs_1.default.unlinkSync(path);
221
- }
222
- if (fs_1.default.existsSync(jsPath)) {
223
- fs_1.default.unlinkSync(jsPath);
224
- }
225
- if (fs_1.default.existsSync(dirPath) && fs_1.default.readdirSync(dirPath).length === 0) {
226
- fs_1.default.rmdirSync(dirPath);
227
- }
228
- });
229
- // Find target path (ignoring {name})
230
- const localePath = "".concat(config.catalogs[0].path
231
- .replace(/{locale}/g, targetLocale)
232
- .replace(/{name}/g, ""), ".po");
233
- const segments = segmentsPerLocale[targetLocale];
234
- const po = new pofile_1.default();
235
- po.headers = getCreateHeaders(targetLocale);
236
- const items = [];
237
- segments.forEach((segment) => {
238
- const item = createPoItemFromSegment(segment);
239
- items.push(item);
240
- });
241
- // Sort items by messageId
242
- po.items = items.sort((a, b) => {
243
- if (a.msgid < b.msgid) {
244
- return -1;
245
- }
246
- if (a.msgid > b.msgid) {
247
- return 1;
248
- }
249
- return 0;
250
- });
251
- // Check that localePath directory exists and save PO file
252
- fs_1.default.promises.mkdir((0, path_1.dirname)(localePath), { recursive: true }).then(() => {
253
- po.save(localePath, (err) => {
254
- if (err) {
255
- console.error("Error while saving target PO files:");
256
- console.error(err);
257
- process.exit(1);
121
+ async function writeSegmentsToCatalogs(config, sourceLocale, extractionResult, segmentsPerLocale) {
122
+ // Create segments from source locale PO items
123
+ for (const { catalog, messagesByLocale } of extractionResult) {
124
+ const sourceMessages = messagesByLocale[sourceLocale];
125
+ for (const targetLocale of Object.keys(segmentsPerLocale)) {
126
+ // Remove existing target POs and JS for this target locale
127
+ {
128
+ const path = catalog.getFilename(targetLocale);
129
+ const jsPath = path.replace(new RegExp(`${catalog.format.getCatalogExtension()}$`), "") + ".js";
130
+ const dirPath = (0, path_1.dirname)(path);
131
+ // todo: check tests and all these logic, maybe it could be simplified to just drop the folder
132
+ // Remove PO, JS and empty dir
133
+ if (fs_1.default.existsSync(path)) {
134
+ await fs_1.default.promises.unlink(path);
135
+ }
136
+ if (fs_1.default.existsSync(jsPath)) {
137
+ await fs_1.default.promises.unlink(jsPath);
138
+ }
139
+ if (fs_1.default.existsSync(dirPath) && fs_1.default.readdirSync(dirPath).length === 0) {
140
+ await fs_1.default.promises.rmdir(dirPath);
258
141
  }
259
- });
260
- });
261
- });
262
- }
263
- function poPathsPerLocale(config) {
264
- const paths = {};
265
- config.locales.forEach((locale) => {
266
- paths[locale] = [];
267
- config.catalogs.forEach((catalog) => {
268
- const path = "".concat(catalog.path.replace(/{locale}/g, locale).replace(/{name}/g, "*"), ".po");
269
- // If {name} is present (replaced by *), list all the existing POs
270
- if (path.includes("*")) {
271
- paths[locale] = paths[locale].concat((0, glob_1.globSync)(path));
272
- }
273
- else {
274
- paths[locale].push(path);
275
142
  }
276
- });
277
- });
278
- return paths;
279
- }
280
- function postTio(action, request, apiKey, successCallback, failCallback) {
281
- const jsonRequest = JSON.stringify(request);
282
- const options = {
283
- hostname: "translation.io",
284
- path: `/api/v1/segments/${action}.json?api_key=${apiKey}`,
285
- method: "POST",
286
- headers: {
287
- "Content-Type": "application/json",
288
- },
289
- };
290
- const req = https_1.default.request(options, (res) => {
291
- res.setEncoding("utf8");
292
- let body = "";
293
- res.on("data", (chunk) => {
294
- body = body.concat(chunk);
295
- });
296
- res.on("end", () => {
297
- const response = JSON.parse(body);
298
- successCallback(response);
299
- });
300
- });
301
- req.on("error", (e) => {
302
- failCallback(e);
303
- });
304
- req.write(jsonRequest);
305
- req.end();
143
+ const translations = Object.fromEntries(segmentsPerLocale[targetLocale].map((segment) => (0, segment_converters_1.createLinguiItemFromSegment)(segment)));
144
+ const messages = Object.fromEntries(Object.entries(sourceMessages).map(([key, entry]) => {
145
+ var _a;
146
+ return [
147
+ key,
148
+ Object.assign(Object.assign({}, entry), { translation: (_a = translations[key]) === null || _a === void 0 ? void 0 : _a.translation }),
149
+ ];
150
+ }));
151
+ await catalog.write(targetLocale, (0, catalog_1.order)(config.orderBy, messages));
152
+ }
153
+ }
306
154
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingui/cli",
3
- "version": "5.8.0",
3
+ "version": "5.9.0",
4
4
  "description": "CLI for working wit message catalogs",
5
5
  "keywords": [
6
6
  "cli",
@@ -62,12 +62,12 @@
62
62
  "@babel/parser": "^7.22.0",
63
63
  "@babel/runtime": "^7.21.0",
64
64
  "@babel/types": "^7.21.2",
65
- "@lingui/babel-plugin-extract-messages": "5.8.0",
66
- "@lingui/babel-plugin-lingui-macro": "5.8.0",
67
- "@lingui/conf": "5.8.0",
68
- "@lingui/core": "5.8.0",
69
- "@lingui/format-po": "5.8.0",
70
- "@lingui/message-utils": "5.8.0",
65
+ "@lingui/babel-plugin-extract-messages": "5.9.0",
66
+ "@lingui/babel-plugin-lingui-macro": "5.9.0",
67
+ "@lingui/conf": "5.9.0",
68
+ "@lingui/core": "5.9.0",
69
+ "@lingui/format-po": "5.9.0",
70
+ "@lingui/message-utils": "5.9.0",
71
71
  "chokidar": "3.5.1",
72
72
  "cli-table": "^0.3.11",
73
73
  "commander": "^10.0.0",
@@ -93,7 +93,8 @@
93
93
  "@types/normalize-path": "^3.0.0",
94
94
  "mock-fs": "^5.2.0",
95
95
  "mockdate": "^3.0.5",
96
+ "msw": "^2.12.7",
96
97
  "ts-node": "^10.9.2"
97
98
  },
98
- "gitHead": "83d0513bdda9ff14003a05d376c7fedf860dd7ee"
99
+ "gitHead": "491d4c17651c3f76116fe7f63f6bb8a554bef8da"
99
100
  }