Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 2 min read

Building a Locale Switcher That Respects hreflang and User Choice

Language switchers look trivial: a dropdown with flags. In practice they are where many multilingual sites break their own SEO and annoy users at the same time. This post outlines a switcher design that keeps search engi

Language switchers look trivial: a dropdown with flags. In practice they are where many multilingual sites break their own SEO and annoy users at the same time. This post outlines a switcher design that keeps search engines and humans happy, with small code samples.

Principles

  1. Every language version has its own URL (/en/, /de/, /ja/).
  2. The switcher links to the equivalent page, not the homepage of the other language.
  3. Automatic detection suggests, it never forces a redirect.
  4. A user's explicit choice is remembered.

Generate links from the same map as hreflang

Keep one data structure that lists equivalents for each page, and use it both for <link rel="alternate" hreflang> tags and for the switcher. If they come from different sources, they will drift.

// alternates is injected server-side: { en: "/en/pricing", de: "/de/preise", ja: "/ja/pricing" }
function renderSwitcher(alternates, current) {
  return Object.entries(alternates).map(([lang, href]) =>
    `<a href="${href}" hreflang="${lang}" lang="${lang}"${lang === current ? ' aria-current="true"' : ''}>${LABELS[lang]}</a>`
  ).join("");
}

Use real anchor tags with href. Switchers built from buttons and JavaScript click handlers are invisible to crawlers.

Labels: language names, not flags

Flags represent countries, not languages. Spanish speakers in Mexico do not want a Spanish flag. Show each language in its own script: English, Deutsch, ζ—₯本θͺž, EspaΓ±ol.

Detection as a banner, not a redirect

If the browser language differs from the page language, show a small banner: "This page is available in Deutsch. Switch?" Redirecting automatically based on Accept-Language or IP can prevent crawlers from seeing other versions and traps travellers.

const pref = localStorage.getItem("lang");
const browser = navigator.language.slice(0, 2);
if (!pref && browser !== document.documentElement.lang && alternates[browser]) {
  showBanner(browser, alternates[browser]);
}

Remember the choice

When a user clicks a language, store it in localStorage or a cookie. On future visits, still serve the URL they asked for; just use the stored preference to decide whether to show the banner.

Missing translations

If a page has no equivalent in a language, either hide that option or link to the closest parent page with a note. Never link to a 404, and never list it in hreflang.

Test it

Crawl the site and check that every hreflang target returns 200, lists the source page back, and appears in the switcher. A few lines of Python with requests and a regex is enough for small sites.

More detail on URL structures and common hreflang errors is in our hreflang implementation guide.

Published by PandaWM, an overseas marketing team based in Hong Kong.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.