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

背景:公司的电子商务网提供多语言的支持。开发语言为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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值