committed by
GitHub
137 changed files with 6458 additions and 159 deletions
@ -0,0 +1,18 @@ |
|||||
|
## 总览 |
||||
|
|
||||
|
# [2021-03-29] |
||||
|
1、增加动态本地化组件支持,用于在运行时替换本地化文本,需要实现 ILocalizationStore; |
||||
|
|
||||
|
2、增加本地化文档管理项目,实现了 ILocalizationStore持久化本地资源到数据库; |
||||
|
使用须知: 需要先定位到 [LocalizationManagement](./aspnet-core/services/localization/LINGYUN.Abp.LocalizationManagement.HttpApi.Host) 目录下 |
||||
|
执行Ef迁移命令: **dotnet ef database update** |
||||
|
|
||||
|
3、增加一个dotnet-compose配置文件[docker-compose.configuration.yml](./docker-compose.configuration.yml); |
||||
|
|
||||
|
4、vueJs 增加本地化管理组件视图: views/localization-management; |
||||
|
|
||||
|
5、vueJs 用户登录后的异步等待调用修改为通过store来调用abp模块初始化事件; |
||||
|
|
||||
|
6、vueJs 修复了HttpProxy模块传递的params与data使用情况混淆的问题; |
||||
|
|
||||
|
7、同步本地化管理组件的网关路由初始化配置。 |
||||
@ -0,0 +1,91 @@ |
|||||
|
{ |
||||
|
"ConnectionStrings": { |
||||
|
"Default": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", |
||||
|
"AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", |
||||
|
"AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", |
||||
|
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", |
||||
|
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" |
||||
|
}, |
||||
|
"CAP": { |
||||
|
"EventBus": { |
||||
|
"DefaultGroup": "Platform", |
||||
|
"Version": "v1", |
||||
|
"FailedRetryInterval": 300, |
||||
|
"FailedRetryCount": 10 |
||||
|
}, |
||||
|
"RabbitMQ": { |
||||
|
"HostName": "Your RabbitMQ server connection address", |
||||
|
"Port": 5672, |
||||
|
"UserName": "Your RabbitMQ server connection user", |
||||
|
"Password": "Your RabbitMQ server connection user password", |
||||
|
"ExchangeName": "The name of your RabbitMQ server switch", |
||||
|
"VirtualHost": "Name of your RabbitMQ server VirtualHost" |
||||
|
} |
||||
|
}, |
||||
|
"Redis": { |
||||
|
"Configuration": "127.0.0.1,defaultDatabase=11", |
||||
|
"InstanceName": "LINGYUN.Abp.Application" |
||||
|
}, |
||||
|
"AuthServer": { |
||||
|
"Authority": "http://localhost:44385/", |
||||
|
"ApiName": "lingyun-abp-application" |
||||
|
} |
||||
|
"Serilog": { |
||||
|
"MinimumLevel": { |
||||
|
"Default": "Debug", |
||||
|
"Override": { |
||||
|
"Microsoft.EntityFrameworkCore": "Debug", |
||||
|
"System": "Warning", |
||||
|
"Microsoft": "Warning" |
||||
|
} |
||||
|
}, |
||||
|
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ], |
||||
|
"WriteTo": [ |
||||
|
{ |
||||
|
"Name": "File", |
||||
|
"Args": { |
||||
|
"path": "Logs/Debug-.log", |
||||
|
"restrictedToMinimumLevel": "Debug", |
||||
|
"rollingInterval": "Day", |
||||
|
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"Name": "File", |
||||
|
"Args": { |
||||
|
"path": "Logs/Info-.log", |
||||
|
"restrictedToMinimumLevel": "Information", |
||||
|
"rollingInterval": "Day", |
||||
|
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"Name": "File", |
||||
|
"Args": { |
||||
|
"path": "Logs/Warn-.log", |
||||
|
"restrictedToMinimumLevel": "Warning", |
||||
|
"rollingInterval": "Day", |
||||
|
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"Name": "File", |
||||
|
"Args": { |
||||
|
"path": "Logs/Error-.log", |
||||
|
"restrictedToMinimumLevel": "Error", |
||||
|
"rollingInterval": "Day", |
||||
|
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"Name": "File", |
||||
|
"Args": { |
||||
|
"path": "Logs/Fatal-.log", |
||||
|
"restrictedToMinimumLevel": "Fatal", |
||||
|
"rollingInterval": "Day", |
||||
|
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.EventBus" Version="4.2.1" /> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.EventBus; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpEventBusModule), |
||||
|
typeof(AbpLocalizationModule))] |
||||
|
public class AbpLocalizationDynamicModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public class DefaultLocalizationStore : ILocalizationStore, ITransientDependency |
||||
|
{ |
||||
|
public DefaultLocalizationStore() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Task<List<LanguageInfo>> GetLanguageListAsync(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.FromResult(new List<LanguageInfo>()); |
||||
|
} |
||||
|
|
||||
|
public Task<Dictionary<string, ILocalizationDictionary>> GetLocalizationDictionaryAsync(string resourceName, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.FromResult(new Dictionary<string, ILocalizationDictionary>()); |
||||
|
} |
||||
|
|
||||
|
public Task<bool> ResourceExistsAsync(string resourceName, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.FromResult(false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
||||
|
[ExposeServices( |
||||
|
typeof(ILanguageProvider), |
||||
|
typeof(DynamicLanguageProvider))] |
||||
|
public class DynamicLanguageProvider : ILanguageProvider |
||||
|
{ |
||||
|
protected ILocalizationStore Store { get; } |
||||
|
protected AbpLocalizationOptions Options { get; } |
||||
|
|
||||
|
public DynamicLanguageProvider( |
||||
|
ILocalizationStore store, |
||||
|
IOptions<AbpLocalizationOptions> options) |
||||
|
{ |
||||
|
Store = store; |
||||
|
Options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<IReadOnlyList<LanguageInfo>> GetLanguagesAsync() |
||||
|
{ |
||||
|
var languages = await Store.GetLanguageListAsync(); |
||||
|
|
||||
|
if (!languages.Any()) |
||||
|
{ |
||||
|
return Options.Languages; |
||||
|
} |
||||
|
|
||||
|
return languages |
||||
|
.Distinct(new LanguageInfoComparer()) |
||||
|
.ToList(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,120 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Nito.AsyncEx; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public class DynamicLocalizationResourceContributor : ILocalizationResourceContributor |
||||
|
{ |
||||
|
private ILogger _logger; |
||||
|
private ILocalizationSubscriber _subscriber; |
||||
|
|
||||
|
private ILocalizationStore _store; |
||||
|
protected ILocalizationStore Store => _store; |
||||
|
|
||||
|
private Dictionary<string, ILocalizationDictionary> _dictionaries; |
||||
|
|
||||
|
private readonly string _resourceName; |
||||
|
private readonly AsyncLock _asyncLock = new AsyncLock(); |
||||
|
|
||||
|
public DynamicLocalizationResourceContributor(string resourceName) |
||||
|
{ |
||||
|
_resourceName = resourceName; |
||||
|
} |
||||
|
|
||||
|
public virtual void Initialize(LocalizationResourceInitializationContext context) |
||||
|
{ |
||||
|
_logger = context.ServiceProvider.GetService<ILogger<DynamicLocalizationResourceContributor>>(); |
||||
|
|
||||
|
_store = context.ServiceProvider.GetRequiredService<ILocalizationStore>(); |
||||
|
_subscriber = context.ServiceProvider.GetRequiredService<ILocalizationSubscriber>(); |
||||
|
_subscriber.Subscribe(OnChanged); |
||||
|
} |
||||
|
|
||||
|
public virtual void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary) |
||||
|
{ |
||||
|
GetDictionaries().GetOrDefault(cultureName)?.Fill(dictionary); |
||||
|
} |
||||
|
|
||||
|
public virtual LocalizedString GetOrNull(string cultureName, string name) |
||||
|
{ |
||||
|
return GetDictionaries().GetOrDefault(cultureName)?.GetOrNull(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual Dictionary<string, ILocalizationDictionary> GetDictionaries() |
||||
|
{ |
||||
|
var dictionaries = _dictionaries; |
||||
|
if (dictionaries != null) |
||||
|
{ |
||||
|
return dictionaries; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
using (_asyncLock.Lock()) |
||||
|
{ |
||||
|
dictionaries = _dictionaries = AsyncHelper.RunSync(async () => |
||||
|
await Store.GetLocalizationDictionaryAsync(_resourceName)); |
||||
|
} |
||||
|
} |
||||
|
catch(Exception ex) |
||||
|
{ |
||||
|
// 错误不应该影响到应用程序,而是记录到日志
|
||||
|
_logger?.LogWarning("Failed to get localized text, error: ", ex.Message); |
||||
|
} |
||||
|
|
||||
|
return dictionaries; |
||||
|
} |
||||
|
|
||||
|
private Task OnChanged(LocalizedStringCacheResetEventData data) |
||||
|
{ |
||||
|
if (string.Equals(_resourceName, data.ResourceName)) |
||||
|
{ |
||||
|
if (!_dictionaries.ContainsKey(data.CultureName)) |
||||
|
{ |
||||
|
// TODO: 需要处理 data.Key data.Value 空引用
|
||||
|
var dictionary = new Dictionary<string, LocalizedString>(); |
||||
|
dictionary.Add(data.Key, new LocalizedString(data.Key, data.Value.NormalizeLineEndings())); |
||||
|
var newLocalizationDictionary = new StaticLocalizationDictionary(data.CultureName, dictionary); |
||||
|
|
||||
|
_dictionaries.Add(data.CultureName, newLocalizationDictionary); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// 取出当前的缓存写入到新字典进行处理
|
||||
|
var nowLocalizationDictionary = _dictionaries[data.CultureName]; |
||||
|
var dictionary = new Dictionary<string, LocalizedString>(); |
||||
|
nowLocalizationDictionary.Fill(dictionary); |
||||
|
|
||||
|
var existsKey = dictionary.ContainsKey(data.Key); |
||||
|
if (!existsKey) |
||||
|
{ |
||||
|
// 如果不存在,则新增
|
||||
|
dictionary.Add(data.Key, new LocalizedString(data.Key, data.Value.NormalizeLineEndings())); |
||||
|
} |
||||
|
else if (existsKey && data.IsDeleted) |
||||
|
{ |
||||
|
// 如果删掉了本地化的节点,删掉当前的缓存
|
||||
|
dictionary.Remove(data.Key); |
||||
|
} |
||||
|
|
||||
|
var newLocalizationDictionary = new StaticLocalizationDictionary(data.CultureName, dictionary); |
||||
|
|
||||
|
if (newLocalizationDictionary != null) |
||||
|
{ |
||||
|
// 重新赋值变更过的缓存
|
||||
|
_dictionaries[data.CultureName] = newLocalizationDictionary; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
internal interface ILocalizationDispatcher |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 发布变更事件
|
||||
|
/// </summary>
|
||||
|
/// <param name="data"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task DispatchAsync(LocalizedStringCacheResetEventData data); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public interface ILocalizationStore |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取语言列表
|
||||
|
/// </summary>
|
||||
|
/// <param name="cancellationToken"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<List<LanguageInfo>> GetLanguageListAsync( |
||||
|
CancellationToken cancellationToken = default); |
||||
|
/// <summary>
|
||||
|
/// 资源是否存在
|
||||
|
/// </summary>
|
||||
|
/// <param name="resourceName">资源名称</param>
|
||||
|
/// <param name="cancellationToken"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<bool> ResourceExistsAsync( |
||||
|
string resourceName, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
/// <summary>
|
||||
|
/// 获取当前资源下的本地化字典
|
||||
|
/// </summary>
|
||||
|
/// <param name="resourceName">资源名称</param>
|
||||
|
/// <param name="cancellationToken"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<Dictionary<string, ILocalizationDictionary>> GetLocalizationDictionaryAsync( |
||||
|
string resourceName, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 本地化资源订阅
|
||||
|
/// </summary>
|
||||
|
internal interface ILocalizationSubscriber |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 订阅变更事件
|
||||
|
/// </summary>
|
||||
|
/// <param name="func"></param>
|
||||
|
void Subscribe(Func<LocalizedStringCacheResetEventData, Task> func); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public class LanguageInfoComparer : IEqualityComparer<LanguageInfo> |
||||
|
{ |
||||
|
public bool Equals(LanguageInfo x, LanguageInfo y) |
||||
|
{ |
||||
|
if (x == null || y == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return x.CultureName.Equals(y.CultureName); |
||||
|
} |
||||
|
|
||||
|
public int GetHashCode(LanguageInfo obj) |
||||
|
{ |
||||
|
return obj?.CultureName.GetHashCode() ?? GetHashCode(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public class LocalizationCacheItem |
||||
|
{ |
||||
|
public string Resource { get; set; } |
||||
|
public string Culture { get; set; } |
||||
|
public List<LocalizationText> Texts { get; set; } |
||||
|
public LocalizationCacheItem() |
||||
|
{ |
||||
|
Texts = new List<LocalizationText>(); |
||||
|
} |
||||
|
|
||||
|
public LocalizationCacheItem( |
||||
|
string resource, |
||||
|
string culture, |
||||
|
List<LocalizationText> texts) |
||||
|
{ |
||||
|
Resource = resource; |
||||
|
Culture = culture; |
||||
|
Texts = texts; |
||||
|
} |
||||
|
|
||||
|
public static string NormalizeKey( |
||||
|
string resource, |
||||
|
string culture) |
||||
|
{ |
||||
|
return $"p:Localization,r:{resource},c:{culture}"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class LocalizationText |
||||
|
{ |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public LocalizationText() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public LocalizationText( |
||||
|
string key, |
||||
|
string value) |
||||
|
{ |
||||
|
Key = key; |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
internal class LocalizationResetSynchronizer : |
||||
|
IDistributedEventHandler<LocalizedStringCacheResetEventData>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
private readonly ILocalizationDispatcher _dispatcher; |
||||
|
|
||||
|
public LocalizationResetSynchronizer( |
||||
|
ILocalizationDispatcher dispatcher) |
||||
|
{ |
||||
|
_dispatcher = dispatcher; |
||||
|
} |
||||
|
public async Task HandleEventAsync(LocalizedStringCacheResetEventData eventData) |
||||
|
{ |
||||
|
await _dispatcher.DispatchAsync(eventData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
[ExposeServices( |
||||
|
typeof(ILocalizationSubscriber), |
||||
|
typeof(ILocalizationDispatcher), |
||||
|
typeof(LocalizationSubscriber))] |
||||
|
internal class LocalizationSubscriber : ILocalizationSubscriber, ILocalizationDispatcher, ISingletonDependency |
||||
|
{ |
||||
|
private Func<LocalizedStringCacheResetEventData, Task> _handler; |
||||
|
|
||||
|
public LocalizationSubscriber() |
||||
|
{ |
||||
|
_handler = (d) => |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public void Subscribe(Func<LocalizedStringCacheResetEventData, Task> func) |
||||
|
{ |
||||
|
_handler += func; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task DispatchAsync(LocalizedStringCacheResetEventData data) |
||||
|
{ |
||||
|
await _handler(data); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
namespace LINGYUN.Abp.Localization.Dynamic |
||||
|
{ |
||||
|
public class LocalizedStringCacheResetEventData |
||||
|
{ |
||||
|
public bool IsDeleted { get; set; } |
||||
|
public string ResourceName { get; set; } |
||||
|
public string CultureName { get; set; } |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public LocalizedStringCacheResetEventData() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public LocalizedStringCacheResetEventData( |
||||
|
string resourceName, |
||||
|
string cultureName, |
||||
|
string key, |
||||
|
string value) |
||||
|
{ |
||||
|
ResourceName = resourceName; |
||||
|
CultureName = cultureName; |
||||
|
Key = key; |
||||
|
Value = value; |
||||
|
|
||||
|
IsDeleted = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
# LINGYUN.Abp.Localization.Dynamic |
||||
|
|
||||
|
动态本地化提供者组件,添加动态提供者可实现运行时替换本地化文本 |
||||
|
|
||||
|
需要实现 ILocalizationStore 接口 |
||||
|
|
||||
|
LocalizationManagement项目提供支持 |
||||
|
|
||||
|
|
||||
|
由于框架设计为延迟初始化,当某个本地化资源被使用的时候才会进行初始化 |
||||
|
当资源被第一次使用到的时候,才会注册动态变更事件 |
||||
|
详情见: [DynamicLocalizationResourceContributor](./LINGYUN/Abp/Localization/Dynamic/DynamicLocalizationResourceContributor.cs#L29-L34) |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpLocalizationDynamicModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<YouProjectResource>() |
||||
|
.AddDynamic(); // 添加动态本地化文档支持 |
||||
|
|
||||
|
// 添加所有资源的动态文档支持,将监听所有的资源包文档变更事件 |
||||
|
// options.Resources.AddDynamic(); |
||||
|
|
||||
|
// 添加所有资源的动态文档支持,忽略 IdentityResource 资源 |
||||
|
// options.Resources.AddDynamic(typeof(IdentityResource)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,42 @@ |
|||||
|
using LINGYUN.Abp.Localization.Dynamic; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Volo.Abp.Localization |
||||
|
{ |
||||
|
public static class LocalizationResourceDictionaryExtensions |
||||
|
{ |
||||
|
public static LocalizationResourceDictionary AddDynamic( |
||||
|
this LocalizationResourceDictionary resources, |
||||
|
params Type[] ignoreResourceTypes) |
||||
|
{ |
||||
|
foreach (var resource in resources) |
||||
|
{ |
||||
|
if (ShouldIgnoreType(resource.Key, ignoreResourceTypes)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
if (ShouldIgnoreType(resource.Value)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
resource.Value.AddDynamic(); |
||||
|
} |
||||
|
return resources; |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldIgnoreType(Type resourceType, params Type[] ignoreResourceTypes) |
||||
|
{ |
||||
|
if (ignoreResourceTypes == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
return ignoreResourceTypes.Any(x => x == resourceType); |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldIgnoreType(LocalizationResource resource) |
||||
|
{ |
||||
|
return resource.Contributors.Exists(x => x is DynamicLocalizationResourceContributor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using LINGYUN.Abp.Localization.Dynamic; |
||||
|
|
||||
|
namespace Volo.Abp.Localization |
||||
|
{ |
||||
|
public static class DynamicLocalizationResourceExtensions |
||||
|
{ |
||||
|
public static LocalizationResource AddDynamic( |
||||
|
[NotNull] this LocalizationResource localizationResource) |
||||
|
{ |
||||
|
Check.NotNull(localizationResource, nameof(localizationResource)); |
||||
|
|
||||
|
localizationResource.Contributors.Add( |
||||
|
new DynamicLocalizationResourceContributor( |
||||
|
localizationResource.ResourceName)); |
||||
|
|
||||
|
return localizationResource; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Authorization" Version="4.2.1" /> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Domain.Shared\LINGYUN.Abp.LocalizationManagement.Domain.Shared.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAuthorizationModule), |
||||
|
typeof(AbpLocalizationManagementDomainSharedModule))] |
||||
|
public class AbpLocalizationManagementApplicationContractsModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class CreateOrUpdateLanguageInput |
||||
|
{ |
||||
|
public virtual bool Enable { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxCultureNameLength))] |
||||
|
public string CultureName { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxUiCultureNameLength))] |
||||
|
public string UiCultureName { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxDisplayNameLength))] |
||||
|
public string DisplayName { get; set; } |
||||
|
|
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxFlagIconLength))] |
||||
|
public string FlagIcon { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class CreateOrUpdateResourceInput |
||||
|
{ |
||||
|
public bool Enable { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxDisplayNameLength))] |
||||
|
public string DisplayName { get; set; } |
||||
|
|
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxDescriptionLength))] |
||||
|
public string Description { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class CreateOrUpdateTextInput |
||||
|
{ |
||||
|
[DynamicStringLength(typeof(TextConsts), nameof(TextConsts.MaxValueLength))] |
||||
|
public string Value { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class CreateTextInput : CreateOrUpdateTextInput |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxNameLength))] |
||||
|
public string ResourceName { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(TextConsts), nameof(TextConsts.MaxKeyLength))] |
||||
|
public string Key { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxCultureNameLength))] |
||||
|
public string CultureName { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class GetLanguagesInput : PagedAndSortedResultRequestDto |
||||
|
{ |
||||
|
public string Filter { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class GetResourcesInput : PagedAndSortedResultRequestDto |
||||
|
{ |
||||
|
public string Filter { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class GetTextByKeyInput |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(TextConsts), nameof(TextConsts.MaxKeyLength))] |
||||
|
public string Key { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxCultureNameLength))] |
||||
|
public string CultureName { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxNameLength))] |
||||
|
public string ResourceName { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class GetTextsInput : PagedAndSortedResultRequestDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxCultureNameLength))] |
||||
|
public string CultureName { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxCultureNameLength))] |
||||
|
public string TargetCultureName { get; set; } |
||||
|
|
||||
|
[DynamicStringLength(typeof(ResourceConsts), nameof(ResourceConsts.MaxNameLength))] |
||||
|
public string ResourceName { get; set; } |
||||
|
|
||||
|
public bool? OnlyNull { get; set; } |
||||
|
|
||||
|
public string Filter { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface ILanguageAppService : |
||||
|
ICrudAppService< |
||||
|
LanguageDto, |
||||
|
Guid, |
||||
|
GetLanguagesInput, |
||||
|
CreateOrUpdateLanguageInput, |
||||
|
CreateOrUpdateLanguageInput |
||||
|
> |
||||
|
{ |
||||
|
Task<ListResultDto<LanguageDto>> GetAllAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface IResourceAppService : |
||||
|
ICrudAppService< |
||||
|
ResourceDto, |
||||
|
Guid, |
||||
|
GetResourcesInput, |
||||
|
CreateOrUpdateResourceInput, |
||||
|
CreateOrUpdateResourceInput> |
||||
|
{ |
||||
|
Task<ListResultDto<ResourceDto>> GetAllAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface ITextAppService : |
||||
|
ICrudAppService< |
||||
|
TextDto, |
||||
|
TextDifferenceDto, |
||||
|
int, |
||||
|
GetTextsInput, |
||||
|
CreateTextInput, |
||||
|
UpdateTextInput> |
||||
|
{ |
||||
|
Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class LanguageDto : AuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public bool Enable { get; set; } |
||||
|
public string CultureName { get; set; } |
||||
|
public string UiCultureName { get; set; } |
||||
|
public string DisplayName { get; set; } |
||||
|
public string FlagIcon { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public static class LocalizationRemoteServiceConsts |
||||
|
{ |
||||
|
public const string RemoteServiceName = "LocalizationManagement"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Localization; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.Permissions |
||||
|
{ |
||||
|
public class LocalizationManagementPermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var permissionGroup = context.AddGroup( |
||||
|
LocalizationManagementPermissions.GroupName, |
||||
|
L("Permissions:LocalizationManagement"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
|
||||
|
var resourcePermission = permissionGroup.AddPermission( |
||||
|
LocalizationManagementPermissions.Resource.Default, |
||||
|
L("Permissions:Resource"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
resourcePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Resource.Create, |
||||
|
L("Permissions:Create"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
resourcePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Resource.Update, |
||||
|
L("Permissions:Update"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
resourcePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Resource.Delete, |
||||
|
L("Permissions:Delete"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
|
||||
|
var languagePermission = permissionGroup.AddPermission( |
||||
|
LocalizationManagementPermissions.Language.Default, |
||||
|
L("Permissions:Language"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
languagePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Language.Create, |
||||
|
L("Permissions:Create"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
languagePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Language.Update, |
||||
|
L("Permissions:Update"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
languagePermission.AddChild( |
||||
|
LocalizationManagementPermissions.Language.Delete, |
||||
|
L("Permissions:Delete"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
|
||||
|
var textPermission = permissionGroup.AddPermission( |
||||
|
LocalizationManagementPermissions.Text.Default, |
||||
|
L("Permissions:Text"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
textPermission.AddChild( |
||||
|
LocalizationManagementPermissions.Text.Create, |
||||
|
L("Permissions:Create"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
textPermission.AddChild( |
||||
|
LocalizationManagementPermissions.Text.Update, |
||||
|
L("Permissions:Update"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
textPermission.AddChild( |
||||
|
LocalizationManagementPermissions.Text.Delete, |
||||
|
L("Permissions:Delete"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host); |
||||
|
} |
||||
|
|
||||
|
private static LocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<LocalizationManagementResource>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement.Permissions |
||||
|
{ |
||||
|
public static class LocalizationManagementPermissions |
||||
|
{ |
||||
|
public const string GroupName = "LocalizationManagement"; |
||||
|
|
||||
|
public class Resource |
||||
|
{ |
||||
|
public const string Default = GroupName + ".Resource"; |
||||
|
|
||||
|
public const string Create = Default + ".Create"; |
||||
|
|
||||
|
public const string Update = Default + ".Update"; |
||||
|
|
||||
|
public const string Delete = Default + ".Delete"; |
||||
|
} |
||||
|
|
||||
|
public class Language |
||||
|
{ |
||||
|
public const string Default = GroupName + ".Language"; |
||||
|
|
||||
|
public const string Create = Default + ".Create"; |
||||
|
|
||||
|
public const string Update = Default + ".Update"; |
||||
|
|
||||
|
public const string Delete = Default + ".Delete"; |
||||
|
} |
||||
|
|
||||
|
public class Text |
||||
|
{ |
||||
|
public const string Default = GroupName + ".Text"; |
||||
|
|
||||
|
public const string Create = Default + ".Create"; |
||||
|
|
||||
|
public const string Update = Default + ".Update"; |
||||
|
|
||||
|
public const string Delete = Default + ".Delete"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class ResourceDto : AuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public bool Enable { get; set; } |
||||
|
public string Name { get; set; } |
||||
|
public string DisplayName { get; set; } |
||||
|
public string Description { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class TextDifferenceDto : EntityDto<int> |
||||
|
{ |
||||
|
public string CultureName { get; set; } |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public string ResourceName { get; set; } |
||||
|
public string TargetCultureName { get; set; } |
||||
|
public string TargetValue { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class TextDto : EntityDto<int> |
||||
|
{ |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public string CultureName { get; set; } |
||||
|
public string ResourceName { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class UpdateTextInput : CreateOrUpdateTextInput |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Domain\LINGYUN.Abp.LocalizationManagement.Domain.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Application.Contracts\LINGYUN.Abp.LocalizationManagement.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AutoMapper; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpDddApplicationModule), |
||||
|
typeof(AbpLocalizationManagementDomainModule), |
||||
|
typeof(AbpLocalizationManagementApplicationContractsModule))] |
||||
|
public class AbpLocalizationManagementApplicationModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAutoMapperObjectMapper<AbpLocalizationManagementApplicationModule>(); |
||||
|
|
||||
|
Configure<AbpAutoMapperOptions>(options => |
||||
|
{ |
||||
|
options.AddProfile<LocalizationManagementApplicationMapperProfile>(validate: true); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Permissions; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class LanguageAppService : |
||||
|
CrudAppService< |
||||
|
Language, |
||||
|
LanguageDto, |
||||
|
Guid, |
||||
|
GetLanguagesInput, |
||||
|
CreateOrUpdateLanguageInput, |
||||
|
CreateOrUpdateLanguageInput>, |
||||
|
ILanguageAppService |
||||
|
{ |
||||
|
public LanguageAppService(ILanguageRepository repository) : base(repository) |
||||
|
{ |
||||
|
GetPolicyName = LocalizationManagementPermissions.Language.Default; |
||||
|
GetListPolicyName = LocalizationManagementPermissions.Language.Default; |
||||
|
CreatePolicyName = LocalizationManagementPermissions.Language.Create; |
||||
|
UpdatePolicyName = LocalizationManagementPermissions.Language.Update; |
||||
|
DeletePolicyName = LocalizationManagementPermissions.Language.Delete; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<ListResultDto<LanguageDto>> GetAllAsync() |
||||
|
{ |
||||
|
await CheckGetListPolicyAsync(); |
||||
|
|
||||
|
var languages = await Repository.GetListAsync(); |
||||
|
|
||||
|
return new ListResultDto<LanguageDto>( |
||||
|
ObjectMapper.Map<List<Language>, List<LanguageDto>>(languages)); |
||||
|
} |
||||
|
|
||||
|
protected override Language MapToEntity(CreateOrUpdateLanguageInput createInput) |
||||
|
{ |
||||
|
return new Language( |
||||
|
createInput.CultureName, |
||||
|
createInput.UiCultureName, |
||||
|
createInput.DisplayName, |
||||
|
createInput.FlagIcon) |
||||
|
{ |
||||
|
Enable = createInput.Enable |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected override void MapToEntity(CreateOrUpdateLanguageInput updateInput, Language entity) |
||||
|
{ |
||||
|
if (!string.Equals(entity.FlagIcon, updateInput.FlagIcon, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
{ |
||||
|
entity.FlagIcon = updateInput.FlagIcon; |
||||
|
} |
||||
|
entity.ChangeCulture(updateInput.CultureName, updateInput.UiCultureName, updateInput.DisplayName); |
||||
|
entity.Enable = updateInput.Enable; |
||||
|
} |
||||
|
|
||||
|
protected override async Task<IQueryable<Language>> CreateFilteredQueryAsync(GetLanguagesInput input) |
||||
|
{ |
||||
|
var query = await base.CreateFilteredQueryAsync(input); |
||||
|
|
||||
|
query = query.WhereIf(!input.Filter.IsNullOrWhiteSpace(), |
||||
|
x => x.CultureName.Contains(input.Filter) || x.UiCultureName.Contains(input.Filter) || |
||||
|
x.DisplayName.Contains(input.Filter)); |
||||
|
|
||||
|
return query; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using AutoMapper; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class LocalizationManagementApplicationMapperProfile : Profile |
||||
|
{ |
||||
|
public LocalizationManagementApplicationMapperProfile() |
||||
|
{ |
||||
|
CreateMap<Language, LanguageDto>(); |
||||
|
CreateMap<Resource, ResourceDto>(); |
||||
|
CreateMap<Text, TextDto>(); |
||||
|
CreateMap<TextDifference, TextDifferenceDto>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Permissions; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class ResourceAppService : |
||||
|
CrudAppService< |
||||
|
Resource, |
||||
|
ResourceDto, |
||||
|
Guid, |
||||
|
GetResourcesInput, |
||||
|
CreateOrUpdateResourceInput, |
||||
|
CreateOrUpdateResourceInput>, |
||||
|
IResourceAppService |
||||
|
{ |
||||
|
public ResourceAppService(IResourceRepository repository) : base(repository) |
||||
|
{ |
||||
|
GetPolicyName = LocalizationManagementPermissions.Resource.Default; |
||||
|
GetListPolicyName = LocalizationManagementPermissions.Resource.Default; |
||||
|
CreatePolicyName = LocalizationManagementPermissions.Resource.Create; |
||||
|
UpdatePolicyName = LocalizationManagementPermissions.Resource.Update; |
||||
|
DeletePolicyName = LocalizationManagementPermissions.Resource.Delete; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<ListResultDto<ResourceDto>> GetAllAsync() |
||||
|
{ |
||||
|
await CheckGetListPolicyAsync(); |
||||
|
|
||||
|
var resources = await Repository.GetListAsync(); |
||||
|
|
||||
|
return new ListResultDto<ResourceDto>( |
||||
|
ObjectMapper.Map<List<Resource>, List<ResourceDto>>(resources)); |
||||
|
} |
||||
|
|
||||
|
protected override Resource MapToEntity(CreateOrUpdateResourceInput createInput) |
||||
|
{ |
||||
|
return new Resource( |
||||
|
createInput.Name, |
||||
|
createInput.DisplayName, |
||||
|
createInput.Description) |
||||
|
{ |
||||
|
Enable = createInput.Enable |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected override void MapToEntity(CreateOrUpdateResourceInput updateInput, Resource entity) |
||||
|
{ |
||||
|
if (!string.Equals(entity.Name, updateInput.Name, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
{ |
||||
|
entity.Name = updateInput.Name; |
||||
|
} |
||||
|
if (!string.Equals(entity.DisplayName, updateInput.DisplayName, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
{ |
||||
|
entity.DisplayName = updateInput.DisplayName; |
||||
|
} |
||||
|
if (!string.Equals(entity.Description, updateInput.Description, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
{ |
||||
|
entity.Description = updateInput.Description; |
||||
|
} |
||||
|
entity.Enable = updateInput.Enable; |
||||
|
} |
||||
|
|
||||
|
protected override async Task<IQueryable<Resource>> CreateFilteredQueryAsync(GetResourcesInput input) |
||||
|
{ |
||||
|
var query = await base.CreateFilteredQueryAsync(input); |
||||
|
|
||||
|
query = query.WhereIf(!input.Filter.IsNullOrWhiteSpace(), |
||||
|
x => x.Name.Contains(input.Filter) || x.DisplayName.Contains(input.Filter)); |
||||
|
|
||||
|
return query; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Permissions; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class TextAppService : |
||||
|
CrudAppService< |
||||
|
Text, |
||||
|
TextDto, |
||||
|
TextDifferenceDto, |
||||
|
int, |
||||
|
GetTextsInput, |
||||
|
CreateTextInput, |
||||
|
UpdateTextInput>, |
||||
|
ITextAppService |
||||
|
{ |
||||
|
private readonly ITextRepository _textRepository; |
||||
|
public TextAppService(ITextRepository repository) : base(repository) |
||||
|
{ |
||||
|
_textRepository = repository; |
||||
|
|
||||
|
GetPolicyName = LocalizationManagementPermissions.Text.Default; |
||||
|
GetListPolicyName = LocalizationManagementPermissions.Text.Default; |
||||
|
CreatePolicyName = LocalizationManagementPermissions.Text.Create; |
||||
|
UpdatePolicyName = LocalizationManagementPermissions.Text.Update; |
||||
|
DeletePolicyName = LocalizationManagementPermissions.Text.Delete; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input) |
||||
|
{ |
||||
|
await CheckGetPolicyAsync(); |
||||
|
|
||||
|
var text = await _textRepository.GetByCultureKeyAsync( |
||||
|
input.ResourceName, input.CultureName, input.Key); |
||||
|
|
||||
|
return await MapToGetOutputDtoAsync(text); |
||||
|
} |
||||
|
|
||||
|
public override async Task<PagedResultDto<TextDifferenceDto>> GetListAsync(GetTextsInput input) |
||||
|
{ |
||||
|
await CheckGetListPolicyAsync(); |
||||
|
|
||||
|
var count = await _textRepository.GetDifferenceCountAsync( |
||||
|
input.CultureName, input.TargetCultureName, |
||||
|
input.ResourceName, input.OnlyNull, input.Filter); |
||||
|
|
||||
|
var texts = await _textRepository.GetDifferencePagedListAsync( |
||||
|
input.CultureName, input.TargetCultureName, |
||||
|
input.ResourceName, input.OnlyNull, input.Filter, |
||||
|
input.Sorting, input.SkipCount, input.MaxResultCount); |
||||
|
|
||||
|
return new PagedResultDto<TextDifferenceDto>(count, |
||||
|
ObjectMapper.Map<List<TextDifference>, List<TextDifferenceDto>>(texts)); |
||||
|
} |
||||
|
|
||||
|
protected override Text MapToEntity(CreateTextInput createInput) |
||||
|
{ |
||||
|
return new Text( |
||||
|
createInput.ResourceName, |
||||
|
createInput.CultureName, |
||||
|
createInput.Key, |
||||
|
createInput.Value); |
||||
|
} |
||||
|
|
||||
|
protected override void MapToEntity(UpdateTextInput updateInput, Text entity) |
||||
|
{ |
||||
|
entity.SetValue(updateInput.Value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\LocalizationManagement\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\LocalizationManagement\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\LocalizationManagement\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\LocalizationManagement\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Validation" Version="4.2.1" /> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,29 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpValidationModule), |
||||
|
typeof(AbpLocalizationModule))] |
||||
|
public class AbpLocalizationManagementDomainSharedModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpLocalizationManagementDomainSharedModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<LocalizationManagementResource>("en") |
||||
|
.AddVirtualJson("/LINGYUN/Abp/LocalizationManagement/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public static class LanguageConsts |
||||
|
{ |
||||
|
public static int MaxCultureNameLength { get; set; } = 20; |
||||
|
public static int MaxUiCultureNameLength { get; set; } = 20; |
||||
|
public static int MaxDisplayNameLength { get; set; } = 64; |
||||
|
public static int MaxFlagIconLength { get; set; } = 30; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.Localization |
||||
|
{ |
||||
|
[LocalizationResourceName("LocalizationManagement")] |
||||
|
public class LocalizationManagementResource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"Delete": "Delete", |
||||
|
"DisplayName:Any": "Any", |
||||
|
"DisplayName:CreationTime": "Creation Time", |
||||
|
"DisplayName:CultureName": "Culture", |
||||
|
"DisplayName:Description": "Description", |
||||
|
"DisplayName:DisplayName": "Display Name", |
||||
|
"DisplayName:Enable": "Enable", |
||||
|
"DisplayName:FlagIcon": "Flag Icon", |
||||
|
"DisplayName:Key": "Key", |
||||
|
"DisplayName:LastModificationTime": "Modification Time", |
||||
|
"DisplayName:Name": "Name", |
||||
|
"DisplayName:OnlyNull": "Only Null", |
||||
|
"DisplayName:ResourceName": "Resource", |
||||
|
"DisplayName:SaveAndNext": "Save & Next", |
||||
|
"DisplayName:TargetCultureName": "Target Culture", |
||||
|
"DisplayName:TargetValue": "Target Value", |
||||
|
"DisplayName:UiCultureName": "Ui Culture", |
||||
|
"DisplayName:Value": "Value", |
||||
|
"Permissions:LocalizationManagement": "Localization", |
||||
|
"Permissions:Language": "Language", |
||||
|
"Permissions:Resource": "Resource", |
||||
|
"Permissions:Text": "Text", |
||||
|
"Permissions:Create": "Create", |
||||
|
"Permissions:Update": "Update", |
||||
|
"Permissions:Delete": "Delete", |
||||
|
"Edit": "Edit", |
||||
|
"EditByName": "Edit - {0}", |
||||
|
"Filter": "Filter", |
||||
|
"Language:AddNew": "Add New Language", |
||||
|
"Resource:AddNew": "Add New Resource", |
||||
|
"SaveAndNext": "Save & Next", |
||||
|
"SearchFilter": "Search", |
||||
|
"Text:AddNew": "Add New Text", |
||||
|
"WillDeleteLanguage": "Language to be deleted {0}", |
||||
|
"WillDeleteResource": "Resource to be deleted {0}", |
||||
|
"WillDeleteText": "Document to be deleted {0}" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"Delete": "删除", |
||||
|
"DisplayName:Any": "所有", |
||||
|
"DisplayName:CreationTime": "创建时间", |
||||
|
"DisplayName:CultureName": "文化名称", |
||||
|
"DisplayName:Description": "描述", |
||||
|
"DisplayName:DisplayName": "显示名称", |
||||
|
"DisplayName:Enable": "启用", |
||||
|
"DisplayName:FlagIcon": "旗帜图标", |
||||
|
"DisplayName:Key": "键", |
||||
|
"DisplayName:LastModificationTime": "变更时间", |
||||
|
"DisplayName:Name": "名称", |
||||
|
"DisplayName:OnlyNull": "仅空值", |
||||
|
"DisplayName:ResourceName": "资源名称", |
||||
|
"DisplayName:SaveAndNext": "保存并下一步", |
||||
|
"DisplayName:TargetCultureName": "目标文化", |
||||
|
"DisplayName:TargetValue": "目标值", |
||||
|
"DisplayName:UiCultureName": "界面文化", |
||||
|
"DisplayName:Value": "值", |
||||
|
"Permissions:LocalizationManagement": "本地化管理", |
||||
|
"Permissions:Language": "语言管理", |
||||
|
"Permissions:Resource": "资源管理", |
||||
|
"Permissions:Text": "文档管理", |
||||
|
"Permissions:Create": "新增", |
||||
|
"Permissions:Update": "编辑", |
||||
|
"Permissions:Delete": "删除", |
||||
|
"Edit": "编辑", |
||||
|
"EditByName": "编辑 - {0}", |
||||
|
"Filter": "过滤字符", |
||||
|
"Language:AddNew": "添加新语言", |
||||
|
"Resource:AddNew": "添加新资源", |
||||
|
"SaveAndNext": "保存并下一步", |
||||
|
"SearchFilter": "请输入过滤字符", |
||||
|
"Text:AddNew": "添加新文档", |
||||
|
"WillDeleteLanguage": "将要删除语言 {0}", |
||||
|
"WillDeleteResource": "将要删除资源 {0}", |
||||
|
"WillDeleteText": "将要删除文档 {0}" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public static class ResourceConsts |
||||
|
{ |
||||
|
public static int MaxNameLength { get; set; } = 50; |
||||
|
public static int MaxDisplayNameLength { get; set; } = 64; |
||||
|
public static int MaxDescriptionLength { get; set; } = 64; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public static class TextConsts |
||||
|
{ |
||||
|
public static int MaxKeyLength { get; set; } = 512; |
||||
|
public static int MaxValueLength { get; set; } = 2 * 1024; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class TextDifference |
||||
|
{ |
||||
|
public int Id { get; set; } |
||||
|
public string CultureName { get; set; } |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public string ResourceName { get; set; } |
||||
|
public string TargetCultureName { get; set; } |
||||
|
public string TargetValue { get; set; } |
||||
|
|
||||
|
public TextDifference() { } |
||||
|
public TextDifference( |
||||
|
int id, |
||||
|
string cultureName, |
||||
|
string key, |
||||
|
string value, |
||||
|
string targetCultureName, |
||||
|
string targetValue = null, |
||||
|
string resourceName = null) |
||||
|
{ |
||||
|
Id = id; |
||||
|
Key = key; |
||||
|
Value = value; |
||||
|
CultureName = cultureName; |
||||
|
TargetCultureName = targetCultureName; |
||||
|
|
||||
|
TargetValue = targetValue; |
||||
|
ResourceName = resourceName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class TextEto |
||||
|
{ |
||||
|
public string CultureName { get; set; } |
||||
|
public string Key { get; set; } |
||||
|
public string Value { get; set; } |
||||
|
public string ResourceName { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AutoMapper" Version="4.2.1" /> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\common\LINGYUN.Abp.Localization.Dynamic\LINGYUN.Abp.Localization.Dynamic.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Domain.Shared\LINGYUN.Abp.LocalizationManagement.Domain.Shared.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,44 @@ |
|||||
|
using LINGYUN.Abp.Localization.Dynamic; |
||||
|
using LINGYUN.Abp.LocalizationManagement.Localization; |
||||
|
using Volo.Abp.AutoMapper; |
||||
|
using Volo.Abp.Domain; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAutoMapperModule), |
||||
|
typeof(AbpDddDomainModule), |
||||
|
typeof(AbpLocalizationDynamicModule), |
||||
|
typeof(AbpLocalizationManagementDomainSharedModule))] |
||||
|
public class AbpLocalizationManagementDomainModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAutoMapperObjectMapper<AbpLocalizationManagementDomainModule>(); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<LocalizationManagementResource>() |
||||
|
.AddDynamic(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpAutoMapperOptions>(options => |
||||
|
{ |
||||
|
options.AddProfile<LocalizationManagementDomainMapperProfile>(validate: true); |
||||
|
}); |
||||
|
|
||||
|
// 分布式事件
|
||||
|
//Configure<AbpDistributedEntityEventOptions>(options =>
|
||||
|
//{
|
||||
|
// options.AutoEventSelectors.Add<Text>();
|
||||
|
// options.EtoMappings.Add<Text, TextEto>();
|
||||
|
//});
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface ILanguageRepository : IRepository<Language, Guid> |
||||
|
{ |
||||
|
Task<Language> FindByCultureNameAsync( |
||||
|
string cultureName, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<List<Language>> GetActivedListAsync(CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface IResourceRepository : IRepository<Resource, Guid> |
||||
|
{ |
||||
|
Task<bool> ExistsAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<Resource> FindByNameAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public interface ITextRepository : IRepository<Text, int> |
||||
|
{ |
||||
|
Task<Text> GetByCultureKeyAsync( |
||||
|
string resourceName, |
||||
|
string cultureName, |
||||
|
string key, |
||||
|
CancellationToken cancellationToken = default |
||||
|
); |
||||
|
|
||||
|
Task<List<Text>> GetListAsync( |
||||
|
string resourceName, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<List<Text>> GetListAsync( |
||||
|
string resourceName, |
||||
|
string cultureName, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<int> GetDifferenceCountAsync( |
||||
|
string cultureName, |
||||
|
string targetCultureName, |
||||
|
string resourceName = null, |
||||
|
bool? onlyNull = null, |
||||
|
string filter = null, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<List<TextDifference>> GetDifferencePagedListAsync( |
||||
|
string cultureName, |
||||
|
string targetCultureName, |
||||
|
string resourceName = null, |
||||
|
bool? onlyNull = null, |
||||
|
string filter = null, |
||||
|
string sorting = nameof(Text.Key), |
||||
|
int skipCount = 1, |
||||
|
int maxResultCount = 10, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class Language : AuditedEntity<Guid>, ILanguageInfo |
||||
|
{ |
||||
|
public virtual bool Enable { get; set; } |
||||
|
public virtual string CultureName { get; protected set; } |
||||
|
public virtual string UiCultureName { get; protected set; } |
||||
|
public virtual string DisplayName { get; protected set; } |
||||
|
public virtual string FlagIcon { get; set; } |
||||
|
protected Language() { } |
||||
|
public Language( |
||||
|
[NotNull] string cultureName, |
||||
|
[NotNull] string uiCultureName, |
||||
|
[NotNull] string displayName, |
||||
|
string flagIcon = null) |
||||
|
{ |
||||
|
CultureName = Check.NotNullOrWhiteSpace(cultureName, nameof(cultureName), LanguageConsts.MaxCultureNameLength); |
||||
|
UiCultureName = Check.NotNullOrWhiteSpace(uiCultureName, nameof(uiCultureName), LanguageConsts.MaxUiCultureNameLength); |
||||
|
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), LanguageConsts.MaxDisplayNameLength); |
||||
|
|
||||
|
FlagIcon = !flagIcon.IsNullOrWhiteSpace() |
||||
|
? Check.Length(flagIcon, nameof(flagIcon), LanguageConsts.MaxFlagIconLength) |
||||
|
: null; |
||||
|
|
||||
|
Enable = true; |
||||
|
} |
||||
|
|
||||
|
public virtual void ChangeCulture(string cultureName, string uiCultureName = null, string displayName = null) |
||||
|
{ |
||||
|
ChangeCultureInternal(cultureName, uiCultureName, displayName); |
||||
|
} |
||||
|
|
||||
|
private void ChangeCultureInternal(string cultureName, string uiCultureName, string displayName) |
||||
|
{ |
||||
|
CultureName = Check.NotNullOrWhiteSpace(cultureName, nameof(cultureName), LanguageConsts.MaxCultureNameLength); |
||||
|
|
||||
|
UiCultureName = !uiCultureName.IsNullOrWhiteSpace() |
||||
|
? Check.Length(uiCultureName, nameof(uiCultureName), LanguageConsts.MaxUiCultureNameLength) |
||||
|
: cultureName; |
||||
|
|
||||
|
DisplayName = !displayName.IsNullOrWhiteSpace() |
||||
|
? Check.Length(displayName, nameof(displayName), LanguageConsts.MaxDisplayNameLength) |
||||
|
: cultureName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public static class LocalizationDbProperties |
||||
|
{ |
||||
|
public static string DbTablePrefix { get; set; } = "AbpLocalization"; |
||||
|
|
||||
|
public static string DbSchema { get; set; } = null; |
||||
|
|
||||
|
public const string ConnectionStringName = "AbpLocalizationManagement"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using AutoMapper; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class LocalizationManagementDomainMapperProfile : Profile |
||||
|
{ |
||||
|
public LocalizationManagementDomainMapperProfile() |
||||
|
{ |
||||
|
CreateMap<Text, TextEto>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
using LINGYUN.Abp.Localization.Dynamic; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[Dependency(ServiceLifetime.Singleton, ReplaceServices = true)] |
||||
|
[ExposeServices( |
||||
|
typeof(ILocalizationStore), |
||||
|
typeof(LocalizationStore))] |
||||
|
public class LocalizationStore : ILocalizationStore |
||||
|
{ |
||||
|
protected ILanguageRepository LanguageRepository { get; } |
||||
|
protected ITextRepository TextRepository { get; } |
||||
|
protected IResourceRepository ResourceRepository { get; } |
||||
|
|
||||
|
public LocalizationStore( |
||||
|
ILanguageRepository languageRepository, |
||||
|
ITextRepository textRepository, |
||||
|
IResourceRepository resourceRepository) |
||||
|
{ |
||||
|
TextRepository = textRepository; |
||||
|
LanguageRepository = languageRepository; |
||||
|
ResourceRepository = resourceRepository; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<LanguageInfo>> GetLanguageListAsync( |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var languages = await LanguageRepository.GetActivedListAsync(cancellationToken); |
||||
|
|
||||
|
return languages |
||||
|
.Select(x => new LanguageInfo(x.CultureName, x.UiCultureName, x.DisplayName, x.FlagIcon)) |
||||
|
.ToList(); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<Dictionary<string, ILocalizationDictionary>> GetLocalizationDictionaryAsync( |
||||
|
string resourceName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
// TODO: 引用缓存?
|
||||
|
var dictionaries = new Dictionary<string, ILocalizationDictionary>(); |
||||
|
var resource = await ResourceRepository.FindByNameAsync(resourceName, cancellationToken); |
||||
|
if (resource == null || !resource.Enable) |
||||
|
{ |
||||
|
// 资源不存在或未启用返回空
|
||||
|
return dictionaries; |
||||
|
} |
||||
|
|
||||
|
var texts = await TextRepository.GetListAsync(resourceName, cancellationToken); |
||||
|
|
||||
|
foreach (var textGroup in texts.GroupBy(x => x.CultureName)) |
||||
|
{ |
||||
|
var cultureTextDictionaires = new Dictionary<string, LocalizedString>(); |
||||
|
foreach (var text in textGroup) |
||||
|
{ |
||||
|
// 本地化名称去重
|
||||
|
if (!cultureTextDictionaires.ContainsKey(text.Key)) |
||||
|
{ |
||||
|
cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.NormalizeLineEndings()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 本地化语言去重
|
||||
|
if (!dictionaries.ContainsKey(textGroup.Key)) |
||||
|
{ |
||||
|
dictionaries[textGroup.Key] = new StaticLocalizationDictionary(textGroup.Key, cultureTextDictionaires); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return dictionaries; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<bool> ResourceExistsAsync(string resourceName, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await ResourceRepository.ExistsAsync(resourceName, cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
using LINGYUN.Abp.Localization.Dynamic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Entities.Events; |
||||
|
using Volo.Abp.EventBus; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class LocalizationSynchronizer : |
||||
|
ILocalEventHandler<EntityCreatedEventData<Text>>, |
||||
|
ILocalEventHandler<EntityUpdatedEventData<Text>>, |
||||
|
ILocalEventHandler<EntityDeletedEventData<Text>>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
private readonly IDistributedEventBus _eventBus; |
||||
|
|
||||
|
public LocalizationSynchronizer( |
||||
|
IDistributedEventBus eventBus) |
||||
|
{ |
||||
|
_eventBus = eventBus; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task HandleEventAsync(EntityCreatedEventData<Text> eventData) |
||||
|
{ |
||||
|
await HandleEventAsync(BuildResetEventData(eventData.Entity)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task HandleEventAsync(EntityUpdatedEventData<Text> eventData) |
||||
|
{ |
||||
|
await HandleEventAsync(BuildResetEventData(eventData.Entity)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task HandleEventAsync(EntityDeletedEventData<Text> eventData) |
||||
|
{ |
||||
|
var data = BuildResetEventData(eventData.Entity); |
||||
|
data.IsDeleted = true; |
||||
|
|
||||
|
await HandleEventAsync(data); |
||||
|
} |
||||
|
|
||||
|
private LocalizedStringCacheResetEventData BuildResetEventData(Text text) |
||||
|
{ |
||||
|
return new LocalizedStringCacheResetEventData( |
||||
|
text.ResourceName, text.CultureName, text.Key, text.Value); |
||||
|
} |
||||
|
|
||||
|
private async Task HandleEventAsync(LocalizedStringCacheResetEventData eventData) |
||||
|
{ |
||||
|
await _eventBus.PublishAsync(eventData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class Resource : AuditedEntity<Guid> |
||||
|
{ |
||||
|
public virtual bool Enable { get; set; } |
||||
|
public virtual string Name { get; set; } |
||||
|
public virtual string DisplayName { get; set; } |
||||
|
public virtual string Description { get; set; } |
||||
|
protected Resource() { } |
||||
|
public Resource( |
||||
|
[NotNull] string name, |
||||
|
[CanBeNull] string displayName = null, |
||||
|
[CanBeNull] string description = null) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), ResourceConsts.MaxNameLength); |
||||
|
|
||||
|
DisplayName = displayName ?? Name; |
||||
|
Description = description; |
||||
|
|
||||
|
Enable = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
public class Text : Entity<int> |
||||
|
{ |
||||
|
public virtual string CultureName { get; protected set; } |
||||
|
public virtual string Key { get; protected set; } |
||||
|
public virtual string Value { get; protected set; } |
||||
|
public virtual string ResourceName { get; protected set; } |
||||
|
protected Text() { } |
||||
|
public Text( |
||||
|
[NotNull] string resourceName, |
||||
|
[NotNull] string cultureName, |
||||
|
[NotNull] string key, |
||||
|
[CanBeNull] string value) |
||||
|
{ |
||||
|
ResourceName = Check.NotNull(resourceName, nameof(resourceName), ResourceConsts.MaxNameLength); |
||||
|
CultureName = Check.NotNullOrWhiteSpace(cultureName, nameof(cultureName), LanguageConsts.MaxCultureNameLength); |
||||
|
Key = Check.NotNullOrWhiteSpace(key, nameof(key), TextConsts.MaxKeyLength); |
||||
|
|
||||
|
Value = !value.IsNullOrWhiteSpace() |
||||
|
? Check.NotNullOrWhiteSpace(value, nameof(value), TextConsts.MaxValueLength) |
||||
|
: ""; |
||||
|
} |
||||
|
|
||||
|
public void SetValue(string value) |
||||
|
{ |
||||
|
Value = !value.IsNullOrWhiteSpace() |
||||
|
? Check.NotNullOrWhiteSpace(value, nameof(value), TextConsts.MaxValueLength) |
||||
|
: Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.1</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Domain\LINGYUN.Abp.LocalizationManagement.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpEntityFrameworkCoreModule), |
||||
|
typeof(AbpLocalizationManagementDomainModule))] |
||||
|
public class AbpLocalizationManagementEntityFrameworkCoreModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAbpDbContext<LocalizationDbContext>(options => |
||||
|
{ |
||||
|
options.AddRepository<Text, EfCoreTextRepository>(); |
||||
|
options.AddRepository<Language, EfCoreLanguageRepository>(); |
||||
|
options.AddRepository<Resource, EfCoreResourceRepository>(); |
||||
|
|
||||
|
options.AddDefaultRepositories(includeAllEntities: true); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class EfCoreLanguageRepository : EfCoreRepository<LocalizationDbContext, Language, Guid>, |
||||
|
ILanguageRepository |
||||
|
{ |
||||
|
public EfCoreLanguageRepository( |
||||
|
IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<Language> FindByCultureNameAsync( |
||||
|
string cultureName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()).Where(x => x.CultureName.Equals(cultureName)) |
||||
|
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<Language>> GetActivedListAsync(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()).Where(x => x.Enable) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class EfCoreResourceRepository : EfCoreRepository<LocalizationDbContext, Resource, Guid>, |
||||
|
IResourceRepository |
||||
|
{ |
||||
|
public EfCoreResourceRepository( |
||||
|
IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<bool> ExistsAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()).AnyAsync(x => x.Name.Equals(name)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<Resource> FindByNameAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()).Where(x => x.Name.Equals(name)) |
||||
|
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,135 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Dynamic.Core; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class EfCoreTextRepository : EfCoreRepository<LocalizationDbContext, Text, int>, |
||||
|
ITextRepository |
||||
|
{ |
||||
|
public EfCoreTextRepository( |
||||
|
IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<Text> GetByCultureKeyAsync( |
||||
|
string resourceName, |
||||
|
string cultureName, |
||||
|
string key, |
||||
|
CancellationToken cancellationToken = default |
||||
|
) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()) |
||||
|
.Where(x => x.ResourceName.Equals(resourceName) && x.CultureName.Equals(cultureName) && x.Key.Equals(key)) |
||||
|
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<int> GetDifferenceCountAsync( |
||||
|
string cultureName, |
||||
|
string targetCultureName, |
||||
|
string resourceName = null, |
||||
|
bool? onlyNull = null, |
||||
|
string filter = null, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await BuildTextDifferenceQueryAsync( |
||||
|
cultureName, |
||||
|
targetCultureName, |
||||
|
resourceName, |
||||
|
onlyNull, |
||||
|
filter)) |
||||
|
.CountAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<Text>> GetListAsync( |
||||
|
string resourceName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var languages = (await GetDbContextAsync()).Set<Language>(); |
||||
|
var texts = await GetDbSetAsync(); |
||||
|
|
||||
|
return await (from txts in texts |
||||
|
join lg in languages |
||||
|
on txts.CultureName equals lg.CultureName |
||||
|
where txts.ResourceName.Equals(resourceName) && |
||||
|
lg.Enable |
||||
|
select txts) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<Text>> GetListAsync( |
||||
|
string resourceName, |
||||
|
string cultureName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetDbSetAsync()) |
||||
|
.Where(x => x.ResourceName.Equals(resourceName) && x.CultureName.Equals(cultureName)) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<TextDifference>> GetDifferencePagedListAsync( |
||||
|
string cultureName, |
||||
|
string targetCultureName, |
||||
|
string resourceName = null, |
||||
|
bool? onlyNull = null, |
||||
|
string filter = null, |
||||
|
string sorting = nameof(TextDifference.Key), |
||||
|
int skipCount = 1, |
||||
|
int maxResultCount = 10, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await BuildTextDifferenceQueryAsync( |
||||
|
cultureName, |
||||
|
targetCultureName, |
||||
|
resourceName, |
||||
|
onlyNull, |
||||
|
filter, |
||||
|
sorting)) |
||||
|
.PageBy(skipCount, maxResultCount) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<IQueryable<TextDifference>> BuildTextDifferenceQueryAsync( |
||||
|
string cultureName, |
||||
|
string targetCultureName, |
||||
|
string resourceName = null, |
||||
|
bool? onlyNull = null, |
||||
|
string filter = null, |
||||
|
string sorting = nameof(TextDifference.Key)) |
||||
|
{ |
||||
|
var textQuery = (await GetDbSetAsync()) |
||||
|
.Where(x => x.CultureName.Equals(cultureName)) |
||||
|
.WhereIf(!resourceName.IsNullOrWhiteSpace(), x => x.ResourceName.Equals(resourceName)) |
||||
|
.WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Key.Contains(filter)) |
||||
|
.OrderBy(sorting ?? nameof(TextDifference.Key)); |
||||
|
|
||||
|
var targetTextQuery = (await GetDbSetAsync()) |
||||
|
.Where(x => x.CultureName.Equals(targetCultureName)) |
||||
|
.WhereIf(!resourceName.IsNullOrWhiteSpace(), x => x.ResourceName.Equals(resourceName)); |
||||
|
|
||||
|
var query = from crtText in textQuery |
||||
|
join tgtText in targetTextQuery |
||||
|
on crtText.Key equals tgtText.Key |
||||
|
into tgt |
||||
|
from tt in tgt.DefaultIfEmpty() |
||||
|
where onlyNull.HasValue && onlyNull.Value |
||||
|
? tt.Value == null |
||||
|
: 1 == 1 |
||||
|
select new TextDifference( |
||||
|
crtText.Id, |
||||
|
crtText.CultureName, |
||||
|
crtText.Key, |
||||
|
crtText.Value, |
||||
|
targetCultureName, |
||||
|
tt != null ? tt.Value : null, |
||||
|
crtText.ResourceName); |
||||
|
return query; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
[ConnectionStringName(LocalizationDbProperties.ConnectionStringName)] |
||||
|
public interface ILocalizationDbContext : IEfCoreDbContext |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
[ConnectionStringName(LocalizationDbProperties.ConnectionStringName)] |
||||
|
public class LocalizationDbContext : AbpDbContext<LocalizationDbContext>, ILocalizationDbContext |
||||
|
{ |
||||
|
public virtual DbSet<Resource> Resources { get; set; } |
||||
|
public virtual DbSet<Language> Languages { get; set; } |
||||
|
public virtual DbSet<Text> Texts { get; set; } |
||||
|
public LocalizationDbContext( |
||||
|
DbContextOptions<LocalizationDbContext> options) : base(options) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
base.OnModelCreating(modelBuilder); |
||||
|
|
||||
|
modelBuilder.ConfigureLocalization(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public static class LocalizationDbContextModelBuilderExtensions |
||||
|
{ |
||||
|
public static void ConfigureLocalization( |
||||
|
this ModelBuilder builder, |
||||
|
Action<LocalizationModelBuilderConfigurationOptions> optionsAction = null) |
||||
|
{ |
||||
|
Check.NotNull(builder, nameof(builder)); |
||||
|
|
||||
|
var options = new LocalizationModelBuilderConfigurationOptions( |
||||
|
LocalizationDbProperties.DbTablePrefix, |
||||
|
LocalizationDbProperties.DbSchema |
||||
|
); |
||||
|
|
||||
|
optionsAction?.Invoke(options); |
||||
|
|
||||
|
builder.Entity<Language>(x => |
||||
|
{ |
||||
|
x.ToTable(options.TablePrefix + "Languages", options.Schema); |
||||
|
|
||||
|
x.Property(p => p.CultureName) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(LanguageConsts.MaxCultureNameLength) |
||||
|
.HasColumnName(nameof(Language.CultureName)); |
||||
|
x.Property(p => p.UiCultureName) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(LanguageConsts.MaxUiCultureNameLength) |
||||
|
.HasColumnName(nameof(Language.UiCultureName)); |
||||
|
x.Property(p => p.DisplayName) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(LanguageConsts.MaxDisplayNameLength) |
||||
|
.HasColumnName(nameof(Language.DisplayName)); |
||||
|
|
||||
|
x.Property(p => p.FlagIcon) |
||||
|
.IsRequired(false) |
||||
|
.HasMaxLength(LanguageConsts.MaxFlagIconLength) |
||||
|
.HasColumnName(nameof(Language.FlagIcon)); |
||||
|
|
||||
|
x.Property(p => p.Enable) |
||||
|
.HasDefaultValue(true); |
||||
|
|
||||
|
x.ConfigureByConvention(); |
||||
|
|
||||
|
x.HasIndex(p => p.CultureName); |
||||
|
}); |
||||
|
|
||||
|
builder.Entity<Resource>(x => |
||||
|
{ |
||||
|
x.ToTable(options.TablePrefix + "Resources", options.Schema); |
||||
|
|
||||
|
x.Property(p => p.Name) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(ResourceConsts.MaxNameLength) |
||||
|
.HasColumnName(nameof(Resource.Name)); |
||||
|
|
||||
|
x.Property(p => p.DisplayName) |
||||
|
.HasMaxLength(ResourceConsts.MaxDisplayNameLength) |
||||
|
.HasColumnName(nameof(Resource.DisplayName)); |
||||
|
x.Property(p => p.Description) |
||||
|
.HasMaxLength(ResourceConsts.MaxDescriptionLength) |
||||
|
.HasColumnName(nameof(Resource.Description)); |
||||
|
|
||||
|
x.Property(p => p.Enable) |
||||
|
.HasDefaultValue(true); |
||||
|
|
||||
|
x.ConfigureByConvention(); |
||||
|
|
||||
|
x.HasIndex(p => p.Name); |
||||
|
}); |
||||
|
|
||||
|
builder.Entity<Text>(x => |
||||
|
{ |
||||
|
x.ToTable(options.TablePrefix + "Texts", options.Schema); |
||||
|
|
||||
|
x.Property(p => p.CultureName) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(LanguageConsts.MaxCultureNameLength) |
||||
|
.HasColumnName(nameof(Text.CultureName)); |
||||
|
x.Property(p => p.Key) |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(TextConsts.MaxKeyLength) |
||||
|
.HasColumnName(nameof(Text.Key)); |
||||
|
x.Property(p => p.Value) |
||||
|
.HasMaxLength(TextConsts.MaxValueLength) |
||||
|
.HasColumnName(nameof(Text.Value)); |
||||
|
|
||||
|
x.ConfigureByConvention(); |
||||
|
|
||||
|
x.HasIndex(p => p.Key); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class LocalizationModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
||||
|
{ |
||||
|
public LocalizationModelBuilderConfigurationOptions( |
||||
|
[NotNull] string tablePrefix = "", |
||||
|
[CanBeNull] string schema = null) |
||||
|
: base( |
||||
|
tablePrefix, |
||||
|
schema) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Application.Contracts\LINGYUN.Abp.LocalizationManagement.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,42 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Validation.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAspNetCoreMvcModule), |
||||
|
typeof(AbpLocalizationManagementApplicationContractsModule))] |
||||
|
public class AbpLocalizationManagementHttpApiModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
// Dto验证本地化
|
||||
|
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.AddAssemblyResource( |
||||
|
typeof(LocalizationManagementResource), |
||||
|
typeof(AbpLocalizationManagementApplicationContractsModule).Assembly); |
||||
|
}); |
||||
|
|
||||
|
PreConfigure<IMvcBuilder>(mvcBuilder => |
||||
|
{ |
||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpLocalizationManagementApplicationContractsModule).Assembly); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<LocalizationManagementResource>() |
||||
|
.AddBaseTypes(typeof(AbpValidationResource)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[RemoteService(Name = LocalizationRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area("localization")] |
||||
|
[Route("api/localization/languages")] |
||||
|
public class LanguageController : AbpController, ILanguageAppService |
||||
|
{ |
||||
|
private readonly ILanguageAppService _service; |
||||
|
|
||||
|
public LanguageController(ILanguageAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
public virtual async Task<LanguageDto> CreateAsync(CreateOrUpdateLanguageInput input) |
||||
|
{ |
||||
|
return await _service.CreateAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task DeleteAsync(Guid id) |
||||
|
{ |
||||
|
await _service.DeleteAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("all")] |
||||
|
public virtual async Task<ListResultDto<LanguageDto>> GetAllAsync() |
||||
|
{ |
||||
|
return await _service.GetAllAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<LanguageDto> GetAsync(Guid id) |
||||
|
{ |
||||
|
return await _service.GetAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual async Task<PagedResultDto<LanguageDto>> GetListAsync(GetLanguagesInput input) |
||||
|
{ |
||||
|
return await _service.GetListAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<LanguageDto> UpdateAsync(Guid id, CreateOrUpdateLanguageInput input) |
||||
|
{ |
||||
|
return await _service.UpdateAsync(id, input); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[RemoteService(Name = LocalizationRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area("localization")] |
||||
|
[Route("api/localization/resources")] |
||||
|
public class ResourceController : AbpController, IResourceAppService |
||||
|
{ |
||||
|
private readonly IResourceAppService _service; |
||||
|
|
||||
|
public ResourceController(IResourceAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
public virtual async Task<ResourceDto> CreateAsync(CreateOrUpdateResourceInput input) |
||||
|
{ |
||||
|
return await _service.CreateAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task DeleteAsync(Guid id) |
||||
|
{ |
||||
|
await _service.DeleteAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("all")] |
||||
|
public virtual async Task<ListResultDto<ResourceDto>> GetAllAsync() |
||||
|
{ |
||||
|
return await _service.GetAllAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<ResourceDto> GetAsync(Guid id) |
||||
|
{ |
||||
|
return await _service.GetAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual async Task<PagedResultDto<ResourceDto>> GetListAsync(GetResourcesInput input) |
||||
|
{ |
||||
|
return await _service.GetListAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<ResourceDto> UpdateAsync(Guid id, CreateOrUpdateResourceInput input) |
||||
|
{ |
||||
|
return await _service.UpdateAsync(id, input); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[RemoteService(Name = LocalizationRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area("localization")] |
||||
|
[Route("api/localization/texts")] |
||||
|
public class TextController : AbpController, ITextAppService |
||||
|
{ |
||||
|
private readonly ITextAppService _service; |
||||
|
|
||||
|
public TextController(ITextAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
public virtual async Task<TextDto> CreateAsync(CreateTextInput input) |
||||
|
{ |
||||
|
return await _service.CreateAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task DeleteAsync(int id) |
||||
|
{ |
||||
|
await _service.DeleteAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<TextDto> GetAsync(int id) |
||||
|
{ |
||||
|
return await _service.GetAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("by-culture-key")] |
||||
|
public virtual async Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input) |
||||
|
{ |
||||
|
return await _service.GetByCultureKeyAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual async Task<PagedResultDto<TextDifferenceDto>> GetListAsync(GetTextsInput input) |
||||
|
{ |
||||
|
return await _service.GetListAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
[Route("{id}")] |
||||
|
public virtual async Task<TextDto> UpdateAsync(int id, UpdateTextInput input) |
||||
|
{ |
||||
|
return await _service.UpdateAsync(id, input); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
# Localization Management |
||||
|
|
||||
|
本地化文档管理模块,因项目路径太长Windows系统不支持,项目目录取简称 lt |
||||
|
|
||||
|
## 模块说明 |
||||
|
|
||||
|
### 基础模块 |
||||
|
|
||||
|
* [LINGYUN.Abp.Localization.Dynamic](../common/LINGYUN.Abp.Localization.Dynamic/LINGYUN.Abp.Localization.Dynamic) 本地化扩展模块,增加 DynamicLocalizationResourceContributor 通过 ILocalizationStore 接口获取动态的本地化资源信息 |
||||
|
* [LINGYUN.Abp.LocalizationManagement.Domain.Shared](./LINGYUN.Abp.LocalizationManagement.Domain.Shared) 领域层公共模块,定义了错误代码、本地化、模块设置 |
||||
|
* [LINGYUN.Abp.LocalizationManagement.Domain](./LINGYUN.Abp.LocalizationManagement.Domain) 领域层模块,实现 ILocalizationStore 接口 |
||||
|
* [LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore](./LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore) 数据访问层模块,集成EfCore |
||||
|
* [LINGYUN.Abp.LocalizationManagement.Application.Contracts](./LINGYUN.Abp.LocalizationManagement.Application.Contracts) 应用服务层公共模块,定义了管理本地化对象的外部接口、权限、功能限制策略 |
||||
|
* [LINGYUN.Abp.LocalizationManagement.Application](./LINGYUN.Abp.LocalizationManagement.Application) 应用服务层实现,实现了本地化对象管理接口 |
||||
|
* [LINGYUN.Abp.LocalizationManagement.HttpApi](./LINGYUN.Abp.LocalizationManagement.HttpApi) RestApi实现,实现了独立的对外RestApi接口 |
||||
|
|
||||
|
### 高阶模块 |
||||
|
|
||||
|
### 权限定义 |
||||
|
|
||||
|
* LocalizationManagement.Resource 授权对象是否允许访问资源 |
||||
|
* LocalizationManagement.Resource.Create 授权对象是否允许创建资源 |
||||
|
* LocalizationManagement.Resource.Update 授权对象是否允许修改资源 |
||||
|
* LocalizationManagement.Resource.Delete 授权对象是否允许删除资源 |
||||
|
* LocalizationManagement.Language 授权对象是否允许访问语言 |
||||
|
* LocalizationManagement.Language.Create 授权对象是否允许创建语言 |
||||
|
* LocalizationManagement.Language.Update 授权对象是否允许修改语言 |
||||
|
* LocalizationManagement.Language.Delete 授权对象是否允许删除语言 |
||||
|
* LocalizationManagement.Text 授权对象是否允许访问文档 |
||||
|
* LocalizationManagement.Text.Create 授权对象是否允许创建文档 |
||||
|
* LocalizationManagement.Text.Update 授权对象是否允许删除Oss对象 |
||||
|
* LocalizationManagement.Text.Delete 授权对象是否允许下载Oss对象 |
||||
|
|
||||
|
### 功能定义 |
||||
|
|
||||
|
### 配置定义 |
||||
|
|
||||
|
## 更新日志 |
||||
|
|
||||
Binary file not shown.
@ -0,0 +1,268 @@ |
|||||
|
using DotNetCore.CAP; |
||||
|
using LINGYUN.Abp.EventBus.CAP; |
||||
|
using LINGYUN.Abp.ExceptionHandling; |
||||
|
using LINGYUN.Abp.ExceptionHandling.Emailing; |
||||
|
using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
using LINGYUN.Abp.MultiTenancy.DbFinder; |
||||
|
using Localization.Resources.AbpUi; |
||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.DataProtection; |
||||
|
using Microsoft.Extensions.Caching.StackExchangeRedis; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.OpenApi.Models; |
||||
|
using StackExchange.Redis; |
||||
|
using System; |
||||
|
using System.Text; |
||||
|
using System.Text.Encodings.Web; |
||||
|
using System.Text.Unicode; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.AspNetCore.Authentication.JwtBearer; |
||||
|
using Volo.Abp.AspNetCore.MultiTenancy; |
||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.Caching.StackExchangeRedis; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore.MySQL; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.Json.SystemTextJson; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.PermissionManagement.EntityFrameworkCore; |
||||
|
using Volo.Abp.Security.Encryption; |
||||
|
using Volo.Abp.SettingManagement.EntityFrameworkCore; |
||||
|
using Volo.Abp.TenantManagement.EntityFrameworkCore; |
||||
|
using Volo.Abp.Threading; |
||||
|
using Volo.Abp.Validation.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpLocalizationManagementApplicationModule), |
||||
|
typeof(AbpLocalizationManagementHttpApiModule), |
||||
|
typeof(AbpLocalizationManagementEntityFrameworkCoreModule), |
||||
|
typeof(AbpAspNetCoreMultiTenancyModule), |
||||
|
typeof(AbpEntityFrameworkCoreMySQLModule), |
||||
|
typeof(AbpTenantManagementEntityFrameworkCoreModule), |
||||
|
typeof(AbpSettingManagementEntityFrameworkCoreModule), |
||||
|
typeof(AbpPermissionManagementEntityFrameworkCoreModule), |
||||
|
typeof(AbpAspNetCoreAuthenticationJwtBearerModule), |
||||
|
typeof(AbpEmailingExceptionHandlingModule), |
||||
|
typeof(AbpCAPEventBusModule), |
||||
|
typeof(AbpDbFinderMultiTenancyModule), |
||||
|
typeof(AbpCachingStackExchangeRedisModule), |
||||
|
typeof(AbpAutofacModule) |
||||
|
)] |
||||
|
public class AbpLocalizationManagementHttpApiHostModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var configuration = context.Services.GetConfiguration(); |
||||
|
|
||||
|
PreConfigure<CapOptions>(options => |
||||
|
{ |
||||
|
options |
||||
|
.UseMySql(configuration.GetConnectionString("Default")) |
||||
|
.UseRabbitMQ(rabbitMQOptions => |
||||
|
{ |
||||
|
configuration.GetSection("CAP:RabbitMQ").Bind(rabbitMQOptions); |
||||
|
}) |
||||
|
.UseDashboard(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
||||
|
var configuration = context.Services.GetConfiguration(); |
||||
|
|
||||
|
// 配置Ef
|
||||
|
Configure<AbpDbContextOptions>(options => |
||||
|
{ |
||||
|
options.UseMySQL(); |
||||
|
}); |
||||
|
|
||||
|
// 解决某些不支持类型的序列化
|
||||
|
Configure<AbpJsonOptions>(options => |
||||
|
{ |
||||
|
options.UseHybridSerializer = true; |
||||
|
}); |
||||
|
// 中文序列化的编码问题
|
||||
|
Configure<AbpSystemTextJsonSerializerOptions>(options => |
||||
|
{ |
||||
|
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); |
||||
|
}); |
||||
|
|
||||
|
// 加解密
|
||||
|
Configure<AbpStringEncryptionOptions>(options => |
||||
|
{ |
||||
|
var encryptionConfiguration = configuration.GetSection("Encryption"); |
||||
|
if (encryptionConfiguration.Exists()) |
||||
|
{ |
||||
|
options.DefaultPassPhrase = encryptionConfiguration["PassPhrase"] ?? options.DefaultPassPhrase; |
||||
|
options.DefaultSalt = encryptionConfiguration.GetSection("Salt").Exists() |
||||
|
? Encoding.ASCII.GetBytes(encryptionConfiguration["Salt"]) |
||||
|
: options.DefaultSalt; |
||||
|
options.InitVectorBytes = encryptionConfiguration.GetSection("InitVector").Exists() |
||||
|
? Encoding.ASCII.GetBytes(encryptionConfiguration["InitVector"]) |
||||
|
: options.InitVectorBytes; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 自定义需要处理的异常
|
||||
|
Configure<AbpExceptionHandlingOptions>(options => |
||||
|
{ |
||||
|
// 加入需要处理的异常类型
|
||||
|
options.Handlers.Add<Volo.Abp.Data.AbpDbConcurrencyException>(); |
||||
|
options.Handlers.Add<AbpInitializationException>(); |
||||
|
options.Handlers.Add<ObjectDisposedException>(); |
||||
|
options.Handlers.Add<StackOverflowException>(); |
||||
|
options.Handlers.Add<OutOfMemoryException>(); |
||||
|
options.Handlers.Add<System.Data.Common.DbException>(); |
||||
|
options.Handlers.Add<Microsoft.EntityFrameworkCore.DbUpdateException>(); |
||||
|
options.Handlers.Add<System.Data.DBConcurrencyException>(); |
||||
|
}); |
||||
|
// 自定义需要发送邮件通知的异常类型
|
||||
|
Configure<AbpEmailExceptionHandlingOptions>(options => |
||||
|
{ |
||||
|
// 是否发送堆栈信息
|
||||
|
options.SendStackTrace = true; |
||||
|
// 未指定异常接收者的默认接收邮件
|
||||
|
// 指定自己的邮件地址
|
||||
|
// options.DefaultReceiveEmail = "colin.in@foxmail.com";
|
||||
|
}); |
||||
|
|
||||
|
Configure<AbpDistributedCacheOptions>(options => |
||||
|
{ |
||||
|
// 最好统一命名,不然某个缓存变动其他应用服务有例外发生
|
||||
|
options.KeyPrefix = "LINGYUN.Abp.Application"; |
||||
|
// 滑动过期30天
|
||||
|
options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30); |
||||
|
// 绝对过期60天
|
||||
|
options.GlobalCacheEntryOptions.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); |
||||
|
}); |
||||
|
|
||||
|
Configure<RedisCacheOptions>(options => |
||||
|
{ |
||||
|
var redisConfig = ConfigurationOptions.Parse(options.Configuration); |
||||
|
options.ConfigurationOptions = redisConfig; |
||||
|
options.InstanceName = configuration["Redis:InstanceName"]; |
||||
|
}); |
||||
|
|
||||
|
// 可以禁用多租户
|
||||
|
Configure<AbpMultiTenancyOptions>(options => |
||||
|
{ |
||||
|
options.IsEnabled = false; |
||||
|
}); |
||||
|
|
||||
|
// 添加动态本地化组件
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources.AddDynamic(); |
||||
|
}); |
||||
|
|
||||
|
// Swagger
|
||||
|
context.Services.AddSwaggerGen( |
||||
|
options => |
||||
|
{ |
||||
|
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Localization Management API", Version = "v1" }); |
||||
|
options.DocInclusionPredicate((docName, description) => true); |
||||
|
options.CustomSchemaIds(type => type.FullName); |
||||
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme |
||||
|
{ |
||||
|
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", |
||||
|
Name = "Authorization", |
||||
|
In = ParameterLocation.Header, |
||||
|
Scheme = "bearer", |
||||
|
Type = SecuritySchemeType.Http, |
||||
|
BearerFormat = "JWT" |
||||
|
}); |
||||
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement |
||||
|
{ |
||||
|
{ |
||||
|
new OpenApiSecurityScheme |
||||
|
{ |
||||
|
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } |
||||
|
}, |
||||
|
new string[] { } |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// 默认支持的本地化语言类型
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Languages.Add(new LanguageInfo("en", "en", "English")); |
||||
|
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); |
||||
|
}); |
||||
|
|
||||
|
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) |
||||
|
.AddJwtBearer(options => |
||||
|
{ |
||||
|
options.Authority = configuration["AuthServer:Authority"]; |
||||
|
options.RequireHttpsMetadata = false; |
||||
|
options.Audience = configuration["AuthServer:ApiName"]; |
||||
|
}); |
||||
|
|
||||
|
if (!hostingEnvironment.IsDevelopment()) |
||||
|
{ |
||||
|
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); |
||||
|
context.Services |
||||
|
.AddDataProtection() |
||||
|
.PersistKeysToStackExchangeRedis(redis, "Localization-Management-Protection-Keys"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
|
||||
|
//{
|
||||
|
// // 注释代码取消启动时缓存本地化信息
|
||||
|
// var initialize = context.ServiceProvider.GetRequiredService<ILocalizationCacheInitialize>();
|
||||
|
// AsyncHelper.RunSync(async () => await initialize.InitializeAsync());
|
||||
|
//}
|
||||
|
|
||||
|
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
var app = context.GetApplicationBuilder(); |
||||
|
var env = context.GetEnvironment(); |
||||
|
|
||||
|
// http调用链
|
||||
|
app.UseCorrelationId(); |
||||
|
// 虚拟文件系统
|
||||
|
app.UseVirtualFiles(); |
||||
|
// 本地化
|
||||
|
app.UseAbpRequestLocalization(); |
||||
|
// 多租户
|
||||
|
// app.UseMultiTenancy();
|
||||
|
//路由
|
||||
|
app.UseRouting(); |
||||
|
// 认证
|
||||
|
app.UseAuthentication(); |
||||
|
// jwt
|
||||
|
app.UseJwtTokenMiddleware(); |
||||
|
// 授权
|
||||
|
app.UseAuthorization(); |
||||
|
// Swagger
|
||||
|
app.UseSwagger(); |
||||
|
// Swagger可视化界面
|
||||
|
app.UseSwaggerUI(options => |
||||
|
{ |
||||
|
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support Localization Management API"); |
||||
|
}); |
||||
|
// 审计日志
|
||||
|
app.UseAuditing(); |
||||
|
// 路由
|
||||
|
app.UseConfiguredEndpoints(); |
||||
|
|
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
AsyncHelper.RunSync(async () => |
||||
|
await app.ApplicationServices.GetRequiredService<IDataSeeder>() |
||||
|
.SeedAsync()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
FROM mcr.microsoft.com/dotnet/aspnet:5.0 |
||||
|
LABEL maintainer="colin.in@foxmail.com" |
||||
|
WORKDIR /app |
||||
|
|
||||
|
COPY . /app |
||||
|
|
||||
|
ENV TZ=Asia/Shanghai |
||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone |
||||
|
|
||||
|
EXPOSE 80/tcp |
||||
|
VOLUME [ "./app/Logs" ] |
||||
|
|
||||
|
ENTRYPOINT ["dotnet", "LINGYUN.Abp.LocalizationManagement.HttpApi.Host.dll"] |
||||
@ -0,0 +1,22 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class LocalizationManagementHttpApiHostMigrationsDbContext : AbpDbContext<LocalizationManagementHttpApiHostMigrationsDbContext> |
||||
|
{ |
||||
|
public LocalizationManagementHttpApiHostMigrationsDbContext( |
||||
|
DbContextOptions<LocalizationManagementHttpApiHostMigrationsDbContext> options) |
||||
|
: base(options) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
base.OnModelCreating(modelBuilder); |
||||
|
|
||||
|
modelBuilder.ConfigureLocalization(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Design; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using System.IO; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class LocalizationManagementHttpApiHostMigrationsDbContextFactory : IDesignTimeDbContextFactory<LocalizationManagementHttpApiHostMigrationsDbContext> |
||||
|
{ |
||||
|
public LocalizationManagementHttpApiHostMigrationsDbContext CreateDbContext(string[] args) |
||||
|
{ |
||||
|
var configuration = BuildConfiguration(); |
||||
|
var connectionString = configuration.GetConnectionString("Default"); |
||||
|
|
||||
|
var builder = new DbContextOptionsBuilder<LocalizationManagementHttpApiHostMigrationsDbContext>() |
||||
|
.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); |
||||
|
|
||||
|
return new LocalizationManagementHttpApiHostMigrationsDbContext(builder.Options); |
||||
|
} |
||||
|
|
||||
|
private static IConfigurationRoot BuildConfiguration() |
||||
|
{ |
||||
|
var builder = new ConfigurationBuilder() |
||||
|
.SetBasePath(Directory.GetCurrentDirectory()) |
||||
|
.AddJsonFile("appsettings.Development.json", optional: false); |
||||
|
|
||||
|
return builder.Build(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue