gtx-cli 2.1.4 → 2.1.5-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- 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 +48 -68
- 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 +54 -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 +7 -3
- package/package.json +1 -1
- 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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.1.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#576](https://github.com/generaltranslation/gt/pull/576) [`be9c1ff`](https://github.com/generaltranslation/gt/commit/be9c1ff24c9a15a35e3f0da26e9ec941e5b41eea) Thanks [@SamEggert](https://github.com/SamEggert)! - Fix url localization
|
|
8
|
+
|
|
3
9
|
## 2.1.4
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -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,11 @@ 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 updateConfig from '../fs/config/updateConfig.js';
|
|
22
21
|
export class BaseCLI {
|
|
23
22
|
library;
|
|
24
23
|
additionalModules;
|
|
@@ -36,7 +35,7 @@ export class BaseCLI {
|
|
|
36
35
|
}
|
|
37
36
|
// Init is never called in a child class
|
|
38
37
|
init() {
|
|
39
|
-
this.
|
|
38
|
+
this.setupTranslateCommand();
|
|
40
39
|
}
|
|
41
40
|
// Execute is called by the main program
|
|
42
41
|
execute() {
|
|
@@ -45,28 +44,49 @@ export class BaseCLI {
|
|
|
45
44
|
process.argv.push('init');
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
|
-
|
|
49
|
-
this.program
|
|
47
|
+
setupStageCommand() {
|
|
48
|
+
attachTranslateFlags(this.program
|
|
49
|
+
.command('stage')
|
|
50
|
+
.description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.')).action(async (initOptions) => {
|
|
51
|
+
displayHeader('Staging project for translation with approval required...');
|
|
52
|
+
await this.handleStage(initOptions);
|
|
53
|
+
endCommand('Done!');
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
setupTranslateCommand() {
|
|
57
|
+
attachTranslateFlags(this.program
|
|
50
58
|
.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) => {
|
|
59
|
+
.description('Translate your project using General Translation')).action(async (initOptions) => {
|
|
63
60
|
displayHeader('Starting translation...');
|
|
64
|
-
|
|
65
|
-
const options = { ...initOptions, ...settings };
|
|
66
|
-
await this.handleGenericTranslate(options);
|
|
61
|
+
await this.handleTranslate(initOptions);
|
|
67
62
|
endCommand('Done!');
|
|
68
63
|
});
|
|
69
64
|
}
|
|
65
|
+
async handleStage(initOptions) {
|
|
66
|
+
const settings = await generateSettings(initOptions);
|
|
67
|
+
if (!settings.stageTranslations) {
|
|
68
|
+
// Update settings.stageTranslations to true
|
|
69
|
+
settings.stageTranslations = true;
|
|
70
|
+
await updateConfig({
|
|
71
|
+
configFilepath: settings.config,
|
|
72
|
+
stageTranslations: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
await handleStage(initOptions, settings, this.library, true);
|
|
76
|
+
}
|
|
77
|
+
async handleTranslate(initOptions) {
|
|
78
|
+
const settings = await generateSettings(initOptions);
|
|
79
|
+
if (!settings.stageTranslations) {
|
|
80
|
+
const results = await handleStage(initOptions, settings, this.library, false);
|
|
81
|
+
if (results) {
|
|
82
|
+
await handleTranslate(initOptions, settings, results);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
await handleDownload(initOptions, settings);
|
|
87
|
+
}
|
|
88
|
+
await postProcessTranslations(settings);
|
|
89
|
+
}
|
|
70
90
|
setupUploadCommand() {
|
|
71
91
|
this.program
|
|
72
92
|
.command('upload')
|
|
@@ -197,48 +217,6 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
197
217
|
// Process all file types at once with a single call
|
|
198
218
|
await upload(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
|
|
199
219
|
}
|
|
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
|
-
// Process all file types at once with a single call
|
|
224
|
-
await translateFiles(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
|
|
225
|
-
// Localize static urls (/docs -> /[locale]/docs)
|
|
226
|
-
if (settings.experimentalLocalizeStaticUrls) {
|
|
227
|
-
await localizeStaticUrls(settings);
|
|
228
|
-
}
|
|
229
|
-
// Localize static imports (/docs -> /[locale]/docs)
|
|
230
|
-
if (settings.experimentalLocalizeStaticImports) {
|
|
231
|
-
await localizeStaticImports(settings);
|
|
232
|
-
}
|
|
233
|
-
// Flatten json files into a single file
|
|
234
|
-
if (settings.experimentalFlattenJsonFiles) {
|
|
235
|
-
await flattenJsonFiles(settings);
|
|
236
|
-
}
|
|
237
|
-
// Copy files to the target locale
|
|
238
|
-
if (settings.options?.copyFiles) {
|
|
239
|
-
await copyFile(settings);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
220
|
async handleSetupReactCommand(options) {
|
|
243
221
|
await handleSetupReactCommand(options);
|
|
244
222
|
}
|
|
@@ -258,13 +236,14 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
258
236
|
const usingCDN = isUsingGT
|
|
259
237
|
? await promptConfirm({
|
|
260
238
|
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
|
|
261
|
-
? 'https://generaltranslation.com/docs/next/
|
|
262
|
-
: 'https://generaltranslation.com/docs/react/
|
|
239
|
+
? 'https://generaltranslation.com/en/docs/next/guides/local-tx'
|
|
240
|
+
: '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.`,
|
|
263
241
|
defaultValue: true,
|
|
264
242
|
})
|
|
265
243
|
: false;
|
|
266
244
|
if (isUsingGT && !usingCDN) {
|
|
267
|
-
logMessage(`
|
|
245
|
+
logMessage(`Make sure to add a loadTranslations function to your app configuration to correctly use local translations.
|
|
246
|
+
See https://generaltranslation.com/en/docs/next/guides/local-tx`);
|
|
268
247
|
}
|
|
269
248
|
// Ask where the translations are stored
|
|
270
249
|
const translationsDir = isUsingGT && !usingCDN
|
|
@@ -313,6 +292,7 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
|
|
|
313
292
|
defaultLocale,
|
|
314
293
|
locales,
|
|
315
294
|
files: Object.keys(files).length > 0 ? files : undefined,
|
|
295
|
+
publish: isUsingGT && usingCDN,
|
|
316
296
|
});
|
|
317
297
|
logSuccess(`Feel free to edit ${chalk.cyan(configFilepath)} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`);
|
|
318
298
|
// 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 { versionId } = filesTranslationResponse.data[TEMPLATE_FILE_ID];
|
|
92
|
+
if (versionId) {
|
|
93
|
+
await updateConfig({
|
|
94
|
+
configFilepath: settings.config,
|
|
95
|
+
_versionId: 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,54 @@
|
|
|
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)
|
|
39
|
+
if (settings.options?.experimentalLocalizeStaticUrls) {
|
|
40
|
+
await localizeStaticUrls(settings);
|
|
41
|
+
}
|
|
42
|
+
// Localize static imports (/docs -> /[locale]/docs)
|
|
43
|
+
if (settings.options?.experimentalLocalizeStaticImports) {
|
|
44
|
+
await localizeStaticImports(settings);
|
|
45
|
+
}
|
|
46
|
+
// Flatten json files into a single file
|
|
47
|
+
if (settings.options?.experimentalFlattenJsonFiles) {
|
|
48
|
+
await flattenJsonFiles(settings);
|
|
49
|
+
}
|
|
50
|
+
// Copy files to the target locale
|
|
51
|
+
if (settings.options?.copyFiles) {
|
|
52
|
+
await copyFile(settings);
|
|
53
|
+
}
|
|
54
|
+
}
|