Yaya
Yaya

Reputation: 4848

How to navigate when using `Next-intl` and `next/link`

I implemented a next-intl for multi-language support. So my paths look like www.next.com/de or www.next.com/en

I'm using NextJS 14.

<NextLink href="/support-us">{t("About us")}</NextLink>

And when I want to navigate to for example /support-us it must navigate me to /en/support-us instead I get to /support-us so it throws an error.

NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

Here is my code. middleware.ts

import createMiddleware from "next-intl/middleware";
import { locales } from "./i18n";

export default createMiddleware({
  // A list of all locales that are supported
  locales,

  // Used when no locale matches
  defaultLocale: "en",
});

export const config = {
  // Match only internationalized pathnames
  matcher: ["/", "/(en|it|de|nl|fr|sv|es|nb|pt|pl)/:path*"],
};

i18next

import { notFound } from "next/navigation";
import { getRequestConfig } from "next-intl/server";

interface Language {
  symbol: string;
  name: string;
}

export const supportedLanguages: Language[] = [
  { symbol: "EN", name: "English" },
  { symbol: "DE", name: "Deutsch" },

];

const languageSymbolsLowercase: string[] = supportedLanguages.map((lang) =>
  lang.symbol.toLowerCase()
);

const initialLocales: string[] = [];

const combinedLocales: string[] = [
  ...initialLocales,
  ...languageSymbolsLowercase,
];

export const locales: string[] = combinedLocales;

export default getRequestConfig(async ({ locale }) => {
  if (!locales.includes(locale)) notFound();

  return {
    messages: (await import(`../messages/${locale}.json`)).default,
  };
});

Upvotes: 5

Views: 3981

Answers (5)

None of the answers above worked for my app(next v15.1.2), but adding this configuration to the middleware worked for me:

import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: [
    // Enable a redirect to a matching locale at the root
    '/',

    // Set a cookie to remember the previous locale for
    // all requests that have a locale prefix
    '/(de|en)/:path*',

    // Enable redirects that add missing locales
    // (e.g. `/pathnames` -> `/en/pathnames`)
    '/((?!_next|_vercel|.*\\..*).*)'
  ]
};

Found this on the sorce code of the App Router Example

Upvotes: 1

Abderrahmane Bihi
Abderrahmane Bihi

Reputation: 1

Or you could simply add a navigation.ts file:

import {createSharedPathnamesNavigation} from 'next-intl/navigation';
import {locales} from './i18n'

export const {Link, redirect, usePathname, useRouter} =
  createSharedPathnamesNavigation({locales,/* ... */});

and use that exported Link:

import { Link } from "@/src/navigation"
    <Link href="/support-us">{t("About us")}</Link>

Upvotes: 0

Greg W.F.R
Greg W.F.R

Reputation: 734

You should simply add the path to your middleware matchers.

middlewere.ts

import createMiddleware from "next-intl/middleware";
import { locales } from "./i18n";

export default createMiddleware({
  // A list of all locales that are supported
  locales,

  // Used when no locale matches
  defaultLocale: "en",
});

export const config = {
  // Match only internationalized pathnames
  matcher: ["/", "/(en|it|de|nl|fr|sv|es|nb|pt|pl)/:path*", "/support-us"],
};

So you just added "/support-us" to the matcher array

Upvotes: 0

Yaya
Yaya

Reputation: 4848

At the end I had to add a navigation file as mentioned in the next-intl documentation in there it exports a Link component which solves the problem and navigates to /[locale]/support-us
for example /en/support-us here is my navigation file

import {
  createLocalizedPathnamesNavigation,
  Pathnames,
} from "next-intl/navigation";
import { locales } from "./i18n";

export const localePrefix = "always"; // Default

export const pathnames = {
  "/support-us": "/support-us",
} satisfies Pathnames<typeof locales>;

export const { Link, redirect, usePathname, useRouter, getPathname } =
  createLocalizedPathnamesNavigation({ locales, localePrefix, pathnames });

And use the Link exported in the navigation file

import { Link as NavLink } from "@navigation"
<NavLink href="/support-us">{t("support us")}</NavLink>

Upvotes: 4

0xKevin
0xKevin

Reputation: 1071

To explicitly include the locale in your paths, you can use the useRouter hook from next/navigation (in nextjs 14) to access the current locale, and then prepend it to your paths.

Here's how you could adjust your <NextLink> usage to incorporate the current locale:

"use client"
import { useRouter } from 'next/navigation';

const SupportUs = () => {
  const router = useRouter();
  const { locale } = router;

  return (
    <NextLink href={`/${locale}/support-us`}>{t("About us")}</NextLink>
  );
};

UPDATE: I tried to simulate your use case on my end, and I think you can achieve your desired result on the server side by adjusting your middleware.ts file. You won't need to modify your component/page file though.

So, below is the updated middleware.ts that I tried to revise:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import createMiddleware from 'next-intl/middleware';
import { locales } from './i18n';

// Your existing createMiddleware setup
const intlMiddleware = createMiddleware({
  locales,
  defaultLocale: 'en',
});

export default function middleware(req: NextRequest) {
  // Attempt to retrieve the locale from the previous URL
  const referer = req.headers.get('referer');
  let refererLocale = '';

  if (referer) {
    // Extract the locale from the URL path in the Referer header
    const pathLocaleMatch = referer.match(/\/\/[^/]+\/([^/]+)/);
    if (pathLocaleMatch && locales.includes(pathLocaleMatch[1])) {
      refererLocale = pathLocaleMatch[1];
    }
  }

  // Fallback to the default locale if no valid locale was found in the Referer
  const effectiveLocale = refererLocale || 'en';

  // Check if the current request's URL already includes a locale
  const hasLocale = locales.some(locale => req.nextUrl.pathname.startsWith(`/${locale}`));
  if (!hasLocale) {
    // If not, redirect to the same URL but with the effective locale prefixed
    const url = req.nextUrl.clone();
    url.pathname = `/${effectiveLocale}${url.pathname}`;
    return NextResponse.redirect(url);
  }

  // Proceed with the next-intl middleware if the locale is already present
  return intlMiddleware(req);
}

export const config = {
  // Match only internationalized pathnames
  matcher: ['/', '/(en|it|de|nl|fr|sv|es|nb|pt|pl)/:path*'],
};

I hope this could resolve your issue in a better approach.

Upvotes: 1

Related Questions