كيف أقوم بتقسيم النص إلى جمل؟
استخدم Intl.Segmenter لتقسيم النص إلى جمل مع اكتشاف الحدود الواعي للغة الذي يتعامل مع علامات الترقيم والاختصارات والقواعد الخاصة باللغة.
مقدمة
عندما تقوم بمعالجة النص للترجمة أو التحليل أو العرض، غالبًا ما تحتاج إلى تقسيمه إلى جمل فردية. يفشل النهج الساذج باستخدام التعبيرات النمطية لأن حدود الجمل أكثر تعقيدًا من النقاط متبوعة بمسافات. يمكن أن تنتهي الجمل بعلامات استفهام أو علامات تعجب أو علامات حذف. تظهر النقاط في الاختصارات مثل "Dr." أو "Inc." دون إنهاء الجمل. تستخدم اللغات المختلفة علامات ترقيم مختلفة كفواصل للجمل.
تحل واجهة برمجة التطبيقات Intl.Segmenter هذه المشكلة من خلال توفير اكتشاف حدود الجمل الواعي للغة. إنها تفهم قواعد تحديد حدود الجمل في اللغات المختلفة وتتعامل مع الحالات الاستثنائية مثل الاختصارات والأرقام وعلامات الترقيم المعقدة تلقائيًا.
المشكلة في التقسيم على النقاط
يمكنك محاولة تقسيم النص إلى جمل عن طريق التقسيم على النقاط متبوعة بمسافات.
const text = "Hello world. How are you? I am fine.";
const sentences = text.split(". ");
console.log(sentences);
// ["Hello world", "How are you? I am fine."]
هذا النهج له مشاكل متعددة. أولاً، لا يتعامل مع علامات الاستفهام أو علامات التعجب. ثانيًا، ينقسم عند الاختصارات التي تحتوي على نقاط. ثالثًا، يزيل النقطة من كل جملة باستثناء الأخيرة. رابعًا، لا يعمل عندما تكون هناك مسافات متعددة بعد النقاط.
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const sentences = text.split(". ");
console.log(sentences);
// ["Dr", "Smith works at Acme Inc", "He starts at 9 a.m."]
ينقسم النص بشكل غير صحيح عند "Dr." و "Inc." لأن هذه الاختصارات تحتوي على نقاط. تحتاج إلى نهج أكثر ذكاءً يفهم قواعد حدود الجمل.
استخدام تعبير نمطي أكثر تعقيدًا
يمكنك تحسين التعبير النمطي للتعامل مع المزيد من الحالات.
const text = "Hello world. How are you? I am fine!";
const sentences = text.split(/[.?!]\s+/);
console.log(sentences);
// ["Hello world", "How are you", "I am fine", ""]
هذا ينقسم على النقاط وعلامات الاستفهام وعلامات التعجب متبوعة بمسافة بيضاء. إنه يتعامل مع المزيد من الحالات ولكنه لا يزال يفشل مع الاختصارات وينشئ سلاسل نصية فارغة. كما أنه يزيل علامات الترقيم من كل جملة.
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const sentences = text.split(/[.?!]\s+/);
console.log(sentences);
// ["Dr", "Smith works at Acme Inc", "He starts at 9 a", "m", ""]
لا يمكن لنهج التعبيرات النمطية التمييز بشكل موثوق بين النقاط التي تنهي الجمل والنقاط التي تظهر في الاختصارات. يصبح بناء تعبير نمطي شامل يتعامل مع جميع الحالات الاستثنائية أمرًا غير عملي. تحتاج إلى حل يفهم القواعد اللغوية.
استخدام Intl.Segmenter لتقسيم الجمل
ينشئ المُنشئ Intl.Segmenter مُقسِّمًا يقسم النص بناءً على القواعد الخاصة باللغة. تحدد لغة وتضبط خيار granularity على "sentence".
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
for (const segment of segments) {
console.log(segment.segment);
}
// "Hello world. "
// "How are you? "
// "I am fine!"
تُرجع الدالة segment() كائنًا قابلاً للتكرار ينتج كائنات مقاطع. يحتوي كل كائن مقطع على خاصية segment تحتوي على نص ذلك المقطع. يحافظ المُقسِّم على علامات الترقيم والمسافات البيضاء في نهاية كل جملة.
يمكنك تحويل المقاطع إلى مصفوفة باستخدام Array.from().
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Hello world. ", "How are you? ", "I am fine!"]
ينشئ هذا مصفوفة حيث كل عنصر هو جملة بعلامات الترقيم والمسافات الأصلية.
كيف يتعامل Intl.Segmenter مع الاختصارات
يفهم المُقسِّم أنماط الاختصارات الشائعة ولا يقسم على النقاط التي تظهر داخل الاختصارات.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Dr. Smith works at Acme Inc. ", "He starts at 9 a.m."]
ينقسم النص بشكل صحيح إلى جملتين. النقاط في "Dr." و"Inc." و"a.m." لا تؤدي إلى فواصل جمل لأن المُقسِّم يتعرف على هذه كاختصارات. هذا التعامل التلقائي مع الحالات الاستثنائية هو السبب في أن Intl.Segmenter أفضل من نهج التعبيرات النمطية.
إزالة المسافات البيضاء من الجمل
يتضمن المُقسِّم المسافات البيضاء اللاحقة في كل جملة. يمكنك إزالة هذه المسافات البيضاء إذا لزم الأمر.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment.trim());
console.log(sentences);
// ["Hello world.", "How are you?", "I am fine!"]
تزيل الدالة trim() المسافات البيضاء البادئة واللاحقة من كل جملة. هذا مفيد عندما تحتاج إلى حدود جمل نظيفة بدون مسافات إضافية.
الحصول على البيانات الوصفية للمقطع
يتضمن كل كائن مقطع بيانات وصفية حول موضع المقطع في النص الأصلي.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you?";
const segments = segmenter.segment(text);
for (const segment of segments) {
console.log({
text: segment.segment,
index: segment.index,
input: segment.input
});
}
// { text: "Hello world. ", index: 0, input: "Hello world. How are you?" }
// { text: "How are you?", index: 13, input: "Hello world. How are you?" }
تشير الخاصية index إلى مكان بداية المقطع في النص الأصلي. تحتوي الخاصية input على النص الأصلي الكامل. هذه البيانات الوصفية مفيدة عندما تحتاج إلى تتبع مواضع الجمل أو إعادة بناء النص الأصلي.
تقسيم الجمل في لغات مختلفة
تحتوي اللغات المختلفة على قواعد مختلفة لحدود الجمل. يقوم المقسّم بتكييف سلوكه بناءً على اللغة المحددة.
في اللغة اليابانية، يمكن أن تنتهي الجمل بنقطة كاملة العرض 。 تسمى كوتِن.
const segmenter = new Intl.Segmenter("ja", { granularity: "sentence" });
const text = "私は猫です。名前はまだない。";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["私は猫です。", "名前はまだない。"]
ينقسم النص بشكل صحيح عند علامات إنهاء الجمل اليابانية. المقسّم المُهيأ للغة الإنجليزية لن يتعرف على هذه الحدود بشكل صحيح.
في اللغة الهندية، يمكن أن تنتهي الجمل بشريط عمودي । يسمى بورنا فيرام.
const segmenter = new Intl.Segmenter("hi", { granularity: "sentence" });
const text = "यह एक वाक्य है। यह दूसरा वाक्य है।";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["यह एक वाक्य है। ", "यह दूसरा वाक्य है।"]
يتعرف المقسّم على النقطة الكاملة في نظام ديفاناغاري كحد للجملة. هذا السلوك الواعي باللغة أمر بالغ الأهمية لمعالجة النصوص الدولية.
استخدام اللغة الصحيحة للنصوص متعددة اللغات
عند معالجة نص يحتوي على لغات متعددة، اختر اللغة التي تطابق اللغة الأساسية للنص. يستخدم المقسّم اللغة المحددة لتحديد قواعد الحدود التي يجب تطبيقها.
const englishText = "Hello world. How are you?";
const japaneseText = "私は猫です。名前はまだない。";
const englishSegmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const japaneseSegmenter = new Intl.Segmenter("ja", { granularity: "sentence" });
const englishSentences = Array.from(
englishSegmenter.segment(englishText),
s => s.segment
);
const japaneseSentences = Array.from(
japaneseSegmenter.segment(japaneseText),
s => s.segment
);
console.log(englishSentences);
// ["Hello world. ", "How are you?"]
console.log(japaneseSentences);
// ["私は猫です。", "名前はまだない。"]
إنشاء مقسّمات منفصلة لكل لغة يضمن الكشف الصحيح عن الحدود. إذا كنت تعالج نصاً حيث اللغة غير معروفة، يمكنك استخدام لغة عامة مثل "en" كبديل احتياطي، على الرغم من أن هذا يقلل من الدقة للنصوص غير الإنجليزية.
التعامل مع النصوص التي لا تحتوي على حدود للجمل
عندما لا يحتوي النص على علامات إنهاء الجمل، يُرجع المقسّم النص بالكامل كمقطع واحد.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Hello world"]
هذا السلوك صحيح لأن النص لا يحتوي على أي حدود جمل. لا يقوم المقسّم بتقسيم النص بشكل اصطناعي إذا كان يشكل جملة واحدة.
التعامل مع السلاسل النصية الفارغة
يتعامل المقسّم مع السلاسل النصية الفارغة من خلال إرجاع مكرر فارغ.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// []
ينتج عن هذا مصفوفة فارغة، وهي النتيجة المتوقعة للإدخال الفارغ.
إعادة استخدام المقسّمات لتحسين الأداء
إنشاء مقسّم له بعض التكلفة الإضافية. عندما تحتاج إلى تقسيم نصوص متعددة بنفس اللغة والخيارات، قم بإنشاء المقسّم مرة واحدة وأعد استخدامه.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const texts = [
"First text. With two sentences.",
"Second text. With three sentences. And more.",
"Third text."
];
texts.forEach(text => {
const sentences = Array.from(segmenter.segment(text), s => s.segment);
console.log(sentences);
});
// ["First text. ", "With two sentences."]
// ["Second text. ", "With three sentences. ", "And more."]
// ["Third text."]
إعادة استخدام المقسّم أكثر كفاءة من إنشاء مقسّم جديد لكل نص.
بناء دالة لعد الجمل
يمكنك استخدام المقسّم لعد الجمل في النص.
function countSentences(text, locale = "en") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
const segments = segmenter.segment(text);
return Array.from(segments).length;
}
console.log(countSentences("Hello world. How are you?"));
// 2
console.log(countSentences("Dr. Smith works at Acme Inc. He starts at 9 a.m."));
// 2
console.log(countSentences("Single sentence"));
// 1
console.log(countSentences("私は猫です。名前はまだない。", "ja"));
// 2
تقوم هذه الدالة بإنشاء مقسّم، وتقسيم النص، وإرجاع عدد المقاطع. تتعامل مع الاختصارات والحدود الخاصة باللغة بشكل صحيح.
بناء دالة لاستخراج الجمل
يمكنك إنشاء دالة تستخرج جملة محددة من النص حسب الفهرس.
function getSentence(text, index, locale = "en") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
const segments = Array.from(segmenter.segment(text), s => s.segment);
return segments[index] || null;
}
const text = "First sentence. Second sentence. Third sentence.";
console.log(getSentence(text, 0));
// "First sentence. "
console.log(getSentence(text, 1));
// "Second sentence. "
console.log(getSentence(text, 2));
// "Third sentence."
console.log(getSentence(text, 3));
// null
تُرجع هذه الدالة الجملة عند الفهرس المحدد، أو null إذا كان الفهرس خارج النطاق.
التحقق من دعم المتصفح وبيئة التشغيل
واجهة Intl.Segmenter متاحة في المتصفحات الحديثة وNode.js. أصبحت جزءاً من الأساس القياسي لمنصة الويب في أبريل 2024 ومدعومة في جميع محركات المتصفحات الرئيسية.
يمكنك التحقق من توفر الواجهة قبل استخدامها.
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you?";
const sentences = Array.from(segmenter.segment(text), s => s.segment);
console.log(sentences);
} else {
console.log("Intl.Segmenter is not supported");
}
بالنسبة للبيئات التي لا تدعمها، تحتاج إلى توفير بديل احتياطي. يستخدم البديل الاحتياطي البسيط تقسيماً أساسياً بتعبير نمطي، على الرغم من أن هذا يفقد دقة التقسيم الواعي للغة.
function splitSentences(text, locale = "en") {
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
return Array.from(segmenter.segment(text), s => s.segment);
}
// Fallback for older environments
return text.split(/[.!?]\s+/).filter(s => s.length > 0);
}
console.log(splitSentences("Hello world. How are you?"));
// ["Hello world. ", "How are you?"]
تستخدم هذه الدالة Intl.Segmenter عندما تكون متاحة وتعود إلى التقسيم بالتعبيرات النمطية في البيئات الأقدم. يفقد الحل البديل ميزات مثل معالجة الاختصارات واكتشاف الحدود الخاصة باللغة، ولكنه يوفر وظائف أساسية.