gtx-cli 2.5.0-alpha.0 → 2.5.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/api/collectUserEditDiffs.d.ts +2 -7
  3. package/dist/api/collectUserEditDiffs.js +33 -78
  4. package/dist/api/downloadFileBatch.d.ts +11 -10
  5. package/dist/api/downloadFileBatch.js +120 -127
  6. package/dist/api/saveLocalEdits.js +18 -15
  7. package/dist/cli/base.js +1 -1
  8. package/dist/cli/commands/stage.d.ts +8 -2
  9. package/dist/cli/commands/stage.js +25 -7
  10. package/dist/cli/commands/translate.d.ts +4 -2
  11. package/dist/cli/commands/translate.js +5 -6
  12. package/dist/cli/flags.js +4 -1
  13. package/dist/config/generateSettings.js +10 -0
  14. package/dist/console/colors.d.ts +0 -1
  15. package/dist/console/colors.js +0 -3
  16. package/dist/console/index.d.ts +0 -6
  17. package/dist/console/index.js +2 -13
  18. package/dist/console/logging.d.ts +1 -1
  19. package/dist/console/logging.js +3 -4
  20. package/dist/formats/files/translate.d.ts +2 -2
  21. package/dist/formats/files/translate.js +31 -5
  22. package/dist/fs/config/downloadedVersions.d.ts +10 -3
  23. package/dist/fs/config/downloadedVersions.js +8 -0
  24. package/dist/fs/config/updateVersions.d.ts +2 -1
  25. package/dist/git/branches.d.ts +7 -0
  26. package/dist/git/branches.js +88 -0
  27. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.d.ts +1 -2
  28. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.js +6 -30
  29. package/dist/react/jsx/evaluateJsx.d.ts +6 -5
  30. package/dist/react/jsx/evaluateJsx.js +4 -32
  31. package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
  32. package/dist/react/jsx/trimJsxStringChildren.js +122 -0
  33. package/dist/react/jsx/utils/constants.d.ts +0 -2
  34. package/dist/react/jsx/utils/constants.js +2 -11
  35. package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
  36. package/dist/react/jsx/utils/parseJsx.js +259 -0
  37. package/dist/react/jsx/utils/parseStringFunction.js +141 -4
  38. package/dist/react/parse/createInlineUpdates.js +70 -19
  39. package/dist/types/branch.d.ts +14 -0
  40. package/dist/types/branch.js +1 -0
  41. package/dist/types/data.d.ts +1 -1
  42. package/dist/types/files.d.ts +7 -0
  43. package/dist/types/index.d.ts +7 -0
  44. package/dist/utils/SpinnerManager.d.ts +30 -0
  45. package/dist/utils/SpinnerManager.js +73 -0
  46. package/dist/utils/gitDiff.js +18 -16
  47. package/dist/workflow/BranchStep.d.ts +13 -0
  48. package/dist/workflow/BranchStep.js +131 -0
  49. package/dist/workflow/DownloadStep.d.ts +19 -0
  50. package/dist/workflow/DownloadStep.js +127 -0
  51. package/dist/workflow/EnqueueStep.d.ts +15 -0
  52. package/dist/workflow/EnqueueStep.js +33 -0
  53. package/dist/workflow/PollJobsStep.d.ts +31 -0
  54. package/dist/workflow/PollJobsStep.js +286 -0
  55. package/dist/workflow/SetupStep.d.ts +16 -0
  56. package/dist/workflow/SetupStep.js +72 -0
  57. package/dist/workflow/UploadStep.d.ts +21 -0
  58. package/dist/workflow/UploadStep.js +72 -0
  59. package/dist/workflow/UserEditDiffsStep.d.ts +11 -0
  60. package/dist/workflow/UserEditDiffsStep.js +30 -0
  61. package/dist/workflow/Workflow.d.ts +4 -0
  62. package/dist/workflow/Workflow.js +2 -0
  63. package/dist/workflow/download.d.ts +22 -0
  64. package/dist/workflow/download.js +104 -0
  65. package/dist/workflow/stage.d.ts +14 -0
  66. package/dist/workflow/stage.js +76 -0
  67. package/package.json +3 -4
  68. package/dist/api/checkFileTranslations.d.ts +0 -23
  69. package/dist/api/checkFileTranslations.js +0 -281
  70. package/dist/api/sendFiles.d.ts +0 -17
  71. package/dist/api/sendFiles.js +0 -127
  72. package/dist/api/sendUserEdits.d.ts +0 -19
  73. package/dist/api/sendUserEdits.js +0 -15
  74. package/dist/cli/commands/edits.d.ts +0 -8
  75. package/dist/cli/commands/edits.js +0 -32
  76. package/dist/react/jsx/utils/buildImportMap.d.ts +0 -9
  77. package/dist/react/jsx/utils/buildImportMap.js +0 -30
  78. package/dist/react/jsx/utils/getPathsAndAliases.d.ts +0 -17
  79. package/dist/react/jsx/utils/getPathsAndAliases.js +0 -89
  80. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.d.ts +0 -6
  81. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.js +0 -199
  82. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.d.ts +0 -13
  83. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.js +0 -42
  84. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.d.ts +0 -5
  85. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.js +0 -69
  86. package/dist/react/jsx/utils/jsxParsing/parseJsx.d.ts +0 -60
  87. package/dist/react/jsx/utils/jsxParsing/parseJsx.js +0 -949
  88. package/dist/react/jsx/utils/jsxParsing/parseTProps.d.ts +0 -8
  89. package/dist/react/jsx/utils/jsxParsing/parseTProps.js +0 -47
  90. package/dist/react/jsx/utils/jsxParsing/types.d.ts +0 -48
  91. package/dist/react/jsx/utils/jsxParsing/types.js +0 -34
  92. package/dist/react/jsx/utils/resolveImportPath.d.ts +0 -11
  93. package/dist/react/jsx/utils/resolveImportPath.js +0 -111
