Aller au contenu

Césure

phil/typography

L'algorithme de Liang

Hyphenator implémente l'algorithme que Frank Liang a conçu pour TeX, écrit de zéro. Un motif est une chaîne de lettres avec des chiffres entre elles (.ach4, a1b, hy3ph), le point marquant le début ou la fin d'un mot. Chaque motif trouvé dans le mot, encadré de points, élève les chiffres entre ses lettres. Une coupure est permise là où le chiffre le plus élevé est impair, jamais plus près de l'une ou l'autre extrémité que les longueurs minimales de la langue, et jamais avant un signe combinant. Un mot listé parmi les exceptions (ta-ble, project) se coupe là où l'exception l'indique.

$hyphenator = Hyphenator::for('en_US');
$hyphenator->breaks('hyphenation');          // [2, 6]
$hyphenator->hyphenate('hyphenation', '-');  // hy-phen-ation
$hyphenator->hyphenate('hyphenation');       // with soft hyphens (U+00AD), the default

Un mot dont la forme en minuscules n'a pas autant de caractères que le mot lui-même (İstanbul) est laissé entier. Les longueurs minimales de la langue peuvent être remplacées : new Hyphenator($patterns, left: 3, right: 3) ; aucune n'est inférieure à un.

Langues

Dictionary trouve les motifs d'une locale par ses candidates et une table d'alias, ou accepte directement une étiquette de motifs (de-1901, la-x-classic, fi-x-school). La plupart des motifs sont ceux de hyph-utf8 (version 2026-02-21), les motifs de césure de TeX en UTF-8, importés pour chaque langue dont la licence les permet dans un paquet MIT. Chaque fichier est une copie intégrale, et data/LICENSES reproduit l'en-tête du fichier source avec son droit d'auteur et sa licence. Quand un fichier est offert sous plusieurs licences, c'est la licence permissive qui est utilisée. Sept langues dont les motifs TeX portent une licence réciproque ou n'en portent aucune ont des motifs écrits pour cette bibliothèque, sous sa licence MIT : voir Motifs écrits pour cette bibliothèque.

