mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: handle Chinese and other non-Latin character encodings in terminal output
- Detect system locale and use appropriate UTF-8 encoding - Convert GBK, SJIS, EUC-KR and other encodings to UTF-8 - Add comprehensive tests for locale detection - Fixes #6768
This commit is contained in:
parent
2b647ed9a1
commit
1f1abd21e8
2 changed files with 78 additions and 7 deletions
|
|
@ -32,21 +32,49 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
|
|||
return terminal
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate UTF-8 locale based on the system's current locale.
|
||||
* This ensures proper encoding for different languages, especially for
|
||||
* non-Latin character sets like Chinese, Japanese, Korean, etc.
|
||||
*/
|
||||
private getUtf8Locale(): { LANG: string; LC_ALL: string } {
|
||||
const currentLang = process.env.LANG || process.env.LC_ALL || ""
|
||||
|
||||
// Extract the language/country part before the encoding (e.g., "zh_CN" from "zh_CN.GBK")
|
||||
const langMatch = currentLang.match(/^([a-z]{2}_[A-Z]{2})/i)
|
||||
|
||||
if (langMatch) {
|
||||
// Use the detected locale with UTF-8 encoding
|
||||
const utf8Locale = `${langMatch[1]}.UTF-8`
|
||||
return {
|
||||
LANG: utf8Locale,
|
||||
LC_ALL: utf8Locale,
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to en_US.UTF-8 if no locale is detected
|
||||
return {
|
||||
LANG: "en_US.UTF-8",
|
||||
LC_ALL: "en_US.UTF-8",
|
||||
}
|
||||
}
|
||||
|
||||
public override async run(command: string) {
|
||||
this.command = command
|
||||
|
||||
try {
|
||||
this.isHot = true
|
||||
|
||||
const utf8Locale = this.getUtf8Locale()
|
||||
|
||||
this.subprocess = execa({
|
||||
shell: true,
|
||||
cwd: this.terminal.getCurrentWorkingDirectory(),
|
||||
all: true,
|
||||
env: {
|
||||
...process.env,
|
||||
// Ensure UTF-8 encoding for Ruby, CocoaPods, etc.
|
||||
LANG: "en_US.UTF-8",
|
||||
LC_ALL: "en_US.UTF-8",
|
||||
// Ensure UTF-8 encoding based on system locale
|
||||
...utf8Locale,
|
||||
},
|
||||
})`${command}`
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,10 @@ describe("ExecaTerminalProcess", () => {
|
|||
})
|
||||
|
||||
describe("UTF-8 encoding fix", () => {
|
||||
it("should set LANG and LC_ALL to en_US.UTF-8", async () => {
|
||||
it("should set LANG and LC_ALL to en_US.UTF-8 when no locale is set", async () => {
|
||||
delete process.env.LANG
|
||||
delete process.env.LC_ALL
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
expect(execaMock).toHaveBeenCalledWith(
|
||||
|
|
@ -81,9 +84,49 @@ describe("ExecaTerminalProcess", () => {
|
|||
expect(calledOptions.env.EXISTING_VAR).toBe("existing")
|
||||
})
|
||||
|
||||
it("should override existing LANG and LC_ALL values", async () => {
|
||||
process.env.LANG = "C"
|
||||
process.env.LC_ALL = "POSIX"
|
||||
it("should convert Chinese GBK locale to UTF-8", async () => {
|
||||
process.env.LANG = "zh_CN.GBK"
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
const calledOptions = execaMock.mock.calls[0][0] as any
|
||||
expect(calledOptions.env.LANG).toBe("zh_CN.UTF-8")
|
||||
expect(calledOptions.env.LC_ALL).toBe("zh_CN.UTF-8")
|
||||
})
|
||||
|
||||
it("should convert Japanese locale to UTF-8", async () => {
|
||||
process.env.LANG = "ja_JP.SJIS"
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
const calledOptions = execaMock.mock.calls[0][0] as any
|
||||
expect(calledOptions.env.LANG).toBe("ja_JP.UTF-8")
|
||||
expect(calledOptions.env.LC_ALL).toBe("ja_JP.UTF-8")
|
||||
})
|
||||
|
||||
it("should handle locale from LC_ALL when LANG is not set", async () => {
|
||||
delete process.env.LANG
|
||||
process.env.LC_ALL = "ko_KR.EUC-KR"
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
const calledOptions = execaMock.mock.calls[0][0] as any
|
||||
expect(calledOptions.env.LANG).toBe("ko_KR.UTF-8")
|
||||
expect(calledOptions.env.LC_ALL).toBe("ko_KR.UTF-8")
|
||||
})
|
||||
|
||||
it("should handle already UTF-8 locale", async () => {
|
||||
process.env.LANG = "zh_CN.UTF-8"
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
const calledOptions = execaMock.mock.calls[0][0] as any
|
||||
expect(calledOptions.env.LANG).toBe("zh_CN.UTF-8")
|
||||
expect(calledOptions.env.LC_ALL).toBe("zh_CN.UTF-8")
|
||||
})
|
||||
|
||||
it("should fallback to en_US.UTF-8 for invalid locale format", async () => {
|
||||
process.env.LANG = "invalid_locale"
|
||||
terminalProcess = new ExecaTerminalProcess(mockTerminal)
|
||||
await terminalProcess.run("echo test")
|
||||
const execaMock = vitest.mocked(execa)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue