diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx index 1a1df9dd..b7c98afc 100644 --- a/desktop/src/__tests__/generalSettings.test.tsx +++ b/desktop/src/__tests__/generalSettings.test.tsx @@ -1651,6 +1651,34 @@ describe('Settings > Providers tab', () => { expect(within(dialog).getByText('Requests will be translated via the local proxy')).toBeInTheDocument() }) + it('localizes the main model placeholder in the provider form', () => { + useSettingsStore.setState({ locale: 'zh' }) + providerStoreState.presets = [ + { + id: 'custom', + name: 'Custom', + baseUrl: 'https://api.example.com/anthropic', + apiFormat: 'anthropic', + defaultModels: { + main: '', + haiku: '', + sonnet: '', + opus: '', + }, + needsApiKey: true, + websiteUrl: '', + }, + ] + + render() + + fireEvent.click(screen.getByRole('button', { name: /添加服务商/i })) + + const dialog = screen.getByRole('dialog') + expect(within(dialog).getByPlaceholderText('模型 ID')).toBeInTheDocument() + expect(within(dialog).queryByPlaceholderText('Model ID')).not.toBeInTheDocument() + }) + it('normalizes blank model mappings to the main model when saving a provider', async () => { providerStoreState.createProvider = vi.fn().mockResolvedValue({ id: 'provider-new', diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index 747bb0bd..ee0b4e3f 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -416,6 +416,7 @@ export const en = { 'settings.providers.sonnetModel': 'Sonnet Model', 'settings.providers.opusModel': 'Opus Model', 'settings.providers.model1mSupportShort': '1M', + 'settings.providers.modelIdPlaceholder': 'Model ID', 'settings.providers.sameAsMain': 'Same as main', 'settings.providers.contextSettingsTitle': 'Context and auto-compact', 'settings.providers.contextSettingsDesc': 'Preset limits are applied automatically. Edit only when the provider changes limits or you use a custom model ID.', @@ -969,6 +970,12 @@ export const en = { 'settings.general.responseLangDefault': 'Default (English)', 'settings.general.outputStyleTitle': 'Output Style', 'settings.general.outputStyleDescription': 'Choose how Claude communicates in new or restarted sessions.', + 'settings.general.outputStyleBuiltin.default.label': 'Default', + 'settings.general.outputStyleBuiltin.default.description': 'Claude completes coding tasks efficiently and provides concise responses', + 'settings.general.outputStyleBuiltin.explanatory.label': 'Explanatory', + 'settings.general.outputStyleBuiltin.explanatory.description': 'Claude explains its implementation choices and codebase patterns', + 'settings.general.outputStyleBuiltin.learning.label': 'Learning', + 'settings.general.outputStyleBuiltin.learning.description': 'Claude pauses and asks you to write small pieces of code for hands-on practice', 'settings.general.outputStyleSelectLabel': 'Select output style', 'settings.general.outputStyleLoading': 'Loading output styles...', 'settings.general.outputStyleSaved': 'Output style saved.', diff --git a/desktop/src/i18n/locales/jp.ts b/desktop/src/i18n/locales/jp.ts index 40524ada..5c2f0c36 100644 --- a/desktop/src/i18n/locales/jp.ts +++ b/desktop/src/i18n/locales/jp.ts @@ -418,6 +418,7 @@ export const jp: Record = { 'settings.providers.sonnetModel': 'Sonnet モデル', 'settings.providers.opusModel': 'Opus モデル', 'settings.providers.model1mSupportShort': '1M', + 'settings.providers.modelIdPlaceholder': 'モデル ID', 'settings.providers.sameAsMain': 'メインと同じ', 'settings.providers.contextSettingsTitle': 'コンテキストと自動コンパクト', 'settings.providers.contextSettingsDesc': 'プリセットの上限は自動的に適用されます。プロバイダーが上限を変更した場合やカスタムモデル ID を使用する場合のみ編集してください。', @@ -971,6 +972,12 @@ export const jp: Record = { 'settings.general.responseLangDefault': 'デフォルト(英語)', 'settings.general.outputStyleTitle': '出力スタイル', 'settings.general.outputStyleDescription': '新しいセッションまたは再起動後のセッションで Claude がどのように応答するかを選択します。', + 'settings.general.outputStyleBuiltin.default.label': 'デフォルト', + 'settings.general.outputStyleBuiltin.default.description': 'Claude はコーディングタスクを効率よく完了し、簡潔に応答します', + 'settings.general.outputStyleBuiltin.explanatory.label': '解説型', + 'settings.general.outputStyleBuiltin.explanatory.description': 'Claude は実装上の判断とコードベースのパターンを説明します', + 'settings.general.outputStyleBuiltin.learning.label': '学習型', + 'settings.general.outputStyleBuiltin.learning.description': 'Claude は一時停止し、実践練習として短いコードの記述を依頼します', 'settings.general.outputStyleSelectLabel': '出力スタイルを選択', 'settings.general.outputStyleLoading': '出力スタイルを読み込み中...', 'settings.general.outputStyleSaved': '出力スタイルを保存しました。', diff --git a/desktop/src/i18n/locales/kr.ts b/desktop/src/i18n/locales/kr.ts index 2ea02d90..ac1f5411 100644 --- a/desktop/src/i18n/locales/kr.ts +++ b/desktop/src/i18n/locales/kr.ts @@ -418,6 +418,7 @@ export const kr: Record = { 'settings.providers.sonnetModel': 'Sonnet 모델', 'settings.providers.opusModel': 'Opus 모델', 'settings.providers.model1mSupportShort': '1M', + 'settings.providers.modelIdPlaceholder': '모델 ID', 'settings.providers.sameAsMain': '메인과 동일', 'settings.providers.contextSettingsTitle': '컨텍스트 및 자동 압축', 'settings.providers.contextSettingsDesc': '사전 설정 한도가 자동으로 적용됩니다. 공급자가 한도를 변경하거나 사용자 지정 모델 ID를 사용할 때만 편집하세요.', @@ -971,6 +972,12 @@ export const kr: Record = { 'settings.general.responseLangDefault': '기본값(영어)', 'settings.general.outputStyleTitle': '출력 스타일', 'settings.general.outputStyleDescription': '새 세션 또는 다시 시작한 세션에서 Claude가 응답하는 방식을 선택합니다.', + 'settings.general.outputStyleBuiltin.default.label': '기본값', + 'settings.general.outputStyleBuiltin.default.description': 'Claude가 코딩 작업을 효율적으로 완료하고 간결하게 응답합니다', + 'settings.general.outputStyleBuiltin.explanatory.label': '설명형', + 'settings.general.outputStyleBuiltin.explanatory.description': 'Claude가 구현 선택과 코드베이스 패턴을 설명합니다', + 'settings.general.outputStyleBuiltin.learning.label': '학습형', + 'settings.general.outputStyleBuiltin.learning.description': 'Claude가 잠시 멈추고 실습을 위해 짧은 코드를 작성하도록 요청합니다', 'settings.general.outputStyleSelectLabel': '출력 스타일 선택', 'settings.general.outputStyleLoading': '출력 스타일 불러오는 중...', 'settings.general.outputStyleSaved': '출력 스타일이 저장되었습니다.', diff --git a/desktop/src/i18n/locales/zh-TW.ts b/desktop/src/i18n/locales/zh-TW.ts index 2503af48..9332b9d9 100644 --- a/desktop/src/i18n/locales/zh-TW.ts +++ b/desktop/src/i18n/locales/zh-TW.ts @@ -418,6 +418,7 @@ export const zh: Record = { 'settings.providers.sonnetModel': 'Sonnet 模型', 'settings.providers.opusModel': 'Opus 模型', 'settings.providers.model1mSupportShort': '1M', + 'settings.providers.modelIdPlaceholder': '模型 ID', 'settings.providers.sameAsMain': '與主模型相同', 'settings.providers.contextSettingsTitle': '上下文與自動壓縮', 'settings.providers.contextSettingsDesc': '預設會自動應用模型視窗。只有服務商調整限制,或使用自定義模型 ID 時才需要修改。', @@ -971,6 +972,12 @@ export const zh: Record = { 'settings.general.responseLangDefault': '預設(英語)', 'settings.general.outputStyleTitle': '輸出風格', 'settings.general.outputStyleDescription': '選擇 Claude 在新會話或重啟後的會話中如何表達。', + 'settings.general.outputStyleBuiltin.default.label': '預設', + 'settings.general.outputStyleBuiltin.default.description': 'Claude 會高效完成編碼任務,並提供簡潔回覆', + 'settings.general.outputStyleBuiltin.explanatory.label': '解釋型', + 'settings.general.outputStyleBuiltin.explanatory.description': 'Claude 會解釋實作選擇和程式碼庫模式', + 'settings.general.outputStyleBuiltin.learning.label': '學習型', + 'settings.general.outputStyleBuiltin.learning.description': 'Claude 會暫停並請你編寫小段程式碼進行實戰練習', 'settings.general.outputStyleSelectLabel': '選擇輸出風格', 'settings.general.outputStyleLoading': '正在載入輸出風格...', 'settings.general.outputStyleSaved': '輸出風格已儲存。', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index 1b5b096a..13bd6d07 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -418,6 +418,7 @@ export const zh: Record = { 'settings.providers.sonnetModel': 'Sonnet 模型', 'settings.providers.opusModel': 'Opus 模型', 'settings.providers.model1mSupportShort': '1M', + 'settings.providers.modelIdPlaceholder': '模型 ID', 'settings.providers.sameAsMain': '与主模型相同', 'settings.providers.contextSettingsTitle': '上下文与自动压缩', 'settings.providers.contextSettingsDesc': '预设会自动应用模型窗口。只有服务商调整限制,或使用自定义模型 ID 时才需要修改。', @@ -971,6 +972,12 @@ export const zh: Record = { 'settings.general.responseLangDefault': '默认(英语)', 'settings.general.outputStyleTitle': '输出风格', 'settings.general.outputStyleDescription': '选择 Claude 在新会话或重启后的会话中如何表达。', + 'settings.general.outputStyleBuiltin.default.label': '默认', + 'settings.general.outputStyleBuiltin.default.description': 'Claude 会高效完成编码任务,并提供简洁回复', + 'settings.general.outputStyleBuiltin.explanatory.label': '解释型', + 'settings.general.outputStyleBuiltin.explanatory.description': 'Claude 会解释实现选择和代码库模式', + 'settings.general.outputStyleBuiltin.learning.label': '学习型', + 'settings.general.outputStyleBuiltin.learning.description': 'Claude 会暂停并请你编写小段代码进行实战练习', 'settings.general.outputStyleSelectLabel': '选择输出风格', 'settings.general.outputStyleLoading': '正在加载输出风格...', 'settings.general.outputStyleSaved': '输出风格已保存。', diff --git a/desktop/src/pages/Settings.tsx b/desktop/src/pages/Settings.tsx index cd1660a7..1b2cea19 100644 --- a/desktop/src/pages/Settings.tsx +++ b/desktop/src/pages/Settings.tsx @@ -83,6 +83,20 @@ const NETWORK_TIMEOUT_MIN_SECONDS = 30 const NETWORK_TIMEOUT_MAX_SECONDS = 1800 const NETWORK_TIMEOUT_STEP_SECONDS = 30 const SETTINGS_CHECKBOX_INPUT_CLASS = 'settings-checkbox-input peer' +const BUILT_IN_OUTPUT_STYLE_TRANSLATION_KEYS = { + default: { + label: 'settings.general.outputStyleBuiltin.default.label', + description: 'settings.general.outputStyleBuiltin.default.description', + }, + Explanatory: { + label: 'settings.general.outputStyleBuiltin.explanatory.label', + description: 'settings.general.outputStyleBuiltin.explanatory.description', + }, + Learning: { + label: 'settings.general.outputStyleBuiltin.learning.label', + description: 'settings.general.outputStyleBuiltin.learning.description', + }, +} satisfies Record function buildH5LaunchUrl(baseUrl: string | null, token: string | null): string | null { if (!baseUrl) return null @@ -1763,7 +1777,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF required={slot === 'main'} value={models[slot]} onChange={(e) => handleModelChange(slot, e.target.value)} - placeholder={slot === 'main' ? 'Model ID' : t('settings.providers.sameAsMain')} + placeholder={slot === 'main' ? t('settings.providers.modelIdPlaceholder') : t('settings.providers.sameAsMain')} />