ÉtiquetteLangueLocalesGauche / droiteLicence utilisée
afAfrikaansaf1 / 2MIT
asAssamaisas1 / 1MIT
beBiélorussebe2 / 2MIT
bgBulgarebg2 / 2Avis de type BSD
caCatalanca2 / 2LPPL
copCoptecop1 / 1MIT
csTchèquecs2 / 2MIT, écrits pour cette bibliothèque
cuSlavon d'églisecu1 / 2MIT
cyGalloiscy2 / 3LPPL
daDanoisda2 / 2LPPL
de-1901Allemand, orthographe traditionnelle—2 / 2MIT
de-1996Allemand, orthographe réforméede2 / 2MIT
de-ch-1901Allemand, orthographe suisse traditionnellede_CH, de_LI2 / 2MIT
el-monotonGrec moderne, orthographe monotoniqueel1 / 1LPPL
el-polytonGrec moderne, orthographe polytonique—1 / 1LPPL
en-gbAnglais, orthographe britanniqueen_AU, en_GB, en_IE, en_IN, en_NZ, en_UK, en_ZA2 / 3MIT
en-usAnglais, orthographe américaineen2 / 3Avis tout permissif
eoEspérantoeo2 / 2LPPL
esEspagnoles2 / 2MIT/X11
etEstonienet2 / 3MIT
euBasqueeu2 / 2Licence de données de type Unicode
fiFinnoisfi2 / 2Avis de libre distribution
fi-x-schoolFinnois, césure scolaire—1 / 1MIT
frFrançaisfr2 / 2MIT
furFrioulanfur2 / 2MIT
gaIrlandaisga2 / 3MIT
glGaliciengl2 / 2Unlicence
grcGrec anciengrc1 / 1LPPL
guGoudjaratigu1 / 1MIT
hrCroatehr2 / 2LPPL
hsbHaut-sorabehsb2 / 2LPPL
huHongroishu2 / 2MIT, écrits pour cette bibliothèque
hyArménienhy2 / 2MIT, écrits pour cette bibliothèque ; expérimental, désactivé par défaut
iaInterlinguaia2 / 2LPPL
idIndonésienid2 / 2MIT, écrits pour cette bibliothèque
isIslandaisis2 / 2LPPL
itItalienit2 / 2LPPL
kaGéorgienka1 / 2MIT
kkKazakhkk2 / 2Domaine public
kmrKurmandji, kurde du Nordkmr, ku2 / 2LPPL
knKannadakn1 / 1MIT
laLatinla2 / 2MIT
la-x-classicLatin classique—2 / 2MIT
la-x-liturgicLatin liturgique—2 / 2MIT
ltLituanienlt2 / 2MIT
lvLettonlv2 / 2MIT, écrits pour cette bibliothèque
mkMacédonienmk2 / 2MIT, écrits pour cette bibliothèque ; expérimental, désactivé par défaut
mlMalayalamml1 / 1MIT
mn-cyrlMongol, écriture cyrilliquemn2 / 2LPPL
mrMarathimr1 / 1MIT
mul-ethiPlusieurs langues qui utilisent l'écriture éthiopienneam, ti1 / 1MIT
nlNéerlandaisnl2 / 2MIT
nnNorvégien nynorsknn, nb, no2 / 2Avis tout permissif
ocOccitanoc2 / 2MIT
orOdia, oriyaor1 / 1MIT
paPendjabipa1 / 1MIT
piPalipi1 / 2MIT
plPolonaispl2 / 2MIT
pmsPiémontaispms2 / 2MIT
ptPortugaispt2 / 3Licence BSD à 3 clauses
rmRomancherm2 / 2MIT
roRoumainro2 / 2MIT, écrits pour cette bibliothèque
ruRusseru2 / 2LPPL
saSanskritsa1 / 3Avis de libre utilisation
sh-cyrlSerbo-croate, écriture cyrilliquesh_Cyrl, sr1 / 2LPPL
sh-latnSerbo-croate, écriture latinebs, sh, sr_Latn2 / 2LPPL
skSlovaquesk2 / 3MIT
slSlovènesl2 / 2LPPL
sqAlbanaissq2 / 2MIT
svSuédoissv2 / 2LPPL
taTamoulta1 / 1MIT
teTélougoute1 / 1MIT
tkTurkmènetk2 / 2MIT
trTurctr2 / 2LPPL
ukUkrainienuk2 / 2MIT
zh-latn-pinyinChinois mandarin, translittération pinyinzh_Latn2 / 2MIT

de donne l'orthographe réformée, de_CH et de_LI l'orthographe suisse traditionnelle, de-1901 l'orthographe traditionnelle. el donne l'orthographe monotonique, el-polyton l'orthographe polytonique. Le norvégien bokmål et le nynorsk partagent les motifs de hyph-no, sous l'étiquette du nynorsk. Le serbe est lu par les motifs du serbo-croate, en cyrillique pour sr et en écriture latine pour sr_Latn.

Motifs écrits pour cette bibliothèque

Les motifs TeX du tchèque (GPL 2 ou ultérieure seulement), du hongrois (MPL 1.1, GPL 2 ou LGPL 2.1 seulement), de l'arménien (LGPL seulement), de l'indonésien (GPL seulement), du letton (LGPL 2.1 ou GPL 2 seulement), du macédonien (GPL seulement) et du roumain (aucune licence déclarée) ne peuvent pas être fournis dans un paquet MIT. Ces sept langues ont plutôt des motifs écrits de zéro, à partir des règles orthographiques publiées de chaque langue : aucun motif, aucune liste d'exceptions ni aucune liste de mots sous une autre licence n'a été lu pour les écrire. Les règles sont écrites sous forme de données dans tests/Generator/SyllableRules.php, tests/Generator/PatternGenerator.php les transforme en motifs de Liang, php bin/generate-patterns.php écrit les fichiers <tag>.pat.txt, et les tests vérifient que les fichiers fournis sont ceux que donnent les règles.

