diff --git a/src/preload/index.ts b/src/preload/index.ts index 3f51913..4e7a688 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -106,7 +106,7 @@ const api: AppApi = { ipcRenderer.send(Ipc.HOSTKEY_RESPOND, promptId, action), getSettings: () => ipcRenderer.invoke(Ipc.SETTINGS_GET), - saveSettings: (next: AppSettings) => ipcRenderer.invoke(Ipc.SETTINGS_SET, next), + saveSettings: (next: Partial) => ipcRenderer.invoke(Ipc.SETTINGS_SET, next), onSettingsChanged: (cb: (s: AppSettings) => void) => { const listener = (_: unknown, s: AppSettings): void => cb(s) ipcRenderer.on(Ipc.SETTINGS_CHANGED, listener) @@ -121,6 +121,7 @@ const api: AppApi = { deleteLayout: (id: string) => ipcRenderer.invoke(Ipc.LAYOUTS_DELETE, id), updateCheck: () => ipcRenderer.invoke(Ipc.UPDATE_CHECK), + getUpdateState: () => ipcRenderer.invoke(Ipc.UPDATE_STATE_GET), updateDownload: () => ipcRenderer.invoke(Ipc.UPDATE_DOWNLOAD), updateInstall: () => ipcRenderer.invoke(Ipc.UPDATE_INSTALL), updateChangelog: () => ipcRenderer.invoke(Ipc.UPDATE_CHANGELOG), diff --git a/src/shared/api.ts b/src/shared/api.ts index b6216d1..31ce523 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -97,7 +97,7 @@ export interface AppApi { // ---- settings ---- getSettings(): Promise - saveSettings(next: AppSettings): Promise + saveSettings(next: Partial): Promise onSettingsChanged(cb: (s: AppSettings) => void): () => void // ---- fonts ---- @@ -112,6 +112,8 @@ export interface AppApi { // ---- updater (domestic feed first, GitHub fallback) ---- updateCheck(): Promise + /** read the updater's current state without triggering a network check */ + getUpdateState(): Promise updateDownload(): Promise updateInstall(): Promise updateChangelog(): Promise diff --git a/src/shared/i18n/dicts/en/main.ts b/src/shared/i18n/dicts/en/main.ts index bf0ea24..2ec71b8 100644 --- a/src/shared/i18n/dicts/en/main.ts +++ b/src/shared/i18n/dicts/en/main.ts @@ -13,7 +13,7 @@ const main: Record = { 'main.tray.closeDetail': 'Minimizing to tray keeps your terminal sessions running.', 'main.tray.rememberChoice': 'Remember my choice, never ask again', - 'main.updater.feedFailed': 'Domestic feed: {gitea}; GitHub: {github}', + 'main.updater.feedFailed': 'Gitea feed: {gitea}; GitHub: {github}', 'main.ssh.connectTimeout': 'Connection timed out ({host}:{port})', 'main.ssh.saveFingerprintFailed': 'Failed to save the host fingerprint: {detail}', @@ -27,6 +27,7 @@ const main: Record = { 'main.sftp.deleteFailed': 'Delete failed: {detail}', 'main.sftp.invalidMode': 'Invalid permission value: {mode}', 'main.sftp.invalidUidGid': 'Invalid uid/gid', + 'main.sftp.commandTimeout': 'Command timed out', 'main.sftp.commandExitCode': 'Command exited with code {code}', 'main.sftp.cancelled': 'Cancelled', @@ -47,6 +48,7 @@ const main: Record = { 'main.sysinfo.sessionGone': 'SSH session is missing or disconnected', 'main.sysinfo.execFailed': 'SSH exec failed', + 'main.sysinfo.pollTimeout': 'Monitoring command timed out', 'main.sysinfo.parseFailed': 'Parse failed: {message}', 'main.pty.serviceNotReady': 'SSH session service is not initialised yet', 'main.pty.missingConnectionId': 'SSH session is missing its connectionId', diff --git a/src/shared/i18n/dicts/en/settings.ts b/src/shared/i18n/dicts/en/settings.ts index cdbd6ba..d10f80d 100644 --- a/src/shared/i18n/dicts/en/settings.ts +++ b/src/shared/i18n/dicts/en/settings.ts @@ -98,6 +98,11 @@ const settings: Record = { 'Together with the global key this shows or hides the window from anywhere; if registration fails (a conflict with another app) it simply has no effect and the app reports no error.', 'settings.system.shortcutRecording': 'Press the keys…', 'settings.system.shortcutPlaceholder': 'Click and press a shortcut, leave empty to disable', + 'settings.system.shortcutConflict': + 'This shortcut is already used by the app (Ctrl+= / Ctrl+- / Ctrl+0 / Ctrl+PgUp / Ctrl+PgDn). Please choose another.', + 'settings.resetTerminal': 'Reset terminal settings', + 'settings.resetTerminalTitle': 'Reset terminal settings?', + 'settings.resetTerminalDesc': 'All terminal settings — font, cursor, rendering and theme — will be restored to defaults.', 'settings.highlight.alert': 'Custom highlighting improves readability at a small performance cost. Rules are applied in priority order. PS: colors are injected as text is written to the terminal, so existing scrollback is unaffected', 'settings.highlight.enabled': 'Enabled', @@ -121,6 +126,17 @@ const settings: Record = { 'settings.highlight.resetOk': 'Restore', 'settings.highlight.createTitle': 'New highlight rule', 'settings.highlight.editTitle': 'Edit highlight rule', + 'settings.highlight.builtin.perm': 'Linux permissions and user/group', + 'settings.highlight.builtin.path': 'Linux file paths', + 'settings.highlight.builtin.shellkw': 'Shell keywords and control flow', + 'settings.highlight.builtin.okstate': 'Success states', + 'settings.highlight.builtin.badstate': 'Error/warning states', + 'settings.highlight.builtin.quoted': 'Strings and quotes', + 'settings.highlight.builtin.envvar': 'Environment variables and arguments', + 'settings.highlight.builtin.ipv4': 'Network and IP addresses', + 'settings.highlight.builtin.datetime': 'Time and date', + 'settings.highlight.builtin.numbers': 'Numbers and counts', + 'settings.highlight.builtin.url': 'Web links', 'settings.theme.accent': 'Tab accent color', 'settings.theme.accentDesc': 'Accent color for the active tab border, the sidebar indicator, and SSH badges', 'settings.theme.customBadge': 'Custom', diff --git a/src/shared/i18n/dicts/en/ssh.ts b/src/shared/i18n/dicts/en/ssh.ts index 23d9b71..6282681 100644 --- a/src/shared/i18n/dicts/en/ssh.ts +++ b/src/shared/i18n/dicts/en/ssh.ts @@ -13,7 +13,7 @@ const ssh: Record = { 'ssh.fields.askPasswordHint': 'Prompts for the password on every connection instead of using the saved one', 'ssh.fields.passwordPlaceholder': 'Leave empty to keep the saved password', - 'ssh.fields.keyPath': 'Private key path (path on the server, optional)', + 'ssh.fields.keyPath': 'Private key file path (on this computer, optional)', 'ssh.fields.keyContent': 'Private key content', 'ssh.fields.keyContentPlaceholder': 'Paste the private key; leave empty to use the path above (leave empty while editing to keep the saved key)', @@ -100,6 +100,8 @@ const ssh: Record = { 'ssh.file.up': 'Parent folder', 'ssh.file.refresh': 'Refresh', 'ssh.file.uploadFile': 'Upload files', + 'ssh.file.overwriteTitle': 'Overwrite existing file?', + 'ssh.file.overwriteDesc': 'A file with the same name exists in the target directory; uploading will overwrite its contents.', 'ssh.file.downloadSelected': 'Download selection', 'ssh.file.permOwner': 'Permissions / owner', 'ssh.file.open': 'Open', diff --git a/src/shared/i18n/dicts/ja/main.ts b/src/shared/i18n/dicts/ja/main.ts index 311f807..e49df22 100644 --- a/src/shared/i18n/dicts/ja/main.ts +++ b/src/shared/i18n/dicts/ja/main.ts @@ -13,7 +13,7 @@ const main: Record = { 'main.tray.closeDetail': 'トレイに最小化すると、ターミナルセッションは実行を続けます。', 'main.tray.rememberChoice': '選択を記憶して今後確認しない', - 'main.updater.feedFailed': '国内フィード: {gitea};GitHub: {github}', + 'main.updater.feedFailed': 'Gitea フィード: {gitea};GitHub: {github}', 'main.ssh.connectTimeout': '接続がタイムアウトしました ({host}:{port})', 'main.ssh.saveFingerprintFailed': 'ホスト鍵のフィンガープリントを保存できませんでした: {detail}', @@ -27,6 +27,7 @@ const main: Record = { 'main.sftp.deleteFailed': '削除に失敗しました: {detail}', 'main.sftp.invalidMode': '不正なパーミッション値: {mode}', 'main.sftp.invalidUidGid': '不正な uid/gid', + 'main.sftp.commandTimeout': 'コマンドがタイムアウトしました', 'main.sftp.commandExitCode': 'コマンドの終了コード {code}', 'main.sftp.cancelled': 'キャンセルしました', @@ -47,6 +48,7 @@ const main: Record = { 'main.sysinfo.sessionGone': 'SSH セッションが存在しないか切断されています', 'main.sysinfo.execFailed': 'SSH exec に失敗しました', + 'main.sysinfo.pollTimeout': '監視コマンドがタイムアウトしました', 'main.sysinfo.parseFailed': '解析に失敗しました: {message}', 'main.pty.serviceNotReady': 'SSH セッションサービスが未初期化です', 'main.pty.missingConnectionId': 'SSH セッションに connectionId がありません', diff --git a/src/shared/i18n/dicts/ja/settings.ts b/src/shared/i18n/dicts/ja/settings.ts index a8bd2a4..879ed64 100644 --- a/src/shared/i18n/dicts/ja/settings.ts +++ b/src/shared/i18n/dicts/ja/settings.ts @@ -95,6 +95,11 @@ const settings: Record = { 'グローバルキーと組み合わせると、どの画面からでもウィンドウを呼び出し/非表示にできます。登録に失敗した場合(他のソフトとの競合)は効果がなく、アプリにエラーは表示されません。', 'settings.system.shortcutRecording': 'キーを押してください…', 'settings.system.shortcutPlaceholder': 'クリックしてショートカットを押す。空欄で無効', + 'settings.system.shortcutConflict': + 'このショートカットはアプリ内で既に使用されています(Ctrl+= / Ctrl+- / Ctrl+0 / Ctrl+PgUp / Ctrl+PgDn)。別のものを選んでください。', + 'settings.resetTerminal': 'ターミナル設定をリセット', + 'settings.resetTerminalTitle': 'ターミナル設定をリセットしますか?', + 'settings.resetTerminalDesc': 'フォント、カーソル、レンダリング、テーマなどすべてのターミナル設定がデフォルトに戻ります。', 'settings.highlight.alert': '独自のハイライトは見やすさを高めますが、わずかに性能を消費します。ルールは優先順に適用されます。PS: ハイライトはターミナルへの書き込み時に色を注入するため、既存のスクロールバックには反映されません', 'settings.highlight.enabled': '有効', @@ -118,6 +123,17 @@ const settings: Record = { 'settings.highlight.resetOk': '元に戻す', 'settings.highlight.createTitle': 'ハイライトルールを作成', 'settings.highlight.editTitle': 'ハイライトルールを編集', + 'settings.highlight.builtin.perm': 'Linux のパーミッションとユーザー', + 'settings.highlight.builtin.path': 'Linux のファイルパス', + 'settings.highlight.builtin.shellkw': 'Shell のキーワードと制御構文', + 'settings.highlight.builtin.okstate': '成功ステータス', + 'settings.highlight.builtin.badstate': 'エラー・警告ステータス', + 'settings.highlight.builtin.quoted': '文字列と引用符', + 'settings.highlight.builtin.envvar': '環境変数と引数', + 'settings.highlight.builtin.ipv4': 'ネットワークと IP アドレス', + 'settings.highlight.builtin.datetime': '時刻と日付', + 'settings.highlight.builtin.numbers': '数値とカウント', + 'settings.highlight.builtin.url': 'ウェブリンク', 'settings.theme.accent': 'タブのアクセントカラー', 'settings.theme.accentDesc': 'アクティブなタブの枠線、サイドバーのインジケーター、SSH バッジのアクセントカラー', 'settings.theme.customBadge': 'カスタム', diff --git a/src/shared/i18n/dicts/ja/ssh.ts b/src/shared/i18n/dicts/ja/ssh.ts index b85f49a..5550004 100644 --- a/src/shared/i18n/dicts/ja/ssh.ts +++ b/src/shared/i18n/dicts/ja/ssh.ts @@ -13,7 +13,7 @@ const ssh: Record = { 'ssh.fields.askPasswordHint': 'オンにすると毎回の接続でパスワード入力ダイアログを表示し、保存済みのパスワードを使いません', 'ssh.fields.passwordPlaceholder': '編集時に空欄のままにすると保存済みのパスワードを保持します', - 'ssh.fields.keyPath': '秘密鍵のパス(サーバー上の秘密鍵のパス、省略可)', + 'ssh.fields.keyPath': '秘密鍵ファイルのパス(このコンピューター上のパス、省略可)', 'ssh.fields.keyContent': '秘密鍵の内容', 'ssh.fields.keyContentPlaceholder': '秘密鍵の内容を貼り付けます。空欄の場合は上のパスを使用します(編集時に空欄のままにすると保存済みの秘密鍵を保持します)', @@ -102,6 +102,8 @@ const ssh: Record = { 'ssh.file.up': '親フォルダへ', 'ssh.file.refresh': '更新', 'ssh.file.uploadFile': 'ファイルをアップロード', + 'ssh.file.overwriteTitle': '既存のファイルを上書きしますか?', + 'ssh.file.overwriteDesc': '対象ディレクトリに同名のファイルが存在します。アップロードすると内容が上書きされます。', 'ssh.file.downloadSelected': '選択項目をダウンロード', 'ssh.file.permOwner': '権限 / 所有者', 'ssh.file.open': '開く', diff --git a/src/shared/i18n/dicts/zh-CN/main.ts b/src/shared/i18n/dicts/zh-CN/main.ts index f4da4aa..7881784 100644 --- a/src/shared/i18n/dicts/zh-CN/main.ts +++ b/src/shared/i18n/dicts/zh-CN/main.ts @@ -27,6 +27,7 @@ const main: Record = { 'main.sftp.deleteFailed': '删除失败: {detail}', 'main.sftp.invalidMode': '非法权限值: {mode}', 'main.sftp.invalidUidGid': '非法 uid/gid', + 'main.sftp.commandTimeout': '命令执行超时', 'main.sftp.commandExitCode': '命令退出码 {code}', 'main.sftp.cancelled': '已取消', @@ -47,6 +48,7 @@ const main: Record = { 'main.sysinfo.sessionGone': 'SSH 会话不存在或已断开', 'main.sysinfo.execFailed': 'SSH exec 失败', + 'main.sysinfo.pollTimeout': '监控命令响应超时', 'main.sysinfo.parseFailed': '解析失败: {message}', 'main.pty.serviceNotReady': 'SSH 会话服务尚未初始化', 'main.pty.missingConnectionId': 'SSH 会话缺少 connectionId', diff --git a/src/shared/i18n/dicts/zh-CN/settings.ts b/src/shared/i18n/dicts/zh-CN/settings.ts index 1aa790c..69cb63e 100644 --- a/src/shared/i18n/dicts/zh-CN/settings.ts +++ b/src/shared/i18n/dicts/zh-CN/settings.ts @@ -92,6 +92,11 @@ const settings: Record = { '配合全局键可在任何界面唤起/隐藏窗口;若注册失败(与其他软件冲突)则不生效,应用不会报错。', 'settings.system.shortcutRecording': '按下组合键…', 'settings.system.shortcutPlaceholder': '点击后按下快捷键,留空禁用', + 'settings.system.shortcutConflict': + '该组合键已被应用内快捷键占用(Ctrl+= / Ctrl+- / Ctrl+0 / Ctrl+PgUp / Ctrl+PgDn),请换一个。', + 'settings.resetTerminal': '恢复默认终端设置', + 'settings.resetTerminalTitle': '恢复默认终端设置?', + 'settings.resetTerminalDesc': '字体、光标、渲染与主题等全部终端设置将恢复为默认值。', 'settings.highlight.alert': '自定义高亮可增强显示效果,但会略微增加性能消耗。规则按优先级顺序应用。PS: 高亮在写入终端时注入颜色,对已有回滚内容不生效', 'settings.highlight.enabled': '启用', @@ -115,6 +120,17 @@ const settings: Record = { 'settings.highlight.resetOk': '恢复', 'settings.highlight.createTitle': '新建高亮规则', 'settings.highlight.editTitle': '编辑高亮规则', + 'settings.highlight.builtin.perm': 'Linux 权限与用户', + 'settings.highlight.builtin.path': 'Linux 文件路径', + 'settings.highlight.builtin.shellkw': 'Shell 关键字与流程', + 'settings.highlight.builtin.okstate': '成功状态', + 'settings.highlight.builtin.badstate': '错误/警告状态', + 'settings.highlight.builtin.quoted': '字符串与引号', + 'settings.highlight.builtin.envvar': '环境变量与参数', + 'settings.highlight.builtin.ipv4': '网络与 IP 地址', + 'settings.highlight.builtin.datetime': '时间与日期', + 'settings.highlight.builtin.numbers': '数字与计数', + 'settings.highlight.builtin.url': '网址链接', 'settings.theme.accent': '标签强调色', 'settings.theme.accentDesc': '活动标签边框、侧栏指示条与 SSH 徽章的强调颜色', 'settings.theme.customBadge': '自定义', diff --git a/src/shared/i18n/dicts/zh-CN/ssh.ts b/src/shared/i18n/dicts/zh-CN/ssh.ts index 9620f35..89c7d65 100644 --- a/src/shared/i18n/dicts/zh-CN/ssh.ts +++ b/src/shared/i18n/dicts/zh-CN/ssh.ts @@ -11,7 +11,7 @@ const ssh: Record = { 'ssh.fields.askPasswordLabel': '连接时询问密码', 'ssh.fields.askPasswordHint': '开启后每次连接弹出对话框输入密码,不再使用已保存的密码', 'ssh.fields.passwordPlaceholder': '编辑时留空表示保留已保存的密码', - 'ssh.fields.keyPath': '私钥路径(服务器上的本地私钥路径,可留空)', + 'ssh.fields.keyPath': '私钥文件路径(本机路径,可留空)', 'ssh.fields.keyContent': '私钥内容', 'ssh.fields.keyContentPlaceholder': '粘贴私钥内容;留空则使用上面的路径(编辑时留空表示保留已保存的私钥)', @@ -94,6 +94,8 @@ const ssh: Record = { 'ssh.file.up': '上一级', 'ssh.file.refresh': '刷新', 'ssh.file.uploadFile': '上传文件', + 'ssh.file.overwriteTitle': '覆盖已存在的文件?', + 'ssh.file.overwriteDesc': '目标目录已存在同名文件,上传将覆盖其内容。', 'ssh.file.downloadSelected': '下载选中项', 'ssh.file.permOwner': '权限 / 属主', 'ssh.file.open': '打开', diff --git a/src/shared/i18n/dicts/zh-TW/main.ts b/src/shared/i18n/dicts/zh-TW/main.ts index 5589b54..d258eb6 100644 --- a/src/shared/i18n/dicts/zh-TW/main.ts +++ b/src/shared/i18n/dicts/zh-TW/main.ts @@ -27,6 +27,7 @@ const main: Record = { 'main.sftp.deleteFailed': '刪除失敗: {detail}', 'main.sftp.invalidMode': '權限值無效: {mode}', 'main.sftp.invalidUidGid': 'uid/gid 無效', + 'main.sftp.commandTimeout': '命令執行逾時', 'main.sftp.commandExitCode': '指令結束碼 {code}', 'main.sftp.cancelled': '已取消', @@ -47,6 +48,7 @@ const main: Record = { 'main.sysinfo.sessionGone': 'SSH 工作階段不存在或已中斷', 'main.sysinfo.execFailed': 'SSH exec 失敗', + 'main.sysinfo.pollTimeout': '監控命令回應逾時', 'main.sysinfo.parseFailed': '解析失敗: {message}', 'main.pty.serviceNotReady': 'SSH 工作階段服務尚未初始化', 'main.pty.missingConnectionId': 'SSH 工作階段缺少 connectionId', diff --git a/src/shared/i18n/dicts/zh-TW/settings.ts b/src/shared/i18n/dicts/zh-TW/settings.ts index d1334bb..b9e33e4 100644 --- a/src/shared/i18n/dicts/zh-TW/settings.ts +++ b/src/shared/i18n/dicts/zh-TW/settings.ts @@ -92,6 +92,11 @@ const settings: Record = { '搭配全域鍵可在任何畫面喚出/隱藏視窗;若註冊失敗(與其他軟體衝突)則不會生效,應用程式也不會報錯。', 'settings.system.shortcutRecording': '請按下組合鍵…', 'settings.system.shortcutPlaceholder': '點擊後按下快速鍵,留空為停用', + 'settings.system.shortcutConflict': + '此組合鍵已被應用內快捷鍵佔用(Ctrl+= / Ctrl+- / Ctrl+0 / Ctrl+PgUp / Ctrl+PgDn),請換一個。', + 'settings.resetTerminal': '恢復預設終端設定', + 'settings.resetTerminalTitle': '恢復預設終端設定?', + 'settings.resetTerminalDesc': '字體、游標、渲染與主題等全部終端設定將恢復為預設值。', 'settings.highlight.alert': '自訂高亮可強化顯示效果,但會略微增加效能負擔。規則依優先順序套用。PS:高亮是在寫入終端時注入顏色,對既有的回捲內容不生效', 'settings.highlight.enabled': '啟用', @@ -115,6 +120,17 @@ const settings: Record = { 'settings.highlight.resetOk': '還原', 'settings.highlight.createTitle': '新增高亮規則', 'settings.highlight.editTitle': '編輯高亮規則', + 'settings.highlight.builtin.perm': 'Linux 權限與使用者', + 'settings.highlight.builtin.path': 'Linux 檔案路徑', + 'settings.highlight.builtin.shellkw': 'Shell 關鍵字與流程', + 'settings.highlight.builtin.okstate': '成功狀態', + 'settings.highlight.builtin.badstate': '錯誤/警告狀態', + 'settings.highlight.builtin.quoted': '字串與引號', + 'settings.highlight.builtin.envvar': '環境變數與參數', + 'settings.highlight.builtin.ipv4': '網路與 IP 位址', + 'settings.highlight.builtin.datetime': '時間與日期', + 'settings.highlight.builtin.numbers': '數字與計數', + 'settings.highlight.builtin.url': '網址連結', 'settings.theme.accent': '標籤強調色', 'settings.theme.accentDesc': '作用中標籤的邊框、側邊欄指示條與 SSH 徽章的強調顏色', 'settings.theme.customBadge': '自訂', diff --git a/src/shared/i18n/dicts/zh-TW/ssh.ts b/src/shared/i18n/dicts/zh-TW/ssh.ts index f040d23..f640383 100644 --- a/src/shared/i18n/dicts/zh-TW/ssh.ts +++ b/src/shared/i18n/dicts/zh-TW/ssh.ts @@ -11,7 +11,7 @@ const ssh: Record = { 'ssh.fields.askPasswordLabel': '連線時詢問密碼', 'ssh.fields.askPasswordHint': '開啟後每次連線彈出對話框輸入密碼,不再使用已儲存的密碼', 'ssh.fields.passwordPlaceholder': '編輯時留空表示保留已儲存的密碼', - 'ssh.fields.keyPath': '私鑰路徑(伺服器上的本機私鑰路徑,可留空)', + 'ssh.fields.keyPath': '私鑰檔案路徑(本機路徑,可留空)', 'ssh.fields.keyContent': '私鑰內容', 'ssh.fields.keyContentPlaceholder': '貼上私鑰內容;留空則使用上面的路徑(編輯時留空表示保留已儲存的私鑰)', @@ -94,6 +94,8 @@ const ssh: Record = { 'ssh.file.up': '上一層', 'ssh.file.refresh': '重新整理', 'ssh.file.uploadFile': '上傳檔案', + 'ssh.file.overwriteTitle': '覆蓋已存在的檔案?', + 'ssh.file.overwriteDesc': '目標目錄已存在同名檔案,上傳將覆蓋其內容。', 'ssh.file.downloadSelected': '下載選取項目', 'ssh.file.permOwner': '權限 / 擁有者', 'ssh.file.open': '開啟', diff --git a/src/shared/i18n/index.ts b/src/shared/i18n/index.ts index 30bbb5f..cd0dc9f 100644 --- a/src/shared/i18n/index.ts +++ b/src/shared/i18n/index.ts @@ -53,6 +53,12 @@ const DICTS: Record = { ja } +/** Own-property read: dictionary literals would otherwise answer `constructor`, + * `toString`, … straight out of Object.prototype and then throw in replace(). */ +function lookup(dict: Dict, key: string): string | undefined { + return Object.prototype.hasOwnProperty.call(dict, key) ? dict[key] : undefined +} + let current: Language = DEFAULT_LANGUAGE const listeners = new Set<(lang: Language) => void>() @@ -75,7 +81,7 @@ export function onLanguageChange(cb: (lang: Language) => void): () => void { /** Translate `key`; `{name}` placeholders are filled from `vars`. */ export function t(key: string, vars?: Record): string { - const raw = DICTS[current][key] ?? DICTS[DEFAULT_LANGUAGE][key] ?? key + const raw = lookup(DICTS[current], key) ?? lookup(DICTS[DEFAULT_LANGUAGE], key) ?? key if (!vars) return raw return raw.replace(/\{(\w+)\}/g, (match, name: string) => vars[name] === undefined ? match : String(vars[name]) diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index 3f79b5c..bdc376d 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -97,6 +97,8 @@ export const Ipc = { UPDATE_DOWNLOAD: 'update:download', UPDATE_INSTALL: 'update:install', UPDATE_CHANGELOG: 'update:changelog', + /** renderer pulls the current updater state without triggering a check */ + UPDATE_STATE_GET: 'update:stateGet', /** main -> renderer broadcast: UpdateState */ UPDATE_STATE: 'update:state',