I18next with storybook in RedwoodJS

Hi I’ve been exploring redwood lately (sounds like 100 acre wood :grin:) and it’s been a walk in the park pleasant. Many improvements since early days. Thank you!
I got stuck trying to get i18next working with storybook. I did follow the i18next config and redwoods add on tutorial for installation as well as used builder config to point to locale files as require function from i18next docs is not defined.

My question is has anyone been able to make it work and if yes can you please share how you did it?

Thank you and I do hope Redwood can become for JS community what Symfony became for PHP. Good luck!

1 Like

Hey @Sebastian :slight_smile:
Happy to know that you liked the walk !

Did you set up i18n with the command yarn rw setup i18n ?
It should come with all the config for storybook.

If you already have some config, you will probably need to add --force and check the diff.

@simoncrypta Thank you for taking time to reply.

I have just now tried the --force option to reconfigure, no dice…
… but it did change the storyboook.preview.js

Now I am not sure if this is because I created that file or if that’s something that 2.1 introduced.

Should I be installing the storybook-react-i18next addon manually?

I’ll explain what I did:

I installed with yarn workspace web add -D storybook-react-i18next

Probably I added packages from i18next README:

yarn add i18next react-i18next i18next-browser-languagedetector i18next-http-backend

Here’s part of package.json:

"dependencies": {
    "@redwoodjs/forms": "^2.1.0",
    "@redwoodjs/router": "^2.1.0",
    "@redwoodjs/web": "^2.1.0",
    "i18n": "^0.15.0",
    "i18next": "^21.8.14",
    "i18next-browser-languagedetector": "^6.1.4",
    "prop-types": "15.8.1",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-i18next": "^11.18.1"
  },
  "devDependencies": {
    "@storybook/addons": "^6.5.9",
    "@storybook/theming": "^6.5.9",
    "autoprefixer": "^10.4.7",
    "i18next-http-backend": "^1.4.1",
    "postcss": "^8.4.14",
    "postcss-loader": "^7.0.0",
    "storybook-react-i18next": "^1.1.2",
    "tailwindcss": "^3.1.4"
  }

Configured the addon in: storybook.config.js

module.exports = {
  /**
   * This line adds all of Storybook's essential addons.
   *
   * @see {@link https://storybook.js.org/addons/tag/essentials}
   */
  addons: ['storybook-react-i18next'],
};

It did change the

But now when loading Storybook it “hangs” with error (the file is present):

Uncaught Error: Cannot find module 'web/src/i18n'
    at webpackMissingModule (RefreshUtils.js:271:1)
    at ./config/storybook.preview.js (RefreshUtils.js:271:1)
    at options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:24:1)
    at fn (hot module replacement:62:1)
    at ../node_modules/@redwoodjs/testing/config/storybook/preview.js (preview.js:25:1)
    at options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:24:1)
    at fn (hot module replacement:62:1)
    at ../node_modules/@redwoodjs/testing/config/storybook/preview.js-generated-config-entry.js (preview.js:28:1)

VM417:1          POST http://localhost:7910/runtime-error 404 (Not Found)
(anonymous) @ VM417:1
onerror @ iframe.html?viewMode=story&id=*:329
error (async)
(anonymous) @ iframe.html?viewMode=story&id=*:323

I also created a file in web/config/i18next:

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

const ns = ['common'];
const supportedLngs = ['pl', 'en'];

i18n
  .use(initReactI18next)
  .use(LanguageDetector)
  .use(Backend)
  .init({
    debug: false,
    lng: 'pl',
    fallbackLng: 'pl',
    defaultNS: 'common',
    ns,
    interpolation: { escapeValue: false },
    react: { useSuspense: false },
    supportedLngs,
    backend: {
      loadPath: '../src/locales/{{lng}}.json',
    },
  });

export default i18n;

but it is not referenced by any file and config from i18n.js is used.
What is curious Backend is recognized in i18next file but not in i18n

Any suggestions are very welcome.

PS. I should add that Locales work properly in frontend with current setup.

1 Like

It’s weird, the setup command should come with everything you need to start.

Probably I added packages from i18next README:
yarn add i18next react-i18next i18next-browser-languagedetector i18next-http-backend

You should have these packages from the workspace web and since we don’t use SSR right now, we don’t need 18next-http-backend

Configured the addon in: storybook.config.js

RedwoodJS have essential add-ons by default that come with i18n for Storybook.
So you don’t have to add anyhing on storybook.config.js for i18n.

I also created a file in web/config/i18next:

The file for i18n config should be web/src/i18n.tsx or web/src/i18n.js

Here is my config inside the i18n file

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import moment from 'moment'
import { initReactI18next } from 'react-i18next'

import en from './locales/en.json'
import fr from './locales/fr.json'

// This is a simple i18n configuration with English and French translation.
// You can find the translation on web/src/locales/{language}.json
// see : https://react.i18next.com
// Here an example of how to use it in your components, pages or layouts :
/*
import { Link, routes } from '@redwoodjs/router'
import { useTranslation } from 'react-i18next'

const HomePage = () => {
  const { t, i18n } = useTranslation()
  return (
    <>
      <h1>{t('HomePage.title')}</h1>
      <button onClick={() => i18n.changeLanguage('fr')}>fr</button>
      <button onClick={() => i18n.changeLanguage('en')}>en</button>
      <p>
        {t('HomePage.info')} <code>./web/src/pages/HomePage/HomePage.js</code>
      </p>
      <p>
        {t('HomePage.route')} <code>home</code>, {t('HomePage.link')}`
        <Link to={routes.home()}>Home</Link>`
      </p>
    </>
  )
}

export default HomePage
*/
i18n
  .use(initReactI18next)
  // detect user language
  // learn more: https://github.com/i18next/i18next-browser-languageDetector
  .use(LanguageDetector)
  .init({
    interpolation: {
      escapeValue: false, // React already does escaping
      format: function (value, lng) {
        if (value instanceof Date) return moment(value).locale(lng).fromNow()
      },
    },
    fallbackLng: 'fr',
    resources: {
      en: {
        translation: en,
      },
      fr: {
        translation: fr,
      },
    },
  })
export default i18n

You also need to import i18n in App.tsx or App.js

import { I18nextProvider } from 'react-i18next'

import { AuthProvider } from '@redwoodjs/auth'

import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'

import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/Other/FatalErrorPage'

import Routes from 'src/Routes'

import i18n from './i18n'

...

I hope this can help you !

Thank you @simoncrypta

I am not sure yet what worked - I installed i18n setup ~4-5 times with force on Friday (after your reply and today). It was not working. Just now it started after I deleted (again) storybook file and “fixed” the bug I mentioned above:

Uncaught Error: Cannot find module 'web/src/i18n'

This is fixable by changing path to:

import i18n from '../src/i18n';

I would have to verify on clean installation but generated import does not work in Storybook and prevents it from loading.

Prior to ‘now’ all clicks in Storybook worked like clicks on the webpage - they would redirect. I “think” there might have been an issue with underlying installation of node_modules, maybe something was missing and RW was reporting OK since code has not changed one bit from what was not working just a few minutes ago.

I have restored addons and theming packages as well and it still works…
The only lead I have is that it might be related to changes in Redwood 2.1.

I will leave all as is in case anyone else (or me in the future) bumps into this issue again.

1 Like