i18nexus / next-i18n-router

Next.js App Router internationalized routing and locale detection.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

middleware issue

sahandghorbani opened this issue · comments

before this mathcer
i used to write my middleware like this

import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
export async function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname;
  const token = request.cookies.get('access_token')?.value || '';
  const isPublicPath = path === '/';
  const PUBLIC_FILE = /\.(.*)$/;
  if (
    path.startsWith('/_next') || // exclude Next.js internals
    path.startsWith('/api') || // exclude all API routes
    path.startsWith('/static') || // exclude static files
    PUBLIC_FILE.test(path) // exclude all files in the public folder
  ) {
    return NextResponse.next();
  }

  if (!isPublicPath && !token) {
    return NextResponse.redirect(new URL('/', request.nextUrl));
  }
  if (isPublicPath && token) {
    return NextResponse.redirect(new URL('/bpms/dashboard', request.nextUrl));
  }
}
// If you don't define any matcher, it matches all routes by default


but based on this libeary , adding return i18nRouter(request, i18nConfig) cause project block;
this was not something i made-out; this was best practice that was recommended in vercel github-issue
how can i fix it

This is fine. You just need to return i18nRouter at the end. You will also need to update isPublicPath to check for your other languages. Here's the updated code:

import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import i18nConfig from './i18nConfig';

export async function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname;
  const token = request.cookies.get('access_token')?.value || '';
  const isPublicPath = path === '/' || i18nConfig.locales.includes(path.slice(1))
  const PUBLIC_FILE = /\.(.*)$/;
  if (
    path.startsWith('/_next') || // exclude Next.js internals
    path.startsWith('/api') || // exclude all API routes
    path.startsWith('/static') || // exclude static files
    PUBLIC_FILE.test(path) // exclude all files in the public folder
  ) {
    return NextResponse.next();
  }

  if (!isPublicPath && !token) {
    return NextResponse.redirect(new URL('/', request.nextUrl));
  }
  if (isPublicPath && token) {
    return NextResponse.redirect(new URL('/bpms/dashboard', request.nextUrl));
  }
  
  return i18nRouter(request, i18nConfig)
}

Great! it works!
but still i have question about this library
according to nextjs documentation in 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'

"It is using 'match' and 'Negotiator' for handling locales in middleware, but do you think it can cause problems in the future?"

Glad it works!

The usage of match and Negotiator is pretty minimal, so I think using them is fine here. Although there is a glaring issue with Next's example above that this library also experienced until we added a patch a while back: The match function throws an error if the accept-language isn't a "valid" locale. Sometimes browsers will have accept-language headers of "*". Or sometimes web-crawlers will have something random like "bot" or no language header at all. This would cause Next's example to return a 500 error. We've adjusted for this in this library by catching the error and returning the default locale.

Thank you for your detailed explanation! Your insights was very helpful. I appreciate the time you took to share your knowledge and experiences.