diff --git a/.gitignore b/.gitignore index 3f8d0ff6cb..ccb98f81a0 100644 --- a/.gitignore +++ b/.gitignore @@ -323,3 +323,7 @@ deploy/npm-auth-token.txt deploy/ssh-password.txt deploy/github-api-key.txt deploy/_run_all_log.txt + + +# No commit yarn.lock files in the subfolders of templates directory +templates/**/yarn.lock \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json index 4738183af4..2cb142bb68 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json @@ -11,6 +11,6 @@ "CommercialSupportWebSite": "Kereskedelmi támogatási webhely", "CommunityWebSite": "ABP közösségi webhely", "ManageAccount": "Saját fiók | ABP.IO", - "ManageYourAccount": "Fiók kezelése" + "ManageYourProfile": "Profilod kezelése" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hu.json index e84bc09995..e006543933 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hu.json @@ -316,14 +316,12 @@ "TrialLicenseStatusFilter": "Állapot", "TrialLicenseStartDateFilter": "Kezdő dátum", "TrialLicenseEndDateFilter": "Befejezés dátuma", - "FirsName": "Keresztnév", + "FirstName": "Keresztnév", "LastName": "Vezetéknév", "StartDate": "Kezdő dátum", "EndDate": "Befejezés dátuma", "PurchasedDate": "Vásárlás dátuma", "OrganizationDetail": "Szervezet részletei", - "SendActivationMail": "Aktiváló e-mail küldése", - "ActivationMailSentSuccessfully": "Az aktiváló levél sikeresen elküldve!", "TrialLicenseStatus": "Próbaengedély állapota", "TrialLicenseDetail": "A próbaengedély részletei", "AcceptsMarketingCommunications": "Marketing kommunikáció", @@ -337,17 +335,70 @@ "Expired": "Lejárt", "TrialLicenseDeletionWarningMessage": "Biztosan törölni szeretné a próbalicencet? A próbaengedély, a szervezet, a támogatási fiókok törlésre kerülnek!", "LicenseCategoryFilter": "Licenc kategória", - "Volo.AbpIo.Commercial:030000": "Már felhasználta a próbaidőszakot.", - "Volo.AbpIo.Commercial:030001": "Ez a szervezetnév már létezik.", - "Volo.AbpIo.Commercial:030002": "Az aktiválás után a próbalicenc nem állítható kérésre!", - "Volo.AbpIo.Commercial:030003": "Nincs ilyen állapot!", - "Volo.AbpIo.Commercial:030004": "Váratlan hiba miatt az állapot nem módosítható!", - "Volo.AbpIo.Commercial:030005": "A kezdő és befejező dátum akkor frissíthető, ha a próbalicenc -aktivált- státuszban van!", - "Volo.AbpIo.Commercial:030006": "A befejező dátumnak mindig nagyobbnak kell lennie, mint a kezdő dátum!", - "Volo.AbpIo.Commercial:030007": "Ezt a próbalicencet már egyszer aktiválták!", - "Volo.AbpIo.Commercial:030008": "A vásárlás dátuma csak Vásárolt állapot esetén állítható be!", - "Volo.AbpIo.Commercial:030009": "Felhasználó nem található!", - "Volo.AbpIo.Commercial:030010": "A próbalicenc megvásárlásához először aktiválnia kell a próbalicencet!", - "Volo.AbpIo.Commercial:030011": "A próbalicenc megvásárlásakor nem törölhető!" + "Permission:SendWelcomeEmail": "Üdvözlő e-mail küldése", + "SendWelcomeEmail": "Üdvözlő e-mail küldése", + "SendWelcomeEmailWarningMessage": "Biztosan üdvözlő e-mailt szeretne küldeni a szervezet tagjainak?", + "SendWelcomeEmailSuccessMessage": "Az üdvözlő e-mail sikeresen elküldve!", + "Activate": "Aktiválás", + "ActivateTrialLicenseWarningMessage": "A próbalicensz aktiválásakor egy üdvözlő e-mailt küldünk a felhasználónak. Szeretné aktiválni?", + "ActivateTrialLicenseSuccessMessage": "Sikeres aktiválás. Az üdvözlő e-mailt elküldtük a szervezet tagjainak.", + "PaymentRequestId": "Fizetési kérelem azonosítója", + "AdditionalDeveloperCount": "További fejlesztők száma", + "LicensePrice": "licensz ára", + "PurchaseDate": "Vásárlás időpontja", + "IsAbpBookDownloaded": "ABP könyv letöltve", + "IsMasteringAbpBookDownloadEnabled": "Az ABP könyv letöltése engedélyezve", + "Permission:Accounting:CustomPaymentLinkGenerator": "Egyéni fizetési link", + "CustomPaymentLink": "Egyéni fizetési link", + "Menu:CustomPaymentLink": "Egyéni fizetési link", + "Amount": "Összeg", + "GenerateCustomPaymentLink": "Egyéni fizetési link létrehozása", + "GeneratedPaymentLink": "Létrehozott fizetési link", + "CopyText": "Szöveg másolása", + "Permission:CommunityEvents": "Események", + "Menu:Events": "Események", + "Events": "Események", + "EventType": "Esemény típus", + "Number": "Szám", + "RegistrationURL": "Regisztrációs URL", + "URL": "URL", + "EventDeletionConfirmationMessage": "Biztosan törli ezt az eseményt?", + "Enum:EventType:0": "Közösségi beszélgetések", + "CreateAnEvent": "Hozzon létre egy eseményt", + "Permission:CommunitySpeakers": "Előadók", + "CreateASpeaker": "Hozzon létre egy hangszórót", + "Speakers": "Előadók", + "Image": "Kép", + "GithubURL": "Github URL", + "SpeakerDeletionConfirmationMessage": "Biztosan törli ezt a hangszórót?", + "Menu:Speakers": "Előadók", + "ChooseSpeakerImage": "Válasszon egy előadó képet...", + "SpeakerImage": "Előadó képe", + "AddSpeaker": "Előadó hozzáadása", + "ShowPurchaseItemsOfOrganizations": "Tételek vásárlása", + "Enum:OrganizationPurchaseState:0": "Nem kézbesített", + "Enum:OrganizationPurchaseState:1": "Szállítva", + "PurchaseItems": "Tételek vásárlása", + "SuccessfullyUpdated": "Sikeresen frissítve", + "SuccessfullyAdded": "Sikeresen hozzáadva", + "PurchaseState": "Vásárlási állapot", + "ShowBetweenDayCount": "Megjelenítés a napok között", + "PurchaseOrder": "Rendelés", + "ShowCreateInvoiceOfOrganization": "Számla létrehozása", + "ShowCreateQuotationOfOrganization": "Árajánlat létrehozása", + "BookDiscounts": "Könyvkedvezmények", + "Permission:BookDiscount": "Könyvkedvezmény", + "Menu:BookDiscounts": "Könyvkedvezmények", + "BookType": "Könyv típusa", + "PurchasePlatform": "Vásárlási platform", + "StartTime": "Kezdési idő", + "EndTime": "Idő vége", + "CreateABookDiscount": "Hozzon létre könyvkedvezményt", + "BookDiscountDeletionConfirmationMessage": "Biztosan törli ezt a könyvkedvezményt?", + "CustomPaymentFlexSwitchDescription": "Licenszel", + "AllowFeatureUpgradeOnLicenseExpire": "A funkció frissítésének engedélyezése a licensz lejártakor", + "Deleted{0}": "[Törölve {0}]", + "Tags": "Címkék", + "SetTagsInfo": "A címkéket vesszővel kell elválasztani. Pl.: CSharp, Entity Framework" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json index bb2e4077cc..b363aa0c64 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json @@ -316,14 +316,12 @@ "TrialLicenseStatusFilter": "地位", "TrialLicenseStartDateFilter": "开始日期", "TrialLicenseEndDateFilter": "结束日期", - "FirsName": "名", + "FirstName": "名字", "LastName": "姓", "StartDate": "开始日期", "EndDate": "结束日期", "PurchasedDate": "购买日期", "OrganizationDetail": "组织详情", - "SendActivationMail": "发送激活邮件", - "ActivationMailSentSuccessfully": "激活邮件发送成功!", "TrialLicenseStatus": "试用许可证状态", "TrialLicenseDetail": "试用许可证详情", "AcceptsMarketingCommunications": "营销传播", @@ -337,17 +335,68 @@ "Expired": "已到期", "TrialLicenseDeletionWarningMessage": "您确定要删除试用许可证吗?试用许可证、组织、支持帐户将被删除!", "LicenseCategoryFilter": "执照类别", - "Volo.AbpIo.Commercial:030000": "您已经使用了试用期。", - "Volo.AbpIo.Commercial:030001": "此组织名称已存在。", - "Volo.AbpIo.Commercial:030002": "一旦激活,试用许可证不能设置为请求!", - "Volo.AbpIo.Commercial:030003": "没有这种状态!", - "Volo.AbpIo.Commercial:030004": "由于意外错误,无法更改状态!", - "Volo.AbpIo.Commercial:030005": "当试用许可证处于 -activated- 状态时,可以更新开始和结束日期!", - "Volo.AbpIo.Commercial:030006": "结束日期必须始终大于开始日期!", - "Volo.AbpIo.Commercial:030007": "此试用许可证已激活一次!", - "Volo.AbpIo.Commercial:030008": "购买日期只能在状态为已购买时设置!", - "Volo.AbpIo.Commercial:030009": "未找到用户!", - "Volo.AbpIo.Commercial:030010": "要购买试用许可证,首先您需要激活您的试用许可证!", - "Volo.AbpIo.Commercial:030011": "购买后,您无法删除试用许可证!" + "Permission:SendWelcomeEmail": "发送欢迎邮件", + "SendWelcomeEmail": "发送欢迎邮件", + "SendWelcomeEmailWarningMessage": "你确定要发送欢迎邮件给组织成员吗?", + "SendWelcomeEmailSuccessMessage": "欢迎邮件发送成功!", + "Activate": "激活", + "ActivateTrialLicenseWarningMessage": "激活试用版权限后,将发送欢迎邮件给用户。你确定要激活吗?", + "ActivateTrialLicenseSuccessMessage": "激活成功,欢迎邮件已发送给组织成员。", + "PaymentRequestId": "付款请求编号", + "AdditionalDeveloperCount": "额外开发者数量", + "LicensePrice": "版权价格", + "PurchaseDate": "购买日期", + "IsAbpBookDownloaded": "ABP书籍已下载", + "IsMasteringAbpBookDownloadEnabled": "ABP书籍下载已启用", + "Permission:Accounting:CustomPaymentLinkGenerator": "自定义付款链接", + "CustomPaymentLink": "自定义付款链接", + "Menu:CustomPaymentLink": "自定义付款链接", + "Amount": "金额", + "GenerateCustomPaymentLink": "生成自定义付款链接", + "GeneratedPaymentLink": "生成的付款链接", + "CopyText": "复制文本", + "Permission:CommunityEvents": "活动", + "Menu:Events": "活动", + "Events": "活动", + "EventType": "活动类型", + "Number": "数量", + "RegistrationURL": "报名网址", + "URL": "网址", + "EventDeletionConfirmationMessage": "你确定要删除这个活动吗?", + "Enum:EventType:0": "社区讲话", + "CreateAnEvent": "创建一个活动", + "Permission:CommunitySpeakers": "演讲者", + "CreateASpeaker": "创建一个演讲者", + "Speakers": "演讲者", + "Image": "图片", + "GithubURL": "Github网址", + "SpeakerDeletionConfirmationMessage": "你确定要删除这个演讲者吗?", + "Menu:Speakers": "演讲者", + "ChooseSpeakerImage": "选择一个演讲者图片...", + "SpeakerImage": "演讲者图片", + "AddSpeaker": "添加演讲者", + "ShowPurchaseItemsOfOrganizations": "购买项目", + "Enum:OrganizationPurchaseState:0": "未送达", + "Enum:OrganizationPurchaseState:1": "已送达", + "PurchaseItems": "购买项目", + "SuccessfullyUpdated": "成功更新", + "SuccessfullyAdded": "成功添加", + "PurchaseState": "购买状态", + "ShowBetweenDayCount": "显示之间的天数", + "PurchaseOrder": "购买订单", + "ShowCreateInvoiceOfOrganization": "创建发票", + "ShowCreateQuotationOfOrganization": "创建报价单", + "BookDiscounts": "书籍折扣", + "Permission:BookDiscount": "书籍折扣", + "Menu:BookDiscounts": "书籍折扣", + "BookType": "书籍类型", + "PurchasePlatform": "购买平台", + "StartTime": "开始时间", + "EndTime": "结束时间", + "CreateABookDiscount": "创建一个书籍折扣", + "BookDiscountDeletionConfirmationMessage": "你确定要删除这个书籍折扣吗?", + "CustomPaymentFlexSwitchDescription": "授权", + "AllowFeatureUpgradeOnLicenseExpire": "允许在授权过期时进行功能升级", + "Deleted{0}": "[已删除 {0}]" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json index 079132d1e8..a85a2369f0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json @@ -14,6 +14,20 @@ "Volo.AbpIo.Domain:020002": "Nem sikerült törölni ezt az NPM-csomagot, mert a \"{Modules}\" modulok ezt a csomagot használják.", "Volo.AbpIo.Domain:020003": "Nem sikerült törölni ezt az NPM-csomagot, mert a \"{Modules}\" modulok ezt a csomagot használják, és a \"{NugetPackages}\" Nuget-csomagok ettől a csomagtól függenek.", "Volo.AbpIo.Domain:020004": "Nem sikerült törölni ezt a Nuget-csomagot, mert a \"{Modules}\" modulok ezt a csomagot használják.", + "Volo.AbpIo.Domain:030000": "Már befejezte a próbaidőszakot.", + "Volo.AbpIo.Domain:030001": "Ez a szervezetnév már létezik.", + "Volo.AbpIo.Domain:030002": "Az aktiválás után a próbalicensz nem váltható át -igényelt- állapotra!", + "Volo.AbpIo.Domain:030003": "Nincs ilyen állapot!", + "Volo.AbpIo.Domain:030004": "Az állapot váratlan hiba miatt nem változtatható meg!", + "Volo.AbpIo.Domain:030005": "A kezdő és befejező dátum akkor frissíthető, ha a próbalicensz -aktivált- státuszban van!", + "Volo.AbpIo.Domain:030006": "A befejező dátumnak nagyobbnak kell lennie, mint a kezdő dátum!", + "Volo.AbpIo.Domain:030007": "Ez a próbalicensz már aktiválva van!", + "Volo.AbpIo.Domain:030008": "A vásárlás dátuma csak -megvásárolt- státusz esetén állítható be!", + "Volo.AbpIo.Domain:030009": "Felhasználó nem található!", + "Volo.AbpIo.Domain:030010": "A próbalicensz megvásárlásához először aktiválnia kell a próbalicenszet!", + "Volo.AbpIo.Domain:030011": "A próbalicensz megvásárlásakor nem törölhető!", + "Volo.AbpIo.Domain:070000": "A szervezet neve csak latin betűket, számokat, pontokat és kötőjeleket tartalmazhat!", + "Volo.AbpIo.Domain:070001": "A cégnév csak latin betűket, számokat, pontokat, szóközt és kötőjelet tartalmazhat!", "WantToLearn?": "Tanulni akar?", "ReadyToGetStarted?": "Készen áll az indulásra?", "JoinOurCommunity": "Csatlakozz a közösségünkhöz", @@ -39,6 +53,132 @@ "TrialLicensePeriodHasExpired": "A próbalicenc időszaka {0} napja lejárt.", "TrialLicensePeriodWillExpire": "A próbalicenc időszaka {0} napon belül lejár.", "TrialLicensePeriodExpireToday": "A próbalicenc ideje ma lejár.", - "PurchaseNow": "Vásároljon most!" + "PurchaseNow": "Vásároljon most!", + "LatestReleaseLogs": "Legújabb kiadási naplók", + "RoadMap": "Útiterv", + "FAQ": "GYIK", + "SourceCode": "Forráskód", + "SeeAllPosts": "Az összes bejegyzés megtekintése", + "Contribute": "Hozzájárulás", + "LiveDemo": "Élő Demo", + "GetLicense": "Licensz beszerzése", + "OpenSource": "Nyílt forráskód", + "WebApplication": "Webalkalmazás", + "MeetTheABP": "Ismerje meg az ABP-t", + "CompleteWebDevelopment": "Teljes körű webfejlesztés", + "Platform": "Felület", + "ABPDescription": "Az ABP Framework egy komplett infrastruktúra modern webalkalmazások létrehozásához, követve a szoftverfejlesztés legjobb gyakorlatait és konvencióit.", + "StrongInfrastructure": "Erős infrastruktúra", + "CompleteArchitecture": "Teljes arhitektúra", + "DeveloperFocused": "Fejlesztőközpontú", + "ShareYourExperiences": "Ossza meg tapasztalatait az ABP keretrendszerrel", + "LatestPosts": "Legutóbbi bejegyzések", + "LatestVideos": "Legújabb videók", + "Views": "Nézetek", + "LearnLatestNewsAboutABPFramework": "Információkat kaphat az ABP eseményeiről, például új kiadásokról, ingyenes forrásokról, bejegyzésekről és egyebekről.", + "DeveloperTools": "Fejlesztői eszközök", + "StartupTemplates": "Indítási sablonok", + "ApplicationModules": "Alkalmazási modulok", + "UI": "UI", + "Themes": "Témák", + "Premium": "Prémium", + "PrivacyPolicy": "Adatvédelmi irányelvek", + "TermsAndConditions": "Felhasználási feltételek", + "WouldLikeToReceiveMarketingMaterials": "Szeretnék marketing anyagokat kapni, például termékajánlatokat és különleges ajánlatokat.", + "JoinOurMarketingNewsletter": "Csatlakozzon marketing hírlevelünkhöz", + "CommunityPrivacyPolicyConfirmation": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot.", + "WouldLikeToReceiveNotification": "Szeretném megkapni a legfrissebb híreket az abp.io webhelyekről.", + "CommercialNewsletterConfirmationMessage": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot .", + "FreeDDDEBook": "Ingyenes DDD e-könyv", + "AdditionalServices": "További szolgáltatások", + "Learn": "Tanulás", + "AccountOverview": "Fiók Áttekintés", + "MyOrganizations": "Szervezeteim", + "MySupportQuestions": "Támogatási kérdéseim", + "MyProfile": "A profilom", + "Logout": "Kijelentkezés", + "Home": "Kezdőlap", + "Posts": "Hozzászólások", + "Videos": "Videók", + "JoinTheABPCommunity": "Csatlakozz az ABP közösséghez", + "SubmitYourPost": "Küldje be bejegyzését", + "Modules": "Modulok", + "Tools": "Eszközök", + "Pricing": "Árazás", + "ChangeLogs": "Változásnaplók", + "SubscribeToNewsletter": "Feliratkozás a Hírlevélre", + "SubscribeToNewsletterDescription": "Információkat kaphat az ABP eseményeiről, például új kiadásokról, ingyenes forrásokról, bejegyzésekről és egyebekről.", + "EmailAddress": "Email cím", + "Subscribe": "Iratkozz fel", + "WelcomeToABP": "Üdvözöljük az ABP-ben", + "EULA": "EULA", + "ABPCommercialIntroductionMessage": "Előre beépített alkalmazásmodulok, fejlett indítási sablonok, gyors alkalmazásfejlesztési eszközök, professzionális felhasználói felületi témák és prémium támogatás.", + "MasteringAbpFrameworkEBook": "Az ABP Framework elsajátítása", + "MasteringTheABPFrameworkExplanation": "Ez a könyv, amelyet az ABP-keretrendszer alkotója írt, segít a keretrendszer és a modern webalkalmazás-fejlesztési technikák teljes megértésében.", + "Speakers": "Előadók", + "PreviousEvents": "Korábbi események", + "WatchTheEvent": "Nézze meg az Eseményt", + "RegisterNow": "Regisztrálj most", + "ThereIsNoEvent": "Nincs esemény.", + "Events": "Események", + "Volo.AbpIo.Domain:080000": "Már van egy \"{Name}\" nevű vásárlási tétel", + "MasteringAbpFrameworkBook": "Könyv: Az ABP-keretrendszer elsajátítása", + "ABPIO-CommonPreferenceDefinition": "Szerezze meg a legfrissebb híreket az ABP Platformról, például új bejegyzésekről, eseményekről és egyebekről.", + "BuiltOn": "Beépített", + "AbpFramework": "ABP-keretrendszer", + "Volo.AbpIo.Domain:080001": "A kezdési idő nem lehet nagyobb, mint a befejezési idő", + "Enum:BookType:0": "Az ABP Framework elsajátítása", + "Enum:PurchasePlatform:0": "Amazon", + "Enum:PurchasePlatform:1": "Csomagolt", + "Copied": "Másolva!", + "CouldNotCopy": "Nem sikerült másolni!", + "CopyNotSupportByYourBrowser": "Ez a funkció nem működik az Ön által használt böngészőben.", + "City": "Város", + "ZipCode": "Irányítószám", + "Address": "Cím", + "Homepage": "Kezdőlap", + "Year": "Év", + "Copyright": "Copyright © {1}", + "DomainDrivenDesign": "Domainvezérelt tervezés", + "CrossCuttingConcerns": "Cross Cutting Concerns", + "AbpCommunity": "ABP közösség", + "Footer_GithubStarCount": "{0} Csillag a GitHubon", + "Footer_NugetDownloadCount": "{0} Letöltések a NuGeten", + "AbpDescription": "Az ABP egy nyílt forráskódú alkalmazás keretrendszer, amely az AspNet Core alapú webalkalmazások fejlesztésére összpontosít. Ne ismételje magát, összpontosítson saját üzleti kódjára.", + "Layout_AbpFramework_MetaTitle": "ABP Framework – Nyílt forráskódú webalkalmazás-keretrendszer", + "CommunityTalks_CountdownDays": "Napok", + "CommunityTalks_CountdownHours": "óra", + "CommunityTalks_CountdownMinutes": "Perc", + "CommunityTalks_CountdownSeconds": "mp", + "SeePreviousEvents": "Lásd: Korábbi események", + "CookieConsent_Accept": "Elfogad", + "CookieConsent_Explanation_1": "Cookie-kat használunk, hogy a legjobb élményt nyújtsuk weboldalunkon.", + "CookieConsent_Explanation_2": "Ha folytatja a böngészést, elfogadja adatvédelmi szabályzatunkat és cookie-kra vonatkozó szabályzatunkat. .", + "Error_Page_400_Title": "Hiba történt a kért oldal kiszolgálása során.", + "Error_Page_400_Description_1": "Ez általában azt jelenti, hogy a kérés feldolgozása során váratlan hiba történt.", + "Error_Page_400_Description_2": "Ha a probléma továbbra is fennáll, vegye fel velünk a kapcsolatot az info@abp.io címen , és mi segítünk az úton.", + "GoToHomepage": "Menj a főoldalra", + "Error_Page_404_Title": "Az oldal nem található!", + "Error_Page_404_Description_1": "Ez nem az a weboldal, amit keres.", + "Error_Page_500_Title": "Úgy tűnik, valami elromlott!", + "Error_Page_500_Description_1": "Ezeket a hibákat automatikusan nyomon követjük, de ha a probléma továbbra is fennáll, nyugodtan megteheti
lépjen kapcsolatba velünk. Addig is próbáljon frissíteni.", + "Error_Page_500_Description_2": "Vegye fel velünk a kapcsolatot az info@abp.io címen .", + "Books": "Könyvek", + "ABPDiscordServer": "ABP Discord szerver", + "ABPCommunityTalks": "ABP közösségi beszélgetések", + "ABPCommunityPosts": "ABP közösségi bejegyzések", + "BuyAndGetMonths": "VÁSÁROLJON 12 HÓNAPOT, 14 HÓNAPOT KAP!", + "GetYourDeal": "Szerezze meg az ajánlatát", + "BuyOrRenewLicense": "Vásároljon vagy újítson meg licencet most, és 2 további hónapot kap!", + "BuyOrRenewLicenseToGetExtra2Months": "Vásároljon vagy újítson meg licencet most, és 2 további hónapot kap! SIESS! ⏰ Utolsó nap: {0}", + "HurryUp": "SIESS!", + "LastDay": "Utolsó nap: {0}", + "BuyNewLicenseBetweenDatesToGetBenefit": "Vásároljon új licencet {0} és {1} között, és további 2 hónapra juthat!", + "CheckAllCommunityTalks": "Jelölje be az Összes közösségi bejegyzést", + "ReadMore": "Olvass tovább", + "Post": "Hozzászólás", + "ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Fedezze fel a központi ABP csapat és az ABP közösség által létrehozott tartalmakat.", + "WelcomeFallCampaign": "Üdvözöljük az őszi kampányban!", + "GiveAwayForNewPurchases": "Az új vásárlásokhoz az Alkalmazásfejlesztési Tantermi Képzést ajándékba adjuk!" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json index e1ad37704f..52358fb0ed 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json @@ -164,6 +164,9 @@ "Error_Page_500_Description_1": "我们会自动跟踪这些错误,但如果问题仍然存在,请随时
联系我们。 与此同时,尝试刷新。", "Error_Page_500_Description_2": "通过 info@abp.io 与我们联系。", "Books": "书籍", + "ABPDiscordServer": "ABP Discord 服务器", + "ABPCommunityTalks": "ABP社区讲话", + "ABPCommunityPosts": "ABP社区文章", "BuyAndGetMonths": "购买 12 个月,获得 14 个月!", "GetYourDeal": "得到你的交易", "BuyOrRenewLicense": "立即购买或续订许可证并额外获得 2 个月!", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/hu.json new file mode 100644 index 0000000000..0cd40d0d5a --- /dev/null +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/hu.json @@ -0,0 +1,7 @@ +{ + "culture": "hu", + "texts": { + "AbpTitle": "ABP Framework – Nyílt forráskódú webalkalmazás-keretrendszer", + "AbpDescription": "Az ABP egy nyílt forráskódú alkalmazáskeret, amely az AspNet Core alapú webalkalmazások fejlesztésére összpontosít. Ne ismételje magát, összpontosítson saját üzleti kódjára." + } +} \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 64cefef9af..2e3c509e0d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -548,7 +548,7 @@ "MyOrganizations_Detail_OwnerRightInfo": "You are using {0} of your {1} owners rights.", "MyOrganizations_Detail_CopyApiKey": "Copy the Key", "MyOrganizations_Detail_ApiKeyDescription": "The API Key is the token of PRO packages hosted on {1}.", - "MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is {0}", + "MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is {0}", "MyOrganizations_Detail_PrivateNugetSourceWarning": "This is automatically added as a feed to your NuGet.Config in your ABP solution. Do not share your private key with unauthorized users!", "MyOrganizations_Detail_DeveloperSeatInfo": "You are using {0} of your {1} developer seats.", "NeedMoreSeatsForYourTeam": "Need more seats for your team?", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json index b6028bb0aa..0de24176ed 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json @@ -9,15 +9,18 @@ "QuestionCount": "Fennmaradó/összes kérdés", "Unlimited": "Korlátlan", "Owners": "Tulajdonosok", + "Owner": "Tulajdonos", "AddMember": "Tag hozzáadása lehetőségre", - "AddOwner": "Tulajdonos hozzáadása", - "AddDeveloper": "Fejlesztő hozzáadása", + "AddNewOwner": "Új tulajdonos hozzáadása", + "AddNewDeveloper": "Új fejlesztő hozzáadása", "UserName": "Felhasználónév", "Name": "Név", "EmailAddress": "Email cím", "Developers": "Fejlesztők", "LicenseType": "Jogosítvány típus", "Manage": "Kezelése", + "SetDefault": "Beállítás alapértelmezettként", + "DefaultOrganization": "Alapértelmezett", "StartDate": "Kezdő dátum", "EndDate": "Befejezés dátuma", "Modules": "Modulok", @@ -60,8 +63,6 @@ "Themes": "Témák", "JoinOurNewsletter": "Csatlakozzon hírlevelünkhöz", "Send": "Küld", - "Learn": "Tanul", - "AdditionalServices": "További szolgáltatások", "WhatIsABPFramework": "MI AZ ABP KERET?", "OpenSourceBaseFramework": "Nyílt forráskódú alapkeretrendszer", "ABPFrameworkExplanation": "

Az ABP Commercial az ABP-keretrendszeren alapul, amely egy nyílt forráskódú és közösségvezérelt webalkalmazás-keretrendszer az ASP.NET Core számára.

Az ABP-keretrendszer kiváló infrastruktúrát biztosít karbantartható, bővíthető íráshoz. és tesztelhető kód a bevált gyakorlatokkal.

Beépített és integrált népszerű eszközök, amelyeket már ismer. Alacsony tanulási görbe, könnyű alkalmazkodás, kényelmes fejlődés.

", @@ -127,6 +128,8 @@ "TellUsWhatYouNeed": "Mondja el, mire van szüksége.", "YourMessage": "Az üzeneted", "YourFullName": "A teljes neved", + "FirstNameField": "Keresztnév", + "LastNameField": "Vezetéknév", "EmailField": "Email cím", "YourEmailAddress": "Az email címed", "HowMayWeHelpYou": "Hogyan segíthetünk?", @@ -157,6 +160,8 @@ "SearchQuestionPlaceholder": "Keressen a gyakran ismételt kérdések között", "WhatIsTheABPCommercial": "Mi az az ABP Commercial?", "WhatAreDifferencesThanAbpFramework": "Mi a különbség a nyílt forráskódú ABP Framework és az ABP Commercial között?", + "AbpCommercialMetaTitle": "ABP Commercial – Teljes webfejlesztési platform: {0} | ABP Commercial", + "AbpCommercialMetaDescription": "Az ABP Commercial a nyílt forráskódú ABP keretrendszerre épülő előre beépített alkalmazásmodulok, gyorsfejlesztő eszközök, UI témák és szolgáltatások készlete.", "ABPCommercialExplanation": "Az ABP Commercial prémium modulok, eszközök, témák és szolgáltatások készlete a nyílt forráskódú ABP keretrendszerre épül fel. Az ABP Commercial-t ugyanaz a csapat fejleszti és támogatja az ABP keretrendszer mögött.", "WhatAreDifferencesThanABPFrameworkExplanation": "

Az ABP-keretrendszer egy moduláris, tematikus, mikroszolgáltatásokkal kompatibilis alkalmazásfejlesztési keretrendszer az ASP.NET Core számára. Teljes architektúrát és erős infrastruktúrát biztosít ahhoz, hogy a saját üzleti kódjára összpontosítson, ahelyett, hogy megismételné magát minden új projektnél. A szoftverfejlesztés bevált gyakorlatain és a már ismert népszerű eszközökön alapul.

Az ABP keretrendszer teljesen ingyenes, nyílt forráskódú és közösségvezérelt. Ingyenes témát és néhány előre beépített modult is biztosít (pl. személyazonosság-kezelés és bérlőkezelés).

", "VisitTheFrameworkVSCommercialDocument": "További információért keresse fel a következő linket: {1} ", @@ -178,6 +183,7 @@ "ChangingLicenseType": "Frissíthetem a licenctípusomat később?", "ChangingLicenseTypeExplanation": "Magasabb licencre frissíthet, ha az aktív licencidőszakon belül kifizeti a különbözetet. Ha magasabb licenccsomagra frissít, megkapja az új csomag előnyeit, de a licencfrissítés nem módosítja a licenc lejárati dátumát. Emellett új fejlesztői helyeket is hozzáadhat meglévő licencéhez, lásd \"Hány fejlesztő dolgozhat az ABP Commercialon?\"", "LicenseExtendUpgradeDiff": "Mi a különbség a licenc kiterjesztése és a frissítés között?", + "LicenseExtendUpgradeDiffExplanation": "Meghosszabbítás: A licensz meghosszabbításával/megújításával továbbra is prémium támogatást kap, valamint kisebb-nagyobb frissítéseket kap a modulokhoz és témákhoz. Emellett folytathatja az új projektek létrehozását. És továbbra is használhatja az ABP Suite-ot, amely felgyorsítja a fejlesztést. A licensz meghosszabbításakor 1 év hozzáadódik a licensz lejárati dátumához.
Frissítés: licenszének frissítésével magasabb licenszcsomagra lép fel, amely további előnyöket biztosít. Tekintse meg a licensz-összehasonlító táblázatot , hogy megtekinthesse a licensztervek közötti különbségeket. Másrészt, amikor frissít, a licensz lejárati dátuma nem változik! A licensz lejárati dátumának meghosszabbításához meg kell hosszabbítania a licenszet.", "LicenseRenewalCost": "Mennyibe kerül a licenc megújítása 1 év után?", "LicenseRenewalCostExplanation": "A normál csapatlicenc megújítási (meghosszabbítási) ára {0} USD, a normál üzleti licencé {1} USD, a normál vállalati licencé pedig {2} USD. Ha Ön már ügyfél, jelentkezzen be fiókjába, hogy áttekintse az elérhető megújítási árakat.", "HowDoIRenewMyLicense": "Hogyan újíthatom meg a jogosítványomat?", @@ -189,9 +195,19 @@ "IsSourceCodeIncludedExplanation4": "

Ha egy modul forráskódját belefoglalja a megoldásba, akkor maximális szabadságot biztosít a modul testreszabásához. Ekkor azonban nem lehet automatikusan frissíteni a modult, amikor új verzió jelenik meg.

A licencek egyike sem tartalmazza az ABP Suite forráskódját, amely egy külső eszköz, amely kódot generál Önnek és segít az Ön fejlesztéséhez.

A licenctípusok közötti egyéb különbségekért tekintse meg az árazási oldalt.

", "ChangingDevelopers": "Módosíthatom a szervezetem regisztrált fejlesztőit a jövőben?", "ChangingDevelopersExplanation": "Amellett, hogy új fejlesztőket ad hozzá a licenchez, további költségek nélkül módosíthatja a meglévő fejlesztőket is (eltávolíthat egy fejlesztőt, és hozzáadhat egy újat ugyanarra a helyre).", + "WhatHappensWhenLicenseEnds": "Mi történik, ha lejár a licenszidőm?", + "WhatHappensWhenLicenseEndsExplanation1": "Az ABP Kereskedelmi engedély örök érvényű licensz . A licensz lejárta után folytathatja a projekt fejlesztését. És nem köteles megújítani a jogosítványát. licenszéhez egyéves frissítés és támogatási terv tartozik. Ha továbbra is új funkciókat, teljesítménynöveléseket, hibajavításokat, támogatást szeretne kapni, és továbbra is használni szeretné az ABP Suite szolgáltatást, meg kell újítania a licenszet. Ha a licensze lejár, nem részesül a következő előnyökben:", + "WhatHappensWhenLicenseEndsExplanation2": "Az ABP Commercial használatával nem hozhat létre új megoldásokat, de a meglévő alkalmazásait örökre továbbfejlesztheti.", + "WhatHappensWhenLicenseEndsExplanation3": "Frissítéseket kaphat a MINOR verzión belüli modulokhoz és témákhoz (kivéve az RC vagy Preview verziókat). Például: ha egy modul v3.2.0-s verzióját használja, továbbra is kaphat frissítéseket a modul v3.2.x-hez (v3.2.1, v3.2.5... stb.). De nem kaphat frissítéseket a következő fő- vagy mellékverzióhoz (például v3.3.0, v3.3.3, 4.xx stb.). Például amikor a licensze lejárt, a legutóbbi kiadás a 4.4.3 volt, és később, amikor a 4.4.4-es és a 4.5.0-s verzió is megjelent, akkor hozzáférhet a v4.4.X-hez, de nem elérheti a v4.5.X.", + "WhatHappensWhenLicenseEndsExplanation4": "A licensz lejárta után nem telepíthet új modulokat és témákat az ABP Commercial platformhoz.", + "WhatHappensWhenLicenseEndsExplanation5": "Az ABP Suite nem használható.", + "WhatHappensWhenLicenseEndsExplanation6": "A prémium támogatást már nem kaphatja meg.", + "WhatHappensWhenLicenseEndsExplanation7": "Meghosszabbíthatja (megújíthatja) az engedélyét, ha továbbra is igénybe kívánja venni ezeket az előnyöket. Ha a licensz lejártát követő 1 hónapon belül meghosszabbítja a licenszet, a következő kedvezmények érvényesek: Team License {0}% kedvezmény, Business License {1}% kedvezmény, Enterprise License {2}% kedvezmény.", + "WhatHappensWhenLicenseEndsExplanation8": "Az Ön által generált ABP projekteket nem tároljuk a szervereinken. Ezért az Ön felelőssége a letöltött forráskód megőrzése. Amikor a licensze lejár, nincs mód a generált ABP projekt forráskódjának lekérésére.", "WhenShouldIRenewMyLicense": "Mikor kell megújítanom a jogosítványomat?", "WhenShouldIRenewMyLicenseExplanation": "Ha a licenc lejártát követő 1 hónapon belül megújítja a licencet, a következő kedvezmények érvényesek: Csapatlicenc {0}% kedvezmény, Üzleti licenc {1}% kedvezmény, Vállalati licenc {2}% kedvezmény . Ha megújítja a licencet 1 hónappal a licenc lejárati dátuma után, a megújítási ár megegyezik a licenc vásárlási árával, és nem jár kedvezmény a megújításra.", "TrialPlan": "Van próbaterv?", + "TrialPlanExplanation": "14 napos próbaidővel rendelkezik az ABP Commercial csapat licenszéhez. További információért látogasson el ide . Továbbá a Team licenszekre 30 napos pénz-visszafizetési garanciát biztosítunk. Az első 30 napban csak visszatérítést kérhet. A Business és Enterprise licenszek esetén 30 napon belül 60%-os visszatérítést biztosítunk. Ennek az az oka, hogy a Business és Enterprise licenszek tartalmazzák az összes modul és téma teljes forráskódját.", "DoYouAcceptBankWireTransfer": "Elfogadja a banki átutalást?", "DoYouAcceptBankWireTransferExplanation": "Igen, elfogadunk banki átutalást.
Miután banki átutalással elküldte a licencdíjat, küldje el nekünk e-mailben az accounting@abp.io címre nyugtát és a kért engedély típusát. Nemzetközi bankszámlánk információi:", "HowToUpgrade": "Hogyan lehet frissíteni a meglévő alkalmazásokat, ha új verzió érhető el?", @@ -348,7 +364,6 @@ "WeWillSendYouADownloadLink": "Az e-könyv letöltésére szolgáló linket elküldtük a(z) {0} címre.
Ellenőrizze a beérkező leveleket/levélszemét/spam dobozokat!", "InvalidFormInputs": "Kérjük, írja be az űrlapon megadott érvényes adatokat.", "DDDBookEmailBody": "Köszönöm.
Könyve letöltéséhez kattintson ide.", - "FreeDDDEBook": "Ingyenes DDD e-könyv", "StartFree": "Kezdje ingyen", "FreeTrial": "Ingyenes próbaverzió", "AcceptsMarketingCommunications": " Igen, szeretnék kapni az ABP Commercial marketingkommunikációit.", @@ -359,25 +374,375 @@ "CompanySize": "Cég Méret", "Next": "Következő", "StartTrial": "Indítsa el az ingyenes próbaverziómat", - "ContactUsIssues": "Ha bármilyen problémája van, lépjen kapcsolatba velünk", + "ContactUsQuestions": "Ha bármilyen kérdése van, forduljon hozzánk", "TrialActivatedWarning": "Kedves {0}! Egy felhasználó csak 1 ingyenes próbaidőszakra jogosult. Már felhasználta a próbaidőszakot.", + "ActivationRequirement": "Még egy lépés választja el a próbaidőszak megkezdésétől.
Az adatok ellenőrzése után aktiváljuk a licenszét. A licensz aktiválása után e-mailt küldünk a következő címre: {0} . Ne aggódjon, ez a folyamat nem tart sokáig!", "SaveAndDownload": "Mentés és letöltés", "CompanyNameValidationMessage": "A cég neve túl hosszú!", "AddressValidationMessage": "A cím túl hosszú!", "TaxNoValidationMessage": "Az ADÓ/ÁFA szám túl hosszú!", "NotesValidationMessage": "A megjegyzésmező túl hosszú!", "CheckYourBillingInfo": "Számlát csak egyszer készíthet! A számla elkészítése előtt ellenőrizze számlázási adatait.", - "Volo.AbpIo.Commercial:030000": "Már felhasználta a próbaidőszakot.", - "Volo.AbpIo.Commercial:030001": "Ez a szervezetnév már létezik.", "StartYourFreeTrial": "Indítsa el az ingyenes próbaidőszakát", "TrialLicenseModelInvalidErrorMessage": "A következő mezők egyike érvénytelen: Ország neve, vállalat mérete, iparág vagy felhasználás célja.", "Trial": "Próba", "Purchased": "Vásárolt", - "PurchaseLicense": "Vásárlási engedély", + "PurchaseNow": "Vásároljon most", "PurchaseTrialLicenseMessage": "A licence lejárati dátuma: {0}.
Ha továbbra is használni szeretné az ingyenes próbaidőszak alatt létrehozott projekteket, módosítania kell a licenckulcsokat az appsettings.secrets.json fájlokban. Itt van a licenckulcsod:", "TrialLicenseExpireMessage": "Ön a próbalicencet használja, és a próbalicence a következő napon lejár: {0}.", "TryForFree": "Próbáld ki ingyen", "TrialLicenseExpiredInfo": "A próbalicensz időszaka lejárt!", - "CommercialNewsletterConfirmationMessage": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi irányelveket ." + "DowngradeLicensePlan": "Leválthatok alacsonyabb licenszcsomagra a jövőben?", + "DowngradeLicensePlanExplanation": "Nem módosíthatja a meglévő licenszcsomagot. De vásárolhat egy új alacsonyabb licenszcsomagot, és folytathatja a fejlesztést az új licenszen. Miután megvásárolta az alacsonyabb licenszet, csak be kell jelentkeznie az új licenszcsomagba az ABP CLI paranccsal: ` abp login -o `.", + "LicenseTransfer": "Egy licenszet át lehet adni egyik fejlesztőről a másikra?", + "LicenseTransferExplanation": "Igen! licensz vásárlásakor Ön lesz a licensz tulajdonosa, így hozzáférhet a szervezetkezelési oldalhoz. Egy szervezetnek tulajdonosi és fejlesztői szerepei vannak. A tulajdonosok kezelhetik a fejlesztői helyeket, és fejlesztőket rendelhetnek hozzá. Minden kijelölt fejlesztő az ABP CLI paranccsal jelentkezik be a rendszerbe, és fejlesztési és támogatási jogosultságokkal rendelkezik.", + "UserOwnerDescription": "A szervezet „Tulajdonosa” a fiók adminisztrátora. licenszek vásárlásával és fejlesztők kiosztásával irányítja a szervezetet. A „Tulajdonos” nem írhat kódot az ABP Commercial projektekben, nem töltheti le az ABP mintaprojekteket, és nem tehet fel kérdéseket a támogatási webhelyen. Ha mindezeket meg akarja tenni, fel kell vennie magát fejlesztőként is.", + "UserDeveloperDescription": "A „fejlesztők” kódot írhatnak az ABP Commercial projektekben, letölthetik az ABP mintaprojekteket, és kérdéseket tehetnek fel a támogatási webhelyen. Másrészt a „Fejlesztők” nem kezelhetik ezt a szervezetet.", + "RemoveCurrentUserFromOrganizationWarningMessage": "Ön eltávolítja magát saját szervezetéből. A továbbiakban nem fogja tudni kezelni ezt a szervezetet, megerősíti?", + "RenewExistingOrganizationOrCreateNewOneMessage": "Az alábbi \"Meghosszabbítás most\" gomb(ok)ra kattintva megújíthatja szervezete(i) licenszét, és így 1 évvel meghosszabbíthatja a licensz lejárati dátumát. Ha folytatja a fizetést, új szervezete lesz. Új szervezetnél szeretné folytatni?", + "PurchaseTrialOrganizationOrCreateNewOneMessage": "Van próbaengedélyed. A próbalicensz megvásárlásához kattintson a Vásárlás most gombra. Ha folytatja a fizetést, új szervezete lesz. Új szervezetnél szeretné folytatni?", + "ExtendNow": "Hosszabbítsa meg most", + "CreateNewOrganization": "Hozzon létre egy új szervezetet", + "RenewLicenseEarly": "Ha korán megújítom a jogosítványomat, megkapom a teljes évet?", + "RenewLicenseEarylExplanation": "Ha megújítja a licenszet a licensz lejárati dátuma előtt, 1 év hozzáadódik a licensz lejárati dátumához. Például, ha licensze {0}-06-06-án lejár, és {0}-01-01-én újítja meg, az új licensz lejárati dátuma {1}-06-06.", + "OpenSourceWebApplication": "Nyílt forráskódú webes alkalmazás", + "CompleteWebDevelopment": "Komplett webfejlesztés", + "ABPFrameworkDescription": "Az ABP Framework egy komplett infrastruktúra modern webalkalmazások létrehozásához a szoftverfejlesztés bevált gyakorlatainak és konvencióinak követésével.", + "CommunityDescription": "Ossza meg tapasztalatait az ABP keretrendszerrel!", + "GetStarted": "Fogj neki", + "Views": "nézetek", + "LatestPosts": "Legutóbbi bejegyzések", + "PreBuiltApplication": "Előre beépített alkalmazás", + "DatabaseProviders": "Adatbázis-szolgáltatók", + "UIFrameworks": "UI keretrendszerek", + "UsefulLinks": "Hasznos Linkek", + "Platform": "Felület", + "CoolestCompaniesUseABPCommercial": "A legmenőbb cégek már használják az ABP Commercial-t.", + "UserInterface": "Felhasználói felület", + "APIGateway": "API átjáró", + "Microservice": "Mikroszolgáltatás", + "Database": "Adatbázis", + "Architecture": "Építészet", + "MicroserviceArchitectureExplanation": "Ez egy teljes megoldás-architektúra, amely több alkalmazásból, API-átjáróból, mikroszolgáltatásból és adatbázisokból áll, hogy a legújabb technológiákkal skálázható mikroszolgáltatási megoldást építsenek ki.", + "BusinessLogic": "Üzleti logika", + "DataAccessLayer": "Adatelérési réteg", + "Monolith": "Monolit", + "ModularArchitectureExplanation": "Ez az indítási sablon egy réteges, moduláris és DDD-alapú megoldás-architektúrát kínál tiszta és karbantartható kódbázis létrehozásához.", + "SeeDetails": "Lásd a részleteket", + "SeeDocumentation": "Nézze meg a Dokumentációt", + "Bs5Compatible": "Bootstrap 5 kompatibilis professzionális téma, tökéletes az adminisztrátori webhelyhez.", + "LeptonXTheme": "LeptonX téma", + "LeptonXDark": "LeptonX sötét", + "LeptonXLight": "LeptonX Light", + "LeptonXSemiDark": "LeptonX félsötét", + "BuiltOnBs5Library": "Bootstrap 5 könyvtárra építve", + "FullyCompatibleWithBs5": "100%-ban kompatibilis a Bootstrap 5 HTML struktúrájával és CSS osztályaival", + "ResponsiveAndMobileCompatible": "Reszponzív, mobil kompatibilis, RTL támogatás", + "ProvidesStylesForDatatables": "Stílusokat biztosít az adattáblákhoz", + "MultipleLayoutOptions": "Többféle elrendezési lehetőség", + "EasilyInstallAndUpgrade": "Könnyen telepíthető és frissíthető", + "SupportForum": "Támogatói fórum", + "TrustedBy": "Megbízható", + "OurPricing": "Áraink", + "Plans": "Tervek", + "NameSurname": "Név vezetéknév", + "Unspecified": "Meg nem határozott", + "LicenceType": "Licensz típus", + "LicenseDiscountWarning": "EZ A KEDVEZMÉNYOLDAL ALAPÉRTELMEZETT KEDVEZMÉNYKÓDOT HASZNÁL ÉS A VOLOSOFT FEJLESZTŐK SZÁMÁRA VAN. AZ ALÁBBI VÁSÁRLÁSI LINKEK NEM MŰKÖDNEK.", + "DiscountedLicenseExplanation": "Ezek a licenszárak kis startup vállalkozásokra, egyéni fejlesztőkre, hallgatókra, nonprofit szervezetekre és projektekre vonatkoznak!", + "General": "Általános", + "License": "Licensz", + "Development": "Fejlődés", + "Payment": "Fizetés", + "WatchExplainerVideo": "Találkozzunk! Nézze meg a magyarázó videót", + "LightDarkAndSemiDarkThemes": "Világos, Sötét és Félsötét", + "LeptonXThemeExplanation": "A Lepton Theme módosíthatja a témát a rendszerbeállításoknak megfelelően.", + "PRO": "PRO", + "WelcomeToABPCommercial": "Üdvözöljük az ABP Commercial oldalán!", + "YourAccountDetails": "Fiók adatai", + "OrganizationName": "Szervezet neve", + "AddDevelopers": "Adjon hozzá fejlesztőket", + "StartDevelopment": "Kezdje el a fejlesztést", + "CreateAndRunApplicationUsingStartupTemplate": "Ismerje meg, hogyan hozhat létre és futtathat új webalkalmazást az ABP Commercial indítási sablon használatával.", + "CommunityDescription2": "A Community.abp.io egy olyan hely, ahol az emberek megoszthatnak ABP-vel kapcsolatos cikkeket. Keressen cikkeket, oktatóanyagokat, kódmintákat, esettanulmányokat, és találkozzon Önnel azonos sávban élő emberekkel.", + "UseABPSuiteExplanation": "Az ABP Suite segítségével töltse le a modulok és témák forráskódját.", + "ManageModulesWithSuite": "ABP-moduljait a Suite segítségével is kezelheti.", + "LearnHowToInstallSuite": "Ismerje meg az ABP Suite telepítését és használatát.", + "SeeMore": "Többet látni", + "SeeLess": "Lásd Kevesebb", + "LayeredSolutionStructure": "Réteges megoldás szerkezete", + "LayeredSolutionStructureExplanation": "A megoldás a tartományvezérelt tervezési elvek és minták alapján rétegzett, hogy elkülönítse az üzleti logikát az infrastruktúrától és az integrációktól, és maximalizálja a kód karbantarthatóságát és újrafelhasználhatóságát. Az ABP Framework már tartalmaz absztrakciókat, alaposztályokat és útmutatókat a DDD tényleges megvalósításához az alkalmazásban.", + "MultipleUIOptions": "Több felhasználói felület opció", + "MultipleUIOptionsExplanation": "Szeretjük a felhasználói felület létrehozásának különböző módjait. Ez az indítási megoldás három különböző felhasználói felületi keretrendszert biztosít az üzleti alkalmazás számára.", + "MultipleDatabaseOptions": "Több adatbázis-beállítás", + "MultipleDatabaseOptionsExplanation": "Két adatbázis-szolgáltató lehetősége van (amellett, hogy mindkettőt egyetlen alkalmazásban használhatja). Az Entity Framework Core segítségével bármilyen relációs adatbázissal dolgozhat, és opcionálisan használja a Dappert, ha alacsony szintű lekérdezéseket kell írnia a jobb teljesítmény érdekében. A MongoDB egy másik lehetőség, ha dokumentum alapú NoSQL adatbázist kell használnia. Noha ezek a szolgáltatók jól integráltak, absztraktáltak és előre konfiguráltak, valójában bármilyen adatbázis-rendszerrel kapcsolatba léphet, amelyet a .NET-tel használhat.", + "ModularArchitectureExplanation2": "A a legfőbb szempont az ABP.IO platformon. Az alkalmazás összes funkciója jól elkülönített opcionális modulokra van felosztva. Az indítási megoldás már előre telepítve tartalmazza az alapvető ABP Commercial modulokat . Saját modulokat is létrehozhat, hogy moduláris rendszert építsen fel saját alkalmazásához.", + "MultiTenancyForSaasBusiness": "Többérlős felépítés az Ön SaaS-üzleteihez", + "MultiTenancyForSaasBusinessExplanation": "Az ABP Commercial teljes körű, többbérlős rendszert biztosít SaaS (Software-as-a-Service) rendszereinek létrehozásához. Lehetővé teszi a bérlők számára, hogy megosszák vagy rendelkezzenek saját adatbázisokkal az on-the-fly adatbázis-létrehozó és migrációs rendszerrel.", + "MicroserviceStartupSolution": "Mikroszolgáltatás indítási megoldás", + "MicroserviceArchitectureExplanation2": "Beszerezheti következő mikroszolgáltatási rendszeréhez, hogy kihasználhassa az előre elkészített alapmegoldást és tiszta élményt.", + "PreIntegratedTools": "Előre integrálva a népszerű eszközökbe", + "PreIntegratedToolsExplanation": "A megoldás már integrálva van az ipari szabványos eszközökbe és technológiákba, miközben Ön bármikor módosíthatja azokat, és integrálhatja kedvenc eszközeibe.", + "SingleSignOnAuthenticationServer": "Egyszeri bejelentkezéses hitelesítési kiszolgáló", + "SingleSignOnAuthenticationServerExplanation": "A megoldás rendelkezik egy hitelesítési kiszolgálóalkalmazással, amelyet a többi alkalmazás egyszeri bejelentkezési kiszolgálóként használ API hozzáférés-kezelési szolgáltatásokkal. Az IdentityServeren alapul.", + "WebAppsWithGateways": "2 webalkalmazás 2 API átjáróval", + "WebAppsWithGatewaysExplanation": "A megoldás két webalkalmazást tartalmaz, mindegyik rendelkezik dedikált API-átjáróval (BFF - Backend For Frontend minta).", + "BackOfficeApplication": "Back Office alkalmazás", + "BackOfficeApplicationExplanation": "A rendszer tényleges webalkalmazása, több felhasználói felületi keretbeállítással. Bármilyen üzleti alkalmazást létrehozhat.", + "LandingWebsite": "Landing webhely", + "LandingWebsiteExplanation": "Általános célú/nyilvános webhely, amely többféle célra is használható, például a cég bemutatására, termékei értékesítésére stb.", + "ABPFrameworkEBook": "Mastering ABP Framework e-book", + "MasteringAbpFrameworkEBookDescription": "Tartalmazza az ABP kereskedelmi licenszét", + "FullName": "Teljes név", + "LicenseTypeNotCorrect": "A licensz típusa nem megfelelő!", + "Trainings": "Képzések", + "ChooseTrainingPlaceholder": "Válaszd ki a képzést...", + "DoYouNeedTrainings": "Szüksége van egy ilyen képzésre?", + "DoYouNeedTraining": "Szüksége van a(z) {0} képzésére?", + "GetInTouchUs": "Vegye fel velünk a kapcsolatot", + "ForMoreInformationClickHere": "További információért kattintson ide.", + "IsGetOnboardingTraining": "Szeretnél bevezető és webalkalmazás-fejlesztő tréningen részt venni?", + "OnboardingWebApplicationDevelopmentTrainingMessage": "Képzési naptárának ütemezéséhez a szervezet létrehozása után vegye fel a kapcsolatot a következővel: {0}", + "CustomPurchaseMessage": "A következő lépéshez kattintson a(z) {0} gombra, és lépjen kapcsolatba velünk.", + "Note": "jegyzet", + "AdditionalNote": "Kiegészítő megjegyzés", + "OnboardingTrainingFaqTitle": "Van ABP bevezető képzése?", + "OnboardingTrainingFaqExplanation": "Igen, van ABP képzési szolgáltatásunk, amely segít abban, hogy ABP-projektjét gyorsan elindítsa. Megtanulja az ABP-t az ABP törzscsoport egyik tagjától, és elsajátítja az ABP-projekt megkezdéséhez szükséges készségeket. A bevezető tréningen elmagyarázzuk, hogyan állítsa be fejlesztői környezetét, telepítse a szükséges eszközöket, készítsen egy teljesen működőképes CRUD oldalt. A tréning élőben lesz, a Zoom alkalmazást használjuk, valamint nyitottak vagyunk más online találkozóplatformok használatára is. A képzés nyelve angol lesz. A foglalkozások során felteheti kérdéseit az ABP-vel kapcsolatban is. Mindkét fél számára megfelelő időpontot és dátumot tervezünk. További információért lépjen kapcsolatba velünk az info@abp.io címen .", + "AddBasket": "Kosárba helyez", + "SendTrainingRequest": "Képzési kérelem küldése", + "OnlyEnglishVersionOfThisDocumentIsTheRecentAndValid": "* A dokumentum angol nyelvű változata a legfrissebb, és minden vitában az angol verzió az irányadó.", + "Pricing_Page_Title": "Tervek és árak", + "Pricing_Page_Description": "Válassza ki azokat a szolgáltatásokat és funkciókat, amelyekre ma vállalkozásának szüksége van. Vásároljon ABP kereskedelmi licenszet, és hozzon létre korlátlan számú projektet.", + "Pricing_Page_HurryUp": "Siess!", + "Pricing_Page_BuyLicense": "Vásároljon licenszet 2021-es árakon január 16-ig!", + "Pricing_Page_ValidForExistingCustomers": "Meglévő ügyfelekre és licenszmegújításokra is érvényes.", + "Pricing_Page_Hint1": "A licensz ára bizonyos számú fejlesztői helyet tartalmaz. Ha több fejlesztője van, bármikor vásárolhat további üléseket.", + "Pricing_Page_Hint2": "További fejlesztői licenszeket vásárolhat most vagy a jövőben. A licenszek szék alapúak, így áthelyezhet egy helyet egy fejlesztőtől a másikhoz.", + "Pricing_Page_Hint3": "licenszével korlátlan számú különböző terméket fejleszthet.", + "Pricing_Page_Hint4": "Az ABP Suite egy olyan eszköz, amely segíti a fejlesztést a termelékenység javítása érdekében. Támogatja a CRUD oldalak generálását és új projektek létrehozását.", + "Pricing_Page_Hint5": "Az összes előre beépített modult használhatja alkalmazásaiban.", + "Pricing_Page_Hint6": "Az összes előre elkészített témát használhatja alkalmazásaiban.", + "Pricing_Page_Hint7": "Az indítási sablon a Visual Studiohoz, amellyel azonnal elkezdheti a projektet. Az összes alapvető modul hozzáadásra és előre konfigurálva van az Ön számára.", + "Pricing_Page_Hint8": "Az ABP-keretrendszer elsajátítása című e-könyv elmagyarázza, hogyan lehet .NET-megoldásokat megvalósítani a legjobb gyakorlatokkal. A könyvet az Amazon.com oldalon árusítják, és licensze keretein belül ingyenesen letöltheti a könyvet.", + "Pricing_Page_Hint9": "Bármely modul forráskódja letölthető. Érdemes lehet a forráskódot hozzáadni a megoldáshoz, hogy radikális változtatásokat hajtson végre, vagy biztonsági okokból egyszerűen megtarthatja magának.", + "Pricing_Page_Hint10": "A licenszek életre szólnak. Ez azt jelenti, hogy örökké folytathatja az alkalmazás fejlesztését. A legfrissebb verzióhoz való hozzáférés és a támogatás megszerzése a licensz időtartamán belül biztosított (1 év, ha nem újítja meg).", + "Pricing_Page_Hint11": "Nincs korlátozás a telepítésre! Tetszőleges számú kiszolgálóra telepítheti, beleértve a felhőszolgáltatásokat vagy a helyszíni szolgáltatásokat.", + "Pricing_Page_Hint12": "A modulokat, témákat és eszközöket frissítheti a legújabb verzióra az aktív licenszidőszakon belül. A licensz lejárta után meg kell újítania, hogy továbbra is frissítéseket kaphasson a hibajavításokról, új funkciókról és fejlesztésekről.", + "Pricing_Page_Hint13": "A prémium támogatást egy évre kaphatja meg (a licensz megújításával meghosszabbítható).", + "Pricing_Page_Hint14": "A csapat- és üzleti licenszeknek van incidens/kérdésszám korlátja. Ha további fejlesztői licenszeket vásárol, az incidensek korlátja fejlesztőnként {0}-kal (a csapatlicensz esetén) vagy {1}-kal (az üzleti licensz esetében) nő.", + "Pricing_Page_Hint15": "Csak az Enterprise licensz tartalmaz magántámogatást. Küldhet e-mailt közvetlenül az ABP csapatának, vagy kérdéseket tehet fel a support.abp.io oldalon privát jegy opcióval. A privát jegyek nem láthatók a nyilvánosság számára.", + "Pricing_Page_Hint16": "Letöltheti az összes ABP téma forráskódját. Érdemes lehet a forráskódot hozzáadni a megoldáshoz, hogy radikális változtatásokat hajtson végre, vagy biztonsági okokból egyszerűen megtarthatja magának.", + "Pricing_Page_Testimonial_1": "Az ABP Commercial lehetővé tette az SC Ventures számára, hogy 9 hónapon belül egy banki szintű többbérlős silo-adatbázis SaaS-platformot szállítson a követelések/tartozások ellátási láncának támogatására, jelentős értékű számlák több integrált horgonyból történő finanszírozásához. Az ABP modularitása lehetővé tette a csapat számára, hogy rekordidő alatt szállítsa, átadja az összes VAPT-t, és a konténeres mikroszolgáltatások veremét teljes CI/CD-n és csővezetékeken keresztül üzembe helyezze.", + "Pricing_Page_Testimonial_2": "Látjuk az ABP Commercial használatának értékét az egyedi fejlesztési projektek általános költségeinek csökkentésére. És a csapat képes egységesíteni a kódmintát a különböző projektfolyamokban. Több lehetőséget látunk a keretrendszerben arra, hogy a korábbinál gyorsabban építsünk új funkciókat. Bízunk benne, hogy folyamatosan látni fogjuk az ABP Commercial kihasználásának értékét.", + "Pricing_Page_Testimonial_3": "Szeretjük az ABP-t. Nem kell mindent a nulláról írnunk. A beépített funkciókból indulunk ki, és csak arra koncentrálunk, amit valóban meg kell írnunk. Ezenkívül az ABP jól felépített, és a kód kiváló minőségű, kevesebb hibával. Ha magunknak kellene megírnunk mindent, amire szükségünk van, akkor lehet, hogy éveket kellene töltenünk. Még egyszer, amit szeretünk, az az, hogy az új verzió, a hibajavítás vagy a fejlesztés nagyon hamar, minden második héten megjelenik. Nem várunk túl sokáig.", + "Pricing_Page_Testimonial_4": "Az ABP Commercial egy fantasztikus termék, amelyet ajánlunk. Kereskedelmi termékek, amelyeket ügyfeleink számára egyetlen konfigurálható platformon értékesíthetnek. Az ugrásszerű indítás, amelyet a keret és az eszközök biztosítanak bármely csapat számára, minden centet megér. Az ABP Commercial volt a legjobban megfelelő az igényeinknek.", + "Pricing_Page_Testimonial_5": "Az ABP Framework nem csak keretrendszer, hanem útmutató is a projektfejlesztéshez/menedzsmenthez, mert DDD, GenericRepository, DI, Microservice és Modularity képzést biztosít. Ha magát a keretrendszert nem is használja, fejlesztheti magát a docs.abp.io-val, amely jól és professzionálisan elkészített (OpenIddict, Redis, Quartz stb.) leírás. Mivel sok minden előre be van építve, jelentősen lerövidíti a projektfejlesztési időt (például bejelentkezési oldal, kivételkezelés, adatszűrés, aéapadatok, audit naplózás, lokalizáció, automatikus API vezérlő stb.). Alkalmazásunkból példaként a Helyi Event Bus-t használtam az állomány karbantartására. Így a rendelési mozgásokat eseményekkel tudom a készlethez igazítani. Csodálatos, hogy nem veszítünk időt a CreationTime-ra, a CreatorId-re. Automatikusan feltöltődnek.", + "AbpBookDownloadArea_ClaimYourEBook": "Igényelje Mastering ABP Framework e-könyvét", + "AddMemberModal_Warning_1": "Ha a hozzáadni kívánt felhasználónév nem létezik a rendszerben, kérje meg csapattagját, hogy regisztráljon a (z) {0} webhelyen, és ossza meg fiókja felhasználónevét Önnel.", + "MyOrganizations_Detail_WelcomeMessage": "Üdvözöljük szervezetében, {0}", + "MyOrganizations_Detail_OrganizationManagement": "Szervezetmenedzsment", + "OrganizationDisplayName": "Szervezet megjelenített neve", + "MyOrganizations_Detail_EditDisplayName": "Megjelenítési név szerkesztése", + "MyOrganizations_Detail_UpgradeYourLicense": "Frissítse licenszét", + "MyOrganizations_Detail_LicenseStartAndExpiryDate": "licensz kezdő dátuma – lejárati dátum", + "MyOrganizations_Detail_OwnerRightInfo": "Ön {1} tulajdonosi jogának {0}-át használja.", + "MyOrganizations_Detail_CopyApiKey": "Másolja ki a kulcsot", + "MyOrganizations_Detail_ApiKeyDescription": "Az API-kulcs a(z) {1} webhelyen tárolt PRO-csomagok tokenje.", + "MyOrganizations_Detail_YourPrivateNugetSource": "Az Ön privát NuGet-forrása {0}", + "MyOrganizations_Detail_PrivateNugetSourceWarning": "Ez automatikusan hozzáadódik feedként a NuGet.Config-hoz az ABP-megoldásban. Ne ossza meg privát kulcsát illetéktelen felhasználókkal!", + "MyOrganizations_Detail_DeveloperSeatInfo": "Ön {1} fejlesztői helyéből {0}-ot használ.", + "NeedMoreSeatsForYourTeam": "Több hozzáférésre van szüksége a csapatának?", + "MyOrganizations_Detail_PricePerYear": "{0}/év", + "MyOrganizations_Detail_PurchaseDeveloperSeats": "Vásároljon fejlesztői hozzáféréseket", + "Invoices": "Számlák", + "RequestInvoice": "Kérjen számlát", + "OrderNumber": "Rendelésszám", + "Date": "Dátum", + "Products": "Termékek", + "TotalPrice": "Teljes ár", + "ThereIsNoInvoice": "Nincs számla", + "MyOrganizations_Detail_PaymentProviderInfo": "Ha licenszét a (z) {0} átjárón keresztül vásárolta, az elküldi a PDF számlát az Ön e-mail címére, lásd: {0} számlázás.", + "MyOrganizations_Detail_PayUInfo": "Ha a PayU átjárón keresztül vásárolt, kattintson a \"Számla kérése\" gombra, és töltse ki a számlázási adatokat.", + "MyOrganizations_Detail_ConclusionInfo": "Számlakérését {0} munkanapon belül lezárjuk.", + "ExtendYourLicense": "Hosszabbítsa meg {0} licenszét", + "Continue": "Folytatni", + "PurchaseLicense": "Vásárlási engedély", + "DownloadInvoiceModal_DownloadInvoice": "Számla letöltése", + "DownloadInvoiceModal_SaveInformationOnlyOnce": "Számlázási adatait csak egyszer mentheti el.", + "InvoiceModal_EnterCompanyName": "Adja meg cégének hivatalos nevét...", + "InvoiceModal_EnterCompanyAddress": "Adja meg cége jogi címét...", + "InvoiceModal_EnterTaxNumber": "Adja meg adószámát, ha van...", + "RequestInvoiceModal_EnterNotes": "Írja be a számlával kapcsolatos extra üzenetet...", + "PrePayment_PayWithIyzico": "Iyzico-val fog fizetni", + "ContinueToCheckout": "Tovább a Pénztárhoz", + "PrePayment_IyzicoRedirectionInfo": "A vásárlás biztonságos befejezéséhez átirányítjuk az Iyzico Payment Gateway oldalra.", + "PrePayment_IyzicoAcceptVisaAndMasterCard": "Az Iyzico Visa és MasterCard kártyákat fogad el.", + "Purchase": "Vásárlás", + "AcceptTermsAndConditions": "Elolvastam, megértettem és elfogadom az adatvédelmi szabályzatot , a feltételeket és az EULA-t.", + "AcceptTermsAndConditionsWarningMessage": "Kérjük, fogadja el az adatvédelmi szabályzatot és a feltételeket", + "SelectGatewayToContinue": "Kérjük, válasszon egy átjárót a folytatáshoz!", + "GatewaySelection_SelectGateway": "Válasszon fizetési átjárót", + "GatewaySelection_RedirectionMessage": "Ezután átirányítjuk a kiválasztott fizetési átjáró webhelyére a tranzakcióhoz.", + "PaymentSucceed_PaymentSuccessMessage": "Sikeres fizetés", + "PaymentSucceed_ThanksForPurchase": "Köszönjük a vásárlást!", + "PaymentSucceed_CreateYourOrganization": "Hozd létre a szervezetedet", + "PaymentSucceed_AddMeAsDeveloper": "Én is fejlesztő vagyok, vegyen fel fejlesztőként a szervezetembe.", + "PaymentSucceed_CreateOrganization": "Szervezet létrehozása", + "PaymentSucceed_OrganizationDescription": "Egy szervezet fejlesztőkből és tulajdonosokból áll. A fejlesztők olyan felhasználók, akik kódot írnak az ABP projektben, és részesülnek a {1} webhely előnyeiből. A tulajdonosok olyan felhasználók, akik fejlesztői helyeket osztanak ki és kezelik a licenszelést.", + "PaymentSucceed_ViewOrganization": "Kattintson ide a szervezet megtekintéséhez", + "Purchase_TotalAnnualPrice": "ÖSSZESEN (éves díj)", + "Purchase_TrainingPrice": "Képzési ár", + "Purchase_OnboardingTraining": "ABP Onboarding és webalkalmazás-fejlesztés élő képzés", + "TotalDeveloperPrice": "Teljes fejlesztői ár", + "Purchase_PricePerDeveloper": "{0} {1} fejlesztőnként", + "Purchase_IncludedDeveloperInfo": "{0} {1} tartalmazza.", + "Purchase_LicenseExtraDeveloperPurchaseMessage": "A(z) {0} licensz {1} fejlesztőt tartalmaz. Hozzáadhat további fejlesztőket most vagy később.", + "StartupTemplates_Page_Title": "Az indítási sablonok", + "StartupTemplates_Page_Description": "Az ABP Commercial lehetővé teszi, hogy bármilyen bonyolultságú megoldást készítsen. Két fő előre beépített indítási megoldást kínál. Kiválaszthatja az igényeinek megfelelőt, és ráépítheti saját egyedi megoldását.", + "MicroserviceStartupSolutionForDotnet": "Microservice Startup Solution for .NET", + "MonolithSolutionForDotnet": "Monolith (moduláris) megoldás .NET-hez", + "TrainingDetailsHeaderInfo_TrainingHour": "{0} óra", + "Trainings_Content": "A képzés tartalma", + "Trial_Page_StartYourFreeTrial": "Indítsa el az ingyenes próbaverziót", + "TrialLicenseFeatures": "Élvezheti az ABP összes kereskedelmi funkcióját", + "TrialPeriodDays": "{0} napos csapatengedéllyel rendelkezik", + "TrialForumSupportIncident": "{0} fórumtámogatási incidense lesz", + "Contact_Page_Title": "Lépjen kapcsolatba az ABP fejlesztési csapatával", + "Contact_Page_Description": "Lépjen kapcsolatba az ABP Development csapatával, ha segítségre van szüksége, vagy ossza meg gondolatait és véleményét! Az ABP támogatási csapata készen áll a segítségére.", + "Demo_Page_Title": "Hozzon létre egy demót", + "Demo_Page_Description": "Hozzon létre egy ingyenes demót az ABP Commercial indítósablon segítségével létrehozott mintaalkalmazás megtekintéséhez. Ne ismételje magát a gyakori alkalmazási követelmények miatt.", + "Discounted_Page_Title": "Kedvezményes ár", + "Discounted_Page_Description": "Válassza ki azokat a szolgáltatásokat és funkciókat, amelyekre ma vállalkozásának szüksége van. Vásároljon ABP kereskedelmi licenszet, és hozzon létre korlátlan számú projektet", + "Faq_Page_Title": "Gyakran Ismételt Kérdések (GYIK)", + "Faq_Page_Description": "Van kérdésed? Keressen gyakran ismételt kérdésekre, vagy tegyen fel nekünk kérdést a kapcsolatfelvételi űrlap segítségével.", + "Faq_Page_SwiftCode": "Swift kód", + "Faq_Page_BankName": "A bank neve", + "Faq_Page_AccountName": "Felhasználónév", + "Faq_Page_AccountNumber": "Számlaszám", + "Faq_Page_Currency": "Valuta", + "Faq_Page_VatNumber": "Adószám", + "Faq_Page_OtherCurrenciesInfo": "A többi pénznemhez lásd az összes fiókot", + "ModuleDetail_Page_Title": "Modul részletei – {0}", + "ProjectCreatedSuccess_Page_Title": "A projekt létrehozva", + "ProjectCreatedSuccess_Page_Description": "ABP projektje sikeresen létrejött!", + "Suite_Page_Title": "ABP Suite – CRUD oldalak létrehozása", + "Suite_Page_Description": "Az ABP Commercial gyors alkalmazásfejlesztési eszközöket biztosít a fejlesztők termelékenységének növelése érdekében. Az ABP Suite segítségével könnyedén hozhat létre CRUD oldalakat.", + "Themes_Page_Title": "Modern és funkcionális felhasználói felület témák", + "Themes_Page_Description": "Az ABP Commercial több professzionális, modern felhasználói felület témát kínál. Hozzon létre egy ingyenes bemutatót, hogy gyorsan áttekintse, hogyan néz ki a felhasználói felület.", + "Tools_Page_Title": "Gyors alkalmazásfejlesztő eszközök", + "Tools_Page_Description": "Az ABP Commercial gyors alkalmazásfejlesztési eszközöket biztosít a fejlesztők termelékenységének növelése érdekében. Az ABP Suite segítségével könnyedén hozhat létre CRUD oldalakat.", + "DeveloperPrice": "Fejlesztői ár", + "AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} fejlesztő", + "LicenseRemainingDays": "{0} napig", + "ExtendPaymentInfoSection_Description": "A licensz meghosszabbításával/megújításával továbbra is prémium támogatást kap. Emellett kisebb-nagyobb frissítéseket is kaphat a modulokhoz és témákhoz. Folytathatja új projektek létrehozását. És továbbra is használhatja az ABP Suite -ot, amely felgyorsítja a fejlesztést.", + "LicenseRenewalPrice": "Licensz megújítási ára", + "LicensePrice": "Licensz ára", + "TrialLicensePaymentInfoSection_Description": "Licensz vásárlása: licensz megvásárlásával továbbra is prémium támogatást kap . Emellett kisebb-nagyobb frissítéseket is kaphat a modulokhoz és témákhoz. Folytathatja új projektek létrehozását. És továbbra is használhatja az ABP Suite -ot, amely felgyorsítja a fejlesztést.
Tekintse meg a licensz-összehasonlító táblázatot a licensztípusok közötti különbségek ellenőrzéséhez.", + "SelectTargetLicense": "Válassza a Target License lehetőséget", + "UpgradePaymentInfoSection_ExtendMyLicenseForOneYear": "Igen, meghosszabbítom az engedélyem lejárati dátumát 1 évvel.", + "UpgradePaymentInfoSection_WantToExtendLicense": "Szeretné meghosszabbítani licenszét további {0} évre?", + "UpgradePaymentInfoSection_UpgradingWillNotExtendLicense": "A frissítés nem hosszabbítja meg a licensz lejárati dátumát!", + "UpgradePaymentInfoSection_LicenseUpgradeDescription": "Licenszének frissítésével magasabb licensztípusra lép, amely további előnyökhöz juttatja Önt. Tekintse meg a licensz-összehasonlító táblázatot a licensztípusok közötti különbségek ellenőrzéséhez.", + "Landing_Page_CustomerStories": "Ügyféltörténetek", + "Landing_Page_OurGreatCustomers": "Nagyszerű vásárlóink", + "Landing_Page_WebApplicationFramework": "Web Application Framework", + "Landing_Page_WebDevelopmentPlatform": "Webfejlesztési platform", + "Landing_Page_CompleteWebDevelopmentPlatform": "Komplett webfejlesztési platform", + "Landing_Page_TryFreeDemo": "Próbáld ki az ingyenes demót", + "Landing_Page_StartingPointForWebApplications": "Az ASP.NET Core alapú webes alkalmazások kiindulópontja! A legjobb webfejlesztés érdekében az ABP-keretrendszeren alapul.", + "Landing_Page_AbpProvidesSoftwareInfrastructure": "Az ABP Framework szoftver infrastruktúrát biztosít kiváló webalkalmazások fejlesztéséhez a legjobb gyakorlatokkal.", + "Landing_Page_MicroserviceCompatibleArchitecture": "Mikroszolgáltatással kompatibilis architektúra", + "Landing_Page_PreBuiltApplicationModulesAndThemes": "Előre elkészített alkalmazásmodulok és témák", + "Landing_Page_MultiTenantArchitecture": "Több bérlős építészet", + "Landing_Page_MultiTenancyDescription": "SaaS alkalmazások egyszerűen! Integrált több bérlés az adatbázistól a felhasználói felületig.", + "Landing_Page_DDDIntroduction": "DDD minták és elvek alapján tervezték és fejlesztették. Réteges modellt biztosít az alkalmazáshoz.", + "Landing_Page_CrossCuttingConcernsInfo": "Teljes infrastruktúra az engedélyezéshez, érvényesítéshez, kivételkezeléshez, gyorsítótárazáshoz, auditnaplózáshoz, tranzakciókezeléshez és még sok máshoz.", + "Landing_Page_PreBuiltApplicationModules": "Előre beépített alkalmazásmodulok, amelyek a leggyakoribb webalkalmazási követelményeket tartalmazzák.", + "Landing_Page_ChatModule": "Csevegés", + "Landing_Page_DocsModule": "Dokumentumok", + "Landing_Page_FileManagementModule": "Dokumentumok", + "Landing_Page_CustomerStory_1": "Az ABP Commercial lehetővé tette az SC Ventures számára, hogy 9 hónapon belül egy banki szintű többbérlős silo-adatbázis SaaS-platformot szállítson a követelések/tartozások ellátási láncának támogatására, jelentős értékű számlák több integrált horgonyból történő finanszírozásához. Az ABP modularitása lehetővé tette a csapat számára, hogy rekordidő alatt szállítson, átadja az összes VAPT-t, és a konténeres mikroszolgáltatások veremét teljes CI/CD-n és csővezetékeken keresztül üzembe helyezze.", + "Landing_Page_CustomerStory_2": "Látjuk az ABP Commercial használatának értékét az egyedi fejlesztési projektek általános költségeinek csökkentésére. És a csapat képes egységesíteni a kódmintát a különböző projektfolyamokban. Több lehetőséget látunk a keretrendszerben arra, hogy a korábbinál gyorsabban építsünk új funkciókat. Bízunk benne, hogy folyamatosan látni fogjuk az ABP Commercial kihasználásának értékét.", + "Landing_Page_CustomerStory_3": "Szeretjük az ABP-t. Nem kell mindent a nulláról írnunk. A beépített funkciókból indulunk ki, és csak arra koncentrálunk, amit valóban meg kell írnunk. Ezenkívül az ABP jól felépített, és a kód kiváló minőségű, kevesebb hibával. Ha magunknak kellene megírnunk mindent, amire szükségünk van, akkor lehet, hogy éveket kellene töltenünk. Még egyszer, amit szeretünk, az az, hogy az új verzió, a hibajavítás vagy a fejlesztés nagyon hamar, minden második héten megjelenik. Nem várunk túl sokáig.", + "Landing_Page_CustomerStory_4": "Az ABP Commercial egy fantasztikus termék, amelyet ajánlunk. Kereskedelmi termékek, amelyeket ügyfeleink számára egyetlen konfigurálható platformon értékesíthetnek. Az ugrásszerű indítás, amelyet a keret és az eszközök biztosítanak bármely csapat számára, minden centet megér. Az ABP Commercial volt a legjobban megfelel az igényeinknek.", + "Landing_Page_AdditionalServices": "Egyedi vagy mennyiségi licensz, beépítés, élő képzés és támogatás, egyedi projektfejlesztés, meglévő projektek portolása és így tovább...", + "Landing_Page_IncludedDeveloperLicenses": "{0} fejlesztői licenszet tartalmaz", + "Landing_Page_SeeOnDemo": "Lásd a demóban", + "Landing_Page_LeptonThemes": "Lepton Themes", + "Landing_Page_AccountModuleDescription_1": "Ez a modul egy alkalmazás hitelesítési rendszerét valósítja meg;", + "Landing_Page_AccountModuleDescription_2": "Bejelentkezési oldalt biztosít a felhasználónévvel és jelszóval", + "Landing_Page_AccountModuleDescription_3": "Regisztrációs oldalt biztosít új fiók létrehozásához.", + "Landing_Page_AccountModuleDescription_4": "Elfelejtett jelszó oldalt biztosít a jelszó-visszaállítási hivatkozás e-mailben történő elküldéséhez.", + "Landing_Page_AccountModuleDescription_5": "E- mail megerősítési funkciót biztosít felhasználói felülettel.", + "Landing_Page_AccountModuleDescription_6": "Kétfaktoros hitelesítést valósít meg (SMS és e-mail).", + "Landing_Page_AccountModuleDescription_7": "Megvalósítja a felhasználói zárolást (zárolja a fiókot a beállított időtartamra, ha bizonyos számú sikertelen bejelentkezés történik érvénytelen hitelesítő adatok miatt egy bizonyos időintervallumon belül).", + "Landing_Page_AccountModuleDescription_8": "Megvalósítja az Identity Server hitelesítési kiszolgáló felhasználói felületét és funkcióit.", + "Landing_Page_AccountModuleDescription_9": "Lehetővé teszi a bérlők közötti váltást több bérlős környezetben.", + "Landing_Page_AccountModuleDescription_10": "Lehetővé teszi az alkalmazás felhasználói felületének nyelvének módosítását.", + "Landing_Page_AuditLoggingModuleDescription_1": "Ez a modul biztosítja a naplózási felületet a naplózási infrastruktúra számára. Lehetővé teszi az ellenőrzési naplóbejegyzések és entitásmódosítási naplók keresését, szűrését és megjelenítését.", + "Landing_Page_AuditLoggingModuleDescription_2": "Az ellenőrzési naplóbejegyzés kritikus adatokat tartalmaz minden ügyfélkérelemről:", + "Landing_Page_AuditLoggingModuleDescription_3": "URL, böngésző, IP-cím, ügyfélnév", + "Landing_Page_AuditLoggingModuleDescription_4": "A felhasználó", + "Landing_Page_AuditLoggingModuleDescription_5": "HTTP metódus, HTTP visszatérési állapotkód", + "Landing_Page_AuditLoggingModuleDescription_6": "Siker/kudarc, kivétel részletei, ha rendelkezésre állnak", + "Landing_Page_AuditLoggingModuleDescription_7": "Kérelem végrehajtásának időtartama", + "Landing_Page_AuditLoggingModuleDescription_8": "Az entitások létrejöttek, törölve vagy frissítve ebben a kérésben (módosított tulajdonságokkal).", + "Landing_Page_BloggingModuleDescription_1": "Ez a modul egy egyszerű blogot ad hozzá az ABP-alkalmazáshoz;", + "Landing_Page_BloggingModuleDescription_2": "Lehetővé teszi több blog létrehozását egyetlen alkalmazásban.", + "Landing_Page_BloggingModuleDescription_3": "Támogatja a Markdown formátumot.", + "Landing_Page_BloggingModuleDescription_4": "Lehetővé teszi megjegyzés írását egy bejegyzéshez.", + "Landing_Page_BloggingModuleDescription_5": "Lehetővé teszi címkék hozzárendelését a blogbejegyzésekhez.", + "Landing_Page_BloggingModuleDescription_6": "Tekintse meg a blog.abp.io webhelyet a blogolási modul élő példájaként.", + "Landing_Page_ChatModuleDescription_1": "Ez a modul valós idejű üzenetküldésre szolgál az alkalmazásban lévő felhasználók között.", + "Landing_Page_ChatModuleDescription_2": "Valós idejű üzenetküldés a chat oldalon.", + "Landing_Page_ChatModuleDescription_3": "Keressen felhasználókat az alkalmazásban új beszélgetésekhez.", + "Landing_Page_ChatModuleDescription_4": "Névjegylista a legutóbbi beszélgetésekhez.", + "Landing_Page_ChatModuleDescription_5": "Új üzenet értesítések, amikor a felhasználó egy másik oldalt néz.", + "Landing_Page_ChatModuleDescription_6": "Teljes olvasatlan üzenetek száma jelvény a menüikonon.", + "Landing_Page_ChatModuleDescription_7": "Az olvasatlan üzenetek száma az egyes beszélgetéseknél.", + "Landing_Page_ChatModuleDescription_8": "Lusta betöltött beszélgetések.", + "Landing_Page_DocsModuleDescription_1": "Ez a modul műszaki dokumentációs weboldalak létrehozására szolgál;", + "Landing_Page_DocsModuleDescription_2": "Beépített GitHub integráció : közvetlenül írhat és kezelhet dokumentumokat a GitHubon.", + "Landing_Page_DocsModuleDescription_3": "Verziókezelési támogatás közvetlenül integrálva a GitHub-kiadásokba.", + "Landing_Page_DocsModuleDescription_4": "Támogatja a több nyelvet (az alapértelmezett nyelv tartalék támogatásával).", + "Landing_Page_DocsModuleDescription_5": "Támogatja a Markdown és HTML formátumokat.", + "Landing_Page_DocsModuleDescription_6": "Navigációt és vázlatos részt biztosít.", + "Landing_Page_DocsModuleDescription_7": "Lehetővé teszi több projektdokumentáció tárolását egyetlen alkalmazásban.", + "Landing_Page_DocsModuleDescription_8": "Hivatkozások a fájlra a GitHubon, így bárki könnyedén hozzájárulhat a Szerkesztés linkre kattintva .", + "Landing_Page_DocsModuleDescription_9": "A GitHub-forrás mellett lehetővé teszi egy mappa egyszerű használatát dokumentációs forrásként.", + "Landing_Page_FileManagementModuleDescription_1": "Fájlok feltöltése, letöltése és rendezése hierarchikus mappastruktúrában.", + "Landing_Page_FileManagementModuleDescription_2": "Ez a modul fájlok feltöltésére, letöltésére és hierarchikus mappastruktúrába rendezésére szolgál. Több bérléssel is kompatibilis, és meghatározhatja bérlői teljes méretkorlátját.", + "Landing_Page_FileManagementModuleDescription_3": "Ez a modul a BLOB Storing rendszeren alapul, így különböző tárolószolgáltatókat használhat a fájltartalom tárolására.", + "Landing_Page_IdentityModuleDescription_1": "Ez a modul egy alkalmazás Felhasználó és szerepkör rendszerét valósítja meg;", + "Landing_Page_IdentityModuleDescription_2": "A Microsoft ASP.NET Core Identity könyvtárára épül.", + "Landing_Page_IdentityModuleDescription_3": "Szerepkörök és felhasználók kezelése a rendszerben. Egy felhasználónak több szerepe is lehet.", + "Landing_Page_IdentityModuleDescription_4": "Állítsa be az engedélyeket szerep- és felhasználói szinteken.", + "Landing_Page_IdentityModuleDescription_5": "Felhasználónként kétfaktoros hitelesítés és felhasználói kizárás engedélyezése/letiltása.", + "Landing_Page_IdentityModuleDescription_6": "Az alapvető felhasználói profil és jelszó kezelése.", + "Landing_Page_IdentityModuleDescription_7": "Igénytípusok kezelése a rendszerben, igények beállítása szerepkörökhöz és felhasználókhoz.", + "Landing_Page_IdentityModuleDescription_8": "Beállítási oldal a jelszó összetettségének , a felhasználói bejelentkezés, a fiók és a kizárás kezeléséhez.", + "Landing_Page_IdentityModuleDescription_9": "Támogatja az LDAP hitelesítést.", + "Landing_Page_IdentityModuleDescription_10": "E-mail és telefonszám ellenőrzést biztosít.", + "Landing_Page_IdentityModuleDescription_11": "Támogatja a közösségi bejelentkezési integrációkat (Twitter, Facebook, GitHub stb.).", + "Landing_Page_IdentityModuleDescription_12": "Szervezeti egységek kezelése a rendszerben.", + "Landing_Page_PaymentModuleDescription_1": "Integrációt biztosít a különböző fizetési átjárókhoz.", + "Landing_Page_PaymentModuleDescription_2": "Ez a modul integrációt biztosít a fizetési átjárókhoz, így könnyen kaphat fizetést ügyfeleitől.", + "Landing_Page_PaymentModuleDescription_3": "Ez a modul a következő fizetési átjárókat támogatja", + "Welcome_Page_UseSameCredentialForCommercialWebsites": "Ugyanazokat a hitelesítési adatokat használja a commercial.abp.io és a support.abp.io fájlokhoz .", + "WatchCrudPagesVideo": "Nézze meg a \"CRUD oldalak létrehozása az ABP Suite segítségével\" videót!", + "WatchGeneratingFromDatabaseVideo": "Nézze meg az \"ABP Suite: CRUD-oldalak generálása meglévő adatbázistáblákból\" videót!", + "WatchTakeCloserLookVideo": "Tekintse meg a „Nézze meg közelebbről a kódgenerálást: ABP Suite” videót!", + "ConfirmedEmailAddressRequiredToStartTrial": "A próbalicenc elindításához rendelkeznie kell egy megerősített e-mail címmel.", + "EmailVerificationMailNotSent": "Nem sikerült elküldeni az ellenőrző e-mailt.", + "GetConfirmationEmail": "Kattintson ide, ha megerősítő e-mailt szeretne kapni, ha még nem kapta meg.", + "WhichLicenseTypeYouAreInterestedIn": "Melyik licenctípus érdekli?", + "DontTakeOurWordForIt": "Ne fogadd el a szavunkat...", + "ReadAbpCommercialUsersWantYouToKnow": "Olvassa el, hogy az ABP Commercial felhasználói mit szeretnének tudni", + "Testimonial_ShortDescription_1": "Az ABP modularitása lehetővé tette, hogy a csapat időben teljesítsen.", + "Testimonial_ShortDescription_2": "Építsen új funkciókat gyorsabban, mint korábban.", + "Testimonial_ShortDescription_3": "A beépített funkciókból indulunk ki, és csak arra koncentrálunk, amit valóban meg kell írnunk.", + "Testimonial_ShortDescription_4": "Az ABP Commercial volt a legjobban megfelelő az igényeinknek.", + "OnlineReviewersOnAbpCommercial": "Online vélemények az ABP Commercial-ról", + "SeeWhatToldAboutAbpCommercial": "Tekintse meg, mit mondtak az ABP Commercialról, és írja le gondolatait, ha akarja." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json index 0d67d9cca5..26ab4a8b60 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -137,7 +137,6 @@ "Success": "成功", "WeWillReplyYou": "你的消息已经发送! 我们会在短时间内给你答复.", "GoHome": "回到主页面", - "Home": "主页", "CreateLiveDemo": "创建在线演示", "RegisterToTheNewsletter": "注册到时事简报以获取有关ABP.IO的消息,比如新发布的内容.", "EnterYourEmailOrLogin": "输入你的e-mail地址来创建你的演示或者使用你的已有账号登录.", @@ -184,6 +183,7 @@ "ChangingLicenseType": "将来更改我的许可类型吗?", "ChangingLicenseTypeExplanation": "你始终可以在同一许可中添加新的开发人员. 参阅 \"有多少开发者可以参与ABP商业版工作?\". 你还可以通过支付计算出的价格差来升级到更高的许可. 当你升级到更高的许可计划时,可以享受新计划的好处,但是许可升级不会更改许可的到期日期.", "LicenseExtendUpgradeDiff": "许可扩展和升级有什么区别?", + "LicenseExtendUpgradeDiffExplanation": "延长: 你通过延长/续费你的许可证,你将继续获得高级支持和主要或次要更新模块和主题。除此之外,你还将继续创建新的项目。并且你仍然可以使用ABP Suite,这将加快你的开发。当你延长你的许可证时,1年将添加到你的许可证到期日。
升级: 你通过升级你的许可证,你将晋升到更高的许可证方案,这将允许你获得额外的奖励。查看许可证比较表来查看许可证方案之间的差异。但是,当你升级时,你的许可证到期日不会更改!要延长你的许可证结束日期,你需要延长你的许可证。", "LicenseRenewalCost": "一年后的许可续期费用是多少?", "LicenseRenewalCostExplanation": "标准 Team 许可证的续订(扩展)价格为 ${0},标准 Business License 为 ${1},标准 Enterprise License 为 ${2}。 如果您已经是客户,请登录您的帐户查看可用的续订价格。", "HowDoIRenewMyLicense": "如何续费我的许可证?", @@ -195,10 +195,19 @@ "IsSourceCodeIncludedExplanation4": "

将模块的源代码包含到解决方案中,可以最大程度地自定义该模块. 但是当新版本发布时,将无法自动升级模块.

这些许可均不包含ABP Suite源代码,该源代码是一个外部工具,可以为你生成代码并帮助你进行开发

有关许可类型之间的其它差异查看定价页面.

", "ChangingDevelopers": "我将来可以更改我组织的注册开发人员吗?", "ChangingDevelopersExplanation": "除了将新的开发人员添加到你的许可中之外,你还可以更改现有的开发人员(可以删除一个开发人员并将新的开发人员添加到同一位置),而无需任何额外费用.", + "WhatHappensWhenLicenseEnds": "我的许可证期间结束时,会发生什么?", + "WhatHappensWhenLicenseEndsExplanation1": "你的ABP商业许可证是一个永久许可证。你的许可证到期后,你可以继续开发你的项目。并且你不需要续费你的许可证。你的许可证是一年更新和支持方案默认的。为了继续获得新的功能,性能改进,修复,支持和继续使用ABP Suite,你需要续费你的许可证。你的许可证到期后,你将失去以下功能:", + "WhatHappensWhenLicenseEndsExplanation2": "你不能使用ABP商业创建新的解决方案,但是你可以继续开发你现有的应用程序。", + "WhatHappensWhenLicenseEndsExplanation3": "你将能够获得模块和主题的更新(除了RC或预览版本)。例如,如果你使用模块的v3.2.0版本,你仍然可以获得v3.2.x版本的更新(v3.2.1,v3.2.5...等等)。但是,你不能获得下一个主要或次要版本的更新(例如v3.3.0,v3.3.3,4.x.x...等等)。例如,你的许可证到期后,最新的发行是v4.4.3,之后,它发布了4.4.4版本和4.5.0版本,你将能够访问v4.4.X,但你不能访问v4.5.X。", + "WhatHappensWhenLicenseEndsExplanation4": "你不能在你的许可证到期后安装ABP商业平台上添加的新模块和主题。", + "WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite。", + "WhatHappensWhenLicenseEndsExplanation6": "你不能再获得高级支持。", + "WhatHappensWhenLicenseEndsExplanation7": "如果你想继续获得这些奖励,你可以继续续费你的许可证。如果你在许可证到期后1个月内续费,将会应用以下折扣:团队许可证{0}%折扣,商业许可证{1}%折扣,企业许可证{2}%折扣。", "WhatHappensWhenLicenseEndsExplanation8": "您生成的 ABP 项目未存储在我们的服务器上。 因此,您有责任保留下载的源代码。 当您的许可证到期时,将无法获取您生成的 ABP 项目源代码。", "WhenShouldIRenewMyLicense": "我什么时候应该续订我的许可?", "WhenShouldIRenewMyLicenseExplanation": "如果您在许可证到期后 1 个月内续订许可证,将享受以下折扣:团队许可证 {0}% 折扣、商业许可证 {1}% 折扣、企业许可证 {2}% 折扣 . 如果您在许可证到期后 1 个月续订许可证,续订价格将与许可证购买价格相同,并且续订不会有折扣。", "TrialPlan": "你们有试用计划吗?", + "TrialPlanExplanation": "ABP商业团队许可证有14天的试用期。若要了解更多信息,请访问这里。此外,我们为团队许可证提供30天的金额返还保证。你只需要在30天内请求退款。商业和企业许可证将提供60%的金额返还保证。这是因为商业和企业许可证包含了所有模块和主题的全部源代码。", "DoYouAcceptBankWireTransfer": "你们接受银行电汇吗?", "DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇。
在通过银行转账发送许可费后,将您的收据和所需的许可类型通过电子邮件发送至accounting@abp.io。 我们的国际银行账户信息:", "HowToUpgrade": "可用新版本时如何升级现有应用程序?", @@ -365,7 +374,9 @@ "CompanySize": "公司规模", "Next": "下一个", "StartTrial": "开始我的免费试用", + "ContactUsQuestions": "如果你有任何问题,请联系我们", "TrialActivatedWarning": "亲爱的{0},用户只能享受 1 个免费试用期。您已经使用了试用期。", + "ActivationRequirement": "你已经距离开始你的试用还有最后一步。
检查你的信息后,我们将激活你的许可证。一旦你的许可证激活,我们将向{0}发送电子邮件。请不要担心,这个过程不会太久!", "SaveAndDownload": "保存和下载", "CompanyNameValidationMessage": "公司名称太长!", "AddressValidationMessage": "地址太长!", @@ -389,6 +400,7 @@ "UserDeveloperDescription": "“开发人员”可以在 ABP 商业版项目中编写代码,下载 ABP 示例项目,并在支持的网站上进行提问。 然而另一方面,“开发者”无法管理这个组织。", "RemoveCurrentUserFromOrganizationWarningMessage": "您正在将自己从自己的组织中移除。 您将无法再管理此组织,您确定吗?", "RenewExistingOrganizationOrCreateNewOneMessage": "您可以通过单击下面的 \"立即延长\"按钮来更新您组织的许可证,因此您可以将许可证到期日期延长 1 年。 如果您继续结帐,您将拥有一个新的组织。 您想继续开始新的组织许可吗?", + "PurchaseTrialOrganizationOrCreateNewOneMessage": "你有试用许可证。如果你想购买试用许可证,请点击“立即购买”按钮。如果你继续支付,你将会有一个新的组织。你想继续使用一个新的组织吗?", "ExtendNow": "立即延长", "CreateNewOrganization": "建立新的组织", "RenewLicenseEarly": "如果我提前更新我的许可证,我会得到一整年吗?", @@ -524,6 +536,7 @@ "Pricing_Page_Testimonial_2": "我们看到了使用 ABP Commercial 能减少定制开发项目开销的价值。 并且团队能够在不同的项目流中统一代码模式。 我们在框架中看到了能比以前更快地构建新功能的更多潜力。 我们相信我们将会持续地看到使用 ABP Commercial 的价值。", "Pricing_Page_Testimonial_3": "我们大爱 ABP。 我们不必从头开始编写所有内容。 我们从\"开箱即用\"的功能开始,只需关注我们真正需要编写的内容。 此外,ABP 架构良好,代码质量高,错误少。 如果我们需要自己来编写所需的一切,我们可能需要花费数年时间。 另一点让我们喜欢的是新版本、问题修复或改进每隔一周很快地就会出现。 我们不会等太久。", "Pricing_Page_Testimonial_4": "ABP 商业版 是一款很值得推荐的出色产品。 是在一个可配置的平台上为我们的客户推向市场的商业产品。 其框架和工具为任何团队提供的快速启动值得每一分钱。 ABP 商业版 最适合我们的需求。", + "Pricing_Page_Testimonial_5": "ABP Framework 不仅是一个框架,它还是项目开发/管理的指南,因为它提供了 DDD、GenericRepository、DI、微服务和模块化培训。 即使你不打算使用框架本身,你也可以通过 docs.abp.io 进行自己的开发,该文档已经做好了专业的准备(OpenIddict、Redis、Quartz 等)。 因为很多东西都是预先构建的,它大大缩短了项目开发时间(例如登录页面、异常处理、数据过滤、种子、审计日志、本地化、自动 API 控制器等)。 作为我们应用程序的一个示例,我使用本地事件总线进行库存控制。 因此我可以通过编写库存处理程序来管理订单移动。 不为 CreationTime,CreatorId 浪费时间真是太好了。 它们正在自动填充。", "AbpBookDownloadArea_ClaimYourEBook": "领取您的掌握ABP框架电子书", "AddMemberModal_Warning_1": "如果您尝试添加的用户名在系统中不存在,请让您的团队成员在 {0} 并与您分享他/她帐户的用户名。", "MyOrganizations_Detail_WelcomeMessage": "欢迎加入您的组织,{0}", @@ -535,7 +548,7 @@ "MyOrganizations_Detail_OwnerRightInfo": "您正在使用您的 {1} 所有者权利中的 {0}。", "MyOrganizations_Detail_CopyApiKey": "复制密钥", "MyOrganizations_Detail_ApiKeyDescription": "API 密钥是托管在 {1} 上的 PRO 包的令牌。 ", - "MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 源是 {0}", + "MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 源是 {0}", "MyOrganizations_Detail_PrivateNugetSourceWarning": "这将自动添加一个源到您的 ABP 解决方案中的 NuGet.Config。 不要与未经授权的用户共享您的私钥!", "MyOrganizations_Detail_DeveloperSeatInfo": "您正在使用您的 {1} 个开发者席位中的 {0} 个。", "NeedMoreSeatsForYourTeam": "您的团队需要更多席位吗?", @@ -591,6 +604,9 @@ "TrainingDetailsHeaderInfo_TrainingHour": "{0} 小时", "Trainings_Content": "培训内容", "Trial_Page_StartYourFreeTrial": "开始您的免费试用", + "TrialLicenseFeatures": "你将能够享受所有 ABP 商业特性", + "TrialPeriodDays": "你将有 {0} 天的团队许可证", + "TrialForumSupportIncident": "你将有 {0} 个论坛支持事件", "Contact_Page_Title": "联系 ABP 开发团队", "Contact_Page_Description": "如果您需要任何帮助或分享您的想法和意见,请与 ABP 开发团队联系! ABP 支持团队随时准备提供帮助。", "Demo_Page_Title": "创建演示", @@ -712,6 +728,13 @@ "Landing_Page_PaymentModuleDescription_1": "为不同的支付网关提供集成。", "Landing_Page_PaymentModuleDescription_2": "该模块提供支付网关的集成,因此您可以轻松地从客户那里获得付款。", "Landing_Page_PaymentModuleDescription_3": "该模块支持以下支付网关", - "Welcome_Page_UseSameCredentialForCommercialWebsites": "commercial.abp.iosupport.abp.io使用相同的凭据。" + "Welcome_Page_UseSameCredentialForCommercialWebsites": "commercial.abp.iosupport.abp.io使用相同的凭据。", + "WatchCrudPagesVideo": "观看“创建 CRUD 页面的 ABP Suite”视频!", + "WatchGeneratingFromDatabaseVideo": "观看”ABP Suite:从现有数据库表生成 CRUD 页面”视频!", + "WatchTakeCloserLookVideo": "观看“详细了解ABP Suite 的代码生成”视频!", + "ConfirmedEmailAddressRequiredToStartTrial": "你应该有一个确认的电子邮件地址,以便开始试用许可证。", + "EmailVerificationMailNotSent": "电子邮件验证邮件不能发送。", + "GetConfirmationEmail": "点击这里获取确认邮件 如果你还没有收到。", + "WhichLicenseTypeYouAreInterestedIn": "你感兴趣的许可证类型是什么?" } -} +} \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json index 68d70f8615..bb401e6577 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json @@ -29,14 +29,12 @@ "ContributionGuide": "Hozzájárulási útmutató", "BugReport": "Hibajelentés", "SeeAllPosts": "Az összes bejegyzés megtekintése", - "WelcomeToABPCommunity!": "Üdvözöljük az ABP közösségben!", - "MyProfile": "A profilom", - "MyOrganizations": "Szervezeteim", + "WelcomeToABP": "Üdvözöljük az ABP-ben", "EmailNotValid": "Kérjük valós e-mail címet adjon meg!", "FeatureRequest": "Funkciókérés", "CreatePostTitleInfo": "A bejegyzés listán megjelenítendő bejegyzés címe.", "CreatePostSummaryInfo": "A bejegyzés rövid összefoglalása, amely megjelenik a bejegyzéslistán.", - "CreatePostCoverInfo": "Hatékony cikk létrehozásához adjon hozzá egy borítóképet. Tölts fel 16:9-es képarányú képeket a legjobb nézet érdekében. Maximális fájlméret: 1 MB.", + "CreatePostCoverInfo": "Hatékony bejegyzés létrehozásához adj hozzá egy borítóképet. Tölts fel 16:9-es képarányú képeket a legjobb nézet érdekében. Maximális fájlméret: 1 MB.", "ThisExtensionIsNotAllowed": "Ez a bővítmény nem engedélyezett.", "TheFileIsTooLarge": "A fájl túl nagy.", "GoToThePost": "Ugrás a cikkre", @@ -45,7 +43,7 @@ "Done": "Kész", "Open": "Nyisd ki", "Closed": "Zárva", - "LatestQuestionOnThe": "Legújabb kérdés a", + "RecentQuestionFrom": "Friss kérdés tőle", "Stackoverflow": "Stackoverflow", "Votes": "szavazatokat", "Answer": "Válasz", @@ -59,7 +57,7 @@ "QuestionItemErrorMessage": "Nem sikerült lekérni a Stackoverflow legújabb kérdés részleteit.", "Oops": "Hoppá!", "CreatePostSuccessMessage": "A cikk sikeresen beküldve. A webhely adminisztrátorának felülvizsgálata után tesszük közzé.", - "ChooseCoverImage": "Válasszon borítóképet...", + "Browse": "Tallózás", "CoverImage": "Borítókép", "ShareYourExperiencesWithTheABPFramework": "Ossza meg tapasztalatait az ABP keretrendszerrel!", "Optional": "Választható", @@ -88,6 +86,8 @@ "PostRequestFromGithubIssue": "Jelenleg nincs cikkkérés.", "LatestPosts": "Legutóbbi bejegyzések", "ArticleRequests": "Cikkigények", + "ArticleRequestsDescription": "Konkrét tartalmat szeretne látni itt? Megkérheted a közösséget, hogy hozza létre!", + "LatestContentRequests": "Legújabb tartalomkérések", "AllPostRequests": "Lásd: Összes cikkigénylés", "SubscribeToTheNewsletter": "Feliratkozás a Hírlevélre", "NewsletterEmailDefinition": "Információkat kaphat az ABP eseményeiről, például új kiadásokról, ingyenes forrásokról, cikkekről és egyebekről.", @@ -115,7 +115,6 @@ "VideoUrl": "Videó URL-je", "GithubPostUrl": "Github cikk URL-je", "ExternalPostUrl": "Külső cikk URL", - "CreatePostCoverInfo": "Hatékony bejegyzés létrehozásához adj hozzá egy borítóképet. Tölts fel 16:9-es képarányú képeket a legjobb nézet érdekében. Maximális fájlméret: 1 MB.", "ThankYouForContribution": "Köszönjük, hogy hozzájárult az ABP közösséghez.", "GithubPost": "Github cikk", "GithubPostSubmitStepOne": "1. Írjon cikket bármely nyilvános GitHub-tárhelyről Markdown formátumban. példa ", @@ -142,6 +141,49 @@ "MinimumSearchContent": "Legalább 3 karaktert kell megadnod!", "Volo.AbpIo.Domain:060001": "A forrás URL (\"{PostUrl}\") nem Github URL", "Volo.AbpIo.Domain:060002": "A cikk tartalma nem érhető el a Github(\"{PostUrl}\") forrásból.", - "Volo.AbpIo.Domain:060003": "Nem található a cikk tartalma!" + "Volo.AbpIo.Domain:060003": "Nem található a cikk tartalma!", + "SeeMore": "Nézek többet", + "JoinTheABPCommunity": "Csatlakozz az ABP közösséghez", + "ABPCommunityTalks": "ABP közösségi beszélgetések", + "LiveDemo": "Élő Demo", + "GetLicense": "Szerezzen licenszet", + "GetStarted": "Fogj neki", + "SourceCode": "Forráskód", + "LeaveComment": "Hagyj megjegyzést", + "ShowMore": "Mutass többet", + "NoPublishedPostsYet": "Még nincsenek közzétett bejegyzések.", + "Name": "Név", + "Surname": "Vezetéknév", + "WebSite": "Weboldal", + "FullURL": "Teljes URL", + "JobTitle": "Munka megnevezése", + "Prev": "Előző", + "Previous": "Előző", + "Next": "Következő", + "Share": "Részvény", + "SortBy": "Sorrend", + "NoPublishedEventsYet": "Még nincsenek közzétett események.", + "SubscribeYoutubeChannel": "Iratkozz fel a Youtube csatornára", + "Enum:EventType:0": "Beszélgetések", + "MemberNotPublishedPostYet": "Ez a tag még nem tett közzé bejegyzéseket.", + "TimeAgo": "{0} ezelőtt", + "Discord_Page_JoinCommunityMessage": "Csatlakozz az ABP Discord közösséghez", + "Discord_Page_Announce": "Örömmel jelentjük be az ABP Community Discord Servert!", + "Discord_Page_Description_1": "Az ABP közösség az első nap óta növekszik. Egy hivatalos ABP Discord szerver létrehozásával a következő lépésre akartuk vinni, hogy az ABP közösség az azonnali üzenetküldés csodáival kommunikálhasson egymással.", + "Discord_Page_Description_2": "Az ABP Community Discord Server az a hely, ahol bemutathatja alkotásait az ABP Framework segítségével, megoszthatja az Ön számára bevált tippeket, értesülhet az ABP Framework-vel kapcsolatos legfrissebb hírekről és bejelentésekről, egyszerűen cseveghet a közösség tagjaival, hogy eszmét cserélhessen, és érezze jól magát!", + "Discord_Page_Description_3": "Ez az ABP Community Discord szerver a hivatalos, ahol az ABP Core Team jelen van a szerveren, hogy felügyelje.", + "Discord_Page_JoinToServer": "Csatlakozz az ABP Discord szerverhez", + "Events_Page_MetaTitle": "ABP közösségi események", + "Events_Page_MetaDescription": "Az ABP Team által rendezett élő műsorok hétköznapi előadások, tele közösségi tartalommal, demókkal, kérdésekkel és válaszokkal, valamint az ABP-ben zajló eseményekről szóló vitákkal.", + "Events_Page_Title": "ABP közösségi beszélgetések", + "Events_Page_WritingFromUser": "Olvassa el {0} írását az ABP közösségben.", + "Post_Create_Page_MetaTitle": "Új bejegyzés", + "Post_Create_Page_MetaDescription": "Hozzon létre bejegyzést, hogy megossza tapasztalatait az ABP keretrendszerrel kapcsolatban, és hozzájáruljon az ABP közösséghez.", + "Post_Create_Page_CreateNewPost": "Új bejegyzés létrehozása", + "Post_Index_Page_MetaDescription": "Az ABP Community célja egy hozzájárulási környezet létrehozása az ABP keretrendszert használó fejlesztők számára.", + "Layout_Title": "{0} | ABP-közösség", + "Layout_MetaDescription": "Az ABP Community egy olyan környezet, ahol az emberek megoszthatnak bejegyzéseket az ABP keretrendszerről, és követhetik a projekteket.", + "Index_Page_CommunityIntroduction": "Ez az ABP Framework, a .NET és a szoftverfejlesztés központja. Elolvashatja a cikkeket, megnézheti az oktatóvideókat, tájékozódhat az ABP fejlesztési előrehaladásáról és az ABP-vel kapcsolatos eseményekről, segíthet más fejlesztőknek, és megoszthatja szakértelmét az ABP közösséggel.", + "TagsInArticle": "Címkék a cikkben" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Support/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Support/Localization/Resources/hu.json new file mode 100644 index 0000000000..292fb51cd2 --- /dev/null +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Support/Localization/Resources/hu.json @@ -0,0 +1,6 @@ +{ + "culture": "hu", + "texts": { + "FAQ": "GYIK" + } +} \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json index 0b9dfab886..faba7ab94e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json @@ -392,6 +392,7 @@ "LeptonXLiteThemeInfo": " A modern and stylish Bootstrap UI theme. Ideal if you want to have a production ready UI theme. This is the newest theme and is the default.", "BasicThemeInfo": "Minimalist UI theme with plain Bootstrap colors and styles. Ideal if you will build your own UI theme.", "SeeDocumentation": "See documentation.", - "SeeFullScreen": "🖼️ See the screenshot" + "SeeFullScreen": "🖼️ See the screenshot", + "BuildingMicroserviceSolutionsShortDescription": "This book is a reference guide for developing and managing microservice-based applications using the ABP Framework." } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/hu.json index cb1639fd42..2a6ebf2feb 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/hu.json @@ -174,6 +174,7 @@ "CreateProjectWizard": "Ez a varázsló új projektet hoz létre az indítási sablonból, amely megfelelően be van állítva a projekt elindításához.", "TieredOption": "Létrehoz egy többszintű megoldást, ahol a webes és a HTTP API-rétegek fizikailag el vannak választva. Ha nincs bejelölve, réteges megoldást hoz létre, amely kevésbé bonyolult és a legtöbb forgatókönyvhöz megfelelő.", "SeparateIdentityServerOption": "A szerveroldalt két alkalmazásra választja szét: az első az identitáskiszolgálóhoz, a második pedig a szerveroldali HTTP API-hoz való.", + "ProgressiveWebApplicationOption": "Progresszív webalkalmazásként határozza meg a projektet", "UseslatestPreVersion": "A legújabb kiadás előtti verziót használja", "ReadTheDocumentation": "Olvassa el a dokumentációt", "Documentation": "Dokumentáció", @@ -214,7 +215,11 @@ "SeeDocs": "Lásd: Dokumentumok", "None": "Egyik sem", "Application": "Alkalmazás", + "ApplicationExplanation": "Teljesen rétegzett megoldást hoz létre a tartományvezérelt tervezési gyakorlatok alapján. Olyan hosszú távú projektekhez ajánlott, amelyek karbantartható és bővíthető kódbázist igényelnek.", + "ApplicationNoLayer": "Alkalmazás (egyrétegű)", + "ApplicationNoLayerExplanation": "Egyrétegű webalkalmazást hoz létre. Egyszerűbb és könnyen érthető architektúrájú alkalmazás készítéséhez ajánlott.", "Module": "Modul", + "ModuleExplanation": "Újrafelhasználható, teljesen rétegzett alkalmazásmodul-megoldást hoz létre. Ezzel a lehetőséggel modulokat hozhat létre a moduláris alkalmazásához.", "PackageName": "Csomag név", "LicenseURL": "Licenc URL", "License": "Engedély", @@ -273,6 +278,120 @@ "SubscribeToNewsletter": "Iratkozzon fel a hírlevélre, hogy tájékozódjon az ABP.IO Platform eseményeiről, például új kiadásokról, cikkekről, ajánlatokról stb.", "FirstEdition": "Első kiadás", "ThankYou": "Köszönöm!", - "CheckboxMandatory": "Ezt ellenőriznie kell a folytatáshoz!" + "CheckboxMandatory": "Ezt ellenőriznie kell a folytatáshoz!", + "UserInterface": "Felhasználói felület", + "APIGateway": "API átjáró", + "Database": "Adatbázis", + "Saas": "Saas", + "OpenSourceWebApp": "Nyílt forráskódú
webalkalmazás", + "Framework": "Keretrendszer", + "AuditLoggingExplanation": "Automatikusan nyomon követheti az összes műveletet és adatváltozást a rendszerben.", + "AbpNewCommandExplanation": "Új megoldásokat hoz létre az ABP indítási sablonjaival.", + "AbpAddModuleCommandExplanation": "Előre elkészített alkalmazásmodulokat telepít a megoldásra", + "AbpUpdateCommandExplanation": "Automatikusan frissíti az összes ABP-vel kapcsolatos NuGet és NPM csomagot a megoldásban.", + "ExploreAllCLICommands": "Fedezze fel az összes CLI-parancsot", + "ExploreDocumentationAndGuides": "Fedezze fel az átfogó dokumentációt és útmutatókat.", + "Documentations": "Dokumentáció", + "Views": "Nézetek", + "EnterYouEmailToGetNews": "Adja meg e-mail címét, hogy megkapja a legfrissebb híreket az ABP-keretrendszerrel kapcsolatban", + "Tiered": "Többszintű", + "SeparateIdentityServer": "Külön identitásszerver", + "ProgressiveWebApplication": "Progresszív webes alkalmazás", + "Preview": "Előnézet", + "CreateANewSolution": "Hozzon létre egy új megoldást", + "ABPFrameworkFeatures": "Az ABP keretrendszer jellemzői", + "Commercial": "Kereskedelmi", + "ThirdPartyTools": "Harmadik féltől származó eszközök", + "Back": "Vissza", + "Community": "Közösség", + "SeeMore": "Többet látni", + "DetailsOfTheEBook": "Az E-könyv részletei", + "JoinOurMarketingNewsletter": "Csatlakozzon marketing hírlevelünkhöz", + "FrameworkNewsletterConfirmationMessage": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot .", + "GetYourFreeEBook": "Szerezze meg ingyenes DDD e-könyvét", + "EverythingYouNeedToKnow": "Minden, amit tudnia kell.", + "PreOrderNow": "Előrendelés most", + "UITheming": "UI témák", + "UIThemingExplanation": "Hozzon létre újrafelhasználható felhasználói felület-témákat és elrendezéseket, vagy használja valamelyik előre elkészített felhasználói felület-témát.", + "DataFilteringExplanation2": "Automatikusan szűrje le az adatbázisból származó lekérdezéseket, hogy könnyen megvalósíthassa az olyan mintákat, mint a soft-delete és a multi-tenancy.", + "NeedHelp": "Kell segítség?", + "GiveYourProjectAName": "Adjon nevet a projektjének", + "SelectProjectType": "Válassza ki a Projekt típusát", + "SelectUIFramework": "Válassza a UI-keretrendszert", + "SelectDatabaseProvider": "Válassza az Adatbázis-szolgáltató lehetőséget", + "SelectDatabaseManagementSystem": "Válassza az Adatbázis-kezelő rendszer lehetőséget", + "InstallingTheABPCLI": "Az ABP CLI telepítése", + "CreateYourProjectNow": "Hozza létre projektjét most", + "OrderOn": "Rendelés itt: {0}", + "DownloadFreeDDDBook": "Ingyenes DDD könyv letöltése", + "WhatIsABPFramework": "Mi az az ABP-keretrendszer?", + "TenantDatabase": "Bérlői {0} adatbázis", + "SharedDatabase": "Megosztott adatbázis", + "ConnectionResolver": "Kapcsolatfeloldó", + "TenantBasedDataFilter": "Bérlő alapú adatszűrő", + "ApplicationCode": "Alkalmazási kód", + "TenantResolution": "Bérlői állásfoglalás", + "TenantUser": "Bérlő {0} felhasználó", + "CardTitle": "Kártya címe", + "View": "Nézet", + "Model": "Modell", + "Email": "Email", + "Password": "Jelszó", + "Address": "Cím", + "Gender": "Nem", + "Male": "Férfi", + "Female": "Nő", + "Submit": "Beküldés", + "Unspecified": "Meg nem határozott", + "StaticFileMiddleware": "Statikus fájl köztes-szoftver", + "RazorViewEngine": "Razor View Engine", + "PhysicalFiles": "Fizikai fájlok (wwwroot)", + "EmbeddedFiles": "Beágyazott fájlok (DLL)", + "DynamicFiles": "Dinamikus fájlok (memória)", + "BuildSolutionsWithAbp": "Építsen karbantartható .NET-megoldásokat az ABP használatával bevált szoftverfejlesztési gyakorlatok követésével.", + "BuyOnAmazon": "Vásároljon az Amazonon", + "BuyOnPackt": "Vásároljon Packt-en", + "Discounted": "Kedvezményes", + "MasteringAbpFramework_Book_KeyFeatures": "Főbb jellemzők", + "MasteringAbpFramework_Book_Key_Features_Description_1": "Építsen robusztus, karbantartható, moduláris és méretezhető szoftvermegoldásokat az ABP Framework segítségével.", + "MasteringAbpFramework_Book_Key_Features_Description_2": "Ismerje meg, hogyan valósíthatja meg a SOLID elveket és a tartományvezérelt tervezést webalkalmazásaiban.", + "MasteringAbpFramework_Book_Key_Features_Description_3": "Fedezze fel, hogyan gyorsítja fel az ABP Framework a fejlesztési ciklust az ismétlődő feladatok automatizálásával.", + "MasteringAbpFramework_Book_Description": "Könyv leírása", + "MasteringAbpFramework_Book_Description_Details_1": "Az ABP Framework egy komplett infrastruktúra modern webalkalmazások létrehozásához a szoftverfejlesztési bevált gyakorlatok és konvenciók követésével. Az ABP magas szintű keretrendszerével és ökoszisztémájával megvalósíthatja a Ne ismételje meg magát (DRY) elvet, és az üzleti kódjára összpontosítson.", + "MasteringAbpFramework_Book_Description_Details_2": "Az ABP Framework megalkotója által írt könyv segít a keretrendszer és a modern webalkalmazás-fejlesztési technikák teljes megértésében. Az alapvető fogalmak lépésről lépésre történő magyarázatával és gyakorlati példákkal megismerheti a modern webes megoldások követelményeit, és megértheti, hogy az ABP Framework hogyan teszi élvezetessé saját megoldásainak fejlesztését. Felfedezi a vállalati webalkalmazás-fejlesztés általános követelményeit, és felfedezheti az ABP által biztosított infrastruktúrát. A könyv során megismerkedhet a szoftverfejlesztés bevált gyakorlataival a karbantartható és moduláris webes megoldások létrehozásához.", + "MasteringAbpFramework_Book_Description_Details_3": "A könyv végére képes lesz egy komplett webes megoldás létrehozására, amely könnyen fejleszthető, karbantartható és tesztelhető.", + "MasteringAbpFramework_Book_WhatYouWillLearn": "Mit fog tanulni", + "MasteringAbpFramework_Book_What_You_Will_Learn_1": "Állítsa be a fejlesztői környezetet, és kezdje el az ABP Framework használatát.", + "MasteringAbpFramework_Book_What_You_Will_Learn_2": "Az Entity Framework Core és a MongoDB segítségével fejlesztheti adathozzáférési rétegét.", + "MasteringAbpFramework_Book_What_You_Will_Learn_3": "Ismerje meg a több területet érintő aggályokat és azt, hogy az ABP hogyan automatizálja az ismétlődő feladatokat.", + "MasteringAbpFramework_Book_What_You_Will_Learn_4": "Ismerkedjen meg a tartományvezérelt tervezés megvalósításával az ABP Framework segítségével.", + "MasteringAbpFramework_Book_What_You_Will_Learn_5": "Hozzon létre felhasználói felület oldalakat és összetevőket az ASP.NET Core MVC (Razor Pages) és a Blazor segítségével.", + "MasteringAbpFramework_Book_What_You_Will_Learn_6": "Moduláris webalkalmazások létrehozásához dolgozzon több bérléssel.", + "MasteringAbpFramework_Book_What_You_Will_Learn_7": "Ismerje meg a modularitást, és hozzon létre újrafelhasználható alkalmazásmodulokat.", + "MasteringAbpFramework_Book_What_You_Will_Learn_8": "Írjon egység-, integrációs és UI-teszteket az ABP-keretrendszer használatával.", + "MasteringAbpFramework_Book_WhoIsThisBookFor": "Kinek szól ez a könyv", + "MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "Ez a könyv azoknak a webfejlesztőknek szól, akik szoftverarchitektúrákat és bevált gyakorlatokat szeretnének megtanulni karbantartható webalapú megoldások Microsoft technológiáit és ABP-keretrendszert használó felépítéséhez. A könyv használatának megkezdéséhez alapszintű C# és ASP.NET Core ismerete szükséges.", + "ComputersAndTechnology": "Számítógépek és technológia", + "BuildingMicroserviceSolutions": "Mikroszolgáltatási megoldások építése", + "MicroserviceBookPracticalGuide": "Ez a könyv egy referencia útmutató a mikroszolgáltatás alapú alkalmazások fejlesztéséhez és kezeléséhez az ABP Framework segítségével. Hivatkozik a .NET Microservice Sample Reference Application : eShopOnContainers alkalmazásra, és tárgyalja az ABP-keretrendszert használó tervezési és megvalósítási megközelítéseket. A könyv végére megtudhatja, hogyan közelíti meg az ABP az olyan általános mikroszolgáltatási bonyolultságokat, mint az engedélyezés, az elosztott tranzakciók, a mikroszolgáltatások közötti kommunikáció, a telepítés stb.", + "IntroducingTheSolution": "Az eShopOnAbp megoldás bemutatása", + "RunningTheSolution": "A megoldás futtatása", + "UnderstandingTheAuthenticationSystem": "A hitelesítési rendszer megértése", + "ExploringTheApplications": "Az alkalmazások felfedezése", + "UnderstandingTheAPIGateways": "Az API-átjárók megértése", + "DevelopingTheMicroservices": "Mikroszolgáltatások fejlesztése", + "UnderstandingTheInfrastructure": "Az infrastruktúra megértése", + "DiggingInTheUseCases": "feltérképezni a használati eseteket", + "DeployingTheSolution": "A megoldás telepítése", + "ThisBookIsInDraftStageAndIsNotCompletedYet": "Ez a könyv tervezet stádiumban van, és még nem készült el.", + "Authors": "Szerzői", + "MicroserviceEBook": "Mikroszolgáltatás e-könyv", + "SelectUITheme": "Válassza ki a felhasználói felület témáját", + "LeptonXLiteTheme": "LeptonX Lite téma", + "BasicTheme": "Alap téma", + "LeptonXLiteThemeInfo": "Modern és stílusos Bootstrap UI téma. Ideális, ha gyártásra kész felhasználói felület témát szeretne. Ez a legújabb téma, és az alapértelmezett.", + "BasicThemeInfo": "Minimalista felhasználói felület téma egyszerű Bootstrap színekkel és stílusokkal. Ideális, ha saját felhasználói felület témát készít.", + "SeeDocumentation": "Lásd a dokumentációt .", + "SeeFullScreen": "🖼️ Nézze meg a képernyőképet" } } \ No newline at end of file diff --git a/build/common.ps1 b/build/common.ps1 index 01a7565fba..61d9cf03d2 100644 --- a/build/common.ps1 +++ b/build/common.ps1 @@ -20,8 +20,7 @@ $solutionPaths = @( "../modules/background-jobs", "../modules/account", "../modules/cms-kit", - "../modules/blob-storing-database", - "../studio" + "../modules/blob-storing-database" ) if ($full -eq "-f") diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md index 4ee58d84ef..869a8075af 100644 --- a/docs/en/Background-Jobs-Hangfire.md +++ b/docs/en/Background-Jobs-Hangfire.md @@ -80,6 +80,41 @@ After you have installed these NuGet packages, you need to configure your projec } ```` +### Specifying Queue + +You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue. + +````csharp +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing; + +namespace MyProject +{ + [Queue("alpha")] + public class EmailSendingJob + : AsyncBackgroundJob, ITransientDependency + { + private readonly IEmailSender _emailSender; + + public EmailSendingJob(IEmailSender emailSender) + { + _emailSender = emailSender; + } + + public override async Task ExecuteAsync(EmailSendingArgs args) + { + await _emailSender.SendAsync( + args.EmailAddress, + args.Subject, + args.Body + ); + } + } +} +```` + ### Dashboard Authorization Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard. diff --git a/docs/en/Distributed-Locking.md b/docs/en/Distributed-Locking.md index d29b60ad04..a1e653a6a6 100644 --- a/docs/en/Distributed-Locking.md +++ b/docs/en/Distributed-Locking.md @@ -101,6 +101,25 @@ namespace AbpDemo * `timeout` (`TimeSpan`): A timeout value to wait to obtain the lock. Default value is `TimeSpan.Zero`, which means it doesn't wait if the lock is already owned by another application. * `cancellationToken`: A cancellation token that can be triggered later to cancel the operation. +### Configuration + +#### AbpDistributedLockOptions + +`AbpDistributedLockOptions` is the main options class to configure the distributed locking. + +**Example: Set the distributed lock key prefix for the application** + +Configure(options => +{ + options.KeyPrefix = "MyApp1"; +}); + +> Write that code inside the `ConfigureServices` method of your [module class](Module-Development-Basics.md). + +##### Available Options + +* KeyPrefix (string, default: null): Specify the lock name prefix. + ### Using DistributedLock Library's API ABP's `IAbpDistributedLock` service is very limited and mainly designed to be internally used by the ABP Framework. For your own applications, you can use the DistributedLock library's own API. See its [own documentation](https://github.com/madelson/DistributedLock) for details. diff --git a/docs/en/Entities.md b/docs/en/Entities.md index c57af27457..ef8f52a04f 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -112,6 +112,21 @@ For the example above, the composite key is composed of `UserId` and `RoleId`. F > Also note that Entities with Composite Primary Keys cannot utilize the `IRepository` interface since it requires a single Id property. However, you can always use `IRepository`. See [repositories documentation](Repositories.md) for more. +### EntityEquals + +`Entity.EntityEquals(...)` method is used to check if two Entity Objects are equals. + +Example: + +```csharp +Book book1 = ... +Book book2 = ... + +if (book1.EntityEquals(book2)) //Check equality +{ + ... +} +``` ## AggregateRoot Class diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index 569bfeb4ca..7dac3762aa 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -222,7 +222,6 @@ The following resolvers are provided and configured by default; * `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**. * `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default. -* `FormTenantResolveContributor`:Tries to find current tenant id from form parameters. The parameter name is `__tenant` by default. * `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route. * `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default. * `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default. diff --git a/docs/en/Themes/LeptonXLite/AspNetCore.md b/docs/en/Themes/LeptonXLite/AspNetCore.md index eeaf55f545..f480141120 100644 --- a/docs/en/Themes/LeptonXLite/AspNetCore.md +++ b/docs/en/Themes/LeptonXLite/AspNetCore.md @@ -9,22 +9,22 @@ LeptonX Lite has implementation for the ABP Framework Razor Pages. It's a simpli This theme is **already installed** when you create a new solution using the startup templates. If you are using any other template, you can install this theme by following the steps below: -- Add **Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite** package to your **Web** application. +- Add the **Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite** package to your **Web** application. ```bash dotnet add package Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite --prerelease ``` -- Remove **Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic** reference from the project since it's not necessary after switching to LeptonX Lite. +- Remove the **Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic** reference from the project since it's not necessary after switching to LeptonX Lite. - Make sure the old theme is removed and LeptonX is added in your Module class. ```diff [DependsOn( - // Remove BasicTheme module from DependsOn attribute + // Remove the BasicTheme module from DependsOn attribute - typeof(AbpAspNetCoreMvcUiBasicThemeModule), - // Add LeptonX Lite module to DependsOn attribute + // Add the LeptonX Lite module to DependsOn attribute + typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule), )] ``` @@ -35,9 +35,9 @@ dotnet add package Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite --prerelease Configure(options => { options.StyleBundles.Configure( - // Remove following line + // Remove the following line - BasicThemeBundles.Styles.Global, - // Add following line instead + // Add the following line instead + LeptonXLiteThemeBundles.Styles.Global bundle => { @@ -47,10 +47,24 @@ Configure(options => }); ``` ---- - ## Customization +### Layouts + +LeptonX Lite Mvc provides **layouts** for your **user interface** based [ABP Framework Theming](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Theming). You can use **layouts** to **organize your user interface**. + +The main responsibility of a theme is to **provide** the layouts. There are **three pre-defined layouts that must be implemented by all the themes:** + +* **Application:** The **default** layout which is used by the **main** application pages. + +* **Account:** Mostly used by the **account module** for **login**, **register**, **forgot password**... pages. + +* **Empty:** The **Minimal** layout that **has no layout components** at all. + +**Layout names** are **constants** defined in the `LeptonXLiteTheme` class in the **Mvc** project **root**. + +> The layout pages define under the `Themes/LeptonXLite/Layouts` folder and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + ### Toolbars LeptonX Lite includes separeted toolbars for desktop & mobile. You can manage toolbars independently. Toolbar names can be accessible in the **LeptonXLiteToolbars** class. @@ -74,3 +88,128 @@ public class MyProjectNameMainToolbarContributor : IToolbarContributor } } ``` +# LeptonX Lite Mvc Components + +Abp **helps** you make **highly customizable UI**. You can easily **customize** your themes to fit your needs. **The Virtual File System** makes it possible to **manage files** that **do not physically** exist on the **file system** (disk). It's mainly used to embed **(js, css, image..)** files into assemblies and **use them like** physical files at runtime. An application (or another module) can **override** a **virtual file of a module** just like placing a file with the **same name** and **extension** into the **same folder** of the **virtual file**. + +LeptonX Lite is built on the [Abp Framework](https://abp.io/), so you can **easily** customize your Asp.Net Core Mvc user interface by following [Abp Mvc UI Customization](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-xUser-Interface). + +## Brand Component + +The **brand component** is a simple component that can be used to display your brand. It contains a **logo** and a **company name**. + +![Brand component](../../images/leptonxlite-brand-component.png) + +### How to override the Brand Component in LeptonX Lite Mvc + +* The **brand component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Brand/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **brand component (C# file)** is defined in the `Themes/LeptonXLite/Components/Brand/MainNavbarBrandViewComponent.cs` file and you can **override it** by creating a file with the **same name** and under the **same folder**. + +## Breadcrumb Component + +On websites that have a lot of pages, **breadcrumb navigation** can greatly **enhance the way users find their way** around. In terms of **usability**, breadcrumbs reduce the number of actions a website **visitor** needs to take in order to get to a **higher-level page**, and they **improve** the **findability** of **website sections** and **pages**. + +![Breadcrumb component](../../images/leptonxlite-breadcrumb-component.png) + +### How to override the Breadcrumb Component in LeptonX Lite Mvc + +* The **breadcrumb component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Breadcrumbs/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **breadcrumb component (C# file)** is defined in the `Themes/LeptonXLite/Components/Breadcrumbs/BreadcrumbsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Sidebar Menu Component + +Sidebar menus have been used as **a directory for Related Pages** to a **Service** offering, **Navigation** items to a **specific service** or topic and even just as **Links** the user may be interested in. + +![Sidebar menu component](../../images/leptonxlite-sidebar-menu-component.png) + +### How to override the Sidebar Menu Component in LeptonX Lite Mvc + +* **Sidebar menu page (.cshtml)** is defined in the `Themes/LeptonXLite/Components/Menu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* If you want to **override the menu component (C#)** you can override the `Themes/LeptonXLite/Components/Menu/MainMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +> The **sidebar menu** renders menu items **dynamically**. The **menu item** is a **partial view** and is defined in the `Themes/LeptonXLite/Components/Menu/_MenuItem.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Page Alerts Component + +Provides contextual **feedback messages** for typical user actions with the handful of **available** and **flexible** **alert messages**. Alerts are available for any length of text, as well as an **optional dismiss button**. + +![Page alerts component](../../images/leptonxlite-page-alerts-component.png) + +### How to override the Page Alerts Component in LeptonX Lite Mvc + +* The **page alerts component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/PageAlerts/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **page alerts component (C#)** is defined in the `Themes/LeptonXLite/Components/PageAlerts/PageAlertsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Toolbar Component + +Toolbar items are used to add **extra functionality to the toolbar**. The toolbar is a **horizontal bar** that **contains** a group of **toolbar items**. + +### How to override the Toolbar Component in LeptonX Lite Mvc + +* The **toolbar component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Toolbar/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **toolbar component (C#)** is defined in the `Themes/LeptonXLite/Components/Toolbar/ToolbarViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Toolbar Item Component + +The toolbar item is a **single item** that **contains** a **link**, an **icon**, a **label** etc.. + +### How to override the Toolbar Item Component in LeptonX Lite Mvc + +* The **toolbar item component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/ToolbarItems/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **toolbar item component (C#)** is defined in the `Themes/LeptonXLite/Components/ToolbarItems/ToolbarItemsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +You can find the toolbar components below: + +## Language Switch Component + +Think about a **multi-lingual** website and the first thing that could **hit your mind** is **the language switch component**. A **navigation bar** is a **great place** to **embed a language switch**. By embedding the language switch in the navigation bar of your website, you would **make it simpler** for users to **find it** and **easily** switch the **language** **without trying to locate it across the website.** + +![Language switch component](../../images/leptonxlite-language-switch-component.png) + +### How to override the Language Switch Component in LeptonX Lite Mvc + +* The **language switch component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/LanguageSwitch/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **language switch component (C#)** is defined in the `Themes/LeptonXLite/Components/LanguageSwitch/LanguageSwitchViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Mobile Language Switch Component + +The **mobile** **language switch component** is used to switch the language of the website **on mobile devices**. The mobile language switch component is a **dropdown menu** that **contains all the languages** of the website. + +![Mobil language switch component](../../images/leptonxlite-mobile-language-switch-component.png) + +### How to override the Mobile Language Switch Component in LeptonX Lite Mvc + +* The **mobile language switch component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/MobileLanguageSwitch/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **mobile language switch component (C#)** is defined in the `Themes/LeptonXLite/Components/MobileLanguageSwitch/MobileLanguageSwitchViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## User Menu Component + +The **User Menu** is the **menu** that **drops down** when you **click your name** or **profile picture** in the **upper right corner** of your page (**in the toolbar**). It drops down options such as **Settings**, **Logout**, etc. + +![User menu component](../../images/leptonxlite-user-menu-component.png) + +### How to override the User Menu Component in LeptonX Lite Mvc + +* The **user menu component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/UserMenu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **user menu component (C#)** is defined in the `Themes/LeptonXLite/Components/UserMenu/UserMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +## Mobile User Menu Component + +The **mobile user menu component** is used to display the **user menu on mobile devices**. The mobile user menu component is a **dropdown menu** that contains all the **options** of the **user menu**. + +![Mobile user menu component](../../images/leptonxlite-mobile-user-menu-component.png) + +### How to override the Mobile User Menu Component in LeptonX Lite Mvc + +* The **mobile user menu component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/MobileUserMenu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. + +* The **mobile user menu component (C#)** is defined in the `Themes/LeptonLite/Components/MobileUserMenu/MobileUserMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**. diff --git a/docs/en/UI/Blazor/Layout-Hooks.md b/docs/en/UI/Blazor/Layout-Hooks.md new file mode 100644 index 0000000000..1118e02d37 --- /dev/null +++ b/docs/en/UI/Blazor/Layout-Hooks.md @@ -0,0 +1,125 @@ +# Blazor UI: Layout Hooks + +ABP Framework theming system places the page layout into the [theme](Theming.md) NuGet packages. That means the final application doesn't include a layout, so you can't directly change the layout code to customize it. + +> If you create a Blazor WASM project, the `index.html` file will be included within the template. You can also customize it to your needs. + +You can copy the theme code into your solution. In this case, you are completely free to customize it. However, then you won't be able to get automatic updates of the theme (by upgrading the theme NuGet package). + +The **Layout Hook System** allows you to **add code** at some specific parts of the layout. All layouts of all themes should implement these hooks. Finally, you can add a **razor component** into a hook point. + +## Example: Add a Simple Announcement Alert + +Assume that you need to add a simple banner to the layout (that will be available for all the pages) to make an announcement about your new product. First, **create a razor component** in your project: + +![bookstore-banner-component](../../images/bookstore-banner-component.png) + +**AnnouncementComponent.razor.cs** + +```csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; + +namespace Acme.BookStore.Blazor.Components; + +public partial class AnnouncementComponent : ComponentBase +{ + private const string AnnouncementLocalStorageKey = "product-announcement-status"; + + [Inject] + public IJSRuntime JsRuntime { get; set; } + + public bool ShowBanner { get; set; } + + protected override async Task OnInitializedAsync() + { + ShowBanner = await ShowAnnouncementBannerAsync(); + } + + private async Task ShowAnnouncementBannerAsync() + { + var announcementLocalStorageValue = await JsRuntime.InvokeAsync("localStorage.getItem", AnnouncementLocalStorageKey); + + return announcementLocalStorageValue != null && + bool.TryParse(announcementLocalStorageValue, out var showAnnouncementBanner) && showAnnouncementBanner; + } + + private async Task HideAnnouncementBannerAsync() + { + await JsRuntime.InvokeVoidAsync("localStorage.setItem", AnnouncementLocalStorageKey, true); + ShowBanner = false; + StateHasChanged(); + } +} +``` + +**AnnouncementComponent.razor** + +```html +@if(ShowBanner) +{ +
+ A brand new product is in sale. Click here to learn more. + +
+} +``` + +Then, you can add this component to any of the hook points in the `ConfigureServices` of your module class: + +```csharp +Configure(options => +{ + options.Add( + LayoutHooks.Body.Last, //The hook name + typeof(AnnouncementComponent) //The component to add + ); +}); +``` + +Now, the `AnnouncementComponent` will be rendered in the `body` of the page as the last item. + +### Specifying the Layout + +The configuration above adds the `AnnouncementComponent` to all layouts. You may want to only add it to a specific layout: + +````csharp +Configure(options => +{ + options.Add( + LayoutHooks.Body.Last, + typeof(AnnouncementComponent), + layout: StandardLayouts.Application //Set the layout to add + ); +}); +```` + +See the *Layouts* section below to learn more about the layout system. + +## Layout Hook Points + +There are some pre-defined layout hook points. The standard hook points are: + +* `LayoutHooks.Body.First`: Used to add a component as the first item in the HTML body tag. +* `LayoutHooks.Body.Last`: Used to add a component as the last item in the HTML body tag. + +> You (or the modules you are using) can add **multiple items to the same hook point**. All of them will be added to the layout in the order they were added. + +## Layouts + +The layout system allows themes to define the standard named layouts and allows any page to select a proper layout for its purpose. There is one pre-defined layout: + +* "**Application**": The main (and the default) layout for an application. It typically contains a header, menu (sidebar), footer, toolbar... etc. + +This layout is defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but this layout is the standard layout and it's implemented by all the themes out of the box. + +> If you don't specify the layout, your razor component will be rendered in all of the layouts. + +### Layout Location + +You can find the `MainLayout.razor` [here](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor) for the basic theme. You can take it as a reference to build your own layouts or you can override it, if necessary. + +## See Also + +* [Customization / Overriding Components](Customization-Overriding-Components.md) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 14c584d376..035f150705 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -939,6 +939,10 @@ { "text": "PWA Configuration", "path": "UI/Blazor/Pwa-Configuration.md" + }, + { + "text": "Layout Hooks", + "path": "UI/Blazor/Layout-Hooks.md" } ] }, diff --git a/docs/en/images/bookstore-banner-component.png b/docs/en/images/bookstore-banner-component.png new file mode 100644 index 0000000000..2363b06fae Binary files /dev/null and b/docs/en/images/bookstore-banner-component.png differ diff --git a/docs/en/images/leptonxlite-brand-component.png b/docs/en/images/leptonxlite-brand-component.png new file mode 100644 index 0000000000..c06a51612f Binary files /dev/null and b/docs/en/images/leptonxlite-brand-component.png differ diff --git a/docs/en/images/leptonxlite-breadcrumb-component.png b/docs/en/images/leptonxlite-breadcrumb-component.png new file mode 100644 index 0000000000..9026ca4c52 Binary files /dev/null and b/docs/en/images/leptonxlite-breadcrumb-component.png differ diff --git a/docs/en/images/leptonxlite-language-switch-component.png b/docs/en/images/leptonxlite-language-switch-component.png new file mode 100644 index 0000000000..027910c23b Binary files /dev/null and b/docs/en/images/leptonxlite-language-switch-component.png differ diff --git a/docs/en/images/leptonxlite-mobile-language-switch-component.png b/docs/en/images/leptonxlite-mobile-language-switch-component.png new file mode 100644 index 0000000000..a058820743 Binary files /dev/null and b/docs/en/images/leptonxlite-mobile-language-switch-component.png differ diff --git a/docs/en/images/leptonxlite-mobile-user-menu-component.png b/docs/en/images/leptonxlite-mobile-user-menu-component.png new file mode 100644 index 0000000000..1a51c341d7 Binary files /dev/null and b/docs/en/images/leptonxlite-mobile-user-menu-component.png differ diff --git a/docs/en/images/leptonxlite-page-alerts-component.png b/docs/en/images/leptonxlite-page-alerts-component.png new file mode 100644 index 0000000000..7976412fb2 Binary files /dev/null and b/docs/en/images/leptonxlite-page-alerts-component.png differ diff --git a/docs/en/images/leptonxlite-sidebar-menu-component.png b/docs/en/images/leptonxlite-sidebar-menu-component.png new file mode 100644 index 0000000000..6afe4029e8 Binary files /dev/null and b/docs/en/images/leptonxlite-sidebar-menu-component.png differ diff --git a/docs/en/images/leptonxlite-user-menu-component.png b/docs/en/images/leptonxlite-user-menu-component.png new file mode 100644 index 0000000000..a0039e996c Binary files /dev/null and b/docs/en/images/leptonxlite-user-menu-component.png differ diff --git a/docs/zh-Hans/Background-Jobs-Hangfire.md b/docs/zh-Hans/Background-Jobs-Hangfire.md index b1bf7b8c79..fb899758cc 100644 --- a/docs/zh-Hans/Background-Jobs-Hangfire.md +++ b/docs/zh-Hans/Background-Jobs-Hangfire.md @@ -66,6 +66,41 @@ public class YourModule : AbpModule } ```` +### 指定队列 + +你可以使用 [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) 来指定队列. + +````csharp +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing; + +namespace MyProject +{ + [Queue("alpha")] + public class EmailSendingJob + : AsyncBackgroundJob, ITransientDependency + { + private readonly IEmailSender _emailSender; + + public EmailSendingJob(IEmailSender emailSender) + { + _emailSender = emailSender; + } + + public override async Task ExecuteAsync(EmailSendingArgs args) + { + await _emailSender.SendAsync( + args.EmailAddress, + args.Subject, + args.Body + ); + } + } +} +```` + 1. 如果你想要使用Hangfire的面板,你可以在 `Module` 类的 `OnApplicationInitialization` 方法添加: `UseHangfireDashboard` ````csharp diff --git a/docs/zh-Hans/Distributed-Locking.md b/docs/zh-Hans/Distributed-Locking.md index 2176230403..a570f598af 100644 --- a/docs/zh-Hans/Distributed-Locking.md +++ b/docs/zh-Hans/Distributed-Locking.md @@ -101,6 +101,25 @@ namespace AbpDemo * `timeout` (`TimeSpan`): 等待获取锁的超时值. 默认值为`TimeSpan.Zero`, 这意味着如果锁已经被另一个应用程序拥有, 它不会等待. * `cancellationToken`: 取消令牌可在触发后取消操作. +### 配置 + +#### AbpDistributedLockOptions + +`AbpDistributedLockOptions` 是配置分布式锁的主要选项类. + +**示例: 设置应用程序的分布式锁Key前缀** + +Configure(options => +{ + options.KeyPrefix = "MyApp1"; +}); + +> 在你的[模块类](Module-Development-Basics.md)中的 `ConfigureServices` 方法进行配置. + +##### 可用选项 + +* KeyPrefix (string, 默认值: null): 指定分布式锁名称前缀. + ### 使用DistributedLock库的API ABP的`IAbpDistributedLock`服务非常有限, 主要用于ABP框架的内部使用. 对于你自己的应用程序, 可以使用DistributedLock库自己的API. 参见[文档](https://github.com/madelson/DistributedLock)详细信息. diff --git a/docs/zh-Hans/Entities.md b/docs/zh-Hans/Entities.md index 8fbf16ddd3..b9c236e346 100644 --- a/docs/zh-Hans/Entities.md +++ b/docs/zh-Hans/Entities.md @@ -105,6 +105,22 @@ public class UserRole : Entity > 需要注意,复合主键实体不可以使用 `IRepository` 接口,因为它需要一个唯一的Id属性. 但你可以使用 `IRepository`.更多信息请参见[仓储](Repositories.md)的文档. +### EntityEquals + +`Entity.EntityEquals(...)` 方法用于检查两个实体对象是否相等. + +示例: + +```csharp +Book book1 = ... +Book book2 = ... + +if (book1.EntityEquals(book2)) //Check equality +{ + ... +} +``` + ## 聚合根 "*聚合是域驱动设计中的一种模式.DDD的聚合是一组可以作为一个单元处理的域对象.例如,订单及订单系列的商品,这些是独立的对象,但将订单(连同订单系列的商品)视为一个聚合通常是很有用的*"( [查看详细介绍](http://martinfowler.com/bliki/DDD_Aggregate.html)) diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md index 472dde9eab..6db625e7d9 100644 --- a/docs/zh-Hans/Multi-Tenancy.md +++ b/docs/zh-Hans/Multi-Tenancy.md @@ -322,7 +322,6 @@ Volo.Abp.AspNetCore.MultiTenancy 添加了下面这些租户解析器,从当前W * **CurrentUserTenantResolveContributor**: 如果当前用户已登录,从当前用户的声明中获取租户Id. **出于安全考虑,应该始终将其做为第一个Contributor**. * **QueryStringTenantResolveContributor**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant". -* **FormTenantResolveContributor**: 尝试从form参数中获取当前租户,默认参数名为"__tenant". * **RouteTenantResolveContributor**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户. * **HeaderTenantResolveContributor**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant". * **CookieTenantResolveContributor**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant". diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs index 523a770a17..c3ba260a88 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs @@ -9,26 +9,43 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly; public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency { - protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } + protected AbpApplicationConfigurationClientProxy ApplicationConfigurationClientProxy { get; } + + protected AbpApplicationLocalizationClientProxy ApplicationLocalizationClientProxy { get; } protected ApplicationConfigurationCache Cache { get; } protected ICurrentTenantAccessor CurrentTenantAccessor { get; } public WebAssemblyCachedApplicationConfigurationClient( - AbpApplicationConfigurationClientProxy applicationConfigurationAppService, + AbpApplicationConfigurationClientProxy applicationConfigurationClientProxy, ApplicationConfigurationCache cache, - ICurrentTenantAccessor currentTenantAccessor) + ICurrentTenantAccessor currentTenantAccessor, + AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy) { - ApplicationConfigurationAppService = applicationConfigurationAppService; + ApplicationConfigurationClientProxy = applicationConfigurationClientProxy; Cache = cache; CurrentTenantAccessor = currentTenantAccessor; + ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; } public virtual async Task InitializeAsync() { - var configurationDto = await ApplicationConfigurationAppService.GetAsync(); + var configurationDto = await ApplicationConfigurationClientProxy.GetAsync( + new ApplicationConfigurationRequestOptions { + IncludeLocalizationResources = false + } + ); + + var localizationDto = await ApplicationLocalizationClientProxy.GetAsync( + new ApplicationLocalizationRequestDto { + CultureName = configurationDto.Localization.CurrentCulture.Name, + OnlyDynamics = true + } + ); + configurationDto.Localization.Resources = localizationDto.Resources; + Cache.Set(configurationDto); CurrentTenantAccessor.Current = new BasicTenantInfo( diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs index ad36cf0f11..e0d8ecd806 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs @@ -15,7 +15,6 @@ public class AbpAspNetCoreMultiTenancyModule : AbpModule Configure(options => { options.TenantResolvers.Add(new QueryStringTenantResolveContributor()); - options.TenantResolvers.Add(new FormTenantResolveContributor()); options.TenantResolvers.Add(new RouteTenantResolveContributor()); options.TenantResolvers.Add(new HeaderTenantResolveContributor()); options.TenantResolvers.Add(new CookieTenantResolveContributor()); diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs index 67ef0a4c2c..d55343c94c 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs @@ -1,10 +1,11 @@ -using System.Linq; +using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy; +[Obsolete("This may make some features of ASP NET Core unavailable, Will be removed in future versions.")] public class FormTenantResolveContributor : HttpTenantResolveContributorBase { public const string ContributorName = "Form"; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs index 198cfce711..2d57dc66ab 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs @@ -15,8 +15,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; [ExposeServices(typeof(IAbpApplicationConfigurationAppService), typeof(AbpApplicationConfigurationClientProxy))] public partial class AbpApplicationConfigurationClientProxy : ClientProxyBase, IAbpApplicationConfigurationAppService { - public virtual async Task GetAsync() + public virtual async Task GetAsync(ApplicationConfigurationRequestOptions options) { - return await RequestAsync(nameof(GetAsync)); + return await RequestAsync(nameof(GetAsync), new ClientProxyRequestTypeValue + { + { typeof(ApplicationConfigurationRequestOptions), options } + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.Generated.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.Generated.cs new file mode 100644 index 0000000000..74db054d18 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.Generated.cs @@ -0,0 +1,25 @@ +// This file is automatically generated by ABP framework to use MVC Controllers from CSharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Http.Client; +using Volo.Abp.Http.Modeling; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +// ReSharper disable once CheckNamespace +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(IAbpApplicationLocalizationAppService), typeof(AbpApplicationLocalizationClientProxy))] +public partial class AbpApplicationLocalizationClientProxy : ClientProxyBase, IAbpApplicationLocalizationAppService +{ + public virtual async Task GetAsync(ApplicationLocalizationRequestDto input) + { + return await RequestAsync(nameof(GetAsync), new ClientProxyRequestTypeValue + { + { typeof(ApplicationLocalizationRequestDto), input } + }); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.cs new file mode 100644 index 0000000000..11bef4cecf --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationLocalizationClientProxy.cs @@ -0,0 +1,7 @@ +// This file is part of AbpApplicationLocalizationClientProxy, you can customize it here +// ReSharper disable once CheckNamespace +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; + +public partial class AbpApplicationLocalizationClientProxy +{ +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/abp-generate-proxy.json b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/abp-generate-proxy.json index bf15515078..ce18e3eefd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/abp-generate-proxy.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/abp-generate-proxy.json @@ -7,6 +7,8 @@ "Pages.Abp.MultiTenancy.AbpTenantController": { "controllerName": "AbpTenant", "controllerGroupName": "AbpTenant", + "isRemoteService": true, + "apiVersion": null, "type": "Pages.Abp.MultiTenancy.AbpTenantController", "interfaces": [ { @@ -93,6 +95,8 @@ "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController": { "controllerName": "AbpApplicationConfiguration", "controllerGroupName": "AbpApplicationConfiguration", + "isRemoteService": true, + "apiVersion": null, "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController", "interfaces": [ { @@ -100,14 +104,36 @@ } ], "actions": { - "GetAsync": { - "uniqueName": "GetAsync", + "GetAsyncByOptions": { + "uniqueName": "GetAsyncByOptions", "name": "GetAsync", "httpMethod": "GET", "url": "api/abp/application-configuration", "supportedVersions": [], - "parametersOnMethod": [], - "parameters": [], + "parametersOnMethod": [ + { + "name": "options", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "options", + "name": "IncludeLocalizationResources", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "options" + } + ], "returnValue": { "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" @@ -117,9 +143,74 @@ } } }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationLocalizationController": { + "controllerName": "AbpApplicationLocalization", + "controllerGroupName": "AbpApplicationLocalization", + "isRemoteService": true, + "apiVersion": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationLocalizationController", + "interfaces": [ + { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationLocalizationAppService" + } + ], + "actions": { + "GetAsyncByInput": { + "uniqueName": "GetAsyncByInput", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/abp/application-localization", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "CultureName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "OnlyDynamics", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationLocalizationAppService" + } + } + }, "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController": { "controllerName": "AbpApiDefinition", "controllerGroupName": "AbpApiDefinition", + "isRemoteService": true, + "apiVersion": null, "type": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController", "interfaces": [], "actions": { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteExternalLocalizationStore.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteExternalLocalizationStore.cs new file mode 100644 index 0000000000..c5548acc62 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteExternalLocalizationStore.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.Localization.External; + +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemoteExternalLocalizationStore : IExternalLocalizationStore, ITransientDependency +{ + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + protected AbpLocalizationOptions LocalizationOptions { get; } + + public RemoteExternalLocalizationStore( + ICachedApplicationConfigurationClient configurationClient, + IOptions localizationOptions) + { + ConfigurationClient = configurationClient; + LocalizationOptions = localizationOptions.Value; + } + + public virtual LocalizationResourceBase GetResourceOrNull(string resourceName) + { + var configurationDto = ConfigurationClient.Get(); + return CreateLocalizationResourceFromConfigurationOrNull(resourceName, configurationDto); + } + + public virtual async Task GetResourceOrNullAsync(string resourceName) + { + var configurationDto = await ConfigurationClient.GetAsync(); + return CreateLocalizationResourceFromConfigurationOrNull(resourceName, configurationDto); + } + + public virtual async Task GetResourceNamesAsync() + { + var configurationDto = await ConfigurationClient.GetAsync(); + return configurationDto + .Localization + .Resources + .Keys + .Where(x => !LocalizationOptions.Resources.ContainsKey(x)) + .ToArray(); +; } + + public virtual async Task GetResourcesAsync() + { + var configurationDto = await ConfigurationClient.GetAsync(); + var resources = new List(); + + foreach (var resource in configurationDto.Localization.Resources) + { + if (LocalizationOptions.Resources.ContainsKey(resource.Key)) + { + continue; + } + + resources.Add(CreateNonTypedLocalizationResource(resource.Key, resource.Value)); + } + + return resources.ToArray(); + } + + protected virtual LocalizationResourceBase CreateLocalizationResourceFromConfigurationOrNull( + string resourceName, + ApplicationConfigurationDto configurationDto) + { + var resourceDto = configurationDto.Localization.Resources.GetOrDefault(resourceName); + + if (resourceDto == null) + { + return null; + } + + return CreateNonTypedLocalizationResource(resourceName, resourceDto); + } + + protected virtual NonTypedLocalizationResource CreateNonTypedLocalizationResource( + string resourceName, + ApplicationLocalizationResourceDto resourceDto) + { + return new NonTypedLocalizationResource(resourceName) + .AddBaseResources(resourceDto.BaseResources); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs index c01fc6a1af..e6e180c5f2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs @@ -1,15 +1,20 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.Localization; namespace Volo.Abp.AspNetCore.Mvc.Client; public class RemoteLocalizationContributor : ILocalizationResourceContributor { - private LocalizationResource _resource; + public bool IsDynamic => true; + + private LocalizationResourceBase _resource; private ICachedApplicationConfigurationClient _applicationConfigurationClient; private ILogger _logger; @@ -21,50 +26,137 @@ public class RemoteLocalizationContributor : ILocalizationResourceContributor ?? NullLogger.Instance; } - public LocalizedString GetOrNull(string cultureName, string name) + public virtual LocalizedString GetOrNull(string cultureName, string name) + { + /* cultureName is not used because remote localization can only + * be done in the current culture. */ + + return GetOrNullInternal(_resource.ResourceName, name); + } + + protected virtual LocalizedString GetOrNullInternal(string resourceName, string name) { - var resource = GetResourceOrNull(); + var resource = GetResourceOrNull(resourceName); if (resource == null) { return null; } - var value = resource.GetOrDefault(name); - if (value == null) + var value = resource.Texts.GetOrDefault(name); + if (value != null) { - return null; + return new LocalizedString(name, value); + } + + foreach (var baseResource in resource.BaseResources) + { + value = GetOrNullInternal(baseResource, name); + if (value != null) + { + return new LocalizedString(name, value); + } + } + + return null; + } + + public virtual void Fill(string cultureName, Dictionary dictionary) + { + /* cultureName is not used because remote localization can only + * be done in the current culture. */ + + FillInternal(_resource.ResourceName, dictionary); + } + + protected virtual void FillInternal(string resourceName, Dictionary dictionary) + { + var resource = GetResourceOrNull(resourceName); + if (resource == null) + { + return; + } + + foreach (var baseResource in resource.BaseResources) + { + FillInternal(baseResource, dictionary); } - return new LocalizedString(name, value); + foreach (var keyValue in resource.Texts) + { + dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); + } } - public void Fill(string cultureName, Dictionary dictionary) + public virtual async Task FillAsync(string cultureName, Dictionary dictionary) + { + /* cultureName is not used because remote localization can only + * be done in the current culture. */ + + await FillInternalAsync(_resource.ResourceName, dictionary); + } + + protected virtual async Task FillInternalAsync(string resourceName, Dictionary dictionary) { - var resource = GetResourceOrNull(); + var resource = await GetResourceOrNullAsync(resourceName); if (resource == null) { return; } + + foreach (var baseResource in resource.BaseResources) + { + await FillInternalAsync(baseResource, dictionary); + } - foreach (var keyValue in resource) + foreach (var keyValue in resource.Texts) { dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); } } - private Dictionary GetResourceOrNull() + public virtual Task> GetSupportedCulturesAsync() + { + /* This contributor does not know all the supported cultures by the + remote localization resource, and it is not needed on the client side */ + return Task.FromResult((IEnumerable)Array.Empty()); + } + + protected virtual ApplicationLocalizationResourceDto GetResourceOrNull(string resourceName) { var applicationConfigurationDto = _applicationConfigurationClient.Get(); + return GetResourceOrNull(applicationConfigurationDto, resourceName); + } + + protected virtual async Task GetResourceOrNullAsync(string resourceName) + { + var applicationConfigurationDto = await _applicationConfigurationClient.GetAsync(); + return GetResourceOrNull(applicationConfigurationDto, resourceName); + } - var resource = applicationConfigurationDto + protected virtual ApplicationLocalizationResourceDto GetResourceOrNull( + ApplicationConfigurationDto applicationConfigurationDto, + string resourceName) + { + var resource = applicationConfigurationDto.Localization.Resources.GetOrDefault(resourceName); + if (resource != null) + { + return resource; + } + + var legacyResource = applicationConfigurationDto .Localization.Values - .GetOrDefault(_resource.ResourceName); + .GetOrDefault(resourceName); - if (resource == null) + if (legacyResource != null) { - _logger.LogWarning($"Could not find the localization resource {_resource.ResourceName} on the remote server!"); + return new ApplicationLocalizationResourceDto + { + Texts = legacyResource, + BaseResources = Array.Empty() + }; } - return resource; + _logger.LogWarning($"Could not find the localization resource {resourceName} on the remote server!"); + return null; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs index 7bb2e67b49..405d43906d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs @@ -15,6 +15,7 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu { protected IHttpContextAccessor HttpContextAccessor { get; } protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } + protected AbpApplicationLocalizationClientProxy ApplicationLocalizationClientProxy { get; } protected ICurrentUser CurrentUser { get; } protected IDistributedCache Cache { get; } @@ -22,11 +23,13 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu IDistributedCache cache, AbpApplicationConfigurationClientProxy applicationConfigurationAppService, ICurrentUser currentUser, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy) { ApplicationConfigurationAppService = applicationConfigurationAppService; CurrentUser = currentUser; HttpContextAccessor = httpContextAccessor; + ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; Cache = cache; } @@ -42,7 +45,7 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu configuration = await Cache.GetOrAddAsync( cacheKey, - async () => await ApplicationConfigurationAppService.GetAsync(), + async () => await GetRemoteConfigurationAsync(), () => new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(300) //TODO: Should be configurable. @@ -57,6 +60,27 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu return configuration; } + private async Task GetRemoteConfigurationAsync() + { + var config = await ApplicationConfigurationAppService.GetAsync( + new ApplicationConfigurationRequestOptions + { + IncludeLocalizationResources = false + } + ); + + var localizationDto = await ApplicationLocalizationClientProxy.GetAsync( + new ApplicationLocalizationRequestDto { + CultureName = config.Localization.CurrentCulture.Name, + OnlyDynamics = true + } + ); + + config.Localization.Resources = localizationDto.Resources; + + return config; + } + public ApplicationConfigurationDto Get() { var cacheKey = CreateCacheKey(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationRequestOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationRequestOptions.cs new file mode 100644 index 0000000000..a01534775a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationRequestOptions.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class ApplicationConfigurationRequestOptions +{ + /// + /// Set to true to fill the Values property in . + /// + public bool IncludeLocalizationResources { get; set; } = true; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs index f92c9d7bb9..b1c0c16e76 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs @@ -7,8 +7,23 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; [Serializable] public class ApplicationLocalizationConfigurationDto { + /// + /// This is not filled if is false. + /// public Dictionary> Values { get; set; } + /// + /// This property will never be filled by the application configuration endpoint + /// (by AbpApplicationConfigurationAppService). However, it is here to be filled + /// using the application localization endpoint (AbpApplicationLocalizationAppService). + /// This is an ugly design, but it is the best solution for backward-compability and + /// simple implementation. + /// + /// It's client's responsibility to fill this property + /// using the application localization endpoint. + /// + public Dictionary Resources { get; set; } = new(); + public List Languages { get; set; } public CurrentCultureDto CurrentCulture { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationDto.cs new file mode 100644 index 0000000000..d301490bed --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationLocalizationDto +{ + public Dictionary Resources { get; set; } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationRequestDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationRequestDto.cs new file mode 100644 index 0000000000..535d572b3f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationRequestDto.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class ApplicationLocalizationRequestDto +{ + [Required] + public string CultureName { get; set; } + + public bool OnlyDynamics { get; set; } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationResourceDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationResourceDto.cs new file mode 100644 index 0000000000..a7200423d2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationResourceDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationLocalizationResourceDto +{ + public Dictionary Texts { get; set; } + + public string[] BaseResources { get; set; } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs index 8f1896e31f..83f1e0c803 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using System; +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] public class CurrentCultureDto { public string DisplayName { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs index 9c8fd3d414..ae4f22cc06 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs @@ -5,5 +5,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; public interface IAbpApplicationConfigurationAppService : IApplicationService { - Task GetAsync(); -} + Task GetAsync(ApplicationConfigurationRequestOptions options); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationLocalizationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationLocalizationAppService.cs new file mode 100644 index 0000000000..af72c8f7ee --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationLocalizationAppService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public interface IAbpApplicationLocalizationAppService : IApplicationService +{ + Task GetAsync(ApplicationLocalizationRequestDto input); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs index f6b709c897..7f218e7fbf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs @@ -16,7 +16,7 @@ public class BundleFileContributor : BundleContributor { foreach (var file in Files) { - context.Files.AddIfNotContains(file); + context.Files.AddIfNotContains(x => x.Equals(file, StringComparison.OrdinalIgnoreCase), () => file); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json index 108b3520d7..0e9d2b2281 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json @@ -1,13 +1,13 @@ { - "culture": "hu", - "texts": { - "GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}", - "GivenTenantIsNotAvailable": "A megadott előfizető nem érhető el: {0}", - "Tenant": "Előfizető", - "Switch": "váltás", - "Name": "Neve", - "SwitchTenantHint": "Hagyja üresen az előfizető nevet hogy a host oldalra kapcsoljon.", - "SwitchTenant": "Előfizető váltás", - "NotSelected": "Nincs kiválasztva" - } + "culture": "hu", + "texts": { + "GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}", + "GivenTenantIsNotAvailable": "A megadott előfizető nem érhető el: {0}", + "Tenant": "Előfizető", + "Switch": "váltás", + "Name": "Neve", + "SwitchTenantHint": "Hagyja üresen az előfizető nevet hogy a host oldalra kapcsoljon.", + "SwitchTenant": "Előfizető váltás", + "NotSelected": "Nincs kiválasztva" } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs index 097fad5f40..56ae47f0c8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; @@ -15,7 +16,7 @@ public class AbpPageToolbarButtonViewComponent : AbpViewComponent StringLocalizerFactory = stringLocalizerFactory; } - public IViewComponentResult Invoke( + public async Task InvokeAsync( ILocalizableString text, string name, string icon, @@ -31,11 +32,11 @@ public class AbpPageToolbarButtonViewComponent : AbpViewComponent return View( "~/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml", new AbpPageToolbarButtonViewModel( - text.Localize(StringLocalizerFactory), + await text.LocalizeAsync(StringLocalizerFactory), name, icon, id, - busyText?.Localize(StringLocalizerFactory), + busyText == null ? null : await busyText.LocalizeAsync(StringLocalizerFactory), iconType, type, size, diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs index 4bc4673860..24c9b071bd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs @@ -43,4 +43,14 @@ public static class ActionDescriptorExtensions { return actionDescriptor is PageActionDescriptor; } + + public static PageActionDescriptor AsPageAction(this ActionDescriptor actionDescriptor) + { + if (!actionDescriptor.IsPageAction()) + { + throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(PageActionDescriptor).AssemblyQualifiedName}"); + } + + return actionDescriptor as PageActionDescriptor; + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 7bcc0b3cd9..0deae4b5b3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -17,6 +17,7 @@ using Volo.Abp.Data; using Volo.Abp.Features; using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; +using Volo.Abp.Localization.External; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; using Volo.Abp.Timing; @@ -82,7 +83,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp _multiTenancyOptions = multiTenancyOptions.Value; } - public virtual async Task GetAsync() + public virtual async Task GetAsync(ApplicationConfigurationRequestOptions options) { //TODO: Optimize & cache..? @@ -93,7 +94,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp Auth = await GetAuthConfigAsync(), Features = await GetFeaturesConfigAsync(), GlobalFeatures = await GetGlobalFeaturesConfigAsync(), - Localization = await GetLocalizationConfigAsync(), + Localization = await GetLocalizationConfigAsync(options), CurrentUser = GetCurrentUser(), Setting = await GetSettingConfigAsync(), MultiTenancy = GetMultiTenancy(), @@ -205,26 +206,42 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp return authConfig; } - protected virtual async Task GetLocalizationConfigAsync() + protected virtual async Task GetLocalizationConfigAsync( + ApplicationConfigurationRequestOptions options) { var localizationConfig = new ApplicationLocalizationConfigurationDto(); localizationConfig.Languages.AddRange(await _languageProvider.GetLanguagesAsync()); - foreach (var resource in _localizationOptions.Resources.Values) + if (options.IncludeLocalizationResources) { - var dictionary = new Dictionary(); - - var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService( - typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType) - ); - - foreach (var localizedString in localizer.GetAllStrings()) + var resourceNames = _localizationOptions + .Resources + .Values + .Select(x => x.ResourceName) + .Union( + await LazyServiceProvider + .LazyGetRequiredService() + .GetResourceNamesAsync() + ); + + foreach (var resourceName in resourceNames) { - dictionary[localizedString.Name] = localizedString.Value; - } + var dictionary = new Dictionary(); + + var localizer = await StringLocalizerFactory + .CreateByResourceNameOrNullAsync(resourceName); + + if (localizer != null) + { + foreach (var localizedString in await localizer.GetAllStringsAsync()) + { + dictionary[localizedString.Name] = localizedString.Value; + } + } - localizationConfig.Values[resource.ResourceName] = dictionary; + localizationConfig.Values[resourceName] = dictionary; + } } localizationConfig.CurrentCulture = GetCurrentCultureInfo(); @@ -291,7 +308,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp { var result = new ApplicationFeatureConfigurationDto(); - foreach (var featureDefinition in _featureDefinitionManager.GetAll()) + foreach (var featureDefinition in await _featureDefinitionManager.GetAllAsync()) { if (!featureDefinition.IsVisibleToClients) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs index 6c410a8a7b..427aaf65a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs @@ -21,9 +21,10 @@ public class AbpApplicationConfigurationController : AbpControllerBase, IAbpAppl } [HttpGet] - public virtual async Task GetAsync() + public virtual async Task GetAsync( + ApplicationConfigurationRequestOptions options) { _antiForgeryManager.SetCookie(); - return await _applicationConfigurationAppService.GetAsync(); + return await _applicationConfigurationAppService.GetAsync(options); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationScriptController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationScriptController.cs index 92a4bdef0d..7fafee261c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationScriptController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationScriptController.cs @@ -17,14 +17,14 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; [ApiExplorerSettings(IgnoreApi = true)] public class AbpApplicationConfigurationScriptController : AbpController { - private readonly IAbpApplicationConfigurationAppService _configurationAppService; + private readonly AbpApplicationConfigurationAppService _configurationAppService; private readonly IJsonSerializer _jsonSerializer; private readonly AbpAspNetCoreMvcOptions _options; private readonly IJavascriptMinifier _javascriptMinifier; private readonly IAbpAntiForgeryManager _antiForgeryManager; public AbpApplicationConfigurationScriptController( - IAbpApplicationConfigurationAppService configurationAppService, + AbpApplicationConfigurationAppService configurationAppService, IJsonSerializer jsonSerializer, IOptions options, IJavascriptMinifier javascriptMinifier, @@ -41,7 +41,13 @@ public class AbpApplicationConfigurationScriptController : AbpController [Produces(MimeTypes.Application.Javascript, MimeTypes.Text.Plain)] public async Task Get() { - var script = CreateAbpExtendScript(await _configurationAppService.GetAsync()); + var script = CreateAbpExtendScript( + await _configurationAppService.GetAsync( + new ApplicationConfigurationRequestOptions { + IncludeLocalizationResources = false + } + ) + ); _antiForgeryManager.SetCookie(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationAppService.cs new file mode 100644 index 0000000000..48d7f949c3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationAppService.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Volo.Abp.Application.Services; +using Volo.Abp.Localization; +using Volo.Abp.Localization.External; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class AbpApplicationLocalizationAppService : + ApplicationService, + IAbpApplicationLocalizationAppService +{ + protected IExternalLocalizationStore ExternalLocalizationStore { get; } + protected AbpLocalizationOptions LocalizationOptions { get; } + + public AbpApplicationLocalizationAppService( + IExternalLocalizationStore externalLocalizationStore, + IOptions localizationOptions) + { + ExternalLocalizationStore = externalLocalizationStore; + LocalizationOptions = localizationOptions.Value; + } + + public async Task GetAsync(ApplicationLocalizationRequestDto input) + { + using (CultureHelper.Use(input.CultureName)) + { + var resources = LocalizationOptions + .Resources + .Values + .Union( + await ExternalLocalizationStore.GetResourcesAsync() + ).ToArray(); + + var localizationConfig = new ApplicationLocalizationDto { + Resources = new Dictionary(resources.Length) + }; + + foreach (var resource in resources) + { + var dictionary = new Dictionary(); + var localizer = await StringLocalizerFactory.CreateByResourceNameOrNullAsync(resource.ResourceName); + if (localizer != null) + { + Dictionary staticLocalizedStrings = null; + + if (input.OnlyDynamics) + { + staticLocalizedStrings = (await localizer.GetAllStringsAsync( + includeParentCultures: true, + includeBaseLocalizers: false, + includeDynamicContributors: false + )).ToDictionary(x => x.Name); + } + + var localizedStringsWithDynamics = await localizer.GetAllStringsAsync( + includeParentCultures: true, + includeBaseLocalizers: false, + includeDynamicContributors: true + ); + + foreach (var localizedString in localizedStringsWithDynamics) + { + if (input.OnlyDynamics) + { + var staticLocalizedString = staticLocalizedStrings.GetOrDefault(localizedString.Name); + if (staticLocalizedString != null && + localizedString.Value == staticLocalizedString.Value) + { + continue; + } + } + + dictionary[localizedString.Name] = localizedString.Value; + } + } + + localizationConfig.Resources[resource.ResourceName] = + new ApplicationLocalizationResourceDto { + Texts = dictionary, + BaseResources = resource.BaseResourceNames.ToArray() + }; + } + + return localizationConfig; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationController.cs new file mode 100644 index 0000000000..a056746022 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationLocalizationController.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Area("abp")] +[RemoteService(Name = "abp")] +[Route("api/abp/application-localization")] +public class AbpApplicationLocalizationController: AbpControllerBase, IAbpApplicationLocalizationAppService +{ + private readonly IAbpApplicationLocalizationAppService _localizationAppService; + + public AbpApplicationLocalizationController(IAbpApplicationLocalizationAppService localizationAppService) + { + _localizationAppService = localizationAppService; + } + + [HttpGet] + public virtual async Task GetAsync(ApplicationLocalizationRequestDto input) + { + return await _localizationAppService.GetAsync(input); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs index 8acfcefd3d..0f9b05830d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs @@ -204,9 +204,7 @@ public class CachedObjectExtensionsDtoService : ICachedObjectExtensionsDtoServic { return new LocalizableStringDto( localizableStringInstance.Name, - localizableStringInstance.ResourceType != null - ? LocalizationResourceNameAttribute.GetName(localizableStringInstance.ResourceType) - : null + localizableStringInstance.ResourceName ); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs index 47ed4c4614..dfffeea5b4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs @@ -70,7 +70,9 @@ public class AbpAuditActionFilter : IAsyncActionFilter, ITransientDependency } var auditingHelper = context.GetRequiredService(); - if (!auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true)) + if (!auditingHelper.ShouldSaveAudit( + context.ActionDescriptor.GetMethodInfo(), + defaultValue: GetDefaultAuditBehavior(options, context.ActionDescriptor))) { return false; } @@ -85,4 +87,20 @@ public class AbpAuditActionFilter : IAsyncActionFilter, ITransientDependency return true; } + + private static bool GetDefaultAuditBehavior( + AbpAuditingOptions abpAuditingOptions, + ActionDescriptor actionDescriptor) + { + if (!abpAuditingOptions.IsEnabledForIntegrationServices && + actionDescriptor + .AsControllerActionDescriptor() + .ControllerTypeInfo + .IsDefined(typeof(IntegrationServiceAttribute), true)) + { + return false; + } + + return true; + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs index 2abfd477db..1f347decf7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs @@ -75,7 +75,7 @@ public class AbpAuditPageFilter : IAsyncPageFilter, ITransientDependency } var auditingHelper = context.GetRequiredService(); - if (!auditingHelper.ShouldSaveAudit(context.HandlerMethod.MethodInfo, true)) + if (!auditingHelper.ShouldSaveAudit(context.HandlerMethod.MethodInfo, defaultValue: true)) { return false; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalRouteBuilder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalRouteBuilder.cs index 3891827c0f..0906d46006 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalRouteBuilder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalRouteBuilder.cs @@ -26,9 +26,11 @@ public class ConventionalRouteBuilder : IConventionalRouteBuilder, ITransientDep string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { - var controllerNameInUrl = NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration); + var apiRoutePrefix = GetApiRoutePrefix(action, configuration); + var controllerNameInUrl = + NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration); - var url = $"api/{rootPath}/{NormalizeControllerNameCase(controllerNameInUrl, configuration)}"; + var url = $"{apiRoutePrefix}/{rootPath}/{NormalizeControllerNameCase(controllerNameInUrl, configuration)}"; //Add {id} path if needed var idParameterModel = action.Parameters.FirstOrDefault(p => p.ParameterName == "id"); @@ -69,6 +71,16 @@ public class ConventionalRouteBuilder : IConventionalRouteBuilder, ITransientDep return url; } + protected virtual string GetApiRoutePrefix(ActionModel actionModel, ConventionalControllerSetting configuration) + { + if (actionModel.Controller.ControllerType.IsDefined(typeof(IntegrationServiceAttribute), true)) + { + return AbpAspNetCoreConsts.DefaultIntegrationServiceApiPrefix; + } + + return AbpAspNetCoreConsts.DefaultApiPrefix; + } + protected virtual string NormalizeUrlActionName(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { @@ -108,7 +120,8 @@ public class ConventionalRouteBuilder : IConventionalRouteBuilder, ITransientDep ); } - protected virtual string NormalizeControllerNameCase(string controllerName, [CanBeNull] ConventionalControllerSetting configuration) + protected virtual string NormalizeControllerNameCase(string controllerName, + [CanBeNull] ConventionalControllerSetting configuration) { if (configuration?.UseV3UrlStyle ?? Options.UseV3UrlStyle) { @@ -120,7 +133,8 @@ public class ConventionalRouteBuilder : IConventionalRouteBuilder, ITransientDep } } - protected virtual string NormalizeActionNameCase(string actionName, [CanBeNull] ConventionalControllerSetting configuration) + protected virtual string NormalizeActionNameCase(string actionName, + [CanBeNull] ConventionalControllerSetting configuration) { if (configuration?.UseV3UrlStyle ?? Options.UseV3UrlStyle) { @@ -132,13 +146,15 @@ public class ConventionalRouteBuilder : IConventionalRouteBuilder, ITransientDep } } - protected virtual string NormalizeIdPropertyNameCase(PropertyInfo property, [CanBeNull] ConventionalControllerSetting configuration) + protected virtual string NormalizeIdPropertyNameCase(PropertyInfo property, + [CanBeNull] ConventionalControllerSetting configuration) { return property.Name; } - protected virtual string NormalizeSecondaryIdNameCase(ParameterModel secondaryId, [CanBeNull] ConventionalControllerSetting configuration) + protected virtual string NormalizeSecondaryIdNameCase(ParameterModel secondaryId, + [CanBeNull] ConventionalControllerSetting configuration) { return secondaryId.ParameterName; } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpApplicationLocalizationScriptController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpApplicationLocalizationScriptController.cs new file mode 100644 index 0000000000..d8da85f871 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpApplicationLocalizationScriptController.cs @@ -0,0 +1,69 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using Volo.Abp.Auditing; +using Volo.Abp.Http; +using Volo.Abp.Json; +using Volo.Abp.Localization; +using Volo.Abp.Minify.Scripts; + +namespace Volo.Abp.AspNetCore.Mvc.Localization; + +[Area("Abp")] +[Route("Abp/ApplicationLocalizationScript")] +[DisableAuditing] +[RemoteService(false)] +[ApiExplorerSettings(IgnoreApi = true)] +public class AbpApplicationLocalizationScriptController : AbpController +{ + protected AbpApplicationLocalizationAppService LocalizationAppService { get; } + protected AbpAspNetCoreMvcOptions Options { get; } + protected IJsonSerializer JsonSerializer { get; } + protected IJavascriptMinifier JavascriptMinifier { get; } + + public AbpApplicationLocalizationScriptController( + AbpApplicationLocalizationAppService localizationAppService, + IOptions options, + IJsonSerializer jsonSerializer, + IJavascriptMinifier javascriptMinifier) + { + LocalizationAppService = localizationAppService; + JsonSerializer = jsonSerializer; + JavascriptMinifier = javascriptMinifier; + Options = options.Value; + } + + [HttpGet] + [Produces(MimeTypes.Application.Javascript, MimeTypes.Text.Plain)] + public async Task GetAsync(ApplicationLocalizationRequestDto input) + { + var script = CreateScript( + await LocalizationAppService.GetAsync(input) + ); + + return Content( + Options.MinifyGeneratedScript == true + ? JavascriptMinifier.Minify(script) + : script, + MimeTypes.Application.Javascript + ); + } + + private string CreateScript(ApplicationLocalizationDto localizationDto) + { + var script = new StringBuilder(); + + script.AppendLine("(function(){"); + script.AppendLine(); + script.AppendLine( + $"$.extend(true, abp.localization, {JsonSerializer.Serialize(localizationDto, indented: true)})"); + script.AppendLine(); + script.Append("})();"); + + return script.ToString(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs index dc44f12124..9f5d6aa530 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs @@ -3,12 +3,14 @@ using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; using Microsoft.AspNetCore.RequestLocalization; +using Volo.Abp.Auditing; using Volo.Abp.Localization; namespace Volo.Abp.AspNetCore.Mvc.Localization; [Area("Abp")] [Route("Abp/Languages/[action]")] +[DisableAuditing] [RemoteService(false)] [ApiExplorerSettings(IgnoreApi = true)] public class AbpLanguagesController : AbpController diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ProxyScripting/ServiceProxyGenerationModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ProxyScripting/ServiceProxyGenerationModel.cs index 20ac2e4cb8..3501882e3a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ProxyScripting/ServiceProxyGenerationModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ProxyScripting/ServiceProxyGenerationModel.cs @@ -5,7 +5,7 @@ using Volo.Abp.Http.ProxyScripting.Generators.JQuery; namespace Volo.Abp.AspNetCore.Mvc.ProxyScripting; -public class ServiceProxyGenerationModel //: TODO: IShouldNormalize +public class ServiceProxyGenerationModel { public string Type { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs index 1324df0865..6fd02a0470 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs @@ -27,7 +27,9 @@ public class AbpRequestLocalizationMiddleware : IMiddleware, ITransientDependenc { var middleware = new RequestLocalizationMiddleware( next, - new OptionsWrapper(await _requestLocalizationOptionsProvider.GetLocalizationOptionsAsync()), + new OptionsWrapper( + await _requestLocalizationOptionsProvider.GetLocalizationOptionsAsync() + ), _loggerFactory ); diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreConsts.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreConsts.cs new file mode 100644 index 0000000000..dff987b6b3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AspNetCore; + +public static class AbpAspNetCoreConsts +{ + public const string DefaultApiPrefix = "api"; + public const string DefaultIntegrationServiceApiPrefix = "integration-api"; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAspNetCoreAuditingOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAspNetCoreAuditingOptions.cs index 7977205096..693945e903 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAspNetCoreAuditingOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAspNetCoreAuditingOptions.cs @@ -10,5 +10,5 @@ public class AbpAspNetCoreAuditingOptions /// will be disabled for URLs /// starting with an ignored URL. /// - public List IgnoredUrls { get; } = new List(); + public List IgnoredUrls { get; } = new(); } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs index 9cae64501a..83deaef26a 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs @@ -94,8 +94,23 @@ public class AbpAuditingMiddleware : IMiddleware, ITransientDependency private bool IsIgnoredUrl(HttpContext context) { - return context.Request.Path.Value != null && - AspNetCoreAuditingOptions.IgnoredUrls.Any(x => context.Request.Path.Value.StartsWith(x)); + if (context.Request.Path.Value == null) + { + return false; + } + + if (!AuditingOptions.IsEnabledForIntegrationServices && + context.Request.Path.Value.StartsWith($"/{AbpAspNetCoreConsts.DefaultIntegrationServiceApiPrefix}/")) + { + return true; + } + + if (AspNetCoreAuditingOptions.IgnoredUrls.Any(x => context.Request.Path.Value.StartsWith(x))) + { + return true; + } + + return false; } private async Task ShouldWriteAuditLogAsync(AuditLogInfo auditLogInfo, HttpContext httpContext, bool hasError) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs index b3c6aeb051..441cd8ef23 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs @@ -37,6 +37,12 @@ public class AbpAuditingOptions /// Default: true. /// public bool AlwaysLogOnException { get; set; } + + /// + /// Disables/enables audit logging for integration services. + /// Default: false. + /// + public bool IsEnabledForIntegrationServices { get; set; } public List>> AlwaysLogSelectors { get; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs index 1f3c6b433c..7468f10986 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs @@ -52,7 +52,7 @@ public class AuditingHelper : IAuditingHelper, ITransientDependency CorrelationIdProvider = correlationIdProvider; } - public virtual bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false) + public virtual bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false, bool ignoreIntegrationServiceAttribute = false) { if (methodInfo == null) { @@ -77,7 +77,7 @@ public class AuditingHelper : IAuditingHelper, ITransientDependency var classType = methodInfo.DeclaringType; if (classType != null) { - var shouldAudit = AuditingInterceptorRegistrar.ShouldAuditTypeByDefaultOrNull(classType); + var shouldAudit = AuditingInterceptorRegistrar.ShouldAuditTypeByDefaultOrNull(classType, ignoreIntegrationServiceAttribute); if (shouldAudit != null) { return shouldAudit.Value; diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs index 63c38ef3c3..78de793b6c 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs @@ -62,7 +62,9 @@ public class AuditingInterceptor : AbpInterceptor, ITransientDependency return false; } - if (!auditingHelper.ShouldSaveAudit(invocation.Method)) + if (!auditingHelper.ShouldSaveAudit( + invocation.Method, + ignoreIntegrationServiceAttribute: options.IsEnabledForIntegrationServices)) { return false; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs index 45af44ef91..db5d44cec5 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs @@ -22,7 +22,7 @@ public static class AuditingInterceptorRegistrar return false; } - if (ShouldAuditTypeByDefaultOrNull(type) == true) + if (ShouldAuditTypeByDefaultOrNull(type, ignoreIntegrationServiceAttribute: true) == true) { return true; } @@ -36,7 +36,7 @@ public static class AuditingInterceptorRegistrar } //TODO: Move to a better place - public static bool? ShouldAuditTypeByDefaultOrNull(Type type) + public static bool? ShouldAuditTypeByDefaultOrNull(Type type, bool ignoreIntegrationServiceAttribute) { //TODO: In an inheritance chain, it would be better to check the attributes on the top class first. @@ -52,7 +52,10 @@ public static class AuditingInterceptorRegistrar if (typeof(IAuditingEnabled).IsAssignableFrom(type)) { - return true; + if (ignoreIntegrationServiceAttribute || !type.IsDefined(typeof(IntegrationServiceAttribute), true)) + { + return true; + } } return null; diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs index f6e1b76cd7..5e92f6622e 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs @@ -9,19 +9,19 @@ public class EntityPropertyChangeInfo /// Maximum length of property. /// Value: 96. /// - public const int MaxPropertyNameLength = 96; + public static int MaxPropertyNameLength = 96; /// /// Maximum length of and properties. /// Value: 512. /// - public const int MaxValueLength = 512; + public static int MaxValueLength = 512; /// /// Maximum length of property. /// Value: 512. /// - public const int MaxPropertyTypeFullNameLength = 192; + public static int MaxPropertyTypeFullNameLength = 192; public virtual string NewValue { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs index 915d2fcfc1..5a2878d1b0 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.Auditing; //TODO: Move ShouldSaveAudit & IsEntityHistoryEnabled and rename to IAuditingFactory public interface IAuditingHelper { - bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false); + bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false, bool ignoreIntegrationServiceAttribute = false); bool IsEntityHistoryEnabled(Type entityType, bool defaultValue = false); diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs index 17ff28798e..4bdd7c0bb8 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Collections; +using System.Collections.Generic; +using Volo.Abp.Collections; namespace Volo.Abp.Authorization.Permissions; @@ -8,9 +9,16 @@ public class AbpPermissionOptions public ITypeList ValueProviders { get; } + public HashSet DeletedPermissions { get; } + + public HashSet DeletedPermissionGroups { get; } + public AbpPermissionOptions() { DefinitionProviders = new TypeList(); ValueProviders = new TypeList(); + + DeletedPermissions = new HashSet(); + DeletedPermissionGroups = new HashSet(); } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs index 2ebad0797f..2583ada9fc 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs @@ -1,6 +1,9 @@ using System; +using System.Reflection; using System.Threading.Tasks; using Hangfire; +using Hangfire.States; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; namespace Volo.Abp.BackgroundJobs.Hangfire; @@ -8,16 +11,35 @@ namespace Volo.Abp.BackgroundJobs.Hangfire; [Dependency(ReplaceServices = true)] public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency { + protected AbpBackgroundJobOptions Options { get; } + + public HangfireBackgroundJobManager(IOptions options) + { + Options = options.Value; + } + public virtual Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { return Task.FromResult(delay.HasValue ? BackgroundJob.Schedule>( - adapter => adapter.ExecuteAsync(args), + adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)),args), delay.Value ) : BackgroundJob.Enqueue>( - adapter => adapter.ExecuteAsync(args) + adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)) ,args) )); } + + protected virtual string GetQueueName(Type argsType) + { + var queueName = EnqueuedState.DefaultQueue; + var queueAttribute = Options.GetJob(argsType).JobType.GetCustomAttribute(); + if (queueAttribute != null) + { + queueName = queueAttribute.Queue; + } + + return queueName; + } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs index a2ad7dc77d..7ba0cd7db4 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Hangfire; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -20,7 +21,8 @@ public class HangfireJobExecutionAdapter Options = options.Value; } - public async Task ExecuteAsync(TArgs args) + [Queue("{0}")] + public async Task ExecuteAsync(string queue, TArgs args) { if (!Options.IsJobExecutionEnabled) { diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs index 6c09a80f73..08cc66130c 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.BackgroundWorkers; +using Volo.Abp.Data; using Volo.Abp.DistributedLocking; using Volo.Abp.Guids; using Volo.Abp.Modularity; @@ -19,10 +20,20 @@ namespace Volo.Abp.BackgroundJobs; )] public class AbpBackgroundJobsModule : AbpModule { - public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + public override void ConfigureServices(ServiceConfigurationContext context) { - var options = context.ServiceProvider.GetRequiredService>().Value; - if (options.IsJobExecutionEnabled) + if (context.Services.IsDataMigrationEnvironment()) + { + Configure(options => + { + options.IsJobExecutionEnabled = false; + }); + } + } + + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + { + if (context.ServiceProvider.GetRequiredService>().Value.IsJobExecutionEnabled) { await context.AddBackgroundWorkerAsync(); } diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs index a1a079fe27..359ea1a422 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs @@ -5,6 +5,7 @@ using Blazorise.Snackbar; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Components.Notifications; +using Volo.Abp.Localization; namespace Volo.Abp.BlazoriseUI.Components; @@ -54,13 +55,15 @@ public partial class UiNotificationAlert : ComponentBase, IDisposable Title = e.Title; Options = e.Options; - var okButtonText = Options?.OkButtonText?.Localize(StringLocalizerFactory); + var okButtonText = Options?.OkButtonText == null + ? null + : await Options.OkButtonText.LocalizeAsync(StringLocalizerFactory); await SnackbarStack.PushAsync(Message, Title, GetSnackbarColor(e.NotificationType), (options) => - { - options.CloseButtonIcon = IconName.Times; - options.ActionButtonText = okButtonText; - }); + { + options.CloseButtonIcon = IconName.Times; + options.ActionButtonText = okButtonText; + }); } public virtual void Dispose() diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index c51ec76fc1..a72a369655 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -21,26 +21,115 @@ namespace Volo.Abp.Caching; /// /// The type of cache item being cached. public class DistributedCache : - DistributedCache, IDistributedCache where TCacheItem : class { - public DistributedCache( - IOptions distributedCacheOption, - IDistributedCache cache, - ICancellationTokenProvider cancellationTokenProvider, - IDistributedCacheSerializer serializer, - IDistributedCacheKeyNormalizer keyNormalizer, - IServiceScopeFactory serviceScopeFactory, - IUnitOfWorkManager unitOfWorkManager) : base( - distributedCacheOption: distributedCacheOption, - cache: cache, - cancellationTokenProvider: cancellationTokenProvider, - serializer: serializer, - keyNormalizer: keyNormalizer, - serviceScopeFactory: serviceScopeFactory, - unitOfWorkManager: unitOfWorkManager) + public IDistributedCache InternalCache { get; } + + public DistributedCache(IDistributedCache internalCache) + { + InternalCache = internalCache; + } + + public TCacheItem Get(string key, bool? hideErrors = null, bool considerUow = false) + { + return InternalCache.Get(key, hideErrors, considerUow); + } + + public KeyValuePair[] GetMany(IEnumerable keys, bool? hideErrors = null, bool considerUow = false) + { + return InternalCache.GetMany(keys, hideErrors, considerUow); + } + + public Task[]> GetManyAsync(IEnumerable keys, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.GetManyAsync(keys, hideErrors, considerUow, token); + } + + public Task GetAsync(string key, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.GetAsync(key, hideErrors, considerUow, token); + } + + public TCacheItem GetOrAdd(string key, Func factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false) + { + return InternalCache.GetOrAdd(key, factory, optionsFactory, hideErrors, considerUow); + } + + public Task GetOrAddAsync(string key, Func> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.GetOrAddAsync(key, factory, optionsFactory, hideErrors, considerUow, token); + } + + public KeyValuePair[] GetOrAddMany(IEnumerable keys, Func, List>> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false) + { + return InternalCache.GetOrAddMany(keys, factory, optionsFactory, hideErrors, considerUow); + } + + public Task[]> GetOrAddManyAsync(IEnumerable keys, Func, Task>>> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.GetOrAddManyAsync(keys, factory, optionsFactory, hideErrors, considerUow, token); + } + + public void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false) + { + InternalCache.Set(key, value, options, hideErrors, considerUow); + } + + public Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.SetAsync(key, value, options, hideErrors, considerUow, token); + } + + public void SetMany(IEnumerable> items, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false) + { + InternalCache.SetMany(items, options, hideErrors, considerUow); + } + + public Task SetManyAsync(IEnumerable> items, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.SetManyAsync(items, options, hideErrors, considerUow, token); + } + + public void Refresh(string key, bool? hideErrors = null) + { + InternalCache.Refresh(key, hideErrors); + } + + public Task RefreshAsync(string key, bool? hideErrors = null, CancellationToken token = default) + { + return InternalCache.RefreshAsync(key, hideErrors, token); + } + + public void RefreshMany(IEnumerable keys, bool? hideErrors = null) + { + InternalCache.RefreshMany(keys, hideErrors); + } + + public Task RefreshManyAsync(IEnumerable keys, bool? hideErrors = null, CancellationToken token = default) + { + return InternalCache.RefreshManyAsync(keys, hideErrors, token); + } + + public void Remove(string key, bool? hideErrors = null, bool considerUow = false) + { + InternalCache.Remove(key, hideErrors, considerUow); + } + + public Task RemoveAsync(string key, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default) + { + return InternalCache.RemoveAsync(key, hideErrors, considerUow, token); + } + + public void RemoveMany(IEnumerable keys, bool? hideErrors = null, bool considerUow = false) + { + InternalCache.RemoveMany(keys, hideErrors, considerUow); + } + + public Task RemoveManyAsync(IEnumerable keys, bool? hideErrors = null, bool considerUow = false, + CancellationToken token = default) { + return InternalCache.RemoveManyAsync(keys, hideErrors, considerUow, token); } } diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 21a2842e15..ab3ca560a8 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -14,6 +14,7 @@ namespace Volo.Abp.Caching; public interface IDistributedCache : IDistributedCache where TCacheItem : class { + IDistributedCache InternalCache { get; } } /// diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index 10c65d95e5..6f013272c3 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -221,7 +221,9 @@ public class AbpIoSourceCodeStore : ISourceCodeStore, ITransientDependency } catch (Exception ex) { - throw new Exception($"Error occured while getting the versions from {url} : {ex.Message}"); + Logger.LogWarning($"Error occured while getting the versions from {url} : {ex.Message}"); + // The remote service is currently unavailable, try to work offline. + return true; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs index 33ecad0719..f3b0fed914 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs @@ -61,7 +61,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep ChangeNamespace( context, "/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs", - $"Volo.Abp.AspNetCore.Components.Web.{defaultThemeName}Theme.Themes.{defaultThemeName}", + $"Volo.Abp.AspNetCore.Components.Web.{defaultThemeName}Theme.Components", "Volo.Abp.AspNetCore.Components.Web.BasicTheme.Themes.Basic" ); @@ -69,7 +69,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep #region Blazor.Server Projects - ChangeThemeToBasicForBlazorServerProjects(context, defaultThemeName); + ChangeThemeToBasicForBlazorProjects(context, defaultThemeName); #endregion @@ -105,6 +105,24 @@ public class ChangeThemeStep : ProjectBuildPipelineStep "node_modules/bootstrap-icons/font/bootstrap-icons.css" ); + if(defaultThemeName == "LeptonX") + { + ReplaceMethodNames( + context, + "/angular/src/app/app.module.ts", + "HttpErrorComponent, ", + "" + ); + + ChangeModuleImportBetweenStatements( + context, + "/angular/src/app/app.module.ts", + "ThemeSharedModule.forRoot", + "AccountAdminConfigModule.forRoot", + "ThemeSharedModule.forRoot()," + ); + } + #endregion } @@ -510,6 +528,41 @@ public class ChangeThemeStep : ProjectBuildPipelineStep file.SetLines(lines.Where(x => x != null)); } + private void ChangeModuleImportBetweenStatements( + ProjectBuildContext context, + string filePath, + string firstStatement, + string lastStatement, + string newStatement) + { + var file = context.Files.FirstOrDefault(x => x.Name.Contains(filePath)); + if (file == null) + { + return; + } + + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + var firstLineIndex = lines.FindIndex(line => line.Contains(firstStatement)); + var lastLineIndex = lines.FindIndex(line => line.Contains(lastStatement)); + + if(firstLineIndex == -1 || lastLineIndex == -1) + { + return; + } + + lines[firstLineIndex] = newStatement; + + for (var i = firstLineIndex + 1; i <= lastLineIndex; i++) + { + lines[i] = null; + } + + + file.SetLines(lines.Where(x => x != null)); + } + protected void ReplaceMethodNames( ProjectBuildContext context, string filePath, @@ -668,7 +721,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep } } - private void ChangeThemeToBasicForBlazorServerProjects(ProjectBuildContext context, string defaultThemeName) + private void ChangeThemeToBasicForBlazorProjects(ProjectBuildContext context, string defaultThemeName) { var projects = new Dictionary { @@ -733,7 +786,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep ChangeNamespaceAndKeyword( context, $"/MyCompanyName.MyProjectName.{project.Key}/Pages/_Host.cshtml", - $"Volo.Abp.AspNetCore.Components.Web.{defaultThemeName}Theme.Themes.{defaultThemeName}", + $"Volo.Abp.AspNetCore.Components.Web.{defaultThemeName}Theme.Components", "Volo.Abp.AspNetCore.Components.Web.BasicTheme.Themes.Basic", $"Blazor{defaultThemeName}ThemeBundles.Styles.Global", "BlazorBasicThemeBundles.Styles.Global" diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveDependencyFromPackageJsonFileStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveDependencyFromPackageJsonFileStep.cs index 3eabb6621e..bff28d703b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveDependencyFromPackageJsonFileStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveDependencyFromPackageJsonFileStep.cs @@ -17,8 +17,7 @@ public class RemoveDependencyFromPackageJsonFileStep : ProjectBuildPipelineStep public override void Execute(ProjectBuildContext context) { - var packageJsonFile = context.Files.FirstOrDefault(f => f.Name.Contains(_packageJsonFilePath)); - + var packageJsonFile = context.Files.FirstOrDefault(f => f.Name.Contains(_packageJsonFilePath) && !f.Name.Contains("node_modules")); if (packageJsonFile == null) { return; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index 16bea2740f..22d33be136 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -210,7 +210,7 @@ public abstract class AppTemplateBase : TemplateInfo } steps.Add(new ChangeThemeStep()); - RemoveLeptonXThemePackagesFromPackageJsonFiles(steps, isProTemplate: IsPro()); + RemoveLeptonXThemePackagesFromPackageJsonFiles(steps, isProTemplate: IsPro(), uiFramework: context.BuildArgs.UiFramework); } private static bool IsDefaultThemeForTemplate(Theme theme) @@ -224,50 +224,55 @@ public abstract class AppTemplateBase : TemplateInfo return defaultThemesForTemplates.Any(defaultTheme => defaultTheme == theme); } - private static void RemoveLeptonXThemePackagesFromPackageJsonFiles(List steps, bool isProTemplate) + private static void RemoveLeptonXThemePackagesFromPackageJsonFiles(List steps, bool isProTemplate, UiFramework uiFramework) { + var mvcUiPackageName = isProTemplate ? "@volo/abp.aspnetcore.mvc.ui.theme.leptonx" : "@abp/aspnetcore.mvc.ui.theme.leptonxlite"; var packageJsonFilePaths = new List { "/MyCompanyName.MyProjectName.Web/package.json", "/MyCompanyName.MyProjectName.Web.Host/package.json", "/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json", + "/MyCompanyName.MyProjectName.HttpApi.Host/package.json", "/MyCompanyName.MyProjectName.AuthServer/package.json", "/MyCompanyName.MyProjectName/package.json", "/MyCompanyName.MyProjectName.Host/package.json", "/MyCompanyName.MyProjectName.Host.Mongo/package.json" }; - - var blazorServerPackageJsonFilePaths = new List - { - "/MyCompanyName.MyProjectName.Blazor.Server/package.json", - "/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json", - "/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json" - }; - - var angularPackageJsonFilePaths = new List - { - "/angular/package.json" - }; - - var mvcUiPackageName = isProTemplate ? "@volo/abp.aspnetcore.mvc.ui.theme.leptonx" : "@abp/aspnetcore.mvc.ui.theme.leptonxlite"; - var blazorServerUiPackageName = isProTemplate ? "@volo/aspnetcore.components.server.leptonxtheme" : "@abp/aspnetcore.components.server.leptonxlitetheme"; - var ngUiPackageName = isProTemplate ? "@volosoft/abp.ng.theme.lepton-x" : "@abp/ng.theme.lepton-x"; foreach (var packageJsonFilePath in packageJsonFilePaths) { steps.Add(new RemoveDependencyFromPackageJsonFileStep(packageJsonFilePath, mvcUiPackageName)); } - foreach (var blazorServerPackageJsonFilePath in blazorServerPackageJsonFilePaths) + if (uiFramework == UiFramework.BlazorServer) { - steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, mvcUiPackageName)); - steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, blazorServerUiPackageName)); + var blazorServerUiPackageName = isProTemplate ? "@volo/aspnetcore.components.server.leptonxtheme" : "@abp/aspnetcore.components.server.leptonxlitetheme"; + var blazorServerPackageJsonFilePaths = new List + { + "/MyCompanyName.MyProjectName.Blazor/package.json", + "/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json", + "/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json" + }; + + foreach (var blazorServerPackageJsonFilePath in blazorServerPackageJsonFilePaths) + { + steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, mvcUiPackageName)); + steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, blazorServerUiPackageName)); + } } - - foreach (var angularPackageJsonFilePath in angularPackageJsonFilePaths) + else if (uiFramework == UiFramework.Angular) { - steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, ngUiPackageName)); - steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, "bootstrap-icons")); + var ngUiPackageName = isProTemplate ? "@volosoft/abp.ng.theme.lepton-x" : "@abp/ng.theme.lepton-x"; + var angularPackageJsonFilePaths = new List + { + "/angular/package.json" + }; + + foreach (var angularPackageJsonFilePath in angularPackageJsonFilePaths) + { + steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, ngUiPackageName)); + steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, "bootstrap-icons")); + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs index 319326f3d4..54d234b5c3 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs @@ -48,46 +48,50 @@ public abstract class MicroserviceTemplateBase : TemplateInfo } steps.Add(new ChangeThemeStep()); - RemoveLeptonXThemePackagesFromPackageJsonFiles(steps); + RemoveLeptonXThemePackagesFromPackageJsonFiles(steps, uiFramework: context.BuildArgs.UiFramework); } - private static void RemoveLeptonXThemePackagesFromPackageJsonFiles(List steps) + private static void RemoveLeptonXThemePackagesFromPackageJsonFiles(List steps, UiFramework uiFramework) { + var mvcUiPackageName = "@volo/abp.aspnetcore.mvc.ui.theme.leptonx"; var packageJsonFilePaths = new List { "/MyCompanyName.MyProjectName.AuthServer/package.json", "/MyCompanyName.MyProjectName.Web/package.json" }; - var blazorServerPackageJsonFilePaths = new List - { - "/MyCompanyName.MyProjectName.Blazor.Server/package.json" - }; - - var angularPackageJsonFilePaths = new List - { - "/angular/package.json" - }; - - var mvcUiPackageName = "@volo/abp.aspnetcore.mvc.ui.theme.leptonx"; - var blazorServerUiPackageName = "@volo/aspnetcore.components.server.leptonxtheme"; - var ngUiPackageName = "@volosoft/abp.ng.theme.lepton-x"; - foreach (var packageJsonFilePath in packageJsonFilePaths) { steps.Add(new RemoveDependencyFromPackageJsonFileStep(packageJsonFilePath, mvcUiPackageName)); } - foreach (var blazorServerPackageJsonFilePath in blazorServerPackageJsonFilePaths) + if (uiFramework == UiFramework.BlazorServer) { - steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, mvcUiPackageName)); - steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, blazorServerUiPackageName)); + var blazorServerUiPackageName = "@volo/aspnetcore.components.server.leptonxtheme"; + var blazorServerPackageJsonFilePaths = new List + { + "/MyCompanyName.MyProjectName.Blazor/package.json" + }; + + foreach (var blazorServerPackageJsonFilePath in blazorServerPackageJsonFilePaths) + { + steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, mvcUiPackageName)); + steps.Add(new RemoveDependencyFromPackageJsonFileStep(blazorServerPackageJsonFilePath, blazorServerUiPackageName)); + } } - - foreach (var angularPackageJsonFilePath in angularPackageJsonFilePaths) + else if (uiFramework == UiFramework.Angular) { - steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, ngUiPackageName)); - steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, "bootstrap-icons")); + var ngUiPackageName = "@volosoft/abp.ng.theme.lepton-x"; + var angularPackageJsonFilePaths = new List + { + "/angular/package.json" + }; + + foreach (var angularPackageJsonFilePath in angularPackageJsonFilePaths) + { + steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, ngUiPackageName)); + steps.Add(new RemoveDependencyFromPackageJsonFileStep(angularPackageJsonFilePath, "bootstrap-icons")); + } } } diff --git a/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs index 2d19fa8bc2..24ece0e52e 100644 --- a/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace System; @@ -17,6 +18,7 @@ public static class AbpObjectExtensions /// Type to be casted /// Object to cast /// Casted object + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T As(this object obj) where T : class { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IRemoteService.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IRemoteService.cs index f17a0619dc..613781629c 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IRemoteService.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IRemoteService.cs @@ -1,5 +1,5 @@ namespace Volo.Abp; -public interface IRemoteService //TODO: Can we move this to another package? +public interface IRemoteService { } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IntegrationServiceAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IntegrationServiceAttribute.cs new file mode 100644 index 0000000000..016a7a49c7 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IntegrationServiceAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp; + +[AttributeUsage(AttributeTargets.Class)] +public class IntegrationServiceAttribute : Attribute +{ + +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs index dded0d794e..74a62116a4 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Linq; using JetBrains.Annotations; namespace Volo.Abp.Localization; @@ -59,4 +60,45 @@ public static class CultureHelper { return new CultureInfo(cultureName).Parent.Name; } + + public static bool IsCompatibleCulture( + string sourceCultureName, + string targetCultureName) + { + if (sourceCultureName == targetCultureName) + { + return true; + } + + if (sourceCultureName.StartsWith("zh") && targetCultureName.StartsWith("zh")) + { + var culture = new CultureInfo(targetCultureName); + do + { + if (culture.Name == sourceCultureName) + { + return true; + } + + culture = new CultureInfo(culture.Name).Parent; + } while (!culture.Equals(CultureInfo.InvariantCulture)); + } + + if (sourceCultureName.Contains("-")) + { + return false; + } + + if (!targetCultureName.Contains("-")) + { + return false; + } + + if (sourceCultureName == GetBaseCultureName(targetCultureName)) + { + return true; + } + + return false; + } } diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironment.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironment.cs new file mode 100644 index 0000000000..cdbdf13eb0 --- /dev/null +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironment.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Data; + +public class AbpDataMigrationEnvironment +{ + +} diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironmentExtensions.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironmentExtensions.cs new file mode 100644 index 0000000000..29907b9385 --- /dev/null +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataMigrationEnvironmentExtensions.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Data; + +public static class AbpDataMigrationEnvironmentExtensions +{ + public static void AddDataMigrationEnvironment(this AbpApplicationCreationOptions options, AbpDataMigrationEnvironment environment = null) + { + options.Services.AddDataMigrationEnvironment(environment ?? new AbpDataMigrationEnvironment()); + } + + public static void AddDataMigrationEnvironment(this IServiceCollection services, AbpDataMigrationEnvironment environment = null) + { + services.AddObjectAccessor(environment ?? new AbpDataMigrationEnvironment()); + } + + public static AbpDataMigrationEnvironment GetDataMigrationEnvironment(this IServiceCollection services) + { + return services.GetObjectOrNull>()?.Value; + } + + public static bool IsDataMigrationEnvironment(this IServiceCollection services) + { + return services.GetDataMigrationEnvironment() != null; + } + + public static AbpDataMigrationEnvironment GetDataMigrationEnvironment(this IServiceProvider serviceProvider) + { + return serviceProvider.GetService>()?.Value; + } + + public static bool IsDataMigrationEnvironment(this IServiceProvider serviceProvider) + { + return serviceProvider.GetDataMigrationEnvironment() != null; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/hu.json b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/hu.json index 6ec29c05e5..abde74576e 100644 --- a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/hu.json +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/hu.json @@ -1,6 +1,6 @@ { - "culture": "hu", - "texts": { - "MaxResultCountExceededExceptionMessage": "{0} nem lehet több mint {1}! Csökkentse {2}.{3} a szerver oldalon, hogy több redeményt kapjon." - } + "culture": "hu", + "texts": { + "MaxResultCountExceededExceptionMessage": "{0} nem lehet több mint {1}! Csökkentse {2}.{3} a szerver oldalon, hogy több redeményt kapjon." + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs index 34fe187f81..ce59e46832 100644 --- a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs @@ -3,8 +3,7 @@ /// /// This interface must be implemented by all application services to register and identify them by convention. /// -public interface IApplicationService : - IRemoteService +public interface IApplicationService : IRemoteService { -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs index 38a7efdb2e..bef608a6ff 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using Volo.Abp.Application.Services; +using Volo.Abp.Aspects; +using Volo.Abp.Auditing; using Volo.Abp.Authorization; using Volo.Abp.Domain; using Volo.Abp.Features; @@ -33,10 +35,12 @@ public class AbpDddApplicationModule : AbpModule { Configure(options => { - //TODO: Should we move related items to their own projects? - options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService)); options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService)); options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IAuditingEnabled)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IValidationEnabled)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IGlobalFeatureCheckingEnabled)); }); } } diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index 03b2857ddc..b24e4b2900 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -41,9 +41,9 @@ public abstract class ApplicationService : [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } - public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "Service" }; + public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "IntService", "IntegrationService", "Service" }; - public List AppliedCrossCuttingConcerns { get; } = new List(); + public List AppliedCrossCuttingConcerns { get; } = new(); protected IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService(); diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/AbpDistributedLockOptions.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/AbpDistributedLockOptions.cs new file mode 100644 index 0000000000..8a885ae877 --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/AbpDistributedLockOptions.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.DistributedLocking; + +public class AbpDistributedLockOptions +{ + /// + /// DistributedLock key prefix. + /// + public string KeyPrefix { get; set; } + + public AbpDistributedLockOptions() + { + KeyPrefix = ""; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/DistributedLockKeyNormalizer.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/DistributedLockKeyNormalizer.cs new file mode 100644 index 0000000000..bce237c5e3 --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/DistributedLockKeyNormalizer.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.DistributedLocking; + +public class DistributedLockKeyNormalizer : IDistributedLockKeyNormalizer, ITransientDependency +{ + protected AbpDistributedLockOptions Options { get; } + + public DistributedLockKeyNormalizer(IOptions options) + { + Options = options.Value; + } + + public virtual string NormalizeKey(string name) + { + return $"{Options.KeyPrefix}{name}"; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IDistributedLockKeyNormalizer.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IDistributedLockKeyNormalizer.cs new file mode 100644 index 0000000000..13f30cca28 --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IDistributedLockKeyNormalizer.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.DistributedLocking; + +public interface IDistributedLockKeyNormalizer +{ + string NormalizeKey(string name); + +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs index 9eb7a76b9d..fdec4f1d3f 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs @@ -9,15 +9,22 @@ namespace Volo.Abp.DistributedLocking; public class LocalAbpDistributedLock : IAbpDistributedLock, ISingletonDependency { private readonly ConcurrentDictionary _localSyncObjects = new(); + protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } + public LocalAbpDistributedLock(IDistributedLockKeyNormalizer distributedLockKeyNormalizer) + { + DistributedLockKeyNormalizer = distributedLockKeyNormalizer; + } + public async Task TryAcquireAsync( string name, TimeSpan timeout = default, CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(name, nameof(name)); - - var semaphore = _localSyncObjects.GetOrAdd(name, _ => new SemaphoreSlim(1, 1)); + var key = DistributedLockKeyNormalizer.NormalizeKey(name); + + var semaphore = _localSyncObjects.GetOrAdd(key, _ => new SemaphoreSlim(1, 1)); if (!await semaphore.WaitAsync(timeout, cancellationToken)) { diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs index 7f95a56542..b4116c105c 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs @@ -10,13 +10,16 @@ public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency protected AbpDaprClientFactory DaprClientFactory { get; } protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; } protected AbpDaprOptions DaprOptions { get; } + protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } public DaprAbpDistributedLock( AbpDaprClientFactory daprClientFactory, IOptions distributedLockDaprOptions, - IOptions daprOptions) + IOptions daprOptions, + IDistributedLockKeyNormalizer distributedLockKeyNormalizer) { DaprClientFactory = daprClientFactory; + DistributedLockKeyNormalizer = distributedLockKeyNormalizer; DaprOptions = daprOptions.Value; DistributedLockDaprOptions = distributedLockDaprOptions.Value; } @@ -32,10 +35,11 @@ public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency } var daprClient = await DaprClientFactory.CreateAsync(); + var key = DistributedLockKeyNormalizer.NormalizeKey(name); var lockResponse = await daprClient.Lock( DistributedLockDaprOptions.StoreName, - name, + key, DaprOptions.AppId, (int)timeout.TotalSeconds, cancellationToken); diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs index 769303a348..0defc5a75f 100644 --- a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs @@ -12,13 +12,17 @@ public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDepend { protected IDistributedLockProvider DistributedLockProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; } + + protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } public MedallionAbpDistributedLock( IDistributedLockProvider distributedLockProvider, - ICancellationTokenProvider cancellationTokenProvider) + ICancellationTokenProvider cancellationTokenProvider, + IDistributedLockKeyNormalizer distributedLockKeyNormalizer) { DistributedLockProvider = distributedLockProvider; CancellationTokenProvider = cancellationTokenProvider; + DistributedLockKeyNormalizer = distributedLockKeyNormalizer; } public async Task TryAcquireAsync( @@ -27,11 +31,12 @@ public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDepend CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(name, nameof(name)); - + var key = DistributedLockKeyNormalizer.NormalizeKey(name); + CancellationTokenProvider.FallbackToProvider(cancellationToken); var handle = await DistributedLockProvider.TryAcquireLockAsync( - name, + key, timeout, CancellationTokenProvider.FallbackToProvider(cancellationToken) ); diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/hu.json b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/hu.json index f2dbce2961..e9eae5a82a 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/hu.json +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/hu.json @@ -1,25 +1,25 @@ { - "culture": "hu", - "texts": { - "DisplayName:Abp.Mailing.DefaultFromAddress": "Alapértlemezett feladó címe", - "DisplayName:Abp.Mailing.DefaultFromDisplayName": "Feladó alapértelmezett megjelenő neve", - "DisplayName:Abp.Mailing.Smtp.Host": "Kiszolgáló", - "DisplayName:Abp.Mailing.Smtp.Port": "Port", - "DisplayName:Abp.Mailing.Smtp.UserName": "Felhasználónév", - "DisplayName:Abp.Mailing.Smtp.Password": "Jelszó", - "DisplayName:Abp.Mailing.Smtp.Domain": "Domain", - "DisplayName:Abp.Mailing.Smtp.EnableSsl": "SSL engedélyezése", - "DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Használja az alapértelmezett hitelesítő adatokat", - "Description:Abp.Mailing.DefaultFromAddress": "Az alapértlemezett feladó címe", - "Description:Abp.Mailing.DefaultFromDisplayName": "A feladó alapértelmezett megjelenő neve", - "Description:Abp.Mailing.Smtp.Host": "Az SMTP kiszolgáló neve vagy IP címe.", - "Description:Abp.Mailing.Smtp.Port": "Az SMTP forgalomhoz használt port.", - "Description:Abp.Mailing.Smtp.UserName": "A hitelesítő adatokhoz társított felhasználói név..", - "Description:Abp.Mailing.Smtp.Password": "A hitelesítő adatokhoz társított felhasználói név jelszava.", - "Description:Abp.Mailing.Smtp.Domain": "A hitelesítő adatokat igazoló tartomány vagy számítógép neve.", - "Description:Abp.Mailing.Smtp.EnableSsl": "Engedélyezze, ha a levelező programja Secure Sockets Layer (SSL) protokollt használ a kapcsolat titkosításához.", - "Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Alkalmazza, hogy a kérésekhez az alapértelmezett hitelesítést használja.", - "TextTemplate:StandardEmailTemplates.Layout": "Alapértelmezett e-mail elrendezési sablon", - "TextTemplate:StandardEmailTemplates.Message": "Egyszerű üzenet sablon az e-mailekhez" - } - } \ No newline at end of file + "culture": "hu", + "texts": { + "DisplayName:Abp.Mailing.DefaultFromAddress": "Alapértlemezett feladó címe", + "DisplayName:Abp.Mailing.DefaultFromDisplayName": "Feladó alapértelmezett megjelenő neve", + "DisplayName:Abp.Mailing.Smtp.Host": "Kiszolgáló", + "DisplayName:Abp.Mailing.Smtp.Port": "Port", + "DisplayName:Abp.Mailing.Smtp.UserName": "Felhasználónév", + "DisplayName:Abp.Mailing.Smtp.Password": "Jelszó", + "DisplayName:Abp.Mailing.Smtp.Domain": "Domain", + "DisplayName:Abp.Mailing.Smtp.EnableSsl": "SSL engedélyezése", + "DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Használja az alapértelmezett hitelesítő adatokat", + "Description:Abp.Mailing.DefaultFromAddress": "Az alapértlemezett feladó címe", + "Description:Abp.Mailing.DefaultFromDisplayName": "A feladó alapértelmezett megjelenő neve", + "Description:Abp.Mailing.Smtp.Host": "Az SMTP kiszolgáló neve vagy IP címe.", + "Description:Abp.Mailing.Smtp.Port": "Az SMTP forgalomhoz használt port.", + "Description:Abp.Mailing.Smtp.UserName": "A hitelesítő adatokhoz társított felhasználói név..", + "Description:Abp.Mailing.Smtp.Password": "A hitelesítő adatokhoz társított felhasználói név jelszava.", + "Description:Abp.Mailing.Smtp.Domain": "A hitelesítő adatokat igazoló tartomány vagy számítógép neve.", + "Description:Abp.Mailing.Smtp.EnableSsl": "Engedélyezze, ha a levelező programja Secure Sockets Layer (SSL) protokollt használ a kapcsolat titkosításához.", + "Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Alkalmazza, hogy a kérésekhez az alapértelmezett hitelesítést használja.", + "TextTemplate:StandardEmailTemplates.Layout": "Alapértelmezett e-mail elrendezési sablon", + "TextTemplate:StandardEmailTemplates.Message": "Egyszerű üzenet sablon az e-mailekhez" + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs index 22c1067082..87ff7716db 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Collections; +using System.Collections.Generic; +using Volo.Abp.Collections; namespace Volo.Abp.Features; @@ -8,9 +9,16 @@ public class AbpFeatureOptions public ITypeList ValueProviders { get; } + public HashSet DeletedFeatures { get; } + + public HashSet DeletedFeatureGroups { get; } + public AbpFeatureOptions() { DefinitionProviders = new TypeList(); ValueProviders = new TypeList(); + + DeletedFeatures = new HashSet(); + DeletedFeatureGroups = new HashSet(); } } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs index 580881ce84..9554860078 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs @@ -37,7 +37,7 @@ public class FeatureChecker : FeatureCheckerBase public override async Task GetOrNullAsync(string name) { - var featureDefinition = FeatureDefinitionManager.Get(name); + var featureDefinition = await FeatureDefinitionManager.GetAsync(name); var providers = Enumerable .Reverse(Providers); diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs index 3123faefc2..5ea7377fd7 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs @@ -7,7 +7,7 @@ using Volo.Abp.Validation.StringValues; namespace Volo.Abp.Features; -public class FeatureDefinition +public class FeatureDefinition : ICanCreateChildFeature { /// /// Unique name of the feature. @@ -178,6 +178,17 @@ public class FeatureDefinition _children.Remove(featureToRemove); } + public FeatureDefinition CreateChildFeature(string name, + string defaultValue = null, + ILocalizableString displayName = null, + ILocalizableString description = null, + IStringValueType valueType = null, + bool isVisibleToClients = true, + bool isAvailableToHost = true) + { + return this.CreateChild(name, defaultValue, displayName, description, valueType, isVisibleToClients, isAvailableToHost); + } + public override string ToString() { return $"[{nameof(FeatureDefinition)}: {Name}]"; diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs index c39db18771..d0293d5859 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs @@ -2,120 +2,70 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Features; public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency { - protected IDictionary FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value; - private readonly Lazy> _lazyFeatureGroupDefinitions; - - protected IDictionary FeatureDefinitions => _lazyFeatureDefinitions.Value; - private readonly Lazy> _lazyFeatureDefinitions; - - protected AbpFeatureOptions Options { get; } - - private readonly IServiceScopeFactory _serviceScopeFactory; + protected IStaticFeatureDefinitionStore StaticStore; + protected IDynamicFeatureDefinitionStore DynamicStore; public FeatureDefinitionManager( - IOptions options, - IServiceScopeFactory serviceScopeFactory) + IStaticFeatureDefinitionStore staticStore, + IDynamicFeatureDefinitionStore dynamicStore) { - _serviceScopeFactory = serviceScopeFactory; - Options = options.Value; - - _lazyFeatureDefinitions = new Lazy>( - CreateFeatureDefinitions, - isThreadSafe: true - ); - - _lazyFeatureGroupDefinitions = new Lazy>( - CreateFeatureGroupDefinitions, - isThreadSafe: true - ); + StaticStore = staticStore; + DynamicStore = dynamicStore; } - public virtual FeatureDefinition Get(string name) + public virtual async Task GetAsync(string name) { - Check.NotNull(name, nameof(name)); - - var feature = GetOrNull(name); - - if (feature == null) + var permission = await GetOrNullAsync(name); + if (permission == null) { throw new AbpException("Undefined feature: " + name); } - return feature; - } - - public virtual IReadOnlyList GetAll() - { - return FeatureDefinitions.Values.ToImmutableList(); - } - - public virtual FeatureDefinition GetOrNull(string name) - { - return FeatureDefinitions.GetOrDefault(name); - } - - public IReadOnlyList GetGroups() - { - return FeatureGroupDefinitions.Values.ToImmutableList(); + return permission; } - protected virtual Dictionary CreateFeatureDefinitions() + public virtual async Task GetOrNullAsync(string name) { - var features = new Dictionary(); - - foreach (var groupDefinition in FeatureGroupDefinitions.Values) - { - foreach (var feature in groupDefinition.Features) - { - AddFeatureToDictionaryRecursively(features, feature); - } - } + Check.NotNull(name, nameof(name)); - return features; + return await StaticStore.GetOrNullAsync(name) ?? + await DynamicStore.GetOrNullAsync(name); } - protected virtual void AddFeatureToDictionaryRecursively( - Dictionary features, - FeatureDefinition feature) + public virtual async Task> GetAllAsync() { - if (features.ContainsKey(feature.Name)) - { - throw new AbpException("Duplicate feature name: " + feature.Name); - } + var staticFeatures = await StaticStore.GetFeaturesAsync(); + var staticFeatureNames = staticFeatures + .Select(p => p.Name) + .ToImmutableHashSet(); - features[feature.Name] = feature; + var dynamicFeatures = await DynamicStore.GetFeaturesAsync(); - foreach (var child in feature.Children) - { - AddFeatureToDictionaryRecursively(features, child); - } + /* We prefer static features over dynamics */ + return staticFeatures.Concat( + dynamicFeatures.Where(d => !staticFeatureNames.Contains(d.Name)) + ).ToImmutableList(); } - protected virtual Dictionary CreateFeatureGroupDefinitions() + public virtual async Task> GetGroupsAsync() { - var context = new FeatureDefinitionContext(); + var staticGroups = await StaticStore.GetGroupsAsync(); + var staticGroupNames = staticGroups + .Select(p => p.Name) + .ToImmutableHashSet(); - using (var scope = _serviceScopeFactory.CreateScope()) - { - var providers = Options - .DefinitionProviders - .Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider) - .ToList(); - - foreach (var provider in providers) - { - provider.Define(context); - } - } + var dynamicGroups = await DynamicStore.GetGroupsAsync(); - return context.Groups; + /* We prefer static groups over dynamics */ + return staticGroups.Concat( + dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)) + ).ToImmutableList(); } } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs index cba2e06584..e584a95c50 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs @@ -5,7 +5,7 @@ using Volo.Abp.Validation.StringValues; namespace Volo.Abp.Features; -public class FeatureGroupDefinition +public class FeatureGroupDefinition : ICanCreateChildFeature { /// /// Unique name of the group. @@ -69,6 +69,16 @@ public class FeatureGroupDefinition return feature; } + public FeatureDefinition CreateChildFeature(string name, + string defaultValue = null, + ILocalizableString displayName = null, + ILocalizableString description = null, + IStringValueType valueType = null, + bool isVisibleToClients = true, + bool isAvailableToHost = true) + { + return AddFeature(name, defaultValue, displayName, description, valueType, isVisibleToClients); + } public virtual List GetFeaturesWithChildren() { var features = new List(); diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/ICanCreateChildFeature.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/ICanCreateChildFeature.cs new file mode 100644 index 0000000000..ecba11e31e --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/ICanCreateChildFeature.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Localization; +using Volo.Abp.Validation.StringValues; + +namespace Volo.Abp.Features; + +public interface ICanCreateChildFeature +{ + FeatureDefinition CreateChildFeature( + string name, + string defaultValue = null, + ILocalizableString displayName = null, + ILocalizableString description = null, + IStringValueType valueType = null, + bool isVisibleToClients = true, + bool isAvailableToHost = true); +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IDynamicFeatureDefinitionStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IDynamicFeatureDefinitionStore.cs new file mode 100644 index 0000000000..90fe14a5e0 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IDynamicFeatureDefinitionStore.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Volo.Abp.Features; + +public interface IDynamicFeatureDefinitionStore +{ + Task GetOrNullAsync(string name); + + Task> GetFeaturesAsync(); + + Task> GetGroupsAsync(); +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs index ebef29b64a..f0201937df 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using JetBrains.Annotations; namespace Volo.Abp.Features; @@ -6,11 +7,11 @@ namespace Volo.Abp.Features; public interface IFeatureDefinitionManager { [NotNull] - FeatureDefinition Get([NotNull] string name); + Task GetAsync([NotNull] string name); - IReadOnlyList GetAll(); + Task> GetAllAsync(); - FeatureDefinition GetOrNull(string name); + Task GetOrNullAsync(string name); - IReadOnlyList GetGroups(); + Task> GetGroupsAsync(); } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IStaticFeatureDefinitionStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IStaticFeatureDefinitionStore.cs new file mode 100644 index 0000000000..586747904e --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IStaticFeatureDefinitionStore.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Volo.Abp.Features; + +public interface IStaticFeatureDefinitionStore +{ + Task GetOrNullAsync(string name); + + Task> GetFeaturesAsync(); + + Task> GetGroupsAsync(); +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullDynamicFeatureDefinitionStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullDynamicFeatureDefinitionStore.cs new file mode 100644 index 0000000000..63e7b476c4 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullDynamicFeatureDefinitionStore.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features; + +public class NullDynamicFeatureDefinitionStore : IDynamicFeatureDefinitionStore, ISingletonDependency +{ + private static readonly Task CachedFeatureResult = Task.FromResult((FeatureDefinition)null); + + private static readonly Task> CachedFeaturesResult = + Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + + private static readonly Task> CachedGroupsResult = + Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + + public Task GetOrNullAsync(string name) + { + return CachedFeatureResult; + } + + public Task> GetFeaturesAsync() + { + return CachedFeaturesResult; + } + + public Task> GetGroupsAsync() + { + return CachedGroupsResult; + } +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs new file mode 100644 index 0000000000..7d683d6caf --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features; + +public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingletonDependency +{ + protected IDictionary FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value; + private readonly Lazy> _lazyFeatureGroupDefinitions; + + protected IDictionary FeatureDefinitions => _lazyFeatureDefinitions.Value; + private readonly Lazy> _lazyFeatureDefinitions; + + protected AbpFeatureOptions Options { get; } + + private readonly IServiceProvider _serviceProvider; + + public StaticFeatureDefinitionStore( + IOptions options, + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + Options = options.Value; + + _lazyFeatureDefinitions = new Lazy>( + CreateFeatureDefinitions, + isThreadSafe: true + ); + + _lazyFeatureGroupDefinitions = new Lazy>( + CreateFeatureGroupDefinitions, + isThreadSafe: true + ); + } + + public virtual async Task GetAsync(string name) + { + Check.NotNull(name, nameof(name)); + + var feature = await GetOrNullAsync(name); + + if (feature == null) + { + throw new AbpException("Undefined feature: " + name); + } + + return feature; + } + + protected virtual Dictionary CreateFeatureDefinitions() + { + var features = new Dictionary(); + + foreach (var groupDefinition in FeatureGroupDefinitions.Values) + { + foreach (var feature in groupDefinition.Features) + { + AddFeatureToDictionaryRecursively(features, feature); + } + } + + return features; + } + + protected virtual void AddFeatureToDictionaryRecursively( + Dictionary features, + FeatureDefinition feature) + { + if (features.ContainsKey(feature.Name)) + { + throw new AbpException("Duplicate feature name: " + feature.Name); + } + + features[feature.Name] = feature; + + foreach (var child in feature.Children) + { + AddFeatureToDictionaryRecursively(features, child); + } + } + + protected virtual Dictionary CreateFeatureGroupDefinitions() + { + var context = new FeatureDefinitionContext(); + + using (var scope = _serviceProvider.CreateScope()) + { + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(context); + } + } + + return context.Groups; + } + + public virtual Task GetOrNullAsync(string name) + { + return Task.FromResult(FeatureDefinitions.GetOrDefault(name)); + } + + public virtual Task> GetFeaturesAsync() + { + return Task.FromResult>(FeatureDefinitions.Values.ToList()); + } + + public virtual Task> GetGroupsAsync() + { + return Task.FromResult>(FeatureGroupDefinitions.Values.ToList()); + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs index 7bc239813d..57233ac972 100644 --- a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs +++ b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net.Http; using System.Reflection; using Castle.DynamicProxy; using JetBrains.Annotations; @@ -196,6 +197,16 @@ public static class ServiceCollectionHttpClientProxyExtensions { clientBuildAction(remoteServiceConfigurationName, provider, client); } + }).ConfigurePrimaryHttpMessageHandler((provider) => + { + var handler = new HttpClientHandler { UseCookies = false }; + + foreach (var handlerAction in preOptions.ProxyClientHandlerActions) + { + handlerAction(remoteServiceConfigurationName, provider, handler); + } + + return handler; }); foreach (var clientBuildAction in preOptions.ProxyClientBuildActions) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientBuilderOptions.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientBuilderOptions.cs index 240f35fe0a..b2ffae98a5 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientBuilderOptions.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientBuilderOptions.cs @@ -12,11 +12,14 @@ public class AbpHttpClientBuilderOptions internal HashSet ConfiguredProxyClients { get; } public List> ProxyClientActions { get; } + + public List> ProxyClientHandlerActions { get; } public AbpHttpClientBuilderOptions() { ProxyClientBuildActions = new List>(); ConfiguredProxyClients = new HashSet(); ProxyClientActions = new List>(); + ProxyClientHandlerActions = new List>(); } } diff --git a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/hu.json b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/hu.json index f5e023cc77..67d6783a0b 100644 --- a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/hu.json +++ b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/hu.json @@ -1,17 +1,17 @@ { - "culture": "hu", - "texts": { - "DisplayName:Abp.Ldap.ServerHost": "Szerver host", - "Description:Abp.Ldap.ServerHost": "Az LDAP ksizolgáló szerver hostneve vagy IP címe", - "DisplayName:Abp.Ldap.ServerPort": "Szerver port", - "Description:Abp.Ldap.ServerPort": "LDAP kommunikáció portja", - "DisplayName:Abp.Ldap.BaseDc": "Bázis tartomány-komponens", - "Description:Abp.Ldap.BaseDc": "Bázis tartomány-komponens", - "DisplayName:Abp.Ldap.Domain": "Tartomány", - "Description:Abp.Ldap.Domain": "Tartomány", - "DisplayName:Abp.Ldap.UserName": "Felhasználónév", - "Description:Abp.Ldap.UserName": "Felhasználónév", - "DisplayName:Abp.Ldap.Password": "Jelszó", - "Description:Abp.Ldap.Password": "Jelszó" - } + "culture": "hu", + "texts": { + "DisplayName:Abp.Ldap.ServerHost": "Szerver host", + "Description:Abp.Ldap.ServerHost": "Az LDAP ksizolgáló szerver hostneve vagy IP címe", + "DisplayName:Abp.Ldap.ServerPort": "Szerver port", + "Description:Abp.Ldap.ServerPort": "LDAP kommunikáció portja", + "DisplayName:Abp.Ldap.BaseDc": "Bázis tartomány-komponens", + "Description:Abp.Ldap.BaseDc": "Bázis tartomány-komponens", + "DisplayName:Abp.Ldap.Domain": "Tartomány", + "Description:Abp.Ldap.Domain": "Tartomány", + "DisplayName:Abp.Ldap.UserName": "Felhasználónév", + "Description:Abp.Ldap.UserName": "Felhasználónév", + "DisplayName:Abp.Ldap.Password": "Jelszó", + "Description:Abp.Ldap.Password": "Jelszó" } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs b/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs index 896fdb6fa0..8aa2515a54 100644 --- a/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs +++ b/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs @@ -1,10 +1,66 @@ -namespace Microsoft.Extensions.Localization; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp; + +namespace Microsoft.Extensions.Localization; public static class AbpStringLocalizerFactoryExtensions { + [CanBeNull] public static IStringLocalizer CreateDefaultOrNull(this IStringLocalizerFactory localizerFactory) { - return (localizerFactory as IAbpStringLocalizerFactoryWithDefaultResourceSupport) + return (localizerFactory as IAbpStringLocalizerFactory) ?.CreateDefaultOrNull(); } + + [CanBeNull] + public static IStringLocalizer CreateByResourceNameOrNull( + this IStringLocalizerFactory localizerFactory, + string resourceName) + { + return (localizerFactory as IAbpStringLocalizerFactory) + ?.CreateByResourceNameOrNull(resourceName); + } + + [NotNull] + public static IStringLocalizer CreateByResourceName( + this IStringLocalizerFactory localizerFactory, + string resourceName) + { + var localizer = localizerFactory.CreateByResourceNameOrNull(resourceName); + if (localizer == null) + { + throw new AbpException("Couldn't find a localizer with given resource name: " + resourceName); + } + + return localizer; + } + + [ItemCanBeNull] + public static async Task CreateByResourceNameOrNullAsync( + this IStringLocalizerFactory localizerFactory, + string resourceName) + { + var abpLocalizerFactory = localizerFactory as IAbpStringLocalizerFactory; + if (abpLocalizerFactory == null) + { + return null; + } + + return await abpLocalizerFactory.CreateByResourceNameOrNullAsync(resourceName); + } + + [NotNull] + public async static Task CreateByResourceNameAsync( + this IStringLocalizerFactory localizerFactory, + string resourceName) + { + var localizer = await localizerFactory.CreateByResourceNameOrNullAsync(resourceName); + if (localizer == null) + { + throw new AbpException("Couldn't find a localizer with given resource name: " + resourceName); + } + + return localizer; + } } diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactory.cs b/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactory.cs new file mode 100644 index 0000000000..8f6699a13d --- /dev/null +++ b/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactory.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Microsoft.Extensions.Localization; + +public interface IAbpStringLocalizerFactory +{ + [CanBeNull] + IStringLocalizer CreateDefaultOrNull(); + + [CanBeNull] + IStringLocalizer CreateByResourceNameOrNull([NotNull] string resourceName); + + [ItemCanBeNull] + Task CreateByResourceNameOrNullAsync([NotNull] string resourceName); +} diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactoryWithDefaultResourceSupport.cs b/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactoryWithDefaultResourceSupport.cs deleted file mode 100644 index 0ab20aec4b..0000000000 --- a/framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactoryWithDefaultResourceSupport.cs +++ /dev/null @@ -1,9 +0,0 @@ -using JetBrains.Annotations; - -namespace Microsoft.Extensions.Localization; - -public interface IAbpStringLocalizerFactoryWithDefaultResourceSupport -{ - [CanBeNull] - IStringLocalizer CreateDefaultOrNull(); -} diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IAsyncLocalizableString.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IAsyncLocalizableString.cs new file mode 100644 index 0000000000..b8389d546f --- /dev/null +++ b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IAsyncLocalizableString.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.Localization; + +public interface IAsyncLocalizableString +{ + Task LocalizeAsync(IStringLocalizerFactory stringLocalizerFactory); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs index 2aecd696ac..d04e61a257 100644 --- a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs +++ b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs @@ -5,4 +5,4 @@ namespace Volo.Abp.Localization; public interface ILocalizableString { LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory); -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs index 88eb8a399d..a98f207a55 100644 --- a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs +++ b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs @@ -1,11 +1,15 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Localization; namespace Volo.Abp.Localization; -public class LocalizableString : ILocalizableString +public class LocalizableString : ILocalizableString, IAsyncLocalizableString { + [CanBeNull] + public string ResourceName { get; } + [CanBeNull] public Type ResourceType { get; } @@ -16,22 +20,53 @@ public class LocalizableString : ILocalizableString { Name = Check.NotNullOrEmpty(name, nameof(name)); ResourceType = resourceType; + + if (resourceType != null) + { + ResourceName = LocalizationResourceNameAttribute.GetName(resourceType); + } + } + + public LocalizableString([NotNull] string name, [CanBeNull] string resourceName = null) + { + Name = Check.NotNullOrEmpty(name, nameof(name)); + ResourceName = resourceName; } public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory) { - var localizer = ResourceType != null - ? stringLocalizerFactory.Create(ResourceType) - : stringLocalizerFactory.CreateDefaultOrNull(); + var localizer = CreateStringLocalizerOrNull(stringLocalizerFactory); + if (localizer == null) + { + return new LocalizedString(Name, Name, resourceNotFound: true); + } + + var result = localizer[Name]; + + if (result.ResourceNotFound && ResourceName != null) + { + /* Search in the default resource if not found in the provided resource */ + localizer = stringLocalizerFactory.CreateDefaultOrNull(); + if (localizer != null) + { + result = localizer[Name]; + } + } + return result; + } + + public async Task LocalizeAsync(IStringLocalizerFactory stringLocalizerFactory) + { + var localizer = await CreateStringLocalizerOrNullAsync(stringLocalizerFactory); if (localizer == null) { - throw new AbpException($"Set {nameof(ResourceType)} or configure the default localization resource type (in the AbpLocalizationOptions)!"); + throw new AbpException($"Set {nameof(ResourceName)} or configure the default localization resource type (in the AbpLocalizationOptions)!"); } var result = localizer[Name]; - if (result.ResourceNotFound && ResourceType != null) + if (result.ResourceNotFound && ResourceName != null) { /* Search in the default resource if not found in the provided resource */ localizer = stringLocalizerFactory.CreateDefaultOrNull(); @@ -44,8 +79,56 @@ public class LocalizableString : ILocalizableString return result; } + private IStringLocalizer CreateStringLocalizerOrNull(IStringLocalizerFactory stringLocalizerFactory) + { + if (ResourceType != null) + { + return stringLocalizerFactory.Create(ResourceType); + } + + if (ResourceName != null) + { + var localizerByName = stringLocalizerFactory.CreateByResourceNameOrNull(ResourceName); + if (localizerByName != null) + { + return localizerByName; + } + } + + return stringLocalizerFactory.CreateDefaultOrNull(); + } + + private async Task CreateStringLocalizerOrNullAsync(IStringLocalizerFactory stringLocalizerFactory) + { + if (ResourceType != null) + { + return stringLocalizerFactory.Create(ResourceType); + } + + if (ResourceName != null) + { + var localizerByName = await stringLocalizerFactory.CreateByResourceNameOrNullAsync(ResourceName); + if (localizerByName != null) + { + return localizerByName; + } + } + + return stringLocalizerFactory.CreateDefaultOrNull(); + } + public static LocalizableString Create([NotNull] string name) { - return new LocalizableString(typeof(TResource), name); + return Create(typeof(TResource), name); + } + + public static LocalizableString Create(Type resourceType,[NotNull] string name) + { + return new LocalizableString(resourceType, name); + } + + public static LocalizableString Create([NotNull] string name, [CanBeNull] string resourceName = null) + { + return new LocalizableString(name, resourceName); } } diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableStringExtensions.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableStringExtensions.cs new file mode 100644 index 0000000000..b7cff0819f --- /dev/null +++ b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableStringExtensions.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.Localization; + +public static class LocalizableStringExtensions +{ + public static async Task LocalizeAsync( + this ILocalizableString localizableString, + IStringLocalizerFactory stringLocalizerFactory) + { + if (localizableString is IAsyncLocalizableString asyncLocalizableString) + { + return await asyncLocalizableString.LocalizeAsync(stringLocalizerFactory); + } + + return localizableString.Localize(stringLocalizerFactory); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj b/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj index 64352257f3..53e71d1c38 100644 --- a/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj +++ b/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj @@ -22,6 +22,8 @@ + + diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs index 61621c2479..cf5c1ebb82 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs @@ -4,13 +4,14 @@ using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Resources; +using System.Threading.Tasks; using Microsoft.Extensions.Localization; namespace Volo.Abp.Localization; -public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance +public class AbpDictionaryBasedStringLocalizer : IAbpStringLocalizer { - public LocalizationResource Resource { get; } + public LocalizationResourceBase Resource { get; } public List BaseLocalizers { get; } @@ -20,7 +21,10 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali public virtual LocalizedString this[string name, params object[] arguments] => GetLocalizedStringFormatted(name, arguments); - public AbpDictionaryBasedStringLocalizer(LocalizationResource resource, List baseLocalizers, AbpLocalizationOptions abpLocalizationOptions) + public AbpDictionaryBasedStringLocalizer( + LocalizationResourceBase resource, + List baseLocalizers, + AbpLocalizationOptions abpLocalizationOptions) { Resource = resource; BaseLocalizers = baseLocalizers; @@ -34,16 +38,46 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali includeParentCultures ); } + + public async Task> GetAllStringsAsync(bool includeParentCultures) + { + return await GetAllStringsAsync( + CultureInfo.CurrentUICulture.Name, + includeParentCultures + ); + } - public IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers) + public IEnumerable GetAllStrings( + bool includeParentCultures, + bool includeBaseLocalizers, + bool includeDynamicContributors) { return GetAllStrings( CultureInfo.CurrentUICulture.Name, includeParentCultures, - includeBaseLocalizers + includeBaseLocalizers, + includeDynamicContributors + ); + } + + public async Task> GetAllStringsAsync( + bool includeParentCultures, + bool includeBaseLocalizers, + bool includeDynamicContributors) + { + return await GetAllStringsAsync( + CultureInfo.CurrentUICulture.Name, + includeParentCultures, + includeBaseLocalizers, + includeDynamicContributors ); } + public Task> GetSupportedCulturesAsync() + { + return Resource.Contributors.GetSupportedCulturesAsync(); + } + protected virtual LocalizedString GetLocalizedStringFormatted(string name, params object[] arguments) { return GetLocalizedStringFormatted(name, CultureInfo.CurrentUICulture.Name, arguments); @@ -84,7 +118,10 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali return value; } - protected virtual LocalizedString GetLocalizedStringOrNull(string name, string cultureName, bool tryDefaults = true) + protected virtual LocalizedString GetLocalizedStringOrNull( + string name, + string cultureName, + bool tryDefaults = true) { //Try to get from original dictionary (with country code) var strOriginal = Resource.Contributors.GetOrNull(cultureName, name); @@ -131,7 +168,67 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali protected virtual IReadOnlyList GetAllStrings( string cultureName, bool includeParentCultures = true, - bool includeBaseLocalizers = true) + bool includeBaseLocalizers = true, + bool includeDynamicContributors = true) + { + //TODO: Can be optimized (example: if it's already default dictionary, skip overriding) + + var allStrings = new Dictionary(); + + if (includeBaseLocalizers) + { + foreach (var baseLocalizer in BaseLocalizers.Select(l => l)) + { + using (CultureHelper.Use(CultureInfo.GetCultureInfo(cultureName))) + { + //TODO: Try/catch is a workaround here! + try + { + var baseLocalizedString = baseLocalizer.GetAllStrings( + includeParentCultures, + includeBaseLocalizers, // Always true, I know! + includeDynamicContributors + ); + + foreach (var localizedString in baseLocalizedString) + { + allStrings[localizedString.Name] = localizedString; + } + } + catch (MissingManifestResourceException) + { + + } + } + } + } + + if (includeParentCultures) + { + //Fill all strings from default culture + if (!Resource.DefaultCultureName.IsNullOrEmpty()) + { + Resource.Contributors.Fill(Resource.DefaultCultureName, allStrings, includeDynamicContributors); + } + + //Overwrite all strings from the language based on country culture + if (cultureName.Contains("-")) + { + Resource.Contributors.Fill(CultureHelper.GetBaseCultureName(cultureName), allStrings, includeDynamicContributors); + } + } + + //Overwrite all strings from the original culture + Resource.Contributors.Fill(cultureName, allStrings, includeDynamicContributors); + + return allStrings.Values.ToImmutableList(); + } + + protected virtual async Task> GetAllStringsAsync( + string cultureName, + bool includeParentCultures = true, + bool includeBaseLocalizers = true, + bool includeDynamicContributors = true) { //TODO: Can be optimized (example: if it's already default dictionary, skip overriding) @@ -146,7 +243,12 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali //TODO: Try/catch is a workaround here! try { - var baseLocalizedString = baseLocalizer.GetAllStrings(includeParentCultures); + var baseLocalizedString = await baseLocalizer.GetAllStringsAsync( + includeParentCultures, + includeBaseLocalizers, // Always true, I know! + includeDynamicContributors + ); + foreach (var localizedString in baseLocalizedString) { allStrings[localizedString.Name] = localizedString; @@ -165,23 +267,35 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali //Fill all strings from default culture if (!Resource.DefaultCultureName.IsNullOrEmpty()) { - Resource.Contributors.Fill(Resource.DefaultCultureName, allStrings); + await Resource.Contributors.FillAsync( + Resource.DefaultCultureName, + allStrings, + includeDynamicContributors + ); } //Overwrite all strings from the language based on country culture if (cultureName.Contains("-")) { - Resource.Contributors.Fill(CultureHelper.GetBaseCultureName(cultureName), allStrings); + await Resource.Contributors.FillAsync( + CultureHelper.GetBaseCultureName(cultureName), + allStrings, + includeDynamicContributors + ); } } //Overwrite all strings from the original culture - Resource.Contributors.Fill(cultureName, allStrings); + await Resource.Contributors.FillAsync( + cultureName, + allStrings, + includeDynamicContributors + ); return allStrings.Values.ToImmutableList(); } - public class CultureWrapperStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance + public class CultureWrapperStringLocalizer : IAbpStringLocalizer { private readonly string _cultureName; private readonly AbpDictionaryBasedStringLocalizer _innerLocalizer; @@ -201,9 +315,28 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali return _innerLocalizer.GetAllStrings(_cultureName, includeParentCultures); } - public IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers) + public IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers, bool includeDynamicContributors) + { + return _innerLocalizer.GetAllStrings(_cultureName, includeParentCultures, includeBaseLocalizers, includeDynamicContributors); + } + + public Task> GetAllStringsAsync(bool includeParentCultures) + { + return _innerLocalizer.GetAllStringsAsync(includeParentCultures); + } + + public Task> GetAllStringsAsync(bool includeParentCultures, bool includeBaseLocalizers, bool includeDynamicContributors) + { + return _innerLocalizer.GetAllStringsAsync( + includeParentCultures, + includeBaseLocalizers, + includeDynamicContributors + ); + } + + public Task> GetSupportedCulturesAsync() { - return _innerLocalizer.GetAllStrings(_cultureName, includeParentCultures, includeBaseLocalizers); + return _innerLocalizer.GetSupportedCulturesAsync(); } } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs index 4ce8c32f88..85be669593 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs @@ -1,6 +1,7 @@ using Volo.Abp.Localization.Resources.AbpLocalization; using Volo.Abp.Modularity; using Volo.Abp.Settings; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Localization; @@ -8,7 +9,8 @@ namespace Volo.Abp.Localization; [DependsOn( typeof(AbpVirtualFileSystemModule), typeof(AbpSettingsModule), - typeof(AbpLocalizationAbstractionsModule) + typeof(AbpLocalizationAbstractionsModule), + typeof(AbpThreadingModule) )] public class AbpLocalizationModule : AbpModule { diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs index 8201043c6e..9b3bb2c201 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Reflection; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Localization; using Volo.Abp.DynamicProxy; @@ -39,14 +41,16 @@ public static class AbpStringLocalizerExtensions public static IEnumerable GetAllStrings( this IStringLocalizer stringLocalizer, bool includeParentCultures, - bool includeBaseLocalizers) + bool includeBaseLocalizers, + bool includeDynamicContributors) { - var internalLocalizer = (ProxyHelper.UnProxy(stringLocalizer) as IStringLocalizer).GetInternalLocalizer(); - if (internalLocalizer is IStringLocalizerSupportsInheritance stringLocalizerSupportsInheritance) + var internalLocalizer = ((IStringLocalizer)ProxyHelper.UnProxy(stringLocalizer)).GetInternalLocalizer(); + if (internalLocalizer is IAbpStringLocalizer abpStringLocalizer) { - return stringLocalizerSupportsInheritance.GetAllStrings( + return abpStringLocalizer.GetAllStrings( includeParentCultures, - includeBaseLocalizers + includeBaseLocalizers, + includeDynamicContributors ); } @@ -54,4 +58,59 @@ public static class AbpStringLocalizerExtensions includeParentCultures ); } + + public static async Task> GetAllStringsAsync( + this IStringLocalizer stringLocalizer, + bool includeParentCultures, + bool includeBaseLocalizers, + bool includeDynamicContributors) + { + var internalLocalizer = ((IStringLocalizer)ProxyHelper.UnProxy(stringLocalizer)).GetInternalLocalizer(); + if (internalLocalizer is IAbpStringLocalizer abpStringLocalizer) + { + return await abpStringLocalizer.GetAllStringsAsync( + includeParentCultures, + includeBaseLocalizers, + includeDynamicContributors + ); + } + + return stringLocalizer.GetAllStrings( + includeParentCultures + ); + } + + public static async Task> GetSupportedCulturesAsync(this IStringLocalizer localizer) + { + var internalLocalizer = ((IStringLocalizer)ProxyHelper.UnProxy(localizer)).GetInternalLocalizer(); + if (internalLocalizer is IAbpStringLocalizer abpStringLocalizer) + { + return await abpStringLocalizer.GetSupportedCulturesAsync(); + } + + return Array.Empty(); + } + + public static Task> GetAllStringsAsync( + this IStringLocalizer localizer) + { + return localizer.GetAllStringsAsync(includeParentCultures: true); + } + + public static Task> GetAllStringsAsync( + this IStringLocalizer localizer, + bool includeParentCultures) + { + Check.NotNull(localizer, nameof(localizer)); + + var internalLocalizer = ((IStringLocalizer)ProxyHelper.UnProxy(localizer)).GetInternalLocalizer(); + if (internalLocalizer is IAbpStringLocalizer abpStringLocalizer) + { + return abpStringLocalizer.GetAllStringsAsync(includeParentCultures: includeParentCultures); + } + + return Task.FromResult( + localizer.GetAllStrings(includeParentCultures: true) + ); + } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs index 3f47fd779d..b89a54abd9 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs @@ -2,60 +2,182 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; +using Volo.Abp.Localization.External; +using Volo.Abp.Threading; namespace Volo.Abp.Localization; -public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLocalizerFactoryWithDefaultResourceSupport +public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLocalizerFactory { protected internal AbpLocalizationOptions AbpLocalizationOptions { get; } protected ResourceManagerStringLocalizerFactory InnerFactory { get; } protected IServiceProvider ServiceProvider { get; } - protected ConcurrentDictionary LocalizerCache { get; } + protected IExternalLocalizationStore ExternalLocalizationStore { get; } + protected ConcurrentDictionary LocalizerCache { get; } + protected SemaphoreSlim LocalizerCacheSemaphore { get; } = new(1, 1); - //TODO: It's better to use decorator pattern for IStringLocalizerFactory instead of getting ResourceManagerStringLocalizerFactory as a dependency. public AbpStringLocalizerFactory( ResourceManagerStringLocalizerFactory innerFactory, IOptions abpLocalizationOptions, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider, + IExternalLocalizationStore externalLocalizationStore) { InnerFactory = innerFactory; ServiceProvider = serviceProvider; + ExternalLocalizationStore = externalLocalizationStore; AbpLocalizationOptions = abpLocalizationOptions.Value; - LocalizerCache = new ConcurrentDictionary(); + LocalizerCache = new ConcurrentDictionary(); } public virtual IStringLocalizer Create(Type resourceType) { - var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceType); + return Create(resourceType, lockCache: true); + } + + private IStringLocalizer Create(Type resourceType, bool lockCache) + { + var resource = AbpLocalizationOptions.Resources.GetOrNull(resourceType); if (resource == null) { return InnerFactory.Create(resourceType); } - if (LocalizerCache.TryGetValue(resourceType, out var cacheItem)) + return CreateInternal(resource.ResourceName, resource, lockCache); + } + + public IStringLocalizer CreateByResourceNameOrNull(string resourceName) + { + return CreateByResourceNameOrNullInternal(resourceName, lockCache: true); + } + + private IStringLocalizer CreateByResourceNameOrNullInternal( + string resourceName, + bool lockCache) + { + var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceName); + if (resource == null) + { + resource = ExternalLocalizationStore.GetResourceOrNull(resourceName); + if (resource == null) + { + return null; + } + } + + return CreateInternal(resourceName, resource, lockCache); + } + + public Task CreateByResourceNameOrNullAsync(string resourceName) + { + return CreateByResourceNameOrNullInternalAsync(resourceName, lockCache: true); + } + + private async Task CreateByResourceNameOrNullInternalAsync( + string resourceName, + bool lockCache) + { + var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceName); + if (resource == null) + { + resource = await ExternalLocalizationStore.GetResourceOrNullAsync(resourceName); + if (resource == null) + { + return null; + } + } + + return await CreateInternalAsync(resourceName, resource, lockCache); + } + + private IStringLocalizer CreateInternal( + string resourceName, + LocalizationResourceBase resource, + bool lockCache) + { + if (LocalizerCache.TryGetValue(resourceName, out var cacheItem)) { return cacheItem.Localizer; } - lock (LocalizerCache) + IStringLocalizer GetOrCreateLocalizer() { + // Double check + if (LocalizerCache.TryGetValue(resourceName, out var cacheItem2)) + { + return cacheItem2.Localizer; + } + return LocalizerCache.GetOrAdd( - resourceType, + resourceName, _ => CreateStringLocalizerCacheItem(resource) ).Localizer; } + + if (lockCache) + { + using (LocalizerCacheSemaphore.Lock()) + { + return GetOrCreateLocalizer(); + } + } + else + { + return GetOrCreateLocalizer(); + } } + + private async Task CreateInternalAsync( + string resourceName, + LocalizationResourceBase resource, + bool lockCache) + { + if (LocalizerCache.TryGetValue(resourceName, out var cacheItem)) + { + return cacheItem.Localizer; + } - private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResource resource) + async Task GetOrCreateLocalizerAsync() + { + // Double check + if (LocalizerCache.TryGetValue(resourceName, out var cacheItem2)) + { + return cacheItem2.Localizer; + } + + var newCacheItem = await CreateStringLocalizerCacheItemAsync(resource); + LocalizerCache[resourceName] = newCacheItem; + return newCacheItem.Localizer; + } + + if (lockCache) + { + using (await LocalizerCacheSemaphore.LockAsync()) + { + return await GetOrCreateLocalizerAsync(); + } + } + else + { + return await GetOrCreateLocalizerAsync(); + } + } + + private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResourceBase resource) { - foreach (var globalContributor in AbpLocalizationOptions.GlobalContributors) + foreach (var globalContributorType in AbpLocalizationOptions.GlobalContributors) { - resource.Contributors.Add((ILocalizationResourceContributor)Activator.CreateInstance(globalContributor)); + resource.Contributors.Add( + Activator + .CreateInstance(globalContributorType) + .As() + ); } var context = new LocalizationResourceInitializationContext(resource, ServiceProvider); @@ -68,7 +190,49 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca return new StringLocalizerCacheItem( new AbpDictionaryBasedStringLocalizer( resource, - resource.BaseResourceTypes.Select(Create).ToList(), + resource + .BaseResourceNames + .Select(x => CreateByResourceNameOrNullInternal(x, lockCache: false)) + .Where(x => x != null) + .ToList(), + AbpLocalizationOptions + ) + ); + } + + private async Task CreateStringLocalizerCacheItemAsync(LocalizationResourceBase resource) + { + foreach (var globalContributorType in AbpLocalizationOptions.GlobalContributors) + { + resource.Contributors.Add( + Activator + .CreateInstance(globalContributorType) + .As() + ); + } + + var context = new LocalizationResourceInitializationContext(resource, ServiceProvider); + + foreach (var contributor in resource.Contributors) + { + contributor.Initialize(context); + } + + var baseLocalizers = new List(); + + foreach (var baseResourceName in resource.BaseResourceNames) + { + var baseLocalizer = await CreateByResourceNameOrNullInternalAsync(baseResourceName, lockCache: false); + if (baseLocalizer != null) + { + baseLocalizers.Add(baseLocalizer); + } + } + + return new StringLocalizerCacheItem( + new AbpDictionaryBasedStringLocalizer( + resource, + baseLocalizers, AbpLocalizationOptions ) ); @@ -76,8 +240,6 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca public virtual IStringLocalizer Create(string baseName, string location) { - //TODO: Investigate when this is called? - return InnerFactory.Create(baseName, location); } @@ -106,4 +268,4 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca return Create(AbpLocalizationOptions.DefaultResourceType); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/IExternalLocalizationStore.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/IExternalLocalizationStore.cs new file mode 100644 index 0000000000..cd48bb1bd9 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/IExternalLocalizationStore.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.Localization.External; + +public interface IExternalLocalizationStore +{ + [CanBeNull] + LocalizationResourceBase GetResourceOrNull([NotNull] string resourceName); + + [ItemCanBeNull] + Task GetResourceOrNullAsync([NotNull] string resourceName); + + Task GetResourceNamesAsync(); + + Task GetResourcesAsync(); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/NullExternalLocalizationStore.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/NullExternalLocalizationStore.cs new file mode 100644 index 0000000000..508f947981 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/NullExternalLocalizationStore.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Localization.External; + +public class NullExternalLocalizationStore : IExternalLocalizationStore, ISingletonDependency +{ + public LocalizationResourceBase GetResourceOrNull(string resourceName) + { + return null; + } + + public Task GetResourceOrNullAsync(string resourceName) + { + return Task.FromResult(null); + } + + public Task GetResourceNamesAsync() + { + return Task.FromResult(Array.Empty()); + } + + public Task GetResourcesAsync() + { + return Task.FromResult(Array.Empty()); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IAbpStringLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IAbpStringLocalizer.cs new file mode 100644 index 0000000000..59fbbcb7b4 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IAbpStringLocalizer.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.Localization; + +public interface IAbpStringLocalizer : IStringLocalizer +{ + IEnumerable GetAllStrings( + bool includeParentCultures, + bool includeBaseLocalizers, + bool includeDynamicContributors + ); + + Task> GetAllStringsAsync( + bool includeParentCultures + ); + + Task> GetAllStringsAsync( + bool includeParentCultures, + bool includeBaseLocalizers, + bool includeDynamicContributors + ); + + Task> GetSupportedCulturesAsync(); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizationResourceContributor.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizationResourceContributor.cs index bfccb660a0..2a79d20821 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizationResourceContributor.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizationResourceContributor.cs @@ -1,13 +1,20 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Localization; namespace Volo.Abp.Localization; public interface ILocalizationResourceContributor { + bool IsDynamic { get; } + void Initialize(LocalizationResourceInitializationContext context); LocalizedString GetOrNull(string cultureName, string name); void Fill(string cultureName, Dictionary dictionary); + + Task FillAsync(string cultureName, Dictionary dictionary); + + Task> GetSupportedCulturesAsync(); } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs deleted file mode 100644 index cf0bff3631..0000000000 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using Microsoft.Extensions.Localization; - -namespace Volo.Abp.Localization; - -public interface IStringLocalizerSupportsInheritance -{ - IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers); -} diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs index 4cfd6e2f32..5006d62ea8 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs @@ -15,9 +15,14 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi public virtual string Serialize(ILocalizableString localizableString) { + if (localizableString == null) + { + return null; + } + if (localizableString is LocalizableString realLocalizableString) { - return $"L:{LocalizationResourceNameAttribute.GetName(realLocalizableString.ResourceType)},{realLocalizableString.Name}"; + return $"L:{realLocalizableString.ResourceName},{realLocalizableString.Name}"; } if (localizableString is FixedLocalizableString fixedLocalizableString) @@ -36,7 +41,7 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi { return new FixedLocalizableString(value); } - + var type = value[0]; switch (type) { @@ -48,22 +53,17 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi { throw new AbpException("Invalid LocalizableString value: " + value); } - + var resourceName = value.Substring(2, commaPosition - 2); var name = value.Substring(commaPosition + 1); if (name.IsNullOrWhiteSpace()) { throw new AbpException("Invalid LocalizableString value: " + value); } - - var resourceType = LocalizationOptions.Resources.GetOrNull(resourceName)?.ResourceType; - - return new LocalizableString( - resourceType, - name - ); + + return LocalizableString.Create(name, resourceName); default: return new FixedLocalizableString(value); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs index a2202dc959..7a55f1f8a3 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs @@ -5,39 +5,21 @@ using JetBrains.Annotations; namespace Volo.Abp.Localization; -public class LocalizationResource +public class LocalizationResource : LocalizationResourceBase { [NotNull] public Type ResourceType { get; } - [NotNull] - public string ResourceName => LocalizationResourceNameAttribute.GetName(ResourceType); - - [CanBeNull] - public string DefaultCultureName { get; set; } - - [NotNull] - public LocalizationResourceContributorList Contributors { get; } - - [NotNull] - public List BaseResourceTypes { get; } - public LocalizationResource( [NotNull] Type resourceType, [CanBeNull] string defaultCultureName = null, [CanBeNull] ILocalizationResourceContributor initialContributor = null) + : base( + LocalizationResourceNameAttribute.GetName(resourceType), + defaultCultureName, + initialContributor) { ResourceType = Check.NotNull(resourceType, nameof(resourceType)); - DefaultCultureName = defaultCultureName; - - BaseResourceTypes = new List(); - Contributors = new LocalizationResourceContributorList(); - - if (initialContributor != null) - { - Contributors.Add(initialContributor); - } - AddBaseResourceTypes(); } @@ -51,8 +33,8 @@ public class LocalizationResource { foreach (var baseResourceType in descriptor.GetInheritedResourceTypes()) { - BaseResourceTypes.AddIfNotContains(baseResourceType); + BaseResourceNames.AddIfNotContains(LocalizationResourceNameAttribute.GetName(baseResourceType)); } } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceBase.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceBase.cs new file mode 100644 index 0000000000..a7ec4fb93b --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceBase.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Localization; + +public abstract class LocalizationResourceBase +{ + [NotNull] + public string ResourceName { get; } + + public List BaseResourceNames { get; } + + [CanBeNull] + public string DefaultCultureName { get; set; } + + [NotNull] + public LocalizationResourceContributorList Contributors { get; } + + public LocalizationResourceBase( + [NotNull] string resourceName, + [CanBeNull] string defaultCultureName = null, + [CanBeNull] ILocalizationResourceContributor initialContributor = null) + { + ResourceName = Check.NotNullOrWhiteSpace(resourceName, nameof(resourceName)); + DefaultCultureName = defaultCultureName; + + Contributors = new LocalizationResourceContributorList(); + BaseResourceNames = new(); + + if (initialContributor != null) + { + Contributors.Add(initialContributor); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs index d52fe78943..6df15920da 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs @@ -1,15 +1,24 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Localization; namespace Volo.Abp.Localization; public class LocalizationResourceContributorList : List { - public LocalizedString GetOrNull(string cultureName, string name) + public LocalizedString GetOrNull( + string cultureName, + string name, + bool includeDynamicContributors = true) { foreach (var contributor in this.Select(x => x).Reverse()) { + if (!includeDynamicContributors && contributor.IsDynamic) + { + continue; + } + var localString = contributor.GetOrNull(cultureName, name); if (localString != null) { @@ -20,11 +29,47 @@ public class LocalizationResourceContributorList : List dictionary) + public void Fill( + string cultureName, + Dictionary dictionary, + bool includeDynamicContributors = true) { foreach (var contributor in this) { + if (!includeDynamicContributors && contributor.IsDynamic) + { + continue; + } + contributor.Fill(cultureName, dictionary); } } + + public async Task FillAsync( + string cultureName, + Dictionary dictionary, + bool includeDynamicContributors = true) + { + foreach (var contributor in this) + { + if (!includeDynamicContributors && contributor.IsDynamic) + { + continue; + } + + await contributor.FillAsync(cultureName, dictionary); + } + } + + internal async Task> GetSupportedCulturesAsync() + { + var cultures = new List(); + + foreach (var contributor in this) + { + cultures.AddRange(await contributor.GetSupportedCulturesAsync()); + } + + return cultures; + } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs index d672a2f17a..4a88eac199 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs @@ -4,9 +4,9 @@ using JetBrains.Annotations; namespace Volo.Abp.Localization; -public class LocalizationResourceDictionary : Dictionary +public class LocalizationResourceDictionary : Dictionary { - private readonly Dictionary _resourcesByNames = new(); + private readonly Dictionary _resourcesByTypes = new(); public LocalizationResource Add([CanBeNull] string defaultCultureName = null) { @@ -15,24 +15,41 @@ public class LocalizationResourceDictionary : Dictionary() + return resource; + } + + public LocalizationResourceBase Get() { var resourceType = typeof(TResource); - var resource = this.GetOrDefault(resourceType); + var resource = _resourcesByTypes.GetOrDefault(resourceType); if (resource == null) { throw new AbpException("Can not find a resource with given type: " + resourceType.AssemblyQualifiedName); @@ -41,9 +58,9 @@ public class LocalizationResourceDictionary : Dictionary( + [NotNull] this TLocalizationResource localizationResource, [NotNull] string virtualPath) + where TLocalizationResource : LocalizationResourceBase { Check.NotNull(localizationResource, nameof(localizationResource)); Check.NotNull(virtualPath, nameof(virtualPath)); @@ -21,16 +22,35 @@ public static class LocalizationResourceExtensions return localizationResource; } - public static LocalizationResource AddBaseTypes( - [NotNull] this LocalizationResource localizationResource, + public static TLocalizationResource AddBaseTypes( + [NotNull] this TLocalizationResource localizationResource, [NotNull] params Type[] types) + where TLocalizationResource : LocalizationResourceBase { Check.NotNull(localizationResource, nameof(localizationResource)); Check.NotNull(types, nameof(types)); foreach (var type in types) { - localizationResource.BaseResourceTypes.AddIfNotContains(type); + localizationResource + .BaseResourceNames + .AddIfNotContains(LocalizationResourceNameAttribute.GetName(type)); + } + + return localizationResource; + } + + public static TLocalizationResource AddBaseResources( + [NotNull] this TLocalizationResource localizationResource, + [NotNull] params string[] baseResourceNames) + where TLocalizationResource : LocalizationResourceBase + { + Check.NotNull(localizationResource, nameof(localizationResource)); + Check.NotNull(baseResourceNames, nameof(baseResourceNames)); + + foreach (var baseResourceName in baseResourceNames) + { + localizationResource.BaseResourceNames.AddIfNotContains(baseResourceName); } return localizationResource; diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceInitializationContext.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceInitializationContext.cs index 845938efb2..7f26466877 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceInitializationContext.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceInitializationContext.cs @@ -4,11 +4,11 @@ namespace Volo.Abp.Localization; public class LocalizationResourceInitializationContext { - public LocalizationResource Resource { get; } + public LocalizationResourceBase Resource { get; } public IServiceProvider ServiceProvider { get; } - public LocalizationResourceInitializationContext(LocalizationResource resource, IServiceProvider serviceProvider) + public LocalizationResourceInitializationContext(LocalizationResourceBase resource, IServiceProvider serviceProvider) { Resource = resource; ServiceProvider = serviceProvider; diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/NonTypedLocalizationResource.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/NonTypedLocalizationResource.cs new file mode 100644 index 0000000000..2a8300d522 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/NonTypedLocalizationResource.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Localization; + +public class NonTypedLocalizationResource : LocalizationResourceBase +{ + public NonTypedLocalizationResource( + [NotNull] string resourceName, + [CanBeNull] string defaultCultureName = null, + [CanBeNull] ILocalizationResourceContributor initialContributor = null + ) : base( + resourceName, + defaultCultureName, + initialContributor) + { + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/hu.json b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/hu.json index d355c69ee3..3e5cf52cca 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/hu.json +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/hu.json @@ -1,7 +1,7 @@ { - "culture": "hu", - "texts": { - "DisplayName:Abp.Localization.DefaultLanguage": "Alapértelmezett nyelv", - "Description:Abp.Localization.DefaultLanguage": "Az alkalmazás alapértelmezett nyelve." - } - } \ No newline at end of file + "culture": "hu", + "texts": { + "DisplayName:Abp.Localization.DefaultLanguage": "Alapértelmezett nyelv", + "Description:Abp.Localization.DefaultLanguage": "Az alkalmazás alapértelmezett nyelve." + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs index 1ac41dba06..15b45efaff 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Localization; @@ -11,6 +12,8 @@ namespace Volo.Abp.Localization.VirtualFiles; public abstract class VirtualFileLocalizationResourceContributorBase : ILocalizationResourceContributor { + public bool IsDynamic => false; + private readonly string _virtualPath; private IVirtualFileProvider _virtualFileProvider; private Dictionary _dictionaries; @@ -37,6 +40,17 @@ public abstract class VirtualFileLocalizationResourceContributorBase : ILocaliza GetDictionaries().GetOrDefault(cultureName)?.Fill(dictionary); } + public Task FillAsync(string cultureName, Dictionary dictionary) + { + Fill(cultureName, dictionary); + return Task.CompletedTask; + } + + public Task> GetSupportedCulturesAsync() + { + return Task.FromResult((IEnumerable)GetDictionaries().Keys); + } + private Dictionary GetDictionaries() { var dictionaries = _dictionaries; diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs index 8b9dca76be..085e486756 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs @@ -8,20 +8,18 @@ public static class ExtensionPropertyConfigurationExtensions public static string GetLocalizationResourceNameOrNull( this ExtensionPropertyConfiguration property) { - var resourceType = property.GetLocalizationResourceTypeOrNull(); - if (resourceType == null) + if (property.DisplayName is LocalizableString localizableString) { - return null; + return localizableString.ResourceName; } - return LocalizationResourceNameAttribute.GetName(resourceType); + return null; } public static Type GetLocalizationResourceTypeOrNull( this ExtensionPropertyConfiguration property) { - if (property.DisplayName != null && - property.DisplayName is LocalizableString localizableString) + if (property.DisplayName is LocalizableString localizableString) { return localizableString.ResourceType; } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/hu.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/hu.json index 7902d093a8..42701a7537 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/hu.json +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/hu.json @@ -1,8 +1,7 @@ { - "culture": "hu", - "texts": { - "DisplayName:Abp.Timing.Timezone": "Időzóna", - "Description:Abp.Timing.Timezone": "Alkalmazás időzónája" - } + "culture": "hu", + "texts": { + "DisplayName:Abp.Timing.Timezone": "Időzóna", + "Description:Abp.Timing.Timezone": "Alkalmazás időzónája" } - \ No newline at end of file +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hu.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hu.json index c391e23db2..ec8b2dd2ec 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hu.json +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hu.json @@ -1,6 +1,6 @@ { - "culture": "hu", - "texts": { - "Menu:Administration": "Adminisztráció" - } - } \ No newline at end of file + "culture": "hu", + "texts": { + "Menu:Administration": "Adminisztráció" + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json index b956b72f96..3db0f6f5c0 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json @@ -1,34 +1,36 @@ { - "culture": "hu", - "texts": { - "'{0}' and '{1}' do not match.": "'{0}' és '{1}' nem egyforma.", - "The {0} field is not a valid credit card number.": "A {0} érvényes bankkártya szám.", - "{0} is not valid.": "{0} nem érvényes.", - "The {0} field is not a valid e-mail address.": "A {0} nem érvényes email cím.", - "The {0} field only accepts files with the following extensions: {1}": "A {0} mező csak olyan fileokat tartalmazhat melynek kiterjesztése ezek: {1}", - "The field {0} must be a string or array type with a maximum length of '{1}'.": "A {0} mező szöveg vagy felsorolás típusú lehet '{1}' maximum hosszal.", - "The field {0} must be a string or array type with a minimum length of '{1}'.": "A {0} mező szöveg vagy felsorolás típusú lehet '{1}' minimum hosszal.", - "The {0} field is not a valid phone number.": "A {0} nem érvényes telefonszám.", - "The field {0} must be between {1} and {2}.": "A {0} értéke {1} és {2} között kell legyen.", - "The field {0} must match the regular expression '{1}'.": "A {0} nem megfelelő a szükséges formátumnak.", - "The {0} field is required.": "A {0} kötelező.", - "The field {0} must be a string with a maximum length of {1}.": "A {0} szövegnek kell lennie maximum {1} hosszan.", - "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "A {0} mezőnek szöveget kell tartalmaznia minimum {2} és maximum {1} hosszan.", - "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "A {0} mező nem érvényes fully-qualified http, https, vagy ftp URL cím.", - "The field {0} is invalid.": "A {0} mező nem érvényes.", - "ThisFieldIsNotAValidCreditCardNumber.": "A mező nem érvényes bankkártya számot tartalmaz.", - "ThisFieldIsNotValid.": "A mező nem érvényes.", - "ThisFieldIsNotAValidEmailAddress.": "A mező nem érvényes email cím.", - "ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "A mező csak fileneveket fogad a következő kiterjesztésekkel: {0}", - "ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "A mezőnek szöveg vagy felsorolás tpusúnak kell lennie maximum '{0}' hosszal.", - "ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "A mezőnek szöveg vagy felsorolás tpusúnak kell lennie minimum '{0}' hosszal.", - "ThisFieldIsNotAValidPhoneNumber.": "A mező nem érvényes telefonszám.", - "ThisFieldMustBeBetween{0}And{1}": "A mező értéke {0} és {1} között kell lennie.", - "ThisFieldMustMatchTheRegularExpression{0}": "A mezőnek meg kell felelnie a következő reguláris kifejezésnek '{0}'.", - "ThisFieldIsRequired.": "A mező szükséges.", - "ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "A mezőnek szöveg tpusúnak kell lennie maximum {0} hosszal.", - "ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "A mezőnek szöveget kell tartalmaznia minimum {1} és maximum {0} hosszan.", - "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "A mező nem érvényes fully-qualified http, https, vagy ftp URL cím.", - "ThisFieldIsInvalid.": "A mező nem érvényes." - } - } \ No newline at end of file + "culture": "hu", + "texts": { + "'{0}' and '{1}' do not match.": "'{0}' és '{1}' nem egyforma.", + "The {0} field is not a valid credit card number.": "A {0} érvényes bankkártya szám.", + "{0} is not valid.": "{0} nem érvényes.", + "The {0} field is not a valid e-mail address.": "A {0} nem érvényes email cím.", + "The {0} field only accepts files with the following extensions: {1}": "A {0} mező csak olyan fileokat tartalmazhat melynek kiterjesztése ezek: {1}", + "The field {0} must be a string or array type with a maximum length of '{1}'.": "A {0} mező szöveg vagy felsorolás típusú lehet '{1}' maximum hosszal.", + "The field {0} must be a string or array type with a minimum length of '{1}'.": "A {0} mező szöveg vagy felsorolás típusú lehet '{1}' minimum hosszal.", + "The {0} field is not a valid phone number.": "A {0} nem érvényes telefonszám.", + "The field {0} must be between {1} and {2}.": "A {0} értéke {1} és {2} között kell legyen.", + "The field {0} must match the regular expression '{1}'.": "A {0} nem megfelelő a szükséges formátumnak.", + "The {0} field is required.": "A {0} kötelező.", + "The field {0} must be a string with a maximum length of {1}.": "A {0} szövegnek kell lennie maximum {1} hosszan.", + "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "A {0} mezőnek szöveget kell tartalmaznia minimum {2} és maximum {1} hosszan.", + "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "A {0} mező nem érvényes fully-qualified http, https, vagy ftp URL cím.", + "The field {0} is invalid.": "A {0} mező nem érvényes.", + "ThisFieldIsNotAValidCreditCardNumber.": "A mező nem érvényes bankkártya számot tartalmaz.", + "ThisFieldIsNotValid.": "A mező nem érvényes.", + "ThisFieldIsNotAValidEmailAddress.": "A mező nem érvényes email cím.", + "ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "A mező csak fileneveket fogad a következő kiterjesztésekkel: {0}", + "ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "A mezőnek szöveg vagy felsorolás tpusúnak kell lennie maximum '{0}' hosszal.", + "ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "A mezőnek szöveg vagy felsorolás tpusúnak kell lennie minimum '{0}' hosszal.", + "ThisFieldIsNotAValidPhoneNumber.": "A mező nem érvényes telefonszám.", + "ThisFieldMustBeBetween{0}And{1}": "A mező értéke {0} és {1} között kell lennie.", + "ThisFieldMustBeGreaterThanOrEqual{0}": "Ennek a mezőnek nagyobbnak vagy egyenlőnek kell lennie, mint {0}.", + "ThisFieldMustBeLessOrEqual{0}": "Ennek a mezőnek kisebbnek vagy egyenlőnek kell lennie, mint {0}.", + "ThisFieldMustMatchTheRegularExpression{0}": "A mezőnek meg kell felelnie a következő reguláris kifejezésnek '{0}'.", + "ThisFieldIsRequired.": "A mező szükséges.", + "ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "A mezőnek szöveg tpusúnak kell lennie maximum {0} hosszal.", + "ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "A mezőnek szöveget kell tartalmaznia minimum {1} és maximum {0} hosszan.", + "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "A mező nem érvényes fully-qualified http, https, vagy ftp URL cím.", + "ThisFieldIsInvalid.": "A mező nem érvényes." + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json index 0a41ce1ed5..3c1a566112 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json @@ -24,6 +24,8 @@ "ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "字段必须是最小长度为'{0}'的字符串或数组.", "ThisFieldIsNotAValidPhoneNumber.": "字段不是有效的手机号码.", "ThisFieldMustBeBetween{0}And{1}": "字段值必须在{0}和{1}范围内.", + "ThisFieldMustBeGreaterThanOrEqual{0}": "该字段必须大于或等于 {0}。", + "ThisFieldMustBeLessOrEqual{0}": "该字段必须小于或等于 {0}。", "ThisFieldMustMatchTheRegularExpression{0}": "字段必须匹配正则表达式'{0}'.", "ThisFieldIsRequired.": "字段不可为空.", "ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "字段必须是长度为{0}的字符串.", @@ -31,4 +33,4 @@ "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.", "ThisFieldIsInvalid.": "该字段无效." } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs index 5d3cb09749..56fc8a583a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs @@ -12,7 +12,7 @@ public class ApplicationConfigurationBuilder_Tests : AspNetCoreMvcTestBase { var applicationConfigurationBuilder = GetRequiredService(); - var config = await applicationConfigurationBuilder.GetAsync(); + var config = await applicationConfigurationBuilder.GetAsync(new ApplicationConfigurationRequestOptions()); config.Auth.ShouldNotBeNull(); config.Localization.ShouldNotBeNull(); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController.cs new file mode 100644 index 0000000000..6e14e04d32 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.AspNetCore.Mvc.Auditing; + +[Route("integration-api/audit-test")] +[IntegrationService] +public class AuditIntegrationServiceTestController : AbpController +{ + [HttpGet] + public IActionResult Get() + { + return Ok(); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController_Tests.cs new file mode 100644 index 0000000000..129035f406 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditIntegrationServiceTestController_Tests.cs @@ -0,0 +1,58 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using NSubstitute; +using Shouldly; +using Volo.Abp.Auditing; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Auditing; + +public class AuditIntegrationServiceTestController_Tests : AspNetCoreMvcTestBase +{ + private readonly AbpAuditingOptions _options; + private IAuditingStore _auditingStore; + + public AuditIntegrationServiceTestController_Tests() + { + _options = ServiceProvider.GetRequiredService>().Value; + _auditingStore = ServiceProvider.GetRequiredService(); + } + + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) + { + _auditingStore = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(_auditingStore)); + base.ConfigureServices(context, services); + } + + [Fact] + public async Task Should_Write_Audit_Log_For_Controllers_With_IntegrationService_Attribute_If_IsEnabledForIntegrationServices() + { + _options.IsEnabledForGetRequests = true; + _options.IsEnabledForIntegrationServices = true; + await GetResponseAsync("/integration-api/audit-test/"); + await _auditingStore + .Received() + .SaveAsync( + Arg.Is( + x => x.Actions.Any( + a => + a.MethodName == nameof(AuditIntegrationServiceTestController.Get) && + a.ServiceName == typeof(AuditIntegrationServiceTestController).FullName + ) + ) + ); + } + + [Fact] + public async Task Should_Not_Write_Audit_Log_For_Controllers_With_IntegrationService_Attribute() + { + _options.IsEnabledForGetRequests = true; + await GetResponseAsync("/integration-api/audit-test/"); + await _auditingStore.DidNotReceive().SaveAsync(Arg.Any()); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs index d36b5a5723..886df9a0ac 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PeopleIntegrationService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PeopleIntegrationService_Tests.cs new file mode 100644 index 0000000000..850eb12862 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PeopleIntegrationService_Tests.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc; + +public class PeopleIntegrationService_Tests : AspNetCoreMvcTestBase +{ + [Fact] + public async Task GetValueAsync() + { + var result = await GetResponseAsStringAsync("/integration-api/app/people/value"); + result.ShouldBe("42"); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index e40fdd8b59..49d92d2776 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -55,6 +55,16 @@ public class Auditing_Tests : AbpAuditingTestBase await _auditingStore.Received().SaveAsync(Arg.Any()); } + + [Fact] + public async Task Should_Not_Write_AuditLog_For_Classes_With_IntegrationService_Attribute() + { + var myAuditedObject1 = GetRequiredService(); + + await myAuditedObject1.DoItAsync(new InputObject { Value1 = "forty-two", Value2 = 42 }); + + await _auditingStore.DidNotReceive().SaveAsync(Arg.Any()); + } public interface IMyAuditedObject : ITransientDependency, IAuditingEnabled { @@ -72,6 +82,20 @@ public class Auditing_Tests : AbpAuditingTestBase }); } } + + /* Integration services should not be audited by default */ + [IntegrationService] + public class MyNotAuditedIntegrationService1 : IMyAuditedObject + { + public virtual Task DoItAsync(InputObject inputObject) + { + return Task.FromResult(new ResultObject + { + Value1 = inputObject.Value1 + "-result", + Value2 = inputObject.Value2 + 1 + }); + } + } public class ResultObject { diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs new file mode 100644 index 0000000000..d3a484c32f --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Xunit; + +namespace Volo.Abp.Authorization; + +public class StaticPermissionDefinitionStore_Tests : AuthorizationTestBase +{ + private readonly IStaticPermissionDefinitionStore _store; + + public StaticPermissionDefinitionStore_Tests() + { + _store = GetRequiredService(); + } + + [Fact] + public async Task GetOrNullAsync() + { + var permission = await _store.GetOrNullAsync("MyPermission1"); + permission.ShouldNotBeNull(); + permission.Name.ShouldBe("MyPermission1"); + permission.StateCheckers.ShouldContain(x => x.GetType() == typeof(TestRequireEditionPermissionSimpleStateChecker)); + + permission = await _store.GetOrNullAsync("NotExists"); + permission.ShouldBeNull(); + } + + [Fact] + public async Task GetPermissionsAsync() + { + var permissions = await _store.GetPermissionsAsync(); + permissions.ShouldContain(x => x.Name == "MyAuthorizedService1"); + permissions.ShouldContain(x => x.Name == "MyPermission1"); + permissions.ShouldContain(x => x.Name == "MyPermission2"); + permissions.ShouldContain(x => x.Name == "MyPermission3"); + permissions.ShouldContain(x => x.Name == "MyPermission4"); + permissions.ShouldContain(x => x.Name == "MyPermission5"); + } + + [Fact] + public async Task GetGroupsAsync() + { + var groups = await _store.GetGroupsAsync(); + groups.ShouldNotContain(x => x.Name == "TestGetGroup"); + } +} diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs index 12c8a16833..513423a781 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs @@ -34,7 +34,19 @@ public class DistributedCache_ConfigureOptions_Test : AbpIntegratedTest { var cache1 = GetRequiredService>(); var cache2 = GetRequiredService>(); - + + cache1.InternalCache.ShouldBe(cache2); + await cache1.SetAsync("john", new PersonCacheItem("John Doe")); var item1 = await cache1.GetAsync("john"); diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Localization/CultureHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Localization/CultureHelper_Tests.cs new file mode 100644 index 0000000000..d55df57800 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Localization/CultureHelper_Tests.cs @@ -0,0 +1,36 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.Localization; + +public class CultureHelper_Tests +{ + [Fact] + public void IsCompatibleCulture() + { + CultureHelper.IsCompatibleCulture("tr", "tr").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("tr", "tr-TR").ShouldBeTrue(); + + CultureHelper.IsCompatibleCulture("en", "tr").ShouldBeFalse(); + CultureHelper.IsCompatibleCulture("en", "tr-TR").ShouldBeFalse(); + + CultureHelper.IsCompatibleCulture("en-US", "en").ShouldBeFalse(); + CultureHelper.IsCompatibleCulture("en-US", "en-GB").ShouldBeFalse(); + + CultureHelper.IsCompatibleCulture("zh", "zh-CN").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-HK").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-MO").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-SG").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-TW").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-Hans").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh", "zh-Hant").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh-Hans", "zh-CN").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh-Hans", "zh-SG").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh-Hant", "zh-HK").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh-Hant", "zh-MO").ShouldBeTrue(); + CultureHelper.IsCompatibleCulture("zh-Hant", "zh-TW").ShouldBeTrue(); + + CultureHelper.IsCompatibleCulture("zh-Hans", "zh-HK").ShouldBeFalse(); + CultureHelper.IsCompatibleCulture("zh-Hant", "zh-SG").ShouldBeFalse(); + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs index 246ffe537d..5e3cb96741 100644 --- a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs @@ -1,4 +1,5 @@ -using Shouldly; +using System.Threading.Tasks; +using Shouldly; using Xunit; namespace Volo.Abp.Features; @@ -13,22 +14,22 @@ public class FeatureDefinitionManager_Tests : FeatureTestBase } [Fact] - public void Should_Get_Defined_Features() + public async Task Should_Get_Defined_Features() { - _featureDefinitionManager.GetOrNull("BooleanTestFeature1").ShouldNotBeNull(); - _featureDefinitionManager.Get("BooleanTestFeature1").Name.ShouldBe("BooleanTestFeature1"); + await _featureDefinitionManager.GetOrNullAsync("BooleanTestFeature1").ShouldNotBeNull(); + (await _featureDefinitionManager.GetAsync("BooleanTestFeature1")).Name.ShouldBe("BooleanTestFeature1"); - _featureDefinitionManager.GetOrNull("IntegerTestFeature1").ShouldNotBeNull(); - _featureDefinitionManager.Get("IntegerTestFeature1").Name.ShouldBe("IntegerTestFeature1"); + await _featureDefinitionManager.GetOrNullAsync("IntegerTestFeature1").ShouldNotBeNull(); + (await _featureDefinitionManager.GetAsync("IntegerTestFeature1")).Name.ShouldBe("IntegerTestFeature1"); } [Fact] - public void Should_Not_Get_Undefined_Features() + public async Task Should_Not_Get_Undefined_Features() { - _featureDefinitionManager.GetOrNull("UndefinedFeature").ShouldBeNull(); - Assert.Throws(() => + (await _featureDefinitionManager.GetOrNullAsync("UndefinedFeature")).ShouldBeNull(); + await Assert.ThrowsAsync(async () => { - _featureDefinitionManager.Get("UndefinedFeature"); + await _featureDefinitionManager.GetAsync("UndefinedFeature"); }); } } diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs index 340d091330..7e01a1c42c 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs @@ -1,5 +1,4 @@ -using Volo.Abp.Localization.TestResources.Base.CountryNames; -using Volo.Abp.Localization.TestResources.Base.Validation; +using Volo.Abp.Localization.TestResources.Base.Validation; using Volo.Abp.Localization.TestResources.Source; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -24,12 +23,13 @@ public class AbpLocalizationTestModule : AbpModule .AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/Validation"); options.Resources - .Add("en") + .Add("LocalizationTestCountryNames") .AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/CountryNames"); options.Resources .Add("en") - .AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); + .AddVirtualJson("/Volo/Abp/Localization/TestResources/Source") + .AddBaseResources("LocalizationTestCountryNames"); options.Resources .Get() diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs index d643f163b9..ee0e929e32 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Localization; using Shouldly; using Volo.Abp.Localization.TestResources.Source; @@ -310,7 +311,7 @@ public class AbpLocalization_Tests : AbpIntegratedTest +{ + private readonly IStringLocalizerFactory _factory; + + public AbpStringLocalizerFactory_Tests() + { + _factory = GetRequiredService(); + } + + [Fact] + public void Factory_Type_Should_Be_AbpStringLocalizerFactory() + { + ProxyHelper.UnProxy(_factory).ShouldBeOfType(); + } + + [Fact] + public void Should_Create_Resource_By_Name() + { + using (CultureHelper.Use("en")) + { + var localizer = _factory.CreateByResourceNameOrNull("Test"); + localizer.ShouldNotBeNull(); + localizer["CarPlural"].Value.ShouldBe("Cars"); + } + } + + [Fact] + public async Task Should_Create_Resource_By_Name_Async() + { + using (CultureHelper.Use("en")) + { + var localizer = await _factory.CreateByResourceNameOrNullAsync("Test"); + localizer.ShouldNotBeNull(); + localizer["CarPlural"].Value.ShouldBe("Cars"); + } + } + + [Fact] + public void Should_Throw_Exception_For_Unknown_Resource_Names() + { + Assert.Throws( + () => _factory.CreateByResourceName("UnknownResourceName") + ); + } + + [Fact] + public async Task Should_Throw_Exception_For_Unknown_Resource_Names_Async() + { + await Assert.ThrowsAsync( + async () => await _factory.CreateByResourceNameAsync("UnknownResourceName") + ); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs index d8fc1717d2..9261d19dbc 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs @@ -59,7 +59,7 @@ public class LocalizableStringSerializer_Tests : AbpIntegratedTest(); - localizableString.ResourceType.ShouldBe(typeof(LocalizationTestResource)); + localizableString.ResourceName.ShouldBe("Test"); localizableString.Name.ShouldBe("Car"); Assert.Throws(() => diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/LocalizationTestCountryNamesResource.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/LocalizationTestCountryNamesResource.cs deleted file mode 100644 index a70436cbc7..0000000000 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/LocalizationTestCountryNamesResource.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Volo.Abp.Localization.TestResources.Base.CountryNames; - -public sealed class LocalizationTestCountryNamesResource -{ - -} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs index bc32e98bcc..95a1bbd32f 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs @@ -1,11 +1,9 @@ -using Volo.Abp.Localization.TestResources.Base.CountryNames; -using Volo.Abp.Localization.TestResources.Base.Validation; +using Volo.Abp.Localization.TestResources.Base.Validation; namespace Volo.Abp.Localization.TestResources.Source; [InheritResource( - typeof(LocalizationTestValidationResource), - typeof(LocalizationTestCountryNamesResource) + typeof(LocalizationTestValidationResource) )] [LocalizationResourceName("Test")] public sealed class LocalizationTestResource diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleIntegrationService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleIntegrationService.cs new file mode 100644 index 0000000000..7e4c76daaa --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleIntegrationService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.TestApp.Application; + +public interface IPeopleIntegrationService : IApplicationService +{ + Task GetValueAsync(); +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleIntegrationService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleIntegrationService.cs new file mode 100644 index 0000000000..4501fdb16f --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleIntegrationService.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.TestApp.Application; + +[IntegrationService] +public class PeopleIntegrationService : ApplicationService, IPeopleIntegrationService +{ + public async Task GetValueAsync() + { + return "42"; + } +} \ No newline at end of file diff --git a/modules/account/Volo.Abp.Account.abpmdl.json b/modules/account/Volo.Abp.Account.abpmdl.json index 6762d81f33..dabb3f2ff9 100644 --- a/modules/account/Volo.Abp.Account.abpmdl.json +++ b/modules/account/Volo.Abp.Account.abpmdl.json @@ -38,8 +38,8 @@ "path": "src/Volo.Abp.Account.Blazor/Volo.Abp.Account.Blazor.abppkg.json", "folder": "src" }, - "Volo.Abp.Account.Installer": { - "path": "src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.abppkg.json", + "Volo.Abp.Account.Web.OpenIddict": { + "path": "src/Volo.Abp.Account.Web.OpenIddict/Volo.Abp.Account.Web.OpenIddict.abppkg.json", "folder": "src" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.abppkg.analyze.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.abppkg.analyze.json index 4edb52319f..8cb409b1fc 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.abppkg.analyze.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.Account.Application.Contracts", - "hash": "eda5b3412f7e1dd8dc07761a04f3a064", + "hash": "63eee204db33a361b137b2cff508e53f", "contents": [ { "namespace": "Volo.Abp.Account", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json index 77fbc0823f..655ac74c10 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json @@ -1,6 +1,7 @@ { "culture": "hu", "texts": { + "Menu:Account": "Fiók", "UserName": "Felhasználónév", "EmailAddress": "Email cím", "UserNameOrEmailAddress": "Felhasználónév és email cím", diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.abppkg.analyze.json b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.abppkg.analyze.json index db23b687a0..6f591b2e46 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.abppkg.analyze.json +++ b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.Account.Application", - "hash": "7fb9c3f35b18f2e9a0577d571fccf9df", + "hash": "9607ce81498fab564ea4a3681c15f72d", "contents": [ { "namespace": "Volo.Abp.Account", @@ -24,26 +24,6 @@ ], "contentType": "applicationService", "name": "ProfileAppService" - }, - { - "defaultValue": "true", - "displayName": "Is self-registration enabled", - "description": "Whether a user can register the account by him or herself.", - "isVisibleToClient": true, - "isInherited": true, - "isEncrypted": false, - "contentType": "setting", - "name": "Abp.Account.IsSelfRegistrationEnabled" - }, - { - "defaultValue": "true", - "displayName": "Authenticate with a local account", - "description": "Indicates if the server will allow users to authenticate with a local account.", - "isVisibleToClient": true, - "isInherited": true, - "isEncrypted": false, - "contentType": "setting", - "name": "Abp.Account.EnableLocalLogin" } ] } \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.HttpApi.Client/Volo.Abp.Account.HttpApi.Client.abppkg.analyze.json b/modules/account/src/Volo.Abp.Account.HttpApi.Client/Volo.Abp.Account.HttpApi.Client.abppkg.analyze.json index 5fec7571fa..57bf3ed42d 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi.Client/Volo.Abp.Account.HttpApi.Client.abppkg.analyze.json +++ b/modules/account/src/Volo.Abp.Account.HttpApi.Client/Volo.Abp.Account.HttpApi.Client.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.Account.HttpApi.Client", - "hash": "ca67872e6cd8c25508461f7d171a8f04", + "hash": "62165afd5965b8041a9ac1f1356ad2e8", "contents": [ { "namespace": "Volo.Abp.Account", diff --git a/modules/account/src/Volo.Abp.Account.HttpApi/Volo.Abp.Account.HttpApi.abppkg.analyze.json b/modules/account/src/Volo.Abp.Account.HttpApi/Volo.Abp.Account.HttpApi.abppkg.analyze.json index 04d48dcab0..597cca045f 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi/Volo.Abp.Account.HttpApi.abppkg.analyze.json +++ b/modules/account/src/Volo.Abp.Account.HttpApi/Volo.Abp.Account.HttpApi.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.Account.HttpApi", - "hash": "b178895fe2a7f470f36c4fd121b7c07a", + "hash": "c789b459a83b26349ca6f0fcab82a244", "contents": [ { "namespace": "Volo.Abp.Account", diff --git a/modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.csproj b/modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.csproj index a35f247250..d9d3f6b21c 100644 --- a/modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.csproj +++ b/modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.csproj @@ -10,13 +10,15 @@ - + + + diff --git a/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AbpAccountInstallerModule.cs b/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AbpAccountInstallerModule.cs index 060b47ef68..01d2fa0e3f 100644 --- a/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AbpAccountInstallerModule.cs +++ b/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AbpAccountInstallerModule.cs @@ -1,11 +1,9 @@ using Volo.Abp.Modularity; -using Volo.Abp.Studio; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Account; [DependsOn( - typeof(AbpStudioModuleInstallerModule), typeof(AbpVirtualFileSystemModule) )] public class AbpAccountInstallerModule : AbpModule diff --git a/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AccountInstallerPipelineBuilder.cs b/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AccountInstallerPipelineBuilder.cs deleted file mode 100644 index 9fb955c9a8..0000000000 --- a/modules/account/src/Volo.Abp.Account.Installer/Volo/Abp/Account/AccountInstallerPipelineBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Studio.ModuleInstalling; - -namespace Volo.Abp.Account; - -[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] -[ExposeServices(typeof(IModuleInstallingPipelineBuilder))] -public class AccountInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency -{ - public async Task BuildAsync(ModuleInstallingContext context) - { - return GetBasePipeline(context); - } -} diff --git a/modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.abppkg.json b/modules/account/src/Volo.Abp.Account.Web.OpenIddict/Volo.Abp.Account.Web.OpenIddict.abppkg.json similarity index 100% rename from modules/account/src/Volo.Abp.Account.Installer/Volo.Abp.Account.Installer.abppkg.json rename to modules/account/src/Volo.Abp.Account.Web.OpenIddict/Volo.Abp.Account.Web.OpenIddict.abppkg.json diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.abppkg.analyze.json b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.abppkg.analyze.json index a5848927a4..02531fd742 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.abppkg.analyze.json +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.Account.Web", - "hash": "5fb2aa05261d4febe7ea7083d80fea74", + "hash": "56ee8c43448399a621216b5e54008a54", "contents": [ { "namespace": "Volo.Abp.Account.Web", diff --git a/modules/audit-logging/Volo.Abp.AuditLogging.abpmdl.json b/modules/audit-logging/Volo.Abp.AuditLogging.abpmdl.json index 9f425bc44e..5b2cc3f54b 100644 --- a/modules/audit-logging/Volo.Abp.AuditLogging.abpmdl.json +++ b/modules/audit-logging/Volo.Abp.AuditLogging.abpmdl.json @@ -37,10 +37,6 @@ "Volo.Abp.AuditLogging.MongoDB.Tests": { "path": "test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.abppkg.json", "folder": "test" - }, - "Volo.Abp.AuditLogging.Installer": { - "path": "src/Volo.Abp.AuditLogging.Installer/Volo.Abp.AuditLogging.Installer.abppkg.json", - "folder": "src" } } } \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo.Abp.AuditLogging.Domain.Shared.csproj b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo.Abp.AuditLogging.Domain.Shared.csproj index ce65a161ce..a28148f0b7 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo.Abp.AuditLogging.Domain.Shared.csproj +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo.Abp.AuditLogging.Domain.Shared.csproj @@ -5,11 +5,22 @@ netstandard2.0 + true + + + + + + + + + + diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AbpAuditLoggingDomainSharedModule.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AbpAuditLoggingDomainSharedModule.cs index 8ab6756ebe..46f1337118 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AbpAuditLoggingDomainSharedModule.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AbpAuditLoggingDomainSharedModule.cs @@ -1,16 +1,27 @@ using Volo.Abp.AuditLogging.Localization; using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.Validation; +using Volo.Abp.Validation.Localization; +using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AuditLogging; +[DependsOn(typeof(AbpValidationModule))] public class AbpAuditLoggingDomainSharedModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => { - options.Resources.Add("en"); + options.Resources.Add("en") + .AddBaseTypes(typeof(AbpValidationResource)) + .AddVirtualJson("/Volo/Abp/AuditLogging/Localization"); }); } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ar.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ar.json new file mode 100644 index 0000000000..e312406b0a --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ar.json @@ -0,0 +1,79 @@ +{ + "culture": "ar", + "texts": { + "Permission:AuditLogging": "تدوين التسجيل", + "Permission:AuditLogs": "سجلات التدقيق", + "Menu:AuditLogging": "سجلات التدقيق", + "AuditLogs": "سجلات التدقيق", + "HttpStatus": "حالة Http", + "HttpMethod": "طريقة Http", + "HttpMethodFilter": "عامل تصفية أسلوب Http", + "HttpRequest": "طلب Http", + "User": "المستعمل", + "UserNameFilter": "مرشح المستخدم", + "HasException": "لديه استثناء", + "IpAddress": "عنوان IP", + "Time": "زمن", + "Duration": "مدة", + "Detail": "التفاصيل", + "Overall": "شاملة", + "Actions": "أجراءات", + "ClientIpAddress": "عنوان IP للعميل", + "ClientName": "اسم العميل", + "BrowserInfo": "معلومات المتصفح", + "Url": "عنوان Url", + "UserName": "اسم االمستخدم", + "UrlFilter": "عامل تصفية عنوان URL", + "Exceptions": "استثناءات", + "Comments": "تعليقات", + "HttpStatusCode": "Http كود الحالة", + "HttpStatusCodeFilter": "Http عامل تصفية رمز الحالة", + "ServiceName": "خدمة", + "MethodName": "طريقة", + "CorrelationId": "معرف الارتباط", + "ApplicationName": "اسم التطبيق", + "ExecutionDuration": "مدة", + "ExtraProperties": "خصائص اضافية", + "MaxDuration": "الأعلى. مدة", + "MinDuration": "دقيقة. مدة", + "{0}Milliseconds": "{0} مللي ثانية", + "ExecutionTime": "زمن", + "Parameters": "المعلمات", + "EntityTypeFullName": "نوع الكيان الاسم الكامل", + "Entity": "شخصية", + "ChangeType": "نوع التغيير", + "ChangeTime": "زمن", + "NewValue": "قيمة جديدة", + "OriginalValue": "القيمة الأصلية", + "PropertyName": "اسم الخاصية", + "PropertyTypeFullName": "نوع الخاصية الاسم الكامل", + "Yes": "نعم", + "No": "لا", + "Changes": "التغييرات", + "AverageExecutionDurationInLogsPerDay": "متوسط مدة التنفيذ", + "AverageExecutionDurationInMilliseconds": "متوسط مدة التنفيذ بالمللي ثانية", + "ErrorRateInLogs": "معدل الخطأ في السجلات", + "Success": "نجاح", + "Fault": "خطأ", + "NoChanges": "لا تغيرات)", + "EntityChanges": "تغييرات الكيان", + "EntityId": "معرف الكيان", + "EntityChangeStartTime": "تاريخ التغيير الأدنى", + "EntityChangeEndTime": "تاريخ التغيير الأقصى", + "EntityHistory": "تاريخ الكيان", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} بواسطة {2}.", + "Created": "خلقت", + "Updated": "محدث", + "Deleted": "تم الحذف", + "ChangeHistory": "تغيير التاريخ", + "FullChangeHistory": "سجل التغيير الكامل", + "ChangeDetails": "تغيير التفاصيل", + "DurationMs": "المدة (مللي ثانية)", + "StartDate": "تاريخ البدء", + "EndDate": "تاريخ الانتهاء", + "Feature:AuditLoggingGroup": "تدوين التسجيل", + "Feature:AuditLoggingEnable": "تمكين صفحة تسجيل التدقيق", + "Feature:AuditLoggingEnableDescription": "تفعيل صفحة تسجيل التدقيق في التطبيق." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de-DE.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de-DE.json new file mode 100644 index 0000000000..b1091b7a63 --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de-DE.json @@ -0,0 +1,79 @@ +{ + "culture": "de-DE", + "texts": { + "Permission:AuditLogging": "Audit-Protokollierung", + "Permission:AuditLogs": "Audit-Protokolle", + "Menu:AuditLogging": "Audit-Protokolle", + "AuditLogs": "Audit-Protokolle", + "HttpStatus": "HTTP-Status", + "HttpMethod": "HTTP-Methode", + "HttpMethodFilter": "HTTP-Methoden-Filter", + "HttpRequest": "HTTP Anfrage", + "User": "Benutzer", + "UserNameFilter": "Benutzer-Filter", + "HasException": "Hat Ausnahme", + "IpAddress": "IP-Adresse", + "Time": "Zeitpunkt", + "Duration": "Dauer", + "Detail": "Details", + "Overall": "Insgesamt", + "Actions": "Aktionen", + "ClientIpAddress": "Client-IP-Adresse", + "ClientName": "Client-Name", + "BrowserInfo": "Browser-Informationen", + "Url": "URL", + "UserName": "Benutzername", + "UrlFilter": "URL-Filter", + "Exceptions": "Ausnahmen", + "Comments": "Kommentare", + "HttpStatusCode": "HTTP-Statuscode", + "HttpStatusCodeFilter": "HTTP-Statuscode-Filter", + "ServiceName": "Service", + "MethodName": "Methode", + "CorrelationId": "Correlation-Id", + "ApplicationName": "Anwendungsname", + "ExecutionDuration": "Dauer", + "ExtraProperties": "Zusätzliche Eigenschaften", + "MaxDuration": "Max. Dauer", + "MinDuration": "Min. Dauer", + "{0}Milliseconds": "{0} Milisekunden", + "ExecutionTime": "Zeitpunkt", + "Parameters": "Parameter", + "EntityTypeFullName": "Voller Name des Entitätstyp", + "Entity": "Entität", + "ChangeType": "Änderungstyp", + "ChangeTime": "Zeitpunkt", + "NewValue": "Neuer Wert", + "OriginalValue": "Alter Wert", + "PropertyName": "Name der Eigenschaft", + "PropertyTypeFullName": "Voller Name der Eigenschaft", + "Yes": "Ja", + "No": "Nein", + "Changes": "Änderungen", + "AverageExecutionDurationInLogsPerDay": "Durchschnittliche Ausführungsdauer", + "AverageExecutionDurationInMilliseconds": "Durchschnittliche Ausführungsdauer in Milisekunden", + "ErrorRateInLogs": "Fehlerrate in Protokollen", + "Success": "Erfolg", + "Fault": "Fehler", + "NoChanges": "Keine Änderung(en)", + "EntityChanges": "Entitäts-Änderungen", + "EntityId": "Entitäts-Id", + "EntityChangeStartTime": "Min. Änderungzeitpunkt", + "EntityChangeEndTime": "Max. Änderungszeitpunkt", + "EntityHistory": "Entitätsverlauf", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} durch {2}.", + "Created": "Erstellt", + "Updated": "Aktualisiert", + "Deleted": "Gelöscht", + "ChangeHistory": "Änderungsverlauf", + "FullChangeHistory": "Voller Änderungsverlauf", + "ChangeDetails": "Änderungsdetails", + "DurationMs": "Dauer (ms)", + "StartDate": "Startdatum", + "EndDate": "Endtermin", + "Feature:AuditLoggingGroup": "Audit-Protokollierung", + "Feature:AuditLoggingEnable": "Aktivieren Sie die Überwachungsprotokollseite", + "Feature:AuditLoggingEnableDescription": "Aktivieren Sie die Überwachungsprotokollseite in der App." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/en.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/en.json new file mode 100644 index 0000000000..5de7585e30 --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/en.json @@ -0,0 +1,81 @@ +{ + "culture": "en", + "texts": { + "Permission:AuditLogging": "Audit Logging", + "Permission:AuditLogs": "Audit Logs", + "Menu:AuditLogging": "Audit Logs", + "AuditLogs": "Audit Logs", + "HttpStatus": "Http Status", + "HttpMethod": "Http Method", + "HttpMethodFilter": "Http Method Filter", + "HttpRequest": "Http Request", + "User": "User", + "UserNameFilter": "User Filter", + "HasException": "Has Exception", + "IpAddress": "Ip Address", + "Time": "Time", + "Duration": "Duration", + "Detail": "Detail", + "Overall": "Overall", + "Actions": "Actions", + "ClientIpAddress": "Client Ip Address", + "ClientName": "Client Name", + "BrowserInfo": "Browser Info", + "Url": "Url", + "UserName": "User Name", + "TenantImpersonator": "Tenant Impersonator", + "UserImpersonator": "User Impersonator", + "UrlFilter": "Url Filter", + "Exceptions": "Exceptions", + "Comments": "Comments", + "HttpStatusCode": "Http Status Code", + "HttpStatusCodeFilter": "Http Status Code Filter", + "ServiceName": "Service", + "MethodName": "Method", + "CorrelationId": "Correlation Id", + "ApplicationName": "Application Name", + "ExecutionDuration": "Duration", + "ExtraProperties": "Extra properties", + "MaxDuration": "Max. Duration", + "MinDuration": "Min. Duration", + "{0}Milliseconds": "{0} milliseconds", + "ExecutionTime": "Time", + "Parameters": "Parameters", + "EntityTypeFullName": "Entity Type Full Name", + "Entity": "Entity", + "ChangeType": "Change Type", + "ChangeTime": "Time", + "NewValue": "New Value", + "OriginalValue": "Original Value", + "PropertyName": "Property Name", + "PropertyTypeFullName": "Property Type Full Name", + "Yes": "Yes", + "No": "No", + "Changes": "Changes", + "AverageExecutionDurationInLogsPerDay": "Average execution duration", + "AverageExecutionDurationInMilliseconds": "Average execution duration in milliseconds", + "ErrorRateInLogs": "Error rate in logs", + "Success": "Success", + "Fault": "Fault", + "NoChanges": "No change(s)", + "EntityChanges": "Entity Changes", + "EntityId": "Entity Id", + "EntityChangeStartTime": "Min Change Date", + "EntityChangeEndTime": "Max Change Date", + "EntityHistory": "Entity History", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} by {2}.", + "Created": "Created", + "Updated": "Updated", + "Deleted": "Deleted", + "ChangeHistory": "Change History", + "FullChangeHistory": "Full Change History", + "ChangeDetails": "Change Details", + "DurationMs": "Duration (ms)", + "StartDate": "Start Date", + "EndDate": "End Date", + "Feature:AuditLoggingGroup": "Audit Logging", + "Feature:AuditLoggingEnable": "Enabled audit logging page", + "Feature:AuditLoggingEnableDescription": "Enable audit logging page in the application." + } +} diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/es.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/es.json new file mode 100644 index 0000000000..e2d3da58b3 --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/es.json @@ -0,0 +1,79 @@ +{ + "culture": "es", + "texts": { + "Permission:AuditLogging": "Registros de auditoría", + "Permission:AuditLogs": "Registros de auditoría", + "Menu:AuditLogging": "Registros de auditoría", + "AuditLogs": "Registros de auditoría", + "HttpStatus": "Estado HTTP", + "HttpMethod": "Metodo HTTP", + "HttpMethodFilter": "Filtro de método HTTP", + "HttpRequest": "Peticion Http", + "User": "Usuario", + "UserNameFilter": "Filtro de usuario", + "HasException": "Tiene error", + "IpAddress": "Dirección Ip", + "Time": "Tiempo", + "Duration": "Duración", + "Detail": "Detalles", + "Overall": "En general", + "Actions": "Acciones", + "ClientIpAddress": "Dirección Ip del Cliente", + "ClientName": "Nombre de cliente", + "BrowserInfo": "Informacion de buscador", + "Url": "Url", + "UserName": "Nombre de usuario", + "UrlFilter": "Filtro de URL", + "Exceptions": "Excepciones", + "Comments": "Comentarios", + "HttpStatusCode": "Código de estado HTTP", + "HttpStatusCodeFilter": "Filtro de código de estado HTTP", + "ServiceName": "Servicio", + "MethodName": "Metodo", + "CorrelationId": "ID de correlación", + "ApplicationName": "Nombre de la aplicación", + "ExecutionDuration": "Duración", + "ExtraProperties": "Propiedades adicionales", + "MaxDuration": "Max. Duración", + "MinDuration": "Min. Duración", + "{0}Milliseconds": "{0} milisegundos", + "ExecutionTime": "Tiempo", + "Parameters": "Parámetros", + "EntityTypeFullName": "Tipo de entidad Nombre completo", + "Entity": "Entidad", + "ChangeType": "Tipo de cambio", + "ChangeTime": "Tiempo", + "NewValue": "Nuevo valor", + "OriginalValue": "Valor original", + "PropertyName": "Nombre de la propiedad", + "PropertyTypeFullName": "Tipo de propiedad Nombre completo", + "Yes": "Si", + "No": "No", + "Changes": "Cambios", + "AverageExecutionDurationInLogsPerDay": "Duración media de ejecución", + "AverageExecutionDurationInMilliseconds": "Duración media de ejecución en milisegundos", + "ErrorRateInLogs": "Tasa de error en los registros", + "Success": "Exitosamente", + "Fault": "Culpa", + "NoChanges": "No cambio(s)", + "EntityChanges": "Cambios en entidad", + "EntityId": "Id Entidad", + "EntityChangeStartTime": "Cambiar fecha mínima", + "EntityChangeEndTime": "Cambiar fecha mámima", + "EntityHistory": "Historial de entidad", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} por {2}.", + "Created": "Creado", + "Updated": "Actualizado", + "Deleted": "Eliminado", + "ChangeHistory": "Historial de cambios", + "FullChangeHistory": "Historial de cambios completo", + "ChangeDetails": "Detalles del cambio", + "DurationMs": "Duración (ms)", + "StartDate": "Fecha de inicio", + "EndDate": "Fecha final", + "Feature:AuditLoggingGroup": "Registros de auditoría", + "Feature:AuditLoggingEnable": "Habilitar la página de registro de auditoría", + "Feature:AuditLoggingEnableDescription": "Habilite la página de registro de auditoría en la aplicación." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fi.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fi.json new file mode 100644 index 0000000000..545ea8b63e --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fi.json @@ -0,0 +1,79 @@ +{ + "culture": "fi", + "texts": { + "Permission:AuditLogging": "Tarkastusten kirjaaminen", + "Permission:AuditLogs": "Tarkastuslokit", + "Menu:AuditLogging": "Tarkastuslokit", + "AuditLogs": "Tarkastuslokit", + "HttpStatus": "Http-tila", + "HttpMethod": "Http-menetelmä", + "HttpMethodFilter": "Http-menetelmän suodatin", + "HttpRequest": "Http-pyyntö", + "User": "Käyttäjä", + "UserNameFilter": "Käyttäjäsuodatin", + "HasException": "Onko poikkeus", + "IpAddress": "IP-osoite", + "Time": "Aika", + "Duration": "Kesto", + "Detail": "Yksityiskohta", + "Overall": "Yleensä ottaen", + "Actions": "Toiminnot", + "ClientIpAddress": "Asiakkaan IP-osoite", + "ClientName": "Asiakkaan Nimi", + "BrowserInfo": "Selaimen tiedot", + "Url": "URL-osoite", + "UserName": "Käyttäjänimi", + "UrlFilter": "URL-suodatin", + "Exceptions": "Poikkeukset", + "Comments": "Kommentit", + "HttpStatusCode": "Http-tilakoodi", + "HttpStatusCodeFilter": "Http-tilakoodisuodatin", + "ServiceName": "Palvelu", + "MethodName": "Menetelmä", + "CorrelationId": "Korrelaatiotunnus", + "ApplicationName": "sovelluksen nimi", + "ExecutionDuration": "Kesto", + "ExtraProperties": "Lisäominaisuudet", + "MaxDuration": "Maks. Kesto", + "MinDuration": "Min. Kesto", + "{0}Milliseconds": "{0} millisekuntia", + "ExecutionTime": "Aika", + "Parameters": "Parametrit", + "EntityTypeFullName": "Yhteisön tyyppi Koko nimi", + "Entity": "Entiteetti", + "ChangeType": "Vaihda tyyppiä", + "ChangeTime": "Aika", + "NewValue": "Uusi arvo", + "OriginalValue": "Alkuperäinen arvo", + "PropertyName": "Kiinteistön nimi", + "PropertyTypeFullName": "Kiinteistötyyppi Koko nimi", + "Yes": "Joo", + "No": "Ei", + "Changes": "Muutokset", + "AverageExecutionDurationInLogsPerDay": "Keskimääräinen suorituksen kesto", + "AverageExecutionDurationInMilliseconds": "Keskimääräinen suorituksen kesto millisekunteina", + "ErrorRateInLogs": "Virheprosentti lokeissa", + "Success": "Menestys", + "Fault": "Vika", + "NoChanges": "Ei muutoksia)", + "EntityChanges": "Entiteetin muutokset", + "EntityId": "Entiteetin tunnus", + "EntityChangeStartTime": "Pienin muutospäivä", + "EntityChangeEndTime": "Korkein muutospäivä", + "EntityHistory": "Entiteettihistoria", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} kirjoittanut {2}.", + "Created": "Luotu", + "Updated": "Päivitetty", + "Deleted": "Poistettu", + "ChangeHistory": "Muutoshistoria", + "FullChangeHistory": "Täysi muutoshistoria", + "ChangeDetails": "Muuta tietoja", + "DurationMs": "Kesto (ms)", + "StartDate": "Aloituspäivämäärä", + "EndDate": "Päättymispäivä", + "Feature:AuditLoggingGroup": "Tarkastusten kirjaaminen", + "Feature:AuditLoggingEnable": "Aktivoi auditointilokisivu", + "Feature:AuditLoggingEnableDescription": "Ota käyttöön tarkastuksen kirjaussivu sovelluksessa." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fr.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fr.json new file mode 100644 index 0000000000..ee3680b87f --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fr.json @@ -0,0 +1,79 @@ +{ + "culture": "fr", + "texts": { + "Permission:AuditLogging": "Journalisation d'audit", + "Permission:AuditLogs": "Journaux d'audit", + "Menu:AuditLogging": "Journaux d'audit", + "AuditLogs": "Journaux d'audit", + "HttpStatus": "État HTTP", + "HttpMethod": "Méthode Http", + "HttpMethodFilter": "Filtre de méthode Http", + "HttpRequest": "Requête HTTP", + "User": "Utilisateur", + "UserNameFilter": "Filtre utilisateur", + "HasException": "A une exception", + "IpAddress": "Adresse IP", + "Time": "Temps", + "Duration": "Durée", + "Detail": "Détail", + "Overall": "Globalement", + "Actions": "Actions", + "ClientIpAddress": "Adresse IP du client", + "ClientName": "Nom du client", + "BrowserInfo": "Informations sur le navigateur", + "Url": "URL", + "UserName": "Nom d'utilisateur", + "UrlFilter": "Filtre d'URL", + "Exceptions": "Exceptions", + "Comments": "commentaires", + "HttpStatusCode": "Code d'état HTTP", + "HttpStatusCodeFilter": "Filtre de code d'état HTTP", + "ServiceName": "Un service", + "MethodName": "Méthode", + "CorrelationId": "ID de corrélation", + "ApplicationName": "Nom de l'application", + "ExecutionDuration": "Durée", + "ExtraProperties": "Propriétés supplémentaires", + "MaxDuration": "Max. Durée", + "MinDuration": "Min. Durée", + "{0}Milliseconds": "{0} millisecondes", + "ExecutionTime": "Temps", + "Parameters": "Paramètres", + "EntityTypeFullName": "Type d'entité Nom complet", + "Entity": "Entité", + "ChangeType": "Changer le type", + "ChangeTime": "Temps", + "NewValue": "Nouvelle valeur", + "OriginalValue": "Valeur d'origine", + "PropertyName": "Nom de la propriété", + "PropertyTypeFullName": "Type de propriété Nom complet", + "Yes": "Oui", + "No": "Non", + "Changes": "Changements", + "AverageExecutionDurationInLogsPerDay": "Durée moyenne d'exécution", + "AverageExecutionDurationInMilliseconds": "Durée moyenne d'exécution en millisecondes", + "ErrorRateInLogs": "Taux d'erreur dans les journaux", + "Success": "Succès", + "Fault": "Faute", + "NoChanges": "Aucun changement)", + "EntityChanges": "Changements d'entité", + "EntityId": "ID d'entité", + "EntityChangeStartTime": "Date de changement min.", + "EntityChangeEndTime": "Date de changement max.", + "EntityHistory": "Historique de l'entité", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} par {2}.", + "Created": "Créé", + "Updated": "Mise à jour", + "Deleted": "Supprimé", + "ChangeHistory": "Historique des modifications", + "FullChangeHistory": "Historique complet des modifications", + "ChangeDetails": "Modifier les détails", + "DurationMs": "Durée (ms)", + "StartDate": "Date de début", + "EndDate": "Date de fin", + "Feature:AuditLoggingGroup": "Journalisation d'audit", + "Feature:AuditLoggingEnable": "Page de journalisation d'audit activée", + "Feature:AuditLoggingEnableDescription": "Activez la page de journalisation d'audit dans l'application." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hi.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hi.json new file mode 100644 index 0000000000..c6d35b093f --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hi.json @@ -0,0 +1,79 @@ +{ + "culture": "hi", + "texts": { + "Permission:AuditLogging": "ऑडिट लॉगिंग", + "Permission:AuditLogs": "ऑडिट लॉग", + "Menu:AuditLogging": "ऑडिट लॉग", + "AuditLogs": "ऑडिट लॉग", + "HttpStatus": "Http स्थिति", + "HttpMethod": "Http विधि", + "HttpMethodFilter": "Http विधि फ़िल्टर", + "HttpRequest": "Http अनुरोध", + "User": "उपयोगकर्ता", + "UserNameFilter": "उपयोगकर्ता फ़िल्टर", + "HasException": "अपवाद है", + "IpAddress": "आईपी पता", + "Time": "समय", + "Duration": "समयांतराल", + "Detail": "विस्तार", + "Overall": "संपूर्ण", + "Actions": "कार्रवाई", + "ClientIpAddress": "ग्राहक इप पता", + "ClientName": "ग्राहक नाम", + "BrowserInfo": "ब्राउज़र जानकारी", + "Url": "यूआरएल", + "UserName": "उपयोगकर्ता नाम", + "UrlFilter": "उर फिल्टर", + "Exceptions": "अपवाद", + "Comments": "टिप्पणियाँ", + "HttpStatusCode": "Http स्थिति कोड", + "HttpStatusCodeFilter": "Http स्थिति कोड फ़िल्टर", + "ServiceName": "सेवा", + "MethodName": "तरीका", + "CorrelationId": "सहसंबंध आईडी", + "ApplicationName": "आवेदन का नाम", + "ExecutionDuration": "समयांतराल", + "ExtraProperties": "अतिरिक्त गुण", + "MaxDuration": "मैक्स। समयांतराल", + "MinDuration": "मिन। समयांतराल", + "{0}Milliseconds": "{0} मिलीसेकंड", + "ExecutionTime": "समय", + "Parameters": "मापदंडों", + "EntityTypeFullName": "संपूर्ण प्रकार पूर्ण नाम", + "Entity": "सत्ता", + "ChangeType": "प्रकार बदलें", + "ChangeTime": "समय", + "NewValue": "नया मूल्य", + "OriginalValue": "असली कीमत", + "PropertyName": "संपत्ति का नाम", + "PropertyTypeFullName": "संपत्ति का पूरा नाम", + "Yes": "हाँ", + "No": "नहीं न", + "Changes": "परिवर्तन", + "AverageExecutionDurationInLogsPerDay": "औसत निष्पादन अवधि", + "AverageExecutionDurationInMilliseconds": "मिलीसेकंड में औसत निष्पादन अवधि", + "ErrorRateInLogs": "लॉग में त्रुटि दर", + "Success": "सफलता", + "Fault": "दोष", + "NoChanges": "कोई बदलाव नहीं)", + "EntityChanges": "इकाई परिवर्तन", + "EntityId": "इकाई आईडी", + "EntityChangeStartTime": "मिन चेंज डेट", + "EntityChangeEndTime": "अधिकतम परिवर्तन तिथि", + "EntityHistory": "इकाई इतिहास", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} से {2}।", + "Created": "बनाया था", + "Updated": "अद्यतन", + "Deleted": "हटाए गए", + "ChangeHistory": "इतिहास बदलें", + "FullChangeHistory": "पूर्ण इतिहास बदलें", + "ChangeDetails": "विवरण बदलें", + "DurationMs": "अवधि (एमएस)", + "StartDate": "आरंभ करने की तिथि", + "EndDate": "समाप्ति तिथि", + "Feature:AuditLoggingGroup": "ऑडिट लॉगिंग", + "Feature:AuditLoggingEnable": "सक्षम ऑडिट लॉगिंग पृष्ठ", + "Feature:AuditLoggingEnableDescription": "एप्लिकेशन में ऑडिट लॉगिंग पृष्ठ सक्षम करें।" + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/it.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/it.json new file mode 100644 index 0000000000..d5a7b70fac --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/it.json @@ -0,0 +1,79 @@ +{ + "culture": "it", + "texts": { + "Permission:AuditLogging": "Registrazione di controllo", + "Permission:AuditLogs": "Registri di controllo", + "Menu:AuditLogging": "Registri di controllo", + "AuditLogs": "Registri di controllo", + "HttpStatus": "Stato HTTP", + "HttpMethod": "Metodo HTTP", + "HttpMethodFilter": "Filtro metodo HTTP", + "HttpRequest": "Richiesta HTTP", + "User": "Utente", + "UserNameFilter": "Filtro utente", + "HasException": "Ha un'eccezione", + "IpAddress": "Indirizzo IP", + "Time": "Orario", + "Duration": "Durata", + "Detail": "Dettaglio", + "Overall": "Complessivo", + "Actions": "Azioni", + "ClientIpAddress": "Indirizzo IP del cliente", + "ClientName": "Nome del cliente", + "BrowserInfo": "Informazioni sul browser", + "Url": "Url", + "UserName": "Nome utente", + "UrlFilter": "Filtro URL", + "Exceptions": "Eccezioni", + "Comments": "Commenti", + "HttpStatusCode": "Codice di stato HTTP", + "HttpStatusCodeFilter": "Filtro codice di stato HTTP", + "ServiceName": "Servizio", + "MethodName": "Metodo", + "CorrelationId": "ID correlazione", + "ApplicationName": "Nome dell'applicazione", + "ExecutionDuration": "Durata", + "ExtraProperties": "Proprietà extra", + "MaxDuration": "Max. Durata", + "MinDuration": "Min. Durata", + "{0}Milliseconds": "{0} millisecondi", + "ExecutionTime": "Orario", + "Parameters": "Parametri", + "EntityTypeFullName": "Nome completo del tipo di entità", + "Entity": "Entità", + "ChangeType": "Cambia tipo", + "ChangeTime": "Orario", + "NewValue": "Nuovo valore", + "OriginalValue": "Valore originale", + "PropertyName": "Nome della proprietà", + "PropertyTypeFullName": "Nome completo del tipo di proprietà", + "Yes": "Sì", + "No": "No", + "Changes": "Modifiche", + "AverageExecutionDurationInLogsPerDay": "Durata media dell'esecuzione", + "AverageExecutionDurationInMilliseconds": "Durata media dell'esecuzione in millisecondi", + "ErrorRateInLogs": "Tasso di errore nei log", + "Success": "Successo", + "Fault": "Fallito", + "NoChanges": "Nessun cambiamento(i)", + "EntityChanges": "Modifiche alle entità", + "EntityId": "Entity Id", + "EntityChangeStartTime": "Data di modifica minima", + "EntityChangeEndTime": "Data di modifica massima", + "EntityHistory": "Storia dell'entità", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} di {2}.", + "Created": "Creato", + "Updated": "Aggiornato", + "Deleted": "Eliminato", + "ChangeHistory": "Cronologia Modifiche", + "FullChangeHistory": "Cronologia completa delle modifiche", + "ChangeDetails": "Modifica dettagli", + "DurationMs": "Durata (ms)", + "StartDate": "Data d'inizio", + "EndDate": "Data di fine", + "Feature:AuditLoggingGroup": "Registrazione di controllo", + "Feature:AuditLoggingEnable": "Pagina di registrazione di controllo abilitata", + "Feature:AuditLoggingEnableDescription": "Abilita la pagina di registrazione dell'audit nell'applicazione." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/pt-BR.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/pt-BR.json new file mode 100644 index 0000000000..d0a66b47dc --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/pt-BR.json @@ -0,0 +1,79 @@ +{ + "culture": "pt-BR", + "texts": { + "Permission:AuditLogging": "Registro de auditoria", + "Permission:AuditLogs": "Logs de auditoria", + "Menu:AuditLogging": "Logs de auditoria", + "AuditLogs": "Logs de auditoria", + "HttpStatus": "Status Http", + "HttpMethod": "Método Http", + "HttpMethodFilter": "Filtro de Método Http", + "HttpRequest": "Pedido Http", + "User": "Usuário", + "UserNameFilter": "Filtro de Usuário", + "HasException": "Tem exceção", + "IpAddress": "Endereço de IP", + "Time": "Tempo", + "Duration": "Duração", + "Detail": "Detalhe", + "Overall": "No geral", + "Actions": "Ações", + "ClientIpAddress": "Endereço IP do cliente", + "ClientName": "Nome do cliente", + "BrowserInfo": "Informação do navegador", + "Url": "Url", + "UserName": "Nome do usuário", + "UrlFilter": "Filtro de Url", + "Exceptions": "Exceções", + "Comments": "Comentários", + "HttpStatusCode": "Código de status Http", + "HttpStatusCodeFilter": "Filtro de código de status Http", + "ServiceName": "Serviço", + "MethodName": "Método", + "CorrelationId": "Id de Correlação", + "ApplicationName": "Nome da Aplicação", + "ExecutionDuration": "Duração", + "ExtraProperties": "Propriedades extras", + "MaxDuration": "Máx. Duração", + "MinDuration": "Min. Duração", + "{0}Milliseconds": "{0} milissegundos", + "ExecutionTime": "Tempo", + "Parameters": "Parâmetros", + "EntityTypeFullName": "Nome completo do tipo da entidade", + "Entity": "Entidade", + "ChangeType": "Tipo de mudança", + "ChangeTime": "Tempo", + "NewValue": "Novo valor", + "OriginalValue": "Valor original", + "PropertyName": "Nome da propriedade", + "PropertyTypeFullName": "Nome completo do tipo da propriedade", + "Yes": "Sim", + "No": "Não", + "Changes": "Mudanças", + "AverageExecutionDurationInLogsPerDay": "Duração média de execução", + "AverageExecutionDurationInMilliseconds": "Duração média de execução em milissegundos", + "ErrorRateInLogs": "Taxa de erros em registros", + "Success": "Sucesso", + "Fault": "Falha", + "NoChanges": "Sem mudança(s)", + "EntityChanges": "Mudanças da Entidade", + "EntityId": "Id da Entidade", + "EntityChangeStartTime": "Data Mínima de Mudança", + "EntityChangeEndTime": "Data Máxima de Mudança", + "EntityHistory": "História da Entidade", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} por {2}.", + "Created": "Criado", + "Updated": "Atualizado", + "Deleted": "Excluído", + "ChangeHistory": "Histórico de Mudanças", + "FullChangeHistory": "Histórico Completo de Mudanças", + "ChangeDetails": "Detalhes de mudança", + "DurationMs": "Duração (ms)", + "StartDate": "Data inicial", + "EndDate": "Data final", + "Feature:AuditLoggingGroup": "Registro de auditoria", + "Feature:AuditLoggingEnable": "Página de registro de auditoria habilitada", + "Feature:AuditLoggingEnableDescription": "Habilite a página de registro de auditoria no aplicativo." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ru.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ru.json new file mode 100644 index 0000000000..4995b5b8be --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ru.json @@ -0,0 +1,79 @@ +{ + "culture": "ru", + "texts": { + "Permission:AuditLogging": "Ведение журнала аудита", + "Permission:AuditLogs": "Журналы аудита", + "Menu:AuditLogging": "Журналы аудита", + "AuditLogs": "Журналы аудита", + "HttpStatus": "HTTP-статус", + "HttpMethod": "HTTP-метод", + "HttpMethodFilter": "Фильтр HTTP-методов", + "HttpRequest": "HTTP-запрос", + "User": "Пользователь", + "UserNameFilter": "Пользовательский фильтр", + "HasException": "Имеет исключение", + "IpAddress": "Айпи адрес", + "Time": "Время", + "Duration": "Продолжительность", + "Detail": "Деталь", + "Overall": "В общем и целом", + "Actions": "Действия", + "ClientIpAddress": "IP-адрес клиента", + "ClientName": "имя клиента", + "BrowserInfo": "Информация о браузере", + "Url": "URL", + "UserName": "Имя пользователя", + "UrlFilter": "URL-фильтр", + "Exceptions": "Исключения", + "Comments": "Комментарии", + "HttpStatusCode": "Код состояния HTTP", + "HttpStatusCodeFilter": "Фильтр кода состояния HTTP", + "ServiceName": "Оказание услуг", + "MethodName": "Метод", + "CorrelationId": "Идентификатор корреляции", + "ApplicationName": "Имя приложения", + "ExecutionDuration": "Продолжительность", + "ExtraProperties": "Дополнительные свойства", + "MaxDuration": "Максимум. Продолжительность", + "MinDuration": "Мин. Продолжительность", + "{0}Milliseconds": "{0} миллисекунд", + "ExecutionTime": "Время", + "Parameters": "Параметры", + "EntityTypeFullName": "Тип объекта Полное имя", + "Entity": "Сущность", + "ChangeType": "Тип изменения", + "ChangeTime": "Время", + "NewValue": "Новое значение", + "OriginalValue": "Исходное значение", + "PropertyName": "Имя свойства", + "PropertyTypeFullName": "Тип недвижимости Полное имя", + "Yes": "да", + "No": "Нет", + "Changes": "Изменения", + "AverageExecutionDurationInLogsPerDay": "Средняя продолжительность выполнения", + "AverageExecutionDurationInMilliseconds": "Средняя продолжительность выполнения в миллисекундах", + "ErrorRateInLogs": "Частота ошибок в логах", + "Success": "Успех", + "Fault": "Вина", + "NoChanges": "Без изменений)", + "EntityChanges": "Изменения сущности", + "EntityId": "Идентификатор объекта", + "EntityChangeStartTime": "Минимальная дата изменения", + "EntityChangeEndTime": "Максимальная дата изменения", + "EntityHistory": "История объекта", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} от {2}.", + "Created": "Созданный", + "Updated": "Обновлено", + "Deleted": "Удалено", + "ChangeHistory": "История изменений", + "FullChangeHistory": "Полная история изменений", + "ChangeDetails": "Изменить детали", + "DurationMs": "Продолжительность (мс)", + "StartDate": "Дата начала", + "EndDate": "Дата окончания", + "Feature:AuditLoggingGroup": "Ведение журнала аудита", + "Feature:AuditLoggingEnable": "Включенная страница ведения журнала аудита", + "Feature:AuditLoggingEnableDescription": "Включить страницу ведения журнала аудита в приложении." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sk.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sk.json new file mode 100644 index 0000000000..5d7a6c03bd --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sk.json @@ -0,0 +1,79 @@ +{ + "culture": "sk", + "texts": { + "Permission:AuditLogging": "Audit logovanie", + "Permission:AuditLogs": "Audit logy", + "Menu:AuditLogging": "Audit logy", + "AuditLogs": "Audit logy", + "HttpStatus": "Http stav", + "HttpMethod": "Http metóda", + "HttpMethodFilter": "Filter metódy Http", + "HttpRequest": "Http požiadavka", + "User": "Používateľ", + "UserNameFilter": "Filter používateľa", + "HasException": "Má výnimku", + "IpAddress": "IP adresa", + "Time": "Čas", + "Duration": "Trvanie", + "Detail": "Detail", + "Overall": "Celkovo", + "Actions": "Akcie", + "ClientIpAddress": "IP adresa klienta", + "ClientName": "Názov klienta", + "BrowserInfo": "Informácie o prehliadači", + "Url": "Url", + "UserName": "Meno používateľa", + "UrlFilter": "Filter URL", + "Exceptions": "Výnimky", + "Comments": "Komentáre", + "HttpStatusCode": "Http stavový kód", + "HttpStatusCodeFilter": "Filter stavového kódu Http", + "ServiceName": "Služba", + "MethodName": "Metóda", + "CorrelationId": "Id korelácie", + "ApplicationName": "Názov aplikácie", + "ExecutionDuration": "Trvanie", + "ExtraProperties": "Ďalšie vlastnosti", + "MaxDuration": "Max. trvanie", + "MinDuration": "Min. trvanie", + "{0}Milliseconds": "{0} milisekúnd", + "ExecutionTime": "Čas", + "Parameters": "Parametre", + "EntityTypeFullName": "Úplný názov typu entity", + "Entity": "Entita", + "ChangeType": "Typ zmeny", + "ChangeTime": "Čas", + "NewValue": "Nová hodnota", + "OriginalValue": "Pôvodná hodnota", + "PropertyName": "Názov vlastnosti", + "PropertyTypeFullName": "Úplný názov typu vlastnosti", + "Yes": "Áno", + "No": "Nie", + "Changes": "Zmeny", + "AverageExecutionDurationInLogsPerDay": "Priemerné trvanie vykonávania", + "AverageExecutionDurationInMilliseconds": "Priemerné trvanie vykonávania v milisekundách", + "ErrorRateInLogs": "Miera chybovosti v logoch", + "Success": "Úspech", + "Fault": "Porucha", + "NoChanges": "Žiadna/e zmena/y", + "EntityChanges": "Zmeny entity", + "EntityId": "Id entity", + "EntityChangeStartTime": "Min. dátum zmeny", + "EntityChangeEndTime": "Max. dátum zmeny", + "EntityHistory": "História entity", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} vykonal {2}.", + "Created": "Vytvorená", + "Updated": "Aktualizovaná", + "Deleted": "Zmazaná", + "ChangeHistory": "História zmien", + "FullChangeHistory": "Úplná história zmien", + "ChangeDetails": "Podrobnosti zmeny", + "DurationMs": "Trvanie (ms)", + "StartDate": "Dátum začiatku", + "EndDate": "Dátum konca", + "Feature:AuditLoggingGroup": "Audit logovanie", + "Feature:AuditLoggingEnable": "Povoliť stránku audit logovania", + "Feature:AuditLoggingEnableDescription": "Povolená stránka audit logovania v aplikácii." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sl.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sl.json new file mode 100644 index 0000000000..d5a782dc9e --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/sl.json @@ -0,0 +1,79 @@ +{ + "culture": "sl", + "texts": { + "Permission:AuditLogging": "Revizijsko zapisovanje", + "Permission:AuditLogs": "Revizijski zapisi", + "Menu:AuditLogging": "Revizijski zapisi", + "AuditLogs": "Revizijski zapisi", + "HttpStatus": "Http status", + "HttpMethod": "Http metoda", + "HttpMethodFilter": "Filter Http metode", + "HttpRequest": "Http zahtevek", + "User": "Uporabnik", + "UserNameFilter": "Filter po uporabniku", + "HasException": "Ima napako", + "IpAddress": "Ip naslov", + "Time": "Čas", + "Duration": "Trajanje", + "Detail": "Podrobnosti", + "Overall": "Splošno", + "Actions": "Dejanja", + "ClientIpAddress": "Ip naslov odjemalca", + "ClientName": "Ime odjemalca", + "BrowserInfo": "Informacije o brskalniku", + "Url": "Url", + "UserName": "Uporabniško ime", + "UrlFilter": "Filter po Url", + "Exceptions": "Napake", + "Comments": "Komentarji", + "HttpStatusCode": "Http koda statusa", + "HttpStatusCodeFilter": "Filter po kodi statusa", + "ServiceName": "Storitev", + "MethodName": "Metoda", + "CorrelationId": "Id korelacije", + "ApplicationName": "Ime aplikacije", + "ExecutionDuration": "Trajanje", + "ExtraProperties": "Dodatne lastnosti", + "MaxDuration": "Najd. trajanje", + "MinDuration": "Najk. trajanje", + "{0}Milliseconds": "{0} milisekund", + "ExecutionTime": "Čas", + "Parameters": "Parametri", + "EntityTypeFullName": "Polno ime vrste entitete", + "Entity": "Entiteta", + "ChangeType": "Vrsta spremember", + "ChangeTime": "Čas", + "NewValue": "Nova vrednost", + "OriginalValue": "Prvotna vrednost", + "PropertyName": "Ime lastnosti", + "PropertyTypeFullName": "Polno ime vrste lastnosti", + "Yes": "Da", + "No": "Ne", + "Changes": "Spremembe", + "AverageExecutionDurationInLogsPerDay": "Povprečno trajanje izvedbe", + "AverageExecutionDurationInMilliseconds": "Povprečno trajanje izvedbe v milisekundah", + "ErrorRateInLogs": "Stopnja napak v zapisih", + "Success": "Uspešno", + "Fault": "Napaka", + "NoChanges": "Ni sprememb", + "EntityChanges": "Spremembe entitet", + "EntityId": "Id. subjekta", + "EntityChangeStartTime": "Najmanjši datum spremembe", + "EntityChangeEndTime": "Največji datum spremembe", + "EntityHistory": "Zgodovina entitete", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} avtorja {2}.", + "Created": "Ustvarjeno", + "Updated": "Posodobljeno", + "Deleted": "Izbrisano", + "ChangeHistory": "Zgodovina sprememb", + "FullChangeHistory": "Popolna zgodovina sprememb", + "ChangeDetails": "Spremeni podrobnosti", + "DurationMs": "Trajanje (ms)", + "StartDate": "Začetni datum", + "EndDate": "Končni datum", + "Feature:AuditLoggingGroup": "Revizijsko beleženje", + "Feature:AuditLoggingEnable": "Omogočena stran za beleženje revizije", + "Feature:AuditLoggingEnableDescription": "Omogočite stran za beleženje revizije v aplikaciji." + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/tr.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/tr.json new file mode 100644 index 0000000000..72b93ad2f4 --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/tr.json @@ -0,0 +1,81 @@ +{ + "culture": "tr", + "texts": { + "Permission:AuditLogging": "İşlem Kayıtları", + "Permission:AuditLogs": "İşlem Kayıtlarına erişim", + "Actions": "Aksiyonlar", + "AuditLogs": "Kullanıcı işlem logları", + "BrowserInfo": "Tarayıcı Bilgisi", + "Changes": "Değişiklikler", + "ChangeTime": "Değişiklik Tarihi", + "ChangeType": "Değişiklik Türü", + "ClientIpAddress": "İstemci Ip Adresi", + "ClientName": "İstemci Adı", + "Comments": "Yorumlar", + "Detail": "Detay", + "Duration": "Süre", + "Entity": "Entity", + "EntityTypeFullName": "Entity Tipi", + "Exceptions": "Hatalar", + "ExecutionDuration": "Geçen Süre", + "HasException": "Hata var mı?", + "MaxDuration": "Max. süre", + "MinDuration": "Min. süre", + "ExecutionTime": "İşlem Tarihi", + "HttpMethod": "Http Metodu", + "HttpMethodFilter": "Http Method Filtresi", + "HttpRequest": "Http İstemi", + "HttpStatus": "Http Durumu", + "HttpStatusCode": "Http Durum Kodu", + "HttpStatusCodeFilter": "Http Durum Kodu Filtresi", + "CorrelationId": "Korelasyon ID", + "ApplicationName": "Uygulama ismi", + "IpAddress": "Ip Adresi", + "Menu:AuditLogging": "Kullanıcı işlem logları", + "MethodName": "Method", + "ExtraProperties": "Ekstra özellikler", + "NewValue": "Yeni Değer", + "{0}Milliseconds": "{0} milisaniye", + "OriginalValue": "İlk Değer", + "Overall": "Genel", + "Parameters": "Parametreler", + "PropertyName": "Property Adı", + "PropertyTypeFullName": "Property Türü", + "ServiceName": "Servis", + "Time": "Tarih", + "Url": "Url", + "UserName": "Kullanıcı adı", + "TenantImpersonator": "Kiracı Taklitçisi", + "UserImpersonator": "Kullanıcı Taklitçisi", + "UrlFilter": "Url Filtresi", + "User": "Kullanıcı", + "Yes": "Evet", + "No": "Hayır", + "UserNameFilter": "Kullanıcı Filtresi", + "AverageExecutionDurationInLogsPerDay": "Ortalama işlem süresi", + "AverageExecutionDurationInMilliseconds": "Milisaniye cinsinden ortalama işlem süresi", + "ErrorRateInLogs": "Loglardaki hata oranı", + "Success": "Başarılı", + "Fault": "Hatalı", + "NoChanges": "Değişiklik yok", + "EntityChanges": "Entity Değişiklikleri", + "EntityId": "Entity Id", + "EntityChangeStartTime": "Min Değişiklik Tarihi", + "EntityChangeEndTime": "Max Değişiklik Tarihi", + "EntityHistory": "Entity Geçmişi", + "DaysAgoTitle": "{1} {0}.", + "DaysAgoWithUserTitle": "{1} {2} tarafından {0}.", + "Created": "Oluşturuldu", + "Updated": "Güncellendi", + "Deleted": "Silindi", + "ChangeHistory": "Değişiklik Geçmişi", + "FullChangeHistory": "Tüm Değişiklik Geçmişi", + "ChangeDetails": "Değişiklik Detayı", + "DurationMs": "Süre (ms)", + "StartDate": "Başlangıç Tarihi", + "EndDate": "Bitiş Tarihi", + "Feature:AuditLoggingGroup": "İşlem Kayıtları", + "Feature:AuditLoggingEnable": "İşlem kayıtları sayfasını etkinleştirin", + "Feature:AuditLoggingEnableDescription": "Uygulamadanızdaki işlem kayıtları sayfasını etkinleştirir." + } +} diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hans.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hans.json new file mode 100644 index 0000000000..a3aed9f1df --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hans.json @@ -0,0 +1,81 @@ +{ + "culture": "zh-Hans", + "texts": { + "Permission:AuditLogging": "审计日志", + "Permission:AuditLogs": "审计日志", + "Menu:AuditLogging": "审计日志", + "AuditLogs": "审计日志", + "HttpStatus": "Http状态", + "HttpMethod": "Http方法", + "HttpMethodFilter": "Http方法过滤", + "HttpRequest": "Http请求", + "User": "用户", + "UserNameFilter": "用户过滤", + "HasException": "存在异常", + "IpAddress": "IP地址", + "Time": "时间", + "Duration": "持续时间", + "Detail": "详情", + "Overall": "总体", + "Actions": "操作", + "ClientIpAddress": "客户端IP地址", + "ClientName": "客户端名称", + "BrowserInfo": "浏览器信息", + "Url": "Url", + "UserName": "用户名", + "TenantImpersonator": "租户模拟", + "UserImpersonator": "用户模拟", + "UrlFilter": "Url过滤", + "Exceptions": "异常", + "Comments": "评论", + "HttpStatusCode": "Http状态码", + "HttpStatusCodeFilter": "Http状态码过滤", + "ServiceName": "服务名称", + "MethodName": "方法名称", + "CorrelationId": "关联Id", + "ApplicationName": "应用名称", + "ExecutionDuration": "持续时间", + "ExtraProperties": "额外属性", + "MaxDuration": "最大持续时间", + "MinDuration": "最小持续时间", + "{0}Milliseconds": "{0} 毫秒", + "ExecutionTime": "时间", + "Parameters": "参数", + "EntityTypeFullName": "实体类型全名", + "Entity": "实体", + "ChangeType": "更改类型", + "ChangeTime": "时间", + "NewValue": "新值", + "OriginalValue": "原始值", + "PropertyName": "属性名称", + "PropertyTypeFullName": "属性类型全名", + "Yes": "Yes", + "No": "No", + "Changes": "变化", + "AverageExecutionDurationInLogsPerDay": "平均处理时间(每天日志)", + "AverageExecutionDurationInMilliseconds": "平均处理时间(毫秒)", + "ErrorRateInLogs": "日志中的错误率", + "Success": "成功", + "Fault": "故障", + "NoChanges": "没有变化", + "EntityChanges": "实体变更", + "EntityId": "实体Id", + "EntityChangeStartTime": "最小更改日期", + "EntityChangeEndTime": "最大更改日期", + "EntityHistory": "实体历史", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} 通过 {2}.", + "Created": "创建", + "Updated": "更新", + "Deleted": "已删除", + "ChangeHistory": "变更记录", + "FullChangeHistory": "完整的变更记录", + "ChangeDetails": "变更详情", + "DurationMs": "持续时间(毫秒)", + "StartDate": "开始时间", + "EndDate": "结束时间", + "Feature:AuditLoggingGroup": "审计日志", + "Feature:AuditLoggingEnable": "启用审计日志页面", + "Feature:AuditLoggingEnableDescription": "在应用程序中启用审计日志页面." + } +} diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hant.json b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hant.json new file mode 100644 index 0000000000..a828fa8e17 --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/zh-Hant.json @@ -0,0 +1,81 @@ +{ + "culture": "zh-Hant", + "texts": { + "Permission:AuditLogging": "審計日誌", + "Permission:AuditLogs": "審計日誌", + "Menu:AuditLogging": "審計日誌", + "AuditLogs": "審計日誌", + "HttpStatus": "Http狀態", + "HttpMethod": "Http方法", + "HttpMethodFilter": "Http方法過濾", + "HttpRequest": "Http請求", + "User": "使用者", + "UserNameFilter": "使用者過濾", + "HasException": "存在異常", + "IpAddress": "IP地址", + "Time": "時間", + "Duration": "持續時間", + "Detail": "詳情", + "Overall": "總體", + "Actions": "操作", + "ClientIpAddress": "客戶端IP地址", + "ClientName": "客戶端名稱", + "BrowserInfo": "瀏覽器資訊", + "Url": "Url", + "UserName": "使用者名稱", + "TenantImpersonator": "租戶模擬", + "UserImpersonator": "用戶模擬", + "UrlFilter": "Url過濾", + "Exceptions": "異常", + "Comments": "評論", + "HttpStatusCode": "Http狀態碼", + "HttpStatusCodeFilter": "Http狀態碼過濾", + "ServiceName": "服務名稱", + "MethodName": "方法名稱", + "CorrelationId": "關聯Id", + "ApplicationName": "應用名稱", + "ExecutionDuration": "持續時間", + "ExtraProperties": "額外屬性", + "MaxDuration": "最大持續時間", + "MinDuration": "最小持續時間", + "{0}Milliseconds": "{0} 毫秒", + "ExecutionTime": "時間", + "Parameters": "參數", + "EntityTypeFullName": "實體型別全名", + "Entity": "實體", + "ChangeType": "更改型別", + "ChangeTime": "時間", + "NewValue": "新值", + "OriginalValue": "原始值", + "PropertyName": "屬性名稱", + "PropertyTypeFullName": "屬性型別全名", + "Yes": "Yes", + "No": "No", + "Changes": "變化", + "AverageExecutionDurationInLogsPerDay": "平均處理時間(每天日誌)", + "AverageExecutionDurationInMilliseconds": "平均處理時間(毫秒)", + "ErrorRateInLogs": "日誌中的錯誤率", + "Success": "成功", + "Fault": "故障", + "NoChanges": "沒有變化", + "EntityChanges": "實體變更", + "EntityId": "實體Id", + "EntityChangeStartTime": "最小更改日期", + "EntityChangeEndTime": "最大更改日期", + "EntityHistory": "實體歷史", + "DaysAgoTitle": "{0} {1}.", + "DaysAgoWithUserTitle": "{0} {1} 通過 {2}.", + "Created": "建立", + "Updated": "更新", + "Deleted": "已刪除", + "ChangeHistory": "變更記錄", + "FullChangeHistory": "完整的變更記錄", + "ChangeDetails": "變更詳情", + "DurationMs": "持續時間(毫秒)", + "StartDate": "開始時間", + "EndDate": "結束時間", + "Feature:AuditLoggingGroup": "審計日誌", + "Feature:AuditLoggingEnable": "啟用審計日誌頁面", + "Feature:AuditLoggingEnableDescription": "在應用程序中啟用審計日誌頁面." + } +} diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo.Abp.AuditLogging.Installer.csproj b/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo.Abp.AuditLogging.Installer.csproj index 8186c1a7fe..deb16126e8 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo.Abp.AuditLogging.Installer.csproj +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo.Abp.AuditLogging.Installer.csproj @@ -10,7 +10,7 @@ - + diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AbpAuditLoggingInstallerModule.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AbpAuditLoggingInstallerModule.cs index 52ed214a90..33601e6c40 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AbpAuditLoggingInstallerModule.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AbpAuditLoggingInstallerModule.cs @@ -1,11 +1,9 @@ using Volo.Abp.Modularity; -using Volo.Abp.Studio; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AuditLogging; [DependsOn( - typeof(AbpStudioModuleInstallerModule), typeof(AbpVirtualFileSystemModule) )] public class AbpAuditLoggingInstallerModule : AbpModule diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AuditLoggingInstallerPipelineBuilder.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AuditLoggingInstallerPipelineBuilder.cs deleted file mode 100644 index 94ae5d81fc..0000000000 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Installer/Volo/Abp/AuditLogging/AuditLoggingInstallerPipelineBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Studio.ModuleInstalling; - -namespace Volo.Abp.AuditLogging; - -[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] -[ExposeServices(typeof(IModuleInstallingPipelineBuilder))] -public class AuditLoggingInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency -{ - public async Task BuildAsync(ModuleInstallingContext context) - { - context.AddEfCoreConfigurationMethodDeclaration( - new EfCoreConfigurationMethodDeclaration( - "Volo.Abp.AuditLogging.EntityFrameworkCore", - "ConfigureAuditLogging" - ) - ); - - return GetBasePipeline(context); - } -} diff --git a/modules/background-jobs/Volo.Abp.BackgroundJobs.abpmdl.json b/modules/background-jobs/Volo.Abp.BackgroundJobs.abpmdl.json index 92042d254e..e3bea65da3 100644 --- a/modules/background-jobs/Volo.Abp.BackgroundJobs.abpmdl.json +++ b/modules/background-jobs/Volo.Abp.BackgroundJobs.abpmdl.json @@ -58,10 +58,6 @@ "Volo.Abp.BackgroundJobs.DemoApp.Quartz": { "path": "app/Volo.Abp.BackgroundJobs.DemoApp.Quartz/Volo.Abp.BackgroundJobs.DemoApp.Quartz.abppkg.json", "folder": "app" - }, - "Volo.Abp.BackgroundJobs.Installer": { - "path": "src/Volo.Abp.BackgroundJobs.Installer/Volo.Abp.BackgroundJobs.Installer.abppkg.json", - "folder": "src" } } } \ No newline at end of file diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo.Abp.BackgroundJobs.Domain.Shared.abppkg.analyze.json b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo.Abp.BackgroundJobs.Domain.Shared.abppkg.analyze.json index 958aef6df3..e142ef6357 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo.Abp.BackgroundJobs.Domain.Shared.abppkg.analyze.json +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo.Abp.BackgroundJobs.Domain.Shared.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.BackgroundJobs.Domain.Shared", - "hash": "d48277ec610b23392edcb12ae3e29175", + "hash": "7be6cb28ee9246d901e9801978a44149", "contents": [ { "namespace": "Volo.Abp.BackgroundJobs", diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json index 954692eeec..d8943cf9a5 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.BackgroundJobs.Domain", - "hash": "1398bbe3f228b2bfd0baec6a64c03b43", + "hash": "edcb98cb920b6d3805b6ff82d280b1bd", "contents": [ { "namespace": "Volo.Abp.BackgroundJobs", @@ -19,7 +19,7 @@ { "namespace": "Volo.Abp.BackgroundJobs", "summary": null, - "entityModel": { + "entityAnalyzeModel": { "namespace": "Volo.Abp.BackgroundJobs", "primaryKeyType": "Guid", "summary": null, diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo.Abp.BackgroundJobs.EntityFrameworkCore.abppkg.analyze.json b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo.Abp.BackgroundJobs.EntityFrameworkCore.abppkg.analyze.json index 66951427ff..48b0997dc9 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo.Abp.BackgroundJobs.EntityFrameworkCore.abppkg.analyze.json +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo.Abp.BackgroundJobs.EntityFrameworkCore.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.BackgroundJobs.EntityFrameworkCore", - "hash": "f3bf7c7db2ab764d6421df0b88ea535b", + "hash": "47c087243281aea34d16f03acf5254cd", "contents": [ { "namespace": "Volo.Abp.BackgroundJobs.EntityFrameworkCore", diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo.Abp.BackgroundJobs.Installer.csproj b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo.Abp.BackgroundJobs.Installer.csproj index e7153fe8bc..da44184bc4 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo.Abp.BackgroundJobs.Installer.csproj +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo.Abp.BackgroundJobs.Installer.csproj @@ -10,13 +10,15 @@ - + + + diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/AbpBackgroundJobsInstallerModule.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/AbpBackgroundJobsInstallerModule.cs index 1e17c8cfd9..299565ea62 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/AbpBackgroundJobsInstallerModule.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/AbpBackgroundJobsInstallerModule.cs @@ -1,11 +1,9 @@ using Volo.Abp.Modularity; -using Volo.Abp.Studio; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.BackgroundJobs; [DependsOn( - typeof(AbpStudioModuleInstallerModule), typeof(AbpVirtualFileSystemModule) )] public class AbpBackgroundJobsInstallerModule : AbpModule diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/BackgroundJobsInstallerPipelineBuilder.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/BackgroundJobsInstallerPipelineBuilder.cs deleted file mode 100644 index ebe2feac61..0000000000 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Installer/Volo/Abp/BackgroundJobs/BackgroundJobsInstallerPipelineBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Studio.ModuleInstalling; - -namespace Volo.Abp.BackgroundJobs; - -[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] -[ExposeServices(typeof(IModuleInstallingPipelineBuilder))] -public class BackgroundJobsInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency -{ - public async Task BuildAsync(ModuleInstallingContext context) - { - context.AddEfCoreConfigurationMethodDeclaration( - new EfCoreConfigurationMethodDeclaration( - "Volo.Abp.BackgroundJobs.EntityFrameworkCore", - "ConfigureBackgroundJobs" - ) - ); - - return GetBasePipeline(context); - } -} diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo.Abp.BackgroundJobs.MongoDB.abppkg.analyze.json b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo.Abp.BackgroundJobs.MongoDB.abppkg.analyze.json index c1b355eb9a..708e849ca4 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo.Abp.BackgroundJobs.MongoDB.abppkg.analyze.json +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo.Abp.BackgroundJobs.MongoDB.abppkg.analyze.json @@ -1,6 +1,6 @@ { "name": "Volo.Abp.BackgroundJobs.MongoDB", - "hash": "def047f007cc2f8c9a74459f1df01330", + "hash": "0a592222ebb3efef215734b1617f8548", "contents": [ { "namespace": "Volo.Abp.BackgroundJobs.MongoDB", diff --git a/modules/basic-theme/Volo.Abp.BasicTheme.abpmdl.json b/modules/basic-theme/Volo.Abp.BasicTheme.abpmdl.json index 4dc0a1ec83..23dcc2e243 100644 --- a/modules/basic-theme/Volo.Abp.BasicTheme.abpmdl.json +++ b/modules/basic-theme/Volo.Abp.BasicTheme.abpmdl.json @@ -33,10 +33,6 @@ "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests": { "path": "test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.abppkg.json", "folder": "test" - }, - "Volo.Abp.BasicTheme.Installer": { - "path": "src/Volo.Abp.BasicTheme.Installer/Volo.Abp.BasicTheme.Installer.abppkg.json", - "folder": "src" } } } \ No newline at end of file diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor index 3565d6add5..4d79218662 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor @@ -1,4 +1,6 @@ @using Volo.Abp.Ui.LayoutHooks +@using Volo.Abp.AspNetCore.Components.Web.Theming.Components.LayoutHooks +@using Volo.Abp.AspNetCore.Components.Web.Theming.Components @using Volo.Abp.AspNetCore.Components.Web.Theming.Layout @inherits LayoutComponentBase