-
-
Notifications
You must be signed in to change notification settings - Fork 233
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
Option to not have the locale anywhere in the URL #366
Comments
Thank you for the feature proposal! I do think for internal apps where SEO is not a concern, there's an opportunity for a Moving routes into a Are you interested in contributing this feature? The relevant code is here:
Would be happy to review a PR! |
Thanks for the quick response @amannn
Why is it still necessary to have the routes in a Let me see if I can indeed contribute this feature. Thanks for pointing to the relevant code. |
@boris-arkenaar would you mind sharing your custom middleware? I'm encountering the same issues:
Currently, I'm using another library for a full-authenticated app with a lot of middleware conditions (for authorization, old browsers detections and other stuff) and to make i18n work I simply have to add a language cookie based of The current middleware implementation is difficult to extend. In my case in my middleware I'd like to:
But the example with Having the |
Soooo actually it's super easy, I just set a cookie |
Having the locale available as a segment in the URL enables static rendering of different language versions of your app (works in the latest stable version for Client Components and is on the roadmap for the RSC beta). Furthermore, the locale that is received in layouts and pages can also be used for the Metadata & Route Handler API. I understand that it might feel unnecessary to require the
That's true, this workaround will work currently, because There will likely be an upgrade path if the cookie solution works for you currently, but Hope this helps!
Fantastic, let me know if I can help with something! I've just migrated the test runner to vitest this week, should be more fun to work with tests now! |
Most of it is copy-paste from this library.
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
import { NextResponse } from 'next/server';
import Negotiator from 'negotiator';
import { match } from '@formatjs/intl-localematcher';
import type { NextRequest } from 'next/server';
const COOKIE_LOCALE_NAME = 'NEXT_LOCALE';
const defaultLocale = 'en';
const locales = ['en', 'nl'];
function getAcceptLanguageLocale(
requestHeaders: Headers,
locales: Array<string>,
defaultLocale: string
) {
let locale;
const languages = new Negotiator({
headers: {
'accept-language': requestHeaders.get('accept-language') || undefined,
},
}).languages();
try {
locale = match(languages, locales, defaultLocale);
} catch (e) {
// Invalid language
}
return locale;
}
function resolveLocale(
locales: Array<string>,
defaultLocale: string,
requestHeaders: Headers,
requestCookies: RequestCookies
) {
let locale;
// Prio 1: Use existing cookie
if (requestCookies) {
if (requestCookies.has(COOKIE_LOCALE_NAME)) {
const value = requestCookies.get(COOKIE_LOCALE_NAME)?.value;
if (value && locales.includes(value)) {
locale = value;
}
}
}
// Prio 2: Use the `accept-language` header
if (!locale && requestHeaders) {
locale = getAcceptLanguageLocale(requestHeaders, locales, defaultLocale);
}
// Prio 3: Use default locale
if (!locale) {
locale = defaultLocale;
}
return locale;
}
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const locale = resolveLocale(
locales,
defaultLocale,
req.headers,
req.cookies
);
const hasOutdatedCookie =
req.cookies.get(COOKIE_LOCALE_NAME)?.value !== locale;
if (hasOutdatedCookie) {
res.cookies.set(COOKIE_LOCALE_NAME, locale, {
sameSite: 'strict',
});
}
res.headers.set('x-my-locale', locale);
return res;
}
export const config = {
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
};
import { NextIntlClientProvider } from 'next-intl';
import { headers } from 'next/headers';
import { notFound } from 'next/navigation';
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const locale = headers().get('x-my-locale') || 'en';
let messages;
try {
messages = (await import(`../messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
return (
<html lang={locale}>
<body>
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
} |
@amannn Hello, Thanks in advance! |
@kylemorena I'd kindly ask you to not hijack issues for usage questions. This question was asked here before, hope this helps: #329. I might include docs for this in the future, as the question has come up a few times before (but is ultimately more related to Next.js itself than next-intl). |
@amannn Thank you for your prompt response. |
…is-arkenaar) fixes #366 --------- Co-authored-by: Jan Amann <[email protected]>
Thank you @amannn and @boris-arkenaar for the amazing work! Would love if this option was available in #149! Can I help in any way? |
@V1RE I'll rebase the RSC branch and publish a new beta—should be ready in a bit! |
|
Maybe this can be helpful to some of you! |
Is your feature request related to a problem? Please describe.
As I understand the only two options next-intl currently provides is having the locale either as a prefix of the path, or as a subdomain. I'd like to use this library for a large application with (almost) everything behind authentication. The end user will have the option to choose their preferred language to use the application in, but I don't like and don't need the locale to show up in the URL.
Describe the solution you'd like
I would like to:
[locale]
directoryapp/layout.tsx
, so I can pass it on toNextIntlClientProvider
, or maybeNextIntlClientProvider
can retrieve the locale internally.Describe alternatives you've considered
I've actually written my own middleware. Copy and pasting the bits of code from this library that I needed, leaving out the parts that didn't suit my use case. At the end, I both save the locale in a cookie and in a custom response header.
In the
app/layout.tsx
I retrieve the current locale from the custom response header (couldn't make it work to read the cookie), and passed the value on to theNextIntlClientProvider
.I am using this library in a Next13 app with the app router. So far I've only got it set up for client components. I have looked at the setup needed for server components, but couldn't figure out how to avoid having the locale in the URL.
So, I can use your library now, with my middleware setup myself. I would love to see this option appear and be able to set things up more properly and start to make use of server components as well.
Thanks for this beautiful library!
The text was updated successfully, but these errors were encountered: