سفارشی سازی ASP.NET Core Identity - قسمت ششم - فارسی سازی پیام‌ها
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سه دقیقه

هرچند ASP.NET Core Identity تمام پیام‌های خطایی را که ارائه می‌دهد از یک فایل resx دریافت می‌کند، اما این فایل در نگارش 1.1 آن حداقل قابلیت چندزبانی شدن را ندارد و اگر فایل resx فارسی آن‌را تهیه کنیم، توسط این فریم ورک استفاده نخواهد شد. در ادامه ابتدا نگاهی خواهیم داشت به زیرساخت استفاده شده‌ی در این فریم ورک برای بومی سازی پیام‌های داخلی آن و سپس نحوه‌ی فارسی کردن آن‌را بررسی می‌کنیم.


ASP.NET Core Identity 1.1 چگونه پیام‌های خطای خود را تامین می‌کند؟

نگارش 1.1 این فریم ورک به همراه یک فایل Resources.resx است که تمام پیام‌های خطاهای ارائه شده‌ی توسط متدهای مختلف آن‌را به همراه دارد. این فایل توسط کلاس IdentityErrorDescriber به نحو ذیل استفاده می‌شود:
    public class IdentityErrorDescriber
    {
        public virtual IdentityError DefaultError()
        {
            return new IdentityError
            {
                Code = nameof(DefaultError),
                Description = Resources.DefaultError
            };
        }
برای مثال برای نمایش یک پیام خطای عمومی، به کلاس Resources معادل فایل resx یاد شده مراجعه کرده و خاصیت DefaultError آن‌را ارائه می‌دهد و به همین نحو برای سایر خطاها و اخطارها.
سپس کلاس IdentityErrorDescriber به سیستم تزریق وابستگی‌های آن اضافه شده و هرجائیکه نیاز به نمایش پیامی را داشته، از آن استفاده می‌کند.
بنابراین همانطور که ملاحظه می‌کنید کلاس Resources آن ثابت است و قابل تغییر نیست. به همین جهت اگر معادل فارسی این فایل را تهیه کنیم، توسط این فریم ورک به صورت خودکار استفاده نخواهد شد.


فارسی سازی IdentityErrorDescriber

بهترین راه فارسی سازی کلاس IdentityErrorDescriber، ارث بری از آن و بازنویسی متدهای virtual آن است که اینکار در کلاس CustomIdentityErrorDescriber انجام شده‌است:
    public class CustomIdentityErrorDescriber : IdentityErrorDescriber
    {
        public override IdentityError DefaultError()
        {
            return new IdentityError
            {
                Code = nameof(DefaultError),
                Description = "خطایی رخ داده‌است."
            };
        }
پس از بازنویسی کامل این کلاس، اکنون نوبت به جایگزینی آن با IdentityErrorDescriber پیش‌فرض است:
services.AddScoped<IdentityErrorDescriber, CustomIdentityErrorDescriber>();

services.AddIdentity<User, Role>(identityOptions =>
            {
            }).AddUserStore<ApplicationUserStore>()
               // the rest of the setting …
              .AddErrorDescriber<CustomIdentityErrorDescriber>()
               // the rest of the setting …
معرفی CustomIdentityErrorDescriber، در دو قسمت معرفی عمومی آن به سیستم تزریق وابستگی‌ها و همچنین متد AddErrorDescriber زنجیره‌ی AddIdentity کلاس IdentityServicesRegistry انجام شده‌است.
به این ترتیب این فریم ورک هرزمانیکه نیاز به وهله‌ای از نوع IdentityErrorDescriber را داشته باشد، از وهله‌ی فارسی سازی شده‌ی ما استفاده می‌کند.


مشکل! هنوز پس از جایگزینی سرویس IdentityServicesRegistry اصلی، تعدادی از خطاها فارسی نیستند!

اگر به کلاس PasswordValidator آن مراجعه کنید، در سازنده‌ی کلاس یک چنین تعریفی را می‌توان مشاهده کرد:
    public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
    {
        public PasswordValidator(IdentityErrorDescriber errors = null)
        {
            Describer = errors ?? new IdentityErrorDescriber();
        }
یعنی اگر ما این کلاس PasswordValidator را سفارشی سازی کردیم و فراموش کردیم که سازنده‌ی آن‌را هم بازنویسی کنیم، پارامتر errors آن نال خواهد بود (چون از مقدار پیش‌فرض پارامترها استفاده کرده‌اند). یعنی از new IdentityErrorDescriber اصلی (بدون مراجعه‌ی به سیستم تزریق وابستگی‌ها و استفاده‌ی از نسخه‌ی سفارشی سازی شده‌ی ما) استفاده می‌کند. بنابراین در هر دو کلاس سفارشی سازی شده‌ی اعتبارسنجی کاربران و کلمات عبور آن‌ها، ذکر سازنده‌ی پیش‌فرض این کلاس‌ها و ذکر پارامتر IdentityErrorDescriber آن، اجباری است:
    public class CustomPasswordValidator : PasswordValidator<User>
    {
        private readonly IUsedPasswordsService _usedPasswordsService;
        private readonly ISet<string> _passwordsBanList;

        public CustomPasswordValidator(
            IdentityErrorDescriber errors,// How to use CustomIdentityErrorDescriber
            IOptionsSnapshot<SiteSettings> configurationRoot,
            IUsedPasswordsService usedPasswordsService) : base(errors)



    public class CustomUserValidator : UserValidator<User>
    {
        private readonly ISet<string> _emailsBanList;

        public CustomUserValidator(
            IdentityErrorDescriber errors,// How to use CustomIdentityErrorDescriber
            IOptionsSnapshot<SiteSettings> configurationRoot
            ) : base(errors)
به این ترتیب، زمانیکه این کلاس‌ها توسط سیستم تزریق وابستگی‌ها وهله سازی می‌شوند، IdentityErrorDescriber آن دقیقا همان کلاس فارسی سازی شده‌ی ما خواهد بود و دیگر شرط ()errors ?? new IdentityErrorDescriber در کلاس پایه محقق نمی‌شود تا بازهم به همان تامین کننده‌ی پیش فرض خطاها مراجعه کند؛ چون  base(errors) آن با کلاس جدید ما مقدار دهی شده‌است.

یک نکته: اگر کلاس‌های زیر را سفارشی سازی کردید، تمامشان از حالت ()errors ?? new IdentityErrorDescriber در سازنده‌ی کلاس خود استفاده می‌کنند. بنابراین ذکر مجدد و بازنویسی سازنده‌ی آن‌ها را فراموش نکنید (در حد ذکر مجدد سازنده‌ی کلاس پایه کفایت می‌کند و مابقی آن توسط سیستم تزریق وابستگی‌ها مدیریت خواهد شد):
- PasswordValidator
- RoleManager
- RoleStore
- UserStore
- UserValidator
- RoleValidator


کدهای کامل این سری را در مخزن کد DNT Identity می‌توانید ملاحظه کنید.
  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۳۱ تیر ۱۳۹۸، ساعت ۱۵:۰۴
    سلام و تشکر. اگر در این حالت بخواهیم سایت را به صورت چندزبانه طراحی کنیم چطور باید این خطاها را سفارشی سازی کنیم که برای زبانهای دیگر هم متن خطا به همان زبان نمایش داده شود؟ همینطور اگه بخواهیم متن این خطاها رو از بانک اطلاعاتی لود کنیم (به دلیل اینکه امکان اضافه و ویرایش کردن متن پیام‌ها به هر زبانی رو در پنل ادمین بخواهیم فراهم کنیم) چطور باید عمل کرد؟
    • #
      ‫۵ سال و ۱ ماه قبل، دوشنبه ۳۱ تیر ۱۳۹۸، ساعت ۱۶:۴۶
      - هدف این پروژه، ارائه‌ی یک سایت تمام فارسی برای کاربران فارسی زبان بوده. این هدف هم حاصل شده. هدف دیگری را هم پیگیری نمی‌کند و نخواهد کرد.
      + پروژه‌ی Identity، بومی‌سازی‌های ثالث را نمی‌پذیرد؛ از این جهت که اطمینانی به ترجمه‌های ثالث ندارند و برای یک شرکت بزرگ این مساله می‌تواند گران تمام شود. به همین جهت حالت پیش‌فرض آن، فقط زبان انگلیسی را پشتیبانی می‌کند.
      + مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی » را باید پیگیری کنید. از این لحاظ که زیرساختی برای کار با فایل‌های منبع و انتخاب خودکار آن‌ها بر اساس زبان انتخابی کاربر جاری سیستم، توسط موتور بومی‌سازی توکار آن در ASP.NET Core وجود دارد.
  • #
    ‫۳ سال و ۹ ماه قبل، سه‌شنبه ۱۸ آذر ۱۳۹۹، ساعت ۱۶:۲۱
    با سلام،
    امکانش هست که یک پروژه جدید دیگر  با BlazorServer به این مثال اضافه کنید؟  برای آشنایی با نحوه پیاده سازی این مثال کامل در کامپوننت‌های blazor.
  • #
    ‫۳ سال و ۷ ماه قبل، سه‌شنبه ۳۰ دی ۱۳۹۹، ساعت ۲۱:۵۴
    با سلام و تشکر. آیا مقاله ای در مورد اینکه چطور این پروژه را در یک هاست ، قراردهیم و اصطلاحا بارگذاری نماییم وجود دارد؟ مشکل من این است که بعد از بارگذاری این مثال روی هاست (ایران هاست) این کارها را انجام دادم:
    1. ساخت پایگاه داده در هاست
    2. ساخت کاربر پایگاه داده در هاست
    3. اصلاح کانکشن استرینگ در قسمت مربوطه در فایل appsettings.json
    4. چک کردن اتصال به سرور توسط خود محیط SQL-Server management studio
    5. پابلیش سایت
    6. بارگذاری فایلهای پابلیش شده، روی هاست

    در اینجا انتظار داشتم که سایت از طریق هاست بیاد بالا
    ولی 403.14 میده.

    ممنون میشم اگه کمک بفرمایید.