Embedding EPiServer language files

May 18, 2010

One of the things that I’m always aiming for when creating a module for EPiServer is that you should be able to install it simply by dropping a single assembly to the bin folder. There should be no or very little configuration needed (Convention over Configuration!)  and you shouldn’t have to place files in folders here and there.

The first and probably most important step towards this goal was introduced to the wider EPiServer audience by Dan, Johan and Allan quite a while back ago, where they showed how to reference embedded .aspx files using a virtual path provider.

At Intergen were I currently work we are using this technique frequently and as of late we also introduced a standard way to embed EPiServer language files within these module assemblies, the MetaPropertyModifier that I wrote about the other week is built with embedded language files. The key requirements that we set up for this was that it should still be possible to add more languages later and also possible to override strings in the included languages.

As Anders has pointed out before, EPiServer doesn’t make it easy for you to override the LanguageManager as none of the methods or properties are virtual. So while we are waiting for this to be changed, we decided to go the easy way and work with what we got. The technique we use is actually very simple as it is utilizing the available API as much as possible. It basically goes like this:

  1. When our self-contained module is initialized
  2. Check if the section we are trying to insert is loaded for the given language
  3. If not load the embedded resource and load the stream using the existing method on  the LanguageDocument class
  4. Repeat 2 & 3 for all available language files

Unfortunately this only allows users to override all or nothing (per language) for our module, but I thought this was a decent trade off considering how much less code we would have to write and maintain and that it would probably all change if <wishlist>the LanguageManager is made easier to override or some events are added that are raised when language resources are loaded</wishlist>.

The actual implementation looks something like this, with the following code in the module initializer:

    LanguageLoader.LoadLanguageResourceIfNotFound("en", "/modules/mymodule", "MyModule.UI.Lang.myembeddedlangfile_EN.xml");

The language loader class looks something like this:

    public class LanguageLoader
    {
        /// <summary>
        /// Load a language resource to the EPiServer Language Manager if not a node with the given key
        /// already has been loaded. This is used to enable language overrides.
        /// </summary>
        /// <param name="language">Language code (2 chars)</param>
        /// <param name="rootKey">
        /// XPath to the root node. If this exists it will be seen as it has been loaded already.
        /// </param>
        /// <param name="resourceName">Name of the embedded language xml resource file to load.</param>
        public static void LoadLanguageResourceIfNotFound(string language, string rootKey, string resourceName)
        {
            if (!LanguageNodeIsLoaded(language, rootKey))
            {
                LoadLanguageResource(resourceName);
            }
        }

        /// <summary>
        /// Loads a language resource to the EPiServer Language manager
        /// </summary>
        /// <param name="resourceName">Name of the resource to load.</param>
        public static void LoadLanguageResource(string resourceName)
        {
            Stream resource = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
            if (resource == null)
            {
                throw new InvalidOperationException("Unable to find a resource with the name: " + resourceName);
            }
            LanguageManager.Instance.LanguageDocument.Load(resource);
        }

        /// <summary>
        /// Checks if a specific language node has been loaded.
        /// </summary>
        /// <param name="language">Language code (2 chars)</param>
        /// <param name="rootKey">XPath to the root node</param>
        /// <returns>
        /// True if a language node with the given path has been loaded for the given language.
        /// </returns>
        public static bool LanguageNodeIsLoaded(string language, string rootKey)
        {
            return LanguageManager.Instance.LanguageDocument.FindNode(language.ToUpper() + rootKey) != null;
        }
    }

As a final note there is of course another obvious way to do it, and that is to use the standard .NET resource assemblies, but where is the fun in that!?

Advertisements

3 Responses to “Embedding EPiServer language files”

  1. Anders Hattestad Says:

    I like this method :)


Comments are closed.

%d bloggers like this: