Click here to Skip to main content
15,885,537 members
Articles / Web Development / ASP.NET

Declarative ASP.NET globalization

Rate me:
Please Sign up or sign in to vote.
4.91/5 (44 votes)
16 Apr 2004CPOL8 min read 219.4K   1.7K   93  
An article on how to implement globalization support for ASP.NET pages through attributes and reflection
using System;
using System.Resources;
using System.Reflection;
using System.Globalization;

namespace GlobalizationModule
{
    /// <summary>
    /// Class LocalizeAttribute implements localization of a simple
    /// control by loading a language specific string from a resource
    /// and assigning it to the target object's property.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field, AllowMultiple = false)]
    public class LocalizeAttribute : Attribute
    {
        /// <summary>
        /// Enum LocalizeMode specifies how to localize
        /// target control.
        ///     Fields => localize child objects of target
        ///     Action => localize target
        /// </summary>
        public enum LocalizeMode
        {
            Fields,
            Action
        }

        public LocalizeAttribute()
        {
            // Default action is to assign to the Text property of the target
            action_ = "Text";
            mode_ = LocalizeMode.Action;
        }

        private string action_;
        /// <summary>
        /// Property of target object to assign to.
        /// </summary>
        public string Action
        {
            get { return action_; }
            set { action_ = value; }
        }

        private string resourceName_;
        /// <summary>
        /// Name of resource to load.
        /// </summary>
        public string ResourceName
        {
            get { return resourceName_; }
            set { resourceName_ = value; }
        }

        private string resourceBaseName_;
        /// <summary>
        /// Base name from which resource will be loaded.
        /// </summary>
        public string ResourceBaseName
        {
            get { return resourceBaseName_; }
            set { resourceBaseName_ = value; }
        }

        private LocalizeMode mode_;
        /// <summary>
        /// Specifies how to localize the target control.
        ///     Fields => localize child objects of target
        ///     Action => localize target
        /// </summary>
        public LocalizeMode Mode
        {
            get { return mode_; }
            set { mode_ = value; }
        }

        /// <summary>
        /// Localize 'target' object by replacing the target's Action
        /// property with a localized string.
        /// </summary>
        /// <param name="target">The object to localize</param>
        /// <param name="targetName">Name of the object in its containing class</param>
        /// <param name="page">The web page class that contains the target</param>
        public virtual void LocalizeObject(object target, System.Web.UI.Page page)
        {   
            // User's page class is superclass of the ASP.NET page class
            Type userPageClass = page.GetType().BaseType;
            // The user's assembly is the one that holds his/her page class
            Assembly targetAssembly = userPageClass.Assembly;

            CultureInfo culture = System.Threading.Thread.CurrentThread.CurrentUICulture;

            string s = (string)LoadResource(ResourceName, culture, page, targetAssembly);

            // Invoke the target's Action property. Most of the time this
            // means we'll set the target's Text property.
            target.GetType().InvokeMember(
                this.Action,
                BindingFlags.SetProperty,
                null,
                target,
                new object[]{ s });
        }

        protected object LoadResource(string resourceName, CultureInfo culture, System.Web.UI.Page page, Assembly assembly)
        {
            // First check if we have the resource string in cache -
            // if so, return it from there.
            string key = string.Format("resource.{0}.{1}.{2}", assembly.FullName, culture.Name, resourceName);
            object o = page.Cache[key];
            if (o != null) return o;

            // Load the resource string from the satellite assembly using the
            // resource manager, and save it into the cache for future requests.

            ResourceManager resMan = GetResourceManager(ResourceBaseName, page, assembly);

            // There are a number of ways we could handle the case when a resource
            // is not found - we could e.g., simply allow the exception to pass and
            // let the global OnError handler handle it. Instead, we'll show a special
            // error string.
            string localized = string.Format("<font color=\"red\">MISSING RESOURCE '{0}' FOR CULTURE: {1}</font>", resourceName, culture.Name);
            try
            {
                localized = resMan.GetString(resourceName, culture);
            }
            catch (MissingManifestResourceException)
            {}

            page.Cache[key] = localized;
            return localized;
        }

        /// <summary>
        /// Avoid allocating ResourceManager on every call by allocating
        /// it once and storing it into HttpContext to be reused during
        /// the request.
        /// </summary>
        protected ResourceManager GetResourceManager(string baseName, System.Web.UI.Page page, Assembly assembly)
        {   
            string key = string.Format("{0}.{1}", assembly.FullName, baseName);
            if (page.Application[key] == null)
                page.Application[key] = new ResourceManager(baseName, assembly);
            return (ResourceManager)page.Application[key];            
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Finland Finland
Sami Vaaraniemi has been working as a software developer since 1990, primarily on Microsoft technologies. After 12 years of Win32 API and C++ he switched to .NET. He currently works as an independent consultant and can be contacted through his website at www.capehill.net.

Comments and Discussions