دو رشتهٔ «یکسان» می‌توانند در پایگاه داده برابر نباشند: «ی» فارسی با U+06CC و «ی» عربی با U+064A فقط شبیه هم دیده می‌شوند. یک زمان ثابت UTC می‌تواند در تهران روز دیگری باشد؛ مبلغ «۱۰۰٬۰۰۰ تومان» نیز با «۱۰۰٬۰۰۰ ریال» ده برابر تفاوت دارد. اگر تست فقط تغییر زبان منو و چند Screenshot را ببیند، این خطاهای واقعی از آن عبور می‌کنند.

تست i18n و L10n باید یک قرارداد کامل را از ورودی تا ذخیره، پردازش، نمایش و خروجی‌های جانبی بررسی کند. در این راهنما، ابتدا تفاوت جهانی‌سازی و بومی‌سازی را روشن می‌کنیم، سپس قرارداد Locale و ماتریس ریسک می‌سازیم و آن را برای فارسی ایران، RTL، Unicode، تاریخ شمسی، منطقهٔ زمانی تهران، ریال/تومان و اتوماسیون Playwright به سناریوهای قابل‌اجرا تبدیل می‌کنیم.

i18n و L10n دقیقاً چه هستند؟

Internationalization یا i18n طراحی و پیاده‌سازی نرم‌افزار به‌گونه‌ای است که بدون بازنویسی منطق اصلی بتواند زبان، خط، منطقه، تقویم، منطقهٔ زمانی و قواعد قالب‌بندی متفاوت را پشتیبانی کند. عدد ۱۸ تعداد حروف میان I و n در واژهٔ Internationalization است.

Localization یا L10n تطبیق یک محصول i18n‌شده برای بازار یا Locale مشخص است: ترجمه، واژه‌نامه، قالب تاریخ و عدد، تصاویر، قوانین محصول، محتوای حقوقی و کیفیت تجربهٔ همان مخاطب. Globalization یا g11n گاهی نام چتر فرایند کسب‌وکاری و فنی عرضه در چند بازار است.

حوزه پرسش اصلی تست نمونهٔ نقص
i18n آیا معماری از تفاوت‌ها بدون Fork کد پشتیبانی می‌کند؟ متن هاردکد، چیدمان فقط LTR، Parser وابسته به فرمت انگلیسی
L10n آیا خروجی برای یک Locale خاص درست و طبیعی است؟ ترجمهٔ نادرست، «تومان» با مقدار ریالی، تاریخ شمسی اشتباه
بازار/محصول آیا سیاست تجاری و حقوقی مقصد درست اعمال شده است؟ روش پرداخت ناموجود، متن رضایت یا نشانی نامتناسب

تست i18n موفق ثابت نمی‌کند ترجمهٔ فارسی خوب است؛ LQA خوب هم ثابت نمی‌کند Parser در Locale دیگری امن است. هر کدام مرز اثبات خودش را دارد.

زبان، Locale، منطقه و جهت را یکی نگیرید

fa زبان فارسی را معرفی می‌کند و fa-IR فارسی در زمینهٔ منطقهٔ ایران را. با این حال، Language Tag به‌تنهایی واحد پول، منطقهٔ زمانی یا تقویم انتخابی کاربر را قطعی نمی‌کند. ممکن است کاربری زبان فارسی، تقویم میلادی، ارقام لاتین و منطقهٔ زمانی اروپا را انتخاب کرده باشد.

تگ‌های زبان بر اساس BCP ۴۷ ساخته می‌شوند. RFC ۵۶۴۶ از IETF ساختار زیرتگ‌های زبان، Script، Region، Variant و Extension را تعریف می‌کند. پس مقدارهایی مانند fa_IR و Persian-Iran را به‌عنوان Language Tag اختراع نکنید؛ قالب استاندارد وب fa-IR است.

  • Language: فارسی، انگلیسی یا عربی؛
  • Script: خط عربی یا لاتین و جهت غالب آن؛
  • Region: بازار و قراردادهای منطقه‌ای؛
  • Calendar: هجری شمسی، میلادی یا انتخاب دیگر؛
  • Time zone: مانند Asia/Tehran؛
  • Numbering system: ارقام فارسی/Extended Arabic-Indic یا لاتین؛
  • Currency/domain unit: IRR، ریال یا نمایش محصولی تومان؛
  • Direction: RTL/LTR؛ ویژگی ارائه است و از نام زبان به‌تنهایی استنتاج کامل نمی‌شود.

پیش از تست، قرارداد Locale بنویسید

چک‌لیست بدون Oracle فقط فهرست چیزهایی است که باید «نگاه شوند». قرارداد Locale منبع قضاوت را برای هر تبدیل مشخص می‌کند:

بخش قرارداد نمونه برای محصول ایرانی شاهد تست
Locale پشتیبانی‌شده fa-IR و en-US Registry نسخه‌دار و تنظیم حساب
ترتیب انتخاب انتخاب کاربر ← حساب/سازمان ← مرورگر ← پیش‌فرض امن تست هر منبع و تعارض آن‌ها
Fallback ترجمه fa-IR → fa → en یا خط‌مشی صریح دیگر کلید گم‌شده و Telemetry fallback
مدل داخلی پول عدد صحیح ریال + کد/واحد صریح API و Ledger، مستقل از متن UI
ورودی عدد پذیرش یا رد ارقام فارسی، عربی و لاتین طبق فیلد جدول نمونه و خطای مشخص
زمان و تقویم Instant در UTC؛ نمایش با Asia/Tehran و تقویم انتخابی Instant ثابت، zone و calendar ثبت‌شده
Normalization NFC برای متن عادی؛ نگاشت ی/ک فقط در حوزهٔ مصوب ورودی کدپوینتی و خروجی/ذخیره
جهت سند فارسی RTL؛ شناسه‌ها و قطعات لاتین ایزوله DOM معنایی + بازبینی دیداری/کیبورد

