gtx-cli 2.1.5-alpha.2 → 2.1.5-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/api/checkFileTranslations.d.ts +4 -1
  2. package/dist/api/checkFileTranslations.js +35 -32
  3. package/dist/api/downloadFile.d.ts +9 -0
  4. package/dist/api/downloadFile.js +74 -0
  5. package/dist/api/downloadFileBatch.d.ts +0 -1
  6. package/dist/api/fetchTranslations.d.ts +7 -0
  7. package/dist/api/fetchTranslations.js +18 -0
  8. package/dist/api/sendFiles.d.ts +13 -2
  9. package/dist/api/sendFiles.js +8 -15
  10. package/dist/api/sendUpdates.d.ts +19 -0
  11. package/dist/api/sendUpdates.js +48 -0
  12. package/dist/api/waitForUpdates.d.ts +9 -0
  13. package/dist/api/waitForUpdates.js +89 -0
  14. package/dist/cli/base.d.ts +17 -5
  15. package/dist/cli/base.js +75 -48
  16. package/dist/cli/next.js +1 -0
  17. package/dist/cli/react.d.ts +5 -2
  18. package/dist/cli/react.js +153 -13
  19. package/dist/config/generateSettings.js +6 -11
  20. package/dist/console/index.d.ts +0 -1
  21. package/dist/console/index.js +0 -1
  22. package/dist/console/logging.d.ts +0 -1
  23. package/dist/console/logging.js +0 -3
  24. package/dist/formats/files/fileMapping.d.ts +1 -2
  25. package/dist/formats/files/fileMapping.js +0 -6
  26. package/dist/formats/files/translate.d.ts +13 -4
  27. package/dist/formats/files/translate.js +142 -30
  28. package/dist/formats/files/upload.js +75 -1
  29. package/dist/formats/gt/save.d.ts +2 -1
  30. package/dist/formats/gt/save.js +5 -2
  31. package/dist/fs/config/setupConfig.d.ts +0 -1
  32. package/dist/fs/config/setupConfig.js +0 -1
  33. package/dist/fs/config/updateConfig.d.ts +2 -1
  34. package/dist/fs/config/updateConfig.js +1 -1
  35. package/dist/fs/copyFile.d.ts +2 -1
  36. package/dist/translation/parse.d.ts +2 -2
  37. package/dist/translation/parse.js +6 -4
  38. package/dist/translation/stage.d.ts +5 -2
  39. package/dist/translation/stage.js +55 -13
  40. package/dist/translation/translate.d.ts +2 -0
  41. package/dist/translation/translate.js +18 -0
  42. package/dist/types/index.d.ts +4 -27
  43. package/dist/utils/flattenJsonFiles.d.ts +2 -2
  44. package/dist/utils/localizeStaticImports.d.ts +2 -2
  45. package/dist/utils/localizeStaticUrls.d.ts +6 -2
  46. package/dist/utils/localizeStaticUrls.js +119 -96
  47. package/package.json +2 -2
  48. package/dist/cli/commands/stage.d.ts +0 -5
  49. package/dist/cli/commands/stage.js +0 -100
  50. package/dist/cli/commands/translate.d.ts +0 -6
  51. package/dist/cli/commands/translate.js +0 -54
  52. package/dist/cli/flags.d.ts +0 -3
  53. package/dist/cli/flags.js +0 -37
  54. package/dist/fs/config/updateVersions.d.ts +0 -10
  55. package/dist/fs/config/updateVersions.js +0 -30
  56. package/dist/types/files.d.ts +0 -1
  57. package/dist/types/files.js +0 -1
  58. package/dist/utils/hash.d.ts +0 -6
  59. package/dist/utils/hash.js +0 -11
