Closed Nelrohd closed 4 years ago
A few things to consider - just my initial thoughts:
i18next-express-middleware
gives us? See https://github.com/isaachinman/next-i18next/issues/9#issuecomment-443274069I am open to any/all suggestions on this topic, but in general regard it as a major refactor of the project from the ground up. If we can remove our dependency on Express/i18next-express-middleware
, I think the package would become a lot more versatile.
However, I am not (currently) doing any work whatsoever with serverless NextJs, and have very little input to add here.
I've gotten next-i18next working in a serverless environment for one of our sites we're building right now (no full source available unfortunately), but it's pretty hacky because we're basically stitching together various parts from next-i18next in a more vanilla i18next setup.
Our approach is that we basically ship the JSON files once to a CDN (with an assetPrefix) and once inside the Lambda "executables". We reference them via import
which makes them real Webpack chunks for the CDN and also allows it to pick them up and include them when packaging the serverless file and we're also running express infront of the bundles so the middleware can just be dropped. I can't really recommend this approach, but it can definitely work.
@beheh Sounds like you had some fun. Are there any lessons or approaches you'd like to bring back to next-i18next
? Being able to support serverless is the eventual goal.
Stumbled upon this issue because I'm researching for a new NextJS project that should have i18n and is planned to be deployed to Now 2.0. I've not used next-i18next yet, nor am I a serverless/now expert, but if someone has some smart ideas I'm willing to help with the implementation.
@yoeran I think the right place to start is rewriting/adapting i18next-express-middleware
to use vanilla NodeJs HTTP interfaces instead of relying on Express.
Would also appreciate it if someone has the knowledge to optimize the package for serverless solutions.
[RFC] serverless is still open (https://github.com/zeit/next.js/issues/7208). The last entry mentions that a routing middleware is still missing in the current version of Next. Tracking the progress there is a merge request with the title "Example for API routes with middleware" https://github.com/zeit/next.js/pull/7958/files . I do not know anything about the internal of the express middleware currently utilized by next-i18next, but maybe the example shown in the merge request is enough to start a migration.
@gurkerl83 That example appears to show how one could use middleware on an API route, not a UI route.
My request sounds different from the original title but I assume it's related cause it's also about reusing the module outside of next execution context. Sorry if I'm wrong though.
We have a successfully working Next.js project using your next-i18next
(thanks!).
Now I also want to create a stand-alone node.js script that would reuse the entire infrastructure we already have and do some interpolations (in the static HTML files if you're curious).
For some reason (I have my brain kinda broken after hours of trying) it doesn't work.
Here's what I'm doing:
const nextConfig = require('next/config');
nextConfig.setConfig(require('./next.config'));
const { i18n } = require('./i18n');
And then trying to use i18n.t
directly. I see that it's getting all the proper configs but does not load the data from the JSON files.
And thus an attempt to translate any key returns the key itself.
I'm trying to figure out what else next
is doing behind the scene that makes your module fetch the needed JSON data and that I'm missing.
For ref, ./i18n
is pretty standard:
const NextI18Next = require('next-i18next').default;
const i18nHelper = require('./lib/i18nHelper');
module.exports = new NextI18Next({
localePath: 'static-src/locales',
defaultLanguage: i18nHelper.getDefaultLanguage(),
otherLanguages: i18nHelper.getOtherLanguages(),
fallbackLng: i18nHelper.getDefaultLanguage(),
defaultNS: i18nHelper.getDefaultNamespace(),
fallbackNS: i18nHelper.getAllNamespaces(),
ns: i18nHelper.getAllNamespaces(),
localeSubpaths: 'foreign'
});
And I can confirm that all the i18nHelper
methods are returning what expected.
@emirotin That's not something we're going to support. Unfortunately you'll need to go it alone. I would suggest simply initialising a normal i18next instance. Note that you can pass your identical next-i18next
config object into the i18next.init
function.
Let's keep this discussion on topic.
Sorry for offtopic then. I truly assumed it's about the same root. Just curious what's wrong with my current setup that makes it unusable?
P.S. ~barebones i18next
doesn't work for me (yet) too~ normal i18next
works!
It seems possible to spoof this with the following in a custom _app.js
import {handle} from 'i18next-express-middleware';
import {i18n, config} from '../i18n';
export default extends App {
// ...
static async getInitialProps(context) {
if (context.ctx.req && context.ctx.res) {
res.set = res.setHeader;
req.path = context.ctx.asPath;
const i18nHandler = handle(i18n, {ignoreRoutes: config.ignoreRoutes});
await new Promise((resolve, reject) => {
try {
i18nHandler(req as any, res as any, resolve);
} catch (e) {
reject(e);
}
});
}
}
// ...
}
Hopefully ZEIT follows through with adding middleware capabilities, and we can easily add this that way
If this is something you are cool with (and actually works? I'm still testing this), I can throw up a PR to introduce a helper function (plus some documentation)
@aequasi Not sure that'd work. The req
and res
objects that come off Next's context are vanilla HTTP interfaces, not Express req
and res
interfaces. What have you seen in your testing?
Because there are so many diverse use cases for this package, I'd rather we proceed in the least hacky way possible. We do have support from the Next team and they're aware of the request for serverless middleware.
The two express specific things (set
and path
) can easily be "mocked"
set
actually needs to be:
req.set = (key, value) => {
if (value !== undefined) {
res.setHeader(key, value);
}
}
From my testing, this works. I'm not having any issues with server-rendered content
To reiterate, I'd rather we proceed in the least hacky way possible. This means we need to:
i18next-node-middleware
next-i18next
accordinglyIf people want to roll their own serverless solutions in the meantime, feel free.
@aequasi nice temporary solution 🤔 But I guess locale subpaths is not working, right?
UPD: yes, server-side locale subpaths is not working (naturally), but it seems fine on the client. I got client-side translations working so far without particularly critical bugs, but it can't work on Zeit Now serverless 🤔
Monkey patching all incoming requests in _app.jsx is not a best way to get things working, so as @isaachinman mentioned - we need to wait for serverless middlware support in Next.js 😞
Zeit compiles everything that it can find from static analysis into a single file. If i had to make an educated guess, the locales are not making it to the deployment, causing the fs.readdirSync to fail. You could look into: https://zeit.co/docs/v2/advanced/builders/overview#including-additional-files
@aequasi I experienced the same error when a deployment with now get executed. With your last comment, do you mean specifying i18n-resource files within the includeFiles section of now deployment configuration (now.json) will resolve the fs.readdirSync error?
@gurkerl83 nope, just tested it with both static
and custom folder with custom localePath
. Tried to use include
in now.json, but no luck.
Regardless of whether or not you want to get this done in a hacky way, doing this research ahead of time makes refactoring take less time, as you know what will have to change.
includeFiles
added the locales to the deployed code, but you need to go a step further.
On now deployments, process.cwd()
isnt the right directory to pull from, and the now team has said to use __dirname
instead.
Setting localePath to path.join(__dirname, '/static/locales')
(or whatever your path is), seems to fix that initial fs.readdirSync issue. This could also be mitigated by forcing the developer to define all namespaces by hand, instead of grabbing it automatically.
After i got past that error, i got stuck on another one for a bit that was a little harder to debug, due to the evals in the repo
Unable to import module 'now__launcher': Error
at Function.Module._resolveFilename (module.js:547:15)
at Function.Module._load (module.js:474:25)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at eval (eval at _default (/var/task/page.js:52306:32), <anonymous>:1:1)
at _default (/var/task/page.js:52306:32)
at new NextI18Next (/var/task/page.js:201598:51)
at Object.<anonymous> (/var/task/page.js:56634:27)
at Object.KbPy (/var/task/page.js:56793:30)
at __webpack_require__ (/var/task/page.js:23:31)
The eval
thats at the line above is:
var _default = function _default(config) {
if (!_i18next["default"].isInitialized) {
if (_detectNode["default"]) {
+ var i18nextNodeBackend = eval("require('i18next-node-fs-backend')");
var i18nextMiddleware = eval("require('i18next-express-middleware')");
_i18next["default"].use(i18nextNodeBackend);
if (config.serverLanguageDetection) {
var serverDetectors = new i18nextMiddleware.LanguageDetector();
config.customDetectors.forEach(function (detector) {
return serverDetectors.addDetector(detector);
});
_i18next["default"].use(serverDetectors);
}
// ...
Something about now doesn't like the eval("require('i18next-node-fs-backend')");
.
@aequasi Do you have a fork of next-18next to share respectively an example which demonstrates/summarises the changes you described.
About your last comment, maybe deploying next in serverless mode does not understand the result of eval statements in combination with loading resources through require. Are those statements replaceable through dynamic import statements?
It will be awesome if this library is useable in a serverless deployment mode without the requirement to have a middleware provided by next in the short term. Regular observations of both middlewares (next serverless - for several months in the RFC state) and express i18next-express-middleware (migration to support pure Http instead of express) have currently zero attention.
I dont. Was able to get there with this:
import * as common from '@static/locales/en/common.json'; // Wherever you have your common locale
export default new NextI18Next({
browserLanguageDetection: false,
serverLanguageDetection: false,
partialBundledLanguages: false,
defaultLanguage: 'en',
ns: ['common'],
defaultNS: 'common',
otherLanguages: ['en'],
resources: {
en: {common},
},
localeSubpaths: 'none',
localePath: path.join(__dirname, 'static', 'locales'), // Wherever you have your common locale
});
For anyone wanting to help: @jamuhl has let us know in https://github.com/i18next/i18next-express-middleware/issues/186 that it should be fairly easy to refactor out the dependency on Express.
This would be a great first step, and will benefit the greater i18next community, not just next-i18next
.
I've taken a look myself and submitted a PR to remove Express as a dependency from i18next-express-middleware
here: https://github.com/i18next/i18next-express-middleware/pull/191.
Reviews, manual QA, and feedback would be much appreciated.
It's kind of strange to retain "middleware" in a vanilla NodeJs environment, but I do think it's the best way forward.
Once this is ready to merge, I think we'll need to deploy an entirely new repo called i18next-middleware
(or similar).
From there, the next piece of work will be refactoring the Express dependency out of next-i18next
itself. At that point, serverless as well as use with Koa/Hapi should be possible with a little bit of added work from users.
Am struggling with this issue and wondering when this will get fixed. What's the workaround ?
@rdewolff The problem, approach, and work necessary are all quite well-documented in this issue and the related ones in i18next repos. You can feel free to contribute!
Hi, just a short update on that issue. The work necessary to fix that issue is
Further experiments have to be conducted to identify pathways around some limitations currently introduced. Some topics have to be discussed, such as the express session handling, integrated into the i18next-express-middleware library we have to loose in a vanilla Http based setup.
What is next? Some investigation in open issues, code cleanup. I will push the changes I have made to i18next-express-middleware and next-18next to different repositories. The naming of repositories was changed because of publishings to Npm required for testing. As soon those things are up, I will prepare those merge requests.
With those adjustments I was able to deploy next-18next example with now V2 in serverless mode (Production build). Some prove...
https://millipede-docs-simple-29i7s3up8.now.sh/
Thx,
@rdewolff The problem, approach, and work necessary are all quite well-documented in this issue and the related ones in i18next repos. You can feel free to contribute!
I have tried the solutions provided here and I could not make it work. Tried applying the the i18n config, localeSubpaths
seems complaining for not having an object but a string. When commenting that out, I get one step further, but the following error occurs :
/[...]/web/node_modules/next-i18next/dist/commonjs/config/create-config.js:54
throw new Error("Default namespace not found at ".concat(defaultNSPath));
^
Error: Default namespace not found at /[...]/dev/static/locales/en/common.json
at _default (/[...]/node_modules/next-i18next/dist/commonjs/config/create-config.js:54:15)
at new NextI18Next (/[...]/node_modules/next-i18next/dist/commonjs/index.js:52:46)
at Object.<anonymous> (/[...]/i18n.ts:5:14)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Module.m._compile (/[...]/node_modules/ts-node/src/index.ts:473:23)
at Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Object.require.extensions.(anonymous function) [as .ts] ([...]/node_modules/ts-node/src/index.ts:476:12)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
Is there a fallback to run it without serverless? Am searching the next.js doc but cannot find the info. Even when commenting out target: "serverless"
in next-config.js
I get the same results.
Tried all the combination I could think of but 👎 - not working. Anyone could provide the source code / sandbox of the working version ? That'd be of great help! Thanks in advance!
Is there any update, another issue to follow status, or package alternative? Thank you!
@michaviehauser Still need to get the middleware bit done first. There is no alternative whatsoever, to my knowledge. Feel free to contribute.
👀 [RFC] Custom Routes https://github.com/zeit/next.js/issues/9081
Hi, just a short update on that issue. The work necessary to fix that issue is
- Port the dependency i18next-express-middleware from express to vanilla Http (Node). I have a local branch on which I have worked on for some time.
- Small portions of next-18next have to be adjusted, including the file next-i18next-middleware.ts and some others. We can keep the example next application, which is built on top of express or provide a new one. On a local branch of next-18next, those adjustments, including the example, are made. The example supports NextJs in serverless deployment mode, especially for production build with now V2.
- Middleware support from Next. I have adjusted the example of next-18next by including a _document file which accesses the document middleware of NextJs which they already provide. The next-18next middleware gets consumed there.
Further experiments have to be conducted to identify pathways around some limitations currently introduced. Some topics have to be discussed, such as the express session handling, integrated into the i18next-express-middleware library we have to loose in a vanilla Http based setup.
What is next? Some investigation in open issues, code cleanup. I will push the changes I have made to i18next-express-middleware and next-18next to different repositories. The naming of repositories was changed because of publishings to Npm required for testing. As soon those things are up, I will prepare those merge requests.
With those adjustments I was able to deploy next-18next example with now V2 in serverless mode (Production build). Some prove...
https://millipede-docs-simple-29i7s3up8.now.sh/
Thx,
@gurkerl83 Could you please share how you got it running on Now2.0? Sample repo link to your site would be great
👀 [RFC] Custom Routes zeit/next.js#9081
is this middlewares,but with another name, or will it not be possible with this RFC ?
I made Next.js (serverless mode v9.1.2) work with Locize and documented it there: https://stackoverflow.com/questions/55994799/how-to-integrate-next-i18next-nextjs-locize/58782594#58782594
Hey @Vadorequest, nice!
However, lots of people use next-i18next
with translations that are loaded over network from another CDN/API, and you could also bundle your translations into your serverless deployment, etc.
It's certainly possible with next-i18next
, we just need someone to take the lead!
Also, I think it would be great if you wrote about your experience in an article on Medium.
+1 for the article on Medium. Thought the same while reading your post on SO.
Hear you, we've got a tech blog on medium at https://medium.com/unly-org/tech/home where you may find some interesting stuff already. But I've got very little time for a medium article right now.
I'm in the process of releasing a Next+Now boilerplate with Locize (and tons of other stuff) production-grade for very quick start, it's not yet ready, been working on it for 2w+. I'll drop the link here when it's released, if you're interested. :)
@gurkerl83 seems to have a working solution. Is there an ETA for the release of his solution and how can others help out to speed up the process?
Also wondering if there is already work being done towards creating a next.js plugin of this to have a clean support for serverless?
@BjoernRave It's been a week or two since I checked on the plugins RFC but as far as I know, it's nowhere near ready for third party dev.
Did you have something in mind?
Indeed, a plugin is probably the future of this package, but that doesn't solve the i18next-express-middleware
dependency issue.
@isaachinman ah, so at first we need to port i18next-express-middleware
to a version which is not depending on express to have a next.js plugin?
Yea, true I guess it's not yet ready for third-party devs, I just can't wait anymore, so I thought maybe someone is already playin around with what the next.js team is providing at this point. There is already a first version of it under an experimental flag
Yes, basically we either need:
req
, res
, and a few other thingsThat being said, I know Tim did prioritise maintaining at least a little bit of parity with commonly-used Express features, so it might end up being pretty much drop-in.
@BjoernRave If you'd like to work on this together, please feel free to email directly.
okay, yea not sure I am much of a help here.
@Vadorequest but that means we need to use locize, right? why is it working with locize, but not without, since your are still using i18next?
@gurkerl83 I would also be very interested if you got something, please share it with us :)
@BjoernRave It's working with Locize and not without because of the backend implementation. I won't go too far into the technicals, but let's just say the Locize implementation was easier to work with. (yet, wasn't THAT easy either, hence the SO post)
But, definitely usable with any tool, the main "difficulty" at hand is to build a universal backend, because Next needs universal compatibility. (node + browser)
I would assume that the most desirable way to consume localisation data in serverless deployments is via a third-party CDN, where caching occurs both at the CDN and server levels.
@isaachinman @Vadorequest there is a big "drawback" with serverless -> the serverless function does not cache the loaded translations like a hosted express server can -> this means every time the serverless function is called to render a page all the namespaces get loaded from the CDN:
that's why we got that "FAT" warning here: https://github.com/locize/i18next-node-locize-backend#important-advice-for-serverless-environments---aws-lambda-google-cloud-functions-azure-functions-etc
Is your feature request related to a problem? Please describe.
Currently next-i18next does not support serverless. It appears that the i18n-middleware create some troubles when used on lambda #271
Describe the solution you'd like
Not sure exactly what would be the final solution but:
Describe alternatives you've considered
None.
Additional context
Open to suggestion for the work/final solution to be done/achieved.