Skip to content

Wrong TranslateHttpLoader after upgrade app to Angular 17 #1470

@icolumbro-asf

Description

@icolumbro-asf

Yesterday I upgraded my app to Angular 17now I get error TypeError: translateService.currentLoader.initialize is not a function, this is my code:

startup.module.ts

import {
  ICultureConfiguration,
  CULTURE_CONFIGURATION_TOKEN,
} from '../types/model';
import {
  TranslateLoader,
  TranslateModule,
  TranslateService,
} from '@ngx-translate/core';
import { CultureService } from '../services/culture.service';
import { firstValueFrom } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { InMemoryTranslateHttpLoader } from '../services/in-memory-translate-loader';
import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core';
import { SpinnerService } from '../services/spinner.service';
import { tap } from 'rxjs/operators';
import { StorageService } from '../services/storage.service';
import { DialogService } from '../services/dialog.service';

export function httpLoaderFactory(
  http: HttpClient,
  cultureConfiguration: ICultureConfiguration
) {
  return new InMemoryTranslateHttpLoader(http, cultureConfiguration);
}

export const translateModule = TranslateModule.forRoot({
  loader: {
    provide: TranslateLoader,
    useFactory: httpLoaderFactory,
    deps: [HttpClient, CULTURE_CONFIGURATION_TOKEN],
  },
});

@NgModule({
  declarations: [],
  imports: [translateModule],
  exports: [],
  providers: [CultureService, DialogService, SpinnerService, StorageService],
})
export class StartupModule {
  public static forRoot(
    cultureConfiguration: ICultureConfiguration
  ): ModuleWithProviders<StartupModule> {
    return {
      ngModule: StartupModule,
      providers: [
        {
          provide: CULTURE_CONFIGURATION_TOKEN,
          useValue: cultureConfiguration,
          multi: false,
        },
        {
          provide: APP_INITIALIZER,
          useFactory: languageLoader,
          deps: [TranslateService, CultureService],
          multi: true,
        },
      ],
    };
  }
}

export function languageLoader(
  translateService: TranslateService,
  cultureService: CultureService
) {
  return function () {
    return firstValueFrom(
      (<InMemoryTranslateHttpLoader>translateService.currentLoader)
        .initialize()
        .pipe(
          tap((t) => {
            cultureService.initialize();
          })
        )
    );
  };
}

in-memory-translate-loader.ts

import '../extensions-methods/array.extensions';
import '../extensions-methods/form.extensions';
import { all } from 'deepmerge';
import { HttpClient } from '@angular/common/http';
import { ICultureConfiguration } from '../types/model';
import { Injectable } from '@angular/core';
import { map, tap } from 'rxjs/operators';
import { Observable, forkJoin, of } from 'rxjs';
import { TranslateLoader } from '@ngx-translate/core';

@Injectable()
export class InMemoryTranslateHttpLoader extends TranslateLoader {
  private _data: TranslationResult[] = [];

  constructor(
    private _httpClient: HttpClient,
    private _cultureConfiguration: ICultureConfiguration
  ) {
    super();
  }

  public initialize = (): Observable<TranslationResult[]> => {
    let httpCalls: Observable<any>[] = [];
    for (let i = 0; i < this._cultureConfiguration.resourcePaths.length; i++) {
      for (let x = 0; x < this._cultureConfiguration.cultureCodes.length; x++) {
        let url = this._cultureConfiguration.resourcePaths[i].replace(
          '{code}',
          this._cultureConfiguration.cultureCodes[x]
        );
        httpCalls.push(
          this._httpClient
            .get(url)
            .pipe(
              map(
                (result) =>
                  new TranslationResult(
                    this._cultureConfiguration.cultureCodes[x],
                    result
                  )
              )
            )
        );
      }
    }
    return forkJoin(httpCalls).pipe(
      tap((results: TranslationResult[]) => {
        this._data = results
          .groupBy((g) => g.cultureCode)
          .map((m) => {
            let innerResource = new TranslationResult(m.key, {});
            m.items.push(innerResource);
            let mergedItems = all(m.items.map((m) => m.data));
            return new TranslationResult(m.key, mergedItems);
          });
      })
    );
  };

  public getTranslation(lang: string): Observable<any> {
    let ret = this._data.find((f) => f.cultureCode == lang).data;
    return of(ret);
  }
}

export class TranslationResult {
  constructor(public cultureCode: string, public data: any) {}
}

in Angular 16 httpLoaderFactory function is executed before languageLoader while in Angular 17 languageLoader is executed leading to the given error...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions