question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No error is thrown if the translation file download fails

See original GitHub issue

🐛 Bug Report

I installed this library to download the translation files as needed (lazy loading), inside my project. If the file is not in the server or the server is offline for some reason, I want to get whatever error happened and show it nicely to the user.

The main problem happens when the file is not in the server or the server is offline for some reason. This library simply throw some warnings on the console.log and change the language anyway (even if the file was not downloaded), like so:

image

As a default behavior, I’d expect the i18n.changeLanguage Promise to throw an error so I can treat it inside a try/catch block function. Or maybe an extra configuration key called “ifFileNotDownloadedChangeLanguageAnyway” so we can prevent the language to be changed if the download fails.

To Reproduce

The installation:

npx create-react-app pig --template typescript
cd pig

Then install the libraries:

npm i react-i18next i18next i18next-http-backend @material-ui/core

My i18n.ts file:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend'

i18n
  .use(initReactI18next)
  .use(Backend)
  .init({
    backend:{
      loadPath: '/translations/{{lng}}.json'
    },
    react:{useSuspense:false},
    debug: true,
    lng: "en",
    keySeparator: false,
    interpolation: {
      escapeValue: false
    }
  });

  export default i18n;

My index.tsx:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './i18n';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
    <App />,
  document.getElementById('root')
);

reportWebVitals();

My huge App.tsx:

import { Backdrop, CircularProgress, Button } from "@material-ui/core"
import { makeStyles } from '@material-ui/core/styles';
import './App.css';
import { useTranslation } from 'react-i18next';
import { useState } from "react"

const useStyles = makeStyles((theme) => ({
  backdrop: {
    zIndex: theme.zIndex.drawer + 1,
    color: '#fff',
  },
}));

function App() {
  const [lang, setLang] = useState("en");
  const [block, setBlock] = useState(false);
  const { t, i18n, ready } = useTranslation();
  const handle = async () => {
    try{
      setBlock(true)
      const newLang = lang==="en"?"pt":"en"
      await i18n.changeLanguage(newLang)
      setLang(newLang)
      setBlock(false)
    }catch(e){
      console.log("this is never called", e)
    }
  }
  const classes = useStyles();
  return (
    <div className="App">
      <Backdrop className={classes.backdrop} open={block || !ready}>
        <CircularProgress/>
      </Backdrop>
      {ready && (
        <>
          <h1 onClick={handle}>{t("pig")}</h1>
          <Button onClick={handle} >Click me</Button>
        </>
      )}
      
    </div>
  );
}

export default App;

My folder structure/translation files:

image

Then you can simply exclude the pt.json file or block the requests to this file in the chrome dev tools > Network tab and click the “Click me” button.

My Environment

  • runtime version: node v10.24.0, npm v6.14.11,
  • i18next version: ^20.2.1
  • os: Windows

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
Tubaleviaocommented, Apr 19, 2021

Thanks for the reply guys! As @jamuhl and @adrai pointed, I was able to make it work after spending a while discovering how those events and callbacks behave.

The code got a little big since we don’t have a library that throws an error when an error occurs, but now it works!

@nunocastromartins I’ll paste the code here with some comments, in case it helps you:

The i18n.ts file:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend'

i18n
  .use(initReactI18next)
  .use(Backend)
  .init({
    backend:{
      loadPath: '/translations/{{lng}}.json'
    },
    react:{useSuspense:false},
    debug: true,
    lng: "en",
    fallbackLng: "dev",
    keySeparator: false,
    interpolation: {
      escapeValue: false
    }
  });

  export default i18n;

The App.tsx file:

import { Backdrop, CircularProgress, Button } from "@material-ui/core"
import { makeStyles } from '@material-ui/core/styles';
import './App.css';
import { useTranslation } from 'react-i18next';
import { useState } from "react"
import { Subject, interval  } from 'rxjs';
import { debounce } from 'rxjs/operators';

const useStyles = makeStyles((theme) => ({
  backdrop: {
    zIndex: theme.zIndex.drawer + 1,
    color: '#fff',
  },
}));

const fail = new Subject<{ lang: any; i18n: any; msg:string; setError:any; setLoading:any;}>()
const lastFail = fail.pipe(debounce(() => interval(100)))
lastFail.subscribe(({lang, i18n, msg, setError, setLoading}) => {
  // msg this is the message of the error!
  setError(msg)
  setLoading(false)
  i18n.changeLanguage(lang)
})

function App() {
  const [lang, setLang] = useState("en");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);
  const { t, i18n, ready } = useTranslation();
  
  i18n.on('failedLoading', (lng,ns,msg) => {
    // failedLoading is called multiple times for the same error
    // so we use a debounce operator to get just the last one
    if(lng !== lang) fail.next({lang, i18n, msg, setError, setLoading})
  })

  const handle = async () => {
    setError('')
    setLoading(true)
    const newLang = lang==="en"?"pt":"en"
    i18n.changeLanguage(newLang, async (err, t) => {
      if(err){ 
        // since the fallbackLng:dev has no translations, go back to en
        i18n.changeLanguage(lang)
      } else {
        // even if err is null, we test if it's using the falbackLng
        if(!error && t('pig') !== 'pig') {
          // if we have some translation, we are done!
          setLang(newLang)
          setLoading(false)
        } else {
          // this means the resource download failed and we are on falbackLgn
          // now every click on the button will lead to here, so we try downloading again
          await i18n.reloadResources(newLang, "translation", () => {
            // we check if we have some translation here, if we do we are done!
            if(t('pig') !== 'pig') setLang(newLang)
          });
          setLoading(false)
        }
      }
    })
  }
  
  const classes = useStyles();
  return (
    <div className="App">
      <Backdrop className={classes.backdrop} open={loading || !ready}>
        <CircularProgress/>
      </Backdrop>
      {ready && (
        <>
          <h1>{t("pig")}</h1>
          <Button onClick={handle} >Click me</Button>
          <h1>{lang}</h1>
          <h2>{error}</h2>
        </>
      )}
      
    </div>
  );
}

export default App;

And then I created an empty json inside my dev.json file, in the translations folder.

0reactions
jamuhlcommented, Apr 18, 2021

because

either it is loading -> suspense is thrown or it loaded / or gave up after retries

if your webapp and translations are running on the same server nothing will run anyway. if you run multiple instances / microservices reload mechanism in i18next should be enough.

but feel free to add a PR

Read more comments on GitHub >

github_iconTop Results From Across the Web

Drupal install throwing "Translation file not found" notice for ...
Problem /Motivation After enabling English (not translatable) in a working site, I get daily a lot of messages: Translation file not found.
Read more >
GitHub Integration throw error: Error while uploading translation
the file that fails to be synced refers to the source file that the system mistakenly considers as a translation file.
Read more >
Common Errors with Exporting and Importing Translation Files
This error message only applies to data translation files. Data translation isn't enabled or was disabled after the data translation file was exported....
Read more >
ngx-translate is not working in Angular 9 #1187 - GitHub
It throws warnings while ngcc compilation but still works. I installed package with angular 8@latest and after updated it to 9@latest version.
Read more >
i18next is not loading the translation file - Stack Overflow
It is just some random number, and it forces browser to download file, even if it was in browser cache previously. You will...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found