i18next / next-i18next

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

[8.0.0-beta.4] Module not found: Can't resolve 'fs' #935

Closed kdelmonte closed 3 years ago

kdelmonte commented 3 years ago

Describe the bug

Getting the following error when running the app:

wait  - compiling...
error - ./node_modules/next-i18next/dist/commonjs/serverSideTranslations.js:28:0
Module not found: Can't resolve 'fs'

Occurs in next-i18next version

8.0.0-beta.4

Steps to reproduce

You must provide a reproducible repository, or copy/paste blocks of code which are sufficient to reproduce the bug.

Expected behaviour

You must provide a clear and concise description of what you expected to happen.

Screenshots

image

isaachinman commented 3 years ago

Hi @kdelmonte – this is most likely because you have imported serverSideTranslations in some place where client side code is being run. It can only be used in server side data fetching methods such as getStaticProps and getServerSideProps. Hope that helps!

kdelmonte commented 3 years ago

Thank you @isaachinman , I am only using it inside getServerSideProps:

export const getServerSideProps = async ({ locale }) => ({
  props: {
    ...(await serverSideTranslations(locale, ['common', 'loginPage'])),
  },
})

Please advise.

isaachinman commented 3 years ago

@kdelmonte You'll need to provide a reproducible repo.

kdelmonte commented 3 years ago

@isaachinman I found this issue. I was exporting getServerSideProps from another file into the Next.js page file. Moving getServerSideProps directly to the page file fixed this issue. Thanks.

ivnmood commented 3 years ago

I have same issue, in 8.0.0-beta.4, but I did all the imports correctly and I totally tried to do it like in read.me file and example from the repository

wait - compiling... error - ../node_modules/next-i18next/dist/commonjs/serverSideTranslations.js:28:0 Module not found: Can't resolve 'fs' null react-i18next:: You will need to pass in an i18next instance by using initReactI18next

_app.tsx


import { appWithTranslation } from "next-i18next";
import { AppProps } from "next/app";

const App: React.FC<AppProps> = ({ Component, pageProps }) => {
  const MainLayout = (Component as any).MainLayout || DefaultLayout;
  const NestedLayout = (Component as any).NestedLayout || DefaultLayout;

  return (
    <>
     /.../
              <MainLayout>
                <NestedLayout>
                  <Component {...pageProps} />
                </NestedLayout>
              </MainLayout>
         /.../
    </>
  );
};

const DefaultLayout: React.FC = ({ children }) => <>{children}</>;

export default appWithTranslation(App);

file where i use serverSideTranslations:

import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { GetStaticProps } from "next";
import Grid from "@material-ui/core/Grid";
import { Header } from "../components/Header";

const AppLayout: React.FC = ({ children }) => (
  <>
    <Grid container direction="column" alignItems="center">
      <Header />
      {children}
    </Grid>
  </>
);

export const getStaticProps: GetStaticProps = async ({ locale }) => ({
  props: {
    ...(await serverSideTranslations(locale, ["translation"])),
  },
});

export default AppLayout;

file where i use useTranslation

import { useTranslation } from "next-i18next";

import { NavLink } from "../../../../components/NavLink";

export const Header = () => {
  const { t } = useTranslation();
  return (
/ .../
          <NavLink title={t("something1")} href="/#"  />
          <NavLink title={t("something2")} href="/#"  />
          <NavLink title={t("something3")} href="/#"  />
/ .../
  );
};

next-18next-config.js

import { initReactI18next } from "react-i18next";

const path = require("path");

module.exports = {
  defaultLocale: "en",
  locales: ["en", "ru"],
  localePath: path.resolve("./public/translations"),
  use: [initReactI18next],   // this line did not affect the error in any way
  debug: true,
};

next.config.js

module.exports = {
  images: {
    domains: ["..."],
  },
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      issuer: {
        test: /\.(js|ts)x?$/,
      },
      use: ["@svgr/webpack"],
    });

    return config;
  },
  async redirects() {
    return [
      {
        source: "/",
        destination: "/...",
        permanent: false,
      },
    ];
  },
  i18n: {
    defaultLocale: "en",
    locales: ["en", "ru"],
  },
};
ivnmood commented 3 years ago

I'm resolve this issues, this code

export const getStaticProps: GetStaticProps = async ({ locale }) => ({
  props: {
    ...(await serverSideTranslations(locale, ["translation"])),
  },
});

must be in pages folder, but i'm use it in components folder

kdelmonte commented 3 years ago

This is exactly what happened to me. Beware, future readers.

isaachinman commented 3 years ago

Just want to note that this is a general NextJs "gotcha", and isn't necessarily specific to next-i18next. It's to do with how chunks are separated by webpack, despite there being client and server code in the same files (pages).

dohomi commented 3 years ago

@isaachinman I am using NextJS and Storybook together and I did not share any code SSR related code within both separate builds.

