From 28b1215efa5723abed8d2bbe3390f49ca4abd90b Mon Sep 17 00:00:00 2001 From: Hank <3490170+Hank076@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:04:58 +0800 Subject: [PATCH] feat: add Traditional Chinese locale support Adds Traditional Chinese locale support and updates locale-related tests. --- .../editor/EditorSettingsDialog.vue | 6 +- .../src/components/ui/LightDropdown.vue | 7 +- apps/desktop/src/i18n/index.ts | 18 +- apps/desktop/src/i18n/locales/zh-TW.ts | 1697 +++++++++++++++++ apps/desktop/src/lib/ai.ts | 12 +- apps/desktop/src/lib/localeOptions.ts | 1 + packages/app-tests/aiPrompt.test.ts | 5 +- packages/app-tests/aiPromptEval.test.ts | 5 +- packages/app-tests/i18nLocale.test.ts | 4 +- 9 files changed, 1742 insertions(+), 13 deletions(-) create mode 100644 apps/desktop/src/i18n/locales/zh-TW.ts diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 4aef9bee1..ee0f5bd97 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -1142,7 +1142,11 @@ watch(
- {{ locale.flag }} + + {{ locale.flag }} + {{ locale.label }}
diff --git a/apps/desktop/src/components/ui/LightDropdown.vue b/apps/desktop/src/components/ui/LightDropdown.vue index ec62ab936..0f49dff55 100644 --- a/apps/desktop/src/components/ui/LightDropdown.vue +++ b/apps/desktop/src/components/ui/LightDropdown.vue @@ -201,7 +201,12 @@ onBeforeUnmount(close); class="h-3 w-3 shrink-0" :class="[isItemSelected(item) ? selectedCheckClass : 'opacity-0']" /> - {{ item.leadingText }} + + {{ item.leadingText }} + ; type I18nGlobal = { locale: { value: Locale }; setLocaleMessage: (locale: Locale, messages: LocaleMessages) => void; }; -const supportedLocales: Locale[] = ["en", "es", "zh-CN"]; +const supportedLocales: Locale[] = ["en", "es", "zh-CN", "zh-TW"]; const defaultLocale: Locale = "zh-CN"; const loadedLocales = new Set([defaultLocale]); const localeLoaders: Record, () => Promise<{ default: LocaleMessages }>> = { en: () => import("./locales/en"), es: () => import("./locales/es"), + "zh-TW": () => import("./locales/zh-TW"), }; export function normalizeLocale(value: string | null): Locale | null { @@ -27,8 +28,17 @@ export function normalizeLocale(value: string | null): Locale | null { export function localeFromLanguageTag(value: string | null | undefined): Locale | null { if (!value) return null; const normalized = value.replace("_", "-").toLowerCase(); - if (normalized === "zh-cn" || normalized.startsWith("zh-")) return "zh-CN"; - if (normalized === "zh") return "zh-CN"; + if (normalized === "zh" || normalized.startsWith("zh-")) { + if ( + normalized.includes("hant") || + normalized.startsWith("zh-tw") || + normalized.startsWith("zh-hk") || + normalized.startsWith("zh-mo") + ) { + return "zh-TW"; + } + return "zh-CN"; + } if (normalized === "en" || normalized.startsWith("en-")) return "en"; if (normalized === "es" || normalized.startsWith("es-")) return "es"; return null; diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts new file mode 100644 index 000000000..333e819e3 --- /dev/null +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -0,0 +1,1697 @@ +export default { + app: { + name: "DBX", + }, + auth: { + setupTitle: "設定存取密碼", + setupDescription: "設定密碼以保護您的實例", + loginDescription: "資料庫管理工具", + newPassword: "輸入新密碼", + confirmPassword: "確認密碼", + enterPassword: "請輸入存取密碼", + passwordMismatch: "兩次輸入的密碼不一致", + setPassword: "設定密碼", + login: "登入", + processing: "處理中……", + loginFailed: "密碼錯誤", + connectFailed: "連線失敗", + changePassword: "修改密碼", + oldPassword: "目前密碼", + oldPasswordWrong: "目前密碼不正確", + passwordChanged: "密碼修改成功", + changePasswordFailed: "密碼修改失敗", + changePasswordDescription: "輸入目前密碼並設定新密碼", + }, + toolbar: { + newConnection: "建立連線", + newQuery: "建立查詢", + execute: "執行", + executeShortcut: "執行選取/全部 (Cmd+Enter)", + stopQuery: "停止執行", + explainPlan: "執行計畫", + stopExplain: "停止執行計畫", + formatSql: "格式化 SQL", + formatSqlFailed: "SQL 格式化失敗", + saveSql: "儲存到 SQL 庫", + openSql: "開啟 SQL 檔案", + theme: "主題", + themeLight: "亮色", + themeDark: "暗色", + themeSystem: "跟隨系統", + sqlSaved: "SQL 已儲存", + sqlOpenFailed: "開啟檔案失敗:{message}", + sqlSaveFailed: "儲存檔案失敗:{message}", + driverManager: "驅動程式管理器", + updatableDriverCount: "可更新驅動程式數量", + }, + updates: { + title: "更新", + check: "檢查更新", + availableTitle: "發現新版本", + availableMessage: "DBX {latest} 已發布,目前版本為 {current}。", + upToDate: "DBX 已是最新版本 ({version})。", + failed: "檢查更新失敗:{error}", + rateLimited: "GitHub 更新檢查暫時觸發頻率限制。你仍然可以開啟下載頁手動檢視。", + openRelease: "開啟下載頁", + downloadAndInstall: "下載並安裝", + portableManualUpdate: + "可攜版無法使用應用程式內建安裝器更新。請從發行頁下載可攜版 ZIP,並解壓縮覆蓋目前的 DBX 目錄,以保留 portable.dbx 和 data。", + downloading: "下載中 {progress}%", + downloadFailed: "更新下載失敗:{error}", + restart: "結束並重新啟動", + restartFailed: "重新啟動應用程式失敗:{error}", + exitAndUpdate: "結束並更新", + reopenHint: "應用程式將自動重新啟動以完成更新", + dockerUsersRun: "Docker 使用者請執行", + toUpdate: "更新", + }, + sidebar: { + connections: "連線", + noConnections: "暫無連線", + import: "匯入連線", + importDbx: "匯入 DBX 設定", + importNavicat: "匯入 Navicat NCX", + importDbeaver: "匯入 DBeaver", + export: "匯出連線", + showMore: "再顯示 {count} 個……", + filterByType: "依類型篩選", + searchScopeConnection: "連線", + searchScopeDatabase: "資料庫", + searchScopeSchema: "Schema", + searchScopeTable: "資料表", + searchScopeView: "檢視", + clearFilter: "清除篩選", + locateActiveTab: "在側邊欄中定位", + }, + savedSql: { + saveToLibrary: "儲存到 SQL 庫", + save: "儲存", + saved: "SQL 已儲存", + saveFailed: "儲存 SQL 失敗:{message}", + fileName: "檔案名稱", + folder: "資料夾", + rootFolder: "SQL 庫", + newFolder: "建立 SQL 資料夾", + newFolderDefault: "建立資料夾", + renameFolder: "重新命名資料夾", + deleteFolder: "刪除資料夾", + deleteFolderConfirm: "確定刪除「{name}」以及其中的 SQL 檔案嗎?", + open: "開啟 SQL", + renameFile: "重新命名 SQL", + deleteFile: "刪除 SQL", + deleteFileConfirm: "確定刪除「{name}」嗎?", + }, + connection: { + title: "建立連線", + name: "名稱", + namePlaceholder: "連線名稱,留空則自動產生", + type: "類型", + host: "主機", + filePath: "檔案路徑", + createDuckDbFile: "建立 DuckDB 檔案", + memoryDatabasePathHint: "輸入 :memory: 可建立 SQLite 或 DuckDB 記憶體資料庫。", + user: "使用者名稱", + password: "密碼", + database: "資料庫", + databasePlaceholder: "可選", + databasePlaceholderWithDefault: "可選,預設 {database}", + defaultDatabase: "預設 DB", + authDatabase: "驗證 DB", + authDatabasePlaceholder: "可選,通常為 admin", + authMechanism: "認證機制", + authMechanismDefault: "預設", + serviceName: "服務/SID", + serviceNameOnly: "服務名稱", + version: "版本", + driverInstallHintPrefix: "需要在上方導覽列「", + driverInstallHintSuffix: "」中安裝對應的驅動程式才能連線。", + driverName: "驅動程式名稱", + driverNamePlaceholder: "廠商或環境名稱", + urlParams: "URL 參數", + sslEnable: "啟用加密連線", + caCertPath: "CA 憑證", + caCertPathPlaceholder: "可選,例如 ~/.yandex/RootCA.crt", + caCertPathBrowse: "瀏覽憑證", + redisTlsInsecure: "跳過憑證驗證", + redisTlsInsecureHint: "等價於 redis-cli --tls --insecure,適用於自簽名憑證或私有 CA。", + mysqlTlsMode: "TLS 模式", + mysqlTlsModePreferred: "優先使用", + mysqlTlsModeDisabled: "停用", + mysqlTlsModeRequired: "必須使用", + mysqlTlsModeVerifyCa: "驗證 CA", + mysqlTlsModeVerifyIdentity: "驗證身份", + mysqlCaCertHint: "伺服器端憑證由私有 CA 簽發時,Verify CA / Verify Identity 模式需要填寫。", + mysqlClientCert: "用戶端認證", + mysqlClientCertPlaceholder: "/path/to/client.crt", + mysqlClientKeyPlaceholder: "/path/to/client.key", + mysqlClientCertHint: "MySQL 要求 mTLS 時,用戶端憑證和私鑰必須一起填寫。", + mysqlClientCertBrowse: "選擇用戶端憑證", + mysqlClientKeyBrowse: "選擇用戶端私鑰", + postgresSslMode: "TLS 模式", + postgresSslModeDisable: "停用", + postgresSslModePrefer: "優先使用", + postgresSslModeRequire: "必須使用", + postgresSslModeVerifyCa: "驗證 CA", + postgresSslModeVerifyFull: "完整驗證", + postgresServerCert: "伺服器端 CA", + postgresRootCertPlaceholder: "/path/to/ca.crt", + postgresRootCertHint: "伺服器端憑證由私有 CA 簽發時,在 verify-ca 或 verify-full 模式下填寫。", + postgresRootCertBrowse: "選擇 CA 憑證", + postgresClientCert: "用戶端認證", + postgresClientCertPlaceholder: "/path/to/client.crt", + postgresClientKeyPlaceholder: "/path/to/client.key", + postgresClientCertHint: "PostgreSQL 要求 mTLS 時,用戶端憑證和私鑰必須一起填寫。", + postgresClientCertBrowse: "選擇用戶端憑證", + postgresClientKeyBrowse: "選擇用戶端私鑰", + connectionUrlOptional: "URL(可選)", + connectionUrlPlaceholder: "postgresql://user:pass{'@'}host:5432/db?sslmode=require", + parseConnectionUrl: "解析連線 URL", + parseConnectionUrlApplied: "已套用連線 URL", + parseConnectionUrlFailed: "解析連線 URL 失敗:{message}", + mode: "連線方式", + modeForm: "表單", + redisStandaloneMode: "直連", + redisSentinelMode: "Sentinel", + redisClusterMode: "叢集", + oceanbaseMySQLMode: "MySQL 模式", + oceanbaseOracleMode: "Oracle 模式", + redisFirstSentinel: "第一個 Sentinel", + redisFirstClusterNode: "第一個種子節點", + redisSentinelNodes: "Sentinel 節點", + redisClusterNodes: "叢集種子節點", + redisSentinelMaster: "Master 名稱", + redisSentinelUser: "Sentinel 使用者", + redisSentinelPassword: "Sentinel 密碼", + redisSentinelTls: "Sentinel TLS", + redisSentinelTlsHint: "連線 Sentinel 節點時使用 TLS", + searchDatabasePlaceholder: "搜尋資料庫類型", + iconView: "圖示檢視", + listView: "清單檢視", + selectedDatabase: "已選擇", + noDatabaseMatches: "沒有相符的資料庫", + next: "下一步", + back: "上一步", + basicTab: "連線資訊", + tlsTab: "TLS/SSL", + test: "測試", + testing: "測試中……", + copyTestResult: "複製測試結果", + saveAndConnect: "儲存並連線", + save: "儲存", + editTitle: "編輯連線", + testSuccess: "連線成功", + connecting: "正在連線 {name}……", + connectSuccess: "已連線 {name}", + connectFailed: "連線失敗:{message}", + driverNotInstalled: "{driver} 驅動程式未安裝,請在驅動程式管理器中安裝。", + jreNotInstalled: "JRE {jre} 執行環境未安裝,請在驅動程式管理器中安裝。", + systemJavaNotFound: "未在 PATH 中找到系統 Java 執行環境,請安裝 Java 或選擇自訂 Java 可執行檔。", + customJavaPathEmpty: "自訂 Java 執行環境路徑為空,請選擇 Java 可執行檔。", + jdbcPluginNotInstalled: "JDBC 外掛程式未安裝,請先安裝 JDBC 外掛程式再使用此連線。", + lastError: "連線錯誤", + clearError: "清除連線錯誤", + saveFailed: "儲存連線失敗:{message}", + deleted: "連線已刪除", + copied: "已複製", + disconnected: "已斷開連線", + databaseConnectionClosed: "已關閉資料庫連線:{name}", + duplicated: "連線已複製", + groupDeleted: "群組已刪除", + loadFailed: "載入已儲存連線失敗:{message}", + sshTunnel: "SSH 隧道", + advancedTab: "進階", + sshEnable: "藉由 SSH 隧道連線", + sshHost: "SSH 主機", + sshUser: "SSH 使用者", + sshPassword: "SSH 密碼", + sshPasswordPlaceholder: "留空則使用金鑰", + sshKeyPath: "金鑰路徑", + sshKeyPassphrase: "金鑰密碼", + sshKeyPassphrasePlaceholder: "金鑰未加密則留空", + sshKeyPathBrowse: "瀏覽", + sshExposeLan: "允許區域網路存取隧道", + sshConnectTimeout: "SSH 逾時時間(秒)", + sshHops: "SSH 層級", + sshHopAdd: "新增層級", + sshHopDuplicate: "複製", + sshHopDelete: "刪除", + sshHopMoveUp: "上移", + sshHopMoveDown: "下移", + sshHopName: "層級名稱", + sshHopNamePlaceholder: "堡壘機、跳板機、生產閘道器", + sshHopDefaultName: "第 {index} 層", + sshHopInvalidHost: "{hop}:SSH 主機不能為空", + sshHopInvalidUser: "{hop}:SSH 使用者不能為空", + sshHopInvalidPort: "{hop}:SSH 埠必須在 1 到 65535 之間", + sshHopInvalidAuth: "{hop}:需要填寫密碼或金鑰路徑", + sshHopInvalidTimeout: "{hop}:SSH 逾時時間必須在 1 到 300 秒之間", + connectTimeout: "連線逾時(秒)", + queryTimeout: "查詢逾時(秒)", + proxy: "代理伺服器", + proxyEnable: "藉由代理伺服器連線資料庫", + proxyType: "代理伺服器類型", + proxyHost: "代理伺服器主機", + proxyUsername: "代理伺服器使用者", + proxyUsernamePlaceholder: "可選", + proxyPassword: "代理伺服器密碼", + proxyPasswordPlaceholder: "可選", + jdbcUrl: "JDBC URL", + jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database", + jdbcDriverClass: "驅動程式類別(可選)", + jdbcDriverClassPlaceholder: "多數驅動程式可自動識別;需要時填寫 com.vendor.jdbc.Driver", + jdbcDriverPaths: "驅動程式 JAR", + jdbcDriverSelectPlaceholder: "選擇已匯入驅動程式", + jdbcDriverPathsPlaceholder: "/path/to/driver.jar\n/path/to/another-driver.jar", + jdbcDriverBrowse: "選擇 JDBC 驅動程式 JAR", + jdbcDocs: "檢視 JDBC 文件", + jdbcPluginHint: "先安裝 DBX JDBC 外掛程式,再匯入資料庫廠商提供的 JDBC 驅動程式 JAR。", + dmCompatHint: "需要在系統上安裝達夢 DM8 ODBC 驅動程式。", + dmDownload: "前往達夢官網下載", + mongoLegacyHint: + "MongoDB 4.2 以下版本需安裝 MongoDB (Legacy) 驅動程式。若帳號建立在 admin 且認證失敗,請將認證庫設為 admin。", + compatible: "相容", + mainstream: "熱門", + color: "顏色", + colorNone: "無顏色", + colorGreen: "綠色", + colorYellow: "黃色", + colorOrange: "橙色", + colorRed: "紅色", + colorBlue: "藍色", + colorPurple: "紫色", + }, + editor: { + pressToExecute: "按 {mod}+Enter 執行查詢", + pressToSaveSql: "按 {mod}+S 儲存 SQL", + queryTimeoutError: "查詢逾時 ({seconds}s),請檢查資料庫連線是否正常", + connectionMayBeLost: "連線可能已斷開,請重新整理資料重試", + showResultsPane: "顯示結果", + hideResultsPane: "收起結果", + noDatabase: "未選擇資料庫", + selectConnection: "選擇連線", + searchConnection: "搜尋連線……", + selectDatabase: "選擇資料庫", + searchDatabase: "搜尋資料庫……", + selectSchema: "選擇 Schema", + setDefaultDatabase: "設為預設", + defaultDatabase: "預設資料庫", + completion: { + nullValue: "空值", + isNull: "判斷是否為 NULL", + isNotNull: "判斷是否不為 NULL", + stringLiteral: "字串常值", + numericLiteral: "數值常值", + booleanValue: "布林值", + starExpansionColumns: "{count} 欄", + functionDescriptions: { + COUNT: "回傳列數", + SUM: "回傳數值欄位的總和", + AVG: "回傳數值欄位的平均值", + MIN: "回傳最小值", + MAX: "回傳最大值", + GROUP_CONCAT: "將分組值串接成字串", + STRING_AGG: "串接分組字串", + CONCAT: "串接多個字串", + CONCAT_WS: "使用分隔符號串接字串", + SUBSTRING: "擷取子字串", + REPLACE: "取代字串中的內容", + TRIM: "移除前後空白", + UPPER: "轉換為大寫", + LOWER: "轉換為小寫", + LENGTH: "回傳字串長度", + REGEXP_REPLACE: "使用正規表示式取代", + DATE_FORMAT: "依指定格式格式化日期", + DATEDIFF: "計算兩個日期的差值", + DATE_ADD: "對日期進行加法運算", + DATE_SUB: "對日期進行減法運算", + EXTRACT: "擷取日期部分", + NOW: "回傳目前日期與時間", + ROUND: "四捨五入到指定小數位", + FLOOR: "向下捨入", + CEIL: "向上捨入", + ABS: "回傳絕對值", + MOD: "回傳除法餘數", + COALESCE: "回傳第一個非 NULL 參數", + IFNULL: "如果為 NULL,則回傳替代值", + NULLIF: "如果參數相等,則回傳 NULL", + CAST: "將運算式轉換為目標類型", + JSON_EXTRACT: "從 JSON 擷取值", + JSON_VALUE: "從 JSON 擷取純量值", + JSON_OBJECT: "建立 JSON 物件", + JSON_ARRAY: "建立 JSON 陣列", + }, + }, + contextMenu: { + executeSelection: "執行選取 SQL", + executeCurrent: "執行 SQL", + copySelection: "複製選取內容", + selectAll: "全選", + }, + search: { + find: "尋找", + replace: "取代", + replaceAll: "全部取代", + caseSensitive: "區分大小寫", + regex: "正規表示式", + collapseReplace: "收起取代", + expandReplace: "展開取代", + prevMatch: "上一個 (Shift+Enter)", + nextMatch: "下一個 (Enter)", + close: "關閉 (Esc)", + noResults: "無結果", + }, + }, + tabs: { + sql: "SQL", + table: "資料表", + tableData: "資料表", + redis: "Redis", + mongo: "Mongo", + objects: "物件", + tooltipTitle: "標題:", + tooltipConnection: "連線:", + tooltipDatabase: "資料庫:", + tooltipTable: "資料表:", + tooltipCollection: "集合:", + tooltipSchema: "Schema:", + resultN: "結果 {n}", + scrollLeft: "向左捲動分頁", + scrollRight: "向右捲動分頁", + openTabs: "已開啟的分頁", + openDataTabs: "已開啟的資料表", + }, + chart: { + title: "圖表", + type: "類型", + line: "折線圖", + bar: "柱狀圖", + pie: "餅圖", + noNumericData: "沒有可用於圖表的數值資料", + }, + grid: { + rows: "{count} 列", + totalRows: "共 {count} 筆", + totalRowCount: "(總計 {count} 筆)", + rowsAffected: "影響 {count} 筆", + querySuccess: "查詢執行成功", + noRows: "暫無資料", + noRowsDescription: "目前結果集沒有任何記錄。", + noSearchResults: "沒有相符結果", + noSearchResultsDescription: "換個關鍵字試試。", + noFilteredRows: "沒有符合條件的資料", + noFilteredRowsDescription: "調整搜尋詞或狀態篩選試試。", + copy: "複製", + copyDdl: "複製 DDL", + copyCell: "複製儲存格", + copyRow: "複製整筆 (JSON)", + copyColumnJson: "複製欄 (JSON)", + copyRowInsert: "複製為 INSERT 語句", + copyRowInsertWithoutPrimaryKeys: "複製為 INSERT 語句(不含主鍵)", + copyRowUpdate: "複製為 UPDATE 語句", + copyAll: "複製全部 (TSV)", + selection: "選區", + copySelectionTsv: "複製選區 (TSV)", + copySelectionCsv: "複製選區 (CSV)", + copySelectionJson: "複製選區 (JSON)", + copySelectionSql: "複製選區為 SQL IN 清單", + clearSelection: "清除選區", + export: "匯出", + exportCsv: "匯出 CSV", + exportXlsx: "匯出 XLSX", + exportJson: "匯出 JSON", + exportMarkdown: "匯出 Markdown", + exportSelectedRowsCsv: "匯出選取資料列為 CSV", + exportSelectedRowsXlsx: "匯出選取資料列為 XLSX", + exportSelectedRowsJson: "匯出選取資料列為 JSON", + exportSelectedRowsMarkdown: "匯出選取資料列為 Markdown", + exported: "匯出完成", + exportFailed: "匯出失敗:{message}", + copied: "已複製", + copyFailed: "複製失敗:{message}", + filter: "篩選", + filterBuilder: "篩選器", + filterBuilderTitle: "篩選器", + filterBuilderHint: "點選篩選條件,並與手寫 WHERE 一起生效。", + filterBuilderSummary: "{count} 條規則", + filterBuilderAddRule: "新增條件", + filterBuilderColumn: "欄位", + filterBuilderValue: "值", + filterBuilderNoValue: "無需輸入值", + filterBuilderEmpty: "新增一條條件後即可開始篩選。", + resetFilterBuilder: "重設條件", + filterBuilderEquals: "等於", + filterBuilderNotEquals: "不等於", + filterBuilderContains: "包含", + filterBuilderNotContains: "不包含", + filterBuilderGreaterThan: "大於", + filterBuilderLessThan: "小於", + filterBuilderIsNull: "為空", + filterBuilderIsNotNull: "不為空", + columnActions: "欄位操作", + localFilter: "本機欄位篩選", + localFilterFor: "本機篩選「{column}」", + columnFormatter: "欄位格式設定", + columnFormatterFor: "格式化「{column}」", + columnFormatterHint: "僅改變顯示效果,原始值保持不變。", + formatterType: "類型", + formatterDatetime: "Unix 時間戳", + formatterJsonPath: "JSON 路徑", + formatterMask: "文字遮罩", + formatterCustomTemplate: "自訂範本", + formatterTimestampUnit: "時間戳單位", + formatterUnitAuto: "自動", + formatterUnitSeconds: "秒", + formatterUnitMilliseconds: "毫秒", + formatterJsonPathInput: "JSON 路徑", + formatterMaskPrefix: "字首保留", + formatterMaskSuffix: "字尾保留", + formatterSavedCustom: "已儲存範本", + formatterNewCustom: "建立範本", + formatterCustomName: "範本名稱", + formatterCustomNamePlaceholder: "例如:使用者標籤", + formatterCustomTemplateInput: "範本", + formatterCustomTemplateHint: "可用變數:${value}、${upper}、${lower}、${length}。填寫名稱後會儲存到清單。", + formatterPreview: "預覽", + saveFormatter: "儲存", + clearFormatter: "清除", + searchValues: "搜尋值……", + filterTypedValue: "使用「{value}」篩選", + value: "值", + count: "數量", + applyFilter: "套用篩選", + clearLocalFilters: "清除本機欄位篩選", + columnVisibility: "欄位篩選", + columnVisibilityHint: "至少保留一欄可見。", + searchColumns: "搜尋欄位……", + invertColumnVisibility: "反選", + showAllColumns: "顯示全部", + moreValues: "還有 {count} 個值,輸入關鍵字繼續縮小範圍", + filterByValue: "篩選此值", + filterExcludeValue: "排除此值", + filterLike: "包含此值", + filterNotLike: "不包含此值", + filterLessThan: "小於此值", + filterGreaterThan: "大於此值", + filterIsNull: "僅顯示 NULL", + filterIsNotNull: "僅顯示非 NULL", + clearFilter: "清除篩選", + sort: "排序", + sortAscending: "遞增排序", + sortDescending: "遞減排序", + clearSort: "清除排序", + pasted: "已貼上!", + search: "搜尋……", + searchOrWhere: "搜尋,或輸入 WHERE 條件……", + applyWhere: "套用 WHERE", + filterRows: "篩選列", + filterAllRows: "全部列", + filterChangedRows: "變更列", + page: "第 {page} 頁", + rowsPerPage: "每頁筆數", + customRowsPerPage: "自訂筆數", + applyPageSize: "套用", + save: "儲存", + discard: "放棄", + dismiss: "關閉", + addRow: "新增資料", + cloneRow: "複製為新資料", + deleteRow: "刪除資料", + deleteRows: "刪除 {count} 筆", + cloneRows: "複製 {count} 筆為新資料", + restoreRows: "復原 {count} 筆", + copyRows: "複製 {count} 筆 (JSON)", + copyColumnsJson: "複製 {count} 欄 (JSON)", + copyRowsInsert: "複製 {count} 筆為 INSERT", + copyRowsInsertWithoutPrimaryKeys: "複製 {count} 筆為 INSERT(不含主鍵)", + copyRowsUpdate: "複製 {count} 筆為 UPDATE", + selectedRows: "已選 {count} 筆", + restoreRow: "復原資料", + statusClean: "未改", + statusNew: "新增列", + statusEdited: "更新列", + statusDeleted: "刪除列", + pendingChanges: "{count} 項待儲存", + selectedCells: "已選 {count} 個儲存格", + cellDetails: "儲存格詳情", + openCellDetailsDialog: "儲存格詳情", + rowDetails: "資料詳情", + rowDetailsFor: "第 {row} 筆資料詳情", + openRowDetailsDialog: "資料詳情", + openColumnDetailsDialog: "欄位詳情", + columnDetailsFor: "「{column}」欄位詳情", + columnsCount: "{count} 欄", + rowCount: "筆數", + rowsCount: "{count} 筆", + fieldIndex: "序號", + cellValue: "值", + valueEditor: "儲存格編輯器", + imagePreview: "圖片預覽", + imageLoadFailed: "圖片載入失敗", + zoomIn: "放大", + zoomOut: "縮小", + fitImage: "適應視窗", + openImage: "開啟圖片", + columnName: "欄位名稱", + columnType: "類型", + columnComment: "註解", + rowNumber: "行號", + valueLength: "長度", + nullValue: "NULL", + noComment: "暫無註解", + formattedJson: "格式化 JSON", + formattedValue: "格式化值", + rawValue: "原始值", + copyValue: "複製值", + downloadBinaryValue: "下載值", + downloadSaved: "已儲存到 {path}", + downloadStarted: "已交給瀏覽器下載:{fileName}", + binaryDownload: { + binary: "原始二進位", + utf8: "文字編碼 (UTF-8)", + gbk: "文字編碼 (GBK)", + }, + copyRowTsv: "複製整筆 (TSV)", + copyColumnValues: "複製欄 (JSON)", + copyColumnTsv: "複製欄 (TSV)", + editValue: "編輯值", + formatJson: "格式化 JSON", + setNull: "設為 NULL", + restoreOriginalValue: "復原資料", + copyColumnName: "複製欄位名稱", + copyAlterColumnSql: "複製為 SQL ALTER", + alterSqlCopied: "SQL ALTER 已複製到剪貼簿", + alterSqlCopiedWithWarnings: "SQL ALTER 已複製到剪貼簿(含 {count} 條警告)", + noAlterSqlAvailable: "此欄沒有可複製的 ALTER SQL", + copyAlterSqlFailed: "複製失敗:{message}", + copySqlCondition: "複製 SQL 條件", + transpose: "轉置檢視", + rowsPerPageShort: " 筆/頁", + columnDetails: "欄位詳情", + tableInfo: "資料表屬性", + tableInfoColumns: "欄位", + tableInfoIndexes: "索引", + tableInfoForeignKeys: "外鍵", + tableInfoTriggers: "觸發器", + tableInfoNullable: "可為空", + tableInfoEmpty: "無", + tableInfoSearch: "搜尋……", + tableInfoNoResults: "沒有相符結果", + queryError: "查詢出錯", + refresh: "重新整理", + commit: "提交", + rollback: "回溯", + transactionSaveHint: "在事務中提交 {count} 項待儲存更改。", + nonTransactionalSaveHint: "逐條儲存 {count} 項更改;如果中途失敗,前面已成功的更改不會回溯。", + transactionActive: "編輯中", + keylessEditWarning: "無主鍵定位", + keylessEditWarningHint: + "此資料表沒有主鍵。更新與刪除會在 WHERE 子句中使用所有原始資料列值;完全重複的資料列可能會一起受到影響。", + queryEditReady: "可編輯", + queryEditReadyHint: "這是來自「{table}」的單一資料表結果,可直接修改、刪除或新增資料列,最後點選待儲存按鈕儲存。", + queryEditReadOnly: "唯讀結果", + queryEditUnsupported: { + "not-select": "只有 SELECT 查詢結果可以直接編輯。", + cte: "包含 WITH/CTE 的查詢暫不支援直接回寫。請改為簡單的單一資料表 SELECT。", + "set-operation": "UNION、INTERSECT 或 EXCEPT 結果無法安全映射回來源資料列。", + aggregation: "包含 DISTINCT、GROUP BY、HAVING 或聚合結果,無法直接編輯原始資料。", + "external-source": "外部檔案和資料表函式結果無法直接回寫。請先匯入資料庫資料表再編輯。", + "complex-source": "JOIN、多個資料表和子查詢無法安全映射到單一來源資料列。", + "computed-columns": "計算運算式或函式結果無法回寫。請改為選取原始欄位名稱。", + "no-table": "未識別到可編輯的資料表。", + "no-primary-key": "目標資料表沒有主鍵,因此無法安全地更新或刪除資料列。", + "primary-key-not-returned": "結果缺少原始主鍵欄位。請用原始欄位名稱將它一併查出。", + "aliased-columns": "結果欄位使用別名或運算式。請使用原始欄位名稱查詢可編輯欄位。", + "metadata-unavailable": "DBX 無法載入資料表 metadata,因此已停用結果編輯。", + }, + sortUnsupported: "目前 SQL 不支援完整排序,請改為單條 SELECT 查詢後再嘗試。", + truncatedHint: "結果已截斷,僅顯示前 {count} 行。可經由底部分頁繼續載入,或調整每頁行數。", + }, + welcome: { + title: "資料庫工作檯", + subtitle: "選擇左側連線以瀏覽結構,或直接開啟查詢分頁。", + connections: "連線", + connected: "已連線", + databaseTypes: "資料庫類型", + quickConnections: "快速開始", + quickConnectionsHint: "點選連線以開啟查詢分頁。", + shortcuts: "常用操作", + shortcutsHint: "從這裡開始你的下一步。", + tip: "也可以在左側展開連線,並在資料庫或資料表上按右鍵查看更多操作。", + tipSidebar: "點選左側資料表以檢視資料", + tipExecute: "執行查詢", + fileOpened: "已開啟 {name}", + mcpTitle: "AI 程式設計助理整合", + mcpDescription: "除了內建 AI 助理,也可以藉由 MCP 讓 Claude Code、Cursor 等程式設計助理查詢你的資料庫。", + mcpLearnMore: "了解更多", + }, + common: { + language: "語言", + loading: "載入中……", + stopping: "正在停止……", + close: "關閉", + }, + explain: { + title: "執行計畫", + tree: "樹狀", + summary: "摘要", + running: "正在讀取執行計畫……", + empty: "暫無執行計畫", + nodeCount: "{count} 個節點", + node: "節點", + relation: "資料表", + index: "索引", + cost: "成本", + rows: "筆數", + details: "詳情", + unsupported: "目前資料庫暫不支援執行計畫", + emptySql: "目前沒有可分析的 SQL", + unsafe: "第一版執行計畫僅支援 SELECT / WITH / TABLE / VALUES", + }, + lineage: { + title: "欄位血緣", + open: "檢視欄位血緣", + loading: "正在讀取 metadata {done}/{total}", + empty: "沒有找到相關血緣", + noFiltered: "目前篩選條件下沒有結果", + targetField: "目前欄位", + searchPlaceholder: "搜尋資料表、欄位、檢視或 SQL 片段……", + showing: "顯示 {shown}/{total} 條結果", + all: "全部", + certain: "確定", + likely: "高置信", + possible: "可能相關", + queryHistory: "查詢歷史", + openTable: "開啟對應資料表", + copy: "複製名稱", + copied: "已複製", + kind: { + foreignKey: "外鍵", + viewReference: "檢視", + historyReference: "歷史 SQL", + sameName: "同名欄位", + }, + description: { + foreignKeyIncoming: "{target} 藉由外鍵指向目前欄位,這是已確認的依賴。", + foreignKeyOutgoing: "目前欄位藉由外鍵引用 {target},這是已確認的依賴。", + viewLikely: "檢視定義同時提到目標資料表和欄位,通常表示查詢依賴。", + viewPossible: "檢視定義提到同名欄位,但沒有同時命中目標資料表,需要人工確認。", + historyLikely: "歷史 SQL 同時提到目標資料表和欄位,可作為排查影響面的參考。", + historyPossible: "歷史 SQL 提到了同名欄位,可能相關但需要確認上下文。", + sameName: "其他資料表存在同名欄位,可能代表相同業務含義,但不是已驗證的資料庫依賴。", + }, + cancel: "取消讀取", + refresh: "重新分析", + }, + connectionGroup: { + createGroup: "建立群組", + groupNamePlaceholder: "群組名稱", + renameGroup: "重新命名群組", + deleteGroup: "刪除群組", + moveToGroup: "移至群組", + moveToNewGroup: "移至新群組", + ungrouped: "取消群組", + newGroup: "新增群組……", + newGroupDefault: "新群組", + deleteGroupConfirmTitle: "刪除群組", + deleteGroupConfirmMessage: "刪除群組「{name}」?其中的連線將移到頂層。", + }, + databaseSearch: { + title: "搜尋資料庫", + open: "搜尋資料庫", + keyword: "關鍵字", + keywordPlaceholder: "輸入使用者 ID、電子郵件、訂單號等……", + limitPerTable: "每表最多", + search: "搜尋", + searching: "搜尋中", + stop: "停止", + hint: "掃描目前資料庫或 schema 中的一般資料表。會跳過二進位欄位,並限制每張資料表的載入量,避免一次載入過多資料。", + loadingTables: "正在讀取表清單……", + progress: "已掃描 {done}/{total} 張資料表", + resultCount: "{count} 筆結果", + limitedTables: "偵測到大型 schema;僅掃描前 {count} 張資料表", + results: "搜尋結果", + openResult: "開啟", + waiting: "搜尋中……", + noResults: "暫無搜尋結果", + errors: "部分資料表搜尋失敗", + }, + configExport: { + passphraseTitle: "加密匯出", + passphraseImportTitle: "解密匯入", + passphraseExportHint: "若要在匯出檔案中包含連線密碼,請設定一組加密密碼。匯入時您將需要使用相同的加密密碼。", + passphraseImportHint: "此檔案已加密,請輸入加密密碼來解密並匯入。", + passphrase: "加密密碼", + passphraseConfirm: "確認加密密碼", + passphrasePlaceholder: "輸入加密密碼", + passphraseConfirmPlaceholder: "再次輸入加密密碼", + passphraseRequired: "請輸入加密密碼", + passphraseMismatch: "兩次輸入的加密密碼不一致", + passphraseTooShort: "加密密碼至少需要 4 個字元", + exportEncrypted: "匯出", + decryptImport: "解密並匯入", + wrongPassphrase: "加密密碼錯誤或檔案已損壞", + exportSuccess: "連線設定匯出成功", + importSuccess: "已匯入 {count} 個連線", + importNavicatSuccess: "已匯入 {count} 個 Navicat 連線,若個別連線密碼為空請補填後測試連線", + importDbeaverSuccess: "已匯入 {count} 個 DBeaver 連線,若個別連線密碼為空請補填後測試連線", + importNone: "沒有新的連線需要匯入", + importLayoutConfirm: "匯入檔案包含連線群組資訊,是否一併套用?", + importLayoutTitle: "匯入群組", + importLayoutApply: "套用", + }, + ai: { + placeholder: "描述你想查什麼……", + settings: "AI 設定", + noConfig: "AI 尚未設定。請在 設定 > AI 中設定你的 AI 服務。", + provider: "供應商", + model: "模型", + browseModels: "瀏覽模型", + refreshModels: "重新整理模型", + loadingModels: "載入模型中……", + searchModels: "搜尋模型", + noModels: "尚未載入模型", + modelListHint: "點選重新整理以從供應商載入模型。", + modelListUnsupported: "此供應商不支援取得模型清單。", + modelListEndpointRequired: "填寫 Endpoint 後再取得模型清單。", + modelListApiKeyRequired: "填寫 API Key 後再取得模型清單。", + run: "執行", + readingSchema: "讀取結構", + noConnection: "目前分頁沒有可用連線", + noSql: "目前沒有 SQL 可處理", + sqlPreview: "SQL 預覽", + copySql: "複製 SQL", + copyCode: "複製程式碼", + executeSql: "立即執行", + copyAll: "複製全部", + copied: "已複製", + replace: "取代編輯器", + append: "追加到編輯器", + apply: "套用到編輯器", + clear: "清空對話", + welcome: "告訴我你想查詢什麼,我會寫出 SQL", + newChat: "新對話", + thinking: "思考中……", + reasoningProcess: "思考過程", + stopGenerating: "停止產生", + tableMentionEmpty: "沒有相符的資料表", + tableMentionPlaceholderHint: "輸入 {'@'} 可提及資料表作為脈絡", + tableMentionTypes: { + table: "資料表", + view: "檢視", + systemTable: "系統資料表", + temporaryTable: "暫存資料表", + }, + modes: { + ask: "Ask", + agent: "Agent", + }, + modeHints: { + ask: "只產生 SQL 和說明,不自動執行", + agent: "可自動執行安全查詢", + }, + fixWithAi: "用 AI 修復", + truncated: "上下文已截斷", + contextSummary: "{database} · {tables} 張資料表", + autoSqlBlocked: "AI 產生的 SQL 風險較高,已阻止自動執行,請手動檢查後再執行。", + agentSteps: { + generated: "已產生 SQL", + noSql: "未找到 SQL", + safe: "通過安全檢查", + needsConfirm: "需要確認", + blocked: "已阻止", + autoExecute: "準備執行", + notRequested: "未請求執行", + skipped: "未執行", + }, + agentStepTitles: { + riskCheck: "風險檢查:{action} · {category} · {environment} · {reasons}", + blocked: "風險策略阻止執行,請手動檢查 SQL。", + requiresConfirmation: "此 SQL 需要確認後執行。", + askMode: "Ask 模式不會自動執行 SQL。", + unsupportedAction: "目前 AI 動作不會自動執行 SQL。", + noSql: "回覆中沒有可執行的 SQL 程式碼區塊。", + notRequested: "使用者沒有表達執行意圖。", + skipped: "此步驟未執行。", + }, + proxy: "代理伺服器", + proxyEnable: "經由代理伺服器發送 AI 請求", + proxyUrl: "代理伺服器地址", + settingsHint: "設定會儲存在本機應用程式資料目錄中。請求由 Tauri 後端發出,避免在前端直接暴露給模型服務。", + enableThinking: "Thinking", + enableThinkingOn: "已啟用", + enableThinkingOff: "已停用", + enableThinkingHint: + "此選項僅對 /chat/completions API 且部分支援的模型生效。設為停用後可大幅節省 token,但產生結果品質可能會略微下降。", + actions: { + generate: "產生 SQL", + explain: "解釋 SQL", + optimize: "最佳化 SQL", + fix: "修復錯誤", + convert: "轉換", + sampleData: "範例資料", + }, + placeholders: { + generate: "描述你想查詢什麼,例如:統計最近 7 天每個使用者的訂單數", + explain: "可留空,或補充你關心的點", + optimize: "可留空,或說明最佳化目標,例如:減少全資料表掃描", + fix: "貼上錯誤或說明預期結果", + convert: "例如:轉換成 PostgreSQL / MySQL / SQL Server", + sampleData: "描述要產生的範例資料或測試語句", + }, + }, + contextMenu: { + openConnection: "開啟連線", + closeConnection: "關閉連線", + closeDatabaseConnection: "關閉資料庫連線", + deleteConnection: "刪除連線", + confirmDeleteTitle: "刪除連線", + confirmDeleteMessage: "確定要刪除「{name}」嗎?此操作無法復原。", + editConnection: "編輯連線", + selectVisibleDatabases: "選擇顯示資料庫", + duplicateConnection: "複製連線", + newQuery: "建立查詢", + openObjectBrowser: "瀏覽物件", + viewData: "檢視資料", + editStructure: "編輯資料表結構", + refreshChildren: "重新整理", + pin: "置頂", + unpin: "取消置頂", + refreshTab: "重新整理資料", + renameTab: "重新命名分頁", + compactTabTitle: "縮短標籤標題", + closeTab: "關閉分頁", + closeOtherTabs: "關閉其他分頁", + closeAllTabs: "關閉全部分頁", + copyName: "複製名稱", + setDefaultDatabase: "設為預設資料庫", + clearDefaultDatabase: "取消預設資料庫", + exportDatabase: "匯出資料庫", + exportDatabaseSuccess: "已匯出 {count} 張資料表,每張最多 {limit} 行", + exportDatabaseFailed: "匯出資料庫失敗:{message}", + importData: "匯入資料", + exportData: "匯出資料", + exportStructure: "匯出資料表結構", + createTable: "建立資料表", + dropTable: "刪除資料表", + emptyTable: "清空資料", + truncateTable: "截斷資料表", + duplicateStructure: "複製資料表結構", + renameObject: "重新命名", + renameObjectTitle: "重新命名物件", + renameObjectNamePlaceholder: "新名稱", + renameObjectSuccess: "已將「{oldName}」重新命名為「{newName}」", + confirmDropTableTitle: "刪除資料表", + confirmDropTableMessage: "確定要刪除「{name}」嗎?這將永久刪除資料表及其所有資料。", + confirmEmptyTableTitle: "清空資料", + confirmEmptyTableMessage: "確定要刪除「{name}」中的所有資料嗎?", + confirmTruncateTableTitle: "截斷資料表", + confirmTruncateTableMessage: "確定要截斷「{name}」嗎?這將刪除所有行。", + dropTableSuccess: "資料表「{name}」已刪除", + editView: "編輯檢視", + viewSource: "檢視原始碼", + viewDdl: "檢視 DDL", + dropView: "刪除檢視", + executeProcedure: "執行預存程序", + confirmExecuteProcedureTitle: "執行預存程序", + confirmExecuteProcedureMessage: "確認執行預存程序「{name}」?可先補充或調整參數值。", + loadingProcedureParameters: "正在讀取參數……", + procedureParametersUnavailable: "未能讀取參數資訊,仍可直接編輯下方 SQL 後執行。", + noProcedureParameters: "未讀取到參數,確認後將直接執行目前 SQL。", + inputParameters: "{count} 個輸入參數", + outputParameters: "{count} 個輸出參數", + parameterName: "名稱", + parameterType: "類型", + parameterMode: "模式", + parameterValue: "值", + parameterNull: "NULL", + parameterDefault: "DEFAULT", + parameterDefaultHint: "勾選 DEFAULT 時會省略該參數,讓資料庫使用預存程序定義中的預設值。", + parameterValuePlaceholder: "輸入參數值", + outputOnly: "僅輸出", + sqlPreview: "SQL 預覽", + resetSqlPreview: "重設 SQL", + openInSqlEditor: "在編輯器中開啟", + dropProcedure: "刪除預存程序", + dropFunction: "刪除函式", + confirmDropViewTitle: "刪除檢視", + confirmDropViewMessage: "確定要刪除檢視「{name}」嗎?", + confirmDropProcedureTitle: "刪除預存程序", + confirmDropProcedureMessage: "確定要刪除預存程序「{name}」嗎?", + confirmDropFunctionTitle: "刪除函式", + confirmDropFunctionMessage: "確定要刪除函式「{name}」嗎?", + dropViewSuccess: "檢視「{name}」已刪除", + dropProcedureSuccess: "預存程序「{name}」已刪除", + dropFunctionSuccess: "函式「{name}」已刪除", + emptyTableSuccess: "已清空「{name}」的所有資料", + truncateTableSuccess: "資料表「{name}」已截斷", + duplicateStructureSuccess: "已複製資料表結構為「{name}」", + tableOperationFailed: "操作失敗:{message}", + duplicateNameTitle: "複製資料表結構", + duplicateNamePlaceholder: "新資料表名稱", + createDatabase: "建立資料庫", + createDuckDbFile: "建立資料庫檔案", + dropDatabase: "刪除資料庫", + confirmDropDatabaseTitle: "刪除資料庫", + confirmDropDatabaseMessage: "確定要刪除資料庫「{name}」嗎?這將永久刪除該資料庫及其所有資料。", + createDatabaseSuccess: "資料庫「{name}」已建立", + createDuckDbFileSuccess: "DuckDB 資料庫檔案「{name}」已建立並附加", + createDuckDbFileDesktopOnly: "建立 DuckDB 資料庫檔案僅支援桌面端", + dropDatabaseSuccess: "資料庫「{name}」已刪除", + createDatabaseNamePlaceholder: "資料庫名稱", + createDatabaseCharset: "字元集", + createDatabaseCharsetPlaceholder: "例如 utf8mb4", + createDatabaseCollation: "排序規則", + createDatabaseCollationPlaceholder: "例如 utf8mb4_unicode_ci", + createSchema: "建立 Schema", + dropSchema: "刪除 Schema", + confirmDropSchemaTitle: "刪除 Schema", + confirmDropSchemaMessage: "確定要刪除 Schema「{name}」嗎?這將永久刪除該 Schema 及其所有物件。", + createSchemaSuccess: "Schema「{name}」已建立", + dropSchemaSuccess: "Schema「{name}」已刪除", + createSchemaNamePlaceholder: "Schema 名稱", + }, + visibleDatabases: { + title: "顯示資料庫", + description: "選擇「{connection}」下要在側邊欄顯示的資料庫。", + searchPlaceholder: "搜尋資料庫……", + selectedCount: "已選擇 {selected}/{total}", + selectAll: "全選", + clear: "清空", + showAll: "顯示全部", + showSystemDatabases: "顯示系統庫", + save: "儲存", + loadFailed: "載入資料庫失敗:{message}", + }, + tree: { + savedSql: "SQL 庫", + defaultDatabase: "預設庫", + columns: "欄位", + indexes: "索引", + foreignKeys: "外鍵", + triggers: "觸發器", + tables: "資料表", + views: "檢視", + procedures: "預存程序", + functions: "函式", + partitions: "分割區", + objectBrowser: "在物件瀏覽器中檢視 ({count})", + }, + objects: { + all: "全部", + tables: "資料表", + views: "檢視", + procedures: "預存程序", + functions: "函式", + table: "資料表", + partitions: "{count} 個分割區", + view: "檢視", + procedure: "預存程序", + function: "函式", + name: "名稱", + type: "類型", + createdAt: "建立時間", + updatedAt: "更新時間", + source: "原始碼", + saveSource: "儲存", + cancelEdit: "取消", + sourceSaved: "原始碼已儲存", + sourceSaveFailed: "儲存原始碼失敗:{message}", + schemaColumn: "Schema", + comment: "註解", + loadingSchemas: "載入 Schema……", + schema: "Schema", + search: "搜尋資料表、檢視、函式或預存程序……", + loading: "正在載入物件……", + empty: "沒有找到物件", + selectedTables: "已選擇 {count} 張資料表", + exportSelected: "匯出所選", + dropSelected: "刪除所選", + clearSelection: "清空選擇", + confirmBatchDropTitle: "刪除所選資料表", + confirmBatchDropMessage: "確定要刪除已選擇的 {count} 張資料表嗎?此操作無法復原。", + batchDropSuccess: "已刪除 {count} 張資料表", + }, + structureEditor: { + title: "編輯資料表結構", + createTitle: "建立資料表", + editTabTitle: "編輯: {tableName}", + tableName: "資料表名稱", + refresh: "重新整理結構", + density: "密度", + densityCompact: "緊湊", + densityStandard: "標準", + densityComfortable: "寬鬆", + columns: "欄位", + indexes: "索引", + foreignKeys: "外鍵", + triggers: "觸發器", + addColumn: "新增欄位", + addIndex: "新增索引", + columnName: "欄位", + dataType: "類型", + length: "長度", + typePlaceholder: "選擇或輸入……", + noMatchingType: "沒有相符類型", + nullable: "可為空", + primaryKey: "主鍵", + defaultValue: "預設值", + comment: "註解", + editComment: "編輯註解", + commentPlaceholder: "輸入欄位註解……", + tableCommentPlaceholder: "輸入資料表註解……", + tableCommentUnsupported: "此資料庫不支援資料表註解", + actions: "操作", + indexName: "索引", + indexColumns: "欄位清單", + indexColumnsPlaceholder: "選擇欄位……", + unique: "唯一", + indexType: "類型", + includedColumns: "包含", + includedColumnsPlaceholder: "選擇欄位……", + filter: "過濾條件", + primary: "主鍵", + drop: "刪除", + restore: "復原", + remove: "移除", + moveColumnUp: "上移欄位", + moveColumnDown: "下移欄位", + yes: "是", + no: "否", + emptyReadonly: "暫無記錄", + sqlPreview: "SQL 預覽", + noChanges: "暫無變更", + apply: "套用變更", + ready: "可執行", + saved: "資料表結構已更新", + extendedProperties: "擴充屬性", + autoIncrement: "自動遞增", + onUpdateCurrentTimestamp: "自動更新時間", + identity: "識別欄位", + identityGeneration: "產生方式", + identitySeed: "起始值", + identityIncrement: "增量", + }, + diagram: { + title: "關係圖", + open: "檢視關係圖", + selectConnection: "選擇連線", + selectDatabase: "選擇資料庫", + selectSchema: "選擇 Schema", + searchTables: "搜尋資料表/欄位/外鍵……", + refresh: "重新整理關係圖", + loading: "正在讀取關係……", + loadingProgress: "正在讀取關係…… {loaded}/{total}", + partialError: "{count} 張資料表的 metadata 讀取失敗,已跳過", + selectTarget: "請選擇連線和資料庫", + empty: "沒有可顯示的資料表", + noMatches: "沒有相符的資料表", + tablesCount: "{count} 張資料表", + relationshipsCount: "{count} 條關係", + tableMode: "資料表結構圖", + engineeringMode: "工程 ER 圖", + allTables: "全部資料表", + relatedTables: "相關資料表", + moreColumns: "+ {count} 個欄位", + exportSvg: "匯出 SVG", + exportedSvg: "SVG 已匯出", + exportSvgFailed: "匯出 SVG 失敗:{message}", + zoomIn: "放大", + zoomOut: "縮小", + resetLayout: "重設布局", + }, + redis: { + selectKey: "選擇一個 key 檢視值", + noKeys: "未找到 key", + pattern: "模式 (例如 user:*)", + valueSearchPlaceholder: "值包含……", + searchByKey: "鍵", + searchByValue: "值", + keys: "{count} 個 key", + loadingKeys: "正在載入 key……", + searchingValues: "正在按值搜尋……", + loadMoreKeys: "載入更多", + items: "{count} 個元素", + fields: "{count} 個欄位", + members: "{count} 個成員", + loadedItems: "已載入 {loaded} / 共 {total} 個元素", + loadedFields: "已載入 {loaded} / 共 {total} 個欄位", + loadedMembers: "已載入 {loaded} / 共 {total} 個成員", + entries: "{count} 筆項目", + noExpiry: "永不過期", + columnType: "類型", + columnKey: "鍵", + columnValue: "值", + columnSize: "大小", + columnTTL: "TTL", + binaryStringReadonlyHint: "二進位字串值會以逸出文字唯讀顯示;不支援編輯原始位元組。", + selectedKeys: "已選擇的 key", + deleteGroup: "刪除群組", + deleteGroupDetails: "{target}\n{count} 個 key", + flushDb: "清空目前 DB", + flushDbMessage: "這會刪除目前 Redis 資料庫中的所有 key,且無法復原。確認繼續?", + flushDbDetails: "目標: Redis db{db}\n範圍: 目前 DB 的所有 key", + flushDbConfirm: "清空 DB", + flushDbSuccess: "Redis db{db} 已清空", + keyDetail: "鍵詳情", + commandLine: "命令列", + selectKeyForDetail: "選擇一個 key 檢視詳情", + commandWelcome: "歡迎使用 Redis 命令列控制台", + commandPlaceholder: "Redis 命令,如 GET user:1", + commandPrefix: "命令", + commandHint: "輸入 Redis 命令,如 SMEMBERS feature:flags", + executeCommand: "執行命令", + commandEmpty: "請輸入 Redis 命令", + commandEmptyResult: "執行 Redis 命令後會在這裡顯示結果", + commandBlocked: "出於安全考慮,此 Redis 命令已被阻止", + createKey: "建立 key", + createKeyName: "Key", + createKeyNamePlaceholder: "user:1", + createKeyType: "類型", + createValue: "值", + createMember: "成員", + createField: "欄位", + createFieldPlaceholder: "name", + createScore: "分數", + createValuePlaceholder: "value", + createKeySubmit: "建立", + createKeyNameRequired: "請輸入 key 名稱", + createFieldRequired: "請輸入 hash 欄位", + createScoreInvalid: "請輸入有效分數", + member: "成員", + memberDetail: "成員詳情", + viewMember: "檢視完整內容", + copyMember: "複製成員", + copied: "已複製", + editMember: "編輯", + jsonView: "JSON 檢視", + rawContent: "原始內容", + wordWrap: "自動換行", + }, + mongo: { + documents: "{count} 個文件", + addField: "新增欄位", + deleteField: "刪除欄位", + field: "欄位", + value: "值", + fieldPlaceholder: "欄位名", + emptyCollection: "空集合", + selectDocument: "選擇一個文件", + readonlyId: "_id 唯讀", + invalidJsonValue: "目前儲存格不是合法 JSON 值", + duplicateField: "欄位名重複:{field}", + edit: "編輯", + documentView: "文件檢視", + tableView: "表格檢視", + filterPlaceholder: "過濾條件……", + sortPlaceholder: "排序條件……", + }, + history: { + title: "歷史", + search: "搜尋歷史……", + empty: "暫無歷史記錄", + failed: "失敗", + success: "成功", + restore: "復原到編輯器", + copy: "複製 SQL", + delete: "刪除", + clear: "清空歷史", + viewDetails: "檢視詳情", + analyzeWithAi: "AI 分析", + aiAnalyzeNoConnection: "無法分析這條歷史記錄:對應連線已不存在。", + aiAnalysisTab: "歷史分析", + rollback: "回溯", + rollbackSql: "回溯 SQL", + rollbackAvailable: "可回溯", + rollbackUnavailable: "無回溯 SQL", + rollbackConfirm: "將執行這條歷史記錄產生的回溯 SQL,確認繼續嗎?", + rollbackSuccess: "回溯已執行", + rollbackFailed: "回溯失敗:{message}", + confirmDelete: "確認刪除這條歷史記錄嗎?", + confirmClear: "確認清空所有歷史記錄嗎?", + filters: { + all: "全部", + query: "查詢", + data_change: "資料變更", + schema_change: "結構變更", + failed: "失敗", + }, + kinds: { + query: "查詢", + data_change: "資料變更", + schema_change: "結構變更", + import: "匯入", + transfer: "傳輸", + }, + kindShort: { + query: "查詢", + data_change: "變更", + schema_change: "結構", + import: "匯入", + transfer: "傳輸", + }, + detail: { + kind: "類型", + operation: "操作", + connection: "連線", + database: "資料庫", + target: "物件", + time: "時間", + duration: "耗時", + affectedRows: "影響筆數", + rollback: "回溯", + status: "狀態", + error: "錯誤", + }, + }, + dangerDialog: { + title: "危險操作", + message: "此 SQL 語句可能不可逆地修改或刪除資料,確認要執行嗎?", + deleteMessage: "此刪除操作可能不可逆,確認要繼續嗎?", + deleteConfirm: "確認刪除", + deleteRowMessage: "此列將被標記為刪除,儲存後會從資料庫刪除,確認要繼續嗎?", + deleteRowsMessage: "這 {count} 列將被標記為刪除,儲存後會從資料庫刪除,確認要繼續嗎?", + deleteRowDetails: "資料表:{table}", + deleteRowDetailsNoTable: "目前結果集中的列", + redisKeyDetails: "Redis key:{key}", + redisHashFieldDetails: "Redis hash:{key}\n欄位:{field}", + redisListItemDetails: "Redis list:{key}\n索引:{index}", + redisSetMemberDetails: "Redis set:{key}\n成員:{member}", + mongoDocumentDetails: "MongoDB 集合:{collection}\n文件 _id:{id}", + mongoFieldDetails: "欄位:{field}", + mongoArrayItemDetails: "陣列項索引:{index}", + cancel: "取消", + confirm: "執行", + }, + transfer: { + title: "資料傳輸", + source: "來源", + target: "目標", + sourceConnection: "來源連線", + sourceDatabase: "來源資料庫", + sourceSchema: "來源 Schema", + targetConnection: "目標連線", + targetDatabase: "目標資料庫", + targetSchema: "目標 Schema", + tables: "資料表", + selectAll: "全選", + deselectAll: "取消全選", + createTable: "目標資料表不存在時自動建立", + transferMode: "傳輸模式", + modeAppend: "追加 (INSERT)", + modeOverwrite: "覆蓋 (TRUNCATE + INSERT)", + modeUpsert: "更新插入 (按主鍵 INSERT 或 UPDATE)", + batchSize: "每批筆數", + start: "開始傳輸", + cancel: "取消", + progress: "傳輸中……", + transferring: "正在傳輸 {table}……", + completed: "傳輸完成", + completedWithErrors: "完成,但有 {count} 張資料表失敗 | 完成,但有 {count} 張資料表失敗", + failed: "傳輸失敗", + cancelled: "傳輸已取消", + rowsTransferred: "已傳輸 {count} 列", + tableProgress: "{current} / {total} 張資料表", + noSqlConnections: "暫無可用的 SQL 連線", + selectTables: "選擇要傳輸的表", + searchTables: "搜尋資料表名稱……", + noTables: "沒有資料表", + selectSourceFirst: "請先選擇來源連線和資料庫", + tableError: "資料表 {table} 傳輸失敗:{error}", + overallProgress: "整體進度", + dataTransfer: "資料傳輸", + }, + tableImport: { + title: "匯入資料表資料", + target: "目標資料表", + selectFile: "選擇檔案", + file: "檔案", + rows: "列數", + mapped: "已映射", + mapping: "欄位映射", + skipColumn: "跳過", + preview: "預覽", + mode: "匯入模式", + append: "追加資料", + truncate: "清空後匯入", + progress: "進度", + start: "開始匯入", + success: "已匯入 {count} 行", + }, + sqlFile: { + title: "執行 SQL 檔案", + file: "檔案", + selectSqlFile: "選擇 SQL 檔案", + browse: "瀏覽", + previewingLines: "預覽 {count} 行", + previewingFirstLines: "預覽前 {count} 行", + target: "目標", + connection: "連線", + selectConnection: "選擇連線", + database: "資料庫", + selectDatabase: "選擇資料庫", + databasePlaceholder: "資料庫名稱", + options: "選項", + continueOnError: "錯誤後繼續", + cancelling: "正在取消……", + cancel: "取消", + execute: "執行", + statement: "語句", + succeeded: "成功", + failed: "失敗", + affectedRows: "影響列數", + currentStatement: "目前語句", + status: { + idle: "閒置", + started: "已開始", + running: "執行中", + statementDone: "語句完成", + statementFailed: "語句失敗", + done: "完成", + error: "錯誤", + cancelled: "已取消", + }, + }, + diff: { + title: "比較資料庫", + source: "來源資料庫", + target: "目標資料庫", + selectConnection: "選擇連線", + selectDatabase: "選擇資料庫", + selectSchema: "選擇 Schema", + compare: "開始比較", + comparing: "正在比較結構……", + noDifferences: "兩個資料庫結構完全一致", + table: "資料表", + status: "狀態", + details: "詳情", + added: "新增", + removed: "刪除", + modified: "修改", + indexes: "索引", + foreignKeys: "外鍵", + triggers: "觸發器", + newTable: "新增資料表", + dropTable: "刪除資料表", + generatedSql: "同步 SQL", + copySql: "複製 SQL", + executeSync: "執行同步", + syncSuccess: "同步執行成功", + swap: "交換來源與目標", + syncProgress: "執行中: {current}/{total}", + syncSummary: "成功 {success} 條,失敗 {failed} 條", + }, + dataCompare: { + title: "比較資料", + selectTable: "選擇資料表", + sourceTables: "來源資料表", + targetTable: "目標資料表", + searchTables: "搜尋資料表名稱……", + selectedTables: "已選擇 {selected}/{total}", + selectAllTables: "全選", + deselectAllTables: "取消全選", + selectSourceTables: "請先選擇來源連線和資料庫,再選擇要比較的資料表", + noTables: "沒有可比較的資料表", + autoMatchHint: "批次模式會依同名資料表自動比對目標資料表", + matchedTables: "已比對 {matched}/{total} 張資料表", + missingTargetTables: "目標端缺少同名資料表:{tables}", + keyColumns: "鍵欄位", + keyColumnsPlaceholder: "以逗號分隔主鍵或唯一欄位", + keyColumnsAutoHint: "留空時,單一資料表會自動偵測主鍵;批次比較則會逐表偵測。", + rowCounts: "來源 {source} 列,目標 {target} 列;已完成完整比較", + previewLimitOption: "預覽 {count}", + compare: "開始比較資料", + summary: "新增 {added} 列,刪除 {removed} 列,修改 {modified} 列", + batchSummary: + "已比較 {tables} 張資料表:{different} 張有差異,{same} 張一致,{failed} 張失敗;新增 {added} 列,刪除 {removed} 列,修改 {modified} 列", + comparingTable: "正在比較 {current}/{total}:{table}", + keyColumnsInline: "鍵欄位:{columns}", + selectedInline: "已選擇 {selected}/{total}", + selectedSummary: "目前選擇:新增 {added} 列,刪除 {removed} 列,修改 {modified} 列", + selectAllKind: "全選{kind}", + clearKind: "清空{kind}", + clearSelection: "清空全部選擇", + showAllRows: "顯示全部 {count} 條", + showLessRows: "收起", + remainingRows: "還有 {count} 條未在預覽中展開", + planningSync: "正在更新同步計畫……", + noSelectedDifferences: "目前沒有選取任何要執行的差異", + planSummary: "執行計畫:新增 {inserts} 條,更新 {updates} 條,刪除 {deletes} 條,共 {statements} 條 SQL", + statusDifferent: "有差異", + statusSame: "一致", + statusError: "失敗", + noKeyColumns: "請至少選擇一個鍵欄位", + missingKeyColumns: "鍵欄位必須同時存在於來源與目標資料表:{columns}", + noCommonColumns: "來源與目標資料表沒有可比較的共同欄位", + targetTableMissing: "找不到目標資料表:{table}", + noComparableTables: "沒有可比較的資料表。請檢查目標端是否存在同名資料表。", + noDifferences: "資料完全一致,不需要產生同步 SQL", + syncSuccess: "資料同步執行成功", + }, + settings: { + title: "設定", + editorTab: "編輯器", + appearanceTab: "外觀", + navigationTab: "導覽", + redisTab: "Redis", + shortcutsTab: "快速鍵", + snippetsTab: "程式碼片段", + syncTab: "同步", + aiTab: "AI", + jdbcTab: "JDBC 驅動程式", + securityTab: "安全", + aboutTab: "關於我們", + fontFamily: "字型", + selectFont: "選擇字型……", + searchFont: "搜尋字型……", + loadingFonts: "正在載入字型……", + noFontsFound: "未找到字型", + useCustomFont: "使用「{font}」", + languageTitle: "語言 / Language", + fontSize: "字級", + uiScale: "介面縮放", + uiScaleDescription: "按比例縮放整個桌面端介面,適合高 DPI 螢幕;修改後立即生效,並在下次啟動時復原。", + theme: "主題", + selectTheme: "選擇主題……", + followAppTheme: "跟隨應用程式主題", + appLayout: "介面配置", + appLayoutSeparated: "現代分隔", + appLayoutSeparatedDescription: "面板分隔更清晰,分頁為塊狀樣式。", + appLayoutClassic: "經典緊湊", + appLayoutClassicDescription: "使用連續標籤欄和更緊湊的面板標題。", + iconTheme: "圖示與 Logo 樣式", + iconThemeDefault: "預設", + iconThemeDefaultDescription: "使用彩色 DBX 應用程式圖示。", + iconThemeBlack: "黑色", + iconThemeBlackDescription: "視窗、系統匣和 Logo 都使用黑色 DBX 標誌。", + showTrayIcon: "顯示系統匣/選單欄圖示", + showTrayIconDescription: "關閉後不顯示圖示,但關閉視窗仍會像之前一樣隱藏到後台。", + dataGridDisplay: "資料表格顯示", + showColumnCommentsInHeader: "在欄位名稱下方顯示註解", + showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。", + compactColumnHeaderActions: "收起欄位表頭工具", + compactColumnHeaderActionsDescription: "將排序、格式設定和本機篩選工具移到更多選單,讓欄位名稱優先顯示。", + sidebarActivation: "側邊欄開啟方式", + sidebarActivationSingle: "單擊開啟", + sidebarActivationSingleDescription: "單擊即可開啟側邊欄中的可操作項目。", + sidebarActivationDouble: "雙擊開啟", + sidebarActivationDoubleDescription: "單擊選取項目,雙擊開啟項目。", + sidebarObjectDisplay: "側邊欄物件配置", + sidebarObjectDisplayGrouped: "進階檢視", + sidebarObjectDisplayGroupedDescription: "針對包含更多物件類型的資料庫,依資料表、檢視、預存程序和函式分組物件。", + sidebarObjectDisplaySimple: "簡單檢視", + sidebarObjectDisplaySimpleDescription: "直接在每個資料庫或 schema 下列出資料表與檢視,減少中間層級。", + autoSelectActiveSidebarNode: "一律選取已開啟項目", + autoSelectActiveSidebarNodeDescription: "切換分頁時,在側邊欄選取相符的可見資料表、集合或 SQL 檔案。", + sidebarHiddenTablePrefixes: "隱藏資料表名稱字首", + sidebarHiddenTablePrefixesDescription: + "每行一個字首。只縮短側邊欄中的資料表、檢視和集合標籤;工具提示和實際操作仍使用完整名稱。", + sidebarHiddenTablePrefixesPlaceholder: "範例:\nODS_\nT8Y2_LONG_", + sidebarHideTableComments: "隱藏側邊欄資料表註解", + sidebarHideTableCommentsDescription: "隱藏側邊欄樹狀清單中名稱旁的資料表/檢視註解,以節省橫向空間。", + sidebarAllowHorizontalScroll: "允許側邊欄水平捲動", + sidebarAllowHorizontalScrollDescription: "透過啟用側邊欄的水平捲動功能,完整顯示長表格、檢視和集合的名稱", + snippetsDescription: "自訂編輯器中觸發的 SQL 程式碼片段範本。", + snippetsAdd: "新增片段", + snippetsLabel: "顯示名", + snippetsPrefix: "觸發鍵", + snippetsBody: "SQL 內容", + snippetsLabelPlaceholder: "如:select *", + snippetsPrefixPlaceholder: "如:sel", + snippetsBodyPlaceholder: "SELECT *\nFROM table\nLIMIT 100;", + snippetsRestoreDefaults: "還原預設", + snippetsAddTitle: "新增片段", + snippetsEditTitle: "編輯片段", + syncWebDavTitle: "WebDAV 同步", + syncWebDavDescription: "上傳 DBX 快照到相容 WebDAV 的儲存服務,或從該服務還原快照。", + syncEndpoint: "WebDAV 地址", + syncUsername: "使用者名稱", + syncPassword: "密碼", + syncPasswordPlaceholder: "輸入密碼", + syncClearSavedPassword: "清除已儲存的密碼", + syncRememberWebDavPassword: "記住 WebDAV 應用密碼", + syncSavedPassword: "(已儲存)", + syncRememberWebDavPasswordDescription: "密碼會加密儲存在本機,不會同步到 WebDAV,也不會取代同步密碼。", + syncRemotePath: "遠端快照路徑", + syncRemotePathDescription: "上傳時 DBX 會自動建立缺失的父目錄。", + syncSecretNotice: + "預設只同步連線詳細資料和設定。資料庫密碼、SSH 密碼、代理伺服器密碼、連線字串和 AI API Key 會保留在本機。", + syncSecrets: "同步加密後的敏感資訊", + syncSecretsDescription: "開啟後,DBX 會先加密資料庫密碼、SSH 密碼和 AI API Key,再上傳到 WebDAV。", + syncSecretsPassphrase: "同步密碼", + syncSecretsPassphraseDescription: "此密碼只用於加密和還原敏感資料。DBX 不會儲存;每台裝置都需要再次輸入。", + syncTest: "測試", + syncUpload: "上傳", + syncDownload: "下載", + syncTestSuccess: "WebDAV 目標可存取。", + syncUploadSuccess: "已上傳 {bytes} 位元組到 {path}。", + syncDownloadSuccess: "已從 {path} 下載並套用 {bytes} 位元組。", + syncSecretsApplied: "已還原加密敏感資訊。", + syncSecretsSkipped: "偵測到加密敏感資訊,但本次未還原。", + syncDownloadConfirm: "要下載並套用遠端 DBX 快照嗎?本機 metadata 和已儲存的 SQL 會被取代。", + apply: "套用", + applyAndClose: "套用並關閉", + reset: "重設", + resetDefaults: "還原預設", + save: "儲存", + cancel: "取消", + executeMode: "執行模式 (Cmd+Enter)", + executeModeAll: "執行全部 SQL", + executeModeCurrent: "執行指標所在語句", + wordWrap: "自動換行", + wordWrapDescription: "長 SQL 在編輯器寬度內自動折行顯示", + redisScanPageSize: "Redis 掃描數量", + redisScanPageSizeDescription: "瀏覽 Redis Key 時每次 SCAN 請求的 Key 數量。", + redisScanPageSizeOption: "{count} 個 Key", + shortcutExecuteSql: "執行 SQL", + shortcutFind: "尋找", + shortcutReplace: "取代", + shortcutSaveSql: "儲存 SQL", + shortcutAcceptCompletion: "接受補全", + shortcutCopyCurrentRow: "複製目前資料列", + shortcutDeleteCurrentRow: "刪除目前資料列", + shortcutNewQuery: "建立查詢", + shortcutCloseTab: "關閉分頁", + shortcutFocusSearch: "聚焦搜尋", + shortcutZoomInUi: "放大全域介面", + shortcutZoomOutUi: "縮小全域介面", + shortcutResetUiZoom: "重設全域介面縮放", + shortcutRefreshData: "重新整理資料", + shortcutToggleTranspose: "切換轉置檢視", + shortcutCancelSearch: "取消搜尋", + shortcutScopeGlobal: "全域", + shortcutScopeEditor: "SQL 編輯器", + shortcutScopeGrid: "資料表格", + shortcutScopeSearch: "搜尋框", + shortcutPressShortcut: "按下快速鍵", + shortcutConflict: "這個快速鍵與同一作用域內的其他操作衝突。", + preview: "即時預覽", + jdbcPlugin: "DBX JDBC 外掛程式", + jdbcPluginInstall: "安裝 JDBC 外掛程式", + jdbcPluginInstallSuccess: "JDBC 外掛程式已安裝", + jdbcPluginUpdate: "更新", + jdbcPluginUninstall: "解除安裝", + jdbcPluginUninstallSuccess: "JDBC 外掛程式已解除安裝", + jdbcPluginInstalled: "已安裝:v{version}", + jdbcPluginIncompatible: "協定不相容", + jdbcPluginNotInstalled: "未安裝", + jdbcDrivers: "JDBC 驅動程式 JAR", + jdbcImport: "匯入 JAR", + jdbcImportPath: "匯入路徑", + jdbcDriverPathPlaceholder: "/path/to/driver.jar", + jdbcImportSuccess: "已匯入 {count} 個驅動程式檔", + jdbcDeleteSuccess: "驅動程式已刪除", + jdbcNoDrivers: "還沒有匯入 JDBC 驅動程式。", + aboutDescription: "開源、輕量的資料庫管理工具。", + community: "社群", + qqGroup: "QQ 群", + wechatGroup: "微信交流群", + wechatGroupInvite: "騰訊文件邀請", + project: "專案", + openSource: "開源倉庫", + officialDocs: "官方文件", + }, + driverStore: { + progressJreExtract: "解壓縮 JRE……", + progressDownloadJre: "下載 JRE", + progressDownloadDriver: "下載驅動程式", + usageTitle: "空間占用明細", + usageTotal: "總計 {size}", + usageTotalLabel: "總計", + calculating: "統計中……", + usageManagedJre: "託管 JRE", + usageAgentDrivers: "內建驅動程式 Agent", + usageJdbcPlugin: "JDBC 外掛程式", + usageJdbcDriverJars: "JDBC 驅動程式 JAR", + javaRuntimeSaved: "Java 執行環境設定已儲存", + javaRuntimeSaveFailed: "Java 執行環境設定失敗: {error}", + chooseJavaExecutable: "選擇 Java 可執行檔", + driverInstallSuccess: "{label} 驅動程式安裝成功", + driverInstallFailed: "{label} 驅動程式安裝失敗: {error}", + upgradeAllSuccess: "{count} 個驅動程式更新完成", + upgradeAllFailed: "批次更新失敗: {error}", + driverUninstallSuccess: "{label} 驅動程式已解除安裝", + driverUninstallFailed: "{label} 驅動程式解除安裝失敗: {error}", + chooseOfflineDriverPackage: "選擇離線驅動程式包", + offlineImportSuccess: "離線匯入完成,已安裝 {count} 個驅動程式", + offlineImportFailed: "離線匯入失敗: {error}", + driverImportSuccess: "{label} 驅動程式匯入成功", + driverImportFailed: "{label} 驅動程式匯入失敗: {error}", + chooseDriverJar: "選擇驅動程式 JAR 檔案", + jreReinstallSuccess: "JRE {jre} 重新安裝成功", + jreReinstallFailed: "JRE {jre} 重新安裝失敗: {error}", + jreUninstallSuccess: "JRE {jre} 已解除安裝", + chooseJdbcPluginZip: "選擇 JDBC 外掛程式 zip 檔", + agentDrivers: "內建驅動程式", + jdbcDrivers: "JDBC 驅動程式", + importing: "匯入中……", + importOfflinePackage: "匯入離線包", + refresh: "重新整理", + javaRuntime: "Java 執行環境", + javaRuntimeManaged: "DBX 託管 JRE", + javaRuntimeSystem: "系統 Java", + javaRuntimeCustom: "自訂路徑", + saving: "儲存中……", + customJavaPathPlaceholder: "/path/to/java 或 /path/to/jdk", + choose: "選擇", + systemJavaHint: "使用目前環境 PATH 中的 java。", + jreRuntimeTitle: "JRE {jre} 執行環境", + notInstalled: "未安裝", + reinstalling: "重新安裝中", + installing: "安裝中", + install: "安裝", + reinstall: "重新安裝", + uninstall: "解除安裝", + jreRuntime: "JRE 執行環境", + jreRuntimeAutoDownloadHint: "首次安裝驅動程式時自動下載", + driversUpdatable: "{count} 個驅動程式可更新", + upgradingProgress: "更新中 ({current}/{total})", + upgradeAll: "全部更新", + queued: "排隊中", + importLocalJar: "匯入本機 JAR", + updating: "更新中", + update: "更新", + localInstall: "本機安裝", + }, + databaseExport: { + title: "匯出資料庫", + includeStructure: "資料表結構 (DDL)", + dropTableIfExists: "匯出前新增 DROP TABLE IF EXISTS", + includeData: "資料表資料 (INSERT)", + includeObjects: "檢視 / 預存程序 / 函式", + tableSelection: "資料表", + selectedTables: "已選擇 {selected}/{total}", + selectAllTables: "全選", + filterTables: "搜尋資料表名稱……", + clearTables: "清空", + loadingTables: "正在載入資料表……", + tableLoadError: "載入資料表失敗:{error}", + noTables: "找不到資料表", + export: "匯出", + exporting: "正在匯出……", + currentTable: "目前: {table} ({current}/{total})", + rowsExported: "已匯出 {count} 列", + exportSuccess: "資料庫匯出成功", + exportError: "匯出失敗: {error}", + exportCancelled: "匯出已取消", + }, +}; diff --git a/apps/desktop/src/lib/ai.ts b/apps/desktop/src/lib/ai.ts index 87dcd4a37..a704495f0 100644 --- a/apps/desktop/src/lib/ai.ts +++ b/apps/desktop/src/lib/ai.ts @@ -10,7 +10,7 @@ import type { QueryTab, } from "@/types/database"; import * as api from "@/lib/api"; -import { currentLocale } from "@/i18n"; +import { currentLocale, type Locale } from "@/i18n"; import { aiTableMentionKey, type AiTableMention } from "@/lib/aiTableMentions"; import { aiSkillForAction } from "@/lib/aiSkills"; import { isSchemaAware } from "@/lib/databaseCapabilities"; @@ -18,6 +18,10 @@ import { isSchemaAware } from "@/lib/databaseCapabilities"; export type AiAction = "generate" | "explain" | "optimize" | "fix" | "convert" | "sampleData"; export type AiAssistantMode = "ask" | "agent"; +function isChineseLocale(locale: Locale): boolean { + return locale === "zh-CN" || locale === "zh-TW"; +} + export interface AiSchemaTable { schema?: string; name: string; @@ -47,7 +51,7 @@ export interface AiRequestInput { } export async function runAiAction(input: AiRequestInput, history?: api.AiMessage[]): Promise { - const isZh = currentLocale() === "zh-CN"; + const isZh = isChineseLocale(currentLocale()); const skill = aiSkillForAction(input.action); const systemPrompt = buildSystemPrompt(input.action, input.context, input.mode); const instruction = isZh ? skill.userInstruction.zh : skill.userInstruction.en; @@ -78,7 +82,7 @@ export async function runAiStream( sessionId?: string, onReasoningDelta?: (delta: string) => void, ): Promise { - const isZh = currentLocale() === "zh-CN"; + const isZh = isChineseLocale(currentLocale()); const skill = aiSkillForAction(input.action); const systemPrompt = buildSystemPrompt(input.action, input.context, input.mode); const instruction = isZh ? skill.userInstruction.zh : skill.userInstruction.en; @@ -136,7 +140,7 @@ export function buildSystemPrompt(action: AiAction, context: AiContext, mode: Ai const resultPreview = context.lastResultPreview ? `\nLast result preview:\n${context.lastResultPreview}\n` : ""; const lastError = context.lastError ? `\nLast error:\n${context.lastError}\n` : ""; - const isZh = currentLocale() === "zh-CN"; + const isZh = isChineseLocale(currentLocale()); const lines: string[] = [ ...buildBasePromptLines(isZh), diff --git a/apps/desktop/src/lib/localeOptions.ts b/apps/desktop/src/lib/localeOptions.ts index ed06c7cd1..2efdf8433 100644 --- a/apps/desktop/src/lib/localeOptions.ts +++ b/apps/desktop/src/lib/localeOptions.ts @@ -4,4 +4,5 @@ export const LOCALE_OPTIONS: { value: Locale; flag: string; label: string }[] = { value: "en", flag: "🇺🇸", label: "English" }, { value: "es", flag: "🇪🇸", label: "Español" }, { value: "zh-CN", flag: "🇨🇳", label: "简体中文" }, + { value: "zh-TW", flag: "繁", label: "繁體中文" }, ]; diff --git a/packages/app-tests/aiPrompt.test.ts b/packages/app-tests/aiPrompt.test.ts index bd232d892..fb31dc0d4 100644 --- a/packages/app-tests/aiPrompt.test.ts +++ b/packages/app-tests/aiPrompt.test.ts @@ -22,8 +22,11 @@ class MemoryStorage { } } +const localStorage = new MemoryStorage(); +localStorage.setItem("dbx-locale", "zh-CN"); + Object.defineProperty(globalThis, "localStorage", { - value: new MemoryStorage(), + value: localStorage, configurable: true, }); diff --git a/packages/app-tests/aiPromptEval.test.ts b/packages/app-tests/aiPromptEval.test.ts index d36517659..426677460 100644 --- a/packages/app-tests/aiPromptEval.test.ts +++ b/packages/app-tests/aiPromptEval.test.ts @@ -22,8 +22,11 @@ class MemoryStorage { } } +const localStorage = new MemoryStorage(); +localStorage.setItem("dbx-locale", "zh-CN"); + Object.defineProperty(globalThis, "localStorage", { - value: new MemoryStorage(), + value: localStorage, configurable: true, }); diff --git a/packages/app-tests/i18nLocale.test.ts b/packages/app-tests/i18nLocale.test.ts index dc48cd6e0..7adb9ffa9 100644 --- a/packages/app-tests/i18nLocale.test.ts +++ b/packages/app-tests/i18nLocale.test.ts @@ -10,13 +10,15 @@ test("normalizes exact supported locales", () => { assert.equal(normalizeLocale("en"), "en"); assert.equal(normalizeLocale("es"), "es"); assert.equal(normalizeLocale("zh-CN"), "zh-CN"); + assert.equal(normalizeLocale("zh-TW"), "zh-TW"); assert.equal(normalizeLocale("fr-FR"), null); assert.equal(normalizeLocale(null), null); }); test("maps user language tags to supported locales", () => { assert.equal(localeFromLanguageTag("zh-Hans-CN"), "zh-CN"); - assert.equal(localeFromLanguageTag("zh_TW"), "zh-CN"); + assert.equal(localeFromLanguageTag("zh_TW"), "zh-TW"); + assert.equal(localeFromLanguageTag("zh-Hant-HK"), "zh-TW"); assert.equal(localeFromLanguageTag("en-US"), "en"); assert.equal(localeFromLanguageTag("es-MX"), "es"); assert.equal(localeFromLanguageTag("fr-FR"), null);