Closed isaachinman closed 5 years ago
@jamuhl Funny, as we were discussing privately - there is a with-react-i18next
example in the NextJs repo. I see you contributed there as well. Is this supposed to be identical/synced with our example here, or different in some way? Should both examples exist simultaneously, or should we deprecate one in favour of the other?
@isaachinman the one on next.js is a lot older (v7) and i planned to update it as soon we got this one here stable (in the past we had just a readme here pointing to the sample on next.js) - but i decided to fix issues here - as i don't follow all the issues on next.js and think issues / questions related to it just pollute the next.js repo.
In the future i would prefer having a link from next.js to the sample here as it would make it easier to be maintained. But yet i'm not sure what the opinion of the next.js team is - as they seem to prefer having all the samples there??!
Important note: This is an example not a boilerplate.
Eventual one problem we have is we try to solve some stuff very convenient by eg. providing the withI18next HOC - which magically solves most needed to be done to work. My bet most people landing here with issues come from another example withApollo, withRedux, withMobx whatever and add the stuff shown in our sample which in most case just won't work without proper adjustments - so eventual we should more focus on showing what needs to be done to solve it (teaching) - instead of trying to hide away the more complicated things...but not completely sure - this is just something that came to my mind in the morning.
I agree with @jamuhl . Teaching users to understand how things work is the hardest and most important thing to be done. So more users could give help to issues, as well as contribute back to the library. Once users understand how things work basically, ones could customize themselves for their need.
Hi,
I've been testing this sample in my project for a few days, it is mostly working really great, so thank you ! I might have some input regarding a few issues :
Concerning language subpath, in your _app.js, you do use router.replace, but you provide the same parameter to both url and :as . As far as I understand this, this is wrong, you need to provide the actual path to your file in url (meaning /index.js) and the "visual" one in :as (meaning /en/index.js). The way you do it trigger a server reload since client can't find /en/index.js. You may want to provide some routing there as well to handle conversion from /post/945 to /post?id=945
I corrected this behavior in my project for changing language quite simply as follow, and the language change is working as I suspect it should (no server reload) :
i18n.on("languageChanged", lng => {
if (process.browser) {
const originalRoute = window.location.pathname;
const correctedVisualPath = originalRoute.replace(/\/.?\//, /${lng}/
)
if (correctedVisualPath !== originalRoute) {
let actualPath = correctedVisualPath.replace(/\/.?\//, "/")
if(correctedVisualPath.startsWith(/${lng}/test
))
{
actualPath = '/test?search=' + correctedVisualPath.match(/[^/]*?$/)[0];
}
Router.replace(actualPath, correctedVisualPath, {
shallow: true
});
}
}
});
I didn't yet find a way for the similar issue on normal Link, so for now I just write every Link as follow, and it work quite nicely :
<Link url='/mypage' as=/${lng}/mypage><a>Link</a></Link>
I also am experiencing the issue described in #603 . It seems to only happens for the default language and on "first load" : if I run npm run build / start
the very first connection won't display the default language, generate a lot of .missing.json. If I hit ctrl-F5, default language is displayed. After that, any connection from any computer is displaying default language correctly.
Finally loading a page with new translation file seems to trigger some kind of clientside rerender, resulting in a "white flash" . I suppose this has something to do with the way translation files are loaded. This doesn't seems to break anything, but it's not "smooth".
Thank you again,
As a rudimentary way of voting, please thumbs up or down this comment to indicate whether or not you think language subpaths belong in the example.
Since your example is easier for you to maintain, you should link from nextjs examples to your example. I suspect the nextjs team will be fine with that since it reduces their maintenance and is a better user experience to see the most up to date example. There is some precedent for that here: https://github.com/zeit/next.js/tree/master/examples/page-transitions
Is there a way to detect broken ssr and throw exception in dev mode? It could be a good idea to consider doing that if it is possible so that people know something is not working as expected before shipping the product to production.
The example _app.js needs a comment linking to a page describing how to add i18next to a project already using something like Apollo. I tried adding i18next to my nextjs apollo project and could not fix the broken ssr despite reading all the relevant issues I could find only hinting I needed to merge something. I finally had to give up and tried adding react-intl example pattern to my apollo project and it worked fine in ssr. If you could try and add i18next to the nextjs with-apollo example and document exactly what it took to get it to work in all the closed "i18next with apollo" tickets that currently don't really contain any resolution, it would help a ton of people be able to use i18next rather than be forced to react-intl.
Hello again @Enalmada. Yes, we all agree the example should be hosted in this repo, and the NextJs team should link here. Just need to confirm that with @timneutkens.
Is there a way to detect broken ssr and throw exception in dev mode
React already does this for you. It'll let you know when the checksum doesn't match.
The example _app.js needs a comment linking to a page describing how to add i18next to a project already using something like Apollo
Couple things here:
_app.js
file.Please, let's stay on topic!
Hmmm...I have never seen a checksum error in the broken ssr situations I experienced. Is that something I need to enable or where would I see that? (I am very new to react so apologies if that is a silly question) If it will still be possible to have broken ssr after your enhancements to the example, just consider for a moment if there is something more that can be done in the next version of the example to prevent users from thinking their implementation works and shipping it to production when actually ssr is broken.
It is obviously on topic to call out ways the average user struggles to use the current example to add i18next to an existing project. I am sure you are already aware of the large number of apollo i18next issues, nextjs spectrum threads, stackoverflow links that exist without any solution to the problem. I am optimistic the future example will eliminate the specific problem people are currently having adding i18next to an apollo project, but if not, I am not asking you to add an apollo to the example, just that you consider a link to a solution in the readme to help people in that very common use case find a way through it.
Nextjs Link element also as some edge cases with i18next. You can see this thread here about it. I don't know if that is something that could be improved in the example code or if we need to talk with Nextjs team about it: https://spectrum.chat/?t=b9d1b95e-177b-4447-8cd1-bf2f488e44d5
Oh, and I would highly recommend creating a "next-i18next" project that would abstract out all code and sensible config defaults in example project that users are unlikely to need to ever modify themselves: like i18n.js, getAllNamespaces.js, location of languages, etc. Ideally to get started with next and react-i18next, new users would only need to run "npm -i next-i18next" and add absolute minimum to server.js to initialize with configuration overrides that differ from sensible defaults. Think how awesome it would be that critical fixes/features that you normally would make to the example repos would now automatically roll out to all users as a version update (rather than users having to watch the example repo for bug fixes that you make. Changes that might be critical but they will likely never know about them because they have copied over the original example project files and moved on).
@Enalmada I have written many times that SSR is an advanced topic, and we expect users to understand and be aware of this.
This issue is to discuss the future of the NextJs example. It has nothing to do with Apollo, nor anything to do with some next-i18next
repo. Unless your comments are directly related to the example, please post them elsewhere.
Apollo, warnings about SSR breakage, and future projects are all well outside the scope of this example and will be marked as off topic if needed.
My comments are all directly related to the future of the example from my perspective...I get that we both come at things from very different angles. Adding some supporting documentation referencing common issues the examples choose not to cover would be so helpful...I am not sure why the hostility.
Please try and understand the value of abstracting out dependencies and code to make it easier for users to onboard and maintainers to roll out fixes to the "glue" code between react-i18next and consumer projects. This is a pattern that nextjs users are used to: next-seo, next-offline, etc each wrapping sensible defaults and dependencies of things. Doing this would make a huge difference to the way you structure a future example so I just wanted to put it out there as an idea before you started just in case.
For those that want to follow the development of the new example, it can be found here (temporarily):
@Enalmada @isaachinman not read through the full thread -> but just my 50 cents on why it workes with react-intl without issues. => You bundle the translations with your applications - therefore is no async loading (namespace splitting) going on - all synchronous - all easy as piece of cake. Just add the translations in react-intl sample as a lazy import and see everything starts breaking.
So we could just remove i18next-xhr-backend, language-detection -> so we get free from even doing custom stuff in the server.js. All gets simple - no problems.
Just this sample is about adding i18next in production ready way - not make it dead simple for just doing getting started demos by oversimplify the real challenges of SSR and i18n. My problem is what i see in all the errors, issues and questions...those people using next.js have most time not the slightest idea what they are doing - how initialProps work because it's so damn easy to get started with one of the samples.
@isaachinman @Enalmada so i really would say the sample should be rather straight forward to learn what needs to be done - but integrations with all the apollo, mobx, redux, whatever needs to be solved in userland. Extending the react-i18next documentation with samples / hints for doing more complex stuff will be very welcome - but would go to far for the sample.
Edit: I have hopes we could make the v10 sample more expicit on what gets done and less magic -> so devs learn what needs to be done and not get a magic solution they do not really understand and therefore can't make the step including it into other eg. with-apollo sample.
@jamuhl Yes, we're on the same page and have been from the start. It's an example, not a boilerplate. We intentionally do not want to "abstract out code". That would defeat the purpose of an example.
The new example I'm working on should be simpler and more clear, and will contain a lot of comment-based documentation.
It seems like everyone (at least so far) thinks the language subpath functionality should remain in the example, so I will rewrite that as well.
The new example is coming along very nicely, and is in fact nearly done.
@jamuhl I've hit one interesting problem that I need your help with - something we did not even realise was occurring in the original example:
Let's assume we've got a namespace called contact-us
that contains some content for the contact page. If a user arrives at the root URL which does not rely on the contact-us
namespace, and then the NextJs app navigates to the contact page, the i18next-xhr-backend
will indeed fetch the relevant contact-us
namespace JSON file. This was clear in the original example.
However, NextJs has no idea about i18next translation JSON network requests, or any of that. The result is that the incoming route (in this case contact-us
) will be displayed before the translation data is loaded over the network.
The place to block a page transition while waiting for data is inside the NextJs getInitialProps
. We can await
any promise. However, I cannot seem to find anything useful to await.
I tried i18n.hasResourceBundle
and i18n.getResourceBundle
, but both are sync functions that return information about data already in the store.
I tried polling i18n.hasResourceBundle
in a loop, but it seems that i18next-xhr-backend
doesn't actually start making network requests until the React tree that requires namespaces is rendered, so that won't work either.
In short, it seems we're going to have to trigger network requests manually. It should be trivial, assuming there is a way to do this. Is there?
@jamuhl I was able to come up with a hacky solution that requires one to initialise the i18next-xhr-backend
via class construction so that we can have a reference to it and later invoke class methods:
await Promise.all(
i18n.nsFromReactTree.map(ns => {
if (!i18n.hasResourceBundle(lng, ns)) {
return new Promise(resolve => {
i18nextXHRBackend.read(lng, ns, (error, loadedNS) => {
i18n.addResourceBundle(lng, ns, loadedNS);
resolve();
});
});
}
return Promise.resolve();
})
);
Hopefully you can understand this. It's a quick solution and is nasty nested code. Basically it's calling the read
method on the in i18next-xhr-backend
class instance manually, and then after receiving the JSON, manually calling i18n.addResourceBundle
.
This works 100%. However, it's clearly a workaround and I feel it's bug prone and likely to break outright in the future.
Let me know if there's an official way to achieve this that I'm missing, or if you would suggest a different approach.
This is the final hurdle to releasing the new example.
that is very hacky and won't work in production -> as soon you got two components both needed that namespace it will load it twice. i18next handles resource loading very nicely by doing proper deduplication. => https://github.com/i18next/react-i18next/blob/master/src/NamespacesConsumer.js#L116 is like it is solved in the current version
The loadNamespace function has a callback that will be called when translations were already loaded or got loaded.
The render prop or hoc both can be set to wait before rendering their inner content or pass down a prop signaling readyness - if that helps.
Thanks, I'll take a look at loadNamespace
tomorrow. I think I tried it, but quickly passed as I didn't realise it did not return a promise. That's good news though.
By the way, none of this is happening inside the HOC, and will take place once per page render - I've refactored things quite a bit.
@isaachinman just had a short look at the sample...looks promising -> like the way initialProps get set explicitly without hiding that magically like in the old hoc
So, the new example is now ready for review.
Some noteworthy points:
_app.js
. I thought we could get away with just using _document.js
, but it's a bit more complicated than that. The way _app.js
is set up, most users should never have to touch/change anything for basic use cases._app.js
, the new HOC approach has completed abandoned NamespacesConsumer
in favour of withNamespaces
, and the I18nextProvider
.withNamespaces
and I18nextProvider
directly out of react-i18next
core, were it not for the need for our nsFromReactTree
array, which keeps track of all required namespaces, given a React component tree - irrespective of whether components are NextJS pages or not (this is crucial).Link
. @timneutkens example was very helpful - we need to parameterise our route params and also pass an as
argument to achieve true SPA with multi-langs. In the new example, you can switch languages in a fully-SPA manner without any hard reloads.For anyone that has time, please do review the code, experiment with the app, and let me know about any issues you might find. I've put the example on a free Heroku dyno here (it can take some time to boot).
The basic use flow for the example goes:
_app.js
alone unless you know what you're doing.withNamespaces
HOC, from lib/withNamespaces
.components/Link
instead of importing Link
from NextJs directly.That's it! Everything else should be straightforward and development should be a more or less "normal" React experience.
@isaachinman looks good to me...one thing I ask myself shouldn't we just add that https://github.com/isaachinman/react-i18next-with-nextjs/blob/master/lib/withNamespaces.js#L23 to the current implementation in the main repo -> does not hurt and for v10 we planned the same to better support SSR out of the box.
@jamuhl Yes, that's exactly what I was thinking as I wrote it. It's just a matter of pushing into a simple array. The thing is, there are definitely use cases where users would want to clear that array - new route, etc.
For this example (and probably in real projects) I decided that clearing the array on a new route doesn't make much sense as it only applies to clientside navigation, and the browser is already going to have the previous-route's namespaces anyways.
Anyways, after getting to know the react-i18next
library better, in my opinion I18nextProvider
and withNamespaces
is the best approach currently available, and nsFromReactTree
works flawlessly if you stick to it. I'm not sure how it would work if people are mixing in NamespacesConsumer
etc.
@isaachinman fun fact withNamespaces uses NamespacesConsumer: https://github.com/i18next/react-i18next/blob/master/src/withNamespaces.js#L33
So doing same nsFromReactTree
in the sample could be done in consumer https://github.com/i18next/react-i18next/blob/master/src/NamespacesConsumer.js#L25 (just in the new way - not using reportNS)
Ah yes, I remember seeing that now. Must have slipped out of my head immediately.
As far as I can tell, the new example solves all open related bugs/issues.
@jamuhl Is there anyone else whose review you'd like to request before we think about merging?
@isaachinman guess we could merge it...and from there improve with the promise based i18next,...
I think the promise-based release would only affect this line of the NextJs example.
But sounds good, I will put this work into a PR soon.
@isaachinman I'm not really a pro i18next config but I had to set fallbackLng
to null because in production the front-end always switched back to the DEFAULT_LANGUAGE
when the server side language was right. Otherwise, the new example looks great, thank you very much.
The last example failed on my project. Decided to rollback to old working version. I'm afraid of trying out new things here. Such a lengthly and hopeless hours of debugging without any chance to get things done. Breaking without any chance for you to fix it. It's this library now. I'm not sure...
@Nelrohd That sounds like a bug. Can you help me to reproduce that? If setting fallbackLng
to null
fixes your problem, that potentially sounds like an issue with i18next-browser-languagedetector
.
@revskill10 I am sorry to hear you are frustrated. I understand - creating SSR React apps with localisation and code splitting is not an easy task.
We are doing our best to improve the example and help other developers as much as we can.
I will personally help you with your project if you'd like - just email me directly.
@isaachinman @Nelrohd could it be initialLanguage gets not passed down. But we really need some steps to reproduce.
@revskill10 sorry for the frustration - if the old system worked there is no need to update i would say. We just try to make the new sample more like a learning point - looking at the last issues related to next.js and react-i18next most cases where related to changes made and combining other next.js samples without the knowhow to "glue" things together -> the sample here is no boilerplate and no just put everything in at it will not break under any changes made.
@jamuhl Yes, agreed, and I want to take some responsibility for those issues too. @revskill10 That is why I took the time to do a deep dive and rewrite the example from scratch. I spent quite some time on it. I am very confident it is a big improvement over the last example. If you have time to check it out, you might be pleasantly surprised. Regardless, if you do want help or assistance getting things up and running, you can PM me.
@isaachinman @jamuhl Really appreciated all your efforts. A thank you is not enough.
But we should stop all the pain one and for once by introducing a next-i18next
package.
I think you have enough deep knowledge to make such thing happen.
Our goal is to minimize the interface to integrate/update to existing project.
Why ?
By introducing an independent package, we have more chance to test it for many use cases without breaking them before installing it. And more next.js
discussion should not belong to this project, which is pure React package.
If nextjs
is too common, a monorepo style is enough in this case.
My two cents.
@revskill10 I believe this is what @Enalmada was asking for as well. Now that we have discussed and completed the example here, maybe it is time to consider this.
What would a nextjs-i18next
package be? There is way more going on than being able to simply do a yarn add nextjs-i18next
and being "done". If you look through the new example, you will see how many areas we need to touch. I suppose a package could be possible, but we would need to create a HOC to wrap _app.js
so that we have access to getInitialProps
, as well as some other things.
@isaachinman @jamuhl
In the config.js
...
detection: {
order: ['header', 'querystring'],
},
...
And set fallbackLng: DEFAULT_LANGUAGE
,
In my case, my Google Chrome language settings is fr
but DEFAULT_LANGUAGE
is en
. So what happens is:
cmd + R
(back-end refresh)french
translationsenglish
translations (not good, I should see french)Both my i18n.language
and initialLanguage
say fr
when I debug.
Let me know if you need more info
Note that set fallbackLng
to null
solves the problem.
@Nelrohd We only have English and German content in the example. What do you mean you see French translations? Do you have a repo I can check out and reproduce this with?
@Nelrohd Regardless of that, why did you change the detection settings? That is most likely your issue. As it says in the codebase:
only change if you understand what you're changing
@isaachinman Thanks for the work, the new example, after some work to make it as I wished, is working allmost perfectly. small question : is it normal that the language change event is fired at every page change ? seems weird (maybe something I did wrong, I "glued" a few thing on top of i18n)
@isaachinman I setup my own website following the example but I can't share the code. I changed the detection because it's not what I wanted. When I change my settings in my browser, I want it to change right away when I refresh, I'm not sure what the cookie settings brings as value, can you explain this please?
@Nelrohd Please open a separate issue if you believe it's a bug, or post your question on StackOverflow. It sounds like a custom-implementation concern.
I won't open anything since the example works fine for me and I have no idea what to asks, I'm just giving you feedback. I can try to reproduce the issue with the example maybe?
@Eruilz That is a great point, I had not noticed that. I believe it's because initialLanguage
is being set on a new page render.
@jamuhl Is this expected/normal? Should we avoid resetting initialLanguage
on the clientside?
If so, should be an easy fix.
@Nelrohd If you can reproduce the issue with the example, that would definitely be a bug. In general, use of cookies is the most reliable method I've found of sharing lang state between client and server. It would make sense in your case that your client reverts to English, because it isn't reading headers.
It's probably the cause, I guess not many people will change their chrome language settings.
It's irrespective of your browser settings.
What do you mean? Google Chrome uses language settings to set the languages header
We can discuss the future of the NextJs example here, as it has gotten a lot of attention lately, and has some shortcomings.
Current problems:
Future goals:
Link
usage to match this example, thus preventing 404s and fixing #569withNamespaces
HOC that supports tree traversal, and make it abundantly clear that this is the only way to translate content. Fixes issues like #603.I believe the best way forward is a complete rewrite to simplify a few things. The first step is to decide if we want to support language subpaths at all.
Are there any other issues people are aware of currently?
I would like to compile a full list of current issues we are facing here, as well as any new features, or features we plan to remove, before proceeding with any work on the NextJs example.
Only then can we have a clear plan and execute it.