@localheroai/cli 0.0.41 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -65,7 +65,18 @@ program
65
65
  program
66
66
  .command('init')
67
67
  .description('Initialize a new LocalHero.ai project')
68
- .action(wrapCommandAction(() => init()));
68
+ .option('-y, --yes', 'Non-interactive mode: use flag values and never prompt')
69
+ .option('--project-id <id>', 'Use an existing project (skips creation)')
70
+ .option('--project-name <name>', 'Name for a new project (defaults to current directory name)')
71
+ .option('--source-locale <code>', 'Source locale, e.g. "en"')
72
+ .option('--target-locales <codes>', 'Comma-separated target locales, e.g. "sv,de,fr,nb-NO"')
73
+ .option('--path <dir>', 'Translation files directory, e.g. "config/locales/"')
74
+ .option('--pattern <glob>', 'Override detected file pattern')
75
+ .option('--ignore <paths>', 'Comma-separated ignore paths')
76
+ .option('--api-key <key>', 'API key for non-interactive authentication')
77
+ .option('--skip-import', 'Do not import existing translation files')
78
+ .option('--github-action', 'Create a GitHub Actions workflow (off by default in --yes mode)')
79
+ .action(wrapCommandAction((options) => init({ options })));
69
80
  program
70
81
  .command('translate')
71
82
  .description('Translate missing keys in your i18n files')
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAe,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAsB,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,EAAE,EAAa,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAmB,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAmB,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,SAAS,UAAU;IACjB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC7D,CAAC;IACF,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAQD,SAAS,cAAc,CAAC,KAAe;IACrC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAExE,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB,CAA6C,MAAS;IAC9E,OAAO,UAAU,GAAG,IAAmB;QACrC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAkB,CAAC;IACjF,CAAC,CAAC;AACJ,CAAC;AAED,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,gGAAgG,CAAC;KAC7G,OAAO,CAAC,UAAU,EAAE,CAAC;KACrB,MAAM,CAAC,SAAS,EAAE,0CAA0C,CAAC;KAC7D,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,sGAAsG,CAAC,CAAC;IACpH,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,iDAAiD,CAAC;KAC9D,MAAM,CAAC,iBAAiB,EAAE,4CAA4C,CAAC;KACvE,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAA4B,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AAElG,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,WAAW,EAAE,wDAAwD,CAAC;KAC7E,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,CAAC;KACvE,MAAM,CAAC,uBAAuB,EAAE,6DAA6D,CAAC;KAC9F,MAAM,CAAC,wBAAwB,EAAE,0BAA0B,CAAC;KAC5D,MAAM,CAAC,0BAA0B,EAAE,uDAAuD,CAAC;KAC3F,MAAM,CAAC,cAAc,EAAE,qDAAqD,CAAC;KAC7E,MAAM,CAAC,kBAAkB,EAAE,gCAAgC,CAAC;KAC5D,MAAM,CAAC,kBAAkB,EAAE,8BAA8B,CAAC;KAC1D,MAAM,CAAC,iBAAiB,EAAE,4CAA4C,CAAC;KACvE,MAAM,CAAC,eAAe,EAAE,0CAA0C,CAAC;KACnE,MAAM,CAAC,iBAAiB,EAAE,iEAAiE,CAAC;KAC5F,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAoB,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;AAE1E,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CAAC,eAAe,EAAE,oCAAoC,CAAC;KAC7D,MAAM,CAAC,cAAc,EAAE,0CAA0C,CAAC;KAClE,MAAM,CAAC,gBAAgB,EAAE,8DAA8D,CAAC;KACxF,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAA2B,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAElF,OAAO;KACJ,OAAO,CAAC,IAAI,CAAC;KACb,WAAW,CAAC,6DAA6D,CAAC;KAC1E,MAAM,CAAC,eAAe,EAAE,oCAAoC,CAAC;KAC7D,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAkB,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAElE,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,eAAe,EAAE,oCAAoC,CAAC;KAC7D,MAAM,CAAC,gBAAgB,EAAE,2DAA2D,CAAC;KACrF,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAqD,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAEvG,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,eAAe,EAAE,oCAAoC,CAAC;KAC7D,MAAM,CAAC,WAAW,EAAE,0BAA0B,CAAC;KAC/C,MAAM,CAAC,aAAa,EAAE,0CAA0C,CAAC;KACjE,MAAM,CAAC,SAAS,EAAE,mDAAmD,CAAC;KACtE,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAA+E,EAAE,EAAE;IAClH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,qBAAqB,EAAE,CAAC;IAC3D,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC,CAAC;AAEN,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,8DAA8D,CAAC;KAC3E,MAAM,CAAC,eAAe,EAAE,oCAAoC,CAAC;KAC7D,MAAM,CAAC,aAAa,EAAE,gCAAgC,CAAC;KACvD,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAA+C,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAElG,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,6BAA6B,CAAC;KAC1C,MAAM,CAAC,uBAAuB,EAAE,sBAAsB,CAAC;KACvD,MAAM,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;KACvD,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAE9E,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,uBAAuB,EAAE,sBAAsB,CAAC;KACvD,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAE9E,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -11,6 +11,15 @@ import { createGitHubActionFile, workflowExists } from '../utils/github.js';
11
11
  import { directoryExists, findFirstExistingPath, getDirectoryContents } from '../utils/files.js';
