diff --git a/frontend/src/components/CalendarSubscriptionModal.vue b/frontend/src/components/CalendarSubscriptionModal.vue index e1791c4..7657fe7 100644 --- a/frontend/src/components/CalendarSubscriptionModal.vue +++ b/frontend/src/components/CalendarSubscriptionModal.vue @@ -79,16 +79,16 @@ async function copyUrl() {

{{ tr('快速配置指南', 'Quick Setup Guide') }}

- 🍎 {{ tr('Apple 日历 (Mac / iOS)', 'Apple Calendar (Mac / iOS)') }} - {{ tr('文件 ➔ 新建日历订阅 ➔ 粘贴上方链接 ➔ 自动刷新建议选择“每小时”。', 'File ➔ New Calendar Subscription ➔ Paste URL ➔ Set Auto-refresh to Every Hour.') }} + {{ tr('Apple 日历 (Mac / iOS)', 'Apple Calendar (Mac / iOS)') }} + {{ tr('文件 / 新建日历订阅 / 粘贴上方链接 / 自动刷新建议选择“每小时”。', 'File / New Calendar Subscription / Paste URL / Set Auto-refresh to Every Hour.') }}
- 📅 Google Calendar - {{ tr('其他日历旁点击 “+” ➔ 通过网址添加 ➔ 粘贴链接 ➔ 添加日历。', 'Click "+" next to Other calendars ➔ From URL ➔ Paste URL ➔ Add calendar.') }} + Google Calendar + {{ tr('其他日历旁点击 “+” / 通过网址添加 / 粘贴链接 / 添加日历。', 'Click "+" next to Other calendars / From URL / Paste URL / Add calendar.') }}
- 📧 Microsoft Outlook - {{ tr('添加日历 ➔ 从 Web 订阅 ➔ 粘贴链接并保存。', 'Add calendar ➔ Subscribe from web ➔ Paste URL and save.') }} + Microsoft Outlook + {{ tr('添加日历 / 从 Web 订阅 / 粘贴链接并保存。', 'Add calendar / Subscribe from web / Paste URL and save.') }}
diff --git a/frontend/src/components/WebhookNotifyModal.vue b/frontend/src/components/WebhookNotifyModal.vue index 4ad8439..2f1adb0 100644 --- a/frontend/src/components/WebhookNotifyModal.vue +++ b/frontend/src/components/WebhookNotifyModal.vue @@ -80,7 +80,7 @@ async function sendNotification() {
{{ tr('推送内容预览', 'Card Preview') }} -
📋 {{ tr('会议纪要:', 'Meeting Summary: ') }}{{ meeting.title }}
+
{{ tr('会议纪要:', 'Meeting Summary: ') }}{{ meeting.title }}
{{ tr('状态:', 'Status: ') }}{{ meeting.status === 'completed' ? tr('已完成', 'Completed') : tr('进行中', 'In Progress') }} · {{ tr('议题数:', 'Agendas: ') }}{{ meeting.agenda_items.length }} diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index d613c16..9883863 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -231,15 +231,15 @@ export const SEEDED_ZH_TO_EN: Record = { '聚焦会前准备、会中专注研讨与会后归档闭环。': 'Focus on pre-meeting prep, live focused discussion, and post-meeting archive loop.', 'Q3 架构演进与中英双语体验评审': 'Q3 Architecture Evolution & Bilingual UX Review', - '评审页内 ✨ AI 智能提炼、双向行动项状态机及中英双语无缝切换体验。': - 'Review in-place ✨ AI Smart Extract, two-way action state machine, and seamless bilingual switching.', - '页内 ✨ AI 智能提炼与结构化产出': 'In-place ✨ AI Smart Extract & Structured Outcomes', + '评审页内 AI 智能提炼、双向行动项状态机及中英双语无缝切换体验。': + 'Review in-place AI Smart Extract, two-way action state machine, and seamless bilingual switching.', + '页内 AI 智能提炼与结构化产出': 'In-place AI Smart Extract & Structured Outcomes', '围绕会议记录与 AI 提炼流程达成以下结论:': 'Key conclusions on meeting notes and AI extraction workflow:', - '团队一致决定采用页内 ✨ AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': - 'Unanimously decided to use in-place ✨ AI Smart Extract directly in agenda notes, removing redundant modals', - '采用页内 ✨ AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': - 'Use in-place ✨ AI Smart Extract directly in agenda notes, removing redundant modals', + '团队一致决定采用页内 AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': + 'Unanimously decided to use in-place AI Smart Extract directly in agenda notes, removing redundant modals', + '采用页内 AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': + 'Use in-place AI Smart Extract directly in agenda notes, removing redundant modals', '请 @admin 完善中英双语标签 (@决策/@Decision、@行动/@Action) 解析与单元测试': 'Please @admin refine bilingual tag (@Decision, @Action) parsing and unit tests', '完善中英双语标签 (@决策/@Decision、@行动/@Action) 解析与单元测试': @@ -266,8 +266,8 @@ export const SEEDED_ZH_TO_EN: Record = { '周会例行同步': 'Weekly Sync', '双周架构评审': 'Bi-weekly Architecture Review', '每周三 10:00 固定同步': 'Every Wednesday at 10:00', - '确认采用页内 ✨ AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': - 'Confirm in-place ✨ AI Smart Extract directly in agenda notes, removing redundant modals', + '确认采用页内 AI 智能提炼直接写入当前议题记录,彻底移除冗余弹窗': + 'Confirm in-place AI Smart Extract directly in agenda notes, removing redundant modals', } export const SEEDED_EN_TO_ZH: Record = Object.fromEntries( @@ -279,7 +279,8 @@ const SORTED_EN_ENTRIES = Object.entries(SEEDED_EN_TO_ZH).sort((a, b) => b[0].le export function translateSeededText(text: string, lang: Locale = locale.value): string { if (!text) return text - const trimmed = text.trim() + const cleaned = text.replace(/\u2728\s*/g, '') + const trimmed = cleaned.trim() if (lang === 'en') { if (SEEDED_ZH_TO_EN[trimmed]) return SEEDED_ZH_TO_EN[trimmed] const discussMatch = trimmed.match(/^###\s+(.+?)\s+·\s+讨论要点$/) @@ -310,7 +311,7 @@ export function translateSeededText(text: string, lang: Locale = locale.value): if (qFallbackMatch) { return `Cross-module boundary details for "${translateSeededText(qFallbackMatch[1], 'en')}" pending final confirmation` } - let replaced = text + let replaced = cleaned for (const [zh, en] of SORTED_ZH_ENTRIES) { if (zh.length >= 4 && replaced.includes(zh)) { replaced = replaced.split(zh).join(en) @@ -348,7 +349,7 @@ export function translateSeededText(text: string, lang: Locale = locale.value): if (enQFallbackMatch) { return `「${translateSeededText(enQFallbackMatch[1], 'zh')}」跨模块边界细节待最终确认` } - let replaced = text + let replaced = cleaned for (const [en, zh] of SORTED_EN_ENTRIES) { if (en.length >= 6 && replaced.includes(en)) { replaced = replaced.split(en).join(zh) diff --git a/frontend/src/tests/focus-and-integrations.test.ts b/frontend/src/tests/focus-and-integrations.test.ts index 0f078b0..e97b1ea 100644 --- a/frontend/src/tests/focus-and-integrations.test.ts +++ b/frontend/src/tests/focus-and-integrations.test.ts @@ -311,7 +311,7 @@ describe('P0 Features: Bidirectional Actions, Focus Mode, Calendar & Webhooks', expect(screen.getByLabelText('议题记录')).toHaveValue('正在输入的未保存笔记内容') }) - it('structures and extracts decisions, actions, and open questions in-place via ✨ AI 智能提炼', async () => { + it('structures and extracts decisions, actions, and open questions in-place via AI 智能提炼', async () => { const meeting = meetingFixture() apiMock.mockImplementation((url: string, init?: RequestInit) => { if (url === '/api/v1/meetings/m1/ingest' && init?.method === 'POST') { diff --git a/frontend/src/tests/ui-foundation.test.ts b/frontend/src/tests/ui-foundation.test.ts index d3b1b86..faedeff 100644 --- a/frontend/src/tests/ui-foundation.test.ts +++ b/frontend/src/tests/ui-foundation.test.ts @@ -316,7 +316,7 @@ describe('icon glyph policy', () => { it('uses lucide svg icons instead of unicode glyphs in the source tree', () => { // Spec §4.3: functional icons must be Lucide SVG wrapped in NIcon; the brand "M" // mark (a logo, not a functional icon) is explicitly kept and is not in this list. - const forbidden = ['⌕', '•••', '✓', '○', '→', '⌁', '⌄', '×', '✕', '✦', '◎', '●'] + const forbidden = ['⌕', '•••', '✓', '○', '→', '⌁', '⌄', '×', '✕', '✦', '◎', '●', '📋', '🍎', '📅', '📧', '✨'] const sources = import.meta.glob(['../**/*.vue', '../**/*.ts', '../**/*.css'], { query: '?raw', import: 'default',