قرارداد باید Owner و نسخه داشته باشد. ارتقای CLDR، ICU، tzdata، فونت یا فایل ترجمه می‌تواند خروجی را بدون تغییر مستقیم کد کسب‌وکار عوض کند؛ این هویت‌ها را در گزارش اجرا ثبت کنید.

ماتریس تست Locale را بر پایهٔ ریسک بسازید

ضرب همهٔ زبان‌ها در همهٔ مرورگرها، دستگاه‌ها، مناطق زمانی و کانال‌ها به انفجار ترکیبی می‌رسد. ابتدا جریان‌های پرپیامد را با ماتریس تست مبتنی بر ریسک اولویت‌بندی کنید، سپس از ترکیب نماینده و Pairwise برای بقیه استفاده کنید.

ابعاد ماتریس

  • Language Tag، Script و جهت پایه؛
  • Region، ارز، واحد دامنه و قواعد محصول؛
  • تقویم، منطقهٔ زمانی و Instant مرزی؛
  • سیستم ارقام، صفحه‌کلید و روش ورودی؛
  • مرورگر، سیستم‌عامل، اندازهٔ صفحه و Font fallback؛
  • کانال: Web، Mobile، API، Email، SMS، Push، PDF و CSV؛
  • منبع Locale: پروفایل، سازمان، URL، Header یا پیش‌فرض؛
  • نسخهٔ Resource bundle، CLDR/ICU، tzdata و Feature Flag.

سه لایهٔ پوشش

  1. Smoke هر Locale: بارگذاری Resource، نبود کلید خام، جهت و مسیر حیاتی؛
  2. Regression نماینده: ترکیب‌های ریسک‌محور در واحد، Component، API و E2E؛
  3. LQA انسانی: زبان، لحن، فرهنگ، دسترس‌پذیری و تجربه روی محصول واقعی.

برای پوشش دستگاه و مرورگر، منطق انتخاب را با راهنمای Browser و Device Matrix ترکیب کنید؛ Locale نباید ماتریس سازگاری جدا و بدون اولویت بسازد.

Unicode و UTF-۸: شرط لازم، نه اثبات نهایی

UTF-۸ از مسیر مرورگر، API، Queue، Log، پایگاه داده، جست‌وجو و Export باید بدون تبدیل ناخواسته عبور کند. راهنمای کوتاه i18n وب از W3C استفاده و اعلام UTF-۸، پرهیز از اتصال رشته، پشتیبانی فرم‌های محلی و تعیین جهت را توصیه می‌کند. اما ذخیرهٔ صحیح بایت‌ها هنوز Equality، Search و Sort درست را ثابت نمی‌کند.

کدپوینت‌های دام‌دار فارسی

نمایش کدپوینت نام/ریسک
ی U+06CC Persian Yeh
ی U+064A Arabic Yeh؛ شبیه ولی متفاوت
ک U+06A9 Keheh فارسی
ک U+0643 Arabic Kaf؛ شبیه ولی متفاوت
‌ U+200C ZWNJ یا نیم‌فاصله؛ نامرئی اما معنادار در واژه
۰…۹ U+06F0…U+06F9 Extended Arabic-Indic / ارقام رایج فارسی
۰…۹ U+0660…U+0669 Arabic-Indic؛ با ارقام فارسی یکسان نیست
0…9 U+0030…U+0039 ASCII؛ در شناسه‌ها و قراردادهای فنی رایج

تست Round-trip بسازید: ورود از کیبورد، API و Import؛ ذخیره؛ بازیابی؛ جست‌وجو؛ و Export. علاوه بر ظاهر، کدپوینت‌ها و طول Grapheme را در شواهد تشخیصیِ امن نمایش دهید.

Normalization را متناسب با دامنه تست کنید

رشته‌های canonically equivalent ممکن است بایت‌های متفاوت داشته باشند. ضمیمهٔ استاندارد Unicode شمارهٔ ۱۵ فرم‌های NFC، NFD، NFKC و NFKD را تعریف می‌کند و هشدار می‌دهد NFKC/NFKD تمایزهای سازگاری را حذف می‌کنند؛ بنابراین نباید کورکورانه روی هر متن اعمال شوند.

  • برای محتوای عادی، سیاست NFC را از ورودی تا Index و خروجی تست کنید.
  • نگاشت ی→ی و ک→ک بخشی از Unicode Normalization استاندارد نیست؛ یک قاعدهٔ دامنه‌ای جداست.
  • در Search نام، می‌توانید متن اصلی را حفظ و یک کلید جست‌وجوی مصوب بسازید.
  • برای Password، Token، Hash، امضای دیجیتال، IBAN یا شناسهٔ قراردادی، تبدیل خودسرانه نکنید؛ Equality باید صریحاً تعریف شود.
  • Transformation را قابل‌مشاهده کنید تا کاربر بداند چه چیزی تغییر کرده و تیم بتواند Incident را بازتولید کند.

