首页 > 其他分享 >造轮子之多语言管理

造轮子之多语言管理

时间:2023-10-11 14:56:27浏览次数:32  
标签:Task return 语言 input new 轮子 之多 public name

多语言也是我们经常能用到的东西,asp.net core中默认支持了多语言,可以使用.resx资源文件来管理多语言配置。
但是在修改资源文件后,我们的应用服务无法及时更新,属实麻烦一些。我们可以通过扩展IStringLocalizer,实现我们想要的多语言配置方式,比如Json配置,PO 文件配置,EF数据库配置等等。
这里我们选用数据库配置的方式,直接查询数据库的多语言配置进行转换。

创建表实体

多语言管理只需要两个表结构,一个是多语言国家表,一个是多语言资源表。两者是一对多关系。

namespace Wheel.Domain.Localization
{
    public class LocalizationCulture : IEntity<int>
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual List<LocalizationResource> Resources { get; set; }
    }
}
namespace Wheel.Domain.Localization
{
    public class LocalizationResource : IEntity<int>
    {
        public int Id { get; set; }
        public string Key { get; set; }
        public string Value { get; set; }
        public virtual int CultureId { get; set; }
        public virtual LocalizationCulture Culture { get; set; }
    }
}

修改DbContext

添加DbSet和配置表结构:

#region Localization
public DbSet<LocalizationCulture> Cultures { get; set; }
public DbSet<LocalizationResource> Resources { get; set; }
#endregion
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    ConfigureIdentity(builder);
    ConfigureLocalization(builder);
    ConfigurePermissionGrants(builder);
}
void ConfigureLocalization(ModelBuilder builder)
{
    builder.Entity<LocalizationCulture>(b =>
    {
        b.Property(a => a.Id).ValueGeneratedOnAdd();
        b.ToTable("LocalizationCulture"); 
        b.Property(a => a.Name).HasMaxLength(32);
        b.HasMany(a => a.Resources);
    });
    builder.Entity<LocalizationResource>(b =>
    {
        b.Property(a => a.Id).ValueGeneratedOnAdd();
        b.ToTable("LocalizationResource");
        b.HasOne(a => a.Culture);
        b.HasIndex(a => a.CultureId);
        b.Property(a => a.Key).HasMaxLength(256);
        b.Property(a => a.Value).HasMaxLength(1024);
    });
}

然后进行数据库迁移即可生成数据库表结构。

实现EF多语言

这里我们需要实现一下EFStringLocalizerFactory和EFStringLocalizer,使用EFStringLocalizerFactory来创建EFStringLocalizer。

namespace Wheel.Localization
{
    public class EFStringLocalizerFactory : IStringLocalizerFactory, ISingletonDependency
    {
        IServiceProvider _serviceProvider;
        public EFStringLocalizerFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public IStringLocalizer Create(Type resourceSource)
        {
            var scope = _serviceProvider.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<WheelDbContext>();
            var cahce = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
            return new EFStringLocalizer(db, cahce);
        }

        public IStringLocalizer Create(string baseName, string location)
        {
            var scope = _serviceProvider.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<WheelDbContext>();
            var cahce = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
            return new EFStringLocalizer(db, cahce);
        }
    }
}

namespace Wheel.Localization
{
    public class EFStringLocalizer : IStringLocalizer
    {
        private readonly WheelDbContext _db;
        private readonly IMemoryCache _memoryCache;
        public EFStringLocalizer(WheelDbContext db, IMemoryCache memoryCache)
        {
            _db = db;
            _memoryCache = memoryCache;
        }

        public LocalizedString this[string name]
        {
            get
            {
                var value = GetString(name);
                return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
            }
        }

