Multilingual Static Sites: Folder Structure, hreflang and Language Switchers

Multilingual Static Sites: Folder Structure, hreflang and Language Switchers

hreflang tags HTML implementation, and language switcher markup that actually work on a static stack in 2025.

Key Takeaways

  • A locale-based folder structure (e.g. /en/, /de/, /fr/) gives each language a clean, crawlable URL path with no server-side logic required.
  • Every page in every locale must carry a complete set of hreflang attributes pointing to all other locales plus itself, including an x-default fallback.
  • Language switchers must link to the equivalent page in each locale, not just the foreign homepage, to avoid confusing both users and crawlers.
  • Bootstrap 5 dropdown components provide accessible, keyboard-navigable language switcher markup with minimal custom JavaScript.
  • An XML sitemap with hreflang annotations is a reliable alternative when you have many pages and maintaining inline <link> tags becomes error-prone.

Choosing a Folder Structure for a Multilingual Static Site

Static HTML sites have three realistic URL strategies for internationalisation: subdomains (de.example.com), subdirectories (example.com/de/), and separate domains (example.de). For most teams working with a single HTML template, subdirectories win. They consolidate domain authority, require no DNS changes, and work on any static host without configuration.

A practical folder layout for a three-language site looks like this:

project/
├── index.html          (redirect or x-default landing page)
├── en/
│   ├── index.html
│   ├── about.html
│   └── pricing.html
├── de/
│   ├── index.html
│   ├── about.html
│   └── pricing.html
└── fr/
    ├── index.html
    ├── about.html
    └── pricing.html

The root index.html can either perform a meta-refresh to the default locale or serve as the x-default page (more on that below). Keeping each locale in its own folder means every locale is independently deployable, cacheable, and auditable. It also maps cleanly to Netlify, GitHub Pages, Cloudflare Pages, and any S3-backed static host.

One naming convention to enforce immediately: use IETF language tags as folder names. Use en, de, fr, pt-br, zh-hans. Avoid invented abbreviations. Google expects IETF tags in hreflang values, so aligning your folder names with those tags prevents a whole class of copy-paste mistakes later.

Multilingual Static Sites: Folder Structure, hreflang and Language Switchers, abstract concept illustration

Writing Correct hreflang Tags in HTML

hreflang tags tell Google which version of a page to serve to which audience. They live in the <head> of every page as <link rel="alternate"> elements. The rules are strict:

  • Every page must reference all locales, including itself.
  • The relationship must be reciprocal. If /en/pricing.html points to /de/pricing.html, then /de/pricing.html must point back.
  • An x-default entry should point to the page that serves users whose language has no specific match, typically the root index.html or the English version.

A complete example for /en/pricing.html:

<head>
  <meta charset="UTF-8">
  <title>Pricing | Example</title>
  <link rel="canonical" href="https://example.com/en/pricing.html">
  <link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing.html">
  <link rel="alternate" hreflang="en" href="https://example.com/en/pricing.html">
  <link rel="alternate" hreflang="de" href="https://example.com/de/pricing.html">
  <link rel="alternate" hreflang="fr" href="https://example.com/fr/pricing.html">
</head>

The canonical tag and the self-referencing hreflang tag must point to the same absolute URL. Mixing absolute and relative URLs in these attributes is a common source of indexing errors. Always use absolute URLs with the protocol and domain.

On a large site with dozens of pages and many locales, maintaining these tags by hand in every file becomes error-prone fast. The scalable alternative is an XML sitemap with hreflang annotations. Google accepts hreflang declared inside a sitemap as equivalent to inline <link> tags. A build tool such as a Gulp task or a simple Node script can generate the sitemap automatically from your folder structure, which is covered in our guide to Gulp, Vite, and Webpack for HTML template workflows.

Setting the lang Attribute on Every Page

The hreflang attributes handle search engines. The lang attribute on the <html> element handles browsers, screen readers, and CSS font selection. Both are required and they must match.

<!-- English version -->
<html lang="en">

<!-- German version -->
<html lang="de">

<!-- Brazilian Portuguese version -->
<html lang="pt-BR">

A mismatch between the lang attribute and the page content triggers accessibility warnings in Lighthouse and may cause screen readers to pronounce text incorrectly. If you are adapting a Bootstrap 5 template for right-to-left languages such as Arabic or Hebrew, also add dir="rtl" to the <html> element. Bootstrap 5 ships an RTL stylesheet (bootstrap.rtl.min.css) that mirrors all directional utilities automatically.

hreflang tags html, abstract technical diagram

Language Switcher Markup with Bootstrap 5

A language switcher lets users navigate to the same page in a different locale. The most common mistake is linking switcher options to the foreign homepage instead of the equivalent page. A user on /en/pricing.html who clicks “Deutsch” should land on /de/pricing.html, not /de/index.html.

Bootstrap 5’s dropdown component provides accessible switcher markup without writing custom keyboard logic. Here is a production-ready example that fits inside a navbar:

