ساخت مبدل تقویم شمسی و تبدیل عدد به حروف فارسی در یک فایل

ساخت ابزار حرفه‌ای تبدیل تاریخ شمسی و میلادی به همراه تبدیل عدد به حروف فارسی برای فاکتور و رسید در یک فایل HTML.

🎧 خلاصه صوتی مقاله (پادکست ۲ دقیقه‌ای):

فرصت مطالعه ندارید؟ چکیده این مطلب را همین‌جا گوش دهید:

طراحی فاکتورها، رسیدهای مالی و سیستم‌های ثبت اسناد فارسی همیشه یک چالش تکراری دارد: نمایش درست تاریخ شمسی و تبدیل اعداد مالی به حروف فارسی برای جلوگیری از هرگونه اشتباه در سندهای رسمی. خیلی از توسعه‌دهندگان برای حل این نیاز ساده، سراغ نصب کتابخانه‌های سنگین، پکیج‌های حجیم Node.js یا افزونه‌های پیچیده می‌روند که حجم پروژه را بالا می‌برند. در این مقاله قصد داریم یاد بگیریم چطور تنها با یک فایل HTML که شامل کدهای CSS و جاوااسکریپت خالص (Vanilla JavaScript) است، یک مبدل تاریخ و اعداد قدرتمند، سبک و زیبا بسازیم که بدون هیچ وابستگی خارجی کار می‌کند.

چرا باید به این ابزار اهمیت بدهید؟ تصور کنید در حال توسعه یک پنل مدیریت فروشگاهی یا یک سیستم صدور فاکتور آنلاین هستید. مشتری یا حسابدار نیاز دارد هم‌زمان با انتخاب تاریخ، مبلغ نهایی فاکتور را به صورت حروف فارسی (مثلاً: «یک میلیون و دویست هزار تومان») در پایین فاکتور ببیند. داشتن این قابلیت به صورت یک ماژول سبک، خودکفا و قابل حمل، سرعت توسعه شما را چند برابر می‌کند و دیگر نیازی به درگیر شدن با وابستگی‌های دردسرساز نخواهید داشت.

ساختار کلی و رابط کاربری گلس‌مورفیسم

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

پیش از نوشتن کدها، اجازه بدهید نگاهی به مشخصات فنی و ویژگی‌های این ابزار بیندازیم تا بدانیم دقیقا چه چیزی را پیاده‌سازی می‌کنیم.

ویژگی مقدار / توضیحات
زبان‌های پیاده‌سازی HTML5, CSS3, Vanilla JavaScript
وابستگی خارجی صفر (بدون نیاز به کتابخانه یا پکیج)
قابلیت‌های کلیدی تبدیل تاریخ میلادی به شمسی، تبدیل عدد به حروف فارسی، طراحی واکنش‌گرا
وضعیت دسترسی ایران کاملاً در دسترس (بدون محدودیت)

کد کامل و یکپارچه ابزار مبدل

کد زیر را می‌توانید به صورت کامل کپی کنید، در یک فایل با پسوند html. (مثلاً converter.html) ذخیره کنید و آن را با هر مرورگری باز کنید. تمام منطق محاسبات تقویم شمسی و الگوریتم تبدیل اعداد به حروف فارسی درون خود اسکریپت جاگذاری شده است.

