From 58d1c734c53a91916346ebfb23bb1ea68888530b Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Tue, 10 Jan 2023 17:24:53 +0800 Subject: [PATCH] add support static localization persistence --- aspnet-core/LINGYUN.MicroService.All.sln | 21 ++- ...ture-Name-With-Resource-Record.Designer.cs | 174 ++++++++++++++++++ ...fault-Culture-Name-With-Resource-Record.cs | 30 +++ ...agementMigrationsDbContextModelSnapshot.cs | 5 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ...INGYUN.Abp.Localization.Persistence.csproj | 16 ++ .../AbpLocalizationPersistenceModule.cs | 15 ++ .../AbpLocalizationPersistenceOptions.cs | 35 ++++ .../Persistence/IStaticLocalizationSaver.cs | 8 + .../StaticLocalizationSaverHostService.cs | 28 +++ .../README.md | 41 +++++ .../LINGYUN.Abp.Localization.csproj | 19 ++ .../LocalizationManagement/ResourceConsts.cs | 1 + ...N.Abp.LocalizationManagement.Domain.csproj | 3 +- .../AbpLocalizationManagementDomainModule.cs | 16 +- .../LocalizationManagement/ITextRepository.cs | 6 + ...calizationManagementExternalContributor.cs | 92 +++++++++ .../Abp/LocalizationManagement/Resource.cs | 11 +- .../StaticLocalizationSaver.cs | 124 +++++++++++++ .../EfCoreTextRepository.cs | 15 ++ ...lizationDbContextModelBuilderExtensions.cs | 3 + ...onManagementHttpApiHostModule.Configure.cs | 2 +- .../appsettings.Development.json | 12 +- 24 files changed, 690 insertions(+), 20 deletions(-) create mode 100644 aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.Designer.cs create mode 100644 aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.cs create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/IStaticLocalizationSaver.cs create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/README.md create mode 100644 aspnet-core/modules/localization/LINGYUN.Abp.Localization/LINGYUN.Abp.Localization.csproj create mode 100644 aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs create mode 100644 aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index 834a0c4bb..1be7d20b1 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/aspnet-core/LINGYUN.MicroService.All.sln @@ -521,7 +521,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "platform", "platform", "{F2 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "realtime-message", "realtime-message", "{B1AC656F-8F4C-43D5-B5A0-CCF5F119EA44}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LY.MicroService.BackendAdmin.DbMigrator", "migrations\LY.MicroService.BackendAdmin.DbMigrator\LY.MicroService.BackendAdmin.DbMigrator.csproj", "{EA144C64-CE14-40DF-A876-5D302A4FC208}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.BackendAdmin.DbMigrator", "migrations\LY.MicroService.BackendAdmin.DbMigrator\LY.MicroService.BackendAdmin.DbMigrator.csproj", "{EA144C64-CE14-40DF-A876-5D302A4FC208}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.AuthServer.DbMigrator", "migrations\LY.MicroService.AuthServer.DbMigrator\LY.MicroService.AuthServer.DbMigrator.csproj", "{D25156E6-532F-4BCC-8EE0-8A3F1986E10E}" EndProject @@ -535,17 +535,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.RealtimeMes EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "notifications", "notifications", "{1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Domain.Shared", "modules\notifications\LINGYUN.Abp.Notifications.Domain.Shared\LINGYUN.Abp.Notifications.Domain.Shared.csproj", "{4CF6F78C-22BE-46C4-BC9B-824E8FEEF719}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Domain.Shared", "modules\notifications\LINGYUN.Abp.Notifications.Domain.Shared\LINGYUN.Abp.Notifications.Domain.Shared.csproj", "{4CF6F78C-22BE-46C4-BC9B-824E8FEEF719}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Domain", "modules\notifications\LINGYUN.Abp.Notifications.Domain\LINGYUN.Abp.Notifications.Domain.csproj", "{CDD4CC0F-5493-40C2-B889-3670263888D8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Domain", "modules\notifications\LINGYUN.Abp.Notifications.Domain\LINGYUN.Abp.Notifications.Domain.csproj", "{CDD4CC0F-5493-40C2-B889-3670263888D8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.EntityFrameworkCore", "modules\notifications\LINGYUN.Abp.Notifications.EntityFrameworkCore\LINGYUN.Abp.Notifications.EntityFrameworkCore.csproj", "{F74AAE01-7B87-44CA-85ED-9A6307D51504}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.EntityFrameworkCore", "modules\notifications\LINGYUN.Abp.Notifications.EntityFrameworkCore\LINGYUN.Abp.Notifications.EntityFrameworkCore.csproj", "{F74AAE01-7B87-44CA-85ED-9A6307D51504}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Application.Contracts", "modules\notifications\LINGYUN.Abp.Notifications.Application.Contracts\LINGYUN.Abp.Notifications.Application.Contracts.csproj", "{E3F010C7-6C96-4EEA-B5FA-BF80515E195F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Application.Contracts", "modules\notifications\LINGYUN.Abp.Notifications.Application.Contracts\LINGYUN.Abp.Notifications.Application.Contracts.csproj", "{E3F010C7-6C96-4EEA-B5FA-BF80515E195F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Application", "modules\notifications\LINGYUN.Abp.Notifications.Application\LINGYUN.Abp.Notifications.Application.csproj", "{B153F98A-7DA9-4A12-A1D7-105BE9408FA1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Application", "modules\notifications\LINGYUN.Abp.Notifications.Application\LINGYUN.Abp.Notifications.Application.csproj", "{B153F98A-7DA9-4A12-A1D7-105BE9408FA1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.HttpApi", "modules\notifications\LINGYUN.Abp.Notifications.HttpApi\LINGYUN.Abp.Notifications.HttpApi.csproj", "{EFC5C34F-81A1-4EFC-966F-50B646C54FA6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.HttpApi", "modules\notifications\LINGYUN.Abp.Notifications.HttpApi\LINGYUN.Abp.Notifications.HttpApi.csproj", "{EFC5C34F-81A1-4EFC-966F-50B646C54FA6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Localization.Persistence", "modules\localization\LINGYUN.Abp.Localization.Persistence\LINGYUN.Abp.Localization.Persistence.csproj", "{42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1397,6 +1399,10 @@ Global {EFC5C34F-81A1-4EFC-966F-50B646C54FA6}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFC5C34F-81A1-4EFC-966F-50B646C54FA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFC5C34F-81A1-4EFC-966F-50B646C54FA6}.Release|Any CPU.Build.0 = Release|Any CPU + {42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1663,6 +1669,7 @@ Global {E3F010C7-6C96-4EEA-B5FA-BF80515E195F} = {1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E} {B153F98A-7DA9-4A12-A1D7-105BE9408FA1} = {1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E} {EFC5C34F-81A1-4EFC-966F-50B646C54FA6} = {1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E} + {42A0FC3F-C38E-4FF4-B78A-5ED29DF144BF} = {90E88EAC-4291-4406-8D88-EFDF61B11292} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} diff --git a/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.Designer.cs b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.Designer.cs new file mode 100644 index 000000000..86c6cab55 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.Designer.cs @@ -0,0 +1,174 @@ +// +using System; +using LY.MicroService.LocalizationManagement.DbMigrator.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.LocalizationManagement.DbMigrator.Migrations +{ + [DbContext(typeof(LocalizationManagementMigrationsDbContext))] + [Migration("20230110091142_Add-Default-Culture-Name-With-Resource-Record")] + partial class AddDefaultCultureNameWithResourceRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "7.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Language", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasColumnName("CultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("FlagIcon") + .HasMaxLength(30) + .HasColumnType("varchar(30)") + .HasColumnName("FlagIcon"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasColumnName("UiCultureName"); + + b.HasKey("Id"); + + b.HasIndex("CultureName"); + + b.ToTable("AbpLocalizationLanguages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Resource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("varchar(64)") + .HasColumnName("DefaultCultureName"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("varchar(64)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .HasMaxLength(64) + .HasColumnType("varchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpLocalizationResources", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasColumnName("CultureName"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("Key"); + + b.Property("ResourceName") + .HasColumnType("longtext"); + + b.Property("Value") + .HasMaxLength(2048) + .HasColumnType("varchar(2048)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("AbpLocalizationTexts", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.cs b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.cs new file mode 100644 index 000000000..93d5ed418 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/20230110091142_Add-Default-Culture-Name-With-Resource-Record.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.LocalizationManagement.DbMigrator.Migrations +{ + /// + public partial class AddDefaultCultureNameWithResourceRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DefaultCultureName", + table: "AbpLocalizationResources", + type: "varchar(64)", + maxLength: 64, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DefaultCultureName", + table: "AbpLocalizationResources"); + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/LocalizationManagementMigrationsDbContextModelSnapshot.cs b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/LocalizationManagementMigrationsDbContextModelSnapshot.cs index 18a431aff..6203ebf62 100644 --- a/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/LocalizationManagementMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/migrations/LY.MicroService.LocalizationManagement.DbMigrator/Migrations/LocalizationManagementMigrationsDbContextModelSnapshot.cs @@ -92,6 +92,11 @@ namespace LY.MicroService.LocalizationManagement.DbMigrator.Migrations .HasColumnType("char(36)") .HasColumnName("CreatorId"); + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("varchar(64)") + .HasColumnName("DefaultCultureName"); + b.Property("Description") .HasMaxLength(64) .HasColumnType("varchar(64)") diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj new file mode 100644 index 000000000..68a757b72 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs new file mode 100644 index 000000000..7ca157451 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Localization.Persistence; + +[DependsOn( + typeof(AbpLocalizationModule))] +public class AbpLocalizationPersistenceModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHostedService(); + } +} diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs new file mode 100644 index 000000000..941de16d0 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.Localization.Persistence; + +public class AbpLocalizationPersistenceOptions +{ + public bool SaveStaticLocalizationsToPersistence { get; set; } + + public HashSet SaveToPersistenceResources { get; } + + public AbpLocalizationPersistenceOptions() + { + SaveStaticLocalizationsToPersistence = true; + + SaveToPersistenceResources = new HashSet(); + } + + public void AddPersistenceResource() + { + AddPersistenceResource(typeof(TResource)); + } + + public void AddPersistenceResource(Type resourceType) + { + var resourceName = LocalizationResourceNameAttribute.GetName(resourceType); + if (SaveToPersistenceResources.Contains(resourceName)) + { + return; + } + + SaveToPersistenceResources.Add(resourceName); + } +} diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/IStaticLocalizationSaver.cs b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/IStaticLocalizationSaver.cs new file mode 100644 index 000000000..025f0c1bd --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/IStaticLocalizationSaver.cs @@ -0,0 +1,8 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Localization.Persistence; + +public interface IStaticLocalizationSaver +{ + Task SaveAsync(); +} diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs new file mode 100644 index 000000000..32c74535d --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Localization.Persistence; + +public class StaticLocalizationSaverHostService : BackgroundService +{ + private readonly AbpLocalizationPersistenceOptions _options; + private readonly IStaticLocalizationSaver _staticLocalizationSaver; + + public StaticLocalizationSaverHostService( + IOptions options, + IStaticLocalizationSaver staticLocalizationSaver) + { + _options = options.Value; + _staticLocalizationSaver = staticLocalizationSaver; + } + + protected async override Task ExecuteAsync(CancellationToken stoppingToken) + { + if (_options.SaveStaticLocalizationsToPersistence) + { + await _staticLocalizationSaver.SaveAsync(); + } + } +} diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/README.md b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/README.md new file mode 100644 index 000000000..85a964815 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization.Persistence/README.md @@ -0,0 +1,41 @@ +# LINGYUN.Abp.Localization.Persistence + +## 模块说明 + +本地化组件持久层模块, 引用模块可将需要的本地化文档持久化到存储设施 + +### 基础模块 + +### 高阶模块 + +### 权限定义 + +### 功能定义 + +### 配置定义 + +### 如何使用 + + +```csharp + + [DependsOn( + typeof(AbpLocalizationPersistenceModule))] + public class YouProjectModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + // 启用持久化设施 + options.SaveStaticLocalizationsToPersistence = true; + + // 指定你的本地化资源类型, 此类型下定义的静态文档将被持久化到存储设施 + options.AddPersistenceResource(); + }); + } + } + +``` + +### 更新日志 diff --git a/aspnet-core/modules/localization/LINGYUN.Abp.Localization/LINGYUN.Abp.Localization.csproj b/aspnet-core/modules/localization/LINGYUN.Abp.Localization/LINGYUN.Abp.Localization.csproj new file mode 100644 index 000000000..a14eb7ce5 --- /dev/null +++ b/aspnet-core/modules/localization/LINGYUN.Abp.Localization/LINGYUN.Abp.Localization.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain.Shared/LINGYUN/Abp/LocalizationManagement/ResourceConsts.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain.Shared/LINGYUN/Abp/LocalizationManagement/ResourceConsts.cs index 2a2d9e2d5..83994ceed 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain.Shared/LINGYUN/Abp/LocalizationManagement/ResourceConsts.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain.Shared/LINGYUN/Abp/LocalizationManagement/ResourceConsts.cs @@ -5,5 +5,6 @@ public static int MaxNameLength { get; set; } = 50; public static int MaxDisplayNameLength { get; set; } = 64; public static int MaxDescriptionLength { get; set; } = 64; + public static int MaxDefaultCultureNameLength { get; set; } = 64; } } diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj index 78c91b6ad..60e64ae09 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj @@ -1,4 +1,4 @@ - + @@ -14,6 +14,7 @@ + diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs index c5e1c30f1..47d253f82 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs @@ -1,13 +1,17 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; using Volo.Abp.Domain; +using Volo.Abp.Localization; using Volo.Abp.Modularity; - +using LINGYUN.Abp.Localization.Persistence; +using LINGYUN.Abp.LocalizationManagement.Localization; + namespace LINGYUN.Abp.LocalizationManagement { [DependsOn( typeof(AbpAutoMapperModule), typeof(AbpDddDomainModule), + typeof(AbpLocalizationPersistenceModule), typeof(AbpLocalizationManagementDomainSharedModule))] public class AbpLocalizationManagementDomainModule : AbpModule { @@ -20,6 +24,16 @@ namespace LINGYUN.Abp.LocalizationManagement options.AddProfile(validate: true); }); + Configure(options => + { + options.GlobalContributors.Add(); + }); + + Configure(options => + { + options.AddPersistenceResource(); + }); + // 分布式事件 //Configure(options => //{ diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs index 64613f44b..db2f36253 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs @@ -7,6 +7,12 @@ namespace LINGYUN.Abp.LocalizationManagement { public interface ITextRepository : IRepository { + Task> GetExistsKeysAsync( + string resourceName, + string cultureName, + IEnumerable keys, + CancellationToken cancellationToken = default); + Task GetByCultureKeyAsync( string resourceName, string cultureName, diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs new file mode 100644 index 000000000..3dac52cc7 --- /dev/null +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs @@ -0,0 +1,92 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.LocalizationManagement; + +public class LocalizationManagementExternalContributor : ILocalizationResourceContributor +{ + public bool IsDynamic => true; + + private LocalizationResourceBase _resource; + private ITextRepository _textRepository; + private IResourceRepository _resourceRepository; + private ILanguageRepository _languageRepository; + + public void Initialize(LocalizationResourceInitializationContext context) + { + _resource = context.Resource; + _textRepository = context.ServiceProvider.GetRequiredService(); + _resourceRepository = context.ServiceProvider.GetRequiredService(); + _languageRepository = context.ServiceProvider.GetRequiredService(); + } + + public virtual void Fill(string cultureName, Dictionary dictionary) + { + FillInternalAsync(_resource.ResourceName, cultureName, dictionary).GetAwaiter().GetResult(); + } + + public async virtual Task FillAsync(string cultureName, Dictionary dictionary) + { + await FillInternalAsync(_resource.ResourceName, cultureName, dictionary); + } + + public virtual LocalizedString GetOrNull(string cultureName, string name) + { + return GetOrNullInternal(_resource.ResourceName, cultureName, name); + } + + protected virtual LocalizedString GetOrNullInternal(string resourceName, string cultureName, string name) + { + var resource = GetResourceOrNullAsync(name).GetAwaiter().GetResult(); + if (resource == null) + { + return null; + } + var text = _textRepository.GetByCultureKeyAsync(resourceName, cultureName, name).GetAwaiter().GetResult(); + if (text != null) + { + return new LocalizedString(name, text.Value); + } + + return null; + } + + public async virtual Task> GetSupportedCulturesAsync() + { + var languages = await _languageRepository.GetActivedListAsync(); + + return languages + .Select(x => x.CultureName) + .ToList(); + } + + protected async virtual Task FillInternalAsync(string resourceName, string cultureName, Dictionary dictionary) + { + var resource = await GetResourceOrNullAsync(resourceName); + if (resource == null) + { + return; + } + + var texts = await GetTextListByResourceAsync(resourceName, cultureName); + + foreach (var text in texts) + { + dictionary[text.Key] = new LocalizedString(text.Key, text.Value); + } + } + + protected async virtual Task GetResourceOrNullAsync(string resourceName) + { + return await _resourceRepository.FindByNameAsync(resourceName); + } + + protected async virtual Task> GetTextListByResourceAsync(string resourceName, string cultureName = null) + { + return await _textRepository.GetListAsync(resourceName, cultureName); + } +} diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/Resource.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/Resource.cs index c8fedf660..db763a96f 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/Resource.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/Resource.cs @@ -11,18 +11,21 @@ namespace LINGYUN.Abp.LocalizationManagement public virtual string Name { get; set; } public virtual string DisplayName { get; set; } public virtual string Description { get; set; } + public virtual string DefaultCultureName { get; set; } protected Resource() { } public Resource( [NotNull] string name, [CanBeNull] string displayName = null, - [CanBeNull] string description = null) + [CanBeNull] string description = null, + [CanBeNull] string defaultCultureName = null) { Name = Check.NotNullOrWhiteSpace(name, nameof(name), ResourceConsts.MaxNameLength); - DisplayName = displayName ?? Name; - Description = description; + DisplayName = Check.Length(displayName ?? Name, nameof(displayName), ResourceConsts.MaxDisplayNameLength);; + Description = Check.Length(description, nameof(description), ResourceConsts.MaxDescriptionLength); + DefaultCultureName = Check.Length(defaultCultureName, nameof(defaultCultureName), ResourceConsts.MaxDefaultCultureNameLength); - Enable = true; + Enable = true; } } } diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs new file mode 100644 index 000000000..816bfbd1a --- /dev/null +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs @@ -0,0 +1,124 @@ +using LINGYUN.Abp.Localization.Persistence; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.LocalizationManagement; + +[Dependency(ReplaceServices = true)] +public class StaticLocalizationSaver : IStaticLocalizationSaver, ITransientDependency +{ + protected ILanguageRepository LanguageRepository { get; } + protected ITextRepository TextRepository { get; } + protected IResourceRepository ResourceRepository { get; } + protected IStringLocalizerFactory StringLocalizerFactory { get; } + protected AbpLocalizationOptions LocalizationOptions { get; } + protected IServiceProvider ServiceProvider { get; } + protected AbpLocalizationPersistenceOptions LocalizationPersistenceOptions { get; } + + public StaticLocalizationSaver( + IServiceProvider serviceProvider, + ILanguageRepository languageRepository, + ITextRepository textRepository, + IResourceRepository resourceRepository, + IStringLocalizerFactory stringLocalizerFactory, + IOptions localizationOptions, + IOptions localizationPersistenceOptions) + { + ServiceProvider = serviceProvider; + LanguageRepository = languageRepository; + TextRepository = textRepository; + ResourceRepository = resourceRepository; + StringLocalizerFactory = stringLocalizerFactory; + LocalizationOptions = localizationOptions.Value; + LocalizationPersistenceOptions = localizationPersistenceOptions.Value; + } + + [UnitOfWork] + public async virtual Task SaveAsync() + { + var insertNewTexts = new List(); + + foreach (var language in LocalizationOptions.Languages) + { + if (await LanguageRepository.FindByCultureNameAsync(language.CultureName) == null) + { + await LanguageRepository.InsertAsync( + new Language( + language.CultureName, + language.UiCultureName, + language.DisplayName, + language.FlagIcon)); + } + + foreach (var resource in LocalizationPersistenceOptions.SaveToPersistenceResources) + { + using (CultureHelper.Use(language.CultureName, language.UiCultureName)) + { + var localizationResource = LocalizationOptions.Resources.GetOrDefault(resource); + if (localizationResource == null) + { + continue; + } + + var context = new LocalizationResourceInitializationContext(localizationResource, ServiceProvider); + + if (await ResourceRepository.FindByNameAsync(localizationResource.ResourceName) == null) + { + await ResourceRepository.InsertAsync( + new Resource( + localizationResource.ResourceName, + localizationResource.ResourceName, + localizationResource.ResourceName, + localizationResource.DefaultCultureName)); + } + + foreach (var contributor in localizationResource.Contributors) + { + if (contributor.IsDynamic) + { + continue; + } + + contributor.Initialize(context); + var fillTexts = new Dictionary(); + + await contributor.FillAsync(language.CultureName, fillTexts); + + var existsKeys = await TextRepository.GetExistsKeysAsync( + localizationResource.ResourceName, + language.CultureName, + fillTexts.Values.Select(x => x.Name)); + + var notExistsKeys = fillTexts.Values.Where(x => !existsKeys.Contains(x.Name)); + notExistsKeys = notExistsKeys.Where(x => !insertNewTexts.Any(t => t.Key == x.Name)); + + foreach (var notExistsKey in notExistsKeys) + { + if (!insertNewTexts.Any(x => x.CultureName == language.CultureName && x.Key == notExistsKey.Name)) + { + insertNewTexts.Add( + new Text( + localizationResource.ResourceName, + language.CultureName, + notExistsKey.Name, + notExistsKey.Value)); + } + } + } + } + } + } + + if (insertNewTexts.Any()) + { + await TextRepository.InsertManyAsync(insertNewTexts); + } + } +} diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs index 331bc0fce..0a1ae3d23 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using System; using System.Collections.Generic; using System.Linq; @@ -18,6 +19,20 @@ namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore { } + public async virtual Task> GetExistsKeysAsync( + string resourceName, + string cultureName, + IEnumerable keys, + CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(x => x.ResourceName.Equals(resourceName) && x.CultureName.Equals(cultureName) + && keys.Contains(x.Key)) + .Select(x => x.Key) + .Distinct() + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public async virtual Task GetByCultureKeyAsync( string resourceName, string cultureName, diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/LocalizationDbContextModelBuilderExtensions.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/LocalizationDbContextModelBuilderExtensions.cs index 81ca35f8a..c5c384627 100644 --- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/LocalizationDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/LocalizationDbContextModelBuilderExtensions.cs @@ -65,6 +65,9 @@ namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore x.Property(p => p.Description) .HasMaxLength(ResourceConsts.MaxDescriptionLength) .HasColumnName(nameof(Resource.Description)); + x.Property(p => p.DefaultCultureName) + .HasMaxLength(ResourceConsts.MaxDefaultCultureNameLength) + .HasColumnName(nameof(Resource.DefaultCultureName)); x.Property(p => p.Enable) .HasDefaultValue(true); diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs index 9a68a4698..f3b306d68 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs @@ -258,7 +258,7 @@ public partial class LocalizationManagementHttpApiHostModule if (isDevelopment) { - // services.AddAlwaysAllowAuthorization(); + services.AddAlwaysAllowAuthorization(); } if (!isDevelopment) diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json index 47bae649e..b25a694b0 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json @@ -1,6 +1,6 @@ { "AgileConfig": { - "IsEnabled": true, + "IsEnabled": false, "env": "DEV", "appId": "LINGYUN.Abp.Localization", "secret": "1q2w3E*", @@ -23,11 +23,11 @@ } }, "ConnectionStrings": { - "Default": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", - "AbpSaas": "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", - "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" + "Default": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456", + "AbpSaas": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456", + "AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456", + "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456", + "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456" }, "CAP": { "EventBus": {