DocsBlogDocsBlog
Log in

Add languages, manage translations, and localize content

Internationalization (i18n)

DocsBlog supports multiple languages out of the box with English (en) and Simplified Chinese (zh-hans).

URL Structure

  • Default language (en): no prefix — /blog, /docs
  • Other languages: with prefix — /zh-hans/blog, /zh-hans/docs

This is controlled by hideLocale: "default-locale" in src/lib/i18n.ts.

Adding a New Language

Step 1: Register the Language

Add an entry to src/config/languages/registry/languages.json:

src/config/languages/registry/languages.json
[
  { "code": "en", "label": "English", "native": "English" },
  { "code": "zh-hans", "label": "Simplified Chinese", "native": "中文简体" },
  { "code": "ja", "label": "Japanese", "native": "日本語" }
]

Step 2: Create Translation File

Copy src/locales/en.ts to src/locales/ja.ts and translate all strings:

src/locales/ja.ts
export const ja = {
  common: {
    search: "検索",
    loading: "読み込み中…",
    noResults: "結果なし",
    // ... translate all keys
  },
  nav: {
    home: "ホーム",
    blog: "ブログ",
    docs: "ドキュメント",
    // ...
  },
  // ... all other sections
};

Step 3: Register in Locales Index

Update src/locales/index.ts to import and register the new locale:

src/locales/index.ts
import { ja } from "./ja";

const dictionaries: Record<string, Translations> = {
  en,
  "zh-hans": zhHans,
  ja,  // Add this line
};

Step 4: Create Content Directories

mkdir -p content/blog/ja
mkdir -p content/docs/ja/admin

Step 5: Create Docs Navigation

Create content/docs/ja/meta.json:

content/docs/ja/meta.json
{
  "title": "ドキュメント",
  "pages": ["index", "project-structure", "configuration", "..."]
}

Then create the corresponding .mdx files in content/docs/ja/.

Translation File Structure

The main translation files follow this structure:

export const en = {
  duration: { second: "{count}s", minute: "{count}m", hour: "{count}h" },
  common: { search: "Search", loading: "Loading…", noResults: "No results", ... },
  nav: { home: "Home", blog: "Blog", docs: "Docs", ... },
  hero: { badge: "...", title: "...", description: "...", cta: "..." },
  twitter: { title: "Twitter", description: "...", ... },
  youtube: { title: "YouTube", ... },
  features: { heading: "...", ... },
  faq: { heading: "...", items: [...] },
  footer: { copyright: "...", twitter: "Twitter" },
  // ... more sections
};

Using Translations in Code

import { getTranslations, interpolate } from "@/locales";

// Get all translations for a locale
const t = getTranslations("zh-hans");
console.log(t.common.search); // "搜索"

// Variable interpolation
interpolate("{count} items found", { count: "42" });
// → "42 items found"

Admin Panel Translations

Admin UI has separate translation files:

  • src/locales/admin/en.ts — English admin labels
  • src/locales/admin/zh-hans.ts — Chinese admin labels

When adding a language, also create src/locales/admin/ja.ts for admin panel localization.

Content Organization

content/
├── blog/
│   ├── en/          # English blog posts
│   └── zh-hans/     # Chinese blog posts
├── docs/
│   ├── en/          # English documentation
│   │   └── admin/
│   └── zh-hans/     # Chinese documentation
│       └── admin/

Each locale has its own content directory. Blog posts and docs are independent per language — you don't need to have the same posts in every language.

Language Switcher

A language switcher is automatically displayed in the top-right corner of the header. It shows all registered languages and switches between them while preserving the current page path.

AI Translation

For bulk translation of content, use the built-in Translation System in the admin panel, powered by DeepSeek AI.