fix(i18n): localize Commit & Sync, Rebase, and Create PR hints in the source control dropdown (#14127)

The Commit & Sync tooltip, the Rebase label/tooltip, and the Create PR
blocked hints were bare English literals while the labels beside them went
through translate(). Route all 25 strings through translate() with named
placeholders and add the keys to the English catalog.

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
This commit is contained in:
Evgenii
2026-10-26 09:04:02 -07:00
committed by GitHub
co-authored by Neil
parent 1d0d3f5e94
commit 79894979d2
5 changed files with 173 additions and 24 deletions
@@ -73,28 +73,52 @@ export function buildCommitDropdownItems(ctx: DropdownActionContext): CommitDrop
const commitSyncTitle = ((): string => {
if (upstreamLoading) {
return 'Checking branch status…'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.checkingBranchStatus',
'Checking branch status…'
)
}
if (publishBlockedByPRLoading) {
return 'Checking PR status…'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.titleCheckingPrStatus',
'Checking PR status…'
)
}
if (publishBlockedByMergedPR) {
return 'PR is already merged'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.titlePrAlreadyMerged',
'PR is already merged'
)
}
if (publishBlockedByDetachedHead) {
return 'Check out a branch before syncing commits'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.titleCheckOutBranchToSync',
'Check out a branch before syncing commits'
)
}
if (!hasUpstream) {
// Point to the existing Publish Branch primary instead of naming a nonexistent compound action.
return 'Publish the branch first to sync commits'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.titlePublishBranchFirst',
'Publish the branch first to sync commits'
)
}
if (shouldForcePushWithLease) {
return (
commitDisabledReason ??
'Use Commit & Force Push — remote only has older copies of local commits'
translate(
'auto.components.right.sidebar.source.control.dropdown.items.titleUseForcePush',
'Use Commit & Force Push — remote only has older copies of local commits'
)
)
}
return commitDisabledReason ?? 'Commit, then pull and push'
return (
commitDisabledReason ??
translate(
'auto.components.right.sidebar.source.control.dropdown.items.titleCommitThenPullPush',
'Commit, then pull and push'
)
)
})()
const commitSync: DropdownItem = {
kind: 'commit_sync',
@@ -1,4 +1,6 @@
import { describe, expect, it } from 'vitest'
import { i18n } from '@/i18n/i18n'
import { PSEUDO_LOCALIZATION_LOCALE } from '@/i18n/pseudo-localization'
import { resolveDropdownItems } from './source-control-dropdown-items'
import type { DropdownActionInputs, DropdownItem } from './source-control-dropdown-item-types'
import {
@@ -645,6 +647,37 @@ describe('resolveDropdownItems', () => {
'Try a fast-forward pull; git may reject local commits'
)
})
it('routes Commit & Sync, Rebase, and Create PR blocked copy through the active locale', async () => {
await i18n.changeLanguage(PSEUDO_LOCALIZATION_LOCALE)
try {
const loading = resolveDropdownItems(inputs({ rebaseBaseRef: 'origin/main' }))
const blocked = resolveDropdownItems(
inputs({
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: {
provider: 'github',
review: null,
canCreate: false,
blockedReason: 'dirty',
nextAction: 'commit',
reviewLookupOutcome: 'not_found'
}
})
)
const loadingByKind = Object.fromEntries(
loading.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e])
)
const blockedByKind = Object.fromEntries(
blocked.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e])
)
expect(loadingByKind.commit_sync.title).toBe('[Checking branch status…]')
expect(loadingByKind.rebase_base.label).toBe('[Rebase from origin/main]')
expect(blockedByKind.create_pr.hint).toBe('[Commit changes first]')
} finally {
await i18n.changeLanguage('en')
}
})
})
// Exercise the real push-target resolution chain used by the component (#8196).
@@ -165,15 +165,34 @@ export function buildRemoteDropdownItems(ctx: DropdownActionContext): RemoteDrop
const hasRemoteBaseRef = rebaseBaseLabel?.includes('/') === true
const rebase: DropdownItem = {
kind: 'rebase_base',
label: rebaseBaseLabel ? `Rebase from ${rebaseBaseLabel}` : 'Rebase from Base',
label: rebaseBaseLabel
? translate(
'auto.components.right.sidebar.source.control.dropdown.items.rebaseFromNamedBase',
'Rebase from {{baseRef}}',
{ baseRef: rebaseBaseLabel }
)
: translate(
'auto.components.right.sidebar.source.control.dropdown.items.rebaseFromBase',
'Rebase from Base'
),
title: ((): string => {
if (!rebaseBaseLabel || !hasRemoteBaseRef) {
return 'Choose a remote base branch to rebase from'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.rebaseChooseRemoteBase',
'Choose a remote base branch to rebase from'
)
}
if (hasDirtyLocalChanges) {
return 'Try rebasing; git may require committing or stashing local changes first'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.rebaseDirtyHint',
'Try rebasing; git may require committing or stashing local changes first'
)
}
return `Rebase current branch with latest commits from ${rebaseBaseLabel}`
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.rebaseWithLatestFrom',
'Rebase current branch with latest commits from {{baseRef}}',
{ baseRef: rebaseBaseLabel }
)
})(),
disabled: globalBusy || !rebaseBaseRef || !hasRemoteBaseRef
}
@@ -39,30 +39,78 @@ export function buildHostedReviewDropdownItems(
const createBlockedHint = ((): string => {
switch (hostedReviewCreation?.blockedReason) {
case 'dirty':
return 'Commit changes first'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedCommitFirst',
'Commit changes first'
)
case 'detached_head':
return 'Check out a branch first'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedCheckOutBranchFirst',
'Check out a branch first'
)
case 'default_branch':
return 'Switch to a feature branch'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedSwitchToFeatureBranch',
'Switch to a feature branch'
)
case 'no_upstream':
return 'Publish Branch'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedPublishBranch',
'Publish Branch'
)
case 'needs_push':
return 'Push first'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedPushFirst',
'Push first'
)
case 'needs_sync':
return shouldForcePushWithLease ? 'Force Push first' : 'Sync first'
return shouldForcePushWithLease
? translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedForcePushFirst',
'Force Push first'
)
: translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedSyncFirst',
'Sync first'
)
case 'auth_required':
return `${createReviewCopy.authInstruction} in this environment`
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedAuthRequired',
'{{authInstruction}} in this environment',
{ authInstruction: createReviewCopy.authInstruction }
)
case 'unsupported_provider':
return 'Unsupported provider'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedUnsupportedProvider',
'Unsupported provider'
)
case 'existing_review':
return `A ${createReviewCopy.reviewLabel} already exists`
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedExistingReview',
'A {{reviewLabel}} already exists',
{ reviewLabel: createReviewCopy.reviewLabel }
)
case 'fork_head_unsupported':
return 'Fork head unsupported'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedForkHeadUnsupported',
'Fork head unsupported'
)
case 'base_not_on_remote':
return 'Base branch is not on the remote'
return translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedBaseNotOnRemote',
'Base branch is not on the remote'
)
case null:
case undefined:
return upstreamLoading ? 'Checking branch status…' : 'Branch is not ready'
return upstreamLoading
? translate(
'auto.components.right.sidebar.source.control.dropdown.items.checkingBranchStatus',
'Checking branch status…'
)
: translate(
'auto.components.right.sidebar.source.control.dropdown.items.blockedBranchNotReady',
'Branch is not ready'
)
}
})()
+26 -1
View File
@@ -14505,7 +14505,32 @@
"226b85a3a7": "Fetch",
"323bb614aa": "Commit & Sync",
"2b8e6595fd": "Commit",
"04d709801d": "Fetch from remote without merging"
"04d709801d": "Fetch from remote without merging",
"checkingBranchStatus": "Checking branch status…",
"titleCheckingPrStatus": "Checking PR status…",
"titlePrAlreadyMerged": "PR is already merged",
"titleCheckOutBranchToSync": "Check out a branch before syncing commits",
"titlePublishBranchFirst": "Publish the branch first to sync commits",
"titleUseForcePush": "Use Commit & Force Push — remote only has older copies of local commits",
"titleCommitThenPullPush": "Commit, then pull and push",
"rebaseFromNamedBase": "Rebase from {{baseRef}}",
"rebaseFromBase": "Rebase from Base",
"rebaseChooseRemoteBase": "Choose a remote base branch to rebase from",
"rebaseDirtyHint": "Try rebasing; git may require committing or stashing local changes first",
"rebaseWithLatestFrom": "Rebase current branch with latest commits from {{baseRef}}",
"blockedCommitFirst": "Commit changes first",
"blockedCheckOutBranchFirst": "Check out a branch first",
"blockedSwitchToFeatureBranch": "Switch to a feature branch",
"blockedPublishBranch": "Publish Branch",
"blockedPushFirst": "Push first",
"blockedForcePushFirst": "Force Push first",
"blockedSyncFirst": "Sync first",
"blockedAuthRequired": "{{authInstruction}} in this environment",
"blockedUnsupportedProvider": "Unsupported provider",
"blockedExistingReview": "A {{reviewLabel}} already exists",
"blockedForkHeadUnsupported": "Fork head unsupported",
"blockedBaseNotOnRemote": "Base branch is not on the remote",
"blockedBranchNotReady": "Branch is not ready"
}
},
"primary": {