# Detect Language, Not Location

**MUST** · **ID:** `content-language-detection` · **Category:** content
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/content-language-detection

> MUST: Pick the UI language from `Accept-Language` / `navigator.languages`, never from IP geolocation (VPNs, travel, and multilingual countries break that assumption), and still expose an explicit language switcher as the override.

Choose the UI language from Accept-Language or navigator.languages, never from IP geolocation

Where a request originates says nothing about what the reader can read: VPNs, travel, corporate proxies, and multilingual countries all break the IP-to-language assumption. Accept-Language and navigator.languages carry the preference the user actually configured, ordered by priority — read those, and still expose an explicit language switcher as the override.

## Bad — do not do this

`content-language-detection-bad`

```tsx
export function LanguageDetectionBad() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <p className="text-xs text-muted-foreground font-mono">
          navigator.languages = ["en-US", "en"]
          <br />
          IP → Frankfurt, DE → serve <span className="text-error">de-DE</span>
        </p>
        <div lang="de" className="pt-1 border-t border-border space-y-1">
          <h4 className="font-medium text-foreground">Bereitstellung fehlgeschlagen</h4>
          <p className="text-sm text-muted-foreground">
            Ihre Änderungen wurden gespeichert. Überprüfen Sie die
            Erstellungsprotokolle.
          </p>
        </div>
      </div>
      <p className="text-xs text-error">
        An English speaker on a VPN, a business trip, or a proxy gets a UI they
        can't read — location is not language
      </p>
    </div>
  );
}
```

## Good — do this

`content-language-detection-good`

```tsx
export function LanguageDetectionGood() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <p className="text-xs text-muted-foreground font-mono">
          Accept-Language: en-US, en;q=0.9
          <br />
          IP → Frankfurt, DE → <span className="text-success">ignored</span>
        </p>
        <div lang="en" className="pt-1 border-t border-border space-y-1">
          <h4 className="font-medium text-foreground">Deployment Failed</h4>
          <p className="text-sm text-muted-foreground">
            Your changes are saved. Check the build logs.
          </p>
        </div>
        <label htmlFor="lang-good" className="block text-xs text-muted-foreground">
          Language
          <select
            id="lang-good"
            defaultValue="en"
            className="mt-1 w-full px-2 py-1.5 text-sm bg-background text-foreground border border-border rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
          >
            <option value="en">English</option>
            <option value="de">Deutsch</option>
          </select>
        </label>
      </div>
      <p className="text-xs text-success">
        The user's stated language preference wins, with an explicit override —
        no guessing from IP
      </p>
    </div>
  );
}
```

## References

- [MDN: Accept-Language](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language)
- [MDN: navigator.languages](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages)
- [W3C i18n: Language priorities](https://www.w3.org/International/questions/qa-lang-priorities)
