From af08d429f9d023d8e6873e5f0ada5cd6fe055509 Mon Sep 17 00:00:00 2001 From: Asterios Raptis Date: Thu, 1 Oct 2026 13:11:00 +0200 Subject: [PATCH 1/2] chore(tooling): the prose gate from the template (template#95) Copied byte for byte from adaptive-learner-content-template 97a2dcc: typing variants in accept, YAML tags, the surnames Weiss and Gross, French grosse and code syntax in plain-text fields are no longer reported as misspellings. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/quality-state.json | 7 --- scripts/check_prose.py | 84 +++++++++++++++++++++++++++++++++-- tests/test_check_prose.py | 89 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 11 deletions(-) delete mode 100644 .github/quality-state.json diff --git a/.github/quality-state.json b/.github/quality-state.json deleted file mode 100644 index a9745a3..0000000 --- a/.github/quality-state.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "prose_gate": { - "blocking": false, - "since": "2026-09-23", - "reason": "Rückstand aus der Zeit vor dem Gate: 169 Em-Dashes und 19 Ersatzschreibungen für Umlaute in 33 Dateien, gemessen am 2026-09-23 mit den Wörterbuch-Daten des Templates (adaptive-learner-content-template#75). Der Rückstand liegt in Dateien, die dem Repo gehören (Lektionen, Manifeste, eigene Doku und Tests), und wird im Inhaltsdurchgang dieses Repos abgebaut; danach wird hier blocking auf true gesetzt." - } -} diff --git a/scripts/check_prose.py b/scripts/check_prose.py index d70edd8..f75b20b 100755 --- a/scripts/check_prose.py +++ b/scripts/check_prose.py @@ -36,8 +36,16 @@ Code is exempt from the umlaut check, and only from that one. An identifier is spelled by whoever wrote it: "laeuft" as a variable name is a choice, "laeuft" in a sentence is a misspelling. In a lesson file that means the code-bearing -fields (``passage``, ``sentence``, ``tokens``, ``code``) and the fenced blocks -inside a theory body; in Markdown it means the fenced blocks. +fields (``passage``, ``sentence``, ``tokens``, ``code``), a string ending in a +semicolon (a code line offered as an option) and the fenced blocks inside a +theory body; in Markdown it means the fenced blocks. + +Machine values and typing variants are not prose either: the id fields of a +lesson file, the ``tags`` of a lesson or a YAML file, and every ``accept`` +entry after the first. The app shows ``accept[0]`` as the solution; the later +entries only catch what a learner types, an ASCII spelling for a keyboard +without umlauts among them. A surname that is also a German word written +without its sharp s goes into ``PROPER_NAMES``, matched case-sensitively. This file, its test, the stem generator and its data are exempt from the umlaut check too, for the same reason the banned characters are built from @@ -110,8 +118,40 @@ def _pair_decisions(stem: str, correction: str) -> dict[int, str]: "neisser", "jinaesseo", "seonsaengnim", + # French "gros -> grosse", "Grosses bises" (adaptive-learner-content, fr-a2 + # and en-b1, 2026-10-01). The cost: these two German misspellings of + # "große"/"großes" are no longer caught as whole words; "grossen", + # "grosser" and every compound still are. + "grosse", + "grosses", +} + +# Surnames that are also a German word written without its sharp s. They are +# matched case-sensitively, so only the capitalised name is exempt and the +# lower-case word ("ich weiss", "gross") stays a finding. Found by: +# alc-psychology, "Hovland & Weiss" and James Gross's process model +# (2026-10-01). +PROPER_NAMES = { + "Weiss", + "Gross", } +# Code syntax German prose never uses, in fields that are plain text and so +# carry code without backticks (a card's back, a hint): a subscript +# (d['key']), an increment (n++), a tag ( list[tuple[str, str]]: umlauts in ``text``.""" findings = [] text = INLINE_CODE.sub(" ", text) + text = CODE_SYNTAX.sub(" ", text) text = TECHNICAL_TOKEN.sub( lambda match: " " if match.group(0) == match.group(0).lower() else match.group(0).replace("_", " "), text, ) for word in WORD.findall(text): lowered = word.lower() - if lowered in FOREIGN_LOOKALIKES or _is_identifier(word): + if lowered in FOREIGN_LOOKALIKES or word in PROPER_NAMES or _is_identifier(word): continue if lowered in WHOLE_WORDS: suggestion = WHOLE_WORDS[lowered] @@ -289,6 +330,8 @@ def prose_segments(path: str, text: str) -> list[tuple[int, str]]: line_no = next((i for i, line in enumerate(lines, start=1) if head[:60] in line), 0) segments.append((line_no, _outside_fences(value))) return segments + if path.endswith((".yaml", ".yml")): + return _yaml_segments(text) if path.endswith(".md"): segments = [] in_fence = False @@ -302,6 +345,31 @@ def prose_segments(path: str, text: str) -> list[tuple[int, str]]: return list(enumerate(text.splitlines(), start=1)) +def _yaml_segments(text: str) -> list[tuple[int, str]]: + """(line number, prose) for a YAML file: every line except the values of a + ``tags`` key. A comment inside a tags block is prose.""" + segments = [] + tags_indent = None + for number, line in enumerate(text.splitlines(), start=1): + stripped = line.lstrip() + if tags_indent is not None: + if not stripped or stripped.startswith("#"): + segments.append((number, line)) + continue + if stripped.startswith("-") and len(line) - len(stripped) >= tags_indent: + continue + tags_indent = None + match = YAML_TAGS.match(line) + if match is None: + segments.append((number, line)) + continue + value = match.group(2) + if not value or value.startswith("#"): + tags_indent = len(match.group(1)) + segments.append((number, value)) + return segments + + def _walk_json(node, in_code: bool, out: list[str]) -> None: if isinstance(node, dict): # An inline example is prose when it is a sample sentence and code when @@ -309,11 +377,19 @@ def _walk_json(node, in_code: bool, out: list[str]) -> None: example_is_code = bool(node.get("language")) and "content" in node for key, value in node.items(): child_is_code = in_code or key in CODE_KEYS or (example_is_code and key == "content") + if key == "accept" and isinstance(value, list): + # The app shows accept[0] as the solution; the later entries + # only catch what a learner types, an ASCII spelling for a + # keyboard without umlauts among them. + _walk_json(value[:1], child_is_code, out) + continue _walk_json(value, child_is_code, out) elif isinstance(node, list): for value in node: _walk_json(value, in_code, out) - elif isinstance(node, str) and not in_code: + elif isinstance(node, str) and not in_code and not node.rstrip().endswith(";"): + # A string ending in ";" is a code statement (a select cloze offering + # code lines); prose does not end a field with a semicolon. out.append(node) diff --git a/tests/test_check_prose.py b/tests/test_check_prose.py index 2840725..10fc9a5 100644 --- a/tests/test_check_prose.py +++ b/tests/test_check_prose.py @@ -289,6 +289,95 @@ def test_inline_code_spans_are_not_prose(): assert [w for w, _ in check_prose.substituted_words("`x` ist fuer dich")] == ["fuer"] +def _scanned(path: str, text: str) -> str: + return " ".join(segment for _, segment in check_prose.prose_segments(path, text)) + + +def test_accept_entries_after_the_first_are_typing_variants(): + # The app shows accept[0] as the solution; the later entries only catch + # what a learner types, and an ASCII spelling is there on purpose for a + # keyboard without umlauts. The first entry is still prose. + lesson = json.dumps( + { + "steps": [ + {"exercise": {"accept": ["Überzeugung", "Ueberzeugung"]}}, + {"exercise": {"blanks": [{"accept": ["Domänenwissen", "Fachwissen", "Domaenenwissen"]}]}}, + {"exercise": {"accept": ["Uebersicht", "Übersicht"]}}, + ] + }, + ensure_ascii=False, + ) + scanned = _scanned("lesson.json", lesson) + assert "Überzeugung" in scanned and "Domänenwissen" in scanned + assert "Ueberzeugung" not in scanned and "Domaenenwissen" not in scanned + assert "Uebersicht" in scanned + + +def test_a_statement_ending_in_a_semicolon_is_code(): + # A select cloze can offer code lines as options; an identifier in them is + # spelled by whoever wrote it. Prose does not end a field with ";". + lesson = json.dumps( + {"exercise": {"distractors": ["zaehler++;", "zaehler = zaehler + 1;", "Der Zaehler steigt"]}}, + ensure_ascii=False, + ) + scanned = _scanned("lesson.json", lesson) + assert "zaehler++;" not in scanned and "zaehler = zaehler + 1;" not in scanned + assert "Der Zaehler steigt" in scanned + + +def test_yaml_tags_are_machine_values(): + # Tags are slugs, ASCII on purpose, as in a lesson file's "tags". + text = ( + "sets:\n" + " - id: hunde\n" + " tags:\n" + " - anfaenger\n" + " # Kommentar fuer Menschen\n" + " - hund\n" + " description: Fuer alle\n" + "books:\n" + " - tags: [\"koerpersprache\", \"hund\"]\n" + "tags:\n" + "- uebung\n" + "title: Zurueck\n" + ) + segments = check_prose.prose_segments("manifest.yaml", text) + scanned = " ".join(segment for _, segment in segments) + assert "anfaenger" not in scanned and "koerpersprache" not in scanned and "uebung" not in scanned + hits = [(n, w) for n, seg in segments for w, _ in check_prose.substituted_words(seg)] + assert hits == [(5, "fuer"), (7, "Fuer"), (12, "Zurueck")] + + +def test_a_capitalised_surname_is_not_a_misspelling(): + # Hovland & Weiss (1951), James Gross: the surnames are spelled that way. + # Only the capitalised form is exempt, so the German words stay findings. + assert check_prose.substituted_words("nach Hovland & Weiss und James Gross") == [] + assert [w for w, _ in check_prose.substituted_words("ich weiss es nicht")] == ["weiss"] + assert [w for w, _ in check_prose.substituted_words("ein gross angelegter Test")] == ["gross"] + + +def test_french_grosse_is_a_foreign_lookalike(): + # French "gros -> grosse", "Grosses bises" in the hub's language sets. + assert check_prose.substituted_words("gros devient grosse; Grosses bises") == [] + + +def test_unmistakable_code_syntax_in_a_plain_text_field_is_not_prose(): + # Card texts are plain text, so code in them carries no backticks. A + # subscript, an increment, a tag, a JSX expression and a method call + # are syntax German prose never uses. + for snippet in ( + "Wert abrufen - d['schluessel']", + "wenn man mit d[\"schluessel\"] zugreift", + "z. B. zaehler++ oder array.push", + "Props wie Attribute: ", + "title={gruss} setzt den Wert ein", + ".get(schluessel) gibt den Wert", + ): + assert check_prose.substituted_words(snippet) == [], snippet + # Parentheses alone are prose. + assert [w for w, _ in check_prose.substituted_words("ein Satz (fuer alle)")] == ["fuer"] + + def test_the_generated_search_index_is_out_of_scope(): assert "search-index.json" in check_prose.EXCLUDED_FILES From 3019ec32736cc52f7481f95ee7fff40a26e0b549 Mon Sep 17 00:00:00 2001 From: Asterios Raptis Date: Thu, 1 Oct 2026 13:11:00 +0200 Subject: [PATCH 2/2] fix(prose): work off the prose backlog Em dashes replaced by the strategy of adaptive-learner-content#201 (comma for an aside, a hyphen in titles, headings, card fronts and after code, a colon after a bold list term); real umlaut substitutes fixed at the reported line; the export test derives the misspelling it asserts against from the correct word instead of holding it. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/export-set-usage.de.md | 6 ++--- manifest.yaml | 14 +++++------ .../lessons/01-erste-schritte.json | 18 +++++++------- .../lessons/02-variablen-und-datentypen.json | 10 ++++---- .../lessons/03-zahlen-und-operatoren.json | 20 ++++++++-------- sets/de/python-basics/lessons/04-strings.json | 14 +++++------ sets/de/python-basics/lessons/05-listen.json | 24 +++++++++---------- .../python-basics/lessons/06-bedingungen.json | 12 +++++----- .../python-basics/lessons/07-schleifen.json | 22 ++++++++--------- .../python-basics/lessons/08-funktionen.json | 10 ++++---- .../lessons/09-dictionaries.json | 22 ++++++++--------- .../lessons/10-tupel-und-mengen.json | 24 +++++++++---------- .../lessons/11-fehler-und-ausnahmen.json | 20 ++++++++-------- .../lessons/12-module-und-imports.json | 22 ++++++++--------- sets/de/python-basics/lessons/13-dateien.json | 24 +++++++++---------- .../lessons/14-comprehensions.json | 14 +++++------ .../lessons/15-wiederholung.json | 6 ++--- sets/de/python-basics/manifest.yaml | 10 ++++---- .../lessons/01-useeffect-verstehen.json | 10 ++++---- .../lessons/02-datenfetching.json | 6 ++--- .../lessons/03-usecontext.json | 4 ++-- .../lessons/04-useref.json | 2 +- .../lessons/05-performance.json | 4 ++-- .../lessons/06-listen-keys.json | 4 ++-- .../lessons/07-formulare.json | 4 ++-- .../lessons/08-komposition-patterns.json | 4 ++-- sets/de/react-fortgeschritten/manifest.yaml | 10 ++++---- sets/de/react-grundlagen/lessons/02-jsx.json | 2 +- .../lessons/04-state-usestate.json | 2 +- .../05-events-und-grundprinzipien.json | 2 +- tests/test_export_set.py | 2 +- 31 files changed, 174 insertions(+), 174 deletions(-) diff --git a/docs/export-set-usage.de.md b/docs/export-set-usage.de.md index 3a2892a..30142b3 100644 --- a/docs/export-set-usage.de.md +++ b/docs/export-set-usage.de.md @@ -31,7 +31,7 @@ python3 scripts/export_set.py [--lang ] [--format yaml|json] [- | `--lang` | Quellsprachen-Verzeichnis (`sets//`), das einen Ordnernamen-Slug eindeutig macht, der unter mehreren Quellsprachen existiert | `de` | | `--format` | Ausgabeformat: `yaml` oder `json` | `yaml` | | `--out` | Pfad der Ausgabedatei (nicht kombinierbar mit `--split-size`) | `exports/--.` | -| `--split-size` | Export in mehrere Dateien von je hoechstens N Lektionen aufteilen, statt einer Datei | aus (eine Datei) | +| `--split-size` | Export in mehrere Dateien von je höchstens N Lektionen aufteilen, statt einer Datei | aus (eine Datei) | Beispiele: @@ -56,10 +56,10 @@ Das Verzeichnis `exports/` wird bei Bedarf angelegt und ist **gitignored**: Exportdateien sind Wegwerf-Artefakte fürs Review und werden nie committet. -Jeder von `--split-size` geschriebene Teil ist eigenstaendig: er traegt +Jeder von `--split-size` geschriebene Teil ist eigenständig: er trägt seine eigene `review_instructions`-Kopie sowie die Felder `part`/`of`/`lesson_count`/`total_lesson_count`, sodass jeder einzelne -Teil fuer sich, in beliebiger Reihenfolge, an eine KI zum Review +Teil für sich, in beliebiger Reihenfolge, an eine KI zum Review gegeben werden kann. Ein unbekannter oder mehrdeutiger Slug bricht mit Exit-Code 2 und diff --git a/manifest.yaml b/manifest.yaml index 3d5c8c1..bf532f5 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,9 +1,9 @@ -# Wurzel-Manifest von alc-programming — die Programmier-Reihe. +# Wurzel-Manifest von alc-programming - die Programmier-Reihe. # # Adaptive-Learner-Content-Repo für Programmierung. Drei deutschsprachige # Sets (domain: programming) nach aufsteigendem Niveau: # -# python-basics Python — Grundlagen (A1, 15 Lektionen) +# python-basics Python - Grundlagen (A1, 15 Lektionen) # react-grundlagen React-Grundlagen (Einsteiger) (A2, 5 Lektionen) # react-fortgeschritten React Fortgeschritten (Teil 2) (B1, 8 Lektionen) # react-19 React 19 in der Praxis (Teil 3) (B2, 8 Lektionen) @@ -18,15 +18,15 @@ # # Anleitung: docs/GETTING-STARTED.md schema_version: '1.5' -name: Adaptive Learner — Programmierung +name: Adaptive Learner - Programmierung description: >- Programmier-Inhalte für Adaptive Learner: Python-Grundlagen für Einsteiger sowie eine zweiteilige React-Reihe (Grundlagen und Fortgeschritten), alle auf Deutsch erklärt. sets: - id: python-basics-from-de - title: Python — Grundlagen - title_native: Python — Basics + title: Python - Grundlagen + title_native: Python - Basics target_language: de source_language: de domain: programming @@ -79,8 +79,8 @@ sets: lesson_count: 8 description: >- Fortsetzung von „React-Grundlagen" (Teil 2, Fortgeschritten, B1): acht - Lektionen mit Schwerpunkt Multiple-Choice — Single-Answer als cloze - select-mode und „Wähle alle zutreffenden" als cloze multiselect — plus + Lektionen mit Schwerpunkt Multiple-Choice, Single-Answer als cloze + select-mode und „Wähle alle zutreffenden" als cloze multiselect, plus matching und kurze Lückentext-Items. Themen: useEffect, Datenfetching, useContext, useRef, Performance, Listen/Keys, Formulare, Komposition und Patterns. React-18, Hooks-first, konzeptuell. diff --git a/sets/de/python-basics/lessons/01-erste-schritte.json b/sets/de/python-basics/lessons/01-erste-schritte.json index c2ea0f1..c595ef5 100644 --- a/sets/de/python-basics/lessons/01-erste-schritte.json +++ b/sets/de/python-basics/lessons/01-erste-schritte.json @@ -1,7 +1,7 @@ { "id": "01-erste-schritte", "title": "Erste Schritte mit Python", - "description": "Was Python ist, dein erstes Programm, print(), Kommentare und typische Anfängerfehler — auf Deutsch erklärt.", + "description": "Was Python ist, dein erstes Programm, print(), Kommentare und typische Anfängerfehler, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -73,7 +73,7 @@ "id": "py-string", "stable_id": "card-ms942h7d006vw", "front": "String (str)", - "back": "Eine Zeichenkette — Text, der in Anführungszeichen steht.", + "back": "Eine Zeichenkette, Text, der in Anführungszeichen steht.", "code_snippet": "name = 'Anna'\nprint(name)", "code_language": "python", "expected_output": "Anna", @@ -87,7 +87,7 @@ "id": "py-comment", "stable_id": "card-ms942h7d007vc", "front": "Kommentar (#)", - "back": "Text nach einem # wird vom Interpreter ignoriert — er dient nur dem Menschen.", + "back": "Text nach einem # wird vom Interpreter ignoriert, er dient nur dem Menschen.", "code_snippet": "# Das ist ein Kommentar\nprint('Hi')", "code_language": "python", "expected_output": "Hi", @@ -101,7 +101,7 @@ "id": "py-quotes", "stable_id": "card-ms942h7d008h1", "front": "Anführungszeichen", - "back": "Strings stehen in einfachen ' ' oder doppelten \" \" Anführungszeichen — Hauptsache, beide Seiten sind gleich.", + "back": "Strings stehen in einfachen ' ' oder doppelten \" \" Anführungszeichen, Hauptsache, beide Seiten sind gleich.", "code_snippet": "print(\"Tschüss\")", "code_language": "python", "expected_output": "Tschüss", @@ -116,7 +116,7 @@ "stable_id": "card-ms942h7d0098l", "front": "SyntaxError", "back": "Fehlermeldung, wenn der Code grammatikalisch falsch ist, z. B. bei einer fehlenden Klammer.", - "notes": "Python zeigt die Zeile mit dem Problem an — sehr hilfreich beim Suchen.", + "notes": "Python zeigt die Zeile mit dem Problem an, sehr hilfreich beim Suchen.", "media_type": "text", "difficulty": 2, "tags": ["fehler"] @@ -136,20 +136,20 @@ { "id": "intro", "type": "theory", - "title": "Was ist Python — und dein erstes Programm", - "body": "# Erste Schritte mit Python\n\n**Python** ist eine **Programmiersprache**: eine Sprache, in der wir einem Computer Anweisungen geben. Python gilt als besonders gut lesbar und einsteigerfreundlich — der Code sieht fast wie englischer Text aus.\n\nDas Programm, das deinen Code ausführt, heißt **Interpreter**. Er liest jede Zeile und führt sie sofort aus.\n\nDas klassische erste Programm gibt einen Gruß aus:\n\n```python\nprint('Hallo Welt')\n```\n\nAusgabe:\n\n```\nHallo Welt\n```\n\nHerzlichen Glückwunsch — das ist ein vollständiges Python-Programm." + "title": "Was ist Python - und dein erstes Programm", + "body": "# Erste Schritte mit Python\n\n**Python** ist eine **Programmiersprache**: eine Sprache, in der wir einem Computer Anweisungen geben. Python gilt als besonders gut lesbar und einsteigerfreundlich, der Code sieht fast wie englischer Text aus.\n\nDas Programm, das deinen Code ausführt, heißt **Interpreter**. Er liest jede Zeile und führt sie sofort aus.\n\nDas klassische erste Programm gibt einen Gruß aus:\n\n```python\nprint('Hallo Welt')\n```\n\nAusgabe:\n\n```\nHallo Welt\n```\n\nHerzlichen Glückwunsch, das ist ein vollständiges Python-Programm." }, { "id": "step-print", "type": "theory", "title": "Text ausgeben mit print()", - "body": "## Die Funktion print()\n\n**print()** ist die wichtigste Funktion am Anfang: Sie gibt etwas auf der **Konsole** aus.\n\n- Der auszugebende Text steht in **Anführungszeichen** — einfach `' '` oder doppelt `\" \"`.\n- Mehrere Werte werden durch **Kommas** getrennt; Python setzt automatisch ein Leerzeichen dazwischen:\n\n```python\nprint('Hallo', 'Welt')\n```\n\nAusgabe:\n\n```\nHallo Welt\n```\n\nText in Anführungszeichen nennt man einen **String** (`str`)." + "body": "## Die Funktion print()\n\n**print()** ist die wichtigste Funktion am Anfang: Sie gibt etwas auf der **Konsole** aus.\n\n- Der auszugebende Text steht in **Anführungszeichen**, einfach `' '` oder doppelt `\" \"`.\n- Mehrere Werte werden durch **Kommas** getrennt; Python setzt automatisch ein Leerzeichen dazwischen:\n\n```python\nprint('Hallo', 'Welt')\n```\n\nAusgabe:\n\n```\nHallo Welt\n```\n\nText in Anführungszeichen nennt man einen **String** (`str`)." }, { "id": "step-fehler", "type": "theory", "title": "Kommentare und typische Fehler", - "body": "## Kommentare\n\nMit dem Zeichen **#** schreibt man einen **Kommentar**. Alles nach `#` wird vom Interpreter ignoriert — Kommentare sind nur für Menschen da:\n\n```python\n# Das hier wird ignoriert\nprint('Hi') # auch das nach dem Code\n```\n\n## Zwei häufige Anfängerfehler\n\n- **SyntaxError** — der Code ist grammatikalisch falsch, z. B. eine **Klammer** oder ein **Anführungszeichen** fehlt.\n- **Einrückung (Indentation)** — Python nutzt Leerzeichen am Zeilenanfang, um Blöcke zu strukturieren. In dieser Lektion reicht es zu wissen, dass Einrückung in Python eine Bedeutung hat." + "body": "## Kommentare\n\nMit dem Zeichen **#** schreibt man einen **Kommentar**. Alles nach `#` wird vom Interpreter ignoriert, Kommentare sind nur für Menschen da:\n\n```python\n# Das hier wird ignoriert\nprint('Hi') # auch das nach dem Code\n```\n\n## Zwei häufige Anfängerfehler\n\n- **SyntaxError**: der Code ist grammatikalisch falsch, z. B. eine **Klammer** oder ein **Anführungszeichen** fehlt.\n- **Einrückung (Indentation)**: Python nutzt Leerzeichen am Zeilenanfang, um Blöcke zu strukturieren. In dieser Lektion reicht es zu wissen, dass Einrückung in Python eine Bedeutung hat." }, { "id": "ex-cloze-print", diff --git a/sets/de/python-basics/lessons/02-variablen-und-datentypen.json b/sets/de/python-basics/lessons/02-variablen-und-datentypen.json index c45726e..c90724e 100644 --- a/sets/de/python-basics/lessons/02-variablen-und-datentypen.json +++ b/sets/de/python-basics/lessons/02-variablen-und-datentypen.json @@ -1,7 +1,7 @@ { "id": "02-variablen-und-datentypen", "title": "Variablen und Datentypen", - "description": "Variablen anlegen und überschreiben, die Grundtypen int, float, str und bool, die Funktion type() und Namensregeln — auf Deutsch erklärt.", + "description": "Variablen anlegen und überschreiben, die Grundtypen int, float, str und bool, die Funktion type() und Namensregeln, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -12,7 +12,7 @@ "stable_id": "card-ms942h7f00inq", "front": "Variable", "back": "Ein benannter Speicherplatz, der einen Wert enthält. Mit = wird ihr ein Wert zugewiesen.", - "notes": "Der Name einer Variablen ist frei wählbar — solange er den Namensregeln folgt.", + "notes": "Der Name einer Variablen ist frei wählbar, solange er den Namensregeln folgt.", "media_type": "text", "difficulty": 1, "tags": ["grundlagen", "variablen"] @@ -147,7 +147,7 @@ "id": "intro-variablen", "type": "theory", "title": "Variablen und Zuweisung", - "body": "# Variablen und Datentypen\n\nEine **Variable** ist ein benannter Speicherplatz. Mit dem **Zuweisungsoperator `=`** speichert man einen Wert unter einem Namen:\n\n```python\nname = 'Anna'\nalter = 25\n```\n\nDanach kann man den Namen überall im Code verwenden:\n\n```python\nprint(name)\nprint(alter)\n```\n\nAusgabe:\n\n```\nAnna\n25\n```\n\nMan kann einer Variablen jederzeit einen **neuen Wert zuweisen** — der alte wird dann ersetzt:\n\n```python\nx = 10\nx = 20\nprint(x) # gibt 20 aus\n```" + "body": "# Variablen und Datentypen\n\nEine **Variable** ist ein benannter Speicherplatz. Mit dem **Zuweisungsoperator `=`** speichert man einen Wert unter einem Namen:\n\n```python\nname = 'Anna'\nalter = 25\n```\n\nDanach kann man den Namen überall im Code verwenden:\n\n```python\nprint(name)\nprint(alter)\n```\n\nAusgabe:\n\n```\nAnna\n25\n```\n\nMan kann einer Variablen jederzeit einen **neuen Wert zuweisen**, der alte wird dann ersetzt:\n\n```python\nx = 10\nx = 20\nprint(x) # gibt 20 aus\n```" }, { "id": "step-datentypen", @@ -159,7 +159,7 @@ "id": "step-namensregeln", "type": "theory", "title": "Namensregeln und Snake Case", - "body": "## Gültige Variablennamen\n\nFür Variablennamen gilt:\n\n- Nur **Buchstaben**, **Ziffern** und **Unterstrich** `_`.\n- **Kein Leerzeichen** — das würde Python verwirren.\n- Nicht mit einer **Ziffer beginnen**: `1name` ist ungültig, `name1` ist erlaubt.\n- Python unterscheidet **Groß- und Kleinschreibung**: `Alter` und `alter` sind zwei verschiedene Variablen.\n\nDer Python-Stilguide empfiehlt **Snake Case** für mehrteilige Namen:\n\n```python\nhoechste_punktzahl = 100\nmein_name = 'Lena'\n```\n\nUnzulässige Namen (verursachen einen `SyntaxError`):\n\n```python\n# 2wert = 5 → beginnt mit Ziffer\n# mein name = 5 → enthält Leerzeichen\n```" + "body": "## Gültige Variablennamen\n\nFür Variablennamen gilt:\n\n- Nur **Buchstaben**, **Ziffern** und **Unterstrich** `_`.\n- **Kein Leerzeichen**: das würde Python verwirren.\n- Nicht mit einer **Ziffer beginnen**: `1name` ist ungültig, `name1` ist erlaubt.\n- Python unterscheidet **Groß- und Kleinschreibung**: `Alter` und `alter` sind zwei verschiedene Variablen.\n\nDer Python-Stilguide empfiehlt **Snake Case** für mehrteilige Namen:\n\n```python\nhoechste_punktzahl = 100\nmein_name = 'Lena'\n```\n\nUnzulässige Namen (verursachen einen `SyntaxError`):\n\n```python\n# 2wert = 5 → beginnt mit Ziffer\n# mein name = 5 → enthält Leerzeichen\n```" }, { "id": "ex-cloze-zuweisung", @@ -177,7 +177,7 @@ { "accept": ["="] } ], "cloze_mode": "type", - "hint": "Die Zuweisung — nicht der doppelte Vergleichsoperator ==" + "hint": "Die Zuweisung, nicht der doppelte Vergleichsoperator ==" } }, { diff --git a/sets/de/python-basics/lessons/03-zahlen-und-operatoren.json b/sets/de/python-basics/lessons/03-zahlen-und-operatoren.json index e0a3d9b..7f44971 100644 --- a/sets/de/python-basics/lessons/03-zahlen-und-operatoren.json +++ b/sets/de/python-basics/lessons/03-zahlen-und-operatoren.json @@ -1,7 +1,7 @@ { "id": "03-zahlen-und-operatoren", "title": "Zahlen und Operatoren", - "description": "Arithmetische Operatoren, Ganzzahldivision, Modulo, Potenz, Operatorrangfolge und der Unterschied zwischen int und float bei Division — auf Deutsch erklärt.", + "description": "Arithmetische Operatoren, Ganzzahldivision, Modulo, Potenz, Operatorrangfolge und der Unterschied zwischen int und float bei Division, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -54,7 +54,7 @@ "code_snippet": "print(10 / 4)\nprint(8 / 2)", "code_language": "python", "expected_output": "2.5\n4.0", - "hint": "8 / 2 ergibt 4.0, nicht 4 — Python gibt immer einen float zurück.", + "hint": "8 / 2 ergibt 4.0, nicht 4, Python gibt immer einen float zurück.", "difficulty": 2, "tags": ["operatoren", "arithmetik", "float"], "media_type": "code" @@ -93,7 +93,7 @@ "code_snippet": "print(2 ** 8)\nprint(3 ** 2)", "code_language": "python", "expected_output": "256\n9", - "hint": "Zwei Sternchen ** — nicht verwechseln mit einem einfachen *.", + "hint": "Zwei Sternchen **, nicht verwechseln mit einem einfachen *.", "difficulty": 2, "tags": ["operatoren", "arithmetik"], "media_type": "code" @@ -106,7 +106,7 @@ "code_snippet": "print(2 + 3 * 4)\nprint((2 + 3) * 4)", "code_language": "python", "expected_output": "14\n20", - "hint": "Ohne Klammern gilt Punkt vor Strich — genau wie in der Mathematik.", + "hint": "Ohne Klammern gilt Punkt vor Strich, genau wie in der Mathematik.", "difficulty": 2, "tags": ["operatoren", "syntax"], "media_type": "code" @@ -129,7 +129,7 @@ "stable_id": "card-ms942h7g019gy", "front": "Klammern in Rechenausdrücken", "back": "Runde Klammern () erzwingen eine andere Berechnungsreihenfolge und machen den Code lesbarer.", - "notes": "Klammern sind in Python auch für Funktionsaufrufe zuständig — der Kontext macht den Unterschied.", + "notes": "Klammern sind in Python auch für Funktionsaufrufe zuständig, der Kontext macht den Unterschied.", "media_type": "text", "difficulty": 1, "tags": ["operatoren", "syntax"] @@ -155,13 +155,13 @@ "id": "step-erweiterte-operatoren", "type": "theory", "title": "Ganzzahldivision, Modulo und Potenz", - "body": "## Drei besondere Operatoren\n\n### Ganzzahldivision `//`\nSchneidet die Nachkommastellen ab — das Ergebnis ist ein `int`:\n\n```python\nprint(10 // 3) # 3 (nicht 3.33...)\nprint(7 // 2) # 3\n```\n\n### Modulo `%`\nGibt den **Rest** der Division zurück:\n\n```python\nprint(10 % 3) # 1 (10 = 3 × 3 + 1)\nprint(8 % 2) # 0 (8 ist gerade, kein Rest)\n```\n\n### Potenz `**`\n`a ** b` bedeutet 'a hoch b':\n\n```python\nprint(2 ** 8) # 256\nprint(3 ** 2) # 9\n```" + "body": "## Drei besondere Operatoren\n\n### Ganzzahldivision `//`\nSchneidet die Nachkommastellen ab, das Ergebnis ist ein `int`:\n\n```python\nprint(10 // 3) # 3 (nicht 3.33...)\nprint(7 // 2) # 3\n```\n\n### Modulo `%`\nGibt den **Rest** der Division zurück:\n\n```python\nprint(10 % 3) # 1 (10 = 3 × 3 + 1)\nprint(8 % 2) # 0 (8 ist gerade, kein Rest)\n```\n\n### Potenz `**`\n`a ** b` bedeutet 'a hoch b':\n\n```python\nprint(2 ** 8) # 256\nprint(3 ** 2) # 9\n```" }, { "id": "step-rangfolge", "type": "theory", "title": "Operatorrangfolge und Klammern", - "body": "## Punkt vor Strich — auch in Python\n\nPython folgt den mathematischen Vorrangregeln: `**` bindet am stärksten, dann `* / // %`, dann `+ -`.\n\n```python\nprint(2 + 3 * 4) # 14 (erst 3*4=12, dann 2+12)\nprint((2 + 3) * 4) # 20 (Klammern zuerst: 5*4)\n```\n\n**Tipp:** Wenn du dir bei der Reihenfolge nicht sicher bist, setze einfach **Klammern** — das macht den Code auch lesbarer.\n\n## int vs. float bei Division\n\n```python\nprint(type(8 / 2)) # \nprint(type(8 // 2)) # \n```\n\nFaustregel: `/` → immer `float`; `//` mit ganzzahligen Operanden → `int`." + "body": "## Punkt vor Strich - auch in Python\n\nPython folgt den mathematischen Vorrangregeln: `**` bindet am stärksten, dann `* / // %`, dann `+ -`.\n\n```python\nprint(2 + 3 * 4) # 14 (erst 3*4=12, dann 2+12)\nprint((2 + 3) * 4) # 20 (Klammern zuerst: 5*4)\n```\n\n**Tipp:** Wenn du dir bei der Reihenfolge nicht sicher bist, setze einfach **Klammern**, das macht den Code auch lesbarer.\n\n## int vs. float bei Division\n\n```python\nprint(type(8 / 2)) # \nprint(type(8 // 2)) # \n```\n\nFaustregel: `/` → immer `float`; `//` mit ganzzahligen Operanden → `int`." }, { "id": "ex-cloze-division", @@ -198,7 +198,7 @@ { "accept": ["%"] } ], "cloze_mode": "type", - "hint": "Das Prozentzeichen — aber es ist kein Prozent, sondern Modulo." + "hint": "Das Prozentzeichen, aber es ist kein Prozent, sondern Modulo." } }, { @@ -213,7 +213,7 @@ "prompt": "Welches Ergebnis liefert 2 ** 8 in Python?", "card_ids": ["py-potenz"], "accept": ["256", "256.0"], - "hint": "2 hoch 8 — multipliziere 2 achtmal miteinander.", + "hint": "2 hoch 8, multipliziere 2 achtmal miteinander.", "distractors": ["16", "64", "512", "28"], "direction": "target_to_source" } @@ -230,7 +230,7 @@ "prompt": "Was gibt print(2 + 3 * 4) aus?", "card_ids": ["py-rangfolge"], "accept": ["14", "14.0"], - "hint": "Multiplikation hat Vorrang vor Addition — erst 3 * 4, dann + 2.", + "hint": "Multiplikation hat Vorrang vor Addition, erst 3 * 4, dann + 2.", "distractors": ["20", "24", "11"], "direction": "target_to_source" } diff --git a/sets/de/python-basics/lessons/04-strings.json b/sets/de/python-basics/lessons/04-strings.json index d4d6d7f..aea6d67 100644 --- a/sets/de/python-basics/lessons/04-strings.json +++ b/sets/de/python-basics/lessons/04-strings.json @@ -1,7 +1,7 @@ { "id": "04-strings", - "title": "Strings — mit Text arbeiten", - "description": "String-Verkettung, f-Strings, len(), die Methoden upper/lower/replace, Indexing, Slicing und der in-Operator — auf Deutsch erklärt.", + "title": "Strings - mit Text arbeiten", + "description": "String-Verkettung, f-Strings, len(), die Methoden upper/lower/replace, Indexing, Slicing und der in-Operator, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -54,7 +54,7 @@ "code_snippet": "s = 'Hallo Welt'\nprint(s.upper())\nprint(s.lower())", "code_language": "python", "expected_output": "HALLO WELT\nhallo welt", - "hint": "Diese Methoden verändern den ursprünglichen String nicht — sie geben einen neuen zurück.", + "hint": "Diese Methoden verändern den ursprünglichen String nicht, sie geben einen neuen zurück.", "difficulty": 2, "tags": ["strings", "methoden"], "media_type": "code" @@ -93,7 +93,7 @@ "code_snippet": "s = 'Python'\nprint(s[0:3])\nprint(s[2:5])", "code_language": "python", "expected_output": "Pyt\ntho", - "hint": "Der stop-Index ist ausschließend — s[0:3] liefert 3 Zeichen, nicht 4.", + "hint": "Der stop-Index ist ausschließend, s[0:3] liefert 3 Zeichen, nicht 4.", "difficulty": 3, "tags": ["strings", "slicing"], "media_type": "code" @@ -106,7 +106,7 @@ "code_snippet": "print('Py' in 'Python')\nprint('Java' in 'Python')", "code_language": "python", "expected_output": "True\nFalse", - "hint": "in gibt einen bool zurück — gut für Bedingungen.", + "hint": "in gibt einen bool zurück, gut für Bedingungen.", "difficulty": 2, "tags": ["strings", "operatoren"], "media_type": "code" @@ -146,13 +146,13 @@ "id": "intro-strings", "type": "theory", "title": "Strings verbinden und formatieren", - "body": "# Strings — mit Text arbeiten\n\nStrings sind Zeichenketten in Python. Mit `+` lassen sich zwei Strings **verketten**:\n\n```python\nvorname = 'Anna'\nnachname = 'Müller'\nprint(vorname + ' ' + nachname)\n```\n\nAusgabe:\n\n```\nAnna Müller\n```\n\n## f-Strings\n\nEine elegantere Alternative sind **f-Strings**. Man stellt dem Anführungszeichen ein `f` voran und setzt Variablen in `{}`:\n\n```python\nname = 'Lena'\nalter = 22\nprint(f'Ich heiße {name} und bin {alter} Jahre alt.')\n```\n\nAusgabe:\n\n```\nIch heiße Lena und bin 22 Jahre alt.\n```\n\n## Länge eines Strings\n\nMit `len()` erfährt man, wie viele Zeichen ein String hat:\n\n```python\nprint(len('Python')) # 6\n```" + "body": "# Strings - mit Text arbeiten\n\nStrings sind Zeichenketten in Python. Mit `+` lassen sich zwei Strings **verketten**:\n\n```python\nvorname = 'Anna'\nnachname = 'Müller'\nprint(vorname + ' ' + nachname)\n```\n\nAusgabe:\n\n```\nAnna Müller\n```\n\n## f-Strings\n\nEine elegantere Alternative sind **f-Strings**. Man stellt dem Anführungszeichen ein `f` voran und setzt Variablen in `{}`:\n\n```python\nname = 'Lena'\nalter = 22\nprint(f'Ich heiße {name} und bin {alter} Jahre alt.')\n```\n\nAusgabe:\n\n```\nIch heiße Lena und bin 22 Jahre alt.\n```\n\n## Länge eines Strings\n\nMit `len()` erfährt man, wie viele Zeichen ein String hat:\n\n```python\nprint(len('Python')) # 6\n```" }, { "id": "step-methoden", "type": "theory", "title": "String-Methoden: upper, lower, replace", - "body": "## Methoden verändern Strings\n\nStrings besitzen eingebaute **Methoden**, die man mit einem Punkt aufruft. Wichtig: Strings sind **unveränderlich** — Methoden geben immer einen *neuen* String zurück.\n\n### `.upper()` und `.lower()`\n\n```python\ns = 'Hallo Welt'\nprint(s.upper()) # HALLO WELT\nprint(s.lower()) # hallo welt\n```\n\n### `.replace(alt, neu)`\n\nErsetzt jeden Treffer des ersten Arguments durch das zweite:\n\n```python\nsatz = 'Ich mag Katzen'\nprint(satz.replace('Katzen', 'Hunde')) # Ich mag Hunde\n```" + "body": "## Methoden verändern Strings\n\nStrings besitzen eingebaute **Methoden**, die man mit einem Punkt aufruft. Wichtig: Strings sind **unveränderlich**, Methoden geben immer einen *neuen* String zurück.\n\n### `.upper()` und `.lower()`\n\n```python\ns = 'Hallo Welt'\nprint(s.upper()) # HALLO WELT\nprint(s.lower()) # hallo welt\n```\n\n### `.replace(alt, neu)`\n\nErsetzt jeden Treffer des ersten Arguments durch das zweite:\n\n```python\nsatz = 'Ich mag Katzen'\nprint(satz.replace('Katzen', 'Hunde')) # Ich mag Hunde\n```" }, { "id": "step-indexing-slicing", diff --git a/sets/de/python-basics/lessons/05-listen.json b/sets/de/python-basics/lessons/05-listen.json index 8b310f6..b607065 100644 --- a/sets/de/python-basics/lessons/05-listen.json +++ b/sets/de/python-basics/lessons/05-listen.json @@ -1,7 +1,7 @@ { "id": "05-listen", "title": "Listen (Lists)", - "description": "Listen erstellen, Elemente abrufen und ändern, Slicing, len() und typische Listenmethoden — alles auf Deutsch erklärt.", + "description": "Listen erstellen, Elemente abrufen und ändern, Slicing, len() und typische Listenmethoden, alles auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -23,7 +23,7 @@ { "id": "py-list-index", "stable_id": "card-ms942h7h021xo", - "front": "Indexing — liste[0]", + "front": "Indexing - liste[0]", "back": "Mit einem Index in eckigen Klammern greift man auf ein einzelnes Element zu. Der erste Index ist 0.", "code_snippet": "farben = ['rot', 'gruen', 'blau']\nprint(farben[0])", "code_language": "python", @@ -36,7 +36,7 @@ { "id": "py-list-neg-index", "stable_id": "card-ms942h7h022v8", - "front": "Negativer Index — liste[-1]", + "front": "Negativer Index - liste[-1]", "back": "Negative Indizes zählen vom Ende der Liste. -1 ist das letzte Element.", "code_snippet": "farben = ['rot', 'gruen', 'blau']\nprint(farben[-1])", "code_language": "python", @@ -49,7 +49,7 @@ { "id": "py-list-slicing", "stable_id": "card-ms942h7h023j8", - "front": "Slicing — liste[1:3]", + "front": "Slicing - liste[1:3]", "back": "Mit Slicing schneidet man einen Teil einer Liste heraus. liste[start:stop] gibt Elemente von Index start bis stop-1 zurück.", "code_snippet": "zahlen = [10, 20, 30, 40, 50]\nprint(zahlen[1:3])", "code_language": "python", @@ -62,7 +62,7 @@ { "id": "py-list-len", "stable_id": "card-ms942h7h02474", - "front": "len() — Länge einer Liste", + "front": "len() - Länge einer Liste", "back": "len(liste) gibt die Anzahl der Elemente in der Liste zurück.", "code_snippet": "farben = ['rot', 'gruen', 'blau']\nprint(len(farben))", "code_language": "python", @@ -88,7 +88,7 @@ { "id": "py-list-append", "stable_id": "card-ms942h7h026qx", - "front": ".append() — Element hinzufügen", + "front": ".append() - Element hinzufügen", "back": ".append(element) fügt ein Element ans Ende der Liste an.", "code_snippet": "farben = ['rot', 'gruen']\nfarben.append('blau')\nprint(farben)", "code_language": "python", @@ -101,7 +101,7 @@ { "id": "py-list-remove", "stable_id": "card-ms942h7h027mg", - "front": ".remove() — Element entfernen", + "front": ".remove() - Element entfernen", "back": ".remove(wert) entfernt das erste Vorkommen des angegebenen Werts aus der Liste.", "code_snippet": "farben = ['rot', 'gruen', 'blau']\nfarben.remove('gruen')\nprint(farben)", "code_language": "python", @@ -114,7 +114,7 @@ { "id": "py-list-insert", "stable_id": "card-ms942h7h028g9", - "front": ".insert() — Element einfügen", + "front": ".insert() - Element einfügen", "back": ".insert(index, wert) fügt ein Element an einer bestimmten Position ein.", "code_snippet": "farben = ['rot', 'blau']\nfarben.insert(1, 'gruen')\nprint(farben)", "code_language": "python", @@ -127,7 +127,7 @@ { "id": "py-list-in", "stable_id": "card-ms942h7h029sg", - "front": "in-Test — Element prüfen", + "front": "in-Test - Element prüfen", "back": "Mit dem Operator in prüft man, ob ein Wert in einer Liste enthalten ist. Das Ergebnis ist True oder False.", "code_snippet": "farben = ['rot', 'gruen', 'blau']\nprint('gruen' in farben)", "code_language": "python", @@ -156,13 +156,13 @@ "id": "step-list-grundlagen", "type": "theory", "title": "Listen erstellen und auf Elemente zugreifen", - "body": "# Listen (Lists)\n\nEine **Liste** speichert mehrere Werte in einer einzigen Variablen. Listen stehen in **eckigen Klammern** `[]`, die Elemente werden durch Kommas getrennt:\n\n```python\nfarben = ['rot', 'gruen', 'blau']\nprint(farben)\n```\n\nAusgabe:\n\n```\n['rot', 'gruen', 'blau']\n```\n\n## Indexing — Elemente abrufen\n\nJedes Element hat eine **Position (Index)**. Der erste Index ist **0**:\n\n```python\nprint(farben[0]) # rot\nprint(farben[1]) # gruen\n```\n\n**Negativer Index:** `-1` ist das letzte Element, `-2` das vorletzte usw.:\n\n```python\nprint(farben[-1]) # blau\n```\n\n## Slicing — Ausschnitt abrufen\n\nMit `liste[start:stop]` bekommt man einen Teil der Liste. Der `stop`-Index selbst ist **nicht** enthalten:\n\n```python\nzahlen = [10, 20, 30, 40, 50]\nprint(zahlen[1:3]) # [20, 30]\n```\n\n## Länge mit len()\n\n```python\nprint(len(farben)) # 3\n```" + "body": "# Listen (Lists)\n\nEine **Liste** speichert mehrere Werte in einer einzigen Variablen. Listen stehen in **eckigen Klammern** `[]`, die Elemente werden durch Kommas getrennt:\n\n```python\nfarben = ['rot', 'gruen', 'blau']\nprint(farben)\n```\n\nAusgabe:\n\n```\n['rot', 'gruen', 'blau']\n```\n\n## Indexing - Elemente abrufen\n\nJedes Element hat eine **Position (Index)**. Der erste Index ist **0**:\n\n```python\nprint(farben[0]) # rot\nprint(farben[1]) # gruen\n```\n\n**Negativer Index:** `-1` ist das letzte Element, `-2` das vorletzte usw.:\n\n```python\nprint(farben[-1]) # blau\n```\n\n## Slicing - Ausschnitt abrufen\n\nMit `liste[start:stop]` bekommt man einen Teil der Liste. Der `stop`-Index selbst ist **nicht** enthalten:\n\n```python\nzahlen = [10, 20, 30, 40, 50]\nprint(zahlen[1:3]) # [20, 30]\n```\n\n## Länge mit len()\n\n```python\nprint(len(farben)) # 3\n```" }, { "id": "step-list-aendern", "type": "theory", - "title": "Listen verändern — append, remove, insert", - "body": "## Elemente ändern\n\nDu kannst ein Element direkt per Index überschreiben:\n\n```python\nfarben = ['rot', 'gruen', 'blau']\nfarben[1] = 'gelb'\nprint(farben) # ['rot', 'gelb', 'blau']\n```\n\n## Elemente hinzufügen — .append()\n\n`.append(wert)` hängt ein Element **ans Ende** an:\n\n```python\nfarben.append('lila')\n# ['rot', 'gelb', 'blau', 'lila']\n```\n\n## Elemente einfügen — .insert()\n\n`.insert(index, wert)` fügt ein Element **an einer bestimmten Position** ein:\n\n```python\nfarben.insert(1, 'orange')\n# ['rot', 'orange', 'gelb', 'blau', 'lila']\n```\n\n## Elemente entfernen — .remove()\n\n`.remove(wert)` entfernt das **erste Vorkommen** dieses Werts:\n\n```python\nfarben.remove('gelb')\n```\n\n## Prüfen, ob ein Element vorhanden ist — in\n\nDer **in**-Operator gibt `True` oder `False` zurück:\n\n```python\nprint('rot' in farben) # True\nprint('pink' in farben) # False\n```" + "title": "Listen verändern - append, remove, insert", + "body": "## Elemente ändern\n\nDu kannst ein Element direkt per Index überschreiben:\n\n```python\nfarben = ['rot', 'gruen', 'blau']\nfarben[1] = 'gelb'\nprint(farben) # ['rot', 'gelb', 'blau']\n```\n\n## Elemente hinzufügen - .append()\n\n`.append(wert)` hängt ein Element **ans Ende** an:\n\n```python\nfarben.append('lila')\n# ['rot', 'gelb', 'blau', 'lila']\n```\n\n## Elemente einfügen - .insert()\n\n`.insert(index, wert)` fügt ein Element **an einer bestimmten Position** ein:\n\n```python\nfarben.insert(1, 'orange')\n# ['rot', 'orange', 'gelb', 'blau', 'lila']\n```\n\n## Elemente entfernen - .remove()\n\n`.remove(wert)` entfernt das **erste Vorkommen** dieses Werts:\n\n```python\nfarben.remove('gelb')\n```\n\n## Prüfen, ob ein Element vorhanden ist - in\n\nDer **in**-Operator gibt `True` oder `False` zurück:\n\n```python\nprint('rot' in farben) # True\nprint('pink' in farben) # False\n```" }, { "id": "ex-cloze-index", diff --git a/sets/de/python-basics/lessons/06-bedingungen.json b/sets/de/python-basics/lessons/06-bedingungen.json index bb71867..8f9ef19 100644 --- a/sets/de/python-basics/lessons/06-bedingungen.json +++ b/sets/de/python-basics/lessons/06-bedingungen.json @@ -1,7 +1,7 @@ { "id": "06-bedingungen", "title": "Bedingungen (if / elif / else)", - "description": "if, elif und else, Vergleichsoperatoren, boolesche Operatoren und die Bedeutung der Einrückung für Codeblöcke — auf Deutsch erklärt.", + "description": "if, elif und else, Vergleichsoperatoren, boolesche Operatoren und die Bedeutung der Einrückung für Codeblöcke, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -54,7 +54,7 @@ "code_snippet": "print(3 == 3)\nprint(3 == 4)", "code_language": "python", "expected_output": "True\nFalse", - "hint": "Zwei Gleichheitszeichen — nicht zu verwechseln mit = (Zuweisung).", + "hint": "Zwei Gleichheitszeichen, nicht zu verwechseln mit = (Zuweisung).", "difficulty": 1, "tags": ["operatoren", "vergleich"], "media_type": "code" @@ -137,7 +137,7 @@ { "id": "py-if-elif-else-kette", "stable_id": "card-ms942h7i02tpa", - "front": "if / elif / else — komplette Kette", + "front": "if / elif / else - komplette Kette", "back": "Mit if, beliebig vielen elif und einem abschließenden else deckt man alle Fälle ab.", "code_snippet": "punkte = 75\nif punkte >= 90:\n print('Sehr gut')\nelif punkte >= 60:\n print('Bestanden')\nelse:\n print('Nicht bestanden')", "code_language": "python", @@ -151,7 +151,7 @@ "id": "py-bool-ausdruck", "stable_id": "card-ms942h7i02uex", "front": "Boolescher Ausdruck (Boolean Expression)", - "back": "Ein Ausdruck, der True oder False ergibt — z. B. 5 > 3 oder alter >= 18.", + "back": "Ein Ausdruck, der True oder False ergibt, z. B. 5 > 3 oder alter >= 18.", "media_type": "text", "difficulty": 1, "tags": ["boolean", "grundlagen"] @@ -161,7 +161,7 @@ { "id": "step-if-else", "type": "theory", - "title": "if, elif und else — Entscheidungen treffen", + "title": "if, elif und else - Entscheidungen treffen", "body": "# Bedingungen (if / elif / else)\n\nMit **`if`** kann man Code nur dann ausführen lassen, wenn eine Bedingung erfüllt ist:\n\n```python\nalter = 18\nif alter >= 18:\n print('Volljährig')\n```\n\nAusgabe:\n\n```\nVolljährig\n```\n\nMit **`elif`** (kurz für *else if*) prüft man weitere Fälle, und **`else`** fängt alles auf, was nicht gepasst hat:\n\n```python\npunkte = 75\nif punkte >= 90:\n print('Sehr gut')\nelif punkte >= 60:\n print('Bestanden')\nelse:\n print('Nicht bestanden')\n```\n\nAusgabe:\n\n```\nBestanden\n```\n\n**Wichtig:** Nach `if`, `elif` und `else` steht immer ein **Doppelpunkt**, und der Codeblock darunter muss **eingerückt** sein (4 Leerzeichen)." }, { @@ -211,7 +211,7 @@ { "accept": ["=="] } ], "cloze_mode": "type", - "hint": "Das Gleichheitszeichen verdoppelt — nicht das einfache der Zuweisung." + "hint": "Das Gleichheitszeichen verdoppelt, nicht das einfache der Zuweisung." } }, { diff --git a/sets/de/python-basics/lessons/07-schleifen.json b/sets/de/python-basics/lessons/07-schleifen.json index b77b5a7..278e660 100644 --- a/sets/de/python-basics/lessons/07-schleifen.json +++ b/sets/de/python-basics/lessons/07-schleifen.json @@ -1,7 +1,7 @@ { "id": "07-schleifen", "title": "Schleifen (for & while)", - "description": "for-Schleife über Listen und range(), while-Schleife, break, continue und typische Zähl- und Summenschleifen — auf Deutsch erklärt.", + "description": "for-Schleife über Listen und range(), while-Schleife, break, continue und typische Zähl- und Summenschleifen, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -15,7 +15,7 @@ "code_snippet": "farben = ['rot', 'gruen', 'blau']\nfor farbe in farben:\n print(farbe)", "code_language": "python", "expected_output": "rot\ngruen\nblau", - "hint": "for variable in liste: — die Variable bekommt nacheinander jeden Wert.", + "hint": "for variable in liste:, die Variable bekommt nacheinander jeden Wert.", "difficulty": 2, "tags": ["schleifen", "for", "liste"], "media_type": "code" @@ -28,7 +28,7 @@ "code_snippet": "for i in range(3):\n print(i)", "code_language": "python", "expected_output": "0\n1\n2", - "hint": "range(3) erzeugt 0, 1, 2 — also drei Zahlen, beginnend bei 0.", + "hint": "range(3) erzeugt 0, 1, 2, also drei Zahlen, beginnend bei 0.", "difficulty": 2, "tags": ["schleifen", "for", "range"], "media_type": "code" @@ -62,7 +62,7 @@ { "id": "py-break", "stable_id": "card-ms942h7i037tj", - "front": "break — Schleife abbrechen", + "front": "break - Schleife abbrechen", "back": "break beendet die Schleife sofort, egal ob die Bedingung noch True wäre.", "code_snippet": "for i in range(5):\n if i == 3:\n break\n print(i)", "code_language": "python", @@ -75,7 +75,7 @@ { "id": "py-continue", "stable_id": "card-ms942h7i038ov", - "front": "continue — Iteration überspringen", + "front": "continue - Iteration überspringen", "back": "continue überspringt den Rest des aktuellen Schleifendurchlaufs und springt zum nächsten.", "code_snippet": "for i in range(5):\n if i == 2:\n continue\n print(i)", "code_language": "python", @@ -88,7 +88,7 @@ { "id": "py-zaehlen-schleife", "stable_id": "card-ms942h7i039yn", - "front": "Zählschleife — Summe berechnen", + "front": "Zählschleife - Summe berechnen", "back": "Ein typisches Muster: Variable vor der Schleife auf 0 setzen, in der Schleife aufaddieren.", "code_snippet": "summe = 0\nfor zahl in [1, 2, 3, 4, 5]:\n summe = summe + zahl\nprint(summe)", "code_language": "python", @@ -102,7 +102,7 @@ "id": "py-schleife-einrueckung", "stable_id": "card-ms942h7i03aa5", "front": "Einrückung in Schleifen", - "back": "Der Körper einer Schleife (alles, was wiederholt werden soll) muss eingerückt sein — genau wie bei if-Blöcken.", + "back": "Der Körper einer Schleife (alles, was wiederholt werden soll) muss eingerückt sein, genau wie bei if-Blöcken.", "notes": "Ohne korrekte Einrückung gehört der Code nicht zum Schleifenkörper und wird nur einmal ausgeführt.", "media_type": "text", "difficulty": 2, @@ -111,7 +111,7 @@ { "id": "py-for-vs-while", "stable_id": "card-ms942h7i03buy", - "front": "for vs. while — Wann was?", + "front": "for vs. while - Wann was?", "back": "for: wenn man die Anzahl der Wiederholungen kennt oder über eine Sammlung iteriert. while: wenn man wiederholen will, bis eine Bedingung eintritt.", "media_type": "text", "difficulty": 2, @@ -142,13 +142,13 @@ "id": "step-while-schleife", "type": "theory", "title": "Die while-Schleife", - "body": "## while-Schleife\n\nDie **`while`-Schleife** läuft, solange eine Bedingung `True` ist:\n\n```python\nzaehler = 0\nwhile zaehler < 3:\n print(zaehler)\n zaehler = zaehler + 1\n```\n\nAusgabe:\n\n```\n0\n1\n2\n```\n\n**Wichtig:** Man muss dafür sorgen, dass die Bedingung irgendwann `False` wird — sonst entsteht eine **Endlosschleife**.\n\n## Summe berechnen — ein typisches Muster\n\n```python\nsumme = 0\nfor zahl in [1, 2, 3, 4, 5]:\n summe = summe + zahl\nprint(summe) # 15\n```" + "body": "## while-Schleife\n\nDie **`while`-Schleife** läuft, solange eine Bedingung `True` ist:\n\n```python\nzaehler = 0\nwhile zaehler < 3:\n print(zaehler)\n zaehler = zaehler + 1\n```\n\nAusgabe:\n\n```\n0\n1\n2\n```\n\n**Wichtig:** Man muss dafür sorgen, dass die Bedingung irgendwann `False` wird, sonst entsteht eine **Endlosschleife**.\n\n## Summe berechnen - ein typisches Muster\n\n```python\nsumme = 0\nfor zahl in [1, 2, 3, 4, 5]:\n summe = summe + zahl\nprint(summe) # 15\n```" }, { "id": "step-break-continue", "type": "theory", "title": "break und continue", - "body": "## break — Schleife sofort beenden\n\n`break` verlässt die Schleife, egal wie viele Durchläufe noch geplant wären:\n\n```python\nfor i in range(5):\n if i == 3:\n break\n print(i)\n```\n\nAusgabe:\n\n```\n0\n1\n2\n```\n\n## continue — Durchlauf überspringen\n\n`continue` springt zum nächsten Durchlauf und überspringt den Rest des aktuellen:\n\n```python\nfor i in range(5):\n if i == 2:\n continue\n print(i)\n```\n\nAusgabe:\n\n```\n0\n1\n3\n4\n```\n\n## Wann for, wann while?\n\n- **`for`**: wenn man die Anzahl der Wiederholungen kennt oder über eine Sammlung iteriert.\n- **`while`**: wenn man bis zu einem Ereignis wiederholen will." + "body": "## break - Schleife sofort beenden\n\n`break` verlässt die Schleife, egal wie viele Durchläufe noch geplant wären:\n\n```python\nfor i in range(5):\n if i == 3:\n break\n print(i)\n```\n\nAusgabe:\n\n```\n0\n1\n2\n```\n\n## continue - Durchlauf überspringen\n\n`continue` springt zum nächsten Durchlauf und überspringt den Rest des aktuellen:\n\n```python\nfor i in range(5):\n if i == 2:\n continue\n print(i)\n```\n\nAusgabe:\n\n```\n0\n1\n3\n4\n```\n\n## Wann for, wann while?\n\n- **`for`**: wenn man die Anzahl der Wiederholungen kennt oder über eine Sammlung iteriert.\n- **`while`**: wenn man bis zu einem Ereignis wiederholen will." }, { "id": "ex-cloze-range", @@ -247,7 +247,7 @@ "id": "ex-tiles-summe", "stable_id": "ex-ms942h7i03ij7", "type": "word_tiles", - "prompt": "Setze die Zeile zusammen, die eine Summenvariable auf 0 setzt — bevor die Schleife startet.", + "prompt": "Setze die Zeile zusammen, die eine Summenvariable auf 0 setzt, bevor die Schleife startet.", "card_ids": ["py-zaehlen-schleife"], "tiles": ["summe", "=", "0"], "hint": "Variablenname, Zuweisungsoperator, Startwert.", diff --git a/sets/de/python-basics/lessons/08-funktionen.json b/sets/de/python-basics/lessons/08-funktionen.json index eb5a356..d3dc0a5 100644 --- a/sets/de/python-basics/lessons/08-funktionen.json +++ b/sets/de/python-basics/lessons/08-funktionen.json @@ -1,7 +1,7 @@ { "id": "08-funktionen", "title": "Funktionen (def)", - "description": "Eigene Funktionen mit def definieren, Parameter und Argumente, return-Werte, Default-Argumente, Docstrings und den Unterschied zwischen print und return — auf Deutsch erklärt.", + "description": "Eigene Funktionen mit def definieren, Parameter und Argumente, return-Werte, Default-Argumente, Docstrings und den Unterschied zwischen print und return, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -54,7 +54,7 @@ "code_language": "python", "expected_output": "7", "notes": "Ohne return gibt die Funktion None zurück.", - "hint": "return gibt einen Wert zurück — print() zeigt ihn nur an.", + "hint": "return gibt einen Wert zurück, print() zeigt ihn nur an.", "difficulty": 2, "tags": ["funktionen", "return"], "media_type": "code" @@ -139,7 +139,7 @@ "id": "fn-scope", "stable_id": "card-ms942h7j03u41", "front": "Lokaler Geltungsbereich (Scope)", - "back": "Variablen, die innerhalb einer Funktion definiert werden, existieren nur dort — außerhalb sind sie nicht sichtbar.", + "back": "Variablen, die innerhalb einer Funktion definiert werden, existieren nur dort, außerhalb sind sie nicht sichtbar.", "notes": "Das verhindert, dass Hilfsvariablen in der Funktion globale Namen überschreiben.", "media_type": "text", "difficulty": 3, @@ -151,13 +151,13 @@ "id": "step-def-grundlagen", "type": "theory", "title": "Funktionen definieren mit def", - "body": "# Funktionen (def)\n\nEine **Funktion** ist ein wiederverwendbarer Code-Block, dem man einen Namen gibt. Mit dem Schlüsselwort **def** wird sie definiert:\n\n```python\ndef begruesse():\n print('Hallo!')\n```\n\nDer Funktionskörper muss **eingerückt** sein (4 Leerzeichen). Aufgerufen wird die Funktion mit ihrem Namen und runden Klammern:\n\n```python\nbegruesse() # Ausgabe: Hallo!\n```\n\n## Parameter und Argumente\n\n**Parameter** sind Platzhaltervariablen im Funktionskopf. Beim Aufruf übergibt man den konkreten Wert — das **Argument**:\n\n```python\ndef begruesse(name):\n print('Hallo', name)\n\nbegruesse('Anna') # Ausgabe: Hallo Anna\n```\n\nEine Funktion kann auch mehrere Parameter haben, getrennt durch Kommas:\n\n```python\ndef addiere(a, b):\n return a + b\n```" + "body": "# Funktionen (def)\n\nEine **Funktion** ist ein wiederverwendbarer Code-Block, dem man einen Namen gibt. Mit dem Schlüsselwort **def** wird sie definiert:\n\n```python\ndef begruesse():\n print('Hallo!')\n```\n\nDer Funktionskörper muss **eingerückt** sein (4 Leerzeichen). Aufgerufen wird die Funktion mit ihrem Namen und runden Klammern:\n\n```python\nbegruesse() # Ausgabe: Hallo!\n```\n\n## Parameter und Argumente\n\n**Parameter** sind Platzhaltervariablen im Funktionskopf. Beim Aufruf übergibt man den konkreten Wert, das **Argument**:\n\n```python\ndef begruesse(name):\n print('Hallo', name)\n\nbegruesse('Anna') # Ausgabe: Hallo Anna\n```\n\nEine Funktion kann auch mehrere Parameter haben, getrennt durch Kommas:\n\n```python\ndef addiere(a, b):\n return a + b\n```" }, { "id": "step-return-default", "type": "theory", "title": "return, Default-Argumente und Docstrings", - "body": "## return\n\nMit **return** gibt eine Funktion einen Wert zurück:\n\n```python\ndef quadrat(x):\n return x * x\n\nergebnis = quadrat(5)\nprint(ergebnis) # 25\n```\n\nOhne `return` gibt die Funktion **None** zurück.\n\n## Default-Argumente\n\nParameter können einen **Standardwert** haben — er gilt, wenn kein Argument übergeben wird:\n\n```python\ndef begruesse(name='Welt'):\n print('Hallo', name)\n\nbegruesse() # Hallo Welt\nbegruesse('Max') # Hallo Max\n```\n\n## Docstring\n\nEin **Docstring** ist ein kurzer Text direkt nach dem Funktionskopf, der die Funktion beschreibt:\n\n```python\ndef berechne(x):\n \"\"\"Gibt das Doppelte von x zurueck.\"\"\"\n return x * 2\n```\n\n## print() vs. return\n\n| | print() | return |\n|---|---|---|\n| Zeigt Wert an | ja | nein |\n| Gibt Wert zurück | nein | ja |\n\n`print()` ist nur für die Ausgabe; `return` gibt den Wert an den Aufrufer zurück, damit er weiterverwendet werden kann." + "body": "## return\n\nMit **return** gibt eine Funktion einen Wert zurück:\n\n```python\ndef quadrat(x):\n return x * x\n\nergebnis = quadrat(5)\nprint(ergebnis) # 25\n```\n\nOhne `return` gibt die Funktion **None** zurück.\n\n## Default-Argumente\n\nParameter können einen **Standardwert** haben, er gilt, wenn kein Argument übergeben wird:\n\n```python\ndef begruesse(name='Welt'):\n print('Hallo', name)\n\nbegruesse() # Hallo Welt\nbegruesse('Max') # Hallo Max\n```\n\n## Docstring\n\nEin **Docstring** ist ein kurzer Text direkt nach dem Funktionskopf, der die Funktion beschreibt:\n\n```python\ndef berechne(x):\n \"\"\"Gibt das Doppelte von x zurueck.\"\"\"\n return x * 2\n```\n\n## print() vs. return\n\n| | print() | return |\n|---|---|---|\n| Zeigt Wert an | ja | nein |\n| Gibt Wert zurück | nein | ja |\n\n`print()` ist nur für die Ausgabe; `return` gibt den Wert an den Aufrufer zurück, damit er weiterverwendet werden kann." }, { "id": "ex-cloze-def", diff --git a/sets/de/python-basics/lessons/09-dictionaries.json b/sets/de/python-basics/lessons/09-dictionaries.json index bb3ae2c..ba23518 100644 --- a/sets/de/python-basics/lessons/09-dictionaries.json +++ b/sets/de/python-basics/lessons/09-dictionaries.json @@ -1,7 +1,7 @@ { "id": "09-dictionaries", "title": "Dictionaries (dict)", - "description": "Dictionaries mit {} und key: value erstellen, Werte abrufen, neue Schlüssel hinzufügen, .keys() / .values() / .items() und den in-Test — auf Deutsch erklärt.", + "description": "Dictionaries mit {} und key: value erstellen, Werte abrufen, neue Schlüssel hinzufügen, .keys() / .values() / .items() und den in-Test, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -23,7 +23,7 @@ { "id": "dict-zugriff", "stable_id": "card-ms942h7j044oj", - "front": "Wert abrufen — d['schluessel']", + "front": "Wert abrufen - d['schluessel']", "back": "Mit eckigen Klammern und dem Schlüssel greift man auf den zugehörigen Wert zu.", "code_snippet": "alter = {'Anna': 28, 'Ben': 34}\nprint(alter['Anna'])", "code_language": "python", @@ -36,8 +36,8 @@ { "id": "dict-get", "stable_id": "card-ms942h7j045no", - "front": ".get() — sicherer Zugriff", - "back": ".get(schluessel) gibt den Wert zurück oder None, wenn der Schlüssel nicht existiert — kein Fehler.", + "front": ".get() - sicherer Zugriff", + "back": ".get(schluessel) gibt den Wert zurück oder None, wenn der Schlüssel nicht existiert, kein Fehler.", "code_snippet": "alter = {'Anna': 28}\nprint(alter.get('Ben'))", "code_language": "python", "expected_output": "None", @@ -62,7 +62,7 @@ { "id": "dict-keys", "stable_id": "card-ms942h7j047ui", - "front": ".keys() — alle Schlüssel", + "front": ".keys() - alle Schlüssel", "back": ".keys() gibt eine Ansicht aller Schlüssel im Dictionary zurück.", "code_snippet": "alter = {'Anna': 28, 'Ben': 34}\nprint(list(alter.keys()))", "code_language": "python", @@ -75,7 +75,7 @@ { "id": "dict-values", "stable_id": "card-ms942h7j048zg", - "front": ".values() — alle Werte", + "front": ".values() - alle Werte", "back": ".values() gibt eine Ansicht aller Werte im Dictionary zurück.", "code_snippet": "alter = {'Anna': 28, 'Ben': 34}\nprint(list(alter.values()))", "code_language": "python", @@ -88,12 +88,12 @@ { "id": "dict-items", "stable_id": "card-ms942h7j049ab", - "front": ".items() — Schlüssel-Wert-Paare", - "back": ".items() gibt alle Schlüssel-Wert-Paare als Tupel-Liste zurück — nützlich für Schleifen.", + "front": ".items() - Schlüssel-Wert-Paare", + "back": ".items() gibt alle Schlüssel-Wert-Paare als Tupel-Liste zurück, nützlich für Schleifen.", "code_snippet": "alter = {'Anna': 28}\nfor name, a in alter.items():\n print(name, a)", "code_language": "python", "expected_output": "Anna 28", - "hint": "items() liefert (schluessel, wert)-Tupel.", + "hint": "items() liefert Tupel aus Schlüssel und Wert.", "difficulty": 3, "tags": ["dictionary", "methode"], "media_type": "code" @@ -159,7 +159,7 @@ "id": "step-dict-methoden", "type": "theory", "title": "Dictionary-Methoden und der in-Test", - "body": "## .keys(), .values(), .items()\n\nDrei nützliche Methoden geben Übersichten zurück:\n\n```python\nalter = {'Anna': 28, 'Ben': 34}\nprint(list(alter.keys())) # ['Anna', 'Ben']\nprint(list(alter.values())) # [28, 34]\n```\n\n**.items()** liefert alle Paare — ideal für eine Schleife:\n\n```python\nfor name, a in alter.items():\n print(name, a)\n# Anna 28\n# Ben 34\n```\n\n## in-Test auf Schlüssel\n\nMit **in** prüft man, ob ein Schlüssel vorhanden ist:\n\n```python\nprint('Anna' in alter) # True\nprint('xyz' in alter) # False\n```\n\nWichtig: `in` prüft nur **Schlüssel**, nicht Werte.\n\n## Zusammenfassung\n\n| Operation | Syntax |\n|---|---|\n| Erstellen | `d = {'k': v}` |\n| Abrufen | `d['k']` |\n| Sicherer Abruf | `d.get('k')` |\n| Hinzufügen | `d['k'] = v` |\n| Schlüssel prüfen | `'k' in d` |" + "body": "## .keys(), .values(), .items()\n\nDrei nützliche Methoden geben Übersichten zurück:\n\n```python\nalter = {'Anna': 28, 'Ben': 34}\nprint(list(alter.keys())) # ['Anna', 'Ben']\nprint(list(alter.values())) # [28, 34]\n```\n\n**.items()** liefert alle Paare, ideal für eine Schleife:\n\n```python\nfor name, a in alter.items():\n print(name, a)\n# Anna 28\n# Ben 34\n```\n\n## in-Test auf Schlüssel\n\nMit **in** prüft man, ob ein Schlüssel vorhanden ist:\n\n```python\nprint('Anna' in alter) # True\nprint('xyz' in alter) # False\n```\n\nWichtig: `in` prüft nur **Schlüssel**, nicht Werte.\n\n## Zusammenfassung\n\n| Operation | Syntax |\n|---|---|\n| Erstellen | `d = {'k': v}` |\n| Abrufen | `d['k']` |\n| Sicherer Abruf | `d.get('k')` |\n| Hinzufügen | `d['k'] = v` |\n| Schlüssel prüfen | `'k' in d` |" }, { "id": "ex-cloze-dict-erstellen", @@ -264,7 +264,7 @@ { "left": ".keys()", "right": "gibt alle Schlüssel zurück" }, { "left": ".values()", "right": "gibt alle Werte zurück" }, { "left": ".items()", "right": "gibt alle Schlüssel-Wert-Paare zurück" }, - { "left": ".get(k)", "right": "gibt den Wert zurück oder None — kein Fehler" } + { "left": ".get(k)", "right": "gibt den Wert zurück oder None, kein Fehler" } ], "direction": "target_to_source" } diff --git a/sets/de/python-basics/lessons/10-tupel-und-mengen.json b/sets/de/python-basics/lessons/10-tupel-und-mengen.json index d10355c..64dd43e 100644 --- a/sets/de/python-basics/lessons/10-tupel-und-mengen.json +++ b/sets/de/python-basics/lessons/10-tupel-und-mengen.json @@ -1,7 +1,7 @@ { "id": "10-tupel-und-mengen", "title": "Tupel und Mengen (tuple & set)", - "description": "Tupel als unveränderliche geordnete Folgen und Mengen als ungeordnete Sammlungen eindeutiger Elemente — Unterschiede zu Listen, Duplikate entfernen und Mengenoperationen.", + "description": "Tupel als unveränderliche geordnete Folgen und Mengen als ungeordnete Sammlungen eindeutiger Elemente, Unterschiede zu Listen, Duplikate entfernen und Mengenoperationen.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -11,7 +11,7 @@ "id": "tuple-erstellen", "stable_id": "card-ms942h7k04llb", "front": "Tupel erstellen (tuple)", - "back": "Ein Tupel speichert mehrere Werte in runden Klammern. Es ist unveränderlich — nach der Erstellung können keine Elemente hinzugefügt oder entfernt werden.", + "back": "Ein Tupel speichert mehrere Werte in runden Klammern. Es ist unveränderlich, nach der Erstellung können keine Elemente hinzugefügt oder entfernt werden.", "code_snippet": "punkt = (3, 7)\nprint(punkt)", "code_language": "python", "expected_output": "(3, 7)", @@ -94,12 +94,12 @@ { "id": "set-vereinigung", "stable_id": "card-ms942h7k04s5q", - "front": "Vereinigung (|) — set union", + "front": "Vereinigung (|) - set union", "back": "Der Operator | gibt eine neue Menge zurück, die alle Elemente beider Mengen enthält.", "code_snippet": "a = {1, 2, 3}\nb = {3, 4, 5}\nprint(len(a | b))", "code_language": "python", "expected_output": "5", - "hint": "| steht für 'oder' — alle Elemente aus beiden Mengen, keine Doppelten.", + "hint": "| steht für 'oder' - alle Elemente aus beiden Mengen, keine Doppelten.", "difficulty": 3, "tags": ["menge", "operation"], "media_type": "code" @@ -107,12 +107,12 @@ { "id": "set-schnittmenge", "stable_id": "card-ms942h7k04tph", - "front": "Schnittmenge (&) — set intersection", + "front": "Schnittmenge (&) - set intersection", "back": "Der Operator & gibt eine Menge zurück, die nur die Elemente enthält, die in beiden Mengen vorkommen.", "code_snippet": "a = {1, 2, 3}\nb = {2, 3, 4}\nprint(a & b == {2, 3})", "code_language": "python", "expected_output": "True", - "hint": "& steht für 'und' — nur die gemeinsamen Elemente.", + "hint": "& steht für 'und' - nur die gemeinsamen Elemente.", "difficulty": 3, "tags": ["menge", "operation"], "media_type": "code" @@ -122,7 +122,7 @@ "stable_id": "card-ms942h7k04uy7", "front": "Mengen sind ungeordnet", "back": "Elemente in einer Menge haben keine feste Reihenfolge. Man kann nicht mit einem Index auf ein einzelnes Element zugreifen.", - "notes": "Deshalb sind Mengenelemente nicht über einen Index abrufbar — dafür gibt es keine Garantie für die Reihenfolge.", + "notes": "Deshalb sind Mengenelemente nicht über einen Index abrufbar, dafür gibt es keine Garantie für die Reihenfolge.", "media_type": "text", "difficulty": 3, "tags": ["menge", "eigenschaft"] @@ -145,14 +145,14 @@ { "id": "step-tupel", "type": "theory", - "title": "Tupel — geordnet und unveränderlich", - "body": "# Tupel und Mengen (tuple & set)\n\n## Tupel (tuple)\n\nEin **Tupel** ist wie eine Liste, aber **unveränderlich (immutable)**. Es steht in **runden Klammern** `()`:\n\n```python\npunkt = (3, 7)\nprint(punkt) # (3, 7)\nprint(punkt[0]) # 3\n```\n\nNach der Erstellung können keine Elemente hinzugefügt, entfernt oder geändert werden. Der Versuch führt zu einem **TypeError**.\n\n## Tupel vs. Liste\n\n| Eigenschaft | Liste `[]` | Tupel `()` |\n|---|---|---|\n| Geordnet | ja | ja |\n| Doppelte Werte erlaubt | ja | ja |\n| Veränderlich | ja | **nein** |\n\nTupel eignen sich für Daten, die sich nicht ändern sollen — zum Beispiel Koordinaten oder feste Konfigurationen." + "title": "Tupel - geordnet und unveränderlich", + "body": "# Tupel und Mengen (tuple & set)\n\n## Tupel (tuple)\n\nEin **Tupel** ist wie eine Liste, aber **unveränderlich (immutable)**. Es steht in **runden Klammern** `()`:\n\n```python\npunkt = (3, 7)\nprint(punkt) # (3, 7)\nprint(punkt[0]) # 3\n```\n\nNach der Erstellung können keine Elemente hinzugefügt, entfernt oder geändert werden. Der Versuch führt zu einem **TypeError**.\n\n## Tupel vs. Liste\n\n| Eigenschaft | Liste `[]` | Tupel `()` |\n|---|---|---|\n| Geordnet | ja | ja |\n| Doppelte Werte erlaubt | ja | ja |\n| Veränderlich | ja | **nein** |\n\nTupel eignen sich für Daten, die sich nicht ändern sollen, zum Beispiel Koordinaten oder feste Konfigurationen." }, { "id": "step-mengen", "type": "theory", - "title": "Mengen — eindeutig und ungeordnet", - "body": "## Mengen (set)\n\nEine **Menge** speichert **eindeutige Elemente** ohne feste Reihenfolge. Sie steht in geschweiften Klammern `{}`:\n\n```python\nfarben = {'rot', 'gruen', 'blau'}\nprint(len(farben)) # 3\n```\n\n## Duplikate entfernen\n\n`set()` auf eine Liste angewendet entfernt alle doppelten Werte:\n\n```python\nzahlen = [1, 2, 2, 3, 3, 3]\nprint(len(set(zahlen))) # 3\n```\n\n## Mengenoperationen\n\n```python\na = {1, 2, 3}\nb = {3, 4, 5}\n```\n\n- **Vereinigung** `|` — alle Elemente aus beiden Mengen: `a | b` → {1, 2, 3, 4, 5}\n- **Schnittmenge** `&` — nur gemeinsame Elemente: `a & b` → {3}\n\n## Wichtig: Mengen sind ungeordnet\n\nEs gibt keinen Index-Zugriff `menge[0]` — die Reihenfolge ist nicht garantiert. Für eine Mitgliedschaftsprüfung nutzt man **in**:\n\n```python\nprint('rot' in farben) # True\n```" + "title": "Mengen - eindeutig und ungeordnet", + "body": "## Mengen (set)\n\nEine **Menge** speichert **eindeutige Elemente** ohne feste Reihenfolge. Sie steht in geschweiften Klammern `{}`:\n\n```python\nfarben = {'rot', 'gruen', 'blau'}\nprint(len(farben)) # 3\n```\n\n## Duplikate entfernen\n\n`set()` auf eine Liste angewendet entfernt alle doppelten Werte:\n\n```python\nzahlen = [1, 2, 2, 3, 3, 3]\nprint(len(set(zahlen))) # 3\n```\n\n## Mengenoperationen\n\n```python\na = {1, 2, 3}\nb = {3, 4, 5}\n```\n\n- **Vereinigung** `|` - alle Elemente aus beiden Mengen: `a | b` → {1, 2, 3, 4, 5}\n- **Schnittmenge** `&` - nur gemeinsame Elemente: `a & b` → {3}\n\n## Wichtig: Mengen sind ungeordnet\n\nEs gibt keinen Index-Zugriff `menge[0]` - die Reihenfolge ist nicht garantiert. Für eine Mitgliedschaftsprüfung nutzt man **in**:\n\n```python\nprint('rot' in farben) # True\n```" }, { "id": "step-vergleich", @@ -207,7 +207,7 @@ "id": "ex-free-unveraenderlich", "stable_id": "ex-ms942h7k04y48", "type": "free_text", - "prompt": "Welche Eigenschaft unterscheidet ein Tupel von einer Liste — Tupel können nicht verändert werden?", + "prompt": "Welche Eigenschaft unterscheidet ein Tupel von einer Liste, Tupel können nicht verändert werden?", "card_ids": ["tuple-unveraenderlich", "tuple-vs-liste"], "accept": ["unveränderlich", "immutable", "nicht veränderbar", "nicht veränderlich"], "hint": "Das englische Fachwort lautet 'immutable'.", diff --git a/sets/de/python-basics/lessons/11-fehler-und-ausnahmen.json b/sets/de/python-basics/lessons/11-fehler-und-ausnahmen.json index 95ae7c5..613d05f 100644 --- a/sets/de/python-basics/lessons/11-fehler-und-ausnahmen.json +++ b/sets/de/python-basics/lessons/11-fehler-und-ausnahmen.json @@ -1,7 +1,7 @@ { "id": "11-fehler-und-ausnahmen", "title": "Fehler und Ausnahmen (try / except)", - "description": "try / except, häufige Ausnahmetypen, else und finally, eigene Fehler mit raise — auf Deutsch erklärt.", + "description": "try / except, häufige Ausnahmetypen, else und finally, eigene Fehler mit raise, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -34,7 +34,7 @@ "id": "exc-valueerror", "stable_id": "card-ms942h7l05529", "front": "ValueError", - "back": "Wird ausgelöst, wenn ein Wert den richtigen Typ hat, aber einen unzulässigen Inhalt besitzt — z. B. int('abc').", + "back": "Wird ausgelöst, wenn ein Wert den richtigen Typ hat, aber einen unzulässigen Inhalt besitzt, z. B. int('abc').", "code_snippet": "try:\n zahl = int('hallo')\nexcept ValueError:\n print('Kein gültiger Zahlenwert')", "code_language": "python", "expected_output": "Kein gültiger Zahlenwert", @@ -47,7 +47,7 @@ "id": "exc-typeerror", "stable_id": "card-ms942h7l0560w", "front": "TypeError", - "back": "Wird ausgelöst, wenn eine Operation auf einem Objekt des falschen Typs angewendet wird — z. B. '2' + 2.", + "back": "Wird ausgelöst, wenn eine Operation auf einem Objekt des falschen Typs angewendet wird, z. B. '2' + 2.", "code_snippet": "try:\n ergebnis = '2' + 2\nexcept TypeError:\n print('Typen passen nicht zusammen')", "code_language": "python", "expected_output": "Typen passen nicht zusammen", @@ -112,7 +112,7 @@ "id": "exc-finally", "stable_id": "card-ms942h7l05bx6", "front": "finally", - "back": "Der finally-Block wird immer ausgeführt — egal ob ein Fehler aufgetreten ist oder nicht. Er eignet sich zum Aufräumen von Ressourcen.", + "back": "Der finally-Block wird immer ausgeführt, egal ob ein Fehler aufgetreten ist oder nicht. Er eignet sich zum Aufräumen von Ressourcen.", "code_snippet": "try:\n x = 10 / 2\nexcept ZeroDivisionError:\n print('Fehler')\nfinally:\n print('Fertig')", "code_language": "python", "expected_output": "Fertig", @@ -125,7 +125,7 @@ "id": "exc-raise", "stable_id": "card-ms942h7l05c0k", "front": "raise", - "back": "Mit raise löst man bewusst eine Ausnahme aus — z. B. um ungültige Eingaben abzulehnen.", + "back": "Mit raise löst man bewusst eine Ausnahme aus, z. B. um ungültige Eingaben abzulehnen.", "code_snippet": "def nur_positiv(n):\n if n <= 0:\n raise ValueError('Zahl muss positiv sein')\n return n\n\ntry:\n nur_positiv(-3)\nexcept ValueError as e:\n print(e)", "code_language": "python", "expected_output": "Zahl muss positiv sein", @@ -169,13 +169,13 @@ "id": "theory-ausnahmetypen", "type": "theory", "title": "Häufige Ausnahmetypen", - "body": "## Die wichtigsten Ausnahmetypen\n\n| Typ | Wann tritt er auf? |\n|-----|--------------------|\n| `ValueError` | Falscher Inhalt, richtiger Typ — z. B. `int('abc')` |\n| `TypeError` | Falscher Typ — z. B. `'2' + 2` |\n| `ZeroDivisionError` | Division durch null: `10 / 0` |\n| `KeyError` | Dictionary-Schlüssel nicht vorhanden |\n| `IndexError` | Listen-Index außerhalb des Bereichs |\n\nMan kann mehrere `except`-Blöcke kombinieren:\n\n```python\ntry:\n wert = liste[index]\nexcept IndexError:\n print('Index ungültig')\nexcept TypeError:\n print('Falscher Typ')\n```" + "body": "## Die wichtigsten Ausnahmetypen\n\n| Typ | Wann tritt er auf? |\n|-----|--------------------|\n| `ValueError` | Falscher Inhalt, richtiger Typ, z. B. `int('abc')` |\n| `TypeError` | Falscher Typ, z. B. `'2' + 2` |\n| `ZeroDivisionError` | Division durch null: `10 / 0` |\n| `KeyError` | Dictionary-Schlüssel nicht vorhanden |\n| `IndexError` | Listen-Index außerhalb des Bereichs |\n\nMan kann mehrere `except`-Blöcke kombinieren:\n\n```python\ntry:\n wert = liste[index]\nexcept IndexError:\n print('Index ungültig')\nexcept TypeError:\n print('Falscher Typ')\n```" }, { "id": "theory-else-finally-raise", "type": "theory", "title": "else, finally und raise", - "body": "## else und finally\n\n```python\ntry:\n x = int('42')\nexcept ValueError:\n print('Fehler!')\nelse:\n print('Kein Fehler — x ist', x) # läuft nur ohne Fehler\nfinally:\n print('Immer ausgeführt') # läuft immer\n```\n\nAusgabe:\n\n```\nKein Fehler — x ist 42\nImmer ausgeführt\n```\n\n- **else** läuft, wenn *kein* Fehler aufgetreten ist.\n- **finally** läuft *immer* — ideal zum Schließen von Dateien oder Verbindungen.\n\n## Eigene Fehler auslösen mit raise\n\nMit `raise` kann man selbst eine Ausnahme werfen:\n\n```python\ndef nur_positiv(n):\n if n <= 0:\n raise ValueError('Zahl muss positiv sein')\n return n\n```\n\nDas macht den Code robuster: falsche Eingaben werden sofort abgelehnt." + "body": "## else und finally\n\n```python\ntry:\n x = int('42')\nexcept ValueError:\n print('Fehler!')\nelse:\n print('Kein Fehler, x ist', x) # läuft nur ohne Fehler\nfinally:\n print('Immer ausgeführt') # läuft immer\n```\n\nAusgabe:\n\n```\nKein Fehler, x ist 42\nImmer ausgeführt\n```\n\n- **else** läuft, wenn *kein* Fehler aufgetreten ist.\n- **finally** läuft *immer*, ideal zum Schließen von Dateien oder Verbindungen.\n\n## Eigene Fehler auslösen mit raise\n\nMit `raise` kann man selbst eine Ausnahme werfen:\n\n```python\ndef nur_positiv(n):\n if n <= 0:\n raise ValueError('Zahl muss positiv sein')\n return n\n```\n\nDas macht den Code robuster: falsche Eingaben werden sofort abgelehnt." }, { "id": "ex-cloze-try", @@ -209,9 +209,9 @@ "prompt": "Ordne jeden Ausnahmetyp der Situation zu, in der er auftritt.", "card_ids": ["exc-valueerror", "exc-typeerror", "exc-zerodivision", "exc-keyerror", "exc-indexerror"], "pairs": [ - { "left": "ValueError", "right": "int('abc') — falscher Inhalt" }, - { "left": "TypeError", "right": "'2' + 2 — falscher Typ" }, - { "left": "ZeroDivisionError", "right": "10 / 0 — Division durch null" }, + { "left": "ValueError", "right": "int('abc') - falscher Inhalt" }, + { "left": "TypeError", "right": "'2' + 2, falscher Typ" }, + { "left": "ZeroDivisionError", "right": "10 / 0, Division durch null" }, { "left": "KeyError", "right": "Dictionary-Schlüssel nicht vorhanden" }, { "left": "IndexError", "right": "Listen-Index außerhalb des Bereichs" } ], diff --git a/sets/de/python-basics/lessons/12-module-und-imports.json b/sets/de/python-basics/lessons/12-module-und-imports.json index 33829c2..3b0381b 100644 --- a/sets/de/python-basics/lessons/12-module-und-imports.json +++ b/sets/de/python-basics/lessons/12-module-und-imports.json @@ -1,7 +1,7 @@ { "id": "12-module-und-imports", "title": "Module und Imports", - "description": "import, from ... import, Aliase, Standardbibliothek mit math und random — auf Deutsch erklärt.", + "description": "import, from ... import, Aliase, Standardbibliothek mit math und random, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -34,7 +34,7 @@ "id": "mod-from-import", "stable_id": "card-ms942h7l05p09", "front": "from modul import name", - "back": "Importiert einen einzelnen Namen direkt in den aktuellen Namensraum — kein Modulpräfix nötig.", + "back": "Importiert einen einzelnen Namen direkt in den aktuellen Namensraum, kein Modulpräfix nötig.", "code_snippet": "from math import sqrt\nprint(sqrt(25))", "code_language": "python", "expected_output": "5.0", @@ -73,11 +73,11 @@ "id": "mod-math-pi", "stable_id": "card-ms942h7l05sql", "front": "math.pi", - "back": "Die mathematische Konstante π (Pi) als float — ungefähr 3,14159.", + "back": "Die mathematische Konstante π (Pi) als float, ungefähr 3,14159.", "code_snippet": "import math\nprint(math.pi)", "code_language": "python", "expected_output": "3.141592653589793", - "hint": "pi ist eine Konstante, keine Funktion — kein () dahinter.", + "hint": "pi ist eine Konstante, keine Funktion, kein () dahinter.", "difficulty": 2, "tags": ["module", "math"], "media_type": "code" @@ -96,7 +96,7 @@ "id": "mod-standardbibliothek", "stable_id": "card-ms942h7l05ufh", "front": "Standardbibliothek (Standard Library)", - "back": "Eine umfangreiche Sammlung von Modulen, die mit Python ausgeliefert wird — kein separater Install nötig.", + "back": "Eine umfangreiche Sammlung von Modulen, die mit Python ausgeliefert wird, kein separater Install nötig.", "notes": "Beispiele: math, random, os, datetime, json, csv.", "media_type": "text", "difficulty": 2, @@ -116,7 +116,7 @@ "id": "mod-from-import-mehrere", "stable_id": "card-ms942h7l05wqo", "front": "from modul import name1, name2", - "back": "Mehrere Namen aus einem Modul auf einmal importieren — durch Kommas getrennt.", + "back": "Mehrere Namen aus einem Modul auf einmal importieren, durch Kommas getrennt.", "code_snippet": "from math import sqrt, pi\nprint(sqrt(4))\nprint(pi)", "code_language": "python", "expected_output": "2.0\n3.141592653589793", @@ -140,14 +140,14 @@ { "id": "theory-import-grundlagen", "type": "theory", - "title": "Module importieren — import und from ... import", - "body": "# Module und Imports\n\nEin **Modul (Module)** ist eine Python-Datei mit fertigem Code (Funktionen, Variablen, Klassen). Mit **import** bindet man es ein.\n\n## import modul\n\n```python\nimport math\nprint(math.pi) # 3.141592653589793\nprint(math.sqrt(16)) # 4.0\n```\n\nNach dem Import greift man mit **modul.name** auf Inhalte zu.\n\n## from modul import name\n\nNur einen einzelnen Namen importieren — kein Präfix nötig:\n\n```python\nfrom math import sqrt\nprint(sqrt(25)) # 5.0\n```\n\nMehrere Namen auf einmal:\n\n```python\nfrom math import sqrt, pi\n```\n\n## import ... as (Alias)\n\nEinem Modul einen kürzeren Namen geben:\n\n```python\nimport math as m\nprint(m.sqrt(9)) # 3.0\n```\n\nAliase sind besonders nützlich bei Modulen mit langen Namen." + "title": "Module importieren - import und from ... import", + "body": "# Module und Imports\n\nEin **Modul (Module)** ist eine Python-Datei mit fertigem Code (Funktionen, Variablen, Klassen). Mit **import** bindet man es ein.\n\n## import modul\n\n```python\nimport math\nprint(math.pi) # 3.141592653589793\nprint(math.sqrt(16)) # 4.0\n```\n\nNach dem Import greift man mit **modul.name** auf Inhalte zu.\n\n## from modul import name\n\nNur einen einzelnen Namen importieren, kein Präfix nötig:\n\n```python\nfrom math import sqrt\nprint(sqrt(25)) # 5.0\n```\n\nMehrere Namen auf einmal:\n\n```python\nfrom math import sqrt, pi\n```\n\n## import ... as (Alias)\n\nEinem Modul einen kürzeren Namen geben:\n\n```python\nimport math as m\nprint(m.sqrt(9)) # 3.0\n```\n\nAliase sind besonders nützlich bei Modulen mit langen Namen." }, { "id": "theory-standardbibliothek", "type": "theory", "title": "Standardbibliothek: math und random", - "body": "## Die Standardbibliothek\n\nPython wird mit einer umfangreichen **Standardbibliothek (Standard Library)** ausgeliefert — viele Module sind sofort nutzbar ohne Installation.\n\n### math — Mathematische Funktionen\n\n| Name | Beschreibung |\n|------|--------------|\n| `math.sqrt(x)` | Quadratwurzel von x (immer float) |\n| `math.pi` | Konstante π ≈ 3,14159 |\n| `math.floor(x)` | Abrunden zur nächsten ganzen Zahl |\n| `math.ceil(x)` | Aufrunden |\n\n```python\nimport math\nprint(math.sqrt(16)) # 4.0\nprint(math.pi) # 3.141592653589793\n```\n\n### random — Zufallszahlen\n\n```python\nimport random\nzahl = random.randint(1, 6) # zufällige Zahl von 1 bis 6\nprint(zahl) # z. B. 3 (jedes Mal anders)\n```\n\n`random.randint(a, b)` gibt eine zufällige ganze Zahl zurück — **beide Grenzen eingeschlossen**.\n\n### Module und Pakete\n\nMehrere Module in einem Verzeichnis bilden ein **Paket (Package)**. Der Import funktioniert genauso: `import paket.modul`.\n\nJedes Modul hat seinen eigenen **Namensraum (Namespace)** — das verhindert Namenskonflikte." + "body": "## Die Standardbibliothek\n\nPython wird mit einer umfangreichen **Standardbibliothek (Standard Library)** ausgeliefert, viele Module sind sofort nutzbar ohne Installation.\n\n### math - Mathematische Funktionen\n\n| Name | Beschreibung |\n|------|--------------|\n| `math.sqrt(x)` | Quadratwurzel von x (immer float) |\n| `math.pi` | Konstante π ≈ 3,14159 |\n| `math.floor(x)` | Abrunden zur nächsten ganzen Zahl |\n| `math.ceil(x)` | Aufrunden |\n\n```python\nimport math\nprint(math.sqrt(16)) # 4.0\nprint(math.pi) # 3.141592653589793\n```\n\n### random - Zufallszahlen\n\n```python\nimport random\nzahl = random.randint(1, 6) # zufällige Zahl von 1 bis 6\nprint(zahl) # z. B. 3 (jedes Mal anders)\n```\n\n`random.randint(a, b)` gibt eine zufällige ganze Zahl zurück, **beide Grenzen eingeschlossen**.\n\n### Module und Pakete\n\nMehrere Module in einem Verzeichnis bilden ein **Paket (Package)**. Der Import funktioniert genauso: `import paket.modul`.\n\nJedes Modul hat seinen eigenen **Namensraum (Namespace)**, das verhindert Namenskonflikte." }, { "id": "ex-cloze-import", @@ -200,7 +200,7 @@ "prompt": "Ordne jede Import-Form ihrer Beschreibung zu.", "card_ids": ["mod-import", "mod-from-import", "mod-alias"], "pairs": [ - { "left": "import math", "right": "ganzes Modul — Zugriff mit math.name" }, + { "left": "import math", "right": "ganzes Modul, Zugriff mit math.name" }, { "left": "from math import sqrt", "right": "einzelner Name, kein Präfix nötig" }, { "left": "import math as m", "right": "Modul unter Kurzname m verfügbar" } ], @@ -219,7 +219,7 @@ "prompt": "Welchen Wert gibt math.sqrt(16) zurück?", "card_ids": ["mod-math-sqrt"], "accept": ["4.0", "4", "vier"], - "hint": "Die Quadratwurzel von 16 ist 4 — als float.", + "hint": "Die Quadratwurzel von 16 ist 4, als float.", "distractors": ["2.0", "8.0", "16.0"], "direction": "target_to_source" } diff --git a/sets/de/python-basics/lessons/13-dateien.json b/sets/de/python-basics/lessons/13-dateien.json index d37f299..335cb75 100644 --- a/sets/de/python-basics/lessons/13-dateien.json +++ b/sets/de/python-basics/lessons/13-dateien.json @@ -1,7 +1,7 @@ { "id": "13-dateien", "title": "Dateien lesen und schreiben", - "description": "open(), Dateimodi, read/write-Methoden, das with-Statement als Context Manager, über Zeilen iterieren — auf Deutsch erklärt.", + "description": "open(), Dateimodi, read/write-Methoden, das with-Statement als Context Manager, über Zeilen iterieren, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -88,7 +88,7 @@ "id": "dat-with", "stable_id": "card-ms942h7m06eml", "front": "with open(...) as f:", - "back": "Das with-Statement öffnet eine Datei als Context Manager. Die Datei wird automatisch geschlossen, sobald der with-Block endet — auch bei einem Fehler.", + "back": "Das with-Statement öffnet eine Datei als Context Manager. Die Datei wird automatisch geschlossen, sobald der with-Block endet, auch bei einem Fehler.", "code_snippet": "with open('notiz.txt', 'w') as f:\n f.write('Hallo Datei')\nprint('Datei geschlossen')", "code_language": "python", "expected_output": "Datei geschlossen", @@ -101,7 +101,7 @@ "id": "dat-context-manager", "stable_id": "card-ms942h7m06fhi", "front": "Context Manager", - "back": "Ein Objekt, das mit dem with-Statement zusammenarbeitet und automatisch Auf- und Abbau-Aktionen ausführt — z. B. eine Datei sicher schließen.", + "back": "Ein Objekt, das mit dem with-Statement zusammenarbeitet und automatisch Auf- und Abbau-Aktionen ausführt, z. B. eine Datei sicher schließen.", "notes": "open() gibt einen Context Manager zurück. Eigene Context Manager kann man mit __enter__ und __exit__ erstellen.", "media_type": "text", "difficulty": 3, @@ -111,7 +111,7 @@ "id": "dat-iteration", "stable_id": "card-ms942h7m06glh", "front": "Über Zeilen einer Datei iterieren", - "back": "Mit einer for-Schleife kann man direkt über ein geöffnetes Datei-Objekt iterieren — Python liefert dabei Zeile für Zeile.", + "back": "Mit einer for-Schleife kann man direkt über ein geöffnetes Datei-Objekt iterieren, Python liefert dabei Zeile für Zeile.", "code_snippet": "with open('liste.txt', 'w') as f:\n f.write('Apfel\\nBanane\\nKirsche')\n\nwith open('liste.txt', 'r') as f:\n for zeile in f:\n print(zeile.strip())", "code_language": "python", "expected_output": "Apfel\nBanane\nKirsche", @@ -136,19 +136,19 @@ "id": "theory-open-modi", "type": "theory", "title": "Dateien öffnen mit open() und Dateimodi", - "body": "# Dateien lesen und schreiben\n\nMit **open()** öffnet man eine Datei. Die Funktion gibt ein **Datei-Objekt** zurück, über das man liest oder schreibt.\n\n```python\ndatei = open('meine_datei.txt', 'r') # zum Lesen öffnen\n```\n\n## Die drei wichtigsten Modi\n\n| Modus | Bedeutung | Datei existiert nicht? |\n|-------|-----------|------------------------|\n| `'r'` | lesen (read) | → FileNotFoundError |\n| `'w'` | schreiben (write) | → neue Datei anlegen |\n| `'a'` | anhängen (append) | → neue Datei anlegen |\n\nBeim Modus `'w'` wird eine vorhandene Datei **vollständig überschrieben**. Mit `'a'` bleibt der alte Inhalt erhalten.\n\n## Lesen und Schreiben\n\n```python\n# Schreiben\ndatei = open('gruss.txt', 'w')\ndatei.write('Hallo Welt\\n')\ndatei.close() # nicht vergessen!\n\n# Lesen\ndatei = open('gruss.txt', 'r')\ninhalt = datei.read() # gesamter Inhalt als String\ndatei.close()\n```\n\nNeben `.read()` gibt es:\n- **`.readline()`** — eine Zeile lesen\n- **`.readlines()`** — alle Zeilen als Liste" + "body": "# Dateien lesen und schreiben\n\nMit **open()** öffnet man eine Datei. Die Funktion gibt ein **Datei-Objekt** zurück, über das man liest oder schreibt.\n\n```python\ndatei = open('meine_datei.txt', 'r') # zum Lesen öffnen\n```\n\n## Die drei wichtigsten Modi\n\n| Modus | Bedeutung | Datei existiert nicht? |\n|-------|-----------|------------------------|\n| `'r'` | lesen (read) | → FileNotFoundError |\n| `'w'` | schreiben (write) | → neue Datei anlegen |\n| `'a'` | anhängen (append) | → neue Datei anlegen |\n\nBeim Modus `'w'` wird eine vorhandene Datei **vollständig überschrieben**. Mit `'a'` bleibt der alte Inhalt erhalten.\n\n## Lesen und Schreiben\n\n```python\n# Schreiben\ndatei = open('gruss.txt', 'w')\ndatei.write('Hallo Welt\\n')\ndatei.close() # nicht vergessen!\n\n# Lesen\ndatei = open('gruss.txt', 'r')\ninhalt = datei.read() # gesamter Inhalt als String\ndatei.close()\n```\n\nNeben `.read()` gibt es:\n- **`.readline()`**: eine Zeile lesen\n- **`.readlines()`**: alle Zeilen als Liste" }, { "id": "theory-with-statement", "type": "theory", - "title": "Das with-Statement — Dateien sicher schließen", - "body": "## Das with-Statement (Context Manager)\n\nOhne `with` muss man `.close()` manuell aufrufen — und vergisst es leicht. Das **with-Statement** sorgt dafür, dass die Datei **automatisch geschlossen** wird, sobald der Block endet — selbst bei einem Fehler:\n\n```python\nwith open('notiz.txt', 'w') as f:\n f.write('Hallo Datei')\n# hier ist die Datei schon zu — kein .close() nötig\n```\n\n`open()` ist ein **Context Manager**: Es implementiert `__enter__` (öffnen) und `__exit__` (schließen), die das `with`-Statement automatisch aufruft.\n\n## Über Zeilen iterieren\n\nMit einer **for-Schleife** kann man direkt über das Datei-Objekt iterieren:\n\n```python\nwith open('liste.txt', 'r') as f:\n for zeile in f:\n print(zeile.strip()) # .strip() entfernt den \\n am Ende\n```\n\nDas ist effizienter als `.readlines()`, weil Python immer nur eine Zeile im Speicher hält." + "title": "Das with-Statement - Dateien sicher schließen", + "body": "## Das with-Statement (Context Manager)\n\nOhne `with` muss man `.close()` manuell aufrufen, und vergisst es leicht. Das **with-Statement** sorgt dafür, dass die Datei **automatisch geschlossen** wird, sobald der Block endet, selbst bei einem Fehler:\n\n```python\nwith open('notiz.txt', 'w') as f:\n f.write('Hallo Datei')\n# hier ist die Datei schon zu - kein .close() nötig\n```\n\n`open()` ist ein **Context Manager**: Es implementiert `__enter__` (öffnen) und `__exit__` (schließen), die das `with`-Statement automatisch aufruft.\n\n## Über Zeilen iterieren\n\nMit einer **for-Schleife** kann man direkt über das Datei-Objekt iterieren:\n\n```python\nwith open('liste.txt', 'r') as f:\n for zeile in f:\n print(zeile.strip()) # .strip() entfernt den \\n am Ende\n```\n\nDas ist effizienter als `.readlines()`, weil Python immer nur eine Zeile im Speicher hält." }, { "id": "theory-lese-methoden", "type": "theory", "title": "Lese-Methoden im Überblick", - "body": "## Drei Wege, eine Datei zu lesen\n\n```python\nwith open('text.txt', 'r') as f:\n # Methode 1: gesamten Inhalt auf einmal\n inhalt = f.read() # ein einziger String\n\nwith open('text.txt', 'r') as f:\n # Methode 2: Zeile für Zeile manuell\n erste = f.readline() # eine Zeile\n zweite = f.readline() # nächste Zeile\n\nwith open('text.txt', 'r') as f:\n # Methode 3: alle Zeilen als Liste\n zeilen = f.readlines() # ['Zeile1\\n', 'Zeile2\\n', ...]\n```\n\n| Methode | Gibt zurück |\n|---------|-------------|\n| `.read()` | gesamter Inhalt als String |\n| `.readline()` | eine Zeile als String |\n| `.readlines()` | alle Zeilen als Liste |\n\nFür große Dateien empfiehlt sich die **for-Schleife** über das Datei-Objekt — sie hält immer nur eine Zeile im Speicher." + "body": "## Drei Wege, eine Datei zu lesen\n\n```python\nwith open('text.txt', 'r') as f:\n # Methode 1: gesamten Inhalt auf einmal\n inhalt = f.read() # ein einziger String\n\nwith open('text.txt', 'r') as f:\n # Methode 2: Zeile für Zeile manuell\n erste = f.readline() # eine Zeile\n zweite = f.readline() # nächste Zeile\n\nwith open('text.txt', 'r') as f:\n # Methode 3: alle Zeilen als Liste\n zeilen = f.readlines() # ['Zeile1\\n', 'Zeile2\\n', ...]\n```\n\n| Methode | Gibt zurück |\n|---------|-------------|\n| `.read()` | gesamter Inhalt als String |\n| `.readline()` | eine Zeile als String |\n| `.readlines()` | alle Zeilen als Liste |\n\nFür große Dateien empfiehlt sich die **for-Schleife** über das Datei-Objekt, sie hält immer nur eine Zeile im Speicher." }, { "id": "ex-match-modi", @@ -162,9 +162,9 @@ "prompt": "Ordne jeden Modus seiner Bedeutung zu.", "card_ids": ["dat-modus-r", "dat-modus-w", "dat-modus-a"], "pairs": [ - { "left": "'r'", "right": "Datei lesen — muss existieren" }, - { "left": "'w'", "right": "Datei schreiben — überschreibt vorhandenes" }, - { "left": "'a'", "right": "Datei anhängen — alter Inhalt bleibt erhalten" } + { "left": "'r'", "right": "Datei lesen, muss existieren" }, + { "left": "'w'", "right": "Datei schreiben, überschreibt vorhandenes" }, + { "left": "'a'", "right": "Datei anhängen, alter Inhalt bleibt erhalten" } ], "direction": "target_to_source" } @@ -185,7 +185,7 @@ { "accept": ["with"] } ], "cloze_mode": "type", - "hint": "Englisch für 'mit' — leitet einen Context-Manager-Block ein." + "hint": "Englisch für 'mit' - leitet einen Context-Manager-Block ein." } }, { diff --git a/sets/de/python-basics/lessons/14-comprehensions.json b/sets/de/python-basics/lessons/14-comprehensions.json index 4be9faf..e9cf762 100644 --- a/sets/de/python-basics/lessons/14-comprehensions.json +++ b/sets/de/python-basics/lessons/14-comprehensions.json @@ -1,7 +1,7 @@ { "id": "14-comprehensions", "title": "List Comprehensions", - "description": "List Comprehensions kompakt erklärt: Syntax, Bedingungen, Vergleich mit der for-Schleife und ein Ausblick auf Dict Comprehensions — auf Deutsch erklärt.", + "description": "List Comprehensions kompakt erklärt: Syntax, Bedingungen, Vergleich mit der for-Schleife und ein Ausblick auf Dict Comprehensions, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -15,7 +15,7 @@ "code_snippet": "zahlen = [1, 2, 3, 4]\ndoppelt = [x * 2 for x in zahlen]\nprint(doppelt)", "code_language": "python", "expected_output": "[2, 4, 6, 8]", - "hint": "Der Ausdruck steht links, die Schleife rechts — alles in eckigen Klammern.", + "hint": "Der Ausdruck steht links, die Schleife rechts, alles in eckigen Klammern.", "difficulty": 3, "tags": ["comprehensions", "listen"], "media_type": "code" @@ -122,7 +122,7 @@ "id": "py-comp-ergebnis-typ", "stable_id": "card-ms942h7m06yk1", "front": "Ergebnistyp einer List Comprehension", - "back": "Eine List Comprehension gibt immer eine neue Liste (list) zurück — unabhängig vom Typ des Iterables.", + "back": "Eine List Comprehension gibt immer eine neue Liste (list) zurück, unabhängig vom Typ des Iterables.", "code_snippet": "ergebnis = [c for c in 'abc']\nprint(ergebnis)\nprint(type(ergebnis))", "code_language": "python", "expected_output": "['a', 'b', 'c']\n", @@ -137,13 +137,13 @@ "id": "theory-comp-grundform", "type": "theory", "title": "Was ist eine List Comprehension?", - "body": "# List Comprehensions\n\nEine **List Comprehension** ist eine kompakte Syntax, um aus einem Iterable eine neue Liste zu erzeugen.\n\n## Grundform\n\n```python\n[Ausdruck for Element in Iterable]\n```\n\nBeispiel — jede Zahl verdoppeln:\n\n```python\nzahlen = [1, 2, 3, 4]\ndoppelt = [x * 2 for x in zahlen]\nprint(doppelt) # [2, 4, 6, 8]\n```\n\nDas ist gleichbedeutend mit dieser klassischen `for`-Schleife:\n\n```python\ndoppelt = []\nfor x in zahlen:\n doppelt.append(x * 2)\n```\n\nDie Comprehension ist kürzer und oft gut lesbar — sie steht in **eckigen Klammern** und gibt immer eine **Liste** zurück." + "body": "# List Comprehensions\n\nEine **List Comprehension** ist eine kompakte Syntax, um aus einem Iterable eine neue Liste zu erzeugen.\n\n## Grundform\n\n```python\n[Ausdruck for Element in Iterable]\n```\n\nBeispiel, jede Zahl verdoppeln:\n\n```python\nzahlen = [1, 2, 3, 4]\ndoppelt = [x * 2 for x in zahlen]\nprint(doppelt) # [2, 4, 6, 8]\n```\n\nDas ist gleichbedeutend mit dieser klassischen `for`-Schleife:\n\n```python\ndoppelt = []\nfor x in zahlen:\n doppelt.append(x * 2)\n```\n\nDie Comprehension ist kürzer und oft gut lesbar, sie steht in **eckigen Klammern** und gibt immer eine **Liste** zurück." }, { "id": "theory-comp-bedingung", "type": "theory", "title": "Bedingungen und Dict Comprehensions", - "body": "## Filtern mit if\n\nEin optionales `if` am Ende filtert die Elemente — nur Elemente, für die die Bedingung `True` ergibt, landen in der neuen Liste:\n\n```python\nzahlen = [1, 2, 3, 4, 5, 6]\ngerade = [x for x in zahlen if x % 2 == 0]\nprint(gerade) # [2, 4, 6]\n```\n\n## Dict Comprehension\n\nMit **geschweiften Klammern** und einem Doppelpunkt entsteht statt einer Liste ein Dictionary:\n\n```python\nzahlen = [1, 2, 3]\nquadrate = {x: x ** 2 for x in zahlen}\nprint(quadrate) # {1: 1, 2: 4, 3: 9}\n```\n\nDie Grundstruktur ist dieselbe — nur `[Ausdruck ...]` wird zu `{Schlüssel: Wert ...}`." + "body": "## Filtern mit if\n\nEin optionales `if` am Ende filtert die Elemente, nur Elemente, für die die Bedingung `True` ergibt, landen in der neuen Liste:\n\n```python\nzahlen = [1, 2, 3, 4, 5, 6]\ngerade = [x for x in zahlen if x % 2 == 0]\nprint(gerade) # [2, 4, 6]\n```\n\n## Dict Comprehension\n\nMit **geschweiften Klammern** und einem Doppelpunkt entsteht statt einer Liste ein Dictionary:\n\n```python\nzahlen = [1, 2, 3]\nquadrate = {x: x ** 2 for x in zahlen}\nprint(quadrate) # {1: 1, 2: 4, 3: 9}\n```\n\nDie Grundstruktur ist dieselbe, nur `[Ausdruck ...]` wird zu `{Schlüssel: Wert ...}`." }, { "id": "ex-cloze-comp-basis", @@ -195,7 +195,7 @@ "prompt": "Welchen Datentyp gibt eine List Comprehension immer zurück?", "card_ids": ["py-comp-ergebnis-typ"], "accept": ["list", "Liste", "eine Liste", "list()"], - "hint": "Der Name steht zwischen den eckigen Klammern — nein, der Typ selbst.", + "hint": "Der Name steht zwischen den eckigen Klammern, nein, der Typ selbst.", "distractors": ["tuple", "dict", "set", "generator"], "direction": "target_to_source" } @@ -229,7 +229,7 @@ "prompt": "Setze die List Comprehension zusammen, die nur gerade Zahlen aus 'zahlen' auswählt.", "card_ids": ["py-list-comp-bedingung"], "tiles": ["[", "x", "for", "x", "in", "zahlen", "if", "x % 2 == 0", "]"], - "hint": "Ausdruck — for — Variable — in — Iterable — if — Bedingung.", + "hint": "Ausdruck, for, Variable, in, Iterable, if, Bedingung.", "direction": "target_to_source" } }, diff --git a/sets/de/python-basics/lessons/15-wiederholung.json b/sets/de/python-basics/lessons/15-wiederholung.json index 4486c86..4456fa2 100644 --- a/sets/de/python-basics/lessons/15-wiederholung.json +++ b/sets/de/python-basics/lessons/15-wiederholung.json @@ -1,7 +1,7 @@ { "id": "15-wiederholung", "title": "Wiederholung und Festigung", - "description": "Kompakte Wiederholung des gesamten Kurses: print(), Variablen, Datentypen, Operatoren, Strings, Listen, Bedingungen, Schleifen, Funktionen, Dictionaries, Fehlerbehandlung und Comprehensions — auf Deutsch erklärt.", + "description": "Kompakte Wiederholung des gesamten Kurses: print(), Variablen, Datentypen, Operatoren, Strings, Listen, Bedingungen, Schleifen, Funktionen, Dictionaries, Fehlerbehandlung und Comprehensions, auf Deutsch erklärt.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -244,7 +244,7 @@ "type": "cloze", "prompt": "Ergänze das Schlüsselwort, mit dem man in Python eine Funktion definiert.", "card_ids": ["rep-funktionen"], - "sentence": "___ quadrat(x): return x ** 2 — so definiert man eine Funktion.", + "sentence": "___ quadrat(x): return x ** 2, so definiert man eine Funktion.", "blanks": [ { "accept": ["def"] } ], @@ -314,7 +314,7 @@ "prompt": "Setze die List Comprehension zusammen, die jede Zahl aus 'zahlen' verdoppelt.", "card_ids": ["rep-comprehension"], "tiles": ["[", "x * 2", "for", "x", "in", "zahlen", "]"], - "hint": "Ausdruck — for — Variable — in — Iterable, alles in eckigen Klammern.", + "hint": "Ausdruck, for, Variable, in, Iterable, alles in eckigen Klammern.", "direction": "target_to_source" } } diff --git a/sets/de/python-basics/manifest.yaml b/sets/de/python-basics/manifest.yaml index 8a57433..bb70d0c 100644 --- a/sets/de/python-basics/manifest.yaml +++ b/sets/de/python-basics/manifest.yaml @@ -1,4 +1,4 @@ -# Python — Grundlagen (für Deutschsprachige) — Set-Manifest +# Python - Grundlagen (für Deutschsprachige) - Set-Manifest # (Phase 61 / schema v1.3). # # Erstes Programmier-Set von Adaptive Learner. ``domain: @@ -14,7 +14,7 @@ # keine Rechtschreibprüfung). Der Content-Loader liest # ``metadata.lessons`` für die Dateiliste. schema_version: '1.3' -name: Python — Grundlagen +name: Python - Grundlagen description: >- Einführung in die Programmierung mit Python für Deutschsprachige (15 Lektionen): von print() und Variablen @@ -24,8 +24,8 @@ description: >- Deutsch, mit den englischen Originalbegriffen in Klammern. sets: - id: python-basics-from-de - title: Python — Grundlagen - title_native: Python — Basics + title: Python - Grundlagen + title_native: Python - Basics target_language: de source_language: de domain: programming @@ -34,7 +34,7 @@ sets: version: '1.0.0' lesson_count: 15 description: >- - Fünfzehn Einführungslektionen Python, auf Deutsch erklärt — + Fünfzehn Einführungslektionen Python, auf Deutsch erklärt, vom ersten print() bis zu List Comprehensions, mit lauffähigen Code-Beispielen und erwarteter Ausgabe. tags: diff --git a/sets/de/react-fortgeschritten/lessons/01-useeffect-verstehen.json b/sets/de/react-fortgeschritten/lessons/01-useeffect-verstehen.json index ca5b9fd..8bc78d8 100644 --- a/sets/de/react-fortgeschritten/lessons/01-useeffect-verstehen.json +++ b/sets/de/react-fortgeschritten/lessons/01-useeffect-verstehen.json @@ -1,7 +1,7 @@ { "id": "01-useeffect-verstehen", "title": "useEffect verstehen", - "description": "Fortsetzung von React-Grundlagen: der useEffect-Hook — Ausführungszeitpunkt nach dem Render, das Dependency-Array (leer, mit Deps, ohne) und die Cleanup-Funktion. Schwerpunkt Multiple-Choice.", + "description": "Fortsetzung von React-Grundlagen: der useEffect-Hook, Ausführungszeitpunkt nach dem Render, das Dependency-Array (leer, mit Deps, ohne) und die Cleanup-Funktion. Schwerpunkt Multiple-Choice.", "target_language": "de", "source_language": "de", "domain": "programming", @@ -24,7 +24,7 @@ "id": "zeitpunkt", "stable_id": "card-ms942h7o07rup", "front": "Ausführungszeitpunkt", - "back": "useEffect läuft NACH dem Render (nachdem der Bildschirm aktualisiert wurde) — nicht davor.", + "back": "useEffect läuft NACH dem Render (nachdem der Bildschirm aktualisiert wurde) - nicht davor.", "notes": "Ein häufiger Irrtum ist, useEffect laufe vor dem Render.", "media_type": "text", "difficulty": 2, @@ -51,7 +51,7 @@ "stable_id": "card-ms942h7o07tuo", "front": "Dependency-Array mit Werten", "back": "Der Effekt läuft erneut, wenn sich einer der aufgeführten Werte ändert.", - "notes": "z. B. [count] — läuft, wenn sich count ändert.", + "notes": "z. B. [count] - läuft, wenn sich count ändert.", "media_type": "text", "difficulty": 2, "tags": [ @@ -91,7 +91,7 @@ "id": "intro", "type": "theory", "title": "Wann und warum useEffect läuft", - "body": "# useEffect verstehen\n\n**useEffect** ist der Hook für **Seiteneffekte** — Dinge außerhalb des reinen Renderns wie Daten laden, Timer oder Abos.\n\nKernpunkt: Ein Effekt läuft **nach dem Render** (nachdem der Bildschirm aktualisiert wurde), **nicht davor** und nicht währenddessen. So bleibt das Rendern schnell, und der Effekt sieht das fertige Ergebnis." + "body": "# useEffect verstehen\n\n**useEffect** ist der Hook für **Seiteneffekte**, Dinge außerhalb des reinen Renderns wie Daten laden, Timer oder Abos.\n\nKernpunkt: Ein Effekt läuft **nach dem Render** (nachdem der Bildschirm aktualisiert wurde), **nicht davor** und nicht währenddessen. So bleibt das Rendern schnell, und der Effekt sieht das fertige Ergebnis." }, { "id": "step-deps", @@ -103,7 +103,7 @@ "id": "step-cleanup", "type": "theory", "title": "Die Cleanup-Funktion", - "body": "## Aufräumen mit Cleanup\n\nGibt ein Effekt eine **Funktion zurück**, ist das seine **Cleanup**-Funktion:\n\n```jsx\nuseEffect(() => {\n const id = setInterval(tick, 1000)\n return () => clearInterval(id) // Cleanup\n}, [])\n```\n\nSie läuft **vor dem nächsten** Effekt und beim **Unmount** — ideal, um Timer zu stoppen oder Abos abzumelden." + "body": "## Aufräumen mit Cleanup\n\nGibt ein Effekt eine **Funktion zurück**, ist das seine **Cleanup**-Funktion:\n\n```jsx\nuseEffect(() => {\n const id = setInterval(tick, 1000)\n return () => clearInterval(id) // Cleanup\n}, [])\n```\n\nSie läuft **vor dem nächsten** Effekt und beim **Unmount**, ideal, um Timer zu stoppen oder Abos abzumelden." }, { "id": "ex-mc-zeitpunkt", diff --git a/sets/de/react-fortgeschritten/lessons/02-datenfetching.json b/sets/de/react-fortgeschritten/lessons/02-datenfetching.json index 92a67e9..3db2b40 100644 --- a/sets/de/react-fortgeschritten/lessons/02-datenfetching.json +++ b/sets/de/react-fortgeschritten/lessons/02-datenfetching.json @@ -38,7 +38,7 @@ "stable_id": "card-ms942h7o086ne", "front": "Fehlerzustand", "back": "Schlägt der Abruf fehl, zeigt man eine Fehlermeldung statt der Daten.", - "notes": "Fehler immer behandeln — Netzwerke sind unzuverlässig.", + "notes": "Fehler immer behandeln, Netzwerke sind unzuverlässig.", "media_type": "text", "difficulty": 2, "tags": [ @@ -104,13 +104,13 @@ "id": "intro", "type": "theory", "title": "Daten laden mit useEffect", - "body": "# Datenfetching-Grundmuster\n\nIn einer Funktionskomponente startet man einen Datenabruf meist in **useEffect** und modelliert drei Zustände:\n\n- **loading** — der Abruf läuft (Spinner zeigen),\n- **error** — der Abruf schlug fehl (Meldung zeigen),\n- **success** — die Daten sind da (anzeigen).\n\nSo weiß die Oberfläche immer, was zu zeigen ist." + "body": "# Datenfetching-Grundmuster\n\nIn einer Funktionskomponente startet man einen Datenabruf meist in **useEffect** und modelliert drei Zustände:\n\n- **loading**: der Abruf läuft (Spinner zeigen),\n- **error**: der Abruf schlug fehl (Meldung zeigen),\n- **success**: die Daten sind da (anzeigen).\n\nSo weiß die Oberfläche immer, was zu zeigen ist." }, { "id": "step-race", "type": "theory", "title": "Race-Conditions vermeiden", - "body": "## Race-Conditions\n\nÄndert sich z. B. ein Suchbegriff schnell, laufen mehrere Abrufe gleichzeitig. Eine **ältere** Antwort kann eine **neuere überschreiben** — das ist eine **Race-Condition**, die veraltete Daten zeigt.\n\nGegenmittel im **Cleanup**: das Ergebnis als **veraltet markieren** (ein `ignore`-Flag) oder den Abruf mit **AbortController abbrechen**. So gewinnt immer die neueste Anfrage." + "body": "## Race-Conditions\n\nÄndert sich z. B. ein Suchbegriff schnell, laufen mehrere Abrufe gleichzeitig. Eine **ältere** Antwort kann eine **neuere überschreiben**, das ist eine **Race-Condition**, die veraltete Daten zeigt.\n\nGegenmittel im **Cleanup**: das Ergebnis als **veraltet markieren** (ein `ignore`-Flag) oder den Abruf mit **AbortController abbrechen**. So gewinnt immer die neueste Anfrage." }, { "id": "ex-mc-wo", diff --git a/sets/de/react-fortgeschritten/lessons/03-usecontext.json b/sets/de/react-fortgeschritten/lessons/03-usecontext.json index 90a8673..e632819 100644 --- a/sets/de/react-fortgeschritten/lessons/03-usecontext.json +++ b/sets/de/react-fortgeschritten/lessons/03-usecontext.json @@ -91,13 +91,13 @@ "id": "intro", "type": "theory", "title": "Context gegen Prop-Drilling", - "body": "# useContext\n\n**Prop-Drilling** heißt: Props durch viele Zwischenebenen reichen, nur um sie tief unten zu nutzen. Das wird schnell mühsam.\n\n**Context** löst das: Ein **Provider** stellt einen Wert für seinen Teilbaum bereit, und jede Komponente darunter liest ihn direkt mit **useContext** — ohne dass die Ebenen dazwischen die Prop weiterreichen müssen." + "body": "# useContext\n\n**Prop-Drilling** heißt: Props durch viele Zwischenebenen reichen, nur um sie tief unten zu nutzen. Das wird schnell mühsam.\n\n**Context** löst das: Ein **Provider** stellt einen Wert für seinen Teilbaum bereit, und jede Komponente darunter liest ihn direkt mit **useContext**, ohne dass die Ebenen dazwischen die Prop weiterreichen müssen." }, { "id": "step-wann", "type": "theory", "title": "Wann Context sinnvoll ist", - "body": "## Wann Context?\n\nContext eignet sich für **globale, selten wechselnde** Daten, die **viele** Komponenten brauchen: **Theme**, **Sprache**, der **angemeldete Nutzer**.\n\nNicht sinnvoll ist Context für **rein lokalen** State einer einzelnen Komponente — dort bleibt `useState` einfacher. Wichtig: Ändert sich der Context-Wert, **rendern alle Konsumenten neu**." + "body": "## Wann Context?\n\nContext eignet sich für **globale, selten wechselnde** Daten, die **viele** Komponenten brauchen: **Theme**, **Sprache**, der **angemeldete Nutzer**.\n\nNicht sinnvoll ist Context für **rein lokalen** State einer einzelnen Komponente, dort bleibt `useState` einfacher. Wichtig: Ändert sich der Context-Wert, **rendern alle Konsumenten neu**." }, { "id": "ex-mc-wogegen", diff --git a/sets/de/react-fortgeschritten/lessons/04-useref.json b/sets/de/react-fortgeschritten/lessons/04-useref.json index 5e2813e..a749f02 100644 --- a/sets/de/react-fortgeschritten/lessons/04-useref.json +++ b/sets/de/react-fortgeschritten/lessons/04-useref.json @@ -1,7 +1,7 @@ { "id": "04-useref", "title": "useRef", - "description": "DOM-Referenzen und veränderliche Werte ohne Re-Render — und die Abgrenzung zu State. Schwerpunkt Multiple-Choice.", + "description": "DOM-Referenzen und veränderliche Werte ohne Re-Render, und die Abgrenzung zu State. Schwerpunkt Multiple-Choice.", "target_language": "de", "source_language": "de", "domain": "programming", diff --git a/sets/de/react-fortgeschritten/lessons/05-performance.json b/sets/de/react-fortgeschritten/lessons/05-performance.json index e2bf268..99f11ce 100644 --- a/sets/de/react-fortgeschritten/lessons/05-performance.json +++ b/sets/de/react-fortgeschritten/lessons/05-performance.json @@ -91,13 +91,13 @@ "id": "intro", "type": "theory", "title": "Was Re-Renders auslöst", - "body": "# Performance\n\nEine Komponente rendert neu, wenn sich ihr **State** oder ihre **Props** ändern — oder wenn ihr **Elternteil** neu rendert. Eine Änderung von `ref.current` löst **keinen** Re-Render aus.\n\nMeist ist Re-Rendern **billig**. Optimieren lohnt sich nur gezielt: **erst messen, dann handeln** (voreilige Optimierung schadet mehr, als sie nützt)." + "body": "# Performance\n\nEine Komponente rendert neu, wenn sich ihr **State** oder ihre **Props** ändern, oder wenn ihr **Elternteil** neu rendert. Eine Änderung von `ref.current` löst **keinen** Re-Render aus.\n\nMeist ist Re-Rendern **billig**. Optimieren lohnt sich nur gezielt: **erst messen, dann handeln** (voreilige Optimierung schadet mehr, als sie nützt)." }, { "id": "step-memo", "type": "theory", "title": "useMemo, useCallback, React.memo", - "body": "## Drei Werkzeuge\n\n- **useMemo** cachet das **Ergebnis** einer **teuren Berechnung** (solange die Deps gleich sind).\n- **useCallback** cachet eine **Funktionsreferenz** (nützlich, wenn ein memoisiertes Kind eine stabile Funktion braucht).\n- **React.memo** lässt eine Komponente ihren Re-Render **überspringen**, solange ihre **Props gleich** bleiben.\n\nWichtig: `useMemo` cachet **nur zwischen Renders**, **nicht dauerhaft** über Unmounts. Und: nicht alles memoisieren — nur wo es messbar hilft." + "body": "## Drei Werkzeuge\n\n- **useMemo** cachet das **Ergebnis** einer **teuren Berechnung** (solange die Deps gleich sind).\n- **useCallback** cachet eine **Funktionsreferenz** (nützlich, wenn ein memoisiertes Kind eine stabile Funktion braucht).\n- **React.memo** lässt eine Komponente ihren Re-Render **überspringen**, solange ihre **Props gleich** bleiben.\n\nWichtig: `useMemo` cachet **nur zwischen Renders**, **nicht dauerhaft** über Unmounts. Und: nicht alles memoisieren, nur wo es messbar hilft." }, { "id": "ex-mc-ausloeser", diff --git a/sets/de/react-fortgeschritten/lessons/06-listen-keys.json b/sets/de/react-fortgeschritten/lessons/06-listen-keys.json index acdb935..ce2c9ba 100644 --- a/sets/de/react-fortgeschritten/lessons/06-listen-keys.json +++ b/sets/de/react-fortgeschritten/lessons/06-listen-keys.json @@ -91,13 +91,13 @@ "id": "intro", "type": "theory", "title": "Wozu Keys dienen", - "body": "# Listen und Keys\n\nRendert man eine Liste, gibt man jedem Element einen **Key**. Damit erkennt React ein Element **über Renders hinweg wieder** und ordnet alte und neue Elemente korrekt zu — so ändert es nur das Nötige.\n\nEin guter Key ist **stabil** und **eindeutig unter den Geschwistern** — meist eine **ID aus den Daten**, nicht der Zählindex." + "body": "# Listen und Keys\n\nRendert man eine Liste, gibt man jedem Element einen **Key**. Damit erkennt React ein Element **über Renders hinweg wieder** und ordnet alte und neue Elemente korrekt zu, so ändert es nur das Nötige.\n\nEin guter Key ist **stabil** und **eindeutig unter den Geschwistern**, meist eine **ID aus den Daten**, nicht der Zählindex." }, { "id": "step-index", "type": "theory", "title": "Warum der Index heikel ist", - "body": "## Der Index als Key\n\nDer **Array-Index** als Key wirkt bequem, ist aber heikel: Wird die Liste **umsortiert**, **eingefügt** oder **gelöscht**, verschieben sich die Indizes — und React ordnet **State falsch** zu (z. B. bleibt ein Eingabefeld beim falschen Eintrag).\n\nDer Index ist nur **vertretbar**, wenn die Liste **statisch** ist. Sonst gilt: eine **stabile ID** verwenden. Und: Keys müssen nur **unter Geschwistern** eindeutig sein, nicht global." + "body": "## Der Index als Key\n\nDer **Array-Index** als Key wirkt bequem, ist aber heikel: Wird die Liste **umsortiert**, **eingefügt** oder **gelöscht**, verschieben sich die Indizes, und React ordnet **State falsch** zu (z. B. bleibt ein Eingabefeld beim falschen Eintrag).\n\nDer Index ist nur **vertretbar**, wenn die Liste **statisch** ist. Sonst gilt: eine **stabile ID** verwenden. Und: Keys müssen nur **unter Geschwistern** eindeutig sein, nicht global." }, { "id": "ex-mc-wozu", diff --git a/sets/de/react-fortgeschritten/lessons/07-formulare.json b/sets/de/react-fortgeschritten/lessons/07-formulare.json index 7d2a067..8c8ec38 100644 --- a/sets/de/react-fortgeschritten/lessons/07-formulare.json +++ b/sets/de/react-fortgeschritten/lessons/07-formulare.json @@ -50,7 +50,7 @@ "id": "single-source", "stable_id": "card-ms942h7r0a6k6", "front": "Einzige Quelle der Wahrheit", - "back": "Bei controlled Inputs kennt der State den aktuellen Wert — genau eine Stelle, nicht das DOM zusätzlich.", + "back": "Bei controlled Inputs kennt der State den aktuellen Wert, genau eine Stelle, nicht das DOM zusätzlich.", "notes": "Erleichtert Validierung und abhängige Felder.", "media_type": "text", "difficulty": 2, @@ -97,7 +97,7 @@ "id": "step-handling", "type": "theory", "title": "Gemeinsames State-Handling", - "body": "## State-Handling\n\nBei controlled Inputs macht das Paar **`value` + `onChange`** das Feld steuerbar:\n\n```jsx\n setName(e.target.value)} />\n```\n\nSo lassen sich mehrere Felder in einem gemeinsamen State bündeln, Eingaben validieren und abhängige Felder ableiten — alles aus **einer** Quelle der Wahrheit." + "body": "## State-Handling\n\nBei controlled Inputs macht das Paar **`value` + `onChange`** das Feld steuerbar:\n\n```jsx\n setName(e.target.value)} />\n```\n\nSo lassen sich mehrere Felder in einem gemeinsamen State bündeln, Eingaben validieren und abhängige Felder ableiten, alles aus **einer** Quelle der Wahrheit." }, { "id": "ex-mc-controlled", diff --git a/sets/de/react-fortgeschritten/lessons/08-komposition-patterns.json b/sets/de/react-fortgeschritten/lessons/08-komposition-patterns.json index 039b1c8..d4f8c81 100644 --- a/sets/de/react-fortgeschritten/lessons/08-komposition-patterns.json +++ b/sets/de/react-fortgeschritten/lessons/08-komposition-patterns.json @@ -63,7 +63,7 @@ "id": "komposition", "stable_id": "card-ms942h7s0alky", "front": "Komposition", - "back": "UI aus kleinen Bausteinen zusammensetzen — Inhalte per children verschachteln statt alles zu vererben.", + "back": "UI aus kleinen Bausteinen zusammensetzen, Inhalte per children verschachteln statt alles zu vererben.", "notes": "React bevorzugt Komposition.", "media_type": "text", "difficulty": 2, @@ -104,7 +104,7 @@ "id": "intro", "type": "theory", "title": "Bausteine zusammensetzen", - "body": "# Komposition und Patterns\n\nReact setzt Oberflächen aus **kleinen Bausteinen** zusammen. Zwei Grundwerkzeuge:\n\n- **children:** die Prop, über die eine Komponente den **verschachtelten Inhalt** erhält — `Inhalt`.\n- **Lifting State Up:** brauchen **mehrere Kinder** denselben State, verschiebt man ihn in ihr **gemeinsames Elternteil** — eine **einzige Quelle der Wahrheit**." + "body": "# Komposition und Patterns\n\nReact setzt Oberflächen aus **kleinen Bausteinen** zusammen. Zwei Grundwerkzeuge:\n\n- **children:** die Prop, über die eine Komponente den **verschachtelten Inhalt** erhält, `Inhalt`.\n- **Lifting State Up:** brauchen **mehrere Kinder** denselben State, verschiebt man ihn in ihr **gemeinsames Elternteil**, eine **einzige Quelle der Wahrheit**." }, { "id": "step-hooks", diff --git a/sets/de/react-fortgeschritten/manifest.yaml b/sets/de/react-fortgeschritten/manifest.yaml index 33e5297..6aabf7d 100644 --- a/sets/de/react-fortgeschritten/manifest.yaml +++ b/sets/de/react-fortgeschritten/manifest.yaml @@ -1,4 +1,4 @@ -# Set-Manifest: React Fortgeschritten (Teil 2) — deutschsprachig. +# Set-Manifest: React Fortgeschritten (Teil 2) - deutschsprachig. # # Direkte Fortsetzung des Sets „React-Grundlagen (Einsteiger)": gleiche Reihe, # eine Niveau-Stufe höher (B1 statt A2), Sachthema (domain: programming), @@ -10,7 +10,7 @@ # # Kein picture_choice (keine Bilder). React-18-korrekt: Hooks-first, keine # Class-Lifecycle-Methoden als „richtige" Antwort. Code-Schnipsel dürfen im -# Fragetext stehen (Code LESEN und Verhalten bewerten) — keine freie +# Fragetext stehen (Code LESEN und Verhalten bewerten) - keine freie # Code-Eingabe. Der Content-Loader liest metadata.lessons für die Dateiliste. schema_version: '1.3' name: React Fortgeschritten (Teil 2) @@ -20,7 +20,7 @@ description: >- useRef, Performance (useMemo/useCallback/React.memo), Listen und Keys, Formularen (controlled vs. uncontrolled) sowie Komposition und Patterns (children, lifting state up, Custom Hooks, Error Boundaries). React-18, - Hooks-first, konzeptuell — kein freies Code-Schreiben. + Hooks-first, konzeptuell, kein freies Code-Schreiben. sets: - id: react-fortgeschritten-from-de title: React Fortgeschritten (Teil 2) @@ -34,8 +34,8 @@ sets: lesson_count: 8 description: >- Teil 2 der React-Reihe (Fortgeschritten, B1): acht Lektionen mit - überwiegend Multiple-Choice — Single-Answer als cloze select-mode und - „Wähle alle zutreffenden" als cloze multiselect — plus matching und + überwiegend Multiple-Choice, Single-Answer als cloze select-mode und + „Wähle alle zutreffenden" als cloze multiselect, plus matching und kurze Lückentext-Items. Themen: useEffect, Datenfetching, useContext, useRef, Performance, Listen/Keys, Formulare, Komposition/Patterns. Distraktoren aus klassischen React-Irrtümern. React-18, Hooks-first. diff --git a/sets/de/react-grundlagen/lessons/02-jsx.json b/sets/de/react-grundlagen/lessons/02-jsx.json index 9143277..b4519fd 100644 --- a/sets/de/react-grundlagen/lessons/02-jsx.json +++ b/sets/de/react-grundlagen/lessons/02-jsx.json @@ -79,7 +79,7 @@ "stable_id": "card-ms942h7s0bfc5", "front": "{} gegen Anführungszeichen", "back": "{ } werten einen JavaScript-Ausdruck aus; Anführungszeichen liefern einen festen Text-String.", - "notes": "title=\"Hallo\" ist fester Text, title={gruss} setzt den Wert der Variablen gruss ein.", + "notes": "title=\"Hallo\" ist fester Text, title={gruss} setzt den Wert dieser Variablen ein.", "media_type": "text", "difficulty": 2, "tags": [ diff --git a/sets/de/react-grundlagen/lessons/04-state-usestate.json b/sets/de/react-grundlagen/lessons/04-state-usestate.json index 4cf2ac5..072e46f 100644 --- a/sets/de/react-grundlagen/lessons/04-state-usestate.json +++ b/sets/de/react-grundlagen/lessons/04-state-usestate.json @@ -200,7 +200,7 @@ "id": "ex-mc-keine-mutation", "stable_id": "ex-ms942h7u0c90t", "type": "cloze", - "prompt": "Du hast const [zaehler, setZaehler] = useState(0);. Wie erhöhst du den Zähler korrekt um 1?", + "prompt": "Du hast `const [zaehler, setZaehler] = useState(0);`. Wie erhöhst du den Zähler korrekt um 1?", "card_ids": [ "card-setter", "card-keine-mutation" diff --git a/sets/de/react-grundlagen/lessons/05-events-und-grundprinzipien.json b/sets/de/react-grundlagen/lessons/05-events-und-grundprinzipien.json index 6085eb1..08f464d 100644 --- a/sets/de/react-grundlagen/lessons/05-events-und-grundprinzipien.json +++ b/sets/de/react-grundlagen/lessons/05-events-und-grundprinzipien.json @@ -270,7 +270,7 @@ } ], "cloze_mode": "type", - "hint": "Wie in useState — der Anfang des Namens." + "hint": "Wie in useState, der Anfang des Namens." } }, { diff --git a/tests/test_export_set.py b/tests/test_export_set.py index 7c7ab08..034b95a 100644 --- a/tests/test_export_set.py +++ b/tests/test_export_set.py @@ -112,7 +112,7 @@ def test_non_ascii_survives_as_real_utf8(tmp_path: Path) -> None: # substitution ("Oberfläche" appears throughout the lesson prose and # has no ASCII-substituted variant anywhere in the source). assert "Oberfläche" in raw_text - assert "Oberflaeche" not in raw_text + assert "Oberfläche".replace("ä", "ae") not in raw_text def test_yaml_reparse_content_equals_source_lessons(tmp_path: Path) -> None: