React-i18next: Is there a way to trigger rerender after addResourceBundle

Created on 11 May 2020  路  11Comments  路  Source: i18next/react-i18next

Most helpful comment

@tylergraf it's always hard to write good documentation (sitting in the forest not seeing the trees - hard for me to see were users could struggle) -> PR's to improve the docs are very appreciated

All 11 comments

fires added event: https://github.com/i18next/i18next/blob/master/src/ResourceStore.js#L100

you can bind store events to trigger a rerender: https://react.i18next.com/latest/i18next-instance (options)

Oops, I didn't realize I clicked "Submit" after seeing "questions should be posted on StackOverflow". Thanks for your quick reply. I worked around this question by a diferent approach. Will definitely try out your suggestion.

Now that the ticket is here, I will try to add more details. So my use cases are somewhat different. Even though down the line we probably will suport full i18n, for now we are only using i18next doing key substitutions, where keys are regular text in "en", and mapped translations are user-defined substitutions stored in database. There really is no locale other than just "en".

Given the scenario, I don't want to use the backend plugins such as i18next-http-backend to fetch the substitution list or do caching, because to my understanding, they are overkill for my cases. And I don't have static locale files stored on server.

So after seeing this method addResourceBundle in API doc, I thought I can load the resoures myself and add it to the i18n instance, expecting UI will be re-rendered (I am using useTranslation hook). This is the orignal question of the issue.

I ended up prefetching resources on init by not passing resources in InitOptions, and a custom backend plugin doing the fetch:

class InflowBackendPlugin {
  static type: 'backend' = 'backend'
  constructor(services: Services, backendOptions: object, i18nextOptions: InitOptions) {
    // noop if don't use any serivces shipped with i18next and custom options
  }

  read(language: string, namespace: string, callback: ReadCallback) {
    // setTImeout to mimic async request
    setTimeout(() => {
      if (language === 'fr') {
        callback(null, {
          'Sales order': 'Translated "Sales order" in French',
        })
      } else {
        callback(null, {
          'Welcome to React': 'Welcome to react-i18next (from library examble)',
          'Sales order': 'Renamed "Sales order"',
          'Location': 'Bin (renamed from "Location")',
          'purchase order': 'purchase order',
          'purchase order_plural': 'purchase orders'
        })
      }
    }, 3000)

    // if method fails/returns an error, call this:
    //    callback(truthyValue, null);
  }

  save(language: string, namespace: string, data: ResourceLanguage) {
    // noop
  }

  createfunction(languages: string[], namespace: string, key: string, fallbackValue: string) { 
    // noop
  }
}

Init part:

i18n.use(initReactI18next)
  .use(InflowBackendPlugin)
  .init({
    // resources: {},     // not passing, which tells i18next to load them async
    lng: "en",
    fallbackLng: 'en',
    keySeparator: false,  // for our case, we do not use keys in form like "SO.button.pay"
    preload: ['fr'],      // pre-load (lazy-load also possible) other languages
    interpolation: {
      escapeValue: false,
    },
    react: {
      useSuspense: false,
    },
  })

I like that...a little more work -> but at the end - simpler to exchange if you eg. switch using a server backend or locize.com

@jamuhl I have the same issue.
it triggers an 'added' event but when debugging I don't see any observers bound.
my code is the following

init code

i18n
    // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
    // learn more: https://github.com/i18next/i18next-http-backend
    .use(Backend)
    // pass the i18n instance to react-i18next.
    .use(initReactI18next)
    // init i18next
    // for all options read: https://www.i18next.com/overview/configuration-options
    .init({
        backend: {
            loadPath: "/locale/{{lng}}.json"
        },
        fallbackLng: "en",
        debug: true,
        // temporary
        lng: "en",
        interpolation: {
            escapeValue: false // not needed for react as it escapes by default
        },
        react: {
            bindI18n: 'added', //tryed both
            bindStore: 'added' //tryed both

        }
    });

Component
setState is to trigger a render

const B=()=> <Suspense fallback="loading">
                    <A />
                </Suspense>;


const A = () => {
    const [a, setState] = React.useState(0);
    const { t } = useTranslation('project');

    return <div>
        {a}
        component A
        {t('key')}
        <button onClick={() => setState(b => b + 1)}>setState</button>
    </div>
}

adding ressourceBundle

        i18n.addResourceBundle(language, "project", {"key":"test"});

I don't see what's wrong ?

@n-dragon it's bindI18nStore not bindStore https://react.i18next.com/latest/i18next-instance

so you need to add the added event to the options to trigger a rerender when using addRessourceBundle ?

@jamuhl on the version I'm using it's not :
https://github.com/i18next/i18next/blob/v19.4.5/index.d.ts#L129

my version of react-i18next is : 11.5

types are not always the reality...looks like the types are wrong

tks,
it's working.
what is the difference between bindStore and bindI18n?
when to use one rather than another ?

bindI18nStore -> are events on store (added keys, ... not needed for most cases)
bindI18n -> events on i18next (changeLanguage, initialize, ...)

Docs aren't super clear on that. Thanks for clarifying.

@tylergraf it's always hard to write good documentation (sitting in the forest not seeing the trees - hard for me to see were users could struggle) -> PR's to improve the docs are very appreciated

Was this page helpful?
0 / 5 - 0 ratings