        public LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                var format = GetString(name);
                var value = string.Format(format ?? name, arguments);
                return new LocalizedString(name, value, resourceNotFound: format == null);
            }
        }

        public IStringLocalizer WithCulture(CultureInfo culture)
        {
            CultureInfo.DefaultThreadCurrentCulture = culture;
            return new EFStringLocalizer(_db, _memoryCache);
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .Select(r => new LocalizedString(r.Key, r.Value, r.Value == null));
        }

        private string? GetString(string name)
        {
            if (_memoryCache.TryGetValue<string>($"{CultureInfo.CurrentCulture.Name}:{name}", out var value))
            {
                return value;
            }
            else
            {
                value = _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .FirstOrDefault(r => r.Key == name)?.Value;
                if (!string.IsNullOrWhiteSpace(value))
                {
                    _memoryCache.Set($"{CultureInfo.CurrentCulture.Name}:{name}", value, TimeSpan.FromMinutes(1));
                }
                return value;
            }
        }
    }

    public class EFStringLocalizer<T> : IStringLocalizer<T>
    {
        private readonly WheelDbContext _db;
        private readonly IMemoryCache _memoryCache;
        public EFStringLocalizer(WheelDbContext db, IMemoryCache memoryCache)
        {
            _db = db;
            _memoryCache = memoryCache;
        }

        public LocalizedString this[string name]
        {
            get
            {
                var value = GetString(name);
                return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
            }
        }

        public LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                var format = GetString(name);
                var value = string.Format(format ?? name, arguments);
                return new LocalizedString(name, value, resourceNotFound: format == null);
            }
        }

        public IStringLocalizer WithCulture(CultureInfo culture)
        {
            CultureInfo.DefaultThreadCurrentCulture = culture;
            return new EFStringLocalizer(_db, _memoryCache);
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .Select(r => new LocalizedString(r.Key, r.Value, true));
        }

        private string? GetString(string name)
        {
            if (_memoryCache.TryGetValue<string>($"{CultureInfo.CurrentCulture.Name}:{name}", out var value))
            {
                return value;
            }
            else
            {
                value = _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .FirstOrDefault(r => r.Key == name)?.Value;
                if (!string.IsNullOrWhiteSpace(value))
                {
                    _memoryCache.Set($"{CultureInfo.CurrentCulture.Name}:{name}", value, TimeSpan.FromMinutes(1));
                }
                return value;
            }
        }
    }
}

这里的GetString方法,我们先通过缓存查询多语言内容,若查询不到再进数据库查询,减少数据库的并发量。
多语言国家编码直接使用CultureInfo.CurrentCulture.Name获取。无需传参配置。

启用多语言

再Program中添加多语言代码:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

app.UseRequestLocalization(new RequestLocalizationOptions
{
    ApplyCurrentCultureToResponseHeaders = true,
    DefaultRequestCulture = new RequestCulture("zh-CN"),
    SupportedCultures = new List<CultureInfo>
            {
                new CultureInfo("en"),
                new CultureInfo("zh-CN"),
            },
    SupportedUICultures = new List<CultureInfo>
            {
                new CultureInfo("en"),
                new CultureInfo("zh-CN"),
            }
});

这里配置默认语言是中文,同时支持英文和中文两种。

多语言管理API实现

接下来我们实现LocalizationManage
ILocalizationManageAppService:

namespace Wheel.Services.LocalizationManage
{
    public interface ILocalizationManageAppService : ITransientDependency
    {
        Task<R<LocalizationCultureDto>> GetLocalizationCultureAsync(int id);
        Task<Page<LocalizationCultureDto>> GetLocalizationCulturePageListAsync(PageRequest input);
        Task<R<LocalizationCultureDto>> CreateLocalizationCultureAsync(CreateLocalizationCultureDto input);
        Task<R> DeleteLocalizationCultureAsync(int id);
        Task<R<LocalizationResourceDto>> CreateLocalizationResourceAsync(CreateLocalizationResourceDto input);
        Task<R> UpdateLocalizationResourceAsync(UpdateLocalizationResourceDto input);
        Task<R> DeleteLocalizationResourceAsync(int id);
    }
}

LocalizationManageAppService:


namespace Wheel.Services.LocalizationManage
{
    /// <summary>
    /// 多语言管理
    /// </summary>
    public class LocalizationManageAppService : WheelServiceBase, ILocalizationManageAppService
    {
        private readonly IBasicRepository<LocalizationCulture, int> _localizationCultureRepository;
        private readonly IBasicRepository<LocalizationResource, int> _localizationResourceRepository;