سناریوهای مهم شامل فاصله در برابر ZWNJ، کاراکتر ترکیبی، Emoji چندکدپوینتی، متن تهی پس از Sanitization، Copy/Paste از پیام‌رسان و ورودی حاوی کنترل نامرئی است.

RTL فقط راست‌چین‌کردن CSS نیست

برای سند فارسی، زبان و جهت را در HTML صریح کنید: <html lang="fa-IR" dir="rtl">. راهنمای اعلام زبان در HTML از W3C استفاده از lang روی عنصر html و علامت‌گذاری بخش‌های با زبان دیگر را توضیح می‌دهد. زبان به فناوری کمکی، جست‌وجو و پردازش کمک می‌کند؛ ولی lang جای dir نیست.

چک‌لیست ساختار و چیدمان RTL

  • از CSS Logical Properties مانند margin-inline-start به‌جای نسخه‌های پراکندهٔ left/right استفاده شده است؟
  • آیکون جهت‌دار مانند بازگشت و Progress با معنا Mirror شده، اما آیکون غیرجهت‌دار بی‌دلیل برنگشته است؟
  • ترتیب DOM، Focus و Screen Reader منطقی مانده و فقط با CSS معکوس نشده است؟
  • جدول، Breadcrumb، نمودار، Slider و صفحه‌بندی در RTL قابل‌فهم‌اند؟
  • Tooltip، Modal، Toast و Validation message از قاب بیرون نمی‌زنند؟

راهنمای ساختار RTL در HTML از W3C تعیین جهت پایه با dir، استفاده از Propertyهای منطقی و dir="auto" برای ورودی کاربر را تشریح می‌کند.

متن دوجهته را با دادهٔ واقعی آزمایش کنید

فارسی در کنار شماره سفارش، URL، Email، کد تخفیف و شناسهٔ شبا یک خط دوجهته می‌سازد. الگوریتم Bidi بیشتر ترتیب را حل می‌کند، اما مرز قطعهٔ LTR ممکن است نیاز به ایزوله‌سازی معنایی با <bdi> یا dir="auto" داشته باشد.

این رشته‌ها را در عنوان، دکمه، جدول، اعلان، Log و Copy/Paste تست کنید:

  • سفارش AB-123-IR پرداخت شد؛
  • شماره پیگیری 2026/08/11-A9؛
  • ایمیل user+qa@example.test ثبت شد؛
  • IR820540102680020817909002 کنار برچسب فارسی؛
  • رشته‌ای که با عدد، پرانتز یا Emoji شروع و تمام می‌شود.

فقط Screenshot کافی نیست. Selection، حرکت Cursor، Backspace، Copy/Paste، خواندن Screen Reader و ترتیب متن در Clipboard را نیز مشاهده کنید.

ارقام فارسی، ورودی عدد و شناسه‌ها

سیاست «همهٔ رقم‌ها را فارسی کن» می‌تواند شناسهٔ فنی، کد OTP یا دادهٔ API را خراب کند. برای هر فیلد سه پرسش جدا دارید: چه چیزی پذیرفته می‌شود، مدل canonical چیست و چه چیزی نمایش داده می‌شود؟

فیلد ورودی محتمل مدل و نمایش پیشنهادیِ قراردادی
مبلغ فارسی/لاتین، جداکننده، Paste Decimal/Integer canonical؛ نمایش Locale-aware با واحد صریح
OTP کیبورد عددی و Paste پیامک قرارداد پذیرش رقم روشن؛ Compare امن با canonical digits
شماره سفارش حروف لاتین، خط تیره، عدد Exact identifier؛ نمایش در Bidi isolate
کد ملی ممکن است صفر آغازین داشته باشد String، نه Number؛ طول و checksum طبق قرارداد دامنه
شماره موبایل 09… یا +98… Parsing و canonical form صریح، بدون Regex جهانی ساده‌انگارانه

سناریوهای Paste شامل فاصلهٔ عادی/نامرئی، جداکنندهٔ عربی، علامت منفی، اعشار، صفر آغازین و مخلوط سه مجموعه رقم را اضافه کنید. پیام خطا باید بگوید چه فرمتی پذیرفته است.

جست‌وجو، مرتب‌سازی و مقایسهٔ فارسی

Binary order برای فهرست نام‌های فارسی الزاماً ترتیب مورد انتظار کاربر نیست. Collation باید با Locale و نیاز دامنه تعیین شود. برای Search نیز دربارهٔ ی/ی، ک/ک، اعراب، ZWNJ، فاصله، بزرگی/کوچکی حروف لاتین و typo tolerance قرارداد بنویسید.

  • نتایج Search با نسخهٔ Index و Normalization یکسان‌اند؟
  • مرتب‌سازی Server و Client نتیجهٔ متناقض نمی‌دهند؟
  • Pagination پس از تغییر Collation پایدار است؟
  • Exact match شناسه با fuzzy match نام قاطی نشده است؟
  • Highlight نتیجه مرز Grapheme و RTL را خراب نمی‌کند؟

دادهٔ تست را از نام‌های کوتاه و لاتین فراتر ببرید. راهنمای مدیریت داده تست برای ساخت دادهٔ مصنوعی، مالکیت و TTL مفید است.

تاریخ، تقویم و منطقهٔ زمانی را جدا کنید

«تاریخ شمسی» سه تصمیم متفاوت را پنهان می‌کند: لحظهٔ واقعی، منطقهٔ زمانی و تقویم نمایش. برای رویداد، یک Instant بدون ابهام ذخیره کنید؛ سپس آن را با منطقهٔ زمانی IANA و تقویم انتخابی نمایش دهید. برای تاریخ کسب‌وکاری بدون زمان—مثلاً تاریخ تولد یا سررسید قراردادی—مدل مناسب می‌تواند Local Date به همراه Calendar/Rule باشد.

