@voltro/cli 0.1.0
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/CHANGELOG.md +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +20236 -0
- package/bin/voltro.mjs +43 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +9 -0
- package/dist/commands-CfPH2Wf4.js +18061 -0
- package/dist/frameworkInspectState-CX2250XB.js +86 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +5 -0
- package/dist/inspectState.d.ts +116 -0
- package/dist/inspectState.js +2 -0
- package/dist/startup.d.ts +26 -0
- package/dist/startup.js +2 -0
- package/dist/startupRunner-CRhuUl91.js +71 -0
- package/package.json +88 -0
- package/templates/AGENTS.core.md +258 -0
- package/templates/AGENTS.md +351 -0
- package/templates/agent-docs/_index.md +93 -0
- package/templates/agent-docs/_manifest.json +655 -0
- package/templates/agent-docs/ai.md +1845 -0
- package/templates/agent-docs/authentication.md +1788 -0
- package/templates/agent-docs/caching.md +624 -0
- package/templates/agent-docs/cli.md +1650 -0
- package/templates/agent-docs/configuration.md +295 -0
- package/templates/agent-docs/data.md +2172 -0
- package/templates/agent-docs/database/advancedqueries.md +1583 -0
- package/templates/agent-docs/database/columntypes.md +1200 -0
- package/templates/agent-docs/database/hosting.md +881 -0
- package/templates/agent-docs/database/migrations.md +2938 -0
- package/templates/agent-docs/database/misc.md +270 -0
- package/templates/agent-docs/database/overview.md +108 -0
- package/templates/agent-docs/database/querying.md +1622 -0
- package/templates/agent-docs/database/scaling.md +331 -0
- package/templates/agent-docs/database/schema.md +1458 -0
- package/templates/agent-docs/database/seedsdialects.md +1235 -0
- package/templates/agent-docs/database/transactions.md +285 -0
- package/templates/agent-docs/deployment.md +999 -0
- package/templates/agent-docs/internationalization.md +359 -0
- package/templates/agent-docs/introduction.md +438 -0
- package/templates/agent-docs/multi-tenancy.md +610 -0
- package/templates/agent-docs/observability.md +350 -0
- package/templates/agent-docs/plugins.md +1273 -0
- package/templates/agent-docs/reference.md +978 -0
- package/templates/agent-docs/routing.md +1553 -0
- package/templates/agent-docs/scheduling.md +666 -0
- package/templates/agent-docs/schema-driven-ui.md +607 -0
- package/templates/agent-docs/security.md +42 -0
- package/templates/agent-docs/templates/apibackends.md +3214 -0
- package/templates/agent-docs/templates/appshells.md +2062 -0
- package/templates/agent-docs/templates/custom.md +128 -0
- package/templates/agent-docs/templates/overview.md +122 -0
- package/templates/agent-docs/templates/serverless.md +315 -0
- package/templates/agent-docs/testing.md +376 -0
- package/templates/agent-docs/workflows.md +1351 -0
- package/templates/apps/api-ai/README.md +105 -0
- package/templates/apps/api-ai/actions/summarize.action.server.tsx +32 -0
- package/templates/apps/api-ai/actions/summarize.action.ts +33 -0
- package/templates/apps/api-ai/agents/support.agent.server.tsx +31 -0
- package/templates/apps/api-ai/agents/support.agent.tsx +27 -0
- package/templates/apps/api-ai/app.config.ts +57 -0
- package/templates/apps/api-ai/database/schema.ts +67 -0
- package/templates/apps/api-ai/package.json +27 -0
- package/templates/apps/api-ai/seeds/docs.seed.ts +67 -0
- package/templates/apps/api-ai/template.json +6 -0
- package/templates/apps/api-ai/tests/summarize.test.ts +50 -0
- package/templates/apps/api-ai/tools/searchDocs.tool.tsx +60 -0
- package/templates/apps/api-ai/tsconfig.json +5 -0
- package/templates/apps/api-auth/.env +17 -0
- package/templates/apps/api-auth/README.md +109 -0
- package/templates/apps/api-auth/actions/me.action.server.ts +20 -0
- package/templates/apps/api-auth/actions/me.action.ts +19 -0
- package/templates/apps/api-auth/app.config.ts +59 -0
- package/templates/apps/api-auth/database/schema.ts +26 -0
- package/templates/apps/api-auth/package.json +28 -0
- package/templates/apps/api-auth/template.json +6 -0
- package/templates/apps/api-auth/tests/me.test.ts +37 -0
- package/templates/apps/api-auth/tsconfig.json +5 -0
- package/templates/apps/api-backend/README.md +25 -0
- package/templates/apps/api-backend/app.config.ts +39 -0
- package/templates/apps/api-backend/database/schema.ts +57 -0
- package/templates/apps/api-backend/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend/package.json +28 -0
- package/templates/apps/api-backend/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend/template.json +6 -0
- package/templates/apps/api-backend/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend/tsconfig.json +5 -0
- package/templates/apps/api-backend-deactivation/README.md +49 -0
- package/templates/apps/api-backend-deactivation/actions/users.get.action.server.ts +17 -0
- package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +18 -0
- package/templates/apps/api-backend-deactivation/app.config.ts +20 -0
- package/templates/apps/api-backend-deactivation/database/schema.ts +41 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.server.ts +6 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +16 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.server.ts +10 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +19 -0
- package/templates/apps/api-backend-deactivation/package.json +28 -0
- package/templates/apps/api-backend-deactivation/template.json +6 -0
- package/templates/apps/api-backend-deactivation/tests/users.deactivate.test.ts +47 -0
- package/templates/apps/api-backend-deactivation/tsconfig.json +5 -0
- package/templates/apps/api-backend-mail/README.md +39 -0
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.server.ts +15 -0
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +21 -0
- package/templates/apps/api-backend-mail/app.config.ts +34 -0
- package/templates/apps/api-backend-mail/database/schema.ts +57 -0
- package/templates/apps/api-backend-mail/emails/welcome.email.tsx +106 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-mail/package.json +30 -0
- package/templates/apps/api-backend-mail/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-mail/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-mail/template.json +6 -0
- package/templates/apps/api-backend-mail/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-mail/tsconfig.json +5 -0
- package/templates/apps/api-backend-mariadb/.env.example +35 -0
- package/templates/apps/api-backend-mariadb/README.md +31 -0
- package/templates/apps/api-backend-mariadb/app.config.ts +68 -0
- package/templates/apps/api-backend-mariadb/database/schema.ts +57 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-mariadb/package.json +30 -0
- package/templates/apps/api-backend-mariadb/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-mariadb/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-mariadb/template.json +6 -0
- package/templates/apps/api-backend-mariadb/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-mariadb/tsconfig.json +5 -0
- package/templates/apps/api-backend-storage/README.md +86 -0
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.server.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.server.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +20 -0
- package/templates/apps/api-backend-storage/app.config.ts +38 -0
- package/templates/apps/api-backend-storage/database/schema.ts +57 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-storage/package.json +27 -0
- package/templates/apps/api-backend-storage/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-storage/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-storage/template.json +6 -0
- package/templates/apps/api-backend-storage/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-storage/tsconfig.json +5 -0
- package/templates/apps/api-data-advanced/.env +17 -0
- package/templates/apps/api-data-advanced/README.md +88 -0
- package/templates/apps/api-data-advanced/app.config.ts +56 -0
- package/templates/apps/api-data-advanced/database/actors.entity.ts +14 -0
- package/templates/apps/api-data-advanced/database/authors.entity.ts +30 -0
- package/templates/apps/api-data-advanced/database/authors.relations.ts +15 -0
- package/templates/apps/api-data-advanced/database/books.entity.ts +59 -0
- package/templates/apps/api-data-advanced/database/books.relations.ts +10 -0
- package/templates/apps/api-data-advanced/database/index.ts +23 -0
- package/templates/apps/api-data-advanced/database/tenants.entity.ts +11 -0
- package/templates/apps/api-data-advanced/package.json +28 -0
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.server.ts +18 -0
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +40 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.server.ts +17 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.ts +39 -0
- package/templates/apps/api-data-advanced/seeds/catalog.seed.ts +101 -0
- package/templates/apps/api-data-advanced/template.json +6 -0
- package/templates/apps/api-data-advanced/tests/queries.test.ts +124 -0
- package/templates/apps/api-data-advanced/tsconfig.json +5 -0
- package/templates/apps/api-durable/README.md +81 -0
- package/templates/apps/api-durable/aggregates/orderStats.aggregate.ts +49 -0
- package/templates/apps/api-durable/app.config.ts +41 -0
- package/templates/apps/api-durable/database/schema.ts +65 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.server.ts +43 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +23 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.server.ts +45 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.ts +40 -0
- package/templates/apps/api-durable/package.json +28 -0
- package/templates/apps/api-durable/schedules/nightlyReport.cron.tsx +37 -0
- package/templates/apps/api-durable/startup/warm.startup.tsx +33 -0
- package/templates/apps/api-durable/subscribers/orderChanges.subscribe.ts +27 -0
- package/templates/apps/api-durable/template.json +6 -0
- package/templates/apps/api-durable/tests/orders.place.test.ts +91 -0
- package/templates/apps/api-durable/triggers/order.placed.trigger.tsx +21 -0
- package/templates/apps/api-durable/tsconfig.json +5 -0
- package/templates/apps/api-durable/workflows/order.fulfill.workflow.server.tsx +103 -0
- package/templates/apps/api-durable/workflows/order.fulfill.workflow.tsx +25 -0
- package/templates/apps/api-feature-flags/README.md +63 -0
- package/templates/apps/api-feature-flags/actions/notes.export.action.server.ts +11 -0
- package/templates/apps/api-feature-flags/actions/notes.export.action.ts +18 -0
- package/templates/apps/api-feature-flags/app.config.ts +41 -0
- package/templates/apps/api-feature-flags/database/schema.ts +37 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.server.ts +29 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +28 -0
- package/templates/apps/api-feature-flags/package.json +29 -0
- package/templates/apps/api-feature-flags/template.json +6 -0
- package/templates/apps/api-feature-flags/tests/notes.create.test.ts +67 -0
- package/templates/apps/api-feature-flags/tsconfig.json +5 -0
- package/templates/apps/api-governance/.env +4 -0
- package/templates/apps/api-governance/README.md +61 -0
- package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -0
- package/templates/apps/api-governance/actions/profiles.get.action.ts +17 -0
- package/templates/apps/api-governance/app.config.ts +35 -0
- package/templates/apps/api-governance/database/schema.ts +41 -0
- package/templates/apps/api-governance/mutations/profiles.create.mutation.server.ts +17 -0
- package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +21 -0
- package/templates/apps/api-governance/package.json +29 -0
- package/templates/apps/api-governance/template.json +6 -0
- package/templates/apps/api-governance/tests/profiles.create.test.ts +59 -0
- package/templates/apps/api-governance/tsconfig.json +5 -0
- package/templates/apps/api-kv/README.md +100 -0
- package/templates/apps/api-kv/actions/sync.pull.action.server.ts +75 -0
- package/templates/apps/api-kv/actions/sync.pull.action.ts +25 -0
- package/templates/apps/api-kv/actions/sync.reset.action.server.ts +25 -0
- package/templates/apps/api-kv/actions/sync.reset.action.ts +22 -0
- package/templates/apps/api-kv/actions/sync.status.action.server.ts +28 -0
- package/templates/apps/api-kv/actions/sync.status.action.ts +17 -0
- package/templates/apps/api-kv/app.config.ts +48 -0
- package/templates/apps/api-kv/database/schema.ts +62 -0
- package/templates/apps/api-kv/package.json +28 -0
- package/templates/apps/api-kv/queries/events.list.query.server.ts +14 -0
- package/templates/apps/api-kv/queries/events.list.query.ts +23 -0
- package/templates/apps/api-kv/template.json +6 -0
- package/templates/apps/api-kv/tests/sync.test.ts +103 -0
- package/templates/apps/api-kv/tsconfig.json +5 -0
- package/templates/apps/api-moderation/README.md +46 -0
- package/templates/apps/api-moderation/app.config.ts +33 -0
- package/templates/apps/api-moderation/database/schema.ts +45 -0
- package/templates/apps/api-moderation/mutations/comments.create.mutation.server.ts +8 -0
- package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +20 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.server.ts +9 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +25 -0
- package/templates/apps/api-moderation/package.json +29 -0
- package/templates/apps/api-moderation/template.json +6 -0
- package/templates/apps/api-moderation/tests/posts.create.test.ts +79 -0
- package/templates/apps/api-moderation/tsconfig.json +5 -0
- package/templates/apps/api-observability/README.md +106 -0
- package/templates/apps/api-observability/app.config.ts +32 -0
- package/templates/apps/api-observability/database/schema.ts +38 -0
- package/templates/apps/api-observability/mutations/notes.create.mutation.server.ts +33 -0
- package/templates/apps/api-observability/mutations/notes.create.mutation.ts +15 -0
- package/templates/apps/api-observability/package.json +29 -0
- package/templates/apps/api-observability/queries/notes.list.query.server.ts +7 -0
- package/templates/apps/api-observability/queries/notes.list.query.ts +14 -0
- package/templates/apps/api-observability/template.json +6 -0
- package/templates/apps/api-observability/tests/notes.create.test.ts +36 -0
- package/templates/apps/api-observability/tsconfig.json +5 -0
- package/templates/apps/api-ratelimit/README.md +49 -0
- package/templates/apps/api-ratelimit/app.config.ts +42 -0
- package/templates/apps/api-ratelimit/database/schema.ts +37 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.server.ts +16 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +34 -0
- package/templates/apps/api-ratelimit/package.json +29 -0
- package/templates/apps/api-ratelimit/template.json +6 -0
- package/templates/apps/api-ratelimit/tests/notes.create.test.ts +69 -0
- package/templates/apps/api-ratelimit/tsconfig.json +5 -0
- package/templates/apps/api-rbac/README.md +59 -0
- package/templates/apps/api-rbac/app.config.ts +48 -0
- package/templates/apps/api-rbac/database/schema.ts +37 -0
- package/templates/apps/api-rbac/mutations/notes.create.mutation.server.ts +22 -0
- package/templates/apps/api-rbac/mutations/notes.create.mutation.ts +26 -0
- package/templates/apps/api-rbac/package.json +29 -0
- package/templates/apps/api-rbac/template.json +6 -0
- package/templates/apps/api-rbac/tests/notes.create.test.ts +80 -0
- package/templates/apps/api-rbac/tsconfig.json +5 -0
- package/templates/apps/api-rest/README.md +85 -0
- package/templates/apps/api-rest/app.config.ts +70 -0
- package/templates/apps/api-rest/database/schema.ts +52 -0
- package/templates/apps/api-rest/lib/product.ts +30 -0
- package/templates/apps/api-rest/package.json +27 -0
- package/templates/apps/api-rest/routes/v1/products.create.route.tsx +45 -0
- package/templates/apps/api-rest/routes/v1/products.delete.route.tsx +25 -0
- package/templates/apps/api-rest/routes/v1/products.get.route.tsx +29 -0
- package/templates/apps/api-rest/routes/v1/products.list.route.tsx +43 -0
- package/templates/apps/api-rest/template.json +6 -0
- package/templates/apps/api-rest/tests/products.create.test.ts +53 -0
- package/templates/apps/api-rest/tsconfig.json +5 -0
- package/templates/apps/api-saas/README.md +106 -0
- package/templates/apps/api-saas/app.config.ts +51 -0
- package/templates/apps/api-saas/database/schema.ts +50 -0
- package/templates/apps/api-saas/mutations/projects.create.mutation.server.ts +52 -0
- package/templates/apps/api-saas/mutations/projects.create.mutation.ts +18 -0
- package/templates/apps/api-saas/package.json +31 -0
- package/templates/apps/api-saas/queries/projects.list.query.server.ts +7 -0
- package/templates/apps/api-saas/queries/projects.list.query.ts +18 -0
- package/templates/apps/api-saas/template.json +6 -0
- package/templates/apps/api-saas/tests/projects.create.test.ts +47 -0
- package/templates/apps/api-saas/tsconfig.json +5 -0
- package/templates/apps/api-search/README.md +68 -0
- package/templates/apps/api-search/app.config.ts +31 -0
- package/templates/apps/api-search/database/schema.ts +47 -0
- package/templates/apps/api-search/lib/search.ts +27 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.server.ts +19 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.ts +35 -0
- package/templates/apps/api-search/package.json +29 -0
- package/templates/apps/api-search/queries/articles.list.query.server.ts +14 -0
- package/templates/apps/api-search/queries/articles.list.query.ts +19 -0
- package/templates/apps/api-search/seeds/articles.seed.ts +41 -0
- package/templates/apps/api-search/startup/searchBackfill.startup.tsx +24 -0
- package/templates/apps/api-search/template.json +6 -0
- package/templates/apps/api-search/tests/articles.create.test.ts +69 -0
- package/templates/apps/api-search/tsconfig.json +5 -0
- package/templates/apps/api-versioning/README.md +51 -0
- package/templates/apps/api-versioning/actions/documents.asOf.action.server.ts +13 -0
- package/templates/apps/api-versioning/actions/documents.asOf.action.ts +14 -0
- package/templates/apps/api-versioning/actions/documents.history.action.server.ts +15 -0
- package/templates/apps/api-versioning/actions/documents.history.action.ts +17 -0
- package/templates/apps/api-versioning/app.config.ts +24 -0
- package/templates/apps/api-versioning/database/schema.ts +38 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.server.ts +8 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +21 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.server.ts +9 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +21 -0
- package/templates/apps/api-versioning/package.json +29 -0
- package/templates/apps/api-versioning/template.json +6 -0
- package/templates/apps/api-versioning/tests/documents.create.test.ts +37 -0
- package/templates/apps/api-versioning/tsconfig.json +5 -0
- package/templates/apps/api-webhooks/.env +6 -0
- package/templates/apps/api-webhooks/README.md +106 -0
- package/templates/apps/api-webhooks/app.config.ts +16 -0
- package/templates/apps/api-webhooks/database/schema.ts +49 -0
- package/templates/apps/api-webhooks/events/order.completed.webhook.tsx +22 -0
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.server.ts +37 -0
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +15 -0
- package/templates/apps/api-webhooks/package.json +28 -0
- package/templates/apps/api-webhooks/queries/orders.list.query.server.ts +7 -0
- package/templates/apps/api-webhooks/queries/orders.list.query.ts +15 -0
- package/templates/apps/api-webhooks/template.json +6 -0
- package/templates/apps/api-webhooks/tests/orders.fulfill.test.ts +51 -0
- package/templates/apps/api-webhooks/tsconfig.json +5 -0
- package/templates/apps/api-webhooks/webhooks/orders.webhook.tsx +35 -0
- package/templates/apps/changelog/README.md +77 -0
- package/templates/apps/changelog/app.config.ts +27 -0
- package/templates/apps/changelog/content/releases/0.1.0.mdx +19 -0
- package/templates/apps/changelog/content/releases/0.2.0.mdx +28 -0
- package/templates/apps/changelog/package.json +30 -0
- package/templates/apps/changelog/scripts/generate-rss.mjs +38 -0
- package/templates/apps/changelog/src/globals.css +66 -0
- package/templates/apps/changelog/src/globals.d.ts +16 -0
- package/templates/apps/changelog/src/lib/locale.test.ts +72 -0
- package/templates/apps/changelog/src/lib/locale.ts +55 -0
- package/templates/apps/changelog/src/lib/releases.ts +21 -0
- package/templates/apps/changelog/src/locales/de.ts +22 -0
- package/templates/apps/changelog/src/locales/en.ts +30 -0
- package/templates/apps/changelog/src/pages/[locale]/[slug].tsx +24 -0
- package/templates/apps/changelog/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +95 -0
- package/templates/apps/changelog/src/pages/[slug].test.tsx +127 -0
- package/templates/apps/changelog/src/pages/[slug].tsx +52 -0
- package/templates/apps/changelog/src/pages/index.test.tsx +112 -0
- package/templates/apps/changelog/src/pages/index.tsx +55 -0
- package/templates/apps/changelog/src/pages/layout.tsx +84 -0
- package/templates/apps/changelog/template.json +6 -0
- package/templates/apps/changelog/tsconfig.json +11 -0
- package/templates/apps/edge-functions/README.md +55 -0
- package/templates/apps/edge-functions/functions/aiComplete.serverless.ts +58 -0
- package/templates/apps/edge-functions/functions/currencyConvert.serverless.ts +47 -0
- package/templates/apps/edge-functions/functions/geoGreeting.serverless.ts +44 -0
- package/templates/apps/edge-functions/functions/health.serverless.ts +29 -0
- package/templates/apps/edge-functions/functions/resolveLink.serverless.ts +32 -0
- package/templates/apps/edge-functions/functions/shareLink.serverless.ts +30 -0
- package/templates/apps/edge-functions/functions/slackNotify.serverless.ts +40 -0
- package/templates/apps/edge-functions/functions/verifySignature.serverless.ts +52 -0
- package/templates/apps/edge-functions/package.json +21 -0
- package/templates/apps/edge-functions/template.json +6 -0
- package/templates/apps/edge-functions/tsconfig.json +10 -0
- package/templates/apps/frontend-admin/README.md +40 -0
- package/templates/apps/frontend-admin/app.config.ts +37 -0
- package/templates/apps/frontend-admin/package.json +30 -0
- package/templates/apps/frontend-admin/src/config.ts +8 -0
- package/templates/apps/frontend-admin/src/globals.css +76 -0
- package/templates/apps/frontend-admin/src/globals.d.ts +6 -0
- package/templates/apps/frontend-admin/src/lib/admin.ts +16 -0
- package/templates/apps/frontend-admin/src/lib/auth.ts +24 -0
- package/templates/apps/frontend-admin/src/locales/de.ts +67 -0
- package/templates/apps/frontend-admin/src/locales/en.ts +79 -0
- package/templates/apps/frontend-admin/src/locales/index.ts +15 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/index.test.tsx +55 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/index.tsx +32 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/layout.tsx +29 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/login.test.tsx +73 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/login.tsx +35 -0
- package/templates/apps/frontend-admin/src/pages/admin/[entity].tsx +121 -0
- package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +119 -0
- package/templates/apps/frontend-admin/src/pages/admin/error.tsx +20 -0
- package/templates/apps/frontend-admin/src/pages/admin/fallbacks.test.tsx +68 -0
- package/templates/apps/frontend-admin/src/pages/admin/index.test.tsx +88 -0
- package/templates/apps/frontend-admin/src/pages/admin/index.tsx +65 -0
- package/templates/apps/frontend-admin/src/pages/admin/layout.test.tsx +114 -0
- package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +97 -0
- package/templates/apps/frontend-admin/src/pages/admin/loading.tsx +15 -0
- package/templates/apps/frontend-admin/src/pages/admin/not-found.tsx +15 -0
- package/templates/apps/frontend-admin/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-admin/src/pages/layouts.test.tsx +54 -0
- package/templates/apps/frontend-admin/template.json +6 -0
- package/templates/apps/frontend-admin/tsconfig.json +11 -0
- package/templates/apps/frontend-app/README.md +98 -0
- package/templates/apps/frontend-app/app.config.ts +43 -0
- package/templates/apps/frontend-app/package.json +30 -0
- package/templates/apps/frontend-app/src/locales/de.ts +36 -0
- package/templates/apps/frontend-app/src/locales/en.ts +43 -0
- package/templates/apps/frontend-app/src/locales/index.ts +15 -0
- package/templates/apps/frontend-app/src/pages/index.test.tsx +167 -0
- package/templates/apps/frontend-app/src/pages/index.tsx +136 -0
- package/templates/apps/frontend-app/src/pages/layout.tsx +41 -0
- package/templates/apps/frontend-app/src/pages/schema-ui.test.tsx +99 -0
- package/templates/apps/frontend-app/src/pages/schema-ui.tsx +74 -0
- package/templates/apps/frontend-app/template.json +6 -0
- package/templates/apps/frontend-app/tsconfig.json +11 -0
- package/templates/apps/frontend-blank/README.md +18 -0
- package/templates/apps/frontend-blank/app.config.ts +29 -0
- package/templates/apps/frontend-blank/package.json +29 -0
- package/templates/apps/frontend-blank/src/locales/de.ts +15 -0
- package/templates/apps/frontend-blank/src/locales/en.ts +22 -0
- package/templates/apps/frontend-blank/src/locales/index.ts +15 -0
- package/templates/apps/frontend-blank/src/pages/index.test.tsx +55 -0
- package/templates/apps/frontend-blank/src/pages/index.tsx +27 -0
- package/templates/apps/frontend-blank/src/pages/layout.test.tsx +54 -0
- package/templates/apps/frontend-blank/src/pages/layout.tsx +35 -0
- package/templates/apps/frontend-blank/template.json +6 -0
- package/templates/apps/frontend-blank/tsconfig.json +11 -0
- package/templates/apps/frontend-contact/README.md +65 -0
- package/templates/apps/frontend-contact/app.config.ts +25 -0
- package/templates/apps/frontend-contact/functions/sendMessage.serverless.ts +69 -0
- package/templates/apps/frontend-contact/package.json +33 -0
- package/templates/apps/frontend-contact/src/components/ContactForm.island.test.tsx +142 -0
- package/templates/apps/frontend-contact/src/components/ContactForm.island.tsx +104 -0
- package/templates/apps/frontend-contact/src/config.ts +12 -0
- package/templates/apps/frontend-contact/src/globals.css +84 -0
- package/templates/apps/frontend-contact/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-contact/src/locales/de.ts +26 -0
- package/templates/apps/frontend-contact/src/locales/en.ts +29 -0
- package/templates/apps/frontend-contact/src/pages/[locale]/index.tsx +14 -0
- package/templates/apps/frontend-contact/src/pages/index.test.tsx +70 -0
- package/templates/apps/frontend-contact/src/pages/index.tsx +63 -0
- package/templates/apps/frontend-contact/src/pages/layout.tsx +65 -0
- package/templates/apps/frontend-contact/template.json +6 -0
- package/templates/apps/frontend-contact/tsconfig.json +11 -0
- package/templates/apps/frontend-dashboard/README.md +54 -0
- package/templates/apps/frontend-dashboard/app.config.ts +39 -0
- package/templates/apps/frontend-dashboard/package.json +29 -0
- package/templates/apps/frontend-dashboard/src/config.ts +8 -0
- package/templates/apps/frontend-dashboard/src/globals.css +72 -0
- package/templates/apps/frontend-dashboard/src/globals.d.ts +6 -0
- package/templates/apps/frontend-dashboard/src/lib/auth.ts +27 -0
- package/templates/apps/frontend-dashboard/src/locales/de.ts +49 -0
- package/templates/apps/frontend-dashboard/src/locales/en.ts +60 -0
- package/templates/apps/frontend-dashboard/src/locales/index.ts +15 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.test.tsx +55 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.tsx +34 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/layout.tsx +32 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.test.tsx +74 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.tsx +40 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/error.tsx +21 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/fallbacks.test.tsx +68 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/index.test.tsx +55 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/index.tsx +38 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.test.tsx +88 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.tsx +69 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/loading.tsx +21 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/not-found.tsx +19 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.test.tsx +69 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.tsx +35 -0
- package/templates/apps/frontend-dashboard/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-dashboard/src/pages/layouts.test.tsx +54 -0
- package/templates/apps/frontend-dashboard/template.json +6 -0
- package/templates/apps/frontend-dashboard/tsconfig.json +11 -0
- package/templates/apps/frontend-docs/README.md +19 -0
- package/templates/apps/frontend-docs/app.config.ts +25 -0
- package/templates/apps/frontend-docs/package.json +29 -0
- package/templates/apps/frontend-docs/src/globals.css +32 -0
- package/templates/apps/frontend-docs/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-docs/src/lib/locale.ts +56 -0
- package/templates/apps/frontend-docs/src/locales/de.ts +24 -0
- package/templates/apps/frontend-docs/src/locales/en.ts +28 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug].tsx +18 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +67 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug].test.tsx +115 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug].tsx +82 -0
- package/templates/apps/frontend-docs/src/pages/index.test.tsx +90 -0
- package/templates/apps/frontend-docs/src/pages/index.tsx +45 -0
- package/templates/apps/frontend-docs/src/pages/layout.test.tsx +84 -0
- package/templates/apps/frontend-docs/src/pages/layout.tsx +66 -0
- package/templates/apps/frontend-docs/template.json +6 -0
- package/templates/apps/frontend-docs/tsconfig.json +11 -0
- package/templates/apps/frontend-i18n/README.md +61 -0
- package/templates/apps/frontend-i18n/app.config.ts +33 -0
- package/templates/apps/frontend-i18n/package.json +28 -0
- package/templates/apps/frontend-i18n/src/globals.css +46 -0
- package/templates/apps/frontend-i18n/src/globals.d.ts +6 -0
- package/templates/apps/frontend-i18n/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-i18n/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-i18n/src/locales/de.ts +24 -0
- package/templates/apps/frontend-i18n/src/locales/en.ts +25 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/about.tsx +11 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/mirrors.test.tsx +50 -0
- package/templates/apps/frontend-i18n/src/pages/about.test.tsx +75 -0
- package/templates/apps/frontend-i18n/src/pages/about.tsx +31 -0
- package/templates/apps/frontend-i18n/src/pages/index.test.tsx +102 -0
- package/templates/apps/frontend-i18n/src/pages/index.tsx +43 -0
- package/templates/apps/frontend-i18n/src/pages/layout.test.tsx +86 -0
- package/templates/apps/frontend-i18n/src/pages/layout.tsx +70 -0
- package/templates/apps/frontend-i18n/template.json +6 -0
- package/templates/apps/frontend-i18n/tsconfig.json +11 -0
- package/templates/apps/frontend-landing/README.md +17 -0
- package/templates/apps/frontend-landing/app.config.ts +25 -0
- package/templates/apps/frontend-landing/package.json +29 -0
- package/templates/apps/frontend-landing/src/globals.css +23 -0
- package/templates/apps/frontend-landing/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-landing/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-landing/src/locales/de.ts +24 -0
- package/templates/apps/frontend-landing/src/locales/en.ts +26 -0
- package/templates/apps/frontend-landing/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-landing/src/pages/[locale]/mirrors.test.tsx +37 -0
- package/templates/apps/frontend-landing/src/pages/index.test.tsx +110 -0
- package/templates/apps/frontend-landing/src/pages/index.tsx +75 -0
- package/templates/apps/frontend-landing/src/pages/layout.test.tsx +84 -0
- package/templates/apps/frontend-landing/src/pages/layout.tsx +66 -0
- package/templates/apps/frontend-landing/template.json +6 -0
- package/templates/apps/frontend-landing/tsconfig.json +11 -0
- package/templates/apps/frontend-spa/README.md +45 -0
- package/templates/apps/frontend-spa/app.config.ts +27 -0
- package/templates/apps/frontend-spa/package.json +29 -0
- package/templates/apps/frontend-spa/src/globals.css +84 -0
- package/templates/apps/frontend-spa/src/locales/de.ts +22 -0
- package/templates/apps/frontend-spa/src/locales/en.ts +29 -0
- package/templates/apps/frontend-spa/src/locales/index.ts +15 -0
- package/templates/apps/frontend-spa/src/pages/index.test.tsx +137 -0
- package/templates/apps/frontend-spa/src/pages/index.tsx +123 -0
- package/templates/apps/frontend-spa/src/pages/layout.tsx +27 -0
- package/templates/apps/frontend-spa/template.json +6 -0
- package/templates/apps/frontend-spa/tsconfig.json +11 -0
- package/templates/apps/frontend-ssr/README.md +68 -0
- package/templates/apps/frontend-ssr/app.config.ts +32 -0
- package/templates/apps/frontend-ssr/package.json +29 -0
- package/templates/apps/frontend-ssr/src/globals.css +67 -0
- package/templates/apps/frontend-ssr/src/locales/de.ts +41 -0
- package/templates/apps/frontend-ssr/src/locales/en.ts +54 -0
- package/templates/apps/frontend-ssr/src/locales/index.ts +16 -0
- package/templates/apps/frontend-ssr/src/pages/feed-swr.test.tsx +69 -0
- package/templates/apps/frontend-ssr/src/pages/feed-swr.tsx +54 -0
- package/templates/apps/frontend-ssr/src/pages/feed.test.tsx +73 -0
- package/templates/apps/frontend-ssr/src/pages/feed.tsx +64 -0
- package/templates/apps/frontend-ssr/src/pages/index.test.tsx +89 -0
- package/templates/apps/frontend-ssr/src/pages/index.tsx +72 -0
- package/templates/apps/frontend-ssr/src/pages/layout.tsx +37 -0
- package/templates/apps/frontend-ssr/template.json +6 -0
- package/templates/apps/frontend-ssr/tsconfig.json +11 -0
- package/templates/apps/frontend-ssr-api/README.md +50 -0
- package/templates/apps/frontend-ssr-api/app.config.ts +43 -0
- package/templates/apps/frontend-ssr-api/package.json +30 -0
- package/templates/apps/frontend-ssr-api/src/globals.css +38 -0
- package/templates/apps/frontend-ssr-api/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr-api/src/locales/de.ts +20 -0
- package/templates/apps/frontend-ssr-api/src/locales/en.ts +31 -0
- package/templates/apps/frontend-ssr-api/src/locales/index.ts +16 -0
- package/templates/apps/frontend-ssr-api/src/pages/index.test.tsx +105 -0
- package/templates/apps/frontend-ssr-api/src/pages/index.tsx +83 -0
- package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +28 -0
- package/templates/apps/frontend-ssr-api/template.json +6 -0
- package/templates/apps/frontend-ssr-api/tsconfig.json +11 -0
- package/templates/apps/frontend-static-blog/README.md +49 -0
- package/templates/apps/frontend-static-blog/app.config.ts +34 -0
- package/templates/apps/frontend-static-blog/package.json +28 -0
- package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.test.tsx +65 -0
- package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.tsx +35 -0
- package/templates/apps/frontend-static-blog/src/content/posts.ts +64 -0
- package/templates/apps/frontend-static-blog/src/globals.css +75 -0
- package/templates/apps/frontend-static-blog/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-static-blog/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-static-blog/src/locales/de.ts +19 -0
- package/templates/apps/frontend-static-blog/src/locales/en.ts +26 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug].tsx +20 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +62 -0
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug].test.tsx +116 -0
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug].tsx +69 -0
- package/templates/apps/frontend-static-blog/src/pages/index.test.tsx +100 -0
- package/templates/apps/frontend-static-blog/src/pages/index.tsx +58 -0
- package/templates/apps/frontend-static-blog/src/pages/layout.tsx +63 -0
- package/templates/apps/frontend-static-blog/template.json +6 -0
- package/templates/apps/frontend-static-blog/tsconfig.json +11 -0
- package/templates/baselines/bare/.env.example +37 -0
- package/templates/baselines/bare/README.md +47 -0
- package/templates/baselines/bare/baseline.json +31 -0
- package/templates/baselines/bare/deploy/README.md +43 -0
- package/templates/baselines/bare/deploy/voltro.service.example +36 -0
- package/templates/baselines/compose/.env.example +46 -0
- package/templates/baselines/compose/README.md +69 -0
- package/templates/baselines/compose/baseline.json +51 -0
- package/templates/baselines/compose/docker/.dockerignore +38 -0
- package/templates/baselines/compose/docker/api.Dockerfile +57 -0
- package/templates/baselines/compose/docker/dev.Dockerfile +35 -0
- package/templates/baselines/compose/docker/web.Dockerfile +59 -0
- package/templates/baselines/compose/docker-compose.dev.yml +89 -0
- package/templates/baselines/compose/docker-compose.prod.yml +87 -0
- package/templates/baselines/compose/docker-compose.yml +41 -0
- package/templates/baselines/compose-mariadb/.env.example +57 -0
- package/templates/baselines/compose-mariadb/README.md +78 -0
- package/templates/baselines/compose-mariadb/baseline.json +51 -0
- package/templates/baselines/compose-mariadb/docker/.dockerignore +38 -0
- package/templates/baselines/compose-mariadb/docker/api.Dockerfile +57 -0
- package/templates/baselines/compose-mariadb/docker/dev.Dockerfile +35 -0
- package/templates/baselines/compose-mariadb/docker/mariadb-init.sql +6 -0
- package/templates/baselines/compose-mariadb/docker/web.Dockerfile +59 -0
- package/templates/baselines/compose-mariadb/docker-compose.dev.yml +117 -0
- package/templates/baselines/compose-mariadb/docker-compose.prod.yml +114 -0
- package/templates/baselines/compose-mariadb/docker-compose.yml +79 -0
- package/templates/baselines/helm/.env.example +39 -0
- package/templates/baselines/helm/README.md +98 -0
- package/templates/baselines/helm/baseline.json +53 -0
- package/templates/baselines/helm/charts/voltro-app/.helmignore +10 -0
- package/templates/baselines/helm/charts/voltro-app/Chart.yaml +10 -0
- package/templates/baselines/helm/charts/voltro-app/templates/_helpers.tpl +36 -0
- package/templates/baselines/helm/charts/voltro-app/templates/configmap.yaml +13 -0
- package/templates/baselines/helm/charts/voltro-app/templates/deployment-api.yaml +120 -0
- package/templates/baselines/helm/charts/voltro-app/templates/deployment-web.yaml +45 -0
- package/templates/baselines/helm/charts/voltro-app/templates/ingress.yaml +37 -0
- package/templates/baselines/helm/charts/voltro-app/templates/postgres-service.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/templates/postgres-statefulset.yaml +73 -0
- package/templates/baselines/helm/charts/voltro-app/templates/secret.yaml +33 -0
- package/templates/baselines/helm/charts/voltro-app/templates/service-api.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/templates/service-web.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/values-dev.yaml +21 -0
- package/templates/baselines/helm/charts/voltro-app/values-prod.yaml +58 -0
- package/templates/baselines/helm/charts/voltro-app/values-staging.yaml +25 -0
- package/templates/baselines/helm/charts/voltro-app/values.yaml +109 -0
- package/templates/baselines/helm/deploy/README.md +94 -0
- package/templates/patches/@effect__cluster@0.59.0.patch +262 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Internationalization
|
|
2
|
+
|
|
3
|
+
> Voltro's i18n layer (@voltro/i18n) — an opinionated wrap over react-intl, auto-wired from a single app.config.ts field, with cookie + Accept-Language locale resolution.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- source: en/i18n/overview.md -->
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
_Voltro's i18n layer (@voltro/i18n) — an opinionated wrap over react-intl, auto-wired from a single app.config.ts field, with cookie + Accept-Language locale resolution._
|
|
13
|
+
|
|
14
|
+
Voltro ships internationalization in **`@voltro/i18n`** — an opinionated, thin wrap over [`react-intl`](https://formatjs.io/docs/react-intl/) plus framework auto-wiring. A web app gets a working `<I18nProvider>` from a single field in `app.config.ts`. You never write the provider, and you never import `@voltro/i18n` in your layout.
|
|
15
|
+
|
|
16
|
+
The public surface is intentionally small — `<I18nProvider>`, `<T>`, `useT`, `useTFn`, `useLocale`, `defineCatalog`, `defineLocale`. Power users who need an API the wrap doesn't expose (custom formatters, rich-text with React-element values, `IntlProvider`'s `timeZone` / `formats` props) `import { … } from 'react-intl'` directly. The wrap is **opt-in, not lock-in** — the library is in your `node_modules`, the wrap is optional.
|
|
17
|
+
|
|
18
|
+
## When to enable
|
|
19
|
+
|
|
20
|
+
Set `locales` in `app.config.ts` **the moment** your app ships more than one user-facing language, or expects to soon. The infrastructure has **zero cost when `locales` is unset** — no provider is generated, no bundle overhead. Enabling early avoids a painful retro-fit when the first translation request lands.
|
|
21
|
+
|
|
22
|
+
Don't enable just because the app *could* theoretically be translated. The framework's empty-i18n state is fine for English-only apps, and adding the wiring later is a one-config-field change.
|
|
23
|
+
|
|
24
|
+
## Setup — auto-wiring
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// apps/<project>/<app>/app.config.ts
|
|
28
|
+
export default {
|
|
29
|
+
type: 'web' as const,
|
|
30
|
+
name: 'myApp',
|
|
31
|
+
port: 5191,
|
|
32
|
+
// Codes the app SHIPS catalogs for. Every code listed here MUST
|
|
33
|
+
// have a corresponding `src/locales/<code>.ts` file or boot fails.
|
|
34
|
+
locales: ['en', 'de'] as const,
|
|
35
|
+
// Fallback locale when no cookie / Accept-Language matches a
|
|
36
|
+
// supported one. MUST be in `locales`. Pick your source-of-truth
|
|
37
|
+
// language — typically English.
|
|
38
|
+
defaultLocale: 'en' as const,
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The framework's generated `.framework/app.tsx` wraps the Router in `<I18nProvider>` automatically. You don't write the provider yourself; you don't import `@voltro/i18n` in your layout. Each code in `locales` must have a matching `src/locales/<code>.ts` catalog file — a missing file fails boot, not silently at runtime.
|
|
43
|
+
|
|
44
|
+
## Locale resolution order
|
|
45
|
+
|
|
46
|
+
Server-side, the active locale is determined by, in priority order:
|
|
47
|
+
|
|
48
|
+
1. **`voltro:lang` cookie** — the user's explicit choice (written by `@voltro/ui-shadcn`'s ProfileMenu).
|
|
49
|
+
2. **`Accept-Language` header** — the browser/OS preference, q-weighted and sorted per RFC 4647.
|
|
50
|
+
3. **`defaultLocale`** — last-resort fallback.
|
|
51
|
+
|
|
52
|
+
The resolved locale is **guaranteed** to be one of the codes in `locales`. Any unsupported value (a cookie pointing at a code you no longer ship, a browser asking for `xx-YY`) falls through to the next signal. RFC 4647 lookup strips subtags one segment at a time — `de-CH-1996` → `de-CH` → `de` — so a `de` catalog serves a `de-CH` browser.
|
|
53
|
+
|
|
54
|
+
The client side mirrors #1 (cookie) and #3 (default) for hydration safety. `Accept-Language` is **server-only** because `navigator.languages` can diverge from what the server saw, which would cause a hydration mismatch.
|
|
55
|
+
|
|
56
|
+
See [Catalogs](/docs/i18n/catalogs) for the type-safe catalog convention and the component hooks, and [URL strategies](/docs/i18n/url-strategies) for cookie-only vs URL-prefix routing.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
<!-- source: en/i18n/catalogs.md -->
|
|
63
|
+
## Catalogs & hooks
|
|
64
|
+
|
|
65
|
+
_Type-safe message catalogs with defineCatalog + defineLocale (parity-enforced), reading translations with useT / <T> / useLocale, ICU placeholders, and the react-intl escape hatch._
|
|
66
|
+
|
|
67
|
+
A **catalog** is a flat `Record<string, string>` of message ID → ICU MessageFormat template. Voltro picks flat-string format (instead of react-intl's `{ defaultMessage, description }` objects) because translation tools (Crowdin / Lokalise / Phrase) import flat string maps natively, the format diffs cleanly in code review, and the base catalog *is* the source of truth — `defaultMessage` becomes redundant.
|
|
68
|
+
|
|
69
|
+
## Catalog files — type-safe convention
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// src/locales/en.ts — base catalog, source of truth
|
|
73
|
+
import { defineCatalog } from '@voltro/i18n'
|
|
74
|
+
|
|
75
|
+
export default defineCatalog({
|
|
76
|
+
'header.cta': 'Get started',
|
|
77
|
+
'home.greeting': 'Hello, {name}',
|
|
78
|
+
'errors.network': 'Connection lost. Retry?',
|
|
79
|
+
} as const)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// src/locales/de.ts — MUST mirror en exactly
|
|
84
|
+
import { defineLocale } from '@voltro/i18n'
|
|
85
|
+
import en from './en'
|
|
86
|
+
|
|
87
|
+
export default defineLocale<typeof en>()({
|
|
88
|
+
'header.cta': 'Loslegen',
|
|
89
|
+
'home.greeting': 'Hallo, {name}',
|
|
90
|
+
'errors.network': 'Verbindung verloren. Erneut versuchen?',
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`defineCatalog` is an identity function that pins the catalog's literal-key type. `defineLocale<typeof en>()` returns a **curried** function whose argument **must mirror the base catalog's keys exactly** — missing or extra keys fail at `tsc --noEmit`, not at runtime. This is what prevents "translation drift", where non-base locales silently fall out of sync as the base catalog grows.
|
|
95
|
+
|
|
96
|
+
This parity check is the whole point of having a base catalog. **Don't skip `defineLocale<typeof en>()`** for non-base catalogs — plain `defineCatalog(...)` works but loses the type-level enforcement.
|
|
97
|
+
|
|
98
|
+
## ICU placeholders
|
|
99
|
+
|
|
100
|
+
Keep ICU MessageFormat placeholders **identical across locales** — `react-intl` validates them at render time. That covers simple interpolation (`{name}`) and the full ICU surface — plurals, select, ordinals:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// en.ts
|
|
104
|
+
export default defineCatalog({
|
|
105
|
+
'cart.items': '{count, plural, one {# item} other {# items}}',
|
|
106
|
+
} as const)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// de.ts
|
|
111
|
+
export default defineLocale<typeof en>()({
|
|
112
|
+
'cart.items': '{count, plural, one {# Artikel} other {# Artikel}}',
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Don't roll your own ICU MessageFormat.** Pluralization, gender, and ordinals across 50+ languages is a solved problem; reimplementing it produces bugs that only surface in specific locales (Turkish dotted-i, Arabic plurals, Finnish cases).
|
|
117
|
+
|
|
118
|
+
## Reading translations in components
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { useT, useTFn, T, useLocale } from '@voltro/i18n'
|
|
122
|
+
|
|
123
|
+
// JSX form — preserves rich-text capabilities (component injection).
|
|
124
|
+
<T id="home.greeting" values={{ name: 'Mario' }} />
|
|
125
|
+
|
|
126
|
+
// Imperative form — for non-JSX contexts (aria-label, placeholder,
|
|
127
|
+
// document.title, toast). Returns a plain string.
|
|
128
|
+
const cta = useT('header.cta')
|
|
129
|
+
const greeting = useT('home.greeting', { name: 'Mario' })
|
|
130
|
+
|
|
131
|
+
// Function form — capture once, replay inside helpers / list maps /
|
|
132
|
+
// conditional branches without N useIntl() calls per render.
|
|
133
|
+
const t = useTFn()
|
|
134
|
+
const labels = items.map((i) => t('cart.items', { count: i.count }))
|
|
135
|
+
|
|
136
|
+
// Active locale code (for switching CSS rules, locale-aware date
|
|
137
|
+
// pickers, etc.).
|
|
138
|
+
const locale = useLocale()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- **`<T id="…" />`** — JSX form. Use this whenever you're rendering a string into the tree; it preserves rich-text (React-element values).
|
|
142
|
+
- **`useT(id, values?)`** — imperative form returning a plain string, for non-JSX call sites: `placeholder`, `aria-label`, `document.title`, toasts, error messages.
|
|
143
|
+
- **`useTFn()`** — returns a `(id, values?) => string` translator the component captures once. Use when one component needs `t` inside helper functions or `.map()` callbacks — `react-intl` runs once, the closure replays.
|
|
144
|
+
- **`useLocale()`** — the active locale code, as React state inside the provider.
|
|
145
|
+
|
|
146
|
+
## The react-intl escape hatch
|
|
147
|
+
|
|
148
|
+
For features the wrap doesn't expose — custom formatters, `Intl` options, rich-text with React-element values — import from `react-intl` directly:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { useIntl, FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The wrap deliberately doesn't re-export everything, to keep the framework's blessed API small. The library is already in your `node_modules`.
|
|
155
|
+
|
|
156
|
+
## Anti-patterns
|
|
157
|
+
|
|
158
|
+
- **Don't bake locale-specific strings into shadcn primitives, hooks, or anything in `packages/*`.** Strings live in the consuming app's `src/locales/`. Framework and kit code stay string-free.
|
|
159
|
+
- **Don't read the cookie directly in components.** Use `useT` / `useLocale` — the active locale is React state, not browser state, inside the provider.
|
|
160
|
+
- **Don't use translated strings as React keys** or as switch discriminators. Use IDs / enums for control flow; translations are presentation only.
|
|
161
|
+
- **Don't translate code samples in markdown.** Comments inside code fences stay in the source language — readers copy code as-is, and translated identifiers don't compile.
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
<!-- source: en/i18n/url-strategies.md -->
|
|
168
|
+
## URL strategies
|
|
169
|
+
|
|
170
|
+
_Cookie-only vs URL-prefix routing (Strategy A / B), the URL-prefix integration sketch, resolveLocale on the /server subpath, the voltro:lang / voltro:theme cookie convention._
|
|
171
|
+
|
|
172
|
+
There are two ways an i18n app can encode the active locale. Pick **one**, based on what the app's URLs are *for*.
|
|
173
|
+
|
|
174
|
+
## Strategy A — cookie-only (default for product apps)
|
|
175
|
+
|
|
176
|
+
- All locales are served from the **same** URL (`/dashboard`, `/settings`).
|
|
177
|
+
- The `voltro:lang` cookie + `Accept-Language` determine which catalog renders.
|
|
178
|
+
- The ProfileMenu's language switch — or the standalone `<LocaleSwitcher>` from `@voltro/ui-shadcn` — writes the cookie and reloads.
|
|
179
|
+
- Use when URLs are **functional** (`/dashboard/projects/42/deployments`) and don't need to encode the language. This is most apps.
|
|
180
|
+
|
|
181
|
+
This is the zero-config default — the framework's auto-wired `<I18nProvider>` is cookie-driven, so Strategy A needs nothing beyond setting `locales` in `app.config.ts`.
|
|
182
|
+
|
|
183
|
+
## Strategy B — URL-prefix (default for docs / marketing)
|
|
184
|
+
|
|
185
|
+
- The default locale lives at the **bare** path (`/docs/foo`).
|
|
186
|
+
- Other locales **prepend** `/<code>` (`/de/docs/foo`).
|
|
187
|
+
- Each translated page has its own crawlable URL → better SEO, shareable language-specific links.
|
|
188
|
+
- Use when URLs **are** the product — search engines index them, links get shared with specific language intent.
|
|
189
|
+
- Requires extra wiring in the layout (below).
|
|
190
|
+
|
|
191
|
+
The two strategies can coexist across sibling apps: voltro-cloud's dashboard uses cookie-only; its docs app (the one you're reading) uses URL-prefix.
|
|
192
|
+
|
|
193
|
+
## Render mode constrains the choice
|
|
194
|
+
|
|
195
|
+
Strategy A isn't always *available* — it depends on how the page renders. Cookie i18n only shows the chosen language two ways: (1) the page **hydrates** on the client (`interactive: 'full'`, the default) and re-renders from the cookie, or (2) the page is **`ssr` / `isr`** and the server resolves the cookie per request.
|
|
196
|
+
|
|
197
|
+
A page that is `renderMode: 'static'` **and** `interactive: 'none'` / `'islands'` runs neither — its HTML is pre-rendered once in the default locale and never re-renders. Cookie i18n there is a **dead switcher**: it writes the cookie, reloads, and shows the same default-language HTML. Zero-JS static sites and islands pages therefore **must** use Strategy B, whose `[locale]` mirrors make `voltro build` pre-render one HTML file per locale — genuinely bilingual with no client JS.
|
|
198
|
+
|
|
199
|
+
| Page | i18n strategy |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| hydrated (`interactive: 'full'`) or `ssr` / `isr` | Strategy A (cookie) works |
|
|
202
|
+
| `static` + `interactive: 'none'` / `'islands'` | Strategy B (URL-prefix) required |
|
|
203
|
+
|
|
204
|
+
The init-templates follow exactly this split: the hydrated/SSR shells (`app`, `blank`, `spa`, `ssr`, `ssr-api`, `admin`, `dashboard`) ship cookie i18n; the static/islands ones (`landing`, `docs`, `static-blog`, `changelog`, `contact`) ship URL-prefix i18n.
|
|
205
|
+
|
|
206
|
+
## URL-prefix integration (Strategy B)
|
|
207
|
+
|
|
208
|
+
The framework's auto-wired `<I18nProvider>` is cookie-driven. For URL-prefix routing you need an **inner** provider that's URL-driven, so client-side navigation between `/docs/x` and `/de/docs/x` swaps the catalog without a full reload.
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
// src/lib/locale.ts
|
|
212
|
+
import { useLocation } from '@voltro/web'
|
|
213
|
+
|
|
214
|
+
export const SUPPORTED = ['en', 'de'] as const
|
|
215
|
+
export const DEFAULT = 'en'
|
|
216
|
+
type Locale = (typeof SUPPORTED)[number]
|
|
217
|
+
|
|
218
|
+
const isSupported = (v: string): v is Locale =>
|
|
219
|
+
(SUPPORTED as ReadonlyArray<string>).includes(v)
|
|
220
|
+
|
|
221
|
+
export const localeFromPathname = (p: string): Locale => {
|
|
222
|
+
const m = /^\/([a-z]{2})(?:\/|$)/.exec(p)
|
|
223
|
+
return m && isSupported(m[1]!) ? m[1] as Locale : DEFAULT
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export const useUrlLocale = (): Locale => localeFromPathname(useLocation())
|
|
227
|
+
|
|
228
|
+
export const withLocalePrefix = (path: string, locale: string): string => {
|
|
229
|
+
if (locale === DEFAULT) return path
|
|
230
|
+
const p = path.startsWith('/') ? path : `/${path}`
|
|
231
|
+
return p === '/' ? `/${locale}` : `/${locale}${p}`
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const stripLocalePrefix = (p: string): string => {
|
|
235
|
+
const m = /^\/([a-z]{2})(\/.*|$)/.exec(p)
|
|
236
|
+
return m && isSupported(m[1]!) ? (m[2] || '/') : p
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
// src/pages/layout.tsx
|
|
242
|
+
import { I18nProvider } from '@voltro/i18n'
|
|
243
|
+
import en from '../locales/en'
|
|
244
|
+
import de from '../locales/de'
|
|
245
|
+
import { useUrlLocale, DEFAULT } from '../lib/locale'
|
|
246
|
+
|
|
247
|
+
const CATALOGS = { en, de } as const
|
|
248
|
+
|
|
249
|
+
const Body = ({ children }) => {
|
|
250
|
+
// EVERY useT() / <T> in here reads from the inner provider
|
|
251
|
+
// (URL-driven). useLocation in useUrlLocale triggers re-render
|
|
252
|
+
// on client-side nav → inner provider re-renders with the new
|
|
253
|
+
// catalog → all useT lookups update.
|
|
254
|
+
return <>{children}</>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export default function Layout({ children }) {
|
|
258
|
+
const locale = useUrlLocale()
|
|
259
|
+
return (
|
|
260
|
+
<I18nProvider locale={locale} messages={CATALOGS[locale]} defaultLocale={DEFAULT}>
|
|
261
|
+
<Body>{children}</Body>
|
|
262
|
+
</I18nProvider>
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Pair this with locale-prefixed page files: `src/pages/[locale]/index.tsx`, `src/pages/[locale]/docs/[...slug].tsx`, etc. Each re-exports the default-locale query's component, which reads `useUrlLocale()` to decide which catalog data to query.
|
|
268
|
+
|
|
269
|
+
Reference implementations: `voltro-dev/apps/voltro-dev/docs/` (URL-prefix on dynamic + static content) and `voltro-dev/apps/voltro-dev/landing/` (URL-prefix on a pure static marketing site — 10 default-locale pages, 10 locale-prefixed mirrors, one combined `voltro build` run).
|
|
270
|
+
|
|
271
|
+
## SSG emission per locale — what `voltro build` does for you
|
|
272
|
+
|
|
273
|
+
When `locales` is set in `app.config.ts` AND the page tree includes `[locale]/…` mirror files, `voltro build` automatically emits per-locale static HTML at the URL-prefixed paths:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
dist/index.html ← default locale (en)
|
|
277
|
+
dist/de/index.html ← de mirror
|
|
278
|
+
dist/features/foo/index.html ← default locale
|
|
279
|
+
dist/de/features/foo/index.html ← de mirror
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Each variant ships with the right `<I18nProvider>`-wrapped body **and** the right per-locale `<title>` / `<meta description>` / `<link rel="canonical">` / OG tags — **if** the page's `meta` is exported as a function of `({ locale })`. With a plain static `meta: PageMeta` object, the body is correctly localised but the head tags stay default-locale on every variant.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
// src/pages/features/foo.tsx — meta as a function of locale
|
|
286
|
+
import { getCatalog } from '../lib/locale'
|
|
287
|
+
import { localeCanonicalUrl, ogTags, standardLinks } from '../lib/seo'
|
|
288
|
+
|
|
289
|
+
export const meta = ({ locale }: { readonly locale: string }) => {
|
|
290
|
+
const c = getCatalog(locale)
|
|
291
|
+
const title = c['seo.features.foo.title'] as string
|
|
292
|
+
const description = c['seo.features.foo.description'] as string
|
|
293
|
+
return {
|
|
294
|
+
title,
|
|
295
|
+
description,
|
|
296
|
+
canonical: localeCanonicalUrl('/features/foo', locale),
|
|
297
|
+
tags: ogTags({ title, description }),
|
|
298
|
+
links: standardLinks('/features/foo'),
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The `meta(ctx)` callback runs once per (page × locale) at build time. The `ctx.locale` value comes from `params.locale` for the `[locale]/…` mirror routes; for the bare-path variant it's the app's `defaultLocale`.
|
|
304
|
+
|
|
305
|
+
### Mechanism
|
|
306
|
+
|
|
307
|
+
At build time the SSG pipeline pre-loads every catalog and wraps each rendered page in a per-locale `<I18nProvider>`. The `useT()` calls inside pages, layouts, and shared components resolve against the active locale's catalog — no IntlProvider context error, no client-side flash of untranslated content. This is invariant: it fires whether you set `locales` for client-routing reasons (Strategy A: cookies) or full SSG (Strategy B: URL-prefix).
|
|
308
|
+
|
|
309
|
+
The `[locale]/…` mirror is the trigger for SSG **output** — without mirror files, only the default-locale URLs are emitted (the framework won't guess that `/de/foo` should also exist). With mirror files, their `getStaticPaths` enumerate which locale-prefixed paths to render, and the build produces one HTML file per (page × locale) combination.
|
|
310
|
+
|
|
311
|
+
### Mirror file boilerplate
|
|
312
|
+
|
|
313
|
+
12 lines per page. Re-export the canonical page's `default`, `renderMode`, `interactive`, `meta`, plus a `getStaticPaths` that enumerates non-default locales:
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
// src/pages/[locale]/features/foo.tsx
|
|
317
|
+
import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../../lib/locale'
|
|
318
|
+
export { default } from '../../features/foo'
|
|
319
|
+
export { renderMode, interactive, meta } from '../../features/foo'
|
|
320
|
+
|
|
321
|
+
export const getStaticPaths = async (): Promise<
|
|
322
|
+
Array<{ params: { locale: string } }>
|
|
323
|
+
> =>
|
|
324
|
+
SUPPORTED_LOCALES
|
|
325
|
+
.filter((l) => l !== DEFAULT_LOCALE)
|
|
326
|
+
.map((locale) => ({ params: { locale } }))
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Cloud-docs and voltro-dev both use this exact pattern — see them for the lib/locale.ts helpers (`SUPPORTED_LOCALES`, `DEFAULT_LOCALE`, `getCatalog`, `useUrlLocale`, `withLocalePrefix`, `stripLocalePrefix`).
|
|
330
|
+
|
|
331
|
+
## Server-side resolution — the `/server` subpath
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { resolveLocale } from '@voltro/i18n/server'
|
|
335
|
+
|
|
336
|
+
// In any SSR-side layout, lib, or middleware:
|
|
337
|
+
const locale = resolveLocale({
|
|
338
|
+
cookieHeader: ssr.headers.cookie,
|
|
339
|
+
acceptLanguageHeader: ssr.headers['accept-language'],
|
|
340
|
+
supported: ['en', 'de'],
|
|
341
|
+
defaultLocale: 'en',
|
|
342
|
+
})
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`resolveLocale` is what the framework's auto-wired entry uses under the hood — exposed here for consumers who need locale info **before** the React tree mounts: setting a `lang` attribute on `<html>`, emitting `hreflang` link tags, picking a locale-specific OG image. It applies the same priority order — cookie → `Accept-Language` → default — and the returned locale is guaranteed to be in `supported`.
|
|
346
|
+
|
|
347
|
+
The `/server` subpath is convention. Both that path and the main entry are pure JS (no Node-only APIs); the split exists so future server-only helpers stay out of browser bundles.
|
|
348
|
+
|
|
349
|
+
## Cookie convention
|
|
350
|
+
|
|
351
|
+
The framework reads + writes **`voltro:lang`** for the active language choice (lowercase IETF tag: `en`, `de`, `fr-CA`). `@voltro/ui-shadcn`'s ProfileMenu and the framework's auto-wired resolver agree on this name — **don't pick a different one in app code.**
|
|
352
|
+
|
|
353
|
+
For theme the parallel cookie is **`voltro:theme`** (values `'system' | 'light' | 'dark'`). Both are managed by the kit's ProfileMenu out of the box; the helpers live in `@voltro/ui-shadcn` (`THEME_COOKIE`, `LANG_COOKIE`, `getCookie`, `setCookie`, `deleteCookie`, `parsePreferenceCookies`, `applyTheme`).
|
|
354
|
+
|
|
355
|
+
## Anti-patterns
|
|
356
|
+
|
|
357
|
+
- **Don't mix strategies within one app.** Pick cookie-only *or* URL-prefix per app; mixing them produces ambiguous canonical URLs and broken language switching.
|
|
358
|
+
- **Don't read `Accept-Language` on the client.** It's server-only — `navigator.languages` can diverge from what the server saw and cause a hydration mismatch.
|
|
359
|
+
- **Don't invent your own cookie name.** The kit and the resolver only agree on `voltro:lang` / `voltro:theme`.
|