Summary#
Only a language the app speaks is ever chosen (A language's words — locales/<language>.osy), and the first of these that names one wins:
- an explicit choice — an address that starts with the language (
/sv/coffee, served in place as/coffeein Swedish), or a switch that calledSession.SetLanguage("sv"); remembered from then on; - the signed-in reader's own
[PreferredCulture](its language); - the browser's
Accept-Language; - the language of
app.DefaultCulture; - the source language — English, or what
app.SourceLanguagenames (app.SourceLanguage — the language the app is written in).
Signature#
string Session.Language // "sv" — the language this reader reads in
List<string> App.Languages // ["en", "sv"] — the source first, then every translated language
string? App.CultureOf(string language) // "sv" → "sv-SE" — the culture the app formats it in, or null
void Session.SetLanguage(string language) // a client effect: the page re-renders in place
[Email] component … { language => order.Language; subject => …; … } // a mail in its recipient's language
MyMail(order, language: "sv") // …or rendered in a chosen one (a preview)Description#
One choice drives everything. The page's text, its <title>, lang and dir (an Arabic page reads right to
left — Right to left — an app that reads in Arabic with no change to the app), a server render, a server function's
error message — and the culture numbers and dates format in (Culture formatting — ToString(format, culture)): a Swedish reader sees
1 234,50 kr even when app.DefaultCulture is en-GB, because the language chooses the app's declared culture of
that language. A reader whose own culture speaks the chosen language keeps it.
Switching is in place. Session.SetLanguage("sv") fetches that language's table (by content address, cached
forever) and every region that shows a translated text re-renders — no reload, no lost state. The choice is kept in
the browser and sent with every request, so the server answers in it too.
A mail is in its recipient's language, not the sender's. Declare it on the mail: language => order.Language;.
Every text in the mail — its subject, its parts, a shared footer — renders in that language, whoever sends it (a
customer at the checkout, staff at the desk, a scheduled job). With none, a mail is in the language of the work
sending it. An app that mails about something should record the language it was placed in (Session.Language at the
moment it is placed). Every mail also takes an optional last argument, language: — MyMail(order, language: "sv")
renders it in Swedish whoever it is for, which is what a desk's preview of the Swedish mail needs.
…and so are its numbers, its money, its dates and its data. Inside a mail, a culture-less format
($"{total:N2}", price.ToString("C"), a date written as text) is in the culture of the MAIL's language — 17,68 kr
in a Swedish one, 17.68 kr in an English one — and a [Translatable] value it reads (a product's name) is read in
it too, wherever the mail is rendered: a request in another language, a workflow, a job, a test. A mail's language
may be named by a culture: language => account.Language; with a [PreferredCulture] holding "sv-SE" is Swedish.
A person's own language is on their account. Give the principal a [PreferredCulture] Culture? Language; and set it
when they switch while signed in: a device where they chose nothing (a new phone) reads the app in it once they sign
in — every request, not only the first page — and mail about them rather than about an order says
language => account.Language;. A choice made on a device still outranks it there.
Words staff wrote for a mail are per language, too. A mail's Editable parts keep the words staff saved per
language ([Email] — mail rendered from data): the mail asks for the words saved in ITS language, and a language with none says the
template's default as that language's locale file translates it — the English words a shop wrote never reach a
Swedish reader.
A server body can switch too. Session.SetLanguage("sv") in a server function puts Swedish in effect for the rest
of that run: its data reads and writes ([Translatable] — an entity's own words in every language), its translated texts, and Session.Language itself.
Where no request is in flight at all — a workflow, a scheduled job, a test body — Session.Language is the source
language and App.Languages is still every language the app speaks. A culture-less format in an app that speaks more
than one language follows the language being read (the request's, the mail's, or the one a body chose), in a page
and in a function alike.
Which culture a language formats in — App.CultureOf. App.CultureOf("sv") answers the culture the app writes
Swedish in: app.DefaultCulture when that is a Swedish one, else the app's first declared culture of Swedish, else null
(the app does not speak it). It is the same rule a request in that language and a mail in it format with, so it is what
to keep as a person's [PreferredCulture] when they switch — without switching the run's language to find out.
Who is served what. A browser is served only its own language's table. The words of pages a signed-out visitor
cannot open (neither [AllowAnonymous] nor [Composable]) are in a separate table served to signed-in readers only,
and a mail's words are never served to a browser at all.
Examples#
[Composable]
[AllowAnonymous]
component LanguageSwitch() {
action Choose(string lang) { Session.SetLanguage(lang); }
render {
Row(gap: 1, label: "Language") {
foreach (var lang in App.Languages.Where(l => l != Session.Language)) {
Pressable(onClick: () => Choose(lang), label: lang) { Text(lang.ToUpper()); }
}
}
}
}
translations sv {
accessibleName "Language" => "Språk";
}string CultureToKeep(string language) {
// null for a language the app does not speak — then there is nothing to keep
return App.CultureOf(language) ?? "";
}See also#
- Every language's page, for a search engine — hreflang, canonical, sitemap — each page's address in every language, for a search engine
- Which words are translated — the compiler finds every text a reader sees — which texts are translated
- Culture formatting — ToString(format, culture) — the culture a language formats in
- A language's words — locales/<language>.osy — the languages an app speaks
- app.SourceLanguage — the language the app is written in —
app.SourceLanguage, the language the app is written in