Repository navigation
Multi Language
oCMS provides comprehensive multi-language support for both content translation and admin UI localization.
The multi-language system consists of two parts:
- Content Translation: Translate pages, categories, tags, and menus into multiple languages
- Admin UI Localization: Display the admin interface in different languages (currently English and Russian)
- Navigate to Admin > Languages
- Click Add Language
- 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
-
Code: 2–10 lowercase ASCII letters, digits, or internal hyphens (for example
- Click Save
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:
- Go to Admin > Languages
- Click the check-mark button (tooltip Set as Default) next to the desired language
Languages appear in the language switcher in the order specified by the Position field. Set the position number when adding or editing a language.
- Open a page in the editor
- Look for the Translations panel in the sidebar
- Click Add Translation for the target language
- A new page is created with:
- The same title (editable)
- Empty body content (to be translated)
- Link to the original page
- Translate the content and save
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
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.
Categories and tags work the same way as pages:
- Open the category/tag editor
- Set the language for the item
- Create translations using the Translations panel
Languages are indicated in URLs using prefixes:
- Default language:
/about-us - Other languages:
/ru/about-us,/de/about-us
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.
The language is determined in this order:
-
URL prefix:
/ru/page-slug→ Russian -
Query preference:
?lang=ruwhen no language prefix is present - Cookie preference: Stored from previous selection (homepage only)
- Accept-Language header: Browser preference (homepage only)
- 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.
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.
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" .}}For RTL languages (Arabic, Hebrew, etc.):
- Set Direction to RTL when configuring the language
- The
<html>tag automatically getsdir="rtl" - Your theme should include RTL-specific CSS
- Open the language selector (globe icon) in the admin header. It is shown only when more than one active language has admin translations
- Choose your preferred language
- The admin interface reloads in the selected language
Currently supported:
- English (
en) - Russian (
ru)
Admin UI translations are stored in internal/i18n/locales/. To add a new language:
- Create a new directory:
internal/i18n/locales/{lang}/ - Create
messages.jsonwith translations:
{
"language": "de",
"messages": [
{
"id": "nav.dashboard",
"message": "Dashboard",
"translation": "Übersicht"
},
{
"id": "btn.save",
"message": "Save",
"translation": "Speichern"
}
]
}- Add the language code to
SupportedLanguagesininternal/i18n/i18n.go - Rebuild the application
See Internationalization for the full i18n system documentation.
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}}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">Create separate menus for each language:
- Create a menu (e.g., "Main Menu - EN")
- Set the menu's language to English
- Create another menu (e.g., "Main Menu - RU")
- Set the menu's language to Russian
- In your theme, load the menu based on current language
See Menu Builder for menu management details.
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.
- Set up languages first: Configure all languages before creating content
- Translate systematically: Create a workflow for translating content
- Use consistent slugs: Consider using transliterated slugs for better SEO
- Test RTL: If supporting RTL languages, test thoroughly
- Plan navigation: Decide how users will discover translated content
- Consider SEO: Ensure hreflang tags are correctly implemented
- Ensure the translated page is published
- Check that the translation link exists (in the Translations panel)
- Verify the target language is active
- Check URL for language prefix
- Clear browser cookies
- Verify language detection order
- Check that
messages.jsonexists for the language - Ensure all required message keys are present
- Restart the server after adding translations
Getting Started
Core Features
- Content Management
- Media Library
- Taxonomy
- Menu Builder
- Forms
- Multi-Language
- SEO
- Feeds
- Video Embedding
- Agent-Ready
Administration
API & Integrations
Security
Theme Development
Module Development
Built-in Modules
- Module: Sentinel
- Module: Developer
- Module: DB Manager
- Module: Migrator
- Module: Informer
- Module: Demo Mode
- Module: Internal Analytics
- Module: External Analytics
- Module: Privacy
- Module: Embed
- Module: MCP Server
Deployment