diff --git a/apps/desktop/electron/terminal-ipc.ts b/apps/desktop/electron/terminal-ipc.ts index 8a4eb61118..92f2560c32 100644 --- a/apps/desktop/electron/terminal-ipc.ts +++ b/apps/desktop/electron/terminal-ipc.ts @@ -31,6 +31,21 @@ export interface TerminalIpcApi { disposeAllTerminalSessions: () => void } +// macOS accepts the bare charset name "UTF-8" as a locale; glibc does not, so +// every Linux pane would print `bash: warning: setlocale: LC_CTYPE: cannot +// change locale (UTF-8)`. Reuse the user's LANG there, else the glibc-guaranteed +// C.UTF-8. Pure: the platform arrives as data so tests need not fake the host. +export function terminalLcCtype( + env: { LANG?: string; LC_CTYPE?: string }, + platform: NodeJS.Platform = process.platform +): string { + if (env.LC_CTYPE) { + return env.LC_CTYPE + } + + return platform === 'darwin' ? 'UTF-8' : env.LANG || 'C.UTF-8' +} + export function registerTerminalIpc({ isWindows, findOnPath, @@ -156,10 +171,7 @@ export function registerTerminalIpc({ delete env.COLORFGBG env.COLORTERM = 'truecolor' - // macOS accepts the bare charset name "UTF-8"; glibc does not, and bash - // warns: setlocale: LC_CTYPE: cannot change locale (UTF-8). Prefer LANG or C.UTF-8 on Linux. - env.LC_CTYPE = - env.LC_CTYPE || (process.platform === 'darwin' ? 'UTF-8' : env.LANG || 'C.UTF-8') + env.LC_CTYPE = terminalLcCtype(env) env.TERM = 'xterm-256color' env.TERM_PROGRAM = 'Hermes' env.TERM_PROGRAM_VERSION = app.getVersion() diff --git a/apps/desktop/electron/terminal-lc-ctype.test.ts b/apps/desktop/electron/terminal-lc-ctype.test.ts new file mode 100644 index 0000000000..f05d1227f4 --- /dev/null +++ b/apps/desktop/electron/terminal-lc-ctype.test.ts @@ -0,0 +1,17 @@ +import assert from 'node:assert/strict' + +import { test, vi } from 'vitest' + +// terminal-ipc.ts imports the Electron and native PTY modules at module scope; +// neither loads under plain node, and this test only needs the pure locale helper. +vi.mock('electron', () => ({ app: {}, ipcMain: {} })) +vi.mock('node-pty', () => ({ default: {} })) + +import { terminalLcCtype } from './terminal-ipc' + +test('terminalLcCtype never hands glibc the bare "UTF-8" locale on Linux', () => { + assert.equal(terminalLcCtype({ LANG: 'en_US.UTF-8' }, 'linux'), 'en_US.UTF-8') + assert.equal(terminalLcCtype({}, 'linux'), 'C.UTF-8') + assert.equal(terminalLcCtype({ LANG: 'en_US.UTF-8', LC_CTYPE: 'de_DE.UTF-8' }, 'linux'), 'de_DE.UTF-8') + assert.equal(terminalLcCtype({ LANG: 'en_US.UTF-8' }, 'darwin'), 'UTF-8') +})