I still realise when I use the useTranslation hook in the Storybook environment I receive the error:

ERROR in ./node_modules/next-i18next/dist/esm/config/createConfig.js
Module not found: Error: Can't resolve 'fs'

I could solve it for now with importing useTranslation from react-118next instead of next-i18next - I am not sure if thats the right way to do this.

// import { useTranslation } from 'next-i18next' // => throws error
import { useTranslation, UseTranslationOptions, UseTranslationResponse } from 'react-i18next'

type TranslationNamespace = 'common'

const useLmTranslation = (
  ns?: TranslationNamespace,
  options?: UseTranslationOptions
): UseTranslationResponse<string> =>
  useTranslation((ns as string) ?? 'common', options)

export default useLmTranslation

EDIT: I add an alias to point via Webpack config to different alias to solve the error for now.

webpackFinal: async (config, {configType}) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      'next-i18next': 'react-i18next'
    }
    return config
  }
isaachinman commented 3 years ago

Hey @dohomi – hopefully we can solve this together.

As a package, next-i18next runs in both server and browser environments. For the createConfig code your error is referencing, we check if process.browser is true.

Strange that process.browser is not true in your Storybook env.

The swap from typeof window === 'undefined' to process.browser was done in #926 as a bundle optimisation. cc @felixmosh – we may need to also add typeof window === 'undefined' back in.

felixmosh commented 3 years ago

@isaachinman, sound OK, I will prepare a PR

isaachinman commented 3 years ago

Fixed by #974.

dohomi commented 3 years ago

@isaachinman @felixmosh I updated to 8.0.1 and the error still exists and Storybook is not building:

ERROR in ./node_modules/next-i18next/dist/esm/config/createConfig.js
Module not found: Error: Can't resolve 'fs' in '/node_modules/next-i18next/dist/esm/config'
 @ ./node_modules/next-i18next/dist/esm/config/createConfig.js 54:15-28
 @ ./node_modules/next-i18next/dist/esm/appWithTranslation.js
 @ ./node_modules/next-i18next/dist/esm/index.js
 @ ./lib/hooks/useLmTranslation.ts

I'm only using the useTranslation hook the rest is run by react-i18next

felixmosh commented 3 years ago

Do you use webpack 5 in you storybook build?

Kalle-kula commented 3 years ago

EDIT: I add an alias to point via Webpack config to different alias to solve the error for now.

webpackFinal: async (config, {configType}) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      'next-i18next': 'react-i18next'
    }
    return config
  }

Putting this in my /storybook/.main.js solved it for me, cheers

wsfung2008 commented 3 years ago

I'm resolve this issues, this code

export const getStaticProps: GetStaticProps = async ({ locale }) => ({
  props: {
    ...(await serverSideTranslations(locale, ["translation"])),
  },
});

must be in pages folder, but i'm use it in components folder

This solved it for me

Ermosparis commented 2 years ago

I'm resolve this issues, this code

export const getStaticProps: GetStaticProps = async ({ locale }) => ({
  props: {
    ...(await serverSideTranslations(locale, ["translation"])),
  },
});

must be in pages folder, but i'm use it in components folder

What to do if I need to use it in components folder?

wsfung2008 commented 2 years ago

What to do if I need to use it in components folder?

you can still use the useTranslation hook in components

Ermosparis commented 2 years ago

What to do if I need to use it in components folder?

you can still use the useTranslation hook in components

Thank you, I figured out 😅

jeremyzilar commented 2 years ago

@dohomi Did you figure out a workaround other than the alias to Webpack config?

I add an alias to point via Webpack config to different alias to solve the error for now.

webpackFinal: async (config, {configType}) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      'next-i18next': 'react-i18next'
    }
    return config
  }
felixmosh commented 2 years ago

The root cause of this issue in storybook is related to this line.

In Webpack 5 there is no default polyfill for fs & path.

You have 2 choices to solve the issue:

  1. disable fs by
    //webpack.config.js
    webpackFinal: async (config, { configType }) => {
    config.resolve.fallback = {
    ...config.resolve.fallback,
    fs: false, // <------
    path: false // <-----
    };
    return config;
    };
  2. if the first option doesn't works, change it to work with browserify-fs
    webpackFinal: async (config, { configType }) => {
    config.resolve.fallback = {
    ...config.resolve.fallback,
    fs: require.resolve("browserify-fs"), // <------
    path: require.resolve("path-browserify"), // <------
    };
    return config;
    };
isaachinman commented 2 years ago

@felixmosh Think we'd be better off relying directly on import at this point?

felixmosh commented 2 years ago

@isaachinman I'm not sure I got the idea, import of what?

The "problem" is that the code is isomorphic, and contains code of server side, import won't change anything (because you want to lazy load the server code on the server only).

isaachinman commented 2 years ago

