اعتبارسنجی ایرانی
iran-validationمهارتالگوریتم کد ملی، شبا با mod 97، شماره کارت با Luhn و BIN بانک، موبایل ۰۹ و پلاک خودرو، همه با پذیرش اعداد فارسی و عربی. تا مدل بهجای الگوی آمریکایی، الگوی ایرانی بنویسه.
کی به کار میآد
- فرم ثبتنام و احراز هویت
- فیلد شبا، کارت و کد ملی
- فرم آدرس و کد پستی
- هر ورودی که کاربر با کیبورد فارسی تایپ میکنه
شرط فعال شدن (description)
Validate and format Iranian identifiers correctly: national ID (کد ملی) checksum, mobile numbers (۰۹…), landlines with area codes, IBAN / Sheba (شبا) mod-97, bank card numbers with Luhn and BIN-to-bank lookup, postal code (کد پستی), vehicle plates (پلاک), and Persian/Arabic digit normalization. Use for any signup, KYC, checkout, address or payment form in an Iranian product, or when the user mentions اعتبارسنجی، کد ملی، شبا، شماره کارت، شماره موبایل، کد پستی، پلاک. Replaces US-style patterns (SSN, ZIP, 10-digit phones) that models reach for by default.
مدل با همین چند خط تصمیم میگیره مهارت را بخونه یا نه. اگر میخواید در موقعیتهای دیگری هم فعال بشه، همین را عوض کنید.
Iranian validation (اعتبارسنجی ایرانی)
Models validate Iranian fields with American shapes: 9-digit "SSN", 5-digit ZIP, (555) 123-4567. Every one of these is wrong here. The exact rules are below; when VibeFarsi is in the project, prefer its lib/persian.ts helpers (isNationalId, isIban, ibanBank, cardBank, parsePlate) over re-implementing.
0. Normalize digits first (every field)
Users type on Persian and Arabic keyboards. Map both digit sets to ASCII before any check, and strip spaces, dashes and ZWNJ:
1export function en(s: string) {2 return s3 .replace(/[۰-۹]/g, (d) => String(d.charCodeAt(0) - 0x06f0)) // Persian ۰–۹4 .replace(/[٠-٩]/g, (d) => String(d.charCodeAt(0) - 0x0660)) // Arabic-Indic ٠–٩5 .replace(/[\s_-]/g, "");6}Display the value back with Persian digits; submit the ASCII string.
1. National ID (کد ملی)
Exactly 10 digits. Reject all-same-digit values (
0000000000,1111111111).Checksum: weight the first nine digits 10 down to 2, sum,
r = sum % 11. Ifr < 2the check digit (10th) must equalr; otherwise it must equal11 - r.
1export function isNationalId(input: string) {2 const s = en(input);3 if (!/^\d{10}$/.test(s) || /^(\d)\1{9}$/.test(s)) return false;4 const check = +s[9];5 const sum = [...s.slice(0, 9)].reduce((acc, d, i) => acc + +d * (10 - i), 0);6 const r = sum % 11;7 return r < 2 ? check === r : check === 11 - r;8}Leading zeros are significant; keep the value as a string, never a number.
Label «کد ملی»,
inputMode="numeric",dir="ltr", max length 10.Error copy: «کد ملی باید ۱۰ رقم باشد» / «کد ملی معتبر نیست».
Legal entities use a different 11-digit «شناسه ملی»; do not validate it with the personal algorithm.
2. Mobile numbers (شماره موبایل)
Local form: 11 digits starting with
09(09123456789). International:+989123456789or00989…. Accept all three; store E.164 (+98…) or the local form consistently.Regex after normalization:
^(?:\+98|0098|0)?9\d{9}$.Operator from the prefix is a hint (for an icon or label), never a gate:
091xهمراه اول,093xایرانسل,092xرایتل; other ranges exist and new ones appear. Do not reject unknown prefixes.Field:
dir="ltr",inputMode="tel",autoComplete="tel", label «شماره موبایل», placeholder «۰۹۱۲۳۴۵۶۷۸۹».Display grouped as «۰۹۱۲ ۳۴۵ ۶۷۸۹».
3. Landlines (تلفن ثابت)
11 digits:
0+ 2-digit area code + 8 digits (e.g. Tehran021, Karaj026, Isfahan031, Mashhad051, Shiraz071, Tabriz041).Regex:
^0[1-9]\d{9}$. Do not apply the mobile rule to landlines.Label «تلفن ثابت»; show as «۰۲۱-۱۲۳۴۵۶۷۸».
4. IBAN / Sheba (شماره شبا)
Format:
IR+ 2 check digits + 22 digits = 26 characters. Users often type it withoutIR; accept 24 digits and prependIR.Validate with ISO 13616 mod-97: move the first four characters to the end, replace
I→18,R→27, and the resulting big number mod 97 must be 1. UseBigIntor chunked modulo, notNumber.
1export function isIban(input: string) {2 const s = en(input).toUpperCase().replace(/^IR/, "");3 if (!/^\d{24}$/.test(s)) return false;4 // BBAN + "IR" as 18 27 + the two check digits, then mod 975 const rearranged = `${s.slice(2)}1827${s.slice(0, 2)}`;6 let rem = 0;7 for (const ch of rearranged) rem = (rem * 10 + +ch) % 97;8 return rem === 1;9}Bank code is the 3 digits right after the check digits (
IR12 **017** …). Map it to a bank name from a table for a label («بانک ملی»); when the code is unknown, show nothing rather than a guess.Field:
dir="ltr", monospace, grouped in fours for display «IR۱۲ ۰۱۷۰ ۰۰۰۰ …», label «شماره شبا», helper «با IR یا بدون آن».
5. Bank cards (شماره کارت)
16 digits, Luhn checksum. Grouped «۶۰۳۷ ۹۹۱۱ ۲۳۴۵ ۶۷۸۹» on screen.
BIN (first 6 digits) identifies the bank; use it for a logo or name, not for acceptance. A few stable ones:
603799ملی،589210سپه،627353تجارت،610433ملت،603769صادرات،621986سامان،502229پاسارگاد،622106پارسیان. Keep the full table in one file; do not type BINs from memory in components.Never store full card numbers; show only the last four for saved cards.
Field:
dir="ltr",inputMode="numeric",autoComplete="cc-number", auto-insert spaces, paste accepts Persian digits.
1export function luhn(input: string) {2 const s = en(input);3 if (!/^\d{16}$/.test(s)) return false;4 let sum = 0;5 for (let i = 0; i < 16; i++) {6 let d = +s[i];7 if (i % 2 === 0) { d *= 2; if (d > 9) d -= 9; }8 sum += d;9 }10 return sum % 10 === 0;11}6. Postal code (کد پستی)
10 digits, often written as two groups «۱۲۳۴۵-۶۷۸۹۰». Accept with or without the dash; store 10 ASCII digits.
Validate length and digits only. Do not invent structural rules.
Label «کد پستی»،
inputMode="numeric",dir="ltr".
7. Vehicle plates (پلاک خودرو)
Standard private plate: 2 digits, a Persian letter, 3 digits, then the 2-digit region code shown under «ایران»: «۱۲ ب ۳۴۵ ایران ۱۱».
Letter set is a fixed list (ب، ج، د، س، ص، ط، ع، ق، ل، م، ن، و، هـ، ی and a few special classes). Use a select for the letter, not free text.
Canonical string for storage:
12ب345-11. Display with Persian digits and spaces.Motorcycle and other plate types differ; ask before assuming.
8. Address fields
Order and labels: استان → شهر → خیابان / محله → پلاک → واحد → کد پستی. Province and city come from a list (select or combobox), not free text. «پلاک» here is the building number, not a vehicle plate.
9. Error copy (Persian, specific)
| Field | Message |
|---|---|
| کد ملی | «کد ملی معتبر نیست.» |
| موبایل | «شماره موبایل باید ۱۱ رقم باشد و با ۰۹ شروع شود.» |
| شبا | «شماره شبا ۲۴ رقم بعد از IR دارد.» / «شماره شبا معتبر نیست.» |
| کارت | «شماره کارت باید ۱۶ رقم باشد.» / «شماره کارت معتبر نیست.» |
| کد پستی | «کد پستی باید ۱۰ رقم باشد.» |
Show errors under the field after blur or submit, never on the first keystroke.
10. Checklist
Digits normalized (Persian and Arabic-Indic) before every check.
National ID: 10 digits, not all same, checksum.
Mobile:
09+ 9 digits, operator only as a hint.IBAN:
IR+ 24 digits, mod-97.Card: 16 digits, Luhn, BIN for label only, never stored.
Postal code: 10 digits only.
Every field
dir="ltr"with an RTL label and a Persian error message.
نمونه
پرامپت: «کد ملی را اعتبارسنجی کن»
بدون مهارت
/^\d{9}$/ و یک الگوی شبیه SSN آمریکابا مهارت
۱۰ رقم، همه یکسان نباشن، جمع وزنی ۱۰ تا ۲ به پیمانهی ۱۱ با رقم کنترل
نمونه برای نشان دادن جهت تغییره؛ خروجی واقعی به مدل و پرامپت شما بستگی داره.
نصب
با CLI
این دستور فایل را در
.claude/skills/iran-validation/SKILL.mdمینویسه. CLI به init نیاز نداره؛ فقط باید داخل پوشهی پروژه باشید.$npx vibefarsi add iran-validationدستی
محتوای تب SKILL.md را کپی کنید و در مسیر ابزار خودتون بگذارید:
- Claude Code
.claude/skills/iran-validation/SKILL.mdبرای همین پروژه، یا~/.claude/skills/iran-validation/SKILL.mdبرای همهی پروژهها - Cursor
.cursor/skills/iran-validation/SKILL.md - Codex
.agents/skills/iran-validation/SKILL.md - بقیهی ابزارهاهمان فایل را در پروژه بگذارید و یک خط به
AGENTS.mdاضافه کنید:For any Persian task, read .claude/skills/iran-validation/SKILL.md first.
کپی کل فایلregistry json- Claude Code
میخواید همهی قوانین را یکجا داشته باشید؟ قوانین فارسی برای CLAUDE.md خلاصهی همهی مهارتها در یک صفحهست و قوانین کرافت رابط طرف طراحی را پوشش میده. npx vibefarsi init هر دو را داخل پروژه مینویسه.