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

Can't find a way to make it work, error with 'options' or 'getFixedT' #252

Closed Pomanks closed 7 years ago

Pomanks commented 7 years ago

Hi,

I'm trying to configure i18next with my React project and I can't make it work at all..

Whatever I try, I get either this :

TypeError: Cannot read property 'options' of undefined
    at new Translate (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/react-i18next/dist/commonjs/translate.js:65:32)

or this :

TypeError: Cannot read property 'getFixedT' of undefined
    at Translate.componentWillMount (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/react-i18next/dist/commonjs/translate.js:84:46)

I initialized my i18n with the React and WebPack 2 example as the following :

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n.use(LanguageDetector)
    .init({
      loadPath:    'locales/{{lng}}/{{ns}}.json',
      preload:     ['en-US', 'fr-FR'],
      fallbackLng: 'fr-FR',
      // wait: true, // globally set to wait for loaded translations in translate hoc

      // have a common namespace used around the full app
      ns: ['common',
        'navbar',
        'footer',
        'header',
        'about',
        'experience',
        'skills',
        'p_app',
        'p_web',
        'cv',
        'social',
        'events',
        'contact',
        'support'],

      defaultNS: 'common',

      debug: true,

      // cache: {
      //   enabled: true
      // },

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

export default i18n

and my component I'm trying to translate is as follow :

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import withStyles from 'isomorphic-style-loader/lib/withStyles'
import Typist from 'react-typist'
import { translate } from 'react-i18next'
import { Parenthesis } from '../../components'

import style from './header.scss'

class Header extends Component {
  static propTypes = {
    t: PropTypes.func.isRequired,
  }

  render() {
    const { t } = this.props

    return (
      <div>

        <section className="header">
          <div className="header_content">
            <div className="header_content_inner">
              <h1 id="homeTextHead">{t('header.title')}</h1>
              <hr className="bg_dark"/>
              <br/>
              <Typist avgTypingDelay={90} className="typed_text">
                {t('header.name')}
                <br/>
                <Parenthesis>{t('header.pseudo')}</Parenthesis>
                <br/>
                <br/>
                {t('header.description')}
              </Typist>
            </div>
          </div>
        </section>

      </div>
    )
  }
}

export default withStyles(style)(translate(['header'], { wait: true })(Header))

I also tried with the decorator but I got the same issues..

@translate(['header'])
class Header extends Component {
...
}

export default withStyles(style)(Header)

Any help is welcome, thanks in advance guys !

jamuhl commented 7 years ago

did you use the i18nextProvider? https://github.com/i18next/react-i18next/blob/master/example/webpack2/app/main.js#L9

Pomanks commented 7 years ago

Yes I did, it is set as follow :

appInstance = ReactDOM.render(
      <I18nextProvider i18n={i18n}>
        <App context={context}>{route.component}</App>
      </I18nextProvider>,
      container,
      () => onRenderComplete(route, location),
    )

And it's in my client.js file

jamuhl commented 7 years ago

<App context={context}>{route.component}</App> what is context there? not sure but seems that overwrites the react context?!?

jamuhl commented 7 years ago

seems like i18n can't be found on the context...which gets provided by the provider

Pomanks commented 7 years ago

The context is set on top of the file (FYI : I'm using Read Starter Kit) and this is it :

const context = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: (...styles) => {
    // eslint-disable-next-line no-underscore-dangle
    const removeCss = styles.map(x => x._insertCss())
    return () => {
      removeCss.forEach(f => f())
    }
  },
  // Universal HTTP client
  fetch:     createFetch({
    baseUrl: window.App.apiUrl,
  }),
}
jamuhl commented 7 years ago

not sure...never saw that...but if this overrides the context it won't work

and it's what the errors state there is no i18n on the context. Try removing the context on your app to test.

or add i18n to the context you inject:

const context = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: (...styles) => {
    // eslint-disable-next-line no-underscore-dangle
    const removeCss = styles.map(x => x._insertCss())
    return () => {
      removeCss.forEach(f => f())
    }
  },
  // Universal HTTP client
  fetch:     createFetch({
    baseUrl: window.App.apiUrl,
  }),
  i18n: require('../wherever/i18next')
}
Pomanks commented 7 years ago

It didn't worked but based on what you said, I modified my App.js from this :

import React, { Children } from 'react'
import PropTypes from 'prop-types'

const ContextType = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: PropTypes.func.isRequired,
  // Universal HTTP client
  fetch:     PropTypes.func.isRequired,
}

class App extends React.PureComponent {

  static propTypes = {
    context:  PropTypes.shape(ContextType).isRequired,
    children: PropTypes.element.isRequired,
  }

  static childContextTypes = ContextType

  getChildContext() {
    return this.props.context
  }

  render() {
    // NOTE: If you need to add or modify header, footer etc. of the app,
    // please do that inside the Layout component.
    return Children.only(this.props.children)
  }
}

export default App

to this :

import React, { Children } from 'react'
import PropTypes from 'prop-types'
import { I18nextProvider } from 'react-i18next'

