Get Country Utility

Teecod3

View Profile
13 views
Jan 03, 2026
interface CountryInfo {
  /** ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB', 'NG') */
  countryCode: string;
  /** Full country name */
  countryName?: string;
  /** Timezone detected */
  timezone?: string;
  /** Browser language */
  language?: string;
  /** Detection method used */
  detectionMethod: 'timezone' | 'language' | 'api' | 'unknown';
}

// Timezone to country code mapping (common timezones)
const TIMEZONE_TO_COUNTRY: Record<string, string> = {
  'America/New_York': 'US',
  'America/Chicago': 'US',
  'America/Denver': 'US',
  'America/Los_Angeles': 'US',
  'America/Phoenix': 'US',
  'America/Anchorage': 'US',
  'America/Toronto': 'CA',
  'America/Vancouver': 'CA',
  'Europe/London': 'GB',
  'Europe/Paris': 'FR',
  'Europe/Berlin': 'DE',
  'Europe/Rome': 'IT',
  'Europe/Madrid': 'ES',
  'Europe/Amsterdam': 'NL',
  'Europe/Brussels': 'BE',
  'Europe/Vienna': 'AT',
  'Europe/Stockholm': 'SE',
  'Europe/Copenhagen': 'DK',
  'Europe/Oslo': 'NO',
  'Europe/Helsinki': 'FI',
  'Europe/Warsaw': 'PL',
  'Europe/Prague': 'CZ',
  'Europe/Budapest': 'HU',
  'Europe/Bucharest': 'RO',
  'Europe/Athens': 'GR',
  'Europe/Lisbon': 'PT',
  'Europe/Dublin': 'IE',
  'Europe/Zurich': 'CH',
  'Europe/Moscow': 'RU',
  'Asia/Dubai': 'AE',
  'Asia/Tokyo': 'JP',
  'Asia/Seoul': 'KR',
  'Asia/Shanghai': 'CN',
  'Asia/Hong_Kong': 'HK',
  'Asia/Singapore': 'SG',
  'Asia/Bangkok': 'TH',
  'Asia/Jakarta': 'ID',
  'Asia/Manila': 'PH',
  'Asia/Kuala_Lumpur': 'MY',
  'Asia/Kolkata': 'IN',
  'Asia/Karachi': 'PK',
  'Asia/Dhaka': 'BD',
  'Asia/Tehran': 'IR',
  'Asia/Baghdad': 'IQ',
  'Asia/Riyadh': 'SA',
  'Asia/Tel_Aviv': 'IL',
  'Asia/Istanbul': 'TR',
  'Africa/Cairo': 'EG',
  'Africa/Johannesburg': 'ZA',
  'Africa/Lagos': 'NG',
  'Africa/Nairobi': 'KE',
  'Africa/Accra': 'GH',
  'Africa/Casablanca': 'MA',
  'Africa/Algiers': 'DZ',
  'Australia/Sydney': 'AU',
  'Australia/Melbourne': 'AU',
  'Australia/Brisbane': 'AU',
  'Australia/Perth': 'AU',
  'Pacific/Auckland': 'NZ',
  'America/Sao_Paulo': 'BR',
  'America/Buenos_Aires': 'AR',
  'America/Santiago': 'CL',
  'America/Lima': 'PE',
  'America/Bogota': 'CO',
  'America/Mexico_City': 'MX',
};

// Language code to country code mapping
const LANGUAGE_TO_COUNTRY: Record<string, string> = {
  'en-US': 'US',
  'en-GB': 'GB',
  'en-CA': 'CA',
  'en-AU': 'AU',
  'en-NZ': 'NZ',
  'en-IE': 'IE',
  'fr-FR': 'FR',
  'fr-CA': 'CA',
  'de-DE': 'DE',
  'de-AT': 'AT',
  'de-CH': 'CH',
  'es-ES': 'ES',
  'es-MX': 'MX',
  'es-AR': 'AR',
  'it-IT': 'IT',
  'pt-BR': 'BR',
  'pt-PT': 'PT',
  'ja-JP': 'JP',
  'ko-KR': 'KR',
  'zh-CN': 'CN',
  'zh-TW': 'TW',
  'zh-HK': 'HK',
  'ru-RU': 'RU',
  'ar-SA': 'SA',
  'ar-EG': 'EG',
  'hi-IN': 'IN',
  'th-TH': 'TH',
  'vi-VN': 'VN',
  'id-ID': 'ID',
  'tr-TR': 'TR',
  'pl-PL': 'PL',
  'nl-NL': 'NL',
  'sv-SE': 'SE',
  'no-NO': 'NO',
  'da-DK': 'DK',
  'fi-FI': 'FI',
};

