Repository: BoboTiG/ebook-reader-dict

Issue:
[DA] Support "etyl" template
- Wiktionary page: https://da.wiktionary.org/wiki/torn
- Model link: https://da.wiktionary.org/wiki/Skabelon:etyl

<!-- POLAR PLEDGE BADGE START -->
## Upvote & Fund

- We're using [Polar.sh](https://polar.sh/tiger-222) so you can upvote and help fund this issue.
- We receive the funding once the issue is completed & confirmed by you.
- Thank you in advance for helping prioritize & fund our backlog.

<a href="https://polar.sh/BoboTiG/ebook-reader-dict/issues/2087">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/BoboTiG/ebook-reader-dict/issues/2087/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/BoboTiG/ebook-reader-dict/issues/2087/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->


Discussion:

Patch:
diff --git a/scripts/__main__.py b/scripts/__main__.py
index e4af1119..b7731d13 100644
--- a/scripts/__main__.py
+++ b/scripts/__main__.py
@@ -11,6 +11,7 @@
     "all-namespaces.py": "wikidict/namespaces.py",
     "ca-labels.py": "wikidict/lang/ca/labels.py",
     "ca-langs.py": "wikidict/lang/ca/langs.py",
+    "da-langs.py": "wikidict/lang/da/langs.py",
     "de-abk.py": "wikidict/lang/de/abk.py",
     "de-langs.py": "wikidict/lang/de/langs.py",
     "de-lang_adjs.py": "wikidict/lang/de/lang_adjs.py",
diff --git a/scripts/da-langs.py b/scripts/da-langs.py
new file mode 100644
index 00000000..b8f6d0c0
--- /dev/null
+++ b/scripts/da-langs.py
@@ -0,0 +1,16 @@
+import re
+
+from scripts_utils import get_soup
+
+soup = get_soup("https://da.wiktionary.org/wiki/Modul:lang/data").text
+pattern = re.compile(r'data\["([^"]+)"\]\s+=\s+\{\s+name\s+=\s+"([^"]+)",')
+
+langs = re.findall(pattern, soup)
+
+# Missing langs
+langs.append(("non", "oldnordisk"))
+
+print("langs = {")
+for key, name in sorted(langs):
+    print(f'    "{key}": "{name}",')
+print(f"}}  # {len(langs):,}")
diff --git a/wikidict/lang/da/__init__.py b/wikidict/lang/da/__init__.py
index 442b9614..b3b13161 100644
--- a/wikidict/lang/da/__init__.py
+++ b/wikidict/lang/da/__init__.py
@@ -58,7 +58,6 @@
     "de",
     "dm",
     "-syn-",
-    "etyl",
     "wikipedia",
     "Wikipedia",
     "infl",
@@ -75,8 +74,6 @@
     "data": "'(' + italic('data') + ')'",
     # {{dublet af|da|boulevard}}
     "dublet af": "'dublet af ' + strong(parts[-1])",
-    # {{en}}
-    "en": "'Engelsk'",
     # {{form of|imperative form|bjerge|lang=da}}
     "form of": "italic(parts[1] + ' of') + ' ' + strong(parts[2])",
     # {{fysik}}
@@ -91,8 +88,6 @@
     "prefix": "parts[1] + '- + ' + parts[2]",
     # {{suffix|Norden|isk|lang=da}}
     "suffix": "parts[1] + ' + -' + parts[2]",
-    # {{term|mouse|lang=en}}
-    "term": "parts[1] + superscript('(' + parts[-1].lstrip('=lang') + ')')",
     # {{trad|en|limnology}}
     "trad": "parts[-1] + superscript('(' + parts[1] + ')')",
     # {{u|de|Reis}}
@@ -134,3 +129,48 @@ def find_pronunciations(code: str) -> list[str]:
     matches = re.findall(pattern, code) or []
 
     return [item for sublist in matches for item in sublist.split("|") if item]
