Internationalisation
CTXR’s translation model starts from one decision: one language per node. A node carries inLanguage, and its content is written in that language — not a bundle of parallel translations crammed into one record. A Dutch place is a Dutch node; clients edit it in Dutch, the language they actually think and write in.
{ "@type": "Place", "inLanguage": "nl-NL", "name": "Fikkie", "description": "Een wandellocatie aan de Maas" }
The philosophy behind it: 100% quality in one language beats 60% in three. A page that’s excellent in its own language serves its audience well; a page machine-translated into three mediocre versions serves no one. Translation in CTXR is help when you want it, never a tax you pay up front.
The translation ladder
Reach for exactly as much as you need — four rungs, each more involved than the last:
1 browser widget zero effort, lowest fidelity
2 t() string substitution translate the UI chrome
3 per-locale templates {template}.{locale}.php
4 TranslateAndAdapt connector edited, routed translations
1 — Browser widget. Let the visitor’s browser translate the page. No work for you, no infrastructure; quality is whatever the browser manages.
2 — t() substitution. Translate the interface strings your templates emit — buttons, labels, microcopy — while content stays in its own language:
<button><?= t('add_to_route', 'Toevoegen aan route') ?></button>
t() looks up the string for the current locale and falls back to the default you pass, so a missing translation is never a blank.
3 — Per-locale templates. When a whole layout should differ by locale, add {template}.{locale}.php beside the base template — place.fr-FR.php next to place.php. The platform picks the locale-specific file when it exists and the base otherwise.
4 — TranslateAndAdapt connector. The full treatment: a connector that translates and editorially adapts content into target locales, producing real translated nodes that an editor can refine. The source node stays canonical; the translations are derived from it.
Locales, fallback, and routing
Locales are BCP-47 — nl-NL, en-UK, fr-FR. When a requested locale isn’t available, resolution walks a predictable ladder:
exact (nl-NL) → language (nl-*) → English (en)
Ask for nl-BE, get nl-NL if that’s the Dutch you have, then English as the floor. A visitor always lands on the closest match, never on nothing.
For rung 4, translations live under a URL prefix (/fr/fikkie) and the canonical source stays at the unprefixed path. Emit hreflang tags so search engines understand the relationship — one helper does it:
<?php the_hreflang($data) ?>
// <link rel="alternate" hreflang="nl-NL" href="…/fikkie">
// <link rel="alternate" hreflang="fr-FR" href="…/fr/fikkie">
The source node is the canonical; the prefixed locales point back to it. Translation is a layer over a node that’s already excellent in its own language — which is the whole point. Start at rung 1, climb only as far as the content deserves.