[i18n]
Locale routing: the locales a site serves, the default one, and the order the URL prefix, Accept-Language and the gio_locale cookie are read in.
gio.toml
[i18n]
locales = ["en", "de", "fr"]
default_locale = "en"With no locales, i18n is off and costs nothing. In components, useLocale() returns the request's locale and <LocaleLink> prefixes links with it. See Internationalization.
Reference
| Key | Default | Description |
|---|---|---|
localesstring[] | [] | The locales the site serves, as they appear in URLs (/de/about). An empty entry, or one listed twice (ignoring case), stops startup. |
default_localestring | "en" | The locale when nothing else decides. Pages in it are served without a prefix, and <html lang> is left as the root layout renders it. With locales set it must be one of them, spelled the same way, or startup stops. |
detect_fromstring[] | ["path", "accept-language", "cookie"] | Where the locale comes from, tried in this order: "path" (the first URL segment), "accept-language" (the header), "cookie" (gio_locale). The first that names a configured locale wins. Any other value stops startup, with the closest valid one. |
Behavior
- A leading locale segment is always removed before routing, whether or not
"path"is indetect_from:/de/aboutrendersapp/about/page.tsxand/derenders the home page. Guards, redirects, rewrites, header rules and rate limits match that path too, so a guard on/admin/*restalso protects/de/admin. Accept-Languageentries are tried by quality value, highest first (the browser's order breaks ties);q=0or a malformedqrules an entry out. An entry matches a locale exactly, ignoring case (pt-brpickspt-BR), or by its language (de-ATpicksde). The result is always spelled as inlocales.- The
gio_localecookie must hold one of the configured locales. GioJS reads it and never sets it; your app sets it, for example from a language switcher. - When the locale is not the default, the server writes it into
<html lang>, replacing the root layout'slang. HTML files inpublic/are served as written, in every locale. <LocaleLink>leavesdefault_localeunprefixed. The server passes this section to the worker (GIO_I18N_CONFIG), so changing it changes the deployment id.- Cached pages are stored per locale. While
detect_fromreads a header or the cookie, a page requested without a locale prefix is sentCache-Control: private, no-cachewithout an ETag: one URL serves several languages there, and shared caches key by URL. Prefixed URLs stay shareable while"path"comes first; whendetect_fromlists a header or the cookie before it, the header can override a prefix, so prefixed URLs areprivatetoo.
No key in this section logs a warning. Startup logs the locales when i18n is on.
Startup errors
Startup and --check-config refuse a section that cannot work as written:
text
gio.toml:3: invalid `i18n.detect_from`: unknown variant `acept-language`, expected one of `path`, `accept-language`, `cookie` - did you mean "accept-language"?
gio.toml:2: invalid `i18n.default_locale`: "en" is not one of locales ("de", "fr")
gio.toml:2: invalid `i18n.locales`: "EN" is listed twice (as "en" before)A default_locale left at its default ("en") is reported on the locales line: set it to one of your locales.
Examples
URL prefixes only
Every language has its own URLs, which CDNs can cache:
gio.toml
[i18n]
locales = ["en", "de"]
default_locale = "en"
detect_from = ["path"]A remembered choice wins over the browser
gio.toml
[i18n]
locales = ["en", "de", "fr"]
detect_from = ["path", "cookie", "accept-language"]Good to know
- The URL prefix and the cookie match
localesexactly:/pt-br/is not thept-BRlocale. OnlyAccept-Languageignores case. revalidatePathandPOST /_gio/revalidatedrop a leading locale from a path, so a purge reaches every language of the page.- The section is part of the deployment id: changing it drops persisted pages.
Related
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | An unknown detect_from value, a default_locale outside a non-empty locales, and an empty or duplicate locale stop startup (they were ignored). Pages whose locale was negotiated from request headers are never public and get no ETag. useLocale() returns the request locale during server rendering too. Accept-Language is read by quality value and gives the locale as spelled in locales (an exact match was lowercased). <html lang> replaces the root layout's lang instead of adding a second one. <LocaleLink> defaults to default_locale. A prefixed URL is private when detect_from tries a header before path. public/ HTML files keep their own lang (it was rewritten under the file's old length, cutting the body short). |
v0.1.0-beta.1 | Introduced with locales, default_locale and detect_from. |