Support for `getServerSideProps`
thomb opened this issue ยท 43 comments
Is your feature request related to a problem? Please describe.
When using getServerSideProps
as opposed to getInitialProps
results in a namespacesRequired
warning, despite the fact that I am returning namespacesRequired
inside the required props
key
I don't know if this is factual or not, or if the warning is a red herring
Describe the solution you'd like
Ideally I'd like getServerSideProps
to be supported.
Apologies if this is vague or unclear, or a completely unreasonable request.
Thanks in advance
NextJs 9.3 was released a week ago. None of the new data fetching methods are supported yet, but in general I'd lean towards getStaticProps
over getServerSideProps
. Typically it's better to associate translation changes with releases, rather than having changes at runtime.
The next-i18next
package will be entirely rewritten once NextJs plugin support lands, as quite a few things have changed since its inception.
I'm kind of newbie so sorry for asking this question. As I've figured out your amazing library currently fully supports Next js 9.2, right? Can I start my project using Next js 9.2 and stick to next-i18next 4.2.1 for the next few months?
It fully supports NextJs 9.3 as well, just not the new data fetching methods.
That is entirely false. That'd be a breaking change, and would go against semver. Read the docs.
No deprecations are introduced and getInitialProps will continue to function as it currently does.
It fully supports NextJs 9.3 as well, just not the new data fetching methods.
May I know if there is any schedule for supporting getServerSideProps
and getStaticProps
?
I do not currently have much time for development on this project due to other factors in life. I don't see this changing for the next few weeks, at least.
As I've stated, next-i18next
will be rewritten once plugin support (finally) lands. If you'd like to see this (and many other features) supported, please let the NextJs team know that you are waiting for plugin support!
Just noticed that you can use i18next
inside getServerSideProps
in the same way as you would do it in the express application, i.e. create & configure plain i18next
instance and use it instead
@maxmalov Can you explain what you mean? We still have a dependency on the i18next browser detector middleware.
getServerSideProps
is being called on the server-side only, consider the example
import i18next from 'i18next';
const createI18Next = (lang) => {
// create & configure i18next instance
}
export const serverSideMethod = ({ params, req, res }) => {
// detect lang from req somehow (probably can reuse express middleware)
const lang = detect(req);
const i18n = createI18Next(lang);
const { t } = i18n;
// do whatever you want with t
}
So later one can use serverSideMethod
inside getServerSideProps
and get already translated content.
// somewhere in pages directory
import { serverSideMethod } from '...';
export const getServerSideProps = ctx => serverSideMethod(ctx)
export default props => { ... }
For now, it's just a theory. We're working on the AMP first app and have to reuse the existing 18next resources we use in the separate single-page app. So in a few days, we eventually find out whether it works or not
Yes, that approach will work if you want to roll your own implementation, but has nothing to do with this package. Let's keep discussion here on topic.
Any updates here?
It can be a workaround for now, or a way to silence
You have not declared a namespacesRequired array on your page-level component: Page. This will cause all namespaces to be sent down to the client, possibly negatively impacting the performance of your app. For more info, see: https://github.com/isaachinman/next-i18next#4-declaring-namespace-dependencies"
Thanks
Hey @pettomartino - no update from my side. You can always silence warnings by setting strictMode
to false
. Please do feel free to contribute if you so choose!
@isaachinman thank for so quick reply. Gonna try to implement something then. Any hints?
@pettomartino Do you mean contributing to next-i18next
? If we want to support getServerSideProps
, we'll need to update the appWithTranslation
HOC to also look for that data fetching method in addition to getInitialProps
.
@pettomartino there is a demonstration that the i18n solution works in SSG, Hope it helps.
vercel/next.js#11841
since we cant use serverSideProps together with initialProps. we can make use of React FunctionComponent.defaultProps
here is the workaround that works for me, and it still works with initialProps (#615 (comment))
- particular page
Page.defaultProps = {
i18nNamespaces: ['home-page']
}
- main app / _app.tsx
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => {
const appProps = await App.getInitialProps(appContext)
const defaultProps = appContext.Component.defaultProps
return {
...appProps,
pageProps: {
namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])]
}
}
}
since we cant use serverSideProps together with initialProps. we can make use of React FunctionComponent.defaultProps
here is the workaround that works for me, and it still works with initialProps (#615 (comment))
- particular page
Page.defaultProps = { i18nNamespaces: ['home-page'] }
- main app / _app.tsx
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => { const appProps = await App.getInitialProps(appContext) const defaultProps = appContext.Component.defaultProps return { ...appProps, pageProps: { namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])] } } }
this works. thanks!
Is it correct that the defaultProps
must be set on the result of the HOC:
This works for me:
const page = withTranslation('toto')(TotoComponent);
page.defaultProps = {
i18nNamespaces: ['toto','bar']
}
export default page;
This does not:
TotoComponent.defaultProps = {
i18nNamespaces: ['toto','bar']
}
export default withTranslation('toto')(TotoComponent);
I'm using this version, so that it picks up recursive namespaces from WrappedComponents when using HOC
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => {
const recursiveNamespaces = (component: NextComponentType & { WrappedComponent?: NextComponentType }, acc: string[] = []): string[] => {
const ns = component.defaultProps?.i18nNamespaces;
const a = (typeof ns === "string") ? [...acc, ns] : [...acc, ...(ns || [])];
let wrappedComponent = component.WrappedComponent;
if (wrappedComponent) {
return recursiveNamespaces(wrappedComponent, a)
} else
return a
}
const appProps = await App.getInitialProps(appContext)
const namespaces: any = recursiveNamespaces(appContext.Component)
return {
...appProps,
pageProps: {
namespacesRequired: [
...(appProps.pageProps.namespacesRequired || []),
...namespaces]
}
}
}
It allows to use your defaultProps like this:
TotoComponent.defaultProps = {
i18nNamespaces: ['toto','bar']
}
export default withTranslation('toto')(TotoComponent);
@rparree I think you're commenting on the wrong issue. This issue is about getServerSideProps
.
@isaachinman I was referring to the comment with the workaround submitted by @icrona above (#652 (comment))
Sorry, what exactly is that workaround achieving?
With the solution in their comment you can use defaultProps
to set the namespaces when using a component that uses getServerSideProps
. My code looks for defaultProps
recursively when using a HOC.
As far as I can tell, that doesn't achieve anything except slightly modifying the API. It won't enable SSG.
since we cant use serverSideProps together with initialProps. we can make use of React FunctionComponent.defaultProps
here is the workaround that works for me, and it still works with initialProps (#615 (comment))
- particular page
Page.defaultProps = { i18nNamespaces: ['home-page'] }
- main app / _app.tsx
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => { const appProps = await App.getInitialProps(appContext) const defaultProps = appContext.Component.defaultProps return { ...appProps, pageProps: { namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])] } } }
how would that work for typescript?
@isaachinman It does the trick for me. I am able to use a component like this (and it will be SSR)
export const getServerSideProps = async function (ctx: NextPageContext) {
โฆ
return {
props: {
totoProp : ""
}
}
}
TotoComponent.defaultProps = {
i18nNamespaces: ['toto','bar']
}
export default withTranslation('toto')(TotoComponent);
since we cant use serverSideProps together with initialProps. we can make use of React FunctionComponent.defaultProps
here is the workaround that works for me, and it still works with initialProps (#615 (comment))1. particular page
Page.defaultProps = { i18nNamespaces: ['home-page'] }
1. main app / _app.tsx
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => { const appProps = await App.getInitialProps(appContext) const defaultProps = appContext.Component.defaultProps return { ...appProps, pageProps: { namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])] } } }
This approach breaks SSR for me. Only the keys are loaded initially and after a delay they are replaced by the translation.
Edit: nvm this works great, I just missed that the defaultProp is called i18nNamespaces
instead of namespacesRequired
To get all namespaces loaded in case none are defined on the page, you would have to adapt icronas solution like this:
NextApp.getInitialProps = async (appContext) => {
const appProps = await App.getInitialProps(appContext);
const { defaultProps } = appContext.Component;
let pageProps = {}
if(appProps.pageProps.namespacesRequired || defaultProps?.i18nNamespaces) {
pageProps = {
namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])],
}
}
return {
...appProps,
pageProps
};
};
@mattcrn The next-i18next
package will already load in all namespaces if you don't define any on the page. Let's keep this issue strictly about the support of getServerSideProps
.
@isaachinman yes, but using icrona's solution will break this functionality since it will return an empty array for namespacesRequired
and the package is simpy checking if namespacesRequired
is defined for loading all namespaces.
So, namespacesRequired: []
means no namespaces are being loaded.
@pettomartino Do you mean contributing to
next-i18next
? If we want to supportgetServerSideProps
, we'll need to update theappWithTranslation
HOC to also look for that data fetching method in addition togetInitialProps
.
As far as I understand it, getServerSideProps is not part of the page component, so we would have to create a higher order function to wrap the pages getServerSideProps. Or is there a better way?
I think when this feature lands, it would be ideal to support both getServerSideProps
and getStaticProps
. For most users of this library, local translations and staticProps will be fine, but the serverSideProps may be useful in projects that are using https://locize.com/ or something similar, where translations are managed outside of the project.
@ckeeney Agreed.
Any update since then ?
Looking forward to have this thing implemented. ๐
@divlo Feel free to submit a PR.
+1
since we cant use serverSideProps together with initialProps. we can make use of React FunctionComponent.defaultProps
here is the workaround that works for me, and it still works with initialProps (#615 (comment))
- particular page
Page.defaultProps = { i18nNamespaces: ['home-page'] }
- main app / _app.tsx
MyApp.getInitialProps = async (appContext: AppContextType<Router>) => { const appProps = await App.getInitialProps(appContext) const defaultProps = appContext.Component.defaultProps return { ...appProps, pageProps: { namespacesRequired: [...(appProps.pageProps.namespacesRequired || []), ...(defaultProps?.i18nNamespaces || [])] } } }
Well, i thought it would work good, but it works with delay:
At the first loading, it does not load the skill-test
namespaces, i need to reload it to get that namespace active and the proper localization strings.
Inspecting it for the first time we get this:
After reloading, we can find the required namespace:
Both namespaces are in the option, but just the first one, defined on _app.js
by getInitialProps
, is loaded.
Why does this happen?
@SalahAdDin This will be solved by #869.
Fixed in next-i18next@8.0.0
.
Thanks a lot for this @isaachinman
@isaachinman Does the new release mean we must deleted the whole workaround for this matter and use the new hook? It is not so much clear for me.
@SalahAdDin Correct, there is no need for a workaround in v8.
Import the serverSideTranslations
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
Now from the getServerSideProps, pass the ..(await serverSideTranslations(locale, ["common"])),
with the props.
export const getServerSideProps: GetStaticProps = async ({
locale,
locales,
query
}: any) => {
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
}
}
};
Now add your language string inside
/public/locales/en/common.json
For example
{
"home": {
"Test": "Test"
}
}
You can add more language strings inside the locales directory.
Restart your Next.js app. It will work.