If you're ever asked to build a multi-lingual site in SharePoint, it quickly becomes apparent that there are a few extra considerations compared to a single language site. These could include:
- Information architecture
- Language/culture detection
- Deciding whether to use variations or not
- URL strategy
..and so on. Clearly these are decisions which will have a different 'answer' for every multi-lingual project, and typically your client's specific requirements will steer your approach. However one challenge which is likely to remain constant across most such projects is this one:
- How to deal with the many small strings of text which are not part of authored page content which need to be translated and displayed in the appropriate language
This is the challenge I'm focusing on here. To illustrate, here's an example from the BBC site where I've highlighted all the strings which may need to be translated but which don't belong to a particular page:
..and that's just one page - it turns out a typical site will have many of these. If you have to translate additional strings shown to authors in edit mode only, you could easily find the total number stretching into the hundreds. So we start to need a framework for storage/retrieval of these 'page furniture' items. If we were dealing with a shrink-wrapped product, .Net resource files could be a good choice, but this approach is probably not flexible enough for a website and won't allow content authors/power users to enter translations. Clearly something based around a SharePoint list is called for, so enter the 'Language Store' - my solution to the problem which you can now download from Codeplex (link at the end).
Introducing the Language Store
The Language Store is an adaptation of my earlier Config Store solution and follows some of the same principles:-
- values are stored in a SharePoint list
- an API is provided to retrieve values with a single method call
- a caching framework is used for optimum performance
- easily deployed as a .wsp
Items in the list are categorized, and have a column for each language translation:
Note that each translation column in the list is named with the convention 'LANG_<culture name>' (N.B. you might know a 'culture name' as a 'locale ID' or similar) - so when a new language needs to be added to the site, you simply create a new column with the appropriate name and add the translations. A list of culture names can be found in the MSDN documentation for the CultureInfo class.
UPDATED - the 'National Language Support' API reference page on MSDN is a better reference - think the CultureInfo page has changed since I linked to it.
To retrieve a value, we simply call the GetValue() method and pass the category and title of the item to retrieve:
Also, since many of the items we might put in the Language Store are only used in the presentation of the page, it's often a shame to have to switch to the code-behind just to fetch these values and assign them to a control's 'Text' property. So I've provided a tokenized method similar to SPUrl, which allows you to simply drop Language Store values into your markup like this:
I like this because it means you don't end up cluttering your code-behind with lots of lines just for fetching values from the Language Store and assigning them to ASP.Net labels or controls. For those who don't know how this is done I'll write more about it in the next post as I think it's a cool, under-used facility in .Net.
In the current implementation, the regional settings of the SPWeb are used to determine which translations are retrieved. It's a single method in the code (a single line in fact!), so this scheme could easily be changed if you have a different requirement. We're using the Language Store on our current project, and using the SPWeb setting makes sense for us since we're building around 100 different sites in ~30 languages, as opposed to one site which displays in the local language (according to the user's thread culture or similar).
Note that if the Language Store doesn't contain a value for the requested culture, a fallback process is used similar to .Net's globalization framework:
- Check preferred culture e.g. fr-CH for French (Switzerland)
- Check preferred culture's parent e.g. fr for French
- Check default language (determined by configuration) e.g. en for English
This is useful where some items might have a different version for say American English (EN-US) and British English (EN-GB), but other items don't require distinction so a single value can be entered into the parent column (EN).
By the way, if you're wondering where the SPWeb regional settings are because you've never needed to change them, they're here:
A checkbox allows the regional settings you make on a given web cascade down to child webs, so we simply set this at the root of the site as a one time operation.
Other bits and pieces
- All items are wrapped up in a Solution/Feature so there is no need to manually create site columns/content types/the Language Store list etc. There is also an install script for you to easily install the Solution.
- Caching implementation is currently based around a CacheDependency on a file - this enables the cache on all servers in your farm to be invalidated when an item is updated, but does require that all WFEs can write to this location (e.g. firewalls are not in the way).
- The Language Store can also be used where no SPContext is present e.g. a list event receiver. In this scenario, it will look for values in your SharePoint web application's web.config file to establish the URL for the site containing the Language Store (N.B. these web.config keys get automatically added when the Language Store is installed to your site). This also means it can be used outside your SharePoint application, e.g. a console app.
- The Language Store can be moved from it's default location of the root web for your site (to do this, create a new list (in whatever child web you want) from the 'Language Store list' template (added during the install), and modify the 'LanguageStoreWebName'/'LanguageStoreListName' keys which were added to your web.config to point to the new location. As an alternative if you already added 100 items which you don't want to recreate, you could use my other tool, the SharePoint Content Deployment Wizard at http://www.codeplex.com/SPDeploymentWizard to move the list.)
- All source code and Solution/Feature files are included, so if you want to change anything, you can.
- Installation instructions are in the readme.txt in the download.
You can download the Language Store and all source code from www.codeplex.com/SPLanguageStore. All feedback welcome!