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

Angular 16 SSR集成TranslationService出现NetworkError求助

Angular 16 SSR下注入TranslationService触发NetworkError问题

我使用Angular 16结合Angular/Universal实现服务端渲染(SSR),开发了TranslationService服务。在AppComponent的构造函数中注入该服务后,触发了NetworkError错误,错误栈如下:

NetworkError
    at send (./node_modules/xhr2/lib/xhr2.js:281:19)
    at observer (./node_modules/@angular/common/fesm2022/http.mjs:2187:21)
    at _trySubscribe (./node_modules/rxjs/dist/cjs/internal/Observable.js:41:25)
    at cb (./node_modules/rxjs/dist/cjs/internal/Observable.js:35:31)
    at errorContext (./node_modules/rxjs/dist/cjs/internal/util/errorContext.js:22:9)
    at subscribe (./node_modules/rxjs/dist/cjs/internal/Observable.js:26:24)
    at onNext (./node_modules/rxjs/dist/cjs/internal/operators/switchMap.js:17:63)
    at _next (./node_modules/rxjs/dist/cjs/internal/operators/OperatorSubscriber.js:33:21)
    at next (./node_modules/rxjs/dist/cjs/internal/Subscriber.js:51:18)
    at <anonymous> (./node_modules/rxjs/dist/cjs/internal/observable/innerFrom.js:120:28)

项目在客户端运行完全正常,但启用SSR时出现该错误。以下是相关代码文件:

TranslationService代码

import { Injectable, ViewChild, AfterViewInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
// import { AngularFirestore } from '@angular/fire/compat/firestore';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class TranslationService implements AfterViewInit {
  public lang = new BehaviorSubject<any>('');
  value: any;
  @ViewChild('body') body: any;

  constructor(public translate: TranslateService) {
    this.lang.next('en');
  }
  ngAfterViewInit(): void {}
  changeLanguage(value: any) {
    this.translate.setDefaultLang(value);
    localStorage.setItem('lang', value);
    this.lang.next(value);
    this.value = value;

    if (value == 'ar') {
      document.documentElement.style.setProperty('direction', 'rtl');
      document.documentElement.style.setProperty('overflow-x', 'hidden');
      document.documentElement.style.setProperty('width', '101%');
    } else {
      document.documentElement.style.setProperty('direction', 'ltr');
      document.documentElement.style.setProperty('width', '100%');
    }
  }
}

AppModule代码

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { environment } from 'src/environments/environment';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreService } from '../services/core.service';
import { HttpClientModule, HttpClient } from '@angular/common/http';

import {
  TranslateModule,
  TranslateLoader,
  TranslateStore,
} from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

export function HttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http);
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
    AppRoutingModule,
    HttpClientModule,
    TranslateModule.forChild({
      defaultLanguage: 'en',
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient],
      },
    }),
  ],
  providers: [CoreService, TranslateStore],
  bootstrap: [AppComponent],
})
export class AppModule {}

package.json代码

{
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "dev:ssr": "ng run angular-app-for-ssr:serve-ssr",
    "serve:ssr": "node dist/angular-app-for-ssr/server/main.js",
    "build:ssr": "ng build && ng run angular-app-for-ssr:server",
    "prerender": "ng run angular-app-for-ssr:prerender"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/fire": "^7.5.0",
    "@angular/forms": "^16.1.0",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/platform-server": "^16.1.0",
    "@angular/router": "^16.1.0",
    "@nguniversal/express-engine": "^16.1.1",
    "@ngx-translate/core": "15.0.0",
    "@ngx-translate/http-loader": "8.0.0",
    "express": "^4.15.2",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  }
}

问题原因

  1. SSR资源路径错误:TranslateHttpLoader默认请求/assets/i18n/{lang}.json,但服务端运行时,相对路径无法正确定位静态资源,导致网络请求失败。
  2. 浏览器API滥用:TranslationService直接操作localStorage、document等浏览器专属API,服务端环境不存在这些对象,不仅会引发后续错误,也会干扰SSR渲染流程。
  3. TranslateModule配置错误:根模块使用TranslateModule.forChild()而非forRoot(),导致服务端与客户端的翻译服务初始化不一致。

解决步骤

1. 修正TranslateModule根配置

将AppModule中的TranslateModule.forChild()替换为TranslateModule.forRoot(),确保翻译服务在根模块正确初始化:

TranslateModule.forRoot({
  defaultLanguage: 'en',
  loader: {
    provide: TranslateLoader,
    useFactory: HttpLoaderFactory,
    deps: [HttpClient],
  },
}),

2. 调整翻译文件请求路径

修改HttpLoaderFactory,根据运行环境动态拼接资源路径,确保服务端能正确访问静态文件:

import { Inject, Optional } from '@angular/core';
import { REQUEST } from '@nguniversal/express-engine/tokens';
import { Request } from 'express';

export function HttpLoaderFactory(http: HttpClient, @Optional() @Inject(REQUEST) request: Request) {
  // 服务端通过请求对象获取当前域名,客户端用相对路径
  const baseUrl = request ? `${request.protocol}://${request.get('host')}` : '';
  return new TranslateHttpLoader(http, `${baseUrl}/assets/i18n/`, '.json');
}

同时更新TranslateModule中loader的deps配置:

deps: [HttpClient, [new Optional(), new Inject(REQUEST)]]

3. 封装浏览器API调用

在TranslationService中添加环境判断,仅在浏览器环境下操作专属API,同时移除服务中无效的组件装饰器与生命周期钩子:

import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class TranslationService {
  public lang = new BehaviorSubject<any>('');
  value: any;

  constructor(public translate: TranslateService) {
    this.lang.next('en');
  }

  // 判断是否为浏览器环境
  private isBrowser(): boolean {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
  }

  changeLanguage(value: any) {
    this.translate.setDefaultLang(value);
    
    if (this.isBrowser()) {
      localStorage.setItem('lang', value);
      if (value == 'ar') {
        document.documentElement.style.setProperty('direction', 'rtl');
        document.documentElement.style.setProperty('overflow-x', 'hidden');
        document.documentElement.style.setProperty('width', '101%');
      } else {
        document.documentElement.style.setProperty('direction', 'ltr');
        document.documentElement.style.setProperty('width', '100%');
      }
    }
    
    this.lang.next(value);
    this.value = value;
  }
}

4. 服务端预加载翻译(可选)

若需SSR直接渲染对应语言内容,可通过Angular Universal的TransferState将服务端加载的翻译数据传递到客户端,避免重复请求,提升首屏渲染速度。


内容的提问来源于stack exchange,提问作者Syed Muhammad Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 07:27:33