feat(player): add subtitle search functionality#217
Conversation
- Implement subtitle search feature across multiple locales - Add new translations for subtitle search in en-us, ja-jp, ru-ru, and zh-cn - Create SubtitleSearchHighlight component for highlighting search results - Update player-ui store to manage subtitle search visibility state - Introduce actions to toggle, show, and hide subtitle search
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Walkthrough在播放器新增“字幕搜索”功能:添加可见性状态与控制方法,页面顶部加入搜索入口,SubtitleList 面板实现输入搜索、结果高亮与节流/键盘交互,并扩展多语言 i18n 文案(en/ja/ru/zh-CN/zh-TW)。快捷键 Hook 增加“输入时禁止触发”的选项。 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as 用户
participant Navbar as PlayerPage Navbar
participant Store as PlayerUI Store
participant Panel as SubtitleListPanel
participant HL as HighlightedText
User->>Navbar: 点击“字幕搜索”图标
Navbar->>Store: toggleSubtitleSearch()
Store-->>Navbar: isSearchVisible = true
Store-->>Panel: isSearchVisible 变更
Panel->>Panel: 聚焦搜索输入框
User->>Panel: 输入查询(debounce 300ms)
Panel->>Panel: 过滤字幕行并统计结果
Panel->>HL: 请求高亮渲染
HL-->>Panel: 返回高亮片段
User->>Panel: 按 ESC
Panel->>Store: hideSubtitleSearch()
Store-->>Panel: isSearchVisible = false
sequenceDiagram
autonumber
actor User as 用户
participant Doc as 输入控件(input/textarea/可编辑)
participant Hook as useShortcut
participant App as 回调处理
User->>Doc: 正在输入
Hook->>Hook: 检测 typingActive = true
alt allowWhenTyping = false
Hook--x App: 阻止触发快捷键
else allowWhenTyping = true
Hook->>App: 触发快捷键回调
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (4)src/renderer/src/**/*.{ts,tsx,scss,css}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/renderer/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/player/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (2)📚 Learning: 2025-09-17T14:59:36.985ZApplied to files:
📚 Learning: 2025-09-17T14:59:36.985ZApplied to files:
🧬 Code graph analysis (1)src/renderer/src/pages/player/components/SubtitleSearchHighlight.tsx (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
🔇 Additional comments (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (10)
src/renderer/src/i18n/locales/en-us.json(1 hunks)src/renderer/src/i18n/locales/ja-jp.json(1 hunks)src/renderer/src/i18n/locales/ru-ru.json(1 hunks)src/renderer/src/i18n/locales/zh-cn.json(4 hunks)src/renderer/src/i18n/locales/zh-tw.json(1 hunks)src/renderer/src/infrastructure/hooks/useShortcut.ts(2 hunks)src/renderer/src/pages/player/PlayerPage.tsx(4 hunks)src/renderer/src/pages/player/components/SubtitleListPanel.tsx(9 hunks)src/renderer/src/pages/player/components/SubtitleSearchHighlight.tsx(1 hunks)src/renderer/src/state/stores/player-ui.store.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/renderer/src/**/*.{ts,tsx,scss,css}
📄 CodeRabbit inference engine (CLAUDE.md)
优先使用 CSS 变量,避免硬编码样式值(颜色等)
Files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/SubtitleSearchHighlight.tsxsrc/renderer/src/pages/player/components/SubtitleListPanel.tsxsrc/renderer/src/state/stores/player-ui.store.tssrc/renderer/src/infrastructure/hooks/useShortcut.ts
src/renderer/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
尺寸与时长等不要硬编码,优先使用 useTheme() 的 token 或集中样式变量(如 motionDurationMid、borderRadiusSM/MD)
Files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/SubtitleSearchHighlight.tsxsrc/renderer/src/pages/player/components/SubtitleListPanel.tsxsrc/renderer/src/state/stores/player-ui.store.tssrc/renderer/src/infrastructure/hooks/useShortcut.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: 定制 antd 组件样式优先使用 styled-components 包装 styled(Component),避免全局 classNames
项目的图标统一使用 lucide-react,不使用 emoji 作为图标
组件/Hook 顶层必须通过 useStore(selector) 使用 Zustand,禁止在 useMemo/useEffect 内部调用 store Hook
避免使用返回对象的 Zustand 选择器(如 useStore(s => ({ a: s.a, b: s.b })));应使用单字段选择器或配合 shallow 比较器
遵循 React「副作用与状态更新」规范:渲染纯函数、Effect 三分法、幂等更新、稳定引用、严格清理、禁止写回自身依赖、Provider 值 memo、外部状态 selector 稳定等
统一使用 loggerService 记录日志而不是 console
logger 使用示例中第二个参数必须为对象字面量(如 logger.error('msg', { error }))
任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
在 styled-components 中:主题相关属性使用 AntD CSS 变量(如 var(--ant-color-bg-elevated));
在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/SubtitleSearchHighlight.tsxsrc/renderer/src/pages/player/components/SubtitleListPanel.tsxsrc/renderer/src/state/stores/player-ui.store.tssrc/renderer/src/infrastructure/hooks/useShortcut.ts
**/player/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/SubtitleSearchHighlight.tsxsrc/renderer/src/pages/player/components/SubtitleListPanel.tsx
**/{stores,infrastructure,persistence}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Zustand 状态管理按三层架构组织:stores、infrastructure、persistence
Files:
src/renderer/src/state/stores/player-ui.store.tssrc/renderer/src/infrastructure/hooks/useShortcut.ts
🧠 Learnings (2)
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/player/**/*.{ts,tsx} : Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Applied to files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/SubtitleListPanel.tsx
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/*.{ts,tsx} : 在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Applied to files:
src/renderer/src/pages/player/components/SubtitleListPanel.tsx
🧬 Code graph analysis (2)
src/renderer/src/pages/player/PlayerPage.tsx (2)
src/renderer/src/state/stores/player-ui.store.ts (1)
usePlayerUIStore(119-121)src/renderer/src/pages/player/components/index.ts (1)
NavbarIcon(15-47)
src/renderer/src/pages/player/components/SubtitleListPanel.tsx (3)
src/renderer/src/infrastructure/types/subtitle.ts (1)
SubtitleItem(10-16)src/renderer/src/state/stores/player-ui.store.ts (1)
usePlayerUIStore(119-121)src/renderer/src/infrastructure/styles/theme.ts (2)
SPACING(43-58)BORDER_RADIUS(61-72)
🪛 Biome (2.1.2)
src/renderer/src/pages/player/components/SubtitleListPanel.tsx
[error] 225-225: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
[error] 384-384: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
[error] 389-389: The elements with this role can be changed to the following elements:
-
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
[error] 393-394: The elements with this role can be changed to the following elements:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: test (windows-latest, 20)
- GitHub Check: test (ubuntu-latest, 20)
- GitHub Check: test (macos-latest, 20)
🔇 Additional comments (21)
src/renderer/src/i18n/locales/zh-tw.json (1)
98-116: LGTM!繁體中文本地化新增的字幕列表相關文案結構完整,包含空狀態和搜索狀態的所有必要文案,並且正確使用了複數形式(count_one、count_other)。
src/renderer/src/pages/player/components/SubtitleSearchHighlight.tsx (1)
14-39: 組件實現正確,錯誤處理完善。
HighlightedText組件正確實現了關鍵詞高亮功能:
- 使用正則轉義處理特殊字符
- 不區分大小寫匹配
- 提供了錯誤降級處理
src/renderer/src/pages/player/PlayerPage.tsx (1)
97-97: LGTM!正確在組件頂層使用 Zustand selector,符合編碼規範中的要求。
根據學習記錄
src/renderer/src/i18n/locales/ja-jp.json (1)
137-155: LGTM!日文本地化新增的字幕列表文案結構完整,與其他語言保持一致。
src/renderer/src/infrastructure/hooks/useShortcut.ts (3)
13-21: LGTM!新增的
allowWhenTyping選項設計合理,默認值為false可防止快捷鍵在輸入時意外觸發,這對於新增的字幕搜索功能很重要。
23-66: 輸入檢測邏輯實現完善。
NON_TYPABLE_INPUT_TYPES集合和isTypingInput函數正確識別了各種輸入場景:
- 涵蓋了 contentEditable、textarea、select 等元素
- 正確排除了按鈕、checkbox 等非文本輸入類型
- 實現邏輯清晰,考慮周全
93-110: 快捷鍵防護邏輯正確。輸入狀態檢測邏輯實現合理:
- 正確獲取當前聚焦元素
- Escape 鍵被豁免(這對於關閉搜索面板很重要)
- enabled 檢查同時考慮了 options.enabled 和 shortcutConfig.enabled
src/renderer/src/i18n/locales/en-us.json (1)
89-105: LGTM!英文本地化新增的字幕列表文案清晰自然,複數形式處理正確。
src/renderer/src/state/stores/player-ui.store.ts (2)
14-16: LGTM!新增的
subtitleSearch狀態樹和三個操作方法(toggle/show/hide)設計清晰,符合狀態管理的最佳實踐。Also applies to: 27-29
100-116: 狀態操作實現正確。三個 action 的實現邏輯正確:
toggleSubtitleSearch:正確切換布爾值showSubtitleSearch:顯式設為 truehideSubtitleSearch:顯式設為 false使用 Immer Draft 進行不可變更新,符合 Zustand 最佳實踐。
src/renderer/src/pages/player/components/SubtitleListPanel.tsx (6)
59-63: LGTM!正確在組件頂層使用 Zustand selector,符合編碼規範要求。
根據學習記錄
72-84: 搜索結果計算邏輯正確。
searchResults的useMemo實現正確:
- 依賴項包含
subtitles和normalizedQuery- 使用
reduce避免中間數組分配- 大小寫不敏感匹配邏輯正確
165-177: 搜索防抖實現正確。防抖邏輯實現合理:
- 有查詢時立即設置
isSearching為 true- 300ms 後設置為 false,提供視覺反饋
- 正確清理 timeout
220-245: LGTM!搜索 UI 樣式符合設計規範。搜索控制欄的樣式實現遵循了編碼規範:
- 使用
SPACING和BORDER_RADIUS常量(符合規範要求)- 使用 AntD CSS 變量(如
var(--color-background-soft))- 動畫實現流暢,過渡效果合理
- 響應式設計考慮周全(focus-within 狀態)
根據學習記錄
Also applies to: 556-697
349-398: 搜索結果面板實現完善。
SubtitleSearchResultsPanel組件實現正確:
- 使用 i18n 提供本地化文案
- 空狀態處理清晰
- 使用
HighlightedText高亮匹配文本- 正確傳遞
onSelect回調
146-162: 保留空格键阻止逻辑
allowWhenTyping 仅阻止 useShortcut 的回调,不拦截事件冒泡;handleSearchKeyDown 中的 stopPropagation 必须保留,以防止空格键触发全局播放/暂停。src/renderer/src/i18n/locales/zh-cn.json (5)
439-439: 为 FFprobe 增加“安装”按钮文案:LGTM与 FFmpeg 匹配,提升一致性。
509-509: 确认 toggle_auto_pause 快捷键映射与 i18n key 一致:已在 usePlayerShortcuts、shortcuts.const.ts、label.ts 及各语言包中同步。
190-211: mediaServerPrompt 结构清晰,文案妥当actions、benefits、subtitle、title 结构化良好,无歧义。
请手动确认其他语言文件已同步新增 mediaServerPrompt 及其子键,避免界面多语言混用。
213-216: 补充或确认其他语言包中的字幕功能键
zh-CN 已加入 player.subtitles.hide、player.subtitles.search、player.subtitles.show。请检查并确保 ru-RU、en-US、ja-JP、zh-TW 等所有 locale 文件的 subtitles 对象中包含“hide”、“search”、“show”三键。
106-120: 确认已更新所有 mask-mode 与 display-mode 路径引用
已在 SubtitleOverlay.tsx 和 CaptionsButton.tsx 中使用player.controls.subtitle.mask-mode.*,未发现旧路径引用。
| "player": { | ||
| "subtitleList": { | ||
| "empty": { | ||
| "title": "В той же папке, что и видео, не найден подходящий файл субтитров", | ||
| "description": "Вы можете выбрать файл субтитров с помощью кнопки ниже или перетащить его в эту область" | ||
| }, | ||
| "search": { | ||
| "placeholder": "Поиск субтитров...", | ||
| "pending": "Поиск...", | ||
| "count": "Найден {{count}} файл субтитров", | ||
| "count_one": "Найден {{count}} файл субтитров", | ||
| "count_few": "Найдено {{count}} файла субтитров", | ||
| "count_many": "Найдено {{count}} файлов субтитров", | ||
| "count_other": "Найдено {{count}} субтитров", | ||
| "none": "Субтитры по запросу не найдены", | ||
| "emptyTitle": "Совпадений не найдено", | ||
| "emptySubtitle": "Попробуйте другой запрос" | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
复数形式基本正确;建议补齐字幕面板按钮文案(跨语言一致性)
- 俄语复数键 count_one/few/many/other 配置合理;占位符 {{count}} 正确。
- 但本地化中缺少字幕面板按钮键:player.subtitles.hide/search/show(在 zh-cn 已新增)。缺失将导致退回默认语言或出现占位键。
建议在本文件补齐如下键值:
"player": {
+ "subtitles": {
+ "hide": "Скрыть список субтитров",
+ "search": "Поиск субтитров",
+ "show": "Показать список субтитров"
+ },
"subtitleList": {请确认代码调用路径为 t('player.subtitleList.search.count', { count }),以使用 i18next 复数推断。若需要,我可批量为所有语言包补齐缺失键并提交补丁。
正在验证 ru-ru.json 是否包含 player.subtitles 键,并检查代码中是否使用 t('player.subtitleList.search.count', { count }) 调用。
🏁 Script executed:
#!/bin/bash
rg -nP '"subtitles"\s*:' -n src/renderer/src/i18n/locales/ru-ru.json
rg -nP '"subtitles"\s*:' -n src/renderer/src/i18n/locales/zh-cn.json
rg -nP "t\(['\"]player\.subtitleList\.search\.count" -C2 -n src/renderer/srcLength of output: 645
🏁 Script executed:
#!/bin/bash
sed -n '190,240p' src/renderer/src/i18n/locales/ru-ru.json
rg -nP '"player"\s*:' -n src/renderer/src/i18n/locales/zh-cn.json -C10Length of output: 557
🏁 Script executed:
#!/bin/bash
rg -nP '"subtitles":\s*{' -n -A15 src/renderer/src/i18n/locales/ru-ru.jsonLength of output: 0
🏁 Script executed:
#!/bin/bash
sed -n '200,260p' src/renderer/src/i18n/locales/ru-ru.jsonLength of output: 0
🏁 Script executed:
#!/bin/bash
FILE=src/renderer/src/i18n/locales/ru-ru.json
ls -l "$FILE"
wc -l "$FILE"
rg -n "Скрыть список" "$FILE"
rg -n "Показать список" "$FILE"
rg -n "Поиск субтитров" "$FILE"Length of output: 183
添加播放器字幕面板按钮翻译键
ru-ru.json 和 zh-cn.json 均缺少 player.subtitles.hide、search、show 三个键,需补充以保持跨语言一致性并避免回退默认语言。
建议在各自语言包的 player 节点下添加:
"player": {
+ "subtitles": {
+ "hide": "Скрыть список субтитров", // 隐藏字幕列表
+ "search": "Поиск субтитров", // 搜索字幕
+ "show": "Показать список субтитров" // 显示字幕列表
+ },
"subtitleList": {
… 📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "player": { | |
| "subtitleList": { | |
| "empty": { | |
| "title": "В той же папке, что и видео, не найден подходящий файл субтитров", | |
| "description": "Вы можете выбрать файл субтитров с помощью кнопки ниже или перетащить его в эту область" | |
| }, | |
| "search": { | |
| "placeholder": "Поиск субтитров...", | |
| "pending": "Поиск...", | |
| "count": "Найден {{count}} файл субтитров", | |
| "count_one": "Найден {{count}} файл субтитров", | |
| "count_few": "Найдено {{count}} файла субтитров", | |
| "count_many": "Найдено {{count}} файлов субтитров", | |
| "count_other": "Найдено {{count}} субтитров", | |
| "none": "Субтитры по запросу не найдены", | |
| "emptyTitle": "Совпадений не найдено", | |
| "emptySubtitle": "Попробуйте другой запрос" | |
| } | |
| } | |
| "player": { | |
| "subtitles": { | |
| "hide": "Скрыть список субтитров", | |
| "search": "Поиск субтитров", | |
| "show": "Показать список субтитров" | |
| }, | |
| "subtitleList": { | |
| "empty": { | |
| "title": "В той же папке, что и видео, не найден подходящий файл субтитров", | |
| "description": "Вы можете выбрать файл субтитров с помощью кнопки ниже или перетащить его в эту область" | |
| }, | |
| "search": { | |
| "placeholder": "Поиск субтитров...", | |
| "pending": "Поиск...", | |
| "count": "Найден {{count}} файл субтитров", | |
| "count_one": "Найден {{count}} файл субтитров", | |
| "count_few": "Найдено {{count}} файла субтитров", | |
| "count_many": "Найдено {{count}} файлов субтитров", | |
| "count_other": "Найдено {{count}} субтитров", | |
| "none": "Субтитры по запросу не найдены", | |
| "emptyTitle": "Совпадений не найдено", | |
| "emptySubtitle": "Попробуйте другой запрос" | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/renderer/src/i18n/locales/ru-ru.json around lines 98-116, the locale is
missing the player.subtitles keys (hide, search, show); add these three keys
under the existing player node so translations match other locales and prevent
fallback to the default language—use appropriate Russian strings such as "Скрыть
субтитры" for hide, "Поиск субтитров" for search, and "Показать субтитры" for
show (ensure JSON syntax and comma placement are correct).
| "dictionary": { | ||
| "error": "查询失败", | ||
| "loading": "查询中...", | ||
| "more_definitions": "... 还有 {{count}} 个释义", | ||
| "pronunciation": "点击发音", | ||
| "translations": "常用翻译" | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
“dictionary” 放于 player 命名空间;核对各处引用是否一致
若组件期望根级 dictionary.* 将导致缺失;请统一为 player.dictionary.* 或调整代码。
可用脚本核对所有引用与各语言包是否存在相同路径:
🏁 Script executed:
#!/bin/bash
echo "代码引用:"
rg -n -C2 "t\\(['\"]player\\.dictionary\\." src || true
echo "Locale 存在性:"
fd -a -g "src/renderer/src/i18n/locales/*.json" | xargs -I{} rg -n "player\"\\s*:\\s*\\{[^}]*\"dictionary\"" -n {}Length of output: 1654
本地化键与代码使用不一致: 代码中使用了 player.dictionary.*,但语言包(如 src/renderer/src/i18n/locales/zh-cn.json)将 dictionary 放在根级,导致翻译无法加载。请将 dictionary 节点移入 player 命名空间,或调整代码引用为根级 dictionary.*。
🤖 Prompt for AI Agents
In src/renderer/src/i18n/locales/zh-cn.json around lines 123 to 129 the
localization keys are defined under a root-level "dictionary" object while the
application expects translations under "player.dictionary.*"; move the existing
"dictionary" node inside a new or existing "player" namespace (i.e., nest
"dictionary" under "player") so keys become player.dictionary.error,
player.dictionary.loading, etc., or alternatively update code to reference the
root-level dictionary keys (change usages from player.dictionary.* to
dictionary.*) — choose the approach consistent with other locale files and keep
key structure identical after the change.
| "subtitleList": { | ||
| "empty": { | ||
| "title": "在视频文件同目录下未找到匹配的字幕文件", | ||
| "description": "您可以点击下方按钮选择字幕文件,或将字幕文件拖拽到此区域" | ||
| }, | ||
| "search": { | ||
| "placeholder": "搜索字幕...", | ||
| "pending": "搜索中...", | ||
| "count": "找到 {{count}} 条字幕", | ||
| "count_one": "找到 {{count}} 条字幕", | ||
| "count_other": "找到 {{count}} 条字幕", | ||
| "none": "未找到匹配的字幕", | ||
| "emptyTitle": "未找到匹配结果", | ||
| "emptySubtitle": "请尝试其他关键词" | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
subtitleList 搜索文案完备;可精简多余复数键(可选)
中文不需要复数变体,保留 count_other 足够;count 与 count_one 可视为冗余(不必改也无害)。
🤖 Prompt for AI Agents
In src/renderer/src/i18n/locales/zh-cn.json around lines 217 to 231, the
subtitleList.search plural keys are redundant for Chinese; remove the
unnecessary "count" and "count_one" entries and keep a single "count" or
"count_other" key (preferably "count") with value "找到 {{count}} 条字幕" to simplify
the locale file while preserving behavior.
| {isSearchVisible && ( | ||
| <ControlBar> | ||
| <SearchInputContainer> | ||
| <SearchIconWrapper> | ||
| {isSearching ? <Loader2 size={14} className="spin" /> : <Search size={14} />} | ||
| </SearchIconWrapper> | ||
| <SearchInput | ||
| ref={searchInputRef} | ||
| type="text" | ||
| value={searchQuery} | ||
| onChange={handleSearchChange} | ||
| onKeyDown={handleSearchKeyDown} | ||
| placeholder={t('player.subtitleList.search.placeholder')} | ||
| autoComplete="off" | ||
| /> | ||
| {searchQuery && ( | ||
| <ClearButton onClick={handleSearchClear}> | ||
| <X size={14} /> | ||
| </ClearButton> | ||
| )} | ||
| <CloseButton onClick={handleSearchClose}> | ||
| <X size={16} /> | ||
| </CloseButton> | ||
| </SearchInputContainer> | ||
| </ControlBar> | ||
| )} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
考慮遵循靜態分析工具的語義化建議。
Biome 靜態分析工具指出以下 ARIA role 可以替換為語義化 HTML 元素:
- Line 225:
role="complementary"→<aside> - Line 384:
role="status"→<output> - Line 389:
role="list"→<ul>或<ol> - Line 393-394:
role="listitem"→<li>
雖然當前實現功能正確,但使用語義化元素可以提高可訪問性並減少代碼複雜度。
示例重構(僅供參考):
return (
- <Container role="complementary" aria-label="caption-list">
+ <Container as="aside" aria-label="caption-list">
{/* ... */}
</Container>
)
function SubtitleSearchResultsPanel({...}) {
if (results.length === 0) {
return (
- <SearchResultsEmpty role="status">
+ <SearchResultsEmpty as="output">
{/* ... */}
</SearchResultsEmpty>
)
}
return (
- <SearchResultsList role="list" aria-label="subtitle-search-results">
+ <SearchResultsList as="ul" aria-label="subtitle-search-results">
{results.map(({ subtitle, index }) => (
- <SearchResultItem key={subtitle.id} role="listitem" /* ... */>
+ <SearchResultItem as="li" key={subtitle.id} /* ... */>
{/* ... */}
</SearchResultItem>
))}
</SearchResultsList>
)
}Also applies to: 349-398
🧰 Tools
🪛 Biome (2.1.2)
[error] 225-225: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/SubtitleListPanel.tsx around lines
220-245 (and also apply similar changes to 349-398), several elements use ARIA
role attributes where semantic HTML elements would be appropriate: replace
elements with role="complementary" with an <aside>, role="status" with an
<output>, role="list" with a semantic <ul> or <ol>, and role="listitem" with
<li>; update the JSX structure accordingly (move children into the new semantic
containers, remove the redundant role attributes, and ensure any styling or
refs/props are preserved) so accessibility and static-analysis warnings are
resolved.
| <Tooltip title={t('player.subtitles.search')} mouseEnterDelay={0.8}> | ||
| <NavbarIcon onClick={toggleSubtitleSearch}> | ||
| <Search size={18} /> | ||
| </NavbarIcon> | ||
| </Tooltip> |
There was a problem hiding this comment.
🧩 Analysis chain
驗證 i18n 鍵是否存在。
第 605 行引用了翻譯鍵 'player.subtitles.search',但在提供的所有本地化文件中,新增的鍵路徑是 player.subtitleList.* 而非 player.subtitles.search。請確認此鍵是否存在,或者應該使用不同的鍵。
運行以下腳本驗證翻譯鍵的使用:
🏁 Script executed:
#!/bin/bash
# 描述:在本地化文件中搜索 player.subtitles.search 鍵
# 預期:找到該鍵的定義或確認應使用其他鍵
# 搜索 player.subtitles.search
echo "=== 搜索 player.subtitles.search ==="
rg -n "subtitles.*search" src/renderer/src/i18n/locales/
# 搜索所有 player.subtitle 相關的鍵
echo "=== 搜索所有 player 下的 subtitle 相關鍵 ==="
rg -n '"subtitle' src/renderer/src/i18n/locales/ -A 2Length of output: 3807
确保 i18n 键 'player.subtitles.search' 在所有本地化文件中定义
zh-CN 已包含该键,但 en-US、ru-RU、ja-JP 等缺失,请在对应 locales 文件中补充该翻译。
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/PlayerPage.tsx around lines 605 to 609 the
Tooltip uses the i18n key 'player.subtitles.search' but that key only exists in
zh-CN; add the missing 'player.subtitles.search' entry to each locales file
(e.g., src/renderer/locales/en-US.json, ru-RU.json, ja-JP.json and any other
supported locale files) with appropriate translations, keep the JSON structure
consistent with existing locale files, then run the i18n lint/build step (or the
project's locale validation script) to ensure no missing keys remain.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Summary
- Added localized tooltip strings for the subtitle search button across the en-US, ja-JP, ru-RU, and zh-TW locale files to match the new
player.subtitles.searchusage and avoid fallback text. src/renderer/src/i18n/locales/en-us.jsonL90-L94 src/renderer/src/i18n/locales/ja-jp.jsonL138-L143 src/renderer/src/i18n/locales/ru-ru.jsonL99-L103 src/renderer/src/i18n/locales/zh-tw.jsonL99-L103
Testing
⚠️ Not run (not requested)
Summary by CodeRabbit
Summary by CodeRabbit