3 # :Copyright: © 2012, 2014 Günter Milde.
4 # :Licence: This work may be distributed and/or modified under
5 # the conditions of the `LaTeX Project Public License`,
6 # either version 1.3 of this license or (at your option)
8 # :Version: 0.3 (2014-06-14)
10 # ===================================================================
11 # Langes oder rundes S: Automatische Konversion nach Silbentrennung
12 # ===================================================================
17 Automatische Bestimmung der S-Schreibung auf Basis der Silbentrennung
18 in der `Wortliste der deutschsprachigen Trennmustermannschaft`.
26 # Lade Funktionen und Klassen für reguläre Ausdrücke::
29 from werkzeug
import WordFile
, join_word
34 # Die freie `Wortliste der deutschsprachigen Trennmustermannschaft`_
35 # ("Lembergsche Liste")
39 wordfile
= WordFile('../../wortliste') # volle Liste (≅ 400 000 Wörter
40 # wordfile = WordFile('../../wortliste-binnen-s') # vorsortierte Liste (≅ 200 000 Wörter)
45 # Die Trennzeichen der Wortliste sind
47 # == ================================================================
48 # \· ungewichtete Trennstellen (solche, wo noch niemand sich um die
49 # Gewichtung gekümmert hat)
50 # . unerwünschte Trennstellen (sinnverwirrend), z.B. Ur-in.stinkt
52 # \- Nebentrennstellen
53 # < Trennstellen nach Vorsilben
54 # > Trennstellen vor Suffixen
55 # == ================================================================
64 # Siehe [wikipedia]_ und ("DDR"-) [Duden]_ (Regeln K 44,45)::
68 # ſ steht im Silbenanlaut::
70 word
= re
.sub(ur
'^s', ur
'ſ', word
)
71 word
= re
.sub(ur
'([-<>=·.])s', ur
'\1ſ', word
)
73 # ſ steht im Inlaut als stimmhaftes s zwischen Vokalen
74 # (gilt auch für ungetrenntes ss zwischen Selbstlauten, z.B. Hausse, Baisse)::
76 word
= re
.sub(ur
'([AEIOUYÄÖÜaeiouäöüé])s([aeiouyäöüé])', ur
'\1ſ\2', word
)
77 word
= re
.sub(ur
'([AEIOUYÄÖÜaeiouäöüé])ss([aeiouyäöüés])', ur
'\1ſſ\2', word
)
79 # ſ steht in den Verbindungen sp, st, sch und in Digraphen::
81 word
= word
.replace(u
'st', u
'ſt')
82 word
= word
.replace(u
'sp', u
'ſp')
83 word
= word
.replace(u
'sch', u
'ſch')
85 # word = word.replace(u'ps', u'pſ')
86 word
= word
.replace(u
'Ps', u
'Pſ') # Ψ
87 word
= re
.sub(ur
'^ps', ur
'pſ', word
) # ψ (ps am Wortanfang)
88 word
= re
.sub(ur
'([-<>=·.])ps', ur
'\1pſ', word
) # ψ (ps am Silbenanfang)
90 # word = word.replace(u'ſsſt', u'ſſſt') # Pssst!
95 # Die Verbindungen ss, sp¹, st werden zu ſſ, ſp und ſt, auch wenn sie
96 # durch eine Nebentrennstelle (Trennung innerhalb eines Wortbestandteiles)
97 # getrennt sind. Das s bleibt rund im Auslaut, d.h. am Wortende und vor
98 # einer Haupttrennstelle (Trennung an der Grenze zweier Wortbestandteile
99 # (Vorsilb<Stamm, Bestimmungswort=Grundwort). Für sz und sk gelten
100 # Spezialregeln, die die Herkunft der Wörter beachten (siehe `Spezialfälle`_
101 # sowie `Fremdwörter und Eingennamen`_).
103 # ¹ s bleibt rund vor Nebentrennstelle wenn ph folgt (Phos-phor).
107 word
= re
.sub(ur
's[-.]+ſ([aeiouyäöüé])', ur
'ſ-ſ\1', word
)
108 word
= re
.sub(ur
's[-.]+p([^h])', ur
'ſ-p\1', word
)
109 word
= re
.sub(ur
's[-.]+t', ur
'ſ-p', word
) # Reformschreibung
115 # ſz trotz Trennzeichen::
117 word
= word
.replace(u
's-zen', ur
'ſ-zen') # Adoleszenz, Aszendent, ...
118 word
= word
.replace(u
's-ze-n', ur
'ſ-ze-n') # Damaszener, ...
119 word
= word
.replace(u
's-zi-n', ur
'ſ-zi-n') # faszinieren, ...
120 word
= word
.replace(u
'as-zi', u
'aſ-zi') # [Ll]asziv, ...
121 word
= word
.replace(u
'vis-zi', u
'viſ-zi') # Mukoviszidose
122 word
= word
.replace(u
's-zil', ur
'ſ-zil') # Os-zil-la-ti-on
123 word
= word
.replace(u
's-zie', ur
'ſ-zie') # fluo-res-zie-ren, ...
125 # ſ wird geschrieben, wenn der S-Laut nur scheinbar im Auslaut steht,
126 # weil ein folgendes unbetontes e ausfällt::
128 # Basel, Beisel, Pilsen, drechseln, wechseln, häckseln
129 word
= word
.replace(u
'Bas-ler', u
'Baſ-ler')
130 word
= word
.replace(u
'Pils-ner', u
'Pilſ-ner')
131 word
= word
.replace(u
'echs-ler', u
'echſ-ler') # Dechsler, Wechsler
132 word
= word
.replace(u
'äcks-ler', u
'äckſ-ler') # Häcksler
133 word
= word
.replace(u
'Rössl', u
'Röſſl')
135 # Insel (Rheininsler), zünseln (Maiszünsler)
136 word
= word
.replace(u
'ins-ler', u
'inſ-ler')
137 word
= word
.replace(u
'üns-ler', u
'ünſ-ler')
139 # unsre, unsrige, ...
140 word
= word
.replace(u
'uns-r', u
'unſ-r')
142 # Häusl, Lisl, bissl, Glasl, Rössl
143 word
= word
.replace(u
'sl', u
'ſl')
144 word
= word
.replace(u
'ssl', u
'ſſl')
146 # ſ steht auch am Ende von Abkürzungen, wenn es im abgekürzten Wort steht
147 # (Abſ. - Abſatz/Abſender, (de)creſc. - (de)creſcendo, daſ. - daſelbst ...)
149 word
= word
.replace(u
'cresc', u
'creſc')
151 # Alternativtrennung, wo beide Fälle ſ verlangen:
153 word
= word
.replace(u
'er<.]sa', u
'er<.]ſa') # Kind=er<.satz/Kin-der=satz
155 # Fremdwörter und Eigennamen
156 # ~~~~~~~~~~~~~~~~~~~~~~~~~~
158 # Schreibung nach Regeln der Herkunftssprache. Dabei ist zu bedenken, daß zu
159 # der Zeit, als das lange s im Antiquasatz noch üblich war (bis ca. 1800), die
160 # Rechtschreibung freier gehandhabt wurde und mehrfach Wandlungen unterworfen
163 # Im Deutschen werden im Fraktursatz nicht eingedeutschte Fremdwörter
164 # lateinischen und romanischen Ursprungs in Antiqua mit rund-s geschrieben.
167 # The long, medial, or descending ess, as distinct from the short or
168 # terminal ess. In Roman script, the long ess was used everywhere except at
169 # the end of words, where the short ess was used, and frequently in what is
170 # now the digraph «ss», which was often written «ſs» rather than «ſſ»
171 # [en.wiktionary.org]_. See also [Typefounder08]_ and [West06]_.
175 word
= word
.replace(u
'sh', u
'ſh') # (englisch)
176 word
= word
.replace(u
'Disc', u
'Diſc') # (englisch)
177 word
= word
.replace(u
'Csar', u
'Cſar') # Cs -> Tsch (ungarisch)
178 # word = word.replace(u'sz', u'ſz') # polnisch, ungarisch
179 word
= word
.replace(u
'Liszt', u
'Liſzt') # ungarisch
180 word
= word
.replace(u
'Pusz', u
'Puſz') # Pusz-ta ungarisch
181 word
= re
.sub(ur
'([Tt])s([aeiouy])', ur
'\1ſ\2', word
) # ts (chinesisch)
184 # Schreibung von Fremdwörtern und Eigennamen mit Schluss-ß:
186 # Der 1971er [Duden]_ führt zu englischen Fremdwörtern mit Schluß-ß die
187 # österreichische Schreibung mit "ss" auf (Miß, engl. und österr. Schreibung
188 # Miss) wobei das Schluß-s nicht unterstrichen ist (also lang sein müßte).
189 # So auch Boss, Business, Stewardess.
191 # Dagegen sagt [en.wiktionary.org]_: "the digraph «ss» was often
192 # written «ſs» rather than «ſſ»".
196 # TODO so machen, oder ſs (wie in de-1996)? :
197 # if sprachvariante == 'de-1901':
198 # word = re.sub(ur'ss$', ur'ſſ', word)
199 # word = word.replace(u'ss=', u'ſſ=')
200 # word = word.replace(u'ss-ſch', u'ſſ-ſch')
202 word
= re
.sub(u
'ss$', u
'ſs', word
)
203 word
= word
.replace(u
'ss=', u
'ſs=')
204 word
= word
.replace(u
'ss-ſch', u
'ſs-ſch')
212 # Test auf verbliebene Unklarheiten
214 # Wenn ein Wort "s" nur an Stellen enthält wo die Regeln rundes S vorsehen,
215 # ist die automatische Konversion abgeschlossen.
217 # Ausnahmen und spezielle Regeln
219 # Liste von Teilstrings, welche stets rund-s behalten ::
221 spezialfaelle_rund_s
= [
225 u
'Ausg', u
'ausſchl', u
'desgl', u
'hrsg', u
'insb',
227 # ausgelassenes flüchtiges e::
229 u
'Dresd-ne', # Dresd·ner/Dresd·ner·in
231 # s steht auch in einigen Fremdwörtern vor z::
233 u
'on-fis-zie', # konfiszieren
234 u
'le-bis-z', # plebiszit
235 u
'is-zi-pl', # Disziplin (nach Duden (1934) auch Diſziplin).
236 u
'mas-ze-ner' # Damaszener
238 # ss im Auslaut (vgl. `Fremdwörter und Eigennamen`_)::
240 # u'Gauss', # vgl. "Briefwechsel zwischen C.F. Gauss und H.C. Schumacher, herausg. von C.A.F. Peters"
241 # aber Boſſ, Busineſſ, Dreſſ
242 u
'ſs-ſch' # Graſs-ſcher, Weiſs-ſches, Zeiſs-ſche
246 spezialfaelle_rund_s
= [(fall
, fall
.replace('s', '~'))
247 for fall
in spezialfaelle_rund_s
]
249 def is_complete(word
):
251 # Ersetze s an Stellen, wo es rund zu schreiben ist, durch ~ und teste auf
252 # verbliebene Vorkommen.
254 # Einzelfälle mit rundem S (substrings)::
256 for fall
, ersatz
in spezialfaelle_rund_s
:
257 word
= word
.replace(fall
, ersatz
)
259 # s steht am Wortende, auch in Zusammensetzungen (vor Haupttrennstellen)::
261 word
= re
.sub(ur
's($|[=<>])', ur
'~\1', word
)
263 # Einige ältere Quellen schreiben ss am Schluss von Fremdwörtern oder Namen
264 # (Gauss). Andere schreiben ſs oder ſſ. ::
266 #word = re.sub(ur'ss(=|$)', ur'~~\1', word)
268 # s steht am Silbenende (vor Nebentrennstellen), wenn kein p, t, z oder ſ
269 # folgt (in der traditionellen Schreibung wird st nicht getrennt)::
271 # word = re.sub(ur'ss?([·.\-][^ptzſ])', ur'~\1', word) # konservativ
272 word
= re
.sub(ur
'ss?([·.\-][^pzſ])', ur
'~\1', word
) # traditionell
274 # s steht auch vor Nebentrennstellen, wenn ph folgt::
276 word
= word
.replace(u
's-ph', u
'~-ph')
278 # s steht nach Vorsilben (wie aus<, dis<) auch wenn s, p, t, oder z folgt::
280 word
= word
.replace(u
's<', u
'~<')
282 # s steht vor Trennstellen am Suffixanfang (wie >sen, >son),
283 # auch wenn s, p, t, oder z folgt::
285 word
= word
.replace(u
's>', u
'~>')
287 # s steht im Inlaut vor k, n, w::
289 word
= re
.sub(ur
's([knw])', ur
'~\1', word
)
291 # und suche nach übrigen Vorkommen::
293 return 's' not in word
299 # Rechtschreibvariante
300 # --------------------
302 # Angabe der Sprachvariante nach [BCP47]_ (Reformschreibung 'de' oder 'de-1996',
303 # Schweiz 'de-CH', ...)
305 sprachtag
= 'de-1901'
307 # Für gebrochene Schriften gibt es den ISO Sprachtag
309 # :Latf: Latin (Fraktur variant)
311 # also "Lateinisches Alphabet, gebrochen". Wir hätten dann "de-1901-Latf" und
313 # http://www.unicode.org/iso15924/iso15924-codes.html
318 # Der Algorithmus sortiert die Wörter der Trennliste in die folgenden
321 # Menge aller Wörter der Liste (ohne Trennmuster)::
325 # Automatisch konvertierte Wörter (ohne Trennmuster)::
329 # Offene Fälle mit Trennmuster-Feldern wie im Original:
331 # Zur automatischen Konvertierung fehlt die Unterscheidung in Haupt- und
332 # Nebentrennstellen (Wichtung)::
336 # Der Algorithmus kann die Schreibweise (noch) nicht ermitteln
337 # (mit teilweisen Ersetzungen)::
344 # Iteration über alle Zeilen der Wortliste::
346 for entry
in wordfile
:
348 word
= entry
.get(sprachtag
) # Wort mit Trennstellen
349 if word
is None: # Wort existiert nicht in der Sprachvariante
352 # Menge aller Wörter der gewählten Schreibweise (ohne Trennstellen)::
359 # Wörter ohne Binnen-s müssen nicht konvertiert werden. Damit wird die
360 # Wortliste ungefähr um die Hälfte kürzer::
362 if 's' not in entry
[0][:-1]:
363 completed
.append(entry
[0])
366 # Regelbasierte s-ſ-Schreibung::
368 word
= s_ersetzen(word
)
370 # Einsortieren nach Vollständigkeit der Ersetzungen::
372 if is_complete(word
):
373 completed
.append(join_word(word
))
376 entry
.set(word
, sprachtag
) # Rückschreiben von teilweisen Ersetzungen
378 if word
.find(u
's·') != -1:
379 ungewichtet
.append(entry
)
387 # Wortliste mit automatisch bestimmter S-Schreibung, ohne Trennstellen::
389 completed_file
= file('words-' + sprachtag
+ 'Latf.txt', 'w')
390 completed_file
.write(u
'\n'.join(completed
).encode('utf8') + '\n')
397 print "# Gesamtwortzahl (traditionelle Rechtschreibung):", len(words
)
398 print "# Automatisch konvertiert:", len(completed
)
399 print "# Kategorisierung der Trennstellen fehlt:", len(ungewichtet
)
400 for entry
in ungewichtet
:
401 print unicode(entry
).encode('utf8')
402 print "# noch offen/unklar:", len(offen
)
404 print unicode(entry
).encode('utf8')
406 # print "# konvertiert+nichtklassifiziert+offen:",
407 # print len(completed) + len(ungewichtet) + len(offen)
417 # Gesamtwortzahl (traditionelle Rechtschreibung): 427746
418 # Automatisch konvertiert: 427740
419 # Kategorisierung der Trennstellen fehlt: 0
422 # Die Mehrzahl der Wörter der Trennliste wurde nach den Regeln des Dudens in
423 # die Schreibung mit langem `S` (ſ) konvertiert (wobei ungefähr die Hälfte der
424 # Wörter kein kleines `s` enthält womit die Konversion trivial wird).
426 # Der größte Teil der ca. 16 000 noch offenen Fälle kann durch Unterscheidung
427 # in Haupt- und Nebentrennstellen (z.B. mit dem SiSiSi_-Algorithmus) gelöst
428 # werden. CTAN enthält eine (alte) Variante mit Atomlisten im Text-Format
429 # (`Ur-SiSiSi`_). Die im Rahmen einer Diplomarbeit [gruber03]_ entstandene
430 # Variante `Java-SiSiSi` enthält eine Schnittstelle zum
431 # Wortanalysealgorithmus, die es ermöglicht SiSiSi als Bibliothek in fremde
432 # Programme einzubinden.
434 # Für eine beschränke Anzahl offener Fälle wurden Ausnahmeregeln und Ausnahmen
437 # Das Resultat muß noch auf nicht erfaßte Ausnahmen und Sonderfälle geprüft
438 # werden. Fehlentscheidungen sind nicht auszuschließen.
444 # Wörter mit ſ am Wort oder Silbenende
445 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
447 # * sp, ss, st und sz wird zu ſp, ſſ, ſt und ſz, auch wenn das ſ vor einer
448 # Nebentrennstelle steht (z.B. Weſ-pe, eſ-ſen, abbürſ-ten (Reformtrennung)
451 # **Aber** rundes s am Wortende und nach Vorsilben, z.B.
452 # dis=putieren, Aus=ſage, aus=tragen, aus=zeichnen.
455 # Wörter mit identischer Schreibung ohne lang-s
456 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
458 # * Wach[s/ſ]tube: Wach-stube oder Wachs-Tube
459 # * Ga[s/ſ]traſſe: Gas-Trasse oder Gast-Rasse
464 # * Tonarten (As-Dur oder Aſ-Dur)
466 # - Im Fraktur-Duden steht As in Antiqua mit rundem s, aber
467 # - im 1976-er [Duden]_ steht As ohne Unterstreichung des `s`.
474 # .. [Duden] `Der Große Duden` 16. Auflage, VEB Bibliographisches Institut
477 # Kennzeichnet im Stichwortteil rundes s durch Unterstreichen.
479 # .. [wikipedia] Langes s
480 # http://de.wikipedia.org/wiki/Langes_s
482 # .. [en.wiktionary.org]
483 # http://en.wiktionary.org/wiki/%C5%BF
486 # http://typefoundry.blogspot.com/2008/01/long-s.html
488 # .. [West06] Andrew West, `The rules for long s`, 2006
489 # http://babelstone.blogspot.com/2006/06/rules-for-long-s.html
491 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
492 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
494 # .. [gruber03] Martin Gruber,
495 # `Effiziente Gestaltung der Wortanalyse in SiSiSi`, Diplomarbeit, 2003,
496 # http://www.ads.tuwien.ac.at/publications/bib/pdf/gruber-03.pdf
500 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
501 # http://mirrors.ctan.org/language/hyphenation/dehyph-exptl/projektbeschreibung.pdf
503 # .. _SiSiSi: http://www.ads.tuwien.ac.at/research/SiSiSi.html
505 # .. _Ur-SiSiSi: ftp://ftp.dante.de/pub/tex/systems/unix/sisisi/
508 # .. Fragen, Spezialfälle, Beispiele
510 # http://www.e-welt.net/bfds_2003/bund/fragen/13_Langes%20oder%20rundes%20S.pdf
511 # http://www.e-welt.net/bfds_2003/bund/fragen/12_hs%20und%20scharfes%20s_1.pdf