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> { // 堆代码 duidaima.com 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
#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); }); }然后进行数据库迁移即可生成数据库表结构。
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方法,我们先通过缓存查询多语言内容,若查询不到再进数据库查询,减少数据库的并发量。
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"), } });这里配置默认语言是中文,同时支持英文和中文两种。
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> /// 堆代码 duidaima.com /// 多语言管理 /// </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实例。