From 5d8172a25f7ccd2e69ce2b1c86e360e6b0be7444 Mon Sep 17 00:00:00 2001 From: brownrw8 Date: Tue, 25 Feb 2025 14:35:32 -1000 Subject: [PATCH] feat: Set preferred language in settings and have it update system prompt (#1538) * feat: set preferred language in settings and have it update system prompt * Update webview-ui/src/components/settings/PreferredLanguagePicker.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * remove unnecessary useEffect * tweak prompt * formatting from main * Update Cline.ts * Revert "Update Cline.ts" This reverts commit bee6f0fee0b0783293aa035e566978589fe837d6. * Fixes * Update Cline.ts * move preferredLanguage to Advanced Settings * remove unneccessary import * remove more imports * skip language prompt for default language `en` * Update package.json * lint --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> --- .changeset/proud-camels-brake.md | 5 ++ package.json | 25 +++++++ src/core/Cline.ts | 23 +++++- src/core/prompts/system.ts | 4 + src/shared/Languages.ts | 73 +++++++++++++++++++ .../settings/__tests__/APIOptions.spec.tsx | 3 + webview-ui/src/utils/vscStyles.ts | 1 + 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 .changeset/proud-camels-brake.md create mode 100644 src/shared/Languages.ts diff --git a/.changeset/proud-camels-brake.md b/.changeset/proud-camels-brake.md new file mode 100644 index 0000000000..173823c50f --- /dev/null +++ b/.changeset/proud-camels-brake.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Add preferred language option to settings diff --git a/package.json b/package.json index 6d59e81c4e..fc5d55130c 100644 --- a/package.json +++ b/package.json @@ -182,6 +182,31 @@ "default": null, "description": "Path to Chrome executable for browser use functionality. If not set, the extension will attempt to find or download it automatically." }, + "cline.preferredLanguage": { + "type": "string", + "enum": [ + "English", + "Arabic - العربية", + "Portuguese - Português (Brasil)", + "Czech - Čeština", + "French - Français", + "German - Deutsch", + "Hindi - हिन्दी", + "Hungarian - Magyar", + "Italian - Italiano", + "Japanese - 日本語", + "Korean - 한국어", + "Polish - Polski", + "Portuguese - Português (Portugal)", + "Russian - Русский", + "Simplified Chinese - 简体中文", + "Spanish - Español", + "Traditional Chinese - 繁體中文", + "Turkish - Türkçe" + ], + "default": "English", + "description": "The language that Cline should use for communication." + }, "cline.mcpMarketplace.enabled": { "type": "boolean", "default": true, diff --git a/src/core/Cline.ts b/src/core/Cline.ts index b1c6baa90c..1a5c058db9 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -59,6 +59,7 @@ import { formatResponse } from "./prompts/responses" import { addUserInstructions, SYSTEM_PROMPT } from "./prompts/system" import { getNextTruncationRange, getTruncatedMessages } from "./sliding-window" import { ClineProvider, GlobalFileNames } from "./webview/ClineProvider" +import { DEFAULT_LANGUAGE_SETTINGS, getLanguageKey, LanguageDisplay, LanguageKey } from "../shared/Languages" const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution @@ -75,6 +76,7 @@ export class Cline { browserSession: BrowserSession private didEditFile: boolean = false customInstructions?: string + preferredLanguage?: LanguageKey autoApprovalSettings: AutoApprovalSettings private browserSettings: BrowserSettings private chatSettings: ChatSettings @@ -135,6 +137,9 @@ export class Cline { this.browserSession = new BrowserSession(provider.context, browserSettings) this.diffViewProvider = new DiffViewProvider(cwd) this.customInstructions = customInstructions + this.preferredLanguage = getLanguageKey( + vscode.workspace.getConfiguration("cline").get("preferredLanguage"), + ) this.autoApprovalSettings = autoApprovalSettings this.browserSettings = browserSettings this.chatSettings = chatSettings @@ -1269,6 +1274,10 @@ export class Cline { let systemPrompt = await SYSTEM_PROMPT(cwd, supportsComputerUse, mcpHub, this.browserSettings) let settingsCustomInstructions = this.customInstructions?.trim() + const preferredLanguageInstructions = + this.preferredLanguage && this.preferredLanguage !== DEFAULT_LANGUAGE_SETTINGS + ? `# Preferred Language\n\nSpeak in ${this.preferredLanguage}.` + : "" const clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules) let clineRulesFileInstructions: string | undefined if (await fileExistsAtPath(clineRulesFilePath)) { @@ -1288,9 +1297,19 @@ export class Cline { clineIgnoreInstructions = `# .clineignore\n\n(The following is provided by a root-level .clineignore file where the user has specified files and directories that should not be accessed. When using list_files, you'll notice a ${LOCK_TEXT_SYMBOL} next to files that are blocked. Attempting to access the file's contents e.g. through read_file will result in an error.)\n\n${clineIgnoreContent}\n.clineignore` } - if (settingsCustomInstructions || clineRulesFileInstructions) { + if ( + settingsCustomInstructions || + clineRulesFileInstructions || + preferredLanguageInstructions || + clineIgnoreInstructions + ) { // altering the system prompt mid-task will break the prompt cache, but in the grand scheme this will not change often so it's better to not pollute user messages with it the way we have to with - systemPrompt += addUserInstructions(settingsCustomInstructions, clineRulesFileInstructions, clineIgnoreInstructions) + systemPrompt += addUserInstructions( + settingsCustomInstructions, + clineRulesFileInstructions, + clineIgnoreInstructions, + preferredLanguageInstructions, + ) } // If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 8d6d13766d..cc321a5b6b 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -979,8 +979,12 @@ export function addUserInstructions( settingsCustomInstructions?: string, clineRulesFileInstructions?: string, clineIgnoreInstructions?: string, + preferredLanguageInstructions?: string, ) { let customInstructions = "" + if (preferredLanguageInstructions) { + customInstructions += preferredLanguageInstructions + "\n\n" + } if (settingsCustomInstructions) { customInstructions += settingsCustomInstructions + "\n\n" } diff --git a/src/shared/Languages.ts b/src/shared/Languages.ts new file mode 100644 index 0000000000..8b933ccc6b --- /dev/null +++ b/src/shared/Languages.ts @@ -0,0 +1,73 @@ +export type LanguageKey = + | "en" + | "ar" + | "pt-BR" + | "cs" + | "fr" + | "de" + | "hi" + | "hu" + | "it" + | "ja" + | "ko" + | "pl" + | "pt-PT" + | "ru" + | "zh-CN" + | "es" + | "zh-TW" + | "tr" + +export type LanguageDisplay = + | "English" + | "Arabic - العربية" + | "Portuguese - Português (Brasil)" + | "Czech - Čeština" + | "French - Français" + | "German - Deutsch" + | "Hindi - हिन्दी" + | "Hungarian - Magyar" + | "Italian - Italiano" + | "Japanese - 日本語" + | "Korean - 한국어" + | "Polish - Polski" + | "Portuguese - Português (Portugal)" + | "Russian - Русский" + | "Simplified Chinese - 简体中文" + | "Spanish - Español" + | "Traditional Chinese - 繁體中文" + | "Turkish - Türkçe" + +export const DEFAULT_LANGUAGE_SETTINGS: LanguageKey = "en" + +export const languageOptions: { key: LanguageKey; display: LanguageDisplay }[] = [ + { key: "en", display: "English" }, + { key: "ar", display: "Arabic - العربية" }, + { key: "pt-BR", display: "Portuguese - Português (Brasil)" }, + { key: "cs", display: "Czech - Čeština" }, + { key: "fr", display: "French - Français" }, + { key: "de", display: "German - Deutsch" }, + { key: "hi", display: "Hindi - हिन्दी" }, + { key: "hu", display: "Hungarian - Magyar" }, + { key: "it", display: "Italian - Italiano" }, + { key: "ja", display: "Japanese - 日本語" }, + { key: "ko", display: "Korean - 한국어" }, + { key: "pl", display: "Polish - Polski" }, + { key: "pt-PT", display: "Portuguese - Português (Portugal)" }, + { key: "ru", display: "Russian - Русский" }, + { key: "zh-CN", display: "Simplified Chinese - 简体中文" }, + { key: "es", display: "Spanish - Español" }, + { key: "zh-TW", display: "Traditional Chinese - 繁體中文" }, + { key: "tr", display: "Turkish - Türkçe" }, +] + +export function getLanguageKey(display: LanguageDisplay | undefined): LanguageKey { + if (!display) { + return DEFAULT_LANGUAGE_SETTINGS + } + const languageOption = languageOptions.find((option) => option.display === display) + if (languageOption) { + return languageOption.key + } + return DEFAULT_LANGUAGE_SETTINGS +} diff --git a/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx b/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx index 73655f227a..8cad54142b 100644 --- a/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx @@ -126,6 +126,7 @@ describe("OpenApiInfoOptions", () => { , ) + fireEvent.click(screen.getByText("Model Configuration")) const apiKeyInput = screen.getByText("Supports Images") expect(apiKeyInput).toBeInTheDocument() }) @@ -136,6 +137,7 @@ describe("OpenApiInfoOptions", () => { , ) + fireEvent.click(screen.getByText("Model Configuration")) const orgIdInput = screen.getByText("Context Window Size") expect(orgIdInput).toBeInTheDocument() }) @@ -146,6 +148,7 @@ describe("OpenApiInfoOptions", () => { , ) + fireEvent.click(screen.getByText("Model Configuration")) const modelInput = screen.getByText("Max Output Tokens") expect(modelInput).toBeInTheDocument() }) diff --git a/webview-ui/src/utils/vscStyles.ts b/webview-ui/src/utils/vscStyles.ts index 69faa5fbad..ac8d34a0b9 100644 --- a/webview-ui/src/utils/vscStyles.ts +++ b/webview-ui/src/utils/vscStyles.ts @@ -1,4 +1,5 @@ export const VSC_INPUT_BACKGROUND = "--vscode-input-background" +export const VSC_INPUT_FOREGROUND = "--vscode-input-foreground" export const VSC_SIDEBAR_BACKGROUND = "--vscode-sideBar-background" export const VSC_FOREGROUND = "--vscode-foreground" export const VSC_EDITOR_FOREGROUND = "--vscode-editor-foreground"