]> git.stg.codes - stg.git/blob - doc/xslt/webhelp/template/content/search/stemmers/de_stemmer.js
Fixed tests.
[stg.git] / doc / xslt / webhelp / template / content / search / stemmers / de_stemmer.js
1 /*
2  * Author: Joder Illi
3  *
4  * Copyright (c) 2010, FormBlitz AG
5  * All rights reserved.
6  * Implementation of the stemming algorithm from http://snowball.tartarus.org/algorithms/german/stemmer.html
7  * Copyright of the algorithm is: Copyright (c) 2001, Dr Martin Porter and can be found at http://snowball.tartarus.org/license.php
8  *
9  * Redistribution and use in source and binary forms, with or without modification, is covered by the standard BSD license.
10  *
11  */
12
13 //var stemmer = function Stemmer() {
14     /*
15     German includes the following accented forms,
16     ä   ö   ü
17     and a special letter, ß, equivalent to double s.
18     The following letters are vowels:
19     a   e   i   o   u   y   ä   ö   ü
20     */
21
22     var stemmer = function(word) {
23         /*
24         Put u and y between vowels into upper case
25         */
26         word = word.replace(/([aeiouyäöü])u([aeiouyäöü])/g, '$1U$2');
27         word = word.replace(/([aeiouyäöü])y([aeiouyäöü])/g, '$1Y$2');
28
29         /*
30         and then do the following mappings,
31         (a) replace ß with ss,
32         (a) replace ae with ä,                          Not doing these, have trouble with diphtongs
33         (a) replace oe with ö,                          Not doing these, have trouble with diphtongs
34         (a) replace ue with ü unless preceded by q.     Not doing these, have trouble with diphtongs
35         So in quelle, ue is not mapped to ü because it follows q, and in feuer it is not mapped because the first part of the rule changes it to feUer, so the u is not found.
36         */
37         word = word.replace(/ß/g, 'ss');
38         //word = word.replace(/ae/g, 'ä');
39         //word = word.replace(/oe/g, 'ö');
40         //word = word.replace(/([^q])ue/g, '$1ü');
41
42         /*
43         R1 and R2 are first set up in the standard way (see the note on R1 and R2), but then R1 is adjusted so that the region before it contains at least 3 letters.
44         R1 is the region after the first non-vowel following a vowel, or is the null region at the end of the word if there is no such non-vowel.
45         R2 is the region after the first non-vowel following a vowel in R1, or is the null region at the end of the word if there is no such non-vowel.
46         */
47
48         var r1Index = word.search(/[aeiouyäöü][^aeiouyäöü]/);
49         var r1 = '';
50         if (r1Index != -1) {
51             r1Index += 2;
52             r1 = word.substring(r1Index);
53         }
54
55         var r2Index = -1;
56         var r2 = '';
57
58         if (r1Index != -1) {
59             var r2Index = r1.search(/[aeiouyäöü][^aeiouyäöü]/);
60             if (r2Index != -1) {
61                 r2Index += 2;
62                 r2 = r1.substring(r2Index);
63                 r2Index += r1Index;
64             } else {
65                 r2 = '';
66             }
67         }
68
69         if (r1Index != -1 && r1Index < 3) {
70             r1Index = 3;
71             r1 = word.substring(r1Index);
72         }
73
74         /*
75         Define a valid s-ending as one of b, d, f, g, h, k, l, m, n, r or t.
76         Define a valid st-ending as the same list, excluding letter r.
77         */
78
79         /*
80         Do each of steps 1, 2 and 3.
81         */
82
83         /*
84         Step 1:
85         Search for the longest among the following suffixes,
86         (a) em   ern   er
87         (b) e   en   es
88         (c) s (preceded by a valid s-ending)
89         */
90         var a1Index = word.search(/(em|ern|er)$/g);
91         var b1Index = word.search(/(e|en|es)$/g);
92         var c1Index = word.search(/([bdfghklmnrt]s)$/g);
93         if (c1Index != -1) {
94             c1Index++;
95         }
96         var index1 = 10000;
97         var optionUsed1 = '';
98         if (a1Index != -1 && a1Index < index1) {
99             optionUsed1 = 'a';
100             index1 = a1Index;
101         }
102         if (b1Index != -1 && b1Index < index1) {
103             optionUsed1 = 'b';
104             index1 = b1Index;
105         }
106         if (c1Index != -1 && c1Index < index1) {
107             optionUsed1 = 'c';
108             index1 = c1Index;
109         }
110
111         /*
112         and delete if in R1. (Of course the letter of the valid s-ending is not necessarily in R1.) If an ending of group (b) is deleted, and the ending is preceded by niss, delete the final s.
113         (For example, äckern -> äck, ackers -> acker, armes -> arm, bedürfnissen -> bedürfnis)
114         */
115
116         if (index1 != 10000 && r1Index != -1) {
117             if (index1 >= r1Index) {
118                 word = word.substring(0, index1);
119                 if (optionUsed1 == 'b') {
120                     if (word.search(/niss$/) != -1) {
121                         word = word.substring(0, word.length -1);
122                     }
123                 }
124             }
125         }
126         /*
127         Step 2:
128         Search for the longest among the following suffixes,
129         (a) en   er   est
130         (b) st (preceded by a valid st-ending, itself preceded by at least 3 letters)
131         */
132
133         var a2Index = word.search(/(en|er|est)$/g);
134         var b2Index = word.search(/(.{3}[bdfghklmnt]st)$/g);
135         if (b2Index != -1) {
136             b2Index += 4;
137         }
138
139         var index2 = 10000;
140         var optionUsed2 = '';
141         if (a2Index != -1 && a2Index < index2) {
142             optionUsed2 = 'a';
143             index2 = a2Index;
144         }
145         if (b2Index != -1 && b2Index < index2) {
146             optionUsed2 = 'b';
147             index2 = b2Index;
148         }
149
150         /*
151         and delete if in R1.
152         (For example, derbsten -> derbst by step 1, and derbst -> derb by step 2, since b is a valid st-ending, and is preceded by just 3 letters)
153         */
154
155         if (index2 != 10000 && r1Index != -1) {
156             if (index2 >= r1Index) {
157                 word = word.substring(0, index2);
158             }
159         }
160
161         /*
162         Step 3: d-suffixes (*)
163         Search for the longest among the following suffixes, and perform the action indicated.
164         end   ung
165             delete if in R2
166             if preceded by ig, delete if in R2 and not preceded by e
167         ig   ik   isch
168             delete if in R2 and not preceded by e
169         lich   heit
170             delete if in R2
171             if preceded by er or en, delete if in R1
172         keit
173             delete if in R2
174             if preceded by lich or ig, delete if in R2
175         */
176
177         var a3Index = word.search(/(end|ung)$/g);
178         var b3Index = word.search(/[^e](ig|ik|isch)$/g);
179         var c3Index = word.search(/(lich|heit)$/g);
180         var d3Index = word.search(/(keit)$/g);
181         if (b3Index != -1) {
182             b3Index ++;
183         }
184
185         var index3 = 10000;
186         var optionUsed3 = '';
187         if (a3Index != -1 && a3Index < index3) {
188             optionUsed3 = 'a';
189             index3 = a3Index;
190         }
191         if (b3Index != -1 && b3Index < index3) {
192             optionUsed3 = 'b';
193             index3 = b3Index;
194         }
195         if (c3Index != -1 && c3Index < index3) {
196             optionUsed3 = 'c';
197             index3 = c3Index;
198         }
199         if (d3Index != -1 && d3Index < index3) {
200             optionUsed3 = 'd';
201             index3 = d3Index;
202         }
203
204         if (index3 != 10000 && r2Index != -1) {
205             if (index3 >= r2Index) {
206                 word = word.substring(0, index3);
207                 var optionIndex = -1;
208                 var optionSubsrt = '';
209                 if (optionUsed3 == 'a') {
210                     optionIndex = word.search(/[^e](ig)$/);
211                     if (optionIndex != -1) {
212                         optionIndex++;
213                         if (optionIndex >= r2Index) {
214                             word = word.substring(0, optionIndex);
215                         }
216                     }
217                 } else if (optionUsed3 == 'c') {
218                     optionIndex = word.search(/(er|en)$/);
219                     if (optionIndex != -1) {
220                         if (optionIndex >= r1Index) {
221                             word = word.substring(0, optionIndex);
222                         }
223                     }
224                 } else if (optionUsed3 == 'd') {
225                     optionIndex = word.search(/(lich|ig)$/);
226                     if (optionIndex != -1) {
227                         if (optionIndex >= r2Index) {
228                             word = word.substring(0, optionIndex);
229                         }
230                     }
231                 }
232             }
233         }
234
235         /*
236         Finally,
237         turn U and Y back into lower case, and remove the umlaut accent from a, o and u.
238         */
239         word = word.replace(/U/g, 'u');
240         word = word.replace(/Y/g, 'y');
241         word = word.replace(/ä/g, 'a');
242         word = word.replace(/ö/g, 'o');
243         word = word.replace(/ü/g, 'u');
244
245         return word;
246     };
247 //}