<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>مبدل حرفه‌ای تاریخ و اعداد فارسی</title>
    <style>
        :root {
            --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            --glass-bg: rgba(255, 255, 255, 0.15);
            --glass-border: rgba(255, 255, 255, 0.3);
            --text-color: #ffffff;
            --accent-color: #ffcc00;
        }
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: Tahoma, system-ui, -apple-system, sans-serif;
        }
        body {
            background: var(--bg-gradient);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
            color: var(--text-color);
        }
        .container {
            background: var(--glass-bg);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 30px;
            width: 100%;
            max-width: 600px;
            box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
        }
        h1 {
            text-align: center;
            margin-bottom: 25px;
            font-size: 1.5rem;
            color: var(--accent-color);
        }
        .card-section {
            margin-bottom: 25px;
            background: rgba(0, 0, 0, 0.1);
            padding: 15px;
            border-radius: 10px;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        .card-section h3 {
            margin-bottom: 10px;
            font-size: 1.1rem;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-bottom: 10px;
        }
        label {
            font-size: 0.9rem;
        }
        input, select {
            padding: 10px;
            border-radius: 8px;
            border: 1px solid rgba(255, 255, 255, 0.4);
            background: rgba(255, 255, 255, 0.2);
            color: #fff;
            font-size: 1rem;
            outline: none;
        }
        input::placeholder {
            color: rgba(255, 255, 255, 0.6);
        }
        input:focus, select:focus {
            border-color: var(--accent-color);
        }
        .result-box {
            background: rgba(0, 0, 0, 0.2);
            padding: 10px;
            border-radius: 6px;
            font-size: 0.95rem;
            color: var(--accent-color);
            word-break: break-word;
        }
    </style>
</head>
<body>

    <div class="container">
        <h1>ابزار محاسبات مالی و تقویم فارسی</h1>

        <!-- بخش تبدیل تاریخ -->
        <div class="card-section">
            <h3>مبدل تاریخ میلادی به شمسی</h3>
            <div class="input-group">
                <label for="gregorianDate">انتخاب تاریخ میلادی:</label>
                <input type="date" id="gregorianDate" onchange="convertDate()">
            </div>
            <div class="result-box" id="persianResult">تاریخ شمسی معادل اینجا نمایش داده می‌شود...</div>
        </div>

        <!-- بخش تبدیل عدد به حروف -->
        <div class="card-section">
            <h3>تبدیل عدد به حروف فارسی (مخصوص فاکتور)</h3>
            <div class="input-group">
                <label for="numberInput">وارد کردن مبلغ (به ریال یا تومان):</label>
                <input type="number" id="numberInput" placeholder="مثلاً 1250000" oninput="convertNumber()">
            </div>
            <div class="result-box" id="wordResult">حروف متناظر عدد اینجا نمایش داده می‌شود...</div>
        </div>
    </div>

    <script>
        // الگوریتم تبدیل تاریخ میلادی به شمسی (Jalali)
        function gregorianToJalali(gy, gm, gd) {
            let g_days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            let j_days_in_month = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
            
            let gyعارض = gy - 1600;
            let gm1 = gm - 1;
            let gd1 = gd - 1;

            let g_day_no = 365 * gyعارض + Math.floor((gyعارض + 3) / 4) - Math.floor((gyعارض + 99) / 100) + Math.floor((gyعارض + 399) / 400);

            for (let i = 0; i < gm1; ++i)
                g_day_no += g_days_in_month[i];
            if (gm1 > 1 && ((gy % 4 === 0 && gy % 100 !== 0) || (gy % 400 === 0)))
                ++g_day_no;
            g_day_no += gd1;

            let j_day_no = g_day_no - 79;
            let j_np = Math.floor(j_day_no / 12053);
            j_day_no %= 12053;
            let jy = 979 + 33 * j_np + 4 * Math.floor(j_day_no / 1461);

            j_day_no %= 1461;
            if (j_day_no >= 365) {
                jy += Math.floor((j_day_no - 1) / 365);
                j_day_no = (j_day_no - 1) % 365;
            }

            let jm, jd;
            for (let i = 0; i < 11 && j_day_no >= j_days_in_month[i]; ++i) {
                j_day_no -= j_days_in_month[i];
                jm = i + 2;
            }
            if (jm === undefined) {
                jm = 1;
            }
            jd = j_day_no + 1;
            return [jy, jm, jd];
        }

        function convertDate() {
            const val = document.getElementById('gregorianDate').value;
            if (!val) return;
            const parts = val.split('-');
            const gy = parseInt(parts[0], 10);
            const gm = parseInt(parts[1], 10);
            const gd = parseInt(parts[2], 10);

            const [jy, jm, jd] = gregorianToJalali(gy, gm, gd);
            const monthsName = ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"];
            
            document.getElementById('persianResult').innerText = `تاریخ شمسی: ${jd} ${monthsName[jm - 1]} ${jy}`;
        }

        // تابع تبدیل عدد به حروف فارسی
        const a = ['', 'یک', 'دو', 'سه', 'چهار', 'پنج', 'شش', 'هفت', 'هشت', 'نه'];
        const b = ['ده', 'یازده', 'دوازده', 'سیزده', 'چهارده', 'پانزده', 'شانزده', 'هفده', 'هجده', 'نوزده'];
        const c = ['', '', 'بیست', 'سی', 'چهل', 'پنجاه', 'شصت', 'هفتاد', 'هشتاد', 'نود'];
        const d = ['', 'یکصد', 'دویست', 'سیصد', 'چهارصد', 'پانصد', 'ششصد', 'هفتصد', 'هشتصد', 'نهصد'];
        const suffixes = ['', 'هزار', 'میلیون', 'میلیارد', 'تریلیون'];

        function convertNumberToWords(num) {
            if (num === 0) return 'صفر';
            if (num < 0) return 'منفی ' + convertNumberToWords(-num);

            let s = num.toString();
            let parts = [];
            while (s.length > 0) {
                parts.unshift(s.slice(Math.max(0, s.length - 3), s.length));
                s = s.slice(0, Math.max(0, s.length - 3));
            }

            let res = [];
            for (let i = 0; i < parts.length; i++) {
                let chunk = parseInt(parts[i], 10);
                if (chunk === 0) continue;
                
                let chunkWords = [];
                let h = Math.floor(chunk / 100);
                let t = Math.floor((chunk % 100) / 10);
                let u = chunk % 10;

                if (h > 0) chunkWords.push(d[h]);
                if (t === 1) {
                    chunkWords.push(b[u]);
                } else {
                    if (t > 1) chunkWords.push(c[t]);
                    if (u > 0) chunkWords.push(a[u]);
                }

                let suffixIdx = parts.length - 1 - i;
                if (suffixes[suffixIdx]) {
                    chunkWords.push(suffixes[suffixIdx]);
                }
                res.push(chunkWords.join(' و '));
            }
            return res.join(' و ');
        }

        function convertNumber() {
            const val = document.getElementById('numberInput').value;
            if (val === '') {
                document.getElementById('wordResult').innerText = 'حروف متناظر عدد اینجا نمایش داده می‌شود...';
                return;
            }
            const num = parseInt(val, 10);
            if (isNaN(num)) {
                document.getElementById('wordResult').innerText = 'لطفاً یک عدد معتبر وارد کنید.';
                return;
            }
            const words = convertNumberToWords(num);
            document.getElementById('wordResult').innerText = words + ' تومان';
        }
    </script>
</body>
</html>

بررسی بخش‌به‌بخش کدهای نوشته‌شده

برای اینکه درک بهتری از نحوه کارکرد این اسکریپت داشته باشید، بخش‌های حیاتی آن را بررسی می‌کنیم:

  • استایل‌های شیشه‌ای (Glassmorphism): در بخش CSS با استفاده از ویژگی backdrop-filter: blur(12px) و پس‌زمینه‌های نیمه‌شفاف سفید، به یک ظاهر مینیمال، مدرن و جذاب دست پیدا کرده‌ایم که روی هر پس‌زمینه‌ای به زیبایی دیده می‌شود.
  • تابع gregorianToJalali: این تابع ریاضیات دقیق تبدیل سال، ماه و روز میلادی به شمسی را بر اساس الگوریتم تقویم جلالی پیاده‌سازی می‌کند. با این تابع نیازی به استفاده از کتابخانه‌های سنگین تاریخ ندارید و خروجی کاملاً دقیق است.
  • آرایه‌های تبدیل عدد به حروف: در بخش جاوااسکریپت، آرایه‌های a تا d به همراه متغیر suffixes مسئولیت خرد کردن اعداد ۳ رقمی، تحلیل دهگان، صدگان و دسته‌ها (هزار، میلیون، میلیارد) را بر عهده دارند و آن‌ها را با حرف ربط «و» به هم متصل می‌کنند.

اگر علاقه‌مندید ابزارهای کاربردی دیگری را برای بهبود جریان کاری خود در برنامه‌نویسی یا تولید محتوا بررسی کنید، پیشنهاد می‌کنیم نگاهی به تبدیل هر صفحه نمایش به تابلو و پیام‌رسان با ابزار Bigwords بیندازید که ترفند جالبی برای نمایش پیام‌ها ارائه می‌دهد. همچنین اگر در پروژه‌های خود نیاز به پردازش فایل‌های متنی یا تبدیل مقالات به صوت دارید، مطالعه‌ی مقاله مربوط به تبدیل متون طولانی و کتاب به صوت با هوش مصنوعی Speechify کمک زیادی به شما خواهد کرد.

اشتباهات رایج و نکات پنهان در پیاده‌سازی

هنگام کار با این نوع اسکریپت‌ها، رعایت چند نکته کوچک تفاوت بین یک کد معمولی و یک ابزار حرفه‌ای را رقم می‌زند:

  • مدیریت اعداد بزرگ: در جاوااسکریپت، اعداد بالاتر از Number.MAX_SAFE_INTEGER ممکن است دقت خود را از دست بدهند. برای فاکتورهای استاندارد تجاری و شرکتی، این اسکریپت تا محدوده‌ی تریلیون‌ها ریال/تومان را به راحتی و بدون خطا پردازش می‌کند.
  • تفاوت ریال و تومان: در سیستم‌های مالی ایران، کاربران معمولاً عدد را به تومان یا ریال وارد می‌کنند. در کدهای بالا، ما به صورت پیش‌فرض کلمه‌ی «تومان» را در انتهای رشته اضافه کرده‌ایم؛ اگر سیستم شما با ریال کار می‌کند، به راحتی می‌توانید برچسب آن را در تابع convertNumber تغییر دهید.
  • ساختار واکنش‌گرا (Responsive): به لطف استفاده از واحدهای درصدی و Flexbox، این ابزار کوچک به خوبی روی نمایشگر موبایل، تبلت و دسکتاپ به نمایش درمی‌آید.

پرسش‌های متداول

آیا این کد در تمام مرورگرها به درستی کار می‌کند؟

بله، این کد از جاوااسکریپت استاندارد مدرن (ES6) و CSS3 استفاده می‌کند و روی تمامی مرورگرهای رایج دسکتاپ و موبایل (کروم، فایرفاکس، سافاری، اج) بدون هیچ خطایی اجرا می‌شود.

چگونه می‌توانم واحد پولی را از تومان به ریال تغییر دهم؟

کافی است در کدهای جاوااسکریپت و درون تابع convertNumber، عبارت + ' تومان' را به + ' ریال' تغییر دهید تا متن خروجی با واحد ریال هماهنگ شود.

آیا این تقویم کبیسه‌ها را به درستی محاسبه می‌کند؟

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

جمع‌بندی

ساخت ابزارهای سبک و خودکفا بدون وابستگی به پکیج‌های سنگین، مهارت ارزشمندی برای هر توسعه‌دهنده وب است. در این مقاله توانستیم تنها با چند خط کد HTML، CSS و جاوااسکریپت خالص، یک مبدل دقیق تاریخ شمسی و یک سیستم کامل تبدیل عدد به حروف فارسی برای فاکتورهای مالی بسازیم. شما می‌توانید این ماژول را به راحتی درون پروژه‌های لاریول، وردپرس، ری‌اکت یا صفحات ساده‌ی استاتیک خود کپی کرده و از سرعت و زیبایی آن لذت ببرید.

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *