@prettifystudio/lyngo-cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lyngo +70 -0
- package/package.json +1 -1
package/bin/lyngo
CHANGED
|
@@ -386,9 +386,77 @@ async function init(opts) {
|
|
|
386
386
|
project = await chooseLanguages(project, opts);
|
|
387
387
|
await scaffold(project);
|
|
388
388
|
setUpClaudeCode(top, remote);
|
|
389
|
+
await fillMissingCatalogs(project, opts);
|
|
389
390
|
console.log('Done. Build as usual; lyngo localize runs when your agent finishes, or run it yourself before committing.');
|
|
390
391
|
}
|
|
391
392
|
|
|
393
|
+
// A language added today has no catalog in the repository yet; the first
|
|
394
|
+
// `lyngo localize` would create one with the keys of the changed files only
|
|
395
|
+
// (pingcrm, 2026-10-12: 11 of 103 keys). Say so, and fill it on request.
|
|
396
|
+
async function fillMissingCatalogs(project, opts) {
|
|
397
|
+
const folder = catalogFolder(project);
|
|
398
|
+
const primary = project.primary_language || 'en';
|
|
399
|
+
const primaryPath = path.join(folder, primary + '.json');
|
|
400
|
+
if (!readIfFile(primaryPath)) return;
|
|
401
|
+
const targets = (project.languages || []).filter((l) => l !== primary);
|
|
402
|
+
const missing = targets.filter((l) => !readIfFile(path.join(folder, l + '.json')));
|
|
403
|
+
if (!missing.length) return;
|
|
404
|
+
const keys = Object.keys(parseCatalog(primaryPath) || {}).length;
|
|
405
|
+
console.log(`No catalog yet for ${missing.join(', ')}: ${folder}/<code>.json is missing, and ${primary}.json holds ${keys} keys that need translating.`);
|
|
406
|
+
const go = opts.yes ? 'y' : await ask(`Translate ${missing.length === 1 ? 'it' : 'them'} now? [Y/n] `);
|
|
407
|
+
if (go !== null && go !== '' && !/^y/i.test(go)) { console.log(`Later: lyngo translate ${missing.join(' ')}`); return; }
|
|
408
|
+
if (go === null) { console.log(`Run: lyngo translate ${missing.join(' ')}`); return; }
|
|
409
|
+
for (const code of missing) await translateLanguage(code, project);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function catalogFolder(project) { return ((project.i18n && project.i18n.catalog_folder) || 'lang').replace(/\/+$/, ''); }
|
|
413
|
+
function parseCatalog(p) { try { return JSON.parse(readIfFile(p) || ''); } catch { return null; } }
|
|
414
|
+
|
|
415
|
+
// ── translate: fill a language's catalog from the primary one ─────────────
|
|
416
|
+
async function translate(opts) {
|
|
417
|
+
const top = root();
|
|
418
|
+
process.chdir(top);
|
|
419
|
+
readToken();
|
|
420
|
+
const codes = opts._;
|
|
421
|
+
if (!codes.length) fail('usage: lyngo translate <code> [<code>...] (fills every key the language lacks)');
|
|
422
|
+
const st = await call('status', {});
|
|
423
|
+
const project = { primary_language: st.primary_language, languages: [st.primary_language, ...(st.languages || [])], i18n: st.i18n };
|
|
424
|
+
for (const code of codes) {
|
|
425
|
+
if (!project.languages.includes(code)) fail(`${code} is not one of the project's languages (${project.languages.join(', ')}). Add it with lyngo init --languages.`);
|
|
426
|
+
await translateLanguage(code, project);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function translateLanguage(code, project) {
|
|
431
|
+
const folder = catalogFolder(project);
|
|
432
|
+
const primary = project.primary_language || 'en';
|
|
433
|
+
const primaryPath = path.join(folder, primary + '.json');
|
|
434
|
+
const targetPath = path.join(folder, code + '.json');
|
|
435
|
+
const source = parseCatalog(primaryPath);
|
|
436
|
+
if (!source) fail(`${primaryPath} is missing or not valid JSON.`);
|
|
437
|
+
const current = parseCatalog(targetPath) || {};
|
|
438
|
+
const missing = Object.keys(source).filter((k) => !(k in current) || String(current[k]).trim() === '');
|
|
439
|
+
if (!missing.length) { console.log(`${code}: complete, ${Object.keys(source).length} keys.`); return; }
|
|
440
|
+
let translated = 0, memory = 0, flagged = [];
|
|
441
|
+
// Chunks keep each request under the server's time; the returned catalog carries forward.
|
|
442
|
+
for (let i = 0; i < missing.length; i += 100) {
|
|
443
|
+
const r = await call('translate', {
|
|
444
|
+
keys: missing.slice(i, i + 100),
|
|
445
|
+
languages: [code],
|
|
446
|
+
catalogs: [{ path: primaryPath, content: readIfFile(primaryPath) }, { path: targetPath, content: readIfFile(targetPath) || '{}' }],
|
|
447
|
+
});
|
|
448
|
+
const out = (r.catalogs || []).find((c) => c.path === targetPath || c.path.endsWith('/' + code + '.json'));
|
|
449
|
+
if (!out) fail(`the server returned no catalog for ${code}.`);
|
|
450
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
451
|
+
fs.writeFileSync(targetPath, out.content);
|
|
452
|
+
const l = (r.report && r.report.languages && r.report.languages[code]) || {};
|
|
453
|
+
translated += l.translated || 0; memory += l.from_memory || 0; flagged.push(...(l.flagged || []));
|
|
454
|
+
process.stdout.write(`\r${code}: ${Math.min(i + 100, missing.length)}/${missing.length}`);
|
|
455
|
+
}
|
|
456
|
+
const flaggedText = flagged.length ? `; ${flagged.length} flagged for review: ${flagged.slice(0, 3).map((x) => JSON.stringify(x.key || x)).join(', ')}` : '';
|
|
457
|
+
console.log(`\r${code}: ${translated} translated, ${memory} from memory${flaggedText} -> ${targetPath}`);
|
|
458
|
+
}
|
|
459
|
+
|
|
392
460
|
// ── localize, check, status ────────────────────────────────────────────────
|
|
393
461
|
|
|
394
462
|
function changedFiles(top) {
|
|
@@ -482,6 +550,7 @@ const HELP = `lyngo ${version()}: localization for your repository, from the ter
|
|
|
482
550
|
(--languages are the target languages; the source stays en unless --primary)
|
|
483
551
|
lyngo localize [--languages fr,de] [--dry]
|
|
484
552
|
localize and translate what changed in the working tree
|
|
553
|
+
lyngo translate <code> [<code>…] fill every key a language's catalog lacks, from the primary catalog
|
|
485
554
|
lyngo check <file> the user-facing strings of one file
|
|
486
555
|
lyngo status the project as Lyngo knows it
|
|
487
556
|
|
|
@@ -521,6 +590,7 @@ function parse(argv) {
|
|
|
521
590
|
case 'whoami': await whoami(); break;
|
|
522
591
|
case 'init': await init(opts); break;
|
|
523
592
|
case 'localize': await localize(opts); break;
|
|
593
|
+
case 'translate': await translate(opts); break;
|
|
524
594
|
case 'check': await check(opts._[0]); break;
|
|
525
595
|
case 'status': await status(); break;
|
|
526
596
|
default: console.error(`lyngo: unknown command "${cmd}".\n\n${HELP}`); process.exitCode = 1;
|
package/package.json
CHANGED