Im currently work on app where we need to use different branding and translations. For that, I need to get some information from the query params and this is working fine for getting all assets (images/icons/logos) which is done during ngOnInit() but I have problem with translations. I created custom translation loader which looks like this:
export class TranslationFileLoader implements TranslateLoader {
private tenant: string | null = "";
private sp: string | null = "";
constructor(private http: HttpClient, private actRoute: ActivatedRoute) {
this.actRoute.queryParams.subscribe(params => {
if (params !== null) {
this.tenant = params["tenant"];
this.sp = params["sp"];
}
});
}
getTranslation(lang: string): Observable<any> {
return this.http.get(environment.blobUrl + "/" + this.tenant.replace(/[.]/g,'')+"/i18n/" + this.sp?.toLowerCase() + "/" + `${lang}` + ".json");
}
}
but when the loader is intialized, query params are empty and then link for translation contain undefined. Is there a way to somehow get those params or maybe do it differently? I thought about set those two variables in localStorage.
The problem was using useFactory
instead we should use useClass
and we should point to the class we created TranslationFileLoader
, then since activated route is always empty, we can use native javascipt query params parsing to solve the issue.
...
const searchParams = window?.location?.href?.split('?')?.[1];
const urlParams = new URLSearchParams(
searchParams ? `?${searchParams}` : ''
);
console.log(urlParams.get('tenant'));
console.log(urlParams.get('sp'));
// if no data then it will fallback to the default values you set using `||` condition in javascript since lhs is false!
this.tenant = urlParams.get('tenant') || 'default'; // you can also give fallback data by giving: params?.["tenant"] || 'asdf';
this.sp = urlParams.get('sp') || 'en'; // you can also give fallback data by giving: params?.["tenant"] || 'en';
...
Below is a working example for your reference!
import { Component, importProvidersFrom } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, provideRouter } from '@angular/router';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppModule } from './app/app.module';
import { BrowserModule } from '@angular/platform-browser';
import {
HttpClient,
HttpClientModule,
provideHttpClient,
} from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import 'zone.js';
import { Observable } from 'rxjs';
export class TranslationFileLoader implements TranslateLoader {
private tenant: string | null = '';
private sp: string | null = '';
constructor(private http: HttpClient) {}
getTranslation(lang: string): Observable<any> {
const searchParams = window.location.href.split('?')[1];
const urlParams = new URLSearchParams(
searchParams ? `?${searchParams}` : ''
);
console.log(urlParams.get('tenant'));
console.log(urlParams.get('sp'));
// if no data then it will fallback to the default values you set using `||` condition in javascript since lhs is false!
this.tenant = urlParams.get('tenant') || 'default'; // you can also give fallback data by giving: params?.["tenant"] || 'asdf';
this.sp = urlParams.get('sp') || 'en'; // you can also give fallback data by giving: params?.["tenant"] || 'en';
return this.http.get(
'https://google.com' +
'/' +
this.tenant.replace(/[.]/g, '') +
'/i18n/' +
this.sp?.toLowerCase() +
'/' +
`${lang}` +
'.json'
);
}
}
@Component({
selector: 'app-root',
imports: [CommonModule, TranslateModule],
standalone: true,
template: `
<div>
<h2>{{ 'HOME.TITLE' | translate }}</h2>
<label>
{{ 'HOME.SELECT' | translate }}
<select #langSelect (change)="translate.use(langSelect.value)">
<option *ngFor="let lang of translate.getLangs()" [value]="lang" [selected]="lang === translate.currentLang">{{ lang }}</option>
</select>
</label>
</div>
`,
})
export class AppComponent {
constructor(public translate: TranslateService) {
translate.addLangs(['en', 'fr']);
translate.setDefaultLang('en');
const browserLang = translate.getBrowserLang();
translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
}
}
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(),
provideRouter([]),
importProvidersFrom(
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslationFileLoader,
deps: [HttpClient],
},
useDefaultLang: false,
})
),
],
});
Try modifying the code so that first we access the route snapshot (no need to subscribe!) and get the data, then we return the API call!
Because this is a case of a timing issue, the subscribe executes after the getTranslation method is called, hence you might get this problem!
export class TranslationFileLoader implements TranslateLoader {
private tenant: string | null = "";
private sp: string | null = "";
constructor(private http: HttpClient, private actRoute: ActivatedRoute) {
}
getTranslation(lang: string): Observable<any> {
const params = this.actRoute?.snapshot?.params;
// if no data then it will fallback to the default values you set using `||` condition in javascript since lhs is false!
this.tenant = params?.["tenant"]; // you can also give fallback data by giving: params?.["tenant"] || 'asdf';
this.sp = params?.["sp"]; // you can also give fallback data by giving: params?.["tenant"] || 'en';
return this.http.get(environment.blobUrl + "/" + this.tenant.replace(/[.]/g,'')+"/i18n/" + this.sp?.toLowerCase() + "/" + `${lang}` + ".json");
}
}
...