Skip to content

i18n URL Routing

The frontend supports translated URL path segments per market language, with automatic middleware rewrites, 301 redirects, and hreflang tag generation.

  1. File routes use English segment names (e.g., pages/[market]/destinations/[country].astro)
  2. Middleware rewrites translated URLs to the English file route (URL bar stays translated)
  3. 301 redirects English URLs to translated versions for markets that have translations
  4. hreflang tags are generated automatically for all markets via SeoHead.astro

Source of truth: frontend/src/shared/config/i18n.ts (SEGMENT_TRANSLATIONS)

English (file route)ES
destinationsdestinos
regionsregiones
about-ussobre-nosotros
termsterminos-condiciones
privacy-policypolitica-privacidad
collectionscolecciones

Markets without a translation entry (DE, US, UK, CA) use the English segment as-is. Only the first path segment (the page type) is translated — slugs like country names stay the same across all markets.

Source: frontend/src/middleware.ts

For a request like /es/destinos/argentina:

  1. Middleware identifies the market (es) and first content segment (destinos)
  2. Reverse-translates destinos to destinations using REVERSE_TRANSLATIONS
  3. Stashes original path in context.locals.originalPath (used by SeoHead.astro for hreflang)
  4. Rewrites to /es/destinations/argentina (Astro file route)

For a request like /es/destinations/argentina (English segment on a translated market):

  1. Middleware detects that destinations has a Spanish translation destinos
  2. Issues a 301 redirect to /es/destinos/argentina

This ensures each page has one canonical URL per market.

Always use localizedPath() for internal links. Never hardcode translated segments.

Parameters:

  • market — Market code (es, de, us, uk, ca)
  • englishPath — Path with English segments, without market prefix (e.g., /destinations/argentina)

Examples:

  • localizedPath("es", "/destinations/argentina") returns /es/destinos/argentina
  • localizedPath("us", "/destinations/argentina") returns /us/destinations/argentina
  • localizedPath("es", "/about-us") returns /es/sobre-nosotros

Source: frontend/src/shared/config/i18n.ts

  • Product pages (PDP): URLs come from the backend API via tour_path_slugs. The catch-all route [...path].astro handles these.
  • Checkout pages: /{market}/checkout/{offerId}/... — not translated.
  • Auth/account pages: /login, /my-account — not market-scoped.

The SeoHead.astro component generates hreflang and canonical tags automatically. It is included in PublicLayout and MainLayout.

For every page, it outputs:

  • <link rel="canonical"> for the current market’s translated URL
  • <link rel="alternate" hreflang="es-ES"> pointing to the ES translated URL
  • <link rel="alternate" hreflang="de-DE"> pointing to the DE URL
  • <link rel="alternate" hreflang="en-US"> pointing to the US URL
  • <link rel="alternate" hreflang="en-GB"> pointing to the UK URL
  • <link rel="alternate" hreflang="en-CA"> pointing to the CA URL
  • <link rel="alternate" hreflang="x-default"> pointing to the ES URL (international fallback)

All URLs use the production domain https://byvolare.com.

Source: frontend/src/shared/ui/SeoHead.astro

MarketHTML langAPI langURL segment behavior
eses-ESesTranslated (Spanish segments)
dede-DEdeEnglish fallback
usen-USenEnglish
uken-GBenEnglish
caen-CAenEnglish

Source: frontend/src/shared/config/markets.ts

  1. Create the file route in English: pages/[market]/new-page.astro
  2. Add a translation to SEGMENT_TRANSLATIONS in frontend/src/shared/config/i18n.ts if the page needs translated URLs
  3. Middleware automatically handles rewrites and 301 redirects
  4. Use localizedPath() in all links to the new page
  5. hreflang tags generate automatically via SeoHead.astro

Adding a new market requires more than updating SEGMENT_TRANSLATIONS.

Required steps:

  1. Add the market code to SUPPORTED_MARKETS in frontend/src/shared/config/markets.ts
  2. Add its API language to MARKET_API_LANG
  3. Add its HTML hreflang value to MARKET_HTML_LANG
  4. Add translated first-segment entries to SEGMENT_TRANSLATIONS if the market should not use English fallback segments
  5. Search for page-level market allowlists or assumptions outside shared config before finishing

The translation reverse-lookup map builds automatically from SEGMENT_TRANSLATIONS, so no extra middleware code is needed for first-segment rewrites once the market and translation config are in place.

  • Frontend Overview — full frontend architecture
  • Market config: frontend/src/shared/config/markets.ts
  • i18n config: frontend/src/shared/config/i18n.ts
  • Middleware: frontend/src/middleware.ts
  • SEO component: frontend/src/shared/ui/SeoHead.astro