33

I would like to be able to detect what country a visitor is from on my website, using PHP.

Please note that I'm not trying to use this as a security measure or for anything important, just changing the spelling of some words (Americans seems to believe that the word "enrolment" has 2 Ls.... crazy yanks), and perhaps to give a default option in a "Select your country" list.

As such, using a Geolocation database is a tad over-the-top and I really don't want to muck about with installing new PHP libraries just for this, so what's the easiest/simplest way to find what country a visitor is from?

nickf
  • 537,072
  • 198
  • 649
  • 721

12 Answers12

33

PHP provides a function since 5.3.0 to parse the $_SERVER['HTTP_ACCEPT_LANGUAGE'] variable into a locale.

$locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
echo $locale; // returns "en_US"

Documentation: https://www.php.net/manual/en/locale.acceptfromhttp.php

Kos
  • 4,890
  • 9
  • 38
  • 42
Trevor Goodyear
  • 486
  • 6
  • 10
  • This works fine if you just want the first two characters for the language. But if you want the full locale there are inconsistencies E.g. for Polish both Edge and Firefox currently give just two characters `pl` But for Vietnamese Edge gives `vi` while Firefox gives `vi_VN` – zsalya Sep 08 '22 at 09:47
21

Not guaranteed, but most browsers submit an Accept-Language HTTP header that specifies en-us if they're from the US. Some older browsers only said they are en, though. And not all machines are set up correctly to indicate which locale they prefer. But it's a good first guess.

English-UK based-users usually set their system or user locale to English-UK, which in default browser configurations should result in en-gb as the Accept Language header. (An earlier version of this said en-uk; that was a typo, sorry.) Other countries also have en locales, such as en-za (south africa), and, primarily theoretically, combinations like en-jp are also possible.

Geo-IP based guesses will less likely be correct on the preferred language/locale, however. Google thinks that content-negotiation based on IP address geolocation makes sense, which really annoys me when I'm in Japan or Korea...

JasonTrue
  • 19,244
  • 4
  • 34
  • 61
  • 5
    The client’s preferred language is not an indicator for his locale. – Gumbo Aug 14 '10 at 10:38
  • 2
    Depends on what you mean by "locale". Presuming you don't mean "location", but are using it in the software internationalization sense: The Accept-Language parameter can and now usually does specify a complete locale in each preferred language specified, since the most common use of the second half of the language field specifies the country (rather than a language variant). The combination of language plus country corresponds neatly to locale-specific conventions for date, time, number and currency conventions. Without the country information, obviously it isn't complete enough to do that. – JasonTrue Aug 14 '10 at 19:40
  • 10
    I HATE when websites show me a language based on locale and not on browser config. You have no idea how frustrating this is until you live in another country. If I arrive at a website and I can't find the option to change the language in 5 seconds, I'm out of there. – Sandy Nov 26 '11 at 20:56
  • IKR when I was in japan I even insisted to microsoft that I wanted hotmail.ca, but no I get hotmail.co.jp – awiebe Feb 20 '12 at 18:14
13

You can check out the HTTP_ACCEPT_LANGUAGE header (from $_SERVER) that most browsers will send.

Take a look at Zend_Locale for an example, or maybe you might even want to use the lib.

Kos
  • 4,890
  • 9
  • 38
  • 42
chroder
  • 4,393
  • 2
  • 27
  • 42
7

You can do some IP comparaison without having a whole library to do it.

Solution #1

Use an API, this way nothing is needed from your side. This is a web API that let you know the country:

 Example: http://api.hostip.info/get_html.php?ip=12.215.42.19
 Return : Country: UNITED STATES (US)

Solution #2

But, Have you think to use the browser agent language? You might be able to know the type of english from it.

Solution #3

This website called BlockCountry let you have a list of IP by country. Of course, you do not want to block, but you can use the list of IP and compare them (get all US IP...) this might not be accurate...

Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341
  • 2
    do you have more information about http://api.hostip.info/. who runs it/how reliable it is in performance and quality of data? – Simon_Weaver Nov 25 '08 at 08:02
  • 1
    Looks not very up to date. I just tried three different IPs from my country and it failed to locate country for any of them! – Primoz Rome Apr 10 '12 at 16:55
  • hostip.info does not look reliable at all. I find that http://ipinfo.io/ has much better info but if you need to look up lots of IPs, you have to pay. – Mikko Rantalainen Jul 07 '15 at 11:19
2

Given your stated purpose, the Accept-Language header is a more suitable solution than IP-based geolocation. Indeed, it's precisely the intended purpose of Accept-Language.

Dave Sherohman
  • 45,363
  • 14
  • 64
  • 102
  • 1
    The client’s preferred language is not an indicator for his locale. – Gumbo Aug 14 '10 at 10:40
  • 1
    True, but OP's stated reason for wanting the locale was "just changing the spelling of some words (Americans seems to believe that the word "enrolment" has 2 Ls.... crazy yanks)" - he wanted the locale so he could use it as a proxy for preferred language. – Dave Sherohman Aug 14 '10 at 10:58
  • 1
    See my comment above... I believe its best to use the client's preferred language than the language spoken at his location. – Sandy Nov 26 '11 at 21:01
2

Parse $_SERVER["HTTP_ACCEPT_LANGUAGE"] to get country and browser's locale.

Kos
  • 4,890
  • 9
  • 38
  • 42
Slipo
  • 37
  • 3
2

I use the HTTP_ACCEPT_LANGUAGE

    $localePreferences = explode(",",$_SERVER['HTTP_ACCEPT_LANGUAGE']);
    if(is_array($localePreferences) && count($localePreferences) > 0) {
       $browserLocale = $localePreferences[0];
       $_SESSION['browser_locale'] = $browserLocale;
    }
Mircea Stanciu
  • 3,675
  • 3
  • 34
  • 37
1

For identifying your visitors country I've used GeoIP extension, very simple to use.

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
0

One thing is which language viewer wants, second - which you can serve:

$SystemLocales = explode("\n", shell_exec('locale -a'));
$BrowserLocales = explode(",",str_replace("-","_",$_SERVER["HTTP_ACCEPT_LANGUAGE"])); // brosers use en-US, Linux uses en_US
for($i=0;$i<count($BrowserLocales);$i++) {         
  list($BrowserLocales[$i])=explode(";",$BrowserLocales[$i]); //trick for "en;q=0.8"
  for($j=0;$j<count($SystemLocales);$j++) {
    if ($BrowserLocales[$i]==substr($SystemLocales[$j],0,strlen($BrowserLocales[$i]))){
      setlocale(LC_ALL, $SystemLocales[$j]);
      break 2; // found and set, so no more check is needed
    }
  }
}

for example, mine system serves only:

  • C
  • POSIX
  • pl_PL.UTF-8

and my browser languages are: pl, en-US, en => so the only correct locale is pl_PL.UTF-8.

When no successful comparison is found - there's no setlocale at all.

Tomasz Brzezina
  • 1,452
  • 5
  • 21
  • 44
0

GeoIP extension is good choice.

BryanH
  • 5,826
  • 3
  • 34
  • 47
0

The http://countries.nerd.dk service is what I use for IP-to-country mapping. It works really well and being based on DNS, is cached well too.

You can also download the database for local use if you don't want to rely on an external service.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
0

Or you can do the following:

download 'geoip.dat' and geoip.inc from http://www.maxmind.com/app/geoip_country

in geoip.inc header you will find how to use it (eg. initialize and the rest...)

zale
  • 9
  • 1