jsverse / transloco

🚀 😍 The internationalization (i18n) library for Angular
https://jsverse.github.io/transloco/
MIT License
1.97k stars 191 forks source link

Bug(SSR): npm run prerender fails with HttpErrorResponse #703

Open limes opened 9 months ago

limes commented 9 months ago

Is there an existing issue for this?

Which Transloco package(s) are the source of the bug?

Transloco

Is this a regression?

No

Current behavior

  1. ng new
  2. ng add @nguniversal/express-engine
  3. ng add @ngneat/transloco
  4. Add {{ 'hello' | transloco }} to app.component.html
  5. Add { "hello": "world" } to assets/i18n/en.json
  6. ng build && ng run {app-name}:server <- works fine
  7. npm run prerender <- fails with error below:
Prerendering 1 route(s) to (...)browser...Error while trying to load "en" HttpErrorResponse {
    headers: HttpHeaders {
      normalizedNames: Map(0) {},
      lazyUpdate: null,
      headers: Map(0) {}
    },
    status: 0,
    statusText: 'Unknown Error',
    url: 'http://localhost:4200/assets/i18n/en.json',
    ok: false,
    name: 'HttpErrorResponse',
    message: 'Http failure response for http://localhost:4200/assets/i18n/en.json: 0 Unknown Error',
    error: ProgressEvent2 {
      type: 'error',
      target: XMLHttpRequest2 {
        onloadstart: null,
        onprogress: null,
        onabort: null,
        onerror: null,
        onload: null,
        ontimeout: null,
        onloadend: null,
        _listeners: [Object],
        onreadystatechange: null,
        _anonymous: undefined,
        readyState: 4,
        response: null,
        responseText: '',
        responseType: 'text',
        responseURL: '',
        status: 0,
        statusText: '',
        timeout: 0,
        upload: [XMLHttpRequestUpload],
        _method: 'GET',
        _url: [Url],
        _sync: false,
        _headers: [Object],
        _loweredHeaders: [Object],
        _mimeOverride: null,
        _request: null,
        _response: null,
        _responseParts: null,
        _responseHeaders: null,
        _aborting: null,
        _error: null,
        _loadedBytes: 0,
        _totalBytes: 0,
        _lengthComputable: false
      },
      currentTarget: XMLHttpRequest2 {
        onloadstart: null,
        onprogress: null,
        onabort: null,
        onerror: null,
        onload: null,
        ontimeout: null,
        onloadend: null,
        _listeners: [Object],
        onreadystatechange: null,
        _anonymous: undefined,
        readyState: 4,
        response: null,
        responseText: '',
        responseType: 'text',
        responseURL: '',
        status: 0,
        statusText: '',
        timeout: 0,
        upload: [XMLHttpRequestUpload],
        _method: 'GET',
        _url: [Url],
        _sync: false,
        _headers: [Object],
        _loweredHeaders: [Object],
        _mimeOverride: null,
        _request: null,
        _response: null,
        _responseParts: null,
        _responseHeaders: null,
        _aborting: null,
        _error: null,
        _loadedBytes: 0,
        _totalBytes: 0,
        _lengthComputable: false
      },
      lengthComputable: false,
      loaded: 0,
      total: 0
    }
  }
  ERROR Error: Unable to load translation and all the fallback languages

Expected behavior

Prerender process should work the same way as ng build && ng run {app-name}:server

Please provide a link to a minimal reproduction of the bug, if you won't provide a link the issue won't be handled.

See current behavior (1-3)

Transloco Config

provideTransloco({
        config: {
          availableLangs: ['en', 'es'],
          defaultLang: 'en',
          // Remove this option if your application doesn't support changing language in runtime.
          reRenderOnLangChange: true,
          // prodMode: !isDevMode(),
        },
        loader: TranslocoHttpLoader
      }),

Please provide the environment you discovered this bug in

Transloco: 5.0.7
Angular: 16
Node: 18
Package Manager: yarn v1
OS: macOS (13.5.2)

Browser

SSR

Additional context

No response

I would like to make a pull request for this bug

No

alcaidio commented 8 months ago

Somebody know how to do prerendering with transloco ?

NLueg commented 7 months ago

Heyo, I solved this by providing a special TranslocoLoader only for the server. I already use this in production with success. This is the AppServerModule I updated to provide the new loader

@NgModule({
  imports: [AppModule, ServerModule, IonicServerModule],
  providers: [
    { provide: TRANSLOCO_LOADER, useClass: TranslocoServerHttpLoaderService },
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

The loader itself don't tries to get the JSON files by using a HTTP call and just uses the file system:

@Injectable({
  providedIn: 'root',
})
export class TranslocoServerHttpLoaderService implements TranslocoLoader {
  getTranslation(lang: string): Observable<Translation> {
    const assetsFolder = join(
      process.cwd(),
      'dist',
      'app',
      'browser',
      'assets',
      'i18n',
    );
    return of(
      JSON.parse(fs.readFileSync(`${assetsFolder}/${lang}.json`, 'utf8')),
    );
  }
}

With this solution I managed it to prerender the files for different languages.

asapha commented 5 months ago

Had a similar issue in an existing app, the new pre-rendering done in ng build failed. I fixed it by following the docs' recommendation.

i.e changing

return this.http.get<Translation>(`${environment["baseUrl"]}/assets/i18n/${lang}.json`);

to

return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
phatyh commented 4 months ago

Heyo, I solved this by providing a special TranslocoLoader only for the server. I already use this in production with success. This is the AppServerModule I updated to provide the new loader

@NgModule({
  imports: [AppModule, ServerModule, IonicServerModule],
  providers: [
    { provide: TRANSLOCO_LOADER, useClass: TranslocoServerHttpLoaderService },
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

The loader itself don't tries to get the JSON files by using a HTTP call and just uses the file system:

@Injectable({
  providedIn: 'root',
})
export class TranslocoServerHttpLoaderService implements TranslocoLoader {
  getTranslation(lang: string): Observable<Translation> {
    const assetsFolder = join(
      process.cwd(),
      'dist',
      'app',
      'browser',
      'assets',
      'i18n',
    );
    return of(
      JSON.parse(fs.readFileSync(`${assetsFolder}/${lang}.json`, 'utf8')),
    );
  }
}

With this solution I managed it to prerender the files for different languages.

thanks. my project angular v17 with ssr without standalone. solved my problem

mackelito commented 4 months ago

No updates on this issue?

shaharkazaz commented 1 month ago

@limes @mackelito Did you try the solution @NLueg suggested? Feels like something worth mentioning in the docs if someone wants to open a PR and add it 🙂