@@ -1,42 +1,38 @@
1
- import { logError } from '../../console/logging.js';
1
+ import { checkFileTranslations } from '../../api/checkFileTranslations.js';
2
+ import { sendFiles } from '../../api/sendFiles.js';
3
+ import { noSupportedFormatError, noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
4
+ import { logErrorAndExit, createSpinner, logError, logSuccess, } from '../../console/logging.js';
2
5
  import { getRelative, readFile } from '../../fs/findFilepath.js';
6
+ import chalk from 'chalk';
7
+ import { downloadFile } from '../../api/downloadFile.js';
8
+ import { downloadFileBatch } from '../../api/downloadFileBatch.js';
3
9
  import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
4
10
  import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
5
11
  import { parseJson } from '../json/parseJson.js';
12
+ import { createFileMapping } from './fileMapping.js';
6
13
  import parseYaml from '../yaml/parseYaml.js';
7
- import { determineLibrary } from '../../fs/determineFramework.js';
8
- export const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
9
- export async function aggregateFiles(settings) {
10
- // Aggregate all files to translate
14
+ const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
15
+ /**
16
+ * Sends multiple files to the API for translation
17
+ * @param filePaths - Resolved file paths for different file types
18
+ * @param placeholderPaths - Placeholder paths for translated files
19
+ * @param transformPaths - Transform paths for file naming
20
+ * @param dataFormat - Format of the data within the files
21
+ * @param options - Translation options including API settings
22
+ * @returns Promise that resolves when translation is complete
23
+ */
24
+ export async function translateFiles(filePaths, placeholderPaths, transformPaths, dataFormat = 'JSX', options) {
25
+ // Collect all files to translate
11
26
  const allFiles = [];
12
- if (!settings.files ||
13
- (Object.keys(settings.files.placeholderPaths).length === 1 &&
14
- settings.files.placeholderPaths.gt)) {
15
- return allFiles;
16
- }
17
- const { resolvedPaths: filePaths } = settings.files;
27
+ const additionalOptions = options.options || {};
18
28
  // Process JSON files
19
29
  if (filePaths.json) {
20
- const { library, additionalModules } = determineLibrary();
21
- // Determine dataFormat for JSONs
22
- let dataFormat;
23
- if (library === 'next-intl') {
24
- dataFormat = 'ICU';
25
- }
26
- else if (library === 'i18next') {
27
- if (additionalModules.includes('i18next-icu')) {
28
- dataFormat = 'ICU';
29
- }
30
- else {
31
- dataFormat = 'I18NEXT';
32
- }
33
- }
34
- else {
35
- dataFormat = 'JSX';
30
+ if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
31
+ logErrorAndExit(noSupportedFormatError);
36
32
  }
37
33
  const jsonFiles = filePaths.json.map((filePath) => {
38
34
  const content = readFile(filePath);
39
- const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
35
+ const parsedJson = parseJson(content, filePath, additionalOptions, options.defaultLocale);
40
36
  const relativePath = getRelative(filePath);
41
37
  return {
42
38
  content: parsedJson,
@@ -49,14 +45,18 @@ export async function aggregateFiles(settings) {
49
45
  }
50
46
  // Process YAML files
51
47
  if (filePaths.yaml) {
48
+ if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
49
+ logErrorAndExit(noSupportedFormatError);
50
+ }
52
51
  const yamlFiles = filePaths.yaml.map((filePath) => {
53
52
  const content = readFile(filePath);
54
- const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
53
+ const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, additionalOptions);
55
54
  const relativePath = getRelative(filePath);
56
55
  return {
57
56
  content: parsedYaml,
58
57
  fileName: relativePath,
59
58
  fileFormat,
59
+ dataFormat,
60
60
  };
61
61
  });
62
62
  allFiles.push(...yamlFiles);
@@ -73,6 +73,7 @@ export async function aggregateFiles(settings) {
73
73
  content: sanitizedContent,
74
74
  fileName: relativePath,
75
75
  fileFormat: fileType.toUpperCase(),
76
+ dataFormat,
76
77
  };
77
78
  });
78
79
  allFiles.push(...files);
@@ -80,6 +81,117 @@ export async function aggregateFiles(settings) {
80
81
  }
81
82
  if (allFiles.length === 0) {
82
83
  logError('No files to translate were found. Please check your configuration and try again.');
84
+ return;
85
+ }
86
+ if (options.dryRun) {
87
+ const fileNames = allFiles.map((file) => `- ${file.fileName}`).join('\n');
88
+ logSuccess(`Dry run: No files were sent to General Translation. Found files:\n${fileNames}`);
89
+ return;
90
+ }
91
+ // Validate required settings are present
92
+ if (!options.locales) {
93
+ logErrorAndExit(noLocalesError);
94
+ }
95
+ if (!options.defaultLocale) {
96
+ logErrorAndExit(noDefaultLocaleError);
97
+ }
98
+ if (!options.apiKey) {
99
+ logErrorAndExit(noApiKeyError);
100
+ }
101
+ if (options.apiKey.startsWith('gtx-dev-')) {
102
+ logErrorAndExit(devApiKeyError);
103
+ }
104
+ if (!options.projectId) {
105
+ logErrorAndExit(noProjectIdError);
106
+ }
107
+ try {
108
+ // Send all files in a single API call
109
+ const response = await sendFiles(allFiles, {
110
+ ...options,
111
+ publish: false,
112
+ wait: true,
113
+ });
114
+ const { data, locales, translations } = response;
115
+ // Create file mapping for all file types
116
+ const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales, options.defaultLocale);
117
+ // Process any translations that were already completed and returned with the initial response
118
+ const downloadStatus = await processInitialTranslations(translations, fileMapping, options);
119
+ // Check for remaining translations
120
+ await checkFileTranslations(data, locales, 600, (sourcePath, locale) => fileMapping[locale][sourcePath], downloadStatus, // Pass the already downloaded files to avoid duplicate requests
121
+ options);
122
+ }
123
+ catch (error) {
124
+ logErrorAndExit(`Error translating files: ${error}`);
125
+ }
126
+ }
127
+ /**
128
+ * Processes translations that were already completed and returned with the initial API response
129
+ * @returns Set of downloaded file+locale combinations
130
+ */
131
+ async function processInitialTranslations(translations = [], fileMapping, options) {
132
+ const downloadStatus = {
133
+ downloaded: new Set(),
134
+ failed: new Set(),
135
+ };
136
+ if (!translations || translations.length === 0) {
137
+ return downloadStatus;
138
+ }
139
+ // Filter for ready translations
140
+ const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
141
+ if (readyTranslations.length > 0) {
142
+ const spinner = createSpinner('dots');
143
+ spinner.start('Downloading translations...');
144
+ // Prepare batch download data
145
+ const batchFiles = readyTranslations
146
+ .map((translation) => {
147
+ const { locale, fileName, id } = translation;
148
+ const outputPath = fileMapping[locale][fileName];
149
+ if (!outputPath) {
150
+ return null;
151
+ }
152
+ return {
153
+ translationId: id,
154
+ inputPath: fileName,
155
+ outputPath,
156
+ fileLocale: `${fileName}:${locale}`,
157
+ locale,
158
+ };
159
+ })
160
+ .filter(Boolean);
161
+ if (batchFiles.length === 0 || batchFiles[0] === null) {
162
+ return downloadStatus;
163
+ }
164
+ // Use batch download if there are multiple files
165
+ if (batchFiles.length > 1) {
166
+ const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, inputPath, locale }) => ({
167
+ translationId,
168
+ outputPath,
169
+ inputPath,
170
+ locale,
171
+ })), options);
172
+ // Process results
173
+ batchFiles.forEach((file) => {
174
+ const { translationId, fileLocale } = file;
175
+ if (batchResult.successful.includes(translationId)) {
176
+ downloadStatus.downloaded.add(fileLocale);
177
+ }
178
+ else if (batchResult.failed.includes(translationId)) {
179
+ downloadStatus.failed.add(fileLocale);
180
+ }
181
+ });
182
+ }
183
+ else if (batchFiles.length === 1) {
184
+ // For a single file, use the original downloadFile method
185
+ const file = batchFiles[0];
186
+ const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
187
+ if (result) {
188
+ downloadStatus.downloaded.add(file.fileLocale);
189
+ }
190
+ else {
191
+ downloadStatus.failed.add(file.fileLocale);
192
+ }
193
+ }
194
+ spinner.stop(chalk.green('Downloaded cached translations'));
83
195
  }
