gtx-cli 2.1.5 → 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/api/checkFileTranslations.d.ts +1 -4
- package/dist/api/checkFileTranslations.js +32 -35
- package/dist/api/downloadFileBatch.d.ts +1 -0
- package/dist/api/sendFiles.d.ts +2 -13
- package/dist/api/sendFiles.js +15 -8
- package/dist/cli/base.d.ts +5 -17
- package/dist/cli/base.js +48 -68
- package/dist/cli/commands/stage.d.ts +5 -0
- package/dist/cli/commands/stage.js +100 -0
- package/dist/cli/commands/translate.d.ts +6 -0
- package/dist/cli/commands/translate.js +53 -0
- package/dist/cli/flags.d.ts +3 -0
- package/dist/cli/flags.js +37 -0
- package/dist/cli/next.js +0 -1
- package/dist/cli/react.d.ts +2 -5
- package/dist/cli/react.js +13 -153
- package/dist/config/generateSettings.js +11 -6
- package/dist/console/index.d.ts +1 -0
- package/dist/console/index.js +1 -0
- package/dist/console/logging.d.ts +1 -0
- package/dist/console/logging.js +3 -0
- package/dist/formats/files/fileMapping.d.ts +2 -1
- package/dist/formats/files/fileMapping.js +6 -0
- package/dist/formats/files/translate.d.ts +4 -13
- package/dist/formats/files/translate.js +30 -142
- package/dist/formats/files/upload.js +1 -75
- package/dist/formats/gt/save.d.ts +1 -2
- package/dist/formats/gt/save.js +2 -5
- package/dist/fs/config/setupConfig.d.ts +1 -0
- package/dist/fs/config/setupConfig.js +1 -0
- package/dist/fs/config/updateConfig.d.ts +1 -2
- package/dist/fs/config/updateConfig.js +1 -1
- package/dist/fs/config/updateVersions.d.ts +10 -0
- package/dist/fs/config/updateVersions.js +30 -0
- package/dist/fs/copyFile.d.ts +1 -2
- package/dist/translation/parse.d.ts +2 -2
- package/dist/translation/parse.js +4 -6
- package/dist/translation/stage.d.ts +2 -5
- package/dist/translation/stage.js +13 -55
- package/dist/types/files.d.ts +1 -0
- package/dist/types/files.js +1 -0
- package/dist/types/index.d.ts +28 -3
- package/dist/utils/flattenJsonFiles.d.ts +2 -2
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/hash.js +11 -0
- package/dist/utils/localizeStaticImports.d.ts +2 -2
- package/dist/utils/localizeStaticUrls.d.ts +6 -2
- package/dist/utils/localizeStaticUrls.js +132 -103
- package/package.json +2 -2
- package/dist/api/downloadFile.d.ts +0 -9
- package/dist/api/downloadFile.js +0 -74
- package/dist/api/fetchTranslations.d.ts +0 -7
- package/dist/api/fetchTranslations.js +0 -18
- package/dist/api/sendUpdates.d.ts +0 -19
- package/dist/api/sendUpdates.js +0 -48
- package/dist/api/waitForUpdates.d.ts +0 -9
- package/dist/api/waitForUpdates.js +0 -89
- package/dist/translation/translate.d.ts +0 -2
- package/dist/translation/translate.js +0 -18
|
@@ -21,19 +21,24 @@ const { isMatch } = micromatch;
|
|
|
21
21
|
* - Support more file types
|
|
22
22
|
* - Support more complex paths
|
|
23
23
|
*/
|
|
24
|
-
export default async function localizeStaticUrls(settings) {
|
|
24
|
+
export default async function localizeStaticUrls(settings, targetLocales) {
|
|
25
25
|
if (!settings.files ||
|
|
26
26
|
(Object.keys(settings.files.placeholderPaths).length === 1 &&
|
|
27
27
|
settings.files.placeholderPaths.gt)) {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
const { resolvedPaths: sourceFiles } = settings.files;
|
|
31
|
-
|
|
31
|
+
// Use filtered locales if provided, otherwise use all locales
|
|
32
|
+
const locales = targetLocales || settings.locales;
|
|
33
|
+
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, // Always use all locales for mapping, filter later
|
|
34
|
+
settings.defaultLocale);
|
|
32
35
|
// Process all file types at once with a single call
|
|
33
36
|
const processPromises = [];
|
|
34
37
|
// First, process default locale files (from source files)
|
|
35
38
|
// This is needed because they might not be in the fileMapping if they're not being translated
|
|
36
|
-
if
|
|
39
|
+
// Only process default locale if it's in the target locales filter
|
|
40
|
+
if (!fileMapping[settings.defaultLocale] &&
|
|
41
|
+
locales.includes(settings.defaultLocale)) {
|
|
37
42
|
const defaultLocaleFiles = [];
|
|
38
43
|
// Collect all .md and .mdx files from sourceFiles
|
|
39
44
|
if (sourceFiles.md) {
|
|
@@ -47,16 +52,20 @@ export default async function localizeStaticUrls(settings) {
|
|
|
47
52
|
// Get file content
|
|
48
53
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
49
54
|
// Localize the file using default locale
|
|
50
|
-
const
|
|
51
|
-
settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls);
|
|
52
|
-
//
|
|
53
|
-
|
|
55
|
+
const result = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, settings.defaultLocale, // Process as default locale
|
|
56
|
+
settings.options?.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
|
|
57
|
+
// Only write the file if there were changes
|
|
58
|
+
if (result.hasChanges) {
|
|
59
|
+
await fs.promises.writeFile(filePath, result.content);
|
|
60
|
+
}
|
|
54
61
|
}));
|
|
55
62
|
processPromises.push(defaultPromise);
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
// Then process all other locales from fileMapping
|
|
59
|
-
const mappingPromises = Object.entries(fileMapping)
|
|
66
|
+
const mappingPromises = Object.entries(fileMapping)
|
|
67
|
+
.filter(([locale, filesMap]) => locales.includes(locale)) // Filter by target locales
|
|
68
|
+
.map(async ([locale, filesMap]) => {
|
|
60
69
|
// Get all files that are md or mdx
|
|
61
70
|
const targetFiles = Object.values(filesMap).filter((path) => path.endsWith('.md') || path.endsWith('.mdx'));
|
|
62
71
|
// Replace the placeholder path with the target path
|
|
@@ -64,9 +73,11 @@ export default async function localizeStaticUrls(settings) {
|
|
|
64
73
|
// Get file content
|
|
65
74
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
66
75
|
// Localize the file (handles both URLs and hrefs in single AST pass)
|
|
67
|
-
const
|
|
68
|
-
//
|
|
69
|
-
|
|
76
|
+
const result = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.options?.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
|
|
77
|
+
// Only write the file if there were changes
|
|
78
|
+
if (result.hasChanges) {
|
|
79
|
+
await fs.promises.writeFile(filePath, result.content);
|
|
80
|
+
}
|
|
70
81
|
}));
|
|
71
82
|
});
|
|
72
83
|
processPromises.push(...mappingPromises);
|
|
@@ -75,21 +86,28 @@ export default async function localizeStaticUrls(settings) {
|
|
|
75
86
|
/**
|
|
76
87
|
* Determines if a URL should be processed based on pattern matching
|
|
77
88
|
*/
|
|
78
|
-
function shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale) {
|
|
79
|
-
// Skip absolute URLs (http://, https://, //, etc.)
|
|
80
|
-
if (originalUrl.includes(':')) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
89
|
+
function shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain) {
|
|
83
90
|
const patternWithoutSlash = patternHead.replace(/\/$/, '');
|
|
91
|
+
// Handle absolute URLs with baseDomain
|
|
92
|
+
let urlToCheck = originalUrl;
|
|
93
|
+
if (baseDomain && originalUrl.startsWith(baseDomain)) {
|
|
94
|
+
urlToCheck = originalUrl.substring(baseDomain.length);
|
|
95
|
+
}
|
|
84
96
|
if (targetLocale === defaultLocale) {
|
|
85
97
|
// For default locale processing, check if URL contains the pattern
|
|
86
|
-
return
|
|
98
|
+
return urlToCheck.includes(patternWithoutSlash);
|
|
87
99
|
}
|
|
88
100
|
else {
|
|
89
101
|
// For non-default locales, check if URL starts with pattern
|
|
90
|
-
return
|
|
102
|
+
return urlToCheck.startsWith(patternWithoutSlash);
|
|
91
103
|
}
|
|
92
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Determines if a URL should be processed based on the base domain
|
|
107
|
+
*/
|
|
108
|
+
function shouldProcessAbsoluteUrl(originalUrl, baseDomain) {
|
|
109
|
+
return originalUrl.startsWith(baseDomain);
|
|
110
|
+
}
|
|
93
111
|
/**
|
|
94
112
|
* Checks if a URL should be excluded based on exclusion patterns
|
|
95
113
|
*/
|
|
@@ -98,102 +116,79 @@ function isUrlExcluded(originalUrl, exclude, defaultLocale) {
|
|
|
98
116
|
return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));
|
|
99
117
|
}
|
|
100
118
|
/**
|
|
101
|
-
*
|
|
102
|
-
*/
|
|
103
|
-
function transformDefaultLocaleUrl(originalUrl, patternHead, defaultLocale, hideDefaultLocale) {
|
|
104
|
-
if (hideDefaultLocale) {
|
|
105
|
-
// Remove locale from URLs that have it: '/docs/en/file' -> '/docs/file'
|
|
106
|
-
if (originalUrl.includes(`/${defaultLocale}/`)) {
|
|
107
|
-
return originalUrl.replace(`/${defaultLocale}/`, '/');
|
|
108
|
-
}
|
|
109
|
-
else if (originalUrl.endsWith(`/${defaultLocale}`)) {
|
|
110
|
-
return originalUrl.replace(`/${defaultLocale}`, '');
|
|
111
|
-
}
|
|
112
|
-
return null; // URL doesn't have default locale
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
// Add locale to URLs that don't have it: '/docs/file' -> '/docs/en/file'
|
|
116
|
-
if (originalUrl.includes(`/${defaultLocale}/`) ||
|
|
117
|
-
originalUrl.endsWith(`/${defaultLocale}`)) {
|
|
118
|
-
return null; // Already has default locale
|
|
119
|
-
}
|
|
120
|
-
if (originalUrl.startsWith(patternHead)) {
|
|
121
|
-
const pathAfterHead = originalUrl.slice(patternHead.length);
|
|
122
|
-
if (pathAfterHead) {
|
|
123
|
-
return `${patternHead}${defaultLocale}/${pathAfterHead}`;
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
return `${patternHead.replace(/\/$/, '')}/${defaultLocale}`;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return null; // URL doesn't match pattern
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Transforms URL for non-default locale processing with hideDefaultLocale=true
|
|
119
|
+
* Main URL transformation function that delegates to specific scenarios
|
|
134
120
|
*/
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
121
|
+
export function transformUrlPath(originalUrl, patternHead, targetLocale, defaultLocale, hideDefaultLocale) {
|
|
122
|
+
const originalPathArray = originalUrl
|
|
123
|
+
.split('/')
|
|
124
|
+
.filter((path) => path !== '');
|
|
125
|
+
const patternHeadArray = patternHead.split('/').filter((path) => path !== '');
|
|
126
|
+
// check if the pattern head matches the original path
|
|
127
|
+
if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {
|
|
139
128
|
return null;
|
|
140
129
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (originalUrl.startsWith(`${expectedPathWithDefaultLocale}/`) ||
|
|
144
|
-
originalUrl === expectedPathWithDefaultLocale) {
|
|
145
|
-
return originalUrl.replace(`${patternHead}${defaultLocale}`, `${patternHead}${targetLocale}`);
|
|
146
|
-
}
|
|
147
|
-
// Handle exact pattern match
|
|
148
|
-
if (originalUrl === patternHead.replace(/\/$/, '')) {
|
|
149
|
-
return `${patternHead.replace(/\/$/, '')}/${targetLocale}`;
|
|
130
|
+
if (patternHeadArray.length > originalPathArray.length) {
|
|
131
|
+
return null; // Pattern is longer than the URL path
|
|
150
132
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// Replace existing default locale with target locale
|
|
165
|
-
return originalUrl.replace(`${patternHead}${defaultLocale}`, `${patternHead}${targetLocale}`);
|
|
166
|
-
}
|
|
167
|
-
else if (originalUrl.startsWith(patternHead)) {
|
|
168
|
-
// Add target locale to URL that doesn't have any locale
|
|
169
|
-
const pathAfterHead = originalUrl.slice(patternHead.length);
|
|
170
|
-
if (pathAfterHead) {
|
|
171
|
-
return `${patternHead}${targetLocale}/${pathAfterHead}`;
|
|
133
|
+
let result = null;
|
|
134
|
+
if (targetLocale === defaultLocale) {
|
|
135
|
+
if (hideDefaultLocale) {
|
|
136
|
+
// check if default locale is already present
|
|
137
|
+
if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
// remove default locale
|
|
141
|
+
const newPathArray = [
|
|
142
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
143
|
+
...originalPathArray.slice(patternHeadArray.length + 1),
|
|
144
|
+
];
|
|
145
|
+
result = newPathArray.join('/');
|
|
172
146
|
}
|
|
173
147
|
else {
|
|
174
|
-
|
|
148
|
+
// check if default locale is already present
|
|
149
|
+
if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
// insert default locale
|
|
153
|
+
const newPathArray = [
|
|
154
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
155
|
+
defaultLocale,
|
|
156
|
+
...originalPathArray.slice(patternHeadArray.length),
|
|
157
|
+
];
|
|
158
|
+
result = newPathArray.join('/');
|
|
175
159
|
}
|
|
176
160
|
}
|
|
177
|
-
return null; // URL doesn't match pattern
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Main URL transformation function that delegates to specific scenarios
|
|
181
|
-
*/
|
|
182
|
-
function transformUrlPath(originalUrl, patternHead, targetLocale, defaultLocale, hideDefaultLocale) {
|
|
183
|
-
if (targetLocale === defaultLocale) {
|
|
184
|
-
return transformDefaultLocaleUrl(originalUrl, patternHead, defaultLocale, hideDefaultLocale);
|
|
185
|
-
}
|
|
186
161
|
else if (hideDefaultLocale) {
|
|
187
|
-
|
|
162
|
+
const newPathArray = [
|
|
163
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
164
|
+
targetLocale,
|
|
165
|
+
...originalPathArray.slice(patternHeadArray.length),
|
|
166
|
+
];
|
|
167
|
+
result = newPathArray.join('/');
|
|
188
168
|
}
|
|
189
169
|
else {
|
|
190
|
-
|
|
170
|
+
// check default locale
|
|
171
|
+
if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
// replace default locale with target locale
|
|
175
|
+
const newPathArray = [...originalPathArray];
|
|
176
|
+
newPathArray[patternHeadArray.length] = targetLocale;
|
|
177
|
+
result = newPathArray.join('/');
|
|
191
178
|
}
|
|
179
|
+
// check for leading and trailing slashes
|
|
180
|
+
if (originalUrl.startsWith('/')) {
|
|
181
|
+
result = '/' + result;
|
|
182
|
+
}
|
|
183
|
+
if (originalUrl.endsWith('/')) {
|
|
184
|
+
result = result + '/';
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
192
187
|
}
|
|
193
188
|
/**
|
|
194
189
|
* AST-based transformation for MDX files using remark-mdx
|
|
195
190
|
*/
|
|
196
|
-
function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', exclude = []) {
|
|
191
|
+
function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', exclude = [], baseDomain) {
|
|
197
192
|
const transformedUrls = [];
|
|
198
193
|
if (!pattern.startsWith('/')) {
|
|
199
194
|
pattern = '/' + pattern;
|
|
@@ -240,13 +235,32 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
240
235
|
return {
|
|
241
236
|
content: mdxContent,
|
|
242
237
|
hasChanges: false,
|
|
243
|
-
transformedUrls
|
|
238
|
+
transformedUrls,
|
|
244
239
|
};
|
|
245
240
|
}
|
|
246
241
|
// Helper function to transform URL based on pattern
|
|
247
242
|
const transformUrl = (originalUrl, linkType) => {
|
|
248
243
|
// Check if URL should be processed
|
|
249
|
-
if (!shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale)) {
|
|
244
|
+
if (!shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain)) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
// Skip absolute URLs (http://, https://, //, etc.)
|
|
248
|
+
if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {
|
|
249
|
+
// Get everything after the base domain
|
|
250
|
+
const afterDomain = originalUrl.substring(baseDomain.length);
|
|
251
|
+
const transformedPath = transformUrlPath(afterDomain, patternHead, targetLocale, defaultLocale, hideDefaultLocale);
|
|
252
|
+
if (!transformedPath) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
transformedUrls.push({
|
|
256
|
+
originalPath: originalUrl,
|
|
257
|
+
newPath: transformedPath,
|
|
258
|
+
type: linkType,
|
|
259
|
+
});
|
|
260
|
+
return transformedPath ? baseDomain + transformedPath : null;
|
|
261
|
+
}
|
|
262
|
+
// Exclude colon-prefixed URLs (http://, https://, //, etc.)
|
|
263
|
+
if (originalUrl.split('?')[0].includes(':')) {
|
|
250
264
|
return null;
|
|
251
265
|
}
|
|
252
266
|
// Transform the URL based on locale and configuration
|
|
@@ -381,8 +395,23 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
381
395
|
}
|
|
382
396
|
// AST-based transformation for MDX files using remark
|
|
383
397
|
function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
|
|
384
|
-
exclude = []) {
|
|
398
|
+
exclude = [], baseDomain) {
|
|
385
399
|
// Use AST-based transformation for MDX files
|
|
386
|
-
|
|
387
|
-
|
|
400
|
+
return transformMdxUrls(file, defaultLocale, targetLocale, hideDefaultLocale, pattern, exclude, baseDomain || '');
|
|
401
|
+
}
|
|
402
|
+
function cleanPath(path) {
|
|
403
|
+
let cleanedPath = path.startsWith('/') ? path.slice(1) : path;
|
|
404
|
+
cleanedPath = cleanedPath.endsWith('/')
|
|
405
|
+
? cleanedPath.slice(0, -1)
|
|
406
|
+
: cleanedPath;
|
|
407
|
+
return cleanedPath;
|
|
408
|
+
}
|
|
409
|
+
function checkIfPathMatchesPattern(originalUrlArray, patternHeadArray) {
|
|
410
|
+
// check if the pattern head matches the original path
|
|
411
|
+
for (let i = 0; i < patternHeadArray.length; i++) {
|
|
412
|
+
if (patternHeadArray[i] !== originalUrlArray?.[i]) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
388
417
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "dist/main.js",
|
|
6
6
|
"files": [
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"esbuild": "^0.25.4",
|
|
88
88
|
"fast-glob": "^3.3.3",
|
|
89
89
|
"form-data": "^4.0.4",
|
|
90
|
-
"generaltranslation": "^7.4.
|
|
90
|
+
"generaltranslation": "^7.4.1",
|
|
91
91
|
"json-pointer": "^0.6.2",
|
|
92
92
|
"jsonpath-plus": "^10.3.0",
|
|
93
93
|
"jsonpointer": "^5.0.1",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Settings } from '../types/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Downloads a file from the API and saves it to a local directory
|
|
4
|
-
* @param translationId - The ID of the translation to download
|
|
5
|
-
* @param outputPath - The path to save the file to
|
|
6
|
-
* @param maxRetries - The maximum number of retries to attempt
|
|
7
|
-
* @param retryDelay - The delay between retries in milliseconds
|
|
8
|
-
*/
|
|
9
|
-
export declare function downloadFile(translationId: string, outputPath: string, inputPath: string, locale: string, options: Settings, maxRetries?: number, retryDelay?: number): Promise<boolean>;
|
package/dist/api/downloadFile.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { logError } from '../console/logging.js';
|
|
4
|
-
import { gt } from '../utils/gt.js';
|
|
5
|
-
import { validateJsonSchema } from '../formats/json/utils.js';
|
|
6
|
-
import { mergeJson } from '../formats/json/mergeJson.js';
|
|
7
|
-
import { TextDecoder } from 'node:util';
|
|
8
|
-
import mergeYaml from '../formats/yaml/mergeYaml.js';
|
|
9
|
-
import { validateYamlSchema } from '../formats/yaml/utils.js';
|
|
10
|
-
/**
|
|
11
|
-
* Downloads a file from the API and saves it to a local directory
|
|
12
|
-
* @param translationId - The ID of the translation to download
|
|
13
|
-
* @param outputPath - The path to save the file to
|
|
14
|
-
* @param maxRetries - The maximum number of retries to attempt
|
|
15
|
-
* @param retryDelay - The delay between retries in milliseconds
|
|
16
|
-
*/
|
|
17
|
-
export async function downloadFile(translationId, outputPath, inputPath, locale, options, maxRetries = 3, retryDelay = 1000) {
|
|
18
|
-
let retries = 0;
|
|
19
|
-
while (retries <= maxRetries) {
|
|
20
|
-
try {
|
|
21
|
-
// Get the file data as an ArrayBuffer
|
|
22
|
-
const fileData = await gt.downloadFile(translationId);
|
|
23
|
-
// Ensure the directory exists
|
|
24
|
-
const dir = path.dirname(outputPath);
|
|
25
|
-
if (!fs.existsSync(dir)) {
|
|
26
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
let data = new TextDecoder().decode(fileData);
|
|
29
|
-
if (options.options?.jsonSchema && locale) {
|
|
30
|
-
const jsonSchema = validateJsonSchema(options.options, inputPath);
|
|
31
|
-
if (jsonSchema) {
|
|
32
|
-
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
33
|
-
if (originalContent) {
|
|
34
|
-
data = mergeJson(originalContent, inputPath, options.options, [
|
|
35
|
-
{
|
|
36
|
-
translatedContent: data,
|
|
37
|
-
targetLocale: locale,
|
|
38
|
-
},
|
|
39
|
-
], options.defaultLocale)[0];
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (options.options?.yamlSchema && locale) {
|
|
44
|
-
const yamlSchema = validateYamlSchema(options.options, inputPath);
|
|
45
|
-
if (yamlSchema) {
|
|
46
|
-
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
47
|
-
if (originalContent) {
|
|
48
|
-
data = mergeYaml(originalContent, inputPath, options.options, [
|
|
49
|
-
{
|
|
50
|
-
translatedContent: data,
|
|
51
|
-
targetLocale: locale,
|
|
52
|
-
},
|
|
53
|
-
])[0];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Write the file to disk
|
|
58
|
-
await fs.promises.writeFile(outputPath, data);
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
// If we've retried too many times, log an error and return false
|
|
63
|
-
if (retries >= maxRetries) {
|
|
64
|
-
logError(`Error downloading file ${outputPath} after ${maxRetries + 1} attempts: ` +
|
|
65
|
-
error);
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
// Increment retry counter and wait before next attempt
|
|
69
|
-
retries++;
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { RetrievedTranslations } from 'generaltranslation/types';
|
|
2
|
-
/**
|
|
3
|
-
* Fetches translations from the API and saves them to a local directory
|
|
4
|
-
* @param translationsDir - The directory to save the translations to
|
|
5
|
-
* @param fileType - The file type to save the translations as (file extension)
|
|
6
|
-
*/
|
|
7
|
-
export declare function fetchTranslations(versionId: string): Promise<RetrievedTranslations>;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { logError } from '../console/logging.js';
|
|
3
|
-
import { gt } from '../utils/gt.js';
|
|
4
|
-
/**
|
|
5
|
-
* Fetches translations from the API and saves them to a local directory
|
|
6
|
-
* @param translationsDir - The directory to save the translations to
|
|
7
|
-
* @param fileType - The file type to save the translations as (file extension)
|
|
8
|
-
*/
|
|
9
|
-
export async function fetchTranslations(versionId) {
|
|
10
|
-
try {
|
|
11
|
-
const result = await gt.fetchTranslations(versionId);
|
|
12
|
-
return result.translations;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
logError(chalk.red('Failed to fetch translations'));
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Settings, SupportedLibraries, Updates } from '../types/index.js';
|
|
2
|
-
import { DataFormat } from '../types/data.js';
|
|
3
|
-
export type ApiOptions = Settings & {
|
|
4
|
-
timeout: string;
|
|
5
|
-
dataFormat: DataFormat;
|
|
6
|
-
description?: string;
|
|
7
|
-
requireApproval?: boolean;
|
|
8
|
-
};
|
|
9
|
-
export type SendUpdatesResult = {
|
|
10
|
-
versionId: string;
|
|
11
|
-
locales: string[];
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Sends updates to the API
|
|
15
|
-
* @param updates - The updates to send
|
|
16
|
-
* @param options - The options for the API call
|
|
17
|
-
* @returns The versionId of the updated project
|
|
18
|
-
*/
|
|
19
|
-
export declare function sendUpdates(updates: Updates, options: ApiOptions, library: SupportedLibraries): Promise<SendUpdatesResult>;
|
package/dist/api/sendUpdates.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { createSpinner, logSuccess, logWarning, } from '../console/logging.js';
|
|
3
|
-
import updateConfig from '../fs/config/updateConfig.js';
|
|
4
|
-
import { isUsingLocalTranslations } from '../config/utils.js';
|
|
5
|
-
import { gt } from '../utils/gt.js';
|
|
6
|
-
/**
|
|
7
|
-
* Sends updates to the API
|
|
8
|
-
* @param updates - The updates to send
|
|
9
|
-
* @param options - The options for the API call
|
|
10
|
-
* @returns The versionId of the updated project
|
|
11
|
-
*/
|
|
12
|
-
export async function sendUpdates(updates, options, library) {
|
|
13
|
-
const spinner = createSpinner('dots');
|
|
14
|
-
spinner.start(`Sending ${library} updates to General Translation API...`);
|
|
15
|
-
try {
|
|
16
|
-
const responseData = await gt.enqueueEntries(updates, {
|
|
17
|
-
sourceLocale: options.defaultLocale,
|
|
18
|
-
targetLocales: options.locales,
|
|
19
|
-
dataFormat: options.dataFormat,
|
|
20
|
-
version: options.version,
|
|
21
|
-
description: options.description,
|
|
22
|
-
requireApproval: options.requireApproval,
|
|
23
|
-
modelProvider: options.modelProvider,
|
|
24
|
-
});
|
|
25
|
-
const { versionId, message, locales, projectSettings } = responseData;
|
|
26
|
-
spinner.stop(chalk.green('Sent updates'));
|
|
27
|
-
logSuccess(message);
|
|
28
|
-
if (isUsingLocalTranslations(options) && projectSettings.cdnEnabled) {
|
|
29
|
-
logWarning(chalk.yellow('Your project is configured to use the CDN, but you are also using local translations. Please disable one or the other.'));
|
|
30
|
-
}
|
|
31
|
-
else if (!isUsingLocalTranslations(options) &&
|
|
32
|
-
!projectSettings.cdnEnabled) {
|
|
33
|
-
logWarning(chalk.yellow('Your project is not using the CDN, nor are you using local translations. Please enable one or the other.'));
|
|
34
|
-
}
|
|
35
|
-
if (options.config) {
|
|
36
|
-
await updateConfig({
|
|
37
|
-
configFilepath: options.config,
|
|
38
|
-
_versionId: versionId,
|
|
39
|
-
locales,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return { versionId, locales };
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
spinner.stop(chalk.red('Failed to send updates'));
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Waits for translations to be deployed to the General Translation API
|
|
3
|
-
* @param versionId - The version ID of the project
|
|
4
|
-
* @param locales - The locales to wait for
|
|
5
|
-
* @param startTime - The start time of the wait
|
|
6
|
-
* @param timeoutDuration - The timeout duration for the wait
|
|
7
|
-
* @returns True if all translations are deployed, false otherwise
|
|
8
|
-
*/
|
|
9
|
-
export declare const waitForUpdates: (versionId: string, startTime: number, timeoutDuration: number) => Promise<boolean>;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { createOraSpinner, logErrorAndExit, } from '../console/logging.js';
|
|
3
|
-
import { getLocaleProperties } from 'generaltranslation';
|
|
4
|
-
import { gt } from '../utils/gt.js';
|
|
5
|
-
/**
|
|
6
|
-
* Waits for translations to be deployed to the General Translation API
|
|
7
|
-
* @param versionId - The version ID of the project
|
|
8
|
-
* @param locales - The locales to wait for
|
|
9
|
-
* @param startTime - The start time of the wait
|
|
10
|
-
* @param timeoutDuration - The timeout duration for the wait
|
|
11
|
-
* @returns True if all translations are deployed, false otherwise
|
|
12
|
-
*/
|
|
13
|
-
export const waitForUpdates = async (versionId, startTime, timeoutDuration) => {
|
|
14
|
-
console.log();
|
|
15
|
-
const spinner = await createOraSpinner();
|
|
16
|
-
spinner.start('Waiting for translation...');
|
|
17
|
-
const checkDeployment = async () => {
|
|
18
|
-
let data;
|
|
19
|
-
try {
|
|
20
|
-
// get translation status
|
|
21
|
-
data = await gt.checkTranslationStatus(versionId);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
// check if any locales are waiting for approval
|
|
27
|
-
const { availableLocales, locales, localesWaitingForApproval } = data;
|
|
28
|
-
if (localesWaitingForApproval.length > 0) {
|
|
29
|
-
spinner.stop();
|
|
30
|
-
logErrorAndExit(`Error! ${localesWaitingForApproval.length} translations are waiting for approval:\n${localesWaitingForApproval
|
|
31
|
-
.map((locale) => {
|
|
32
|
-
const localeProperties = getLocaleProperties(locale);
|
|
33
|
-
return `${localeProperties.name} (${localeProperties.code})`;
|
|
34
|
-
})
|
|
35
|
-
.join('\n')}\nPlease approve these locales in the General Translation dashboard, then re-run the command.`);
|
|
36
|
-
}
|
|
37
|
-
// check if all locales are available
|
|
38
|
-
if (availableLocales) {
|
|
39
|
-
availableLocales.forEach((locale) => {
|
|
40
|
-
if (!availableLocales.includes(locale)) {
|
|
41
|
-
availableLocales.push(locale);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
const newSuffixText = [
|
|
45
|
-
chalk.green(`[${availableLocales.length}/${locales.length}]`) +
|
|
46
|
-
` translations completed`,
|
|
47
|
-
...availableLocales.map((locale) => {
|
|
48
|
-
const localeProperties = getLocaleProperties(locale);
|
|
49
|
-
return `Translation completed for ${chalk.green(localeProperties.name)} (${chalk.green(localeProperties.code)})`;
|
|
50
|
-
}),
|
|
51
|
-
];
|
|
52
|
-
spinner.text = newSuffixText.join('\n');
|
|
53
|
-
}
|
|
54
|
-
// check if all locales are available
|
|
55
|
-
if (locales.every((locale) => availableLocales.includes(locale))) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
};
|
|
60
|
-
// Calculate time until next 5-second interval since startTime
|
|
61
|
-
const msUntilNextInterval = Math.max(0, 5000 - ((Date.now() - startTime) % 5000));
|
|
62
|
-
// Do first check immediately
|
|
63
|
-
const initialCheck = await checkDeployment();
|
|
64
|
-
if (initialCheck) {
|
|
65
|
-
spinner.succeed(chalk.green('All translations are live!'));
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
return new Promise((resolve) => {
|
|
69
|
-
let intervalCheck;
|
|
70
|
-
// Start the interval aligned with the original request time
|
|
71
|
-
setTimeout(() => {
|
|
72
|
-
intervalCheck = setInterval(async () => {
|
|
73
|
-
const isDeployed = await checkDeployment();
|
|
74
|
-
const elapsed = Date.now() - startTime;
|
|
75
|
-
if (isDeployed || elapsed >= timeoutDuration) {
|
|
76
|
-
clearInterval(intervalCheck);
|
|
77
|
-
if (isDeployed) {
|
|
78
|
-
spinner.succeed(chalk.green('All translations are live!'));
|
|
79
|
-
resolve(true);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
spinner.fail(chalk.red('Timed out waiting for translations'));
|
|
83
|
-
resolve(false);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}, 5000);
|
|
87
|
-
}, msUntilNextInterval);
|
|
88
|
-
});
|
|
89
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { fetchTranslations } from '../api/fetchTranslations.js';
|
|
2
|
-
import { waitForUpdates } from '../api/waitForUpdates.js';
|
|
3
|
-
import { saveTranslations } from '../formats/gt/save.js';
|
|
4
|
-
import { isUsingLocalTranslations } from '../config/utils.js';
|
|
5
|
-
export async function translate(settings, versionId) {
|
|
6
|
-
// timeout was validated earlier
|
|
7
|
-
const startTime = Date.now();
|
|
8
|
-
const timeout = parseInt(settings.timeout) * 1000;
|
|
9
|
-
const result = await waitForUpdates(versionId, startTime, timeout);
|
|
10
|
-
if (!result) {
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
const translations = await fetchTranslations(versionId);
|
|
14
|
-
// Save translations to local directory if files.gt.output is provided
|
|
15
|
-
if (settings.files && isUsingLocalTranslations(settings)) {
|
|
16
|
-
await saveTranslations(translations, settings.files.placeholderPaths, 'JSX');
|
|
17
|
-
}
|
|
18
|
-
}
|