I'm submitting a ... (check one with "x")
[x ] bug report => check the FAQ and search github for a similar issue or PR before submitting
Current behavior
The GET request for fetching the translations .json fails in _some_ cases with following message: Response with status: 0 for URL: null. The problem only occurs on iOS devices using WKWebview (according to my error tracking tool).
I use the basic-http loader in form of an exported function like suggested in the docs. Is anybody else seeing this error?
Expected/desired behavior
The request should be successful.
Please tell us about your environment:
ngx-translate version: 7.0.0
Angular version: 4.1.3
Ionic version: 3.5.3
Browser: iOS XX WKWebView
+1
ng2-translate 5.0.0
Angular 4.1.3
Ionic 3.6.0
iOS only bug
+1
+1
+1
+1
Stop adding +1 messages, it won't change anything.
If you want to see this fix, someone will have to work on a PR because I don't have an iOS device and I don't have a mobile app with a webkit view
@ocombe Since the error only happens on iOS and when using the WKWebView, I think the proper way to fix it is by using a _custom loader_. The following seems to be working properly on:
ionic serve)1) Install the cordova-plugin-file (I'm using Ionic Native, but you could do the same without it):
ionic cordova plugin add cordova-plugin-file
npm install --save @ionic-native/file
2) Add it to the AppModule in the app.module.ts file:
import { File } from '@ionic-native/file';
// ...
@NgModule({
declarations: [/* ... */],
imports: [/* ... */],
bootstrap: [/* ... */],
entryComponents: [/* ... */],
providers: [
File, // <--- here!
// ...
]
})
export class AppModule { }
3) Create a custom loader that uses the cordova-plugin-file when running on a mobile device (we also handle what to do when working in the browser)
import { Observable } from 'rxjs/observable';
import { fromPromise } from 'rxjs/observable/fromPromise';
import { HttpClient } from '@angular/common/http';
import { File } from '@ionic-native/file';
// ...
export class CustomLoader implements TranslateLoader {
constructor(private http: HttpClient, private platform: Platform, private file: File) { }
getTranslation(lang: string): Observable<any> {
if (this.platform.is('cordova')) {
let readFilePromise = this.file.readAsText(this.file.applicationDirectory, `www/assets/i18n/${lang}.json`);
return fromPromise(readFilePromise).map((data: string) => JSON.parse(data));
} else {
return this.http.get(`./assets/i18n/${lang}.json`);
}
}
}
NOTE: In the code above I'm using the Lettable operators (rxjs >= 5.5.X). If you're using an older version of rxjs, instead of return fromPromise(readFilePromise)... you'll need to do something like return Observable.fromPromise(readFilePromise)... and also remove the following import: import { fromPromise } from 'rxjs/observable/fromPromise';
4) Update the AppModule of the app.module.ts file to use our custom loader
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { Platform } from 'ionic-angular';
import { File } from '@ionic-native/file';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
// ... don't forget to import the CustomLoader if it's not in the same file!
// ...
@NgModule({
declarations: [/* ... */],
imports: [
HttpClientModule, // <--- We need to import the HttpClientModule
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: CustomLoader,
deps: [HttpClient, Platform, File]
}
}),
// ...
],
bootstrap: [/* ... */]],
entryComponents: [/* ... */],
providers: [/* ... */]
})
export class AppModule { }
Thanks, any chance that you could extract this loader somewhere (github repo, or gist) and add a link to it from the readme so that people can find it easily?
@sebaferreras @ocombe If you are interested I was able to work around that in a similar way, I created a custom TranslateLoader which returns observables of imported .ts constants:
import { de } from '../core/translations/de'; // export const de = { [key: string]: value: string, }
import { en } from '../core/translations/en';
import { sl } from '../core/translations/sl';
export class LocalTranslateLoader implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
switch (lang) {
case 'de':
return of(de);
case 'en':
return of(en);
case 'sl':
return of(sl);
default:
return of(de);
}
}
}
@ocombe of course. It'd be great if some other users could give this a try to make sure it works properly on some other devices/OS versions. So if it's ok for you, later today I will create a Github repo with a ready-to-use demo app and will add the link to that repo in every Github issue related to this.
In a few days if it seems to be working properly I will create a PR to add the link to that repo in the Additional Framework Support section of the readme.
Perfect, thanks
@sebaferreras Any updates on this? ... I know - it's already old :)
@CodeAndWeb I'm sorry, I was waiting to see if there were more comments on this issue, but I guess I should make a PR in the ionic-example repo with the changes listed on my comment... Would that be ok?
I am not sure if the example repo is the best place for it.
@ocombe I think creating a separate ionic-loader repo (similar to the http-loader) with the loader and updating the demo to use it would be the better choice.
Well - put it in the demo repo. We can move it from here later.
Thanks for your time making the updates!
Cons
@ocombe Since the error only happens on iOS and when using the WKWebView, I think the proper way to fix it is by using a _custom loader_. The following seems to be working properly on:
Sometimes the file plugin is accessed before the platform is ready. In this case, the interface is not translated on app launch. Needs to reload app. How can I make it wait for the platform to be ready and then try to download the translation file?
Most helpful comment
@ocombe Since the error only happens on iOS and when using the WKWebView, I think the proper way to fix it is by using a _custom loader_. The following seems to be working properly on:
ionic serve)1) Install the
cordova-plugin-file(I'm using Ionic Native, but you could do the same without it):2) Add it to the
AppModulein theapp.module.tsfile:3) Create a custom loader that uses the
cordova-plugin-filewhen running on a mobile device (we also handle what to do when working in the browser)NOTE: In the code above I'm using the Lettable operators (rxjs >= 5.5.X). If you're using an older version of rxjs, instead of
return fromPromise(readFilePromise)...you'll need to do something likereturn Observable.fromPromise(readFilePromise)...and also remove the following import:import { fromPromise } from 'rxjs/observable/fromPromise';4) Update the
AppModuleof theapp.module.tsfile to use our custom loader