Core: WKWebview: Response with status: 0 for URL: null

Created on 23 Aug 2017  路  15Comments  路  Source: ngx-translate/core

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

Review help wanted need more info

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:

  • [x] Android
  • [x] iOS (UIWebView and WKWebView)
  • [x] Browser (i.e. when using 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 { }

All 15 comments

+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:

  • [x] Android
  • [x] iOS (UIWebView and WKWebView)
  • [x] Browser (i.e. when using 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?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jvquarck picture jvquarck  路  3Comments

gmquiroga picture gmquiroga  路  3Comments

egornoveo picture egornoveo  路  4Comments

briancullinan picture briancullinan  路  3Comments

pndewit picture pndewit  路  3Comments