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.
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/Chicagois used in both the US and... only the US, but with multiple statesEurope/Berlincovers Germany, but also historical default for several Central European countriesAsia/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
- Always allow manual override — Users may be traveling or using VPNs
- Store the preference — Save the selected country in a cookie or account settings
- Combine signals — Use IP + locale + timezone for higher confidence
- Respect privacy — Do not require GPS for basic localization
- 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
Related Topics
Country Codes in IP Geolocation and Geo-Targeting
Industry
ISO 3166-1 Alpha-2 Codes — The Two-Letter Country Standard
Standards
Using Intl.DisplayNames for Country Names in JavaScript
Programming
Using Country Codes in REST APIs and GraphQL
Programming
EU Country Codes — All 27 European Union Member States
Industry