From 0d97653e3928f511cc28cb7a446c1985126e11fd Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:24:24 -0700 Subject: [PATCH] fix(ui): restore missing spaces in i18n JSX fragment copy (#9513) --- .../src/components/GitHubItemDialog.tsx | 5 +- .../src/components/GitLabItemDialog.tsx | 4 +- .../src/components/PullRequestPage.tsx | 5 +- src/renderer/src/components/TaskPage.tsx | 2 +- src/renderer/src/components/UpdateCard.tsx | 3 +- .../automations/AutomationsPage.tsx | 2 +- .../ExternalAutomationRunTable.tsx | 2 +- .../ContextualTourProgressDots.tsx | 2 +- .../components/editor/CombinedDiffViewer.tsx | 2 +- .../components/editor/ConflictComponents.tsx | 6 +- .../src/components/editor/IpynbViewer.tsx | 2 +- .../src/components/editor/MermaidBlock.tsx | 3 +- .../editor/RichMarkdownDocLinkMenu.tsx | 4 +- .../ReviewNotesAnimatedVisual.tsx | 2 +- .../ReviewPRViewAnimatedVisual.tsx | 2 +- .../feature-wall/ReviewShipAnimatedVisual.tsx | 2 +- .../feature-wall/review-notes-diff-rows.tsx | 2 +- .../components/github/PRFilterSections.tsx | 3 +- .../github/github-rate-limit-display.tsx | 4 +- .../gitlab/gitlab-rate-limit-display.tsx | 2 +- .../src/components/linear-priority-icon.tsx | 9 +- .../right-sidebar/FileExplorerTreeStatus.tsx | 2 +- .../SourceControlAgentActionDialogForm.tsx | 2 +- .../right-sidebar/checks-panel-content.tsx | 4 +- .../src/components/settings/AccountsPane.tsx | 4 +- .../AutoRenameBranchFromWorkSetting.tsx | 8 +- .../src/components/settings/BaseRefPicker.tsx | 4 +- .../settings/BrowserSessionCookiesSection.tsx | 2 +- .../src/components/settings/CliSection.tsx | 2 +- .../settings/CommitMessageAiPane.tsx | 2 +- .../settings/GeneralEditorSettingsSection.tsx | 2 +- .../settings/GeneralUpdateSettingsSection.tsx | 4 +- .../src/components/settings/GitPane.tsx | 9 +- .../settings/ManageSessionKillDialog.tsx | 2 +- .../settings/NotificationSoundSection.tsx | 2 +- .../settings/RepositoryHooksSection.tsx | 6 +- .../components/settings/RepositoryPane.tsx | 2 +- ...orySourceControlAiHostedReviewDefaults.tsx | 2 +- .../settings/RuntimeEnvironmentsPane.tsx | 2 +- .../settings/SshPassphraseDialog.tsx | 4 +- .../settings/WslCliRegistration.tsx | 2 +- .../sidebar/DeleteWorktreeWarningPanels.tsx | 4 +- .../sidebar/SetupScriptPromptCardViews.tsx | 6 +- .../src/components/stats/ShareUsageCard.tsx | 2 +- .../components/stats/UsageOverviewPane.tsx | 2 +- .../src/lib/i18n-jsx-spacing-guard.test.ts | 140 ++++++++++++++++++ 46 files changed, 212 insertions(+), 77 deletions(-) create mode 100644 src/renderer/src/lib/i18n-jsx-spacing-guard.test.ts diff --git a/src/renderer/src/components/GitHubItemDialog.tsx b/src/renderer/src/components/GitHubItemDialog.tsx index 6f6b7fb2424..5d98a84f0f1 100644 --- a/src/renderer/src/components/GitHubItemDialog.tsx +++ b/src/renderer/src/components/GitHubItemDialog.tsx @@ -4935,13 +4935,12 @@ function ChecksTab({ {startedAt && ( - {translate('auto.components.GitHubItemDialog.4812814bc8', 'Started')} - {startedAt} + {translate('auto.components.GitHubItemDialog.4812814bc8', 'Started')} {startedAt} )} {completedAt && ( - {translate('auto.components.GitHubItemDialog.0f478f5efa', 'Completed')} + {translate('auto.components.GitHubItemDialog.0f478f5efa', 'Completed')}{' '} {completedAt} )} diff --git a/src/renderer/src/components/GitLabItemDialog.tsx b/src/renderer/src/components/GitLabItemDialog.tsx index b71386b4c34..827d86ad2d8 100644 --- a/src/renderer/src/components/GitLabItemDialog.tsx +++ b/src/renderer/src/components/GitLabItemDialog.tsx @@ -1034,7 +1034,7 @@ export default function GitLabItemDialog({ {item.author ? ( - {translate('auto.components.GitLabItemDialog.9bfb4a24d7', 'by')} + {translate('auto.components.GitLabItemDialog.9bfb4a24d7', 'by')}{' '} {item.author} ) : null} @@ -1521,7 +1521,7 @@ export default function GitLabItemDialog({ {translate( 'auto.components.GitLabItemDialog.a7eb4f4916', 'from' - )} + )}{' '} {file.oldPath} ) : null} diff --git a/src/renderer/src/components/PullRequestPage.tsx b/src/renderer/src/components/PullRequestPage.tsx index b53ba354618..8cd3f872974 100644 --- a/src/renderer/src/components/PullRequestPage.tsx +++ b/src/renderer/src/components/PullRequestPage.tsx @@ -5030,13 +5030,12 @@ function ChecksTab({ {startedAt && ( - {translate('auto.components.PullRequestPage.76551b1161', 'Started')} - {startedAt} + {translate('auto.components.PullRequestPage.76551b1161', 'Started')} {startedAt} )} {completedAt && ( - {translate('auto.components.PullRequestPage.000f90afcf', 'Completed')} + {translate('auto.components.PullRequestPage.000f90afcf', 'Completed')}{' '} {completedAt} )} diff --git a/src/renderer/src/components/TaskPage.tsx b/src/renderer/src/components/TaskPage.tsx index 51bbc201879..727c0a44970 100644 --- a/src/renderer/src/components/TaskPage.tsx +++ b/src/renderer/src/components/TaskPage.tsx @@ -9323,7 +9323,7 @@ export default function TaskPage(): React.JSX.Element { // Why: per-repo partial-failure signal — distinct from a hard // IPC reject (tasksError). The two are mutually exclusive.
- {failedCount} {translate('auto.components.TaskPage.7762f4b03a', 'of')} + {failedCount} {translate('auto.components.TaskPage.7762f4b03a', 'of')}{' '} {selectedRepos.length}{' '} {translate('auto.components.TaskPage.d1766fd62d', 'projects failed to load')}
diff --git a/src/renderer/src/components/UpdateCard.tsx b/src/renderer/src/components/UpdateCard.tsx index 9010a740e10..c080b518f64 100644 --- a/src/renderer/src/components/UpdateCard.tsx +++ b/src/renderer/src/components/UpdateCard.tsx @@ -887,8 +887,7 @@ function DownloadingContent({
{release ? (

- {translate('auto.components.UpdateCard.f58b5c57a6', 'New:')} - {release.title} + {translate('auto.components.UpdateCard.f58b5c57a6', 'New:')} {release.title}

) : (

diff --git a/src/renderer/src/components/automations/AutomationsPage.tsx b/src/renderer/src/components/automations/AutomationsPage.tsx index 98a78ef9845..538cec979e3 100644 --- a/src/renderer/src/components/automations/AutomationsPage.tsx +++ b/src/renderer/src/components/automations/AutomationsPage.tsx @@ -2877,7 +2877,7 @@ export default function AutomationsPage(): React.JSX.Element { )} - {translate('auto.components.automations.AutomationsPage.0e110a3469', 'Runs')} + {translate('auto.components.automations.AutomationsPage.0e110a3469', 'Runs')}{' '} {selectedRuns.length} diff --git a/src/renderer/src/components/automations/ExternalAutomationRunTable.tsx b/src/renderer/src/components/automations/ExternalAutomationRunTable.tsx index aa8ff9666c7..f6e5f693371 100644 --- a/src/renderer/src/components/automations/ExternalAutomationRunTable.tsx +++ b/src/renderer/src/components/automations/ExternalAutomationRunTable.tsx @@ -288,7 +288,7 @@ export function ExternalAutomationRunTable({ {pageStart}-{pageEnd}{' '} - {translate('auto.components.automations.ExternalAutomationRunTable.7475c0ce96', 'of')} + {translate('auto.components.automations.ExternalAutomationRunTable.7475c0ce96', 'of')}{' '} {totalCount}

diff --git a/src/renderer/src/components/contextual-tours/ContextualTourProgressDots.tsx b/src/renderer/src/components/contextual-tours/ContextualTourProgressDots.tsx index b52cbb6fdf4..bb7fde46fe1 100644 --- a/src/renderer/src/components/contextual-tours/ContextualTourProgressDots.tsx +++ b/src/renderer/src/components/contextual-tours/ContextualTourProgressDots.tsx @@ -46,7 +46,7 @@ export function ContextualTourProgressDots({ {current}{' '} - {translate('auto.components.contextual.tours.ContextualTourProgressDots.7734cb8ad3', 'of')} + {translate('auto.components.contextual.tours.ContextualTourProgressDots.7734cb8ad3', 'of')}{' '} {total} diff --git a/src/renderer/src/components/editor/CombinedDiffViewer.tsx b/src/renderer/src/components/editor/CombinedDiffViewer.tsx index 9ae116de477..7622980a231 100644 --- a/src/renderer/src/components/editor/CombinedDiffViewer.tsx +++ b/src/renderer/src/components/editor/CombinedDiffViewer.tsx @@ -2022,7 +2022,7 @@ export default function CombinedDiffViewer({ {translate('auto.components.editor.CombinedDiffViewer.948a5fd6c8', 'Clear Notes')} - {translate('auto.components.editor.CombinedDiffViewer.84898c548d', 'Clear')} + {translate('auto.components.editor.CombinedDiffViewer.84898c548d', 'Clear')}{' '} {diffCommentCount}{' '} {diffCommentCount === 1 ? translate('auto.components.editor.CombinedDiffViewer.8ab3248fd8', 'note') diff --git a/src/renderer/src/components/editor/ConflictComponents.tsx b/src/renderer/src/components/editor/ConflictComponents.tsx index 47c74ae88db..32159e23f00 100644 --- a/src/renderer/src/components/editor/ConflictComponents.tsx +++ b/src/renderer/src/components/editor/ConflictComponents.tsx @@ -101,7 +101,7 @@ export function ConflictBanner({ )} {label}{' '} - {translate('auto.components.editor.ConflictComponents.55d61a0ccd', 'conflict ·')} + {translate('auto.components.editor.ConflictComponents.55d61a0ccd', 'conflict ·')}{' '} {CONFLICT_KIND_LABELS[conflict.conflictKind]} {conflictNavigation && conflictNavigation.total > 0 && ( @@ -171,7 +171,7 @@ export function ConflictBanner({ )} {entry?.oldPath && (
- {translate('auto.components.editor.ConflictComponents.d5edd81755', 'Renamed from')} + {translate('auto.components.editor.ConflictComponents.d5edd81755', 'Renamed from')}{' '} {entry.oldPath}
)} @@ -334,7 +334,7 @@ export function ConflictReviewPanel({ {translate( 'auto.components.editor.ConflictComponents.a1ce36f77d', 'Snapshot captured at' - )} + )}{' '} {snapshotTime}. diff --git a/src/renderer/src/components/editor/IpynbViewer.tsx b/src/renderer/src/components/editor/IpynbViewer.tsx index fc19ca04175..ecb20f2ff63 100644 --- a/src/renderer/src/components/editor/IpynbViewer.tsx +++ b/src/renderer/src/components/editor/IpynbViewer.tsx @@ -899,7 +899,7 @@ export default function IpynbViewer({ {translate('auto.components.editor.IpynbViewer.329764e9fc', 'BETA')} - {translate('auto.components.editor.IpynbViewer.8c3b21369a', 'nbformat')} + {translate('auto.components.editor.IpynbViewer.8c3b21369a', 'nbformat')}{' '} {notebook.nbformat} diff --git a/src/renderer/src/components/editor/MermaidBlock.tsx b/src/renderer/src/components/editor/MermaidBlock.tsx index 250a0c32161..f25de3983ff 100644 --- a/src/renderer/src/components/editor/MermaidBlock.tsx +++ b/src/renderer/src/components/editor/MermaidBlock.tsx @@ -103,8 +103,7 @@ export default function MermaidBlock({ return (
- {translate('auto.components.editor.MermaidBlock.dcc132e691', 'Diagram error:')} - {error} + {translate('auto.components.editor.MermaidBlock.dcc132e691', 'Diagram error:')} {error}
           {content}
diff --git a/src/renderer/src/components/editor/RichMarkdownDocLinkMenu.tsx b/src/renderer/src/components/editor/RichMarkdownDocLinkMenu.tsx
index d339f1e3da6..dafc70c658b 100644
--- a/src/renderer/src/components/editor/RichMarkdownDocLinkMenu.tsx
+++ b/src/renderer/src/components/editor/RichMarkdownDocLinkMenu.tsx
@@ -68,9 +68,9 @@ export function RichMarkdownDocLinkMenu({
       )}
       {overflow ? (
         
- {translate('auto.components.editor.RichMarkdownDocLinkMenu.2aaf7d9678', 'Showing')} + {translate('auto.components.editor.RichMarkdownDocLinkMenu.2aaf7d9678', 'Showing')}{' '} {rows.length}{' '} - {translate('auto.components.editor.RichMarkdownDocLinkMenu.90c5f0e1e4', 'of')} + {translate('auto.components.editor.RichMarkdownDocLinkMenu.90c5f0e1e4', 'of')}{' '} {totalMatches}
) : null} diff --git a/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx index 24c8399761e..ba9684ba708 100644 --- a/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx @@ -381,7 +381,7 @@ export function ReviewNotesAnimatedVisual(props: { reducedMotion: boolean }): JS
- {translate('auto.components.feature.wall.ReviewNotesAnimatedVisual.a7a89d8f94', 'Line')} + {translate('auto.components.feature.wall.ReviewNotesAnimatedVisual.a7a89d8f94', 'Line')}{' '} ?
diff --git a/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx index be3148a0e7c..461da2f21c6 100644 --- a/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx @@ -272,7 +272,7 @@ export function ReviewPRViewAnimatedVisual(props: { reducedMotion: boolean }): J {translate( 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.6f4c2d7cb7', 'Add a coverage case for' - )} + )}{' '} {translate( 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.c2062da7ec', diff --git a/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx index 0b2ef880ffa..548ac8334f2 100644 --- a/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx @@ -303,7 +303,7 @@ export function ReviewShipAnimatedVisual(props: { reducedMotion: boolean }): JSX {translate( 'auto.components.feature.wall.ReviewShipAnimatedVisual.e725000cd7', 'Changes' - )} + )}{' '} {SHIP_FILES.length} diff --git a/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx b/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx index c5c476a39d2..cbba8da521c 100644 --- a/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx +++ b/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx @@ -85,7 +85,7 @@ export function ReviewDiffRows(): JSX.Element { {translate( 'auto.components.feature.wall.review.notes.diff.rows.f621c734f8', 'Note · line' - )} + )}{' '} ?
diff --git a/src/renderer/src/components/github/PRFilterSections.tsx b/src/renderer/src/components/github/PRFilterSections.tsx index 59fc7634120..6f65eee8e55 100644 --- a/src/renderer/src/components/github/PRFilterSections.tsx +++ b/src/renderer/src/components/github/PRFilterSections.tsx @@ -208,8 +208,7 @@ export function SectionMenu({ return (
- {translate('auto.components.github.PRFilterSections.8177eda37e', 'Filter')} - {subject} + {translate('auto.components.github.PRFilterSections.8177eda37e', 'Filter')} {subject}
{rows.map((row) => (
diff --git a/src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx b/src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx index 6bc87d967e8..a85c566b0d6 100644 --- a/src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx +++ b/src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx @@ -133,7 +133,7 @@ function GitLabRateLimitRows({ )} > {rest.remaining}{' '} - {translate('auto.components.gitlab.gitlab.rate.limit.display.ea8ad0bae8', 'of')} + {translate('auto.components.gitlab.gitlab.rate.limit.display.ea8ad0bae8', 'of')}{' '} {rest.limit}{' '} {translate( 'auto.components.gitlab.gitlab.rate.limit.display.3e2c982cfa', diff --git a/src/renderer/src/components/linear-priority-icon.tsx b/src/renderer/src/components/linear-priority-icon.tsx index ae9ebbb16a8..92f8bfaa210 100644 --- a/src/renderer/src/components/linear-priority-icon.tsx +++ b/src/renderer/src/components/linear-priority-icon.tsx @@ -54,8 +54,7 @@ export function LinearPriorityIcon({ > - {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} - {label} + {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} {label} ) @@ -72,8 +71,7 @@ export function LinearPriorityIcon({ className="size-3 rounded-full border border-muted-foreground/55" /> - {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} - {label} + {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} {label} ) @@ -110,8 +108,7 @@ export function LinearPriorityIcon({ })} - {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} - {label} + {translate('auto.components.linear.priority.icon.c43d3e065b', 'Priority:')} {label} ) diff --git a/src/renderer/src/components/right-sidebar/FileExplorerTreeStatus.tsx b/src/renderer/src/components/right-sidebar/FileExplorerTreeStatus.tsx index 7f8d3824151..58fa294e5da 100644 --- a/src/renderer/src/components/right-sidebar/FileExplorerTreeStatus.tsx +++ b/src/renderer/src/components/right-sidebar/FileExplorerTreeStatus.tsx @@ -29,7 +29,7 @@ export function FileExplorerTreeStatus({ {translate( 'auto.components.right.sidebar.FileExplorerTreeStatus.c76693e456', 'Could not load files for this workspace:' - )} + )}{' '} {error}
) diff --git a/src/renderer/src/components/right-sidebar/SourceControlAgentActionDialogForm.tsx b/src/renderer/src/components/right-sidebar/SourceControlAgentActionDialogForm.tsx index 4f2caf0133b..8f9fff3e657 100644 --- a/src/renderer/src/components/right-sidebar/SourceControlAgentActionDialogForm.tsx +++ b/src/renderer/src/components/right-sidebar/SourceControlAgentActionDialogForm.tsx @@ -378,7 +378,7 @@ export function SourceControlAgentActionDialogForm({ {translate( 'auto.components.right.sidebar.SourceControlAgentActionDialogForm.1bc0bdbb5e', 'Launch:' - )} + )}{' '} {deliveryPlan.commandLabel}
{deliveryPlan.caveat}
diff --git a/src/renderer/src/components/right-sidebar/checks-panel-content.tsx b/src/renderer/src/components/right-sidebar/checks-panel-content.tsx index 84798c944f8..0dd54d7b7dd 100644 --- a/src/renderer/src/components/right-sidebar/checks-panel-content.tsx +++ b/src/renderer/src/components/right-sidebar/checks-panel-content.tsx @@ -735,7 +735,7 @@ function CheckRunDetails({ {translate( 'auto.components.right.sidebar.checks.panel.content.fd46a70f1a', 'Started' - )} + )}{' '} {startedAt} )} @@ -744,7 +744,7 @@ function CheckRunDetails({ {translate( 'auto.components.right.sidebar.checks.panel.content.00e1c1658a', 'Completed' - )} + )}{' '} {completedAt} )} diff --git a/src/renderer/src/components/settings/AccountsPane.tsx b/src/renderer/src/components/settings/AccountsPane.tsx index 5268a890e36..4735f0b06c5 100644 --- a/src/renderer/src/components/settings/AccountsPane.tsx +++ b/src/renderer/src/components/settings/AccountsPane.tsx @@ -1501,14 +1501,14 @@ export function AccountsPane({ {translate( 'auto.components.settings.AccountsPane.0023cc336e', 'Paste either the raw token value (e.g.' - )} + )}{' '} {translate('auto.components.settings.AccountsPane.922b51e02d', 'Fe26.2**…')} {translate( 'auto.components.settings.AccountsPane.338820326a', ') or the full cookie header (e.g.' - )} + )}{' '} {translate('auto.components.settings.AccountsPane.8951c5309f', 'auth=Fe26.2**…')} diff --git a/src/renderer/src/components/settings/AutoRenameBranchFromWorkSetting.tsx b/src/renderer/src/components/settings/AutoRenameBranchFromWorkSetting.tsx index b8540cdefa9..c4d520cbdb7 100644 --- a/src/renderer/src/components/settings/AutoRenameBranchFromWorkSetting.tsx +++ b/src/renderer/src/components/settings/AutoRenameBranchFromWorkSetting.tsx @@ -159,7 +159,7 @@ export function AutoRenameBranchFromWorkSetting({ {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.12ea4a408d', 'When an agent starts working in a new workspace, Orca renames its auto-generated branch (e.g.' - )} + )}{' '} {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.1626524572', @@ -223,7 +223,7 @@ export function AutoRenameBranchFromWorkSetting({ {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.9241b59bf5', 'Use' - )} + )}{' '} {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.c71770c455', @@ -261,7 +261,7 @@ export function AutoRenameBranchFromWorkSetting({ {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.56580dcf60', '. You can also reference' - )} + )}{' '} {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.2ee2779c05', @@ -281,7 +281,7 @@ export function AutoRenameBranchFromWorkSetting({ {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.5d569f5199', '. Orca generates only the final segment, like' - )} + )}{' '} {translate( 'auto.components.settings.AutoRenameBranchFromWorkSetting.800edb1e54', diff --git a/src/renderer/src/components/settings/BaseRefPicker.tsx b/src/renderer/src/components/settings/BaseRefPicker.tsx index b0c0b6e72ef..8bb96a508cc 100644 --- a/src/renderer/src/components/settings/BaseRefPicker.tsx +++ b/src/renderer/src/components/settings/BaseRefPicker.tsx @@ -195,14 +195,14 @@ export function BaseRefPicker({ {translate( 'auto.components.settings.BaseRefPicker.a5c16712c1', 'Multiple remotes detected. Type a remote name (e.g.' - )} + )}{' '} {translate('auto.components.settings.BaseRefPicker.915ad97875', 'upstream')} {translate( 'auto.components.settings.BaseRefPicker.80f7c82303', ') or a full ref (e.g.' - )} + )}{' '} {translate('auto.components.settings.BaseRefPicker.b468f46726', 'upstream/main')} diff --git a/src/renderer/src/components/settings/BrowserSessionCookiesSection.tsx b/src/renderer/src/components/settings/BrowserSessionCookiesSection.tsx index f1c39ca8fec..8fa1e6c9a60 100644 --- a/src/renderer/src/components/settings/BrowserSessionCookiesSection.tsx +++ b/src/renderer/src/components/settings/BrowserSessionCookiesSection.tsx @@ -72,7 +72,7 @@ export function BrowserSessionCookiesSection({ {translate( 'auto.components.settings.BrowserPane.cd47bc9622', 'Select a default profile for new browser tabs. Import cookies and switch profiles per-tab via the' - )} + )}{' '} ···{' '} {translate('auto.components.settings.BrowserPane.e4aaf8051b', 'toolbar menu.')}

diff --git a/src/renderer/src/components/settings/CliSection.tsx b/src/renderer/src/components/settings/CliSection.tsx index 5f61d1e8ef3..6f6eab2ee94 100644 --- a/src/renderer/src/components/settings/CliSection.tsx +++ b/src/renderer/src/components/settings/CliSection.tsx @@ -317,7 +317,7 @@ export function CliSection({ {translate( 'auto.components.settings.CliSection.b0c310ab46', 'Existing launcher target:' - )} + )}{' '} {status.currentTarget}

) : null} diff --git a/src/renderer/src/components/settings/CommitMessageAiPane.tsx b/src/renderer/src/components/settings/CommitMessageAiPane.tsx index 9b8ebc6d9ff..e24fc97ee40 100644 --- a/src/renderer/src/components/settings/CommitMessageAiPane.tsx +++ b/src/renderer/src/components/settings/CommitMessageAiPane.tsx @@ -273,7 +273,7 @@ export function CommitMessageAiPane({ {translate( 'auto.components.settings.CommitMessageAiPane.4f722a5f53', 'Used by commit-message, pull-request, and branch-name recipes that select Custom command. Use' - )} + )}{' '} {translate('auto.components.settings.CommitMessageAiPane.b8b6fd55b4', '{prompt}')} {' '} diff --git a/src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx b/src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx index 9cccdf166ee..2a524600b07 100644 --- a/src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx +++ b/src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx @@ -170,7 +170,7 @@ export function GeneralEditorSettingsSection({ {translate( 'auto.components.settings.GeneralEditorSettingsSection.8112cd6dcf', 'How long Orca waits after your last edit before saving automatically. First launch defaults to' - )} + )}{' '} {DEFAULT_EDITOR_AUTO_SAVE_DELAY_MS}{' '} {translate('auto.components.settings.GeneralEditorSettingsSection.fc5c5306ff', 'ms.')}

diff --git a/src/renderer/src/components/settings/GeneralUpdateSettingsSection.tsx b/src/renderer/src/components/settings/GeneralUpdateSettingsSection.tsx index 51668b1ac12..da1984cca0e 100644 --- a/src/renderer/src/components/settings/GeneralUpdateSettingsSection.tsx +++ b/src/renderer/src/components/settings/GeneralUpdateSettingsSection.tsx @@ -161,7 +161,7 @@ export function GeneralUpdateSettingsSection(): React.JSX.Element { {translate( 'auto.components.settings.GeneralUpdateSettingsSection.a6b37929dc', 'Version' - )} + )}{' '} {updateStatus.version}{' '} {translate( 'auto.components.settings.GeneralUpdateSettingsSection.8311da27ba', @@ -199,7 +199,7 @@ export function GeneralUpdateSettingsSection(): React.JSX.Element { {translate( 'auto.components.settings.GeneralUpdateSettingsSection.a6b37929dc', 'Version' - )} + )}{' '} {updateStatus.version}{' '} {translate( 'auto.components.settings.GeneralUpdateSettingsSection.d89806cc89', diff --git a/src/renderer/src/components/settings/GitPane.tsx b/src/renderer/src/components/settings/GitPane.tsx index 6d346fb1de6..bdfff4a6967 100644 --- a/src/renderer/src/components/settings/GitPane.tsx +++ b/src/renderer/src/components/settings/GitPane.tsx @@ -236,11 +236,14 @@ export function GitPane({ {translate( 'auto.components.settings.GitPane.976afc6b3e', 'When you create a workspace, Orca refreshes the remote base and safely fast-forwards your matching local branch, such as' - )} + )}{' '} {translate('auto.components.settings.GitPane.ffba483bae', 'main')}{' '} - {translate('auto.components.settings.GitPane.5bf885be48', 'or')} + {translate('auto.components.settings.GitPane.5bf885be48', 'or')}{' '} {translate('auto.components.settings.GitPane.3ae3de8898', 'master')} - {translate('auto.components.settings.GitPane.db3a127eb1', '. This keeps commands like')} + {translate( + 'auto.components.settings.GitPane.db3a127eb1', + '. This keeps commands like' + )}{' '} {translate('auto.components.settings.GitPane.d072a12995', 'git diff main...HEAD')} {' '} diff --git a/src/renderer/src/components/settings/ManageSessionKillDialog.tsx b/src/renderer/src/components/settings/ManageSessionKillDialog.tsx index 56ddb9845ee..04c795f3748 100644 --- a/src/renderer/src/components/settings/ManageSessionKillDialog.tsx +++ b/src/renderer/src/components/settings/ManageSessionKillDialog.tsx @@ -66,7 +66,7 @@ export function ManageSessionKillDialog({ {translate( 'auto.components.settings.ManageSessionKillDialog.8401328fed', 'Force-quits' - )} + )}{' '} {session.sessionId} {translate( 'auto.components.settings.ManageSessionKillDialog.ad9832aa26', diff --git a/src/renderer/src/components/settings/NotificationSoundSection.tsx b/src/renderer/src/components/settings/NotificationSoundSection.tsx index 0d0b92279e7..da1fe4e11cc 100644 --- a/src/renderer/src/components/settings/NotificationSoundSection.tsx +++ b/src/renderer/src/components/settings/NotificationSoundSection.tsx @@ -159,7 +159,7 @@ export function NotificationSoundSection({ className="truncate font-mono text-[11px] text-muted-foreground" title={notificationSettings.customSoundPath} > - {translate('auto.components.settings.NotificationsPane.4aa5085cd7', 'Custom:')} + {translate('auto.components.settings.NotificationsPane.4aa5085cd7', 'Custom:')}{' '} {notificationSettings.customSoundPath}

) : null} diff --git a/src/renderer/src/components/settings/RepositoryHooksSection.tsx b/src/renderer/src/components/settings/RepositoryHooksSection.tsx index 1ee7cfe19f8..bec7ba52a28 100644 --- a/src/renderer/src/components/settings/RepositoryHooksSection.tsx +++ b/src/renderer/src/components/settings/RepositoryHooksSection.tsx @@ -441,7 +441,7 @@ function ExampleTemplateCard({ return (

- {translate('auto.components.settings.RepositoryHooksSection.175daba180', 'Example')} + {translate('auto.components.settings.RepositoryHooksSection.175daba180', 'Example')}{' '} {translate('auto.components.settings.RepositoryHooksSection.39da2ae12f', 'orca.yaml')} {' '} @@ -665,7 +665,7 @@ function ScriptEditor({ - {translate('auto.components.settings.RepositoryHooksSection.b113344b6a', 'Edit')} + {translate('auto.components.settings.RepositoryHooksSection.b113344b6a', 'Edit')}{' '} {translate( 'auto.components.settings.RepositoryHooksSection.39da2ae12f', @@ -1308,7 +1308,7 @@ export function RepositoryHooksSection({ {translate( 'auto.components.settings.RepositoryHooksSection.ac9038d2cc', 'When both' - )} + )}{' '} {translate( 'auto.components.settings.RepositoryHooksSection.39da2ae12f', diff --git a/src/renderer/src/components/settings/RepositoryPane.tsx b/src/renderer/src/components/settings/RepositoryPane.tsx index bb178d3c17f..a90c94e5ea8 100644 --- a/src/renderer/src/components/settings/RepositoryPane.tsx +++ b/src/renderer/src/components/settings/RepositoryPane.tsx @@ -226,7 +226,7 @@ export function RepositoryPane({ )}

- {translate('auto.components.settings.RepositoryPane.323debba71', 'Type:')} + {translate('auto.components.settings.RepositoryPane.323debba71', 'Type:')}{' '} {getRepoKindLabel(repo)}

{isFolder ? ( diff --git a/src/renderer/src/components/settings/RepositorySourceControlAiHostedReviewDefaults.tsx b/src/renderer/src/components/settings/RepositorySourceControlAiHostedReviewDefaults.tsx index 7d620ee7ace..07156b38388 100644 --- a/src/renderer/src/components/settings/RepositorySourceControlAiHostedReviewDefaults.tsx +++ b/src/renderer/src/components/settings/RepositorySourceControlAiHostedReviewDefaults.tsx @@ -82,7 +82,7 @@ export function RepositorySourceControlAiHostedReviewDefaults({ {translate( 'auto.components.settings.RepositorySourceControlAiHostedReviewDefaults.a68849a859', 'Global default is' - )} + )}{' '} {inherited}. diff --git a/src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx b/src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx index 116dc02c5be..af3e2be3ee0 100644 --- a/src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx +++ b/src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx @@ -813,7 +813,7 @@ export function RuntimeEnvironmentsPane({ className="h-8 min-w-0 font-mono text-xs" />

- {translate('auto.components.settings.RuntimeEnvironmentsPane.163671f7b5', 'Run')} + {translate('auto.components.settings.RuntimeEnvironmentsPane.163671f7b5', 'Run')}{' '} {translate( 'auto.components.settings.RuntimeEnvironmentsPane.960e901ae4', diff --git a/src/renderer/src/components/settings/SshPassphraseDialog.tsx b/src/renderer/src/components/settings/SshPassphraseDialog.tsx index 7cfb4dbd0b8..56fd3c3d374 100644 --- a/src/renderer/src/components/settings/SshPassphraseDialog.tsx +++ b/src/renderer/src/components/settings/SshPassphraseDialog.tsx @@ -133,7 +133,7 @@ export function SshPassphraseDialog(): React.JSX.Element | null { {translate( 'auto.components.settings.SshPassphraseDialog.dbf9b6f2d0', 'Enter the password for' - )} + )}{' '} {label} ) : ( @@ -141,7 +141,7 @@ export function SshPassphraseDialog(): React.JSX.Element | null { {translate( 'auto.components.settings.SshPassphraseDialog.ce4fdf7914', 'Enter the passphrase for' - )} + )}{' '} {label} )} diff --git a/src/renderer/src/components/settings/WslCliRegistration.tsx b/src/renderer/src/components/settings/WslCliRegistration.tsx index 9d23dbeca93..7d01d96d58b 100644 --- a/src/renderer/src/components/settings/WslCliRegistration.tsx +++ b/src/renderer/src/components/settings/WslCliRegistration.tsx @@ -217,7 +217,7 @@ export function WslCliRegistration({ {translate( 'auto.components.settings.WslCliRegistration.1dbb0377d9', 'Existing launcher target:' - )} + )}{' '} {status.currentTarget}

) : null} diff --git a/src/renderer/src/components/sidebar/DeleteWorktreeWarningPanels.tsx b/src/renderer/src/components/sidebar/DeleteWorktreeWarningPanels.tsx index 89b22f6c841..b143148249c 100644 --- a/src/renderer/src/components/sidebar/DeleteWorktreeWarningPanels.tsx +++ b/src/renderer/src/components/sidebar/DeleteWorktreeWarningPanels.tsx @@ -21,7 +21,7 @@ export function DeleteWorktreeWarningPanels({ {translate( 'auto.components.sidebar.DeleteWorktreeWarningPanels.e3be9eba15', 'This is the' - )} + )}{' '} {translate( 'auto.components.sidebar.DeleteWorktreeWarningPanels.c4f96a6e18', @@ -32,7 +32,7 @@ export function DeleteWorktreeWarningPanels({ 'auto.components.sidebar.DeleteWorktreeWarningPanels.026738155a', '(the original clone directory).' )} - {mainWorktreeBlocker} + {mainWorktreeBlocker ? <> {mainWorktreeBlocker} : null}
diff --git a/src/renderer/src/components/sidebar/SetupScriptPromptCardViews.tsx b/src/renderer/src/components/sidebar/SetupScriptPromptCardViews.tsx index 45f2947d3ed..25a960bff8c 100644 --- a/src/renderer/src/components/sidebar/SetupScriptPromptCardViews.tsx +++ b/src/renderer/src/components/sidebar/SetupScriptPromptCardViews.tsx @@ -71,7 +71,7 @@ export function DetectedSetupPreview({ {translate( 'auto.components.sidebar.SetupScriptPromptCardViews.d02e6a42b1', 'Detected from' - )} + )}{' '} {provenance}

) : null} @@ -157,7 +157,7 @@ export function SetupScriptPromptBody({ {translate( 'auto.components.sidebar.SetupScriptPromptCardViews.bb879db364', 'This repo ignores shared' - )} + )}{' '} {translate('auto.components.sidebar.SetupScriptPromptCardViews.8f6be51aa1', 'orca.yaml')} {' '} @@ -184,7 +184,7 @@ export function SetupScriptPromptBody({ {translate( 'auto.components.sidebar.SetupScriptPromptCardViews.b56d1322f7', 'Found a setup command in' - )} + )}{' '} {candidateSource} {translate( 'auto.components.sidebar.SetupScriptPromptCardViews.8349e3fa4c', diff --git a/src/renderer/src/components/stats/ShareUsageCard.tsx b/src/renderer/src/components/stats/ShareUsageCard.tsx index cb6eb5c92bf..dcd2f673cb2 100644 --- a/src/renderer/src/components/stats/ShareUsageCard.tsx +++ b/src/renderer/src/components/stats/ShareUsageCard.tsx @@ -233,7 +233,7 @@ function ChartHeader(props: {
{props.sessions}{' '} - {translate('auto.components.stats.ShareUsageCard.4a4c6c79a3', 'sessions ·')} + {translate('auto.components.stats.ShareUsageCard.4a4c6c79a3', 'sessions ·')}{' '} {props.turnsOrEvents.count} {props.turnsOrEvents.label}
diff --git a/src/renderer/src/components/stats/UsageOverviewPane.tsx b/src/renderer/src/components/stats/UsageOverviewPane.tsx index 1f2080c995d..03001c2bec9 100644 --- a/src/renderer/src/components/stats/UsageOverviewPane.tsx +++ b/src/renderer/src/components/stats/UsageOverviewPane.tsx @@ -253,7 +253,7 @@ export function UsageOverviewPane(): React.JSX.Element {

{overview.enabledProviderCount}{' '} - {translate('auto.components.stats.UsageOverviewPane.ecb0cd8a4c', 'enabled -')} + {translate('auto.components.stats.UsageOverviewPane.ecb0cd8a4c', 'enabled -')}{' '} {overview.dataProviderCount}{' '} {translate('auto.components.stats.UsageOverviewPane.444585cb41', 'with data')}

diff --git a/src/renderer/src/lib/i18n-jsx-spacing-guard.test.ts b/src/renderer/src/lib/i18n-jsx-spacing-guard.test.ts new file mode 100644 index 00000000000..f2dde24571e --- /dev/null +++ b/src/renderer/src/lib/i18n-jsx-spacing-guard.test.ts @@ -0,0 +1,140 @@ +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { describe, expect, it } from 'vitest' + +/** + * Guard against the i18n fragment glue-word class of bugs: + * `translate('…', 'word')}` immediately followed by `` / `{value}` without + * an explicit `{' '}` space. Newline indentation alone is not reliable. + */ +const ROOT = path.resolve(__dirname, '..') + +type GuardCase = { + file: string + /** English fallback that must be followed by `{' '}` before the next token. */ + afterFallback: string + /** Human-readable description for assertion messages. */ + label: string +} + +const CASES: GuardCase[] = [ + { + file: 'components/sidebar/SetupScriptPromptCardViews.tsx', + afterFallback: 'This repo ignores shared', + label: 'shared orca.yaml' + }, + { + file: 'components/sidebar/SetupScriptPromptCardViews.tsx', + afterFallback: 'Detected from', + label: 'Detected from provenance' + }, + { + file: 'components/sidebar/SetupScriptPromptCardViews.tsx', + afterFallback: 'Found a setup command in', + label: 'Found a setup command in source' + }, + { + file: 'components/settings/RepositoryHooksSection.tsx', + afterFallback: 'Example', + label: 'Example orca.yaml template' + }, + { + file: 'components/settings/RepositoryHooksSection.tsx', + afterFallback: 'Edit', + label: 'Edit orca.yaml' + }, + { + file: 'components/settings/RepositoryHooksSection.tsx', + afterFallback: 'When both', + label: 'When both orca.yaml' + }, + { + file: 'components/settings/GitPane.tsx', + afterFallback: 'or', + label: 'main or master' + }, + { + file: 'components/settings/GitPane.tsx', + afterFallback: 'such as', + label: 'such as main' + }, + { + file: 'components/settings/RuntimeEnvironmentsPane.tsx', + afterFallback: 'Run', + label: 'Run orca serve' + }, + { + file: 'components/settings/AutoRenameBranchFromWorkSetting.tsx', + afterFallback: 'Use', + label: 'Use {basePrompt}' + }, + { + file: 'components/settings/CommitMessageAiPane.tsx', + afterFallback: 'Use', + label: 'Use {prompt}' + }, + { + file: 'components/editor/RichMarkdownDocLinkMenu.tsx', + afterFallback: 'Showing', + label: 'Showing N of M' + }, + { + file: 'components/editor/RichMarkdownDocLinkMenu.tsx', + afterFallback: 'of', + label: 'of totalMatches' + }, + { + file: 'components/contextual-tours/ContextualTourProgressDots.tsx', + afterFallback: 'of', + label: 'tour of total' + }, + { + file: 'components/github/PRFilterSections.tsx', + afterFallback: 'Filter', + label: 'Filter pull requests' + }, + { + file: 'components/editor/ConflictComponents.tsx', + afterFallback: 'Renamed from', + label: 'Renamed from path' + }, + { + file: 'components/settings/SshPassphraseDialog.tsx', + afterFallback: 'Enter the password for', + label: 'password for host' + }, + { + file: 'components/settings/ManageSessionKillDialog.tsx', + afterFallback: 'Force-quits', + label: 'Force-quits session id' + }, + { + file: 'components/sidebar/DeleteWorktreeWarningPanels.tsx', + afterFallback: 'This is the', + label: 'This is the main worktree' + } +] + +function hasExplicitSpaceAfterFallback(source: string, fallback: string): boolean { + // Match translate fallbacks that end with (or equal) the given phrase, then + // require an explicit {' '} after the closing )} so JSX whitespace is not + // left to indentation alone. + const escaped = fallback.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const pattern = new RegExp( + String.raw`translate\(\s*'[^']+',\s*(?:'[^']*${escaped}'|"[^"]*${escaped}")\s*(?:,\s*\{[^}]*\})?\s*\)\s*\}\s*\{\s*['"]\s['"]\s*\}`, + 'm' + ) + return pattern.test(source) +} + +describe('i18n JSX fragment spacing guard', () => { + for (const testCase of CASES) { + it(`keeps an explicit space after "${testCase.label}" in ${testCase.file}`, () => { + const source = readFileSync(path.join(ROOT, testCase.file), 'utf8') + expect( + hasExplicitSpaceAfterFallback(source, testCase.afterFallback), + `Expected {' '} immediately after translate fallback "${testCase.afterFallback}" in ${testCase.file}` + ).toBe(true) + }) + } +})