i18next / next-i18next

The easiest way to translate your NextJs apps.
https://next.i18next.com
MIT License
5.51k stars 762 forks source link

How to add translation to page that has parameter, but does not need SSR or static generation? #1400

Closed kristijorgji closed 3 years ago

kristijorgji commented 3 years ago

In relation to https://github.com/isaachinman/next-i18next/issues/1399

I will explain one scenario, that just adding getStaticProps is not working.

consider one structure pages/posts/[id]/edit.tsx Without translation, this does not need any ssr or static generation, as gets in client side the id, then fetches from api the post and user can modify it.

Now I want to add next-i18next to this page.

After adding this function

// some page definition react comp
//...

const tNamespaces = ['header'];
export async function getStaticProps({ locale }: GetStaticPropsContext): Promise<GetStaticPropsResult<SSRConfig>> {
    return {
        props: {
            ...(await serverSideTranslations(locale || DEFAULT_LOCALE, tNamespaces)),
        },
    };
}
export default withTranslation(tNamespaces)(EditPostPage as any);

I am getting error like this

> Build error occurred
Error: getStaticPaths is required for dynamic SSG pages and is missing

How should getStaticPaths be implemented for this use case ? We have no interest to pre-generate pages like /posts/?id?/edit because is not needed as explained at the top. We only need that all these pages, have available a particular set of translations, and ids are known only at runtime

Any suggestion would be appreciated

isaachinman commented 3 years ago

This is clearly explained in the NextJs documentation. The next-i18next package does not handle routing at all.

If, for some reason, you want your dynamic pages to indeed only render on the client, and not be statically generated (unwise choice in most cases), then just use fallback: 'blocking'.

Good luck!

sambP commented 2 years ago

@isaachinman I have the same problem as @kristijorgji . It is absolutely impossible to prerender all my dynamic routes statically because they belong to the user and are changing all the time. At this point, it would be nice to have only one prerendered skeleton per language. Fallback blocking is pretty bad because the loading time is way too long and not necessary. I got it working for me with fallback true, but the result is also not very satisfying. The loading time is also very long because nearly every request has a different route.

Can you recommend me a solution to this problem? I'm currently thinking about replacing the dynamic routes with query parameters, but that seems kind of hacky to me.

adrai commented 2 years ago

You could try with client side translations: https://github.com/i18next/next-i18next#client-side-loading-of-translations-via-http

sambP commented 2 years ago

Thank you very much! We are currently still on v10, I will definitely have a look at this. Sounds very promising.

kristijorgji commented 2 years ago

@isaachinman I have the same problem as @kristijorgji . It is absolutely impossible to prerender all my dynamic routes statically because they belong to the user and are changing all the time. At this point, it would be nice to have only one prerendered skeleton per language. Fallback blocking is pretty bad because the loading time is way too long and not necessary. I got it working for me with fallback true, but the result is also not very satisfying. The loading time is also very long because nearly every request has a different route.

Can you recommend me a solution to this problem? I'm currently thinking about replacing the dynamic routes with query parameters, but that seems kind of hacky to me.

I am back with one HACKY answer for you, that works fine but adds one unwanted nextjs rewrite.

So that shitty solution is to use what you first suggest another url, so instead of /posts/[id] /posts and use query parameters but as a rewrite!!! just to trick this stupid part of nextjs not to generate new prerendered pages for every different id.

So the flow would be user opens still /posts/3 -> nextjs rewrites this to /posts?id=3 -> posts.tsx file handles this and checks if query params contains id or not

sambP commented 2 years ago

Thanks for this tipp! I got it only working with the ?id=3, but I like your hacky solution much more :D

ludalex commented 1 year ago

Hey, facing the same problem. I know this has been closed, but has anyone found a better solution that doesn't involve:

Kottakji commented 1 year ago

Hey, facing the same problem. I know this has been closed, but has anyone found a better solution that doesn't involve:

* client side translation fetching (and then dealing with having to check if locale is ready every time)

* going around Next.js build time shenanigans and using query params

* setting fallback: true or blocking and then increasing page load time?

I found a solution. Albeit it feels a bit hacky, and it's odd that there is no standard solution in this library.

posts/[id].jsx

// For example: posts/1
export default function Posts() {
  const { asPath } = useRouter();
  const id = asPath.split("/posts/")[1];
  const { t } = useTranslation();
 // etc
}

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, [
        'common',
      ])),
    },
  }
}

export const getStaticPaths  = async () => {
  return {
    paths: [], 
    fallback: "blocking",
  };
};

Note: Code just copied and pasted to make it simple, not tested.

ludalex commented 1 year ago

@JorisKok Adding fallback: "blocking" increases page load by quite a bit! That's one of the solutions I want to avoid and listed in the comment before.

Also, since in your example you're basically doing everything by the book, I don't know why you're getting the post id by doing asPath.split("/posts/")[1] instead of simply using the router.query object.

Kottakji commented 1 year ago

@JorisKok Adding fallback: "blocking" increases page load by quite a bit! That's one of the solutions I want to avoid and listed in the comment before.

Good point. Just wanted to share the code in case people are looking for a solution they can copy/paste.

Also, since in your example you're basically doing everything by the book, I don't know why you're getting the post id by doing asPath.split("/posts/")[1] instead of simply using the router.query object.

Ah nice, wasn't aware of it.