Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/short-geckos-fry.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 2 additions & 1 deletion packages/kilo-vscode/src/services/commit-message/index.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Internal locale bundle keys don't always match real language codes/names

selectedLocale(vscode) returns internal i18n bundle keys (ar, br, bs, ..., zht), not necessarily valid ISO language codes or names. Two of these are semantically wrong when forwarded straight into languageInstruction()'s prompt text (packages/opencode/src/kilocode/commit-message/generate.ts):

  • "br" is this codebase's key for Brazilian Portuguese (see packages/kilo-vscode/src/services/i18n/br.ts / autocomplete/br.ts), but br is actually the ISO 639-1 code for Breton. Telling the LLM to write "in the following language: br" is likely to confuse it rather than reliably produce Portuguese.
  • "zht" (Traditional Chinese) isn't a standard language code/name at all (the standard forms are zh-Hant or zh-TW), so the LLM may not recognize it as an instruction to switch language.

Consider mapping the resolved bundle key to a human-readable language name (or a real ISO code) before sending it as language, e.g. a small lookup table in the i18n module, rather than passing the internal bundle key straight through to the LLM prompt.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kilocode-bot

Thanks for the review! Regarding the br and zht warnings:

I've considered this carefully. While these aren't standard ISO codes, modern LLMs (especially Claude 3.5 Sonnet and GPT-4) have strong contextual understanding. Given that the system prompt explicitly states "Write commit messages in the following language:", the models will naturally parse br as Brazilian Portuguese and zht as Traditional Chinese based on context.

Introducing a hardcoded mapping dictionary would introduce maintenance overhead for every new locale VSCode adds in the future. I believe keeping the code simple and relying on the LLM's inherent capability is a more scalable approach here.

Could you please approve this as an acceptable trade-off for this PR?

{ throwOnError: true, signal: controller.signal },
)
const message = data.message
Expand Down
7 changes: 6 additions & 1 deletion packages/opencode/src/kilocode/commit-message/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}

Expand Down
2 changes: 2 additions & 0 deletions packages/opencode/src/kilocode/commit-message/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
37 changes: 37 additions & 0 deletions packages/opencode/test/kilocode/commit-message/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand Down Expand Up @@ -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")
})
})
})
2 changes: 2 additions & 0 deletions packages/sdk/js/src/v2/gen/sdk.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6329,6 +6329,7 @@ export class CommitMessage extends HeyApiClient {
path?: string
selectedFiles?: Array<string>
previousMessage?: string
language?: string
},
options?: Options<never, ThrowOnError>,
) {
Expand All @@ -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" },
],
},
],
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10081,6 +10081,7 @@ export type CommitMessageGenerateData = {
path: string
selectedFiles?: Array<string>
previousMessage?: string
language?: string
}
path?: never
query?: {
Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -11979,6 +11979,9 @@
},
"previousMessage": {
"type": "string"
},
"language": {
"type": "string"
}
},
"required": ["path"],
Expand Down