.NET中 本就为我们提供了自定义国际化资源的借口,IStringLocalizerFactory 和 IStringLocalizer,只要实现了这两个借口就可以自定义我们的国际化资源。我的国际化资源是存放在数据库里面的,在数据库按指定区域的区域码提取后,转存到redis中,再供给后端各个服务使用。
其中
IStringLocalizerFactory 这个接口就是要我们实现数据来源的,他可以是json文件,也可以是数据库资源。
IStringLocalizer 这个接口在底层会被IStringLocalizerFactory接口创建实现了IStringLocalizer接口的对象,我们在后端的各个服务中使用国际化,实际上用的就是实现 IStringLocalizer 接口的实例。
来看一下我的 IStringLocalizerFactory接口
namespace ***********
{
public class SqlStringLocalizerFactory : IStringLocalizerFactory
{
private readonly ISysCacheService cache;
public SqlStringLocalizerFactory(ISysCacheService _cache)
{
cache = _cache;
//向Redis中注入翻译数据
getTranslateLibToRedis();
}
public IStringLocalizer Create(Type resourceSource)
{
//创建IStringLocalizer 接口的实例对象,默认都是使用此方法
return new SqlStringLocalizer(this.cache);
}
public IStringLocalizer Create(string baseName, string location)
{
//创建IStringLocalizer 接口的实例对象
return new SqlStringLocalizer(this.cache);
}
/// <summary>
/// 将数据库中所有的翻译数据注入到redis
/// </summary>
/// <returns></returns>
public async void getTranslateLibToRedis()
{
//获取区域码列表,这里不能使用数据仓储,国际化服务会被注册为单例模式,仓储模型是个泛型,要用原生sql
var cultureCodes = await @"select DISTINCT CultureCode from LocalizerCultureList;".SqlQueryAsync<string>();
foreach (string cultureCode in cultureCodes)
{
//按区域码分类,并向redis中注入
var libs = await @"SELECT CultureCode,TextKey,TranslatedText FROM [dbo].[LocalizerTranslationLib] where CultureCode=@Code;"
.SqlQueryAsync<LocslizerLibDto>(new { Code = cultureCode });
Dictionary<string, string> localizerLib = new Dictionary<string, string>();
foreach (LocslizerLibDto lib in libs)
{
localizerLib.Add(lib.TextKey, lib.TranslatedText);
}
await cache.SetAsync(cultureCode, localizerLib);
}
//提供静态的国际化对象
var sqlStringLocalizer = new SqlStringLocalizer(this.cache);
LocalizerCommon.setLocalizerCommon(sqlStringLocalizer);
}
}
/// <summary>
/// 提供静态的国际化对象
/// </summary>
public static class LocalizerCommon
{
public static SqlStringLocalizer localizer;
/// <summary>
/// 添加国际化对象的引用
/// </summary>
/// <param name="_localizer"></param>
public static void setLocalizerCommon(SqlStringLocalizer _localizer)
{
localizer = _localizer;
}
/// <summary>
/// 获取国际化对象
/// </summary>
/// <returns></returns>
public static SqlStringLocalizer getLocalizerCommon()
{
return localizer;
}
}
}
然后就是实现我们的IStringLocalizer
namespace ******
{
public class SqlStringLocalizer : IStringLocalizer
{
private readonly ISysCacheService cache;
public SqlStringLocalizer(ISysCacheService _cache)
{
cache = _cache; //这是我的redis对象
}
//为LocalizedString创建实例对象
public LocalizedString this[string name]
{
get
{
var value = GetString(name).Result;//异步返回,要加一个Result,否则是Task类型
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
var format = GetString(name).Result;
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
throw new NotImplementedException();
}
public IStringLocalizer WithCulture(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentCulture = culture;
throw new NotImplementedException();
}
/// <summary>
/// redis中获取对应的翻译文本
/// </summary>
/// <param name="name">原始文本</param>
/// <returns></returns>
private async Task<string> GetString(string name)
{
//CultureInfo.CurrentCulture.Name 当前环境的区域码,无需额外声明,这是.net框架中内置的,引入国际化服务即可生效
var result= await cache.GetAsync<Dictionary<string,string>>(CultureInfo.CurrentCulture.Name);
if (result != null)
return result.GetValueOrDefault(name);
else return null;
}
}
}
在IStringLocalizerFactory的实现类中别忘了引入IStringLocalizer实现类所在的命名空间。
经过以上两步我们的自定义国际化资源已经配置完成了,为了我们可以随时使用还需要在startup中启用国际化服务
public void ConfigureServices(IServiceCollection services)
{
//其他代码
//国际化服务
services.AddSingleton<IStringLocalizerFactory, SqlStringLocalizerFactory>();
services.AddLocalization();
//其他代码
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//其他代码
#region 国际化配置
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US"),
new CultureInfo("en-AU"),
new CultureInfo("en-GB"),
new CultureInfo("es-ES"),
new CultureInfo("ja-JP"),
new CultureInfo("fr-FR"),
new CultureInfo("zh"),
new CultureInfo("zh-CN")
};
var LocalizerOptions = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
app.UseRequestLocalization(LocalizerOptions);
#endregion
app.UseStaticFiles();
//其他代码
}
因为我们配置了静态类,所以现在我们就可以在任意位置使用了,无需使用依赖注入
// LocalizerCommon这是一个静态类,引入命名空间即可
SqlStringLocalizer localizer = LocalizerCommon.getLocalizerCommon();
//使用方式 localizer[key]
例 localizer["请求成功"].Value ; //为了返回的是一个string 可以加上 .Value后缀
此方法依旧可以使用依赖注入的方式和sharedResource的方式进行调用,静态方法只是为某些不允许依赖注入的地方提供国家化对象的一种取巧方式。
使用依赖注入无需修改代码,.net框架为我们提供此类接口,他默认会去寻找 IStringLocalizerFactory的接口去实现
private readonly IStringLocalizer<SharedResource> localizer;
public 构造方法(
IStringLocalizer<SharedResource> _localizer
)
{
localizer = _localizer;
}
//使用方式 localizer[key]
例 localizer["请求成功"].Value ; //为了返回的是一个string 可以加上 .Value后缀
//SharedResource是一个空类,他是一个共享类,为实体等提供公共国际化资源的,我这里业务没有用到,有兴趣的小伙伴可以再深入研究一下
展示结果
在usl中带入culture参数 .net就可以自动识别区域码了,此参数后端无需显示接收
希望对你的开发有所帮助,谢谢!如有错误,欢迎指出。