feat(i18n): replace hardcoded strings with translation keys in HomePageClient

Replace hardcoded English text in quick start links and getting started
steps with proper i18n translation keys. Use `t.rich()` for step
descriptions containing inline links to support rich text interpolation.
This commit is contained in:
diegosouzapw 2026-02-26 03:05:30 -03:00
parent 187aba0514
commit 107b9e8cd2
27 changed files with 1497 additions and 869 deletions

View file

@ -4,6 +4,7 @@ import { useState, useEffect, useRef } from "react";
import { Card, Button, ModelSelectModal, ManualConfigModal } from "@/shared/components";
import Image from "next/image";
import CliStatusBadge from "./CliStatusBadge";
import { useTranslations } from "next-intl";
const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL;
@ -19,6 +20,7 @@ export default function ClineToolCard({
batchStatus,
lastConfiguredAt,
}) {
const t = useTranslations("cliTools");
const [clineStatus, setClineStatus] = useState(null);
const [checkingCline, setCheckingCline] = useState(false);
const [applying, setApplying] = useState(false);
@ -109,12 +111,12 @@ export default function ClineToolCard({
body: JSON.stringify({ tool: "cline", backupId }),
});
if (res.ok) {
setMessage({ type: "success", text: "Backup restored! Reloading status..." });
setMessage({ type: "success", text: t("backupRestoredReloading") });
await checkClineStatus();
await fetchBackups();
} else {
const data = await res.json();
setMessage({ type: "error", text: data.error || "Failed to restore backup" });
setMessage({ type: "error", text: data.error || t("failedRestoreBackup") });
}
} catch (e) {
setMessage({ type: "error", text: e.message });
@ -161,11 +163,11 @@ export default function ClineToolCard({
});
const data = await res.json();
if (res.ok) {
setMessage({ type: "success", text: data.message || "Applied!" });
setMessage({ type: "success", text: data.message || t("applied") });
await checkClineStatus();
await fetchBackups();
} else {
setMessage({ type: "error", text: data.error || "Failed" });
setMessage({ type: "error", text: data.error || t("failed") });
}
} catch (error) {
setMessage({ type: "error", text: error.message });
@ -181,13 +183,13 @@ export default function ClineToolCard({
const res = await fetch("/api/cli-tools/cline-settings", { method: "DELETE" });
const data = await res.json();
if (res.ok) {
setMessage({ type: "success", text: data.message || "Reset!" });
setMessage({ type: "success", text: data.message || t("resetDone") });
setSelectedModel("");
hasInitializedModel.current = false;
await checkClineStatus();
await fetchBackups();
} else {
setMessage({ type: "error", text: data.error || "Failed" });
setMessage({ type: "error", text: data.error || t("failed") });
}
} catch (error) {
setMessage({ type: "error", text: error.message });
@ -240,7 +242,7 @@ export default function ClineToolCard({
lastConfiguredAt={lastConfiguredAt}
/>
</div>
<p className="text-xs text-text-muted truncate">{tool.description}</p>
<p className="text-xs text-text-muted truncate">{t("toolDescriptions.cline")}</p>
</div>
</div>
<span
@ -257,7 +259,7 @@ export default function ClineToolCard({
<span className="material-symbols-outlined animate-spin text-base">
progress_activity
</span>
<span>Checking Cline CLI...</span>
<span>{t("checkingCli", { tool: "Cline" })}</span>
</div>
)}
@ -273,14 +275,14 @@ export default function ClineToolCard({
<div className="flex flex-col gap-1">
<p className="text-sm font-medium">
{cliReady
? "Cline CLI detected and ready"
? t("cliDetectedReady", { tool: "Cline" })
: clineStatus.installed
? "Cline CLI installed but not runnable"
: "Cline CLI not detected"}
? t("cliNotRunnable", { tool: "Cline" })
: t("cliNotDetected", { tool: "Cline" })}
</p>
{clineStatus.commandPath && (
<p className="text-xs text-text-muted">
Binary:{" "}
{t("binary")}:{" "}
<code className="px-1 py-0.5 rounded bg-black/5 dark:bg-white/10">
{clineStatus.commandPath}
</code>
@ -288,7 +290,7 @@ export default function ClineToolCard({
)}
{clineStatus.globalStatePath && (
<p className="text-xs text-text-muted">
Config:{" "}
{t("configPathShort")}:{" "}
<code className="px-1 py-0.5 rounded bg-black/5 dark:bg-white/10">
{clineStatus.globalStatePath}
</code>
@ -307,10 +309,10 @@ export default function ClineToolCard({
</span>
<div className="flex flex-col gap-1">
<p className="text-sm text-green-700 dark:text-green-300">
OmniRoute is configured as OpenAI-compatible provider
{t("omnirouteConfiguredOpenAiCompatible")}
</p>
<p className="text-xs text-text-muted">
Provider: <strong>openai</strong> Model:{" "}
{t("provider")}: <strong>openai</strong> {t("model")}:{" "}
<strong>{clineStatus.settings?.openAiModelId || "—"}</strong>
</p>
</div>
@ -319,13 +321,13 @@ export default function ClineToolCard({
{/* Model selection */}
<div className="flex flex-col gap-2">
<label className="text-sm text-text-muted">Model</label>
<label className="text-sm text-text-muted">{t("model")}</label>
<div className="flex items-center gap-2">
<input
type="text"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
placeholder="provider/model-id"
placeholder={t("providerModelPlaceholder")}
className="flex-1 px-3 py-2 bg-bg-secondary rounded-lg text-sm border border-border focus:outline-none focus:ring-1 focus:ring-primary/50"
/>
<Button
@ -334,7 +336,7 @@ export default function ClineToolCard({
onClick={() => setModalOpen(true)}
disabled={!hasActiveProviders}
>
Select
{t("select")}
</Button>
<Button
variant="ghost"
@ -348,7 +350,7 @@ export default function ClineToolCard({
{/* API Key selection */}
<div className="flex flex-col gap-2">
<label className="text-sm text-text-muted">API Key</label>
<label className="text-sm text-text-muted">{t("apiKey")}</label>
{apiKeys && apiKeys.length > 0 ? (
<select
value={selectedApiKey}
@ -363,7 +365,7 @@ export default function ClineToolCard({
</select>
) : (
<p className="text-sm text-text-muted">
{cloudEnabled ? "No API keys available" : "Using default: sk_omniroute"}
{cloudEnabled ? t("noApiKeysAvailable") : t("usingDefaultOmniroute")}
</p>
)}
</div>
@ -378,14 +380,14 @@ export default function ClineToolCard({
loading={applying}
>
<span className="material-symbols-outlined text-[14px] mr-1">save</span>
{configStatus === "configured" ? "Update Config" : "Apply Config"}
{configStatus === "configured" ? t("updateConfig") : t("applyConfig")}
</Button>
{configStatus === "configured" && (
<Button variant="outline" size="sm" onClick={handleReset} loading={restoring}>
<span className="material-symbols-outlined text-[14px] mr-1">
restart_alt
</span>
Reset
{t("reset")}
</Button>
)}
</div>
@ -414,7 +416,7 @@ export default function ClineToolCard({
chevron_right
</span>
<span className="material-symbols-outlined text-[16px]">backup</span>
Backups {backups.length > 0 && `(${backups.length})`}
{t("backups")} {backups.length > 0 && `(${backups.length})`}
</button>
{showBackups && backups.length > 0 && (
<div className="mt-2 flex flex-col gap-1.5 pl-6">
@ -435,14 +437,14 @@ export default function ClineToolCard({
onClick={() => handleRestoreBackup(b.id)}
loading={restoringBackup === b.id}
>
Restore
{t("restore")}
</Button>
</div>
))}
</div>
)}
{showBackups && backups.length === 0 && (
<p className="mt-2 pl-6 text-xs text-text-muted">No backups available.</p>
<p className="mt-2 pl-6 text-xs text-text-muted">{t("noBackupsAvailable")}</p>
)}
</div>
</>
@ -458,13 +460,13 @@ export default function ClineToolCard({
onSelect={handleSelectModel}
selectedModel={selectedModel}
activeProviders={activeProviders}
title="Select Model for Cline"
title={t("selectModelForTool", { tool: "Cline" })}
/>
{showManualConfigModal && (
<ManualConfigModal
isOpen={showManualConfigModal}
onClose={() => setShowManualConfigModal(false)}
title="Cline Manual Configuration"
title={t("clineManualConfiguration")}
{...({
onApply: handleManualConfig,
currentConfig: {