可重用类设计 - 多语言功能开发

    技术2022-05-20  39

    背景:公司的电子商务网提供多语言的支持。开发语言为C#.net ,最初每个网站有一组本地化文件。分别用resx文件保存,各式例如:text.en-us.resx.

     

    然后第一个类,实现读取.resx文件到缓存中。再次使用时直接从缓存中查询数据。

     

    然后每到一个新的项目,直接复制此类文件,然后稍作修改,就又能在新项目中使用。然而这样不断的复制,提高了维护代价,每个不同的平台都有一个类似的类。

     

    同时,为了提高对资源文件的管理,公司要求将本地化的文件保存到数据库中,实现统一的本地化数据管理。另外有可能需要通过创建API提供资源。而非直接数据库读取。

     

    设计,首先定义并实现接口:

     

     

    public interface IResxManager     {                 void Init();         /// <summary>         /// 资源分组,当大量文件存储时。通过分组来分批获得。         /// </summary>         string ResourceGroupName { get; set; }         /// <summary>         /// 语言区.例如:en-us         /// </summary>         string CultureZone { get; set; }         /// <summary>         /// 获取对应key的value         /// </summary>         /// <param name="szKey"></param>         /// <returns></returns>         string GetValue(string szKey);         /// <summary>         /// 设置对应key的值         /// </summary>         /// <param name="key"></param>         /// <param name="value"></param>         void SetValue(string key, string value);         /// <summary>         /// 获取key的值         /// </summary>         /// <param name="szKey"></param>         /// <returns></returns>         string this[string szKey] { get; set; }         /// <summary>         /// 全部资源         /// </summary>         NameValueCollection KeyAndValue { get;  }         /// <summary>         /// Cache管理器         /// </summary>         ICache ResourceCache { get; set; }         IResouceAccess ResourceAccess { get; set; }     }

     

    除了基本的获取内容和设置内容外,重要的是:1.提供数据接口,2.缓存接口(考虑到未来可能分布式缓存)

     

    本地化数据读取接口定义

     

        public interface IResouceAccess     {         NameValueCollection ReadResource(string resourceGroupName, string cultureZone);     }

     

    缓存接口定义

     

    public interface ICache     {         void Add(string szCacheKey, object oObjectToCache);         void Add(string szCacheKey, object oObjectToCache, int nCacheSecs);         object Read(string szCacheKey);         bool Check(string szCacheKey);     }

     

    接口定义完成,开始进行配置类的开发。

     

    允许配置:是否记录丢失的字符串,Cache 提供类,本地化资源提供类,默认语言,默认读取数据分组等信息。通过继承 IConfigurationSectionHandler 接口,实现在web.config文件中添加自定义配置项,从而避免有可能和appsettings重复。

     

     

    public class ResourceClientConfig : IConfigurationSectionHandler     {         private static ResourceClientConfig _config;         public string ResourceAccess { get; set; }         NameValueCollection _nvc;         public string GetSetting(string key)         {             return _nvc[key.ToLower()];         }         public bool LogMissWord {get;set;}         public bool DisplayMissWordError {get;set;}         public string DefaultGroupName { get; set; }         public string CacheProvider { get; set; }         public string DefaultCulterZone { get; set; }         internal ResourceClientConfig()         {                    }         public static ResourceClientConfig GetConfig()         {             if (_config == null)                 _config = (ResourceClientConfig)System.Configuration.ConfigurationManager.GetSection("ResourceClientConfig");             return _config;         }         public object Create(object parent, object configContext, System.Xml.XmlNode section)         {             _nvc =new NameValueCollection();             foreach (System.Xml.XmlNode node in section.ChildNodes)             {                 if (node.Name.ToLower() == "add")                 {                     string value = node.Attributes["value"].Value;                     _nvc.Add(node.Attributes["key"].Value.ToLower(), value);                     switch (node.Attributes["key"].Value.ToLower())                     {                         case "logmissword":                             LogMissWord = value.ToLower() == "true";                             break;                         case "displaymissworderror":                             DisplayMissWordError = value.ToLower() == "true";                             break;                         case "resourceaccess":                             ResourceAccess = value;                             break;                         case "defaultgroupname":                             DefaultGroupName = value;                             break;                         case "cacheprovider":                             CacheProvider = value;                             break;                         case "defaultculterzone":                             DefaultCulterZone = value;                             break;                         default:                             break;                     }                 }             }                        return this;         }     }

     

    本地化资源管理器实现:

     

    /// <summary>     /// Provide localization text     /// </summary>     public class ResxManager:IResxManager     {         protected  NameValueCollection _KeyAndValue = new NameValueCollection();         public virtual NameValueCollection KeyAndValue         {             get { return _KeyAndValue; }         }                 private string _CultureZone;         public virtual string CultureZone         {             get { return _CultureZone; }             set { _CultureZone = value; }         }         public ResxManager()         {         }         public ResxManager(ICache cacheSupplyer, IResouceAccess resourceAccess)         {             this.ResourceCache = cacheSupplyer;             this.ResourceAccess = resourceAccess;         }         /// <summary>         /// Return value given key         /// </summary>         /// <param name="szKey"></param>         /// <returns></returns>         public virtual string GetValue(string szKey)         {             if (_KeyAndValue != null)             {                 if (_KeyAndValue[szKey] != null)                 {                     return _KeyAndValue[szKey];                 }                 else                 {                     return "";                 }             }             else             {                 return "";             }         }         /// <summary>         /// Set key and value pair         /// </summary>         /// <param name="szKey"></param>         /// <param name="szValue"></param>         public virtual void SetValue(string szKey, string szValue)         {             if (_KeyAndValue != null)             {                 if (_KeyAndValue[szKey] != null)                 {                     _KeyAndValue[szKey] = szValue;                 }                 else                 {                     _KeyAndValue.Add(szKey, szValue);                 }             }         }         /// <summary>         /// this         /// </summary>         /// <param name="szKey"></param>         /// <returns></returns>         public virtual string this[string szKey]         {             get             {                 return GetValue(szKey);             }             set             {                 SetValue(szKey, value);             }                     }         /// <summary>         /// Init         /// </summary>         public virtual void Init()         {             NameValueCollection nvc;                         string szCacheKey = string.Format("Resources.{0}.{1}", ResourceGroupName, CultureZone);             if (ResourceCache != null)             {                 if (ResourceCache.Check(szCacheKey))                 {                     nvc = (NameValueCollection)ResourceCache.Read(szCacheKey);                     _KeyAndValue = nvc;                 }                 else                 {                     _KeyAndValue = ResourceAccess.ReadResource(ResourceGroupName, CultureZone);                     ResourceCache.Add(szCacheKey, _KeyAndValue);                 }             }             else             {                 if (_nvc == null)                     _nvc = new Hashtable();                 if (_nvc.ContainsKey(szCacheKey))                 {                     nvc = (NameValueCollection)_nvc[szCacheKey];                     _KeyAndValue = nvc;                 }                 else                 {                     _KeyAndValue = ResourceAccess.ReadResource(ResourceGroupName, CultureZone);                     _nvc.Add(szCacheKey, _KeyAndValue);                 }             }         }         /// <summary>         /// Can be used as cache while not supply cache manager.         /// </summary>         private static Hashtable _nvc;         public virtual ICache ResourceCache         {             get;             set;         }         public virtual string ResourceGroupName         {             get;             set;         }         public IResouceAccess ResourceAccess         {             get;             set;         }     }

     

    为了方便调用,创建一个工厂类,读取配置信息,并返回初始化好的资源管理器,这里通过反射得到资源提供器和缓存管理器的对象。

     

        public class ResourceFactory     {         public static IResxManager GetResourceManager()         {             ResourceClientConfig config = ResourceClientConfig.GetConfig();             IResouceAccess reAc = (IResouceAccess)Activator.CreateInstance(Type.GetType(config.ResourceAccess));             ICache cache = null;             if(!string.IsNullOrEmpty(config.CacheProvider))                 cache = (ICache)Activator.CreateInstance(Type.GetType(config.CacheProvider));             IResxManager _instance = new ResxManager(cache, reAc);             _instance.ResourceGroupName = config.DefaultGroupName;             _instance.CultureZone = config.DefaultCulterZone;             return _instance;         }     }

     

    一个资源管理器就开发完成,这个类可以方便的被任意项目引用,并通过在config文件中配置,实现读取本地化资源。

     

    然后分别编写3个项目,来提供:1.resx文件读取提供器,2.数据库读取提供器,3.API读取提供器, 3个提供器都将继承自接口 IResouceAccess

     

    1.resx文件读取提供器

     

    internal class ResxFileResourceAccess:IResouceAccess     {         public ResxFileResourceAccess()         {             path = ResourceClientConfig.GetConfig().GetSetting("Path");             if (string.IsNullOrEmpty(path))                 throw new Exception("RESX Manager Error. get resource error. not config the resource path");         }         private string path;         public System.Collections.Specialized.NameValueCollection ReadResource(string resourceGroupName, string cultureZone)         {             NameValueCollection nvc = new NameValueCollection();             string szResxFileName = string.Format("{2}/{1}.{0}.resx", cultureZone, resourceGroupName, path);             szResxFileName = System.Web.HttpContext.Current == null ? szResxFileName : System.Web.HttpContext.Current.Server.MapPath(szResxFileName);             if (System.IO.File.Exists(szResxFileName) == true)             {                 System.Diagnostics.Debug.WriteLine(szResxFileName);                 ResXResourceReader r = new ResXResourceReader(szResxFileName);                 foreach (DictionaryEntry d in r)                 {                     nvc.Add(d.Key.ToString(), d.Value.ToString());                 }                 r.Close();             }             else             {                 System.Diagnostics.Debug.WriteLine("无法找到资源文件:" + szResxFileName);                 throw new Exception("Can't find the resx file. filename:" + szResxFileName);             }             return nvc;         }     }

     

    2.数据库读取提供器

     

    internal class ResourceAccess : IResouceAccess     {         public ResourceAccess()         {             strConn = ResourceClientConfig.GetConfig().GetSetting("StrConn");             if (string.IsNullOrEmpty(strConn))                 throw new Exception("RESX Manager Error. get resource error. not config the resource path");         }         private string strConn;         public System.Collections.Specialized.NameValueCollection ReadResource(string resourceGroupName, string cultureZone)         {             NameValueCollection nvc = new NameValueCollection();             string strSql = string.Format(@"select szKey,szText,szLocalText from tb_Localization a left join (select nTextFK,szLocalText from tb_LocalizationLanguage                                  where szCultureZone='{0}') b                                 on a.nTextID=b.nTextFK", cultureZone);             Database oDB = DatabaseFactory.CreateDatabase(strConn);             DbCommand dbCommand = oDB.GetSqlStringCommand(strSql);             DataSet ds = oDB.ExecuteDataSet(dbCommand);             if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)             {                 foreach (DataRow dr in ds.Tables[0].Rows)                 {                     if (!string.IsNullOrEmpty(dr["szLocalText"].ToString()))                         nvc.Add(dr["szKey"].ToString(), dr["szLocalText"].ToString());                     else                         nvc.Add(dr["szKey"].ToString(), dr["szText"].ToString());                 }             }             return nvc;         }     }

     

     

    web.config

     

      <ResourceClientConfig>     <!--<add key="resourceaccess" value="Creative.ResourceClient.ResxFileResourceAccess, Creative.ResourceClient"></add>-->     <!--<add key="resourceaccess" value="Creative.Mobile.EStore.Business.APIResxAccess, Creative.Mobile.EStore.Business"></add>-->     <add key="resourceaccess" value="Creative.ResourceClient.ResourceManagerSQLData.ResourceAccess, Creative.ResourceClient.ResourceManagerSQLData"/>     <add key="cacheProvider" value="Creative.Mobile.EStore.Business.ResourceCache, Creative.Mobile.EStore.Business"></add>     <add key="LogMissWord" value="true"></add>     <add key="DisplayMissWordError" value="true"></add>     <add key="DefaultGroupName" value="estore"/>     <add key="DefaultCulterZone" value="en-gb"/>     <add key= "Path"   value= "~/resources/"/>     <add key="StrConn" value="ResourceData"/>   </ResourceClientConfig>

     

    source code: http://download.csdn.net/user/rungoosc


    最新回复(0)