mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 11:53:10 +08:00
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:
@@ -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',
|
||||
|
||||
@@ -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': '低',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -29,5 +29,6 @@ export type UserSettings = {
|
||||
skipWebFetchPreflight?: boolean
|
||||
desktopNotificationsEnabled?: boolean
|
||||
webSearch?: WebSearchSettings
|
||||
language?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user