Copy lived in docs while production said something else
You kept Spanish in a spreadsheet while the hero on production was still English. That week you did not ship the translation because duplicating the whole page felt like owning two products.
The enemy is copy that lives outside the builder—rename a line once and you chase six places.
Here is what ships: one Translations home (Pages → Advanced), languages plus entries, and dot keys like hero.title. Put localized copy in markup with t() or the pipe form | t (same lookup as t()). When a language has no row yet, you still see what you authored, or built-in strings where t() / | t apply. The optional second argument on t(), or | t:params with a context object, is only for built-in placeholder objects—not a plain-text fallback.
<h1>{{ t('hero.title') }}</h1>
<p>{{ 'hero.subtitle' | t }}</p>
<img src="/hero.png" alt="{{ t('hero.alt') }}" />
<input placeholder="{{ t('form.email_placeholder') }}" /><h1>{{ t('hero.title') }}</h1>
<p>{{ t('product.savings_percent', { percent: 20 }) }}</p>
<p>{{ getResolvedLanguage() }}</p>
<p>{{ locale }}</p>
<p>{{ defaultLocale() }}</p>When a backend script needs the resolved string before markup runs, keep it in one page-scoped block and await the same helpers (getLocale() lines up with {{ locale }} on the page).
<script scope="backend">
var hero = await t('hero.title');
setVariable('hero_title', hero);
var label = await t('title.greeting');
setVariable('greeting', label);
</script>Try it: add one language, save hero.title, hit the page with ?hl= (or a {hl} slug segment) and watch template output swap without cloning the file.