Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed request to load remote resources aborts future reloads #147

Closed
itsramiel opened this issue Aug 19, 2024 · 11 comments
Closed

Failed request to load remote resources aborts future reloads #147

itsramiel opened this issue Aug 19, 2024 · 11 comments

Comments

@itsramiel
Copy link

🐛 Bug Report

I am loading my translation resources using a backend. The issue is that if a request fails for some reason then subsequent requests are not attempted even if they were to succeed.

Use case in real case scenario: I have a react native app that loads resources from the backend. It is possible that user opens the app and translations are loaded while he is offline, which leads to a failed request. I reload resourced using i18next.reloadResources() at certain points to make sure translations are up to date, however reloading resources no longer makes a request, which is the bug.

There is a similar issue #142 that was opened but it did not really fix this behavior

To Reproduce

const i18next = require("i18next");
const HttpBackend = require("i18next-http-backend");

let loadPath = "https://your-backend/{{ns}}/{{lng}}.json"; // incorrect backend url to fail requests

i18next
  .use(HttpBackend)
  .init({
    backend: {
      loadPath: loadPath,
      parse(data, languages, namespaces) {
        console.log("languages", languages);
        console.log("namespaces", namespaces);
        return JSON.parse(data);
      },
    },
    lng: "en",
    fallbackLng: "en",
    defaultNS: "translation",
    ns: ["translation"],
    debug: true,
  })
  .then(() => {
    console.log(i18next.t("key"));

    loadPath = "https://your-backend/{{ns}}/{{lng}}.json"; // corrected backend url

    setTimeout(() => {
      console.log("will reload");
      i18next.reloadResources().then(() => {
        console.log("reloaded");
      });
    }, 1000);
  });

If you run this, you will see in debug logs that there was an error because of the wrong url but later on the url was corrected but no request is attempted. Of course you need to pass in actual urls, these are placeholders

Expected behavior

I expect that every time reloadResources is called then a request is made to the backend, and not that subsequent failed requests not go through

Your Environment

  • runtime version: i.e. node v20.11.1
  • *i18next: v23.13.0, i18next-http-backend v2.5.2
  • os: Mac
@itsramiel itsramiel changed the title Failed request to load remote resources, aborts future reloads Failed request to load remote resources aborts future reloads Aug 19, 2024
adrai added a commit that referenced this issue Aug 19, 2024
@adrai
Copy link
Member

adrai commented Aug 19, 2024

Try with i18next-http-backend v2.6.0 and i18next v23.14.0

@itsramiel
Copy link
Author

@adrai Thanks for the fix, will give it a try tomorrow and let you know 🙏

@itsramiel
Copy link
Author

Hey @adrai, the new versions did not fix the problem unfortunately. There are is no request made for the reloadResources

@adrai
Copy link
Member

adrai commented Aug 20, 2024

I just checked this twice and it seemed to work.
Can you provide a little example repository that reproduces it?

@itsramiel
Copy link
Author

Hi @adrai here is a sample repo

Try running the app, and you will that the first time it will fail because the url is wrong and then the url is fixed but no request is made and there are logs about a failed/success request. And the key is still untranslated.

If you fix the url from the beginning, both requests are logged and translations are there

@adrai
Copy link
Member

adrai commented Aug 20, 2024

You can't just change the loadPath like that... it will not use the new url...
you need to do something like this:

i18next.services.backendConnector.backend.options.loadPath = 'https://jsonplaceholder.typicode.com/posts/1'

@itsramiel
Copy link
Author

You can't just change the loadPath like that... it will not use the new url... you need to do something like this:

i18next.services.backendConnector.backend.options.loadPath = 'https://jsonplaceholder.typicode.com/posts/1'

Hi @adrai,

Didnt know that is not the correct way. I did that in the sample when I created the issue and didnt receive this feedback.

Anyway I updated repo and you can pull the changes and test that it still doesnt work. Note that this is not what I do in production. This is simply a reproducible example to simulate the issue I am seeing in my react native app.

