Building multilingual sites in Umbraco with Vorto, Part 2
In my last post, I wrote about how I used Vorto and a custom ContentFinder to build a test multilingual site where each language is hosted as a subfolder on the same domain instead of separate domains (that is sitename.com/en and sitename.com/es instead of en.sitename.com and es.sitename.com). In this post I describe some modifications I’ve made to the ContentFinder implementation as well as a custom URLProvider so that internal links are correct. Note I am still looking into implementing language specific URLs if time permits and appreciate the feedback I’ve received.
I’ve updated the MultilingualContentFinder so that it redirects the requests to the root URL (“/”) to either the English home (“/en”) or the Spanish home (“/es”) based on the user’s language. Each browser has a language preference setting which allows the user to specify an ordered list of preferred languages. The browser will then send the list of preferred languages with each request using the “Accept-Language” HTTP Header. In ASP.NET, you can access this using the UserLanguages property of the HTTP Request object which is a sorted string array of client language preferences. My updated MultilingualContentFinder iterates through that array until it finds a language that starts with en or es, and redirects to the appropriate URL. If there is no match, I redirect to the English home by default.
The other change I made was to prevent content from loading from the default Umbraco URLs. In other words, if I have a node named “test” that is a child node of the Home node, I want it to load from /en/test and /es/test but not from /test. In my original implementation, I had registered MultilingualContentFinder by inserting it before the ContentFinderByNotFoundHandlers.
Umbraco’s request pipeline will use Content Finders to find content in the order they are set in the ContentFinderResolver. Since I had inserted this before the ContentFinderByNotFoundHandlers, which is the last content finder that runs and sets the 404 content, this means that Umbraco’s ContentFinderByNiceUrl was getting processed before my MultilingualContentFinder and was setting the content using Umbraco’s standard routing. My solution is to clear the ContentFinders set by default and then add the ContentFinderByNotFoundHandlers back in and insert my MultilingualContentFinder before it. This seems to work fine but I’ll need to do more testing to make sure there are no unexpected consequences.
So far, I have been dealing with how Umbraco processes HTTP requests for content and implemented a custom ContentFinder that sets the correct content for a contentRequest. In order to have internal links within content work, we need to have a custom UrlProvider that will be used to generate the correct links. To do this, I’ve created a custom UrlProvider to be used to output the correct URL.
There are two methods that need to be implemented: GetUrl and GetOtherUrls. As far as I can tell, GetUrl is what gets called when Umbraco renders a link in the front end and GetOtherUrls gets called along with GetUrl to display the list of links in the back office in the property tab for each node. Fortunately, the GetUrl method has UmbracoContext passed as a parameter. From this, we can access the culture of the request. This allows one to return a URL based on the culture name (which is the language code).
For the back office, I am able to have the properties tab for each node display the correct URLs by implementing GetOtherUrls to return one of the URLs and GetUrl to return the other (in this case the PublishedContentRequest property of UmbracoContext is null). See my test implementation.