i18next / react-i18next

Internationalization for react done right. Using the i18next i18n ecosystem.
https://react.i18next.com
MIT License
9.33k stars 1.03k forks source link

missingKey issue #322

Closed wijdench closed 6 years ago

wijdench commented 7 years ago

Hi, i get this console error in any interaction with my view 'i18next::translator: missingKey fr backoffice'

even i tried ti put react: { wait: true, }

but this did not work

Help!

jamuhl commented 7 years ago

looks like render gets called before i18n.init

wijdench commented 7 years ago

i18next is initialized in the parent page and this is my i18next.js file import i18next from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import XHR from 'i18next-xhr-backend';

i18next .use(XHR) .use(LanguageDetector) .init({ debug: true, backend: { loadPath: '/locales/resources.json?lng={{lng}}&ns={{ns}}', allowMultiLoading: true, }, fallbackLng: { 'en-US': ['en'], 'fr-FR': ['fr'], default: ['en'], }, ns: ['common', 'search'], interpolation: { escapeValue: false, // not needed for react!! },

react: { wait: true, },

});

export default i18next;

jamuhl commented 7 years ago

nothing to do with i18next config...do you "require/import" i18n file before calling ReactDOM.render

Best might be providing a webpackbin like https://www.webpackbin.com/bins/-KvzvUPFsBVI_74Jll99 so i could have a look.

sojimaxi commented 7 years ago

@wijdench I had the same problem once and my app translate before backend connector loads the resource bundle. I could see the output in console. The best solution would be to wrap it with an I18nextProvider and that takes care of it.

wijdench commented 7 years ago

I'm using I18nextProvider but this does not solve my problem

jamuhl commented 7 years ago

not wrapped your component with the translate hoc? not seeing from your config alone why it should not work...?!?

https://www.webpackbin.com/bins/-KvzvUPFsBVI_74Jll99 works in the bin...so must be something in your app

could you please do a bin to reproduce?

jamuhl commented 7 years ago

any update on this?

viiiprock commented 7 years ago

Hi @jamuhl , I got similar issue when trying using i18n.t('key') My config:

import i18n from 'i18next'
import Backend from 'i18next-xhr-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
import { reactI18nextModule } from 'react-i18next'

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(reactI18nextModule)
  .init({
    preload: ['vi-VN', 'en-US'],
    load: 'currentOnly',
    fallbackLng: 'en-US',
    ns: ['translations'],
    defaultNS: 'translations',
    debug: true,

    interpolation: {
      escapeValue: false, // not needed for react!!
    },

    react: {
      wait: true,
      bindI18n: 'languageChanged loaded',
    }
  }, (err, t) => {
    console.log(t("header.signup"))
  })

export default i18n

console.log(i18n.t("header.signup"))

And the log is

screen shot 2017-11-26 at 7 47 04 am

It works well with react stuff but when I trying to use function i18n.t('key') in some cases, the missing error is i18next::translator: missingKey undefined translations
How do I fix ? Thanks for help.

jamuhl commented 7 years ago

you get missing key header.signup because you call t before i18next has loaded the translations ---- loading is an xhr request, which per definition is asynchronous.

i18next.init(options);
i18next.t('key'); // not loaded -> error

i18next.init(options, () => { i18next.t('key'); // loaded -> no error});

that's why you have the translate hoc or the i18n render prop - those assert everything is loaded (if wait is set to true) before rendering

viiiprock commented 7 years ago

clear, @jamuhl , it should be async, I have a bit struggling to add translate function into my constraints validation messages object (https://validatejs.org), any hint?

jamuhl commented 7 years ago

delay building the constraints -> use https://www.i18next.com/api.html#oninitialized or onLoaded

viiiprock commented 7 years ago

Thanks @jamuhl so much, I'll try this, have a nice day! I solved by a simple function like const callLang = () => i18n.on("load"), then call it in validator function. Awesome.

developdeez commented 5 years ago

Same issue. I was following the tutorial and everything was working but once I replaced the jsons with the backend I get Missing Key. *i18n is loaded first in my App,js I have to use the src folder due to react-app constraint. So I changed the default below

import i18n from "i18next";
import detector from "i18next-browser-languagedetector";
import backend from "i18next-xhr-backend";
import { reactI18nextModule } from "react-i18next";
// translations are already at
// '../public/locales/en/translation.json'
// which is the default for the xhr backend to load from​
i18n
  .use(detector)
  .use(backend)
  .use(reactI18nextModule) // passes i18n down to react-i18next
  .init({
    backend: {
      loadPath: "./locales/{{lng}}/translation.json"
    },
    lng: "en",
    fallbackLng: "en", // use en if detected lng is not available
    keySeparator: false, // we do not use keys in form messages.welcome
    interpolation: {
      escapeValue: false // react already safes from xss
    },
    react: {
      wait: true
    }
  });
export default i18n;
jamuhl commented 5 years ago

@developdeez is your application based on create-react-app? if so the public folder should be accessible during development (and added into the production build). https://github.com/i18next/react-i18next/tree/master/example/react/public

developdeez commented 5 years ago

@jamuhl I am using create react app. I get this error when doing it that way: ./src/i18n.js Module not found: You attempted to import ../public/locales/de/translation.json which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.

Is there a way for it to use the src folder? Seems the backend may be checking the wrong place or something.

jamuhl commented 5 years ago

Module not found: You attempted to import ../public/locales/de/translation.json which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.

This states you try to import those files - you not have to import them using the xhr backend.

developdeez commented 5 years ago

I see that error lead me to move the files causing more errors. Thanks Unrealted: but does detector work automatically. I changed my browser to German and it still says its English. Also is add missing automatic? I removed a definition from the json in German and refreshed. It showed in english but no add folder was created showing it missing.

jamuhl commented 5 years ago

detector per default once detected sets your lng in localstorage -> means if you programmatically use changelanguage that would be stored too -> next visit is always based on the previous set or last detection. Deleting that localstorage entry or incognito should show the expected result.

jamuhl commented 5 years ago

Missing depends on your backend able to handle this...and on how it is configured (https://www.i18next.com/overview/configuration-options#missing-keys) but anyway will only trigger if it does not find that new key (not in fallback - so you got that key to your source language -> from there a translation management like eg. our https://locize.com should take care of having all keys in all target langauges)

NurbekGithub commented 5 years ago

Hey, @jamuhl, first of all thanks for great work. I have this issue when I need two namespaces in a component the t function is searching in the first ns only. How to make it look for all namespaces I provided inside withNamespaces(['ns1', 'ns2'])? My init is:

   i18n
  .use(XHR)
  .init({
    debug: process.env.NODE_ENV === 'development',
    ns: ['common'],
    defaultNS: 'common',
    nonExplicitWhitelist: true,
    lng: 'ru', // 'kz' | 'en' | 'ru'
    fallbackLng: 'ru',
    backend: {
      loadPath: process.env.NODE_ENV === 'development'
        ? '/locales/{{lng}}/{{ns}}.json'
        : `/${process.env.REACT_APP_APP_NAME}/client/locales/{{lng}}/{{ns}}.json`
    },
    react: {
      wait: true
    }
  });
jamuhl commented 5 years ago

withNamespaces(['ns1', 'ns2']) only tells the code to load this two namespaces (or assert that they got loaded). ns1 will be used as primary namespace, so:

t('key') will return translations from ns1 t('ns2:key) will return translation from ns2

https://www.i18next.com/translation-function/essentials#accessing-keys-in-different-namespaces

Alternative you can set: nsMode: 'fallback' -> https://react.i18next.com/components/namespacesconsumer#namespacesconsumer-props

NurbekGithub commented 5 years ago

@jamuhl This is now very clear. Thanks!

jamuhl commented 5 years ago

If you like this module don’t forget to star this repo. Make a tweet, share the word or have a look at our https://locize.com to support the devs of this project -> there are many ways to help this project :pray:

jamuhl commented 5 years ago

i18next::translator: missingKey undefined translation nav.help nav.help

undefined is the language

looks like a component renders and access that key before i18next did init -> Guess that component does not use the HOC or hook

aleksandraif commented 5 years ago

Hi! React React i18next. In the console appears the error: i18next::translator: missingKey undefined translation settings.title settings.title. My config:

import i18n from 'i18next'; import XHR from 'i18next-xhr-backend'; import { initReactI18next } from 'react-i18next'; i18n .use(XHR) .use(initReactI18next) // bind react-i18next to the instance .init({ fallbackLng: false, debug: true, interpolation: { escapeValue: false, // not needed for react!! }, lng: 'ru', // 'en' | 'es'

backend: { loadPath: './locales/{{lng}}/translation.json', }, react: { wait: true, }, }); export default i18n; index.js: import as React from 'react'; import as ReactDOM from 'react-dom'; import { I18nextProvider } from 'react-i18next'; import i18n from '@app/core/i18n'; import { App } from './App'; ReactDOM.render(

, , document.getElementById('root')); App.js: import * as React from 'react'; import { Route, Switch, HashRouter } from 'react-router-dom'; import { Setting } from '@app/component/Setting'; import i18n from '@app/core/i18n'; interface Props {} interface State { lng: string; } export class App extends React.PureComponent { constructor(props: Props) { super(props); this.state = { lng: 'en', }; this.onLanguageChanged = this.onLanguageChanged.bind(this); } private onLanguageChanged(lng: string) { this.setState({ lng, }); } public componentDidMount() { i18n.on('languageChanged', this.onLanguageChanged); } public componentWillUnmount() { i18n.off('languageChanged', this.onLanguageChanged); } render() { return (
); } } export default App; Header.js: import * as React from 'react'; import i18n from '@app/core/i18n'; interface IProps { } interface IState { message: string; } export class Setting extends React.Component { constructor(props: IProps) { super(props); } public render() { return (

{i18n.t('settings.title')}

); } }
jamuhl commented 5 years ago

same like above

jramalho commented 5 years ago

Facing the same issue. Some keys sometimes dont load. The keys are specific but whether they load or no is random. The error is something like: 'i18next::translator: missingKey', 'pt-BR', 'app', 'theMissingKey'.

Using react-native with react-i18next:10.6.2 and i18next: 15.0.10.

` import i18next from "i18next"; import { initReactI18next } from "react-i18next"; import DeviceInfo from "react-native-device-info"; import Locize from "./locize"; export const DEFAULT_LANGUAGE = "pt-BR"; import { version } from "../../package.json";

i18next .use(Locize) .use(initReactI18next) .use({ type: 'languageDetector', detect: () => DeviceInfo.getDeviceLocale(), init: () => {}, cacheUserLanguage: () => {}, }) .init({ debug: true, initImmediate: false, saveMissing: true, fallbackLng: DEFAULT_LANGUAGE, ns: ['app'], interpolation: { escapeValue: false, },

react: {
  useSuspense: false,
},

backend: {
  private: __DEV__,
  version: __DEV__ ? 'develop' : version,
  projectId: '695e6211-ac07-4d1f-aa21-754bc8d86693',
  apiKey: 'b9ab9f51-c271-4eec-84c0-4ce31cd4ecc2',
  referenceLng: DEFAULT_LANGUAGE,
  setContentTypeJSON: true,
},

});

export default i18next;

`

jamuhl commented 5 years ago

@jramalho you're loading the translations asynchronously from our locize service and set useSuspense: false, so as long you do not handle ready https://react.i18next.com/latest/usetranslation-hook#not-using-suspense

const { t, i18n, ready } = useTranslation('ns1', { useSuspense: false });--> if ready false you will get missing...timings are random - but if you do not get that ready: false and missings that's more or less luck...

So either useSuspense or do not render your content before that value is ready: true

jramalho commented 5 years ago

@jamuhl Thanks for the response, we are using the withTranslation HOC. It gives me a tReady prop, we put that on the shouldComponentUpdate and now it works fine. By the way, the prop tReady is not on the documentation. We discovered it opening the lib. But thanks again. That solved our problem.

jamuhl commented 5 years ago

@jramalho yes it's only in the code commented here: https://react.i18next.com/latest/withtranslation-hoc#not-using-suspense --> will update the documentation for the "not suspense" case making it more clear - absolutely agree with you this is too important when disabling suspense.

bitttttten commented 5 years ago

I am using the Translation component only and I am getting the same error. In my App:

<Translation>
    {t => (
        <Home
            routeName="home"
            aria-label={t('home.title')}
        >
            <StyledIconVC />
        </Home>
    )}
</Translation>

Then on my server I set it up like so:


i18next
    .use(initReactI18next)
    .use(nodeFsBackend)
    .init({
        ns: ['common'],
        fallbackLng: lang,
        debug: true,
        lng: lang,
        keySeparator: '__KEY__',
        defaultNS: 'common',
        interpolation: {
            escapeValue: false,
        },
        react: {
            useSuspense: false,
            wait: true,
        },
    })

Then when I render:

try {
    await i18next.loadLanguages(lang)
} catch (e) {
    // eslint-disable-next-line no-console
    console.error('[renderApp:loadLanguages]', e)
}

const withI18n = React.createElement(
    I18nextProvider,
    { i18n },
    App,
)

I get the same error as reported here:

i18next::backendConnector: loaded namespace common for language nl { 'home.title': 'Home', 'my_profile.title': 'My Profile', 'recipes.title': 'Recepten', 'menuplanner.title': 'Planner', 'shopping.title': 'Boodschappen' } i18next: languageChanged nl i18next: initialized { debug: true, initImmediate: true, ns: [ 'common' ], defaultNS: 'common', fallbackLng: [ 'nl' ], fallbackNS: false, whitelist: false, nonExplicitWhitelist: false, load: 'all', preload: false, simplifyPluralSuffix: true, keySeparator: 'KEY', nsSeparator: ':', pluralSeparator: '', contextSeparator: '_', partialBundledLanguages: false, saveMissing: false, updateMissing: false, saveMissingTo: 'fallback', saveMissingPlurals: true, missingKeyHandler: false, missingInterpolationHandler: false, postProcess: false, returnNull: true, returnEmptyString: true, returnObjects: false, joinArrays: false, returnedObjectHandler: false, parseMissingKeyHandler: false, appendNamespaceToMissingKey: false, appendNamespaceToCIMode: false, overloadTranslationOptionHandler: [Function: handle], interpolation: { escapeValue: false }, lng: 'nl', react: { useSuspense: false, wait: true } } ---- render happens ---- i18next::translator: missingKey undefined common my_profile.title my_profile.title i18next::translator: missingKey undefined common home.title home.title i18next::translator: missingKey undefined common home.title home.title i18next: languageChanged nl i18next: initialized { debug: true / .. / }

Yet I only get the keys rendered from the server. It seems like I am missing something really obvious..

jamuhl commented 5 years ago

@bitttttten yes...you miss something very obvious...there is no wait option -> https://react.i18next.com/latest/withtranslation-hoc#not-using-suspense

bitttttten commented 5 years ago

I appreciate the link but it doesn't mention in there anything about the wait option. Do you mean the one that I have set in the react-i18next options?

Also I am using Suspense in my app, but this is just for SSR. So it should not be triggering the Suspense loader.

jamuhl commented 5 years ago

@bitttttten no mention of wait might say it does not exist...and for SSR (adding all namespaces to the ns array and setting preload option to all languages might be a good idea...so you neither run into a not ready nor trigger a suspense)

bitttttten commented 5 years ago

I saw it in one of your github comments and I assumed it's still there because it's still in the typings (ReactOptions.wait?: boolean). But, no problem! I can remove it.

Thanks for sticking it out with me thus far... currently, I only have one namespace and it's being loaded into i18n successfully, it's also the default namespace. I called it 'common'. So now on the server, I am doing:

await i18next.loadLanguages(lang)
await i18next.loadNamespaces('common')

(side note: although if I do omit fallbackLng from the init options then it attempts to load the 'dev' langauge, even though I am setting lng: 'nl' to load the language... I am not sure if this is an issue or not :))

I feel totally at a loss right now! I even copy and pasted the setup from a working example 🤔

Here is my current config at the moment...

i18next
    // pass the i18n instance to react-i18next
    .use(initReactI18next)
    // use node-fs instead of xhr backend
    .use(webpackContextBackend)
    .init({
        ns: ['common'],
        debug: true,
        lng: 'nl',
        load: 'all',
        fallbackLng: 'nl',
        keySeparator: '__KEY__',
        defaultNS: 'common',
        interpolation: {
            escapeValue: false,
        },
        react: {
            useSuspense: false,
        },
    })
jamuhl commented 5 years ago

that comment was november 18 and for the old v9!!! regarding the typings...in that case those are wrong...as I do not maintain them myself you might provide a PR and ping @rosskevin for a review.

Really the only option on client is using either Suspense or render an alternative UI if not ready as loading translations from the server is done async.

If you need to do SSR have a look at https://github.com/isaachinman/next-i18next how they solved it (passing translations from server to client and pass them to i18next before rendering by using https://react.i18next.com/latest/ssr)

bitttttten commented 5 years ago

Okay, thanks. I will open a PR to fix the typings.

I think I have the passing down translations correctly. It is that on the server, it does not pick up any translations. When the client renders, it then fetches the translations and renders correctly. Although you see a flash of i18n keys, and then the text appearing. So my issue is actually on the server 🤔

For example, on the server render:

<p>shopping.title</p>

When client renders:

<p>Boodschappen</p>

But thanks, I will check out the nextjs example!

jamuhl commented 5 years ago

did you use the i18next-node-fs-backend ?!? might be just the path to your translations is wrong?

bitttttten commented 5 years ago

On the server I am actually using a custom backend, which does work in another project. I added logging in there and it does send back data correctly.

It's a very simple custom backend, just reads from a big object of this.storage[language][namespace]. I can share it later once I am at a desktop and not on my phone :)

bitttttten commented 5 years ago
interface ILocales {
    [key: string]: {
        [key: string]: object
    }
}

const context = require.context('../../src/i18n/locales/', true, /\.json$/)
const locales: ILocales = {}

context.keys().forEach(key => {
    const [, lang, file] = key.split('/')
    locales[lang] = locales[lang] || {}
    const [namespace] = file.split('.')
    locales[lang][namespace] = context(key)
})

class WebpackContextBackend {
    public static type: string
    public services: any
    public storage: ILocales

    public constructor(services: any) {
        this.services = services
        this.storage = locales
        this.type = 'backend'
    }

    public read(
        language: string,
        namespace: string,
        callback: (error: string | null, response: any) => any,
    ) {
        try {
            const resource = this.storage[language][namespace]
            callback(null, resource)
        } catch (e) {
            console.log('[i18next-webpack-context:read]', e)
            return callback(e, false)
        }
    }

    public save(language: string, namespace: string, data: object) {
        /* .. */
    }
}

WebpackContextBackend.type = 'backend'

export default WebpackContextBackend
jamuhl commented 5 years ago

Hm....are you using i18next-express-middleware on serverside? Or how do you pass the i18next instance (per request - asserting the language is bound to the request - requests user language)...

bitttttten commented 5 years ago

No, I am just wrapping the app with the provider. It worked very well like this in another project.

Currently I am just using 'nl' for the language, just to get it working. I will then work on languages after that :p

const chunkCapture = React.createElement(
    Loadable.Capture,
    { report: moduleName => modules.push(moduleName) },
    React.createElement(App, { rootStore }),
)

const withI18n = React.createElement(
    I18nextProvider,
    { i18n },
    chunkCapture,
)

const html = ReactDOMServer.renderToString(withI18n)
jamuhl commented 5 years ago

Hm...i hope you thought about how this works...but if you got any async operation inside this - it will work - is just a - it will work on my machine. Can't work in production (or only work as long you only got one language) - requests are async - any operation async can end in the event loop giving another request the opportunity to change the language on i18next and you end up with mixed languages...=> so you end in concurrency issues

That's the reason why i18next-express-middleware creates a i18next instance per user to be save on parallel requests from multiple users with different languages (using a singleton will bring funny results).

Further eg. next-i18next inject that req.i18n into the provider for SSR...

bitttttten commented 5 years ago

Right, so I should clone the instance on each request? Something like:

    const i18n = i18next.cloneInstance()
    i18n.changeLanguage(lang)
    await i18n.loadLanguages(i18n.language)
jamuhl commented 5 years ago

yes -> but yet that won't solve your issue -> as you some where still do render before loading is done...at least looking at:

--- render happens ----
i18next::translator: missingKey undefined common my_profile.title my_profile.title
i18next::translator: missingKey undefined common home.title home.title
i18next::translator: missingKey undefined common home.title home.title
i18next: languageChanged nl
i18next: initialized { debug: true /* .. */ }

Not having a reproducible stripped-down example it's hard to help...my guess await i18n.loadLanguages(i18n.language) is called somewhere but not inside the request or not inside an async function - but I'm not to deep into serverside usage of async await

ebs12345 commented 5 years ago

Hey error is next-i18next::translator: missingKey undefined translations How do I fix ?

jamuhl commented 5 years ago

@ebs12345 by making sure you do not call any t before i18next.init is done. Make sure you use either withTranslation or useTranslation and have the language detector or set lng on init

developdeez commented 5 years ago

I check tReady before rendering the component.

From: Priyanka notifications@github.com Sent: Thursday, November 14, 2019 11:48 PM To: i18next/react-i18next react-i18next@noreply.github.com Cc: developdeez cecilcjcarter@gmail.com; Mention mention@noreply.github.com Subject: Re: [i18next/react-i18next] missingKey issue (#322)

Hey error is next-i18next::translator: missingKey undefined translations How do I fix ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/i18next/react-i18next/issues/322?email_source=notifications&email_token=ADDULRJ72WWIDVZ66TYBTW3QTZH2PA5CNFSM4D6W2322YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEETK4Q#issuecomment-554251634 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ADDULRM3S6IWXZXLWW6A7FLQTZH2PANCNFSM4D6W232Q . https://github.com/notifications/beacon/ADDULRNJ2WOGKBCDQRF6MF3QTZH2PA5CNFSM4D6W2322YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEETK4Q.gif

jamuhl commented 5 years ago

still: the info says there is a missing key (not loaded, not initialized)

missingKey undefined translations ---> missingKey currentLng currentNamespace

so there is somewhere something wrong in your code - I can't find magically issues in your code...

ebs12345 commented 5 years ago

Hey @jamuhl i have implemented next-i18next package for multi-language. But I have the issue that when the default value is selected the language key is not loaded or key value are printed .I have used s3 amazone server for the uploaded json file. So can you help me please.