Skip to content

Multi Language

Oleg Ivanchenko edited this page Oct 18, 2026 · 8 revisions

Multi-Language Support

oCMS provides comprehensive multi-language support for both content translation and admin UI localization.

Overview

The multi-language system consists of two parts:

  1. Content Translation: Translate pages, categories, tags, and menus into multiple languages
  2. Admin UI Localization: Display the admin interface in different languages (currently English and Russian)

Setting Up Languages

Adding Languages

  1. Navigate to Admin > Languages
  2. Click Add Language
  3. Fill in:
    • Code: 2–10 lowercase ASCII letters, digits, or internal hyphens (for example ru, de, pt-br)
    • Name: English name (e.g., "Russian")
    • Native Name: Name in the language (e.g., "Русский")
    • Direction: LTR (left-to-right) or RTL (right-to-left)
    • Active: Whether the language is available on the site
  4. Click Save

Default Language

One language must be set as the default. This is the fallback language when:

  • A translation doesn't exist for the requested language
  • No language preference is detected
  • The URL doesn't include a language prefix

To set the default language:

  1. Go to Admin > Languages
  2. Click the check-mark button (tooltip Set as Default) next to the desired language

Language Order

Languages appear in the language switcher in the order specified by the Position field. Set the position number when adding or editing a language.

Translating Content

Translating Pages

  1. Open a page in the editor
  2. Look for the Translations panel in the sidebar
  3. Click Add Translation for the target language
  4. A new page is created with:
    • The same title (editable)
    • Empty body content (to be translated)
    • Link to the original page
  5. Translate the content and save

Translation Links

Translation links connect content across languages. When you create a translation:

  • The original page and translation are bidirectionally linked
  • The language switcher shows available translations
  • Deleting a page removes its translation links

Viewing Translations

In the page list, translated pages show language badges indicating:

  • The page's language
  • Number of translations available

Filter pages by language using the language dropdown in the list view.

Translating Categories and Tags

Categories and tags work the same way as pages:

  1. Open the category/tag editor
  2. Set the language for the item
  3. Create translations using the Translations panel

Frontend Language Handling

URL Structure

Languages are indicated in URLs using prefixes:

  • Default language: /about-us
  • Other languages: /ru/about-us, /de/about-us

Reserved Route Names

Core route names cannot be activated as language codes: admin, api, blog, uploads, static, themes, health, login, logout, language, forms, search, tag, category, page, session, account, csp-report, and the built-in module routes analytics, embed, and example.

Page slugs and single-segment aliases cannot use the names whose exact URL a core route answers: account, admin, blog, example, health, login, and search. The admin form rejects them as slugs and aliases, the REST API as slugs, the Migrator suffixes them (blog-2), and content import rejects an archive that contains one. The other reserved names own only deeper paths or other methods, so pages may use them. The admin form also rejects a multi-segment alias that a route answers, such as blog/tag/news or anything below uploads/; other multi-segment aliases such as blog/post/55 keep working. Existing pages already stored at a reserved slug stay editable while the slug is unchanged.

Language Detection

The language is determined in this order:

  1. URL prefix: /ru/page-slug → Russian
  2. Query preference: ?lang=ru when no language prefix is present
  3. Cookie preference: Stored from previous selection (homepage only)
  4. Accept-Language header: Browser preference (homepage only)
  5. Default language: Fallback

The cookie and the Accept-Language header apply to the homepage only: every other URL without a language prefix is served in the default language. The URL prefix always wins over ?lang.

Feed Language

RSS and Atom subscriptions use the language in their URL. /rss.xml and /atom.xml always select the active default language; /ru/rss.xml and /ru/atom.xml select Russian. Query preferences, cookies and Accept-Language do not override feed URLs. Category/tag slugs must belong to that language; missing terms and unknown or inactive language prefixes return 404. See Feeds for all subscription paths.

Language Switcher

The frontend language switcher (if enabled in your theme) shows:

  • Current language
  • Available translations for the current page
  • Links to homepage in other languages (if no translation exists)

Include the language switcher in your theme:

{{template "language-switcher.html" .}}

RTL Support

For RTL languages (Arabic, Hebrew, etc.):

  • Set Direction to RTL when configuring the language
  • The <html> tag automatically gets dir="rtl"
  • Your theme should include RTL-specific CSS

Admin UI Localization

Changing Admin Language

  1. Open the language selector (globe icon) in the admin header. It is shown only when more than one active language has admin translations
  2. Choose your preferred language
  3. The admin interface reloads in the selected language

Supported Admin Languages

Currently supported:

  • English (en)
  • Russian (ru)

Adding Admin Translations

Admin UI translations are stored in internal/i18n/locales/. To add a new language:

  1. Create a new directory: internal/i18n/locales/{lang}/
  2. Create messages.json with translations:
{
    "language": "de",
    "messages": [
        {
            "id": "nav.dashboard",
            "message": "Dashboard",
            "translation": "Übersicht"
        },
        {
            "id": "btn.save",
            "message": "Save",
            "translation": "Speichern"
        }
    ]
}
  1. Add the language code to SupportedLanguages in internal/i18n/i18n.go
  2. Rebuild the application

See Internationalization for the full i18n system documentation.

Theme Integration

Accessing Language in Templates

In theme templates, you have access to:

<!-- Current language code -->
{{.LangCode}}

<!-- Language direction (ltr/rtl) -->
{{.LangDirection}}

<!-- Available translations for current page -->
{{range .Translations}}
    <a href="{{.URL}}">{{.Language.NativeName}}</a>
{{end}}

hreflang Tags

The system automatically generates hreflang meta tags for SEO:

<link rel="alternate" hreflang="en" href="https://example.com/about-us">
<link rel="alternate" hreflang="ru" href="https://example.com/ru/about-us">
<link rel="alternate" hreflang="x-default" href="https://example.com/about-us">

Menu Translation

Create separate menus for each language:

  1. Create a menu (e.g., "Main Menu - EN")
  2. Set the menu's language to English
  3. Create another menu (e.g., "Main Menu - RU")
  4. Set the menu's language to Russian
  5. In your theme, load the menu based on current language

See Menu Builder for menu management details.

API Access

Language of a Page

Every page returned by /api/v2/pages carries the code of its language:

{
    "data": {
        "id": 1,
        "title": "About Us",
        "slug": "about-us",
        "language_code": "en"
    }
}

When creating a page, send language_code to choose the language. It falls back to the default language when omitted, and the language must be active. The v2 API has no language filter, sends no Content-Language header and does not return page translation links.

See REST API for full API documentation.

Best Practices

  1. Set up languages first: Configure all languages before creating content
  2. Translate systematically: Create a workflow for translating content
  3. Use consistent slugs: Consider using transliterated slugs for better SEO
  4. Test RTL: If supporting RTL languages, test thoroughly
  5. Plan navigation: Decide how users will discover translated content
  6. Consider SEO: Ensure hreflang tags are correctly implemented

Troubleshooting

Translation not showing in switcher

  • Ensure the translated page is published
  • Check that the translation link exists (in the Translations panel)
  • Verify the target language is active

Wrong language displayed

  • Check URL for language prefix
  • Clear browser cookies
  • Verify language detection order

Missing admin translations

  • Check that messages.json exists for the language
  • Ensure all required message keys are present
  • Restart the server after adding translations

Clone this wiki locally