        public LocalizationManageAppService(IBasicRepository<LocalizationCulture, int> localizationCultureRepository, IBasicRepository<LocalizationResource, int> localizationResourceRepository)
        {
            _localizationCultureRepository = localizationCultureRepository;
            _localizationResourceRepository = localizationResourceRepository;
        }
        /// <summary>
        /// 获取地区多语言详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<R<LocalizationCultureDto>> GetLocalizationCultureAsync(int id)
        {
            var entity = await _localizationCultureRepository.FindAsync(id);

            return new R<LocalizationCultureDto>(Mapper.Map<LocalizationCultureDto>(entity));
        }
        /// <summary>
        /// 分页获取地区多语言列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<Page<LocalizationCultureDto>> GetLocalizationCulturePageListAsync(PageRequest input)
        {
            var (entities, total) = await _localizationCultureRepository
                .GetPageListAsync(a => true,
                (input.PageIndex - 1) * input.PageSize,
                input.PageSize,
                propertySelectors: a => a.Resources
                );

            return new Page<LocalizationCultureDto>(Mapper.Map<List<LocalizationCultureDto>>(entities), total);
        }
        /// <summary>
        /// 创建地区多语言
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<R<LocalizationCultureDto>> CreateLocalizationCultureAsync(CreateLocalizationCultureDto input)
        {
            var entity = Mapper.Map<LocalizationCulture>(input);
            entity = await _localizationCultureRepository.InsertAsync(entity);
            await UnitOfWork.SaveChangesAsync();
            return new R<LocalizationCultureDto>(Mapper.Map<LocalizationCultureDto>(entity));
        }
        /// <summary>
        /// 删除地区多语言
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<R> DeleteLocalizationCultureAsync(int id)
        {
            await _localizationCultureRepository.DeleteAsync(id);
            await UnitOfWork.SaveChangesAsync();
            return new R();
        }
        /// <summary>
        /// 创建多语言资源
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<R<LocalizationResourceDto>> CreateLocalizationResourceAsync(CreateLocalizationResourceDto input)
        {
            var entity = Mapper.Map<LocalizationResource>(input);
            entity = await _localizationResourceRepository.InsertAsync(entity);
            await UnitOfWork.SaveChangesAsync();
            return new R<LocalizationResourceDto>(Mapper.Map<LocalizationResourceDto>(entity));
        }
        /// <summary>
        /// 修改多语言资源
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<R> UpdateLocalizationResourceAsync(UpdateLocalizationResourceDto input)
        {
            await _localizationResourceRepository.UpdateAsync(a => a.Id == input.Id,
                a => a.SetProperty(b => b.Key, b => input.Key).SetProperty(b => b.Value, b => input.Value));
            await UnitOfWork.SaveChangesAsync();
            return new R();
        }
        /// <summary>
        /// 删除多语言资源
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<R> DeleteLocalizationResourceAsync(int id)
        {
            await _localizationResourceRepository.DeleteAsync(id);
            await UnitOfWork.SaveChangesAsync();
            return new R();
        }
    }
}

这里包含了多语言的CURD的实现
LocalizationManageController:

namespace Wheel.Controllers
{
    /// <summary>
    /// 多语言管理
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class LocalizationManageController : WheelControllerBase
    {
        private readonly ILocalizationManageAppService _localizationManageAppService;

        public LocalizationManageController(ILocalizationManageAppService localizationManageAppService)
        {
            _localizationManageAppService = localizationManageAppService;
        }

        /// <summary>
        /// 获取地区多语言详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("Culture/{id}")]
        public async Task<R<LocalizationCultureDto>> GetCulture(int id)
        {
            return await _localizationManageAppService.GetLocalizationCultureAsync(id);
        }
        /// <summary>
        /// 创建地区多语言
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("Culture")]
        public async Task<R<LocalizationCultureDto>> CreateCulture(CreateLocalizationCultureDto input)
        {
            return await _localizationManageAppService.CreateLocalizationCultureAsync(input);
        }
        /// <summary>
        /// 删除地区多语言
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete("Culture/{id}")]
        public async Task<R> DeleteCulture(int id)
        {
            return await _localizationManageAppService.DeleteLocalizationCultureAsync(id);
        }
        /// <summary>
        /// 分页获取地区多语言列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("Culture")]
        public async Task<Page<LocalizationCultureDto>> GetCulturePageList([FromQuery]PageRequest input)
        {
            return await _localizationManageAppService.GetLocalizationCulturePageListAsync(input);
        }
        /// <summary>
        /// 创建多语言资源
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("Resource")]
        public async Task<R<LocalizationResourceDto>> CreateResource(CreateLocalizationResourceDto input)
        {
            return await _localizationManageAppService.CreateLocalizationResourceAsync(input);
        }
        /// <summary>
        /// 修改多语言资源
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPut("Resource")]
        public async Task<R> UpdateResource(UpdateLocalizationResourceDto input)
        {
            return await _localizationManageAppService.UpdateLocalizationResourceAsync(input);
        }
        /// <summary>
        /// 删除多语言资源
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete("Resource/{id}")]
        public async Task<R> DeleteResource(int id)
        {
            return await _localizationManageAppService.DeleteLocalizationResourceAsync(id);
        }
        /// <summary>
        /// 获取多语言资源列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("Resources")]
        [AllowAnonymous]
        public Task<R<Dictionary<string, string>>> GetResources()
        {
            var resources = L.GetAllStrings().ToDictionary(a=>a.Name, a=>a.Value);
            return Task.FromResult(new R<Dictionary<string, string>>(resources));
        }
    }
}