import i18n from '../i18n'

const ContextType = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: PropTypes.func.isRequired,
  // Universal HTTP client
  fetch:     PropTypes.func.isRequired,
}

class App extends React.PureComponent {

  static propTypes = {
    context:  PropTypes.shape(ContextType).isRequired,
    children: PropTypes.element.isRequired,
  }

  static childContextTypes = ContextType

  getChildContext() {
    return this.props.context
  }

  render() {
    // NOTE: If you need to add or modify header, footer etc. of the app,
    // please do that inside the Layout component.
    return (
      <I18nextProvider i18n={i18n}>
        {Children.only(this.props.children)}
      </I18nextProvider>
    )
  }
}

export default App

and removed the <I18nextProvider/> part from my client.js

I'm not having the TypeErroranymore but when I add the decorator, my components aren't displaying.. 🤔 any idea why ?

jamuhl commented 7 years ago

the translate hoc is configured to wait for loading the resources.

but you did not use eg. xhr-backend to load the translations: https://github.com/i18next/react-i18next/blob/master/example/webpack2/app/i18n.js#L8

or for serverside rendering the fs-backend

but you do not load: so it waits forever

Pomanks commented 7 years ago

Oh right.. sorry, my bad. I did correct that and my views are back as expected!

I must be doing something wrong again though because it's just giving me my key path for my translations but not the translations themselves.. (e.g. : header.title instead of Welcome) not sure I understand why yet but I'll read the doc again 🤔

Pomanks commented 7 years ago

I'm sorry, I'm sure this should be pretty easy but I don't understand why I can't load the resources..

I was using XHR as backend but it told me in console that XMLHttpRequest is not defined so I went with Fetch client and it still doesn't want to load my files..

I also got undefined or [object Object] for my English locale in the console but I get the good one for the French locale fr

jamuhl commented 7 years ago

XMLHttpRequest undefined?!? Wondering...is this in browser? How can that be undefined...?!?

Could you load the resources directly when going to the route locales/en/{{ns}}.json ?!?

Pomanks commented 7 years ago

I couldn't, it's interpreted as a route and tells me the titleproperty is undefined. For XMLHttpRequest it was in the browser's console and in terminal

jamuhl commented 7 years ago

strange...honestly i got no idea...would have to debug your environment...looks odd to me

Pomanks commented 7 years ago

Ok, thanks anyway for your help ! I'll close the initial issue as it was solved.

jamuhl commented 7 years ago

ok...but still wondering what's going wrong...if you find what the cause was please update me...eager to learn

Pomanks commented 7 years ago

I'll sure do, I'm gonna see in the initial starter project and I'll keep you posted

Pomanks commented 7 years ago

I'm just correcting a mistake, it wasn't undefined but not defined (pretty much the same though)

The complete error is :

ReferenceError: XMLHttpRequest is not defined
    at Object.ajax (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next-xhr-backend/dist/commonjs/ajax.js:45:18)
    at Backend.loadUrl (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next-xhr-backend/dist/commonjs/index.js:82:20)
    at Backend.read (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next-xhr-backend/dist/commonjs/index.js:75:12)
    at Connector.read (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/BackendConnector.js:163:25)
    at Connector.readOne (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/BackendConnector.js:226:16)
    at /Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/BackendConnector.js:237:19
    at Array.forEach (native)
    at Connector.load (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/BackendConnector.js:236:23)
    at /Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/i18next.js:251:42
    at Connector.load (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/CacheConnector.js:56:41)
    at I18n.loadResources (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/i18next.js:250:36)
    at I18n.changeLanguage (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/i18next.js:321:10)
    at Timeout.load [as _onTimeout] (/Users/ank49/Documents/React Projects/Portfolio React i18n/node_modules/i18next/dist/commonjs/i18next.js:198:14)
    at ontimeout (timers.js:386:14)
    at tryOnTimeout (timers.js:250:5)
    at Timer.listOnTimeout (timers.js:214:5)
jamuhl commented 7 years ago

you're sure this is not on serverside rendering...?!?

sedubois commented 7 years ago

