-
void exportConfig()} title="Export config">
+ void exportConfig()} title={t('settings.exportConfig')}>
@@ -175,7 +177,7 @@ export function SettingsView({ gateway, onClose, onConfigSaved, onMainModelChang
triggerHaptic('warning')
void resetConfig()
}}
- title="Reset to defaults"
+ title={t('settings.resetDefaults')}
>
diff --git a/apps/desktop/src/app/shell/titlebar-controls.tsx b/apps/desktop/src/app/shell/titlebar-controls.tsx
index 1f0a8690ed52..814851e0bae1 100644
--- a/apps/desktop/src/app/shell/titlebar-controls.tsx
+++ b/apps/desktop/src/app/shell/titlebar-controls.tsx
@@ -11,6 +11,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
+import { useDesktopI18n } from '@/i18n'
import { triggerHaptic } from '@/lib/haptics'
import { Volume2, VolumeX } from '@/lib/icons'
import { cn } from '@/lib/utils'
@@ -54,6 +55,7 @@ export function TitlebarControls({
onOpenSearch
}: TitlebarControlsProps) {
const navigate = useNavigate()
+ const { t } = useDesktopI18n()
const hapticsMuted = useStore($hapticsMuted)
const fileBrowserOpen = useStore($fileBrowserOpen)
const sidebarOpen = useStore($sidebarOpen)
@@ -74,7 +76,7 @@ export function TitlebarControls({
{
icon:
,
id: 'sidebar',
- label: sidebarOpen ? 'Hide sidebar' : 'Show sidebar',
+ label: sidebarOpen ? t('titlebar.hideSidebar') : t('titlebar.showSidebar'),
onSelect: () => {
triggerHaptic('tap')
toggleSidebarOpen()
@@ -84,12 +86,12 @@ export function TitlebarControls({
active: commandCenterOpen,
icon:
,
id: 'search',
- label: 'Search',
+ label: t('titlebar.search'),
onSelect: () => {
triggerHaptic('open')
onOpenSearch()
},
- title: 'Search sessions, views, and actions'
+ title: t('titlebar.searchTitle')
},
...leftTools
]
@@ -98,7 +100,7 @@ export function TitlebarControls({
active: fileBrowserOpen,
icon:
,
id: 'right-sidebar',
- label: fileBrowserOpen ? 'Hide right sidebar' : 'Show right sidebar',
+ label: fileBrowserOpen ? t('titlebar.hideRightSidebar') : t('titlebar.showRightSidebar'),
onSelect: () => {
triggerHaptic('tap')
toggleFileBrowserOpen()
@@ -111,13 +113,13 @@ export function TitlebarControls({
active: hapticsMuted,
icon: hapticsMuted ?
:
,
id: 'haptics',
- label: hapticsMuted ? 'Unmute haptics' : 'Mute haptics',
+ label: hapticsMuted ? t('titlebar.unmuteHaptics') : t('titlebar.muteHaptics'),
onSelect: toggleHaptics
},
{
icon:
,
id: 'settings',
- label: 'Open settings',
+ label: t('titlebar.openSettings'),
onSelect: () => {
triggerHaptic('open')
onOpenSettings()
@@ -133,7 +135,7 @@ export function TitlebarControls({
return (
<>
{leftToolbarTools
@@ -153,7 +155,7 @@ export function TitlebarControls({
*/}
{visiblePaneTools.length > 0 && (
{visiblePaneTools.map(tool => (
@@ -163,7 +165,7 @@ export function TitlebarControls({
)}
{visibleSystemToolsBeforeSettings.map(tool => (
@@ -178,14 +180,16 @@ export function TitlebarControls({
}
function ProfilesMenuButton({ navigate }: { navigate: ReturnType
}) {
+ const { t } = useDesktopI18n()
+
return (
diff --git a/apps/desktop/src/components/boot-failure-overlay.tsx b/apps/desktop/src/components/boot-failure-overlay.tsx
index 943981302580..c03a1abfe587 100644
--- a/apps/desktop/src/components/boot-failure-overlay.tsx
+++ b/apps/desktop/src/components/boot-failure-overlay.tsx
@@ -2,6 +2,7 @@ import { useStore } from '@nanostores/react'
import { useEffect, useState } from 'react'
import { Button } from '@/components/ui/button'
+import { useDesktopI18n } from '@/i18n'
import { AlertTriangle, FileText, Loader2, RefreshCw, Wrench } from '@/lib/icons'
import { $desktopBoot } from '@/store/boot'
import { $desktopOnboarding } from '@/store/onboarding'
@@ -13,6 +14,7 @@ type BusyAction = 'local' | 'repair' | 'retry' | null
// renders dead — "gateway offline", no composer, only a toast — with no way
// to retry, repair the install, switch the gateway, or find the logs.
export function BootFailureOverlay() {
+ const { t } = useDesktopI18n()
const boot = useStore($desktopBoot)
const onboarding = useStore($desktopOnboarding)
const [busy, setBusy] = useState(null)
@@ -69,11 +71,8 @@ export function BootFailureOverlay() {
-
Hermes couldn't start
-
- The background gateway didn't come up. Try one of the recovery steps below — nothing here deletes your
- chats or settings.
-
+
{t('bootFailure.title')}
+
{t('bootFailure.description')}
@@ -86,24 +85,22 @@ export function BootFailureOverlay() {
-
- Repair re-runs the installer and can take a few minutes on a fresh machine.
-
+
{t('bootFailure.repairHint')}
{logs.length > 0 ? (
@@ -113,7 +110,7 @@ export function BootFailureOverlay() {
onClick={() => setShowLogs(v => !v)}
type="button"
>
- {showLogs ? 'Hide' : 'Show'} recent logs
+ {showLogs ? t('bootFailure.hideLogs') : t('bootFailure.showLogs')}
{showLogs ? (
diff --git a/apps/desktop/src/components/desktop-install-overlay.tsx b/apps/desktop/src/components/desktop-install-overlay.tsx
index 16ccc6ad3ab8..5ae3765bd026 100644
--- a/apps/desktop/src/components/desktop-install-overlay.tsx
+++ b/apps/desktop/src/components/desktop-install-overlay.tsx
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { Button } from '@/components/ui/button'
+import { useDesktopI18n } from '@/i18n'
import { AlertTriangle, Check, ChevronDown, ChevronRight, Loader2 } from '@/lib/icons'
import { cn } from '@/lib/utils'
import type {
@@ -49,12 +50,12 @@ interface StageRowProps {
now: number
}
-const STATE_LABEL: Record = {
- pending: 'Pending',
- running: 'Installing',
- succeeded: 'Done',
- skipped: 'Skipped',
- failed: 'Failed'
+const STATE_LABEL_KEY: Record = {
+ pending: 'install.pending',
+ running: 'install.running',
+ succeeded: 'install.succeeded',
+ skipped: 'install.skipped',
+ failed: 'install.failed'
}
function formatStageName(name: string): string {
@@ -85,6 +86,7 @@ function formatElapsed(ms: number): string {
}
function StageRow({ descriptor, result, isCurrent, now }: StageRowProps) {
+ const { t } = useDesktopI18n()
const state: DesktopBootstrapStageState = result?.state || 'pending'
const elapsed =
state === 'running' && typeof result?.startedAt === 'number' ? formatElapsed(now - result.startedAt) : ''
@@ -121,9 +123,13 @@ function StageRow({ descriptor, result, isCurrent, now }: StageRowProps) {
{formatStageName(descriptor.name)}
- {state === 'running' ? (elapsed ? `${STATE_LABEL[state]} · ${elapsed}` : STATE_LABEL[state]) : null}
+ {state === 'running'
+ ? elapsed
+ ? `${t(STATE_LABEL_KEY[state])} · ${elapsed}`
+ : t(STATE_LABEL_KEY[state])
+ : null}
{state === 'succeeded' || state === 'skipped' ? formatDuration(result?.durationMs) : null}
- {state === 'failed' ? STATE_LABEL[state] : null}
+ {state === 'failed' ? t(STATE_LABEL_KEY[state]) : null}
{reason && state !== 'pending' &&