Ce sont des règles, pas un dictionnaire, moins complètes que les motifs TeX. Une règle de syllabation voit des lettres, pas des mots : elle ne peut pas distinguer un mot composé ou un préfixe d'une syllabe, ni une diphtongue de deux voyelles écrites de la même façon. Là où les règles d'une langue en dépendent, les motifs ne permettent aucune coupure plutôt qu'une mauvaise. Ils manquent donc des coupures que trouvent les motifs TeX, et un mot hors des règles peut encore se couper là où un dictionnaire ne le ferait pas. Chaque langue est testée sur une liste de mots courants qui doivent se couper exactement là où ses règles le permettent.

ÉtiquetteRègles suiviesCe que font les motifsLaissé sans coupure
csLes règles de coupure des mots de l'Institut de la langue tchèque de l'Académie des sciencesUne consonne entre deux voyelles passe à la ligne suivante ; dans un groupe, la dernière, ou une occlusive ou une fricative suivie de r, l ou ř (ses-tra) ; ch est une seule lettre ; un r ou un l à l'initiale d'un mot entre deux consonnes porte une syllabe (vl-na, Br-no)Les préfixes roz, bez, nad, pod et před devant une voyelle ; les groupes de d et d'une liquide, qu'un préfixe peut terminer
huLes règles orthographiques de l'Académie hongroise des sciencesUne consonne entre deux voyelles passe à la ligne suivante ; dans un groupe, seulement la dernière (temp-lom, prog-ram) ; deux voyelles côte à côte sont séparées (ki-a-dás) ; cs, dz, dzs, gy, ly, ny, sz, ty et zs sont des lettres simplesLes digrammes doublés, écrits en abrégé et rétablis à la coupure (asszony), ce qu'un trait d'union conditionnel ne peut pas faire ; les préfixes meg, fel et szét devant une voyelle ; les mots composés, là où sz et zs se rencontrent (egészség)
hyLes règles de coupure de ligne des grammaires scolaires arméniennes, dans l'orthographe réforméeUne consonne entre deux voyelles passe à la ligne suivante ; dans un groupe, la dernière, avec le յ qui suit une consonne (գի-տու-թյուն) ; ու est une seule voyelleLes coupures voisines de և ; les deux premières lettres d'un mot qui commence par deux consonnes, où la voyelle n'est pas écrite
idLes directives orthographiques générales de l'Agence de développement et de promotion de la langueUne consonne entre deux voyelles passe à la ligne suivante ; de deux, la seconde ; de trois ou plus, toutes sauf la première (in-stru-men) ; ng, ny, kh et sy sont des consonnes simples ; deux voyelles côte à côte sont séparées (sa-at)Les diphtongues ai, au, ei et oi, ainsi que eu et ou, même là où ce sont deux voyelles (main)
lvLes règles orthographiques de l'Académie lettone des sciencesUne consonne entre deux voyelles passe à la ligne suivante ; dans un groupe, la dernière (raks-tīt) ; dz et dž ne sont pas coupésLes diphtongues, o compris ; les préfixes aiz et pār devant une voyelle
mkL'orthographe de la langue littéraire macédonienneUne consonne entre deux voyelles passe à la ligne suivante ; dans un groupe, toutes sauf la première (сес-тра)Un р syllabique sans voyelle écrite (срце) ; les préfixes без, раз, под, над et пред devant une voyelle
roLe dictionnaire orthographique de l'Académie roumaineUne consonne entre deux voyelles passe à la ligne suivante ; de deux, la seconde, sauf une occlusive ou f, h, v devant l ou r (ta-blou) ; de trois ou plus, toutes sauf la première, sauf lpt, mpt, mpț, ncș, nct, ncț, ndv, rct, rtf et stm (sculp-tor) ; ch et gh sont des consonnes simplesLes voyelles côte à côte, diphtongue et hiatus s'écrivant de la même façon ; un i final après une consonne, qui n'est pas une syllabe (lupi, membri)

