Country Codes for Geolocation and Locale Detection

Practical guide to detecting a user's country from IP, browser locale, and timezone for content localization, currency selection, and compliance.

Historical

Detailed Explanation

Multi-Signal Country Detection

Reliable country detection combines multiple signals rather than relying on a single source. Each signal has different accuracy and availability characteristics.

Detection Signals

Signal Accuracy Availability Example
IP geolocation 95-99% Server-side CF-IPCountry: JP
Browser locale High (user-set) Client-side navigator.language = "ja-JP"
Timezone ~90% Client-side Asia/Tokyo → JP
Accept-Language High (user-set) Both Accept-Language: ja-JP,ja;q=0.9
GPS (mobile) Very high Permission needed Lat/Long → Reverse geocode

Implementation Pattern

function detectCountry(req) {
  // 1. Explicit user preference (highest priority)
  const saved = getCookie('preferred_country');
  if (saved) return saved;

  // 2. CDN geolocation header
  const cfCountry = req.headers['cf-ipcountry'];
  if (cfCountry && cfCountry !== 'XX') return cfCountry;

  // 3. Accept-Language header
  const lang = req.headers['accept-language'];
  const match = lang?.match(/[a-z]{2}-([A-Z]{2})/);
  if (match) return match[1];

  // 4. Default
  return 'US';
}

Timezone to Country Mapping

Many IANA timezones map to a single country:

const TZ_TO_COUNTRY = {
  'Asia/Tokyo': 'JP',
  'Asia/Seoul': 'KR',
  'Europe/London': 'GB',
  'Europe/Berlin': 'DE',
  'Europe/Paris': 'FR',
  'America/New_York': 'US',
  'America/Toronto': 'CA',
  'Australia/Sydney': 'AU',
  'Pacific/Auckland': 'NZ',
};

function countryFromTimezone() {
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
  return TZ_TO_COUNTRY[tz] || null;
}

However, some timezones are shared:

  • America/Chicago is used in both the US and... only the US, but with multiple states
  • Europe/Berlin covers Germany, but also historical default for several Central European countries
  • Asia/Kolkata = India (IN), Asia/Colombo = Sri Lanka (LK) — these are different

Client-Side Detection

function detectCountryClient() {
  // Language-based
  const locale = navigator.language || navigator.userLanguage;
  const parts = locale.split('-');
  if (parts.length >= 2) {
    return parts[1].toUpperCase(); // "en-US" → "US"
  }

  // Timezone-based fallback
  return countryFromTimezone();
}

Best Practices

  1. Always allow manual override — Users may be traveling or using VPNs
  2. Store the preference — Save the selected country in a cookie or account settings
  3. Combine signals — Use IP + locale + timezone for higher confidence
  4. Respect privacy — Do not require GPS for basic localization
  5. Handle edge cases — VPN users, proxies, shared IPs (cloud, corporate)

Use Case

A news website detects the user's country from the Cloudflare CF-IPCountry header on first visit, confirms it against the Accept-Language header, and saves the preference in a cookie. If the signals conflict (e.g., IP says DE but language is en-US), the site shows a country selector banner.

Try It — Country Code Reference

Open full tool