Next.js Discord

Discord Forum

App Router : i18n and Supabase

Unanswered
Pacific herring posted this in #help-forum
Open in Discord
Pacific herringOP
Okay its maybe a dummy question but how can we manage i18n in web app without slug (like spotify, instagram,...) ? I use Supabase for backend so I can save the language preference for each user. But how set the language of the user in the nextjs app ? And how manage the fact that user can be not logged ? Im new with i18n so I have no idea. My first idea is to use React Context, but I guess I need cookies to store the preference of the user (logged and unlogged) ?

36 Replies

Spotify handles content by fetching all of the text through GQL on the client, but best practice with next would be to use domain and/or subpath routing so it's cached on the route segment for better performance
@Marchy Spotify handles content by fetching all of the text through GQL on the client, but best practice with next would be to use domain and/or subpath routing so it's cached on the route segment for better performance
Pacific herringOP
Yeah Im gonna use subpath like [lang]in the doc but in the middlesware make something like, if the user is connected take is favorite language, if not connected, take the one from the Accept-Language header, is a good practice ?
Pacific herringOP
Because im already use middleware to catch user session with Supabase :
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextRequest, NextResponse } from 'next/server'

export async function middleware(req: NextRequest) {
  const res = NextResponse.next()
  const supabase = createMiddlewareClient({ req, res })
  await supabase.auth.getSession()
  return res
}

export const config = {
    matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}

So I guess I can have something with the language
You could overwrite that in middleware based on user settings
@Marchy You could overwrite that in middleware based on user settings
Pacific herringOP
ChatGPT help me to just think about the way of doing and show me this example :
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextRequest, NextResponse } from 'next/server'

export async function middleware(req: NextRequest) {
  const res = NextResponse.next()
  const supabase = createMiddlewareClient({ req, res })
  const session = await supabase.auth.getSession()

  if (session) {
    const userLanguage = await getUserLanguageFromSupabase(session.user.id) // Obtenez la langue de l'utilisateur depuis Supabase
    setLanguageCookie(res, userLanguage)
  } else {
    const acceptLanguage = req.headers.get('Accept-Language')
    const userLanguage = determineUserLanguage(acceptLanguage) // Déterminez la langue préférée en fonction de l'en-tête Accept-Language
    setLanguageCookie(res, userLanguage)
  }

  return res
}

function setLanguageCookie(res, language) {
  res.setHeader('Set-Cookie', `rcmd_locale=${language}; Path=/; HttpOnly; Secure; SameSite=Strict`)
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}
Ive check the cookie from Spotify and they store the sp_locale value for the locale
Pacific herringOP
But for now I have an error in the dictionaries.ts file :
import 'server-only'
 
const dictionaries = {
  en: () => import('./dictionaries/en.json').then((module) => module.default),
  nl: () => import('./dictionaries/fr.json').then((module) => module.default),
}
 
export const getDictionary = async (locale: string | number) => dictionaries[locale]() => ERROR HERE WITH dictionaries[locale] => 

Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type '{ en: () => Promise<{ products: { cart: string; }; }>; nl: () => Promise<{ products: { cart: string; }; }>; }'.
  No index signature with a parameter of type 'string' was found on type '{ en: () => Promise<{ products: { cart: string; }; }>; nl: () => Promise<{ products: { cart: string; }; }>; }
Ive try adding interface like that but not working as well :
interface Dictionary {
  [key: string]: () => Promise<{ [key: string]: string }>
}
The locale gets passed as a param to each page.jsx, it might be easier to just have a content.json in the closest route segment that can be used to manage all the content there
@Marchy actually, you can override this with the NEXT_LOCALE cookie https://nextjs.org/docs/pages/building-your-application/routing/internationalization#leveraging-the-next_locale-cookie
Pacific herringOP
Ohh ! I was thinking about create a new cookies rcmd_locale but if I can override the NEXT_LOCALE its even better
Its the Pages Router, isnt a problem ?
@Pacific herring Its the Pages Router, isnt a problem ?
Actually, maybe? I don't see it in the /app docs :meow_stare:
it'd be worth a try
same difference either way, just a little bit extra in middleware if it's not automatic
@Marchy Actually, maybe? I don't see it in the /app docs <:meow_stare:763826437341839422>
Pacific herringOP
And when u say override NEXT_LOCALE, is only for a specific website right ? Like isnt gonna chance the lang of all the website ?
@Pacific herring And when u say override NEXT_LOCALE, is only for a specific website right ? Like isnt gonna chance the lang of all the website ?
NEXT_LOCALE probably uses the same method as doing it manually in middleware. It would be just for your site, because the cookie is from your domain
it might also just be missing from app docs, in which case I'd be interested if it works so I can add it
Pacific herringOP
Im gonna try this tomorrow, im gonna sleep rn so i'll keep u informed !
THx again for ur help and sry for my dummy questions 😄
@Pacific herring THx again for ur help and sry for my dummy questions 😄
no such thing as a dumb question, next is a 100% different arch pattern so things happen in different places than you might expect lol
Pacific herringOP
ahah yeah probably
Btw to use "server-only" we have to enable experimental feature no ? Because in the /app docs they use server-only for dictionaries.ts
or its maybe "user-server"
@Pacific herring or its maybe `"user-server"`
'use server' , it was server-only at one point :lolsob:
Pacific herringOP
Well maybe I can use the plugin next-intl no ? Because I have some difficulties to setup the middleware ahah
Pacific herringOP
Okay Ive setup the next-intl plugin with server component with this middleware :
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextRequest, NextResponse } from 'next/server'
import createIntlMiddleware from 'next-intl/middleware'

const locales = ['en', 'fr'];

const intlMiddleware = createIntlMiddleware({
  locales,
  defaultLocale: 'en'
})

export async function middleware(req: NextRequest) {
  const res = NextResponse.next()
  const supabase = createMiddlewareClient({ req, res })
  await supabase.auth.getSession()
  return intlMiddleware(req)
}

export const config = {
    matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}
Pacific herringOP
Well no I have to setup my middleware if my user is connected to change the NEXT_LOCALE value
Pacific herringOP
Ive try this :
export async function middleware(req: NextRequest) {
  const res = NextResponse.next()
  const supabase = createMiddlewareClient({ req, res })
  const { data: { session } } = await supabase.auth.getSession();
  const { data: user } = await supabase.from('user').select('*').eq('id', session?.user.id).single() as { data: User }
  if (user) {
    console.log('user', user)
    // Mettre à jour le cookie NEXT_LOCALE avec la langue préférée de l'utilisateur
    res.cookies.set(
      {
        name: 'NEXT_LOCALE',
        value: user.language,
        maxAge: 365 * 24 * 60 * 60, // Durée de validité du cookie (1 an)
        path: '/', // Le chemin du cookie
        sameSite: 'strict',
        priority: 'medium'
    });
  }
  return intlMiddleware(req)
}

But in the cookies tabs nothing is change. Ive tried to create a new cookie with differente name and isnt working too
Okay the problem come from the `return intMiddleware(req)`` because if I do that :
return (res)
// return intlMiddleware(req)

I can override cookies. The problem is intMiddleware use req
Pacific herringOP
I really dont understand with the cookies isnt set with my actual middleware... Because if I change manually the NEXT_LOCALE cookie value in the app tabs the lang change
Pacific herringOP
Pacific herringOP
arrhh doesnt work either
Pacific herringOP
So maybe I have to create a custom intlMiddleware to use the same resvariable ?