Search code examples
c#asp.net-coreasp.net-mvc-routingasp.net-core-middleware

Localization based on country in asp.net core 2.1


I am trying to localize the content sent back to the user based on the country they are viewing my app from meaning that their country code should be part of the request url. For example, if the user is in the US, all request URLs should be something like http://website.com/us/{controller}/{action}.

With that said, is there a way to identify the country of origin of the user and then modify the request URL by appending their country Iso-2-Letter-Code to that URL and then redirect the user with new URL. If the request URL has a country code, then this request modification should be bypassed.

I would prefer a solution that uses or creates an asp.net core middleware that i can attach to my HTTP request pipeline in Startup.cs or one that involves adding a new custom route to the default MVC routes.

Your help & ideas will be greatly appreciated.


Solution

  • There is an existing feature in .Net that you can know what is the origin. It's called Culture Info.

    By default, each client will pass in a culture info or they can specify what is the current language/country they intended to use.

    My suggestions is to allow client to specify the culture-info in the Header name call "Accept-Language". For example, I am in US and intended to use English, I would sent 'en-us' as part of my request inside the header.

    Inside backend server, I would check against this header and set the culture-info appropriately.

     public class LanguageMessageHandler : DelegatingHandler
    {
        private const string LangenUS = "en-US";
    
        private readonly List<string> _supportedLanguages = new List<string> { LangenUS };
    
        private bool SetHeaderIfAcceptLanguageMatchesSupportedLanguage(HttpRequestMessage request)
        {
            foreach (var lang in request.Headers.AcceptLanguage)
            {
                if (_supportedLanguages.Contains(lang.Value))
                {
                    SetCulture(request, lang.Value);
                    return true;
                }
            }
    
            return false;
        }
    
        private void SetCulture(HttpRequestMessage request, string lang)
        {
            request.Headers.AcceptLanguage.Clear();
            request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(lang));
            Thread.CurrentThread.CurrentCulture = new CultureInfo(lang);
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
        }
    
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!SetHeaderIfAcceptLanguageMatchesSupportedLanguage(request))
            {
                // Whoops no localization found. Lets try Globalisation
                if (!SetHeaderIfGlobalAcceptLanguageMatchesSupportedLanguage(request))
                {
                    // no global or localization found
                    SetCulture(request, LangenUS);
                }
            }
    
            var response = await base.SendAsync(request, cancellationToken);
            return response;
        }
    
        private bool SetHeaderIfGlobalAcceptLanguageMatchesSupportedLanguage(HttpRequestMessage request)
        {
            foreach (var lang in request.Headers.AcceptLanguage)
            {
                var globalLang = lang.Value.Substring(0, 2);
                if (_supportedLanguages.Any(t => t.StartsWith(globalLang)))
                {
                    SetCulture(request, _supportedLanguages.FirstOrDefault(i => i.StartsWith(globalLang)));
                    return true;
                }
            }
    
            return false;
        }
    }