| 1 | import { useCallback } from "react"; |
| 2 | import { useI18n, type DictKey, type Locale } from "../../../lib/i18n"; |
| 3 | |
| 4 | export const heartbeatFeatureEn = { |
| 5 | "heartbeat.addTaskToScope": "Add task in {name}", |
| 6 | "heartbeat.btnNew": "New", |
| 7 | "heartbeat.btnPause": "Pause", |
| 8 | "heartbeat.btnStart": "Start", |
| 9 | "heartbeat.clickPause": "Click to pause", |
| 10 | "heartbeat.clickStart": "Click to start", |
| 11 | "heartbeat.cronAt": "at {time}", |
| 12 | "heartbeat.cronEveryHour": "every {n} hour(s)", |
| 13 | "heartbeat.cronEveryMin": "every {n} min", |
| 14 | "heartbeat.cronHourly": "hourly", |
| 15 | "heartbeat.cronNextRun": "Next run:", |
| 16 | "heartbeat.cronOnHour": "at minute {n} of every hour", |
| 17 | "heartbeat.cronPlaceholder": "0 * * * *", |
| 18 | "heartbeat.cronWeekdayFri": "Fri", |
| 19 | "heartbeat.cronWeekdayMon": "Mon", |
| 20 | "heartbeat.cronWeekdaySat": "Sat", |
| 21 | "heartbeat.cronWeekdaySun": "Sun", |
| 22 | "heartbeat.cronWeekdayThu": "Thu", |
| 23 | "heartbeat.cronWeekdayTue": "Tue", |
| 24 | "heartbeat.cronWeekdayWed": "Wed", |
| 25 | "heartbeat.cycleTime": "Time", |
| 26 | "heartbeat.cycleTimeAt": " (Time: {time})", |
| 27 | "heartbeat.daysAgo": "{n} d ago", |
| 28 | "heartbeat.detailTitle": "Task detail", |
| 29 | "heartbeat.discardChanges": "Discard unsaved changes?", |
| 30 | "heartbeat.dueSoon": "due soon", |
| 31 | "heartbeat.filterStatus": "Status", |
| 32 | "heartbeat.freqCron": "Custom", |
| 33 | "heartbeat.frequencyConversionFailed": "This schedule cannot be converted without changing when it runs.", |
| 34 | "heartbeat.heroSubtitle": "Let AI schedule tasks, set reminders, or monitor updates", |
| 35 | "heartbeat.heroTitle": "Automation", |
| 36 | "heartbeat.hoursAgo": "{n} hr ago", |
| 37 | "heartbeat.joinComma": ", ", |
| 38 | "heartbeat.later": "", |
| 39 | "heartbeat.minutesAgo": "{n} min ago", |
| 40 | "heartbeat.monthDay": "", |
| 41 | "heartbeat.newTask": "New", |
| 42 | "heartbeat.nextRun": "Next run in", |
| 43 | "heartbeat.remaining": "in ", |
| 44 | "heartbeat.runHistory": "Run history", |
| 45 | "heartbeat.runHistoryEmpty": "No runs yet", |
| 46 | "heartbeat.runHistoryNoTopic": "No conversation", |
| 47 | "heartbeat.saveFailed": "Could not save because automations changed elsewhere. Your draft is still here; review it and try again.", |
| 48 | "heartbeat.selectTask": "Select a task to view details", |
| 49 | "heartbeat.statusDisabled": "Paused", |
| 50 | "heartbeat.statusEnabled": "Active", |
| 51 | "heartbeat.sugDailyReview": "Daily review", |
| 52 | "heartbeat.sugDailyReviewDesc": "Read today's conversations across projects and review what was done, what's left, and tomorrow's plan", |
| 53 | "heartbeat.sugDailyReviewFreq": "Daily at 20:00", |
| 54 | "heartbeat.sugDailyReviewPrompt": "Read all of today's conversations and produce a multi-project review: what was completed, what issues remain, and what to do tomorrow. Output a structured review.", |
| 55 | "heartbeat.sugDownloads": "Downloads folder tidy-up report", |
| 56 | "heartbeat.sugDownloadsDesc": "Scan the downloads folder read-only and produce a categorized tidy-up report (no file moves)", |
| 57 | "heartbeat.sugDownloadsFreq": "Fridays at 16:00", |
| 58 | "heartbeat.sugDownloadsPrompt": "Scan the downloads folder of the current OS read-only (macOS/Linux: ~/Downloads, Windows: %USERPROFILE%\\Downloads), group files by type, and output a tidy-up suggestion report. Do not move or delete any files.", |
| 59 | "heartbeat.sugProductUpdate": "Product update digest", |
| 60 | "heartbeat.sugProductUpdateDesc": "Fetch the latest Reasonix release notes and learn about new capabilities", |
| 61 | "heartbeat.sugProductUpdateFreq": "Daily at 12:00", |
| 62 | "heartbeat.sugProductUpdatePrompt": "Visit https://reasonix.io/changelog/ to read the latest release notes, highlight recent new capabilities (New) and notable fixes in a bullet summary of at most 10 items. If the page is unreachable, fall back to release-notes/releases.json in the root of the local DeepSeek-Reasonix repository (if unsure where the repo is, locate the file with find).", |
| 63 | "heartbeat.suggestions": "Suggestions", |
| 64 | "heartbeat.suggestionsHint": "Create common automation tasks in one click", |
| 65 | "heartbeat.unitDay": "d", |
| 66 | "heartbeat.viewFlat": "Flat list", |
| 67 | "heartbeat.viewGrouped": "Group by project", |
| 68 | } as const; |
| 69 | |
| 70 | export type HeartbeatFeatureKey = keyof typeof heartbeatFeatureEn; |
| 71 | export type HeartbeatTranslationKey = DictKey | HeartbeatFeatureKey; |
| 72 | export type HeartbeatTranslator = ( |
| 73 | key: HeartbeatTranslationKey, |
| 74 | vars?: Record<string, string | number>, |
| 75 | ) => string; |
| 76 | |
| 77 | const heartbeatFeatureZh = { |
| 78 | "heartbeat.addTaskToScope": "在 {name} 中添加任务", |
| 79 | "heartbeat.btnNew": "新建", |
| 80 | "heartbeat.btnPause": "暂停", |
| 81 | "heartbeat.btnStart": "启动", |
| 82 | "heartbeat.clickPause": "点击暂停", |
| 83 | "heartbeat.clickStart": "点击启动", |
| 84 | "heartbeat.cronAt": "在 {time}", |
| 85 | "heartbeat.cronEveryHour": "每{n}小时", |
| 86 | "heartbeat.cronEveryMin": "每{n}分钟", |
| 87 | "heartbeat.cronHourly": "整点", |
| 88 | "heartbeat.cronNextRun": "下次运行:", |
| 89 | "heartbeat.cronOnHour": "每小时 {n} 分", |
| 90 | "heartbeat.cronPlaceholder": "0 * * * *", |
| 91 | "heartbeat.cronWeekdayFri": "周五", |
| 92 | "heartbeat.cronWeekdayMon": "周一", |
| 93 | "heartbeat.cronWeekdaySat": "周六", |
| 94 | "heartbeat.cronWeekdaySun": "周日", |
| 95 | "heartbeat.cronWeekdayThu": "周四", |
| 96 | "heartbeat.cronWeekdayTue": "周二", |
| 97 | "heartbeat.cronWeekdayWed": "周三", |
| 98 | "heartbeat.cycleTime": "时间", |
| 99 | "heartbeat.cycleTimeAt": "(时间:{time})", |
| 100 | "heartbeat.daysAgo": "{n} 天前", |
| 101 | "heartbeat.detailTitle": "任务详情", |
| 102 | "heartbeat.discardChanges": "放弃未保存的修改?", |
| 103 | "heartbeat.dueSoon": "即将触发", |
| 104 | "heartbeat.filterStatus": "状态筛选", |
| 105 | "heartbeat.freqCron": "自定义", |
| 106 | "heartbeat.frequencyConversionFailed": "该调度无法在不改变运行时间的前提下转换。", |
| 107 | "heartbeat.heroSubtitle": "让 AI 安排任务、设置提醒或监测更新", |
| 108 | "heartbeat.heroTitle": "自动化", |
| 109 | "heartbeat.hoursAgo": "{n} 小时前", |
| 110 | "heartbeat.joinComma": "、", |
| 111 | "heartbeat.later": "后", |
| 112 | "heartbeat.minutesAgo": "{n} 分钟前", |
| 113 | "heartbeat.monthDay": "日", |
| 114 | "heartbeat.newTask": "新建", |
| 115 | "heartbeat.nextRun": "下次运行", |
| 116 | "heartbeat.remaining": "剩余", |
| 117 | "heartbeat.runHistory": "运行历史记录", |
| 118 | "heartbeat.runHistoryEmpty": "暂无执行记录", |
| 119 | "heartbeat.runHistoryNoTopic": "无会话", |
| 120 | "heartbeat.saveFailed": "自动化配置已在其他位置更改,本次保存失败。草稿仍已保留,请确认后重试。", |
| 121 | "heartbeat.selectTask": "选择一个任务查看详情", |
| 122 | "heartbeat.statusDisabled": "已暂停", |
| 123 | "heartbeat.statusEnabled": "活跃", |
| 124 | "heartbeat.sugDailyReview": "每日复盘", |
| 125 | "heartbeat.sugDailyReviewDesc": "读取今天所有项目的对话,复盘当天完成的工作、遗留问题与明日计划", |
| 126 | "heartbeat.sugDailyReviewFreq": "每天 20:00", |
| 127 | "heartbeat.sugDailyReviewPrompt": "读取今天的全部对话,按项目进行多项目复盘:今天完成了什么、有什么遗留问题、明天计划做什么。输出结构化复盘。", |
| 128 | "heartbeat.sugDownloads": "下载文件夹整理报告", |
| 129 | "heartbeat.sugDownloadsDesc": "只读扫描下载文件夹,生成分类整理建议(不移动文件)", |
| 130 | "heartbeat.sugDownloadsFreq": "每周五 16:00", |
| 131 | "heartbeat.sugDownloadsPrompt": "只读扫描当前操作系统的下载文件夹(macOS/Linux 为 ~/Downloads,Windows 为 %USERPROFILE%\\Downloads),按类型整理出分类清单,输出整理建议报告。不要移动或删除任何文件。", |
| 132 | "heartbeat.sugProductUpdate": "产品更新速览", |
| 133 | "heartbeat.sugProductUpdateDesc": "查询 Reasonix 最新发布说明,了解新能力与用法", |
| 134 | "heartbeat.sugProductUpdateFreq": "每天 12:00", |
| 135 | "heartbeat.sugProductUpdatePrompt": "访问 https://reasonix.io/changelog/ 查看最新的发布说明,重点介绍最近版本的新能力(New/新增)和重要修复,用要点式摘要输出,控制在 10 条以内。如果页面无法访问,改用本地 DeepSeek-Reasonix 代码仓库根目录下的 release-notes/releases.json 兜底(若不确定仓库位置,用 find 命令搜索该文件)。", |
| 136 | "heartbeat.suggestions": "建议", |
| 137 | "heartbeat.suggestionsHint": "一键创建常用自动化任务", |
| 138 | "heartbeat.unitDay": "天", |
| 139 | "heartbeat.viewFlat": "纯列表", |
| 140 | "heartbeat.viewGrouped": "按项目分组", |
| 141 | } satisfies Record<HeartbeatFeatureKey, string>; |
| 142 | |
| 143 | const heartbeatFeatureZhTW = { |
| 144 | "heartbeat.addTaskToScope": "在 {name} 中添加任务", |
| 145 | "heartbeat.btnNew": "新增", |
| 146 | "heartbeat.btnPause": "暫停", |
| 147 | "heartbeat.btnStart": "啟動", |
| 148 | "heartbeat.clickPause": "點擊暫停", |
| 149 | "heartbeat.clickStart": "點擊啟動", |
| 150 | "heartbeat.cronAt": "在 {time}", |
| 151 | "heartbeat.cronEveryHour": "每{n}小時", |
| 152 | "heartbeat.cronEveryMin": "每{n}分鐘", |
| 153 | "heartbeat.cronHourly": "整點", |
| 154 | "heartbeat.cronNextRun": "下次運行:", |
| 155 | "heartbeat.cronOnHour": "每小時 {n} 分", |
| 156 | "heartbeat.cronPlaceholder": "0 * * * *", |
| 157 | "heartbeat.cronWeekdayFri": "週五", |
| 158 | "heartbeat.cronWeekdayMon": "週一", |
| 159 | "heartbeat.cronWeekdaySat": "週六", |
| 160 | "heartbeat.cronWeekdaySun": "週日", |
| 161 | "heartbeat.cronWeekdayThu": "週四", |
| 162 | "heartbeat.cronWeekdayTue": "週二", |
| 163 | "heartbeat.cronWeekdayWed": "週三", |
| 164 | "heartbeat.cycleTime": "時間", |
| 165 | "heartbeat.cycleTimeAt": "(時間:{time})", |
| 166 | "heartbeat.daysAgo": "{n} 天前", |
| 167 | "heartbeat.detailTitle": "任務詳情", |
| 168 | "heartbeat.discardChanges": "放棄未儲存的修改?", |
| 169 | "heartbeat.dueSoon": "即將觸發", |
| 170 | "heartbeat.filterStatus": "狀態篩選", |
| 171 | "heartbeat.freqCron": "自訂", |
| 172 | "heartbeat.frequencyConversionFailed": "該排程無法在不改變執行時間的前提下轉換。", |
| 173 | "heartbeat.heroSubtitle": "讓 AI 安排任務、設定提醒或監測更新", |
| 174 | "heartbeat.heroTitle": "自動化", |
| 175 | "heartbeat.hoursAgo": "{n} 小時前", |
| 176 | "heartbeat.joinComma": "、", |
| 177 | "heartbeat.later": "後", |
| 178 | "heartbeat.minutesAgo": "{n} 分鐘前", |
| 179 | "heartbeat.monthDay": "日", |
| 180 | "heartbeat.newTask": "新建", |
| 181 | "heartbeat.nextRun": "下次運行", |
| 182 | "heartbeat.remaining": "剩餘", |
| 183 | "heartbeat.runHistory": "運行歷史記錄", |
| 184 | "heartbeat.runHistoryEmpty": "暫無執行記錄", |
| 185 | "heartbeat.runHistoryNoTopic": "無會話", |
| 186 | "heartbeat.saveFailed": "自動化設定已在其他位置變更,本次儲存失敗。草稿仍已保留,請確認後重試。", |
| 187 | "heartbeat.selectTask": "選擇一個任務查看詳情", |
| 188 | "heartbeat.statusDisabled": "已暫停", |
| 189 | "heartbeat.statusEnabled": "活躍", |
| 190 | "heartbeat.sugDailyReview": "每日覆盤", |
| 191 | "heartbeat.sugDailyReviewDesc": "讀取今天所有專案的對話,覆盤當天完成的工作、遺留問題與明日計劃", |
| 192 | "heartbeat.sugDailyReviewFreq": "每天 20:00", |
| 193 | "heartbeat.sugDailyReviewPrompt": "讀取今天的全部對話,按專案進行多專案覆盤:今天完成了什麼、有什麼遺留問題、明天計劃做什麼。輸出結構化覆盤。", |
| 194 | "heartbeat.sugDownloads": "下載資料夾整理報告", |
| 195 | "heartbeat.sugDownloadsDesc": "唯讀掃描下載資料夾,產生分類整理建議(不移動檔案)", |
| 196 | "heartbeat.sugDownloadsFreq": "每週五 16:00", |
| 197 | "heartbeat.sugDownloadsPrompt": "唯讀掃描目前作業系統的下載資料夾(macOS/Linux 為 ~/Downloads,Windows 為 %USERPROFILE%\\Downloads),按類型整理出分類清單,輸出整理建議報告。不要移動或刪除任何檔案。", |
| 198 | "heartbeat.sugProductUpdate": "產品更新速覽", |
| 199 | "heartbeat.sugProductUpdateDesc": "查詢 Reasonix 最新發佈說明,了解新能力與用法", |
| 200 | "heartbeat.sugProductUpdateFreq": "每天 12:00", |
| 201 | "heartbeat.sugProductUpdatePrompt": "訪問 https://reasonix.io/changelog/ 查看最新的發佈說明,重點介紹最近版本的新能力(New/新增)和重要修復,用要點式摘要輸出,控制在 10 條以內。如果頁面無法訪問,改用本地 DeepSeek-Reasonix 程式碼倉庫根目錄下的 release-notes/releases.json 兜底(若不確定倉庫位置,用 find 指令搜尋該檔案)。", |
| 202 | "heartbeat.suggestions": "建議", |
| 203 | "heartbeat.suggestionsHint": "一鍵建立常用自動化任務", |
| 204 | "heartbeat.unitDay": "天", |
| 205 | "heartbeat.viewFlat": "純列表", |
| 206 | "heartbeat.viewGrouped": "按專案分組", |
| 207 | } satisfies Record<HeartbeatFeatureKey, string>; |
| 208 | |
| 209 | export const heartbeatFeatureKeys = Object.keys(heartbeatFeatureEn) as HeartbeatFeatureKey[]; |
| 210 | |
| 211 | const heartbeatFeatureDictionaries: Record<Locale, Record<HeartbeatFeatureKey, string>> = { |
| 212 | en: heartbeatFeatureEn, |
| 213 | zh: heartbeatFeatureZh, |
| 214 | "zh-TW": heartbeatFeatureZhTW, |
| 215 | }; |
| 216 | |
| 217 | function interpolate(message: string, vars?: Record<string, string | number>): string { |
| 218 | if (!vars) return message; |
| 219 | return message.replace(/\{(\w+)\}/g, (_, key) => ( |
| 220 | vars[key] === undefined ? `{${key}}` : String(vars[key]) |
| 221 | )); |
| 222 | } |
| 223 | |
| 224 | export function useHeartbeatT(): HeartbeatTranslator { |
| 225 | const { locale, t } = useI18n(); |
| 226 | return useCallback<HeartbeatTranslator>((key, vars) => { |
| 227 | const message = heartbeatFeatureDictionaries[locale][key as HeartbeatFeatureKey]; |
| 228 | return message === undefined ? t(key as DictKey, vars) : interpolate(message, vars); |
| 229 | }, [locale, t]); |
| 230 | } |
| 231 |