Borrissol serves its content in four languages — Catalan (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/constanza101/borrissol/llms.txt
Use this file to discover all available pages before exploring further.
ca), Spanish (es), English (en), and French (fr) — without duplicating any page files. Catalan is the site’s default locale and lives at the root URL with no prefix (e.g. /, /gallery, /blog). Every other language gets its own two-letter prefix (/es, /en, /fr). The entire approach is powered by Astro 6’s built-in i18n routing combined with a single translation catalog in src/i18n/ui.ts and a set of utility functions in src/i18n/utils.ts.
Routing architecture
Astro’s i18n config inastro.config.mjs declares the four locales and sets prefixDefaultLocale: false so Catalan URLs stay clean:
astro.config.mjs
src/pages/[lang]/ (e.g. src/pages/[lang]/index.astro). That file calls getStaticPaths() to emit one static route per non-default locale. The Catalan version of every page has a twin file directly in src/pages/ (e.g. src/pages/index.astro) that handles the root route. This means there is exactly one .astro file per page, not one per language.
The translation catalog (src/i18n/ui.ts)
All copy for every language lives in one file. There are no per-language JSON files to keep in sync.
src/i18n/ui.ts
ui object holds every translation key for Spanish, English, and French. Catalan translations are inlined directly into components through the fallback mechanism (the default locale’s keys are used when a language’s entry is missing).
The notFound object is intentionally separated from ui. The 404 page is built once as a static HTML file in the default locale, so it serializes all four languages and picks the correct one at runtime from the URL:
src/i18n/ui.ts
Utility functions (src/i18n/utils.ts)
Four helpers cover every i18n use case across the codebase.
| Function | Purpose |
|---|---|
getLangFromUrl(url) | Extracts locale from the URL path; falls back to defaultLang |
useTranslations(lang) | Returns a t(key) function bound to the given locale |
localizedPath(lang, path) | Prefixes a path for non-default locales |
getAlternatePath(url, targetLang) | Computes the equivalent URL in another locale |
nonDefaultLangPaths() | Returns getStaticPaths entries for all non-default locales |
getAlternatePath does a URL-prefix swap instead of a lookup table, so it works for every route automatically — including future ones:
localeMap in utils.ts maps short codes to BCP 47 tags for Open Graph and the HTML lang attribute:
Adding a new translation key
Open `src/i18n/ui.ts`
Add the key to all four language objects inside the
ui export. Catalan is the fallback, so if you add it only to ca other locales will silently display the Catalan text — always add to all four.useTranslations falls back first to the default locale’s value, then to the raw key string. A missing translation will never crash the build, but it will surface Catalan text in other locales — treat missing keys as bugs.Hreflang tags
Every page gets<link rel="alternate" hreflang="…"> tags in its <head> via Seo.astro. The sitemap generated by @astrojs/sitemap also emits hreflang cross-references inside the sitemap XML, reinforcing the signal for search engines:
astro.config.mjs
301 redirects
Theredirects block in astro.config.mjs handles two categories of legacy or incorrect URLs:
Legacy /ca/* URLs — Catalan was historically served under a /ca prefix. Those paths no longer exist; all are redirected to the root equivalent:
astro.config.mjs
astro.config.mjs
Astro requires redirect destinations to match real routes, which is why each path is listed explicitly rather than using a wildcard pattern.
Language switcher
The language switcher is part ofNavbar.astro. It uses getAlternatePath to compute the target URL for each locale and preserves the URL hash via client-side JavaScript, so a visitor on /en#workshops lands on /es#workshops after switching to Spanish — not just /es.