orchardcmsorchardcms-1.10

How to intercept a request to get the raw URL for site localization?


My site works this way:

  1. All content has autoroute for {culture/slug} URLs
  2. Users can select the site culture, so that everything is presented in the language they choose

I'm trying to achieve this functionality:

  1. User selects site in English.
  2. User goes to "site.com/es/content", which is a content in Spanish.
  3. The site has to automatically change the culture to Spanish and return the requested content.

What I think I need is to intercept the request, parse the URL and get the culture to see if it's the same as the current one.

I've tried getting it in the ItemsController in Orchard.Core.Contents using the IHttpContextAccessor but it doesn't have the raw Url.

I've also tried catching the request in Orchard.Autoroute and Orchard.Alias services but they are not the ones processing the request.

Any pointers would be appreciated.


Solution

  • There are some ways to do this.

    Implement ICultureSelector

    namespace SomeModule
    {
        using System;
        using System.Globalization;
        using System.Linq;
        using System.Web;
        using Orchard.Localization.Services;
    
        public class CultureSelectorByHeader : ICultureSelector
        {
            private readonly ICultureManager cultureManager;
    
            public CultureSelectorByHeader(ICultureManager cultureManager)
            {
                this.cultureManager = cultureManager;
            }
    
            public CultureSelectorResult GetCulture(HttpContextBase context)
            {
                var acceptedLanguageHeader = context?.Request?.UserLanguages?.FirstOrDefault();
                if ( acceptedLanguageHeader == null )
                    return null;
    
                var enabledCultures = this.cultureManager.ListCultures();
                var siteCulture = this.cultureManager.GetSiteCulture();
    
                // Select the specified culture if it's enabled.
                // Otherwise, or if it wasn't found, fall back to the default site culture.
                var culture = enabledCultures.Contains(acceptedLanguageHeader, StringComparer.InvariantCultureIgnoreCase)
                    ? CultureInfo.CreateSpecificCulture(acceptedLanguageHeader).Name
                    : CultureInfo.CreateSpecificCulture(siteCulture).Name;
    
                return new CultureSelectorResult { CultureName = culture, Priority = 0 };
            }
        }
    }
    

    You can go wild in GetCulture, read headers, cookies, a query string or get some settings for the current user from DB. Whatever fits your need.

    Set the culture directly

    private void SetWorkContextCulture(string cultureTwoLetterIsoCode)
    {
        if ( !string.IsNullOrWhitespace(cultureTwoLetterIsoCode) )
        {
            try
            {
                var culture = CultureInfo.CreateSpecificCulture(cultureTwoLetterIsoCode);
                this.Services.WorkContext.CurrentCulture = culture.TwoLetterISOLanguageName;
            }
            catch ( CultureNotFoundException )
            {
                Debug.WriteLine("Couldn't change thread culture.");
            }
        }
    }
    

    Just change the current culture of WorkContext before returning your result and you're good to go.

    Fun fact: Changing the WorkContext.Culture in a controller will override everything you did in your ICultureSelector implementation.