/**
 * Gets the country code from browser timezone
 * @returns string | null - ISO 3166-1 alpha-2 country code or null
 */
function getCountryFromTimezone(): string | null {
  try {
    if (typeof Intl === 'undefined' || !Intl.DateTimeFormat) {
      return null;
    }

    const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

    if (!timezone) {
      return null;
    }

    return TIMEZONE_TO_COUNTRY[timezone] || null;
  } catch (error) {
    console.warn('Failed to detect country from timezone:', error);
    return null;
  }
}

/**
 * Gets the country code from browser language
 * @returns string | null - ISO 3166-1 alpha-2 country code or null
 */
function getCountryFromLanguage(): string | null {
  try {
    if (typeof navigator === 'undefined') {
      return null;
    }

    const language =
      navigator.language || (navigator as UnknownObject).userLanguage;

    if (!language) {
      return null;
    }

    // Try exact match first
    if (LANGUAGE_TO_COUNTRY[language]) {
      return LANGUAGE_TO_COUNTRY[language];
    }

    // Try language code only (e.g., 'en' from 'en-US')
    const langCode = language.split('-')[0];

    // Some basic mappings for language-only codes
    const langOnlyMap: Record<string, string> = {
      en: 'US',
      fr: 'FR',
      de: 'DE',
      es: 'ES',
      it: 'IT',
      pt: 'BR',
      ja: 'JP',
      ko: 'KR',
      zh: 'CN',
      ru: 'RU',
      ar: 'SA',
      hi: 'IN',
    };

    return langOnlyMap[langCode] || null;
  } catch (error) {
    console.warn('Failed to detect country from language:', error);
    return null;
  }
}

/**
 * Gets the browser's country using multiple detection methods
 * @returns string - ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB', 'NG')
 */
export function getCountry(): string {
  // Try timezone first (most reliable)
  const countryFromTimezone = getCountryFromTimezone();
  if (countryFromTimezone) {
    return countryFromTimezone;
  }

  // Try language as fallback
  const countryFromLanguage = getCountryFromLanguage();
  if (countryFromLanguage) {
    return countryFromLanguage;
  }

  // Default to 'US' if nothing works
  return 'US';
}

/**
 * Gets detailed country information using multiple detection methods
 * @returns CountryInfo - Detailed country information
 */
export function getCountryInfo(): CountryInfo {
  const timezone =
    typeof Intl !== 'undefined' && Intl.DateTimeFormat
      ? Intl.DateTimeFormat().resolvedOptions().timeZone
      : undefined;

  const language =
    typeof navigator !== 'undefined' ? navigator.language : undefined;

  // Try timezone first
  const countryFromTimezone = getCountryFromTimezone();
  if (countryFromTimezone) {
    return {
      countryCode: countryFromTimezone,
      timezone,
      language,
      detectionMethod: 'timezone',
    };
  }

  // Try language as fallback
  const countryFromLanguage = getCountryFromLanguage();
  if (countryFromLanguage) {
    return {
      countryCode: countryFromLanguage,
      timezone,
      language,
      detectionMethod: 'language',
    };
  }

  // Unknown
  return {
    countryCode: 'US',
    timezone,
    language,
    detectionMethod: 'unknown',
  };
}

/**
 * Checks if the user is from a specific country
 * @param countryCode - ISO 3166-1 alpha-2 country code to check
 * @returns boolean
 */
export function isCountry(countryCode: string): boolean {
  const userCountry = getCountry();
  return userCountry.toLowerCase() === countryCode.toLowerCase();
}

/**
 * Checks if the user is from any of the specified countries
 * @param countryCodes - Array of ISO 3166-1 alpha-2 country codes
 * @returns boolean
 */
export function isFromCountries(countryCodes: string[]): boolean {
  const userCountry = getCountry().toLowerCase();
  return countryCodes.some((code) => code.toLowerCase() === userCountry);
}