diff --git a/config/render-path-translate-baseline.txt b/config/render-path-translate-baseline.txt index 8fabb878a11..31d0e501f18 100644 --- a/config/render-path-translate-baseline.txt +++ b/config/render-path-translate-baseline.txt @@ -64,26 +64,8 @@ 1 src/renderer/src/components/activity/activity-thread-virtual-row.tsx 21 src/renderer/src/components/agent-session-continuation/AgentSessionContinuationDialog.tsx 1 src/renderer/src/components/agent-session-continuation/AgentSessionContinuationMenuItem.tsx -8 src/renderer/src/components/agent/AgentCombobox.tsx -3 src/renderer/src/components/agent/AgentLaunchPicker.tsx -2 src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx 2 src/renderer/src/components/agent/AgentLaunchPromptComposer.tsx -1 src/renderer/src/components/agent/AgentLaunchSetupActions.tsx -3 src/renderer/src/components/agent/AgentLaunchSetupButton.tsx -2 src/renderer/src/components/agent/AgentLaunchSetupFooter.tsx 9 src/renderer/src/components/agent/AgentLaunchSetupPanel.tsx -1 src/renderer/src/components/agent/AgentLaunchTextModelDefault.tsx -2 src/renderer/src/components/agent/AgentSettingsDialog.tsx -1 src/renderer/src/components/agent/AgentSettingsMenuItem.tsx -2 src/renderer/src/components/agent/agent-combobox-items.tsx -1 src/renderer/src/components/agent/agent-combobox-surface-items.tsx -6 src/renderer/src/components/agent/launch-settings/AgentChatChoiceControls.tsx -5 src/renderer/src/components/agent/launch-settings/AgentChoiceControls.tsx -1 src/renderer/src/components/agent/launch-settings/AgentDefaultSurfacePreviews.tsx -1 src/renderer/src/components/agent/launch-settings/AgentLaunchChoiceEditor.tsx -11 src/renderer/src/components/agent/launch-settings/AgentLaunchDefaultsEditor.tsx -19 src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceControls.tsx -4 src/renderer/src/components/agent/launch-settings/codex-session-source-home-control.tsx 6 src/renderer/src/components/artifacts/ArtifactActions.tsx 2 src/renderer/src/components/artifacts/ArtifactCollection.tsx 1 src/renderer/src/components/artifacts/ArtifactDetailDrawer.tsx @@ -138,23 +120,6 @@ 10 src/renderer/src/components/emulator-pane/emulator-device-picker.tsx 9 src/renderer/src/components/emulator-pane/emulator-pane-toolbar.tsx 5 src/renderer/src/components/emulator-pane/emulator-screen-stream-content.tsx -3 src/renderer/src/components/feature-tips/ChatUiHereCornerCard.tsx -2 src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx -3 src/renderer/src/components/feature-tips/ChatUiHereTipDialog.tsx -5 src/renderer/src/components/feature-tips/ChatUiSwitchedCornerNote.tsx -2 src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx -6 src/renderer/src/components/feature-tips/CliSetupTipDialog.tsx -2 src/renderer/src/components/feature-tips/CliSkillSetupPanel.tsx -7 src/renderer/src/components/feature-tips/CliSkillSetupTerminal.tsx -1 src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.tsx -1 src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.tsx -1 src/renderer/src/components/feature-tips/FeatureTipActions.tsx -5 src/renderer/src/components/feature-tips/FeatureTipsModal.tsx -2 src/renderer/src/components/feature-tips/SessionSearchTipDialog.tsx -4 src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx -3 src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx -1 src/renderer/src/components/feature-tips/feature-tip-session-panel-demo.tsx -1 src/renderer/src/components/feature-tips/use-session-search-tip-setup.ts 2 src/renderer/src/components/file-actions/OpenInContextSubmenu.tsx 5 src/renderer/src/components/floating-terminal/FloatingTerminalEmptyState.tsx 3 src/renderer/src/components/floating-terminal/FloatingTerminalIconContextMenu.tsx @@ -244,44 +209,7 @@ 18 src/renderer/src/components/mobile/slides/HomeSlide.tsx 35 src/renderer/src/components/mobile/slides/TerminalSlide.tsx 7 src/renderer/src/components/mobile/slides/WorktreeListSlide.tsx -1 src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx -9 src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx -6 src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx -4 src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx -8 src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx -6 src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.tsx -1 src/renderer/src/components/new-workspace/NewWorkspaceComposerFooter.tsx -4 src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx -8 src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx -6 src/renderer/src/components/new-workspace/ProjectCombobox.tsx -2 src/renderer/src/components/new-workspace/RunTargetCombobox.tsx -3 src/renderer/src/components/new-workspace/RunTargetField.tsx -5 src/renderer/src/components/new-workspace/RunTargetRowActions.tsx -6 src/renderer/src/components/new-workspace/RunTargetSubmenus.tsx -6 src/renderer/src/components/new-workspace/SetProjectLocationDialog.tsx -13 src/renderer/src/components/new-workspace/SetProjectLocationForms.tsx -4 src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.tsx -2 src/renderer/src/components/new-workspace/smart-workspace-source-row-content.tsx 9 src/renderer/src/components/notifications/mac-notification-permission-card.tsx -11 src/renderer/src/components/onboarding/AgentStep.tsx -12 src/renderer/src/components/onboarding/AgentStepSurfaceSections.tsx -10 src/renderer/src/components/onboarding/AgentSurfaceStep.tsx -3 src/renderer/src/components/onboarding/FeatureSetupInlineTerminal.tsx -7 src/renderer/src/components/onboarding/GhosttyDiscoveryRow.tsx -25 src/renderer/src/components/onboarding/IntegrationsStep.tsx -9 src/renderer/src/components/onboarding/NotificationStep.tsx -7 src/renderer/src/components/onboarding/OnboardingFlow.tsx -2 src/renderer/src/components/onboarding/OnboardingFooter.tsx -1 src/renderer/src/components/onboarding/OnboardingInlineCommandTerminal.tsx -4 src/renderer/src/components/onboarding/OnboardingLanguageLoading.tsx -18 src/renderer/src/components/onboarding/OnboardingLanguageSwitcher.tsx -4 src/renderer/src/components/onboarding/TerminalLaunchDemo.tsx -4 src/renderer/src/components/onboarding/ThemeLookCarousel.tsx -8 src/renderer/src/components/onboarding/ThemeLookGallery.tsx -6 src/renderer/src/components/onboarding/ThemeModeTiles.tsx -13 src/renderer/src/components/onboarding/ThemeStep.tsx -27 src/renderer/src/components/onboarding/WindowsTerminalStep.tsx -8 src/renderer/src/components/onboarding/YoloPermissionsControl.tsx 4 src/renderer/src/components/orca-profiles/OrcaProfileSignOutConfirmDialog.tsx 6 src/renderer/src/components/orchestration-crew/CrewRowDetails.tsx 5 src/renderer/src/components/orchestration-crew/CrewRowMenu.tsx @@ -298,27 +226,7 @@ 2 src/renderer/src/components/provider-accounts/AntigravityProviderSignInArea.tsx 2 src/renderer/src/components/provider-accounts/ProviderHostScopeControl.tsx 1 src/renderer/src/components/provider-accounts/UnsealedCredentialNotice.tsx -5 src/renderer/src/components/pull-request-page/actions/panel.tsx -14 src/renderer/src/components/pull-request-page/checks/details.tsx -3 src/renderer/src/components/pull-request-page/checks/fix-dialog.tsx -1 src/renderer/src/components/pull-request-page/checks/toolbar.tsx -3 src/renderer/src/components/pull-request-page/comments/composer.tsx -3 src/renderer/src/components/pull-request-page/comments/reply-form.tsx -8 src/renderer/src/components/pull-request-page/conversation/comment-card.tsx -2 src/renderer/src/components/pull-request-page/conversation/comment-group.tsx -2 src/renderer/src/components/pull-request-page/conversation/comments-list.tsx -7 src/renderer/src/components/pull-request-page/conversation/description.tsx -1 src/renderer/src/components/pull-request-page/conversation/tab.tsx -6 src/renderer/src/components/pull-request-page/edit/section.tsx -3 src/renderer/src/components/pull-request-page/files/combined-diff-viewer.tsx -7 src/renderer/src/components/pull-request-page/files/toolbar.tsx -16 src/renderer/src/components/pull-request-page/page/header.tsx 1 src/renderer/src/components/pull-request-page/page/surface.tsx -6 src/renderer/src/components/pull-request-page/page/tabs-shell.tsx -1 src/renderer/src/components/pull-request-page/reviewers/panel.tsx -3 src/renderer/src/components/pull-request-page/reviewers/picker-row.tsx -9 src/renderer/src/components/pull-request-page/reviewers/picker.tsx -4 src/renderer/src/components/pull-request-page/reviewers/requested-list.tsx 5 src/renderer/src/components/quick-open-install-rg-guidance.tsx 9 src/renderer/src/components/repo/CreateFromPicker.tsx 5 src/renderer/src/components/repo/NestedRepoChecklist.tsx @@ -342,24 +250,7 @@ 15 src/renderer/src/components/ssh-authentication/SshPassphraseDialog.tsx 10 src/renderer/src/components/star-nag/StarNagToastHost.tsx 12 src/renderer/src/components/stats/ClaudeUsageDailyChart.tsx -15 src/renderer/src/components/stats/ClaudeUsageDetails.tsx -1 src/renderer/src/components/stats/ClaudeUsageLoadingState.tsx -22 src/renderer/src/components/stats/ClaudeUsagePane.tsx 12 src/renderer/src/components/stats/CodexUsageDailyChart.tsx -14 src/renderer/src/components/stats/CodexUsageDetails.tsx -20 src/renderer/src/components/stats/CodexUsagePane.tsx -9 src/renderer/src/components/stats/GrokUsagePane.tsx -14 src/renderer/src/components/stats/MuseUsageDetails.tsx -20 src/renderer/src/components/stats/MuseUsagePane.tsx -14 src/renderer/src/components/stats/OpenCodeUsageDetails.tsx -20 src/renderer/src/components/stats/OpenCodeUsagePane.tsx -6 src/renderer/src/components/stats/ShareUsageButton.tsx -9 src/renderer/src/components/stats/ShareUsageCard.tsx -8 src/renderer/src/components/stats/StatsPane.tsx -2 src/renderer/src/components/stats/UsageBreakdownSection.tsx -20 src/renderer/src/components/stats/UsageOverviewPane.tsx -3 src/renderer/src/components/stats/share-card-utils.tsx -23 src/renderer/src/components/stats/usage-overview-sections.tsx 1 src/renderer/src/components/tab-group/TabGroupDropOverlay.tsx 3 src/renderer/src/components/tab-group/TabGroupPaneActionsMenu.tsx 1 src/renderer/src/components/tab-group/TabGroupPanel.tsx diff --git a/src/renderer/src/components/agent/AgentCombobox.tsx b/src/renderer/src/components/agent/AgentCombobox.tsx index 18112c32c9d..64d98889abe 100644 --- a/src/renderer/src/components/agent/AgentCombobox.tsx +++ b/src/renderer/src/components/agent/AgentCombobox.tsx @@ -18,7 +18,7 @@ import { AgentIconLabel, renderAgentComboboxItem } from './agent-combobox-items' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { rowLabel } from '@/components/tab-bar/tab-agent-row-presentation' import { SurfaceAgentItems, @@ -76,8 +76,9 @@ export default function AgentCombobox({ surfaceRows, ...selection }: AgentComboboxProps): React.JSX.Element { + const t = useTranslate() const blankText = - blankLabel ?? translate('auto.components.agent.AgentCombobox.986f946354', 'Blank Terminal') + blankLabel ?? t('auto.components.agent.AgentCombobox.986f946354', 'Blank Terminal') const allowBlankTerminal = selection.allowBlankTerminal === true const [open, setOpen] = useState(false) const [query, setQuery] = useState('') @@ -102,7 +103,7 @@ export default function AgentCombobox({ [allowBlankTerminal, query] ) const pickerValues = { agents, query, value, blankValue: BLANK_VALUE, blankMatchesQuery } - const surface = surfaceRows ? surfaceCommandValues(surfaceRows.model, pickerValues) : null + const surface = surfaceRows ? surfaceCommandValues(surfaceRows.model, pickerValues, t) : null const surfaceList = surface?.rows ?? [] const openCommandValue = selectionCleared ? '' @@ -279,17 +280,14 @@ export default function AgentCombobox({ {selectionCleared ? ( - {translate( - 'components.agentSessionContinuation.selectAgent', - 'Select an Agent' - )} + {t('components.agentSessionContinuation.selectAgent', 'Select an Agent')} ) : selectedAgent ? ( } label={ picksCli - ? rowLabel({ kind: 'cli', agent: selectedAgent.id }) + ? rowLabel({ kind: 'cli', agent: selectedAgent.id }, t) : selectedAgent.label } /> @@ -299,10 +297,7 @@ export default function AgentCombobox({ label={ allowBlankTerminal ? blankText - : translate( - 'components.agentSessionContinuation.selectAgent', - 'Select an Agent' - ) + : t('components.agentSessionContinuation.selectAgent', 'Select an Agent') } /> )} @@ -331,16 +326,13 @@ export default function AgentCombobox({ > - {translate( + {t( 'auto.components.agent.AgentCombobox.579c768bde', 'No agents match your search.' )} @@ -390,7 +382,7 @@ export default function AgentCombobox({ onMouseEnter={() => setCommandValue('')} className="h-9 w-full justify-start rounded-none px-3 text-xs font-normal text-muted-foreground" > - {translate('auto.components.agent.AgentCombobox.19522e25ee', 'Manage agents')} + {t('auto.components.agent.AgentCombobox.19522e25ee', 'Manage agents')} @@ -407,10 +399,7 @@ export default function AgentCombobox({ size="icon-xs" disabled={disabled} className="absolute right-8 top-1/2 -translate-y-1/2" - aria-label={translate( - 'components.agentCombobox.clearSelection', - 'Clear agent selection' - )} + aria-label={t('components.agentCombobox.clearSelection', 'Clear agent selection')} onClick={() => { clearSelection.onClear() closePicker() @@ -421,7 +410,7 @@ export default function AgentCombobox({ - {translate('components.agentCombobox.clearSelection', 'Clear agent selection')} + {t('components.agentCombobox.clearSelection', 'Clear agent selection')} )} diff --git a/src/renderer/src/components/agent/AgentLaunchPicker.tsx b/src/renderer/src/components/agent/AgentLaunchPicker.tsx index 7225639941e..e7cc75ad0ca 100644 --- a/src/renderer/src/components/agent/AgentLaunchPicker.tsx +++ b/src/renderer/src/components/agent/AgentLaunchPicker.tsx @@ -22,7 +22,7 @@ import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { AgentIcon, getAgentLabel } from '@/lib/agent-catalog' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { rowLabel } from '../tab-bar/tab-agent-row-presentation' import type { NewTabAgentMenuModel } from '../tab-bar/use-new-tab-agent-menu-rows' import { useAgentPickerDraftScope } from './AgentLaunchSetupButton' @@ -92,6 +92,7 @@ export const AgentLaunchPicker = memo( surface?: AgentPickerDraft['surface'] ) => void }) { + const t = useTranslate() useUiLanguageRevision() const ownDraftScope = useAgentPickerDraftScope() const [settingsOpen, setSettingsOpen] = useState(false) @@ -175,9 +176,9 @@ export const AgentLaunchPicker = memo( const summary = [modelLabel, choices?.options?.effort].filter(Boolean).join(' · ') const agentLabel = agent ? cli - ? rowLabel({ kind: 'cli', agent }) + ? rowLabel({ kind: 'cli', agent }, t) : getAgentLabel(agent) - : (emptyLabel ?? translate('agentPicker.select', 'Select agent')) + : (emptyLabel ?? t('agentPicker.select', 'Select agent')) return (
@@ -186,7 +187,7 @@ export const AgentLaunchPicker = memo( ref={trigger} type="button" data-agent-launch-picker-trigger="true" - aria-label={translate('agentPicker.select', 'Select agent')} + aria-label={t('agentPicker.select', 'Select agent')} variant="outline" className="w-full min-w-0" disabled={disabled || model.state !== 'settled'} @@ -260,7 +261,7 @@ export const AgentLaunchPicker = memo( /> ) : ( - {translate('agentPicker.select', 'Select agent')} + {t('agentPicker.select', 'Select agent')} )} diff --git a/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx b/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx index 8250dc309d7..42e4135285f 100644 --- a/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx +++ b/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx @@ -8,7 +8,7 @@ import { parseExecutionHostId } from '../../../../shared/execution-host' import { Input } from '@/components/ui/input' import { AgentPickerMenuRow, type AgentPickerMenuLaunch } from './AgentLaunchPickerMenuRow' import { DropdownMenuItem } from '@/components/ui/dropdown-menu' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { retireAgentPickerDrafts, type AgentPickerSetupIdentity } from './AgentLaunchSetupButton' import type { NewTabAgentMenuModel } from '../tab-bar/use-new-tab-agent-menu-rows' import { @@ -58,6 +58,7 @@ export function AgentLaunchPickerMenu({ defaultAgent?: AgentPickerEntry['agent'] | 'blank' | null onSetDefault?: (agent: AgentPickerEntry['agent']) => void }) { + const t = useTranslate() useEffect(() => { if (setupDisabled) { return @@ -154,7 +155,7 @@ export function AgentLaunchPickerMenu({ const end = event.currentTarget.value.length event.currentTarget.setSelectionRange(end, end) }} - placeholder={translate('agentPicker.search', 'Search agents and setups…')} + placeholder={t('agentPicker.search', 'Search agents and setups…')} onChange={(event) => setQuery(event.target.value)} onKeyDown={(event) => { if (event.key === 'Escape') { @@ -228,10 +229,7 @@ export function AgentLaunchPickerMenu({ })} {!visible.length && !setups.length ? ( - {translate( - 'auto.components.agent.AgentCombobox.579c768bde', - 'No agents match your search.' - )} + {t('auto.components.agent.AgentCombobox.579c768bde', 'No agents match your search.')} ) : null}
diff --git a/src/renderer/src/components/agent/AgentLaunchPickerMenuRow.tsx b/src/renderer/src/components/agent/AgentLaunchPickerMenuRow.tsx index 0f6af990518..6cc0cd70eee 100644 --- a/src/renderer/src/components/agent/AgentLaunchPickerMenuRow.tsx +++ b/src/renderer/src/components/agent/AgentLaunchPickerMenuRow.tsx @@ -19,6 +19,7 @@ import { type AgentPickerEntry, type AgentPickerSurface } from '../../../../shared/agent-launch/picker/agent-launch-picker' +import { useTranslate } from '@/i18n/use-translate' function pickerLaunchRow(entry: AgentPickerEntry, surface: AgentPickerSurface): NewTabAgentMenuRow { return { @@ -89,6 +90,7 @@ export const AgentPickerMenuRow = memo(function AgentPickerMenuRow({ /** Base agent rows only; setup rows are presets, not default-agent choices. */ onSetDefault?: (agent: AgentPickerEntry['agent']) => void }) { + const t = useTranslate() useUiLanguageRevision() const setupId = useId() const preset = initialModel ? { model: initialModel, effort: initialEffort } : undefined @@ -115,7 +117,7 @@ export const AgentPickerMenuRow = memo(function AgentPickerMenuRow({ { onSelectAgent?.(entry.agent) if ( diff --git a/src/renderer/src/components/agent/AgentLaunchSetupActions.tsx b/src/renderer/src/components/agent/AgentLaunchSetupActions.tsx index 29cc9e42e9b..ee845924700 100644 --- a/src/renderer/src/components/agent/AgentLaunchSetupActions.tsx +++ b/src/renderer/src/components/agent/AgentLaunchSetupActions.tsx @@ -1,5 +1,5 @@ import { Button } from '@/components/ui/button' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import type { AgentPickerEntry, AgentPickerSurface @@ -18,11 +18,12 @@ export function AgentLaunchSetupActions({ surface: AgentPickerSurface onUse: (surface: AgentPickerSurface) => void }): React.JSX.Element { + const t = useTranslate() return (
{entry.textGeneration ? ( ) : ( void onUse: (draft: AgentPickerDraft) => boolean | void | Promise }) { + const t = useTranslate() const [localOpen, setOpen] = useState(false) const open = controlledOpen ?? localOpen const setup = useAgentPickerSetup({ @@ -185,7 +186,7 @@ export function AgentLaunchSetupButton({ ) : null}
) - const label = translate('agentPicker.configure', 'Configure {{agent}}', { agent: entry.label }) + const label = t('agentPicker.configure', 'Configure {{agent}}', { agent: entry.label }) if (submenu && renderAnchor) { return ( event.stopPropagation()} @@ -235,7 +236,7 @@ export function AgentLaunchSetupButton({ sticky="always" align="start" sideOffset={6} - aria-label={translate('agentPicker.configure', 'Configure {{agent}}', { + aria-label={t('agentPicker.configure', 'Configure {{agent}}', { agent: entry.label })} // Why: base popovers sit below menus (z-60), so this menu-launched panel would render under the menu's shadow; sharing the menu layer stacks it by open order, like a submenu. diff --git a/src/renderer/src/components/agent/AgentLaunchSetupFooter.tsx b/src/renderer/src/components/agent/AgentLaunchSetupFooter.tsx index 67062434c2a..5392d7079ca 100644 --- a/src/renderer/src/components/agent/AgentLaunchSetupFooter.tsx +++ b/src/renderer/src/components/agent/AgentLaunchSetupFooter.tsx @@ -4,6 +4,7 @@ import { CornerDownLeft } from 'lucide-react' import { Button } from '@/components/ui/button' import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' import { translate } from '@/i18n/i18n' +import { useTranslate, type Translate } from '@/i18n/use-translate' import { getShortcutPlatform } from '@/lib/shortcut-platform' import type { NativeChatComposerHandle } from '../native-chat/composer/native-chat-composer-types' import { AgentLaunchPromptComposer } from './AgentLaunchPromptComposer' @@ -18,20 +19,21 @@ import { formatKeybinding } from '../../../../shared/keybindings' export function agentLaunchSetupArgumentsNote( label: string, options: ReturnType, - noModelFlag: boolean + noModelFlag: boolean, + t: Translate = translate ) { const composed = options.some((option) => option.apply.composedIntoModel) const noOptionFlag = options.some( (option) => !option.apply.cli?.length && !option.apply.composedIntoModel ) return noModelFlag || noOptionFlag - ? translate( + ? t( 'agentPicker.chatChoicesNote', '{{agent}} model or effort choices without a CLI flag apply to chats.', { agent: label } ) : composed - ? translate( + ? t( 'agentPicker.composedChoicesNote', '{{agent}} effort and speed choices are included in the model argument.', { agent: label } @@ -55,6 +57,7 @@ export function AgentLaunchSurfaceButtons({ primary: AgentPickerSurface onSelect: (surface: AgentPickerSurface) => void }): React.JSX.Element { + const t = useTranslate() const platform = getShortcutPlatform() return (
@@ -62,8 +65,8 @@ export function AgentLaunchSurfaceButtons({ const isPrimary = next === primary const label = next === 'chat' - ? translate('agentPicker.launchChat', 'Launch chat') - : translate('agentPicker.launchTerminal', 'Launch terminal') + ? t('agentPicker.launchChat', 'Launch chat') + : t('agentPicker.launchTerminal', 'Launch terminal') return ( )}
@@ -111,6 +112,7 @@ export function AgentDefaultArgsInput({ /** The caller already labels the field, e.g. with a disclosure row. */ hideLabel?: boolean }): React.JSX.Element { + const t = useTranslate() const [draft, setDraft] = useState(null) const inputRef = useRef(null) const chipRef = useRef(null) @@ -157,7 +159,7 @@ export function AgentDefaultArgsInput({
{hideLabel ? null : ( - {translate('auto.components.settings.AgentsPane.cfb3f35775', 'Arguments')} + {t('auto.components.settings.AgentsPane.cfb3f35775', 'Arguments')} )}
@@ -203,7 +205,7 @@ export function AgentDefaultArgsInput({ value={argsDraft} disabled={disabled} readOnly={pending} - aria-label={translate('auto.components.settings.AgentsPane.cfb3f35775', 'Arguments')} + aria-label={t('auto.components.settings.AgentsPane.cfb3f35775', 'Arguments')} onChange={(event) => (onDraftChange ?? setDraft)(event.target.value.replace(/[\r\n]/g, '')) } @@ -239,10 +241,7 @@ export function AgentDefaultArgsInput({ ? undefined : placeholder || defaultArgs || - translate( - 'auto.components.settings.AgentsPane.6f99bf5dd0', - 'No default arguments' - ) + t('auto.components.settings.AgentsPane.6f99bf5dd0', 'No default arguments') } spellCheck={false} className="block min-h-5 max-w-full [field-sizing:content] [overflow-wrap:anywhere]" @@ -268,7 +267,7 @@ export function AgentDefaultArgsInput({ }} className="h-7 shrink-0 text-xs text-muted-foreground hover:text-foreground" > - {translate('auto.components.settings.AgentsPane.5200dac9da', 'Reset')} + {t('auto.components.settings.AgentsPane.5200dac9da', 'Reset')} )}
@@ -286,6 +285,7 @@ export function AgentDefaultEnvInput({ envOverride: Record onSaveEnv: (value: Record) => void | Promise }): React.JSX.Element { + const t = useTranslate() const defaultEnvText = stringifyAgentDefaultEnvDraft(defaultEnv) const draftSeed = stringifyAgentDefaultEnvDraft(envOverride) const [draft, setDraft] = useState(null) @@ -316,7 +316,7 @@ export function AgentDefaultEnvInput({ return (
- {translate('auto.components.settings.AgentsPane.8fbe1f37c1', 'Environment')} + {t('auto.components.settings.AgentsPane.8fbe1f37c1', 'Environment')}
- {translate('auto.components.settings.AgentsPane.5200dac9da', 'Reset')} + {t('auto.components.settings.AgentsPane.5200dac9da', 'Reset')} )}
{envDraftError && (

{envDraftError === 'too-large' - ? translate( + ? t( 'auto.components.settings.AgentsPane.3f1bdf3cb4', 'Environment text is too large to parse safely.' ) - : translate( + : t( 'settings.agents.environmentUnclosedQuote', 'Close the quote to save the environment.' )} diff --git a/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceControls.tsx b/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceControls.tsx index bac97f62d38..e0e630ca7cb 100644 --- a/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceControls.tsx +++ b/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceControls.tsx @@ -4,6 +4,7 @@ import type { Ref } from 'react' import type { AgentChatPermissionMode } from '../../../../../shared/agent-launch/permissions/agent-chat-permission-mode' import type { AgentPermissionChoice } from '../../../../../shared/agent-launch/permissions/tui-agent-permission-choice' import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import type { Translate } from '@/i18n/use-translate' import { cn } from '@/lib/utils' import { Button } from '../../ui/button' @@ -48,9 +49,10 @@ export function AllAgentsPermissionSetting({ onChange: (choice: AgentPermissionChoice) => void onChangeWithoutAuto: (choice: AgentPermissionChoice) => void }): React.JSX.Element { - const title = translate('settings.agents.permissionsTitle', 'Permissions') - const ownLabel = translate('settings.agents.permissionEachOwn', "Each agent's own settings") - const withoutAutoTitle = translate( + const t = useTranslate() + const title = t('settings.agents.permissionsTitle', 'Permissions') + const ownLabel = t('settings.agents.permissionEachOwn', "Each agent's own settings") + const withoutAutoTitle = t( 'settings.agents.otherAgentsPermissionsLabel', 'Permissions for other agents' ) @@ -88,7 +90,7 @@ export function AllAgentsPermissionSetting({

- {translate( + {t( 'settings.agents.permissionsChoiceExplanation', 'What every agent may do without asking. You can set one agent differently below.' )} @@ -97,7 +99,7 @@ export function AllAgentsPermissionSetting({ {choice === 'auto' ? (

- {translate('settings.agents.permissionWithoutAuto', 'Agents without Approve for me')} + {t('settings.agents.permissionWithoutAuto', 'Agents without Approve for me')} @@ -110,7 +112,7 @@ export function AllAgentsPermissionSetting({ > {withoutAutoChoice === null ? ( - {translate('settings.agents.permissionMixed', 'Mixed')} + {t('settings.agents.permissionMixed', 'Mixed')} ) : ( {layout === 'row' ? ( - {translate('settings.agents.rowPermissions', 'Permissions')} + {t('settings.agents.rowPermissions', 'Permissions')} ) : layout === 'stacked' ? ( - + ) : ( - {translate('settings.agents.rowPermissions', 'Permissions')} + {t('settings.agents.rowPermissions', 'Permissions')} )} @@ -216,7 +216,7 @@ export function AgentPermissionMenu({ type="button" variant="outline" size={size} - aria-label={translate('settings.agents.rowPermissionsLabel', '{{agent}} permissions', { + aria-label={t('settings.agents.rowPermissionsLabel', '{{agent}} permissions', { agent: label })} className={cn('min-w-0 max-w-full', layout === 'stacked' && 'w-full justify-between')} @@ -246,10 +246,10 @@ export function AgentPermissionMenu({ {permission.terminalChoice === 'own' && permission.chatChoice ? ( - {translate( + {t( 'settings.agents.permissionSurfaceDifference', 'Terminals use the agent’s own settings; new chats use {{mode}}.', - { mode: nativeChatPermissionModeLabel(permission.chatChoice) } + { mode: nativeChatPermissionModeLabel(permission.chatChoice, t) } )} ) : null} @@ -276,6 +276,7 @@ function AgentArgsChoiceChip({ choiceKey: string source?: 'arguments' }): React.JSX.Element { + const t = useTranslate() return ( @@ -305,8 +306,8 @@ function AgentArgsChoiceChip({ }} aria-label={ source === 'arguments' || choiceKey !== 'permissionMode' - ? translate('settings.agents.choiceChipRemove', 'Remove {{flag}}', { flag }) - : translate( + ? t('settings.agents.choiceChipRemove', 'Remove {{flag}}', { flag }) + : t( 'settings.agents.permissionChipRemove', "Remove {{flag}} and use {{agent}}'s own settings", { flag, agent: label } @@ -320,19 +321,19 @@ function AgentArgsChoiceChip({ {source === 'arguments' - ? translate('agentPicker.argumentsHint', 'Applies only to CLI launches.') + ? t('agentPicker.argumentsHint', 'Applies only to CLI launches.') : choiceKey !== 'permissionMode' - ? translate( + ? t( 'settings.agents.choiceChipTooltip', 'Added by {{choice}}. Remove it to use the default.', { choice: choiceKey === 'model' - ? translate('settings.sourceControl.actionModel.model', 'Model') - : translate('settings.sourceControl.actionModel.effort', 'Effort') + ? t('settings.sourceControl.actionModel.model', 'Model') + : t('settings.sourceControl.actionModel.effort', 'Effort') } ) - : translate( + : t( 'settings.agents.permissionChipTooltip', 'Added by Permissions. Remove it to use {{agent}}’s own settings.', { agent: label } diff --git a/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceItems.tsx b/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceItems.tsx index 45ad00e7900..dc2ab01f575 100644 --- a/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceItems.tsx +++ b/src/renderer/src/components/agent/launch-settings/AgentPermissionChoiceItems.tsx @@ -1,20 +1,21 @@ import { Terminal } from 'lucide-react' import type { AgentPermissionChoice } from '../../../../../shared/agent-launch/permissions/tui-agent-permission-choice' import { translate } from '@/i18n/i18n' +import { useTranslate, type Translate } from '@/i18n/use-translate' import { DropdownMenuRadioItem, DropdownMenuSeparator } from '../../ui/dropdown-menu' import { NativeChatPermissionModeItems, NativeChatPermissionModeName } from '../../native-chat/session/options/controls/NativeChatPermissionModePicker' -function ownDescription(chatOwnSupported: boolean): string { +function ownDescription(chatOwnSupported: boolean, t: Translate = translate): string { if (!chatOwnSupported) { - return translate( + return t( 'settings.agents.permissionOwnLegacyDescription', 'Orca adds no permission flag in terminals; chats use Settings → Chat.' ) } - return translate( + return t( 'settings.agents.permissionOwnDescription', 'Terminals and chats follow the agent’s own permission settings.' ) @@ -57,6 +58,7 @@ export function AgentPermissionChoiceItems({ disabled?: boolean onSelect: (choice: AgentPermissionChoice) => void }): React.JSX.Element { + const t = useTranslate() const modes = choices.filter((choice) => choice !== 'own') return ( <> @@ -73,7 +75,7 @@ export function AgentPermissionChoiceItems({
- {ownDescription(chatOwnSupported)} + {ownDescription(chatOwnSupported, t)}
diff --git a/src/renderer/src/components/agent/launch-settings/codex-session-source-home-control.tsx b/src/renderer/src/components/agent/launch-settings/codex-session-source-home-control.tsx index a1a0463b08a..2a17dc631f9 100644 --- a/src/renderer/src/components/agent/launch-settings/codex-session-source-home-control.tsx +++ b/src/renderer/src/components/agent/launch-settings/codex-session-source-home-control.tsx @@ -5,7 +5,7 @@ import { normalizeGlobalWindowsRuntimeDefault } from '../../../../../shared/proj import { Button } from '../../ui/button' import { Input } from '../../ui/input' import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' export type AgentSessionSourceHomeControl = { runtimeLabel: string @@ -85,6 +85,7 @@ export function AgentSessionSourceHomeInput({ value, onSave }: AgentSessionSourceHomeControl): React.JSX.Element { + const t = useTranslate() const [draft, setDraft] = useState(value) const commit = (): void => { @@ -94,15 +95,12 @@ export function AgentSessionSourceHomeInput({ return (
- {translate( - 'auto.components.settings.AgentsPane.codexSessionSource', - 'Codex home to import from' - )} + {t('auto.components.settings.AgentsPane.codexSessionSource', 'Codex home to import from')} )}
diff --git a/src/renderer/src/components/feature-tips/ChatUiHereCornerCard.tsx b/src/renderer/src/components/feature-tips/ChatUiHereCornerCard.tsx index 49cc343ad8b..08e84eae8e4 100644 --- a/src/renderer/src/components/feature-tips/ChatUiHereCornerCard.tsx +++ b/src/renderer/src/components/feature-tips/ChatUiHereCornerCard.tsx @@ -4,7 +4,7 @@ import { FEATURE_TIPS } from '../../../../shared/feature-tips' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { useMountedRef } from '@/hooks/useMountedRef' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { ChatSurfacePreview } from '../agent/launch-settings/AgentDefaultSurfacePreviews' import { getChatUiHereVariantCopy } from './chat-ui-here-copy' @@ -23,6 +23,7 @@ type CornerCardState = /** The nonblocking launch card for terminal-default profiles, sharing the tip's copy and a compact chat preview. */ export function ChatUiHereCornerCard({ onClose }: { onClose: () => void }): JSX.Element | null { + const t = useTranslate() const mountedRef = useMountedRef() const settings = useAppStore((s) => s.settings) const [state, setState] = useState({ stage: 'prompt' }) @@ -51,7 +52,7 @@ export function ChatUiHereCornerCard({ onClose }: { onClose: () => void }): JSX. if (state.stage === 'switched') { return } - const { paragraphs, secondaryLabel, primaryLabel } = getChatUiHereVariantCopy('chat-ui-off') + const { paragraphs, secondaryLabel, primaryLabel } = getChatUiHereVariantCopy('chat-ui-off', t) const [intro = ''] = paragraphs const handleUseChatUi = async (event: MouseEvent): Promise => { @@ -95,13 +96,13 @@ export function ChatUiHereCornerCard({ onClose }: { onClose: () => void }): JSX.
- {translate('featureTips.chatUiHere.title', 'Chat UI is here')} + {t('featureTips.chatUiHere.title', 'Chat UI is here')} @@ -121,7 +122,7 @@ export function ChatUiHereCornerCard({ onClose }: { onClose: () => void }): JSX. {intro} Settings → Chat UI.' )} diff --git a/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx index 7934ec63269..8843b4abca0 100644 --- a/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx @@ -4,7 +4,7 @@ import { LoadingSpinner } from '@/components/LoadingSpinner' import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' import { nativeChatPermissionModeLabel } from '@/components/native-chat/session/options/controls/native-chat-permission-mode-labels' import { nativeChatToolRowVerb } from '@/components/native-chat/tools/lines/native-chat-tool-line-label' -import { translate } from '@/i18n/i18n' +import { useIntlLocale, useTranslate } from '@/i18n/use-translate' import { AgentIcon } from '@/lib/agent-catalog' import { cn } from '@/lib/utils' import { @@ -81,6 +81,7 @@ export function FlowDiagram({ } function Transcript({ copy, frame }: { copy: ChatDemoCopy; frame: ChatDemoFrame }): JSX.Element { + const t = useTranslate() return (
- {nativeChatToolRowVerb('read')} + {nativeChatToolRowVerb('read', t)} {CHAT_DEMO_FILE}
@@ -113,7 +114,7 @@ function Transcript({ copy, frame }: { copy: ChatDemoCopy; frame: ChatDemoFrame
- {translate('components.native-chat.status.working', 'Working…')} + {t('components.native-chat.status.working', 'Working…')}
@@ -153,6 +154,7 @@ function Transcript({ copy, frame }: { copy: ChatDemoCopy; frame: ChatDemoFrame } function Composer({ prompt, frame }: { prompt: string; frame: ChatDemoFrame }): JSX.Element { + const t = useTranslate() const typing = frame.typed > 0 return (
@@ -165,7 +167,7 @@ function Composer({ prompt, frame }: { prompt: string; frame: ChatDemoFrame }): ) : ( - {translate( + {t( 'components.native-chat.composer.placeholder', 'Ask anything, @ to mention files, / for commands' )} @@ -179,7 +181,7 @@ function Composer({ prompt, frame }: { prompt: string; frame: ChatDemoFrame }): - {nativeChatPermissionModeLabel('ask')} + {nativeChatPermissionModeLabel('ask', t)}
@@ -224,8 +226,10 @@ export function ChatUiDemoCard({ loop?: boolean className?: string }): JSX.Element { + const t = useTranslate() + const intlLocale = useIntlLocale() const reducedMotion = usePrefersReducedMotion() - const copy = getChatDemoCopy() + const copy = getChatDemoCopy(t, intlLocale) const promptLength = Array.from(copy.prompt).length const timeline = useMemo( () => createChatDemoTimeline(promptLength, copy.reply.length), diff --git a/src/renderer/src/components/feature-tips/ChatUiHereTipDialog.tsx b/src/renderer/src/components/feature-tips/ChatUiHereTipDialog.tsx index 4335bd7fd65..eb431588056 100644 --- a/src/renderer/src/components/feature-tips/ChatUiHereTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/ChatUiHereTipDialog.tsx @@ -1,7 +1,7 @@ import { useRef, type JSX } from 'react' import type { FeatureTip } from '../../../../shared/feature-tips' import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { ChatUiHereFeatureTipVisual } from './ChatUiHereFeatureTipVisual' import { getChatUiHereVariantCopy, type ShownChatUiLaunchTipVariant } from './chat-ui-here-copy' import { FeatureTipActions } from './FeatureTipActions' @@ -34,8 +34,9 @@ export function ChatUiHereTipDialog({ onSkip: () => void onSettingsClick: () => void }): JSX.Element { + const t = useTranslate() const primaryButtonRef = useRef(null) - const { paragraphs, secondaryLabel, primaryLabel } = getChatUiHereVariantCopy(variant) + const { paragraphs, secondaryLabel, primaryLabel } = getChatUiHereVariantCopy(variant, t) return (
- + - {translate('featureTips.chatUiHere.title', 'Chat UI is here')} + {t('featureTips.chatUiHere.title', 'Chat UI is here')} @@ -63,7 +64,7 @@ export function ChatUiHereTipDialog({ ))} Settings → Chat UI.' )} diff --git a/src/renderer/src/components/feature-tips/ChatUiSwitchedCornerNote.tsx b/src/renderer/src/components/feature-tips/ChatUiSwitchedCornerNote.tsx index b72f93504e3..fc2a0e8b483 100644 --- a/src/renderer/src/components/feature-tips/ChatUiSwitchedCornerNote.tsx +++ b/src/renderer/src/components/feature-tips/ChatUiSwitchedCornerNote.tsx @@ -3,7 +3,8 @@ import { Trans } from 'react-i18next' import { Check, X } from 'lucide-react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { i18n, translate } from '@/i18n/i18n' +import { i18n } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { openChatUiFeatureTipSettings } from './chat-ui-here-actions' const AUTO_DISMISS_MS = 12_000 @@ -15,6 +16,7 @@ export function ChatUiSwitchedCornerNote({ onClose: () => void focusSettings?: boolean }): JSX.Element { + const t = useTranslate() const settingsButtonRef = useRef(null) useLayoutEffect(() => { @@ -63,17 +65,14 @@ export function ChatUiSwitchedCornerNote({ - {translate( - 'featureTips.chatUiHere.switchedNoteTitle', - 'New agents will open in Chat UI' - )} + {t('featureTips.chatUiHere.switchedNoteTitle', 'New agents will open in Chat UI')}
@@ -84,7 +83,7 @@ export function ChatUiSwitchedCornerNote({ Chat UI in Settings.' )} @@ -93,7 +92,7 @@ export function ChatUiSwitchedCornerNote({
diff --git a/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx index d9ca76e9745..ab1dd5c599d 100644 --- a/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx @@ -5,7 +5,7 @@ import { ORCHESTRATION_CLI_COMMAND_TIMINGS_MS } from '@/components/feature-wall/agents-orchestration/orchestration-types' import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' const CLI_AGENT_COMMANDS = [ 'orca worktree create --name auth-pr-1', @@ -15,6 +15,7 @@ const CLI_AGENT_COMMANDS = [ ] export function CliFeatureTipVisual(): JSX.Element { + const t = useTranslate() const reducedMotion = usePrefersReducedMotion() const [animatedVisibleCommandCount, setAnimatedVisibleCommandCount] = useState(0) // Show the reduced-motion end state directly; only animation needs timer-backed state. @@ -63,7 +64,7 @@ export function CliFeatureTipVisual(): JSX.Element {
● - {translate( + {t( 'auto.components.feature.tips.CliFeatureTipVisual.22e62f3bab', 'Claude Code session started' )} @@ -77,7 +78,7 @@ export function CliFeatureTipVisual(): JSX.Element { className={`truncate ${isVisible ? 'animate-cli-tip-command-line' : 'invisible'}`} > - {translate('auto.components.feature.tips.CliFeatureTipVisual.badb4fc342', '>')} + {t('auto.components.feature.tips.CliFeatureTipVisual.badb4fc342', '>')} {command} {isCurrentLine ? ( diff --git a/src/renderer/src/components/feature-tips/CliSetupTipDialog.tsx b/src/renderer/src/components/feature-tips/CliSetupTipDialog.tsx index f20ee6a636a..c592d0dac18 100644 --- a/src/renderer/src/components/feature-tips/CliSetupTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/CliSetupTipDialog.tsx @@ -9,7 +9,8 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { i18n, translate } from '@/i18n/i18n' +import { i18n } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { Trans } from 'react-i18next' import { CliFeatureTipVisual } from './CliFeatureTipVisual' import { CliSkillSetupPanel } from './CliSkillSetupPanel' @@ -41,6 +42,7 @@ export function CliSetupTipDialog({ onPrimaryAction: () => void onSkip: () => void }): JSX.Element { + const t = useTranslate() const copy = getLocalizedFeatureTipCopy(tip) return ( @@ -75,15 +77,12 @@ export function CliSetupTipDialog({ }`} >

- {translate( - 'auto.components.feature.tips.FeatureTipsModal.4795ac2d4a', - 'Try asking:' - )} + {t('auto.components.feature.tips.FeatureTipsModal.4795ac2d4a', 'Try asking:')}

worktrees and create PRs for each.”' )} @@ -91,17 +90,14 @@ export function CliSetupTipDialog({ />

- {translate( + {t( 'auto.components.feature.tips.FeatureTipsModal.864e2db28f', '“When the agent in' )} - {translate( - 'auto.components.feature.tips.FeatureTipsModal.298301b7a0', - 'worktree' - )} + {t('auto.components.feature.tips.FeatureTipsModal.298301b7a0', 'worktree')} {' '} - {translate( + {t( 'auto.components.feature.tips.FeatureTipsModal.3c6c478462', 'X finishes, send it the review task.”' )} @@ -114,7 +110,7 @@ export function CliSetupTipDialog({ {skillTerminalOpen ? ( ) : ( void handleCopySkillCommand()} - aria-label={translate( + aria-label={t( 'auto.components.feature.tips.CliSkillSetupTerminal.5eca672aac', 'Copy skill install command' )} @@ -67,25 +68,19 @@ export function CliSkillSetupTerminal(): React.JSX.Element { - {translate( - 'auto.components.feature.tips.CliSkillSetupTerminal.5c3aee22c0', - 'Copy command' - )} + {t('auto.components.feature.tips.CliSkillSetupTerminal.5c3aee22c0', 'Copy command')}

- {translate( + {t( 'auto.components.feature.tips.CmdJPaletteFeatureTipVisual.ab94e16d44', 'Create worktree "{{value0}}"', { value0: renderQuery.trim() } diff --git a/src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.tsx b/src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.tsx index 5f6c16d912c..23ad8b44137 100644 --- a/src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.tsx @@ -10,7 +10,7 @@ import { FeatureTipEyebrow, FeatureTipSettingsLine } from './FeatureTipDialogFrame' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' export function CmdJPaletteTipDialog({ open, @@ -29,6 +29,7 @@ export function CmdJPaletteTipDialog({ onSkip: () => void onRebindClick: () => void }): JSX.Element { + const t = useTranslate() // Use the live binding for rebindings and platform-specific shortcuts. const worktreePaletteShortcutLabel = useShortcutLabel('worktree.palette') const displayShortcutLabel = @@ -69,7 +70,7 @@ export function CmdJPaletteTipDialog({ {copy.description} Settings → Shortcuts.' )} diff --git a/src/renderer/src/components/feature-tips/FeatureTipActions.tsx b/src/renderer/src/components/feature-tips/FeatureTipActions.tsx index 2d6c4a97cda..70fd4fb4430 100644 --- a/src/renderer/src/components/feature-tips/FeatureTipActions.tsx +++ b/src/renderer/src/components/feature-tips/FeatureTipActions.tsx @@ -3,16 +3,17 @@ import { Loader2 } from 'lucide-react' import type { FeatureTip, FeatureTipAction } from '../../../../shared/feature-tips' import { Button } from '@/components/ui/button' import { translate } from '@/i18n/i18n' +import { useTranslate, type Translate } from '@/i18n/use-translate' import { getLocalizedFeatureTipCopy } from './feature-tip-localized-copy' -function getPrimaryBusyLabel(action: FeatureTipAction): string { +function getPrimaryBusyLabel(action: FeatureTipAction, t: Translate = translate): string { if (action === 'setup-cli') { - return translate('auto.components.feature.tips.FeatureTipActions.installing', 'Installing...') + return t('auto.components.feature.tips.FeatureTipActions.installing', 'Installing...') } if (action === 'enable-session-search') { - return translate('auto.components.feature.tips.FeatureTipActions.turningOn', 'Turning on...') + return t('auto.components.feature.tips.FeatureTipActions.turningOn', 'Turning on...') } - return translate('auto.components.feature.tips.FeatureTipActions.working', 'Working...') + return t('auto.components.feature.tips.FeatureTipActions.working', 'Working...') } export function FeatureTipActions({ @@ -41,6 +42,7 @@ export function FeatureTipActions({ /** Overrides the secondary button's default "Maybe Later". */ secondaryLabel?: string }): JSX.Element { + const t = useTranslate() const skipButton = showSkip ? ( ) : null @@ -67,7 +69,7 @@ export function FeatureTipActions({ {primaryBusy ? ( <> - {getPrimaryBusyLabel(currentTip.action)} + {getPrimaryBusyLabel(currentTip.action, t)} ) : ( (label ?? getLocalizedFeatureTipCopy(currentTip).ctaLabel) diff --git a/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx b/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx index 4fd50fd06d5..297cde673a2 100644 --- a/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx +++ b/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx @@ -25,12 +25,13 @@ import { } from './feature-tip-telemetry' import { useMountedRef } from '@/hooks/useMountedRef' import { isWebClientLocation } from '@/lib/web-client-location' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { SessionSearchTipDialog } from './SessionSearchTipDialog' import { useSessionSearchTipSetup } from './use-session-search-tip-setup' import { VoiceDictationTipDialog } from './VoiceDictationTipDialog' export default function FeatureTipsModal(): JSX.Element | null { + const t = useTranslate() const activeModal = useAppStore((s) => s.activeModal) const closeModal = useAppStore((s) => s.closeModal) const openSettingsPage = useAppStore((s) => s.openSettingsPage) @@ -213,7 +214,7 @@ export default function FeatureTipsModal(): JSX.Element | null { } enableOrchestrationSkillSetup() toast.success( - translate( + t( 'auto.components.feature.tips.FeatureTipsModal.ce13a742d0', 'Registered `orca` in PATH.' ) @@ -227,14 +228,14 @@ export default function FeatureTipsModal(): JSX.Element | null { return } toast.warning( - translate( + t( 'auto.components.feature.tips.FeatureTipsModal.1da82af45b', 'Orca CLI needs attention' ), { description: result.status.detail ?? - translate( + t( 'auto.components.feature.tips.FeatureTipsModal.d1a86c7eb5', 'Open Settings to finish CLI setup.' ) @@ -246,7 +247,7 @@ export default function FeatureTipsModal(): JSX.Element | null { const message = error instanceof Error ? error.message - : translate( + : t( 'auto.components.feature.tips.FeatureTipsModal.cliInstallFailed', 'Failed to install Orca CLI.' ) @@ -260,7 +261,7 @@ export default function FeatureTipsModal(): JSX.Element | null { } enableOrchestrationSkillSetup() toast.info( - translate( + t( 'auto.components.feature.tips.FeatureTipsModal.53905bd076', 'Development preview: opening skills setup terminal.' ) diff --git a/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx index 7226af29c96..3eaf6d6db2c 100644 --- a/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx @@ -5,6 +5,7 @@ import { getSessionSearchDemoRecentRows, getSessionSearchDemoScenes } from './session-search-feature-tip-demo' +import { useTranslate } from '@/i18n/use-translate' type DemoPhase = 'idle' | 'typing' | 'searching' | 'results' | 'focused' | 'erasing' type DemoState = { scene: number; phase: DemoPhase; chars: number } @@ -40,9 +41,10 @@ function nextDemoState(state: DemoState, queryLength: number, sceneCount: number } export function SessionSearchFeatureTipVisual(): JSX.Element { + const t = useTranslate() const reducedMotion = usePrefersReducedMotion() - const scenes = getSessionSearchDemoScenes() - const recentRows = getSessionSearchDemoRecentRows() + const scenes = getSessionSearchDemoScenes(t) + const recentRows = getSessionSearchDemoRecentRows(t) const [state, setState] = useState({ scene: 0, phase: 'idle', chars: 0 }) const shown: DemoState = reducedMotion ? { scene: 0, phase: 'results', chars: scenes[0].query.length } diff --git a/src/renderer/src/components/feature-tips/SessionSearchTipDialog.tsx b/src/renderer/src/components/feature-tips/SessionSearchTipDialog.tsx index fc7aff44392..fac94e4fb0e 100644 --- a/src/renderer/src/components/feature-tips/SessionSearchTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/SessionSearchTipDialog.tsx @@ -7,6 +7,7 @@ import { } from '@/components/settings/session-history/session-history-status-copy' import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { translate } from '@/i18n/i18n' +import { useIntlLocale, useTranslate, type Translate } from '@/i18n/use-translate' import { FeatureTipActions } from './FeatureTipActions' import { getLocalizedFeatureTipCopy } from './feature-tip-localized-copy' import { @@ -19,7 +20,8 @@ import type { SessionSearchTipStage } from './use-session-search-tip-setup' function stageCopy( tip: FeatureTip, - stage: SessionSearchTipStage + stage: SessionSearchTipStage, + t: Translate = translate ): { title: string; description: string; cta: string } { switch (stage) { case 'offer': { @@ -28,31 +30,35 @@ function stageCopy( } case 'indexing': return { - title: translate('featureTips.sessionSearch.indexingTitle', 'Indexing your agent sessions'), - description: translate( + title: t('featureTips.sessionSearch.indexingTitle', 'Indexing your agent sessions'), + description: t( 'featureTips.sessionSearch.indexingDescription', 'Orca is reading your past agent transcripts so you can search them. This can take a few minutes.' ), - cta: translate('featureTips.sessionSearch.continueInBackground', 'Continue in background') + cta: t('featureTips.sessionSearch.continueInBackground', 'Continue in background') } case 'ready': return { - title: translate('featureTips.sessionSearch.readyTitle', 'Session search is ready'), - description: translate( + title: t('featureTips.sessionSearch.readyTitle', 'Session search is ready'), + description: t( 'featureTips.sessionSearch.readyDescription', 'Every agent session on this computer can now be found by what was said in it.' ), - cta: translate('featureTips.sessionSearch.startSearching', 'Start searching') + cta: t('featureTips.sessionSearch.startSearching', 'Start searching') } } } -function progressMessage(status: AiVaultSearchStatus | null): string { +function progressMessage( + status: AiVaultSearchStatus | null, + t: Translate = translate, + intlLocale?: string +): string { // Why: right after turning on, the indexer may not have started; the shared copy would call that unavailable. if (!status?.enabled || status.phase === 'idle' || status.phase === 'closed') { - return translate('featureTips.sessionSearch.progressStarting', 'Finding your sessions…') + return t('featureTips.sessionSearch.progressStarting', 'Finding your sessions…') } - return sessionSearchStatusMessage(status) + return sessionSearchStatusMessage(status, t, intlLocale) } function IndexProgress({ @@ -62,6 +68,8 @@ function IndexProgress({ indexing: boolean status: AiVaultSearchStatus | null }): JSX.Element { + const t = useTranslate() + const intlLocale = useIntlLocale() return ( {indexing ? ( @@ -71,9 +79,9 @@ function IndexProgress({ ) : null} - {progressMessage(status)} + {progressMessage(status, t, intlLocale)} - {sessionSearchStatusDetails(status).map((line) => ( + {sessionSearchStatusDetails(status, t).map((line) => ( {line} @@ -101,8 +109,9 @@ export function SessionSearchTipDialog({ stage: SessionSearchTipStage status: AiVaultSearchStatus | null }): JSX.Element { + const t = useTranslate() const primaryButtonRef = useRef(null) - const copy = stageCopy(tip, stage) + const copy = stageCopy(tip, stage, t) return ( {copy.description} {stage === 'offer' ? ( - {translate( + {t( 'featureTips.sessionSearch.agentsInstruction', 'Your agents can search it too. Ask one to “find the session where we fixed the login timeout.”' )} @@ -133,7 +142,7 @@ export function SessionSearchTipDialog({ )} Settings → Agent Session Search.' )} diff --git a/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx index ff94ed96fd3..42d19c42f42 100644 --- a/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx @@ -3,16 +3,17 @@ import { Mic, Square } from 'lucide-react' import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' import { useShortcutKeyDetails } from '@/hooks/useShortcutLabel' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' const TYPE_START_DELAY_MS = 650 const TYPE_INTERVAL_MS = 36 const WAVEFORM_BAR_HEIGHTS = [38, 70, 100, 58, 82] export function VoiceDictationFeatureTipVisual(): JSX.Element { + const t = useTranslate() const reducedMotion = usePrefersReducedMotion() const shortcut = useShortcutKeyDetails('voice.dictation') - const dictatedPrompt = translate( + const dictatedPrompt = t( 'featureTips.voice.demoPrompt', 'Review this diff for edge cases and add tests for anything you find.' ) @@ -61,7 +62,7 @@ export function VoiceDictationFeatureTipVisual(): JSX.Element { keyCapClassName="h-7 min-w-7 bg-foreground/[0.08] px-2 font-semibold shadow-xs" /> - {translate('featureTips.voice.startDictation', 'Start dictation')} + {t('featureTips.voice.startDictation', 'Start dictation')}
) : null} @@ -69,7 +70,7 @@ export function VoiceDictationFeatureTipVisual(): JSX.Element {
- {translate('featureTips.voice.agentPromptTitle', 'Agent prompt')} + {t('featureTips.voice.agentPromptTitle', 'Agent prompt')}
@@ -99,7 +100,7 @@ export function VoiceDictationFeatureTipVisual(): JSX.Element { ))} - {translate('featureTips.voice.listening', 'Listening...')} + {t('featureTips.voice.listening', 'Listening...')}
diff --git a/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx b/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx index fd5b041fa7a..5e323039b29 100644 --- a/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx @@ -3,7 +3,8 @@ import type { FeatureTip } from '../../../../shared/feature-tips' import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { useShortcutKeyDetails } from '@/hooks/useShortcutLabel' -import { i18n, translate } from '@/i18n/i18n' +import { i18n } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { Trans } from 'react-i18next' import { FeatureTipActions } from './FeatureTipActions' import { getLocalizedFeatureTipCopy } from './feature-tip-localized-copy' @@ -31,6 +32,7 @@ export function VoiceDictationTipDialog({ onSkip: () => void onVoiceSettingsClick: () => void }): JSX.Element { + const t = useTranslate() const shortcut = useShortcutKeyDetails('voice.dictation') const primaryButtonRef = useRef(null) const copy = getLocalizedFeatureTipCopy(tip) @@ -56,7 +58,7 @@ export function VoiceDictationTipDialog({ to start voice dictation. Press again to stop.' )} @@ -74,14 +76,14 @@ export function VoiceDictationTipDialog({ ) : ( - {translate( + {t( 'featureTips.voice.unassignedInstruction', 'Assign a dictation shortcut before starting voice dictation in a focused pane.' )} )} Settings → Voice.' )} diff --git a/src/renderer/src/components/feature-tips/chat-ui-here-copy.ts b/src/renderer/src/components/feature-tips/chat-ui-here-copy.ts index 16254bafae7..19584acc79e 100644 --- a/src/renderer/src/components/feature-tips/chat-ui-here-copy.ts +++ b/src/renderer/src/components/feature-tips/chat-ui-here-copy.ts @@ -1,9 +1,13 @@ import type { ChatUiLaunchTipVariant } from '../../../../shared/native-chat/chat-ui-launch-tip-audience' import { translate } from '@/i18n/i18n' +import type { Translate } from '@/i18n/use-translate' export type ShownChatUiLaunchTipVariant = Exclude -export function getChatUiHereVariantCopy(variant: ShownChatUiLaunchTipVariant): { +export function getChatUiHereVariantCopy( + variant: ShownChatUiLaunchTipVariant, + t: Translate = translate +): { paragraphs: string[] secondaryLabel: string | null primaryLabel: string @@ -11,31 +15,31 @@ export function getChatUiHereVariantCopy(variant: ShownChatUiLaunchTipVariant): if (variant === 'chat-ui-off') { return { paragraphs: [ - translate( + t( 'featureTips.chatUiHere.offIntro', 'Agents can now open as a chat, with rich messages, visuals and images.' ), - translate( + t( 'featureTips.chatUiHere.offTerminal', 'Prefer the terminal? It stays fully supported. Your agents keep opening there until you choose Chat.' ) ], - secondaryLabel: translate('featureTips.chatUiHere.stayInTerminal', 'Stay in terminal'), - primaryLabel: translate('featureTips.chatUiHere.cta', 'Use Chat UI') + secondaryLabel: t('featureTips.chatUiHere.stayInTerminal', 'Stay in terminal'), + primaryLabel: t('featureTips.chatUiHere.cta', 'Use Chat UI') } } return { paragraphs: [ - translate( + t( 'featureTips.chatUiHere.onIntro', 'Chat UI is out of Experimental and now part of Orca. Your agents keep opening as chats, with rich messages, visuals and images.' ), - translate( + t( 'featureTips.chatUiHere.onTerminal', 'The terminal stays fully supported too. Switch any agent back anytime.' ) ], - secondaryLabel: translate('featureTips.chatUiHere.switchToTerminal', 'Switch to terminal'), - primaryLabel: translate('featureTips.gotIt', 'Got it') + secondaryLabel: t('featureTips.chatUiHere.switchToTerminal', 'Switch to terminal'), + primaryLabel: t('featureTips.gotIt', 'Got it') } } diff --git a/src/renderer/src/components/feature-tips/chat-ui-here-feature-tip-demo.ts b/src/renderer/src/components/feature-tips/chat-ui-here-feature-tip-demo.ts index 5cef9b0ef2b..c8acae4553d 100644 --- a/src/renderer/src/components/feature-tips/chat-ui-here-feature-tip-demo.ts +++ b/src/renderer/src/components/feature-tips/chat-ui-here-feature-tip-demo.ts @@ -1,4 +1,5 @@ import { getIntlLocale, translate } from '@/i18n/i18n' +import type { Translate } from '@/i18n/use-translate' export const CHAT_DEMO_FILE = 'auth.ts' export const CHAT_DEMO_MODEL = 'Opus' @@ -15,11 +16,12 @@ export type ChatDemoCopy = { flow: { name: string; arrow: string | null }[] } -export function chatDemoRevealTokens(text: string): ChatDemoToken[] { +export function chatDemoRevealTokens( + text: string, + intlLocale: string = getIntlLocale() +): ChatDemoToken[] { const tokens: ChatDemoToken[] = [] - for (const { segment } of new Intl.Segmenter(getIntlLocale(), { granularity: 'word' }).segment( - text - )) { + for (const { segment } of new Intl.Segmenter(intlLocale, { granularity: 'word' }).segment(text)) { const last = tokens.at(-1) if (last && !last.link && segment.trim() === '') { last.text += segment @@ -30,32 +32,35 @@ export function chatDemoRevealTokens(text: string): ChatDemoToken[] { return tokens } -export function getChatDemoCopy(): ChatDemoCopy { - const [before, ...after] = translate( +export function getChatDemoCopy(t: Translate = translate, intlLocale?: string): ChatDemoCopy { + const [before, ...after] = t( 'featureTips.chatUiHere.demoReply', 'Login starts in {{file}}: the browser posts to /login, the server checks the password and creates a session, then redirects you to the app. Here’s the flow:', { file: FILE_SLOT } ).split(FILE_SLOT) return { - tabTitle: translate('featureTips.chatUiHere.demoTabTitle', 'Explain login'), - prompt: translate('featureTips.chatUiHere.demoPrompt', 'Map out how login works'), + tabTitle: t('featureTips.chatUiHere.demoTabTitle', 'Explain login'), + prompt: t('featureTips.chatUiHere.demoPrompt', 'Map out how login works'), reply: [ - ...chatDemoRevealTokens(before), + ...chatDemoRevealTokens(before, intlLocale), ...(after.length > 0 - ? [{ text: CHAT_DEMO_FILE, link: true as const }, ...chatDemoRevealTokens(after.join(''))] + ? [ + { text: CHAT_DEMO_FILE, link: true as const }, + ...chatDemoRevealTokens(after.join(''), intlLocale) + ] : []) ], flow: [ { - name: translate('featureTips.chatUiHere.demoBrowser', 'Browser'), - arrow: translate('featureTips.chatUiHere.demoPassword', 'password') + name: t('featureTips.chatUiHere.demoBrowser', 'Browser'), + arrow: t('featureTips.chatUiHere.demoPassword', 'password') }, { name: '/login', arrow: '' }, { - name: translate('featureTips.chatUiHere.demoSession', 'Session'), - arrow: translate('featureTips.chatUiHere.demoCookie', 'cookie') + name: t('featureTips.chatUiHere.demoSession', 'Session'), + arrow: t('featureTips.chatUiHere.demoCookie', 'cookie') }, - { name: translate('featureTips.chatUiHere.demoApp', 'App'), arrow: null } + { name: t('featureTips.chatUiHere.demoApp', 'App'), arrow: null } ] } } diff --git a/src/renderer/src/components/feature-tips/feature-tip-session-panel-demo.tsx b/src/renderer/src/components/feature-tips/feature-tip-session-panel-demo.tsx index c7dcd3a7cd2..31241c22cd8 100644 --- a/src/renderer/src/components/feature-tips/feature-tip-session-panel-demo.tsx +++ b/src/renderer/src/components/feature-tips/feature-tip-session-panel-demo.tsx @@ -17,7 +17,7 @@ import { DEFAULT_AI_VAULT_HIDE_EMPTY_SESSIONS } from '@/components/right-sidebar/ai-vault/scope/ai-vault-view-defaults' import { AgentIcon } from '@/lib/agent-catalog' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import type { SessionSearchDemoRow } from './session-search-feature-tip-demo' const ROW_STAGGER_MS = 90 @@ -136,6 +136,7 @@ export function DemoSessionRow({ index: number children?: ReactNode }): JSX.Element { + const t = useTranslate() return (
{AI_VAULT_AGENT_LABELS[row.agent]} - {translate( - 'auto.components.right.sidebar.AiVaultSessionRow.messageCount', - '{{value0}} msgs', - { value0: row.messages } - )} + {t('auto.components.right.sidebar.AiVaultSessionRow.messageCount', '{{value0}} msgs', { + value0: row.messages + })} · {row.age} diff --git a/src/renderer/src/components/feature-tips/session-search-feature-tip-demo.ts b/src/renderer/src/components/feature-tips/session-search-feature-tip-demo.ts index 123c917e15f..95d29e51cb6 100644 --- a/src/renderer/src/components/feature-tips/session-search-feature-tip-demo.ts +++ b/src/renderer/src/components/feature-tips/session-search-feature-tip-demo.ts @@ -1,4 +1,5 @@ import { translate } from '@/i18n/i18n' +import type { Translate } from '@/i18n/use-translate' export type SessionSearchDemoRow = { agent: 'claude' | 'codex' @@ -12,131 +13,128 @@ export type SessionSearchDemoRow = { export type SessionSearchDemoScene = { query: string; hits: SessionSearchDemoRow[] } -function assistantRole(): string { - return translate('featureTips.sessionSearch.demoRoleAssistant', 'Assistant') +function assistantRole(t: Translate = translate): string { + return t('featureTips.sessionSearch.demoRoleAssistant', 'Assistant') } -function userRole(): string { - return translate('featureTips.sessionSearch.demoRoleUser', 'User') +function userRole(t: Translate = translate): string { + return t('featureTips.sessionSearch.demoRoleUser', 'User') } /** What the panel lists before anything is typed. */ -export function getSessionSearchDemoRecentRows(): SessionSearchDemoRow[] { +export function getSessionSearchDemoRecentRows(t: Translate = translate): SessionSearchDemoRow[] { return [ { agent: 'claude', - title: translate('featureTips.sessionSearch.recent1Title', 'Tidy the settings sidebar'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.recent1Title', 'Tidy the settings sidebar'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.recent1Text', 'Moved the search entries under Workflows and updated the tests.' ), messages: 42, - age: translate('featureTips.sessionSearch.ageMinutes', '12m') + age: t('featureTips.sessionSearch.ageMinutes', '12m') }, { agent: 'codex', - title: translate('featureTips.sessionSearch.recent2Title', 'Bump the lockfile'), - role: userRole(), - text: translate( + title: t('featureTips.sessionSearch.recent2Title', 'Bump the lockfile'), + role: userRole(t), + text: t( 'featureTips.sessionSearch.recent2Text', 'Update the dependencies and make sure the build still passes.' ), messages: 18, - age: translate('featureTips.sessionSearch.ageHours', '2h') + age: t('featureTips.sessionSearch.ageHours', '2h') }, { agent: 'claude', - title: translate('featureTips.sessionSearch.recent3Title', 'Release notes draft'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.recent3Title', 'Release notes draft'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.recent3Text', 'Here is a first pass at the notes, grouped by area.' ), messages: 9, - age: translate('featureTips.sessionSearch.ageYesterday', '1d') + age: t('featureTips.sessionSearch.ageYesterday', '1d') } ] } -export function getSessionSearchDemoScenes(): SessionSearchDemoScene[] { +export function getSessionSearchDemoScenes(t: Translate = translate): SessionSearchDemoScene[] { return [ { - query: translate('featureTips.sessionSearch.demoQuery', 'login timeout'), + query: t('featureTips.sessionSearch.demoQuery', 'login timeout'), hits: [ { agent: 'claude', - title: translate('featureTips.sessionSearch.demoHit1Title', 'Fix flaky auth redirect'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit1Title', 'Fix flaky auth redirect'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.demoHit1Snippet', 'Raised the [[login timeout]] to 30s and added a retry on the token refresh.' ), messages: 64, - age: translate('featureTips.sessionSearch.demoHit1Age', '3d') + age: t('featureTips.sessionSearch.demoHit1Age', '3d') }, { agent: 'codex', - title: translate( - 'featureTips.sessionSearch.demoHit2Title', - 'Session refresh after sleep' - ), - role: userRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit2Title', 'Session refresh after sleep'), + role: userRole(t), + text: t( 'featureTips.sessionSearch.demoHit2Snippet', 'Why does the [[login]] page hang after a [[timeout]] on wake?' ), messages: 27, - age: translate('featureTips.sessionSearch.demoHit2Age', '1w') + age: t('featureTips.sessionSearch.demoHit2Age', '1w') }, { agent: 'claude', - title: translate('featureTips.sessionSearch.demoHit3Title', 'Auth e2e hardening'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit3Title', 'Auth e2e hardening'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.demoHit3Snippet', 'Added a test that covers the [[login timeout]] path end to end.' ), messages: 31, - age: translate('featureTips.sessionSearch.demoHit3Age', '2w') + age: t('featureTips.sessionSearch.demoHit3Age', '2w') } ] }, { - query: translate('featureTips.sessionSearch.demoQuery2', 'migration rollback'), + query: t('featureTips.sessionSearch.demoQuery2', 'migration rollback'), hits: [ { agent: 'codex', - title: translate('featureTips.sessionSearch.demoHit4Title', 'Orders table migration'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit4Title', 'Orders table migration'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.demoHit4Snippet', 'The [[rollback]] drops the new index first, then restores the old column.' ), messages: 53, - age: translate('featureTips.sessionSearch.demoHit4Age', '5d') + age: t('featureTips.sessionSearch.demoHit4Age', '5d') }, { agent: 'claude', - title: translate('featureTips.sessionSearch.demoHit5Title', 'Staging deploy failed'), - role: userRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit5Title', 'Staging deploy failed'), + role: userRole(t), + text: t( 'featureTips.sessionSearch.demoHit5Snippet', 'Can you write a [[migration]] [[rollback]] plan before we retry?' ), messages: 22, - age: translate('featureTips.sessionSearch.demoHit5Age', '3w') + age: t('featureTips.sessionSearch.demoHit5Age', '3w') }, { agent: 'claude', - title: translate('featureTips.sessionSearch.demoHit6Title', 'Schema review notes'), - role: assistantRole(), - text: translate( + title: t('featureTips.sessionSearch.demoHit6Title', 'Schema review notes'), + role: assistantRole(t), + text: t( 'featureTips.sessionSearch.demoHit6Snippet', 'Every [[migration]] here is reversible except the enum rename.' ), messages: 14, - age: translate('featureTips.sessionSearch.demoHit6Age', '1mo') + age: t('featureTips.sessionSearch.demoHit6Age', '1mo') } ] } diff --git a/src/renderer/src/components/feature-tips/use-session-search-tip-setup.ts b/src/renderer/src/components/feature-tips/use-session-search-tip-setup.ts index 34ea5101117..e23a6e608cb 100644 --- a/src/renderer/src/components/feature-tips/use-session-search-tip-setup.ts +++ b/src/renderer/src/components/feature-tips/use-session-search-tip-setup.ts @@ -9,6 +9,7 @@ import { } from '@/components/settings/session-history/session-history-status-copy' import { useSessionSearchStatus } from '@/components/settings/session-history/use-session-search-status' import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' export type SessionSearchTipStage = 'offer' | 'indexing' | 'ready' @@ -26,6 +27,7 @@ export function useSessionSearchTipSetup({ }: { dialogOpen: boolean }): SessionSearchTipSetup { + const t = useTranslate() const showAiVaultSearch = useAppStore((s) => s.showAiVaultSearch) const searchEnabled = useAppStore((s) => resolveAiVaultSearchSettings(s.settings).enabled) const [wasOpen, setWasOpen] = useState(dialogOpen) @@ -79,7 +81,7 @@ export function useSessionSearchTipSetup({ }) } catch { toast.error( - translate( + t( 'featureTips.sessionSearch.enableFailed', 'Could not turn on session search. Try again.' ) diff --git a/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx b/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx index 8ff66c1d34e..a5229577277 100644 --- a/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx +++ b/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx @@ -5,7 +5,7 @@ import { useRepoMap } from '@/store/repos/use-repository-catalog' import { useWorktreesForRepo } from '@/store/slices/worktrees/listing/use-worktree-catalog' import { getRepoExecutionHostId } from '../../../../shared/execution-host' import { toast } from 'sonner' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' type ComposerBaseRefPickerProps = { repoId: string @@ -23,6 +23,7 @@ export function ComposerBaseRefPicker({ resetHint, readOnly = false }: ComposerBaseRefPickerProps): React.JSX.Element { + const t = useTranslate() const repoMap = useRepoMap() const repo = repoMap.get(repoId) const repoWorktrees = useWorktreesForRepo(repoId) @@ -50,7 +51,7 @@ export function ComposerBaseRefPicker({ ) if (!updated) { toast.error( - translate( + t( 'auto.components.NewWorkspaceComposerCard.defaultBranchUpdateFailed', 'Could not update the project default branch. Try again.' ) diff --git a/src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx b/src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx index 0b162eeaee6..abe3afbadf7 100644 --- a/src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx +++ b/src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx @@ -3,7 +3,7 @@ import { ArrowRight } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/utils' type Props = { @@ -25,16 +25,17 @@ export function ComposerBranchChoice({ onOpenSelectedBranchWorkspace, disabled }: Props) { + const t = useTranslate() const id = useId() const branchNameInputRef = useRef(null) const showOpenWorkspace = reuseSelectedBranch && Boolean(onOpenSelectedBranchWorkspace) return (
- {translate('workspace.branchChoice.title', 'Branch for this worktree')} + {t('workspace.branchChoice.title', 'Branch for this worktree')} onReuseSelectedBranchChange(value === 'existing')} disabled={disabled} @@ -59,7 +60,7 @@ export function ComposerBranchChoice({ - {translate('workspace.branchChoice.existing', 'Existing branch')} + {t('workspace.branchChoice.existing', 'Existing branch')} {existingBranchName ? ( - {translate('workspace.branchChoice.existingHint', 'New commits go on this branch.')} + {t('workspace.branchChoice.existingHint', 'New commits go on this branch.')} @@ -86,7 +87,7 @@ export function ComposerBranchChoice({ size="xs" onClick={onOpenSelectedBranchWorkspace} > - {translate( + {t( 'auto.components.NewWorkspaceComposerCard.openExistingWorkspace', 'Open existing workspace' )} @@ -111,17 +112,17 @@ export function ComposerBranchChoice({ /> - {translate('workspace.branchChoice.new', 'New branch')} + {t('workspace.branchChoice.new', 'New branch')} - {translate('workspace.branchChoice.newHint', 'Create a new local branch')} + {t('workspace.branchChoice.newHint', 'Create a new local branch')} {!reuseSelectedBranch ? (
onBranchNameOverrideChange(event.target.value)} - placeholder={translate('workspace.branchChoice.automaticName', 'Automatic name')} + placeholder={t('workspace.branchChoice.automaticName', 'Automatic name')} autoCapitalize="none" spellCheck={false} /> diff --git a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx b/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx index d047ac082b8..dd7ef6d6471 100644 --- a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx +++ b/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button' import { Command, CommandInput, CommandList } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { cn } from '@/lib/utils' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { getIndexedAllWorktrees, @@ -87,6 +87,7 @@ function ComposerParentWorktreePickerImpl({ disabled = false, activeFolderWorkspaceId = null }: ComposerParentWorktreePickerProps): React.JSX.Element { + const t = useTranslate() // Subscribe so translate() refreshes on language changes. useTranslation() const [open, setOpen] = useState(false) @@ -122,7 +123,7 @@ function ComposerParentWorktreePickerImpl({ return (
- {translate('auto.components.ComposerParentWorktreePicker.label', 'Parent worktree')} + {t('auto.components.ComposerParentWorktreePicker.label', 'Parent worktree')} @@ -137,7 +138,7 @@ function ComposerParentWorktreePickerImpl({ > {parentName ?? - translate('auto.components.ComposerParentWorktreePicker.noParent', 'No parent')} + t('auto.components.ComposerParentWorktreePicker.noParent', 'No parent')} @@ -168,7 +169,7 @@ function ComposerParentWorktreePickerImpl({

- {translate( + {t( 'auto.components.ComposerParentWorktreePicker.description', 'Nests this workspace under another in the sidebar. Does not change the base branch.' )} @@ -217,6 +218,7 @@ function ParentWorktreeCandidateList({ activeFolderWorkspaceId, onSelect }: ParentWorktreeCandidateListProps): React.JSX.Element { + const t = useTranslate() const worktreesByRepo = useAppStore((s) => selectWorktreesByRepo(s)) const repoMap = useRepoMap() const worktreeLineageById = useAppStore((s) => s.worktreeLineageById) @@ -357,7 +359,7 @@ function ParentWorktreeCandidateList({ onValueChange={handleSearchChange} onKeyDown={handleKeyDown} wrapperClassName="shrink-0" - placeholder={translate( + placeholder={t( 'auto.components.ComposerParentWorktreePicker.searchPlaceholder', 'Search workspaces...' )} @@ -413,10 +415,7 @@ function ParentWorktreeCandidateList({

) : ( - {translate( - 'auto.components.ComposerParentWorktreePicker.noParent', - 'No parent' - )} + {t('auto.components.ComposerParentWorktreePicker.noParent', 'No parent')} )}
@@ -425,7 +424,7 @@ function ParentWorktreeCandidateList({
{filtered.length === 0 && search.trim().length > 0 ? (
- {translate('auto.components.ComposerParentWorktreePicker.noMatches', 'No matches.')} + {t('auto.components.ComposerParentWorktreePicker.noMatches', 'No matches.')}
) : null} diff --git a/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx b/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx index 896197b1148..bbc3979c7c6 100644 --- a/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx +++ b/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx @@ -8,7 +8,7 @@ import { DialogFooter } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' -import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import type { DirectWorkItemSetupRequest } from '@/lib/workspace/creation/work-item/direct-work-item-setup' import { WorkspaceSetupSection } from './WorkspaceSetupSection' import { selectWorkspaceSetupCopy } from './workspace-setup-section-model' @@ -19,6 +19,7 @@ export function DirectWorkItemSetupDialog({ }: { request: DirectWorkItemSetupRequest }): React.JSX.Element { + const t = useTranslate() const [inspection, setInspection] = useState(request.inspection) const [checking, setChecking] = useState(false) const retry = async (): Promise => { @@ -49,14 +50,14 @@ export function DirectWorkItemSetupDialog({ - {translate('components.newWorkspace.directSetup.run', 'Run setup')} · {request.repoName} + {t('components.newWorkspace.directSetup.run', 'Run setup')} · {request.repoName} void retry() } @@ -79,7 +80,7 @@ export function DirectWorkItemSetupDialog({ /> diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx index 910d3385b9f..ecbc518222c 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx +++ b/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx @@ -15,6 +15,7 @@ import { shouldHandleTextControlPaste } from '@/lib/text-control-paste' import { translate } from '@/i18n/i18n' +import { useTranslate } from '@/i18n/use-translate' import { ComposerParentWorktreePicker } from './ComposerParentWorktreePicker' import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props' @@ -95,6 +96,7 @@ export function NewWorkspaceComposerAdvancedSection({ onSparseSelectPreset, canUseSparseCheckout }: NewWorkspaceComposerAdvancedSectionProps): React.JSX.Element { + const t = useTranslate() const handleNotePaste = React.useCallback((event: React.ClipboardEvent) => { const text = event.clipboardData.getData('text/plain') const byteLengthMeasurement = measureTextControlPasteByteLength(text, { @@ -150,13 +152,13 @@ export function NewWorkspaceComposerAdvancedSection({ {smartNameSelection ? (
onNameValueChange(event.target.value)} - placeholder={translate( + placeholder={t( 'auto.components.NewWorkspaceComposerCard.0ee17638fe', 'Workspace name' )} @@ -174,14 +176,14 @@ export function NewWorkspaceComposerAdvancedSection({ htmlFor={branchNameInputId} className="text-xs font-medium text-muted-foreground" > - {translate('auto.components.NewWorkspaceComposerCard.branchName', 'Branch name')} + {t('auto.components.NewWorkspaceComposerCard.branchName', 'Branch name')} onBranchNameOverrideChange(event.target.value)} - placeholder={translate( + placeholder={t( 'auto.components.NewWorkspaceComposerCard.branchNamePlaceholder', 'feature/my-branch' )} @@ -204,16 +206,13 @@ export function NewWorkspaceComposerAdvancedSection({