Closed hectorstudio closed 3 years ago
Hi @hectorstudio – unfortunately I'm unable to determine what is going wrong based on the snippets you've copy/pasted. Can you please provide a full repo?
Also just a heads up, regional locales use dashes, not underscores. A correct locale would be en-US
.
@hectorstudio On second thought, you're probably just not using serverSideTranslations
.
I used serverSideTranslation
on page-level component.
`export const getStaticProps = async ({ locale }) => ({
props: {
...await serverSideTranslations(locale, ['common']),
},
})
export default Homepage;`
Can you provide a reproducible repo?
I also have this issue. I think this is because the AWS serverless deployment plugin (https://github.com/serverless-nextjs/serverless-next.js
) is not copying in the configuration files to final build packages that get uploaded to lambda. I have tried copying the next-i18next.config.js
and next.config.js
into the .serverless_nextjs/default-lambda
folder post-build but that did not work.
Where does the next-i18next plugin expect the config files to live?
This is the additional copying script I added:
serverless.yml
# https://github.com/serverless-nextjs/serverless-next.js#readme
omnea:
component: '@sls-next/serverless-component@1.19.0-alpha.32'
inputs:
bucketName: omnea
build:
postBuildCommands: ['node post-build.js']
post-build.js:
const fs = require('fs-extra')
console.log('-> Copying locales directory...')
const localeSrc = './public/locales'
const localeDest = './.serverless_nextjs/default-lambda/public/locales'
fs.copySync(localeSrc, localeDest, { recursive: true })
const localeSrc1 = './next-i18next.config.js'
const localeDest1 = './.serverless_nextjs/default-lambda/next-i18next.config.js'
fs.copySync(localeSrc1, localeDest1)
const localeSrc2 = './next.config.js'
const localeDest2 = './.serverless_nextjs/default-lambda/next.config.js'
fs.copySync(localeSrc2, localeDest2)
console.log('Locale directory was copied successfully')
Where does the next-i18next plugin expect the config files to live?
In the root dir, as per the documentation.
Let me know if I can assist in any way. Various serverless platforms do tend to do strange things with the fs.
Have same issue, but on my local build (all config files in the root dir)
Props passed to appWithTranslation:
{ _nextI18Next: { initialI18nStore: { ru: [Object], en: [Object] }, initialLocale: 'ru', userConfig: null } }
UPD: next-i18next.config should be always separate file
I had a chance to look into this a bit further and it seems to be a problem within the generated webpack code as opposed to the file system on the server (well that is also a problem but the code I linked above does resolve that). When the file resolution is called with __webpack_require__
as opposed to require
the generated code fails.
I was able to get this to happen locally as well once I'd generated the webpack code for myself.
@isaachinman Are you aware of needing to add the file next-i18next.config.js
to any webpack config such that it is included in the compiled code? I'm not greatly familiar with the configuration setup of webpack.
@BenAllenUK Not entirely sure what you're asking, do you mind elaborating? The difference between __webpack_require__
and require
should not matter – the config file needs to exist on the filesystem.
Sure.
This is the error I get on the server (after deploying via serverless next js tool to AWS lambda).
2021-03-02T13:38:14.118Z d38da14f-297f-4dbc-a2cc-98ab7c8ba2e2 ERROR Unhandled error during request: Error: Cannot find module '/var/task/next-i18next.config.js'
at webpackEmptyContext (/var/task/pages/[projectSlug]/editor/[articleSlug].js:79610:10)
at /var/task/pages/[projectSlug]/editor/[articleSlug].js:1348:90
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
code: 'MODULE_NOT_FOUND'
}
Root of project lambda is at /var/task
.
I have next.config.js
at root and next-i18next.config.js
at root. Here's the generated file structure visible from lambdas function code editor:
To confirm I have the following code in pages/[projectSlug]/editor/[articleSlug].tsx
:
export async function getServerSideProps({ params, locale }: GetServerSidePropsContext) {
return {
props: {
...(await serverSideTranslations(locale, ['common', 'editor'])),
},
}
Contents of `next-i18next.config.js:
const path = require('path')
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
localePath: path.resolve('./public/locales'),
}
So this works in development but once I use the production bundle it fails.
I narrowed the error down to the serverSideTranslations
function within my bundled /var/task/pages/[projectSlug]/editor/[articleSlug].js
file. Within this function it throws on line 1374.
I walked through this and Promise.resolve("".concat(_path["default"].resolve(DEFAULT_CONFIG_PATH)))
correctly resolves to the file path of /var/task/next-i18next.config.js
. The error is next thrown when __webpack_require__("cgfX")(s)
is called. This is a reference to the webpackEmptyContext
that we see in the initial stack trace
My earlier comment was to say that if I replace with __webpack_require__("cgfX")(s)
with require(s)
, this loads the file correctly. Obviously this is not a solution, just something I've found that might help me find the solution.
Any advice / things to test here would be greatly apprecaited.
Interesting, thanks for a very detailed account.
I think what you're suggesting would involve changing this import to a regular require
. Do you want to give that a test, and see if it solves the issue?
Good suggestion. Unfortunately, this didn't work. At the point in which the node modules get packaged up into one JS file the require
also gets replaced with __webpack_require__
so the compiled code is the same.
TIL that it is crucial that the config file looks exactly like the one described in the README. I was getting this exact error scratching my head as to why it's missing, then I realized my config file looks like this:
// next-i18next.config.js
module.exports = {
locales: ['en', 'hu'],
defaultLocale: 'en',
};
// next.config.js
const i18n = require('./next-i18next.config.js');
module.exports = { i18n };
instead of this:
// next-i18next.config.js
module.exports = {
i18n: {
locales: ['en', 'hu'],
defaultLocale: 'en',
},
};
// next.config.js
const { i18n } = require('./next-i18next.config.js');
module.exports = { i18n };
I hadn't realized that was a load-bearing i18n
key
Sounds like another way to get the same error. I confirmed that my next-i18next.config.js
is correct and the i18n
key import also works in my setup.
const { i18n } = require('./next-i18next.config')
const path = require('path')
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
localePath: path.resolve('./public/locales'),
}
@BenAllenUK struggling with similar issue while trying to do the same - build and deploy next-i18next with serverless component. I probably am a step behind you. I think that my issue is that it does not let me build app without next-i18next.config.js
. Thus I can't even get to run any postBuildCommands
. I run locally serverless build --component myNextApplication
and it just gloriously fails. Error points to a file that actually exists.
Theres a couple of issues at serverless component github, but they all seem to have dealt with different localization packages that did not require file before build?
@BenAllenUK I dug just as far as you did now. There is indeed something wrong with that __webpack_require__
you pointed out. Not sure what, sorry :(
I have a temporary solution and its sorta ugly - to not to use default configuration file. This package apparently allows to override config within app. So, rename your i18n config file to something else and explicitly use it everywhere. Use this file in next.config.js
, override default in appWithTranslation
. And make a helper for serverSideTranslations
that uses the new config. And it works!
Hey @Reiiya - thanks for confirming. I'm glad its not just me. I'm still stuck on this unfortunately as well.
Thanks, that workaround makes sense, I may look at doing that but will hold out atm to see if anyone else can pick up where I left off.
Closing now, as this seems to simply be a shortcoming of various serverless platforms. Explicitly passing the config into the two functions should work, though.
@BenAllenUK If you do find a solution, please do let us know.
Why was this one closed? The error still occurs.
Why was this one closed? The error still occurs.
Because it was deemed as issue happening on Serverless side (but honestly I'm not exactly sure where it happens). I have not seen anyone actively fixing it there either. From what I've seen, everyone just uses the workaround mentioned. Unfortunately I am among those who just rely on workaround for now and cant jump in the fun :(
I got the same error and for me the problem was that I use the serializeConfig: false,
option. Once I added the configuration manually to appWithTranslation like this appWithTranslation(MyApp, nextI18NextConfig)
it worked. I realize it is not the same config as the reporter's one, but it may help someone.
@Reiiya Could you please provide an example for the workaround that you are applying please, I'm facing the exact same issue. Thank you.
I had the same error and for me was the case was that I was not sending the correct props down to the pages.
I was sending in an object containing the key _nextI18Next
:
// this is wrong
{
_nextI18Next: {
initialI18nStore: [Object],
initialLocale: 'en',
userConfig: [Object]
}
instead of the correct object:
// this is expected
{
initialI18nStore: [Object],
initialLocale: 'en',
userConfig: [Object]
}
I was not spreading the ...serverSideTranslations
and got confused that was the expected props.
Hopefully, this will help someone.
Adding config to serverSideTranslations fixed it for me.
...(await serverSideTranslations(locale, ['common'], nextI18NextConfig)),
Adding config to serverSideTranslations fixed it for me.
...(await serverSideTranslations(locale, ['common'], nextI18NextConfig)),
this reference solved my problem @crathert thx^^
Nothing here worked for me. Seriously!
@BenAllenUK had the right approach. I ended up tracing through the same issue because I didn't see the notes until after I was done :)
I have a hand-crufted fix in hand, deployed in a real app, details in next section. I do not know how to make this as a patch that we can ship in the library that will work in both non-bundled and bundled cases with the module at the root of a monorepo or in a child package directory. I'm willing to do a Google Meet or other brainstorming / pairing session with anyone that has an idea to try out.
and I believe we can fix this by changing to (turns out the await import
to prevent Webpack from pointing the config file import to the empty dummy using the technique for how next.config.js
is avoiding this problemnext.config.js
approach only works because next
is external / not bundled).
I am NOT using target: serverless
(using this with Next.js 13.1.1) but that does not matter. I have a replacement for target: serverless
that I'll be sharing shortly that is even better but does the same bundling of node_modules
to prevent enormous deployments and slow starts on Lambda. The solution below will apply go target: serverless
though as the problem is just Webpack.
node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
return Promise.resolve("".concat(_path["default"].resolve(DEFAULT_CONFIG_PATH))).then(function (s) {
next.config.js
from below - This will cause the contents of the file to be bundled (so editing the next-i18next.config.js
file at runtime will have no effect)next.config.js
Blockconst path = require('path');
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
webpack: (config, options) => {
const { dev, isServer } = options;
if (isServer && config.name === 'server' && !dev) {
const contextReplacePlugin = new options.webpack.ContextReplacementPlugin(/next-i18next/, './', {
// Left side (./next-i18next.config.js) is the path that require is looking for at build time
// Right side (path.resolve(./next-i18next.config.js)) is the path to the file that will be
// bundled into the server bundle and used at runtime
'./next-i18next.config.js': path.resolve('./next-i18next.config.js'),
});
// Put at front of plugins array
config.plugins.unshift(contextReplacePlugin);
}
return config;
},
};
node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
Block// Make this require no longer an expression so the path stays the same
// for Webpack - This allows us to fix the path with ContextReplacementPlugin
// When the path is wrapped with `path.resolve` it makes it compute one value
// at build time and then a different value at runtime so the `map` that is built
// to find the name at runtime has an entry that does not match the item being
// looked for. It's possible to hand-fix this map by placing the fully qualified
// path to the file into the map and pointing to the same module number to prove
// that the issue is the difference in paths between build time and runtime.
return _interopRequireWildcard(require(DEFAULT_CONFIG_PATH));
node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
requires the config file via a expression (path.resolve
primarily), resolved as a promise, which then evaluates another expression (path.resolve
again)require
to the empty module and fails at runtime
full dynamic
in webpack and were changed to point to the empty context: https://webpack.js.org/migrate/3/#full-dynamic-requires-now-fail-by-default.next/server/chunks
, making it very frustrating / misleading to try to fix this../**/next-i18next.config
in application code work fine because they are not an expression and webpack can find them at build timerequire
it will not be able to follow any directives in config.externals
such as { 'next-i18next.config': './next-i18next.config.js' }
- The problem is not that the file is not externalled but rather that Webpack has no idea what this require is so it just points it to emptyThe path.resolve
call from DEFAULT_CONFIG_PATH
has to be removed as a temporary workaround.
Now... why does this work for the next.js
import of next.config.js
? It appears that using Correction: this works for await import(...)
is either a workaround or a legit way to say that we want this imported at runtime, in any case it appears that it stops Webpack from messing this up while still allowing discovery of the path at runtime.next
because next
is not bundled so the import expression is evaluated only at runtime.
patch-package
postinstall
script that runs patch-package
"postinstall": "patch-package --patch-dir ./patches/"
patches/next-i18next+13.0.3.patch
npm i
or yarn install
again to run patch-package
npx patch-package next-i18next
to regenerate the patch - if that fails, apply the patch by hand-editing the file (make sure you get the commonjs
version), then run npx patch-package next-i18next
to regenerate the patchnext-i18next
is installed in the node_modules
directory that is in the same directory as next-i18next.config.js
- If these are different (e.g. next-i18next is a child of another module) then you'll have to edit the number of ../
in the pathrm -rf .next/cache
or rm -rf .next
next-i18next.config.js
if deployingpatches/next-i18next+13.0.3.patch
Patch File Contentsdiff --git a/node_modules/next-i18next/dist/commonjs/serverSideTranslations.js b/node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
index 67dc9af..fc5b765 100644
--- a/node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
+++ b/node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
@@ -79,9 +79,7 @@ var serverSideTranslations = /*#__PURE__*/function () {
break;
}
_context.next = 9;
- return Promise.resolve("".concat(_path["default"].resolve(DEFAULT_CONFIG_PATH))).then(function (s) {
- return _interopRequireWildcard(require(s));
- });
+ return require('../../../../next-i18next.config.js');
case 9:
userConfig = _context.sent;
case 10:
next-i18next
that Breaks Webpacknext.config.js
at RuntimeThis works for next
when next
is not bundled. But this fails just the same if used in next-i18next
as it still uses an expression for the import.
Locally patching node_modules/next-i18next/dist/commonjs/serverSideTranslations.js
and re-running next build
will show no change, driving you mad.
The problem is that the node_modules
are only compiled once and cached in .next/cache
.
If you make a change to a file in node_modules
you have to rm -rf .next/cache
before you build again, then you will see that the resulting chunk file has your changes.
next-i18next
Require of next-i18next.config.js
is an Expression return Promise.resolve("".concat(_path["default"].resolve(DEFAULT_CONFIG_PATH))).then(function (s) {
return require("".concat(_path["default"].resolve(DEFAULT_CONFIG_PATH));
});
I added the example code for the hand-crufted fix to the comment above. I'm not sure about how to make the permanent / shippable fix here, but hopefully others can validate that the solution works for them and/or have some ideas on how to make this shippable.
It actually may be as simple as marking the file external when bundling the server, which kinda stinks because every consumer would need this fix (or we could put the fix into next.js
as they have many similar fixes in handleExternals
already):
const path = require('path');
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
webpack: (config, options) => {
const { dev, isServer } = options;
if (isServer && config.name === 'server' && !dev) {
// Make some packages external
config.externals.push({
path.resolve('./next-i18next.config.js'): `commonjs ${path.resolve('./next-i18next.config.js')}`,
});
}
return config;
},
};
I have not tried the externals approach yet; it may have the same issue where the path changes at runtime and causes a mismatch.
I got the same error and for me the problem was that I use the
serializeConfig: false,
option. Once I added the configuration manually to appWithTranslation like thisappWithTranslation(MyApp, nextI18NextConfig)
it worked. I realize it is not the same config as the reporter's one, but it may help someone.
yep, this way helped me 🔥
Describe the bug
Error: appWithTranslation was called without a next-i18next config
Occurs in next-i18next version
I tried the new version - 8.0.1
Steps to reproduce
next-i18next.config.js
module.exports = { i18n: { defaultLocale: 'en_US', locales: ['en_US', 'de_DE', 'fr_CH'], }, }
next.config.js const { i18n } = require('./next-18next.config')
module.exports = { i18n, }`
const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />
export default appWithTranslation(MyApp)
Screenshots
OS (please complete the following information)
Additional context
Add any other context about the problem here.