84
- return allFiles;
196
+ return downloadStatus;
85
197
  }
@@ -1,6 +1,9 @@
1
1
  import { noSupportedFormatError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
2
- import { logErrorAndExit, logError } from '../../console/logging.js';
2
+ import { logErrorAndExit, createSpinner, logError, } from '../../console/logging.js';
3
3
  import { getRelative, readFile } from '../../fs/findFilepath.js';
4
+ import chalk from 'chalk';
5
+ import { downloadFile } from '../../api/downloadFile.js';
6
+ import { downloadFileBatch } from '../../api/downloadFileBatch.js';
4
7
  import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
5
8
  import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
6
9
  import { parseJson } from '../json/parseJson.js';
@@ -136,3 +139,74 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
136
139
  logErrorAndExit(`Error uploading files: ${error}`);
137
140
  }
138
141
  }
142
+ /**
143
+ * Processes translations that were already completed and returned with the initial API response
144
+ * @returns Set of downloaded file+locale combinations
145
+ */
146
+ async function processInitialTranslations(translations = [], fileMapping, options) {
147
+ const downloadStatus = {
148
+ downloaded: new Set(),
149
+ failed: new Set(),
150
+ };
151
+ if (!translations || translations.length === 0) {
152
+ return downloadStatus;
153
+ }
154
+ // Filter for ready translations
155
+ const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
156
+ if (readyTranslations.length > 0) {
157
+ const spinner = createSpinner('dots');
158
+ spinner.start('Downloading translations...');
159
+ // Prepare batch download data
160
+ const batchFiles = readyTranslations
161
+ .map((translation) => {
162
+ const { locale, fileName, id } = translation;
163
+ const outputPath = fileMapping[locale][fileName];
164
+ if (!outputPath) {
165
+ return null;
166
+ }
167
+ return {
168
+ translationId: id,
169
+ outputPath,
170
+ inputPath: fileName,
171
+ fileLocale: `${fileName}:${locale}`,
172
+ locale,
173
+ };
174
+ })
175
+ .filter(Boolean);
176
+ if (batchFiles.length === 0 || batchFiles[0] === null) {
177
+ return downloadStatus;
178
+ }
179
+ // Use batch download if there are multiple files
180
+ if (batchFiles.length > 1) {
181
+ const batchResult = await downloadFileBatch(batchFiles.map(({ translationId, outputPath, inputPath, locale }) => ({
182
+ translationId,
183
+ outputPath,
184
+ inputPath,
185
+ locale,
186
+ })), options);
187
+ // Process results
188
+ batchFiles.forEach((file) => {
189
+ const { translationId, fileLocale } = file;
190
+ if (batchResult.successful.includes(translationId)) {
191
+ downloadStatus.downloaded.add(fileLocale);
192
+ }
193
+ else if (batchResult.failed.includes(translationId)) {
194
+ downloadStatus.failed.add(fileLocale);
195
+ }
196
+ });
197
+ }
198
+ else if (batchFiles.length === 1) {
199
+ // For a single file, use the original downloadFile method
200
+ const file = batchFiles[0];
201
+ const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
202
+ if (result) {
203
+ downloadStatus.downloaded.add(file.fileLocale);
204
+ }
205
+ else {
206
+ downloadStatus.failed.add(file.fileLocale);
207
+ }
208
+ }
209
+ spinner.stop(chalk.green('Downloaded cached translations'));
210
+ }
211
+ return downloadStatus;
212
+ }
@@ -1,9 +1,10 @@
1
1
  import { RetrievedTranslations } from 'generaltranslation/types';
