next-i18next icon indicating copy to clipboard operation
next-i18next copied to clipboard

Add support for ES module syntax in `next-i18next.config.js`

Open trezy opened this issue 3 years ago • 11 comments

Is your feature request related to a problem? Please describe.

Next.js 12 supports ES module syntax for the next.config file if the file extension is .mjs. Creating next-i18next.config.mjs doesn't work, however, since next-i18next looks specifically for a next-i18next.config.js file.

Describe the solution you'd like

next-i18next should support ES module syntax for the next-i18next.config.js file.

Describe alternatives you've considered

I can achieve this by manually importing a next-i18next.config.js file and passing it to serverSideTranslations, but this is burdensome to do for apps with lots of pages.

Additional context

It was very surprising to me that next-i18next requires the existence of a next-i18next.config.js file. It'd be cool if in addition to adding ES modules support, next-i18next would grab its settings from the next.config.js file in the absence of a next-i18next.config.js file.

trezy avatar Nov 08 '21 18:11 trezy

We can release this in the v9.0.0 major. I'd certainly appreciate help if you have time to look into it.

It was very surprising to me that next-i18next requires the existence of a next-i18next.config.js file

The next-i18next package requires additional configuration on top of the NextJs i18n config object. It's not easy/advisable to try and "share" the next.config.js file as it is handled in a very specific way by the NextJs framework and Vercel platform. That is why we need a separate file.

If we standardise/force the localePath option (effectively removing it) as per https://github.com/isaachinman/next-i18next/issues/1202#issuecomment-947779838, we may also be able to drop the requirement of a config file, however we'd need to test filesystem issues on Vercel and other serverless platforms.

isaachinman avatar Nov 08 '21 19:11 isaachinman

Is there an update to this issue? The last time i tried to use "next-i18next.config.mjs" in combination with "next.config.mjs" i got some problems.

LaCocoRoco avatar May 30 '22 10:05 LaCocoRoco

No update. @LaCocoRoco Would you like to look into it and contribute?

isaachinman avatar May 30 '22 11:05 isaachinman

This is probably way out of my league. If i got some free time i will take a look into it and if i have a solution i will give a response regarding the problem.

LaCocoRoco avatar May 30 '22 20:05 LaCocoRoco

Supporting the cjs syntax would be a nice stopgap next-i18next.config.cjs

jlarmstrongiv avatar Jun 01 '22 01:06 jlarmstrongiv

@jlarmstrongiv PRs welcome.

isaachinman avatar Jun 01 '22 08:06 isaachinman

@isaachinman The issue at hand is the usage of cjs imports within createConfig() which gets passed as is into esm bundle and thus resulting into invalid ESM module code. No idea how to fix it, especially since with require() rewritten as top level imports it builds just fine with yarn build and the test for it works too when ran standalone as yarn jest ./src/config/createConfig.test.ts. However when running the package with the changes above I get this error:

error - ../next-i18next/dist/esm/config/createConfig.js:19:0
Module not found: Can't resolve 'fs'
  17 | 
  18 | import { defaultConfig } from './defaultConfig';
