4
* Copyright (c) 2010, FormBlitz AG
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
9
* Redistribution and use in source and binary forms, with or without modification, is covered by the standard BSD license.
13
//var stemmer = function Stemmer() {
15
German includes the following accented forms,
17
and a special letter, ß, equivalent to double s.
18
The following letters are vowels:
22
var stemmer = function(word) {
24
Put u and y between vowels into upper case
26
word = word.replace(/([aeiouyäöü])u([aeiouyäöü])/g, '$1U$2');
27
word = word.replace(/([aeiouyäöü])y([aeiouyäöü])/g, '$1Y$2');
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.
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ü');
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.
48
var r1Index = word.search(/[aeiouyäöü][^aeiouyäöü]/);
52
r1 = word.substring(r1Index);
59
var r2Index = r1.search(/[aeiouyäöü][^aeiouyäöü]/);
62
r2 = r1.substring(r2Index);
69
if (r1Index != -1 && r1Index < 3) {
71
r1 = word.substring(r1Index);
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.
80
Do each of steps 1, 2 and 3.
85
Search for the longest among the following suffixes,
88
(c) s (preceded by a valid s-ending)
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);
98
if (a1Index != -1 && a1Index < index1) {
102
if (b1Index != -1 && b1Index < index1) {
106
if (c1Index != -1 && c1Index < index1) {
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)
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);
128
Search for the longest among the following suffixes,
130
(b) st (preceded by a valid st-ending, itself preceded by at least 3 letters)
133
var a2Index = word.search(/(en|er|est)$/g);
134
var b2Index = word.search(/(.{3}[bdfghklmnt]st)$/g);
140
var optionUsed2 = '';
141
if (a2Index != -1 && a2Index < index2) {
145
if (b2Index != -1 && b2Index < index2) {
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)
155
if (index2 != 10000 && r1Index != -1) {
156
if (index2 >= r1Index) {
157
word = word.substring(0, index2);
162
Step 3: d-suffixes (*)
163
Search for the longest among the following suffixes, and perform the action indicated.
166
if preceded by ig, delete if in R2 and not preceded by e
168
delete if in R2 and not preceded by e
171
if preceded by er or en, delete if in R1
174
if preceded by lich or ig, delete if in R2
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);
186
var optionUsed3 = '';
187
if (a3Index != -1 && a3Index < index3) {
191
if (b3Index != -1 && b3Index < index3) {
195
if (c3Index != -1 && c3Index < index3) {
199
if (d3Index != -1 && d3Index < index3) {
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) {
213
if (optionIndex >= r2Index) {
214
word = word.substring(0, optionIndex);
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);
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);
237
turn U and Y back into lower case, and remove the umlaut accent from a, o and u.
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');
b'\\ No newline at end of file'