http-loader
http-loader copied to clipboard
Multiple HTTP requests
Current behavior
Currently TranslateHttpLoader produces HTTP request every time getTranslation is called.
Expected behavior
TranslateHttpLoader should store state of language file loading and only one HTTP request is made.
How do you think that we should fix this?
This is how I implemented this (proof of concept, it performs only ONE request, everything works and tokens are translated):
private errorState = false;
private waiter = new Subject<Object>();
private requestedLang: string;
getTranslation(lang: string): Observable<Object>
{
if(this.errorState)
return of({});
if(this.requestedLang != null && this.requestedLang == lang)
return this.waiter.asObservable();
let languagPath = `assets/locale/${lang}.json`;
this.requestedLang = lang;
this.http.get(languagPath).pipe(
catchError(error => {
console.warn("can't load language file", languagPath, error);
this.errorState = true;
return of({});
})
).subscribe(result => {
//console.log("translation", lang,"loaded!");
this.waiter.next(result);
});
return this.waiter.asObservable();
}
Minimal reproduction of the problem with instructions
Even standard ngx-translate template calls getTranslation at least two times. My app got approximately 140 (app with multiple pages and modules) and lots of annoying alerts were displayed in case when translations were not found. If success, other requests fetched (304), but fetch still has delay.
Environment
ngx-translate version: 11.0.1
Angular version: 7.1.4
Browser:
- [x] Chrome (desktop) version XX
For Tooling issues:
- Node version: 10.15
- Platform: Windows
I'm experiencing the same issue.
The json file with i18n translations is requested multiple times. It seems to be requested in an infinite loop
It only occurs with non default language files (= languages that have not been set with setDefaultLang()
)
See https://cl.ly/f5b0f57313a8
@emeryowa this is indeed not well behaviour. In fact now i'm not using http-loader due to this issue. This TranslateHttpLoader
implementation itself is too trivial to be added as package to your project IMO. Instead, it's better to implement your own TranslateLoader with the getTranslation
function i've posted in the issue.
@SoapyMan I have implemented a custom loader, identical to the function you posted. I am getting the same error though = the call to the translations file is requested multiple times. Do you have any tips?
@emeryowa @SoapyMan
I've encountered the same error. Fortunately, the error message with your loader @SoapyMan is much better than the one in the ngx-translate loader.
My Error shows:
my-translate-loader.ts:31 can't load language file assets/i18n/en.json TypeError: Cannot read property 'length' of undefined
at HttpHeaders.push../node_modules/@angular/common/fesm5/http.js.HttpHeaders.applyUpdate (http.js:244)
at http.js:215
at Array.forEach (<anonymous>)
at HttpHeaders.push../node_modules/@angular/common/fesm5/http.js.HttpHeaders.init (http.js:215)
at HttpHeaders.push../node_modules/@angular/common/fesm5/http.js.HttpHeaders.forEach (http.js:280)
at Observable._subscribe (http.js:1596)
at Observable.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe (Observable.js:43)
at Observable.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe (Observable.js:29)
at subscribeToResult (subscribeToResult.js:13)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._innerSub (mergeMap.js:74)
I created the following loader which does simple caching:
import {HttpClient} from "@angular/common/http";
import {TranslateLoader} from "@ngx-translate/core";
import {Observable} from "rxjs";
import {shareReplay} from "rxjs/operators";
export class CachedHttpTranslationLoader implements TranslateLoader {
cache$: Observable<Object> = null;
cachedLang: string = null;
constructor(private http: HttpClient, public prefix: string = "/assets/i18n/", public suffix: string = ".json") {}
/**
* Gets the translations from the server
*/
public getTranslation(lang: string): Observable<Object> {
if (!this.cache$ || this.cachedLang !== lang) {
this.cache$ = this.http.get(`${this.prefix}${lang}${this.suffix}`).pipe(shareReplay(1));
this.cachedLang = lang;
}
return this.cache$;
}
}
@larsvliet thank you It works very well
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: CachedHttpTranslationLoader,
deps: [HttpClient]
},
defaultLanguage: 'ko'
}),