<div class="dropdown">
  <button
    class="btn btn-sm btn-outline-secondary dropdown-toggle"
    type="button"
    id="languageSwitcher"
    data-bs-toggle="dropdown"
    aria-expanded="false"
    aria-label="Select language">
    EN
  </button>
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="languageSwitcher">
    <li><a class="dropdown-item active" href="/en/pricing.html" hreflang="en" lang="en">English</a></li>
    <li><a class="dropdown-item" href="/de/pricing.html" hreflang="de" lang="de">Deutsch</a></li>
    <li><a class="dropdown-item" href="/fr/pricing.html" hreflang="fr" lang="fr">Français</a></li>
  </ul>
</div>

A few details worth calling out: the hreflang and lang attributes on each anchor reinforce the language signal for assistive technologies. The active class on the current locale gives users a clear visual indicator. The aria-label on the button names the control for screen readers that would otherwise announce only the abbreviated text “EN”.

If your template uses CSS custom properties for theming, the switcher button inherits --cnvs-themecolor from the Canvas theme layer with no extra work. The same token-driven theming approach is explained in our post on theming one HTML template for multiple client brands with CSS variables.

Why You Should Avoid JavaScript-Only Language Detection

A common shortcut is to place a single index.html at the root and use JavaScript to read navigator.language, then redirect the user. This pattern has two serious problems.

First, Googlebot may not execute the redirect during its first crawl pass. The crawler sees only a blank or near-blank page, indexes nothing useful, and the hreflang annotations never get processed. Second, users with JavaScript disabled or on slow connections land on an empty page. Both problems are avoidable.

If you need locale detection at all, use a server-side mechanism. Netlify and Cloudflare Pages both support redirect rules based on the Accept-Language header. A Netlify _redirects file entry looks like this:

/ /de/index.html 302 Language=de
/ /fr/index.html 302 Language=fr
/ /en/index.html 302

The redirect is handled at the CDN edge before the browser parses any HTML, so Googlebot sees the actual locale page rather than a script. Pair this with the x-default hreflang pointing to the root URL and you have a technically sound setup.

Testing and Validating hreflang Implementation

After deploying, validate your implementation with these tools and checks:

  • Google Search Console. The “International Targeting” report under Legacy Tools flags hreflang errors including missing return tags and incorrect language codes.
  • hreflang Tags Testing Tool (Merkle). Crawls a URL and reports on all alternate links, reciprocal matches, and x-default status.
  • Screaming Frog SEO Spider. Can crawl the entire site and export a matrix of hreflang relationships, which makes spotting missing entries much faster than manual inspection.
  • Browser DevTools. Inspect the <head> on each locale page to confirm the lang attribute and canonical tags are correct for that locale, not copied verbatim from a template without substitution.

A frequent production bug: staging URLs end up in one or more hreflang attributes because someone copy-pasted the <head> block before updating the domain. Running Screaming Frog against the live domain before launch catches this in minutes.

For teams maintaining a large Bootstrap 5 template such as the Canvas HTML Template across multiple languages, keeping a shared head-includes.html partial per locale and assembling pages with a build step dramatically reduces the risk of mismatched tags. See our overview in The Complete Guide to Building Websites with Bootstrap 5 in 2026 for how to structure a template-based build pipeline.

Frequently Asked Questions

Yes. Even with two languages, Google needs the reciprocal hreflang annotations to understand which page to serve in which region. Without them, Google may index only one version or display the wrong locale in search results for users in a given country.

The x-default value designates the fallback page for users whose browser language does not match any of your specific locale pages. It is also used on language-selector pages where you present the user with a choice rather than automatically redirecting them.

Yes. Google accepts hreflang annotations inside an XML sitemap as equivalent to inline <link rel="alternate"> tags. This is often more practical for large sites because all locale relationships live in one file rather than being duplicated across every HTML page.

Use text labels, not flags alone. Flags represent countries, not languages. Portuguese is spoken in Brazil and Portugal; Spanish is spoken across more than 20 countries. A flag icon for “Spanish” is ambiguous and can feel exclusionary. If you use a flag for visual decoration, always pair it with the full language name as visible text.

Bootstrap 5 ships a dedicated RTL stylesheet, bootstrap.rtl.min.css, which mirrors all directional spacing, flex, and float utilities. Replace the standard Bootstrap CSS link with the RTL version for those locale pages, add dir="rtl" to the <html> element, and test all custom components since third-party plugins may need additional RTL overrides.

Looking for a production-ready Bootstrap 5 HTML template? Browse Canvas Template demos and find the perfect starting point for your next project.

If you’re building with the Canvas HTML Template and want to ship production-ready Bootstrap 5 layouts faster, try Canvas Builder free — the visual builder that exports clean Canvas-ready markup in minutes.

Skip the setup and build it free

Spin up a complete Bootstrap 5 site, blog included, with Canvas Builder. No coding, no cost.

Share:
Canvas Team
Canvas Team

Tutorials and tips for building beautiful Bootstrap 5 websites with the Canvas HTML Template and Canvas Builder.

More from the Canvas Blog