Skip to content

Multi-language sites

DocsForge has built-in static internationalization (i18n). Add translated files next to your default-language files and DocsForge builds locale-agnostic pages: every URL serves the same address, while the service worker returns the requested locale sibling (index.<locale>.html) from the cache.

Enabling i18n

Exactly one language must be marked as default. Two equivalent ways to configure it:

Zero-config — the i18n plugin loads automatically; declare languages under extra.i18n_languages (what this documentation site uses):

extra:
  i18n_languages:
    - locale: en
      name: English
      default: true
    - locale: zh
      name: 中文

Explicit plugin — declare the plugin with languages: when you need to tune plugin options:

plugins:
  - material/i18n:
      languages:
        - locale: en
          name: English
          default: true
        - locale: zh
          name: 中文

File naming

Translated pages use the locale suffix. They are emitted as siblings of the default page and share the same public URL.

docs/
├── index.md          # English (default) → site/index.html
├── index.zh.md       # Chinese          → site/index.zh.html
├── second.md         # English          → site/second/index.html
└── second.zh.md      # Chinese          → site/second/index.zh.html

Both index.html and index.zh.html are served from the same locale-agnostic URL (/ and /second/). The service worker stores the user's preferred locale in IndexedDB and returns the matching sibling from the cache.

A translation keeps the identity of the page it translates: index.zh.md is an index page just like index.md, so navigation tab icons, navigation.indexes section merging, and automatic ordering behave identically in every language.

Translated files can live anywhere in docs/, including subdirectories:

docs/
└── guide/
    ├── intro.md
    └── intro.zh.md

Fallback pages

The fallback_to_default option is a legacy setting and is ignored in the current locale-agnostic architecture. DocsForge only builds translations that actually exist; there are no fallback copies.

Translate section and page titles in the language switcher and sidebar with nav_translations:

plugins:
  - material/i18n:
      languages:
        - locale: en
          name: English
          default: true
        - locale: zh
          name: 中文
          nav_translations:
            Home: 首页
            "Getting started": 入门
            Installation: 安装

Language switcher

A language switcher appears automatically in the header when the i18n plugin is configured. Selecting a language stores the preference in IndexedDB and reloads the same URL; the service worker then serves the matching locale sibling. The page also emits <link rel="alternate" hreflang="..."> tags for SEO.

Site name and description per language

Override site_name and site_description for a locale:

plugins:
  - material/i18n:
      languages:
        - locale: en
          name: English
          default: true
          site_name: My Project
        - locale: zh
          name: 中文
          site_name: 我的项目
          site_description: 中文文档

Search and sitemaps

Each locale gets its own search index, but URLs remain locale-agnostic and only one sitemap is emitted at the site root.

site/
├── search/
│   ├── search_index.json     # default locale   └── search_index.zh.json  # Chinese
└── sitemap.xml

The frontend loads search/search_index.<locale>.json for the currently displayed locale, so search results stay in the same language.

Submit the single root sitemap to search engines. The sitemap points to the locale-agnostic canonical URLs.

How it works

  1. Build emits index.html and index.<locale>.html as siblings for every translated page.
  2. The cache manifest lists both files.
  3. The service worker caches all siblings during activation.
  4. On each page request the service worker reads the preferred locale from IndexedDB and returns the matching sibling if cached, otherwise the default index.html.
  5. The language switcher writes the new preference to IndexedDB and reloads the same URL.

Limitations

  • Only the suffix file layout is supported (index.zh.md).
  • The language switcher requires JavaScript and service-worker support; without it, the default locale is displayed.

Next steps