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-47nl-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.