zoukankan      html  css  js  c++  java
  • [Asp.net 5] Localization-resx资源文件的管理

    上一篇博文地址:[Asp.net 5] Localization-简单易用的本地化-全球化信息

    本文继续介绍asp.net 5多语言。今天重点讲的是多语言的resx文件。涉及到的工程有:Microsoft.Framework.Localization.Abstractions以及Microsoft.Framework.Localization。他们之间的类结构如下如所示:

    • Abstractions包中,包含了定义在工程Microsoft.Framework.Localization.Abstractions的大部分类和接口
    • Localization包中,包含了定义在工程Microsoft.Framework.Localization中的大部分类和接口

    Microsoft.Framework.Localization.Abstractions

    该工程定义了本地化(全球化)的基本接口,以及一些抽象类。

    • LocalizedString 封装name、value等属性的结构体,用于表示本地化寻找的结果
    • IStringLocalizer 本地化访问的基本接口,通过方法或者属性(通过扩展方法,使得方法和属性调用一套逻辑)获得LocalizedString结构体对象;本解决方案中,对资源文件就是该接口的 实例操作的
    • IStringLocalizer<T> 本接口中未定义任何方法,是IStringLocalizer 的泛型版本
    • StringLocalizer<TResourceSource>,该类是IStringLocalizer<T> 的实现类,内部分装IStringLocalizer 接口。使用了代理模式(前面我们介绍的配置文件、以及依赖注入都使用了该模式),所有方法都是内部IStringLocalizer对象的封装。该类的构造函数是IStringLocalizerFactory接口。
    • IStringLocalizerFactory接口:根据不同的Type类型创建不同的IStringLocalizer对象
    public struct LocalizedString
        {
            public LocalizedString([NotNull] string name, [NotNull] string value)
                : this(name, value, resourceNotFound: false)
            {
    
            }
    
            public LocalizedString([NotNull] string name, [NotNull] string value, bool resourceNotFound)
            {
                Name = name;
                Value = value;
                ResourceNotFound = resourceNotFound;
            }
    
            public static implicit operator string (LocalizedString localizedString)
            {
                return localizedString.Value;
            }
    
            public string Name { get; }
    
            public string Value { get; }
    
            public bool ResourceNotFound { get; }
    
            public override string ToString() => Value;
        }
    LocalizedString
        public interface IStringLocalizer : IEnumerable<LocalizedString>
        {
            LocalizedString this[string name] { get; }
    
            LocalizedString this[string name, params object[] arguments] { get; }
            
            IStringLocalizer WithCulture(CultureInfo culture);
        }
    IStringLocalizer
    public interface IStringLocalizer<T> : IStringLocalizer
        {
    
        }
    
    public class StringLocalizer<TResourceSource> : IStringLocalizer<TResourceSource>
        {
            private IStringLocalizer _localizer;
    
            public StringLocalizer([NotNull] IStringLocalizerFactory factory)
            {
                _localizer = factory.Create(typeof(TResourceSource));
            }
    
            public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture);
    
            public virtual LocalizedString this[[NotNull] string name] => _localizer[name];
    
            public virtual LocalizedString this[[NotNull] string name, params object[] arguments] =>
                _localizer[name, arguments];
    
            public virtual LocalizedString GetString([NotNull] string name) => _localizer.GetString(name);
    
            public virtual LocalizedString GetString([NotNull] string name, params object[] arguments) =>
                _localizer.GetString(name, arguments);
    
            public IEnumerator<LocalizedString> GetEnumerator() => _localizer.GetEnumerator();
    
            IEnumerator IEnumerable.GetEnumerator() => _localizer.GetEnumerator();
        }
    StringLocalizer

    根据上面的介绍,我们可以推断该类库的使用方式如下:

    public void Test()
    {    
           IStringLocalizerFactory factory=new ResourceManagerStringLocalizerFactory();
           IStringLocalizer localizer=new StringLocalizer<TType>(factory);
    //IStringLocalizer<TType> localizer=new StringLocalizer<TType>();
    //localizer=localizer.WithCulture(CultureInfo.CurrentUICulture); var localizedString=localizer.GetString(key); }

    [StringLocalizer<TResource>的作用就是隐藏具体IStringLocalizer,使我们不用关心是哪个IStringLocalizer,这样做对于直接书写代码可能看不出直接意义,但是如果使用依赖注入,则可以明显的看出好处,我们可以为IStringLocalizer<TType>直接注入StringLocalizer<TType>类]

    [如果对于依赖注入了解不多,可以看下篇博客:DependencyInjection项目代码分析-目录,略长]

    Microsoft.Framework.Localization

    • ResourceManagerStringLocalizerFactory:创建ResourceManagerStringLocalizer类资源
    • ResourceManagerStringLocalizer:resx多语言文件的访问器(实际该代码并未另起灶炉,而是使用了ResourceManager类)
    • ResourceManagerWithCultureStringLocalizer:ResourceManagerStringLocalizer的子类,提供多语言访问接口。该类实际上是在父类中WithCulture中创建,而该方法内实际也是一个类似于代理模式的设计
    • LocalizationServiceCollectionExtensions类:该类将ResourceManagerStringLocalizerFactory注入到IStringLocalizerFactory接口,StringLocalizer<T> 注入到IStringLocalizer<T> 接口。
    public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory
        {
            private readonly IApplicationEnvironment _applicationEnvironment;
    
            public ResourceManagerStringLocalizerFactory([NotNull] IApplicationEnvironment applicationEnvironment)
            {
                _applicationEnvironment = applicationEnvironment;
            }
    
            public IStringLocalizer Create([NotNull] Type resourceSource)
            {
                var typeInfo = resourceSource.GetTypeInfo();
                var assembly = new AssemblyWrapper(typeInfo.Assembly);
                var baseName = typeInfo.FullName;
                return new ResourceManagerStringLocalizer(new ResourceManager(resourceSource), assembly, baseName);
            }
    
            public IStringLocalizer Create([NotNull] string baseName, [NotNull] string location)
            {
                var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName));
    
                return new ResourceManagerStringLocalizer(
                    new ResourceManager(baseName, assembly),
                    new AssemblyWrapper(assembly),
                    baseName);
            }
        }
    ResourceManagerStringLocalizerFactory
    public class ResourceManagerStringLocalizer : IStringLocalizer
        {
            private static readonly ConcurrentDictionary<string, IList<string>> _resourceNamesCache =
                new ConcurrentDictionary<string, IList<string>>();
    
            private readonly ConcurrentDictionary<string, object> _missingManifestCache =
                new ConcurrentDictionary<string, object>();
    
            private readonly ResourceManager _resourceManager;
            private readonly AssemblyWrapper _resourceAssemblyWrapper;
            private readonly string _resourceBaseName;
    
            public ResourceManagerStringLocalizer(
                [NotNull] ResourceManager resourceManager,
                [NotNull] Assembly resourceAssembly,
                [NotNull] string baseName)
                : this(resourceManager, new AssemblyWrapper(resourceAssembly), baseName)
            {
                
            }
    
            public ResourceManagerStringLocalizer(
                [NotNull] ResourceManager resourceManager,
                [NotNull] AssemblyWrapper resourceAssemblyWrapper,
                [NotNull] string baseName)
            {
                _resourceAssemblyWrapper = resourceAssemblyWrapper;
                _resourceManager = resourceManager;
                _resourceBaseName = baseName;
            }
    
            public virtual LocalizedString this[[NotNull] string name]
            {
                get
                {
                    var value = GetStringSafely(name, null);
                    return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
                }
            }
    
            public virtual LocalizedString this[[NotNull] string name, params object[] arguments]
            {
                get
                {
                    var format = GetStringSafely(name, null);
                    var value = string.Format(format ?? name, arguments);
                    return new LocalizedString(name, value, resourceNotFound: format == null);
                }
            }
    
            public IStringLocalizer WithCulture(CultureInfo culture)
            {
                return culture == null
                    ? new ResourceManagerStringLocalizer(_resourceManager, _resourceAssemblyWrapper, _resourceBaseName)
                    : new ResourceManagerWithCultureStringLocalizer(_resourceManager,
                        _resourceAssemblyWrapper,
                        _resourceBaseName,
                        culture);
            }
    
            protected string GetStringSafely([NotNull] string name, CultureInfo culture)
            {
                var cacheKey = $"name={name}&culture={(culture ?? CultureInfo.CurrentUICulture).Name}";
    
                if (_missingManifestCache.ContainsKey(cacheKey))
                {
                    return null;
                }
    
                try
                {
                    return culture == null ? _resourceManager.GetString(name) : _resourceManager.GetString(name, culture);
                }
                catch (MissingManifestResourceException)
                {
                    _missingManifestCache.TryAdd(cacheKey, null);
                    return null;
                }
            }
    
            public virtual IEnumerator<LocalizedString> GetEnumerator() => GetEnumerator(CultureInfo.CurrentUICulture);
    
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    
            protected IEnumerator<LocalizedString> GetEnumerator([NotNull] CultureInfo culture)
            {
                var resourceNames = GetResourceNamesFromCultureHierarchy(culture);
    
                foreach (var name in resourceNames)
                {
                    var value = GetStringSafely(name, culture);
                    yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
                }
            }
    
            internal static void ClearResourceNamesCache() => _resourceNamesCache.Clear();
    
            private IEnumerable<string> GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture)
            {
                var currentCulture = startingCulture;
                var resourceNames = new HashSet<string>();
    
                while (true)
                {
                    try
                    {
                        var cultureResourceNames = GetResourceNamesForCulture(currentCulture);
                        foreach (var resourceName in cultureResourceNames)
                        {
                            resourceNames.Add(resourceName);
                        }
                    }
                    catch (MissingManifestResourceException) { }
    
                    if (currentCulture == currentCulture.Parent)
                    {
                        // currentCulture begat currentCulture, probably time to leave
                        break;
                    }
    
                    currentCulture = currentCulture.Parent;
                }
    
                return resourceNames;
            }
    
            private IList<string> GetResourceNamesForCulture(CultureInfo culture)
            {
                var resourceStreamName = _resourceBaseName;
                if (!string.IsNullOrEmpty(culture.Name))
                {
                    resourceStreamName += "." + culture.Name;
                }
                resourceStreamName += ".resources";
    
                var cacheKey = $"assembly={_resourceAssemblyWrapper.FullName};resourceStreamName={resourceStreamName}";
    
                var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, key =>
                {
                    var names = new List<string>();
                    using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(key))
                    using (var resources = new ResourceReader(cultureResourceStream))
                    {
                        foreach (DictionaryEntry entry in resources)
                        {
                            var resourceName = (string)entry.Key;
                            names.Add(resourceName);
                        }
                    }
    
                    return names;
                });
    
                return cultureResourceNames;
            }
        }
    ResourceManagerStringLocalizer
    public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer
        {
            private readonly CultureInfo _culture;
    
            public ResourceManagerWithCultureStringLocalizer(
                [NotNull] ResourceManager resourceManager,
                [NotNull] Assembly assembly,
                [NotNull] string baseName,
                [NotNull] CultureInfo culture)
                : base(resourceManager, assembly, baseName)
            {
                _culture = culture;
            }
    
            public ResourceManagerWithCultureStringLocalizer(
                [NotNull] ResourceManager resourceManager,
                [NotNull] AssemblyWrapper assemblyWrapper,
                [NotNull] string baseName,
                [NotNull] CultureInfo culture)
                : base(resourceManager, assemblyWrapper, baseName)
            {
                _culture = culture;
            }
    
            public override LocalizedString this[[NotNull] string name]
            {
                get
                {
                    var value = GetStringSafely(name, _culture);
                    return new LocalizedString(name, value ?? name);
                }
            }
    
            public override LocalizedString this[[NotNull] string name, params object[] arguments]
            {
                get
                {
                    var format = GetStringSafely(name, _culture);
                    var value = string.Format(_culture, format ?? name, arguments);
                    return new LocalizedString(name, value ?? name, resourceNotFound: format == null);
                }
            }
    
            public override IEnumerator<LocalizedString> GetEnumerator() => GetEnumerator(_culture);
        }
    ResourceManagerWithCultureStringLocalizer
    public static class LocalizationServiceCollectionExtensions
        {
            public static IServiceCollection AddLocalization([NotNull] this IServiceCollection services)
            {
                services.TryAdd(new ServiceDescriptor(
                    typeof(IStringLocalizerFactory),
                    typeof(ResourceManagerStringLocalizerFactory),
                    ServiceLifetime.Singleton));
                services.TryAdd(new ServiceDescriptor(
                    typeof(IStringLocalizer<>),
                    typeof(StringLocalizer<>),
                    ServiceLifetime.Transient));
    
                return services;
            }
        }
    LocalizationServiceCollectionExtensions

    由于依赖注入的关系所以我们可以将代码简单的修改成如下:

              //var services = new ServiceCollection();
            //services.AddLocalization(); 系统初始化时执行过该部分代码
            public string Test(string key)
            {    
                 var localizer=Provider.GetService<IStringLocalizer<RescoureType>>();
                 //localizer=localizer.WithCulture(CultureInfo.CurrentUICulture);
                 var localizedString=localizer.GetString(key);
    if(localizedString.ResourceNotFound){
    reuturn null;
    }
    return localizedString.Value; }


     

  • 相关阅读:
    ERP/MIS开发 30道ORM问题与解答 LLBL Gen 3.x Adapter
    升级LLBL Gen 2.x项目到3.x
    软件公司为什么要加密源代码,而且是前前后后,反反复复
    ERP/MIS系统中集成命令行式的功能调用
    工作多年后才明白的.NET底层开发技术
    OSGI:从面向接口编程来理解OSGI
    幸福框架:如何阅读幸福框架的代码
    OSGI:C#如何实现简单的OSGI
    技术人生:技术之路,需要的是热情和梦想
    EHR:对人力资源信息系统的认识
  • 原文地址:https://www.cnblogs.com/watermoon2/p/4537660.html
Copyright © 2011-2022 走看看