Skip to main content

LanguageSwitcher

The LanguageSwitcher component provides a button to toggle between Spanish (es) and English (en) versions of the site, maintaining the current page path across languages.

Import

---
import LanguageSwitcher from '@/components/LanguageSwitcher.astro';
---

Props

This component does not accept any props. It automatically detects the current language from the URL.

Usage

In Header

Header.astro
---
import LanguageSwitcher from '@/components/LanguageSwitcher.astro';
---

<header>
  <nav>
    <LanguageSwitcher />
  </nav>
</header>
The component is typically placed in the site header alongside navigation links and the theme toggle.

Features

Automatic Language Detection

The component uses the getLangFromUrl utility to detect the current language from the URL path:
import { getLangFromUrl } from "../utils/i18n";

const lang = getLangFromUrl(Astro.url);  // "es" or "en"

Path Preservation

When switching languages, the component preserves the current page path:
// Current: /es/palabras/chapin
// Switches to: /en/palabras/chapin

// Current: /en/indice
// Switches to: /es/indice
The algorithm removes the current language prefix and prepends the target language:
const basePath = currentPath.replace(/^\/(es|en)/, "") || "/";
const targetPath = `/${targetLang}${basePath}`;

Dynamic Label

The button displays the target language code:
  • When on Spanish pages: Shows “EN”
  • When on English pages: Shows “ES”

Accessibility

The component includes a localized aria-label:
aria-label={lang === "es" ? "Switch to English" : "Cambiar a Español"}
This provides screen reader users with clear information about what the button does.

Analytics Tracking

Language switches are tracked using Umami analytics with source and target language context:
data-umami-event="Language Switch"
data-umami-event-from={lang}
data-umami-event-to={targetLang}

View Transitions Support

The component includes a client-side script that updates after each navigation when using Astro’s View Transitions:
document.addEventListener("astro:page-load", updateLanguageSwitchers);
This ensures the switcher always points to the correct target language, even after client-side navigation.

Styling

The component uses utility classes for styling:
<a
  class="language-switcher border-theme bg-card text-theme hover:bg-card-hover hover:border-primary-light inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-all"
>
  <Globe size="18" />
  <span>{lang === "es" ? "EN" : "ES"}</span>
</a>
The styling includes:
  • Card background with hover effects
  • Theme-aware borders and text colors
  • Globe icon from Lucide
  • Smooth transitions

Implementation Details

URL Structure

Chapinismos uses language prefixes in all URLs:
/es/           # Spanish homepage
/en/           # English homepage
/es/palabras/  # Spanish word index
/en/palabras/  # English word index
The switcher assumes this structure and transforms URLs accordingly.

Multiple Switchers

The component supports multiple instances on the same page (e.g., desktop and mobile headers). The client-side script updates all instances:
const switchers = document.querySelectorAll(".language-switcher");
switchers.forEach((switcher) => {
  switcher.setAttribute("href", targetPath);
});

Complete Example

Base.astro
---
import LanguageSwitcher from '@/components/LanguageSwitcher.astro';
import ThemeToggle from '@/components/ThemeToggle.astro';
import { getLangFromUrl } from '@/utils/i18n';

const lang = getLangFromUrl(Astro.url);
---

<!DOCTYPE html>
<html lang={lang}>
  <head>
    <meta charset="UTF-8" />
    <title>Chapinismos</title>
  </head>
  <body>
    <header>
      <nav class="flex items-center gap-4">
        <a href={`/${lang}/`}>Home</a>
        <a href={`/${lang}/buscar`}>Search</a>
        <a href={`/${lang}/indice`}>Index</a>
        
        <div class="flex items-center gap-2 ml-auto">
          <LanguageSwitcher />
          <ThemeToggle />
        </div>
      </nav>
    </header>
    
    <main>
      <slot />
    </main>
  </body>
</html>

Edge Cases

Root Path

When on the language root (e.g., /es/ or /en/), the switcher correctly handles the base path:
const basePath = currentPath.replace(/^\/(es|en)/, "") || "/";
// Result: "/"
This ensures switching from /es/ goes to /en/, not /en//.

Missing Content

The component does not check whether content exists in the target language. If a word exists in Spanish but not in English, the switcher will still link to the English version, which may result in a 404.
For production use, consider adding validation to check if the target language version exists before rendering the switcher link.
  • Header - Includes the language switcher in the navigation bar
  • i18n Utilities - Language detection and translation utilities

Internationalization

For a complete guide on how the bilingual system works, see Internationalization.

Build docs developers (and LLMs) love