حل مشکل حروف فارسی در صفحات ایجاد وب در ASP.NET (مشکل ی و ک فارسی و عربی)

تصویر مهدی

مقدمه:

یکی از مشکلات زبان فارسی در صفحات وب، وجود استانداردهای متفاوت برای کدهای حروف و صفحه‌کلید می‌باشد که خوشبختانه با جا افتادن استاندارد 2901 و در ادامه آن استانداردهای 6219 و 9147 تا حدودی سر و سامان گرفته است. مشکل اینجاست که هنوز هم کاربران زیادی هستند که صفحه کلید آنها به صورت استاندارد نیست و بیشترین مشکل هم در حروف ی و ک وجود دارد که در بسیاری موارد به اشتباه به صورت عربی آن (ي و ك) وارد می‌شود.
راه حل متداول این مشکل این است که تمام رشته‌های ورودی کاربر، قبل از استفاده بررسی شوند و حروف مشکل‌دار آن‌ها تصحیح گردد ولی مشکلی که این روش دارد اینست که هم کار زیادی برای برنامه‌نویس ایجاد می‌شود و هم ممکن است برخی قسمت‌ها از قلم بیافتد و همیشه این شک وجود دارد که آیا رشته‌ها به طور کامل بررسی شده‌اند یا نه؟
راه حل من برای این مشکل اینست که به طور یکپارچه، تمام ورودی ها قبل از اجرای هر کدی توسط یک HttpModule تصحیح گردند. به این صورت در هیچ قسمت برنامه نیازی به تغییر نیست و بدون توجه به آرایش صفحه کلید کاربران سایت، ورودی‌های دریافت شده توسط برنامه، همیشه با فرمت صحیح خواهند بود. در اینجا HttpModule مورد بحث را ارایه داده‌ام.

روش استفاده:

این ماژول توسط Framework نسخه 2 کامپایل شده است و برای اجرا در ASP.NET نسخه 2 و 3.5 و 4 نباید مشکلی وجود داشته باشد. برای استفاده کافیست که dll ضمیمه شده را به پروژه اضافه کرده و در فایل web.config در بخش httpModules آن را معرفی کنید.

<configuration>
    <system.web>
        <httpModules>
            <add name="FixFarsiCharsModule" type="FixFarsiCharsModule.FixFarsiCharsModule" />
        </httpModules>
    </system.web>
</configuration>

پس از اضافه کردن این قسمت، دیگر در برنامه ورودی غیر مجاز دریافت نخواهد شد و این ماژول به طور خودکار ورودی‌های کاربران را قبل از رسیدن به دست برنامه تصحیح خواهد کرد.
این تصحیح در قسمتهای مختلف شامل ورودی‌های فرم‌ها، کوئری استرینگ، کوکی‌ها و ورودی‌های با فرمت JSON انجام می‌شود. همچنین به طور خودکار این اسکریپ ورودی‌های مخصوص ASP.NET مانند ViewState را شناسایی کرده و از تغییر آن‌ها جلوگیری می‌شود.
تنها نکته‌ای که در استفاده از این اسکریپت وجود دارد، اینست که در مرحله‌ای که این اسکریپت کار خود را انجام می‌دهد، هنوز کنترل‌ها مشخص نیستند و به عنوان مثال معلوم نیست که مقدار ورودی، یک ورودی از یک TextBox است یا مقدار یک DropDownList. بنابراین اگر برای کنترل‌هایی مثل DropDownList، مقادیر Value به صورت فارسی وارد شوند، آن‌ها نیز تصحیح می‌گردند. در نتیجه باید دقت کرد که در صورت استفاده از این ماژول یا Value‌ها در کنترل‌هایی مثل این به صورت صحیح وارد شود. البته در حالت کلی توصیه من اینست که تا حد امکان از متن فارسی در Valueها استفاده نکنید و متن فارسی را فقط برای ویژگی Text استفاده کنید و برای Value از مقادیری مثل عددها استفاده کنید.