پایگاه دادهٔ منطقهٔ زمانی IANA تاریخچه و قواعد قابل‌خواندن ماشین را نگه می‌دارد و با تغییر تصمیم‌های سیاسی به‌روزرسانی می‌شود. پس Asia/Tehran را به offset ثابت +03:30 تبدیل نکنید؛ نسخهٔ tzdata محیط را در شاهد ثبت کنید.

سناریوهای مرزی زمان برای ایران

  • لحظهٔ نزدیک نیمه‌شب تهران که تاریخ UTC و محلی متفاوت است؛
  • پایان اسفند و قواعد سال کبیسهٔ تقویم مورد استفاده؛
  • تبدیل رفت‌وبرگشت Gregorian ↔ Persian بدون جابه‌جایی روز؛
  • تاریخ تاریخی در نسخه‌های متفاوت tzdata؛
  • Session یا Token که بر Instant منقضی می‌شود، نه متن تاریخ نمایش؛
  • گزارش «روزانه» که مرز روز را بر اساس منطقهٔ سازمان می‌بندد؛
  • کاربر فارسی در منطقهٔ زمانی دیگری و کاربر انگلیسی در تهران.

استاندارد Unicode LDML یا UTS #۳۵ مدل دادهٔ Locale و قالب‌های تاریخ، عدد، واحد و ترجیحات را مشخص می‌کند. خروجی کتابخانه را به‌عنوان حقیقت کسب‌وکار فرض نکنید؛ نسخه و قرارداد محصول را هم بررسی کنید.

ریال و تومان را به Locale Library واگذار نکنید

IRR کد ارز ریال ایران است؛ «تومان» یک واحد رایج دامنه‌ای است و تبدیل آن باید در قرارداد محصول صریح باشد. Formatter ممکن است نماد، نام یا تعداد رقم متفاوتی تولید کند، اما نمی‌داند کسب‌وکار شما چه زمانی تومان را می‌پذیرد.

چک‌لیست پول

  • مقدار canonical و واحد در API، Database، Queue و Ledger صریح است؟
  • تبدیل ریال/تومان دقیقاً یک‌بار و در مرز تعیین‌شده انجام می‌شود؟
  • از Float برای مبلغ استفاده نشده و قاعدهٔ گردکردن مشخص است؟
  • عدد و واحد در UI، رسید، Email، PDF و Export با هم سازگارند؟
  • Copy/Paste جداکننده‌ها را بدون تغییر مرتبهٔ بزرگی Parse می‌کند؟
  • حداقل/حداکثر، صفر، مبلغ منفی، Refund و جمع سبد آزموده شده‌اند؟
  • در Log و Error، واحد کنار مقدار وجود دارد؟

مثال پرریسک: UI «۱۲۵٬۰۰۰ تومان» نشان می‌دهد، API باید طبق قرارداد { "amount": 1250000, "unit": "IRR" } بفرستد و Ledger همان ۱٬۲۵۰٬۰۰۰ ریال را یک‌بار ثبت کند. Assertion روی متن UI به‌تنهایی کافی نیست.

رشته‌ها را ترجمه‌پذیر طراحی کنید

اتصال "سلام " + name + "!" فقط یک مشکل کدنویسی نیست؛ ترتیب، جنس، جمع و نشانه‌گذاری در زبان‌ها فرق می‌کند. هر پیام باید یک واحد معنایی با Placeholder نام‌دار باشد و مترجم Context، Screenshot، محدودیت و توضیح متغیرها را ببیند.

  • کلید پایدار است و متن منبع به‌عنوان شناسه استفاده نشده؟
  • Placeholderها نام‌دار، typed و در ترجمه حفظ شده‌اند؟
  • Plural/Select به قواعد Locale سپرده شده، نه شرط‌های انگلیسی؟
  • HTML/Markdown داخل ترجمه محدود، Escape و از نظر XSS بررسی شده است؟
  • ترجمهٔ گم‌شده، خالی، قدیمی و Extra key در Build قابل‌مشاهده‌اند؟
  • Fallback کنترل‌شده است و متن چندزبانهٔ ناخواسته را پنهان نمی‌کند؟

راهنمای MessageFormat در ICU الگوهای Argument، Select و Plural را توضیح می‌دهد. تست باید شاخه‌های واقعی پیام، حفظ Placeholder و خروجی خطرناک را پوشش دهد؛ صرف Compileشدن فایل ترجمه کافی نیست.

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

Pseudo-localization رشته‌ها را به شکل کنترل‌شده تغییر می‌دهد تا نقص معماری زودتر دیده شود. این تکنیک جای ترجمهٔ واقعی یا بازبین بومی را نمی‌گیرد.

سه پروفایل مفید

  • Expansion: متن را با نسبت آزمایشی مبتنی بر دادهٔ محصول بلند و در کروشه محصور کنید تا clipping و متن هاردکد دیده شود.
  • Accented/Unicode: کاراکترهای غیرASCII و ترکیبی تزریق کنید، در حالی که Placeholder و Markup سالم می‌مانند.
  • Pseudo-RTL: جهت و متن مختلط را فعال کنید تا وابستگی به left/right و ترتیب نامناسب آشکار شود.