12
12
  import { verifyApiKey } from '../api/auth.js';
13
13
  import { Spinner } from '../utils/spinner.js';
14
+ export class MissingFlagsError extends Error {
15
+ flags;
16
+ constructor(flags) {
17
+ const hint = '\n(or use --project-id to reuse an existing project)';
18
+ super(`Missing required flags for --yes: ${flags.join(', ')}${hint}`);
19
+ this.name = 'MissingFlagsError';
20
+ this.flags = flags;
21
+ }
22
+ }
14
23
  const PROJECT_TYPES = {
15
24
  django: {
16
25
  directIndicators: ['manage.py'],
@@ -36,6 +45,21 @@ const PROJECT_TYPES = {
36
45
  'config/locales'
37
46
  ]
38
47
  },
48
+ nextIntl: {
49
+ directIndicators: [],
50
+ packageCheck: {
51
+ requires: ['next', 'next-intl']
52
+ },
53
+ defaults: {
54
+ translationPath: 'messages/',
55
+ filePattern: '**/*.json'
56
+ },
57
+ commonPaths: [
58
+ 'messages',
59
+ 'src/messages',
60
+ 'app/messages'
61
+ ]
62
+ },
39
63
  nextjs: {
40
64
  directIndicators: ['next.config.js', 'next.config.mjs'],
41
65
  packageCheck: {
@@ -85,6 +109,29 @@ const PROJECT_TYPES = {
85
109
  'i18n'
86
110
  ]
87
111
  },
112
+ lingui: {
113
+ directIndicators: [
114
+ 'lingui.config.js',
115
+ 'lingui.config.ts',
116
+ 'lingui.config.cjs',
117
+ 'lingui.config.mjs',
118
+ '.linguirc',
119
+ '.linguirc.json'
120
+ ],
121
+ packageCheck: {
122
+ oneOf: ['@lingui/cli', '@lingui/core', '@lingui/react', '@lingui/macro']
123
+ },
124
+ defaults: {
125
+ translationPath: 'src/locales/',
126
+ filePattern: '**/*.po',
127
+ sourceCodePaths: ['src/**/*.{ts,tsx,js,jsx}']
128
+ },
129
+ commonPaths: [
130
+ 'src/locales',
131
+ 'locales',
132
+ 'locale'
133
+ ]
134
+ },
88
135
  reactIntl: {
89
136
  directIndicators: ['.babelrc'],
90
137
  packageCheck: {
@@ -150,7 +197,10 @@ const PROJECT_TYPES = {
150
197
  'public/locales',
151
198
  'config/locales',
152
199
  'locales',
200
+ 'messages',
201
+ 'src/messages',
153
202
  'src/i18n',
203
+ 'app/i18n',
154
204
  'src/translations',
155
205
  'src/lang',
156
206
  'assets/i18n',
@@ -196,6 +246,22 @@ async function detectFramework(config) {
196
246
  }
197
247
  return false;
198
248
  }
249
+ export function buildFilePatternFromContents(contents) {
250
+ const formats = [];
251
+ if (contents.jsonFiles.length > 0)
252
+ formats.push('json');
253
+ if (contents.yamlFiles.length > 0)
254
+ formats.push('yml', 'yaml');
255
+ if (contents.poFiles.length > 0)
256
+ formats.push('po');
257
+ if (formats.length === 0) {
258
+ return '**/*.{json,yml,yaml,po}';
259
+ }
260
+ if (formats.length === 1 && formats[0] !== 'yml') {
261
+ return `**/*.${formats[0]}`;
262
+ }
263
+ return `**/*.{${formats.join(',')}}`;
264
+ }
199
265
  async function detectProjectType() {
200
266
  for (const [type, config] of Object.entries(PROJECT_TYPES)) {
201
267
  if (!config.directIndicators?.length && !config.packageCheck)
@@ -234,18 +300,7 @@ async function detectProjectType() {
234
300
  defaults: {
235
301
  commonPaths: commonPaths,
236
302
  translationPath: `${translationPath}/`,
237
- filePattern: (() => {
238
- const formats = [];
239
- if (contents.jsonFiles.length > 0)
240
- formats.push('json');
241
- if (contents.yamlFiles.length > 0)
242
- formats.push('yml', 'yaml');
243
- if (contents.poFiles.length > 0)
244
- formats.push('po');
245
- return formats.length === 1 && formats[0] !== 'yml'
246
- ? `**/*.${formats[0]}`
247
- : `**/*.{${formats.join(',')}}`;
248
- })()
303
+ filePattern: buildFilePatternFromContents(contents)
249
304
  }
250
305
  };
251
306
  }
@@ -325,8 +380,8 @@ async function handleImportProcess(config, basePath, importUtils, console, confi
325
380
  return { success: false, hasWarnings: false };
326
381
  }
327
382
  }
328
- async function handleGitHubWorkflowSetup(basePath, translationPaths, promptService, console) {
329
- if (workflowExists(basePath)) {
383
+ async function handleGitHubWorkflowSetup(basePath, translationPaths, promptService, console, githubUtils, autoAnswer, sourceCodePaths) {
384
+ if (githubUtils.workflowExists(basePath)) {
330
385
  console.log(chalk.green('✓ GitHub Actions workflow found'));
331
386
  console.log(chalk.yellow('\n⚠️ Remember to add your API key to repository secrets:'));
332
387
  console.log(' Name: LOCALHERO_API_KEY');
@@ -334,15 +389,21 @@ async function handleGitHubWorkflowSetup(basePath, translationPaths, promptServi
334
389
  console.log(' Location: Repository Settings → Secrets and variables → Actions (On GitHub repo page)\n');
335
390
  return { created: false };
336
391
  }
337
- const shouldSetupGitHubAction = await promptService.confirm({
338
- message: 'Would you like to set up GitHub Actions for automatic translations?',
339
- default: true
340
- });
392
+ let shouldSetupGitHubAction;
393
+ if (autoAnswer !== undefined) {
394
+ shouldSetupGitHubAction = autoAnswer;
395
+ }
396
+ else {
397
+ shouldSetupGitHubAction = await promptService.confirm({
398
+ message: 'Would you like to set up GitHub Actions for automatic translations?',
399
+ default: true
400
+ });
401
+ }
341
402
  if (!shouldSetupGitHubAction) {
342
403
  return { created: false };
343
404
  }
344
405
  try {
345
- const workflowFile = await createGitHubActionFile(basePath, translationPaths);
406
+ const workflowFile = await githubUtils.createGitHubActionFile(basePath, translationPaths, sourceCodePaths);
346
407
  console.log(chalk.green(`\n✓ Created GitHub Action workflow at ${workflowFile}`));
347
408
  console.log('\nNext steps:');
348
409
  console.log('1. Add your API key to your repository\'s secrets:');
@@ -358,6 +419,17 @@ async function handleGitHubWorkflowSetup(basePath, translationPaths, promptServi
358
419
  return { created: false, error: errorMessage };
359
420
  }
360
421
  }
422
+ function printLinguiWorkflowNotice(console) {
423
+ console.log(chalk.yellow('\n⚠️ Lingui projects need an extract step in the workflow'));
424
+ console.log('\nLingui generates .po files from your source code via `lingui extract`.');
425
+ console.log('Add this step to the workflow before the localhero-action step:\n');
426
+ console.log(chalk.gray(' - uses: actions/setup-node@v4'));
427
+ console.log(chalk.gray(' with:'));
428
+ console.log(chalk.gray(' node-version: \'20\''));
429
+ console.log(chalk.gray(' - run: npm ci'));
430
+ console.log(chalk.gray(' - run: npx lingui extract'));
431
+ console.log('\nWithout this, the workflow will not pick up new translatable strings.\n');
432
+ }
361
433
  function displayFinalInstructions(workflowCreated, workflowExists, hasErrors, console) {
362
434
  if (hasErrors) {
363
435
  return;
@@ -374,8 +446,8 @@ function displayFinalInstructions(workflowCreated, workflowExists, hasErrors, co
374
446
  }
375
447
  console.log(' npx @localheroai/cli translate');
376
448
  }
377
- async function handleExistingConfiguration(existingConfig, deps) {
378
- const { console, basePath, promptService, authUtils, projectApi, login: loginFn, importUtils, configUtils } = deps;
449
+ async function handleExistingConfiguration(existingConfig, options, nonInteractive, deps) {
450
+ const { console, basePath, promptService, authUtils, projectApi, importUtils, configUtils, githubUtils } = deps;
379
451
  let workflowCreated = false;
380
452
  console.log(chalk.green('✓ Configuration found! Let\'s verify and set up your API access.\n'));
381
453
  const validation = await validateExistingConfig(existingConfig);
@@ -389,19 +461,8 @@ async function handleExistingConfiguration(existingConfig, deps) {
389
461
  console.log(` Source: ${existingConfig.sourceLocale}`);
390
462
  console.log(` Targets: ${existingConfig.outputLocales.join(', ')}`);
391
463
  console.log(` Pattern: ${existingConfig.translationFiles?.pattern || 'not specified'}\n`);
392
- const isAuthenticated = await authUtils.checkAuth();
393
- if (!isAuthenticated) {
394
- console.log('Let\'s connect to your LocalHero account.');
395
- await loginFn({
396
- console,
397
- basePath,
398
- promptService,
399
- configUtils: deps.configUtils,
400
- verifyApiKey: authUtils.verifyApiKey,
401
- isCalledFromInit: true
402
- });
403
- }
404
- else {
464
+ await ensureAuthenticated(nonInteractive, options, deps);
465
+ if (await authUtils.checkAuth()) {
405
466
  console.log(chalk.green('✓ API key found and valid'));
406
467
  try {
407
468
  const projects = await projectApi.listProjects();
@@ -414,72 +475,70 @@ async function handleExistingConfiguration(existingConfig, deps) {
414
475
  console.log(chalk.green('✓ Project access verified'));
415
476
  }
416
477
  catch {
478
+ if (nonInteractive) {
479
+ throw new Error('Could not verify project access against the Localhero API');
480
+ }
417
481
  console.log(chalk.yellow('⚠️ Could not verify project access. Continuing anyway...\n'));
418
482
  }
419
483
  }
420
- // Check if we've previously imported files
421
484
  if (existingConfig.lastSyncedAt) {
422
485
  console.log(chalk.green('✓ Translation files previously imported'));
423
486
  }
424
487
  else {
425
- const shouldImport = await promptService.confirm({
426
- message: 'Would you like to import existing translation files? (recommended)',
427
- default: true
428
- });
488
+ let shouldImport;
489
+ if (nonInteractive) {
490
+ shouldImport = !options.skipImport;
491
+ }
492
+ else {
493
+ shouldImport = await promptService.confirm({
494
+ message: 'Would you like to import existing translation files? (recommended)',
495
+ default: true
496
+ });
497
+ }
429
498
  if (shouldImport) {
430
499
  await handleImportProcess(existingConfig, basePath, importUtils, console, configUtils);
431
500
  }
432
501
  }
433
- const workflowResult = await handleGitHubWorkflowSetup(basePath, existingConfig.translationFiles?.paths || [''], promptService, console);
502
+ const workflowResult = await handleGitHubWorkflowSetup(basePath, existingConfig.translationFiles?.paths || [''], promptService, console, githubUtils, nonInteractive ? options.githubAction === true : undefined);
434
503
  workflowCreated = workflowResult.created;
435
- displayFinalInstructions(workflowCreated, workflowExists(basePath), false, console);
504
+ displayFinalInstructions(workflowCreated, githubUtils.workflowExists(basePath), false, console);
436
505
  }
437
- async function handleNewProjectSetup(deps) {
438
- const { console, basePath, promptService, authUtils, projectApi, login: loginFn, importUtils, configUtils } = deps;
439
- const isAuthenticated = await authUtils.checkAuth();
440
- if (!isAuthenticated) {
441
- console.log('LocalHero.ai - Automate your i18n translations\n');
442
- console.log(chalk.yellow('No API key found. Let\'s get you authenticated.'));
443
- await loginFn({
444
- console,
445
- basePath,
446
- promptService,
447
- configUtils,
448
- verifyApiKey: authUtils.verifyApiKey || verifyApiKey,
449
- isCalledFromInit: true
450
- });
506
+ async function handleNewProjectSetup(options, nonInteractive, deps) {
507
+ const { console, basePath, promptService, projectApi, importUtils, configUtils, githubUtils } = deps;
508
+ await ensureAuthenticated(nonInteractive, options, deps);
509
+ if (!nonInteractive) {
510
+ console.log('\nLet\'s set up configuration for your project.\n');
451
511
  }
452
- console.log('\nLet\'s set up configuration for your project.\n');
453
512
  let workflowCreated = false;
454
513
  const projectDefaults = await detectProjectType();
455
- const answers = await promptForConfig(projectDefaults, projectApi, promptService, console);
456
- if (!answers) {
514
+ const inputs = nonInteractive
515
+ ? await collectInputsFromFlags(options, projectDefaults, projectApi, console)
516
+ : await collectInputsInteractive(projectDefaults, projectApi, promptService, console);
517
+ const finalized = await finalizeProjectAndBuildConfig(inputs, projectDefaults, projectApi, console);
518
+ if (!finalized) {
457
519
  return;
458
520
  }
459
- const config = {
460
- schemaVersion: '1.0',
461
- projectId: answers.projectId,
462
- sourceLocale: answers.sourceLocale,
463
- outputLocales: answers.outputLocales,
464
- translationFiles: {
465
- paths: answers.translationPath ? [answers.translationPath] : [],
466
- pattern: answers.filePattern || '**/*.{json,yml,yaml,po}',
467
- ignore: answers.ignorePaths || [],
468
- ...(projectDefaults.defaults.workflow && { workflow: projectDefaults.defaults.workflow })
469
- },
470
- lastSyncedAt: null
471
- };
521
+ const { config, newProject, url } = finalized;
472
522
  await configUtils.saveProjectConfig(config, basePath);
473
523
  console.log(chalk.green('\n✓ Created localhero.json'));
474
- if (answers.newProject) {
475
- console.log(chalk.green(`✓ Project created, view it at: ${answers.url}\n`));
524
+ if (newProject && url) {
525
+ console.log(chalk.green(`✓ Project created, view it at: ${url}\n`));
476
526
  }
477
- const workflowResult = await handleGitHubWorkflowSetup(basePath, answers.translationPath ? [answers.translationPath] : [''], promptService, console);
527
+ const workflowResult = await handleGitHubWorkflowSetup(basePath, config.translationFiles.paths.length > 0 ? config.translationFiles.paths : [''], promptService, console, githubUtils, nonInteractive ? options.githubAction === true : undefined, projectDefaults.defaults.sourceCodePaths);
478
528
  workflowCreated = workflowResult.created;
479
- const shouldImport = await promptService.confirm({
480
- message: 'Would you like to import existing translation files? (recommended)',
481
- default: true
482
- });
529
+ if (workflowCreated && projectDefaults.type === 'lingui') {
530
+ printLinguiWorkflowNotice(console);
531
+ }
532
+ let shouldImport;
533
+ if (nonInteractive) {
534
+ shouldImport = !options.skipImport;
535
+ }
536
+ else {
537
+ shouldImport = await promptService.confirm({
538
+ message: 'Would you like to import existing translation files? (recommended)',
539
+ default: true
540
+ });
541
+ }
483
542
  let hasErrors = false;
484
543
  if (shouldImport) {
485
544
  console.log('\nSearching for translation files in:');
@@ -487,48 +546,50 @@ async function handleNewProjectSetup(deps) {
487
546
  const importResult = await handleImportProcess(config, basePath, importUtils, console, configUtils);
488
547
  hasErrors = !importResult.success;
489
548
  }
490
- displayFinalInstructions(workflowCreated, workflowExists(basePath), hasErrors, console);
549
+ displayFinalInstructions(workflowCreated, githubUtils.workflowExists(basePath), hasErrors, console);
550
+ }
551
+ function normalizeTrailingSlash(p) {
552
+ return p.endsWith('/') ? p : `${p}/`;
491
553
  }
492
- async function promptForConfig(projectDefaults, projectService, promptService, console = global.console) {
554
+ function parseCsv(raw) {
555
+ return (raw ?? '').split(',').map(s => s.trim()).filter(Boolean);
556
+ }
557
+ function filterSourceFromTargets(sourceLocale, rawTargets, console) {
558
+ const filtered = rawTargets.filter(lang => lang !== sourceLocale);
559
+ if (filtered.length < rawTargets.length) {
560
+ console.log(chalk.yellow(`⚠️ Source language '${sourceLocale}' removed from target languages`));
561
+ }
562
+ return filtered;
563
+ }
564
+ async function collectInputsInteractive(projectDefaults, projectService, promptService, console) {
493
565
  const { choice: projectChoice, project: existingProject } = await promptService.selectProject(projectService);
494
566
  if (!projectChoice) {
495
567
  throw new Error('Project selection is required');
496
568
  }
497
- let projectId = projectChoice;
498
- let newProject = null;
499
- let projectUrl = null;
500
- let config;
569
+ let projectName;
570
+ let sourceLocale;
571
+ let outputLocales;
501
572
  if (!existingProject) {
502
- const sourceLocale = await promptService.input({
573
+ sourceLocale = await promptService.input({
503
574
  message: 'Source language locale:',
504
575
  default: 'en',
505
576
  hint: '\nThis is the language we will translate FROM. Enter the locale code as it appears in your I18n files. Examples:\n\n Framework File Structure Enter\n ----------- -------------------------------- --------\n Rails config/locales/en.yml en\n React locales/en_GB.json en_GB\n Next.js public/locales/en-US/common.json en-US\n'
506
577
  });
507
- const rawOutputLocales = (await promptService.input({
578
+ const rawOutputLocales = parseCsv(await promptService.input({
508
579
  message: 'Target language locales (comma-separated):',
509
580
  hint: '\nThese are the languages we will translate TO. Enter locale codes as they appear in your files:\n\n Pattern Type Target Files Enter\n ------------------ -------------------------------- --------------------\n Basic de.json, fr.json, es.json de,fr,es\n Region-specific fr-CA.json, es-MX.json, de-AT.json fr-CA,es-MX,de-AT\n Directory-based /locales/ja/, /locales/zh/ ja,zh\n'
510
- })).split(',').map(lang => lang.trim()).filter(Boolean);
511
- const outputLocales = rawOutputLocales.filter(lang => lang !== sourceLocale);
512
- if (outputLocales.length < rawOutputLocales.length) {
513
- console.log(chalk.yellow(`⚠️ Source language '${sourceLocale}' removed from target languages`));
514
- }
515
- config = {
516
- projectName: await promptService.input({
517
- message: 'Project name:',
518
- default: path.basename(process.cwd()),
519
- }),
520
- sourceLocale,
521
- outputLocales
522
- };
581
+ }));
582
+ outputLocales = filterSourceFromTargets(sourceLocale, rawOutputLocales, console);
583
+ projectName = await promptService.input({
584
+ message: 'Project name:',
585
+ default: path.basename(process.cwd()),
586
+ });
523
587
  }
524
588
  else {
525
- // existingProject is guaranteed to exist here since !existingProject was false
526
589
  const project = existingProject;
527
- config = {
528
- projectName: project.name,
529
- sourceLocale: project.source_language,
530
- outputLocales: project.target_languages
531
- };
590
+ projectName = project.name;
591
+ sourceLocale = project.source_language;
592
+ outputLocales = project.target_languages;
532
593
  }
533
594
  const commonPaths = projectDefaults.defaults.commonPaths || [];
534
595
  const existingDirs = [];
@@ -552,20 +613,100 @@ async function promptForConfig(projectDefaults, projectService, promptService, c
552
613
  });
553
614
  const filePattern = projectDefaults.defaults.filePattern;
554
615
  const defaultIgnorePaths = projectDefaults.defaults.ignorePaths || [];
555
- const ignorePaths = await promptService.input({
616
+ const ignorePathsRaw = await promptService.input({
556
617
  message: 'Paths to ignore (comma-separated, leave empty for none):',
557
618
  hint: ' Example: locales/ignored,locales/temp',
558
619
  default: defaultIgnorePaths.join(', ')
559
620
  });
560
- if (!existingProject) {
621
+ return {
622
+ mode: existingProject ? 'existing' : 'new',
623
+ existingProject: existingProject,
624
+ projectName,
625
+ sourceLocale,
626
+ outputLocales,
627
+ translationPath,
628
+ filePattern,
629
+ ignorePaths: parseCsv(ignorePathsRaw)
630
+ };
631
+ }
632
+ async function collectInputsFromFlags(options, projectDefaults, projectService, console) {
633
+ const filePattern = options.pattern ?? projectDefaults.defaults.filePattern;
634
+ const ignorePaths = options.ignore !== undefined
635
+ ? parseCsv(options.ignore)
636
+ : (projectDefaults.defaults.ignorePaths ?? []);
637
+ if (options.projectId) {
638
+ const projects = await projectService.listProjects();
639
+ const match = projects.find(p => p.id === options.projectId);
640
+ if (!match) {
641
+ throw new Error(`Project ${options.projectId} not found in your organization`);
642
+ }
643
+ const providedSource = options.sourceLocale;
644
+ const providedTargets = options.targetLocales ? parseCsv(options.targetLocales) : undefined;
645
+ const conflictsSource = providedSource !== undefined && providedSource !== match.source_language;
646
+ const conflictsTargets = providedTargets !== undefined &&
647
+ (providedTargets.length !== match.target_languages.length ||
648
+ providedTargets.some(l => !match.target_languages.includes(l)));
649
+ if (conflictsSource || conflictsTargets) {
650
+ const warnings = [];
651
+ if (conflictsSource)
652
+ warnings.push('--source-locale');
653
+ if (conflictsTargets)
654
+ warnings.push('--target-locales');
655
+ console.log(chalk.yellow(`⚠️ Ignoring ${warnings.join(' and ')}; using project '${match.name}' locales (source: ${match.source_language}, targets: ${match.target_languages.join(', ')})`));
656
+ }
657
+ if (!options.path) {
658
+ throw new MissingFlagsError(['--path']);
659
+ }
660
+ return {
661
+ mode: 'existing',
662
+ existingProject: match,
663
+ projectName: match.name,
664
+ sourceLocale: match.source_language,
665
+ outputLocales: match.target_languages,
666
+ translationPath: normalizeTrailingSlash(options.path),
667
+ filePattern,
668
+ ignorePaths
669
+ };
670
+ }
671
+ const missing = [];
672
+ if (!options.sourceLocale)
673
+ missing.push('--source-locale');
674
+ if (!options.targetLocales || parseCsv(options.targetLocales).length === 0)
675
+ missing.push('--target-locales');
676
+ if (!options.path)
677
+ missing.push('--path');
678
+ if (missing.length > 0) {
679
+ throw new MissingFlagsError(missing);
680
+ }
681
+ const sourceLocale = options.sourceLocale;
682
+ const outputLocales = filterSourceFromTargets(sourceLocale, parseCsv(options.targetLocales), console);
683
+ if (outputLocales.length === 0) {
684
+ throw new Error('After removing the source locale, no target locales remain');
685
+ }
686
+ return {
687
+ mode: 'new',
688
+ projectName: options.projectName ?? path.basename(process.cwd()),
689
+ sourceLocale,
690
+ outputLocales,
691
+ translationPath: normalizeTrailingSlash(options.path),
692
+ filePattern,
693
+ ignorePaths
694
+ };
695
+ }
696
+ async function finalizeProjectAndBuildConfig(inputs, projectDefaults, projectService, console) {
697
+ let projectId;
698
+ let projectUrl;
699
+ let isNewProject;
700
+ if (inputs.mode === 'new') {
561
701
  try {
562
- newProject = await projectService.createProject({
563
- name: config.projectName,
564
- sourceLocale: config.sourceLocale,
565
- targetLocales: config.outputLocales
702
+ const created = await projectService.createProject({
703
+ name: inputs.projectName,
704
+ sourceLocale: inputs.sourceLocale,
705
+ targetLocales: inputs.outputLocales
566
706
  });
567
- projectId = newProject.id;
568
- projectUrl = newProject.url;
707
+ projectId = created.id;
708
+ projectUrl = created.url;
709
+ isNewProject = true;
569
710
  }
570
711
  catch (error) {
571
712
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@@ -574,23 +715,54 @@ async function promptForConfig(projectDefaults, projectService, promptService, c
574
715
  }
575
716
  }
576
717
  else {
577
- const project = existingProject;
718
+ const project = inputs.existingProject;
578
719
  projectId = project.id;
579
720
  projectUrl = project.url;
721
+ isNewProject = false;
580
722
  }
581
- return {
723
+ const config = {
724
+ schemaVersion: '1.0',
582
725
  projectId,
583
- sourceLocale: config.sourceLocale,
584
- outputLocales: config.outputLocales,
585
- translationPath,
586
- filePattern,
587
- ignorePaths: ignorePaths.split(',').map(p => p.trim()).filter(Boolean),
588
- newProject: !existingProject,
589
- url: projectUrl
726
+ sourceLocale: inputs.sourceLocale,
727
+ outputLocales: inputs.outputLocales,
728
+ translationFiles: {
729
+ paths: inputs.translationPath ? [inputs.translationPath] : [],
730
+ pattern: inputs.filePattern || '**/*.{json,yml,yaml,po}',
731
+ ignore: inputs.ignorePaths,
732
+ ...(projectDefaults.defaults.workflow && { workflow: projectDefaults.defaults.workflow })
733
+ },
734
+ lastSyncedAt: null
590
735
  };
736
+ return { config, newProject: isNewProject, url: projectUrl };
737
+ }
738
+ async function ensureAuthenticated(nonInteractive, options, deps) {
739
+ const { console, basePath, promptService, configUtils, authUtils, login: loginFn } = deps;
740
+ if (await authUtils.checkAuth()) {
741
+ return;
742
+ }
743
+ const envKey = process.env.LOCALHERO_API_KEY;
744
+ const apiKey = options.apiKey || (envKey && envKey.length > 0 ? envKey : undefined);
745
+ if (apiKey || !nonInteractive) {
746
+ if (!nonInteractive && !apiKey) {
747
+ console.log('Localhero.ai - Automate your i18n translations\n');
748
+ console.log(chalk.yellow('No API key found. Let\'s get you authenticated.'));
749
+ }
750
+ await loginFn({
751
+ console,
752
+ basePath,
753
+ promptService,
754
+ configUtils,
755
+ verifyApiKey: authUtils.verifyApiKey || verifyApiKey,
756
+ isCalledFromInit: true,
757
+ ...(apiKey ? { apiKey } : {})
758
+ });
759
+ return;
760
+ }
761
+ throw new Error('API key required: pass --api-key, set LOCALHERO_API_KEY, or run `localhero login` first');
591
762
  }
592
763
  export async function init(deps = {}) {
593
- const { console = global.console, basePath = process.cwd(), promptService = createPromptService({ inquirer: await import('@inquirer/prompts') }), configUtils = configService, authUtils = { checkAuth }, importUtils = importService, projectApi = { createProject, listProjects }, login: loginFn = login } = deps;
764
+ const { console = global.console, basePath = process.cwd(), promptService = createPromptService({ inquirer: await import('@inquirer/prompts') }), configUtils = configService, authUtils = { checkAuth }, importUtils = importService, projectApi = { createProject, listProjects }, githubUtils = { createGitHubActionFile, workflowExists }, login: loginFn = login, options = {} } = deps;
765
+ const nonInteractive = options.yes === true;
594
766
  const requiredDeps = {
595
767
  console,
596
768
  basePath,
@@ -599,14 +771,15 @@ export async function init(deps = {}) {
599
771
  authUtils,
600
772
  importUtils,
601
773
  projectApi,
774
+ githubUtils,
602
775
  login: loginFn
603
776
  };
604
777
  const existingConfig = await configUtils.getProjectConfig(basePath);
605
778
  if (existingConfig) {
606
- await handleExistingConfiguration(existingConfig, requiredDeps);
779
+ await handleExistingConfiguration(existingConfig, options, nonInteractive, requiredDeps);
607
780
  }
608
781
  else {
609
- await handleNewProjectSetup(requiredDeps);
782
+ await handleNewProjectSetup(options, nonInteractive, requiredDeps);
610
783
  }
611
784
  }
612
785
  //# sourceMappingURL=init.js.map