I also got the error ReferenceError: XMLHttpRequest is not defined (same as https://github.com/i18next/react-i18next/issues/252#issuecomment-298242675) when running gatsby build after having followed the instructions at https://www.gatsbyjs.org/blog/2017-10-17-building-i18n-with-gatsby/. It happens during the step Building static HTML for pages. It does not seem to prevent building, but the error is confusing/annoying.

jamuhl commented 7 years ago

@sedubois ok...in build time that is reasonable there won't be any XMLHttpRequest on node.js ;)...if you like make a PR over at: https://github.com/i18next/i18next-xhr-backend ejecting on https://github.com/i18next/i18next-xhr-backend/blob/master/src/ajax.js#L39 if on serverside

sedubois commented 7 years ago

Thanks @jamuhl, at the moment I’ve been having lots of struggle with HMR and smooth React and dev mode integration, so trialing React-intl instead.

jamuhl commented 7 years ago

https://github.com/i18next/react-i18next/issues/6

sedubois commented 7 years ago

I've read that, but it's quite cumbersome. The closest I got is detailed below. I had to fuse all translation files into one per language otherwise it was beyond my webpack skills, but that defeats the point of loading just the relevant component's translations by XHR. Besides, even with this I still have warnings during gatsby build. I think all these implementation details should be part of the package.

```js // src/utils/i18n.js, make sure to just import it from somewhere import i18n from 'i18next' import XHRBackend from 'i18next-xhr-backend' import LanguageDetector from 'i18next-browser-languagedetector' import { reactI18nextModule } from 'react-i18next' const config = { fallbackLng: 'en', interpolation: { escapeValue: false, // code escaping in interpolated text is not needed with react }, react: { wait: false, }, } if(module.hot) { // config.debug = true config.resources = require('../locales') const translations = ['../locales/en.json', '../locales/fr.json'] module.hot.accept(translations, () => reloadTranslations()) } else { i18n.use(XHRBackend) } i18n .use(LanguageDetector) .use(reactI18nextModule) .init(config) function reloadTranslations() { const translations = require('../locales') Object.keys(translations).forEach(lang => { Object.keys(translations[lang]).forEach(namespace => { i18n.addResourceBundle( lang, namespace, translations[lang][namespace], true, true ) }) }) i18n.emit('loaded') } export default i18n // src/locales/index.js const en = require('./en.json') const fr = require('./fr.json') export default { en, fr } // src/locales/en.json { "MyComponent": { "key1": "value1", ... } } ```
jamuhl commented 7 years ago

In the end you can do whatever you like...one file, n files...all possible. what you mean by part of the package?

sedubois commented 7 years ago

I mean that to the extent that i18next wants to integrate with different frameworks, like react (react-i18next), I think it should provide the tools needed with that framework. The code like I wrote above should be provided within react-i18next instead of in userland, IMHO, otherwise it's a quite high barrier to entry.

jamuhl commented 7 years ago

there are tons of samples in this repo, from react(cra)/preact with render-props or hoc, nextjs, expojs/react-native, razzle, storybook, dat, ...

I'm more than open to accept a PR for a complete gatsby sample...but OSS - don't expect me to go that extra mile for gatsby support too. Still i18next is done in my free time - not backed by a big company - and the community does great work in enriching the ecosystem of i18next.

Even a i18next-gatsby would be awesome...eg. taking react-i18next and extending that for the gatsby use case (if there are bigger extensions needed).

EDIT:

sedubois commented 7 years ago

Of course. A start would be to fix the blog post about Gatsby + i18next. Anyway I just wanted to put out there what I managed to understand so far in case it helps someone.

jamuhl commented 7 years ago

@sedubois Why not taking the extra step and a) make a working sample others can use or even b) make a module extending react-i18next for gatsby?

jamuhl commented 7 years ago

Further i think that blogpost does not need a fix. Samuel did a write up how they solved i18n with gatsby and i18next in their company - looks like it works for them. Does not mean it's meant as a guideline for all or covers everything. It's a good starting point. But neither related to i18next's or gatsby's creators/maintainers...the post does not come from us. But just my opinion.

sedubois commented 7 years ago

I do think the post needs some corrections, as it leads the reader to think that it makes it easier to start with using Gatsby + i18n. Actually it made me lose many hours; without it I would have concentrated on using the pre-existing gatsby-plugin-i18n correctly, which has a working demo to start from.

I will concentrate on understanding how to use gatsby-plugin-i18n and see if I can help improving it. Then the rest can follow.

jamuhl commented 7 years ago

Like said....gatsby-plugin-i18n does the conversion of file extension file.en.js to a url /en/file.js not i18n. Personally naming that plugin i18n is misleading - better would be something like gatsby-plugin-i18n-routes or something like that.

From there you can use react-intl or react-i18next. You could even write a language detector plugin for i18n to read/set language from the url. Then fetch translations or use bundled translations.

Or even more awesome...write a real gatsby-plugin-i18next which takes the translations upfront and replaces translations during build time -> so the final /en/file.js is translated statically -> but might be contra productive if you got dynamic content (react components doing more than just static content) you doomed again (and honestly why one should use gatsby to only generate static content? Mixed content is where it shines in my opinion.

jamuhl commented 7 years ago

Anyway...you found your solution you like to invest your time in and that's great.

sedubois commented 7 years ago

Yes that was my next question, why not putting the translations into the source directly. But first I just need to get something working...

juan-manuel-alberro commented 6 years ago

If anyone is having the error XMLHttpRequest is not defined using gatsby build, just install the package npm i -S xmlhttprequest and then in the top of gatsby-node.js file add:

// XMLHttpRequest polyfill
global.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

And now the build command works as expected, you can verify it by running gatsby serve.

rodrigobdz commented 6 years ago

Shouldn't this be in the docs?

jamuhl commented 6 years ago

not needed in the docs i guess...docs are not for every gatsby, next, razzle, ... would be a never ending story....

BUT: it would be awesome if someone could provide a sample here under example...