تنظیمات ماژول:

در این ماژول امکان تنظیم تمام رفتارها در فایل web.config وجود دارد و می‌توانید بسته به نیازهای خاص خود، آن را تنظیم کنید. برای استفاده از تنظیمات، ابتدا بخش مورد نظر این ماژول را در بخش configSections در فایل web.config معرفی کنید. این تعریف باید حتما اولین قسمت از فایل web.config باشد.

<configuration>
    <configSections>
        <section name="fixFarsiChars" type="FixFarsiCharsModule.FixFarsiCharsConfigurationSection" allowLocation="true" allowDefinition="Everywhere" />
    </configSections>
<configuration>

بعد از آن قسمت fixFarsiChars را در فایل web.config اضافه کنید و تنظمات مورد نظر خود را در آن قرار دهید. در اینجا به عنوان نمونه، یک حالت تنظیم ماژول را آورده‌ام. بجز فیلد استثنا (someControlID) بقیه قسمت‌ها تنظیمات پیش‌فرض ماژول هستند و در صورت نیاز به استفاده در همین حالت، اصلا نیازی به وجود این بخش نخواهد بود.

<fixFarsiChars applyToForm="true" applyToCookie="true" applyToJson="true" applyToQueryString="true">
    <replacements>
        <add original="ي" replacement="ی"/>
        <add original="ك" replacement="ک"/>
    </replacements>
    <exceptions>
        <add prefix="__" />
        <add id="someControlID" />
    </exceptions>
</fixFarsiChars>

تنظیمات ماژول تقریبا مشخص است ولی باز هم به طور خلاصه آن‌ها را توضیح می‌دهم. در قسمت اول قابلیت تنظیم این مساله وجود دارد که این ماژول عمل تصحیح را در کدام قسمت‌های ورودی انجام دهد. در صورت نیاز، می‌توانید هر قسمت که مورد نیاز نبود غیرفعال کنید.
در بخش replacements عمیات تصحیحی که مورد نیاز است معرفی می‌شود. در این قسمت می‌توانید تنظیمات خاص خود را وارد کنید. به عنوان مثال ممکن است در پروژه‌ای نیاز باشد که برعکس کار ماژول، حروف به صورت عربی باشند و یا به عنوان مثال اعداد نیز فارسی شوند که به راحتی این موراد در این قسمت قابل انجام است. در هر مورد original به معنی کارکتری که باید تصحیح شود و replacement به معنی کارکتر جایگزین آن است.
در بخش exceptions نیز می‌توانید کنترل‌ها و فیلدهایی که تصحیح بر روی آن‌ها نباید انجام شود را معرفی کنید. به طور پیش فرض تمام کنترل‌هایی که با دو حرف خط زیر (__) شروع می‌شوند استثنا شده‌اند که این امر به منظور جلوگیری از تداخل کار ماژول با مقادیر مورد استفاده در خود ASP.NET (مانند VIEWSTATE__ و EVENTTARGET__ و ...) است. در صورت لزوم می‌توانید کنترل‌های خاص خود را در این قسمت برای وارد کنید. از حالت prefix برای استثنا کردن کنترل‌ها به صورت گروهی با وارد کردن بخش ابتدایی نام آن‌ها و از id برای استثنا کردن کنترل با id خاص استفاده می‌شود.

منابع:

کد قمست پارسر JSON بر اساس کد بسیار ساده و زیبای این مقاله نوشته شده است:

ضمائم:

در این قسمت فایل dll ماژول طراحی شده و همچنین سورس آن را برای افرادی که تمایل به دیدن شیوه کار ماژول و یا احیانا رفع باگ‌های احتمالی دارند ضمیمه شد‌ه است. فراموش نکنید که اگر در سورس برنامه مشکلی دیدید و یا قسمتی از آن را بهبود دادید، من را در جریان قرار دهید.

