15

I need a code example or library which would parse Accept-Language header and return me preferred language. RFC2616 states that:

The Accept-Language request-header field is similar to Accept, but restricts the set of natural languages that are preferred as a response to the request. Language tags are defined in section 3.10.

   Accept-Language = "Accept-Language" ":"
                     1#( language-range [ ";" "q" "=" qvalue ] )
   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) 

Each language-range MAY be given an associated quality value which represents an estimate of the user's preference for the languages specified by that range. The quality value defaults to "q=1".

Further reading shows that there are too many "optional", "should", "may" and other turns of speech that prevent me from reinventing the wheel - all I want to know is what language user prefers, any browser answers this question billion times a day.

Any code snippet in any language (except Lisp and Assembler please) would be helpful.

Many thanks in advance!

andbi
  • 4,426
  • 5
  • 45
  • 70

3 Answers3

47

Solution:

namespace ConsoleApplication
{
    using System;
    using System.Linq;
    using System.Net.Http.Headers;

    class Program
    {
        static void Main(string[] args)
        {
            string header = "en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2";
            var languages = header.Split(',')
                .Select(StringWithQualityHeaderValue.Parse)
                .OrderByDescending(s => s.Quality.GetValueOrDefault(1));
        }
    }
}

Result:

enter image description here

weston
  • 54,145
  • 21
  • 145
  • 203
Zygimantas
  • 8,547
  • 7
  • 42
  • 54
3

Try this, its in PHP but using the same regex i'm sure its adaptable to any language :

$langs = array(); // used to store values

if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
    // break up string into pieces (languages and q factors)
    preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);

    if (count($lang_parse[1])) {
        // create a list like "en" => 0.8
        $langs = array_combine($lang_parse[1], $lang_parse[4]);

        // set default to 1 for any without q factor
        foreach ($langs as $lang => $val) {
            if ($val === '') $langs[$lang] = 1;
        }

        // sort list based on value 
        arsort($langs, SORT_NUMERIC);
    }
}

produces a sorted array with preferred language first :

Array
(
    [en-ca] => 1
    [en] => 0.8
    [en-us] => 0.6
    [de-de] => 0.4
    [de] => 0.2
)

From example ACCEPT_LANGUAGE header : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2

Working example here

Manse
  • 37,765
  • 10
  • 83
  • 108
  • Thanks, you're just in time - I was goggling at my stub method always returning "en" )) – andbi Mar 30 '12 at 13:40
1

Update for ASP.NET Core:

Microsoft has that built in by now. Use the AcceptLanguageHeaderRequestCultureProvider:

IApplicationBuilder builder = ...;
builder.UseRequestLocalization(options => options
  .AddInitialRequestCultureProvider(new AcceptLanguageHeaderRequestCultureProvider()));

you can then get the IRequestCultureFeature through the context:

// probably injected through your DI container
IHttpContextAccessor _accessor = ...;
await _accessor.HttpContext.Features
  .Get<IRequestCultureFeature>().Provider
  .DetermineProviderCultureResult(_accessor.HttpContext);

that returns a ProviderCultureResult?, you can then read the cultures (in form of IList<StringSegment>) from that.

Mafii
  • 7,227
  • 1
  • 35
  • 55