diff --git a/src/apprt/gtk/CloseDialog.zig b/src/apprt/gtk/CloseDialog.zig index 2077f9b76..da6a45a36 100644 --- a/src/apprt/gtk/CloseDialog.zig +++ b/src/apprt/gtk/CloseDialog.zig @@ -6,12 +6,12 @@ const gio = @import("gio"); const adw = @import("adw"); const gtk = @import("gtk"); +const i18n = @import("../../os/main.zig").i18n; const App = @import("App.zig"); const Window = @import("Window.zig"); const Tab = @import("Tab.zig"); const Surface = @import("Surface.zig"); const adwaita = @import("adwaita.zig"); -const i18n = @import("i18n.zig"); const log = std.log.scoped(.close_dialog); diff --git a/src/os/i18n.zig b/src/os/i18n.zig index 6ae8683fd..69b222da5 100644 --- a/src/os/i18n.zig +++ b/src/os/i18n.zig @@ -76,23 +76,25 @@ pub fn initGlobalDomain() error{OutOfMemory}!void { _ = textdomain(build_config.bundle_id) orelse return error.OutOfMemory; } -/// Finds the closest matching locale for a given language code. -pub fn closestLocaleForLanguage(lang: []const u8) ?[:0]const u8 { - for (locales) |locale| { - const idx = std.mem.indexOfScalar(u8, locale, '_') orelse continue; - if (std.mem.eql(u8, locale[0..idx], lang)) { - return locale; - } - } - - return null; -} - /// Translate a message for the Ghostty domain. pub fn _(msgid: [*:0]const u8) [*:0]const u8 { return dgettext(build_config.bundle_id, msgid); } +/// This can be called at any point a compile-time-known locale is +/// available. This will use comptime to verify the locale is supported. +pub fn staticLocale(comptime v: [*:0]const u8) [*:0]const u8 { + comptime { + for (locales) |locale| { + if (std.mem.eql(u8, locale, v)) { + return locale; + } + } + + @compileError("unsupported locale"); + } +} + // Manually include function definitions for the gettext functions // as libintl.h isn't always easily available (e.g. in musl) extern fn bindtextdomain(domainname: [*:0]const u8, dirname: [*:0]const u8) ?[*:0]const u8; diff --git a/src/os/locale.zig b/src/os/locale.zig index e96ab5e3e..840687143 100644 --- a/src/os/locale.zig +++ b/src/os/locale.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; const macos = @import("macos"); const objc = @import("objc"); const internal_os = @import("main.zig"); +const i18n = internal_os.i18n; const log = std.log.scoped(.os_locale); @@ -103,66 +104,6 @@ fn setLangFromCocoa() void { log.warn("error setting locale env var", .{}); return; } - - // We also want to set our LANGUAGE for translations. We do this using - // NSLocale.preferredLanguages over our system locale since we want to - // match our app's preferred languages. - language: { - const i18n = internal_os.i18n; - - // We need to get our app's preferred languages. These may not - // match the system locale (NSLocale.currentLocale). - const preferred: *macos.foundation.Array = array: { - const ns = NSLocale.msgSend( - objc.Object, - objc.sel("preferredLanguages"), - .{}, - ); - break :array @ptrCast(ns.value); - }; - for (0..preferred.getCount()) |i| { - const str = preferred.getValueAtIndex(macos.foundation.String, i); - const c_str = c_str: { - const raw = str.cstring(&buf, .utf8) orelse { - // I don't think this can happen but if it does then I want - // to know about it if a user has translation issues. - log.warn("failed to convert a preferred language to UTF-8", .{}); - continue; - }; - - // We want to strip at "-" since we only care about the language - // code, not the region code. i.e. "zh-Hans" -> "zh" - const idx = std.mem.indexOfScalar(u8, raw, '-') orelse raw.len; - break :c_str raw[0..idx]; - }; - - // If our preferred language is equal to our system language - // then we can be done, since the locale above we set everything. - if (std.mem.eql(u8, c_str, z_lang)) { - log.debug("preferred language matches system locale={s}", .{c_str}); - break :language; - } - - // Note: there are many improvements that can be made here to make - // this more and more robust. For example, we can try to search for - // the MOST matching supported locale for translations. Right now - // we fall directly back to language code. - log.debug("searching for closest matching locale preferred={s}", .{c_str}); - if (i18n.closestLocaleForLanguage(c_str)) |i18n_locale| { - log.info("setting LANGUAGE to closest matching locale={s}", .{i18n_locale}); - _ = internal_os.setenv("LANGUAGE", i18n_locale); - break :language; - } - } - - // No matches or our preferred languages are empty. As a final - // try we try to match our system locale. - if (i18n.closestLocaleForLanguage(z_lang)) |i18n_locale| { - log.info("setting LANGUAGE to closest matching locale={s}", .{i18n_locale}); - _ = internal_os.setenv("LANGUAGE", i18n_locale); - break :language; - } - } } const LC_ALL: c_int = 6; // from locale.h