Comment diviser un texte en phrases ?
Utilisez Intl.Segmenter pour diviser un texte en phrases avec une détection des limites tenant compte de la locale, gérant la ponctuation, les abréviations et les règles spécifiques à chaque langue.
Introduction
Lorsque vous traitez du texte pour la traduction, l'analyse ou l'affichage, vous devez souvent le diviser en phrases individuelles. Une approche naïve utilisant des expressions régulières échoue car les limites de phrases sont plus complexes que des points suivis d'espaces. Les phrases peuvent se terminer par des points d'interrogation, des points d'exclamation ou des points de suspension. Les points apparaissent dans les abréviations comme "Dr." ou "Inc." sans terminer les phrases. Différentes langues utilisent différents signes de ponctuation comme terminateurs de phrases.
L'API Intl.Segmenter résout ce problème en fournissant une détection des limites de phrases tenant compte de la locale. Elle comprend les règles d'identification des limites de phrases dans différentes langues et gère automatiquement les cas particuliers comme les abréviations, les nombres et la ponctuation complexe.
Le problème de la division sur les points
Vous pouvez essayer de diviser le texte en phrases en le divisant sur les points suivis d'espaces.
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."]
Cette approche présente plusieurs problèmes. Premièrement, elle ne gère pas les points d'interrogation ou d'exclamation. Deuxièmement, elle se casse sur les abréviations contenant des points. Troisièmement, elle supprime le point de chaque phrase sauf la dernière. Quatrièmement, elle ne fonctionne pas lorsqu'il y a plusieurs espaces après les points.
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."]
Le texte se divise incorrectement à "Dr." et "Inc." car ces abréviations contiennent des points. Vous avez besoin d'une approche plus intelligente qui comprend les règles de limites de phrases.
Utiliser une expression régulière plus complexe
Vous pouvez améliorer l'expression régulière pour gérer plus de cas.
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", ""]
Cela divise sur les points, les points d'interrogation et les points d'exclamation suivis d'espaces. Cela gère plus de cas mais échoue toujours avec les abréviations et crée des chaînes vides. Cela supprime également la ponctuation de chaque phrase.
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", ""]
L'approche par regex ne peut pas distinguer de manière fiable les points qui terminent des phrases et les points qui apparaissent dans les abréviations. Construire une regex complète qui gère tous les cas limites devient impraticable. Vous avez besoin d'une solution qui comprend les règles linguistiques.
Utiliser Intl.Segmenter pour diviser en phrases
Le constructeur Intl.Segmenter crée un segmenteur qui divise le texte selon des règles spécifiques à la locale. Vous spécifiez une locale et définissez l'option granularity sur "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!"
La méthode segment() retourne un itérable qui produit des objets segment. Chaque objet segment possède une propriété segment contenant le texte de ce segment. Le segmenteur préserve la ponctuation et les espaces à la fin de chaque phrase.
Vous pouvez convertir les segments en tableau en utilisant 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!"]
Cela crée un tableau où chaque élément est une phrase avec sa ponctuation et son espacement d'origine.
Comment Intl.Segmenter gère les abréviations
Le segmenteur comprend les modèles d'abréviation courants et ne divise pas sur les points qui apparaissent dans les abréviations.
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."]
Le texte se divise correctement en deux phrases. Les points dans "Dr.", "Inc." et "a.m." ne déclenchent pas de ruptures de phrase car le segmenteur reconnaît ces éléments comme des abréviations. Cette gestion automatique des cas limites explique pourquoi Intl.Segmenter est supérieur aux approches par regex.
Supprimer les espaces des phrases
Le segmenteur inclut les espaces de fin dans chaque phrase. Vous pouvez supprimer ces espaces si nécessaire.
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!"]
La méthode trim() supprime les espaces de début et de fin de chaque phrase. Cela est utile lorsque vous avez besoin de limites de phrases nettes sans espacement supplémentaire.
Obtenir les métadonnées de segment
Chaque objet segment inclut des métadonnées sur la position du segment dans le texte original.
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?" }
La propriété index indique où le segment commence dans le texte original. La propriété input contient le texte original complet. Ces métadonnées sont utiles lorsque vous devez suivre les positions des phrases ou reconstruire le texte original.
Diviser les phrases dans différentes langues
Différentes langues ont différentes règles de limites de phrases. Le segmenteur adapte son comportement en fonction de la locale spécifiée.
En japonais, les phrases peuvent se terminer par un point pleine chasse 。 appelé kuten.
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);
// ["私は猫です。", "名前はまだない。"]
Le texte se divise correctement aux terminateurs de phrases japonais. Un segmenteur configuré pour l'anglais ne reconnaîtrait pas correctement ces limites.
En hindi, les phrases peuvent se terminer par une barre verticale । appelée purna viram.
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);
// ["यह एक वाक्य है। ", "यह दूसरा वाक्य है।"]
Le segmenteur reconnaît le point devanagari comme une limite de phrase. Ce comportement sensible à la locale est essentiel pour le traitement de texte internationalisé.
Utiliser la locale correcte pour le texte multilingue
Lorsque vous traitez du texte contenant plusieurs langues, choisissez la locale qui correspond à la langue principale du texte. Le segmenteur utilise la locale spécifiée pour déterminer quelles règles de limites appliquer.
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);
// ["私は猫です。", "名前はまだない。"]
Créer des segmenteurs séparés pour chaque langue garantit une détection correcte des limites. Si vous traitez du texte dont la langue est inconnue, vous pouvez utiliser une locale générique comme "en" en solution de repli, bien que cela réduise la précision pour le texte non anglais.
Gérer le texte sans limites de phrases
Lorsque le texte ne contient aucun terminateur de phrase, le segmenteur renvoie le texte entier comme un seul segment.
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"]
Ce comportement est correct car le texte ne contient aucune limite de phrase. Le segmenteur ne divise pas artificiellement un texte qui forme une seule phrase.
Gestion des chaînes vides
Le segmenteur gère les chaînes vides en renvoyant un itérateur vide.
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);
// []
Cela produit un tableau vide, ce qui est le résultat attendu pour une entrée vide.
Réutilisation des segmenteurs pour de meilleures performances
La création d'un segmenteur entraîne une certaine surcharge. Lorsque vous devez segmenter plusieurs textes avec les mêmes paramètres régionaux et options, créez le segmenteur une fois et réutilisez-le.
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."]
Réutiliser le segmenteur est plus efficace que d'en créer un nouveau pour chaque texte.
Création d'une fonction de comptage de phrases
Vous pouvez utiliser le segmenteur pour compter les phrases dans un texte.
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
Cette fonction crée un segmenteur, divise le texte et renvoie le nombre de segments. Elle gère correctement les abréviations et les limites spécifiques à la langue.
Création d'une fonction d'extraction de phrase
Vous pouvez créer une fonction qui extrait une phrase spécifique d'un texte par index.
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
Cette fonction renvoie la phrase à l'index spécifié, ou null si l'index est hors limites.
Vérification de la prise en charge du navigateur et de l'environnement d'exécution
L'API Intl.Segmenter est disponible dans les navigateurs modernes et Node.js. Elle fait partie de la base de la plateforme web depuis avril 2024 et est prise en charge par tous les principaux moteurs de navigateur.
Vous pouvez vérifier si l'API est disponible avant de l'utiliser.
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");
}
Pour les environnements sans prise en charge, vous devez fournir une solution de repli. Une solution de repli simple utilise une division regex basique, bien que cela perde la précision de la segmentation tenant compte des paramètres régionaux.
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?"]
Cette fonction utilise Intl.Segmenter lorsqu'il est disponible et se replie sur la division par regex dans les environnements plus anciens. La solution de repli perd des fonctionnalités telles que la gestion des abréviations et la détection des limites spécifiques à la langue, mais fournit une fonctionnalité de base.