frontend

Complete Guide to Building Multilingual Sites with Next.js 15

An in-depth exploration of building modern multilingual websites using Next.js 15 App Router, including routing configuration, SEO optimization, and performance best practices.

David Zhang
12/15/2024
8 min read
Next.jsi18nSEOApp Router

Complete Guide to Building Multilingual Sites with Next.js 15

In today's globalized world, building multilingual websites has become an essential skill for modern web development. Next.js 15's App Router provides powerful and flexible solutions for multilingual website development.

Project Initialization

First, let's create a new Next.js 15 project:

npx create-next-app@latest my-multilingual-site
cd my-multilingual-site
npm install next-intl

Internationalization Configuration

1. Create Language Configuration File

Create src/lib/i18n.ts:

export const locales = ['zh', 'en', 'fr', 'de'] as const;
export type Locale = typeof locales[number];

export const defaultLocale: Locale = 'en';

export const localeNames: Record<Locale, string> = {
  zh: '中文',
  en: 'English',
  fr: 'Français',
  de: 'Deutsch'
};

2. Configure Middleware

Create middleware.ts:

import { createMiddleware } from 'next-intl/middleware';
import { locales, defaultLocale } from './src/lib/i18n';

export default createMiddleware({
  locales,
  defaultLocale,
  localePrefix: 'always'
});

export const config = {
  matcher: [
    '/((?!api|_next|_vercel|.*\..*).*)',
    '/([\w-]+)?/users/(.+)'
  ]
};

Route Structure Design

Using App Router's dynamic routing features:

src/app/
├── [locale]/
│   ├── layout.tsx
│   ├── page.tsx
│   ├── about/
│   │   └── page.tsx
│   ├── blog/
│   │   ├── page.tsx
│   │   └── [slug]/
│   │       └── page.tsx
│   └── contact/
│       └── page.tsx
├── globals.css
└── layout.tsx

Translation File Management

Create Translation Files

messages/zh.json:

{
  "navigation": {
    "home": "首页",
    "about": "关于我们",
    "blog": "博客",
    "contact": "联系我们"
  },
  "home": {
    "title": "欢迎来到我们的网站",
    "description": "这是一个多语言网站示例"
  }
}

messages/en.json:

{
  "navigation": {
    "home": "Home",
    "about": "About",
    "blog": "Blog",
    "contact": "Contact"
  },
  "home": {
    "title": "Welcome to Our Website",
    "description": "This is a multilingual website example"
  }
}

Component Internationalization

Create Navigation Component

'use client';

import { useTranslations } from 'next-intl';
import { useParams } from 'next/navigation';
import Link from 'next/link';

export default function Navigation() {
  const t = useTranslations('navigation');
  const params = useParams();
  const locale = params.locale as string;

  const navItems = [
    { key: 'home', href: `/${locale}` },
    { key: 'about', href: `/${locale}/about` },
    { key: 'blog', href: `/${locale}/blog` },
    { key: 'contact', href: `/${locale}/contact` }
  ];

  return (
    <nav className="flex space-x-6">
      {navItems.map(item => (
        <Link
          key={item.key}
          href={item.href}
          className="hover:text-blue-600 transition-colors"
        >
          {t(item.key)}
        </Link>
      ))}
    </nav>
  );
}

SEO Optimization

Multilingual SEO Configuration

import { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';

interface Props {
  params: { locale: string };
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const t = await getTranslations({ locale: params.locale, namespace: 'metadata' });

  return {
    title: t('title'),
    description: t('description'),
    alternates: {
      canonical: `https://example.com/${params.locale}`,
      languages: {
        'zh': 'https://example.com/zh',
        'en': 'https://example.com/en',
        'fr': 'https://example.com/fr',
        'de': 'https://example.com/de'
      }
    },
    openGraph: {
      title: t('title'),
      description: t('description'),
      locale: params.locale,
      alternateLocale: ['zh', 'en', 'fr', 'de'].filter(l => l !== params.locale)
    }
  };
}

Performance Optimization

1. Dynamic Import of Translation Files

async function getMessages(locale: string) {
  try {
    return (await import(`../messages/${locale}.json`)).default;
  } catch (error) {
    return (await import('../messages/en.json')).default;
  }
}

2. Static Generation Optimization

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

Language Switcher

'use client';

import { useParams, usePathname } from 'next/navigation';
import Link from 'next/link';
import { locales, localeNames } from '@/lib/i18n';

export default function LanguageSwitcher() {
  const params = useParams();
  const pathname = usePathname();
  const currentLocale = params.locale as string;

  const getLocalizedPath = (locale: string) => {
    const segments = pathname.split('/');
    segments[1] = locale;
    return segments.join('/');
  };

  return (
    <div className="relative">
      <select
        value={currentLocale}
        onChange={(e) => {
          const newPath = getLocalizedPath(e.target.value);
          window.location.href = newPath;
        }}
        className="bg-white border border-gray-300 rounded px-3 py-1"
      >
        {locales.map(locale => (
          <option key={locale} value={locale}>
            {localeNames[locale]}
          </option>
        ))}
      </select>
    </div>
  );
}

Best Practices

1. Translation Key Naming Conventions

  • Use nested structures to organize translation keys
  • Keep key names concise and descriptive
  • Use consistent naming conventions

2. Content Management

  • Consider using CMS for multilingual content management
  • Implement translation workflows
  • Regularly review and update translations

3. Testing Strategy

// Test multilingual routing
describe('Multilingual Routing', () => {
  test('should redirect to default locale', () => {
    // Test logic
  });

  test('should preserve locale in navigation', () => {
    // Test logic
  });
});

Conclusion

Next.js 15's App Router provides powerful tools and flexible architecture for building multilingual websites. With proper configuration and optimization, we can create high-performance, SEO-friendly multilingual websites.

Key takeaways:

  • Use middleware for language detection and redirection
  • Properly organize translation files and route structure
  • Focus on SEO optimization and performance improvement
  • Establish comprehensive testing and maintenance processes

In the next article, we'll explore how to implement complex content management and dynamic translation features in multilingual websites.

Enjoyed This Article?

Follow our blog for more technical articles and industry insights