uniweb 0.5.6 → 0.5.7
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/package.json +1 -1
- package/src/commands/i18n.js +121 -3
package/package.json
CHANGED
package/src/commands/i18n.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* uniweb i18n extract Extract translatable strings to manifest
|
|
8
|
+
* uniweb i18n init [locales] Generate starter locale files from manifest
|
|
8
9
|
* uniweb i18n sync Sync manifest with content changes
|
|
9
10
|
* uniweb i18n status Show translation coverage per locale
|
|
10
11
|
* uniweb i18n --target <path> Specify site directory explicitly
|
|
@@ -99,6 +100,9 @@ export async function i18n(args) {
|
|
|
99
100
|
case 'extract':
|
|
100
101
|
await runExtract(siteRoot, config, effectiveArgs)
|
|
101
102
|
break
|
|
103
|
+
case 'init':
|
|
104
|
+
await runInit(siteRoot, config, effectiveArgs)
|
|
105
|
+
break
|
|
102
106
|
case 'sync':
|
|
103
107
|
await runSync(siteRoot, config, effectiveArgs)
|
|
104
108
|
break
|
|
@@ -313,6 +317,113 @@ async function runExtract(siteRoot, config, args) {
|
|
|
313
317
|
}
|
|
314
318
|
}
|
|
315
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Init command - generate starter translation files from manifest
|
|
322
|
+
*
|
|
323
|
+
* Usage:
|
|
324
|
+
* uniweb i18n init es fr Initialize specific locales
|
|
325
|
+
* uniweb i18n init Initialize all configured locales
|
|
326
|
+
* uniweb i18n init --empty Use empty strings instead of source text
|
|
327
|
+
* uniweb i18n init --force Overwrite existing files entirely
|
|
328
|
+
*/
|
|
329
|
+
async function runInit(siteRoot, config, args) {
|
|
330
|
+
const useEmpty = args.includes('--empty')
|
|
331
|
+
const force = args.includes('--force')
|
|
332
|
+
|
|
333
|
+
// Collect locale codes from positional args (skip flags)
|
|
334
|
+
const positionalLocales = args.filter(a => !a.startsWith('-'))
|
|
335
|
+
|
|
336
|
+
// Read manifest
|
|
337
|
+
const localesPath = join(siteRoot, config.localesDir)
|
|
338
|
+
const manifestPath = join(localesPath, 'manifest.json')
|
|
339
|
+
|
|
340
|
+
if (!existsSync(manifestPath)) {
|
|
341
|
+
error('No manifest found. Run "uniweb i18n extract" first to generate one.')
|
|
342
|
+
process.exit(1)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const manifestRaw = await readFile(manifestPath, 'utf-8')
|
|
346
|
+
const manifest = JSON.parse(manifestRaw)
|
|
347
|
+
const units = manifest.units || {}
|
|
348
|
+
const unitCount = Object.keys(units).length
|
|
349
|
+
|
|
350
|
+
if (unitCount === 0) {
|
|
351
|
+
warn('Manifest has no translatable strings. Nothing to initialize.')
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Determine target locales
|
|
356
|
+
let targetLocales = positionalLocales.length > 0
|
|
357
|
+
? positionalLocales
|
|
358
|
+
: config.locales
|
|
359
|
+
|
|
360
|
+
if (!targetLocales || targetLocales.length === 0) {
|
|
361
|
+
error('No target locales specified.')
|
|
362
|
+
log(`${colors.dim}Specify locales as arguments (e.g., "uniweb i18n init es fr")`)
|
|
363
|
+
log(`or configure them in site.yml under i18n.locales.${colors.reset}`)
|
|
364
|
+
process.exit(1)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
log(`\n${colors.cyan}Initializing translation files...${colors.reset}\n`)
|
|
368
|
+
|
|
369
|
+
await mkdir(localesPath, { recursive: true })
|
|
370
|
+
|
|
371
|
+
for (const locale of targetLocales) {
|
|
372
|
+
// Skip default locale
|
|
373
|
+
if (locale === config.defaultLocale) {
|
|
374
|
+
warn(`Skipped ${locale} (default locale)`)
|
|
375
|
+
continue
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const localePath = join(localesPath, `${locale}.json`)
|
|
379
|
+
|
|
380
|
+
if (existsSync(localePath) && !force) {
|
|
381
|
+
// Merge mode: add only missing keys
|
|
382
|
+
const existingRaw = await readFile(localePath, 'utf-8')
|
|
383
|
+
let existing
|
|
384
|
+
try {
|
|
385
|
+
existing = JSON.parse(existingRaw)
|
|
386
|
+
} catch {
|
|
387
|
+
warn(`${locale}.json has invalid JSON, skipping (use --force to overwrite)`)
|
|
388
|
+
continue
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const existingKeys = new Set(Object.keys(existing))
|
|
392
|
+
let added = 0
|
|
393
|
+
|
|
394
|
+
for (const [hash, unit] of Object.entries(units)) {
|
|
395
|
+
if (!existingKeys.has(hash)) {
|
|
396
|
+
existing[hash] = useEmpty ? '' : unit.source
|
|
397
|
+
added++
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (added > 0) {
|
|
402
|
+
await writeFile(localePath, JSON.stringify(existing, null, 2) + '\n')
|
|
403
|
+
const alreadyCount = existingKeys.size
|
|
404
|
+
success(`Updated ${locale}.json (${added} new string${added !== 1 ? 's' : ''} added, ${alreadyCount} already translated)`)
|
|
405
|
+
} else {
|
|
406
|
+
success(`${locale}.json already has all ${unitCount} strings`)
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
// Create new file (or overwrite with --force)
|
|
410
|
+
const localeData = {}
|
|
411
|
+
|
|
412
|
+
for (const [hash, unit] of Object.entries(units)) {
|
|
413
|
+
localeData[hash] = useEmpty ? '' : unit.source
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
await writeFile(localePath, JSON.stringify(localeData, null, 2) + '\n')
|
|
417
|
+
success(`Created ${locale}.json (${unitCount} string${unitCount !== 1 ? 's' : ''})`)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
log(`\n${colors.dim}Next steps:`)
|
|
422
|
+
log(` 1. Edit locale files to add translations`)
|
|
423
|
+
log(` 2. Run 'uniweb build' to build with translations`)
|
|
424
|
+
log(` 3. Run 'uniweb i18n status' to check coverage${colors.reset}`)
|
|
425
|
+
}
|
|
426
|
+
|
|
316
427
|
/**
|
|
317
428
|
* Sync command - detect changes and update manifest
|
|
318
429
|
*/
|
|
@@ -1348,6 +1459,7 @@ ${colors.bright}Commands:${colors.reset}
|
|
|
1348
1459
|
${colors.dim}# Hash-based (granular) translation${colors.reset}
|
|
1349
1460
|
(default) Same as sync - extract/update strings (runs if no command given)
|
|
1350
1461
|
extract Extract translatable strings to locales/manifest.json
|
|
1462
|
+
init Generate starter locale files from manifest keys
|
|
1351
1463
|
sync Update manifest with content changes (detects moved/changed content)
|
|
1352
1464
|
status Show translation coverage per locale
|
|
1353
1465
|
audit Find stale translations (no longer in manifest) and missing ones
|
|
@@ -1363,6 +1475,8 @@ ${colors.bright}Options:${colors.reset}
|
|
|
1363
1475
|
-t, --target <path> Site directory (auto-detected if not specified)
|
|
1364
1476
|
--verbose Show detailed output
|
|
1365
1477
|
--dry-run (sync/prune) Show changes without writing files
|
|
1478
|
+
--empty (init) Use empty strings instead of source text
|
|
1479
|
+
--force (init) Overwrite existing locale files entirely
|
|
1366
1480
|
--clean (audit) Remove stale entries from locale files
|
|
1367
1481
|
--missing (status) List all missing strings instead of summary
|
|
1368
1482
|
--freeform (status/prune) Include free-form translation status
|
|
@@ -1384,9 +1498,10 @@ ${colors.bright}Configuration:${colors.reset}
|
|
|
1384
1498
|
|
|
1385
1499
|
${colors.bright}Workflow:${colors.reset}
|
|
1386
1500
|
1. Build your site: uniweb build
|
|
1387
|
-
2. Extract strings: uniweb i18n
|
|
1388
|
-
3.
|
|
1389
|
-
4.
|
|
1501
|
+
2. Extract strings: uniweb i18n extract
|
|
1502
|
+
3. Initialize locale files: uniweb i18n init es fr
|
|
1503
|
+
4. Translate locale files: Edit locales/es.json, locales/fr.json, etc.
|
|
1504
|
+
5. Build with translations: uniweb build (generates locale-specific output)
|
|
1390
1505
|
|
|
1391
1506
|
${colors.bright}File Structure:${colors.reset}
|
|
1392
1507
|
locales/
|
|
@@ -1406,6 +1521,9 @@ ${colors.bright}Examples:${colors.reset}
|
|
|
1406
1521
|
uniweb i18n extract # Extract all translatable strings
|
|
1407
1522
|
uniweb i18n extract --verbose # Show extracted strings
|
|
1408
1523
|
uniweb i18n extract --with-collections # Extract pages + collections
|
|
1524
|
+
uniweb i18n init es fr # Create starter files for Spanish and French
|
|
1525
|
+
uniweb i18n init --empty # Create files with empty values (for translators)
|
|
1526
|
+
uniweb i18n init --force # Overwrite existing locale files
|
|
1409
1527
|
uniweb i18n sync # Update manifest after content changes
|
|
1410
1528
|
uniweb i18n status # Show coverage for all locales
|
|
1411
1529
|
uniweb i18n status es # Show coverage for Spanish only
|