Repository navigation
Internationalisation
How to run AstroBaaS in more than one language. This page covers locale configuration, localised URLs, how content and the catalogue are translated, the Translations screen, right-to-left languages, the admin interface language and hreflang. It is for operators setting up a multilingual site and for developers building a multilingual front end.
Languages are set with two environment variables:
SITE_LOCALES=el,en,de # the first entry is the default...
SITE_DEFAULT_LOCALE=el # ...unless this names another configured locale| Variable | Meaning |
|---|---|
SITE_LOCALES |
Comma- or space-separated locale codes. Each must look like en, de or pt-BR (a 2 or 3 letter language, optionally with a region or variant). Invalid codes and duplicates are dropped |
SITE_DEFAULT_LOCALE |
The default locale. Ignored if it is not in SITE_LOCALES
|
If SITE_LOCALES is unset or lists one locale, the site runs in single-language mode. Everything is en (or the one locale), URLs get no prefix, and the admin hides the language controls. Existing single-language installs behave exactly as before i18n existed.
Restart the server after changing these. The Translations screen reminds you of this on a single-language site. Source: src/lib/i18n.ts. For all environment variables, see Configuration.
The default locale is never prefixed. Every other locale gets a path prefix:
| URL | Serves |
|---|---|
/about |
The default locale |
/de/about |
German |
/de/blog |
German blog listing |
/de/blog/<slug> |
A German article |
Turning on a second language therefore cannot break an existing URL. AstroBaaS does not use one page directory per locale. The middleware strips the prefix, records the locale for the request, and serves the same templates with content in that language. An unknown first segment is left alone, so it is either a normal route or a 404.
Links on the site keep readers in their language:
- a link to a record (such as a post card) uses the record's own locale, so a German article always links to
/de/blog/...; - a link to a route (such as
/about,/contactor/blog) uses the reader's current locale.
A crawlable language switcher made of plain links, with no JavaScript, appears in the header. It marks which entries are real translations. The sitemap, RSS feed, canonical links and breadcrumbs all use localised URLs. Source: src/lib/locale-links.ts.
AstroBaaS uses two translation models, because posts and products are different kinds of thing.
Each language version is a separate post with its own slug, status, publish date and revision history.
| Field | Meaning |
|---|---|
locale |
The language this record is written in. Records without one count as the default locale |
translation_of |
Links translations together. It is conventionally the id of the original post, and every translation in the set shares the same value |
In the editor, a multilingual site shows Language and Translation of (the original post's id) under Publish settings. Leave Translation of empty for an original. Linked posts show up on the Translations screen and get hreflang tags.
Over the API, GET /api/posts?locale=de returns one language. An unknown locale falls back to the default, so a typo never returns an empty list. Search can be narrowed by locale too. See Headless and REST API.
A product is one physical thing with one price and one stock count, so it is not copied per language. Products, brands, product categories and shipping methods each carry an i18n sidecar that holds translated labels for each locale. A request in a locale gets those translations in place of the default text, field by field. A blank translation is not applied, so a half-translated product shows the default text for the fields nobody translated. Source: src/lib/i18n/catalogue-translations.ts. See Commerce.
These four settings can have a value per locale:
| Setting |
|---|
site_title |
site_tagline |
site_author_name |
maintenance_message |
The translations live in one setting, site_i18n, shaped like { "de": { "site_title": "…" } }. They are used by the page <title> and header, the RSS feed and the Open Graph image. A blank value falls back to the default. There is no dedicated form for them in Settings, so set them through the settings API (/api/settings/update). Transactional emails are not localised this way, because an email has no request locale. Their wording is edited once per install under Email wording. Source: src/lib/settings-i18n.ts.
The legal page library ships each template in English, Greek and German. See Content and media.
/admin/translations is open to admins, editors, authors and managers. It shows what is still untranslated in each configured locale, in two sections:
- Articles and pages: records that exist in the default locale but have no translation linked to them in this locale, with a percentage and examples;
- Products: products with no translation for the locale.
A third box, Half translated, lists products that have a translation for a language but not for every field. For example, a German name above an English description. Nothing else reports this case.
On a single-language site the screen explains how to set SITE_LOCALES instead. Source: src/lib/i18n/status.ts.
Writing direction comes from the locale. There is no separate setting to forget. These languages are right-to-left:
ar (Arabic), he (Hebrew), fa (Persian), ur (Urdu), ps (Pashto), sd (Sindhi), ug (Uyghur), yi (Yiddish), dv (Divehi), ckb (Sorani Kurdish), syr (Syriac), smp (Samaritan), mid (Mandaic), nqo (N'Ko).
Only the language part of the tag counts, so ar, ar-EG and ar_SA are all right-to-left. Every page's <html> element gets lang and dir for the request's locale.
The admin uses left/right Tailwind utilities. src/styles/global.css flips them under [dir="rtl"], and node scripts/gen-rtl.mjs --check (part of the test suite) fails if a utility in use is missing its flip. The admin's dir follows the locale of the URL, and admin URLs have no locale prefix, so the admin runs right-to-left when the site's default locale is right-to-left.
The flip list covers the Tailwind utilities that core uses. A theme's own stylesheet has to handle dir="rtl" itself. See Themes. Source: src/lib/i18n/direction.ts.
The admin's own labels are separate from your content language. A Greek editor writing German product copy can still have Greek buttons.
| Catalogue | Bundled |
|---|---|
English (en) |
Yes (the base language) |
Greek (el) |
Yes |
German (de) |
Yes |
For each request, the admin language is the first of these that is both listed in SITE_LOCALES and has a catalogue:
- the staff member's saved
localepreference, if their account has one; - the browser's
Accept-Languageheader, trying the full tag and then the language alone; - the site's default locale;
- English.
In practice, each person gets their browser's language if the site serves it. Shipping a catalogue does not switch a language on. Only SITE_LOCALES does that. Plugins can register more admin catalogues with registerCatalogue(), which is how a language pack would work. A missing string falls back to English. Sources: src/lib/i18n/resolve.ts, src/locales/.
The admin has no screen yet for saving a personal language preference. The catalogue already contains the strings for an "Admin language" picker, but no screen uses them in this release.
On a multilingual site, each post, page and blog listing emits <link rel="alternate" hreflang="…"> tags for its whole translation set, including itself, plus x-default. x-default points at the default-locale URL, which is also the unprefixed one. The set is the original post plus every post whose translation_of points at it.
Search engines ignore hreflang unless every page in a set lists every other page, so AstroBaaS always emits complete sets. A single-language site emits no hreflang tags at all. Source: src/lib/hreflang.ts.
- Add the code to
SITE_LOCALESand restart. - Translate posts and pages: create a post in the new language and put the original's id in Translation of.
- Translate products, brands and categories through their
i18nfields. - If needed, set per-locale values for the site title and tagline in
site_i18n. - Check Admin → Translations until the gaps are closed.
AstroBaaS is free software under GPL-3.0-or-later. This wiki describes the latest release; the repository and its CHANGELOG are the final word. Found a mistake here? Open an issue.
Getting started
Using the CMS
Building with it
Extending
Commerce and email
Operating
Project