DEV Community

Digital dev
Digital dev

Posted on

Migrating a Vite i18n App to Next.js Without Breaking Everything

The Challenge of Framework-Level Localization

When building a Single Page Application (SPA) with Vite, internationalization (i18n) is typically handled entirely on the client side. Libraries like react-i18next or react-intl initialize a singleton instance that fetches translation JSON files and swaps strings dynamically.

However, moving that logic to Next.js introduces a paradigm shift. In Next.js, i18n isn't just about translating text; it's about routing, SEO-friendly URLs (like /en/about or /es/acerca), and Server-Side Rendering (SSR). If you simply copy-paste your Vite setup, you'll lose the benefits of pre-rendered localized content. Here is how to migrate without breaking your architecture.

1. Understanding the Routing Shift

In a Vite app, your i18n logic likely lives in an i18n.ts file and changes state internally without changing the URL path. Next.js expects the locale to be part of the URL.

Before you start moving components, decide between the two main patterns:

  • Sub-path routing: example.com/en/dashboard vs example.com/fr/dashboard.
  • Domain routing: example.com vs example.fr.

For most migrations, sub-path routing is the standard. If you are using the App Router (Next.js 13+), you will need to wrap your routes in a dynamic segment: app/[lng]/layout.tsx.

2. Setting Up the Middleware

Unlike Vite, where you might handle language detection in a useEffect, Next.js uses Middleware to intercept requests. This allows you to detect the user's preferred language via the Accept-Language header and redirect them before the page even loads.

import { NextResponse } from 'next/server'
import acceptLanguage from 'accept-language'
import { fallbackLng, languages } from './app/i18n/settings'

export function middleware(req) {
  let lng
  if (req.cookies.has('i18next')) lng = req.cookies.get('i18next').value
  if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'))
  if (!lng) lng = fallbackLng

  // Redirect if lng in path is not supported
  if (
    !languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
    !req.nextUrl.pathname.startsWith('/_next')
  ) {
    return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url))
  }

  return NextResponse.next()
}
Enter fullscreen mode Exit fullscreen mode

3. Handling Client vs. Server Components

In Vite, every component is a client component. In Next.js, you must distinguish between them.

Server Components

You cannot use hooks like useTranslation() in Server Components. Instead, you'll need to initialize an i18next instance on the server for every request. This ensures that the HTML sent to the browser is already translated, which is vital for SEO.

Client Components

For components that require interactivity (forms, buttons), you can use the familiar hook pattern, but ensure you include the 'use client' directive at the top of the file.

If you find the manual restructuring of these component types and routing logic overwhelming, you can use ViteToNext.AI to automate the heavy lifting of converting your Vite structure into a Next.js-ready architecture.

4. Structuring Translation Files

In Vite, you might have imported JSON files directly:

import en from './locales/en.json';
Enter fullscreen mode Exit fullscreen mode

In Next.js, it is better to load these files dynamically based on the current locale to keep your client-side bundle small. Use the i18next-resources-to-backend plugin to fetch only the necessary namespace for the current page.

5. SEO and Metadata

One of the primary reasons to migrate is SEO. In your layout.tsx, make sure to use the generateMetadata function to set the correct lang attribute and localized titles:

export async function generateMetadata({ params: { lng } }) {
  return {
    title: lng === 'en' ? 'Welcome' : 'Bienvenue',
    description: '...'
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Migrating i18n from Vite to Next.js requires moving away from global client-side state toward URL-driven, server-aware localization. By leveraging Middleware for language detection and properly splitting your logic between Server and Client components, you gain significant performance and SEO advantages.

Start by mapping your routes, move your JSON resources to a shared directory, and implement a robust Middleware to handle the redirects. Your users (and search engines) will thank you.

Further reading: Explore automated migration strategies at vitetonext.codebypaki.online.

Top comments (0)