في هذه الصفحة
Normalizer#
يجعل الصنف RtlyKit\Text\Normalizer النص الفارسي قابلًا للمقارنة والتخزين. فمدخلات المستخدمين تخلط بين أشكال الحروف العربية والفارسية (لوحة المفاتيح العربية تكتب «ي» و«ك»)، وتتضمن حركات وتطويلًا ومسافات زائدة؛ فتبدو الكلمة نفسها متطابقة على الشاشة لكنها تفشل في مقارنة السلاسل أو في البحث في قاعدة البيانات.
normalize()#
تفعل Normalizer::normalize($text, $removeDiacritics = true) ما يلي:
- تتحول الحروف العربية إلى نظيراتها الفارسية: «ك» إلى «ک»، و«ي» «ى» «ے» إلى «ی»، و«ة» «ۀ» إلى «ه»، و«ؤ» إلى «و»، و«إ» «أ» «ٱ» إلى «ا».
- تتحول الأرقام العربية الهندية (
٠-٩) إلى أرقام فارسية (۰-۹). أما الأرقام الإنجليزية فتُترك كما هي. - يُحذف التطويل (tatweel، المحرف U+0640).
- تُحذف الحركات، أي علامات الحركات القصيرة (من U+064B إلى U+065F وU+0670)، إلا إذا مرّرت
falseوسيطًا ثانيًا. - تُختزل سلاسل المسافات البيضاء (المسافات والجداول والأسطر الجديدة) إلى مسافة واحدة، وتُقصّ الأطراف.
<?php
require 'vendor/autoload.php';
use RtlyKit\Text\Normalizer;
use function RtlyKit\normalize_text;
echo Normalizer::normalize("علي كتاب ١٢٣ 456"), "\n"; // علی کتاب ۱۲۳ 456
echo Normalizer::normalize("كــتاب"), "\n"; // کتاب
echo Normalizer::normalize(" سلام \n\t دنیا "), "\n"; // سلام دنیا
echo Normalizer::normalize("أحمد إبراهيم مؤمن ة"), "\n"; // احمد ابراهیم مومن ه
echo Normalizer::normalize("ثَبْت ١٢٣ 456"), "\n"; // ثبت ۱۲۳ 456
echo Normalizer::normalize("ثَبْت", false), "\n"; // ثَبْت (تبقى الحركات)
echo normalize_text('كتاب'), "\n"; // کتاب
تستدعي الدالة المساعدة normalize_text() الدالة Normalizer::normalize() بالقيم الافتراضية.
fixHalfSpace()#
نصف المسافة (ZWNJ، U+200C) هو ما يجعل «میخواهم» تظهر صحيحة. وكثيرًا ما يترك النسخ واللصق عدة منها متتالية، أو واحدة معلّقة بجوار مسافة أو في نهاية النص. تنظّف fixHalfSpace() ذلك:
- تُحذف الواصلات الناعمة (U+00AD).
- تُختزل سلسلة من محارف ZWNJ إلى واحد.
- يُحذف ZWNJ الواقع مباشرة قبل المسافة البيضاء أو بعدها.
- تُحذف محارف ZWNJ في بداية النص ونهايته تمامًا.
$a = "می\u{200C}\u{200C}\u{200C}خواهم";
echo mb_strlen($a), ' ', mb_strlen(Normalizer::fixHalfSpace($a)), "\n"; // 10 8
$b = "\u{200C}سلام \u{200C}دنیا\u{200C}";
echo mb_strlen($b), ' ', mb_strlen(Normalizer::fixHalfSpace($b)), "\n"; // 12 9
echo Normalizer::fixHalfSpace($b), "\n"; // سلام دنیا
لا تُدرج أنصاف المسافات الناقصة، ولا تختزل المسافات العادية.
clean()#
الدالة Normalizer::clean() هي النسخة الشاملة للتخزين والبحث: normalize() ثم fixHalfSpace() ثم حذف المحارف الخفية: المسافة ذات العرض الصفري (U+200B) ووصلة العرض الصفري (U+200D) وعلامة ترتيب البايت (U+FEFF). ويبقى ZWNJ المحرف الوحيد ذو العرض الصفري الذي يُحتفظ به.
$c = "كتاب\u{200B}ي ١٢٣";
echo Normalizer::clean($c), "\n"; // کتابی ۱۲۳
أما تطبيق normalize() وحدها على النص نفسه فكان سيترك المسافة ذات العرض الصفري داخل الكلمة، ولهذا وُجدت clean(). استخدمها قبل تخزين النصوص التي يدخلها المستخدمون أو فهرستها.
Detector#
يجيب RtlyKit\Text\Detector عن أسئلة الكتابة (script) واللغة والاتجاه. كل دوالّه ساكنة (static) ولا تُطلق استثناءً أبدًا.
isPersian() و isArabic()#
كلتاهما استدلال تقريبي (heuristic) للنص المكتوب بالخط العربي. تُعرف الفارسية من الحروف الموجودة في الفارسية وحدها (پ چ ژ گ، والشكلان الفارسيان ک وی، والأرقام الفارسية). وتُعرف العربية من ي ك ى ة والأرقام العربية الهندية. أما الحروف المشتركة بين اللغتين، ومنها «ه» وصور الهمزة، فلا تُحتسب. ويُعدّ النص فارسيًا عندما تكون الحروف الفارسية الخالصة بتكرار لا يقل عن الحروف العربية الخالصة، فحالات التعادل والنصوص التي لا تحوي حروفًا مميِّزة تُعدّ فارسية.
use RtlyKit\Text\Detector;
var_dump(Detector::isPersian('سلام')); // bool(true)، لا حروف مميِّزة، فارسية افتراضياً
var_dump(Detector::isPersian('گچپژ')); // bool(true)
var_dump(Detector::isArabic('مرحبا بكم')); // bool(true)
var_dump(Detector::isArabic('كتاب ي')); // bool(true)
var_dump(Detector::isPersian('hello')); // bool(false)، لا خط عربي إطلاقاً
var_dump(Detector::isPersian('۱۲۳')); // bool(true)، أرقام فارسية
var_dump(Detector::isArabic('٣٤٥')); // bool(true)، أرقام عربية هندية
var_dump(Detector::isPersian('')); // bool(false)
الاتجاه و RTL#
containsRtl($text): هل يحتوي النص على أي محرف من خط يُكتب من اليمين إلى اليسار (العبرية والعربية وملحقاتها والسريانية والتانا وN'Ko وغيرها، وأشكال العرض العربية) أو علامة RLM.direction($text): تُرجع'rtl'أو'ltr'بحسب أول حرف قوي، وفق قاعدة Unicode للنص ثنائي الاتجاه. الأرقام وعلامات الترقيم والمسافات محايدة. والنص الذي لا يحوي حروفًا يُرجع'ltr'.isHebrew($text): هل يحتوي النص على حروف عبرية.
var_dump(Detector::containsRtl('abc')); // bool(false)
echo Detector::direction('سلام hello'), "\n"; // rtl
echo Detector::direction('Hello مرحبا'), "\n"; // ltr (الحرف الأول لاتيني)
echo Detector::direction('۱۲۳'), "\n"; // ltr (الأرقام محايدة ولا يوجد حرف)
var_dump(Detector::isHebrew('שלום')); // bool(true)
تغلّف الدالتان المساعدتان contains_rtl() وtext_direction() الدالتين containsRtl() وdirection(). استخدم direction() للسمة dir في HTML على النصوص التي ينشئها المستخدمون.
isRtlLocale()#
تخبرك isRtlLocale($locale) إن كان وسم اللغة المحلية (locale) يُكتب من اليمين إلى اليسار. تقسّم الوسم عند - و_ و. و@، وتتجاهل حالة الأحرف والمسافات المحيطة، وتُرجع true عندما تكون اللغة من اللغات التي تُكتب من اليمين إلى اليسار (fa وar وhe وur وps وug وdv وckb وazb وغيرها) أو عندما يوجد وسم فرعي للخط مثل Arab أو Hebr.
var_dump(Detector::isRtlLocale('fa_IR')); // bool(true)
var_dump(Detector::isRtlLocale('ar-SA')); // bool(true)
var_dump(Detector::isRtlLocale('ckb-IQ')); // bool(true)
var_dump(Detector::isRtlLocale('az-Arab')); // bool(true)، الوسم الفرعي للخط هو الحاسم
var_dump(Detector::isRtlLocale('ku')); // bool(false)، "ku" وحدها ليست في قائمة اللغات RTL
var_dump(Detector::isRtlLocale('en')); // bool(false)
var_dump(Detector::isRtlLocale('')); // bool(false)
Slugify#
تحوّل Slugify::make($text, $separator = '-') العنوان إلى معرّف نصي (slug) ملائم للروابط مع إبقاء الحروف الفارسية والعربية مقروءة. ولا تُجري أي نقحرة (transliteration) أبدًا.
- يُطبَّع النص بـ
Normalizer::normalize(). - تتحول المسافات البيضاء وأنصاف المسافات إلى الفاصل.
- يُحذف كل شيء عدا الحروف (من أي خط) وعلامات الدمج والأرقام (من أي مجموعة) والواصلات والشرطات السفلية. وتختفي علامات الترقيم والرموز التعبيرية.
- تُختزل الواصلات والشرطات السفلية والفواصل المتكررة إلى واحد، وتُقصّ الفواصل من الطرفين.
- تُحوَّل النتيجة إلى أحرف صغيرة بترميز UTF-8.
use RtlyKit\Text\Slugify;
echo Slugify::make('سلام دنیا'), "\n"; // سلام-دنیا
echo Slugify::make("کتاب\u{200C}خانه ملی"), "\n"; // کتاب-خانه-ملی
echo Slugify::make('Hello, World!'), "\n"; // hello-world
echo Slugify::make(' A--B__C '), "\n"; // a-b_c
echo Slugify::make('سلام!!! دنیا؟'), "\n"; // سلام-دنیا
echo Slugify::make('۱۲۳ ابر'), "\n"; // ۱۲۳-ابر
echo Slugify::make('كتاب ي'), "\n"; // کتاب-ی
echo Slugify::make('سلام 😀 دنیا'), "\n"; // سلام-دنیا
echo Slugify::make('عکس.jpg'), "\n"; // عکسjpg
var_dump(Slugify::make('$%^&')); // string(0) ""
أمران يجب الانتباه إليهما. الأرقام تحتفظ بالمجموعة التي كُتبت بها (۱۲۳ تبقى فارسية)، فشغّل Digits::toEnglish() أولًا إن أردت أرقام ASCII في الروابط. والنقطة تُحذف ولا تُعامل فاصلاً، فتلتصق امتدادات الملفات باسمها (عکسjpg)؛ فأنشئ المعرّف النصي للاسم وللامتداد كلٍّ على حدة.
الفاصل#
مرّر أي سلسلة وسيطًا ثانيًا: '_'، أو سلسلة أطول، أو سلسلة فارغة لضم الكلمات دون أي فاصل.
echo Slugify::make('سلام دنیا', '_'), "\n"; // سلام_دنیا
echo Slugify::make('سلام دنیا', ''), "\n"; // سلامدنیا
echo Slugify::make('Hello World', '--'), "\n"; // hello--world
الفاصل محدود بـ 64 بايت ويجب أن يكون UTF-8 صالحًا. والنص نفسه يجب أن يكون UTF-8 صالحًا كذلك. الفاصل الأطول من الحد يُثير RtlyKitException بالرمز input_too_long، وأي نص أو فاصل غير صالح يُثيره بالرمز invalid_argument (والسياق argument يقول أيهما):
try {
Slugify::make('Hello World', str_repeat('x', 65));
} catch (\RtlyKit\Exceptions\RtlyKitException $e) {
echo $e->getMessage(), ' [', $e->getErrorCode()->value, "]\n";
// The slug separator is too long. [input_too_long]
}
try {
Slugify::make("abc\xff");
} catch (\RtlyKit\Exceptions\RtlyKitException $e) {
echo $e->getMessage(), ' [', $e->getErrorCode()->value, "]\n";
// The text to slugify must be valid UTF-8. [invalid_argument]
}