With the arrival of Angular 13 the SystemJsNgModuleLoader function is deprecated. I have just upgraded my Angular 12 and I needed this function in order to translate the routes (for SEO) in a multi-language site. But with Angular 12, my service doesn't seem to work anymore. I would like to know what is the substitute function to SystemJsNgModuleLoader?
this is the code :
localize.loader.ts
import { ROUTES, Route, Routes } from '@angular/router';
import {
SystemJsNgModuleLoader, NgModuleFactory, Injector,
SystemJsNgModuleLoaderConfig, Optional, Compiler, Injectable, Inject
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import * as utils from '../classes/localize.utils';
import { SITE_INFO, SfSiteInfo } from 'sf-lib';
/**
* Extension of SystemJsNgModuleLoader to enable localization of route on lazy load
*/
@Injectable()
export class SfLocalizeLoader extends SystemJsNgModuleLoader {
constructor(
private translate: TranslateService,
@Inject(SITE_INFO) private siteInfo: SfSiteInfo,
_compiler: Compiler,
@Optional() config?: SystemJsNgModuleLoaderConfig
) {
super(_compiler, config);
}
/**
* Extend load with custom functionality
*/
load(path: string): Promise<NgModuleFactory<any>> {
return super.load(path).then((factory: NgModuleFactory<any>) => {
return {
moduleType: factory.moduleType,
create: (parentInjector: Injector) => {
const module = factory.create(parentInjector);
const getMethod = module.injector.get.bind(module.injector);
module.injector['get'] = (token: any, notFoundValue: any) => {
const getResult = getMethod(token, notFoundValue);
if (token === ROUTES) {
const newRoutes: Routes = [].concat(...getResult);
newRoutes.forEach(
(route: Route) => this.confRoute(route)
);
return newRoutes;
} else {
return getResult;
}
};
return module;
}
};
});
}
private confRoute(route: Route) {
if (!route.data) {
route.data = {};
}
if (route.path && route.path !== '' && route.path !== '**') {
route.data['path'] = utils.anonymize(route.path, this.siteInfo.defaultLang, this.siteInfo.translationObject);
route.path = utils.unanonymize(route.data.path, this.siteInfo.lang, this.siteInfo.translationObject);
}
if (route.redirectTo && route.redirectTo !== '' && route.redirectTo !== '**') {
route.data['redirectTo'] = utils.anonymize(route.redirectTo, this.siteInfo.defaultLang, this.siteInfo.translationObject);
route.redirectTo = utils.unanonymize(route.data.redirectTo, this.siteInfo.lang, this.siteInfo.translationObject);
}
if (route.children) {
route.children.forEach(
(child: Route) => {
this.confRoute(child);
}
);
}
}
}
localize.module.ts
import { NgModule, ModuleWithProviders, NgModuleFactoryLoader } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SfLocalizeService } from './services/localize.service';
import { SfLocalizeLoader } from './services/localize.loader';
import { SfLocalizePipe } from './pipes/localize.pipe';
import { SfLang, SfSiteInfo, SITE_INFO } from 'sf-lib';
import { SfLocalizeConfig } from './models/localize-config.model';
@NgModule({
imports: [
CommonModule
],
declarations: [SfLocalizePipe],
exports: [SfLocalizePipe]
})
export class SfLocalizeModule {
static forRoot(config: SfLocalizeConfig): ModuleWithProviders<SfLocalizeModule> {
return {
ngModule: SfLocalizeModule,
providers: [
config.siteInfo ? config.siteInfo :
{
provide: SITE_INFO,
useFactory: dummySiteInfo
},
SfLocalizeService,
{ provide: NgModuleFactoryLoader, useClass: SfLocalizeLoader }
],
};
}
}
export function dummySiteInfo(): SfSiteInfo {
const lang: SfLang = {
sfType: 'SfLang',
country: 'com',
code: 'en',
default: true
};
const si: SfSiteInfo = {
lang: lang,
locales: [lang],
defaultLang: lang,
path: '',
translationObject: {},
origin: ''
};
return si;
}
app.module.ts
import { NgModule, Injector, APP_INITIALIZER, PLATFORM_ID, ErrorHandler, Injectable } from '@angular/core';
import { isPlatformServer, Location } from '@angular/common';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { extractExtension, extractLang } from './shared/utils/translate/translate.utils'
import {
SfLib,
SfSiteInfo,
SITE_INFO,
} from 'sf-lib';
import { site } from '@assets/mock/site.mock';
import { AppRoutingModule, routes } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { AppInjector } from './core/app-injector';
import { environment } from '@env/environment';
import { translationObject } from '@assets/translationObject';
import { CookieModule } from 'ngx-cookie';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { BrowserModule, HammerGestureConfig, HAMMER_GESTURE_CONFIG, TransferState } from '@angular/platform-browser';
import { GlobalErrorHandlerService } from './core/error-handler/services/error-handler.service';
import { SfLocalizeService } from './core/localize/services/localize.service';
import { SfLocalizeModule } from './core/localize/localize.module';
import { LocalizeParser, LocalizeRouterModule, LocalizeRouterSettings } from '@gilsdav/ngx-translate-router';
import { localizeBrowserLoaderFactory } from './core/localize/services/localize-browser.loader';
/** @fixme HACK ! https://github.com/angular/material2/issues/5488 */
@NgModule({
providers: [
{
provide: APP_INITIALIZER,
deps: [MatIconRegistry],
useFactory: preserveSvgViewBox,
multi: true
}
]
})
export class IconModule {
}
// override hammerjs default configuration
@Injectable()
export class MyHammerConfig extends HammerGestureConfig {
overrides = <any>{
// tslint:disable-next-line
'swipe': { direction: 2 | 4},
'pinch': { enable: false },
'rotate': { enable: false },
'pan': { enable: false }
};
}
export function preserveSvgViewBox(matIconRegistry: MatIconRegistry) {
return () => {
const oldToSvgElement = matIconRegistry['_toSvgElement'];
matIconRegistry['_toSvgElement'] = (element: Element) => {
const svg = oldToSvgElement.call(matIconRegistry, element);
const viewBox = element.getAttribute('viewBox');
if (viewBox !== null) {
svg.setAttribute('viewBox', viewBox);
}
return svg;
};
};
}
export function httpTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http);
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule.withServerTransition({appId: 'sombrero'}),
CookieModule.forRoot(),
AppRoutingModule,
CoreModule,
MatIconModule,
IconModule,
HttpClientModule,
// SFLIB
SfLib.forRoot({
apiUrl: environment.url
}),
SfLocalizeModule.forRoot({
siteInfo: {
provide: SITE_INFO,
useFactory: localizeInit,
deps: [Injector, PLATFORM_ID]
}
}),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: httpTranslateLoader,
deps: [HttpClient]
}
}),
LocalizeRouterModule.forRoot(routes, {
parser: {
provide: LocalizeParser,
useFactory: localizeBrowserLoaderFactory,
deps: [TranslateService, Location, LocalizeRouterSettings, HttpClient, TransferState],
},
initialNavigation: true,
})
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: localizeServiceInit,
deps: [Injector],
multi: true
},
{
provide: HAMMER_GESTURE_CONFIG,
useClass: MyHammerConfig
},
// Error handling
{
provide: ErrorHandler,
useClass: GlobalErrorHandlerService
}
]
})
export class AppModule {
constructor(
injector: Injector
) {
// Store module's injector in the AppInjector class
AppInjector.setInjector(injector);
}
}
export function localizeServiceInit(injector: Injector) {
return () => {
injector.get(SfLocalizeService);
};
}
export function localizeInit(injector: Injector, platformId: string) {
const siteInfo: SfSiteInfo = new SfSiteInfo();
let hostExtension: string;
let lang: string;
if (isPlatformServer(platformId)) {
// Fetch HOSTNAME value token defined in server.ts (when run on server)
hostExtension = extractExtension(injector.get('HOSTNAME'));
siteInfo.lang.country = site.find((s) => s.lang.country === hostExtension) ? hostExtension : 'com';
siteInfo.locales = site
.filter((s) => s.lang.country === siteInfo.lang.country)
.map((s) => s.lang);
siteInfo.path = injector.get('PATH');
(siteInfo as any).origin = injector.get('ORIGIN').replace(':443', '');
lang = extractLang(siteInfo.path, siteInfo.locales);
if (!lang) {
const foundSiteLang: {country: string, code: string} = environment.siteLang.find((sl) => sl.country === siteInfo.lang.country);
lang = foundSiteLang ? foundSiteLang.code : siteInfo.lang.country;
siteInfo.lang.default = true;
} else if (lang && lang === siteInfo.lang.country) {
siteInfo.lang.default = true;
} else {
siteInfo.lang.default = false;
}
siteInfo.lang.code = lang;
} else {
hostExtension = extractExtension((<any>window).location.hostname);
siteInfo.lang.country = site.find((s) => s.lang.country === hostExtension) ? hostExtension : 'com';
siteInfo.locales = site
.filter((s) => s.lang.country === siteInfo.lang.country)
.map((s) => s.lang);
siteInfo.path = (<any>window).location.pathname;
(siteInfo as any).origin = (<any>window).location.origin;
lang = extractLang(siteInfo.path, siteInfo.locales);
if (!lang) {
const foundSiteLang: {country: string, code: string} = environment.siteLang.find((sl) => sl.country === siteInfo.lang.country);
lang = foundSiteLang ? foundSiteLang.code : siteInfo.lang.country;
siteInfo.lang.default = true;
} else if (lang && lang === siteInfo.lang.country) {
siteInfo.lang.default = true;
} else {
siteInfo.lang.default = false;
}
siteInfo.lang.code = lang;
}
siteInfo.defaultLang = {
sfType: 'SfLang',
country: 'com',
code: 'en',
default: true
};
const translate = injector.get(TranslateService);
translate.setDefaultLang(`${siteInfo.lang.code}`);
console.log(translate.currentLang)
return siteInfo;
}
Thanks for your help!