31

How can I (in ASP .NET MVC) get the CultureInfo of the current visitor (based on his/her browser languages)?

I have no idea where to start. I tried looking into the "Accept-Languages" header sent by the browser. But is that the best way of doing it?

Mathias Lykkegaard Lorenzen
  • 15,031
  • 23
  • 100
  • 187

6 Answers6

61

Request.UserLanguages is the property you're looking for. Just keep in mind that this array may contain arbitrary (even non-exsitent) languages as set by request headers.

UPDATE

Example:

// Get Browser languages.
var userLanguages = Request.UserLanguages;
CultureInfo ci;
if (userLanguages.Count() > 0)
{
    try
    {
        ci = new CultureInfo(userLanguages[0]);
    }
    catch(CultureNotFoundException)
    {
         ci = CultureInfo.InvariantCulture;
    }
}
else
{
    ci = CultureInfo.InvariantCulture;
}
// Here CultureInfo should already be set to either user's prefereable language
// or to InvariantCulture if user transmitted invalid culture ID
Ferdinand.kraft
  • 12,579
  • 10
  • 47
  • 69
Sergey Kudriavtsev
  • 10,328
  • 4
  • 43
  • 68
  • What types of values does these headers normally contain? Examples would be great to work with. – Mathias Lykkegaard Lorenzen Feb 23 '12 at 13:41
  • 4
    Quote: `Typically these consist of a two-character codes for the language, a hyphen, and a two-character code for the culture, such as "en-us" for U.S. English and "fr-ca" for Canadian French.`. So it's a `string[]` array containing values like those. – Sergey Kudriavtsev Feb 23 '12 at 13:44
  • 1
    I've just been working with this, so just for anyone looking at this in the future, Request.UserLanguages is essentially a comma separated version of Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"]. The first item in the array will just be the country code (e.g. 'en-GB') and subsequent items will usually contain a 'weighting' (e.g. 'en-US;q=0.8'), for example mine is "en-GB,en-US;q=0.8,en;q=0.6" so you also might need to split each item again to find the actual code and the weighting. – Ian Routledge Dec 18 '12 at 11:12
  • 2
    The general form of these language tags is specified in RFC 5646 ( http://tools.ietf.org/html/rfc5646 ). If you assume "ab-CD", you'll get thrown when somebody specifies an alphabet (sd-Arab-PK). – Steve Howard Aug 09 '13 at 04:19
  • @SteveHoward: True. Also this information comes just from the header which can be freely modified on client and contain just anything, ranging from short two-character language code (i.e. "en") to arbitrary string (i.e. "I-am-a-cool-hax0RR!"). – Sergey Kudriavtsev Aug 10 '13 at 16:43
  • 1
    I'm actually running into this at work which is why I brought it up. For Taiwan, IE9- sends "zh-TW". IE10+ sends "zh-Hant-TW". I don't actually care about malicious users since they're just going to get fallback stuff, I do care about how there are now multiple ways to "spell" a particular locale. – Steve Howard Aug 10 '13 at 23:33
  • is it possible to give me a brief explanation about what is the duty of InvariantCulture? and what if user do not have any language in their preferred language list ? thank you very much @SergeyKudriavtsev – neda Derakhshesh Sep 23 '18 at 10:29
  • 1
    Be careful, this answer does not handle: choosing the best culture from the *user list*, taking care of the priority value, setting the current culture for resource localization. – SandRock Dec 31 '18 at 11:28
14

Asp.Net Core version: using RequestLocalization ie the culture is retrieved form the HTTP Request.

in Startup.cs - Configure

app.UseRequestLocalization();

Then in your Controller/Razor Page.cs

var locale = Request.HttpContext.Features.Get<IRequestCultureFeature>();
var BrowserCulture = locale.RequestCulture.UICulture.ToString();
tinmac
  • 2,357
  • 3
  • 25
  • 41
7

You can use code similar to the following to get various details from your user (including languages):

MembershipUser user = Membership.GetUser(model.UserName);
string browser = HttpContext.Request.Browser.Browser;
string version = HttpContext.Request.Browser.Version;
string type = HttpContext.Request.Browser.Type;
string platform = HttpContext.Request.Browser.Platform;
string userAgent = HttpContext.Request.UserAgent;
string[] userLang = HttpContext.Request.UserLanguages
Chris
  • 3,191
  • 4
  • 22
  • 37
6

It appears Request.UserLanguages is not available in later mvc versions (Asp.net core mvc 2.0.2 didn't have it.)

I made an extension method for HTTPRequest. Use it as follows:

var requestedLanguages = Request.GetAcceptLanguageCultures();

The method will give you the cultures from the Accept-Language header in order of preference (a.k.a. "quality").

public static class HttpRequestExtensions
{
    public static IList<CultureInfo> GetAcceptLanguageCultures(this HttpRequest request)
    {
        var requestedLanguages = request.Headers["Accept-Language"];
        if (StringValues.IsNullOrEmpty(requestedLanguages) || requestedLanguages.Count == 0)
        {
            return null;
        }

        var preferredCultures = requestedLanguages.ToString().Split(',')
            // Parse the header values
            .Select(s => new StringSegment(s))
            .Select(StringWithQualityHeaderValue.Parse)
            // Ignore the "any language" rule
            .Where(sv => sv.Value != "*")
            // Remove duplicate rules with a lower value
            .GroupBy(sv => sv.Value).Select(svg => svg.OrderByDescending(sv => sv.Quality.GetValueOrDefault(1)).First())
            // Sort by preference level
            .OrderByDescending(sv => sv.Quality.GetValueOrDefault(1))
            .Select(sv => new CultureInfo(sv.Value.ToString()))
            .ToList();

        return preferredCultures;
    }
}

Tested with ASP.NET Core MVC 2.0.2

It's similar to @mare's answer, but a bit more up-to-date and the q (quality) is not ignored. Also, you may want to append the CultureInfo.InvariantCulture to the end of the list, depending on your usage.

Cory-G
  • 1,025
  • 14
  • 26
  • `StringWithQualityHeaderValue` - useful – Phil Jul 09 '18 at 09:18
  • @Phil, yeah, it's great. Parsing the header yourself would be a mess. There are a lot of little assumptions that could bite you. – Cory-G Jul 09 '18 at 17:22
  • 1
    `new CultureInfo(sv.Value.ToString())` is likely to crash with a CultureNotFoundException if the client gives an invalid culture name! – SandRock Dec 31 '18 at 11:24
  • @SandRock That is a good note to those using this. I'm not handling the exeptions for you. It's a good idea watch for a `CultureNotFoundException` and return a `400` or `500` or something to let them know you don't have that language support. – Cory-G Jan 03 '19 at 22:41
5

I am marking this question for myself with a star and sharing here some code that essentially turns the Request.UserLanguages into an array of CultureInfo instances for further use in your application. It is also more flexible to work with CultureInfo than just the ISO codes, because with CultureInfo you get access to all the properties of a culture (like Name, Two character language name, Native name, ...):

        // Create array of CultureInfo objects
        string locale = string.Empty;
        CultureInfo[] cultures = new CultureInfo[Request.UserLanguages.Length + 1];
        for (int ctr = Request.UserLanguages.GetLowerBound(0); ctr <= Request.UserLanguages.GetUpperBound(0);
                 ctr++)
        {
            locale = Request.UserLanguages[ctr];
            if (!string.IsNullOrEmpty(locale))
            {

                // Remove quality specifier, if present.
                if (locale.Contains(";"))
                    locale = locale.Substring(0, locale.IndexOf(';'));
                try
                {
                    cultures[ctr] = new CultureInfo(locale, false);
                }
                catch (Exception) { continue; }
            }
            else
            {
                cultures[ctr] = CultureInfo.CurrentCulture;
            }
        }
        cultures[Request.UserLanguages.Length] = CultureInfo.InvariantCulture;

HTH

mare
  • 13,033
  • 24
  • 102
  • 191
-6

var userLanguage = CultureInfo.CurrentUICulture;

jeffmaher
  • 1,824
  • 3
  • 21
  • 20