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.
- package/dist/api/checkFileTranslations.d.ts +4 -1
- package/dist/api/checkFileTranslations.js +194 -135
- package/dist/api/downloadFile.d.ts +1 -1
- package/dist/api/downloadFile.js +37 -22
- package/dist/api/downloadFileBatch.d.ts +16 -0
- package/dist/api/downloadFileBatch.js +133 -0
- package/dist/api/sendFiles.d.ts +1 -0
- package/dist/api/sendFiles.js +3 -3
- package/dist/api/sendUpdates.d.ts +2 -2
- package/dist/api/sendUpdates.js +3 -2
- package/dist/cli/base.d.ts +2 -2
- package/dist/cli/react.js +2 -3
- package/dist/console/errors.d.ts +0 -1
- package/dist/console/errors.js +1 -2
- package/dist/formats/files/translate.js +115 -35
- package/package.json +1 -1
- package/dist/formats/json/save.d.ts +0 -11
- package/dist/formats/json/save.js +0 -67
- package/dist/formats/json/translate.d.ts +0 -17
- package/dist/formats/json/translate.js +0 -52
|
@@ -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
|
|
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
|
-
|
|
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
|
|
38
|
+
const initialCheck = yield checkTranslationDeployment(baseUrl, apiKey, fileQueryData, downloadStatus, spinner, resolveOutputPath);
|
|
168
39
|
if (initialCheck) {
|
|
169
|
-
spinner.succeed(chalk_1.default.green('
|
|
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
|
|
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>;
|
package/dist/api/downloadFile.js
CHANGED
|
@@ -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(
|
|
50
|
-
return __awaiter(this,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
+
}
|
package/dist/api/sendFiles.d.ts
CHANGED
package/dist/api/sendFiles.js
CHANGED
|
@@ -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 {};
|
package/dist/api/sendUpdates.js
CHANGED
|
@@ -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
|
-
|
|
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`, {
|
package/dist/cli/base.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Settings, SupportedLibraries } from '../types';
|
|
2
2
|
export declare class BaseCLI {
|
|
3
|
-
|
|
4
|
-
|
|
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(
|
|
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
|
});
|
package/dist/console/errors.d.ts
CHANGED
|
@@ -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.";
|
package/dist/console/errors.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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,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
|
-
}
|