ضمیمهاندازه
نسخه 1.0.1.0 ماژول به صورت dll6.73 کیلو بایت
سورس کد نسخه 1.0.1.0 ماژول7.26 کیلو بایت

نظرات

استفاده از کدهای یونی‌کد

سلام،
روش شما جامع و بهتر هست. اما شاید لازم نباشد همیشه این چک کردن اتفاق بیفتد.
من یک Custom textbox دارم و در پروژه‌ها به جای asp.net textbox از آن استفاده می‌کنم، Text آن به این صورت override شده است:

public override string Text
{
get
{
EnsureChildControls();

const char PersianYeh = '\u06cc';
return txtValidTextBox.Text.Replace('\u064a', PersianYeh) //Arabic Letter Yeh
.Replace('\u0649', PersianYeh) //Arabic Letter Alef Maksura
.Replace('\ufbfc', PersianYeh) //Arabic Letter Yeh Isolated Form
.Replace('\ufeef', PersianYeh) //Arabic Letter Alef Maksura Isolated Form
.Replace('\ufef1', PersianYeh) //Arabic Letter Yeh Isolated Form
.Replace('\ufb8e', PersianYeh) //Arabic Letter Keheh Isolated Form
.Replace('\u0643', '\u06a9') //Arabic Letter Kaf, Farsi Keheh

//numbers
.Replace('\u0660', '\u06f0') //0
.Replace('\u0661', '\u06f1') //1
.Replace('\u0662', '\u06f2') //2
.Replace('\u0663', '\u06f3') //3
.Replace('\u0664', '\u06f4') //4
.Replace('\u0665', '\u06f5') //5
.Replace('\u0666', '\u06f6') //6
.Replace('\u0667', '\u06f7') //7
.Replace('\u0668', '\u06f8') //8
.Replace('\u0669', '\u06f9'); //9

}
set
{
txtValidTextBox.Text = value;
}
}

تصویر مهدی

استفاده از کدهای یونی‌کد

سلام،
ممنون از نظرتون.

در صورتی که فقط نیاز به تصحیح مقادیر کنترلها باشه (مثلا ورودی Json برای وب-سرویسها و QueryString و... رو نخواید چک کنید)، و همچنین ورودیها همه از کلاس TextBox استاندارد استفاده کرده باشند، روش پیشنهادی شما احتمالا مفیدتر خواهد بود.
فقط در تکمیل پیشنهاد خوب شما، اینم بگم که برای استفاده از این کنترل هم میتونید با استفاده از tagMapping بدون تغییر در کدهای موجود (با استفاده از همون asp:TextBox) از این کنترل جدید استفاده کرد.

بسیار عالی

ترفند بسیار بسیار جالبی بود.

فقط این مورد در هنگام Upload فایل مشکلی ایجاد نخواهد کرد؟ تدبیری اندیشیدید یا اصلاً نیازی به این کار نیست؟ چون تا جایی که متوجه شدم، Json، Cookie، Form و QueryString را چک می‌کنید. اگر اشتباه نکنم فایلهای جزو آیتم‌های Form ارسال می‌شوند.

موفق باشید.

تصویر مهدی

Upload فایل

سلام،
ممنون از نظرتون.

برای آپلود فایل مشکلی پیش نمیاد. فایلها در Request.Files قرار دارند (و نه Request.Form). بنابراین اصلا به محتویات فایل کاری نداریم.

Override OnClick

با سلام

داداش ، اگر ممکن دستور Override کردن OnClick ، Button رو برام در سایت قرار بده ، من می خوام در Base Class ،چک کنم

کند شدن سایت؟!

با سلام و عرض تبریک به خاطر برگشتن و گذاشتن مطالب مفید و زیبا برخلاف سایت های Ctrl + C و Ctrl + V فارسی زبان.
من قبل از دیدن مقاله خوب شما در زمان ورود داده از کاربر تبدیل مورد نظر را انجام می دادم تا داده ای متنی فارسی در هنگام جستجو به مشکلی برنخورند.
با توجه به مقاله شما بدلیل اینکه از httpmodule استفاده می کنید و اینکار per request انجام می شود به نظر شما آیا باعث کندی نمی شود؟

