From a6014524eff03197c9c35e0049bdf0a246ee4558 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 25 Feb 2026 13:49:37 -0300 Subject: [PATCH] feat(i18n): migrate onboarding wizard - Onboarding: 37 strings (all wizard steps, labels, messages, buttons) - Expanded en.json and pt-BR.json onboarding namespace with 30+ keys - Dynamic STEPS array from translations for wizard step titles --- .../(dashboard)/dashboard/onboarding/page.tsx | 100 ++++++++---------- src/i18n/messages/en.json | 35 +++++- src/i18n/messages/pt-BR.json | 35 +++++- 3 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/app/(dashboard)/dashboard/onboarding/page.tsx b/src/app/(dashboard)/dashboard/onboarding/page.tsx index a782f63964..4c5de1be08 100644 --- a/src/app/(dashboard)/dashboard/onboarding/page.tsx +++ b/src/app/(dashboard)/dashboard/onboarding/page.tsx @@ -2,14 +2,10 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; -const STEPS = [ - { id: "welcome", title: "Welcome", icon: "waving_hand" }, - { id: "security", title: "Security", icon: "lock" }, - { id: "provider", title: "Provider", icon: "dns" }, - { id: "test", title: "Test", icon: "play_circle" }, - { id: "done", title: "Ready!", icon: "check_circle" }, -]; +const STEP_IDS = ["welcome", "security", "provider", "test", "done"]; +const STEP_ICONS = ["waving_hand", "lock", "dns", "play_circle", "check_circle"]; const COMMON_PROVIDERS = [ { id: "openai", name: "OpenAI", color: "#10A37F" }, @@ -22,6 +18,8 @@ const COMMON_PROVIDERS = [ export default function OnboardingWizard() { const router = useRouter(); + const t = useTranslations("onboarding"); + const tc = useTranslations("common"); const [step, setStep] = useState(0); const [loading, setLoading] = useState(true); @@ -60,6 +58,12 @@ export default function OnboardingWizard() { checkSetup(); }, [router]); + const STEPS = STEP_IDS.map((id, i) => ({ + id, + title: t(id === "done" ? "ready" : id), + icon: STEP_ICONS[i], + })); + const currentStep = STEPS[step]; const isLastStep = step === STEPS.length - 1; @@ -88,12 +92,12 @@ export default function OnboardingWizard() { }); if (!res.ok) { const data = await res.json().catch(() => ({})); - setErrorMessage(data.error || "Failed to set password. Try again."); + setErrorMessage(data.error || t("failedSetPassword")); return; } handleNext(); } catch { - setErrorMessage("Connection error. Please try again."); + setErrorMessage(t("connectionError")); } }; @@ -123,18 +127,18 @@ export default function OnboardingWizard() { }); if (!res.ok) { const data = await res.json().catch(() => ({})); - setErrorMessage(data.error || "Failed to add provider. Try again."); + setErrorMessage(data.error || t("failedAddProvider")); return; } handleNext(); } catch { - setErrorMessage("Connection error. Please try again."); + setErrorMessage(t("connectionError")); } }; const handleTestProvider = async () => { setTestStatus("testing"); - setTestMessage("Testing connection..."); + setTestMessage(t("testingConnection")); try { const res = await fetch("/api/providers"); if (!res.ok) throw new Error("Failed to fetch"); @@ -142,21 +146,21 @@ export default function OnboardingWizard() { const conn = data.connections?.[0]; if (!conn) { setTestStatus("error"); - setTestMessage("No provider found. You can add one from the dashboard later."); + setTestMessage(t("noProviderFound")); return; } const testRes = await fetch(`/api/providers/${conn.id}/test`, { method: "POST" }); if (testRes.ok) { setTestStatus("success"); - setTestMessage("Connection successful! Your provider is ready."); + setTestMessage(t("connectionSuccessful")); } else { const err = await testRes.json().catch(() => ({})); setTestStatus("error"); - setTestMessage(err.error || "Test failed, but you can configure this later."); + setTestMessage(err.error || t("testFailed")); } } catch { setTestStatus("error"); - setTestMessage("Could not test right now. You can test from the dashboard."); + setTestMessage(t("couldNotTest")); } }; @@ -176,7 +180,7 @@ export default function OnboardingWizard() { if (loading) { return (
-
Loading...
+
{tc("loading")}
); } @@ -234,15 +238,14 @@ export default function OnboardingWizard() { {currentStep.id === "welcome" && (

- OmniRoute is your local AI API proxy. - It routes requests to multiple AI providers with load balancing, failover, and - usage tracking. + OmniRoute{" "} + {t("welcomeDesc").replace("OmniRoute is your local AI API proxy. ", "")}

{[ - { icon: "swap_horiz", label: "Multi-Provider" }, - { icon: "monitoring", label: "Usage Tracking" }, - { icon: "shield", label: "API Key Mgmt" }, + { icon: "swap_horiz", label: t("multiProvider") }, + { icon: "monitoring", label: t("usageTracking") }, + { icon: "shield", label: t("apiKeyMgmt") }, ].map((f) => (
-

- Set a password to protect your dashboard, or skip for now. -

+

{t("securityDesc")}

{!skipSecurity && (
setPassword(e.target.value)} className="w-full px-4 py-2.5 bg-white/[0.04] border border-white/10 rounded-lg text-text-main text-sm placeholder:text-text-muted/50 focus:outline-none focus:ring-2 focus:ring-primary/40" /> setConfirmPassword(e.target.value)} className="w-full px-4 py-2.5 bg-white/[0.04] border border-white/10 rounded-lg text-text-main text-sm placeholder:text-text-muted/50 focus:outline-none focus:ring-2 focus:ring-primary/40" /> {password && confirmPassword && password !== confirmPassword && ( -

Passwords do not match

+

{t("passwordsMismatch")}

)}
)} @@ -300,9 +301,7 @@ export default function OnboardingWizard() { {/* Provider */} {currentStep.id === "provider" && (
-

- Connect your first AI provider. You can add more later. -

+

{t("providerDesc")}

{COMMON_PROVIDERS.map((p) => ( )} {testStatus === "testing" && ( @@ -380,7 +377,7 @@ export default function OnboardingWizard() { onClick={handleTestProvider} className="text-xs text-text-muted underline cursor-pointer" > - Retry + {t("retry")}
)} @@ -390,12 +387,9 @@ export default function OnboardingWizard() { {/* Done */} {currentStep.id === "done" && (
-

- You're all set! Your OmniRoute instance is configured and ready to proxy AI - requests. -

+

{t("doneDesc")}

-

Your endpoint:

+

{t("yourEndpoint")}

http://localhost:20128/api/v1
@@ -410,7 +404,7 @@ export default function OnboardingWizard() { onClick={handleBack} className="px-4 py-2 text-sm text-text-muted hover:text-text-main transition-colors cursor-pointer" > - Back + {tc("back")} )}
@@ -420,7 +414,7 @@ export default function OnboardingWizard() { onClick={handleNext} className="px-4 py-2 text-sm text-text-muted hover:text-text-main transition-colors cursor-pointer" > - Skip + {t("skip")} )} {currentStep.id === "welcome" && ( @@ -428,7 +422,7 @@ export default function OnboardingWizard() { onClick={handleNext} className="px-6 py-2.5 bg-primary rounded-lg text-white font-medium text-sm hover:bg-primary/90 transition-colors cursor-pointer" > - Get Started + {t("getStarted")} )} {currentStep.id === "security" && ( @@ -437,7 +431,7 @@ export default function OnboardingWizard() { disabled={!skipSecurity && (!password || password !== confirmPassword)} className="px-6 py-2.5 bg-primary rounded-lg text-white font-medium text-sm hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer" > - {skipSecurity ? "Skip & Continue" : "Set Password"} + {skipSecurity ? t("skipAndContinue") : t("setPassword")} )} {currentStep.id === "provider" && ( @@ -446,7 +440,7 @@ export default function OnboardingWizard() { disabled={!selectedProvider || !providerKey} className="px-6 py-2.5 bg-primary rounded-lg text-white font-medium text-sm hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer" > - Add Provider + {t("addProvider")} )} {currentStep.id === "test" && ( @@ -454,7 +448,7 @@ export default function OnboardingWizard() { onClick={handleNext} className="px-6 py-2.5 bg-primary rounded-lg text-white font-medium text-sm hover:bg-primary/90 transition-colors cursor-pointer" > - {testStatus === "success" ? "Continue" : "Skip"} + {testStatus === "success" ? t("continue") : t("skip")} )} {isLastStep && ( @@ -462,7 +456,7 @@ export default function OnboardingWizard() { onClick={handleFinish} className="px-6 py-2.5 bg-green-500 rounded-lg text-white font-medium text-sm hover:bg-green-500/90 transition-colors cursor-pointer" > - Go to Dashboard → + {t("goToDashboard")} )}
@@ -476,7 +470,7 @@ export default function OnboardingWizard() { onClick={handleFinish} className="text-xs text-text-muted/60 hover:text-text-muted transition-colors cursor-pointer" > - Skip wizard entirely + {t("skipWizard")}
)} diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 11bf5342c9..5ed2b824f4 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -301,15 +301,46 @@ "console": "Console" }, "onboarding": { - "welcome": "Welcome to OmniRoute", + "welcome": "Welcome", + "security": "Security", + "test": "Test", + "ready": "Ready!", "setPassword": "Set Password", "addProvider": "Add your first provider", "getStarted": "Get Started", "skip": "Skip", + "skipWizard": "Skip wizard entirely", + "skipPassword": "Skip password setup", + "skipAndContinue": "Skip & Continue", "passwordLabel": "Password", "confirmPassword": "Confirm Password", + "enterPassword": "Enter password", + "confirmPasswordPlaceholder": "Confirm password", + "passwordsMismatch": "Passwords do not match", "setupComplete": "Setup Complete!", - "goToDashboard": "Go to Dashboard" + "goToDashboard": "Go to Dashboard \u2192", + "welcomeDesc": "OmniRoute is your local AI API proxy. It routes requests to multiple AI providers with load balancing, failover, and usage tracking.", + "multiProvider": "Multi-Provider", + "usageTracking": "Usage Tracking", + "apiKeyMgmt": "API Key Mgmt", + "securityDesc": "Set a password to protect your dashboard, or skip for now.", + "providerDesc": "Connect your first AI provider. You can add more later.", + "apiKeyRequired": "API Key (required)", + "customUrlOptional": "Custom URL (optional)", + "testDesc": "Let's verify your provider connection works.", + "runTest": "Run Connection Test", + "testingConnection": "Testing connection...", + "connectionSuccessful": "Connection successful! Your provider is ready.", + "noProviderFound": "No provider found. You can add one from the dashboard later.", + "testFailed": "Test failed, but you can configure this later.", + "couldNotTest": "Could not test right now. You can test from the dashboard.", + "doneDesc": "You're all set! Your OmniRoute instance is configured and ready to proxy AI requests.", + "yourEndpoint": "Your endpoint:", + "continue": "Continue", + "retry": "Retry", + "failedSetPassword": "Failed to set password. Try again.", + "failedAddProvider": "Failed to add provider. Try again.", + "connectionError": "Connection error. Please try again." }, "providers": { "title": "Providers", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index b6290ec994..825cdbaba3 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -301,15 +301,46 @@ "console": "Console" }, "onboarding": { - "welcome": "Bem-vindo ao OmniRoute", + "welcome": "Bem-vindo", + "security": "Segurança", + "test": "Teste", + "ready": "Pronto!", "setPassword": "Definir Senha", "addProvider": "Adicione seu primeiro provedor", "getStarted": "Começar", "skip": "Pular", + "skipWizard": "Pular assistente completamente", + "skipPassword": "Pular configuração de senha", + "skipAndContinue": "Pular e Continuar", "passwordLabel": "Senha", "confirmPassword": "Confirmar Senha", + "enterPassword": "Digite a senha", + "confirmPasswordPlaceholder": "Confirme a senha", + "passwordsMismatch": "As senhas não coincidem", "setupComplete": "Configuração Concluída!", - "goToDashboard": "Ir para o Painel" + "goToDashboard": "Ir para o Painel \u2192", + "welcomeDesc": "OmniRoute é seu proxy local de API de IA. Ele roteia requisições para múltiplos provedores de IA com balanceamento de carga, failover e rastreamento de uso.", + "multiProvider": "Multi-Provedor", + "usageTracking": "Rastreamento de Uso", + "apiKeyMgmt": "Ger. de Chaves API", + "securityDesc": "Defina uma senha para proteger seu painel, ou pule por enquanto.", + "providerDesc": "Conecte seu primeiro provedor de IA. Você pode adicionar mais depois.", + "apiKeyRequired": "Chave de API (obrigatório)", + "customUrlOptional": "URL personalizada (opcional)", + "testDesc": "Vamos verificar se a conexão com seu provedor funciona.", + "runTest": "Executar Teste de Conexão", + "testingConnection": "Testando conexão...", + "connectionSuccessful": "Conexão bem-sucedida! Seu provedor está pronto.", + "noProviderFound": "Nenhum provedor encontrado. Você pode adicionar um pelo painel depois.", + "testFailed": "Teste falhou, mas você pode configurar isso depois.", + "couldNotTest": "Não foi possível testar agora. Você pode testar pelo painel.", + "doneDesc": "Tudo pronto! Sua instância OmniRoute está configurada e pronta para rotear requisições de IA.", + "yourEndpoint": "Seu endpoint:", + "continue": "Continuar", + "retry": "Tentar Novamente", + "failedSetPassword": "Falha ao definir senha. Tente novamente.", + "failedAddProvider": "Falha ao adicionar provedor. Tente novamente.", + "connectionError": "Erro de conexão. Tente novamente." }, "providers": { "title": "Provedores",