gtx-cli 2.5.36 → 2.5.37

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.5.37
4
+
5
+ ### Patch Changes
6
+
7
+ - [#911](https://github.com/generaltranslation/gt/pull/911) [`6af64c0`](https://github.com/generaltranslation/gt/commit/6af64c04fa6e3d6332a206d9b68fa1a46de1c002) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Adding `experimentalCanonicalLocaleKeys` option to `gt.config.json`. It overrides alias configurations when setting keys in a JSON schema
8
+
9
+ - [#908](https://github.com/generaltranslation/gt/pull/908) [`1e7e52f`](https://github.com/generaltranslation/gt/commit/1e7e52f3a77835887ff187ffeb99d6e3dc2a9e6c) Thanks [@brian-lou](https://github.com/brian-lou)! - Fix timeout logic; Refactor upload command
10
+
11
+ - Updated dependencies [[`1e7e52f`](https://github.com/generaltranslation/gt/commit/1e7e52f3a77835887ff187ffeb99d6e3dc2a9e6c)]:
12
+ - generaltranslation@8.1.5
13
+
3
14
  ## 2.5.36
4
15
 
5
16
  ### Patch Changes
package/dist/cli/base.js CHANGED
@@ -130,14 +130,9 @@ export class BaseCLI {
130
130
  clearDownloaded();
131
131
  }
132
132
  setupUploadCommand() {
133
- this.program
133
+ attachTranslateFlags(this.program
134
134
  .command('upload')
135
- .description('Upload source files and translations to the General Translation platform')
136
- .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
137
- .option('--api-key <key>', 'API key for General Translation cloud service')
138
- .option('--project-id <id>', 'Project ID for the translation service')
139
- .option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
140
- .action(async (initOptions) => {
135
+ .description('Upload source files and translations to the General Translation platform')).action(async (initOptions) => {
141
136
  displayHeader('Starting upload...');
142
137
  const settings = await generateSettings(initOptions);
143
138
  const options = { ...initOptions, ...settings };
@@ -40,6 +40,7 @@ export async function collectFiles(options, settings, library) {
40
40
  formatMetadata: fileMetadata,
41
41
  fileId: TEMPLATE_FILE_ID,
42
42
  versionId: hashStringSync(JSON.stringify({ data: fileData, metadata: fileMetadata })),
43
+ locale: settings.defaultLocale,
43
44
  });
44
45
  }
45
46
  }
@@ -57,6 +57,7 @@ export async function aggregateFiles(settings) {
57
57
  fileName: relativePath,
58
58
  fileFormat: 'JSON',
59
59
  dataFormat,
60
+ locale: settings.defaultLocale,
60
61
  };
61
62
  })
62
63
  .filter((file) => {
@@ -91,6 +92,7 @@ export async function aggregateFiles(settings) {
91
92
  fileFormat,
92
93
  fileId: hashStringSync(relativePath),
93
94
  versionId: hashStringSync(parsedYaml),
95
+ locale: settings.defaultLocale,
94
96
  };
95
97
  })
96
98
  .filter((file) => {
@@ -124,6 +126,7 @@ export async function aggregateFiles(settings) {
124
126
  fileFormat: fileType.toUpperCase(),
125
127
  fileId: hashStringSync(relativePath),
126
128
  versionId: hashStringSync(content),
129
+ locale: settings.defaultLocale,
127
130
  };
128
131
  })
129
132
  .filter((file) => {
@@ -5,10 +5,11 @@ import { getRelative, readFile } from '../../fs/findFilepath.js';
5
5
  import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
6
6
  import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
7
7
  import { parseJson } from '../json/parseJson.js';
8
- import { uploadFiles } from '../../api/uploadFiles.js';
8
+ import { uploadFiles } from '../../workflow/upload.js';
9
9
  import { existsSync, readFileSync } from 'node:fs';
10
10
  import { createFileMapping } from './fileMapping.js';
11
11
  import parseYaml from '../yaml/parseYaml.js';
12
+ import { hashStringSync } from '../../utils/hash.js';
12
13
  const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
13
14
  /**
14
15
  * Sends multiple files to the API for translation
@@ -38,6 +39,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
38
39
  fileFormat: 'JSON',
39
40
  dataFormat,
40
41
  locale: options.defaultLocale,
42
+ fileId: hashStringSync(relativePath),
43
+ versionId: hashStringSync(parsedJson),
41
44
  };
42
45
  });
43
46
  allFiles.push(...jsonFiles);
@@ -57,6 +60,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
57
60
  fileFormat,
58
61
  dataFormat,
59
62
  locale: options.defaultLocale,
63
+ fileId: hashStringSync(relativePath),
64
+ versionId: hashStringSync(parsedYaml),
60
65
  };
61
66
  });
62
67
  allFiles.push(...yamlFiles);
@@ -75,6 +80,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
75
80
  fileFormat: fileType.toUpperCase(),
76
81
  dataFormat,
77
82
  locale: options.defaultLocale,
83
+ fileId: hashStringSync(relativePath),
84
+ versionId: hashStringSync(sanitizedContent),
78
85
  };
79
86
  });
80
87
  allFiles.push(...files);
@@ -107,6 +114,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
107
114
  fileFormat: file.fileFormat,
108
115
  dataFormat: file.dataFormat,
109
116
  locale: file.locale,
117
+ fileId: file.fileId,
118
+ versionId: file.versionId,
110
119
  };
111
120
  const translations = [];
112
121
  for (const locale of locales) {
@@ -115,10 +124,12 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
115
124
  const translatedContent = readFileSync(translatedFileName, 'utf8');
116
125
  translations.push({
117
126
  content: translatedContent,
118
- fileName: translatedFileName,
127
+ fileName: file.fileName,
119
128
  fileFormat: file.fileFormat,
120
129
  dataFormat: file.dataFormat,
121
130
  locale,
131
+ fileId: file.fileId,
132
+ versionId: file.versionId,
122
133
  });
123
134
  }
124
135
  }
@@ -5,6 +5,7 @@ import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPoin
5
5
  import { JSONPath } from 'jsonpath-plus';
6
6
  import { getLocaleProperties } from 'generaltranslation';
7
7
  import { replaceLocalePlaceholders } from '../utils.js';
8
+ import { gt } from '../../utils/gt.js';
8
9
  export function mergeJson(originalContent, inputPath, options, targets, defaultLocale, localeOrder = []) {
9
10
  const jsonSchema = validateJsonSchema(options, inputPath);
10
11
  if (!jsonSchema) {
@@ -18,6 +19,13 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
18
19
  logger.error(`Invalid JSON file: ${inputPath}`);
19
20
  return exitSync(1);
20
21
  }
22
+ const useCanonicalLocaleKeys = options?.experimentalCanonicalLocaleKeys ?? false;
23
+ const canonicalDefaultLocale = useCanonicalLocaleKeys
24
+ ? gt.resolveCanonicalLocale(defaultLocale)
25
+ : defaultLocale;
26
+ const canonicalLocaleOrder = useCanonicalLocaleKeys
27
+ ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))
28
+ : localeOrder;
21
29
  // Handle include
22
30
  if (jsonSchema.include) {
23
31
  const output = [];
@@ -59,7 +67,7 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
59
67
  return exitSync(1);
60
68
  }
61
69
  // Get source item for default locale
62
- const matchingDefaultLocaleItems = findMatchingItemArray(defaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
70
+ const matchingDefaultLocaleItems = findMatchingItemArray(canonicalDefaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
63
71
  if (!Object.keys(matchingDefaultLocaleItems).length) {
64
72
  logger.warn(`Matching sourceItems not found at path: ${sourceObjectPointer}. Please check your JSON file includes the key field. Skipping this target`);
65
73
  continue;
@@ -88,7 +96,9 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
88
96
  targetItems = {};
89
97
  }
90
98
  // 2. Track all array indecies to remove (will be overwritten)
91
- const targetItemsToRemove = findMatchingItemArray(target.targetLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
99
+ const targetItemsToRemove = findMatchingItemArray(useCanonicalLocaleKeys
100
+ ? gt.resolveCanonicalLocale(target.targetLocale)
101
+ : target.targetLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
92
102
  Object.values(targetItemsToRemove).forEach(({ index }) => indiciesToRemove.add(index));
93
103
  // 3. Merge matchingDefaultLocaleItems and targetItems
94
104
  const mergedItems = {
@@ -110,14 +120,21 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
110
120
  const defaultLocaleSourceItem = matchingDefaultLocaleItems[sourceItemPointer].sourceItem;
111
121
  const defaultLocaleKeyPointer = matchingDefaultLocaleItems[sourceItemPointer].keyPointer;
112
122
  const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);
113
- const { identifyingLocaleProperty: targetLocaleKeyProperty } = getSourceObjectOptionsArray(target.targetLocale, sourceObjectPointer, sourceObjectOptions);
123
+ const { identifyingLocaleProperty: targetLocaleKeyProperty } = getSourceObjectOptionsArray(useCanonicalLocaleKeys
124
+ ? gt.resolveCanonicalLocale(target.targetLocale)
125
+ : target.targetLocale, sourceObjectPointer, sourceObjectOptions);
114
126
  JSONPointer.set(mutatedSourceItem, defaultLocaleKeyPointer, targetLocaleKeyProperty);
115
127
  for (const [translatedKeyJsonPointer, translatedValue,] of Object.entries(targetItem || {})) {
128
+ const valueToSet = useCanonicalLocaleKeys &&
129
+ defaultLocaleKeyPointer &&
130
+ translatedKeyJsonPointer === defaultLocaleKeyPointer
131
+ ? targetLocaleKeyProperty
132
+ : translatedValue;
116
133
  try {
117
134
  const value = JSONPointer.get(mutatedSourceItem, translatedKeyJsonPointer);
118
135
  if (!value)
119
136
  continue;
120
- JSONPointer.set(mutatedSourceItem, translatedKeyJsonPointer, translatedValue);
137
+ JSONPointer.set(mutatedSourceItem, translatedKeyJsonPointer, valueToSet);
121
138
  }
122
139
  catch {
123
140
  /* empty */
@@ -137,7 +154,7 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
137
154
  const filteredSourceObjectValue = sourceObjectValue.filter((_, index) => !indiciesToRemove.has(index));
138
155
  // 10. Add all items to the original JSON
139
156
  filteredSourceObjectValue.push(...itemsToAdd);
140
- JSONPointer.set(mergedJson, sourceObjectPointer, sortByLocaleOrder(filteredSourceObjectValue, sourceObjectOptions, localeOrder, sourceObjectPointer, defaultLocale));
157
+ JSONPointer.set(mergedJson, sourceObjectPointer, sortByLocaleOrder(filteredSourceObjectValue, sourceObjectOptions, canonicalLocaleOrder, sourceObjectPointer, canonicalDefaultLocale));
141
158
  }
142
159
  else {
143
160
  // Validate type
@@ -146,7 +163,7 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
146
163
  return exitSync(1);
147
164
  }
148
165
  // Validate localeProperty
149
- const matchingDefaultLocaleItem = findMatchingItemObject(defaultLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
166
+ const matchingDefaultLocaleItem = findMatchingItemObject(canonicalDefaultLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
150
167
  // Validate source item exists
151
168
  if (!matchingDefaultLocaleItem.sourceItem) {
152
169
  logger.error(`Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`);
@@ -169,7 +186,9 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
169
186
  targetItems = {};
170
187
  }
171
188
  // 2. Find the source item for the target locale
172
- const matchingTargetItem = findMatchingItemObject(target.targetLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
189
+ const matchingTargetItem = findMatchingItemObject(useCanonicalLocaleKeys
190
+ ? gt.resolveCanonicalLocale(target.targetLocale)
191
+ : target.targetLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
173
192
  // If the target locale has a matching source item, use it to mutate the source item
174
193
  // Otherwise, fallback to the default locale source item
175
194
  const mutateSourceItem = structuredClone(defaultLocaleSourceItem);
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.5.36";
1
+ export declare const PACKAGE_VERSION = "2.5.37";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.5.36';
2
+ export const PACKAGE_VERSION = '2.5.37';
@@ -186,6 +186,7 @@ export type AdditionalOptions = {
186
186
  match: string;
187
187
  replace: string;
188
188
  }>;
189
+ experimentalCanonicalLocaleKeys?: boolean;
189
190
  };
190
191
  export type SharedStaticAssetsConfig = {
191
192
  include: string | string[];
@@ -4,7 +4,7 @@ import { GT } from 'generaltranslation';
4
4
  import { Settings } from '../types/index.js';
5
5
  import { BranchData } from '../types/branch.js';
6
6
  import type { FileReference } from 'generaltranslation/types';
7
- export declare class UploadStep extends WorkflowStep<{
7
+ export declare class UploadSourcesStep extends WorkflowStep<{
8
8
  files: FileToUpload[];
9
9
  branchData: BranchData;
10
10
  }, FileReference[]> {
@@ -1,7 +1,7 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
2
  import { logger } from '../console/logger.js';
3
3
  import chalk from 'chalk';
4
- export class UploadStep extends WorkflowStep {
4
+ export class UploadSourcesStep extends WorkflowStep {
5
5
  gt;
6
6
  settings;
7
7
  spinner = logger.createSpinner('dots');
@@ -62,6 +62,7 @@ export class UploadStep extends WorkflowStep {
62
62
  fileName: f.fileName,
63
63
  fileFormat: f.fileFormat,
64
64
  dataFormat: f.dataFormat,
65
+ locale: f.locale,
65
66
  })));
66
67
  this.spinner.stop(chalk.green('Files uploaded successfully'));
67
68
  return this.result;
@@ -0,0 +1,22 @@
1
+ import { WorkflowStep } from './Workflow.js';
2
+ import { GT } from 'generaltranslation';
3
+ import { Settings } from '../types/index.js';
4
+ import { BranchData } from '../types/branch.js';
5
+ import type { FileReference, FileToUpload } from 'generaltranslation/types';
6
+ type UploadTranslationsInput = {
7
+ files: {
8
+ source: FileToUpload;
9
+ translations: FileToUpload[];
10
+ }[];
11
+ branchData: BranchData;
12
+ };
13
+ export declare class UploadTranslationsStep extends WorkflowStep<UploadTranslationsInput, FileReference[]> {
14
+ private gt;
15
+ private settings;
16
+ private spinner;
17
+ private result;
18
+ constructor(gt: GT, settings: Settings);
19
+ run({ files, branchData, }: UploadTranslationsInput): Promise<FileReference[]>;
20
+ wait(): Promise<void>;
21
+ }
22
+ export {};
@@ -0,0 +1,71 @@
1
+ import { WorkflowStep } from './Workflow.js';
2
+ import { logger } from '../console/logger.js';
3
+ import chalk from 'chalk';
4
+ export class UploadTranslationsStep extends WorkflowStep {
5
+ gt;
6
+ settings;
7
+ spinner = logger.createSpinner('dots');
8
+ result = [];
9
+ constructor(gt, settings) {
10
+ super();
11
+ this.gt = gt;
12
+ this.settings = settings;
13
+ }
14
+ async run({ files, branchData, }) {
15
+ // Filter to only files that have translations
16
+ const filesWithTranslations = files.filter((f) => f.translations.length > 0);
17
+ if (filesWithTranslations.length === 0) {
18
+ logger.info('No translation files to upload... skipping upload translations step');
19
+ return [];
20
+ }
21
+ const totalTranslations = filesWithTranslations.reduce((acc, f) => acc + f.translations.length, 0);
22
+ this.spinner.start(`Syncing ${totalTranslations} translation file${totalTranslations !== 1 ? 's' : ''} with General Translation API...`);
23
+ // Build the query for existing translation files
24
+ const translatedFilesQuery = filesWithTranslations.flatMap((f) => f.translations.map((t) => ({
25
+ fileId: t.fileId,
26
+ versionId: t.versionId,
27
+ branchId: t.branchId ?? branchData.currentBranch.id,
28
+ locale: t.locale,
29
+ })));
30
+ // Query for existing translation files
31
+ const fileData = await this.gt.queryFileData({
32
+ translatedFiles: translatedFilesQuery,
33
+ });
34
+ // Build a map of existing translations: branchId:fileId:versionId:locale
35
+ const existingTranslationsMap = new Set();
36
+ fileData.translatedFiles?.forEach((f) => {
37
+ existingTranslationsMap.add(`${f.branchId}:${f.fileId}:${f.versionId}:${f.locale}`);
38
+ });
39
+ // Filter out translations that already exist
40
+ const filesToUpload = filesWithTranslations
41
+ .map((f) => {
42
+ const newTranslations = f.translations.filter((t) => {
43
+ const branchId = t.branchId ?? branchData.currentBranch.id;
44
+ const key = `${branchId}:${t.fileId}:${t.versionId}:${t.locale}`;
45
+ return !existingTranslationsMap.has(key);
46
+ });
47
+ return {
48
+ source: f.source,
49
+ translations: newTranslations,
50
+ };
51
+ })
52
+ .filter((f) => f.translations.length > 0);
53
+ const skippedCount = totalTranslations -
54
+ filesToUpload.reduce((acc, f) => acc + f.translations.length, 0);
55
+ if (filesToUpload.length === 0) {
56
+ this.spinner.stop(chalk.green(`All ${totalTranslations} translation files already uploaded`));
57
+ return [];
58
+ }
59
+ const uploadCount = filesToUpload.reduce((acc, f) => acc + f.translations.length, 0);
60
+ const response = await this.gt.uploadTranslations(filesToUpload, {
61
+ sourceLocale: this.settings.defaultLocale,
62
+ modelProvider: this.settings.modelProvider,
63
+ });
64
+ this.result = response.uploadedFiles;
65
+ this.spinner.stop(chalk.green(`Translation files synced successfully! Uploaded: (${uploadCount}), Skipped: (${skippedCount})`));
66
+ return this.result;
67
+ }
68
+ async wait() {
69
+ return;
70
+ }
71
+ }
@@ -1,6 +1,6 @@
1
1
  import { logErrorAndExit } from '../console/logging.js';
2
2
  import { gt } from '../utils/gt.js';
3
- import { UploadStep } from './UploadStep.js';
3
+ import { UploadSourcesStep } from './UploadSourcesStep.js';
4
4
  import { SetupStep } from './SetupStep.js';
5
5
  import { BranchStep } from './BranchStep.js';
6
6
  import { logCollectedFiles } from '../console/logging.js';
@@ -26,7 +26,7 @@ export async function setupProject(files, options, settings) {
26
26
  const timeoutMs = calculateTimeout(options.timeout);
27
27
  // Create workflow with steps
28
28
  const branchStep = new BranchStep(gt, settings);
29
- const uploadStep = new UploadStep(gt, settings);
29
+ const uploadStep = new UploadSourcesStep(gt, settings);
30
30
  const setupStep = new SetupStep(gt, settings, timeoutMs);
31
31
  // first run the branch step
32
32
  const branchData = await branchStep.run();
@@ -1,6 +1,6 @@
1
1
  import { logCollectedFiles, logErrorAndExit } from '../console/logging.js';
2
2
  import { gt } from '../utils/gt.js';
3
- import { UploadStep } from './UploadStep.js';
3
+ import { UploadSourcesStep } from './UploadSourcesStep.js';
4
4
  import { SetupStep } from './SetupStep.js';
5
5
  import { EnqueueStep } from './EnqueueStep.js';
6
6
  import { BranchStep } from './BranchStep.js';
@@ -27,7 +27,7 @@ export async function stageFiles(files, options, settings) {
27
27
  const timeoutMs = calculateTimeout(options.timeout);
28
28
  // Create workflow with steps
29
29
  const branchStep = new BranchStep(gt, settings);
30
- const uploadStep = new UploadStep(gt, settings);
30
+ const uploadStep = new UploadSourcesStep(gt, settings);
31
31
  const userEditDiffsStep = new UserEditDiffsStep(settings);
32
32
  const setupStep = new SetupStep(gt, settings, timeoutMs);
33
33
  const enqueueStep = new EnqueueStep(gt, settings, options.force);
@@ -0,0 +1,12 @@
1
+ import { Settings } from '../types/index.js';
2
+ import type { FileToUpload } from 'generaltranslation/types';
3
+ /**
4
+ * Uploads multiple files to the API using a workflow pattern
5
+ * @param files - Array of file objects to upload
6
+ * @param options - The options for the API call
7
+ * @returns The uploaded content or version ID
8
+ */
9
+ export declare function uploadFiles(files: {
10
+ source: FileToUpload;
11
+ translations: FileToUpload[];
12
+ }[], options: Settings): Promise<undefined>;
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ import { logger } from '../console/logger.js';
3
+ import { logErrorAndExit } from '../console/logging.js';
4
+ import { gt } from '../utils/gt.js';
5
+ import { BranchStep } from './BranchStep.js';
6
+ import { UploadSourcesStep } from './UploadSourcesStep.js';
7
+ import { UploadTranslationsStep } from './UploadTranslationsStep.js';
8
+ /**
9
+ * Uploads multiple files to the API using a workflow pattern
10
+ * @param files - Array of file objects to upload
11
+ * @param options - The options for the API call
12
+ * @returns The uploaded content or version ID
13
+ */
14
+ export async function uploadFiles(files, options) {
15
+ try {
16
+ logger.message(chalk.cyan('Files to upload:') +
17
+ '\n' +
18
+ files
19
+ .map((file) => ` - ${chalk.bold(file.source.fileName)}${file.translations.length > 0 ? ` -> ${file.translations.map((t) => t.locale).join(', ')}` : ''}`)
20
+ .join('\n'));
21
+ // Create workflow steps
22
+ const branchStep = new BranchStep(gt, options);
23
+ const uploadStep = new UploadSourcesStep(gt, options);
24
+ const uploadTranslationsStep = new UploadTranslationsStep(gt, options);
25
+ // Step 1: Resolve branch information
26
+ const branchData = await branchStep.run();
27
+ await branchStep.wait();
28
+ if (!branchData) {
29
+ return logErrorAndExit('Failed to resolve git branch information.');
30
+ }
31
+ await uploadStep.run({ files: files.map((f) => f.source), branchData });
32
+ await uploadStep.wait();
33
+ // Step 3: Upload translations (if any exist)
34
+ const filesWithTranslations = files.filter((f) => f.translations.length > 0);
35
+ if (filesWithTranslations.length > 0) {
36
+ await uploadTranslationsStep.run({
37
+ files: filesWithTranslations,
38
+ branchData,
39
+ });
40
+ await uploadTranslationsStep.wait();
41
+ }
42
+ logger.success('All files uploaded successfully');
43
+ }
44
+ catch (error) {
45
+ return logErrorAndExit('Failed to upload files. ' + error);
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.5.36",
3
+ "version": "2.5.37",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [
@@ -106,7 +106,7 @@
106
106
  "unified": "^11.0.5",
107
107
  "unist-util-visit": "^5.0.0",
108
108
  "yaml": "^2.8.0",
109
- "generaltranslation": "8.1.4"
109
+ "generaltranslation": "8.1.5"
110
110
  },
111
111
  "devDependencies": {
112
112
  "@babel/types": "^7.28.4",
@@ -1,27 +0,0 @@
1
- import { Settings } from '../types/index.js';
2
- import { DataFormat, FileFormat } from '../types/data.js';
3
- export type FileUpload = {
4
- content: string;
5
- fileName: string;
6
- fileFormat: FileFormat;
7
- dataFormat?: DataFormat;
8
- locale: string;
9
- };
10
- export type UploadData = {
11
- data: {
12
- source: FileUpload;
13
- translations: FileUpload[];
14
- }[];
15
- sourceLocale: string;
16
- modelProvider?: string;
17
- };
18
- /**
19
- * Uploads multiple files to the API
20
- * @param files - Array of file objects to upload
21
- * @param options - The options for the API call
22
- * @returns The uploaded content or version ID
23
- */
24
- export declare function uploadFiles(files: {
25
- source: FileUpload;
26
- translations: FileUpload[];
27
- }[], options: Settings): Promise<undefined>;
@@ -1,41 +0,0 @@
1
- import chalk from 'chalk';
2
- import { logger } from '../console/logger.js';
3
- import { exitSync } from '../console/logging.js';
4
- import { gt } from '../utils/gt.js';
5
- /**
6
- * Uploads multiple files to the API
7
- * @param files - Array of file objects to upload
8
- * @param options - The options for the API call
9
- * @returns The uploaded content or version ID
10
- */
11
- export async function uploadFiles(files, options) {
12
- logger.message(chalk.cyan('Files to upload:') +
13
- '\n' +
14
- files
15
- .map((file) => ` - ${chalk.bold(file.source.fileName)} -> ${file.translations
16
- .map((t) => t.locale)
17
- .join(', ')}`)
18
- .join('\n'));
19
- const spinner = logger.createSpinner('dots');
20
- spinner.start(`Uploading ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation...`);
21
- try {
22
- // Upload sources
23
- await gt.uploadSourceFiles(files, {
24
- ...options,
25
- sourceLocale: options.defaultLocale,
26
- });
27
- // Upload translations (if any exist)
28
- const withTranslations = files.filter((f) => f.translations.length > 0);
29
- if (withTranslations.length > 0) {
30
- await gt.uploadTranslations(withTranslations, {
31
- ...options,
32
- sourceLocale: options.defaultLocale, // optional, safe to include
33
- });
34
- }
35
- spinner.stop(chalk.green('Files uploaded successfully'));
36
- }
37
- catch {
38
- spinner.stop(chalk.red('An unexpected error occurred while uploading files'));
39
- return exitSync(1);
40
- }
41
- }