@adrai
Copy link
Member

adrai commented Aug 20, 2024

2 points:

  1. there is another option that needs to be set..
  2. the response status code of: https://jsonplaceholder.typicode.com/posts/1 is 404, and this is not retriable... => https://github.com/i18next/i18next-http-backend/blob/master/lib/index.js#L76 so changed your example with a non existing domain...
const i18next = require("i18next");
const HttpBackend = require("i18next-http-backend");

let loadPath = "https://WRONGjsonplaceholder.typicode.com/posts/1";

i18next
  .use(HttpBackend)
  .init({
    backend: {
      loadPath: loadPath,
      customHeaders: {
        "Cache-Control": "no-cache",
        Pragma: "no-cache",
        Expires: "0",
      },
      parse(data, languages, namespaces) {
        console.log("languages", languages);
        console.log("namespaces", namespaces);
        return JSON.parse(data);
      },
    },
    lng: "en",
    fallbackLng: "en",
    defaultNS: "translation",
    ns: ["translation"],
    debug: true,
  })
  .then(() => {
    console.log(i18next.t("userId"));
    console.log(i18next.t("title"));

    // HACK: these 2 options needs to be overwritten, if the i18next instance is init() is not called again
    i18next.options.backend.loadPath = "https://jsonplaceholder.typicode.com/posts/1";
    i18next.services.backendConnector.backend.options.loadPath = i18next.options.backend.loadPath;

    setTimeout(() => {
      console.log("will reload");
      i18next.reloadResources().then(() => {
        console.log(i18next.t("userId"));
        console.log(i18next.t("title"));
        console.log("reloaded");
      });
    }, 1000);
  });

@itsramiel
Copy link
Author

itsramiel commented Aug 21, 2024

Hey @adrai

Alright I guess my reproducible isnt 100% accurate because it returns 404 while my actual error is when there is no internet

My guess is that:

In here

if (!res && err && err.message && (err.message.indexOf('Failed to fetch') > -1 || (err.message.indexOf('failed') > -1 && err.message.indexOf('fetch') > -1))) return callback('failed loading ' + url + ': ' + err.message, true /* retry */)

If there was no response, you check for a specific error message, which I believe is the bug?
In the browser, you get Failed to fetch error while in react native it is Network request failed which probably is an implementation detail of the js engine. Browser likely v8 while react native uses Hermes


To further check it yourself:

I created a repo that is exactly the case I encounter.
The repo can be found here

If you dont know how to run an expo react native app then expand this
  1. clone repo, install deps with yarn install, and run yarn start. Now you should see a qr code in your terminal
  2. Install the expo app on your ios/android device from the app store/play store
  3. Scan the qr code shown in the terminal with your device and just like that the app is running

Here is the normal case when user has internet:

Screen.Recording.2024-08-21.at.9.mp4

