diff --git a/app/i18n.ts b/app/i18n.ts index 44fa179..87871b7 100644 --- a/app/i18n.ts +++ b/app/i18n.ts @@ -8,6 +8,8 @@ import heroEN from "../public/locales/en/hero.json"; import heroES from "../public/locales/es/hero.json"; import featuresEN from "../public/locales/en/features.json"; import featuresES from "../public/locales/es/features.json"; +import showcaseEN from "../public/locales/en/showcase.json"; +import showcaseES from "../public/locales/es/showcase.json"; import downloadEN from "../public/locales/en/download.json"; import downloadES from "../public/locales/es/download.json"; import communityEN from "../public/locales/en/community.json"; @@ -28,6 +30,7 @@ i18n hero: heroEN, supportedBy: supportedByEN, features: featuresEN, + showcase: showcaseEN, download: downloadEN, community: communityEN, contact: contactEN, @@ -38,6 +41,7 @@ i18n hero: heroES, supportedBy: supportedByES, features: featuresES, + showcase: showcaseES, download: downloadES, community: communityES, contact: contactES, diff --git a/components/download-section.tsx b/components/download-section.tsx index 020f36c..911756b 100644 --- a/components/download-section.tsx +++ b/components/download-section.tsx @@ -39,8 +39,7 @@ function CopyButton({ text }: { text: string }) { } export function DownloadSection() { - const { i18n } = useTranslation() - const lang = i18n.language?.startsWith("es") ? "es" : "en" + const { t } = useTranslation("download") const [exes, setExes] = useState(FALLBACK) const [releaseTag, setReleaseTag] = useState("v0.9.3-alpha") @@ -76,7 +75,7 @@ export function DownloadSection() {
- [ 04 ] {lang === "es" ? "Descarga" : "Download"} + [ 04 ] {t("sectionLabel")}
@@ -84,13 +83,13 @@ export function DownloadSection() { className="font-medium leading-[.96] text-foreground mb-4" style={{ fontSize: "clamp(48px,7vw,96px)", letterSpacing: "-.04em" }} > - {lang === "es" ? <>Descargá dashAI. : <>Download dashAI.} + {t("heading")} dashAI.
{releaseTag} ·MIT License ·Linux · macOS · Windows - ·{lang === "es" ? "Mayo 2026" : "May 2026"} + ·{t("releaseMonth")}
@@ -110,13 +109,11 @@ export function DownloadSection() {
01 - {lang === "es" ? "Ejecutables (recomendado)" : "Executables (recommended)"} + {t("executables.badge")}
-

{lang === "es" ? "Instalador nativo" : "Native installer"}

+

{t("executables.title")}

- {lang === "es" - ? "Sin Python ni configuración. Descargá el ejecutable de tu sistema operativo desde GitHub Releases." - : "No Python, no setup. Download the executable for your OS from GitHub Releases."} + {t("executables.description")}

{exes.map((e, i) => ( @@ -155,7 +152,7 @@ export function DownloadSection() { rel="noopener" className="mt-auto font-mono text-xs text-[#A7C7FF] flex items-center gap-1.5 hover:gap-2.5 transition-all duration-200" > - {lang === "es" ? "Ver releases" : "View releases"} → + {t("executables.viewReleases")} →
@@ -165,18 +162,16 @@ export function DownloadSection() { 02 PyPI
-

{lang === "es" ? "Instalación con pip" : "Install with pip"}

+

{t("pip.title")}

- {lang === "es" - ? <>dashai levanta el servidor y abre el navegador en localhost:8000. - : <>dashai starts the server and opens the browser at localhost:8000.} + dashai {t("pip.descriptionMiddle")} localhost:8000.

$ pip install dashai
$ dashai
- # {lang === "es" ? "http://localhost:8000/ — El navegador abre solo." : "http://localhost:8000/ — Browser opens on its own."} + # {t("pip.comment")}
- {lang === "es" ? "Ver en PyPI" : "View on PyPI"} → + {t("pip.viewOnPyPI")} → @@ -193,14 +188,14 @@ export function DownloadSection() {
- {lang === "es" ? "Requisitos" : "Requirements"} + {t("requirements.badge")}
-

{lang === "es" ? "Lo que necesitás" : "What you need"}

+

{t("requirements.title")}

{[ - { label: lang === "es" ? "Workspace" : "Workspace", items: [ - { name: lang === "es" ? "Datasets" : "Datasets", badge: "12" }, - { name: lang === "es" ? "Modelos" : "Models", badge: "28", active: true }, - { name: lang === "es" ? "Generativo" : "Generative", badge: "5" }, - { name: lang === "es" ? "Plugins" : "Plugins", badge: "+" }, + { label: t("sidebar.workspaceLabel"), items: [ + { name: t("sidebar.datasets"), badge: "12" }, + { name: t("sidebar.models"), badge: "28", active: true }, + { name: t("sidebar.generative"), badge: "5" }, + { name: t("sidebar.plugins"), badge: "+" }, ]}, - { label: lang === "es" ? "Catálogo" : "Catalog", items: [ - { name: "Tabular", badge: "30" }, - { name: "NLP", badge: "15" }, - { name: lang === "es" ? "Traducción" : "Translation", badge: "9" }, - { name: lang === "es" ? "Visión" : "Vision", badge: "11" }, + { label: t("sidebar.catalogLabel"), items: [ + { name: t("sidebar.tabular"), badge: "30" }, + { name: t("sidebar.nlp"), badge: "15" }, + { name: t("sidebar.translation"), badge: "9" }, + { name: t("sidebar.vision"), badge: "11" }, ]}, ].map(sec => (
@@ -115,21 +113,21 @@ export function ShowcaseSection() { {/* Main */}
- {lang === "es" ? "Modelos" : "Models"} / Tabular / {lang === "es" ? "Configurar" : "Configure"} + {t("sidebar.models")} / Tabular / {t("main.configure")}

RandomForestClassifier

- {lang === "es" ? "Hiperparámetros generados desde" : "Hyperparameters generated from"}{" "} + {t("main.hyperparamsFrom")}{" "} MyForestSchema

{/* Form grid */}
{[ - { label: "n_estimators", type: "int · 10–1000", name: "", input:
3501000
, help: lang === "es" ? "Número de árboles en el bosque." : "Number of trees in the forest." }, - { label: "max_depth", type: "int | None", name: "", input:
None
, help: lang === "es" ? "Profundidad máxima del árbol." : "Maximum depth of the tree." }, - { label: "criterion", type: "enum", name: "", input:
gini
, help: lang === "es" ? "Función para medir la calidad del split." : "Function to measure split quality." }, - { label: "bootstrap", type: "bool", name: "", input:
TrueON
, help: lang === "es" ? "Usar muestras bootstrap al construir cada árbol." : "Use bootstrap samples when building each tree." }, + { label: "n_estimators", type: "int · 10–1000", input:
3501000
, help: t("main.form.nEstimatorsHelp") }, + { label: "max_depth", type: "int | None", input:
None
, help: t("main.form.maxDepthHelp") }, + { label: "criterion", type: "enum", input:
gini
, help: t("main.form.criterionHelp") }, + { label: "bootstrap", type: "bool", input:
TrueON
, help: t("main.form.bootstrapHelp") }, ].map(f => (
@@ -146,12 +144,12 @@ export function ShowcaseSection() {
- {lang === "es" ? "Formulario auto-generado del schema" : "Form auto-generated from schema"} + {t("main.formAutoGenerated")} · - {lang === "es" ? "4 campos" : "4 fields"} + {t("main.fields")}
@@ -160,26 +158,7 @@ export function ShowcaseSection() { {/* Callouts */}
- {[ - { - title: lang === "es" ? "1. Schema → UI" : "1. Schema → UI", - body: lang === "es" - ? "Cada campo del formulario nace de una línea de Pydantic. Tipo, rango, default y descripción se renderizan automáticamente." - : "Each form field is born from a Pydantic line. Type, range, default and description render automatically.", - }, - { - title: lang === "es" ? "2. Sin código de frontend" : "2. No frontend code", - body: lang === "es" - ? "El componente nuevo no toca un solo archivo React. La interfaz aparece sola cuando el backend lo registra." - : "The new component touches zero React files. The UI shows up on its own once the backend registers it.", - }, - { - title: lang === "es" ? "3. Catálogo navegable" : "3. Browsable catalog", - body: lang === "es" - ? "Tabular, NLP, traducción, LLMs y visión generativa coexisten en una sola UI consistente — todos sobre las mismas doce abstracciones." - : "Tabular, NLP, translation, LLMs and generative vision coexist in one consistent UI — all on the same twelve abstractions.", - }, - ].map(c => ( + {callouts.map(c => (
- {lang === "es" ? "Catálogo / Modelos" : "Catalog / Models"} + {t("strip.card1.title")}
[ TABULAR · 30 ]
-
{lang === "es" ? "Seleccionar un modelo" : "Pick a model"}
+
{t("strip.card1.heading")}
- {[["RandomForest", lang === "es" ? "Bosque aleatorio" : "Random forest", true],["XGBoost","Gradient boosting",false],["LightGBM","Microsoft GBM",false],["SVM","Support Vector",false],["MLPClassifier",lang === "es" ? "Red neuronal" : "Neural net",false],["KNN","k-Nearest",false]].map(([name, desc, sel]) => ( + {[ + ["RandomForest", t("strip.card1.randomForest"), true], + ["XGBoost", "Gradient boosting", false], + ["LightGBM", "Microsoft GBM", false], + ["SVM", "Support Vector", false], + ["MLPClassifier", t("strip.card1.neuralNet"), false], + ["KNN", "k-Nearest", false], + ].map(([name, desc, sel]) => (
{name}
{desc}
@@ -219,11 +205,11 @@ export function ShowcaseSection() {
- {lang === "es" ? "Resultados / Job #218" : "Results / Job #218"} + {t("strip.card2.title")}
-
[ {lang === "es" ? "COMPLETADO" : "COMPLETED"} · 2m 14s ]
-
{lang === "es" ? "Métricas de validación" : "Validation metrics"}
+
[ {t("strip.card2.status")} · 2m 14s ]
+
{t("strip.card2.heading")}
{[["accuracy","0.9342","#90F1C4"],["f1_macro","0.9118","#90F1C4"],["precision","0.8987","#90F1C4"],["recall","0.8512","#FFA578"]].map(([k,v,c]) => (
{k} @@ -243,16 +229,16 @@ export function ShowcaseSection() {
- Jobs · {lang === "es" ? "Cola" : "Queue"} + Jobs · {t("strip.card3.queueLabel")}
-
[ 8 BaseJob · 2 {lang === "es" ? "activos" : "running"} ]
-
{lang === "es" ? "Orquestación" : "Orchestration"}
+
[ 8 BaseJob · 2 {t("strip.card3.runningCount")} ]
+
{t("strip.card3.heading")}
{[ - { pill: lang === "es" ? "Activo" : "Running", pillStyle: { background: "color-mix(in oklab, #90F1C4 25%, var(--ink-deep))", color: "#90F1C4", border: "1px solid color-mix(in oklab, #90F1C4 40%, transparent)" }, name: "train_rf_v2", meta: "68%" }, - { pill: lang === "es" ? "Activo" : "Running", pillStyle: { background: "color-mix(in oklab, #90F1C4 25%, var(--ink-deep))", color: "#90F1C4", border: "1px solid color-mix(in oklab, #90F1C4 40%, transparent)" }, name: "explain_shap", meta: "12s" }, - { pill: lang === "es" ? "Listo" : "Done", pillStyle: { background: "color-mix(in oklab, var(--primary) 22%, var(--ink-deep))", color: "#A7C7FF", border: "1px solid color-mix(in oklab, var(--primary) 40%, transparent)" }, name: "predict_test", meta: "1m 4s" }, - { pill: lang === "es" ? "Cola" : "Queued", pillStyle: { background: "var(--background)", color: "var(--muted-foreground)", border: "1px solid var(--border)" }, name: "optimize_hpo", meta: "—" }, + { pill: t("strip.card3.pills.running"), pillStyle: { background: "color-mix(in oklab, #90F1C4 25%, var(--ink-deep))", color: "#90F1C4", border: "1px solid color-mix(in oklab, #90F1C4 40%, transparent)" }, name: "train_rf_v2", meta: "68%" }, + { pill: t("strip.card3.pills.running"), pillStyle: { background: "color-mix(in oklab, #90F1C4 25%, var(--ink-deep))", color: "#90F1C4", border: "1px solid color-mix(in oklab, #90F1C4 40%, transparent)" }, name: "explain_shap", meta: "12s" }, + { pill: t("strip.card3.pills.done"), pillStyle: { background: "color-mix(in oklab, var(--primary) 22%, var(--ink-deep))", color: "#A7C7FF", border: "1px solid color-mix(in oklab, var(--primary) 40%, transparent)" }, name: "predict_test", meta: "1m 4s" }, + { pill: t("strip.card3.pills.queued"), pillStyle: { background: "var(--background)", color: "var(--muted-foreground)", border: "1px solid var(--border)" }, name: "optimize_hpo", meta: "—" }, ].map(j => (
{j.pill} diff --git a/public/locales/en/download.json b/public/locales/en/download.json index a634b17..02ad6d8 100644 --- a/public/locales/en/download.json +++ b/public/locales/en/download.json @@ -1,25 +1,29 @@ { - "title": "Download DashAI", - "badge": "Beta Version", - "description": "This early version lets you explore DashAI’s main features. We appreciate your feedback to help us improve before the official release.", - "button": "Download", - "cards": { - "macIntel": { - "platform": "macOS Intel processors", - "format": "binary" - }, - "macArm": { - "platform": "macOS ARM processors", - "format": "binary" - }, - "windows": { - "platform": "Windows", - "format": ".exe" - } + "sectionLabel": "Download", + "heading": "Download", + "releaseMonth": "May 2026", + "executables": { + "badge": "Executables (recommended)", + "title": "Native installer", + "description": "No Python, no setup. Download the executable for your OS from GitHub Releases.", + "viewReleases": "View releases" }, "pip": { - "title": "Install via pip", - "subtitle": "For developers who prefer command-line installation", - "command": "pip install dashai" + "title": "Install with pip", + "descriptionMiddle": "starts the server and opens the browser at", + "comment": "http://localhost:8000/ — Browser opens on its own.", + "viewOnPyPI": "View on PyPI" + }, + "requirements": { + "badge": "Requirements", + "title": "What you need", + "items": { + "python": { "note": "Only for pip install" }, + "ram": { "note": "16 GB for local LLMs" }, + "os": { "note": "Native executables available" } + } + }, + "footer": { + "cloneText": "Want to modify the core? Clone the repo:" } } diff --git a/public/locales/en/showcase.json b/public/locales/en/showcase.json new file mode 100644 index 0000000..4565c09 --- /dev/null +++ b/public/locales/en/showcase.json @@ -0,0 +1,67 @@ +{ + "sectionLabel": "The application", + "heading": "The interface that emerges from the schema.", + "description": "When you register a component, the UI isn't programmed: it's generated. The server exposes its Pydantic schema and the React frontend renders the form without prior knowledge.", + "sidebar": { + "workspaceLabel": "Workspace", + "datasets": "Datasets", + "models": "Models", + "generative": "Generative", + "plugins": "Plugins", + "catalogLabel": "Catalog", + "tabular": "Tabular", + "nlp": "NLP", + "translation": "Translation", + "vision": "Vision" + }, + "main": { + "configure": "Configure", + "hyperparamsFrom": "Hyperparameters generated from", + "form": { + "nEstimatorsHelp": "Number of trees in the forest.", + "maxDepthHelp": "Maximum depth of the tree.", + "criterionHelp": "Function to measure split quality.", + "bootstrapHelp": "Use bootstrap samples when building each tree." + }, + "formAutoGenerated": "Form auto-generated from schema", + "fields": "4 fields", + "train": "Train →" + }, + "callouts": [ + { + "title": "1. Schema → UI", + "body": "Each form field is born from a Pydantic line. Type, range, default and description render automatically." + }, + { + "title": "2. No frontend code", + "body": "The new component touches zero React files. The UI shows up on its own once the backend registers it." + }, + { + "title": "3. Browsable catalog", + "body": "Tabular, NLP, translation, LLMs and generative vision coexist in one consistent UI — all on the same twelve abstractions." + } + ], + "strip": { + "card1": { + "title": "Catalog / Models", + "heading": "Pick a model", + "randomForest": "Random forest", + "neuralNet": "Neural net" + }, + "card2": { + "title": "Results / Job #218", + "status": "COMPLETED", + "heading": "Validation metrics" + }, + "card3": { + "queueLabel": "Queue", + "runningCount": "running", + "heading": "Orchestration", + "pills": { + "running": "Running", + "done": "Done", + "queued": "Queued" + } + } + } +} diff --git a/public/locales/es/download.json b/public/locales/es/download.json index 9665881..5e8528b 100644 --- a/public/locales/es/download.json +++ b/public/locales/es/download.json @@ -1,25 +1,29 @@ { - "title": "Descarga DashAI", - "badge": "Versión Beta", - "description": "Esta versión temprana te permite explorar las principales funciones de DashAI. Agradecemos tus comentarios para ayudarnos a mejorar antes del lanzamiento oficial.", - "button": "Descargar", - "cards": { - "macIntel": { - "platform": "macOS con Intel", - "format": "binario" - }, - "macArm": { - "platform": "macOS con ARM", - "format": "binario" - }, - "windows": { - "platform": "Windows", - "format": ".exe" - } + "sectionLabel": "Descarga", + "heading": "Descarga", + "releaseMonth": "Mayo 2026", + "executables": { + "badge": "Ejecutables (recomendado)", + "title": "Instalador nativo", + "description": "Sin Python ni configuración. Descarga el ejecutable de tu sistema operativo desde GitHub Releases.", + "viewReleases": "Ver releases" }, "pip": { - "title": "Instala con pip", - "subtitle": "Para desarrolladores que prefieren la instalación por línea de comandos", - "command": "pip install dashai" + "title": "Instalación con pip", + "descriptionMiddle": "levanta el servidor y abre el navegador en", + "comment": "http://localhost:8000/ — El navegador abre solo.", + "viewOnPyPI": "Ver en PyPI" + }, + "requirements": { + "badge": "Requisitos", + "title": "Lo que necesitas", + "items": { + "python": { "note": "Solo para instalación pip" }, + "ram": { "note": "16 GB para LLMs locales" }, + "os": { "note": "Ejecutables nativos disponibles" } + } + }, + "footer": { + "cloneText": "¿Quieres modificar el núcleo? Clona el repo:" } } diff --git a/public/locales/es/showcase.json b/public/locales/es/showcase.json new file mode 100644 index 0000000..dfb243d --- /dev/null +++ b/public/locales/es/showcase.json @@ -0,0 +1,67 @@ +{ + "sectionLabel": "La aplicación", + "heading": "La interfaz que emerge del schema.", + "description": "Cuando registras un componente, la UI no se programa: se genera. El servidor expone su schema Pydantic y el frontend renderiza el formulario sin conocerlo previamente.", + "sidebar": { + "workspaceLabel": "Workspace", + "datasets": "Datasets", + "models": "Modelos", + "generative": "Generativo", + "plugins": "Plugins", + "catalogLabel": "Catálogo", + "tabular": "Tabular", + "nlp": "NLP", + "translation": "Traducción", + "vision": "Visión" + }, + "main": { + "configure": "Configurar", + "hyperparamsFrom": "Hiperparámetros generados desde", + "form": { + "nEstimatorsHelp": "Número de árboles en el bosque.", + "maxDepthHelp": "Profundidad máxima del árbol.", + "criterionHelp": "Función para medir la calidad del split.", + "bootstrapHelp": "Usar muestras bootstrap al construir cada árbol." + }, + "formAutoGenerated": "Formulario auto-generado del schema", + "fields": "4 campos", + "train": "Entrenar →" + }, + "callouts": [ + { + "title": "1. Schema → UI", + "body": "Cada campo del formulario nace de una línea de Pydantic. Tipo, rango, default y descripción se renderizan automáticamente." + }, + { + "title": "2. Sin código de frontend", + "body": "El componente nuevo no toca un solo archivo React. La interfaz aparece sola cuando el backend lo registra." + }, + { + "title": "3. Catálogo navegable", + "body": "Tabular, NLP, traducción, LLMs y visión generativa coexisten en una sola UI consistente — todos sobre las mismas doce abstracciones." + } + ], + "strip": { + "card1": { + "title": "Catálogo / Modelos", + "heading": "Seleccionar un modelo", + "randomForest": "Bosque aleatorio", + "neuralNet": "Red neuronal" + }, + "card2": { + "title": "Resultados / Job #218", + "status": "COMPLETADO", + "heading": "Métricas de validación" + }, + "card3": { + "queueLabel": "Cola", + "runningCount": "activos", + "heading": "Orquestación", + "pills": { + "running": "Activo", + "done": "Listo", + "queued": "Cola" + } + } + } +}