Globalization
Product version: |
EPiServer CMS 4.62 |
Document version: |
1.0 |
Document creation date: |
07-06-2006 |
Document last saved: |
05-10-2007 |
Purpose
EPiServer CMS has supported the creation of multilingual Web sites, with the multi-language functionality, since version 4.30. This functionality has been improved and partially rewritten in EPiServer CMS 4.60. This document outlines some of the technical issues regarding globalized Web sites.
Table of Contents
Introduction
- Display Language to Web Site Visitors
Globalization Scenarios
- Scenario 1: Global Domain with Multiple Languages
- Scenario 2: Local Domains Mapped to Languages
- Scenario 3: Remember User Preference
Page Language
- Page Properties
Language Branch
- Page Language Settings
- Language Selector
- Dynamic Properties
- Archive Page
- Subscription
Visitor Language Selection
- Custom Language Selection
- The Detection Steps
- Web Browser Preference
- Troubleshooting Language Selection
Searching
- Property Searching (FindPagesWithCriteria)
Introduction
Version 1 of the multi-language support in EPiServer CMS caused problems for both editors and developers. It was not particularly easy to edit multi-language pages and it was slightly unpredictable how all the languages worked together and affected one another. Version 1 was also not adequately integrated in the interface and had technical limitations, such as searching, simple address, etc.
Display Language to Web Site Visitors
How does EPiServer CMS know which language to display to visitors? The short answer is that EPiServer CMS always enforces the language to be visible in the URL, either in the path or the domain part of the URL.
The reasons for this are simple:
- search engines, such as Google, must be able to crawl a Web site and easily separate content
- users expect to be able to cut and paste a link into an e-mail and send it to a friend so that the friend can click on the link and will always get the same content.
There are also some technical reasons such as output caching in .NET and Web browser caching on the client that expects a single URL to be rendering the same content to anonymous users.
Globalization Scenarios
This chapter outlines some recommended globalization scenarios and describes how to manage them.
Scenario 1: Global Domain with Multiple Languages
You want all your visitors to go to the official "site.com" address, but need to display different content depending on their language selection.
- Set a default language that most of your visitors will understand, normally English for a global site (EPsDefaultLanguageBranch in web.config).
- Activate language detection based on browser preference (EPfBrowserLanguageDetection in web.config).
- Add links or flags to the header or start page that link to other languages. Get the URL from PageData.DetermineAutomaticURL in the API or Language drop-down if you are using the Link Editor).
Test the Configuration:
Test the configuration by following the instructions below:
- Open Internet Explorer and select Internet Options from the Tools menu. Click Languages in the History group box and select the language preference. You should be redirected to the correct language, if your language selection can be matched to a site language.
Scenario 2: Local Domains Mapped to Languages
There are two approaches to this depending on how you market your Web sites. One approach is to configure a site redirect at your hosting provider, for example redirecting http://site.se to http://site.com/sweden.
The other approach is when you want http://site.se to be the same as http://site.com/sweden, but without the redirect. This approach requires configuration.
1. Add a configuration definition section in web.config. For example:
<configuration>
<configSections>
<sectionGroup name="episerver">
<section name="domainLanguageMappings" allowDefinition="MachineToApplication" allowLocation="false" type="EPiServer.Util.DomainLanguageConfigurationHandler,EPiServer" />
2. Add the actual configuration, for example:
<episerver>
<domainLanguageMappings>
<map domain="site.se" language="SV" />
<map domain="site.no" language="NO" />
</domainLanguageMappings>
3. Add links or flags to the header or start page that link to other languages with their domain names.
Remember that the domain to language mapping always overrides cookies and other client state. This means that visitors to site.no will always get language with code "NO". The only selection that can override this is a language selection in the path part of the URL as discussed in the details. This also means that you can have local sites mapped to different languages, but still have a .com site that has browser detection on languages.
Scenario 3: Remember User Preference
If you have a single domain and require the user language preference to be persisted, you can set a cookie with the current language selection. This means that the next time the user visits your site, they will also be redirected to the correct language. This may also be used if you, for example, are redirecting the user to another system, where language preference is not retained in the URL. You can set a cookie to make sure that when the user returns to the site he or she gets the same language as before.
Be careful when using cookies and always try to build the Web site based on the concept that language is included in the URL. This will ensure that you never lose language context when the user is navigating your site.
Imagine that you have language cookie "NO" and click on a link from a friend that leads to the English site. You should of course continue to use the English site when surfing, so use cookies with care if you actually need them.
Page Language
A page is created on a language branch and the first language created becomes the master language branch for that page. This applies to all pages, globalized or not. The master contains all properties, both properties for that language and common properties. When new languages are created for a page, they will only save properties that are specific for that language.
As a developer all languages will contain the common properties from the master version in the PageData object, but they are not editable. If you change a property that should not be saved per language, you will get an exception when calling the DataFactory.Save method if the language you are publishing is not the master language for that page.
Note Those familiar with the database table tblPage should familiarize themselves with the new table tblPageLanguage, which contains a subset of the metadata from tblPage. You may see that some metadata exists both in tblPage and tblPageLanguage, as the master version will, for backwards compatibility, always store its data in both tables, but it is always tblPageLanguage that is used for loading page content.
Page Properties
Language-specific properties are defined by the administrator/developer on a page type. The metadata (for example PageName) that is language-specific cannot be changed and has been defined by the system. Metadata properties that relate to content are language-specific and metadata properties that relate to navigation are common.
The follow properties are defined per language:
- PagePendingPublish
- PageWorkStatus
- PageSaved
- PageChanged
- PageCreatedBy
- PageChangedBy
- PageCreatedSID
- PageLanguageBranch
- PageName
- PageStartPublish
- PageStopPublish
- PageChangedOnPublish
- PageCreated
- PageLanguageID
- PageExternalURL
- PageURLSegment
- PageShortcutType
- PageShortcutLink
- PageTargetFrame
- PageLinkURL
- PageDelayedPublish
You can programmatically check if a property is language-specific by checking IsLanguageSpecific on the PropertyData class (see samples for an example).
Language Branch
A language branch has a unique identifier in the database to handle constraints, but is always exposed in APIs as a language code, for example ”EN”. Language codes must therefore be unique; two language branches cannot use the same language code as the reverse lookup would fail. The difference between a Language Code and a Language Branch may be subtle, but they were made for two different purposes, where a language branch extends a language code with metadata used for page content.
Page Language Settings
To help the system know which languages should be used on which part of the site we have Page Language Settings, which actually define the languages that should be available to the editor when creating new pages. They also define fallback languages and replacement languages. The administrative API for page language settings are EPiServer.DataAbstraction.PageLanguageSetting. The runtime API to read settings with support for inheritance is EPiServer.DataFactory.PageLanguageSettings. Page Language Settings does not restrict the languages that are rendered on the site, it only helps EPiServer make intelligent choices based on custom settings.
Language Selector
Languages are selected at runtime using a language selector (EPiServer.Core.LanguageSelector). A new instance of this class is created and passed to most methods in the global instance of DataFactory (Global.EPDataFactory). Custom implementations of EPiServer.Core.ILanguageSelector can be used to get a customized language selection. The Language selector uses the Page Language Settings, for example, to know when to fallback a missing language to another.
A language is considered available by the language selector if it has been published (CurrentPage.PendingPublish is false). The language selector does not check publish dates, so for example when a news item expires on one language, it is no longer displayed (no fallback to another language is applied).
Dynamic Properties
Dynamic properties can also be made available per language. This should, however, be used with caution as over usage of dynamic properties is not recommended as it may negatively affect performance. Only use dynamic properties for administrative settings that must be done per language.
Dynamic properties do not use Page Language Settings and are always loaded with the same language as the page, so for example a Swedish page will always get Swedish dynamic properties (even if displayed on an English site due to fallback configuration).
Archive Page
A page is archived when an archive page has been set and the "stop publish" date has passed. In the process the "stop publish" date will be cleared. On globalized pages you have multiple stop publish dates, but only the master language "stop publish" dates are checked, and when the move is made to archive page, all "stop publish" dates for all languages are cleared.
Subscription
When a language/page is updated, it will be sent information to all users that have the updated language as their current preference. The user’s language preference is determined from the property PersonalizedData.Language. If the user has no language preference, updates on the first found language are sent to the user. The user’s language preference can also be edited in admin mode for the user; this list is though filtered on current language files (XML resource files) in the Lang-directory so make sure all languages have a resource file.
The subscription mailer reads Page Language Settings and will take replacement and fallback language into account.
Note! The subscription is based on some special predefined properties, for example “EPSUBSCRIBE” and “EPSUBSCRIBE-ROOT”, neither of these are supported as language specific. |
Visitor Language Selection
Current language is handled by a single class in EPiServer called LanguageContext in the namespace EPiServer.Core. You can access this class by the static Current accessor, i.e. LanguageContext.Current. One instance per request is created of this class and it is automatically created on first access.
This class also supports scheduled jobs and background threads to it are not limited to an available HttpContext. Just access LanguageContext.Current and you will get an instance for the specific thread you are running on. The auto-detection steps outlined below are only run once per request on first access.
Custom Language Selection
Always try to fit your solution into the recommended scenarios and configuration options. Selecting language with custom code will add to complexity, so make sure that you have a well thought-out plan before starting.
The first step when changing language is to make sure that you do not override anything that you shouldn't, for example overriding the edit-cookie can cause problems. There is, however, an easy way to see who selected the current language - the LanguageContext class has an accessor CurrentLanguageBranch which contains the current language code, for example "EN". It also has an accessor CurrentLanguageBranchSource, which contains the functionality that actually selected the language. This allows you, for example, to only override the default language.
The Detection Steps
1. Friendly URL
Friendly URLs in globalization scenarios always include a language prefix that can be defined in Admin mode under Web Site Languages. The language prefix will always be used if available as the current language selection (/english/news).
2. Query String
The query string always takes precedence and is for example used by Edit mode to make sure that the preview of the site is rendered on a specific language. The language selection is also added to every page's URL to make sure a URL always links to the same content whoever is using it.
For example "http://<site>/templates/page.aspx?id=3&epslanguage=EN
3. Editor Cookie "editlanguagebranch"
A session cookie is used in Edit mode to make sure that language selection is not lost when navigating between functionality and, for example, switching between Edit mode and Admin mode. This cookie is only valid in directories Edit, Admin and Util. In all other directories this cookie is completely ignored.
4. Full Qualified Domain Name
If a domain is mapped to a language code, it is always used. The only thing that can override the domain name is when specifying the language in the path part of the URL.
<domainLanguageMappings>
<map domain="www.site.no" language="NO" />
<map domain="www.site.dk" language="DK" />
</domainlanguageMapping>
For example "http://www.site.no" is mapped to language code NO.
5. Visitor Cookie "epslanguage"
This cookie is never set by EPiServer, but can be used when developing a site where you need to remember the user's last language selection.
6. Session Variable "epslanguage"
Session language could be used as an alternative to visitor cookie, but it is not normally recommended that you use session variables.
7. Web Browser Preference (Enabled in System Settings in Admin Mode)
The Web browser sends along the Accept-Language headers which map to the language codes defined as active page languages in EPiServer.
8. Default web.config Setting "EPsDefaultLanguageBranch"
If no language has been detected, the default language branch is used as defined in web.config.
Web Browser Preference
The Web browser sends headers that inform the site of the languages that the user prefers. For example on a Swedish Internet Explorer on Windows XP the header will contain "sv". If this feature is enabled in web.config/System Settings in Admin mode, EPiServer CMS will try to match this value to the language code that is enabled as a Web site language. An exact match is always preferred, for example a Web site visitor with language preference English New Zealand (EN-NZ) will try to get an exact match, but will fallback to English (EN) if found instead.
You should always use the ISO language codes, but to in order to enable the fallback of user preference you may want to, for example, use language code "EN" for the English version that should be "master"-English.
Troubleshooting Language Selection
Activate debug logging to a file in EPiServer (see the technical note "Logging in EPiServer CMS") and connect a program such as Tail for Win32, available from http://tailforwin32.sourceforge.net/. EPiServer will log every time language changes due to some of the above mentioned detection scenarios. It is a very easy way to see what EPiServer actually does when you request a page.
Searching
The search control PageSearch supports searching on either all languages or a list of defined languages. On templates before EPiServer CMS 4.60, searching is performed by default on all languages and results are displayed on the current language if available. The actual search does not use Page Language Settings as part of the search query, but will use Page Language Settings when selecting which language version of the page that should be displayed to the user.
No special treatment is used for files so if you have language-specific files, you must separate them in different directories.
Property Searching (FindPagesWithCriteria)
The property search control PropertySearch and the underlying API DataFactory.FindPagesWithCritiera will by default search on all languages. The hits will, however, use the same language selection process as any other page loading. There are two parameters that you can control:
- Search only on a specific language
- Pass in language selector which will be used when returning the actual pages.
This lets you for example find a property only on a specific language branch.
Samples
Required Namespaces
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
Get a Page with Automatically Selected Language
PageData page = Global.EPDataFactory.GetPage(pageLink);
Identical to
PageData page = Global.EPDataFactory.GetPage(pageLink, LanguageSelector.Auto());
Get a Listing with Automatically Selected Language
PageDataCollection pages = Global.EPDataFactory.GetChildren(pageLink);
Identical to
PageDataCollection pages = Global.EPDataFactory.GetChildren(pageLink, LanguageSelector.Auto());
Get a Page or Listing on a Specific Language
PageData page = Global.EPDataFactory.GetPage(pageLink,new LanguageSelector(”EN”));
PageDataCollection pages = Global.EPDataFactory.GetChildren(pageLink,new LanguageSelector(”EN”));
Get a Page or Listing on a Preferred Language, but with Fallback Support
PageData page = Global.EPDataFactory.GetPage(pageLink, LanguageSelector.Fallback(”EN”,true));
PageDataCollection pages = Global.EPDataFactory.GetChildren(pageLink, LanguageSelector.Fallback(”EN”,false));
Load the Master Language for a Page
PageData page = Global.EPDataFactory.GetPage(pageLink, LanguageSelector.Master());
Get all Languages for a Page
PageDataCollection pages = Global.EPDataFactory.GetLanguageBranches(pageLink);
Create a New Page on a Specific Language Branch
PageData page = Global.EPDataFactory.GetDefaultPageData(parentLink, pageTypeID,new LanguageSelector(”EN”));
Create a New Language for an Existing Page
PageData page = Global.EPDataFactory.CreateLanguageBranch(pageLink, new LanguageSelector(”SV”));
List all Enabled Language Branches Defined by the Administrator
LanguageBranchCollection branches = LanguageBranch.List(true);
Load Language Settings at Runtime for the Current Page
PageLanguageSetting []settings = DataFactory.PageLanguageSettings.Get(pageLink);
Change Content Language for the Current Request
LanguageContext.Current.CurrentLanguageBranch = ”EN”;
Changing UI Language for the Current Request (Translate Controls)
LanguageContext.Current.CurrentUILanguage = ”EN”;
Redirecting to the Current Page with Current Language
Response.Redirect(CurrentPage.Link);
Redirecting to the Current Page with Another Language Selection
Response.Redirect(CurrentPage.DetermineAutomaticURL(”SV”));
Determine Current Request Language Branch
Response.Write(LanguageContext.Current.CurrentLanguageBranch);
Determine Current Page Language Branch
Response.Write(CurrentPage.LanguageBranch);
Check if a Property is Defined per Language (Language-Specific)
CurrentPage.Property[”MainBody”].IsLanguageSpecific
Check if the Currently Loaded Language is the Master Language for the Current Page
CurrentPage.IsMasterLanguageBranch