gtx-cli 2.3.6-alpha.2 → 2.3.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/api/checkFileTranslations.d.ts +23 -0
  3. package/dist/api/checkFileTranslations.js +236 -0
  4. package/dist/api/downloadFileBatch.d.ts +20 -0
  5. package/dist/api/downloadFileBatch.js +113 -0
  6. package/dist/api/sendFiles.d.ts +17 -0
  7. package/dist/api/sendFiles.js +115 -0
  8. package/dist/api/uploadFiles.d.ts +27 -0
  9. package/dist/api/uploadFiles.js +40 -0
  10. package/dist/cli/base.d.ts +32 -0
  11. package/dist/cli/base.js +335 -0
  12. package/dist/cli/commands/stage.d.ts +5 -0
  13. package/dist/cli/commands/stage.js +100 -0
  14. package/dist/cli/commands/translate.d.ts +6 -0
  15. package/dist/cli/commands/translate.js +63 -0
  16. package/dist/cli/flags.d.ts +3 -0
  17. package/dist/cli/flags.js +38 -0
  18. package/dist/cli/next.d.ts +11 -0
  19. package/dist/cli/next.js +20 -0
  20. package/dist/cli/react.d.ts +18 -0
  21. package/dist/cli/react.js +175 -0
  22. package/dist/config/generateSettings.d.ts +9 -0
  23. package/dist/config/generateSettings.js +176 -0
  24. package/dist/config/optionPresets.d.ts +2 -0
  25. package/dist/config/optionPresets.js +56 -0
  26. package/dist/config/resolveConfig.d.ts +4 -0
  27. package/dist/config/resolveConfig.js +19 -0
  28. package/dist/config/utils.d.ts +2 -0
  29. package/dist/config/utils.js +4 -0
  30. package/dist/config/validateSettings.d.ts +3 -0
  31. package/dist/config/validateSettings.js +32 -0
  32. package/dist/console/colors.d.ts +5 -0
  33. package/dist/console/colors.js +16 -0
  34. package/dist/console/index.d.ts +21 -0
  35. package/dist/console/index.js +24 -0
  36. package/dist/console/logging.d.ts +53 -0
  37. package/dist/console/logging.js +185 -0
  38. package/dist/formats/files/fileMapping.d.ts +11 -0
  39. package/dist/formats/files/fileMapping.js +82 -0
  40. package/dist/formats/files/save.d.ts +5 -0
  41. package/dist/formats/files/save.js +17 -0
  42. package/dist/formats/files/supportedFiles.d.ts +10 -0
  43. package/dist/formats/files/supportedFiles.js +18 -0
  44. package/dist/formats/files/translate.d.ts +4 -0
  45. package/dist/formats/files/translate.js +119 -0
  46. package/dist/formats/files/upload.d.ts +13 -0
  47. package/dist/formats/files/upload.js +136 -0
  48. package/dist/formats/gt/save.d.ts +9 -0
  49. package/dist/formats/gt/save.js +26 -0
  50. package/dist/formats/json/flattenJson.d.ts +14 -0
  51. package/dist/formats/json/flattenJson.js +64 -0
  52. package/dist/formats/json/mergeJson.d.ts +13 -0
  53. package/dist/formats/json/mergeJson.js +257 -0
  54. package/dist/formats/json/parseJson.d.ts +2 -0
  55. package/dist/formats/json/parseJson.js +108 -0
  56. package/dist/formats/json/utils.d.ts +47 -0
  57. package/dist/formats/json/utils.js +149 -0
  58. package/dist/formats/utils.d.ts +2 -0
  59. package/dist/formats/utils.js +24 -0
  60. package/dist/formats/yaml/mergeYaml.d.ts +5 -0
  61. package/dist/formats/yaml/mergeYaml.js +55 -0
  62. package/dist/formats/yaml/parseYaml.d.ts +5 -0
  63. package/dist/formats/yaml/parseYaml.js +23 -0
  64. package/dist/formats/yaml/utils.d.ts +2 -0
  65. package/dist/formats/yaml/utils.js +22 -0
  66. package/dist/fs/config/loadConfig.d.ts +1 -0
  67. package/dist/fs/config/loadConfig.js +9 -0
  68. package/dist/fs/config/parseFilesConfig.d.ts +27 -0
  69. package/dist/fs/config/parseFilesConfig.js +129 -0
  70. package/dist/fs/config/setupConfig.d.ts +17 -0
  71. package/dist/fs/config/setupConfig.js +50 -0
  72. package/dist/fs/config/updateConfig.d.ts +10 -0
  73. package/dist/fs/config/updateConfig.js +36 -0
  74. package/dist/fs/config/updateVersions.d.ts +10 -0
  75. package/dist/fs/config/updateVersions.js +30 -0
  76. package/dist/fs/copyFile.d.ts +7 -0
  77. package/dist/fs/copyFile.js +39 -0
  78. package/dist/fs/createLoadTranslationsFile.d.ts +1 -0
  79. package/dist/fs/createLoadTranslationsFile.js +36 -0
  80. package/dist/fs/determineFramework.d.ts +5 -0
  81. package/dist/fs/determineFramework.js +46 -0
  82. package/dist/fs/findFilepath.d.ts +36 -0
  83. package/dist/fs/findFilepath.js +89 -0
  84. package/dist/fs/getPackageResource.d.ts +1 -0
  85. package/dist/fs/getPackageResource.js +6 -0
  86. package/dist/fs/index.d.ts +1 -0
  87. package/dist/fs/index.js +1 -0
  88. package/dist/fs/loadJSON.d.ts +6 -0
  89. package/dist/fs/loadJSON.js +17 -0
  90. package/dist/fs/matchFiles.d.ts +1 -0
  91. package/dist/fs/matchFiles.js +8 -0
  92. package/dist/fs/saveJSON.d.ts +1 -0
  93. package/dist/fs/saveJSON.js +7 -0
  94. package/dist/fs/utils.d.ts +1 -0
  95. package/dist/fs/utils.js +16 -0
  96. package/dist/hooks/postProcess.d.ts +4 -0
  97. package/dist/hooks/postProcess.js +110 -0
  98. package/dist/index.d.ts +4 -0
  99. package/dist/index.js +20 -0
  100. package/dist/main.d.ts +2 -0
  101. package/dist/main.js +9 -0
  102. package/dist/next/config/parseNextConfig.d.ts +10 -0
  103. package/dist/next/config/parseNextConfig.js +53 -0
  104. package/dist/next/jsx/utils.d.ts +7 -0
  105. package/dist/next/jsx/utils.js +42 -0
  106. package/dist/next/parse/handleInitGT.d.ts +7 -0
  107. package/dist/next/parse/handleInitGT.js +208 -0
  108. package/dist/next/parse/wrapContent.d.ts +11 -0
  109. package/dist/next/parse/wrapContent.js +163 -0
  110. package/dist/react/config/createESBuildConfig.d.ts +2 -0
  111. package/dist/react/config/createESBuildConfig.js +119 -0
  112. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.d.ts +8 -0
  113. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.js +111 -0
  114. package/dist/react/jsx/evaluateJsx.d.ts +17 -0
  115. package/dist/react/jsx/evaluateJsx.js +85 -0
  116. package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
  117. package/dist/react/jsx/trimJsxStringChildren.js +95 -0
  118. package/dist/react/jsx/utils/constants.d.ts +10 -0
  119. package/dist/react/jsx/utils/constants.js +31 -0
  120. package/dist/react/jsx/utils/parseAst.d.ts +30 -0
  121. package/dist/react/jsx/utils/parseAst.js +277 -0
  122. package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
  123. package/dist/react/jsx/utils/parseJsx.js +244 -0
  124. package/dist/react/jsx/utils/parseStringFunction.d.ts +16 -0
  125. package/dist/react/jsx/utils/parseStringFunction.js +411 -0
  126. package/dist/react/jsx/utils/validateStringFunction.d.ts +7 -0
  127. package/dist/react/jsx/utils/validateStringFunction.js +31 -0
  128. package/dist/react/jsx/wrapJsx.d.ts +51 -0
  129. package/dist/react/jsx/wrapJsx.js +387 -0
  130. package/dist/react/parse/createDictionaryUpdates.d.ts +3 -0
  131. package/dist/react/parse/createDictionaryUpdates.js +169 -0
  132. package/dist/react/parse/createInlineUpdates.d.ts +6 -0
  133. package/dist/react/parse/createInlineUpdates.js +122 -0
  134. package/dist/react/parse/wrapContent.d.ts +11 -0
  135. package/dist/react/parse/wrapContent.js +162 -0
  136. package/dist/react/utils/flattenDictionary.d.ts +20 -0
  137. package/dist/react/utils/flattenDictionary.js +75 -0
  138. package/dist/react/utils/getEntryAndMetadata.d.ts +5 -0
  139. package/dist/react/utils/getEntryAndMetadata.js +11 -0
  140. package/dist/react/utils/getVariableName.d.ts +25 -0
  141. package/dist/react/utils/getVariableName.js +37 -0
  142. package/dist/setup/userInput.d.ts +4 -0
  143. package/dist/setup/userInput.js +29 -0
  144. package/dist/setup/wizard.d.ts +2 -0
  145. package/dist/setup/wizard.js +127 -0
  146. package/dist/translation/parse.d.ts +15 -0
  147. package/dist/translation/parse.js +76 -0
  148. package/dist/translation/stage.d.ts +2 -0
  149. package/dist/translation/stage.js +44 -0
  150. package/dist/translation/validate.d.ts +2 -0
  151. package/dist/translation/validate.js +50 -0
  152. package/dist/types/data/json.d.ts +6 -0
  153. package/dist/types/data/json.js +1 -0
  154. package/dist/types/data.d.ts +30 -0
  155. package/dist/types/data.js +1 -0
  156. package/dist/types/files.d.ts +1 -0
  157. package/dist/types/files.js +1 -0
  158. package/dist/types/index.d.ts +173 -0
  159. package/dist/types/index.js +1 -0
  160. package/dist/utils/addExplicitAnchorIds.d.ts +24 -0
  161. package/dist/utils/addExplicitAnchorIds.js +260 -0
  162. package/dist/utils/constants.d.ts +2 -0
  163. package/dist/utils/constants.js +2 -0
  164. package/dist/utils/credentials.d.ts +12 -0
  165. package/dist/utils/credentials.js +119 -0
  166. package/dist/utils/flattenJsonFiles.d.ts +2 -0
  167. package/dist/utils/flattenJsonFiles.js +36 -0
  168. package/dist/utils/gt.d.ts +2 -0
  169. package/dist/utils/gt.js +2 -0
  170. package/dist/utils/hash.d.ts +6 -0
  171. package/dist/utils/hash.js +11 -0
  172. package/dist/utils/headers.d.ts +1 -0
  173. package/dist/utils/headers.js +14 -0
  174. package/dist/utils/installPackage.d.ts +3 -0
  175. package/dist/utils/installPackage.js +77 -0
  176. package/dist/utils/localizeStaticImports.d.ts +15 -0
  177. package/dist/utils/localizeStaticImports.js +341 -0
  178. package/dist/utils/localizeStaticUrls.d.ts +19 -0
  179. package/dist/utils/localizeStaticUrls.js +432 -0
  180. package/dist/utils/packageInfo.d.ts +3 -0
  181. package/dist/utils/packageInfo.js +17 -0
  182. package/dist/utils/packageJson.d.ts +6 -0
  183. package/dist/utils/packageJson.js +76 -0
  184. package/dist/utils/packageManager.d.ts +28 -0
  185. package/dist/utils/packageManager.js +269 -0
  186. package/dist/utils/processAnchorIds.d.ts +6 -0
  187. package/dist/utils/processAnchorIds.js +47 -0
  188. package/dist/utils/sanitizeFileContent.d.ts +6 -0
  189. package/dist/utils/sanitizeFileContent.js +29 -0
  190. package/dist/utils/validateMdx.d.ts +10 -0
  191. package/dist/utils/validateMdx.js +25 -0
  192. package/package.json +3 -3
