next-i18next: Next dynamic routing is incompatible with next-i18next's Link

Describe the bug

I tried to use dynamic routing from Next.js with next-i18next. I imported Link from NextI18Next instead of next/link and it causes the error saying: Your <Link>'s asvalue is incompatible with thehref value. This is invalid.

What I tried:

  • Add as property to Link as well as remove it

Does any one have the same issue?

Occurs in next-i18next version

next-i18next version: 0.50.0

Steps to reproduce

I set up a minimal repository here: https://github.com/lazidoca/dynamic-routing-next-i18next-app

Expected behaviour

You must provide a clear and concise description of what you expected to happen.

OS (please complete the following information)

  • Device: Windows 10
  • Browser: Chrome 75.0.3770.142
  • Node: v10.16.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 43
  • Comments: 49 (13 by maintainers)

Most upvoted comments

I can confirm that it is working now in next v9.2.1. @eduludi you need to supply query params to your href.

e.g.

<Link href="/[id]?id=foo" as="/foo">
  <button type="button">{t("to-second-page")}</button>
</Link>

or

<Link href={{ pathname: "/[id]", query: { id: "foo" } }} as="/foo">
  <button type="button">{t("to-second-page")}</button>
</Link>

This is a huge deal breaker and should be stated in the first lines of the readme. šŸ¤¦ā€ā™‚

Can anyone suggest any workarounds here ?

This might be a bit hacky, but it’s a pretty simple workaround and works for me.

Have your folder structure like this:

- pages
  - news
    - [id].js
    - page.js

Write your code in Page.js and in [id].js, the following:

import Page from './page';
export default Page;

Then, use i18n’s Link component just like this:

<Link href={`/news/page?id=${post.slug}`} as={`/news/${post.slug}`}>
    <a>{post.title}</a>
</Link>

Client-side navigation & SSR šŸŽ‰

Like @vimutti77, it also seems to work as expected in my project:

<Link
  href={{
    pathname: '/videos/[slug]',
    query: {
      slug: 'example',
    },
  }}
  as="/videos/example"
  passHref={true}
>
  <a>Link to example video</a>
</Link>

I also needed to replicate the same linking behaviour with Router.push() elsewhere — using Router from NextI18NextInstance — which I got to work like so:

Router.push(
  {
    pathname: '/videos/[slug]',
    query: {
      slug: 'example',
    },
  },
  '/videos/example'
)

Anyone want to roll those changes into next-i18next, or should we assume users are happy to handle that complexity?

@isaachinman for sure I will. Anyway I think a warning or pin this issue is not a bad call. When I see an active project like this I would expect it to support and integrate with all latest features of the main lib. šŸ¤·ā€ā™‚

Anyway good work. I will try to find a workaround and update here if I succeed 😃

@RafaPolit This problem occurs when users:

  1. Need localeSubpaths
  2. Need dynamic routing

Essentially, the two are mutually exclusive due to a limitation on the NextJs side of things. Please do feel free to have a look at the implementation here and see if you can come up with a creative fix.

In general, if people want to see this implemented, the most helpful thing to do is make some noise over in the NextJs repo.

Calling i18n.changeLanguage(foo) in dynamic page also not working properly. The language is changed but the url is not.

Hi there, I have just released next-i18next@8.0.0, which uses the internationalised routing built into NextJs v10+. This new version of next-i18next is quite different, thus making this PR no longer relevant. Thanks!

@oswaldoacauan Feel free to relay your frustrations to the NextJs team. Not sure if it’s a ā€œdeal breakerā€, as there are no other prebuilt options that I’m aware of. There are workarounds posted above.

@pixelass Since you seem to be having a look around, just be aware that we’ve got utils like subpath-from-lng and subpath-is-required that should handle most of the heavy lifting for you.

zeit/next.js/pull/9837

Anyone using rewrites in next.config.js running into page reloads in production?

I’ve just used now and it works perfect.

@michaelmota Thanks for your responses. This is exactly what I am doing, as you suggested.

server.ts

import nextI18NextMiddleware from 'next-i18next/middleware';
import nextI18next from '../../src/lib/i18n';

...

server.use(nextI18NextMiddleware(nextI18next));
...
server.get('*', (req: Request, res: Response) => handle(req, res));
...

i18n.ts

import isNode from 'detect-node';
import {NextComponentType, NextPageContext} from 'next';
import NextI18Next, {InitConfig} from 'next-i18next';
import {useTranslation as originalUseTranslation} from 'react-i18next';
import {DEFAULT_LANGUAGE, LOCALE_SUBPATHS} from '../../utils';

