zudo-doc
GitHub repository

Type to search...

to open search from anywhere

Internationalization (i18n)

Created Mar 11, 2026Updated Jun 14, 2026Takeshi Takatsudo
Tags:#i18n

Add multi-language support to your documentation

zudo-doc supports multiple languages using zfb's i18n routing.

How It Works

  • English (default): /docs/... — content lives in src/content/docs/

  • Japanese: /ja/docs/... — content lives in src/content/docs-ja/

The language switcher in the site header lets users toggle between available languages.

Settings-Based Locale Configuration

Locales are configured in src/config/settings.ts:

export const settings = {
  // ...
  locales: {
    ja: { label: "JA", dir: "src/content/docs-ja" },
  },
};

For each locale entry, zudo-doc automatically:

  • Creates a zfb content collection (docs-{code})

  • Registers the locale in zfb's i18n routing

  • Generates the appropriate page routes

  • Adds the locale to the language switcher

This means adding a new locale only requires a settings entry and a content directory — no manual collection or route setup needed.

Directory Structure

Localized docs mirror the English directory structure:

src/content/
├── docs/
│   ├── getting-started/
│   │   ├── introduction.mdx
│   │   └── installation.mdx
│   └── guides/
│       └── configuration.mdx
└── docs-ja/
    ├── getting-started/
    │   ├── introduction.mdx
    │   └── installation.mdx
    └── guides/
        └── configuration.mdx

Language Switcher

The language switcher appears in the header's right edge. It shows the current language code (EN or JA) and a link to switch to the alternate language. The switcher automatically computes the alternate URL by adding or removing the /ja/ prefix.

UI Translation Keys

zudo-doc uses a translation key system for built-in UI strings. Keys are organized by namespace:

  • nav.* — Header navigation labels (e.g., nav.gettingStarted, nav.guides)

  • toc.* — Table of contents labels

  • search.* — Search dialog text

  • code.* — Code block UI (copy button, etc.)

  • doc.* — Document-level UI (edit link, metadata labels, etc.)

These keys allow the entire UI (not just content) to be localized across all configured languages.

zfb Configuration

The locale list flows through zfb.config.ts. Each entry in settings.locales becomes a docs-{code} content collection automatically — there is no separate i18n block to maintain. Page routes under pages/[locale]/docs/[...slug].tsx enumerate locales via their paths() export, so English pages are served without a language prefix (/docs/...) while non-default locales use their code as the prefix (/ja/docs/...).

Note

The locale list in zfb.config.ts is automatically derived from settings.locales, so you only need to maintain locales in one place.

Adding a New Language

To add a new language (e.g., Korean):

  1. Add the locale to settings.ts:

    locales: {
      ja: { label: "JA", dir: "src/content/docs-ja" },
      ko: { label: "KO", dir: "src/content/docs-ko" },
    },
  2. Create a content directory: src/content/docs-ko/

  3. Mirror the English directory structure with translated files

  4. Add UI translations for the new locale

Tip

Step 1 is the key step — adding the locale to settings.ts automatically creates the content collection and registers the i18n routing. No per-locale page route file is needed; the parameterized route at pages/[locale]/docs/[[...slug]].tsx handles every locale automatically.

defaultLocale

defaultLocale identifies which locale is served without a URL prefix. It defaults to "en" and must match one of the keys that zfb uses for its default-locale routing:

export const settings = {
  // ...
  defaultLocale: "en" as const,
  locales: {
    ja: { label: "JA", dir: "src/content/docs-ja" },
  },
};

With prefixDefaultLocale: false (configured in zfb.config.ts), the default locale is served at /docs/... with no language code in the path, while every other locale uses its key as a prefix — /ja/docs/... for Japanese. Changing defaultLocale requires updating the corresponding routing and collection wiring in zfb.config.ts.

Default-locale-only prefixes

Some content is intentionally only available in the default locale. These routes are called default-locale-only prefixes and are controlled by the defaultLocaleOnlyPrefixes setting in src/config/settings.ts:

export const settings = {
  // ...
  defaultLocaleOnlyPrefixes: [
    "/docs/claude-md/",
    "/docs/claude-skills/",
    "/docs/claude-agents/",
    "/docs/claude-commands/",
  ] as string[],
};

What it means

Any page whose URL starts with one of these prefixes exists only in the default locale (English). The language switcher suppresses the alternate-locale link for those pages, and no Japanese mirror file should be created under docs-ja/ for them.

Current entries

The four prefixes above cover the Claude-resource sections — the auto-generated pages that surface CLAUDE.md, skills, agents, and commands from the project's .claude/ directory. These are English-only because the source material (Claude config files) is not translated.

Note

The top-level /docs/claude/ index page is not in this list — it has a bilingual stub at docs-ja/claude/index.mdx. Only the four deeper prefixes (claude-md/, claude-skills/, claude-agents/, claude-commands/) are default-locale-only.

How the router enforces this

The page route at pages/[locale]/docs/[[...slug]].tsx checks each slug against defaultLocaleOnlyPrefixes in its paths() export. If the reconstructed URL matches a prefix, that path is omitted from the non-default-locale path list, so no /ja/docs/claude-md/... route is ever generated.

Adding your own prefix

If you have a section that should remain English-only (for example, auto-generated API reference pages), add its URL prefix to the array:

defaultLocaleOnlyPrefixes: [
  "/docs/claude-md/",
  "/docs/claude-skills/",
  "/docs/claude-agents/",
  "/docs/claude-commands/",
  "/docs/api-reference/", // your new prefix
] as string[],

Make sure the prefix:

  • Starts with /docs/ (or the equivalent path for your content directory)

  • Ends with a trailing slash

  • Does not overlap with a prefix you want to keep bilingual

Revision History

Takeshi TakatsudoCreated: 2026-03-11T22:32:04+09:00Updated: 2026-06-14T00:23:26Z

AI Assistant

Ask a question about the documentation.