Internationalisation
SynaptikCMS has a two-track i18n system: one for the front-end, one for the admin panel, independently configurable (see Translation Editor for the admin-facing side of this).
Locale files
| Scope | Path |
|---|---|
| Front-end | lang/front/{locale}.json |
| Admin | lang/admin/{locale}.json |
Context-aware loading
The lang loader (core/lang-cache.php) reads LANG_CONTEXT, defined as 'admin' by admin/includes/admin-functions.php right before it requires lang-cache.php, and defaulting to 'front' everywhere else:
LANG_CONTEXT = 'admin' → read admin_language → fallback to active_language → fallback to 'en'
LANG_CONTEXT = 'front' → read active_language → fallback to 'en'
lang_current(): string returns the locale for the current context. lang_available(): array lists locales for the current context; lang_available_for_scope(string $scope): array takes an explicit 'front'/'admin' scope instead (used by the Settings page to list both side by side).
admin_front_url_slug(string $type): string is the one deliberate exception — it always reads active_language directly from config.json, bypassing lang_current(), so URLs generated from the admin panel always match the real public routes regardless of which locale the admin panel itself is running in.
Compiled cache
Each context has its own cache — cache/lang/front/{locale}.php and cache/lang/admin/{locale}.php — so changing one locale never invalidates the other. Each cache file is a plain return [...] array compiled once from the source JSON, served straight from OPcache on later requests (no json_decode() on the hot path). Auto-invalidates whenever the source .json or config.json is newer than the cache file.
Output helpers
$label = __t('new_article'); // Return
_e('new_article'); // Echo
printf(__t('media_files_size'), admin_format_file_size($s)); // Placeholder
__n('%d comment', '%d comments', $count); // Plural-aware
JS bridge — inject once in header.php:
<script type="application/json" id="cms-lang-json"><?php echo lang_js_bridge(); ?></script>
Adding a new string
Add the key/value to every locale file in lang/front/ and lang/admin/ as appropriate — en.json first, as the reference schema, then translate for the others via the Translation Editor. Never hardcode a user-visible string in PHP.
Adding a new language
Prefer the Translation Editor's New locale button — it validates and duplicates en.json correctly in one step. Manually, the equivalent is: copy lang/front/en.json → lang/front/{locale}.json and lang/admin/en.json → lang/admin/{locale}.json, translate all values, then set active_language (and optionally admin_language) in config.json.
url_slug_* keys live inside the regular locale files and can be translated like any other string — but changing them affects the public URL routes. Verify against parseRequestUri() if you customize them.
