@echogarden/text-segmentation 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -25
- package/dist/EastAsianCharacterPatterns.d.ts +0 -6
- package/dist/EastAsianCharacterPatterns.js +6 -6
- package/dist/EastAsianCharacterPatterns.js.map +1 -1
- package/dist/Patterns.d.ts +4 -29
- package/dist/Patterns.js +137 -61
- package/dist/Patterns.js.map +1 -1
- package/dist/Suppressions.d.ts +1 -0
- package/dist/Suppressions.js +19 -9
- package/dist/Suppressions.js.map +1 -1
- package/dist/Test.js +48 -5
- package/dist/Test.js.map +1 -1
- package/dist/TextSegmentation.d.ts +3 -4
- package/dist/TextSegmentation.js +68 -39
- package/dist/TextSegmentation.js.map +1 -1
- package/dist/WordSequence.d.ts +5 -3
- package/dist/WordSequence.js +13 -6
- package/dist/WordSequence.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -0
- package/dist/utilities/Utilities.js +7 -0
- package/dist/utilities/Utilities.js.map +1 -1
- package/package.json +4 -4
- package/src/EastAsianCharacterPatterns.ts +6 -6
- package/src/Patterns.ts +180 -87
- package/src/Suppressions.ts +20 -9
- package/src/Test.ts +55 -5
- package/src/TextSegmentation.ts +91 -51
- package/src/WordSequence.ts +17 -7
- package/src/utilities/Utilities.ts +10 -0
package/src/Suppressions.ts
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
export const cldrSuppressions: Record<string, string[]> = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
//en: ['L.P.', 'Alt.', 'Approx.', 'E.G.', 'O.', 'Maj.', 'Misc.', 'P.O.', 'J.D.', 'Jam.', 'Card.', 'Dec.', 'Sept.', 'MR.', 'Long.', 'Hat.', 'G.', 'Link.', 'DC.', 'D.C.', 'M.T.', 'Hz.', 'Mrs.', 'By.', 'Act.', 'Var.', 'N.V.', 'Aug.', 'B.', 'S.A.', 'Up.', 'Job.', 'Num.', 'M.I.T.', 'Ok.', 'Org.', 'Ex.', 'Cont.', 'U.', 'Mart.', 'Fn.', 'Abs.', 'Lt.', 'OK.', 'Z.', 'E.', 'Kb.', 'Est.', 'A.M.', 'L.A.', 'Prof.', 'U.S.', 'Nov.', 'Ph.D.', 'Mar.', 'I.T.', 'exec.', 'Jan.', 'N.Y.', 'X.', 'Md.', 'Op.', 'vs.', 'D.A.', 'A.D.', 'R.L.', 'P.M.', 'Or.', 'M.R.', 'Cap.', 'PC.', 'Feb.', 'Exec.', 'I.e.', 'Sep.', 'Gb.', 'K.', 'U.S.C.', 'Mt.', 'S.', 'A.S.', 'C.O.D.', 'Capt.', 'Col.', 'In.', 'C.F.', 'Adj.', 'AD.', 'I.D.', 'Mgr.', 'R.T.', 'B.V.', 'M.', 'Conn.', 'Yr.', 'Rev.', 'Phys.', 'pp.', 'Ms.', 'To.', 'Sgt.', 'J.K.', 'Nr.', 'Jun.', 'Fri.', 'S.A.R.', 'Lev.', 'Lt.Cdr.', 'Def.', 'F.', 'Do.', 'Joe.', 'Id.', 'Mr.', 'Dept.', 'Is.', 'Pvt.', 'Diff.', 'Hon.B.A.', 'Q.', 'Mb.', 'On.', 'Min.', 'J.B.', 'Ed.', 'AB.', 'A.', 'S.p.A.', 'I.', 'a.m.', 'Comm.', 'Go.', 'VS.', 'L.', 'All.', 'PP.', 'P.V.', 'T.', 'K.R.', 'Etc.', 'D.', 'Adv.', 'Lib.', 'E.g.', 'Pro.', 'U.S.A.', 'S.E.', 'AA.', 'Rep.', 'Sq.', 'As.'],
|
|
3
|
+
en: ['L.P.', 'Alt.', 'Approx.', 'E.G.', 'Maj.', 'Misc.', 'P.O.', 'J.D.', 'Dec.', 'Sept.', 'MR.', 'DC.', 'D.C.', 'M.T.', 'Mrs.', 'N.V.', 'Aug.', 'S.A.', 'Num.', 'M.I.T.', 'Org.', 'Ex.', 'Cont.', 'Mart.', 'Fn.', 'Abs.', 'Lt.', 'Est.', 'A.M.', 'L.A.', 'Prof.', 'U.S.', 'Nov.', 'Ph.D.', 'Mar.', 'I.T.', 'exec.', 'Jan.', 'N.Y.', 'Md.', 'Op.', 'vs.', 'D.A.', 'A.D.', 'R.L.', 'P.M.', 'M.R.', 'Feb.', 'Exec.', 'I.e.', 'Sep.', 'U.S.C.', 'Mt.', 'A.S.', 'C.O.D.', 'Capt.', 'Col.', 'C.F.', 'Adj.', 'I.D.', 'Mgr.', 'R.T.', 'B.V.', 'M.', 'Conn.', 'Yr.', 'Rev.', 'Phys.', 'pp.', 'Ms.', 'Sgt.', 'J.K.', 'Nr.', 'Jun.', 'Fri.', 'S.A.R.', 'Lev.', 'Lt.Cdr.', 'Def.', 'Mr.', 'Dept.', 'Pvt.', 'Diff.', 'Hon.B.A.', 'Mb.', 'Min.', 'J.B.', 'Ed.', 'AB.', 'S.p.A.', 'I.', 'a.m.', 'Comm.', 'VS.', 'PP.', 'P.V.', 'K.R.', 'Etc.', 'Adv.', 'Lib.', 'E.g.', 'U.S.A.', 'S.E.', 'AA.', 'Rep.', 'Sq.'],
|
|
4
|
+
de: ['Port.', 'Alt.', 'Di.', 'Ges.', 'frz.', 'entspr.', 'Gebr.', 'erw.', 'Frl.', 'Inh.', 'k.u.k.', 'Ca.', 'J.D.', 'Ausg.', 'evtl.', 'So.', 'i.B.', 's.a.', 'kgl.', 'Sept.', 'o.B.', 'Sa.', 'ev.', 'Dez.', 'am.', 'i.R.', 'eigtl.', 'i.J.', 'u.U.', 'G.', 'z.Hd.', 'u.A.w.g.', 'Kl.', 'Spezif.', 'Obj.', 'Ing.', 'D. h.', 'Folg.', 'Akt.', 'i.A.', 'Msp.', 'U.U.', 'Chr.', 'R.', 'Einh.', 'schwäb.', 'Vgl.', 'Aug.', 'Dipl.-Ing.', 'W.', 'B.', 'U. U.', 'J.', 'Fa.', 'Mo.', 'n.u.Z.', 'Op.', 'Mrd.', 'e.h.', 'Hr.', 'Hrn.', 'Ztr.', 'k. u. k.', 'Bibl.', 'd.Ä.', 'b.', 'M.', 'i.H.', 'v.R.w.', 'o.A.', 'St.', 'Dr.', 'Fn.', 'Abs.', 'Rd.', 'Dtzd.', 'Jahrh.', 'Z.', 'Std.', 'n. Chr.', 'möbl.', 'tägl.', 'gest.', 'gesch.', 'z.B.', 'Hbf.', 'Abt.', 'A.M.', 'e.Wz.', 'v.T.', 'Nov.', 'z.', 'Prot.', 'U.S.', 'Wg.', 'u.v.a.', 'Adr.', 'App.', 'ggf.', 'ggfs.', 'Jan.', 'O.', 'Rel.', 'od.', 'Pfd.', 'a.a.O.', 'p.Adr.', 'P.', 'Gem.', 'v. Chr.', 'Art.', 'z.Z.', 'S.A.', 'i.V.', 'verh.', 'Ausschl.', 'm.W.', 'Dir.', 'Verf.', 'Sek.', 'r.', 'Chin.', 'Feb.', 'Int.', 'Sep.', 'Gesch.', 'schweiz.', 'Bed.', 'a.Rh.', 'jew.', 'vgl.', 'a.M.', 'Str.', 'exkl.', 'gek.', 'Erf.', 'u.Ä.', 'ehem.', 'näml.', 'u. Z.', 'v. u. Z.', 'sog.', 'C.', 'Dipl.-Kfm.', 'mtl.', 'Hrsg.', 'Qu.', 'röm.', 'u.', 'U.', 'Adj.', 'Kap.', 'hpts.', 'a.D.', 'gedr.', 'Best.', 'N.', 'v.u.Z.', 'Phys.', 'Fr.', 'd.J.', 'Reg.-Bez.', 'm.E.', 'schles.', 'Max.', 'Ltd.', 'südd.', 'inkl.', 'geb.', 'Ggf.', 'Inc.', 'kath.', 'kfm.', 'Nr.', 'Proz.', 'Dim.', 'verw.', 'Reg.', 'Dat.', 'Evtl.', 'led.', 'F.', 'Test.', 'Schr.', 'Do.', 'PIN.', 'Z. Zt.', 'v.Chr.', 'Tägl.', 's.', 'amtl.', 'Temp.', 'Mind.', 'e.V.', 'Abw.', 'P.M.', 'F.f.', 'a.a.S.', 'Mod.', 'Co.', 'Min.', 'Allg.', 'Geograph.', 'Jr.', 'Urspr.', 'Apr.', 'Z. B.', 'v.H.', 'A.', 'einschl.', 'Trans.', 'zzgl.', 'StR.', 'Fam.', 'I.', 'jhrl.', 'u.a.', 'Ben.', 'o.g.', 'Kfm.', 'Konv.', 'Mi.', 'L.', 'beil.', 'T.', 'Ursprüngl.', 'röm.-kath.', 'Okt.', 'u.ä.', 'Tel.', 'D.', 'Ber.', 'Kop.', 'Mio.', 'Y.', 'U.S.A.', 'v. H.', 'Forts. f.', 'Rep.', 'Hptst.', 'österr.'],
|
|
5
|
+
es: ['Rdos.', 'JJ.OO.', 'Sres.', 'fig.', 'may.', 'RR.HH.', 'oct.', 'cap.', 'mié.', 'doc.', 'Excmo.', 'Trab.', 'Excmos.', 'Kit.', 'Inc.', 'FF.CC.', 'DC.', 'ago.', 'trad.', 'SA.', 'Rvdos.', 'ed.', 'Exmo.', 'jul.', 'col.', 'RAM.', 'Srtas.', 'ene.', 'Rol.', 'Fabric.', 'Comm.', 'vid.', 'Da.', 'dic.', 'ss.', 'abr.', 'ntra.', 'Sra.', 'dtor.', 'cf.', 'dom.', 'prov.', 'Emm.', 'Sr.', 'licdo.', 'p.ej.', 'bol.', 'figs.', 'Vda.', 'Dr.', 'ntro.', 'Desv.', 'O.M.', 'Ldo.', 'Drs.', 'sáb.', 'feb.', 'Ltda.', 'Lcda.', 'Exma.', 'C.V.', 'SS.MM.', 'Lda.', 'U.S.', 'hnos.', 'R.D.', 'Korn.', 'v.gr.', 'vs.', 'Ilmas.', 'Rdo.', 'ej.', 'vie.', 'jue.', 'a. C.', 'Ilmos.', 'e. c.', 'Excma.', 'afma.', 'licda.', 'Em.', 'K.', 'sras.', 'MM.', 'fund.', 'Mons.', 'Lcdo.', 'afmo.', 'C.', 'A.C.', 'dptos.', 'Col.', 'Srta.', 'Av.', 'Ant.', 'depto.', 'Var.', 'H.P.', 'D.', 'M.', 'C.P.', 'Rev.', 'Rvdmos.', 'Fr.', 'Ilmo.', 'afmos.', 'Ltd.', 'afmas.', 'prof.', 'lun.', 'SS.AA.', 'Sol.', 'nov.', 'mss.', 'Dña.', 'Seg.', 'mar.', 'Rvdmo.', 'Reg.', 'ms.', 'Sras.', 'sres.', 'U.S.A.', 'Sta.', 'Sdad.', 'Dra.', 'srs.', 'R.U.', 'deptos.', 'dpto.', 'jun.', 'bco.', 'Cía.', 'Id.', 'Mr.', 'e.g.', 'C.S.', 'Excmas.', 'Dª.', 'Rvdo.', 'Lic.', 'cfr.', 'Corp.', 'Dto.', 'Ilma.', 'L.', 'All.', 'PP.', 'd. C.', 'Ltdo.', 'mtro.', 'Mrs.', 'Desc.', 'Avda.', 'Exmas.', 'a. e. c.', 'Bien.', 'Exmos.', 'AA.', 'Sto.', 'CA.', 'sept.', 'Exc.', 'c/c.'],
|
|
6
|
+
fr: ['aux.', 'config.', 'collab.', 'M.', 'dim.', 'imprim.', 'oct.', 'syst.', 'bull.', 'MM.', 'doc.', 'P.O.', 'hôp.', 'Mart.', 'juil.', 'broch.', 'adr.', 'symb.', 'C.', 'anc.', 'voit.', 'Jr.', 'graph.', 'dir.', 'éd.', 'fig.', 'édit.', 'niv.', 'quart.', 'cam.', 'éval.', 'anon.', 'réf.', 'Comm.', 'Prof.', 'févr.', 'indus.', 'DC.', 'équiv.', 'illustr.', 'acoust.', 'nov.', 'L.', 'All.', 'U.S.', 'S.M.A.R.T.', 'sept.', 'avr.', 'jeu.', 'dest.', 'P.-D. G.', 'ill.', 'coll.', 'encycl.', 'mer.', 'Desc.', 'ven.', 'P.', 'lun.', 'Inc.', 'sam.', 'D.', 'append.', 'Var.', 'categ.', 'janv.', 'S.A.', 'imm.', 'U.S.A.', 'mar.', 'exempl.', 'déc.', 'ann.', 'U.', 'synth.', 'dict.', 'av. J.-C.', 'W.', 'Op.', 'ap. J.-C.', 'gouv.', 'trav. publ.'],
|
|
7
|
+
it: ['N.B.', 'div.', 'a.C.', 'fig.', 'd.p.R.', 'c.c.p.', 'Cfr.', 'vol.', 'Geom.', 'O.d.G.', 'S.p.A.', 'ver.', 'N.d.A.', 'dott.', 'arch.', 'd.C.', 'N.d.T.', 'rag.', 'Sig.', 'Mod.', 'pag.', 'dr.', 'tav.', 'N.d.E.', 'DC.', 'mitt.', 'Ing.', 'int.', 'on.', 'C.P.', 'ag.', 'L.', 'U.S.', 'S.M.A.R.T.', 'p.i.', 'tab.', 'Ltd.', 'Liv.', 'D.', 'U.S.A.', 'sez.', 'avv.', 'S.A.R.', 'all.', 'p.'],
|
|
8
|
+
pt: ['psicol.', 'fig.', 'compl.', 'rep.', 'cap.', 'doc.', 'fisiol.', 'dipl.', 'astron.', 'port.', 'eletrôn.', 'geom.', 'mov.', 'ago.', 'trad.', 'arquit.', 'dez.', 'ed.', 'apt.', 'Exmo.', 'col.', 'ff.', 'univ.', 'res.', 'R.', 'transp.', 'D.C', 'l.', 'des.', 'fev.', 'abr.', 'liter.', 'lat.', 'Dir.', 'cf.', 'adm.', 'fot.', 'p.m.', 'P.M.', 'créd.', 'jur.', 'com.', 'anat.', 'dir.', 'end.', 'fís.', 'E.', 'Est.', 'cont.', 'matem.', 'Drs.', 'gên.', 'neol.', 'pág.', 'índ.', 'Ltda.', 'Exma.', 'esp.', 'ingl.', 'tecnol.', 'Mar.', 'símb.', 'Pe.', 'pal.', 'filos.', 'V.T.', 'fasc.', 'vs.', 'mai.', 'S.A.', 'profa.', 'N.Sra.', 'r.s.v.p.', 'cel.', 'mat.', 'abrev.', 'out.', 'long.', 'aux.', 'arit.', 'aer.', 'jul.', 'lin.', 'S.', 'méd.', 'odontol.', 'org.', 'A.C.', 'jun.', 'déb.', 'Av.', 'álg.', 'sup.', 'fl.', 'odont.', 'caps.', 'relat.', 'organiz.', 'hist.', 'Fr.', 'Ilmo.', 'fem.', 'ap.', 'Ltd.', 'pol.', 'séc.', 'prof.', 'cx.', 'nov.', 'quím.', 'mús.', 'agric.', 'mar.', 'W.C.', 'fr.', 'cat.', 'jan.', 'pron.', 'rel.', 'autom.', 'Sta.', 'Dra.', 'p.', 'tel.', 'div.', 'p. ex.', 'a.C.', 'bras.', 'Alm.', 'Dr.', 'comp.', 'pq.', 'arqueol.', 'náut.', 'biogr.', 'f.', 'círc.', 'fac.', 'd.C.', 'apart.', 'ex.', 'Jr.', 'set.', 'tec.', 'sociol.', 'gram.', 'ind.', 'Ilma.', 'vol.', 'eng.', 'rod.', 'Ph.D.', 'Dras.', 'pp.', 'elem.', 'máq.', 'cód.', 'eletr.', 'prod.', 'ref.', 'fil.', 'a.m.', 'A.M', 'obs.', 'N.T.', 'contab.', 'Sto.', 'lit.', 'educ.', 'rementente', 'desc.', 'próx.'],
|
|
9
|
+
ru: ['руб.', 'янв.', 'до н. э.', 'сент.', 'тел.', 'дек.', 'февр.', 'нояб.', 'апр.', 'н. э.', 'окт.', 'тыс.', 'авг.', 'проф.', 'н.э.', 'кв.', 'ул.', 'отд.'],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const additionalSuppressions: Record<string, string[] > = {
|
|
13
|
+
en: [
|
|
14
|
+
'TL;DR'
|
|
15
|
+
],
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
export const leadingApostropheContractionSuppressions: Record<string, string[]> = {
|
|
12
|
-
'en': [
|
|
13
|
-
|
|
19
|
+
'en': [
|
|
20
|
+
`'cause`, `'til`, `'bout`, `'twas`, `'tis`
|
|
21
|
+
],
|
|
22
|
+
'af': [
|
|
23
|
+
`'n`
|
|
24
|
+
]
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
export const nounSuppressions = [
|
package/src/Test.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { addMissingPunctuationWordsToWordSequence, SegmentationResult, segmentText, splitToWords, WordSequence } from "./TextSegmentation.js"
|
|
1
|
+
import { addMissingPunctuationWordsToWordSequence, SegmentationOptions, SegmentationResult, segmentText, segmentWordSequence, splitToWords, WordSequence } from "./TextSegmentation.js"
|
|
2
2
|
import { Timer } from "./utilities/Timer.js"
|
|
3
3
|
|
|
4
4
|
const log = console.log
|
|
5
5
|
|
|
6
6
|
async function test1() {
|
|
7
7
|
{
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
//const text = 'Hello! 1. Say who? 2. How are you? This is v2.0 that good. I have 2 344 234ms C# is good games'
|
|
9
|
+
const text = 'The time 🇬🇧 is 06:12. Hello 😄 ! How 🎉 are 👨👩👧👦 you?'
|
|
10
|
+
//const text = 'Hello world! 23rd? 123% 3/4/7 Привет мир! 你好世界!'
|
|
11
|
+
|
|
12
|
+
const options: SegmentationOptions = {
|
|
13
|
+
language: 'en'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const wordSequence = await splitToWords(text, options)
|
|
17
|
+
|
|
18
|
+
console.log(JSON.stringify(wordSequence.wordArray))
|
|
19
|
+
|
|
20
|
+
const segmentedText = await segmentWordSequence(wordSequence)
|
|
21
|
+
|
|
22
|
+
const x = 0
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
const { readFileSync, writeFileSync } = await import('fs')
|
|
@@ -16,7 +29,7 @@ async function test1() {
|
|
|
16
29
|
|
|
17
30
|
let result: SegmentationResult
|
|
18
31
|
|
|
19
|
-
for (let i = 0; i <
|
|
32
|
+
for (let i = 0; i < 5; i++) {
|
|
20
33
|
result = await segmentText(text, {
|
|
21
34
|
language: 'en',
|
|
22
35
|
customSuppressions: [],
|
|
@@ -39,7 +52,7 @@ async function test1() {
|
|
|
39
52
|
for (let phraseIndex = 0; phraseIndex < phrases.length; phraseIndex++) {
|
|
40
53
|
const phrase = phrases[phraseIndex]
|
|
41
54
|
|
|
42
|
-
segmentedText += phrase.
|
|
55
|
+
segmentedText += phrase.words.wordArray.join(' | ')
|
|
43
56
|
|
|
44
57
|
if (phraseIndex < phrases.length - 1) {
|
|
45
58
|
segmentedText += `\n${'-'.repeat(100)}\n`
|
|
@@ -68,5 +81,42 @@ async function test2() {
|
|
|
68
81
|
const x = 0
|
|
69
82
|
}
|
|
70
83
|
|
|
84
|
+
async function test3() {
|
|
85
|
+
{
|
|
86
|
+
const text = `
|
|
87
|
+
Hello 12/43 yo good-go bobo_baba man!
|
|
88
|
+
2.4a, 5.6 x&y x'v·y v‧z x·y·5
|
|
89
|
+
abc123 23+42.534 645
|
|
90
|
+
年代主演兩123部電影系列後
|
|
91
|
+
2004年-12月,公园被国家旅游局评定为国家4A级旅游景区.
|
|
92
|
+
ah'f.bf5.c.d.
|
|
93
|
+
-345.45%
|
|
94
|
+
|
|
95
|
+
Hello 1/ how are you?
|
|
96
|
+
76.54af's567
|
|
97
|
+
in an 8.2x8x3 grid
|
|
98
|
+
|
|
99
|
+
5343.234$
|
|
100
|
+
|
|
101
|
+
Hello World. How are you?
|
|
102
|
+
|
|
103
|
+
x·y·z
|
|
104
|
+
756.534-54
|
|
105
|
+
|
|
106
|
+
This is 23 GB.
|
|
107
|
+
|
|
108
|
+
That’s great if you want to cram as many of your friends’ genomes 'cause that's not.
|
|
109
|
+
|
|
110
|
+
I like C# and C++ languages!
|
|
111
|
+
|
|
112
|
+
Price is $60 or 60$
|
|
113
|
+
|
|
114
|
+
`
|
|
115
|
+
|
|
116
|
+
const result = (await splitToWords(text)).wordArray
|
|
117
|
+
log(result.join(' | '))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
71
121
|
test1()
|
|
72
122
|
//test2()
|
package/src/TextSegmentation.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { cldrSuppressions, leadingApostropheContractionSuppressions, nounSuppressions, tldSuppressions } from './Suppressions.js'
|
|
1
|
+
import { buildWordOrNumberPattern as buildWordSplitterPattern, phraseSeparatorRegExp, sentenceSeparatorTrailingPunctuationRegExp, sentenceSeparatorRegExp, whitespacePatternRegExp, letterPatternGlobalRegExp } from './Patterns.js'
|
|
2
|
+
import { cldrSuppressions, additionalSuppressions, leadingApostropheContractionSuppressions, nounSuppressions, tldSuppressions } from './Suppressions.js'
|
|
4
3
|
import { eastAsianCharRangesRegExp } from './EastAsianCharacterPatterns.js'
|
|
5
4
|
import { WordSequence } from './WordSequence.js'
|
|
6
|
-
|
|
7
|
-
export { cldrSuppressions } from './Suppressions.js'
|
|
5
|
+
import { getShortLanguageCode } from './utilities/Utilities.js'
|
|
8
6
|
export { WordSequence, type WordEntry } from './WordSequence.js'
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
options = { ...defaultSegmentationOptions, ...(options ?? {}) }
|
|
8
|
+
import { buildRegExp } from 'regexp-composer'
|
|
12
9
|
|
|
10
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Exported methods
|
|
12
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
export async function segmentText(text: string, options?: SegmentationOptions) {
|
|
13
14
|
const wordSequence = await splitToWords(text, options)
|
|
14
15
|
|
|
15
16
|
return segmentWordSequence(wordSequence)
|
|
@@ -18,12 +19,27 @@ export async function segmentText(text: string, options?: SegmentationOptions) {
|
|
|
18
19
|
export async function segmentWordSequence(wordSequence: WordSequence) {
|
|
19
20
|
const sentenceWordRanges: Range[] = []
|
|
20
21
|
|
|
22
|
+
const minimumSentenceLetterCount = 2
|
|
23
|
+
|
|
21
24
|
let sentenceStartWordOffset = 0
|
|
25
|
+
let currentSentenceLetterCount = 0
|
|
22
26
|
|
|
23
27
|
for (let wordIndex = 0; wordIndex < wordSequence.length; wordIndex++) {
|
|
24
28
|
const word = wordSequence.getWordAt(wordIndex)
|
|
25
29
|
|
|
26
|
-
if (
|
|
30
|
+
if (currentSentenceLetterCount < minimumSentenceLetterCount) {
|
|
31
|
+
const matches = word.matchAll(letterPatternGlobalRegExp)
|
|
32
|
+
|
|
33
|
+
for (const match of matches) {
|
|
34
|
+
currentSentenceLetterCount += 1
|
|
35
|
+
|
|
36
|
+
if (currentSentenceLetterCount >= minimumSentenceLetterCount) {
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (currentSentenceLetterCount >= minimumSentenceLetterCount && sentenceSeparatorRegExp.test(word)) {
|
|
27
43
|
while (wordIndex < wordSequence.length - 1) {
|
|
28
44
|
const nextWord = wordSequence.getWordAt(wordIndex + 1)
|
|
29
45
|
|
|
@@ -40,6 +56,7 @@ export async function segmentWordSequence(wordSequence: WordSequence) {
|
|
|
40
56
|
})
|
|
41
57
|
|
|
42
58
|
sentenceStartWordOffset = wordIndex + 1
|
|
59
|
+
currentSentenceLetterCount = 0
|
|
43
60
|
}
|
|
44
61
|
}
|
|
45
62
|
|
|
@@ -106,7 +123,7 @@ export async function segmentWordSequence(wordSequence: WordSequence) {
|
|
|
106
123
|
}
|
|
107
124
|
|
|
108
125
|
const result: SegmentationResult = {
|
|
109
|
-
wordSequence,
|
|
126
|
+
words: wordSequence,
|
|
110
127
|
sentences,
|
|
111
128
|
}
|
|
112
129
|
|
|
@@ -122,6 +139,10 @@ export async function splitToWords(text: string, options?: SegmentationOptions)
|
|
|
122
139
|
|
|
123
140
|
options = { ...defaultSegmentationOptions, ...options }
|
|
124
141
|
|
|
142
|
+
if (options.language) {
|
|
143
|
+
options.language = getShortLanguageCode(options.language)
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
const optionsAsJson = JSON.stringify(options)
|
|
126
147
|
|
|
127
148
|
let wordSplitterRegExp = cachedWordSplitterRegExps.get(optionsAsJson)
|
|
@@ -142,7 +163,9 @@ export async function splitToWords(text: string, options?: SegmentationOptions)
|
|
|
142
163
|
|
|
143
164
|
function addPunctuationWordIfNeeded() {
|
|
144
165
|
if (charOffset > punctuationWordStartOffset) {
|
|
145
|
-
|
|
166
|
+
const wordText = text.substring(punctuationWordStartOffset, charOffset)
|
|
167
|
+
wordSequence.addWord(wordText, punctuationWordStartOffset, true)
|
|
168
|
+
|
|
146
169
|
punctuationWordStartOffset = charOffset
|
|
147
170
|
}
|
|
148
171
|
}
|
|
@@ -178,7 +201,8 @@ export async function splitToWords(text: string, options?: SegmentationOptions)
|
|
|
178
201
|
addPunctuationWordsBetween(lastMatchEndOffset, matchStartOffset)
|
|
179
202
|
}
|
|
180
203
|
|
|
181
|
-
|
|
204
|
+
const wordText = text.substring(matchStartOffset, matchEndOffset)
|
|
205
|
+
wordSequence.addWord(wordText, matchStartOffset, false)
|
|
182
206
|
|
|
183
207
|
lastMatchEndOffset = matchEndOffset
|
|
184
208
|
}
|
|
@@ -220,47 +244,27 @@ async function postprocessEastAsianWords(containingText: string, wordSequence: W
|
|
|
220
244
|
const wordBreaks = [...icuSegmentation.createWordBreakIterator(word)]
|
|
221
245
|
|
|
222
246
|
for (let i = 0; i < wordBreaks.length - 1; i++) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
247
|
+
const subwordStartOffset = wordStartOffset + wordBreaks[i]
|
|
248
|
+
const subwordEndOffset = wordStartOffset + wordBreaks[i + 1]
|
|
249
|
+
|
|
250
|
+
const subwordText = containingText.substring(subwordStartOffset, subwordEndOffset)
|
|
251
|
+
|
|
252
|
+
newWordSequence.addWord(
|
|
253
|
+
subwordText,
|
|
254
|
+
subwordStartOffset,
|
|
255
|
+
false,
|
|
228
256
|
)
|
|
229
257
|
}
|
|
230
258
|
} else {
|
|
231
|
-
|
|
259
|
+
const wordText = containingText.substring(wordEntry.startOffset, wordEntry.endOffset)
|
|
260
|
+
|
|
261
|
+
newWordSequence.addWord(wordText, wordEntry.startOffset, wordEntry.isPunctuation)
|
|
232
262
|
}
|
|
233
263
|
}
|
|
234
264
|
|
|
235
265
|
return newWordSequence
|
|
236
266
|
}
|
|
237
267
|
|
|
238
|
-
function buildWordSplitterRegExpForOptions(options: SegmentationOptions) {
|
|
239
|
-
const cldrSuppressionsForLang = cldrSuppressions[options.language ?? ''] ?? []
|
|
240
|
-
const contractionSuppressionsForLang = leadingApostropheContractionSuppressions[options.language ?? ''] ?? []
|
|
241
|
-
const contractionSuppressionsForLangWithSingleQuote = contractionSuppressionsForLang.map(str => str.replaceAll(`'`, `’`))
|
|
242
|
-
const customSuppressions = options.customSuppressions ?? []
|
|
243
|
-
|
|
244
|
-
let suppressions = [
|
|
245
|
-
...customSuppressions,
|
|
246
|
-
...cldrSuppressionsForLang,
|
|
247
|
-
...contractionSuppressionsForLang,
|
|
248
|
-
...contractionSuppressionsForLangWithSingleQuote,
|
|
249
|
-
...nounSuppressions,
|
|
250
|
-
...tldSuppressions,
|
|
251
|
-
]
|
|
252
|
-
|
|
253
|
-
const wordPattern = buildWordSplitterPattern([
|
|
254
|
-
...suppressions,
|
|
255
|
-
...suppressions.map(word => word.toLocaleLowerCase()),
|
|
256
|
-
...suppressions.map(word => word.toLocaleUpperCase()),
|
|
257
|
-
])
|
|
258
|
-
|
|
259
|
-
const wordSplitterRegExp = buildRegExp(wordPattern, { global: true })
|
|
260
|
-
|
|
261
|
-
return wordSplitterRegExp
|
|
262
|
-
}
|
|
263
|
-
|
|
264
268
|
// Add any missing punctuation words to a word sequence
|
|
265
269
|
export function addMissingPunctuationWordsToWordSequence(wordSequence: WordSequence, sourceText: string) {
|
|
266
270
|
const originalWordsReverseMapping = new Map<number, number>()
|
|
@@ -280,7 +284,9 @@ export function addMissingPunctuationWordsToWordSequence(wordSequence: WordSeque
|
|
|
280
284
|
wordSequenceWithPunctuation.lastEntry.text += ' '
|
|
281
285
|
wordSequenceWithPunctuation.lastEntry.endOffset = charEndOffset
|
|
282
286
|
} else {
|
|
283
|
-
|
|
287
|
+
const wordText = sourceText.substring(charOffset, charEndOffset)
|
|
288
|
+
|
|
289
|
+
wordSequenceWithPunctuation.addWord(wordText, charOffset, true)
|
|
284
290
|
}
|
|
285
291
|
|
|
286
292
|
charOffset = charEndOffset
|
|
@@ -316,6 +322,37 @@ export function addMissingPunctuationWordsToWordSequence(wordSequence: WordSeque
|
|
|
316
322
|
return { wordSequenceWithPunctuation, originalWordsReverseMapping }
|
|
317
323
|
}
|
|
318
324
|
|
|
325
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
326
|
+
// Helper methods
|
|
327
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
328
|
+
function buildWordSplitterRegExpForOptions(options: SegmentationOptions) {
|
|
329
|
+
const cldrSuppressionsForLang = cldrSuppressions[options.language ?? ''] ?? []
|
|
330
|
+
const extendedSuppressionsForLang = additionalSuppressions[options.language ?? ''] ?? []
|
|
331
|
+
const contractionSuppressionsForLang = leadingApostropheContractionSuppressions[options.language ?? ''] ?? []
|
|
332
|
+
const contractionSuppressionsForLangWithSingleQuote = contractionSuppressionsForLang.map(str => str.replaceAll(`'`, `’`))
|
|
333
|
+
const customSuppressions = options.customSuppressions ?? []
|
|
334
|
+
|
|
335
|
+
let suppressions = [
|
|
336
|
+
...customSuppressions,
|
|
337
|
+
...cldrSuppressionsForLang,
|
|
338
|
+
...extendedSuppressionsForLang,
|
|
339
|
+
...contractionSuppressionsForLang,
|
|
340
|
+
...contractionSuppressionsForLangWithSingleQuote,
|
|
341
|
+
...nounSuppressions,
|
|
342
|
+
...tldSuppressions,
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
const wordPattern = buildWordSplitterPattern([
|
|
346
|
+
...suppressions,
|
|
347
|
+
...suppressions.map(word => word.toLocaleLowerCase()),
|
|
348
|
+
...suppressions.map(word => word.toLocaleUpperCase()),
|
|
349
|
+
])
|
|
350
|
+
|
|
351
|
+
const wordSplitterRegExp = buildRegExp(wordPattern, { global: true })
|
|
352
|
+
|
|
353
|
+
return wordSplitterRegExp
|
|
354
|
+
}
|
|
355
|
+
|
|
319
356
|
async function getIcuSegmentation() {
|
|
320
357
|
try {
|
|
321
358
|
const icuSegmentation = await import('@echogarden/icu-segmentation-wasm')
|
|
@@ -326,28 +363,31 @@ async function getIcuSegmentation() {
|
|
|
326
363
|
}
|
|
327
364
|
}
|
|
328
365
|
|
|
366
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
367
|
+
// Types
|
|
368
|
+
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
329
369
|
export interface SegmentationResult {
|
|
330
|
-
|
|
370
|
+
words: WordSequence
|
|
331
371
|
sentences: Sentence[]
|
|
332
372
|
}
|
|
333
373
|
|
|
334
374
|
export class TextFragment {
|
|
335
375
|
wordRange: Range
|
|
336
|
-
|
|
376
|
+
words: WordSequence
|
|
337
377
|
|
|
338
|
-
constructor(wordRange: Range,
|
|
378
|
+
constructor(wordRange: Range, words: WordSequence) {
|
|
339
379
|
this.wordRange = wordRange
|
|
340
|
-
this.
|
|
380
|
+
this.words = words
|
|
341
381
|
}
|
|
342
382
|
|
|
343
383
|
get text() {
|
|
344
|
-
return this.
|
|
384
|
+
return this.words.text
|
|
345
385
|
}
|
|
346
386
|
|
|
347
387
|
get charRange(): Range {
|
|
348
388
|
return {
|
|
349
|
-
start: this.
|
|
350
|
-
end: this.
|
|
389
|
+
start: this.words.firstEntry.startOffset,
|
|
390
|
+
end: this.words.lastEntry.endOffset
|
|
351
391
|
}
|
|
352
392
|
}
|
|
353
393
|
}
|
package/src/WordSequence.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export class WordSequence {
|
|
2
2
|
entries: WordEntry[] = []
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const
|
|
4
|
+
addWord(text: string, textStartOffset: number, isPunctuation: boolean) {
|
|
5
|
+
const startOffset = textStartOffset
|
|
6
|
+
const endOffset = startOffset + text.length
|
|
6
7
|
|
|
7
8
|
this.entries.push({
|
|
8
|
-
text
|
|
9
|
+
text,
|
|
9
10
|
startOffset,
|
|
10
11
|
endOffset,
|
|
11
12
|
isPunctuation
|
|
@@ -13,7 +14,7 @@ export class WordSequence {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
getWordRange(startIndex: number, endIndex: number) {
|
|
16
|
-
return
|
|
17
|
+
return this.getEntryRange(startIndex, endIndex).map(entry => entry.text)
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
*iterateWordRange(startIndex: number, endIndex: number) {
|
|
@@ -52,7 +53,7 @@ export class WordSequence {
|
|
|
52
53
|
return slicedSequence
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
get
|
|
56
|
+
get wordArray() {
|
|
56
57
|
return this.entries.map(entry => entry.text)
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -72,6 +73,14 @@ export class WordSequence {
|
|
|
72
73
|
return this.entries[this.length - 1]
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
get punctuationEntries() {
|
|
77
|
+
return this.entries.filter(entry => entry.isPunctuation === true)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get punctuationWords() {
|
|
81
|
+
return this.entries.filter(entry => entry.isPunctuation === true).map(entry => entry.text)
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
get nonPunctuationEntries() {
|
|
76
85
|
return this.entries.filter(entry => entry.isPunctuation === false)
|
|
77
86
|
}
|
|
@@ -81,7 +90,7 @@ export class WordSequence {
|
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
get text() {
|
|
84
|
-
return this.
|
|
93
|
+
return this.wordArray.join('')
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
get length() {
|
|
@@ -91,8 +100,9 @@ export class WordSequence {
|
|
|
91
100
|
|
|
92
101
|
export interface WordEntry {
|
|
93
102
|
text: string
|
|
94
|
-
isPunctuation: boolean
|
|
95
103
|
|
|
96
104
|
startOffset: number
|
|
97
105
|
endOffset: number
|
|
106
|
+
|
|
107
|
+
isPunctuation: boolean
|
|
98
108
|
}
|
|
@@ -31,3 +31,13 @@ export function extractSuppressions(entries: { suppression: string}[]) {
|
|
|
31
31
|
|
|
32
32
|
return suppressions
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
export function getShortLanguageCode(langCode: string) {
|
|
36
|
+
const dashIndex = langCode.indexOf('-')
|
|
37
|
+
|
|
38
|
+
if (dashIndex == -1) {
|
|
39
|
+
return langCode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return langCode.substring(0, dashIndex).toLowerCase()
|
|
43
|
+
}
|