Nuxt v4 i18n language switch with localized URLs

Hi everyone,

I’m working on a Nuxt 4 project and have multiple locales set up.
However, I’m not sure how to extend the language switch in Nuxt.

The URLs work with path targeting (e.g. default: /leistungen, English: /en/services).

But how can I make it switch from /leistungen to /en/services?
The current behavior is switching from /leistungen to /en/leistungen.

Thanks in advance!

Hi @creinelt ,

Nuxt’s built-in i18n (via nuxt/i18n) by default just “prefixes” the path with the locale — so it will always translate /leistungen into /en/leistungen unless you explicitly map that route to its English equivalent.

To fix this, you need to use the pages.json route mapping or the locales.routes configuration in nuxt.config.ts (depending on how you’ve set up i18n). That’s where you define per-locale slugs for the same page.

Example: Defining localized paths

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n'],
  i18n: {
    locales: [
      {
        code: 'de',
        iso: 'de-DE',
        file: 'de.json'
      },
      {
        code: 'en',
        iso: 'en-US',
        file: 'en.json'
      }
    ],
    defaultLocale: 'de',
    strategy: 'prefix',
    pages: {
      // map German slug to English slug
      leistungen: {
        de: '/leistungen',
        en: '/services'
      }
    }
  }
})

With this setup:

  • Visiting /leistungen in German → /leistungen.

  • Switching locale → /en/services (instead of /en/leistungen).

Usage in template

When you use the Nuxt i18n link helpers:

<template>
  <nuxt-link :to="switchLocalePath('en')">
    English
  </nuxt-link>
</template>

It will automatically resolve to /en/services because of the mapping above.

Please note you need to tell Nuxt i18n how slugs differ between locales. By default, it assumes they’re identical.

1 Like

Thank you very much, but with this solution, the content manager cannot do this. It is extremely cumbersome if a developer has to be called in every time new pages are created.

I have found another (automatic) solution where I use the setI18nParams composable.