gt 2.15.0 → 2.16.1
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 +30 -0
- package/dist/api/downloadFileBatch.js +28 -1
- package/dist/api/downloadFileBatch.js.map +1 -1
- package/dist/cli/base.js +16 -0
- package/dist/cli/base.js.map +1 -1
- package/dist/cli/commands/download.js +2 -1
- package/dist/cli/commands/download.js.map +1 -1
- package/dist/cli/commands/upload.js +5 -3
- package/dist/cli/commands/upload.js.map +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/flags.js.map +1 -1
- package/dist/console/index.d.ts +3 -0
- package/dist/console/index.js +4 -1
- package/dist/console/index.js.map +1 -1
- package/dist/formats/files/aggregateFiles.js +32 -2
- package/dist/formats/files/aggregateFiles.js.map +1 -1
- package/dist/formats/files/collectFonts.d.ts +7 -0
- package/dist/formats/files/collectFonts.js +30 -0
- package/dist/formats/files/collectFonts.js.map +1 -0
- package/dist/formats/files/detectLottieExpressions.d.ts +8 -0
- package/dist/formats/files/detectLottieExpressions.js +42 -0
- package/dist/formats/files/detectLottieExpressions.js.map +1 -0
- package/dist/formats/files/supportedFiles.d.ts +2 -1
- package/dist/formats/files/supportedFiles.js +4 -2
- package/dist/formats/files/supportedFiles.js.map +1 -1
- package/dist/formats/files/transformFormat.d.ts +2 -0
- package/dist/formats/files/transformFormat.js +6 -3
- package/dist/formats/files/transformFormat.js.map +1 -1
- package/dist/fs/findFilepath.d.ts +7 -0
- package/dist/fs/findFilepath.js +11 -1
- package/dist/fs/findFilepath.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/workflows/enqueue.js +2 -0
- package/dist/workflows/enqueue.js.map +1 -1
- package/dist/workflows/stage.js +2 -0
- package/dist/workflows/stage.js.map +1 -1
- package/dist/workflows/upload.js +2 -0
- package/dist/workflows/upload.js.map +1 -1
- package/dist/workflows/utils/syncFonts.d.ts +9 -0
- package/dist/workflows/utils/syncFonts.js +25 -0
- package/dist/workflows/utils/syncFonts.js.map +1 -0
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.16.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#2019](https://github.com/generaltranslation/gt/pull/2019) [`60d154c`](https://github.com/generaltranslation/gt/commit/60d154c851d6ae0510447b7d66ec5e12104f0d1b) Thanks [@eoinest](https://github.com/eoinest)! - Save local edits before translation by default, with a `--no-save-local` flag to opt out.
|
|
8
|
+
|
|
9
|
+
- [#2028](https://github.com/generaltranslation/gt/pull/2028) [`8066d0a`](https://github.com/generaltranslation/gt/commit/8066d0ad64d4f9c3475a935e6bbb27d9f7389b20) Thanks [@eoinest](https://github.com/eoinest)! - Stop the setup wizard at monorepo workspace roots and direct users to run it from the app they want to localize.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`9b3eb92`](https://github.com/generaltranslation/gt/commit/9b3eb92fb1a916b5f47d15f51a9f39f6c62840a9)]:
|
|
12
|
+
- generaltranslation@9.1.1
|
|
13
|
+
- @generaltranslation/python-extractor@0.2.34
|
|
14
|
+
- @generaltranslation/supported-locales@2.1.14
|
|
15
|
+
|
|
16
|
+
## 2.16.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#1994](https://github.com/generaltranslation/gt/pull/1994) [`bd961d1`](https://github.com/generaltranslation/gt/commit/bd961d1474547f7c6d470583c1b1190dce0112ca) Thanks [@logflash](https://github.com/logflash)! - Add Lottie file support and project font syncing.
|
|
21
|
+
- Support `.lottie` files as a new `LOTTIE` file format. Lottie bundles are binary (zip), so their content is carried base64-encoded end-to-end — including existing translated `.lottie` targets on `gt upload` — and skips the UTF-8 encode/decode and text merge paths (new `isBinaryFileFormat` / `BINARY_FILE_FORMATS` exports). Lottie translations are processed asynchronously and require the `gt stage` + `gt download` flow, so `gt translate` now exits with an error pointing users there when a project has Lottie files and staging isn't enabled. (As before, enabling staging makes `gt translate` download staged results rather than translating inline.)
|
|
22
|
+
- Reject `.lottie` files that use After Effects expressions (executable code): the upload fails and names every offending file, since expression-driven text can't be translated safely.
|
|
23
|
+
- Add `GT.uploadFonts` and a `fonts` config option (include/exclude globs) so project fonts are synced to the API before translating formats that need them (including during `gt stage`, so async Lottie jobs get the real fonts). Globs resolve from the project root. Font sync is idempotent and non-fatal on failure.
|
|
24
|
+
- Keep staged lock entries staged until every configured locale has downloaded, and only require `_versionId` for `gt download` when an inline GTJSON template is part of the download and config IDs aren't omitted (staged downloads resolve versions from `gt-lock.json`, file-only projects never have a `_versionId`, and `omitConfigIds` projects use the GTJSON's own content-derived version).
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`bd961d1`](https://github.com/generaltranslation/gt/commit/bd961d1474547f7c6d470583c1b1190dce0112ca)]:
|
|
29
|
+
- generaltranslation@9.1.0
|
|
30
|
+
- @generaltranslation/python-extractor@0.2.33
|
|
31
|
+
- @generaltranslation/supported-locales@2.1.13
|
|
32
|
+
|
|
3
33
|
## 2.15.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
|
@@ -15,6 +15,7 @@ import mergeYaml from "../formats/yaml/mergeYaml.js";
|
|
|
15
15
|
import { recordDownloaded, recordRemerged } from "../state/recentDownloads.js";
|
|
16
16
|
import * as fs$1 from "fs";
|
|
17
17
|
import * as path$1 from "path";
|
|
18
|
+
import { isBinaryFileFormat } from "generaltranslation/types";
|
|
18
19
|
import stringify from "fast-json-stable-stringify";
|
|
19
20
|
//#region src/api/downloadFileBatch.ts
|
|
20
21
|
/**
|
|
@@ -130,6 +131,32 @@ async function downloadFileBatch(fileTracker, files, options, forceDownload = fa
|
|
|
130
131
|
const existingEntry = entryMap.get(fileId);
|
|
131
132
|
const downloadedTranslation = existingEntry?.versionId === versionId ? existingEntry.translations[locale] : void 0;
|
|
132
133
|
const fileExists = fs$1.existsSync(outputPath);
|
|
134
|
+
if (isBinaryFileFormat(file.fileFormat)) {
|
|
135
|
+
if (!forceDownload && fileExists && downloadedTranslation) {
|
|
136
|
+
result.skipped.push(requestedFile);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
await fs$1.promises.writeFile(outputPath, Buffer.from(file.data, "base64"));
|
|
140
|
+
recordDownloaded(outputPath, {
|
|
141
|
+
branchId,
|
|
142
|
+
fileId,
|
|
143
|
+
versionId,
|
|
144
|
+
locale,
|
|
145
|
+
inputPath
|
|
146
|
+
});
|
|
147
|
+
result.successful.push(requestedFile);
|
|
148
|
+
if (fileId && versionId && locale) {
|
|
149
|
+
const entry = findOrCreateEntry(entryMap, downloadedVersions.entries, fileId, versionId);
|
|
150
|
+
entry.fileName = inputPath;
|
|
151
|
+
entry.translations[locale] = {
|
|
152
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
153
|
+
fileName: getRelative(outputPath)
|
|
154
|
+
};
|
|
155
|
+
if (entry.staged) entry.staged = !options.locales.every((l) => entry.translations[l]);
|
|
156
|
+
didUpdateDownloadedLock = true;
|
|
157
|
+
}
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
133
160
|
const isInPlaceComposite = options.options?.jsonSchema ? !!validateJsonSchema(options.options, inputPath)?.composite : false;
|
|
134
161
|
if (!forceDownload && fileExists && downloadedTranslation && !isInPlaceComposite) {
|
|
135
162
|
try {
|
|
@@ -173,11 +200,11 @@ async function downloadFileBatch(fileTracker, files, options, forceDownload = fa
|
|
|
173
200
|
if (fileId && versionId && locale) {
|
|
174
201
|
const entry = findOrCreateEntry(entryMap, downloadedVersions.entries, fileId, versionId);
|
|
175
202
|
entry.fileName = inputPath;
|
|
176
|
-
entry.staged = false;
|
|
177
203
|
entry.translations[locale] = {
|
|
178
204
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
179
205
|
fileName: getRelative(outputPath)
|
|
180
206
|
};
|
|
207
|
+
if (entry.staged) entry.staged = !options.locales.every((l) => entry.translations[l]);
|
|
181
208
|
didUpdateDownloadedLock = true;
|
|
182
209
|
}
|
|
183
210
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadFileBatch.js","names":["fs","path"],"sources":["../../src/api/downloadFileBatch.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { logger } from '../console/logger.js';\nimport { gt } from '../utils/gt.js';\nimport { Settings } from '../types/index.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { validateYamlSchema } from '../formats/yaml/utils.js';\nimport { mergeJson } from '../formats/json/mergeJson.js';\nimport { extractJson } from '../formats/json/extractJson.js';\nimport mergeYaml from '../formats/yaml/mergeYaml.js';\nimport { extractYaml } from '../formats/yaml/extractYaml.js';\nimport {\n resolveMintlifyRefs,\n shouldResolveRefs,\n} from '../utils/resolveMintlifyRefs.js';\nimport {\n readLockfile,\n writeLockfile,\n findOrCreateEntry,\n} from '../fs/config/downloadedVersions.js';\nimport { recordDownloaded, recordRemerged } from '../state/recentDownloads.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport stringify from 'fast-json-stable-stringify';\nimport type { FileStatusTracker } from '../workflows/steps/PollJobsStep.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../formats/files/transformFormat.js';\nimport { getRelative } from '../fs/findFilepath.js';\n\n/**\n * The platform withholds unapproved review-gated GTJSON components from\n * served output. Reports the delta between the source component count and\n * the served content so partially-served files don't read as fully\n * translated — for both fresh downloads and already-downloaded skips.\n */\nfunction reportWithheldGtJsonComponents(\n fileFormat: string | undefined,\n servedContent: string,\n componentCount: number | undefined,\n locale: string\n): void {\n if (fileFormat !== 'GTJSON' || componentCount == null) return;\n const received = countGtJsonEntries(servedContent);\n if (received == null) return;\n const withheld = componentCount - received;\n if (withheld > 0) {\n recordWarning(\n 'pending_review',\n '<React Elements>',\n `${withheld} component translation(s) for locale ${locale} require review and are not approved yet`\n );\n }\n}\n\n/**\n * Counts entries in downloaded GTJSON output, tolerating both the flat\n * public shape and a wrapped { type, data } shape.\n */\nfunction countGtJsonEntries(content: string): number | undefined {\n try {\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n const entries =\n parsed.type === 'GTJSON' &&\n parsed.data &&\n typeof parsed.data === 'object' &&\n !Array.isArray(parsed.data)\n ? parsed.data\n : parsed;\n return Object.keys(entries).length;\n } catch {\n return undefined;\n }\n}\n\nfunction sortJsonString(data: string): string {\n const sortedData = stringify(JSON.parse(data));\n return JSON.stringify(JSON.parse(sortedData), null, 2);\n}\n\n/**\n * Merges translated content with the current source file for schema-based formats.\n */\nfunction mergeWithSource(\n translatedContent: string,\n locale: string,\n inputPath: string,\n options: Settings\n): string {\n if (shouldSkipSourceFormatMerge(inputPath, options)) {\n return translatedContent;\n }\n if (!options.options) return translatedContent;\n\n const jsonSchema = options.options.jsonSchema\n ? validateJsonSchema(options.options, inputPath)\n : null;\n const yamlSchema =\n !jsonSchema && options.options.yamlSchema\n ? validateYamlSchema(options.options, inputPath)\n : null;\n\n if (!jsonSchema && !yamlSchema) return translatedContent;\n\n const sourceContent = fs.readFileSync(inputPath, 'utf8');\n if (!sourceContent) return translatedContent;\n\n if (jsonSchema) {\n // Resolve $ref before merging if configured\n let resolvedSourceContent = sourceContent;\n if (shouldResolveRefs(inputPath, options.options)) {\n try {\n const json = JSON.parse(sourceContent);\n const { resolved } = resolveMintlifyRefs(json, inputPath);\n resolvedSourceContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n return mergeJson(\n resolvedSourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale,\n options.locales\n )[0];\n } else {\n return mergeYaml(\n sourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale\n )[0];\n }\n}\n\n/**\n * Determines whether a source file should be skipped for schema re-merging.\n * @param inputPath - The path of the source file\n * @param options - The settings for the project\n * @returns True if the source file should be skipped for schema re-merging, false otherwise\n */\nfunction shouldSkipSourceFormatMerge(\n inputPath: string,\n options: Settings\n): boolean {\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(options, fileType)) continue;\n\n const transformedSourcePaths = options.files.resolvedPaths[fileType] || [];\n if (\n transformedSourcePaths.some(\n (sourcePath) => getRelative(sourcePath) === inputPath\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport type BatchedFiles = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n outputPath: string;\n inputPath: string;\n}[];\n\nexport type DownloadFileBatchResult = {\n successful: BatchedFiles;\n failed: BatchedFiles;\n skipped: BatchedFiles;\n};\n/**\n * Downloads multiple translation files in a single batch request\n * @param files - Array of files to download with their output paths\n * @param maxRetries - Maximum number of retry attempts\n * @param retryDelay - Delay between retries in milliseconds\n * @returns Object containing successful and failed file IDs\n */\nexport async function downloadFileBatch(\n fileTracker: FileStatusTracker,\n files: BatchedFiles,\n options: Settings,\n forceDownload: boolean = false\n): Promise<DownloadFileBatchResult> {\n // Local record of what version was last downloaded for each fileName:locale\n const {\n data: downloadedVersions,\n entryMap,\n originalV1,\n } = readLockfile(options);\n let didUpdateDownloadedLock = false;\n\n // Create a map of requested file keys to the file object\n const requestedFileMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file,\n ])\n );\n const result: DownloadFileBatchResult = {\n successful: [],\n failed: [],\n skipped: [],\n };\n\n // Create a map of translationId to outputPath for easier lookup\n const outputPathMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file.outputPath,\n ])\n );\n\n try {\n // Download the files\n const responseData = await gt.downloadFileBatch(\n files.map((file) => ({\n fileId: file.fileId,\n branchId: file.branchId,\n versionId: file.versionId,\n locale: file.locale,\n }))\n );\n const downloadedFiles = responseData.files || [];\n\n // Process each file in the response\n for (const file of downloadedFiles) {\n const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;\n const requestedFile = requestedFileMap.get(fileKey);\n if (!requestedFile) {\n continue;\n }\n try {\n const outputPath = outputPathMap.get(fileKey);\n const fileProperties = fileTracker.completed.get(fileKey);\n\n if (!outputPath || !fileProperties) {\n logger.warn(`No input/output path found for file: ${fileKey}`);\n recordWarning(\n 'failed_download',\n fileKey,\n 'No input/output path found'\n );\n result.failed.push(requestedFile);\n continue;\n }\n\n const {\n fileId,\n versionId,\n locale,\n branchId,\n fileName: inputPath,\n } = fileProperties;\n\n // Ensure the directory exists\n const dir = path.dirname(outputPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n // If a local translation already exists for the same source version, skip overwrite\n const existingEntry = entryMap.get(fileId);\n const downloadedTranslation =\n existingEntry?.versionId === versionId\n ? existingEntry.translations[locale]\n : undefined;\n const fileExists = fs.existsSync(outputPath);\n\n // Composite schema files merge translations into the source file itself,\n // so outputPath always exists and the lock can't tell whether derived\n // split outputs (e.g. {locale}/docs.json) are still on disk. Always\n // merge fresh API data so derived files are regenerated every run;\n // local edits to translated output are preserved via `gt save-local`.\n const isInPlaceComposite = options.options?.jsonSchema\n ? !!validateJsonSchema(options.options, inputPath)?.composite\n : false;\n\n if (\n !forceDownload &&\n fileExists &&\n downloadedTranslation &&\n !isInPlaceComposite\n ) {\n // For schema-based files, re-merge with current source in case\n // non-translatable fields changed (skip the API download, not the merge)\n try {\n let existingContent = fs.readFileSync(outputPath, 'utf8');\n // Resolve $ref before extraction if configured\n if (shouldResolveRefs(outputPath, options.options)) {\n try {\n const json = JSON.parse(existingContent);\n const { resolved } = resolveMintlifyRefs(json, outputPath);\n existingContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n const jsonExtracted = options.options?.jsonSchema\n ? extractJson(\n existingContent,\n inputPath,\n options.options,\n locale,\n options.defaultLocale\n )\n : null;\n const extracted =\n jsonExtracted ??\n (options.options?.yamlSchema\n ? extractYaml(existingContent, inputPath, options.options)\n : null);\n if (extracted) {\n const remerged = mergeWithSource(\n extracted,\n locale,\n inputPath,\n options\n );\n let remergedData = remerged;\n if (outputPath.endsWith('.json')) {\n try {\n remergedData = sortJsonString(remergedData);\n } catch {\n // Fall through with unsorted content\n }\n }\n if (remergedData !== existingContent) {\n await fs.promises.writeFile(outputPath, remergedData);\n }\n // Track for postprocessing (e.g. openapi path localization)\n // even when the API download was skipped\n recordRemerged(outputPath);\n }\n } catch (error) {\n // If re-merge fails, still count as skipped — not worth failing\n // the download, but surface it so missing output is diagnosable\n logger.warn(\n `Failed to re-merge existing translation for ${outputPath} (${locale}): ${error}`\n );\n }\n // Even when the local copy is current, the served content may\n // still be missing review-withheld components — keep reporting\n reportWithheldGtJsonComponents(\n file.fileFormat,\n file.data,\n fileProperties.componentCount,\n locale\n );\n result.skipped.push(requestedFile);\n continue;\n }\n let data = mergeWithSource(file.data, locale, inputPath, options);\n\n // Stable sort JSON keys for deterministic output\n if (file.fileFormat === 'GTJSON' || outputPath.endsWith('.json')) {\n try {\n data = sortJsonString(data);\n } catch (error) {\n logger.warn(`Failed to sort JSON file: ${file.id}: ` + error);\n }\n }\n\n // Write the file to disk\n await fs.promises.writeFile(outputPath, data);\n // Track as downloaded with metadata for downstream postprocessing\n recordDownloaded(outputPath, {\n branchId,\n fileId,\n versionId,\n locale,\n inputPath,\n });\n\n reportWithheldGtJsonComponents(\n file.fileFormat,\n data,\n fileProperties.componentCount,\n locale\n );\n\n result.successful.push(requestedFile);\n if (fileId && versionId && locale) {\n const entry = findOrCreateEntry(\n entryMap,\n downloadedVersions.entries,\n fileId,\n versionId\n );\n entry.fileName = inputPath;\n entry.staged = false;\n entry.translations[locale] = {\n updatedAt: new Date().toISOString(),\n fileName: getRelative(outputPath),\n };\n didUpdateDownloadedLock = true;\n }\n } catch (error) {\n logger.error(`Error saving file ${fileKey}: ` + error);\n recordWarning(\n 'failed_download',\n fileKey,\n `Error saving file: ${error}`\n );\n result.failed.push(requestedFile);\n }\n }\n\n // Add any files that weren't in the response to the failed list\n const downloadedFileKeys = new Set(\n downloadedFiles.map(\n (file) =>\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`\n )\n );\n for (const [fileKey, requestedFile] of requestedFileMap.entries()) {\n if (!downloadedFileKeys.has(fileKey)) {\n result.failed.push(requestedFile);\n }\n }\n\n // Persist any updates to the downloaded map at the end of a successful cycle\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n didUpdateDownloadedLock = false;\n }\n return result;\n } catch (error) {\n logger.error(\n `An unexpected error occurred while downloading files: ` + error\n );\n }\n\n // Mark all files as failed if we get here\n result.failed = [...requestedFileMap.values()];\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n }\n return result;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"downloadFileBatch.js","names":["fs","path"],"sources":["../../src/api/downloadFileBatch.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { isBinaryFileFormat } from 'generaltranslation/types';\nimport { logger } from '../console/logger.js';\nimport { gt } from '../utils/gt.js';\nimport { Settings } from '../types/index.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { validateYamlSchema } from '../formats/yaml/utils.js';\nimport { mergeJson } from '../formats/json/mergeJson.js';\nimport { extractJson } from '../formats/json/extractJson.js';\nimport mergeYaml from '../formats/yaml/mergeYaml.js';\nimport { extractYaml } from '../formats/yaml/extractYaml.js';\nimport {\n resolveMintlifyRefs,\n shouldResolveRefs,\n} from '../utils/resolveMintlifyRefs.js';\nimport {\n readLockfile,\n writeLockfile,\n findOrCreateEntry,\n} from '../fs/config/downloadedVersions.js';\nimport { recordDownloaded, recordRemerged } from '../state/recentDownloads.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport stringify from 'fast-json-stable-stringify';\nimport type { FileStatusTracker } from '../workflows/steps/PollJobsStep.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../formats/files/transformFormat.js';\nimport { getRelative } from '../fs/findFilepath.js';\n\n/**\n * The platform withholds unapproved review-gated GTJSON components from\n * served output. Reports the delta between the source component count and\n * the served content so partially-served files don't read as fully\n * translated — for both fresh downloads and already-downloaded skips.\n */\nfunction reportWithheldGtJsonComponents(\n fileFormat: string | undefined,\n servedContent: string,\n componentCount: number | undefined,\n locale: string\n): void {\n if (fileFormat !== 'GTJSON' || componentCount == null) return;\n const received = countGtJsonEntries(servedContent);\n if (received == null) return;\n const withheld = componentCount - received;\n if (withheld > 0) {\n recordWarning(\n 'pending_review',\n '<React Elements>',\n `${withheld} component translation(s) for locale ${locale} require review and are not approved yet`\n );\n }\n}\n\n/**\n * Counts entries in downloaded GTJSON output, tolerating both the flat\n * public shape and a wrapped { type, data } shape.\n */\nfunction countGtJsonEntries(content: string): number | undefined {\n try {\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n const entries =\n parsed.type === 'GTJSON' &&\n parsed.data &&\n typeof parsed.data === 'object' &&\n !Array.isArray(parsed.data)\n ? parsed.data\n : parsed;\n return Object.keys(entries).length;\n } catch {\n return undefined;\n }\n}\n\nfunction sortJsonString(data: string): string {\n const sortedData = stringify(JSON.parse(data));\n return JSON.stringify(JSON.parse(sortedData), null, 2);\n}\n\n/**\n * Merges translated content with the current source file for schema-based formats.\n */\nfunction mergeWithSource(\n translatedContent: string,\n locale: string,\n inputPath: string,\n options: Settings\n): string {\n if (shouldSkipSourceFormatMerge(inputPath, options)) {\n return translatedContent;\n }\n if (!options.options) return translatedContent;\n\n const jsonSchema = options.options.jsonSchema\n ? validateJsonSchema(options.options, inputPath)\n : null;\n const yamlSchema =\n !jsonSchema && options.options.yamlSchema\n ? validateYamlSchema(options.options, inputPath)\n : null;\n\n if (!jsonSchema && !yamlSchema) return translatedContent;\n\n const sourceContent = fs.readFileSync(inputPath, 'utf8');\n if (!sourceContent) return translatedContent;\n\n if (jsonSchema) {\n // Resolve $ref before merging if configured\n let resolvedSourceContent = sourceContent;\n if (shouldResolveRefs(inputPath, options.options)) {\n try {\n const json = JSON.parse(sourceContent);\n const { resolved } = resolveMintlifyRefs(json, inputPath);\n resolvedSourceContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n return mergeJson(\n resolvedSourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale,\n options.locales\n )[0];\n } else {\n return mergeYaml(\n sourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale\n )[0];\n }\n}\n\n/**\n * Determines whether a source file should be skipped for schema re-merging.\n * @param inputPath - The path of the source file\n * @param options - The settings for the project\n * @returns True if the source file should be skipped for schema re-merging, false otherwise\n */\nfunction shouldSkipSourceFormatMerge(\n inputPath: string,\n options: Settings\n): boolean {\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(options, fileType)) continue;\n\n const transformedSourcePaths = options.files.resolvedPaths[fileType] || [];\n if (\n transformedSourcePaths.some(\n (sourcePath) => getRelative(sourcePath) === inputPath\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport type BatchedFiles = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n outputPath: string;\n inputPath: string;\n}[];\n\nexport type DownloadFileBatchResult = {\n successful: BatchedFiles;\n failed: BatchedFiles;\n skipped: BatchedFiles;\n};\n/**\n * Downloads multiple translation files in a single batch request\n * @param files - Array of files to download with their output paths\n * @param maxRetries - Maximum number of retry attempts\n * @param retryDelay - Delay between retries in milliseconds\n * @returns Object containing successful and failed file IDs\n */\nexport async function downloadFileBatch(\n fileTracker: FileStatusTracker,\n files: BatchedFiles,\n options: Settings,\n forceDownload: boolean = false\n): Promise<DownloadFileBatchResult> {\n // Local record of what version was last downloaded for each fileName:locale\n const {\n data: downloadedVersions,\n entryMap,\n originalV1,\n } = readLockfile(options);\n let didUpdateDownloadedLock = false;\n\n // Create a map of requested file keys to the file object\n const requestedFileMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file,\n ])\n );\n const result: DownloadFileBatchResult = {\n successful: [],\n failed: [],\n skipped: [],\n };\n\n // Create a map of translationId to outputPath for easier lookup\n const outputPathMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file.outputPath,\n ])\n );\n\n try {\n // Download the files\n const responseData = await gt.downloadFileBatch(\n files.map((file) => ({\n fileId: file.fileId,\n branchId: file.branchId,\n versionId: file.versionId,\n locale: file.locale,\n }))\n );\n const downloadedFiles = responseData.files || [];\n\n // Process each file in the response\n for (const file of downloadedFiles) {\n const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;\n const requestedFile = requestedFileMap.get(fileKey);\n if (!requestedFile) {\n continue;\n }\n try {\n const outputPath = outputPathMap.get(fileKey);\n const fileProperties = fileTracker.completed.get(fileKey);\n\n if (!outputPath || !fileProperties) {\n logger.warn(`No input/output path found for file: ${fileKey}`);\n recordWarning(\n 'failed_download',\n fileKey,\n 'No input/output path found'\n );\n result.failed.push(requestedFile);\n continue;\n }\n\n const {\n fileId,\n versionId,\n locale,\n branchId,\n fileName: inputPath,\n } = fileProperties;\n\n // Ensure the directory exists\n const dir = path.dirname(outputPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n // If a local translation already exists for the same source version, skip overwrite\n const existingEntry = entryMap.get(fileId);\n const downloadedTranslation =\n existingEntry?.versionId === versionId\n ? existingEntry.translations[locale]\n : undefined;\n const fileExists = fs.existsSync(outputPath);\n\n // Binary formats (e.g. LOTTIE zip bundles) carry base64 content that must\n // not go through the text merge/sort path — decode straight to bytes and\n // write. Skip only when an unchanged local translation already exists.\n if (isBinaryFileFormat(file.fileFormat)) {\n if (!forceDownload && fileExists && downloadedTranslation) {\n result.skipped.push(requestedFile);\n continue;\n }\n await fs.promises.writeFile(\n outputPath,\n Buffer.from(file.data, 'base64')\n );\n recordDownloaded(outputPath, {\n branchId,\n fileId,\n versionId,\n locale,\n inputPath,\n });\n result.successful.push(requestedFile);\n if (fileId && versionId && locale) {\n const entry = findOrCreateEntry(\n entryMap,\n downloadedVersions.entries,\n fileId,\n versionId\n );\n entry.fileName = inputPath;\n entry.translations[locale] = {\n updatedAt: new Date().toISOString(),\n // Store a relative path (matches the text path) so gt-lock.json\n // stays portable and the skip-on-re-download check matches.\n fileName: getRelative(outputPath),\n };\n // A staged entry tracks the whole file, not one locale. Keep it\n // staged until every configured locale has downloaded, so a partial\n // batch (locales still translating) doesn't abandon the rest.\n if (entry.staged) {\n entry.staged = !options.locales.every(\n (l) => entry.translations[l]\n );\n }\n didUpdateDownloadedLock = true;\n }\n continue;\n }\n\n // Composite schema files merge translations into the source file itself,\n // so outputPath always exists and the lock can't tell whether derived\n // split outputs (e.g. {locale}/docs.json) are still on disk. Always\n // merge fresh API data so derived files are regenerated every run;\n // local edits to translated output are preserved via `gt save-local`.\n const isInPlaceComposite = options.options?.jsonSchema\n ? !!validateJsonSchema(options.options, inputPath)?.composite\n : false;\n\n if (\n !forceDownload &&\n fileExists &&\n downloadedTranslation &&\n !isInPlaceComposite\n ) {\n // For schema-based files, re-merge with current source in case\n // non-translatable fields changed (skip the API download, not the merge)\n try {\n let existingContent = fs.readFileSync(outputPath, 'utf8');\n // Resolve $ref before extraction if configured\n if (shouldResolveRefs(outputPath, options.options)) {\n try {\n const json = JSON.parse(existingContent);\n const { resolved } = resolveMintlifyRefs(json, outputPath);\n existingContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n const jsonExtracted = options.options?.jsonSchema\n ? extractJson(\n existingContent,\n inputPath,\n options.options,\n locale,\n options.defaultLocale\n )\n : null;\n const extracted =\n jsonExtracted ??\n (options.options?.yamlSchema\n ? extractYaml(existingContent, inputPath, options.options)\n : null);\n if (extracted) {\n const remerged = mergeWithSource(\n extracted,\n locale,\n inputPath,\n options\n );\n let remergedData = remerged;\n if (outputPath.endsWith('.json')) {\n try {\n remergedData = sortJsonString(remergedData);\n } catch {\n // Fall through with unsorted content\n }\n }\n if (remergedData !== existingContent) {\n await fs.promises.writeFile(outputPath, remergedData);\n }\n // Track for postprocessing (e.g. openapi path localization)\n // even when the API download was skipped\n recordRemerged(outputPath);\n }\n } catch (error) {\n // If re-merge fails, still count as skipped — not worth failing\n // the download, but surface it so missing output is diagnosable\n logger.warn(\n `Failed to re-merge existing translation for ${outputPath} (${locale}): ${error}`\n );\n }\n // Even when the local copy is current, the served content may\n // still be missing review-withheld components — keep reporting\n reportWithheldGtJsonComponents(\n file.fileFormat,\n file.data,\n fileProperties.componentCount,\n locale\n );\n result.skipped.push(requestedFile);\n continue;\n }\n let data = mergeWithSource(file.data, locale, inputPath, options);\n\n // Stable sort JSON keys for deterministic output\n if (file.fileFormat === 'GTJSON' || outputPath.endsWith('.json')) {\n try {\n data = sortJsonString(data);\n } catch (error) {\n logger.warn(`Failed to sort JSON file: ${file.id}: ` + error);\n }\n }\n\n // Write the file to disk\n await fs.promises.writeFile(outputPath, data);\n // Track as downloaded with metadata for downstream postprocessing\n recordDownloaded(outputPath, {\n branchId,\n fileId,\n versionId,\n locale,\n inputPath,\n });\n\n reportWithheldGtJsonComponents(\n file.fileFormat,\n data,\n fileProperties.componentCount,\n locale\n );\n\n result.successful.push(requestedFile);\n if (fileId && versionId && locale) {\n const entry = findOrCreateEntry(\n entryMap,\n downloadedVersions.entries,\n fileId,\n versionId\n );\n entry.fileName = inputPath;\n entry.translations[locale] = {\n updatedAt: new Date().toISOString(),\n fileName: getRelative(outputPath),\n };\n // A staged entry tracks the whole file, not one locale. Keep it\n // staged until every configured locale has downloaded, so a partial\n // batch (locales still translating) doesn't abandon the rest.\n if (entry.staged) {\n entry.staged = !options.locales.every((l) => entry.translations[l]);\n }\n didUpdateDownloadedLock = true;\n }\n } catch (error) {\n logger.error(`Error saving file ${fileKey}: ` + error);\n recordWarning(\n 'failed_download',\n fileKey,\n `Error saving file: ${error}`\n );\n result.failed.push(requestedFile);\n }\n }\n\n // Add any files that weren't in the response to the failed list\n const downloadedFileKeys = new Set(\n downloadedFiles.map(\n (file) =>\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`\n )\n );\n for (const [fileKey, requestedFile] of requestedFileMap.entries()) {\n if (!downloadedFileKeys.has(fileKey)) {\n result.failed.push(requestedFile);\n }\n }\n\n // Persist any updates to the downloaded map at the end of a successful cycle\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n didUpdateDownloadedLock = false;\n }\n return result;\n } catch (error) {\n logger.error(\n `An unexpected error occurred while downloading files: ` + error\n );\n }\n\n // Mark all files as failed if we get here\n result.failed = [...requestedFileMap.values()];\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n }\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAS,+BACP,YACA,eACA,gBACA,QACM;AACN,KAAI,eAAe,YAAY,kBAAkB,KAAM;CACvD,MAAM,WAAW,mBAAmB,cAAc;AAClD,KAAI,YAAY,KAAM;CACtB,MAAM,WAAW,iBAAiB;AAClC,KAAI,WAAW,EACb,eACE,kBACA,oBACA,GAAG,SAAS,uCAAuC,OAAO,0CAC3D;;;;;;AAQL,SAAS,mBAAmB,SAAqC;AAC/D,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAChE;EAEF,MAAM,UACJ,OAAO,SAAS,YAChB,OAAO,QACP,OAAO,OAAO,SAAS,YACvB,CAAC,MAAM,QAAQ,OAAO,KAAK,GACvB,OAAO,OACP;AACN,SAAO,OAAO,KAAK,QAAQ,CAAC;SACtB;AACN;;;AAIJ,SAAS,eAAe,MAAsB;CAC5C,MAAM,aAAa,UAAU,KAAK,MAAM,KAAK,CAAC;AAC9C,QAAO,KAAK,UAAU,KAAK,MAAM,WAAW,EAAE,MAAM,EAAE;;;;;AAMxD,SAAS,gBACP,mBACA,QACA,WACA,SACQ;AACR,KAAI,4BAA4B,WAAW,QAAQ,CACjD,QAAO;AAET,KAAI,CAAC,QAAQ,QAAS,QAAO;CAE7B,MAAM,aAAa,QAAQ,QAAQ,aAC/B,mBAAmB,QAAQ,SAAS,UAAU,GAC9C;CACJ,MAAM,aACJ,CAAC,cAAc,QAAQ,QAAQ,aAC3B,mBAAmB,QAAQ,SAAS,UAAU,GAC9C;AAEN,KAAI,CAAC,cAAc,CAAC,WAAY,QAAO;CAEvC,MAAM,gBAAgBA,KAAG,aAAa,WAAW,OAAO;AACxD,KAAI,CAAC,cAAe,QAAO;AAE3B,KAAI,YAAY;EAEd,IAAI,wBAAwB;AAC5B,MAAI,kBAAkB,WAAW,QAAQ,QAAQ,CAC/C,KAAI;GAEF,MAAM,EAAE,aAAa,oBADR,KAAK,MAAM,cACqB,EAAE,UAAU;AACzD,2BAAwB,KAAK,UAAU,UAAU,MAAM,EAAE;UACnD;AAIV,SAAO,UACL,uBACA,WACA,QAAQ,SACR,CAAC;GAAE;GAAmB,cAAc;GAAQ,CAAC,EAC7C,QAAQ,eACR,QAAQ,QACT,CAAC;OAEF,QAAO,UACL,eACA,WACA,QAAQ,SACR,CAAC;EAAE;EAAmB,cAAc;EAAQ,CAAC,EAC7C,QAAQ,cACT,CAAC;;;;;;;;AAUN,SAAS,4BACP,WACA,SACS;AACT,MAAK,MAAM,YAAY,2BAA2B;AAChD,MAAI,CAAC,yCAAyC,SAAS,SAAS,CAAE;AAGlE,OAD+B,QAAQ,MAAM,cAAc,aAAa,EAAE,EAEjD,MACpB,eAAe,YAAY,WAAW,KAAK,UAC7C,CAED,QAAO;;AAIX,QAAO;;;;;;;;;AAwBT,eAAsB,kBACpB,aACA,OACA,SACA,gBAAyB,OACS;CAElC,MAAM,EACJ,MAAM,oBACN,UACA,eACE,aAAa,QAAQ;CACzB,IAAI,0BAA0B;CAG9B,MAAM,mBAAmB,IAAI,IAC3B,MAAM,KAAK,SAAS,CAClB,GAAG,KAAK,SAAS,GAAG,KAAK,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,UAC1D,KACD,CAAC,CACH;CACD,MAAM,SAAkC;EACtC,YAAY,EAAE;EACd,QAAQ,EAAE;EACV,SAAS,EAAE;EACZ;CAGD,MAAM,gBAAgB,IAAI,IACxB,MAAM,KAAK,SAAS,CAClB,GAAG,KAAK,SAAS,GAAG,KAAK,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,UAC1D,KAAK,WACN,CAAC,CACH;AAED,KAAI;EAUF,MAAM,mBAAkB,MARG,GAAG,kBAC5B,MAAM,KAAK,UAAU;GACnB,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,QAAQ,KAAK;GACd,EAAE,CACJ,EACoC,SAAS,EAAE;AAGhD,OAAK,MAAM,QAAQ,iBAAiB;GAClC,MAAM,UAAU,GAAG,KAAK,SAAS,GAAG,KAAK,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK;GAC1E,MAAM,gBAAgB,iBAAiB,IAAI,QAAQ;AACnD,OAAI,CAAC,cACH;AAEF,OAAI;IACF,MAAM,aAAa,cAAc,IAAI,QAAQ;IAC7C,MAAM,iBAAiB,YAAY,UAAU,IAAI,QAAQ;AAEzD,QAAI,CAAC,cAAc,CAAC,gBAAgB;AAClC,YAAO,KAAK,wCAAwC,UAAU;AAC9D,mBACE,mBACA,SACA,6BACD;AACD,YAAO,OAAO,KAAK,cAAc;AACjC;;IAGF,MAAM,EACJ,QACA,WACA,QACA,UACA,UAAU,cACR;IAGJ,MAAM,MAAMC,OAAK,QAAQ,WAAW;AACpC,QAAI,CAACD,KAAG,WAAW,IAAI,CACrB,MAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;IAGxC,MAAM,gBAAgB,SAAS,IAAI,OAAO;IAC1C,MAAM,wBACJ,eAAe,cAAc,YACzB,cAAc,aAAa,UAC3B,KAAA;IACN,MAAM,aAAaA,KAAG,WAAW,WAAW;AAK5C,QAAI,mBAAmB,KAAK,WAAW,EAAE;AACvC,SAAI,CAAC,iBAAiB,cAAc,uBAAuB;AACzD,aAAO,QAAQ,KAAK,cAAc;AAClC;;AAEF,WAAMA,KAAG,SAAS,UAChB,YACA,OAAO,KAAK,KAAK,MAAM,SAAS,CACjC;AACD,sBAAiB,YAAY;MAC3B;MACA;MACA;MACA;MACA;MACD,CAAC;AACF,YAAO,WAAW,KAAK,cAAc;AACrC,SAAI,UAAU,aAAa,QAAQ;MACjC,MAAM,QAAQ,kBACZ,UACA,mBAAmB,SACnB,QACA,UACD;AACD,YAAM,WAAW;AACjB,YAAM,aAAa,UAAU;OAC3B,4BAAW,IAAI,MAAM,EAAC,aAAa;OAGnC,UAAU,YAAY,WAAW;OAClC;AAID,UAAI,MAAM,OACR,OAAM,SAAS,CAAC,QAAQ,QAAQ,OAC7B,MAAM,MAAM,aAAa,GAC3B;AAEH,gCAA0B;;AAE5B;;IAQF,MAAM,qBAAqB,QAAQ,SAAS,aACxC,CAAC,CAAC,mBAAmB,QAAQ,SAAS,UAAU,EAAE,YAClD;AAEJ,QACE,CAAC,iBACD,cACA,yBACA,CAAC,oBACD;AAGA,SAAI;MACF,IAAI,kBAAkBA,KAAG,aAAa,YAAY,OAAO;AAEzD,UAAI,kBAAkB,YAAY,QAAQ,QAAQ,CAChD,KAAI;OAEF,MAAM,EAAE,aAAa,oBADR,KAAK,MAAM,gBACqB,EAAE,WAAW;AAC1D,yBAAkB,KAAK,UAAU,UAAU,MAAM,EAAE;cAC7C;MAaV,MAAM,aATgB,QAAQ,SAAS,aACnC,YACE,iBACA,WACA,QAAQ,SACR,QACA,QAAQ,cACT,GACD,UAGD,QAAQ,SAAS,aACd,YAAY,iBAAiB,WAAW,QAAQ,QAAQ,GACxD;AACN,UAAI,WAAW;OAOb,IAAI,eANa,gBACf,WACA,QACA,WACA,QAEyB;AAC3B,WAAI,WAAW,SAAS,QAAQ,CAC9B,KAAI;AACF,uBAAe,eAAe,aAAa;eACrC;AAIV,WAAI,iBAAiB,gBACnB,OAAMA,KAAG,SAAS,UAAU,YAAY,aAAa;AAIvD,sBAAe,WAAW;;cAErB,OAAO;AAGd,aAAO,KACL,+CAA+C,WAAW,IAAI,OAAO,KAAK,QAC3E;;AAIH,oCACE,KAAK,YACL,KAAK,MACL,eAAe,gBACf,OACD;AACD,YAAO,QAAQ,KAAK,cAAc;AAClC;;IAEF,IAAI,OAAO,gBAAgB,KAAK,MAAM,QAAQ,WAAW,QAAQ;AAGjE,QAAI,KAAK,eAAe,YAAY,WAAW,SAAS,QAAQ,CAC9D,KAAI;AACF,YAAO,eAAe,KAAK;aACpB,OAAO;AACd,YAAO,KAAK,6BAA6B,KAAK,GAAG,MAAM,MAAM;;AAKjE,UAAMA,KAAG,SAAS,UAAU,YAAY,KAAK;AAE7C,qBAAiB,YAAY;KAC3B;KACA;KACA;KACA;KACA;KACD,CAAC;AAEF,mCACE,KAAK,YACL,MACA,eAAe,gBACf,OACD;AAED,WAAO,WAAW,KAAK,cAAc;AACrC,QAAI,UAAU,aAAa,QAAQ;KACjC,MAAM,QAAQ,kBACZ,UACA,mBAAmB,SACnB,QACA,UACD;AACD,WAAM,WAAW;AACjB,WAAM,aAAa,UAAU;MAC3B,4BAAW,IAAI,MAAM,EAAC,aAAa;MACnC,UAAU,YAAY,WAAW;MAClC;AAID,SAAI,MAAM,OACR,OAAM,SAAS,CAAC,QAAQ,QAAQ,OAAO,MAAM,MAAM,aAAa,GAAG;AAErE,+BAA0B;;YAErB,OAAO;AACd,WAAO,MAAM,qBAAqB,QAAQ,MAAM,MAAM;AACtD,kBACE,mBACA,SACA,sBAAsB,QACvB;AACD,WAAO,OAAO,KAAK,cAAc;;;EAKrC,MAAM,qBAAqB,IAAI,IAC7B,gBAAgB,KACb,SACC,GAAG,KAAK,SAAS,GAAG,KAAK,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,SAC7D,CACF;AACD,OAAK,MAAM,CAAC,SAAS,kBAAkB,iBAAiB,SAAS,CAC/D,KAAI,CAAC,mBAAmB,IAAI,QAAQ,CAClC,QAAO,OAAO,KAAK,cAAc;AAKrC,MAAI,yBAAyB;AAC3B,iBAAc,oBAAoB,WAAW;AAC7C,6BAA0B;;AAE5B,SAAO;UACA,OAAO;AACd,SAAO,MACL,2DAA2D,MAC5D;;AAIH,QAAO,SAAS,CAAC,GAAG,iBAAiB,QAAQ,CAAC;AAC9C,KAAI,wBACF,eAAc,oBAAoB,WAAW;AAE/C,QAAO"}
|
package/dist/cli/base.js
CHANGED
|
@@ -4,6 +4,7 @@ import { DEFAULT_TRANSLATIONS_DIR, DEFAULT_VITE_TRANSLATIONS_DIR } from "../util
|
|
|
4
4
|
import { parseGlobPatterns } from "../console/promptParsing.js";
|
|
5
5
|
import { displayHeader, exitSync, logErrorAndExit, promptConfirm, promptGlobPatterns, promptMultiSelect, promptSelect, promptText } from "../console/logging.js";
|
|
6
6
|
import findFilepath from "../fs/findFilepath.js";
|
|
7
|
+
import { lottieTranslateError } from "../console/index.js";
|
|
7
8
|
import { INLINE_LIBRARIES } from "../types/libraries.js";
|
|
8
9
|
import { FILE_EXT_TO_EXT_LABEL } from "../formats/files/supportedFiles.js";
|
|
9
10
|
import { generateSettings } from "../config/generateSettings.js";
|
|
@@ -37,6 +38,7 @@ import { warnReactPackageCompatibility } from "../utils/reactPackageCompatibilit
|
|
|
37
38
|
import chalk from "chalk";
|
|
38
39
|
import path from "node:path";
|
|
39
40
|
import fs from "node:fs";
|
|
41
|
+
import { createDiagnosticMessage } from "generaltranslation/internal";
|
|
40
42
|
//#region src/cli/base.ts
|
|
41
43
|
const ID_COMPATIBILITY_WARNING_COMMANDS = new Set([
|
|
42
44
|
"download",
|
|
@@ -47,6 +49,17 @@ const ID_COMPATIBILITY_WARNING_COMMANDS = new Set([
|
|
|
47
49
|
"translate",
|
|
48
50
|
"validate"
|
|
49
51
|
]);
|
|
52
|
+
const workspaceRootSetupError = createDiagnosticMessage({
|
|
53
|
+
source: "gt",
|
|
54
|
+
severity: "Error",
|
|
55
|
+
whatHappened: "The setup wizard cannot run from a monorepo workspace root",
|
|
56
|
+
why: "GT must be configured in the specific app you want to localize",
|
|
57
|
+
fix: "Change to that app's directory and rerun `npx gt@latest`"
|
|
58
|
+
});
|
|
59
|
+
async function exitIfWorkspaceRoot() {
|
|
60
|
+
const packageJson = await searchForPackageJson();
|
|
61
|
+
if (fs.existsSync(path.join(process.cwd(), "pnpm-workspace.yaml")) || packageJson?.workspaces) logErrorAndExit(workspaceRootSetupError);
|
|
62
|
+
}
|
|
50
63
|
var BaseCLI = class {
|
|
51
64
|
library;
|
|
52
65
|
additionalModules;
|
|
@@ -209,6 +222,7 @@ var BaseCLI = class {
|
|
|
209
222
|
const settings = await generateSettings(initOptions, void 0, { requireConfig: true });
|
|
210
223
|
await processSharedStaticAssets(settings);
|
|
211
224
|
if (!settings.stageTranslations) {
|
|
225
|
+
if (settings.files?.resolvedPaths.lottie?.length) return logErrorAndExit(lottieTranslateError);
|
|
212
226
|
const results = await handleStage(initOptions, settings, this.library, false);
|
|
213
227
|
if (results) await handleTranslate(initOptions, settings, results.fileVersionData, results.jobData, results.branchData, results.publishMap);
|
|
214
228
|
} else await handleDownload(initOptions, settings, this.library);
|
|
@@ -260,6 +274,7 @@ var BaseCLI = class {
|
|
|
260
274
|
}
|
|
261
275
|
setupInitCommand() {
|
|
262
276
|
this.program.command("init").description("Run the setup wizard to configure your project for General Translation").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}'").option("-c, --config <path>", "Filepath to config file, by default gt.config.json", findFilepath(["gt.config.json"])).action(async (options) => {
|
|
277
|
+
await exitIfWorkspaceRoot();
|
|
263
278
|
const settings = await generateSettings(options);
|
|
264
279
|
displayHeader("Running setup wizard...");
|
|
265
280
|
const framework = await detectFramework();
|
|
@@ -305,6 +320,7 @@ var BaseCLI = class {
|
|
|
305
320
|
}
|
|
306
321
|
setupConfigureCommand() {
|
|
307
322
|
this.program.command("configure").description("Configure your project for General Translation. This will create a gt.config.json file in your codebase.").action(async () => {
|
|
323
|
+
await exitIfWorkspaceRoot();
|
|
308
324
|
displayHeader("Configuring project...");
|
|
309
325
|
logger.info("Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.");
|
|
310
326
|
const framework = await detectFramework();
|
package/dist/cli/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command } from 'commander';\nimport {\n DEFAULT_TRANSLATIONS_DIR,\n DEFAULT_VITE_TRANSLATIONS_DIR,\n} from '../utils/constants.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport {\n displayHeader,\n promptText,\n logErrorAndExit,\n exitSync,\n promptConfirm,\n promptMultiSelect,\n promptSelect,\n promptGlobPatterns,\n} from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport { parseGlobPatterns } from '../console/promptParsing.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport {\n FilesOptions,\n Settings,\n SupportedLibraries,\n SetupOptions,\n TranslateFlags,\n SharedFlags,\n} from '../types/index.js';\nimport { generateSettings } from '../config/generateSettings.js';\nimport chalk from 'chalk';\nimport { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';\nimport { handleSetupReactCommand } from '../setup/wizard.js';\nimport {\n isPackageInstalled,\n searchForPackageJson,\n} from '../utils/packageJson.js';\nimport { getDesiredLocales } from '../setup/userInput.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { retrieveCredentials, setCredentials } from '../utils/credentials.js';\nimport { areCredentialsSet } from '../utils/credentials.js';\nimport { upload } from './commands/upload.js';\nimport { attachSharedFlags, attachTranslateFlags } from './flags.js';\nimport { handleStage } from './commands/stage.js';\nimport { handleSetupProject } from './commands/setupProject.js';\nimport { handleDownload } from './commands/download.js';\nimport {\n handleTranslate,\n postProcessTranslations,\n} from './commands/translate.js';\nimport {\n getNeedsPostprocessing,\n clearDownloaded,\n} from '../state/recentDownloads.js';\nimport { clearWarnings } from '../state/translateWarnings.js';\nimport { displayTranslateSummary } from '../console/displayTranslateSummary.js';\nimport updateConfig from '../fs/config/updateConfig.js';\nimport { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';\nimport { saveLocalEdits } from '../api/saveLocalEdits.js';\nimport processSharedStaticAssets, {\n mirrorAssetsToLocales,\n} from '../utils/sharedStaticAssets.js';\nimport { setupLocadex } from '../locadex/setupFlow.js';\nimport { detectFramework } from '../setup/detectFramework.js';\nimport {\n getFrameworkDisplayName,\n getReactFrameworkLibrary,\n} from '../setup/frameworkUtils.js';\nimport { INLINE_LIBRARIES } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver, type MergeDriverName } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\n\nconst ID_COMPATIBILITY_WARNING_COMMANDS = new Set([\n 'download',\n 'enqueue',\n 'generate',\n 'setup',\n 'stage',\n 'translate',\n 'validate',\n]);\n\nexport type UploadOptions = {\n config?: string;\n apiKey?: string;\n projectId?: string;\n defaultLocale?: string;\n};\n\nexport type LoginOptions = {\n config?: string;\n keyType?: 'development' | 'production' | 'all';\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\nexport class BaseCLI {\n protected library: SupportedLibraries;\n protected additionalModules: SupportedLibraries[];\n protected program: Command;\n // Constructor is shared amongst all CLI class types\n public constructor(\n program: Command,\n library: SupportedLibraries,\n additionalModules?: SupportedLibraries[]\n ) {\n this.program = program;\n this.library = library;\n this.additionalModules = additionalModules || [];\n\n this.program.option(\n '--skip-version-check',\n 'Skip the monorepo GT package version consistency check'\n );\n this.program.option(\n '--suppress-id-compatibility-warning',\n 'Suppress the React package ID compatibility warning'\n );\n this.program.option(\n '-q, --quiet',\n 'Suppress informational output; only warnings and errors are shown'\n );\n // Apply --quiet before any other hook or command action runs so the\n // singleton logger is muted for the rest of the invocation. The flag is a\n // global root option, so commander resolves it in any position and for\n // nested commands (e.g. `gt git setup --quiet`).\n this.program.hook('preAction', () => {\n logger.setQuiet(Boolean(this.program.opts().quiet));\n });\n this.program.hook('preAction', async (thisCommand, actionCommand) => {\n // Nested commands (e.g. `gt git setup`) can share leaf names with\n // translation commands; only direct children of the root qualify\n if (actionCommand.parent !== thisCommand) return;\n if (!ID_COMPATIBILITY_WARNING_COMMANDS.has(actionCommand.name())) return;\n await warnReactPackageCompatibility(\n Boolean(this.program.opts().suppressIdCompatibilityWarning)\n );\n });\n\n this.setupInitCommand();\n this.setupConfigureCommand();\n this.setupUploadCommand();\n this.setupLoginCommand();\n this.setupSendDiffsCommand();\n this.setupGitCommand();\n }\n // Init is never called in a child class\n public init() {\n this.setupSetupProjectCommand();\n this.setupStageCommand();\n this.setupTranslateCommand();\n this.setupDownloadCommand();\n this.setupEnqueueCommand();\n }\n // Execute is called by the main program\n public execute() {\n // If no command is specified, run 'init'\n if (process.argv.length <= 2) {\n process.argv.push('init');\n }\n }\n\n protected setupSetupProjectCommand(): void {\n attachTranslateFlags(\n this.program\n .command('setup')\n .description(\n 'Upload source files and setup the project for translation'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Uploading source files and setting up project...');\n await this.handleSetupProject(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupStageCommand(): void {\n attachTranslateFlags(\n this.program\n .command('stage')\n .description(\n 'Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader(\n 'Staging project for translation with approval required...'\n );\n await this.handleStage(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupEnqueueCommand(): void {\n attachTranslateFlags(\n this.program\n .command('enqueue')\n .description('Enqueues translations for a given set of files')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Enqueuing translations...');\n await this.handleEnqueue(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupDownloadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('download')\n .description('Download translations that were originally staged')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Downloading translations...');\n await this.handleDownload(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupTranslateCommand(): void {\n attachTranslateFlags(\n this.program\n .command('translate')\n .description('Translate your project using General Translation')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Starting translation...');\n await this.handleTranslate(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupSendDiffsCommand(): void {\n attachSharedFlags(\n this.program\n .command('save-local')\n .description(\n 'Save local edits for all configured files by sending diffs (no translation enqueued)'\n )\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .action(async (initOptions: SharedFlags) => {\n displayHeader('Saving local edits...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\n });\n }\n\n protected setupGitCommand(): void {\n const gitCommand = this.program\n .command('git')\n .description('Configure Git integrations for General Translation');\n\n gitCommand\n .command('setup')\n .description('Set up GT merge drivers for generated translation files')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--dry-run', 'Print changes without writing files', false)\n .option(\n '--omit-config-ids',\n 'Persist omitConfigIds and remove generated config IDs'\n )\n .option(\n '--driver-command <command>',\n 'Command Git should use to invoke gt, e.g. \"pnpm exec gt\"'\n )\n .action(async (options: GitSetupOptions) => {\n displayHeader('Setting up GT Git merge drivers...');\n const settings = await generateSettings(options, undefined, {\n requireConfig: true,\n });\n const omitConfigIds = await this.resolveGitSetupOmitConfigIds(\n options,\n settings\n );\n const result = await setupGitMergeDrivers(settings, {\n dryRun: options.dryRun,\n omitConfigIds,\n driverCommand: options.driverCommand,\n });\n\n for (const line of result.addedAttributes) {\n logger.step(\n `${options.dryRun ? 'Would add' : 'Added'} ${chalk.cyan(\n line\n )} to ${chalk.cyan(result.gitattributesPath)}`\n );\n }\n if (result.addedAttributes.length === 0) {\n logger.info(`${chalk.cyan('.gitattributes')} is already configured.`);\n }\n\n for (const args of result.gitConfigCommands) {\n logger.step(\n `${options.dryRun ? 'Would run' : 'Configured'} ${chalk.cyan(\n `git config --local ${args.join(' ')}`\n )}`\n );\n }\n\n if (result.updatedConfig) {\n logger.step(\n `${options.dryRun ? 'Would persist' : 'Persisted'} ${chalk.cyan(\n 'omitConfigIds: true'\n )} in ${chalk.cyan(settings.config)}`\n );\n } else if (options.dryRun && !settings.omitConfigIds) {\n logger.info(\n `Run without ${chalk.cyan('--dry-run')} to be prompted to persist ${chalk.cyan(\n 'omitConfigIds: true'\n )}, or pass ${chalk.cyan('--omit-config-ids')}.`\n );\n }\n\n for (const warning of result.warnings) {\n logger.warn(chalk.yellow(warning));\n }\n\n logger.endCommand(\n options.dryRun\n ? 'Dry run complete.'\n : 'GT Git merge drivers configured.'\n );\n });\n\n gitCommand\n .command('merge-driver', { hidden: true })\n .argument('<driver>', 'Merge driver name')\n .argument('<base>', 'Common ancestor file')\n .argument('<ours>', 'Current branch file')\n .argument('<theirs>', 'Incoming branch file')\n .argument('[path]', 'Merged path')\n .action((driver: string, base: string, ours: string, theirs: string) => {\n if (driver !== 'gt-lock' && driver !== 'gtjson') {\n logger.error(`Unknown GT merge driver: ${driver}`);\n exitSync(1);\n }\n const result = runMergeDriver(\n driver as MergeDriverName,\n base,\n ours,\n theirs\n );\n if (!result.ok) {\n logger.error(result.reason);\n exitSync(1);\n }\n });\n }\n\n protected async resolveGitSetupOmitConfigIds(\n options: GitSetupOptions,\n settings: Settings\n ): Promise<boolean> {\n if (options.omitConfigIds) return true;\n // Already opted in: persist again so stale config IDs still get removed\n if (settings.omitConfigIds) return true;\n if (options.dryRun || !process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return promptConfirm({\n message:\n 'Also set omitConfigIds: true to reduce gt.config.json merge conflicts?',\n defaultValue: true,\n });\n }\n\n protected async handleSetupProject(\n initOptions: TranslateFlags\n ): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n await handleSetupProject(initOptions, settings, this.library);\n }\n\n protected async handleStage(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Update settings.stageTranslations to true\n settings.stageTranslations = true;\n await updateConfig(settings.config, {\n stageTranslations: true,\n });\n }\n await handleStage(initOptions, settings, this.library, true);\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleEnqueue(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleEnqueue(initOptions, settings, this.library);\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleDownload(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleDownload(initOptions, settings, this.library);\n }\n\n protected async handleTranslate(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n const results = await handleStage(\n initOptions,\n settings,\n this.library,\n false\n );\n if (results) {\n await handleTranslate(\n initOptions,\n settings,\n results.fileVersionData,\n results.jobData,\n results.branchData,\n results.publishMap\n );\n }\n } else {\n await handleDownload(initOptions, settings, this.library);\n }\n // Only postprocess files downloaded in this run\n const include = getNeedsPostprocessing();\n if (include.size > 0) {\n await postProcessTranslations(settings, include);\n }\n // Split Mintlify language entries into $ref files to keep docs.json small\n await splitMintlifyLanguageRefs(settings);\n // Mirror assets after translations are downloaded and locale dirs are populated\n await mirrorAssetsToLocales(settings);\n clearDownloaded();\n displayTranslateSummary();\n clearWarnings();\n }\n\n protected setupUploadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('upload')\n .description(\n 'Upload source files and translations to the General Translation platform'\n )\n ).action(async (initOptions: UploadOptions) => {\n displayHeader('Starting upload...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n const options = { ...initOptions, ...settings };\n\n await this.handleUploadCommand(options);\n logger.endCommand('Done!');\n });\n }\n\n protected setupLoginCommand(): void {\n this.program\n .command('auth')\n .description('Generate General Translation API keys and project ID')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option(\n '-t, --key-type <type>',\n 'Type of key to generate, production | development | all'\n )\n .action(async (options: LoginOptions) => {\n displayHeader('Authenticating with General Translation...');\n if (!options.keyType) {\n options.keyType = await promptSelect<\n 'development' | 'production' | 'all'\n >({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n } else {\n if (\n options.keyType !== 'development' &&\n options.keyType !== 'production' &&\n options.keyType !== 'all'\n ) {\n logErrorAndExit(\n 'Invalid key type, must be development, production, or all'\n );\n }\n }\n await this.handleLoginCommand(options);\n logger.endCommand(\n `Done! ${options.keyType} keys have been generated and saved to your .env.local file.`\n );\n });\n }\n\n protected setupInitCommand(): void {\n this.program\n .command('init')\n .description(\n 'Run the setup wizard to configure your project for General Translation'\n )\n .option(\n '--src <paths...>',\n \"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}'\"\n )\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .action(async (options: SetupOptions) => {\n const settings = await generateSettings(options);\n displayHeader('Running setup wizard...');\n\n const framework = await detectFramework();\n\n const useAgent = await (async () => {\n let useAgentMessage;\n if (framework.name === 'mintlify') {\n useAgentMessage = `Mintlify project detected. Would you like to connect to GitHub so that the Locadex AI Agent can translate your project automatically?`;\n }\n if (framework.name === 'next-app') {\n useAgentMessage = `Next.js App Router detected. Would you like to connect to GitHub so that the Locadex AI Agent can set up your project automatically?`;\n }\n if (useAgentMessage) {\n return await promptConfirm({\n message: useAgentMessage,\n defaultValue: false,\n });\n }\n return false;\n })();\n\n if (useAgent) {\n await setupLocadex(settings);\n logger.endCommand(\n 'Once installed, Locadex will open a PR to your repository. See the docs for more information: https://generaltranslation.com/docs/locadex'\n );\n } else {\n // Get framework display info for the defaults message\n const frameworkDisplayName =\n framework.type === 'react'\n ? getFrameworkDisplayName(framework)\n : null;\n const library =\n framework.type === 'react'\n ? getReactFrameworkLibrary(framework)\n : null;\n\n // Build defaults description based on detected framework\n const defaultTranslationsDir =\n framework.name === 'vite'\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n const defaultsDescription =\n framework.type === 'react'\n ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : `Files saved locally in ${defaultTranslationsDir}`;\n\n // Ask if user wants to use defaults\n const useDefaults = await promptConfirm({\n message: `Would you like to use the recommended General Translation defaults? ${chalk.dim(`(${defaultsDescription})`)}`,\n defaultValue: true,\n });\n\n let ranReactSetup = false;\n\n // so that people can run init in non-js projects\n if (framework.type === 'react') {\n const wrap = useDefaults\n ? true\n : await promptConfirm({\n message: `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,\n defaultValue: true,\n });\n\n if (wrap) {\n logger.info(\n `${chalk.yellow('[EXPERIMENTAL]')} Configuring project...`\n );\n await handleSetupReactCommand(options, framework, useDefaults);\n logger.endCommand(\n `Done! Since this wizard is experimental, please review the changes and make modifications as needed.\n\\nNext step: start internationalizing! See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n );\n ranReactSetup = true;\n }\n }\n\n if (ranReactSetup) {\n logger.startCommand('Setting up project config...');\n }\n // Configure gt.config.json\n await this.handleInitCommand(\n ranReactSetup,\n useDefaults,\n framework.name === 'vite'\n );\n\n logger.endCommand(\n 'Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs'\n );\n }\n });\n }\n\n protected setupConfigureCommand(): void {\n this.program\n .command('configure')\n .description(\n 'Configure your project for General Translation. This will create a gt.config.json file in your codebase.'\n )\n .action(async () => {\n displayHeader('Configuring project...');\n\n logger.info(\n 'Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.'\n );\n\n // Configure gt.config.json\n const framework = await detectFramework();\n await this.handleInitCommand(false, false, framework.name === 'vite');\n\n logger.endCommand(\n 'Done! Make sure you have an API key and project ID to use General Translation. Get them on the dashboard: https://generaltranslation.com/dashboard'\n );\n });\n }\n\n protected async handleUploadCommand(\n settings: Settings & UploadOptions\n ): Promise<void> {\n if (!settings.files) {\n return;\n }\n\n // Process all file types at once with a single call\n await upload(settings);\n }\n\n // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false\n ): Promise<void> {\n const { defaultLocale, locales } = await getDesiredLocales(); // Locales should still be asked for even if using defaults\n\n const packageJson = await searchForPackageJson();\n\n // Ask if using another i18n library\n const gtInstalled =\n !!packageJson &&\n INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson));\n const isUsingGT = ranReactSetup || gtInstalled;\n\n // Ask where the translations are stored\n const usingCDN = await (async () => {\n if (!isUsingGT) return false;\n if (useDefaults) return false; // Default to local\n const selectedValue = await promptSelect({\n message: `Would you like to save translation files locally or use the General Translation CDN to store them?`,\n options: [\n { value: 'local', label: 'Save locally' },\n { value: 'cdn', label: 'Use CDN' },\n ],\n defaultValue: 'local',\n });\n return selectedValue === 'cdn';\n })();\n\n const defaultTranslationsDir = isVite\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n // Ask where the translations are stored\n const translationsDir =\n isUsingGT && !usingCDN\n ? useDefaults\n ? defaultTranslationsDir\n : await promptText({\n message:\n 'What is the path to the directory where you would like to store your translation files?',\n defaultValue: defaultTranslationsDir,\n })\n : null;\n\n // Determine final translations directory with fallback\n const finalTranslationsDir =\n translationsDir?.trim() || defaultTranslationsDir;\n\n if (isUsingGT && !usingCDN) {\n // Create loadTranslations.js file for local translations\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n logger.message(\n `Created ${chalk.cyan('loadTranslations.js')} file for local translations.\nMake sure to add this function to your app configuration.\nSee https://generaltranslation.com/en/docs/next/guides/local-tx`\n );\n }\n\n const message = !isUsingGT\n ? 'What is the format of your language resource files? Select as many as applicable.\\nAdditionally, you can translate any other files you have in your project.'\n : `Do you have any additional files in this project to translate? For example, Markdown files for docs. ${chalk.dim(\n '(To continue without selecting press Enter)'\n )}`;\n const fileExtensions =\n useDefaults && isUsingGT\n ? [] // Skip for GT projects when using defaults\n : await promptMultiSelect({\n message,\n options: [\n { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },\n { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },\n { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },\n { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },\n { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },\n { value: 'yaml', label: FILE_EXT_TO_EXT_LABEL.yaml },\n // TWILIO_CONTENT_JSON not supported in CLI init as its too niche\n ],\n required: !isUsingGT,\n });\n\n const files: FilesOptions = {};\n for (const fileExtension of fileExtensions) {\n const label = FILE_EXT_TO_EXT_LABEL[fileExtension];\n const paths = await promptGlobPatterns({\n label,\n message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\\nMake sure to include [locale] in the patterns.\\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,\n defaultValue: `./**/[locale]/*.${fileExtension}`,\n });\n\n files[fileExtension] = {\n include: parseGlobPatterns(paths),\n };\n }\n\n // Add GT translations if using GT and storing locally\n if (isUsingGT && !usingCDN) {\n files.gt = {\n output: path.join(finalTranslationsDir, `[locale].json`),\n };\n }\n\n let configFilepath = 'gt.config.json';\n if (fs.existsSync('src/gt.config.json')) {\n configFilepath = 'src/gt.config.json';\n }\n\n // Create gt.config.json\n await createOrUpdateConfig(configFilepath, {\n defaultLocale,\n locales,\n files: Object.keys(files).length > 0 ? files : undefined,\n publish: isUsingGT && usingCDN,\n });\n\n logger.success(\n `Edit ${chalk.cyan(\n configFilepath\n )} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`\n );\n\n // Install gt if not installed\n const isCLIInstalled = packageJson\n ? isPackageInstalled('gt', packageJson, true, true)\n : true; // if no package.json, we can't install it\n\n if (!isCLIInstalled) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner();\n spinner.start(\n `Installing gt as a dev dependency with ${packageManager.name}...`\n );\n await installPackage('gt', packageManager, true);\n spinner.stop(chalk.green('Installed gt.'));\n }\n\n // Set credentials\n if (!areCredentialsSet()) {\n const loginQuestion = useDefaults\n ? true\n : await promptConfirm({\n message:\n 'Would you like the wizard to automatically generate API keys and a project ID for you?',\n defaultValue: true,\n });\n if (loginQuestion) {\n const settings = await generateSettings({});\n const keyType = useDefaults\n ? 'all'\n : await promptSelect<'development' | 'production' | 'all'>({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n }\n }\n protected async handleLoginCommand(options: LoginOptions): Promise<void> {\n const settings = await generateSettings({ config: options.config });\n const keyType = options.keyType || 'all';\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command } from 'commander';\nimport {\n DEFAULT_TRANSLATIONS_DIR,\n DEFAULT_VITE_TRANSLATIONS_DIR,\n} from '../utils/constants.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport {\n displayHeader,\n promptText,\n logErrorAndExit,\n exitSync,\n promptConfirm,\n promptMultiSelect,\n promptSelect,\n promptGlobPatterns,\n} from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport { lottieTranslateError } from '../console/index.js';\nimport { parseGlobPatterns } from '../console/promptParsing.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport {\n FilesOptions,\n Settings,\n SupportedLibraries,\n SetupOptions,\n TranslateFlags,\n SharedFlags,\n} from '../types/index.js';\nimport { generateSettings } from '../config/generateSettings.js';\nimport chalk from 'chalk';\nimport { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';\nimport { handleSetupReactCommand } from '../setup/wizard.js';\nimport {\n isPackageInstalled,\n searchForPackageJson,\n} from '../utils/packageJson.js';\nimport { getDesiredLocales } from '../setup/userInput.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { retrieveCredentials, setCredentials } from '../utils/credentials.js';\nimport { areCredentialsSet } from '../utils/credentials.js';\nimport { upload } from './commands/upload.js';\nimport { attachSharedFlags, attachTranslateFlags } from './flags.js';\nimport { handleStage } from './commands/stage.js';\nimport { handleSetupProject } from './commands/setupProject.js';\nimport { handleDownload } from './commands/download.js';\nimport {\n handleTranslate,\n postProcessTranslations,\n} from './commands/translate.js';\nimport {\n getNeedsPostprocessing,\n clearDownloaded,\n} from '../state/recentDownloads.js';\nimport { clearWarnings } from '../state/translateWarnings.js';\nimport { displayTranslateSummary } from '../console/displayTranslateSummary.js';\nimport updateConfig from '../fs/config/updateConfig.js';\nimport { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';\nimport { saveLocalEdits } from '../api/saveLocalEdits.js';\nimport processSharedStaticAssets, {\n mirrorAssetsToLocales,\n} from '../utils/sharedStaticAssets.js';\nimport { setupLocadex } from '../locadex/setupFlow.js';\nimport { detectFramework } from '../setup/detectFramework.js';\nimport {\n getFrameworkDisplayName,\n getReactFrameworkLibrary,\n} from '../setup/frameworkUtils.js';\nimport { INLINE_LIBRARIES } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver, type MergeDriverName } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\n\nconst ID_COMPATIBILITY_WARNING_COMMANDS = new Set([\n 'download',\n 'enqueue',\n 'generate',\n 'setup',\n 'stage',\n 'translate',\n 'validate',\n]);\nconst workspaceRootSetupError = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The setup wizard cannot run from a monorepo workspace root',\n why: 'GT must be configured in the specific app you want to localize',\n fix: \"Change to that app's directory and rerun `npx gt@latest`\",\n});\n\nasync function exitIfWorkspaceRoot(): Promise<void> {\n const packageJson = await searchForPackageJson();\n if (\n fs.existsSync(path.join(process.cwd(), 'pnpm-workspace.yaml')) ||\n packageJson?.workspaces\n ) {\n logErrorAndExit(workspaceRootSetupError);\n }\n}\n\nexport type UploadOptions = {\n config?: string;\n apiKey?: string;\n projectId?: string;\n defaultLocale?: string;\n};\n\nexport type LoginOptions = {\n config?: string;\n keyType?: 'development' | 'production' | 'all';\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\nexport class BaseCLI {\n protected library: SupportedLibraries;\n protected additionalModules: SupportedLibraries[];\n protected program: Command;\n // Constructor is shared amongst all CLI class types\n public constructor(\n program: Command,\n library: SupportedLibraries,\n additionalModules?: SupportedLibraries[]\n ) {\n this.program = program;\n this.library = library;\n this.additionalModules = additionalModules || [];\n\n this.program.option(\n '--skip-version-check',\n 'Skip the monorepo GT package version consistency check'\n );\n this.program.option(\n '--suppress-id-compatibility-warning',\n 'Suppress the React package ID compatibility warning'\n );\n this.program.option(\n '-q, --quiet',\n 'Suppress informational output; only warnings and errors are shown'\n );\n // Apply --quiet before any other hook or command action runs so the\n // singleton logger is muted for the rest of the invocation. The flag is a\n // global root option, so commander resolves it in any position and for\n // nested commands (e.g. `gt git setup --quiet`).\n this.program.hook('preAction', () => {\n logger.setQuiet(Boolean(this.program.opts().quiet));\n });\n this.program.hook('preAction', async (thisCommand, actionCommand) => {\n // Nested commands (e.g. `gt git setup`) can share leaf names with\n // translation commands; only direct children of the root qualify\n if (actionCommand.parent !== thisCommand) return;\n if (!ID_COMPATIBILITY_WARNING_COMMANDS.has(actionCommand.name())) return;\n await warnReactPackageCompatibility(\n Boolean(this.program.opts().suppressIdCompatibilityWarning)\n );\n });\n\n this.setupInitCommand();\n this.setupConfigureCommand();\n this.setupUploadCommand();\n this.setupLoginCommand();\n this.setupSendDiffsCommand();\n this.setupGitCommand();\n }\n // Init is never called in a child class\n public init() {\n this.setupSetupProjectCommand();\n this.setupStageCommand();\n this.setupTranslateCommand();\n this.setupDownloadCommand();\n this.setupEnqueueCommand();\n }\n // Execute is called by the main program\n public execute() {\n // If no command is specified, run 'init'\n if (process.argv.length <= 2) {\n process.argv.push('init');\n }\n }\n\n protected setupSetupProjectCommand(): void {\n attachTranslateFlags(\n this.program\n .command('setup')\n .description(\n 'Upload source files and setup the project for translation'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Uploading source files and setting up project...');\n await this.handleSetupProject(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupStageCommand(): void {\n attachTranslateFlags(\n this.program\n .command('stage')\n .description(\n 'Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader(\n 'Staging project for translation with approval required...'\n );\n await this.handleStage(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupEnqueueCommand(): void {\n attachTranslateFlags(\n this.program\n .command('enqueue')\n .description('Enqueues translations for a given set of files')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Enqueuing translations...');\n await this.handleEnqueue(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupDownloadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('download')\n .description('Download translations that were originally staged')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Downloading translations...');\n await this.handleDownload(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupTranslateCommand(): void {\n attachTranslateFlags(\n this.program\n .command('translate')\n .description('Translate your project using General Translation')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Starting translation...');\n await this.handleTranslate(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupSendDiffsCommand(): void {\n attachSharedFlags(\n this.program\n .command('save-local')\n .description(\n 'Save local edits for all configured files by sending diffs (no translation enqueued)'\n )\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .action(async (initOptions: SharedFlags) => {\n displayHeader('Saving local edits...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\n });\n }\n\n protected setupGitCommand(): void {\n const gitCommand = this.program\n .command('git')\n .description('Configure Git integrations for General Translation');\n\n gitCommand\n .command('setup')\n .description('Set up GT merge drivers for generated translation files')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--dry-run', 'Print changes without writing files', false)\n .option(\n '--omit-config-ids',\n 'Persist omitConfigIds and remove generated config IDs'\n )\n .option(\n '--driver-command <command>',\n 'Command Git should use to invoke gt, e.g. \"pnpm exec gt\"'\n )\n .action(async (options: GitSetupOptions) => {\n displayHeader('Setting up GT Git merge drivers...');\n const settings = await generateSettings(options, undefined, {\n requireConfig: true,\n });\n const omitConfigIds = await this.resolveGitSetupOmitConfigIds(\n options,\n settings\n );\n const result = await setupGitMergeDrivers(settings, {\n dryRun: options.dryRun,\n omitConfigIds,\n driverCommand: options.driverCommand,\n });\n\n for (const line of result.addedAttributes) {\n logger.step(\n `${options.dryRun ? 'Would add' : 'Added'} ${chalk.cyan(\n line\n )} to ${chalk.cyan(result.gitattributesPath)}`\n );\n }\n if (result.addedAttributes.length === 0) {\n logger.info(`${chalk.cyan('.gitattributes')} is already configured.`);\n }\n\n for (const args of result.gitConfigCommands) {\n logger.step(\n `${options.dryRun ? 'Would run' : 'Configured'} ${chalk.cyan(\n `git config --local ${args.join(' ')}`\n )}`\n );\n }\n\n if (result.updatedConfig) {\n logger.step(\n `${options.dryRun ? 'Would persist' : 'Persisted'} ${chalk.cyan(\n 'omitConfigIds: true'\n )} in ${chalk.cyan(settings.config)}`\n );\n } else if (options.dryRun && !settings.omitConfigIds) {\n logger.info(\n `Run without ${chalk.cyan('--dry-run')} to be prompted to persist ${chalk.cyan(\n 'omitConfigIds: true'\n )}, or pass ${chalk.cyan('--omit-config-ids')}.`\n );\n }\n\n for (const warning of result.warnings) {\n logger.warn(chalk.yellow(warning));\n }\n\n logger.endCommand(\n options.dryRun\n ? 'Dry run complete.'\n : 'GT Git merge drivers configured.'\n );\n });\n\n gitCommand\n .command('merge-driver', { hidden: true })\n .argument('<driver>', 'Merge driver name')\n .argument('<base>', 'Common ancestor file')\n .argument('<ours>', 'Current branch file')\n .argument('<theirs>', 'Incoming branch file')\n .argument('[path]', 'Merged path')\n .action((driver: string, base: string, ours: string, theirs: string) => {\n if (driver !== 'gt-lock' && driver !== 'gtjson') {\n logger.error(`Unknown GT merge driver: ${driver}`);\n exitSync(1);\n }\n const result = runMergeDriver(\n driver as MergeDriverName,\n base,\n ours,\n theirs\n );\n if (!result.ok) {\n logger.error(result.reason);\n exitSync(1);\n }\n });\n }\n\n protected async resolveGitSetupOmitConfigIds(\n options: GitSetupOptions,\n settings: Settings\n ): Promise<boolean> {\n if (options.omitConfigIds) return true;\n // Already opted in: persist again so stale config IDs still get removed\n if (settings.omitConfigIds) return true;\n if (options.dryRun || !process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return promptConfirm({\n message:\n 'Also set omitConfigIds: true to reduce gt.config.json merge conflicts?',\n defaultValue: true,\n });\n }\n\n protected async handleSetupProject(\n initOptions: TranslateFlags\n ): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n await handleSetupProject(initOptions, settings, this.library);\n }\n\n protected async handleStage(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Update settings.stageTranslations to true\n settings.stageTranslations = true;\n await updateConfig(settings.config, {\n stageTranslations: true,\n });\n }\n await handleStage(initOptions, settings, this.library, true);\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleEnqueue(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleEnqueue(initOptions, settings, this.library);\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleDownload(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleDownload(initOptions, settings, this.library);\n }\n\n protected async handleTranslate(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Lottie translations finish asynchronously server-side (layout\n // refinement runs after the translation job completes), so the immediate\n // translate flow would try to download them before they're ready. Only\n // the stage + download flow supports them.\n if (settings.files?.resolvedPaths.lottie?.length) {\n return logErrorAndExit(lottieTranslateError);\n }\n const results = await handleStage(\n initOptions,\n settings,\n this.library,\n false\n );\n if (results) {\n await handleTranslate(\n initOptions,\n settings,\n results.fileVersionData,\n results.jobData,\n results.branchData,\n results.publishMap\n );\n }\n } else {\n await handleDownload(initOptions, settings, this.library);\n }\n // Only postprocess files downloaded in this run\n const include = getNeedsPostprocessing();\n if (include.size > 0) {\n await postProcessTranslations(settings, include);\n }\n // Split Mintlify language entries into $ref files to keep docs.json small\n await splitMintlifyLanguageRefs(settings);\n // Mirror assets after translations are downloaded and locale dirs are populated\n await mirrorAssetsToLocales(settings);\n clearDownloaded();\n displayTranslateSummary();\n clearWarnings();\n }\n\n protected setupUploadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('upload')\n .description(\n 'Upload source files and translations to the General Translation platform'\n )\n ).action(async (initOptions: UploadOptions) => {\n displayHeader('Starting upload...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n const options = { ...initOptions, ...settings };\n\n await this.handleUploadCommand(options);\n logger.endCommand('Done!');\n });\n }\n\n protected setupLoginCommand(): void {\n this.program\n .command('auth')\n .description('Generate General Translation API keys and project ID')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option(\n '-t, --key-type <type>',\n 'Type of key to generate, production | development | all'\n )\n .action(async (options: LoginOptions) => {\n displayHeader('Authenticating with General Translation...');\n if (!options.keyType) {\n options.keyType = await promptSelect<\n 'development' | 'production' | 'all'\n >({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n } else {\n if (\n options.keyType !== 'development' &&\n options.keyType !== 'production' &&\n options.keyType !== 'all'\n ) {\n logErrorAndExit(\n 'Invalid key type, must be development, production, or all'\n );\n }\n }\n await this.handleLoginCommand(options);\n logger.endCommand(\n `Done! ${options.keyType} keys have been generated and saved to your .env.local file.`\n );\n });\n }\n\n protected setupInitCommand(): void {\n this.program\n .command('init')\n .description(\n 'Run the setup wizard to configure your project for General Translation'\n )\n .option(\n '--src <paths...>',\n \"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}'\"\n )\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .action(async (options: SetupOptions) => {\n await exitIfWorkspaceRoot();\n const settings = await generateSettings(options);\n displayHeader('Running setup wizard...');\n\n const framework = await detectFramework();\n\n const useAgent = await (async () => {\n let useAgentMessage;\n if (framework.name === 'mintlify') {\n useAgentMessage = `Mintlify project detected. Would you like to connect to GitHub so that the Locadex AI Agent can translate your project automatically?`;\n }\n if (framework.name === 'next-app') {\n useAgentMessage = `Next.js App Router detected. Would you like to connect to GitHub so that the Locadex AI Agent can set up your project automatically?`;\n }\n if (useAgentMessage) {\n return await promptConfirm({\n message: useAgentMessage,\n defaultValue: false,\n });\n }\n return false;\n })();\n\n if (useAgent) {\n await setupLocadex(settings);\n logger.endCommand(\n 'Once installed, Locadex will open a PR to your repository. See the docs for more information: https://generaltranslation.com/docs/locadex'\n );\n } else {\n // Get framework display info for the defaults message\n const frameworkDisplayName =\n framework.type === 'react'\n ? getFrameworkDisplayName(framework)\n : null;\n const library =\n framework.type === 'react'\n ? getReactFrameworkLibrary(framework)\n : null;\n\n // Build defaults description based on detected framework\n const defaultTranslationsDir =\n framework.name === 'vite'\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n const defaultsDescription =\n framework.type === 'react'\n ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : `Files saved locally in ${defaultTranslationsDir}`;\n\n // Ask if user wants to use defaults\n const useDefaults = await promptConfirm({\n message: `Would you like to use the recommended General Translation defaults? ${chalk.dim(`(${defaultsDescription})`)}`,\n defaultValue: true,\n });\n\n let ranReactSetup = false;\n\n // so that people can run init in non-js projects\n if (framework.type === 'react') {\n const wrap = useDefaults\n ? true\n : await promptConfirm({\n message: `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,\n defaultValue: true,\n });\n\n if (wrap) {\n logger.info(\n `${chalk.yellow('[EXPERIMENTAL]')} Configuring project...`\n );\n await handleSetupReactCommand(options, framework, useDefaults);\n logger.endCommand(\n `Done! Since this wizard is experimental, please review the changes and make modifications as needed.\n\\nNext step: start internationalizing! See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n );\n ranReactSetup = true;\n }\n }\n\n if (ranReactSetup) {\n logger.startCommand('Setting up project config...');\n }\n // Configure gt.config.json\n await this.handleInitCommand(\n ranReactSetup,\n useDefaults,\n framework.name === 'vite'\n );\n\n logger.endCommand(\n 'Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs'\n );\n }\n });\n }\n\n protected setupConfigureCommand(): void {\n this.program\n .command('configure')\n .description(\n 'Configure your project for General Translation. This will create a gt.config.json file in your codebase.'\n )\n .action(async () => {\n await exitIfWorkspaceRoot();\n displayHeader('Configuring project...');\n\n logger.info(\n 'Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.'\n );\n\n // Configure gt.config.json\n const framework = await detectFramework();\n await this.handleInitCommand(false, false, framework.name === 'vite');\n\n logger.endCommand(\n 'Done! Make sure you have an API key and project ID to use General Translation. Get them on the dashboard: https://generaltranslation.com/dashboard'\n );\n });\n }\n\n protected async handleUploadCommand(\n settings: Settings & UploadOptions\n ): Promise<void> {\n if (!settings.files) {\n return;\n }\n\n // Process all file types at once with a single call\n await upload(settings);\n }\n\n // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false\n ): Promise<void> {\n const { defaultLocale, locales } = await getDesiredLocales(); // Locales should still be asked for even if using defaults\n\n const packageJson = await searchForPackageJson();\n\n // Ask if using another i18n library\n const gtInstalled =\n !!packageJson &&\n INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson));\n const isUsingGT = ranReactSetup || gtInstalled;\n\n // Ask where the translations are stored\n const usingCDN = await (async () => {\n if (!isUsingGT) return false;\n if (useDefaults) return false; // Default to local\n const selectedValue = await promptSelect({\n message: `Would you like to save translation files locally or use the General Translation CDN to store them?`,\n options: [\n { value: 'local', label: 'Save locally' },\n { value: 'cdn', label: 'Use CDN' },\n ],\n defaultValue: 'local',\n });\n return selectedValue === 'cdn';\n })();\n\n const defaultTranslationsDir = isVite\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n // Ask where the translations are stored\n const translationsDir =\n isUsingGT && !usingCDN\n ? useDefaults\n ? defaultTranslationsDir\n : await promptText({\n message:\n 'What is the path to the directory where you would like to store your translation files?',\n defaultValue: defaultTranslationsDir,\n })\n : null;\n\n // Determine final translations directory with fallback\n const finalTranslationsDir =\n translationsDir?.trim() || defaultTranslationsDir;\n\n if (isUsingGT && !usingCDN) {\n // Create loadTranslations.js file for local translations\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n logger.message(\n `Created ${chalk.cyan('loadTranslations.js')} file for local translations.\nMake sure to add this function to your app configuration.\nSee https://generaltranslation.com/en/docs/next/guides/local-tx`\n );\n }\n\n const message = !isUsingGT\n ? 'What is the format of your language resource files? Select as many as applicable.\\nAdditionally, you can translate any other files you have in your project.'\n : `Do you have any additional files in this project to translate? For example, Markdown files for docs. ${chalk.dim(\n '(To continue without selecting press Enter)'\n )}`;\n const fileExtensions =\n useDefaults && isUsingGT\n ? [] // Skip for GT projects when using defaults\n : await promptMultiSelect({\n message,\n options: [\n { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },\n { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },\n { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },\n { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },\n { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },\n { value: 'yaml', label: FILE_EXT_TO_EXT_LABEL.yaml },\n // TWILIO_CONTENT_JSON not supported in CLI init as its too niche\n ],\n required: !isUsingGT,\n });\n\n const files: FilesOptions = {};\n for (const fileExtension of fileExtensions) {\n const label = FILE_EXT_TO_EXT_LABEL[fileExtension];\n const paths = await promptGlobPatterns({\n label,\n message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\\nMake sure to include [locale] in the patterns.\\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,\n defaultValue: `./**/[locale]/*.${fileExtension}`,\n });\n\n files[fileExtension] = {\n include: parseGlobPatterns(paths),\n };\n }\n\n // Add GT translations if using GT and storing locally\n if (isUsingGT && !usingCDN) {\n files.gt = {\n output: path.join(finalTranslationsDir, `[locale].json`),\n };\n }\n\n let configFilepath = 'gt.config.json';\n if (fs.existsSync('src/gt.config.json')) {\n configFilepath = 'src/gt.config.json';\n }\n\n // Create gt.config.json\n await createOrUpdateConfig(configFilepath, {\n defaultLocale,\n locales,\n files: Object.keys(files).length > 0 ? files : undefined,\n publish: isUsingGT && usingCDN,\n });\n\n logger.success(\n `Edit ${chalk.cyan(\n configFilepath\n )} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`\n );\n\n // Install gt if not installed\n const isCLIInstalled = packageJson\n ? isPackageInstalled('gt', packageJson, true, true)\n : true; // if no package.json, we can't install it\n\n if (!isCLIInstalled) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner();\n spinner.start(\n `Installing gt as a dev dependency with ${packageManager.name}...`\n );\n await installPackage('gt', packageManager, true);\n spinner.stop(chalk.green('Installed gt.'));\n }\n\n // Set credentials\n if (!areCredentialsSet()) {\n const loginQuestion = useDefaults\n ? true\n : await promptConfirm({\n message:\n 'Would you like the wizard to automatically generate API keys and a project ID for you?',\n defaultValue: true,\n });\n if (loginQuestion) {\n const settings = await generateSettings({});\n const keyType = useDefaults\n ? 'all'\n : await promptSelect<'development' | 'production' | 'all'>({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n }\n }\n protected async handleLoginCommand(options: LoginOptions): Promise<void> {\n const settings = await generateSettings({ config: options.config });\n const keyType = options.keyType || 'all';\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { exitSync, logErrorAndExit } from "../../console/logging.js";
|
|
2
|
-
import { noFilesError } from "../../console/index.js";
|
|
2
|
+
import { noFilesError, noVersionIdError } from "../../console/index.js";
|
|
3
3
|
import { getStagedEntriesFromLockfile } from "../../fs/config/downloadedVersions.js";
|
|
4
4
|
import { createFileMapping } from "../../formats/files/fileMapping.js";
|
|
5
5
|
import { hasValidCredentials, hasValidLocales } from "./utils/validation.js";
|
|
@@ -22,6 +22,7 @@ async function handleDownload(options, settings, library) {
|
|
|
22
22
|
if (settings.stageTranslations) fileVersionData = getStagedEntriesFromLockfile(settings);
|
|
23
23
|
else {
|
|
24
24
|
const { files } = await collectFiles(options, settings, library);
|
|
25
|
+
if (!settings.omitConfigIds && !settings._versionId && files.some((file) => file.fileFormat === "GTJSON")) return logErrorAndExit(noVersionIdError);
|
|
25
26
|
fileVersionData = convertToFileTranslationData(files);
|
|
26
27
|
}
|
|
27
28
|
await runDownloadWorkflow({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.js","names":[],"sources":["../../../src/cli/commands/download.ts"],"sourcesContent":["import { noFilesError } from '../../console/index.js';\nimport { SupportedLibraries, TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport { getStagedEntriesFromLockfile } from '../../fs/config/downloadedVersions.js';\nimport {\n runDownloadWorkflow,\n FileTranslationData,\n} from '../../workflows/download.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\nimport { convertToFileTranslationData } from '../../formats/files/convertToFileTranslationData.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\n\n// Downloads translations that were originally staged\n\n/**\n * Downloads translations that were originally staged\n * @param options - The options for the command\n * @param settings - The settings for the command\n */\nexport async function handleDownload(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries\n) {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n if (!settings.files) {\n return logErrorAndExit(noFilesError);\n }\n // Files\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n // Collect the hashes for all files we need to download\n let fileVersionData: FileTranslationData;\n if (settings.stageTranslations) {\n fileVersionData = getStagedEntriesFromLockfile(settings);\n } else {\n const { files } = await collectFiles(options, settings, library);\n fileVersionData = convertToFileTranslationData(files);\n }\n\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: undefined,\n branchData: undefined,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale][sourcePath] ?? null,\n options: settings,\n forceRetranslation: false, // force is not applicable for downloading staged translations\n forceDownload: options.force || options.forceDownload,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,eAAsB,eACpB,SACA,UACA,SACA;AACA,KAAI,CAAC,gBAAgB,SAAS,CAAE,QAAO,SAAS,EAAE;AAElD,KAAI,CAAC,QAAQ,UAAU,CAAC,oBAAoB,SAAS,CAAE,QAAO,SAAS,EAAE;AACzE,KAAI,CAAC,SAAS,MACZ,QAAO,gBAAgB,aAAa;CAGtC,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAGD,IAAI;AACJ,KAAI,SAAS,kBACX,mBAAkB,6BAA6B,SAAS;MACnD;EACL,MAAM,EAAE,UAAU,MAAM,aAAa,SAAS,UAAU,QAAQ;
|
|
1
|
+
{"version":3,"file":"download.js","names":[],"sources":["../../../src/cli/commands/download.ts"],"sourcesContent":["import { noFilesError, noVersionIdError } from '../../console/index.js';\nimport { SupportedLibraries, TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport { getStagedEntriesFromLockfile } from '../../fs/config/downloadedVersions.js';\nimport {\n runDownloadWorkflow,\n FileTranslationData,\n} from '../../workflows/download.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\nimport { convertToFileTranslationData } from '../../formats/files/convertToFileTranslationData.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\n\n// Downloads translations that were originally staged\n\n/**\n * Downloads translations that were originally staged\n * @param options - The options for the command\n * @param settings - The settings for the command\n */\nexport async function handleDownload(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries\n) {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n if (!settings.files) {\n return logErrorAndExit(noFilesError);\n }\n // Files\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n // Collect the hashes for all files we need to download\n let fileVersionData: FileTranslationData;\n if (settings.stageTranslations) {\n fileVersionData = getStagedEntriesFromLockfile(settings);\n } else {\n const { files } = await collectFiles(options, settings, library);\n // _versionId is only written by stage when an inline GTJSON template was\n // staged, so demand it only when a GTJSON is part of this download —\n // file-only projects never have one and don't need it. (Staged downloads\n // above resolve all versions from gt-lock.json.) omitConfigIds intentionally\n // never writes _versionId; in that mode the collected GTJSON template carries\n // its own content-derived versionId, so skip the guard rather than fail.\n if (\n !settings.omitConfigIds &&\n !settings._versionId &&\n files.some((file) => file.fileFormat === 'GTJSON')\n ) {\n return logErrorAndExit(noVersionIdError);\n }\n fileVersionData = convertToFileTranslationData(files);\n }\n\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: undefined,\n branchData: undefined,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale][sourcePath] ?? null,\n options: settings,\n forceRetranslation: false, // force is not applicable for downloading staged translations\n forceDownload: options.force || options.forceDownload,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,eAAsB,eACpB,SACA,UACA,SACA;AACA,KAAI,CAAC,gBAAgB,SAAS,CAAE,QAAO,SAAS,EAAE;AAElD,KAAI,CAAC,QAAQ,UAAU,CAAC,oBAAoB,SAAS,CAAE,QAAO,SAAS,EAAE;AACzE,KAAI,CAAC,SAAS,MACZ,QAAO,gBAAgB,aAAa;CAGtC,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAGD,IAAI;AACJ,KAAI,SAAS,kBACX,mBAAkB,6BAA6B,SAAS;MACnD;EACL,MAAM,EAAE,UAAU,MAAM,aAAa,SAAS,UAAU,QAAQ;AAOhE,MACE,CAAC,SAAS,iBACV,CAAC,SAAS,cACV,MAAM,MAAM,SAAS,KAAK,eAAe,SAAS,CAElD,QAAO,gBAAgB,iBAAiB;AAE1C,oBAAkB,6BAA6B,MAAM;;AAIvD,OAAM,oBAAoB;EACP;EACjB,SAAS,KAAA;EACT,YAAY,KAAA;EACZ,SAAS,SAAS;EAClB,iBAAiB,QAAQ;EACzB,oBAAoB,YAAY,WAC9B,YAAY,QAAQ,eAAe;EACrC,SAAS;EACT,oBAAoB;EACpB,eAAe,QAAQ,SAAS,QAAQ;EACzC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { logger } from "../../console/logger.js";
|
|
2
2
|
import { exitSync, logErrorAndExit } from "../../console/logging.js";
|
|
3
|
-
import { getRelative, readFile } from "../../fs/findFilepath.js";
|
|
3
|
+
import { getRelative, readBinaryFileBase64, readFile } from "../../fs/findFilepath.js";
|
|
4
4
|
import { noDefaultLocaleError } from "../../console/index.js";
|
|
5
5
|
import { validateJsonSchema } from "../../formats/json/utils.js";
|
|
6
6
|
import { extractJson } from "../../formats/json/extractJson.js";
|
|
@@ -10,6 +10,7 @@ import { hasValidCredentials } from "./utils/validation.js";
|
|
|
10
10
|
import { runPublishWorkflow } from "../../workflows/publish.js";
|
|
11
11
|
import { aggregateFiles } from "../../formats/files/aggregateFiles.js";
|
|
12
12
|
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { isBinaryFileFormat } from "generaltranslation/types";
|
|
13
14
|
//#region src/cli/commands/upload.ts
|
|
14
15
|
/**
|
|
15
16
|
* Sends multiple files to the API for translation
|
|
@@ -59,11 +60,12 @@ async function upload(settings) {
|
|
|
59
60
|
} else {
|
|
60
61
|
const translatedFileName = fileMapping[locale]?.[file.fileName];
|
|
61
62
|
if (translatedFileName && existsSync(translatedFileName)) {
|
|
62
|
-
const
|
|
63
|
+
const translationFormat = file.transformFormat ?? file.fileFormat;
|
|
64
|
+
const translatedContent = isBinaryFileFormat(translationFormat) ? readBinaryFileBase64(translatedFileName) : readFileSync(translatedFileName, "utf8");
|
|
63
65
|
translations.push({
|
|
64
66
|
content: translatedContent,
|
|
65
67
|
fileName: translatedFileName,
|
|
66
|
-
fileFormat:
|
|
68
|
+
fileFormat: translationFormat,
|
|
67
69
|
dataFormat: file.dataFormat,
|
|
68
70
|
locale,
|
|
69
71
|
fileId: file.fileId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","names":[],"sources":["../../../src/cli/commands/upload.ts"],"sourcesContent":["import { noDefaultLocaleError } from '../../console/index.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {
|
|
1
|
+
{"version":3,"file":"upload.js","names":[],"sources":["../../../src/cli/commands/upload.ts"],"sourcesContent":["import { noDefaultLocaleError } from '../../console/index.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n getRelative,\n readFile,\n readBinaryFileBase64,\n} from '../../fs/findFilepath.js';\nimport { isBinaryFileFormat } from 'generaltranslation/types';\nimport { Settings } from '../../types/index.js';\nimport { UploadOptions } from '../base.js';\nimport { extractJson } from '../../formats/json/extractJson.js';\nimport { validateJsonSchema } from '../../formats/json/utils.js';\nimport { runUploadFilesWorkflow } from '../../workflows/upload.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport type { FileToUpload } from 'generaltranslation/types';\nimport { hasValidCredentials } from './utils/validation.js';\nimport { runPublishWorkflow } from '../../workflows/publish.js';\nimport { aggregateFiles } from '../../formats/files/aggregateFiles.js';\n\n/**\n * Sends multiple files to the API for translation\n * @param settings - Translation options including API settings\n * @returns Promise that resolves when translation is complete\n */\nexport async function upload(\n settings: Settings & UploadOptions\n): Promise<void> {\n if (!settings.files) {\n return;\n }\n\n const additionalOptions = settings.options || {};\n const {\n resolvedPaths: filePaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n } = settings.files;\n\n // Reuse the same source aggregation path as translate/stage so source\n // parsing behavior stays consistent across commands.\n const { files: allFiles, publishMap } = await aggregateFiles(settings);\n const compositeJsonFiles = new Map<\n string,\n { filePath: string; content: string }\n >();\n\n if (filePaths.json) {\n const sourceFileNames = new Set(allFiles.map((file) => file.fileName));\n for (const filePath of filePaths.json) {\n const relativePath = getRelative(filePath);\n if (!sourceFileNames.has(relativePath)) continue;\n\n const jsonSchema = validateJsonSchema(additionalOptions, filePath);\n if (jsonSchema?.composite) {\n compositeJsonFiles.set(relativePath, {\n filePath,\n content: readFile(filePath),\n });\n }\n }\n }\n\n if (allFiles.length === 0) {\n logger.error(\n 'No files to upload were found. Check your configuration and try again.'\n );\n return;\n }\n\n if (!settings.defaultLocale) {\n return logErrorAndExit(noDefaultLocaleError);\n }\n if (!hasValidCredentials(settings)) return exitSync(1);\n\n const locales = settings.locales || [];\n // Create file mapping for all file types\n const fileMapping = createFileMapping(\n filePaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n locales,\n settings.defaultLocale\n );\n\n // construct object\n const uploadData = allFiles.map((file) => {\n const sourceFile: FileToUpload = { ...file };\n\n const translations: FileToUpload[] = [];\n const compositeInfo = compositeJsonFiles.get(file.fileName);\n\n for (const locale of locales) {\n if (compositeInfo) {\n // Composite JSON: extract translations from the same source file\n const extracted = extractJson(\n compositeInfo.content,\n compositeInfo.filePath,\n additionalOptions,\n locale,\n settings.defaultLocale\n );\n if (extracted) {\n translations.push({\n content: extracted,\n fileName: file.fileName,\n fileFormat: file.transformFormat ?? file.fileFormat,\n dataFormat: file.dataFormat,\n locale,\n fileId: file.fileId,\n versionId: file.versionId,\n });\n }\n } else {\n // Non-composite: look for separate translation files\n const translatedFileName = fileMapping[locale]?.[file.fileName];\n if (translatedFileName && existsSync(translatedFileName)) {\n const translationFormat = file.transformFormat ?? file.fileFormat;\n // Binary formats (e.g. LOTTIE zip bundles) travel base64-encoded;\n // decoding their bytes as UTF-8 would corrupt the archive.\n const translatedContent = isBinaryFileFormat(translationFormat)\n ? readBinaryFileBase64(translatedFileName)\n : readFileSync(translatedFileName, 'utf8');\n translations.push({\n content: translatedContent,\n fileName: translatedFileName,\n fileFormat: translationFormat,\n dataFormat: file.dataFormat,\n locale,\n fileId: file.fileId,\n versionId: file.versionId,\n });\n }\n }\n }\n return {\n source: sourceFile,\n translations,\n };\n });\n\n try {\n // Send all files in a single API call\n const { branchData } = await runUploadFilesWorkflow({\n files: uploadData,\n options: settings,\n });\n\n // Publish files to CDN if publish config exists\n await runPublishWorkflow(\n allFiles,\n publishMap,\n branchData.currentBranch.id,\n settings\n );\n } catch (error) {\n logErrorAndExit(`Error uploading files: ${error}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0BA,eAAsB,OACpB,UACe;AACf,KAAI,CAAC,SAAS,MACZ;CAGF,MAAM,oBAAoB,SAAS,WAAW,EAAE;CAChD,MAAM,EACJ,eAAe,WACf,kBACA,gBACA,qBACE,SAAS;CAIb,MAAM,EAAE,OAAO,UAAU,eAAe,MAAM,eAAe,SAAS;CACtE,MAAM,qCAAqB,IAAI,KAG5B;AAEH,KAAI,UAAU,MAAM;EAClB,MAAM,kBAAkB,IAAI,IAAI,SAAS,KAAK,SAAS,KAAK,SAAS,CAAC;AACtE,OAAK,MAAM,YAAY,UAAU,MAAM;GACrC,MAAM,eAAe,YAAY,SAAS;AAC1C,OAAI,CAAC,gBAAgB,IAAI,aAAa,CAAE;AAGxC,OADmB,mBAAmB,mBAAmB,SAC3C,EAAE,UACd,oBAAmB,IAAI,cAAc;IACnC;IACA,SAAS,SAAS,SAAS;IAC5B,CAAC;;;AAKR,KAAI,SAAS,WAAW,GAAG;AACzB,SAAO,MACL,yEACD;AACD;;AAGF,KAAI,CAAC,SAAS,cACZ,QAAO,gBAAgB,qBAAqB;AAE9C,KAAI,CAAC,oBAAoB,SAAS,CAAE,QAAO,SAAS,EAAE;CAEtD,MAAM,UAAU,SAAS,WAAW,EAAE;CAEtC,MAAM,cAAc,kBAClB,WACA,kBACA,gBACA,kBACA,SACA,SAAS,cACV;CAGD,MAAM,aAAa,SAAS,KAAK,SAAS;EACxC,MAAM,aAA2B,EAAE,GAAG,MAAM;EAE5C,MAAM,eAA+B,EAAE;EACvC,MAAM,gBAAgB,mBAAmB,IAAI,KAAK,SAAS;AAE3D,OAAK,MAAM,UAAU,QACnB,KAAI,eAAe;GAEjB,MAAM,YAAY,YAChB,cAAc,SACd,cAAc,UACd,mBACA,QACA,SAAS,cACV;AACD,OAAI,UACF,cAAa,KAAK;IAChB,SAAS;IACT,UAAU,KAAK;IACf,YAAY,KAAK,mBAAmB,KAAK;IACzC,YAAY,KAAK;IACjB;IACA,QAAQ,KAAK;IACb,WAAW,KAAK;IACjB,CAAC;SAEC;GAEL,MAAM,qBAAqB,YAAY,UAAU,KAAK;AACtD,OAAI,sBAAsB,WAAW,mBAAmB,EAAE;IACxD,MAAM,oBAAoB,KAAK,mBAAmB,KAAK;IAGvD,MAAM,oBAAoB,mBAAmB,kBAAkB,GAC3D,qBAAqB,mBAAmB,GACxC,aAAa,oBAAoB,OAAO;AAC5C,iBAAa,KAAK;KAChB,SAAS;KACT,UAAU;KACV,YAAY;KACZ,YAAY,KAAK;KACjB;KACA,QAAQ,KAAK;KACb,WAAW,KAAK;KACjB,CAAC;;;AAIR,SAAO;GACL,QAAQ;GACR;GACD;GACD;AAEF,KAAI;EAEF,MAAM,EAAE,eAAe,MAAM,uBAAuB;GAClD,OAAO;GACP,SAAS;GACV,CAAC;AAGF,QAAM,mBACJ,UACA,YACA,WAAW,cAAc,IACzB,SACD;UACM,OAAO;AACd,kBAAgB,0BAA0B,QAAQ"}
|
package/dist/cli/flags.js
CHANGED
|
@@ -12,7 +12,7 @@ function attachTranslateFlags(command) {
|
|
|
12
12
|
if (isNaN(parsedValue)) throw new Error("Invalid timeout: not a number.");
|
|
13
13
|
if (parsedValue < 0) throw new Error("Invalid timeout: must be a positive number.");
|
|
14
14
|
return parsedValue;
|
|
15
|
-
}, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations",
|
|
15
|
+
}, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations", true).option("--no-save-local", "Skip detecting and saving local edits before enqueuing translations").option("--publish", "Publish translations to the CDN", false).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).option("--experimental-hide-default-locale", "When localizing static locales, hide the default locale from the path", false).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).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).option("--experimental-localize-relative-assets", "Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.", false).option("--force", "Force a retranslation, invalidating all existing cached translations if they exist.", false).option("--force-download", "Force download and overwrite local files, bypassing gt-lock.json checks.", false).option("--omit-config-ids", "Do not write _versionId or _branchId to gt.config.json").option("--experimental-clear-locale-dirs", "Clear locale directories before downloading new translations", false).option("--branch <branch>", "Specify a custom branch to use for translations").option("--disable-branch-detection", "Disable additional branch detection and optimizations and use the manually specified branch", false).option("--enable-branching", "Enable branching for the project").option("--remote-name <name>", "Specify a custom remote name to use for branch detection", DEFAULT_GIT_REMOTE_NAME).option("--tag [value]", "Tag this translation run (auto-resolves from git if no value provided)").option("-m, --message <message>", "Message to attach to the translation tag");
|
|
16
16
|
return command;
|
|
17
17
|
}
|
|
18
18
|
/**
|