fix(i18n,ui): 补 ja/ko 缺失 key + SelectionAsk 启用开关右置

PR #236 合并后两处后续修复:

1. ja.ts / ko.ts 缺 PR 引入的 7 个 comboRecord* 键和 1 个
   triggers.custom 键,导致 main 的 npm run build 直接 TS 报错。
   补齐后 tsc 通过。

2. SelectionAsk 划词追问页:触发快捷键卡片把"右上角状态徽标 +
   下方启用按钮"换成与 Style 页一致的右上角 36×20 toggle;
   保存历史卡片同步从底部 44×24 开关改成右上角同款 toggle。
   抽出 CardHeaderToggle 复用,跟 design_handoff Style 页对齐。
This commit is contained in:
baiqing committed 2026-05-07 09:27:59 +08:00
1 parent 98da5970bf
commit 1e04433443
3 files changed
+76 -56

No files matched your search

+8
View File
@@ -282,6 +282,13 @@ export const ja: typeof zhCN = {
muteDuringRecordingDesc: '音声入力開始時にシステム出力を一時的にミュートし、停止/キャンセル/エラー後に元のミュート状態を復元。スピーカーの音がマイクに入らないようにします。',
restoreClipboardLabel: '入力後にクリップボードを復元',
restoreClipboardDesc: 'Windows / Linux のみ:ペースト成功後に元のクリップボード内容を復元(既定 ON)。OFF にするとディクテーションテキストがクリップボードに残り、ペーストが効かなかった場合に手動で Ctrl+V できます。詳細は issue #111。',
comboRecordLabel: 'ショートカットを記録',
comboRecordDesc: 'クリック後、希望するキーの組み合わせ(例:⌘⇧D)を押してください。トグル / 押し続けの両方に対応。',
comboRecordBtn: 'ショートカットを記録',
comboRecordHint: 'ショートカットの組み合わせを押してください…',
comboRecorded: '記録済み',
comboClear: 'クリア',
comboConflict: 'このショートカットの組み合わせは使用できません',
allowNonTsfFallbackLabel: '非 TSF フォールバックを許可',
allowNonTsfFallbackDesc: 'Windows のみ:TSF による直接入力が失敗した場合、Unicode SendInput / ショートカットペースト / WM_PASTE への切り替えを許可。OFF にすると TSF 入力が実際に使われているか検証できます。',
startupAtBoot: '起動時に自動起動',
@@ -527,6 +534,7 @@ export const ja: typeof zhCN = {
rightCommand: '右 Command',
fn: 'Fn (地球キー)',
rightAlt: '右 Alt',
custom: 'カスタム',
},
fallback: 'グローバルショートカット',
modeHoldSuffix: '(押し続けて話す)',
+8
View File
@@ -282,6 +282,13 @@ export const ko: typeof zhCN = {
muteDuringRecordingDesc: '음성 입력 시작 시 시스템 출력을 일시적으로 음소거하고, 정지/취소/오류 후 원래 음소거 상태를 복원합니다. 스피커 소리가 마이크에 들어가지 않도록 합니다.',
restoreClipboardLabel: '입력 후 클립보드 복원',
restoreClipboardDesc: 'Windows / Linux 만: 붙여넣기 성공 후 원래 클립보드 내용을 복원합니다(기본 ON). OFF 시 받아쓰기 텍스트가 클립보드에 남아 붙여넣기가 실패한 경우 수동으로 Ctrl+V 할 수 있습니다. issue #111 참조.',
comboRecordLabel: '단축키 녹화',
comboRecordDesc: '클릭 후 원하는 단축키 조합(예: ⌘⇧D)을 누르세요. 토글 및 누르기 모드 모두 지원합니다.',
comboRecordBtn: '단축키 녹화',
comboRecordHint: '단축키 조합을 눌러 주세요…',
comboRecorded: '녹화됨',
comboClear: '지우기',
comboConflict: '이 단축키 조합은 사용할 수 없습니다',
allowNonTsfFallbackLabel: '비 TSF 폴백 허용',
allowNonTsfFallbackDesc: 'Windows 만: TSF 직접 입력이 실패할 경우 Unicode SendInput / 단축키 붙여넣기 / WM_PASTE 로 전환을 허용합니다. OFF 시 실제로 TSF 입력이 사용되는지 검증할 수 있습니다.',
startupAtBoot: '부팅 시 자동 시작',
@@ -527,6 +534,7 @@ export const ko: typeof zhCN = {
rightCommand: '오른쪽 Command',
fn: 'Fn (지구본 키)',
rightAlt: '오른쪽 Alt',
custom: '사용자 정의',
},
fallback: '전역 단축키',
modeHoldSuffix: '(눌러서 말하기)',
+60 -56
View File
@@ -58,36 +58,18 @@ export function SelectionAsk() {
{/* 1. 触发快捷键 */}
<Card>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
<div style={{ fontSize: 13, fontWeight: 600 }}>{t('selectionAsk.hotkey.title')}</div>
<span
style={{
padding: '2px 8px',
fontSize: 10.5,
fontWeight: 600,
letterSpacing: '0.04em',
borderRadius: 999,
background: enabled ? 'rgba(37,99,235,0.10)' : 'rgba(0,0,0,0.05)',
color: enabled ? 'var(--ol-blue)' : 'var(--ol-ink-4)',
textTransform: 'uppercase',
}}
>
{enabled ? t('selectionAsk.statusEnabled') : t('selectionAsk.statusDisabled')}
</span>
</div>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: 12, lineHeight: 1.55 }}>
{t('selectionAsk.hotkey.desc', { recordHotkey: recordHotkeyLabel })}
</div>
<button
onClick={async () => {
<CardHeaderToggle
title={t('selectionAsk.hotkey.title')}
checked={enabled}
onToggle={async () => {
const nextHotkey = enabled ? null : defaultQaHotkey;
await setQaHotkey(nextHotkey);
await savePrefs({ ...prefs, qaHotkey: nextHotkey });
}}
style={{ fontSize: 12, padding: '5px 14px', background: enabled ? 'rgba(0,0,0,0.06)' : 'var(--ol-blue)', color: enabled ? 'var(--ol-ink-2)' : '#fff', border: 0, borderRadius: 6, fontFamily: 'inherit', fontWeight: 500, cursor: 'default', marginBottom: 10 }}
>
{enabled ? t('selectionAsk.hotkey.optionDisabled') : t('selectionAsk.statusEnabled')}
</button>
/>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: prefs.qaHotkey ? 12 : 0, lineHeight: 1.55 }}>
{t('selectionAsk.hotkey.desc', { recordHotkey: recordHotkeyLabel })}
</div>
{prefs.qaHotkey && (
<ShortcutRecorder
value={prefs.qaHotkey}
@@ -101,38 +83,14 @@ export function SelectionAsk() {
{/* 2. 历史保存 */}
<Card>
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>{t('selectionAsk.history.title')}</div>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: 12, lineHeight: 1.55 }}>
<CardHeaderToggle
title={t('selectionAsk.history.title')}
checked={prefs.qaSaveHistory}
onToggle={() => onSaveHistoryChange(!prefs.qaSaveHistory)}
/>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', lineHeight: 1.55 }}>
{t('selectionAsk.history.desc')}
</div>
<button
onClick={() => onSaveHistoryChange(!prefs.qaSaveHistory)}
style={{
position: 'relative',
width: 44,
height: 24,
borderRadius: 999,
border: 0,
background: prefs.qaSaveHistory ? 'var(--ol-blue)' : 'rgba(0,0,0,0.18)',
cursor: 'default',
transition: 'background 0.16s var(--ol-motion-quick)',
padding: 0,
}}
>
<span
style={{
position: 'absolute',
top: 2,
left: prefs.qaSaveHistory ? 22 : 2,
width: 20,
height: 20,
borderRadius: 999,
background: '#fff',
boxShadow: '0 1px 2px rgba(0,0,0,.18)',
transition: 'left .16s var(--ol-motion-spring)',
}}
/>
</button>
</Card>
{/* 3. 使用方法 */}
@@ -181,3 +139,49 @@ export function SelectionAsk() {
</>
);
}
// 卡片标题行右侧开关:与 Style 页面顶栏的 36×20 toggle 同款,保持全局视觉一致。
function CardHeaderToggle({
title,
checked,
onToggle,
}: {
title: string;
checked: boolean;
onToggle: () => void;
}) {
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
<div style={{ fontSize: 13, fontWeight: 600 }}>{title}</div>
<button
onClick={onToggle}
aria-pressed={checked}
style={{
position: 'relative',
width: 36,
height: 20,
borderRadius: 999,
border: 0,
background: checked ? 'var(--ol-blue)' : 'rgba(0,0,0,0.15)',
cursor: 'default',
transition: 'background 0.16s var(--ol-motion-quick)',
padding: 0,
}}
>
<span
style={{
position: 'absolute',
top: 2,
left: checked ? 18 : 2,
width: 16,
height: 16,
borderRadius: 999,
background: '#fff',
boxShadow: '0 1px 2px rgba(0,0,0,.2)',
transition: 'left .16s var(--ol-motion-spring)',
}}
/>
</button>
</div>
);
}