Before Requesting
Describe the feature you'd like!
You said a plugin localization system could work but would need proper planning, so here it is. The short version: I want third parties to be able to ship translations for plugins they don't own, and for that a plugin needs to be able to depend on another and load after it. So dependencies come first, localization builds on top. This is the dependency design - I want your take before I write any code, and it would land in both Millennium and the PluginTemplate.
Add an optional dependencies to plugin.json - a list of internal plugin names (the same name the loader already uses), with an optional semver range since CMP_VERSION already exists:
On install. No auto-installing anything - same stance you took on the sidebar PR. If a plugin has missing dependencies, the installer shows a dialog listing them and lets the user install anyway or cancel. The plugin is expected to degrade gracefully if a dependency is absent.
Resolution. Check what's actually on disk, so a dependency someone installed manually counts. Three states: missing, present but disabled, present and enabled. Each dependency name is resolved to its page via the same plugin API the installer uses, shown as a link; if the lookup fails, show the raw id.
Load order. Backends already all start before any frontend, but the order among them is arbitrary, so I'd topologically sort the spawn order (cycle detection falls back to current order with a warning). Frontends have no ordering at all today since the shims are appended as async module scripts, so I'd sort them in dependency order and make injection ordered. Plus a Millennium.whenPluginReady(name) as a safety net.
openQuickAccess({ plugin }) fits here as a compatibility affordance: a plugin can deep-link into a dependency's settings panel, including from the dependency dialog.
How localization uses this (for context): a plugin ships a locales/ folder with english.json as the source of truth, plus a small helper in the SDK (same pattern as the core locale manager). A translation plugin then depends on the target and contributes its strings to a shared registry in core - it has to be in core because plugins run in isolated worlds and can't patch each other directly. The target's lookup merges its own strings with any contributed overrides. No target installed means the translation plugin is a no-op. I'm not trying to extract strings from old plugins; a plugin is localizable once it adopts the system.
A few open questions:
- Dependencies referencing internal
name with optional name@range - good, or bare names only for v1?
- Warn-and-allow vs hard block on missing deps. I lean warn.
- The ordered frontend loading touches the loader and shim injection - want me to take a pass at it, or own that part yourself like you did with quick access?
Anything else?
References for context:
Millennium.openQuickAccess({ plugin }) from the quick access work in next.
- Version comparison already wired through the
CMP_VERSION IPC in the plugin loader.
- The core locale manager (
src/typescript/frontend/utils/localization-manager.ts) as the pattern for the per-plugin helper.
Before Requesting
Describe the feature you'd like!
You said a plugin localization system could work but would need proper planning, so here it is. The short version: I want third parties to be able to ship translations for plugins they don't own, and for that a plugin needs to be able to depend on another and load after it. So dependencies come first, localization builds on top. This is the dependency design - I want your take before I write any code, and it would land in both Millennium and the PluginTemplate.
Add an optional
dependenciesto plugin.json - a list of internal plugin names (the samenamethe loader already uses), with an optional semver range since CMP_VERSION already exists:On install. No auto-installing anything - same stance you took on the sidebar PR. If a plugin has missing dependencies, the installer shows a dialog listing them and lets the user install anyway or cancel. The plugin is expected to degrade gracefully if a dependency is absent.
Resolution. Check what's actually on disk, so a dependency someone installed manually counts. Three states: missing, present but disabled, present and enabled. Each dependency name is resolved to its page via the same plugin API the installer uses, shown as a link; if the lookup fails, show the raw id.
Load order. Backends already all start before any frontend, but the order among them is arbitrary, so I'd topologically sort the spawn order (cycle detection falls back to current order with a warning). Frontends have no ordering at all today since the shims are appended as async module scripts, so I'd sort them in dependency order and make injection ordered. Plus a
Millennium.whenPluginReady(name)as a safety net.openQuickAccess({ plugin })fits here as a compatibility affordance: a plugin can deep-link into a dependency's settings panel, including from the dependency dialog.How localization uses this (for context): a plugin ships a
locales/folder withenglish.jsonas the source of truth, plus a small helper in the SDK (same pattern as the core locale manager). A translation plugin then depends on the target and contributes its strings to a shared registry in core - it has to be in core because plugins run in isolated worlds and can't patch each other directly. The target's lookup merges its own strings with any contributed overrides. No target installed means the translation plugin is a no-op. I'm not trying to extract strings from old plugins; a plugin is localizable once it adopts the system.A few open questions:
namewith optionalname@range- good, or bare names only for v1?Anything else?
References for context:
Millennium.openQuickAccess({ plugin })from the quick access work innext.CMP_VERSIONIPC in the plugin loader.src/typescript/frontend/utils/localization-manager.ts) as the pattern for the per-plugin helper.