+
+
+def last_template_handler(template: tuple[str, ...], locale: str, word: str = "") -> str:
+    """
+    Will be called in utils.py::transform() when all template handlers were not used.
+
+        >>> last_template_handler(["en"], "da")
+        'Engelsk'
+        >>> last_template_handler(["unknown"], "da")
+        '##opendoublecurly##unknown##closedoublecurly##'
+
+        >>> last_template_handler(["etyl", "fr", "da"], "da")
+        'fransk'
+        >>> last_template_handler(["etyl", "non", "da"], "da")
+        'oldnordisk'
+
+        >>> last_template_handler(["term", "mouse", "lang=en"], "da")
+        'mouse'
+        >>> last_template_handler(["term", "cabotage", "", "kysttransport", "lang=fr"], "da")
+        'cabotage (“‘kysttransport’”)'
+        >>> last_template_handler(["term", "αὐτός", "autós", "selv", "lang=grc"], "da")
+        'autós (“‘selv’”)'
+    """
+    from ...lang import defaults
+    from ...user_functions import capitalize, extract_keywords_from, term
+    from .langs import langs
+
+    tpl, *parts = template
+    extract_keywords_from(parts)
+
+    if tpl == "etyl":
+        return langs[parts[0]]
+
+    if tpl == "term":
+        if len(parts) == 3:
+            return f"{parts[1] or parts[0]} (“‘{parts[2]}’”)"
+        return parts[0]
+
+    if len(parts) == 1:
+        return term(tpl)
+
+    if not parts and (lang := langs.get(tpl)):
+        return capitalize(lang)
+
+    return defaults.last_template_handler(template, locale, word=word)
diff --git a/wikidict/lang/da/langs.py b/wikidict/lang/da/langs.py
new file mode 100644
index 00000000..cb647a3b
--- /dev/null
+++ b/wikidict/lang/da/langs.py
@@ -0,0 +1,274 @@
+"""
+List of languages.
+Auto-generated with `python -m scripts`.
+"""
+
+# START
+langs = {
+    "aa": "afar",
+    "ab": "abkhasisk",
+    "ae": "avestisk",
+    "af": "afrikaans",
+    "ak": "akan",
+    "akc": "alabama",
+    "akk": "akkadisk",
+    "als": "alemannisk",
+    "am": "amharisk",
+    "an": "aragonisk",
+    "ang": "oldengelsk",
+    "aqc": "artjinsk",
+    "ar": "arabisk",
+    "arc": "assyrisk neoaramæisk",
+    "arz": "egyptisk arabisk",
+    "ast": "asturiansk",
+    "ay": "aymara",
+    "az": "aserbajdsjansk",
+    "ba": "basjkirsk",
+    "ban": "balinesisk",
+    "bat-smg": "žemaitisk",
+    "bcl": "bikol",
+    "be": "hviderussisk",
+    "bg": "bulgarsk",
+    "bn": "bengali",
+    "bo": "tibetansk",
+    "br": "bretonsk",
+    "bs": "bosnisk",
+    "bug": "buginesisk",
+    "bxr": "burjatisk",
+    "ca": "catalansk",
+    "ccn": "urnordkaukasisk",
+    "ccs-pro": "urkartvelsk",
+    "ce": "tjetjensk",
+    "ceb": "cebuano",
+    "cel": "keltisk",
+    "chr": "cherokee",
+    "chs": "chumash",
+    "ckb": "sorani",
+    "co": "korsikansk",
+    "cop": "koptisk",
+    "crh": "krim-tatarisk",
+    "cs": "tjekkisk",
+    "csb": "kasjubisk",
+    "cu": "oldkirkeslavisk",
+    "cv": "tjuvasjisk",
+    "cy": "walisisk",
+    "da": "dansk",
+    "de": "tysk",
+    "dlm": "dalmatisk",
+    "dsb": "nedersorbisk",
+    "dum": "middelnederlandsk",
+    "dv": "dhivehi",
+    "dz": "dzongkha",
+    "egy": "egyptisk",
+    "el": "græsk",
+    "en": "engelsk",
+    "eo": "esperanto",
+    "es": "spansk",
+    "et": "estisk",
+    "eu": "baskisk",
+    "ew": "ewe",
+    "fa": "persisk",
+    "fab": "fa d'Ambu",
+    "fi": "finsk",
+    "fil": "filippinsk",
+    "fj": "fijiansk",
+    "fo": "færøsk",
+    "fr": "fransk",
+    "frk": "frankisk",
+    "frm": "middelalderfransk",
+    "fro": "oldfransk",
+    "frr": "nordfrisisk",
+    "fur": "friulisk",
+    "fy": "vestfrisisk",
+    "ga": "irsk",
+    "gd": "skotsk gælisk",
+    "gem-pro": "urgermansk",
+    "gl": "galicisk",
+    "gmh": "middelhøjtysk",
+    "gml": "middelnedertysk",
+    "gmq-oda": "gammeldansk",
+    "gmq-osw": "oldsvensk",
+    "gmq-pro": "urnordisk",
+    "gn": "guarani",
+    "goh": "oldhøjtysk",
+    "got": "gotisk",
+    "grc": "oldgræsk",
+    "gu": "gujarati",
+    "gv": "mansk",
+    "hak": "hakka",
+    "haw": "hawaiiansk",
+    "he": "hebræisk",
+    "hi": "hindi",
+    "hif": "fijiansk hindi",
+    "hr": "kroatisk",
+    "hsb": "øvresorbisk",
+    "ht": "haitiansk (kreol)",
+    "hu": "ungarsk",
+    "hy": "armensk",
+    "hyx-pro": "urarmensk",
+    "ia": "interlingua",
+    "id": "indonesisk",
+    "ie": "interlingue",
+    "ik": "inupiak",
+    "ine-pro": "urindoeuropæisk",
+    "io": "ido",
+    "is": "islandsk",
+    "it": "italiensk",
+    "iu": "inuktitut",
+    "ja": "japansk",
+    "jbo": "lojban",
+    "jv": "javanesisk",
+    "ka": "georgisk",
+    "kab": "kabylsk",
+    "kk": "kasakhisk",
+    "kky": "guugu yimidhirr",
+    "kl": "grønlandsk",
+    "km": "khmer",
+    "kn": "kannada",
+    "ko": "koreansk",
+    "kpg": "kapingamarangisk",
+    "ks": "kashmirsk",
+    "ku": "kurdisk",
+    "kum": "kumyk",
+    "kv": "komi",
+    "kw": "kornisk",
+    "ky": "kirgisisk",
+    "la": "latin",
+    "lb": "luxembourgsk",
+    "li": "limburgsk",
+    "lij": "ligurisk",
+    "lld": "ladinsk",
+    "lmo": "lombardisk",
+    "ln": "lingala",
+    "lng": "langobardisk",
+    "lo": "laotisk",
+    "lt": "litauisk",
+    "lv": "lettisk",
+    "mad": "maduresisk",
+    "mak": "makassarisk",
+    "map-pro": "uraustronesisk",
+    "mch": "maquiritari",
+    "mfe": "morisyen",
+    "mg": "malagassisk",
+    "mh": "marshallesisk",
+    "mi": "maori",
+    "mk": "makedonsk",
+    "ml": "malayalam",
+    "mn": "mongolsk",
+    "mnc": "manchu",
+    "mo": "moldovisk",
+    "mr": "marathi",
+    "ms": "malajisk",
+    "mt": "maltesisk",
+    "mul": "tværsprogligt",
+    "my": "burmesisk",
+    "myn": "maya",
+    "na": "naurisk",
+    "nah": "nahuatl",
+    "nan": "min nan",
+    "nb": "norsk bokmål",
+    "nds": "plattysk",
+    "nds-nl": "hviderussisk",
+    "nhd": "nyhøjtysk",
+    "nl": "nederlandsk",
+    "nn": "nynorsk",
+    "no": "norsk",
+    "non": "oldnordisk",
+    "nov": "novial",
+    "nrf": "normannisk",
+    "nv": "navajo",
+    "ny": "chichewa",
+    "oc": "occitansk",
+    "odt": "oldnederlandsk",
+    "ofs": "oldfrisisk",
+    "ood": "o'odham",
+    "or": "oriya",
+    "os": "ossetisk",
+    "osx": "oldsaksisk",
+    "pa": "punjabi",
+    "pcd": "pikardisk",
+    "pdc": "pennsylvania dutch",
+    "pi": "pali",
+    "pih": "norfuk",
+    "pl": "polsk",
+    "pms": "piemontesisk",
+    "pnb": "vestlig punjabisk",
+    "pox": "polabisk",
+    "pro": "gammelprovencalsk",
+    "prv": "provencalsk",
+    "ps": "pashto",
+    "pt": "portugisisk",
+    "qu": "quechua",
+    "rap": "rapanui",
+    "rm": "rætoromansk",
+    "ro": "rumænsk",
+    "roa-jer": "jèrriais",
+    "roa-opt": "oldportugisisk",
+    "roa-rup": "aromunsk",
+    "rom": "romani",
+    "ru": "russisk",
+    "rw": "kinyarwanda",
+    "sa": "sanskrit",
+    "sah": "jakutisk",
+    "sc": "sardinsk",
+    "scn": "siciliansk",
+    "sco": "skotsk",
+    "sd": "sindhi",
+    "se": "nordsamisk",
+    "sem-pro": "ursemitisk",
+    "sg": "sango",
+    "sh": "serbokroatisk",
+    "si": "sinhalesisk",
+    "sk": "slovakisk",
+    "sl": "slovensk",
+    "sla-pro": "urslavisk",
+    "sm": "samoansk",
+    "sn": "shona",
+    "so": "somalisk",
+    "sq": "albansk",
+    "sr": "serbisk",
+    "ss": "swati",
+    "stq": "saterfrisisk",
+    "su": "sundansk",
+    "sv": "svensk",
+    "sw": "swahili",
+    "syc": "syrisk",
+    "szl": "schlesisk",
+    "ta": "tamilsk",
+    "te": "telugu",
+    "tet": "tetum",
+    "tg": "tadsjikisk",
+    "th": "thailandsk",
+    "tk": "turkmensk",
+    "tl": "tagalog",
+    "tnq": "taino",
+    "tpi": "tok pisin",
+    "tpn": "tupinambá",
+    "tr": "tyrkisk",
+    "tt": "tatarisk",
+    "tw": "twi",
+    "ty": "tahitisk",
+    "ug": "uigurisk",
+    "uk": "ukrainsk",
+    "ulk": "meriam",
+    "ur": "urdu",
+    "urj-pro": "ururalsk",
+    "uz": "usbesisk",
+    "vec": "venetiansk",
+    "vi": "vietnamesisk",
+    "vls": "vestflamsk",
+    "vo": "volapyk",
+    "wa": "vallonsk",
+    "wo": "wolof",
+    "wym": "vimisørisk",
+    "xal": "kalmykisk",
+    "xcl": "oldarmensk",
+    "xls": "lusitansk",
+    "yi": "jiddisch",
+    "zai": "landtangezapotekansk",
+    "zh": "kinesisk",
+    "zlw-slv": "slovincinsk",
+    "zu": "zulu",
+    "zza": "zazaki",
+}  # 265
+# END
diff --git a/wikidict/lang/defaults.py b/wikidict/lang/defaults.py
index 2047fe2c..c2463527 100644
--- a/wikidict/lang/defaults.py
+++ b/wikidict/lang/defaults.py
@@ -79,10 +79,7 @@ def last_template_handler(template: tuple[str, ...], locale: str, word: str = ""
 
     # {{tpl|item}} -> <i>(Templatet gf)</i>
     if len(template) == 2:
-        ret = lookup_italic(tpl, locale)
-        if locale != "da":
-            ret = capitalize(ret)
-        return term(ret)
+        return term(capitalize(lookup_italic(tpl, locale)))
 
     if italic := lookup_italic(tpl, locale, empty_default=True):
         return term(capitalize(italic))
diff --git a/wikidict/render.py b/wikidict/render.py
index d6c5a59b..9d3f6e0b 100644
--- a/wikidict/render.py
+++ b/wikidict/render.py
@@ -148,6 +148,11 @@ def find_etymology(word: str, locale: str, parsed_section: wtp.Section) -> list[
         definitions.append(process_templates(word, parsed_section.contents, locale))
         return definitions
 
+    elif locale == "da":
+        if def_list := parsed_section.get_lists(pattern=("#",)):
+            return [etyl for item in def_list[0].items if (etyl := process_templates(word, item, locale))]
+        return [process_templates(word, parsed_section.contents, locale)]
+
     elif locale == "en":
         items = [
             item
@@ -181,16 +186,12 @@ def find_etymology(word: str, locale: str, parsed_section: wtp.Section) -> list[
                 etyl = parsed_section.get_lists(pattern=("",))[0].items[1]
         definitions.append(process_templates(word, etyl, locale))
         return definitions
+
     elif locale == "ru":
         section_title = parsed_section.title.strip()
         if section_title == "Этимология":
             definitions.append(process_templates(word, parsed_section.contents, locale))
         return definitions
-    elif locale == "da":
-        section_title = parsed_section.title.strip()
-        if section_title in {"{{etym}}", "Etymologi"}:
-            definitions.append(process_templates(word, parsed_section.contents, locale))
-        return definitions
 
     tables = parsed_section.tables
     tableindex = 0


Test patch:
diff --git a/tests/test_da.py b/tests/test_da.py
index 906291ba..1d5ca26e 100644
--- a/tests/test_da.py
+++ b/tests/test_da.py
@@ -8,34 +8,42 @@
 
 
 @pytest.mark.parametrize(
-    "word, pronunciations, definitions, variants",
+    "word, pronunciations, etymology, definitions, variants",
     [
         (
             "hund",
             ["[ˈhunə-]", "[ˈhunˀ]"],
+            [
+                "Menes at stamme fra indoeuropæisk sprog <i>ḱʷn̥tós</i>, fra <i>ḱwṓ</i> og derfra videre til germansk sprog <i>*hundaz</i> og fra oldnordisk hundr."
+            ],
             [
                 "(<i>zoologi</i>): et pattedyr af underarten <i>Canis lupus familiaris</i>.",
                 "(<i>slang</i>): 100 DKK-seddel (bruges ikke i flertal)",
             ],
             [],
         ),
-        ("jørme", [], ["vrimle, myldre; sværme"], []),
+        ("jørme", [], [], ["vrimle, myldre; sværme"], []),
         (
             "mus",
             [],
+            [
+                "Fra oldnordisk mús.",
+                "Fra engelsk mouse.",
+            ],
             [
                 "(<i>zoologi</i>) pattedyr",
                 "(<i>data</i>) en enhed som tilsluttes computere",
             ],
             [],
         ),
-        ("skulle", [], ["Er nødt til at gøre. Forpligtet til at gøre."], []),
-        ("PMV", [], ["<i>(militær)</i> <i>Forkortelser på</i> <b>pansret mandskabsvogn</b>"], []),
+        ("skulle", [], [], ["Er nødt til at gøre. Forpligtet til at gøre."], []),
+        ("PMV", [], [], ["<i>(militær)</i> <i>Forkortelser på</i> <b>pansret mandskabsvogn</b>"], []),
     ],
 )
 def test_parse_word(
     word: str,
     pronunciations: list[str],
+    etymology: list[Definitions],
     definitions: list[Definitions],
     variants: list[str],
     page: Callable[[str, str], str],
@@ -45,6 +53,7 @@ def test_parse_word(
     details = parse_word(word, code, "da", force=True)
     assert pronunciations == details.pronunciations
     assert definitions == details.definitions
+    assert etymology == details.etymology
     assert variants == details.variants
 
 
@@ -55,7 +64,6 @@ def test_parse_word(
         ("{{abbreviation of|lang=da|pansret mandskabsvogn}}", "<i>Forkortelser på</i> <b>pansret mandskabsvogn</b>"),
         ("{{com|hjemme|værn|langa=da}}", "hjemme + værn"),
         ("{{compound|hjemme|værn|langa=da}}", "hjemme + værn"),
-        ("{{en}}", "Engelsk"),
         ("{{form of|imperative form|bjerge|lang=da}}", "<i>imperative form of</i> <b>bjerge</b>"),
         ("{{fysik}}", "(<i>fysik</i>)"),
         ("{{init of|lang=da|København}}", "<i>Initialforkortelse af</i> <b>København</b>"),
@@ -63,7 +71,6 @@ def test_parse_word(
         ("{{label|militær|våben}}", "(<i>militær</i>, <i>våben</i>)"),
         ("{{suf|Norden|isk|lang=da}}", "Norden + -isk"),
         ("{{suffix|Norden|isk|lang=da}}", "Norden + -isk"),
-        ("{{term|mouse|lang=en}}", "mouse<sup>(en)</sup>"),
         ("{{trad|en|limnology}}", "limnology<sup>(en)</sup>"),
         ("{{u|de|Reis}}", "Reis<sup>(de)</sup>"),
     ],