react-i18n useSuspense never ends if backend loading fails - reactjs

In my react app, I am using i18next-http-backend to load translation data from backend response. Currently my app works fine in the below configuration:
config.js
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import HttpApi from 'i18next-http-backend';
import axios from "axios";
const backendOptions = {
loadPath: 'http://localhost:3000/messages',
request: async (options, url, payload, callback) => {
try {
const translation = await axios.get(url);
callback(null,{
status: 200,
data: JSON.stringify(translation.data),
});
} catch (e) {
callback(e);
}
},
};
const i18nextOptions = {
debug:true,
backend: backendOptions,
fallbackLng: 'en',
lng: 'en',
load:"languageOnly",
react: {
useSuspense: true,
},
ns: ['translations'],
defaultNS: 'translations'
}
i18n.use(initReactI18next)
.use(HttpApi )
.init(i18nextOptions);
i18n.languages = ['en', 'jp'];
i18n.on('failedLoading', function(lng, ns, msg) {
console.log("backend error");
})
export default i18n;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import './i18n/config';
import {store} from "./app/store";
import {Provider} from "react-redux";
import {ErrorBoundary} from "react-error-boundary";
import {Suspense} from "react";
import RuntimeErrorPage from "./features/error/runtime-error-page";
import Spinner from "./components/common/Spinner";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ErrorBoundary FallbackComponent={RuntimeErrorPage}>
<Suspense fallback={<Spinner/>}>
<Provider store={store}>
<App/>
</Provider>
</Suspense>
</ErrorBoundary>
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
However, if there is any backend error, the app gets stuck in Suspense. If I disable suspense then the i18next initializes without any resource. How can I handle any error comes from request? I need a way to either invoke ErrorBoundary fallback component or redirect to any error page.

i18next never throws an error...
Save those failed loadings somewhere else: https://codesandbox.io/s/react-i18next-http-example-forked-9ted7y?file=/src/i18n.js:188-295
and then create your own logic to show stuff: https://codesandbox.io/s/react-i18next-http-example-forked-9ted7y?file=/src/app.js:155-377
fyi: throwing inside an event listener is not a good idea
btw: why do you want to show an error when translations are not loaded? Why not showing a fallback text? The error is not intended for the end users anyway...

Related

react-i18next the key name is displayed instead of it's value

I'm trying to use different languages for my react-creat-app project and I'm facing this problem where the key name is shown in DOM instead of its value.
index.js looks like this :
import React from "react";
import ReactDOM from "react-dom";
import "./index.scss";
import App from "./components/App/App";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
i18n
.use(initReactI18next)
.use(LanguageDetector)
.use(HttpApi)
.init({
debug: true,
fallbackLng: "ar",
detection: {
order: ["htmlTag", "cookie"],
caches: ["cookie"],
},
backend: {
loadPath: "../public/locales/{{lng}}/translation.json",
},
react: { useSuspense: false },
});
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
locales/ar/translation.json looks like this :
{
"meet_team": "تعرفوا على فريقنا"
}
locales/tr/translation.json looks like this :
{
"meet_team": "ekipimizle tanışın"
}
The component I want to translate looks like this :
import { useTranslation } from 'react-i18next';
const OurTeam = () => {
const { t } = useTranslation();
return <h1 className="foo">{t("meet_team")}</h1>;
};
what is displayed on the page is this :
meet_team
How can I resolve this issue?
The problem was at index.js :
loadPath: "../public/locales/{{lng}}/translation.json",
changing it to :
loadPath: "/locales/{{lng}}/translation.json",
has fixed the problem

Loading in i18n translations without showing translation ids

I've implemented i18next in my nextjs project, and I'm having the issue, that I can briefly see all the text-id's before the translations get fully loaded. Is there any way to avoid showing these text ID's as it causing a bit of flickering on load? It's especially obvious when refreshing the page quickly.
I have tried different ways with both useSuspense and wait in the config and App file. Currently this is what I have (translation files are located in public/locales/xx/translation.json):
i18n.jsx
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
i18n
.use(Backend)
.use(initReactI18next)
.init({
debug: true,
fallbackLng: 'en',
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
react: {
useSuspense: true
}
});
export default i18n;
Usage:
import { useTranslation } from 'react-i18next';
export default function Text () {
const { t } = useTranslation();
return (<Text>{t('text.string.here')}</Text>)
App
import { Suspense } from 'react'
import i18n from 'components/i18n'
function MyApp({ Component, pageProps }) {
<Suspense fallback="loading">
<Component {...pageProps}/>
</Suspense>
}

react-i18next giving a missingKey error in build only

Below are my index.js and i18next.js(the config for i18n)
index.js
import React, { Suspense } from 'react'
import i18n from './i18next'
import './i18next'
import ReactDOM from 'react-dom'
import App from './App'
import * as serviceWorker from './serviceWorker'
import Loading from './components/Styled/Loading/Loading'
i18n.init().then(() =>
ReactDOM.render(
<React.StrictMode>
<Suspense fallback={<Loading />}>
<App />
</Suspense>
</React.StrictMode>,
document.getElementById('root')
)
)
i18next.js
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
// not like to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
const Languages = ['en', 'fr']
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
debug: true,
whitelist: Languages,
keySeparator: false,
defaultNS: 'translation',
ns: ['translation'],
backend: {
loadPath: `/locales/{{lng}}/{{ns}}.json`,
},
load: 'unspecific',
react: {
wait: true,
},
interpolation: {
escapeValue: false,
},
})
export default i18n
console
So, here's the gist. I use the useTranslation hook in my components and it works perfectly fine in localhost. However, I constantly keep getting the missingKey error on production where the build is deployed. Have tried all the feasible settings from other answers, without success. Apologies for the huge content, I just wanted to be thorough.
Figured out the solution. Because I'm serving the build in a sub-directory, I had to append it to loadPath.
.env
REACT_APP_VERSION=$npm_package_version
REACT_APP_PUBLIC_URL='/dy/'
i18next.js
backend: {
loadPath: `${process.env.PUBLIC_URL}/locales/{{lng}}/{{ns}}.json`,
}

React i18next - Don't show translations until request finished

I am getting language info from backend. I would like to make the request to get language info, and then signalize to i18next that it can show translations.
Currently, it shows the default language translations for a second, until the request finishes and I call i18next.changeLanguage().
How would I achieve this ?
This is my config:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import XHR from "i18next-xhr-backend";
i18n
.use(initReactI18next)
.use(XHR)
.init({
fallbackLng: "en-GB",
keySeparator: false,
interpolation: {
escapeValue: false
},
backend: {
loadPath: "/locales/{{lng}}.json"
}
});
I am using the useTranslation hook to get the t function:
const { t } = useTranslation();
In case of react-i18next make sure useSuspense is enabled or handle the ready state in HOCs or hooks yourself.
You need to wrap your root component with Suspense component to determine what should be rendered while translations are loading.
import React, { Suspense } from 'react';
import RealApp from './App';
import Loading from './Loading';
import { I18nextProvider } from 'react-i18next';
import App from './App';
function Root(props) {
return (
<Suspense fallback={<Loading />}>
<I18nextProvider i18n={i18n}>
<Root />
</I18nextProvider>
</Suspense>
);
}
For more info.

Dynamic imports in React

I am trying to import dynamic of a file by means of process.env.NODE_ENV to import a style sheet or another one in production or in development. I have made a condition to load it but it gives me an error Error in ./src/index.js
Syntax error: 'import' and 'export' may only appear at the top level (13: 4) I guess this is not correct but ... how can I do it? I use create-react-app
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './components/App';
import routes from './routes';
import configureStore from './store/configureStore';
import initialState from './reducers/initialState';
if (process.env.NODE_ENV === 'production') {
import './styles/index.css';
}else {
import './styles/index.scss';
}
const store = configureStore(initialState);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Thanks!!!
As Laoujin mentions in the comments, you'll want to use require in this scenario.
For example, here's how I configure access to my Redux store, based on NODE_ENV, which could be adjusted to suit your needs:
const INITIAL_STATE = {};
function getStore () {
const configureStore = process.env.NODE_ENV === 'production'
? require('./configure-store.prod').default
: require('./configure-store.dev').default;
return configureStore(INITIAL_STATE);
}
export default getStore();

Resources