multi lingual websites in umbraco
DESCRIPTION
Presentation given at UK Umbraco User Group Meeting in Summer 09TRANSCRIPT
MULTI-LINGUAL WEBSITES IN UMBRACO
A Case Study of Savant.AECom.comPaul MardenDirector
Background
This is a practical case study, not intended to be a demonstration of best practice.
Project was to migrate an existing ASP / Flash site from a proprietary CMS to Umbraco.
Aim was to provide a functionally equivalent front-end that required the minimum amount of rework of the Flash components.
About the client
Savant is an international construction company with 20 offices across 13 countries in Europe and Central Asia.
Their site provides all primary content in English and Russian, with 11 regional office micro-sites in English and that country’s language.
In all cases there is a 1-to-1 match between English / Russian nodes or English / 3rd Language nodes.
Specs
Built on 4.0.0 RC1 and currently deployed on 4.0.1
Running on Fasthosts dedicated server; Windows Server 2003 Web Edition SQL Server 2005 Express Edition
~320 nodes, 22 document types, 28 templates & 23 macros
Flash integration
The site uses Flash movies extensively to display content in an attractive way.
Much is legacy code inherited from the existing version of the site/cms.
We made use of sIFR v3 to render page headers and menu’s. Started with fonts generated by sIFR
Generator but reverted to creating the sIFR font file in Flash – not all Cyrillic fonts are the same!
Approaches to multilingual sites
After some research there seemed to be two design patterns for multi-lingual sites: Multi-lingual 1:1 sites (as written about by
Torleik) Separate branches for each language
We ended up with a compromise between both!
Separate host names
Originally designed to separate out English and Russian pages with two root nodes. In the dev version these had separate host names and languages (best practice?). Also required configuration of IIS to route
traffic to the appropriate Website! An advantage of this was that we could
have Cyrillic URL’s giving better SEO.
content
en
About
News
EN News Ite
m 1
EN News Ite
m 2
People …
rs
Введение
О компании
RS News Item 1
RS News Item 2
Персонал …
lv
Par mums
Pakalpojumi
Pieredze
…
Information Architecture
But...
Existing Flash movies for News, Projects etc, needed EN & RS in the same XML feed, so we needed a way of retrieving EN & RS for News Item 1, News Item 2 etc.
URLs with Cyrillic's broke the movies and rendered rather strangely when pasted into emails... http://rs.savant.capbuild.com/%D0%BE%D0%BF%D1%8B%D1%82-%D1%80%D
0%B0%D0%B1%D0%BE%D1%82%D1%8B.aspx
We kept the separate root nodes for EN and RS, but gave up on Cyrillic URLs and also didn’t implement separate host names. Instead naming pages e.g. Experience.aspx and ExperienceRS.aspx
! This was a bad design decision !
Relating nodes
Each page on the site has to relate together EN and RS content, so that it was possible to flick between different language versions of the page.
We achieved this using a content picker field on each doc type.
Within the template we call a macro to insert the link to the other page:
<xsl:variable name=“link” select=“$currentPage/data[@alias='LinkedPage‘]”/>
<xsl:if select=“$link != ‘’">
<a href="{umbraco.library:NiceUrl($link)}">English</a>
</xsl:if>
Listing pages
The site consists of four “database” like document types. These hold job opportunities, projects completed, press releases and office locations.
Each is output on a listing page. This page contains a paged static HTML version of the items, as well as a call to a Flash movie which retrieves the items as an XML file to display a prettier version.
We began with separating out the list item nodes by language, but the users found this difficult to manage, so we reverted to a 1:1 multilingual pattern.
Listing pages (templates & macros) Each listing has an HTML view and an
XML view. In order to retain backwards
compatibility with existing Flash movie we implemented two pages e.g. news.aspx and getNewsXML.aspx. Each had their own Document Type, Template and Macro for retrieving list items. Although were it to be a virgin site, I would
have gone for one page, with two templates then used ?alttemplate=othertemplate
Plan was deploy to Savant’s shared server architecture.
But once deployed we found this was a medium trust environment.
So the day before planned go live we bought a Fasthosts dedicated server.
Implementation Pains
Dictionary
In the latest edition of the site we’ve implemented translation using the Umbraco dictionary.
This makes use of the Languages defined for the site to display a neat interface (in U4) that display words to be translated.
Functions are then available in umbraco.library to insert a translated value for a certain keyword.
This reduces the number of macros in the site, simplifies them, and provides a better editors interface.
Standard dictionary access
In a macro to retrieve a translated value use:<xsl:value-of
select=“umbraco.library:GetDictionaryItem(‘Aviation’)”/>
Which returns a string containing the translated value using the current language context.
Language context is calculated by the hostname.
Orcare Dictionary Access (part 1) Reading the API docs (with help from
Ron Brouwer) I found that it is possible to access the Dictionary via the API.
Attempt number 1 tried to embed a C# function into the macro’s XSL. Plagiarising code from Doug’s XSLTSearch.
But I couldn’t make the call out to the api...
Orcare Dictionary Access (part 1)<msxsl:script language="C#" implements-prefix="orcare">
<![CDATA[ public string GetDictionaryItemByContext(string key, int languageId) { return new umbraco.cms.businesslogic.Dictionary.DictionaryItem(key).Value(languageId); } ]]></msxsl:script>
Error in XSLT at line 233, char 20231: public string GetDictionaryItemByContext(string key, int languageId)232: {233: >>> return new umbraco.cms.businesslogic.Dictionary.DictionaryItem(key).Value(languageId); <<<234: }
Swag Alert
Orcare Dictionary Access (part 2) So we built our own library which can be
called from XSL files. Based on very old notes from Wikibooks.
Create a new project Create a class (OrcareLib) Import all the necessary parts of the
Umbraco API Create your methods
Public Class OrcareLib Public Shared Function GetDictionaryItemByLanguageId(ByVal
key As String, ByVal languageId As Integer) As String Dim d As New Dictionary.DictionaryItem(key) GetDictionaryItemByLanguageId = d.Value(languageId) End Function
Public Shared Function GetDictionaryItemByCultureCode(ByVal key As String, ByVal cultureCode As String) As String
Dim d As New Dictionary.DictionaryItem(key) Dim l As Language l = Language.GetByCultureCode(cultureCode) GetDictionaryItemByCultureCode = d.Value(l.id) End FunctionEnd Class
Orcare Dictionary Access (part 2)
Build the project, and copy the dll into the /bin folder in umbraco
Register the library in /config/xsltExtensions.xml
<ext assembly="/bin/OrcareLib" type="com.orcare.OrcareLib" alias="orcare.library" />
Add the library into the head of any XSL filesxmlns:orcare.library ="urn:orcare.library" exclude-result-
prefixes="msxml umbraco.library orcare.library”
Call the new methods<xsl:value-of
select=“orcare.library:GetDictionaryItemByCultureCode (‘Aviation’,’ru-ru’)”/>
Orcare Dictionary Access (part 2)
Think more carefully about how you architect the site. 1:1 may be easier for your users, but breaks the
dictionary. Separate hostnames for separate languages gives
much better flexibility, but can be confusing for editors.
But the editors still love the editing experience in Umbraco, even with the compromises we made.
If you are planning to deploy to a new environment test it early to make sure it’s suitable.
Parting shots