From f9428cb336205b703140f03823b594252f91188c Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:29:11 +0900 Subject: [PATCH 1/5] refactor(i18n): migrate UI catalogs to the resolver-based message format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts 73 UI message catalogs from `{...} satisfies UiCatalog` (which requires every UI_LOCALES member, including a future `ko`) to `resolveUiMessageCatalog(defineUiMessageCatalog()({...}))`, where only `en` is required and other locales are optional DeepPartial overlays merged over it. This is the compat PR me2seeks asked for in the #5100 review thread, ahead of that PR's `ko` core commit, so the resolver migration is validated against the existing en/zh-CN/zh-TW locales before any new locale lands. No translated strings change. Every converted catalog was already a complete Record, so resolveUiMessageCatalog's merge (translation wins when present, otherwise falls back to en) reproduces the exact same values for every existing locale. Two real issues surfaced during conversion, not just mechanical risk: - astryx-i18n.tsx's OVERRIDES_BY_LOCALE was excluded from this PR. Its `Overrides` type is self-locale-keyed (each locale's own value is keyed by that same locale name, to match Astryx's override API shape), which breaks mergeUiMessages: it walks the English fallback's top-level keys ('en'), never finds that key in the zh-CN/ zh-TW override objects (whose top-level key is their own locale name), and silently discards all of their content. Wrapping this catalog would have been a real behavior regression, not a type-only change, so it stays on `satisfies UiCatalog`. - DeepPartial and ExactMessageShape (packages/core/src/ui-locale.ts) decompose object types via `keyof`, which is `never` for a bare function type, collapsing any function-valued catalog leaf (e.g. a `(name: string) => string` copy formatter) to `{}` during inference and losing its parameter types. This surfaced as TS7006 implicit-any errors on catalogs with parameterized copy. Fixed per-catalog by hoisting each locale's block into its own `const x: T = {...}` (using the full, non-partial T, since these catalogs are already complete for every locale) before passing it into defineUiMessageCatalog, which sidesteps the generic inference path entirely. Applied to host-handoff-copy.ts (manual) and 23 other catalogs with function-valued leaves (scripted). Verified: full workspace build, typecheck, and scripts/check-locale-hygiene.mjs are clean; lint and format pass; the full test suite passes (confirmed via a pristine-vs-modified comparison — concurrent heavy workspace suites had been causing unrelated timing flakiness in runtime-host's integration tests, not this change). Co-Authored-By: Claude Sonnet 5 --- .../main/__tests__/bot-chat-detail.test.ts | 14 +- .../main/__tests__/health-center-copy.test.ts | 12 +- .../__tests__/settings-memory-copy.test.ts | 20 +- apps/desktop/src/main/browser-message-box.ts | 4 +- .../main/client-settings-confirmation-copy.ts | 6 +- .../src/main/computer-use/status-item.ts | 19 +- apps/desktop/src/main/main.ts | 16 +- .../src/main/native-diagnostic-dialog-copy.ts | 19 +- .../src/main/native-file-dialog-copy.ts | 6 +- apps/desktop/src/main/notifications-policy.ts | 6 +- .../permission-overlay-copy.ts | 21 +- apps/desktop/src/main/project-picker-copy.ts | 4 +- .../main/runtime-host-artifacts-ipc-main.ts | 6 +- .../src/main/runtime-host-config-ipc-main.ts | 6 +- .../src/main/runtime-host-quit-copy.ts | 6 +- .../src/main/runtime-host-wsl-handoff.ts | 6 +- apps/desktop/src/main/settings-recovery.ts | 10 +- apps/desktop/src/main/windows-app-tray.ts | 6 +- .../contracts/conversation-copy.ts | 19 +- .../application/contracts/transcript-copy.ts | 6 +- .../app-update/locales/app-update-copy.ts | 6 +- .../settings-provider-copy.ts | 6 +- .../conversation/model/executor-submission.ts | 6 +- .../locales/runtime-host-handoff-copy.ts | 6 +- .../workhub/locales/workhub-live-copy.ts | 6 +- .../src/renderer/locales/agent-graph-copy.ts | 19 +- .../src/renderer/locales/artifact-copy.ts | 19 +- .../src/renderer/locales/browser-copy.ts | 19 +- .../locales/capability-reason-copy.ts | 6 +- .../locales/external-session-import-copy.ts | 19 +- apps/desktop/src/renderer/locales/mcp-copy.ts | 19 +- .../src/renderer/locales/onboarding-copy.ts | 6 +- .../src/renderer/locales/peer-mesh-copy.ts | 6 +- .../locales/permission-center-copy.ts | 19 +- .../src/renderer/locales/plan-mode-copy.ts | 19 +- .../locales/session-collaboration-copy.ts | 4 +- .../renderer/locales/session-local-copy.ts | 6 +- .../src/renderer/locales/settings-bot-copy.ts | 10 +- .../locales/settings-daily-review-copy.ts | 6 +- .../renderer/locales/settings-data-copy.ts | 6 +- .../locales/settings-external-agents-copy.ts | 4 +- .../renderer/locales/settings-health-copy.ts | 45 +- .../src/renderer/locales/settings-jev-copy.ts | 6 +- .../renderer/locales/settings-memory-copy.ts | 19 +- .../locales/settings-navigation-copy.ts | 6 +- .../locales/settings-preferences-copy.ts | 19 +- .../locales/settings-projects-copy.ts | 6 +- .../renderer/locales/settings-shared-copy.ts | 6 +- .../locales/settings-subagents-copy.ts | 6 +- .../renderer/locales/settings-tasks-copy.ts | 6 +- .../locales/settings-test-result-copy.ts | 19 +- .../renderer/locales/settings-usage-copy.ts | 19 +- .../locales/settings-web-search-copy.ts | 6 +- .../src/renderer/locales/shell-copy.ts | 19 +- .../renderer/locales/shell-remaining-copy.ts | 6 +- .../renderer/locales/storage-usage-copy.ts | 6 +- .../renderer/locales/task-readiness-copy.ts | 6 +- .../renderer/locales/work-board-error-copy.ts | 6 +- .../settings/provider-display-copy.ts | 624 +++++++++--------- packages/core/src/redaction.ts | 50 +- packages/core/src/relative-time-policy.ts | 14 +- .../src/client/host-handoff-copy.ts | 396 +++++------ .../tool-activity-presentation.test.ts | 12 +- packages/ui/src/conversation-copy.ts | 19 +- packages/ui/src/daily-review-copy.ts | 19 +- packages/ui/src/executor-model-picker.tsx | 6 +- packages/ui/src/locale-helpers.ts | 6 +- packages/ui/src/runtime-resume-copy.ts | 6 +- packages/ui/src/scheduled-task-copy.ts | 19 +- packages/ui/src/shared-ui-copy.ts | 19 +- packages/ui/src/shell-controls-copy.ts | 6 +- packages/ui/src/skills-copy.ts | 19 +- packages/ui/src/tool-activity/copy.ts | 19 +- 73 files changed, 927 insertions(+), 942 deletions(-) diff --git a/apps/desktop/src/main/__tests__/bot-chat-detail.test.ts b/apps/desktop/src/main/__tests__/bot-chat-detail.test.ts index fe9c4040cad..74e8ac91fd5 100644 --- a/apps/desktop/src/main/__tests__/bot-chat-detail.test.ts +++ b/apps/desktop/src/main/__tests__/bot-chat-detail.test.ts @@ -27,7 +27,7 @@ import { pathToFileURL } from 'node:url'; import { createDefaultBotChannel } from '@maka/core/bot-chat-settings'; import { MAX_ALLOWED_USER_IDS, createDefaultSettings } from '@maka/core/settings'; import { BotRegistry, SlackBotBridge, WechatBridge, type BotStatus } from '@maka/runtime/bots'; -import { UI_LOCALES, type UiCatalog, type UiLocale } from '@maka/core/ui-locale'; +import { UI_LOCALES, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { AstryxLocaleProvider, LocaleProvider, ToastProvider } from '@maka/ui'; import { build } from 'esbuild'; import { parseHTML } from 'linkedom'; @@ -69,7 +69,11 @@ after(async () => { }); const invalidUsers = ['@alice', '@bob', '@carol', '@dave', '@eve']; -const expectedCopy = { +const expectedCopy = resolveUiMessageCatalog(defineUiMessageCatalog<{ + help: string; + cappedHelp: string; + warnings: [number, string][]; +}>()({ 'zh-CN': { help: 'Telegram 用户 ID 是 64 位整数;填入后只接收列表里这些 ID 的来信,其它人发的消息会被静默忽略(不会回弹任何提示)。', cappedHelp: 'Telegram 用户 ID 是 64 位整数;填入后只接收列表里这些 ID 的来信,其它人发的消息会被静默忽略(不会回弹任何提示)。 (已达到上限)', @@ -100,11 +104,7 @@ const expectedCopy = { [5, 'These entries are not numeric IDs and may be usernames, so they will not match anyone: @alice, @bob, @carol and 2 more'], ], }, -} satisfies UiCatalog<{ - help: string; - cappedHelp: string; - warnings: [number, string][]; -}>; +})); for (const locale of UI_LOCALES) { const expected = expectedCopy[locale]; diff --git a/apps/desktop/src/main/__tests__/health-center-copy.test.ts b/apps/desktop/src/main/__tests__/health-center-copy.test.ts index 5cf03c20ae3..28f5f6a6c58 100644 --- a/apps/desktop/src/main/__tests__/health-center-copy.test.ts +++ b/apps/desktop/src/main/__tests__/health-center-copy.test.ts @@ -20,7 +20,7 @@ import assert from 'node:assert/strict'; import { test } from 'node:test'; import { CAPABILITY_REASON_CODES } from '@maka/core/capabilities'; -import { UI_LOCALES, type UiCatalog } from '@maka/core/ui-locale'; +import { UI_LOCALES, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { connectionLastTestMessageDisplay } from '../../renderer/features/connection-settings/index.js'; import { getHealthCenterCopy } from '../../renderer/locales/settings-health-copy.js'; import { getCapabilityReasonCopy } from '../../renderer/locales/capability-reason-copy.js'; @@ -100,11 +100,11 @@ test('renders runtime probe details from structured params, not string parsing', }); test('capability reason codes have copy in every locale, unknown reasons keep the fallback', () => { - const fallback = { - 'zh-CN': '状态详情请见对应设置页。', - 'zh-TW': '狀態詳細資料請參閱對應的設定頁。', - en: 'See the corresponding settings page for details.', - } satisfies UiCatalog; + const fallback = resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': '状态详情请见对应设置页。', + 'zh-TW': '狀態詳細資料請參閱對應的設定頁。', + en: 'See the corresponding settings page for details.', + })); assert.equal(getCapabilityReasonCopy('zh-CN').platform_credentials_missing, '未配置平台凭据'); for (const locale of UI_LOCALES) { const reasons = getCapabilityReasonCopy(locale); diff --git a/apps/desktop/src/main/__tests__/settings-memory-copy.test.ts b/apps/desktop/src/main/__tests__/settings-memory-copy.test.ts index d18566f5939..dc6ae816670 100644 --- a/apps/desktop/src/main/__tests__/settings-memory-copy.test.ts +++ b/apps/desktop/src/main/__tests__/settings-memory-copy.test.ts @@ -19,7 +19,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { UI_LOCALES, type UiCatalog } from '@maka/core/ui-locale'; +import { UI_LOCALES, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { getMemorySettingsCopy, memoryResultMessage, @@ -51,7 +51,14 @@ test('falls back for an unknown Runtime Host memory rejection code', () => { } }); -const formattedCopy = { +const formattedCopy = resolveUiMessageCatalog(defineUiMessageCatalog<{ + counts: string[][]; + summaries: string[][]; + redacted: string[]; + backupFailures: string[]; + preview: string[]; + labels: string[]; +}>()({ 'zh-CN': { counts: [ ['0 条生效', '草稿 0 条生效', '0 条已归档', '草稿 0 条已归档', '0 条记忆'], @@ -145,14 +152,7 @@ const formattedCopy = { 'The current MEMORY.md will be backed up before the selected backup replaces it. Restore: Backup #2', ], }, -} satisfies UiCatalog<{ - counts: string[][]; - summaries: string[][]; - redacted: string[]; - backupFailures: string[]; - preview: string[]; - labels: string[]; -}>; +})); for (const locale of UI_LOCALES) { const expected = formattedCopy[locale]; diff --git a/apps/desktop/src/main/browser-message-box.ts b/apps/desktop/src/main/browser-message-box.ts index d3f1af108bd..a91313ca69c 100644 --- a/apps/desktop/src/main/browser-message-box.ts +++ b/apps/desktop/src/main/browser-message-box.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog } from '@maka/core/ui-locale'; +import { defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { randomUUID } from 'node:crypto'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; @@ -649,4 +649,4 @@ function escapeHtml(value: string): string { }); } -const CLOSE_LABEL = { 'zh-CN': '关闭', 'zh-TW': '關閉', en: 'Close' } satisfies UiCatalog; +const CLOSE_LABEL = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': '关闭', 'zh-TW': '關閉', en: 'Close' })); diff --git a/apps/desktop/src/main/client-settings-confirmation-copy.ts b/apps/desktop/src/main/client-settings-confirmation-copy.ts index 6168a8776f6..42cf8540864 100644 --- a/apps/desktop/src/main/client-settings-confirmation-copy.ts +++ b/apps/desktop/src/main/client-settings-confirmation-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ClientSettingsChange } from './client-settings-tools.js'; type ConfirmationCopy = { @@ -28,7 +28,7 @@ type ConfirmationCopy = { buttons: [string, string]; }; -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { labels: { theme: '主题', palette: '配色', uiLocale: '界面语言', runComplete: '系统通知', keepSystemAwake: '保持系统唤醒' }, on: '开启', @@ -50,7 +50,7 @@ const COPY = { message: "Allow Maka to update this client's settings?", buttons: ['Apply changes', 'Cancel'], }, -} satisfies UiCatalog; +})); export function clientSettingsConfirmation( changes: readonly ClientSettingsChange[], diff --git a/apps/desktop/src/main/computer-use/status-item.ts b/apps/desktop/src/main/computer-use/status-item.ts index 1dba08cb108..85e90b563f4 100644 --- a/apps/desktop/src/main/computer-use/status-item.ts +++ b/apps/desktop/src/main/computer-use/status-item.ts @@ -20,7 +20,7 @@ import { createRequire } from 'node:module'; import { join } from 'node:path'; import type { Menu as MenuType, Tray } from 'electron'; -import { resolveSystemUiLocale, type UiCatalog, type UiLocale } from '@maka/core/ui-locale'; +import { resolveSystemUiLocale, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { CuOverlayHook } from '@maka/runtime/computer-use-types'; // Electron is CommonJS, and named imports from it fail outside a main process. @@ -130,24 +130,23 @@ interface StatusItemCopy { /** Codex's empty state. Reachable only if a menu outlives its rows. */ empty: string; } - -const COPY: UiCatalog = { - 'zh-CN': { +const copyZhCn: StatusItemCopy = { stopUsing: (appName) => `停止操作 ${appName}`, stopUnnamed: '停止 Computer Use', empty: '没有正在进行的任务', - }, - 'zh-TW': { + }; +const copyZhTw: StatusItemCopy = { stopUsing: (appName) => `停止操作 ${appName}`, stopUnnamed: '停止 Computer Use', empty: '沒有正在進行的任務', - }, - en: { + }; +const copyEn: StatusItemCopy = { stopUsing: (appName) => `Stop Using ${appName}`, stopUnnamed: 'Stop Computer Use', empty: 'No Active Sessions', - }, -}; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); function defaultResolveLocale(): UiLocale { // Lazy on purpose: tool assembly runs at module load, before diff --git a/apps/desktop/src/main/main.ts b/apps/desktop/src/main/main.ts index 3384fa327c3..34e8ee7ade5 100644 --- a/apps/desktop/src/main/main.ts +++ b/apps/desktop/src/main/main.ts @@ -17,7 +17,7 @@ * under the License. */ -import { resolveSystemUiLocale, type UiCatalog } from '@maka/core/ui-locale'; +import { resolveSystemUiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { DEV_LOSER_EXIT_CODE, developmentLaunchResultFile, @@ -263,7 +263,12 @@ if (!app.requestSingleInstanceLock()) { }); } -const DEV_SINGLETON_COPY = { +const DEV_SINGLETON_COPY = resolveUiMessageCatalog(defineUiMessageCatalog<{ + title: string; + message: string; + detail(profilePath: string): string; + exit: string; +}>()({ 'zh-CN': { title: 'Maka Dev 已在运行', message: '另一个 Maka Dev 实例正在使用此开发配置。', @@ -282,9 +287,4 @@ const DEV_SINGLETON_COPY = { detail: (profilePath: string) => `Development profile: ${profilePath}\n\nQuit the running instance, then retry.`, exit: 'Exit', }, -} satisfies UiCatalog<{ - title: string; - message: string; - detail(profilePath: string): string; - exit: string; -}>; +})); diff --git a/apps/desktop/src/main/native-diagnostic-dialog-copy.ts b/apps/desktop/src/main/native-diagnostic-dialog-copy.ts index 522033559cc..9dba28455f3 100644 --- a/apps/desktop/src/main/native-diagnostic-dialog-copy.ts +++ b/apps/desktop/src/main/native-diagnostic-dialog-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; interface NativeDiagnosticDialogCopy { readonly dialog: { @@ -55,9 +55,7 @@ interface NativeDiagnosticDialogCopy { readonly exit: string; }; } - -const COPY = { - en: { +const copyEn: NativeDiagnosticDialogCopy = { dialog: { copy: 'Copy Diagnostics', copyAgain: 'Copy Again', @@ -95,8 +93,8 @@ const COPY = { repair: 'Repair Workspace', exit: 'Exit', }, - }, - 'zh-CN': { + }; +const copyZhCn: NativeDiagnosticDialogCopy = { dialog: { copy: '复制诊断信息', copyAgain: '再次复制', @@ -133,8 +131,8 @@ const COPY = { repair: '修复工作区', exit: '退出', }, - }, - 'zh-TW': { + }; +const copyZhTw: NativeDiagnosticDialogCopy = { dialog: { copy: '複製診斷資訊', copyAgain: '再次複製', @@ -171,8 +169,9 @@ const COPY = { repair: '修復工作區', exit: '退出', }, - }, -} satisfies UiCatalog; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: copyEn, 'zh-CN': copyZhCn, 'zh-TW': copyZhTw })); export function getNativeDiagnosticDialogCopy(locale: UiLocale): NativeDiagnosticDialogCopy { return COPY[locale]; diff --git a/apps/desktop/src/main/native-file-dialog-copy.ts b/apps/desktop/src/main/native-file-dialog-copy.ts index 3ab00ff9b3d..3939f68f26c 100644 --- a/apps/desktop/src/main/native-file-dialog-copy.ts +++ b/apps/desktop/src/main/native-file-dialog-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Copy for the native open/save panels. Format names (`Markdown`, @@ -35,7 +35,7 @@ interface NativeFileDialogCopy { readonly allFiles: string; } -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { referenceFolder: '引用文件夹', addAttachments: '添加附件', @@ -63,7 +63,7 @@ const COPY = { saveConversation: 'Save conversation', allFiles: 'All Files', }, -} satisfies UiCatalog; +})); export function nativeFileDialogCopy(locale: UiLocale): NativeFileDialogCopy { return COPY[locale]; diff --git a/apps/desktop/src/main/notifications-policy.ts b/apps/desktop/src/main/notifications-policy.ts index 6cdc22586bd..26785b9dcbd 100644 --- a/apps/desktop/src/main/notifications-policy.ts +++ b/apps/desktop/src/main/notifications-policy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { SessionAttention } from '@maka/runtime-host/protocol'; export interface RunNotificationGate { @@ -36,7 +36,7 @@ export interface RunNotificationCopy { readonly body: string; } -const RUN_NOTIFICATION_COPY = { +const RUN_NOTIFICATION_COPY = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': { errored: { title: '任务出错', body: '本轮回答未能完成,点击查看详情。' }, completed: { title: '回答已生成', body: 'Maka 已完成本轮回答,点击查看。' }, @@ -52,7 +52,7 @@ const RUN_NOTIFICATION_COPY = { completed: { title: 'Response ready', body: 'Maka finished this response. Click to view it.' }, waiting: { title: 'Waiting for you', body: 'Maka needs your answer to continue. Click to view it.' }, }, -} satisfies UiCatalog>; +})); export interface RunNotificationEvent extends SessionAttention { readonly title?: string; diff --git a/apps/desktop/src/main/permission-overlay/permission-overlay-copy.ts b/apps/desktop/src/main/permission-overlay/permission-overlay-copy.ts index 5311ff04693..b3ca292ac66 100644 --- a/apps/desktop/src/main/permission-overlay/permission-overlay-copy.ts +++ b/apps/desktop/src/main/permission-overlay/permission-overlay-copy.ts @@ -25,7 +25,7 @@ * resolves the locale and ships the finished strings in the show payload. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { DragGrantPermissionId } from './permission-overlay-controller.js'; export interface PermissionOverlayCopy { @@ -46,11 +46,7 @@ export interface PermissionOverlayCopy { /** Shown instead of the drag affordance when there is no .app to drag. */ noBundle: string; } - -type Catalog = UiCatalog>; - -const COPY: Catalog = { - 'zh-CN': { +const copyZhCn: Record = { accessibility: { headline: (appName) => `把 ${appName} 拖到上面的列表里,即可开启「辅助功能」`, fallback: '也可以在系统设置里点 + 号,从「应用程序」中选择本 App。', @@ -68,8 +64,8 @@ const COPY: Catalog = { restartHint: '若仍显示未授权,需要重启 App —— macOS 会缓存上一次的拒绝结果。', noBundle: '当前不是以 .app 方式运行,无法拖拽。请在系统设置里手动添加。', }, - }, - 'zh-TW': { + }; +const copyZhTw: Record = { accessibility: { headline: (appName) => `把 ${appName} 拖到上面的列表裡,即可開啟「輔助功能」`, fallback: '也可以在系統設定裡點 + 號,從「應用程式」中選擇本 App。', @@ -87,8 +83,8 @@ const COPY: Catalog = { restartHint: '若仍顯示未授權,需要重啟 App —— macOS 會快取上一次的拒絕結果。', noBundle: '目前不是以 .app 方式執行,無法拖拽。請在系統設定裡手動新增。', }, - }, - en: { + }; +const copyEn: Record = { accessibility: { headline: (appName) => `Drag ${appName} into the list above to allow Accessibility`, fallback: 'Or click + in System Settings and pick the app from Applications.', @@ -106,8 +102,9 @@ const COPY: Catalog = { restartHint: 'If it still reads as denied, restart the app — macOS caches the previous denial.', noBundle: 'Not running from a .app bundle, so there is nothing to drag. Add it manually in System Settings.', }, - }, -}; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); export function getPermissionOverlayCopy( locale: UiLocale, diff --git a/apps/desktop/src/main/project-picker-copy.ts b/apps/desktop/src/main/project-picker-copy.ts index 1ce70aa251f..0f774dcc55a 100644 --- a/apps/desktop/src/main/project-picker-copy.ts +++ b/apps/desktop/src/main/project-picker-copy.ts @@ -17,9 +17,9 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; -const TITLE = { 'zh-CN': '添加项目', 'zh-TW': '新增專案', en: 'Add project' } satisfies UiCatalog; +const TITLE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': '添加项目', 'zh-TW': '新增專案', en: 'Add project' })); export function projectPickerTitle(locale: UiLocale): string { return TITLE[locale]; diff --git a/apps/desktop/src/main/runtime-host-artifacts-ipc-main.ts b/apps/desktop/src/main/runtime-host-artifacts-ipc-main.ts index 2d7b3b0911f..b50701c0d35 100644 --- a/apps/desktop/src/main/runtime-host-artifacts-ipc-main.ts +++ b/apps/desktop/src/main/runtime-host-artifacts-ipc-main.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { randomUUID } from "node:crypto"; import { open, mkdir, rename, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; @@ -320,8 +320,8 @@ async function syncDirectory(path: string): Promise { } } -const ARTIFACT_DIALOG_COPY = { +const ARTIFACT_DIALOG_COPY = resolveUiMessageCatalog(defineUiMessageCatalog<{ saveAs(name: string): string }>()({ 'zh-CN': { saveAs: (name: string) => `另存为 ${name}` }, 'zh-TW': { saveAs: (name: string) => `另存為 ${name}` }, en: { saveAs: (name: string) => `Save ${name} as` }, -} satisfies UiCatalog<{ saveAs(name: string): string }>; +})); diff --git a/apps/desktop/src/main/runtime-host-config-ipc-main.ts b/apps/desktop/src/main/runtime-host-config-ipc-main.ts index 4f6f39160f9..56b64962a27 100644 --- a/apps/desktop/src/main/runtime-host-config-ipc-main.ts +++ b/apps/desktop/src/main/runtime-host-config-ipc-main.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { readFile, writeFile } from 'node:fs/promises'; import { isDeepStrictEqual } from 'node:util'; import type { IpcMain } from 'electron'; @@ -662,8 +662,8 @@ function sanitizeStrategy(value: unknown): ConnectionConflictStrategy { return value === 'overwrite' ? 'overwrite' : 'skip'; } -const CONFIG_DIALOG_COPY = { +const CONFIG_DIALOG_COPY = resolveUiMessageCatalog(defineUiMessageCatalog<{ exportTitle: string; importTitle: string }>()({ 'zh-CN': { exportTitle: '导出 Maka 配置', importTitle: '导入 Maka 配置' }, 'zh-TW': { exportTitle: '匯出 Maka 設定', importTitle: '匯入 Maka 設定' }, en: { exportTitle: 'Export Maka configuration', importTitle: 'Import Maka configuration' }, -} satisfies UiCatalog<{ exportTitle: string; importTitle: string }>; +})); diff --git a/apps/desktop/src/main/runtime-host-quit-copy.ts b/apps/desktop/src/main/runtime-host-quit-copy.ts index 4fd7bb58393..afb11499110 100644 --- a/apps/desktop/src/main/runtime-host-quit-copy.ts +++ b/apps/desktop/src/main/runtime-host-quit-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { MessageBoxOptions } from 'electron'; export interface RuntimeHostQuitDialog { @@ -54,7 +54,7 @@ interface RuntimeHostQuitCopy { readonly keepRunning: string; } -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: { activeTitle: 'Maka is still working', activeMessage: 'Background work is still running.', @@ -79,4 +79,4 @@ const COPY = { stopAndQuit: '停止工作並結束', keepRunning: '繼續執行 Maka', }, -} satisfies UiCatalog; +})); diff --git a/apps/desktop/src/main/runtime-host-wsl-handoff.ts b/apps/desktop/src/main/runtime-host-wsl-handoff.ts index 67cc4034b4d..28df91e9ccd 100644 --- a/apps/desktop/src/main/runtime-host-wsl-handoff.ts +++ b/apps/desktop/src/main/runtime-host-wsl-handoff.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { EnvironmentRuntimeHostProfile, HostHandoffBlocker, HostHandoffPhase, RuntimeHostRemoteCompatibilityError } from '@maka/runtime-host/client'; import { compareProductReleaseVersions } from '@maka/runtime-host/operator'; import type { DesktopRuntimeHostManagedServiceBinding } from './runtime-host-managed-services.js'; @@ -118,7 +118,7 @@ interface WslHandoffGuidance { readonly development: string; } -const GUIDANCE = { +const GUIDANCE = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: { client: 'Update Desktop to a build compatible with this Host. The Host will not be downgraded.', binding: 'The WSL management binding is unavailable. Restore it through Runtime Host settings before updating.', @@ -140,4 +140,4 @@ const GUIDANCE = { target: '目前 Desktop 無法驗證合適的替換套件。請更新 Desktop,或透過開發環境明確管理選定的開發套件;現有 Host 會被保留。', development: '選定的開發構建', }, -} satisfies UiCatalog; +})); diff --git a/apps/desktop/src/main/settings-recovery.ts b/apps/desktop/src/main/settings-recovery.ts index 8f2a151a770..4c8b2523d27 100644 --- a/apps/desktop/src/main/settings-recovery.ts +++ b/apps/desktop/src/main/settings-recovery.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { CorruptSettingsRecovery } from '@maka/storage/settings-store'; interface RecoveryCopy { @@ -28,7 +28,9 @@ interface RecoveryCopy { acknowledge: string; } -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog & { + review: string; settings: string; backup: string; acknowledge: string; +}>()({ 'zh-CN': { recovered: { title: '设置已恢复为默认值', @@ -71,9 +73,7 @@ const COPY = { backup: 'Original file backup: ', acknowledge: 'OK', }, -} satisfies UiCatalog & { - review: string; settings: string; backup: string; acknowledge: string; -}>; +})); export function settingsRecoveryCopy( recovery: CorruptSettingsRecovery, diff --git a/apps/desktop/src/main/windows-app-tray.ts b/apps/desktop/src/main/windows-app-tray.ts index 66762db5698..d675a56516a 100644 --- a/apps/desktop/src/main/windows-app-tray.ts +++ b/apps/desktop/src/main/windows-app-tray.ts @@ -18,7 +18,7 @@ */ import type { Menu, MenuItemConstructorOptions, Tray } from 'electron'; -import type { UiCatalog } from '@maka/core/ui-locale'; +import { defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { DesktopLocaleAuthority } from './desktop-locale-authority.js'; type TraySurface = Pick; @@ -40,11 +40,11 @@ interface WindowsAppTrayCopy { readonly quit: string; } -const copy = { +const copy = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: { open: 'Open Maka', workHub: 'Open WorkHub', quit: 'Quit Maka' }, 'zh-CN': { open: '打开 Maka', workHub: '打开 WorkHub', quit: '退出 Maka' }, 'zh-TW': { open: '開啟 Maka', workHub: '開啟 WorkHub', quit: '結束 Maka' }, -} satisfies UiCatalog; +})); /** Windows needs a visible way back after the last product window closes. */ export function createWindowsAppTray(deps: WindowsAppTrayDeps) { diff --git a/apps/desktop/src/renderer/application/contracts/conversation-copy.ts b/apps/desktop/src/renderer/application/contracts/conversation-copy.ts index d3b1d2e8483..fc37dc3b1ea 100644 --- a/apps/desktop/src/renderer/application/contracts/conversation-copy.ts +++ b/apps/desktop/src/renderer/application/contracts/conversation-copy.ts @@ -24,7 +24,7 @@ import type { SessionSendProjection } from '@maka/core/session-send-projection'; import type { ModelCallKind } from '@maka/core/model-call-attempt'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface DesktopConversationCopy { actions: { @@ -326,9 +326,7 @@ function zhDetail(parts: readonly string[]): string { function enDetail(parts: readonly string[]): string { return parts.length > 0 ? `: ${parts.join(', ')}` : ''; } - -const COPY = { - 'zh-CN': { +const copyZhCn: DesktopConversationCopy = { actions: { stopFailedTitle: '停止失败', stopFailedFallback: '任务操作失败,请稍后重试。', refreshSessionsFailedTitle: '刷新任务列表失败', refreshSessionsFailedFallback: '刷新任务列表失败,请稍后重试。', conversationErrorTitle: '任务出错', conversationErrorFallback: '任务运行失败,请稍后重试。', branchCreatedTitle: '已创建分支', branchCreatedDescription: (name) => `新任务 ${name}`, revisionStartedTitle: '已创建修改版草稿', revisionStartedDescription: '原任务仍会保留;发送后将在新版本中继续', revisionReadyTitle: '可以修改并重发了', revisionReadyDescription: '已回到该消息之前;可直接重发或修改后发送', revisionUnavailableTitle: '暂时无法编辑这条消息', revisionAttachmentsUnsupported: '这条消息自带的附件不参与编辑并重发,请复制文字后新建消息。', revisionTransformedTextUnsupported: '通过显式技能发送的历史消息暂不支持编辑并重发,请复制文字后重新选择技能。', revisionDraftAttachmentConflict: 'Composer 中已有待发送附件,请先发送或移除附件,再编辑历史消息。', revisionCommandUnsupported: '修改消息时不能执行 /compact、/side 或编排命令,请取消修改后再试。', revisionAlreadyActive: '已有一条消息正在修改,请先发送或取消当前修改。', revisionCancelLabel: '取消', revisionBannerTitle: '正在修改已发送消息', revisionBannerDetail: '· 发送后创建新版本', operationFailedTitle: '操作失败', operationFailedFallback: '任务操作失败,请稍后重试。', attachmentFailedTitle: '添加附件失败', folderNotAttachable: '文件夹不能作为附件添加。', folderNotAttachableUseReference: '文件夹不能作为附件添加,请改用“引用文件夹”。', imageAttachmentNotDirectTitle: '图片已作为附件添加', imageAttachmentNotDirectDescription: '当前模型不会直接接收图片。图片已作为附件提供给模型。', tryAgain: '请稍后重试。', modelReboundTitle: '已切换到可用模型', modelReboundDescription: (modelId) => `原任务使用的连接已不可用${modelId ? ` · ${modelId}` : ''}`, messageReadFailedTitle: '读取任务失败', scrollMainToBottom: '滚动主对话到底部' }, model: { fakeBackendLabel: '本地模拟连接', @@ -570,8 +568,8 @@ const COPY = { testError: { label: '上次连接测试失败', tooltip: '最近一次连接测试因网络 / 超时 / 5xx 失败。这不会拦截发送,但若问题持续请到 设置 · 模型 检查 Base URL / 代理。' }, }, turnError: { streamTruncated: '响应中途断开。', requestRejected: '模型服务拒绝了请求,请检查模型与请求配置。', retryExhausted: '已达到自动重试次数上限。', retryDeclined: { side_effects: '本次已有工具活动,为避免重复操作,未自动重试。请先检查工具结果。', observable_output: '本次已有部分输出,未自动重试。请先检查已保留的内容。', policy: '按当前重试规则,本次未自动重试。', budget: '本次执行预算已用尽,未自动重试。' }, unknown: '出错了,暂时无法确定原因。', contextOverflow: '上下文超出模型窗口限制,减少附件或开启新任务。', timeout: '模型请求超时。', auth: '模型鉴权失败,请到设置里重新连接或登录。', providerBilling: '模型服务计费受限,请检查账号余额或订阅状态。', providerCapacity: '模型服务暂时满载。', rateLimit: '模型请求太频繁被限流了。', network: '网络连接失败,请检查网络。', provider: '模型服务返回错误。', stepCap: '达到工具调用步数上限,任务可能没做完。发消息让它继续。', tool: '工具调用失败,看一下上面的工具结果再决定要不要重试。', permission: '这一轮在等权限确认时结束了,重新发消息会再问一次。', restarted: '本地应用重启,上一轮没有完成', sandboxBoundaryClosed: '本地应用重启时,等待确认的「允许访问工作区以外的内容」请求已按拒绝关闭。重新发消息可以再决定一次。', executionState: { erroredTool: '这一轮有工具执行出错,先看它的结果,再决定要不要重发。', toolRan: '这一轮已经执行过工具,可能已经产生实际改动,重发前先看工具结果。' } }, - }, - 'zh-TW': { + }; +const copyZhTw: DesktopConversationCopy = { actions: { stopFailedTitle: '停止失敗', stopFailedFallback: '任務操作失敗,請稍後重試。', refreshSessionsFailedTitle: '重新整理任務列表失敗', refreshSessionsFailedFallback: '重新整理任務列表失敗,請稍後重試。', conversationErrorTitle: '任務出錯', conversationErrorFallback: '任務執行失敗,請稍後重試。', branchCreatedTitle: '已建立分支', branchCreatedDescription: (name) => `新任務 ${name}`, revisionStartedTitle: '已建立修改版草稿', revisionStartedDescription: '原任務仍會保留;傳送後將在新版本中繼續', revisionReadyTitle: '可以修改並重發了', revisionReadyDescription: '已回到該訊息之前;可直接重發或修改後傳送', revisionUnavailableTitle: '暫時無法編輯這條訊息', revisionAttachmentsUnsupported: '這條訊息自帶的附件不參與編輯並重發,請複製文字後建立訊息。', revisionTransformedTextUnsupported: '透過顯式技能傳送的歷史訊息暫不支援編輯並重發,請複製文字後重新選擇技能。', revisionDraftAttachmentConflict: 'Composer 中已有待發送附件,請先發送或移除附件,再編輯歷史訊息。', revisionCommandUnsupported: '修改訊息時不能執行 /compact、/side 或編排命令,請取消修改後再試。', revisionAlreadyActive: '已有一條訊息正在修改,請先發送或取消目前修改。', revisionCancelLabel: '取消', revisionBannerTitle: '正在修改已傳送訊息', revisionBannerDetail: '· 傳送後建立新版本', operationFailedTitle: '操作失敗', operationFailedFallback: '任務操作失敗,請稍後重試。', attachmentFailedTitle: '新增附件失敗', folderNotAttachable: '資料夾不能作為附件新增。', folderNotAttachableUseReference: '資料夾不能作為附件新增,請改用「引用資料夾」。', imageAttachmentNotDirectTitle: '圖片已作為附件新增', imageAttachmentNotDirectDescription: '目前模型不會直接接收圖片。圖片已作為附件提供給模型。', tryAgain: '請稍後重試。', modelReboundTitle: '已切換到可用模型', modelReboundDescription: (modelId) => `原任務使用的連線已不可用${modelId ? ` · ${modelId}` : ''}`, messageReadFailedTitle: '讀取任務失敗', scrollMainToBottom: '滾動主對話到底部' }, model: { fakeBackendLabel: '本地模擬連線', @@ -804,8 +802,8 @@ const COPY = { testError: { label: '上次連線測試失敗', tooltip: '最近一次連線測試因網路 / 超時 / 5xx 失敗。這不會攔截發送,但若問題持續請到 設定 · 模型 檢查 Base URL / 代理。' }, }, turnError: { streamTruncated: '回應中途斷開。', requestRejected: '模型服務拒絕了請求,請檢查模型與請求設定。', retryExhausted: '已達到自動重試次數上限。', retryDeclined: { side_effects: '本次已有工具活動,為避免重複操作,未自動重試。請先檢查工具結果。', observable_output: '本次已有部分輸出,未自動重試。請先檢查已保留的內容。', policy: '依目前重試規則,本次未自動重試。', budget: '本次執行預算已用盡,未自動重試。' }, unknown: '出錯了,暫時無法確定原因。', contextOverflow: '上下文超出模型視窗限制,減少附件或開啟新任務。', timeout: '模型請求逾時。', auth: '模型鑑權失敗,請到設定裡重新連線或登入。', providerBilling: '模型服務計費受限,請檢查帳號餘額或訂閱狀態。', providerCapacity: '模型服務暫時滿載。', rateLimit: '模型請求太頻繁而受到速率限制。', network: '網路連線失敗,請檢查網路。', provider: '模型服務回傳錯誤。', stepCap: '達到工具呼叫步數上限,任務可能尚未完成。傳送訊息讓它繼續。', tool: '工具呼叫失敗,先看上面的工具結果再決定是否重試。', permission: '這一輪在等待權限確認時結束,重新傳送訊息會再詢問一次。', restarted: '本機應用程式重啟,上一輪沒有完成', sandboxBoundaryClosed: '本機應用程式重啟時,等待確認的「允許存取工作區以外的內容」請求已按拒絕關閉。重新傳送訊息可以再次決定。', executionState: { erroredTool: '這一輪有工具執行出錯,先看它的結果,再決定是否重發。', toolRan: '這一輪已經執行過工具,可能已經產生實際變更,重發前先看工具結果。' } }, - }, - en: { + }; +const copyEn: DesktopConversationCopy = { actions: { stopFailedTitle: 'Failed to stop', stopFailedFallback: 'The task action failed. Try again later.', refreshSessionsFailedTitle: 'Failed to refresh tasks', refreshSessionsFailedFallback: 'The task list could not be refreshed. Try again later.', conversationErrorTitle: 'Task error', conversationErrorFallback: 'The task run failed. Try again later.', branchCreatedTitle: 'Branch created', branchCreatedDescription: (name) => `New task: ${name}`, revisionStartedTitle: 'Edit draft ready', revisionStartedDescription: 'The original task is kept; sending creates a new version', revisionReadyTitle: 'Ready to edit and resend', revisionReadyDescription: 'Rewound to before that message; resend as is or edit first', revisionUnavailableTitle: 'This message cannot be edited yet', revisionAttachmentsUnsupported: "A message's own attachments are not rewritten by edit & resend. Copy the text into a new message instead.", revisionTransformedTextUnsupported: 'Edit & resend does not yet support messages sent with an explicit skill. Copy the text and select the skill again instead.', revisionDraftAttachmentConflict: 'The composer already has pending attachments. Send or remove them before editing a sent message.', revisionCommandUnsupported: 'You cannot run /compact, /side, or orchestration commands while editing a sent message. Cancel the edit first.', revisionAlreadyActive: 'Another message is already being edited. Send or cancel that edit first.', revisionCancelLabel: 'Cancel', revisionBannerTitle: 'Editing sent message', revisionBannerDetail: '· New version on send', operationFailedTitle: 'Action failed', operationFailedFallback: 'The task action failed. Try again later.', attachmentFailedTitle: 'Failed to add attachment', folderNotAttachable: 'Folders cannot be added as attachments.', folderNotAttachableUseReference: 'Folders cannot be added as attachments. Use Reference folder instead.', imageAttachmentNotDirectTitle: 'Image added as an attachment', imageAttachmentNotDirectDescription: 'The current model does not receive images directly. The image has been provided as an attachment.', tryAgain: 'Try again later.', modelReboundTitle: 'Switched to an available model', modelReboundDescription: (modelId) => `The previous connection is unavailable${modelId ? ` · ${modelId}` : ''}`, messageReadFailedTitle: 'Failed to load task', scrollMainToBottom: 'Scroll main conversation to bottom' }, model: { fakeBackendLabel: 'Local simulation', @@ -1054,8 +1052,9 @@ const COPY = { testError: { label: 'Last connection test failed', tooltip: 'The latest test failed because of a network, timeout, or 5xx error. Sending is not blocked; check Base URL or proxy settings if it persists.' }, }, turnError: { streamTruncated: 'The response stream ended before completion.', requestRejected: 'The model service rejected the request. Check the model and request configuration.', retryExhausted: 'The automatic retry limit was reached.', retryDeclined: { side_effects: 'Tool activity already occurred in this attempt. Automatic retry was declined to avoid repeating operations. Check the tool results first.', observable_output: 'This attempt already produced output, so it was not retried automatically. Check the retained content first.', policy: 'This attempt was not retried under the current retry policy.', budget: 'The execution budget was exhausted, so this attempt was not retried automatically.' }, unknown: 'Something went wrong; the cause is unknown.', contextOverflow: 'Context exceeded the model window. Reduce attachments or start a new task.', timeout: 'The model request timed out.', auth: 'Model authentication failed. Reconnect or sign in again from Settings.', providerBilling: 'Model billing is restricted. Check the account balance or subscription.', providerCapacity: 'The model service is temporarily at capacity.', rateLimit: 'Requests were rate-limited.', network: 'The network connection failed. Check the network.', provider: 'The model service returned an error.', stepCap: 'The tool-step limit was reached, so the task may be incomplete. Send a message to continue.', tool: 'A tool call failed. Check the tool result above before deciding whether to retry.', permission: 'This turn ended while waiting for permission. Send a message and it will ask again.', restarted: 'The app restarted before the previous turn completed', sandboxBoundaryClosed: 'The app restarted, so the pending request to reach outside the workspace was closed as denied. Send a message to decide again.', executionState: { erroredTool: 'A tool errored during this turn. Read its result before deciding whether to send another message.', toolRan: 'Tools already ran during this turn and may have made real changes. Read their results before sending another message.' } }, - }, -} satisfies UiCatalog; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); export function getDesktopConversationCopy(locale: UiLocale): DesktopConversationCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/application/contracts/transcript-copy.ts b/apps/desktop/src/renderer/application/contracts/transcript-copy.ts index d748cfa263a..86cfdd6960e 100644 --- a/apps/desktop/src/renderer/application/contracts/transcript-copy.ts +++ b/apps/desktop/src/renderer/application/contracts/transcript-copy.ts @@ -17,14 +17,14 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { classifiedErrorFallback } from './operation-diagnostics.js'; import { getDesktopConversationCopy } from './conversation-copy.js'; -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog<{ read: string; refresh: string; refreshTitle: string }>()({ 'zh-CN': { read: '任务内容暂时无法读取,请稍后重试。', refresh: '任务内容暂时无法刷新,请稍后重试。', refreshTitle: '刷新任务失败' }, 'zh-TW': { read: '任務內容暫時無法讀取,請稍後重試。', refresh: '任務內容暫時無法重新整理,請稍後重試。', refreshTitle: '重新整理任務失敗' }, en: { read: 'Task content is temporarily unavailable. Try again later.', refresh: 'Task content could not be refreshed. Try again later.', refreshTitle: 'Could not refresh task' }, -} satisfies UiCatalog<{ read: string; refresh: string; refreshTitle: string }>; +})); export function transcriptErrorMessage(error: unknown, locale: UiLocale, kind: 'read' | 'refresh' | 'restore'): string { if (kind === 'restore') { return classifiedErrorFallback(error, getDesktopConversationCopy(locale).actions.operationFailedFallback, locale, 'desktop'); diff --git a/apps/desktop/src/renderer/features/app-update/locales/app-update-copy.ts b/apps/desktop/src/renderer/features/app-update/locales/app-update-copy.ts index 695ab79621e..52f7fe2e8c2 100644 --- a/apps/desktop/src/renderer/features/app-update/locales/app-update-copy.ts +++ b/apps/desktop/src/renderer/features/app-update/locales/app-update-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface AppUpdateCopy { readonly installFailedTitle: string; @@ -31,7 +31,7 @@ export interface AppUpdateCopy { readonly retryFailedFallback: string; } -const COPY_BY_LOCALE = { +const COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { installFailedTitle: '无法安装更新', installFailedFallback: '请稍后重试。', @@ -65,7 +65,7 @@ const COPY_BY_LOCALE = { retryFailedTitle: 'Could not retry update download', retryFailedFallback: 'Try again later, or download the latest version manually.', }, -} satisfies UiCatalog; +})); export function getAppUpdateCopy(locale: UiLocale): AppUpdateCopy { return COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/features/connection-settings/settings-provider-copy.ts b/apps/desktop/src/renderer/features/connection-settings/settings-provider-copy.ts index dc361dc2682..1e78077def2 100644 --- a/apps/desktop/src/renderer/features/connection-settings/settings-provider-copy.ts +++ b/apps/desktop/src/renderer/features/connection-settings/settings-provider-copy.ts @@ -19,7 +19,7 @@ import { generalizedErrorMessageForLocale, redactSecrets } from '@maka/core/redaction'; import type { SubscriptionActionCode, SubscriptionActionFailureReason } from '@maka/core/oauth-subscription'; -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type SubscriptionResultCode = | SubscriptionActionCode @@ -773,11 +773,11 @@ const enCopy: ProviderSettingsCopy = { }, }; -const PROVIDER_SETTINGS_COPY = { +const PROVIDER_SETTINGS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': zhCopy, 'zh-TW': zhTwCopy, en: enCopy, -} satisfies UiCatalog; +})); export function getProviderSettingsCopy(locale: UiLocale): ProviderSettingsCopy { return PROVIDER_SETTINGS_COPY[locale]; diff --git a/apps/desktop/src/renderer/features/conversation/model/executor-submission.ts b/apps/desktop/src/renderer/features/conversation/model/executor-submission.ts index ce9c474e1ab..f1ac16c770d 100644 --- a/apps/desktop/src/renderer/features/conversation/model/executor-submission.ts +++ b/apps/desktop/src/renderer/features/conversation/model/executor-submission.ts @@ -22,7 +22,7 @@ import type { ChatDefaultPermissionMode } from '@maka/core/settings'; import type { CollaborationMode } from '@maka/core/collaboration'; import type { OrchestrationMode } from '@maka/core/orchestration'; import type { ThinkingLevel } from '@maka/core/model-thinking'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { NewChatModel } from './shell-chat-model-selection.js'; export interface ExecutorSubmission { @@ -30,7 +30,7 @@ export interface ExecutorSubmission { executorEntry?: Pick; } -const SUBMISSION_COPY = { +const SUBMISSION_COPY = resolveUiMessageCatalog(defineUiMessageCatalog<{ attachments: string; unavailable: string }>()({ en: { attachments: 'Remove unsupported attachments or select Maka. Your draft is preserved.', unavailable: 'Check External Agents settings or start a new task.', @@ -43,7 +43,7 @@ const SUBMISSION_COPY = { attachments: '請移除不支援的附件或選擇 Maka,草稿會保留。', unavailable: '請檢查外部 Agent 設定,或建立新任務。', }, -} satisfies UiCatalog<{ attachments: string; unavailable: string }>; +})); export function executorSubmissionError( input: ExecutorSubmission, diff --git a/apps/desktop/src/renderer/features/runtime-host-management/locales/runtime-host-handoff-copy.ts b/apps/desktop/src/renderer/features/runtime-host-management/locales/runtime-host-handoff-copy.ts index 2ec1f706ef2..21a5d2c2093 100644 --- a/apps/desktop/src/renderer/features/runtime-host-management/locales/runtime-host-handoff-copy.ts +++ b/apps/desktop/src/renderer/features/runtime-host-management/locales/runtime-host-handoff-copy.ts @@ -17,14 +17,14 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; interface RuntimeHostHandoffCopy { readonly copyDiagnostics: string; readonly diagnosticsCopied: string; } -const COPY_BY_LOCALE: UiCatalog = { +const COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { copyDiagnostics: '复制诊断信息', diagnosticsCopied: '诊断信息已复制', @@ -37,7 +37,7 @@ const COPY_BY_LOCALE: UiCatalog = { copyDiagnostics: 'Copy diagnostics', diagnosticsCopied: 'Diagnostics copied', }, -}; +})); export function getRuntimeHostHandoffCopy(locale: UiLocale): RuntimeHostHandoffCopy { return COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/features/workhub/locales/workhub-live-copy.ts b/apps/desktop/src/renderer/features/workhub/locales/workhub-live-copy.ts index 449975cb968..1fc69385400 100644 --- a/apps/desktop/src/renderer/features/workhub/locales/workhub-live-copy.ts +++ b/apps/desktop/src/renderer/features/workhub/locales/workhub-live-copy.ts @@ -17,8 +17,8 @@ * under the License. */ -import type { UiCatalog } from '@maka/core/ui-locale'; -export const workHubLiveCopy = { +import { defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +export const workHubLiveCopy = resolveUiMessageCatalog(defineUiMessageCatalog>()({ en: { filterConversation: 'Filter conversation by Work', clearConversationFilter: 'Show all conversations', noWorkConversation: 'No conversations for this Work in this part of history.', work: 'Work', attachmentLimit: 'Attachment count or size exceeds the limit', attachmentUploadFailed: 'Attachment upload did not return a reference', reviewAttachments: 'Please review the attachments.', sendFailed: 'Could not send', retrySteering: 'Retry the original text and attachments with Cmd/Ctrl+Enter to resolve the previous submission first.', retryFollowup: 'Retry the original text and attachments with Enter to resolve the previous submission first.', @@ -91,4 +91,4 @@ export const workHubLiveCopy = { restore: '收回工作台', openWork: '開啟任務', openResult: '開啟結果', }, -} satisfies UiCatalog>; +})); diff --git a/apps/desktop/src/renderer/locales/agent-graph-copy.ts b/apps/desktop/src/renderer/locales/agent-graph-copy.ts index 3111132a63e..aca3b386b0c 100644 --- a/apps/desktop/src/renderer/locales/agent-graph-copy.ts +++ b/apps/desktop/src/renderer/locales/agent-graph-copy.ts @@ -21,7 +21,7 @@ import type { AgentGraphClientOperator, AgentGraphClientSnapshot, } from '@maka/runtime/stream-graph-read-model'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface AgentGraphPanelCopy { title: string; @@ -51,9 +51,7 @@ export interface AgentGraphPanelCopy { operatorStatus(status: AgentGraphClientOperator['status']): string; wait(operator: AgentGraphClientOperator): string | undefined; } - -const AGENT_GRAPH_PANEL_COPY = { - 'zh-CN': { +const agentGraphPanelCopyZhCn: AgentGraphPanelCopy = { title: 'Agent Graph', loading: '正在读取 Graph 状态…', retry: '重试', @@ -101,8 +99,8 @@ const AGENT_GRAPH_PANEL_COPY = { cancelled: '取消', })[status], wait: waitReasonZh, - }, - 'zh-TW': { + }; +const agentGraphPanelCopyZhTw: AgentGraphPanelCopy = { title: 'Agent Graph', loading: '正在讀取 Graph 狀態…', retry: '重試', @@ -150,8 +148,8 @@ const AGENT_GRAPH_PANEL_COPY = { cancelled: '已取消', })[status], wait: waitReasonZhTw, - }, - en: { + }; +const agentGraphPanelCopyEn: AgentGraphPanelCopy = { title: 'Agent Graph', loading: 'Loading graph state…', retry: 'Retry', @@ -199,8 +197,9 @@ const AGENT_GRAPH_PANEL_COPY = { cancelled: 'Cancelled', })[status], wait: waitReasonEn, - }, -} satisfies UiCatalog; + }; + +const AGENT_GRAPH_PANEL_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': agentGraphPanelCopyZhCn, 'zh-TW': agentGraphPanelCopyZhTw, en: agentGraphPanelCopyEn })); export function getAgentGraphPanelCopy(locale: UiLocale): AgentGraphPanelCopy { return AGENT_GRAPH_PANEL_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/artifact-copy.ts b/apps/desktop/src/renderer/locales/artifact-copy.ts index 22d72fb79bf..dadc495a104 100644 --- a/apps/desktop/src/renderer/locales/artifact-copy.ts +++ b/apps/desktop/src/renderer/locales/artifact-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type ReasonCopy = { title: string; description: string }; @@ -94,9 +94,7 @@ export type ArtifactCopy = { loadingImage: string; }; }; - -const ARTIFACT_COPY = { - 'zh-CN': { +const artifactCopyZhCn: ArtifactCopy = { pane: { refreshFailed: '刷新生成文件失败', openFailed: '无法在 Finder 中打开生成文件', copyFailed: '复制失败', readTextFailed: '无法读取生成文件文本内容。', copied: '已复制生成文件文本', saved: '已另存生成文件', saveFailed: '另存失败', @@ -133,8 +131,8 @@ const ARTIFACT_COPY = { readFailed: { title: '加载预览失败', description: '无法读取文件内容(可能已被删除、移动或权限不足)。请通过「在 Finder 中打开」检查文件。' }, unsupported: '暂不支持的预览', name: '名称', unnamed: '(未命名)', type: '类型', size: '大小', openInFinder: '在 Finder 中打开', loadingImage: '加载图片预览…', }, - }, - 'zh-TW': { + }; +const artifactCopyZhTw: ArtifactCopy = { pane: { refreshFailed: '重新整理生成檔案失敗', openFailed: '無法在 Finder 中開啟生成檔案', copyFailed: '複製失敗', readTextFailed: '無法讀取生成檔案文本內容。', copied: '已複製生成檔案文本', saved: '已另存生成檔案', saveFailed: '另存失敗', @@ -171,8 +169,8 @@ const ARTIFACT_COPY = { readFailed: { title: '載入預覽失敗', description: '無法讀取檔案內容(可能已被刪除、移動或權限不足)。請透過「在 Finder 中開啟」檢查檔案。' }, unsupported: '暫不支援的預覽', name: '名稱', unnamed: '(未命名)', type: '型別', size: '大小', openInFinder: '在 Finder 中開啟', loadingImage: '載入圖片預覽…', }, - }, - en: { + }; +const artifactCopyEn: ArtifactCopy = { pane: { refreshFailed: 'Failed to refresh generated files', openFailed: 'Could not show generated file in Finder', copyFailed: 'Copy failed', readTextFailed: 'Could not read the generated file as text.', copied: 'Generated file text copied', saved: 'Generated file saved as', saveFailed: 'Save as failed', @@ -209,8 +207,9 @@ const ARTIFACT_COPY = { readFailed: { title: 'Failed to load preview', description: 'The file could not be read. It may have been deleted, moved, or blocked by permissions. Use “Show in Finder” to inspect it.' }, unsupported: 'Unsupported preview', name: 'Name', unnamed: '(unnamed)', type: 'Type', size: 'Size', openInFinder: 'Show in Finder', loadingImage: 'Loading image preview…', }, - }, -} satisfies UiCatalog; + }; + +const ARTIFACT_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': artifactCopyZhCn, 'zh-TW': artifactCopyZhTw, en: artifactCopyEn })); export function getArtifactCopy(locale: UiLocale): ArtifactCopy { return ARTIFACT_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/browser-copy.ts b/apps/desktop/src/renderer/locales/browser-copy.ts index e7594a29958..b043952f7b0 100644 --- a/apps/desktop/src/renderer/locales/browser-copy.ts +++ b/apps/desktop/src/renderer/locales/browser-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type BrowserCopy = { unsupportedScheme: string; @@ -57,9 +57,7 @@ export type BrowserCopy = { title: string; description: string; }; - -const BROWSER_COPY = { - 'zh-CN': { +const browserCopyZhCn: BrowserCopy = { unsupportedScheme: '嵌入式浏览器只支持打开 HTTP/HTTPS 网页地址。', invalidUrl: '这个地址无法识别,请检查网址后重试。', openFailed: '无法打开地址', @@ -96,8 +94,8 @@ const BROWSER_COPY = { restorePreview: '还原分栏', title: '嵌入式浏览器', description: '输入网址打开页面,或让助手帮你导航并操作。', - }, - 'zh-TW': { + }; +const browserCopyZhTw: BrowserCopy = { unsupportedScheme: '嵌入式瀏覽器只支援開啟 HTTP/HTTPS 網頁地址。', invalidUrl: '這個地址無法識別,請檢查網址後重試。', openFailed: '無法開啟地址', @@ -134,8 +132,8 @@ const BROWSER_COPY = { restorePreview: '還原分欄', title: '嵌入式瀏覽器', description: '輸入網址開啟頁面,或讓助手幫你導航並操作。', - }, - en: { + }; +const browserCopyEn: BrowserCopy = { unsupportedScheme: 'The embedded browser only supports HTTP and HTTPS addresses.', invalidUrl: 'This address is not valid. Check it and try again.', openFailed: 'Could not open address', @@ -172,8 +170,9 @@ const BROWSER_COPY = { restorePreview: 'Restore split view', title: 'Embedded browser', description: 'Enter an address, or ask the assistant to navigate and interact with a page.', - }, -} satisfies UiCatalog; + }; + +const BROWSER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': browserCopyZhCn, 'zh-TW': browserCopyZhTw, en: browserCopyEn })); export function getBrowserCopy(locale: UiLocale): BrowserCopy { return BROWSER_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/capability-reason-copy.ts b/apps/desktop/src/renderer/locales/capability-reason-copy.ts index 1178ebb03d4..7471bf62e9f 100644 --- a/apps/desktop/src/renderer/locales/capability-reason-copy.ts +++ b/apps/desktop/src/renderer/locales/capability-reason-copy.ts @@ -18,11 +18,11 @@ */ import type { CapabilityReasonCode } from '@maka/core/capabilities'; -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type CapabilityReasonCopy = Record; -const CAPABILITY_REASON_COPY = { +const CAPABILITY_REASON_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { disabled: '该能力当前已关闭。', platform_credentials_missing: '未配置平台凭据', @@ -98,7 +98,7 @@ const CAPABILITY_REASON_COPY = { notifications_unsupported: 'Electron notifications are unavailable', permission_probe_failed: 'Permission probe failed', }, -} satisfies UiCatalog; +})); export function getCapabilityReasonCopy(locale: UiLocale): CapabilityReasonCopy { return CAPABILITY_REASON_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/external-session-import-copy.ts b/apps/desktop/src/renderer/locales/external-session-import-copy.ts index ddc04148abc..b86c3aaf8fc 100644 --- a/apps/desktop/src/renderer/locales/external-session-import-copy.ts +++ b/apps/desktop/src/renderer/locales/external-session-import-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ExternalSessionLimit } from '@maka/core/external-session'; /** @@ -117,9 +117,7 @@ type ExternalSessionImportCopy = { bundleUnreadable: string; bundleFailed: string; }; - -const COPY = { - 'zh-CN': { +const copyZhCn: ExternalSessionImportCopy = { modeLabel: '导入或导出', modeImport: '导入任务', modeExport: '导出任务', @@ -200,8 +198,8 @@ const COPY = { batchNothingImported: '没有对话被导入。', importOutcomeUnknownDescription: (names) => `以下对话的导入结果无法确认:${names.map((name) => `「${name}」`).join('、')}。可以先在任务列表中查找,也可以再次导入;再次导入会创建独立任务。`, - }, - 'zh-TW': { + }; +const copyZhTw: ExternalSessionImportCopy = { modeLabel: '匯入或匯出', modeImport: '匯入任務', modeExport: '匯出任務', @@ -282,8 +280,8 @@ const COPY = { batchNothingImported: '沒有匯入任何對話。', importOutcomeUnknownDescription: (names) => `以下對話的匯入結果無法確認:${names.map((name) => `「${name}」`).join('、')}。可以先在任務列表中查詢,也可以再次匯入;再次匯入會建立獨立任務。`, - }, - en: { + }; +const copyEn: ExternalSessionImportCopy = { modeLabel: 'Import or export', modeImport: 'Import tasks', modeExport: 'Export tasks', @@ -368,8 +366,9 @@ const COPY = { batchNothingImported: 'No conversation was imported.', importOutcomeUnknownDescription: (names) => `Maka could not confirm the outcome of these imports: ${names.map((name) => `“${name}”`).join(', ')}. Check the task list or import again; importing again creates an independent task.`, - }, -} satisfies UiCatalog; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); export function getExternalSessionImportCopy(locale: UiLocale): ExternalSessionImportCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/mcp-copy.ts b/apps/desktop/src/renderer/locales/mcp-copy.ts index d8e525777f1..8160b314451 100644 --- a/apps/desktop/src/renderer/locales/mcp-copy.ts +++ b/apps/desktop/src/renderer/locales/mcp-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type McpCopy = { errors: { @@ -62,9 +62,7 @@ export type McpCopy = { protocolHelp: string; sseProtocolHelp: string; advanced: string; stdioProtocolHelp: string; }; }; - -const MCP_COPY = { - 'zh-CN': { +const mcpCopyZhCn: McpCopy = { errors: { load: '载入 MCP 失败', save: '保存 MCP 失败', invalidConfigFile: (path) => `${path} 中的 JSON 无效,文件未被修改。请关闭应用,备份并修复此文件后重试。`, @@ -125,8 +123,8 @@ const MCP_COPY = { protocolHelp: '自动协商会按服务支持的版本连接;遇到兼容性问题时再固定版本。', sseProtocolHelp: '旧版 SSE 使用传统协议。', advanced: '高级设置', stdioProtocolHelp: '自动协商或仅使用新版协议时,会额外启动一次服务器进行探测。', }, - }, - 'zh-TW': { + }; +const mcpCopyZhTw: McpCopy = { errors: { load: '載入 MCP 失敗', save: '儲存 MCP 失敗', invalidConfigFile: (path) => `${path} 中的 JSON 無效,檔案未被修改。請關閉應用程式,備份並修復此檔案後重試。`, @@ -187,8 +185,8 @@ const MCP_COPY = { protocolHelp: '自動協商會依服務支援的版本連線;遇到相容性問題時再固定版本。', sseProtocolHelp: '舊版 SSE 使用傳統協議。', advanced: '進階設定', stdioProtocolHelp: '自動協商或僅使用新版協議時,會額外啟動一次伺服器進行探測。', }, - }, - en: { + }; +const mcpCopyEn: McpCopy = { errors: { load: 'Failed to load MCP', save: 'Failed to save MCP', invalidConfigFile: (path) => `Invalid JSON in ${path}. The file is unchanged. Close the app, back up and repair this file before retrying.`, @@ -249,8 +247,9 @@ const MCP_COPY = { protocolHelp: 'Auto-negotiation uses a version the service supports; pin a version only for compatibility.', sseProtocolHelp: 'Legacy SSE uses the legacy protocol.', advanced: 'Advanced settings', stdioProtocolHelp: 'Auto-negotiation or modern-only mode starts the server one extra time to check protocol support.', }, - }, -} satisfies UiCatalog; + }; + +const MCP_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': mcpCopyZhCn, 'zh-TW': mcpCopyZhTw, en: mcpCopyEn })); export function getMcpCopy(locale: UiLocale): McpCopy { return MCP_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/onboarding-copy.ts b/apps/desktop/src/renderer/locales/onboarding-copy.ts index fb4fd829588..69e164d3711 100644 --- a/apps/desktop/src/renderer/locales/onboarding-copy.ts +++ b/apps/desktop/src/renderer/locales/onboarding-copy.ts @@ -18,7 +18,7 @@ */ import type { OnboardingState } from '@maka/core/onboarding'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { OnboardingHeroCopy } from '../onboarding-hero-copy.js'; // Blocked states are keyed by reason, not just by kind: a new blocked reason @@ -50,7 +50,7 @@ export interface OnboardingCatalog { snapshotErrorFallback: string; } -const ONBOARDING_COPY_BY_LOCALE: UiCatalog = { +const ONBOARDING_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { hero: { needs_connection: { @@ -198,7 +198,7 @@ const ONBOARDING_COPY_BY_LOCALE: UiCatalog = { skip: 'Skip onboarding', snapshotErrorFallback: 'First-run status is temporarily unavailable. Try again later.', }, -}; +})); export function getOnboardingCopy(locale: UiLocale): OnboardingCatalog { return ONBOARDING_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/peer-mesh-copy.ts b/apps/desktop/src/renderer/locales/peer-mesh-copy.ts index c9fa53ee34f..bc59849778b 100644 --- a/apps/desktop/src/renderer/locales/peer-mesh-copy.ts +++ b/apps/desktop/src/renderer/locales/peer-mesh-copy.ts @@ -18,7 +18,7 @@ */ import type { PeerMeshMemberProjection, PeerMeshQueryResult } from '@maka/runtime-host/protocol'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface PeerMeshCopy { readonly title: string; @@ -146,7 +146,7 @@ export interface PeerMeshCopy { readonly localHostMissingHint: string; } -const PEER_MESH_COPY = { +const PEER_MESH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { title: 'Peer Mesh', experimental: '实验性', @@ -626,7 +626,7 @@ const PEER_MESH_COPY = { localHostMissingHint: 'Add it so other members can reach tasks shared from this device through the Mesh.', }, -} satisfies UiCatalog; +})); export function getPeerMeshCopy(locale: UiLocale): PeerMeshCopy { return PEER_MESH_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/permission-center-copy.ts b/apps/desktop/src/renderer/locales/permission-center-copy.ts index e2398a71eb2..a8d810542b6 100644 --- a/apps/desktop/src/renderer/locales/permission-center-copy.ts +++ b/apps/desktop/src/renderer/locales/permission-center-copy.ts @@ -27,7 +27,7 @@ import type { RuntimeProbeState, } from '@maka/core/capabilities'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type Tone = StatusSemantic; type StatusCopy = { label: string; tone: Tone }; @@ -99,9 +99,7 @@ export type PermissionCenterCopy = { cuBackendStatus(missingPermissionLabels: readonly string[], health: RuntimeProbeState): string; reasonFallback: string; }; - -const PERMISSION_CENTER_COPY = { - 'zh-CN': { +const permissionCenterCopyZhCn: PermissionCenterCopy = { readiness: { not_configured: { label: '等待配置', detail: '需要先打开开关或补齐配置才能启用。', tone: 'neutral' }, denied: { label: '系统拒绝', detail: '所需系统权限被拒绝或当前平台不支持。', tone: 'error' }, @@ -157,8 +155,8 @@ const PERMISSION_CENTER_COPY = { not_run: 'service 将在首次调用时启动;按目标与动作类别授权后可操作本机应用。', } satisfies Record)[health], reasonFallback: '状态详情请查看运行日志。', - }, - 'zh-TW': { + }; +const permissionCenterCopyZhTw: PermissionCenterCopy = { readiness: { not_configured: { label: '等待設定', detail: '需要先開啟開關或補齊設定才能啟用。', tone: 'neutral' }, denied: { label: '系統拒絕', detail: '所需系統權限被拒絕或目前平臺不支援。', tone: 'error' }, @@ -214,8 +212,8 @@ const PERMISSION_CENTER_COPY = { not_run: 'service 將在首次呼叫時啟動;依目標與動作類別授權後可操作本機應用程式。', } satisfies Record)[health], reasonFallback: '狀態詳情請查看執行日誌。', - }, - en: { + }; +const permissionCenterCopyEn: PermissionCenterCopy = { readiness: { not_configured: { label: 'Needs setup', detail: 'Enable the feature or complete its configuration first.', tone: 'neutral' }, denied: { label: 'Denied by system', detail: 'A required system permission was denied or is unsupported on this platform.', tone: 'error' }, @@ -271,8 +269,9 @@ const PERMISSION_CENTER_COPY = { not_run: 'The service starts on first use; grant by target and action category to operate local apps.', } satisfies Record)[health], reasonFallback: 'See the runtime logs for details.', - }, -} satisfies UiCatalog; + }; + +const PERMISSION_CENTER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': permissionCenterCopyZhCn, 'zh-TW': permissionCenterCopyZhTw, en: permissionCenterCopyEn })); export function getPermissionCenterCopy(locale: UiLocale): PermissionCenterCopy { return PERMISSION_CENTER_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/plan-mode-copy.ts b/apps/desktop/src/renderer/locales/plan-mode-copy.ts index 71a035ea11f..9707f7ceea7 100644 --- a/apps/desktop/src/renderer/locales/plan-mode-copy.ts +++ b/apps/desktop/src/renderer/locales/plan-mode-copy.ts @@ -18,7 +18,7 @@ */ import type { PlanExecutionStep, PlanProposal } from '@maka/core/plan'; -import { lookupCopy, type UiCatalog, type UiLocale } from '@maka/core/ui-locale'; +import { lookupCopy, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { PlanControlErrorCode } from '@maka/runtime-host/protocol'; import type { PlanControlIpcResult } from '../../shared/plan-mode-ipc.js'; @@ -56,9 +56,7 @@ export interface PlanModeCopy { readonly stepStatuses: Record; }; } - -const COPY = { - 'zh-CN': { +const copyZhCn: PlanModeCopy = { operationFailed: '计划操作失败,请稍后重试。', controlFailure: { not_found: '计划方案已不存在,请刷新后重试', @@ -90,8 +88,8 @@ const COPY = { resume: '恢复执行', abandon: '放弃计划', stepStatuses: { pending: '未开始', in_progress: '正在执行', completed: '已完成', skipped: '已跳过' }, }, - }, - 'zh-TW': { + }; +const copyZhTw: PlanModeCopy = { operationFailed: '計劃操作失敗,請稍後重試。', controlFailure: { not_found: '計劃方案已不存在,請重新整理後重試', @@ -123,8 +121,8 @@ const COPY = { resume: '恢復執行', abandon: '放棄計劃', stepStatuses: { pending: '未開始', in_progress: '正在執行', completed: '已完成', skipped: '已跳過' }, }, - }, - en: { + }; +const copyEn: PlanModeCopy = { operationFailed: 'The plan action failed. Try again later.', controlFailure: { not_found: 'This plan proposal no longer exists. Refresh and try again.', @@ -157,8 +155,9 @@ const COPY = { resume: 'Resume', abandon: 'Abandon plan', stepStatuses: { pending: 'Not started', in_progress: 'In progress', completed: 'Completed', skipped: 'Skipped' }, }, - }, -} satisfies UiCatalog; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); export function getPlanModeCopy(locale: UiLocale): PlanModeCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/session-collaboration-copy.ts b/apps/desktop/src/renderer/locales/session-collaboration-copy.ts index f0a2c92a48f..da017587cfe 100644 --- a/apps/desktop/src/renderer/locales/session-collaboration-copy.ts +++ b/apps/desktop/src/renderer/locales/session-collaboration-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; const ZH_CN = { shareAction: '分享此任务', @@ -349,7 +349,7 @@ const EN = { pendingTurnRequestCount: (count: number) => `${count} pending Turn ${count === 1 ? 'request' : 'requests'}`, } satisfies SessionCollaborationCopy; -const COPY = { 'zh-CN': ZH_CN, 'zh-TW': ZH_TW, en: EN } satisfies UiCatalog; +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': ZH_CN, 'zh-TW': ZH_TW, en: EN })); export function getSessionCollaborationCopy(locale: UiLocale) { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/session-local-copy.ts b/apps/desktop/src/renderer/locales/session-local-copy.ts index c6422159307..0ba116de6a2 100644 --- a/apps/desktop/src/renderer/locales/session-local-copy.ts +++ b/apps/desktop/src/renderer/locales/session-local-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; interface SessionLocalCopy { saved: string; @@ -29,7 +29,7 @@ interface SessionLocalCopy { updateError: string; } -const catalog = { +const catalog = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: { saved: 'Waiting to send', unknown: 'Delivery unconfirmed. Do not send again.', @@ -57,7 +57,7 @@ const catalog = { check: '檢查是否送達', updateError: '無法更新已儲存的訊息', }, -} satisfies UiCatalog; +})); export function getSessionLocalCopy(locale: UiLocale): SessionLocalCopy { return catalog[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-bot-copy.ts b/apps/desktop/src/renderer/locales/settings-bot-copy.ts index 2cc0f0f30b5..f5f9c50e1ed 100644 --- a/apps/desktop/src/renderer/locales/settings-bot-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-bot-copy.ts @@ -23,7 +23,7 @@ import type { BotStatusCode, BotTestErrorCode, WechatBridgeQrHintCode } from '@m import type { BotOnboardingErrorCode, BotOnboardingProvider, BotOnboardingSnapshot, BotOnboardingRetryFailureCategory } from '@maka/core/bot-onboarding'; import type { GeneralizedErrorClass } from '@maka/core/redaction'; -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type WidenCopy = T extends string ? string @@ -33,7 +33,7 @@ type WidenCopy = T extends string // Bot transport failures name the platform, not the model service that the // shared generalized copy describes. -const BOT_TRANSPORT_ERRORS = { +const BOT_TRANSPORT_ERRORS = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': { timeout: '请求超时,请稍后重试', rate_limited: '请求过于频繁,请稍后重试', @@ -55,7 +55,7 @@ const BOT_TRANSPORT_ERRORS = { provider_error: 'The platform is temporarily unavailable. Try again later', network_error: 'Network error. Check the network and proxy settings', }, -} satisfies UiCatalog>; +})); const zhCopy = { providers: { @@ -419,11 +419,11 @@ const enCopy: BotSettingsCopy = { wechat: { token: 'WeChat Bot Token', tokenPlaceholder: 'Local wechat-bridge Bearer Token', collapseAdvanced: 'Hide advanced settings', expandAdvanced: 'Advanced settings (Official Account / local bridge URL)', bridgeAddress: 'Local bridge URL', appId: 'Official Account App ID', appIdPlaceholder: 'WeChat Official Account App ID', appSecret: 'Official Account App Secret', appSecretPlaceholder: 'WeChat Official Account App Secret', advancedNotice: 'The local bridge defaults to http://127.0.0.1:18400. Official Account App ID and App Secret are used only for Official Account messaging; personal WeChat QR sign-in uses the local bridge.', readQrFailed: 'Could not read a QR code from the local wechat-bridge. Make sure the bridge is running.', title: 'WeChat QR sign-in', subtitle: 'Scan the QR code with WeChat and confirm signing in to the local wechat-bridge on your phone.', close: 'Close WeChat QR sign-in', generating: 'Generating QR code…', loggedIn: 'WeChat is signed in. Return to test the connection or restart the listener.', expired: 'QR code expired', expiredHint: 'Refresh the QR code and scan again to continue signing in.', refreshing: 'Refreshing…', refresh: 'Refresh QR code', qrAlt: 'WeChat sign-in QR code', waiting: 'Waiting for confirmation… Sign-in status refreshes every 3 seconds.', retrying: 'Retrying…', retry: 'Retry', bridgeGenerating: 'The bridge is generating a QR code', bridgeGeneratingHint: 'The QR code appears automatically once ready; you can also fetch it again.', fetching: 'Fetching…', fetchAgain: 'Fetch again' }, }; -const BOT_SETTINGS_COPY = { +const BOT_SETTINGS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': zhCopy, 'zh-TW': zhTwCopy, en: enCopy, -} satisfies UiCatalog; +})); export function getBotSettingsCopy(locale: UiLocale): BotSettingsCopy { return BOT_SETTINGS_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts b/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts index 489ba804802..26586239f28 100644 --- a/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type DailyReviewSettingsCopy = { defaultModel: string; @@ -39,7 +39,7 @@ export type DailyReviewSettingsCopy = { modelHelp: string; }; -const SETTINGS_DAILY_REVIEW_COPY = { +const SETTINGS_DAILY_REVIEW_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { defaultModel: '跟随任务默认', saveFailed: '保存每日回顾设置失败', @@ -97,7 +97,7 @@ const SETTINGS_DAILY_REVIEW_COPY = { model: 'Analysis model', modelHelp: 'Follows the current task default when unspecified.', }, -} satisfies UiCatalog; +})); export function getDailyReviewSettingsCopy(locale: UiLocale): DailyReviewSettingsCopy { return SETTINGS_DAILY_REVIEW_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-data-copy.ts b/apps/desktop/src/renderer/locales/settings-data-copy.ts index 17c53c62b9a..4b3ea0f2958 100644 --- a/apps/desktop/src/renderer/locales/settings-data-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-data-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ConfigCategory } from '@maka/storage/config-transfer'; export type DataSettingsCopy = { @@ -40,7 +40,7 @@ export type DataSettingsCopy = { exportConfig: string; importConfig: string; }; -const SETTINGS_DATA_COPY = { +const SETTINGS_DATA_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { categories: { connections: { label: '模型连接', detail: '供应商连接与默认模型(不含密钥)' }, @@ -125,7 +125,7 @@ const SETTINGS_DATA_COPY = { sensitiveWarning: '⚠️ Secrets will be written to the export file as plain text. Anyone with this file can use them. Store it securely and do not share it.', conflictAria: 'How to handle connections with the same name during import', skip: 'Skip', overwrite: 'Overwrite', exportConfig: 'Export configuration…', importConfig: 'Import configuration…', }, -} satisfies UiCatalog; +})); export function getDataSettingsCopy(locale: UiLocale): DataSettingsCopy { return SETTINGS_DATA_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts b/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts index 8d007cfa861..9b22ca243b8 100644 --- a/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiLocale, UiCatalog } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ExternalAgentSetupFailure } from '@maka/runtime-host/protocol'; const en = { programTitle: "Program", @@ -201,7 +201,7 @@ const zhTW: typeof en = { cleanup_failed: '無法釋放暫時程序,請重新啟動 Host 後重試。', }, }; -const catalog = { en, 'zh-CN': zh, 'zh-TW': zhTW } satisfies UiCatalog; +const catalog = resolveUiMessageCatalog(defineUiMessageCatalog()({ en, 'zh-CN': zh, 'zh-TW': zhTW })); export function getExternalAgentsCopy(locale: UiLocale) { return catalog[locale]; } diff --git a/apps/desktop/src/renderer/locales/settings-health-copy.ts b/apps/desktop/src/renderer/locales/settings-health-copy.ts index b4f9229b283..e69c753a4ca 100644 --- a/apps/desktop/src/renderer/locales/settings-health-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-health-copy.ts @@ -28,7 +28,7 @@ import type { HealthSignalStatus, } from '@maka/core/health'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Health signals carry their own severity ladder — error > warning > info > ok @@ -104,9 +104,7 @@ const layersEn: HealthCenterCopy['layers'] = { runtime_probe: { label: 'Runtime probe', description: 'The latest real send, stream, or event-receipt observation.' }, storage: { label: 'Storage', description: 'Health of workspace files, JSONL, SQLite, and other local storage.' }, }; - -const SETTINGS_HEALTH_COPY = { - 'zh-CN': { +const settingsHealthCopyZhCn: HealthCenterCopy = { loading: '正在加载健康快照', readFailed: '无法读取健康快照', noData: '健康服务未返回数据。', readAgain: '重新读取', title: '健康中心', subtitle: '各项能力当前的运行状况检查。', badge: '只读快照', lastRead: '最近一次读取:', refresh: '刷新', summaryAria: '按状态筛选健康信号', summaryFilterAria: (label, count, selected) => selected ? `${label} ${count} 项,当前筛选;再次按下显示全部` : `仅显示${label}健康信号,共 ${count} 项`, @@ -124,8 +122,8 @@ const SETTINGS_HEALTH_COPY = { signalLabel: (signal) => (signal.id.endsWith(':runtime') ? `${signal.label} 运行态` : signal.label), signalMessage: (signal) => signalMessagesZh[signal.message], signalDetail: (signal) => signalDetailZh(signal.detail), - }, - 'zh-TW': { + }; +const settingsHealthCopyZhTw: HealthCenterCopy = { loading: '正在載入健康快照', readFailed: '無法讀取健康快照', noData: '健康服務未返回資料。', readAgain: '重新讀取', title: '健康中心', subtitle: '各項能力目前的執行狀況檢查。', badge: '只讀快照', lastRead: '最近一次讀取:', refresh: '重新整理', summaryAria: '按狀態篩選健康訊號', summaryFilterAria: (label, count, selected) => selected ? `${label} ${count} 項,目前篩選;再次按下顯示全部` : `僅顯示${label}健康訊號,共 ${count} 項`, @@ -143,8 +141,8 @@ const SETTINGS_HEALTH_COPY = { signalLabel: (signal) => (signal.id.endsWith(':runtime') ? `${signal.label} 執行狀態` : signal.label), signalMessage: (signal) => signalMessagesZhTw[signal.message], signalDetail: (signal) => signalDetailZhTw(signal.detail), - }, - en: { + }; +const settingsHealthCopyEn: HealthCenterCopy = { loading: 'Loading health snapshot', readFailed: 'Could not read health snapshot', noData: 'The health service returned no data.', readAgain: 'Read again', title: 'Health center', subtitle: 'How each capability is currently doing.', badge: 'Read-only snapshot', lastRead: 'Last read: ', refresh: 'Refresh', summaryAria: 'Filter health signals by status', summaryFilterAria: (label, count, selected) => selected ? `${label}, ${count}; filter selected. Press again to show all signals` : `Show only ${label.toLowerCase()} health signals, ${count}`, @@ -162,8 +160,9 @@ const SETTINGS_HEALTH_COPY = { signalLabel: (signal) => (signal.id.endsWith(':runtime') ? `${signal.label} runtime` : signal.label), signalMessage: (signal) => signalMessagesEn[signal.message], signalDetail: (signal) => signalDetailEn(signal.detail), - }, -} satisfies UiCatalog; + }; + +const SETTINGS_HEALTH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsHealthCopyZhCn, 'zh-TW': settingsHealthCopyZhTw, en: settingsHealthCopyEn })); export function getHealthCenterCopy(locale: UiLocale): HealthCenterCopy { return SETTINGS_HEALTH_COPY[locale]; @@ -228,30 +227,29 @@ const signalMessagesEn: Record = { capability_denied: 'The capability is blocked by a required system permission.', capability_degraded: 'The capability runtime probe is degraded.', }; - -const connectionTestErrorMessages = { - 'zh-CN': { +const connectiontesterrormessagesZhCn: Record = { auth: '鉴权失败', timeout: '请求超时', provider_unavailable: '模型服务返回错误', network: '网络错误', unknown: '连接测试失败', - }, - 'zh-TW': { + }; +const connectiontesterrormessagesZhTw: Record = { auth: '驗證失敗', timeout: '請求逾時', provider_unavailable: '模型服務傳回錯誤', network: '網路錯誤', unknown: '連線測試失敗', - }, - en: { + }; +const connectiontesterrormessagesEn: Record = { auth: 'Authentication failed', timeout: 'Request timed out', provider_unavailable: 'Model service returned an error', network: 'Network error', unknown: 'Connection test failed', - }, -} satisfies UiCatalog>; + }; + +const connectionTestErrorMessages = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': connectiontesterrormessagesZhCn, 'zh-TW': connectiontesterrormessagesZhTw, en: connectiontesterrormessagesEn })); function signalDetailZh(detail: HealthSignalDetail | undefined): string | undefined { if (!detail) return undefined; @@ -336,12 +334,11 @@ function signalDetailEn(detail: HealthSignalDetail | undefined): string | undefi return unhandledDetail(detail); } } +const unknownruntimeerrorclassZhCn: string = '未知错误'; +const unknownruntimeerrorclassZhTw: string = '未知錯誤'; +const unknownruntimeerrorclassEn: string = 'Unknown error'; -const unknownRuntimeErrorClass = { - 'zh-CN': '未知错误', - 'zh-TW': '未知錯誤', - en: 'Unknown error', -} satisfies UiCatalog; +const unknownRuntimeErrorClass = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': unknownruntimeerrorclassZhCn, 'zh-TW': unknownruntimeerrorclassZhTw, en: unknownruntimeerrorclassEn })); // Runtime probes carry the turn's failure class (rate_limit, context_overflow, // …), a wider vocabulary than connection tests; unmapped classes stay visible. diff --git a/apps/desktop/src/renderer/locales/settings-jev-copy.ts b/apps/desktop/src/renderer/locales/settings-jev-copy.ts index ab24bd9ac9e..d635242c369 100644 --- a/apps/desktop/src/renderer/locales/settings-jev-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-jev-copy.ts @@ -17,9 +17,9 @@ * under the License. */ -import type { UiCatalog } from '@maka/core/ui-locale'; +import { defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; -export const JEV_COPY = { +export const JEV_COPY = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': { advanced: '高级设置', title: 'Jev 辅助决策', help: '使用 TypeSafe Jev 辅助 WorkHub 的意图分类和工作路由。会发送当前消息、近期对话与候选工作摘要;对话、执行和标题仍使用原模型。', @@ -44,4 +44,4 @@ export const JEV_COPY = { behavior: 'Uncertain decisions ask for clarification; service failures use the existing router.', failure: 'Could not save Jev settings. Please try again.', }, -} satisfies UiCatalog>; +})); diff --git a/apps/desktop/src/renderer/locales/settings-memory-copy.ts b/apps/desktop/src/renderer/locales/settings-memory-copy.ts index aeaa911bb69..c6dd15eefa3 100644 --- a/apps/desktop/src/renderer/locales/settings-memory-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-memory-copy.ts @@ -20,7 +20,7 @@ import type { LocalMemoryOperationCode, LocalMemoryState } from '@maka/core/local-memory'; import type { MemoryMutationRejectionReason } from '@maka/runtime-host/protocol'; -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type MemoryTextKey = | 'localFile' | 'localFileHelp' | 'enableLocalFile' | 'agentReadable' | 'agentReadableHelp' | 'enableAgentRead' @@ -93,9 +93,7 @@ const zhTwText = { const enText = { localFile: 'Local MEMORY.md', localFileHelp: 'A transparent Markdown file stored in the current local workspace. Content is never extracted from chats automatically.', enableLocalFile: 'Enable local MEMORY.md', agentReadable: 'Available to model context', agentReadableHelp: 'Off by default. When enabled, local memory is given to the model along with your message; incognito mode still withholds it.', enableAgentRead: 'Allow model context to read local memory', waitingFile: 'Waiting to create MEMORY.md', waitingBackup: 'Waiting to create a previous-version backup', dirty: 'Unsaved changes', savedDraft: 'Draft saved', backupCandidates: 'Backup candidates', backupCandidatesAria: 'Local memory backup candidates', opening: 'Opening…', open: 'Open', restoring: 'Restoring…', restore: 'Restore', copying: 'Copying…', copyReference: 'Copy reference', backupHelp: 'Previous-version actions use the latest candidate. Only metadata is shown here, never backup contents.', savedAt: 'Saved ', previewPaused: 'Draft entry preview paused', filterAria: 'Filter local memory', filterPlaceholder: 'Filter title, content, ID, or tags', clear: 'Clear', filterEmpty: 'No matching memory entries', filterEmptyHelp: 'Filtering does not modify MEMORY.md. Clear the filter to show all entries.', activeMemories: 'Active memories', archivedMemories: 'Archived memories', waitingEntry: 'Ready to add a memory entry', waitingEntryHelp: 'Confirm a memory in chat, or use "Add memory" above.', manualAddAria: 'Add local memory manually', manualAdd: 'Add memory manually', manualAddHelp: 'Written to the local MEMORY.md immediately.', title: 'Memory title', titlePlaceholder: 'Title', tags: 'Memory tags', tagsPlaceholder: 'Tags (comma-separated, optional)', content: 'Memory content', contentPlaceholder: 'Content', addDraft: 'Add memory', sensitiveDraft: 'Draft may contain sensitive fields', sensitiveDraftHelp: 'Suspected tokens, API keys, and passwords are redacted before MEMORY.md is written.', fileContent: 'MEMORY.md content', fileActionsAria: 'MEMORY.md file actions', saving: 'Saving…', save: 'Save', saved: 'Saved', openFile: 'Open MEMORY.md', openFolder: 'Open containing folder', loading: 'Loading…', reload: 'Reload', openPrevious: 'Open previous version', copyPath: 'Copy path', copyPrevious: 'Copy previous-version reference', resetting: 'Resetting…', resetBackup: 'Reset and back up', restorePrevious: 'Restore previous version', archiveDraftNotice: 'Archive and restore actions update only the draft until you save MEMORY.md.', noMatchEntry: 'No matching entries.', noEntry: 'No entries yet.', created: 'Created ', updated: 'Updated ', archivedNoPrompt: 'Archived; not given to the model', activePrompt: 'Active entry; given to the model when you send', locateDraft: 'Locate in draft', promptPreview: 'Model context preview', willInject: 'Included when sending', willNotInject: 'Not currently included', copyContext: 'Copy context', promptPreviewHelp: 'What will be given to the model when you send. Archived entries are excluded and suspected secrets are redacted.', safeModePreview: 'MEMORY.md is too large, so no model-context preview is generated.', emptyPromptPreview: 'No active memories will be given to the model.', loadFailed: 'Failed to load local memory', reloaded: 'MEMORY.md reloaded', reloadDiscarded: 'Unsaved draft changes were discarded.', toggleFailed: 'Failed to update local memory', agentReadFailed: 'Failed to update model read access', saveBlocked: 'Save blocked', safeMode: 'MEMORY.md is too large and entered safe mode.', savedRedacted: 'Saved with sensitive fields redacted', savedFile: 'MEMORY.md saved', saveFailed: 'Failed to save MEMORY.md', resetDone: 'MEMORY.md reset', resetDoneDetail: 'The previous version was saved as a backup.', resetFailed: 'Failed to reset MEMORY.md', noBackup: 'No backup available to restore', noBackupDetail: 'A previous-version backup is created after you save or reset MEMORY.md.', restoreLatestTitle: 'Restore the previous MEMORY.md version?', restoreCandidateTitle: 'Restore this MEMORY.md backup?', confirmRestore: 'Restore', cancel: 'Cancel', restoredLatest: 'Previous MEMORY.md version restored', restoredCandidate: 'MEMORY.md backup candidate restored', restoredDetail: 'The file from before the restore was saved as restore.bak.', restoreFailed: 'Restore failed', restoreLatestFailed: 'Failed to restore previous version', restoreCandidateFailed: 'Failed to restore backup', openFailed: 'Open failed', openPreviousFailed: 'Failed to open previous version', pathCopied: 'Path copied', copyFailed: 'Copy failed', copyFailedDetail: 'The clipboard is unavailable or access was denied by the system.', backupReferenceCopied: 'Previous-version reference copied', entryReferenceCopied: 'Memory reference copied', locateFailed: 'Could not locate memory', locateFailedDetail: 'This memory is not in the current draft. Save or reload, then try again.', emptyTitle: 'Title is required', emptyTitleDetail: 'Give this memory a short title.', emptyContent: 'Content is required', emptyContentDetail: 'Enter the preference or fact to retain.', draftOversize: 'Draft is too large', oversizeDetail: 'MEMORY.md exceeds the safety limit. Remove older content first.', addedDraft: 'Memory added', addedDraftDetail: 'Written to MEMORY.md.', updateFailed: 'Could not update memory', invalidIdDetail: 'This memory has no recognizable ID, so the update was stopped.', archivedDraft: 'Memory archived in draft', restoredDraft: 'Memory restored in draft', updateBlocked: 'Update blocked', archived: 'Memory archived', restored: 'Memory restored', archiveFailed: 'Failed to archive memory', entryRestoreFailed: 'Failed to restore memory', promptCopied: 'Model context preview copied', promptCopiedDetail: 'Uses the same prompt-preview and redaction path.', restoreDraftAction: 'Restore to draft', archiveDraftAction: 'Archive in draft', restoreAction: 'Restore', archiveAction: 'Archive', } satisfies Record; - -const SETTINGS_MEMORY_COPY = { - 'zh-CN': { +const settingsMemoryCopyZhCn: MemorySettingsCopy = { intlLocale: 'zh-CN', text: zhText, countActive: (count, draft) => draft ? `草稿 ${count} 条生效` : `${count} 条生效`, @@ -137,8 +135,8 @@ const SETTINGS_MEMORY_COPY = { upload_conflict: '另一个记忆上传正在进行,请重试。', }, origins: { manual: '手动记录', imported: '导入记录', extracted: '确认提取', unknown: '手写条目' }, entryStatuses: { draft: '草稿', review_required: '待确认', active: '生效', archived: '已归档', rejected: '已拒绝', unknown: '未识别' }, backupKinds: { reset: '重置前备份', restore: '恢复前备份', save: '保存前备份' }, memoryStatuses: { ok: '本地文件已就绪', disabled: '已关闭', safe_mode: '安全模式', incognito_blocked: '隐身禁用', error: '读取失败' }, promptBlocked: { disabled: '本地记忆已关闭。', incognito: '隐身模式下不会提供本地记忆。', safeMode: 'MEMORY.md 过大,当前不会提供。', agentRead: '模型上下文读取未开启。' }, backupOversize: '备份过大,无法预览条目', previewOversize: '草稿过大,条目预览已暂停;保存前请先删减 MEMORY.md 内容。', previewTruncationMarker: '[本地记忆已按长度截断]', - }, - 'zh-TW': { + }; +const settingsMemoryCopyZhTw: MemorySettingsCopy = { intlLocale: 'zh-TW', text: zhTwText, countActive: (count, draft) => draft ? `草稿 ${count} 則生效` : `${count} 則生效`, @@ -180,8 +178,8 @@ const SETTINGS_MEMORY_COPY = { upload_conflict: '另一個記憶上傳正在進行,請重試。', }, origins: { manual: '手動記錄', imported: '匯入記錄', extracted: '確認提取', unknown: '手寫條目' }, entryStatuses: { draft: '草稿', review_required: '待確認', active: '生效', archived: '已歸檔', rejected: '已拒絕', unknown: '未識別' }, backupKinds: { reset: '重置前備份', restore: '恢復前備份', save: '儲存前備份' }, memoryStatuses: { ok: '本地檔案已就緒', disabled: '已關閉', safe_mode: '安全模式', incognito_blocked: '隱身停用', error: '讀取失敗' }, promptBlocked: { disabled: '本地記憶已關閉。', incognito: '隱身模式下不會提供本地記憶。', safeMode: 'MEMORY.md 過大,目前不會提供。', agentRead: '模型上下文讀取未開啟。' }, backupOversize: '備份過大,無法預覽條目', previewOversize: '草稿過大,條目預覽已暫停;儲存前請先刪減 MEMORY.md 內容。', previewTruncationMarker: '[本地記憶已按長度截斷]', - }, - en: { + }; +const settingsMemoryCopyEn: MemorySettingsCopy = { intlLocale: 'en-US', text: enText, countActive: (count, draft) => draft @@ -227,8 +225,9 @@ const SETTINGS_MEMORY_COPY = { upload_conflict: 'Another memory upload is in progress. Try again.', }, origins: { manual: 'Manual entry', imported: 'Imported entry', extracted: 'Confirmed extraction', unknown: 'Handwritten entry' }, entryStatuses: { draft: 'Draft', review_required: 'Needs review', active: 'Active', archived: 'Archived', rejected: 'Rejected', unknown: 'Unrecognized' }, backupKinds: { reset: 'Before reset', restore: 'Before restore', save: 'Before save' }, memoryStatuses: { ok: 'Local file ready', disabled: 'Off', safe_mode: 'Safe mode', incognito_blocked: 'Disabled in incognito', error: 'Read failed' }, promptBlocked: { disabled: 'Local memory is disabled.', incognito: 'Local memory is never added in incognito mode.', safeMode: 'MEMORY.md is too large and will not be added.', agentRead: 'Model context access is disabled.' }, backupOversize: 'Backup is too large to preview entries', previewOversize: 'The draft is too large, so entry preview is paused. Reduce MEMORY.md before saving.', previewTruncationMarker: '[Local memory truncated to the length limit]', - }, -} satisfies UiCatalog; + }; + +const SETTINGS_MEMORY_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsMemoryCopyZhCn, 'zh-TW': settingsMemoryCopyZhTw, en: settingsMemoryCopyEn })); export function getMemorySettingsCopy(locale: UiLocale): MemorySettingsCopy { return SETTINGS_MEMORY_COPY[locale]; } diff --git a/apps/desktop/src/renderer/locales/settings-navigation-copy.ts b/apps/desktop/src/renderer/locales/settings-navigation-copy.ts index 19dad1b4ebd..840373d767c 100644 --- a/apps/desktop/src/renderer/locales/settings-navigation-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-navigation-copy.ts @@ -18,7 +18,7 @@ */ import type { SettingsSection } from '@maka/core/settings'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { SettingsNavGroup } from '../settings/nav-group-summary.js'; export type SettingsNavigationCopy = { @@ -26,7 +26,7 @@ export type SettingsNavigationCopy = { sections: Record; }; -const SETTINGS_NAVIGATION_COPY_BY_LOCALE = { +const SETTINGS_NAVIGATION_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { groups: { preferences: '偏好', @@ -108,7 +108,7 @@ const SETTINGS_NAVIGATION_COPY_BY_LOCALE = { about: { label: 'About', description: 'Version, updates, and support.' }, }, }, -} satisfies UiCatalog; +})); export function getSettingsNavigationCopy(locale: UiLocale): SettingsNavigationCopy { return SETTINGS_NAVIGATION_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts index 01b48ff5f86..584569fc344 100644 --- a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts @@ -18,7 +18,7 @@ */ import type { AppIcon, ThemePalette, ThemePreference } from '@maka/core/settings'; -import type { UiCatalog, UiLocale, UiLocalePreference } from '@maka/core/ui-locale'; +import { type UiLocale, type UiLocalePreference, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type OptionCopy = { label: string; help: string }; @@ -267,9 +267,7 @@ export type SettingsPreferencesCopy = { value: string; }; }; - -const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { - 'zh-CN': { +const settingsPreferencesCopyByLocaleZhCn: SettingsPreferencesCopy = { personalization: { saveFailed: '保存失败', displayName: '显示名称', displayNameHelp: 'Maka 在聊天里会以这个名字称呼你。留空就用默认的“你”。', displayNamePlaceholder: '例如:JK', displayNameUnset: '未设置,Maka 会称呼你“你”', displayNameChange: '更改', displayNameSet: '设置', interfaceLanguage: '界面语言', interfaceLanguageHelp: '选择 Maka 界面的显示语言。切换后立即生效,重启后保持。', localeOptions: [['auto', '跟随系统'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['en', 'English']], @@ -374,8 +372,8 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { sourceCode: '源码', releaseNotes: '发行说明', }, password: { copyFailed: '复制失败', clipboardUnavailable: '剪贴板不可用或被系统拒绝。', copying: '复制中', copied: '已复制', copy: '复制', hide: '隐藏', show: '显示', value: '凭据值' }, - }, - 'zh-TW': { + }; +const settingsPreferencesCopyByLocaleZhTw: SettingsPreferencesCopy = { personalization: { saveFailed: '儲存失敗', displayName: '顯示名稱', displayNameHelp: 'Maka 在聊天裡會以這個名字稱呼你。留空就用預設的“你”。', displayNamePlaceholder: '例如:JK', displayNameUnset: '未設定,Maka 會稱呼你“你”', displayNameChange: '更改', displayNameSet: '設定', interfaceLanguage: '介面語言', interfaceLanguageHelp: '選擇 Maka 介面的顯示語言。切換後立即生效,重新啟動後仍會保留。', localeOptions: [['auto', '自動(跟隨系統)'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['en', 'English']], @@ -472,8 +470,8 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { sourceCode: '原始碼', releaseNotes: '發行說明', }, password: { copyFailed: '複製失敗', clipboardUnavailable: '剪貼簿不可用或被系統拒絕。', copying: '複製中', copied: '已複製', copy: '複製', hide: '隱藏', show: '顯示', value: '憑據值' }, - }, - en: { + }; +const settingsPreferencesCopyByLocaleEn: SettingsPreferencesCopy = { personalization: { saveFailed: 'Could not save', displayName: 'Display name', displayNameHelp: 'Maka uses this name when addressing you. Leave it blank to use “you”.', displayNamePlaceholder: 'For example: JK', displayNameUnset: 'Not set — Maka will say “you”', displayNameChange: 'Change', displayNameSet: 'Set', interfaceLanguage: 'Interface language', interfaceLanguageHelp: 'Choose the language used by Maka. Changes apply immediately and persist after restart.', localeOptions: [['auto', 'Follow system'], ['zh-CN', 'Simplified Chinese'], ['zh-TW', 'Traditional Chinese'], ['en', 'English']], assistantTone: 'Assistant tone', assistantToneHelp: 'Up to 500 characters. This changes response style only; permission and safety rules still apply. Changes save automatically.', assistantTonePlaceholder: 'For example: technically rigorous, concise, and no emoji.', }, @@ -541,8 +539,9 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { sourceCode: 'Source code', releaseNotes: 'Release notes', }, password: { copyFailed: 'Copy failed', clipboardUnavailable: 'The clipboard is unavailable or access was denied.', copying: 'Copying', copied: 'Copied', copy: 'Copy', hide: 'Hide', show: 'Show', value: 'credential value' }, - }, -} satisfies UiCatalog; + }; + +const SETTINGS_PREFERENCES_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsPreferencesCopyByLocaleZhCn, 'zh-TW': settingsPreferencesCopyByLocaleZhTw, en: settingsPreferencesCopyByLocaleEn })); export function getSettingsPreferencesCopy(locale: UiLocale): SettingsPreferencesCopy { return SETTINGS_PREFERENCES_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-projects-copy.ts b/apps/desktop/src/renderer/locales/settings-projects-copy.ts index 1de08f3fe82..3fc72456601 100644 --- a/apps/desktop/src/renderer/locales/settings-projects-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-projects-copy.ts @@ -18,7 +18,7 @@ */ import type { RuntimeHostServiceErrorCode } from '@maka/runtime-host/operator'; -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type SettingsProjectsCopy = { runtimeHost: { @@ -316,7 +316,7 @@ export type SettingsProjectsCopy = { moreActions(projectName: string): string; }; -const SETTINGS_PROJECTS_COPY_BY_LOCALE = { +const SETTINGS_PROJECTS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { runtimeHost: { title: 'Runtime Host', @@ -1363,7 +1363,7 @@ const SETTINGS_PROJECTS_COPY_BY_LOCALE = { 'Add a project folder and new tasks can start in it, with the sidebar grouping tasks by project.', moreActions: (projectName: string) => `More actions for ${projectName}`, }, -} satisfies UiCatalog; +})); export function getSettingsProjectsCopy(locale: UiLocale): SettingsProjectsCopy { return SETTINGS_PROJECTS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-shared-copy.ts b/apps/desktop/src/renderer/locales/settings-shared-copy.ts index 0b087c10e93..20c7d11bcc8 100644 --- a/apps/desktop/src/renderer/locales/settings-shared-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-shared-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type SettingsSharedCopy = { modalLabel: string; @@ -67,7 +67,7 @@ export type SettingsSharedCopy = { }; }; -const SETTINGS_SHARED_COPY_BY_LOCALE = { +const SETTINGS_SHARED_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { modalLabel: '设置', contentLabel: '设置内容', @@ -188,7 +188,7 @@ const SETTINGS_SHARED_COPY_BY_LOCALE = { reviewScheduleHelp: 'When the daily review runs, and which model writes it.', }, }, -} satisfies UiCatalog; +})); export function getSettingsSharedCopy(locale: UiLocale): SettingsSharedCopy { return SETTINGS_SHARED_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-subagents-copy.ts b/apps/desktop/src/renderer/locales/settings-subagents-copy.ts index a77ccfae235..616fcccb359 100644 --- a/apps/desktop/src/renderer/locales/settings-subagents-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-subagents-copy.ts @@ -21,7 +21,7 @@ import type { SubagentProfile } from '@maka/core/subagent-settings'; import type { ThinkingLevel } from '@maka/core/model-thinking'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type ProfileCopy = { label: string; @@ -99,7 +99,7 @@ export type SubagentSettingsCopy = { thinking: Record; }; -const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = { +const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { section: { title: '已批准的子 Agent', @@ -346,7 +346,7 @@ const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = { max: 'Maximum', }, }, -} satisfies UiCatalog; +})); export function getSubagentSettingsCopy(locale: UiLocale): SubagentSettingsCopy { return SETTINGS_SUBAGENTS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-tasks-copy.ts b/apps/desktop/src/renderer/locales/settings-tasks-copy.ts index 005e7eaa3c0..a3558f8af2c 100644 --- a/apps/desktop/src/renderer/locales/settings-tasks-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-tasks-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Restoring and deleting a single task speak through the rail's own row @@ -85,7 +85,7 @@ export type SettingsTasksCopy = { emptyBody: string; }; -const SETTINGS_TASKS_COPY_BY_LOCALE = { +const SETTINGS_TASKS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { listAria: '已归档任务', noProject: '无项目', @@ -231,7 +231,7 @@ const SETTINGS_TASKS_COPY_BY_LOCALE = { emptyTitle: 'Nothing archived', emptyBody: 'Archive a task from the rail to restore or permanently delete it here.', }, -} satisfies UiCatalog; +})); export function getSettingsTasksCopy(locale: UiLocale): SettingsTasksCopy { return SETTINGS_TASKS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-test-result-copy.ts b/apps/desktop/src/renderer/locales/settings-test-result-copy.ts index 46c54c86d09..2ac7a296ce4 100644 --- a/apps/desktop/src/renderer/locales/settings-test-result-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-test-result-copy.ts @@ -20,7 +20,7 @@ import type { SettingsTestResult, SettingsTestResultCode } from '@maka/core/settings'; import type { BotTestErrorCode } from '@maka/runtime/bots'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { lookupCopy } from '@maka/core/ui-locale'; type SettingsTestResultCopy = { @@ -48,9 +48,7 @@ type SettingsTestResultCopy = { >; }; }; - -const COPY = { - 'zh-CN': { +const copyZhCn: SettingsTestResultCopy = { proxy: { reachable: (endpoint, location) => ["代理配置有效", endpoint, location].filter(Boolean).join(" · "), @@ -84,8 +82,8 @@ const COPY = { wechat_ilink_credentials_incomplete: '请先完成微信扫码登录,保存 iLink bot token 与 base URL。', }, }, - }, - 'zh-TW': { + }; +const copyZhTw: SettingsTestResultCopy = { proxy: { reachable: (endpoint, location) => ["代理設定有效", endpoint, location].filter(Boolean).join(" · "), @@ -119,8 +117,8 @@ const COPY = { wechat_ilink_credentials_incomplete: '請先完成微信掃碼登入,儲存 iLink bot token 與 base URL。', }, }, - }, - en: { + }; +const copyEn: SettingsTestResultCopy = { proxy: { reachable: (endpoint, location) => ["The proxy is reachable", endpoint, location] @@ -161,8 +159,9 @@ const COPY = { wechat_ilink_credentials_incomplete: 'Complete WeChat QR sign-in first to save the iLink bot token and base URL.', }, }, - }, -} satisfies UiCatalog; + }; + +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); export function settingsTestResultMessage( result: SettingsTestResult, diff --git a/apps/desktop/src/renderer/locales/settings-usage-copy.ts b/apps/desktop/src/renderer/locales/settings-usage-copy.ts index 8615eb1c048..82e9ce29f7b 100644 --- a/apps/desktop/src/renderer/locales/settings-usage-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-usage-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type UsageSettingsCopy = { staleTitle: string; staleBody: string; loadFailed: string; capacityBody: string; retainedBody: string; @@ -38,9 +38,7 @@ export type UsageSettingsCopy = { toolEmptyTitle: string; toolEmptyBody: string; pricingEmptyBody: string; }; }; - -const SETTINGS_USAGE_COPY = { - 'zh-CN': { +const settingsUsageCopyZhCn: UsageSettingsCopy = { staleTitle: "统计已更新", staleBody: "当前显示的是之前的完整结果。请刷新后继续浏览。", loadFailed: "无法加载使用统计", capacityBody: "统计结果超出显示容量,请求未返回任何部分数据。", retainedBody: "当前仍显示上次成功加载的结果,新查询尚未生效。", saveFailed: '保存使用统计设置失败', toolbarAria: '使用统计范围与刷新', rangeAria: '使用统计时间范围', ranges: ['24h', '7天', '30天', '全部'], refreshingAria: '正在刷新使用统计', refreshAria: '刷新使用统计', summaryAria: '使用统计汇总指标', totalRequests: '模型调用', totalCost: '总费用', costHelp: '以模型供应商最终结算为准', @@ -64,8 +62,8 @@ const SETTINGS_USAGE_COPY = { toolEmptyTitle: '暂无工具调用', toolEmptyBody: '智能体调用工具后,这里会按工具聚合调用次数、成功、错误与平均耗时。', pricingEmptyBody: '未配置定价覆盖时,费用按内置模型定价表结算;在此可为特定模型登记自定义价格。', }, - }, - 'zh-TW': { + }; +const settingsUsageCopyZhTw: UsageSettingsCopy = { staleTitle: "統計已更新", staleBody: "目前顯示先前的完整結果。請重新整理後繼續瀏覽。", loadFailed: "無法載入使用統計", capacityBody: "統計結果超出顯示容量,請求未傳回任何部分資料。", retainedBody: "目前仍顯示上次成功載入的結果,新查詢尚未生效。", tokenTooltip: { total: '總計', input: '輸入', output: '輸出', cached: '快取', new: '新', hit: '命中', created: '建立' }, saveFailed: '儲存使用統計設定失敗', toolbarAria: '使用統計範圍與重新整理', rangeAria: '使用統計時間範圍', ranges: ['24h', '7天', '30天', '全部'], @@ -89,8 +87,8 @@ const SETTINGS_USAGE_COPY = { toolEmptyTitle: '暫無工具呼叫', toolEmptyBody: '智慧體呼叫工具後,這裡會按工具聚合呼叫次數、成功、錯誤與平均耗時。', pricingEmptyBody: '未設定定價覆蓋時,費用按內建模型定價表結算;在此可為特定模型登記自訂價格。', }, - }, - en: { + }; +const settingsUsageCopyEn: UsageSettingsCopy = { staleTitle: 'Usage has changed', staleBody: 'The previous complete result is still shown. Refresh to continue browsing.', loadFailed: 'Unable to load Usage', capacityBody: 'This complete result exceeds the display capacity. No partial result was loaded.', retainedBody: 'The last successfully loaded result is still shown; the new query has not taken effect.', saveFailed: 'Failed to save usage settings', toolbarAria: 'Usage range and refresh', rangeAria: 'Usage time range', ranges: ['24h', '7 days', '30 days', 'All'], refreshingAria: 'Refreshing usage', refreshAria: 'Refresh usage', summaryAria: 'Usage summary metrics', totalRequests: 'Model calls', totalCost: 'Total cost', costHelp: 'Final billing is determined by the model provider', @@ -114,8 +112,9 @@ const SETTINGS_USAGE_COPY = { toolEmptyTitle: 'No tool calls', toolEmptyBody: 'After an agent calls a tool, calls, successes, errors, and average duration appear here by tool.', pricingEmptyBody: 'Without pricing overrides, costs use the built-in model pricing table. Add custom prices here for specific models.', }, - }, -} satisfies UiCatalog; + }; + +const SETTINGS_USAGE_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsUsageCopyZhCn, 'zh-TW': settingsUsageCopyZhTw, en: settingsUsageCopyEn })); export function getUsageSettingsCopy(locale: UiLocale): UsageSettingsCopy { return SETTINGS_USAGE_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-web-search-copy.ts b/apps/desktop/src/renderer/locales/settings-web-search-copy.ts index 0cd8730afff..68aab496bad 100644 --- a/apps/desktop/src/renderer/locales/settings-web-search-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-web-search-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { WebSearchCredentialStatus, WebSearchErrorReason } from '@maka/core/web-search'; @@ -37,7 +37,7 @@ export type WebSearchSettingsCopy = { errors: Record; }; -const SETTINGS_WEB_SEARCH_COPY = { +const SETTINGS_WEB_SEARCH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { saveFailed: '保存联网搜索设置失败', saveStatusFailed: '保存联网搜索状态失败', keySaved: '已保存 Tavily 密钥', keySavedDetail: '可点击「测试」做一次真实请求验证。', credentialsCleared: '已清空 Tavily 凭据', credentialsClearedDetail: '联网搜索已自动关闭。', credentialValid: 'Tavily 凭据可用', resultCount: (count) => `返回 ${count} 条结果。`, @@ -89,6 +89,6 @@ const SETTINGS_WEB_SEARCH_COPY = { sources: { model: 'Source: current model connection', envWithSaved: 'Source: environment variable (saved key available as backup)', env: 'Source: environment variable', saved: 'Source: key saved on this device', none: 'Source: not configured' }, errors: { invalid_query: 'Enter a valid search query.', incognito_active: 'Web search is unavailable in incognito mode.', not_configured: 'The selected search source is not configured.', invalid_credentials: 'The search provider rejected the current credential. Update it and try again.', rate_limited: 'The search provider is receiving too many requests. Try again later.', network_error: 'The network request failed. Check your connection and try again.', timeout: 'The search request timed out. Try again.', unsupported_provider: 'The current model does not support hosted search, or Maka has not implemented its protocol yet. Select Tavily to continue.', experimental_disabled: 'The experimental web search feature is currently disabled.' }, }, -} satisfies UiCatalog; +})); export function getWebSearchSettingsCopy(locale: UiLocale): WebSearchSettingsCopy { return SETTINGS_WEB_SEARCH_COPY[locale]; } diff --git a/apps/desktop/src/renderer/locales/shell-copy.ts b/apps/desktop/src/renderer/locales/shell-copy.ts index d5e16025214..5fff0cd9acf 100644 --- a/apps/desktop/src/renderer/locales/shell-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiCatalog, type UiLocale, lookupCopy } from '@maka/core/ui-locale'; +import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { type PermissionMode } from '@maka/core/permission'; @@ -744,9 +744,7 @@ const EN_SETTINGS_SECTIONS: Record = { health: 'Health', about: 'About', }; - -const SHELL_COPY_BY_LOCALE = { - 'zh-CN': { +const shellCopyByLocaleZhCn: ShellCopy = { navigation: { settings: '设置' }, actions: { retry: '重试' }, paths: { @@ -1269,8 +1267,8 @@ const SHELL_COPY_BY_LOCALE = { graphHistoryDescription: '请在 Agent Graph 面板的运行轮次菜单中查看历史记录。', resizeWorkbar: '调整任务工作栏宽度', }, - }, - 'zh-TW': { + }; +const shellCopyByLocaleZhTw: ShellCopy = { navigation: { settings: '設定' }, actions: { retry: '重試' }, paths: { @@ -1793,8 +1791,8 @@ const SHELL_COPY_BY_LOCALE = { graphHistoryDescription: '請在 Agent Graph 面板的執行輪次選單中檢視歷史記錄。', resizeWorkbar: '調整任務工作欄寬度', }, - }, - en: { + }; +const shellCopyByLocaleEn: ShellCopy = { navigation: { settings: 'Settings' }, actions: { retry: 'Retry' }, paths: { @@ -2366,8 +2364,9 @@ const SHELL_COPY_BY_LOCALE = { graphHistoryDescription: 'Use the run menu in the Agent Graph panel to inspect history.', resizeWorkbar: 'Resize task workbar', }, - }, -} satisfies UiCatalog; + }; + +const SHELL_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': shellCopyByLocaleZhCn, 'zh-TW': shellCopyByLocaleZhTw, en: shellCopyByLocaleEn })); export function getShellCopy(locale: UiLocale): ShellCopy { return SHELL_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts index 17fb8059084..85d7bfd71eb 100644 --- a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type WidenCopy = T extends string ? string @@ -251,11 +251,11 @@ const enCopy: ShellRemainingCopy = { }, }; -const COPY = { +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': zhCopy, 'zh-TW': zhTwCopy, en: enCopy, -} satisfies UiCatalog; +})); export function getShellRemainingCopy(locale: UiLocale): ShellRemainingCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/storage-usage-copy.ts b/apps/desktop/src/renderer/locales/storage-usage-copy.ts index 387cb413612..dd86634a37d 100644 --- a/apps/desktop/src/renderer/locales/storage-usage-copy.ts +++ b/apps/desktop/src/renderer/locales/storage-usage-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { StorageUsageKind } from '@maka/runtime-host/protocol'; export type StorageUsageCopy = { @@ -43,7 +43,7 @@ export type StorageUsageCopy = { taskSizeNote: string; }; -const STORAGE_USAGE_COPY = { +const STORAGE_USAGE_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { title: '存储占用', help: '当前运行时主机的数据占用。此处只读,不会删除或压缩任何内容。', @@ -140,7 +140,7 @@ const STORAGE_USAGE_COPY = { taskSizeNote: 'Task sizes are estimates. Offloaded context is counted for every task that references it, so shared content is counted more than once. Usage history is kept after a task is deleted.', }, -} satisfies UiCatalog; +})); export function getStorageUsageCopy(locale: UiLocale): StorageUsageCopy { return STORAGE_USAGE_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/task-readiness-copy.ts b/apps/desktop/src/renderer/locales/task-readiness-copy.ts index 0c66a865158..3f8fcc34ace 100644 --- a/apps/desktop/src/renderer/locales/task-readiness-copy.ts +++ b/apps/desktop/src/renderer/locales/task-readiness-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface TaskReadinessCopy { readonly runtime: { @@ -32,7 +32,7 @@ export interface TaskReadinessCopy { }; } -const TASK_READINESS_COPY = { +const TASK_READINESS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { runtime: { title: 'Maka 运行服务暂时不可用。', @@ -69,7 +69,7 @@ const TASK_READINESS_COPY = { actionLabel: { workspace_picker: 'Choose workspace', retry: 'Check again' }, }, }, -} satisfies UiCatalog; +})); export function getTaskReadinessCopy(locale: UiLocale): TaskReadinessCopy { return TASK_READINESS_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/work-board-error-copy.ts b/apps/desktop/src/renderer/locales/work-board-error-copy.ts index c6a28da90a6..d1542683a93 100644 --- a/apps/desktop/src/renderer/locales/work-board-error-copy.ts +++ b/apps/desktop/src/renderer/locales/work-board-error-copy.ts @@ -17,13 +17,13 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { lookupCopy } from '@maka/core/ui-locale'; import type { WorkBoardErrorCode } from '../../shared/work-board-ipc.js'; type WorkBoardErrorCopy = Record; -const WORK_BOARD_ERROR_COPY = { +const WORK_BOARD_ERROR_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { invalid_input: '这项操作无效,请检查后重试', not_found: '这条事项已不存在', @@ -48,7 +48,7 @@ const WORK_BOARD_ERROR_COPY = { must_archive_first: 'Archive this item before deleting it.', unknown: 'The action failed. Try again later.', }, -} satisfies UiCatalog; +})); export function getWorkBoardErrorCopy(locale: UiLocale): WorkBoardErrorCopy { return WORK_BOARD_ERROR_COPY[locale]; diff --git a/apps/desktop/src/renderer/settings/provider-display-copy.ts b/apps/desktop/src/renderer/settings/provider-display-copy.ts index 766cffa4f4a..44b8b40c5b3 100644 --- a/apps/desktop/src/renderer/settings/provider-display-copy.ts +++ b/apps/desktop/src/renderer/settings/provider-display-copy.ts @@ -19,7 +19,7 @@ import type { ProviderType } from '@maka/core/llm-connections'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Pure-data provider introduction copy, localized zh / en. @@ -47,327 +47,327 @@ export interface ProviderCopy { badge?: string; } -export const UNKNOWN_PROVIDER_DESCRIPTION = { +export const UNKNOWN_PROVIDER_DESCRIPTION = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': '该 provider 在当前版本未注册。', 'zh-TW': '該 provider 在目前版本未註冊。', en: 'This provider is not registered in the current build.', -} satisfies UiCatalog; +})); export const PROVIDER_DISPLAY_COPY = { - 'kimi-coding-plan': { - 'zh-CN': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 相容', badge: 'Coding' }, - en: { name: 'Kimi Coding Plan', description: 'Moonshot · Anthropic-compatible', badge: 'Coding' }, - }, - 'minimax-coding-plan': { - 'zh-CN': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 相容', badge: 'Coding' }, - en: { name: 'MiniMax Coding Plan', description: 'MiniMax coding plan · Anthropic-compatible', badge: 'Coding' }, - }, - deepseek: { - 'zh-CN': { name: 'DeepSeek', description: 'DeepSeek 官方接入', badge: 'API' }, - 'zh-TW': { name: 'DeepSeek', description: 'DeepSeek 官方 API 連線', badge: 'API' }, - en: { name: 'DeepSeek', description: 'Official DeepSeek API access.', badge: 'API' }, - }, - moonshot: { - 'zh-CN': { name: 'Moonshot', description: 'Moonshot 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Moonshot', description: 'Moonshot 官方 API 連線', badge: 'API' }, - en: { name: 'Moonshot', description: 'Official Moonshot API access.', badge: 'API' }, - }, - 'moonshot-global': { - 'zh-CN': { name: 'Moonshot 国际版', description: 'Moonshot 国际版官方 API 接入', badge: 'API' }, - 'zh-TW': { name: 'Moonshot 國際版', description: 'Moonshot 國際版官方 API 連線', badge: 'API' }, - en: { name: 'Moonshot Global', description: 'Official Moonshot international API access.', badge: 'API' }, - }, - 'zai-coding-plan': { - 'zh-CN': { name: 'Z.AI Coding Plan', description: '智谱 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Z.AI Coding Plan', description: '智譜 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Z.AI Coding Plan', description: 'Zhipu · OpenAI-compatible', badge: 'Coding' }, - }, - MiniMax: { - 'zh-CN': { name: 'MiniMax', description: 'MiniMax · Anthropic 兼容', badge: 'API' }, - 'zh-TW': { name: 'MiniMax', description: 'MiniMax · Anthropic 相容', badge: 'API' }, - en: { name: 'MiniMax', description: 'MiniMax · Anthropic-compatible', badge: 'API' }, - }, - 'MiniMax-cn': { - 'zh-CN': { name: 'MiniMax 中国站', description: 'MiniMax 中国站 · Anthropic 兼容', badge: 'API' }, - 'zh-TW': { name: 'MiniMax 中國站', description: 'MiniMax 中國站 · Anthropic 相容', badge: 'API' }, - en: { name: 'MiniMax China', description: 'MiniMax China · Anthropic-compatible', badge: 'API' }, - }, - siliconflow: { - 'zh-CN': { name: 'SiliconFlow', description: '硅基流动多模型 API,支持精确模型 ID。', badge: '聚合' }, - 'zh-TW': { name: 'SiliconFlow', description: '矽基流動多模型 API,支援精確模型 ID。', badge: '聚合' }, - en: { name: 'SiliconFlow', description: 'Hosted multi-model API with exact upstream model ids.', badge: 'Aggregator' }, - }, - anthropic: { - 'zh-CN': { name: 'Anthropic', description: 'Anthropic 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Anthropic', description: 'Anthropic 官方 API 連線', badge: 'API' }, - en: { name: 'Anthropic', description: 'Official Anthropic API access.', badge: 'API' }, - }, - openai: { - 'zh-CN': { name: 'OpenAI', description: 'OpenAI 官方接入', badge: 'API' }, - 'zh-TW': { name: 'OpenAI', description: 'OpenAI 官方 API 連線', badge: 'API' }, - en: { name: 'OpenAI', description: 'Official OpenAI API access.', badge: 'API' }, - }, - google: { - 'zh-CN': { name: 'Google Gemini', description: 'Google AI Studio 接入', badge: 'API' }, - 'zh-TW': { name: 'Google Gemini', description: 'Google AI Studio API 連線', badge: 'API' }, - en: { name: 'Google Gemini', description: 'Google AI Studio API access.', badge: 'API' }, - }, - xai: { - 'zh-CN': { name: 'xAI', description: 'xAI 官方接入,Grok 系列模型', badge: 'API' }, - 'zh-TW': { name: 'xAI', description: 'xAI 官方 API 連線,支援 Grok 系列模型', badge: 'API' }, - en: { name: 'xAI', description: 'Official xAI API access for Grok models.', badge: 'API' }, - }, - zai: { - 'zh-CN': { name: 'Z.AI', description: '智谱官方接入,GLM 系列模型', badge: 'API' }, - 'zh-TW': { name: 'Z.AI', description: '智譜官方 API 連線,支援 GLM 系列模型', badge: 'API' }, - en: { name: 'Z.AI', description: 'Official Z.AI API access for GLM models.', badge: 'API' }, - }, - xiaomi: { - 'zh-CN': { name: 'Xiaomi', description: '小米官方接入,MiMo 系列模型', badge: 'API' }, - 'zh-TW': { name: 'Xiaomi', description: '小米官方 API 連線,支援 MiMo 系列模型', badge: 'API' }, - en: { name: 'Xiaomi', description: 'Official Xiaomi API access for MiMo models.', badge: 'API' }, - }, - 'xiaomi-token-plan-cn': { - 'zh-CN': { name: 'Xiaomi Token Plan 中国', description: '小米 MiMo Token Plan 订阅 · 中国 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 中國', description: '小米 MiMo Token Plan 訂閱 · 中國 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (China)', description: 'Xiaomi MiMo Token Plan subscription (China) for coding tools.', badge: 'Token' }, - }, - 'xiaomi-token-plan-sgp': { - 'zh-CN': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 订阅 · 新加坡 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 訂閱 · 新加坡 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (Singapore)', description: 'Xiaomi MiMo Token Plan subscription (Singapore) for coding tools.', badge: 'Token' }, - }, - 'xiaomi-token-plan-ams': { - 'zh-CN': { name: 'Xiaomi Token Plan 欧洲', description: '小米 MiMo Token Plan 订阅 · 欧洲 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 歐洲', description: '小米 MiMo Token Plan 訂閱 · 歐洲 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (Europe)', description: 'Xiaomi MiMo Token Plan subscription (Europe) for coding tools.', badge: 'Token' }, - }, - cerebras: { - 'zh-CN': { name: 'Cerebras', description: '高速推理托管开源模型', badge: 'API' }, - 'zh-TW': { name: 'Cerebras', description: '高速推理託管開源模型', badge: 'API' }, - en: { name: 'Cerebras', description: 'Fast hosted open-model inference.', badge: 'API' }, - }, - mistral: { - 'zh-CN': { name: 'Mistral', description: 'Mistral 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Mistral', description: 'Mistral 官方 API 連線', badge: 'API' }, - en: { name: 'Mistral', description: 'Official Mistral API access.', badge: 'API' }, - }, - togetherai: { - 'zh-CN': { name: 'Together AI', description: '托管开源模型 API', badge: 'API' }, - 'zh-TW': { name: 'Together AI', description: '託管開源模型 API', badge: 'API' }, - en: { name: 'Together AI', description: 'Hosted open models over one API.', badge: 'API' }, - }, - ollama: { - 'zh-CN': { name: 'Ollama', description: '本机运行 · 离线可用', badge: 'Local' }, - 'zh-TW': { name: 'Ollama', description: '本機執行 · 離線可用', badge: 'Local' }, - en: { name: 'Ollama', description: 'Runs locally · works offline', badge: 'Local' }, - }, - 'lm-studio': { - 'zh-CN': { name: 'LM Studio', description: '本机 LM Studio 服务 · 离线可用', badge: 'Local' }, - 'zh-TW': { name: 'LM Studio', description: '本機 LM Studio 服務 · 離線可用', badge: 'Local' }, - en: { name: 'LM Studio', description: 'Local models served by LM Studio.', badge: 'Local' }, - }, - localai: { - 'zh-CN': { name: 'LocalAI', description: '本机 LocalAI 服务,可选密钥保护', badge: 'Local' }, - 'zh-TW': { name: 'LocalAI', description: '本機 LocalAI 服務,可選金鑰保護', badge: 'Local' }, - en: { name: 'LocalAI', description: 'Local models served by LocalAI, with optional API key.', badge: 'Local' }, - }, - custom: { - 'zh-CN': { name: '自定义连接', description: '中转站、代理服务或自部署网关,支持 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可按模型选择协议。', badge: '自定义' }, - 'zh-TW': { name: '自訂連線', description: '中轉站、代理服務或自部署閘道器,支援 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可依模型選擇協定。', badge: '自訂' }, - en: { name: 'Custom connection', description: 'Relay, proxy, or self-hosted gateway speaking OpenAI Chat, OpenAI Responses, or Anthropic Messages, chosen per model.', badge: 'Custom' }, - }, - 'fireworks-ai': { - 'zh-CN': { name: 'Fireworks AI', description: 'Serverless 开源模型托管', badge: 'API' }, - 'zh-TW': { name: 'Fireworks AI', description: 'Serverless 開源模型託管', badge: 'API' }, - en: { name: 'Fireworks AI', description: 'Serverless open models with exact Fireworks model paths.', badge: 'API' }, - }, - nvidia: { - 'zh-CN': { name: 'NVIDIA', description: 'NVIDIA 官方托管模型接入', badge: 'API' }, - 'zh-TW': { name: 'NVIDIA', description: 'NVIDIA 官方託管模型服務', badge: 'API' }, - en: { name: 'NVIDIA', description: 'NVIDIA-hosted models API access.', badge: 'API' }, - }, - 'tencent-tokenhub': { - 'zh-CN': { name: 'Tencent TokenHub', description: '腾讯云 TokenHub 按量接入,混元等模型', badge: 'API' }, - 'zh-TW': { name: 'Tencent TokenHub', description: '騰訊雲 TokenHub 按量計費模型服務,包含混元等模型', badge: 'API' }, - en: { name: 'Tencent TokenHub', description: 'Tencent Cloud TokenHub pay-as-you-go access.', badge: 'API' }, - }, - stepfun: { - 'zh-CN': { name: 'StepFun 中国站', description: '阶跃星辰官方接入 · 中国站', badge: 'API' }, - 'zh-TW': { name: 'StepFun 中國站', description: '階躍星辰官方 API 連線 · 中國站', badge: 'API' }, - en: { name: 'StepFun (China)', description: 'Official StepFun China API access.', badge: 'API' }, - }, - 'tencent-coding-plan': { - 'zh-CN': { name: 'Tencent Coding Plan', description: '腾讯云 Coding 套餐 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Tencent Coding Plan', description: '騰訊雲 Coding 套餐 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Tencent Coding Plan', description: 'Tencent Cloud coding plan · OpenAI-compatible', badge: 'Coding' }, - }, - 'stepfun-ai': { - 'zh-CN': { name: 'StepFun 国际站', description: '阶跃星辰官方接入 · 国际站', badge: 'API' }, - 'zh-TW': { name: 'StepFun 國際站', description: '階躍星辰官方 API 連線 · 國際站', badge: 'API' }, - en: { name: 'StepFun (Global)', description: 'Official StepFun Global API access.', badge: 'API' }, - }, - 'volcengine-ark': { - 'zh-CN': { name: '火山方舟', description: '火山引擎官方接入,豆包等模型', badge: 'API' }, - 'zh-TW': { name: '火山方舟', description: '火山引擎官方 API 連線,包含豆包等模型', badge: 'API' }, - en: { name: 'Volcengine Ark (China)', description: 'Volcengine Ark direct API access in China.', badge: 'API' }, - }, - 'volcengine-coding-plan': { - 'zh-CN': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 订阅 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 訂閱 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Volcengine Ark Coding Plan (China)', description: 'Volcengine Ark coding subscription · OpenAI-compatible', badge: 'Coding' }, - }, - 'volcengine-agent-plan': { - 'zh-CN': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 订阅 · Responses API', badge: 'Agent' }, - 'zh-TW': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 訂閱 · Responses API', badge: 'Agent' }, - en: { name: 'Volcengine Ark Agent Plan (China)', description: 'Volcengine Ark agent subscription · Responses API', badge: 'Agent' }, - }, - 'tencent-token-plan': { - 'zh-CN': { name: 'Tencent Token Plan', description: '腾讯云 Token 套餐,个人智能体与编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Tencent Token Plan', description: '騰訊雲 Token 套餐,個人智慧體與編碼工具', badge: 'Token' }, - en: { name: 'Tencent Token Plan', description: 'Tencent Cloud token plan for personal agents and coding tools.', badge: 'Token' }, - }, - 'stepfun-step-plan': { - 'zh-CN': { name: 'StepFun Step Plan 中国站', description: '阶跃星辰订阅套餐 · 中国站', badge: 'Plan' }, - 'zh-TW': { name: 'StepFun Step Plan 中國站', description: '階躍星辰訂閱套餐 · 中國站', badge: 'Plan' }, - en: { name: 'StepFun Step Plan (China)', description: 'StepFun China subscription for coding and agent tools.', badge: 'Plan' }, - }, - deepinfra: { - 'zh-CN': { name: 'DeepInfra', description: '开源模型托管推理 · OpenAI 兼容', badge: 'API' }, - 'zh-TW': { name: 'DeepInfra', description: '開源模型託管推理 · OpenAI 相容', badge: 'API' }, - en: { name: 'DeepInfra', description: 'Hosted open-model inference · OpenAI-compatible', badge: 'API' }, - }, - cohere: { - 'zh-CN': { name: 'Cohere', description: 'Cohere 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Cohere', description: 'Cohere 官方 API 連線', badge: 'API' }, - en: { name: 'Cohere', description: 'Official Cohere Chat API access.', badge: 'API' }, - }, - vercel: { - 'zh-CN': { name: 'Vercel AI Gateway', description: '一个密钥接入多家托管模型', badge: '网关' }, - 'zh-TW': { name: 'Vercel AI Gateway', description: '使用一組金鑰連線多家託管模型', badge: '閘道器' }, - en: { name: 'Vercel AI Gateway', description: 'One API key for hosted models with exact creator/model ids.', badge: 'Gateway' }, - }, - 'stepfun-ai-step-plan': { - 'zh-CN': { name: 'StepFun Step Plan 国际站', description: '阶跃星辰订阅套餐 · 国际站', badge: 'Plan' }, - 'zh-TW': { name: 'StepFun Step Plan 國際站', description: '階躍星辰訂閱套餐 · 國際站', badge: 'Plan' }, - en: { name: 'StepFun Step Plan (Global)', description: 'StepFun Global subscription for coding and agent tools.', badge: 'Plan' }, - }, - 'cloudflare-workers-ai': { - 'zh-CN': { name: 'Cloudflare Workers AI', description: 'Cloudflare 托管模型,账户级接入', badge: 'API' }, - 'zh-TW': { name: 'Cloudflare Workers AI', description: 'Cloudflare 託管模型,使用帳號層級連線', badge: 'API' }, - en: { name: 'Cloudflare Workers AI', description: 'Cloudflare-hosted models over the account-scoped API.', badge: 'API' }, - }, - huggingface: { - 'zh-CN': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家托管模型', badge: '路由' }, - 'zh-TW': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家託管模型', badge: '路由' }, - en: { name: 'Hugging Face', description: 'Inference Providers router across hosted models.', badge: 'Router' }, - }, - 'ollama-cloud': { - 'zh-CN': { name: 'Ollama Cloud', description: 'Ollama 官方云端托管模型', badge: 'API' }, - 'zh-TW': { name: 'Ollama Cloud', description: 'Ollama 官方雲端託管模型', badge: 'API' }, - en: { name: 'Ollama Cloud', description: 'Ollama-hosted cloud models over the official remote API.', badge: 'API' }, - }, - zenmux: { - 'zh-CN': { name: 'ZenMux', description: '模型路由网关,一个密钥接入多家模型', badge: '网关' }, - 'zh-TW': { name: 'ZenMux', description: '模型路由閘道器,使用一組金鑰連線多家模型', badge: '閘道器' }, - en: { name: 'ZenMux', description: 'One API key for routed models with exact creator/model ids.', badge: 'Gateway' }, - }, - opencode: { - 'zh-CN': { name: 'OpenCode Zen', description: '面向编码智能体的按量模型精选', badge: 'Plan' }, - 'zh-TW': { name: 'OpenCode Zen', description: '面向編碼智慧體的按量模型精選', badge: 'Plan' }, - en: { name: 'OpenCode Zen', description: 'Curated pay-as-you-go models for coding agents.', badge: 'Plan' }, - }, - 'opencode-go': { - 'zh-CN': { name: 'OpenCode Go', description: '低价订阅制的开源编码模型精选', badge: 'Plan' }, - 'zh-TW': { name: 'OpenCode Go', description: '低價訂閱制的開源編碼模型精選', badge: 'Plan' }, - en: { name: 'OpenCode Go', description: 'Low-cost subscription to curated open coding models.', badge: 'Plan' }, - }, - 'opencode-free': { - 'zh-CN': { name: 'OpenCode Free', description: '已停用:OpenCode 免费服务仅限其官方客户端使用。', badge: 'Free' }, - 'zh-TW': { name: 'OpenCode Free', description: '已停用:OpenCode 免費服務僅限其官方用戶端使用。', badge: 'Free' }, - en: { name: 'OpenCode Free', description: 'Retired: OpenCode restricts its free tier to its own client.', badge: 'Free' }, - }, - commandcode: { - 'zh-CN': { name: 'Command Code', description: '使用 Command Code 套餐额度,连接后自动获取模型。', badge: 'Coding' }, - 'zh-TW': { name: 'Command Code', description: '使用 Command Code 方案額度,連線後自動取得模型。', badge: 'Coding' }, - en: { name: 'Command Code', description: 'Use your Command Code plan credits. Models are fetched when you connect.', badge: 'Coding' }, - }, + 'kimi-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 相容', badge: 'Coding' }, + en: { name: 'Kimi Coding Plan', description: 'Moonshot · Anthropic-compatible', badge: 'Coding' }, + })), + 'minimax-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 相容', badge: 'Coding' }, + en: { name: 'MiniMax Coding Plan', description: 'MiniMax coding plan · Anthropic-compatible', badge: 'Coding' }, + })), + deepseek: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'DeepSeek', description: 'DeepSeek 官方接入', badge: 'API' }, + 'zh-TW': { name: 'DeepSeek', description: 'DeepSeek 官方 API 連線', badge: 'API' }, + en: { name: 'DeepSeek', description: 'Official DeepSeek API access.', badge: 'API' }, + })), + moonshot: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Moonshot', description: 'Moonshot 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Moonshot', description: 'Moonshot 官方 API 連線', badge: 'API' }, + en: { name: 'Moonshot', description: 'Official Moonshot API access.', badge: 'API' }, + })), + 'moonshot-global': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Moonshot 国际版', description: 'Moonshot 国际版官方 API 接入', badge: 'API' }, + 'zh-TW': { name: 'Moonshot 國際版', description: 'Moonshot 國際版官方 API 連線', badge: 'API' }, + en: { name: 'Moonshot Global', description: 'Official Moonshot international API access.', badge: 'API' }, + })), + 'zai-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Z.AI Coding Plan', description: '智谱 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Z.AI Coding Plan', description: '智譜 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Z.AI Coding Plan', description: 'Zhipu · OpenAI-compatible', badge: 'Coding' }, + })), + MiniMax: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'MiniMax', description: 'MiniMax · Anthropic 兼容', badge: 'API' }, + 'zh-TW': { name: 'MiniMax', description: 'MiniMax · Anthropic 相容', badge: 'API' }, + en: { name: 'MiniMax', description: 'MiniMax · Anthropic-compatible', badge: 'API' }, + })), + 'MiniMax-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'MiniMax 中国站', description: 'MiniMax 中国站 · Anthropic 兼容', badge: 'API' }, + 'zh-TW': { name: 'MiniMax 中國站', description: 'MiniMax 中國站 · Anthropic 相容', badge: 'API' }, + en: { name: 'MiniMax China', description: 'MiniMax China · Anthropic-compatible', badge: 'API' }, + })), + siliconflow: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'SiliconFlow', description: '硅基流动多模型 API,支持精确模型 ID。', badge: '聚合' }, + 'zh-TW': { name: 'SiliconFlow', description: '矽基流動多模型 API,支援精確模型 ID。', badge: '聚合' }, + en: { name: 'SiliconFlow', description: 'Hosted multi-model API with exact upstream model ids.', badge: 'Aggregator' }, + })), + anthropic: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Anthropic', description: 'Anthropic 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Anthropic', description: 'Anthropic 官方 API 連線', badge: 'API' }, + en: { name: 'Anthropic', description: 'Official Anthropic API access.', badge: 'API' }, + })), + openai: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenAI', description: 'OpenAI 官方接入', badge: 'API' }, + 'zh-TW': { name: 'OpenAI', description: 'OpenAI 官方 API 連線', badge: 'API' }, + en: { name: 'OpenAI', description: 'Official OpenAI API access.', badge: 'API' }, + })), + google: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Google Gemini', description: 'Google AI Studio 接入', badge: 'API' }, + 'zh-TW': { name: 'Google Gemini', description: 'Google AI Studio API 連線', badge: 'API' }, + en: { name: 'Google Gemini', description: 'Google AI Studio API access.', badge: 'API' }, + })), + xai: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'xAI', description: 'xAI 官方接入,Grok 系列模型', badge: 'API' }, + 'zh-TW': { name: 'xAI', description: 'xAI 官方 API 連線,支援 Grok 系列模型', badge: 'API' }, + en: { name: 'xAI', description: 'Official xAI API access for Grok models.', badge: 'API' }, + })), + zai: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Z.AI', description: '智谱官方接入,GLM 系列模型', badge: 'API' }, + 'zh-TW': { name: 'Z.AI', description: '智譜官方 API 連線,支援 GLM 系列模型', badge: 'API' }, + en: { name: 'Z.AI', description: 'Official Z.AI API access for GLM models.', badge: 'API' }, + })), + xiaomi: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Xiaomi', description: '小米官方接入,MiMo 系列模型', badge: 'API' }, + 'zh-TW': { name: 'Xiaomi', description: '小米官方 API 連線,支援 MiMo 系列模型', badge: 'API' }, + en: { name: 'Xiaomi', description: 'Official Xiaomi API access for MiMo models.', badge: 'API' }, + })), + 'xiaomi-token-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Xiaomi Token Plan 中国', description: '小米 MiMo Token Plan 订阅 · 中国 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 中國', description: '小米 MiMo Token Plan 訂閱 · 中國 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (China)', description: 'Xiaomi MiMo Token Plan subscription (China) for coding tools.', badge: 'Token' }, + })), + 'xiaomi-token-plan-sgp': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 订阅 · 新加坡 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 訂閱 · 新加坡 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (Singapore)', description: 'Xiaomi MiMo Token Plan subscription (Singapore) for coding tools.', badge: 'Token' }, + })), + 'xiaomi-token-plan-ams': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Xiaomi Token Plan 欧洲', description: '小米 MiMo Token Plan 订阅 · 欧洲 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 歐洲', description: '小米 MiMo Token Plan 訂閱 · 歐洲 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (Europe)', description: 'Xiaomi MiMo Token Plan subscription (Europe) for coding tools.', badge: 'Token' }, + })), + cerebras: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Cerebras', description: '高速推理托管开源模型', badge: 'API' }, + 'zh-TW': { name: 'Cerebras', description: '高速推理託管開源模型', badge: 'API' }, + en: { name: 'Cerebras', description: 'Fast hosted open-model inference.', badge: 'API' }, + })), + mistral: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Mistral', description: 'Mistral 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Mistral', description: 'Mistral 官方 API 連線', badge: 'API' }, + en: { name: 'Mistral', description: 'Official Mistral API access.', badge: 'API' }, + })), + togetherai: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Together AI', description: '托管开源模型 API', badge: 'API' }, + 'zh-TW': { name: 'Together AI', description: '託管開源模型 API', badge: 'API' }, + en: { name: 'Together AI', description: 'Hosted open models over one API.', badge: 'API' }, + })), + ollama: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Ollama', description: '本机运行 · 离线可用', badge: 'Local' }, + 'zh-TW': { name: 'Ollama', description: '本機執行 · 離線可用', badge: 'Local' }, + en: { name: 'Ollama', description: 'Runs locally · works offline', badge: 'Local' }, + })), + 'lm-studio': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'LM Studio', description: '本机 LM Studio 服务 · 离线可用', badge: 'Local' }, + 'zh-TW': { name: 'LM Studio', description: '本機 LM Studio 服務 · 離線可用', badge: 'Local' }, + en: { name: 'LM Studio', description: 'Local models served by LM Studio.', badge: 'Local' }, + })), + localai: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'LocalAI', description: '本机 LocalAI 服务,可选密钥保护', badge: 'Local' }, + 'zh-TW': { name: 'LocalAI', description: '本機 LocalAI 服務,可選金鑰保護', badge: 'Local' }, + en: { name: 'LocalAI', description: 'Local models served by LocalAI, with optional API key.', badge: 'Local' }, + })), + custom: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: '自定义连接', description: '中转站、代理服务或自部署网关,支持 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可按模型选择协议。', badge: '自定义' }, + 'zh-TW': { name: '自訂連線', description: '中轉站、代理服務或自部署閘道器,支援 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可依模型選擇協定。', badge: '自訂' }, + en: { name: 'Custom connection', description: 'Relay, proxy, or self-hosted gateway speaking OpenAI Chat, OpenAI Responses, or Anthropic Messages, chosen per model.', badge: 'Custom' }, + })), + 'fireworks-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Fireworks AI', description: 'Serverless 开源模型托管', badge: 'API' }, + 'zh-TW': { name: 'Fireworks AI', description: 'Serverless 開源模型託管', badge: 'API' }, + en: { name: 'Fireworks AI', description: 'Serverless open models with exact Fireworks model paths.', badge: 'API' }, + })), + nvidia: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'NVIDIA', description: 'NVIDIA 官方托管模型接入', badge: 'API' }, + 'zh-TW': { name: 'NVIDIA', description: 'NVIDIA 官方託管模型服務', badge: 'API' }, + en: { name: 'NVIDIA', description: 'NVIDIA-hosted models API access.', badge: 'API' }, + })), + 'tencent-tokenhub': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Tencent TokenHub', description: '腾讯云 TokenHub 按量接入,混元等模型', badge: 'API' }, + 'zh-TW': { name: 'Tencent TokenHub', description: '騰訊雲 TokenHub 按量計費模型服務,包含混元等模型', badge: 'API' }, + en: { name: 'Tencent TokenHub', description: 'Tencent Cloud TokenHub pay-as-you-go access.', badge: 'API' }, + })), + stepfun: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'StepFun 中国站', description: '阶跃星辰官方接入 · 中国站', badge: 'API' }, + 'zh-TW': { name: 'StepFun 中國站', description: '階躍星辰官方 API 連線 · 中國站', badge: 'API' }, + en: { name: 'StepFun (China)', description: 'Official StepFun China API access.', badge: 'API' }, + })), + 'tencent-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Tencent Coding Plan', description: '腾讯云 Coding 套餐 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Tencent Coding Plan', description: '騰訊雲 Coding 套餐 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Tencent Coding Plan', description: 'Tencent Cloud coding plan · OpenAI-compatible', badge: 'Coding' }, + })), + 'stepfun-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'StepFun 国际站', description: '阶跃星辰官方接入 · 国际站', badge: 'API' }, + 'zh-TW': { name: 'StepFun 國際站', description: '階躍星辰官方 API 連線 · 國際站', badge: 'API' }, + en: { name: 'StepFun (Global)', description: 'Official StepFun Global API access.', badge: 'API' }, + })), + 'volcengine-ark': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: '火山方舟', description: '火山引擎官方接入,豆包等模型', badge: 'API' }, + 'zh-TW': { name: '火山方舟', description: '火山引擎官方 API 連線,包含豆包等模型', badge: 'API' }, + en: { name: 'Volcengine Ark (China)', description: 'Volcengine Ark direct API access in China.', badge: 'API' }, + })), + 'volcengine-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 订阅 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 訂閱 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Volcengine Ark Coding Plan (China)', description: 'Volcengine Ark coding subscription · OpenAI-compatible', badge: 'Coding' }, + })), + 'volcengine-agent-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 订阅 · Responses API', badge: 'Agent' }, + 'zh-TW': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 訂閱 · Responses API', badge: 'Agent' }, + en: { name: 'Volcengine Ark Agent Plan (China)', description: 'Volcengine Ark agent subscription · Responses API', badge: 'Agent' }, + })), + 'tencent-token-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Tencent Token Plan', description: '腾讯云 Token 套餐,个人智能体与编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Tencent Token Plan', description: '騰訊雲 Token 套餐,個人智慧體與編碼工具', badge: 'Token' }, + en: { name: 'Tencent Token Plan', description: 'Tencent Cloud token plan for personal agents and coding tools.', badge: 'Token' }, + })), + 'stepfun-step-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'StepFun Step Plan 中国站', description: '阶跃星辰订阅套餐 · 中国站', badge: 'Plan' }, + 'zh-TW': { name: 'StepFun Step Plan 中國站', description: '階躍星辰訂閱套餐 · 中國站', badge: 'Plan' }, + en: { name: 'StepFun Step Plan (China)', description: 'StepFun China subscription for coding and agent tools.', badge: 'Plan' }, + })), + deepinfra: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'DeepInfra', description: '开源模型托管推理 · OpenAI 兼容', badge: 'API' }, + 'zh-TW': { name: 'DeepInfra', description: '開源模型託管推理 · OpenAI 相容', badge: 'API' }, + en: { name: 'DeepInfra', description: 'Hosted open-model inference · OpenAI-compatible', badge: 'API' }, + })), + cohere: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Cohere', description: 'Cohere 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Cohere', description: 'Cohere 官方 API 連線', badge: 'API' }, + en: { name: 'Cohere', description: 'Official Cohere Chat API access.', badge: 'API' }, + })), + vercel: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Vercel AI Gateway', description: '一个密钥接入多家托管模型', badge: '网关' }, + 'zh-TW': { name: 'Vercel AI Gateway', description: '使用一組金鑰連線多家託管模型', badge: '閘道器' }, + en: { name: 'Vercel AI Gateway', description: 'One API key for hosted models with exact creator/model ids.', badge: 'Gateway' }, + })), + 'stepfun-ai-step-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'StepFun Step Plan 国际站', description: '阶跃星辰订阅套餐 · 国际站', badge: 'Plan' }, + 'zh-TW': { name: 'StepFun Step Plan 國際站', description: '階躍星辰訂閱套餐 · 國際站', badge: 'Plan' }, + en: { name: 'StepFun Step Plan (Global)', description: 'StepFun Global subscription for coding and agent tools.', badge: 'Plan' }, + })), + 'cloudflare-workers-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Cloudflare Workers AI', description: 'Cloudflare 托管模型,账户级接入', badge: 'API' }, + 'zh-TW': { name: 'Cloudflare Workers AI', description: 'Cloudflare 託管模型,使用帳號層級連線', badge: 'API' }, + en: { name: 'Cloudflare Workers AI', description: 'Cloudflare-hosted models over the account-scoped API.', badge: 'API' }, + })), + huggingface: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家托管模型', badge: '路由' }, + 'zh-TW': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家託管模型', badge: '路由' }, + en: { name: 'Hugging Face', description: 'Inference Providers router across hosted models.', badge: 'Router' }, + })), + 'ollama-cloud': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Ollama Cloud', description: 'Ollama 官方云端托管模型', badge: 'API' }, + 'zh-TW': { name: 'Ollama Cloud', description: 'Ollama 官方雲端託管模型', badge: 'API' }, + en: { name: 'Ollama Cloud', description: 'Ollama-hosted cloud models over the official remote API.', badge: 'API' }, + })), + zenmux: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'ZenMux', description: '模型路由网关,一个密钥接入多家模型', badge: '网关' }, + 'zh-TW': { name: 'ZenMux', description: '模型路由閘道器,使用一組金鑰連線多家模型', badge: '閘道器' }, + en: { name: 'ZenMux', description: 'One API key for routed models with exact creator/model ids.', badge: 'Gateway' }, + })), + opencode: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenCode Zen', description: '面向编码智能体的按量模型精选', badge: 'Plan' }, + 'zh-TW': { name: 'OpenCode Zen', description: '面向編碼智慧體的按量模型精選', badge: 'Plan' }, + en: { name: 'OpenCode Zen', description: 'Curated pay-as-you-go models for coding agents.', badge: 'Plan' }, + })), + 'opencode-go': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenCode Go', description: '低价订阅制的开源编码模型精选', badge: 'Plan' }, + 'zh-TW': { name: 'OpenCode Go', description: '低價訂閱制的開源編碼模型精選', badge: 'Plan' }, + en: { name: 'OpenCode Go', description: 'Low-cost subscription to curated open coding models.', badge: 'Plan' }, + })), + 'opencode-free': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenCode Free', description: '已停用:OpenCode 免费服务仅限其官方客户端使用。', badge: 'Free' }, + 'zh-TW': { name: 'OpenCode Free', description: '已停用:OpenCode 免費服務僅限其官方用戶端使用。', badge: 'Free' }, + en: { name: 'OpenCode Free', description: 'Retired: OpenCode restricts its free tier to its own client.', badge: 'Free' }, + })), + commandcode: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Command Code', description: '使用 Command Code 套餐额度,连接后自动获取模型。', badge: 'Coding' }, + 'zh-TW': { name: 'Command Code', description: '使用 Command Code 方案額度,連線後自動取得模型。', badge: 'Coding' }, + en: { name: 'Command Code', description: 'Use your Command Code plan credits. Models are fetched when you connect.', badge: 'Coding' }, + })), // Retired: its transport reached a private endpoint under the official CLI's // identity. Kept so an existing connection stays identifiable and readable; // the connection card states that nothing can send through it any more. - 'commandcode-go': { - 'zh-CN': { name: 'Command Code GO', description: '已停用:该套餐通过官方 CLI 的私有通道访问,已不再支持。', badge: 'Coding' }, - 'zh-TW': { name: 'Command Code GO', description: '已停用:該方案透過官方 CLI 的私有通道存取,已不再支援。', badge: 'Coding' }, - en: { name: 'Command Code GO', description: 'Retired: this plan reached a private endpoint under the official CLI\u2019s identity.', badge: 'Coding' }, - }, - groq: { - 'zh-CN': { name: 'Groq', description: 'LPU 高速推理托管开源模型', badge: 'API' }, - 'zh-TW': { name: 'Groq', description: 'LPU 高速推理託管開源模型', badge: 'API' }, - en: { name: 'Groq', description: 'Ultra-fast LPU-hosted open models.', badge: 'API' }, - }, - openrouter: { - 'zh-CN': { name: 'OpenRouter', description: '一个密钥接入各大模型厂商 · OpenAI 兼容', badge: '聚合' }, - 'zh-TW': { name: 'OpenRouter', description: '使用一組金鑰連線各大模型廠商 · OpenAI 相容', badge: '聚合' }, - en: { name: 'OpenRouter', description: 'One API key across all major model labs · OpenAI-compatible', badge: 'Aggregator' }, - }, - alibaba: { - 'zh-CN': { name: 'Alibaba', description: '阿里云百炼接入,通义千问 Qwen 模型', badge: 'API' }, - 'zh-TW': { name: 'Alibaba', description: '阿里雲百鍊 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, - en: { name: 'Alibaba', description: 'Alibaba Cloud API access for Qwen models.', badge: 'API' }, - }, - 'alibaba-cn': { - 'zh-CN': { name: 'Alibaba 中国站', description: '阿里云百炼中国站接入,通义千问 Qwen 模型', badge: 'API' }, - 'zh-TW': { name: 'Alibaba 中國站', description: '阿里雲百鍊中國站 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, - en: { - name: 'Alibaba (China)', - description: 'Alibaba Cloud China-platform API access for Qwen models.', - badge: 'API', - }, - }, - 'alibaba-coding-plan-cn': { - 'zh-CN': { name: 'Alibaba Coding Plan 中国站', description: '阿里云百炼 Coding Plan 订阅 · 中国站', badge: 'Plan' }, - 'zh-TW': { name: 'Alibaba Coding Plan 中國站', description: '阿里雲百鍊 Coding Plan 訂閱 · 中國站', badge: 'Plan' }, - en: { name: 'Alibaba Coding Plan (China)', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools · China.', badge: 'Plan' }, - }, - 'alibaba-coding-plan': { - 'zh-CN': { name: 'Alibaba Coding Plan 国际站', description: '阿里云百炼 Coding Plan 订阅 · 国际站', badge: 'Plan' }, - 'zh-TW': { name: 'Alibaba Coding Plan 國際站', description: '阿里雲百鍊 Coding Plan 訂閱 · 國際站', badge: 'Plan' }, - en: { name: 'Alibaba Coding Plan', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools.', badge: 'Plan' }, - }, - 'alibaba-token-plan-cn': { - 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 北京', badge: 'Token' }, - 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 北京', badge: 'Token' }, - en: { name: 'Alibaba Token Plan (China)', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Beijing region.', badge: 'Token' }, - }, - 'alibaba-token-plan': { - 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 新加坡', badge: 'Token' }, - 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 新加坡', badge: 'Token' }, - en: { name: 'Alibaba Token Plan', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Singapore region.', badge: 'Token' }, - }, + 'commandcode-go': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Command Code GO', description: '已停用:该套餐通过官方 CLI 的私有通道访问,已不再支持。', badge: 'Coding' }, + 'zh-TW': { name: 'Command Code GO', description: '已停用:該方案透過官方 CLI 的私有通道存取,已不再支援。', badge: 'Coding' }, + en: { name: 'Command Code GO', description: 'Retired: this plan reached a private endpoint under the official CLI\u2019s identity.', badge: 'Coding' }, + })), + groq: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Groq', description: 'LPU 高速推理托管开源模型', badge: 'API' }, + 'zh-TW': { name: 'Groq', description: 'LPU 高速推理託管開源模型', badge: 'API' }, + en: { name: 'Groq', description: 'Ultra-fast LPU-hosted open models.', badge: 'API' }, + })), + openrouter: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenRouter', description: '一个密钥接入各大模型厂商 · OpenAI 兼容', badge: '聚合' }, + 'zh-TW': { name: 'OpenRouter', description: '使用一組金鑰連線各大模型廠商 · OpenAI 相容', badge: '聚合' }, + en: { name: 'OpenRouter', description: 'One API key across all major model labs · OpenAI-compatible', badge: 'Aggregator' }, + })), + alibaba: resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba', description: '阿里云百炼接入,通义千问 Qwen 模型', badge: 'API' }, + 'zh-TW': { name: 'Alibaba', description: '阿里雲百鍊 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, + en: { name: 'Alibaba', description: 'Alibaba Cloud API access for Qwen models.', badge: 'API' }, + })), + 'alibaba-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba 中国站', description: '阿里云百炼中国站接入,通义千问 Qwen 模型', badge: 'API' }, + 'zh-TW': { name: 'Alibaba 中國站', description: '阿里雲百鍊中國站 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, + en: { + name: 'Alibaba (China)', + description: 'Alibaba Cloud China-platform API access for Qwen models.', + badge: 'API', + }, + })), + 'alibaba-coding-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba Coding Plan 中国站', description: '阿里云百炼 Coding Plan 订阅 · 中国站', badge: 'Plan' }, + 'zh-TW': { name: 'Alibaba Coding Plan 中國站', description: '阿里雲百鍊 Coding Plan 訂閱 · 中國站', badge: 'Plan' }, + en: { name: 'Alibaba Coding Plan (China)', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools · China.', badge: 'Plan' }, + })), + 'alibaba-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba Coding Plan 国际站', description: '阿里云百炼 Coding Plan 订阅 · 国际站', badge: 'Plan' }, + 'zh-TW': { name: 'Alibaba Coding Plan 國際站', description: '阿里雲百鍊 Coding Plan 訂閱 · 國際站', badge: 'Plan' }, + en: { name: 'Alibaba Coding Plan', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools.', badge: 'Plan' }, + })), + 'alibaba-token-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 北京', badge: 'Token' }, + 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 北京', badge: 'Token' }, + en: { name: 'Alibaba Token Plan (China)', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Beijing region.', badge: 'Token' }, + })), + 'alibaba-token-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 新加坡', badge: 'Token' }, + 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 新加坡', badge: 'Token' }, + en: { name: 'Alibaba Token Plan', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Singapore region.', badge: 'Token' }, + })), // OAuth account providers (not in CATALOG_PROVIDER_TYPES; shown in the // accounts section and on connection rows). - 'github-copilot': { - 'zh-CN': { name: 'GitHub Copilot', description: 'GitHub Copilot 订阅接入,复用本机 GitHub 登录', badge: 'Account' }, - 'zh-TW': { name: 'GitHub Copilot', description: '透過 GitHub Copilot 訂閱連線,沿用本機 GitHub 登入', badge: 'Account' }, - en: { name: 'GitHub Copilot', description: 'GitHub Copilot subscription access using an existing GitHub login.', badge: 'Account' }, - }, - 'claude-subscription': { - 'zh-CN': { name: 'Claude Subscription', description: 'Claude Pro / Max 订阅账号登录;登录后自动成为可用模型连接。' }, - 'zh-TW': { name: 'Claude Subscription', description: 'Claude Pro / Max 訂閱帳號登入;登入後自動成為可用模型連線。' }, - en: { name: 'Claude Subscription', description: 'Sign in with a Claude Pro / Max subscription; it becomes an available model connection once signed in.' }, - }, - 'openai-codex': { - 'zh-CN': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 账号登录;登录后自动成为可用模型连接。' }, - 'zh-TW': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 帳號登入;登入後自動成為可用模型連線。' }, - en: { name: 'OpenAI OAuth', description: 'Sign in with a ChatGPT / Codex account; it becomes an available model connection once signed in.' }, - }, - 'xai-oauth': { - 'zh-CN': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 账号登录。', badge: 'Account' }, - 'zh-TW': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 帳號登入。', badge: 'Account' }, - en: { name: 'xAI OAuth', description: 'Sign in with SuperGrok or X Premium.', badge: 'Account' }, - }, + 'github-copilot': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'GitHub Copilot', description: 'GitHub Copilot 订阅接入,复用本机 GitHub 登录', badge: 'Account' }, + 'zh-TW': { name: 'GitHub Copilot', description: '透過 GitHub Copilot 訂閱連線,沿用本機 GitHub 登入', badge: 'Account' }, + en: { name: 'GitHub Copilot', description: 'GitHub Copilot subscription access using an existing GitHub login.', badge: 'Account' }, + })), + 'claude-subscription': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'Claude Subscription', description: 'Claude Pro / Max 订阅账号登录;登录后自动成为可用模型连接。' }, + 'zh-TW': { name: 'Claude Subscription', description: 'Claude Pro / Max 訂閱帳號登入;登入後自動成為可用模型連線。' }, + en: { name: 'Claude Subscription', description: 'Sign in with a Claude Pro / Max subscription; it becomes an available model connection once signed in.' }, + })), + 'openai-codex': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 账号登录;登录后自动成为可用模型连接。' }, + 'zh-TW': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 帳號登入;登入後自動成為可用模型連線。' }, + en: { name: 'OpenAI OAuth', description: 'Sign in with a ChatGPT / Codex account; it becomes an available model connection once signed in.' }, + })), + 'xai-oauth': resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 账号登录。', badge: 'Account' }, + 'zh-TW': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 帳號登入。', badge: 'Account' }, + en: { name: 'xAI OAuth', description: 'Sign in with SuperGrok or X Premium.', badge: 'Account' }, + })), } satisfies Record>; export function providerDisplay(type: ProviderType, locale: UiLocale): ProviderCopy { diff --git a/packages/core/src/redaction.ts b/packages/core/src/redaction.ts index 04087e76cbf..1f07cec0fda 100644 --- a/packages/core/src/redaction.ts +++ b/packages/core/src/redaction.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from './ui-locale.js'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from './ui-locale.js'; const SENSITIVE_KEY_SUFFIXES = new Set([ 'auth', @@ -310,29 +310,31 @@ export function classifyGeneralizedError(error: unknown): GeneralizedErrorClass /** Locale copy for each {@link GeneralizedErrorClass}; catalog authors spread * this per-locale block instead of restating the sentences. */ -export const GENERALIZED_ERROR_COPY = { - 'zh-CN': { - timeout: '请求超时', - rate_limited: '触发模型速率限制', - auth_failed: '鉴权失败', - provider_error: '模型服务返回错误', - network_error: '网络错误', - }, - 'zh-TW': { - timeout: '請求逾時', - rate_limited: '已達模型速率限制', - auth_failed: '驗證失敗', - provider_error: '模型服務傳回錯誤', - network_error: '網路錯誤', - }, - en: { - timeout: 'Request timed out', - rate_limited: 'Rate limit exceeded', - auth_failed: 'Authentication failed', - provider_error: 'Provider returned an error', - network_error: 'Network error', - }, -} satisfies UiCatalog>; +export const GENERALIZED_ERROR_COPY = resolveUiMessageCatalog( + defineUiMessageCatalog>()({ + 'zh-CN': { + timeout: '请求超时', + rate_limited: '触发模型速率限制', + auth_failed: '鉴权失败', + provider_error: '模型服务返回错误', + network_error: '网络错误', + }, + 'zh-TW': { + timeout: '請求逾時', + rate_limited: '已達模型速率限制', + auth_failed: '驗證失敗', + provider_error: '模型服務傳回錯誤', + network_error: '網路錯誤', + }, + en: { + timeout: 'Request timed out', + rate_limited: 'Rate limit exceeded', + auth_failed: 'Authentication failed', + provider_error: 'Provider returned an error', + network_error: 'Network error', + }, + }), +); export function generalizedErrorMessageForLocale( error: unknown, diff --git a/packages/core/src/relative-time-policy.ts b/packages/core/src/relative-time-policy.ts index b2ee1d8d79a..e6ea0891ee4 100644 --- a/packages/core/src/relative-time-policy.ts +++ b/packages/core/src/relative-time-policy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog } from './ui-locale.js'; +import { defineUiMessageCatalog, resolveUiMessageCatalog } from './ui-locale.js'; export const RELATIVE_HORIZON_MS = 7 * 24 * 60 * 60 * 1000; export const JUST_NOW_MS = 60_000; @@ -28,11 +28,13 @@ export const MONTH_MS = 30 * DAY_MS; export const YEAR_MS = 12 * MONTH_MS; const MAX_SIDEBAR_REFRESH_MS = 24 * DAY_MS; -export const JUST_NOW: UiCatalog = { - 'zh-CN': '刚刚', - 'zh-TW': '剛剛', - en: 'just now', -}; +export const JUST_NOW = resolveUiMessageCatalog( + defineUiMessageCatalog()({ + 'zh-CN': '刚刚', + 'zh-TW': '剛剛', + en: 'just now', + }), +); const SIDEBAR_TIME_BUCKETS = [ { unitMs: MINUTE_MS, maxValue: 60, suffix: 'min' }, diff --git a/packages/runtime-host/src/client/host-handoff-copy.ts b/packages/runtime-host/src/client/host-handoff-copy.ts index 31021046028..eb8e6260259 100644 --- a/packages/runtime-host/src/client/host-handoff-copy.ts +++ b/packages/runtime-host/src/client/host-handoff-copy.ts @@ -17,7 +17,11 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { + type UiLocale, + defineUiMessageCatalog, + resolveUiMessageCatalog, +} from '@maka/core/ui-locale'; import { isHostActivityIdle } from '../protocol/host-status.js'; import type { HostHandoffAction, @@ -54,202 +58,206 @@ interface HostHandoffCopy { progressDetail: string; } -const COPY = { - 'zh-CN': { - titles: { - replacement_required: '需要切换 WSL 后台服务', - busy: '后台服务仍在使用中', - activity_unknown: '需要确认是否停止后台服务', - operator_required: '后台服务需要手动更新', - repair_required: '后台服务需要修复', - retry_required: '暂时无法完成交接', +const COPY = resolveUiMessageCatalog( + defineUiMessageCatalog()({ + 'zh-CN': { + titles: { + replacement_required: '需要切换 WSL 后台服务', + busy: '后台服务仍在使用中', + activity_unknown: '需要确认是否停止后台服务', + operator_required: '后台服务需要手动更新', + repair_required: '后台服务需要修复', + retry_required: '暂时无法完成交接', + }, + descriptions: (targetName: string) => ({ + replacement_required: + '当前尚未更新。选择继续后,将通过已安装的管理程序安全停止旧服务并启动选定版本;有活动工作时会另行确认。', + busy: '其它连接或正在执行的工作阻止了自动交接。停止并继续可能中断这些工作。', + activity_unknown: + '后台服务与当前客户端不兼容,且无法确认有哪些工作仍在运行。停止并继续会重新启动服务,可能中断其他窗口或设备上的工作。', + operator_required: `目前无法从这里更新后台服务。请通过 ${targetName} 上管理此服务的应用或命令更新,然后重试。`, + repair_required: + '上次启动或交接失败。请查看下方诊断信息,修复原因后再重试;如果状态没有变化,重复重试通常不会解决问题。', + retry_required: '服务状态发生了变化,或交接尚未完成。可以安全重试,不会默认中断工作。', + }), + recoveryGuidance: { + managed: + '此后台服务由安装它的 Maka 管理。如果由 Desktop 安装,请在该 Desktop 中打开此工作区,然后选择“停止旧服务并继续”;否则请使用安装它的 Maka 更新服务,再回到这里重试。', + owner: '此后台服务属于另一个 Maka 安装。请使用管理它的 Maka 安装更新,然后重试。', + installation: + '本次启动使用临时安装,无法接管现有后台服务。请使用已安装的 Maka 更新服务,然后重试。', + identity: + '无法确认旧后台服务的进程身份,因此不能安全停止它。请关闭启动它的 Maka 或通过其管理程序停止服务,然后重试。', + }, + manualBusy: + '旧服务拒绝安全停止;这可能是其他连接或后台工作所致。关闭其他客户端后可再次尝试安全停止,或明确选择中断工作并继续。', + manualOperatorTitle: '无法连接后台服务', + manualOperatorDescription: + '当前版本无法连接,且尚未执行更新。请查看诊断或更新客户端,完成后重新检查。', + activity: (connections: number, operations: number) => + `${connections} 个连接 · ${operations} 个进行中的操作`, + background: (count: number) => `${count} 个后台工作`, + backgroundUnknown: '后台工作数量未知', + waiting: { + recheck: '重新检查只会重试连接,不会更新服务。', + natural: 'Maka 会持续检查,并在可以安全继续时自动继续。', + blocked: 'Maka 会持续检查。若状态没有变化,等待或重试不会解决此问题。', + }, + repairNotice: '修复将使用当前应用配套的版本,可能替换现有服务版本。', + labels: { + cancel: '取消', + retry: '安全重试', + recheck: '重新检查', + replace: '停止旧服务并继续', + interrupt: '停止并继续', + interruptRepair: '中断并修复', + interruptRecheck: '中断工作并继续', + }, + phases: { + checking: '正在检查服务', + staging: '正在准备更新', + pausing: '正在等待安全暂停点', + retiring: '正在停止旧服务', + replacing: '正在替换服务', + verifying: '正在确认工作区已就绪', + }, + progressTitle: '正在继续打开工作区', + progressDetail: '正在完成交接或安全收尾,请稍候。', }, - descriptions: (targetName) => ({ - replacement_required: - '当前尚未更新。选择继续后,将通过已安装的管理程序安全停止旧服务并启动选定版本;有活动工作时会另行确认。', - busy: '其它连接或正在执行的工作阻止了自动交接。停止并继续可能中断这些工作。', - activity_unknown: - '后台服务与当前客户端不兼容,且无法确认有哪些工作仍在运行。停止并继续会重新启动服务,可能中断其他窗口或设备上的工作。', - operator_required: `目前无法从这里更新后台服务。请通过 ${targetName} 上管理此服务的应用或命令更新,然后重试。`, - repair_required: - '上次启动或交接失败。请查看下方诊断信息,修复原因后再重试;如果状态没有变化,重复重试通常不会解决问题。', - retry_required: '服务状态发生了变化,或交接尚未完成。可以安全重试,不会默认中断工作。', - }), - recoveryGuidance: { - managed: - '此后台服务由安装它的 Maka 管理。如果由 Desktop 安装,请在该 Desktop 中打开此工作区,然后选择“停止旧服务并继续”;否则请使用安装它的 Maka 更新服务,再回到这里重试。', - owner: '此后台服务属于另一个 Maka 安装。请使用管理它的 Maka 安装更新,然后重试。', - installation: - '本次启动使用临时安装,无法接管现有后台服务。请使用已安装的 Maka 更新服务,然后重试。', - identity: - '无法确认旧后台服务的进程身份,因此不能安全停止它。请关闭启动它的 Maka 或通过其管理程序停止服务,然后重试。', + 'zh-TW': { + titles: { + replacement_required: '需要切換 WSL 背景服務', + busy: '背景服務仍在使用中', + activity_unknown: '需要確認是否停止背景服務', + operator_required: '背景服務需要手動更新', + repair_required: '背景服務需要修復', + retry_required: '暫時無法完成交接', + }, + descriptions: (targetName: string) => ({ + replacement_required: + '目前尚未更新。選擇繼續後,將透過已安裝的管理程式安全停止舊服務並啟動選定版本;有進行中的工作時會另行確認。', + busy: '其他連線或正在執行的工作阻止了自動交接。停止並繼續可能中斷這些工作。', + activity_unknown: + '背景服務與目前用戶端不相容,且無法確認有哪些工作仍在執行。停止並繼續會重新啟動服務,可能中斷其他視窗或裝置上的工作。', + operator_required: `目前無法從這裡更新背景服務。請透過 ${targetName} 上管理此服務的應用程式或命令更新,再重試。`, + repair_required: + '上次啟動或交接失敗。請查看下方診斷資訊,修復原因後再重試;如果狀態沒有改變,重複重試通常不會解決問題。', + retry_required: '服務狀態發生了變化,或交接尚未完成。可以安全重試,不會預設中斷工作。', + }), + recoveryGuidance: { + managed: + '此背景服務由安裝它的 Maka 管理。如果由 Desktop 安裝,請在該 Desktop 中開啟此工作區,然後選擇「停止舊服務並繼續」;否則請使用安裝它的 Maka 更新服務,再回到這裡重試。', + owner: '此背景服務屬於另一個 Maka 安裝。請使用管理它的 Maka 安裝更新,再重試。', + installation: + '這次啟動使用暫存安裝,無法接管現有背景服務。請使用已安裝的 Maka 更新服務,再重試。', + identity: + '無法確認舊背景服務的處理程序身分,因此不能安全停止它。請關閉啟動它的 Maka 或透過其管理程式停止服務,再重試。', + }, + manualBusy: + '舊服務拒絕安全停止;這可能是其他連線或背景工作所致。關閉其他用戶端後可再次嘗試安全停止,或明確選擇中斷工作並繼續。', + manualOperatorTitle: '無法連線到背景服務', + manualOperatorDescription: + '目前版本無法連線,且尚未執行更新。請查看診斷或更新用戶端,完成後重新檢查。', + activity: (connections: number, operations: number) => + `${connections} 個連線 · ${operations} 個進行中的操作`, + background: (count: number) => `${count} 個背景工作`, + backgroundUnknown: '背景工作數量未知', + waiting: { + recheck: '重新檢查只會重試連線,不會更新服務。', + natural: 'Maka 會持續檢查,並在可以安全繼續時自動繼續。', + blocked: 'Maka 會持續檢查。若狀態沒有改變,等待或重試不會解決此問題。', + }, + repairNotice: '修復將使用目前應用程式配套的版本,可能替換現有服務版本。', + labels: { + cancel: '取消', + retry: '安全重試', + recheck: '重新檢查', + replace: '停止舊服務並繼續', + interrupt: '停止並繼續', + interruptRepair: '中斷並修復', + interruptRecheck: '中斷工作並繼續', + }, + phases: { + checking: '正在檢查服務', + staging: '正在準備更新', + pausing: '正在等待安全暫停點', + retiring: '正在停止舊服務', + replacing: '正在替換服務', + verifying: '正在確認工作區已就緒', + }, + progressTitle: '正在繼續開啟工作區', + progressDetail: '正在完成交接或安全收尾,請稍候。', }, - manualBusy: - '旧服务拒绝安全停止;这可能是其他连接或后台工作所致。关闭其他客户端后可再次尝试安全停止,或明确选择中断工作并继续。', - manualOperatorTitle: '无法连接后台服务', - manualOperatorDescription: - '当前版本无法连接,且尚未执行更新。请查看诊断或更新客户端,完成后重新检查。', - activity: (connections, operations) => `${connections} 个连接 · ${operations} 个进行中的操作`, - background: (count) => `${count} 个后台工作`, - backgroundUnknown: '后台工作数量未知', - waiting: { - recheck: '重新检查只会重试连接,不会更新服务。', - natural: 'Maka 会持续检查,并在可以安全继续时自动继续。', - blocked: 'Maka 会持续检查。若状态没有变化,等待或重试不会解决此问题。', + en: { + titles: { + replacement_required: 'Switch the WSL background service', + busy: 'Your background service is still in use', + activity_unknown: 'Confirm before stopping the service', + operator_required: 'The background service needs a manual update', + repair_required: 'Your background service needs repair', + retry_required: 'The handoff could not finish yet', + }, + descriptions: (targetName: string) => ({ + replacement_required: + 'No update is running. Continue to retire the old service through its installed operator and start the selected version. Interrupting active work requires a separate confirmation.', + busy: 'Other connections or work in progress prevent an automatic handoff. Stopping the service may interrupt that work.', + activity_unknown: + 'The background service is incompatible with this client, and its active work is unknown. Stop and continue restarts it and may interrupt work in other windows or devices.', + operator_required: `This client cannot update the background service here. Use its managing app or operator command on ${targetName}, then retry.`, + repair_required: + 'The last startup or handoff failed. Review the diagnostic below, correct the cause, then retry. Repeating the same action without a state change will usually fail again.', + retry_required: + 'The service changed or the handoff has not finished. A safe retry will not interrupt work by default.', + }), + recoveryGuidance: { + managed: + 'This background service is managed by the Maka installation that created it. If Desktop installed it, open this workspace there and choose Stop old service and continue. Otherwise update the service with that Maka installation, then return here and retry.', + owner: + 'Another Maka installation owns this background service. Update it using that installation, then retry.', + installation: + 'This temporary installation cannot take over the existing background service. Update it using an installed Maka, then retry.', + identity: + 'The old background process could not be identified safely. Close the Maka instance that started it or stop it through its operator, then retry.', + }, + manualBusy: + 'The old service refused safe retirement. Other connections or background work may be keeping it in use. Close other clients and try stopping safely again, or explicitly interrupt work and continue.', + manualOperatorTitle: 'Cannot connect to the background service', + manualOperatorDescription: + 'These builds cannot connect and no update is running. Check the diagnostic or update the client, then recheck.', + activity: (connections: number, operations: number) => + `${connections} connections · ${operations} operations in progress`, + background: (count: number) => `${count} background activities`, + backgroundUnknown: 'Background activity count unknown', + waiting: { + recheck: 'Recheck retries the connection without updating the service.', + natural: 'Maka keeps checking and continues automatically when it is safe.', + blocked: + 'Maka keeps checking. Waiting or retrying will not resolve this unless the service state changes.', + }, + repairNotice: + 'Repair uses the version supplied with this app and may replace the installed service version.', + labels: { + cancel: 'Cancel', + retry: 'Retry safely', + recheck: 'Recheck', + replace: 'Stop old service and continue', + interrupt: 'Stop and continue', + interruptRepair: 'Interrupt and repair', + interruptRecheck: 'Interrupt work and continue', + }, + phases: { + checking: 'Checking the service', + staging: 'Preparing the update', + pausing: 'Waiting for a safe pause point', + retiring: 'Stopping the previous service', + replacing: 'Replacing the service', + verifying: 'Verifying your workspace is ready', + }, + progressTitle: 'Continuing to your workspace', + progressDetail: 'Finishing the handoff or its safe recovery. Please wait.', }, - repairNotice: '修复将使用当前应用配套的版本,可能替换现有服务版本。', - labels: { - cancel: '取消', - retry: '安全重试', - recheck: '重新检查', - replace: '停止旧服务并继续', - interrupt: '停止并继续', - interruptRepair: '中断并修复', - interruptRecheck: '中断工作并继续', - }, - phases: { - checking: '正在检查服务', - staging: '正在准备更新', - pausing: '正在等待安全暂停点', - retiring: '正在停止旧服务', - replacing: '正在替换服务', - verifying: '正在确认工作区已就绪', - }, - progressTitle: '正在继续打开工作区', - progressDetail: '正在完成交接或安全收尾,请稍候。', - }, - 'zh-TW': { - titles: { - replacement_required: '需要切換 WSL 背景服務', - busy: '背景服務仍在使用中', - activity_unknown: '需要確認是否停止背景服務', - operator_required: '背景服務需要手動更新', - repair_required: '背景服務需要修復', - retry_required: '暫時無法完成交接', - }, - descriptions: (targetName) => ({ - replacement_required: - '目前尚未更新。選擇繼續後,將透過已安裝的管理程式安全停止舊服務並啟動選定版本;有進行中的工作時會另行確認。', - busy: '其他連線或正在執行的工作阻止了自動交接。停止並繼續可能中斷這些工作。', - activity_unknown: - '背景服務與目前用戶端不相容,且無法確認有哪些工作仍在執行。停止並繼續會重新啟動服務,可能中斷其他視窗或裝置上的工作。', - operator_required: `目前無法從這裡更新背景服務。請透過 ${targetName} 上管理此服務的應用程式或命令更新,再重試。`, - repair_required: - '上次啟動或交接失敗。請查看下方診斷資訊,修復原因後再重試;如果狀態沒有改變,重複重試通常不會解決問題。', - retry_required: '服務狀態發生了變化,或交接尚未完成。可以安全重試,不會預設中斷工作。', - }), - recoveryGuidance: { - managed: - '此背景服務由安裝它的 Maka 管理。如果由 Desktop 安裝,請在該 Desktop 中開啟此工作區,然後選擇「停止舊服務並繼續」;否則請使用安裝它的 Maka 更新服務,再回到這裡重試。', - owner: '此背景服務屬於另一個 Maka 安裝。請使用管理它的 Maka 安裝更新,再重試。', - installation: - '這次啟動使用暫存安裝,無法接管現有背景服務。請使用已安裝的 Maka 更新服務,再重試。', - identity: - '無法確認舊背景服務的處理程序身分,因此不能安全停止它。請關閉啟動它的 Maka 或透過其管理程式停止服務,再重試。', - }, - manualBusy: - '舊服務拒絕安全停止;這可能是其他連線或背景工作所致。關閉其他用戶端後可再次嘗試安全停止,或明確選擇中斷工作並繼續。', - manualOperatorTitle: '無法連線到背景服務', - manualOperatorDescription: - '目前版本無法連線,且尚未執行更新。請查看診斷或更新用戶端,完成後重新檢查。', - activity: (connections, operations) => `${connections} 個連線 · ${operations} 個進行中的操作`, - background: (count) => `${count} 個背景工作`, - backgroundUnknown: '背景工作數量未知', - waiting: { - recheck: '重新檢查只會重試連線,不會更新服務。', - natural: 'Maka 會持續檢查,並在可以安全繼續時自動繼續。', - blocked: 'Maka 會持續檢查。若狀態沒有改變,等待或重試不會解決此問題。', - }, - repairNotice: '修復將使用目前應用程式配套的版本,可能替換現有服務版本。', - labels: { - cancel: '取消', - retry: '安全重試', - recheck: '重新檢查', - replace: '停止舊服務並繼續', - interrupt: '停止並繼續', - interruptRepair: '中斷並修復', - interruptRecheck: '中斷工作並繼續', - }, - phases: { - checking: '正在檢查服務', - staging: '正在準備更新', - pausing: '正在等待安全暫停點', - retiring: '正在停止舊服務', - replacing: '正在替換服務', - verifying: '正在確認工作區已就緒', - }, - progressTitle: '正在繼續開啟工作區', - progressDetail: '正在完成交接或安全收尾,請稍候。', - }, - en: { - titles: { - replacement_required: 'Switch the WSL background service', - busy: 'Your background service is still in use', - activity_unknown: 'Confirm before stopping the service', - operator_required: 'The background service needs a manual update', - repair_required: 'Your background service needs repair', - retry_required: 'The handoff could not finish yet', - }, - descriptions: (targetName) => ({ - replacement_required: - 'No update is running. Continue to retire the old service through its installed operator and start the selected version. Interrupting active work requires a separate confirmation.', - busy: 'Other connections or work in progress prevent an automatic handoff. Stopping the service may interrupt that work.', - activity_unknown: - 'The background service is incompatible with this client, and its active work is unknown. Stop and continue restarts it and may interrupt work in other windows or devices.', - operator_required: `This client cannot update the background service here. Use its managing app or operator command on ${targetName}, then retry.`, - repair_required: - 'The last startup or handoff failed. Review the diagnostic below, correct the cause, then retry. Repeating the same action without a state change will usually fail again.', - retry_required: - 'The service changed or the handoff has not finished. A safe retry will not interrupt work by default.', - }), - recoveryGuidance: { - managed: - 'This background service is managed by the Maka installation that created it. If Desktop installed it, open this workspace there and choose Stop old service and continue. Otherwise update the service with that Maka installation, then return here and retry.', - owner: - 'Another Maka installation owns this background service. Update it using that installation, then retry.', - installation: - 'This temporary installation cannot take over the existing background service. Update it using an installed Maka, then retry.', - identity: - 'The old background process could not be identified safely. Close the Maka instance that started it or stop it through its operator, then retry.', - }, - manualBusy: - 'The old service refused safe retirement. Other connections or background work may be keeping it in use. Close other clients and try stopping safely again, or explicitly interrupt work and continue.', - manualOperatorTitle: 'Cannot connect to the background service', - manualOperatorDescription: - 'These builds cannot connect and no update is running. Check the diagnostic or update the client, then recheck.', - activity: (connections, operations) => - `${connections} connections · ${operations} operations in progress`, - background: (count) => `${count} background activities`, - backgroundUnknown: 'Background activity count unknown', - waiting: { - recheck: 'Recheck retries the connection without updating the service.', - natural: 'Maka keeps checking and continues automatically when it is safe.', - blocked: - 'Maka keeps checking. Waiting or retrying will not resolve this unless the service state changes.', - }, - repairNotice: - 'Repair uses the version supplied with this app and may replace the installed service version.', - labels: { - cancel: 'Cancel', - retry: 'Retry safely', - recheck: 'Recheck', - replace: 'Stop old service and continue', - interrupt: 'Stop and continue', - interruptRepair: 'Interrupt and repair', - interruptRecheck: 'Interrupt work and continue', - }, - phases: { - checking: 'Checking the service', - staging: 'Preparing the update', - pausing: 'Waiting for a safe pause point', - retiring: 'Stopping the previous service', - replacing: 'Replacing the service', - verifying: 'Verifying your workspace is ready', - }, - progressTitle: 'Continuing to your workspace', - progressDetail: 'Finishing the handoff or its safe recovery. Please wait.', - }, -} satisfies UiCatalog; + }), +); export interface HostHandoffPresentation { title: string; diff --git a/packages/ui/src/__tests__/tool-activity-presentation.test.ts b/packages/ui/src/__tests__/tool-activity-presentation.test.ts index e1196db7639..5483c306663 100644 --- a/packages/ui/src/__tests__/tool-activity-presentation.test.ts +++ b/packages/ui/src/__tests__/tool-activity-presentation.test.ts @@ -22,7 +22,7 @@ import { describe, it } from 'node:test'; import { createElement, type ReactNode } from 'react'; import { renderToStaticMarkup as renderReactToStaticMarkup } from 'react-dom/server'; import { computerUseModelCallArgs } from '@maka/core/computer-use'; -import { UI_LOCALES, type UiCatalog, type UiLocale } from '@maka/core/ui-locale'; +import { UI_LOCALES, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { ToolCallDetail, ToolTrow } from '../tool-activity.js'; import type { ToolActivityItem } from '../materialize.js'; import { LocaleProvider } from '../locale-context.js'; @@ -82,11 +82,11 @@ describe('tool activity presentation', () => { assert.match(en, /Bypass mode required/); assert.match(en, /Switch and retry/); - const errorMessages = { - 'zh-CN': '需要“绕过”模式。此操作会直接控制本机应用,无法在沙箱模式下执行。', - 'zh-TW': '需要“繞過”模式。此操作會直接控制本機應用,無法在沙箱模式下執行。', - en: 'Bypass mode required. This action controls a local app directly and cannot run inside the sandbox.', - } satisfies UiCatalog; + const errorMessages = resolveUiMessageCatalog(defineUiMessageCatalog()({ + 'zh-CN': '需要“绕过”模式。此操作会直接控制本机应用,无法在沙箱模式下执行。', + 'zh-TW': '需要“繞過”模式。此操作會直接控制本機應用,無法在沙箱模式下執行。', + en: 'Bypass mode required. This action controls a local app directly and cannot run inside the sandbox.', + })); for (const locale of UI_LOCALES) { const row = renderToStaticMarkup(createElement(ToolTrow, { items: [item] }), locale); assert.ok(row.includes(`title="${errorMessages[locale]}"`), `${locale}: full bypass error`); diff --git a/packages/ui/src/conversation-copy.ts b/packages/ui/src/conversation-copy.ts index 28bdde55b5e..450159b9ad2 100644 --- a/packages/ui/src/conversation-copy.ts +++ b/packages/ui/src/conversation-copy.ts @@ -21,7 +21,7 @@ import type { ProviderRetryReason } from '@maka/core/events'; import type { PermissionMode } from '@maka/core/permission'; import type { SessionBlockedReason, SessionStatus } from '@maka/core/session'; import type { ThinkingLevel } from '@maka/core/model-thinking'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { formatCompactTokenCount } from './compact-token-count.js'; export type DayPeriod = 'morning' | 'noon' | 'afternoon' | 'evening'; @@ -431,9 +431,7 @@ export interface ConversationCopy { archiveCount: (count: number) => string; }; } - -const CONVERSATION_COPY = { - 'zh-CN': { +const conversationCopyZhCn: ConversationCopy = { empty: { ariaLabel: '开始任务', surfaceAriaLabel: '新任务对话', @@ -556,8 +554,8 @@ const CONVERSATION_COPY = { blockedReason: { NO_REAL_CONNECTION: '等待配置可用模型连接', auth: '需要重新登录', permission_required: '等待权限确认', tool_failed: '工具调用失败', unknown: '运行中断,可重试' }, listAriaLabel: '任务列表', showMore: '显示更多', showMoreAriaLabel: (count) => `显示 ${count} 条更多任务`, renameAriaLabel: '重命名任务', renameProjectTitle: '重命名项目', renameSubmit: '保存', respondingAriaLabel: '正在响应', respondingTitle: '任务正在流式响应中', staleTitle: '此任务使用的模型连接已不可用,发送时会切换到默认连接', staleAriaLabel: '任务已过期', stale: '已过期', unreadAriaLabel: '未读消息', actionsAriaLabel: (name) => `${name} 任务操作`, pin: '置顶', unpin: '取消置顶', rename: '重命名', archive: '归档', delete: '删除', moveToProject: '移动到项目', moveToNoProject: '移出项目', pinned: '置顶', recent: '最近', projects: '项目', groupByTime: '按时间', groupByProject: '按项目', groupingAriaLabel: '任务分组方式', projectActionsAriaLabel: (name) => `${name} 项目操作`, projectNewTask: '新建任务', projectRename: '重命名', projectArchive: '归档', projectRestore: '恢复', projectRelink: '重新定位', projectUnavailable: '项目目录不可用', archivedProjects: '已归档项目', archivedProjectsAriaLabel: '展开已归档项目', worktreeAriaLabel: 'Git 工作树', promptRailAriaLabel: '按提问跳转', emptyPrompt: '(空提问)', jumpToPrompt: (preview) => `跳到提问:${preview}`, pickedAriaLabel: '已选中', pinCount: (count) => `置顶 ${count} 项`, unpinCount: (count) => `取消置顶 ${count} 项`, archiveCount: (count) => `归档 ${count} 项`, }, - }, - 'zh-TW': { + }; +const conversationCopyZhTw: ConversationCopy = { empty: { ariaLabel: '開始任務', surfaceAriaLabel: '新任務對話', @@ -680,8 +678,8 @@ const CONVERSATION_COPY = { blockedReason: { NO_REAL_CONNECTION: '等待設定可用模型連線', auth: '需要重新登入', permission_required: '等待權限確認', tool_failed: '工具呼叫失敗', unknown: '執行中斷,可重試' }, listAriaLabel: '任務列表', showMore: '顯示更多', showMoreAriaLabel: (count) => `顯示 ${count} 條更多工`, renameAriaLabel: '重新命名任務', renameProjectTitle: '重新命名專案', renameSubmit: '儲存', respondingAriaLabel: '正在響應', respondingTitle: '任務正在流式響應中', staleTitle: '此任務使用的模型連線已不可用,傳送時會切換到預設連線', staleAriaLabel: '任務已過期', stale: '已過期', unreadAriaLabel: '未讀訊息', actionsAriaLabel: (name) => `${name} 任務操作`, pin: '置頂', unpin: '取消置頂', rename: '重新命名', archive: '歸檔', delete: '刪除', moveToProject: '移動到專案', moveToNoProject: '移出專案', pinned: '置頂', recent: '最近', projects: '專案', groupByTime: '按時間', groupByProject: '按專案', groupingAriaLabel: '任務分組方式', projectActionsAriaLabel: (name) => `${name} 專案操作`, projectNewTask: '建立任務', projectRename: '重新命名', projectArchive: '歸檔', projectRestore: '恢復', projectRelink: '重新定位', projectUnavailable: '專案目錄不可用', archivedProjects: '已歸檔專案', archivedProjectsAriaLabel: '展開已歸檔專案', worktreeAriaLabel: 'Git 工作樹', promptRailAriaLabel: '按提問跳轉', emptyPrompt: '(空提問)', jumpToPrompt: (preview) => `跳到提問:${preview}`, pickedAriaLabel: '已選取', pinCount: (count) => `置頂 ${count} 項`, unpinCount: (count) => `取消置頂 ${count} 項`, archiveCount: (count) => `歸檔 ${count} 項`, }, - }, - en: { + }; +const conversationCopyEn: ConversationCopy = { empty: { ariaLabel: 'Start a task', surfaceAriaLabel: 'New task conversation', @@ -801,8 +799,9 @@ const CONVERSATION_COPY = { blockedReason: { NO_REAL_CONNECTION: 'Waiting for an available model connection', auth: 'Sign in again', permission_required: 'Waiting for permission', tool_failed: 'Tool call failed', unknown: 'Run interrupted; retry available' }, listAriaLabel: 'Task list', showMore: 'Show more', showMoreAriaLabel: (count) => `Show ${count} more tasks`, renameAriaLabel: 'Rename task', renameProjectTitle: 'Rename project', renameSubmit: 'Save', respondingAriaLabel: 'Responding', respondingTitle: 'This task is streaming a response', staleTitle: 'This task\'s model connection is unavailable; sending will switch to the default connection', staleAriaLabel: 'Stale task', stale: 'Stale', unreadAriaLabel: 'Unread messages', actionsAriaLabel: (name) => `${name} task actions`, pin: 'Pin', unpin: 'Unpin', rename: 'Rename', archive: 'Archive', delete: 'Delete', moveToProject: 'Move to project', moveToNoProject: 'Remove from project', pinned: 'Pinned', recent: 'Recent', projects: 'Projects', groupByTime: 'By time', groupByProject: 'By project', groupingAriaLabel: 'Task grouping', projectActionsAriaLabel: (name) => `${name} project actions`, projectNewTask: 'New task', projectRename: 'Rename', projectArchive: 'Archive', projectRestore: 'Restore', projectRelink: 'Relocate', projectUnavailable: 'Project directory unavailable', archivedProjects: 'Archived projects', archivedProjectsAriaLabel: 'Expand archived projects', worktreeAriaLabel: 'Git worktree', promptRailAriaLabel: 'Jump by prompt', emptyPrompt: '(empty prompt)', jumpToPrompt: (preview) => `Jump to prompt: ${preview}`, pickedAriaLabel: 'Selected', pinCount: (count) => `Pin ${count} tasks`, unpinCount: (count) => `Unpin ${count} tasks`, archiveCount: (count) => `Archive ${count} tasks`, }, - }, -} satisfies UiCatalog; + }; + +const CONVERSATION_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': conversationCopyZhCn, 'zh-TW': conversationCopyZhTw, en: conversationCopyEn })); export function getConversationCopy(locale: UiLocale): ConversationCopy { return CONVERSATION_COPY[locale]; diff --git a/packages/ui/src/daily-review-copy.ts b/packages/ui/src/daily-review-copy.ts index 298ba3167b2..d602d283b9e 100644 --- a/packages/ui/src/daily-review-copy.ts +++ b/packages/ui/src/daily-review-copy.ts @@ -19,7 +19,7 @@ import type { DailyReviewArchive } from '@maka/core/daily-review'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type ArchiveSectionKey = keyof DailyReviewArchive['sections']; @@ -102,9 +102,7 @@ export interface DailyReviewCopy { requestCount: (count: number) => string; }; } - -const DAILY_REVIEW_COPY = { - 'zh-CN': { +const dailyReviewCopyZhCn: DailyReviewCopy = { archive: { section: { summary: '任务摘要', gaps: '遗漏提醒', usage: '使用洞察', code: '代码建议' }, status: { ok: '已生成', no_model: '缺少模型', no_data: '无数据', failed: '生成失败', skipped: '已跳过' }, @@ -138,8 +136,8 @@ const DAILY_REVIEW_COPY = { markdown: { separator: ':', title: (dayLabel) => `# Maka · 每日回顾 · ${dayLabel}`, conversations: '任务', requests: '模型调用', tokens: 'Token', cost: '费用', errors: '错误', activeConversations: '活跃任务', modelUsage: '模型使用', toolCalls: '工具调用', requestCount: (count) => `${count} 次`, }, - }, - 'zh-TW': { + }; +const dailyReviewCopyZhTw: DailyReviewCopy = { archive: { section: { summary: '任務摘要', gaps: '遺漏提醒', usage: '使用洞察', code: '程式碼建議' }, status: { ok: '已生成', no_model: '缺少模型', no_data: '無資料', failed: '生成失敗', skipped: '已跳過' }, @@ -173,8 +171,8 @@ const DAILY_REVIEW_COPY = { markdown: { separator: ':', title: (dayLabel) => `# Maka · 每日回顧 · ${dayLabel}`, conversations: '任務', requests: '請求', tokens: 'Token', cost: '費用', errors: '錯誤', activeConversations: '活躍任務', modelUsage: '模型使用', toolCalls: '工具呼叫', requestCount: (count) => `${count} 次`, }, - }, - en: { + }; +const dailyReviewCopyEn: DailyReviewCopy = { archive: { section: { summary: 'Task summary', gaps: 'Missed items', usage: 'Usage insights', code: 'Code suggestions' }, status: { ok: 'Generated', no_model: 'Model unavailable', no_data: 'No data', failed: 'Generation failed', skipped: 'Skipped' }, @@ -208,8 +206,9 @@ const DAILY_REVIEW_COPY = { markdown: { separator: ':', title: (dayLabel) => `# Maka · Daily review · ${dayLabel}`, conversations: 'Tasks', requests: 'Model calls', tokens: 'Tokens', cost: 'Cost', errors: 'Errors', activeConversations: 'Active tasks', modelUsage: 'Model usage', toolCalls: 'Tool calls', requestCount: (count) => `${count} ${count === 1 ? 'call' : 'calls'}`, }, - }, -} satisfies UiCatalog; + }; + +const DAILY_REVIEW_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': dailyReviewCopyZhCn, 'zh-TW': dailyReviewCopyZhTw, en: dailyReviewCopyEn })); export function getDailyReviewCopy(locale: UiLocale): DailyReviewCopy { return DAILY_REVIEW_COPY[locale]; diff --git a/packages/ui/src/executor-model-picker.tsx b/packages/ui/src/executor-model-picker.tsx index 7b66869718e..82ebb2b5665 100644 --- a/packages/ui/src/executor-model-picker.tsx +++ b/packages/ui/src/executor-model-picker.tsx @@ -26,7 +26,7 @@ import type { } from '@maka/core/executor-catalog'; export type { ExecutorSelection } from '@maka/core/executor-catalog'; import type { ProviderType } from '@maka/core/llm-connections'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { Settings, ICON_SIZE } from './icons.js'; import { ModelPickerPanel, ModelPickerPanelContext } from './model-picker-panel.js'; import { ComposerModelLabel } from './composer-model-label.js'; @@ -78,7 +78,7 @@ interface ExecutorCopy { selectionFailed: string; } -const EXECUTOR_COPY = { +const EXECUTOR_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ en: { title: 'Executor', nativeOperations: 'This operation requires Maka. Start a new Maka task.', @@ -144,7 +144,7 @@ const EXECUTOR_COPY = { newTask: '建立新任務', selectionFailed: '模型切換失敗,請重試。', }, -} satisfies UiCatalog; +})); export function executorCopy(locale: UiLocale): ExecutorCopy { return EXECUTOR_COPY[locale]; diff --git a/packages/ui/src/locale-helpers.ts b/packages/ui/src/locale-helpers.ts index 00a6045497e..e072605ef12 100644 --- a/packages/ui/src/locale-helpers.ts +++ b/packages/ui/src/locale-helpers.ts @@ -46,7 +46,7 @@ * dimension. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type { UiLocale } from '@maka/core/ui-locale'; @@ -69,7 +69,7 @@ export type PromptSuggestion = { label: string; prompt: string }; * translate, draft a message) so the empty-chat surface reads as a * general assistant first, a coding assistant second. */ -const PROMPT_SUGGESTIONS_BY_LOCALE: UiCatalog = { +const PROMPT_SUGGESTIONS_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': [ { label: '总结代码库', prompt: '帮我总结当前代码库的目录结构和关键模块。' }, { label: '解释这段代码', prompt: '我贴一段代码进来,请帮我逐行解释它做什么、有没有坑:\n\n```\n\n```' }, @@ -94,7 +94,7 @@ const PROMPT_SUGGESTIONS_BY_LOCALE: UiCatalog = { { label: 'Draft message', prompt: 'Help me draft a ____ message to ____, with the goal of ____:\n\nPoints to cover:\n- \n- ' }, { label: 'Review code', prompt: 'Please review this code — readability, error handling, performance concerns:\n\n```\n\n```' }, ], -}; +})); export function getPromptSuggestions(locale: UiLocale): PromptSuggestion[] { return PROMPT_SUGGESTIONS_BY_LOCALE[locale]; diff --git a/packages/ui/src/runtime-resume-copy.ts b/packages/ui/src/runtime-resume-copy.ts index 7ffb412374b..cbdbea9ee69 100644 --- a/packages/ui/src/runtime-resume-copy.ts +++ b/packages/ui/src/runtime-resume-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface ResumeParkToastCopy { title: string; @@ -69,7 +69,7 @@ interface ResumeParkCopy { reasons: ResumeParkReasonCopy; } -const RESUME_PARK_COPY = { +const RESUME_PARK_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { title: '暂时无法继续这一轮', fallbackDescription: '当前任务不满足继续的条件。', @@ -187,7 +187,7 @@ const RESUME_PARK_COPY = { resume_feature_disabled: 'Resuming interrupted tasks is not enabled.', }, }, -} satisfies UiCatalog; +})); export function resumeParkToastCopy(reasons: readonly string[], locale: UiLocale): ResumeParkToastCopy { const copy = RESUME_PARK_COPY[locale]; diff --git a/packages/ui/src/scheduled-task-copy.ts b/packages/ui/src/scheduled-task-copy.ts index 1dc267e75e1..26558a43a04 100644 --- a/packages/ui/src/scheduled-task-copy.ts +++ b/packages/ui/src/scheduled-task-copy.ts @@ -18,7 +18,7 @@ */ import type { ScheduledTask, ScheduledTaskRunOutcome, ScheduledTaskStatus } from '@maka/core/scheduled-task'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ScheduledTaskDelivery, ScheduledTaskRecurrence, @@ -161,9 +161,7 @@ export interface ScheduledTaskCopy { agentDelivery: string; }; } - -const SCHEDULED_TASK_COPY = { - 'zh-CN': { +const scheduledTaskCopyZhCn: ScheduledTaskCopy = { templates: [ { id: 'daily-download-cleanup', title: '每日下载文件夹清理', note: '请帮我整理「下载」文件夹,把截图、安装包和临时文档按类型归档,并列出可删除项。', scheduleLabel: '每天 18:30', recurrence: 'cron', cronExpression: '30 18 * * *', nextRun: { hour: 18, minute: 30 } }, { id: 'midday-reset', title: '午间充电站', note: '午休时间到了,帮我回顾上午完成了什么,并给下午列一个轻量可执行计划。', scheduleLabel: '工作日 12:30', recurrence: 'cron', cronExpression: '30 12 * * 1-5', nextRun: { hour: 12, minute: 30 } }, @@ -196,8 +194,8 @@ const SCHEDULED_TASK_COPY = { agentSourceHint: '到点后,Maka 会使用创建时的执行设置启动新任务。', agentDelivery: '交给 Agent 执行', }, - }, - 'zh-TW': { + }; +const scheduledTaskCopyZhTw: ScheduledTaskCopy = { templates: [ { id: 'daily-download-cleanup', title: '每日下載資料夾清理', note: '請幫我整理「下載」資料夾,把截圖、安裝包和臨時文件按型別歸檔,並列出可刪除項。', scheduleLabel: '每天 18:30', recurrence: 'cron', cronExpression: '30 18 * * *', nextRun: { hour: 18, minute: 30 } }, { id: 'midday-reset', title: '午間充電站', note: '午休時間到了,幫我回顧上午完成了什麼,並給下午列一個輕量可執行計劃。', scheduleLabel: '工作日 12:30', recurrence: 'cron', cronExpression: '30 12 * * 1-5', nextRun: { hour: 12, minute: 30 } }, @@ -230,8 +228,8 @@ const SCHEDULED_TASK_COPY = { agentSourceHint: '到點後,Maka 會使用建立時的執行設定啟動新任務。', agentDelivery: '交給 Agent 執行', }, - }, - en: { + }; +const scheduledTaskCopyEn: ScheduledTaskCopy = { templates: [ { id: 'daily-download-cleanup', title: 'Clean up Downloads', note: 'Organize screenshots, installers, and temporary documents in Downloads by type, then list items that can be deleted.', scheduleLabel: 'Daily at 18:30', recurrence: 'cron', cronExpression: '30 18 * * *', nextRun: { hour: 18, minute: 30 } }, { id: 'midday-reset', title: 'Midday reset', note: 'Review what I completed this morning and create a lightweight, actionable plan for the afternoon.', scheduleLabel: 'Weekdays at 12:30', recurrence: 'cron', cronExpression: '30 12 * * 1-5', nextRun: { hour: 12, minute: 30 } }, @@ -265,8 +263,9 @@ const SCHEDULED_TASK_COPY = { 'When due, Maka starts a new task using the execution settings captured at creation.', agentDelivery: 'Run via the Agent', }, - }, -} satisfies UiCatalog; + }; + +const SCHEDULED_TASK_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': scheduledTaskCopyZhCn, 'zh-TW': scheduledTaskCopyZhTw, en: scheduledTaskCopyEn })); export function getScheduledTaskCopy(locale: UiLocale): ScheduledTaskCopy { return SCHEDULED_TASK_COPY[locale]; diff --git a/packages/ui/src/shared-ui-copy.ts b/packages/ui/src/shared-ui-copy.ts index 74d2aed6366..19aa3f33af5 100644 --- a/packages/ui/src/shared-ui-copy.ts +++ b/packages/ui/src/shared-ui-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface SharedUiCopy { capabilityAudit: { @@ -112,9 +112,7 @@ export interface SharedUiCopy { artifact: { unknownSize: string }; providers: { minimaxChina: string; custom: string; claudeSubscription: string }; } - -const SHARED_UI_COPY = { - 'zh-CN': { +const sharedUiCopyZhCn: SharedUiCopy = { capabilityAudit: { ariaLabel: '能力风险提示', needsAuthorization: (count) => `${count} 个来源等待授权`, @@ -193,8 +191,8 @@ const SHARED_UI_COPY = { stream: { assistantTailTruncated: '\n\n[…后续已截断]', thinkingHeadTruncated: '[…已截断早期 reasoning]\n', toolChunkTruncated: '\n[…已截断]\n' }, artifact: { unknownSize: '未知大小' }, providers: { minimaxChina: 'MiniMax 中国站', custom: '自定义', claudeSubscription: 'Claude 订阅' }, - }, - 'zh-TW': { + }; +const sharedUiCopyZhTw: SharedUiCopy = { capabilityAudit: { ariaLabel: '能力風險提示', needsAuthorization: (count) => `${count} 個來源等待授權`, @@ -273,8 +271,8 @@ const SHARED_UI_COPY = { stream: { assistantTailTruncated: '\n\n[…後續已截斷]', thinkingHeadTruncated: '[…已截斷早期 reasoning]\n', toolChunkTruncated: '\n[…已截斷]\n' }, artifact: { unknownSize: '未知大小' }, providers: { minimaxChina: 'MiniMax 中國站', custom: '自訂', claudeSubscription: 'Claude 訂閱' }, - }, - en: { + }; +const sharedUiCopyEn: SharedUiCopy = { capabilityAudit: { ariaLabel: 'Capability risks', needsAuthorization: (count) => `${count} ${count === 1 ? 'source' : 'sources'} awaiting authorization`, @@ -353,8 +351,9 @@ const SHARED_UI_COPY = { stream: { assistantTailTruncated: '\n\n[…remaining output truncated]', thinkingHeadTruncated: '[…earlier reasoning truncated]\n', toolChunkTruncated: '\n[…truncated]\n' }, artifact: { unknownSize: 'Unknown size' }, providers: { minimaxChina: 'MiniMax China', custom: 'Custom', claudeSubscription: 'Claude subscription' }, - }, -} satisfies UiCatalog; + }; + +const SHARED_UI_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': sharedUiCopyZhCn, 'zh-TW': sharedUiCopyZhTw, en: sharedUiCopyEn })); export function getSharedUiCopy(locale: UiLocale): SharedUiCopy { return SHARED_UI_COPY[locale]; diff --git a/packages/ui/src/shell-controls-copy.ts b/packages/ui/src/shell-controls-copy.ts index 3223641365c..1f71805f451 100644 --- a/packages/ui/src/shell-controls-copy.ts +++ b/packages/ui/src/shell-controls-copy.ts @@ -18,7 +18,7 @@ */ import type { RecallFailureReason } from '@maka/core/recall'; -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * The failures the Search modal can show. Recall's own vocabulary, because @@ -58,7 +58,7 @@ type ShellControlsCopy = { }; }; -const SHELL_CONTROLS_COPY_BY_LOCALE = { +const SHELL_CONTROLS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { shared: { close: '关闭' }, navigation: { @@ -150,7 +150,7 @@ const SHELL_CONTROLS_COPY_BY_LOCALE = { resultsLabel: 'Search results', }, }, -} satisfies UiCatalog; +})); export function getShellControlsCopy(locale: UiLocale): ShellControlsCopy { return SHELL_CONTROLS_COPY_BY_LOCALE[locale]; diff --git a/packages/ui/src/skills-copy.ts b/packages/ui/src/skills-copy.ts index 4a67307d05d..2cf0b3a1611 100644 --- a/packages/ui/src/skills-copy.ts +++ b/packages/ui/src/skills-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ManagedSkillCategory, SkillEntry } from './module-panel-types.js'; type ManagedUpdateStatus = NonNullable; @@ -122,9 +122,7 @@ export interface SkillsCopy { pathLabel: string; }; } - -const SKILLS_COPY = { - 'zh-CN': { +const skillsCopyZhCn: SkillsCopy = { categories: { '内容创作': '内容创作', '数据与AI': '数据与 AI', '设计与UI': '设计与 UI', 'DevOps与部署': 'DevOps 与部署', '文档与写作': '文档与写作', '效率工具': '效率工具', '研究与分析': '研究与分析' }, discover: { builtinFallback: '应用自带 Skill。', sourceFallback: '本地来源库 Skill。' }, install: { action: (name) => `安装 ${name}`, short: '安装' }, @@ -137,8 +135,8 @@ const SKILLS_COPY = { page: { title: '技能', actionsAria: '技能操作', installed: '已安装', discover: '发现', add: '添加', importLocal: '导入本地 Skill', searchMatches: (count) => `${count} 个匹配`, search: '搜索技能', clearSearch: '清空搜索', locations: '技能位置…', refreshing: '刷新中…', refresh: '刷新' }, locations: { labels: { 'project:maka': '项目 · Maka', 'project:agents': '项目 · Agents', 'workspace:legacy': '工作区兼容目录', 'user:maka': '用户 · Maka', 'user:agents': '用户 · Agents' }, count: (count) => `${count} 个 Skill`, missing: '创建并打开', blocked: '路径已被阻止', readFailed: '无法读取' }, detail: { enabled: '启用', idLabel: '标识', scopeLabel: '范围', toolsLabel: '工具', pathLabel: '路径' }, - }, - 'zh-TW': { + }; +const skillsCopyZhTw: SkillsCopy = { categories: { '内容创作': '內容創作', '数据与AI': '資料與 AI', '设计与UI': '設計與 UI', 'DevOps与部署': 'DevOps 與部署', '文档与写作': '文件與寫作', '效率工具': '效率工具', '研究与分析': '研究與分析' }, discover: { builtinFallback: '應用自帶 Skill。', sourceFallback: '本地來源庫 Skill。' }, install: { action: (name) => `安裝 ${name}`, short: '安裝' }, @@ -151,8 +149,8 @@ const SKILLS_COPY = { page: { title: '技能', actionsAria: '技能操作', installed: '已安裝', discover: '探索', add: '新增', importLocal: '匯入本地 Skill', searchMatches: (count) => `${count} 個符合`, search: '搜尋技能', clearSearch: '清空搜尋', locations: '技能位置…', refreshing: '重新整理中…', refresh: '重新整理' }, locations: { labels: { 'project:maka': '專案 · Maka', 'project:agents': '專案 · Agents', 'workspace:legacy': '工作區相容目錄', 'user:maka': '使用者 · Maka', 'user:agents': '使用者 · Agents' }, count: (count) => `${count} 個 Skill`, missing: '建立並開啟', blocked: '路徑已被阻止', readFailed: '無法讀取' }, detail: { enabled: '啟用', idLabel: '標識', scopeLabel: '範圍', toolsLabel: '工具', pathLabel: '路徑' }, - }, - en: { + }; +const skillsCopyEn: SkillsCopy = { categories: { '内容创作': 'Content creation', '数据与AI': 'Data & AI', '设计与UI': 'Design & UI', 'DevOps与部署': 'DevOps & deployment', '文档与写作': 'Documents & writing', '效率工具': 'Productivity', '研究与分析': 'Research & analysis' }, discover: { builtinFallback: 'Skill included with the app.', sourceFallback: 'Local source-library Skill.' }, install: { action: (name) => `Install ${name}`, short: 'Install' }, @@ -165,8 +163,9 @@ const SKILLS_COPY = { page: { title: 'Skills', actionsAria: 'Skill actions', installed: 'Installed', discover: 'Discover', add: 'Add', importLocal: 'Import local Skill', searchMatches: (count) => `${count} ${count === 1 ? 'match' : 'matches'}`, search: 'Search skills', clearSearch: 'Clear search', locations: 'Skill locations…', refreshing: 'Refreshing…', refresh: 'Refresh' }, locations: { labels: { 'project:maka': 'Project · Maka', 'project:agents': 'Project · Agents', 'workspace:legacy': 'Workspace compatibility folder', 'user:maka': 'User · Maka', 'user:agents': 'User · Agents' }, count: (count) => count === 1 ? '1 Skill' : `${count} Skills`, missing: 'Create and open', blocked: 'Path blocked', readFailed: 'Could not read' }, detail: { enabled: 'Enabled', idLabel: 'ID', scopeLabel: 'Scope', toolsLabel: 'Tools', pathLabel: 'Path' }, - }, -} satisfies UiCatalog; + }; + +const SKILLS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': skillsCopyZhCn, 'zh-TW': skillsCopyZhTw, en: skillsCopyEn })); export function getSkillsCopy(locale: UiLocale): SkillsCopy { return SKILLS_COPY[locale]; diff --git a/packages/ui/src/tool-activity/copy.ts b/packages/ui/src/tool-activity/copy.ts index 8b042f8a714..36217c76328 100644 --- a/packages/ui/src/tool-activity/copy.ts +++ b/packages/ui/src/tool-activity/copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type BackgroundTerminalStatus = 'running' | 'completed' | 'failed' | 'timed_out' | 'cancelled' | 'orphaned'; type WebCredentialCopyKey = 'env' | 'settings' | 'missing' | 'unknown'; @@ -180,9 +180,7 @@ export interface ToolActivityCopy { readOnly: string; }; } - -const TOOL_ACTIVITY_COPY = { - 'zh-CN': { +const toolActivityCopyZhCn: ToolActivityCopy = { errorLabel: '错误', status: { sandboxBlocked: '可能被沙箱阻止', interrupted: '已中断' }, output: { redacted: '[已脱敏]', truncated: '输出已截断' }, @@ -289,8 +287,8 @@ const TOOL_ACTIVITY_COPY = { subagentStatus: { completed: '已完成', failed: '失败', cancelled: '已取消', running: '运行中', waiting_for_user: '等待用户输入' }, readOnly: '只读', }, - }, - 'zh-TW': { + }; +const toolActivityCopyZhTw: ToolActivityCopy = { errorLabel: '錯誤', status: { sandboxBlocked: '可能被沙箱阻止', interrupted: '已中斷' }, output: { redacted: '[已脫敏]', truncated: '輸出已截斷' }, @@ -397,8 +395,8 @@ const TOOL_ACTIVITY_COPY = { subagentStatus: { completed: '已完成', failed: '失敗', cancelled: '已取消', running: '執行中', waiting_for_user: '等待使用者輸入' }, readOnly: '只讀', }, - }, - en: { + }; +const toolActivityCopyEn: ToolActivityCopy = { errorLabel: 'Error', status: { sandboxBlocked: 'Possibly blocked by sandbox', interrupted: 'Interrupted' }, output: { redacted: '[Redacted]', truncated: 'Output truncated' }, @@ -502,8 +500,9 @@ const TOOL_ACTIVITY_COPY = { subagentStatus: { completed: 'Completed', failed: 'Failed', cancelled: 'Cancelled', running: 'Running', waiting_for_user: 'Waiting for user input' }, readOnly: 'Read only', }, - }, -} satisfies UiCatalog; + }; + +const TOOL_ACTIVITY_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': toolActivityCopyZhCn, 'zh-TW': toolActivityCopyZhTw, en: toolActivityCopyEn })); export function getToolActivityCopy(locale: UiLocale): ToolActivityCopy { return TOOL_ACTIVITY_COPY[locale]; From 33fe162315c6b4699bfbe88199320ce8a3c54a9b Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:44:05 +0900 Subject: [PATCH 2/5] fix(desktop): keep the UiCatalog marker on resolver-based copy catalogs The renderer architecture check admits a locales/*-copy.ts file as a validated copy catalog only when it carries a `satisfies UiCatalog` marker. The resolver migration dropped that marker, so every catalog fell out of the admitted class and the ratchet reported their imports as unbudgeted legacy debt. Wrap each resolveUiMessageCatalog(...) result in `satisfies UiCatalog` with the same type argument. This keeps the per-locale key contract on the resolved value and needs no change to the checker, which the base cross-check forbids a PR from loosening. Regenerate the ledger for the new @maka/core/ui-locale dependency edge. settings/provider-display-copy.ts stays on the old form: it lives outside locales/, so a runtime ui-locale import would be new debt. Co-Authored-By: Claude Sonnet 5.5 --- apps/desktop/renderer-architecture.json | 104 +++++++++++++----- .../src/renderer/locales/agent-graph-copy.ts | 4 +- .../src/renderer/locales/artifact-copy.ts | 4 +- .../src/renderer/locales/browser-copy.ts | 4 +- .../locales/capability-reason-copy.ts | 4 +- .../locales/external-session-import-copy.ts | 4 +- apps/desktop/src/renderer/locales/mcp-copy.ts | 4 +- .../src/renderer/locales/onboarding-copy.ts | 4 +- .../src/renderer/locales/peer-mesh-copy.ts | 4 +- .../locales/permission-center-copy.ts | 4 +- .../src/renderer/locales/plan-mode-copy.ts | 4 +- .../locales/session-collaboration-copy.ts | 4 +- .../renderer/locales/session-local-copy.ts | 4 +- .../src/renderer/locales/settings-bot-copy.ts | 6 +- .../locales/settings-daily-review-copy.ts | 4 +- .../renderer/locales/settings-data-copy.ts | 4 +- .../locales/settings-external-agents-copy.ts | 4 +- .../renderer/locales/settings-health-copy.ts | 8 +- .../src/renderer/locales/settings-jev-copy.ts | 4 +- .../renderer/locales/settings-memory-copy.ts | 4 +- .../locales/settings-navigation-copy.ts | 4 +- .../locales/settings-preferences-copy.ts | 4 +- .../locales/settings-projects-copy.ts | 4 +- .../renderer/locales/settings-shared-copy.ts | 4 +- .../locales/settings-subagents-copy.ts | 4 +- .../renderer/locales/settings-tasks-copy.ts | 4 +- .../locales/settings-test-result-copy.ts | 4 +- .../renderer/locales/settings-usage-copy.ts | 4 +- .../locales/settings-web-search-copy.ts | 4 +- .../src/renderer/locales/shell-copy.ts | 4 +- .../renderer/locales/shell-remaining-copy.ts | 4 +- .../renderer/locales/storage-usage-copy.ts | 4 +- .../renderer/locales/task-readiness-copy.ts | 4 +- .../renderer/locales/work-board-error-copy.ts | 4 +- 34 files changed, 146 insertions(+), 96 deletions(-) diff --git a/apps/desktop/renderer-architecture.json b/apps/desktop/renderer-architecture.json index 1c79a7b7e3e..c9c48a133b6 100644 --- a/apps/desktop/renderer-architecture.json +++ b/apps/desktop/renderer-architecture.json @@ -1154,7 +1154,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/artifact-copy.ts": { "bridgePaths": {}, @@ -1163,7 +1165,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/browser-copy.ts": { "bridgePaths": {}, @@ -1172,7 +1176,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/capability-reason-copy.ts": { "bridgePaths": {}, @@ -1192,7 +1198,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/mcp-copy.ts": { "bridgePaths": {}, @@ -1201,7 +1209,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/onboarding-copy.ts": { "bridgePaths": {}, @@ -1210,7 +1220,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/peer-mesh-copy.ts": { "bridgePaths": {}, @@ -1219,7 +1231,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/permission-center-copy.ts": { "bridgePaths": {}, @@ -1228,7 +1242,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/plan-mode-copy.ts": { "bridgePaths": {}, @@ -1248,7 +1264,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/session-local-copy.ts": { "bridgePaths": {}, @@ -1257,7 +1275,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-bot-copy.ts": { "bridgePaths": {}, @@ -1277,7 +1297,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-data-copy.ts": { "bridgePaths": {}, @@ -1286,7 +1308,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-external-agents-copy.ts": { "bridgePaths": {}, @@ -1295,7 +1319,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-health-copy.ts": { "bridgePaths": {}, @@ -1304,7 +1330,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-jev-copy.ts": { "bridgePaths": {}, @@ -1313,7 +1341,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-memory-copy.ts": { "bridgePaths": {}, @@ -1333,7 +1363,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-preferences-copy.ts": { "bridgePaths": {}, @@ -1342,7 +1374,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-projects-copy.ts": { "bridgePaths": {}, @@ -1362,7 +1396,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-subagents-copy.ts": { "bridgePaths": {}, @@ -1371,7 +1407,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-tasks-copy.ts": { "bridgePaths": {}, @@ -1380,7 +1418,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-test-result-copy.ts": { "bridgePaths": {}, @@ -1390,7 +1430,7 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { - "@maka/core/ui-locale": 1 + "@maka/core/ui-locale": 2 } }, "src/renderer/locales/settings-usage-copy.ts": { @@ -1400,7 +1440,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/settings-web-search-copy.ts": { "bridgePaths": {}, @@ -1409,7 +1451,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/shell-copy.ts": { "bridgePaths": {}, @@ -1431,7 +1475,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/storage-usage-copy.ts": { "bridgePaths": {}, @@ -1440,7 +1486,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/task-readiness-copy.ts": { "bridgePaths": {}, @@ -1449,7 +1497,9 @@ "lifecycleMethods": {}, "unresolvedDependencies": 0, "actionFactories": [], - "dependencyPaths": {} + "dependencyPaths": { + "@maka/core/ui-locale": 1 + } }, "src/renderer/locales/work-board-error-copy.ts": { "bridgePaths": {}, @@ -1459,7 +1509,7 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { - "@maka/core/ui-locale": 1 + "@maka/core/ui-locale": 2 } }, "src/renderer/model-catalog-choices.ts": { diff --git a/apps/desktop/src/renderer/locales/agent-graph-copy.ts b/apps/desktop/src/renderer/locales/agent-graph-copy.ts index aca3b386b0c..f89b7838e92 100644 --- a/apps/desktop/src/renderer/locales/agent-graph-copy.ts +++ b/apps/desktop/src/renderer/locales/agent-graph-copy.ts @@ -21,7 +21,7 @@ import type { AgentGraphClientOperator, AgentGraphClientSnapshot, } from '@maka/runtime/stream-graph-read-model'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface AgentGraphPanelCopy { title: string; @@ -199,7 +199,7 @@ const agentGraphPanelCopyEn: AgentGraphPanelCopy = { wait: waitReasonEn, }; -const AGENT_GRAPH_PANEL_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': agentGraphPanelCopyZhCn, 'zh-TW': agentGraphPanelCopyZhTw, en: agentGraphPanelCopyEn })); +const AGENT_GRAPH_PANEL_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': agentGraphPanelCopyZhCn, 'zh-TW': agentGraphPanelCopyZhTw, en: agentGraphPanelCopyEn })) satisfies UiCatalog; export function getAgentGraphPanelCopy(locale: UiLocale): AgentGraphPanelCopy { return AGENT_GRAPH_PANEL_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/artifact-copy.ts b/apps/desktop/src/renderer/locales/artifact-copy.ts index dadc495a104..7eab83a65e4 100644 --- a/apps/desktop/src/renderer/locales/artifact-copy.ts +++ b/apps/desktop/src/renderer/locales/artifact-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type ReasonCopy = { title: string; description: string }; @@ -209,7 +209,7 @@ const artifactCopyEn: ArtifactCopy = { }, }; -const ARTIFACT_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': artifactCopyZhCn, 'zh-TW': artifactCopyZhTw, en: artifactCopyEn })); +const ARTIFACT_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': artifactCopyZhCn, 'zh-TW': artifactCopyZhTw, en: artifactCopyEn })) satisfies UiCatalog; export function getArtifactCopy(locale: UiLocale): ArtifactCopy { return ARTIFACT_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/browser-copy.ts b/apps/desktop/src/renderer/locales/browser-copy.ts index b043952f7b0..958fec65b0b 100644 --- a/apps/desktop/src/renderer/locales/browser-copy.ts +++ b/apps/desktop/src/renderer/locales/browser-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type BrowserCopy = { unsupportedScheme: string; @@ -172,7 +172,7 @@ const browserCopyEn: BrowserCopy = { description: 'Enter an address, or ask the assistant to navigate and interact with a page.', }; -const BROWSER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': browserCopyZhCn, 'zh-TW': browserCopyZhTw, en: browserCopyEn })); +const BROWSER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': browserCopyZhCn, 'zh-TW': browserCopyZhTw, en: browserCopyEn })) satisfies UiCatalog; export function getBrowserCopy(locale: UiLocale): BrowserCopy { return BROWSER_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/capability-reason-copy.ts b/apps/desktop/src/renderer/locales/capability-reason-copy.ts index 7471bf62e9f..6a1fd84e69b 100644 --- a/apps/desktop/src/renderer/locales/capability-reason-copy.ts +++ b/apps/desktop/src/renderer/locales/capability-reason-copy.ts @@ -18,7 +18,7 @@ */ import type { CapabilityReasonCode } from '@maka/core/capabilities'; -import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type CapabilityReasonCopy = Record; @@ -98,7 +98,7 @@ const CAPABILITY_REASON_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getCapabilityReasonCopy(locale: UiLocale): CapabilityReasonCopy { return CAPABILITY_REASON_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/external-session-import-copy.ts b/apps/desktop/src/renderer/locales/external-session-import-copy.ts index 0567caf4c23..0eada258bf7 100644 --- a/apps/desktop/src/renderer/locales/external-session-import-copy.ts +++ b/apps/desktop/src/renderer/locales/external-session-import-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ExternalSessionLimit } from '@maka/core/external-session'; /** @@ -372,7 +372,7 @@ const copyEn: ExternalSessionImportCopy = { `Maka could not confirm the outcome of these imports: ${names.map((name) => `“${name}”`).join(', ')}. Check the task list or import again; importing again creates an independent task.`, }; -const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })) satisfies UiCatalog; export function getExternalSessionImportCopy(locale: UiLocale): ExternalSessionImportCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/mcp-copy.ts b/apps/desktop/src/renderer/locales/mcp-copy.ts index 8160b314451..e1e736e9009 100644 --- a/apps/desktop/src/renderer/locales/mcp-copy.ts +++ b/apps/desktop/src/renderer/locales/mcp-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type McpCopy = { errors: { @@ -249,7 +249,7 @@ const mcpCopyEn: McpCopy = { }, }; -const MCP_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': mcpCopyZhCn, 'zh-TW': mcpCopyZhTw, en: mcpCopyEn })); +const MCP_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': mcpCopyZhCn, 'zh-TW': mcpCopyZhTw, en: mcpCopyEn })) satisfies UiCatalog; export function getMcpCopy(locale: UiLocale): McpCopy { return MCP_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/onboarding-copy.ts b/apps/desktop/src/renderer/locales/onboarding-copy.ts index 69e164d3711..ece8c9d4f11 100644 --- a/apps/desktop/src/renderer/locales/onboarding-copy.ts +++ b/apps/desktop/src/renderer/locales/onboarding-copy.ts @@ -18,7 +18,7 @@ */ import type { OnboardingState } from '@maka/core/onboarding'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { OnboardingHeroCopy } from '../onboarding-hero-copy.js'; // Blocked states are keyed by reason, not just by kind: a new blocked reason @@ -198,7 +198,7 @@ const ONBOARDING_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog skip: 'Skip onboarding', snapshotErrorFallback: 'First-run status is temporarily unavailable. Try again later.', }, -})); +})) satisfies UiCatalog; export function getOnboardingCopy(locale: UiLocale): OnboardingCatalog { return ONBOARDING_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/peer-mesh-copy.ts b/apps/desktop/src/renderer/locales/peer-mesh-copy.ts index bc59849778b..c16189d21f2 100644 --- a/apps/desktop/src/renderer/locales/peer-mesh-copy.ts +++ b/apps/desktop/src/renderer/locales/peer-mesh-copy.ts @@ -18,7 +18,7 @@ */ import type { PeerMeshMemberProjection, PeerMeshQueryResult } from '@maka/runtime-host/protocol'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface PeerMeshCopy { readonly title: string; @@ -626,7 +626,7 @@ const PEER_MESH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getPeerMeshCopy(locale: UiLocale): PeerMeshCopy { return PEER_MESH_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/permission-center-copy.ts b/apps/desktop/src/renderer/locales/permission-center-copy.ts index a8d810542b6..638bf14b81d 100644 --- a/apps/desktop/src/renderer/locales/permission-center-copy.ts +++ b/apps/desktop/src/renderer/locales/permission-center-copy.ts @@ -27,7 +27,7 @@ import type { RuntimeProbeState, } from '@maka/core/capabilities'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type Tone = StatusSemantic; type StatusCopy = { label: string; tone: Tone }; @@ -271,7 +271,7 @@ const permissionCenterCopyEn: PermissionCenterCopy = { reasonFallback: 'See the runtime logs for details.', }; -const PERMISSION_CENTER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': permissionCenterCopyZhCn, 'zh-TW': permissionCenterCopyZhTw, en: permissionCenterCopyEn })); +const PERMISSION_CENTER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': permissionCenterCopyZhCn, 'zh-TW': permissionCenterCopyZhTw, en: permissionCenterCopyEn })) satisfies UiCatalog; export function getPermissionCenterCopy(locale: UiLocale): PermissionCenterCopy { return PERMISSION_CENTER_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/plan-mode-copy.ts b/apps/desktop/src/renderer/locales/plan-mode-copy.ts index 9707f7ceea7..9870f7803fe 100644 --- a/apps/desktop/src/renderer/locales/plan-mode-copy.ts +++ b/apps/desktop/src/renderer/locales/plan-mode-copy.ts @@ -18,7 +18,7 @@ */ import type { PlanExecutionStep, PlanProposal } from '@maka/core/plan'; -import { lookupCopy, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, lookupCopy, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { PlanControlErrorCode } from '@maka/runtime-host/protocol'; import type { PlanControlIpcResult } from '../../shared/plan-mode-ipc.js'; @@ -157,7 +157,7 @@ const copyEn: PlanModeCopy = { }, }; -const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })) satisfies UiCatalog; export function getPlanModeCopy(locale: UiLocale): PlanModeCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/session-collaboration-copy.ts b/apps/desktop/src/renderer/locales/session-collaboration-copy.ts index da017587cfe..3b2e015e012 100644 --- a/apps/desktop/src/renderer/locales/session-collaboration-copy.ts +++ b/apps/desktop/src/renderer/locales/session-collaboration-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; const ZH_CN = { shareAction: '分享此任务', @@ -349,7 +349,7 @@ const EN = { pendingTurnRequestCount: (count: number) => `${count} pending Turn ${count === 1 ? 'request' : 'requests'}`, } satisfies SessionCollaborationCopy; -const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': ZH_CN, 'zh-TW': ZH_TW, en: EN })); +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': ZH_CN, 'zh-TW': ZH_TW, en: EN })) satisfies UiCatalog; export function getSessionCollaborationCopy(locale: UiLocale) { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/session-local-copy.ts b/apps/desktop/src/renderer/locales/session-local-copy.ts index 0ba116de6a2..393dc18afdd 100644 --- a/apps/desktop/src/renderer/locales/session-local-copy.ts +++ b/apps/desktop/src/renderer/locales/session-local-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; interface SessionLocalCopy { saved: string; @@ -57,7 +57,7 @@ const catalog = resolveUiMessageCatalog(defineUiMessageCatalog check: '檢查是否送達', updateError: '無法更新已儲存的訊息', }, -})); +})) satisfies UiCatalog; export function getSessionLocalCopy(locale: UiLocale): SessionLocalCopy { return catalog[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-bot-copy.ts b/apps/desktop/src/renderer/locales/settings-bot-copy.ts index f5f9c50e1ed..84913cd9594 100644 --- a/apps/desktop/src/renderer/locales/settings-bot-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-bot-copy.ts @@ -23,7 +23,7 @@ import type { BotStatusCode, BotTestErrorCode, WechatBridgeQrHintCode } from '@m import type { BotOnboardingErrorCode, BotOnboardingProvider, BotOnboardingSnapshot, BotOnboardingRetryFailureCategory } from '@maka/core/bot-onboarding'; import type { GeneralizedErrorClass } from '@maka/core/redaction'; -import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type WidenCopy = T extends string ? string @@ -55,7 +55,7 @@ const BOT_TRANSPORT_ERRORS = resolveUiMessageCatalog(defineUiMessageCatalog>; const zhCopy = { providers: { @@ -423,7 +423,7 @@ const BOT_SETTINGS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getBotSettingsCopy(locale: UiLocale): BotSettingsCopy { return BOT_SETTINGS_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts b/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts index 26586239f28..8ffca16472f 100644 --- a/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-daily-review-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type DailyReviewSettingsCopy = { defaultModel: string; @@ -97,7 +97,7 @@ const SETTINGS_DAILY_REVIEW_COPY = resolveUiMessageCatalog(defineUiMessageCatalo model: 'Analysis model', modelHelp: 'Follows the current task default when unspecified.', }, -})); +})) satisfies UiCatalog; export function getDailyReviewSettingsCopy(locale: UiLocale): DailyReviewSettingsCopy { return SETTINGS_DAILY_REVIEW_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-data-copy.ts b/apps/desktop/src/renderer/locales/settings-data-copy.ts index 4b3ea0f2958..1384a7a8a01 100644 --- a/apps/desktop/src/renderer/locales/settings-data-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-data-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ConfigCategory } from '@maka/storage/config-transfer'; export type DataSettingsCopy = { @@ -125,7 +125,7 @@ const SETTINGS_DATA_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getDataSettingsCopy(locale: UiLocale): DataSettingsCopy { return SETTINGS_DATA_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts b/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts index 9b22ca243b8..1533cfce389 100644 --- a/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-external-agents-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { ExternalAgentSetupFailure } from '@maka/runtime-host/protocol'; const en = { programTitle: "Program", @@ -201,7 +201,7 @@ const zhTW: typeof en = { cleanup_failed: '無法釋放暫時程序,請重新啟動 Host 後重試。', }, }; -const catalog = resolveUiMessageCatalog(defineUiMessageCatalog()({ en, 'zh-CN': zh, 'zh-TW': zhTW })); +const catalog = resolveUiMessageCatalog(defineUiMessageCatalog()({ en, 'zh-CN': zh, 'zh-TW': zhTW })) satisfies UiCatalog; export function getExternalAgentsCopy(locale: UiLocale) { return catalog[locale]; } diff --git a/apps/desktop/src/renderer/locales/settings-health-copy.ts b/apps/desktop/src/renderer/locales/settings-health-copy.ts index f8aa7424cc4..3fadc587807 100644 --- a/apps/desktop/src/renderer/locales/settings-health-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-health-copy.ts @@ -28,7 +28,7 @@ import type { HealthSignalStatus, } from '@maka/core/health'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Health signals carry their own severity ladder — error > warning > info > ok @@ -163,7 +163,7 @@ const settingsHealthCopyEn: HealthCenterCopy = { signalDetail: (signal) => signalDetailEn(signal.detail), }; -const SETTINGS_HEALTH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsHealthCopyZhCn, 'zh-TW': settingsHealthCopyZhTw, en: settingsHealthCopyEn })); +const SETTINGS_HEALTH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsHealthCopyZhCn, 'zh-TW': settingsHealthCopyZhTw, en: settingsHealthCopyEn })) satisfies UiCatalog; export function getHealthCenterCopy(locale: UiLocale): HealthCenterCopy { return SETTINGS_HEALTH_COPY[locale]; @@ -250,7 +250,7 @@ const connectiontesterrormessagesEn: Record>()({ 'zh-CN': connectiontesterrormessagesZhCn, 'zh-TW': connectiontesterrormessagesZhTw, en: connectiontesterrormessagesEn })); +const connectionTestErrorMessages = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': connectiontesterrormessagesZhCn, 'zh-TW': connectiontesterrormessagesZhTw, en: connectiontesterrormessagesEn })) satisfies UiCatalog>; function signalDetailZh(detail: HealthSignalDetail | undefined): string | undefined { if (!detail) return undefined; @@ -339,7 +339,7 @@ const unknownruntimeerrorclassZhCn: string = '未知错误'; const unknownruntimeerrorclassZhTw: string = '未知錯誤'; const unknownruntimeerrorclassEn: string = 'Unknown error'; -const unknownRuntimeErrorClass = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': unknownruntimeerrorclassZhCn, 'zh-TW': unknownruntimeerrorclassZhTw, en: unknownruntimeerrorclassEn })); +const unknownRuntimeErrorClass = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': unknownruntimeerrorclassZhCn, 'zh-TW': unknownruntimeerrorclassZhTw, en: unknownruntimeerrorclassEn })) satisfies UiCatalog; // Runtime probes carry the turn's failure class (rate_limit, context_overflow, // …), a wider vocabulary than connection tests; unmapped classes stay visible. diff --git a/apps/desktop/src/renderer/locales/settings-jev-copy.ts b/apps/desktop/src/renderer/locales/settings-jev-copy.ts index d635242c369..987f10d8a1e 100644 --- a/apps/desktop/src/renderer/locales/settings-jev-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-jev-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export const JEV_COPY = resolveUiMessageCatalog(defineUiMessageCatalog>()({ 'zh-CN': { @@ -44,4 +44,4 @@ export const JEV_COPY = resolveUiMessageCatalog(defineUiMessageCatalog>; diff --git a/apps/desktop/src/renderer/locales/settings-memory-copy.ts b/apps/desktop/src/renderer/locales/settings-memory-copy.ts index c6dd15eefa3..25395c72851 100644 --- a/apps/desktop/src/renderer/locales/settings-memory-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-memory-copy.ts @@ -20,7 +20,7 @@ import type { LocalMemoryOperationCode, LocalMemoryState } from '@maka/core/local-memory'; import type { MemoryMutationRejectionReason } from '@maka/runtime-host/protocol'; -import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type MemoryTextKey = | 'localFile' | 'localFileHelp' | 'enableLocalFile' | 'agentReadable' | 'agentReadableHelp' | 'enableAgentRead' @@ -227,7 +227,7 @@ const settingsMemoryCopyEn: MemorySettingsCopy = { origins: { manual: 'Manual entry', imported: 'Imported entry', extracted: 'Confirmed extraction', unknown: 'Handwritten entry' }, entryStatuses: { draft: 'Draft', review_required: 'Needs review', active: 'Active', archived: 'Archived', rejected: 'Rejected', unknown: 'Unrecognized' }, backupKinds: { reset: 'Before reset', restore: 'Before restore', save: 'Before save' }, memoryStatuses: { ok: 'Local file ready', disabled: 'Off', safe_mode: 'Safe mode', incognito_blocked: 'Disabled in incognito', error: 'Read failed' }, promptBlocked: { disabled: 'Local memory is disabled.', incognito: 'Local memory is never added in incognito mode.', safeMode: 'MEMORY.md is too large and will not be added.', agentRead: 'Model context access is disabled.' }, backupOversize: 'Backup is too large to preview entries', previewOversize: 'The draft is too large, so entry preview is paused. Reduce MEMORY.md before saving.', previewTruncationMarker: '[Local memory truncated to the length limit]', }; -const SETTINGS_MEMORY_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsMemoryCopyZhCn, 'zh-TW': settingsMemoryCopyZhTw, en: settingsMemoryCopyEn })); +const SETTINGS_MEMORY_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsMemoryCopyZhCn, 'zh-TW': settingsMemoryCopyZhTw, en: settingsMemoryCopyEn })) satisfies UiCatalog; export function getMemorySettingsCopy(locale: UiLocale): MemorySettingsCopy { return SETTINGS_MEMORY_COPY[locale]; } diff --git a/apps/desktop/src/renderer/locales/settings-navigation-copy.ts b/apps/desktop/src/renderer/locales/settings-navigation-copy.ts index 840373d767c..1762c000c66 100644 --- a/apps/desktop/src/renderer/locales/settings-navigation-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-navigation-copy.ts @@ -18,7 +18,7 @@ */ import type { SettingsSection } from '@maka/core/settings'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { SettingsNavGroup } from '../settings/nav-group-summary.js'; export type SettingsNavigationCopy = { @@ -108,7 +108,7 @@ const SETTINGS_NAVIGATION_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessa about: { label: 'About', description: 'Version, updates, and support.' }, }, }, -})); +})) satisfies UiCatalog; export function getSettingsNavigationCopy(locale: UiLocale): SettingsNavigationCopy { return SETTINGS_NAVIGATION_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts index 8b654e7e51d..cc261078b93 100644 --- a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts @@ -18,7 +18,7 @@ */ import type { AppIcon, ThemePalette, ThemePreference } from '@maka/core/settings'; -import { type UiLocale, type UiLocalePreference, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, type UiLocalePreference, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type OptionCopy = { label: string; help: string }; @@ -541,7 +541,7 @@ const settingsPreferencesCopyByLocaleEn: SettingsPreferencesCopy = { password: { copyFailed: 'Copy failed', clipboardUnavailable: 'The clipboard is unavailable or access was denied.', copying: 'Copying', copied: 'Copied', copy: 'Copy', hide: 'Hide', show: 'Show', value: 'credential value' }, }; -const SETTINGS_PREFERENCES_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsPreferencesCopyByLocaleZhCn, 'zh-TW': settingsPreferencesCopyByLocaleZhTw, en: settingsPreferencesCopyByLocaleEn })); +const SETTINGS_PREFERENCES_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsPreferencesCopyByLocaleZhCn, 'zh-TW': settingsPreferencesCopyByLocaleZhTw, en: settingsPreferencesCopyByLocaleEn })) satisfies UiCatalog; export function getSettingsPreferencesCopy(locale: UiLocale): SettingsPreferencesCopy { return SETTINGS_PREFERENCES_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-projects-copy.ts b/apps/desktop/src/renderer/locales/settings-projects-copy.ts index 2153e8155c1..78f35f20d79 100644 --- a/apps/desktop/src/renderer/locales/settings-projects-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-projects-copy.ts @@ -18,7 +18,7 @@ */ import type { RuntimeHostServiceErrorCode } from '@maka/runtime-host/operator'; -import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type SettingsProjectsCopy = { runtimeHost: { @@ -1367,7 +1367,7 @@ const SETTINGS_PROJECTS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessage 'Add a project folder and new tasks can start in it, with the sidebar grouping tasks by project.', moreActions: (projectName: string) => `More actions for ${projectName}`, }, -})); +})) satisfies UiCatalog; export function getSettingsProjectsCopy(locale: UiLocale): SettingsProjectsCopy { return SETTINGS_PROJECTS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-shared-copy.ts b/apps/desktop/src/renderer/locales/settings-shared-copy.ts index 59918e61921..820f547309e 100644 --- a/apps/desktop/src/renderer/locales/settings-shared-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-shared-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type SettingsSharedCopy = { modalLabel: string; @@ -188,7 +188,7 @@ const SETTINGS_SHARED_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCa reviewScheduleHelp: 'When the daily review runs, and which model writes it.', }, }, -})); +})) satisfies UiCatalog; export function getSettingsSharedCopy(locale: UiLocale): SettingsSharedCopy { return SETTINGS_SHARED_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-subagents-copy.ts b/apps/desktop/src/renderer/locales/settings-subagents-copy.ts index 616fcccb359..c7a47b9ff6d 100644 --- a/apps/desktop/src/renderer/locales/settings-subagents-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-subagents-copy.ts @@ -21,7 +21,7 @@ import type { SubagentProfile } from '@maka/core/subagent-settings'; import type { ThinkingLevel } from '@maka/core/model-thinking'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type ProfileCopy = { label: string; @@ -346,7 +346,7 @@ const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessag max: 'Maximum', }, }, -})); +})) satisfies UiCatalog; export function getSubagentSettingsCopy(locale: UiLocale): SubagentSettingsCopy { return SETTINGS_SUBAGENTS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-tasks-copy.ts b/apps/desktop/src/renderer/locales/settings-tasks-copy.ts index a3558f8af2c..f5553eaf417 100644 --- a/apps/desktop/src/renderer/locales/settings-tasks-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-tasks-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; /** * Restoring and deleting a single task speak through the rail's own row @@ -231,7 +231,7 @@ const SETTINGS_TASKS_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCat emptyTitle: 'Nothing archived', emptyBody: 'Archive a task from the rail to restore or permanently delete it here.', }, -})); +})) satisfies UiCatalog; export function getSettingsTasksCopy(locale: UiLocale): SettingsTasksCopy { return SETTINGS_TASKS_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-test-result-copy.ts b/apps/desktop/src/renderer/locales/settings-test-result-copy.ts index 2ac7a296ce4..5105a7165ff 100644 --- a/apps/desktop/src/renderer/locales/settings-test-result-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-test-result-copy.ts @@ -20,7 +20,7 @@ import type { SettingsTestResult, SettingsTestResultCode } from '@maka/core/settings'; import type { BotTestErrorCode } from '@maka/runtime/bots'; -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { lookupCopy } from '@maka/core/ui-locale'; type SettingsTestResultCopy = { @@ -161,7 +161,7 @@ const copyEn: SettingsTestResultCopy = { }, }; -const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })); +const COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': copyZhCn, 'zh-TW': copyZhTw, en: copyEn })) satisfies UiCatalog; export function settingsTestResultMessage( result: SettingsTestResult, diff --git a/apps/desktop/src/renderer/locales/settings-usage-copy.ts b/apps/desktop/src/renderer/locales/settings-usage-copy.ts index 82e9ce29f7b..9f9105cff91 100644 --- a/apps/desktop/src/renderer/locales/settings-usage-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-usage-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export type UsageSettingsCopy = { staleTitle: string; staleBody: string; loadFailed: string; capacityBody: string; retainedBody: string; @@ -114,7 +114,7 @@ const settingsUsageCopyEn: UsageSettingsCopy = { }, }; -const SETTINGS_USAGE_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsUsageCopyZhCn, 'zh-TW': settingsUsageCopyZhTw, en: settingsUsageCopyEn })); +const SETTINGS_USAGE_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': settingsUsageCopyZhCn, 'zh-TW': settingsUsageCopyZhTw, en: settingsUsageCopyEn })) satisfies UiCatalog; export function getUsageSettingsCopy(locale: UiLocale): UsageSettingsCopy { return SETTINGS_USAGE_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/settings-web-search-copy.ts b/apps/desktop/src/renderer/locales/settings-web-search-copy.ts index 68aab496bad..deff0a549c5 100644 --- a/apps/desktop/src/renderer/locales/settings-web-search-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-web-search-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { WebSearchCredentialStatus, WebSearchErrorReason } from '@maka/core/web-search'; @@ -89,6 +89,6 @@ const SETTINGS_WEB_SEARCH_COPY = resolveUiMessageCatalog(defineUiMessageCatalog< sources: { model: 'Source: current model connection', envWithSaved: 'Source: environment variable (saved key available as backup)', env: 'Source: environment variable', saved: 'Source: key saved on this device', none: 'Source: not configured' }, errors: { invalid_query: 'Enter a valid search query.', incognito_active: 'Web search is unavailable in incognito mode.', not_configured: 'The selected search source is not configured.', invalid_credentials: 'The search provider rejected the current credential. Update it and try again.', rate_limited: 'The search provider is receiving too many requests. Try again later.', network_error: 'The network request failed. Check your connection and try again.', timeout: 'The search request timed out. Try again.', unsupported_provider: 'The current model does not support hosted search, or Maka has not implemented its protocol yet. Select Tavily to continue.', experimental_disabled: 'The experimental web search feature is currently disabled.' }, }, -})); +})) satisfies UiCatalog; export function getWebSearchSettingsCopy(locale: UiLocale): WebSearchSettingsCopy { return SETTINGS_WEB_SEARCH_COPY[locale]; } diff --git a/apps/desktop/src/renderer/locales/shell-copy.ts b/apps/desktop/src/renderer/locales/shell-copy.ts index 5fff0cd9acf..379bab86b45 100644 --- a/apps/desktop/src/renderer/locales/shell-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, lookupCopy, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { type PermissionMode } from '@maka/core/permission'; @@ -2366,7 +2366,7 @@ const shellCopyByLocaleEn: ShellCopy = { }, }; -const SHELL_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': shellCopyByLocaleZhCn, 'zh-TW': shellCopyByLocaleZhTw, en: shellCopyByLocaleEn })); +const SHELL_COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': shellCopyByLocaleZhCn, 'zh-TW': shellCopyByLocaleZhTw, en: shellCopyByLocaleEn })) satisfies UiCatalog; export function getShellCopy(locale: UiLocale): ShellCopy { return SHELL_COPY_BY_LOCALE[locale]; diff --git a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts index 3336452d096..e857bc2aa92 100644 --- a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; type WidenCopy = T extends string ? string @@ -240,7 +240,7 @@ const COPY = resolveUiMessageCatalog(defineUiMessageCatalog( 'zh-CN': zhCopy, 'zh-TW': zhTwCopy, en: enCopy, -})); +})) satisfies UiCatalog; export function getShellRemainingCopy(locale: UiLocale): ShellRemainingCopy { return COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/storage-usage-copy.ts b/apps/desktop/src/renderer/locales/storage-usage-copy.ts index dd86634a37d..6f536380724 100644 --- a/apps/desktop/src/renderer/locales/storage-usage-copy.ts +++ b/apps/desktop/src/renderer/locales/storage-usage-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import type { StorageUsageKind } from '@maka/runtime-host/protocol'; export type StorageUsageCopy = { @@ -140,7 +140,7 @@ const STORAGE_USAGE_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getStorageUsageCopy(locale: UiLocale): StorageUsageCopy { return STORAGE_USAGE_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/task-readiness-copy.ts b/apps/desktop/src/renderer/locales/task-readiness-copy.ts index 3f8fcc34ace..a7f4a25ca07 100644 --- a/apps/desktop/src/renderer/locales/task-readiness-copy.ts +++ b/apps/desktop/src/renderer/locales/task-readiness-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface TaskReadinessCopy { readonly runtime: { @@ -69,7 +69,7 @@ const TASK_READINESS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getTaskReadinessCopy(locale: UiLocale): TaskReadinessCopy { return TASK_READINESS_COPY[locale]; diff --git a/apps/desktop/src/renderer/locales/work-board-error-copy.ts b/apps/desktop/src/renderer/locales/work-board-error-copy.ts index d1542683a93..a76516defd2 100644 --- a/apps/desktop/src/renderer/locales/work-board-error-copy.ts +++ b/apps/desktop/src/renderer/locales/work-board-error-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import { type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; import { lookupCopy } from '@maka/core/ui-locale'; import type { WorkBoardErrorCode } from '../../shared/work-board-ipc.js'; @@ -48,7 +48,7 @@ const WORK_BOARD_ERROR_COPY = resolveUiMessageCatalog(defineUiMessageCatalog; export function getWorkBoardErrorCopy(locale: UiLocale): WorkBoardErrorCopy { return WORK_BOARD_ERROR_COPY[locale]; From 476cbe246acae79da589f48f1f53d62c1ac0c179 Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:45:31 +0900 Subject: [PATCH 3/5] fix(desktop): leave provider-display-copy on the satisfies form It lives outside locales/, so a runtime @maka/core/ui-locale import would be new debt under the architecture ratchet. Its exported types are unchanged. Co-Authored-By: Claude Sonnet 5.5 --- .../settings/provider-display-copy.ts | 624 +++++++++--------- 1 file changed, 312 insertions(+), 312 deletions(-) diff --git a/apps/desktop/src/renderer/settings/provider-display-copy.ts b/apps/desktop/src/renderer/settings/provider-display-copy.ts index 44b8b40c5b3..766cffa4f4a 100644 --- a/apps/desktop/src/renderer/settings/provider-display-copy.ts +++ b/apps/desktop/src/renderer/settings/provider-display-copy.ts @@ -19,7 +19,7 @@ import type { ProviderType } from '@maka/core/llm-connections'; -import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; +import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; /** * Pure-data provider introduction copy, localized zh / en. @@ -47,327 +47,327 @@ export interface ProviderCopy { badge?: string; } -export const UNKNOWN_PROVIDER_DESCRIPTION = resolveUiMessageCatalog(defineUiMessageCatalog()({ +export const UNKNOWN_PROVIDER_DESCRIPTION = { 'zh-CN': '该 provider 在当前版本未注册。', 'zh-TW': '該 provider 在目前版本未註冊。', en: 'This provider is not registered in the current build.', -})); +} satisfies UiCatalog; export const PROVIDER_DISPLAY_COPY = { - 'kimi-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 相容', badge: 'Coding' }, - en: { name: 'Kimi Coding Plan', description: 'Moonshot · Anthropic-compatible', badge: 'Coding' }, - })), - 'minimax-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 相容', badge: 'Coding' }, - en: { name: 'MiniMax Coding Plan', description: 'MiniMax coding plan · Anthropic-compatible', badge: 'Coding' }, - })), - deepseek: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'DeepSeek', description: 'DeepSeek 官方接入', badge: 'API' }, - 'zh-TW': { name: 'DeepSeek', description: 'DeepSeek 官方 API 連線', badge: 'API' }, - en: { name: 'DeepSeek', description: 'Official DeepSeek API access.', badge: 'API' }, - })), - moonshot: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Moonshot', description: 'Moonshot 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Moonshot', description: 'Moonshot 官方 API 連線', badge: 'API' }, - en: { name: 'Moonshot', description: 'Official Moonshot API access.', badge: 'API' }, - })), - 'moonshot-global': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Moonshot 国际版', description: 'Moonshot 国际版官方 API 接入', badge: 'API' }, - 'zh-TW': { name: 'Moonshot 國際版', description: 'Moonshot 國際版官方 API 連線', badge: 'API' }, - en: { name: 'Moonshot Global', description: 'Official Moonshot international API access.', badge: 'API' }, - })), - 'zai-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Z.AI Coding Plan', description: '智谱 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Z.AI Coding Plan', description: '智譜 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Z.AI Coding Plan', description: 'Zhipu · OpenAI-compatible', badge: 'Coding' }, - })), - MiniMax: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'MiniMax', description: 'MiniMax · Anthropic 兼容', badge: 'API' }, - 'zh-TW': { name: 'MiniMax', description: 'MiniMax · Anthropic 相容', badge: 'API' }, - en: { name: 'MiniMax', description: 'MiniMax · Anthropic-compatible', badge: 'API' }, - })), - 'MiniMax-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'MiniMax 中国站', description: 'MiniMax 中国站 · Anthropic 兼容', badge: 'API' }, - 'zh-TW': { name: 'MiniMax 中國站', description: 'MiniMax 中國站 · Anthropic 相容', badge: 'API' }, - en: { name: 'MiniMax China', description: 'MiniMax China · Anthropic-compatible', badge: 'API' }, - })), - siliconflow: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'SiliconFlow', description: '硅基流动多模型 API,支持精确模型 ID。', badge: '聚合' }, - 'zh-TW': { name: 'SiliconFlow', description: '矽基流動多模型 API,支援精確模型 ID。', badge: '聚合' }, - en: { name: 'SiliconFlow', description: 'Hosted multi-model API with exact upstream model ids.', badge: 'Aggregator' }, - })), - anthropic: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Anthropic', description: 'Anthropic 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Anthropic', description: 'Anthropic 官方 API 連線', badge: 'API' }, - en: { name: 'Anthropic', description: 'Official Anthropic API access.', badge: 'API' }, - })), - openai: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenAI', description: 'OpenAI 官方接入', badge: 'API' }, - 'zh-TW': { name: 'OpenAI', description: 'OpenAI 官方 API 連線', badge: 'API' }, - en: { name: 'OpenAI', description: 'Official OpenAI API access.', badge: 'API' }, - })), - google: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Google Gemini', description: 'Google AI Studio 接入', badge: 'API' }, - 'zh-TW': { name: 'Google Gemini', description: 'Google AI Studio API 連線', badge: 'API' }, - en: { name: 'Google Gemini', description: 'Google AI Studio API access.', badge: 'API' }, - })), - xai: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'xAI', description: 'xAI 官方接入,Grok 系列模型', badge: 'API' }, - 'zh-TW': { name: 'xAI', description: 'xAI 官方 API 連線,支援 Grok 系列模型', badge: 'API' }, - en: { name: 'xAI', description: 'Official xAI API access for Grok models.', badge: 'API' }, - })), - zai: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Z.AI', description: '智谱官方接入,GLM 系列模型', badge: 'API' }, - 'zh-TW': { name: 'Z.AI', description: '智譜官方 API 連線,支援 GLM 系列模型', badge: 'API' }, - en: { name: 'Z.AI', description: 'Official Z.AI API access for GLM models.', badge: 'API' }, - })), - xiaomi: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Xiaomi', description: '小米官方接入,MiMo 系列模型', badge: 'API' }, - 'zh-TW': { name: 'Xiaomi', description: '小米官方 API 連線,支援 MiMo 系列模型', badge: 'API' }, - en: { name: 'Xiaomi', description: 'Official Xiaomi API access for MiMo models.', badge: 'API' }, - })), - 'xiaomi-token-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Xiaomi Token Plan 中国', description: '小米 MiMo Token Plan 订阅 · 中国 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 中國', description: '小米 MiMo Token Plan 訂閱 · 中國 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (China)', description: 'Xiaomi MiMo Token Plan subscription (China) for coding tools.', badge: 'Token' }, - })), - 'xiaomi-token-plan-sgp': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 订阅 · 新加坡 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 訂閱 · 新加坡 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (Singapore)', description: 'Xiaomi MiMo Token Plan subscription (Singapore) for coding tools.', badge: 'Token' }, - })), - 'xiaomi-token-plan-ams': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Xiaomi Token Plan 欧洲', description: '小米 MiMo Token Plan 订阅 · 欧洲 · 编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Xiaomi Token Plan 歐洲', description: '小米 MiMo Token Plan 訂閱 · 歐洲 · 編碼工具', badge: 'Token' }, - en: { name: 'Xiaomi Token Plan (Europe)', description: 'Xiaomi MiMo Token Plan subscription (Europe) for coding tools.', badge: 'Token' }, - })), - cerebras: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Cerebras', description: '高速推理托管开源模型', badge: 'API' }, - 'zh-TW': { name: 'Cerebras', description: '高速推理託管開源模型', badge: 'API' }, - en: { name: 'Cerebras', description: 'Fast hosted open-model inference.', badge: 'API' }, - })), - mistral: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Mistral', description: 'Mistral 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Mistral', description: 'Mistral 官方 API 連線', badge: 'API' }, - en: { name: 'Mistral', description: 'Official Mistral API access.', badge: 'API' }, - })), - togetherai: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Together AI', description: '托管开源模型 API', badge: 'API' }, - 'zh-TW': { name: 'Together AI', description: '託管開源模型 API', badge: 'API' }, - en: { name: 'Together AI', description: 'Hosted open models over one API.', badge: 'API' }, - })), - ollama: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Ollama', description: '本机运行 · 离线可用', badge: 'Local' }, - 'zh-TW': { name: 'Ollama', description: '本機執行 · 離線可用', badge: 'Local' }, - en: { name: 'Ollama', description: 'Runs locally · works offline', badge: 'Local' }, - })), - 'lm-studio': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'LM Studio', description: '本机 LM Studio 服务 · 离线可用', badge: 'Local' }, - 'zh-TW': { name: 'LM Studio', description: '本機 LM Studio 服務 · 離線可用', badge: 'Local' }, - en: { name: 'LM Studio', description: 'Local models served by LM Studio.', badge: 'Local' }, - })), - localai: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'LocalAI', description: '本机 LocalAI 服务,可选密钥保护', badge: 'Local' }, - 'zh-TW': { name: 'LocalAI', description: '本機 LocalAI 服務,可選金鑰保護', badge: 'Local' }, - en: { name: 'LocalAI', description: 'Local models served by LocalAI, with optional API key.', badge: 'Local' }, - })), - custom: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: '自定义连接', description: '中转站、代理服务或自部署网关,支持 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可按模型选择协议。', badge: '自定义' }, - 'zh-TW': { name: '自訂連線', description: '中轉站、代理服務或自部署閘道器,支援 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可依模型選擇協定。', badge: '自訂' }, - en: { name: 'Custom connection', description: 'Relay, proxy, or self-hosted gateway speaking OpenAI Chat, OpenAI Responses, or Anthropic Messages, chosen per model.', badge: 'Custom' }, - })), - 'fireworks-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Fireworks AI', description: 'Serverless 开源模型托管', badge: 'API' }, - 'zh-TW': { name: 'Fireworks AI', description: 'Serverless 開源模型託管', badge: 'API' }, - en: { name: 'Fireworks AI', description: 'Serverless open models with exact Fireworks model paths.', badge: 'API' }, - })), - nvidia: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'NVIDIA', description: 'NVIDIA 官方托管模型接入', badge: 'API' }, - 'zh-TW': { name: 'NVIDIA', description: 'NVIDIA 官方託管模型服務', badge: 'API' }, - en: { name: 'NVIDIA', description: 'NVIDIA-hosted models API access.', badge: 'API' }, - })), - 'tencent-tokenhub': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Tencent TokenHub', description: '腾讯云 TokenHub 按量接入,混元等模型', badge: 'API' }, - 'zh-TW': { name: 'Tencent TokenHub', description: '騰訊雲 TokenHub 按量計費模型服務,包含混元等模型', badge: 'API' }, - en: { name: 'Tencent TokenHub', description: 'Tencent Cloud TokenHub pay-as-you-go access.', badge: 'API' }, - })), - stepfun: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'StepFun 中国站', description: '阶跃星辰官方接入 · 中国站', badge: 'API' }, - 'zh-TW': { name: 'StepFun 中國站', description: '階躍星辰官方 API 連線 · 中國站', badge: 'API' }, - en: { name: 'StepFun (China)', description: 'Official StepFun China API access.', badge: 'API' }, - })), - 'tencent-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Tencent Coding Plan', description: '腾讯云 Coding 套餐 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: 'Tencent Coding Plan', description: '騰訊雲 Coding 套餐 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Tencent Coding Plan', description: 'Tencent Cloud coding plan · OpenAI-compatible', badge: 'Coding' }, - })), - 'stepfun-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'StepFun 国际站', description: '阶跃星辰官方接入 · 国际站', badge: 'API' }, - 'zh-TW': { name: 'StepFun 國際站', description: '階躍星辰官方 API 連線 · 國際站', badge: 'API' }, - en: { name: 'StepFun (Global)', description: 'Official StepFun Global API access.', badge: 'API' }, - })), - 'volcengine-ark': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: '火山方舟', description: '火山引擎官方接入,豆包等模型', badge: 'API' }, - 'zh-TW': { name: '火山方舟', description: '火山引擎官方 API 連線,包含豆包等模型', badge: 'API' }, - en: { name: 'Volcengine Ark (China)', description: 'Volcengine Ark direct API access in China.', badge: 'API' }, - })), - 'volcengine-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 订阅 · OpenAI 兼容', badge: 'Coding' }, - 'zh-TW': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 訂閱 · OpenAI 相容', badge: 'Coding' }, - en: { name: 'Volcengine Ark Coding Plan (China)', description: 'Volcengine Ark coding subscription · OpenAI-compatible', badge: 'Coding' }, - })), - 'volcengine-agent-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 订阅 · Responses API', badge: 'Agent' }, - 'zh-TW': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 訂閱 · Responses API', badge: 'Agent' }, - en: { name: 'Volcengine Ark Agent Plan (China)', description: 'Volcengine Ark agent subscription · Responses API', badge: 'Agent' }, - })), - 'tencent-token-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Tencent Token Plan', description: '腾讯云 Token 套餐,个人智能体与编码工具', badge: 'Token' }, - 'zh-TW': { name: 'Tencent Token Plan', description: '騰訊雲 Token 套餐,個人智慧體與編碼工具', badge: 'Token' }, - en: { name: 'Tencent Token Plan', description: 'Tencent Cloud token plan for personal agents and coding tools.', badge: 'Token' }, - })), - 'stepfun-step-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'StepFun Step Plan 中国站', description: '阶跃星辰订阅套餐 · 中国站', badge: 'Plan' }, - 'zh-TW': { name: 'StepFun Step Plan 中國站', description: '階躍星辰訂閱套餐 · 中國站', badge: 'Plan' }, - en: { name: 'StepFun Step Plan (China)', description: 'StepFun China subscription for coding and agent tools.', badge: 'Plan' }, - })), - deepinfra: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'DeepInfra', description: '开源模型托管推理 · OpenAI 兼容', badge: 'API' }, - 'zh-TW': { name: 'DeepInfra', description: '開源模型託管推理 · OpenAI 相容', badge: 'API' }, - en: { name: 'DeepInfra', description: 'Hosted open-model inference · OpenAI-compatible', badge: 'API' }, - })), - cohere: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Cohere', description: 'Cohere 官方接入', badge: 'API' }, - 'zh-TW': { name: 'Cohere', description: 'Cohere 官方 API 連線', badge: 'API' }, - en: { name: 'Cohere', description: 'Official Cohere Chat API access.', badge: 'API' }, - })), - vercel: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Vercel AI Gateway', description: '一个密钥接入多家托管模型', badge: '网关' }, - 'zh-TW': { name: 'Vercel AI Gateway', description: '使用一組金鑰連線多家託管模型', badge: '閘道器' }, - en: { name: 'Vercel AI Gateway', description: 'One API key for hosted models with exact creator/model ids.', badge: 'Gateway' }, - })), - 'stepfun-ai-step-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'StepFun Step Plan 国际站', description: '阶跃星辰订阅套餐 · 国际站', badge: 'Plan' }, - 'zh-TW': { name: 'StepFun Step Plan 國際站', description: '階躍星辰訂閱套餐 · 國際站', badge: 'Plan' }, - en: { name: 'StepFun Step Plan (Global)', description: 'StepFun Global subscription for coding and agent tools.', badge: 'Plan' }, - })), - 'cloudflare-workers-ai': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Cloudflare Workers AI', description: 'Cloudflare 托管模型,账户级接入', badge: 'API' }, - 'zh-TW': { name: 'Cloudflare Workers AI', description: 'Cloudflare 託管模型,使用帳號層級連線', badge: 'API' }, - en: { name: 'Cloudflare Workers AI', description: 'Cloudflare-hosted models over the account-scoped API.', badge: 'API' }, - })), - huggingface: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家托管模型', badge: '路由' }, - 'zh-TW': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家託管模型', badge: '路由' }, - en: { name: 'Hugging Face', description: 'Inference Providers router across hosted models.', badge: 'Router' }, - })), - 'ollama-cloud': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Ollama Cloud', description: 'Ollama 官方云端托管模型', badge: 'API' }, - 'zh-TW': { name: 'Ollama Cloud', description: 'Ollama 官方雲端託管模型', badge: 'API' }, - en: { name: 'Ollama Cloud', description: 'Ollama-hosted cloud models over the official remote API.', badge: 'API' }, - })), - zenmux: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'ZenMux', description: '模型路由网关,一个密钥接入多家模型', badge: '网关' }, - 'zh-TW': { name: 'ZenMux', description: '模型路由閘道器,使用一組金鑰連線多家模型', badge: '閘道器' }, - en: { name: 'ZenMux', description: 'One API key for routed models with exact creator/model ids.', badge: 'Gateway' }, - })), - opencode: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenCode Zen', description: '面向编码智能体的按量模型精选', badge: 'Plan' }, - 'zh-TW': { name: 'OpenCode Zen', description: '面向編碼智慧體的按量模型精選', badge: 'Plan' }, - en: { name: 'OpenCode Zen', description: 'Curated pay-as-you-go models for coding agents.', badge: 'Plan' }, - })), - 'opencode-go': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenCode Go', description: '低价订阅制的开源编码模型精选', badge: 'Plan' }, - 'zh-TW': { name: 'OpenCode Go', description: '低價訂閱制的開源編碼模型精選', badge: 'Plan' }, - en: { name: 'OpenCode Go', description: 'Low-cost subscription to curated open coding models.', badge: 'Plan' }, - })), - 'opencode-free': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenCode Free', description: '已停用:OpenCode 免费服务仅限其官方客户端使用。', badge: 'Free' }, - 'zh-TW': { name: 'OpenCode Free', description: '已停用:OpenCode 免費服務僅限其官方用戶端使用。', badge: 'Free' }, - en: { name: 'OpenCode Free', description: 'Retired: OpenCode restricts its free tier to its own client.', badge: 'Free' }, - })), - commandcode: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Command Code', description: '使用 Command Code 套餐额度,连接后自动获取模型。', badge: 'Coding' }, - 'zh-TW': { name: 'Command Code', description: '使用 Command Code 方案額度,連線後自動取得模型。', badge: 'Coding' }, - en: { name: 'Command Code', description: 'Use your Command Code plan credits. Models are fetched when you connect.', badge: 'Coding' }, - })), + 'kimi-coding-plan': { + 'zh-CN': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Kimi Coding Plan', description: '月之暗面 · Anthropic 相容', badge: 'Coding' }, + en: { name: 'Kimi Coding Plan', description: 'Moonshot · Anthropic-compatible', badge: 'Coding' }, + }, + 'minimax-coding-plan': { + 'zh-CN': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'MiniMax Coding Plan', description: 'MiniMax Coding 套餐 · Anthropic 相容', badge: 'Coding' }, + en: { name: 'MiniMax Coding Plan', description: 'MiniMax coding plan · Anthropic-compatible', badge: 'Coding' }, + }, + deepseek: { + 'zh-CN': { name: 'DeepSeek', description: 'DeepSeek 官方接入', badge: 'API' }, + 'zh-TW': { name: 'DeepSeek', description: 'DeepSeek 官方 API 連線', badge: 'API' }, + en: { name: 'DeepSeek', description: 'Official DeepSeek API access.', badge: 'API' }, + }, + moonshot: { + 'zh-CN': { name: 'Moonshot', description: 'Moonshot 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Moonshot', description: 'Moonshot 官方 API 連線', badge: 'API' }, + en: { name: 'Moonshot', description: 'Official Moonshot API access.', badge: 'API' }, + }, + 'moonshot-global': { + 'zh-CN': { name: 'Moonshot 国际版', description: 'Moonshot 国际版官方 API 接入', badge: 'API' }, + 'zh-TW': { name: 'Moonshot 國際版', description: 'Moonshot 國際版官方 API 連線', badge: 'API' }, + en: { name: 'Moonshot Global', description: 'Official Moonshot international API access.', badge: 'API' }, + }, + 'zai-coding-plan': { + 'zh-CN': { name: 'Z.AI Coding Plan', description: '智谱 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Z.AI Coding Plan', description: '智譜 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Z.AI Coding Plan', description: 'Zhipu · OpenAI-compatible', badge: 'Coding' }, + }, + MiniMax: { + 'zh-CN': { name: 'MiniMax', description: 'MiniMax · Anthropic 兼容', badge: 'API' }, + 'zh-TW': { name: 'MiniMax', description: 'MiniMax · Anthropic 相容', badge: 'API' }, + en: { name: 'MiniMax', description: 'MiniMax · Anthropic-compatible', badge: 'API' }, + }, + 'MiniMax-cn': { + 'zh-CN': { name: 'MiniMax 中国站', description: 'MiniMax 中国站 · Anthropic 兼容', badge: 'API' }, + 'zh-TW': { name: 'MiniMax 中國站', description: 'MiniMax 中國站 · Anthropic 相容', badge: 'API' }, + en: { name: 'MiniMax China', description: 'MiniMax China · Anthropic-compatible', badge: 'API' }, + }, + siliconflow: { + 'zh-CN': { name: 'SiliconFlow', description: '硅基流动多模型 API,支持精确模型 ID。', badge: '聚合' }, + 'zh-TW': { name: 'SiliconFlow', description: '矽基流動多模型 API,支援精確模型 ID。', badge: '聚合' }, + en: { name: 'SiliconFlow', description: 'Hosted multi-model API with exact upstream model ids.', badge: 'Aggregator' }, + }, + anthropic: { + 'zh-CN': { name: 'Anthropic', description: 'Anthropic 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Anthropic', description: 'Anthropic 官方 API 連線', badge: 'API' }, + en: { name: 'Anthropic', description: 'Official Anthropic API access.', badge: 'API' }, + }, + openai: { + 'zh-CN': { name: 'OpenAI', description: 'OpenAI 官方接入', badge: 'API' }, + 'zh-TW': { name: 'OpenAI', description: 'OpenAI 官方 API 連線', badge: 'API' }, + en: { name: 'OpenAI', description: 'Official OpenAI API access.', badge: 'API' }, + }, + google: { + 'zh-CN': { name: 'Google Gemini', description: 'Google AI Studio 接入', badge: 'API' }, + 'zh-TW': { name: 'Google Gemini', description: 'Google AI Studio API 連線', badge: 'API' }, + en: { name: 'Google Gemini', description: 'Google AI Studio API access.', badge: 'API' }, + }, + xai: { + 'zh-CN': { name: 'xAI', description: 'xAI 官方接入,Grok 系列模型', badge: 'API' }, + 'zh-TW': { name: 'xAI', description: 'xAI 官方 API 連線,支援 Grok 系列模型', badge: 'API' }, + en: { name: 'xAI', description: 'Official xAI API access for Grok models.', badge: 'API' }, + }, + zai: { + 'zh-CN': { name: 'Z.AI', description: '智谱官方接入,GLM 系列模型', badge: 'API' }, + 'zh-TW': { name: 'Z.AI', description: '智譜官方 API 連線,支援 GLM 系列模型', badge: 'API' }, + en: { name: 'Z.AI', description: 'Official Z.AI API access for GLM models.', badge: 'API' }, + }, + xiaomi: { + 'zh-CN': { name: 'Xiaomi', description: '小米官方接入,MiMo 系列模型', badge: 'API' }, + 'zh-TW': { name: 'Xiaomi', description: '小米官方 API 連線,支援 MiMo 系列模型', badge: 'API' }, + en: { name: 'Xiaomi', description: 'Official Xiaomi API access for MiMo models.', badge: 'API' }, + }, + 'xiaomi-token-plan-cn': { + 'zh-CN': { name: 'Xiaomi Token Plan 中国', description: '小米 MiMo Token Plan 订阅 · 中国 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 中國', description: '小米 MiMo Token Plan 訂閱 · 中國 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (China)', description: 'Xiaomi MiMo Token Plan subscription (China) for coding tools.', badge: 'Token' }, + }, + 'xiaomi-token-plan-sgp': { + 'zh-CN': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 订阅 · 新加坡 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 新加坡', description: '小米 MiMo Token Plan 訂閱 · 新加坡 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (Singapore)', description: 'Xiaomi MiMo Token Plan subscription (Singapore) for coding tools.', badge: 'Token' }, + }, + 'xiaomi-token-plan-ams': { + 'zh-CN': { name: 'Xiaomi Token Plan 欧洲', description: '小米 MiMo Token Plan 订阅 · 欧洲 · 编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Xiaomi Token Plan 歐洲', description: '小米 MiMo Token Plan 訂閱 · 歐洲 · 編碼工具', badge: 'Token' }, + en: { name: 'Xiaomi Token Plan (Europe)', description: 'Xiaomi MiMo Token Plan subscription (Europe) for coding tools.', badge: 'Token' }, + }, + cerebras: { + 'zh-CN': { name: 'Cerebras', description: '高速推理托管开源模型', badge: 'API' }, + 'zh-TW': { name: 'Cerebras', description: '高速推理託管開源模型', badge: 'API' }, + en: { name: 'Cerebras', description: 'Fast hosted open-model inference.', badge: 'API' }, + }, + mistral: { + 'zh-CN': { name: 'Mistral', description: 'Mistral 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Mistral', description: 'Mistral 官方 API 連線', badge: 'API' }, + en: { name: 'Mistral', description: 'Official Mistral API access.', badge: 'API' }, + }, + togetherai: { + 'zh-CN': { name: 'Together AI', description: '托管开源模型 API', badge: 'API' }, + 'zh-TW': { name: 'Together AI', description: '託管開源模型 API', badge: 'API' }, + en: { name: 'Together AI', description: 'Hosted open models over one API.', badge: 'API' }, + }, + ollama: { + 'zh-CN': { name: 'Ollama', description: '本机运行 · 离线可用', badge: 'Local' }, + 'zh-TW': { name: 'Ollama', description: '本機執行 · 離線可用', badge: 'Local' }, + en: { name: 'Ollama', description: 'Runs locally · works offline', badge: 'Local' }, + }, + 'lm-studio': { + 'zh-CN': { name: 'LM Studio', description: '本机 LM Studio 服务 · 离线可用', badge: 'Local' }, + 'zh-TW': { name: 'LM Studio', description: '本機 LM Studio 服務 · 離線可用', badge: 'Local' }, + en: { name: 'LM Studio', description: 'Local models served by LM Studio.', badge: 'Local' }, + }, + localai: { + 'zh-CN': { name: 'LocalAI', description: '本机 LocalAI 服务,可选密钥保护', badge: 'Local' }, + 'zh-TW': { name: 'LocalAI', description: '本機 LocalAI 服務,可選金鑰保護', badge: 'Local' }, + en: { name: 'LocalAI', description: 'Local models served by LocalAI, with optional API key.', badge: 'Local' }, + }, + custom: { + 'zh-CN': { name: '自定义连接', description: '中转站、代理服务或自部署网关,支持 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可按模型选择协议。', badge: '自定义' }, + 'zh-TW': { name: '自訂連線', description: '中轉站、代理服務或自部署閘道器,支援 OpenAI Chat、OpenAI Responses 和 Anthropic Messages,可依模型選擇協定。', badge: '自訂' }, + en: { name: 'Custom connection', description: 'Relay, proxy, or self-hosted gateway speaking OpenAI Chat, OpenAI Responses, or Anthropic Messages, chosen per model.', badge: 'Custom' }, + }, + 'fireworks-ai': { + 'zh-CN': { name: 'Fireworks AI', description: 'Serverless 开源模型托管', badge: 'API' }, + 'zh-TW': { name: 'Fireworks AI', description: 'Serverless 開源模型託管', badge: 'API' }, + en: { name: 'Fireworks AI', description: 'Serverless open models with exact Fireworks model paths.', badge: 'API' }, + }, + nvidia: { + 'zh-CN': { name: 'NVIDIA', description: 'NVIDIA 官方托管模型接入', badge: 'API' }, + 'zh-TW': { name: 'NVIDIA', description: 'NVIDIA 官方託管模型服務', badge: 'API' }, + en: { name: 'NVIDIA', description: 'NVIDIA-hosted models API access.', badge: 'API' }, + }, + 'tencent-tokenhub': { + 'zh-CN': { name: 'Tencent TokenHub', description: '腾讯云 TokenHub 按量接入,混元等模型', badge: 'API' }, + 'zh-TW': { name: 'Tencent TokenHub', description: '騰訊雲 TokenHub 按量計費模型服務,包含混元等模型', badge: 'API' }, + en: { name: 'Tencent TokenHub', description: 'Tencent Cloud TokenHub pay-as-you-go access.', badge: 'API' }, + }, + stepfun: { + 'zh-CN': { name: 'StepFun 中国站', description: '阶跃星辰官方接入 · 中国站', badge: 'API' }, + 'zh-TW': { name: 'StepFun 中國站', description: '階躍星辰官方 API 連線 · 中國站', badge: 'API' }, + en: { name: 'StepFun (China)', description: 'Official StepFun China API access.', badge: 'API' }, + }, + 'tencent-coding-plan': { + 'zh-CN': { name: 'Tencent Coding Plan', description: '腾讯云 Coding 套餐 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: 'Tencent Coding Plan', description: '騰訊雲 Coding 套餐 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Tencent Coding Plan', description: 'Tencent Cloud coding plan · OpenAI-compatible', badge: 'Coding' }, + }, + 'stepfun-ai': { + 'zh-CN': { name: 'StepFun 国际站', description: '阶跃星辰官方接入 · 国际站', badge: 'API' }, + 'zh-TW': { name: 'StepFun 國際站', description: '階躍星辰官方 API 連線 · 國際站', badge: 'API' }, + en: { name: 'StepFun (Global)', description: 'Official StepFun Global API access.', badge: 'API' }, + }, + 'volcengine-ark': { + 'zh-CN': { name: '火山方舟', description: '火山引擎官方接入,豆包等模型', badge: 'API' }, + 'zh-TW': { name: '火山方舟', description: '火山引擎官方 API 連線,包含豆包等模型', badge: 'API' }, + en: { name: 'Volcengine Ark (China)', description: 'Volcengine Ark direct API access in China.', badge: 'API' }, + }, + 'volcengine-coding-plan': { + 'zh-CN': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 订阅 · OpenAI 兼容', badge: 'Coding' }, + 'zh-TW': { name: '火山方舟 Coding Plan', description: '火山引擎 Coding 訂閱 · OpenAI 相容', badge: 'Coding' }, + en: { name: 'Volcengine Ark Coding Plan (China)', description: 'Volcengine Ark coding subscription · OpenAI-compatible', badge: 'Coding' }, + }, + 'volcengine-agent-plan': { + 'zh-CN': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 订阅 · Responses API', badge: 'Agent' }, + 'zh-TW': { name: '火山方舟 Agent Plan', description: '火山引擎 Agent 訂閱 · Responses API', badge: 'Agent' }, + en: { name: 'Volcengine Ark Agent Plan (China)', description: 'Volcengine Ark agent subscription · Responses API', badge: 'Agent' }, + }, + 'tencent-token-plan': { + 'zh-CN': { name: 'Tencent Token Plan', description: '腾讯云 Token 套餐,个人智能体与编码工具', badge: 'Token' }, + 'zh-TW': { name: 'Tencent Token Plan', description: '騰訊雲 Token 套餐,個人智慧體與編碼工具', badge: 'Token' }, + en: { name: 'Tencent Token Plan', description: 'Tencent Cloud token plan for personal agents and coding tools.', badge: 'Token' }, + }, + 'stepfun-step-plan': { + 'zh-CN': { name: 'StepFun Step Plan 中国站', description: '阶跃星辰订阅套餐 · 中国站', badge: 'Plan' }, + 'zh-TW': { name: 'StepFun Step Plan 中國站', description: '階躍星辰訂閱套餐 · 中國站', badge: 'Plan' }, + en: { name: 'StepFun Step Plan (China)', description: 'StepFun China subscription for coding and agent tools.', badge: 'Plan' }, + }, + deepinfra: { + 'zh-CN': { name: 'DeepInfra', description: '开源模型托管推理 · OpenAI 兼容', badge: 'API' }, + 'zh-TW': { name: 'DeepInfra', description: '開源模型託管推理 · OpenAI 相容', badge: 'API' }, + en: { name: 'DeepInfra', description: 'Hosted open-model inference · OpenAI-compatible', badge: 'API' }, + }, + cohere: { + 'zh-CN': { name: 'Cohere', description: 'Cohere 官方接入', badge: 'API' }, + 'zh-TW': { name: 'Cohere', description: 'Cohere 官方 API 連線', badge: 'API' }, + en: { name: 'Cohere', description: 'Official Cohere Chat API access.', badge: 'API' }, + }, + vercel: { + 'zh-CN': { name: 'Vercel AI Gateway', description: '一个密钥接入多家托管模型', badge: '网关' }, + 'zh-TW': { name: 'Vercel AI Gateway', description: '使用一組金鑰連線多家託管模型', badge: '閘道器' }, + en: { name: 'Vercel AI Gateway', description: 'One API key for hosted models with exact creator/model ids.', badge: 'Gateway' }, + }, + 'stepfun-ai-step-plan': { + 'zh-CN': { name: 'StepFun Step Plan 国际站', description: '阶跃星辰订阅套餐 · 国际站', badge: 'Plan' }, + 'zh-TW': { name: 'StepFun Step Plan 國際站', description: '階躍星辰訂閱套餐 · 國際站', badge: 'Plan' }, + en: { name: 'StepFun Step Plan (Global)', description: 'StepFun Global subscription for coding and agent tools.', badge: 'Plan' }, + }, + 'cloudflare-workers-ai': { + 'zh-CN': { name: 'Cloudflare Workers AI', description: 'Cloudflare 托管模型,账户级接入', badge: 'API' }, + 'zh-TW': { name: 'Cloudflare Workers AI', description: 'Cloudflare 託管模型,使用帳號層級連線', badge: 'API' }, + en: { name: 'Cloudflare Workers AI', description: 'Cloudflare-hosted models over the account-scoped API.', badge: 'API' }, + }, + huggingface: { + 'zh-CN': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家托管模型', badge: '路由' }, + 'zh-TW': { name: 'Hugging Face', description: 'Inference Providers 路由,聚合多家託管模型', badge: '路由' }, + en: { name: 'Hugging Face', description: 'Inference Providers router across hosted models.', badge: 'Router' }, + }, + 'ollama-cloud': { + 'zh-CN': { name: 'Ollama Cloud', description: 'Ollama 官方云端托管模型', badge: 'API' }, + 'zh-TW': { name: 'Ollama Cloud', description: 'Ollama 官方雲端託管模型', badge: 'API' }, + en: { name: 'Ollama Cloud', description: 'Ollama-hosted cloud models over the official remote API.', badge: 'API' }, + }, + zenmux: { + 'zh-CN': { name: 'ZenMux', description: '模型路由网关,一个密钥接入多家模型', badge: '网关' }, + 'zh-TW': { name: 'ZenMux', description: '模型路由閘道器,使用一組金鑰連線多家模型', badge: '閘道器' }, + en: { name: 'ZenMux', description: 'One API key for routed models with exact creator/model ids.', badge: 'Gateway' }, + }, + opencode: { + 'zh-CN': { name: 'OpenCode Zen', description: '面向编码智能体的按量模型精选', badge: 'Plan' }, + 'zh-TW': { name: 'OpenCode Zen', description: '面向編碼智慧體的按量模型精選', badge: 'Plan' }, + en: { name: 'OpenCode Zen', description: 'Curated pay-as-you-go models for coding agents.', badge: 'Plan' }, + }, + 'opencode-go': { + 'zh-CN': { name: 'OpenCode Go', description: '低价订阅制的开源编码模型精选', badge: 'Plan' }, + 'zh-TW': { name: 'OpenCode Go', description: '低價訂閱制的開源編碼模型精選', badge: 'Plan' }, + en: { name: 'OpenCode Go', description: 'Low-cost subscription to curated open coding models.', badge: 'Plan' }, + }, + 'opencode-free': { + 'zh-CN': { name: 'OpenCode Free', description: '已停用:OpenCode 免费服务仅限其官方客户端使用。', badge: 'Free' }, + 'zh-TW': { name: 'OpenCode Free', description: '已停用:OpenCode 免費服務僅限其官方用戶端使用。', badge: 'Free' }, + en: { name: 'OpenCode Free', description: 'Retired: OpenCode restricts its free tier to its own client.', badge: 'Free' }, + }, + commandcode: { + 'zh-CN': { name: 'Command Code', description: '使用 Command Code 套餐额度,连接后自动获取模型。', badge: 'Coding' }, + 'zh-TW': { name: 'Command Code', description: '使用 Command Code 方案額度,連線後自動取得模型。', badge: 'Coding' }, + en: { name: 'Command Code', description: 'Use your Command Code plan credits. Models are fetched when you connect.', badge: 'Coding' }, + }, // Retired: its transport reached a private endpoint under the official CLI's // identity. Kept so an existing connection stays identifiable and readable; // the connection card states that nothing can send through it any more. - 'commandcode-go': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Command Code GO', description: '已停用:该套餐通过官方 CLI 的私有通道访问,已不再支持。', badge: 'Coding' }, - 'zh-TW': { name: 'Command Code GO', description: '已停用:該方案透過官方 CLI 的私有通道存取,已不再支援。', badge: 'Coding' }, - en: { name: 'Command Code GO', description: 'Retired: this plan reached a private endpoint under the official CLI\u2019s identity.', badge: 'Coding' }, - })), - groq: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Groq', description: 'LPU 高速推理托管开源模型', badge: 'API' }, - 'zh-TW': { name: 'Groq', description: 'LPU 高速推理託管開源模型', badge: 'API' }, - en: { name: 'Groq', description: 'Ultra-fast LPU-hosted open models.', badge: 'API' }, - })), - openrouter: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenRouter', description: '一个密钥接入各大模型厂商 · OpenAI 兼容', badge: '聚合' }, - 'zh-TW': { name: 'OpenRouter', description: '使用一組金鑰連線各大模型廠商 · OpenAI 相容', badge: '聚合' }, - en: { name: 'OpenRouter', description: 'One API key across all major model labs · OpenAI-compatible', badge: 'Aggregator' }, - })), - alibaba: resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba', description: '阿里云百炼接入,通义千问 Qwen 模型', badge: 'API' }, - 'zh-TW': { name: 'Alibaba', description: '阿里雲百鍊 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, - en: { name: 'Alibaba', description: 'Alibaba Cloud API access for Qwen models.', badge: 'API' }, - })), - 'alibaba-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba 中国站', description: '阿里云百炼中国站接入,通义千问 Qwen 模型', badge: 'API' }, - 'zh-TW': { name: 'Alibaba 中國站', description: '阿里雲百鍊中國站 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, - en: { - name: 'Alibaba (China)', - description: 'Alibaba Cloud China-platform API access for Qwen models.', - badge: 'API', - }, - })), - 'alibaba-coding-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba Coding Plan 中国站', description: '阿里云百炼 Coding Plan 订阅 · 中国站', badge: 'Plan' }, - 'zh-TW': { name: 'Alibaba Coding Plan 中國站', description: '阿里雲百鍊 Coding Plan 訂閱 · 中國站', badge: 'Plan' }, - en: { name: 'Alibaba Coding Plan (China)', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools · China.', badge: 'Plan' }, - })), - 'alibaba-coding-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba Coding Plan 国际站', description: '阿里云百炼 Coding Plan 订阅 · 国际站', badge: 'Plan' }, - 'zh-TW': { name: 'Alibaba Coding Plan 國際站', description: '阿里雲百鍊 Coding Plan 訂閱 · 國際站', badge: 'Plan' }, - en: { name: 'Alibaba Coding Plan', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools.', badge: 'Plan' }, - })), - 'alibaba-token-plan-cn': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 北京', badge: 'Token' }, - 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 北京', badge: 'Token' }, - en: { name: 'Alibaba Token Plan (China)', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Beijing region.', badge: 'Token' }, - })), - 'alibaba-token-plan': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 新加坡', badge: 'Token' }, - 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 新加坡', badge: 'Token' }, - en: { name: 'Alibaba Token Plan', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Singapore region.', badge: 'Token' }, - })), + 'commandcode-go': { + 'zh-CN': { name: 'Command Code GO', description: '已停用:该套餐通过官方 CLI 的私有通道访问,已不再支持。', badge: 'Coding' }, + 'zh-TW': { name: 'Command Code GO', description: '已停用:該方案透過官方 CLI 的私有通道存取,已不再支援。', badge: 'Coding' }, + en: { name: 'Command Code GO', description: 'Retired: this plan reached a private endpoint under the official CLI\u2019s identity.', badge: 'Coding' }, + }, + groq: { + 'zh-CN': { name: 'Groq', description: 'LPU 高速推理托管开源模型', badge: 'API' }, + 'zh-TW': { name: 'Groq', description: 'LPU 高速推理託管開源模型', badge: 'API' }, + en: { name: 'Groq', description: 'Ultra-fast LPU-hosted open models.', badge: 'API' }, + }, + openrouter: { + 'zh-CN': { name: 'OpenRouter', description: '一个密钥接入各大模型厂商 · OpenAI 兼容', badge: '聚合' }, + 'zh-TW': { name: 'OpenRouter', description: '使用一組金鑰連線各大模型廠商 · OpenAI 相容', badge: '聚合' }, + en: { name: 'OpenRouter', description: 'One API key across all major model labs · OpenAI-compatible', badge: 'Aggregator' }, + }, + alibaba: { + 'zh-CN': { name: 'Alibaba', description: '阿里云百炼接入,通义千问 Qwen 模型', badge: 'API' }, + 'zh-TW': { name: 'Alibaba', description: '阿里雲百鍊 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, + en: { name: 'Alibaba', description: 'Alibaba Cloud API access for Qwen models.', badge: 'API' }, + }, + 'alibaba-cn': { + 'zh-CN': { name: 'Alibaba 中国站', description: '阿里云百炼中国站接入,通义千问 Qwen 模型', badge: 'API' }, + 'zh-TW': { name: 'Alibaba 中國站', description: '阿里雲百鍊中國站 API 連線,支援通義千問 Qwen 模型', badge: 'API' }, + en: { + name: 'Alibaba (China)', + description: 'Alibaba Cloud China-platform API access for Qwen models.', + badge: 'API', + }, + }, + 'alibaba-coding-plan-cn': { + 'zh-CN': { name: 'Alibaba Coding Plan 中国站', description: '阿里云百炼 Coding Plan 订阅 · 中国站', badge: 'Plan' }, + 'zh-TW': { name: 'Alibaba Coding Plan 中國站', description: '阿里雲百鍊 Coding Plan 訂閱 · 中國站', badge: 'Plan' }, + en: { name: 'Alibaba Coding Plan (China)', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools · China.', badge: 'Plan' }, + }, + 'alibaba-coding-plan': { + 'zh-CN': { name: 'Alibaba Coding Plan 国际站', description: '阿里云百炼 Coding Plan 订阅 · 国际站', badge: 'Plan' }, + 'zh-TW': { name: 'Alibaba Coding Plan 國際站', description: '阿里雲百鍊 Coding Plan 訂閱 · 國際站', badge: 'Plan' }, + en: { name: 'Alibaba Coding Plan', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools.', badge: 'Plan' }, + }, + 'alibaba-token-plan-cn': { + 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 北京', badge: 'Token' }, + 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 北京', badge: 'Token' }, + en: { name: 'Alibaba Token Plan (China)', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Beijing region.', badge: 'Token' }, + }, + 'alibaba-token-plan': { + 'zh-CN': { name: 'Alibaba Token Plan(团队版)', description: '阿里云百炼 Token Plan 订阅,交互式智能体与编码工具 · 新加坡', badge: 'Token' }, + 'zh-TW': { name: 'Alibaba Token Plan(團隊版)', description: '阿里雲百鍊 Token Plan 訂閱,互動式智慧體與編碼工具 · 新加坡', badge: 'Token' }, + en: { name: 'Alibaba Token Plan', description: 'Alibaba Cloud Model Studio Token Plan for interactive agents and coding tools, Singapore region.', badge: 'Token' }, + }, // OAuth account providers (not in CATALOG_PROVIDER_TYPES; shown in the // accounts section and on connection rows). - 'github-copilot': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'GitHub Copilot', description: 'GitHub Copilot 订阅接入,复用本机 GitHub 登录', badge: 'Account' }, - 'zh-TW': { name: 'GitHub Copilot', description: '透過 GitHub Copilot 訂閱連線,沿用本機 GitHub 登入', badge: 'Account' }, - en: { name: 'GitHub Copilot', description: 'GitHub Copilot subscription access using an existing GitHub login.', badge: 'Account' }, - })), - 'claude-subscription': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'Claude Subscription', description: 'Claude Pro / Max 订阅账号登录;登录后自动成为可用模型连接。' }, - 'zh-TW': { name: 'Claude Subscription', description: 'Claude Pro / Max 訂閱帳號登入;登入後自動成為可用模型連線。' }, - en: { name: 'Claude Subscription', description: 'Sign in with a Claude Pro / Max subscription; it becomes an available model connection once signed in.' }, - })), - 'openai-codex': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 账号登录;登录后自动成为可用模型连接。' }, - 'zh-TW': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 帳號登入;登入後自動成為可用模型連線。' }, - en: { name: 'OpenAI OAuth', description: 'Sign in with a ChatGPT / Codex account; it becomes an available model connection once signed in.' }, - })), - 'xai-oauth': resolveUiMessageCatalog(defineUiMessageCatalog()({ - 'zh-CN': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 账号登录。', badge: 'Account' }, - 'zh-TW': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 帳號登入。', badge: 'Account' }, - en: { name: 'xAI OAuth', description: 'Sign in with SuperGrok or X Premium.', badge: 'Account' }, - })), + 'github-copilot': { + 'zh-CN': { name: 'GitHub Copilot', description: 'GitHub Copilot 订阅接入,复用本机 GitHub 登录', badge: 'Account' }, + 'zh-TW': { name: 'GitHub Copilot', description: '透過 GitHub Copilot 訂閱連線,沿用本機 GitHub 登入', badge: 'Account' }, + en: { name: 'GitHub Copilot', description: 'GitHub Copilot subscription access using an existing GitHub login.', badge: 'Account' }, + }, + 'claude-subscription': { + 'zh-CN': { name: 'Claude Subscription', description: 'Claude Pro / Max 订阅账号登录;登录后自动成为可用模型连接。' }, + 'zh-TW': { name: 'Claude Subscription', description: 'Claude Pro / Max 訂閱帳號登入;登入後自動成為可用模型連線。' }, + en: { name: 'Claude Subscription', description: 'Sign in with a Claude Pro / Max subscription; it becomes an available model connection once signed in.' }, + }, + 'openai-codex': { + 'zh-CN': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 账号登录;登录后自动成为可用模型连接。' }, + 'zh-TW': { name: 'OpenAI OAuth', description: 'ChatGPT / Codex 帳號登入;登入後自動成為可用模型連線。' }, + en: { name: 'OpenAI OAuth', description: 'Sign in with a ChatGPT / Codex account; it becomes an available model connection once signed in.' }, + }, + 'xai-oauth': { + 'zh-CN': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 账号登录。', badge: 'Account' }, + 'zh-TW': { name: 'xAI OAuth', description: '使用 SuperGrok 或 X Premium 帳號登入。', badge: 'Account' }, + en: { name: 'xAI OAuth', description: 'Sign in with SuperGrok or X Premium.', badge: 'Account' }, + }, } satisfies Record>; export function providerDisplay(type: ProviderType, locale: UiLocale): ProviderCopy { From ecb29e5199acbd1b363faa59e53bf9a436329597 Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Sat, 3 Oct 2026 14:17:23 +0900 Subject: [PATCH 4/5] fix(core): keep message functions contextually typed in resolver catalogs DeepPartial and ExactMessageShape treated functions as plain objects and mapped them to `{}`, so function-valued messages in non-en locales lost their contextual signature and fell back to implicit any (TS7006). Pass functions through both helpers unchanged, which restores parameter type and arity checking for interpolated messages. Co-Authored-By: Claude Sonnet 5.5 --- packages/core/src/ui-locale.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/core/src/ui-locale.ts b/packages/core/src/ui-locale.ts index 2272506d792..393fa2f8daf 100644 --- a/packages/core/src/ui-locale.ts +++ b/packages/core/src/ui-locale.ts @@ -32,7 +32,10 @@ export const UI_LOCALE_PREFERENCES = ['auto', ...UI_LOCALES] as const; /** A catalog must carry copy for every supported resolved locale. */ export type UiCatalog = Record; -type DeepPartial = T extends readonly unknown[] +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type MessageFunction = (...args: any[]) => unknown; + +type DeepPartial = T extends readonly unknown[] | MessageFunction ? T : T extends object ? { readonly [Key in keyof T]?: DeepPartial } @@ -44,20 +47,24 @@ export type UiMessageCatalog = { readonly [Locale in Exclude]: DeepPartial; }>; -type ExactMessageShape = Actual extends readonly unknown[] - ? NonNullable extends readonly unknown[] +type ExactMessageShape = Actual extends MessageFunction + ? NonNullable extends MessageFunction ? Actual : never - : Actual extends object - ? Exclude> extends never - ? { - readonly [Key in keyof Actual]: ExactMessageShape< - Actual[Key], - NonNullable[Key & keyof NonNullable] - >; - } + : Actual extends readonly unknown[] + ? NonNullable extends readonly unknown[] + ? Actual : never - : Actual; + : Actual extends object + ? Exclude> extends never + ? { + readonly [Key in keyof Actual]: ExactMessageShape< + Actual[Key], + NonNullable[Key & keyof NonNullable] + >; + } + : never + : Actual; const messageFormatters = new Map(); From 7962619c995d81a053e592cd49ed632350819156 Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:42:41 +0900 Subject: [PATCH 5/5] refactor(i18n): migrate diagnostics-copy to the resolver-based message format Wrap the catalog in resolveUiMessageCatalog(defineUiMessageCatalog()(...)) like the other migrated catalogs. The file arrived on main via #5892 after the initial sweep and was the last plain-object catalog still on satisfies UiCatalog. Co-Authored-By: Claude Sonnet 5.5 --- .../features/diagnostics/locales/diagnostics-copy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts b/apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts index 01180e7fb80..77f69c441fb 100644 --- a/apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts +++ b/apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { UiCatalog, UiLocale } from '@maka/core/ui-locale'; +import { type UiCatalog, type UiLocale, defineUiMessageCatalog, resolveUiMessageCatalog } from '@maka/core/ui-locale'; export interface DiagnosticsCopy { readonly previousMainProcessInterruption: { @@ -27,7 +27,7 @@ export interface DiagnosticsCopy { }; } -const COPY_BY_LOCALE = { +const COPY_BY_LOCALE = resolveUiMessageCatalog(defineUiMessageCatalog()({ 'zh-CN': { previousMainProcessInterruption: { title: 'Maka 已恢复', @@ -49,7 +49,7 @@ const COPY_BY_LOCALE = { copyDiagnostics: 'Copy report', }, }, -} satisfies UiCatalog; +})) satisfies UiCatalog; export function getDiagnosticsCopy(locale: UiLocale): DiagnosticsCopy { return COPY_BY_LOCALE[locale];