Friday, December 28, 2018

Lost in Translation - Building Your Language and Site Context from Scratch

This post is somewhat of a sequel to my previous post around multi-site and multi-language setup. When there is only one site and only one language things are pretty straightforward in terms of site context. Once multiple sites are introduced every time we are getting an item in Sitecore and/or building a link we need to make sure we do it within the correct site and correct language. For the most part Sitecore does it out of the box but there are cases when this functionality breaks down.

Here's an example. Suppose we have a multi-level navigation module that we assemble in the following manner:

1. an ajax call is made to the backend
2. backend method gets the item hierarchy from Sitecore
3. backend method returns serialized items as Json
4. in our java script code we generate navigation html with all the data we just got from Sitecore

Sounds easy, right? Not quite. By the time we arrive at step 2 we are stuck with null values for any item we try to get. We have lost our site context.

Let's bring it back! First thing to do is to pass some values from the view along with the ajax call. This could be done multiple ways. Below is just one way of doing so. We will add hidden values that would carry things like current item id, context database and language.

<input type="hidden" id="currentItem" value="@Sitecore.Context.Item.ID" />
<input type="hidden" id="currentDb" value="@Sitecore.Context.Database" />
<input type="hidden" id="currentLanguage" value="@Sitecore.Context.Site.Language" />

Then in the ajax call we need to make sure we pass these values to our backend method. Read the values first:

var currentItem = document.getElementById('currentItem').value;
var currentDb = document.getElementById('currentDb').value;
var currentLan = document.getElementById('currentLanguage').value;

and send them on their way:

var urlString = "yourUrlHere?item=" + currentItem + "&db=" + currentDb + "&lan=" + currentLan;

url: urlString......
and so on

And now we can rebuild our context and get what we need from the sitecore tree making sure we grab a correct site and a correct language.

public ActionResult GetJson()
//read query string params from the url
string currentItem = Request.QueryString["item"];
string currentDb = Request.QueryString["db"];
string currentLan = Request.QueryString["lan"];

// get current database, get current item and its pathvar db = Sitecore.Configuration.Factory.GetDatabase(currentDb);
Item item = db.GetItem(currentItem);
string itemPath = item.Paths.FullPath;

//build sitecore context
SiteInfo site = SiteContextFactory.Sites
.Where(s => s.RootPath != "" & itemPath.StartsWith(s.RootPath, StringComparison.OrdinalIgnoreCase))
.Where(s => s.Language == currentLan)
.OrderByDescending(s => s.RootPath.Length)

var website = new SiteContext(site);

// now that we have our context let's throw in some url options that you might need depending on how you want it set up

    using (new SiteContextSwitcher(website))
        var options = LinkManager.GetDefaultUrlOptions();
        options.AlwaysIncludeServerUrl = true; // could be false, depends on requirements
        options.Site = new SiteContext(site);
        options.SiteResolving = true;
        options.LanguageEmbedding = LanguageEmbedding.Never; // could be Always or As Needed   depending on requirements

       // from here on we can get our items in the proper context and language and build links using the url options we just built. And of course serialize it all and return as json.

Friday, December 14, 2018

Sustainable Growth: Going Hybrid for a Multi-site and Multi-lingual Sitecore setup

As a website evolves and its audience grows making your content available in multiple languages becomes a top priority. The most common way to achieve this goal is to use Sitecore's out of the box language versioning functionality where the tree is shared and the same item in can have translated versions. But what if it's not only your content that differs between various country sites but also presentation? What if your sites intended for various countries are independent from each other and possibly share some components but not all? What you can do is set up a site in such a way that tree is split between countries meaning each site has its own site definition with it's own hierarchy.

Now let's consider a more sophisticated arrangement where not only we have several country sites but each site needs to have content in multiple languages. Also, country sites need to be independent from one another and have their own site settings and their own hierarchy. In addition to these requirements we are also asked to maintain the same host name for all our global sites.

What are they thinking? They are asking for the moon! Not really. What our stakeholders are asking is a hybrid approach that will allows us to combine shared tree with split tree setup.

Let's walk through an example. Our company XYZ Incorporated is going through an expansion and we are building a site for a global audience. For now let's add a Japanese site keeping in mind that we plan to have content for Japan in two languages: Japanese and English. Obviously, the English version of the Japanese site would be different from the English version of our US site, therefore we cannot reuse the US node as a starting point.

In order to accommodate this we have to create a custom culture for English in Japan. You can install it on your system by using C# code or run a Powershell script for example. Once it's available we can add a new language under System Languages node in sitecore using our new culture.

What you see highlighted in red is English (Japan). It can be represented with /en-us in our url.

Our next step is to add site definitions for each country (familiar task) but wait, because we need multiple languages for multiple countries we need to use language-culture to determine a site. What do I mean? 

We create two site definitions for each of our language-cultures. In other words we would have a site and site both using the same host name with either /ja-jp or /en-jp in the url to indicate a language. We need to make sure we add virtualFolder and physicalFolder to our site definitions using language-culture combination. But also indicate a language separately. 

I know, it's getting complicated but believe me in the end the XYZ corporation will have a beautiful and versatile site their global audience would love!

Let's continue. We created nodes, we added site definitions for each language in each country. Are we done? Almost. Now we need to add a config patch file and set AlwaysStripLanguage setting to false. 

Now we are done and the content team can start building pages for our brand new Japanese site and use content in Japanese as well as English languages.