fix(web-shell): localize remaining hardcoded UI strings (#5189)

Route the last few hardcoded English strings in web-shell through the
existing useI18n()/t() layer and add matching en / zh-CN keys:

- close-button tooltips across 9 dialogs
- the history-search label, placeholder and key hint in the editor
- icon-button aria-labels (toast dismiss, thinking toggle, sub-agent
  stream toggle), which also improves screen-reader support

No new i18n infrastructure; follows the existing pattern used by the
same components.

Co-authored-by: qingyu <qingyu@taobao.com>
Co-authored-by: 易良 <1204183885@qq.com>
This commit is contained in:
carffuca 2026-06-17 11:56:13 +08:00 committed by GitHub
parent ccf1e5672f
commit 6fbcf802a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 33 additions and 17 deletions

View file

@ -1934,18 +1934,18 @@ export const Editor = forwardRef<EditorHandle, EditorProps>(function Editor(
{searchMode && (
<div ref={searchUiRef}>
<div className={styles.searchBar}>
<span className={styles.searchLabel}>reverse-i-search:</span>
<span className={styles.searchLabel}>
{t('editor.searchLabel')}
</span>
<input
ref={searchInputRef}
className={styles.searchInput}
value={searchQuery}
onChange={handleSearchInput}
onKeyDown={handleSearchKeyDown}
placeholder="type to search..."
placeholder={t('editor.searchPlaceholder')}
/>
<span className={styles.searchHint}>
ctrl+r next · tab accept · enter send · esc cancel
</span>
<span className={styles.searchHint}>{t('editor.searchHint')}</span>
</div>
{searchMatches.length > 0 && (
<div className={styles.searchResults}>

View file

@ -1,4 +1,5 @@
import { useEffect } from 'react';
import { useI18n } from '../i18n';
import styles from './ToastHost.module.css';
export type ToastTone = 'info' | 'warning' | 'error' | 'success';
@ -44,6 +45,7 @@ function ToastItem({
onDismiss: (id: string) => void;
autoDismissMs: number;
}) {
const { t } = useI18n();
useEffect(() => {
const timer = window.setTimeout(() => onDismiss(toast.id), autoDismissMs);
return () => window.clearTimeout(timer);
@ -56,8 +58,8 @@ function ToastItem({
type="button"
className={styles.close}
onClick={() => onDismiss(toast.id)}
aria-label="Dismiss notification"
title="Dismiss"
aria-label={t('toast.dismiss')}
title={t('toast.dismissShort')}
>
x
</button>

View file

@ -82,7 +82,7 @@ export function ApprovalModeDialog({
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -288,7 +288,7 @@ export function DeleteSessionDialog({
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -311,7 +311,7 @@ export function HelpDialog({ commands, onClose }: HelpDialogProps) {
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -464,7 +464,7 @@ export function McpDialog({ onClose }: McpDialogProps) {
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -370,7 +370,7 @@ export function MemoryDialog({
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -191,7 +191,7 @@ export function ReleaseSessionDialog({
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -138,7 +138,7 @@ export function ResumeDialog({ onSelect, onClose }: ResumeDialogProps) {
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -95,7 +95,7 @@ export function SkillsDialog({ onClose }: SkillsDialogProps) {
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -77,7 +77,7 @@ export function ThemeDialog({
<button
className={dp('resume-picker-close')}
onClick={onClose}
title="Close"
title={t('common.close')}
>
ESC
</button>

View file

@ -8,6 +8,7 @@ import {
} from 'react';
import type { ACPToolCall } from '../../../adapters/types';
import { useWebShellCustomization } from '../../../customization';
import { useI18n } from '../../../i18n';
// Circular import with ToolGroup (agents render tool rows; agent tool
// rows render SubAgentPanel). Safe only while both modules dereference
// each other's exports at render time — never in top-level code.
@ -159,6 +160,7 @@ type SubAgentTab = 'result' | 'tools';
*/
function SubAgentStream({ text }: { text: string }) {
const { compactThinking } = useWebShellCustomization();
const { t } = useI18n();
const [streamExpanded, setStreamExpanded] = useState(false);
const [overflowing, setOverflowing] = useState(false);
const streamRef = useRef<HTMLPreElement>(null);
@ -199,7 +201,7 @@ function SubAgentStream({ text }: { text: string }) {
className={styles.expandToggle}
onClick={() => setStreamExpanded((v) => !v)}
aria-expanded={streamExpanded}
aria-label="Toggle agent stream details"
aria-label={t('subagent.toggleStream')}
>
{streamExpanded ? '▲' : '▼'}
</button>

View file

@ -162,6 +162,9 @@ const EN: Messages = {
'agent.view': 'View',
'agents.closed': 'Agents panel closed.',
'agents.title': 'Agents',
'subagent.toggleStream': 'Toggle agent stream details',
'toast.dismiss': 'Dismiss notification',
'toast.dismissShort': 'Dismiss',
'insight.ready': 'Insight report generated successfully!',
'request.cancelled': 'Request cancelled.',
'approval.execQuestion': (v) => `Allow execution of: '${v?.tool ?? ''}'?`,
@ -306,6 +309,9 @@ const EN: Messages = {
'editor.noHistory': 'No matching history',
'editor.placeholder': 'Type a message or @ file path',
'editor.processing': 'Processing. New messages will be queued.',
'editor.searchHint': 'ctrl+r next · tab accept · enter send · esc cancel',
'editor.searchLabel': 'reverse-i-search:',
'editor.searchPlaceholder': 'type to search...',
'error.unsupportedTheme':
'Unsupported theme. Use /theme light or /theme dark.',
'queue.cleared': 'Queued messages cleared',
@ -1020,6 +1026,9 @@ const ZH: Messages = {
'agent.view': '查看',
'agents.closed': '智能体面板已关闭。',
'agents.title': '智能体',
'subagent.toggleStream': '展开/收起子智能体详情',
'toast.dismiss': '关闭通知',
'toast.dismissShort': '关闭',
'insight.ready': 'Insight 报告已生成!',
'request.cancelled': '请求已取消。',
'approval.execQuestion': (v) => `允许执行:'${v?.tool ?? ''}'`,
@ -1151,6 +1160,9 @@ const ZH: Messages = {
'editor.noHistory': '没有匹配的历史记录',
'editor.placeholder': '输入消息或 @ 文件路径',
'editor.processing': '处理中。新消息会进入队列。',
'editor.searchHint': 'ctrl+r 下一条 · tab 采纳 · enter 发送 · esc 取消',
'editor.searchLabel': '历史搜索:',
'editor.searchPlaceholder': '输入以搜索…',
'error.unsupportedTheme':
'不支持该主题。请使用 /theme light 或 /theme dark。',
'queue.cleared': '已清空排队消息',