EnglishAdvanced GuidesManual i18n Setup

Manual i18n with Static Export

Next.js’s standard Internationalized Routing does not support output: 'export', which is required for hosting on Cloudflare Pages (Static). This guide explains how to implement multilingual support manually.

Directory Structure

Instead of relying on Next.js routing, we physically separate content into language folders.

pages/
├── index.mdx       # Root redirect
├── _meta.js        # Root navigation config
├── en/             # English content root
│   ├── index.mdx
│   └── ...
└── ja/             # Japanese content root
    ├── index.mdx
    └── ...

1. Root Configuration

Root Redirect (pages/index.mdx)

Create a root page that immediately redirects to your default language (e.g., English).

pages/index.mdx
import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function Index() {
  const router = useRouter()
  useEffect(() => {
    router.replace('/en')
  }, [])
  return null
}

Root Meta (pages/_meta.js)

Define your language folders as top-level pages. Do not use display: 'hidden' here, as it may hide the sidebar navigation entirely.

pages/_meta.js
export default {
  "index": {
    "display": "hidden" // Hide the redirect page itself
  },
  "en": {
    "title": "English",
    "type": "page"
  },
  "ja": {
    "title": "日本語",
    "type": "page"
  }
}

2. Language Switcher

Since we are using custom paths, we need a custom language switcher component in theme.config.tsx.

theme.config.tsx
import { useRouter } from 'next/router'
 
const LanguageSwitch = () => {
    const { asPath } = useRouter()
    const isJa = asPath.startsWith('/ja')
    
    // Toggle logic: /ja/foo -> /en/foo, /en/foo -> /ja/foo
    // Handle root case defaulting to /en
    const currentPath = asPath === '/' ? '/en' : asPath
    const targetPath = isJa 
        ? currentPath.replace('/ja', '/en')
        : currentPath.replace('/en', '/ja')
 
    return (
        <a href={targetPath}>
            {isJa ? 'English' : '日本語'}
        </a>
    )
}
 
const config = {
    // ...
    navbar: {
        extraContent: <LanguageSwitch />
    }
}

Benefits

  • Fully Static: Works perfectly with Cloudflare Pages and next export.
  • Isolated Navigation: Users viewing English docs won’t see Japanese page titles in the sidebar, and vice versa.
  • Simple: No complex middleware or edge functions required.