Translating content blocks recursively in Optimizely CMS

Translating a lot of pages in Optimizely can become a real chore for content editors because pages generally have a lot of child content in the form of blocks. To make things more complicated, blocks can recursively also have child blocks. An average page can easily have 10 or more blocks depending on the amount of content that is displayed.

By default, blocks have to be translated individually. This means that translating a page will require the editor to do a lot of clicking to translate the content. Luckily there is a sneaky configuration that makes it a lot quicker.

The Language Manager

The language manager can be installed as a NuGet package that does not come with the base installation of Optimizely CMS. It's very useful for managing the language variations for your content. There are options available for auto-translating and duplicating content into other languages, which by default does not affect child content.

Optimizely Languages app – Support Help Center

Translating content

To give an example of how the translation works by default, I will use the Language Manager to duplicate content from one language to another on a page that has blocks inside it.

I'm also using the projects feature to get a clear view of what content is being changed. I recommend using projects when translating content because it can also be used to export and import translation (XLIFF) files.

I've created a page called 'About us' and added some blocks.

The 'Accordion - about us' block also has some child blocks.

Then I use the 'Duplicate content' feature to translate the 'About us' page to Japanese.

When I look into my project, I can see that only the About page itself has been created in the Japanese language, while the blocks inside it are still only available in the Australian language.

Next, I'll show you how to configure the Language Manager to automatically include child blocks recursively in the translations.

Configuring the content types that should include blocks

There is a somewhat hidden configuration that will make the Language Manager include blocks recursively using some of its translation features. To do this, we just need to configure the content types that should be checked for child blocks when being translated. That can be done in either the AppSettings.json or Startup.cs files, as can be seen in the examples below.

AppSettings.json

{  
    "Episerver": {  
        "CmsUI": {  
            "LanguageManager": {  
                "TranslateOrCopyContentAreaChildrenBlockForTypes" : [  
                  "MyProject.Models.Pages.StandardPage",  
                  "MyProject.Models.Pages.HomePage"  
                ]  
            }  
        }  
    }  
}

Startup.cs

using EPiServer.Labs.LanguageManager;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<LanguageManagerOptions>(o =>
        {
            o.TranslateOrCopyContentAreaChildrenBlockForTypes.Add("MyProject.Models.Pages.StandardPage");
            o.TranslateOrCopyContentAreaChildrenBlockForTypes.Add("MyProject.Models.Pages.HomePage");
        });
    }
}

Most of the time you will only need to do this for page types. Note that you need to include the entire namespace path, plus the name of the class.

The result

The result is that when I duplicate the content using the Language Manager, as I've shown earlier in this article, the content of the blocks is automatically created in the target language.

Something to note is that if your block is published in the language that you're copying from, then the block will also be published automatically in the new language. It will not automatically publish your pages, which means you can still review the content fully before publishing it on the site.

When I was asked if there was an easier way to bulk translate content, I thought that there wasn't going to be an easy approach. Luckily I found the Language Manager configuration that I had never heard about, even though it is in the documentation here. It's easy to miss, so I figured I should share it here!