思路大概是这样的,将翻译成不同语言的词句以xml键值对的格式分别存在不同文件夹下,扩展MVC HtmlHelper, 在扩展方法里根据用户当前访问Action所在的路径以及当前所选的语言类型读取语言文件夹下的xml文件,再通过HtmlHelper传过来的key获取对应的value。若找不到,则将key自动添加到相应的文件里面。切换语言时将所选的语言类型保存在Session,再Redirect。
写一个静态类LangHelper,用以操作语言文件。本例中,将中文、英文语言文件分别保存在网站根目录下Resources下的zh-cn和en文件夹里面,文件类型为res。为防止每次访问都去读取操作那些语言文件,我声明一个Dictionary静态变量,用来保存访问过的语言文件里的key\value,在下次(别的用户)访问时先从已保存在服务器内存的静态字典中读取。这样就加快了访问速度。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Linq; using System.Web;namespace Utility {public static class LangHelper{//全局静态变量private static Dictionary<string, LanguageInfo> Languages = new Dictionary<string, LanguageInfo>();public static string GetLangString(string key, string langType){if (String.IsNullOrEmpty(key))throw new ArgumentException("key can not be null!", "key");if (String.IsNullOrEmpty(langType))throw new ArgumentException("language type can not be null!", langType);string filename = "common";langType = langType.ToLower();LanguageInfo info = GetLanguageInfo(langType, filename);//默认先到通用的语言文件里去找if (info.LanguageDictionary.ContainsKey(key.ToLower()))return info.LanguageDictionary[key.ToLower()];else{string filePath = HttpContext.Current.Request.FilePath;if (filePath == HttpContext.Current.Request.ApplicationPath){info.AddKey(key, key);//添加到通用语言文件return key;}else{if (filePath[0] == '/') filePath = filePath.Substring(1, filePath.Length - 1);string[] words = filePath.Split(new char[] { '/' });filename = String.Format("{0}", words[0]);//当前Controller所在的文件夹名字,也即语言文件的名字info = GetLanguageInfo(langType, filename);if (info.LanguageDictionary.ContainsKey(key.ToLower()))return info.LanguageDictionary[key.ToLower()];else{info.AddKey(key, key);return key;}}}}private static LanguageInfo GetLanguageInfo(string langType, string filename){string cacheKey = String.Format("{0}{1}", langType, filename);LanguageInfo info = null;if (Languages.ContainsKey(cacheKey))info = Languages[cacheKey];else{info = new LanguageInfo(langType, filename);if (filename != "common") Languages.Clear();Languages[cacheKey] = info;}return info;}}public class LanguageInfo{public string LanguageCode { get; set; }public string ResourceFile { get; set; }public XElement RootElement { get; private set; }public Dictionary<string, string> LanguageDictionary { get; private set; }public LanguageInfo(string languageCode, string resourceFile){this.LanguageCode = languageCode;this.ResourceFile = HttpContext.Current.Server.MapPath(String.Format("~/Resources/{0}/{1}.res",languageCode, resourceFile));this.LanguageDictionary = this.BuildLanguageDictionary();}/// <summary>/// 读取XML文件构建语言字典/// </summary>/// <returns></returns>public Dictionary<string, string> BuildLanguageDictionary(){Dictionary<string, string> dic = new Dictionary<string, string>();if (System.IO.File.Exists(ResourceFile))RootElement = XElement.Load(ResourceFile);else{RootElement = new XElement("language");}IEnumerable<XElement> adds = RootElement.Elements();foreach (XElement add in adds){string k = add.Attribute("key").Value;string v = add.Attribute("value").Value;dic[k.ToLower()] = v;}return dic;}public void SaveKey(string key, string value){if (String.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return;XElement e = RootElement.Elements().Where(a => a.Attribute("key").Value.ToLower() == key.ToLower()).FirstOrDefault();if (e == null) return;e.SetAttributeValue("value", value);RootElement.Save(ResourceFile);}/// <summary>/// 向语言文件添加键值对/// </summary>/// <param name="key"></param>/// <param name="value"></param>public void AddKey(string key, string value){XElement newel = new XElement("add");newel.SetAttributeValue("key", key);newel.SetAttributeValue("value", key);RootElement.Add(newel);try{RootElement.Save(ResourceFile);}catch (Exception e){}LanguageDictionary[key.ToLower()] = value;}public void RemoveKey(string key){if (String.IsNullOrEmpty(key)) return;XElement e = RootElement.Elements().Where(a => a.Attribute("key").Value.ToLower() == key.ToLower()).FirstOrDefault();if (e == null) return;e.Remove();RootElement.Save(ResourceFile);}} }
GlobalizeUtil 类用来控制当前用户选择的语言类型
public class GlobalizeUtil{ public static string GetCurrentLanguage(){ object lang = HttpContext.Current.Session["language"];if (lang == null)return "zh-cn";elsereturn lang.ToString(); }public static void SetCurrentLanguage(string langCode){HttpContext.Current.Session["language"] = langCode;}}
扩展MVC HtmlHelper,方便在View中调用。扩展方法名为String,需要一个string类型参数,此参数即为key,当语言文件中不存在此key时,将此key直接作为value输出,并保存在语言文件中。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; using System.Web; using System.Web.Mvc; using System.Xml.Linq; namespace Utility {public static class Extensions{/// <summary>/// 在Mvc View中调用/// </summary>/// <param name="htmlHelper"></param>/// <param name="key"></param>/// <returns></returns>public static string String(this HtmlHelper htmlHelper, string key){return htmlHelper.Encode(GetLangString(key));}/// <summary>/// 在C#代码中使用/// </summary>/// <param name="key"></param>/// <returns></returns>public static string LangString(string key){return GetLangString(key);}private static string GetLangString(string key){ string langType = GlobalizeUtil.GetCurrentLanguage();return LangHelper.GetLangString(key, langType);} } }
在View中的使用如: Html.String("Username"),在common.res语言文件里面就会添加一条 <add key="Username" value="Username" />记录,将zh-cn文件夹下的common.res里这条记录的value改为"用户名",则当用户在中英文之间切换时就会按不同语言显示。
效果图一:
效果图二:
不知有没有更好的方法,欢迎指教!