در CI بررسی کنید همهٔ رشته‌های قابل‌مشاهده از Resource می‌آیند، Placeholderها گم نشده‌اند و Screenshotهای صفحات کلیدی Overflow ندارند. درصد Expansion قانون جهانی نیست؛ آن را از توزیع طول ترجمه‌های واقعی محصول تنظیم کنید.

تست L10n سه لایه دارد

بازبینی زبانی

دقت معنا، اصطلاحات، دستور، املا، لحن، جمع، ضمیر و سازگاری با واژه‌نامه بررسی می‌شود. مترجم باید Context داشته باشد؛ رشتهٔ «Save» بدون اینکه دکمه است یا اسم، قابل‌قضاوت نیست.

بازبینی کارکردی

لینک، Shortcut، Validation، پرداخت، Search، فایل، Notification و انتخاب Locale اجرا می‌شوند. ترجمهٔ درست اگر Placeholder را شکسته یا Route را تغییر داده باشد، محصول هنوز معیوب است.

بازبینی دیداری و فرهنگی

Clipping، Font fallback، Line height، جهت، تصویر، رنگ، مثال، واحد و محتوای حساس بازار بررسی می‌شوند. نظر یک فرد بومی دادهٔ کیفی مهمی است، نه تضمین جهانی؛ بازبین، تصمیم و نسخهٔ محتوا را ثبت کنید.

دسترس‌پذیری در Locale فارسی

ترجمهٔ قابل‌دیدن لزوماً قابل‌خواندن برای فناوری کمکی نیست. lang درست به تلفظ Screen Reader کمک می‌کند و dir ترتیب نمایش را هدایت می‌کند. راهنمای کامل تست دسترس‌پذیری و WCAG ۲.۲ را در کنار سناریوهای زیر اجرا کنید:

  • زبان صفحه و قطعهٔ انگلیسی داخل آن درست علامت‌گذاری شده است؛
  • ترتیب Focus با منطق کار و نه صرفاً جهت بصری هماهنگ است؛
  • Error با فیلد مرتبط و به فارسی قابل‌فهم اعلام می‌شود؛
  • Live region، Dialog و Toast ترجمه‌شده به‌موقع خوانده می‌شوند؛
  • Zoom، اندازه متن و فونت جایگزین باعث clipping نمی‌شود؛
  • آیکون بدون متن، نام دسترس‌پذیر بومی و دقیق دارد؛
  • کیبورد فیزیکی و On-screen فارسی، Shortcutها را از کار نمی‌اندازند.

فرم نام، نشانی و تلفن را محلی تست کنید

فرض‌هایی مانند «نام دو بخش دارد»، «همهٔ نشانی‌ها یک خط‌اند» یا «شماره تلفن همیشه ده رقم است» دادهٔ کاربر را رد می‌کنند. فیلدها را فقط تا حد نیاز کسب‌وکار ساخت‌یافته کنید.

  • نام تک‌بخشی، چندبخشی، فارسی/لاتین، ZWNJ و فاصلهٔ اضافی؛
  • استان، شهر، کدپستی با صفر آغازین و نشانی چندخطی؛
  • موبایل ملی و قالب بین‌المللی +98؛
  • کد ملی/شناسه با مدل String، Validation مرحله‌ای و پیام امن؛
  • Auto-complete و Browser autofill در RTL؛
  • Paste، IME، Voice input و اصلاح خودکار کیبورد موبایل؛
  • حفظ متن اصلی در کنار نسخهٔ canonical برای موارد مجاز.

برای PII از دادهٔ مصنوعی معتبر استفاده کنید؛ Screenshot، Video و Trace تست نباید نام، تلفن یا شناسهٔ واقعی را منتشر کنند.

همهٔ کانال‌ها را در قرارداد بگنجانید

کانال ریسک i18n/L10n شاهد
Web/Mobile RTL، Font، keyboard، deep link و cache Locale DOM/semantics، screenshot هدفمند، device run
API/Webhook تبدیل متن نمایش به مدل canonical، header و error code Schema، payload و contract test
Email/SMS/Push Bidi، Placeholder، محدودیت طول، لینک و fallback render واقعی و capture امن
PDF/Print Font embedding، glyph shaping، pagination و جدول RTL متن استخراج‌شده + بازبینی تصویری
CSV/Excel Encoding، delimiter، formula injection و ارقام بازکردن در مصرف‌کنندهٔ هدف و round-trip
Search/Index Normalization/Collation متفاوت با Database query corpus نسخه‌دار

ماتریس تست Cross Browser با Playwright به شما کمک می‌کند اختلاف موتورهای رندر و سیستم‌عامل را از نقص ترجمه یا Locale جدا کنید.

امنیت Unicode و محتوای بومی‌شده

کاراکترهای هم‌شکل، کنترل‌های Bidi و متن نامرئی می‌توانند Log، شناسه، نام کاربری یا حتی مرور کد را گمراه کنند. استاندارد UTS #۳۹ درباره سازوکارهای امنیت Unicode روش‌هایی برای شناسایی Confusableها و محدودسازی شناسه‌ها ارائه می‌کند. خط‌مشی شما باید متناسب با نوع فیلد باشد، نه ممنوعیت عمومی زبان‌های غیرلاتین.

  • نام نمایشی می‌تواند گسترده باشد؛ نام کاربری/دامنه/شناسه سیاست محدودتر و هشدار شباهت می‌خواهد.
  • کنترل‌های Bidi غیرمنتظره در Source، Log و فیلد امنیتی شناسایی و قابل‌مشاهده شوند.
  • Placeholder ترجمه با Context مناسب Escape شود و HTML مترجم قابل‌اعتماد فرض نشود.
  • Resource bundle و TMS دسترسی، Audit trail و امضای Release داشته باشند.
  • Error بومی‌شده Secret، Stack، Token یا وجود حساب کاربری را افشا نکند.
  • Normalization پیش از Validation و پس از آن طبق ترتیب مستند و تست‌شده انجام شود.

