Speaking the visitor's language
Two halves, and only one is yours: the framework translates its own sentences, your app translates its own.What the framework does on its own
The sentences Bretzel produces — a carousel's accessibility labels, a `file_upload`'s maximum-size message, an empty table's text — go through its own table. Declare the app's languages, and they follow.app = Bretzel(
secret_key="…",
lang="en", # the default language
languages=("en", "fr"), # the ones we accept
)
The language is resolved PER REQUEST: an explicit choice wins over the browser's header, and a language one has not declared falls back to the default rather than rendering a raw key.What your app must do
`Language()` reads like its three ambient sisters — `Screen()`, `ColorScheme()`, `LiveConnection()`: one object, one field. The table is yours.from bretzel import Language
PHRASES = {
"en": {"save": "Save", "cancel": "Cancel"},
"fr": {"save": "Enregistrer",
"cancel": "Annuler"},
}
def say(key: str) -> str:
"""A function, not a component: this is TEXT, and
it must be able to go into a `placeholder=` as
much as into a `ui.text`."""
code = Language().code.split("-")[0]
return PHRASES.get(code, PHRASES["en"])[key]
ui.button(say("save"), color="primary")
`Language().code` can carry a region (`fr-CA`, `en-GB`). Cutting at the hyphen is the simplest form that works; keeping the region only makes sense if the table really distinguishes it.Letting the user choose
`Language.set` is a SERVER handler: it picks the language and reloads the page in it. So one passes it as a `partial`, not as a call — `Language.set("fr")` would run at RENDER time, not on the click.from functools import partial
ui.button("English", on_click=partial(Language.set, "en"))
ui.button("Français", on_click=partial(Language.set, "fr"))
Why this exists when the language is already automatic: `Accept-Language` describes the operating system's configuration, not a reading choice. With no way of saying otherwise, somebody whose machine is in English cannot read in French.What it does not do
A seam, not a library
This is not an i18n library. No `.po` files, no plural rules, no string extraction, no date or currency formatting beyond what the browser can do. It is a SEAM: the request's language, and the framework's table for its own words. The roadmap announces full i18n in 2.1.