2
2
  import { ResolvedFiles } from '../../types/index.js';
3
+ import { DataFormat } from '../../types/data.js';
3
4
  /**
4
5
  * Saves translations to a local directory
5
6
  * @param translations - The translations to save
6
7
  * @param translationsDir - The directory to save the translations to
7
8
  * @param fileType - The file type to save the translations as (file extension)
8
9
  */
9
- export declare function saveTranslations(translations: RetrievedTranslations, placeholderPaths: ResolvedFiles): Promise<void>;
10
+ export declare function saveTranslations(translations: RetrievedTranslations, placeholderPaths: ResolvedFiles, dataFormat: DataFormat): Promise<void>;
@@ -9,7 +9,7 @@ import { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';
9
9
  * @param translationsDir - The directory to save the translations to
10
10
  * @param fileType - The file type to save the translations as (file extension)
11
11
  */
12
- export async function saveTranslations(translations, placeholderPaths) {
12
+ export async function saveTranslations(translations, placeholderPaths, dataFormat) {
13
13
  for (const translation of translations) {
14
14
  const locale = translation.locale;
15
15
  const translationFiles = resolveLocaleFiles(placeholderPaths, locale);
@@ -21,6 +21,9 @@ export async function saveTranslations(translations, placeholderPaths) {
21
21
  const translationData = translation.translation;
22
22
  // Ensure directory exists
23
23
  await fs.promises.mkdir(path.dirname(filepath), { recursive: true });
24
- await fs.promises.writeFile(filepath, JSON.stringify(translationData, null, 2));
24
+ // Handle different file types
25
+ if (dataFormat === 'JSX') {
26
+ await fs.promises.writeFile(filepath, JSON.stringify(translationData, null, 2));
27
+ }
25
28
  }
26
29
  }
@@ -13,5 +13,4 @@ export declare function createOrUpdateConfig(configFilepath: string, options: {
13
13
  files?: FilesOptions;
14
14
  framework?: SupportedFrameworks;
15
15
  baseUrl?: string;
16
- publish?: boolean;
17
16
  }): Promise<string>;
@@ -17,7 +17,6 @@ export async function createOrUpdateConfig(configFilepath, options) {
17
17
  ...(options.files && { files: options.files }),
18
18
  ...(options.framework && { framework: options.framework }),
19
19
  ...(options.baseUrl && { baseUrl: options.baseUrl }),
20
- ...(options.publish && { publish: options.publish }),
21
20
  };
22
21
  try {
23
22
  // if file exists
@@ -2,9 +2,10 @@
2
2
  * Update the config file version id, locales, and projectId (if necessary)
3
3
  * @param {Record<string, any>} configObject - The config object to write if the file does not exist.
4
4
  */
5
- export default function updateConfig({ configFilepath, projectId, _versionId, stageTranslations, }: {
5
+ export default function updateConfig({ configFilepath, projectId, _versionId, locales, stageTranslations, }: {
6
6
  configFilepath: string;
7
7
  projectId?: string;
8
8
  _versionId?: string;
9
+ locales?: string[];
9
10
  stageTranslations?: boolean;
10
11
  }): Promise<void>;
@@ -5,7 +5,7 @@ import { logError } from '../../console/logging.js';
5
5
  * Update the config file version id, locales, and projectId (if necessary)
6
6
  * @param {Record<string, any>} configObject - The config object to write if the file does not exist.
7
7
  */
8
- export default async function updateConfig({ configFilepath, projectId, _versionId, stageTranslations, }) {
8
+ export default async function updateConfig({ configFilepath, projectId, _versionId, locales, stageTranslations, }) {
9
9
  // Filter out empty string values from the config object
10
10
  const newContent = {
11
11
  ...(projectId && { projectId }),
@@ -1,7 +1,8 @@
1
1
  import { Settings } from '../types/index.js';
2
+ import { TranslateOptions } from '../cli/base.js';
2
3
  /**
3
4
  * Copy a file to target locale without translation
4
5
  *
5
6
  * This is a naive approach, does not allow for wild cards
6
7
  */
7
- export default function copyFile(settings: Settings): Promise<void>;
8
+ export default function copyFile(settings: Settings & TranslateOptions): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { Updates, TranslateFlags } from '../types/index.js';
1
+ import { Options, GenerateSourceOptions, Updates } from '../types/index.js';
2
2
  /**
3
3
  * Searches for gt-react or gt-next dictionary files and creates updates for them,
4
4
  * as well as inline updates for <T> tags and useGT()/getGT() calls
@@ -8,7 +8,7 @@ import { Updates, TranslateFlags } from '../types/index.js';
8
8
  * @param pkg - The package name
9
9
  * @returns An object containing the updates and errors
10
10
  */
11
- export declare function createUpdates(options: TranslateFlags, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next', validate: boolean): Promise<{
11
+ export declare function createUpdates(options: Options | GenerateSourceOptions, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next', validate: boolean): Promise<{
12
12
  updates: Updates;
13
13
  errors: string[];
14
14
  warnings: string[];
@@ -48,10 +48,12 @@ export async function createUpdates(options, sourceDictionary, pkg, validate) {
48
48
  }
49
49
  }
50
50
  // Scan through project for <T> tags
51
- const { updates: newUpdates, errors: newErrors, warnings: newWarnings, } = await createInlineUpdates(pkg, validate, options.src);
52
- errors = [...errors, ...newErrors];
53
- warnings = [...warnings, ...newWarnings];
54
- updates = [...updates, ...newUpdates];
51
+ if (options.inline) {
52
+ const { updates: newUpdates, errors: newErrors, warnings: newWarnings, } = await createInlineUpdates(pkg, validate, options.src);
53
+ errors = [...errors, ...newErrors];
54
+ warnings = [...warnings, ...newWarnings];
55
+ updates = [...updates, ...newUpdates];
56
+ }
55
57
  // Metadata addition and validation
56
58
  const idHashMap = new Map();
57
59
  const duplicateIds = new Set();
@@ -1,2 +1,5 @@
1
- import { Settings, TranslateFlags, Updates } from '../types/index.js';
2
- export declare function aggregateReactTranslations(options: TranslateFlags, settings: Settings, library: 'gt-react' | 'gt-next'): Promise<Updates>;
1
+ import { Options, Settings } from '../types/index.js';
2
+ export declare function stageProject(settings: Options & Settings, pkg: 'gt-react' | 'gt-next'): Promise<{
3
+ versionId: string;
4
+ locales: string[];
5
+ } | null>;
@@ -1,11 +1,14 @@
1
+ import { devApiKeyError } from '../console/index.js';
1
2
  import { logErrorAndExit } from '../console/logging.js';
2
3
  import chalk from 'chalk';
3
4
  import findFilepath from '../fs/findFilepath.js';
4
- import { logWarning, logError } from '../console/logging.js';
5
+ import { logSuccess, logWarning, logError } from '../console/logging.js';
5
6
  import { createUpdates } from './parse.js';
6
- export async function aggregateReactTranslations(options, settings, library) {
7
- if (!options.dictionary) {
8
- options.dictionary = findFilepath([
7
+ import { noLocalesError, noDefaultLocaleError, noProjectIdError, noApiKeyError, } from '../console/index.js';
8
+ import { sendUpdates } from '../api/sendUpdates.js';
9
+ export async function stageProject(settings, pkg) {
10
+ if (!settings.dictionary) {
11
+ settings.dictionary = findFilepath([
9
12
  './dictionary.js',
10
13
  './src/dictionary.js',
11
14
  './dictionary.json',
@@ -14,16 +17,29 @@ export async function aggregateReactTranslations(options, settings, library) {
14
17
  './src/dictionary.ts',
15
18
  ]);
16
19
  }
20
+ // Separate defaultLocale from locales
21
+ settings.locales = settings.locales.filter((locale) => locale !== settings.defaultLocale);
22
+ // validate timeout
23
+ const timeout = parseInt(settings.timeout);
24
+ if (isNaN(timeout) || timeout < 0) {
25
+ logErrorAndExit(`Invalid timeout: ${settings.timeout}. Must be a positive integer.`);
26
+ }
27
+ settings.timeout = timeout.toString();
17
28
  // ---- CREATING UPDATES ---- //
18
- const { updates, errors, warnings } = await createUpdates(options, options.dictionary, library, false);
29
+ const { updates, errors, warnings } = await createUpdates(settings, settings.dictionary, pkg, false);
19
30
  if (warnings.length > 0) {
20
- logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.\n` +
21
- warnings
22
- .map((warning) => chalk.yellow('• Warning: ') + chalk.white(warning))
23
- .join('\n')));
31
+ if (settings.suppressWarnings) {
32
+ logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content. ${chalk.gray('To view these warnings, re-run without the --suppress-warnings flag')}`));
33
+ }
34
+ else {
35
+ logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content. ${chalk.gray('To suppress these warnings, re-run with --suppress-warnings')}\n` +
36
+ warnings
37
+ .map((warning) => chalk.yellow('• Warning: ') + chalk.white(warning))
38
+ .join('\n')));
39
+ }
24
40
  }
25
41
  if (errors.length > 0) {
26
- if (options.ignoreErrors) {
42
+ if (settings.ignoreErrors) {
27
43
  logWarning(chalk.yellow(`Warning: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. These components will not be translated.\n` +
28
44
  errors
29
45
  .map((error) => chalk.yellow('• ') + chalk.white(error) + '\n')
@@ -36,9 +52,35 @@ export async function aggregateReactTranslations(options, settings, library) {
36
52
  .join('')));
37
53
  }
38
54
  }
55
+ if (settings.dryRun) {
56
+ logSuccess('Dry run: No translations were sent to General Translation.');
57
+ return null;
58
+ }
39
59
  if (updates.length == 0) {
40
- logError(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(library)}. Are you sure you're running this command in the right directory?`));
41
- return updates;
60
+ logError(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(pkg)}. Are you sure you're running this command in the right directory?`));
61
+ return null;
62
+ }
63
+ // Send updates to General Translation API
64
+ if (!settings.locales) {
65
+ logErrorAndExit(noLocalesError);
66
+ }
67
+ if (!settings.defaultLocale) {
68
+ logErrorAndExit(noDefaultLocaleError);
69
+ }
70
+ if (!settings.apiKey) {
71
+ logErrorAndExit(noApiKeyError);
72
+ }
73
+ if (settings.apiKey.startsWith('gtx-dev-')) {
74
+ logErrorAndExit(devApiKeyError);
75
+ }
76
+ if (!settings.projectId) {
77
+ logErrorAndExit(noProjectIdError);
42
78
  }
43
- return updates;
79
+ const updateResponse = await sendUpdates(updates, {
80
+ ...settings,
81
+ timeout: settings.timeout,
82
+ dataFormat: 'JSX',
83
+ }, pkg);
84
+ const { versionId, locales } = updateResponse;
85
+ return { versionId, locales };
44
86
  }
@@ -0,0 +1,2 @@
1
+ import { Options, Settings } from '../types/index.js';
2
+ export declare function translate(settings: Options & Settings, versionId: string): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { fetchTranslations } from '../api/fetchTranslations.js';
2
+ import { waitForUpdates } from '../api/waitForUpdates.js';
3
+ import { saveTranslations } from '../formats/gt/save.js';
4
+ import { isUsingLocalTranslations } from '../config/utils.js';
5
+ export async function translate(settings, versionId) {
6
+ // timeout was validated earlier
7
+ const startTime = Date.now();
8
+ const timeout = parseInt(settings.timeout) * 1000;
9
+ const result = await waitForUpdates(versionId, startTime, timeout);
10
+ if (!result) {
11
+ process.exit(1);
12
+ }
13
+ const translations = await fetchTranslations(versionId);
14
+ // Save translations to local directory if files.gt.output is provided
15
+ if (settings.files && isUsingLocalTranslations(settings)) {
16
+ await saveTranslations(translations, settings.files.placeholderPaths, 'JSX');
17
+ }
18
+ }
@@ -15,35 +15,13 @@ export type Options = {
15
15
  ignoreErrors: boolean;
16
16
  suppressWarnings: boolean;
17
17
  dryRun: boolean;
18
- timeout: number;
18
+ timeout: string;
19
19
  stageTranslations?: boolean;
20
20
  experimentalLocalizeStaticUrls?: boolean;
21
21
  experimentalHideDefaultLocale?: boolean;
22
22
  experimentalFlattenJsonFiles?: boolean;
23
23
  experimentalLocalizeStaticImports?: boolean;
24
24
  };
25
- export type TranslateFlags = {
26
- config?: string;
27
- apiKey?: string;
28
- projectId?: string;
29
- versionId?: string;
30
- jsconfig?: string;
31
- dictionary?: string;
32
- defaultLocale?: string;
33
- locales?: string[];
34
- ignoreErrors?: boolean;
35
- src?: string[];
36
- timeout: number;
37
- dryRun: boolean;
38
- stageTranslations?: boolean;
39
- publish?: boolean;
40
- experimentalLocalizeStaticUrls?: boolean;
41
- experimentalHideDefaultLocale?: boolean;
42
- experimentalFlattenJsonFiles?: boolean;
43
- experimentalLocalizeStaticImports?: boolean;
44
- excludeStaticUrls?: string[];
45
- excludeStaticImports?: string[];
46
- };
47
25
  export type WrapOptions = {
48
26
  src?: string[];
49
27
  config: string;
@@ -102,11 +80,10 @@ export type FilesOptions = {
102
80
  };
103
81
  export type Settings = {
104
82
  config: string;
105
- configDirectory: string;
106
83
  baseUrl: string;
107
84
  dashboardUrl: string;
108
- apiKey?: string;
109
- projectId?: string;
85
+ apiKey: string;
86
+ projectId: string;
110
87
  defaultLocale: string;
111
88
  locales: string[];
112
89
  files: {
@@ -115,7 +92,6 @@ export type Settings = {
115
92
  transformPaths: TransformFiles;
116
93
  } | undefined;
117
94
  stageTranslations: boolean;
118
- publish: boolean;
119
95
  _versionId?: string;
120
96
  version?: string;
121
97
  description?: string;
@@ -141,6 +117,7 @@ export type AdditionalOptions = {
141
117
  experimentalLocalizeStaticUrls?: boolean;
142
118
  experimentalHideDefaultLocale?: boolean;
143
119
  experimentalFlattenJsonFiles?: boolean;
120
+ baseDomain?: string;
144
121
  };
145
122
  export type JsonSchema = {
146
123
  preset?: 'mintlify';
@@ -1,2 +1,2 @@
1
- import { Settings } from '../types/index.js';
2
- export default function flattenJsonFiles(settings: Settings): Promise<void>;
1
+ import { Settings, Options } from '../types/index.js';
2
+ export default function flattenJsonFiles(settings: Omit<Settings & Options, 'ignoreErrors' | 'suppressWarnings' | 'timeout'>): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { Settings } from '../types/index.js';
1
+ import { Options, Settings } from '../types/index.js';
2
2
  /**
3
3
  * Localizes static imports in content files.
4
4
  * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
@@ -12,4 +12,4 @@ import { Settings } from '../types/index.js';
12
12
  * - Support more file types
13
13
  * - Support more complex paths
14
14
  */
15
- export default function localizeStaticImports(settings: Settings): Promise<void>;
15
+ export default function localizeStaticImports(settings: Omit<Settings & Options, 'ignoreErrors' | 'suppressWarnings' | 'timeout'>): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { Settings } from '../types/index.js';
1
+ import { Options, Settings } from '../types/index.js';
2
2
  /**
3
3
  * Localizes static urls in content files.
4
4
  * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
@@ -12,4 +12,8 @@ import { Settings } from '../types/index.js';
12
12
  * - Support more file types
13
13
  * - Support more complex paths
14
14
  */
15
- export default function localizeStaticUrls(settings: Settings): Promise<void>;
15
+ export default function localizeStaticUrls(settings: Omit<Settings & Options, 'ignoreErrors' | 'suppressWarnings' | 'timeout'>, targetLocales?: string[]): Promise<void>;
16
+ /**
17
+ * Main URL transformation function that delegates to specific scenarios
18
+ */
19
+ export declare function transformUrlPath(originalUrl: string, patternHead: string, targetLocale: string, defaultLocale: string, hideDefaultLocale: boolean): string | null;