gtx-cli 2.1.5-alpha.5 → 2.1.5-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/checkFileTranslations.d.ts +1 -4
- package/dist/api/checkFileTranslations.js +32 -35
- package/dist/api/downloadFileBatch.d.ts +1 -0
- package/dist/api/sendFiles.d.ts +2 -13
- package/dist/api/sendFiles.js +15 -8
- package/dist/cli/base.d.ts +5 -17
- package/dist/cli/base.js +57 -75
- package/dist/cli/commands/stage.d.ts +5 -0
- package/dist/cli/commands/stage.js +100 -0
- package/dist/cli/commands/translate.d.ts +6 -0
- package/dist/cli/commands/translate.js +58 -0
- package/dist/cli/flags.d.ts +3 -0
- package/dist/cli/flags.js +37 -0
- package/dist/cli/next.js +0 -1
- package/dist/cli/react.d.ts +2 -5
- package/dist/cli/react.js +13 -153
- package/dist/config/generateSettings.js +11 -6
- package/dist/console/index.d.ts +1 -0
- package/dist/console/index.js +1 -0
- package/dist/console/logging.d.ts +1 -0
- package/dist/console/logging.js +3 -0
- package/dist/formats/files/fileMapping.d.ts +2 -1
- package/dist/formats/files/fileMapping.js +6 -0
- package/dist/formats/files/translate.d.ts +4 -13
- package/dist/formats/files/translate.js +30 -142
- package/dist/formats/files/upload.js +1 -75
- package/dist/formats/gt/save.d.ts +1 -2
- package/dist/formats/gt/save.js +2 -5
- package/dist/fs/config/setupConfig.d.ts +1 -0
- package/dist/fs/config/setupConfig.js +1 -0
- package/dist/fs/config/updateConfig.d.ts +1 -2
- package/dist/fs/config/updateConfig.js +1 -1
- package/dist/fs/config/updateVersions.d.ts +10 -0
- package/dist/fs/config/updateVersions.js +30 -0
- package/dist/fs/copyFile.d.ts +1 -2
- package/dist/translation/parse.d.ts +2 -2
- package/dist/translation/parse.js +4 -6
- package/dist/translation/stage.d.ts +2 -5
- package/dist/translation/stage.js +13 -55
- package/dist/types/files.d.ts +1 -0
- package/dist/types/files.js +1 -0
- package/dist/types/index.d.ts +27 -3
- package/dist/utils/flattenJsonFiles.d.ts +2 -2
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/hash.js +11 -0
- package/dist/utils/localizeStaticImports.d.ts +2 -2
- package/dist/utils/localizeStaticUrls.d.ts +2 -2
- package/dist/utils/localizeStaticUrls.js +13 -15
- package/package.json +2 -2
- package/dist/api/downloadFile.d.ts +0 -9
- package/dist/api/downloadFile.js +0 -74
- package/dist/api/fetchTranslations.d.ts +0 -7
- package/dist/api/fetchTranslations.js +0 -18
- package/dist/api/sendUpdates.d.ts +0 -19
- package/dist/api/sendUpdates.js +0 -48
- package/dist/api/waitForUpdates.d.ts +0 -9
- package/dist/api/waitForUpdates.js +0 -89
- package/dist/translation/translate.d.ts +0 -2
- package/dist/translation/translate.js +0 -18
|
@@ -20,7 +20,4 @@ export declare function checkFileTranslations(data: {
|
|
|
20
20
|
versionId: string;
|
|
21
21
|
fileName: string;
|
|
22
22
|
};
|
|
23
|
-
}, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string,
|
|
24
|
-
downloaded: Set<string>;
|
|
25
|
-
failed: Set<string>;
|
|
26
|
-
}, options: Settings): Promise<boolean>;
|
|
23
|
+
}, locales: string[], timeoutDuration: number, resolveOutputPath: (sourcePath: string, locale: string) => string | null, options: Settings): Promise<boolean>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { createOraSpinner, logError } from '../console/logging.js';
|
|
3
3
|
import { getLocaleProperties } from 'generaltranslation';
|
|
4
|
-
import { downloadFile } from './downloadFile.js';
|
|
5
4
|
import { downloadFileBatch } from './downloadFileBatch.js';
|
|
6
5
|
import { gt } from '../utils/gt.js';
|
|
6
|
+
import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
|
|
7
7
|
/**
|
|
8
8
|
* Checks the status of translations for a given version ID
|
|
9
9
|
* @param apiKey - The API key for the General Translation API
|
|
@@ -14,13 +14,18 @@ import { gt } from '../utils/gt.js';
|
|
|
14
14
|
* @param timeoutDuration - The timeout duration for the wait in seconds
|
|
15
15
|
* @returns True if all translations are deployed, false otherwise
|
|
16
16
|
*/
|
|
17
|
-
export async function checkFileTranslations(data, locales, timeoutDuration, resolveOutputPath,
|
|
17
|
+
export async function checkFileTranslations(data, locales, timeoutDuration, resolveOutputPath, options) {
|
|
18
18
|
const startTime = Date.now();
|
|
19
19
|
console.log();
|
|
20
20
|
const spinner = await createOraSpinner();
|
|
21
21
|
spinner.start('Waiting for translation...');
|
|
22
22
|
// Initialize the query data
|
|
23
23
|
const fileQueryData = prepareFileQueryData(data, locales);
|
|
24
|
+
const downloadStatus = {
|
|
25
|
+
downloaded: new Set(),
|
|
26
|
+
failed: new Set(),
|
|
27
|
+
skipped: new Set(),
|
|
28
|
+
};
|
|
24
29
|
// Do first check immediately
|
|
25
30
|
const initialCheck = await checkTranslationDeployment(fileQueryData, downloadStatus, spinner, resolveOutputPath, options);
|
|
26
31
|
if (initialCheck) {
|
|
@@ -146,7 +151,8 @@ function generateStatusSuffixText(downloadStatus, fileQueryData) {
|
|
|
146
151
|
localeStatuses.push(chalk.yellow(`${pendingCodes}`));
|
|
147
152
|
}
|
|
148
153
|
// Format the line
|
|
149
|
-
|
|
154
|
+
const prettyFileName = fileName === TEMPLATE_FILE_NAME ? '<React Elements>' : fileName;
|
|
155
|
+
newSuffixText.push(`${chalk.bold(prettyFileName)} [${localeStatuses.join(', ')}]`);
|
|
150
156
|
}
|
|
151
157
|
// If we couldn't show all files, add an indicator
|
|
152
158
|
if (filesArray.length > maxFilesToShow) {
|
|
@@ -161,7 +167,8 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
161
167
|
try {
|
|
162
168
|
// Only query for files that haven't been downloaded yet
|
|
163
169
|
const currentQueryData = fileQueryData.filter((item) => !downloadStatus.downloaded.has(`${item.fileName}:${item.locale}`) &&
|
|
164
|
-
!downloadStatus.failed.has(`${item.fileName}:${item.locale}`)
|
|
170
|
+
!downloadStatus.failed.has(`${item.fileName}:${item.locale}`) &&
|
|
171
|
+
!downloadStatus.skipped.has(`${item.fileName}:${item.locale}`));
|
|
165
172
|
// If all files have been downloaded, we're done
|
|
166
173
|
if (currentQueryData.length === 0) {
|
|
167
174
|
return true;
|
|
@@ -173,11 +180,17 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
173
180
|
const readyTranslations = translations.filter((translation) => translation.isReady && translation.fileName);
|
|
174
181
|
if (readyTranslations.length > 0) {
|
|
175
182
|
// Prepare batch download data
|
|
176
|
-
const batchFiles = readyTranslations
|
|
183
|
+
const batchFiles = readyTranslations
|
|
184
|
+
.map((translation) => {
|
|
177
185
|
const locale = translation.locale;
|
|
178
186
|
const fileName = translation.fileName;
|
|
179
187
|
const translationId = translation.id;
|
|
180
188
|
const outputPath = resolveOutputPath(fileName, locale);
|
|
189
|
+
// Skip downloading GTJSON files that are not in the files configuration
|
|
190
|
+
if (outputPath === null) {
|
|
191
|
+
downloadStatus.skipped.add(`${fileName}:${locale}`);
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
181
194
|
return {
|
|
182
195
|
translationId,
|
|
183
196
|
inputPath: fileName,
|
|
@@ -185,44 +198,28 @@ async function checkTranslationDeployment(fileQueryData, downloadStatus, spinner
|
|
|
185
198
|
locale,
|
|
186
199
|
fileLocale: `${fileName}:${locale}`,
|
|
187
200
|
};
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})), options);
|
|
197
|
-
// Process results
|
|
198
|
-
batchFiles.forEach((file) => {
|
|
199
|
-
const { translationId, fileLocale } = file;
|
|
200
|
-
if (batchResult.successful.includes(translationId)) {
|
|
201
|
-
downloadStatus.downloaded.add(fileLocale);
|
|
202
|
-
}
|
|
203
|
-
else if (batchResult.failed.includes(translationId)) {
|
|
204
|
-
downloadStatus.failed.add(fileLocale);
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
else if (batchFiles.length === 1) {
|
|
209
|
-
// For a single file, use the original downloadFile method
|
|
210
|
-
const file = batchFiles[0];
|
|
211
|
-
const result = await downloadFile(file.translationId, file.outputPath, file.inputPath, file.locale, options);
|
|
212
|
-
if (result) {
|
|
213
|
-
downloadStatus.downloaded.add(file.fileLocale);
|
|
201
|
+
})
|
|
202
|
+
.filter((file) => file !== null);
|
|
203
|
+
const batchResult = await downloadFileBatch(batchFiles, options);
|
|
204
|
+
// Process results
|
|
205
|
+
batchFiles.forEach((file) => {
|
|
206
|
+
const { translationId, fileLocale } = file;
|
|
207
|
+
if (batchResult.successful.includes(translationId)) {
|
|
208
|
+
downloadStatus.downloaded.add(fileLocale);
|
|
214
209
|
}
|
|
215
|
-
else {
|
|
216
|
-
downloadStatus.failed.add(
|
|
210
|
+
else if (batchResult.failed.includes(translationId)) {
|
|
211
|
+
downloadStatus.failed.add(fileLocale);
|
|
217
212
|
}
|
|
218
|
-
}
|
|
213
|
+
});
|
|
219
214
|
}
|
|
220
215
|
// Force a refresh of the spinner display
|
|
221
216
|
const statusText = generateStatusSuffixText(downloadStatus, fileQueryData);
|
|
222
217
|
// Clear and reapply the suffix to force a refresh
|
|
223
218
|
spinner.text = statusText;
|
|
224
219
|
// If all files have been downloaded, we're done
|
|
225
|
-
if (downloadStatus.downloaded.size +
|
|
220
|
+
if (downloadStatus.downloaded.size +
|
|
221
|
+
downloadStatus.failed.size +
|
|
222
|
+
downloadStatus.skipped.size ===
|
|
226
223
|
fileQueryData.length) {
|
|
227
224
|
return true;
|
|
228
225
|
}
|
package/dist/api/sendFiles.d.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { Settings } from '../types/index.js';
|
|
1
|
+
import { Settings, TranslateFlags } from '../types/index.js';
|
|
2
2
|
import { CompletedFileTranslationData, FileToTranslate } from 'generaltranslation/types';
|
|
3
|
-
export type ApiOptions = Settings & {
|
|
4
|
-
publish: boolean;
|
|
5
|
-
wait: boolean;
|
|
6
|
-
};
|
|
7
3
|
export type SendFilesResult = {
|
|
8
4
|
data: Record<string, {
|
|
9
5
|
fileName: string;
|
|
@@ -18,11 +14,4 @@ export type SendFilesResult = {
|
|
|
18
14
|
* @param options - The options for the API call
|
|
19
15
|
* @returns The translated content or version ID
|
|
20
16
|
*/
|
|
21
|
-
export declare function sendFiles(files: FileToTranslate[], options:
|
|
22
|
-
data: Record<string, {
|
|
23
|
-
fileName: string;
|
|
24
|
-
versionId: string;
|
|
25
|
-
}>;
|
|
26
|
-
locales: string[];
|
|
27
|
-
translations: CompletedFileTranslationData[];
|
|
28
|
-
}>;
|
|
17
|
+
export declare function sendFiles(files: FileToTranslate[], options: TranslateFlags, settings: Settings): Promise<SendFilesResult>;
|
package/dist/api/sendFiles.js
CHANGED
|
@@ -1,27 +1,34 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { createSpinner, logMessage, logSuccess } from '../console/logging.js';
|
|
3
3
|
import { gt } from '../utils/gt.js';
|
|
4
|
+
import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
|
|
4
5
|
/**
|
|
5
6
|
* Sends multiple files for translation to the API
|
|
6
7
|
* @param files - Array of file objects to translate
|
|
7
8
|
* @param options - The options for the API call
|
|
8
9
|
* @returns The translated content or version ID
|
|
9
10
|
*/
|
|
10
|
-
export async function sendFiles(files, options) {
|
|
11
|
+
export async function sendFiles(files, options, settings) {
|
|
11
12
|
logMessage(chalk.cyan('Files to translate:') +
|
|
12
13
|
'\n' +
|
|
13
|
-
files
|
|
14
|
+
files
|
|
15
|
+
.map((file) => {
|
|
16
|
+
if (file.fileName === TEMPLATE_FILE_NAME) {
|
|
17
|
+
return `- <React Elements>`;
|
|
18
|
+
}
|
|
19
|
+
return `- ${file.fileName}`;
|
|
20
|
+
})
|
|
21
|
+
.join('\n'));
|
|
14
22
|
const spinner = createSpinner('dots');
|
|
15
23
|
spinner.start(`Sending ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation API...`);
|
|
16
24
|
try {
|
|
17
25
|
// Send the files to the API
|
|
18
26
|
const responseData = await gt.enqueueFiles(files, {
|
|
19
|
-
publish:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
modelProvider: options.modelProvider,
|
|
27
|
+
publish: settings.publish,
|
|
28
|
+
sourceLocale: settings.defaultLocale,
|
|
29
|
+
targetLocales: settings.locales,
|
|
30
|
+
version: settings.version, // not set ATM
|
|
31
|
+
modelProvider: settings.modelProvider,
|
|
25
32
|
});
|
|
26
33
|
// Handle version ID response (for async processing)
|
|
27
34
|
const { data, message, locales, translations } = responseData;
|
package/dist/cli/base.d.ts
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { Settings, SupportedLibraries, SetupOptions } from '../types/index.js';
|
|
2
|
+
import { Settings, SupportedLibraries, SetupOptions, TranslateFlags } from '../types/index.js';
|
|
3
3
|
export type UploadOptions = {
|
|
4
4
|
config?: string;
|
|
5
5
|
apiKey?: string;
|
|
6
6
|
projectId?: string;
|
|
7
7
|
defaultLocale?: string;
|
|
8
8
|
};
|
|
9
|
-
export type TranslateOptions = {
|
|
10
|
-
config?: string;
|
|
11
|
-
defaultLocale?: string;
|
|
12
|
-
locales?: string[];
|
|
13
|
-
apiKey?: string;
|
|
14
|
-
projectId?: string;
|
|
15
|
-
dryRun: boolean;
|
|
16
|
-
experimentalLocalizeStaticUrls?: boolean;
|
|
17
|
-
experimentalHideDefaultLocale?: boolean;
|
|
18
|
-
experimentalFlattenJsonFiles?: boolean;
|
|
19
|
-
experimentalLocalizeStaticImports?: boolean;
|
|
20
|
-
excludeStaticUrls?: string[];
|
|
21
|
-
excludeStaticImports?: string[];
|
|
22
|
-
};
|
|
23
9
|
export type LoginOptions = {
|
|
24
10
|
keyType?: 'development' | 'production';
|
|
25
11
|
};
|
|
@@ -30,14 +16,16 @@ export declare class BaseCLI {
|
|
|
30
16
|
constructor(program: Command, library: SupportedLibraries, additionalModules?: SupportedLibraries[]);
|
|
31
17
|
init(): void;
|
|
32
18
|
execute(): void;
|
|
33
|
-
protected
|
|
19
|
+
protected setupStageCommand(): void;
|
|
20
|
+
protected setupTranslateCommand(): void;
|
|
21
|
+
protected handleStage(initOptions: TranslateFlags): Promise<void>;
|
|
22
|
+
protected handleTranslate(initOptions: TranslateFlags): Promise<void>;
|
|
34
23
|
protected setupUploadCommand(): void;
|
|
35
24
|
protected setupLoginCommand(): void;
|
|
36
25
|
protected setupInitCommand(): void;
|
|
37
26
|
protected setupConfigureCommand(): void;
|
|
38
27
|
protected setupSetupCommand(): void;
|
|
39
28
|
protected handleUploadCommand(settings: Settings & UploadOptions): Promise<void>;
|
|
40
|
-
protected handleGenericTranslate(settings: Settings & TranslateOptions): Promise<void>;
|
|
41
29
|
protected handleSetupReactCommand(options: SetupOptions): Promise<void>;
|
|
42
30
|
protected handleInitCommand(ranReactSetup: boolean): Promise<void>;
|
|
43
31
|
protected handleLoginCommand(options: LoginOptions): Promise<void>;
|
package/dist/cli/base.js
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'node:path';
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import { generateSettings } from '../config/generateSettings.js';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
import { translateFiles } from '../formats/files/translate.js';
|
|
9
8
|
import { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';
|
|
10
9
|
import { handleSetupReactCommand } from '../setup/wizard.js';
|
|
11
10
|
import { isPackageInstalled, searchForPackageJson, } from '../utils/packageJson.js';
|
|
@@ -14,11 +13,12 @@ import { installPackage } from '../utils/installPackage.js';
|
|
|
14
13
|
import { getPackageManager } from '../utils/packageManager.js';
|
|
15
14
|
import { retrieveCredentials, setCredentials } from '../utils/credentials.js';
|
|
16
15
|
import { areCredentialsSet } from '../utils/credentials.js';
|
|
17
|
-
import localizeStaticUrls from '../utils/localizeStaticUrls.js';
|
|
18
|
-
import flattenJsonFiles from '../utils/flattenJsonFiles.js';
|
|
19
|
-
import localizeStaticImports from '../utils/localizeStaticImports.js';
|
|
20
|
-
import copyFile from '../fs/copyFile.js';
|
|
21
16
|
import { upload } from '../formats/files/upload.js';
|
|
17
|
+
import { attachTranslateFlags } from './flags.js';
|
|
18
|
+
import { handleStage } from './commands/stage.js';
|
|
19
|
+
import { handleDownload, handleTranslate, postProcessTranslations, } from './commands/translate.js';
|
|
20
|
+
import localizeStaticUrls from '../utils/localizeStaticUrls.js';
|
|
21
|
+
import updateConfig from '../fs/config/updateConfig.js';
|
|
22
22
|
export class BaseCLI {
|
|
23
23
|
library;
|
|
24
24
|
additionalModules;
|
|
@@ -36,7 +36,7 @@ export class BaseCLI {
|
|
|
36
36
|
}
|
|
37
37
|
// Init is never called in a child class
|
|
38
38
|
init() {
|
|
39
|
-
this.
|
|
39
|
+
this.setupTranslateCommand();
|
|
40
40
|
}
|
|
41
41
|
// Execute is called by the main program
|
|
42
42
|
execute() {
|
|
@@ -45,28 +45,57 @@ export class BaseCLI {
|
|
|
45
45
|
process.argv.push('init');
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
this.program
|
|
48
|
+
setupStageCommand() {
|
|
49
|
+
attachTranslateFlags(this.program
|
|
50
|
+
.command('stage')
|
|
51
|
+
.description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.')).action(async (initOptions) => {
|
|
52
|
+
displayHeader('Staging project for translation with approval required...');
|
|
53
|
+
await this.handleStage(initOptions);
|
|
54
|
+
endCommand('Done!');
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
setupTranslateCommand() {
|
|
58
|
+
attachTranslateFlags(this.program
|
|
50
59
|
.command('translate')
|
|
51
|
-
.description('Translate your project using General Translation')
|
|
52
|
-
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
53
|
-
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
54
|
-
.option('--project-id <id>', 'Project ID for the translation service')
|
|
55
|
-
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
56
|
-
.option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
|
|
57
|
-
.option('--dry-run', 'Dry run, does not send updates to General Translation API', false)
|
|
58
|
-
.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)
|
|
59
|
-
.option('--experimental-hide-default-locale', 'When localizing static locales, hide the default locale from the path', false)
|
|
60
|
-
.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)
|
|
61
|
-
.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)
|
|
62
|
-
.action(async (initOptions) => {
|
|
60
|
+
.description('Translate your project using General Translation')).action(async (initOptions) => {
|
|
63
61
|
displayHeader('Starting translation...');
|
|
64
|
-
|
|
65
|
-
const options = { ...initOptions, ...settings };
|
|
66
|
-
await this.handleGenericTranslate(options);
|
|
62
|
+
await this.handleTranslate(initOptions);
|
|
67
63
|
endCommand('Done!');
|
|
68
64
|
});
|
|
69
65
|
}
|
|
66
|
+
async handleStage(initOptions) {
|
|
67
|
+
const settings = await generateSettings(initOptions);
|
|
68
|
+
if (!settings.stageTranslations) {
|
|
69
|
+
// Update settings.stageTranslations to true
|
|
70
|
+
settings.stageTranslations = true;
|
|
71
|
+
await updateConfig({
|
|
72
|
+
configFilepath: settings.config,
|
|
73
|
+
stageTranslations: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
await handleStage(initOptions, settings, this.library, true);
|
|
77
|
+
}
|
|
78
|
+
async handleTranslate(initOptions) {
|
|
79
|
+
const settings = await generateSettings(initOptions);
|
|
80
|
+
if (!settings.stageTranslations) {
|
|
81
|
+
const results = await handleStage(initOptions, settings, this.library, false);
|
|
82
|
+
// Process default locale static URLs first, before translations
|
|
83
|
+
if (settings.options?.experimentalLocalizeStaticUrls) {
|
|
84
|
+
await localizeStaticUrls(settings, [settings.defaultLocale]);
|
|
85
|
+
}
|
|
86
|
+
if (results) {
|
|
87
|
+
await handleTranslate(initOptions, settings, results);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Process default locale static URLs first, before downloading translations
|
|
92
|
+
if (settings.options?.experimentalLocalizeStaticUrls) {
|
|
93
|
+
await localizeStaticUrls(settings, [settings.defaultLocale]);
|
|
94
|
+
}
|
|
95
|
+
await handleDownload(initOptions, settings);
|
|
96
|
+
}
|
|
97
|
+
await postProcessTranslations(settings);
|
|
98
|
+
}
|
|
70
99
|
setupUploadCommand() {
|
|
71
100
|
this.program
|
|
72
101
|
.command('upload')
|
|
@@ -197,55 +226,6 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
197
226
|
// Process all file types at once with a single call
|
|
198
227
|
await upload(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
|
|
199
228
|
}
|
|
200
|
-
async handleGenericTranslate(settings) {
|
|
201
|
-
// dataFormat for JSONs
|
|
202
|
-
let dataFormat;
|
|
203
|
-
if (this.library === 'next-intl') {
|
|
204
|
-
dataFormat = 'ICU';
|
|
205
|
-
}
|
|
206
|
-
else if (this.library === 'i18next') {
|
|
207
|
-
if (this.additionalModules.includes('i18next-icu')) {
|
|
208
|
-
dataFormat = 'ICU';
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
dataFormat = 'I18NEXT';
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
dataFormat = 'JSX';
|
|
216
|
-
}
|
|
217
|
-
if (!settings.files ||
|
|
218
|
-
(Object.keys(settings.files.placeholderPaths).length === 1 &&
|
|
219
|
-
settings.files.placeholderPaths.gt)) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
const { resolvedPaths: sourceFiles, placeholderPaths, transformPaths, } = settings.files;
|
|
223
|
-
// Localize static urls in default locale BEFORE translation
|
|
224
|
-
if (settings.experimentalLocalizeStaticUrls) {
|
|
225
|
-
await localizeStaticUrls(settings, [settings.defaultLocale]);
|
|
226
|
-
}
|
|
227
|
-
// Process all file types at once with a single call
|
|
228
|
-
await translateFiles(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
|
|
229
|
-
// Localize static urls in other locales AFTER translation
|
|
230
|
-
if (settings.experimentalLocalizeStaticUrls) {
|
|
231
|
-
const otherLocales = settings.locales.filter((locale) => locale !== settings.defaultLocale);
|
|
232
|
-
if (otherLocales.length > 0) {
|
|
233
|
-
await localizeStaticUrls(settings, otherLocales);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// Localize static imports (/docs -> /[locale]/docs)
|
|
237
|
-
if (settings.experimentalLocalizeStaticImports) {
|
|
238
|
-
await localizeStaticImports(settings);
|
|
239
|
-
}
|
|
240
|
-
// Flatten json files into a single file
|
|
241
|
-
if (settings.experimentalFlattenJsonFiles) {
|
|
242
|
-
await flattenJsonFiles(settings);
|
|
243
|
-
}
|
|
244
|
-
// Copy files to the target locale
|
|
245
|
-
if (settings.options?.copyFiles) {
|
|
246
|
-
await copyFile(settings);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
229
|
async handleSetupReactCommand(options) {
|
|
250
230
|
await handleSetupReactCommand(options);
|
|
251
231
|
}
|
|
@@ -265,13 +245,14 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
265
245
|
const usingCDN = isUsingGT
|
|
266
246
|
? await promptConfirm({
|
|
267
247
|
message: `Auto-detected that you're using gt-next or gt-react. Would you like to use the General Translation CDN to store your translations?\nSee ${isUsingGTNext
|
|
268
|
-
? 'https://generaltranslation.com/docs/next/
|
|
269
|
-
: 'https://generaltranslation.com/docs/react/
|
|
248
|
+
? 'https://generaltranslation.com/en/docs/next/guides/local-tx'
|
|
249
|
+
: 'https://generaltranslation.com/en/docs/react/guides/local-tx'} for more information.\nIf you answer no, we'll configure the CLI tool to download completed translations.`,
|
|
270
250
|
defaultValue: true,
|
|
271
251
|
})
|
|
272
252
|
: false;
|
|
273
253
|
if (isUsingGT && !usingCDN) {
|
|
274
|
-
logMessage(`
|
|
254
|
+
logMessage(`Make sure to add a loadTranslations function to your app configuration to correctly use local translations.
|
|
255
|
+
See https://generaltranslation.com/en/docs/next/guides/local-tx`);
|
|
275
256
|
}
|
|
276
257
|
// Ask where the translations are stored
|
|
277
258
|
const translationsDir = isUsingGT && !usingCDN
|
|
@@ -320,6 +301,7 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
320
301
|
defaultLocale,
|
|
321
302
|
locales,
|
|
322
303
|
files: Object.keys(files).length > 0 ? files : undefined,
|
|
304
|
+
publish: isUsingGT && usingCDN,
|
|
323
305
|
});
|
|
324
306
|
logSuccess(`Feel free to edit ${chalk.cyan(configFilepath)} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`);
|
|
325
307
|
// Install gtx-cli if not installed
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Settings, SupportedLibraries, TranslateFlags } from '../../types/index.js';
|
|
2
|
+
import { SendFilesResult } from '../../api/sendFiles.js';
|
|
3
|
+
export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
|
|
4
|
+
export declare const TEMPLATE_FILE_ID: string;
|
|
5
|
+
export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<SendFilesResult | undefined>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { logErrorAndExit, logSuccess } from '../../console/logging.js';
|
|
2
|
+
import { noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, invalidConfigurationError, } from '../../console/index.js';
|
|
3
|
+
import { aggregateFiles } from '../../formats/files/translate.js';
|
|
4
|
+
import { aggregateReactTranslations } from '../../translation/stage.js';
|
|
5
|
+
import { sendFiles } from '../../api/sendFiles.js';
|
|
6
|
+
import { updateVersions } from '../../fs/config/updateVersions.js';
|
|
7
|
+
import updateConfig from '../../fs/config/updateConfig.js';
|
|
8
|
+
import { hashStringSync } from '../../utils/hash.js';
|
|
9
|
+
export const TEMPLATE_FILE_NAME = '__INTERNAL_GT_TEMPLATE_NAME__';
|
|
10
|
+
export const TEMPLATE_FILE_ID = hashStringSync(TEMPLATE_FILE_NAME);
|
|
11
|
+
export async function handleStage(options, settings, library, stage) {
|
|
12
|
+
// Validate required settings are present if not in dry run
|
|
13
|
+
if (!options.dryRun) {
|
|
14
|
+
if (!settings.locales) {
|
|
15
|
+
logErrorAndExit(noLocalesError);
|
|
16
|
+
}
|
|
17
|
+
if (!settings.defaultLocale) {
|
|
18
|
+
logErrorAndExit(noDefaultLocaleError);
|
|
19
|
+
}
|
|
20
|
+
if (!settings.apiKey) {
|
|
21
|
+
logErrorAndExit(noApiKeyError);
|
|
22
|
+
}
|
|
23
|
+
if (settings.apiKey.startsWith('gtx-dev-')) {
|
|
24
|
+
logErrorAndExit(devApiKeyError);
|
|
25
|
+
}
|
|
26
|
+
if (!settings.projectId) {
|
|
27
|
+
logErrorAndExit(noProjectIdError);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Aggregate files
|
|
31
|
+
const allFiles = await aggregateFiles(settings);
|
|
32
|
+
// Parse for React components
|
|
33
|
+
let reactComponents = 0;
|
|
34
|
+
if (library === 'gt-react' || library === 'gt-next') {
|
|
35
|
+
const updates = await aggregateReactTranslations(options, settings, library);
|
|
36
|
+
if (updates.length > 0) {
|
|
37
|
+
if (!options.dryRun &&
|
|
38
|
+
!settings.publish &&
|
|
39
|
+
!settings.files?.placeholderPaths.gt) {
|
|
40
|
+
logErrorAndExit(invalidConfigurationError);
|
|
41
|
+
}
|
|
42
|
+
reactComponents = updates.length;
|
|
43
|
+
// Convert updates to a file object
|
|
44
|
+
const fileData = {};
|
|
45
|
+
const fileMetadata = {};
|
|
46
|
+
// Convert updates to the proper data format
|
|
47
|
+
for (const update of updates) {
|
|
48
|
+
const { source, metadata, dataFormat } = update;
|
|
49
|
+
metadata.dataFormat = dataFormat; // add the data format to the metadata
|
|
50
|
+
const { hash, id } = metadata;
|
|
51
|
+
if (id) {
|
|
52
|
+
fileData[id] = source;
|
|
53
|
+
fileMetadata[id] = metadata;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
fileData[hash] = source;
|
|
57
|
+
fileMetadata[hash] = metadata;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
allFiles.push({
|
|
61
|
+
fileName: TEMPLATE_FILE_NAME,
|
|
62
|
+
content: JSON.stringify(fileData),
|
|
63
|
+
fileFormat: 'GTJSON',
|
|
64
|
+
formatMetadata: fileMetadata,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Dry run
|
|
69
|
+
if (options.dryRun) {
|
|
70
|
+
const fileNames = allFiles
|
|
71
|
+
.map((file) => {
|
|
72
|
+
if (file.fileName === TEMPLATE_FILE_NAME) {
|
|
73
|
+
return `- <React Elements> (${reactComponents})`;
|
|
74
|
+
}
|
|
75
|
+
return `- ${file.fileName}`;
|
|
76
|
+
})
|
|
77
|
+
.join('\n');
|
|
78
|
+
logSuccess(`Dry run: No files were sent to General Translation. Found files:\n${fileNames}`);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
// Send translations to General Translation
|
|
82
|
+
let filesTranslationResponse;
|
|
83
|
+
if (allFiles.length > 0) {
|
|
84
|
+
filesTranslationResponse = await sendFiles(allFiles, options, settings);
|
|
85
|
+
if (stage) {
|
|
86
|
+
await updateVersions({
|
|
87
|
+
configDirectory: settings.configDirectory,
|
|
88
|
+
versionData: filesTranslationResponse.data,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const templateData = filesTranslationResponse.data[TEMPLATE_FILE_ID];
|
|
92
|
+
if (templateData?.versionId) {
|
|
93
|
+
await updateConfig({
|
|
94
|
+
configFilepath: settings.config,
|
|
95
|
+
_versionId: templateData.versionId,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return filesTranslationResponse;
|
|
100
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SendFilesResult } from '../../api/sendFiles.js';
|
|
2
|
+
import { TranslateFlags } from '../../types/index.js';
|
|
3
|
+
import { Settings } from '../../types/index.js';
|
|
4
|
+
export declare function handleTranslate(options: TranslateFlags, settings: Settings, filesTranslationResponse: SendFilesResult | undefined): Promise<void>;
|
|
5
|
+
export declare function handleDownload(options: TranslateFlags, settings: Settings): Promise<void>;
|
|
6
|
+
export declare function postProcessTranslations(settings: Settings): Promise<void>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { checkFileTranslations } from '../../api/checkFileTranslations.js';
|
|
2
|
+
import { createFileMapping } from '../../formats/files/fileMapping.js';
|
|
3
|
+
import { logError } from '../../console/logging.js';
|
|
4
|
+
import { getStagedVersions } from '../../fs/config/updateVersions.js';
|
|
5
|
+
import copyFile from '../../fs/copyFile.js';
|
|
6
|
+
import localizeStaticImports from '../../utils/localizeStaticImports.js';
|
|
7
|
+
import flattenJsonFiles from '../../utils/flattenJsonFiles.js';
|
|
8
|
+
import localizeStaticUrls from '../../utils/localizeStaticUrls.js';
|
|
9
|
+
import { noFilesError, noVersionIdError } from '../../console/index.js';
|
|
10
|
+
// Downloads translations that were completed
|
|
11
|
+
export async function handleTranslate(options, settings, filesTranslationResponse) {
|
|
12
|
+
if (filesTranslationResponse && settings.files) {
|
|
13
|
+
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
14
|
+
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
15
|
+
const { data } = filesTranslationResponse;
|
|
16
|
+
// Check for remaining translations
|
|
17
|
+
await checkFileTranslations(data, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Downloads translations that were originally staged
|
|
21
|
+
export async function handleDownload(options, settings) {
|
|
22
|
+
if (!settings._versionId) {
|
|
23
|
+
logError(noVersionIdError);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
if (!settings.files) {
|
|
27
|
+
logError(noFilesError);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Files
|
|
31
|
+
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
32
|
+
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
33
|
+
const stagedVersionData = await getStagedVersions(settings.configDirectory);
|
|
34
|
+
// Check for remaining translations
|
|
35
|
+
await checkFileTranslations(stagedVersionData, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings);
|
|
36
|
+
}
|
|
37
|
+
export async function postProcessTranslations(settings) {
|
|
38
|
+
// Localize static urls (/docs -> /[locale]/docs) for non-default locales only
|
|
39
|
+
// Default locale is processed earlier in the flow in base.ts
|
|
40
|
+
if (settings.options?.experimentalLocalizeStaticUrls) {
|
|
41
|
+
const nonDefaultLocales = settings.locales.filter(locale => locale !== settings.defaultLocale);
|
|
42
|
+
if (nonDefaultLocales.length > 0) {
|
|
43
|
+
await localizeStaticUrls(settings, nonDefaultLocales);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Localize static imports (/docs -> /[locale]/docs)
|
|
47
|
+
if (settings.options?.experimentalLocalizeStaticImports) {
|
|
48
|
+
await localizeStaticImports(settings);
|
|
49
|
+
}
|
|
50
|
+
// Flatten json files into a single file
|
|
51
|
+
if (settings.options?.experimentalFlattenJsonFiles) {
|
|
52
|
+
await flattenJsonFiles(settings);
|
|
53
|
+
}
|
|
54
|
+
// Copy files to the target locale
|
|
55
|
+
if (settings.options?.copyFiles) {
|
|
56
|
+
await copyFile(settings);
|
|
57
|
+
}
|
|
58
|
+
}
|