Closed Kinbaum closed 2 weeks ago
Thanks for raising this!
As you've correctly mentioned, this can currently be implemented by composing the middleware:
import createIntlMiddleware from 'next-intl/middleware';
import {NextRequest} from 'next/server';
export default async function middleware(request: NextRequest) {
const handleI18nRouting = createIntlMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en'
});
const response = handleI18nRouting(request);
if (response.cookies.get('NEXT_LOCALE')) {
response.cookies.delete('NEXT_LOCALE');
}
return response;
}
export const config = {
matcher: ['/((?!_next|.*\\..*).*)']
};
In case there's a growing demand for this, I think we could consider built-in support. I haven't yet thought out all implications this might have though (e.g. we need additional guardrails when someone tries to use this together with localePrefix: 'never'
).
the delete next locale will continue to send the cookie empty header. Would be nice to have the opt out feature.
Setting the NEXT_LOCALE
cookie on the response will now be disabled if localeDetection: false
is set as of next-intl@3.1.3
(see https://github.com/amannn/next-intl/pull/654).
@amannn I'm using this workaround from your earlier comment:
if (response.cookies.get('NEXT_LOCALE')) {
response.cookies.delete('NEXT_LOCALE');
}
This full example seems to work well so far:
import createIntlMiddleware from 'next-intl/middleware';
import {NextRequest} from 'next/server';
export default async function middleware(request: NextRequest) {
const handleI18nRouting = createIntlMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en',
localePrefix: 'never'
});
const response = handleI18nRouting(request);
if (response.cookies.get('NEXT_LOCALE')) {
response.cookies.delete('NEXT_LOCALE');
}
return response;
}
export const config = {
matcher: ['/((?!_next|.*\\..*).*)']
};
I'm also using localePrefix: 'never'
. You mention needing additional guard rails when using this option, could you please elaborate on what those are? For context, I just want the accept-language
header to determine the correct locale.
I needed to disabled the cookie detection as well. You workaround works but it is kind of ugly.
a simple localeCookieDetection: false would be great =)
Agreed with @sysyamer here, the change in https://github.com/amannn/next-intl/pull/654 is great if you want to opt out of detection entirely. However it doesn't give an option to continue using locale detection via accept-language
header, but not use the NEXT_LOCALE
cookie.
The middleware workaround provided works, but feels like a bit of a hack. It would be awesome if there was an option in the config for something like
// Use the browser `accept-language` header to determine locale
localeDetection: true,
// Don't set the `NEXT_LOCALE` cookie
localeCookie: false
@amannn would you be open to a PR of a change like this? I would be willing to submit one if you think this is a potentially useful change.
@loudwinston Can you share what your use case is for disabling the cookie but keeping accept-language
?
My main concern currently is that there can be many combinations, so a large question would be the right API:
E.g.:
// Allow for complete customization, but users might have to reimplement parts of the
// `next-intl` middleware if e.g. only the cookie management needs to be adapted
localeDetection(request) {
return locale;
}
// Isn't quite correct, a prefix or domain will still be taken into account
// (that's arguably already the case with `localeDetection: false`)
localeDetection: 'accept-language-only'
// Allows more customization, but e.g. not a custom cookie name
localeDetection: ['prefix', 'accept-language']
It seems to me like this goes down a rabbit hole, where it's likely that there might still be a use case that's not ideally supported. Currently we take a very high-level approach, where it's basically an all-or-nothing and customization can be added before/after the middleware has run. This gives users the most control, but I do agree it can feel a bit hacky to delete a cookie after it was configured on the response.
Additionally, it seems like there could be changes to the middleware in Next.js. In any case I'd wait a bit to see what changes are coming here before we change things too much.
So for now I'd be happy to hear more about use cases, so please let me know!
@loudwinston Can you share what your use case is for disabling the cookie but keeping
accept-language
?My main concern currently is that there can be many combinations, so a large question would be the right API:
E.g.:
// Allow for complete customization, but users might have to reimplement parts of the // `next-intl` middleware if e.g. only the cookie management needs to be adapted localeDetection(request) { return locale; } // Isn't quite correct, a prefix or domain will still be taken into account // (that's arguably already the case with `localeDetection: false`) localeDetection: 'accept-language-only' // Allows more customization, but e.g. not a custom cookie name localeDetection: ['prefix', 'accept-language']
It seems to me like this goes down a rabbit hole, where it's likely that there might still be a use case that's not ideally supported. Currently we take a very high-level approach, where it's basically an all-or-nothing and customization can be added before/after the middleware has run. This gives users the most control, but I do agree it can feel a bit hacky to delete a cookie after it was configured on the response.
Additionally, it seems like there could be changes to the middleware in Next.js. In any case I'd wait a bit to see what changes are coming here before we change things too much.
So for now I'd be happy to hear more about use cases, so please let me know!
@amannn I guess it's the same issue I have. This is how I would imagine the lang detection algorithm to work:
if the path contains a locale prefix eg. /en/some-website /de/some-website /pl/some-website then it should prioritize the language that is inside the pathname.
if the user didn't allow cookies yet and the url doesn't have a lang prefix eg. / /some-website then we should look at the 'Accept-Language' header.
if the user allows for cookies, we first check the pathname, then if there was a cookie with the preferred language and last we look at the 'Accept-Language' header. Checking if user allows cookies or not should be on our side.
it's all about the compliance with GDPR.
it's all about the compliance with GDPR.
@damian-balas As far as I'm informed, a cookie that remembers the language preference of the user for a multilingual site falls into the category of "functionality cookies", which do not require explicit consent.
E.g. imagine this workflow:
/
and is redirected based on the accept-language
header to /en
accept-language
header was off due to using a friends computer) and therefore switches to /es
/
to /en
againI'd argue this may appear as a broken website, the user's expectation would be that the language preference is remembered.
Cookies that are necessary for a website to function correctly don't need to be asked for opt-in with a cookie banner but should be covered in the privacy policy of your website.
I haven't confirmed this with an expert though, so take this with a grain of salt. Would be happy to discuss if someone has a different opinion!
it's all about the compliance with GDPR.
@damian-balas As far as I'm informed, a cookie that remembers the language preference of the user for a multilingual site falls into the category of "functionality cookies", which do not require explicit consent.
E.g. imagine this workflow:
- A user visits
/
and is redirected based on theaccept-language
header to/en
- The user wants to change the locale (e.g. because the
accept-language
header was off due to using a friends computer) and therefore switches to/es
- The user opens a second instance of the website (e.g. to compare products) and is redirected from
/
to/en
againI'd argue this may appear as a broken website, the user's expectation would be that the language preference is remembered.
Cookies that are necessary for a website to function correctly don't need to be asked for opt-in with a cookie banner but should be covered in the privacy policy of your website.
I haven't confirmed this with an expert though, so take this with a grain of salt. Would be happy to discuss if someone has a different opinion!
I don't this a language preference cookie is necessary for a multi lang website. The website would still work without it. It's more a convenience than a requirement. Check out what Cookiebot has to say about this.
Hmm, on a second thought, you might be right. I've asked the StackExchange Webmasters community if someone is familiar with the topic and can provide clarification: Does saving the language preference of the user in a cookie require a cookie banner?
If yes, I agree and we should rather sooner than later support this use case better with the next-intl
middleware.
I'll reopen this issue for the time being.
@damian-balas I think I came to a conclusion here.
The Article 29 Working Party opinion 04/2012 provides clear guidelines for this use case (see section 3.6 "UI customization cookies"):
In contrast, what next-intl
currently does is:
NEXT_LOCALE
cookie is always set, even if the negotiated locale matches the accept-language
header (the only way to opt-out is localeDetection: false
)I'd really like next-intl
to be GDPR-compliant out-of-the-box and clearly state this, so that there's no detective work necessary on the end user side.
An action plan could be:
NEXT_LOCALE
when the user requests a localized version that doesn't match the accept-language
header (typically when a locale switcher is used). An edge case could be if someone receives a link that would open a site with a locale prefix that doesn't match the accept-language
header of the user. Is that an explicit locale change? Not sure. An alternative would be to avoid setting the cookie in general and let the user handle this.
There's also the use case of localePrefix: 'never'
with multiple locales being used on the same host. Here, the cookie is really vital and should have a maximum expiration (and might require consent).
I'll add this topic to the list in https://github.com/amannn/next-intl/issues/779 as this will require a breaking change in any case.
As a temporary workaround to modify the cookie, you can use something like this in the middleware:
import {NextRequest} from 'next/server';
import createMiddleware from 'next-intl/middleware';
const handleI18nRouting = createMiddleware(/* ... */);
export default function middleware(request: NextRequest) {
const response = handleI18nRouting(request);
if (response.cookies.get('NEXT_LOCALE')) {
response.cookies.set(
'NEXT_LOCALE',
response.cookies.get('NEXT_LOCALE').value,
{
// Change cookie options
}
);
}
return response;
}
@amannn Apologies for the late reply here, I've been quite busy getting a new application released (using next-intl
successfully!) and haven't had time to respond.
I think my use case similar to other folks in this thread
We don't currently allow users to choose a different language than what we detect from their browser settings. Understood that this is best solved by the use of cookies - when we introduce this feature we'll be ensuring GDPR compliance.
Update: A new localeCookie
option for the middleware has been added in https://github.com/amannn/next-intl/pull/1414 that should help with the use cases discussed in this thread. The feature is available in the latest canary release.
A bit further down the road, as part of the next major release, I'm planning to decrease the cookie expiration in relation to https://github.com/amannn/next-intl/issues/454#issuecomment-2238885859.
Edit: I noticed there's a bug in the current implementation of the localeCookie
feature, need to have another look in https://github.com/amannn/next-intl/pull/1417.
Re https://github.com/amannn/next-intl/issues/454#issuecomment-2405449355: Ok, a new canary
is out that addresses the bug I've noticed. A small API change was necessary: the localeCookie
option was moved from the second argument of the middleware to defineRouting
(or just the first parameter of the middleware). However, the previous API is still supported, but now deprecated.
The proposed docs have been updated accordingly.
Is your feature request related to a problem? Please describe.
Similar to #366, but I would like to have the option to disable the use of cookies for the locale.
Describe the solution you'd like
Adding an option to
createMiddleware
innext-intl
to disable the use of cookies for locale and derive the value only from the Accept-Language header or URL itself could have several benefits:Describe alternatives you've considered
Attempt to delete the cookie myself in the middleware with each request.