[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;
}
[Asp.net 5] Localization-resx资源文件的管理的更多相关文章
- 解决asp.net mvc中*.resx资源文件访问报错
个人笔记 问题重现 在asp.net mvc中,使用资源文件会出现一个问题,例如: 紧接着我进入视图界面,输入下面代码: <a href="javascript:void(0);&qu ...
- .net RESX资源文件
RESX资源文件最大的优势就是: 支持多语言 快速创建资源 管理方便 RESX可以支持多语言,Visual Studio编译后会出现附属程序集(satellite assembly),事实上是连接器( ...
- VS下对Resx资源文件的操作
原文:VS下对Resx资源文件的操作 读取 using System.IO; using System.Resources; using System.Collections; using Syste ...
- asp.net core合并压缩资源文件引发的学习之旅
0. 在asp.net core中使用BuildBundlerMinifier合并压缩资源文件 在asp.net mvc中可以使用Bundle来压缩合并css,js 不知道的见:http://www. ...
- asp.net core合并压缩资源文件(转载)
在asp.net core中使用BuildBundlerMinifier合并压缩资源文件 在asp.net mvc中可以使用Bundle来压缩合并css,js 不知道的见:http://www.cnb ...
- C#调用Resources.resx资源文件中的资源
使用到了.NET中的资源文件,也就是Resources.resx,于是就学会了如何调用资源文件中的资源.首先,资源文件可以从项目属性中的资源标签添加.比如,我添加一个图片,叫做aaa.png,添加入资 ...
- PyQt(Python+Qt)学习随笔:Qt Designer中图像资源的使用及资源文件的管理
一.概述 在Qt Designer中要使用图片资源有三种方法:通过图像文件指定.通过资源文件指定.通过theme主题方式指定,对应的设置界面在需要指定图像的属性栏如windowIcon中通过点击属性设 ...
- Asp.net 引用css/js资源文件
注意Page.ResolveUrl之前的双引号,不是单引号 <script type="text/javascript" src="<%= Page.Reso ...
- Asp.net中使用资源文件实现网站多语言
首先需要新建一个ASP.NET Web Application.然后右键项目文件Add->Add ASP.NET Folder->App-GlobalResources. 新建好资源文件夹 ...
随机推荐
- CYQ.Data V5 从入门到放弃ORM系列:框架的优势
前言: 框架开源后,学习使用的人越来越多了,所以我也更加积极的用代码回应了. 在框架完成了:数据库读写分离功能 和 分布式缓存功能 后: 经过三天三夜的不眠不休,终于完成框架第三个重量级的功能:自动化 ...
- 剑指Offer面试题:25.二叉搜索树与双向链表
一.题目:二叉搜索树与双向链表 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.比如输入下图中左边的二叉搜索树,则输出转换之后的 ...
- LOMA280保险原理读书笔记
LOMA是国际金融保险管理学院(Life Office Management Association)的英文简称.国际金融保险管理学院是一个保险和金融服务机构的国际组织,它的创建目的是为了促进信息交流 ...
- .NET中异常处理的最佳实践(译)
本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头.鼠标以及键盘 “写操作”同样可能失效 安全编程 不要抛出“new ...
- NodeJS写个爬虫,把文章放到kindle中阅读
这两天看了好几篇不错的文章,有的时候想把好的文章 down 下来放到 kindle 上看,便写了个爬虫脚本,因为最近都在搞 node,所以就很自然的选择 node 来爬咯- 本文地址:http://w ...
- 迷你MVVM框架 avalonjs 实现上的几个难点
经过两个星期的性能优化,avalon终于实现在一个页面绑定达到上万个的时候不卡顿的目标(angular的限制是2000).现在稍作休息,总结一下avalon遇到的一些难题. 首先是如何监控的问题.所有 ...
- 跟我一起数据挖掘(21)——redis
什么是Redis Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工 ...
- 如何在 ASP.NET MVC 中集成 AngularJS(3)
今天来为大家介绍如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分内容. 调试路由表 - HTML 缓存清除 就在我以为示例应用程序完成之后,我意识到,我必须提供两个版本的路由表 ...
- JavaScript必须了解的知识点总结。
整理的知识点不全面但是很实用. 主要分三块: (1)JS代码预解析原理(包括三个段落): (2)函数相关(包括 函数传参,带参数函数的调用方式,闭包): (3)面向对象(包括 对象创建.原型链,数据类 ...
- Execute Sql Task 的Result DataSet如何返回
Execute Sql Task的Result DataSet 主要有以下四种,当Execute Sql Task返回结果之后,需要使用SSIS Variable 来接收数据. 例子中使用的数据表代码 ...