fix(plugins): wire up translation loading to i18n service (CRITICAL)

Fixes the critical issue where translations were loaded but never
passed to PluginI18nService, making the i18n system non-functional.

Changes:
- Inject PluginI18nService in PluginService
- Load translations into i18n service in 3 locations:
  - _loadPluginLazy() for lazy-loaded plugins
  - _loadPlugin() for file-based plugins
  - _loadUploadedPlugin() for cached plugins
- Improve LANGUAGE_CHANGE hook type guard
  - Use explicit LanguageCode type predicate
  - Remove non-null assertion (no longer needed)

This makes api.translate() functional for all plugin loading paths.
This commit is contained in:
Johannes Millan 2026-01-16 18:48:41 +01:00
parent 5caef4132f
commit 7f4e5381d0
2 changed files with 32 additions and 5 deletions

View file

@ -45,6 +45,7 @@ import {
} from '../features/work-context/store/work-context-meta.actions';
import { LOCAL_ACTIONS } from '../util/local-actions.token';
import { PlannerActions } from '../features/planner/store/planner.actions';
import { LanguageCode } from '../core/locale.constants';
@Injectable()
export class PluginHooksEffects {
@ -210,11 +211,11 @@ export class PluginHooksEffects {
filter((action) => action.sectionKey === 'localization'),
withLatestFrom(this.store.pipe(select(selectLocalizationConfig))),
map(([_, localizationConfig]) => localizationConfig.lng),
filter((lng) => !!lng),
filter((lng): lng is LanguageCode => typeof lng === 'string' && lng.length > 0),
distinctUntilChanged(),
tap((newLanguage) => {
// Update plugin i18n service with new language
this.pluginI18nService.setCurrentLanguage(newLanguage!);
this.pluginI18nService.setCurrentLanguage(newLanguage);
// Dispatch hook to notify plugins
this.pluginService.dispatchHook(PluginHooks.LANGUAGE_CHANGE, {

View file

@ -31,6 +31,7 @@ import { validatePluginManifest } from './util/validate-manifest.util';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../t.const';
import { PluginLog } from '../core/log';
import { PluginI18nService } from './plugin-i18n.service';
@Injectable({
providedIn: 'root',
@ -48,6 +49,7 @@ export class PluginService implements OnDestroy {
private readonly _cleanupService = inject(PluginCleanupService);
private readonly _pluginLoader = inject(PluginLoaderService);
private readonly _translateService = inject(TranslateService);
private readonly _pluginI18nService = inject(PluginI18nService);
private _isInitialized = false;
private _loadedPlugins: PluginInstance[] = [];
@ -373,13 +375,21 @@ export class PluginService implements OnDestroy {
private async _loadPluginLazy(state: PluginState): Promise<PluginInstance> {
// Load the plugin code and assets
const assets = await this._pluginLoader.loadPluginAssets(state.path);
const { code: pluginCode, indexHtml } = assets;
const { code: pluginCode, indexHtml, translations } = assets;
// Store assets
if (indexHtml) {
this._pluginIndexHtml.set(state.manifest.id, indexHtml);
}
// Load translations into i18n service
if (translations && Object.keys(translations).length > 0) {
this._pluginI18nService.loadPluginTranslationsFromContent(
state.manifest.id,
translations,
);
}
// Create base config
const baseCfg = this._getBaseCfg();
@ -454,7 +464,7 @@ export class PluginService implements OnDestroy {
try {
// Use the loader service for lazy loading
const assets = await this._pluginLoader.loadPluginAssets(pluginPath);
const { manifest, code: pluginCode, indexHtml, icon } = assets;
const { manifest, code: pluginCode, indexHtml, icon, translations } = assets;
// Store assets if loaded
if (indexHtml) {
@ -466,6 +476,14 @@ export class PluginService implements OnDestroy {
this._pluginIconsSignal.set(new Map(this._pluginIcons));
}
// Load translations into i18n service
if (translations && Object.keys(translations).length > 0) {
this._pluginI18nService.loadPluginTranslationsFromContent(
manifest.id,
translations,
);
}
// Check if plugin should be loaded based on persisted enabled state
const isPluginEnabled = await this._pluginMetaPersistenceService.isPluginEnabled(
manifest.id,
@ -1210,7 +1228,7 @@ export class PluginService implements OnDestroy {
try {
// Use the loader service for uploaded plugins
const assets = await this._pluginLoader.loadUploadedPluginAssets(pluginId);
const { manifest, code: pluginCode, indexHtml, icon } = assets;
const { manifest, code: pluginCode, indexHtml, icon, translations } = assets;
// Store assets if loaded
if (indexHtml) {
@ -1222,6 +1240,14 @@ export class PluginService implements OnDestroy {
this._pluginIconsSignal.set(new Map(this._pluginIcons));
}
// Load translations into i18n service
if (translations && Object.keys(translations).length > 0) {
this._pluginI18nService.loadPluginTranslationsFromContent(
manifest.id,
translations,
);
}
// Validate manifest
const manifestValidation = validatePluginManifest(manifest);
if (!manifestValidation.isValid) {