gtx-cli 1.1.7-alpha.1 → 1.1.7-alpha.2

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.
@@ -13,4 +13,7 @@ export declare function checkFileTranslations(apiKey: string, baseUrl: string, d
13
13
  versionId: string;
14
14
  fileName: string;
15
15
  };
16
- }, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string): Promise<boolean>;
16
+ }, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string, downloadStatus: {
17
+ downloaded: Set<string>;
18
+ failed: Set<string>;
19
+ }): Promise<boolean>;
@@ -17,6 +17,7 @@ const chalk_1 = __importDefault(require("chalk"));
17
17
  const console_1 = require("../console/console");
18
18
  const generaltranslation_1 = require("generaltranslation");
19
19
  const downloadFile_1 = require("./downloadFile");
20
+ const downloadFileBatch_1 = require("./downloadFileBatch");
20
21
  /**
21
22
  * Checks the status of translations for a given version ID
22
23
  * @param apiKey - The API key for the General Translation API
@@ -27,154 +28,26 @@ const downloadFile_1 = require("./downloadFile");
27
28
  * @param timeoutDuration - The timeout duration for the wait in seconds
28
29
  * @returns True if all translations are deployed, false otherwise
29
30
  */
30
- function checkFileTranslations(apiKey, baseUrl, data, locales, timeoutDuration, resolveOutputPath) {
31
+ function checkFileTranslations(apiKey, baseUrl, data, locales, timeoutDuration, resolveOutputPath, downloadStatus) {
31
32
  return __awaiter(this, void 0, void 0, function* () {
32
33
  const startTime = Date.now();
33
34
  const spinner = yield (0, console_1.displayLoadingAnimation)('Waiting for translation...');
34
- const downloadedFiles = new Set(); // Track which file+locale combinations have been downloaded
35
- let fileQueryData = [];
36
35
  // Initialize the query data
37
- for (const file in data) {
38
- for (const locale of locales) {
39
- fileQueryData.push({
40
- versionId: data[file].versionId,
41
- fileName: data[file].fileName,
42
- locale,
43
- });
44
- }
45
- }
46
- const checkDeployment = () => __awaiter(this, void 0, void 0, function* () {
47
- try {
48
- // Only query for files that haven't been downloaded yet
49
- const currentQueryData = fileQueryData.filter((item) => !downloadedFiles.has(`${item.fileName}:${item.locale}`));
50
- // If all files have been downloaded, we're done
51
- if (currentQueryData.length === 0) {
52
- return true;
53
- }
54
- const response = yield fetch(`${baseUrl}/v1/project/translations/files/retrieve`, {
55
- method: 'POST',
56
- headers: Object.assign({ 'Content-Type': 'application/json' }, (apiKey && { 'x-gt-api-key': apiKey })),
57
- body: JSON.stringify({ files: currentQueryData }),
58
- });
59
- if (response.ok) {
60
- const responseData = yield response.json();
61
- const translations = responseData.translations || [];
62
- // Process available translations
63
- for (const translation of translations) {
64
- const locale = translation.locale;
65
- const fileName = translation.fileName;
66
- const translationId = translation.id;
67
- if (translation.isReady && fileName) {
68
- // Mark this file+locale as downloaded
69
- downloadedFiles.add(`${fileName}:${locale}`);
70
- // Download the file
71
- const outputPath = resolveOutputPath(fileName, locale);
72
- yield (0, downloadFile_1.downloadFile)(baseUrl, apiKey, translationId, outputPath);
73
- }
74
- }
75
- // Force a refresh of the spinner display
76
- const statusText = generateStatusSuffixText(downloadedFiles, fileQueryData);
77
- // Clear and reapply the suffix to force a refresh
78
- spinner.suffixText = statusText;
79
- }
80
- if (downloadedFiles.size === fileQueryData.length) {
81
- return true;
82
- }
83
- return false;
84
- }
85
- catch (error) {
86
- console.error('Error checking translation status:', error);
87
- return false;
88
- }
89
- });
90
- /**
91
- * Generates a formatted status text showing translation progress
92
- * @param downloadedFiles - Set of downloaded file+locale combinations
93
- * @param fileQueryData - Array of file query data objects
94
- * @returns Formatted status text
95
- */
96
- function generateStatusSuffixText(downloadedFiles, fileQueryData) {
97
- var _a;
98
- // Simple progress indicator
99
- const progressText = chalk_1.default.green(`[${downloadedFiles.size}/${fileQueryData.length}]`) +
100
- ` translations completed`;
101
- // Get terminal height to adapt our output
102
- const terminalHeight = process.stdout.rows || 24; // Default to 24 if undefined
103
- // If terminal is very small, just show the basic progress
104
- if (terminalHeight < 6) {
105
- return `\n${progressText}`;
106
- }
107
- const newSuffixText = [`\n${progressText}`];
108
- // Organize data by filename
109
- const fileStatus = new Map();
110
- // Initialize with all files and locales from fileQueryData
111
- for (const item of fileQueryData) {
112
- if (!fileStatus.has(item.fileName)) {
113
- fileStatus.set(item.fileName, {
114
- completed: new Set(),
115
- pending: new Set([item.locale]),
116
- });
117
- }
118
- else {
119
- (_a = fileStatus.get(item.fileName)) === null || _a === void 0 ? void 0 : _a.pending.add(item.locale);
120
- }
121
- }
122
- // Mark which ones are completed
123
- for (const fileLocale of downloadedFiles) {
124
- const [fileName, locale] = fileLocale.split(':');
125
- const status = fileStatus.get(fileName);
126
- if (status) {
127
- status.pending.delete(locale);
128
- status.completed.add(locale);
129
- }
130
- }
131
- // Calculate how many files we can show based on terminal height
132
- // Each file takes 1 line now
133
- const filesArray = Array.from(fileStatus.entries());
134
- const maxFilesToShow = Math.min(filesArray.length, terminalHeight - 3 // Header + progress + buffer
135
- );
136
- // Display each file with its status on a single line
137
- for (let i = 0; i < maxFilesToShow; i++) {
138
- const [fileName, status] = filesArray[i];
139
- // Create condensed locale status
140
- const localeStatuses = [];
141
- // Add completed locales
142
- if (status.completed.size > 0) {
143
- const completedCodes = Array.from(status.completed)
144
- .map((locale) => (0, generaltranslation_1.getLocaleProperties)(locale).code)
145
- .join(', ');
146
- localeStatuses.push(chalk_1.default.green(`${completedCodes}`));
147
- }
148
- // Add pending locales
149
- if (status.pending.size > 0) {
150
- const pendingCodes = Array.from(status.pending)
151
- .map((locale) => (0, generaltranslation_1.getLocaleProperties)(locale).code)
152
- .join(', ');
153
- localeStatuses.push(chalk_1.default.yellow(`${pendingCodes}`));
154
- }
155
- // Format the line
156
- newSuffixText.push(`${chalk_1.default.bold(fileName)} [${localeStatuses.join(', ')}]`);
157
- }
158
- // If we couldn't show all files, add an indicator
159
- if (filesArray.length > maxFilesToShow) {
160
- newSuffixText.push(`... and ${filesArray.length - maxFilesToShow} more files`);
161
- }
162
- return newSuffixText.join('\n');
163
- }
164
- // Calculate time until next 5-second interval since startTime
165
- const msUntilNextInterval = Math.max(0, 5000 - ((Date.now() - startTime) % 5000));
36
+ const fileQueryData = prepareFileQueryData(data, locales);
166
37
  // Do first check immediately
167
- const initialCheck = yield checkDeployment();
38
+ const initialCheck = yield checkTranslationDeployment(baseUrl, apiKey, fileQueryData, downloadStatus, spinner, resolveOutputPath);
168
39
  if (initialCheck) {
169
- spinner.succeed(chalk_1.default.green('All translations are live!'));
40
+ spinner.succeed(chalk_1.default.green('Files translated!'));
170
41
  return true;
171
42
  }
43
+ // Calculate time until next 5-second interval since startTime
44
+ const msUntilNextInterval = Math.max(0, 5000 - ((Date.now() - startTime) % 5000));
172
45
  return new Promise((resolve) => {
173
46
  let intervalCheck;
174
47
  // Start the interval aligned with the original request time
175
48
  setTimeout(() => {
176
49
  intervalCheck = setInterval(() => __awaiter(this, void 0, void 0, function* () {
177
- const isDeployed = yield checkDeployment();
50
+ const isDeployed = yield checkTranslationDeployment(baseUrl, apiKey, fileQueryData, downloadStatus, spinner, resolveOutputPath);
178
51
  const elapsed = Date.now() - startTime;
179
52
  if (isDeployed || elapsed >= timeoutDuration * 1000) {
180
53
  process.stdout.write('\n');
@@ -193,3 +66,189 @@ function checkFileTranslations(apiKey, baseUrl, data, locales, timeoutDuration,
193
66
  });
194
67
  });
195
68
  }
69
+ /**
70
+ * Prepares the file query data from input data and locales
71
+ */
72
+ function prepareFileQueryData(data, locales) {
73
+ const fileQueryData = [];
74
+ for (const file in data) {
75
+ for (const locale of locales) {
76
+ fileQueryData.push({
77
+ versionId: data[file].versionId,
78
+ fileName: data[file].fileName,
79
+ locale,
80
+ });
81
+ }
82
+ }
83
+ return fileQueryData;
84
+ }
85
+ /**
86
+ * Generates a formatted status text showing translation progress
87
+ * @param downloadedFiles - Set of downloaded file+locale combinations
88
+ * @param fileQueryData - Array of file query data objects
89
+ * @returns Formatted status text
90
+ */
91
+ function generateStatusSuffixText(downloadStatus, fileQueryData) {
92
+ var _a;
93
+ // Simple progress indicator
94
+ const progressText = chalk_1.default.green(`[${downloadStatus.downloaded.size + downloadStatus.failed.size}/${fileQueryData.length}]`) + ` translations completed`;
95
+ // Get terminal height to adapt our output
96
+ const terminalHeight = process.stdout.rows || 24; // Default to 24 if undefined
97
+ // If terminal is very small, just show the basic progress
98
+ if (terminalHeight < 6) {
99
+ return `\n${progressText}`;
100
+ }
101
+ const newSuffixText = [`\n${progressText}`];
102
+ // Organize data by filename
103
+ const fileStatus = new Map();
104
+ // Initialize with all files and locales from fileQueryData
105
+ for (const item of fileQueryData) {
106
+ if (!fileStatus.has(item.fileName)) {
107
+ fileStatus.set(item.fileName, {
108
+ completed: new Set(),
109
+ pending: new Set([item.locale]),
110
+ failed: new Set(),
111
+ });
112
+ }
113
+ else {
114
+ (_a = fileStatus.get(item.fileName)) === null || _a === void 0 ? void 0 : _a.pending.add(item.locale);
115
+ }
116
+ }
117
+ // Mark which ones are completed or failed
118
+ for (const fileLocale of downloadStatus.downloaded) {
119
+ const [fileName, locale] = fileLocale.split(':');
120
+ const status = fileStatus.get(fileName);
121
+ if (status) {
122
+ status.pending.delete(locale);
123
+ status.completed.add(locale);
124
+ }
125
+ }
126
+ for (const fileLocale of downloadStatus.failed) {
127
+ const [fileName, locale] = fileLocale.split(':');
128
+ const status = fileStatus.get(fileName);
129
+ if (status) {
130
+ status.pending.delete(locale);
131
+ status.failed.add(locale);
132
+ }
133
+ }
134
+ // Calculate how many files we can show based on terminal height
135
+ const filesArray = Array.from(fileStatus.entries());
136
+ const maxFilesToShow = Math.min(filesArray.length, terminalHeight - 3 // Header + progress + buffer
137
+ );
138
+ // Display each file with its status on a single line
139
+ for (let i = 0; i < maxFilesToShow; i++) {
140
+ const [fileName, status] = filesArray[i];
141
+ // Create condensed locale status
142
+ const localeStatuses = [];
143
+ // Add completed locales
144
+ if (status.completed.size > 0) {
145
+ const completedCodes = Array.from(status.completed)
146
+ .map((locale) => (0, generaltranslation_1.getLocaleProperties)(locale).code)
147
+ .join(', ');
148
+ localeStatuses.push(chalk_1.default.green(`${completedCodes}`));
149
+ }
150
+ // Add failed locales
151
+ if (status.failed.size > 0) {
152
+ const failedCodes = Array.from(status.failed)
153
+ .map((locale) => (0, generaltranslation_1.getLocaleProperties)(locale).code)
154
+ .join(', ');
155
+ localeStatuses.push(chalk_1.default.red(`${failedCodes}`));
156
+ }
157
+ // Add pending locales
158
+ if (status.pending.size > 0) {
159
+ const pendingCodes = Array.from(status.pending)
160
+ .map((locale) => (0, generaltranslation_1.getLocaleProperties)(locale).code)
161
+ .join(', ');
162
+ localeStatuses.push(chalk_1.default.yellow(`${pendingCodes}`));
163
+ }
164
+ // Format the line
165
+ newSuffixText.push(`${chalk_1.default.bold(fileName)} [${localeStatuses.join(', ')}]`);
166
+ }
167
+ // If we couldn't show all files, add an indicator
168
+ if (filesArray.length > maxFilesToShow) {
169
+ newSuffixText.push(`... and ${filesArray.length - maxFilesToShow} more files`);
170
+ }
171
+ return newSuffixText.join('\n');
172
+ }
173
+ /**
174
+ * Checks translation status and downloads ready files
175
+ */
176
+ function checkTranslationDeployment(baseUrl, apiKey, fileQueryData, downloadStatus, spinner, resolveOutputPath) {
177
+ return __awaiter(this, void 0, void 0, function* () {
178
+ try {
179
+ // Only query for files that haven't been downloaded yet
180
+ const currentQueryData = fileQueryData.filter((item) => !downloadStatus.downloaded.has(`${item.fileName}:${item.locale}`) &&
181
+ !downloadStatus.failed.has(`${item.fileName}:${item.locale}`));
182
+ // If all files have been downloaded, we're done
183
+ if (currentQueryData.length === 0) {
184
+ return true;
185
+ }
186
+ const response = yield fetch(`${baseUrl}/v1/project/translations/files/retrieve`, {
187
+ method: 'POST',
188
+ headers: Object.assign({ 'Content-Type': 'application/json' }, (apiKey && { 'x-gt-api-key': apiKey })),
189
+ body: JSON.stringify({ files: currentQueryData }),
190
+ });
191
+ if (response.ok) {
192
+ const responseData = yield response.json();
193
+ const translations = responseData.translations || [];
194
+ // Filter for ready translations
195
+ const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
196
+ if (readyTranslations.length > 0) {
197
+ // Prepare batch download data
198
+ const batchFiles = readyTranslations.map((translation) => {
199
+ const locale = translation.locale;
200
+ const fileName = translation.fileName;
201
+ const translationId = translation.id;
202
+ const outputPath = resolveOutputPath(fileName, locale);
203
+ return {
204
+ translationId,
205
+ outputPath,
206
+ fileLocale: `${fileName}:${locale}`,
207
+ };
208
+ });
209
+ // Use batch download if there are multiple files
210
+ if (batchFiles.length > 1) {
211
+ const batchResult = yield (0, downloadFileBatch_1.downloadFileBatch)(baseUrl, apiKey, batchFiles.map(({ translationId, outputPath }) => ({
212
+ translationId,
213
+ outputPath,
214
+ })));
215
+ // Process results
216
+ batchFiles.forEach((file) => {
217
+ const { translationId, fileLocale } = file;
218
+ if (batchResult.successful.includes(translationId)) {
219
+ downloadStatus.downloaded.add(fileLocale);
220
+ }
221
+ else if (batchResult.failed.includes(translationId)) {
222
+ downloadStatus.failed.add(fileLocale);
223
+ }
224
+ });
225
+ }
226
+ else if (batchFiles.length === 1) {
227
+ // For a single file, use the original downloadFile method
228
+ const file = batchFiles[0];
229
+ const result = yield (0, downloadFile_1.downloadFile)(baseUrl, apiKey, file.translationId, file.outputPath);
230
+ if (result) {
231
+ downloadStatus.downloaded.add(file.fileLocale);
232
+ }
233
+ else {
234
+ downloadStatus.failed.add(file.fileLocale);
235
+ }
236
+ }
237
+ }
238
+ // Force a refresh of the spinner display
239
+ const statusText = generateStatusSuffixText(downloadStatus, fileQueryData);
240
+ // Clear and reapply the suffix to force a refresh
241
+ spinner.suffixText = statusText;
242
+ }
243
+ if (downloadStatus.downloaded.size + downloadStatus.failed.size ===
244
+ fileQueryData.length) {
245
+ return true;
246
+ }
247
+ return false;
248
+ }
249
+ catch (error) {
250
+ console.error('Error checking translation status:', error);
251
+ return false;
252
+ }
253
+ });
254
+ }
@@ -1 +1 @@
1
- export declare function downloadFile(baseUrl: string, apiKey: string, translationId: string, outputPath: string): Promise<boolean>;
1
+ export declare function downloadFile(baseUrl: string, apiKey: string, translationId: string, outputPath: string, maxRetries?: number, retryDelay?: number): Promise<boolean>;
@@ -46,30 +46,45 @@ exports.downloadFile = downloadFile;
46
46
  const fs = __importStar(require("fs"));
47
47
  const path = __importStar(require("path"));
48
48
  // Helper function to download a file
49
- function downloadFile(baseUrl, apiKey, translationId, outputPath) {
50
- return __awaiter(this, void 0, void 0, function* () {
51
- try {
52
- const downloadResponse = yield fetch(`${baseUrl}/v1/project/translations/files/${translationId}/download`, {
53
- method: 'GET',
54
- headers: Object.assign({}, (apiKey && { 'x-gt-api-key': apiKey })),
55
- });
56
- if (downloadResponse.ok) {
57
- // Ensure the directory exists
58
- const dir = path.dirname(outputPath);
59
- if (!fs.existsSync(dir)) {
60
- fs.mkdirSync(dir, { recursive: true });
49
+ function downloadFile(baseUrl_1, apiKey_1, translationId_1, outputPath_1) {
50
+ return __awaiter(this, arguments, void 0, function* (baseUrl, apiKey, translationId, outputPath, maxRetries = 3, retryDelay = 1000) {
51
+ let retries = 0;
52
+ while (retries <= maxRetries) {
53
+ try {
54
+ const downloadResponse = yield fetch(`${baseUrl}/v1/project/translations/files/${translationId}/download`, {
55
+ method: 'GET',
56
+ headers: Object.assign({}, (apiKey && { 'x-gt-api-key': apiKey })),
57
+ });
58
+ if (downloadResponse.ok) {
59
+ // Ensure the directory exists
60
+ const dir = path.dirname(outputPath);
61
+ if (!fs.existsSync(dir)) {
62
+ fs.mkdirSync(dir, { recursive: true });
63
+ }
64
+ // Get the file data as an ArrayBuffer
65
+ const fileData = yield downloadResponse.arrayBuffer();
66
+ // Write the file to disk
67
+ fs.writeFileSync(outputPath, Buffer.from(fileData));
68
+ return true;
61
69
  }
62
- // Get the file data as an ArrayBuffer
63
- const fileData = yield downloadResponse.arrayBuffer();
64
- // Write the file to disk
65
- fs.writeFileSync(outputPath, Buffer.from(fileData));
66
- return true;
70
+ // If we get here, the response was not OK
71
+ if (retries >= maxRetries) {
72
+ console.error(`Failed to download file ${outputPath}. Status: ${downloadResponse.status} after ${maxRetries + 1} attempts.`);
73
+ return false;
74
+ }
75
+ // Increment retry counter and wait before next attempt
76
+ retries++;
77
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
78
+ }
79
+ catch (error) {
80
+ if (retries >= maxRetries) {
81
+ console.error(`Error downloading file ${outputPath} after ${maxRetries + 1} attempts:`, error);
82
+ return false;
83
+ }
84
+ retries++;
85
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
67
86
  }
68
- return false;
69
- }
70
- catch (error) {
71
- console.error('Error downloading file:', error);
72
- return false;
73
87
  }
88
+ return false;
74
89
  });
75
90
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Downloads multiple translation files in a single batch request
3
+ * @param baseUrl - The base URL for the General Translation API
4
+ * @param apiKey - The API key for the General Translation API
5
+ * @param files - Array of files to download with their output paths
6
+ * @param maxRetries - Maximum number of retry attempts
7
+ * @param retryDelay - Delay between retries in milliseconds
8
+ * @returns Object containing successful and failed file IDs
9
+ */
10
+ export declare function downloadFileBatch(baseUrl: string, apiKey: string, files: Array<{
11
+ translationId: string;
12
+ outputPath: string;
13
+ }>, maxRetries?: number, retryDelay?: number): Promise<{
14
+ successful: string[];
15
+ failed: string[];
16
+ }>;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.downloadFileBatch = downloadFileBatch;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ /**
49
+ * Downloads multiple translation files in a single batch request
50
+ * @param baseUrl - The base URL for the General Translation API
51
+ * @param apiKey - The API key for the General Translation API
52
+ * @param files - Array of files to download with their output paths
53
+ * @param maxRetries - Maximum number of retry attempts
54
+ * @param retryDelay - Delay between retries in milliseconds
55
+ * @returns Object containing successful and failed file IDs
56
+ */
57
+ function downloadFileBatch(baseUrl_1, apiKey_1, files_1) {
58
+ return __awaiter(this, arguments, void 0, function* (baseUrl, apiKey, files, maxRetries = 3, retryDelay = 1000) {
59
+ let retries = 0;
60
+ const fileIds = files.map((file) => file.translationId);
61
+ const result = { successful: [], failed: [] };
62
+ // Create a map of translationId to outputPath for easier lookup
63
+ const outputPathMap = new Map(files.map((file) => [file.translationId, file.outputPath]));
64
+ while (retries <= maxRetries) {
65
+ try {
66
+ const response = yield fetch(`${baseUrl}/v1/project/translations/files/batch-download`, {
67
+ method: 'POST',
68
+ headers: Object.assign({ 'Content-Type': 'application/json' }, (apiKey && { 'x-gt-api-key': apiKey })),
69
+ body: JSON.stringify({ fileIds }),
70
+ });
71
+ if (response.ok) {
72
+ const responseData = yield response.json();
73
+ const downloadedFiles = responseData.files || [];
74
+ // Process each file in the response
75
+ for (const file of downloadedFiles) {
76
+ try {
77
+ const translationId = file.id;
78
+ const outputPath = outputPathMap.get(translationId);
79
+ if (!outputPath) {
80
+ console.warn(`No output path found for file: ${translationId}`);
81
+ result.failed.push(translationId);
82
+ continue;
83
+ }
84
+ // Ensure the directory exists
85
+ const dir = path.dirname(outputPath);
86
+ if (!fs.existsSync(dir)) {
87
+ fs.mkdirSync(dir, { recursive: true });
88
+ }
89
+ // Write the file to disk
90
+ fs.writeFileSync(outputPath, file.data);
91
+ result.successful.push(translationId);
92
+ }
93
+ catch (error) {
94
+ console.error(`Error saving file ${file.id}:`, error);
95
+ result.failed.push(file.id);
96
+ }
97
+ }
98
+ // Add any files that weren't in the response to the failed list
99
+ const downloadedIds = new Set(downloadedFiles.map((file) => file.id));
100
+ for (const fileId of fileIds) {
101
+ if (!downloadedIds.has(fileId) && !result.failed.includes(fileId)) {
102
+ result.failed.push(fileId);
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ // If we get here, the response was not OK
108
+ if (retries >= maxRetries) {
109
+ console.error(`Failed to download files in batch. Status: ${response.status} after ${maxRetries + 1} attempts.`);
110
+ // Mark all files as failed
111
+ result.failed = [...fileIds];
112
+ return result;
113
+ }
114
+ // Increment retry counter and wait before next attempt
115
+ retries++;
116
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
117
+ }
118
+ catch (error) {
119
+ if (retries >= maxRetries) {
120
+ console.error(`Error downloading files in batch after ${maxRetries + 1} attempts:`, error);
121
+ // Mark all files as failed
122
+ result.failed = [...fileIds];
123
+ return result;
124
+ }
125
+ retries++;
126
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
127
+ }
128
+ }
129
+ // Mark all files as failed if we get here
130
+ result.failed = [...fileIds];
131
+ return result;
132
+ });
133
+ }
@@ -19,5 +19,6 @@ type ApiOptions = Settings & {
19
19
  export declare function sendFiles(files: FileToTranslate[], options: ApiOptions): Promise<{
20
20
  data: any;
21
21
  locales: any;
22
+ translations: any;
22
23
  }>;
23
24
  export {};
@@ -27,7 +27,7 @@ function sendFiles(files, options) {
27
27
  console.log(chalk_1.default.cyan('\nFiles to translate:'));
28
28
  console.log(files.map((file) => ` - ${chalk_1.default.bold(file.fileName)}`).join('\n'));
29
29
  console.log();
30
- const spinner = yield (0, console_1.displayLoadingAnimation)(`Sending ${files.length} file${files.length > 1 ? 's' : ''} to Translation API...`);
30
+ const spinner = yield (0, console_1.displayLoadingAnimation)(`Sending ${files.length} file${files.length > 1 ? 's' : ''} to General Translation API...`);
31
31
  try {
32
32
  // Create form data
33
33
  const formData = new FormData();
@@ -58,9 +58,9 @@ function sendFiles(files, options) {
58
58
  }
59
59
  const responseData = yield response.json();
60
60
  // Handle version ID response (for async processing)
61
- const { data, message, locales } = responseData;
61
+ const { data, message, locales, translations } = responseData;
62
62
  spinner.succeed(chalk_1.default.green(message || 'Translation job submitted successfully'));
63
- return { data, locales };
63
+ return { data, locales, translations };
64
64
  }
65
65
  catch (error) {
66
66
  spinner.fail(chalk_1.default.red('Failed to send files for translation'));
@@ -1,4 +1,4 @@
1
- import { Settings, Updates } from '../types';
1
+ import { Settings, SupportedLibraries, Updates } from '../types';
2
2
  import { DataFormat } from '../types/data';
3
3
  type ApiOptions = Settings & {
4
4
  publish: boolean;
@@ -12,7 +12,7 @@ type ApiOptions = Settings & {
12
12
  * @param options - The options for the API call
13
13
  * @returns The versionId of the updated project
14
14
  */
15
- export declare function sendUpdates(updates: Updates, options: ApiOptions): Promise<{
15
+ export declare function sendUpdates(updates: Updates, options: ApiOptions, library: SupportedLibraries): Promise<{
16
16
  versionId: any;
17
17
  } | undefined>;
18
18
  export {};
@@ -23,7 +23,7 @@ const waitForUpdates_1 = require("./waitForUpdates");
23
23
  * @param options - The options for the API call
24
24
  * @returns The versionId of the updated project
25
25
  */
26
- function sendUpdates(updates, options) {
26
+ function sendUpdates(updates, options, library) {
27
27
  return __awaiter(this, void 0, void 0, function* () {
28
28
  const { apiKey, projectId, defaultLocale, dataFormat } = options;
29
29
  const globalMetadata = Object.assign(Object.assign({}, (projectId && { projectId })), (defaultLocale && { sourceLocale: defaultLocale }));
@@ -31,7 +31,8 @@ function sendUpdates(updates, options) {
31
31
  // If not, then options.locales will be translated
32
32
  // If neither, then project.current_locales will be translated
33
33
  const body = Object.assign(Object.assign(Object.assign(Object.assign({ updates }, (options.locales && { locales: options.locales })), { metadata: globalMetadata, publish: options.publish }), (dataFormat && { dataFormat })), (options.versionId && { versionId: options.versionId }));
34
- const spinner = yield (0, console_1.displayLoadingAnimation)('Sending updates to General Translation API...');
34
+ console.log();
35
+ const spinner = yield (0, console_1.displayLoadingAnimation)(`Sending ${library} updates to General Translation API...`);
35
36
  try {
36
37
  const startTime = Date.now();
37
38
  const response = yield fetch(`${options.baseUrl}/v1/project/translations/update`, {
@@ -1,7 +1,7 @@
1
1
  import { Settings, SupportedLibraries } from '../types';
2
2
  export declare class BaseCLI {
3
- private library;
4
- private additionalModules;
3
+ protected library: SupportedLibraries;
4
+ protected additionalModules: SupportedLibraries[];
5
5
  constructor(library: SupportedLibraries, additionalModules?: SupportedLibraries[]);
6
6
  init(): void;
7
7
  execute(): void;
package/dist/cli/react.js CHANGED
@@ -52,7 +52,6 @@ const console_1 = require("../console/console");
52
52
  const loadJSON_1 = __importDefault(require("../fs/loadJSON"));
53
53
  const findFilepath_1 = __importStar(require("../fs/findFilepath"));
54
54
  const createESBuildConfig_1 = __importDefault(require("../react/config/createESBuildConfig"));
55
- const errors_1 = require("../console/errors");
56
55
  const internal_1 = require("generaltranslation/internal");
57
56
  const chalk_1 = __importDefault(require("chalk"));
58
57
  const prompts_1 = require("@inquirer/prompts");
@@ -439,7 +438,7 @@ class ReactCLI extends base_1.BaseCLI {
439
438
  // Error if no projectId at this point
440
439
  if (!settings.projectId)
441
440
  throw new Error('No General Translation Project ID found. Use the --project-id flag to provide one.');
442
- const updateResponse = yield (0, sendUpdates_1.sendUpdates)(updates, Object.assign(Object.assign({}, settings), { publish: options.publish, wait: options.wait, timeout: options.timeout, dataFormat: 'JSX' }));
441
+ const updateResponse = yield (0, sendUpdates_1.sendUpdates)(updates, Object.assign(Object.assign({}, settings), { publish: options.publish, wait: options.wait, timeout: options.timeout, dataFormat: 'JSX' }), this.library);
443
442
  const versionId = updateResponse === null || updateResponse === void 0 ? void 0 : updateResponse.versionId;
444
443
  // Save translations to local directory if files.gt.output is provided
445
444
  if (versionId && options.files.placeholderPaths.gt) {
@@ -449,7 +448,7 @@ class ReactCLI extends base_1.BaseCLI {
449
448
  }
450
449
  }
451
450
  else {
452
- console.log(chalk_1.default.red(errors_1.noTranslationsError));
451
+ console.log(chalk_1.default.red(`No in-line content or dictionaries were found for ${chalk_1.default.green(this.library)}. Are you sure you're running this command in the right directory?`));
453
452
  process.exit(0);
454
453
  }
455
454
  });
@@ -1,4 +1,3 @@
1
- export declare const noTranslationsError = "No in-line content or dictionaries were found. Are you sure you're running this command in the right directory?";
2
1
  export declare const noLocalesError = "No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.";
3
2
  export declare const noDefaultLocaleError = "No default locale found! Please provide a default locale, or specify it in your gt.config.json file.";
4
3
  export declare const noFilesError = "No files configuration found! Please make sure your files are configured correctly in your gt.config.json file.";
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noProjectIdError = exports.noApiKeyError = exports.noSupportedDataFormatError = exports.noDataFormatError = exports.noSourceFileError = exports.noFilesError = exports.noDefaultLocaleError = exports.noLocalesError = exports.noTranslationsError = void 0;
4
- exports.noTranslationsError = `No in-line content or dictionaries were found. Are you sure you're running this command in the right directory?`;
3
+ exports.noProjectIdError = exports.noApiKeyError = exports.noSupportedDataFormatError = exports.noDataFormatError = exports.noSourceFileError = exports.noFilesError = exports.noDefaultLocaleError = exports.noLocalesError = void 0;
5
4
  exports.noLocalesError = `No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.`;
6
5
  exports.noDefaultLocaleError = `No default locale found! Please provide a default locale, or specify it in your gt.config.json file.`;
7
6
  exports.noFilesError = `No files configuration found! Please make sure your files are configured correctly in your gt.config.json file.`;
@@ -20,6 +20,9 @@ const parseFilesConfig_1 = require("../../fs/config/parseFilesConfig");
20
20
  const findFilepath_1 = require("../../fs/findFilepath");
21
21
  const flattenDictionary_1 = require("../../react/utils/flattenDictionary");
22
22
  const path_1 = __importDefault(require("path"));
23
+ const downloadFile_1 = require("../../api/downloadFile");
24
+ const downloadFileBatch_1 = require("../../api/downloadFileBatch");
25
+ const console_1 = require("../../console/console");
23
26
  const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
24
27
  /**
25
28
  * Sends multiple files to the API for translation
@@ -91,46 +94,123 @@ function translateFiles(filePaths_1, placeholderPaths_1, transformPaths_1) {
91
94
  try {
92
95
  // Send all files in a single API call
93
96
  const response = yield (0, sendFiles_1.sendFiles)(allFiles, Object.assign(Object.assign({}, options), { publish: false, wait: true }));
94
- const { data, locales } = response;
97
+ const { data, locales, translations } = response;
95
98
  // Create file mapping for all file types
96
- const fileMapping = {};
97
- for (const locale of locales) {
98
- const translatedPaths = (0, parseFilesConfig_1.resolveLocaleFiles)(placeholderPaths, locale);
99
- const localeMapping = {};
100
- // Process each file type
101
- for (const typeIndex of ['json', 'mdx', 'md']) {
102
- if (!filePaths[typeIndex] || !translatedPaths[typeIndex])
103
- continue;
104
- const sourcePaths = filePaths[typeIndex];
105
- let translatedFiles = translatedPaths[typeIndex];
106
- if (!translatedFiles)
107
- continue;
108
- const transformPath = transformPaths[typeIndex];
109
- if (transformPath) {
110
- translatedFiles = translatedFiles.map((filePath) => {
111
- const directory = path_1.default.dirname(filePath);
112
- const fileName = path_1.default.basename(filePath);
113
- const baseName = fileName.split('.')[0];
114
- const transformedFileName = transformPath
115
- .replace('*', baseName)
116
- .replace('[locale]', locale);
117
- return path_1.default.join(directory, transformedFileName);
118
- });
99
+ const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales);
100
+ // Process any translations that were already completed and returned with the initial response
101
+ const downloadStatus = yield processInitialTranslations(translations, fileMapping, options);
102
+ // Check for remaining translations
103
+ yield (0, checkFileTranslations_1.checkFileTranslations)(options.apiKey, options.baseUrl, data, locales, 600, (sourcePath, locale) => fileMapping[locale][sourcePath], downloadStatus // Pass the already downloaded files to avoid duplicate requests
104
+ );
105
+ }
106
+ catch (error) {
107
+ console.error('Error translating files:', error);
108
+ }
109
+ });
110
+ }
111
+ /**
112
+ * Creates a mapping between source files and their translated counterparts for each locale
113
+ */
114
+ function createFileMapping(filePaths, placeholderPaths, transformPaths, locales) {
115
+ const fileMapping = {};
116
+ for (const locale of locales) {
117
+ const translatedPaths = (0, parseFilesConfig_1.resolveLocaleFiles)(placeholderPaths, locale);
118
+ const localeMapping = {};
119
+ // Process each file type
120
+ for (const typeIndex of ['json', 'mdx', 'md']) {
121
+ if (!filePaths[typeIndex] || !translatedPaths[typeIndex])
122
+ continue;
123
+ const sourcePaths = filePaths[typeIndex];
124
+ let translatedFiles = translatedPaths[typeIndex];
125
+ if (!translatedFiles)
126
+ continue;
127
+ const transformPath = transformPaths[typeIndex];
128
+ if (transformPath) {
129
+ translatedFiles = translatedFiles.map((filePath) => {
130
+ const directory = path_1.default.dirname(filePath);
131
+ const fileName = path_1.default.basename(filePath);
132
+ const baseName = fileName.split('.')[0];
133
+ const transformedFileName = transformPath
134
+ .replace('*', baseName)
135
+ .replace('[locale]', locale);
136
+ return path_1.default.join(directory, transformedFileName);
137
+ });
138
+ }
139
+ for (let i = 0; i < sourcePaths.length; i++) {
140
+ const sourceFile = (0, findFilepath_1.getRelative)(sourcePaths[i]);
141
+ const translatedFile = (0, findFilepath_1.getRelative)(translatedFiles[i]);
142
+ localeMapping[sourceFile] = translatedFile;
143
+ }
144
+ }
145
+ fileMapping[locale] = localeMapping;
146
+ }
147
+ return fileMapping;
148
+ }
149
+ /**
150
+ * Processes translations that were already completed and returned with the initial API response
151
+ * @returns Set of downloaded file+locale combinations
152
+ */
153
+ function processInitialTranslations() {
154
+ return __awaiter(this, arguments, void 0, function* (translations = [], fileMapping, options) {
155
+ const downloadStatus = {
156
+ downloaded: new Set(),
157
+ failed: new Set(),
158
+ };
159
+ if (!translations || translations.length === 0) {
160
+ return downloadStatus;
161
+ }
162
+ // Filter for ready translations
163
+ const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
164
+ if (readyTranslations.length > 0) {
165
+ const spinner = yield (0, console_1.displayLoadingAnimation)('Downloading translations...');
166
+ // Prepare batch download data
167
+ const batchFiles = readyTranslations
168
+ .map((translation) => {
169
+ const { locale, fileName, id } = translation;
170
+ const outputPath = fileMapping[locale][fileName];
171
+ if (!outputPath) {
172
+ return null;
173
+ }
174
+ return {
175
+ translationId: id,
176
+ outputPath,
177
+ fileLocale: `${fileName}:${locale}`,
178
+ };
179
+ })
180
+ .filter(Boolean);
181
+ if (batchFiles.length === 0 || batchFiles[0] === null) {
182
+ return downloadStatus;
183
+ }
184
+ // Use batch download if there are multiple files
185
+ if (batchFiles.length > 1) {
186
+ const batchResult = yield (0, downloadFileBatch_1.downloadFileBatch)(options.baseUrl, options.apiKey, batchFiles.map(({ translationId, outputPath }) => ({
187
+ translationId,
188
+ outputPath,
189
+ })));
190
+ // Process results
191
+ batchFiles.forEach((file) => {
192
+ const { translationId, fileLocale } = file;
193
+ if (batchResult.successful.includes(translationId)) {
194
+ downloadStatus.downloaded.add(fileLocale);
119
195
  }
120
- for (let i = 0; i < sourcePaths.length; i++) {
121
- const sourceFile = (0, findFilepath_1.getRelative)(sourcePaths[i]);
122
- const translatedFile = (0, findFilepath_1.getRelative)(translatedFiles[i]);
123
- localeMapping[sourceFile] = translatedFile;
196
+ else if (batchResult.failed.includes(translationId)) {
197
+ downloadStatus.failed.add(fileLocale);
124
198
  }
199
+ });
200
+ }
201
+ else if (batchFiles.length === 1) {
202
+ // For a single file, use the original downloadFile method
203
+ const file = batchFiles[0];
204
+ const result = yield (0, downloadFile_1.downloadFile)(options.baseUrl, options.apiKey, file.translationId, file.outputPath);
205
+ if (result) {
206
+ downloadStatus.downloaded.add(file.fileLocale);
207
+ }
208
+ else {
209
+ downloadStatus.failed.add(file.fileLocale);
125
210
  }
126
- fileMapping[locale] = localeMapping;
127
211
  }
128
- yield (0, checkFileTranslations_1.checkFileTranslations)(options.apiKey, options.baseUrl, data, locales, 600, (sourcePath, locale) => {
129
- return fileMapping[locale][sourcePath];
130
- });
131
- }
132
- catch (error) {
133
- console.error('Error translating files:', error);
212
+ spinner.succeed('Downloaded cached translations');
134
213
  }
214
+ return downloadStatus;
135
215
  });
136
216
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "1.1.7-alpha.1",
3
+ "version": "1.1.7-alpha.2",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "scripts": {
@@ -1,11 +0,0 @@
1
- import { RetrievedTranslations } from '../../types/api';
2
- import { DataFormat } from '../../types/data';
3
- import { ResolvedFiles } from '../../types';
4
- /**
5
- * Saves translations to a file
6
- * @param translations - The translations to save
7
- * @param filePath - The file path to save the translations to
8
- * @param dataFormat - The data format to save the translations as
9
- * @deprecated Use saveFiles instead
10
- */
11
- export declare function saveTranslations(translations: RetrievedTranslations, placeholderPaths: ResolvedFiles, dataFormat: DataFormat): void;
@@ -1,67 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.saveTranslations = saveTranslations;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const parseFilesConfig_1 = require("../../fs/config/parseFilesConfig");
10
- const errors_1 = require("../../console/errors");
11
- /**
12
- * Saves translations to a file
13
- * @param translations - The translations to save
14
- * @param filePath - The file path to save the translations to
15
- * @param dataFormat - The data format to save the translations as
16
- * @deprecated Use saveFiles instead
17
- */
18
- function saveTranslations(translations, placeholderPaths, dataFormat) {
19
- for (const translation of translations) {
20
- const locale = translation.locale;
21
- const translationFiles = (0, parseFilesConfig_1.resolveLocaleFiles)(placeholderPaths, locale);
22
- if (!translationFiles.json) {
23
- console.error(errors_1.noFilesError);
24
- process.exit(1);
25
- }
26
- const translationData = translation.translation;
27
- const translationMetadata = translation.metadata;
28
- // Ensure directory exists
29
- fs_1.default.mkdirSync(path_1.default.dirname(translationFiles.json[0]), { recursive: true });
30
- // Handle different file types
31
- let writeData;
32
- if (dataFormat === 'ICU' ||
33
- dataFormat === 'I18NEXT' ||
34
- dataFormat === 'JSX') {
35
- // JSONs need to be mapped back to the original format
36
- const revertedJson = {};
37
- for (const hash in translationData) {
38
- const metadata = translationMetadata[hash];
39
- const entry = translationData[hash];
40
- if (metadata.id) {
41
- const keyPath = metadata.id.split('.');
42
- let current = revertedJson;
43
- // Process all keys except the last one
44
- for (let i = 0; i < keyPath.length - 1; i++) {
45
- const key = keyPath[i];
46
- // Make sure the current key points to an object
47
- if (!current[key] ||
48
- typeof current[key] !== 'object' ||
49
- Array.isArray(current[key])) {
50
- current[key] = {};
51
- }
52
- current = current[key];
53
- }
54
- // Set the value at the last key
55
- current[keyPath[keyPath.length - 1]] = entry;
56
- }
57
- }
58
- writeData = JSON.stringify(revertedJson, null, 2);
59
- }
60
- // else if (dataFormat === 'yaml') {
61
- // writeData = yaml.stringify(translationData);
62
- // }
63
- if (writeData) {
64
- fs_1.default.writeFileSync(translationFiles.json[0], writeData);
65
- }
66
- }
67
- }
@@ -1,17 +0,0 @@
1
- import { Settings } from '../../types';
2
- import { DataFormat } from '../../types/data';
3
- import { ResolvedFiles } from '../../types';
4
- /**
5
- * Translates a JSON object and saves the translations to a local directory
6
- * @param sourceJson - The source JSON object
7
- * @param defaultLocale - The default locale
8
- * @param locales - The locales to translate to
9
- * @param library - The library to use
10
- * @param apiKey - The API key for the General Translation API
11
- * @param projectId - The project ID
12
- * @param config - The config file path
13
- * @param translationsDir - The directory to save the translations to
14
- * @param fileType - The file type to save the translations as (file extension)
15
- * @deprecated Use translateFiles instead
16
- */
17
- export declare function translateJson(sourceJson: any, settings: Settings, dataFormat: DataFormat, placeholderPaths: ResolvedFiles): Promise<void>;
@@ -1,52 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.translateJson = translateJson;
13
- const flattenDictionary_1 = require("../../react/utils/flattenDictionary");
14
- const sendUpdates_1 = require("../../api/sendUpdates");
15
- const fetchTranslations_1 = require("../../api/fetchTranslations");
16
- const save_1 = require("./save");
17
- /**
18
- * Translates a JSON object and saves the translations to a local directory
19
- * @param sourceJson - The source JSON object
20
- * @param defaultLocale - The default locale
21
- * @param locales - The locales to translate to
22
- * @param library - The library to use
23
- * @param apiKey - The API key for the General Translation API
24
- * @param projectId - The project ID
25
- * @param config - The config file path
26
- * @param translationsDir - The directory to save the translations to
27
- * @param fileType - The file type to save the translations as (file extension)
28
- * @deprecated Use translateFiles instead
29
- */
30
- function translateJson(sourceJson, settings, dataFormat, placeholderPaths) {
31
- return __awaiter(this, void 0, void 0, function* () {
32
- const flattened = (0, flattenDictionary_1.flattenJsonDictionary)(sourceJson);
33
- const updates = [];
34
- for (const id of Object.keys(flattened)) {
35
- const source = flattened[id];
36
- const metadata = {
37
- id,
38
- };
39
- updates.push({
40
- dataFormat,
41
- source,
42
- metadata,
43
- });
44
- }
45
- // Actually do the translation
46
- const updateResponse = yield (0, sendUpdates_1.sendUpdates)(updates, Object.assign(Object.assign({}, settings), { publish: false, wait: true, timeout: '600', dataFormat }));
47
- if (updateResponse === null || updateResponse === void 0 ? void 0 : updateResponse.versionId) {
48
- const translations = yield (0, fetchTranslations_1.fetchTranslations)(settings.baseUrl, settings.apiKey, updateResponse.versionId);
49
- (0, save_1.saveTranslations)(translations, placeholderPaths, dataFormat);
50
- }
51
- });
52
- }