Internationalization
Internationalization is hard. That's why Bones includes it from the start.
Most apps ship in English and plan to translate later. Later means finding every hardcoded string, in every app, email, and error message, then mirroring every layout for a right-to-left language. Bones does both from the start.
Multilingual
Bones ships in English, French, Spanish, and Hebrew. English is the default.
- Every surface. The web app, desktop app, marketing site, blog, docs, emails, and the backend's error messages all go through one library, i18next.
- English stays in the code. Each string's English is written where it's used. A translation overrides it, and a missing one falls back to the English, never to a blank or a key name.
- Fully translated. 577 English strings across seven catalogs, and French, Spanish, and Hebrew each have all of them.
- Local formats. Dates and numbers use the language Bones shows and the region from the browser, so French in a Canadian browser formats as
fr-CA. - Offered, not forced. Everything starts in English. On a first visit, a browser set to French, Spanish, or Hebrew gets a dialog offering that language, written in it. Either answer is remembered.
- Saved to the account. In the apps, the language is a setting on the user's account. Emails go out in the recipient's language.
- Language URLs on the public sites. The marketing site, blog, and docs serve each language under
/fr,/es, and/il, with English unprefixed. Every page lists its translations for search engines, and the sitemaps list every language. - One switch. An admin can turn languages off in Feature Flags, and everyone sees English.
CI fails on interface text that skips translation, and on a translation made from English that has since changed.
Left to right and right to left
Hebrew reads right to left, and every surface follows it.
- Layouts mirror. Styles use start and end instead of left and right, so every layout flips with the language.
- Every page says its direction. Each app page, static page, and email carries its language and direction, so browsers and screen readers read it correctly.
- Machine values stay left to right. Code, paths, and IDs keep their own direction inside a Hebrew page.
- What people write keeps its direction. Chat messages, posts, and the terms set their direction from their own text, so English inside a Hebrew page keeps its punctuation in place.
- Checked on every pull request. The accessibility sweep includes a Hebrew page on the marketing site, blog, and docs.
What isn't done yet
- No native speaker has reviewed the translations. Claude wrote them in three passes: translate, review against the English, and review in the rendered UI.
- Hebrew typography. Neither Bones font has Hebrew letters, so the browser falls back to a system font. That's for a design pass.
- Content stays in its own language. Blog posts, docs pages, the terms, and feature labels are written once, in one language.
- A few strings are English only: form validation messages from Zod, and the site-wide 404 page.
How it compares
| Option | Trade-off |
|---|---|
| English only, translate later | Nothing to set up now, and every string to find later |
| A framework's own library (next-intl) | Fits one framework, so Vite apps and emails need a second system |
| A translation platform (Crowdin, Lokalise) | Tools for translators, but paid, and it doesn't wire up your code |
Bones wires every surface to one library, with the catalogs as JSON files in the repo.
Go deeper
- i18next — why this library.
- Internationalization — adding and translating a string.