7: Vertalingen
Phlo behandelt vertaling als een renderingskwestie: je schrijft view-tekst in je brontaal, markeert het, en de lang resource levert de juiste taal op het moment van aanvraag, waarbij ontbrekende invoer op de achtergrond wordt vertaald. In dit hoofdstuk leert de poll Nederlands onder een /nl prefix.
7.1: Source-language markers
Static view text gets a marker keyed by the language the text is written in. This app's source language is English, so the key is en:
view:
<main#app.poll>
<h1>{en: Which stack wins?}</h1>
{{ $this->results }}
{{ $this->choices }}
</main>
{en: ...} transpiles to a call to the en() helper, which asks the translation layer for the text in the active language. If the active language is English, the text passes through untouched. If you were writing a Dutch-source app you would use {nl: ...} with the same mechanics; the marker always names the source language of that specific string. Outside views, the same helpers work in code: method title => en('Phlo Poll').
Update the <h1> in poll.phlo now; the page still renders identically, because the active language is still English.
7.2: Configure the language layer
Three small pieces of configuration. First, the resources in data/app.json (the lang resource brings cookie detection, INI file caching, and AI-backed translation):
{
"resources": [
"DB/DB",
"DB/model",
"DB/JSONDB",
"DB/JSON.result",
"payload",
"phlo.async",
"DOM/form",
"lang",
"cookies",
"files/INI",
"AI/AI",
"AI/OpenAI",
"security/creds"
]
}
Here phlo.async is the backend resource used by the translation layer to schedule its CLI backfill. It is not the SPA request handler from the previous chapter; frontend navigation and forms are handled by the generated frontend runtime and their DOM resources.
Second, tell the runtime where translation files live, in www/app.php:
phlo_app(
id: 'Poll',
host: 'localhost',
build: true,
debug: true,
app: dirname(__DIR__).'/',
langs: dirname(__DIR__).'/langs/',
);
Every named argument to phlo_app() becomes an app-wide constant, so langs is now a path you (and the lang resource) can use anywhere. Create the folder: mkdir app/langs.
Third, declare the app's languages in app.phlo:
prop lang = 'en'
prop langs = ['en' => 'English', 'nl' => 'Nederlands']
Rebuild and lint; both end at [].
7.3: Een /nl route prefix
De actieve taal staat op %app->lang. Stel deze in via de URL met een tweede route in poll.phlo:
route GET poll => $this->home
route GET $lang:nl poll {
%app->lang = $lang
$this->home
}
$lang:nl is een waarde-lijst segment: het matcht alleen wanneer het eerste padsegment letterlijk nl is. Met meer talen breid je de lijst uit: $lang:nl,de,fr. De Engelse route behoudt de schone URL; de geprefixeerde route stelt de taal in en rendert dezelfde pagina.
Open http://localhost/nl/poll. De <html lang="nl"> attribuut volgt %app->lang, maar de koptekst leest nog steeds Engels: er is nog geen Nederlandse vertaling.
7.4: De vertaalcache
Vertalingen bevinden zich in langs/<lang>.ini, één regel per string, gekeyed op een hash van de brontekst. Wanneer een gemarkeerde string geen invoer heeft voor de actieve taal, toont de pagina de brontekst en plant een achtergrondvertaling via OpenAI; het volgende verzoek leest het gecachte resultaat. Voor die backfill om te werken, zet een API-sleutel in data/creds.ini:
OpenAI = sk-...
Geen sleutel? Alles werkt nog steeds, ontbrekende strings blijven gewoon in de brontaal. Je kunt het bestand ook zelf schrijven; na de eerste backfill ziet langs/nl.ini er als volgt uit:
enWhichStad3 = "Welke stack wint?"
De sleutel encodeert de brontaal plus een hash van de exacte tekst, zodat gewijzigde brontekst een nieuwe invoer krijgt. Herlaad http://localhost/nl/poll zodra de invoer bestaat:
<h1>Welke stack wint?</h1>
Zelfde view, dezelfde route-logica, tweede taal. De poll is nog maar één hoofdstuk verwijderd van voltooiing: het kan nog steeds geen resultaten naar andere kijkers pushen.
Laatst bijgewerkt op 23-08-2026