@gjsify/node-gi 0.64.4 → 0.64.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/gi.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  clearInterval,
18
18
  runtimeProcess,
19
19
  windowingEnvWrites,
20
+ bundledLocaleDir,
20
21
  FOREIGN_PARENT_CLASSES,
21
22
  } from '#host';
22
23
  import { createGioDBus } from './overrides/gio-dbus.js';
@@ -1289,6 +1290,7 @@ function flushPropertiesToJsSetters(instance, handle, reg) {
1289
1290
  // process loop) — for concurrent runtime-loop + GLib work there, drive GLib with
1290
1291
  // startMainContextPump instead of a blocking run.
1291
1292
  function applicationRunAsync(handle) {
1293
+ armBundledTextDomains(handle);
1292
1294
  return new Promise((resolve, reject) => {
1293
1295
  setImmediate(() => {
1294
1296
  try {
@@ -1300,6 +1302,37 @@ function applicationRunAsync(handle) {
1300
1302
  });
1301
1303
  }
1302
1304
 
1305
+ // ---- the GTK runtime bundle's own catalogs ----
1306
+ //
1307
+ // gtk_init, adw_init, gtk_source_init and GLib's first translated message each call
1308
+ // bindtextdomain(<domain>, <dir compiled into the build host>) — on the darwin bundle a
1309
+ // Homebrew Cellar path a user's Mac does not have, so GTK's own strings (Adw widgets, entry
1310
+ // context menus, shortcuts) stayed English under a German locale. Measured: the domains read
1311
+ // /opt/homebrew/Cellar/{gtk4,libadwaita}/… in a `startup` handler and NOT before `run()`, so
1312
+ // any earlier binding is undone. Armed on `register` and `run` (either emits `startup`), the
1313
+ // handler runs right after GApplication's
1314
+ // RUN_FIRST class closure (where those inits happen) and before `activate` builds the UI.
1315
+ // An app that builds widgets in its OWN startup handler connected before `run()` sees them
1316
+ // first, untranslated. Armed only when the loader found catalogs in the bundle.
1317
+ const BUNDLED_TEXT_DOMAINS = ['gtk40', 'libadwaita', 'glib20', 'gtksourceview-5'];
1318
+ const textDomainsArmed = new WeakSet();
1319
+
1320
+ function armBundledTextDomains(handle) {
1321
+ const dir = bundledLocaleDir();
1322
+ if (dir === undefined || textDomainsArmed.has(handle)) return;
1323
+ // `run` is not only GApplication's (GtkDialog, GTask, …), and only it emits `startup`.
1324
+ if (!native.isInstanceOf(handle, 'Gio', 'Application')) return;
1325
+ textDomainsArmed.add(handle);
1326
+ native.connectSignal(
1327
+ handle,
1328
+ 'startup',
1329
+ wrapSignalCallback(() => {
1330
+ for (const domain of BUNDLED_TEXT_DOMAINS) native.bindtextdomain(domain, dir);
1331
+ }),
1332
+ false,
1333
+ );
1334
+ }
1335
+
1303
1336
  // ---- portable GLib main-context pump (Bun/Deno) ----
1304
1337
  //
1305
1338
  // On Node the uv-in-GLib bridge co-pumps libuv during a blocking GLib loop, so a
@@ -1676,7 +1709,10 @@ function wrapInstance(handle, userProto) {
1676
1709
  let thunk = giMethodThunks[prop];
1677
1710
  if (thunk === undefined) {
1678
1711
  thunk = instanceHasMethod(handle, prop)
1679
- ? (...args) => wrapReturn(native.callMethod(handle, prop, unwrapArgs(args)))
1712
+ ? (...args) => {
1713
+ if (prop === 'run' || prop === 'register') armBundledTextDomains(handle);
1714
+ return wrapReturn(native.callMethod(handle, prop, unwrapArgs(args)));
1715
+ }
1680
1716
  : null;
1681
1717
  giMethodThunks[prop] = thunk;
1682
1718
  }
@@ -1979,6 +2015,7 @@ function makeGiMethod(where, method, arity) {
1979
2015
  // the registration is honoured here, where the instance is known.
1980
2016
  const promisified = promisifiedMethods.size === 0 ? undefined : resolveRegistered(handle, method);
1981
2017
  if (promisified !== undefined) return promisified.apply(this, args);
2018
+ if (method === 'run' || method === 'register') armBundledTextDomains(handle);
1982
2019
  return wrapReturn(native.callMethod(handle, method, unwrapArgs(args)));
1983
2020
  };
1984
2021
  nativeThunks.add(fn);
package/gtk-runtime.js CHANGED
@@ -46,6 +46,14 @@ let windowingEnvWritesMade = [];
46
46
  * Empty off win32 (POSIX `setenv()` has one environment) and empty when the wiring no-opped.
47
47
  * @returns {Array<[string, string]>}
48
48
  */
49
+ /** Where the windowing bundle's own catalogs live, or `undefined` (see `armBundledTextDomains` in gi.js). */
50
+ let bundleLocaleDir;
51
+
52
+ /** @returns {string | undefined} */
53
+ export function bundledLocaleDir() {
54
+ return bundleLocaleDir;
55
+ }
56
+
49
57
  export function windowingEnvWrites() {
50
58
  return process.platform === 'win32' ? windowingEnvWritesMade.slice() : [];
51
59
  }
@@ -484,6 +492,14 @@ export function maybeWireGtkWindowingEnv() {
484
492
  }
485
493
  }
486
494
 
495
+ // The catalogs the bundle ships for its own libraries. Binding them HERE would be undone:
496
+ // gtk_init, adw_init and GLib's first translated message each call
497
+ // bindtextdomain(<domain>, <path compiled into the build host>) themselves, and that path
498
+ // does not exist on a Mac without Homebrew. So only the directory is recorded here;
499
+ // gi.js re-binds the domains once those inits have run (armBundledTextDomains).
500
+ const localeDir = join(bundle.dir, 'share', 'locale');
501
+ bundleLocaleDir = existsSync(localeDir) ? localeDir : undefined;
502
+
487
503
  const fontsConf = join(bundle.dir, 'etc', 'fonts', 'fonts.conf');
488
504
  if (existsSync(fontsConf)) {
489
505
  setIfUnset('FONTCONFIG_PATH', join(bundle.dir, 'etc', 'fonts'));
@@ -99,6 +99,10 @@ export function windowingEnvWrites() {
99
99
  return [];
100
100
  }
101
101
 
102
+ export function bundledLocaleDir() {
103
+ return undefined;
104
+ }
105
+
102
106
  // NativeScript's runtime class hands out the Application without @nativescript/core
103
107
  // (android.app.ActivityThread is a hidden API and absent from the metadata).
104
108
  function deriveAppDir() {
package/host.node.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  maybeReexecForGtkRuntime,
19
19
  maybeWireGtkWindowingEnv,
20
20
  windowingEnvWrites,
21
+ bundledLocaleDir,
21
22
  } from './gtk-runtime.js';
22
23
  import { activateNativePrebuilds } from './native-prebuilds.js';
23
24
  // From node:timers, not the globals, for two reasons: setImmediate is a Node/Bun global
@@ -31,7 +32,7 @@ import { setImmediate, setInterval, clearInterval } from 'node:timers';
31
32
  // loop-liveness hooks in gi.js would silently never fire.
32
33
  import runtimeProcess from 'node:process';
33
34
 
34
- export { setImmediate, setInterval, clearInterval, runtimeProcess, windowingEnvWrites };
35
+ export { setImmediate, setInterval, clearInterval, runtimeProcess, windowingEnvWrites, bundledLocaleDir };
35
36
 
36
37
  const require = createRequire(import.meta.url);
37
38
  const here = packageRoot;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/node-gi",
3
- "version": "0.64.4",
3
+ "version": "0.64.5",
4
4
  "description": "GObject-Introspection runtime for Node.js — load gi:// namespaces (GLib/GObject/Gio/…) with GJS-compatible semantics. Vendored + rewritten from node-gtk under MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
Binary file
Binary file
Binary file
Binary file