gtx-cli 2.1.5 → 2.1.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/CHANGELOG.md +15 -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 +2 -2
- 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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import findFilepath from '../fs/findFilepath.js';
|
|
2
|
+
const DEFAULT_TIMEOUT = 600;
|
|
3
|
+
export function attachTranslateFlags(command) {
|
|
4
|
+
command
|
|
5
|
+
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
6
|
+
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
7
|
+
.option('--project-id <id>', 'General Translation project ID')
|
|
8
|
+
.option('--version-id <id>', 'General Translation version ID')
|
|
9
|
+
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
10
|
+
.option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
|
|
11
|
+
.option('--dry-run', 'Dry run, do not send updates to the General Translation API', false)
|
|
12
|
+
.option('--timeout <seconds>', 'Translation wait timeout in seconds', (value) => {
|
|
13
|
+
const parsedValue = parseInt(value, 10);
|
|
14
|
+
if (isNaN(parsedValue)) {
|
|
15
|
+
throw new Error('Not a number.');
|
|
16
|
+
}
|
|
17
|
+
if (parsedValue < 0) {
|
|
18
|
+
throw new Error('Timeout must be a positive number.');
|
|
19
|
+
}
|
|
20
|
+
return parsedValue;
|
|
21
|
+
}, DEFAULT_TIMEOUT)
|
|
22
|
+
.option('--publish', 'Publish translations to the CDN', false)
|
|
23
|
+
.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)
|
|
24
|
+
.option('--experimental-hide-default-locale', 'When localizing static locales, hide the default locale from the path', false)
|
|
25
|
+
.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)
|
|
26
|
+
.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);
|
|
27
|
+
return command;
|
|
28
|
+
}
|
|
29
|
+
export function attachAdditionalReactTranslateFlags(command) {
|
|
30
|
+
command
|
|
31
|
+
.option('--tsconfig, --jsconfig <path>', 'Path to custom jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
|
|
32
|
+
.option('--dictionary <path>', 'Path to dictionary file')
|
|
33
|
+
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
34
|
+
.option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
|
|
35
|
+
.option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false);
|
|
36
|
+
return command;
|
|
37
|
+
}
|
package/dist/cli/next.js
CHANGED
package/dist/cli/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { Options, SupportedFrameworks, WrapOptions,
|
|
2
|
+
import { Options, SupportedFrameworks, WrapOptions, SupportedLibraries, TranslateFlags } from '../types/index.js';
|
|
3
3
|
import { BaseCLI } from './base.js';
|
|
4
4
|
export declare class ReactCLI extends BaseCLI {
|
|
5
5
|
constructor(command: Command, library: 'gt-react' | 'gt-next', additionalModules?: SupportedLibraries[]);
|
|
@@ -12,10 +12,7 @@ export declare class ReactCLI extends BaseCLI {
|
|
|
12
12
|
protected setupTranslateCommand(): void;
|
|
13
13
|
protected setupValidateCommand(): void;
|
|
14
14
|
protected setupGenerateSourceCommand(): void;
|
|
15
|
-
protected
|
|
16
|
-
protected handleGenerateSourceCommand(initOptions: GenerateSourceOptions): Promise<void>;
|
|
15
|
+
protected handleGenerateSourceCommand(initOptions: TranslateFlags): Promise<void>;
|
|
17
16
|
protected handleScanCommand(options: WrapOptions): Promise<void>;
|
|
18
|
-
protected handleStage(initOptions: Options): Promise<void>;
|
|
19
|
-
protected handleTranslate(initOptions: Options): Promise<void>;
|
|
20
17
|
protected handleValidate(initOptions: Options, files?: string[]): Promise<void>;
|
|
21
18
|
}
|
package/dist/cli/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { displayHeader, endCommand, logError,
|
|
1
|
+
import { displayHeader, endCommand, logError, logStep, logSuccess, logWarning, promptConfirm, } from '../console/logging.js';
|
|
2
2
|
import loadJSON from '../fs/loadJSON.js';
|
|
3
3
|
import findFilepath from '../fs/findFilepath.js';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -8,15 +8,12 @@ import { wrapContentReact } from '../react/parse/wrapContent.js';
|
|
|
8
8
|
import { generateSettings } from '../config/generateSettings.js';
|
|
9
9
|
import { saveJSON } from '../fs/saveJSON.js';
|
|
10
10
|
import { resolveLocaleFiles } from '../fs/config/parseFilesConfig.js';
|
|
11
|
-
import { noFilesError
|
|
12
|
-
import {
|
|
13
|
-
import { createUpdates } from '../translation/parse.js';
|
|
14
|
-
import { translate } from '../translation/translate.js';
|
|
15
|
-
import updateConfig from '../fs/config/updateConfig.js';
|
|
11
|
+
import { noFilesError } from '../console/index.js';
|
|
12
|
+
import { aggregateReactTranslations } from '../translation/stage.js';
|
|
16
13
|
import { validateConfigExists } from '../config/validateSettings.js';
|
|
17
14
|
import { validateProject } from '../translation/validate.js';
|
|
18
15
|
import { intro } from '@clack/prompts';
|
|
19
|
-
|
|
16
|
+
import { attachAdditionalReactTranslateFlags, attachTranslateFlags, } from './flags.js';
|
|
20
17
|
const pkg = 'gt-react';
|
|
21
18
|
export class ReactCLI extends BaseCLI {
|
|
22
19
|
constructor(command, library, additionalModules) {
|
|
@@ -25,7 +22,6 @@ export class ReactCLI extends BaseCLI {
|
|
|
25
22
|
init() {
|
|
26
23
|
this.setupStageCommand();
|
|
27
24
|
this.setupTranslateCommand();
|
|
28
|
-
this.setupScanCommand();
|
|
29
25
|
this.setupGenerateSourceCommand();
|
|
30
26
|
this.setupValidateCommand();
|
|
31
27
|
}
|
|
@@ -36,50 +32,18 @@ export class ReactCLI extends BaseCLI {
|
|
|
36
32
|
return wrapContentReact(options, pkg, framework, errors, warnings);
|
|
37
33
|
}
|
|
38
34
|
setupStageCommand() {
|
|
39
|
-
this.program
|
|
35
|
+
attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
|
|
40
36
|
.command('stage')
|
|
41
|
-
.description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.')
|
|
42
|
-
|
|
43
|
-
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
44
|
-
.option('--project-id <id>', 'Project ID for the translation service')
|
|
45
|
-
.option('--version-id <id>', 'Version ID for the translation service')
|
|
46
|
-
.option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
|
|
47
|
-
.option('--dictionary <path>', 'Path to dictionary file')
|
|
48
|
-
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
49
|
-
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
50
|
-
.option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
|
|
51
|
-
.option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
|
|
52
|
-
.option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false)
|
|
53
|
-
.option('--dry-run', 'Dry run, does not send updates to General Translation API', false)
|
|
54
|
-
.option('--timeout <seconds>', 'Timeout in seconds for waiting for updates to be deployed to the CDN', DEFAULT_TIMEOUT.toString())
|
|
55
|
-
.action(async (options) => {
|
|
56
|
-
displayHeader('Staging project for translation with approval...');
|
|
37
|
+
.description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'))).action(async (options) => {
|
|
38
|
+
displayHeader('Staging project for translation with approval required...');
|
|
57
39
|
await this.handleStage(options);
|
|
58
40
|
endCommand('Done!');
|
|
59
41
|
});
|
|
60
42
|
}
|
|
61
43
|
setupTranslateCommand() {
|
|
62
|
-
this.program
|
|
44
|
+
attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
|
|
63
45
|
.command('translate')
|
|
64
|
-
.description('Scans the project for a dictionary and/or <T> tags, and sends the updates to the General Translation API for translation.')
|
|
65
|
-
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
66
|
-
.option('--api-key <key>', 'API key for General Translation cloud service')
|
|
67
|
-
.option('--project-id <id>', 'Project ID for the translation service')
|
|
68
|
-
.option('--version-id <id>', 'Version ID for the translation service')
|
|
69
|
-
.option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
|
|
70
|
-
.option('--dictionary <path>', 'Path to dictionary file')
|
|
71
|
-
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
72
|
-
.option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
|
|
73
|
-
.option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
|
|
74
|
-
.option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
|
|
75
|
-
.option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false)
|
|
76
|
-
.option('--dry-run', 'Dry run, does not send updates to General Translation API', false)
|
|
77
|
-
.option('--timeout <seconds>', 'Timeout in seconds for waiting for updates to be deployed to the CDN', DEFAULT_TIMEOUT.toString())
|
|
78
|
-
.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)
|
|
79
|
-
.option('--experimental-hide-default-locale', 'When localizing static locales, hide the default locale from the path', false)
|
|
80
|
-
.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)
|
|
81
|
-
.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)
|
|
82
|
-
.action(async (options) => {
|
|
46
|
+
.description('Scans the project for a dictionary and/or <T> tags, and sends the updates to the General Translation API for translation.'))).action(async (options) => {
|
|
83
47
|
displayHeader('Translating project...');
|
|
84
48
|
await this.handleTranslate(options);
|
|
85
49
|
endCommand('Done!');
|
|
@@ -102,79 +66,17 @@ export class ReactCLI extends BaseCLI {
|
|
|
102
66
|
});
|
|
103
67
|
}
|
|
104
68
|
setupGenerateSourceCommand() {
|
|
105
|
-
this.program
|
|
69
|
+
attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
|
|
106
70
|
.command('generate')
|
|
107
|
-
.description('Generate a translation file for the source locale.
|
|
108
|
-
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
109
|
-
.option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
|
|
110
|
-
.option('--dictionary <path>', 'Path to dictionary file')
|
|
111
|
-
.option('--default-language, --default-locale <locale>', 'Source locale (e.g., en)')
|
|
112
|
-
.option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
|
|
113
|
-
.option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false)
|
|
114
|
-
.option('--suppress-warnings', 'Suppress warnings encountered while scanning for <T> tags', false)
|
|
115
|
-
.option('-t, --translations-dir, --translation-dir <path>', 'Path to directory where translations will be saved. If this flag is not provided, translations will not be saved locally.')
|
|
116
|
-
.action(async (options) => {
|
|
71
|
+
.description('Generate a translation file for the source locale. This command should be used if you are handling your own translations.'))).action(async (initOptions) => {
|
|
117
72
|
displayHeader('Generating source templates...');
|
|
118
|
-
await this.handleGenerateSourceCommand(
|
|
119
|
-
endCommand('Done!');
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
setupScanCommand() {
|
|
123
|
-
this.program
|
|
124
|
-
.command('scan')
|
|
125
|
-
.description('Scans the project and wraps all JSX elements in the src directory with a <T> tag, with unique ids')
|
|
126
|
-
.option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
|
|
127
|
-
.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
|
|
128
|
-
.option('--disable-ids', 'Disable id generation for the <T> tags', false)
|
|
129
|
-
.option('--disable-formatting', 'Disable formatting of edited files', false)
|
|
130
|
-
.option('--skip-ts', 'Skip wrapping <T> tags in TypeScript files', false)
|
|
131
|
-
.action(async (options) => {
|
|
132
|
-
displayHeader('Scanning project...');
|
|
133
|
-
await this.handleScanCommand(options);
|
|
73
|
+
await this.handleGenerateSourceCommand(initOptions);
|
|
134
74
|
endCommand('Done!');
|
|
135
75
|
});
|
|
136
76
|
}
|
|
137
77
|
async handleGenerateSourceCommand(initOptions) {
|
|
138
78
|
const settings = await generateSettings(initOptions);
|
|
139
|
-
const
|
|
140
|
-
if (!options.dictionary) {
|
|
141
|
-
options.dictionary = findFilepath([
|
|
142
|
-
'./dictionary.js',
|
|
143
|
-
'./src/dictionary.js',
|
|
144
|
-
'./dictionary.json',
|
|
145
|
-
'./src/dictionary.json',
|
|
146
|
-
'./dictionary.ts',
|
|
147
|
-
'./src/dictionary.ts',
|
|
148
|
-
]);
|
|
149
|
-
}
|
|
150
|
-
// User has to provide a dictionary file
|
|
151
|
-
// will not read from settings.files.resolvedPaths.json
|
|
152
|
-
const { updates, errors, warnings } = await createUpdates(options, options.dictionary, this.library === 'gt-next' ? 'gt-next' : 'gt-react', false);
|
|
153
|
-
if (warnings.length > 0) {
|
|
154
|
-
if (options.suppressWarnings) {
|
|
155
|
-
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')}`));
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
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` +
|
|
159
|
-
warnings
|
|
160
|
-
.map((warning) => chalk.yellow('• Warning: ') + chalk.white(warning))
|
|
161
|
-
.join('\n')));
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (errors.length > 0) {
|
|
165
|
-
if (options.ignoreErrors) {
|
|
166
|
-
logWarning(chalk.yellow(`CLI tool encountered errors while scanning for translatable content. These components will not be translated.\n` +
|
|
167
|
-
errors
|
|
168
|
-
.map((error) => chalk.yellow('• Warning: ') + chalk.white(error))
|
|
169
|
-
.join('\n')));
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
logErrorAndExit(chalk.red(`CLI tool encountered errors while scanning for translatable content. ${chalk.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
|
|
173
|
-
errors
|
|
174
|
-
.map((error) => chalk.red('• Error: ') + chalk.white(error))
|
|
175
|
-
.join('\n')));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
79
|
+
const updates = await aggregateReactTranslations(initOptions, settings, this.library === 'gt-next' ? 'gt-next' : 'gt-react');
|
|
178
80
|
// Convert updates to the proper data format
|
|
179
81
|
const newData = {};
|
|
180
82
|
for (const update of updates) {
|
|
@@ -255,48 +157,6 @@ export class ReactCLI extends BaseCLI {
|
|
|
255
157
|
.join('\n'));
|
|
256
158
|
}
|
|
257
159
|
}
|
|
258
|
-
async handleStage(initOptions) {
|
|
259
|
-
const settings = await generateSettings(initOptions);
|
|
260
|
-
// First run the base class's handleTranslate method
|
|
261
|
-
const options = { ...initOptions, ...settings };
|
|
262
|
-
if (!settings.stageTranslations) {
|
|
263
|
-
// Update settings.stageTranslations to true
|
|
264
|
-
settings.stageTranslations = true;
|
|
265
|
-
await updateConfig({
|
|
266
|
-
configFilepath: options.config,
|
|
267
|
-
stageTranslations: true,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
const pkg = this.library === 'gt-next' ? 'gt-next' : 'gt-react';
|
|
271
|
-
await stageProject(options, pkg);
|
|
272
|
-
}
|
|
273
|
-
async handleTranslate(initOptions) {
|
|
274
|
-
const settings = await generateSettings(initOptions);
|
|
275
|
-
// First run the base class's handleTranslate method
|
|
276
|
-
const options = { ...initOptions, ...settings };
|
|
277
|
-
try {
|
|
278
|
-
await super.handleGenericTranslate(options);
|
|
279
|
-
// If the base class's handleTranslate completes successfully, continue with ReactCLI-specific code
|
|
280
|
-
}
|
|
281
|
-
catch {
|
|
282
|
-
// Continue with ReactCLI-specific code even if base handleTranslate failed
|
|
283
|
-
}
|
|
284
|
-
if (!settings.stageTranslations) {
|
|
285
|
-
// If stageTranslations is false, stage the project
|
|
286
|
-
const pkg = this.library === 'gt-next' ? 'gt-next' : 'gt-react';
|
|
287
|
-
const results = await stageProject(options, pkg);
|
|
288
|
-
if (results) {
|
|
289
|
-
await translate(options, results.versionId);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
if (!settings._versionId) {
|
|
294
|
-
logError(noVersionIdError);
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
await translate(options, settings._versionId);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
160
|
async handleValidate(initOptions, files) {
|
|
301
161
|
validateConfigExists();
|
|
302
162
|
const settings = await generateSettings(initOptions);
|
|
@@ -76,9 +76,14 @@ export async function generateSettings(options, cwd = process.cwd()) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
// merge options
|
|
79
|
-
|
|
79
|
+
const mergedOptions = { ...gtConfig, ...options };
|
|
80
|
+
// Add defaultLocale if not provided
|
|
81
|
+
mergedOptions.defaultLocale =
|
|
82
|
+
mergedOptions.defaultLocale || libraryDefaultLocale;
|
|
80
83
|
// merge locales
|
|
81
84
|
mergedOptions.locales = Array.from(new Set([...(gtConfig.locales || []), ...(options.locales || [])]));
|
|
85
|
+
// Separate defaultLocale from locales
|
|
86
|
+
mergedOptions.locales = mergedOptions.locales.filter((locale) => locale !== mergedOptions.defaultLocale);
|
|
82
87
|
// Add apiKey if not provided
|
|
83
88
|
mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;
|
|
84
89
|
// Add projectId if not provided
|
|
@@ -87,9 +92,6 @@ export async function generateSettings(options, cwd = process.cwd()) {
|
|
|
87
92
|
mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;
|
|
88
93
|
// Add dashboardUrl if not provided
|
|
89
94
|
mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;
|
|
90
|
-
// Add defaultLocale if not provided
|
|
91
|
-
mergedOptions.defaultLocale =
|
|
92
|
-
mergedOptions.defaultLocale || libraryDefaultLocale;
|
|
93
95
|
// Add locales if not provided
|
|
94
96
|
mergedOptions.locales = mergedOptions.locales || [];
|
|
95
97
|
// Add default config file name if not provided
|
|
@@ -101,14 +103,16 @@ export async function generateSettings(options, cwd = process.cwd()) {
|
|
|
101
103
|
// Add stageTranslations if not provided
|
|
102
104
|
// For human review, always stage the project
|
|
103
105
|
mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;
|
|
106
|
+
// Add publish if not provided
|
|
107
|
+
mergedOptions.publish = mergedOptions.publish ?? false;
|
|
104
108
|
// Populate src if not provided
|
|
105
109
|
mergedOptions.src = mergedOptions.src || DEFAULT_SRC_PATTERNS;
|
|
106
110
|
// Resolve all glob patterns in the files object
|
|
107
111
|
mergedOptions.files = mergedOptions.files
|
|
108
112
|
? resolveFiles(mergedOptions.files, mergedOptions.defaultLocale, mergedOptions.locales, cwd)
|
|
109
113
|
: undefined;
|
|
110
|
-
mergedOptions = {
|
|
111
|
-
...mergedOptions,
|
|
114
|
+
mergedOptions.options = {
|
|
115
|
+
...(mergedOptions.options || {}),
|
|
112
116
|
experimentalLocalizeStaticImports: gtConfig.options?.experimentalLocalizeStaticImports ||
|
|
113
117
|
options.experimentalLocalizeStaticImports,
|
|
114
118
|
experimentalLocalizeStaticUrls: gtConfig.options?.experimentalLocalizeStaticUrls ||
|
|
@@ -153,6 +157,7 @@ export async function generateSettings(options, cwd = process.cwd()) {
|
|
|
153
157
|
framework: mergedOptions.framework,
|
|
154
158
|
});
|
|
155
159
|
}
|
|
160
|
+
mergedOptions.configDirectory = path.join(cwd, '.gt');
|
|
156
161
|
validateSettings(mergedOptions);
|
|
157
162
|
// Set up GT instance
|
|
158
163
|
gt.setConfig({
|
package/dist/console/index.d.ts
CHANGED
|
@@ -18,3 +18,4 @@ export declare const noApiKeyError = "No API key found! Please provide an API ke
|
|
|
18
18
|
export declare const devApiKeyError = "You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gtx-cli auth -t production";
|
|
19
19
|
export declare const noProjectIdError = "No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.";
|
|
20
20
|
export declare const noVersionIdError = "No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.";
|
|
21
|
+
export declare const invalidConfigurationError = "Invalid files configuration! Please either provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.";
|
package/dist/console/index.js
CHANGED
|
@@ -21,3 +21,4 @@ export const noApiKeyError = `No API key found! Please provide an API key using
|
|
|
21
21
|
export const devApiKeyError = `You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gtx-cli auth -t production`;
|
|
22
22
|
export const noProjectIdError = `No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.`;
|
|
23
23
|
export const noVersionIdError = `No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.`;
|
|
24
|
+
export const invalidConfigurationError = `Invalid files configuration! Please either provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.`;
|
|
@@ -13,6 +13,7 @@ export declare function displayProjectId(projectId: string): void;
|
|
|
13
13
|
export declare function displayResolvedPaths(resolvedPaths: [string, string][]): void;
|
|
14
14
|
export declare function displayCreatedConfigFile(configFilepath: string): void;
|
|
15
15
|
export declare function displayUpdatedConfigFile(configFilepath: string): void;
|
|
16
|
+
export declare function displayUpdatedVersionsFile(versionFilepath: string): void;
|
|
16
17
|
export declare function createSpinner(indicator?: 'dots' | 'timer'): import("@clack/prompts").SpinnerResult;
|
|
17
18
|
export declare function createOraSpinner(indicator?: 'dots' | 'circleHalves'): Promise<import("ora").Ora>;
|
|
18
19
|
export declare function promptText({ message, defaultValue, validate, }: {
|
package/dist/console/logging.js
CHANGED
|
@@ -73,6 +73,9 @@ export function displayCreatedConfigFile(configFilepath) {
|
|
|
73
73
|
export function displayUpdatedConfigFile(configFilepath) {
|
|
74
74
|
log.success(`Updated config file ${chalk.cyan(configFilepath)}`);
|
|
75
75
|
}
|
|
76
|
+
export function displayUpdatedVersionsFile(versionFilepath) {
|
|
77
|
+
log.success(`Updated versions file ${chalk.cyan(versionFilepath)}`);
|
|
78
|
+
}
|
|
76
79
|
// Spinner functionality
|
|
77
80
|
export function createSpinner(indicator = 'timer') {
|
|
78
81
|
return spinner({ indicator });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ResolvedFiles, TransformFiles } from '../../types/index.js';
|
|
2
|
+
import { FileMapping } from '../../types/files.js';
|
|
2
3
|
/**
|
|
3
4
|
* Creates a mapping between source files and their translated counterparts for each locale
|
|
4
5
|
* @param filePaths - Resolved file paths for different file types
|
|
@@ -7,4 +8,4 @@ import { ResolvedFiles, TransformFiles } from '../../types/index.js';
|
|
|
7
8
|
* @param locales - List of locales to create a mapping for
|
|
8
9
|
* @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths
|
|
9
10
|
*/
|
|
10
|
-
export declare function createFileMapping(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, targetLocales: string[], defaultLocale: string):
|
|
11
|
+
export declare function createFileMapping(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, targetLocales: string[], defaultLocale: string): FileMapping;
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { getRelative } from '../../fs/findFilepath.js';
|
|
5
5
|
import { getLocaleProperties } from 'generaltranslation';
|
|
6
6
|
import { replaceLocalePlaceholders } from '../utils.js';
|
|
7
|
+
import { TEMPLATE_FILE_NAME } from '../../cli/commands/stage.js';
|
|
7
8
|
/**
|
|
8
9
|
* Creates a mapping between source files and their translated counterparts for each locale
|
|
9
10
|
* @param filePaths - Resolved file paths for different file types
|
|
@@ -18,6 +19,11 @@ export function createFileMapping(filePaths, placeholderPaths, transformPaths, t
|
|
|
18
19
|
const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);
|
|
19
20
|
const localeMapping = {};
|
|
20
21
|
// Process each file type
|
|
22
|
+
// Start with GTJSON Template files
|
|
23
|
+
if (translatedPaths.gt) {
|
|
24
|
+
const filepath = translatedPaths.gt;
|
|
25
|
+
localeMapping[TEMPLATE_FILE_NAME] = filepath;
|
|
26
|
+
}
|
|
21
27
|
for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {
|
|
22
28
|
if (!filePaths[typeIndex] || !translatedPaths[typeIndex])
|
|
23
29
|
continue;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Sends multiple files to the API for translation
|
|
6
|
-
* @param filePaths - Resolved file paths for different file types
|
|
7
|
-
* @param placeholderPaths - Placeholder paths for translated files
|
|
8
|
-
* @param transformPaths - Transform paths for file naming
|
|
9
|
-
* @param dataFormat - Format of the data within the files
|
|
10
|
-
* @param options - Translation options including API settings
|
|
11
|
-
* @returns Promise that resolves when translation is complete
|
|
12
|
-
*/
|
|
13
|
-
export declare function translateFiles(filePaths: ResolvedFiles, placeholderPaths: ResolvedFiles, transformPaths: TransformFiles, dataFormat: DataFormat | undefined, options: Settings & TranslateOptions): Promise<void>;
|
|
1
|
+
import { Settings } from '../../types/index.js';
|
|
2
|
+
import { FileToTranslate } from '../../types/data.js';
|
|
3
|
+
export declare const SUPPORTED_DATA_FORMATS: string[];
|
|
4
|
+
export declare function aggregateFiles(settings: Settings): Promise<FileToTranslate[]>;
|
|
@@ -1,38 +1,42 @@
|
|
|
1
|
-
import {
|
|
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';
|
|
1
|
+
import { logError } from '../../console/logging.js';
|
|
5
2
|
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';
|
|
9
3
|
import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
10
4
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
11
5
|
import { parseJson } from '../json/parseJson.js';
|
|
12
|
-
import { createFileMapping } from './fileMapping.js';
|
|
13
6
|
import parseYaml from '../yaml/parseYaml.js';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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
|
|
26
11
|
const allFiles = [];
|
|
27
|
-
|
|
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;
|
|
28
18
|
// Process JSON files
|
|
29
19
|
if (filePaths.json) {
|
|
30
|
-
|
|
31
|
-
|
|
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';
|
|
32
36
|
}
|
|
33
37
|
const jsonFiles = filePaths.json.map((filePath) => {
|
|
34
38
|
const content = readFile(filePath);
|
|
35
|
-
const parsedJson = parseJson(content, filePath,
|
|
39
|
+
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
36
40
|
const relativePath = getRelative(filePath);
|
|
37
41
|
return {
|
|
38
42
|
content: parsedJson,
|
|
@@ -45,18 +49,14 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
45
49
|
}
|
|
46
50
|
// Process YAML files
|
|
47
51
|
if (filePaths.yaml) {
|
|
48
|
-
if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
|
|
49
|
-
logErrorAndExit(noSupportedFormatError);
|
|
50
|
-
}
|
|
51
52
|
const yamlFiles = filePaths.yaml.map((filePath) => {
|
|
52
53
|
const content = readFile(filePath);
|
|
53
|
-
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath,
|
|
54
|
+
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
|
|
54
55
|
const relativePath = getRelative(filePath);
|
|
55
56
|
return {
|
|
56
57
|
content: parsedYaml,
|
|
57
58
|
fileName: relativePath,
|
|
58
59
|
fileFormat,
|
|
59
|
-
dataFormat,
|
|
60
60
|
};
|
|
61
61
|
});
|
|
62
62
|
allFiles.push(...yamlFiles);
|
|
@@ -73,7 +73,6 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
73
73
|
content: sanitizedContent,
|
|
74
74
|
fileName: relativePath,
|
|
75
75
|
fileFormat: fileType.toUpperCase(),
|
|
76
|
-
dataFormat,
|
|
77
76
|
};
|
|
78
77
|
});
|
|
79
78
|
allFiles.push(...files);
|
|
@@ -81,117 +80,6 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
81
80
|
}
|
|
82
81
|
if (allFiles.length === 0) {
|
|
83
82
|
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'));
|
|
195
83
|
}
|
|
196
|
-
return
|
|
84
|
+
return allFiles;
|
|
197
85
|
}
|