94 changed files with 2631 additions and 20 deletions
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,38 @@
|
||||
# Sanhe.Abp.Localization.Dynamic |
||||
|
||||
动态本地化提供者组件,添加动态提供者可实现运行时替换本地化文本 |
||||
|
||||
需要实现 ILocalizationStore 接口 |
||||
|
||||
LocalizationManagement项目提供支持 |
||||
|
||||
## 配置使用 |
||||
|
||||
```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)); |
||||
}); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
## 注意事项 |
||||
|
||||
动态资源在启动时加载,如果通过LocalizationManagement模块查询,可能受后端存储资源体量影响整体启动时间 |
||||
|
||||
详情见: [DynamicLocalizationInitializeService](./Sanhe/Abp/Localization/Dynamic/DynamicLocalizationInitializeService.cs#L25-L38) |
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,17 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Volo.Abp.EventBus; |
||||
using Volo.Abp.Localization; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
[DependsOn( |
||||
typeof(AbpEventBusModule), |
||||
typeof(AbpLocalizationModule))] |
||||
public class AbpLocalizationDynamicModule : AbpModule |
||||
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
||||
context.Services.AddHostedService<DynamicLocalizationInitializeService>(); |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
public class AbpLocalizationDynamicOptions |
||||
{ |
||||
internal LocalizationDictionary LocalizationDictionary { get; } |
||||
|
||||
public AbpLocalizationDynamicOptions() |
||||
{ |
||||
LocalizationDictionary = new LocalizationDictionary(); |
||||
} |
||||
|
||||
internal void AddOrUpdate(string resourceName, Dictionary<string, ILocalizationDictionary> dictionaries) |
||||
{ |
||||
var currentDictionaries = LocalizationDictionary |
||||
.GetOrAdd(resourceName, () => new Dictionary<string, ILocalizationDictionary>()); |
||||
|
||||
currentDictionaries.AddIfNotContains(dictionaries); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.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,41 @@
|
||||
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 Sanhe.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 async virtual Task<IReadOnlyList<LanguageInfo>> GetLanguagesAsync() |
||||
{ |
||||
var languages = await Store.GetLanguageListAsync(); |
||||
|
||||
if (!languages.Any()) |
||||
{ |
||||
return Options.Languages; |
||||
} |
||||
|
||||
return languages |
||||
.Distinct(new LanguageInfoComparer()) |
||||
.ToList(); |
||||
} |
||||
} |
@ -0,0 +1,47 @@
|
||||
using Microsoft.Extensions.Hosting; |
||||
using Microsoft.Extensions.Options; |
||||
using System; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
public class DynamicLocalizationInitializeService : BackgroundService |
||||
{ |
||||
protected ILocalizationStore Store { get; } |
||||
protected AbpLocalizationOptions LocalizationOptions { get; } |
||||
protected AbpLocalizationDynamicOptions DynamicOptions { get; } |
||||
|
||||
public DynamicLocalizationInitializeService( |
||||
ILocalizationStore store, |
||||
IOptions<AbpLocalizationOptions> localizationOptions, |
||||
IOptions<AbpLocalizationDynamicOptions> dynamicOptions) |
||||
{ |
||||
Store = store; |
||||
DynamicOptions = dynamicOptions.Value; |
||||
LocalizationOptions = localizationOptions.Value; |
||||
} |
||||
|
||||
protected async override Task ExecuteAsync(CancellationToken stoppingToken) |
||||
{ |
||||
try |
||||
{ |
||||
foreach (var resource in LocalizationOptions.Resources) |
||||
{ |
||||
foreach (var contributor in resource.Value.Contributors) |
||||
{ |
||||
if (contributor.GetType().IsAssignableFrom(typeof(DynamicLocalizationResourceContributor))) |
||||
{ |
||||
var resourceLocalizationDict = await Store |
||||
.GetLocalizationDictionaryAsync( |
||||
resource.Value.ResourceName, |
||||
stoppingToken); |
||||
DynamicOptions.AddOrUpdate(resource.Value.ResourceName, resourceLocalizationDict); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (OperationCanceledException) { } // 忽略此异常 |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Localization; |
||||
using Microsoft.Extensions.Options; |
||||
using System.Collections.Generic; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
public class DynamicLocalizationResourceContributor : ILocalizationResourceContributor |
||||
{ |
||||
private readonly string _resourceName; |
||||
|
||||
private AbpLocalizationDynamicOptions _options; |
||||
|
||||
public DynamicLocalizationResourceContributor(string resourceName) |
||||
{ |
||||
_resourceName = resourceName; |
||||
} |
||||
|
||||
public virtual void Initialize(LocalizationResourceInitializationContext context) |
||||
{ |
||||
_options = context.ServiceProvider.GetService<IOptions<AbpLocalizationDynamicOptions>>().Value; |
||||
} |
||||
|
||||
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() |
||||
{ |
||||
return _options.LocalizationDictionary |
||||
.GetOrAdd(_resourceName, () => new Dictionary<string, ILocalizationDictionary>()); |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.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,22 @@
|
||||
using System.Collections.Generic; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.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,58 @@
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
/// <summary> |
||||
/// 本地化缓存项 |
||||
/// </summary> |
||||
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,12 @@
|
||||
using System.Collections.Generic; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
/// <summary> |
||||
/// 用于查找本地化字符串的字典。 |
||||
/// </summary> |
||||
public class LocalizationDictionary : Dictionary<string, Dictionary<string, ILocalizationDictionary>> |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,75 @@
|
||||
using Microsoft.Extensions.Localization; |
||||
using Microsoft.Extensions.Options; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.EventBus.Distributed; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.Localization.Dynamic |
||||
{ |
||||
internal class LocalizationResetSynchronizer : |
||||
IDistributedEventHandler<LocalizedStringCacheResetEventData>, |
||||
ITransientDependency |
||||
{ |
||||
private readonly AbpLocalizationDynamicOptions _options; |
||||
|
||||
public LocalizationResetSynchronizer( |
||||
IOptions<AbpLocalizationDynamicOptions> options) |
||||
{ |
||||
_options = options.Value; |
||||
} |
||||
|
||||
public virtual Task HandleEventAsync(LocalizedStringCacheResetEventData eventData) |
||||
{ |
||||
var dictionaries = GetDictionaries(eventData.ResourceName); |
||||
|
||||
if (!dictionaries.ContainsKey(eventData.CultureName)) |
||||
{ |
||||
// TODO: 需要处理 data.Key data.Value 空引用 |
||||
var dictionary = new Dictionary<string, LocalizedString>(); |
||||
dictionary.Add(eventData.Key, new LocalizedString(eventData.Key, eventData.Value.NormalizeLineEndings())); |
||||
|
||||
var newLocalizationDictionary = new StaticLocalizationDictionary(eventData.CultureName, dictionary); |
||||
|
||||
dictionaries.Add(eventData.CultureName, newLocalizationDictionary); |
||||
} |
||||
else |
||||
{ |
||||
// 取出当前的缓存写入到新字典进行处理 |
||||
var nowLocalizationDictionary = dictionaries[eventData.CultureName]; |
||||
var dictionary = new Dictionary<string, LocalizedString>(); |
||||
nowLocalizationDictionary.Fill(dictionary); |
||||
|
||||
var existsKey = dictionary.ContainsKey(eventData.Key); |
||||
if (!existsKey) |
||||
{ |
||||
// 如果不存在,则新增 |
||||
dictionary.Add(eventData.Key, new LocalizedString(eventData.Key, eventData.Value.NormalizeLineEndings())); |
||||
} |
||||
else if (existsKey && eventData.IsDeleted) |
||||
{ |
||||
// 如果删掉了本地化的节点,删掉当前的缓存 |
||||
dictionary.Remove(eventData.Key); |
||||
} |
||||
|
||||
var newLocalizationDictionary = new StaticLocalizationDictionary(eventData.CultureName, dictionary); |
||||
|
||||
if (newLocalizationDictionary != null) |
||||
{ |
||||
// 重新赋值变更过的缓存 |
||||
dictionaries[eventData.CultureName] = newLocalizationDictionary; |
||||
} |
||||
} |
||||
|
||||
return Task.CompletedTask; |
||||
} |
||||
|
||||
protected virtual Dictionary<string, ILocalizationDictionary> GetDictionaries(string resourceName) |
||||
{ |
||||
return _options.LocalizationDictionary |
||||
.GetOrAdd(resourceName, () => new Dictionary<string, ILocalizationDictionary>()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,44 @@
|
||||
namespace Sanhe.Abp.Localization.Dynamic; |
||||
|
||||
public class LocalizedStringCacheResetEventData |
||||
{ |
||||
/// <summary> |
||||
/// 是否删除 |
||||
/// </summary> |
||||
public bool IsDeleted { get; set; } |
||||
/// <summary> |
||||
/// 资源名 |
||||
/// </summary> |
||||
public string ResourceName { get; set; } |
||||
/// <summary> |
||||
/// 文化名称 |
||||
/// </summary> |
||||
public string CultureName { get; set; } |
||||
/// <summary> |
||||
/// 键 |
||||
/// </summary> |
||||
public string Key { get; set; } |
||||
/// <summary> |
||||
/// 值 |
||||
/// </summary> |
||||
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,42 @@
|
||||
using Sanhe.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 Sanhe.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,39 @@
|
||||
# Localization Management |
||||
|
||||
本地化文档管理模块,因项目路径太长Windows系统不支持,项目目录取简称 lt |
||||
|
||||
## 模块说明 |
||||
|
||||
### 基础模块 |
||||
|
||||
* [Sanhe.Abp.Localization.Dynamic](../common/Sanhe.Abp.Localization.Dynamic/Sanhe.Abp.Localization.Dynamic) 本地化扩展模块,增加 DynamicLocalizationResourceContributor 通过 ILocalizationStore 接口获取动态的本地化资源信息 |
||||
* [Sanhe.Abp.LocalizationManagement.Domain.Shared](./Sanhe.Abp.LocalizationManagement.Domain.Shared) 领域层公共模块,定义了错误代码、本地化、模块设置 |
||||
* [Sanhe.Abp.LocalizationManagement.Domain](./Sanhe.Abp.LocalizationManagement.Domain) 领域层模块,实现 ILocalizationStore 接口 |
||||
* [Sanhe.Abp.LocalizationManagement.EntityFrameworkCore](./Sanhe.Abp.LocalizationManagement.EntityFrameworkCore) 数据访问层模块,集成EfCore |
||||
* [Sanhe.Abp.LocalizationManagement.Application.Contracts](./Sanhe.Abp.LocalizationManagement.Application.Contracts) 应用服务层公共模块,定义了管理本地化对象的外部接口、权限、功能限制策略 |
||||
* [Sanhe.Abp.LocalizationManagement.Application](./Sanhe.Abp.LocalizationManagement.Application) 应用服务层实现,实现了本地化对象管理接口 |
||||
* [Sanhe.Abp.LocalizationManagement.HttpApi](./Sanhe.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对象 |
||||
|
||||
### 功能定义 |
||||
|
||||
### 配置定义 |
||||
|
||||
## 更新日志 |
||||
|
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Domain.Shared\Sanhe.Abp.LocalizationManagement.Domain.Shared.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,12 @@
|
||||
using Volo.Abp.Authorization; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
[DependsOn( |
||||
typeof(AbpAuthorizationModule), |
||||
typeof(AbpLocalizationManagementDomainSharedModule))] |
||||
public class AbpLocalizationManagementApplicationContractsModule : AbpModule |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations; |
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.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,19 @@
|
||||
using System.ComponentModel.DataAnnotations; |
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.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,9 @@
|
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class CreateOrUpdateTextInput |
||||
{ |
||||
[DynamicStringLength(typeof(TextConsts), nameof(TextConsts.MaxValueLength))] |
||||
public string Value { get; set; } |
||||
} |
@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations; |
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.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,8 @@
|
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class GetLanguagesInput : PagedAndSortedResultRequestDto |
||||
{ |
||||
public string Filter { get; set; } |
||||
} |
@ -0,0 +1,8 @@
|
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class GetResourcesInput : PagedAndSortedResultRequestDto |
||||
{ |
||||
public string Filter { get; set; } |
||||
} |
@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations; |
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.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,23 @@
|
||||
using System.ComponentModel.DataAnnotations; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.Validation; |
||||
|
||||
namespace Sanhe.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,17 @@
|
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.Application.Services; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public interface ILanguageAppService : |
||||
ICrudAppService< |
||||
LanguageDto, |
||||
Guid, |
||||
GetLanguagesInput, |
||||
CreateOrUpdateLanguageInput, |
||||
CreateOrUpdateLanguageInput> |
||||
{ |
||||
Task<ListResultDto<LanguageDto>> GetAllAsync(); |
||||
} |
@ -0,0 +1,17 @@
|
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.Application.Services; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public interface IResourceAppService : |
||||
ICrudAppService< |
||||
ResourceDto, |
||||
Guid, |
||||
GetResourcesInput, |
||||
CreateOrUpdateResourceInput, |
||||
CreateOrUpdateResourceInput> |
||||
{ |
||||
Task<ListResultDto<ResourceDto>> GetAllAsync(); |
||||
} |
@ -0,0 +1,16 @@
|
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Application.Services; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public interface ITextAppService : |
||||
ICrudAppService< |
||||
TextDto, |
||||
TextDifferenceDto, |
||||
int, |
||||
GetTextsInput, |
||||
CreateTextInput, |
||||
UpdateTextInput> |
||||
{ |
||||
Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input); |
||||
} |
@ -0,0 +1,13 @@
|
||||
using System; |
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.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,6 @@
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public static class LocalizationRemoteServiceConsts |
||||
{ |
||||
public const string RemoteServiceName = "LocalizationManagement"; |
||||
} |
@ -0,0 +1,72 @@
|
||||
using Sanhe.Abp.LocalizationManagement.Localization; |
||||
using Volo.Abp.Authorization.Permissions; |
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.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,39 @@
|
||||
namespace Sanhe.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,12 @@
|
||||
using System; |
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.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,13 @@
|
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.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,11 @@
|
||||
using Volo.Abp.Application.Dtos; |
||||
|
||||
namespace Sanhe.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,5 @@
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class UpdateTextInput : CreateOrUpdateTextInput |
||||
{ |
||||
} |
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Domain\Sanhe.Abp.LocalizationManagement.Domain.csproj" /> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Application.Contracts\Sanhe.Abp.LocalizationManagement.Application.Contracts.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,23 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Volo.Abp.Application; |
||||
using Volo.Abp.AutoMapper; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.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,70 @@
|
||||
using Sanhe.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 Sanhe.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 async virtual 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 async override 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,14 @@
|
||||
using AutoMapper; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class LocalizationManagementApplicationMapperProfile : Profile |
||||
{ |
||||
public LocalizationManagementApplicationMapperProfile() |
||||
{ |
||||
CreateMap<Language, LanguageDto>(); |
||||
CreateMap<Resource, ResourceDto>(); |
||||
CreateMap<Text, TextDto>(); |
||||
CreateMap<TextDifference, TextDifferenceDto>(); |
||||
} |
||||
} |
@ -0,0 +1,75 @@
|
||||
using Sanhe.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 Sanhe.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 async virtual 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 async override 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,71 @@
|
||||
using Sanhe.Abp.LocalizationManagement.Permissions; |
||||
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.Application.Services; |
||||
|
||||
namespace Sanhe.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 async virtual Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input) |
||||
{ |
||||
await CheckGetPolicyAsync(); |
||||
|
||||
var text = await _textRepository.GetByCultureKeyAsync( |
||||
input.ResourceName, input.CultureName, input.Key); |
||||
|
||||
return await MapToGetOutputDtoAsync(text); |
||||
} |
||||
|
||||
public async override 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,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<None Remove="Sanhe\Abp\LocalizationManagement\Localization\Resources\en.json" /> |
||||
<None Remove="Sanhe\Abp\LocalizationManagement\Localization\Resources\zh-Hans.json" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<EmbeddedResource Include="Sanhe\Abp\LocalizationManagement\Localization\Resources\en.json" /> |
||||
<EmbeddedResource Include="Sanhe\Abp\LocalizationManagement\Localization\Resources\zh-Hans.json" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Validation" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,29 @@
|
||||
using Sanhe.Abp.LocalizationManagement.Localization; |
||||
using Volo.Abp.Localization; |
||||
using Volo.Abp.Modularity; |
||||
using Volo.Abp.Validation; |
||||
using Volo.Abp.VirtualFileSystem; |
||||
|
||||
namespace Sanhe.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("/Sanhe/Abp/LocalizationManagement/Localization/Resources"); |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
namespace Sanhe.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,8 @@
|
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement.Localization; |
||||
|
||||
[LocalizationResourceName("LocalizationManagement")] |
||||
public class LocalizationManagementResource |
||||
{ |
||||
} |
@ -0,0 +1,45 @@
|
||||
{ |
||||
"culture": "en", |
||||
"texts": { |
||||
"Languages": "Languages", |
||||
"Resources": "Resources", |
||||
"Texts": "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,45 @@
|
||||
{ |
||||
"culture": "zh-Hans", |
||||
"texts": { |
||||
"Languages": "语言", |
||||
"Resources": "资源", |
||||
"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 Sanhe.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 Sanhe.Abp.LocalizationManagement |
||||
{ |
||||
public static class TextConsts |
||||
{ |
||||
public static int MaxKeyLength { get; set; } = 512; |
||||
public static int MaxValueLength { get; set; } = 2 * 1024; |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
namespace Sanhe.Abp.LocalizationManagement |
||||
{ |
||||
/// <summary> |
||||
/// 文本差异 |
||||
/// </summary> |
||||
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,22 @@
|
||||
namespace Sanhe.Abp.LocalizationManagement |
||||
{ |
||||
public class TextEto |
||||
{ |
||||
/// <summary> |
||||
/// 文化名称 |
||||
/// </summary> |
||||
public string CultureName { get; set; } |
||||
/// <summary> |
||||
/// Key |
||||
/// </summary> |
||||
public string Key { get; set; } |
||||
/// <summary> |
||||
/// Value |
||||
/// </summary> |
||||
public string Value { get; set; } |
||||
/// <summary> |
||||
/// 资源名称 |
||||
/// </summary> |
||||
public string ResourceName { get; set; } |
||||
} |
||||
} |
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\..\common\Sanhe.Abp.Localization.Dynamic\Sanhe.Abp.Localization.Dynamic.csproj" /> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Domain.Shared\Sanhe.Abp.LocalizationManagement.Domain.Shared.csproj" /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,41 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Sanhe.Abp.Localization.Dynamic; |
||||
using Sanhe.Abp.LocalizationManagement.Localization; |
||||
using Volo.Abp.AutoMapper; |
||||
using Volo.Abp.Domain; |
||||
using Volo.Abp.Localization; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.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,14 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Domain.Repositories; |
||||
|
||||
namespace Sanhe.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,17 @@
|
||||
using System; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Domain.Repositories; |
||||
|
||||
namespace Sanhe.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,43 @@
|
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Domain.Repositories; |
||||
|
||||
namespace Sanhe.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; |
||||
|
||||
namespace Sanhe.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,10 @@
|
||||
namespace Sanhe.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,11 @@
|
||||
using AutoMapper; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement; |
||||
|
||||
public class LocalizationManagementDomainMapperProfile : Profile |
||||
{ |
||||
public LocalizationManagementDomainMapperProfile() |
||||
{ |
||||
CreateMap<Text, TextEto>(); |
||||
} |
||||
} |
@ -0,0 +1,85 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Localization; |
||||
using Sanhe.Abp.Localization.Dynamic; |
||||
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 Sanhe.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 async virtual 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 async virtual 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 async virtual Task<bool> ResourceExistsAsync(string resourceName, CancellationToken cancellationToken = default) |
||||
{ |
||||
return await ResourceRepository.ExistsAsync(resourceName, cancellationToken); |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
using Sanhe.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 Sanhe.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 async virtual Task HandleEventAsync(EntityCreatedEventData<Text> eventData) |
||||
{ |
||||
await HandleEventAsync(BuildResetEventData(eventData.Entity)); |
||||
} |
||||
|
||||
public async virtual Task HandleEventAsync(EntityUpdatedEventData<Text> eventData) |
||||
{ |
||||
await HandleEventAsync(BuildResetEventData(eventData.Entity)); |
||||
} |
||||
|
||||
public async virtual 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,29 @@
|
||||
using JetBrains.Annotations; |
||||
using System; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
||||
namespace Sanhe.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,36 @@
|
||||
using JetBrains.Annotations; |
||||
using System; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Domain.Entities; |
||||
|
||||
namespace Sanhe.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,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net6.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Domain\Sanhe.Abp.LocalizationManagement.Domain.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,23 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Volo.Abp.EntityFrameworkCore; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.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,32 @@
|
||||
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 Sanhe.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
||||
public class EfCoreLanguageRepository : EfCoreRepository<LocalizationDbContext, Language, Guid>, ILanguageRepository |
||||
{ |
||||
public EfCoreLanguageRepository(IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public async virtual Task<Language> FindByCultureNameAsync( |
||||
string cultureName, |
||||
CancellationToken cancellationToken = default) |
||||
{ |
||||
return await (await GetDbSetAsync()).Where(x => x.CultureName.Equals(cultureName)) |
||||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
||||
} |
||||
|
||||
public async virtual Task<List<Language>> GetActivedListAsync(CancellationToken cancellationToken = default) |
||||
{ |
||||
return await (await GetDbSetAsync()).Where(x => x.Enable) |
||||
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
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 Sanhe.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
||||
public class EfCoreResourceRepository : EfCoreRepository<LocalizationDbContext, Resource, Guid>, IResourceRepository |
||||
{ |
||||
public EfCoreResourceRepository(IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
{ |
||||
} |
||||
|
||||
public async virtual Task<bool> ExistsAsync( |
||||
string name, |
||||
CancellationToken cancellationToken = default) |
||||
{ |
||||
return await (await GetDbSetAsync()).AnyAsync(x => x.Name.Equals(name), cancellationToken: cancellationToken); |
||||
} |
||||
|
||||
public async virtual Task<Resource> FindByNameAsync( |
||||
string name, |
||||
CancellationToken cancellationToken = default) |
||||
{ |
||||
return await (await GetDbSetAsync()).Where(x => x.Name.Equals(name)) |
||||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
||||
} |
||||
} |
@ -0,0 +1,132 @@
|
||||
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 Sanhe.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
||||
public class EfCoreTextRepository : EfCoreRepository<LocalizationDbContext, Text, int>, ITextRepository |
||||
{ |
||||
public EfCoreTextRepository(IDbContextProvider<LocalizationDbContext> dbContextProvider) : base(dbContextProvider) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public async virtual 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 async virtual 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 async virtual 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 async virtual 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 async virtual 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 async virtual 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,13 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using Volo.Abp.Data; |
||||
using Volo.Abp.EntityFrameworkCore; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
||||
[ConnectionStringName(LocalizationDbProperties.ConnectionStringName)] |
||||
public interface ILocalizationDbContext : IEfCoreDbContext |
||||
{ |
||||
DbSet<Resource> Resources { get; set; } |
||||
DbSet<Language> Languages { get; set; } |
||||
DbSet<Text> Texts { get; set; } |
||||
} |
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using Volo.Abp.Data; |
||||
using Volo.Abp.EntityFrameworkCore; |
||||
|
||||
namespace Sanhe.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,98 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using System; |
||||
using Volo.Abp; |
||||
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
||||
namespace Sanhe.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,17 @@
|
||||
using JetBrains.Annotations; |
||||
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
||||
namespace Sanhe.Abp.LocalizationManagement.EntityFrameworkCore; |
||||
|
||||
public class LocalizationModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
||||
{ |
||||
public LocalizationModelBuilderConfigurationOptions( |
||||
[NotNull] string tablePrefix = "", |
||||
[CanBeNull] string schema = null) |
||||
: base( |
||||
tablePrefix, |
||||
schema) |
||||
{ |
||||
|
||||
} |
||||
} |
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
</Weavers> |
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net6.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Sanhe.Abp.LocalizationManagement.Application.Contracts\Sanhe.Abp.LocalizationManagement.Application.Contracts.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,41 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Sanhe.Abp.LocalizationManagement.Localization; |
||||
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 Sanhe.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,61 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.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 Task<LanguageDto> CreateAsync(CreateOrUpdateLanguageInput input) |
||||
{ |
||||
return _service.CreateAsync(input); |
||||
} |
||||
|
||||
[HttpDelete] |
||||
[Route("{id}")] |
||||
public virtual Task DeleteAsync(Guid id) |
||||
{ |
||||
return _service.DeleteAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("all")] |
||||
public virtual Task<ListResultDto<LanguageDto>> GetAllAsync() |
||||
{ |
||||
return _service.GetAllAsync(); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("{id}")] |
||||
public virtual Task<LanguageDto> GetAsync(Guid id) |
||||
{ |
||||
return _service.GetAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
public virtual Task<PagedResultDto<LanguageDto>> GetListAsync(GetLanguagesInput input) |
||||
{ |
||||
return _service.GetListAsync(input); |
||||
} |
||||
|
||||
[HttpPut] |
||||
[Route("{id}")] |
||||
public virtual Task<LanguageDto> UpdateAsync(Guid id, CreateOrUpdateLanguageInput input) |
||||
{ |
||||
return _service.UpdateAsync(id, input); |
||||
} |
||||
} |
@ -0,0 +1,61 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.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 Task<ResourceDto> CreateAsync(CreateOrUpdateResourceInput input) |
||||
{ |
||||
return _service.CreateAsync(input); |
||||
} |
||||
|
||||
[HttpDelete] |
||||
[Route("{id}")] |
||||
public virtual Task DeleteAsync(Guid id) |
||||
{ |
||||
return _service.DeleteAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("all")] |
||||
public virtual Task<ListResultDto<ResourceDto>> GetAllAsync() |
||||
{ |
||||
return _service.GetAllAsync(); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("{id}")] |
||||
public virtual Task<ResourceDto> GetAsync(Guid id) |
||||
{ |
||||
return _service.GetAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
public virtual Task<PagedResultDto<ResourceDto>> GetListAsync(GetResourcesInput input) |
||||
{ |
||||
return _service.GetListAsync(input); |
||||
} |
||||
|
||||
[HttpPut] |
||||
[Route("{id}")] |
||||
public virtual Task<ResourceDto> UpdateAsync(Guid id, CreateOrUpdateResourceInput input) |
||||
{ |
||||
return _service.UpdateAsync(id, input); |
||||
} |
||||
} |
@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Application.Dtos; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.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 Task<TextDto> CreateAsync(CreateTextInput input) |
||||
{ |
||||
return _service.CreateAsync(input); |
||||
} |
||||
|
||||
[HttpDelete] |
||||
[Route("{id}")] |
||||
public virtual Task DeleteAsync(int id) |
||||
{ |
||||
return _service.DeleteAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("{id}")] |
||||
public virtual Task<TextDto> GetAsync(int id) |
||||
{ |
||||
return _service.GetAsync(id); |
||||
} |
||||
|
||||
[HttpGet] |
||||
[Route("by-culture-key")] |
||||
public virtual Task<TextDto> GetByCultureKeyAsync(GetTextByKeyInput input) |
||||
{ |
||||
return _service.GetByCultureKeyAsync(input); |
||||
} |
||||
|
||||
[HttpGet] |
||||
public virtual Task<PagedResultDto<TextDifferenceDto>> GetListAsync(GetTextsInput input) |
||||
{ |
||||
return _service.GetListAsync(input); |
||||
} |
||||
|
||||
[HttpPut] |
||||
[Route("{id}")] |
||||
public virtual Task<TextDto> UpdateAsync(int id, UpdateTextInput input) |
||||
{ |
||||
return _service.UpdateAsync(id, input); |
||||
} |
||||
} |
Loading…
Reference in new issue