مرز انسان و اتوماسیون در تست بومی‌سازی

کنترل مناسب ماشین داوری مناسب انسان
کلید مفقود/اضافی، Placeholder، Encoding، Schema معنای ترجمه، لحن و تناسب فرهنگی
Hardcoded text، API ممنوع، Property منطقی CSS خوانایی، سلسله‌مراتب بصری و طبیعی‌بودن UI
Formatter/Parser با Golden و boundary corpus قابل‌فهم‌بودن تاریخ، پول و پیام برای کاربر هدف
Pseudo-localization، screenshot diff، overflow heuristic تشخیص تغییر معنادار از اختلاف رندر بی‌اهمیت
E2E مسیر حیاتی با Locale/zone مشخص Exploration، فناوری کمکی و رفتار فرهنگی پیچیده

Ruleهای قطعی مانند متن هاردکد و استفاده از left/right را با الگوی تحلیل استاتیک و Quality Gate خودکار کنید. بازبین انسانی نباید وقت خود را صرف تکرار خروجی Linter کند.

نمونه تست Locale با Playwright

مستند Emulation در Playwright تنظیم Locale و Timezone را در BrowserContext نشان می‌دهد. این قابلیت محیط را شبیه‌سازی می‌کند، اما برای نتیجهٔ مالی یا زمان کسب‌وکاری هنوز به Oracle مستقل نیاز دارید.

import { test, expect } from '@playwright/test';

test.use({
  locale: 'fa-IR',
  timezoneId: 'Asia/Tehran',
});

test('Persian checkout keeps rial semantics and RTL contract', async ({ page, request }) => {
  const order = await createOrder({ amountRial: 1_250_000 });
  await page.goto(`/checkout/${order.id}`);

  await expect(page.locator('html')).toHaveAttribute('lang', 'fa-IR');
  await expect(page.locator('html')).toHaveAttribute('dir', 'rtl');
  await expect(page.getByTestId('payable-amount'))
    .toHaveText('۱٬۲۵۰٬۰۰۰ ریال');

  await page.getByRole('button', { name: 'پرداخت' }).click();
  await completeSandboxPayment(order.id);

  const response = await request.get(`/test-support/orders/${order.id}`);
  expect(await response.json()).toMatchObject({
    status: 'PAID', amountRial: 1_250_000, ledgerEntries: 1
  });
});

در پروژهٔ واقعی، helperها باید دادهٔ یکتا، پاک‌سازی امن و هویت Run داشته باشند. برای جلوگیری از Abstractionهای مبهم، اصول کد تست قابل نگهداری را اعمال کنید.

معماری پیشنهادی Suite اتوماسیون

  1. Static: کلید، Placeholder، Hardcode، Encoding، Bidi control و CSS physical property؛
  2. Unit: Parser/Formatter، fallback، Normalization، plural/select و تبدیل دامنه؛
  3. Property/Fuzz: Unicode corpus، round-trip، طول و رشته‌های مختلط؛
  4. Component: pseudo locales، overflow، جهت، state و accessibility tree؛
  5. API/Contract: مدل canonical، Error code و نشت‌نکردن متن نمایش در قرارداد؛
  6. Visual: صفحات نماینده با OS/font/browser pin‌شده و Review انسانی؛
  7. E2E: تعداد کمی مسیر حیاتی در ترکیب‌های پرریسک؛
  8. LQA/Exploratory: بازبین بومی و سناریوهای تازه در نسخهٔ Candidate.

Screenshot را Oracle همه‌چیز نکنید. اختلاف Anti-aliasing می‌تواند نویز بسازد و در عین حال خطای Ledger را نبیند. «قرارداد کنترل و مشاهده» در راهنمای تست‌پذیری نرم‌افزار به طراحی seamهای Locale، clock و evidence کمک می‌کند.

ماتریس عملی فارسی و بازار ایران

ریسک داده/محرک Oracle اصلی سطح
ی/ک عربی و فارسی چهار ترکیب در نام و Search سیاست Normalization + حفظ متن اصلی Unit/API
ZWNJ و فاصله «می‌شود»، «می شود»، «میشود» Search contract و نمایش بدون خرابی Unit/Search
سه مجموعه رقم ۱۲۳، ۱۲۳، ۱۲۳ و مخلوط پذیرش/رد و canonical value صریح Unit/Component
Bidi شناسه متن فارسی + AB-123 DOM isolate، visual و Clipboard Component/E2E
ریال/تومان ۱۲۵٬۰۰۰ تومان ۱٬۲۵۰٬۰۰۰ IRR در API/Ledger API/E2E
نیمه‌شب تهران Instant دو سوی مرز روز Local date طبق Asia/Tehran Unit/API
تقویم شمسی پایان اسفند/سال کبیسه کتابخانه/قرارداد نسخه‌دار و round-trip Unit
callback تکراری پرداخت دو callback با authority یکسان یک Payment، Ledger و fulfillment Integration/E2E
PDF فارسی جدول، عدد و شناسه LTR Font embedded، متن قابل‌استخراج، visual Integration/Manual
Locale leakage دو کاربر fa/en پشت cache پاسخ هر کاربر مطابق preference خودش Integration/Security

