Monday, August 17, 2015

Sitecore: Custom Language Fallback Strategy

Background

Recently a client requested that a multisite solution be implemented.  At the same time, each site could have multiple international versions.  We will get into handling multiple hostnames with the same domain but different suffix in a future post.  With international sites also come with language fallback strategies because not every piece of content needs to be translated.  Some are perfectly fine left in English or whatever the default language may be.

Why is language fallback necessary?

Sometimes international sites only have a few items that are unique to their locale such as homepage and contact page. Some items like a registration component or an employee biography page do not necessarily require translation and are perfectly fine displayed in English.  If there is no language fallback in place, and we are on a Japanese site, and the Japanese site does not have its own version of the employee biographies, the user will see a blank page in the main content section with just a Japanese header menu and possibly Japanese footer.  With language fallback, even though the header and footer are in Japanese, at least the main content with the biographies will be in English, much better than a blank content area.

Solution

We know the system languages are stored in "/sitecore/system/languages". Although you can technically create a version of an item in any possible language, adding languages to the system languages folder provides a list of all languages in the drop down when selecting languages to create versions for.  This is much more convenient than going into the language picker and finding the one you need every time the first version is created for a new language.

Lets start by modifying the language template a little bit.  We can add a droplink field for the purpose of storing the fallback language.  Of course the choices allowed in this field would be another language in the same folder so just set the source to be the system languages folder.  It would look something like this:



Next, we have to make use of this new drop down field value. We have to store all these language fallbacks somewhere to be accessed later.  One way to do this is to create an index that stores all language definitions and their fields and values.  Another way is to create a Dictionary object with language name being the key and the fallback language name being the value.  If you choose to go this route, make sure you enhance the performance by caching the Dictionary object as a static item so all web requests will be pulling the fallback language from the same Dictionary object.  Achieve this by creating a static CacheManager class based on HttpContext.Current.Cache to store shared objects within the same app pool.  You can create this static Dictionary object in a custom SiteResolver.

Now how do you make use of this static Dictionary object?  We can create a custom ItemProvider, which is derived from the default ItemProvider, to look for language fallbacks.  The default GetItem() method looks like this:



It takes in the context language and then returns the resulting item.  If the resulting item does not have any versions, then we look up the fallback language for the context language via the index or static Dictionary object.  We try again to get an item in this language.



This method provides for single level language fallback and should satisfy most project requirements.  For projects that require multiple levels, you are on your own but shouldn't be more than minor adjustments.

Enhancements

Lets say that we are happy with the fallback strategy above and all seems to be working fine but then you want to suppress language fallback for certain items.  Why would you need this?  Lets say that you have a slideshow with five slides in English and only the first four slides have Japanese versions for the Japanese site.  Allowing for language fallback in the Japanese version of the site would render five slides, the first four with Japanese content and the last with English content due to fallback.  If we can guarantee that all content items have versions across all languages, it would be perfect but that is not always the case.  This is a very common scenario and it would be best to have a strategy for suppressing fallback for certain items.

You can easily achieve this by creating a sitecore template to store constant values that just have a general link field to internal items.  Create a subfolder, either in a global folder or settings folder, and use this folder to store a constant item for each template you want to bypass language fallback.  Now in the custom SiteResolver, you can create a static List object that stores all the IDs of items derived from all the templates stored in the subfolder of constants.  Tweak the ItemProvider a little to check whether the item ID matches any of the IDs in the List object.



This should return the item in the current context language.  This code must be placed before the check for fallback language.

Finally, what other enhancement can we make?  A very important one is to make sure that language fallback does not take place if we are in the content editor.  We can do this by placing this code before the check for fallback language as well.



Summary

Language fallback is very important in the real world.  Most corporate sites have international versions but the international versions have less content than the main corporate site.  If we don't enable language fallback, many pages will end up displaying a header and a footer with no main content in between.  Enabling language fallback ensures a seamless experience for the end users and removes worries from content editors knowing that if they forget to create a version of a content item in the other language, at least the end user will see something instead of nothing at all.

No comments:

Post a Comment