Working with Localization and Language Branches in EPiServer 7 MVC

March 2022 Update: This post was originally written for EPiServer version 7. In 2021, Episerver (now known as Optimizely) released version 12. While some parts of this post are outdated, other parts are still relevant and may be helpful. If you run into any issues when working with localization and language branches, you should refer to Optimizely's official documentation. No content in this post has been changed for the current version.

One powerful feature that EPiServer 7 provides is the localization of page content and the creation of language branches for your site, which allows you to create a multi-language, international site without the need for a huge amount of work. In this post, we'll look at how to configure, utilize, and develop for localization in EPiServer 7.

Languages Branches in Edit Mode

When comparing to previous versions of EPiServer, the latest release of EPiServer 7 has dramatically changed the way you work with language branches in Edit Mode. While some may think this new approach is taking a step backwards, it definitely takes a bit more steps to add a language branch to a page. But at the same time, it provides better organization when working with multi-site EPiServer applications.

Adding Languages

Let's first start with adding languages. This part has not changed between EPiServer 6 CMS and EPiServer 7. In Admin Mode, under the Config tab, you'll find the "Manage Website Languages" page. On this page, you can add a new language, enable/disable languages, and set access rights for a language branch. One thing to note that the access rights in the area only control which languages editors/administrators can work with in Edit Mode. It does not affect access rights on the visitor's side.

Also, the list of languages that you can add is generated from the .NET Framework's CultureInfo class in the System.Globalization namespace, and only contains the language codes that adhere to the RFC 1766 standard. If you want to add a custom language code, you'll need to add a custom culture, and this culture will need to be added on any server the site lives on.

Making Languages Available

After all the languages you want to support are added and enabled, you need to make them available in Edit Mode. Click on the Root Page of your EPiServer site, go into Forms Editing mode, click the "Tools" dropdown, and select "Language Settings". Click the "Change" button in the "Available Languages" section, check all of the languages you want to make available for editing, then click "Save". If you already have a Start Page set up and configured, you may need to do the same steps on the Start Page.

Changing the Master Language

If you want to change the master language for the site, you don't necessarily need to worry about this in EPiServer 7, primarily because you no longer need the Root Page to be in the same language branch as your master language. After you add, enable, and make available the language branches you want to use, just assign the "master" language to the host name, and switch to that language branch. If you have previously created pages in the old "master" language, you can just remove them then disable that language branch.

Adding a Page to a Language Branch

The biggest change between EPiServer 6 CMS and EPiServer 7 is the steps needed to switch from one language branch to another in order to add a new page:

  1. Open the navigation pane
  2. Click the "Sites" tab
  3. Click the "Settings" button (the little cog) in the bottom right of the navigation pane and choose "Show All Languages" (you might be able to skip this step if you are using EPiServer 7.1)
  4. Expand the current site and select the language you want (Edit Mode will reload)
  5. Open the navigation pane again
  6. Click the "Settings" button (the little cog) in the bottom right and select "Show All Languages"
  7. Click the page in the other language and click the button "Translate"
  8. Edit the page

I find it useful to pin the navigation pane open when working on sites with multiple languages.

Note: If you are using EPiServer 7.1, the "Show All Languages" text has been switched to say "Show Content Not in {insert your current language}". Also, the flow of language handling has been improved (see below).

Translating Blocks and Their Folders (added in EPiServer 7.1)

Much like translating pages, with the release of EPiServer 7.1, you can now translate blocks and the folders they live in. From the release notes:

Now you can translate blocks and their folders in the same way as for pages with the option Show All Languages for the shared blocks gadget. This option will show blocks and folders for all languages. Not translated blocks have a language code representing the fallback language, when this is turned off only blocks and folders that are available for the current language will be shown.

Edit Mode "View As" Settings (added in EPiServer 7.1)

Also added in EPiServer 7.1 is the improved flow of language handling, which added a new option to the view settings. When you click the "Toggle view settings" button (the little eye) in Edit Mode, you'll see a new option with your current language branch.

Clicking that will open the "View in this language" dropdown with all enabled languages for the site. When you select one of those languages, it will display the currently viewed page in the selected language, though it will not switch completely to that language branch. If the page does not exist in the selected language, it will give you direct link to translate it, which will switch Edit Mode to that language branch.

Localization Provider Configuration

In EPiServerFramework.config, you can configure the location of your language files, the fallback behavior, and the fallback culture:

<localization fallbackBehavior="Echo, MissingMessage, FallbackCulture" fallbackCulture="en-US">
  <providers>
    <add virtualPath="~/Resources/LanguageFiles" name="languageFiles" type="EPiServer.Framework.Localization.XmlResources.FileXmlLocalizationProvider, EPiServer.Framework" />
  </providers>
