Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Simone Chiaretta 134 posts 541 karma points c-trib
    Mar 14, 2016 @ 14:27
    Simone Chiaretta
    0

    Setting the culture during the inbound request pipeline in a 1:1 multilang site with Vorto

    I'm doing a 1:1 multilingual site using Vorto. And while not strictly a question, I'd love to hear comments on how implemented it and also I found a strange behavior with PublishedContentRequest and IContentFinder

    Small introduction: my setup has 3 languages (bg, en, fr), the name of each node (thus the URL) is in English only, and content is in Vorto properties.

    Vanilla implementation would give me the same /folder/nodeName url for all language versions and I'd have had to differentiate by adding something like ?lang=fr and use the querystring to set the current thread culture in the page.

    But this was not a nice solution, not for users with strange URLs and not for the code as was not very clean.

    So I decided to dig into Umbraco extensibility and try to make everything more transparent and better-looking.

    First step was to make sure URL are automatically generated based on the current culture, so that I don't have to manually add /fr/ or ?lang=fr everytime I needed to write a URL.

    For this I created a IUrlProvider by extending the default DefaultUrlProvider. Here is the code:

        public class UrlWithLanguage : DefaultUrlProvider
        {
    
            public UrlWithLanguage()
            : base(UmbracoConfig.For.UmbracoSettings().RequestHandler){ }
    
    
            public override string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode)
            {
                var currentCulture = System.Globalization.CultureInfo.CurrentUICulture;
                var currentCultureName = currentCulture.TwoLetterISOLanguageName;
                var original = base.GetUrl(umbracoContext, id, current, mode);
                return "/" + currentCultureName +  original;
            }
        }
    

    With this in place urls are automatically /en/folder/nodeName/

    Then I had to make sure that when the request came back in, the language identifier was removed (otherwise umbraco would look for a node /en/folder/nodeName/ and would find nothing) and that the current culture was set to what was the language identified.

    To this I created a IContentFinder, extending the ContentFinderByNiceUrl. Here is the code:

    public class LanguageAwareContentFinder : ContentFinderByNiceUrl
    {
    
        public override bool TryFindContent(PublishedContentRequest contentRequest)
        {
            string route;
            if (contentRequest.HasDomain)
                route = contentRequest.Domain.RootNodeId.ToString() + DomainHelper.PathRelativeToDomain(contentRequest.DomainUri, contentRequest.Uri.GetAbsolutePathDecoded());
            else
                route = contentRequest.Uri.GetAbsolutePathDecoded();
    
            CultureInfo ci = new CultureInfo("en");
            if (contentRequest.Uri.Segments.Length > 1)
            {
                var cultureString = contentRequest.Uri.Segments[1].TrimEnd('/');
                var languages = ApplicationContext.Current.Services.LocalizationService.GetAllLanguages().AsQueryable();
                var language = languages.SingleOrDefault(l => l.IsoCode == cultureString);
    
                if (language != null)
                {
                    ci = language.CultureInfo;
                    route = route.Replace("/"+contentRequest.Uri.Segments[1], "/");
                }
            }
    
            var node = FindContent(contentRequest, route);
    
            if (node == null) return false;
    
            contentRequest.PublishedContent = node;
            contentRequest.Culture = ci;
    
            return true;
        }
    }
    

    Basically I copied the code of the ContentFinderByNiceUrl and I remove the language identifier from the route sent to the FindContent. I also set the contentRequest.Culture to the culture identified in the url.

    Unfortunately what I set here is overwritten later, and in the pages I always got en, no matter which was the language in the url.

    I fixed the problem by handling the PublishedContentRequest.Prepared event, basically running the same code as above:

    private void PublishedContentRequest_Prepared(object sender, EventArgs e)
    {
        var contentRequest = sender as PublishedContentRequest;
        CultureInfo ci = new CultureInfo("en");
        if (contentRequest.Uri.Segments.Length > 1)
        {
            var cultureString = contentRequest.Uri.Segments[1].TrimEnd('/');
            var languages = ApplicationContext.Current.Services.LocalizationService.GetAllLanguages().AsQueryable();
            var language = languages.SingleOrDefault(l => l.IsoCode == cultureString);
    
            if (language != null)
            {
                ci = language.CultureInfo;
            }
        }
        contentRequest.Culture = ci;
    }
    

    So, back to the question: is this a good approach? Any comments? Do you see any obvious problem I might have not seen?

    Then, why is the value of the PublishedContentRequest.Culture reset after the execution of the IContentFinder ends?

    Thx Simone

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Mar 14, 2016 @ 14:53
    Dave Woestenborghs
    0

    Hi Simone,

    My colleague Jeroen Breuer wrote a blog, did a hangout and created a sample project on doing 1-on-1 multilanguage with Vorto

    http://24days.in/umbraco/2015/multilingual-vorto-nested-content/

    https://www.youtube.com/watch?time_continue=1&v=DWjbJiIUQdk

    https://our.umbraco.org/projects/developer-tools/1-1-multilingual-example/

    Dave

  • Simone Chiaretta 134 posts 541 karma points c-trib
    Mar 14, 2016 @ 14:59
    Simone Chiaretta
    0

    Yeah, I know, I've seen them, but my situation is different, as I don't need different URLs per node, and also I didn't want to build my own method for finding content as I fine with just removing the language identifier before processing the standard request.

    Also, I was asking about the PublishedContentRequest.Culture and why it's not kept exiting from the IContentFinder.

    Thanks... but no thanks :)

  • Stephen 767 posts 2273 karma points c-trib
    Mar 14, 2016 @ 18:38
    Stephen
    100

    That would be the best approach, pre-7.4.2.

    Version 7.4.2 will being a PublishedContentRequest.Preparing event that can be used to modify the request's Uri before it reaches the finders (though I'm not sure how we would deal with the culture).

    That being said... setting the culture, the way you do it, should work. Will investigate.

  • Stephen 767 posts 2273 karma points c-trib
    Mar 14, 2016 @ 18:56
    Stephen
    0

    Mostly a guess but... could it be that you have a "wildcard" hostname set somewhere in your tree? That is, a language-only hostname (no url)? That's the only reason I can see why the Culture you set on the request would change afterwards.

  • Simone Chiaretta 134 posts 541 karma points c-trib
    Mar 15, 2016 @ 16:05
    Simone Chiaretta
    0

    Indeed, no domain is specified. Here is my "hostname and cultures" dialog. Shall I specify it? To the main language?

    "hostname and cultures" dialog

  • Stephen 767 posts 2273 karma points c-trib
    Mar 15, 2016 @ 16:09
    Stephen
    0

    That would be it. The language setting is overriding whatever you set on the PublishedContentRequest, because dealing with these language settings happens after the content finders, because... we need a content to know which branch of the tree to navigate.

    The Prepared event runs after the language setting thing, and that is why it works.

    I'd suggest removing the language setting, since you don't need it, since you deal with cultures by yourself (ie, turn it back to Inherit). Then it should work.

    ?

Please Sign in or register to post replies

Write your reply to:

Draft