uniweb 0.6.18 → 0.6.20
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/README.md +21 -12
- package/package.json +3 -3
- package/partials/agents-md.hbs +24 -0
- package/src/commands/i18n.js +61 -89
package/README.md
CHANGED
|
@@ -85,6 +85,8 @@ my-project/
|
|
|
85
85
|
|
|
86
86
|
**Pages are folders.** Create `pages/about/` with markdown files inside → visit `/about`. That's the whole routing model.
|
|
87
87
|
|
|
88
|
+
**Batteries included:** File-based routing, pre-rendering, localization, dynamic routes, media processing, search indexing, and more. See [Building with Uniweb](https://github.com/uniweb/docs/blob/main/development/building-with-uniweb.md) for the full list.
|
|
89
|
+
|
|
88
90
|
### Content as Markdown
|
|
89
91
|
|
|
90
92
|
```markdown
|
|
@@ -163,7 +165,7 @@ After creating your project:
|
|
|
163
165
|
|
|
164
166
|
3. **Learn the configuration** — Run `uniweb docs site` or `uniweb docs page` for quick reference on configuration options.
|
|
165
167
|
|
|
166
|
-
4. **Create a section type** — Add a file to `foundation/src/sections/` (e.g., `Banner.jsx`) and rebuild. Bare files at the root are discovered automatically — no `meta.js` needed. Add `meta.js` when you want to declare params or presets. See the [Component Metadata
|
|
168
|
+
4. **Create a section type** — Add a file to `foundation/src/sections/` (e.g., `Banner.jsx`) and rebuild. Bare files at the root are discovered automatically — no `meta.js` needed. Add `meta.js` when you want to declare params or presets. See the [Component Metadata Reference](https://github.com/uniweb/docs/blob/main/reference/component-metadata.md) for the full schema.
|
|
167
169
|
|
|
168
170
|
The `meta.js` file defines what content and parameters a component accepts. The runtime uses this metadata to apply defaults and guarantee content structure—no defensive null checks needed in your component code.
|
|
169
171
|
|
|
@@ -234,24 +236,27 @@ Start with local files deployed anywhere. The same foundation works across all t
|
|
|
234
236
|
|
|
235
237
|
---
|
|
236
238
|
|
|
237
|
-
##
|
|
238
|
-
|
|
239
|
-
[Developer Guides](./guides/developers/) — Building foundations and components, converting designs, component patterns, theming contexts
|
|
239
|
+
## Documentation
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
Full documentation is available at **[github.com/uniweb/docs](https://github.com/uniweb/docs)**:
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
| Section | Topics |
|
|
244
|
+
| ------- | ------ |
|
|
245
|
+
| [Getting Started](https://github.com/uniweb/docs/tree/main/getting-started) | Introduction, quickstart, templates |
|
|
246
|
+
| [Authoring](https://github.com/uniweb/docs/tree/main/authoring) | Writing content, site setup, theming, collections, translations |
|
|
247
|
+
| [Development](https://github.com/uniweb/docs/tree/main/development) | Building foundations, component patterns, data fetching, layouts |
|
|
248
|
+
| [Reference](https://github.com/uniweb/docs/tree/main/reference) | Configuration files, kit API, CLI commands, deployment |
|
|
244
249
|
|
|
245
250
|
### Quick Reference
|
|
246
251
|
|
|
247
252
|
| Topic | Guide |
|
|
248
253
|
| ------------------ | ------------------------------------------------------------------- |
|
|
249
|
-
| Content Structure | [How markdown becomes component props](
|
|
250
|
-
| Component Metadata | [The meta.js schema](
|
|
251
|
-
| Site Configuration | [site.yml reference](
|
|
252
|
-
| CLI Commands | [create, build, docs, i18n](
|
|
253
|
-
| Templates | [Built-in, official, and external templates](
|
|
254
|
-
| Deployment | [Vercel, Netlify, Cloudflare, and more](
|
|
254
|
+
| Content Structure | [How markdown becomes component props](https://github.com/uniweb/docs/blob/main/reference/content-structure.md) |
|
|
255
|
+
| Component Metadata | [The meta.js schema](https://github.com/uniweb/docs/blob/main/reference/component-metadata.md) |
|
|
256
|
+
| Site Configuration | [site.yml reference](https://github.com/uniweb/docs/blob/main/reference/site-configuration.md) |
|
|
257
|
+
| CLI Commands | [create, build, docs, i18n](https://github.com/uniweb/docs/blob/main/reference/cli-commands.md) |
|
|
258
|
+
| Templates | [Built-in, official, and external templates](https://github.com/uniweb/docs/blob/main/getting-started/templates.md) |
|
|
259
|
+
| Deployment | [Vercel, Netlify, Cloudflare, and more](https://github.com/uniweb/docs/blob/main/reference/deployment.md) |
|
|
255
260
|
|
|
256
261
|
---
|
|
257
262
|
|
|
@@ -349,6 +354,10 @@ Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layou
|
|
|
349
354
|
|
|
350
355
|
Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index and a `[slug]` template that renders each post.
|
|
351
356
|
|
|
357
|
+
**Is Uniweb good for documentation sites?**
|
|
358
|
+
|
|
359
|
+
Yes — documentation is a natural fit. Content stays in markdown (easy to version, review, and contribute to), while the foundation handles navigation, search, and rendering. [Uniweb's own docs](https://github.com/uniweb/docs) use this pattern: pure markdown in a public repo, rendered by a separate foundation.
|
|
360
|
+
|
|
352
361
|
## Related Packages
|
|
353
362
|
|
|
354
363
|
- [`@uniweb/build`](https://github.com/uniweb/build) — Foundation build tooling
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.20",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"tar": "^7.0.0",
|
|
43
43
|
"@uniweb/core": "0.4.7",
|
|
44
44
|
"@uniweb/kit": "0.5.10",
|
|
45
|
-
"@uniweb/
|
|
46
|
-
"@uniweb/
|
|
45
|
+
"@uniweb/build": "0.6.17",
|
|
46
|
+
"@uniweb/runtime": "0.5.21"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/partials/agents-md.hbs
CHANGED
|
@@ -159,6 +159,10 @@ site/pages/
|
|
|
159
159
|
|
|
160
160
|
Decimals insert between: `2.5-testimonials.md` goes between `2-` and `3-`.
|
|
161
161
|
|
|
162
|
+
**Ignored files/folders:**
|
|
163
|
+
- `README.md` — repo documentation, not site content
|
|
164
|
+
- `_*.md` or `_*/` — drafts and private content (e.g., `_drafts/`, `_old-hero.md`)
|
|
165
|
+
|
|
162
166
|
**page.yml:**
|
|
163
167
|
```yaml
|
|
164
168
|
title: About Us
|
|
@@ -369,3 +373,23 @@ Use with: `bg-primary`, `text-primary`, `bg-primary/10`
|
|
|
369
373
|
**Component not appearing** — Verify `meta.js` exists and doesn't have `hidden: true`. Rebuild: `cd foundation && pnpm build`.
|
|
370
374
|
|
|
371
375
|
**Styles not applying** — Verify `@source` in `styles.css` includes your component paths. Check custom colors match `@theme` definitions.
|
|
376
|
+
|
|
377
|
+
## Further Documentation
|
|
378
|
+
|
|
379
|
+
Full Uniweb documentation is available at **https://github.com/uniweb/docs** — raw markdown files you can fetch directly.
|
|
380
|
+
|
|
381
|
+
| Section | Path | Topics |
|
|
382
|
+
|---------|------|--------|
|
|
383
|
+
| **Getting Started** | `getting-started/` | What is Uniweb, quickstart guide, templates overview |
|
|
384
|
+
| **Authoring** | `authoring/` | Writing content, site setup, collections, theming, linking, search, recipes, translations |
|
|
385
|
+
| **Development** | `development/` | Building foundations, component patterns, data fetching, custom layouts, i18n, converting existing designs |
|
|
386
|
+
| **Reference** | `reference/` | site.yml, page.yml, content structure, meta.js, kit hooks/components, theming tokens, CLI commands, deployment |
|
|
387
|
+
|
|
388
|
+
**Quick access pattern:** `https://raw.githubusercontent.com/uniweb/docs/main/{section}/{page}.md`
|
|
389
|
+
|
|
390
|
+
Examples:
|
|
391
|
+
- Content structure details: `reference/content-structure.md`
|
|
392
|
+
- Component metadata (meta.js): `reference/component-metadata.md`
|
|
393
|
+
- Kit hooks and components: `reference/kit-reference.md`
|
|
394
|
+
- Theming tokens: `reference/site-theming.md`
|
|
395
|
+
- Data fetching patterns: `reference/data-fetching.md`
|
package/src/commands/i18n.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Commands for managing site content internationalization.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* uniweb i18n extract
|
|
8
|
-
* uniweb i18n
|
|
9
|
-
* uniweb i18n
|
|
10
|
-
* uniweb i18n
|
|
11
|
-
*
|
|
7
|
+
* uniweb i18n extract Extract/update translatable strings (default)
|
|
8
|
+
* uniweb i18n generate [locales] Generate starter locale files from manifest
|
|
9
|
+
* uniweb i18n status Show translation coverage per locale
|
|
10
|
+
* uniweb i18n --target <path> Specify site directory explicitly
|
|
11
|
+
*
|
|
12
|
+
* Aliases: sync → extract, init → generate
|
|
12
13
|
*
|
|
13
14
|
* When run from workspace root, auto-detects sites. If multiple exist,
|
|
14
15
|
* prompts for selection.
|
|
@@ -81,9 +82,9 @@ export async function i18n(args) {
|
|
|
81
82
|
// Parse --target option
|
|
82
83
|
const { target, remainingArgs } = parseTargetOption(args)
|
|
83
84
|
|
|
84
|
-
// Default to '
|
|
85
|
+
// Default to 'extract' if no subcommand (or if first arg is an option)
|
|
85
86
|
const firstArg = remainingArgs[0]
|
|
86
|
-
const effectiveSubcommand = !firstArg || firstArg.startsWith('-') ? '
|
|
87
|
+
const effectiveSubcommand = !firstArg || firstArg.startsWith('-') ? 'extract' : firstArg
|
|
87
88
|
const effectiveArgs = !firstArg || firstArg.startsWith('-') ? remainingArgs : remainingArgs.slice(1)
|
|
88
89
|
|
|
89
90
|
// Find site root
|
|
@@ -98,14 +99,13 @@ export async function i18n(args) {
|
|
|
98
99
|
|
|
99
100
|
switch (effectiveSubcommand) {
|
|
100
101
|
case 'extract':
|
|
102
|
+
case 'sync':
|
|
101
103
|
await runExtract(siteRoot, config, effectiveArgs)
|
|
102
104
|
break
|
|
105
|
+
case 'generate':
|
|
103
106
|
case 'init':
|
|
104
107
|
await runInit(siteRoot, config, effectiveArgs)
|
|
105
108
|
break
|
|
106
|
-
case 'sync':
|
|
107
|
-
await runSync(siteRoot, config, effectiveArgs)
|
|
108
|
-
break
|
|
109
109
|
case 'status':
|
|
110
110
|
await runStatus(siteRoot, config, effectiveArgs)
|
|
111
111
|
break
|
|
@@ -228,13 +228,14 @@ async function loadSiteConfig(siteRoot) {
|
|
|
228
228
|
*/
|
|
229
229
|
async function runExtract(siteRoot, config, args) {
|
|
230
230
|
const verbose = args.includes('--verbose') || args.includes('-v')
|
|
231
|
+
const dryRun = args.includes('--dry-run')
|
|
231
232
|
const collectionsOnly = args.includes('--collections-only') || args.includes('--collections')
|
|
232
233
|
const noCollections = args.includes('--no-collections')
|
|
233
234
|
// --with-collections is now a no-op (collections are included by default)
|
|
234
235
|
|
|
235
236
|
// Extract page content (unless --collections-only)
|
|
236
237
|
if (!collectionsOnly) {
|
|
237
|
-
log(`\n${colors.cyan}Extracting translatable content...${colors.reset}\n`)
|
|
238
|
+
log(`\n${colors.cyan}Extracting translatable content${dryRun ? ' (dry run)' : ''}...${colors.reset}\n`)
|
|
238
239
|
|
|
239
240
|
// Check if site has been built
|
|
240
241
|
const siteContentPath = join(siteRoot, 'dist', 'site-content.json')
|
|
@@ -247,22 +248,32 @@ async function runExtract(siteRoot, config, args) {
|
|
|
247
248
|
// Dynamic import to avoid loading at CLI startup
|
|
248
249
|
const { extractManifest, formatSyncReport } = await import('@uniweb/build/i18n')
|
|
249
250
|
|
|
251
|
+
// Check if this is a first-time extract (no previous manifest)
|
|
252
|
+
const manifestPath = join(siteRoot, config.localesDir, 'manifest.json')
|
|
253
|
+
const isUpdate = existsSync(manifestPath)
|
|
254
|
+
|
|
250
255
|
const { manifest, report } = await extractManifest(siteRoot, {
|
|
251
256
|
localesDir: config.localesDir,
|
|
252
257
|
siteContentPath,
|
|
253
258
|
verbose,
|
|
259
|
+
dryRun,
|
|
254
260
|
})
|
|
255
261
|
|
|
256
262
|
// Show results
|
|
257
263
|
const unitCount = Object.keys(manifest.units).length
|
|
258
264
|
success(`Extracted ${unitCount} translatable strings`)
|
|
259
265
|
|
|
260
|
-
|
|
266
|
+
// Show sync report for updates (skip on first extract — everything would be "added")
|
|
267
|
+
if (report && isUpdate) {
|
|
261
268
|
log('')
|
|
262
269
|
log(formatSyncReport(report))
|
|
263
270
|
}
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
if (dryRun) {
|
|
273
|
+
log(`\n${colors.dim}Dry run — no files were modified.${colors.reset}`)
|
|
274
|
+
} else {
|
|
275
|
+
log(`\nManifest written to: ${colors.dim}${config.localesDir}/manifest.json${colors.reset}`)
|
|
276
|
+
}
|
|
266
277
|
|
|
267
278
|
if (config.locales.length === 0) {
|
|
268
279
|
log(`\n${colors.dim}No translation files found in ${config.localesDir}/.`)
|
|
@@ -277,7 +288,7 @@ async function runExtract(siteRoot, config, args) {
|
|
|
277
288
|
|
|
278
289
|
// Extract collection content (by default, skip with --no-collections)
|
|
279
290
|
if (!noCollections) {
|
|
280
|
-
log(`\n${colors.cyan}Extracting collection content...${colors.reset}\n`)
|
|
291
|
+
log(`\n${colors.cyan}Extracting collection content${dryRun ? ' (dry run)' : ''}...${colors.reset}\n`)
|
|
281
292
|
|
|
282
293
|
// Check if collections exist
|
|
283
294
|
const dataDir = join(siteRoot, 'public', 'data')
|
|
@@ -293,20 +304,28 @@ async function runExtract(siteRoot, config, args) {
|
|
|
293
304
|
try {
|
|
294
305
|
const { extractCollectionManifest, formatSyncReport } = await import('@uniweb/build/i18n')
|
|
295
306
|
|
|
307
|
+
const collectionsManifestPath = join(siteRoot, config.localesDir, 'collections', 'manifest.json')
|
|
308
|
+
const isUpdate = existsSync(collectionsManifestPath)
|
|
309
|
+
|
|
296
310
|
const { manifest, report } = await extractCollectionManifest(siteRoot, {
|
|
297
311
|
localesDir: config.localesDir,
|
|
312
|
+
dryRun,
|
|
298
313
|
})
|
|
299
314
|
|
|
300
315
|
const unitCount = Object.keys(manifest.units).length
|
|
301
316
|
if (unitCount > 0) {
|
|
302
317
|
success(`Extracted ${unitCount} translatable strings from collections`)
|
|
303
318
|
|
|
304
|
-
if (report) {
|
|
319
|
+
if (report && isUpdate) {
|
|
305
320
|
log('')
|
|
306
321
|
log(formatSyncReport(report))
|
|
307
322
|
}
|
|
308
323
|
|
|
309
|
-
|
|
324
|
+
if (dryRun) {
|
|
325
|
+
log(`\n${colors.dim}Dry run — no files were modified.${colors.reset}`)
|
|
326
|
+
} else {
|
|
327
|
+
log(`\nManifest written to: ${colors.dim}${config.localesDir}/collections/manifest.json${colors.reset}`)
|
|
328
|
+
}
|
|
310
329
|
} else {
|
|
311
330
|
log(`${colors.dim}No translatable content found in collections.${colors.reset}`)
|
|
312
331
|
}
|
|
@@ -319,13 +338,13 @@ async function runExtract(siteRoot, config, args) {
|
|
|
319
338
|
}
|
|
320
339
|
|
|
321
340
|
/**
|
|
322
|
-
*
|
|
341
|
+
* Generate command - generate starter translation files from manifest
|
|
323
342
|
*
|
|
324
343
|
* Usage:
|
|
325
|
-
* uniweb i18n
|
|
326
|
-
* uniweb i18n
|
|
327
|
-
* uniweb i18n
|
|
328
|
-
* uniweb i18n
|
|
344
|
+
* uniweb i18n generate es fr Generate specific locales
|
|
345
|
+
* uniweb i18n generate Generate all configured locales
|
|
346
|
+
* uniweb i18n generate --empty Use empty strings instead of source text
|
|
347
|
+
* uniweb i18n generate --force Overwrite existing files entirely
|
|
329
348
|
*/
|
|
330
349
|
async function runInit(siteRoot, config, args) {
|
|
331
350
|
const useEmpty = args.includes('--empty')
|
|
@@ -360,7 +379,7 @@ async function runInit(siteRoot, config, args) {
|
|
|
360
379
|
|
|
361
380
|
if (!targetLocales || targetLocales.length === 0) {
|
|
362
381
|
error('No target locales specified.')
|
|
363
|
-
log(`${colors.dim}Specify locales as arguments (e.g., "uniweb i18n
|
|
382
|
+
log(`${colors.dim}Specify locales as arguments (e.g., "uniweb i18n generate es fr")`)
|
|
364
383
|
log(`or configure them in site.yml under i18n.locales.${colors.reset}`)
|
|
365
384
|
process.exit(1)
|
|
366
385
|
}
|
|
@@ -425,55 +444,6 @@ async function runInit(siteRoot, config, args) {
|
|
|
425
444
|
log(` 3. Run 'uniweb i18n status' to check coverage${colors.reset}`)
|
|
426
445
|
}
|
|
427
446
|
|
|
428
|
-
/**
|
|
429
|
-
* Sync command - detect changes and update manifest
|
|
430
|
-
*/
|
|
431
|
-
async function runSync(siteRoot, config, args) {
|
|
432
|
-
const verbose = args.includes('--verbose') || args.includes('-v')
|
|
433
|
-
const dryRun = args.includes('--dry-run')
|
|
434
|
-
|
|
435
|
-
log(`\n${colors.cyan}Syncing i18n manifest...${colors.reset}\n`)
|
|
436
|
-
|
|
437
|
-
// Check if site has been built
|
|
438
|
-
const siteContentPath = join(siteRoot, 'dist', 'site-content.json')
|
|
439
|
-
if (!existsSync(siteContentPath)) {
|
|
440
|
-
error('Site content not found. Run "uniweb build" first.')
|
|
441
|
-
process.exit(1)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Check if manifest exists
|
|
445
|
-
const manifestPath = join(siteRoot, config.localesDir, 'manifest.json')
|
|
446
|
-
if (!existsSync(manifestPath)) {
|
|
447
|
-
warn('No existing manifest found. Running extract instead.')
|
|
448
|
-
return runExtract(siteRoot, config, args)
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
try {
|
|
452
|
-
const { extractManifest, formatSyncReport } = await import('@uniweb/build/i18n')
|
|
453
|
-
|
|
454
|
-
if (dryRun) {
|
|
455
|
-
log(`${colors.dim}(dry run - no files will be modified)${colors.reset}\n`)
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const { manifest, report } = await extractManifest(siteRoot, {
|
|
459
|
-
localesDir: config.localesDir,
|
|
460
|
-
siteContentPath,
|
|
461
|
-
verbose,
|
|
462
|
-
dryRun,
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
log(formatSyncReport(report))
|
|
466
|
-
|
|
467
|
-
if (!dryRun) {
|
|
468
|
-
success('\nManifest updated')
|
|
469
|
-
}
|
|
470
|
-
} catch (err) {
|
|
471
|
-
error(`Sync failed: ${err.message}`)
|
|
472
|
-
if (verbose) console.error(err)
|
|
473
|
-
process.exit(1)
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
447
|
/**
|
|
478
448
|
* Status command - show translation coverage
|
|
479
449
|
*/
|
|
@@ -1458,10 +1428,8 @@ ${colors.bright}Usage:${colors.reset}
|
|
|
1458
1428
|
|
|
1459
1429
|
${colors.bright}Commands:${colors.reset}
|
|
1460
1430
|
${colors.dim}# Hash-based (granular) translation${colors.reset}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
init Generate starter locale files from manifest keys
|
|
1464
|
-
sync Update manifest with content changes (detects moved/changed content)
|
|
1431
|
+
extract Extract/update translatable strings (default if no command given)
|
|
1432
|
+
generate Generate starter locale files from manifest keys
|
|
1465
1433
|
status Show translation coverage per locale
|
|
1466
1434
|
audit Find stale translations (no longer in manifest) and missing ones
|
|
1467
1435
|
|
|
@@ -1475,9 +1443,9 @@ ${colors.bright}Commands:${colors.reset}
|
|
|
1475
1443
|
${colors.bright}Options:${colors.reset}
|
|
1476
1444
|
-t, --target <path> Site directory (auto-detected if not specified)
|
|
1477
1445
|
--verbose Show detailed output
|
|
1478
|
-
--dry-run (
|
|
1479
|
-
--empty (
|
|
1480
|
-
--force (
|
|
1446
|
+
--dry-run (extract/prune) Preview changes without writing files
|
|
1447
|
+
--empty (generate) Use empty strings instead of source text
|
|
1448
|
+
--force (generate) Overwrite existing locale files entirely
|
|
1481
1449
|
--clean (audit) Remove stale entries from locale files
|
|
1482
1450
|
--missing (status) List all missing strings instead of summary
|
|
1483
1451
|
--freeform (status/prune) Include free-form translation status
|
|
@@ -1500,7 +1468,7 @@ ${colors.bright}Configuration:${colors.reset}
|
|
|
1500
1468
|
${colors.bright}Workflow:${colors.reset}
|
|
1501
1469
|
1. Build your site: uniweb build
|
|
1502
1470
|
2. Extract strings: uniweb i18n extract
|
|
1503
|
-
3.
|
|
1471
|
+
3. Generate locale files: uniweb i18n generate es fr
|
|
1504
1472
|
4. Translate locale files: Edit locales/es.json, locales/fr.json, etc.
|
|
1505
1473
|
5. Build with translations: uniweb build (generates locale-specific output)
|
|
1506
1474
|
|
|
@@ -1519,18 +1487,18 @@ ${colors.bright}File Structure:${colors.reset}
|
|
|
1519
1487
|
|
|
1520
1488
|
${colors.bright}Examples:${colors.reset}
|
|
1521
1489
|
${colors.dim}# Hash-based workflow${colors.reset}
|
|
1522
|
-
uniweb i18n extract
|
|
1523
|
-
uniweb i18n extract --
|
|
1490
|
+
uniweb i18n extract # Extract all translatable strings
|
|
1491
|
+
uniweb i18n extract --dry-run # Preview without writing
|
|
1492
|
+
uniweb i18n extract --verbose # Show extracted strings
|
|
1524
1493
|
uniweb i18n extract --no-collections # Pages only (skip collections)
|
|
1525
|
-
uniweb i18n
|
|
1526
|
-
uniweb i18n
|
|
1527
|
-
uniweb i18n
|
|
1528
|
-
uniweb i18n
|
|
1529
|
-
uniweb i18n status
|
|
1530
|
-
uniweb i18n status es # Show coverage for Spanish only
|
|
1494
|
+
uniweb i18n generate es fr # Create starter files for Spanish and French
|
|
1495
|
+
uniweb i18n generate --empty # Create files with empty values (for translators)
|
|
1496
|
+
uniweb i18n generate --force # Overwrite existing locale files
|
|
1497
|
+
uniweb i18n status # Show coverage for all locales
|
|
1498
|
+
uniweb i18n status es # Show coverage for Spanish only
|
|
1531
1499
|
uniweb i18n status es --missing --json # Export missing for AI translation
|
|
1532
|
-
uniweb i18n audit
|
|
1533
|
-
uniweb i18n audit --clean
|
|
1500
|
+
uniweb i18n audit # Find stale and missing translations
|
|
1501
|
+
uniweb i18n audit --clean # Remove stale entries
|
|
1534
1502
|
|
|
1535
1503
|
${colors.dim}# Free-form workflow (complete section replacement)${colors.reset}
|
|
1536
1504
|
uniweb i18n init-freeform es pages/about hero
|
|
@@ -1542,6 +1510,10 @@ ${colors.bright}Examples:${colors.reset}
|
|
|
1542
1510
|
uniweb i18n prune --freeform --dry-run # Preview orphan cleanup
|
|
1543
1511
|
uniweb i18n --target site # Specify site directory explicitly
|
|
1544
1512
|
|
|
1513
|
+
${colors.bright}Aliases:${colors.reset}
|
|
1514
|
+
sync → extract (backward-compatible)
|
|
1515
|
+
init → generate (backward-compatible)
|
|
1516
|
+
|
|
1545
1517
|
${colors.bright}Notes:${colors.reset}
|
|
1546
1518
|
Run from a site directory to operate on that site.
|
|
1547
1519
|
Run from workspace root to auto-detect sites (prompts if multiple).
|