با سلام. از توضیحات دقیق و

با سلام.
از توضیحات دقیق و جامع و سورس کدهای روان و سلیس شما همیشه استفاده میکنم و چیز یاد میگیرم. و همیشه از رسم امانتداری شما در کدهاتون لذت مبرم.
و خوشحالم دوباره بروز میکنید.
یه سوال: اگر از قبل کارکترهایی مثل همین "ک"عربی در دیتابیسمون (به مقدار زیاد!) موجود باشه پیشنهاد شما به عنوان بهترین روش برای replaceکردن آنها چیه؟

تصویر مهدی

تصحیح دیتابیس موجود

سلام،
خیلی ممنون از لطفتون. خوشحالم که مطالبم به دردتون خورده.
در مورد دیتابیس‌های موجود، آقای نصیری یک مطلب خوب خیلی وقت پیش گذاشته بودند که دقیقا پاسخ سوال شما هست: http://www.dotnettips.info/2009/01/blog-post_13.html

تشكر

ممنون

httpcookiecollection

httpcookiecollection به اشتباه parse شده : حلقه دوم نيازي نيست
http://msdn.microsoft.com/en-us/library/system.web.httpcookiecollection....

تصویر مهدی

httpcookiecollection

ممنون از نظرتون.
اگر حلقه دوم رو نذارم و مستقیما روی Value تغییرات رو انجام بدم، در حالتی که در یک کوکی چند مقدار ذخیره شده باشه، تصحیح روی کلیدها و مقادیر هردو با هم انجام میشه و من میخواستم فقط مقادیر تصحیح بشن. حلقه دوم فقط برای همین حالت (چند مقدار در یک کوکی) در نظر گرفته شده. باز هم اگر شما فکر دیگه‌ای در این مورد دارین، خوشحال میشم نظرتون رو بدونم.

يك breakpoint بگذاريد داخل

يك breakpoint بگذاريد داخل حلقه دوم. مقادير كليدها تماما null است.

تصویر مهدی

httpcookiecollection

در صورتی که در کوکی فقط یک مقدار باشه، همینطوره که شما میگید ولی یک کوکی به این صورت تعریف کنید تا منظورم رو متوجه بشید:

            HttpCookie cookie = new HttpCookie("test");
            cookie.Values.Add("test1", "value1");
            cookie.Values.Add("test2", "value2");
            Response.Cookies.Add(cookie);

نيازي به حلقه دوم نيست چون در

نيازي به حلقه دوم نيست چون در اين حالت (با استفاده از يك حلقه):
cookie.Value => key1=value1&key2=value2
اين Value حاوي مقادير تمام كوكي‌ها هست.

تصویر مهدی

httpcookiecollection

یه بار دیگه صحبت بالایی من رو مطالعه کنید:
اگر حلقه دوم رو نذارم ... تصحیح روی کلیدها و مقادیر هردو با هم انجام میشه و من میخواستم فقط مقادیر تصحیح بشن.

افشار محبی

سلام. راه جالبی است. البته استاندارد ۲۹۰۱ توسط استاندارد ۶۲۱۹ جایگزین شده است.

تصویر مهدی

استاندارد 6219

سلام،
خیلی ممنون از نظرتون. متن رو ویرایش کردم.

بسیار عالی

سلام مهدی جان

مطلب مفیدی بود. تو فکرم بشه یه پکیج NuGet براش بنویسم. یا اگر خودت نوشتی تو همین پست بزار. ممنون.

تصویر مهدی

پکیج NuGet

سلام،
خیلی ممنون از نظر و پیشنهادتون. پیشنهاد خوبی بود. اگر مشکلی پیش نیاد سعی می‌کنم تا چند روز آینده همین کار رو انجام بدم.
باز هم ممنون.