fix: replace hardcoded locale.h constants with build-system TranslateC (#11920)

Replace hardcoded locale.h constants and extern function declarations
with build-system TranslateC, following the same pattern as pty.c.

This fixes LC_ALL being hardcoded to 6 (the musl/glibc implementation
value), which is implementation-defined and differs on Windows MSVC
(where LC_ALL is 0), causing `setlocale()` to crash with an invalid
parameter error.

## Changes

- Added `src/os/locale.c` — includes `locale.h` for TranslateC
- Added TranslateC step in `src/build/SharedDeps.zig` (same pattern as
pty.c)
- Replaced hardcoded constants and extern declarations in
`src/os/locale.zig` with `@import("locale-c")`

## AI disclosure

Claude Code was used to assist with debugging and identifying this
issue.
This commit is contained in:
Mitchell Hashimoto
2026-03-28 08:56:40 -07:00
committed by GitHub
3 changed files with 26 additions and 6 deletions

View File

@@ -135,6 +135,24 @@ pub fn add(
// Every exe needs the terminal options
self.config.terminalOptions().add(b, step.root_module);
// C imports for locale constants and functions
{
const c = b.addTranslateC(.{
.root_source_file = b.path("src/os/locale.c"),
.target = target,
.optimize = optimize,
});
if (target.result.os.tag.isDarwin()) {
const libc = try std.zig.LibCInstallation.findNative(.{
.allocator = b.allocator,
.target = &target.result,
.verbose = false,
});
c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? });
}
step.root_module.addImport("locale-c", c.createModule());
}
// C imports needed to manage/create PTYs
switch (target.result.os.tag) {
.freebsd,

1
src/os/locale.c Normal file
View File

@@ -0,0 +1 @@
#include <locale.h>

View File

@@ -210,9 +210,10 @@ fn preferredLanguageFromCocoa(
return slice[0 .. slice.len - 1 :0];
}
const LC_ALL: c_int = 6; // from locale.h
const LC_ALL_MASK: c_int = 0x7fffffff; // from locale.h
const locale_t = ?*anyopaque;
extern "c" fn setlocale(category: c_int, locale: ?[*]const u8) ?[*:0]u8;
extern "c" fn newlocale(category: c_int, locale: ?[*]const u8, base: locale_t) locale_t;
extern "c" fn freelocale(v: locale_t) void;
const c = @import("locale-c");
const LC_ALL: c_int = c.LC_ALL;
const LC_ALL_MASK: c_int = c.LC_ALL_MASK;
const locale_t = c.locale_t;
const setlocale = c.setlocale;
const newlocale = c.newlocale;
const freelocale = c.freelocale;