عیب‌یابی شکست‌های i18n و L10n

نشانه علت محتمل شاهد بعدی
کلید خام دیده می‌شود Bundle ناقص، namespace یا fallback resource version، key lookup trace
فقط CI متن متفاوت دارد نسخه CLDR/ICU، OS، font یا timezone image/runtime/tzdata/font identity
تاریخ یک روز جابه‌جا است UTC/local، parse بدون zone یا calendar raw instant، zone، calendar و formatter
جست‌وجوی «ی» نتیجه ندارد Normalization/Index ناسازگار code points، analyzer و index version
پرانتز/شناسه برعکس است نبود base direction یا isolation DOM lang/dir/bdi و clipboard text
مبلغ ده برابر است تبدیل دوباره یا واحد ضمنی value+unit در هر hop و ledger
ترجمه کاربر دیگری دیده می‌شود cache key فاقد Locale/tenant request headers، cache key، user preference
Screenshot شکست خورده ولی UI سالم است font/anti-aliasing محیط pixel diff، font hash و semantic assertions

گزارش شکست باید Locale resolved، منبع انتخاب، timezone، calendar، numbering system، resource version، tzdata/ICU/CLDR، browser/OS، run ID و دادهٔ حساس‌زدایی‌شده را ثبت کند.

حاکمیت و مالکیت کیفیت بومی‌سازی

  • Product/Market owner: بازار، واحد، محتوای حقوقی و ریسک پذیرفته‌شده؛
  • Engineering: قرارداد canonical، fallback، formatter/parser و observability؛
  • Design: Layout انعطاف‌پذیر، typography و componentهای bidirectional؛
  • Localization/Linguist: واژه‌نامه، لحن، Context و LQA؛
  • QA: ماتریس ریسک، Oracle، coverage و evidence؛
  • Security/Accessibility: کنترل‌های تخصصی برای جریان‌های مربوط؛

هر Locale یک DRI، وضعیت Launch و Exit criteria می‌خواهد. «ترجمه ۱۰۰٪» به معنی آمادگی انتشار نیست؛ ممکن است همهٔ کلیدها ترجمه شده باشند اما پرداخت، Screen Reader یا PDF کار نکند.

معیارهایی که واقعاً کمک می‌کنند

  • نرخ کلید مفقود و Fallback در Runtime، به تفکیک Locale و صفحه؛
  • نقص‌های تولید بر اساس دسته: معنا، جهت، قالب، Parser، داده، cache یا accessibility؛
  • زمان رفع نقص Locale و سهم Reopen؛
  • پوشش جریان‌های پرریسک در برابر ماتریس، نه تعداد کل Screenshot؛
  • نرخ شکست Pseudo-localization و Placeholder gate؛
  • اختلاف نسخه Resource/CLDR/ICU/tzdata بین محیط‌ها؛
  • درصد Visual diffهای معنادار در برابر نویز؛
  • سن و مالک Gapهای پذیرفته‌شدهٔ هر بازار.

تعداد کلمات ترجمه‌شده و درصد Automation شاخص ظرفیت‌اند، نه کیفیت. آن‌ها را بدون Defect، Risk coverage و تجربهٔ کاربر به KPI موفقیت تبدیل نکنید.

برنامهٔ ۳۰روزهٔ اجرای تست فارسی و RTL

هفتهٔ اول: قرارداد و Baseline

جریان‌های ورود، جست‌وجو، پرداخت، گزارش و اعلان را فهرست کنید. Locale contract، ترتیب انتخاب/fallback و مالک ریال/تومان، زمان و Normalization را تصویب کنید. پنج نقص اخیر را دسته‌بندی کنید.

هفتهٔ دوم: Gateهای ارزان

بررسی کلید/Placeholder، UTF-۸، متن هاردکد، CSS physical property و pseudo locales را به CI اضافه کنید. Corpus فارسی شامل ی/ک، ZWNJ، سه مجموعه رقم و متن Bidi بسازید.

هفتهٔ سوم: Oracleهای دامنه

Unit testهای زمان/پول/Normalization و Contract testهای API را تکمیل کنید. دو E2E حیاتی با Locale و timezone مشخص بسازید و نتیجهٔ مالی/داده‌ای را جدا از متن UI Assert کنید.

هفتهٔ چهارم: LQA و بازخورد تولید

یک LQA با متخصص فارسی و تست فناوری کمکی انجام دهید. Telemetry امن برای fallback و Locale resolution را بررسی، داشبورد متوازن را راه‌اندازی و Gapها را با Owner و تاریخ انقضا ثبت کنید.

اشتباهات رایج در تست i18n و L10n

  • یکی‌گرفتن Language، Locale، Region، Direction، Calendar و Timezone؛
  • اعلام «پشتیبانی فارسی» فقط به دلیل UTF-۸ یا یک صفحهٔ RTL؛
  • راست‌چین‌کردن با CSS بدون lang/dir معنایی؛
  • تبدیل همهٔ رقم‌ها و شناسه‌ها به فارسی بدون قرارداد فیلد؛
  • نگاشت کور ی/ی و ک/ک روی Password، Token یا شناسه؛
  • استفاده از offset ثابت به جای Asia/Tehran و tzdata؛
  • واگذاری ریال/تومان به Formatter و نبود واحد در API/Log؛
  • اتصال تکه‌رشته‌ها و شرط جمع مخصوص انگلیسی؛
  • Fallback ساکت که ترجمهٔ گم‌شده را تا تولید پنهان می‌کند؛
  • ماتریس Cartesian عظیم بدون اولویت ریسک؛
  • Screenshot به‌عنوان تنها Oracle و ذخیره Baseline روی محیط ناپایدار؛
  • فرض اینکه مترجم بومی همهٔ نقص‌های فنی را می‌یابد؛
  • تست فقط Web و فراموشی SMS، PDF، CSV، Search و Log؛
  • استفاده از PII واقعی در Screenshot/Trace؛
  • اعلام آمادگی بازار بر اساس «۱۰۰٪ ترجمه».