@@ -0,0 +1,11 @@
1
+ import type { FileReference } from 'generaltranslation/types';
2
+ import { WorkflowStep } from './Workflow.js';
3
+ import { Settings } from '../types/index.js';
4
+ export declare class UserEditDiffsStep extends WorkflowStep<FileReference[], FileReference[]> {
5
+ private settings;
6
+ private spinner;
7
+ private completed;
8
+ constructor(settings: Settings);
9
+ run(files: FileReference[]): Promise<FileReference[]>;
10
+ wait(): Promise<void>;
11
+ }
@@ -0,0 +1,30 @@
1
+ import { WorkflowStep } from './Workflow.js';
2
+ import { createSpinner } from '../console/logging.js';
3
+ import chalk from 'chalk';
4
+ import { collectAndSendUserEditDiffs } from '../api/collectUserEditDiffs.js';
5
+ export class UserEditDiffsStep extends WorkflowStep {
6
+ settings;
7
+ spinner = createSpinner('dots');
8
+ completed = false;
9
+ constructor(settings) {
10
+ super();
11
+ this.settings = settings;
12
+ }
13
+ async run(files) {
14
+ this.spinner.start('Updating translations...');
15
+ try {
16
+ await collectAndSendUserEditDiffs(files, this.settings);
17
+ this.completed = true;
18
+ }
19
+ catch {
20
+ // Non-fatal; keep going to enqueue
21
+ this.completed = true;
22
+ }
23
+ return files;
24
+ }
25
+ async wait() {
26
+ if (this.completed) {
27
+ this.spinner.stop(chalk.green('Updated translations'));
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,4 @@
1
+ export declare abstract class WorkflowStep<TInput = void, TOutput = void> {
2
+ abstract run(input: TInput): Promise<TOutput>;
3
+ abstract wait(): Promise<void>;
4
+ }
@@ -0,0 +1,2 @@
1
+ export class WorkflowStep {
2
+ }
@@ -0,0 +1,22 @@
1
+ import { Settings } from '../types/index.js';
2
+ import { EnqueueFilesResult } from 'generaltranslation/types';
3
+ import { BranchData } from '../types/branch.js';
4
+ export type FileTranslationData = {
5
+ [fileId: string]: {
6
+ versionId: string;
7
+ fileName: string;
8
+ };
9
+ };
10
+ /**
11
+ * Checks the status of translations and downloads them using a workflow pattern
12
+ * @param fileVersionData - Mapping of file IDs to their version and name information
13
+ * @param jobData - Optional job data from enqueue operation
14
+ * @param locales - The locales to wait for
15
+ * @param timeoutDuration - The timeout duration for the wait in seconds
16
+ * @param resolveOutputPath - Function to resolve the output path for a given source path and locale
17
+ * @param options - Settings configuration
18
+ * @param forceRetranslation - Whether to force retranslation
19
+ * @param forceDownload - Whether to force download even if file exists
20
+ * @returns True if all translations are downloaded successfully, false otherwise
21
+ */
22
+ export declare function downloadTranslations(fileVersionData: FileTranslationData, jobData: EnqueueFilesResult | undefined, branchData: BranchData | undefined, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string | null, options: Settings, forceRetranslation?: boolean, forceDownload?: boolean): Promise<boolean>;
@@ -0,0 +1,104 @@
1
+ import path from 'node:path';
2
+ import { gt } from '../utils/gt.js';
3
+ import { clearLocaleDirs } from '../fs/clearLocaleDirs.js';
4
+ import { PollTranslationJobsStep } from './PollJobsStep.js';
5
+ import { DownloadTranslationsStep } from './DownloadStep.js';
6
+ import { logErrorAndExit } from '../console/logging.js';
7
+ import { BranchStep } from './BranchStep.js';
8
+ /**
9
+ * Checks the status of translations and downloads them using a workflow pattern
10
+ * @param fileVersionData - Mapping of file IDs to their version and name information
11
+ * @param jobData - Optional job data from enqueue operation
12
+ * @param locales - The locales to wait for
13
+ * @param timeoutDuration - The timeout duration for the wait in seconds
14
+ * @param resolveOutputPath - Function to resolve the output path for a given source path and locale
15
+ * @param options - Settings configuration
16
+ * @param forceRetranslation - Whether to force retranslation
17
+ * @param forceDownload - Whether to force download even if file exists
18
+ * @returns True if all translations are downloaded successfully, false otherwise
19
+ */
20
+ export async function downloadTranslations(fileVersionData, jobData, branchData, locales, timeoutDuration, resolveOutputPath, options, forceRetranslation, forceDownload) {
21
+ if (!branchData) {
22
+ // Run the branch step
23
+ const branchStep = new BranchStep(gt, options);
24
+ const branchResult = await branchStep.run();
25
+ await branchStep.wait();
26
+ if (!branchResult) {
27
+ logErrorAndExit('Failed to resolve git branch information.');
28
+ }
29
+ branchData = branchResult;
30
+ }
31
+ // Prepare the query data
32
+ const fileQueryData = prepareFileQueryData(fileVersionData, locales, branchData);
33
+ // Clear translated files before any downloads (if enabled)
34
+ if (options.options?.experimentalClearLocaleDirs === true &&
35
+ fileQueryData.length > 0) {
36
+ const translatedFiles = new Set(fileQueryData
37
+ .map((file) => {
38
+ const outputPath = resolveOutputPath(file.fileName, file.locale);
39
+ // Only clear if the output path is different from the source (i.e., there's a transform)
40
+ return outputPath !== null && outputPath !== file.fileName
41
+ ? outputPath
42
+ : null;
43
+ })
44
+ .filter((path) => path !== null));
45
+ // Derive cwd from config path
46
+ const cwd = path.dirname(options.config);
47
+ await clearLocaleDirs(translatedFiles, locales, options.options?.clearLocaleDirsExclude, cwd);
48
+ }
49
+ // Initialize download status
50
+ const fileTracker = {
51
+ completed: new Map(),
52
+ inProgress: new Map(),
53
+ failed: new Map(),
54
+ skipped: new Map(),
55
+ };
56
+ // Step 1: Poll translation jobs if jobData exists
57
+ if (jobData) {
58
+ const pollStep = new PollTranslationJobsStep(gt);
59
+ const pollResult = await pollStep.run({
60
+ fileTracker,
61
+ fileQueryData,
62
+ jobData,
63
+ timeoutDuration,
64
+ forceRetranslation,
65
+ });
66
+ await pollStep.wait();
67
+ if (!pollResult.success) {
68
+ return false;
69
+ }
70
+ }
71
+ else {
72
+ for (const file of fileQueryData) {
73
+ // Staging - assume all files are completed
74
+ fileTracker.completed.set(`${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`, file);
75
+ }
76
+ }
77
+ // Step 2: Download translations
78
+ const downloadStep = new DownloadTranslationsStep(gt, options);
79
+ const downloadResult = await downloadStep.run({
80
+ fileTracker,
81
+ resolveOutputPath,
82
+ forceDownload,
83
+ });
84
+ await downloadStep.wait();
85
+ return downloadResult;
86
+ }
87
+ /**
88
+ * Prepares the file query data from input data and locales
89
+ */
90
+ function prepareFileQueryData(fileVersionData, locales, branchData) {
91
+ const fileQueryData = [];
92
+ for (const fileId in fileVersionData) {
93
+ for (const locale of locales) {
94
+ fileQueryData.push({
95
+ versionId: fileVersionData[fileId].versionId,
96
+ fileName: fileVersionData[fileId].fileName,
97
+ fileId,
98
+ locale,
99
+ branchId: branchData.currentBranch.id,
100
+ });
101
+ }
102
+ }
103
+ return fileQueryData;
104
+ }
@@ -0,0 +1,14 @@
1
+ import { Settings, TranslateFlags } from '../types/index.js';
2
+ import { EnqueueFilesResult, FileToUpload } from 'generaltranslation/types';
3
+ import { BranchData } from '../types/branch.js';
4
+ /**
5
+ * Sends multiple files for translation to the API using a workflow pattern
6
+ * @param files - Array of file objects to translate
7
+ * @param options - The options for the API call
8
+ * @param settings - Settings configuration
9
+ * @returns The translated content or version ID
10
+ */
11
+ export declare function stageFiles(files: FileToUpload[], options: TranslateFlags, settings: Settings): Promise<{
12
+ branchData: BranchData;
13
+ enqueueResult: EnqueueFilesResult;
14
+ }>;
@@ -0,0 +1,76 @@
1
+ import chalk from 'chalk';
2
+ import { logErrorAndExit, logMessage } from '../console/logging.js';
3
+ import { gt } from '../utils/gt.js';
4
+ import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
5
+ import { UploadStep } from './UploadStep.js';
6
+ import { SetupStep } from './SetupStep.js';
7
+ import { EnqueueStep } from './EnqueueStep.js';
8
+ import { BranchStep } from './BranchStep.js';
9
+ import { UserEditDiffsStep } from './UserEditDiffsStep.js';
10
+ /**
11
+ * Helper: Calculate timeout with validation
12
+ */
13
+ function calculateTimeout(timeout) {
14
+ const value = timeout !== undefined ? Number(timeout) : 600;
15
+ return (Number.isFinite(value) ? value : 600) * 1000;
16
+ }
17
+ /**
18
+ * Helper: Log files to be translated
19
+ */
20
+ function logFilesToTranslate(files) {
21
+ logMessage(chalk.cyan('Files found in project:') +
22
+ '\n' +
23
+ files
24
+ .map((file) => {
25
+ if (file.fileName === TEMPLATE_FILE_NAME) {
26
+ return `- <React Elements>`;
27
+ }
28
+ return `- ${file.fileName}`;
29
+ })
30
+ .join('\n'));
31
+ }
32
+ /**
33
+ * Sends multiple files for translation to the API using a workflow pattern
34
+ * @param files - Array of file objects to translate
35
+ * @param options - The options for the API call
36
+ * @param settings - Settings configuration
37
+ * @returns The translated content or version ID
38
+ */
39
+ export async function stageFiles(files, options, settings) {
40
+ try {
41
+ // Log files to be translated
42
+ logFilesToTranslate(files);
43
+ // Calculate timeout for setup step
44
+ const timeoutMs = calculateTimeout(options.timeout);
45
+ // Create workflow with steps
46
+ const branchStep = new BranchStep(gt, settings);
47
+ const uploadStep = new UploadStep(gt, settings);
48
+ const userEditDiffsStep = new UserEditDiffsStep(settings);
49
+ const setupStep = new SetupStep(gt, settings, timeoutMs);
50
+ const enqueueStep = new EnqueueStep(gt, settings, options.force);
51
+ // first run the branch step
52
+ const branchData = await branchStep.run();
53
+ await branchStep.wait();
54
+ if (!branchData) {
55
+ logErrorAndExit('Failed to resolve git branch information.');
56
+ }
57
+ // then run the upload step
58
+ const uploadedFiles = await uploadStep.run({ files, branchData });
59
+ await uploadStep.wait();
60
+ // optionally run the user edit diffs step
61
+ if (options?.saveLocal) {
62
+ await userEditDiffsStep.run(uploadedFiles);
63
+ await userEditDiffsStep.wait();
64
+ }
65
+ // then run the setup step
66
+ await setupStep.run(uploadedFiles);
67
+ await setupStep.wait();
68
+ // then run the enqueue step
69
+ const enqueueResult = await enqueueStep.run(uploadedFiles);
70
+ await enqueueStep.wait();
71
+ return { branchData, enqueueResult };
72
+ }
73
+ catch (error) {
74
+ logErrorAndExit('Failed to send files for translation. ' + error);
75
+ }
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.5.0-alpha.0",
3
+ "version": "2.5.0-alpha.2",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [
@@ -67,7 +67,7 @@
67
67
  "@babel/parser": "^7.25.7",
68
68
  "@babel/plugin-transform-react-jsx": "^7.25.9",
69
69
  "@babel/traverse": "^7.25.7",
70
- "@clack/prompts": "^1.0.0-alpha.1",
70
+ "@clack/prompts": "^1.0.0-alpha.6",
71
71
  "@formatjs/icu-messageformat-parser": "^2.11.4",
72
72
  "chalk": "^5.4.1",
73
73
  "commander": "^12.1.0",
@@ -84,7 +84,6 @@
84
84
  "mdast-util-find-and-replace": "^3.0.2",
85
85
  "micromatch": "^4.0.8",
86
86
  "open": "^10.1.1",
87
- "ora": "^8.2.0",
88
87
  "remark-frontmatter": "^5.0.0",
89
88
  "remark-mdx": "^3.1.0",
90
89
  "remark-parse": "^11.0.0",
@@ -94,7 +93,7 @@
94
93
  "unified": "^11.0.5",
95
94
  "unist-util-visit": "^5.0.0",
96
95
  "yaml": "^2.8.0",
97
- "generaltranslation": "7.9.0"
96
+ "generaltranslation": "8.0.0-alpha.1"
98
97
  },
99
98
  "devDependencies": {
100
99
  "@babel/types": "^7.28.4",
@@ -1,23 +0,0 @@
1
- import { Settings } from '../types/index.js';
2
- export type CheckFileTranslationData = {
3
- [key: string]: {
4
- versionId: string;
5
- fileName: string;
6
- };
7
- };
8
- /**
9
- * Checks the status of translations for a given version ID
10
- * @param apiKey - The API key for the General Translation API
11
- * @param baseUrl - The base URL for the General Translation API
12
- * @param versionId - The version ID of the project
13
- * @param locales - The locales to wait for
14
- * @param startTime - The start time of the wait
15
- * @param timeoutDuration - The timeout duration for the wait in seconds
16
- * @returns True if all translations are deployed, false otherwise
17
- */
18
- export declare function checkFileTranslations(data: {
19
- [key: string]: {
20
- versionId: string;
21
- fileName: string;
22
- };
23
- }, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string | null, options: Settings, forceRetranslation?: boolean, forceDownload?: boolean): Promise<boolean>;
@@ -1,281 +0,0 @@
1
- import chalk from 'chalk';
2
- import { createOraSpinner, logError } from '../console/logging.js';
3
- import { getLocaleProperties } from 'generaltranslation';
4
- import { downloadFileBatch } from './downloadFileBatch.js';
5
- import { gt } from '../utils/gt.js';
6
- import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
7
- import { clearLocaleDirs } from '../fs/clearLocaleDirs.js';
8
- import path from 'node:path';
9
- /**
10
- * Checks the status of translations for a given version ID
11
- * @param apiKey - The API key for the General Translation API
12
- * @param baseUrl - The base URL for the General Translation API
13
- * @param versionId - The version ID of the project
14
- * @param locales - The locales to wait for
15
- * @param startTime - The start time of the wait
16
- * @param timeoutDuration - The timeout duration for the wait in seconds
17
- * @returns True if all translations are deployed, false otherwise
18
- */
19
- export async function checkFileTranslations(data, locales, timeoutDuration, resolveOutputPath, options, forceRetranslation, forceDownload) {
20
- const startTime = Date.now();
21
- console.log();
22
- const spinner = await createOraSpinner();
23
- const spinnerMessage = forceRetranslation
24
- ? 'Waiting for retranslation...'
25
- : 'Waiting for translation...';
26
- spinner.start(spinnerMessage);
27
- // Initialize the query data
28
- const fileQueryData = prepareFileQueryData(data, locales);
29
- // Clear translated files before any downloads (if enabled)
30
- if (options.options?.experimentalClearLocaleDirs === true &&
31
- fileQueryData.length > 0) {
32
- const translatedFiles = new Set(fileQueryData
33
- .map((file) => {
34
- const outputPath = resolveOutputPath(file.fileName, file.locale);
35
- // Only clear if the output path is different from the source (i.e., there's a transform)
36
- return outputPath !== null && outputPath !== file.fileName
37
- ? outputPath
38
- : null;
39
- })
40
- .filter((path) => path !== null));
41
- // Derive cwd from config path
42
- const cwd = path.dirname(options.config);
43
- await clearLocaleDirs(translatedFiles, locales, options.options?.clearLocaleDirsExclude, cwd);
44
- }
45
- const downloadStatus = {
46
- downloaded: new Set(),
47
- failed: new Set(),
48
- skipped: new Set(),
49
- };
50
- // Do first check immediately, but skip if force retranslation is enabled
51
- if (!forceRetranslation) {
52
- const initialCheck = await checkTranslationDeployment(fileQueryData, downloadStatus, spinner, resolveOutputPath, options, forceDownload);
53
- if (initialCheck) {
54
- spinner.succeed(chalk.green('Files translated!'));
55
- return true;
56
- }
57
- }
58
- // Calculate time until next 5-second interval since startTime
59
- const msUntilNextInterval = Math.max(0, 5000 - ((Date.now() - startTime) % 5000));
60
- return new Promise((resolve) => {
61
- let intervalCheck;
62
- // Start the interval aligned with the original request time
63
- setTimeout(() => {
64
- intervalCheck = setInterval(async () => {
65
- const isDeployed = await checkTranslationDeployment(fileQueryData, downloadStatus, spinner, resolveOutputPath, options, forceDownload);
66
- const elapsed = Date.now() - startTime;
67
- if (isDeployed || elapsed >= timeoutDuration * 1000) {
68
- clearInterval(intervalCheck);
69
- if (isDeployed) {
70
- spinner.succeed(chalk.green('All files translated!'));
71
- resolve(true);
72
- }
73
- else {
74
- spinner.fail(chalk.red('Timed out waiting for translations'));
75
- resolve(false);
76
- }
77
- }
78
- }, 5000);
79
- }, msUntilNextInterval);
80
- });
81
- }
82
- /**
83
- * Prepares the file query data from input data and locales
84
- */
85
- function prepareFileQueryData(data, locales) {
86
- const fileQueryData = [];
87
- for (const file in data) {
88
- for (const locale of locales) {
89
- fileQueryData.push({
90
- versionId: data[file].versionId,
91
- fileName: data[file].fileName,
92
- locale,
93
- });
94
- }
95
- }
96
- return fileQueryData;
97
- }
98
- /**
99
- * Generates a formatted status text showing translation progress
100
- * @param downloadedFiles - Set of downloaded file+locale combinations
101
- * @param fileQueryData - Array of file query data objects
102
- * @returns Formatted status text
103
- */
104
- function generateStatusSuffixText(downloadStatus, fileQueryData) {
105
- // Simple progress indicator
106
- const progressText = chalk.green(`[${downloadStatus.downloaded.size +
107
- downloadStatus.failed.size +
108
- downloadStatus.skipped.size}/${fileQueryData.length}]`) + ` translations completed`;
109
- // Get terminal height to adapt our output
110
- const terminalHeight = process.stdout.rows || 24; // Default to 24 if undefined
111
- // If terminal is very small, just show the basic progress
112
- if (terminalHeight < 6) {
113
- return `${progressText}`;
114
- }
115
- const newSuffixText = [`${progressText}`];
116
- // Organize data by filename
117
- const fileStatus = new Map();
118
- // Initialize with all files and locales from fileQueryData
119
- for (const item of fileQueryData) {
120
- if (!fileStatus.has(item.fileName)) {
121
- fileStatus.set(item.fileName, {
122
- completed: new Set(),
123
- pending: new Set([item.locale]),
124
- failed: new Set(),
125
- skipped: new Set(),
126
- });
127
- }
128
- else {
129
- fileStatus.get(item.fileName)?.pending.add(item.locale);
130
- }
131
- }
132
- // Mark which ones are completed or failed
133
- for (const fileLocale of downloadStatus.downloaded) {
134
- const [fileName, locale] = fileLocale.split(':');
135
- const status = fileStatus.get(fileName);
136
- if (status) {
137
- status.pending.delete(locale);
138
- status.completed.add(locale);
139
- }
140
- }
141
- for (const fileLocale of downloadStatus.failed) {
142
- const [fileName, locale] = fileLocale.split(':');
143
- const status = fileStatus.get(fileName);
144
- if (status) {
145
- status.pending.delete(locale);
146
- status.failed.add(locale);
147
- }
148
- }
149
- for (const fileLocale of downloadStatus.skipped) {
150
- const [fileName, locale] = fileLocale.split(':');
151
- const status = fileStatus.get(fileName);
152
- if (status) {
153
- status.pending.delete(locale);
154
- status.skipped.add(locale);
155
- }
156
- }
157
- // Calculate how many files we can show based on terminal height
158
- const filesArray = Array.from(fileStatus.entries());
159
- const maxFilesToShow = Math.min(filesArray.length, terminalHeight - 3 // Header + progress + buffer
160
- );
161
- // Display each file with its status on a single line
162
- for (let i = 0; i < maxFilesToShow; i++) {
163
- const [fileName, status] = filesArray[i];
164
- // Create condensed locale status
165
- const localeStatuses = [];
166
- // Add completed locales
167
- if (status.completed.size > 0) {
168
- const completedCodes = Array.from(status.completed)
169
- .map((locale) => getLocaleProperties(locale).code)
170
- .join(', ');
171
- localeStatuses.push(chalk.green(`${completedCodes}`));
172
- }
173
- // Add (translated but not downloaded) skipped locales
174
- if (status.skipped.size > 0) {
175
- const skippedCodes = Array.from(status.skipped)
176
- .map((locale) => getLocaleProperties(locale).code)
177
- .join(', ');
178
- localeStatuses.push(chalk.green(`${skippedCodes}`));
179
- }
180
- // Add failed locales
181
- if (status.failed.size > 0) {
182
- const failedCodes = Array.from(status.failed)
183
- .map((locale) => getLocaleProperties(locale).code)
184
- .join(', ');
185
- localeStatuses.push(chalk.red(`${failedCodes}`));
186
- }
187
- // Add pending locales
188
- if (status.pending.size > 0) {
189
- const pendingCodes = Array.from(status.pending)
190
- .map((locale) => getLocaleProperties(locale).code)
191
- .join(', ');
192
- localeStatuses.push(chalk.yellow(`${pendingCodes}`));
193
- }
194
- // Format the line
195
- const prettyFileName = fileName === TEMPLATE_FILE_NAME ? '<React Elements>' : fileName;
196
- newSuffixText.push(`${chalk.bold(prettyFileName)} [${localeStatuses.join(', ')}]`);
197
- }
198
- // If we couldn't show all files, add an indicator
199
- if (filesArray.length > maxFilesToShow) {
200
- newSuffixText.push(`... and ${filesArray.length - maxFilesToShow} more files`);
201
- }
202
- return newSuffixText.join('\n');
203
- }
204
- /**
205
- * Checks translation status and downloads ready files
206
- */
207
- async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner, resolveOutputPath, options, forceDownload) {
208
- try {
209
- // Only query for files that haven't been downloaded yet
210
- const currentQueryData = fileQueryData.filter((item) => !downloadStatus.downloaded.has(`${item.fileName}:${item.locale}`) &&
211
- !downloadStatus.failed.has(`${item.fileName}:${item.locale}`) &&
212
- !downloadStatus.skipped.has(`${item.fileName}:${item.locale}`));
213
- // If all files have been downloaded, we're done
214
- if (currentQueryData.length === 0) {
215
- return true;
216
- }
217
- // Check for translations
218
- const responseData = await gt.checkFileTranslations(currentQueryData);
219
- const translations = responseData.translations || [];
220
- // Filter for ready translations
221
- const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
222
- if (readyTranslations.length > 0) {
223
- // Build version map by fileName:locale for this batch
224
- const versionMap = new Map(fileQueryData.map((item) => [
225
- `${item.fileName}:${gt.resolveAliasLocale(item.locale)}`,
226
- item.versionId,
227
- ]));
228
- // Prepare batch download data
229
- const batchFiles = readyTranslations
230
- .map((translation) => {
231
- const locale = gt.resolveAliasLocale(translation.locale);
232
- const fileName = translation.fileName;
233
- const translationId = translation.id;
234
- const outputPath = resolveOutputPath(fileName, locale);
235
- // Skip downloading GTJSON files that are not in the files configuration
236
- if (outputPath === null) {
237
- downloadStatus.skipped.add(`${fileName}:${locale}`);
238
- return null;
239
- }
240
- return {
241
- translationId,
242
- inputPath: fileName,
243
- outputPath,
244
- locale,
245
- fileLocale: `${fileName}:${locale}`,
246
- fileId: translation.fileId,
247
- versionId: versionMap.get(`${fileName}:${locale}`),
248
- };
249
- })
250
- .filter((file) => file !== null);
251
- if (batchFiles.length > 0) {
252
- const batchResult = await downloadFileBatch(batchFiles, options, 3, 1000, Boolean(forceDownload));
253
- // Process results
254
- batchFiles.forEach((file) => {
255
- const { translationId, fileLocale } = file;
256
- if (batchResult.successful.includes(translationId)) {
257
- downloadStatus.downloaded.add(fileLocale);
258
- }
259
- else if (batchResult.failed.includes(translationId)) {
260
- downloadStatus.failed.add(fileLocale);
261
- }
262
- });
263
- }
264
- }
265
- // Force a refresh of the spinner display
266
- const statusText = generateStatusSuffixText(downloadStatus, fileQueryData);
267
- // Clear and reapply the suffix to force a refresh
268
- spinner.text = statusText;
269
- // If all files have been downloaded, we're done
270
- if (downloadStatus.downloaded.size +
271
- downloadStatus.failed.size +
272
- downloadStatus.skipped.size ===
273
- fileQueryData.length) {
274
- return true;
275
- }
276
- }
277
- catch (error) {
278
- logError(chalk.red('Error checking translation status: ') + error);
279
- }
280
- return false;
281
- }
@@ -1,17 +0,0 @@
1
- import { Settings, TranslateFlags } from '../types/index.js';
2
- import { CompletedFileTranslationData, FileToTranslate } from 'generaltranslation/types';
3
- export type SendFilesResult = {
4
- data: Record<string, {
5
- fileName: string;
6
- versionId: string;
7
- }>;
8
- locales: string[];
9
- translations: CompletedFileTranslationData[];
10
- };
11
- /**
12
- * Sends multiple files for translation to the API
13
- * @param files - Array of file objects to translate
14
- * @param options - The options for the API call
15
- * @returns The translated content or version ID
16
- */
17
- export declare function sendFiles(files: FileToTranslate[], options: TranslateFlags, settings: Settings): Promise<SendFilesResult>;