ngx-translate / core

The internationalization (i18n) library for Angular
MIT License
4.51k stars 577 forks source link

Wrong TranslateHttpLoader after upgrade app to Angular 17 #1470

Open icolumbro-asf opened 8 months ago

icolumbro-asf commented 8 months ago

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...

dharma-code-chris commented 4 months ago

Perhaps have a look at this article: https://medium.com/@k4kunal/exploring-ngx-translate-support-in-angular-17-transitioning-to-standalone-components-c19324c56ce5