چک‌لیست نهایی انتشار Locale

  1. Language Tag استاندارد، جهت و ترتیب انتخاب/fallback نسخه‌دار است.
  2. مدل canonical از نمایش تاریخ، عدد و پول جداست.
  3. UTF-۸ در Web/API/DB/Queue/Export round-trip شده است.
  4. سیاست NFC و نگاشت‌های دامنه‌ای فارسی با مرز روشن تست شده‌اند.
  5. ی/ی، ک/ک، ZWNJ، سه مجموعه رقم و متن Bidi در Corpus هستند.
  6. CSS منطقی، DOM/Focus و Componentهای RTL بررسی شده‌اند.
  7. زمان با Instant، Calendar، Asia/Tehran و tzdata نسخه‌دار سنجیده شده است.
  8. ریال/تومان در هر hop واحد صریح و Oracle مالی مستقل دارد.
  9. Placeholder، plural/select، missing key و pseudo locale Gate دارند.
  10. فرم، Search، Sort، PDF، CSV، Email/SMS و Notification پوشش متناسب دارند.
  11. LQA فارسی، دسترس‌پذیری و امنیت Unicode انجام شده‌اند.
  12. گزارش اجرا Locale/resource/runtime identity و دادهٔ امن دارد.
  13. Gap باقی‌مانده Owner، اثر و تاریخ بازنگری دارد.

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

تفاوت تست i18n و L10n چیست؟

تست i18n انعطاف معماری برای زبان، خط، Locale، قالب و جهت‌های مختلف را می‌سنجد؛ تست L10n درستی ترجمه، قالب، محتوا و تجربهٔ یک بازار مشخص مانند فارسی ایران را بررسی می‌کند. اولی زیرساخت عمومی را هدف می‌گیرد و دومی کیفیت تطبیق واقعی را؛ هیچ‌کدام جای دیگری نیست.

آیا fa-IR به‌تنهایی تقویم شمسی و منطقهٔ زمانی تهران را تعیین می‌کند؟

خیر. fa-IR Language Tag است، اما Calendar، Timezone، Numbering system و ترجیح کاربر باید جداگانه در قرارداد و Runtime حل شوند. کاربر فارسی ممکن است خارج ایران باشد یا تقویم میلادی بخواهد. مقدار resolved هر بُعد را در شواهد تست ثبت کنید.

برای فارسی، ی و ک عربی را همیشه باید به فارسی تبدیل کنیم؟

نه به‌صورت جهانی. برای Search نام یا متن نمایشی می‌توان یک نگاشت دامنه‌ای مصوب داشت، ولی Password، Token، امضا و شناسهٔ قراردادی ممکن است Exact match بخواهند. متن اصلی را در موارد لازم حفظ و نسخهٔ canonical را جدا تعریف کنید.

چگونه ریال و تومان را تست کنیم؟

مقدار و واحد را در هر مرز—UI، API، Database، Queue و Ledger—صریح کنید. سناریو باید تبدیل دقیق یک‌باره، جداکننده، ارقام، حدها، Refund و اثر مالی را بسنجد. Assertion متن «تومان» بدون Oracle روی مقدار canonical و Ledger کافی نیست.

آیا اتوماسیون و شبه‌بومی‌سازی جای بازبین فارسی‌زبان را می‌گیرند؟

خیر. ماشین کلید مفقود، Placeholder، Overflow، Parser، جهت و Regression را سریع پیدا می‌کند؛ انسان معنا، لحن، حساسیت فرهنگی، خوانایی و تجربه را داوری می‌کند. بهترین استراتژی، Gateهای خودکار ارزان در هر تغییر و LQA انسانی هدفمند پیش از انتشار است.

جمع‌بندی: Locale را به یک قرارداد قابل‌آزمون تبدیل کنید

تست بومی‌سازی با ترجمهٔ چند صفحه تمام نمی‌شود. Language Tag، جهت، Unicode، Normalization، ورودی، Search، تقویم، زمان، پول، کانال و Resource version باید از مدل canonical تا تجربهٔ کاربر قابل‌ردیابی باشند. برای ایران، ی/ک، نیم‌فاصله، سه مجموعه رقم، Bidi، تقویم شمسی، Asia/Tehran و مرز ریال/تومان سناریوهای درجه‌یک‌اند، نه جزئیات ظاهری.

از یک جریان پرریسک مانند پرداخت شروع کنید: قرارداد Locale را بنویسید، Corpus فارسی بسازید، Oracle UI را به API/Ledger وصل کنید و Pseudo-RTL را در CI اجرا کنید. وقتی هر شکست Locale identity، نسخه و شاهد قابل‌بازتولید داشته باشد، تیم به‌جای شکار Screenshotهای پراکنده می‌تواند درباره ریسک انتشار تصمیم بگیرد.

دیدگاهتان را بنویسید