feat: implement trilingual i18n and locale export pipeline - #23
Conversation
There was a problem hiding this comment.
Pull request overview
此 PR 在 Confession 的 Webview/Extension/Backend 之間導入三語系(zh-TW / zh-CN / en)i18n,並讓匯出流程(CSV/Markdown/PDF)可依 locale 在後端產生本地化內容,同時把 ui.language 納入設定 schema、正規化與同步管線。
Changes:
- 新增
PluginConfig.ui.language(含auto)並貫穿 web/extension 的 default、schema、normalize、同步寫入 VS Code settings 與.confession/config.json。 - 新增 shared i18n core(locale resolve + date/time formatter + options),並套用到 dashboard/settings/trend/list/detail/loading 等 UI 流程。
/api/export新增locale?;CSV/Markdown/PDF 渲染器支援在後端輸出本地化標題/文案(JSON/SARIF schema 維持不變),並補齊對應測試與文件規範。
Reviewed changes
Copilot reviewed 42 out of 42 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/server/storage/snapshot-codec.ts | DEFAULT_CONFIG 與 config normalize 新增 ui.language。 |
| web/src/server/storage.test.ts | 測試預設 config 補上 ui.language。 |
| web/src/server/routes/export.ts | /api/export 支援 locale,並在未指定時由 config 解析 locale。 |
| web/src/server/routes/export.test.ts | 匯出測試新增 locale 覆蓋與 config fallback 情境;改用 TextDecoder。 |
| web/src/server/routes/config.ts | config API schema/normalize/merge 支援 ui.language。 |
| web/src/server/export/renderers.ts | CSV/Markdown 依 locale 本地化欄位標題/章節文案。 |
| web/src/server/export/printable-text.ts | 新增可列印 HTML 的三語系文案表。 |
| web/src/server/export/printable-html.ts | PDF(HTML) 輸出依 locale 本地化並套用 html lang。 |
| web/src/server/export/common.ts | groupBySeverity 支援 locale 化的 section label。 |
| web/src/server/agents/messages.pbt.test.ts | PBT config arb 補上 ui.language。 |
| web/src/common/libs/ui-messages.ts | sync phase / engine mode / more-info 文案支援 locale。 |
| web/src/common/libs/types.ts | 新增 UiLanguage 與 PluginConfig.ui;export_pdf message 加入 locale?。 |
| web/src/common/libs/i18n.ts | 新增 locale resolve/detect 與日期時間格式化工具。 |
| web/src/common/libs/i18n.test.ts | i18n resolver 單元測試。 |
| web/src/common/libs/dashboard-insights.ts | 趨勢洞察/總結卡等文案支援 locale;新增 getPresetLabel。 |
| web/src/common/libs/dashboard-insights.test.ts | dashboard insights 新增 en/zh-CN 覆蓋測試。 |
| web/src/common/libs/atoms.ts | configAtom 預設值補上 ui.language。 |
| web/src/common/hooks/use-i18n.ts | 新增 i18n hook,從 config 解析 locale 並提供 t/formatters/options。 |
| web/src/common/hooks/use-config.ts | 更新 config merge 將 ui 納入。 |
| web/src/common/components/vulnerability-list/main.tsx | 列表日期格式化與 preset label locale 化。 |
| web/src/common/components/vulnerability-detail/main.tsx | 事件時間顯示改用 locale formatter。 |
| web/src/common/components/trend-chart.tsx | 趨勢圖文案/tooltip/preset label locale 化。 |
| web/src/common/components/settings/main.tsx | 設定頁新增「介面語言」選項並讓各區塊文案 locale 化。 |
| web/src/common/components/dashboard/main.tsx | dashboard 匯出與 preset label/時間顯示 locale 化;export request 帶 locale。 |
| web/src/app/vulnerability-detail/loading.tsx | Loading 文案 locale 化。 |
| web/src/app/vulnerabilities/loading.tsx | Loading 文案 locale 化。 |
| web/src/app/settings/loading.tsx | Loading 文案 locale 化。 |
| web/src/app/loading.tsx | Loading 文案 locale 化。 |
| extension/src/webview.ts | export_pdf request body 轉送 locale;寫入 settings 補上 ui.language。 |
| extension/src/webview.test.ts | 測試 config/arb 補上 ui.language。 |
| extension/src/types.ts | extension 端 PluginConfig/WebToExtMsg 補上 ui.language/locale?。 |
| extension/src/ignore-file.ts | .confession/config.json normalize/default 補上 ui.language。 |
| extension/src/ignore-file.test.ts | ignore-file 測試 default config 補上 ui.language。 |
| extension/src/extension.ts | 從 VS Code settings 與 scoped config 合併讀取 ui.language。 |
| extension/package.json | 新增 confession.ui.language 設定項宣告。 |
| AGENTS.md | 更新 export locale 規範與 config 同步規範;文件樹新增 printable-text。 |
| .kiro/steering/structure.md | 文件樹新增 printable-text;標註 i18n core/hook 位置。 |
| .kiro/steering/product.md | 補充 Webview 三語系與 auto 行為。 |
| .kiro/steering/language-preferences.md | 補充 Webview 三語系與 auto 行為。 |
| .kiro/steering/extension-guide.md | 補充 confession.ui.language 與 auto 持續跟隨宿主語系。 |
| .kiro/steering/code-conventions.md | PluginConfig 定義補充 ui 設定。 |
| .kiro/steering/api-standards.md | /api/export 與 /api/config 規範補上 locale / ui.language。 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const VulnCard: React.FC<VulnCardProps> = ({ vuln, onClick, onMore }) => { | ||
| const { formatDate } = useI18n() | ||
| return ( | ||
| <div |
There was a problem hiding this comment.
VulnCard calls useI18n() per card render. In a large list/grid this creates many hook subscriptions to config state and can amplify re-render cost when language changes. Consider calling useI18n() once in the parent and passing formatDate (or locale) down as a prop to VulnCard.
| import { type ResolvedLocale,resolveLocale } from '@/libs/i18n' | ||
| import type { UiLanguage } from '@/libs/types' | ||
|
|
There was a problem hiding this comment.
import { type ResolvedLocale,resolveLocale } ... is missing a space after the comma and will likely fail formatting/lint. Consider reformatting to keep imports consistent (e.g. resolveLocale before type ResolvedLocale and ensure proper spacing).
| export function useI18n() { | ||
| const config = useConfig() | ||
| const language: UiLanguage = config.ui?.language ?? 'auto' | ||
| const locale: ResolvedLocale = useMemo( | ||
| () => resolveLocale(language), | ||
| [language], | ||
| ) |
There was a problem hiding this comment.
ui.language=auto is supposed to keep following the host locale, but this hook memoizes resolveLocale(language) and only re-computes when language changes. If the host locale changes while language stays auto, locale won’t update; consider subscribing to languagechange (or tracking navigator.language/languages) and including it in the dependencies.
| function getPresetLabel( | ||
| preset: VulnerabilityFilterPreset, | ||
| locale: ResolvedLocale, | ||
| ): string { | ||
| switch (preset) { | ||
| case 'critical_open': | ||
| return lt(locale, { | ||
| 'zh-TW': '嚴重級待處理', | ||
| 'zh-CN': '严重级待处理', | ||
| en: 'Open Critical', | ||
| }) | ||
| case 'high_open': | ||
| return lt(locale, { | ||
| 'zh-TW': '高風險待處理', | ||
| 'zh-CN': '高风险待处理', | ||
| en: 'Open High Risk', | ||
| }) | ||
| default: | ||
| return lt(locale, { | ||
| 'zh-TW': '全部待處理', | ||
| 'zh-CN': '全部待处理', | ||
| en: 'All Open', | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
This file re-implements getPresetLabel(...) locally even though an equivalent helper already exists in @/libs/dashboard-insights. Duplicating preset label logic risks drift across UI surfaces; consider importing and reusing the shared helper instead of maintaining a second copy here.
| analysis: { triggerMode: 'manual', depth: 'standard', debounceMs: 500 }, | ||
| ignore: { paths: [], types: [] }, | ||
| api: { baseUrl: 'http://localhost:3000', mode: 'local' }, | ||
| ui: { language: 'auto' }, |
There was a problem hiding this comment.
ui is mis-indented in this object literal (it’s not aligned with the other top-level config keys). This likely indicates a formatting issue that will either fail linting or make future diffs noisy; please run the formatter so the ui block aligns with llm/analysis/ignore/api.
| ui: { language: 'auto' }, | |
| ui: { language: 'auto' }, |
| analysis: { triggerMode: 'manual', depth: 'standard', debounceMs: 500 }, | ||
| ignore: { paths: [], types: [] }, | ||
| api: { baseUrl: 'http://localhost:3000', mode: 'local' }, | ||
| ui: { language: 'auto' }, |
There was a problem hiding this comment.
Same indentation/formatting issue here: ui is not aligned with the other keys in mockConfig. Running the formatter (or fixing whitespace) will avoid lint failures and keep object literals consistent.
| ui: { language: 'auto' }, | |
| ui: { language: 'auto' }, |
Summary
ui.language(auto|zh-TW|zh-CN|en) across web/extension config schema, normalization, and syncresolveLocale,useI18n) and apply to dashboard/settings/trend/list/detail/loading flows/api/exportlocale, csv/markdown/pdf localized, json/sarif schema unchanged)Validation