gtx-cli 2.1.5-alpha.2 → 2.1.5-alpha.5
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 +35 -32
- package/dist/api/downloadFile.d.ts +9 -0
- package/dist/api/downloadFile.js +74 -0
- package/dist/api/downloadFileBatch.d.ts +0 -1
- package/dist/api/fetchTranslations.d.ts +7 -0
- package/dist/api/fetchTranslations.js +18 -0
- package/dist/api/sendFiles.d.ts +13 -2
- package/dist/api/sendFiles.js +8 -15
- package/dist/api/sendUpdates.d.ts +19 -0
- package/dist/api/sendUpdates.js +48 -0
- package/dist/api/waitForUpdates.d.ts +9 -0
- package/dist/api/waitForUpdates.js +89 -0
- package/dist/cli/base.d.ts +17 -5
- package/dist/cli/base.js +75 -48
- package/dist/cli/next.js +1 -0
- package/dist/cli/react.d.ts +5 -2
- package/dist/cli/react.js +153 -13
- package/dist/config/generateSettings.js +6 -11
- package/dist/console/index.d.ts +0 -1
- package/dist/console/index.js +0 -1
- package/dist/console/logging.d.ts +0 -1
- package/dist/console/logging.js +0 -3
- package/dist/formats/files/fileMapping.d.ts +1 -2
- package/dist/formats/files/fileMapping.js +0 -6
- package/dist/formats/files/translate.d.ts +13 -4
- package/dist/formats/files/translate.js +142 -30
- package/dist/formats/files/upload.js +75 -1
- package/dist/formats/gt/save.d.ts +2 -1
- package/dist/formats/gt/save.js +5 -2
- package/dist/fs/config/setupConfig.d.ts +0 -1
- package/dist/fs/config/setupConfig.js +0 -1
- package/dist/fs/config/updateConfig.d.ts +2 -1
- package/dist/fs/config/updateConfig.js +1 -1
- package/dist/fs/copyFile.d.ts +2 -1
- package/dist/translation/parse.d.ts +2 -2
- package/dist/translation/parse.js +6 -4
- package/dist/translation/stage.d.ts +5 -2
- package/dist/translation/stage.js +55 -13
- package/dist/translation/translate.d.ts +2 -0
- package/dist/translation/translate.js +18 -0
- package/dist/types/index.d.ts +4 -27
- package/dist/utils/flattenJsonFiles.d.ts +2 -2
- package/dist/utils/localizeStaticImports.d.ts +2 -2
- package/dist/utils/localizeStaticUrls.d.ts +6 -2
- package/dist/utils/localizeStaticUrls.js +119 -96
- package/package.json +2 -2
- package/dist/cli/commands/stage.d.ts +0 -5
- package/dist/cli/commands/stage.js +0 -100
- package/dist/cli/commands/translate.d.ts +0 -6
- package/dist/cli/commands/translate.js +0 -54
- package/dist/cli/flags.d.ts +0 -3
- package/dist/cli/flags.js +0 -37
- package/dist/fs/config/updateVersions.d.ts +0 -10
- package/dist/fs/config/updateVersions.js +0 -30
- package/dist/types/files.d.ts +0 -1
- package/dist/types/files.js +0 -1
- package/dist/utils/hash.d.ts +0 -6
- package/dist/utils/hash.js +0 -11
|
@@ -7,6 +7,7 @@ import remarkMdx from 'remark-mdx';
|
|
|
7
7
|
import remarkFrontmatter from 'remark-frontmatter';
|
|
8
8
|
import remarkStringify from 'remark-stringify';
|
|
9
9
|
import { visit } from 'unist-util-visit';
|
|
10
|
+
import { logMessage } from '../console/logging.js';
|
|
10
11
|
const { isMatch } = micromatch;
|
|
11
12
|
/**
|
|
12
13
|
* Localizes static urls in content files.
|
|
@@ -21,19 +22,25 @@ const { isMatch } = micromatch;
|
|
|
21
22
|
* - Support more file types
|
|
22
23
|
* - Support more complex paths
|
|
23
24
|
*/
|
|
24
|
-
export default async function localizeStaticUrls(settings
|
|
25
|
+
export default async function localizeStaticUrls(settings, targetLocales // Optional filter for specific locales
|
|
26
|
+
) {
|
|
25
27
|
if (!settings.files ||
|
|
26
28
|
(Object.keys(settings.files.placeholderPaths).length === 1 &&
|
|
27
29
|
settings.files.placeholderPaths.gt)) {
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
32
|
const { resolvedPaths: sourceFiles } = settings.files;
|
|
31
|
-
|
|
33
|
+
// Use filtered locales if provided, otherwise use all locales
|
|
34
|
+
const locales = targetLocales || settings.locales;
|
|
35
|
+
const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, // Always use all locales for mapping, filter later
|
|
36
|
+
settings.defaultLocale);
|
|
32
37
|
// Process all file types at once with a single call
|
|
33
38
|
const processPromises = [];
|
|
34
39
|
// First, process default locale files (from source files)
|
|
35
40
|
// This is needed because they might not be in the fileMapping if they're not being translated
|
|
36
|
-
if
|
|
41
|
+
// Only process default locale if it's in the target locales filter
|
|
42
|
+
if (!fileMapping[settings.defaultLocale] &&
|
|
43
|
+
locales.includes(settings.defaultLocale)) {
|
|
37
44
|
const defaultLocaleFiles = [];
|
|
38
45
|
// Collect all .md and .mdx files from sourceFiles
|
|
39
46
|
if (sourceFiles.md) {
|
|
@@ -48,7 +55,7 @@ export default async function localizeStaticUrls(settings) {
|
|
|
48
55
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
49
56
|
// Localize the file using default locale
|
|
50
57
|
const localizedFile = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, settings.defaultLocale, // Process as default locale
|
|
51
|
-
settings.
|
|
58
|
+
settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
|
|
52
59
|
// Write the localized file back to the same path
|
|
53
60
|
await fs.promises.writeFile(filePath, localizedFile);
|
|
54
61
|
}));
|
|
@@ -56,7 +63,9 @@ export default async function localizeStaticUrls(settings) {
|
|
|
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,7 +73,7 @@ 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 localizedFile = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.
|
|
76
|
+
const localizedFile = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
|
|
68
77
|
// Write the localized file to the target path
|
|
69
78
|
await fs.promises.writeFile(filePath, localizedFile);
|
|
70
79
|
}));
|
|
@@ -75,21 +84,28 @@ export default async function localizeStaticUrls(settings) {
|
|
|
75
84
|
/**
|
|
76
85
|
* Determines if a URL should be processed based on pattern matching
|
|
77
86
|
*/
|
|
78
|
-
function shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale) {
|
|
79
|
-
// Skip absolute URLs (http://, https://, //, etc.)
|
|
80
|
-
if (originalUrl.includes(':')) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
87
|
+
function shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain) {
|
|
83
88
|
const patternWithoutSlash = patternHead.replace(/\/$/, '');
|
|
89
|
+
// Handle absolute URLs with baseDomain
|
|
90
|
+
let urlToCheck = originalUrl;
|
|
91
|
+
if (baseDomain && originalUrl.startsWith(baseDomain)) {
|
|
92
|
+
urlToCheck = originalUrl.substring(baseDomain.length);
|
|
93
|
+
}
|
|
84
94
|
if (targetLocale === defaultLocale) {
|
|
85
95
|
// For default locale processing, check if URL contains the pattern
|
|
86
|
-
return
|
|
96
|
+
return urlToCheck.includes(patternWithoutSlash);
|
|
87
97
|
}
|
|
88
98
|
else {
|
|
89
99
|
// For non-default locales, check if URL starts with pattern
|
|
90
|
-
return
|
|
100
|
+
return urlToCheck.startsWith(patternWithoutSlash);
|
|
91
101
|
}
|
|
92
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Determines if a URL should be processed based on the base domain
|
|
105
|
+
*/
|
|
106
|
+
function shouldProcessAbsoluteUrl(originalUrl, baseDomain) {
|
|
107
|
+
return originalUrl.startsWith(baseDomain);
|
|
108
|
+
}
|
|
93
109
|
/**
|
|
94
110
|
* Checks if a URL should be excluded based on exclusion patterns
|
|
95
111
|
*/
|
|
@@ -98,102 +114,79 @@ function isUrlExcluded(originalUrl, exclude, defaultLocale) {
|
|
|
98
114
|
return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));
|
|
99
115
|
}
|
|
100
116
|
/**
|
|
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
|
|
117
|
+
* Main URL transformation function that delegates to specific scenarios
|
|
134
118
|
*/
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
119
|
+
export function transformUrlPath(originalUrl, patternHead, targetLocale, defaultLocale, hideDefaultLocale) {
|
|
120
|
+
const originalPathArray = originalUrl
|
|
121
|
+
.split('/')
|
|
122
|
+
.filter((path) => path !== '');
|
|
123
|
+
const patternHeadArray = patternHead.split('/').filter((path) => path !== '');
|
|
124
|
+
// check if the pattern head matches the original path
|
|
125
|
+
if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {
|
|
139
126
|
return null;
|
|
140
127
|
}
|
|
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}`;
|
|
128
|
+
if (patternHeadArray.length > originalPathArray.length) {
|
|
129
|
+
return null; // Pattern is longer than the URL path
|
|
150
130
|
}
|
|
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}`;
|
|
131
|
+
let result = null;
|
|
132
|
+
if (targetLocale === defaultLocale) {
|
|
133
|
+
if (hideDefaultLocale) {
|
|
134
|
+
// check if default locale is already present
|
|
135
|
+
if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
// remove default locale
|
|
139
|
+
const newPathArray = [
|
|
140
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
141
|
+
...originalPathArray.slice(patternHeadArray.length + 1),
|
|
142
|
+
];
|
|
143
|
+
result = newPathArray.join('/');
|
|
172
144
|
}
|
|
173
145
|
else {
|
|
174
|
-
|
|
146
|
+
// check if default locale is already present
|
|
147
|
+
if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// insert default locale
|
|
151
|
+
const newPathArray = [
|
|
152
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
153
|
+
defaultLocale,
|
|
154
|
+
...originalPathArray.slice(patternHeadArray.length),
|
|
155
|
+
];
|
|
156
|
+
result = newPathArray.join('/');
|
|
175
157
|
}
|
|
176
158
|
}
|
|
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
159
|
else if (hideDefaultLocale) {
|
|
187
|
-
|
|
160
|
+
const newPathArray = [
|
|
161
|
+
...originalPathArray.slice(0, patternHeadArray.length),
|
|
162
|
+
targetLocale,
|
|
163
|
+
...originalPathArray.slice(patternHeadArray.length),
|
|
164
|
+
];
|
|
165
|
+
result = newPathArray.join('/');
|
|
188
166
|
}
|
|
189
167
|
else {
|
|
190
|
-
|
|
168
|
+
// check default locale
|
|
169
|
+
if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
// replace default locale with target locale
|
|
173
|
+
const newPathArray = [...originalPathArray];
|
|
174
|
+
newPathArray[patternHeadArray.length] = targetLocale;
|
|
175
|
+
result = newPathArray.join('/');
|
|
176
|
+
}
|
|
177
|
+
// check for leading and trailing slashes
|
|
178
|
+
if (originalUrl.startsWith('/')) {
|
|
179
|
+
result = '/' + result;
|
|
180
|
+
}
|
|
181
|
+
if (originalUrl.endsWith('/')) {
|
|
182
|
+
result = result + '/';
|
|
191
183
|
}
|
|
184
|
+
return result;
|
|
192
185
|
}
|
|
193
186
|
/**
|
|
194
187
|
* AST-based transformation for MDX files using remark-mdx
|
|
195
188
|
*/
|
|
196
|
-
function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', exclude = []) {
|
|
189
|
+
function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', exclude = [], baseDomain) {
|
|
197
190
|
const transformedUrls = [];
|
|
198
191
|
if (!pattern.startsWith('/')) {
|
|
199
192
|
pattern = '/' + pattern;
|
|
@@ -246,7 +239,21 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
246
239
|
// Helper function to transform URL based on pattern
|
|
247
240
|
const transformUrl = (originalUrl, linkType) => {
|
|
248
241
|
// Check if URL should be processed
|
|
249
|
-
if (!shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale)) {
|
|
242
|
+
if (!shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain)) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
// Skip absolute URLs (http://, https://, //, etc.)
|
|
246
|
+
if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {
|
|
247
|
+
// Get everything after the base domain
|
|
248
|
+
logMessage(`originalUrl: ${originalUrl}`);
|
|
249
|
+
logMessage(`baseDomain: ${baseDomain}`);
|
|
250
|
+
const afterDomain = originalUrl.substring(baseDomain.length);
|
|
251
|
+
logMessage(`afterDomain: ${afterDomain}`);
|
|
252
|
+
const transformedPath = transformUrlPath(afterDomain, patternHead, targetLocale, defaultLocale, hideDefaultLocale);
|
|
253
|
+
return transformedPath ? baseDomain + transformedPath : null;
|
|
254
|
+
}
|
|
255
|
+
// Exclude colon-prefixed URLs (http://, https://, //, etc.)
|
|
256
|
+
if (originalUrl.split('?')[0].includes(':')) {
|
|
250
257
|
return null;
|
|
251
258
|
}
|
|
252
259
|
// Transform the URL based on locale and configuration
|
|
@@ -381,8 +388,24 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
381
388
|
}
|
|
382
389
|
// AST-based transformation for MDX files using remark
|
|
383
390
|
function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
|
|
384
|
-
exclude = []) {
|
|
391
|
+
exclude = [], baseDomain) {
|
|
385
392
|
// Use AST-based transformation for MDX files
|
|
386
|
-
const result = transformMdxUrls(file, defaultLocale, targetLocale, hideDefaultLocale, pattern, exclude);
|
|
393
|
+
const result = transformMdxUrls(file, defaultLocale, targetLocale, hideDefaultLocale, pattern, exclude, baseDomain || '');
|
|
387
394
|
return result.content;
|
|
388
395
|
}
|
|
396
|
+
function cleanPath(path) {
|
|
397
|
+
let cleanedPath = path.startsWith('/') ? path.slice(1) : path;
|
|
398
|
+
cleanedPath = cleanedPath.endsWith('/')
|
|
399
|
+
? cleanedPath.slice(0, -1)
|
|
400
|
+
: cleanedPath;
|
|
401
|
+
return cleanedPath;
|
|
402
|
+
}
|
|
403
|
+
function checkIfPathMatchesPattern(originalUrlArray, patternHeadArray) {
|
|
404
|
+
// check if the pattern head matches the original path
|
|
405
|
+
for (let i = 0; i < patternHeadArray.length; i++) {
|
|
406
|
+
if (patternHeadArray[i] !== originalUrlArray?.[i]) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return true;
|
|
411
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "2.1.5-alpha.
|
|
3
|
+
"version": "2.1.5-alpha.5",
|
|
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.0",
|
|
91
91
|
"json-pointer": "^0.6.2",
|
|
92
92
|
"jsonpath-plus": "^10.3.0",
|
|
93
93
|
"jsonpointer": "^5.0.1",
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Settings, SupportedLibraries, TranslateFlags } from '../../types/index.js';
|
|
2
|
-
import { SendFilesResult } from '../../api/sendFiles.js';
|
|
3
|
-
export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
|
|
4
|
-
export declare const TEMPLATE_FILE_ID: string;
|
|
5
|
-
export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<SendFilesResult | undefined>;
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { logErrorAndExit, logSuccess } from '../../console/logging.js';
|
|
2
|
-
import { noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, invalidConfigurationError, } from '../../console/index.js';
|
|
3
|
-
import { aggregateFiles } from '../../formats/files/translate.js';
|
|
4
|
-
import { aggregateReactTranslations } from '../../translation/stage.js';
|
|
5
|
-
import { sendFiles } from '../../api/sendFiles.js';
|
|
6
|
-
import { updateVersions } from '../../fs/config/updateVersions.js';
|
|
7
|
-
import updateConfig from '../../fs/config/updateConfig.js';
|
|
8
|
-
import { hashStringSync } from '../../utils/hash.js';
|
|
9
|
-
export const TEMPLATE_FILE_NAME = '__INTERNAL_GT_TEMPLATE_NAME__';
|
|
10
|
-
export const TEMPLATE_FILE_ID = hashStringSync(TEMPLATE_FILE_NAME);
|
|
11
|
-
export async function handleStage(options, settings, library, stage) {
|
|
12
|
-
// Validate required settings are present if not in dry run
|
|
13
|
-
if (!options.dryRun) {
|
|
14
|
-
if (!settings.locales) {
|
|
15
|
-
logErrorAndExit(noLocalesError);
|
|
16
|
-
}
|
|
17
|
-
if (!settings.defaultLocale) {
|
|
18
|
-
logErrorAndExit(noDefaultLocaleError);
|
|
19
|
-
}
|
|
20
|
-
if (!settings.apiKey) {
|
|
21
|
-
logErrorAndExit(noApiKeyError);
|
|
22
|
-
}
|
|
23
|
-
if (settings.apiKey.startsWith('gtx-dev-')) {
|
|
24
|
-
logErrorAndExit(devApiKeyError);
|
|
25
|
-
}
|
|
26
|
-
if (!settings.projectId) {
|
|
27
|
-
logErrorAndExit(noProjectIdError);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// Aggregate files
|
|
31
|
-
const allFiles = await aggregateFiles(settings);
|
|
32
|
-
// Parse for React components
|
|
33
|
-
let reactComponents = 0;
|
|
34
|
-
if (library === 'gt-react' || library === 'gt-next') {
|
|
35
|
-
const updates = await aggregateReactTranslations(options, settings, library);
|
|
36
|
-
if (updates.length > 0) {
|
|
37
|
-
if (!options.dryRun &&
|
|
38
|
-
!settings.publish &&
|
|
39
|
-
!settings.files?.placeholderPaths.gt) {
|
|
40
|
-
logErrorAndExit(invalidConfigurationError);
|
|
41
|
-
}
|
|
42
|
-
reactComponents = updates.length;
|
|
43
|
-
// Convert updates to a file object
|
|
44
|
-
const fileData = {};
|
|
45
|
-
const fileMetadata = {};
|
|
46
|
-
// Convert updates to the proper data format
|
|
47
|
-
for (const update of updates) {
|
|
48
|
-
const { source, metadata, dataFormat } = update;
|
|
49
|
-
metadata.dataFormat = dataFormat; // add the data format to the metadata
|
|
50
|
-
const { hash, id } = metadata;
|
|
51
|
-
if (id) {
|
|
52
|
-
fileData[id] = source;
|
|
53
|
-
fileMetadata[id] = metadata;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
fileData[hash] = source;
|
|
57
|
-
fileMetadata[hash] = metadata;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
allFiles.push({
|
|
61
|
-
fileName: TEMPLATE_FILE_NAME,
|
|
62
|
-
content: JSON.stringify(fileData),
|
|
63
|
-
fileFormat: 'GTJSON',
|
|
64
|
-
formatMetadata: fileMetadata,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Dry run
|
|
69
|
-
if (options.dryRun) {
|
|
70
|
-
const fileNames = allFiles
|
|
71
|
-
.map((file) => {
|
|
72
|
-
if (file.fileName === TEMPLATE_FILE_NAME) {
|
|
73
|
-
return `- <React Elements> (${reactComponents})`;
|
|
74
|
-
}
|
|
75
|
-
return `- ${file.fileName}`;
|
|
76
|
-
})
|
|
77
|
-
.join('\n');
|
|
78
|
-
logSuccess(`Dry run: No files were sent to General Translation. Found files:\n${fileNames}`);
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
// Send translations to General Translation
|
|
82
|
-
let filesTranslationResponse;
|
|
83
|
-
if (allFiles.length > 0) {
|
|
84
|
-
filesTranslationResponse = await sendFiles(allFiles, options, settings);
|
|
85
|
-
if (stage) {
|
|
86
|
-
await updateVersions({
|
|
87
|
-
configDirectory: settings.configDirectory,
|
|
88
|
-
versionData: filesTranslationResponse.data,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
const { versionId } = filesTranslationResponse.data[TEMPLATE_FILE_ID];
|
|
92
|
-
if (versionId) {
|
|
93
|
-
await updateConfig({
|
|
94
|
-
configFilepath: settings.config,
|
|
95
|
-
_versionId: versionId,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return filesTranslationResponse;
|
|
100
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { SendFilesResult } from '../../api/sendFiles.js';
|
|
2
|
-
import { TranslateFlags } from '../../types/index.js';
|
|
3
|
-
import { Settings } from '../../types/index.js';
|
|
4
|
-
export declare function handleTranslate(options: TranslateFlags, settings: Settings, filesTranslationResponse: SendFilesResult | undefined): Promise<void>;
|
|
5
|
-
export declare function handleDownload(options: TranslateFlags, settings: Settings): Promise<void>;
|
|
6
|
-
export declare function postProcessTranslations(settings: Settings): Promise<void>;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { checkFileTranslations } from '../../api/checkFileTranslations.js';
|
|
2
|
-
import { createFileMapping } from '../../formats/files/fileMapping.js';
|
|
3
|
-
import { logError } from '../../console/logging.js';
|
|
4
|
-
import { getStagedVersions } from '../../fs/config/updateVersions.js';
|
|
5
|
-
import copyFile from '../../fs/copyFile.js';
|
|
6
|
-
import localizeStaticImports from '../../utils/localizeStaticImports.js';
|
|
7
|
-
import flattenJsonFiles from '../../utils/flattenJsonFiles.js';
|
|
8
|
-
import localizeStaticUrls from '../../utils/localizeStaticUrls.js';
|
|
9
|
-
import { noFilesError, noVersionIdError } from '../../console/index.js';
|
|
10
|
-
// Downloads translations that were completed
|
|
11
|
-
export async function handleTranslate(options, settings, filesTranslationResponse) {
|
|
12
|
-
if (filesTranslationResponse && settings.files) {
|
|
13
|
-
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
14
|
-
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
15
|
-
const { data } = filesTranslationResponse;
|
|
16
|
-
// Check for remaining translations
|
|
17
|
-
await checkFileTranslations(data, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
// Downloads translations that were originally staged
|
|
21
|
-
export async function handleDownload(options, settings) {
|
|
22
|
-
if (!settings._versionId) {
|
|
23
|
-
logError(noVersionIdError);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
if (!settings.files) {
|
|
27
|
-
logError(noFilesError);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
// Files
|
|
31
|
-
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
32
|
-
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
33
|
-
const stagedVersionData = await getStagedVersions(settings.configDirectory);
|
|
34
|
-
// Check for remaining translations
|
|
35
|
-
await checkFileTranslations(stagedVersionData, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings);
|
|
36
|
-
}
|
|
37
|
-
export async function postProcessTranslations(settings) {
|
|
38
|
-
// Localize static urls (/docs -> /[locale]/docs)
|
|
39
|
-
if (settings.options?.experimentalLocalizeStaticUrls) {
|
|
40
|
-
await localizeStaticUrls(settings);
|
|
41
|
-
}
|
|
42
|
-
// Localize static imports (/docs -> /[locale]/docs)
|
|
43
|
-
if (settings.options?.experimentalLocalizeStaticImports) {
|
|
44
|
-
await localizeStaticImports(settings);
|
|
45
|
-
}
|
|
46
|
-
// Flatten json files into a single file
|
|
47
|
-
if (settings.options?.experimentalFlattenJsonFiles) {
|
|
48
|
-
await flattenJsonFiles(settings);
|
|
49
|
-
}
|
|
50
|
-
// Copy files to the target locale
|
|
51
|
-
if (settings.options?.copyFiles) {
|
|
52
|
-
await copyFile(settings);
|
|
53
|
-
}
|
|
54
|
-
}
|
package/dist/cli/flags.d.ts
DELETED
package/dist/cli/flags.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import findFilepath from '../fs/findFilepath.js';
|
|
2
|
-
const DEFAULT_TIMEOUT = 600;
|
|
3
|
-
export function attachTranslateFlags(command) {
|
|
4
|
-
command
|
|
5
|
-
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
6
|
-
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
7
|
-
.option('--project-id <id>', 'General Translation project ID')
|
|
8
|
-
.option('--version-id <id>', 'General Translation version ID')
|
|
9
|
-
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
10
|
-
.option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
|
|
11
|
-
.option('--dry-run', 'Dry run, do not send updates to the General Translation API', false)
|
|
12
|
-
.option('--timeout <seconds>', 'Translation wait timeout in seconds', (value) => {
|
|
13
|
-
const parsedValue = parseInt(value, 10);
|
|
14
|
-
if (isNaN(parsedValue)) {
|
|
15
|
-
throw new Error('Not a number.');
|
|
16
|
-
}
|
|
17
|
-
if (parsedValue < 0) {
|
|
18
|
-
throw new Error('Timeout must be a positive number.');
|
|
19
|
-
}
|
|
20
|
-
return parsedValue;
|
|
21
|
-
}, DEFAULT_TIMEOUT)
|
|
22
|
-
.option('--publish', 'Publish translations to the CDN', false)
|
|
23
|
-
.option('--experimental-localize-static-urls', 'Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.', false)
|
|
24
|
-
.option('--experimental-hide-default-locale', 'When localizing static locales, hide the default locale from the path', false)
|
|
25
|
-
.option('--experimental-flatten-json-files', 'Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.', false)
|
|
26
|
-
.option('--experimental-localize-static-imports', 'Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.', false);
|
|
27
|
-
return command;
|
|
28
|
-
}
|
|
29
|
-
export function attachAdditionalReactTranslateFlags(command) {
|
|
30
|
-
command
|
|
31
|
-
.option('--tsconfig, --jsconfig <path>', 'Path to custom jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
|
|
32
|
-
.option('--dictionary <path>', 'Path to dictionary file')
|
|
33
|
-
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
34
|
-
.option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
|
|
35
|
-
.option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false);
|
|
36
|
-
return command;
|
|
37
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
type StagedVersionData = Record<string, {
|
|
2
|
-
fileName: string;
|
|
3
|
-
versionId: string;
|
|
4
|
-
}>;
|
|
5
|
-
export declare function updateVersions({ configDirectory, versionData, }: {
|
|
6
|
-
configDirectory: string;
|
|
7
|
-
versionData: StagedVersionData;
|
|
8
|
-
}): Promise<void>;
|
|
9
|
-
export declare function getStagedVersions(configDirectory: string): Promise<StagedVersionData>;
|
|
10
|
-
export {};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { displayUpdatedVersionsFile } from '../../console/logging.js';
|
|
3
|
-
import { logError } from '../../console/logging.js';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
const STAGED_VERSIONS_FILE = 'staged-versions.json';
|
|
6
|
-
// Update the versions.json file with the new version ids
|
|
7
|
-
// of the translations that were sent to the API
|
|
8
|
-
export async function updateVersions({ configDirectory, versionData, }) {
|
|
9
|
-
const versionFilepath = path.join(configDirectory, STAGED_VERSIONS_FILE);
|
|
10
|
-
try {
|
|
11
|
-
fs.mkdirSync(configDirectory, { recursive: true });
|
|
12
|
-
fs.writeFileSync(versionFilepath, JSON.stringify(versionData, null, 2));
|
|
13
|
-
// show update in console
|
|
14
|
-
displayUpdatedVersionsFile(versionFilepath);
|
|
15
|
-
}
|
|
16
|
-
catch (error) {
|
|
17
|
-
logError(`An error occurred while updating ${versionFilepath}: ${error}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export async function getStagedVersions(configDirectory) {
|
|
21
|
-
try {
|
|
22
|
-
const versionFilepath = path.join(configDirectory, STAGED_VERSIONS_FILE);
|
|
23
|
-
const versionData = JSON.parse(fs.readFileSync(versionFilepath, 'utf8'));
|
|
24
|
-
return versionData;
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
logError(`An error occurred while getting staged versions: ${error}`);
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
}
|
package/dist/types/files.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type FileMapping = Record<string, Record<string, string>>;
|
package/dist/types/files.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/hash.d.ts
DELETED
package/dist/utils/hash.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
/**
|
|
3
|
-
* Hashes a string using SHA-256 algorithm.
|
|
4
|
-
* @param {string} string - The string to be hashed.
|
|
5
|
-
* @returns {string} The hashed string.
|
|
6
|
-
*/
|
|
7
|
-
export function hashStringSync(string) {
|
|
8
|
-
const hash = crypto.createHash('sha256');
|
|
9
|
-
hash.update(string);
|
|
10
|
-
return hash.digest('hex');
|
|
11
|
-
}
|