@@ -0,0 +1,335 @@
1
+ import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
2
+ import findFilepath from '../fs/findFilepath.js';
3
+ import { displayHeader, promptText, logErrorAndExit, endCommand, promptConfirm, promptMultiSelect, logSuccess, logInfo, startCommand, createSpinner, logMessage, } from '../console/logging.js';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+ import { generateSettings } from '../config/generateSettings.js';
7
+ import chalk from 'chalk';
8
+ import { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';
9
+ import { handleSetupReactCommand } from '../setup/wizard.js';
10
+ import { isPackageInstalled, searchForPackageJson, } from '../utils/packageJson.js';
11
+ import { getDesiredLocales } from '../setup/userInput.js';
12
+ import { installPackage } from '../utils/installPackage.js';
13
+ import { getPackageManager } from '../utils/packageManager.js';
14
+ import { retrieveCredentials, setCredentials } from '../utils/credentials.js';
15
+ import { areCredentialsSet } from '../utils/credentials.js';
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';
21
+ import { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';
22
+ export class BaseCLI {
23
+ library;
24
+ additionalModules;
25
+ program;
26
+ // Constructor is shared amongst all CLI class types
27
+ constructor(program, library, additionalModules) {
28
+ this.program = program;
29
+ this.library = library;
30
+ this.additionalModules = additionalModules || [];
31
+ this.setupInitCommand();
32
+ this.setupConfigureCommand();
33
+ this.setupSetupCommand();
34
+ this.setupUploadCommand();
35
+ this.setupLoginCommand();
36
+ }
37
+ // Init is never called in a child class
38
+ init() {
39
+ this.setupTranslateCommand();
40
+ }
41
+ // Execute is called by the main program
42
+ execute() {
43
+ // If no command is specified, run 'init'
44
+ if (process.argv.length <= 2) {
45
+ process.argv.push('init');
46
+ }
47
+ }
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
59
+ .command('translate')
60
+ .description('Translate your project using General Translation')).action(async (initOptions) => {
61
+ displayHeader('Starting translation...');
62
+ await this.handleTranslate(initOptions);
63
+ endCommand('Done!');
64
+ });
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
+ if (results) {
83
+ await handleTranslate(initOptions, settings, results);
84
+ }
85
+ }
86
+ else {
87
+ await handleDownload(initOptions, settings);
88
+ }
89
+ await postProcessTranslations(settings);
90
+ }
91
+ setupUploadCommand() {
92
+ this.program
93
+ .command('upload')
94
+ .description('Upload source files and translations to the General Translation platform')
95
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
96
+ .option('--api-key <key>', 'API key for General Translation cloud service')
97
+ .option('--project-id <id>', 'Project ID for the translation service')
98
+ .option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
99
+ .action(async (initOptions) => {
100
+ displayHeader('Starting upload...');
101
+ const settings = await generateSettings(initOptions);
102
+ const options = { ...initOptions, ...settings };
103
+ await this.handleUploadCommand(options);
104
+ endCommand('Done!');
105
+ });
106
+ }
107
+ setupLoginCommand() {
108
+ this.program
109
+ .command('auth')
110
+ .description('Generate a General Translation API key and project ID')
111
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
112
+ .option('-t, --key-type <type>', 'Type of key to generate, production | development')
113
+ .action(async (options) => {
114
+ displayHeader('Authenticating with General Translation...');
115
+ if (!options.keyType) {
116
+ const packageJson = await searchForPackageJson();
117
+ const isUsingGTNext = packageJson
118
+ ? isPackageInstalled('gt-next', packageJson)
119
+ : false;
120
+ const isUsingGTReact = packageJson
121
+ ? isPackageInstalled('gt-react', packageJson)
122
+ : false;
123
+ if (isUsingGTNext || isUsingGTReact) {
124
+ options.keyType = 'development';
125
+ }
126
+ else {
127
+ options.keyType = 'production';
128
+ }
129
+ }
130
+ else {
131
+ if (options.keyType !== 'development' &&
132
+ options.keyType !== 'production') {
133
+ logErrorAndExit('Invalid key type, must be development or production');
134
+ }
135
+ }
136
+ await this.handleLoginCommand(options);
137
+ endCommand(`Done! A ${options.keyType} key has been generated and saved to your .env.local file.`);
138
+ });
139
+ }
140
+ setupInitCommand() {
141
+ this.program
142
+ .command('init')
143
+ .description('Run the setup wizard to configure your project for General Translation')
144
+ .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}'")
145
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
146
+ .action(async (options) => {
147
+ displayHeader('Running setup wizard...');
148
+ const packageJson = await searchForPackageJson();
149
+ let ranReactSetup = false;
150
+ // so that people can run init in non-js projects
151
+ if (packageJson && isPackageInstalled('react', packageJson)) {
152
+ const wrap = await promptConfirm({
153
+ message: `Detected that this project is using React. Would you like to run the React setup wizard?\nThis will install gt-react|gt-next as a dependency and internationalize your app.`,
154
+ defaultValue: true,
155
+ });
156
+ if (wrap) {
157
+ logInfo(`${chalk.yellow('[EXPERIMENTAL]')} Running React setup wizard...`);
158
+ await this.handleSetupReactCommand(options);
159
+ endCommand(`Done! Since this wizard is experimental, please review the changes and make modifications as needed.
160
+ Certain aspects of your app may still need manual setup.
161
+ See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`);
162
+ ranReactSetup = true;
163
+ }
164
+ }
165
+ if (ranReactSetup) {
166
+ startCommand('Setting up project config...');
167
+ }
168
+ // Configure gt.config.json
169
+ await this.handleInitCommand(ranReactSetup);
170
+ endCommand('Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs');
171
+ });
172
+ }
173
+ setupConfigureCommand() {
174
+ this.program
175
+ .command('configure')
176
+ .description('Configure your project for General Translation. This will create a gt.config.json file in your codebase.')
177
+ .action(async () => {
178
+ displayHeader('Configuring project...');
179
+ logInfo('Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.');
180
+ // Configure gt.config.json
181
+ await this.handleInitCommand(false);
182
+ endCommand('Done! Make sure you have an API key and project ID to use General Translation. Get them on the dashboard: https://generaltranslation.com/dashboard');
183
+ });
184
+ }
185
+ setupSetupCommand() {
186
+ this.program
187
+ .command('setup')
188
+ .description('Run the setup to configure your Next.js or React project for General Translation')
189
+ .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}'")
190
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
191
+ .action(async (options) => {
192
+ displayHeader('Running React setup wizard...');
193
+ await this.handleSetupReactCommand(options);
194
+ endCommand("Done! Take advantage of all of General Translation's features by signing up for a free account! https://generaltranslation.com/signup");
195
+ });
196
+ }
197
+ async handleUploadCommand(settings) {
198
+ // dataFormat for JSONs
199
+ let dataFormat;
200
+ if (this.library === 'next-intl') {
201
+ dataFormat = 'ICU';
202
+ }
203
+ else if (this.library === 'i18next') {
204
+ if (this.additionalModules.includes('i18next-icu')) {
205
+ dataFormat = 'ICU';
206
+ }
207
+ else {
208
+ dataFormat = 'I18NEXT';
209
+ }
210
+ }
211
+ else {
212
+ dataFormat = 'JSX';
213
+ }
214
+ if (!settings.files) {
215
+ return;
216
+ }
217
+ const { resolvedPaths: sourceFiles, placeholderPaths, transformPaths, } = settings.files;
218
+ // Process all file types at once with a single call
219
+ await upload(sourceFiles, placeholderPaths, transformPaths, dataFormat, settings);
220
+ }
221
+ async handleSetupReactCommand(options) {
222
+ await handleSetupReactCommand(options);
223
+ }
224
+ // Wizard for configuring gt.config.json
225
+ async handleInitCommand(ranReactSetup) {
226
+ const { defaultLocale, locales } = await getDesiredLocales();
227
+ const packageJson = await searchForPackageJson();
228
+ const isUsingGTNext = packageJson
229
+ ? isPackageInstalled('gt-next', packageJson)
230
+ : false;
231
+ const isUsingGTReact = packageJson
232
+ ? isPackageInstalled('gt-react', packageJson)
233
+ : false;
234
+ // Ask if using another i18n library
235
+ const isUsingGT = isUsingGTNext || isUsingGTReact || ranReactSetup;
236
+ // Ask where the translations are stored
237
+ const usingCDN = isUsingGT
238
+ ? await promptConfirm({
239
+ 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
240
+ ? 'https://generaltranslation.com/en/docs/next/guides/local-tx'
241
+ : '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.`,
242
+ defaultValue: true,
243
+ })
244
+ : false;
245
+ // Ask where the translations are stored
246
+ const translationsDir = isUsingGT && !usingCDN
247
+ ? await promptText({
248
+ message: 'What is the path to the directory where you would like to locally store your translations?',
249
+ defaultValue: './public/_gt',
250
+ })
251
+ : null;
252
+ // Determine final translations directory with fallback
253
+ const finalTranslationsDir = translationsDir?.trim() || './public/_gt';
254
+ if (isUsingGT && !usingCDN) {
255
+ // Create loadTranslations.js file for local translations
256
+ await createLoadTranslationsFile(process.cwd(), finalTranslationsDir);
257
+ logMessage(`Created ${chalk.cyan('loadTranslations.js')} file for local translations.
258
+ Make sure to add this function to your app configuration.
259
+ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
260
+ }
261
+ const message = !isUsingGT
262
+ ? 'What is the format of your language resource files? Select as many as applicable.\nAdditionally, you can translate any other files you have in your project.'
263
+ : `${chalk.dim('(Optional)')} Do you have any separate files you would like to translate? For example, extra Markdown files for docs.`;
264
+ const fileExtensions = await promptMultiSelect({
265
+ message,
266
+ options: [
267
+ { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },
268
+ { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },
269
+ { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },
270
+ { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },
271
+ { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },
272
+ { value: 'yaml', label: FILE_EXT_TO_EXT_LABEL.yaml },
273
+ ],
274
+ required: !isUsingGT,
275
+ });
276
+ const files = {};
277
+ for (const fileExtension of fileExtensions) {
278
+ const paths = await promptText({
279
+ message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Please enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\nMake sure to include [locale] in the patterns.\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,
280
+ defaultValue: `./**/[locale]/*.${fileExtension}`,
281
+ });
282
+ files[fileExtension] = {
283
+ include: paths.split(' '),
284
+ };
285
+ }
286
+ // Add GT translations if using GT and storing locally
287
+ if (isUsingGT && !usingCDN) {
288
+ files.gt = {
289
+ output: path.join(finalTranslationsDir, `[locale].json`),
290
+ };
291
+ }
292
+ let configFilepath = 'gt.config.json';
293
+ if (fs.existsSync('src/gt.config.json')) {
294
+ configFilepath = 'src/gt.config.json';
295
+ }
296
+ // Create gt.config.json
297
+ await createOrUpdateConfig(configFilepath, {
298
+ defaultLocale,
299
+ locales,
300
+ files: Object.keys(files).length > 0 ? files : undefined,
301
+ publish: isUsingGT && usingCDN,
302
+ });
303
+ logSuccess(`Feel free to edit ${chalk.cyan(configFilepath)} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`);
304
+ // Install gtx-cli if not installed
305
+ const isCLIInstalled = packageJson
306
+ ? isPackageInstalled('gtx-cli', packageJson, true, true)
307
+ : true; // if no package.json, we can't install it
308
+ if (!isCLIInstalled) {
309
+ const packageManager = await getPackageManager();
310
+ const spinner = createSpinner();
311
+ spinner.start(`Installing gtx-cli as a dev dependency with ${packageManager.name}...`);
312
+ await installPackage('gtx-cli', packageManager, true);
313
+ spinner.stop(chalk.green('Installed gtx-cli.'));
314
+ }
315
+ // Set credentials
316
+ if (!areCredentialsSet()) {
317
+ const loginQuestion = await promptConfirm({
318
+ message: `Would you like the wizard to automatically generate a ${isUsingGT ? 'development' : 'production'} API key and project ID for you?`,
319
+ defaultValue: true,
320
+ });
321
+ if (loginQuestion) {
322
+ const settings = await generateSettings({});
323
+ const keyType = isUsingGT ? 'development' : 'production';
324
+ const credentials = await retrieveCredentials(settings, keyType);
325
+ await setCredentials(credentials, keyType, settings.framework);
326
+ }
327
+ }
328
+ }
329
+ async handleLoginCommand(options) {
330
+ const settings = await generateSettings({});
331
+ const keyType = options.keyType || 'production';
332
+ const credentials = await retrieveCredentials(settings, keyType);
333
+ await setCredentials(credentials, keyType, settings.framework);
334
+ }
335
+ }
@@ -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,63 @@
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 flattenJsonFiles from '../../utils/flattenJsonFiles.js';
7
+ import localizeStaticUrls from '../../utils/localizeStaticUrls.js';
8
+ import processAnchorIds from '../../utils/processAnchorIds.js';
9
+ import { noFilesError, noVersionIdError } from '../../console/index.js';
10
+ import localizeStaticImports from '../../utils/localizeStaticImports.js';
11
+ // Downloads translations that were completed
12
+ export async function handleTranslate(options, settings, filesTranslationResponse) {
13
+ if (filesTranslationResponse && settings.files) {
14
+ const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
15
+ const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
16
+ const { data } = filesTranslationResponse;
17
+ // Check for remaining translations
18
+ await checkFileTranslations(data, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings, options.force);
19
+ }
20
+ }
21
+ // Downloads translations that were originally staged
22
+ export async function handleDownload(options, settings) {
23
+ if (!settings._versionId) {
24
+ logError(noVersionIdError);
25
+ process.exit(1);
26
+ }
27
+ if (!settings.files) {
28
+ logError(noFilesError);
29
+ process.exit(1);
30
+ }
31
+ // Files
32
+ const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
33
+ const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
34
+ const stagedVersionData = await getStagedVersions(settings.configDirectory);
35
+ // Check for remaining translations
36
+ await checkFileTranslations(stagedVersionData, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings, false // force is not applicable for downloading staged translations
37
+ );
38
+ }
39
+ export async function postProcessTranslations(settings) {
40
+ // Localize static urls (/docs -> /[locale]/docs) and preserve anchor IDs for non-default locales
41
+ // Default locale is processed earlier in the flow in base.ts
42
+ if (settings.options?.experimentalLocalizeStaticUrls) {
43
+ const nonDefaultLocales = settings.locales.filter((locale) => locale !== settings.defaultLocale);
44
+ if (nonDefaultLocales.length > 0) {
45
+ await localizeStaticUrls(settings, nonDefaultLocales);
46
+ }
47
+ // Add explicit anchor IDs to translated MDX/MD files to preserve navigation
48
+ // Uses inline {#id} format by default, or div wrapping if experimentalAddHeaderAnchorIds is 'mintlify'
49
+ await processAnchorIds(settings);
50
+ }
51
+ // Localize static imports (import Snippet from /snippets/file.mdx -> import Snippet from /snippets/[locale]/file.mdx)
52
+ if (settings.options?.experimentalLocalizeStaticImports) {
53
+ await localizeStaticImports(settings);
54
+ }
55
+ // Flatten json files into a single file
56
+ if (settings.options?.experimentalFlattenJsonFiles) {
57
+ await flattenJsonFiles(settings);
58
+ }
59
+ // Copy files to the target locale
60
+ if (settings.options?.copyFiles) {
61
+ await copyFile(settings);
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function attachTranslateFlags(command: Command): Command;
3
+ export declare function attachAdditionalReactTranslateFlags(command: Command): Command;
@@ -0,0 +1,38 @@
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
+ .option('--force', 'Force a retranslation, invalidating all existing cached translations if they exist.', false);
28
+ return command;
29
+ }
30
+ export function attachAdditionalReactTranslateFlags(command) {
31
+ command
32
+ .option('--tsconfig, --jsconfig <path>', 'Path to custom jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
33
+ .option('--dictionary <path>', 'Path to dictionary file')
34
+ .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}'")
35
+ .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
36
+ .option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false);
37
+ return command;
38
+ }
@@ -0,0 +1,11 @@
1
+ import { WrapOptions, SupportedFrameworks, SupportedLibraries } from '../types/index.js';
2
+ import { ReactCLI } from './react.js';
3
+ import { Command } from 'commander';
4
+ export declare class NextCLI extends ReactCLI {
5
+ constructor(command: Command, library: 'gt-next', additionalModules?: SupportedLibraries[]);
6
+ init(): void;
7
+ execute(): void;
8
+ protected wrapContent(options: WrapOptions, framework: SupportedFrameworks, errors: string[], warnings: string[]): Promise<{
9
+ filesUpdated: string[];
10
+ }>;
11
+ }
@@ -0,0 +1,20 @@
1
+ import { ReactCLI } from './react.js';
2
+ import { wrapContentNext } from '../next/parse/wrapContent.js';
3
+ const pkg = 'gt-next';
4
+ export class NextCLI extends ReactCLI {
5
+ constructor(command, library, additionalModules) {
6
+ super(command, library, additionalModules);
7
+ }
8
+ init() {
9
+ this.setupStageCommand();
10
+ this.setupTranslateCommand();
11
+ this.setupGenerateSourceCommand();
12
+ this.setupValidateCommand();
13
+ }
14
+ execute() {
15
+ super.execute();
16
+ }
17
+ wrapContent(options, framework, errors, warnings) {
18
+ return wrapContentNext(options, pkg, errors, warnings);
19
+ }
20
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from 'commander';
2
+ import { Options, SupportedFrameworks, WrapOptions, SupportedLibraries, TranslateFlags } from '../types/index.js';
3
+ import { BaseCLI } from './base.js';
4
+ export declare class ReactCLI extends BaseCLI {
5
+ constructor(command: Command, library: 'gt-react' | 'gt-next', additionalModules?: SupportedLibraries[]);
6
+ init(): void;
7
+ execute(): void;
8
+ protected wrapContent(options: WrapOptions, framework: SupportedFrameworks, errors: string[], warnings: string[]): Promise<{
9
+ filesUpdated: string[];
10
+ }>;
11
+ protected setupStageCommand(): void;
12
+ protected setupTranslateCommand(): void;
13
+ protected setupValidateCommand(): void;
14
+ protected setupGenerateSourceCommand(): void;
15
+ protected handleGenerateSourceCommand(initOptions: TranslateFlags): Promise<void>;
16
+ protected handleScanCommand(options: WrapOptions): Promise<void>;
17
+ protected handleValidate(initOptions: Options, files?: string[]): Promise<void>;
18
+ }