</localization>

Notice in the configuration the values in the fallbackBehavior attribute. There are four possible values available:

  • Echo - If no match for the key is found, and if the key does not start with a '#' or a '/', it will return back the key unmodified.
  • MissingMessage - If no match for the key is found, it will return a message stating that the resource could not be found.
  • FallbackCulture - If no match for the key is found, it will attempt to return the resource from the culture specified in the fallbackCulture attribute.
  • None - If no match for the key is found, nothing will be returned.

If you would like to use a custom localization provider, check out this page in the EPiServer documentation.

Linking to Another Language Branch

Getting your visitors to a different language branch has became much easier with EPiServer 7 MVC. You just need to provide the language code to the route data.

For example, in your front-end views (using Razor):

@Html.PageLink("Link Text", pageData.PageLink, new { language = "sv-SE" }, null)

Or your back-end code:

return RedirectToAction("Index", new { language = "sv-SE" });

Back-End Localization

To access localization data from the back-end code, use an instance of the LocalizationService, which is part of the EPiServer.Framework.Localization namespace.

var translatedValue = LocalizationService.Current.GetString("/sometemplate/somevalue");

Other methods in this class allow you to return all localization keys available, or to return localization data for a specified culture. I recommend checking out the LocalizationService class in the EPiServer 7 SDK when it comes to finding the right method for your needs.

From the back-end, you can also override the fallbackBehavior language provider configuration attribute directly in the method from the LocalizationService instance. For example, if you do not want to show any fallback text, you can pass in FallbackBehaviors.None as a parameter:

var translatedValue = LocalizationService.Current.GetString("/sometemplate/somevalue", FallbackBehaviors.None);

Alternatively, if you want to directly specify the fallback text, you can just pass in a string:

var translatedValue = LocalizationService.Current.GetString("/sometemplate/somevalue", "Fallback String");
``

## Front-End Localization

To access localization data from the front-end view (in our case, using Razor), use the `Translate` helper, which is a provided EPiServer MVC HTML helper:

```csharp
@Html.Translate("/sometemplate/somevalue")

You could also use the TranslateFallback helper, which will return a supplied fallback string if no match is found:

@Html.TranslateFallback("sometemplate/somemissingvalue", "Missing a translation")

EPiServer has also created some HTML helpers that can generate HTML for forms, such as the TranslatedButton, TranslatedLabel, and TranslatedTextBox helpers.

Language Files

More than likely, you'll create custom language files for your application, either to store translated text used in Edit Mode for editors in different cultures, or in the view for visitors in different parts of the world.

The Old 'lang' Folder

With the release of EPiServer 7, the system localization normally stored in the 'lang' folder have disappeared. The documentation explains what happened to them:

Though strictly not needed, the lang folder under the web directory will continue to work as before and any language XML files placed here will be read by a default localization provider that is added automatically. If you do not wish for this to happen, you can just remove the folder or set the value of the addDefaultProvider attribute of the resources element to false.

If you want to override the system localizations that are shipped with EPiServer products, you will notice that the language files are no longer installed in the lang folder. They are now instead embedded in the assemblies but just as before you can override individual string by placing your own language XML in the lang folder. Of course you can include your localizations through any other LocalizationProvider and they will take precedence over the system localizations as long as the provider is registered before the system ones.

Our Custom Language Files

In the above configuration file, we specified our location of the language file. At this location, you just need to add (one or many) XML files to hold the values. Any XML files you add here needs to have the XML declaration, followed by the parent <languages> node which contains (one or many) <language> nodes for each language code (that matches the language branch):

<?xml version="1.0" encoding="utf-8" ?>
<languages>
  <language name="English (United States)" id="en-US">
    <sometemplate>
    <somevalue>Translated Value</somevalue>
  </sometemplate>
  </language>
</languages>

Aside from the <languages> and <language> nodes, there are no naming standard you need to follow when naming the custom keys or the XML files. So as you can imagine, you could either put all translated values for all languages in one XML file, or you could split it up into many XML files so it's better organized.

The one exception to this is when you are trying to localize the text in Edit Mode. Unfortunately, it is not thoroughly documented what the names of the keys should be, though there has been a couple blog posts that has helped shed some light on these values. For more information on this, I've found these resources:

If you still cannot find the necessary names for the keys, the best solution is just look at the source code using a decompiler/reflector tool. I had to do this when I was trying to figure out how to localize error messages generated from an XForm property.