const config: InitConfig = {
  ns: [
    Namespaces._error,
    Namespaces.common,
    Namespaces.home,
    Namespaces.assets,
    Namespaces.newsroom,
    Namespaces.article,
  ],
  defaultNS: Namespaces.common,
  load: 'currentOnly',
  lowerCaseLng: true,
  cleanCode: true,
  defaultLanguage: DEFAULT_LANGUAGE,
  preload: [DEFAULT_LANGUAGE],
  fallbackLng: DEFAULT_LANGUAGE,
  otherLanguages: ['ar', 'zh_cn', 'zh_tw', 'th', 'ja', 'ko'],
  whitelist: ['en', 'ar', 'zh_cn', 'zh_tw', 'th', 'ja', 'ko'],
  localePath: isServer ? 'public/locales' : 'locales',
  localeSubpaths: LOCALE_SUBPATHS,
  interpolation: {
    escapeValue: false,
  },
  browserLanguageDetection: true,
  serverLanguageDetection: true,
  debug: process.env.NODE_ENV === 'development' ? true : false,
  strictMode: process.env.NODE_ENV === 'development' ? true : false,
};

const NextI18NextInstance = new NextI18Next(config);

export const useTranslation = originalUseTranslation;
export const {appWithTranslation, withTranslation, Link, i18n, Trans} = NextI18NextInstance;

export const includeDefaultNamespaces = (namespaces: string[]) => [
  Namespaces.common,
  Namespaces._error,
  ...namespaces,
];

export type I18nPage<P = {}> = NextComponentType<
  NextPageContext,
  {namespacesRequired: string[]},
  P & {namespacesRequired?: string[]}
>;

export default NextI18NextInstance;

Link.ts

import {withTranslation, Namespaces, Link} from '../../lib/i18n';

    <Link passHref href={href} as={as} replace={replace} scroll={scroll} {...rest}>
      <StyledAnchor>{children}</StyledAnchor>
    </Link>

Get’s passed these values, as an example"

    const href = `/news/[slug]?slug=${featuredArticle.slug}`;
    const as = `/news/${featuredArticle.slug}`;

I just upgraded to:

"next": "^9.5.0",
"next-i18next": "^6.0.2",

And refactored my next-18next implementation from removing the additions to the custom server to use rewrites in next.config.js, and all of my links start working without the hard reloading!!

We have the same setup and you can use this lib but you will not be able to use the he localeSubpath option and you will also need to write your own language switcher component that is aware of the new slug for the new language.

On Mon 27. Jul 2020 at 17:00, Gregor Adams notifications@github.com wrote:

@isaachinman https://github.com/isaachinman we have a rather complicated setup so while it’s very nice to offer help we will have to figure this one out ourselves (I’m currently in discovery of our project-setup).

tldr;

The HOC didn’t work, it was causing a server-side render instead of client-side. I noticed this while digging into the code of next-i18next. I tried fixing this within https://github.com/isaachinman/next-i18next/blob/master/src/utils/lng-path-corrector.ts#L42 and then realized that the issue can’t be fixed with my approach. Let’s just say: ā€œI was tired and overworked when I wrote all those commentsā€.

I think right now the solution from #413 (comment) https://github.com/isaachinman/next-i18next/issues/413#issuecomment-579284859 or #413 (comment) https://github.com/isaachinman/next-i18next/issues/413#issuecomment-643285517 should work.

… … IN DETAIL

Context Our urls are fully localized so even switching the language causes complications since the english page doesn’t know the urls of the other languages and simply switching the subpath will not work.

Example

I might look into it some other time but right now I’m not sure if we can use this library (which is not due to this library but our url-logic)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/isaachinman/next-i18next/issues/413#issuecomment-664449205, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJVWJRWEEBE5PVJQGGT6TDR5WJBDANCNFSM4IFXIRLA .

@pixelass We would want to extend the existing component, and we would need unit tests and most likely e2e tests as well.

@vimutti77 That might be a separate issue. Any client side calls to changeLanguage should trigger this callback. If you think you’re experiencing a bug there, please do open a separate issue.

The Link component that next-i18next exports does not call change or do anything imperatively. It simply returns the default Link component from next/link, modifying the href and as props where necessary.

I believe this is related to https://github.com/zeit/next.js/issues/7488.

Here’s some JSX that causes the error, within a next-i18next project:

<Link href='/post/[id]' as='/post/second'>
    <a>Second Post</a>
</Link>

With localeSubpaths set to "all", and a current language of "en", we’d expect this Link to eventually lead to /en/post/second, while displaying the page located at pages/post/[id]. Instead, we get the error:

Your `<Link>`'s `as` value is incompatible with the `href` value. This is invalid.

However, this doesn’t appear to be specifically related to next-i18next. If you import Link directly from next/link and change your JSX to:

<Link href='/post/[id]' as='/en/post/second'>
    <a>Second Post</a>
</Link>

You will see the exact same error.

Not sure what to say, except that we might need to drop localeSubpath support altogether, as it has always been a complicated and opinionated thing to support, and there isn’t a clear way forward.