import of whatever we are currently using fs to achieve. Dynamic imports on the server side should be possible.

brayoh commented 2 years ago

import of whatever we are currently using fs to achieve. Dynamic imports on the server side should be possible.

import of what?

sauldeleon commented 2 years ago

@dohomi Did you figure out a workaround other than the alias to Webpack config?

webpackFinal: async (config, {configType}) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      'next-i18next': 'react-i18next'
    }
    return config
  }

Hi!

when I follow this approach to get this work in storybook then I start getting this errors

ModuleNotFoundError: Module not found: Error: Can't resolve 'next-i18next/serverSideTranslations' in 'myproject/src/pages'

I bet this is because storybook is running in the same folder as the next.js app, and is loading I dont know why a file with that import.

Anybody has faced the same issue?

MonchiLin commented 2 years ago

nextjs 12.2 add Edge SSR(https://nextjs.org/blog/next-12-2#edge-server-rendering-experimental) feature, it can also cause this issue.

Imran-cse commented 2 years ago

I'm using getServerSideProps inside pages but still facing the issue

lemonspb commented 2 years ago

Сan anyone explain how to solve the problem using components outside of pages?

t18n commented 2 years ago

In my case, that was the use of

import { useTranslation } from 'next-i18next';

Replace all the occurences with fixes the problem.

import { useTranslation } from 'react-i18next';

As a side note, importing serverSideTranslations as an util works fine for me

export async function getServerSideProps() {
    return {
        props: {
            ...(await getI18nProps()),
        },
    };
}
RichGriff commented 1 year ago

Hey, I'm trying to get my head around the internationalization stuff but running into this 'Can't resolve fs' error. I'm using Next.js 13 and with the app routing option. Has anyone managed to find a fix/work around for this?

adrai commented 1 year ago

@RichGriff https://locize.com/blog/next-13-app-dir-i18n/

RithPisey commented 1 year ago

Create a i18n.js file in the root of your project with the following content:

const NextI18Next = require('next-i18next').default;

module.exports = new NextI18Next({
  defaultLanguage: 'en',
  otherLanguages: ['fr'], // Add your supported languages here
  localeSubpaths: {},
  localePath: typeof window !== 'undefined' ? 'public/locales' : 'locales',
});
juanccamachob94 commented 1 year ago

I personally identified that dependencies serverSideTranslations and useTranslation cannot be together. It works for me.

./i18n/useTranslation.ts

import { useTranslation as useOfficialTranslation } from 'next-i18next';
import { UseTranslationResponse } from 'react-i18next';

import { namespaces } from '@/i18n/helpers/namespaces';

export function useTranslation (ns: string | string[]) {
  const translation = useOfficialTranslation(namespaces(ns));
  const res: UseTranslationResponse<string> = { ...translation };
  res.t = (key: string | string[], options?: any) => {
    if (typeof key === 'string' && typeof ns === 'string' && key.split(':').length === 1)
      return translation.t(`${ns}:${key}`, options);
    return translation.t(key, options);
  };
  return res;
}

./i18n/serverSideTranslations.ts

import { serverSideTranslations as officialServerSideTranslations }
  from 'next-i18next/serverSideTranslations';

import { namespaces } from '@/i18n/helpers/namespaces';

export async function serverSideTranslations (locale: string | undefined, ns: string | string[]) {
  return await officialServerSideTranslations(locale ?? 'de', namespaces(ns));
}

./i18n/helpers/namespaces.ts

export function namespaces (ns: string | string[]) {
  return ['common', ...(typeof ns === 'string' ? [ns] : ns)];
}

any NextJS page

import { useTranslation } from '@/i18n/useTranslation';
import { serverSideTranslations } from '@/i18n/serverSideTranslations';

With this configuration I managed to bypass the explicit use of common. I will implicitly have the specific translation that I require and common.

mss-souloftware commented 1 year ago

getStaticProps

Hello @ivnmood But I want to use it in the components folder because I'm using the navbar as a component. Here is code I'm using.

import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { UseTranslation } from 'next-i18next';

import Link from 'next/link';
import Language from './Language';

const links = [
    { href: '/', label: 'Home' },
    { href: '/about', label: 'About' }
];

export default function MenuItems() {
    const { t } = UseTranslation('menus');
    return (
        <ul className='flex'>
            {
                links.map((link, index) => (
                    <Link
                        key={index}
                        href={link.href}
                        className=" text-[#414052] hover:text-[#AC6DDE] transition-colors mx-[10px]"
                    >
                        {link.label}
                    </Link>
                ))
            }
            <Language />
            {t('about')}
        </ul>
    )
}

export async function getStaticProps({ locale }) {
    return {
        props: {
            ...(await serverSideTranslations(locale, ['menus']))
        }
    };
}
adrai commented 1 year ago

https://github.com/i18next/next-i18next#translate-in-child-components