feat: add response language selector in General settings

Expose the CLI's `language` setting in the desktop app's General tab.
Users can now pick from 21 pre-defined languages via a dropdown (value
maps directly to CLI language names such as "chinese", "japanese").
Selecting a language writes it to ~/.claude/settings.json so the CLI
injects the language instruction into every session's system prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes)
2026-05-10 11:54:17 +08:00
parent 065a80580b
commit b693aa53b4
5 changed files with 63 additions and 0 deletions
+3
View File
@@ -643,6 +643,9 @@ export const en = {
'settings.general.appearance.dark': 'Dark',
'settings.general.languageTitle': 'Language',
'settings.general.languageDescription': 'Choose the display language for the application.',
'settings.general.responseLangTitle': 'Response Language',
'settings.general.responseLangDescription': 'Instruct Claude to always respond in a specific language.',
'settings.general.responseLangDefault': 'Default (English)',
'settings.general.effortTitle': 'Effort Level',
'settings.general.effortDescription': 'Controls how much computation the model uses.',
'settings.general.effort.low': 'Low',
+3
View File
@@ -645,6 +645,9 @@ export const zh: Record<TranslationKey, string> = {
'settings.general.appearance.dark': '暗色',
'settings.general.languageTitle': '语言',
'settings.general.languageDescription': '选择应用程序的显示语言。',
'settings.general.responseLangTitle': '回复语言',
'settings.general.responseLangDescription': '指定 Claude 始终以某种语言回复。',
'settings.general.responseLangDefault': '默认(英语)',
'settings.general.effortTitle': '推理强度',
'settings.general.effortDescription': '控制模型使用的计算量。',
'settings.general.effort.low': '低',
+42
View File
@@ -1355,6 +1355,8 @@ function GeneralSettings() {
setDesktopNotificationsEnabled,
webSearch,
setWebSearch,
responseLanguage,
setResponseLanguage,
} = useSettingsStore()
const t = useTranslation()
const [webSearchDraft, setWebSearchDraft] = useState(webSearch)
@@ -1388,6 +1390,31 @@ function GeneralSettings() {
{ value: 'zh', label: '中文' },
]
const RESPONSE_LANGUAGES: Array<{ value: string; label: string }> = [
{ value: '', label: t('settings.general.responseLangDefault') },
{ value: 'english', label: 'English' },
{ value: 'chinese', label: '中文 (Chinese)' },
{ value: 'japanese', label: '日本語 (Japanese)' },
{ value: 'korean', label: '한국어 (Korean)' },
{ value: 'spanish', label: 'Español (Spanish)' },
{ value: 'french', label: 'Français (French)' },
{ value: 'german', label: 'Deutsch (German)' },
{ value: 'portuguese', label: 'Português (Portuguese)' },
{ value: 'italian', label: 'Italiano (Italian)' },
{ value: 'russian', label: 'Русский (Russian)' },
{ value: 'dutch', label: 'Nederlands (Dutch)' },
{ value: 'polish', label: 'Polski (Polish)' },
{ value: 'turkish', label: 'Türkçe (Turkish)' },
{ value: 'hindi', label: 'हिन्दी (Hindi)' },
{ value: 'indonesian', label: 'Bahasa Indonesia' },
{ value: 'ukrainian', label: 'Українська (Ukrainian)' },
{ value: 'greek', label: 'Ελληνικά (Greek)' },
{ value: 'czech', label: 'Čeština (Czech)' },
{ value: 'danish', label: 'Dansk (Danish)' },
{ value: 'swedish', label: 'Svenska (Swedish)' },
{ value: 'norwegian', label: 'Norsk (Norwegian)' },
]
const THEMES: Array<{ value: ThemeMode; label: string }> = [
{ value: 'light', label: t('settings.general.appearance.light') },
{ value: 'dark', label: t('settings.general.appearance.dark') },
@@ -1493,6 +1520,21 @@ function GeneralSettings() {
))}
</div>
{/* Response Language */}
<h2 className="text-base font-semibold text-[var(--color-text-primary)] mb-1">{t('settings.general.responseLangTitle')}</h2>
<p className="text-sm text-[var(--color-text-tertiary)] mb-3">{t('settings.general.responseLangDescription')}</p>
<div className="flex gap-2 mb-8">
<select
value={responseLangDraft}
onChange={(e) => void setResponseLanguage(e.target.value)}
className="flex-1 px-3 py-2 text-sm rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-container-low)] text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-brand)] transition-colors cursor-pointer"
>
{RESPONSE_LANGUAGES.map(({ value, label }) => (
<option key={value} value={value}>{label}</option>
))}
</select>
</div>
{/* Effort Level */}
<h2 className="text-base font-semibold text-[var(--color-text-primary)] mb-1">{t('settings.general.effortTitle')}</h2>
<p className="text-sm text-[var(--color-text-tertiary)] mb-3">{t('settings.general.effortDescription')}</p>
+14
View File
@@ -28,6 +28,7 @@ type SettingsStore = {
skipWebFetchPreflight: boolean
desktopNotificationsEnabled: boolean
webSearch: WebSearchSettings
responseLanguage: string
isLoading: boolean
error: string | null
@@ -41,6 +42,7 @@ type SettingsStore = {
setSkipWebFetchPreflight: (enabled: boolean) => Promise<void>
setDesktopNotificationsEnabled: (enabled: boolean) => Promise<void>
setWebSearch: (settings: WebSearchSettings) => Promise<void>
setResponseLanguage: (language: string) => Promise<void>
}
export const useSettingsStore = create<SettingsStore>((set, get) => ({
@@ -55,6 +57,7 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
skipWebFetchPreflight: true,
desktopNotificationsEnabled: false,
webSearch: { mode: 'auto', tavilyApiKey: '', braveApiKey: '' },
responseLanguage: '',
isLoading: false,
error: null,
@@ -81,6 +84,7 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
skipWebFetchPreflight: userSettings.skipWebFetchPreflight !== false,
desktopNotificationsEnabled: userSettings.desktopNotificationsEnabled === true,
webSearch: normalizeWebSearchSettings(userSettings.webSearch),
responseLanguage: typeof userSettings.language === 'string' ? userSettings.language : '',
isLoading: false,
error: null,
})
@@ -186,6 +190,16 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
set({ webSearch: prev })
}
},
setResponseLanguage: async (language) => {
const prev = get().responseLanguage
set({ responseLanguage: language })
try {
await settingsApi.updateUser({ language: language || undefined })
} catch {
set({ responseLanguage: prev })
}
},
}))
function normalizeWebSearchSettings(settings: WebSearchSettings | undefined): WebSearchSettings {
+1
View File
@@ -29,5 +29,6 @@ export type UserSettings = {
skipWebFetchPreflight?: boolean
desktopNotificationsEnabled?: boolean
webSearch?: WebSearchSettings
language?: string
[key: string]: unknown
}