Click here to Skip to main content
15,894,343 members
Articles / Web Development

Localizing WebSites, JavaScript and Assemblies with simple RESX files

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
10 Dec 2012CPOL11 min read 30.7K   246   19  
History of globalization in ASP.NET and available options.
/*
 * T4ResX
 * Author  Robert Hoffmann (itechnology)
 * License MIT / http://bit.ly/mit-license
 *
 * Version 0.99
 * https://github.com/itechnology/T4ResX
 */ 
 using System;
 using System.Linq; 
 using System.Threading;
 using System.Reflection;
 using System.Collections.Generic; 
 using System.Text.RegularExpressions;

 namespace LocalizedAssembly {
 	/// <summary>
	/// Class that contains all our little helper functions
	/// </summary>
	public static class Utilities {
		/// <summary>
		/// A fake attribute that allows us to filter classes by Attribute
		/// It's helpfull when using GetResourcesByNameSpace(), and when T4ResX is tossed into a project containing other classes/properties
		/// Like this we only return stuff generated by T4ResX itself
		/// </summary>
		public class Localized : Attribute {}

		///<summary>
		/// We bind this function to our replacement function when needed
		/// Like this the replacement function can reside in any assembly you like
		/// Bind it once on ApplicationStart, or rebind it to a different replacement function before calling it
		///
		/// Poor Man's IOC
		/// http://www.i-technology.net
		///</summary>
		public static Func<string, string> GetReplacementString = key => key;

		#region Methods
		/// <summary>
		/// Look up ressources from a specific namespace
		/// </summary>
		/// <param name="ns">Namspace to get resources from</param>
		/// <returns>Dictionary&lt;namespace, Dictionary&lt;key, value&gt;&gt;</returns>
		public static Dictionary<string, Dictionary<string, string>> GetResourcesByNameSpace(string ns)
		{
			var result = new Dictionary<string, Dictionary<string, string>>();

			var qs = ns.Split('^');
				ns = qs[0];

			var path  = string.IsNullOrEmpty(ns) ? "LocalizedAssembly" : string.Format("{0}.{1}", "LocalizedAssembly", ns);
			var wCard = path;

			if (ns.EndsWith(".*"))
			{
				wCard = path.Replace(".*", "");
			}
            
			var current = Assembly.GetExecutingAssembly();
			current
				.GetTypes()
                .Where(type => type.GetCustomAttributes(typeof(Localized), false).Length != 0)
                .Where(type => type.Namespace != null && (ns == "" || (ns.EndsWith(".*")
                                                                           ? type.Namespace.StartsWith(wCard, StringComparison.InvariantCultureIgnoreCase)
                                                                           : string.Equals(type.Namespace, path, StringComparison.InvariantCultureIgnoreCase)))
                 )
				.Where(type => qs.Length != 2 || Regex.IsMatch(type.Name, qs[1], RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline))
				.ToList()
				.ForEach(typeFound =>
				{
					var instance = current.CreateInstance(typeFound.FullName);
					if (instance != null)
					{
						var instanceType  = instance.GetType();
						var instanceClass = instanceType.FullName.Replace(wCard, "");
						var propertyList  = new Dictionary<string, string>();

						instanceType
							.GetProperties()
							.Where(t => t.GetCustomAttributes(typeof(Localized), false).Length != 0)
							.ToList()
							.ForEach(property => propertyList.Add(property.Name, property.GetValue(null, null).ToString()));

					    if (propertyList.Count > 0)
					    {
					        result.Add(instanceClass.StartsWith(".") ? instanceClass.Substring(1) : instanceClass, propertyList);
					    }
					}
				});

			return result;
		}
		#endregion
	}
 }
    
namespace LocalizedAssembly.Resources.Models  {
	[Utilities.Localized]	
	public partial class User {
	
		///<summary>
		/// Return this class as a Dictionary&lt;class, Dictionary&lt;key, value&gt;&gt;
		///</summary>
		public static Dictionary<string, Dictionary<string, string>> GetAsDictionary() {
			return Utilities.GetResourcesByNameSpace("Resources.Models^User");
		}

		private static System.Resources.ResourceManager _resourceManager;    
    
		///<summary>
		/// Get the ResourceManager
		///</summary>
		private static System.Resources.ResourceManager ResourceManager 
		{
			get 
			{
				return _resourceManager ?? (_resourceManager = new System.Resources.ResourceManager("LocalizedAssembly.Resources.Models.User", typeof(User).Assembly));
			}
		}

		///<summary>
		///	Get localized entry for a given key
		///</summary>
		public static string GetResourceString(string key, params object[] args)
		{
			var value = ResourceManager.GetString(key, Thread.CurrentThread.CurrentUICulture);

			if (!string.IsNullOrEmpty(value))
			{
                var regex  = @"{\b\p{Lu}{3,}\b}";
                var tokens = Regex.Matches(value, regex).Cast<Match>().Select(m => m.Value).ToList();
                    tokens
                        .ForEach(t =>
                        {
                            value = value.Replace(t, Utilities.GetReplacementString(t.Replace("{", "").Replace("}", "")));
                        });

                if (args.Any())
                {
                    regex  = @"{[0-9]{1}}";
                    tokens = Regex.Matches(value, regex).Cast<Match>().Select(m => m.Value).ToList();

                    if (tokens.Any())
                    {
                        // If argument length is less than token length, add an error message
                        // This can happen if arguments are accidentally forgottent in a translation
                        if (args.Count() < tokens.Count())
                        {
                            var newArgs = new List<object>();
                            for (var i = 0; i < tokens.Count(); i++)
                            {
                                newArgs.Add(args.Length > i ? args[i] : "argument {" + i + "} is undefined");
                            }

                            args = newArgs.ToArray();
                        }

                        value = string.Format(value, args);
                    }
                }		        		
			}
	
			return value;
		} 
		
        ///<summary>
        ///    <list type='bullet'>
        ///        <item>
        ///            <description>Enter your pseudo</description>
        ///        </item>
        ///        <item>
        ///            <description></description>
        ///        </item>
        ///    </list>
		///</summary>
		[Utilities.Localized]
		public static string Pseudo { get { return GetResourceString("Pseudo"); } }

		
	}
}

namespace LocalizedAssembly.Resources.Models  {
		
	public partial class User {
	
        ///<summary>
        ///    <list type='bullet'>
        ///        <item>
        ///            <description>Thanks!</description>
        ///        </item>
        ///        <item>
        ///            <description></description>
        ///        </item>
        ///    </list>
		///</summary>
		[Utilities.Localized]
		public static string PseudoOk { get { return GetResourceString("PseudoOk"); } }

		
	}
}

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
Software Developer (Senior) Index Multimedia
France France
Internet & Technology related professional since 1994. Passionate about WEB 2.0 and Community/Social networking related type Websites, or anything that is tech related to bridging the gap between the User and his everyday Multimedia Experience.

Comments and Discussions