diff --git a/apps/app/src/app/components/den-settings-panel.tsx b/apps/app/src/app/components/den-settings-panel.tsx
index bf33410b9..6dc7a06b2 100644
--- a/apps/app/src/app/components/den-settings-panel.tsx
+++ b/apps/app/src/app/components/den-settings-panel.tsx
@@ -21,6 +21,7 @@ import {
readDenTemplateCacheSnapshot,
} from "../lib/den-template-cache";
import { usePlatform } from "../context/platform";
+import { t } from "../../i18n";
type DenSettingsPanelProps = {
developerMode: boolean;
@@ -55,18 +56,18 @@ function workerStatusMeta(status: string) {
const normalized = status.trim().toLowerCase();
switch (normalized) {
case "healthy":
- return { label: "Ready", tone: "ready" as const, canOpen: true };
+ return { label: t("den.status_ready"), tone: "ready" as const, canOpen: true };
case "provisioning":
- return { label: "Provisioning", tone: "warning" as const, canOpen: false };
+ return { label: t("den.status_provisioning"), tone: "warning" as const, canOpen: false };
case "failed":
- return { label: "Failed", tone: "error" as const, canOpen: false };
+ return { label: t("den.status_failed"), tone: "error" as const, canOpen: false };
case "stopped":
- return { label: "Stopped", tone: "neutral" as const, canOpen: false };
+ return { label: t("den.status_stopped"), tone: "neutral" as const, canOpen: false };
default:
return {
label: normalized
? `${normalized.slice(0, 1).toUpperCase()}${normalized.slice(1)}`
- : "Unknown",
+ : t("den.status_unknown"),
tone: "neutral" as const,
canOpen: normalized === "ready",
};
@@ -145,10 +146,10 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
});
const summaryLabel = createMemo(() => {
- if (authError()) return "Needs attention";
- if (sessionBusy()) return "Checking session";
- if (isSignedIn()) return "Connected";
- return "Signed out";
+ if (authError()) return t("den.summary_needs_attention");
+ if (sessionBusy()) return t("den.summary_checking");
+ if (isSignedIn()) return t("den.summary_connected");
+ return t("den.summary_signed_out");
});
createEffect(() => {
@@ -177,8 +178,8 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
platform.openLink(buildDenAuthUrl(baseUrl(), mode));
setStatusMessage(
mode === "sign-up"
- ? "Finish account creation in your browser to connect OpenWork."
- : "Finish signing in in your browser to connect OpenWork.",
+ ? t("den.finish_creation")
+ : t("den.finish_sign_in"),
);
setAuthError(null);
};
@@ -300,7 +301,11 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
const applyBaseUrl = () => {
const normalized = normalizeDenBaseUrl(baseUrlDraft());
if (!normalized) {
+<<<<<<< HEAD
+ setBaseUrlError(t("den.invalid_url"));
+=======
setBaseUrlError("Enter a valid http:// or https:// Cloud control plane URL.");
+>>>>>>> upstream/dev
return;
}
@@ -313,7 +318,11 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
setBaseUrl(resolved.baseUrl);
setBaseUrlDraft(resolved.baseUrl);
+<<<<<<< HEAD
+ clearSignedInState(t("den.url_updated"));
+=======
clearSignedInState("Updated the Cloud control plane URL. Sign in again to continue.");
+>>>>>>> upstream/dev
};
const refreshOrgs = async (quiet = false) => {
@@ -343,11 +352,11 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
});
if (!quiet && response.orgs.length > 0) {
setStatusMessage(
- `Loaded ${response.orgs.length} org${response.orgs.length === 1 ? "" : "s"}.`,
+ t("den.loaded_orgs").replace("{count}", String(response.orgs.length)),
);
}
} catch (error) {
- setOrgsError(error instanceof Error ? error.message : "Failed to load orgs.");
+ setOrgsError(error instanceof Error ? error.message : t("den.failed_load_orgs"));
} finally {
setOrgsBusy(false);
}
@@ -367,14 +376,15 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
const nextWorkers = await client().listWorkers(orgId, 20);
setWorkers(nextWorkers);
if (!quiet) {
+ const orgName = activeOrg()?.name ?? "this org";
setStatusMessage(
nextWorkers.length > 0
- ? `Loaded ${nextWorkers.length} worker${nextWorkers.length === 1 ? "" : "s"} for ${activeOrg()?.name ?? "this org"}.`
- : `No workers found for ${activeOrg()?.name ?? "this org"}.`,
+ ? t("den.loaded_workers").replace("{count}", String(nextWorkers.length)).replace("{org}", orgName)
+ : t("den.no_workers_found").replace("{org}", orgName),
);
}
} catch (error) {
- setWorkersError(error instanceof Error ? error.message : "Failed to load workers.");
+ setWorkersError(error instanceof Error ? error.message : t("den.failed_load_workers"));
} finally {
setWorkersBusy(false);
}
@@ -431,7 +441,7 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
.then((nextUser) => {
if (cancelled) return;
setUser(nextUser);
- setStatusMessage(`Signed in as ${nextUser.email}.`);
+ setStatusMessage(t("den.signed_in_as").replace("{email}", nextUser.email));
})
.catch((error) => {
if (cancelled) return;
@@ -441,7 +451,11 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
clearSessionState();
}
setAuthError(
+<<<<<<< HEAD
+ error instanceof Error ? error.message : t("den.no_session"),
+=======
error instanceof Error ? error.message : "No active Cloud session found.",
+>>>>>>> upstream/dev
);
})
.finally(() => {
@@ -484,13 +498,22 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
setAuthError(null);
setStatusMessage(
customEvent.detail.email?.trim()
+<<<<<<< HEAD
+ ? t("den.connected_as").replace("{email}", customEvent.detail.email.trim())
+ : t("den.connected"),
+=======
? `Connected OpenWork Cloud as ${customEvent.detail.email.trim()}.`
: "Connected OpenWork Cloud.",
+>>>>>>> upstream/dev
);
} else if (customEvent.detail?.status === "error") {
setAuthError(
customEvent.detail.message?.trim() ||
+<<<<<<< HEAD
+ t("den.sign_in_failed"),
+=======
"Failed to finish OpenWork Cloud sign-in.",
+>>>>>>> upstream/dev
);
}
};
@@ -520,15 +543,19 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
setAuthBusy(false);
}
+<<<<<<< HEAD
+ clearSignedInState(t("den.signed_out_msg"));
+=======
clearSignedInState(
"Signed out and cleared your OpenWork Cloud session on this device.",
);
+>>>>>>> upstream/dev
};
const handleOpenWorker = async (workerId: string, workerName: string) => {
const orgId = activeOrgId().trim();
if (!orgId) {
- setWorkersError("Choose an org before opening a worker.");
+ setWorkersError(t("den.choose_org"));
return;
}
@@ -541,9 +568,7 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
const accessToken =
tokens.ownerToken?.trim() || tokens.clientToken?.trim() || "";
if (!openworkUrl || !accessToken) {
- throw new Error(
- "Worker is not ready to open yet. Try again after provisioning finishes.",
- );
+ throw new Error(t("den.worker_not_ready"));
}
const ok = await props.connectRemoteWorkspace({
@@ -553,13 +578,13 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
displayName: workerName,
});
if (!ok) {
- throw new Error(`Failed to open ${workerName} in OpenWork.`);
+ throw new Error(t("den.failed_open_worker").replace("{name}", workerName));
}
- setStatusMessage(`Opened ${workerName} in OpenWork.`);
+ setStatusMessage(t("den.opened_worker").replace("{name}", workerName));
} catch (error) {
setWorkersError(
- error instanceof Error ? error.message : `Failed to open ${workerName}.`,
+ error instanceof Error ? error.message : t("den.failed_open").replace("{name}", workerName),
);
} finally {
setOpeningWorkerId(null);
@@ -626,6 +651,16 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
+<<<<<<< HEAD
+ {t("den.title")}
+
+
+
+ {t("den.description")}
+
+
+ {t("den.tagline")}
+=======
OpenWork Cloud
@@ -635,6 +670,7 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
Sign in to OpenWork Cloud to keep your tasks alive even when your
computer sleeps.
+>>>>>>> upstream/dev
@@ -649,11 +685,19 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
setBaseUrlDraft(event.currentTarget.value)}
+ placeholder={DEFAULT_DEN_BASE_URL}
+ hint={t("den.control_plane_url_hint")}
+=======
label="Cloud control plane URL"
value={baseUrlDraft()}
onInput={(event) => setBaseUrlDraft(event.currentTarget.value)}
placeholder={DEFAULT_DEN_BASE_URL}
hint="Developer mode only. Use this to target a local or self-hosted Cloud control plane. Changing it signs you out so the app can re-hydrate against the new control plane."
+>>>>>>> upstream/dev
disabled={authBusy() || sessionBusy()}
/>
+<<<<<<< HEAD
+ {t("den.workers_scoped")}
+=======
Cloud workers and team templates are scoped to the selected org.
+>>>>>>> upstream/dev
@@ -835,7 +897,11 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
activeOrgName: nextOrg?.name ?? null,
});
setStatusMessage(
+<<<<<<< HEAD
+ t("den.switched_org").replace("{org}", activeOrg()?.name ?? "the selected org"),
+=======
`Switched to ${nextOrg?.name ?? "the selected org"}.`,
+>>>>>>> upstream/dev
);
}}
disabled={orgsBusy() || orgs().length === 0}
@@ -843,7 +909,7 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
{(org) => (
)}
@@ -874,17 +940,24 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
+<<<<<<< HEAD
+ {t("den.workers_title")}
+=======
Cloud workers
+>>>>>>> upstream/dev
- Open workers directly into OpenWork using the same
- remote-connect flow the app already uses elsewhere.
+ {t("den.workers_description")}
+<<<<<<< HEAD
+ {activeOrg()?.name || t("den.no_org_selected")}
+=======
{activeOrgName()}
+>>>>>>> upstream/dev
@@ -908,8 +981,12 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
+<<<<<<< HEAD
+ {t("den.no_workers_visible")}
+=======
No cloud workers are visible for this org yet. Create one in
Cloud, then refresh this tab.
+>>>>>>> upstream/dev
@@ -930,13 +1007,18 @@ export default function DenSettingsPanel(props: DenSettingsPanelProps) {
{status().label}
+<<<<<<< HEAD
+
+ {t("den.mine_badge")}
+=======
Mine
+>>>>>>> upstream/dev
{props.target === "default"
+<<<<<<< HEAD
+ ? translate("model_picker.default_desc")
+ : translate("model_picker.chat_desc")}
+=======
? "Choose the default model for new chats, then fine-tune reasoning profiles on its card before pressing Done."
: "Choose the model for this chat. If a model supports reasoning profiles, configure them on its card."}
+>>>>>>> upstream/dev
diff --git a/apps/app/src/app/components/question-modal.tsx b/apps/app/src/app/components/question-modal.tsx
index 4b3b070bd..52a0269d5 100644
--- a/apps/app/src/app/components/question-modal.tsx
+++ b/apps/app/src/app/components/question-modal.tsx
@@ -2,6 +2,7 @@ import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "so
import type { QuestionInfo } from "@opencode-ai/sdk/v2/client";
import { Check, ChevronRight, HelpCircle } from "lucide-solid";
+import { t, currentLocale } from "../../i18n";
import Button from "./button";
@@ -14,6 +15,7 @@ export type QuestionModalProps = {
};
export default function QuestionModal(props: QuestionModalProps) {
+ const translate = (key: string) => t(key, currentLocale());
const [currentIndex, setCurrentIndex] = createSignal(0);
const [answers, setAnswers] = createSignal([]);
const [currentSelection, setCurrentSelection] = createSignal([]);
@@ -138,7 +140,7 @@ export default function QuestionModal(props: QuestionModalProps) {
- {entries().length ? "No providers match your search." : "No providers available."}
+ {entries().length ? translate("provider.no_match") : translate("provider.none_available")}
Sign in in the browser tab we just opened. We will complete the connection automatically.
+
{translate("provider.browser_signin_desc")}
}
>
@@ -883,13 +899,13 @@ export default function ProviderAuthModal(props: ProviderAuthModalProps) {
The first time you do this you'll need to enable Device auth in your account settings.
@@ -1867,7 +2000,7 @@ export default function SettingsView(props: SettingsViewProps) {
-
Hide titlebar
+
{t("settings.hide_titlebar")}
Hide the window titlebar. Useful for tiling window
managers on Linux (Hyprland, i3, sway).
@@ -1888,18 +2021,113 @@ export default function SettingsView(props: SettingsViewProps) {
+<<<<<<< HEAD
+
+
+
+
+
+
+
+
Model
+
+ Pick the default chat model and review how it reasons.
+
+
+
+
+
+
+ {props.defaultModelLabel}
+
+
+ {props.defaultModelRef}
+
+
+
+ Change
+
+
+
+
+
+
{t("settings.show_model_reasoning")}
+
+ Expand reasoning traces in the UI when a model exposes them.
+
+
+
+ {props.showThinking ? "On" : "Off"}
+
+
+
+
+
+
+ Auto context compaction
+
+
+ Automatically compact after a run completes.
+
+
+
+ {props.autoCompactContext ? "On" : "Off"}
+
+
+
+
+
+
{t("settings.model_behavior")}
+
+ Open the default model picker to choose reasoning profiles when they are available.
+
+
+ {props.modelVariantLabel}
+
+
+
+ Configure
+
+
+
+
+=======
+>>>>>>> upstream/dev
-
Runtime
+
{t("settings.runtime")}
Status for your local engine and OpenWork server.
@@ -1965,7 +2193,7 @@ export default function SettingsView(props: SettingsViewProps) {
-
Enable Exa web search
+
{t("settings.enable_exa_search")}
Applies when OpenWork Orchestrator launches OpenCode. Off by
default until the integration is fully rolled out.
@@ -1987,7 +2215,7 @@ export default function SettingsView(props: SettingsViewProps) {
-
Developer mode
+
{t("settings.developer_mode")}
Enables debug tools, diagnostics, and the Developer tab.