在控制器额外添加一个匿名访问的API,GetResources()用于客户端集成多语言配置。L是IStringLocalizer实例。

启用服务测试一下。
image.png
image.png
可以看到成功获取英文和中文的多语言列表。

就这样我们完成多语言管理的实现。

轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。

image.png

标签:Task,return,语言,input,new,轮子,之多,public,name
From: https://www.cnblogs.com/fanshaoO/p/17757078.html

相关文章

  • 2023-10-11:用go语言,一个数字n,一定要分成k份, 得到的乘积尽量大是多少? 数字n和k,可能非常
    2023-10-11:用go语言,一个数字n,一定要分成k份,得到的乘积尽量大是多少?数字n和k,可能非常大,到达10^12规模。结果可能更大,所以返回结果对1000000007取模。来自华为。来自左程云。答案2023-10-11:大体过程如下:算法1:暴力递归1.首先判断k是否为0或者n是否小于k,若是则返回-1。2.调用递归函数pr......
  • 仅作笔记用:C语言 将结构体以二进制形式写入文件
    直接以文本文件的方式写入固然也可以,但是如果遇到数据量大的情况,会占用比较多的磁盘空间。这里收集汇总了一下将结构体数据写入二进制文件以及后续读取为结构体的办法。写入二进制文件的话,成员变量就可以直接以例如int、float、double这样的形式存储到磁盘,而不是转换成字符串,这......
  • 2023-10-11:用go语言,一个数字n,一定要分成k份, 得到的乘积尽量大是多少? 数字n和k,可能非常
    2023-10-11:用go语言,一个数字n,一定要分成k份,得到的乘积尽量大是多少?数字n和k,可能非常大,到达10^12规模。结果可能更大,所以返回结果对1000000007取模。来自华为。来自左程云。答案2023-10-11:大体过程如下:算法1:暴力递归1.首先判断k是否为0或者n是否小于k,若是则返回-1。2.调......
  • C语言 - 预处理
    C预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把C预处理器(CPreprocessor)简写为CPP。所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符......
  • C语言 - 预处理器
    C预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把C预处理器(CPreprocessor)简写为CPP。所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符......
  • R语言非线性动态回归模型ARIMAX、随机、确定性趋势时间序列预测个人消费和收入、用电
    全文链接:https://tecdat.cn/?p=33838原文出处:拓端数据部落公众号传统时间序列模型允许包含过去观察到的系列信息,但不允许客户包含其他可能相关的信息。例如,假期的影响、竞争对手的活动、法律变化、整体经济或其他外部变量可能解释了某些历史变动,并且可能导致更准确的预测。另一......
  • R语言无套利区间模型期货期现研究:正向套利和反向套利次数、收益率分析华泰柏瑞300ETF
    全文链接:http://tecdat.cn/?p=31973最近我们被客户要求撰写关于无套利区间模型的研究报告,包括一些图形和统计输出。股指期货的套利交易有助于股指期货实现其价格发现以及风险规避的功能,因此提高套利交易的效率,对于发挥股指期货在经济发展中的作用有着重要的意义本文帮助客户对......
  • R语言门限误差修正模型(TVECM)参数估计沪深300指数和股指期货指数可视化|附代码数据
    全文链接:http://tecdat.cn/?p=32511原文出处:拓端数据部落公众号时间序列模型的理论已经非常丰富,模型的应用也相当广泛。但现实生活中,越来越多的时间序列模型呈现出了非线性的特点,因此,研究非线性时间序列模型的理论及对其参数进行估计有着极其重要的意义。门限模型作为非线性......
  • 复习课15 C语言作业讲解
    一.选择题1.以下哪一项不属于C语言内置的数据类型()A.intB.shortC.structStrD.float答案:C解析:C语言中内置的数据类型有:intshortfloatdoublelongchar等,并不包含C选项中的structStr,故选C2.局部变量的作用域是()A.main()函数内部B.整个程序C.main()函数之前D.局部变量所在地局部......
  • 记录python语言的数组去重并输出
    deffind_duplicates(arr):seen=set()duplicates=[]fornuminarr:ifnuminseen:duplicates.append(num)seen.add(num)returnduplicatesarr=['1000223453','1000227458','1000223......