Langues expérimentales

Les règles de l'arménien et du macédonien sont les moins sûres des sept : elles attendent encore la lecture d'un locuteur de chaque langue. Leurs motifs sont fournis, mais ils sont expérimentaux et désactivés par défaut : un Dictionary ne les sert que lorsqu'il est construit avec leurs étiquettes, listées dans Dictionary::EXPERIMENTAL. Sans elles, hy et mk n'ont aucun motif : supports() vaut false, les préréglages retirent hyphen et patterns() lance UnsupportedLanguage.

$dictionary = new Dictionary(experimental: ['hy']);    // Armenian served, Macedonian still off
$dictionary = new Dictionary(experimental: Dictionary::EXPERIMENTAL);
Typographer::for('hy_AM', dictionary: $dictionary);

Une étiquette hors de Dictionary::EXPERIMENTAL donnée à cet endroit ne change rien : les autres langues sont toujours servies. Dans Symfony, phil/typography-bundle règle la liste avec experimental_hyphenation.

Une application qui accepte la licence des motifs TeX de l'une de ces langues et veut leur complétude les charge elle-même, comme le montre Vos propres motifs.

Coupures de mots du thaï

Le thaï s'écrit sans espace entre les mots et coupe une ligne entre deux mots sans trait d'union visible, ce qu'un trait d'union conditionnel ne peut pas exprimer. Son préréglage exécute le correcteur word_break au lieu de hyphen : WordBreak place une espace de largeur nulle (U+200B) à chaque occasion de coupure de ligne qu'ICU trouve dans une suite de caractères thaïs, par IntlBreakIterator et son dictionnaire thaï. Un navigateur sans dictionnaire propre coupe ainsi quand même la ligne entre deux mots. Le correcteur ne s'exécute que là où la césure est demandée (Typographer::for('th', hyphenation: false) le retire). ext-intl est déjà un prérequis de la bibliothèque, pour Normalizer : le correcteur n'a donc besoin de rien de plus. Un correcteur n'ajoute jamais de balisage : la coupure est donc un caractère, pas un élément <wbr>. Les deux se comportent de la même façon dans un navigateur.

Typographer::for('th_TH')->fixText('สวัสดีครับ'); // "สวัสดี\u{200B}ครับ"

Les coupures sont celles de l'ICU auquel la construction de PHP est liée, et peuvent varier légèrement d'une version d'ICU à l'autre.

Écartées

LanguePourquoi
Serbe, motifs cyrilliques propres (sr-cyrl)GPL seulement ; le serbe est servi par sh-cyrl et sh-latn
Zoulou (zu)Aucun motif dans hyph-utf8

Pour ces étiquettes, et pour le thaï, Dictionary::supports() vaut false, les préréglages retirent Hyphen, et Hyphenator::for() lance UnsupportedLanguage.

Vos propres motifs

new Dictionary($directory) lit <tag>.pat.txt et <tag>.hyp.txt dans un autre répertoire, pour les mêmes étiquettes. Un fichier d'exceptions absent signifie qu'il n'y en a aucune, et supports() ne vaut true que lorsque le fichier de motifs est présent. Les motifs peuvent aussi être donnés directement :

$patterns = Patterns::parse(file_get_contents('hyph-cs.pat.txt'), file_get_contents('hyph-cs.hyp.txt'), 2, 3);
$hyphenator = new Hyphenator($patterns);

C'est ainsi qu'une application utilise des motifs sous une licence que cette bibliothèque ne fournit pas, sous sa propre responsabilité.

Performance

Un dictionnaire lit les motifs d'une langue une fois et les garde : les motifs allemands, les plus volumineux, prennent environ quarante millisecondes à lire. Typographers partage un seul dictionnaire entre ses typographes ; un processus de longue durée devrait garder ses typographes plutôt que de les construire à chaque requête.