You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular项目基于依赖注入实现国际化时,Header组件注入TRANSLATION令牌出现NullInjectorError问题解决

Fixing the NullInjectorError for TRANSLATION Token in Header Component

Let's break down why you're seeing this error and how to fix it properly.

Root Cause

The TRANSLATION token is only provided in your lazy-loaded language modules (SiteEnModule, SiteFrModule). These modules create their own child injectors, so components inside them (like SiteComponent) can access the token. However, your HeaderComponent is rendered in AppComponent, which belongs to the root AppModule injector. Since AppModule doesn't have a provider for TRANSLATION, Angular throws the NullInjectorError when trying to inject it into HeaderComponent.

Solution: Use a Shared Translation Service

The cleanest way to make translations available to all components (including those in the root injector) is to create a singleton service that dynamically loads and exposes the current language's translations based on the active route.

Step 1: Create a TranslationService

This service will observe the router to detect the current language, load the corresponding translation file, and provide an observable for components to subscribe to:

import { Injectable } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs/operators';
import { WebsiteLanguage, Translation } from './path/to/translation';
import { en } from './path/to/en.translation';
import { fr } from './path/to/fr.translation';

@Injectable({ providedIn: 'root' })
export class TranslationService {
  private translationSubject = new BehaviorSubject<Translation>(en); // Default to English
  currentTranslation$: Observable<Translation> = this.translationSubject.asObservable();

  constructor(private router: Router, private activatedRoute: ActivatedRoute) {
    this.initTranslationUpdates();
  }

  private initTranslationUpdates(): void {
    this.router.events.pipe(
      // Extract the language from the root route
      map(() => {
        let route = this.activatedRoute.root;
        while (route.firstChild) route = route.firstChild;
        return route.snapshot.url[0]?.path || WebsiteLanguage.English;
      }),
      distinctUntilChanged() // Only update if language changes
    ).subscribe(lang => {
      const translation = this.getTranslationForLang(lang as WebsiteLanguage);
      this.translationSubject.next(translation);
    });
  }

  private getTranslationForLang(lang: WebsiteLanguage): Translation {
    switch (lang) {
      case WebsiteLanguage.French:
        return fr;
      case WebsiteLanguage.English:
      default:
        return en;
    }
  }

  // Optional: Get current translation synchronously if needed
  getCurrentTranslation(): Translation {
    return this.translationSubject.value;
  }
}

Step 2: Update HeaderComponent to Use the Service

Replace the direct TRANSLATION injection with the service:

import { Component } from '@angular/core';
import { TranslationService } from './path/to/translation.service';
import { Translation } from './path/to/translation';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html'
})
export class HeaderComponent {
  currentTranslation: Translation;

  constructor(private translationService: TranslationService) {
    this.translationService.currentTranslation$.subscribe(trans => {
      this.currentTranslation = trans;
      console.log('Current language:', trans.language);
    });
  }
}

Step 3: Update Other Components (Optional)

You can migrate your SiteComponent and other lazy-loaded components to use the service too, for consistency:

// In SiteComponent
export class SiteComponent implements OnInit {
  translations = Object.entries(WebsiteLanguage);
  currentTranslation: Translation;

  constructor(private translationService: TranslationService) {
    this.translationService.currentTranslation$.subscribe(trans => {
      this.currentTranslation = trans;
      console.log('Current language:', trans.language);
    });
  }

  ngOnInit(): void {}
}

Step 4: Clean Up (Optional)

If you migrate all components to use the service, you can remove the TRANSLATION provider from your lazy-loaded language modules (SiteEnModule, SiteFrModule).

Alternative Approach: Move Header to Lazy-Loaded Context

If you prefer to keep using direct DI for TRANSLATION, you can:

  1. Remove HeaderModule from AppModule imports
  2. Add HeaderComponent to the declarations of SiteModule (or language-specific modules)
  3. Move <app-header> from app.component.html to site.component.html

This way, HeaderComponent will be part of the lazy-loaded module's injector and can access the TRANSLATION token. However, this means the header will only render when the user is on an /en or /fr route (which works for your setup since you redirect all paths to /en).

内容的提问来源于stack exchange,提问作者davidvera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:02:38