> 19 | import fs from 'fs';
  20 | import path from 'path';
  21 | var deepMergeObjects = ['backend', 'detection'];
  22 | export var createConfig = function createConfig(userConfig) {

Import trace for requested module:
../next-i18next/dist/esm/appWithTranslation.js
../next-i18next/dist/esm/index.js
./src/pages/_app.tsx

https://nextjs.org/docs/messages/module-not-found

appWithTranslation() in the logs suggests the bundler is confused about the environment this function is ran at and doesn't code-split it properly.

GabenGar avatar Jun 17 '22 10:06 GabenGar

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 10 '22 05:07 stale[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 30 '22 17:07 stale[bot]

I hate bots

tkrotoff avatar Jul 30 '22 17:07 tkrotoff

Hey everyone.

Before opening a PR to support cjs, I want to double check if this is the only part needed to be added here or?

const DEFAULT_CONFIG_PATH = './next-i18next.config.js'
const DEFAULT_CONFIG_PATH_CJS = './next-i18next.config.cjs'

[...]

  if (!userConfig) {
    if (fs.existsSync(path.resolve(DEFAULT_CONFIG_PATH))) {
      userConfig = await import(path.resolve(DEFAULT_CONFIG_PATH))
    } else if (fs.existsSync(path.resolve(DEFAULT_CONFIG_PATH_CJS))) {
      userConfig = await import(path.resolve(DEFAULT_CONFIG_PATH_CJS))
    }
  }
  
  [...]

I added it locally but I don't understand how the testing works?

AhmedBHameed avatar Aug 10 '22 12:08 AhmedBHameed

What about mjs? @AhmedBHameed

otterDeveloper avatar Aug 15 '22 23:08 otterDeveloper

Well I made an attempt at https://github.com/otterDeveloper/next-i18next/tree/esm-config-support-1

Importing cjs works but I didn't have any luck with mjs.

It either cant find the mjs file: Error: Cannot find module next-i18next.config.mjs

or because the package has support for CommonJS, in another test project, it throws an error about importing an ESM module using require Error: require() of ES Module next-i18next.config.mjs not supported.

Even Jest doesn't like importing mjs

So I think this is a no go right now

A mitigation would be to make it clear in the Readme that the config must be named exactly next-i18next.config.js and add an example in on how to import from a mjs next config, next.config.mjs

otterDeveloper avatar Aug 16 '22 07:08 otterDeveloper

@otterDeveloper have you tried the await import("/path/to/next-i18next.config.mjs") syntax when the package has support for CommonJS? (may not work in jest)

jlarmstrongiv avatar Aug 16 '22 08:08 jlarmstrongiv

Maybe I should open another issue for this but just to add to the conversation: Since Next.js now supports having the config as a function (even an async one since v12.1.0) it would be very nice to have the same for next-i18next.config.js.

For a use case example, as I tried to have only one source of truth for the list of supported locales, I used to do something like this in my next.config.js (with support for prefixing the default locale):

// @ts-check

const fs = require('fs/promises');

module.exports = async (/* phase, { defaultConfig } */) => {
  const locales = await fs.readdir('./public/locales');
  const defaultLocale = 'default';

  /**
   * @type {import('next').NextConfig}
   */
  const nextConfig = {
    i18n: {
      locales: [defaultLocale, ...locales],
      defaultLocale,
      localeDetection: false,
    },
  };
}

Elindorath avatar Sep 01 '22 16:09 Elindorath

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 09 '22 00:09 stale[bot]

not stale

techquestions avatar Sep 29 '22 20:09 techquestions

this issue should be open

Sevi7 avatar Jan 20 '23 17:01 Sevi7

Any workaround?

mattp0123 avatar Mar 06 '23 10:03 mattp0123

This is still an issue that is not addressed. There are too many benefits to using mjs to not have a work around for this.

Simvolick avatar Apr 02 '23 09:04 Simvolick

Almost 2 years still nothing! yikes

mustafamoe avatar Apr 04 '23 10:04 mustafamoe

feel free to provide a PR if you like it

adrai avatar Apr 04 '23 11:04 adrai

不使用mjs也不使用cjs后缀就直接写成js即可 // next-i18next.config.js

// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
const languagesConfig = [
    {
        label: 'English',
        value: 'EN',
    },
    {
        label: '中文',
        value: 'ZH_CN',
    },
];

const i18n = {
    defaultLocale: 'ZH_CN',
    locales: languagesConfig.map((language) => language.value),
    localePath: path.resolve('./public/locales'),
};

module.exports = {
    i18n,
    languagesConfig,
};

import withBundleAnalyzer from "@next/bundle-analyzer"
import withPlugins from "next-compose-plugins"
import { env } from "./env.mjs"
import { i18n  } from "./next-i18next.config.js";
/**
 * @type {import('next').NextConfig}
 */
const config = withPlugins([[withBundleAnalyzer({ enabled: env.ANALYZE })]], {
  reactStrictMode: true,
  experimental: { instrumentationHook: true },
  rewrites() {
    return [
      { source: "/healthz", destination: "/api/health" },
      { source: "/api/healthz", destination: "/api/health" },
      { source: "/health", destination: "/api/health" },
      { source: "/ping", destination: "/api/health" },
    ]
  },
  head: {
    link: [
      {
        rel: 'icon',
        href: '/favicon.ico',
      },
    ],
  },
  i18n
})

export default config

我这样写可以使用

Quietly-20201113 avatar Jun 28 '23 07:06 Quietly-20201113

Why isn't this reopened?

nick4fake avatar Jul 03 '23 11:07 nick4fake

reopen

perezpefaur avatar Apr 13 '24 19:04 perezpefaur