mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
core/gtk: add language config entry to override GUI localization (#10428)
Fixes #10276 <img width="853" height="637" alt="Screenshot From 2026-01-23 16-45-11" src="https://github.com/user-attachments/assets/aff9d2f8-eb3e-411e-bd3d-ebd32e5c7973" />
This commit is contained in:
@@ -213,6 +213,11 @@ pub const Application = extern struct {
|
||||
/// Providers for loading custom stylesheets defined by user
|
||||
custom_css_providers: std.ArrayListUnmanaged(*gtk.CssProvider) = .empty,
|
||||
|
||||
/// A copy of the LANG environment variable that was provided to Ghostty
|
||||
/// by the system. If this is null, the LANG environment variable did
|
||||
/// not exist in Ghostty's environment variable.
|
||||
saved_language: ?[:0]const u8 = null,
|
||||
|
||||
pub var offset: c_int = 0;
|
||||
};
|
||||
|
||||
@@ -249,15 +254,6 @@ pub const Application = extern struct {
|
||||
gtk_version.logVersion();
|
||||
adw_version.logVersion();
|
||||
|
||||
// Set gettext global domain to be our app so that our unqualified
|
||||
// translations map to our translations.
|
||||
internal_os.i18n.initGlobalDomain() catch |err| {
|
||||
// Failures shuldn't stop application startup. Our app may
|
||||
// not translate correctly but it should still work. In the
|
||||
// future we may want to add this to the GUI to show.
|
||||
log.warn("i18n initialization failed error={}", .{err});
|
||||
};
|
||||
|
||||
// Load our configuration.
|
||||
var config = CoreConfig.load(alloc) catch |err| err: {
|
||||
// If we fail to load the configuration, then we should log
|
||||
@@ -275,6 +271,27 @@ pub const Application = extern struct {
|
||||
};
|
||||
defer config.deinit();
|
||||
|
||||
const saved_language: ?[:0]const u8 = saved_language: {
|
||||
const old_language = old_language: {
|
||||
const result = (internal_os.getenv(alloc, "LANG") catch break :old_language null) orelse break :old_language null;
|
||||
defer result.deinit(alloc);
|
||||
break :old_language alloc.dupeZ(u8, result.value) catch break :old_language null;
|
||||
};
|
||||
|
||||
if (config.language) |language| _ = internal_os.setenv("LANG", language);
|
||||
|
||||
break :saved_language old_language;
|
||||
};
|
||||
|
||||
// Set gettext global domain to be our app so that our unqualified
|
||||
// translations map to our translations.
|
||||
internal_os.i18n.initGlobalDomain() catch |err| {
|
||||
// Failures shuldn't stop application startup. Our app may
|
||||
// not translate correctly but it should still work. In the
|
||||
// future we may want to add this to the GUI to show.
|
||||
log.warn("i18n initialization failed error={}", .{err});
|
||||
};
|
||||
|
||||
// Setup our GTK init env vars
|
||||
setGtkEnv(&config) catch |err| switch (err) {
|
||||
error.NoSpaceLeft => {
|
||||
@@ -374,7 +391,7 @@ pub const Application = extern struct {
|
||||
// Setup our private state. More setup is done in the init
|
||||
// callback that GObject calls, but we can't pass this data through
|
||||
// to there (and we don't need it there directly) so this is here.
|
||||
const priv = self.private();
|
||||
const priv: *Private = self.private();
|
||||
priv.* = .{
|
||||
.rt_app = rt_app,
|
||||
.core_app = core_app,
|
||||
@@ -383,6 +400,7 @@ pub const Application = extern struct {
|
||||
.css_provider = css_provider,
|
||||
.custom_css_providers = .empty,
|
||||
.global_shortcuts = gobject.ext.newInstance(GlobalShortcuts, .{}),
|
||||
.saved_language = saved_language,
|
||||
};
|
||||
|
||||
// Signals
|
||||
@@ -415,11 +433,12 @@ pub const Application = extern struct {
|
||||
/// ensures that our memory is cleaned up properly.
|
||||
pub fn deinit(self: *Self) void {
|
||||
const alloc = self.allocator();
|
||||
const priv = self.private();
|
||||
const priv: *Private = self.private();
|
||||
priv.config.unref();
|
||||
priv.winproto.deinit(alloc);
|
||||
priv.global_shortcuts.unref();
|
||||
if (priv.transient_cgroup_base) |base| alloc.free(base);
|
||||
if (priv.saved_language) |language| alloc.free(language);
|
||||
if (gdk.Display.getDefault()) |display| {
|
||||
gtk.StyleContext.removeProviderForDisplay(
|
||||
display,
|
||||
@@ -445,6 +464,12 @@ pub const Application = extern struct {
|
||||
return self.private().core_app.alloc;
|
||||
}
|
||||
|
||||
/// Get the original language that Ghostty was launched with. This returns a
|
||||
/// pointer to internal memory so it must be copied by callers.
|
||||
pub fn savedLanguage(self: *Self) ?[:0]const u8 {
|
||||
return self.private().saved_language;
|
||||
}
|
||||
|
||||
/// Run the application. This is a replacement for `gio.Application.run`
|
||||
/// because we want more tight control over our event loop so we can
|
||||
/// integrate it with libghostty.
|
||||
|
||||
@@ -1595,10 +1595,17 @@ pub const Surface = extern struct {
|
||||
}
|
||||
|
||||
pub fn defaultTermioEnv(self: *Self) !std.process.EnvMap {
|
||||
const alloc = Application.default().allocator();
|
||||
const app = Application.default();
|
||||
const alloc = app.allocator();
|
||||
var env = try internal_os.getEnvMap(alloc);
|
||||
errdefer env.deinit();
|
||||
|
||||
if (app.savedLanguage()) |language| {
|
||||
try env.put("LANG", language);
|
||||
} else {
|
||||
env.remove("LANG");
|
||||
}
|
||||
|
||||
// Don't leak these GTK environment variables to child processes.
|
||||
env.remove("GDK_DEBUG");
|
||||
env.remove("GDK_DISABLE");
|
||||
|
||||
@@ -94,6 +94,27 @@ pub const compatibility = std.StaticStringMap(
|
||||
.{ "macos-dock-drop-behavior", compatMacOSDockDropBehavior },
|
||||
});
|
||||
|
||||
/// Set Ghostty's graphical user interface language to a language other than the
|
||||
/// system default language. The language must be fully specified, including the
|
||||
/// encoding. For example:
|
||||
///
|
||||
/// language = de_DE.UTF-8
|
||||
///
|
||||
/// will force the strings in Ghostty's graphical user interface to be in German
|
||||
/// rather than the system default.
|
||||
///
|
||||
/// This will not affect the language used by programs run _within_ Ghostty.
|
||||
/// Those will continue to use the default system language. There are also many
|
||||
/// non-GUI elements in Ghostty that are not translated - this setting will have
|
||||
/// no effect on those.
|
||||
///
|
||||
/// Warning: This setting cannot be reloaded at runtime. To change the language
|
||||
/// you must fully restart Ghostty.
|
||||
///
|
||||
/// GTK only.
|
||||
/// Available since 1.3.0.
|
||||
language: ?[:0]const u8 = null,
|
||||
|
||||
/// The font families to use.
|
||||
///
|
||||
/// You can generate the list of valid values using the CLI:
|
||||
|
||||
Reference in New Issue
Block a user