[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;
        }
时间: 2024-12-14 16:53:43

[Asp.net 5] Localization-resx资源文件的管理的相关文章

解决asp.net mvc中*.resx资源文件访问报错

个人笔记 问题重现 在asp.net mvc中,使用资源文件会出现一个问题,例如: 紧接着我进入视图界面,输入下面代码: 1 <a href="javascript:void(0);">测试@KuaiLeYouNi.Web.AppResource.Space</a> 以上编译不会报错,但是运行是会报错:“编译器错误消息: CS0122: “KuaiLeYouNi.Web.AppResource”不可访问,因为它受保护级别限制” 问题解决 原来资源文件默认的访问修

VS下对Resx资源文件的操作

原文:VS下对Resx资源文件的操作 读取 using System.IO; using System.Resources; using System.Collections; using System.Reflection; namespace ResxEdit { class ResxDemo { void ReadResx(string strResxPath, Boolean isResourcePath) { AssemblyName[] referencedAssemblies =

本地化资源文件关键字重复的报错解决。

A dictionary can not contain same key twice. There are some duplicated names: Name, EmailAddress. 解决: Localization本地化资源文件,删除重复的Name.EmailAddress.

在.NET中读取嵌入和使用资源文件的方法

转http://www.jb51.net/article/84660.htm 本文分别介绍了使用GetManifestResourceStream读取嵌入资源,和使用. resx资源文件嵌入资源,希望对大家有所帮助. Dotnet中嵌入资源(位图.图标或光标等)有两种方式,一是直接把资源文件加入到项目,作为嵌入资源,在代码中通过Assembly的GetManifestResourceStream方法获取资源的Stream.另一种方法是在项目中加入. resx资源文件,在资源文件中添加资源,由Re

【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OSS(https://www.aliyun.com/product/oss) 4.HTTPS(http://baike.baidu.com/view/14121.htm) 阅读目录结构 引: 一.准备工作 二.整体功能结构 三.具体实现步骤 四.关键点和问题处理 五.延伸与扩展 六.总结与思考 引:

asp.net 资源文件的使用

通过简单的设置和引用即可实现不同语言的转变 1.新建资源文件Resource.resx,VS2005自动将其放在App_GlobalResources文件夹中: 2.打开资源文件,输入常用的字符串,文件里面是一些key/value的集合,key用来做引用的标识,value用来表示字符串的值:(保存后可以看到Resource.resx实际是一个xml文件) 3.在系统里面进行引用: cs文件中的引用:string strHint = Resources.Resource.key aspx文件中的普

asp.net 网站国际化在使用资源文件发布到IIS上不可访问,因为它受保护级别限制

asp.net 网站国际化在使用资源文件发布到IIS上报错:编译器错误消息: CS0122: "Resources.Resource"不可访问,因为它受保护级别限制 后来在网上找了很久,才找了个解决方案 在资源文件属性上选择生成操作为"内容",不要选择"嵌入的资源"

C# 如何使用程序添加查询和编辑resx文件(资源文件)

在C#项目中难免用到资源文件(以resx结尾的文件) 读取和添加的方法: 在项目中首先要引用命名空间 using System.Resources; string [email protected]".\Resources1.resx"; //给资源文件中写入字符串 using(ResXResourceWriter resx=new ResXResourceWriter(respath)) { resx.AddResource("String1","你想要

让asp.net网站支持多语言,使用资源文件

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1