diff --git a/tests/test_de_gloss.py b/tests/test_de_gloss.py new file mode 100644 index 00000000..3d19861b --- /dev/null +++ b/tests/test_de_gloss.py @@ -0,0 +1,46 @@ +import unittest +from collections import defaultdict + +from wikitextprocessor import Wtp + +from wiktextract.config import WiktionaryConfig +from wiktextract.extractor.de.gloss import extract_glosses +from wiktextract.thesaurus import close_thesaurus_db +from wiktextract.wxr_context import WiktextractContext + + +class TestGlossList(unittest.TestCase): + def setUp(self) -> None: + self.wxr = WiktextractContext( + Wtp(lang_code="de"), WiktionaryConfig(dump_file_lang_code="de") + ) + + def tearDown(self) -> None: + self.wxr.wtp.close_db_conn() + close_thesaurus_db( + self.wxr.thesaurus_db_path, self.wxr.thesaurus_db_conn + ) + + def test_de_extract_glosses(self): + self.wxr.wtp.start_page("") + root = self.wxr.wtp.parse(":[1] gloss1 \n:[2] gloss2") + + page_data = [defaultdict(list)] + + extract_glosses(self.wxr, page_data, root.children[0]) + + self.assertEqual( + page_data, + [ + { + "senses": [ + { + "glosses": ["gloss1"], + }, + { + "glosses": ["gloss2"], + }, + ] + } + ], + ) diff --git a/tests/test_de_page.py b/tests/test_de_page.py new file mode 100644 index 00000000..4451898c --- /dev/null +++ b/tests/test_de_page.py @@ -0,0 +1,213 @@ +# Tests for parsing a page from the German Wiktionary + +import unittest + +from collections import defaultdict + +from wikitextprocessor import Wtp + +from wiktextract.config import WiktionaryConfig +from wiktextract.extractor.de.page import ( + parse_page, + parse_section, + fix_level_hierarchy_of_subsections, +) +from wiktextract.thesaurus import close_thesaurus_db +from wiktextract.wxr_context import WiktextractContext + + +class DePageTests(unittest.TestCase): + def setUp(self): + conf1 = WiktionaryConfig( + dump_file_lang_code="de", + # capture_language_codes=None, + # capture_translations=True, + # capture_pronunciation=True, + # capture_linkages=True, + # capture_compounds=True, + # capture_redirects=True, + # capture_examples=True, + ) + self.wxr = WiktextractContext(Wtp(lang_code="de"), conf1) + + def tearDown(self) -> None: + self.wxr.wtp.close_db_conn() + close_thesaurus_db( + self.wxr.thesaurus_db_path, self.wxr.thesaurus_db_conn + ) + + def test_de_parse_page(self): + self.wxr.wtp.add_page("Vorlage:Sprache", 10, "") + lst = parse_page( + self.wxr, + "Beispiel", + """ +== Beispiel ({{Sprache|Deutsch}}) == +""", + ) + self.assertEqual( + lst, + [ + { + "lang": "Deutsch", + "lang_code": "de", + "word": "Beispiel", + } + ], + ) + + def test_de_parse_page_skipping_head_templates(self): + self.wxr.wtp.add_page("Vorlage:Wort der Woche", 10, "") + self.wxr.wtp.add_page("Vorlage:Siehe auch", 10, "") + self.wxr.wtp.add_page("Vorlage:Sprache", 10, "") + lst = parse_page( + self.wxr, + "Beispiel", + """ +{{Wort der Woche|46|2020}} +{{Siehe auch|[[cát]]}} +== Beispiel ({{Sprache|Deutsch}}) == +""", + ) + self.assertEqual( + lst, + [ + { + "lang": "Deutsch", + "lang_code": "de", + "word": "Beispiel", + } + ], + ) + + # The way append_base_data() works requires the presence of a sense + # dictionary before starting a new pos section. Therefore, we need to add + # at least one sense data point to the test case. + def test_de_parse_section(self): + self.wxr.wtp.add_page("Vorlage:Wortart", 10, "") + self.wxr.wtp.add_page("Vorlage:Bedeutungen", 10, "") + page_text = """ +=== {{Wortart|Adjektiv|Englisch}}, {{Wortart|Adverb|Englisch}} === +{{Bedeutungen}} +:[1] gloss1 +=== {{Wortart|Verb|Englisch}} === +{{Bedeutungen}} +:[1] gloss2 +=== {{Wortart|Substantiv|Englisch}} === +{{Bedeutungen}} +:[1] gloss3 + +""" + self.wxr.wtp.start_page("") + root = self.wxr.wtp.parse( + page_text, + pre_expand=True, + ) + + base_data = defaultdict(list, {"lang_code": "de"}) + page_data = [defaultdict(list, {"lang_code": "de"})] + parse_section(self.wxr, page_data, base_data, root.children) + + self.assertEqual( + page_data, + [ + { + "lang_code": "de", + "pos": "adj", + "senses": [ + { + "glosses": ["gloss1"], + }, + ], + }, + { + "lang_code": "de", + "pos": "adv", + "senses": [ + { + "glosses": ["gloss1"], + }, + ], + }, + { + "lang_code": "de", + "pos": "verb", + "senses": [ + { + "glosses": ["gloss2"], + }, + ], + }, + { + "lang_code": "de", + "pos": "noun", + "senses": [ + { + "glosses": ["gloss3"], + }, + ], + }, + ], + ) + + def test_de_fix_level_hierarchy_of_subsections(self): + self.wxr.wtp.add_page("Vorlage:Englisch Substantiv Übersicht", 10, "") + self.wxr.wtp.add_page("Vorlage:Worttrennung", 10, "") + self.wxr.wtp.add_page("Vorlage:Aussprache", 10, "") + self.wxr.wtp.add_page("Vorlage:Übersetzungen", 10, "") + self.wxr.wtp.add_page("Vorlage:Ü-Tabelle", 10, "") + self.wxr.wtp.add_page("Vorlage:Referenzen", 10, "") + + page_text = """ +{{Englisch Substantiv Übersicht +|args=args}} + +{{Worttrennung}} +:item + +{{Aussprache}} +:item + +==== {{Übersetzungen}} ==== +{{Ü-Tabelle|1|G=arg|Ü-Liste= +:item +}} + +{{Referenzen}} +:item +""" + self.wxr.wtp.start_page("") + root = self.wxr.wtp.parse( + page_text, + pre_expand=True, + ) + + subsections = fix_level_hierarchy_of_subsections( + self.wxr, root.children + ) + + target_page_text = """==== {{Englisch Substantiv Übersicht\n|args=args}} ==== + +==== {{Worttrennung}} ==== +:item + +==== {{Aussprache}} ==== +:item + +==== {{Übersetzungen}} ==== +{{Ü-Tabelle|1|G=arg|Ü-Liste= +:item +}} + +==== {{Referenzen}} ==== +:item +""" + root = self.wxr.wtp.parse( + target_page_text, + pre_expand=True, + ) + + self.assertEqual( + [str(s) for s in subsections], + [str(t) for t in root.children], + ) diff --git a/usertools/de_language_data.py b/usertools/de_language_data.py new file mode 100644 index 00000000..40dd2cbb --- /dev/null +++ b/usertools/de_language_data.py @@ -0,0 +1,72 @@ +# Export German Wiktionary language data to JSON. +# +# Usage: +# +# python language_data.py de dewiktionary_dump_file [--languages languages_output_file] + +import argparse +from wikitextprocessor import Wtp +from wiktextract.config import WiktionaryConfig +from wiktextract.wxr_context import WiktextractContext +from wiktextract.page import clean_node +from wikitextprocessor.dumpparser import process_dump +from wikitextprocessor import NodeKind, WikiNode + +import json + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Export Wiktionary language data to JSON" + ) + parser.add_argument("lang_code", type=str, help="Dump file language code") + parser.add_argument("dump", type=str, help="Wiktionary xml dump file path") + parser.add_argument( + "--languages", + type=str, + default="languages.json", + help="Language data output file path", + ) + args = parser.parse_args() + wxr = WiktextractContext(Wtp(lang_code=args.lang_code), WiktionaryConfig()) + + wxr = WiktextractContext( + Wtp( + lang_code=args.lang_code, db_path="wikt-db_de_language_data_temp.db" + ), + WiktionaryConfig(), + ) + help_ns_id = wxr.wtp.NAMESPACE_DATA["Help"]["id"] + template_ns_id = wxr.wtp.NAMESPACE_DATA["Template"]["id"] + process_dump(wxr.wtp, args.dump, {help_ns_id, template_ns_id}) + + # The page 'Hilfe:Sprachkürzel seems to be the only central collection of + # language codes and their German expansions. We will use this until we find + # perhaps a more authoritative source. + sprachkuerzel = wxr.wtp.get_page("Hilfe:Sprachkürzel") + + wxr.config.word = sprachkuerzel.title + wxr.wtp.start_page(sprachkuerzel.title) + tree = wxr.wtp.parse( + sprachkuerzel.body, + pre_expand=True, + ) + + languages = {} + for node in filter(lambda n: isinstance(n, WikiNode), tree.children): + if node.kind != NodeKind.LEVEL3: + continue + + for table_row in node.find_child_recursively(NodeKind.TABLE_ROW): + third_row_content = table_row.children[2].children[0] + if ( + isinstance(third_row_content, str) + or third_row_content.kind != NodeKind.TEMPLATE + ): + continue + lang_code = third_row_content.template_name + + languages[lang_code] = [clean_node(wxr, None, third_row_content)] + + with open(args.languages, "w", encoding="utf-8") as fout: + json.dump(languages, fout, indent=2, ensure_ascii=False, sort_keys=True) diff --git a/wiktextract/config.py b/wiktextract/config.py index 07fd12c1..ac9cc53d 100644 --- a/wiktextract/config.py +++ b/wiktextract/config.py @@ -56,6 +56,8 @@ class WiktionaryConfig: "POS_TYPES", "OTHER_SUBTITLES", "ZH_PRON_TAGS", + "FR_FORM_TABLES", + "DE_FORM_TABLES", "LANGUAGES_BY_NAME", "LANGUAGES_BY_CODE", "FORM_OF_TEMPLATES", @@ -123,6 +125,10 @@ def __init__( self.set_attr_from_json( "FORM_OF_TEMPLATES", "form_of_templates.json" ) + if dump_file_lang_code == "fr": + self.set_attr_from_json("FR_FORM_TABLES", "form_tables.json") + if dump_file_lang_code == "de": + self.set_attr_from_json("DE_FORM_TABLES", "form_templates.json") def to_kwargs(self): return { diff --git a/wiktextract/data/de/form_tables.json b/wiktextract/data/de/form_tables.json new file mode 100644 index 00000000..939a2674 --- /dev/null +++ b/wiktextract/data/de/form_tables.json @@ -0,0 +1,119 @@ +[ + "Deutsch Substantiv Übersicht", + "Deutsch Substantiv Übersicht -sch", + "Deutsch Toponym Übersicht", + "Deutsch adjektivisch Übersicht", + "Deutsch Eigenname Übersicht", + "Deutsch Name Übersicht", + "Deutsch Vorname Übersicht m", + "Deutsch Vorname Übersicht f", + "Deutsch Vorname Übersicht n", + "Deutsch Verb Übersicht", + "Deutsch Adjektiv Übersicht", + "Pronomina-Tabelle", + "Afrikaans Substantiv Übersicht", + "Albanisch Verb Übersicht", + "Altgriechisch Adjektiv Übersicht", + "Altgriechisch Substantiv Übersicht", + "Altirisch Substantiv Übersicht", + "Altnordisch Substantiv Übersicht", + "Aserbaidschanisch Substantiv Übersicht", + "Asturisch Substantiv Übersicht", + "Baschkirisch Substantiv Übersicht", + "Baskisch Substantiv Übersicht", + "Bosnisch Substantiv Übersicht", + "Bretonisch Substantiv Übersicht", + "Bulgarisch Verb Übersicht", + "Dänisch Adjektiv Übersicht", + "Dänisch Possessivpronomen Übersicht", + "Dänisch Substantiv Übersicht", + "Dänisch Verb Übersicht", + "Englisch Adjektiv Übersicht", + "Englisch Substantiv Übersicht", + "Englisch Verb Übersicht", + "Esperanto Substantiv Übersicht", + "Färöisch Substantiv Übersicht", + "Färöisch Verb Übersicht", + "Finnisch Substantiv Übersicht", + "Finnisch Verb Übersicht", + "Französisch Adjektiv Übersicht", + "Französisch Substantiv Übersicht", + "Französisch Verb Übersicht", + "Galicisch Substantiv Übersicht", + "Hausa Adjektiv Übersicht", + "Hausa Possessiv Übersicht", + "Hausa Substantiv Übersicht", + "Hebräisch Substantiv Übersicht", + "Ido Substantiv Übersicht", + "Irisch Adjektiv Übersicht", + "Irisch Substantiv Übersicht", + "Isländisch Name Übersicht", + "Isländisch Substantiv Übersicht", + "Isländisch Verb Übersicht", + "Italienisch Adjektiv Übersicht", + "Italienisch Substantiv Übersicht", + "Italienisch Verb Übersicht", + "Katalanisch Adjektiv Übersicht", + "Katalanisch Substantiv Übersicht", + "Katalanisch Verb Übersicht", + "Keilschrift Übersicht", + "Korsisch Substantiv Übersicht", + "Kroatisch Adjektiv Übersicht", + "Kroatisch Substantiv Übersicht", + "Kurdisch Substantiv Übersicht", + "Latein Adjektiv Übersicht", + "Latein Adverb Übersicht", + "Latein Substantiv Übersicht", + "Lettisch Substantiv Übersicht", + "Lettisch Verb Übersicht", + "Mazedonisch Substantiv Übersicht", + "Nahuatl Substantiv Übersicht", + "Neugriechisch Substantiv Übersicht", + "Niederdeutsch Adjektiv Übersicht", + "Niederländisch Adjektiv Übersicht", + "Niederländisch Substantiv Übersicht", + "Niedersorbisch Substantiv Übersicht", + "Norwegisch Adjektiv Übersicht", + "Norwegisch Eigenname Übersicht", + "Norwegisch Substantiv Übersicht", + "Norwegisch Verb Übersicht", + "Okzitanisch Substantiv Übersicht", + "Okzitanisch Verb Übersicht", + "Papiamentu Substantiv Übersicht", + "Polnisch Grundzahl Übersicht", + "Polnisch Substantiv Übersicht", + "Portugiesisch Substantiv Übersicht", + "Rumänisch Numerale Übersicht", + "Rumänisch Personalpronomen Übersicht", + "Rumänisch Substantiv Übersicht", + "Rumänisch Verb Übersicht", + "Russisch Substantiv Übersicht", + "Sardisch Substantiv Übersicht", + "Schwedisch Adjektiv Übersicht", + "Schwedisch Adverb Übersicht", + "Schwedisch Eigenname Übersicht", + "Schwedisch Pronomen Übersicht", + "Schwedisch Verb Übersicht", + "Scots Substantiv Übersicht", + "Serbisch Substantiv Übersicht", + "Sesotho Substantiv Übersicht", + "Slowakisch Adjektiv Übersicht", + "Slowenisch Substantiv Übersicht", + "Spanisch Adjektiv Übersicht", + "Spanisch Substantiv Übersicht", + "Spanisch Verb Übersicht", + "Suaheli Substantiv Übersicht", + "Suaheli Verb Übersicht", + "Symbol Übersicht", + "Tschechisch universal Übersicht", + "Türkisch Substantiv Übersicht", + "Ukrainisch Substantiv Übersicht", + "Umbrisch Substantiv Übersicht", + "Ungarisch Adjektiv Übersicht", + "Ungarisch Substantiv Übersicht", + "Ungarisch Verb Übersicht", + "Usbekisch Substantiv Übersicht", + "Venezianisch Substantiv Übersicht", + "Walisisch Substantiv Übersicht", + "Westflämisch Substantiv Übersicht" +] diff --git a/wiktextract/data/de/languages.json b/wiktextract/data/de/languages.json new file mode 100644 index 00000000..bc0151f2 --- /dev/null +++ b/wiktextract/data/de/languages.json @@ -0,0 +1,490 @@ +{ + "MHA": ["modernes Hocharabisch"], + "aa": ["Afar"], + "aae": ["Arbëresh"], + "ab": ["Abchasisch"], + "abe": ["West-Abenaki"], + "abq": ["Abasinisch"], + "ace": ["Acehnesisch"], + "acf": ["Antillen-Kreolisch"], + "acw": ["Hijazi-Arabisch"], + "ady": ["Adygeisch"], + "ae": ["Avestisch"], + "af": ["Afrikaans"], + "agf": ["Arguni"], + "agj": ["Argobba"], + "aie": ["Amara"], + "ain": ["Ainu"], + "ak": ["Akan"], + "akg": ["Anakalangu"], + "akk": ["Akkadisch"], + "akz": ["Alabama"], + "ale": ["Aleutisch"], + "aln": ["Gegisch"], + "alp": ["Alune"], + "alq": ["Algonkin"], + "als": ["Alemannisch"], + "alt": ["Altaisch"], + "am": ["Amharisch"], + "amk": ["Ambai"], + "amu": ["Amuzgo"], + "an": ["Aragonesisch"], + "ang": ["Altenglisch"], + "apc": ["Levantinisches Arabisch"], + "apw": ["West-Apache"], + "ar": ["Arabisch"], + "arc": ["Aramäisch"], + "ari": ["Arikara"], + "arn": ["Mapudungun"], + "arw": ["Arawak"], + "ary": ["Marokkanisch-Arabisch"], + "arz": ["Ägyptisch-Arabisch"], + "as": ["Assamesisch"], + "asb": ["Assiniboine"], + "ast": ["Asturisch"], + "atv": ["Nordaltaisch"], + "aua": ["Asumboa"], + "aud": ["Anutisch"], + "av": ["Awarisch"], + "ay": ["Aymara"], + "az": ["Aserbaidschanisch"], + "azb": ["Südaserbaidschanisch"], + "azd": ["Östliches Durango-Nahuatl"], + "azj": ["Nordaserbaidschanisch"], + "ba": ["Baschkirisch"], + "baa": ["Babatana"], + "bal": ["Belutschi"], + "ban": ["Balinesisch"], + "bar": ["Bairisch"], + "bat": ["Altpreußisch"], + "bbc": ["Batak Toba"], + "bci": ["Baule"], + "bcl": ["Zentral-Bikolano"], + "bcm": ["Banoni"], + "bde": ["Bade"], + "be": ["Weißrussisch"], + "bem": ["Bemba"], + "ber": ["Berbersprache"], + "bg": ["Bulgarisch"], + "bh": ["Bihari"], + "bhw": ["Biak"], + "bi": ["Bislama"], + "bjn": ["Banjar"], + "bla": ["Blackfoot"], + "bm": ["Bambara"], + "bmg": ["Bangi"], + "bn": ["Bengalisch"], + "bnd": ["Banda"], + "bo": ["Tibetisch"], + "bpy": ["Bishnupriya"], + "br": ["Bretonisch"], + "bs": ["Bosnisch"], + "bty": ["Bobot"], + "bua": ["Burjatisch"], + "bug": ["Buginesisch"], + "bxr": ["Russisches Burjatisch"], + "bzg": ["Babuza"], + "ca": ["Katalanisch"], + "ccc": ["Chamicuro"], + "ce": ["Tschetschenisch"], + "ceb": ["Cebuano"], + "cel": ["Keltisch"], + "ch": ["Chamorro"], + "chc": ["Catawba"], + "chm": ["Mari"], + "cho": ["Choctaw"], + "chp": ["Chipewyan"], + "chr": ["Cherokee"], + "chy": ["Cheyenne"], + "ciw": ["Chippewa"], + "ckb": ["Sorani"], + "ckt": ["Tschuktschisch"], + "co": ["Korsisch"], + "com": ["Comanche"], + "cr": ["Cree"], + "crh": ["Krimtatarisch"], + "cri": ["Saotomensisches Kreol"], + "cro": ["Crow"], + "crs": ["Seselwa"], + "cs": ["Tschechisch"], + "csb": ["Kaschubisch"], + "ctu": ["Tumbalá-Chol"], + "cu": ["Altkirchenslawisch"], + "cv": ["Tschuwaschisch"], + "cy": ["Walisisch"], + "da": ["Dänisch"], + "dag": ["Dagbani"], + "dak": ["Dakota"], + "ddn": ["Dendi"], + "de": ["Deutsch"], + "dhv": ["Dehu"], + "diq": ["Dimli"], + "dlm": ["Dalmatisch"], + "dng": ["Dunganisch"], + "dob": ["Dobu"], + "dsb": ["Niedersorbisch"], + "dum": ["Mittelniederländisch"], + "dv": ["Maledivisch"], + "dz": ["Dzongkha"], + "ee": ["Ewe"], + "egl": ["Emilianisch"], + "egy": ["Ägyptisch"], + "el": ["Griechisch (Neu-)"], + "ems": ["Alutiiq"], + "en": ["Englisch"], + "enm": ["Mittelenglisch"], + "eo": ["Esperanto"], + "es": ["Spanisch"], + "ess": ["Sibirisch-Yupik"], + "esu": ["Zentral-Alaska-Yupik"], + "et": ["Estnisch"], + "eu": ["Baskisch"], + "ext": ["Extremadurisch"], + "fa": ["Persisch"], + "fan": ["Fang"], + "ff": ["Fulfulde"], + "fi": ["Finnisch"], + "fj": ["Fidschi"], + "fng": ["Fanagalo"], + "fo": ["Färöisch"], + "fon": ["Fon"], + "fr": ["Französisch"], + "frk": ["Altfränkisch"], + "fro": ["Altfranzösisch"], + "frp": ["Frankoprovenzalisch"], + "frr": ["Nordfriesisch"], + "fud": ["Futunisch"], + "fur": ["Friaulisch"], + "fy": ["Westfriesisch"], + "ga": ["Irisch"], + "gag": ["Gagausisch"], + "gan": ["Gan"], + "gay": ["Gayo"], + "gcf": ["Guadeloupe-Kreolisch"], + "gd": ["Schottisch-Gälisch"], + "gem": ["Urgermanisch"], + "gez": ["Ge’ez"], + "gha": ["Ghadames"], + "gil": ["Gilbertesisch"], + "gl": ["Galicisch"], + "glk": ["Gilaki"], + "gmh": ["Mittelhochdeutsch"], + "gml": ["Mittelniederdeutsch"], + "gn": ["Guaraní"], + "gnc": ["Guanche"], + "goh": ["Althochdeutsch"], + "got": ["Gotisch"], + "grc": ["Altgriechisch"], + "gsw": ["Schweizerdeutsch"], + "gu": ["Gujarati"], + "gv": ["Manx"], + "ha": ["Hausa"], + "hac": ["Gorani"], + "hak": ["Hakka"], + "haw": ["Hawaiianisch"], + "he": ["Hebräisch"], + "hi": ["Hindi"], + "hid": ["Hidatsa"], + "hif": ["Fiji Hindi"], + "hil": ["Hiligaynon"], + "hit": ["Hethitisch"], + "ho": ["Hiri Motu"], + "hr": ["Kroatisch"], + "hsb": ["Obersorbisch"], + "ht": ["Haitianisch"], + "hu": ["Ungarisch"], + "hy": ["Armenisch"], + "hz": ["Otjiherero"], + "ia": ["Interlingua"], + "iba": ["Iban"], + "id": ["Indonesisch"], + "ie": ["Interlingue"], + "ig": ["Igbo"], + "ik": ["Inupiaq"], + "ikt": ["Inuinnaqtun"], + "ilo": ["Ilokano"], + "inh": ["Inguschisch"], + "io": ["Ido"], + "is": ["Isländisch"], + "it": ["Italienisch"], + "iu": ["Inuktitut"], + "ja": ["Japanisch"], + "jbo": ["Lojban"], + "jv": ["Javanisch"], + "ka": ["Georgisch"], + "kaa": ["Karakalpakisch"], + "kab": ["Kabylisch"], + "kai": ["Karekare"], + "kaw": ["Kawi"], + "kbd": ["Kabardinisch"], + "kca": ["Chantisch"], + "kdr": ["Karaimisch"], + "kea": ["Kapverdisches Kreol"], + "kg": ["Kikongo"], + "khw": ["Khowar"], + "ki": ["Kikuyu"], + "kic": ["Kickapoo"], + "kj": ["Kuanyama"], + "kjh": ["Chakassisch"], + "kjj": ["Chinalugisch"], + "kk": ["Kasachisch"], + "kl": ["Grönländisch"], + "km": ["Kambodschanisch"], + "kmr": ["Kurmandschi"], + "kn": ["Kannada"], + "ko": ["Koreanisch"], + "koi": ["Komi-Permjakisch"], + "kok": ["Konkani"], + "kos": ["Kosraeanisch"], + "krc": ["Karatschai-Balkarisch"], + "krl": ["Karelisch"], + "ks": ["Kashmiri"], + "ksh": ["Kölsch"], + "ksk": ["Kansa"], + "ku": ["Kurdisch"], + "kum": ["Kumükisch"], + "kv": ["Komi"], + "kw": ["Kornisch"], + "ky": ["Kirgisisch"], + "kyh": ["Karok"], + "la": ["Latein"], + "lad": ["Ladino"], + "lb": ["Luxemburgisch"], + "lep": ["Lepcha"], + "lg": ["Luganda"], + "li": ["Limburgisch"], + "lij": ["Ligurisch"], + "liv": ["Livisch"], + "lkt": ["Lakota"], + "lld": ["Ladinisch"], + "lmo": ["Lombardisch"], + "ln": ["Lingala"], + "lo": ["Laotisch"], + "lt": ["Litauisch"], + "lv": ["Lettisch"], + "lzz": ["Lasisch"], + "mad": ["Maduresisch"], + "mak": ["Makassar"], + "mas": ["Maa"], + "mdf": ["Mokscha"], + "mg": ["Madagassisch"], + "mga": ["Mittelirisch"], + "mh": ["Marshallesisch"], + "mi": ["Maori"], + "mia": ["Miami-Illinois"], + "mic": ["Micmac"], + "min": ["Minangkabau"], + "mjy": ["Mohican"], + "mk": ["Mazedonisch"], + "ml": ["Malayalam"], + "mn": ["Mongolisch"], + "mnc": ["Mandschurisch"], + "mns": ["Mansisch"], + "moh": ["Mohawk"], + "mr": ["Marathi"], + "ms": ["Malaiisch"], + "mt": ["Maltesisch"], + "mus": ["Creek"], + "mxi": ["Mozarabisch"], + "my": ["Birmanisch"], + "myv": ["Ersja"], + "na": ["Nauruisch"], + "nah": ["Nahuatl"], + "nan": ["Min Nan"], + "nap": ["Neapolitanisch"], + "naq": ["Nama"], + "nb": ["Bokmål"], + "nch": ["Huastekisches Zentral-Nahuatl"], + "nci": ["Klassisches Nahuatl"], + "nd": ["Nord-Ndebele"], + "nds": ["Niederdeutsch"], + "ne": ["Nepalesisch"], + "new": ["Newari"], + "ng": ["Ndonga"], + "ngo": ["Ngoni"], + "nic": ["Dogon"], + "nl": ["Niederländisch"], + "nld": ["Flämisch"], + "nmn": ["ǃXóõ"], + "nn": ["Nynorsk"], + "no": ["Norwegisch"], + "nog": ["Nogaisch"], + "non": ["Altnordisch"], + "nov": ["Novial"], + "nqo": ["N'Ko"], + "nr": ["Süd-Ndebele"], + "nrf": ["Altnormannisch"], + "nso": ["Nord-Sotho"], + "nup": ["Nupe"], + "nv": ["Navajo"], + "ny": ["Chichewa"], + "obt": ["Altbretonisch"], + "oc": ["Okzitanisch"], + "oco": ["Altkornisch"], + "ofs": ["Altfriesisch"], + "oge": ["Altgeorgisch"], + "oj": ["Ojibwe"], + "om": ["Oromo"], + "omr": ["Altmarathi"], + "or": ["Oriya"], + "orv": ["Altostslawisch"], + "os": ["Ossetisch"], + "osa": ["Osage"], + "osx": ["Altsächsisch"], + "otk": ["Alttürkisch"], + "otw": ["Ottawa"], + "owl": ["Altwalisisch"], + "pa": ["Pandschabi"], + "pam": ["Kapampangan"], + "pap": ["Papiamentu"], + "pau": ["Palauisch"], + "paw": ["Pawnee"], + "pdc": ["Pennsylvaniadeutsch"], + "pdt": ["Plautdietsch"], + "peo": ["Altpersisch"], + "pi": ["Pali"], + "pih": ["Pitkern"], + "pis": ["Pijin"], + "pl": ["Polnisch"], + "pms": ["Piemontesisch"], + "pov": ["Guineabissauisches Kreol"], + "pox": ["Polabisch"], + "pqm": ["Malecite-Passamaquoddy"], + "pre": ["Principensisches Kreol"], + "prg": ["Prußisch"], + "pro": ["Altprovenzalisch/Altokzitanisch"], + "prs": ["Dari"], + "ps": ["Paschtu"], + "pt": ["Portugiesisch"], + "qka": ["Erzgebirgisch"], + "qu": ["Quechua"], + "qua": ["Quapaw"], + "quz": ["Cusco-Quechua"], + "raj": ["Rajasthani"], + "rap": ["Rapanui"], + "rm": ["Rätoromanisch"], + "rmq": ["Caló"], + "rmy": ["Vlax"], + "rn": ["Kirundi"], + "ro": ["Rumänisch"], + "rom": ["Romani"], + "ru": ["Russisch"], + "ruo": ["Istrorumänisch"], + "rup": ["Aromunisch"], + "ruq": ["Meglenorumänisch"], + "rw": ["Kinyarwanda"], + "ryu": ["Zentral-Okinawa"], + "sa": ["Sanskrit"], + "sac": ["Fox"], + "sah": ["Jakutisch"], + "sc": ["Sardisch"], + "scn": ["Sizilianisch"], + "sco": ["Scots"], + "sd": ["Sindhi"], + "se": ["Nordsamisch"], + "sg": ["Sango"], + "sga": ["Altirisch"], + "sgs": ["Schemaitisch"], + "sgw": ["Gurage"], + "sh": ["Serbokroatisch"], + "si": ["Singhalesisch"], + "sjd": ["Kildinsamisch"], + "sjn": ["Sindarin"], + "sjw": ["Shawnee"], + "sk": ["Slowakisch"], + "sl": ["Slowenisch"], + "sm": ["Samoanisch"], + "smi": ["Sami"], + "smn": ["Inarisamisch"], + "sn": ["Shona"], + "so": ["Somalisch"], + "sq": ["Albanisch"], + "sr": ["Serbisch"], + "srn": ["Sranantongo"], + "ss": ["Siswati"], + "st": ["Sesotho"], + "stq": ["Saterfriesisch"], + "su": ["Sundanesisch"], + "sux": ["Sumerisch"], + "sv": ["Schwedisch"], + "sva": ["Swanisch"], + "sw": ["Suaheli"], + "swb": ["Komorisch"], + "swg": ["Schwäbisch"], + "syr": ["Syrisch"], + "szl": ["Schlesisch (Polnisch)"], + "ta": ["Tamil"], + "tay": ["Atayal"], + "te": ["Telugu"], + "tet": ["Tetum"], + "tg": ["Tadschikisch"], + "tgw": ["Tagwana"], + "th": ["Thai"], + "ti": ["Tigrinya"], + "tk": ["Turkmenisch"], + "tl": ["Tagalog"], + "tlh": ["Klingonisch"], + "tli": ["Tlingit"], + "tn": ["Setswana"], + "to": ["Tongaisch"], + "tok": ["Toki Pona"], + "tpi": ["Tok Pisin"], + "tpn": ["Tupinambá"], + "tpw": ["Tupí"], + "tr": ["Türkisch"], + "trv": ["Taroko"], + "trw": ["Torwali"], + "ts": ["Xitsonga"], + "tsi": ["Tsimshian"], + "tt": ["Tatarisch"], + "tvl": ["Tuvaluisch"], + "ty": ["Tahitianisch"], + "tyv": ["Tuwinisch"], + "udm": ["Udmurtisch"], + "ug": ["Uigurisch"], + "uk": ["Ukrainisch"], + "umu": ["Munsee"], + "unm": ["Unami"], + "ur": ["Urdu"], + "uz": ["Usbekisch"], + "ve": ["Tshivenda"], + "vec": ["Venezianisch"], + "vep": ["Wepsisch"], + "vi": ["Vietnamesisch"], + "vls": ["Westflämisch"], + "vo": ["Volapük"], + "vot": ["Wotisch"], + "vro": ["Võro"], + "wa": ["Wallonisch"], + "war": ["Waray"], + "wen": ["Sorbisch"], + "wlc": ["shiMwali"], + "wni": ["shiNdzuani"], + "wo": ["Wolof"], + "xal": ["Kalmückisch"], + "xcl": ["Altarmenisch"], + "xfa": ["Faliskisch"], + "xga": ["Galatisch"], + "xh": ["isiXhosa"], + "xhu": ["Hurritisch"], + "xld": ["Lydisch"], + "xlu": ["Luwisch"], + "xno": ["Anglonormannisch"], + "xpq": ["Mohegan-Pequot"], + "xtg": ["Gallisch"], + "xur": ["Urartäisch"], + "xve": ["Venetisch"], + "yak": ["Yakima"], + "yi": ["Jiddisch"], + "yo": ["Yoruba"], + "yua": ["Mayathan"], + "yue": ["Kantonesisch"], + "za": ["Zhuang"], + "zdj": ["shiNgazidja"], + "zea": ["Seeländisch"], + "zh": ["Chinesisch"], + "zh-cn": ["Chinesisch (vereinfacht)"], + "zh-tw": ["Chinesisch (traditionell)"], + "zu": ["isiZulu"], + "zza": ["Zazaki"] +} diff --git a/wiktextract/data/de/other_subtitles.json b/wiktextract/data/de/other_subtitles.json new file mode 100644 index 00000000..22a657a7 --- /dev/null +++ b/wiktextract/data/de/other_subtitles.json @@ -0,0 +1,4 @@ +{ + "etymology": ["Herkunft"], + "pronunciation": ["Aussprache"] +} diff --git a/wiktextract/data/de/pos_subtitles.json b/wiktextract/data/de/pos_subtitles.json new file mode 100644 index 00000000..63d5b984 --- /dev/null +++ b/wiktextract/data/de/pos_subtitles.json @@ -0,0 +1,91 @@ +{ + "Abkürzung (Deutsch)": { "pos": "abbrev" }, + "Abkürzung": { "pos": "abbrev" }, + "Abtönungspartikel": { "pos": "particle" }, + "Adjektiv ": { "pos": "adj" }, + "Adjektiv": { "pos": "adj" }, + "Adverb ": { "pos": "adv" }, + "Adverb": { "pos": "adv" }, + "Affix": { "pos": "affix" }, + "Antwortpartikel": { "pos": "particle" }, + "Artikel": { "pos": "det" }, + "Bruchzahlwort": { "pos": "num" }, + "Buchstabe": { "pos": "character" }, + "Demonstrativpronomen": { "pos": "pron" }, + "Eigenname ": { "pos": "name" }, + "Eigenname": { "pos": "name" }, + "Enklitikon": { "pos": "suffix" }, + "Fokuspartikel": { "pos": "particle" }, + "Formel": { "pos": "phrase" }, + "Gebundenes Lexem": { "pos": "lexeme" }, + "Geflügeltes Wort": { "pos": "phrase" }, + "Gentilname": { "pos": "name" }, + "Gradpartikel": { "pos": "particle" }, + "Grußformel": { "pos": "phrase" }, + "Hilfsverb": { "pos": "aux" }, + "Hiragana": { "pos": "character" }, + "Indefinitpronomen": { "pos": "pron" }, + "Infinitiv ": { "pos": "verb" }, + "Infinitiv": { "pos": "verb" }, + "Infix": { "pos": "infix" }, + "Interfix": { "pos": "interfix" }, + "Interjektion": { "pos": "intj" }, + "Interrogativadverb": { "pos": "adv" }, + "Interrogativpronomen": { "pos": "pron" }, + "Kardinalzahl": { "pos": "num" }, + "Kausaladverb": { "pos": "adv" }, + "Kognomen": { "pos": "nomen" }, + "Konjunktion": { "pos": "conj" }, + "Konjunktionaladverb": { "pos": "adv" }, + "Kontraktion": { "pos": "abbrev" }, + "Lokaladverb": { "pos": "adv" }, + "Merkspruch": { "pos": "phrase" }, + "Modaladverb": { "pos": "adv" }, + "Modalpartikel": { "pos": "particle" }, + "Nachname": { "pos": "name" }, + "Negationspartikel": { "pos": "particle" }, + "Numerale": { "pos": "num" }, + "Onomatopoetikum": { "pos": "intj" }, + "Ortsnamengrundwort": { "pos": "name" }, + "Ordinalzahl": { "pos": "num" }, + "Partikel": { "pos": "particle" }, + "Partikelverb": { "pos": "verb" }, + "Patronym": { "pos": "name" }, + "Personalpronomen ": { "pos": "pron" }, + "Personalpronomen": { "pos": "pron" }, + "Possessivpronomen ": { "pos": "pron" }, + "Possessivpronomen": { "pos": "pron" }, + "Postposition": { "pos": "postp" }, + "Präfix": { "pos": "prefix" }, + "Präfixoid": { "pos": "prefix" }, + "Präposition ": { "pos": "prep" }, + "Präposition": { "pos": "prep" }, + "Pronomen": { "pos": "pron" }, + "Pronominaladverb": { "pos": "adv" }, + "Redewendung": { "pos": "phrase" }, + "Reflexives Personalpronomen": { "pos": "pron" }, + "Reflexivpronomen": { "pos": "pron" }, + "Relativpronomen": { "pos": "pron" }, + "Reziprokpronomen": { "pos": "pron" }, + "Schriftzeichen": { "pos": "character" }, + "Sprichwort": { "pos": "phrase" }, + "Straßenname": { "pos": "name" }, + "Subjunktion": { "pos": "conj" }, + "Substantiv": { "pos": "noun" }, + "Suffix": { "pos": "suffix" }, + "Suffixoid": { "pos": "suffix" }, + "Symbol": { "pos": "symbol" }, + "Temporaladverb": { "pos": "adv" }, + "Temporaldverb": { "pos": "adv" }, + "Toponym": { "pos": "name" }, + "Verb": { "pos": "verb" }, + "Vergleichspartikel": { "pos": "particle" }, + "Vervielfältigungszahlwort": { "pos": "num" }, + "Vorname": { "pos": "name" }, + "Wiederholungszahlwort": { "pos": "num" }, + "Wortverbindung": { "pos": "phrase" }, + "Zahlklassifikator": { "pos": "noun" }, + "Zahlzeichen": { "pos": "num" }, + "Zirkumfix": { "pos": "circumfix" }, + "Zirkumposition": { "pos": "circumpos" } +} diff --git a/wiktextract/extractor/de/gloss.py b/wiktextract/extractor/de/gloss.py new file mode 100644 index 00000000..b209f455 --- /dev/null +++ b/wiktextract/extractor/de/gloss.py @@ -0,0 +1,61 @@ +from collections import defaultdict +from typing import Dict, List + +import re + +from wikitextprocessor import NodeKind, WikiNode + +from wiktextract.page import clean_node +from wiktextract.wxr_context import WiktextractContext + + +def extract_glosses( + wxr: WiktextractContext, + page_data: List[Dict], + list_node: WikiNode, +) -> None: + for list_item_node in list_node.find_child(NodeKind.LIST_ITEM): + item_type = list_item_node.sarg + if item_type == "*": + wxr.wtp.debug( + f"Skipped a sense modifier in gloss list: {list_item_node}", + sortid="extractor/de/glosses/extract_glosses/19", + ) + # XXX: We should extract the modifier. However, it seems to affect + # multiple glosses. Needs investigation. + pass + elif item_type == ":": + gloss_data = defaultdict(list) + for sub_list_node in list_item_node.find_child(NodeKind.LIST): + wxr.wtp.debug( + f"Skipped a sub-list in gloss list: {sub_list_node}", + sortid="extractor/de/glosses/extract_glosses/27", + ) + # XXX: We should extract the subglosses as subsenses. + pass + + gloss_text = clean_node(wxr, gloss_data, list_item_node.children) + + match = re.match(r"\[(\d+[a-z]?)\]", gloss_text) + if match: + sense_number = match.group(1) + gloss_text = gloss_text[match.end() :].strip() + else: + sense_number = None + + if not sense_number: + wxr.wtp.debug( + f"Failed to extract sense number from gloss: {gloss_text}", + sortid="extractor/de/glosses/extract_glosses/28", + ) + + gloss_data["glosses"] = [gloss_text] + + page_data[-1]["senses"].append(gloss_data) + + else: + wxr.wtp.debug( + f"Unexpected list item in glosses: {list_item_node}", + sortid="extractor/de/glosses/extract_glosses/29", + ) + continue diff --git a/wiktextract/extractor/de/page.py b/wiktextract/extractor/de/page.py new file mode 100644 index 00000000..57537790 --- /dev/null +++ b/wiktextract/extractor/de/page.py @@ -0,0 +1,394 @@ +import copy +import logging + +from collections import defaultdict +from typing import Dict, List, Union + +from wikitextprocessor import NodeKind, WikiNode + +from wikitextprocessor.parser import LevelNode + +from wiktextract.datautils import append_base_data +from wiktextract.wxr_context import WiktextractContext + +from .gloss import extract_glosses + +# Templates that are used to form panels on pages and that should be ignored in +# various positions +PANEL_TEMPLATES = set() + +# Template name prefixes used for language-specific panel templates (i.e., +# templates that create side boxes or notice boxes or that should generally +# be ignored). +PANEL_PREFIXES = set() + +# Additional templates to be expanded in the pre-expand phase +ADDITIONAL_EXPAND_TEMPLATES = set() + + +# Templates that should not be pre-expanded +DO_NOT_PRE_EXPAND_TEMPLATES = { + "Ü-Tabelle", # Translation table + "Quellen", # Can be ignored since we have the tags in the tree +} + + +def fix_level_hierarchy_of_subsections( + wxr: WiktextractContext, tree: List[WikiNode] +) -> List[WikiNode]: + """ + This function introduces level hierarchy to subsections and their content. + + The German Wiktionary does generally not use level 4 headings but instead + uses templates to define the subsections. These templates are usually + followed by a list of content that belongs to the subsection. Yet, in the + tree the content is on the same level as the subsection template. In Gernman + wiktionary, for cosmetic reasons, a level 4 heading is used to introduce the + translation subsection that then also contains other subsections not related + to translations. + + See: + https://de.wiktionary.org/wiki/Hilfe:Formatvorlage#Der_%E2%80%9EEndteil%E2%80%9C + """ + level_nodes: List[WikiNode] = [] + for node in tree: + if isinstance(node, WikiNode): + # A level 4 heading is used to introduce the translation + # section. + if node.kind == NodeKind.LEVEL4: + # Find the index of the first template after the Ü-Tabelle + # template + split_idx = len(node.children) + for idx, child in enumerate(node.children): + if split_idx < len(node.children): + if ( + isinstance(child, WikiNode) + and child.kind == NodeKind.TEMPLATE + ): + break + else: + split_idx = idx + 1 + if ( + isinstance(child, WikiNode) + and child.kind == NodeKind.TEMPLATE + and child.template_name == "Ü-Tabelle" + ): + split_idx = idx + 1 + + children_until_translation_table = node.children[:split_idx] + + children_after_translation_table = node.children[split_idx:] + + node.children = children_until_translation_table + level_nodes.append(node) + + level_nodes.extend( + fix_level_hierarchy_of_subsections( + wxr, children_after_translation_table + ) + ) + + elif node.kind == NodeKind.TEMPLATE: + level_node = LevelNode(NodeKind.LEVEL4, node.loc) + level_node.largs = [[node]] + level_nodes.append(level_node) + + elif node.kind == NodeKind.LIST: + if len(level_nodes) > 0: + level_nodes[-1].children.append(node) + else: + wxr.wtp.debug( + f"Unexpected list while introducing level hierarchy: {node}", + sortid="extractor/de/page/introduce_level_hierarchy/52", + ) + continue + + # Sometimes links are used outside of a section to link the whole + # entry to a category. We treat them here as level 4 headings, + # without any children. + elif node.kind == NodeKind.LINK: + level_node = LevelNode(NodeKind.LEVEL4, node.loc) + level_node.largs = [[node]] + level_nodes.append(level_node) + + # ignore
tags + elif node.kind == NodeKind.HTML and node.sarg == "br": + pass + else: + wxr.wtp.debug( + f"Unexpected WikiNode while introducing level hierarchy: {node}", + sortid="extractor/de/page/introduce_level_hierarchy/55", + ) + else: + if not len(level_nodes): + if not isinstance(node, str) or not node.strip() == "": + wxr.wtp.debug( + f"Unexpected string while introducing level hierarchy: {node}", + sortid="extractor/de/page/introduce_level_hierarchy/61", + ) + continue + level_nodes[-1].children.append(node) + return level_nodes + + +def parse_section( + wxr: WiktextractContext, + page_data: List[Dict], + base_data: Dict, + level_node: Union[WikiNode, List[Union[WikiNode, str]]], +) -> None: + # Page structure: https://de.wiktionary.org/wiki/Hilfe:Formatvorlage + + if isinstance(level_node, list): + for x in level_node: + parse_section(wxr, page_data, base_data, x) + return + + elif not isinstance(level_node, WikiNode): + if not isinstance(level_node, str) or not level_node.strip() == "": + wxr.wtp.debug( + f"Unexpected node type in parse_section: {level_node}", + sortid="extractor/de/page/parse_section/31", + ) + return + + # Level 3 headings are used to start POS sections like + # === {{Wortart|Verb|Deutsch}} === + elif level_node.kind == NodeKind.LEVEL3: + for template_node in level_node.find_content(NodeKind.TEMPLATE): + # German Wiktionary uses a `Wortart` template to define the POS + if template_node.template_name == "Wortart": + process_pos_section( + wxr, page_data, base_data, level_node, template_node + ) + return + + # Level 4 headings were introduced by fix_level_hierarchy_of_subsections() + # for subsections that are introduced by templates. + elif level_node.kind == NodeKind.LEVEL4: + for template_node in level_node.find_content(NodeKind.TEMPLATE): + section_name = template_node.template_name + wxr.wtp.start_subsection(section_name) + if section_name == "Bedeutungen": + for list_node in level_node.find_child(NodeKind.LIST): + extract_glosses(wxr, page_data, list_node) + + +FORM_POS = { + "Konjugierte Form", + "Deklinierte Form", + "Dekliniertes Gerundivum", + "Komparativ", + "Superlativ", + "Supinum", + "Partizip", + "Partizip I", + "Partizip II", + "Erweiterter Infinitiv", + "Adverbialpartizip", + "Exzessiv", + "Gerundium", +} + +IGNORE_POS = {"Albanisch", "Pseudopartizip", "Ajami"} + + +def process_pos_section( + wxr: WiktextractContext, + page_data: List[Dict], + base_data: Dict, + level_node: LevelNode, + pos_template_node: WikiNode, +) -> None: + # Extract the POS + pos_argument = pos_template_node.template_parameters.get(1) + if pos_argument in IGNORE_POS: + return + if pos_argument in FORM_POS: + # XXX: Extract form from form pages. Investigate first if this is needed + # at all or redundant with form tables. + return + + pos_type = wxr.config.POS_SUBTITLES.get(pos_argument) + + if pos_type is None: + wxr.wtp.debug( + f"Unknown POS type: {pos_argument}", + sortid="extractor/de/page/process_pos_section/55", + ) + return + pos = pos_type["pos"] + + wxr.wtp.start_section(page_data[-1]["lang_code"] + "_" + pos) + + base_data["pos"] = pos + append_base_data(page_data, "pos", pos, base_data) + + # There might be other templates in the level node that define grammatical + # features other than the POS. Extract them here. + for template_node in level_node.find_content(NodeKind.TEMPLATE): + template_name = template_node.template_name + + GENDER_TAGS_TEMPLATES = { + "m", + "f", + "f ", + "n", + "n ", + "mf", + "mn.", + "fn", + "fm", + "nf", + "nm", + "mfn", + "u", + "un", + "Geschlecht", # placeholder template + } + + VERB_TAGS_TEMPLATES = { + "unreg.", + "intrans.", + "trans.", + "refl.", + } + + ARAB_VERB_STEM_TEMPLATES = { + "Grundstamm", + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + } + + NOUN_TAGS_TEMPLATES = { + "adjektivische Deklination", + "kPl.", + "Pl.", + "mPl.", + "fPl.", + "nPl.", + "Sachklasse", + "Personenklasse", + "indekl.", + "Suaheli Klassen", + } + + if template_name == "Wortart": + continue + + elif template_name in GENDER_TAGS_TEMPLATES.union( + ARAB_VERB_STEM_TEMPLATES + ).union(NOUN_TAGS_TEMPLATES).union(VERB_TAGS_TEMPLATES): + # XXX: de: Extract additional grammatical markers + pass + + else: + wxr.wtp.debug( + f"Unexpected template in POS section heading: {template_node}", + sortid="extractor/de/page/process_pos_section/31", + ) + + subsections = fix_level_hierarchy_of_subsections(wxr, level_node.children) + + for subsection in subsections: + parse_section(wxr, page_data, base_data, subsection) + return + + +def parse_page( + wxr: WiktextractContext, page_title: str, page_text: str +) -> List[Dict[str, str]]: + if wxr.config.verbose: + logging.info(f"Parsing page: {page_title}") + + wxr.config.word = page_title + wxr.wtp.start_page(page_title) + + # Parse the page, pre-expanding those templates that are likely to + # influence parsing + tree = wxr.wtp.parse( + page_text, + pre_expand=True, + additional_expand=ADDITIONAL_EXPAND_TEMPLATES, + do_not_pre_expand=DO_NOT_PRE_EXPAND_TEMPLATES.update( + wxr.config.DE_FORM_TABLES + ), + ) + + page_data = [] + for node in filter(lambda n: isinstance(n, WikiNode), tree.children): + # ignore certain top level templates + if node.kind == NodeKind.TEMPLATE: + template_name = node.template_name + + # Mostly meta-templates at the top of the page that do not carry + # any semantic information + IGNORE_TOP_LEVEL_TEMPLATES = { + "Wort der Woche", + "Siehe auch", + "erweitern", + "Abschnitte fehlen", + "überarbeiten", + "Zeichen", + "Wortart fehlt", + "TOC limit", + "Neuer Eintrag", + "Löschantrag/Vorlage", + "keine Belegstelle/Vorlage", + "Anmerkung Keilschrift", + "In Arbeit", + "Halbgeschützte Seite", + "anpassen", + } + + if template_name in IGNORE_TOP_LEVEL_TEMPLATES: + continue + + # ignore certain top level magic words + if node.kind == NodeKind.MAGIC_WORD and node.sarg in { + "__TOC__", + "__NOTOC__", + "__NOEDITSECTION__", + }: + continue + + if node.kind != NodeKind.LEVEL2: + wxr.wtp.warning( + f"Unexpected top-level node: {node}", + sortid="extractor/de/page/parse_page/61", + ) + continue + + for subtitle_template in node.find_content(NodeKind.TEMPLATE): + # The language sections are marked with + # == ({{Sprache|<lang_name>}}) == + # where <title> is the title of the page and <lang_name> is the + # German name of the language of the section. + if subtitle_template.template_name == "Sprache": + lang_name = subtitle_template.template_parameters.get(1) + lang_code = wxr.config.LANGUAGES_BY_NAME.get(lang_name) + if not lang_code: + wxr.wtp.warning( + f"Unknown language: {lang_name}", + sortid="extractor/de/page/parse_page/76", + ) + continue + + base_data = defaultdict( + list, + { + "lang": lang_name, + "lang_code": lang_code, + "word": wxr.wtp.title, + }, + ) + page_data.append(copy.deepcopy(base_data)) + parse_section(wxr, page_data, base_data, node.children) + + return page_data