Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Internationalization middleware.js example does not work #58556

Open
kaganAhmetOkan opened this issue Nov 16, 2023 · 2 comments
Open

Docs: Internationalization middleware.js example does not work #58556

kaganAhmetOkan opened this issue Nov 16, 2023 · 2 comments
Labels
Documentation Related to Next.js' official documentation.

Comments

@kaganAhmetOkan
Copy link

What is the improvement or update you wish to see?

Example from https://nextjs.org/docs/app/building-your-application/routing/internationalization

import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
 
let headers = { 'accept-language': 'en-US,en;q=0.5' }
let languages = new Negotiator({ headers }).languages()
let locales = ['en-US', 'nl-NL', 'nl']
let defaultLocale = 'en-US'
 
match(languages, locales, defaultLocale) // -> 'en-US'

Example won't be working because Negotiator will not recognize the NextRequest object that the middleware passes in the next example.

Next example:

let locales = ['en-US', 'nl-NL', 'nl']
 
// Get the preferred locale, similar to the above or using a library
function getLocale(request) { ... }
 
export function middleware(request) {
  // Check if there is any supported locale in the pathname
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )
 
  if (pathnameHasLocale) return
 
  // Redirect if there is no locale
  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  // e.g. incoming request is /products
  // The new URL is now /en-US/products
  return Response.redirect(request.nextUrl)
}
 
export const config = {
  matcher: [
    // Skip all internal paths (_next)
    '/((?!_next).*)',
    // Optional: only run on root (/) URL
    // '/'
  ],
}

Comment from Negotiator's creator: jshttp/negotiator#65 (comment)
Yes, I know the second example says getLocale() is "similar" to above code and not exactly the same. But it would be nice if both examples could work with each other.

Is there any context that might help us understand?

This is the function I wrote using the docs:

import { match } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";

export default function getLocale(request, locales) {
  const { headers } = request;
  const languages = new Negotiator({ headers }).languages(); // returns ["*"] causing match() to throw an error
  const defaultLocale = "en";

  return match(languages, locales, defaultLocale); // throws error "Incorrect locale information provided"
};

The request param is the request object given by the middleware and the locales is an array of locales.

new Negotiator({ headers }).languages() returns ["*"]. Which causes the match function to throw this error:

 ⨯ node_modules/@formatjs/intl-localematcher/lib/abstract/CanonicalizeLocaleList.js (7:16) @ getCanonicalLocales
 ⨯ Incorrect locale information provided

The middleware:

import getLocale from "./lib/getLocale";

const locales = ["en", "tr"];

export function middleware(request) {
  const { pathname } = request.nextUrl;
  const pathnameHasLocale = locales.some(
    locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (pathnameHasLocale) return;

  const locale = getLocale(request, locales); // returns null
  request.nextUrl.pathname = `/${locale}${pathname}`;
  return Response.redirect(request.nextUrl);
};

export const config = {
  matcher: ["/((?!_next).*)"],
};

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/pages/building-your-application/routing/internationalization

@kaganAhmetOkan kaganAhmetOkan added the Documentation Related to Next.js' official documentation. label Nov 16, 2023
@pier7529
Copy link

pier7529 commented Jan 1, 2024

Same issue in v14.0.4
(with default locale defined as 'fr')

probably fixed with this answer :
#https://stackoverflow.com/questions/76447732/nextjs-13-i18n-incorrect-locale-information-provided

@nhuethmayr
Copy link

This should do the trick

// Negotiator expects plain object so we need to transform headers
const negotiatorHeaders: Record<string, string> = {};
request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));

// @ts-ignore locales are readonly
const locales: string[] = i18n.locales;

// Use negotiator and intl-localematcher to get best locale
let languages = new Negotiator({ headers: negotiatorHeaders }).languages(
  locales,
);

Taken from: https://github.com/vercel/next.js/blob/canary/examples/app-dir-i18n-routing/middleware.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation Related to Next.js' official documentation.
Projects
None yet
Development

No branches or pull requests

3 participants