From 32426ebab8773e517a6260cbf6b3b0a82a1d8198 Mon Sep 17 00:00:00 2001 From: IOLOII Date: Tue, 7 Jul 2026 11:21:33 +0800 Subject: [PATCH 1/4] feat(commit-message): add language parameter to commit message generation Add support for generating commit messages in the user's selected UI language. The language parameter flows through the entire stack: VSCode extension passes the selected locale, the HTTP API accepts the new field, and the generation service appends language instructions to the system prompt for non-English locales. - Update CommitMessageRequest types to include optional language field - Modify VSCode service to pass selected locale to the API - Append language requirements to system prompt when language is not English - Update OpenAPI specification and regenerate SDK types - Add test coverage for language instruction generation --- .changeset/short-geckos-fry.md | 6 +++ .../src/services/commit-message/index.ts | 3 +- .../src/kilocode/commit-message/generate.ts | 7 +++- .../src/kilocode/commit-message/types.ts | 2 + .../server/httpapi/groups/commit-message.ts | 3 ++ .../server/httpapi/handlers/commit-message.ts | 1 + .../kilocode/commit-message/generate.test.ts | 37 +++++++++++++++++++ packages/sdk/js/src/v2/gen/sdk.gen.ts | 2 + packages/sdk/js/src/v2/gen/types.gen.ts | 1 + packages/sdk/openapi.json | 3 ++ 10 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 .changeset/short-geckos-fry.md diff --git a/.changeset/short-geckos-fry.md b/.changeset/short-geckos-fry.md new file mode 100644 index 00000000000..f7733130c82 --- /dev/null +++ b/.changeset/short-geckos-fry.md @@ -0,0 +1,6 @@ +--- +"kilo-code": patch +"@kilocode/cli": patch +--- + +Generate commit messages in the user's selected UI language instead of always using English. diff --git a/packages/kilo-vscode/src/services/commit-message/index.ts b/packages/kilo-vscode/src/services/commit-message/index.ts index ecb8cd2e4a8..a96bb473c26 100644 --- a/packages/kilo-vscode/src/services/commit-message/index.ts +++ b/packages/kilo-vscode/src/services/commit-message/index.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode" import type { KiloConnectionService } from "../cli-backend/connection-service" import { getErrorMessage } from "../../kilo-provider-utils" +import { selectedLocale } from "../i18n" let lastGeneratedMessage: string | undefined let lastWorkspacePath: string | undefined @@ -94,7 +95,7 @@ export function registerCommitMessageService( try { const { data } = await client.commitMessage.generate( - { path, selectedFiles: undefined, previousMessage }, + { path, selectedFiles: undefined, previousMessage, language: selectedLocale(vscode) }, { throwOnError: true, signal: controller.signal }, ) const message = data.message diff --git a/packages/opencode/src/kilocode/commit-message/generate.ts b/packages/opencode/src/kilocode/commit-message/generate.ts index c3e9b5a4dc0..dbf22100112 100644 --- a/packages/opencode/src/kilocode/commit-message/generate.ts +++ b/packages/opencode/src/kilocode/commit-message/generate.ts @@ -98,6 +98,11 @@ For significant changes, include a detailed body explaining the changes. Return ONLY the commit message in the conventional format, nothing else.` +function languageInstruction(language?: string): string { + if (!language || language.toLowerCase() === "en") return "" + return `\n\n## Language Requirement\nCRITICAL: You MUST generate the commit message in the following language: ${language}. The entire commit message including type, scope, description, body, and footer MUST be in this language.` +} + function buildUserMessage(ctx: GitContext): string { const fileList = ctx.files.map((f) => `${f.status} ${f.path}`).join("\n") const diffs = ctx.files @@ -162,7 +167,7 @@ export async function generateCommitMessage(request: CommitMessageRequest): Prom hidden: true, options: {}, permission: [], - prompt: request.prompt || SYSTEM_PROMPT, + prompt: (request.prompt || SYSTEM_PROMPT) + languageInstruction(request.language), temperature: 0.3, } diff --git a/packages/opencode/src/kilocode/commit-message/types.ts b/packages/opencode/src/kilocode/commit-message/types.ts index 580875f31b6..08c0ac6ef26 100644 --- a/packages/opencode/src/kilocode/commit-message/types.ts +++ b/packages/opencode/src/kilocode/commit-message/types.ts @@ -7,6 +7,8 @@ export interface CommitMessageRequest { previousMessage?: string /** Optional custom system prompt — overrides the default conventional commits prompt */ prompt?: string + /** Target language for the generated commit message (e.g. "zh", "en"). Falls back to English. */ + language?: string } export interface CommitMessageResponse { diff --git a/packages/opencode/src/kilocode/server/httpapi/groups/commit-message.ts b/packages/opencode/src/kilocode/server/httpapi/groups/commit-message.ts index cd1c611f661..07477c22f16 100644 --- a/packages/opencode/src/kilocode/server/httpapi/groups/commit-message.ts +++ b/packages/opencode/src/kilocode/server/httpapi/groups/commit-message.ts @@ -18,6 +18,9 @@ export const CommitMessagePayload = Schema.Struct({ previousMessage: Schema.optional(Schema.String).annotate({ description: "Previously generated message — triggers regeneration with a different result", }), + language: Schema.optional(Schema.String).annotate({ + description: "Target language for the generated commit message (e.g. zh, en). Falls back to English.", + }), }) const CommitMessageResponse = Schema.Struct({ diff --git a/packages/opencode/src/kilocode/server/httpapi/handlers/commit-message.ts b/packages/opencode/src/kilocode/server/httpapi/handlers/commit-message.ts index d1c4366a0e2..102e4a76e26 100644 --- a/packages/opencode/src/kilocode/server/httpapi/handlers/commit-message.ts +++ b/packages/opencode/src/kilocode/server/httpapi/handlers/commit-message.ts @@ -21,6 +21,7 @@ export const commitMessageHandlers = HttpApiBuilder.group(InstanceHttpApi, "comm selectedFiles: ctx.payload.selectedFiles ? [...ctx.payload.selectedFiles] : undefined, previousMessage: ctx.payload.previousMessage, prompt, + language: ctx.payload.language, }), ) return { message: result.message } diff --git a/packages/opencode/test/kilocode/commit-message/generate.test.ts b/packages/opencode/test/kilocode/commit-message/generate.test.ts index d58873302d7..494f31e22b0 100644 --- a/packages/opencode/test/kilocode/commit-message/generate.test.ts +++ b/packages/opencode/test/kilocode/commit-message/generate.test.ts @@ -27,6 +27,12 @@ const defaultGitContext: GitContext = { let mockGitContext: GitContext = { ...defaultGitContext } let captured: { path: string; selected?: string[] } = { path: "" } +function lastPrompt(): string | undefined { + const args = stream.mock.calls[stream.mock.calls.length - 1] + if (!args) return undefined + return (args[0] as { agent?: { prompt?: string } }).agent?.prompt +} + mock.module("@/agent/agent", () => ({ ...realAgent, Agent: {}, @@ -162,6 +168,37 @@ describe("commit-message.generate", () => { test("uses custom prompt when provided", async () => { const result = await generateCommitMessage({ path: "/repo", prompt: "Write a haiku commit message." }) expect(result.message).toBeTruthy() + expect(lastPrompt()).toContain("Write a haiku commit message.") + }) + }) + + describe("language", () => { + test("does not add language instruction when language is omitted", async () => { + await generateCommitMessage({ path: "/repo" }) + const prompt = lastPrompt() + expect(prompt).toBeTruthy() + expect(prompt).not.toContain("Language Requirement") + }) + + test("does not add language instruction when language is English", async () => { + await generateCommitMessage({ path: "/repo", language: "en" }) + const prompt = lastPrompt() + expect(prompt).toBeTruthy() + expect(prompt).not.toContain("Language Requirement") + }) + + test("adds language instruction for non-English languages", async () => { + await generateCommitMessage({ path: "/repo", language: "zh" }) + const prompt = lastPrompt() + expect(prompt).toContain("Language Requirement") + expect(prompt).toContain("following language: zh") + }) + + test("appends language instruction to custom prompt", async () => { + await generateCommitMessage({ path: "/repo", prompt: "Write a haiku commit message.", language: "zh" }) + const prompt = lastPrompt() + expect(prompt).toContain("Write a haiku commit message.") + expect(prompt).toContain("Language Requirement") }) }) }) diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index c315c6edc86..92cb20b9224 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -6329,6 +6329,7 @@ export class CommitMessage extends HeyApiClient { path?: string selectedFiles?: Array previousMessage?: string + language?: string }, options?: Options, ) { @@ -6342,6 +6343,7 @@ export class CommitMessage extends HeyApiClient { { in: "body", key: "path" }, { in: "body", key: "selectedFiles" }, { in: "body", key: "previousMessage" }, + { in: "body", key: "language" }, ], }, ], diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 5282b260f4c..dcbf7aee4de 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -10081,6 +10081,7 @@ export type CommitMessageGenerateData = { path: string selectedFiles?: Array previousMessage?: string + language?: string } path?: never query?: { diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 267d53bb1d7..cb87aa2a7f2 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -11979,6 +11979,9 @@ }, "previousMessage": { "type": "string" + }, + "language": { + "type": "string" } }, "required": ["path"], From 7188af1c9c0a1b7073472d34bb1b988857f2563a Mon Sep 17 00:00:00 2001 From: IOLOII Date: Tue, 7 Jul 2026 14:32:59 +0800 Subject: [PATCH 2/4] feat(commit-message): add configurable language setting for AI-generated commit messages Add a new `kilo-code.new.languageCommitMessage` VS Code setting that allows users to choose a specific language for AI-generated commit messages, independent of the UI language. When set to "sync" (default), the commit message language follows the Kilo Code UI language. - Add language selector dropdown to CommitMessageTab settings UI - Add getCommitMessageLanguage() helper in i18n service - Pass languageCommitMessage setting through KiloProvider to webview - Add translation strings for all 20 supported locales --- packages/kilo-vscode/package.json | 51 ++++++++++++++++ packages/kilo-vscode/src/KiloProvider.ts | 16 +++-- .../src/services/commit-message/index.ts | 4 +- .../kilo-vscode/src/services/i18n/index.ts | 7 +++ .../components/settings/CommitMessageTab.tsx | 59 ++++++++++++++++--- .../kilo-vscode/webview-ui/src/i18n/ar.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/br.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/bs.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/da.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/de.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/en.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/es.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/fr.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/it.ts | 6 ++ .../kilo-vscode/webview-ui/src/i18n/ja.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/ko.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/nl.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/no.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/pl.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/ru.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/th.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/tr.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/uk.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/zh.ts | 5 ++ .../kilo-vscode/webview-ui/src/i18n/zht.ts | 5 ++ 25 files changed, 223 insertions(+), 15 deletions(-) diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index f91fb383f19..4f20f294c30 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -865,6 +865,57 @@ "Italiano" ] }, + "kilo-code.new.languageCommitMessage": { + "type": "string", + "default": "sync", + "description": "Language used for AI-generated commit messages. Choose 'sync' to follow the Kilo Code UI language, or select a specific language.", + "enum": [ + "sync", + "en", + "zh", + "zht", + "ko", + "de", + "es", + "fr", + "da", + "ja", + "pl", + "ru", + "ar", + "no", + "br", + "th", + "bs", + "tr", + "nl", + "uk", + "it" + ], + "enumDescriptions": [ + "跟随界面语言 (Sync with UI language)", + "English", + "简体中文", + "繁體中文", + "한국어", + "Deutsch", + "Español", + "Français", + "Dansk", + "日本語", + "Polski", + "Русский", + "العربية", + "Norsk", + "Português (Brasil)", + "ภาษาไทย", + "Bosanski", + "Türkçe", + "Nederlands", + "Українська", + "Italiano" + ] + }, "kilo-code.new.model.providerID": { "type": "string", "default": "kilo", diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 2d85c39ac24..33542cfe469 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -2418,7 +2418,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper config, globalConfig: global, projectConfig: overlay?.project, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: configFeatures(config), } this.cachedConfigMessage = message @@ -2514,7 +2514,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper config, globalConfig: global, projectConfig: overlay?.project, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: configFeatures(config), } this.postMessage({ @@ -2522,7 +2522,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper config, globalConfig: global, projectConfig: overlay?.project, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: configFeatures(config), }) } catch (error) { @@ -2904,7 +2904,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper config: merged, globalConfig: global, projectConfig: overlay?.project, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: configFeatures(merged), } this.postMessage({ @@ -2912,7 +2912,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper config: merged, globalConfig: global, projectConfig: overlay?.project, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: configFeatures(merged), }) this.requirements.clear() @@ -2934,7 +2934,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper type: "configUpdated", config: optimistic, globalConfig: this.cachedGlobalConfig ?? undefined, - settings: { maxCost: this.maxCostSetting() }, + settings: { maxCost: this.maxCostSetting(), languageCommitMessage: this.commitMessageLanguageSetting() }, features: features ?? configFeatures(optimistic as Config), }) this.requirements.clear() @@ -3075,6 +3075,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper return this.setMaxCost(vscode.workspace.getConfiguration("kilo-code.new").get("maxCost", 0)) } + private commitMessageLanguageSetting(): string { + return vscode.workspace.getConfiguration("kilo-code.new").get("languageCommitMessage", "sync") + } + private setMaxCost(value: unknown): number { maxCost = MaxCostNudge.normalizeLimit(typeof value === "number" ? value : Number(value)) ?? 0 this.costs.setLimit(maxCost) diff --git a/packages/kilo-vscode/src/services/commit-message/index.ts b/packages/kilo-vscode/src/services/commit-message/index.ts index a96bb473c26..0c0c20fb63d 100644 --- a/packages/kilo-vscode/src/services/commit-message/index.ts +++ b/packages/kilo-vscode/src/services/commit-message/index.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode" import type { KiloConnectionService } from "../cli-backend/connection-service" import { getErrorMessage } from "../../kilo-provider-utils" -import { selectedLocale } from "../i18n" +import { getCommitMessageLanguage } from "../i18n" let lastGeneratedMessage: string | undefined let lastWorkspacePath: string | undefined @@ -95,7 +95,7 @@ export function registerCommitMessageService( try { const { data } = await client.commitMessage.generate( - { path, selectedFiles: undefined, previousMessage, language: selectedLocale(vscode) }, + { path, selectedFiles: undefined, previousMessage, language: getCommitMessageLanguage(vscode) }, { throwOnError: true, signal: controller.signal }, ) const message = data.message diff --git a/packages/kilo-vscode/src/services/i18n/index.ts b/packages/kilo-vscode/src/services/i18n/index.ts index 0082c2f9026..4da8dda2921 100644 --- a/packages/kilo-vscode/src/services/i18n/index.ts +++ b/packages/kilo-vscode/src/services/i18n/index.ts @@ -66,6 +66,13 @@ export function selectedLocale(vscode: typeof import("vscode")): string { return resolveLocale(lang || vscode.env.language) } +export function getCommitMessageLanguage(vscode: typeof import("vscode")): string { + const cfg = vscode.workspace.getConfiguration("kilo-code.new") + const commitLang = cfg.get("languageCommitMessage") ?? "sync" + if (commitLang === "sync") return selectedLocale(vscode) + return resolveLocale(commitLang) +} + export function translate( locale: string, key: keyof typeof enDict | string, diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx index ca463c17ff9..b641a7ad8ef 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx @@ -1,15 +1,23 @@ -import { Component, Show, createSignal } from "solid-js" +import { Component, Show, createSignal, createMemo } from "solid-js" import { Switch } from "@kilocode/kilo-ui/switch" import { TextField } from "@kilocode/kilo-ui/text-field" import { Card } from "@kilocode/kilo-ui/card" +import { Select } from "@kilocode/kilo-ui/select" import { useConfig } from "../../context/config" -import { useLanguage } from "../../context/language" +import { useLanguage, LOCALES, LOCALE_LABELS } from "../../context/language" +import type { Locale } from "../../context/language" import SettingsRow from "./SettingsRow" +const SYNC = "sync" +const opts = [SYNC, ...LOCALES] as const +type Option = typeof SYNC | Locale + const CommitMessageTab: Component = () => { - const { config, updateConfig } = useConfig() + const { config, updateConfig, settings, updateSetting } = useConfig() const language = useLanguage() + const langValue = () => settings().languageCommitMessage ?? SYNC + const [expanded, setExpanded] = createSignal(Boolean(config().commit_message?.prompt)) const toggle = (checked: boolean) => { @@ -19,9 +27,46 @@ const CommitMessageTab: Component = () => { } } + const label = (opt: Option) => + opt === SYNC ? language.t("settings.commitMessage.language.sync") : LOCALE_LABELS[opt] + + const value = (opt: Option) => opt + + const onSelect = (opt: Option | undefined) => { + if (opt !== undefined) updateSetting("languageCommitMessage", opt) + } + + const currentLabel = createMemo(() => label(langValue() as Option)) + return ( -
- + +
+

+ {language.t("settings.commitMessage.language.description")} +

+