mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
Merge pull request #6796 from deepseek-harness/07akioni/japanese-locale
feat(locale): 增加日语界面支持
This commit is contained in:
commit
d4aa52c2be
172 files changed
+4012
-254
No files matched your search
@@ -33,8 +33,8 @@
|
||||
en: 372297005b57a08b
|
||||
zh: 074a635ac17e4362
|
||||
/deepseek-harness-desktop/installation-ownership:
|
||||
en: 22153a298a6662e1
|
||||
zh: 3600803c8c9d0274
|
||||
en: b3cf09d7c901d727
|
||||
zh: 2ec34045a201d8e9
|
||||
/deepseek-harness-desktop/installation-ownership/runtime-and-plugin-activation:
|
||||
en: a1a47b09f4a72ba4
|
||||
zh: 7b1cf27f0b72cf31
|
||||
@@ -45,8 +45,8 @@
|
||||
en: faf7676ba18bbf2f
|
||||
zh: f1d7b75ad348c548
|
||||
/deepseek-harness-desktop/develop/startup-onboarding:
|
||||
en: a51346b8d880ddba
|
||||
zh: 7913214c1c66063c
|
||||
en: d87d51b901b72ecd
|
||||
zh: 525da1c4d56af659
|
||||
/deepseek-harness-desktop/develop/welcome-window-appearance:
|
||||
en: 617a89aeb28131b9
|
||||
zh: 4d84bd5d86501c86
|
||||
|
||||
@@ -150,7 +150,7 @@ The product UI retains Web actions, including "Open In..." through the shared au
|
||||
|
||||
Update copy preserves the complete version, including prerelease suffixes, without adding v or V. When no update is available, the dialog title reports the result and the body shows the current version.
|
||||
|
||||
Electron chooses typed English or Chinese shell copy from its application locale and falls back to English. The macOS application bundle declares English and Simplified Chinese in `CFBundleLocalizations`, allowing macOS to match the initial application language to the user’s preferred languages. Saved Client UI language preferences still take precedence for the main interface. On Windows, the main document's language updates desktop menus, recovery and update prompts. The repository Client UI i18n gate checks desktop sources.
|
||||
Electron chooses typed English, Chinese, or Japanese shell copy from its application locale and falls back to English. The macOS application bundle declares English, Simplified Chinese, and Japanese in `CFBundleLocalizations`, allowing macOS to match the initial application language to the user’s preferred languages. Saved Client UI language preferences still take precedence for the main interface. On Windows, the main document's language updates desktop menus, recovery and update prompts. The repository Client UI i18n gate checks desktop sources.
|
||||
|
||||
Windows and macOS relay native fullscreen transitions and the state after each page load to the renderer, so overlays can adapt their caption clearance.
|
||||
|
||||
@@ -218,7 +218,7 @@ Repeated launches and `dsh://open` keep the workspace hidden until the startup c
|
||||
|
||||
Desktop checks configured model API-key references after the Host starts and before opening the workspace. With no configured key, the welcome window offers the API-key page. Save and continue writes through the existing credential service using the official DeepSeek provider's configured reference, then opens the workspace. Set up later opens the workspace without saving the draft or a completion setting; the next process launch checks credentials again. Back to sign in returns to the entry and clears the unsaved key and validation message. Buttons keep their labels and block competing actions while saving or opening the workspace. The Desktop preload marker suppresses the Web credential dialog, while retaining the Models settings page and the welcome notice.
|
||||
|
||||
The welcome window reads the shared `locale.preference` before it appears. An explicit English or Chinese choice wins; otherwise Desktop picks the first supported OS language and falls back to English. The main UI reads the same preference and OS language order through its isolated preload before mounting. Language changes in Settings update the shell’s current dictionary and menu; automatic selection writes no preference. The welcome window has no language selector.
|
||||
The welcome window reads the shared `locale.preference` before it appears. An explicit English, Chinese, or Japanese choice wins; otherwise Desktop picks the first supported OS language and falls back to English. The main UI reads the same preference and OS language order through its isolated preload before mounting. Language changes in Settings update the shell’s current dictionary and menu; automatic selection writes no preference. The welcome window has no language selector.
|
||||
|
||||
The browser-login waiting page offers a copy-link action for the current pending authorization, a loading indicator, and cancellation. Clipboard failures leave the copy action available for retry. Copy feedback resets after two seconds; the copied state disables the link until it resets. Welcome text uses Montserrat Light with system fallbacks; large action buttons keep the system font, while text buttons use Montserrat Light. English introductory copy is 24px throughout. Chinese introductory copy is 24px with a 26px product name. Login action buttons are 240px wide with 14px labels. Authorization status headings use 20px Montserrat Regular text. The API-key page uses a 20px heading and a 14px back action, with 84px between the secondary button’s bottom edge and the window bottom.
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ Electron 拥有 `$DSH_HOME/profiles/desktop`。其 `dependencies` 包含 pnpm
|
||||
|
||||
升级文案完整展示版本号(包括预发布后缀),不额外添加 v 或 V。无可用更新时,弹窗标题显示检查结果,正文显示当前版本。
|
||||
|
||||
Electron 根据应用语言选择类型化的英文或中文 shell 文案,并回退到英文。macOS 应用包通过 `CFBundleLocalizations` 声明支持英语和简体中文,让 macOS 根据用户的首选语言匹配初始应用语言。主界面仍优先使用已保存的 Client UI 语言偏好。在 Windows 上,主文档的语言会更新桌面菜单、恢复与更新提示。仓库 Client UI i18n 检查覆盖桌面端源码。
|
||||
Electron 根据应用语言选择类型化的英文、中文或日文 shell 文案,并回退到英文。macOS 应用包通过 `CFBundleLocalizations` 声明支持英语、简体中文和日语,让 macOS 根据用户的首选语言匹配初始应用语言。主界面仍优先使用已保存的 Client UI 语言偏好。在 Windows 上,主文档的语言会更新桌面菜单、恢复与更新提示。仓库 Client UI i18n 检查覆盖桌面端源码。
|
||||
|
||||
Windows 和 macOS 会在原生全屏切换及每次页面加载后向渲染器同步全屏状态,让浮层调整顶栏避让距离。
|
||||
|
||||
@@ -218,7 +218,7 @@ API Key 输入框初始为空,并通过 `autocomplete="new-password"` 请求 C
|
||||
|
||||
Desktop 在 Host 启动后、打开工作区前检查模型 API Key 引用是否已配置。没有已配置的密钥时,欢迎窗口提供 API Key 页面。“保存并继续”通过现有凭证服务写入 DeepSeek 官方提供方配置的引用,然后打开工作区。“稍后配置”打开工作区,但不保存草稿或完成标记;下次进程启动时会重新检查凭证。“返回登录”回到入口并清空未保存的密钥和校验提示。保存或打开工作区期间,按钮保持原文案并禁用竞争操作。Desktop preload 标记使 Web 凭证弹窗不再显示,同时保留模型设置页和欢迎须知。
|
||||
|
||||
欢迎窗口在显示前读取共享的 `locale.preference`。用户明确选择的英文或中文优先;否则 Desktop 按系统语言顺序匹配支持的语言,并以英文兜底。主界面在挂载前通过隔离 preload 读取同一偏好和系统语言顺序。在设置中切换语言会更新桌面壳的当前词典和菜单;自动选择不会写入偏好。欢迎窗口不提供语言切换入口。
|
||||
欢迎窗口在显示前读取共享的 `locale.preference`。用户明确选择的英文、中文或日文优先;否则 Desktop 按系统语言顺序匹配支持的语言,并以英文兜底。主界面在挂载前通过隔离 preload 读取同一偏好和系统语言顺序。在设置中切换语言会更新桌面壳的当前词典和菜单;自动选择不会写入偏好。欢迎窗口不提供语言切换入口。
|
||||
|
||||
等待浏览器登录时,欢迎页提供当前待授权请求的链接复制入口、加载指示和取消操作;剪贴板写入失败后可以重试复制,复制结果提示在两秒后恢复;已复制状态下链接禁用,恢复后可再次点击。Welcome 文字使用 Montserrat Light 并回退到系统字体,底部大按钮保留系统字体,文字按钮使用 Montserrat Light。英文欢迎正文及产品名均为 24px,中文欢迎正文为 24px、产品名为 26px。登录操作按钮宽 240px,文字为 14px。授权状态标题使用 20px Montserrat Regular 字重。API Key 页的标题为 20px,返回操作为 14px,次级按钮底边距窗口底部 84px。
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ export interface DesktopElectronBuilderConfig {
|
||||
...{ readonly from: string, readonly to: 'tray.ico' }[],
|
||||
]
|
||||
readonly mac: {
|
||||
readonly extendInfo: { readonly NSMicrophoneUsageDescription: string }
|
||||
readonly extendInfo: {
|
||||
readonly CFBundleLocalizations: readonly string[]
|
||||
readonly NSMicrophoneUsageDescription: string
|
||||
}
|
||||
readonly entitlements: string
|
||||
readonly entitlementsInherit: string
|
||||
readonly identity: string | undefined
|
||||
|
||||
@@ -162,12 +162,14 @@ export function createElectronBuilderConfig(
|
||||
icon: fileURLToPath(new URL('../resources/icon-macos.png', import.meta.url)),
|
||||
category: 'public.app-category.developer-tools',
|
||||
// macOS matches the application locale against this bundle, not Electron Framework resources.
|
||||
extendInfo: { CFBundleLocalizations: ['en', 'zh_CN'] },
|
||||
extendInfo: {
|
||||
CFBundleLocalizations: ['en', 'zh_CN', 'ja'],
|
||||
NSMicrophoneUsageDescription: 'DeepSeek Harness uses your microphone to transcribe speech into message drafts.',
|
||||
},
|
||||
// Ad-hoc signing keeps the modified Electron executable runnable without a Developer ID.
|
||||
identity: unsigned ? '-' : macOSSigning?.signingIdentity,
|
||||
forceCodeSigning: !unsigned,
|
||||
hardenedRuntime: true,
|
||||
extendInfo: { NSMicrophoneUsageDescription: 'DeepSeek Harness uses your microphone to transcribe speech into message drafts.' },
|
||||
entitlements: fileURLToPath(new URL('./macos-entitlements.plist', import.meta.url)),
|
||||
entitlementsInherit: fileURLToPath(new URL('./macos-entitlements.plist', import.meta.url)),
|
||||
// Prepared runtime files retain their signatures; PAK resources are sealed by their enclosing bundle.
|
||||
|
||||
+177
-5
@@ -1,4 +1,4 @@
|
||||
/** Typed English and Chinese copy owned by the Electron shell. */
|
||||
/** Typed English, Chinese, and Japanese copy owned by the Electron shell. */
|
||||
|
||||
export const en = {
|
||||
cliCommandMenu: 'Manage dsh Command…',
|
||||
@@ -341,15 +341,187 @@ export const zh = {
|
||||
mandatoryNotification: '返回应用确认安装并重启。',
|
||||
} as const satisfies DesktopMessages
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
cliCommandMenu: 'dsh コマンドを管理…',
|
||||
cliCommandTitle: 'dsh コマンドを管理',
|
||||
cliCommandLocation: 'デスクトップコマンド: {path}',
|
||||
cliCommandSelected: '現在の dsh コマンド: {path}',
|
||||
cliCommandTarget: '現在のランチャーターゲット: {path}',
|
||||
cliCommandShadowed: '別の dsh が優先されています。デフォルトでデスクトップコマンドを使用するには、そのインストールを削除するか順序を変更してください。デスクトップコマンドはフルパスでも実行できます。',
|
||||
cliCommandSelectionUnknown: 'シェルコマンドを確認できませんでした。エイリアスや別の dsh インストールが優先されている可能性があります。',
|
||||
cliCommandInstalled: 'デスクトップコマンドがインストールされています。',
|
||||
cliCommandNotInstalled: 'デスクトップコマンドをターミナルに追加します。',
|
||||
cliCommandBroken: 'デスクトップコマンドの修復が必要です。',
|
||||
cliCommandInstall: 'インストール',
|
||||
cliCommandRepair: '修復',
|
||||
cliCommandRemove: '削除',
|
||||
cliCommandClose: '閉じる',
|
||||
cliCommandSwitch: 'デスクトップコマンドで続行しますか?',
|
||||
cliCommandPreserve: '既存のコマンドは保持されます。他のインストールやシェルの起動ファイルは変更されません。',
|
||||
cliCommandContinue: '続行',
|
||||
cliCommandInstallApp: 'dsh コマンドを管理する前に、デスクトップをアプリケーションフォルダにインストールしてください。',
|
||||
cliCommandUpdating: 'アップデートをインストール中です。インストール完了後にコマンドを管理してください。',
|
||||
cliCommandNewTerminal: '新しいターミナルを開いて dsh --version を実行してください。',
|
||||
cliCommandRemoved: 'デスクトップコマンドの登録を削除しました。',
|
||||
cliCommandPreviousRestored: '以前のランチャーを復元しました。',
|
||||
cliCommandOtherKept: '他のコマンドインストールはそのまま残されています。',
|
||||
cliCommandBackupKept: '以前のランチャーは次の場所に保持されています: {path}',
|
||||
cliCommandChanged: 'このダイアログを開いている間にコマンドまたは PATH が変更されました。現在の状態を確認するには、dsh コマンドを管理を再度開いてください。',
|
||||
cliCommandOwnershipError: 'コマンド登録またはバックアップが変更されました。無関係なコマンドは削除されていません。',
|
||||
cliCommandFailed: 'コマンドを更新できませんでした。アプリケーションがインストールされ、保存先に書き込み権限があることを確認してから再試行してください。',
|
||||
application: 'アプリケーション',
|
||||
fileMenu: 'ファイル',
|
||||
closePage: 'ページまたはウィンドウを閉じる',
|
||||
aboutMenu: 'DeepSeek Harness について',
|
||||
aboutProduct: 'DeepSeek Harness',
|
||||
aboutVersion: 'バージョン V{version}',
|
||||
hideApplication: 'DeepSeek Harness を隠す',
|
||||
hideOtherApplications: 'ほかを隠す',
|
||||
showAllApplications: 'すべて表示',
|
||||
quitApplication: 'DeepSeek Harness を終了',
|
||||
openApplication: 'DeepSeek Harness を開く',
|
||||
quit: '終了',
|
||||
cancel: 'キャンセル',
|
||||
quitTitle: 'DeepSeek Harness を終了しますか?',
|
||||
quitActiveTasks: '実行中のタスクは中断されます。',
|
||||
quitScheduledTasks: 'アプリの終了中はスケジュールされたタスクが実行されません。',
|
||||
quitActiveAndScheduledTasks: '実行中のタスクは中断され、アプリの終了中はスケジュールされたタスクが実行されません。',
|
||||
backgroundNoticeBody: '実行中のタスクは継続されます。システムトレイからウィンドウを再度開けます。',
|
||||
backgroundNoticeConfirm: '確認',
|
||||
edit: '編集',
|
||||
menuBar: 'アプリケーションメニュー',
|
||||
delete: '削除',
|
||||
undo: '元に戻す',
|
||||
redo: 'やり直す',
|
||||
cut: '切り取り',
|
||||
copy: 'コピー',
|
||||
paste: '貼り付け',
|
||||
selectAll: 'すべて選択',
|
||||
startupFailed: 'DeepSeek Harness を利用できません',
|
||||
fatalSummary: 'アプリケーションを起動できなかったか、予期せず停止しました。',
|
||||
startupAddressInUse: '別の DSH インスタンス (dsh web やデスクトップアプリなど) が実行中です。同時に起動することはできません。実行中の他の DSH インスタンスを終了してから再起動してください。',
|
||||
diagnosticTruncated: '… エラー詳細は省略されています。',
|
||||
reportWrittenTo: '診断レポート: {path}',
|
||||
startupReinstallAdvice: 'アプリケーションファイルが見つからないか破損している場合は、アプリケーションを閉じて再インストールしてください。タスクは別途保存されています。',
|
||||
exitApplication: '終了',
|
||||
restartApplication: '再起動',
|
||||
recoveryOperationFailed: '復旧操作に失敗しました',
|
||||
disableThirdPartyPlugins: 'サードパーティのプラグインを無効化し、プロファイルパッチをバックアップして再起動',
|
||||
welcomeTitle: 'DeepSeek Harness',
|
||||
welcomeBrand: 'DeepSeek Harness',
|
||||
welcomeTaglineBefore: 'ようこそ ',
|
||||
welcomeTaglineBrand: 'DeepSeek Harness',
|
||||
welcomeTaglineAfter: '',
|
||||
welcomeDescription: '可能性を築く。知能を探求する。',
|
||||
welcomeAuthStarting: 'ログインを開いています…',
|
||||
welcomeAuthWaiting: 'ブラウザが自動で開きませんでしたか?',
|
||||
welcomeAuthWaitingDescription: 'ログインリンクをコピーしてブラウザで開き、ログインしてください。',
|
||||
welcomeAuthExchanging: 'ログインを完了しています…',
|
||||
welcomeAuthExpired: 'ログインがタイムアウトしました',
|
||||
welcomeAuthExpiredDescription: '続行するには再度ログインしてください',
|
||||
welcomeAuthFailed: 'ログインを完了できませんでした。もう一度お試しください。',
|
||||
welcomeAuthCopyLink: 'ログインリンクをコピー',
|
||||
welcomeAuthCopied: 'コピーしました',
|
||||
welcomeAuthCopyFailed: 'コピーできませんでした。もう一度お試しください。',
|
||||
welcomeAuthCancel: 'キャンセル',
|
||||
welcomeAuthRetry: '再度ログイン',
|
||||
welcomeSignIn: 'ログイン',
|
||||
welcomeApiKey: 'APIキーを追加',
|
||||
welcomeKeyTitle: 'APIキーを追加して開始',
|
||||
welcomeKeyDescription: '公式のDeepSeekモデルを設定してHarnessを使い始めましょう',
|
||||
welcomeKeyPlaceholder: 'APIキーを入力',
|
||||
welcomeKeySave: '保存して続行',
|
||||
welcomeKeyLater: '後で設定',
|
||||
welcomeKeyBack: 'ログインに戻る',
|
||||
welcomeSessionExpired: 'アカウントからログアウトしました。再度ログインしてください。',
|
||||
welcomeKeyBlank: 'APIキーを入力してください。',
|
||||
welcomeKeyInvalid: '引用符、スペース、環境変数の代入を含めず、APIキーそのものを入力してください。',
|
||||
welcomeKeyFailed: 'APIキーを保存できませんでした。もう一度お試しください。',
|
||||
welcomeContinueFailed: 'ワークスペースを開けませんでした。もう一度お試しください。',
|
||||
checkUpdatesMenu: 'アップデートを確認…',
|
||||
reloadPageMenu: 'ページを再読み込み',
|
||||
restartAppHostMenu: 'アプリとホストを再起動',
|
||||
updateCheckFailedTitle: 'アップデートの確認に失敗しました',
|
||||
updateCheckFailed: 'アップデートを確認できませんでした。後でもう一度お試しください。',
|
||||
updateDownloadFailed: 'アップデートをダウンロードできませんでした。もう一度お試しください。',
|
||||
updateInstallFailed: 'アップデートをインストールできませんでした。後でもう一度お試しください。',
|
||||
updateCheckNetworkFailed: 'アップデートを確認できませんでした。接続を確認してもう一度お試しください。',
|
||||
updateDownloadNetworkFailed: 'アップデートをダウンロードできませんでした。接続を確認してもう一度お試しください。',
|
||||
updateInstallNetworkFailed: 'アップデートをインストールできませんでした。接続を確認してもう一度お試しください。',
|
||||
unknownError: '不明なエラー',
|
||||
updateCheckTitle: 'アップデートを確認',
|
||||
updateCurrentDetail: '現在のバージョン: {version}',
|
||||
updateCurrent: '最新です!',
|
||||
updateChecking: 'アップデートを確認中…',
|
||||
updateDownload: 'アップデートをダウンロード',
|
||||
updateDownloadedTitle: 'バージョン {version} をインストールする準備ができました',
|
||||
updateDownloadedDetail: 'アップデート中はアプリが終了し、完了後に自動的に再起動します。',
|
||||
updateDownloadedDetailWindows: 'アップデート中はアプリが一時的に終了し、完了後に自動的に再起動します。\n\nアップデートには時間がかかる場合があります。インストール中はアプリを再度起動せずにお待ちください。',
|
||||
updateClose: '閉じる',
|
||||
updateAcknowledge: 'OK',
|
||||
updateLater: '後でアップデート',
|
||||
updateDownloading: 'ダウンロード中 {percent}%…',
|
||||
updateVerifying: 'アップデートファイルを検証中…',
|
||||
updateInstalling: '再起動の準備中…',
|
||||
updateRetry: 'アップデートを再試行',
|
||||
updateActiveTasks: 'タスクがまだ実行中です',
|
||||
updateActiveTasksDetail: 'アップデートすると実行中のタスクが停止し、アプリが再起動します。続行しますか?',
|
||||
updateStopTasks: 'タスクを停止してアップデート',
|
||||
updateTasksChanged: '新しいタスクが開始されました。タスクを停止してアップデートするには再度確認してください。',
|
||||
updateTasksUnavailable: 'タスクの状態を取得できません。ワークスペースの準備ができてから再度アップデートをお試しください。',
|
||||
updateFullscreenTimeout: 'フルスクリーンを終了できませんでした。フルスクリーンを終了してからアップデートを再試行してください。',
|
||||
updateStopFailed: 'タスクを安全に停止できませんでした。アップデートはインストールされていません。後でもう一度お試しください。',
|
||||
updateTechnicalDetails: '技術的な詳細を表示',
|
||||
updateTitle: 'DeepSeek Harness アップデート',
|
||||
updateAvailable: '新しいバージョンがあります: {version}',
|
||||
updateDetail: 'ダウンロードが完了したら、アップデートをインストールしてアプリを再起動できます。',
|
||||
installAndRestart: 'インストールして再起動',
|
||||
later: '後で',
|
||||
updateFailedTitle: 'アップデートに失敗しました',
|
||||
mandatoryTitle: '続行するにはアップデートしてください',
|
||||
mandatoryDetail: 'このバージョンはサポートされなくなりました。続行するにはアップデートしてください。実行中のタスクは、インストールの確認と再起動を行うまで実行を継続します。',
|
||||
mandatoryUnavailable: 'アップデートの要件を確認できませんでした。後でもう一度お試しください。',
|
||||
policyLoginTitle: 'テスト環境にログイン',
|
||||
policyLoginRequired: 'Feishuでログインして、このテストビルドのアップデート要件を確認します。ログインしてもアップデートのダウンロードやインストールは行われません。',
|
||||
policyLogin: 'Feishuでログイン',
|
||||
policyLoginFailed: 'Feishuのログインが完了しませんでした。もう一度お試しください。',
|
||||
policyLoginLoading: 'ログインページを読み込み中…',
|
||||
mandatoryNoRelease: '互換性のあるアップデートが見つかりませんでした。再度確認するか、サポートにお問い合わせください。',
|
||||
mandatoryRefresh: '再確認',
|
||||
mandatoryPage: '公式サイトからダウンロード',
|
||||
mandatoryCopy: 'ダウンロードリンクをコピー',
|
||||
mandatoryPageFailed: '公式ダウンロードページを開けませんでした。リンクをコピーしてブラウザで開いてください。',
|
||||
mandatoryActionFailed: '操作に失敗しました。もう一度お試しください。アップデートが完了すると、アプリを再度使用できます。',
|
||||
mandatoryReady: 'アップデートの準備完了',
|
||||
mandatoryVersion: '新しいバージョン: {version}',
|
||||
mandatoryReadyDetail: 'アップデート中にアプリが終了し、完了後に自動的に再起動します。',
|
||||
mandatoryDeferred: '実行中のタスクはそのまま続行されます。アプリを再度使用する前にアップデートを完了してください。',
|
||||
mandatoryContinue: 'アップデートを続行',
|
||||
mandatoryInspecting: 'タスクを確認中…',
|
||||
mandatoryStopping: 'タスクを停止中…',
|
||||
mandatoryRestarting: 'まもなくアプリが再起動します。しばらくお待ちください。',
|
||||
mandatoryDownloadFailed: 'アップデートファイルをダウンロードまたは準備できませんでした。再試行してください。',
|
||||
mandatoryInstallFailed: 'アップデートはインストールされていません。タスクを再度確認して再試行してください。',
|
||||
mandatoryOpenHelp: 'ページが開きませんでしたか?',
|
||||
mandatoryReopen: '公式ダウンロードページを再度開く',
|
||||
mandatoryCopied: 'リンクをコピーしました',
|
||||
mandatoryCopyFailed: 'リンクをコピーできませんでした。以下から選択してコピーしてください。',
|
||||
mandatoryAddress: 'ダウンロードリンク',
|
||||
mandatoryNotification: 'アプリに戻り、インストールと再起動を確認してください。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
|
||||
/** Locale payload exposed to the Desktop-owned renderer. */
|
||||
export interface DesktopLocale {
|
||||
readonly id: 'en' | 'zh-CN'
|
||||
readonly id: 'en' | 'zh-CN' | 'ja'
|
||||
readonly messages: DesktopMessages
|
||||
}
|
||||
|
||||
/** Resolve Electron's locale to one shipped Desktop dictionary. */
|
||||
export function resolveDesktopLocale(locale: string): DesktopLocale {
|
||||
return locale.toLowerCase().startsWith('zh')
|
||||
const primary = locale.toLowerCase().split('-')[0]
|
||||
if (primary === 'ja') return { id: 'ja', messages: ja }
|
||||
return primary === 'zh'
|
||||
? { id: 'zh-CN', messages: zh }
|
||||
: { id: 'en', messages: en }
|
||||
}
|
||||
@@ -362,10 +534,10 @@ export function resolveDesktopLocale(locale: string): DesktopLocale {
|
||||
*/
|
||||
export function resolveDesktopStartupLocale(preference: string | null, languages: readonly string[]): DesktopLocale {
|
||||
const selected = preference?.toLowerCase()
|
||||
if (selected === 'zh' || selected === 'en') return resolveDesktopLocale(selected)
|
||||
if (selected === 'zh' || selected === 'en' || selected === 'ja') return resolveDesktopLocale(selected)
|
||||
for (const language of languages) {
|
||||
const primary = language.toLowerCase().split('-')[0]
|
||||
if (primary === 'zh' || primary === 'en') return resolveDesktopLocale(primary)
|
||||
if (primary === 'zh' || primary === 'en' || primary === 'ja') return resolveDesktopLocale(primary)
|
||||
}
|
||||
return resolveDesktopLocale('en')
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { en, formatDesktopMessage, resolveDesktopLocale, resolveDesktopStartupLocale, zh } from '../src/locale.ts'
|
||||
import { en, formatDesktopMessage, resolveDesktopLocale, resolveDesktopStartupLocale, ja, zh } from '../src/locale.ts'
|
||||
|
||||
describe('desktop locale dictionaries', () => {
|
||||
it('ships the same key set in English and Chinese', () => {
|
||||
it('ships the same key set in English, Chinese, and Japanese', () => {
|
||||
expect(Object.keys(zh)).toEqual(Object.keys(en))
|
||||
expect(Object.keys(ja)).toEqual(Object.keys(en))
|
||||
expect(resolveDesktopLocale('JA-jp')).toEqual({ id: 'ja', messages: ja })
|
||||
expect(resolveDesktopLocale('zh-Hans-CN').messages).toEqual(zh)
|
||||
expect(resolveDesktopLocale('en-US').messages).toEqual(en)
|
||||
expect(resolveDesktopLocale('fr-FR').messages).toEqual(en)
|
||||
@@ -17,11 +19,11 @@ describe('desktop locale dictionaries', () => {
|
||||
it('prefers an explicit supported choice, then the first supported system language', () => {
|
||||
expect(resolveDesktopStartupLocale('zh', ['en-US']).id).toBe('zh-CN')
|
||||
expect(resolveDesktopStartupLocale('EN', ['zh-CN']).id).toBe('en')
|
||||
expect(resolveDesktopStartupLocale(null, ['ja-JP', 'zh-Hant', 'en-US']).id).toBe('zh-CN')
|
||||
expect(resolveDesktopStartupLocale(null, ['ja-JP', 'zh-Hant', 'en-US']).id).toBe('ja')
|
||||
expect(resolveDesktopStartupLocale(null, ['en-US', 'zh-CN']).id).toBe('en')
|
||||
expect(resolveDesktopStartupLocale(null, ['ja-JP']).id).toBe('en')
|
||||
expect(resolveDesktopStartupLocale(null, ['ja-JP']).id).toBe('ja')
|
||||
expect(resolveDesktopStartupLocale(null, []).id).toBe('en')
|
||||
expect(resolveDesktopStartupLocale('ja', ['zh-CN']).id).toBe('zh-CN')
|
||||
expect(resolveDesktopStartupLocale('ja', ['zh-CN']).id).toBe('ja')
|
||||
})
|
||||
|
||||
})
|
||||
@@ -51,6 +51,7 @@ describe('desktop macOS release signature', () => {
|
||||
expect(config.protocols).toEqual([{ name: 'DeepSeek Harness', schemes: ['dsh'] }])
|
||||
expect(portablePath(config.directories.output)).toContain('/.desktop-build/targets/mac-arm64/artifacts')
|
||||
expect(config.mac.extendInfo.NSMicrophoneUsageDescription).toContain('microphone')
|
||||
expect(config.mac.extendInfo.CFBundleLocalizations).toEqual(['en', 'zh_CN', 'ja'])
|
||||
expect(config.mac.entitlementsInherit).toBe(config.mac.entitlements)
|
||||
const entitlements = readFileSync(config.mac.entitlements, 'utf8')
|
||||
for (const key of ['com.apple.security.cs.allow-jit', 'com.apple.security.cs.allow-unsigned-executable-memory',
|
||||
|
||||
@@ -21,6 +21,7 @@ describe.skipIf(MODE === 'record')('web e2e: native and Client locale preference
|
||||
let page: Page
|
||||
let tripwire: ReturnType<typeof watchConsole>
|
||||
const reported: string[] = []
|
||||
let languages = ['fr-FR', 'zh-CN', 'en-US']
|
||||
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold()
|
||||
@@ -29,7 +30,7 @@ describe.skipIf(MODE === 'record')('web e2e: native and Client locale preference
|
||||
tripwire = watchConsole(page)
|
||||
await page.exposeFunction('nativeLocaleRead', () => {
|
||||
const section = scaffold.ctx.settings.describe().find(row => row.ns === 'locale')!.value as { preference?: string }
|
||||
return { languages: ['ja-JP', 'zh-CN', 'en-US'], preference: section.preference ?? null }
|
||||
return { languages, preference: section.preference ?? null }
|
||||
})
|
||||
await page.exposeFunction('nativeLocaleChanged', (locale: string) => { reported.push(locale) })
|
||||
await page.addInitScript(() => {
|
||||
@@ -61,7 +62,17 @@ describe.skipIf(MODE === 'record')('web e2e: native and Client locale preference
|
||||
expect(scaffold.ctx.settings.describe().find(row => row.ns === 'locale')!.value).toEqual({})
|
||||
await compareOrRefreshGolden(join(SNAPSHOT_DIR, 'automatic.expected.md'),
|
||||
await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd, versionCapture), MODE)
|
||||
await zhDialog.getByRole('button', { name: '中文', exact: true }).click()
|
||||
languages = ['ja-JP', 'zh-CN', 'en-US']
|
||||
const automaticWarnings = tripwire.warnings.length
|
||||
reported.length = 0
|
||||
await page.reload({ waitUntil: 'load' })
|
||||
acknowledgeReloadConnectionLoss(tripwire, automaticWarnings)
|
||||
await openSettings(page, 'ja')
|
||||
const jaDialog = page.getByRole('dialog', { name: '設定', exact: true })
|
||||
await jaDialog.getByRole('button', { name: '日本語', exact: true }).waitFor()
|
||||
await expect.poll(() => reported).toEqual(['ja'])
|
||||
expect(scaffold.ctx.settings.describe().find(row => row.ns === 'locale')!.value).toEqual({})
|
||||
await jaDialog.getByRole('button', { name: '日本語', exact: true }).click()
|
||||
await page.getByRole('menuitem', { name: 'English', exact: true }).click()
|
||||
const enDialog = page.getByRole('dialog', { name: 'Settings', exact: true })
|
||||
await enDialog.getByRole('button', { name: 'English', exact: true }).waitFor()
|
||||
@@ -79,6 +90,40 @@ describe.skipIf(MODE === 'record')('web e2e: native and Client locale preference
|
||||
await expect.poll(() => reported).toEqual(['en'])
|
||||
expect(await page.evaluate(() => document.documentElement.lang)).toBe('en')
|
||||
expect(await page.evaluate(() => localStorage.getItem('dsh.locale'))).toBeNull()
|
||||
await enDialog.getByRole('button', { name: 'English', exact: true }).click()
|
||||
await page.getByRole('menuitem', { name: '日本語', exact: true }).click()
|
||||
await jaDialog.getByRole('button', { name: '日本語', exact: true }).waitFor()
|
||||
await expect.poll(() => readFile(join(scaffold.harnessHome, 'profiles', 'scaffold', 'cordis.patch.yml'), 'utf8'))
|
||||
.toContain('preference: ja')
|
||||
const japaneseReloadWarnings = tripwire.warnings.length
|
||||
reported.length = 0
|
||||
await page.reload({ waitUntil: 'load' })
|
||||
acknowledgeReloadConnectionLoss(tripwire, japaneseReloadWarnings)
|
||||
await openSettings(page, 'ja')
|
||||
await jaDialog.getByRole('button', { name: '日本語', exact: true }).waitFor()
|
||||
await expect.poll(() => reported).toEqual(['ja'])
|
||||
expect(await page.evaluate(() => document.documentElement.lang)).toBe('ja')
|
||||
const overflows = (): Promise<number> => jaDialog.evaluate((dialog) => {
|
||||
const containers = [dialog, ...dialog.querySelectorAll<HTMLElement>('*')]
|
||||
.filter(element => element === dialog || ['auto', 'scroll'].includes(getComputedStyle(element).overflowX))
|
||||
return containers.filter(element => element.clientWidth > 0 && element.scrollWidth > element.clientWidth + 1).length
|
||||
})
|
||||
await expect.poll(overflows).toBe(0)
|
||||
await jaDialog.evaluate((dialog) => {
|
||||
const options = dialog.querySelector('nav')!.nextElementSibling!.lastElementChild!
|
||||
const probe = document.createElement('div')
|
||||
probe.dataset.localeOverflowProbe = ''
|
||||
probe.style.width = '2000px'
|
||||
probe.style.height = '1px'
|
||||
options.append(probe)
|
||||
})
|
||||
try {
|
||||
expect(await overflows()).toBeGreaterThan(0)
|
||||
} finally {
|
||||
await jaDialog.locator('[data-locale-overflow-probe]').evaluate((element) => { element.remove() })
|
||||
}
|
||||
await expect.poll(overflows).toBe(0)
|
||||
|
||||
expect(tripwire.warnings).toEqual([])
|
||||
expect(tripwire.pageErrors).toEqual([])
|
||||
await assertFixtureInventory(SNAPSHOT_DIR, ['automatic.expected.md', 'selected.expected.md'])
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
acknowledgeReloadConnectionLoss, captureExpandedTurnProcessAria, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
|
||||
launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts'
|
||||
import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, openSettings, saveFailureShot } from './support.ts'
|
||||
|
||||
const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/ptc-round/session.v3.jsonl', import.meta.url))
|
||||
const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/ptc-round/ui.expected.md', import.meta.url))
|
||||
@@ -275,6 +275,27 @@ describe('web e2e: PTC mode round renders nested sub-calls', () => {
|
||||
expect(await wrap.getAttribute('aria-pressed')).toBe('true')
|
||||
})
|
||||
|
||||
it.skipIf(MODE === 'record')('renders the same recorded tool round in Japanese without translating model or tool data', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-ptc-japanese'))
|
||||
await openSettings(page, 'en')
|
||||
const settings = page.getByRole('dialog', { name: 'Settings', exact: true })
|
||||
await settings.getByRole('button', { name: 'English', exact: true }).click()
|
||||
await page.getByRole('menuitem', { name: '日本語', exact: true }).click()
|
||||
await page.getByRole('dialog', { name: '設定', exact: true }).waitFor()
|
||||
await page.keyboard.press('Escape')
|
||||
await page.getByRole('tab', { name: 'チャット', exact: true }).click()
|
||||
await page.getByText('DONE', { exact: true }).waitFor()
|
||||
expect(await page.evaluate(() => document.documentElement.lang)).toBe('ja')
|
||||
const row = page.locator('[data-subcalls] [data-sample="bash"]').first()
|
||||
await expandOwningTurnProcess(page, row)
|
||||
if (await row.getAttribute('aria-expanded') !== 'true') await row.click()
|
||||
await row.locator('xpath=..').getByText('echo CODE_ROUND_OK', { exact: true }).waitFor()
|
||||
const snapshot = await captureExpandedTurnProcessAria(page, '[class*="centerCol"]', scaffold.workspaceCwd)
|
||||
await compareOrRefreshGolden(
|
||||
fileURLToPath(new URL('../../../snapshots/web/ptc-round/ui-ja.expected.md', import.meta.url)), snapshot, MODE,
|
||||
)
|
||||
})
|
||||
|
||||
it.skipIf(MODE === 'record')('stayed clean: no page errors, no reconnect churn', () => {
|
||||
expect(tripwire.pageErrors).toEqual([])
|
||||
expect(tripwire.warnings).toEqual([])
|
||||
|
||||
@@ -333,7 +333,8 @@ export function conversationContextKey(kind: string, id: string): string {
|
||||
* @param page - browser page with the mounted sidebar.
|
||||
* @param locale - current UI language.
|
||||
*/
|
||||
export async function openSettings(page: Page, locale: 'en' | 'zh'): Promise<void> {
|
||||
await page.getByRole('button', { name: locale === 'zh' ? '账号菜单' : 'Account menu', exact: true }).click()
|
||||
await page.getByRole('menuitem', { name: locale === 'zh' ? '设置' : 'Settings', exact: true }).click()
|
||||
export async function openSettings(page: Page, locale: 'en' | 'zh' | 'ja'): Promise<void> {
|
||||
const labels = { en: ['Account menu', 'Settings'], zh: ['账号菜单', '设置'], ja: ['アカウントメニュー', '設定'] } as const
|
||||
await page.getByRole('button', { name: labels[locale][0], exact: true }).click()
|
||||
await page.getByRole('menuitem', { name: labels[locale][1], exact: true }).click()
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-rc.2/built-in-japanese/guide.md
|
||||
/:
|
||||
en: 383326881143f69b
|
||||
zh: cd7ff0e2b97f9c2f
|
||||
/japanese-is-a-built-in-language:
|
||||
en: f97bae63a42a3627
|
||||
zh: cec418f8bf6ac168
|
||||
/japanese-is-a-built-in-language/change:
|
||||
en: feb1f2d1cafdcfe0
|
||||
zh: 94d527865ec108bb
|
||||
/japanese-is-a-built-in-language/migration:
|
||||
en: ced2c2baad9124b4
|
||||
zh: 7b0b5b0e345994b8
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "Built-in Japanese registrations conflict with third-party Japanese language packs."
|
||||
---
|
||||
|
||||
# Japanese is a built-in language
|
||||
|
||||
English | [中文](guide.zh.md)
|
||||
|
||||
## Change
|
||||
|
||||
The locale plugin registers the `ja` language and Japanese dictionaries for `common` and `settings.locale`. Third-party language packs following the previous Japanese README example now fail with an already-registered language or dictionary error.
|
||||
|
||||
## Migration
|
||||
|
||||
1. In the language pack's client entry, remove `ctx.locale.addLanguage({ id: 'ja', ... })` and registrations for `common` and `settings.locale` under `ja`. Remove other Japanese dictionary registrations whose namespaces are now supplied by built-in plugins; keep translations for namespaces owned by your own plugins.
|
||||
2. If the pack represents a distinct language variant, register a separate BCP 47-style id, such as `ja-JP`, for both the definition and its dictionaries instead of `ja`.
|
||||
3. Reload the application with the pack enabled. Confirm it starts without duplicate-registration errors and that selecting the intended language displays its translations.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "内置日语注册会与第三方日语语言包冲突。"
|
||||
---
|
||||
|
||||
# 日语成为内置语言
|
||||
|
||||
[English](guide.md) | 中文
|
||||
|
||||
## 变更
|
||||
|
||||
locale 插件注册 `ja` 语言,以及 `common` 和 `settings.locale` 的日文字典。按此前 README 日语示例编写的第三方语言包会因语言或字典已注册而加载失败。
|
||||
|
||||
## 迁移
|
||||
|
||||
1. 在语言包的 client 入口中,删除 `ctx.locale.addLanguage({ id: 'ja', ... })`,以及 `common` 和 `settings.locale` 下的 `ja` 注册。删除其他已由内置插件提供的命名空间的日语字典注册;保留自己插件所拥有命名空间的翻译。
|
||||
2. 如果语言包代表不同的语言变体,为语言定义及其字典注册独立的 BCP 47 风格 id,例如 `ja-JP`,代替 `ja`。
|
||||
3. 启用语言包并重新加载应用。确认启动时没有重复注册错误,且选择目标语言后显示对应译文。
|
||||
@@ -3,14 +3,14 @@
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/locale/README.md
|
||||
/:
|
||||
en: d2aaa955e01c2679
|
||||
zh: 9d264cf8be1536cf
|
||||
en: 5dea50fae7364d1e
|
||||
zh: dc788b9ce6ffd101
|
||||
/deepseek-ai-dsh-client-locale:
|
||||
en: 9365090d78467dea
|
||||
zh: 74b6fbb9769d795f
|
||||
/deepseek-ai-dsh-client-locale/summary:
|
||||
en: d8cc5ae25160ca20
|
||||
zh: e8fc2b9fb663cdee
|
||||
en: a2c282377f0a043e
|
||||
zh: 2274f1ea41774549
|
||||
/deepseek-ai-dsh-client-locale/table-of-contents:
|
||||
en: d152484eb41ac6b4
|
||||
zh: bda879a892f240f0
|
||||
@@ -21,8 +21,8 @@
|
||||
en: 2b82458e185ba321
|
||||
zh: e55a7e709a3f0158
|
||||
/deepseek-ai-dsh-client-locale/use-this-package/registering-a-dictionary:
|
||||
en: 9b2fcfa8cae8dc4b
|
||||
zh: 293012a4d364ef59
|
||||
en: 69b07cde5b2b4deb
|
||||
zh: b8204c127b10a711
|
||||
/deepseek-ai-dsh-client-locale/use-this-package/resolving-package-text:
|
||||
en: 3b63e84fe0fdf8f4
|
||||
zh: e97b5d1b92dff7ec
|
||||
@@ -42,11 +42,11 @@
|
||||
en: d602d957654641c6
|
||||
zh: 2db822b746a6471d
|
||||
/deepseek-ai-dsh-client-locale/understand-the-implementation/dictionary-lookup:
|
||||
en: f8dcc4035830e738
|
||||
zh: 8452ac63ee148c61
|
||||
en: 68373e745761494c
|
||||
zh: 43a5b8c19d03268a
|
||||
/deepseek-ai-dsh-client-locale/understand-the-implementation/source-map:
|
||||
en: a382e032c06ced3f
|
||||
zh: 419cb5f60b075388
|
||||
en: fc93a8f6804aa5bd
|
||||
zh: 9d8364dc4c03d055
|
||||
/deepseek-ai-dsh-client-locale/further-exploration:
|
||||
en: f73d411c9e90dead
|
||||
zh: 8dd05f13e081af88
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "Localization for the web GUI: the zh/en preference, browser-derived fallback, typed namespace dictionaries, and the framework translation seat, for users and plugin authors."
|
||||
description: "Localization for the web GUI: language preferences, browser-derived fallback, typed namespace dictionaries, and the framework translation seat, for users and plugin authors."
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
Use `dsh-client-locale` to switch the web GUI between the shipped English and Chinese locales or languages added by client plugins. User selections take effect immediately; every authenticated browser persists them as `locale.preference` in `$DSH_HOME/cordis.patch.yml` through the Host settings service. New browsers use the first supported language requested by the browser until the Host read delivers a stored preference. Plugin authors add typed namespace dictionaries and translate through the public locale API; slot-rendered copy updates without a reload.
|
||||
Use `dsh-client-locale` to switch the web GUI between the shipped English, Chinese, and Japanese locales or languages added by client plugins. User selections take effect immediately; every authenticated browser persists them as `locale.preference` in `$DSH_HOME/cordis.patch.yml` through the Host settings service. New browsers use the first supported language requested by the browser until the Host read delivers a stored preference. Plugin authors add typed namespace dictionaries and translate through the public locale API; slot-rendered copy updates without a reload.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -37,7 +37,7 @@ Native shells may provide `__DSH_LOCALE__` with an asynchronous `read()` and an
|
||||
|
||||
### Registering a dictionary
|
||||
|
||||
Call `ctx.locale.register(ns, { zh, en })` with a namespace merged into `LocaleNamespaceMap`; the compiler checks every key against the namespace's typed key union and requires both shipped locales. Consumers translate through `ctx.locale.bind(ns)` or the framework-injected `t` seat. A dictionary registered after the UI is already mounted is picked up without a remount.
|
||||
Call `ctx.locale.register(ns, { zh, en, ja })` with a namespace merged into `LocaleNamespaceMap`; the compiler checks every key against the namespace's typed key union and requires both baseline locales (`zh` and `en`). Additional locale ids are optional, and their dictionaries are checked against the same namespace keys. Consumers translate through `ctx.locale.bind(ns)` or the framework-injected `t` seat. A dictionary registered after the UI is already mounted is picked up without a remount.
|
||||
|
||||
### Resolving package text
|
||||
|
||||
@@ -52,13 +52,13 @@ export const inject = ['locale']
|
||||
|
||||
export function apply(ctx) {
|
||||
ctx.effect(
|
||||
() => ctx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
|
||||
() => ctx.locale.addLanguage({ id: 'fr', label: 'Français', fallback: 'en' }),
|
||||
'my-locale: language',
|
||||
)
|
||||
ctx.effect(
|
||||
() => ctx.locale.register('common', 'ja', {
|
||||
cancel: 'キャンセル',
|
||||
close: '閉じる',
|
||||
() => ctx.locale.register('common', 'fr', {
|
||||
cancel: 'Annuler',
|
||||
close: 'Fermer',
|
||||
}),
|
||||
'my-locale: common dictionary',
|
||||
)
|
||||
@@ -93,7 +93,7 @@ The provisional locale comes from the browser (`navigator.languages` matched by
|
||||
|
||||
Document language synchronization writes `<html lang>` only when its value changes; dictionary-only revisions leave the attribute untouched.
|
||||
|
||||
The typed object form requires complete dictionaries for both built-in locales. The per-locale form lets language packs register each namespace independently. For each key, lookup walks the active language's declared fallback chain in the requested namespace, repeats that chain in `common`, then displays the key itself. Bound translate functions retain stable identity per namespace so they can ride inject surfaces without breaking memoization.
|
||||
The typed object form requires complete dictionaries for both baseline locales. The per-locale form lets language packs register each namespace independently. For each key, lookup walks the active language's declared fallback chain in the requested namespace, repeats that chain in `common`, then displays the key itself. Bound translate functions retain stable identity per namespace so they can ride inject surfaces without breaking memoization.
|
||||
|
||||
### Source map
|
||||
|
||||
@@ -102,7 +102,7 @@ The typed object form requires complete dictionaries for both built-in locales.
|
||||
| [`src/client/index.ts`](src/client/index.ts) | `LocaleRuntime`, dictionary registry, Language row registration, `locale/change` event |
|
||||
| [`src/index.ts`](src/index.ts) | Node half: registers the `locale` settings namespace |
|
||||
| [`src/locale-settings.ts`](src/locale-settings.ts) | The durable schema for `locale.preference` |
|
||||
| [`src/locales/`](src/locales/) | The shipped `zh`/`en` dictionaries |
|
||||
| [`src/locales/`](src/locales/) | The shipped `zh`/`en`/`ja` dictionaries |
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "面向用户与插件作者的 web GUI 本地化说明:zh/en 偏好、浏览器派生回退、类型化命名空间字典与框架翻译席位。"
|
||||
description: "面向用户与插件作者的 web GUI 本地化说明:语言偏好、浏览器派生回退、类型化命名空间字典与框架翻译席位。"
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ kind: "package-reference"
|
||||
|
||||
## 概述
|
||||
|
||||
使用 `dsh-client-locale` 可在 web GUI 中切换内置的英文和中文 locale,或 client 插件添加的语言。用户选择会立即生效;每个已认证浏览器都通过 Host settings 服务把选择以 `locale.preference` 持久化到 `$DSH_HOME/cordis.patch.yml`。全新浏览器会使用浏览器请求的第一个受支持语言,直到 Host 读取送达已存储偏好。插件作者可添加类型化命名空间字典,并通过公开 locale API 翻译;经 slot 渲染的文案无需重新加载即可随语言切换更新。
|
||||
使用 `dsh-client-locale` 可在 web GUI 中切换内置的英文、中文和日文 locale,或 client 插件添加的语言。用户选择会立即生效;每个已认证浏览器都通过 Host settings 服务把选择以 `locale.preference` 持久化到 `$DSH_HOME/cordis.patch.yml`。全新浏览器会使用浏览器请求的第一个受支持语言,直到 Host 读取送达已存储偏好。插件作者可添加类型化命名空间字典,并通过公开 locale API 翻译;经 slot 渲染的文案无需重新加载即可随语言切换更新。
|
||||
|
||||
## 目录
|
||||
|
||||
@@ -37,7 +37,7 @@ kind: "package-reference"
|
||||
|
||||
### 注册字典
|
||||
|
||||
用已合并进 `LocaleNamespaceMap` 的命名空间调用 `ctx.locale.register(ns, { zh, en })`;编译器会对照该命名空间的类型化键并集检查每个键,并要求两个内置 locale 齐全。消费方通过 `ctx.locale.bind(ns)` 或框架注入的 `t` 席位翻译。UI 已挂载后再注册的字典无需重新挂载即可生效。
|
||||
用已合并进 `LocaleNamespaceMap` 的命名空间调用 `ctx.locale.register(ns, { zh, en, ja })`;编译器会对照该命名空间的类型化键并集检查每个键,并要求两个基础 locale(`zh` 和 `en`)齐全。其他 locale id 为可选项,其字典同样按该命名空间的键检查。消费方通过 `ctx.locale.bind(ns)` 或框架注入的 `t` 席位翻译。UI 已挂载后再注册的字典无需重新挂载即可生效。
|
||||
|
||||
### 解析包文本
|
||||
|
||||
@@ -52,13 +52,13 @@ export const inject = ['locale']
|
||||
|
||||
export function apply(ctx) {
|
||||
ctx.effect(
|
||||
() => ctx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
|
||||
() => ctx.locale.addLanguage({ id: 'fr', label: 'Français', fallback: 'en' }),
|
||||
'my-locale: language',
|
||||
)
|
||||
ctx.effect(
|
||||
() => ctx.locale.register('common', 'ja', {
|
||||
cancel: 'キャンセル',
|
||||
close: '閉じる',
|
||||
() => ctx.locale.register('common', 'fr', {
|
||||
cancel: 'Annuler',
|
||||
close: 'Fermer',
|
||||
}),
|
||||
'my-locale: common dictionary',
|
||||
)
|
||||
@@ -93,7 +93,7 @@ export function apply(ctx) {
|
||||
|
||||
文档语言同步只在值变化时写入 `<html lang>`;仅更新字典的 revision 不改动该属性。
|
||||
|
||||
带类型的对象形式要求两个内置 locale 都有完整字典;逐 locale 形式允许语言包独立注册每个命名空间。逐键查找会先在请求命名空间中沿生效语言声明的 fallback 链查找,再在 `common` 中重复该链,最后显示键本身。绑定的翻译函数按命名空间保持稳定身份,因此可通过 inject 机制传递,且不会破坏 memoization。
|
||||
带类型的对象形式要求两个基础 locale 都有完整字典;逐 locale 形式允许语言包独立注册每个命名空间。逐键查找会先在请求命名空间中沿生效语言声明的 fallback 链查找,再在 `common` 中重复该链,最后显示键本身。绑定的翻译函数按命名空间保持稳定身份,因此可通过 inject 机制传递,且不会破坏 memoization。
|
||||
|
||||
### 源码地图
|
||||
|
||||
@@ -102,7 +102,7 @@ export function apply(ctx) {
|
||||
| [`src/client/index.ts`](src/client/index.ts) | `LocaleRuntime`、字典注册表、Language 行注册、`locale/change` 事件 |
|
||||
| [`src/index.ts`](src/index.ts) | node 半侧:注册 `locale` 设置命名空间 |
|
||||
| [`src/locale-settings.ts`](src/locale-settings.ts) | `locale.preference` 的持久 schema |
|
||||
| [`src/locales/`](src/locales/) | 内置的 `zh`/`en` 字典 |
|
||||
| [`src/locales/`](src/locales/) | 内置的 `zh`/`en`/`ja` 字典 |
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
LOCALE_ID_PATTERN, LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE,
|
||||
type BuiltInLocaleId, type LocaleId, type LocaleSettings,
|
||||
} from '../locale-settings.ts'
|
||||
import { en, zh, type CommonKey } from '../locales/index.ts'
|
||||
import { en, ja, zh, type CommonKey } from '../locales/index.ts'
|
||||
import {
|
||||
en as settingsEn, zh as settingsZh, type SettingsLocaleKey,
|
||||
en as settingsEn, ja as settingsJa, zh as settingsZh, type SettingsLocaleKey,
|
||||
} from '../locales/settings.ts'
|
||||
import type { LanguageRowInjected } from './LanguageRow.tsx'
|
||||
import { LanguageRow } from './LanguageRow.tsx'
|
||||
@@ -114,7 +114,7 @@ export const COMMON_NS = 'common'
|
||||
/** Namespace owning this feature's settings-row copy. */
|
||||
export const SETTINGS_NS = 'settings.locale'
|
||||
|
||||
/** The two locales and dictionaries shipped by this package. */
|
||||
/** The baseline locales required by typed dictionary registrations. */
|
||||
const BUILT_IN_LOCALE_METADATA = {
|
||||
zh: { label: '中文', fallback: 'en' },
|
||||
en: { label: 'English' },
|
||||
@@ -378,15 +378,18 @@ export class LocaleRuntime {
|
||||
* Register a declared namespace's dictionaries, all locales in one call —
|
||||
* the typed form: each dictionary is checked against the namespace's
|
||||
* {@link LocaleNamespaceMap} key union (a missing or extra key is a
|
||||
* compile error), and every shipped locale is required (bilingual balance
|
||||
* compile error), and both baseline locales are required (bilingual balance
|
||||
* enforced at registration). Duplicate (ns, locale) throws (single occupant; a
|
||||
* namespace's texts have one owner). Registration bumps the revision so
|
||||
* mounted outlets pick up late-arriving dictionaries.
|
||||
* @param ns - a namespace merged into LocaleNamespaceMap.
|
||||
* @param dicts - complete dictionaries keyed by built-in locale id.
|
||||
* @param dicts - complete baseline dictionaries and optional dictionaries for additional locale ids.
|
||||
* @returns disposer removing every locale registered by this call (idempotent).
|
||||
*/
|
||||
register<N extends Extract<keyof LocaleNamespaceMap, string>>(ns: N, dicts: Record<BuiltInLocaleId, LocaleDictOf<N>>): () => void
|
||||
register<N extends Extract<keyof LocaleNamespaceMap, string>>(
|
||||
ns: N,
|
||||
dicts: Record<BuiltInLocaleId, LocaleDictOf<N>> & Record<LocaleId, LocaleDictOf<N>>,
|
||||
): () => void
|
||||
/**
|
||||
* Single-locale untyped form for language-pack contributions and namespaces
|
||||
* outside the merge table.
|
||||
@@ -576,8 +579,9 @@ export async function apply(ctx: ClientContext): Promise<void> {
|
||||
}
|
||||
const host = ctx.configForms.get<LocaleSettings>(LOCALE_SETTINGS_NAMESPACE)
|
||||
const locale = new LocaleRuntime(ctx, host, bootstrap)
|
||||
locale.register(COMMON_NS, { zh, en })
|
||||
locale.register(SETTINGS_NS, { zh: settingsZh, en: settingsEn })
|
||||
ctx.effect(() => locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }), 'locale: Japanese language')
|
||||
ctx.effect(() => locale.register(COMMON_NS, { zh, en, ja }), 'locale: common dictionaries')
|
||||
ctx.effect(() => locale.register(SETTINGS_NS, { zh: settingsZh, en: settingsEn, ja: settingsJa }), 'locale: settings dictionaries')
|
||||
ctx.provide('locale', locale)
|
||||
if (bridge !== undefined) {
|
||||
ctx.on('locale/change', (snapshot) => { bridge.onChange(snapshot.active) })
|
||||
|
||||
@@ -11,10 +11,10 @@ export const LOCALE_PREFERENCE_FIELD = 'preference'
|
||||
/** Accepted BCP 47-style language ids. */
|
||||
export const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u
|
||||
|
||||
/** Locale identifiers shipped by the browser client. */
|
||||
/** Baseline locales required by typed namespace dictionary registration. */
|
||||
export const LOCALE_IDS = ['zh', 'en'] as const
|
||||
|
||||
/** Locale identifier shipped by the browser client. */
|
||||
/** Baseline locale required by typed namespace dictionary registration. */
|
||||
export type BuiltInLocaleId = typeof LOCALE_IDS[number]
|
||||
|
||||
/** Open locale identifier accepted from language-pack plugins. */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* The common-namespace dictionary pair. zh is the source of truth for the
|
||||
* key set (Chinese-first repo convention); en is checked complete against it
|
||||
* — a missing or extra en key is a compile error.
|
||||
* Common-namespace dictionaries. Chinese defines the keys; English and
|
||||
* Japanese are checked against the same keys by TypeScript.
|
||||
*/
|
||||
export { zh } from './zh.ts'
|
||||
export { en } from './en.ts'
|
||||
export { ja } from './ja.ts'
|
||||
export type { CommonKey } from './zh.ts'
|
||||
@@ -0,0 +1,47 @@
|
||||
/** Japanese common vocabulary. */
|
||||
import type { en } from './en.ts'
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'ok': 'OK',
|
||||
'cancel': 'キャンセル',
|
||||
'close': '閉じる',
|
||||
'copy': 'コピー',
|
||||
'copied': 'コピーしました',
|
||||
'codeBlock.title': 'コードブロック',
|
||||
'codeBlock.wrap': '行を折り返す',
|
||||
'codeBlock.unwrap': '行を折り返さない',
|
||||
'copy.failed': 'コピーに失敗しました',
|
||||
'copy.value': '値をコピー',
|
||||
'copy.json': 'JSONをコピー',
|
||||
'copy.path': 'プロパティパスをコピー',
|
||||
'copy.prettyJson': '整形JSONをコピー',
|
||||
'copy.compactJson': 'コンパクトJSONをコピー',
|
||||
'copy.optionsHint': '{action}。右クリックでコピーオプションを表示',
|
||||
'retry': '再試行',
|
||||
'loading': '読み込み中…',
|
||||
'load.failed': '読み込みに失敗しました',
|
||||
'submit': '送信',
|
||||
'submitting': '送信中…',
|
||||
'next': '次へ',
|
||||
'previous': '前へ',
|
||||
'skip': 'スキップ',
|
||||
'delete': '削除',
|
||||
'edit': '編集',
|
||||
'save': '保存',
|
||||
'search': '検索',
|
||||
'more': 'その他',
|
||||
'collapse': '折りたたむ',
|
||||
'expand': '展開',
|
||||
'back': '戻る',
|
||||
'brand.localBuild': 'DSHローカルビルド',
|
||||
'workspace.defaultName': 'デフォルトワークスペース',
|
||||
'unknown': '不明',
|
||||
'none': 'なし',
|
||||
'truncated': '切り詰め済み',
|
||||
'json.label': 'JSON',
|
||||
'markdown.footnotes': '脚注',
|
||||
'markdown.truncatedCharacters': '… {total}文字で切り捨て',
|
||||
'number.thousand': '{value}K',
|
||||
'number.million': '{value}M',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -12,3 +12,8 @@ export type SettingsLocaleKey = keyof typeof zh
|
||||
export const en = {
|
||||
'language.title': 'Language',
|
||||
} satisfies Record<SettingsLocaleKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'language.title': '言語',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -2,7 +2,7 @@
|
||||
* Language row registration, snapshot projection into the row store, and
|
||||
* recovery after an HMR collapse of the declaring entry. */
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'
|
||||
import { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import { apply as settingsApply, inject as settingsInject } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import { TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
@@ -155,7 +155,7 @@ describe('locale apply', () => {
|
||||
const { entry, instance, face } = faceOf(b.slots)
|
||||
// The inject-time re-sync sealed the init window: the mirror is current.
|
||||
expect(instance.getSnapshot().active).toBe('en')
|
||||
expect(instance.getSnapshot().options.map(o => o.id)).toEqual(['zh', 'en'])
|
||||
expect(instance.getSnapshot().options.map(o => o.id)).toEqual(['zh', 'en', 'ja'])
|
||||
// Copy rides the standard locale seat: the entry declares the namespace.
|
||||
expect(entry.locale).toBe(SETTINGS_NS)
|
||||
expect(locale.bind(SETTINGS_NS)('language.title')).toBe('Language')
|
||||
@@ -176,7 +176,7 @@ describe('locale apply', () => {
|
||||
const languagePack = b.ctx.plugin({
|
||||
inject: ['locale'],
|
||||
apply: packCtx => packCtx.effect(
|
||||
() => packCtx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
|
||||
() => packCtx.locale.addLanguage({ id: 'fr', label: 'Français', fallback: 'en' }),
|
||||
'test language pack registration',
|
||||
),
|
||||
})
|
||||
@@ -185,10 +185,35 @@ describe('locale apply', () => {
|
||||
{ id: 'zh', label: '中文' },
|
||||
{ id: 'en', label: 'English' },
|
||||
{ id: 'ja', label: '日本語' },
|
||||
{ id: 'fr', label: 'Français' },
|
||||
])
|
||||
|
||||
await languagePack.dispose()
|
||||
expect(instance.getSnapshot().options.map(option => option.id)).toEqual(['zh', 'en'])
|
||||
expect(instance.getSnapshot().options.map(option => option.id)).toEqual(['zh', 'en', 'ja'])
|
||||
})
|
||||
|
||||
it('restores Japanese from the Host and releases its language and dictionaries on unload', async () => {
|
||||
const b = await bench('ja')
|
||||
onTestFinished(() => b.ctx.fiber.dispose())
|
||||
declareItems(b.slots)
|
||||
const fiber = b.ctx.plugin({ inject: [...inject], apply })
|
||||
await fiber.await()
|
||||
const locale = b.ctx.get('locale') as LocaleRuntime
|
||||
const { instance, face } = faceOf(b.slots)
|
||||
expect(instance.getSnapshot().active).toBe('ja')
|
||||
expect(locale.bind(SETTINGS_NS)('language.title')).toBe('言語')
|
||||
expect(locale.bind('common')('cancel')).toBe('キャンセル')
|
||||
face.setLocale('en')
|
||||
face.setLocale('ja')
|
||||
await vi.waitFor(() => { expect(b.mutate).toHaveBeenCalledTimes(2) })
|
||||
expect(b.mutate.mock.calls.at(-1)?.[1]).toEqual([{ op: 'set', path: ['preference'], value: 'ja' }])
|
||||
await fiber.dispose()
|
||||
expect(locale.getSnapshot().locales.map(option => option.id)).toEqual(['zh', 'en'])
|
||||
expect(() => { locale.setLocale('ja') }).toThrow('not registered')
|
||||
const remove = locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' })
|
||||
locale.setLocale('ja')
|
||||
expect(locale.bind('common')('cancel')).toBe('cancel')
|
||||
remove()
|
||||
})
|
||||
|
||||
it('loads and refreshes the explicit Host preference after nonblocking activation', async () => {
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('document language', () => {
|
||||
const observer = new MutationObserver(() => {})
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] })
|
||||
try {
|
||||
const remove = locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' })
|
||||
const remove = locale.addLanguage({ id: 'fr', label: 'Français', fallback: 'en' })
|
||||
expect(observer.takeRecords()).toHaveLength(0)
|
||||
remove()
|
||||
expect(observer.takeRecords()).toHaveLength(0)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { stubConfigForm, type StubConfigForm } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
import type { LocaleSettings, LocaleSnapshot } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { zh, en, ja } from '../src/locales/settings.ts'
|
||||
import { FALLBACK_LOCALE, LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
|
||||
const make = (host?: StubConfigForm<LocaleSettings>): {
|
||||
ctx: Context
|
||||
@@ -36,6 +37,24 @@ describe('LocaleRuntime', () => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it('checks additional dictionaries against the declared namespace and requires both baseline locales', () => {
|
||||
const { svc } = make()
|
||||
svc.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' })
|
||||
const dispose = svc.register('settings.locale', { zh, en, ja, fr: ja })
|
||||
svc.setLocale('ja')
|
||||
expect(svc.bind('settings.locale')('language.title')).toBe('言語')
|
||||
dispose()
|
||||
expect(svc.bind('settings.locale')('language.title')).toBe('language.title')
|
||||
if (false) {
|
||||
// @ts-expect-error additional locales require the declared namespace's keys
|
||||
svc.register('settings.locale', { zh, en, ja: { other: '別' } })
|
||||
// @ts-expect-error additional locales cannot omit a namespace key
|
||||
svc.register('settings.locale', { zh, en, ja: {} })
|
||||
// @ts-expect-error additional locales do not replace the English baseline
|
||||
svc.register('settings.locale', { zh, ja })
|
||||
}
|
||||
})
|
||||
|
||||
it('translates through the active-locale -> en -> key chain', () => {
|
||||
const { svc } = make()
|
||||
svc.register('ns', 'zh', { hello: '你好' })
|
||||
|
||||
@@ -150,3 +150,26 @@ export const guideZh: Record<PresetGuideKey, string> = {
|
||||
'预期产出:可在新任务中选择的自定义模式。审查要求用于指导 Agent,实际可执行的操作仍由权限设置决定。',
|
||||
].join('\n\n'),
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const guideJa = {
|
||||
'modeExplanation': 'モードの詳細',
|
||||
'howToUse': '使い方',
|
||||
'guideSections': 'ガイドのセクション',
|
||||
'guideExampleTask': 'タスク例',
|
||||
'guideCopy': 'コピー',
|
||||
'guideCopied': 'コピーしました',
|
||||
'guideFootnotes': '脚注',
|
||||
'guideStandardIntro': '新しいタスクを始めるときは標準モードを選びます。達成したいことを説明し、関連するファイルを示し、結果の確認方法を伝えてください。',
|
||||
'guideStandardExplanation': '### 仕組み\n\nエージェントはツールを直接呼び出してファイルの読み取りや編集、検索、ターミナルコマンドの実行を行います。スキル、計画、目標、サブエージェント、ワークフロー、プラグイン管理、コンテキストの圧縮に対応しています。\n\n### 選ぶべき場面\n\n日々のコーディング、ファイル作業、調査はまずここから始めてください。標準モードではスクリプトの作成やファイルの一括処理も行えます。PTC はツール呼び出しの構成を変えるものであり、一括処理に必須ではありません。',
|
||||
'guideStandardUsage': '### バグを修正する\n\n> 検索フォームを二度送信すると結果が消える原因を調べてください。修正して関連するテストを実行し、原因と変更内容を説明してください。\n\n期待される出力: コードの変更、関連するテスト結果、原因の説明。\n\n### プロジェクトのメモを整理する\n\n> このプロジェクト内の Markdown メモを読み、合意された決定と未解決の課題を、元ファイルへのリンク付きで要約してください。\n\n期待される出力: 元のメモと照合できる参照付きの要約。',
|
||||
'guidePtcIntro': '新しいタスクを始めるときは PTC モードを選びます。入力ファイル、処理ルール、出力形式を指定してください。コードはエージェントが書きます。',
|
||||
'guidePtcExplanation': '### ツールの呼び出され方\n\nPTC は Programmatic Tool Calling の略です。この組み込みプリセットでは、エージェントは run_code を使い、生成された SDK 経由でツールを呼び出す TypeScript プログラムを書きます。プログラムではループ、条件分岐、エラー処理、必要に応じて並行呼び出しを利用できます。\n\n### モデルに届くもの\n\nツールの結果はまずプログラムに届き、そこでフィルタリングや結合ができます。モデルが受け取るのはプログラムが出力または返した内容です。画像の結果は別途添付されます。ネストされたツール呼び出しも記録され、ツール権限の対象のままです。\n\n### 標準モードとの比較\n\nどちらのモードもコーディングや一括処理のタスクを扱えます。標準モードは個々のツールを直接公開し、PTC はツール呼び出しをコードで整理します。現在の PTC プリセットではワークフローとプラグイン管理ツールが無効になっています。速度とトークン消費はタスクやプログラムの結果の扱い方によって変わります。',
|
||||
'guidePtcUsage': '### 設定ファイルのセットを検証する\n\n> configs/ 内のすべての JSON ファイルを検査してください。schema.json に基づき、必須フィールドの欠落と無効な値を一覧にしてください。問題ごとに 1 行の CSV を保存してください。読み取れないファイルもレポートに含め、残りの検査は続けてください。元のファイルは変更しないでください。\n\n期待される出力: 問題の要約と CSV レポート。プログラムは同じ検査を繰り返し、個々の失敗に対処し、結果を集約できます。\n\n### エラーログを要約する\n\n> logs/ 内のログファイルを分析してください。エラーをサービスとエラー種別でグループ化し、頻度上位 10 グループと各 1 件の例を示してください。完全な件数を CSV に保存してください。\n\n期待される出力: 上位のエラーグループと完全な件数表。中間データは要約がモデルに届く前にプログラム内で集約できます。',
|
||||
'guideMinimalIntro': '新しいタスクには最小モードを選びます。比較を行う場合は、モデル、権限、入力、開始時のワークスペース状態を実行間で一定に保ってください。',
|
||||
'guideMinimalExplanation': '### 含まれるもの\n\n永続的なシェルツール 1 つ、read_image、固定のシステムプロンプト。画像の読み取りには画像対応モデルが必要です。古い履歴はコンテキストの負荷に応じて自動的に圧縮されます。エージェントがアイドル状態のときに /compact を使えば手動で圧縮できます。この組み込みプリセットはスキル、計画、標準のランタイムコンテキストを読み込みません。\n\n### 選ぶべき場面\n\n実験や比較のベースラインとして使用します。シェルコマンド経由でファイルの読み取りやスクリプトの実行はできますが、長いタスクを管理するための組み込み手段は少なくなります。ツールが少ないからといって、初心者にとって必ずしも簡単になるわけではありません。',
|
||||
'guideMinimalUsage': '### 小さなバグ修正で性能を比較する\n\n> このプロジェクトのテストを実行し、失敗の原因を見つけて最小限の修正をしてください。関連するテストを再度実行し、結果を報告してください。\n\n同じ開始状態から、標準モードと最小モードで同じタスクを別々に実行します。タスクの完了状況、ツール呼び出し、生じた変更を比較してください。最小モードはターミナルコマンドを通じて作業を行います。',
|
||||
'guideCordisIntro': '新しいタスクにはクリエイターモードを選びます。実現したい機能、表示される場所、確認方法を説明してください。',
|
||||
'guideCordisExplanation': '### 作成できるもの\n\nクリエイターモードには標準的なタスクツールに加え、ランタイムの検査、Cordis プラグインやエージェントプリセットの作成ガイドが含まれます。機能や UI を追加するプラグインを作成することも、特定の作業向けにツールとプロンプトを組み合わせたプリセットを作成することもできます。\n\n### プラグインとモード\n\nプラグインはツール、サービス接続、UI エントリなど、DSH に機能を追加します。モードはエージェントプリセットであり、ツールを選択し、タスク内でのエージェントの動作を定義します。プラグインはカスタムプリセットに含めることができます。\n\n### 結果を反映させる方法\n\nソースコードを生成するだけでなく、エージェントにインストールと確認を依頼してください。プラグインは変更内容によっては即座に読み込まれるか、再起動が必要になる場合があります。新しく作成したプリセットは、新しいタスクを始めるときに選択されます。',
|
||||
'guideCordisUsage': '### UI を追加する\n\n> サイドバーにプロジェクトメモのエントリを追加する DSH プラグインを作成してください。このワークスペース内の Markdown ファイルを閲覧でき、選択したメモをプレビューできるようにしてください。インストールしてページが開くことを確認してください。\n\n期待される出力: 動作するエントリとプレビューページを備えた、インストール済みのプラグイン。残っている有効化手順があればそれも含みます。\n\n### ツールを追加する\n\n> このプロジェクトのテストレポートを読み、失敗したテストを要約するツールを備えたプラグインを作成してください。登録し、サンプルのレポートで確認してください。\n\n期待される出力: 呼び出し可能なツールを備えたプラグインと、確認済みのサンプル呼び出し。\n\n### 独自のモードを作成する\n\n> 標準モードをベースに「コードレビュー」モードを作成してください。潜在的なバグとテストの不足を優先し、ファイルパスと行番号を示し、ファイルを変更する前に確認を求めるようにしてください。選択可能なプリセットとして保存してください。\n\n期待される出力: 新しいタスク用のカスタムプリセット。これらのレビュー指示はエージェントを導くものであり、実行できる操作は権限設定によって決まります。',
|
||||
} satisfies Record<keyof typeof guideEn, string>
|
||||
@@ -42,7 +42,7 @@ import { AgentPresetSection } from './AgentPresetSection.tsx'
|
||||
import type { AgentPresetSectionInjected } from './AgentPresetSection.tsx'
|
||||
import { AgentPresetSeatController, type AgentPresetStage } from './seat-store.ts'
|
||||
import { AgentPresetSectionController } from './section-store.ts'
|
||||
import { en, zh, type AgentPresetSettingsKey } from './locales.ts'
|
||||
import { en, ja, zh, type AgentPresetSettingsKey } from './locales.ts'
|
||||
import { AGENT_PRESET_SETTINGS_NS, AgentPresetSettingsController } from './settings-store.ts'
|
||||
import { analyticsMode } from './analytics.ts'
|
||||
|
||||
@@ -128,7 +128,7 @@ export function apply(ctx: ClientContext): void {
|
||||
return binding === undefined ? undefined : seatFor(binding)
|
||||
}
|
||||
|
||||
ctx.effect(() => ctx.locale.register('settings.agentPreset', { zh, en }), 'ui-agent-preset: settings row dictionaries')
|
||||
ctx.effect(() => ctx.locale.register('settings.agentPreset', { zh, en, ja }), 'ui-agent-preset: settings row dictionaries')
|
||||
|
||||
ctx.effect(() => {
|
||||
let requested = 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Locale bundles for the agent-preset hero chip, header label, and management section. */
|
||||
|
||||
import { guideEn, guideZh, type PresetGuideKey } from './guide-locales.ts'
|
||||
import { guideEn, guideJa, guideZh, type PresetGuideKey } from './guide-locales.ts'
|
||||
|
||||
/** Locale keys these surfaces render. */
|
||||
export type AgentPresetSettingsKey =
|
||||
@@ -125,3 +125,36 @@ export const zh: Record<AgentPresetSettingsKey, string> = {
|
||||
// Settings plugin list inlines the same fold over this plugin's dictionaries.
|
||||
export { isBuiltInPreset, presetDisplayText } from '@deepseek-ai/dsh-agent-preset-registry/display'
|
||||
export type { PresetDisplaySource, PresetDisplayText } from '@deepseek-ai/dsh-agent-preset-registry/display'
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
...guideJa,
|
||||
'builtInGroup': '組み込み',
|
||||
'customGroup': 'カスタム',
|
||||
'sectionIntro': 'エージェントのツールと動作を選択します。日々のタスクには標準モードを、DSH に機能を追加するにはクリエイターモードを使用してください。',
|
||||
'seatHint': '新しいタスクで使用するエージェントプリセットを選択',
|
||||
'headerHint': 'このタスクの開始時に選択されたエージェントプリセット',
|
||||
'nav': 'エージェントプリセット',
|
||||
'setDefault': '新しいタスクの既定に設定',
|
||||
'view': '設定を表示',
|
||||
'presetStandardName': '標準モード',
|
||||
'presetStandardDescription': 'コード、ファイル、情報を扱います。検索、編集、ターミナルコマンドなどのツールを必要に応じて利用でき、ほとんどのタスクに適しています。',
|
||||
'presetPtcName': 'PTC モード',
|
||||
'presetPtcDescription': '標準モードのすべての機能を含みます。ツールを一括で呼び出し、その結果を絞り込み、整理、重複排除、集計、要約するタスクに適しています。',
|
||||
'presetMinimalName': '最小モード',
|
||||
'presetMinimalDescription': 'エージェントはターミナルと画像読み取りを使用し、コンテキスト圧縮にも対応します。基本的な性能のテストや比較に役立ちます。',
|
||||
'presetCordisName': 'クリエイターモード',
|
||||
'presetCordisDescription': '会話を通じて DSH をカスタマイズします。機能や UI を追加するプラグインをエージェントに書かせたり、ツールとプロンプトを組み合わせて独自のモードを作成したりできます。',
|
||||
'inUse': '新しいタスクの既定',
|
||||
'noDescription': '説明はありません。',
|
||||
'brokenBadge': '読み込みに失敗',
|
||||
'switchRefused': '{name} に切り替えられませんでした: {reason}',
|
||||
'standardUnavailable': '標準モードは利用できません。復元するか、利用可能な別のモードを選択してください。',
|
||||
'close': '閉じる',
|
||||
'creatorDraft': 'エージェントにプリセット作成を手伝ってもらう',
|
||||
'createPlugin': 'エージェントにプラグインを作成してもらう',
|
||||
'createPluginDescription': 'クリエイターモードに入り、独自の DSH プラグインを作成',
|
||||
'createPluginChecking': 'クリエイターモードが利用可能か確認中',
|
||||
'createPluginUnavailable': '一時的に利用できません。このメニューを開き直して再試行してください',
|
||||
'createPluginMissing': 'この構成にはクリエイターモードが含まれていません',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -1,7 +1,7 @@
|
||||
/** Web-localized copy for the four shipped presets and declared copy for every other row. */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { en, presetDisplayText, zh } from '../src/client/locales.ts'
|
||||
import { en, ja, presetDisplayText, zh } from '../src/client/locales.ts'
|
||||
|
||||
const translate = (bundle: typeof en) => (key: keyof typeof en): string => bundle[key]
|
||||
|
||||
@@ -11,13 +11,15 @@ describe('preset display copy', () => {
|
||||
['ptc', 'presetPtcName', 'presetPtcDescription'],
|
||||
['minimal', 'presetMinimalName', 'presetMinimalDescription'],
|
||||
['cordis', 'presetCordisName', 'presetCordisDescription'],
|
||||
] as const)('localizes the shipped %s preset in English and Chinese', (id, nameKey, descriptionKey) => {
|
||||
] as const)('localizes the shipped %s preset in English, Chinese, and Japanese', (id, nameKey, descriptionKey) => {
|
||||
const preset = { id }
|
||||
|
||||
expect(presetDisplayText(preset, translate(en)))
|
||||
.toEqual({ name: en[nameKey], description: en[descriptionKey] })
|
||||
expect(presetDisplayText(preset, translate(zh)))
|
||||
.toEqual({ name: zh[nameKey], description: zh[descriptionKey] })
|
||||
expect(presetDisplayText(preset, translate(ja)))
|
||||
.toEqual({ name: ja[nameKey], description: ja[descriptionKey] })
|
||||
})
|
||||
|
||||
it('keeps declared metadata untranslated for named and unknown presets', () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { ShortcutCommandId } from '@deepseek-ai/dsh-client-shortcuts/client
|
||||
import { ApprovalCard } from './ApprovalCard.tsx'
|
||||
import { ApprovalPanel } from './ApprovalPanel.tsx'
|
||||
import { PendingApproval } from './contract/slots.ts'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
|
||||
export type {
|
||||
ApprovalCardInput,
|
||||
@@ -77,7 +77,7 @@ async function answerApproval(
|
||||
* @param ctx - Client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-approval: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-approval: dictionaries')
|
||||
ctx.inject(['shortcuts'], (scope) => {
|
||||
const t = ctx.locale.bind(NS)
|
||||
scope.effect(() => scope.shortcuts.registerFixed({
|
||||
|
||||
@@ -24,3 +24,14 @@ export const en = {
|
||||
allowOnce: 'Allow once',
|
||||
allowAlways: 'Always allow',
|
||||
} satisfies Record<ApprovalKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'waiting': '承認待ち',
|
||||
'detail.aria': '承認の詳細',
|
||||
'escalation': 'ツール {toolName} が特権実行を要求しています',
|
||||
'reject': '拒否',
|
||||
'allow': '許可',
|
||||
'allowOnce': '1 回だけ許可',
|
||||
'allowAlways': '常に許可',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -36,7 +36,7 @@ import { UserContent } from './chat/UserContent.tsx'
|
||||
import { ActivityPill, UsagePill } from './chat/StatsPills.tsx'
|
||||
import { registerConversationNodes } from './conversation-nodes/register.ts'
|
||||
import { QuotaNoticeHost } from './chat/QuotaNoticeHost.tsx'
|
||||
import { en, NS, zh } from './locale.ts'
|
||||
import { en, ja, NS, zh } from './locale.ts'
|
||||
import { TranscriptViewRow, type TranscriptViewRowInjected } from './settings/TranscriptViewRow.tsx'
|
||||
import { createChatStore } from './stores.ts'
|
||||
import { TranscriptViewPolicy } from './transcript-view.ts'
|
||||
@@ -133,7 +133,7 @@ export function apply(ctx: Context): void {
|
||||
resolve: binding => ({ hooks: { chat: chatSource(binding) } }),
|
||||
})
|
||||
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-chat: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-chat: dictionaries')
|
||||
const t = ctx.locale.bind(NS)
|
||||
const chatStore = createChatStore()
|
||||
const chatScrollPositions = new Map<SessionId, ChatScrollPosition>()
|
||||
|
||||
@@ -403,3 +403,202 @@ export const en = {
|
||||
'clock.md': '{m}/{d}',
|
||||
'clock.ymd': '{y}-{m}-{d}',
|
||||
} satisfies Record<ChatKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'message.stepProcess.thinking': 'リクエストを分析中',
|
||||
'message.stepProcess.read': 'ファイルを読み取り中',
|
||||
'message.stepProcess.readImage': '画像を読み取り中',
|
||||
'message.stepProcess.write': 'ファイルを書き込み中',
|
||||
'message.stepProcess.search': 'コードを検索中',
|
||||
'message.stepProcess.edit': 'ファイルを編集中',
|
||||
'message.stepProcess.commands': 'コマンドを実行中',
|
||||
'message.stepProcess.code': 'コードを実行中',
|
||||
'message.stepProcess.webSearch': 'ウェブを検索中',
|
||||
'message.stepProcess.webFetch': 'ウェブページを閲覧中',
|
||||
'message.stepProcess.subagents': 'サブエージェントを調整中',
|
||||
'message.stepProcess.plan': '計画を更新中',
|
||||
'message.stepProcess.questions': '操作を待機中',
|
||||
'message.stepProcess.tools': 'ツールを呼び出し中',
|
||||
'message.stepProcess.prepare.read': 'ファイル読み取りの準備中',
|
||||
'message.stepProcess.prepare.readImage': '画像読み取りの準備中',
|
||||
'message.stepProcess.prepare.write': 'ファイル書き込みの準備中',
|
||||
'message.stepProcess.prepare.search': 'コード検索の準備中',
|
||||
'message.stepProcess.prepare.edit': 'ファイル編集の準備中',
|
||||
'message.stepProcess.prepare.commands': 'コマンド実行の準備中',
|
||||
'message.stepProcess.prepare.code': 'コード実行の準備中',
|
||||
'message.stepProcess.prepare.webSearch': 'ウェブ検索の準備中',
|
||||
'message.stepProcess.prepare.webFetch': 'ウェブページ閲覧の準備中',
|
||||
'message.stepProcess.prepare.subagents': 'サブエージェント調整の準備中',
|
||||
'message.stepProcess.prepare.plan': '計画更新の準備中',
|
||||
'message.stepProcess.prepare.questions': '質問の準備中',
|
||||
'message.stepProcess.prepare.tools': 'ツール呼び出しの準備中',
|
||||
'message.stepProcess.done.thinking': '分析完了',
|
||||
'message.stepProcess.done.read': 'ファイルを読み取りました',
|
||||
'message.stepProcess.done.readImage': '画像を読み取りました',
|
||||
'message.stepProcess.done.write': 'ファイルを書き込みました',
|
||||
'message.stepProcess.done.search': 'コードを検索しました',
|
||||
'message.stepProcess.done.edit': 'ファイルを編集しました',
|
||||
'message.stepProcess.done.commands': 'コマンドを実行しました',
|
||||
'message.stepProcess.done.code': 'コードを実行しました',
|
||||
'message.stepProcess.done.webSearch': 'ウェブを検索しました',
|
||||
'message.stepProcess.done.webFetch': 'ウェブページを閲覧しました',
|
||||
'message.stepProcess.done.subagents': 'サブエージェントを調整しました',
|
||||
'message.stepProcess.done.plan': '計画を更新しました',
|
||||
'message.stepProcess.done.questions': '質問しました',
|
||||
'message.stepProcess.done.tools': 'ツールを呼び出しました',
|
||||
'message.stepProcess.joinTwo': '{first}と{second}',
|
||||
'message.stepProcess.comma': '、',
|
||||
'message.stepProcess.sharedPrefix': '',
|
||||
'message.stepProcess.more': '{title}など',
|
||||
'message.trigger.request': '実行が要求されました',
|
||||
'message.trigger.goal': '目標を継続中',
|
||||
'message.trigger.agent': 'タスクメッセージを受信しました',
|
||||
'message.trigger.team': 'チームメッセージを受信しました',
|
||||
'message.trigger.subagent': 'サブタスクの状態が更新されました',
|
||||
'message.trigger.github': 'GitHubイベントを受信しました',
|
||||
'message.trigger.webhook': '外部イベントを受信しました',
|
||||
'message.trigger.schedule': '自動化タスク',
|
||||
'message.trigger.job': 'バックグラウンドタスクが更新されました',
|
||||
'message.trigger.plugin': 'プラグインの状態が更新されました',
|
||||
'message.trigger.explanation': 'この通知がこの応答のトリガーとなりました。',
|
||||
'message.turnProcess.worked': '完了',
|
||||
'message.turnProcess.took': '完了まで ',
|
||||
'message.turnProcess.failed': '失敗',
|
||||
'view.chat': 'チャット',
|
||||
'image.open': '画像を全画面表示',
|
||||
'image.loading': '画像を読み込み中…',
|
||||
'image.failed': '画像プレビューを利用できません',
|
||||
'image.dialog': '画像プレビュー',
|
||||
'image.close': '画像プレビューを閉じる',
|
||||
'number.groupSeparator': ',',
|
||||
'duration.compactSeconds': '{seconds}秒',
|
||||
'duration.compactMinutes': '{minutes}分{seconds}秒',
|
||||
'duration.milliseconds': '{milliseconds}ms',
|
||||
'stats.counts': '{turns}ターン {steps}ステップ',
|
||||
'stats.cacheHit': 'キャッシュヒット {percent}%',
|
||||
'stats.dialog.title': 'セッション統計',
|
||||
'stats.dialog.usageTitle': 'トークン使用量',
|
||||
'stats.dialog.llmTime': 'LLM時間',
|
||||
'stats.dialog.toolTime': 'ツール時間',
|
||||
'stats.dialog.ttft': '平均初回トークン時間(TTFT)',
|
||||
'stats.dialog.speed': '毎秒トークン数(TPS)',
|
||||
'chat.loadingHistory': '履歴を読み込み中…',
|
||||
'chat.loadError': '履歴の読み込みに失敗しました: {message}({code})',
|
||||
'chat.loadOlder': '以前を読み込む',
|
||||
'chat.toBottom': '最下部へ戻る',
|
||||
'chat.deepDiving': '深掘り中',
|
||||
'chat.deepDivingFor': '{duration} の間深く分析中 ···',
|
||||
'chat.turnNavigation.label': 'ターン移動',
|
||||
'chat.turnNavigation.jump': 'ターン {turn} へ移動',
|
||||
'chat.turnNavigation.jumpLoad': '読み込んでターン {turn} へ移動',
|
||||
'chat.turnNavigation.turn': 'ターン {turn}',
|
||||
'settings.performance.title': 'パフォーマンスと使用状況',
|
||||
'settings.performance.description': 'パフォーマンスと使用状況の表示量を選択',
|
||||
'settings.performance.compact': 'コンパクト',
|
||||
'settings.performance.detailed': '詳細',
|
||||
'settings.links.title': 'チャットリンクを開く場所',
|
||||
'settings.links.description': 'Web リンクを開く場所を選択',
|
||||
'settings.links.sidebar': 'アプリ内サイドバー',
|
||||
'settings.links.newTab': '既定のブラウザー',
|
||||
'settings.transcript.title': '作業の詳細',
|
||||
'settings.transcript.description': 'ツール呼び出しの表示詳細度を選択',
|
||||
'settings.collapse.title': '作業詳細を折りたたむタイミング',
|
||||
'settings.collapse.description': '作業詳細を自動で折りたたむタイミングを選択',
|
||||
'settings.collapse.completion': '完了時',
|
||||
'settings.collapse.nextInput': '次のメッセージ送信時',
|
||||
'settings.foldedReplies.title': '折りたたみ後の返信',
|
||||
'settings.foldedReplies.description': '作業詳細の折りたたみ時に表示するアシスタントの返信を選択',
|
||||
'settings.foldedReplies.all': 'すべての返信',
|
||||
'settings.foldedReplies.final': '最終回答のみ',
|
||||
'settings.transcript.compact': 'コンパクト',
|
||||
'settings.transcript.standard': '標準',
|
||||
'settings.transcript.detailed': '詳細',
|
||||
'settings.transcript.verbose': '冗長',
|
||||
'fileOpen.title': 'ファイルを開けませんでした',
|
||||
'fileOpen.unknown': 'このファイルを開けませんでした',
|
||||
'message.extraBlock': '追加のコンテンツブロック',
|
||||
'message.systemPrompt': 'システムプロンプト',
|
||||
'message.systemPromptUpdate': 'システムプロンプトの更新',
|
||||
'message.toolAdded': 'ツールを追加: {name}',
|
||||
'message.toolRemoved': 'ツールを削除: {name}',
|
||||
'message.toolsAdded': '追加: {names}',
|
||||
'message.toolsAddedCount': '{count} 件追加',
|
||||
'message.toolsChanged': '{added} 件追加、{removed} 件削除',
|
||||
'message.toolsRemoved': '削除: {names}',
|
||||
'message.toolsRemovedCount': '{count} 件削除',
|
||||
'message.toolsUpdated': 'ツールを更新しました',
|
||||
'message.contextInjection': 'コンテキスト注入',
|
||||
'message.contextRecall': 'セッションの呼び出し',
|
||||
'message.referenceSummary': '参照セッション · {labels}',
|
||||
'message.referenceSeparator': ', ',
|
||||
'message.context.instructions.loaded': '読み込み済み',
|
||||
'message.context.instructions.added': '追加済み',
|
||||
'message.context.instructions.updated': '更新済み',
|
||||
'message.context.instructions.removed': '削除済み',
|
||||
'message.context.catalog.replaced': '置換カタログ',
|
||||
'message.context.catalog.more': '… 他 {count} 件',
|
||||
'message.context.snapshot.supersedes': '以前のスナップショットを置き換えます',
|
||||
'message.context.relay.from': 'セッション {session} から',
|
||||
'message.context.recall.counts': '{retained} 件保持 · {omitted} 件除外',
|
||||
'message.context.recall.truncated': '切り詰め済み',
|
||||
'message.compaction': 'コンテキストを圧縮しました',
|
||||
'message.compaction.running': 'コンテキストを圧縮中…',
|
||||
'message.compaction.completed': '履歴 {items} 件を圧縮しました(約 {tokens} トークン)',
|
||||
'message.compaction.expand': '圧縮の要約を表示',
|
||||
'message.compaction.unavailable': '圧縮の要約は利用できません',
|
||||
'message.compaction.commandTitle': 'compact',
|
||||
'message.think': '思考',
|
||||
'message.unknownSurface': '不明なサーフェスイベント: {type}',
|
||||
'message.unknownBlock': '不明なコンテンツブロック',
|
||||
'message.turnProcess.toolCalls.one': 'ツール呼び出し {count} 件',
|
||||
'message.turnProcess.toolCalls.other': 'ツール呼び出し {count} 件',
|
||||
'message.turnProcess.messages.one': 'メッセージ {count} 件',
|
||||
'message.turnProcess.messages.other': 'メッセージ {count} 件',
|
||||
'message.turnProcess.subagents.one': 'サブエージェント {count} 件',
|
||||
'message.turnProcess.subagents.other': 'サブエージェント {count} 件',
|
||||
'message.turnProcess.thoughtForAWhile': 'しばらく思考しました',
|
||||
'message.turnProcess.separator': ' · ',
|
||||
'message.stopped': '停止しました',
|
||||
'message.branch': '新しい会話に分岐',
|
||||
'message.branchUnavailable': '完了したターンの最後のメッセージでのみ利用できます',
|
||||
'message.retry.active': 'モデルリクエストを再試行中',
|
||||
'message.retry.cancelled': 'モデルリクエストの再試行をキャンセルしました',
|
||||
'message.retry.started': 'モデルリクエストを再試行しました',
|
||||
'message.retry.scheduled': 'モデルリクエストの再試行待ちです',
|
||||
'message.retry.status': '{label} ({retry}/{maximum}) · {seconds}秒',
|
||||
'message.retry.delay': '再試行までの待機時間: ',
|
||||
'message.retry.failure': '失敗理由: ',
|
||||
'message.failure.auth': 'APIキーが無効です',
|
||||
'message.taskStopped': 'タスクを停止しました',
|
||||
'message.failure.accountSignedOut': 'DeepSeekからログアウトしたため停止しました。',
|
||||
'message.failure.interrupted': 'このターンが完了する前にDSHプロセスが終了したため(強制終了、クラッシュ、シャットダウン中のタイムアウトなど)、DSHの再起動時にターンは閉じられました。未完了のツール呼び出しの結果は不明です。',
|
||||
'message.failure.accountSignInRequired': 'DeepSeekにログインし、リクエスト先がアカウント認証に対応していることを確認してください。',
|
||||
'message.failure.quota': 'リクエストのクォータを使い切りました。',
|
||||
'message.turnError': 'このターンは失敗しました',
|
||||
'message.maxTokens': '出力トークンの上限に達しました',
|
||||
'message.maxTokens.hint': '応答が途中で打ち切られました。それまでの出力は会話に保持されています。「続けて」と送信するとモデルが再開します。',
|
||||
'message.tokensPerSecond': '{tps} tok/s',
|
||||
'message.turnUsage.title': 'ターンの使用量',
|
||||
'message.turnUsage.consumed': '使用量 {total}',
|
||||
'message.turnUsage.model': 'プロバイダー / モデル',
|
||||
'message.turnUsage.cacheHit': 'キャッシュヒット',
|
||||
'message.turnUsage.input': '未キャッシュの入力',
|
||||
'message.turnUsage.cacheRead': 'キャッシュ済み入力',
|
||||
'message.turnUsage.cacheWrite': 'キャッシュ書き込み',
|
||||
'message.turnUsage.output': '出力',
|
||||
'message.turnUsage.reasoning': ' (推論 {tokens})',
|
||||
'message.turnUsage.count': '{count} tok',
|
||||
'duration.secondUnit': '秒',
|
||||
'duration.minuteUnit': '分 ',
|
||||
'duration.hourUnit': '時間 ',
|
||||
'command.running': '実行中…',
|
||||
'command.failed': 'コマンドが失敗しました',
|
||||
'command.done': '完了',
|
||||
'command.title': 'コマンド',
|
||||
'row.running': '実行中',
|
||||
'row.failed': '失敗',
|
||||
'json.truncated': '… 省略表示、全{total}文字',
|
||||
'clock.md': '{m}/{d}',
|
||||
'clock.ymd': '{y}-{m}-{d}',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -18,7 +18,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-session/client'
|
||||
import { CommandUiRuntime } from './service.ts'
|
||||
import type { PopupSelectInjected } from './PopupSelectView.tsx'
|
||||
import { PopupSelectView } from './PopupSelectView.tsx'
|
||||
import { en, zh, type CommandKey } from './locales.ts'
|
||||
import { en, ja, zh, type CommandKey } from './locales.ts'
|
||||
|
||||
export { CommandUiRuntime } from './service.ts'
|
||||
export { CommandDirectory } from './directory.ts'
|
||||
@@ -56,7 +56,7 @@ export const inject = ['inputTriggers', 'sessions', 'remote', 'remote.commands',
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-commands: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-commands: dictionaries')
|
||||
ctx.plugin(CommandUiRuntime)
|
||||
ctx.inject(['slots', 'commandUi', 'sessions'], (scope: ClientContext) => {
|
||||
const command = scope.commandUi
|
||||
|
||||
@@ -71,3 +71,35 @@ export const en = {
|
||||
'listbox.aria': '/{command} matches',
|
||||
'notice.attachmentsUnsupported': '/{command} does not accept attachments; remove them first',
|
||||
} satisfies Record<CommandKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'section.add': '追加',
|
||||
'section.commands': 'コマンド',
|
||||
'label.goal': '目標',
|
||||
'label.plan': '計画',
|
||||
'label.feedback': 'フィードバック',
|
||||
'label.compact': '圧縮',
|
||||
'label.permission': '権限',
|
||||
'label.export': 'エクスポート',
|
||||
'description.goal': '長時間実行するタスクの目標を設定または表示',
|
||||
'description.plan': '計画モードに入るまたは終了する',
|
||||
'description.feedback': 'このセッションに関するフィードバックを記録',
|
||||
'description.compact': '古い会話履歴を圧縮',
|
||||
'description.permission': '権限プリセットを切り替える (サンドボックスモード + 承認ポリシー)',
|
||||
'description.export': 'このセッションログを ZIP アーカイブとしてダウンロード',
|
||||
'token.goal': 'goal',
|
||||
'token.plan': 'plan',
|
||||
'token.feedback': 'feedback',
|
||||
'token.compact': 'compact',
|
||||
'token.permission': 'permission',
|
||||
'token.export': 'export',
|
||||
'search.placeholder': '検索…',
|
||||
'search.aria': 'オプションを絞り込む',
|
||||
'status.loading': 'オプションを読み込み中…',
|
||||
'status.applying': '適用中…',
|
||||
'status.empty': 'オプションなし',
|
||||
'overlay.aria': '/{command} のオプション',
|
||||
'listbox.aria': '/{command} の一致項目',
|
||||
'notice.attachmentsUnsupported': '/{command} は添付ファイルを受け付けません。先に削除してください',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -46,7 +46,7 @@ import { todoDockEntry } from './skeleton/TodoPanel.tsx'
|
||||
import { installStopShortcut } from './stop-shortcut.ts'
|
||||
import { TRAJECTORY_VIEW_ID, resolveActiveView } from './view-selection.ts'
|
||||
import { createViewPins } from './view-pins.ts'
|
||||
import { en, NS, zh, type ConversationKey } from './locales.ts'
|
||||
import { en, ja, NS, zh, type ConversationKey } from './locales.ts'
|
||||
import { CONVERSATION_SETTINGS_NAMESPACE, type ConversationSettings } from '../submission-settings.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
@@ -185,7 +185,7 @@ export function apply(ctx: Context, config: Config = Config({})): void {
|
||||
yield slots.register({ name: 'conversation.user.text.node', key: 'session', priority: 100 }, SessionReference)
|
||||
})
|
||||
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-conversation: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-conversation: dictionaries')
|
||||
const t = ctx.locale.bind(NS)
|
||||
const conversationStore = createConversationStore()
|
||||
const submissionPolicy = new ComposerSubmissionPolicy(
|
||||
|
||||
@@ -769,3 +769,381 @@ export const en = {
|
||||
'terminal.sendInput': '(send input)',
|
||||
'terminal.session': 'Terminal {sessionId}',
|
||||
} satisfies Record<ConversationKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'shortcut.newline': '改行',
|
||||
'shortcut.complementary': '補助のキュー / 実行中の指示変更を使用',
|
||||
'shortcut.slash': 'コマンドメニューを開く',
|
||||
'shortcut.mention': '参照メニューを開く',
|
||||
'hint.plan': '計画を生成するにはタスクを説明してください',
|
||||
'hint.goal': '長時間実行するタスクの目標を説明してください',
|
||||
'hint.goal.active': '目標アクティブ — 編集 / 一時停止 / 再開 / クリア',
|
||||
'placeholder.plan': '計画を生成するにはタスクを説明してください',
|
||||
'placeholder.default': 'メッセージを送信するかタスクを実行、/ コマンド、@ ファイルやセッション',
|
||||
'placeholder.unavailable': 'セッションを利用できません',
|
||||
'placeholder.parentOffline': '親セッションがオフラインです。送信はできませんが、実行は停止できます',
|
||||
'placeholder.hero': '作りたいものを説明してください。/ コマンド、@ ファイルやセッション',
|
||||
'placeholder.workspace': '開始するワークスペースを選択',
|
||||
'placeholder.steerQueue': 'Cmd/Ctrl+Enterでキュー内のすべてのメッセージを実行中の指示に追加',
|
||||
'input.commands': 'ファイルを追加またはコマンドを実行',
|
||||
'input.file': 'ファイル',
|
||||
'input.stop': '生成を停止',
|
||||
'input.send': 'メッセージを送信',
|
||||
'input.send.queue': 'メッセージをキューに追加',
|
||||
'input.send.steer': '実行中の指示にメッセージを追加',
|
||||
'attachment.pending': '保留中の添付ファイル',
|
||||
'attachment.scrollLeft': '添付ファイルを左にスクロール',
|
||||
'attachment.scrollRight': '添付ファイルを右にスクロール',
|
||||
'attachment.dropTitle': 'ファイルや画像をここにドラッグして追加',
|
||||
'attachment.dropDesc': '画像の上限: 最大{count}枚、各{size}',
|
||||
'attachment.dropBlocked': '現在ファイルや画像を追加できません',
|
||||
'attachment.directoryDesktopOnly': 'フォルダはデスクトップアプリでのみ追加できます。ブラウザでは個別のファイルを追加してください',
|
||||
'attachment.pathUnavailable': 'フォルダのパスを取得できませんでした。もう一度ドラッグしてください',
|
||||
'attachment.pathUnsupported': 'パスに参照できない文字が含まれています。名前を変更して再試行してください',
|
||||
'image.pending': '保留中の画像',
|
||||
'image.openOriginal': '元の画像を表示',
|
||||
'image.openOriginalLabel': '{label}、クリックで元の画像を表示',
|
||||
'image.remove': '画像{name}を削除',
|
||||
'image.original': '元の画像',
|
||||
'image.label': '画像',
|
||||
'image.loadFailed': '画像を読み込めませんでした。クリックで再試行',
|
||||
'image.loading': '画像を読み込み中…',
|
||||
'image.preview': '元の画像のプレビュー',
|
||||
'image.closePreview': '元の画像のプレビューを閉じる',
|
||||
'image.unsupportedType': '対応している画像形式はPNG、JPG、WebP、GIFのみです',
|
||||
'image.tooMany': '1つのメッセージに最大{count}枚の画像を含められます',
|
||||
'image.fileTooLarge': '各画像は{size}未満である必要があります',
|
||||
'image.totalTooLarge': '画像の合計が{size}を超えています。一部を削除して再試行してください',
|
||||
'image.tooManyPixels': '画像の解像度が高すぎます。圧縮して再試行してください',
|
||||
'image.dimensionTooLarge': '画像の各辺は最大{size}pxです。縮小して再試行してください',
|
||||
'image.modelUnsupported': '現在のモデルは画像に対応していません。対応モデルに切り替えてください',
|
||||
'image.sendFailed': '画像の送信に失敗しました({reason})。再度追加して再試行してください',
|
||||
'file.pending': '保留中のファイル',
|
||||
'file.remove': 'ファイル{name}を削除',
|
||||
'file.uploading': 'アップロード中…',
|
||||
'file.uploadFailed': 'アップロードに失敗しました。クリックで再試行',
|
||||
'file.retry': '{name}のアップロードを再試行',
|
||||
'file.stillUploading': 'ファイルをアップロード中です。完了後に送信してください',
|
||||
'file.sessionUnavailable': 'セッションを利用できないため、ファイルをアップロードできません',
|
||||
'file.notStaged': 'ファイルのアップロードが完了していません。再度追加して再試行してください',
|
||||
'file.label': 'ファイル',
|
||||
'context.aria': 'コンテキストの{percent}を使用中',
|
||||
'context.used': 'コンテキスト使用量',
|
||||
'context.system': 'システムプロンプト',
|
||||
'context.tools': 'ツール定義',
|
||||
'context.messages': 'メッセージ',
|
||||
'settings.enter.title': '実行中の送信動作',
|
||||
'settings.enter.description': 'エージェントの実行中にEnterキーと送信ボタンが行う動作。Cmd/Ctrl+Enterではもう一方の動作になります',
|
||||
'settings.enter.queue': 'キュー',
|
||||
'settings.enter.steer': '指示を変更',
|
||||
'hero.headline': '未知への挑戦',
|
||||
'hero.preview': 'プレビュー',
|
||||
'hero.chooseWorkspace': 'ワークスペースを選択',
|
||||
'session.hierarchy': 'セッション階層',
|
||||
'session.moreViews': 'その他の会話ビュー',
|
||||
'todo.title': 'ToDo',
|
||||
'todo.progress.done': '{done}件完了',
|
||||
'todo.progress.active': '{active}件進行中',
|
||||
'todo.progress.pending': '{pending}件保留中',
|
||||
'todo.status.completed': '完了',
|
||||
'todo.status.inProgress': '進行中',
|
||||
'todo.status.pending': '保留中',
|
||||
'todo.rowTitle': 'ToDoリストを更新',
|
||||
'tool.title.createGoal': '目標を作成',
|
||||
'tool.title.getGoal': '目標を表示',
|
||||
'tool.title.updateGoal': '目標を更新',
|
||||
'tool.preparing.content': '{kilobytes}KB',
|
||||
'tool.title.createSchedule': 'リマインダーを作成',
|
||||
'tool.title.listSchedules': 'リマインダーを一覧表示',
|
||||
'tool.title.deleteSchedule': 'リマインダーを削除',
|
||||
'tool.title.updateSchedule': 'リマインダーを更新',
|
||||
'detail.state': 'ステータス',
|
||||
'detail.todo.completed': '完了',
|
||||
'detail.todo.in_progress': '進行中',
|
||||
'detail.todo.pending': '保留中',
|
||||
'detail.todo.empty': 'ToDoリストは空です',
|
||||
'todo.diff.initial': '初期リスト',
|
||||
'todo.diff.compare': '前回のリストからの変更',
|
||||
'todo.diff.unavailable': '前回のリストは利用できません',
|
||||
'todo.diff.noChanges': 'リストに変更はありません',
|
||||
'todo.diff.added': '{count}件追加',
|
||||
'todo.diff.updated': '{count}件更新',
|
||||
'todo.diff.removed': '{count}件削除',
|
||||
'todo.diff.unchanged': '{count}件変更なし',
|
||||
'todo.diff.addedItem': '追加',
|
||||
'todo.diff.updatedItem': 'ステータス変更',
|
||||
'todo.diff.movedItem': '順序変更',
|
||||
'todo.diff.removedItem': '削除',
|
||||
'detail.goal.empty': '目標なし',
|
||||
'detail.goal.active': 'アクティブ',
|
||||
'detail.goal.disarmed': '継続待ち',
|
||||
'detail.goal.paused': '一時停止',
|
||||
'detail.goal.blocked': 'ブロック中',
|
||||
'detail.goal.complete': '完了',
|
||||
'detail.goal.rounds': 'ラウンド',
|
||||
'detail.goal.reason': 'ブロッカー',
|
||||
'detail.days': '{count} 日',
|
||||
'detail.hours': '{count} 時間',
|
||||
'detail.minutes': '{count} 分',
|
||||
'detail.seconds': '{count} 秒',
|
||||
'detail.schedule.once': '1回のみ',
|
||||
'detail.schedule.every': '{interval} ごと',
|
||||
'detail.schedule.when': 'スケジュール設定',
|
||||
'detail.schedule.frequency': '繰り返し',
|
||||
'detail.schedule.scheduled': 'スケジュール済み',
|
||||
'detail.schedule.overdue': '期限超過、セッション再開待ち',
|
||||
'detail.schedule.empty': 'リマインダーなし',
|
||||
'detail.schedule.deleted': '削除済み',
|
||||
'detail.schedule.count': '{count}件のリマインダー',
|
||||
'detail.schedule.daily': '毎日 {time} ({zone})',
|
||||
'detail.schedule.weekly': '毎週 {days} の {time} ({zone})',
|
||||
'detail.schedule.cron': 'Cron {expression} ({zone})',
|
||||
'detail.weekday.1': '月',
|
||||
'detail.weekday.2': '火',
|
||||
'detail.weekday.3': '水',
|
||||
'detail.weekday.4': '木',
|
||||
'detail.weekday.5': '金',
|
||||
'detail.weekday.6': '土',
|
||||
'detail.weekday.7': '日',
|
||||
'detail.weekday.join': ', ',
|
||||
'tool.title.inspectProviders': 'プロバイダーを検査',
|
||||
'tool.title.queryRuntime': 'ランタイムを照会',
|
||||
'tool.title.inspectPlugins': 'プラグインを検査',
|
||||
'tool.title.workflow': 'ワークフローを実行',
|
||||
'tool.title.ralph': 'ralph ループを実行',
|
||||
'tool.title.readEvent': 'イベントを読み取り',
|
||||
'tool.title.searchEvents': 'イベントを検索',
|
||||
'tool.title.traceEvent': 'イベントをトレース',
|
||||
'tool.title.searchSessions': 'セッションを検索',
|
||||
'tool.title.traceSession': 'セッションをトレース',
|
||||
'tool.title.listModels': 'モデルを一覧表示',
|
||||
'tool.title.subagent': 'サブエージェントを作成',
|
||||
'tool.title.listAgents': 'サブエージェントを一覧表示',
|
||||
'tool.title.sendMessage': 'メッセージを送信',
|
||||
'tool.title.interruptAgent': 'エージェントを中断',
|
||||
'tool.title.listJobs': 'バックグラウンドジョブを一覧表示',
|
||||
'tool.title.readJob': 'ジョブ出力を読み取り',
|
||||
'tool.title.killJob': 'バックグラウンドジョブをキャンセル',
|
||||
'tool.title.openTerminal': 'ターミナルを開く',
|
||||
'tool.title.readTerminal': 'ターミナルを読み取り',
|
||||
'tool.title.listTerminals': 'ターミナルを一覧表示',
|
||||
'tool.title.signalTerminal': 'ターミナルにシグナル送信',
|
||||
'tool.title.closeTerminal': 'ターミナルを閉じる',
|
||||
'tool.title.lsp': 'コードシンボルを照会',
|
||||
'tool.title.findDefinition': '定義を検索',
|
||||
'tool.title.findReferences': '参照を検索',
|
||||
'tool.title.findImplementation': '実装を検索',
|
||||
'tool.title.hoverSymbol': 'シンボルを検査',
|
||||
'tool.title.spawnTeammate': 'チームメイトを作成',
|
||||
'tool.title.createTeamTask': 'チームタスクを作成',
|
||||
'tool.title.getTeamTask': 'チームタスクを読み取り',
|
||||
'tool.title.updateTeamTask': 'チームタスクを更新',
|
||||
'tool.title.listTeamTasks': 'チームタスクを一覧表示',
|
||||
'tool.title.waitAgent': 'サブエージェントを待機',
|
||||
'detail.recordedResult': '記録された結果',
|
||||
'detail.empty': '結果なし',
|
||||
'detail.none': 'なし',
|
||||
'detail.yes': 'はい',
|
||||
'detail.no': 'いいえ',
|
||||
'detail.moreInInspect': 'Inspect で残り {count}件を表示できます',
|
||||
'detail.status.running': '実行中',
|
||||
'detail.status.idle': '待機中',
|
||||
'detail.status.ready': '準備完了',
|
||||
'detail.status.inactive': '非アクティブ',
|
||||
'detail.status.provisioning': 'プロビジョニング中',
|
||||
'detail.status.failed': '失敗',
|
||||
'detail.status.completed': '完了',
|
||||
'detail.status.deleted': '削除済み',
|
||||
'detail.status.killed': 'キャンセル済み',
|
||||
'detail.status.accepted': '承認済み',
|
||||
'detail.status.queued': 'キュー待ち',
|
||||
'detail.status.exited': '終了',
|
||||
'detail.field.id': 'ID',
|
||||
'detail.field.revision': 'リビジョン',
|
||||
'detail.field.platform': 'プラットフォーム',
|
||||
'detail.field.provider': 'プロバイダー',
|
||||
'detail.field.model': 'モデル',
|
||||
'detail.field.role': 'ロール',
|
||||
'detail.field.context': 'コンテキスト',
|
||||
'detail.field.owner': 'オーナー',
|
||||
'detail.field.ready': '準備完了',
|
||||
'detail.field.dependencies': '依存関係',
|
||||
'detail.field.writeScopes': '書き込みスコープ',
|
||||
'detail.field.warnings': '警告',
|
||||
'detail.field.diagnostics': '診断',
|
||||
'detail.field.methods': 'メソッド',
|
||||
'detail.field.inputSchema': '入力スキーマ',
|
||||
'detail.field.outputSchema': '出力スキーマ',
|
||||
'detail.field.currentPackage': '現在のパッケージ',
|
||||
'detail.field.nextPackage': '次のパッケージ',
|
||||
'detail.field.latestRun': '最新の実行',
|
||||
'detail.field.packages': 'パッケージ',
|
||||
'detail.field.registrations': '登録',
|
||||
'detail.field.props': 'Props',
|
||||
'detail.field.data': 'データ',
|
||||
'detail.field.source': 'ソース',
|
||||
'detail.field.content': '内容',
|
||||
'detail.field.message': 'メッセージ',
|
||||
'detail.field.messageId': 'メッセージ ID',
|
||||
'detail.field.root': 'ルート',
|
||||
'detail.field.pid': 'プロセス ID',
|
||||
'detail.field.type': '種類',
|
||||
'detail.field.time': '時刻',
|
||||
'detail.field.seq': 'イベントシーケンス',
|
||||
'detail.field.turn': 'ターン',
|
||||
'detail.field.step': 'ステップ',
|
||||
'detail.field.callId': '呼び出し ID',
|
||||
'detail.field.agents': '起動したエージェント',
|
||||
'detail.field.result': '結果',
|
||||
'detail.field.parent': '親',
|
||||
'detail.field.depth': '深さ',
|
||||
'detail.field.exitCode': '終了コード',
|
||||
'detail.field.signal': 'シグナル',
|
||||
'detail.field.previousStatus': '以前のステータス',
|
||||
'detail.field.agent': 'エージェント ID',
|
||||
'detail.field.job': 'ジョブ ID',
|
||||
'detail.field.task': 'タスク',
|
||||
'detail.field.processGroup': 'プロセスグループ',
|
||||
'detail.field.availability': '可用性',
|
||||
'detail.field.bestMatch': '最適な一致',
|
||||
'detail.field.target': '対象イベント',
|
||||
'detail.field.surface': 'レコードステータス',
|
||||
'detail.agents.count': '{count} エージェント',
|
||||
'detail.jobs.count': '{count} バックグラウンドジョブ',
|
||||
'detail.terminals.count': '{count} ターミナル',
|
||||
'detail.tasks.count': '{count} チームタスク',
|
||||
'detail.tasks.nextPage': 'さらにタスクがあります。次のカーソルは {cursor} です',
|
||||
'detail.locations.count': '{count}件の場所',
|
||||
'detail.location': '{line} 行目、{column} 列目',
|
||||
'detail.receipt.delivered': 'メッセージを配信しました',
|
||||
'detail.receipt.interrupt': '中断を要求しました',
|
||||
'detail.receipt.started': '開始済み',
|
||||
'detail.receipt.cancel': 'キャンセルを要求しました',
|
||||
'detail.receipt.alreadyFinished': '既に完了',
|
||||
'detail.receipt.signal': 'シグナル送信済み',
|
||||
'detail.receipt.closed': 'クローズ済み',
|
||||
'detail.receipt.closing': 'クローズ中',
|
||||
'detail.wait.noProgress': 'アクティブなサブエージェントなし',
|
||||
'detail.wait.title': 'サブエージェントのアクティビティ',
|
||||
'detail.wait.timeout': '待機がタイムアウトしました',
|
||||
'detail.wait.changed': '変更を検出',
|
||||
'detail.agent.reply': 'エージェントの応答',
|
||||
'detail.models.title': '利用可能なモデル',
|
||||
'detail.output.lines': '{total} 行中 {begin}–{end} 行',
|
||||
'detail.output.truncated': '出力は切り詰められています',
|
||||
'detail.providers.count': '{count}件の inspect プロバイダー',
|
||||
'detail.plugins.count': '{count}件の動的プラグイン',
|
||||
'detail.workflow.script': 'ワークフロースクリプト',
|
||||
'detail.ralph.reportedComplete': 'ワーカーが完了を報告',
|
||||
'detail.ralph.reportedBlocker': 'ワーカーがブロッカーを報告',
|
||||
'detail.ralph.limit': 'ラウンド上限に達しました',
|
||||
'detail.report.nextSteps': '残りの作業',
|
||||
'detail.trace.replacedBy': '置き換え元',
|
||||
'detail.trace.replacementChain': '置き換えチェーン',
|
||||
'detail.trace.replaces': '置き換えられたイベント',
|
||||
'detail.trace.sources': 'ソースイベント',
|
||||
'detail.trace.derived': '派生イベント',
|
||||
'detail.trace.ancestors': '祖先セッション',
|
||||
'detail.trace.descendants': '子孫セッション',
|
||||
'detail.matches.count': '{count}件の一致',
|
||||
'detail.matches.capped': '結果の上限に達しました。さらに絞り込んでください',
|
||||
'detail.event.neighbors': '周辺のイベント',
|
||||
'todo.completed': '{done}/{total} 完了',
|
||||
'command.attachmentsUnsupported': '/{command} は添付ファイルを受け付けません。先に削除してください',
|
||||
'ask.rowTitle': '質問する',
|
||||
'ask.waiting': '待機中',
|
||||
'ask.pending': '継続中。引き続き回答できます',
|
||||
'ask.pendingDetail': 'これらの保留中の質問は、コンポーザーから引き続き回答できます。',
|
||||
'ask.reopen': '回答',
|
||||
'ask.review': '回答を表示',
|
||||
'ask.closed': 'クローズ済み',
|
||||
'ask.closedDetail': 'この質問はクローズされました。結果は下の会話にあります。',
|
||||
'ask.cancelled': 'キャンセル済み',
|
||||
'ask.cancelledDetail': 'この質問セットは回答が送信される前にキャンセルされました。',
|
||||
'ask.interrupted': '中断済み',
|
||||
'ask.interruptedDetail': 'この質問セットは回答が送信される前に中断されました。',
|
||||
'ask.answered': '{answered}/{total} 回答済み',
|
||||
'ask.skipped': '未回答',
|
||||
'bash.running': '実行中',
|
||||
'bash.failed': '失敗',
|
||||
'bash.stopped': '停止',
|
||||
'row.running': '実行中',
|
||||
'row.preparing': 'ツール呼び出しを準備中',
|
||||
'row.failed': '失敗',
|
||||
'row.stopped': '停止',
|
||||
'row.commandArguments': 'コマンド引数',
|
||||
'row.input': 'IN',
|
||||
'row.output': 'OUT',
|
||||
'row.inspect': '検査',
|
||||
'row.imageSize': '{width}x{height}px、{bytes} バイト',
|
||||
'row.imageSizeDownscaled': '{width}x{height}px({originalWidth}x{originalHeight}px から縮小)、{bytes} バイト',
|
||||
'tool.title.search': '検索',
|
||||
'tool.title.read': '読み取り',
|
||||
'tool.title.bash': 'Bash',
|
||||
'tool.title.write': '書き込み',
|
||||
'tool.title.edit': '編集',
|
||||
'tool.title.code': 'コード',
|
||||
'tool.title.generic': 'ツール呼び出し',
|
||||
'tool.title.inspect': 'Cordis 環境を照会',
|
||||
'tool.title.runCordis': 'Cordis プラグインを実行',
|
||||
'tool.title.stopCordis': 'Cordis プラグインを停止',
|
||||
'tool.title.removeCordis': 'Cordis プラグインを削除',
|
||||
'tool.title.pwsh': 'Pwsh',
|
||||
'tool.title.readImage': '画像を読み取り',
|
||||
'tool.title.grep': 'Grep',
|
||||
'tool.title.glob': 'Glob',
|
||||
'tool.title.webSearch': '検索',
|
||||
'tool.title.webFetch': '取得',
|
||||
'tool.autoReviewRejected': '自動レビューにより拒否されました',
|
||||
'tool.autoReviewNotExecuted': 'ツールは実行されませんでした。理由: {reason}',
|
||||
'tool.autoReviewReasonFallback': '自動レビューがこの操作を承認しませんでした',
|
||||
'diff.collapseAria': '差分を折りたたむ',
|
||||
'diff.expandAria': '差分の残り{count}行を表示',
|
||||
'diff.expandRest': '… 残り{count}行',
|
||||
'read.window': '{total}行中{shown}行を表示',
|
||||
'read.collapseAria': '内容を折りたたむ',
|
||||
'read.expandAria': '残り{count}行を表示',
|
||||
'read.expandRest': '… 残り{count}行',
|
||||
'search.paths': '{shown}件のパス',
|
||||
'search.paths.truncated': '{total}件中{shown}件のパスを表示',
|
||||
'search.matches': '{shown}件の一致 · {files}ファイル',
|
||||
'search.matches.truncated': '{total}件中{shown}件の一致を表示 · {files}ファイル',
|
||||
'search.noResults': '結果なし',
|
||||
'search.collapseAria': '結果を折りたたむ',
|
||||
'search.expandAria': '残り{count}行の結果を表示',
|
||||
'search.expandRest': '… 残り{count}行',
|
||||
'web.noResults': '結果が見つかりません',
|
||||
'web.sourcesTruncated': 'ソース一覧を切り詰めました',
|
||||
'web.http': 'HTTP',
|
||||
'web.contentTruncated': '内容を切り詰めました',
|
||||
'details.running': '実行中…',
|
||||
'queue.count': '{n}件のキュー済みメッセージ',
|
||||
'queue.sending': '送信中…',
|
||||
'queue.image': 'キュー済みメッセージの画像',
|
||||
'queue.file': 'キュー済みファイル {name}',
|
||||
'queue.edit': 'キュー済みメッセージを編集',
|
||||
'queue.edit.unsupported': 'テキスト以外の内容を含むため、まだ編集できません',
|
||||
'queue.save': 'キュー済みメッセージを保存',
|
||||
'queue.cancelEdit': '編集をキャンセル',
|
||||
'queue.remove': 'キュー済みメッセージを削除',
|
||||
'queue.steer': 'キュー内のメッセージを実行中の指示に追加',
|
||||
'queue.steer.unavailable': '指示の変更はエージェントの実行中のみ利用できます',
|
||||
'error.sessionInUse': 'このセッションはすでに使用中です。別の実行中のDSHインスタンス(dsh webやデスクトップアプリなど)が使用している可能性があります。他のDSHインスタンスを終了して再試行してください。',
|
||||
'queue.editFailed': '編集に失敗しました。このメッセージはすでに送信が開始されている可能性があります。',
|
||||
'queue.removeFailed': '削除に失敗しました。このメッセージはすでに送信が開始されている可能性があります。',
|
||||
'queue.steerFailed': '指示の変更に失敗しました。再試行してください。',
|
||||
'terminal.signal': 'シグナル {signal}',
|
||||
'terminal.exitCode': '終了コード {code}',
|
||||
'terminal.noExitCode': '終了コードなし',
|
||||
'terminal.commandLine': 'コマンドライン {n}',
|
||||
'terminal.running': '実行中',
|
||||
'terminal.failed': '失敗',
|
||||
'terminal.done': '完了',
|
||||
'terminal.noOutput': '出力なし',
|
||||
'terminal.collapseAria': '出力を折りたたむ',
|
||||
'terminal.expandAria': '残り{n}行の出力を表示',
|
||||
'terminal.expandRest': '… 残り{n}行',
|
||||
'terminal.sendInput': '(入力を送信)',
|
||||
'terminal.session': 'ターミナル {sessionId}',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -4,7 +4,7 @@ import type {} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-model-selection/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import { DeepSeekModelLabel } from './DeepSeekModelLabel.tsx'
|
||||
import { en, zh, type LabelKey } from './locales.ts'
|
||||
import { en, ja, zh, type LabelKey } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
@@ -22,7 +22,7 @@ export const inject = ['slots', 'locale']
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
const namespace = 'deepseek-model-label-1017'
|
||||
ctx.effect(() => ctx.locale.register(namespace, { zh, en }), 'deepseek-model-label-1017: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(namespace, { zh, en, ja }), 'deepseek-model-label-1017: dictionaries')
|
||||
ctx.slots.inject('conversation.input.model.label', () => ctx.slots.register({
|
||||
name: 'conversation.input.model.label', locale: namespace,
|
||||
}, DeepSeekModelLabel))
|
||||
|
||||
@@ -8,3 +8,8 @@ export type LabelKey = keyof typeof zh
|
||||
|
||||
/** English badge copy. */
|
||||
export const en: Record<LabelKey, string> = { 'badge.label': '1017' }
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'badge.label': '1017',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -27,7 +27,7 @@ import { DeliverablesTail, type DeliverablesInjected } from './Deliverables.tsx'
|
||||
import { ReviewTab, type ReviewInjected } from './ReviewTab.tsx'
|
||||
import { CHANGES_REVIEW_ID, changesReviewDefinition } from './review-definition.ts'
|
||||
import { createReviewStore } from './review-store.ts'
|
||||
import { en, NS, zh, type DeliverablesKey } from './locales.ts'
|
||||
import { en, ja, NS, zh, type DeliverablesKey } from './locales.ts'
|
||||
import {
|
||||
deliverablesDefinition, presentedForClosing, producedFileMentions, selectProducedFiles,
|
||||
} from './turn-deliverables.ts'
|
||||
@@ -57,7 +57,7 @@ export function apply(ctx: ClientContext): void {
|
||||
diffs.reset()
|
||||
})
|
||||
ctx.uiConversation.events.register(deliverablesDefinition)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-deliverables: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-deliverables: dictionaries')
|
||||
ctx.slots.inject(
|
||||
'conversation.chat.turnTail',
|
||||
() => ctx.slots.register({
|
||||
|
||||
@@ -133,3 +133,67 @@ export const en: Record<DeliverablesKey, string> = {
|
||||
|
||||
/** Union of this namespace's dictionary keys. */
|
||||
export type DeliverablesKey = keyof typeof zh
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'presented.nativeUnavailable': 'このファイルには利用可能なHostパスがありません。サイドバーでプレビューしてください。',
|
||||
'presented.revealError': 'ファイルマネージャーで表示できませんでした。再試行してください。',
|
||||
'presented.directoryError': '含まれるフォルダーを開けませんでした。再試行してください。',
|
||||
'presented.directoryOpening': '含まれるフォルダーを開いています…',
|
||||
'presented.directoryOpened': '含まれるフォルダーの表示をリクエストしました',
|
||||
'presented.revealed': 'ファイルマネージャーでの表示をリクエストしました',
|
||||
'presented.revealing': 'ファイルマネージャーで表示しています…',
|
||||
'presented.unavailable': 'このHostには、外部アプリでファイルやフォルダーを開くためのデスクトップがありません。ファイルはサイドバーで引き続きプレビューできます。',
|
||||
'presented.retry': '再試行',
|
||||
'presented.hostError': 'Hostのデスクトップ情報を読み取れませんでした',
|
||||
'presented.preview': 'サイドバーでプレビュー',
|
||||
'presented.previewButton': '{name}をサイドバーで開く',
|
||||
'presented.previewCard': '{name}をサイドバーでプレビュー',
|
||||
'presented.all': '全{count}ファイル',
|
||||
'presented.expandAria': '配信された全{count}ファイルを表示',
|
||||
'presented.collapse': '折りたたむ',
|
||||
'presented.collapseAria': '配信されたファイルを折りたたむ',
|
||||
'presented.opening': '開いています…',
|
||||
'presented.opened': '表示をリクエストしました',
|
||||
'presented.error': '開けませんでした。クリックして再試行してください。',
|
||||
'presented.file': 'ファイル',
|
||||
'row.title': 'ファイルを提示',
|
||||
'row.running': '配信中',
|
||||
'row.preparing': '成果物を準備中',
|
||||
'row.ok': '配信済み',
|
||||
'row.error': '配信に失敗しました',
|
||||
'row.stopped': '中断されました',
|
||||
'row.inspect': '呼び出しを検査',
|
||||
'changes.title': '{count}ファイルを編集しました',
|
||||
'changes.singleTitle': '{name}を編集しました',
|
||||
'changes.added': '+{count}',
|
||||
'changes.deleted': '-{count}',
|
||||
'changes.binary': 'バイナリ',
|
||||
'changes.openReview': 'このターンの変更をサイドバーで確認',
|
||||
'changes.all': '全{count}ファイル',
|
||||
'changes.expandAria': '変更された全{count}ファイルを表示',
|
||||
'changes.collapse': '折りたたむ',
|
||||
'changes.collapseAria': '変更されたファイルを折りたたむ',
|
||||
'changes.oversized': 'サイズが大きすぎます',
|
||||
'changes.viewDiff': '{name}の変更を表示',
|
||||
'review.title': 'レビュー · ターン{turn}',
|
||||
'review.selectFile': 'レビューするファイルを選択',
|
||||
'review.split': '分割ビューに切り替え',
|
||||
'review.unified': '統合ビューに切り替え',
|
||||
'review.splitAria': '分割ビュー',
|
||||
'review.wrap': '行の折り返しを有効化',
|
||||
'review.nowrap': '行の折り返しを無効化',
|
||||
'review.wrapAria': '行の折り返し',
|
||||
'review.openFile': 'ファイル全体をサイドバーで開く',
|
||||
'review.openFileAria': '{name}をサイドバーで開く',
|
||||
'diff.loading': '変更を読み込んでいます…',
|
||||
'diff.missing': 'このターンの変更内容は利用できなくなりました',
|
||||
'diff.error': '変更を読み取れませんでした',
|
||||
'diff.binary': 'バイナリファイルのため、変更を表示できません',
|
||||
'diff.oversized': 'ファイルが大きすぎるため、変更を表示できません',
|
||||
'diff.created': 'このターンで作成',
|
||||
'diff.deleted': 'このターンで削除',
|
||||
'diff.unchanged': '両方の行内容が同一です',
|
||||
'diff.coarse': '行比較がタイムアウトしたため、ファイル全体の置換として表示しています',
|
||||
'diff.truncated': '最初の{count}行を表示',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -30,7 +30,7 @@ export const inject = ['slots', 'uiWorkspace', 'locale']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => {
|
||||
// The two dictionaries land as a unit: if the second registration hits a
|
||||
// The dictionaries land as a unit: if the second registration hits a
|
||||
// rival owner of the namespace, the first rolls back before the throw —
|
||||
// a failed activation must not squat the namespace's other locale.
|
||||
const disposers: (() => void)[] = []
|
||||
@@ -65,6 +65,21 @@ export function apply(ctx: ClientContext): void {
|
||||
'browser.truncated': 'Too many folders to list; only the beginning is shown.',
|
||||
'browser.showHidden': 'Show hidden files',
|
||||
}],
|
||||
['ja', {
|
||||
'browser.title': 'ワークスペースディレクトリを選択',
|
||||
'browser.home': 'ホーム',
|
||||
'browser.newFolder': '新しいフォルダ',
|
||||
'browser.folderName': 'フォルダ名',
|
||||
'browser.createIn': '「{name}」内に新しいフォルダ',
|
||||
'browser.untitledFolder': '名称未設定フォルダ',
|
||||
'browser.create': '作成',
|
||||
'browser.cancel': 'キャンセル',
|
||||
'browser.open': '開く',
|
||||
'browser.editPath': 'パスを編集',
|
||||
'browser.loading': '読み込み中…',
|
||||
'browser.truncated': 'フォルダが多すぎて一覧表示できません。先頭のみ表示しています。',
|
||||
'browser.showHidden': '隠しファイルを表示',
|
||||
}],
|
||||
]
|
||||
try {
|
||||
for (const [locale, dict] of dictionaries) disposers.push(ctx.locale.register(LOCALE_NS, locale, dict))
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-plugin-manager/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-workspace/client'
|
||||
import { EntryMenuContent, type EntryMenuInjected, type EntryRegion } from './EntryMenuContent.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
@@ -26,7 +26,7 @@ export const inject = ['slots', 'locale', 'pluginNavigation', 'uiWorkspace']
|
||||
* @param ctx - Browser context with slot, locale, plugin navigation, and Workspace services.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
ctx.effect(() => ctx.locale.register('entryCustomization', { en, zh }))
|
||||
ctx.effect(() => ctx.locale.register('entryCustomization', { en, zh, ja }))
|
||||
const t = ctx.locale.bind('entryCustomization')
|
||||
const injected = (region: EntryRegion): EntryMenuInjected => ({
|
||||
region,
|
||||
|
||||
@@ -29,3 +29,19 @@ export const zh: Record<keyof typeof en, string> = {
|
||||
'createPrompt.conversation': '创建一个 DSH 会话视图插件。',
|
||||
'createPrompt.rightbar': '创建一个带右侧栏入口的 DSH 插件。',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'pin.sidebar': '左サイドバーに固定',
|
||||
'unpin.sidebar': '左サイドバーから固定解除',
|
||||
'pin.conversation': '会話タブに固定',
|
||||
'unpin.conversation': '会話タブから固定解除',
|
||||
'pin.rightbar': '右サイドバーに固定',
|
||||
'unpin.rightbar': '右サイドバーから固定解除',
|
||||
'pinLabel': '{action}: {name}',
|
||||
'install': '他のプラグインを探す',
|
||||
'create': 'プラグインを作成',
|
||||
'createPrompt.sidebar': '左サイドバーにエントリを持つ DSH プラグインを作成します。',
|
||||
'createPrompt.conversation': 'DSH 会話ビュープラグインを作成します。',
|
||||
'createPrompt.rightbar': '右サイドバーにエントリを持つ DSH プラグインを作成します。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -35,7 +35,7 @@ import { GoalCommandReference } from './GoalCommandReference.tsx'
|
||||
import { goalCommandInputDefinition } from './goal-command-input.ts'
|
||||
import { goalCommandReferenceRule } from './user-text-rules.ts'
|
||||
export type {} from './user-text-rules.ts'
|
||||
import { en, zh, type GoalKey } from './locales.ts'
|
||||
import { en, ja, zh, type GoalKey } from './locales.ts'
|
||||
|
||||
export { GoalBar, GoalDock } from './GoalBar.tsx'
|
||||
export type {
|
||||
@@ -70,7 +70,7 @@ export const inject = ['slots', 'sessions', 'remote', 'remote.goals', 'locale',
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.uiConversation.events.register(goalCommandInputDefinition)
|
||||
ctx.uiConversation.userText.register(goalCommandReferenceRule)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-goal: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-goal: dictionaries')
|
||||
|
||||
ctx.slots.inject('conversation.user.text.node', () => ctx.slots.register({
|
||||
name: 'conversation.user.text.node',
|
||||
|
||||
@@ -34,3 +34,19 @@ export const en = {
|
||||
'action.edit': 'Edit goal',
|
||||
'action.clear': 'Clear goal',
|
||||
} satisfies Record<GoalKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'phase.active': '進行中の目標',
|
||||
'phase.active.disarmed': '非アクティブな目標',
|
||||
'phase.paused': '一時停止中の目標',
|
||||
'phase.blocked': 'ブロックされた目標',
|
||||
'objective.aria': '目標の目的',
|
||||
'commandInput.aria': 'コマンド入力',
|
||||
'action.save': '目標を保存',
|
||||
'action.cancel': '編集をキャンセル',
|
||||
'action.pause': '目標を一時停止',
|
||||
'action.resume': '目標を再開',
|
||||
'action.edit': '目標を編集',
|
||||
'action.clear': '目標をクリア',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -13,7 +13,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-session/client'
|
||||
import { InputTriggerService } from './service.ts'
|
||||
import type { MenuViewInjected } from './slots.ts'
|
||||
import { MenuView } from './MenuView.tsx'
|
||||
import { en, zh, type MenuKey } from './locales.ts'
|
||||
import { en, ja, zh, type MenuKey } from './locales.ts'
|
||||
|
||||
export { InputTriggerService } from './service.ts'
|
||||
export { InputTriggerController } from './controller.ts'
|
||||
@@ -58,7 +58,7 @@ export const inject = ['sessions', 'locale']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.plugin(InputTriggerService)
|
||||
ctx.effect(() => ctx.locale.register(MENU_NS, { zh, en }), 'ui-input-trigger: menu dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(MENU_NS, { zh, en, ja }), 'ui-input-trigger: menu dictionaries')
|
||||
ctx.inject(['slots', 'inputTriggers', 'sessions'], (scope: ClientContext) => {
|
||||
const inputTriggers = scope.inputTriggers
|
||||
const sessions = scope.sessions
|
||||
|
||||
@@ -32,3 +32,16 @@ export const en = {
|
||||
'crumbs.aria': 'Folder navigation',
|
||||
'suggestions.aria': 'Trigger suggestions',
|
||||
} satisfies Record<MenuKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'command': 'コマンド',
|
||||
'skill': 'スキル',
|
||||
'subagent': 'サブエージェント',
|
||||
'loading': '読み込み中…',
|
||||
'drill.aria': 'フォルダを参照',
|
||||
'drill.hint': 'フォルダを参照',
|
||||
'drill.key': 'Tab',
|
||||
'crumbs.aria': 'フォルダナビゲーション',
|
||||
'suggestions.aria': '候補を表示',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -12,7 +12,7 @@ import type {} from '@deepseek-ai/dsh-api-job-controller/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-session/client'
|
||||
import { en, NS, zh, type JobKey } from './locales.ts'
|
||||
import { en, ja, NS, zh, type JobKey } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
@@ -31,7 +31,7 @@ export const inject = ['jobs', 'slots', 'locale']
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-jobs: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-jobs: dictionaries')
|
||||
ctx.slots.inject(
|
||||
'conversation.session.header.actions',
|
||||
() => ctx.slots.register({
|
||||
|
||||
@@ -93,3 +93,47 @@ export const en: Record<JobKey, string> = {
|
||||
|
||||
/** Key domain of the `job` namespace (zh is the source of truth). */
|
||||
export type JobKey = keyof typeof zh
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'count.live.one': 'バックグラウンドジョブ {count} 件が実行中',
|
||||
'count.live.other': 'バックグラウンドジョブ {count} 件が実行中',
|
||||
'count.idle.one': 'バックグラウンドジョブ {count} 件',
|
||||
'count.idle.other': 'バックグラウンドジョブ {count} 件',
|
||||
'list.aria': 'バックグラウンドジョブ',
|
||||
'section.live': '実行中',
|
||||
'section.settledCount': '完了 {count} 件',
|
||||
'section.clear': 'クリア',
|
||||
'row.expandAria': '{label} のライブ出力を表示',
|
||||
'row.collapseAria': '{label} のライブ出力を非表示',
|
||||
'kill.stop': 'タスク {label} を停止',
|
||||
'kill.confirm': 'もう一度クリックして確定',
|
||||
'kill.confirmAction': '停止を確定',
|
||||
'kill.failed': '停止に失敗しました',
|
||||
'status.running': '実行中',
|
||||
'status.stopping': '停止中',
|
||||
'status.completed': '完了',
|
||||
'status.killed': 'キャンセル済み',
|
||||
'status.failed': '失敗',
|
||||
'duration.seconds': '{seconds}秒',
|
||||
'duration.minutes': '{minutes}分 {seconds}秒',
|
||||
'duration.hours': '{hours}時間 {minutes}分',
|
||||
'duration.title.live': '実行時間 {duration}',
|
||||
'duration.title.done': '所要時間 {duration}',
|
||||
'output.gap': '…以前の出力は破棄されました…',
|
||||
'output.error': 'ライブ出力ストリームが中断されました: {error}',
|
||||
'terminal.signal': 'シグナル {signal}',
|
||||
'terminal.exitCode': '終了コード {code}',
|
||||
'terminal.noExitCode': '終了コードなし',
|
||||
'terminal.commandLine': 'コマンドライン {n}',
|
||||
'terminal.running': '実行中',
|
||||
'terminal.failed': '失敗',
|
||||
'terminal.done': '完了',
|
||||
'terminal.copy': 'コピー',
|
||||
'terminal.copied': 'コピーしました',
|
||||
'terminal.noOutput': '(出力なし)',
|
||||
'terminal.collapse': '折りたたむ',
|
||||
'terminal.collapseAria': '出力を折りたたむ',
|
||||
'terminal.expand': '残り {n} 行を表示',
|
||||
'terminal.expandAria': '折りたたまれた出力 {n} 行を展開',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -18,7 +18,7 @@ import { AppFrame } from './AppFrame.tsx'
|
||||
import { createLayoutStore } from './stores.ts'
|
||||
import { LayoutController } from './service.ts'
|
||||
import type { ShortcutCommandId } from '@deepseek-ai/dsh-client-shortcuts/client'
|
||||
import { en, zh } from './shortcut-locales.ts'
|
||||
import { en, ja, zh } from './shortcut-locales.ts'
|
||||
import { ThemePresenter } from './theme-presenter.ts'
|
||||
|
||||
// Contract exports only (export-convergence rule: cross-package consumers
|
||||
@@ -156,7 +156,7 @@ export const inject = ['slots', 'theme', 'locale', 'shortcuts']
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register('shortcuts.layout', { zh, en }), 'layout: command labels')
|
||||
ctx.effect(() => ctx.locale.register('shortcuts.layout', { zh, en, ja }), 'layout: command labels')
|
||||
const t = ctx.locale.bind('shortcuts.layout')
|
||||
|
||||
ctx.effect(() => {
|
||||
|
||||
@@ -2,3 +2,8 @@
|
||||
export const zh = { toggle: '展开/收起左侧栏' }
|
||||
/** English labels for the same layout commands. */
|
||||
export const en: Record<keyof typeof zh, string> = { toggle: 'Toggle left sidebar' }
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'toggle': '左サイドバーを切り替え',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -26,7 +26,7 @@ import { FeedbackDialog } from './FeedbackDialog.tsx'
|
||||
import { MessageFeedbackActions } from './MessageFeedbackActions.tsx'
|
||||
import type { FeedbackDialogInjected, MessageFeedbackInjected } from './slots.ts'
|
||||
import { FeedbackSurface } from './surface.ts'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
|
||||
export type {
|
||||
MessageFeedbackActionFailure, MessageFeedbackActionResult, MessageFeedbackStatus,
|
||||
@@ -66,7 +66,7 @@ export const inject = ['slots', 'remote', 'remote.messageFeedback', 'remote.sess
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-message-feedback: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-message-feedback: dictionaries')
|
||||
|
||||
const surfaces = new Map<SessionId, FeedbackSurface>()
|
||||
const surfaceFor = (sessionId: SessionId): FeedbackSurface => {
|
||||
|
||||
@@ -57,3 +57,27 @@ export const en = {
|
||||
'error.generic': 'Could not save feedback',
|
||||
'error.noteTooLarge': 'The description is too long; shorten it and submit again',
|
||||
} satisfies Record<MessageFeedbackKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'action.like': '良い回答',
|
||||
'action.likeActive': '評価を取り消す',
|
||||
'action.dislike': '悪い回答',
|
||||
'action.dislikeActive': '評価を取り消す',
|
||||
'dialog.title': 'フィードバックを送信',
|
||||
'dialog.categories': 'フィードバックのカテゴリ',
|
||||
'dialog.detail': 'フィードバックの詳細',
|
||||
'dialog.hint': '改善のため詳細を追加してください。現在の会話ログが送信内容に含まれます。',
|
||||
'category.task-result': 'タスク結果',
|
||||
'category.instruction-following': '指示の理解と遵守',
|
||||
'category.product-interaction': '製品機能と操作',
|
||||
'category.service-stability': '安定性と速度',
|
||||
'category.resource-cost': 'リソース使用量とコスト',
|
||||
'category.security-privacy-permission': 'セキュリティ、プライバシー、権限',
|
||||
'category.other': 'その他',
|
||||
'toast.recorded': 'フィードバックありがとうございます',
|
||||
'error.conflict': 'このフィードバックは別の場所で変更されました。最新の状態を表示しています',
|
||||
'error.load': 'フィードバックを読み込めませんでした',
|
||||
'error.generic': 'フィードバックを保存できませんでした',
|
||||
'error.noteTooLarge': '説明が長すぎます。短くして再送信してください',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -27,7 +27,7 @@ import type { ModelDirectoryState } from './directory.ts'
|
||||
import { ModelDirectoryResolver } from './service.ts'
|
||||
import type { ModelSelectInjected } from './slots.ts'
|
||||
import { ModelSelect } from './ModelSelect.tsx'
|
||||
import { en, zh, type ModelKey } from './locales.ts'
|
||||
import { en, ja, zh, type ModelKey } from './locales.ts'
|
||||
import { orderModelProviders } from './provider-order.ts'
|
||||
|
||||
export { ModelDirectory } from './directory.ts'
|
||||
@@ -113,7 +113,7 @@ export const inject = ['commandUi', 'locale', 'sessions', 'slots', 'remote', 're
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-model-selection: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-model-selection: dictionaries')
|
||||
|
||||
// Non-slot faces (the command description, the popup option builder) read
|
||||
// through the bound translate; the seat component reads the standard seat.
|
||||
|
||||
@@ -64,3 +64,30 @@ export const en = {
|
||||
'empty.models': 'No models available.',
|
||||
'empty.efforts': 'This model provides no reasoning effort levels.',
|
||||
} satisfies Record<ModelKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'provider.account': 'DeepSeek アカウント',
|
||||
'command.label': 'モデル',
|
||||
'command.description': 'この会話で使用するモデルを選択',
|
||||
'option.loadError': 'カタログの読み込みに失敗しました: {message}',
|
||||
'trigger.fallback': 'モデルを選択',
|
||||
'trigger.loading': 'モデルを読み込み中…',
|
||||
'trigger.selectAria': 'モデルを選択',
|
||||
'trigger.aria': 'モデルを選択、現在 {model}',
|
||||
'trigger.ariaEffort': 'モデルを選択、現在 {model}、推論強度 {effort}',
|
||||
'menu.aria': 'モデルと推論強度',
|
||||
'menu.model': 'モデル',
|
||||
'menu.effort': '強度',
|
||||
'effort.providerDefault': 'デフォルト',
|
||||
'status.loading': 'モデル一覧を更新中…',
|
||||
'error.action': 'モデル操作に失敗しました: {message}',
|
||||
'error.sessionInUse': 'このセッションは既に使用中です。別の DSH インスタンス(dsh web やデスクトップアプリなど)が実行中の可能性があります。他の DSH インスタンスを終了してから再試行してください。',
|
||||
'action.reload': '再読み込み',
|
||||
'warning.groupLoad': '{name} の読み込みに失敗しました: {message}',
|
||||
'search.placeholder': 'モデルを検索…',
|
||||
'search.clear': '検索をクリア',
|
||||
'search.empty': '一致するモデルがありません。',
|
||||
'empty.models': '利用可能なモデルがありません。',
|
||||
'empty.efforts': 'このモデルには推論強度の設定がありません。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -25,7 +25,7 @@ import { OpenInAppPathController } from './open-path.ts'
|
||||
import { OpenPathAction, type OpenPathInjected } from './OpenPathAction.tsx'
|
||||
import { FileRouteAction } from './FileRouteAction.tsx'
|
||||
import { OpenPathEmptyAction } from './OpenPathEmptyAction.tsx'
|
||||
import { en, NS, zh, type OpenInAppKey } from './locales.ts'
|
||||
import { en, ja, NS, zh, type OpenInAppKey } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
@@ -51,7 +51,7 @@ export function apply(ctx: ClientContext): void {
|
||||
const controller = new OpenInAppController()
|
||||
void controller.load()
|
||||
const paths = new OpenInAppPathController(ctx.remote)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'open-in-app: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'open-in-app: dictionaries')
|
||||
const t = ctx.locale.bind(NS)
|
||||
const target = () => {
|
||||
if (ctx.layout.panelInfo.getSnapshot().activePanelId !== null) return undefined
|
||||
|
||||
@@ -79,3 +79,23 @@ export const en: Record<OpenInAppKey, string> = {
|
||||
|
||||
/** Key domain of the `open-in-app` namespace (zh is the source of truth). */
|
||||
export type OpenInAppKey = keyof typeof zh
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
...PRODUCT_NAMES,
|
||||
'open.title': '{app}で開く',
|
||||
'path.appDefault': '{app}(デフォルト)',
|
||||
'path.appsError': 'アプリケーションを読み込めませんでした',
|
||||
'shortcut.busy': 'ワークスペースを開いています',
|
||||
'shortcut.unavailable': '現在のワークスペースまたはローカルアプリケーションを利用できません',
|
||||
'open.tooltip': 'ローカルで開く',
|
||||
'path.open': '開く',
|
||||
'path.more': 'その他の開き方',
|
||||
'path.reveal': 'ファイルの場所を表示',
|
||||
'path.openError': '開けませんでした。もう一度お試しください。',
|
||||
'path.revealError': 'ファイルの場所を表示できませんでした。もう一度お試しください。',
|
||||
'app.finder': 'Finder',
|
||||
'app.explorer': 'エクスプローラー',
|
||||
'app.filemanager': 'ファイル',
|
||||
'app.terminal': 'ターミナル',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -41,7 +41,7 @@ import type { PermissionSelectInjected } from './PermissionSelect.tsx'
|
||||
import { PermissionRow } from './PermissionRow.tsx'
|
||||
import type { PermissionRowInjected } from './PermissionRow.tsx'
|
||||
import {
|
||||
accessEn, accessZh, en, PERMISSION_ACCESS_NS, zh,
|
||||
accessEn, accessJa, accessZh, en, ja, PERMISSION_ACCESS_NS, zh,
|
||||
} from './locales.ts'
|
||||
import {
|
||||
AUTO_REVIEW_PRESET, displayPermissionPreset, FULL_ACCESS_PRESET,
|
||||
@@ -113,8 +113,9 @@ function optionsOf(
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const command = ctx.get('commandUi') as CommandUiContract
|
||||
const sessions = ctx.sessions
|
||||
const accessDictionaries = { zh: accessZh, en: accessEn, ja: accessJa }
|
||||
ctx.effect(
|
||||
() => ctx.locale.register(PERMISSION_ACCESS_NS, { zh: accessZh, en: accessEn }),
|
||||
() => ctx.locale.register(PERMISSION_ACCESS_NS, accessDictionaries),
|
||||
'ui-permission: current-session dictionaries',
|
||||
)
|
||||
const t = ctx.locale.bind(PERMISSION_ACCESS_NS)
|
||||
@@ -141,7 +142,7 @@ export function apply(ctx: ClientContext): void {
|
||||
'ui-permission: dismiss stale slash choices',
|
||||
)
|
||||
|
||||
ctx.effect(() => ctx.locale.register('settings.permission', { zh, en }), 'ui-permission: settings row dictionaries')
|
||||
ctx.effect(() => ctx.locale.register('settings.permission', { zh, en, ja }), 'ui-permission: settings row dictionaries')
|
||||
|
||||
// The shared ConfigForm mirror updates after document commits and reconnects.
|
||||
const controller = new PermissionPresetSettingsController(
|
||||
|
||||
@@ -82,3 +82,40 @@ export const accessEn = {
|
||||
'auto.confirm.acknowledge': 'I understand these risks and want to continue',
|
||||
'auto.confirm.enable': 'Enable Auto review',
|
||||
} satisfies Record<PermissionAccessKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': '権限',
|
||||
'description': '新しいセッションのデフォルトの権限モードを選択',
|
||||
'loading': '読み込み中',
|
||||
'unavailable': '利用不可',
|
||||
'preset.readOnly': '読み取り専用',
|
||||
'preset.workspaceWrite': 'ワークスペース書き込み',
|
||||
'preset.fullAccess': 'フルアクセス',
|
||||
'confirm.title': 'フルアクセスを有効にしますか?',
|
||||
'confirm.description': 'フルアクセスでは、新しいセッションで確認ステップが減り、機密操作・ファイル変更・外部コマンドなど多くの操作を直接実行できるようになります。以降のタスクを信頼できる場合にのみ使用してください。',
|
||||
'confirm.acknowledge': 'リスクを理解し、続行します',
|
||||
'confirm.cancel': 'キャンセル',
|
||||
'confirm.enable': 'フルアクセスを有効にする',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const accessJa = {
|
||||
'mode': 'アクセスモード、現在: {name}',
|
||||
'close': '閉じる',
|
||||
'preset.readOnly': '読み取り専用',
|
||||
'preset.workspaceWrite': 'ワークスペース書き込み',
|
||||
'preset.fullAccess': 'フルアクセス',
|
||||
'confirm.title': 'フルアクセスを有効にしますか?',
|
||||
'confirm.description': 'フルアクセスでは確認ステップが減り、エージェントが機密操作・ファイル変更・外部コマンドなど多くの操作を直接実行できるようになります。現在のタスクを信頼できる場合にのみ使用してください。',
|
||||
'confirm.acknowledge': 'リスクを理解し、続行します',
|
||||
'confirm.cancel': 'キャンセル',
|
||||
'confirm.enable': 'フルアクセスを有効にする',
|
||||
'auto.label': '自動レビュー',
|
||||
'auto.badge': 'EXP',
|
||||
'auto.description': 'すべてのネイティブツール呼び出しと PTC 内部呼び出しを実験的な同一モデルでレビューしたうえで、サンドボックスなしで実行します。',
|
||||
'auto.confirm.title': '自動レビュー(実験的)を有効にしますか?',
|
||||
'auto.confirm.description': '自動レビューはサンドボックスなしで実行されます。ネイティブツール呼び出しと PTC 内部呼び出しのたびに、現在のエージェントと同じモデルが許可可否をレビューし、拒否された呼び出しはあなたが承認または却下します。この機能は実験的であり、操作を誤って許可・拒否する可能性があり、追加のトークンを消費します。',
|
||||
'auto.confirm.acknowledge': 'これらのリスクを理解し、続行します',
|
||||
'auto.confirm.enable': '自動レビューを有効にする',
|
||||
} satisfies Record<keyof typeof accessEn, string>
|
||||
@@ -25,7 +25,7 @@ import { planAddress, parsePlanAddress } from './plan.ts'
|
||||
import { isReviewPreviewAddress, reviewPreviewAddress } from './review-preview.ts'
|
||||
import { createPlanReviewStore } from './review-store.ts'
|
||||
import { PlanChip } from './PlanModeControl.tsx'
|
||||
import { en, zh, type PlanKey } from './locales.ts'
|
||||
import { en, ja, zh, type PlanKey } from './locales.ts'
|
||||
|
||||
export type { PlanKey } from './locales.ts'
|
||||
|
||||
@@ -56,7 +56,7 @@ export const inject = ['slots', 'remote', 'remote.commands', 'remote.session', '
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plan: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-plan: dictionaries')
|
||||
|
||||
const previewId = '@deepseek-ai/dsh-client-ui-plan'
|
||||
const t = ctx.locale.bind(NS)
|
||||
|
||||
@@ -44,3 +44,24 @@ export const en = {
|
||||
'chip.on.title': 'Plan mode on — click to turn off (/plan off)',
|
||||
'chip.exitFailed': 'Failed to exit plan mode',
|
||||
} satisfies Record<PlanKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'chip.label': '計画',
|
||||
'preview.title': '計画',
|
||||
'preview.document': '計画 · Markdown',
|
||||
'preview.action': '開く',
|
||||
'preview.open': 'サイドバーで計画を開く',
|
||||
'preview.full': '計画全体を表示',
|
||||
'preview.openNamed': '計画を開く: {title}',
|
||||
'preview.loading': '計画を読み込み中…',
|
||||
'preview.failed': '計画を読み込めませんでした',
|
||||
'preview.invalidAddress': '計画のアドレスが無効です',
|
||||
'preview.historyUnavailable': 'セッション履歴を利用できません',
|
||||
'preview.notFound': 'この計画は見つかりませんでした',
|
||||
'preview.unavailable': '計画のプレビューを利用できません',
|
||||
'preview.expired': 'この一時的な計画プレビューは期限切れです。保留中のレビューカードから再度開いてください。',
|
||||
'chip.on.aria': '計画モードはオンです。押すとオフになります',
|
||||
'chip.on.title': '計画モードはオンです — クリックでオフ (/plan off)',
|
||||
'chip.exitFailed': '計画モードを終了できませんでした',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -27,7 +27,7 @@ import { PluginsPanelIcon } from './PluginsPanelIcon.tsx'
|
||||
import { configLedgerSource } from './config-ledger.ts'
|
||||
import { tabLedgerSource } from './tab-ledger.ts'
|
||||
import { PluginManagerController } from './manager-store.ts'
|
||||
import { en, zh, type PluginManagerLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type PluginManagerLocaleKey } from './locales.ts'
|
||||
import { createNavigationStore } from './navigation-store.ts'
|
||||
import type {} from './slot-contract.ts'
|
||||
|
||||
@@ -88,7 +88,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.pluginManager', 'rem
|
||||
* @param ctx - the browser plugin context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plugin-manager: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-plugin-manager: dictionaries')
|
||||
const t = ctx.locale.bind(NS)
|
||||
const controller = new PluginManagerController(ctx)
|
||||
ctx.effect(() => () => { controller.dispose() }, 'ui-plugin-manager: controller')
|
||||
|
||||
@@ -589,3 +589,294 @@ export const en = {
|
||||
detailFailed: 'Could not read the details.',
|
||||
detailFootnotes: 'Footnotes',
|
||||
} satisfies Record<PluginManagerLocaleKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'panel': 'プラグイン',
|
||||
'title': 'プラグイン',
|
||||
'intro': 'プラグインのインストール、有効化、設定',
|
||||
'infoLabel': 'プラグインについて',
|
||||
'infoDescription': '公式プラグインの設定や、その他のプラグインのインストール・管理はここで行えます。組み込みプラグインの一覧と実行状態は「設定 → 組み込みプラグイン」で確認できます。',
|
||||
'loading': 'プラグインを読み込み中…',
|
||||
'error': '一部のプラグインを読み込めませんでした。ネットワークの問題が考えられます',
|
||||
'unavailable': 'このデプロイでは管理可能なプロファイルが実行されていないため、ここでプラグインのインストールや切り替えはできません。',
|
||||
'retry': '再試行',
|
||||
'refresh': '更新',
|
||||
'refreshError': '更新に失敗しました。もう一度お試しください。',
|
||||
'empty': 'プラグインはまだインストールされていません。',
|
||||
'addPlugin': 'プラグインを追加',
|
||||
'chooseAddMethod': 'プラグインの追加方法を選択',
|
||||
'installExisting': 'サードパーティのプラグインをインストール',
|
||||
'installExistingDescription': 'npm パッケージ、Git リポジトリ、ローカルディレクトリに対応',
|
||||
'restartNotice': '変更を適用するには DSH を再起動してください。{reason}',
|
||||
'restartReasonNoHmr': 'このプロファイルでは起動時にのみ変更が適用されます。',
|
||||
'restartReasonPackageMoved': 'インストール済みパッケージの解決先が別のディレクトリになったか、変更によって DSH インストールが提供するパッケージが置き換えられました。別の場所からインストールする前に、そのパッケージをアンインストールしてください。',
|
||||
'restartReasonHostModule': '変更はDSH自身が読み込んだモジュールにも及びます。',
|
||||
'restartReasonUnmanagedPlugin': '変更はプラグイン一覧外で動作しているプラグインにも及びます。',
|
||||
'restartReasonModuleComparison': 'DSHは読み込まれたプラグインとそのファイルを比較できませんでした。',
|
||||
'overriddenNotice': '{name}は保存されましたが、優先度の高い設定が上書きしているため有効になっていません',
|
||||
'bundlesTitle': 'インストール済みプラグイン',
|
||||
'basicTitle': '組み込みプラグイン設定',
|
||||
'extensionsTitle': '実験的プラグイン',
|
||||
'moreTitle': '実験的プラグイン',
|
||||
'moreAction': 'その他',
|
||||
'tabsLabel': 'プラグインビュー',
|
||||
'installedTab': 'インストール済み',
|
||||
'statusProblem': '問題',
|
||||
'statusBeta': '実験的',
|
||||
'reasonLabel': '理由',
|
||||
'metadataError': 'パッケージメタデータのエラー: {error}',
|
||||
'versionTag': 'v{version}',
|
||||
'partsLabel': 'コンポーネント',
|
||||
'sourceTitle': 'ソース',
|
||||
'sourceSpec': 'コードソース',
|
||||
'sourceBuiltIn': '組み込み',
|
||||
'sourceVersion': '現在のバージョン',
|
||||
'partsEmpty': 'このプラグインパックにはコンポーネントが含まれていません。',
|
||||
'partsCountTotal': '計{count}件',
|
||||
'partsCountRunning': '{count}件稼働中',
|
||||
'partsCountOff': '{count}件オフ',
|
||||
'partOff': 'オフ',
|
||||
'partsCountFailed': '{count}件失敗',
|
||||
'partsFilter': 'コンポーネントを絞り込む',
|
||||
'partsFilterEmpty': '一致するコンポーネントはありません。',
|
||||
'partToggle': 'コンポーネント{name}を有効化',
|
||||
'rowPhasePending': '依存関係を待機中',
|
||||
'rowPhaseLoading': '読み込み中',
|
||||
'rowPhaseActive': '稼働中',
|
||||
'rowPhaseFailed': '問題',
|
||||
'rowPhaseUnloading': 'アンロード中',
|
||||
'applyingChange': '{name} の変更を適用中',
|
||||
'installBundledTitle': 'このプラグインは DSH に同梱されています',
|
||||
'installBundledDescription': '追加のダウンロードなしで有効にできます',
|
||||
'enableToggle': '{name}を有効化',
|
||||
'openDetail': '{name}を表示',
|
||||
'backToList': 'プラグイン一覧に戻る',
|
||||
'crumbRoot': 'プラグイン',
|
||||
'backToPackage': '{name}に戻る',
|
||||
'configureRow': '{name}を設定',
|
||||
'rowStateIdle': '未稼働',
|
||||
'uninstall': 'アンインストール',
|
||||
'uninstallLabel': '{name}をアンインストール',
|
||||
'installTitle': 'プラグインを追加',
|
||||
'installDescription': 'プラグインのパッケージ名、GitHubリポジトリのアドレス、またはローカルディレクトリのパスを入力してください。',
|
||||
'installSpecLabel': 'パッケージ名またはアドレス',
|
||||
'installSpecPlaceholder': '例: dsh-plugin-whale-pet',
|
||||
'installGuideToggle': 'インストールガイドと例',
|
||||
'installGuideHide': 'ガイドを隠す',
|
||||
'installGuideIdTitle': 'プラグインのnpmパッケージ名を入力',
|
||||
'installGuideIdExample': 'dsh-plugin-whale-pet',
|
||||
'installGuideIdHint': 'プラグインパッケージ名とはnpmパッケージ名(dsh-xxxや@author/pluginなど)です。コミュニティプラグインのREADMEのインストールコマンドにあるdsh plugin addやpnpm addの後ろの部分です。',
|
||||
'installGuideExampleLabel': '例: ',
|
||||
'installGitTemplateHint': '実際のGitリポジトリのアドレスに置き換えてください。',
|
||||
'installPathTemplateHint': 'ローカルプラグインディレクトリの実際のパスに置き換えてください。',
|
||||
'installGuideFill': '例を使用',
|
||||
'installGuideFillAria': '例{example}を使用',
|
||||
'installGuideSafety': '信頼できるプラグインのみをインストールしてください。プラグインはあなたの権限で動作し、DeepSeek Harnessを破壊したりデータを漏洩させたりする可能性があります。',
|
||||
'installUpgradeNotice': 'オンデマンドの公式プラグインは、バージョンがDSHと異なる場合に更新を提供します。その他のプラグインを更新するには、アンインストールして再インストールしてください。',
|
||||
'registryToggle': 'レジストリ',
|
||||
'registryLegend': 'プラグインのダウンロード元のnpmレジストリ',
|
||||
'registryDefault': 'デフォルトレジストリ',
|
||||
'registryOfficial': '公式npmレジストリ',
|
||||
'registryNpmmirror': '中国本土ミラー',
|
||||
'registryCustom': 'カスタムアドレス',
|
||||
'registryCustomPlaceholder': 'https://npm.example.com/',
|
||||
'registryCustomHint': 'http://またはhttps://で始まる社内・プライベートのnpmレジストリのアドレスを入力してください。ログインが必要な場合は、このマシンの~/.npmrcに認証情報を保存してください。',
|
||||
'registryCustomInvalid': 'http://またはhttps://で始まるアドレスを入力してください',
|
||||
'registryListSeparator': ', ',
|
||||
'sentenceSeparator': ' ',
|
||||
'installRun': 'インストール',
|
||||
'installChecking': '確認中…',
|
||||
'installConfirmedTitle': 'プラグインをインストール',
|
||||
'installConfirmedRefused': 'このプラグインはインストールできません',
|
||||
'installProblemInvalid': 'パッケージ名またはインストール可能なアドレスではありません: {reason}',
|
||||
'installProblemInstalled': 'このプラグインはすでにインストールされています。アップグレードするには、アンインストールしてから再インストールしてください',
|
||||
'installProblemShipped': 'このプラグインはDSHに同梱されています。DSHをアップグレードすると更新されます',
|
||||
'installProblemNotFound': '該当するプラグインが見つかりません',
|
||||
'installProblemNotPackage': 'パスが存在しないか、有効なプラグインパッケージではありません',
|
||||
'installProblemNotBundle': 'このパッケージはバンドルを宣言していないため、プラグインとしてインストールできません: {reason}',
|
||||
'installProblemNetwork': 'プラグインレジストリに接続できませんでした。ネットワークを確認して再試行してください',
|
||||
'installProblemNetworkAll': 'どのレジストリにも接続できませんでした(試行: {registries})。ネットワークまたはプロキシ設定を確認するか、レジストリを変更してください',
|
||||
'installProblemUnknown': 'プラグインを照会できませんでした: {reason}',
|
||||
'installingTitle': 'プラグインをインストール中…',
|
||||
'installedTitle': 'インストール済み',
|
||||
'statusNotInstalled': '未インストール',
|
||||
'statusMissingFiles': 'パッケージ利用不可',
|
||||
'statusUpdateAvailable': 'アップデートあり',
|
||||
'sourceNotInstalled': '未インストール',
|
||||
'sourceTargetSpec': 'このDSH用のソース',
|
||||
'sourceTargetVersion': 'このDSH用のバージョン',
|
||||
'update': 'アップデート',
|
||||
'updateLabel': '{name}をアップデート',
|
||||
'versionsTitle': 'バージョン',
|
||||
'versionsRecheck': '再確認',
|
||||
'versionsChecking': 'アップデートを確認中',
|
||||
'versionsAvailable': '{version}へアップデート可能',
|
||||
'versionsPublished': '{date}に公開',
|
||||
'versionsLatest': '最新です',
|
||||
'versionsCheckedAt': '最終確認: {time}',
|
||||
'versionsBlocked': '新しいバージョンにはDSHのアップデートが必要です',
|
||||
'versionsBlockedDetail': '{version}には{peers}が必要です。このDSHは{runtime}です',
|
||||
'versionsNoneInstallable': '直接インストールできる新しいバージョンはありません',
|
||||
'versionsNoneInstallableDetail': '新しいバージョンは非推奨、プレリリース、またはバンドル未宣言です。展開して確認してください',
|
||||
'versionsFailedNetwork': 'レジストリに接続できませんでした',
|
||||
'versionsFailedOther': 'アップデートを確認できませんでした',
|
||||
'versionsDetails': '詳細',
|
||||
'versionsNewerCount': '新しいバージョン {count} 件',
|
||||
'versionsCollapse': '折りたたむ',
|
||||
'versionsInstalledTag': 'インストール済み',
|
||||
'versionsPrereleaseTag': 'プレリリース',
|
||||
'versionsDeprecatedTag': '非推奨',
|
||||
'versionsNeedsDsh': '{peers}が必要',
|
||||
'versionsNotBundle': 'このバージョンはバンドルを宣言していないためインストールできません',
|
||||
'versionsUpdateLabel': '{version}へアップデート',
|
||||
'versionsRegistry': 'レジストリ',
|
||||
'versionsCheckedAtLabel': '確認日時',
|
||||
'versionsFallbackTag': 'フォールバック',
|
||||
'versionsFallbackHint': '{registry}に接続できませんでした。リストはフォールバックレジストリからのものです',
|
||||
'versionsPrimaryTag': 'プライマリ',
|
||||
'versionsShowPrerelease': 'プレリリースを表示',
|
||||
'versionsOmitted': 'インストール済みより新しいバージョンが他に {count} 件ありますが、表示されていません',
|
||||
'versionsProblemNetwork': '到達不能',
|
||||
'versionsProblemNotFound': '見つかりません',
|
||||
'versionsProblemUnknown': '失敗',
|
||||
'updatesAvailable': 'アップデートあり: {count}',
|
||||
'updatesNone': 'すべてのプラグインは最新です',
|
||||
'updatesFailed': '一部のプラグインを確認できませんでした',
|
||||
'updateTitle': '{name}をアップデート',
|
||||
'updateSource': 'インストール元',
|
||||
'updateDescription': 'アップデートにより{from}が{to}に置き換わります。プラグインが一時停止する場合があります。インストールに失敗した場合、{from}は保持されるか再インストールされます。',
|
||||
'updateRunning': 'アップデート中',
|
||||
'updateStepChecking': '{version}を確認',
|
||||
'updateStepRemoving': '{version}を停止して削除',
|
||||
'updateStepInstalling': '{registry}から{version}をインストール',
|
||||
'updateStepRestoring': '{version}を再インストール',
|
||||
'updateDone': '{version}にアップデートしました',
|
||||
'updateDoneDetail': '新しいバージョンが実行中です。',
|
||||
'updateRestart': '{version}をインストールしました。再起動後に有効になります',
|
||||
'updateRefused': '{version}へアップデートできません',
|
||||
'updateRemoveFailed': '{version}を削除できませんでした',
|
||||
'updateRestored': 'アップデートに失敗しました。{version}を復元しました',
|
||||
'updateRestoredDetail': '{to}のインストールに失敗しました。{from}を再インストールしました。',
|
||||
'updateKept': '{version}へアップデートできませんでした',
|
||||
'updateKeptDetail': '{from}は削除されず、引き続き使用されます。{reason}',
|
||||
'updateBroken': 'アップデートに失敗しました。プラグインはインストールされていません',
|
||||
'updateBrokenDetail': '{to}のインストールに失敗し、{from}の再インストールにも失敗しました。プラグインを追加から{name}を再インストールしてください。',
|
||||
'updateLost': 'アップデート結果が不明です',
|
||||
'updateLostDetail': 'DSHへの接続が中断されました: {reason}。プラグインリストを更新してインストール済みバージョンを確認してください。',
|
||||
'updateOutput': 'インストール出力を表示',
|
||||
'updateFinish': '完了',
|
||||
'installFailedTitle': 'プラグインをインストールできませんでした',
|
||||
'installGithubFailedTitle': 'GitHubにアクセスできません',
|
||||
'installGithubTimeoutTitle': 'GitHubへの接続がタイムアウトしました',
|
||||
'installGithubFailedDescription': '別のインストール元を試してください。',
|
||||
'installUseGithubMirror': '中国本土ミラーを使用',
|
||||
'installTryAnotherWay': '別の方法を試す',
|
||||
'installPackageLabel': 'プラグインパッケージ名',
|
||||
'installEdit': '編集',
|
||||
'installEditAria': '編集に戻る',
|
||||
'installCancelAndEdit': 'インストールをキャンセルして編集に戻る',
|
||||
'installApplyingCancellationError': 'キャンセルが確認されませんでした。インストールを適用中です。結果をお待ちください。{reason}',
|
||||
'installReconcile': 'インストール状態を確認',
|
||||
'installUnknownTitle': 'インストール結果を取得できません',
|
||||
'installUnknownDescription': 'HostにこのリクエストIDの有効なインストールがありません。再試行する前にプラグイン一覧を確認してください。',
|
||||
'installResultUnconfirmed': 'インストール結果を受信できませんでした。インストール状態を確認してください。{reason}',
|
||||
'installAwaitingAcceptance': 'Hostがインストールを承認するのを待っています。承認後、キャンセルは自動的に再試行されます。',
|
||||
'installBackgroundUnknown': 'インストール結果を取得できません。プラグイン一覧を確認してください。',
|
||||
'installCancel': 'インストールをキャンセル',
|
||||
'installCloseCancels': 'インストールをキャンセルして閉じる',
|
||||
'installViewTask': 'インストールを表示',
|
||||
'installUnconfirmedTitle': 'インストール状態が未確認です',
|
||||
'installBackgroundDone': 'インストールが完了しました。インストール詳細を表示します。',
|
||||
'installBackgroundFailed': 'インストールに失敗しました。インストール詳細を表示します。',
|
||||
'installBackgroundUnconfirmed': 'インストール状態が未確認です。インストールを表示して詳細を確認してください。',
|
||||
'installBackgroundApplying': 'インストールを適用中のためキャンセルできません。インストールの進行状況を表示します。',
|
||||
'installStarting': 'インストールを準備中…',
|
||||
'installCancelling': 'インストールを停止中…',
|
||||
'installApplying': '設定を適用しています。お待ちください…',
|
||||
'installCancelledShort': 'キャンセル済み',
|
||||
'installCancelled': 'インストールをキャンセルしました。プラグインは有効化されておらず、ダウンロードしたファイルが残る場合があります',
|
||||
'installCancelUnconfirmed': 'インストールの停止が確認されていません。キャンセルを再試行するか、インストール結果をお待ちください。{reason}',
|
||||
'installEnableNow': '今すぐ有効化',
|
||||
'installDetailsShow': 'インストール詳細を表示',
|
||||
'installDetailsHide': 'インストール詳細を隠す',
|
||||
'installVersion': 'バージョン {version}',
|
||||
'installSubjectPath': 'ローカルディレクトリ',
|
||||
'installSubjectGit': 'Gitリポジトリ',
|
||||
'installSubjectTarball': 'Tarball',
|
||||
'installLocation': '{dir} にインストール',
|
||||
'installRetry': '再試行',
|
||||
'installChangeRegistry': 'レジストリを変更',
|
||||
'installAttempt': '{previous} ではパッケージを提供できませんでした。{registry} 経由で再試行しています(レジストリ {index}/{total})',
|
||||
'installAttemptBadge': '試行 {index} · {registry}',
|
||||
'installFailureNetwork': 'ネットワーク接続に失敗しました',
|
||||
'installFailureNetworkAll': 'レジストリに接続できませんでした(試行: {registries})。ネットワークまたはプロキシ設定を確認するか、レジストリを変更して再試行してください。',
|
||||
'installFailureNetworkHost': '{host} に接続できませんでした。GitHubアドレスや.tgzリンクはレジストリ経由では取得されません。このマシンが直接またはプロキシ経由で接続できる必要があります。プラグインがnpmにも公開されている場合は、パッケージ名を入力してください。',
|
||||
'installFailureNotFound': '該当するプラグインが見つかりません',
|
||||
'installFailureNoMatchingVersion': '要求に一致するバージョンがありません',
|
||||
'installFailureDiskFull': 'ディスクの容量が不足しているため、インストールを停止しました',
|
||||
'installFailurePermission': '書き込み権限がないため、プラグインをインストールできません',
|
||||
'installFailureBuildBlocked': 'インストールを続行するには、依存関係のインストールスクリプトに許可を与える必要があります',
|
||||
'installFailureBuildBlockedManual': 'pnpmがインストールスクリプトをブロックしました。pnpm-workspace.yamlのallowBuildsで許可して再試行してください',
|
||||
'installFailureIntegrity': 'ダウンロードしたパッケージの整合性チェックに失敗しました',
|
||||
'installFailureTimeout': 'インストールがタイムアウトしました',
|
||||
'installFailurePnpmMissing': 'pnpmが見つからないため、インストールできません',
|
||||
'installFailureGeneric': 'インストール中に問題が発生しました。詳細をご確認ください',
|
||||
'terminalCommandLine': 'コマンドライン {n}',
|
||||
'terminalRunning': '実行中',
|
||||
'terminalFailed': '失敗',
|
||||
'terminalDone': '完了',
|
||||
'terminalCopy': 'コピー',
|
||||
'terminalCopied': 'コピー済み',
|
||||
'terminalNoOutput': '出力なし',
|
||||
'terminalCollapseAria': '出力を折りたたむ',
|
||||
'terminalCollapse': '折りたたむ',
|
||||
'terminalExpandAria': '残り {n} 行の出力を展開',
|
||||
'terminalExpand': '… さらに {n} 行',
|
||||
'terminalExitCode': '終了コード {code}',
|
||||
'terminalSignal': 'シグナル {signal}',
|
||||
'terminalNoExitCode': '終了コードなし',
|
||||
'installDoneNothing': '新しい依存関係はなく、インストールが完了しました。',
|
||||
'installDoneRestart': 'インストール済みです。読み込むにはDSHを再起動してください。{reason}',
|
||||
'installDoneApproved': '{names} のインストールスクリプトを許可しました',
|
||||
'installDoneOtherVersion': 'pnpm のサプライチェーンセキュリティポリシー(minimumReleaseAge)では、必須の経過時間より新しく公開されたバージョンは自動的にインストールされないため、このインストールでは最新バージョン {version} ではなく {installed} を使用しました。新しく公開されたバージョンはこの待機期間を経過しておらず、検出されていない悪意のあるコードや欠陥を含む可能性があります。{version} が必要な場合は、現在のバージョンをアンインストールしてから {exact} をインストールしてそのバージョンを要求してください。',
|
||||
'installApprovalTitle': 'インストールスクリプトには許可が必要です',
|
||||
'installApprovalDescription': 'これらのパッケージには pnpm が実行しなかったインストールスクリプトがあります。',
|
||||
'installApprovalConsequence': '許可すると、スクリプトはここであなたの権限で実行され、許可はこのプロファイルに保存されます。',
|
||||
'installApprovalCaution': '信頼できるパッケージのみ許可してください。',
|
||||
'installApproveAndRetry': 'これらのスクリプトを許可して再試行',
|
||||
'installClose': '完了',
|
||||
'close': '閉じる',
|
||||
'cancel': 'キャンセル',
|
||||
'confirmUninstallTitle': '「{name}」をアンインストールしますか?',
|
||||
'confirmUninstallDescription': 'アンインストールすると、提供している機能は利用できなくなります。',
|
||||
'confirmUninstall': 'アンインストール',
|
||||
'failedEnable': '有効化できませんでした: {reason}',
|
||||
'failedDisable': '無効化できませんでした: {reason}',
|
||||
'failedUninstall': 'アンインストールできませんでした: {reason}',
|
||||
'failedRowEnable': 'コンポーネントを有効化できませんでした: {reason}',
|
||||
'failedRowDisable': 'コンポーネントを無効化できませんでした: {reason}',
|
||||
'reasonManagementRequired': 'プラグイン管理に必要です。無効化もアンインストールもできません。',
|
||||
'reasonUnaddressable': 'プロファイルパッチでこれを一意に指定できません。',
|
||||
'reasonUnknownPlugin': 'そのようなプラグインはありません。',
|
||||
'reasonInvalidSpec': '有効なパッケージ名またはアドレスを入力してください。',
|
||||
'reasonAlreadyInstalled': 'このパッケージはすでにインストールされています。別のバージョンをインストールする前にアンインストールしてください。',
|
||||
'reasonAmbiguousInstall': '依存関係の変更から、どのパッケージがインストールされたか判断できません。',
|
||||
'reasonNotBundle': 'このパッケージはバンドルを宣言していないため、プラグインとして管理できません。',
|
||||
'reasonNotRemovable': 'このパッケージはプロファイル所有ではないか、プラグイン管理に必要です。',
|
||||
'reasonStopProfile': 'このプロファイルは HMR なしで動作します。停止してから dsh plugin でパッケージをアンインストールしてください。',
|
||||
'reasonBundleInUse': '他の設定がこのバンドルのコンポーネントをまだ使用しています。先にそれらを無効にしてください。',
|
||||
'reasonStaleApproval': '保留中のスクリプト承認が変更されました。インストールをやり直して更新してください。',
|
||||
'reasonIncompatibleVersion': '{plugin} は DSH {runtime} と互換性がありません({requirements} が必要)。実行するとクラッシュやデータ損失を引き起こす可能性があります。',
|
||||
'reasonIncompatibleVersionUnnamed': 'このプラグインは実行中の DSH バージョンと互換性がありません。実行するとクラッシュやデータ損失を引き起こす可能性があります。',
|
||||
'reasonIncompatibleInstall': 'この DSH と互換性のあるプラグインバージョンをインストールしてください。',
|
||||
'reasonIncompatibleInstalled': 'アンインストールして、この DSH と互換性のあるバージョンをインストールしてください。',
|
||||
'reasonOperationError': 'Host がエラーを報告しました。',
|
||||
'detailTitle': '詳細',
|
||||
'detailLoading': '詳細を読み込み中',
|
||||
'detailTooLarge': '詳細がサイズ上限を超えているため読み込まれませんでした。',
|
||||
'detailNetwork': '詳細の取得元に接続できませんでした。',
|
||||
'detailInvalid': 'プラグインが無効な詳細ファイルを宣言しています。',
|
||||
'detailFailed': '詳細を読み込めませんでした。',
|
||||
'detailFootnotes': '脚注',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -12,7 +12,7 @@ import type {} from '@deepseek-ai/dsh-plugin-manager/types'
|
||||
import type {} from '@deepseek-ai/dsh-plugin-market/types'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-plugin-manager/client'
|
||||
import { DiscoverSection } from './DiscoverSection.tsx'
|
||||
import { en, zh, type PluginMarketLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type PluginMarketLocaleKey } from './locales.ts'
|
||||
import { DiscoverController } from './market-store.ts'
|
||||
|
||||
export type { DiscoverFace, DiscoverState } from './market-store.ts'
|
||||
@@ -36,7 +36,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.pluginMarket', 'remo
|
||||
* @param ctx - the browser plugin context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plugin-market: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-plugin-market: dictionaries')
|
||||
const controller = new DiscoverController(ctx, () => document.baseURI)
|
||||
ctx.effect(() => () => { controller.dispose() }, 'ui-plugin-market: controller')
|
||||
ctx.effect(() => {
|
||||
|
||||
@@ -184,3 +184,94 @@ export const en = {
|
||||
detailFailed: 'Could not read the details.',
|
||||
footnotes: 'Footnotes',
|
||||
} satisfies Record<PluginMarketLocaleKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': '見つける',
|
||||
'refresh': '更新',
|
||||
'loading': 'インデックスを読み込み中',
|
||||
'retry': '再試行',
|
||||
'close': '閉じる',
|
||||
'cancel': 'キャンセル',
|
||||
'emptyTitle': 'プラグインマーケットはまだありません',
|
||||
'emptyDescription': 'プラグインマーケットのインデックスを追加すると、ここでプラグインを閲覧してインストールできます。',
|
||||
'addFirstSource': 'インデックスソースを追加',
|
||||
'sourcesFailed': 'インデックスソースを読み取れませんでした。',
|
||||
'manageSources': 'インデックスソースを管理…',
|
||||
'marketMenu': 'プラグインマーケットを切り替え、現在は{market}',
|
||||
'sourcesTitle': 'インデックスソース',
|
||||
'sourcesDescription': 'プラグインマーケットは1つのインデックスファイルのURLとして公開されます。「見つける」ではここに一覧されたインデックスを閲覧します。',
|
||||
'sourcesLabel': '追加済みのインデックスソース',
|
||||
'sourceUrl': 'インデックスURL',
|
||||
'sourcePlaceholder': 'https://example.com/index.json',
|
||||
'addSource': '追加',
|
||||
'removeSource': '削除',
|
||||
'removeSourceNamed': '{url}を削除',
|
||||
'sourcesTrust': 'インデックスは第三者提供です。信頼できるソースのみ追加してください。インストールされたプラグインはあなたの権限で実行されます。',
|
||||
'sourceInvalid': 'ユーザー名、パスワード、#フラグメントを含まないhttpまたはhttpsのアドレスを使用してください。',
|
||||
'sourceDuplicate': 'このインデックスソースはすでに追加されています。',
|
||||
'sourceUnavailable': 'ここではインデックスソースを保存できません。',
|
||||
'sourceFailed': 'インデックスソースを保存できませんでした。',
|
||||
'readFailed': 'このインデックスを読み取れませんでした。',
|
||||
'readInvalid': 'このインデックスは有効な形式ではありません。',
|
||||
'readChanged': '読み込み中にこのインデックスが変更されました。',
|
||||
'search': 'プラグインを検索',
|
||||
'filter': 'ラベル',
|
||||
'filterCount': 'ラベル · {count}',
|
||||
'sortBy': '並び替え: {sort}',
|
||||
'sortRelevance': '一致度順',
|
||||
'sortDefault': 'デフォルト順',
|
||||
'sortPublished': '公開日',
|
||||
'switchToAscending': '昇順で並び替え',
|
||||
'switchToDescending': '降順で並び替え',
|
||||
'categoriesLabel': 'カテゴリ',
|
||||
'allCategories': 'すべて',
|
||||
'emptyIndex': 'このインデックスにはプラグインがありません。',
|
||||
'noMatches': '一致するプラグインがありません。',
|
||||
'showMore': 'さらに表示',
|
||||
'entryMeta': '{version} · {date}',
|
||||
'entryMetaAuthor': '{version} · {author} · {date}',
|
||||
'openEntry': '{name}を表示',
|
||||
'install': 'インストール',
|
||||
'installTitle': '{name}をインストール',
|
||||
'installVersionTitle': '{name} {version}をインストール',
|
||||
'openInstalled': 'インストール済みで{name}を表示',
|
||||
'statusInstalled': 'インストール済み',
|
||||
'statusUpdate': 'アップデートあり',
|
||||
'statusProvided': 'DSH が提供',
|
||||
'incompatible': 'このバージョンは実行中の DSH {runtime} と互換性がありません。DSH {range} が必要です。',
|
||||
'noCompatibleVersion': 'DSH {runtime} と互換性のあるバージョンはありません',
|
||||
'exempted': 'このバージョンは実行中の DSH と互換性がありませんが、互換性の例外が設定されています。',
|
||||
'backToList': '見つける',
|
||||
'factSource': 'ソース',
|
||||
'factPackage': 'パッケージ',
|
||||
'factRegistry': 'レジストリ',
|
||||
'factRepository': 'リポジトリ',
|
||||
'factCommit': 'コミット',
|
||||
'factDirectory': 'ディレクトリ',
|
||||
'factPublished': '公開日',
|
||||
'factAuthor': '作者',
|
||||
'factHomepage': 'ホームページ',
|
||||
'factSourceCode': 'ソースコード',
|
||||
'factCategory': 'カテゴリ',
|
||||
'factLabels': 'ラベル',
|
||||
'factMarket': 'プラグインマーケット',
|
||||
'versionsTitle': 'バージョン',
|
||||
'versionsLabel': '{name} のバージョン',
|
||||
'versionDefault': '推奨',
|
||||
'versionInstalled': 'インストール済み',
|
||||
'versionIncompatible': '非互換',
|
||||
'versionExempted': '例外適用',
|
||||
'versionPrerelease': 'プレリリース',
|
||||
'confirmTitle': '「{name}」をインストールしますか?',
|
||||
'installTrust': 'プラグインはこのマシン上であなたの権限で実行されます。提供元が信頼できることを確認してください。',
|
||||
'detailTitle': '詳細',
|
||||
'detailLoading': '詳細を読み込み中',
|
||||
'detailTooLarge': '詳細またはそのリポジトリがサイズ上限を超えたため、読み込まれませんでした。',
|
||||
'detailNetwork': '詳細の取得元に接続できませんでした。',
|
||||
'detailDenied': '詳細のリポジトリにアクセスできませんでした。Git の認証情報を確認してください。',
|
||||
'detailGitUnavailable': '詳細は Git で読み取る必要があるリポジトリにありますが、Git を実行できませんでした。Git をインストールしてから再試行してください。',
|
||||
'detailInvalid': 'プラグインが無効な詳細ファイルを宣言しています。',
|
||||
'detailFailed': '詳細を読み取れませんでした。',
|
||||
'footnotes': '脚注',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -33,7 +33,7 @@ import { formatFileMention } from '@deepseek-ai/dsh-file-reference/grammar'
|
||||
import type { FileReferenceCandidate } from '@deepseek-ai/dsh-file-reference/types'
|
||||
import type { SessionReferenceMentionCandidate } from '@deepseek-ai/dsh-session-reference/types'
|
||||
import { abbreviateHomePath, fileAddressFor } from '@deepseek-ai/dsh-util-workspace-path'
|
||||
import { en, NS, zh, type ReferenceKey } from './locales.ts'
|
||||
import { en, ja, NS, zh, type ReferenceKey } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-api-session-controller/client' {
|
||||
interface SessionReferenceSourceMap {
|
||||
@@ -53,7 +53,7 @@ export const inject = [
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-reference: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-reference: dictionaries')
|
||||
const t = ctx.locale.bind(NS)
|
||||
const sessions = ctx.get('sessions') as ISessions
|
||||
ctx.uiConversation.userText.register(fileReferenceRule)
|
||||
|
||||
@@ -58,3 +58,22 @@ export const en = {
|
||||
'time.months': '{n}mo',
|
||||
'time.years': '{n}y',
|
||||
} satisfies Record<ReferenceKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'section.files': 'ファイルとフォルダ',
|
||||
'section.subagents': 'サブエージェント',
|
||||
'section.sessions': 'セッション',
|
||||
'candidate.noCwd': '(cwdなし)',
|
||||
'crumb.root': 'ワークスペース',
|
||||
'fileOpen.title': 'ファイルを開けませんでした',
|
||||
'fileOpen.failed': '「{path}」を開けませんでした。再試行するか、このメッセージを閉じてください。',
|
||||
'fileOpen.close': '閉じる',
|
||||
'fileOpen.retry': '再試行',
|
||||
'time.now': '今',
|
||||
'time.minutes': '{n}分',
|
||||
'time.hours': '{n}時間',
|
||||
'time.days': '{n}日',
|
||||
'time.months': '{n}か月',
|
||||
'time.years': '{n}年',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -125,3 +125,61 @@ export const frequencyEn: Record<keyof typeof frequencyZh, string> = {
|
||||
'relative.future': 'in {value} {unit}',
|
||||
'relative.overdue': '{value} {unit} overdue',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const frequencyJa = {
|
||||
'time.locale': 'ja-JP',
|
||||
'time.utcPrefix': 'UTC',
|
||||
'frequency.daily': '毎日 {time} ({timeZone})',
|
||||
'frequency.dailyLocal': '毎日 {time}',
|
||||
'frequency.weekly': '毎週 {weekdays} の {time} ({timeZone})',
|
||||
'frequency.weeklyLocal': '毎週 {weekdays} の {time}',
|
||||
'frequency.cron': 'Cron {expression} ({timeZone})',
|
||||
'frequency.cronLocal': 'Cron {expression}',
|
||||
'frequency.cronRule': '{rule} ({timeZone})',
|
||||
'cron.list.join': '、',
|
||||
'cron.part.join': ' ',
|
||||
'cron.weekday.name': '{weekday}',
|
||||
'cron.weekday.range': '{from}〜{to}',
|
||||
'cron.months': ' {months}',
|
||||
'cron.day.every': '毎日{months}',
|
||||
'cron.day.weekdays': '{weekdays}{months}',
|
||||
'cron.day.monthDays': '毎月{days}日{months}',
|
||||
'cron.day.both': '毎月{days}日または{weekdays}{months}',
|
||||
'cron.day.bothStarred': '毎月{days}日と{weekdays}{months}',
|
||||
'cron.hours.range': '{from}〜{to}',
|
||||
'cron.hours.list': '{hours}',
|
||||
'cron.time.everyMinute': '毎分',
|
||||
'cron.time.everyMinutes': '{step}分ごと',
|
||||
'cron.time.joinedEveryMinute': '毎分',
|
||||
'cron.time.joinedEveryMinutes': '{step}分ごと',
|
||||
'cron.time.everyHour': '毎時',
|
||||
'cron.time.joinedEveryHour': '毎時',
|
||||
'cron.time.everyNHours': '{count}時間ごと',
|
||||
'cron.time.joinedEveryNHours': '{count}時間ごと',
|
||||
'cron.time.hourlyAt': '毎時{minutes}分',
|
||||
'cron.time.joinedHourlyAt': '毎時{minutes}分',
|
||||
'cron.time.hoursEveryMinute': '{hours}時の毎分',
|
||||
'cron.time.hoursEveryMinutes': '{hours}時の{step}分ごと',
|
||||
'cron.time.at': '{times}に',
|
||||
'cron.time.hoursAt': '{hours}時の{minutes}分に',
|
||||
'frequency.weekday.join': '、',
|
||||
'frequency.weekday.1': '月',
|
||||
'frequency.weekday.2': '火',
|
||||
'frequency.weekday.3': '水',
|
||||
'frequency.weekday.4': '木',
|
||||
'frequency.weekday.5': '金',
|
||||
'frequency.weekday.6': '土',
|
||||
'frequency.weekday.7': '日',
|
||||
'unit.day.one': '日',
|
||||
'unit.day.other': '日',
|
||||
'unit.hour.one': '時間',
|
||||
'unit.hour.other': '時間',
|
||||
'unit.minute.one': '分',
|
||||
'unit.minute.other': '分',
|
||||
'unit.second.one': '秒',
|
||||
'unit.second.other': '秒',
|
||||
'relative.now': 'まもなく期限',
|
||||
'relative.future': '{value}{unit}後',
|
||||
'relative.overdue': '{value}{unit}超過',
|
||||
} satisfies Record<keyof typeof frequencyEn, string>
|
||||
@@ -58,8 +58,8 @@ import { TaskManagerPage, type TaskManagerInjected } from './TaskManagerPage.tsx
|
||||
import type { TaskDetailInjected } from './TaskDetail.tsx'
|
||||
import { TaskManagerIcon } from './TaskManagerIcon.tsx'
|
||||
import { sessionLinkState } from './session-link.ts'
|
||||
import { en, NS, zh, type ScheduleCatalogKey } from './locales.ts'
|
||||
import { en as managerEn, zh as managerZh, type TaskManagerKey } from './task-manager-locales.ts'
|
||||
import { en, ja, NS, zh, type ScheduleCatalogKey } from './locales.ts'
|
||||
import { en as managerEn, ja as managerJa, zh as managerZh, type TaskManagerKey } from './task-manager-locales.ts'
|
||||
|
||||
const MANAGER_NS = 'schedule.manager'
|
||||
const PANEL_ID = 'schedules' as MainPanelId
|
||||
@@ -86,8 +86,9 @@ export const inject = [
|
||||
* @param ctx - browser services used by these contributions.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-schedule: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(MANAGER_NS, { zh: managerZh, en: managerEn }), 'ui-schedule: manager dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-schedule: dictionaries')
|
||||
const managerDictionaries = { zh: managerZh, en: managerEn, ja: managerJa }
|
||||
ctx.effect(() => ctx.locale.register(MANAGER_NS, managerDictionaries), 'ui-schedule: manager dictionaries')
|
||||
const t = ctx.locale.bind(MANAGER_NS)
|
||||
const manager: CatalogInjected<ScheduleCatalogEntry> = createCatalogSource<ScheduleCatalogEntry>({
|
||||
list: () => ctx.remote.schedule.catalog(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* `schedule.catalog` namespace dictionaries: the Session header catalog and the
|
||||
* Sidebar row mark with its hover-card task section.
|
||||
*/
|
||||
import { frequencyEn, frequencyZh } from './frequency-locales.ts'
|
||||
import { frequencyEn, frequencyJa, frequencyZh } from './frequency-locales.ts'
|
||||
|
||||
/** Dictionary namespace owned by this plugin. */
|
||||
export const NS = 'schedule.catalog'
|
||||
@@ -51,3 +51,24 @@ export const en: Record<ScheduleCatalogKey, string> = {
|
||||
|
||||
/** Key domain of the Schedule catalog namespace. */
|
||||
export type ScheduleCatalogKey = keyof typeof zh
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
...frequencyJa,
|
||||
'trigger.label': 'リマインダー',
|
||||
'list.loading': 'リマインダーを読み込み中…',
|
||||
'list.error': 'リマインダーを読み込めませんでした。',
|
||||
'list.retry': '再試行',
|
||||
'delete.action': '削除',
|
||||
'delete.pending': '削除中…',
|
||||
'delete.label': 'リマインダーを削除: {title}',
|
||||
'list.open': 'リマインダーの詳細を開く: {title}',
|
||||
'trigger.one': '{count}件のリマインダー',
|
||||
'trigger.other': '{count}件のリマインダー',
|
||||
'list.aria': '有効なリマインダー',
|
||||
'list.nextRun': '次回実行',
|
||||
'frequency.once': '1回のみ',
|
||||
'frequency.every': '{value}{unit}ごと',
|
||||
'mark.aria': 'スケジュールされたタスク {count}件',
|
||||
'hover.more': '他 {count}件',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -1,5 +1,5 @@
|
||||
/** Copy for the retained-reminder task manager's `schedule.manager` namespace. */
|
||||
import { frequencyEn, frequencyZh } from './frequency-locales.ts'
|
||||
import { frequencyEn, frequencyJa, frequencyZh } from './frequency-locales.ts'
|
||||
|
||||
/** English task-manager dictionary and key domain. */
|
||||
export const en = {
|
||||
@@ -283,3 +283,142 @@ export const zh: Record<TaskManagerKey, string> = {
|
||||
'card.deleted': '已删除',
|
||||
'tool.invoked': '已调用 {name}',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
...frequencyJa,
|
||||
'panel': '自動化タスク',
|
||||
'title': '自動化タスク',
|
||||
'new.action': '新規',
|
||||
'list.label': 'タスク一覧',
|
||||
'list.nextPrefix': '次回予定日時: ',
|
||||
'list.loading': 'タスクを読み込み中…',
|
||||
'list.error': 'タスクを読み込めませんでした。',
|
||||
'list.retry': '再試行',
|
||||
'list.empty': 'タスクはまだありません。セッションで作成したタスクがここに表示されます。',
|
||||
'list.emptyInactive': '無効な自動化タスクはありません',
|
||||
'list.noMatches': '一致するタスクはありません',
|
||||
'search.label': 'タスクを検索',
|
||||
'search.placeholder': '自動化タスクを検索',
|
||||
'search.clear': '検索をクリア',
|
||||
'empty.action': '新規自動化タスク',
|
||||
'statusFilter.label': 'タスクの状態',
|
||||
'statusFilter.all': 'すべて',
|
||||
'status.active': '有効',
|
||||
'status.inactive': '無効',
|
||||
'detail.label': 'タスクの詳細',
|
||||
'detail.close': '詳細を閉じる',
|
||||
'detail.more': '自動化タスクの操作',
|
||||
'detail.nextRun': '次回実行',
|
||||
'detail.tabs': 'タスク詳細ビュー',
|
||||
'detail.rule': 'ルール',
|
||||
'detail.records': '記録',
|
||||
'delivery.empty': '配信記録はありません',
|
||||
'delivery.loading': '配信記録を読み込み中…',
|
||||
'delivery.error': '配信記録を読み込めませんでした。',
|
||||
'delivery.notFound': 'このタスクは利用できなくなりました。',
|
||||
'delivery.cursorError': '記録が更新されました。',
|
||||
'delivery.retry': '再試行',
|
||||
'delivery.refresh': '更新',
|
||||
'delivery.loadMore': 'さらに読み込む',
|
||||
'delivery.expand': '展開',
|
||||
'delivery.collapse': '折りたたむ',
|
||||
'delivery.pruned': '以前の配信記録は削除されました',
|
||||
'delivery.retention': '保持ルール',
|
||||
'delivery.retentionBounds': '各タスクは過去 {days} 日間の配信記録を最大 {records} 件保持します。',
|
||||
'delivery.retentionExplanation': '新しい記録を保存すると、これらの上限を超えた古い記録は自動的に削除されます。記録を削除してもタスクは停止しません。',
|
||||
'detail.name': '自動化タスク名',
|
||||
'detail.instruction': '自動化タスクの指示',
|
||||
'detail.status': '状態',
|
||||
'detail.next': '次回予定日時',
|
||||
'detail.frequency': '頻度',
|
||||
'detail.id': 'タスク ID',
|
||||
'detail.session': 'リンクされたセッション',
|
||||
'detail.openSession': 'リンクされたセッション: 元のセッションを開く',
|
||||
'detail.openSessionTitle': 'リンクされたセッション {title}: 元のセッションを開く',
|
||||
'detail.sessionLoading': '元のセッション情報を読み込み中。',
|
||||
'detail.sessionArchived': '元のセッションはアーカイブされています。',
|
||||
'detail.sessionUnavailable': '元のセッションは利用できません。',
|
||||
'detail.missing': 'タスクを利用できません。削除された可能性があります。',
|
||||
'delivery.label': '保存済みの配信記録',
|
||||
'frequency.once': '1回のみ',
|
||||
'frequency.every': '{value}{unit}ごと',
|
||||
'timing.date': '日付',
|
||||
'timing.time': '時刻',
|
||||
'timing.hour': '時',
|
||||
'timing.minute': '分',
|
||||
'timing.second': '秒',
|
||||
'timing.prevMonth': '前月',
|
||||
'timing.nextMonth': '翌月',
|
||||
'timing.zone': 'タイムゾーン',
|
||||
'timing.zoneSearch': 'UTCオフセット、IANA ID、都市名で検索',
|
||||
'timing.zoneNoResults': '一致するタイムゾーンがありません',
|
||||
'timing.interval': '繰り返し間隔',
|
||||
'timing.intervalIncrease': '間隔を増やす',
|
||||
'timing.intervalDecrease': '間隔を減らす',
|
||||
'timing.unit.hour': '時間',
|
||||
'timing.unit.minute': '分',
|
||||
'timing.unit.second': '秒',
|
||||
'timing.intervalHint.hour': '1時間以上を指定してください。間隔はタスクの作成時または最後のルール変更時から数え、タイムゾーンには依存しません。',
|
||||
'timing.intervalHint.minute': '1分以上を指定してください。間隔はタスクの作成時または最後のルール変更時から数え、タイムゾーンには依存しません。',
|
||||
'timing.intervalHint.second': '60秒以上を指定してください。間隔はタスクの作成時または最後のルール変更時から数え、タイムゾーンには依存しません。',
|
||||
'timing.zoneNoStored': '1回のみのタスクは実行時刻のみを保存し、タイムゾーンは保存しません。日付と時刻は選択したタイムゾーンで解釈されます。',
|
||||
'timing.inactive': '無効なタスクは読み取り専用です。タイミングは編集できません。',
|
||||
'timing.conflict': '編集中にこのタスクが変更されました。下書きは保持されています。最新のルールを編集するには、キャンセルして再読み込み後に開き直してください。',
|
||||
'timing.notFound': 'このタスクは利用できなくなりました。下書きは保持されています。キャンセルするとエディターを閉じます。',
|
||||
'timing.subagentSession': 'このセッションはサブエージェントのものであり、配信リマインダーを受け取りません。',
|
||||
'timing.invalid': '有効な日付と時刻を入力するか、タイミングの各項目を確認してください。',
|
||||
'timing.invalidZone': '有効なIANAタイムゾーンを入力してください(例: Asia/Shanghai)。',
|
||||
'timing.notFuture': '未来の日付と時刻を選択してください。',
|
||||
'timing.invalidInterval': '1分以上の間隔を入力してください。',
|
||||
'timing.invalidInterval.hour': '1時間以上の間隔を入力してください。',
|
||||
'timing.invalidInterval.minute': '1分以上の間隔を入力してください。',
|
||||
'timing.invalidInterval.second': '60秒以上の間隔を入力してください。',
|
||||
'timing.error': 'タイミングの更新を確認できませんでした。下書きは保持されています。タスクを確認してから再試行してください。',
|
||||
'rule.title': '実行時刻',
|
||||
'rule.repeat': '繰り返し',
|
||||
'rule.weekday': '曜日',
|
||||
'rule.weekdayOption': '{weekday}曜日',
|
||||
'rule.unsaved': '未保存の変更',
|
||||
'rule.save': '変更を保存',
|
||||
'rule.saving': '保存中…',
|
||||
'rule.cancel': 'キャンセル',
|
||||
'rule.invalidTitle': 'タスク名は120文字以内で入力してください。',
|
||||
'rule.invalidPrompt': 'タスクの指示を入力してください。',
|
||||
'rule.once': '1回のみ',
|
||||
'rule.everyMinutes': 'N分ごと',
|
||||
'rule.everyHours': 'N時間ごと',
|
||||
'rule.everySeconds': 'N秒ごと',
|
||||
'rule.daily': '毎日',
|
||||
'rule.weekdays': '月曜日から金曜日',
|
||||
'rule.weekly': '毎週',
|
||||
'rule.cron': 'カスタム',
|
||||
'rule.cronLabel': 'Cron式',
|
||||
'rule.cronInvalid': '5フィールドのCron式を入力してください(例: 0 9 * * 1-5)。',
|
||||
'cronForm.frequency': '頻度',
|
||||
'cronForm.monthly': '毎月',
|
||||
'cronForm.weekly': '毎週',
|
||||
'cronForm.daily': '毎日',
|
||||
'cronForm.dates': '日付指定',
|
||||
'cronForm.dateOption': '{day}日',
|
||||
'cronForm.atMinute': '分',
|
||||
'cronForm.minuteIncrease': '分を増やす',
|
||||
'cronForm.minuteDecrease': '分を減らす',
|
||||
'rule.zone.system': '(システム)',
|
||||
'rule.error.conflict': '更新の保存前にこのタスクが変更されました。行には保存済みのルールが表示されています。もう一度お試しください。',
|
||||
'rule.error.notFound': 'このタスクは利用できなくなりました。',
|
||||
'rule.error.unknown': 'ルールの更新を確認できませんでした。行には保存済みのルールが表示されています。',
|
||||
'delete.action': 'タスクを削除',
|
||||
'delete.title': 'このタスクを削除しますか?',
|
||||
'delete.description': 'タスクの実行が停止し、保存済みの配信記録とともに削除されます。元のセッションとそのメッセージは残り、キューに入ったメッセージは取り消されません。',
|
||||
'delete.confirm': '削除を確定',
|
||||
'delete.cancel': 'キャンセル',
|
||||
'delete.close': '削除確認を閉じる',
|
||||
'delete.pending': '削除中…',
|
||||
'toast.deleted': 'タスクを削除しました。',
|
||||
'toast.deleteFailed': 'タスクを削除できませんでした。',
|
||||
'card.open': '開く',
|
||||
'card.openLabel': 'タスクの詳細を開く: {title}',
|
||||
'card.deleted': '削除済み',
|
||||
'tool.invoked': '{name} を実行しました',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -15,7 +15,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { SecretText } from './SecretText.tsx'
|
||||
import { createSecretRule } from './secret-rule.ts'
|
||||
export type {} from './secret-rule.ts'
|
||||
import { en, NS, zh } from './locales.ts'
|
||||
import { en, ja, NS, zh } from './locales.ts'
|
||||
|
||||
/** Required services: the copy, the node renderer registry, and the UserText rule registry. */
|
||||
export const inject = ['locale', 'slots', 'uiConversation']
|
||||
@@ -25,7 +25,7 @@ export const inject = ['locale', 'slots', 'uiConversation']
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-secret-mask: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-secret-mask: dictionaries')
|
||||
ctx.uiConversation.userText.register(createSecretRule())
|
||||
ctx.slots.inject('conversation.user.text.node', () => ctx.slots.register({
|
||||
name: 'conversation.user.text.node', key: 'secret', priority: 100, locale: NS,
|
||||
|
||||
@@ -26,3 +26,9 @@ export const en = {
|
||||
'secret.reveal': 'Show secret',
|
||||
'secret.hide': 'Hide secret',
|
||||
} satisfies Record<SecretMaskKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'secret.reveal': 'シークレットを表示',
|
||||
'secret.hide': 'シークレットを隠す',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -23,7 +23,7 @@ import { createPlatformPages, type PlatformPages } from './platform-pages.ts'
|
||||
import { AccountSection, type AccountSnapshot, type AccountSectionInjected } from './AccountSection.tsx'
|
||||
import { createBonusNoticeController } from './bonus-notices.ts'
|
||||
import { accountClientMetadata } from './client-metadata.ts'
|
||||
import { en, zh, type AccountKey } from './locales.ts'
|
||||
import { en, ja, zh, type AccountKey } from './locales.ts'
|
||||
import { AccountQuotaNotice, type AccountQuotaNoticeInjected } from './AccountQuotaNotice.tsx'
|
||||
import { DESKTOP_ONBOARDING_NAMESPACE, type OnboardingSettings } from '../onboarding-settings.ts'
|
||||
import { DesktopOnboardingController } from './onboarding-state.ts'
|
||||
@@ -45,7 +45,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.account', 'remote.se
|
||||
/** Register account UI in Desktop and plain Web clients. @param ctx - client plugin context. */
|
||||
export function apply(ctx: Context): void {
|
||||
const desktop = 'dshDesktop' in globalThis
|
||||
ctx.effect(() => ctx.locale.register('settings.account', { en, zh }), 'account: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register('settings.account', { en, zh, ja }), 'account: dictionaries')
|
||||
const t = ctx.locale.bind('settings.account')
|
||||
const page = globalThis as Partial<Record<typeof CONTACT_CONFIG_GLOBAL, unknown>>
|
||||
const config = ContactConfig(page[CONTACT_CONFIG_GLOBAL] ?? {})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/** Account settings copy, owned by the account feature. */
|
||||
import { onboardingCopy, onboardingEnglishCopy } from './locales/onboarding.ts'
|
||||
import { onboardingZh, onboardingEn, onboardingJa } from './locales/onboarding.ts'
|
||||
|
||||
/** English account dictionary. */
|
||||
export const en = {
|
||||
modelSignInRequired: 'Model unavailable. Please sign in and try again.',
|
||||
sessionExpired: 'You have signed out of your account, please log in again.',
|
||||
...onboardingEnglishCopy,
|
||||
...onboardingEn,
|
||||
close: 'Close', addApiKey: 'Add API Key', retry: 'Sign in again',
|
||||
loginTitle: 'Get started', loginDescription: 'Sign in to your DeepSeek account or add an API Key to get started. Your projects and files are stored locally.',
|
||||
browserTitle: 'Waiting for sign in', browserPrompt: 'Page did not open automatically? ', copyLink: 'Copy sign-in link', copiedLink: 'Link copied', copyFailed: 'Copy failed',
|
||||
@@ -41,7 +41,7 @@ export type AccountKey = keyof typeof en
|
||||
export const zh: Record<AccountKey, string> = {
|
||||
modelSignInRequired: '当前模型暂不可用,请登录后再试',
|
||||
sessionExpired: '登录信息已失效,请重新登录',
|
||||
...onboardingCopy,
|
||||
...onboardingZh,
|
||||
close: '关闭', addApiKey: '添加 API Key', retry: '重新登录',
|
||||
loginTitle: '开始使用', loginDescription: '登录 DeepSeek 账号,或添加 API Key,即可开始使用。你的项目和文件保存在本地。',
|
||||
browserTitle: '等待登录', browserPrompt: '没有自动打开新页面?', copyLink: '复制登录链接', copiedLink: '链接已复制', copyFailed: '复制失败',
|
||||
@@ -71,3 +71,63 @@ export const zh: Record<AccountKey, string> = {
|
||||
quotaTopUp: '去充值',
|
||||
bonusNoticeTitle: '赠金已到账',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
...onboardingJa,
|
||||
'modelSignInRequired': 'モデルを利用できません。ログインしてから再試行してください。',
|
||||
'sessionExpired': 'アカウントからログアウトしました。もう一度ログインしてください。',
|
||||
'close': '閉じる',
|
||||
'addApiKey': 'API Key を追加',
|
||||
'retry': '再ログイン',
|
||||
'loginTitle': 'はじめに',
|
||||
'loginDescription': 'DeepSeek アカウントにログインするか、API Key を追加して開始します。プロジェクトとファイルはローカルに保存されます。',
|
||||
'browserTitle': 'ログインを待機中',
|
||||
'browserPrompt': 'ページが自動で開きませんでしたか? ',
|
||||
'copyLink': 'ログインリンクをコピー',
|
||||
'copiedLink': 'リンクをコピーしました',
|
||||
'copyFailed': 'コピーに失敗しました',
|
||||
'browserDescription': 'をコピーし、ブラウザで開いてログインを完了してください。',
|
||||
'timeoutTitle': 'ログインがタイムアウトしました',
|
||||
'timeoutDescription': '続行するには再ログインしてください。',
|
||||
'failureTitle': 'ログインできませんでした',
|
||||
'platformFailed': '操作を完了できませんでした。再試行してください。',
|
||||
'platformRetry': '再試行',
|
||||
'loading': '読み込み中…',
|
||||
'backToHarness': 'DeepSeek Harness に戻る',
|
||||
'settings': '設定',
|
||||
'contactUs': 'フィードバック',
|
||||
'menu': 'アカウントメニュー',
|
||||
'nav': 'アカウント',
|
||||
'signedIn': 'DeepSeek にログイン済み',
|
||||
'signedOut': '未ログイン',
|
||||
'signIn': 'ログイン',
|
||||
'signOut': 'ログアウト',
|
||||
'signOutUnknownDescription': '実行中のタスクを確認できませんでした。ログアウトすると、このアカウントを使用しているタスクが中断される可能性があります。今すぐログアウトしますか?',
|
||||
'signOutDescription': 'ログアウトしてもデータは削除されません。このアカウントに再度ログインできます。',
|
||||
'signOutRunningDescription': '現在実行中のタスクがあります。ログアウトすると中断されます。今すぐログアウトしますか?',
|
||||
'cancel': 'キャンセル',
|
||||
'open': 'ブラウザで開く',
|
||||
'initializing': 'ログインを開始しています…',
|
||||
'waiting': 'ブラウザで続行してください',
|
||||
'completing': 'ログインを完了しています…',
|
||||
'expired': 'ログインの有効期限が切れました。もう一度お試しください。',
|
||||
'failed': '操作を完了できませんでした。もう一度お試しください。',
|
||||
'noResponse': '問題が発生しました。ネットワーク接続を確認して、もう一度お試しください。',
|
||||
'settingsSignedOutTitle': 'DeepSeek Harness にログインしていません',
|
||||
'settingsSignedOutDescription': 'DeepSeek Harness にログインして専用の API Key を取得してください',
|
||||
'signInDescription': 'DeepSeek アカウントで始めましょう。',
|
||||
'profileUnavailable': 'アカウント情報はまだ利用できません。',
|
||||
'balance': 'チャージ残高',
|
||||
'bonusBalance': '付与残高',
|
||||
'balanceUnavailable': 'Platform で表示',
|
||||
'balanceSignedOut': 'ログインして表示',
|
||||
'accountInfo': 'アカウント情報の詳細',
|
||||
'more': 'その他',
|
||||
'usage': '利用状況を表示',
|
||||
'topUp': 'チャージ',
|
||||
'quotaTitle': '残高がありません',
|
||||
'quotaDescription': '残高がない場合、DeepSeek Harness はこのアカウントで新しいタスクを開始できません。チャージしますか?後から設定 → アカウントでチャージすることもできます。',
|
||||
'quotaTopUp': 'チャージ',
|
||||
'bonusNoticeTitle': 'ボーナスが付与されました',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -1,5 +1,5 @@
|
||||
/** Chinese first-run copy from the onboarding requirements. */
|
||||
export const onboardingCopy = {
|
||||
export const onboardingZh = {
|
||||
onboardingArtworkLocale: 'zh',
|
||||
onboardingWelcome: '欢迎使用',
|
||||
onboardingBrand: 'DeepSeek Harness',
|
||||
@@ -42,7 +42,7 @@ export const onboardingCopy = {
|
||||
} as const
|
||||
|
||||
/** English first-run copy with the same keys as the Chinese dictionary. */
|
||||
export const onboardingEnglishCopy: Record<keyof typeof onboardingCopy, string> = {
|
||||
export const onboardingEn: Record<keyof typeof onboardingZh, string> = {
|
||||
onboardingArtworkLocale: 'en',
|
||||
onboardingWelcome: 'Welcome to',
|
||||
onboardingBrand: 'DeepSeek Harness',
|
||||
@@ -83,3 +83,46 @@ export const onboardingEnglishCopy: Record<keyof typeof onboardingCopy, string>
|
||||
onboardingRetry: 'Retry',
|
||||
onboardingLoading: 'Loading settings…',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const onboardingJa = {
|
||||
'onboardingArtworkLocale': 'en',
|
||||
'onboardingWelcome': 'ようこそ',
|
||||
'onboardingBrand': 'DeepSeek Harness',
|
||||
'onboardingIntroduction': 'DeepSeek Harness はローカルフォルダで動作し、ツールを使ってお使いのコンピューター上のファイルを読み書きします。情報の調査や整理、ドキュメントやスプレッドシートの作成、コードの作成、問題のトラブルシューティングなどに役立ちます。',
|
||||
'onboardingStart': '始める',
|
||||
'onboardingCredit': 'クレジットを追加',
|
||||
'onboardingCreditDescription': 'DeepSeek Harness は、モデルとツールが使用したトークンに基づいてクレジットを消費します。タスクを中断なく実行し続けるために、事前にクレジットを追加してください。残高はエージェントがタスクを実行している間のみ使用されます。',
|
||||
'onboardingTopUp': 'クレジットを追加',
|
||||
'onboardingLater': '次へ',
|
||||
'onboardingFundedTopUp': 'クレジットを追加',
|
||||
'onboardingPurposePrefix': '',
|
||||
'onboardingPurposeSuffix': 'にどのような作業を任せたいですか?',
|
||||
'onboardingPurposeDescription': 'お仕事のスタイルに合わせてインターフェースとツールを調整します。',
|
||||
'onboardingOffice': 'オフィス・クリエイティブ作業',
|
||||
'onboardingOfficeDescription': 'ドキュメントの編集、データの整理、プレゼンテーションの作成など',
|
||||
'onboardingDevelopment': 'コーディング・開発',
|
||||
'onboardingDevelopmentDescription': 'コードの編集、デバッグ、コマンドの実行、プロジェクトファイルの管理など',
|
||||
'onboardingContinue': '続ける',
|
||||
'onboardingProcess': 'どの程度の詳細を表示しますか?',
|
||||
'onboardingProcessDescription': 'これは進捗の表示方法を変えるだけで、DeepSeek Harness の機能には影響しません。',
|
||||
'onboardingCompact': '結果のみ',
|
||||
'onboardingCompactDescription': 'シンプルでクリーンなインターフェースで結果だけを表示します',
|
||||
'onboardingStandard': '重要な詳細',
|
||||
'onboardingStandardDescription': '結果を中心に、重要なステップとアクションのみを表示します',
|
||||
'onboardingDetailed': 'すべての詳細',
|
||||
'onboardingDetailedDescription': 'デバッグやトラブルシューティングをしやすいよう、すべてのプロセスを表示します',
|
||||
'onboardingEnter': 'アプリを開く',
|
||||
'onboardingBack': '戻る',
|
||||
'onboardingSkip': 'スキップ',
|
||||
'onboardingSkipTitle': 'セットアップをスキップしますか?',
|
||||
'onboardingSkipDescription': '進捗、パフォーマンス、利用状況の表示方法や Coding Tools の有効化は、設定 → 一般でいつでも変更できます。',
|
||||
'onboardingKeepSetting': 'セットアップを続ける',
|
||||
'onboardingNoCreditTitle': 'クレジットの追加をスキップしますか?',
|
||||
'onboardingNoCreditDescription': 'クレジットがないと DeepSeek Harness は新しいタスクを開始できません。後から設定 → アカウントで追加できます。',
|
||||
'onboardingUnderstood': '了解',
|
||||
'onboardingGoTopUp': 'クレジットを追加',
|
||||
'onboardingSaveFailed': '設定を保存できませんでした。もう一度お試しください。',
|
||||
'onboardingRetry': '再試行',
|
||||
'onboardingLoading': '設定を読み込んでいます…',
|
||||
} satisfies Record<keyof typeof onboardingEn, string>
|
||||
@@ -16,7 +16,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
||||
import { AgentLoopCard } from './AgentLoopCard.tsx'
|
||||
import { AGENT_LOOP_NS, AgentLoopCardController } from './agent-loop-card-controller.ts'
|
||||
import { en, zh, type AgentLoopSettingsLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type AgentLoopSettingsLocaleKey } from './locales.ts'
|
||||
|
||||
export type { AgentLoopCardProps } from './AgentLoopCard.tsx'
|
||||
export type { AgentLoopCardFace, AgentLoopCardState, AgentLoopSettings } from './agent-loop-card-controller.ts'
|
||||
@@ -41,7 +41,7 @@ export const inject = ['slots', 'locale', 'configForms']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-agent-loop: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-agent-loop: dictionaries')
|
||||
const card = new AgentLoopCardController(ctx.configForms.get(AGENT_LOOP_NS))
|
||||
ctx.effect(() => () => { card.dispose() }, 'ui-settings-agent-loop: form subscription')
|
||||
ctx.effect(() => ctx.configForms.whileServed([AGENT_LOOP_NS], () => ctx.slots.inject('plugins.item', () => ctx.slots.register({
|
||||
|
||||
@@ -48,3 +48,19 @@ export const zh: Record<AgentLoopSettingsLocaleKey, string> = {
|
||||
export function formLabels(t: (key: AgentLoopSettingsLocaleKey) => string): SettingsFormLabels {
|
||||
return { unavailable: t('unavailable'), readOnly: t('readOnly'), saveFailed: t('saveFailed'), save: t('save'), saving: t('saving') }
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': 'エージェントループ',
|
||||
'description': 'エージェントのツール呼び出しのディスパッチ方法を制御します。',
|
||||
'maxParallel': '並列ツール呼び出し',
|
||||
'maxParallelHint': '1ステップ内で同時に実行される並列安全呼び出しの上限。',
|
||||
'overridden': '上書き済み',
|
||||
'reset': 'デフォルトにリセット',
|
||||
'readOnly': 'このデプロイメントでは設定は読み取り専用で保存されます。',
|
||||
'unavailable': 'このプラグインは読み込まれていないため、現在設定できません。',
|
||||
'save': '保存',
|
||||
'saving': '保存中…',
|
||||
'saveFailed': 'デプロイがこれらの値を受け付けませんでした。修正のために残されています。',
|
||||
'invalidNumber': '数値を入力するか、空欄のままにするとデフォルトが使用されます。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -42,7 +42,7 @@ import { SettingsDocumentAction } from './SettingsDocumentAction.tsx'
|
||||
import type { SettingsDocumentActionInjected } from './SettingsDocumentAction.tsx'
|
||||
import { SettingsDocumentStore } from './settings-document-store.ts'
|
||||
import { HostReleasePrompt, type HostReleasePromptInjected } from './HostReleasePrompt.tsx'
|
||||
import { en, zh, type SettingsKey } from './locales.ts'
|
||||
import { en, ja, zh, type SettingsKey } from './locales.ts'
|
||||
|
||||
export type {
|
||||
CloseLabelProps, HeaderContentProps, TriggerContentProps,
|
||||
@@ -97,7 +97,7 @@ export function apply(ctx: ClientContext): void {
|
||||
ctx.slots.inject('settings.general.item', () => ctx.slots.register({
|
||||
name: 'settings.general.item', id: 'current-version', order: 100, locale: NS,
|
||||
}, CurrentVersionRow))
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-general: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-general: dictionaries')
|
||||
const connection = ctx.get('connection') as ConnectionHandle
|
||||
const carrier = (globalThis as typeof globalThis & { dshDesktop?: { protocolVersion: number; updates?: DesktopUpdateBridge } }).dshDesktop
|
||||
const desktopUpdate = new DesktopUpdateSource(carrier?.protocolVersion === 1 ? carrier.updates : undefined)
|
||||
|
||||
@@ -94,3 +94,49 @@ export const en = {
|
||||
'hostRelease.reload': 'Reload to load the new version',
|
||||
'hostRelease.later': 'Not now',
|
||||
} satisfies Record<SettingsKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'trigger': '設定',
|
||||
'shortcut.open': '設定を開く',
|
||||
'desktop.update.available': '更新',
|
||||
'desktop.update.checking': '更新を確認中…',
|
||||
'desktop.update.progress': '{percent}%',
|
||||
'desktop.update.verifying': '更新ファイルを検証中…',
|
||||
'desktop.update.installing': '再起動の準備中…',
|
||||
'desktop.update.ready': 'インストールして再起動',
|
||||
'desktop.update.retry': '更新を再試行',
|
||||
'desktop.update.versionDetail': '{label}: {version}',
|
||||
'desktop.update.downloadDetail': '更新をダウンロード中: {percent}%\n対象バージョン: {version}',
|
||||
'desktop.update.checkFailed': '更新を確認できませんでした。しばらくしてから再試行してください。',
|
||||
'desktop.update.downloadFailed': '更新をダウンロードできませんでした。再試行してください。',
|
||||
'desktop.update.installFailed': '更新をインストールできませんでした。しばらくしてから再試行してください。',
|
||||
'desktop.update.checkNetworkFailed': '更新を確認できませんでした。接続を確認して再試行してください。',
|
||||
'desktop.update.downloadNetworkFailed': '更新をダウンロードできませんでした。接続を確認して再試行してください。',
|
||||
'desktop.update.installNetworkFailed': '更新をインストールできませんでした。接続を確認して再試行してください。',
|
||||
'desktop.update.fullscreenTimeout': '全画面表示を終了できませんでした。全画面表示を終了してから更新を再試行してください。',
|
||||
'desktop.update.stopFailed': 'タスクを安全に停止できませんでした。更新はインストールされていません。しばらくしてから再試行してください。',
|
||||
'desktop.update.tasksChanged': '新しいタスクが開始されました。タスクを停止して更新するには、もう一度確認してください。',
|
||||
'desktop.update.tasksUnavailable': 'タスクの状態を取得できません。ワークスペースの準備ができてから更新を再試行してください。',
|
||||
'title': '設定',
|
||||
'close': '閉じる',
|
||||
'openDocument': '設定ファイルを開く',
|
||||
'openDocument.error': '設定ファイルを開けませんでした',
|
||||
'general.nav': '一般',
|
||||
'general.currentVersion': '現在のバージョン: {version}',
|
||||
'developerTools.title': 'コーディングビューを表示',
|
||||
'developerTools.error': '保存できませんでした。再試行してください。',
|
||||
'developerTools.description': '軌跡、コード差分、すべてのエージェントプリセットを表示します',
|
||||
'connection.error': '切断済み',
|
||||
'connection.connecting': '再接続中',
|
||||
'connection.connected': '接続済み',
|
||||
'connection.reconnect': '切断されました。今すぐ再接続',
|
||||
'connection.restart': '再接続中、今すぐ再接続',
|
||||
'hostRelease.title': 'サービスのバージョンが変更されました',
|
||||
'hostRelease.description': 'サービスのバージョンが変更されました。このページのライブ更新は停止しています。再読み込みして新しいバージョンを読み込んでください。',
|
||||
'hostRelease.service': 'サービス',
|
||||
'hostRelease.page': 'ページ',
|
||||
'hostRelease.unknown': '不明',
|
||||
'hostRelease.reload': '再読み込みして新しいバージョンを読み込む',
|
||||
'hostRelease.later': '後で',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -26,7 +26,7 @@ import { WelcomeNoticeStore } from './welcome-store.ts'
|
||||
import { ModelsSettingsStore } from './store.ts'
|
||||
import { createModelsOperations } from './operations.ts'
|
||||
import { createSettingsSchemaOperations } from './schema-operations.ts'
|
||||
import { en, zh, type ModelsKey } from './locales.ts'
|
||||
import { en, ja, zh, type ModelsKey } from './locales.ts'
|
||||
import { WELCOME_NOTICE_SETTINGS_NAMESPACE } from '../onboarding-copy.ts'
|
||||
import { Config, ONBOARDING_CONFIG_GLOBAL } from '../onboarding-config.ts'
|
||||
|
||||
@@ -80,7 +80,7 @@ export function apply(ctx: ClientContext): void {
|
||||
const payload = page[ONBOARDING_CONFIG_GLOBAL]
|
||||
const configured = Config(payload === undefined ? {} : payload)
|
||||
const credentialOnboarding = configured.credentialOnboarding && !('dshDesktop' in globalThis)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-models: copy dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-models: copy dictionaries')
|
||||
|
||||
const schema = createSettingsSchemaOperations(ctx.settingsSchema)
|
||||
// Bound once here, where the Remote namespaces are declared in this plugin's
|
||||
|
||||
@@ -280,3 +280,142 @@ export const zh: { [Key in keyof typeof en]: string } = {
|
||||
onboardingSaving: '保存中…',
|
||||
keyRequired: '请输入 API 密钥后继续。',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'nav': 'モデル',
|
||||
'deepSeekAccount': 'DeepSeekアカウント',
|
||||
'title': 'モデル',
|
||||
'intro': '以下のプロバイダーのモデルを使用するには、APIキーを入力してください。',
|
||||
'edit': '編集',
|
||||
'editProvider': '{provider}を編集',
|
||||
'remove': '削除',
|
||||
'removeProvider': '{provider}を削除',
|
||||
'deleteTitle': '{provider}を削除しますか?',
|
||||
'deleteDescription': '{provider}を削除すると、その設定が削除されます。使用している認証情報は別の場所で管理されているため、保持されます。',
|
||||
'deleteDescriptionWithCredential': '{provider}を削除すると、その設定と保存済みのAPIキーが削除されます。',
|
||||
'deleteConfirm': '{provider}を削除',
|
||||
'deleting': '{provider}を削除中…',
|
||||
'add': 'モデルプロバイダーを追加',
|
||||
'addMode': '追加方法',
|
||||
'addCatalog': 'サードパーティのモデルプロバイダー',
|
||||
'addCustom': 'カスタムモデルAPI',
|
||||
'addCatalogHint': '組み込みカタログからOpenAI、Anthropic、Kimiなどのプロバイダーを選択し、APIキーを入力してください。',
|
||||
'addCustomHint': 'ベースURL、プロトコル、モデルを指定して、リレー、セルフホストサーバー、その他のOpenAI/Anthropic互換エンドポイントに接続します。',
|
||||
'addCatalogExhausted': 'カタログ内のすべてのプロバイダーが設定済みです。',
|
||||
'addCustomUnavailable': '宣言できるAPIプロトコルがありません。',
|
||||
'provider': 'プロバイダー',
|
||||
'close': '閉じる',
|
||||
'cancel': 'キャンセル',
|
||||
'apply': '適用',
|
||||
'applying': '適用中…',
|
||||
'savedProvider': '{provider}を保存しました。',
|
||||
'credentialConfigured': 'APIキー設定済み',
|
||||
'credentialMissing': 'APIキー未設定',
|
||||
'accountSignedIn': 'ログイン済み',
|
||||
'readOnly': 'このデプロイでは設定ドキュメントは読み取り専用です。',
|
||||
'loadFailed': 'プロバイダーディレクトリの読み込みに失敗しました',
|
||||
'conflict': 'このカードを開いている間に、他のユーザーが設定を変更しました。閉じて開き直し、最新の値で編集してください。',
|
||||
'retry': '再試行',
|
||||
'keyInput': 'APIキー',
|
||||
'keyReveal': 'シークレットを表示',
|
||||
'keyHide': 'シークレットを非表示',
|
||||
'keyPlaceholder': 'APIキーを入力',
|
||||
'keyPlaceholderFallback': '空欄のままにすると環境変数を認証に使用します ({hint})',
|
||||
'keyPlaceholderSaved': '空欄のままにすると保存済みの認証情報を使用します',
|
||||
'keyFallbackMissing': '環境変数が検出されません。APIキーを入力するか、環境変数に{hint}を設定してください。',
|
||||
'keyFallbackDetected': '環境変数を検出しました。({hint})',
|
||||
'keyFallbackDetectedHidden': '環境変数を検出しました。',
|
||||
'keyFallbackUnchecked': '環境変数の認証情報の状態は確認されていません。',
|
||||
'keyHelp': '保存済みの認証情報が優先されます。以下に示す環境変数のフォールバック(この順): {env}。参照元は起動環境、$cwd/.env、$DSH_HOME/.envの順です。',
|
||||
'authHelp': '認証の詳細',
|
||||
'customized': 'カスタマイズされた設定',
|
||||
'baseUrl': 'ベースURL',
|
||||
'baseUrlDefault': 'デフォルトエンドポイントなし',
|
||||
'baseUrlEnvironment': 'プロバイダーのエンドポイント設定: {env}',
|
||||
'baseUrlConfigured': 'プロバイダー設定のエンドポイント',
|
||||
'baseUrlPlaceholder': '{url}',
|
||||
'deepSeekBaseUrl': 'https://api.deepseek.com/anthropic (デフォルトはDEEPSEEK_BASE_URLを使用)',
|
||||
'deepSeekEndpointHint': 'Anthropic Messagesと互換性のあるAPIエンドポイントを使用してください。',
|
||||
'models': 'モデル',
|
||||
'modelsInherited': 'アダプターのデフォルトを使用',
|
||||
'modelsCustomized': 'カスタマイズされたモデルカタログ',
|
||||
'resetModels': 'デフォルトに戻す',
|
||||
'model': 'モデル',
|
||||
'modelId': 'モデルID',
|
||||
'modelName': '表示名',
|
||||
'modelNamePlaceholder': '空欄の場合はモデルIDを使用',
|
||||
'contextWindow': 'コンテキストウィンドウ',
|
||||
'contextWindowPlaceholder': 'プロバイダーのデフォルトを使用',
|
||||
'maxTokens': '最大出力トークン数',
|
||||
'maxTokensPlaceholder': 'プロバイダーのデフォルトを使用',
|
||||
'modelAdvanced': 'モデルオプション',
|
||||
'modelInputTypes': '入力タイプ',
|
||||
'modelInputText': 'テキスト',
|
||||
'modelInputImage': '画像',
|
||||
'modelReasoning': '推論レベル',
|
||||
'modelReasoningLevel': 'レベル',
|
||||
'modelReasoningValue': 'API値',
|
||||
'modelReasoningHelp': '推論レベルについて',
|
||||
'modelReasoningHelpText': 'API値はリクエストの推論強度フィールド(OpenAIのreasoning_effortなど)で送信されます。デフォルトはレベル名です。モデルプロバイダーのAPIドキュメントで別の値が指定されている場合はそれに従ってください。オフは推論を無効にするため値は不要です。設定に既に記述されている値は保持され、送信されます。',
|
||||
'modelReasoningNeedsLevel': 'オフ以外のレベルを少なくとも1つ追加するか、推論レベルを無効にしてください。',
|
||||
'modelReasoningValueRequired': 'オフ以外の各レベルにはAPI値が必要で、空の値は使用できません。',
|
||||
'modelReasoningAdd': 'レベルを追加',
|
||||
'modelReasoningRemove': 'レベルを削除',
|
||||
'modelReasoningOffValuePlaceholder': '推論を無効にします',
|
||||
'addModel': 'モデルを追加',
|
||||
'removeModel': 'モデルを削除',
|
||||
'modelsEmpty': 'セレクターにモデルが表示されません。一覧にないIDも直接送信できます。',
|
||||
'keyBlank': 'APIキーを入力してください。空欄のままにすると保存済みのキーが保持されます。',
|
||||
'keyBlankNew': 'APIキーを入力してください。このプロバイダーが別の方法で認証する場合は空欄のままにしてください。',
|
||||
'keyIllegalCharacters': 'このAPIキーは有効な形式ではありません。確認してください。',
|
||||
'modelIdRequired': 'モデルIDは必須です。',
|
||||
'modelIdDuplicate': 'モデルIDは一意である必要があります。',
|
||||
'modelNameInvalid': '表示名は空にできません。',
|
||||
'modelContextInvalid': 'コンテキストウィンドウは正の数値(131072、256K、1Mなど)で指定してください。',
|
||||
'modelMaxTokensInvalid': '最大出力トークン数は正の数値(8192、64K、1Mなど)で指定してください。',
|
||||
'advancedHint': 'その他のフィールドはcordis.patch.ymlにあります。そのセクションを直接編集してください。',
|
||||
'modelCapacityInvalid': '容量は数値で指定し、末尾にKまたはMを付けられます。',
|
||||
'modelDuplicate': '各モデルIDは1回のみ指定できます。',
|
||||
'fetchModels': '利用可能なモデルを取得',
|
||||
'fetching': 'プロバイダーに問い合わせ中…',
|
||||
'fetchNeedsBaseUrl': '先にベースURLを入力してから取得してください。',
|
||||
'fetchEmpty': 'プロバイダーはモデルを一覧表示しませんでした。手動で追加してください。',
|
||||
'fetchTitle': 'モデルを選択',
|
||||
'fetchDescription': 'このプロバイダーで利用可能なモデルです。チェックしたモデルが一覧になります。',
|
||||
'fetchSearch': 'モデルを検索',
|
||||
'fetchNoMatches': '一致するモデルがありません。',
|
||||
'fetchSelectAll': 'すべて選択',
|
||||
'fetchDeselectAll': 'すべて解除',
|
||||
'fetchAdopt': '一覧を更新',
|
||||
'fetchKeepOne': '少なくとも1つのモデルをチェックしたままにしてください。',
|
||||
'customTag': 'カスタム',
|
||||
'customRoute': 'プロバイダーID',
|
||||
'customRouteHint': 'リクエスト時および認証情報名としてこのプロバイダーを一意に識別する、小文字で始まる小文字の識別子です。',
|
||||
'customRouteInvalid': '小文字で始め、その後は小文字・数字・ハイフンを使用してください。',
|
||||
'customRouteTaken': 'このIDは既に別のプロバイダーで使用されています。',
|
||||
'customDisplayName': '表示名',
|
||||
'customApi': 'APIプロトコル',
|
||||
'customApiUnset': '未選択',
|
||||
'protocolOpenAiCompletions': 'OpenAI Chat Completions',
|
||||
'protocolOpenAiResponses': 'OpenAI Responses',
|
||||
'protocolAnthropicMessages': 'Anthropic Messages',
|
||||
'customNeedsBaseUrl': 'カスタムプロバイダーにはベースURLが必要です。',
|
||||
'customBaseUrlInvalid': '有効なHTTPまたはHTTPSのURLを入力してください。',
|
||||
'customNeedsModels': 'カスタムプロバイダーには少なくとも1つのモデルが必要です。',
|
||||
'customBaseUrlPlaceholder': 'https://gateway.example/v1',
|
||||
'customAnthropicBaseUrlPlaceholder': 'https://gateway.example',
|
||||
'settingsPathUnresolvable': '解決できない設定パス',
|
||||
'create': 'プロバイダーを作成',
|
||||
'creating': '作成中…',
|
||||
'welcomeTitle': 'プレビューに関するお知らせ',
|
||||
'welcomeBody': 'DeepSeek Harness 0.2はまだプレビュー版であり、多くの領域で継続的な改善と洗練が必要です。開発者とユーザーの皆さまからのフィードバックとご提案を歓迎します。新しいデスクトップアプリは幅広いユーザーを対象としていますが、開発者向けの高度な機能は設定で有効にできます。DeepSeek Harnessの製品機能とプラグインAPIは今後も急速な反復と進化を続け、徐々に安定していく見込みです。\n\nオープンソースで再利用可能かつ組み合わせ可能なインフラストラクチャを基盤に、世界中のユーザーと開発者と共に知能の限界を探求していくことを楽しみにしています。皆さまがDeepSeek Harnessでアイデアを形にし、コミュニティに参加してプラグインエコシステムを豊かにしてくださることを歓迎します。',
|
||||
'welcomeContinue': '続ける',
|
||||
'welcomeError': '確認を保存できませんでした。もう一度お試しください。',
|
||||
'onboardingTitle': 'APIキーを追加して始める',
|
||||
'onboardingDescription': '公式のDeepSeekプロバイダーを設定して開発を始めましょう。',
|
||||
'onboardingLater': '後で設定',
|
||||
'onboardingSave': '保存して続ける',
|
||||
'onboardingSaving': '保存中…',
|
||||
'keyRequired': '続けるにはAPIキーを入力してください。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -11,7 +11,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-agent-preset/client'
|
||||
// Inline-safe shared fold: shipped ids map to dictionary keys in one home.
|
||||
import { presetDisplayText } from '@deepseek-ai/dsh-agent-preset-registry/display'
|
||||
import { PluginInventorySettingsTab, type PluginInventorySettingsTabInjected } from './PluginInventorySettingsTab.tsx'
|
||||
import { en, zh, type PluginInventoryLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type PluginInventoryLocaleKey } from './locales.ts'
|
||||
|
||||
export type { PluginInventorySettingsTabInjected, PluginInventorySettingsTabProps } from './PluginInventorySettingsTab.tsx'
|
||||
export type { PluginInventoryLocaleKey } from './locales.ts'
|
||||
@@ -31,7 +31,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.pluginInventory', 'm
|
||||
|
||||
/** Contribute the lazy inventory tab to the Plugins settings section. */
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-plugin-inventory: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-plugin-inventory: dictionaries')
|
||||
|
||||
const t = ctx.locale.bind(NS)
|
||||
const list: PluginInventorySettingsTabInjected['list'] = async () => {
|
||||
|
||||
@@ -84,3 +84,44 @@ export const en = {
|
||||
failed: 'Failed to start',
|
||||
unloading: 'Unloading',
|
||||
} satisfies Record<PluginInventoryLocaleKey, string>
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'tab': 'プラグイン一覧',
|
||||
'loading': 'プラグインを読み込み中…',
|
||||
'clientSyncing': 'このページでプラグインを同期中…',
|
||||
'clientSyncFailed': '一部のプラグインをこのページで同期できませんでした。ホスト側の有効化状態は変更されていません。',
|
||||
'clientSyncRetry': 'このページを再試行',
|
||||
'error': 'プラグインは一時的に利用できません。',
|
||||
'metadataError': 'パッケージメタデータのエラー: {error}',
|
||||
'retry': '再試行',
|
||||
'search': 'プラグインを検索',
|
||||
'empty': '利用可能なプラグインはありません。',
|
||||
'emptySearch': '一致するプラグインはありません。',
|
||||
'presetTitle': 'セッションプラグイン',
|
||||
'presetSubtitle': 'エージェントプリセットによりセッションごとに構成されます',
|
||||
'switcherLabel': '確認するエージェントプリセットを選択',
|
||||
'presetOptionDefault': '{name} (デフォルト)',
|
||||
'presetOptionBroken': '{name} (読み込み失敗)',
|
||||
'globalTitle': 'グローバルプラグイン',
|
||||
'globalSubtitle': 'システムとすべてのセッションで共有',
|
||||
'presetProvidedDetail': 'グローバルでは無効。エージェントプリセットがセッションごとに提供します',
|
||||
'enabledIn': '有効化先',
|
||||
'matchesInOtherPresets': '他のプリセットにさらに{count}件一致: ',
|
||||
'enabledTag': '有効',
|
||||
'disabledTag': '無効',
|
||||
'conditionalTag': '条件付き',
|
||||
'presetEnabledTag': 'プリセット経由',
|
||||
'failedTag': '失敗',
|
||||
'moduleLabel': 'モジュール',
|
||||
'fromPreset': '提供元',
|
||||
'condition': '無効になる条件',
|
||||
'configuration': '構成',
|
||||
'runtime': 'ステータス',
|
||||
'unobserved': '停止中',
|
||||
'pending': '依存関係の待機中',
|
||||
'loadingPhase': '読み込み中',
|
||||
'active': '実行中',
|
||||
'failed': '起動に失敗',
|
||||
'unloading': 'アンロード中',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -18,7 +18,7 @@ import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
||||
import { resolveSlotLabel } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { PluginsSettingsSection } from './PluginsSettingsSection.tsx'
|
||||
import type { PluginsSettingsSectionInjected, PluginsSettingsTabEntry } from './PluginsSettingsSection.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
|
||||
export type { PluginsSettingsSectionInjected, PluginsSettingsSectionProps } from './PluginsSettingsSection.tsx'
|
||||
|
||||
@@ -34,7 +34,7 @@ export const inject = ['slots', 'locale']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-plugins: section dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-plugins: section dictionaries')
|
||||
|
||||
let tabsVersion = -1
|
||||
let tabsRevision = -1
|
||||
|
||||
@@ -20,3 +20,12 @@ export const zh: Record<PluginsSettingsLocaleKey, string> = {
|
||||
tabs: '插件视图',
|
||||
empty: '本部署没有开放任何插件视图。',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'nav': '組み込みプラグイン',
|
||||
'title': '組み込みプラグイン',
|
||||
'intro': 'このデプロイメントに含まれるプラグインを確認します。',
|
||||
'tabs': 'プラグインビュー',
|
||||
'empty': 'このデプロイメントにはプラグインビューがありません。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -6,7 +6,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-layout/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import { UploadPreference, type UploadSettings } from './upload-preference.ts'
|
||||
import { UploadRow, UploadToast, type UploadInjected } from './UploadRow.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
@@ -25,7 +25,7 @@ export const inject = ['slots', 'locale', 'configForms']
|
||||
export function apply(ctx: Context): void {
|
||||
const locale = 'settings.sessionLog'
|
||||
const namespace = 'session-log-deepseek'
|
||||
ctx.effect(() => ctx.locale.register(locale, { en, zh }))
|
||||
ctx.effect(() => ctx.locale.register(locale, { en, zh, ja }))
|
||||
const form = ctx.configForms.get<UploadSettings>(namespace)
|
||||
const preference = new UploadPreference(form)
|
||||
const face = (): UploadInjected => ({
|
||||
|
||||
@@ -13,3 +13,11 @@ export const zh: Record<keyof typeof en, string> = {
|
||||
saved: '设置已保存',
|
||||
failed: '无法保存设置',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': '公式モデル API 使用時にセッションログをアップロード',
|
||||
'description': 'DeepSeek のモデルと製品の改善にご協力ください。',
|
||||
'saved': '設定を保存しました',
|
||||
'failed': '設定を保存できませんでした',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -17,7 +17,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
||||
import { ShellCard } from './ShellCard.tsx'
|
||||
import { BASH_NS, PWSH_NS, ShellCardController } from './shell-card-controller.ts'
|
||||
import { en, zh, type ShellSettingsLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type ShellSettingsLocaleKey } from './locales.ts'
|
||||
|
||||
export type { ShellCardProps } from './ShellCard.tsx'
|
||||
export type { ShellCardFace, ShellCardState, ShellSettings } from './shell-card-controller.ts'
|
||||
@@ -42,7 +42,7 @@ export const inject = ['slots', 'locale', 'configForms']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-shell: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-shell: dictionaries')
|
||||
// The base bundle composes exactly one shell executor per platform; the page binds the served one.
|
||||
const bash = new ShellCardController(ctx.configForms.get(BASH_NS))
|
||||
const pwsh = new ShellCardController(ctx.configForms.get(PWSH_NS))
|
||||
|
||||
@@ -53,3 +53,21 @@ export const zh: Record<ShellSettingsLocaleKey, string> = {
|
||||
export function formLabels(t: (key: ShellSettingsLocaleKey) => string): SettingsFormLabels {
|
||||
return { unavailable: t('unavailable'), readOnly: t('readOnly'), saveFailed: t('saveFailed'), save: t('save'), saving: t('saving') }
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': 'シェル',
|
||||
'description': '各コマンドの実行時間と出力量を制限します。',
|
||||
'timeoutMs': 'コマンドのタイムアウト (ms)',
|
||||
'timeoutMsHint': '1つのコマンドが強制終了されるまでに実行できる時間。',
|
||||
'maxOutputBytes': 'ストリームごとの出力上限 (バイト)',
|
||||
'maxOutputBytesHint': 'この上限を超えた出力は失われず、一時ファイルに書き出されます。',
|
||||
'overridden': '上書き済み',
|
||||
'reset': 'デフォルトに戻す',
|
||||
'readOnly': 'このデプロイでは設定が読み取り専用で保存されます。',
|
||||
'unavailable': 'このプラグインは読み込まれていないため、現在設定できません。',
|
||||
'save': '保存',
|
||||
'saving': '保存中…',
|
||||
'saveFailed': 'デプロイがこれらの値を受け付けませんでした。修正のため残されています。',
|
||||
'invalidNumber': '数値を入力するか、空欄のままにするとデフォルトが使用されます。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -23,7 +23,7 @@ import { SubagentLimitsCardController } from './subagent-limits-card-controller.
|
||||
import {
|
||||
SUBAGENT_MODEL_SELECTION_NS, SubagentModelSelectionCardController,
|
||||
} from './subagent-model-selection-card-controller.ts'
|
||||
import { en, zh, type SubagentSettingsLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type SubagentSettingsLocaleKey } from './locales.ts'
|
||||
|
||||
export type { SubagentCardProps } from './SubagentCard.tsx'
|
||||
export type { SubagentCardFace } from './subagent-card-controller.ts'
|
||||
@@ -58,7 +58,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.session', 'configFor
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-subagent: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-subagent: dictionaries')
|
||||
const limits = new SubagentLimitsCardController(ctx.configForms.get(SUBAGENT_NS))
|
||||
ctx.effect(() => () => { limits.dispose() }, 'ui-settings-subagent: limits form subscription')
|
||||
const models = new SubagentModelSelectionCardController(
|
||||
|
||||
@@ -107,3 +107,42 @@ export const zh: Record<SubagentSettingsLocaleKey, string> = {
|
||||
export function formLabels(t: (key: SubagentSettingsLocaleKey) => string): SettingsFormLabels {
|
||||
return { unavailable: t('unavailable'), readOnly: t('readOnly'), saveFailed: t('saveFailed'), save: t('save'), saving: t('saving') }
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'overridden': '上書き済み',
|
||||
'reset': 'デフォルトに戻す',
|
||||
'readOnly': 'このデプロイメントでは設定は読み取り専用で保存されます。',
|
||||
'unavailable': 'このプラグインは読み込まれていないため、現在は設定できません。',
|
||||
'save': '保存',
|
||||
'saving': '保存中…',
|
||||
'saveFailed': 'デプロイメントがこれらの値を受け付けませんでした。修正のため残されています。',
|
||||
'subagentTitle': 'サブエージェント',
|
||||
'subagentDescription': 'サブエージェントの再帰深度、数、モデルを設定します。',
|
||||
'subagentLimitsTitle': '制限',
|
||||
'subagentMaxDepth': '最大再帰深度',
|
||||
'subagentDepthHelpLabel': '最大再帰深度について',
|
||||
'subagentDepthHelp': 'エージェントが作成できるサブエージェントの階層数を制限します。',
|
||||
'subagentDepthZero': 'サブエージェントを無効化',
|
||||
'subagentDepthOne': 'メインエージェントのみサブエージェントを作成可能',
|
||||
'subagentDepthOverride': 'ツールが独自の最大再帰深度を定義している場合、その設定が優先されます。',
|
||||
'subagentMaxActive': 'サブエージェントの並列数制限',
|
||||
'subagentCapacityHelpLabel': 'サブエージェントの並列数制限について',
|
||||
'subagentCapacityHelp': '同じメインエージェント配下の全再帰階層にわたる、稼働中のサブエージェントの合計数です。メインエージェントは含まれません。上限に達すると新しい開始リクエストは拒否されます。',
|
||||
'subagentDepthInvalid': '0以上の整数を入力してください。',
|
||||
'subagentCapacityInvalid': '1以上の整数を入力してください。',
|
||||
'subagentModelSelectionTitle': 'モデル選択',
|
||||
'subagentModelSelectionToggle': 'エージェントによるサブエージェントのモデル選択を許可',
|
||||
'subagentModelSelectionChoose': '有効にすると、エージェントは以下の許可されたモデルからサブエージェントごとにプロバイダー、モデル、推論強度を選択できます。新しいセッションにのみ適用されます。',
|
||||
'subagentModelSelectionAllowed': 'エージェントが選択可能なモデル',
|
||||
'subagentModelSelectionLoading': 'モデルを読み込み中…',
|
||||
'subagentModelSelectionLoadFailed': 'モデルを読み込めませんでした。',
|
||||
'subagentModelSelectionRetry': '再試行',
|
||||
'subagentModelSelectionPartial': '一部のモデルプロバイダーを読み込めませんでした。保存済みの選択肢は引き続き削除できます。',
|
||||
'subagentModelSelectionUnavailable': '現在利用不可',
|
||||
'subagentModelSelectionUnavailableGroup': '保存済みだが現在利用不可',
|
||||
'subagentModelSelectionEmpty': '現在、モデルを公開しているモデルプロバイダーはありません。',
|
||||
'subagentModelSelectionRequired': '保存する前に少なくとも1つのモデルを選択してください。',
|
||||
'subagentModelSelectionConflict': '設定が別の場所で変更されました。下書きを破棄して再試行してください。',
|
||||
'subagentModelSelectionOff': 'サブエージェントは設定済みのデフォルトを使用するか、親エージェントのモデルを継承します。保存済みのモデル選択は保持されます。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -22,7 +22,7 @@ import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
||||
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
||||
import { WebSearchCard } from './WebSearchCard.tsx'
|
||||
import { WEB_API_NS, WEB_NS, WEB_SEARCH_NS, WebSearchCardController } from './web-search-card-controller.ts'
|
||||
import { en, zh, type WebSearchSettingsLocaleKey } from './locales.ts'
|
||||
import { en, ja, zh, type WebSearchSettingsLocaleKey } from './locales.ts'
|
||||
|
||||
export type { WebSearchCardProps } from './WebSearchCard.tsx'
|
||||
export type {
|
||||
@@ -49,7 +49,7 @@ export const inject = ['slots', 'locale', 'remote', 'remote.credentials', 'confi
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const t = ctx.locale.bind(NS)
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-web-search: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en, ja }), 'ui-settings-web-search: dictionaries')
|
||||
const card = new WebSearchCardController({
|
||||
web: ctx.configForms.get(WEB_NS), messages: ctx.configForms.get(WEB_SEARCH_NS), api: ctx.configForms.get(WEB_API_NS),
|
||||
}, ctx)
|
||||
|
||||
@@ -80,3 +80,34 @@ export const zh: Record<WebSearchSettingsLocaleKey, string> = {
|
||||
export function formLabels(t: (key: WebSearchSettingsLocaleKey) => string): SettingsFormLabels {
|
||||
return { unavailable: t('unavailable'), readOnly: t('readOnly'), saveFailed: t('saveFailed'), save: t('save'), saving: t('saving') }
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'title': 'Web 検索',
|
||||
'description': 'DeepSeek 検索プロバイダーを設定します。',
|
||||
'useSearchApi': '新しい Web Search API を使用する',
|
||||
'useSearchApiHint': 'デフォルトではオフです。検索は DeepSeek Messages 経由で実行されます。新しい Web Search API の機能と料金については、',
|
||||
'searchGuide': '検索と取得のガイド',
|
||||
'searchGuideUrl': 'https://api-docs.deepseek.com/guides/search',
|
||||
'apiKey': 'API キー',
|
||||
'apiKeyHint': '設定ファイルの外に保存されます。現在のキーを維持するには空欄のままにしてください。',
|
||||
'apiKeySet': 'キーが設定されています。',
|
||||
'apiKeyUnset': 'キーが設定されていません。DeepSeek Account のモデルを使用する会話のみ、デフォルトのエンドポイント経由で検索できます。',
|
||||
'baseUrl': 'Messages API ベース URL',
|
||||
'baseUrlHint': 'https://api.deepseek.com/anthropic/v1 など、API プレフィックスを含めてください。プロバイダーは /messages を追加します。デフォルトに戻すには空欄にしてください。',
|
||||
'searchApiBaseUrl': 'Search / Fetch API ベース URL',
|
||||
'searchApiBaseUrlHint': 'https://api.deepseek.com/v1/tools など、API プレフィックスを含めてください。プロバイダーは /search または /fetch を追加します。デフォルトに戻すには空欄にしてください。',
|
||||
'maxUses': 'リクエストあたりの最大検索回数',
|
||||
'maxUsesHint': '1 回の Messages リクエスト内で補助モデルが実行できる検索回数です。Messages API にのみ適用されます。',
|
||||
'count': '検索あたりの結果数',
|
||||
'countHint': 'クエリごとに返される結果数: 1〜50 の整数、デフォルトは 10 です。これは結果数であり、検索呼び出し数ではありません。web_search は自身の結果上限を超えるソースを保持しません。',
|
||||
'overridden': '上書き済み',
|
||||
'reset': 'デフォルトに戻す',
|
||||
'readOnly': 'このデプロイでは設定は読み取り専用で保存されます。',
|
||||
'unavailable': 'このプラグインは読み込まれていないため、現在設定できません。',
|
||||
'save': '保存',
|
||||
'saving': '保存中…',
|
||||
'saveFailed': 'デプロイがこれらの値を受け入れませんでした。修正用に残されています。',
|
||||
'invalidNumber': '数値を入力するか、デフォルトを使用するには空欄にしてください。',
|
||||
'invalidCount': '1〜50 の整数を入力するか、デフォルトを使用するには空欄にしてください。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -8,7 +8,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-layout/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import { createShortcutsStore } from './store.ts'
|
||||
import { ShortcutReference, ShortcutsRow } from './Reference.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
import { fixedCommands } from './fixed.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
@@ -26,7 +26,7 @@ export const inject = ['shortcuts', 'locale', 'slots']
|
||||
* @param ctx - plugin-owned client context.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
ctx.effect(() => ctx.locale.register('shortcuts', { zh, en }), 'shortcuts: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register('shortcuts', { zh, en, ja }), 'shortcuts: dictionaries')
|
||||
const t = ctx.locale.bind('shortcuts')
|
||||
const handle = createShortcutsStore()
|
||||
const instance = handle.create()
|
||||
|
||||
@@ -68,3 +68,63 @@ export const en: Record<keyof typeof zh, string> = {
|
||||
empty: 'No matching shortcuts',
|
||||
move: 'Move menu selection', select: 'Select menu item', dismiss: 'Close menu or top dialog',
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'edit-label': '{command}のショートカットを編集',
|
||||
'record': 'ショートカットを押してください',
|
||||
'record-help': 'キーを離すと保存されます。Tab でアクション間を移動、Esc でキャンセルします。',
|
||||
'web-help': 'ブラウザの組み合わせ: Mod+/、Mod+Shift+,、Mod+Shift+.。Mod は Mac では Command、それ以外では Ctrl です。',
|
||||
'unsupported-key': 'このキーはサポートされていません。',
|
||||
'reserved': 'この組み合わせはシステム操作またはテキスト編集操作に予約されています。',
|
||||
'modifier-required': '組み合わせに Command、Ctrl、または Alt を含めてください。',
|
||||
'too-many-keys': '修飾キー以外のキーは最大 2 つまでです。キーを離して再試行してください。',
|
||||
'macos-web-help': 'Command+/、Command+,、Command+バックスラッシュ、Control+バッククォート、Command+Option+キー、または Command+Shift+キーを使用してください。3 つまたは 4 つの異なる修飾キーを使った組み合わせもサポートされています。ブラウザやシステムのショートカットはページに届かない場合があります。',
|
||||
'windows-web-help': 'Ctrl+/、Ctrl+,、Ctrl+Alt+キー、または Ctrl+Shift+キーを使用してください。3 つまたは 4 つの異なる修飾キーを使った組み合わせもサポートされています。ブラウザやシステムのショートカットはページに届かない場合があります。',
|
||||
'unsupported-browser': 'このブラウザはこの組み合わせにまだ対応していません。',
|
||||
'conflict': '「{commands}」で既に使用されています',
|
||||
'saved': '変更済み',
|
||||
'clear': '削除',
|
||||
'retry-save': '保存を再試行',
|
||||
'reset': 'デフォルトに戻す',
|
||||
'reset-all': 'すべてデフォルトに戻す',
|
||||
'modified-count': '{count} 件がカスタマイズ済み',
|
||||
'reset-title': 'すべてのショートカットをデフォルトに戻しますか?',
|
||||
'reset-description': 'このプラットフォームのショートカットをデフォルトに戻します。変更または削除されたすべてのショートカットが復元されます。他のプラットフォームには影響しません。',
|
||||
'cancel': 'キャンセル',
|
||||
'reset-saved': 'デフォルトのショートカットを復元しました',
|
||||
'close-confirmation': '確認を閉じる',
|
||||
'reset-failed': 'デフォルトを復元できませんでした。ショートカットは変更されていません。再試行してください。',
|
||||
'review': '最新の構成を確認しました',
|
||||
'stale': 'ショートカット構成または利用可能なコマンドが変更されました。保存する前に最新のバインドを確認してください。',
|
||||
'write-failed': '保存できませんでした。以前のショートカットと現在の下書きは保持されています。再試行してください。',
|
||||
'not-ready': 'ショートカットの準備ができていません。もう一度お試しください。',
|
||||
'read': '{location} を読み取れませんでした。アクセス権限を確認してから、{reload}してください。',
|
||||
'invalid': '{location} のショートカット構成が破損しています。この構成をバックアップして修復してから、{reload}してください。',
|
||||
'future': '{location} のショートカット構成は新しいバージョンで作成されています。Harness をアップグレードして再試行してください。',
|
||||
'web-document': 'このサイトの localStorage エントリ dsh.keybindings.v1',
|
||||
'desktop-document': 'userData/keybindings.json',
|
||||
'web-reload': 'ページを再読み込み',
|
||||
'desktop-reload': 'Harness を再起動',
|
||||
'using-defaults': 'デフォルトのバインドが有効です。',
|
||||
'using-accepted': '最後に正常に読み取られたバインドが有効なままです。',
|
||||
'native-failed': 'デスクトップでのキー記録を保護できませんでした。記録を終了して再試行してください。',
|
||||
'global-hint': 'どこからでも開く',
|
||||
'clear-search': '検索をクリア',
|
||||
'title': 'キーボードショートカット',
|
||||
'open': 'キーボードショートカットを開く',
|
||||
'settings': 'キーボードショートカット',
|
||||
'view': 'ショートカットを編集',
|
||||
'description': '利用可能なショートカットと入力アクションを表示・編集',
|
||||
'search': 'ショートカットを検索',
|
||||
'close': 'キーボードショートカットを閉じる',
|
||||
'application': 'アプリケーション',
|
||||
'input': 'メッセージ入力',
|
||||
'menus': 'メニューとダイアログ',
|
||||
'approval': '承認エリア',
|
||||
'unbound': 'ショートカットなし',
|
||||
'empty': '一致するショートカットがありません',
|
||||
'move': 'メニュー選択を移動',
|
||||
'select': 'メニュー項目を選択',
|
||||
'dismiss': 'メニューまたは最上位ダイアログを閉じる',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -16,7 +16,7 @@ import type { DesktopBrowserBridge } from '../types.ts'
|
||||
import { browserWorkspace } from './electron/workspace.ts'
|
||||
import type { BrowserPageFactory } from './browser/BrowserPage.ts'
|
||||
import { BROWSER_ID, browserDefinition } from './definition.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { en, ja, zh } from './locales.ts'
|
||||
import { createBrowserStore } from './browser/store.ts'
|
||||
|
||||
export type { BrowserBodyProps } from './view/BrowserBody.tsx'
|
||||
@@ -70,7 +70,7 @@ export function apply(ctx: Context): void {
|
||||
dshDesktop?: { readonly protocolVersion: number; readonly browser?: DesktopBrowserBridge }
|
||||
}).dshDesktop
|
||||
const desktop = carrier?.protocolVersion === 1 ? carrier.browser : undefined
|
||||
ctx.effect(() => ctx.locale.register(namespace, { zh, en }), 'ui-sidebar-browser.copy')
|
||||
ctx.effect(() => ctx.locale.register(namespace, { zh, en, ja }), 'ui-sidebar-browser.copy')
|
||||
ctx.effect(() => ctx.sidebarRightTabs.register({ ...browserDefinition(t), keepMounted: desktop !== undefined }), 'ui-sidebar-browser.type')
|
||||
const installFrames = (scope: Context, factory: (sessionId: BrowserBodyProps['sessionId']) => BrowserPageFactory): void => {
|
||||
const controllers = new Map<BrowserBodyProps['sessionId'], BrowserInjected>()
|
||||
|
||||
@@ -67,3 +67,33 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
sidebarBrowser: SidebarBrowserKey
|
||||
}
|
||||
}
|
||||
|
||||
/** Japanese copy, checked against the English keys. */
|
||||
export const ja = {
|
||||
'type.label': 'ブラウザ',
|
||||
'guide.title': 'ブラウザ',
|
||||
'guide.description': 'Webページを閲覧',
|
||||
'shortcut.noSession': '先にセッションを開いてください',
|
||||
'address.placeholder': 'HTTP(S)アドレスを入力',
|
||||
'address.changed': 'URLが変更されました',
|
||||
'back': '戻る',
|
||||
'forward': '進む',
|
||||
'reload': '再読み込み',
|
||||
'go': '移動',
|
||||
'external': 'システムブラウザで開く',
|
||||
'sandbox.disable': 'サンドボックス制限を無効化',
|
||||
'sandbox.enable': 'サンドボックス制限を復元',
|
||||
'sandbox.warning': 'サンドボックス制限が無効です。ページはトップレベルのアプリを遷移し、ダウンロード、モーダルダイアログ、入力ロックを使用できます。',
|
||||
'start': 'HTTP(S)アドレスを入力して閲覧を開始',
|
||||
'loading': '開いています…',
|
||||
'restore.previous': '前回開いたページ',
|
||||
'restore.action': 'ページを復元',
|
||||
'error.empty': 'アドレスを入力してください。',
|
||||
'error.invalid': 'アドレスが無効か、長すぎます。',
|
||||
'error.protocol': 'サポートされるのはHTTPとHTTPSのアドレスのみです。ローカルファイルにはドキュメントプレビューを使用してください。',
|
||||
'error.credentials': 'アドレスにユーザー名やパスワードを含めることはできません。',
|
||||
'error.application-origin': '埋め込みブラウザはDSHアプリ自体を開けません。',
|
||||
'load.failed': 'ページを読み込めませんでした。再読み込みするか、システムブラウザで開いてください。',
|
||||
'load.failed.detail': 'ページの読み込みに失敗しました({code}): {description}',
|
||||
'address.unknown': 'ページが遷移しました。このキャリアは新しいURLを読み取れません。',
|
||||
} satisfies Record<keyof typeof en, string>
|
||||
@@ -11,7 +11,7 @@ import { BrowserTitle } from '../src/client/view/BrowserTitle.tsx'
|
||||
import type { BrowserInjected } from '../src/client/browser/BrowserController.ts'
|
||||
import { BROWSER_ID, BROWSER_KIND } from '../src/client/definition.tsx'
|
||||
import { apply, inject } from '../src/client/index.ts'
|
||||
import { en, zh } from '../src/client/locales.ts'
|
||||
import { en, ja, zh } from '../src/client/locales.ts'
|
||||
import { createBrowserStore } from '../src/client/browser/store.ts'
|
||||
import type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit'
|
||||
import type { DesktopBrowserBridge, DesktopBrowserLeaseId } from '../src/types.ts'
|
||||
@@ -125,7 +125,7 @@ describe('ui-sidebar-browser apply', () => {
|
||||
expect(definition?.title('sidebar://browser')).toBe('type.label')
|
||||
expect(definition?.guide?.map(entry => [entry.order, entry.title(), entry.description?.()]))
|
||||
.toEqual([[30, 'guide.title', 'guide.description']])
|
||||
expect(dictionaries.get('sidebarBrowser')).toEqual({ zh, en })
|
||||
expect(dictionaries.get('sidebarBrowser')).toEqual({ zh, en, ja })
|
||||
expect(dictionaries.get('sidebarBrowser')).toMatchObject({
|
||||
zh: { 'guide.description': '浏览网页' },
|
||||
en: { 'guide.description': 'Browse web pages' },
|
||||
|
||||
Loaded 100 of 172 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user