console logs

 WARN  i18next: hasLoadedNamespace: i18next was not initialized ["en"]
    at App (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:130363:60)
    at withDevTools(App) (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:129917:27)
    at RCTView
    at View (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at RCTView
    at View (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at AppContainer (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74400:25)
    at main(RootComponent) (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:117830:28)
 WARN  i18next::translator: key "first_name" for languages "en" won't get resolved as namespace "translation" was not yet loaded This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!
    at App (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:130363:60)
    at withDevTools(App) (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:129917:27)
    at RCTView
    at View (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at RCTView
    at View (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at AppContainer (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74400:25)
    at main(RootComponent) (http://192.168.0.75:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:117830:28)
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  called parse
 LOG  i18next::backendConnector: loaded namespace translation for language en {"address": {"city": "Belleburgh", "coordinates": {"lat": -77.03848548019587, "lng": -158.40973938049999}, "country": "United States", "state": "Connecticut", "street_address": "4609 Eddy Prairie", "street_name": "Marvin Plains", "zip_code": "26384"}, "avatar": "https://robohash.org/etmaioresquo.png?size=300x300&set=set1", "credit_card": {"cc_number": "4300-1044-3323-9307"}, "date_of_birth": "1974-07-15", "email": "buster.harber@email.com", "employment": {"key_skill": "Leadership", "title": "Product Mining Planner"}, "first_name": "Buster", "gender": "Polygender", "id": 3193, "last_name": "Harber", "password": "x2ulB0gRDE", "phone_number": "+595 116-438-7438 x726", "social_insurance_number": "950840389", "subscription": {"payment_method": "Money transfer", "plan": "Platinum", "status": "Pending", "term": "Monthly"}, "uid": "2eb0bdb2-b602-4ee3-b401-ce1fc66e1e23", "username": "buster.harber"}
 LOG  i18next: languageChanged en
 LOG  i18next: initialized {"appendNamespaceToCIMode": false, "appendNamespaceToMissingKey": false, "backend": {"loadPath": "https://random-data-api.com/api/v2/users", "parse": [Function parse]}, "compatibilityJSON": "v3", "contextSeparator": "_", "debug": true, "defaultNS": "translation", "fallbackLng": ["en"], "fallbackNS": false, "ignoreJSONStructure": true, "initImmediate": true, "interpolation": {"escapeValue": true, "format": [Function bound format], "formatSeparator": ",", "maxReplaces": 1000, "nestingOptionsSeparator": ",", "nestingPrefix": "$t(", "nestingSuffix": ")", "prefix": "{{", "skipOnVariables": true, "suffix": "}}", "unescapePrefix": "-"}, "joinArrays": false, "keySeparator": ".", "lng": "en", "load": "all", "missingInterpolationHandler": false, "missingKeyHandler": false, "nonExplicitSupportedLngs": false, "ns": ["translation"], "nsSeparator": ":", "overloadTranslationOptionHandler": [Function overloadTranslationOptionHandler], "parseMissingKeyHandler": false, "partialBundledLanguages": false, "pluralSeparator": "_", "postProcess": false, "postProcessPassResolved": false, "preload": false, "react": {"useSuspense": false}, "returnEmptyString": true, "returnNull": false, "returnObjects": false, "returnedObjectHandler": false, "saveMissing": false, "saveMissingPlurals": true, "saveMissingTo": "fallback", "simplifyPluralSuffix": true, "supportedLngs": false, "updateMissing": false}
 LOG  called parse
 LOG  i18next::backendConnector: loaded namespace translation for language en {"address": {"city": "North Rory", "coordinates": {"lat": 30.893290764917097, "lng": 38.945948564904484}, "country": "United States", "state": "Oklahoma", "street_address": "7587 Gulgowski Underpass", "street_name": "Dallas Locks", "zip_code": "46148-7810"}, "avatar": "https://robohash.org/nihilsuntcum.png?size=300x300&set=set1", "credit_card": {"cc_number": "5121-5155-9625-1884"}, "date_of_birth": "1965-03-24", "email": "reinaldo.mohr@email.com", "employment": {"key_skill": "Fast learner", "title": "Senior Strategist"}, "first_name": "Reinaldo", "gender": "Polygender", "id": 2562, "last_name": "Mohr", "password": "kaKAmOeVJE", "phone_number": "+36 929-081-8861 x61975", "social_insurance_number": "732030481", "subscription": {"payment_method": "Cash", "plan": "Diamond", "status": "Pending", "term": "Payment in advance"}, "uid": "b2cb65c7-e066-4489-a5bf-39391123a9c5", "username": "reinaldo.mohr"}
 LOG  reloaded
 LOG  i18next: languageChanged en
 LOG  called parse
 LOG  i18next::backendConnector: loaded namespace translation for language en {"address": {"city": "Port Danfort", "coordinates": {"lat": -50.96101765653314, "lng": 50.5101549628622}, "country": "United States", "state": "Montana", "street_address": "3588 Tatyana Key", "street_name": "Moore Flat", "zip_code": "23908-6544"}, "avatar": "https://robohash.org/quianumquamexercitationem.png?size=300x300&set=set1", "credit_card": {"cc_number": "5216-2444-9385-5418"}, "date_of_birth": "2004-08-24", "email": "davis.dare@email.com", "employment": {"key_skill": "Teamwork", "title": "Consulting Designer"}, "first_name": "Davis", "gender": "Bigender", "id": 9707, "last_name": "Dare", "password": "bcAxsHUDnY", "phone_number": "+1-671 681.462.6180 x4179", "social_insurance_number": "355898404", "subscription": {"payment_method": "Cheque", "plan": "Basic", "status": "Active", "term": "Monthly"}, "uid": "da769fb3-9b44-4bbe-b6f6-48f96e9ea117", "username": "davis.dare"}
 LOG  reloaded
 LOG  i18next: languageChanged en

Now here is the case where I start offline and then go online:

Screen.Recording.2024-08-21.at.9.1.mp4

console logs:

 WARN  i18next: hasLoadedNamespace: i18next was not initialized ["en"]
    at App (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:130363:60)
    at withDevTools(App) (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:129917:27)
    at RCTView
    at View (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at RCTView
    at View (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at AppContainer (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74400:25)
    at main(RootComponent) (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:117830:28)
 WARN  i18next::translator: key "first_name" for languages "en" won't get resolved as namespace "translation" was not yet loaded This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!
    at App (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:130363:60)
    at withDevTools(App) (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:129917:27)
    at RCTView
    at View (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at RCTView
    at View (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74557:43)
    at AppContainer (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:74400:25)
    at main(RootComponent) (http://127.0.0.1:8081/node_modules/expo/AppEntry.bundle//&platform=ios&dev=true&hot=false&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=app:117830:28)
 LOG  i18next::translator: missingKey en translation first_name first_name
 WARN  i18next::backendConnector: loading namespace translation for language en failed [TypeError: Network request failed]
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next: languageChanged en
 LOG  i18next: initialized {"appendNamespaceToCIMode": false, "appendNamespaceToMissingKey": false, "backend": {"loadPath": "https://random-data-api.com/api/v2/users", "parse": [Function parse]}, "compatibilityJSON": "v3", "contextSeparator": "_", "debug": true, "defaultNS": "translation", "fallbackLng": ["en"], "fallbackNS": false, "ignoreJSONStructure": true, "initImmediate": true, "interpolation": {"escapeValue": true, "format": [Function bound format], "formatSeparator": ",", "maxReplaces": 1000, "nestingOptionsSeparator": ",", "nestingPrefix": "$t(", "nestingSuffix": ")", "prefix": "{{", "skipOnVariables": true, "suffix": "}}", "unescapePrefix": "-"}, "joinArrays": false, "keySeparator": ".", "lng": "en", "load": "all", "missingInterpolationHandler": false, "missingKeyHandler": false, "nonExplicitSupportedLngs": false, "ns": ["translation"], "nsSeparator": ":", "overloadTranslationOptionHandler": [Function overloadTranslationOptionHandler], "parseMissingKeyHandler": false, "partialBundledLanguages": false, "pluralSeparator": "_", "postProcess": false, "postProcessPassResolved": false, "preload": false, "react": {"useSuspense": false}, "returnEmptyString": true, "returnNull": false, "returnObjects": false, "returnedObjectHandler": false, "saveMissing": false, "saveMissingPlurals": true, "saveMissingTo": "fallback", "simplifyPluralSuffix": true, "supportedLngs": false, "updateMissing": false}
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  reloaded
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next: languageChanged en
 LOG  reloaded
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next: languageChanged en
 LOG  reloaded
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next: languageChanged en
 LOG  reloaded
 LOG  i18next::translator: missingKey en translation first_name first_name
 LOG  i18next: languageChanged en

@adrai
Copy link
Member

adrai commented Aug 21, 2024

did not try, but can you check if v2.6.1 works?

@itsramiel
Copy link
Author

Yes, it does. Thank you!

Closing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants