jspru
jspru

Reputation: 1238

Using react-i18next with React standalone component - 'missingKey' error

I am getting 'missingKey' error messages from i18next even though the translations appear to be loading (can see console logging). What am I doing wrong here?

Here is my i18n.js file:

import i18n from 'i18next'
import i18nhttp from 'i18next-http-backend'
import Backend from 'i18next-chained-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'

const options = {
  backends: [
    i18nhttp
  ],
    
  backendOptions: [
    { loadPath: '/api/constants/{{lng}}/{{ns}}.lang.json' }
  ]
}

i18n.use(Backend).use(LanguageDetector).use(initReactI18next).init({
  backend: options,
  ns: ['fin'],
  defaultNS: 'fin',
  fallbackNS: 'fin',
  fallbackLng: 'en',
  keySeparator: false,
  debug: true,
  detection: {
    order: ['queryString', 'cookie'],
    cache: ['cookie'],
    lookupCookie:'website#lang',
  },
  interpolation: {
    escapeValue: false
  },
  react: {
    useSuspense: true
  }
}, (err, t) => {
  if (err) return console.log('something went wrong loading', err);
  t('pinDescription') 
})

export default i18n

And here is my React component file:

import React, { Suspense }  from 'react'
import ReactDOM from 'react-dom'
import '../../../scripts/i18n'
import { useTranslation } from 'react-i18next'

  const Translation = () => {
    const { t, i18n } = useTranslation()
  
    return (
      <div className="translation-sample">
        <h2>Translation Sample - from files</h2>
        <p>{t('accountNumberDescription')}</p>
      </div>
    )
  }

  const domElement = document.getElementById('translation-sample')

  if (domElement) {
    ReactDOM.render(
      <React.StrictMode>
        <Suspense fallback={<div>Loading ... </div>}>
          <Translation />
        </Suspense>
      </React.StrictMode>,
      domElement
    );
  }

For the translation that I'm trying to render in the init callback (t('pinDescription')) I'm getting this error:

i18next::translator: key "pinDescription" for languages "en" won't get resolved as namespace "fin" 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!!!

Which is strange since the react-i81n docs indicates that this callback runs after the translations have loaded.

For this and for the key I'm trying to render in the component I also get 'missing Key' error:

i18next::translator: missingKey en fin pinDescription pinDescription
i18next::translator: missingKey en fin accountDescription accountDescription

I have tried several things including:

  1. Wrapping the init call in a promise, exporting that function and awaiting it in the component
  2. Using the HOC method
  3. Exporting the i18n instance from the i18n.js file and then importing it in the component and calling init (tried both awaiting and promise / then)

Additionally, if I use a Suspense wrapper component I see that the translations have loaded in the console (however I still get missing key errors). When I remove 'Suspense' and opt out of it in the i81n.js config, then I never see any indication in the console that the translations have loaded.

I feel like I'm missing something fundamental here. What is the best way to await the init / translation loading process before using 't' in the component?

The main difference between what I am doing and what most of the examples in the documentation show is that they show i18n.js being loaded in the index.js file of a React application. I don't have any index.js file or React application - I am working with a single React component that will later be injected into an existing non-React application.

Additionally, there are no network errors, I can access the language json files directly in the browser without a problem and I can see the translations loading in the console.

Output from the browser console (I'm using a different namespace here, but the code is the same):

enter image description here

Thanks in advance for any tips or for pointing out where I am going wrong!

Upvotes: 1

Views: 2691

Answers (1)

felixmosh
felixmosh

Reputation: 35563

You missed the i18next provider

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import i18n from '../../../scripts/i18n';
import { useTranslation, I18nextProvider } from 'react-i18next';

const Translation = () => {
  const { t } = useTranslation();

  return (
    <div className="translation-sample">
      <h2>Translation Sample - from files</h2>
      <p>{t('accountNumberDescription')}</p>
    </div>
  );
};

const domElement = document.getElementById('translation-sample');

if (domElement) {
  ReactDOM.render(
    <React.StrictMode>
      <I18nextProvider i18n={i18n}> // <----
        <Suspense fallback={<div>Loading ... </div>}>
          <Translation />
        </Suspense>
      </I18nextProvider>
    </React.StrictMode>,
    domElement
  );
}

Upvotes: 0

Related Questions