Skip to content

Feature: localizable sandboxed plugins (manifest locales + api.i18n.t)#370

Merged
rathlinus merged 1 commit into
bulwarkmail:mainfrom
dealerweb:pr/localizable-plugins
May 30, 2026
Merged

Feature: localizable sandboxed plugins (manifest locales + api.i18n.t)#370
rathlinus merged 1 commit into
bulwarkmail:mainfrom
dealerweb:pr/localizable-plugins

Conversation

@dealerweb
Copy link
Copy Markdown
Contributor

Summary

The sandbox already received the active locale (init payload + 'locale-change') and plugins could declare a locales map, but it was unusable: the locales never reached the runtime and buildPluginApi exposed no i18n, so plugin UI was stuck in one language. This wires it up end-to-end and adds two related plugin-dialog fixes.

Changes

  • i18n: ServerPlugin gains locales; the upload route persists manifest.locales and /api/plugins surfaces it, so it flows registry -> client -> sandbox. The runtime sets __PLUGIN_LOCALE__ at init and exposes api.i18n.locale + api.i18n.t(key, vars) (resolves against the plugin's manifest.locales, English/key fallback, {placeholder} interpolation). The sandbox locale is wired to the app locale in initializePlugins. Plugin metadata sync is centralised in one serverMeta() mapper so passthrough fields (incl. locales) can't be silently dropped.
  • Fix: interactive ui.confirm / ui.alert no longer time out after 30s while waiting for the user (the generic callApi timeout is opt-out for these).
  • Fix: the host-rendered plugin dialog now follows the app theme (it referenced non-existent --background/etc.; corrected to the real --color-* tokens), wraps long unbreakable text, and supports lightweight **bold** in messages.

Type of Change

  • New feature (also includes two related plugin-dialog bug fixes)

Checklist

  • I have read the Contributing Guide
  • My code follows the project's code style and conventions
  • I have run npm run typecheck && npm run lint and there are no errors
  • The build passes (npm run build)
  • I have tested my changes locally
  • I have updated translations (locales/) if my changes affect user-facing text

Notes for Reviewers

Real-world consumer: our localized fork of the "External Link Warning" plugin uses api.i18n.t for its dialog - https://github.com/dealerweb/bulwark-plugin-external-link-warning . The plugin-store test suite passes; plugin-store keeps the sandbox locale in step via the plugin-loader facade rather than importing lib/plugin-sandbox/loader directly.

The plugin runtime received the active locale (init payload + 'locale-change')
and plugins could declare a `locales` map, but none of it was usable: the
locales never reached the runtime, and buildPluginApi exposed no i18n. So
plugin code calling pluginApi.i18n.t(...) (as the External Link Warning plugin
does) always got undefined and fell back to English.

Thread plugin locales end to end and surface an i18n API:
- ServerPlugin gains `locales`; the upload route persists manifest.locales
  (alongside configSchema/settingsSchema), and /api/plugins surfaces it to the
  client so it flows registry -> client -> sandbox host-bridge -> runtime.
- runtime sets __PLUGIN_LOCALE__ at init (not only on later 'locale-change')
  and buildPluginApi exposes `i18n.locale` + `i18n.t(key, vars)` resolving
  against the plugin's declared locales (manifest.locales) with English/key
  fallback and {placeholder} interpolation.

Lets any sandboxed plugin localize its strings from its manifest.
@rathlinus rathlinus merged commit 2ba0003 into bulwarkmail:main May 30, 2026
@dealerweb dealerweb deleted the pr/localizable-plugins branch May 31, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants