Ensuring Language is Preserved in URLs in Next.js with Sitecore XM Cloud
Author
Roberto Armas
Date Published

You might have noticed that when setting up URLs in Sitecore, the language is not always included in the URL—even if you’ve enabled languageEmbedding in the SXA Site settings.
Luckily, there’s a fix for this.
I followed the Next.js i18n routing guide, which recommends explicitly adding the “default” locale to your next.config.js. This is key to ensuring that language prefixes are consistently applied.
Here’s how your config should look:
1// next.config.js2module.exports = {3 i18n: {4 locales: ['default', 'en', 'de', 'fr'], // Add 'default' intentionally5 defaultLocale: 'en',6 },7 trailingSlash: true,8}
After updating your config, you’ll likely need to implement custom middleware to handle redirection when the locale is detected as default.
Here’s an example of a middleware plugin I implemented:
1import { NextRequest, NextResponse } from 'next/server';2import { MiddlewarePlugin } from '..';3import config from 'temp/config';45const PUBLIC_FILE = /\.([^.]+)$/;67class MultilanguagePlugin implements MiddlewarePlugin {8 // Ensure this plugin runs early in the middleware chain9 order = 0.1;1011 async exec(req: NextRequest, res: NextResponse): Promise<NextResponse> {12 if (13 !(14 req.nextUrl.pathname.startsWith('/_next') ||15 req.nextUrl.pathname.includes('/api/') ||16 req.nextUrl.pathname.includes('/robots.txt') ||17 req.nextUrl.pathname.includes('/sitemap.xml') ||18 req.nextUrl.pathname.includes('/sitemapindex.xml') ||19 PUBLIC_FILE.test(req.nextUrl.pathname)20 )21 ) {22 if (req.nextUrl.locale === 'default') {23 const locale = req.cookies.get('NEXT_LOCALE')?.value || config.defaultLanguage;24 return NextResponse.redirect(25 new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url),26 30127 );28 }29 }3031 return res;32 }33}3435export const multilanguagePlugin = new MultilanguagePlugin();
What this middleware does:
If the locale in the URL is default, it will look for a NEXT_LOCALE cookie or fall back to your configured default language. It then performs a 301 redirect to the correct language-prefixed path (e.g., /en/about instead of just /about).
This approach ensures that all visitors—regardless of whether they hit your site directly or via internal navigation—are consistently served URLs with the correct language code.
Patching the Link Resolver in Sitecore
To ensure that URLs generated within Sitecore always include the language code, I also patched the linkManager configuration by setting languageEmbedding="always".
1<linkManager>2 <providers>3 <add name="myLinkProvider"4 type="itecore.XA.Foundation.Multisite.LinkManager.LocalizableLinkProvider, Sitecore.XA.Foundation.Multisite"5 cacheExpiration="5"6 addAspxExtension="false"7 alwaysIncludeServerUrl="false"8 encodeNames="true"9 languageEmbedding="always"10 languageLocation="filePath"11 lowercaseUrls="true"12 shortenUrls="true"13 useDisplayName="false" />1415 <add name="mySitemapLinkProvider"16 type="itecore.XA.Foundation.Multisite.LinkManager.LocalizableLinkProvider, Sitecore.XA.Foundation.Multisite"17 cacheExpiration="5"18 addAspxExtension="false"19 alwaysIncludeServerUrl="true"20 encodeNames="true"21 languageEmbedding="always"22 languageLocation="filePath"23 lowercaseUrls="true"24 shortenUrls="true"25 useDisplayName="false" />26 </providers>27</linkManager>
By setting languageEmbedding="always"
, you enforce consistent language-prefixed URLs across all link generation in Sitecore, whether for internal navigation or sitemap XMLs.
Final Thoughts
Combining this Sitecore configuration with the Next.js i18n setup and middleware ensures that your multilingual site always respects language-specific routing—boosting SEO, improving UX, and keeping everything nice and consistent.