Problem
The site and the sign-in already read Croatian, and the console did not. A Croatian customer signs in to an English console with no sign that Croatian exists, and the language box in its header changed every other host but not the console itself.
The owner set the behaviour on 2026-10-05: a first visit reads English, with a banner offering Croatian, and the banner is the only Croatian on the page until the person switches.
Proposal
One preference, every host
The person's language is the cookie inorbit.lang (en or hr) on the registrable
domain, the one the site, the sign-in and the docs already share (ui/shared/prefs.tsx).
It is written only when the person chooses. The console adopts it on arrival; without it
the console reads English and never picks Croatian by itself. That differs from the site,
which reads Croatian for a visitor from Croatia, Bosnia and Herzegovina, Serbia or
Montenegro without asking: a public page can guess, a working tool should not change under
someone who did not ask.
The choice is made in four places: the banner, the language box in the header, a language menu in the person's menu and ⌘K, and a Language card on the profile page.
The offer
A visitor who has not chosen, and whose browser's language is Croatian, Bosnian or
Serbian, or whose country ('s CF-IPCountry, answered by the console's own
/whereami as on the site) is one of the four above, sees one line under the header, in
Croatian: "Konzolu možete koristiti i na hrvatskom." with "Prebaci na hrvatski" and "Ne,
hvala".
- "Prebaci na hrvatski" sets
inorbit.lang=hr: the console, and every other host, reads Croatian from then on. - "Ne, hvala" puts the offer away (
inorbit.lang_offer=dismissed) and leaves the language unset, so the site still reads Croatian for that visitor by country. Writingenhere would have switched the site to English for someone who only declined the console's offer.
Neither the country nor the browser's language is stored or logged by the page; the only thing kept is a choice the person made.
A diagram is drawn here in the RFCs product; this page does not show diagrams yet.
The dictionaries
No library: the console already had the same small machinery as the site (two
dictionaries keyed <namespace>.<slug>, useT, dates and numbers formatted in the page's
language). It adds counts in three forms (Croatian's one, few and other, chosen by
Intl.PluralRules), and page names keyed by their English name, so the sidebar, the
crumbs, the section navs and ⌘K translate from one place.
tool/check.ts, part of ui:console:check, fails when:
- a key is missing in either language;
- a key is not used anywhere;
- a
t("…")asks for a key no dictionary has; - a page in the console's map has no Croatian name.
In steps
The console has about 120 pages and dialogs. The first step translates the shell (the sidebar, header, crumbs, section navs, ⌘K, the person's and the account's menus, the session notices, the counts beside the products) and the profile page, and adds the offer and the switches. Each later step translates an area's pages; a page not yet translated reads English inside a Croatian shell until its step lands.
Croatian follows docs/style/writing.md: standard Croatian, the formal "vi" a business
tool uses, and engineering terms kept in English where Croatian engineers keep them
(API, webhook, RFC, developer, gateway). The glossary is the dictionaries themselves
(ui/console/src/lib/i18n/messages).
Alternatives considered
- A library (next-intl, i18next). More features than two languages need, a server runtime the console's static export does not have, and a second way of doing what the site and the sign-in already do by hand. Not taken.
- Croatian by country, as the site does. Refused by the owner's call: the console stays English until the person chooses.
- The preference on the account, server-side. It would follow the person to a new device. The cookie already follows them across every InOrbit host in one browser; the account field is a later step if people ask for it.
Decision
Open. The shell and the profile page are built; the areas follow.
Status log
- 2026-10-05: opened. The offer, the four switches, the shell and the profile page in
Croatian, plurals, and the dictionary checks in
ui:console:check. - 2026-10-05: the account area in Croatian (Privacy, Your data, Sign-in and security), and the shared danger zone and pager.
- 2026-10-05: API tokens and keys (the list, the create and reveal dialogs) and Usage (the month and its meter, usage over time, the request log, the alert settings) in Croatian.
- 2026-10-05: Webhooks (the list, an endpoint with its deliveries, health, settings and signing secret, the test inbox), Events (the live tail, the history, MQTT) and Domains (the list and the six-step wizard, with each DNS provider's steps in Croatian; the providers' own button names stay as their dashboards show them) in Croatian. Rates and times follow the language (99,5 %).
- 2026-10-05: the team area in Croatian (overview, members, invitations, security, the audit log with its filters, settings and the danger zone), the new-team dialog, the team banner and the invitation answer; audit verbs, roles and relative times follow the language.
- 2026-10-07: Checked: a scan of the console's pages finds every area reading the dictionaries (#343 to #385, in console-ui at 3a40912a) except the welcome page and the agreement's signing page. Open until those two follow.