Back to blog
#nuxt #i18n #seo #vue #web-performance

How to Build a Lightning-Fast Multilingual Website with Nuxt 4 & Static Prerendering

Adam Abdel-Djamal 7 min read

Front-end developers looking for production-ready internationalization (@nuxtjs/i18n), SEO best practices, and 100 Lighthouse scores.

Building a multilingual website often turns into a performance and SEO compromise: slow dynamic routing, duplicated canonical URLs, broken browser redirect cookies, and plummeting Lighthouse scores.

When building this portfolio, my goal was strict: 5 languages (EN, FR, DE, ES, JA), sub-50ms static load times, perfect hreflang sitemaps, and 100/100 Lighthouse scores.

In this guide, I share the exact configuration that achieves zero-runtime static prerendering with Nuxt 4 and @nuxtjs/i18n.


1. The Core Routing Strategy: prefix_except_default

By default, multilingual setups often create redundant redirects for the root URL (/ -> /en), adding a costly 200ms TTFB penalty.

The optimal strategy is prefix_except_default:

  • English (default): /, /portfolio, /blog
  • French: /fr, /fr/portfolio, /fr/blog
  • German: /de, /de/portfolio, /de/blog
  • Spanish: /es, /es/portfolio, /es/blog
  • Japanese: /ja, /ja/portfolio, /ja/blog
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n', '@nuxtjs/sitemap'],

  i18n: {
    langDir: 'locales/',
    defaultLocale: 'en',
    strategy: 'prefix_except_default',
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n_redirected',
      redirectOn: 'root',
      alwaysRedirect: false,
      fallbackLocale: 'en',
    },
    locales: [
      { code: 'en', language: 'en-US', file: 'en.json', name: 'English' },
      { code: 'fr', language: 'fr-FR', file: 'fr.json', name: 'Français' },
      { code: 'de', language: 'de-DE', file: 'de.json', name: 'Deutsch' },
      { code: 'es', language: 'es-ES', file: 'es.json', name: 'Español' },
      { code: 'ja', language: 'ja-JP', file: 'ja.json', name: '日本語' },
    ],
  },
})

Never hardcode internal navigation links (<NuxtLink to="/contact">). If a visitor on the French version (/fr) clicks an unlocalized link, they are forcefully knocked back to the English version.

Always wrap navigation targets in localePath():

<template>
  <NuxtLink :to="localePath('/contact')">
    {{ t('nav.contact') }}
  </NuxtLink>
</template>

<script setup lang="ts">
const localePath = useLocalePath()
const { t } = useI18n()
</script>

For Google to rank your pages in target countries without duplicate content penalties, your sitemap must declare hreflang alternate links.

@nuxtjs/sitemap pairs with @nuxtjs/i18n to generate this automatically:

<url>
  <loc>https://adam-portfolio.vercel.app/portfolio</loc>
  <xhtml:link rel="alternate" hreflang="en-US" href="https://adam-portfolio.vercel.app/portfolio" />
  <xhtml:link rel="alternate" hreflang="fr-FR" href="https://adam-portfolio.vercel.app/fr/portfolio" />
  <xhtml:link rel="alternate" hreflang="de-DE" href="https://adam-portfolio.vercel.app/de/portfolio" />
  <xhtml:link rel="alternate" hreflang="es-ES" href="https://adam-portfolio.vercel.app/es/portfolio" />
  <xhtml:link rel="alternate" hreflang="ja-JP" href="https://adam-portfolio.vercel.app/ja/portfolio" />
</url>

4. Prerendering the Entire Site with Nitro

To achieve instant delivery, configure Nitro to crawl and generate static HTML files at build time:

nitro: {
  prerender: {
    crawlLinks: true,
    routes: ['/robots.txt', '/sitemap.xml'],
  },
}

Running npm run build generates 100% pre-compiled HTML into .vercel/output/static. The site deploys to Vercel's Edge Network with global sub-30ms response times.

Share this article

AA

Adam Abdel-Djamal

Full Stack Developer

Adam Abdel-Djamal is a Full-Stack & AI Engineer crafting high-performance web applications with Nuxt, Vue, Laravel, and autonomous AI agents.

Related Articles

Adam Abdel-Djamal

Full Stack Web Developer

We use cookies to remember your language preference and measure anonymous traffic. Privacy Policy