i18next / next-i18next

The easiest way to translate your NextJs apps.
https://next.i18next.com
MIT License
5.51k stars 762 forks source link

How to use next-i18next with storybook? #1843

Closed kristijorgji closed 2 years ago

kristijorgji commented 2 years ago

I would like to integrate next-i18next with our storybook that is used in our nextjs app.

I cannot use appWithTranslation wrapper` as the storybook wrapper is just a normal react component and not App (ts fails as well)

One story is defined for example as Banana.stories.tsx

storiesOf('Banana', module).add('default', () => {
    return (
        <StoryWrapper>
            <BananaForm {...props} />
        </StoryWrapper>
    );
});

and StoryWrapper.tsx is just adding the styled components context to all the stories

    <ThemeProvider theme={getTheme(props.theme)}>
        <Fragment>
            {props.children}
        </Fragment>
    </ThemeProvider>

If BananaForm contains useTranslation hook storybook will crash and that brings me to the question posted here.

How to integrate this library with storybook ?

Would be really awesome developer experience to use same translations as the real project

isaachinman commented 2 years ago

storybook will crash

How?

If you can't wrap your code in appWithTranslation, you will need to figure out another way to wrap it with an I18nextProvider.

kristijorgji commented 2 years ago

I can reproduce that very easy in my current code.

If I add this line in render method of my BananaForm.tsx component (just example name) const { t } = useTranslation();

than try to start storybook, it will crash

 __child-HtmlWebpackPlugin_0  6.23 KiB  HtmlWebpackPlugin_0  HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/@storybook/core-common/dist/cjs/templates/index.ejs] 1.98 KiB {HtmlWebpackPlugin_0} [built]
ModuleNotFoundError: Module not found: Error: Can't resolve 'fs' in '/Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/dist/esm/config'
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/lib/Compilation.js:925:10
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/lib/NormalModuleFactory.js:401:22
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/lib/NormalModuleFactory.js:130:21
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/lib/NormalModuleFactory.js:224:22
    at /Users/kristi.jorgji/Desktop/example/node_modules/neo-async/async.js:2830:7
    at /Users/kristi.jorgji/Desktop/example/node_modules/neo-async/async.js:6877:13
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/lib/NormalModuleFactory.js:214:25
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/Resolver.js:213:14
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/Users/kristi.jorgji/Desktop/example/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:44:7
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/Users/kristi.jorgji/Desktop/example/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/Users/kristi.jorgji/Desktop/example/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
    at /Users/kristi.jorgji/Desktop/example/node_modules/webpack/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:67:43
resolve 'fs' in '/Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/dist/esm/config'
  Parsed request is a module
  using description file: /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/package.json (relative path: ./dist/esm/config)
    Field 'browser' doesn't contain a valid alias configuration
    resolve as module
      /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/dist/esm/config/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/dist/esm/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/dist/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/Desktop/example/node_modules/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/Desktop/test/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/Desktop/node_modules doesn't exist or is not a directory
      /Users/kristi.jorgji/node_modules doesn't exist or is not a directory
      /Users/node_modules doesn't exist or is not a directory
      /node_modules doesn't exist or is not a directory
      looking for modules in /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules
        using description file: /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/package.json (relative path: ./node_modules)
          Field 'browser' doesn't contain a valid alias configuration
      looking for modules in /Users/kristi.jorgji/Desktop/example/node_modules
        using description file: /Users/kristi.jorgji/Desktop/example/package.json (relative path: ./node_modules)
          Field 'browser' doesn't contain a valid alias configuration
          using description file: /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/package.json (relative path: ./node_modules/fs)
            no extension
              Field 'browser' doesn't contain a valid alias configuration
          using description file: /Users/kristi.jorgji/Desktop/example/package.json (relative path: ./node_modules/fs)
            no extension
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs doesn't exist
            .mjs
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs doesn't exist
            .mjs
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.mjs doesn't exist
            .js
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.mjs doesn't exist
            .js
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.js doesn't exist
            .jsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.js doesn't exist
            .jsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.jsx doesn't exist
            .ts
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.jsx doesn't exist
            .ts
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.ts doesn't exist
            .tsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.ts doesn't exist
            .tsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.tsx doesn't exist
            .json
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.tsx doesn't exist
            .json
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.json doesn't exist
            .cjs
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.json doesn't exist
            .cjs
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.cjs doesn't exist
            .ts
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.cjs doesn't exist
            .ts
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.ts doesn't exist
            .tsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.ts doesn't exist
            .tsx
              Field 'browser' doesn't contain a valid alias configuration
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs.tsx doesn't exist
              /Users/kristi.jorgji/Desktop/example/node_modules/fs.tsx doesn't exist
            as directory
              /Users/kristi.jorgji/Desktop/example/node_modules/next-i18next/node_modules/fs doesn't exist
            as directory
              /Users/kristi.jorgji/Desktop/example/node_modules/fs doesn't exist

WARN Broken build, fix the error above.
WARN You may need to refresh the browser.

And of course when I remove that, the storybook starts and run fine.

I would love to know a way how to use the next-i18next with storybook or if you can redirect me to some example would be great. Thanks

isaachinman commented 2 years ago

Duplicate of #1713.

thebinaryfelix commented 1 year ago

Just for the record, I've searched for examples and articles about how to use next-i18next with Storybook and this is how I made it work.

// .storybook/i18n.js

import { appWithTranslation } from 'next-i18next'
import { useEffect, useState } from 'react'

const ns = ['common']
const locales = ['pt-BR', 'en']

const resources = ns.reduce((acc, n) => {
  locales.forEach((lng) => {
    if (!acc[lng]) acc[lng] = {}
    acc[lng] = {
      ...acc[lng],
      [n]: require(`../public/locales/${lng}/${n}.json`),
    }
  })
  return acc
}, {})

export default (Story, context) => {
  const [locale, setLocale] = useState('pt-BR')

  const _nextI18Next = {
    ns,
    initialLocale: locale,
    initialI18nStore: {
      [locale]: {
        ...resources[locale],
      },
    },
    userConfig: {
      resources,
      i18n: {
        locales,
        defaultLocale: locale,
      },
    },
  }

  useEffect(() => {
    setLocale(context.globals.locale)
  }, [context.globals.locale])

  const AppWithTranslation = appWithTranslation(Story)

  return (
    <AppWithTranslation
      pageProps={{
        _nextI18Next,
      }}
    />
  )
}
// .storybook/preview.js

import i18n from './i18n'

export const decorators = [
  // other decorators...
  i18n,
]

export const globalTypes = {
  locale: {
    name: 'Locale',
    description: 'Internationalization locale',
    toolbar: {
      icon: 'globe',
      items: [
        { value: 'en', right: 'πŸ‡ΊπŸ‡Έ', title: 'English' },
        { value: 'pt-BR', right: 'πŸ‡§πŸ‡·', title: 'Portuguese' },
      ],
    },
  },
}
// .storybook/main.js

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    // ...
  ],
  webpackFinal: async (config) => {
    config.node = { fs: 'empty' } // this solved the "Module not found: Error: Can't resolve 'fs'"
    return config
  },
}

And on package.json

"storybook": "start-storybook -s ./public -p 6006"
zaratan commented 1 year ago

If anyone is using webpack 5 with storybook @thebinaryfelix solution almost worked for me.

The only difference was in .storybook/main.js as webpack config do not support {node: { fs: 'empty' }} anymore.

module.exports = {
  // …
  webpackFinal: async (config) => {
    config.resolve ||= {}
    config.resolve.fallback ||= {}
    config.resolve.fallback.fs =  false;

    return config
  },
}
nightire commented 1 year ago

For those who use .yaml files:

  1. webpack needs a yaml-loader

    webpackFinal: (config) => {
    config.module.rules.push({ test: /\.ya?ml$/, use: 'yaml-loader' });
    config.resolve.fallback = { ...config.resolve.fallback, fs: false };
    return config;
    },
  2. require yaml file needs a little change:

-[n]: require(`../public/locales/${lng}/${n}.json`),
+[n]: require(`../public/locales/${lng}/${n}.yaml`).default,