`-Elemente mit der Klasse `.form-control< /code> haben eine Breite von 100 %.
+
+--hints--
+
+Der Bestätigungsbutton in deinem Formular sollte die Klassen btn btn-primary` haben.
+
+```js
+assert($('button[type="submit"]').hasClass('btn btn-primary'));
+```
+
+Du solltest ein ` ` innerhalb deines `button`-Elements hinzufügen.
+
+```js
+assert($('button[type="submit"]:has(i.fa.fa-paper-plane)').length > 0);
+```
+
+Der Text `input` in deinem Formular sollte die Klasse `form-control` haben.
+
+```js
+assert($('input[type="text"]').hasClass('form-control'));
+```
+
+Jedes deiner `i`-Elemente sollte ein abschließendes Tag haben.
+
+```js
+assert(code.match(/<\/i>/g) && code.match(/<\/i/g).length > 3);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+
CatPhotoApp
+
+
+
+
+
+
+
+
+ Like
+
+
+ Info
+
+
+ Delete
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
+
CatPhotoApp
+
+
+
+
+
+
+
+
+ Like
+
+
+ Info
+
+
+ Delete
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/taste-the-bootstrap-button-color-rainbow.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/taste-the-bootstrap-button-color-rainbow.md
new file mode 100644
index 00000000000..56fb7f1b196
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/taste-the-bootstrap-button-color-rainbow.md
@@ -0,0 +1,167 @@
+---
+id: bad87fee1348cd8acef08811
+title: 'Bootstrap-Buttons: Regenbogenfarbe'
+challengeType: 0
+forumTopicId: 18323
+dashedName: taste-the-bootstrap-button-color-rainbow
+---
+
+# --description--
+
+Die Klasse `btn-primary` ist die Hauptfarbe, die du in deiner App verwenden solltest. Es ist nützlich, um Aktionen hervorzuheben, die der Nutzer ausführen soll.
+
+Ersetze die `btn-default`-Klasse von Bootstrap durch `btn-primary` in deinem Button.
+
+Vergiss nicht, dass dieser Button trotzdem noch die Klassen `btn` und `btn-block` benötigt.
+
+# --hints--
+
+Dein Button sollte die Klasse `btn-primary` haben.
+
+```js
+assert($('button').hasClass('btn-primary'));
+```
+
+Dein Button sollte trotzdem noch die `btn` und `btn-block` Klassen haben.
+
+```js
+assert($('button').hasClass('btn-block') && $('button').hasClass('btn'));
+```
+
+All deine `button`-Elemente sollten abschließende Tags haben.
+
+```js
+assert(
+ code.match(/<\/button>/g) &&
+ code.match(//g).length === code.match(/
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
Like
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
Like
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-a-span-to-target-inline-elements.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-a-span-to-target-inline-elements.md
new file mode 100644
index 00000000000..57f89428da0
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-a-span-to-target-inline-elements.md
@@ -0,0 +1,185 @@
+---
+id: bad87fee1348bd9aedf08845
+title: Verwende span, um Inline-Elemente zu verändern
+challengeType: 0
+forumTopicId: 18370
+dashedName: use-a-span-to-target-inline-elements
+---
+
+# --description--
+
+Du kannst span-Elemente verwenden, um Inline-Elemente zu erstellen. Erinnerst du dich daran, wie wir die Klasse `btn-block` verwendet haben, damit der Button die gesamte Zeile ausfüllt?
+
+Normaler Button
+
+„btn-block“-Button
+
+Dies verdeutlicht den Unterschied zwischen einem "Inline"-Element und einem "Block"-Element.
+
+Mit dem Inline-Element `span` kannst du mehrere Elemente in dieselbe Zeile setzen und sogar verschiedene Teile derselben Zeile unterschiedlich gestalten.
+
+Verwende ein `span` Element, um das Wort `love` in das `p`-Element zu verschachteln, das derzeit den Text `Things cats love` enthält. Gib dann `span` die Klasse `text-danger`, um den Text rot zu machen.
+
+Hier siehst du, wie du es für das `p`-Element machen würdest, das den Text `Top 3 things cats hate` enthält:
+
+```html
+Top 3 things cats hate:
+```
+
+# --hints--
+
+Dein `span`-Element sollte sich innerhalb deines `p`-Elements befinden.
+
+```js
+assert($('p span') && $('p span').length > 0);
+```
+
+Dein `span`-Element sollte den Text `love` haben.
+
+```js
+assert(
+ $('p span') &&
+ $('p span').text().match(/love/i) &&
+ !$('p span')
+ .text()
+ .match(/Things cats/i)
+);
+```
+
+Dein `span`-Element sollte die Klasse `text-danger` haben.
+
+```js
+assert($('span').hasClass('text-danger'));
+```
+
+Dein `span`-Element sollte einen abschließenden Tag haben.
+
+```js
+assert(
+ code.match(/<\/span>/g) &&
+ code.match(//g).length === code.match(/
+
+
+
+
CatPhotoApp
+
+
+
+
+
+
+ Like
+
+
+ Info
+
+
+ Delete
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
CatPhotoApp
+
+
+
+
+
+
+ Like
+
+
+ Info
+
+
+ Delete
+
+
+
Things cats love :
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-comments-to-clarify-code.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-comments-to-clarify-code.md
new file mode 100644
index 00000000000..e20ecf745e2
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-comments-to-clarify-code.md
@@ -0,0 +1,98 @@
+---
+id: bad87fee1348bd9aec908857
+title: Verwende Kommentare zur Verdeutlichung der Codes
+challengeType: 0
+forumTopicId: 18347
+dashedName: use-comments-to-clarify-code
+---
+
+# --description--
+
+Wenn wir mit der Benutzung von jQuery anfangen, werden wir HTML-Elemente modifizieren, ohne diese tatsächlich im HTML abzuändern.
+
+Stellen wir sicher, dass jeder weiß, dass er diesen Code nicht direkt ändern sollte.
+
+Denk daran, dass du einen Kommentar mit `` schließt
+
+Füge den Kommentar `Code below this line should not be changed` am Anfang deiner HTML-Datei ein
+
+# --hints--
+
+Du solltest einen Kommentar mit `.*this line))\s*.*this line.*\s*-->/gi));
+```
+
+Du solltest deinen Kommentar mit `-->` schließen.
+
+```js
+assert(code.match(/-->.*\n+.+/g));
+```
+
+Du solltest gleich viele Kommentar-Öffner und -Schließer haben.
+
+```js
+assert(code.match(//g).length);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers.md
new file mode 100644
index 00000000000..86a6a84ac76
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers.md
@@ -0,0 +1,170 @@
+---
+id: bad87fee1348bd9acde08712
+title: Verwende responsives Design mit Bootstrap Fluid Container
+challengeType: 0
+forumTopicId: 18362
+dashedName: use-responsive-design-with-bootstrap-fluid-containers
+---
+
+# --description--
+
+In der HTML5 und CSS-Sektion des freeCodeCamp haben wir eine Katzen-Foto-App gebaut. Lasst uns jetzt dazu zurückkehren. Dieses Mal werden wir sie mit dem beliebten Bootstrap-responsive CSS-Framework gestalten.
+
+Bootstrap ermittelt, wie breit dein Bildschirm ist und reagiert darauf, indem es die Größe deiner HTML-Elemente anpasst - daher der Name responsive design .
+
+Mit responsivem Design ist es nicht nötig, eine mobile Version deiner Website zu entwerfen. Sie sieht auf Geräten mit beliebig breiten Bildschirmen gut aus.
+
+Du kannst Bootstrap zu jeder App hinzufügen, indem du folgenden Code oben in deinem HTML-Code einfügst:
+
+```html
+
+```
+
+In diesem Fall haben wir es bereits auf dieser Seite für dich eingefügt. Beachte, dass die Verwendung von `>` oder `/>` zum Schließen des `link`-Tags möglich ist.
+
+Um zu beginnen, sollten wir unseren HTML-Code (ausgenommen der `link`-Tag und das`style`-Element) in einem `div`-Element mit der Klasse `container-fluid` verschachteln.
+
+# --hints--
+
+Dein `div`-Element sollte die Klasse `container-fluid` haben.
+
+```js
+assert($('div').hasClass('container-fluid'));
+```
+
+Dein `div`-Element sollte einen abschließenden Tag haben.
+
+```js
+assert(
+ code.match(/<\/div>/g) &&
+ code.match(//g).length === code.match(/
= 8 && !$('.container-fluid').has("style").length && !$('.container-fluid').has("link").length);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-the-bootstrap-grid-to-put-elements-side-by-side.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-the-bootstrap-grid-to-put-elements-side-by-side.md
new file mode 100644
index 00000000000..52f2c14443a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/use-the-bootstrap-grid-to-put-elements-side-by-side.md
@@ -0,0 +1,196 @@
+---
+id: bad88fee1348ce8acef08815
+title: Verwende das Bootstrap-Raster, um Elemente nebeneinander zu platzieren
+challengeType: 0
+forumTopicId: 18371
+dashedName: use-the-bootstrap-grid-to-put-elements-side-by-side
+---
+
+# --description--
+
+Bootstrap verwendet ein responsives 12-Spalten-Rastersystem, das es einfach macht, Elemente in Zeilen zu platzieren und die relative Breite jedes Elements anzugeben. Die meisten Bootstrap-Klassen können auf ein `div`-Element angewendet werden.
+
+Bootstrap hat verschiedene Spaltenbreiten-Attribute, die je nach Breite des Bildschirms des Nutzers verwendet werden. Beispielsweise haben Telefone schmale und Laptops breitere Bildschirme.
+
+Zum Beispiel die Bootstrap-Klasse `col-md-*`. In diesem Fall bedeutet `md` mittel und `*` ist die Anzahl der Spalten, die die Breite des Elements angibt. In diesem Fall wird die Spaltenbreite eines Elements auf einem mittelgroßen Bildschirm, z. B. einem Laptop, angegeben.
+
+In der Katzenfoto-App, die wir erstellen, verwenden wir `col-xs-*`, wobei `xs` extra klein bedeutet (wie ein extra kleiner Handybildschirm), und `*` ist die Anzahl der Spalten, die angibt wie viele Spalten das Element breit sein soll.
+
+Platziere die `Like`, `Info` und `Delete`-Buttons nebeneinander, indem du alle drei in einem `
`-Element und anschließend jedes innerhalb eines `
`-Elements verschachtelst.
+
+Die `row`-Klasse wird auf ein `div` angewendet und die Buttons selbst können darin verschachtelt werden.
+
+# --hints--
+
+Deine Buttons sollten alle innerhalb desselben `div`-Elements mit der Klasse `row` verschachtelt sein.
+
+```js
+assert($('div.row:has(button)').length > 0);
+```
+
+Jeder d Beiner ootstrap-Buttons sollte in einem eigenen `div`-Element mit der Klasse `col-xs-4` verschachtelt sein.
+
+```js
+assert($('div.col-xs-4:has(button)').length > 2);
+```
+
+Jedes deiner `button`-Elemente sollte ein abschließendes Tag haben.
+
+```js
+assert(
+ code.match(/<\/button>/g) &&
+ code.match(/
/g).length === code.match(//g) &&
+ code.match(//g).length === code.match(/
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
Like
+
Info
+
Delete
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
+
+ Like
+
+
+ Info
+
+
+ Delete
+
+
+
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/warn-your-users-of-a-dangerous-action-with-btn-danger.md b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/warn-your-users-of-a-dangerous-action-with-btn-danger.md
new file mode 100644
index 00000000000..eb6cca1740e
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/bootstrap/warn-your-users-of-a-dangerous-action-with-btn-danger.md
@@ -0,0 +1,176 @@
+---
+id: bad87fee1348ce8acef08814
+title: Warne deine Nutzer mit btn-danger vor einer schädigenden Aktion
+challengeType: 0
+forumTopicId: 18375
+dashedName: warn-your-users-of-a-dangerous-action-with-btn-danger
+---
+
+# --description--
+
+Bootstrap enthält mehrere vordefinierte Farben für Buttons. Die `btn-danger`-Klasse ist die Farbe des Buttons, die du verwenden kannst, um den Nutzern mitzuteilen, dass der Button eine schädigende Aktion ausführt, wie zum Beispiel das Löschen eines Katzenfotos.
+
+Erstelle einen Button mit dem Text `Delete` und gib ihm die Klasse `btn-danger`.
+
+Beachte, dass diese Buttons immer noch die Klassen `btn` und `btn-block` benötigen.
+
+# --hints--
+
+Du solltest ein neues `button`-Element mit dem Text `Delete` erstellen.
+
+```js
+assert(new RegExp('Delete', 'gi').test($('button').text()));
+```
+
+All deine Bootstrap-Buttons sollten die Klassen `btn` und `btn-block` haben.
+
+```js
+assert($('button.btn-block.btn').length > 2);
+```
+
+Dein neuer Button sollte die Klasse `btn-danger` haben.
+
+```js
+assert($('button').hasClass('btn-danger'));
+```
+
+All deine `button`-Elemente sollten abschließende Tags haben.
+
+```js
+assert(
+ code.match(/<\/button>/g) &&
+ code.match(/
/g).length === code.match(/
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
Like
+
Info
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
CatPhotoApp
+
+
Click here for cat photos .
+
+
+
+
+
Like
+
Info
+
Delete
+
Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-25-5-clock.md b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-25-5-clock.md
new file mode 100644
index 00000000000..77fd324ad6d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-25-5-clock.md
@@ -0,0 +1,81 @@
+---
+id: bd7158d8c442eddfaeb5bd0f
+title: Erstelle eine 25 + 5 Uhr
+challengeType: 3
+forumTopicId: 301373
+dashedName: build-a-25--5-clock
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/XpKrrW aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst jede Mischung aus HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux und jQuery verwenden, um dieses Projekt abzuschließen. Du solltest ein Frontend-Framework (wie zum Beispiel React) verwenden, da es in diesem Abschnitt um das Lernen von Frontend Frameworks geht. Zusätzliche Technologien, die oben nicht aufgeführt sind, werden nicht empfohlen und die Verwendung erfolgt auf eigene Gefahr. Wir prüfen die Unterstützung anderer Frontend-Frameworks wie Angular und Vue, aber sie werden derzeit nicht unterstützt. Wir werden alle Fehlerberichte akzeptieren und versuchen, sie zu beheben, die den vorgeschlagenen Technologie-Stack für dieses Projekt verwenden. Viel Spaß beim Programmieren!
+
+**User Story #1:** Ich kann ein Element mit `id="break-label"` sehen, das einen String enthält (z. B. "Pausenlänge").
+
+**User Story #2:** Ich kann ein Element mit `id="session-label"` sehen, das einen String enthält (z. B. "Sitzungslänge").
+
+**User Story #3:** Ich kann zwei anklickbare Elemente mit entsprechenden IDs sehen: `id="break-decrement"` und `id="session-decrement"`.
+
+**User Story #4:** Ich sehe zwei anklickbare Elemente mit entsprechenden IDs: `id="break-increment"` und `id="session-increment"`.
+
+**User Story #5:** Ich kann ein Element mit einer entsprechenden `id="break-length"` sehen, das standardmäßig (beim Laden) einen Wert von 5 anzeigt.
+
+**User Story #6:** Ich kann ein Element mit einer entsprechenden `id="session-length"` sehen, das standardmäßig einen Wert von 25 anzeigt.
+
+**User Story #7:** Ich kann ein Element mit einem entsprechenden `id="timer-label"` sehen, das einen String enthält, der anzeigt, dass eine Sitzung begonnen wurde (z. B. "Sitzung").
+
+**User Story #8:** Ich kann ein Element mit dem entsprechenden `id="time-left"` sehen. HINWEIS: Ob angehalten oder ausgeführt, der Wert in diesem Feld sollte immer im Format `mm:ss` angezeigt werden (z. B. 25:00).
+
+**User Story #9:** Ich kann ein anklickbares Element mit einem entsprechenden `id="start_stop"` sehen.
+
+**User Story Nr. 10:** Ich sehe ein anklickbares Element mit einem entsprechenden `id="reset"`.
+
+**User Story #11:** Wenn ich auf das Element mit der ID `reset` klicke, sollte jeder laufende Timer gestoppt werden, der Wert innerhalb von ` id="break-length"` sollte auf `5` zurück gestellt werden, der Wert in `id="session-length"` sollte auf 25 zurück gestellt werden und das Element mit < code>id="time-left" sollte auf den Standardwert zurückgesetzt werden.
+
+**User Story #12:** Wenn ich auf das Element mit der ID `break-decrement` klicke, wird der Wert innerhalb von `id="break- length"` um einen Wert von 1 verringert und ich kann den aktualisierten Wert sehen.
+
+**User Story #13:** Wenn ich auf das Element mit der ID `break-increment` klicke, wird der Wert innerhalb von `id="break- length"` um den Wert 1 erhöht, und ich kann den aktualisierten Wert sehen.
+
+**User Story #14:** Wenn ich auf das Element mit der ID von `session-decrement` klicke, wird der Wert in `id="session- length"` um einen Wert von 1 verringert, und ich kann den aktualisierten Wert sehen.
+
+**User Story #15:** Wenn ich auf das Element mit der ID von `session-increment` klicke, wird der Wert in `id="session- length"` um den Wert 1 erhöht, und ich kann den aktualisierten Wert sehen.
+
+**User Story #16:** Ich sollte nicht in der Lage sein, eine Sitzungs- oder Pausenlänge auf <= 0 zu setzen.
+
+**User Story #17:** Ich sollte nicht in der Lage sein, eine Sitzungs- oder Pausenlänge auf > 60 zu setzen.
+
+**User Story #18:** Wenn ich zum ersten Mal auf das Element mit `id="start_stop"` klicke, sollte der Timer mit dem aktuell angezeigten Wert beginnen `id="session-length"`, auch wenn der Wert vom ursprünglichen Wert 25 erhöht oder verringert wurde.
+
+**User Story #19:** Wenn der Timer läuft, sollte das Element mit der ID `time-left` die verbleibende Zeit im `mm:ss`-Format anzeigen ( Verminderung um einen Wert von 1 und Aktualisierung der Anzeige alle 1000ms).
+
+**User Story #20:** Wenn der Timer läuft und ich auf das Element mit `id="start_stop"` klicke, sollte der Countdown pausieren.
+
+**User Story #21:** Wenn der Timer pausiert ist und ich auf das Element mit `id="start_stop"` klicke, sollte der Countdown ab dem Punkt weiterlaufen, an dem es pausiert wurde.
+
+**User Story #22:** Wenn ein Sitzungs-Countdown Null erreicht (HINWEIS: Der Timer MUSS 00:00 erreichen) und ein neuer Countdown beginnt, sollte das Element mit der ID `timer-label` einen String anzeigen, der angibt, dass eine Pause begonnen hat.
+
+**User Story #23:** Wenn ein Sitzungs-Countdown Null erreicht (HINWEIS: Der Timer MUSS 00:00 erreichen), sollte ein neuer Pausen-Countdown beginnen, der von dem `id="break-length"`-Element angezeigten Wert herunterzählt.
+
+**User Story #24:** Wenn ein Pausen-Countdown Null erreicht (HINWEIS: Der Timer MUSS 00:00 erreichen) und ein neuer Countdown beginnt, sollte das Element mit der ID `timer-label` einen String anzeigen, der anzeigt, dass eine Sitzung begonnen hat.
+
+**User Story #25:** Wenn ein Pausen-Countdown Null erreicht (HINWEIS: Der Timer MUSS 00:00 erreichen), sollte ein neuer Sitzungs-Countdown beginnen, der von dem aktuell angezeigten Wert im `id="session-length"`-Element herunterzählt.
+
+**User Story #26:** Wenn ein Countdown Null erreicht (HINWEIS: Der Timer MUSS 00:00 erreichen), sollte ein Ton abgespielt werden, dass die Zeit abgelaufen ist. Dieser sollte ein HTML5-`audio`-Tag verwenden und einen entsprechenden `id="beep"` haben.
+
+**User Story #27:** Das Audioelement mit `id="beep"` sollte 1 Sekunde oder länger sein.
+
+**User Story #28:** Das Audioelement mit der ID `beep` sollte aufhören zu ertönen und zum Anfang zurückgespult werden, wenn das Element mit der ID `reset` angeklickt wird.
+
+Du kannst dein Projekt erstellen, indem du diese CodePen Vorlage verwendest und `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-drum-machine.md b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-drum-machine.md
new file mode 100644
index 00000000000..2d4a50f53ee
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-drum-machine.md
@@ -0,0 +1,39 @@
+---
+id: 587d7dbc367417b2b2512bae
+title: Baue eine Dum Machine
+challengeType: 3
+forumTopicId: 301370
+dashedName: build-a-drum-machine
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/MJyNMd aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst eine beliebige Mischung aus HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux und jQuery verwenden, um dieses Projekt fertigzustellen. Du solltest ein Frontend Framework (wie zum Beispiel React) verwenden, da es in diesem Abschnitt um das Lernen von Frontend Frameworks geht. Zusätzliche Technologien, die oben nicht aufgeführt sind, werden nicht empfohlen und ihre Verwendung erfolgt auf eigene Gefahr. Wir prüfen die Unterstützung anderer Frontend-Frameworks wie Angular und Vue, aber sie werden derzeit nicht unterstützt. Wir werden alle Fehlerberichte akzeptieren und versuchen, sie zu beheben, die den vorgeschlagenen Technologie-Stack für dieses Projekt verwenden. Viel Spaß beim Programmieren!
+
+**User Story #1:** Ich sollte einen äußeren Container mit einer zugehörigen `id="drum-machine"` sehen, der alle anderen Elemente enthält.
+
+**User Story #2:** Innerhalb `#drum-machine` kann ich ein Element mit der entsprechenden `id="display"` sehen.
+
+**User Story #3:** Innerhalb von `#drum-machine` sehe ich 9 klickbare Drum-Pad-Elemente, jedes mit einem Klassennamen `drum-pad`, einer eindeutigen ID, die den Audioclip beschreibt, den das Drum-Pad auslösen soll, und einem inneren Text, der einer der folgenden Tasten auf der Tastatur entspricht: `Q`, `W`, `E`, `A`, `S`, `D`, `Z`, `X`, `C`. Die Drum Pads MÜSSEN in dieser Reihenfolge angeordnet sein.
+
+**User Story #4:** Innerhalb von jedem `.drum-pad`, sollte es ein HTML5 `audio`-Element geben, das ein `src`-Attribute hat, das auf einen Audio Clip hinweist, mit dem Klassenname `clip`, und einer ID, die dem inneren Text seines übergeordneten Elements `.drum-pad` entspricht (e.g. `id="Q"`, `id="W"`, `id="E"` etc.).
+
+**User Story#5:** Wenn ich auf ein `.drum-pad`-Element klicke, sollte der Audioclip, der in seinem untergeordneten `audio`-Element enthalten ist, ausgelöst werden.
+
+**User Story #6:** Wenn ich die Trigger-Taste drücke, die mit jedem `.drum-pad` verbunden ist, sollte der Audio-Clip, der in seinem untergeordneten `audio`-Element enthalten ist, ausgelöst werden (z.B. sollte das Drücken der `Q`-Taste das Drum-Pad auslösen, das den String `Q` enthält, das Drücken der `W`-Taste sollte das Drum-Pad auslösen, das den String `W` enthält, usw.).
+
+**User Story #7:** Wenn ein `.drum-pad` ausgelöst wird, wird ein String, der den zugehörigen Audioclip beschreibt, als innerer Text des `#display`-Elements angezeigt (jede Zeichenfolge muss eindeutig sein).
+
+Du kannst dein Projekt erstellen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-javascript-calculator.md b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-javascript-calculator.md
new file mode 100644
index 00000000000..27ee60981e5
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-javascript-calculator.md
@@ -0,0 +1,62 @@
+---
+id: bd7158d8c442eddfaeb5bd17
+title: Erstelle einen JavaScript-Rechner
+challengeType: 3
+forumTopicId: 301371
+dashedName: build-a-javascript-calculator
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/wgGVVX aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst jede Mischung aus HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux und jQuery verwenden, um dieses Projekt abzuschließen. Du solltest ein Frontend-Framework (wie zum Beispiel React) verwenden, da es in diesem Abschnitt um das Lernen von Frontend Frameworks geht. Zusätzliche Technologien, die oben nicht aufgeführt sind, werden nicht empfohlen und die Verwendung erfolgt auf eigene Gefahr. Wir prüfen die Unterstützung anderer Frontend-Frameworks wie Angular und Vue, aber sie werden derzeit nicht unterstützt. Wir sind offen für Fehlermeldungen und kümmern uns um all jene, die die vorgeschlagenen Technologien für dieses Projekt verwenden. Viel Spaß beim Programmieren!
+
+**User Story #1:** Mein Rechner sollte ein anklickbares Element enthalten, das ein `=` (Gleichheitszeichen) mit einem entsprechenden `id="equals"` enthält.
+
+**User Story #2:** Mein Rechner sollte 10 anklickbare Elemente enthalten, die jeweils eine Zahl von 0-9 enthalten, mit den folgenden IDs: `id="zero"`, `id="one"`, `id="two"`, `id="three"`, `id="four"`, `id="five"`, `id="six"`, `id="seven"`, `id="eight"`, und `id="nine"`.
+
+**User Story #3:** Mein Rechner sollte 4 anklickbare Elemente enthalten, die jeweils einen der 4 primären mathematischen Operatoren mit den folgenden IDs enthalten: `id="add"`, `id="subtract"`, `id="multiply"`, `id="divide"`.
+
+**User Story #4:** Mein Rechner sollte ein anklickbares Element enthalten, das ein `.` (Dezimalpunkt) Symbol mit einem entsprechenden `id="decimal"` enthält.
+
+**User Story #5:** Mein Rechner sollte ein anklickbares Element mit einer `id="clear"` enthalten.
+
+**User Story #6:** Mein Rechner sollte ein Element zur Anzeige von Werten mit einer entsprechenden `id="display"` enthalten.
+
+**User Story #7:** Durch Drücken des `clear`-Buttons können jederzeit die Eingabe- und Ausgabewerte gelöscht werden und der Rechner wird in den Ausgangszustand zurückgesetzt; im Element mit der ID `display` sollte 0 angezeigt werden.
+
+**User Story #8:** Wenn ich Zahlen eingebe, sollte ich in der Lage sein, meine Eingabe in dem Element mit der ID `display` zu sehen.
+
+**User Story #9:** Ich sollte in der Lage sein, eine Zahlenkette beliebiger Länge in beliebiger Reihenfolge zu addieren, subtrahieren, multiplizieren und dividieren, und wenn ich auf `=` klicke, sollte das korrekte Ergebnis in dem Element mit der ID `display` angezeigt werden.
+
+**User Story #10:** Bei der Eingabe von Zahlen sollte mein Rechner nicht zulassen, dass eine Zahl mit mehreren Nullen beginnt.
+
+**User Story #11:** Wenn das Dezimalelement angeklickt wird, sollte ein `.` an den aktuell angezeigten Wert angehängt werden; zwei `.` in einer Zahl sollten nicht akzeptiert werden.
+
+**User Story #12:** Ich sollte in der Lage sein, jede beliebige Operation (`+`, `-`, `*`, `/`) mit Zahlen durchzuführen, die Dezimalstellen enthalten.
+
+**User Story #13:** Wenn 2 oder mehr Operatoren nacheinander eingegeben werden, wird die Operation mit dem zuletzt eingegebenen Operator ausgeführt (das Negative ausgeschlossen (`-`) Zeichen). Wird zum Beispiel `5 + * 7 =` eingegeben, sollte das Ergebnis `35` (i.e. `5 * 7`) sein; wird `5 * - 5 =` eingegeben, sollte das Ergebnis `-25` sein (i.e. <`5 * (-5)`).
+
+**User Story #14:** Wenn du einen Operator unmittelbar nach `=` drückst, wird eine neue Berechnung gestartet, die auf dem Ergebnis der vorherigen Auswertung basiert.
+
+**User Story #15:** Mein Rechner sollte mehrere Dezimalstellen genau sein, wenn es ums Runden geht (Beachte, dass es keinen exakten Standard gibt, aber du solltest in der Lage sein, Berechnungen wie `2 / 7` mit angemessener Genauigkeit auf mindestens 4 Dezimalstellen durchzuführen).
+
+**Anmerkung zur Logik des Rechners:** Es ist anzumerken, dass es zwei wesentliche Denkansätze zur Logik der Rechnereingabe gibt: immediate execution logic und formula logic . Unser Beispiel verwendet Formellogik und beachtet die Rangfolge der Operationen, die unmittelbare Ausführung jedoch nicht. Beides ist möglich, aber beachte bitte, dass dein Rechner je nach Wahl für bestimmte Gleichungen andere Ergebnisse liefern kann als unser Rechner (siehe Beispiel unten). Solange deine Berechnungen mit einem anderen Produktionsrechner überprüft werden können, betrachte dies bitte nicht als Fehler.
+
+**BEISPIEL:** `3 + 5 x 6 - 2 / 4 =`
+
+- **Unmittelbare Ausführungslogik:** `11.5`
+- **Formel-/Ausdrucklogik:** `32.5`
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-markdown-previewer.md b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-markdown-previewer.md
new file mode 100644
index 00000000000..214b5dcf184
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-markdown-previewer.md
@@ -0,0 +1,39 @@
+---
+id: bd7157d8c242eddfaeb5bd13
+title: Erstelle einen Markdown Previewer
+challengeType: 3
+forumTopicId: 301372
+dashedName: build-a-markdown-previewer
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/GrZVVO aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst jede Mischung aus HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux und jQuery verwenden, um dieses Projekt abzuschließen. Du solltest ein Frontend-Framework (wie zum Beispiel React) verwenden, da es in diesem Abschnitt um das Lernen von Frontend Frameworks geht. Zusätzliche Technologien, die oben nicht aufgeführt sind, werden nicht empfohlen und die Verwendung erfolgt auf eigene Gefahr. Wir prüfen die Unterstützung anderer Frontend-Frameworks wie Angular und Vue, aber sie werden derzeit nicht unterstützt. Wir werden alle Fehlerberichte akzeptieren und versuchen, sie zu beheben, die den vorgeschlagenen Technologie-Stack für dieses Projekt verwenden. Viel Spaß beim Programmieren!
+
+**User Story #1:** Ich kann ein `textarea`-Element mit einem entsprechenden `id="editor"` sehen.
+
+**User Story #2:** Ich kann ein Element mit einem entsprechenden `id="preview"` sehen.
+
+**User Story #3:** Wenn ich Text in das `#editor`-Element eingebe, wird das `#preview`-Element während der Eingabe aktualisiert, um den Inhalt des Textfeldes anzuzeigen.
+
+**User Story #4:** Wenn ich GitHub-Markdown in das `#editor`-Element eingebe, wird der Text als HTML im `#preview`-Element wiedergegeben, während ich tippe (TIPP: Du brauchst Markdown nicht selbst zu parsen - du kannst die Marked-Bibliothek dafür importieren: ).
+
+**User Story #5:** Wenn mein Markdown-Previewer zum ersten Mal geladen wird, sollte der Standardtext im `#editor`-Feld gültiges Markdown enthalten, das mindestens eines der folgenden Elemente darstellt: ein Überschriftenelement (H1-Größe), ein Unterüberschriftenelement (H2-Größe), einen Link, Inline-Code, einen Codeblock, ein Listenelement, ein Blockquote, ein Bild und fettgedruckten Text.
+
+**User Story #6:** Wenn mein Markdown-Previewer zum ersten Mal geladen wird, sollte das Standard-Markdown im `#editor`-Feld als HTML im `#preview`-Element dargestellt werden.
+
+**Optionaler Bonus (du musst diesen Test nicht bestehen):** Mein Markdown-Previewer interpretiert Zeilenumbrüche und gibt sie als `br` (Zeilenumbruch) Elemente aus.
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-random-quote-machine.md b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-random-quote-machine.md
new file mode 100644
index 00000000000..27b8b0ff2da
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/front-end-development-libraries-projects/build-a-random-quote-machine.md
@@ -0,0 +1,49 @@
+---
+id: bd7158d8c442eddfaeb5bd13
+title: Erstelle eine Zufalls-Zitat-Maschine
+challengeType: 3
+forumTopicId: 301374
+dashedName: build-a-random-quote-machine
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/qRZeGZ aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst jede Mischung aus HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux und jQuery verwenden, um dieses Projekt abzuschließen. Du solltest ein Frontend-Framework (wie zum Beispiel React) verwenden, da es in diesem Abschnitt um das Lernen von Frontend Frameworks geht. Zusätzliche Technologien, die oben nicht aufgeführt sind, werden nicht empfohlen und die Verwendung erfolgt auf eigene Gefahr. Wir prüfen die Unterstützung anderer Frontend-Frameworks wie Angular und Vue, aber sie werden derzeit nicht unterstützt. Wir werden alle Fehlerberichte akzeptieren und versuchen, sie zu beheben, die den vorgeschlagenen Technologie-Stack für dieses Projekt verwenden. Viel Spaß beim Programmieren!
+
+**User Story #1:** Ich kann ein Wrapper-Element mit einer entsprechenden `id="quote-box"` sehen.
+
+**User Story #2:** Innerhalb `#quote-box` sehe ich ein Element mit einer entsprechenden `id="text"`.
+
+**User Story #3:** Innerhalb `#quote-box` sehe ich ein Element mit einer entsprechenden `id="author"`.
+
+**User Story #4:** Innerhalb von `#quote-box` sehe ich ein klickbares Element mit einer entsprechenden `id="new-quote"`.
+
+**User Story #5:** Innerhalb der `#quote-box` sehe ich ein klickbares `a`-Element mit einer entsprechenden `id="tweet-quote"`.
+
+**User Story #6:** Beim ersten Laden zeigt meine Zitatmaschine ein zufälliges Zitat in dem Element mit `id="text"` an.
+
+**User Story #7:** Beim ersten Laden zeigt meine Zitatmaschine den Autor des zufälligen Zitats in dem Element mit `id="author"` an.
+
+**User Story #8:** Wenn der `#new-quote` Button angeklickt wird, soll meine Zitatmaschine ein neues Zitat holen und es im `#text`-Element anzeigen.
+
+**User Story #9:** Meine Zitatmaschine soll den Autor des neuen Zitats holen, wenn der Button `#new-quote` angeklickt wird, und ihn im Element `#author` anzeigen.
+
+**User Story #10:** Ich kann das aktuelle Zitat tweeten, indem ich auf das `#tweet-quote` `a`-Element klicke. Dieses `a`-Element sollte den Pfad `"twitter.com/intent/tweet"` in seinem `href`-Attribut enthalten, um das aktuelle Zitat zu twittern.
+
+**User Story #11:** Das `#quote-box`-Wrapper-Element sollte horizontal zentriert sein. Bitte führe die Tests mit einer Zoomstufe von 100% und maximierter Seite durch.
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in einer beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, sende die URL an dein Arbeitsprojekt mit all seinen Tests.
+
+**Hinweis:** Twitter erlaubt es nicht, Links in einem iframe zu laden. Versuche es mit dem `target="_blank"` oder `target="_top"`-Attribut im `#tweet-quote`-Element, wenn dein Tweet nicht geladen werden kann. `target="_top"` ersetzt den aktuellen Tab, also stelle sicher, dass deine Arbeit gespeichert ist.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-text-inside-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-text-inside-an-element-using-jquery.md
new file mode 100644
index 00000000000..68d04b3d37d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-text-inside-an-element-using-jquery.md
@@ -0,0 +1,131 @@
+---
+id: 564944c91be2204b269d51e3
+title: Text innerhalb eines Elements mit jQuery ändern
+challengeType: 6
+forumTopicId: 16773
+dashedName: change-text-inside-an-element-using-jquery
+---
+
+# --description--
+
+Mit jQuery kannst du den Text zwischen den Start- und End-Tags eines Elements ändern. Du kannst sogar das HTML-Markup ändern.
+
+jQuery hat eine Funktion namens `.html()`, mit der du HTML-Tags und Text innerhalb eines Elements hinzufügen kannst. Alle Inhalte, die sich zuvor in dem Element befanden, werden vollständig durch die Inhalte ersetzt, die du mit dieser Funktion bereitstellst.
+
+So würdest du den Text unserer Überschrift umformulieren und hervorheben:
+
+```js
+$("h3").html("jQuery Playground ");
+```
+
+jQuery hat auch eine ähnliche Funktion namens `.text()`, die nur den Text ändert, ohne Tags hinzuzufügen. Das heißt, diese Funktion wertet keine übergebenen HTML-Tags aus, sondern behandelt sie als den Text, durch den du den vorhandenen Inhalt ersetzen willst.
+
+Ändere den Button mit der id `target4`, indem du seinen Text hervorhebst.
+
+Sieh dir unseren News-Artikel zu <em> an, um den Unterschied zwischen `` und `` und deren Verwendung zu erfahren.
+
+Beachte, dass der ``-Tag zwar traditionell zur Hervorhebung von Text verwendet wurde, aber inzwischen auch als Tag für Icons eingesetzt wird. Der ``-Tag ist heute allgemein als Tag für die Hervorhebung akzeptiert. Für diese Aufgabe ist beides geeignet.
+
+# --hints--
+
+Du solltest den Text in deinem Button `target4` hervorheben, indem du HTML-Tags hinzufügst.
+
+```js
+assert.isTrue(
+ /|\s*#target4\s*<\/em>|<\/i>/gi.test($('#target4').html())
+);
+```
+
+Der Text sollte ansonsten unverändert bleiben.
+
+```js
+assert($('#target4') && $('#target4').text().trim() === '#target4');
+```
+
+Du solltest keinen anderen Text ändern.
+
+```js
+assert.isFalse(/|/gi.test($('h3').html()));
+```
+
+Du solltest `.html()` und nicht `.text()` verwenden.
+
+```js
+assert(code.match(/\.html\(/g));
+```
+
+Du solltest `button id="target4"` mit jQuery auswählen.
+
+```js
+assert(code.match(/\$\(\s*?(\"|\')#target4(\"|\')\s*?\)\.html\(/));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-the-css-of-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-the-css-of-an-element-using-jquery.md
new file mode 100644
index 00000000000..db000a4db07
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/change-the-css-of-an-element-using-jquery.md
@@ -0,0 +1,119 @@
+---
+id: bad87fee1348bd9aed908826
+title: Ändere das CSS eines Elements mit jQuery
+challengeType: 6
+forumTopicId: 16776
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: change-the-css-of-an-element-using-jquery
+---
+
+# --description--
+
+Wir können auch das CSS eines HTML-Elements direkt mit jQuery ändern.
+
+jQuery hat eine Funktion namens `.css()`, mit der du das CSS eines Elements ändern kannst.
+
+So ändern wir die Farbe in Blau:
+
+```js
+$("#target1").css("color", "blue");
+```
+
+Dies unterscheidet sich etwas von einer normalen CSS-Deklaration, da die CSS-Eigenschaft und ihr Wert in Anführungszeichen stehen und durch ein Komma statt eines Doppelpunkts getrennt sind.
+
+Lösche deine jQuery-Selektoren und hinterlasse eine leere `document ready function`.
+
+Wähle `target1` und ändere seine Farbe in rot.
+
+# --hints--
+
+Dein Element `target1` sollte roten Text beinhalten.
+
+```js
+assert($('#target1').css('color') === 'rgb(255, 0, 0)');
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/clone-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/clone-an-element-using-jquery.md
new file mode 100644
index 00000000000..33b35cffa18
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/clone-an-element-using-jquery.md
@@ -0,0 +1,121 @@
+---
+id: bad87fee1348bd9aed508826
+title: Ein Element mit jQuery klonen
+challengeType: 6
+forumTopicId: 16780
+dashedName: clone-an-element-using-jquery
+---
+
+# --description--
+
+Du kannst Elemente nicht nur verschieben, sondern auch von einem Ort zum anderen kopieren.
+
+jQuery hat eine Funktion namens `clone()`, die eine Kopie eines Elements erstellt.
+
+Wenn wir zum Beispiel `target2` von unserem `left-well` zu unserem `right-well` kopieren wollen, würden wir Folgendes verwenden:
+
+```js
+$("#target2").clone().appendTo("#right-well");
+```
+
+Hast du bemerkt, dass dazu zwei jQuery-Funktionen miteinander verbunden werden müssen? Das nennt man Funktionsverkettung (function chaining) und es ist ein bequemer Weg, um Dinge mit jQuery zu erledigen.
+
+Klone dein Element `target5` und füge es in dein `left-well` ein.
+
+# --hints--
+
+Dein Element `target5` sollte sich innerhalb deines `right-well` befinden.
+
+```js
+assert($('#right-well').children('#target5').length > 0);
+```
+
+Eine Kopie deines Elements `target5` sollte auch in deinem `left-well` vorhanden sein.
+
+```js
+assert($('#left-well').children('#target5').length > 0);
+```
+
+Du solltest nur jQuery verwenden, um diese Elemente zu verschieben.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/delete-your-jquery-functions.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/delete-your-jquery-functions.md
new file mode 100644
index 00000000000..fb2af05ba2b
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/delete-your-jquery-functions.md
@@ -0,0 +1,125 @@
+---
+id: bad87fee1348bd9aeda08726
+title: Deine jQuery-Funktionen löschen
+challengeType: 6
+forumTopicId: 17561
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: delete-your-jquery-functions
+---
+
+# --description--
+
+Am Anfang waren diese Animationen cool, aber jetzt lenken sie irgendwie ab.
+
+Lösche alle drei dieser jQuery-Funktionen aus deiner `document ready function`, aber lass deine `document ready function` selbst intakt.
+
+# --hints--
+
+Alle drei jQuery-Funktionen sollten aus deiner `document ready function` gelöscht werden.
+
+```js
+assert(code.match(/\{\s*\}\);/g));
+```
+
+Du solltest dein `script`-Element intakt lassen.
+
+```js
+assert(code.match(/
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/disable-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/disable-an-element-using-jquery.md
new file mode 100644
index 00000000000..e87f00302e8
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/disable-an-element-using-jquery.md
@@ -0,0 +1,120 @@
+---
+id: bad87fee1348bd9aed808826
+title: Deaktiviere ein Element mit jQuery
+challengeType: 6
+forumTopicId: 17563
+dashedName: disable-an-element-using-jquery
+---
+
+# --description--
+
+Du kannst auch die Nicht-CSS-Eigenschaften von HTML-Elementen mit jQuery ändern. Du kannst zum Beispiel Buttons deaktivieren.
+
+Wenn du einen Button deaktivierst, wird er ausgegraut und kann nicht mehr angeklickt werden.
+
+jQuery hat eine Funktion namens `.prop()`, mit der du die Eigenschaften von Elementen anpassen kannst.
+
+So kannst du alle Buttons deaktivieren:
+
+```js
+$("button").prop("disabled", true);
+```
+
+Deaktiviere nur den Button `target1`.
+
+# --hints--
+
+Dein Button `target1` sollte deaktiviert sein.
+
+```js
+assert(
+ $('#target1') &&
+ $('#target1').prop('disabled') &&
+ code.match(/["']disabled["'],( true|true)/g)
+);
+```
+
+Alle anderen Buttons sollten nicht deaktiviert sein.
+
+```js
+assert($('#target2') && !$('#target2').prop('disabled'));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/disabled[^<]*>/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/learn-how-script-tags-and-document-ready-work.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/learn-how-script-tags-and-document-ready-work.md
new file mode 100644
index 00000000000..71449a63e24
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/learn-how-script-tags-and-document-ready-work.md
@@ -0,0 +1,118 @@
+---
+id: bad87fee1348bd9acdd08826
+title: Lerne, wie Script Tags und Document Ready funktionieren
+challengeType: 6
+forumTopicId: 18224
+dashedName: learn-how-script-tags-and-document-ready-work
+---
+
+# --description--
+
+Jetzt sind wir bereit, jQuery zu lernen, das beliebteste JavaScript-Tool aller Zeiten.
+
+Bevor wir mit jQuery arbeiten können, müssen wir einige Dinge zu unserem HTML hinzufügen.
+
+Als erstes fügst du ein `script`-Element oben auf deiner Seite ein. Achte darauf, dass du es in der folgenden Zeile schließt.
+
+Dein Browser führt jedes JavaScript innerhalb eines `script`-Elements aus, auch jQuery.
+
+Im `script`-Element fügst du den Code `$(document).ready(function() {` zum `script` hinzu. Dann schließe es in der folgenden Zeile (immer noch innerhalb deines `script`-Elements) mit: `});`
+
+Wir werden später mehr über Funktionen (`functions`) lernen. Das Wichtige das du wissen musst ist, dass der Code, den du innerhalb dieser `function` einfügst, ausgeführt wird, sobald dein Browser deine Seite geladen hat.
+
+Das ist wichtig, denn ohne deine `document ready function` könnte dein Code laufen, bevor dein HTML gerendert wird, was zu Fehlern führen würde.
+
+# --hints--
+
+Du solltest ein `script`-Element erstellen und sicherstellen, dass es gültig ist und einen schließenden Tag hat.
+
+```js
+assert(
+ code.match(/<\/script\s*>/g) &&
+ code.match(
+ /
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-an-element-using-jquery.md
new file mode 100644
index 00000000000..05407a64d5a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-an-element-using-jquery.md
@@ -0,0 +1,109 @@
+---
+id: bad87fee1348bd9aed708826
+title: Entferne ein Element mit jQuery
+challengeType: 6
+forumTopicId: 18262
+dashedName: remove-an-element-using-jquery
+---
+
+# --description--
+
+Jetzt wollen wir mit jQuery ein HTML-Element aus deiner Seite entfernen.
+
+jQuery hat eine Funktion namens `.remove()`, die ein HTML-Element vollständig entfernt
+
+Entferne das Element `#target4` aus der Seite, indem du die Funktion `.remove()` verwendest.
+
+# --hints--
+
+Du solltest jQuery verwenden, um dein Element `target4` von deiner Seite zu entfernen.
+
+```js
+assert(
+ $('#target4').length === 0 && code.match(/\$\(["']#target4["']\).remove\(\)/g)
+);
+```
+
+Du solltest nur jQuery verwenden, um dieses Element zu entfernen.
+
+```js
+assert(
+ code.match(/id="target4/g) &&
+ !code.match(//g) &&
+ $('#right-well').length > 0
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-classes-from-an-element-with-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-classes-from-an-element-with-jquery.md
new file mode 100644
index 00000000000..29293b95dc8
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/remove-classes-from-an-element-with-jquery.md
@@ -0,0 +1,122 @@
+---
+id: bad87fee1348bd9aed918626
+title: Klassen aus einem Element mit jQuery entfernen
+challengeType: 6
+forumTopicId: 18264
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: remove-classes-from-an-element-with-jquery
+---
+
+# --description--
+
+Genauso wie du mit der Funktion `addClass()` von jQuery Klassen zu einem Element hinzufügen kannst, kannst du sie mit der Funktion `removeClass()` von jQuery entfernen.
+
+Hier siehst du, wie du das für einen bestimmten Button machen kannst:
+
+```js
+$("#target2").removeClass("btn-default");
+```
+
+Entfernen wir die Klasse `btn-default` aus allen unseren `button`-Elementen.
+
+# --hints--
+
+Die Klasse `btn-default` sollte aus all deinen `button`-Elementen entfernt werden.
+
+```js
+assert($('.btn-default').length === 0);
+```
+
+Du solltest jQuery nur verwenden, um diese Klasse aus dem Element zu entfernen.
+
+```js
+assert(code.match(/btn btn-default/g));
+```
+
+Du solltest nur die Klasse `btn-default` entfernen.
+
+```js
+assert(
+ code.match(
+ /\.[\v\s]*removeClass[\s\v]*\([\s\v]*('|")\s*btn-default\s*('|")[\s\v]*\)/gm
+ )
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-a-specific-child-of-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-a-specific-child-of-an-element-using-jquery.md
new file mode 100644
index 00000000000..51c3aea1273
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-a-specific-child-of-an-element-using-jquery.md
@@ -0,0 +1,144 @@
+---
+id: bad87fee1348bd9aed108826
+title: Ein bestimmtes Kindelement eines Elements mit jQuery auswählen
+challengeType: 6
+forumTopicId: 18315
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-a-specific-child-of-an-element-using-jquery
+---
+
+# --description--
+
+Du hast gesehen, warum id-Attribute so praktisch für das Auswählen mit jQuery-Selektoren sind. Aber du wirst nicht immer saubere ids haben, mit denen du arbeiten kannst.
+
+Zum Glück gibt es in jQuery noch ein paar andere Tricks, um die richtigen Elemente zu finden.
+
+jQuery verwendet CSS-Selektoren, um Elemente auszuwählen. Der CSS-Selektor `target:nth-child(n)` ermöglicht es dir, alle n-ten Elemente mit der Zielklasse oder dem Elementtyp auszuwählen.
+
+So würdest du dem dritten Element in jedem "well" die Bounce-Klasse geben:
+
+```js
+$(".target:nth-child(3)").addClass("animated bounce");
+```
+
+Lass das zweite Kindelement in jedem deiner "well"-Elemente hüpfen. Du musst die Kindelemente der Elemente mit der Klasse `target` auswählen.
+
+# --hints--
+
+Das zweite Element im Element `target` sollte einen Bounce-Animationseffekt haben.
+
+```js
+assert(
+ $('.target:nth-child(2)').hasClass('animated') &&
+ $('.target:nth-child(2)').hasClass('bounce')
+);
+```
+
+Nur zwei Elemente sollten hüpfen (bounce).
+
+```js
+assert($('.animated.bounce').length === 2);
+```
+
+Du solltest den Selektor `:nth-child()` verwenden, um diese Elemente zu ändern.
+
+```js
+assert(code.match(/\:nth-child\(/g));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(
+ code.match(/\$\(".target:nth-child\(2\)"\)/g) ||
+ code.match(/\$\('.target:nth-child\(2\)'\)/g) ||
+ code.match(/\$\(".target"\).filter\(":nth-child\(2\)"\)/g) ||
+ code.match(/\$\('.target'\).filter\(':nth-child\(2\)'\)/g)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-class-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-class-using-jquery.md
new file mode 100644
index 00000000000..864535f3349
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-class-using-jquery.md
@@ -0,0 +1,113 @@
+---
+id: bad87fee1348bd9aedc08826
+title: Elemente nach Klasse mit jQuery auswählen
+challengeType: 6
+forumTopicId: 18316
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-elements-by-class-using-jquery
+---
+
+# --description--
+
+Siehst du, wie wir alle deine `button`-Elemente hüpfen lassen? Wir haben sie mit `$("button")` ausgewählt und ihnen dann mit `.addClass("animated bounce");` einige CSS-Klassen hinzugefügt.
+
+Du hast gerade die Funktion `.addClass()` von jQuery verwendet, mit der du Klassen zu Elementen hinzufügen kannst.
+
+Zuerst wollen wir deine `div`-Elemente mit der Klasse `well` ausstatten, indem wir den Selektor `$(".well")` verwenden.
+
+Beachte, dass du, genau wie bei CSS-Deklarationen, ein `.` vor dem Namen der Klasse schreibst.
+
+Dann benutze die Funktion `.addClass()` von jQuery, um die Klassen `animated` und `shake` hinzuzufügen.
+
+Du könntest zum Beispiel alle Elemente mit der Klasse `text-primary` zum Wackeln bringen, indem du die folgende Zeile in deine `document ready function` einfügst:
+
+```js
+$(".text-primary").addClass("animated shake");
+```
+
+# --hints--
+
+Du solltest die jQuery-Funktion `addClass()` verwenden, um allen deinen Elementen mit der Klasse `well` die Klassen `animated` und `shake` zu übergeben.
+
+```js
+assert($('.well').hasClass('animated') && $('.well').hasClass('shake'));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/class\.\*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-id-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-id-using-jquery.md
new file mode 100644
index 00000000000..1b4957492f0
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-elements-by-id-using-jquery.md
@@ -0,0 +1,123 @@
+---
+id: bad87fee1348bd9aeda08826
+title: Elemente mithilfe von id mit jQuery anvisieren
+challengeType: 6
+forumTopicId: 18317
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-elements-by-id-using-jquery
+---
+
+# --description--
+
+Du kannst Elemente auch über ihre id-Attribute auswählen.
+
+Verwende zunächst den Selektor `$("#target3")` für dein `button`-Element mit der id `target3`.
+
+Beachte, dass du, genau wie bei CSS-Deklarationen, ein `#` vor dem Namen der id schreibst.
+
+Dann benutze die Funktion `.addClass()` von jQuery, um die Klassen `animated` und `fadeOut` hinzuzufügen.
+
+Hier siehst du, wie du das `button`-Element mit der id `target6` ausblendest:
+
+```js
+$("#target6").addClass("animated fadeOut");
+```
+
+# --hints--
+
+Du solltest das `button`-Element mit der `id` von `target3` auswählen und die jQuery Funktion `addClass()` verwenden, um ihm die Klasse `animated` zu geben.
+
+```js
+assert($('#target3').hasClass('animated'));
+```
+
+Du solltest das Element mit der id `target3` auswählen und die jQuery-Funktion `addClass()` verwenden, um ihm die Klasse `fadeOut` zu geben.
+
+```js
+assert(
+ ($('#target3').hasClass('fadeOut') || $('#target3').hasClass('fadeout')) &&
+ code.match(/\$\(\s*.#target3.\s*\)/g)
+);
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-even-elements-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-even-elements-using-jquery.md
new file mode 100644
index 00000000000..fa6724b04cb
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-even-elements-using-jquery.md
@@ -0,0 +1,139 @@
+---
+id: bad87fee1348bd9aed008826
+title: Wähle gerade Elemente mit jQuery aus
+challengeType: 6
+forumTopicId: 18318
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-even-elements-using-jquery
+---
+
+# --description--
+
+Mit den Selektoren `:odd` (ungerade) und `:even` (gerade) kannst du auch Elemente anhand ihrer Position auswählen.
+
+Beachte, dass jQuery einen Null-Index hat, was bedeutet, dass das erste Element in einer Auswahl die Position 0 hat. Das kann etwas verwirrend sein, da `:odd` entgegen der Intuition das zweite Element (Position 1), das vierte Element (Position 3) und so weiter auswählt.
+
+So würdest du alle ungeraden Elemente mit der Klasse `target` anvisieren und ihnen Klassen zuweisen:
+
+```js
+$(".target:odd").addClass("animated shake");
+```
+
+Versuche, alle geraden `target`-Elemente auszuwählen und ihnen die Klassen `animated` und `shake` zu geben. Denke daran, dass **even** sich auf die Position der Elemente bezieht, wenn du ein nullbasiertes System im Kopf hast.
+
+# --hints--
+
+Alle `target`-Elemente, die jQuery als gerade betrachtet, sollten wackeln.
+
+```js
+assert(
+ $('.target:even').hasClass('animated') && $('.target:even').hasClass('shake')
+);
+```
+
+Du solltest den Selektor `:even` verwenden, um diese Elemente zu ändern.
+
+```js
+assert(code.match(/\:even/g));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(
+ code.match(/\$\(".target:even"\)/g) ||
+ code.match(/\$\('.target:even'\)/g) ||
+ code.match(/\$\(".target"\).filter\(":even"\)/g) ||
+ code.match(/\$\('.target'\).filter\(':even'\)/g)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-html-elements-with-selectors-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-html-elements-with-selectors-using-jquery.md
new file mode 100644
index 00000000000..cf9ab192ce5
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-html-elements-with-selectors-using-jquery.md
@@ -0,0 +1,118 @@
+---
+id: bad87fee1348bd9bedc08826
+title: HTML-Elemente mithilfe von Selektoren mit jQuery auswählen
+challengeType: 6
+forumTopicId: 18319
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-html-elements-with-selectors-using-jquery
+---
+
+# --description--
+
+Jetzt haben wir eine `document ready`-Funktion.
+
+Jetzt schreiben wir unsere erste jQuery-Anweisung. Alle jQuery-Funktionen beginnen mit einem `$`, der normalerweise als Dollarzeichen-Operator oder als Bling bezeichnet wird.
+
+Oft wählt jQuery ein HTML-Element mit einem Selektor aus und macht dann etwas mit diesem Element.
+
+Lass zum Beispiel alle deine `button`-Elemente hüpfen. Füge diesen Code einfach in deine document ready-Funktion ein:
+
+```js
+$("button").addClass("animated bounce");
+```
+
+Beachte, dass wir sowohl die jQuery-Bibliothek als auch die Animate.css-Bibliothek bereits im Hintergrund eingebunden haben, damit du sie im Editor verwenden kannst. Du benutzt also jQuery, um die Animate.css Klasse `bounce` auf deine `button`-Elemente anzuwenden.
+
+# --hints--
+
+Du solltest die jQuery Funktion `addClass()` verwenden, um deinen `button`-Elementen die Klassen `animated` und `bounce` zu geben.
+
+```js
+assert($('button').hasClass('animated') && $('button').hasClass('bounce'));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+Dein jQuery-Code sollte innerhalb der `$(document).ready();`-Funktion stehen.
+
+```js
+assert(
+ code.replace(/\s/g, '').match(/\$\(document\)\.ready\(function\(\)\{\$/g)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-children-of-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-children-of-an-element-using-jquery.md
new file mode 100644
index 00000000000..18d86ef4c6a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-children-of-an-element-using-jquery.md
@@ -0,0 +1,125 @@
+---
+id: bad87fee1348bd9aed208826
+title: Mit jQuery die Kindelemente eines Elements auswählen
+challengeType: 6
+forumTopicId: 18320
+dashedName: target-the-children-of-an-element-using-jquery
+---
+
+# --description--
+
+Wenn HTML-Elemente eine Ebene unter einem anderen platziert werden, nennt man sie Kinder dieses Elements. Zum Beispiel sind die Button-Elemente in dieser Aufgabe mit dem Text `#target1`, `#target2` und `#target3` alle Kinder des Elements ``.
+
+jQuery hat eine Funktion namens `children()`, mit der du auf die Kinder des von dir ausgewählten Elements zugreifen kannst.
+
+Hier ist ein Beispiel dafür, wie du die Funktion `children()` verwendest, um den Kindern deines Elements `left-well` die Farbe `blue` zu geben:
+
+```js
+$("#left-well").children().css("color", "blue")
+```
+
+# --instructions--
+
+Gib allen Kindelementen deines Elements `right-well` die Farbe Orange.
+
+# --hints--
+
+Alle Kindelemente von `#right-well` sollten orangefarbenen Text enthalten.
+
+```js
+assert($('#right-well').children().css('color') === 'rgb(255, 165, 0)');
+```
+
+Du solltest die Funktion `children()` verwenden, um diese Elemente zu ändern.
+
+```js
+assert(code.match(/\.children\(\)\.css/g));
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(code.match(/
/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-parent-of-an-element-using-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-parent-of-an-element-using-jquery.md
new file mode 100644
index 00000000000..82718297a9e
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-parent-of-an-element-using-jquery.md
@@ -0,0 +1,140 @@
+---
+id: bad87fee1348bd9aed308826
+title: Das Elternelement eines Elements mit jQuery auswählen
+challengeType: 6
+forumTopicId: 18321
+dashedName: target-the-parent-of-an-element-using-jquery
+---
+
+# --description--
+
+Jedes HTML-Element hat ein Eltern(`parent`)-element, von dem es Eigenschaften erbt (`inherits`).
+
+Zum Beispiel hat dein `jQuery Playground` `h3`-Element das Elternelement von `
`, das wiederum das Elternelement `body` hat.
+
+jQuery hat eine Funktion namens `parent()`, mit der du auf das Elternelement des von dir ausgewählten Elements zugreifen kannst.
+
+Hier ist ein Beispiel dafür, wie du die Funktion `parent()` verwenden würdest, wenn du dem Elternelement des Elements `left-well` eine blaue Hintergrundfarbe geben möchtest:
+
+```js
+$("#left-well").parent().css("background-color", "blue")
+```
+
+Gib dem Elternelement des Elements `#target1` eine rote Hintergrundfarbe.
+
+# --hints--
+
+Dein Element `left-well` sollte einen roten Hintergrund haben.
+
+```js
+assert(
+ $('#left-well').css('background-color') === 'red' ||
+ $('#left-well').css('background-color') === 'rgb(255, 0, 0)' ||
+ $('#left-well').css('background-color').toLowerCase() === '#ff0000' ||
+ $('#left-well').css('background-color').toLowerCase() === '#f00'
+);
+```
+
+Du solltest die Funktion `.parent()` verwenden, um dieses Element zu ändern.
+
+```js
+assert(code.match(/\.parent\s*\(\s*\)\s*\.css/g));
+```
+
+Die Methode `.parent()` sollte für das Element `#target1` aufgerufen werden.
+
+```js
+assert(
+ code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")\s*?\)\s*?\.parent/gi)
+);
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(code.match(/
/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-same-element-with-multiple-jquery-selectors.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-same-element-with-multiple-jquery-selectors.md
new file mode 100644
index 00000000000..1e4c5f1aa85
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/target-the-same-element-with-multiple-jquery-selectors.md
@@ -0,0 +1,143 @@
+---
+id: bad87fee1348bd9aed908626
+title: Mit mehreren jQuery-Selektoren dasselbe Element auswählen
+challengeType: 6
+forumTopicId: 18322
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: target-the-same-element-with-multiple-jquery-selectors
+---
+
+# --description--
+
+Du kennst jetzt drei Möglichkeiten, Elemente anzusprechen: nach Typ: `$("button")`, nach Klasse: `$(".btn")`, und nach der id `$("#target1")`.
+
+Obwohl es möglich ist, mehrere Klassen in einem einzigen `.addClass()`-Aufruf hinzuzufügen, wollen wir sie demselben Element auf *drei verschiedene Arten* hinzufügen.
+
+Füge mit `.addClass()` jeweils nur eine Klasse auf drei verschiedene Arten zum selben Element hinzu:
+
+Füge die Klasse `animated` zu allen Elementen mit dem Typ `button` hinzu.
+
+Füge die Klasse `shake` zu allen Buttons mit der Klasse `.btn` hinzu.
+
+Füge die Klasse `btn-primary` zu dem Button mit der id `#target1` hinzu.
+
+**Hinweis:** Du solltest immer nur ein Element auswählen und nur eine Klasse auf einmal hinzufügen. Insgesamt fügen deine drei einzelnen Selektoren am Ende die drei Klassen `shake`, `animated` und `btn-primary` zu `#target1` hinzu.
+
+# --hints--
+
+Dein Code sollte den Selektor `$("button")` verwenden.
+
+```js
+assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?button\s*?(?:'|")/gi));
+```
+
+Dein Code sollte den Selektor `$(".btn")` verwenden.
+
+```js
+assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?\.btn\s*?(?:'|")/gi));
+```
+
+Dein Code sollte den Selektor `$("#target1")` verwenden.
+
+```js
+assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")/gi));
+```
+
+Du solltest nur eine Klasse mit jedem deiner drei Selektoren hinzufügen.
+
+```js
+assert(
+ code.match(/addClass/g) &&
+ code.match(/addClass\s*?\(\s*?('|")\s*?[\w-]+\s*?\1\s*?\)/g).length > 2
+);
+```
+
+Dein Element `#target1` sollte die Klassen `animated`' `shake` und `btn-primary` besitzen.
+
+```js
+assert(
+ $('#target1').hasClass('animated') &&
+ $('#target1').hasClass('shake') &&
+ $('#target1').hasClass('btn-primary')
+);
+```
+
+Du solltest nur jQuery verwenden, um diese Klassen zu dem Element hinzuzufügen.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-appendto-to-move-elements-with-jquery.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-appendto-to-move-elements-with-jquery.md
new file mode 100644
index 00000000000..dd0f3cfc4b8
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-appendto-to-move-elements-with-jquery.md
@@ -0,0 +1,117 @@
+---
+id: bad87fee1348bd9aed608826
+title: Verwende appendTo, um Elemente mit jQuery zu verschieben
+challengeType: 6
+forumTopicId: 18340
+dashedName: use-appendto-to-move-elements-with-jquery
+---
+
+# --description--
+
+Jetzt wollen wir versuchen, Elemente von einem `div` in ein anderes zu verschieben.
+
+jQuery hat eine Funktion namens `appendTo()`, mit der du HTML-Elemente auswählen und sie an ein anderes Element anhängen kannst.
+
+Wenn wir zum Beispiel `target4` von rechts nach links verschieben wollen, würden wir Folgendes verwenden:
+
+```js
+$("#target4").appendTo("#left-well");
+```
+
+Verschiebe dein Element `target2` von deinem `left-well` zu deinem `right-well`.
+
+# --hints--
+
+Dein Element `target2` sollte sich nicht innerhalb deines `left-well` befinden.
+
+```js
+assert($('#left-well').children('#target2').length === 0);
+```
+
+Dein Element `target2` sollte sich innerhalb deines `right-well` befinden.
+
+```js
+assert($('#right-well').children('#target2').length > 0);
+```
+
+Du solltest nur jQuery verwenden, um diese Elemente zu verschieben.
+
+```js
+assert(!code.match(/class.*animated/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-jquery-to-modify-the-entire-page.md b/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-jquery-to-modify-the-entire-page.md
new file mode 100644
index 00000000000..33691c57bd4
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/jquery/use-jquery-to-modify-the-entire-page.md
@@ -0,0 +1,118 @@
+---
+id: bad87fee1348bd9aecb08826
+title: Verwende jQuery, um die gesamte Seite zu ändern
+challengeType: 6
+forumTopicId: 18361
+required:
+ -
+ link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
+dashedName: use-jquery-to-modify-the-entire-page
+---
+
+# --description--
+
+Wir sind fertig mit unserem jQuery-Spielplatz. Lasst ihn uns abreißen!
+
+jQuery kann auch das `body`-Element auswählen.
+
+So würden wir den gesamten Body ausblenden lassen: `$("body").addClass("animated fadeOut");`
+
+Aber lass uns etwas Dramatischeres machen. Füge die Klassen `animated` und `hinge` zu deinem `body`-Element hinzu.
+
+# --hints--
+
+Du solltest die Klassen `animated` und `hinge` zu deinem `body`-Element hinzufügen.
+
+```js
+assert($('body').hasClass('animated') && $('body').hasClass('hinge'));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+
jQuery Playground
+
+
+
#left-well
+
+ #target1
+ #target2
+ #target3
+
+
+
+
#right-well
+
+ #target4
+ #target5
+ #target6
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-react.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-react.md
new file mode 100644
index 00000000000..f82f9b06e25
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-react.md
@@ -0,0 +1,156 @@
+---
+id: 5a24c314108439a4d4036147
+title: Redux mit React verbinden
+challengeType: 6
+forumTopicId: 301426
+dashedName: connect-redux-to-react
+---
+
+# --description--
+
+Nachdem du nun die Funktionen `mapStateToProps()` und `mapDispatchToProps()` geschrieben hast, kannst du sie verwenden, um `state` und `dispatch` auf die `props` einer deiner React-Komponenten zuzuordnen. Die `connect` Methode von React Redux kann diese Aufgabe übernehmen. Diese Methode hat zwei optionale Argumente, `mapStateToProps()` und `mapDispatchToProps()`. Sie sind optional, weil du vielleicht eine Komponente hast, die nur auf den `state` zugreifen, aber keine Aktionen absetzen muss, oder andersherum.
+
+Um diese Methode zu verwenden, übergibst du die Funktionen als Argumente und rufst das Ergebnis sofort mit deiner Komponente auf. Diese Syntax ist ein wenig ungewöhnlich und sieht wie folgt aus:
+
+```js
+connect(mapStateToProps, mapDispatchToProps)(MyComponent)
+```
+
+**Hinweis:** Wenn du eines der Argumente für die Methode `connect` weglassen willst, übergibst du stattdessen `null`.
+
+# --instructions--
+
+Der Code-Editor enthält die Funktionen `mapStateToProps()` und `mapDispatchToProps()` und eine neue React-Komponente namens `Presentational`. Verbinde diese Komponente mit der `connect`-Methode aus dem globalen `ReactRedux`-Objekt mit Redux und rufe sie sofort in der `Presentational`-Komponente auf. Weise das Ergebnis einer neuen Konstanten (`const`) namens `ConnectedComponent` zu, die die verbundene Komponente darstellt. Das war's, jetzt bist du mit Redux verbunden! Versuche, eines der Argumente von `connect` in `null` zu ändern und beobachte die Testergebnisse.
+
+# --hints--
+
+Die Komponente `Presentational` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('Presentational').length === 1;
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte über `connect` eine Eigenschaft `messages` erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const props = mockedComponent.find('Presentational').props();
+ return props.messages === '__INITIAL__STATE__';
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte eine Eigenschaft`submitNewMessage` über `connect` erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const props = mockedComponent.find('Presentational').props();
+ return typeof props.submitNewMessage === 'function';
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+const store = Redux.createStore(
+ (state = '__INITIAL__STATE__', action) => state
+);
+class AppWrapper extends React.Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const addMessage = (message) => {
+ return {
+ type: 'ADD',
+ message: message
+ }
+};
+
+const mapStateToProps = (state) => {
+ return {
+ messages: state
+ }
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (message) => {
+ dispatch(addMessage(message));
+ }
+ }
+};
+
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
This is a Presentational Component
+ }
+};
+
+const connect = ReactRedux.connect;
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+const addMessage = (message) => {
+ return {
+ type: 'ADD',
+ message: message
+ }
+};
+
+const mapStateToProps = (state) => {
+ return {
+ messages: state
+ }
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (message) => {
+ dispatch(addMessage(message));
+ }
+ }
+};
+
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
This is a Presentational Component
+ }
+};
+
+const connect = ReactRedux.connect;
+// Change code below this line
+
+const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-the-messages-app.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-the-messages-app.md
new file mode 100644
index 00000000000..347051ecf81
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/connect-redux-to-the-messages-app.md
@@ -0,0 +1,300 @@
+---
+id: 5a24c314108439a4d4036148
+title: Redux mit der Nachrichten-App verbinden
+challengeType: 6
+forumTopicId: 301427
+dashedName: connect-redux-to-the-messages-app
+---
+
+# --description--
+
+Da du nun weißt, wie du `connect` verwendest, um React mit Redux zu verbinden, kannst du das Gelernte auf deine React-Komponente anwenden, die Nachrichten verarbeitet.
+
+In der letzten Lektion wurde die Komponente, die du mit Redux verbunden hast, `Presentational` genannt, und das war nicht willkürlich. Dieser Begriff *bezieht sich im Allgemeinen* auf React-Komponenten, die nicht direkt mit Redux verbunden sind. Sie sind lediglich für die Präsentation der Benutzeroberfläche(UI) verantwortlich und tun dies in Abhängigkeit von den Eigenschaften(Props), die sie erhalten. Im Gegensatz dazu sind die Container-Komponenten mit Redux verbunden. Diese sind in der Regel für das Versenden von Aktionen an den Store verantwortlich und übergeben den Zustand des Stores oft als Eigenschaften an die Kindkomponenten.
+
+# --instructions--
+
+Der Code-Editor enthält den gesamten Code, den du bisher in diesem Abschnitt geschrieben hast. Die einzige Änderung ist, dass die React-Komponente in `Presentational` umbenannt wurde. Erstelle eine neue Komponente in einer Konstante namens `Container`, die `connect` verwendet, um die Komponente `Presentational` mit Redux zu verbinden. Dann renderst du im `AppWrapper` die React Redux `Provider` Komponente. Übergib `Provider` an den Redux `store` als Eigenschaft und rendere `Container` als Kindkomponente. Sobald alles eingerichtet ist, siehst du, dass die Nachrichten-App wieder auf der Seite angezeigt wird.
+
+# --hints--
+
+Der `AppWrapper` sollte auf der Seite gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('AppWrapper').length === 1;
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte auf der Seite gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('Presentational').length === 1;
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte ein `h2`, `input`, `button` und `ul`-Element rendern.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ return (
+ PresentationalComponent.find('div').length === 1 &&
+ PresentationalComponent.find('h2').length === 1 &&
+ PresentationalComponent.find('button').length === 1 &&
+ PresentationalComponent.find('ul').length === 1
+ );
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte Nachrichten (`messages`) aus dem Redux-Store als Eigenschaft erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ const props = PresentationalComponent.props();
+ return Array.isArray(props.messages);
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte den `submitMessage`-Action Creator als Eigenschaft erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ const props = PresentationalComponent.props();
+ return typeof props.submitNewMessage === 'function';
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message: message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => {
+ const currentMessage = state.input;
+ return {
+ input: '',
+ messages: state.messages.concat(currentMessage)
+ };
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+
+// React-Redux:
+const mapStateToProps = (state) => {
+ return { messages: state }
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (newMessage) => {
+ dispatch(addMessage(newMessage))
+ }
+ }
+};
+
+const Provider = ReactRedux.Provider;
+const connect = ReactRedux.connect;
+
+// Define the Container component here:
+
+
+class AppWrapper extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ // Complete the return statement:
+ return (null);
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message: message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => {
+ const currentMessage = state.input;
+ return {
+ input: '',
+ messages: state.messages.concat(currentMessage)
+ };
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+
+// React-Redux:
+const mapStateToProps = (state) => {
+ return { messages: state }
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (newMessage) => {
+ dispatch(addMessage(newMessage))
+ }
+ }
+};
+
+const Provider = ReactRedux.Provider;
+const connect = ReactRedux.connect;
+
+const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
+
+class AppWrapper extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-local-state-into-redux.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-local-state-into-redux.md
new file mode 100644
index 00000000000..d69f85c1375
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-local-state-into-redux.md
@@ -0,0 +1,397 @@
+---
+id: 5a24c314108439a4d4036149
+title: Lokalen Zustand in Redux extrahieren
+challengeType: 6
+forumTopicId: 301428
+dashedName: extract-local-state-into-redux
+---
+
+# --description--
+
+Du hast es fast geschafft! Erinnere dich daran, dass du den gesamten Redux-Code geschrieben hast, damit Redux die Zustandsverwaltung deiner React-Messages-App steuern kann. Jetzt, wo Redux angebunden ist, musst du die Zustandsverwaltung aus der Komponente `Presentational` herausnehmen und in Redux integrieren. Im Moment hast du Redux verbunden, aber du handhabst den Zustand lokal innerhalb der Komponente `Presentational`.
+
+# --instructions--
+
+Entferne in der Komponente `Presentational` zuerst die Eigenschaft `messages` im lokalen `state`. Diese Nachrichten werden von Redux verwaltet. Als Nächstes änderst du die Methode `submitMessage()` so, dass sie `submitNewMessage()` von `this.props` aus auslöst, und übergibst die aktuelle Nachrichteneingabe vom lokalen `state` als Argument. Da du `messages` aus dem lokalen Zustand entfernt hast, entferne auch hier die Eigenschaft `messages` aus dem Aufruf von `this.setState()`. Schließlich änderst du die `render()`-Methode so, dass sie die Nachrichten von `props` und nicht von `state` übernimmt.
+
+Sobald diese Änderungen vorgenommen wurden, funktioniert die App weiterhin wie bisher, nur dass Redux den Zustand verwaltet. Dieses Beispiel zeigt auch, wie eine Komponente einen lokalen `state` haben kann: Deine Komponente verfolgt die Benutzereingaben immer noch lokal in ihrem eigenen `state`. Wie du siehst, bietet Redux ein nützliches Framework zur Zustandsverwaltung auf der Basis von React. Du hast das gleiche Ergebnis erzielt, indem du zunächst nur den lokalen Zustand von React verwendet hast, und das ist bei einfachen Apps normalerweise möglich. Doch je größer und komplexer deine Apps werden, desto schwieriger wird auch die Zustandsverwaltung, und genau dieses Problem löst Redux.
+
+# --hints--
+
+Der `AppWrapper` sollte auf der Seite gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('AppWrapper').length === 1;
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte auf der Seite gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('Presentational').length === 1;
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte ein `h2`, `input`, `button` und `ul`-Element rendern.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ return (
+ PresentationalComponent.find('div').length === 1 &&
+ PresentationalComponent.find('h2').length === 1 &&
+ PresentationalComponent.find('button').length === 1 &&
+ PresentationalComponent.find('ul').length === 1
+ );
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte `messages` aus dem Redux-Store als Eigenschaft erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ const props = PresentationalComponent.props();
+ return Array.isArray(props.messages);
+ })()
+);
+```
+
+Die Komponente `Presentational` sollte den `submitMessage` Action Creator als Eigenschaft erhalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalComponent = mockedComponent.find('Presentational');
+ const props = PresentationalComponent.props();
+ return typeof props.submitNewMessage === 'function';
+ })()
+);
+```
+
+Der Zustand der Komponente `Presentational` sollte eine Eigenschaft `input` erhalten, die mit einem leeren String initialisiert wird.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const PresentationalState = mockedComponent
+ .find('Presentational')
+ .instance().state;
+ return (
+ typeof PresentationalState.input === 'string' &&
+ Object.keys(PresentationalState).length === 1
+ );
+ })()
+);
+```
+
+Die Eingabe in das `input`-Element sollte den Zustand der Komponente `Presentational` aktualisieren.
+
+```js
+async () => {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const testValue = '__MOCK__INPUT__';
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ let initialInput = mockedComponent.find('Presentational').find('input');
+ const changed = () => {
+ causeChange(mockedComponent, testValue);
+ return waitForIt(() => mockedComponent);
+ };
+ const updated = await changed();
+ const updatedInput = updated.find('Presentational').find('input');
+ assert(
+ initialInput.props().value === '' &&
+ updatedInput.props().value === '__MOCK__INPUT__'
+ );
+};
+```
+
+Das Senden der `submitMessage` an die Komponente `Presentational` sollte den Redux-Store aktualisieren und die Eingabe im lokalen Zustand löschen.
+
+```js
+async () => {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
+ let beforeProps = mockedComponent.find('Presentational').props();
+ const testValue = '__TEST__EVENT__INPUT__';
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const changed = () => {
+ causeChange(mockedComponent, testValue);
+ return waitForIt(() => mockedComponent);
+ };
+ const clickButton = () => {
+ mockedComponent.find('button').simulate('click');
+ return waitForIt(() => mockedComponent);
+ };
+ const afterChange = await changed();
+ const afterChangeInput = afterChange.find('input').props().value;
+ const afterClick = await clickButton();
+ const afterProps = mockedComponent.find('Presentational').props();
+ assert(
+ beforeProps.messages.length === 0 &&
+ afterChangeInput === testValue &&
+ afterProps.messages.pop() === testValue &&
+ afterClick.find('input').props().value === ''
+ );
+};
+```
+
+Die Komponente `Presentational` soll die `messages` aus dem Redux-Store wiedergeben.
+
+```js
+async () => {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
+ let beforeProps = mockedComponent.find('Presentational').props();
+ const testValue = '__TEST__EVENT__INPUT__';
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const changed = () => {
+ causeChange(mockedComponent, testValue);
+ return waitForIt(() => mockedComponent);
+ };
+ const clickButton = () => {
+ mockedComponent.find('button').simulate('click');
+ return waitForIt(() => mockedComponent);
+ };
+ const afterChange = await changed();
+ const afterChangeInput = afterChange.find('input').props().value;
+ const afterClick = await clickButton();
+ const afterProps = mockedComponent.find('Presentational').props();
+ assert(
+ beforeProps.messages.length === 0 &&
+ afterChangeInput === testValue &&
+ afterProps.messages.pop() === testValue &&
+ afterClick.find('input').props().value === '' &&
+ afterClick.find('ul').childAt(0).text() === testValue
+ );
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message: message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+const Provider = ReactRedux.Provider;
+const connect = ReactRedux.connect;
+
+// Change code below this line
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => ({
+ input: '',
+ messages: state.messages.concat(state.input)
+ }));
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+// Change code above this line
+
+const mapStateToProps = (state) => {
+ return {messages: state}
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (message) => {
+ dispatch(addMessage(message))
+ }
+ }
+};
+
+const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
+
+class AppWrapper extends React.Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message: message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+const Provider = ReactRedux.Provider;
+const connect = ReactRedux.connect;
+
+// Change code below this line
+class Presentational extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: ''
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.props.submitNewMessage(this.state.input);
+ this.setState({
+ input: ''
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.props.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+// Change code above this line
+
+const mapStateToProps = (state) => {
+ return {messages: state}
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: (message) => {
+ dispatch(addMessage(message))
+ }
+ }
+};
+
+const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
+
+class AppWrapper extends React.Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-state-logic-to-redux.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-state-logic-to-redux.md
new file mode 100644
index 00000000000..4e31b04aa04
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/extract-state-logic-to-redux.md
@@ -0,0 +1,115 @@
+---
+id: 5a24c314108439a4d4036143
+title: Zustandslogik nach Redux extrahieren
+challengeType: 6
+forumTopicId: 301429
+dashedName: extract-state-logic-to-redux
+---
+
+# --description--
+
+Nachdem du die React-Komponente fertiggestellt hast, musst du die Logik, die sie lokal in ihrem `state` ausführt, in Redux übertragen. Dies ist der erste Schritt, um die einfache React-App mit Redux zu verbinden. Die einzige Funktion, die deine App hat, ist das Hinzufügen neuer Nachrichten des Nutzers zu einer ungeordneten Liste. Das Beispiel ist simpel, um zu zeigen, wie React und Redux zusammenarbeiten.
+
+# --instructions--
+
+Definiere zuerst einen Aktionstyp `ADD` und setze ihn auf eine Konstante `ADD`. Als nächstes definierst du einen Action Creator `addMessage()`, der die Aktion zum Hinzufügen einer Nachricht erstellt. Du musst eine `message` an diesen Action Creator übergeben und die Nachricht in die zurückgegebene `action` einfügen.
+
+Dann erstelle einen Reducer namens `messageReducer()`, der den Zustand für die Nachrichten verwaltet. Der Initialzustand sollte ein leeres Array sein. Dieser Reducer sollte eine Nachricht zu dem Array der Nachrichten im Zustand hinzufügen oder den aktuellen Zustand zurückgeben. Schließlich erstellst du deinen Redux-Store und übergibst ihm den Reducer.
+
+# --hints--
+
+Die Konstante `ADD` sollte existieren und einen Wert enthalten, der dem String `ADD` entspricht.
+
+```js
+assert(ADD === 'ADD');
+```
+
+Der Action Creator `addMessage` sollte ein Objekt mit `type` gleich `ADD` und `message` gleich der übergebenen Nachricht zurückgeben.
+
+```js
+assert(
+ (function () {
+ const addAction = addMessage('__TEST__MESSAGE__');
+ return addAction.type === ADD && addAction.message === '__TEST__MESSAGE__';
+ })()
+);
+```
+
+`messageReducer` sollte eine Funktion sein.
+
+```js
+assert(typeof messageReducer === 'function');
+```
+
+Der Store sollte existieren und einen Initialzustand haben, der auf ein leeres Array gesetzt ist.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ return typeof store === 'object' && initialState.length === 0;
+ })()
+);
+```
+
+Wenn du `addMessage` an den Store sendest, sollte eine neue Nachricht unveränderlich zu dem Array der Nachrichten im Zustand hinzugefügt werden.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ const isFrozen = DeepFreeze(initialState);
+ store.dispatch(addMessage('__A__TEST__MESSAGE'));
+ const addState = store.getState();
+ return isFrozen && addState[0] === '__A__TEST__MESSAGE';
+ })()
+);
+```
+
+Der `messageReducer` sollte den aktuellen Zustand zurückgeben, wenn er mit einer anderen Aktion aufgerufen wird.
+
+```js
+assert(
+ (function () {
+ const addState = store.getState();
+ store.dispatch({ type: 'FAKE_ACTION' });
+ const testState = store.getState();
+ return addState === testState;
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+// Define ADD, addMessage(), messageReducer(), and store here:
+```
+
+# --solutions--
+
+```jsx
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/getting-started-with-react-redux.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/getting-started-with-react-redux.md
new file mode 100644
index 00000000000..160381c4901
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/getting-started-with-react-redux.md
@@ -0,0 +1,102 @@
+---
+id: 5a24c314108439a4d4036141
+title: Erste Schritte mit React Redux
+challengeType: 6
+forumTopicId: 301430
+dashedName: getting-started-with-react-redux
+---
+
+# --description--
+
+Diese Serie von Aufgaben zeigt dir, wie du Redux mit React nutzen kannst. Zunächst ein Überblick über die wichtigsten Prinzipien der einzelnen Technologien. React ist eine Ansichtsbibliothek, die du mit Daten versorgst und die dann die Ansicht auf eine effiziente, vorhersehbare Weise rendert. Redux ist ein Zustandsmanagement-Framework, das du verwenden kannst, um die Verwaltung des Zustands deiner Anwendung zu vereinfachen. Normalerweise erstellst du in einer React Redux-App einen einzigen Redux-Store, der den Zustand deiner gesamten App verwaltet. Deine React-Komponenten beziehen nur die Daten aus dem Store, die für ihre Rolle relevant sind. Dann sendest du Aktionen direkt von React-Komponenten aus, die dann Aktualisierungen im Store auslösen.
+
+Obwohl React-Komponenten ihren eigenen Zustand lokal verwalten können, ist es bei einer komplexen App in der Regel besser, den Zustand der App mit Redux an einem einzigen Ort zu bewahren. Es gibt Ausnahmen, in denen einzelne Komponenten lokale Zustände haben können, die nur für sie gelten. Da Redux nicht für die Zusammenarbeit mit React entwickelt wurde, musst du das `react-redux`-Paket verwenden. Es bietet dir die Möglichkeit, Redux `state` und `dispatch` als Eigenschaften (`props`) an deine React-Komponenten zu übergeben.
+
+In den nächsten Aufgaben wirst du zunächst eine einfache React-Komponente erstellen, mit der du neue Textnachrichten eingeben kannst. Diese werden zu einem Array hinzugefügt, das in der Ansicht angezeigt wird. Dies ist eine gute Wiederholung dessen, was du in den React-Lektionen gelernt hast. Als Nächstes erstellst du einen Redux-Store und Aktionen, die den Status des Nachrichten-Arrays verwalten. Schließlich verwendest du `react-redux`, um den Redux-Store mit deiner Komponente zu verbinden und so den lokalen Zustand in den Redux-Store zu extrahieren.
+
+# --instructions--
+
+Beginne mit einer `DisplayMessages` Komponente. Füge einen Konstruktor zu dieser Komponente hinzu und initialisiere sie mit einem Status, der zwei Eigenschaften besitzt: `input`, die auf einen leeren String gesetzt wird, und `messages`, die auf ein leeres Array gesetzt wird.
+
+# --hints--
+
+Die Komponente `DisplayMessages` sollte ein leeres `div`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ return mockedComponent.find('div').text() === '';
+ })()
+);
+```
+
+Der Konstruktor `DisplayMessages` sollte richtig mit `super` aufgerufen werden und `props` übergeben.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
+ return (
+ noWhiteSpace.includes('constructor(props)') &&
+ noWhiteSpace.includes('super(props')
+ );
+ })()
+ );
+```
+
+Die Komponente `DisplayMessages` sollte einen Anfangszustand haben, der `{input: "", messages: []}` lautet.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const initialState = mockedComponent.state();
+ return (
+ typeof initialState === 'object' &&
+ initialState.input === '' &&
+ Array.isArray(initialState.messages) &&
+ initialState.messages.length === 0
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class DisplayMessages extends React.Component {
+ // Change code below this line
+
+ // Change code above this line
+ render() {
+ return
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class DisplayMessages extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ }
+ render() {
+ return
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/manage-state-locally-first.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/manage-state-locally-first.md
new file mode 100644
index 00000000000..2a4b2b71ad6
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/manage-state-locally-first.md
@@ -0,0 +1,250 @@
+---
+id: 5a24c314108439a4d4036142
+title: Zustand zuerst lokal verwalten
+challengeType: 6
+forumTopicId: 301431
+dashedName: manage-state-locally-first
+---
+
+# --description--
+
+Hier beendest du die Erstellung der Komponente `DisplayMessages`.
+
+# --instructions--
+
+Zuerst lässt du die Komponente in der `render()` Methode ein `input` Element, ein `button` Element und ein `ul` Element rendern. Wenn sich das `input`-Element ändert, sollte es eine `handleChange()` Methode auslösen. Außerdem sollte das `input`-Element den Wert von `input` wiedergeben, der im Zustand der Komponente vorhanden ist. Das `button`-Element sollte eine `submitMessage()` Methode auslösen, wenn es angeklickt wird.
+
+Zweitens: Schreibe diese beiden Methoden. Die `handleChange()` Methode sollte das `input` mit dem aktualisieren, was der Benutzer gerade eingibt. Die Methode `submitMessage()` sollte die aktuelle Nachricht (gespeichert in `input`) mit dem Array `messages` im lokalen Zustand verketten und den Wert von `input` löschen.
+
+Schließlich verwendest du `ul`, um ein Array von `messages` abzubilden und es als Liste von `li`-Elementen auf dem Bildschirm darzustellen.
+
+# --hints--
+
+Die Komponente `DisplayMessages` sollte mit einem Zustand initialisiert werden, der `{ input: "", messages: [] }` lautet.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const initialState = mockedComponent.state();
+ return (
+ typeof initialState === 'object' &&
+ initialState.input === '' &&
+ initialState.messages.length === 0
+ );
+ })()
+);
+```
+
+Die Komponente `DisplayMessages` sollte ein `div` darstellen, das ein `h2`-Element, ein `button`-Element, ein `ul`-Element und `li`-Elemente als Kindelemente enthält.
+
+```js
+() => {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const state = () => {
+ mockedComponent.setState({ messages: ['__TEST__MESSAGE'] });
+ return mockedComponent;
+ };
+ const updated = state();
+ assert(
+ updated.find('div').length === 1 &&
+ updated.find('h2').length === 1 &&
+ updated.find('button').length === 1 &&
+ updated.find('ul').length === 1 &&
+ updated.find('li').length > 0
+ );
+};
+```
+
+`.map` sollte für das `messages`-Array verwendet werden.
+
+```js
+assert(code.match(/this\.state\.messages\.map/g));
+```
+
+Das `input`-Element sollte den Wert von `input` im lokalen Zustand wiedergeben.
+
+```js
+() => {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const testValue = '__TEST__EVENT__INPUT';
+ const changed = () => {
+ causeChange(mockedComponent, testValue);
+ return mockedComponent;
+ };
+ const updated = changed();
+ assert(updated.find('input').props().value === testValue);
+};
+```
+
+Der Aufruf der Methode `handleChange` soll den `input`-Wert im Zustand auf den aktuellen Input aktualisieren.
+
+```js
+() => {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const initialState = mockedComponent.state();
+ const testMessage = '__TEST__EVENT__MESSAGE__';
+ const changed = () => {
+ causeChange(mockedComponent, testMessage);
+ return mockedComponent;
+ };
+ const afterInput = changed();
+ assert(
+ initialState.input === '' &&
+ afterInput.state().input === '__TEST__EVENT__MESSAGE__'
+ );
+};
+```
+
+Wenn du auf den Button `Add message` klickst, sollte die Methode `submitMessage` aufgerufen werden, die das aktuelle `input` zum `messages` Array im Zustand hinzufügen sollte.
+
+```js
+() => {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const initialState = mockedComponent.state();
+ const testMessage_1 = '__FIRST__MESSAGE__';
+ const firstChange = () => {
+ causeChange(mockedComponent, testMessage_1);
+ return mockedComponent;
+ };
+ const firstResult = firstChange();
+ const firstSubmit = () => {
+ mockedComponent.find('button').simulate('click');
+ return mockedComponent;
+ };
+ const afterSubmit_1 = firstSubmit();
+ const submitState_1 = afterSubmit_1.state();
+ const testMessage_2 = '__SECOND__MESSAGE__';
+ const secondChange = () => {
+ causeChange(mockedComponent, testMessage_2);
+ return mockedComponent;
+ };
+ const secondResult = secondChange();
+ const secondSubmit = () => {
+ mockedComponent.find('button').simulate('click');
+ return mockedComponent;
+ };
+ const afterSubmit_2 = secondSubmit();
+ const submitState_2 = afterSubmit_2.state();
+ assert(
+ initialState.messages.length === 0 &&
+ submitState_1.messages.length === 1 &&
+ submitState_2.messages.length === 2 &&
+ submitState_2.messages[1] === testMessage_2
+ );
+};
+```
+
+Die `submitMessage` Methode sollte das aktuelle Input leeren.
+
+```js
+() => {
+ const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
+ const causeChange = (c, v) =>
+ c.find('input').simulate('change', { target: { value: v } });
+ const initialState = mockedComponent.state();
+ const testMessage = '__FIRST__MESSAGE__';
+ const firstChange = () => {
+ causeChange(mockedComponent, testMessage);
+ return mockedComponent;
+ };
+ const firstResult = firstChange();
+ const firstState = firstResult.state();
+ const firstSubmit = () => {
+ mockedComponent.find('button').simulate('click');
+ return mockedComponent;
+ };
+ const afterSubmit = firstSubmit();
+ const submitState = afterSubmit.state();
+ assert(firstState.input === testMessage && submitState.input === '');
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class DisplayMessages extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ }
+ // Add handleChange() and submitMessage() methods here
+
+ render() {
+ return (
+
+
Type in a new Message:
+ { /* Render an input, button, and ul below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class DisplayMessages extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => {
+ const currentMessage = state.input;
+ return {
+ input: '',
+ messages: state.messages.concat(currentMessage)
+ };
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-dispatch-to-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-dispatch-to-props.md
new file mode 100644
index 00000000000..caf443b105c
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-dispatch-to-props.md
@@ -0,0 +1,107 @@
+---
+id: 5a24c314108439a4d4036146
+title: Versand Eigenschaften zuordnen
+challengeType: 6
+forumTopicId: 301432
+dashedName: map-dispatch-to-props
+---
+
+# --description--
+
+Die Funktion `mapDispatchToProps()` wird verwendet, um deinen React-Komponenten bestimmte Action Creator zur Verfügung zu stellen, damit sie Aktionen an den Redux-Store senden können. Sie ist ähnlich aufgebaut wie die `mapStateToProps()` Funktion, die du in der letzten Aufgabe geschrieben hast. Sie gibt ein Objekt zurück, das Versandaktionen den Eigenschaftsnamen zuordnet, die zu `props` der Komponente werden. Anstatt jedoch einen `state` zurückzugeben, gibt jede Eigenschaft eine Funktion zurück, die `dispatch` mit einem Action Creator und allen relevanten Aktionsdaten aufruft. Du hast Zugriff auf dieses `dispatch`, weil es als Parameter an `mapDispatchToProps()` übergeben wird, wenn du die Funktion definierst, genauso wie du `state` an `mapStateToProps()` übergeben hast. Hinter den Kulissen nutzt React Redux `store.dispatch()`, um diese Versendungen mit `mapDispatchToProps()` durchzuführen. Das ist ähnlich wie bei der Verwendung von `store.subscribe()` für Komponenten, die `state` zugeordnet sind.
+
+Du hast zum Beispiel einen `loginUser()` Action Creator, der einen `username` als Action Payload benötigt. Das Objekt, das von `mapDispatchToProps()` für diesen Action Creator zurückgegeben wird, würde etwa so aussehen:
+
+```jsx
+{
+ submitLoginUser: function(username) {
+ dispatch(loginUser(username));
+ }
+}
+```
+
+# --instructions--
+
+Der Code-Editor stellt einen Action Creator namens `addMessage()` zur Verfügung. Schreibe die Funktion `mapDispatchToProps()`, die `dispatch` als Argument nimmt und dann ein Objekt zurückgibt. Das Objekt sollte eine Eigenschaft `submitNewMessage` haben, die auf die Dispatch-Funktion gesetzt ist. Diese nimmt einen Parameter für die neue Nachricht, die hinzugefügt werden soll, wenn sie `addMessage()` versendet.
+
+# --hints--
+
+`addMessage` sollte ein Objekt mit den Schlüsseln `type` und `message` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const addMessageTest = addMessage();
+ return (
+ addMessageTest.hasOwnProperty('type') &&
+ addMessageTest.hasOwnProperty('message')
+ );
+ })()
+);
+```
+
+`mapDispatchToProps` sollte eine Funktion sein.
+
+```js
+assert(typeof mapDispatchToProps === 'function');
+```
+
+`mapDispatchToProps` sollte ein Objekt zurückgeben.
+
+```js
+assert(typeof mapDispatchToProps() === 'object');
+```
+
+Der Versand von `addMessage` mit `submitNewMessage` von `mapDispatchToProps` sollte eine Nachricht an die Dispatch-Funktion zurückgeben.
+
+```js
+assert(
+ (function () {
+ let testAction;
+ const dispatch = (fn) => {
+ testAction = fn;
+ };
+ let dispatchFn = mapDispatchToProps(dispatch);
+ dispatchFn.submitNewMessage('__TEST__MESSAGE__');
+ return (
+ testAction.type === 'ADD' && testAction.message === '__TEST__MESSAGE__'
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+const addMessage = (message) => {
+ return {
+ type: 'ADD',
+ message: message
+ }
+};
+
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+const addMessage = (message) => {
+ return {
+ type: 'ADD',
+ message: message
+ }
+};
+
+// Change code below this line
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ submitNewMessage: function(message) {
+ dispatch(addMessage(message));
+ }
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-state-to-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-state-to-props.md
new file mode 100644
index 00000000000..918836c1ce7
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/map-state-to-props.md
@@ -0,0 +1,69 @@
+---
+id: 5a24c314108439a4d4036145
+title: Zustand auf Eigenschaften übertragen
+challengeType: 6
+forumTopicId: 301433
+dashedName: map-state-to-props
+---
+
+# --description--
+
+Mit der Komponente `Provider` kannst du deinen React-Komponenten `state` und `dispatch` zur Verfügung stellen, aber du musst genau angeben, welchen Zustand und welche Aktionen du möchtest. Auf diese Weise stellst du sicher, dass jede Komponente nur auf den Zustand zugreifen kann, den sie braucht. Das erreichst du, indem du zwei Funktionen erstellst: `mapStateToProps()` und `mapDispatchToProps()`.
+
+In diesen Funktionen gibst du an, auf welche Teile des Zustands du Zugriff haben willst und welche Action Creator du versenden können musst. Sobald diese Funktionen eingerichtet sind, erfährst du in einer weiteren Aufgabe, wie du sie mit der React Redux `connect`-Methode mit deinen Komponenten verbinden kannst.
+
+**Hinweis:** Hinter den Kulissen verwendet React Redux die Methode `store.subscribe()`, um `mapStateToProps()` zu implementieren.
+
+# --instructions--
+
+Erstelle eine Funktion `mapStateToProps()`. Diese Funktion sollte `state` als Argument nehmen und dann ein Objekt zurückgeben, das den Zustand bestimmte Eigenschaftsnamen zuordnet. Diese Eigenschaften werden für deine Komponente über `props` zugänglich. Da in diesem Beispiel der gesamte Zustand der App in einem einzigen Array gespeichert wird, kannst du diesen gesamten Zustand an deine Komponente übergeben. Erstelle eine Eigenschaft `messages` in dem Objekt, das zurückgegeben wird, und setze sie auf `state`.
+
+# --hints--
+
+Die Konstante `state` sollte ein leeres Array sein.
+
+```js
+assert(Array.isArray(state) && state.length === 0);
+```
+
+`mapStateToProps` sollte eine Funktion sein.
+
+```js
+assert(typeof mapStateToProps === 'function');
+```
+
+`mapStateToProps` sollte ein Objekt zurückgeben.
+
+```js
+assert(typeof mapStateToProps() === 'object');
+```
+
+Die Übergabe eines Arrays als Zustand an `mapStateToProps` sollte dieses Array zurückgeben, das einem Schlüssel von `messages` zugeordnet ist.
+
+```js
+assert(mapStateToProps(['messages']).messages.pop() === 'messages');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+const state = [];
+
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+const state = [];
+
+// Change code below this line
+
+const mapStateToProps = (state) => {
+ return {
+ messages: state
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/moving-forward-from-here.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/moving-forward-from-here.md
new file mode 100644
index 00000000000..39ae531755d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/moving-forward-from-here.md
@@ -0,0 +1,69 @@
+---
+id: 5a24c314108439a4d403614a
+title: Von hier aus weitergehen
+challengeType: 6
+forumTopicId: 301434
+dashedName: moving-forward-from-here
+---
+
+# --description--
+
+Glückwunsch! Du hast die Lektionen über React und Redux abgeschlossen. Bevor du weitermachst, gibt es noch einen letzten Punkt, den du beachten solltest. Normalerweise schreibst du React-Apps nicht in einem Code-Editor wie diesem. Diese Aufgabe gibt dir einen Einblick, wie die Syntax aussieht, wenn du mit npm und einem Dateisystem auf deinem eigenen Rechner arbeitest. Der Code sollte ähnlich aussehen, bis auf die Verwendung von `import`-Anweisungen (diese ziehen alle Abhängigkeiten herein, die dir in den Aufgaben zur Verfügung gestellt wurden). Der Abschnitt "Pakete mit npm verwalten" geht näher auf npm ein.
+
+Schließlich erfordert das Schreiben von React- und Redux-Code in der Regel eine gewisse Konfiguration. Das kann schnell kompliziert werden. Wenn du auf deinem eigenen Rechner experimentieren möchtest, ist die
Create React App bereits konfiguriert und einsatzbereit.
+
+Alternativ kannst du Babel als JavaScript-Präprozessor in CodePen aktivieren, React und ReactDOM als externe JavaScript-Ressourcen hinzufügen und auch dort arbeiten.
+
+# --instructions--
+
+Logge die Meldung `'Now I know React and Redux!'` in der Konsole.
+
+# --hints--
+
+Die Meldung `Now I know React and Redux!` sollte in der Konsole geloggt werden.
+
+```js
+(getUserInput) =>
+ assert(
+ /console\s*\.\s*log\s*\(\s*('|"|`)Now I know React and Redux!\1\s*\)/.test(
+ getUserInput('index')
+ )
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+/*
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { Provider, connect } from 'react-redux'
+import { createStore, combineReducers, applyMiddleware } from 'redux'
+import thunk from 'redux-thunk'
+
+import rootReducer from './redux/reducers'
+import App from './components/App'
+
+const store = createStore(
+ rootReducer,
+ applyMiddleware(thunk)
+);
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+);
+*/
+
+// Only change code below this line
+```
+
+# --solutions--
+
+```jsx
+console.log('Now I know React and Redux!');
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/use-provider-to-connect-redux-to-react.md b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/use-provider-to-connect-redux-to-react.md
new file mode 100644
index 00000000000..00d17443217
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react-and-redux/use-provider-to-connect-redux-to-react.md
@@ -0,0 +1,263 @@
+---
+id: 5a24c314108439a4d4036144
+title: Provider verwenden, um Redux mit React zu verbinden
+challengeType: 6
+forumTopicId: 301435
+dashedName: use-provider-to-connect-redux-to-react
+---
+
+# --description--
+
+In der letzten Aufgabe hast du einen Redux-Store erstellt, um das messages-Array zu verwalten und eine Aktion zum Hinzufügen neuer Nachrichten zu erstellen. Der nächste Schritt besteht darin, React Zugriff auf den Redux-Store und die Aktionen zu geben, die es braucht, um Updates zu versenden. React Redux stellt sein `react-redux` Paket zur Verfügung, um diese Aufgaben zu bewältigen.
+
+React Redux bietet eine kleine API mit zwei wichtigen Funktionen: `Provider` und `connect`. Eine weitere Aufgabe befasst sich mit `connect`. Der `Provider` ist eine Wrapper-Komponente von React Redux, die deine React-App umhüllt. Dieser Wrapper ermöglicht dir dann den Zugriff auf die Redux-Funktionen `store` und `dispatch` in deinem Komponentenbaum. `Provider` nimmt zwei Eigenschaften(props) an, den Redux-Store und die Kindkomponenten deiner App. Die Definition des `Provider` für eine App-Komponente könnte so aussehen:
+
+```jsx
+
+
+
+```
+
+# --instructions--
+
+Der Code-Editor zeigt jetzt deinen gesamten Redux- und React-Code aus den letzten Aufgaben an. Es enthält den Redux Store, Aktionen und die Komponente `DisplayMessages`. Das einzige neue Element ist die Komponente `AppWrapper` im unteren Bereich. Verwende diese Top-Level-Komponente, um den `Provider` von `ReactRedux` zu rendern und den Redux-Store als Eigenschaft zu übergeben. Dann rendere die Komponente `DisplayMessages` als Kindelement. Sobald du fertig bist, solltest du sehen, wie deine React-Komponente auf der Seite gerendert wird.
+
+**Hinweis:** React Redux ist hier als globale Variable verfügbar, so dass du mit der Punktnotation auf den Provider zugreifen kannst. Der Code im Editor macht sich das zunutze und setzt ihn auf eine Konstante `Provider`, die du in der `AppWrapper` Rendering-Methode verwenden kannst.
+
+# --hints--
+
+Der `AppWrapper` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return mockedComponent.find('AppWrapper').length === 1;
+ })()
+);
+```
+
+Die `Provider` Wrapper-Komponente sollte eine Eigenschaft `store` besitzen, die dem Redux-Store entspricht.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return __helpers
+ .removeWhiteSpace(getUserInput('index'))
+ .includes('
');
+ })()
+ );
+```
+
+`DisplayMessages` sollte als Kindelement von `AppWrapper` dargestellt werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return (
+ mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1
+ );
+ })()
+);
+```
+
+Die Komponente `DisplayMessages` sollte ein `h2`, `input`, `button` und `ul`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
+ return (
+ mockedComponent.find('div').length === 1 &&
+ mockedComponent.find('h2').length === 1 &&
+ mockedComponent.find('button').length === 1 &&
+ mockedComponent.find('ul').length === 1
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+
+class DisplayMessages extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => {
+ const currentMessage = state.input;
+ return {
+ input: '',
+ messages: state.messages.concat(currentMessage)
+ };
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+
+const Provider = ReactRedux.Provider;
+
+class AppWrapper extends React.Component {
+ // Render the Provider below this line
+
+ // Change code above this line
+};
+```
+
+# --solutions--
+
+```jsx
+// Redux:
+const ADD = 'ADD';
+
+const addMessage = (message) => {
+ return {
+ type: ADD,
+ message
+ }
+};
+
+const messageReducer = (state = [], action) => {
+ switch (action.type) {
+ case ADD:
+ return [
+ ...state,
+ action.message
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(messageReducer);
+
+// React:
+
+class DisplayMessages extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ messages: []
+ }
+ this.handleChange = this.handleChange.bind(this);
+ this.submitMessage = this.submitMessage.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ submitMessage() {
+ this.setState((state) => {
+ const currentMessage = state.input;
+ return {
+ input: '',
+ messages: state.messages.concat(currentMessage)
+ };
+ });
+ }
+ render() {
+ return (
+
+
Type in a new Message:
+
+
Submit
+
+ {this.state.messages.map( (message, idx) => {
+ return (
+ {message}
+ )
+ })
+ }
+
+
+ );
+ }
+};
+
+const Provider = ReactRedux.Provider;
+
+class AppWrapper extends React.Component {
+ // Change code below this line
+ render() {
+ return (
+
+
+
+ );
+ }
+ // Change code above this line
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/access-props-using-this.props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/access-props-using-this.props.md
new file mode 100644
index 00000000000..ec15c43bd80
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/access-props-using-this.props.md
@@ -0,0 +1,148 @@
+---
+id: 5a24c314108439a4d403616e
+title: Zugriff auf Eigenschaften mit this.props
+challengeType: 6
+forumTopicId: 301375
+dashedName: access-props-using-this-props
+---
+
+# --description--
+
+In den letzten Aufgaben ging es um die grundlegenden Möglichkeiten, Eigenschaften an Kindkomponenten zu übergeben. Was aber, wenn die Kindkomponente, der du eine Eigenschaft übergibst, eine ES6-Klassenkomponente ist und nicht eine zustandslose funktionale Komponente? Die ES6-Klassenkomponente verwendet eine etwas andere Konvention für den Zugriff auf Eigenschaften.
+
+Immer wenn du dich auf eine Klassenkomponente innerhalb ihrer selbst beziehst, verwendest du das Schlüsselwort `this`. Um auf Eigenschaften innerhalb einer Klassenkomponente zuzugreifen, stellst du dem Code, mit dem du auf sie zugreifst, `this` voran. Wenn eine ES6-Klassenkomponente zum Beispiel eine Eigenschaft namens `data` hat, schreibst du `{this.props.data}` in JSX.
+
+# --instructions--
+
+Rendere eine Instanz der Komponente `Welcome` in der Elternkomponente `App`. Hier gibst du `Welcome` eine Eigenschaft `name` und weist ihm einen Wert eines Strings zu. Innerhalb des Kindes `Welcome` greifst du auf die Eigenschaft `name` innerhalb des `strong`-Tags zu.
+
+# --hints--
+
+Die Komponente `App` sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(App));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Das Kind von `App` sollte die Komponente `Welcome` sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(App));
+ return (
+ mockedComponent.children().childAt(0).name() === 'Welcome'
+ );
+ })()
+);
+```
+
+Die Komponente `Welcome` sollte eine Eigenschaft namens `name` besitzen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(App));
+ return mockedComponent.find('Welcome').props().name;
+ })()
+);
+```
+
+Die Komponente `Welcome` sollte den String, den du als Eigenschaft `name` übergibst, innerhalb des `strong`-Tags anzeigen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(App));
+ return (
+ mockedComponent.find('strong').text() ===
+ mockedComponent.find('Welcome').props().name
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+
+class Welcome extends React.Component {
+ constructor(props) {
+ super(props);
+
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
Hello, !
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class Welcome extends React.Component {
+ constructor(props) {
+ super(props);
+
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
Hello, {this.props.name} !
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/add-comments-in-jsx.md b/curriculum/challenges/german/03-front-end-development-libraries/react/add-comments-in-jsx.md
new file mode 100644
index 00000000000..a83b227b798
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/add-comments-in-jsx.md
@@ -0,0 +1,82 @@
+---
+id: 5a24bbe0dba28a8d3cbd4c5e
+title: Kommentare in JSX hinzufügen
+challengeType: 6
+forumTopicId: 301376
+dashedName: add-comments-in-jsx
+---
+
+# --description--
+
+JSX ist eine Syntax, die in gültiges JavaScript kompiliert wird. Manchmal musst du aus Gründen der Lesbarkeit Kommentare in deinen Code einfügen. Wie die meisten Programmiersprachen hat auch JSX seine eigene Art, dies zu tun.
+
+Um Kommentare in JSX einzufügen, verwendest du die Syntax `{/* */}`, um den Kommentartext einzuschließen.
+
+# --instructions--
+
+Der Code-Editor hat ein JSX-Element, das dem ähnelt, das du in der letzten Aufgabe erstellt hast. Füge irgendwo innerhalb des bereitgestellten `div`-Elements einen Kommentar hinzu, ohne die bestehenden `h1`- oder `p`-Elemente zu verändern.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `div`-Element zurückgeben.
+
+```js
+assert(JSX.type === 'div');
+```
+
+Das `div` sollte ein `h1`-Tag als erstes Element enthalten.
+
+```js
+assert(JSX.props.children[0].type === 'h1');
+```
+
+Das `div` sollte ein `p`-Tag als zweites Element enthalten.
+
+```js
+assert(JSX.props.children[1].type === 'p');
+```
+
+Die bestehenden `h1`- und `p`-Elemente sollten nicht verändert werden.
+
+```js
+assert(
+ JSX.props.children[0].props.children === 'This is a block of JSX' &&
+ JSX.props.children[1].props.children === "Here's a subtitle"
+);
+```
+
+Das `JSX` sollte eine gültige Kommentarsyntax verwenden.
+
+```js
+assert(/[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code));
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(JSX, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const JSX = (
+
+
This is a block of JSX
+
Here's a subtitle
+
+);
+```
+
+# --solutions--
+
+```jsx
+const JSX = (
+
+
This is a block of JSX
+ { /* this is a JSX comment */ }
+
Here's a subtitle
+
);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/add-event-listeners.md b/curriculum/challenges/german/03-front-end-development-libraries/react/add-event-listeners.md
new file mode 100644
index 00000000000..0655dfc20bb
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/add-event-listeners.md
@@ -0,0 +1,179 @@
+---
+id: 5a24c314108439a4d403617e
+title: Ereignis-Listener hinzufügen (Event Listener)
+challengeType: 6
+forumTopicId: 301377
+dashedName: add-event-listeners
+---
+
+# --description--
+
+Die Methode `componentDidMount()` ist auch der beste Ort, um Event-Listener hinzuzufügen, die du für bestimmte Funktionen brauchst. React bietet ein synthetisches Eventsystem, das das native Eventsystem des Browsers umhüllt. Das bedeutet, dass sich das synthetische Eventsystem unabhängig vom Browser des Nutzers genau gleich verhält - auch wenn sich die nativen Events in verschiedenen Browsern unterschiedlich verhalten können.
+
+Du hast bereits einige dieser synthetischen Event-Handler wie `onClick()` verwendet. Das synthetische Eventsystem von React ist ideal für die meisten Interaktionen, die du mit DOM-Elementen durchführst. Wenn du jedoch einen Event-Handler an die Dokument- oder Fensterobjekte anhängen möchtest, musst du dies direkt tun.
+
+# --instructions--
+
+Füge in der Methode `componentDidMount()` einen Ereignis-Listener für `keydown`-Events hinzu und lass diese Events den Callback `handleKeyPress()` auslösen. Du kannst `document.addEventListener()` verwenden, das den Event (in Anführungszeichen) als erstes Argument und den Callback als zweites Argument nimmt.
+
+Entferne dann in `componentWillUnmount()` denselben Event-Listener. Du kannst die gleichen Argumente an `document.removeEventListener()` übergeben. Es ist eine gute Praxis, diese Lifecycle-Methode zu verwenden, um React-Komponenten zu säubern, bevor sie demontiert und zerstört werden. Das Entfernen von Event-Listenern ist ein Beispiel für eine solche Aufräumaktion.
+
+# --hints--
+
+`MyComponent` soll ein `div`-Element darstellen, das ein `h1`-Tag umhüllt.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('div').children().find('h1').length === 1;
+ })()
+);
+```
+
+Ein `keydown`-Listener sollte an das Dokument in `componentDidMount` angehängt werden.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const didMountString = mockedComponent
+ .instance()
+ .componentDidMount.toString();
+ return new RegExp(
+ 'document.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
+ ).test(didMountString);
+ })()
+);
+```
+
+Der `keydown`-Listener sollte in `componentWillUnmount` aus dem Dokument entfernt werden.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const willUnmountString = mockedComponent
+ .instance()
+ .componentWillUnmount.toString();
+ return new RegExp(
+ 'document.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
+ ).test(willUnmountString);
+ })()
+);
+```
+
+Sobald die Komponente montiert ist, sollte das Drücken von `enter` ihren Zustand und das gerenderte `h1`-Tag aktualisieren.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const beforeState = mockedComponent.state('message');
+ const beforeText = mockedComponent.find('h1').text();
+ const pressEnterKey = () => {
+ mockedComponent.instance().handleKeyPress({ keyCode: 13 });
+ return waitForIt(() => {
+ mockedComponent.update();
+ return {
+ state: mockedComponent.state('message'),
+ text: mockedComponent.find('h1').text()
+ };
+ });
+ };
+ const afterKeyPress = await pressEnterKey();
+ assert(
+ beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text
+ );
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ message: ''
+ };
+ this.handleEnter = this.handleEnter.bind(this);
+ this.handleKeyPress = this.handleKeyPress.bind(this);
+ }
+ // Change code below this line
+ componentDidMount() {
+
+ }
+ componentWillUnmount() {
+
+ }
+ // Change code above this line
+ handleEnter() {
+ this.setState((state) => ({
+ message: state.message + 'You pressed the enter key! '
+ }));
+ }
+ handleKeyPress(event) {
+ if (event.keyCode === 13) {
+ this.handleEnter();
+ }
+ }
+ render() {
+ return (
+
+
{this.state.message}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ message: ''
+ };
+ this.handleKeyPress = this.handleKeyPress.bind(this);
+ this.handleEnter = this.handleEnter.bind(this); }
+ componentDidMount() {
+ // Change code below this line
+ document.addEventListener('keydown', this.handleKeyPress);
+ // Change code above this line
+ }
+ componentWillUnmount() {
+ // Change code below this line
+ document.removeEventListener('keydown', this.handleKeyPress);
+ // Change code above this line
+ }
+ handleEnter() {
+ this.setState((state) => ({
+ message: state.message + 'You pressed the enter key! '
+ }));
+ }
+ handleKeyPress(event) {
+ if (event.keyCode === 13) {
+ this.handleEnter();
+ }
+ }
+ render() {
+ return (
+
+
{this.state.message}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/add-inline-styles-in-react.md b/curriculum/challenges/german/03-front-end-development-libraries/react/add-inline-styles-in-react.md
new file mode 100644
index 00000000000..abb69be4212
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/add-inline-styles-in-react.md
@@ -0,0 +1,112 @@
+---
+id: 5a24c314108439a4d4036182
+title: Inline-Stile in React hinzufügen
+challengeType: 6
+forumTopicId: 301378
+dashedName: add-inline-styles-in-react
+---
+
+# --description--
+
+In der letzten Aufgabe hast du vielleicht bemerkt, dass es neben dem `style`-Attribut, das auf ein JavaScript-Objekt gesetzt wird, noch einige andere Syntaxunterschiede zu HTML-Inline-Stilen gibt. Erstens verwenden die Namen bestimmter CSS-Stileigenschaften die Groß- und Kleinschreibung. In der letzten Aufgabe hast du zum Beispiel die Größe der Schrift mit `fontSize` statt `font-size` festgelegt. Wörter mit Bindestrich wie `font-size` sind eine ungültige Syntax für JavaScript-Objekteigenschaften, deshalb verwendet React die Groß- und Kleinschreibung. In der Regel werden alle Stileigenschaften mit Bindestrich in JSX in Camel Case geschrieben.
+
+Alle Längeneinheiten der Eigenschaftswerte (wie `height`, `width` und `fontSize`) werden, wenn nicht anders angegeben, in `px` angenommen. Wenn du z. B. `em` verwenden willst, verpackst du den Wert und die Einheiten in Anführungszeichen, wie `{fontSize: "4em"}`. Abgesehen von den Längenwerten, die standardmäßig `px` lauten, sollten alle anderen Eigenschaftswerte in Anführungszeichen eingeschlossen werden.
+
+# --instructions--
+
+Wenn du eine große Anzahl von Stilen hast, kannst du ein Style-`object` einer Konstante zuweisen, um deinen Code zu organisieren. Deklariere deine Stilkonstante als globale Variable am Anfang der Datei. Initialisiere die Konstante `styles` und weise ihr ein `object` mit drei Stileigenschaften und deren Werten zu. Gib dem `div` die Farbe `purple`, eine Schriftgröße von `40` und einen Rahmen von `2px solid purple`. Dann setze das `style`-Attribut gleich der `styles`-Konstante.
+
+# --hints--
+
+Die `styles`-Variable sollte ein `object` mit drei Eigenschaften sein.
+
+```js
+assert(Object.keys(styles).length === 3);
+```
+
+Die `styles`-Variable sollte eine `color`-Eigenschaft haben, die auf den Wert `purple` gesetzt ist.
+
+```js
+assert(styles.color === 'purple');
+```
+
+Die `styles`-Variable sollte eine `fontSize`-Eigenschaft besitzen, die auf den Wert `40` gesetzt ist.
+
+```js
+assert(styles.fontSize == 40);
+```
+
+Die `styles`-Variable sollte eine `border`-Eigenschaft besitzen, die auf den Wert `2px solid purple` gesetzt ist.
+
+```js
+assert(styles.border === '2px solid purple');
+```
+
+Die Komponente sollte ein `div`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
+ return mockedComponent.type() === 'div';
+ })()
+);
+```
+
+Das `div`-Element sollte seine Stile durch das `styles`-Objekt definieren lassen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
+ return (
+ mockedComponent.props().style.color === 'purple' &&
+ mockedComponent.props().style.fontSize == 40 &&
+ mockedComponent.props().style.border === '2px solid purple'
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+// Change code above this line
+class Colorful extends React.Component {
+ render() {
+ // Change code below this line
+ return (
+
Style Me!
+ );
+ // Change code above this line
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const styles = {
+ color: "purple",
+ fontSize: 40,
+ border: "2px solid purple"
+};
+// Change code above this line
+class Colorful extends React.Component {
+ render() {
+ // Change code below this line
+ return (
+
Style Me!
+ );
+ // Change code above this line
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/bind-this-to-a-class-method.md b/curriculum/challenges/german/03-front-end-development-libraries/react/bind-this-to-a-class-method.md
new file mode 100644
index 00000000000..93f3aa7f823
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/bind-this-to-a-class-method.md
@@ -0,0 +1,136 @@
+---
+id: 5a24c314108439a4d4036174
+title: Binde "this" an eine Klassenmethode
+challengeType: 6
+forumTopicId: 301379
+dashedName: bind-this-to-a-class-method
+---
+
+# --description--
+
+Neben dem Setzen und Aktualisieren des `state`, kannst du auch Methoden für deine Komponentenklasse definieren. Eine Klassenmethode muss normalerweise das Schlüsselwort `this` verwenden, damit sie auf Eigenschaften der Klasse (wie `state` und `props`) innerhalb des Geltungsbereichs der Methode zugreifen kann. Es gibt ein paar Möglichkeiten, den Methoden deiner Klasse den Zugriff auf `this` zu ermöglichen.
+
+Eine gängige Methode ist, `this` explizit im Konstruktor zu verknüpfen, so dass `this` mit den Methoden der Klasse verbunden wird, wenn die Komponente initialisiert wird. Du hast vielleicht bemerkt, dass die letzte Aufgabe `this.handleClick = this.handleClick.bind(this)` für ihre Methode `handleClick` im Konstruktor verwendet. Wenn du dann eine Funktion wie `this.setState()` innerhalb deiner Klassenmethode aufrufst, bezieht sich `this` auf die Klasse und ist nicht `undefined`.
+
+**Hinweis:** Das `this`-Schlüsselwort ist einer der verwirrendsten Aspekte von JavaScript, aber es spielt eine wichtige Rolle in React. Obwohl das Verhalten hier völlig normal ist, sind diese Aufgaben nicht der richtige Ort, um `this` ausführlich zu besprechen, also schau bitte in anderen Aufgaben nach, wenn das oben genannte verwirrend ist!
+
+# --instructions--
+
+Der Code-Editor hat eine Komponente mit einem `state`, die den Text im Auge behält. Sie hat auch eine Methode, mit der du den Text auf `You clicked!` setzen kannst. Die Methode funktioniert jedoch nicht, weil sie das Schlüsselwort `this` verwendet, das undefiniert (undefined) ist. Behebe es, indem du `this` explizit an die Methode `handleClick()` im Konstruktor der Komponente bindest.
+
+Als Nächstes fügst du dem `button`-Element in der Render-Methode einen Click-Handler hinzu. Er sollte die Methode `handleClick()` auslösen, wenn der Button ein Klickereignis erhält. Denke daran, dass die Methode, die du an den `onClick`-Handler übergibst, geschweifte Klammern braucht, weil sie direkt als JavaScript interpretiert werden soll.
+
+Sobald du die oben genannten Schritte abgeschlossen hast, solltest du auf den Button klicken können und `You clicked!` sehen.
+
+# --hints--
+
+`MyComponent` sollte ein `div`-Element zurückgeben, das zwei Elemente umhüllt, einen Button und ein `h1`-Element, in dieser Reihenfolge.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 &&
+ Enzyme.mount(React.createElement(MyComponent))
+ .find('div')
+ .childAt(0)
+ .type() === 'button' &&
+ Enzyme.mount(React.createElement(MyComponent))
+ .find('div')
+ .childAt(1)
+ .type() === 'h1'
+);
+```
+
+Der Zustand von `MyComponent` sollte mit dem Schlüssel-Wert-Paar `{ text: "Hello" }` initialisiert werden.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyComponent)).state('text') === 'Hello'
+);
+```
+
+Wenn du auf das `button`-Element klickst, sollte die `handleClick`-Methode ausgeführt werden und der state `text` auf `You clicked!` gesetzt werden.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ text: 'Hello' });
+ return waitForIt(() => mockedComponent.state('text'));
+ };
+ const second = () => {
+ mockedComponent.find('button').simulate('click');
+ return waitForIt(() => mockedComponent.state('text'));
+ };
+ const firstValue = await first();
+ const secondValue = await second();
+ assert(firstValue === 'Hello' && secondValue === 'You clicked!');
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ text: "Hello"
+ };
+ // Change code below this line
+
+ // Change code above this line
+ }
+ handleClick() {
+ this.setState({
+ text: "You clicked!"
+ });
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+ Click Me
+ { /* Change code above this line */ }
+
{this.state.text}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ text: "Hello"
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ this.setState({
+ text: "You clicked!"
+ });
+ }
+ render() {
+ return (
+
+ Click Me
+
{this.state.text}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/change-inline-css-conditionally-based-on-component-state.md b/curriculum/challenges/german/03-front-end-development-libraries/react/change-inline-css-conditionally-based-on-component-state.md
new file mode 100644
index 00000000000..e9c972be000
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/change-inline-css-conditionally-based-on-component-state.md
@@ -0,0 +1,176 @@
+---
+id: 5a24c314108439a4d4036189
+title: Inline-CSS bedingungsabhängig vom Komponentenzustand ändern
+challengeType: 6
+forumTopicId: 301380
+dashedName: change-inline-css-conditionally-based-on-component-state
+---
+
+# --description--
+
+Bis jetzt hast du mehrere Anwendungen des bedingungsabhängigen Renderings und die Verwendung von Inline-Styles gesehen. Hier ist ein weiteres Beispiel, das diese beiden Themen kombiniert. Du kannst CSS auch abhängig vom Zustand einer React-Komponente rendern. Dazu überprüfst du eine Bedingung, und wenn diese erfüllt ist, änderst du das Styles-Objekt, das den JSX-Elementen in der Render-Methode zugewiesen wird.
+
+Dieses Paradigma ist wichtig zu verstehen, denn es ist eine drastische Veränderung gegenüber dem traditionellen Ansatz der Anwendung von Stilen durch die direkte Änderung von DOM-Elementen (was zum Beispiel bei jQuery sehr verbreitet ist). Bei diesem Ansatz musst du verfolgen, wann sich Elemente ändern und die eigentliche Manipulation direkt vornehmen. Es kann schwierig werden, den Überblick über Änderungen zu behalten, was deine Benutzeroberfläche (UI) unberechenbar machen kann. Wenn du ein Style-Objekt basierend auf einer Bedingung festlegst, beschreibst du, wie die Benutzeroberfläche in Abhängigkeit vom Zustand der Anwendung aussehen soll. Es gibt einen klaren Informationsfluss, der nur in eine Richtung geht. Dies ist die bevorzugte Methode, wenn du Anwendungen mit React schreibst.
+
+# --instructions--
+
+Der Code-Editor hat eine einfache kontrollierte Eingabekomponente mit einem gestylten Rahmen. Du möchtest diesen Rahmen rot gestalten, wenn der Benutzer mehr als 15 Zeichen Text in das Eingabefeld eingibt. Füge eine Bedingung hinzu, um dies zu überprüfen und setze, wenn die Bedingung gültig ist, den Rahmenstil der Eingabe auf `3px solid red`. Du kannst es ausprobieren, indem du Text in das Eingabefeld eingibst.
+
+# --hints--
+
+Die Komponente `GateKeeper` sollte ein `div`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
+ return mockedComponent.find('div').length === 1;
+ })()
+);
+```
+
+Die Komponente `GateKeeper` sollte mit einem Zustandsschlüssel `input` initialisiert werden, der auf einen leeren String gesetzt ist.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
+ return mockedComponent.state().input === '';
+ })()
+);
+```
+
+Die Komponente `GateKeeper` sollte ein `h3`-Tag und ein `input`-Tag darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
+ return (
+ mockedComponent.find('h3').length === 1 &&
+ mockedComponent.find('input').length === 1
+ );
+ })()
+);
+```
+
+Das `input`-Tag sollte anfangs einen Stil von `1px solid black` für die Eigenschaft `border` besitzen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
+ return (
+ mockedComponent.find('input').props().style.border === '1px solid black'
+ );
+ })()
+);
+```
+
+Das `input`-Tag sollte mit einem Rahmen von `3px solid red` gestaltet werden, wenn der Eingabewert im Zustand länger als 15 Zeichen ist.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
+ const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
+ const simulateChange = (el, value) =>
+ el.simulate('change', { target: { value } });
+ let initialStyle = mockedComponent.find('input').props().style.border;
+ const state_1 = () => {
+ mockedComponent.setState({ input: 'this is 15 char' });
+ return waitForIt(() => mockedComponent.find('input').props().style.border);
+ };
+ const state_2 = () => {
+ mockedComponent.setState({
+ input: 'A very long string longer than 15 characters.'
+ });
+ return waitForIt(() => mockedComponent.find('input').props().style.border);
+ };
+ const style_1 = await state_1();
+ const style_2 = await state_2();
+ assert(
+ initialStyle === '1px solid black' &&
+ style_1 === '1px solid black' &&
+ style_2 === '3px solid red'
+ );
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class GateKeeper extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(event) {
+ this.setState({ input: event.target.value })
+ }
+ render() {
+ let inputStyle = {
+ border: '1px solid black'
+ };
+ // Change code below this line
+
+ // Change code above this line
+ return (
+
+
Don't Type Too Much:
+
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class GateKeeper extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(event) {
+ this.setState({ input: event.target.value })
+ }
+ render() {
+ let inputStyle = {
+ border: '1px solid black'
+ };
+ if (this.state.input.length > 15) {
+ inputStyle.border = '3px solid red';
+ };
+ return (
+
+
Don't Type Too Much:
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/compose-react-components.md b/curriculum/challenges/german/03-front-end-development-libraries/react/compose-react-components.md
new file mode 100644
index 00000000000..086fcadf5f3
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/compose-react-components.md
@@ -0,0 +1,199 @@
+---
+id: 5a24c314108439a4d4036166
+title: React-Komponenten zusammenstellen
+challengeType: 6
+forumTopicId: 301381
+dashedName: compose-react-components
+---
+
+# --description--
+
+Da die Aufgaben immer komplexere Kompositionen mit React-Komponenten und JSX verwenden, gibt es einen wichtigen Punkt zu beachten. Das Rendern von Klassenkomponenten im ES6-Stil innerhalb anderer Komponenten unterscheidet sich nicht vom Rendern der einfachen Komponenten, die du in den letzten Aufgaben verwendet hast. Du kannst JSX-Elemente, zustandslose funktionale Komponenten und ES6-Klassenkomponenten innerhalb anderer Komponenten darstellen.
+
+# --instructions--
+
+Im Code-Editor rendert die Komponente `TypesOfFood` bereits eine Komponente namens `Vegetables`. Außerdem gibt es noch die Komponente `Fruits` aus der letzten Aufgabe.
+
+Verschachtle zwei Komponenten innerhalb von `Fruits` - zuerst `NonCitrus`, und dann `Citrus`. Diese beiden Komponenten werden hinter den Kulissen für dich bereitgestellt. Als Nächstes verschachtelst du die Klassenkomponente `Fruits` in die Komponente `TypesOfFood`, unter dem `h1`-Überschriftenelement und über `Vegetables`. Das Ergebnis sollte eine Reihe von verschachtelten Komponenten sein, die zwei verschiedene Komponententypen verwenden.
+
+# --hints--
+
+Die Komponente `TypesOfFood` sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Die Komponente `TypesOfFood` sollte die Komponente `Fruits` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().childAt(1).name() === 'Fruits';
+ })()
+);
+```
+
+Die Komponente `Fruits` sollte die Komponente `NonCitrus` und die Komponente `Citrus` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return (
+ mockedComponent.find('Fruits').children().find('NonCitrus').length ===
+ 1 &&
+ mockedComponent.find('Fruits').children().find('Citrus').length === 1
+ );
+ })()
+);
+```
+
+Die Komponente `TypesOfFood` sollte die Komponente `Vegetables` unterhalb der Komponente `Fruits` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().childAt(2).name() === 'Vegetables';
+ })()
+);
+```
+
+# --seed--
+
+## --before-user-code--
+
+```jsx
+class NonCitrus extends React.Component {
+ render() {
+ return (
+
+
Non-Citrus:
+
+ Apples
+ Blueberries
+ Strawberries
+ Bananas
+
+
+ );
+ }
+};
+class Citrus extends React.Component {
+ render() {
+ return (
+
+
Citrus:
+
+ Lemon
+ Lime
+ Orange
+ Grapefruit
+
+
+ );
+ }
+};
+class Vegetables extends React.Component {
+ render() {
+ return (
+
+
Vegetables:
+
+ Brussel Sprouts
+ Broccoli
+ Squash
+
+
+ );
+ }
+};
+```
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class Fruits extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Fruits:
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Types of Food:
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class Fruits extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Fruits:
+ { /* Change code below this line */ }
+
+
+ { /* Change code above this line */ }
+
+ )
+ }
+}
+
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Types of Food:
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-complex-jsx-element.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-complex-jsx-element.md
new file mode 100644
index 00000000000..4434c13487d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-complex-jsx-element.md
@@ -0,0 +1,110 @@
+---
+id: 5a24bbe0dba28a8d3cbd4c5d
+title: Ein komplexes JSX-Element erstellen
+challengeType: 6
+forumTopicId: 301382
+dashedName: create-a-complex-jsx-element
+---
+
+# --description--
+
+Die letzte Aufgabe war ein einfaches Beispiel für JSX, aber JSX kann auch komplexeres HTML darstellen.
+
+Eine wichtige Sache, die du über verschachtelte JSX wissen musst, ist, dass sie ein einzelnes Element zurückgeben müssen.
+
+Dieses eine Elternelement würde alle anderen Ebenen der verschachtelten Elemente umschließen.
+
+Zum Beispiel werden mehrere JSX-Elemente, die als Geschwister ohne elterliches Wrapper-Element geschrieben wurden, nicht transpiliert.
+
+Hier ist ein Beispiel:
+
+**Gültiges JSX:**
+
+```jsx
+
+
Paragraph One
+
Paragraph Two
+
Paragraph Three
+
+```
+
+**Ungültiges JSX:**
+
+```jsx
+
Paragraph One
+
Paragraph Two
+
Paragraph Three
+```
+
+# --instructions--
+
+Definiere eine neue Konstante `JSX`, die ein `div` rendert, das die folgenden Elemente in dieser Reihenfolge enthält:
+
+Ein `h1`, ein `p` und eine ungeordnete Liste, die drei `li` Elemente enthält. Du kannst jeden beliebigen Text in jedes Element einfügen.
+
+**Hinweis:** Wenn du mehrere Elemente wie dieses darstellst, kannst du sie alle in Klammern einschließen, aber das ist nicht unbedingt erforderlich. Beachte auch, dass diese Aufgabe ein `div`-Tag verwendet, um alle Kindelemente in einem einzigen Elternelement zu verpacken. Wenn du das `div` entfernst, wird das JSX nicht mehr transpiliert. Behalte dies im Hinterkopf, denn es gilt auch, wenn du JSX-Elemente in React-Komponenten zurückgibst.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `div`-Element zurückgeben.
+
+```js
+assert(JSX.type === 'div');
+```
+
+Das `div` sollte ein `h1`-Tag als erstes Element enthalten.
+
+```js
+assert(JSX.props.children[0].type === 'h1');
+```
+
+Das `div` sollte ein `p`-Tag als zweites Element enthalten.
+
+```js
+assert(JSX.props.children[1].type === 'p');
+```
+
+Das `div` sollte ein `ul`-Tag als drittes Element enthalten.
+
+```js
+assert(JSX.props.children[2].type === 'ul');
+```
+
+Das `ul` sollte drei `li`-Elemente enthalten.
+
+```js
+assert(
+ JSX.props.children
+ .filter((ele) => ele.type === 'ul')[0]
+ .props.children.filter((ele) => ele.type === 'li').length === 3
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(JSX, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+
+```
+
+# --solutions--
+
+```jsx
+const JSX = (
+
+
Hello JSX!
+
Some info
+
+ An item
+ Another item
+ A third item
+
+
);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-component-with-composition.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-component-with-composition.md
new file mode 100644
index 00000000000..747218c8da6
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-component-with-composition.md
@@ -0,0 +1,134 @@
+---
+id: 5a24c314108439a4d4036164
+title: Eine Komponente mit Komposition erstellen
+challengeType: 6
+forumTopicId: 301383
+dashedName: create-a-component-with-composition
+---
+
+# --description--
+
+Jetzt schauen wir uns an, wie wir mehrere React-Komponenten zusammensetzen können. Stell dir vor, du baust eine App und hast drei Komponenten erstellt: eine `Navbar`, `Dashboard` und `Footer`.
+
+Um diese Komponenten zusammenzustellen, könntest du eine `App` *Elternkomponente* erstellen, die jede dieser drei Komponenten als *Kinderkomponente* wiedergibt. Um eine Komponente als Kind in einer React-Komponente darzustellen, fügst du den Komponentennamen als benutzerdefinierten HTML-Tag in die JSX ein. Zum Beispiel könntest du in der Methode `render` schreiben:
+
+```jsx
+return (
+
+
+
+
+
+)
+```
+
+Wenn React auf ein benutzerdefiniertes HTML-Tag stößt, das auf eine andere Komponente verweist (ein Komponentenname, der wie in diesem Beispiel in `< />` eingeschlossen ist), wird das Markup für diese Komponente an der Stelle des Tags gerendert. Dies soll die Eltern/Kind-Beziehung zwischen der `App`-Komponente und der `Navbar`, dem `Dashboard` und dem `Footer` verdeutlichen.
+
+# --instructions--
+
+Im Code-Editor gibt es eine einfache funktionale Komponente namens `ChildComponent` und eine Klassenkomponente namens `ParentComponent`. Setze die beiden zusammen, indem du die Kinderkomponente (`ChildComponent`) innerhalb der Elternkomponente (`ParentComponent`) renderst. Achte darauf, dass du den `ChildComponent`-Tag mit einem Schrägstrich schließt.
+
+**Hinweis:** `ChildComponent` wird mit einer ES6-Pfeilfunktion (arrow function) definiert, weil dies eine sehr gängige Praxis bei der Verwendung von React ist. Du solltest aber wissen, dass dies nur eine Funktion ist. Wenn du mit der Syntax der Pfeilfunktion nicht vertraut bist, sieh dir bitte den Abschnitt über JavaScript an.
+
+# --hints--
+
+Die React-Komponente sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
+ return shallowRender.type() === 'div';
+ })()
+);
+```
+
+Die Komponente sollte zwei verschachtelte Elemente zurückgeben.
+
+```js
+assert(
+ (function () {
+ var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
+ return shallowRender.children().length === 2;
+ })()
+);
+```
+
+Die Komponente sollte die `ChildComponent` als ihr zweites Kind zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ParentComponent));
+ return (
+ mockedComponent.find('ParentComponent').find('ChildComponent').length ===
+ 1
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const ChildComponent = () => {
+ return (
+
+ );
+};
+
+class ParentComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
I am the parent
+ { /* Change code below this line */ }
+
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const ChildComponent = () => {
+ return (
+
+ );
+};
+
+class ParentComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
I am the parent
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-form.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-form.md
new file mode 100644
index 00000000000..2d4fe879ec2
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-form.md
@@ -0,0 +1,220 @@
+---
+id: 5a24c314108439a4d4036179
+title: Ein kontrolliertes Formular erstellen
+challengeType: 6
+forumTopicId: 301384
+dashedName: create-a-controlled-form
+---
+
+# --description--
+
+Die letzte Aufgabe hat gezeigt, dass React den internen Zustand für bestimmte Elemente wie `input` und `textarea` kontrollieren kann, was sie zu kontrollierten Komponenten macht. Das gilt auch für andere Formularelemente, einschließlich des normalen HTML `form`-Elements.
+
+# --instructions--
+
+Die Komponente `MyForm` wird mit einem leeren `form` mit einem Submit-Handler eingerichtet. Der Submit-Handler wird aufgerufen, wenn das Formular abgeschickt wird.
+
+Wir haben einen Button hinzugefügt, mit dem du das Formular abschicken kannst. Du kannst sehen, dass der `type` auf `submit` gesetzt ist, was bedeutet, dass es der Button ist, der das Formular steuert. Füge das `input`-Element in das `form` ein und setze seine Attribute `value` und `onChange()` wie in der letzten Aufgabe. Du solltest dann die Methode `handleSubmit` so ergänzen, dass sie die Komponenteneigenschaft `submit` auf den aktuellen Eingabewert im lokalen `state` setzt.
+
+**Hinweis:** Du musst auch `event.preventDefault()` im Submit-Handler aufrufen, um zu verhindern, dass das Formular standardmäßig abgeschickt wird und die Webseite aktualisiert wird. Der Einfachheit halber wurde das Standardverhalten hier deaktiviert, um zu verhindern, dass Updates den Aufgabencode zurücksetzen.
+
+Schließlich erstellst du einen `h1`-Tag nach dem `form`, der den `submit`-Wert aus dem `state` der Komponente wiedergibt. Du kannst dann das Formular eintippen und auf die Schaltfläche klicken (oder die Eingabetaste drücken), und deine Eingaben sollten auf der Seite angezeigt werden.
+
+# --hints--
+
+`MyForm` sollte ein `div`-Element zurückgeben, das ein `form` und ein `h1`-Tag enthält. Das Formular sollte ein `input` und einen `button` enthalten.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyForm));
+ return (
+ mockedComponent.find('div').children().find('form').length === 1 &&
+ mockedComponent.find('div').children().find('h1').length === 1 &&
+ mockedComponent.find('form').children().find('input').length === 1 &&
+ mockedComponent.find('form').children().find('button').length === 1
+ );
+ })()
+);
+```
+
+Der Zustand von `MyForm` sollte mit den Eigenschaften von `input` und `submit` initialisiert werden, die beide auf leere Strings gesetzt sind.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyForm)).state('input') === '' &&
+ Enzyme.mount(React.createElement(MyForm)).state('submit') === ''
+);
+```
+
+Die Eingabe des `input`-Elements sollte die `input`-Eigenschaft des Zustands der Komponente aktualisieren.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyForm));
+ const _1 = () => {
+ mockedComponent.setState({ input: '' });
+ return mockedComponent.state('input');
+ };
+ const _2 = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: 'TestInput' } });
+ return {
+ state: mockedComponent.state('input'),
+ inputVal: mockedComponent.find('input').props().value
+ };
+ };
+ const before = _1();
+ const after = _2();
+ assert(
+ before === '' &&
+ after.state === 'TestInput' &&
+ after.inputVal === 'TestInput'
+ );
+})();
+```
+
+Beim Absenden des Formulars sollte `handleSubmit` ausgeführt werden, das die `submit`-Eigenschaft auf den Zustand der aktuellen Eingabe setzt.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyForm));
+ const _1 = () => {
+ mockedComponent.setState({ input: '' });
+ mockedComponent.setState({ submit: '' });
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: 'SubmitInput' } });
+ return mockedComponent.state('submit');
+ };
+ const _2 = () => {
+ mockedComponent.find('form').simulate('submit');
+ return mockedComponent.state('submit');
+ };
+ const before = _1();
+ const after = _2();
+ assert(before === '' && after === 'SubmitInput');
+})();
+```
+
+`handleSubmit` sollte `event.preventDefault` aufrufen
+
+```js
+assert(
+ __helpers.isCalledWithNoArgs(
+ 'event.preventDefault',
+ MyForm.prototype.handleSubmit.toString()
+ )
+);
+```
+
+Das `h1`-Überschriftenelement sollte den Wert des `submit`-Feldes aus dem Zustand der Komponente wiedergeben.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyForm));
+ const _1 = () => {
+ mockedComponent.setState({ input: '' });
+ mockedComponent.setState({ submit: '' });
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: 'TestInput' } });
+ return mockedComponent.find('h1').text();
+ };
+ const _2 = () => {
+ mockedComponent.find('form').simulate('submit');
+ return mockedComponent.find('h1').text();
+ };
+ const before = _1();
+ const after = _2();
+ assert(before === '' && after === 'TestInput');
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class MyForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ submit: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ handleSubmit(event) {
+ // Change code below this line
+
+ // Change code above this line
+ }
+ render() {
+ return (
+
+
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+class MyForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: '',
+ submit: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ handleSubmit(event) {
+ event.preventDefault();
+ this.setState(state => ({
+ submit: state.input
+ }));
+ }
+ render() {
+ return (
+
+
+
{this.state.submit}
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-input.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-input.md
new file mode 100644
index 00000000000..d7f7b352988
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-controlled-input.md
@@ -0,0 +1,150 @@
+---
+id: 5a24c314108439a4d4036178
+title: Erstelle ein kontrolliertes Eingabefeld
+challengeType: 6
+forumTopicId: 301385
+dashedName: create-a-controlled-input
+---
+
+# --description--
+
+Deine Anwendung kann komplexere Interaktionen zwischen dem `state` und der gerenderten Benutzeroberfläche haben. Formular-Steuerelemente für die Texteingabe, wie `input` und `textarea`, behalten ihren eigenen Status im DOM, wenn der Benutzer tippt. Mit React kannst du diesen veränderbaren Zustand in den `state` einer React-Komponente verschieben. Die Eingaben des Nutzers werden Teil des `state` der Anwendung, sodass React den Wert des Eingabefeldes kontrolliert. Wenn du React-Komponenten mit Eingabefeldern hast, in die der Nutzer etwas eingeben kann, handelt es sich in der Regel um ein kontrolliertes Eingabeformular.
+
+# --instructions--
+
+Der Code-Editor hat das Skelett einer Komponente namens `ControlledInput`, um ein kontrolliertes `input`-Element zu erstellen. Der `state` der Komponente ist bereits mit einer `input`-Eigenschaft initialisiert, die einen leeren String enthält. Dieser Wert stellt den Text dar, den ein Benutzer in das Eingabefeld (`input`) eingibt.
+
+Als erstes erstellst du eine Methode namens `handleChange()`, die einen Parameter namens `event` besitzt. Wenn die Methode aufgerufen wird, erhält sie ein `event`-Objekt, das einen String mit Text aus dem `input`-Element enthält. Auf diesen String kannst du mit `event.target.value` innerhalb der Methode zugreifen. Aktualisiere die `input`-Eigenschaft des `state` der Komponente mit diesem neuen String.
+
+In der `render`-Methode erstellst du das `input`-Element über dem `h4`-Tag. Füge ein `value`-Attribut hinzu, das gleich der `input`-Eigenschaft des `state` der Komponente ist. Dann füge einen `onChange()` Event-Handler hinzu, der auf die `handleChange()`-Methode gesetzt wird.
+
+Wenn du in das Eingabefeld tippst, wird dieser Text von der `handleChange()`-Methode verarbeitet, als `input`-Eigenschaft im lokalen `state` gesetzt und als Wert im `input`-Feld auf der Seite dargestellt. Der `state` der Komponente ist die einzige Quelle der Wahrheit über die Eingabedaten.
+
+Zu guter Letzt vergiss nicht, die notwendigen Bindungen im Konstruktor hinzuzufügen.
+
+# --hints--
+
+`ControlledInput` sollte ein `div`-Element zurückgeben, das ein `input` und ein `p`-Tag enthält.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(ControlledInput))
+ .find('div')
+ .children()
+ .find('input').length === 1 &&
+ Enzyme.mount(React.createElement(ControlledInput))
+ .find('div')
+ .children()
+ .find('p').length === 1
+);
+```
+
+Der Zustand von `ControlledInput` sollte mit einer `input`-Eigenschaft initialisiert werden, die auf einen leeren String gesetzt ist.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(ControlledInput)).state('input'),
+ ''
+);
+```
+
+Wenn du in das Eingabeelement tippst, sollten der Zustand und der Wert der Eingabe aktualisiert werden und das `p`-Element sollte diesen Zustand während der Eingabe wiedergeben.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(ControlledInput));
+ const _1 = () => {
+ mockedComponent.setState({ input: '' });
+ return waitForIt(() => mockedComponent.state('input'));
+ };
+ const _2 = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: 'TestInput' } });
+ return waitForIt(() => ({
+ state: mockedComponent.state('input'),
+ text: mockedComponent.find('p').text(),
+ inputVal: mockedComponent.find('input').props().value
+ }));
+ };
+ const before = await _1();
+ const after = await _2();
+ assert(
+ before === '' &&
+ after.state === 'TestInput' &&
+ after.text === 'TestInput' &&
+ after.inputVal === 'TestInput'
+ );
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class ControlledInput extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: ''
+ };
+ // Change code below this line
+
+ // Change code above this line
+ }
+ // Change code below this line
+
+ // Change code above this line
+ render() {
+ return (
+
+ { /* Change code below this line */}
+
+ { /* Change code above this line */}
+
Controlled Input:
+
{this.state.input}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class ControlledInput extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ input: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ input: event.target.value
+ });
+ }
+ render() {
+ return (
+
+
+
Controlled Input:
+
+
{this.state.input}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-react-component.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-react-component.md
new file mode 100644
index 00000000000..5e55e36c253
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-react-component.md
@@ -0,0 +1,102 @@
+---
+id: 5a24c314108439a4d4036163
+title: Erstelle eine React-Komponente
+challengeType: 6
+forumTopicId: 301386
+dashedName: create-a-react-component
+---
+
+# --description--
+
+Die andere Möglichkeit, eine React-Komponente zu definieren, ist die ES6 `class`-Syntax. Im folgenden Beispiel erweitert `Kitten` `React.Component`:
+
+```jsx
+class Kitten extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
Hi
+ );
+ }
+}
+```
+
+Dadurch wird eine ES6-Klasse `Kitten` erstellt, die die Klasse `React.Component` erweitert. So hat die Klasse `Kitten` jetzt Zugriff auf viele nützliche React-Funktionen, wie lokale Zustände und Lifecycle-Hooks. Mach dir keine Sorgen, wenn du mit diesen Begriffen noch nicht vertraut bist, sie werden in späteren Aufgaben ausführlicher behandelt. Beachte auch, dass die Klasse `Kitten` einen `constructor` hat, der `super()` aufruft. Sie verwendet `super()`, um den Konstruktor der Elternklasse aufzurufen, in diesem Fall `React.Component`. Der Konstruktor ist eine spezielle Methode, die bei der Initialisierung von Objekten verwendet wird, die mit dem Schlüsselwort `class` erstellt werden. Am besten rufst du den `constructor` einer Komponente mit `super` auf und übergibst `props` an beide. Dadurch wird sichergestellt, dass die Komponente richtig initialisiert wird. Du solltest wissen, dass es Standard ist, dass dieser Code enthalten ist. Bald wirst du auch andere Verwendungsmöglichkeiten für den Konstruktor und die `props` sehen.
+
+# --instructions--
+
+`MyComponent` wird im Code-Editor mit der Klassensyntax definiert. Schreibe die Methode `render` fertig, damit sie ein `div`-Element zurückgibt, das ein `h1` mit dem Text `Hello React!` enthält.
+
+# --hints--
+
+Die React-Komponente sollte ein `div`-Element zurückgeben.
+
+```js
+assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
+```
+
+Das zurückgegebene `div` sollte ein `h1`-Überschriftenelement enthalten.
+
+```js
+assert(
+ /
.*<\/h1><\/div>/.test(
+ Enzyme.shallow(React.createElement(MyComponent)).html()
+ )
+);
+```
+
+Das `h1`-Überschriftenelement sollte den String `Hello React!` enthalten.
+
+```js
+assert(
+ Enzyme.shallow(React.createElement(MyComponent)).html() ===
+ '
Hello React! '
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ // Change code below this line
+
+
+
+ // Change code above this line
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ // Change code below this line
+ return (
+
+
Hello React!
+
+ );
+ // Change code above this line
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-simple-jsx-element.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-simple-jsx-element.md
new file mode 100644
index 00000000000..fdca70c1077
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-simple-jsx-element.md
@@ -0,0 +1,57 @@
+---
+id: 587d7dbc367417b2b2512bb1
+title: Ein einfaches JSX-Element erstellen
+challengeType: 6
+forumTopicId: 301390
+dashedName: create-a-simple-jsx-element
+---
+
+# --description--
+
+React ist eine Open-Source-View-Bibliothek, die von Facebook entwickelt und gepflegt wird. Es ist ein großartiges Werkzeug, um die Benutzeroberfläche (UI) von modernen Webanwendungen zu gestalten.
+
+React verwendet eine Syntax-Erweiterung von JavaScript namens JSX, mit der du HTML direkt in JavaScript schreiben kannst. Das hat mehrere Vorteile. Damit kannst du die volle Programmierleistung von JavaScript in HTML nutzen und deinen Code lesbar halten. Im Großen und Ganzen ähnelt JSX dem HTML, das du bereits gelernt hast, aber es gibt ein paar wichtige Unterschiede, die wir in diesen Aufgaben behandeln werden.
+
+Da JSX zum Beispiel eine syntaktische Erweiterung von JavaScript ist, kannst du JavaScript direkt in JSX schreiben. Dazu schließt du einfach den Code, der als JavaScript behandelt werden soll, in geschweifte Klammern ein: `{ 'this is treated as JavaScript code' }`. Behalte dies im Hinterkopf, da es in mehreren zukünftigen Aufgaben verwendet wird.
+
+Da JSX jedoch kein gültiges JavaScript ist, muss JSX-Code in JavaScript kompiliert werden. Der Transpiler Babel ist ein beliebtes Werkzeug für diesen Prozess. Damit du es leichter hast, ist er hinter den Kulissen für diese Aufgaben bereits hinzugefügt. Wenn du zufällig syntaktisch ungültiges JSX schreibst, wird der erste Test in dieser Aufgabe fehlschlagen.
+
+Es ist erwähnenswert, dass die Aufgaben unter der Haube `ReactDOM.render(JSX, document.getElementById('root'))` aufrufen. Dieser Funktionsaufruf platziert dein JSX in Reacts eigener, leichtgewichtiger Repräsentation des DOMs. React verwendet dann Snapshots des eigenen DOM, um nur bestimmte Teile des aktuellen DOM zu aktualisieren.
+
+# --instructions--
+
+Der aktuelle Code verwendet JSX, um ein `div`-Element der Konstante `JSX` zuzuordnen. Ersetze das `div` durch ein `h1`-Element und füge den Text `Hello JSX!` darin ein.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `h1`-Element zurückgeben.
+
+```js
+assert(JSX.type === 'h1');
+```
+
+Der `h1`-Tag sollte den Text `Hello JSX!` enthalten.
+
+```js
+assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(JSX, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const JSX =
;
+```
+
+# --solutions--
+
+```jsx
+const JSX = Hello JSX! ;
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateful-component.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateful-component.md
new file mode 100644
index 00000000000..83e13fb89a4
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateful-component.md
@@ -0,0 +1,134 @@
+---
+id: 5a24c314108439a4d4036170
+title: Eine zustandsabhängige Komponente erstellen
+challengeType: 6
+forumTopicId: 301391
+dashedName: create-a-stateful-component
+---
+
+# --description--
+
+Eines der wichtigsten Themen in React ist der Zustand (`state`). Der Zustand besteht aus allen Daten, die deine Anwendung kennen muss und die sich im Laufe der Zeit ändern können. Du möchtest, dass deine Apps auf Zustandsänderungen reagieren und bei Bedarf eine aktualisierte Benutzeroberfläche präsentieren. React bietet eine gute Lösung für die Zustandsverwaltung von modernen Webanwendungen.
+
+Du erstellst einen Zustand in einer React-Komponente, indem du eine `state`-Eigenschaft für die Komponentenklasse in ihrem `constructor` deklarierst. Dies initialisiert die Komponente mit `state`, wenn sie erstellt wird. Die Eigenschaft `state` muss auf ein JavaScript `object` gesetzt werden. Die Deklaration sieht so aus:
+
+```jsx
+this.state = {
+
+}
+```
+
+Du hast während der gesamten Lebensdauer deiner Komponente Zugriff auf das `state`-Objekt. Du kannst es aktualisieren, in deiner Benutzeroberfläche darstellen und als Eigenschaften an Kindkomponenten weitergeben. Das `state`-Objekt kann so komplex oder so einfach sein, wie du es brauchst. Beachte, dass du eine Komponentenklasse erstellen musst, indem du `React.Component` erweiterst, um `state` auf diese Weise zu erstellen.
+
+# --instructions--
+
+Es gibt eine Komponente im Code-Editor, die versucht eine `firstName`-Eigenschaft aus dem `state` zu übertragen. Es ist jedoch kein `state` definiert. Initialisiere die Komponente mit `state` im `constructor` und weise deinen Namen zu einer Eigenschaft `firstName`.
+
+# --hints--
+
+`StatefulComponent` sollte existieren und gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(
+ React.createElement(StatefulComponent)
+ );
+ return mockedComponent.find('StatefulComponent').length === 1;
+ })()
+);
+```
+
+`StatefulComponent` sollte ein `div` und ein `h1`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(
+ React.createElement(StatefulComponent)
+ );
+ return (
+ mockedComponent.find('div').length === 1 &&
+ mockedComponent.find('h1').length === 1
+ );
+ })()
+);
+```
+
+Der Zustand von `StatefulComponent` sollte mit einer Eigenschaft `firstName` eingeleitet werden, die einem String zugeordnet ist.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(
+ React.createElement(StatefulComponent)
+ );
+ const initialState = mockedComponent.state();
+ return (
+ typeof initialState === 'object' && typeof initialState.firstName === 'string'
+ );
+ })()
+);
+```
+
+Die Eigenschaft `firstName` im `StatefulComponent` sollte im `h1`-Element übertragen werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(
+ React.createElement(StatefulComponent)
+ );
+ const initialState = mockedComponent.state();
+ return mockedComponent.find('h1').text() === initialState.firstName;
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class StatefulComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ // Only change code below this line
+
+ // Only change code above this line
+ }
+ render() {
+ return (
+
+
{this.state.firstName}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class StatefulComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ firstName: 'freeCodeCamp!'
+ }
+ }
+ render() {
+ return (
+
+
{this.state.firstName}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateless-functional-component.md b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateless-functional-component.md
new file mode 100644
index 00000000000..a46ac84bc0c
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/create-a-stateless-functional-component.md
@@ -0,0 +1,102 @@
+---
+id: 5a24c314108439a4d4036162
+title: Erstelle eine zustandslose funktionelle Komponente
+challengeType: 6
+forumTopicId: 301392
+dashedName: create-a-stateless-functional-component
+---
+
+# --description--
+
+Komponenten sind der Kern von React. Alles in React ist eine Komponente und hier lernst du, wie du eine erstellst.
+
+Es gibt zwei Möglichkeiten, eine React-Komponente zu erstellen. Die erste Möglichkeit ist die Verwendung einer JavaScript-Funktion. Wenn du eine Komponente auf diese Weise definierst, entsteht eine *zustandslose funktionale Komponente (stateless functional component)*. Das Konzept des Zustands in einer Anwendung wird in späteren Aufgaben behandelt. Stell dir eine zustandslose Komponente als eine Komponente vor, die Daten empfangen und darstellen kann, aber keine Änderungen an diesen Daten verwaltet oder verfolgt. (Die zweite Möglichkeit, eine React-Komponente zu erstellen, behandeln wir in der nächsten Aufgabe.)
+
+Um eine Komponente mit einer Funktion zu erstellen, schreibst du einfach eine JavaScript-Funktion, die entweder JSX oder `null` zurückgibt. Eine wichtige Sache, die du beachten musst, ist, dass React verlangt, dass dein Funktionsname mit einem Großbuchstaben beginnt. Hier ist ein Beispiel für eine zustandslose funktionale Komponente, die eine HTML-Klasse in JSX zuweist:
+
+```jsx
+const DemoComponent = function() {
+ return (
+
+ );
+};
+```
+
+Nach der Umsetzung hat das `` eine CSS-Klasse von `customClass`.
+
+Da eine JSX-Komponente HTML darstellt, kannst du mehrere Komponenten zusammenfügen, um eine komplexere HTML-Seite zu erstellen. Dies ist einer der wichtigsten Vorteile der Komponentenarchitektur von React. Es ermöglicht dir, deine Benutzeroberfläche (UI) aus vielen separaten, isolierten Komponenten zusammenzustellen. Das macht es einfacher, komplexe Benutzeroberflächen zu erstellen und zu pflegen.
+
+# --instructions--
+
+Der Code-Editor hat eine Funktion namens `MyComponent`. Vervollständige diese Funktion so, dass sie ein einzelnes `div`-Element zurückgibt, das einen String mit Text enthält.
+
+**Hinweis:** Der Text wird als Kindelement des `div`-Elements betrachtet, daher kannst du kein selbstschließendes Tag verwenden.
+
+# --hints--
+
+`MyComponent` sollte JSX zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.length === 1;
+ })()
+);
+```
+
+`MyComponent` sollte ein `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Das `div`-Element sollte einen String aus Text enthalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('div').text() !== '';
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const MyComponent = function() {
+ // Change code below this line
+
+
+
+ // Change code above this line
+}
+```
+
+# --solutions--
+
+```jsx
+const MyComponent = function() {
+ // Change code below this line
+ return (
+
+ Demo Solution
+
+ );
+ // Change code above this line
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/define-an-html-class-in-jsx.md b/curriculum/challenges/german/03-front-end-development-libraries/react/define-an-html-class-in-jsx.md
new file mode 100644
index 00000000000..79ac8aa27ed
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/define-an-html-class-in-jsx.md
@@ -0,0 +1,62 @@
+---
+id: 5a24c314108439a4d4036160
+title: Definiere eine HTML-Klasse in JSX
+challengeType: 6
+forumTopicId: 301393
+dashedName: define-an-html-class-in-jsx
+---
+
+# --description--
+
+Jetzt, wo du dich mit dem Schreiben von JSX vertraut gemacht hast, fragst du dich vielleicht, wie es sich von HTML unterscheidet.
+
+Bisher mag es so aussehen, als ob HTML und JSX genau dasselbe sind.
+
+Ein wichtiger Unterschied in JSX ist, dass du das Wort `class` nicht mehr verwenden kannst, um HTML-Klassen zu definieren. Das liegt daran, dass `class` ein reserviertes Wort in JavaScript ist. Stattdessen verwendet JSX `className`.
+
+Tatsächlich wird die Namenskonvention für alle HTML-Attribute und Ereignisreferenzen in JSX zu camelCase. Ein Klick-Ereignis in JSX heißt zum Beispiel `onClick`, statt `onclick`. Genauso wird `onchange` zu `onChange`. Das ist zwar ein feiner Unterschied, aber ein wichtiger, den du im Hinterkopf behalten solltest.
+
+# --instructions--
+
+Wende eine Klasse von `myDiv` auf das `div` an, das im JSX-Code angegeben ist.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `div`-Element zurückgeben.
+
+```js
+assert.strictEqual(JSX.type, 'div');
+```
+
+Das `div` sollte eine Klasse von `myDiv` besitzen.
+
+```js
+assert.strictEqual(JSX.props.className, 'myDiv');
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(JSX, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const JSX = (
+
+
Add a class to this div
+
+);
+```
+
+# --solutions--
+
+```jsx
+const JSX = (
+
+
Add a class to this div
+);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/give-sibling-elements-a-unique-key-attribute.md b/curriculum/challenges/german/03-front-end-development-libraries/react/give-sibling-elements-a-unique-key-attribute.md
new file mode 100644
index 00000000000..59f1dd014ed
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/give-sibling-elements-a-unique-key-attribute.md
@@ -0,0 +1,147 @@
+---
+id: 5a24c314108439a4d403618b
+title: Geschwisterelemente mit einem eindeutigen Schlüsselattribut versehen
+challengeType: 6
+forumTopicId: 301394
+dashedName: give-sibling-elements-a-unique-key-attribute
+---
+
+# --description--
+
+In der letzten Aufgabe wurde gezeigt, wie die Methode `map` verwendet wird, um eine Reihe von Elementen auf der Grundlage von Benutzereingaben dynamisch darzustellen. Allerdings fehlte in diesem Beispiel ein wichtiger Teil. Wenn du ein Array mit Elementen erstellst, braucht jedes Element ein Schlüssel(`key`)-Attribut, das auf einen eindeutigen Wert gesetzt ist. React verwendet diese Schlüssel, um zu verfolgen, welche Elemente hinzugefügt, geändert oder entfernt werden. Dadurch wird der Prozess des Re-Renderings effizienter, wenn die Liste in irgendeiner Weise verändert wird.
+
+**Hinweis:** Schlüssel müssen nur zwischen Geschwisterelementen eindeutig sein, sie müssen in deiner Anwendung nicht global eindeutig sein.
+
+# --instructions--
+
+Der Code-Editor enthält ein Array mit einigen Frontend-Frameworks und eine zustandslose funktionale Komponente namens `Frameworks()`. `Frameworks()` muss das Array in eine ungeordnete Liste umwandeln, ähnlich wie in der letzten Aufgabe. Vervollständige den `map`-Callback, um ein `li`-Element für jedes Framework im `frontEndFrameworks`-Array zurückzugeben. Achte dieses Mal darauf, dass du jedem `li` ein `key`-Attribut zuweist, das auf einen eindeutigen Wert gesetzt ist. Die `li`-Elemente sollten auch Text aus `frontEndFrameworks` enthalten.
+
+Normalerweise solltest du den Schlüssel so gestalten, dass er das darzustellende Element eindeutig identifiziert. Als letzter Ausweg kann der Array-Index verwendet werden, aber normalerweise solltest du versuchen, eine eindeutige Kennung zu verwenden.
+
+# --hints--
+
+Die Komponente `Frameworks` sollte existieren und auf der Seite angezeigt werden.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1
+);
+```
+
+`Frameworks` sollte ein `h1`-Element darstellen.
+
+```js
+assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
+```
+
+`Frameworks` sollten ein `ul`-Element darstellen.
+
+```js
+assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
+```
+
+Das `ul`-Tag sollte 6 untergeordnete `li`-Elemente darstellen.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length ===
+ 6 &&
+ Enzyme.mount(React.createElement(Frameworks))
+ .find('ul')
+ .childAt(0)
+ .name() === 'li' &&
+ Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6
+);
+```
+
+Jedes Listenelement sollte ein eindeutiges `key`-Attribut besitzen.
+
+```js
+assert(
+ (() => {
+ const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul');
+ const keys = new Set([
+ ul.childAt(0).key(),
+ ul.childAt(1).key(),
+ ul.childAt(2).key(),
+ ul.childAt(3).key(),
+ ul.childAt(4).key(),
+ ul.childAt(5).key()
+ ]);
+ return keys.size === 6;
+ })()
+);
+```
+
+Jedes Listenelement sollte Text aus `frontEndFrameworks` enthalten.
+
+```js
+assert(
+ (() => {
+ const li = Enzyme.mount(React.createElement(Frameworks))
+ .find('ul')
+ .children();
+ return [...Array(5)].every((_, i) =>
+ frontEndFrameworks.includes(li.at(i).text())
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const frontEndFrameworks = [
+ 'React',
+ 'Angular',
+ 'Ember',
+ 'Knockout',
+ 'Backbone',
+ 'Vue'
+];
+
+function Frameworks() {
+ const renderFrameworks = null; // Change this line
+ return (
+
+
Popular Front End JavaScript Frameworks
+
+
+ );
+};
+```
+
+# --solutions--
+
+```jsx
+const frontEndFrameworks = [
+ 'React',
+ 'Angular',
+ 'Ember',
+ 'Knockout',
+ 'Backbone',
+ 'Vue'
+];
+
+function Frameworks() {
+ const renderFrameworks = frontEndFrameworks.map((fw, i) =>
{fw} );
+ return (
+
+
Popular Front End JavaScript Frameworks
+
+
+ );
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/introducing-inline-styles.md b/curriculum/challenges/german/03-front-end-development-libraries/react/introducing-inline-styles.md
new file mode 100644
index 00000000000..fe20d77da26
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/introducing-inline-styles.md
@@ -0,0 +1,103 @@
+---
+id: 5a24c314108439a4d4036181
+title: Einführung in die Inline-Styles
+challengeType: 6
+forumTopicId: 301395
+dashedName: introducing-inline-styles
+---
+
+# --description--
+
+Es gibt noch weitere komplexe Konzepte, die deinem React-Code mächtige Fähigkeiten verleihen. Vielleicht fragst du dich aber auch, wie du die JSX-Elemente, die du in React erstellst, stylen kannst. Du weißt wahrscheinlich, dass es nicht genau die gleiche Arbeit sein wird wie mit HTML, aufgrund von
der Art und Weise, wie du Klassen auf JSX-Elemente anwendest.
+
+Wenn du Stile aus einem Stylesheet importierst, ist es nicht viel anders. Du wendest eine Klasse mit dem Attribut `className` auf dein JSX-Element an und wendest Stile auf die Klasse in deinem Stylesheet an. Eine andere Möglichkeit ist die Anwendung von Inline-Styles, die in der ReactJS-Entwicklung sehr verbreitet sind.
+
+Du wendest Inline-Stile auf JSX-Elemente an, ähnlich wie du es in HTML tust, allerdings mit ein paar JSX-Unterschieden. Hier ist ein Beispiel für einen Inline-Stil in HTML:
+
+```jsx
+
Mellow Yellow
+```
+
+JSX-Elemente verwenden das `style`-Attribut, aber aufgrund der Art und Weise, wie JSX transpiliert wird, kannst du den Wert nicht auf einen `string` setzen. Stattdessen setzt du es gleich mit einem JavaScript `object`. Hier ist ein Beispiel:
+
+```jsx
+
Mellow Yellow
+```
+
+Hast du bemerkt, dass wir die Eigenschaft `fontSize` in CamelCase schreiben? Das liegt daran, dass React keine Groß- und Kleinschreibung im Style-Objekt akzeptiert. React wird den korrekten Eigenschaftsnamen für uns im HTML übernehmen.
+
+# --instructions--
+
+Füge im Code-Editor ein `style`-Attribut zum `div` hinzu, um dem Text eine rote Farbe und eine Schriftgröße von `72px` zu geben.
+
+Beachte, dass du die Schriftgröße optional als Zahl angeben kannst, indem du die Einheit `px` weglässt, oder sie als `72px` schreibst.
+# --hints--
+
+Die Komponente sollte ein `div`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Colorful));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Das `div`-Element sollte die Farbe `red` haben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Colorful));
+ return mockedComponent.children().props().style.color === 'red';
+ })()
+);
+```
+
+Das `div`-Element sollte eine Schriftgröße von `72px` haben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Colorful));
+ return (
+ mockedComponent.children().props().style.fontSize === 72 ||
+ mockedComponent.children().props().style.fontSize === '72' ||
+ mockedComponent.children().props().style.fontSize === '72px'
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class Colorful extends React.Component {
+ render() {
+ return (
+
Big Red
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class Colorful extends React.Component {
+ render() {
+ return (
+
Big Red
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/learn-about-self-closing-jsx-tags.md b/curriculum/challenges/german/03-front-end-development-libraries/react/learn-about-self-closing-jsx-tags.md
new file mode 100644
index 00000000000..836f2fe2f05
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/learn-about-self-closing-jsx-tags.md
@@ -0,0 +1,75 @@
+---
+id: 5a24c314108439a4d4036161
+title: Lerne etwas über selbstschließende JSX-Tags
+challengeType: 6
+forumTopicId: 301396
+dashedName: learn-about-self-closing-jsx-tags
+---
+
+# --description--
+
+Bisher hast du gesehen, dass sich JSX in einem wichtigen Punkt von HTML unterscheidet, nämlich in der Verwendung von `className` gegenüber `class` für die Definition von HTML-Klassen.
+
+Ein weiterer wichtiger Unterschied zwischen JSX und HTML besteht in der Idee des selbstschließenden Tags.
+
+In HTML haben fast alle Tags sowohl einen öffnenden als auch einen schließenden Tag: `
`; der schließende Tag hat immer einen Schrägstrich vor dem Namen des Tags, den du schließt. Es gibt jedoch spezielle Fälle in HTML, die "selbstschließende Tags" genannt werden, also Tags, die keinen öffnenden und schließenden Tag benötigen, bevor ein anderer Tag beginnen kann.
+
+Das Zeilenumbruch-Tag kann zum Beispiel als `
` oder als `
` geschrieben werden, sollte aber nie als `
` geschrieben werden, da es keinen Inhalt enthält.
+
+In JSX sind die Regeln ein wenig anders. Jedes JSX-Element kann mit einem selbstschließenden Tag geschrieben werden, und jedes Element muss geschlossen werden. Der Zeilenumbruch-Tag muss zum Beispiel immer als `
` geschrieben werden, um gültiges JSX zu sein, das transpiliert werden kann. Ein `
` hingegen kann als `
` oder `
` geschrieben werden. Der Unterschied ist, dass es in der ersten Syntaxversion keine Möglichkeit gibt, etwas in das `
` einzuschließen. Du wirst in späteren Aufgaben sehen, dass diese Syntax beim Rendern von React-Komponenten nützlich ist.
+
+# --instructions--
+
+Behebe die Fehler im Code-Editor, damit es gültiges JSX ist und erfolgreich transpiliert wird. Achte darauf, dass du nichts am Inhalt änderst - du musst nur die Tags schließen, wo sie gebraucht werden.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `div`-Element zurückgeben.
+
+```js
+assert.strictEqual(JSX.type, 'div');
+```
+
+Das `div` sollte ein `br`-Tag enthalten.
+
+```js
+assert(Enzyme.shallow(JSX).find('br').length === 1);
+```
+
+Das `div` sollte ein `hr`-Tag enthalten.
+
+```js
+assert(Enzyme.shallow(JSX).find('hr').length === 1);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(JSX, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const JSX = (
+
+
Welcome to React!
+
Be sure to close all tags!
+
+
+);
+```
+
+# --solutions--
+
+```jsx
+const JSX = (
+
+
Welcome to React!
+
Be sure to close all tags!
+
+
+);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/optimize-re-renders-with-shouldcomponentupdate.md b/curriculum/challenges/german/03-front-end-development-libraries/react/optimize-re-renders-with-shouldcomponentupdate.md
new file mode 100644
index 00000000000..4e41c9ce9b1
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/optimize-re-renders-with-shouldcomponentupdate.md
@@ -0,0 +1,187 @@
+---
+id: 5a24c314108439a4d4036180
+title: Optimiere Re-Renderings mit shouldComponentUpdate
+challengeType: 6
+forumTopicId: 301398
+dashedName: optimize-re-renders-with-shouldcomponentupdate
+---
+
+# --description--
+
+Wenn eine Komponente einen neuen Zustand (`state`) oder neue Eigenschafte (`props`) erhält, werden sie selbst und alle ihre Kindelemente neu geordnet. Das ist normalerweise in Ordnung. Aber React bietet eine Lifecycle-Methode, die du aufrufen kannst, wenn Kindkomponenten einen neuen `state` oder `props` erhalten, und die genau festlegt, ob die Komponenten aktualisiert werden sollen oder nicht. Die Methode heißt `shouldComponentUpdate()` und nimmt `nextProps` und `nextState` als Parameter.
+
+Diese Methode ist eine nützliche Methode, um die Leistung zu optimieren. Zum Beispiel ist das Standardverhalten, dass deine Komponente neu gerendert wird, wenn sie neue `props` erhält, auch wenn sich die `props` nicht geändert haben. Du kannst `shouldComponentUpdate()` verwenden, um dies zu verhindern, indem du die `props` vergleichst. Die Methode muss einen `boolean`-Wert zurückgeben, der React mitteilt, ob die Komponente aktualisiert werden soll oder nicht. Du kannst die aktuellen Eigenschaften (`this.props`) mit den nächsten Eigenschaften (`nextProps`) vergleichen, um festzustellen, ob du aktualisieren musst oder nicht, und entsprechend `true` oder `false` zurückgeben.
+
+# --instructions--
+
+Die Methode `shouldComponentUpdate()` wird in einer Komponente namens `OnlyEvens` hinzugefügt. Zurzeit gibt diese Methode `true` zurück, so dass `OnlyEvens` jedes Mal neu gestartet wird, wenn es neue `props` erhält. Ändere die Methode so, dass `OnlyEvens` nur aktualisiert wird, wenn der `value` der neuen Eigenschaften gerade ist. Klicke auf den Button `Add` und beobachte die Reihenfolge der Ereignisse in der Konsole deines Browsers, wenn die Lifecycle Hooks ausgelöst werden.
+
+# --hints--
+
+Die Komponente `Controller` sollte die Komponente `OnlyEvens` als Kindelement darstellen.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(Controller));
+ return (
+ mockedComponent.find('Controller').length === 1 &&
+ mockedComponent.find('OnlyEvens').length === 1
+ );
+ })()
+);
+```
+
+Die Methode `shouldComponentUpdate` sollte für die Komponente `OnlyEvens` definiert werden.
+
+```js
+assert(
+ (() => {
+ const child = React.createElement(OnlyEvens)
+ .type.prototype.shouldComponentUpdate.toString()
+ .replace(/s/g, '');
+ return child !== 'undefined';
+ })()
+);
+```
+
+Die Komponente `OnlyEvens` sollte ein `h1`-Tag zurückgeben, das den Wert von `this.props.value` wiedergibt.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(Controller));
+ const first = () => {
+ mockedComponent.setState({ value: 1000 });
+ return mockedComponent.find('h1').html();
+ };
+ const second = () => {
+ mockedComponent.setState({ value: 10 });
+ return mockedComponent.find('h1').html();
+ };
+ const firstValue = first();
+ const secondValue = second();
+ assert(firstValue === '
1000 ' && secondValue === '
10 ');
+})();
+```
+
+`OnlyEvens` sollte nur neu gerendert werden, wenn `nextProps.value` gerade ist.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(Controller));
+ const first = () => {
+ mockedComponent.setState({ value: 8 });
+ return mockedComponent.find('h1').text();
+ };
+ const second = () => {
+ mockedComponent.setState({ value: 7 });
+ return mockedComponent.find('h1').text();
+ };
+ const third = () => {
+ mockedComponent.setState({ value: 42 });
+ return mockedComponent.find('h1').text();
+ };
+ const firstValue = first();
+ const secondValue = second();
+ const thirdValue = third();
+ assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class OnlyEvens extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ shouldComponentUpdate(nextProps, nextState) {
+ console.log('Should I update?');
+ // Change code below this line
+ return true;
+ // Change code above this line
+ }
+ componentDidUpdate() {
+ console.log('Component re-rendered.');
+ }
+ render() {
+ return
{this.props.value} ;
+ }
+}
+
+class Controller extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ value: 0
+ };
+ this.addValue = this.addValue.bind(this);
+ }
+ addValue() {
+ this.setState(state => ({
+ value: state.value + 1
+ }));
+ }
+ render() {
+ return (
+
+ Add
+
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+class OnlyEvens extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ shouldComponentUpdate(nextProps, nextState) {
+ console.log('Should I update?');
+ // Change code below this line
+ return nextProps.value % 2 === 0;
+ // Change code above this line
+ }
+ componentDidUpdate() {
+ console.log('Component re-rendered.');
+ }
+ render() {
+ return
{this.props.value} ;
+ }
+}
+
+class Controller extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ value: 0
+ };
+ this.addValue = this.addValue.bind(this);
+ }
+ addValue() {
+ this.setState(state => ({
+ value: state.value + 1
+ }));
+ }
+ render() {
+ return (
+
+ Add
+
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/override-default-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/override-default-props.md
new file mode 100644
index 00000000000..1a34fad1ff9
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/override-default-props.md
@@ -0,0 +1,112 @@
+---
+id: 5a24c314108439a4d403616c
+title: Standardeigenschaften überschreiben
+challengeType: 6
+forumTopicId: 301399
+dashedName: override-default-props
+---
+
+# --description--
+
+Die Möglichkeit, Standardeigenschaften (Props) zu setzen, ist eine nützliche Funktion in React. Du kannst die Standardeigenschaften außer Kraft setzen, indem du die Eigenschaften für eine Komponente explizit festlegst.
+
+# --instructions--
+
+Die Komponente `ShoppingCart` rendert jetzt eine Kindkomponente `Items`. Diese Komponente `Items` hat eine Standardeigenschaft`quantity`, die auf die Ganzzahl `0` gesetzt ist. Überschreibe die Standardeigenschaft, indem du einen Wert von `10` für `quantity` angibst.
+
+**Hinweis:** Denke daran, dass die Syntax zum Hinzufügen einer Eigenschaft zu einer Komponente ähnlich aussieht wie beim Hinzufügen von HTML-Attributen. Da der Wert für `quantity` jedoch eine ganze Zahl ist, wird er nicht in Anführungszeichen gesetzt, sondern sollte in geschweifte Klammern eingeschlossen werden. Zum Beispiel: `{100}`. Mit dieser Syntax wird JSX angewiesen, den Wert innerhalb der geschweiften Klammern direkt als JavaScript zu interpretieren.
+
+# --hints--
+
+Die Komponente `ShoppingCart` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return mockedComponent.find('ShoppingCart').length === 1;
+ })()
+);
+```
+
+Die Komponente `Items` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return mockedComponent.find('Items').length === 1;
+ })()
+);
+```
+
+Die Komponente `Items` sollte eine Eigenschaft `{ quantity: 10 }` von der Komponente `ShoppingCart` übernehmen.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return (
+ mockedComponent.find('Items').props().quantity == 10 &&
+ getUserInput('index')
+ .replace(/ /g, '')
+ .includes('
')
+ );
+ })()
+ );
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const Items = (props) => {
+ return
Current Quantity of Items in Cart: {props.quantity}
+}
+
+Items.defaultProps = {
+ quantity: 0
+}
+
+class ShoppingCart extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ { /* Change code below this line */ }
+ return
+ { /* Change code above this line */ }
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const Items = (props) => {
+ return
Current Quantity of Items in Cart: {props.quantity}
+}
+
+Items.defaultProps = {
+ quantity: 0
+}
+
+class ShoppingCart extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ { /* Change code below this line */ }
+ return
+ { /* Change code above this line */ }
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/pass-a-callback-as-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-a-callback-as-props.md
new file mode 100644
index 00000000000..e4aea0d5c6e
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-a-callback-as-props.md
@@ -0,0 +1,217 @@
+---
+id: 5a24c314108439a4d403617b
+title: Einen Callback als Eigenschaft übergeben
+challengeType: 6
+forumTopicId: 301400
+dashedName: pass-a-callback-as-props
+---
+
+# --description--
+
+Du kannst `state` als Eigenschaften an Kindkomponenten übergeben, aber du bist nicht auf die Übergabe von Daten beschränkt. Du kannst auch Handler-Funktionen oder jede Methode, die in einer React-Komponente definiert ist, an eine Kindkomponente übergeben. So erlaubst du den Kindkomponenten, mit ihren Elternkomponenten zu interagieren. Du übergibst Methoden an ein Kind wie eine normale Eigenschaft. Ihr wird ein Name zugewiesen und du hast Zugriff auf diesen Methodennamen unter `this.props` in der Kindkomponente.
+
+# --instructions--
+
+Es gibt drei Komponenten, die im Code-Editor beschrieben werden. Die Komponente `MyApp` ist die Elternkomponente, die die Kindkomponenten `GetInput` und `RenderInput` rendert. Füge die Komponente `GetInput` zur Rendering-Methode in `MyApp` hinzu und übergebe ihr eine Eigenschaft namens `input`, die dem `inputValue` aus `MyApp`'s `state` zugewiesen ist. Erstelle außerdem eine Eigenschaft namens `handleChange` und übergebe ihr den Input-Handler `handleChange`.
+
+Als Nächstes fügst du `RenderInput` zur Render-Methode in `MyApp` hinzu. Dann erstellst du eine Eigenschaft namens `input` und übergibst den `inputValue` aus `state` an sie. Sobald du fertig bist, kannst du das `input`-Feld in die `GetInput`-Komponente eingeben, die dann die Handler-Methode in ihrer Elternkomponente über props aufruft. Dadurch wird die Eingabe im `state` der Elternkomponente aktualisiert, der als Eigenschaft an die beiden Kinderkomponente übergeben wird. Beobachte, wie die Daten zwischen den Komponenten fließen und wie die einzige Quelle der Wahrheit der `state` der übergeordneten Komponente bleibt. Zugegeben, dieses Beispiel ist ein bisschen konstruiert, aber es soll veranschaulichen, wie Daten und Rückrufe zwischen React-Komponenten weitergegeben werden können.
+
+# --hints--
+
+Die Komponente `MyApp` soll gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ return mockedComponent.find('MyApp').length === 1;
+ })()
+);
+```
+
+Die Komponente `GetInput` soll gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ return mockedComponent.find('GetInput').length === 1;
+ })()
+);
+```
+
+Die Komponente `RenderInput` soll gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ return mockedComponent.find('RenderInput').length === 1;
+ })()
+);
+```
+
+Die Komponente `GetInput` sollte die `MyApp`-Zustandseigenschaft `inputValue` als Eigenschaft erhalten und ein `input`-Element enthalten, das den `MyApp`-Zustand verändert.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ const state_1 = () => {
+ mockedComponent.setState({ inputValue: '' });
+ return waitForIt(() => mockedComponent.state());
+ };
+ const state_2 = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: 'TestInput' } });
+ return waitForIt(() => mockedComponent.state());
+ };
+ const updated_1 = await state_1();
+ const updated_2 = await state_2();
+ assert(updated_1.inputValue === '' && updated_2.inputValue === 'TestInput');
+};
+```
+
+Die Komponente `RenderInput` sollte die `MyApp`-Zustandseigenschaft `inputValue` als Eigenschaft erhalten.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ const state_1 = () => {
+ mockedComponent.setState({ inputValue: 'TestName' });
+ return waitForIt(() => mockedComponent);
+ };
+ const updated_1 = await state_1();
+ assert(updated_1.find('p').text().includes('TestName'));
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ inputValue: ''
+ }
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ inputValue: event.target.value
+ });
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+
+class GetInput extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Get Input:
+
+
+ );
+ }
+};
+
+class RenderInput extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Input Render:
+
{this.props.input}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ inputValue: ''
+ }
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(event) {
+ this.setState({
+ inputValue: event.target.value
+ });
+ }
+ render() {
+ return (
+
+
+
+
+ );
+ }
+};
+
+class GetInput extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Get Input:
+
+
+ );
+ }
+};
+
+class RenderInput extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Input Render:
+
{this.props.input}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/pass-an-array-as-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-an-array-as-props.md
new file mode 100644
index 00000000000..adc4df58df5
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-an-array-as-props.md
@@ -0,0 +1,186 @@
+---
+id: 5a24c314108439a4d403616a
+title: Übergabe eines Arrays als Eigenschaft
+challengeType: 6
+forumTopicId: 301401
+dashedName: pass-an-array-as-props
+---
+
+# --description--
+
+In der letzten Aufgabe wurde gezeigt, wie man Informationen von einer Elternkomponente an eine Kindkomponente als `props` oder Eigenschaften weitergibt. In dieser Aufgabe geht es darum, wie Arrays als `props` übergeben werden können. Um ein Array an ein JSX-Element zu übergeben, muss es als JavaScript behandelt werden und in geschweifte Klammern eingeschlossen werden.
+
+```jsx
+
+
+
+```
+
+Die Kindkomponente hat dann Zugriff auf die Array-Eigenschaft `colors`. Array-Methoden wie `join()` können beim Zugriff auf die Eigenschaft verwendet werden. `const ChildComponent = (props) =>
{props.colors.join(', ')}
` Das fügt alle `colors` Array-Elemente zu einem kommagetrennten String zusammen und erzeugt: `
green, blue, red
` Später werden wir andere gängige Methoden kennenlernen, um Arrays von Daten in React darzustellen.
+
+# --instructions--
+
+Im Code-Editor gibt es die Komponenten `List` und `ToDo`. Beim Rendern jeder `List` der Komponente `ToDo` übergibst du eine `tasks`-Eigenschaft, die einem Array von To-Do-Aufgaben zugewiesen ist, zum Beispiel `["walk dog", "workout"]`. Dann greifst du auf dieses `tasks`-Array in der Komponente `List` zu und zeigst seinen Wert innerhalb des `p`-Elements an. Verwende `join(", ")`, um das `props.tasks`array im `p`-Element als kommagetrennte Liste anzuzeigen. Die Liste von heute sollte mindestens 2 Aufgaben und die von morgen mindestens 3 Aufgaben enthalten.
+
+# --hints--
+
+Die Komponente `ToDo` sollte ein einzelnes äußeres `div` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return mockedComponent.children().first().type() === 'div';
+ })()
+);
+```
+
+Das dritte Kindelement der Komponente `ToDo` sollte eine Instanz der Komponente `List` sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return mockedComponent.children().first().childAt(2).name() === 'List';
+ })()
+);
+```
+
+Das fünfte Kindelement der Komponente `ToDo` sollte eine Instanz der Komponente `List` sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return mockedComponent.children().first().childAt(4).name() === 'List';
+ })()
+);
+```
+
+Beide Instanzen der Komponente `List` sollten eine Eigenschaft namens `tasks` haben und `tasks` sollte vom Typ Array sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return (
+ Array.isArray(mockedComponent.find('List').get(0).props.tasks) &&
+ Array.isArray(mockedComponent.find('List').get(1).props.tasks)
+ );
+ })()
+);
+```
+
+Die erste `List`, die die Aufgaben für heute darstellt, sollte 2 oder mehr Einträge enthalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return mockedComponent.find('List').get(0).props.tasks.length >= 2;
+ })()
+);
+```
+
+Die zweite `List`, die die Aufgaben für morgen darstellt, sollte 3 oder mehr Einträge enthalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return mockedComponent.find('List').get(1).props.tasks.length >= 3;
+ })()
+);
+```
+
+Die Komponente `List` sollte den Wert aus der Eigenschaft `tasks` im `p`-Tag wiedergeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ToDo));
+ return (
+ mockedComponent
+ .find('p')
+ .get(0)
+ .props.children.replace(/\s*,\s*/g, ',') ===
+ mockedComponent
+ .find('List')
+ .get(0)
+ .props.tasks.join(',')
+ .replace(/\s*,\s*/g, ',') &&
+ mockedComponent
+ .find('p')
+ .get(1)
+ .props.children.replace(/\s*,\s*/g, ',') ===
+ mockedComponent
+ .find('List')
+ .get(1)
+ .props.tasks.join(',')
+ .replace(/\s*,\s*/g, ',')
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const List = (props) => {
+ { /* Change code below this line */ }
+ return
{}
+ { /* Change code above this line */ }
+};
+
+class ToDo extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
To Do Lists
+ Today
+ { /* Change code below this line */ }
+
+ Tomorrow
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const List= (props) => {
+ return
{props.tasks.join(', ')}
+};
+
+class ToDo extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
To Do Lists
+ Today
+
+ Tomorrow
+
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/pass-props-to-a-stateless-functional-component.md b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-props-to-a-stateless-functional-component.md
new file mode 100644
index 00000000000..be1f0922562
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-props-to-a-stateless-functional-component.md
@@ -0,0 +1,164 @@
+---
+id: 5a24c314108439a4d4036169
+title: Übergabe von Eigenschaften an eine zustandslose funktionale Komponente
+challengeType: 6
+forumTopicId: 301402
+dashedName: pass-props-to-a-stateless-functional-component
+---
+
+# --description--
+
+In den vorherigen Aufgaben ging es um das Erstellen und Zusammenstellen von JSX-Elementen, funktionalen Komponenten und Klassenkomponenten im ES6-Stil in React. Mit dieser Grundlage ist es an der Zeit, sich eine weitere Funktion anzusehen, die in React sehr verbreitet ist: **props**. In React kannst du Eigenschaften (props) an Kindkomponenten übergeben. Angenommen, du hast eine Komponente `App`, die eine Kindkomponente namens `Welcome` darstellt, die eine zustandslose funktionale Komponente ist. Du kannst `Welcome` eine `user`-Eigenschaft übergeben, indem du schreibst:
+
+```jsx
+
+
+
+```
+
+Du verwendest **eigene HTML-Attribute**, die von dir erstellt und von React unterstützt werden, um sie an die Komponente zu übergeben. In diesem Fall wird die erstellte Eigenschaft `user` an die Komponente `Welcome` übergeben. Da `Welcome` eine zustandslose funktionale Komponente ist, kann sie auf diesen Wert wie folgt zugreifen:
+
+```jsx
+const Welcome = (props) =>
Hello, {props.user}!
+```
+
+Es ist Standard, diesen Wert `props` zu nennen. Wenn du mit zustandslosen funktionalen Komponenten arbeitest, betrachtest du ihn grundsätzlich als Argument für eine Funktion, die JSX zurückgibt. Du kannst auf den Wert des Arguments im Funktionskörper zugreifen. Bei Klassenkomponenten ist das ein bisschen anders.
+
+# --instructions--
+
+Im Code-Editor gibt es die Komponenten `Calendar` und `CurrentDate`. Wenn du `CurrentDate` aus der Kompente `Calendar` renderst, übergibst du eine Eigenschaft von `date`, die dem aktuellen Datum aus dem `Date`-Objekt von JavaScript zugewiesen ist. Dann greifst du auf diese `prop` in der Komponente `CurrentDate` zu und zeigst ihren Wert innerhalb der `p`-Tags an. Beachte, dass `prop`-Werte in geschweifte Klammern eingeschlossen sein müssen, damit sie als JavaScript ausgewertet werden können, zum Beispiel `date={Date()}`.
+
+# --hints--
+
+Die Komponente `Calendar` sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Calendar));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Das zweite Kindelement der Komponente `Calendar` sollte die Komponente `CurrentDate` sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Calendar));
+ return mockedComponent.children().childAt(1).name() === 'CurrentDate';
+ })()
+);
+```
+
+Die Komponente `CurrentDate` sollte eine Eigenschaft namens `date` besitzen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Calendar));
+ return mockedComponent.children().childAt(1).props().date;
+ })()
+);
+```
+
+Die Eigenschaft `date` des `CurrentDate` sollte einen String mit Text enthalten.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(Calendar));
+ const prop = mockedComponent.children().childAt(1).props().date;
+ return typeof prop === 'string' && prop.length > 0;
+ })()
+);
+```
+
+Die Eigenschaft `date` sollte durch den Aufruf `Date()` erzeugt werden
+
+```js
+assert(/
/.test(__helpers.removeWhiteSpace(code)));
+```
+
+Die Komponente `CurrentDate` sollte den Wert aus dem `date`-prop im `p`-Tag wiedergeben.
+
+```js
+let date = 'dummy date';
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(
+ React.createElement(CurrentDate, { date })
+ );
+ return mockedComponent.find('p').html().includes(date);
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const CurrentDate = (props) => {
+ return (
+
+ { /* Change code below this line */ }
+
The current date is:
+ { /* Change code above this line */ }
+
+ );
+};
+
+class Calendar extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
What date is it?
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const CurrentDate = (props) => {
+ return (
+
+ { /* Change code below this line */ }
+
The current date is: {props.date}
+ { /* Change code above this line */ }
+
+ );
+};
+
+class Calendar extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
What date is it?
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/pass-state-as-props-to-child-components.md b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-state-as-props-to-child-components.md
new file mode 100644
index 00000000000..fccfd8099ee
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/pass-state-as-props-to-child-components.md
@@ -0,0 +1,145 @@
+---
+id: 5a24c314108439a4d403617a
+title: Den Zustand als Eigenschaft an Kinderkomponenten weitergeben
+challengeType: 6
+forumTopicId: 301403
+dashedName: pass-state-as-props-to-child-components
+---
+
+# --description--
+
+In den vorherigen Aufgaben hast du viele Beispiele gesehen, in denen Eigenschaften an untergeordnete JSX-Elemente und untergeordnete React-Komponenten übergeben wurden. Du fragst dich vielleicht, woher diese Eigenschaften kommen. Ein gängiges Muster ist es, eine zustandsfähige Komponente zu haben, die den `state` enthält, der für deine Anwendung wichtig ist, und die dann Kindkomponenten rendert. Du möchtest, dass diese Komponenten Zugriff auf einige Teile des `state` haben, die als Eigenschaften (props) übergeben werden.
+
+Vielleicht hast du zum Beispiel eine `App`-Komponente, die neben anderen Komponenten auch eine `Navbar` darstellt. In deiner `App` hast du einen `state`, der viele Benutzerinformationen enthält, aber die `Navbar` braucht nur Zugriff auf den Benutzernamen des Benutzers, damit sie ihn anzeigen kann. Du übergibst diesen Teil des `state` an die `Navbar`-Komponente als Eigenschaft.
+
+Dieses Muster veranschaulicht einige wichtige Paradigmen in React. Das erste ist der *unidirektionale Datenfluss*. Der Zustand fließt in eine Richtung durch den Baum deiner Anwendungskomponenten, von der zustandsfähigen Elternkomponente zu den Kinderkomponenten. Die Kinderkomponenten erhalten nur die Zustandsdaten, die sie benötigen. Zweitens können komplexe zustandsbehaftete Anwendungen in nur wenige oder vielleicht eine einzige zustandsfähige Komponente zerlegt werden. Der Rest deiner Komponenten erhält einfach den Zustand von der Elternkomponente als Eigenschaften und rendert eine Benutzeroberfläche aus diesem Zustand. Dadurch wird eine Trennung geschaffen, bei der die Zustandsverwaltung in einem Teil des Codes und das Rendering der Benutzeroberfläche in einem anderen Teil erfolgt. Dieses Prinzip der Trennung von Zustandslogik und UI-Logik ist eines der Grundprinzipien von React. Wenn es richtig eingesetzt wird, wird das Design komplexer, zustandsfähiger Anwendungen wesentlich einfacher.
+
+# --instructions--
+
+Die Komponente `MyApp` ist zustandsfähig und stellt eine `Navbar`-Komponente als Kindelement dar. Gib die Eigenschaft `name` in ihrem `state` an die Kindkomponente weiter und zeige dann den `name` im `h1`-Tag, der Teil der `Navbar`-Rendermethode ist. `name` sollte nach dem Text `Hello, my name is:` erscheinen.
+
+# --hints--
+
+Die Komponente `MyApp` sollte mit einer `Navbar`-Komponente gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ return (
+ mockedComponent.find('MyApp').length === 1 &&
+ mockedComponent.find('Navbar').length === 1
+ );
+ })()
+);
+```
+
+Die `Navbar`-Komponente sollte die `MyApp`-Zustandseigenschaft `name` als Eigenschaft erhalten.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ const setState = () => {
+ mockedComponent.setState({ name: 'TestName' });
+ return waitForIt(() => mockedComponent.find('Navbar').props());
+ };
+ const navProps = await setState();
+ assert(navProps.name === 'TestName');
+};
+```
+
+Das `h1`-Element in der `Navbar` sollte die Eigenschaft `name` darstellen.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyApp));
+ const navH1Before = mockedComponent.find('Navbar').find('h1').text();
+ const setState = () => {
+ mockedComponent.setState({ name: 'TestName' });
+ return waitForIt(() => mockedComponent.find('Navbar').find('h1').text());
+ };
+ const navH1After = await setState();
+ assert(new RegExp('TestName').test(navH1After) && navH1After !== navH1Before);
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'CamperBot'
+ }
+ }
+ render() {
+ return (
+
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
+ );
+ }
+};
+
+class Navbar extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+ {/* Change code below this line */}
+
Hello, my name is:
+ {/* Change code above this line */}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'CamperBot'
+ }
+ }
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+class Navbar extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Hello, my name is: {this.props.name}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-a-class-component-to-the-dom.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-a-class-component-to-the-dom.md
new file mode 100644
index 00000000000..389ca7db45d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-a-class-component-to-the-dom.md
@@ -0,0 +1,159 @@
+---
+id: 5a24c314108439a4d4036167
+title: Eine Klassenkomponente in das DOM rendern
+challengeType: 6
+forumTopicId: 301404
+dashedName: render-a-class-component-to-the-dom
+---
+
+# --description--
+
+Vielleicht erinnerst du dich daran, dass du in einer früheren Aufgabe die ReactDOM-API benutzt hast, um JSX-Elemente in das DOM zu übertragen. Der Prozess zum Rendern von React-Komponenten sieht sehr ähnlich aus. In den letzten Aufgaben ging es um Komponenten und Komposition, das Rendering wurde also hinter den Kulissen für dich erledigt. Allerdings wird kein React-Code, den du schreibst, im DOM gerendert, ohne dass du die ReactDOM-API aufrufst.
+
+Hier ist eine Auffrischung der Syntax: `ReactDOM.render(componentToRender, targetNode)`. Das erste Argument ist die React-Komponente, die du rendern willst. Das zweite Argument ist der DOM-Knoten, in dem du die Komponente darstellen willst.
+
+React-Komponenten werden in `ReactDOM.render()` ein wenig anders übergeben als JSX-Elemente. Bei JSX-Elementen gibst du den Namen des Elements an, das du rendern möchtest. Für React-Komponenten musst du jedoch die gleiche Syntax verwenden, als würdest du eine verschachtelte Komponente rendern, zum Beispiel `ReactDOM.render(
, targetNode)`. Du verwendest diese Syntax sowohl für ES6-Klassenkomponenten als auch für funktionale Komponenten.
+
+# --instructions--
+
+Sowohl die Komponente `Fruits` als auch die Komponente `Vegetables` werden hinter den Kulissen für dich definiert. Rendere beide Komponenten als Kindelemente der Komponente `TypesOfFood` und rendere dann `TypesOfFood` in das DOM. Es gibt ein `div` mit `id='challenge-node'`, dass du verwenden kannst.
+
+# --hints--
+
+Die Komponente `TypesOfFood` sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().type() === 'div';
+ })()
+);
+```
+
+Die Komponente `TypesOfFood` sollte die Komponente `Fruits` nach dem Element `h1` darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().childAt(1).name() === 'Fruits';
+ })()
+);
+```
+
+Die Komponente `TypesOfFood` sollte die Komponente `Vegetables` nach `Fruits` darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
+ return mockedComponent.children().childAt(2).name() === 'Vegetables';
+ })()
+);
+```
+
+Die Komponente `TypesOfFood` sollte im DOM innerhalb des `div` mit der id `challenge-node` dargestellt werden.
+
+```js
+assert(
+ (function () {
+ const html = document.getElementById('challenge-node').childNodes[0]
+ .innerHTML;
+ return (
+ html.includes(
+ '
Fruits: Non-Citrus: Apples Blueberries Strawberries Bananas Citrus: Lemon Lime Orange Grapefruit '
+ ) &&
+ html.includes(
+ '
Vegetables: Brussel Sprouts Broccoli Squash '
+ )
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --before-user-code--
+
+```jsx
+const Fruits = () => {
+ return (
+
+
Fruits:
+
Non-Citrus:
+
+ Apples
+ Blueberries
+ Strawberries
+ Bananas
+
+
Citrus:
+
+ Lemon
+ Lime
+ Orange
+ Grapefruit
+
+
+ );
+};
+const Vegetables = () => {
+ return (
+
+
Vegetables:
+
+ Brussel Sprouts
+ Broccoli
+ Squash
+
+
+ );
+};
+```
+
+## --seed-contents--
+
+```jsx
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Types of Food:
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
+ );
+ }
+};
+
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
Types of Food:
+ {/* Change code below this line */}
+
+
+ {/* Change code above this line */}
+
+ );
+ }
+};
+
+// Change code below this line
+ReactDOM.render(
, document.getElementById('challenge-node'));
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-conditionally-from-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-conditionally-from-props.md
new file mode 100644
index 00000000000..d1f242f3ac6
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-conditionally-from-props.md
@@ -0,0 +1,307 @@
+---
+id: 5a24c314108439a4d4036188
+title: Rendere abhängig von den Eigenschaften
+challengeType: 6
+forumTopicId: 301405
+dashedName: render-conditionally-from-props
+---
+
+# --description--
+
+Bisher hast du gesehen, wie man `if/else`, `&&` und den ternären Operator (`condition ? expressionIfTrue : expressionIfFalse`) verwendet, um bedingte Entscheidungen darüber zu treffen, was wann dargestellt werden soll. Es bleibt jedoch noch ein wichtiges Thema zu besprechen, mit dem du jedes dieser Konzepte mit einer anderen leistungsstarken React-Funktion kombinieren kannst: Props (Eigenschaften). Die Verwendung von Eigenschaften zum bedingten Rendern von Code ist bei React-Entwicklern sehr verbreitet - das heißt, sie verwenden den Wert einer bestimmten Eigenschaft, um automatisch Entscheidungen darüber zu treffen, was gerendert werden soll.
+
+In dieser Aufgabe wirst du eine Kindkomponente einrichten, die Rendering-Entscheidungen anhand von Eigenschaften trifft. Du wirst auch den ternären Operator verwenden, aber du kannst sehen, dass einige der anderen Konzepte, die in den letzten Aufgaben behandelt wurden, in diesem Zusammenhang genauso nützlich sein können.
+
+# --instructions--
+
+Der Code-Editor hat zwei Komponenten, die teilweise für dich definiert sind: ein Elternelement namens `GameOfChance` und ein Kindelement namens `Results`. Sie werden verwendet, um ein einfaches Spiel zu erstellen, bei dem der/die NutzerIn einen Knopf drückt, um zu sehen, ob er/sie gewinnt oder verliert.
+
+Zuerst brauchst du einen einfachen Ausdruck, der bei jeder Ausführung zufällig einen anderen Wert liefert. Du kannst `Math.random()` verwenden. Diese Methode gibt bei jedem Aufruf einen Wert zwischen `0` (einschließlich) und `1` (ausschließlich) zurück. Für eine 50/50-Wahrscheinlichkeit verwendest du also `Math.random() >= .5` in deinem Ausdruck. Statistisch gesehen wird dieser Ausdruck in 50% der Fälle `true` und in den anderen 50% `false` zurückgeben. Ersetze in der Render-Methode `null` durch den obigen Ausdruck, um die Variablendeklaration zu vervollständigen.
+
+Jetzt hast du einen Ausdruck, den du verwenden kannst, um eine zufällige Entscheidung im Code zu treffen. Als nächstes musst du dies umsetzen. Rendere die Komponente `Results` als Kindelement von `GameOfChance` und übergebe `expression` als Eigenschaft namens `fiftyFifty`. Schreibe in der Komponente `Results` einen ternären Ausdruck, um das `h1`-Element mit dem Text `You Win!` oder `You Lose!` basierend auf der Eigenschaft `fiftyFifty`, die von `GameOfChance` übergeben wird, darzustellen. Stelle schließlich sicher, dass die `handleClick()`-Methode jede Runde korrekt zählt, damit der Benutzer weiß, wie oft er gespielt hat. Dies dient auch dazu, dass der Benutzer weiß, dass die Komponente aktualisiert wurde, falls er zweimal hintereinander gewinnt oder verliert.
+
+# --hints--
+
+Die Komponente `GameOfChance` sollte existieren und auf der Seite dargestellt werden.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length,
+ 1
+);
+```
+
+`GameOfChance` sollte ein einzelnes `button`-Element zurückgeben.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(GameOfChance)).find('button').length,
+ 1
+);
+```
+
+`GameOfChance` sollte eine einzelne Instanz der Komponente `Results` zurückgeben, die eine Eigenschaft namens `fiftyFifty` hat.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(GameOfChance)).find('Results').length ===
+ 1 &&
+ Enzyme.mount(React.createElement(GameOfChance))
+ .find('Results')
+ .props()
+ .hasOwnProperty('fiftyFifty') === true
+);
+```
+
+Der Zustand von `GameOfChance` sollte mit der Eigenschaft `counter` initialisiert werden, die auf den Wert `1` gesetzt ist.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(GameOfChance)).state().counter,
+ 1
+);
+```
+
+Wenn die Komponente `GameOfChance` zum ersten Mal im DOM gerendert wird, sollte ein `p`-Element mit dem inneren Text `Turn: 1` zurückgegeben werden.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(GameOfChance)).find('p').text(),
+ 'Turn: 1'
+);
+```
+
+Jedes Mal, wenn der Button angeklickt wird, sollte der Zählerstand um den Wert 1 erhöht werden und ein einzelnes `p`-Element sollte im DOM gerendert werden, das den Text `Turn: N` enthält, wobei `N` der Wert des Zählerstandes ist.
+
+```js
+(() => {
+ const comp = Enzyme.mount(React.createElement(GameOfChance));
+ const simulate = () => {
+ comp.find('button').simulate('click');
+ };
+ const result = () => ({
+ count: comp.state('counter'),
+ text: comp.find('p').text()
+ });
+ const _1 = () => {
+ simulate();
+ return result();
+ };
+ const _2 = () => {
+ simulate();
+ return result();
+ };
+ const _3 = () => {
+ simulate();
+ return result();
+ };
+ const _4 = () => {
+ simulate();
+ return result();
+ };
+ const _5 = () => {
+ simulate();
+ return result();
+ };
+ const _1_val = _1();
+ const _2_val = _2();
+ const _3_val = _3();
+ const _4_val = _4();
+ const _5_val = _5();
+ assert(
+ _1_val.count === 2 &&
+ _1_val.text === 'Turn: 2' &&
+ _2_val.count === 3 &&
+ _2_val.text === 'Turn: 3' &&
+ _3_val.count === 4 &&
+ _3_val.text === 'Turn: 4' &&
+ _4_val.count === 5 &&
+ _4_val.text === 'Turn: 5' &&
+ _5_val.count === 6 &&
+ _5_val.text === 'Turn: 6'
+ );
+})();
+```
+
+Wenn die Komponente `GameOfChance` zum ersten Mal in das DOM eingebunden wird und, wenn jedes Mal der Button danach angeklickt wird, sollte ein einzelnes `h1`-Element zurückgegeben werden, das zufällig entweder `You Win!` oder `You Lose!` wiedergibt. Hinweis: Dies kann willkürlich fehlschlagen. Wenn das passiert, versuche es bitte erneut.
+
+```js
+(() => {
+ const comp = Enzyme.mount(React.createElement(GameOfChance));
+ const simulate = () => {
+ comp.find('button').simulate('click');
+ };
+ const result = () => ({
+ h1: comp.find('h1').length,
+ text: comp.find('h1').text()
+ });
+ const _1 = result();
+ const _2 = () => {
+ simulate();
+ return result();
+ };
+ const _3 = () => {
+ simulate();
+ return result();
+ };
+ const _4 = () => {
+ simulate();
+ return result();
+ };
+ const _5 = () => {
+ simulate();
+ return result();
+ };
+ const _6 = () => {
+ simulate();
+ return result();
+ };
+ const _7 = () => {
+ simulate();
+ return result();
+ };
+ const _8 = () => {
+ simulate();
+ return result();
+ };
+ const _9 = () => {
+ simulate();
+ return result();
+ };
+ const _10 = () => {
+ simulate();
+ return result();
+ };
+ const _2_val = _2();
+ const _3_val = _3();
+ const _4_val = _4();
+ const _5_val = _5();
+ const _6_val = _6();
+ const _7_val = _7();
+ const _8_val = _8();
+ const _9_val = _9();
+ const _10_val = _10();
+ const __text = new Set([
+ _1.text,
+ _2_val.text,
+ _3_val.text,
+ _4_val.text,
+ _5_val.text,
+ _6_val.text,
+ _7_val.text,
+ _8_val.text,
+ _9_val.text,
+ _10_val.text
+ ]);
+ const __h1 = new Set([
+ _1.h1,
+ _2_val.h1,
+ _3_val.h1,
+ _4_val.h1,
+ _5_val.h1,
+ _6_val.h1,
+ _7_val.h1,
+ _8_val.h1,
+ _9_val.h1,
+ _10_val.h1
+ ]);
+ assert(__text.size === 2 && __h1.size === 1);
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class Results extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ {/* Change code below this line */}
+ return
;
+ {/* Change code above this line */}
+ }
+}
+
+class GameOfChance extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ counter: 1
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ this.setState(prevState => {
+ // Complete the return statement:
+ return {
+ counter: prevState
+ }
+ });
+ }
+ render() {
+ const expression = null; // Change this line
+ return (
+
+
Play Again
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
{'Turn: ' + this.state.counter}
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+// We want this to be deterministic for testing purposes.
+const randomSequence = [true, false, false, true, true, false, false, true, true, false];
+let index = 0;
+const fiftyFifty = () => randomSequence[index++ % randomSequence.length];
+
+class Results extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'} ;
+ }
+}
+
+class GameOfChance extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ counter: 1
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ this.setState(prevState => {
+ return {
+ counter: prevState.counter + 1
+ }
+ });
+ }
+ render() {
+ return (
+
+
Play Again
+
+
{'Turn: ' + this.state.counter}
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-html-elements-to-the-dom.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-html-elements-to-the-dom.md
new file mode 100644
index 00000000000..e7e82de292d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-html-elements-to-the-dom.md
@@ -0,0 +1,75 @@
+---
+id: 5a24bbe0dba28a8d3cbd4c5f
+title: HTML-Elemente in das DOM rendern
+challengeType: 6
+forumTopicId: 301406
+dashedName: render-html-elements-to-the-dom
+---
+
+# --description--
+
+Bis jetzt hast du gelernt, dass JSX ein praktisches Werkzeug ist, um lesbares HTML in JavaScript zu schreiben. Mit React können wir dieses JSX direkt in das HTML-DOM rendern, indem wir die ReactDOM genannte Rendering-API von React verwenden.
+
+ReactDOM bietet eine einfache Methode zum Rendern von React-Elementen im DOM, die wie folgt aussieht: `ReactDOM.render(componentToRender, targetNode)`, wobei das erste Argument das React-Element oder die Komponente ist, die du rendern willst, und das zweite Argument der DOM-Knoten, in den du die Komponente rendern willst.
+
+Wie zu erwarten, muss `ReactDOM.render()` nach den JSX-Elementdeklarationen aufgerufen werden, genau wie du Variablen deklarieren musst, bevor du sie benutzt.
+
+# --instructions--
+
+Der Code-Editor hat eine einfache JSX-Komponente. Verwende die Methode `ReactDOM.render()`, um diese Komponente auf der Seite darzustellen. Du kannst definierte JSX-Elemente direkt als erstes Argument übergeben und `document.getElementById()` verwenden, um den DOM-Knoten auszuwählen, in dem sie gerendert werden sollen. Es gibt ein `div` mit `id='challenge-node'`, das du verwenden kannst. Achte darauf, dass du die Konstante `JSX` nicht änderst.
+
+# --hints--
+
+Die Konstante `JSX` sollte ein `div`-Element zurückgeben.
+
+```js
+assert(JSX.type === 'div');
+```
+
+Das `div` sollte ein `h1`-Tag als erstes Element enthalten.
+
+```js
+assert(JSX.props.children[0].type === 'h1');
+```
+
+Das `div` sollte ein `p`-Tag als zweites Element enthalten.
+
+```js
+assert(JSX.props.children[1].type === 'p');
+```
+
+Das angegebene JSX-Element sollte zum DOM-Knoten mit der id `challenge-node` werden.
+
+```js
+assert(
+ document.getElementById('challenge-node').childNodes[0].innerHTML ===
+ '
Hello World Lets render this to the DOM
'
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+const JSX = (
+
+
Hello World
+
Lets render this to the DOM
+
+);
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+const JSX = (
+
+
Hello World
+
Lets render this to the DOM
+
+);
+// Change code below this line
+ReactDOM.render(JSX, document.getElementById('challenge-node'));
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-react-on-the-server-with-rendertostring.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-react-on-the-server-with-rendertostring.md
new file mode 100644
index 00000000000..12a1c10ef44
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-react-on-the-server-with-rendertostring.md
@@ -0,0 +1,76 @@
+---
+id: 5a24c314108439a4d403618d
+title: React auf dem Server mit renderToString rendern
+challengeType: 6
+forumTopicId: 301407
+dashedName: render-react-on-the-server-with-rendertostring
+---
+
+# --description--
+
+Bis jetzt hast du React-Komponenten auf dem Client gerendert. Normalerweise wirst du das immer tun. Es gibt jedoch einige Anwendungsfälle, in denen es sinnvoll ist, eine React-Komponente auf dem Server zu rendern. Da React eine JavaScript-View-Bibliothek ist und du mit Node JavaScript auf dem Server ausführen kannst, ist dies möglich. Tatsächlich bietet React eine `renderToString()`-Methode, die du für diesen Zweck verwenden kannst.
+
+Es gibt zwei wichtige Gründe, warum das Rendering auf dem Server in einer realen Anwendung verwendet werden kann. Erstens würden deine React-Apps sonst aus einer relativ leeren HTML-Datei und einem großen Bündel von JavaScript bestehen, wenn sie zum ersten Mal in den Browser geladen werden. Das ist vielleicht nicht ideal für Suchmaschinen, die versuchen, den Inhalt deiner Seiten zu indexieren, damit die Leute dich finden können. Wenn du das anfängliche HTML-Markup auf dem Server renderst und an den Client sendest, enthält das erste Laden der Seite das gesamte Markup der Seite, das von Suchmaschinen gecrawlt werden kann. Zweitens wird die Seite dadurch schneller geladen, weil der gerenderte HTML-Code kleiner ist als der JavaScript-Code der gesamten App. React wird deine App auch nach dem ersten Laden erkennen und verwalten können.
+
+# --instructions--
+
+Die Methode `renderToString()` wird auf dem `ReactDOMServer` bereitgestellt, der hier als globales Objekt verfügbar ist. Die Methode benötigt ein Argument, das ein React-Element ist. Verwende dies, um `App` als String zu rendern.
+
+# --hints--
+
+Die Komponente `App` sollte mit `ReactDOMServer.renderToString` als String gerendert werden.
+
+```js
+(getUserInput) =>
+ assert(
+ getUserInput('index')
+ .replace(/ /g, '')
+ .includes('ReactDOMServer.renderToString(
)') &&
+ Enzyme.mount(React.createElement(App)).children().name() === 'div'
+ );
+```
+
+# --seed--
+
+## --before-user-code--
+
+```jsx
+var ReactDOMServer = { renderToString(x) { return null; } };
+```
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
+ }
+};
+
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
+ }
+};
+
+// Change code below this line
+ReactDOMServer.renderToString(
);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface-another-way.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface-another-way.md
new file mode 100644
index 00000000000..860d84ca639
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface-another-way.md
@@ -0,0 +1,119 @@
+---
+id: 5a24c314108439a4d4036172
+title: Den Zustand in der Benutzeroberfläche auf andere Weise darstellen
+challengeType: 6
+forumTopicId: 301408
+dashedName: render-state-in-the-user-interface-another-way
+---
+
+# --description--
+
+Es gibt noch eine andere Möglichkeit, auf den Zustand (`state`) einer Komponente zuzugreifen. In die Methode `render()`, vor der `return`-Anweisung, kannst du direkt JavaScript schreiben. Du könntest zum Beispiel Funktionen deklarieren, auf Daten aus `state` oder `props` zugreifen, Berechnungen mit diesen Daten durchführen und so weiter. Dann kannst du Variablen beliebige Daten zuweisen, auf die du in der `return`-Anweisung Zugriff hast.
+
+# --instructions--
+
+Definiere in der Rendering-Methode von `MyComponent` eine Konstante(`const`) mit dem Namen `name` und setze sie auf den Wert des Namens im `state` der Komponente. Da du JavaScript direkt in diesen Teil des Codes schreiben kannst, musst du diesen Verweis nicht in geschweifte Klammern einschließen.
+
+In der Return-Anweisung gibst du diesen Wert dann in einem `h1`-Tag mit der Variablen `name` wieder. Denke daran, dass du die JSX-Syntax (geschweifte Klammern für JavaScript) in der Return-Anweisung verwenden musst.
+
+# --hints--
+
+`MyComponent` sollte einen Schlüssel `name` mit dem Wert `freeCodeCamp` in seinem Zustand gespeichert haben.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyComponent)).state('name') ===
+ 'freeCodeCamp'
+);
+```
+
+`MyComponent` sollte ein `h1`-Überschriftenelement darstellen, das von einem einzelnen `div` eingeschlossen ist.
+
+```js
+assert(
+ /
.*<\/h1><\/div>/.test(
+ Enzyme.mount(React.createElement(MyComponent)).html()
+ )
+);
+```
+
+Das gerenderte `h1`-Tag sollte einen Verweis auf `{name}` enthalten.
+
+```js
+(getUserInput) =>
+ assert(/\n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index')));
+```
+
+Das gerenderte `h1`-Überschriftenelement sollte Text enthalten, der aus dem Zustand der Komponente gerendert wurde.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ name: 'TestName' });
+ return waitForIt(() => mockedComponent.html());
+ };
+ const firstValue = await first();
+ assert(firstValue === '
TestName ');
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'freeCodeCamp'
+ }
+ }
+ render() {
+ // Change code below this line
+
+ // Change code above this line
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'freeCodeCamp'
+ }
+ }
+ render() {
+ // Change code below this line
+ const name = this.state.name;
+ // Change code above this line
+ return (
+
+ { /* Change code below this line */ }
+
{name}
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface.md
new file mode 100644
index 00000000000..ed8364ba03d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-state-in-the-user-interface.md
@@ -0,0 +1,113 @@
+---
+id: 5a24c314108439a4d4036171
+title: Darstellung des Zustands in der Benutzeroberfläche
+challengeType: 6
+forumTopicId: 301409
+dashedName: render-state-in-the-user-interface
+---
+
+# --description--
+
+Sobald du den Ausgangszustand einer Komponente definiert hast, kannst du jeden Teil der Komponente in der gerenderten Benutzeroberfläche anzeigen. Wenn eine Komponente zustandsabhängig ist, hat sie in ihrer `render()`-Methode immer Zugriff auf die Daten im `state`. Du kannst auf die Daten mit `this.state` zugreifen.
+
+Wenn du innerhalb des `return` der Render-Methode auf einen Zustandswert zugreifen willst, musst du den Wert in geschweifte Klammern einschließen.
+
+Der `state` ist eine der mächtigsten Funktionen der Komponenten in React. Sie ermöglicht es dir, wichtige Daten in deiner App zu verfolgen und eine Benutzeroberfläche als Reaktion auf Änderungen dieser Daten darzustellen. Wenn sich deine Daten ändern, wird sich auch deine Benutzeroberfläche ändern. React verwendet ein sogenanntes virtuelles DOM, um Änderungen hinter den Kulissen zu verfolgen. Wenn die Zustandsdaten aktualisiert werden, werden die Komponenten, die diese Daten verwenden, neu gerendert - einschließlich der Kindkomponenten, die die Daten als Eigenschaft (prop) erhalten haben. React aktualisiert das eigentliche DOM, aber nur, wenn es nötig ist. Das bedeutet, dass du dich nicht um die Änderung des DOM kümmern musst. Du gibst einfach an, wie die Benutzeroberfläche aussehen soll.
+
+Beachte, dass wenn du eine Komponente zustandsabhängig machst, andere Komponenten nichts von ihrem `state` wissen. Ihr `state` ist vollständig gekapselt oder lokal für diese Komponente, es sei denn, du übergibst Zustandsdaten als `props` an eine Kindkomponente. Das Konzept des gekapselten `state` ist sehr wichtig, weil es dir erlaubt, eine bestimmte Logik zu schreiben und diese Logik dann an einer Stelle in deinem Code zu isolieren.
+
+# --instructions--
+
+Im Code-Editor ist `MyComponent` bereits zustandsfähig. Definiere ein `h1`-Tag in der Render-Methode der Komponente, das den Wert von `name` aus dem Zustand der Komponente rendert.
+
+**Hinweis:** Die `h1` sollte nur den Wert von `state` wiedergeben und nichts anderes. In JSX wird jeder Code, den du mit geschweiften Klammern `{ }` schreibst, als JavaScript behandelt. Um auf den Wert von `state` zuzugreifen, musst du die Referenz in geschweifte Klammern einschließen.
+
+# --hints--
+
+`MyComponent` sollte einen Schlüssel `name` mit dem Wert `freeCodeCamp` in seinem Status gespeichert haben.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyComponent)).state('name') ===
+ 'freeCodeCamp'
+);
+```
+
+`MyComponent` sollte ein `h1`-Überschriftenelement darstellen, das von einem einzelnen `div` eingeschlossen ist.
+
+```js
+assert(
+ /.*<\/h1><\/div>/.test(
+ Enzyme.mount(React.createElement(MyComponent)).html()
+ )
+);
+```
+
+Das gerenderte `h1`-Überschriftenelement sollte nur Text enthalten, der aus dem Zustand der Komponente gerendert wurde.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ name: 'TestName' });
+ return waitForIt(() => mockedComponent.html());
+ };
+ const firstValue = await first();
+ const getValue = firstValue.replace(/\s/g, '');
+ assert(getValue === '
TestName ');
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'freeCodeCamp'
+ }
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'freeCodeCamp'
+ }
+ }
+ render() {
+ return (
+
+ { /* Change code below this line */ }
+
{this.state.name}
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/render-with-an-if-else-condition.md b/curriculum/challenges/german/03-front-end-development-libraries/react/render-with-an-if-else-condition.md
new file mode 100644
index 00000000000..a973fa0a03f
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/render-with-an-if-else-condition.md
@@ -0,0 +1,155 @@
+---
+id: 5a24c314108439a4d4036184
+title: Rendering mit einer If-Else-Bedingung
+challengeType: 6
+forumTopicId: 301410
+dashedName: render-with-an-if-else-condition
+---
+
+# --description--
+
+Eine weitere Möglichkeit, die gerenderte Ansicht mit JavaScript zu steuern, besteht darin, die gerenderten Elemente an eine Bedingung zu knüpfen. Wenn die Bedingung erfüllt ist, wird eine Ansicht gerendert. Wenn sie falsch ist, ist das eine andere Ansicht. Das kannst du mit einer standardmäßigen `if/else`-Anweisung in der `render()`-Methode einer React-Komponente machen.
+
+# --instructions--
+
+MyComponent enthält einen `boolean` in ihrem Zustand, der festhält, ob du ein Element in der Benutzeroberfläche anzeigen möchtest oder nicht. Der `button` schaltet den Zustand dieses Wertes um. Derzeit wird jedes Mal die gleiche Benutzeroberfläche angezeigt. Schreibe die `render()`-Methode mit einer `if/else`-Anweisung um, so dass du, wenn `display` gleich `true` ist, das aktuelle Markup zurückgibst. Andernfalls wird das Markup ohne das `h1`-Element zurückgegeben.
+
+**Hinweis:** Du musst ein `if/else` schreiben, um die Tests zu bestehen. Die Verwendung des ternären Operators wird hier nicht akzeptiert.
+
+# --hints--
+
+`MyComponent` sollte existieren und gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('MyComponent').length === 1;
+ })()
+);
+```
+
+Wenn `display` auf `true` gesetzt ist, sollten ein `div`, ein `button` und ein `h1` gerendert werden.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const state_1 = () => {
+ mockedComponent.setState({ display: true });
+ return waitForIt(() => mockedComponent);
+ };
+ const updated = await state_1();
+ assert(
+ mockedComponent.find('div').length === 1 &&
+ mockedComponent.find('div').children().length === 2 &&
+ mockedComponent.find('button').length === 1 &&
+ mockedComponent.find('h1').length === 1
+ );
+};
+```
+
+Wenn `display` auf `false` gesetzt ist, sollten nur ein `div` und ein `button` gerendert werden.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const state_1 = () => {
+ mockedComponent.setState({ display: false });
+ return waitForIt(() => mockedComponent);
+ };
+ const updated = await state_1();
+ assert(
+ mockedComponent.find('div').length === 1 &&
+ mockedComponent.find('div').children().length === 1 &&
+ mockedComponent.find('button').length === 1 &&
+ mockedComponent.find('h1').length === 0
+ );
+};
+```
+
+Die Render-Methode sollte eine `if/else`-Anweisung verwenden, um die Bedingung von `this.state.display` zu überprüfen.
+
+```js
+(getUserInput) =>
+ assert(
+ getUserInput('index').includes('if') &&
+ getUserInput('index').includes('else')
+ );
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ display: true
+ }
+ this.toggleDisplay = this.toggleDisplay.bind(this);
+ }
+ toggleDisplay() {
+ this.setState((state) => ({
+ display: !state.display
+ }));
+ }
+ render() {
+ // Change code below this line
+
+ return (
+
+ Toggle Display
+
Displayed!
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ display: true
+ }
+ this.toggleDisplay = this.toggleDisplay.bind(this);
+ }
+ toggleDisplay() {
+ this.setState((state) => ({
+ display: !state.display
+ }));
+ }
+ render() {
+ // Change code below this line
+ if (this.state.display) {
+ return (
+
+ Toggle Display
+
Displayed!
+
+ );
+ } else {
+ return (
+
+ Toggle Display
+
+ );
+ }
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/review-using-props-with-stateless-functional-components.md b/curriculum/challenges/german/03-front-end-development-libraries/react/review-using-props-with-stateless-functional-components.md
new file mode 100644
index 00000000000..177163486a0
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/review-using-props-with-stateless-functional-components.md
@@ -0,0 +1,145 @@
+---
+id: 5a24c314108439a4d403616f
+title: Überprüfung der Verwendung von Eigenschaften mit zustandslosen funktionalen Komponenten
+challengeType: 6
+forumTopicId: 301411
+dashedName: review-using-props-with-stateless-functional-components
+---
+
+# --description--
+
+Mit Ausnahme der letzten Aufgabe hast du Eigenschaften an zustandslose funktionale Komponenten übergeben. Diese Komponenten verhalten sich wie reine Funktionen. Sie akzeptieren Eigenschaften als Eingabe und geben jedes Mal dieselbe Ansicht zurück, wenn ihnen dieselben Eigenschaften übergeben werden. Du fragst dich vielleicht, was ein Zustand ist. In der nächsten Aufgabe werden wir ihn genauer erklären. Zuvor aber noch ein Überblick über die Terminologie der Komponenten.
+
+Eine *zustandslose funktionale Komponente* ist jede Funktion, die du schreibst, die Eigenschaften akzeptiert und JSX zurückgibt. Eine *zustandslose Komponente* hingegen ist eine Klasse, die `React.Component` erweitert, aber keinen internen Zustand verwendet (wird in der nächsten Aufgabe behandelt). Schließlich ist eine *zustandsfähige Komponente* eine Klassenkomponente, die ihren eigenen internen Zustand beibehält. Du kannst sehen, dass zustandsabhängige Komponenten einfach als Komponenten oder React-Komponenten bezeichnet werden.
+
+Ein gängiges Muster ist der Versuch, die Zustandsabhängigkeit zu minimieren und zustandslose funktionale Komponenten zu erstellen, wo immer dies möglich ist. So kannst du deine Zustandsverwaltung auf einen bestimmten Bereich deiner Anwendung beschränken. Das wiederum verbessert die Entwicklung und Wartung deiner App, da du leichter nachvollziehen kannst, wie sich Änderungen am Status auf das Verhalten der App auswirken.
+
+# --instructions--
+
+Der Code-Editor hat eine Komponente `CampSite`, die eine Komponente `Camper` als Kind rendert. Definiere die Komponente `Camper` und weise ihr die Standardeigenschaft `{ name: 'CamperBot' }` zu. Innerhalb der Komponente `Camper` kannst du jeden beliebigen Code rendern, aber achte darauf, dass es ein `p`-Element gibt, das nur den `name`-Wert enthält, der als `prop` übergeben wird. Schließlich definierst du `propTypes` auf der Komponente `Camper`, um zu verlangen, dass `name` als Eigenschaft angegeben wird, und um zu überprüfen, ob er vom Typ `string` ist.
+
+# --hints--
+
+Die Komponente `CampSite` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(CampSite));
+ return mockedComponent.find('CampSite').length === 1;
+ })()
+);
+```
+
+Die Komponente `Camper` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(CampSite));
+ return mockedComponent.find('Camper').length === 1;
+ })()
+);
+```
+
+Die Komponente `Camper` sollte ein Standardeigenschaft enthalten, die dem Schlüssel `name` den String `CamperBot` zuweist.
+
+```js
+assert(
+ /Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(
+ __helpers.removeWhiteSpace(code)
+ )
+);
+```
+
+Die Komponente `Camper` sollte Prop-Typen enthalten, bei denen die Eigenschaft `name` vom Typ `string` sein muss.
+
+```js
+assert(
+ /Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(
+ __helpers.removeWhiteSpace(code)
+ )
+);
+```
+
+Die Komponente `Camper` sollte ein `p`-Element enthalten, das nur den Text aus der Eigenschaft `name` enthält.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(CampSite));
+ return (
+ mockedComponent.find('p').text() ===
+ mockedComponent.find('Camper').props().name
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --before-user-code--
+
+```jsx
+var PropTypes = {
+ string: { isRequired: true }
+};
+```
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class CampSite extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+class CampSite extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
+
+ );
+ }
+};
+// Change code below this line
+
+const Camper = (props) => {
+ return (
+
+ );
+};
+
+Camper.propTypes = {
+ name: PropTypes.string.isRequired
+};
+
+Camper.defaultProps = {
+ name: 'CamperBot'
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/set-state-with-this.setstate.md b/curriculum/challenges/german/03-front-end-development-libraries/react/set-state-with-this.setstate.md
new file mode 100644
index 00000000000..c5b4b0362c3
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/set-state-with-this.setstate.md
@@ -0,0 +1,143 @@
+---
+id: 5a24c314108439a4d4036173
+title: Zustand mit this.setState setzen
+challengeType: 6
+forumTopicId: 301412
+dashedName: set-state-with-this-setstate
+---
+
+# --description--
+
+In den vorherigen Aufgaben ging es um den `state` der Komponente und wie man den Zustand im `constructor` initialisiert. Es gibt auch eine Möglichkeit, den `state` der Komponente zu ändern. React bietet eine Methode zum Aktualisieren des `state` einer Komponente namens `setState`. Du rufst die Methode `setState` innerhalb deiner Komponentenklasse wie folgt auf: `this.setState()`, wobei du ein Objekt mit Schlüssel-Wert-Paaren übergibst. Die Schlüssel sind deine Zustandseigenschaften und die Werte sind die aktualisierten Zustandsdaten. Wenn wir zum Beispiel einen `username` im Zustand speichern und ihn aktualisieren wollen, würde es so aussehen:
+
+```jsx
+this.setState({
+ username: 'Lewis'
+});
+```
+
+React erwartet, dass du den `state` nie direkt änderst, sondern immer `this.setState()` verwendest, wenn Zustandsänderungen auftreten. Außerdem solltest du beachten, dass React mehrere Zustandsaktualisierungen stapeln kann, um die Leistung zu verbessern. Das bedeutet, dass Zustandsaktualisierungen durch die Methode `setState` asynchron sein können. Es gibt eine alternative Syntax für die Methode `setState`, mit der sich dieses Problem umgehen lässt. Das ist zwar selten nötig, aber es ist gut, es im Hinterkopf zu behalten! Weitere Informationen findest du in unserem React Artikel .
+
+# --instructions--
+
+Im Code-Editor gibt es ein `button`-Element, das einen `onClick()`-Handler hat. Dieser Handler wird ausgelöst, wenn der `button` ein Klick-Ereignis im Browser erhält und führt die Methode `handleClick` aus, die auf `MyComponent` definiert ist. Innerhalb der Methode `handleClick` aktualisierst du den `state` der Komponente mit `this.setState()`. Setze die Eigenschaft `name` in `state` auf den String `React Rocks!`.
+
+Klicke auf den Button und beobachte, wie der gerenderte Zustand aktualisiert wird. Mach dir keine Sorgen, wenn du an dieser Stelle noch nicht ganz verstehst, wie der Klick-Handler-Code funktioniert. Das wird in den kommenden Aufgaben behandelt.
+
+# --hints--
+
+Der Zustand von `MyComponent` sollte mit dem Schlüssel-Wert-Paar `{ name: Initial State }` initialisiert werden.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MyComponent)).state('name') ===
+ 'Initial State'
+);
+```
+
+`MyComponent` sollte ein `h1`-Überschriftenelement darstellen.
+
+```js
+assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
+```
+
+Das gerenderte `h1`-Überschriftenelement sollte Text enthalten, der aus dem Zustand der Komponente gerendert wurde.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ name: 'TestName' });
+ return waitForIt(() => mockedComponent.html());
+ };
+ const firstValue = await first();
+ assert(/TestName<\/h1>/.test(firstValue));
+};
+```
+
+Der Aufruf der Methode `handleClick` auf `MyComponent` sollte die Eigenschaft name in state auf den Wert `React Rocks!` setzen.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ name: 'Before' });
+ return waitForIt(() => mockedComponent.state('name'));
+ };
+ const second = () => {
+ mockedComponent.instance().handleClick();
+ return waitForIt(() => mockedComponent.state('name'));
+ };
+ const firstValue = await first();
+ const secondValue = await second();
+ assert(firstValue === 'Before' && secondValue === 'React Rocks!');
+};
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render( , document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'Initial State'
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ // Change code below this line
+
+ // Change code above this line
+ }
+ render() {
+ return (
+
+ Click Me
+
{this.state.name}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ name: 'Initial State'
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ // Change code below this line
+ this.setState({
+ name: 'React Rocks!'
+ });
+ // Change code above this line
+ }
+ render() {
+ return (
+
+ Click Me
+
{this.state.name}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use--for-a-more-concise-conditional.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use--for-a-more-concise-conditional.md
new file mode 100644
index 00000000000..466066a5708
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use--for-a-more-concise-conditional.md
@@ -0,0 +1,146 @@
+---
+id: 5a24c314108439a4d4036185
+title: Verwende && für eine präzisere Bedingung
+challengeType: 6
+forumTopicId: 301413
+dashedName: use--for-a-more-concise-conditional
+---
+
+# --description--
+
+Die `if/else`-Anweisungen haben in der letzten Aufgabe funktioniert, aber es gibt einen präziseren Weg, um das gleiche Ergebnis zu erzielen. Stell dir vor, du verfolgst mehrere Bedingungen in einer Komponente und du möchtest, dass je nach Bedingung unterschiedliche Elemente gerendert werden. Wenn du viele `else if`-Anweisungen schreibst, um leicht unterschiedliche Benutzeroberflächen (UIs) zurückzugeben, wiederholst du möglicherweise Code, der Raum für Fehler lässt. Stattdessen kannst du den logischen Operator `&&` verwenden, um bedingte Logik auf eine präzisere Weise auszuführen. Das ist möglich, weil du prüfen willst, ob eine Bedingung `true` ist, und wenn ja, ein Markup zurückgeben willst. Hier ist ein Beispiel:
+
+```jsx
+{condition && markup
}
+```
+
+Wenn die Bedingung (`condition`) `true` ist, wird das Markup zurückgegeben. Wenn die Bedingung `false` ist, gibt die Operation sofort `false` zurück, nachdem sie die `condition` ausgewertet hat und liefert nichts zurück. Du kannst diese Anweisungen direkt in dein JSX einfügen und mehrere Bedingungen aneinanderreihen, indem du `&&` nach jeder Bedingung schreibst. So kannst du komplexere bedingte Logik in deiner `render()`-Methode verarbeiten, ohne eine Menge Code zu wiederholen.
+
+# --instructions--
+
+Löse das vorherige Beispiel erneut, so dass das `h1` nur gerendert wird, wenn `display` `true` ist, aber verwende den logischen Operator `&&` anstelle einer `if/else`-Anweisung.
+
+# --hints--
+
+`MyComponent` sollte existieren und gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('MyComponent').length;
+ })()
+);
+```
+
+Wenn `display` auf `true` gesetzt ist, sollten ein `div`, ein `button` und ein `h1` gerendert werden.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const state_1 = () => {
+ mockedComponent.setState({ display: true });
+ return waitForIt(() => mockedComponent);
+ };
+ const updated = await state_1();
+ assert(
+ updated.find('div').length === 1 &&
+ updated.find('div').children().length === 2 &&
+ updated.find('button').length === 1 &&
+ updated.find('h1').length === 1
+ );
+};
+```
+
+Wenn `display` auf `false` gesetzt ist, sollten nur ein `div` und ein `button` gerendert werden.
+
+```js
+async () => {
+ const waitForIt = (fn) =>
+ new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const state_1 = () => {
+ mockedComponent.setState({ display: false });
+ return waitForIt(() => mockedComponent);
+ };
+ const updated = await state_1();
+ assert(
+ updated.find('div').length === 1 &&
+ updated.find('div').children().length === 1 &&
+ updated.find('button').length === 1 &&
+ updated.find('h1').length === 0
+ );
+};
+```
+
+Die Render-Methode sollte den logischen Operator `&&` verwenden, um die Bedingung von `this.state.display` zu überprüfen.
+
+```js
+(getUserInput) => assert(getUserInput('index').includes('&&'));
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ display: true
+ }
+ this.toggleDisplay = this.toggleDisplay.bind(this);
+ }
+ toggleDisplay() {
+ this.setState(state => ({
+ display: !state.display
+ }));
+ }
+ render() {
+ // Change code below this line
+ return (
+
+ Toggle Display
+
Displayed!
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ display: true
+ }
+ this.toggleDisplay = this.toggleDisplay.bind(this);
+ }
+ toggleDisplay() {
+ this.setState(state => ({
+ display: !state.display
+ }));
+ }
+ render() {
+ // Change code below this line
+ return (
+
+ Toggle Display
+ {this.state.display &&
Displayed! }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-a-ternary-expression-for-conditional-rendering.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-a-ternary-expression-for-conditional-rendering.md
new file mode 100644
index 00000000000..93db03fb911
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-a-ternary-expression-for-conditional-rendering.md
@@ -0,0 +1,280 @@
+---
+id: 5a24c314108439a4d4036187
+title: Verwende einen ternären Ausdruck für bedingtes Rendering
+challengeType: 6
+forumTopicId: 301414
+dashedName: use-a-ternary-expression-for-conditional-rendering
+---
+
+# --description--
+
+Bevor wir uns den dynamischen Rendering-Techniken zuwenden, gibt es noch eine letzte Möglichkeit, die eingebauten JavaScript-Bedingungen zu nutzen, um das zu rendern, was du willst: den
ternären Operator . Der ternäre Operator wird oft als Abkürzung für `if/else`-Anweisungen in JavaScript verwendet. Sie sind nicht ganz so robust wie traditionelle `if/else`-Anweisungen, aber sie sind bei React-Entwicklern sehr beliebt. Ein Grund dafür ist, dass aufgrund der Art und Weise, wie JSX kompiliert wird, `if/else`-Anweisungen nicht direkt in JSX-Code eingefügt werden können. Du hast das vielleicht schon vor ein paar Aufgaben bemerkt - wenn eine `if/else`-Anweisung erforderlich war, war sie immer *außerhalb* der `return`-Anweisung. Ternäre Ausdrücke können eine hervorragende Alternative sein, wenn du bedingte Logik in deine JSX implementieren willst. Erinnere dich daran, dass ein ternärer Operator aus drei Teilen besteht, aber du kannst mehrere ternäre Ausdrücke miteinander kombinieren. Hier ist die grundlegende Syntax:
+
+```jsx
+condition ? expressionIfTrue : expressionIfFalse;
+```
+
+# --instructions--
+
+Der Code-Editor hat drei Konstanten in der `CheckUserAge`-Methode der Komponente `render()` definiert. Sie heißen `buttonOne`, `buttonTwo` und `buttonThree`. Jedem dieser Elemente ist ein einfacher JSX-Ausdruck zugeordnet, der ein Button-Element darstellt. Zuerst initialisierst du den Zustand von `CheckUserAge` mit `input` und `userAge`, die beide auf einen leeren String gesetzt sind.
+
+Sobald die Komponente Informationen an die Seite überträgt, sollten die Nutzer eine Möglichkeit haben, mit ihr zu interagieren. In der `return`-Anweisung der Komponente erstellst du einen ternären Ausdruck, der die folgende Logik implementiert: Wenn die Seite zum ersten Mal geladen wird, wird der Submit-Button `buttonOne` auf der Seite angezeigt. Wenn ein Benutzer sein Alter eingibt und auf den Button klickt, wird je nach Alter ein anderer Button angezeigt. Wenn ein Benutzer eine Zahl kleiner als `18` eingibt, wird `buttonThree` angezeigt. Wenn ein Benutzer eine Zahl größer oder gleich `18` eingibt, wird `buttonTwo` angezeigt.
+
+# --hints--
+
+Die Komponente `CheckUserAge` sollte mit einem einzelnen `input`-Element und einem einzelnen `button`-Element dargestellt werden.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input')
+ .length === 1 &&
+ Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button')
+ .length === 1
+);
+```
+
+Der Zustand der Komponente `CheckUserAge` sollte mit einer Eigenschaft von `userAge` und einer Eigenschaft von `input` initialisiert werden, die beide auf einen leeren String gesetzt sind.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' &&
+ Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === ''
+);
+```
+
+Wenn die Komponente `CheckUserAge` zum ersten Mal in das DOM gerendert wird, sollte der innere Text des `button` "Submit" sein.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() ===
+ 'Submit'
+);
+```
+
+Wenn eine Zahl kleiner als 18 in das `input`-Element eingegeben und der `button` angeklickt wird, sollte der innere Text des `button` lauten: `You Shall Not Pass`.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
+ const initialButton = mockedComponent.find('button').text();
+ const enter3AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '3' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const enter17AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '17' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const userAge3 = enter3AndClickButton();
+ const userAge17 = enter17AndClickButton();
+ assert(
+ initialButton === 'Submit' &&
+ userAge3 === 'You Shall Not Pass' &&
+ userAge17 === 'You Shall Not Pass'
+ );
+})();
+```
+
+Wenn eine Zahl größer oder gleich 18 in das `input`-Element eingegeben und der `button` angeklickt wird, sollte der innere Text des `button` lauten: `You May Enter`.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
+ const initialButton = mockedComponent.find('button').text();
+ const enter18AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '18' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const enter35AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '35' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const userAge18 = enter18AndClickButton();
+ const userAge35 = enter35AndClickButton();
+ assert(
+ initialButton === 'Submit' &&
+ userAge18 === 'You May Enter' &&
+ userAge35 === 'You May Enter'
+ );
+})();
+```
+
+Sobald eine Zahl übermittelt wurde und der Wert des `input` erneut geändert wird, sollte der `button` wieder `Submit` anzeigen.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
+ const enter18AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '18' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const changeInputDontClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '5' } });
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const enter10AndClickButton = () => {
+ mockedComponent
+ .find('input')
+ .simulate('change', { target: { value: '10' } });
+ mockedComponent.find('button').simulate('click');
+ mockedComponent.update();
+ return mockedComponent.find('button').text();
+ };
+ const userAge18 = enter18AndClickButton();
+ const changeInput1 = changeInputDontClickButton();
+ const userAge10 = enter10AndClickButton();
+ const changeInput2 = changeInputDontClickButton();
+ assert(
+ userAge18 === 'You May Enter' &&
+ changeInput1 === 'Submit' &&
+ userAge10 === 'You Shall Not Pass' &&
+ changeInput2 === 'Submit'
+ );
+})();
+```
+
+Dein Code sollte keine `if/else`-Anweisungen enthalten.
+
+```js
+assert(
+ new RegExp(/(\s|;)if(\s|\()/).test(
+ code
+ ) === false
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+const inputStyle = {
+ width: 235,
+ margin: 5
+};
+
+class CheckUserAge extends React.Component {
+ constructor(props) {
+ super(props);
+ // Change code below this line
+
+ // Change code above this line
+ this.submit = this.submit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(e) {
+ this.setState({
+ input: e.target.value,
+ userAge: ''
+ });
+ }
+ submit() {
+ this.setState(state => ({
+ userAge: state.input
+ }));
+ }
+ render() {
+ const buttonOne =
Submit ;
+ const buttonTwo =
You May Enter ;
+ const buttonThree =
You Shall Not Pass ;
+ return (
+
+
Enter Your Age to Continue
+
+
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+const inputStyle = {
+ width: 235,
+ margin: 5
+};
+
+class CheckUserAge extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ userAge: '',
+ input: ''
+ };
+ this.submit = this.submit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleChange(e) {
+ this.setState({
+ input: e.target.value,
+ userAge: ''
+ });
+ }
+ submit() {
+ this.setState(state => ({
+ userAge: state.input
+ }));
+ }
+ render() {
+ const buttonOne =
Submit ;
+ const buttonTwo =
You May Enter ;
+ const buttonThree =
You Shall Not Pass ;
+ return (
+
+
Enter Your Age to Continue
+
+
+ {this.state.userAge === ''
+ ? buttonOne
+ : this.state.userAge >= 18
+ ? buttonTwo
+ : buttonThree}
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-advanced-javascript-in-react-render-method.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-advanced-javascript-in-react-render-method.md
new file mode 100644
index 00000000000..88044035743
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-advanced-javascript-in-react-render-method.md
@@ -0,0 +1,334 @@
+---
+id: 5a24c314108439a4d4036183
+title: Verwende fortgeschrittenes JavaScript in der React-Rendering-Methode
+challengeType: 6
+forumTopicId: 301415
+dashedName: use-advanced-javascript-in-react-render-method
+---
+
+# --description--
+
+In früheren Aufgaben hast du gelernt, wie du mit geschweiften Klammern (`{ }`) JavaScript-Code in JSX-Code einfügst, z. B. für den Zugriff auf Eigenschaften (props), die Übergabe von Eigenschaften, den Zugriff auf den Zustand (state), das Einfügen von Kommentaren in deinen Code und vor allem für das Styling deiner Komponenten. Das sind alles gängige Anwendungsfälle für JavaScript in JSX, aber sie sind nicht die einzige Möglichkeit, wie du JavaScript-Code in deinen React-Komponenten verwenden kannst.
+
+Du kannst JavaScript auch direkt in deine `render`-Methoden schreiben, vor der `return`-Anweisung, ***ohne*** es in geschweifte Klammern einzufügen. Das liegt daran, dass sie noch nicht im JSX-Code enthalten ist. Wenn du eine Variable später im JSX-Code *innerhalb* der `return`-Anweisung verwenden willst, setzt du den Variablennamen in geschweifte Klammern.
+
+# --instructions--
+
+Im angegebenen Code enthält die `render`-Methode ein Array mit 20 Phrasen, die die Antworten aus dem klassischen Magic Eight Ball aus den 1980er Jahren darstellen. Der Klick-Event ist an die Methode `ask` gebunden. Jedes Mal, wenn der Button geklickt wird, wird eine Zufallszahl erzeugt und als `randomIndex` im Zustand gespeichert. Lösche in Zeile 52 den String `change me!` und weise die `answer`-Konstante neu zu, damit dein Code bei jeder Aktualisierung der Komponente zufällig auf einen anderen Index des Arrays `possibleAnswers` zugreift. Zum Schluss fügst du die `answer`-Konstante innerhalb des `p`-Tags ein.
+
+# --hints--
+
+Die Komponente `MagicEightBall` sollte existieren und auf der Seite dargestellt werden.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall')
+ .length,
+ 1
+);
+```
+
+Das erste Kindelement von `MagicEightBall` sollte ein `input`-Element sein.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MagicEightBall))
+ .children()
+ .childAt(0)
+ .name(),
+ 'input'
+);
+```
+
+Das dritte Kindelement von `MagicEightBall` sollte ein `button`-Element sein.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MagicEightBall))
+ .children()
+ .childAt(2)
+ .name(),
+ 'button'
+);
+```
+
+Der Zustand von `MagicEightBall` sollte mit einer Eigenschaft `userInput` und einer Eigenschaft `randomIndex` initialisiert werden, die beide auf einen leeren String gesetzt sind.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') ===
+ '' &&
+ Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === ''
+);
+```
+
+Wenn `MagicEightBall` zum ersten Mal in das DOM eingebunden wird, sollte es ein leeres `p`-Element zurückgeben.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 &&
+ Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === ''
+);
+```
+
+Wenn du Text in das `input`-Element eingibst und auf den Button klickst, sollte die Komponente `MagicEightBall` ein `p`-Element zurückgeben, das ein zufälliges Element aus dem Array `possibleAnswers` enthält.
+
+```js
+(() => {
+ const comp = Enzyme.mount(React.createElement(MagicEightBall));
+ const simulate = () => {
+ comp.find('input').simulate('change', { target: { value: 'test?' } });
+ comp.find('button').simulate('click');
+ };
+ const result = () => comp.find('p').text();
+ const _1 = () => {
+ simulate();
+ return result();
+ };
+ const _2 = () => {
+ simulate();
+ return result();
+ };
+ const _3 = () => {
+ simulate();
+ return result();
+ };
+ const _4 = () => {
+ simulate();
+ return result();
+ };
+ const _5 = () => {
+ simulate();
+ return result();
+ };
+ const _6 = () => {
+ simulate();
+ return result();
+ };
+ const _7 = () => {
+ simulate();
+ return result();
+ };
+ const _8 = () => {
+ simulate();
+ return result();
+ };
+ const _9 = () => {
+ simulate();
+ return result();
+ };
+ const _10 = () => {
+ simulate();
+ return result();
+ };
+ const _1_val = _1();
+ const _2_val = _2();
+ const _3_val = _3();
+ const _4_val = _4();
+ const _5_val = _5();
+ const _6_val = _6();
+ const _7_val = _7();
+ const _8_val = _8();
+ const _9_val = _9();
+ const _10_val = _10();
+ const actualAnswers = [
+ _1_val,
+ _2_val,
+ _3_val,
+ _4_val,
+ _5_val,
+ _6_val,
+ _7_val,
+ _8_val,
+ _9_val,
+ _10_val
+ ];
+ const hasIndex = actualAnswers.filter(
+ (answer, i) => possibleAnswers.indexOf(answer) !== -1
+ );
+ const notAllEqual = new Set(actualAnswers);
+ assert(notAllEqual.size > 1 && hasIndex.length === 10);
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+var possibleAnswers = [
+ 'It is certain',
+ 'It is decidedly so',
+ 'Without a doubt',
+ 'Yes, definitely',
+ 'You may rely on it',
+ 'As I see it, yes',
+ 'Outlook good',
+ 'Yes',
+ 'Signs point to yes',
+ 'Reply hazy try again',
+ 'Ask again later',
+ 'Better not tell you now',
+ 'Cannot predict now',
+ 'Concentrate and ask again',
+ "Don't count on it",
+ 'My reply is no',
+ 'My sources say no',
+ 'Outlook not so good',
+ 'Very doubtful',
+ 'Most likely'
+];
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+const inputStyle = {
+ width: 235,
+ margin: 5
+};
+
+class MagicEightBall extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ userInput: '',
+ randomIndex: ''
+ };
+ this.ask = this.ask.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ ask() {
+ if (this.state.userInput) {
+ this.setState({
+ randomIndex: Math.floor(Math.random() * 20),
+ userInput: ''
+ });
+ }
+ }
+ handleChange(event) {
+ this.setState({
+ userInput: event.target.value
+ });
+ }
+ render() {
+ const possibleAnswers = [
+ 'It is certain',
+ 'It is decidedly so',
+ 'Without a doubt',
+ 'Yes, definitely',
+ 'You may rely on it',
+ 'As I see it, yes',
+ 'Outlook good',
+ 'Yes',
+ 'Signs point to yes',
+ 'Reply hazy try again',
+ 'Ask again later',
+ 'Better not tell you now',
+ 'Cannot predict now',
+ 'Concentrate and ask again',
+ "Don't count on it",
+ 'My reply is no',
+ 'My sources say no',
+ 'Most likely',
+ 'Outlook not so good',
+ 'Very doubtful'
+ ];
+ const answer = 'change me!'; // Change this line
+ return (
+
+
+
+
Ask the Magic Eight Ball!
+
+
Answer:
+
+ {/* Change code below this line */}
+
+ {/* Change code above this line */}
+
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+const inputStyle = {
+ width: 235,
+ margin: 5
+};
+
+class MagicEightBall extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ userInput: '',
+ randomIndex: ''
+ };
+ this.ask = this.ask.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ ask() {
+ if (this.state.userInput) {
+ this.setState({
+ randomIndex: Math.floor(Math.random() * 20),
+ userInput: ''
+ });
+ }
+ }
+ handleChange(event) {
+ this.setState({
+ userInput: event.target.value
+ });
+ }
+ render() {
+ const possibleAnswers = [
+ 'It is certain',
+ 'It is decidedly so',
+ 'Without a doubt',
+ 'Yes, definitely',
+ 'You may rely on it',
+ 'As I see it, yes',
+ 'Outlook good',
+ 'Yes',
+ 'Signs point to yes',
+ 'Reply hazy try again',
+ 'Ask again later',
+ 'Better not tell you now',
+ 'Cannot predict now',
+ 'Concentrate and ask again',
+ "Don't count on it",
+ 'My reply is no',
+ 'My sources say no',
+ 'Outlook not so good',
+ 'Very doubtful',
+ 'Most likely'
+ ];
+ const answer = possibleAnswers[this.state.randomIndex];
+ return (
+
+
+
+
Ask the Magic Eight Ball!
+
+
Answer:
+
{answer}
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.filter-to-dynamically-filter-an-array.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.filter-to-dynamically-filter-an-array.md
new file mode 100644
index 00000000000..1c66c996247
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.filter-to-dynamically-filter-an-array.md
@@ -0,0 +1,236 @@
+---
+id: 5a24c314108439a4d403618c
+title: Verwende Array.filter(), um ein Array dynamisch zu filtern
+challengeType: 6
+forumTopicId: 301416
+dashedName: use-array-filter-to-dynamically-filter-an-array
+---
+
+# --description--
+
+Die `map`-Array-Methode ist ein mächtiges Werkzeug, das du bei der Arbeit mit React oft verwenden wirst. Eine andere Methode, die mit `map` verwandt ist, ist `filter`, die den Inhalt eines Arrays anhand einer Bedingung filtert und dann ein neues Array zurückgibt. Wenn du zum Beispiel ein Array von Benutzern hast, die alle eine Eigenschaft `online` besitzen, die auf `true` oder `false` gesetzt werden kann, kannst du nur die Benutzer filtern, die online sind, indem du schreibst:
+
+```js
+let onlineUsers = users.filter(user => user.online);
+```
+
+# --instructions--
+
+Im Code-Editor wird der `state` von `MyComponent` mit einem Array von Benutzern initialisiert. Einige Nutzer sind online, andere nicht. Filtere das Feld so, dass du nur die Nutzer siehst, die online sind. Dazu verwendest du zunächst `filter`, um ein neues Array zu erstellen, das nur die Benutzer enthält, deren `online`-Eigenschaft `true` ist. In der Variablen `renderOnline` mappst du dann das gefilterte Array und gibst für jeden Benutzer ein `li`-Element zurück, das den Text seines `username` enthält. Achte darauf, dass du auch einen eindeutigen `key` einfügst, wie in den letzten Aufgaben.
+
+# --hints--
+
+`MyComponent` sollte existieren und auf der Seite dargestellt werden.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length,
+ 1
+);
+```
+
+`MyComponent`sollte mit einem Array von sechs Benutzern initialisiert werden.
+
+```js
+assert(
+ Array.isArray(
+ Enzyme.mount(React.createElement(MyComponent)).state('users')
+ ) === true &&
+ Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6
+);
+```
+
+`MyComponent` sollte ein `div`, ein `h1` und dann eine ungeordnete Liste mit `li`-Elementen für jeden Benutzer zurückgeben, dessen Online-Status auf `true` gesetzt ist.
+
+```js
+(() => {
+ const comp = Enzyme.mount(React.createElement(MyComponent));
+ const users = (bool) => ({
+ users: [
+ { username: 'Jeff', online: bool },
+ { username: 'Alan', online: bool },
+ { username: 'Mary', online: bool },
+ { username: 'Jim', online: bool },
+ { username: 'Laura', online: bool }
+ ]
+ });
+ const result = () => comp.find('li').length;
+ const _1 = result();
+ const _2 = () => {
+ comp.setState(users(true));
+ return result();
+ };
+ const _3 = () => {
+ comp.setState(users(false));
+ return result();
+ };
+ const _4 = () => {
+ comp.setState({ users: [] });
+ return result();
+ };
+ const _2_val = _2();
+ const _3_val = _3();
+ const _4_val = _4();
+ assert(
+ comp.find('div').length === 1 &&
+ comp.find('h1').length === 1 &&
+ comp.find('ul').length === 1 &&
+ _1 === 4 &&
+ _2_val === 5 &&
+ _3_val === 0 &&
+ _4_val === 0
+ );
+})();
+```
+
+`MyComponent` sollte `li`-Elemente darstellen, die den `username` jedes Online-Nutzers enthalten.
+
+```js
+(() => {
+ const comp = Enzyme.mount(React.createElement(MyComponent));
+ const users = (bool) => ({
+ users: [
+ { username: 'Jeff', online: bool },
+ { username: 'Alan', online: bool },
+ { username: 'Mary', online: bool },
+ { username: 'Jim', online: bool },
+ { username: 'Laura', online: bool }
+ ]
+ });
+ const ul = () => {
+ comp.setState(users(true));
+ return comp.find('ul').html();
+ };
+ const html = ul();
+ assert(
+ html ===
+ '
'
+ );
+})();
+```
+
+Jedes Listenelement sollte ein eindeutiges `key`-Attribut besitzen.
+
+```js
+assert(
+ (() => {
+ const ul = Enzyme.mount(React.createElement(MyComponent)).find('ul');
+ console.log(ul.debug());
+ const keys = new Set([
+ ul.childAt(0).key(),
+ ul.childAt(1).key(),
+ ul.childAt(2).key(),
+ ul.childAt(3).key()
+ ]);
+ return keys.size === 4;
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ users: [
+ {
+ username: 'Jeff',
+ online: true
+ },
+ {
+ username: 'Alan',
+ online: false
+ },
+ {
+ username: 'Mary',
+ online: true
+ },
+ {
+ username: 'Jim',
+ online: false
+ },
+ {
+ username: 'Sara',
+ online: true
+ },
+ {
+ username: 'Laura',
+ online: true
+ }
+ ]
+ };
+ }
+ render() {
+ const usersOnline = null; // Change this line
+ const renderOnline = null; // Change this line
+ return (
+
+
Current Online Users:
+
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ users: [
+ {
+ username: 'Jeff',
+ online: true
+ },
+ {
+ username: 'Alan',
+ online: false
+ },
+ {
+ username: 'Mary',
+ online: true
+ },
+ {
+ username: 'Jim',
+ online: false
+ },
+ {
+ username: 'Sara',
+ online: true
+ },
+ {
+ username: 'Laura',
+ online: true
+ }
+ ]
+ };
+ }
+ render() {
+ const usersOnline = this.state.users.filter(user => {
+ return user.online;
+ });
+ const renderOnline = usersOnline.map(user => {
+ return
{user.username} ;
+ });
+ return (
+
+
Current Online Users:
+
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.map-to-dynamically-render-elements.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.map-to-dynamically-render-elements.md
new file mode 100644
index 00000000000..45f24c5acd2
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-array.map-to-dynamically-render-elements.md
@@ -0,0 +1,265 @@
+---
+id: 5a24c314108439a4d403618a
+title: Verwende Array.map(), um Elemente dynamisch zu rendern
+challengeType: 6
+forumTopicId: 301417
+dashedName: use-array-map-to-dynamically-render-elements
+---
+
+# --description--
+
+Bedingungsabhängiges Rendering ist nützlich, aber es kann sein, dass deine Komponenten eine unbekannte Anzahl von Elementen rendern müssen. Bei der reaktiven Programmierung hat ein Programmierer oft keine Möglichkeit, den Zustand einer Anwendung bis zur Laufzeit zu kennen, weil so viel von der Interaktion des Benutzers mit dem Programm abhängt. Die Programmierer müssen ihren Code so schreiben, dass er diesen unbekannten Zustand im Voraus richtig behandelt. Die Verwendung von `Array.map()` in React veranschaulicht dieses Konzept.
+
+Du erstellst zum Beispiel eine einfache "To Do List"-App. Als Programmierer hast du keine Möglichkeit zu wissen, wie viele Punkte ein Nutzer auf seiner Liste haben könnte. Du musst deine Komponente so einrichten, dass sie dynamisch die richtige Anzahl von Listenelementen darstellt, lange bevor jemand, der das Programm benutzt, entscheidet, dass heute Wäschetag ist.
+
+# --instructions--
+
+Im Code-Editor ist der größte Teil der Komponente `MyToDoList` bereits eingerichtet. Einige dieser Codes sollten dir bekannt vorkommen, wenn du die Aufgabe über kontrollierte Formulare gelöst hast. Du wirst ein `textarea` und einen `button` sehen, zusammen mit ein paar Methoden, die ihre Zustände verfolgen, aber noch wird nichts auf der Seite gerendert.
+
+Erstelle im `constructor` ein `this.state`-Objekt und definiere zwei Zustände: `userInput` sollte als leerer String initialisiert werden und `toDoList` als leeres Array. Lösche als Nächstes den `null`-Wert in der `render()`-Methode neben der `items`-Variablen. Übertrage stattdessen das `toDoList`-Array, das im internen Zustand der Komponente gespeichert ist, und rendere dynamisch ein `li` für jedes Element. Versuche, den String `eat, code, sleep, repeat` in das `textarea`-Element einzugeben, klicke dann auf den Button und schau, was passiert.
+
+**Hinweis:** Du weißt vielleicht, dass alle Geschwister-Elemente, die durch eine Zuordnungsoperation wie diese erstellt werden, mit einem eindeutigen `key`-Attribut versehen werden müssen. Mach dir keine Sorgen, das ist das Thema der nächsten Aufgabe.
+
+# --hints--
+
+Die Komponente MyToDoList sollte existieren und auf der Seite angezeigt werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ return mockedComponent.find('MyToDoList').length === 1;
+ })()
+);
+```
+
+Das erste Kindelement von `MyToDoList` sollte ein `textarea`-Element sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ return (
+ mockedComponent.find('MyToDoList').children().childAt(0).type() ===
+ 'textarea'
+ );
+ })()
+);
+```
+
+Das zweite Kindelement von `MyToDoList` sollte ein `br`-Element sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ return (
+ mockedComponent.find('MyToDoList').children().childAt(1).type() === 'br'
+ );
+ })()
+);
+```
+
+Das dritte Kindelement von `MyToDoList` sollte ein `button`-Element sein.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ return (
+ mockedComponent.find('MyToDoList').children().childAt(2).type() ===
+ 'button'
+ );
+ })()
+);
+```
+
+Der Zustand von `MyToDoList` sollte mit `toDoList` als leeres Array initialisiert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ const initialState = mockedComponent.state();
+ return (
+ Array.isArray(initialState.toDoList) === true &&
+ initialState.toDoList.length === 0
+ );
+ })()
+);
+```
+
+Der Zustand von `MyToDoList` sollte mit `userInput` als Leerstring initialisiert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ const initialState = mockedComponent.state();
+ return (
+ typeof initialState.userInput === 'string' &&
+ initialState.userInput.length === 0
+ );
+ })()
+);
+```
+
+Wenn du auf den Button `Create List` klickst, sollte die Komponente `MyToDoList` dynamisch eine ungeordnete Liste zurückgeben, die für jedes Element einer kommagetrennten Liste, die in das `textarea`-Element eingegeben wird, ein Listenelement enthält.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
+ const simulateChange = (el, value) =>
+ el.simulate('change', { target: { value } });
+ const state_1 = () => {
+ return mockedComponent.find('ul').find('li');
+ };
+ const setInput = () => {
+ return simulateChange(
+ mockedComponent.find('textarea'),
+ 'testA, testB, testC'
+ );
+ };
+ const click = () => {
+ return mockedComponent.find('button').simulate('click');
+ };
+ const state_2 = () => {
+ const nodes = mockedComponent.find('ul').find('li');
+ return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
+ };
+ const setInput_2 = () => {
+ return simulateChange(
+ mockedComponent.find('textarea'),
+ 't1, t2, t3, t4, t5, t6'
+ );
+ };
+ const click_1 = () => {
+ return mockedComponent.find('button').simulate('click');
+ };
+ const state_3 = () => {
+ const nodes = mockedComponent.find('ul').find('li');
+ return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
+ };
+ const awaited_state_1 = state_1();
+ const awaited_setInput = setInput();
+ const awaited_click = click();
+ const awaited_state_2 = state_2();
+ const awaited_setInput_2 = setInput_2();
+ const awaited_click_1 = click_1();
+ const awaited_state_3 = state_3();
+ assert(
+ awaited_state_1.length === 0 &&
+ awaited_state_2.nodes.length === 3 &&
+ awaited_state_3.nodes.length === 6 &&
+ awaited_state_2.text === 'testAtestBtestC' &&
+ awaited_state_3.text === 't1t2t3t4t5t6'
+ );
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+const textAreaStyles = {
+ width: 235,
+ margin: 5
+};
+
+class MyToDoList extends React.Component {
+ constructor(props) {
+ super(props);
+ // Change code below this line
+
+ // Change code above this line
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleSubmit() {
+ const itemsArray = this.state.userInput.split(',');
+ this.setState({
+ toDoList: itemsArray
+ });
+ }
+ handleChange(e) {
+ this.setState({
+ userInput: e.target.value
+ });
+ }
+ render() {
+ const items = null; // Change this line
+ return (
+
+
+
+
Create List
+
My "To Do" List:
+
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+const textAreaStyles = {
+ width: 235,
+ margin: 5
+};
+
+class MyToDoList extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ toDoList: [],
+ userInput: ''
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+ handleSubmit() {
+ const itemsArray = this.state.userInput.split(',');
+ this.setState({
+ toDoList: itemsArray
+ });
+ }
+ handleChange(e) {
+ this.setState({
+ userInput: e.target.value
+ });
+ }
+ render() {
+ const items = this.state.toDoList.map((item, i) => {
+ return
{item} ;
+ });
+ return (
+
+
+
+
Create List
+
My "To Do" List:
+
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-default-props.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-default-props.md
new file mode 100644
index 00000000000..71af5f91da7
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-default-props.md
@@ -0,0 +1,78 @@
+---
+id: 5a24c314108439a4d403616b
+title: Standardeigenschaft verwenden
+challengeType: 6
+forumTopicId: 301418
+dashedName: use-default-props
+---
+
+# --description--
+
+React hat auch eine Option, um Standard-Props zu setzen. Du kannst einer Komponente Standardeigenschaft als Eigenschaft der Komponente selbst zuweisen und React weist die Standardeigenschaft bei Bedarf zu. Damit kannst du festlegen, wie ein Prop-Wert aussehen soll, wenn kein Wert explizit angegeben wird. Wenn du zum Beispiel `MyComponent.defaultProps = { location: 'San Francisco' }` deklarierst, hast du eine Locationeigenschaft definiert, die auf den String `San Francisco` gesetzt wird, sofern du nichts anderes angibst. React weist Standardeigenschaften zu, wenn Eigenschaften undefiniert sind, wenn du aber `null` als Wert für eine Eigenschaft übergibst, bleibt sie `null`.
+
+# --instructions--
+
+Der Code-Editor zeigt eine Komponente `ShoppingCart`. Definiere Standardeigenschaften für diese Komponente, die eine Eigenschaft `items` mit einem Wert von `0` enthält.
+
+# --hints--
+
+Die Komponente `ShoppingCart` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return mockedComponent.find('ShoppingCart').length === 1;
+ })()
+);
+```
+
+Die Komponente `ShoppingCart` sollte eine Standardeinstellung von `{ items: 0 }` besitzen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ mockedComponent.setProps({ items: undefined });
+ return mockedComponent.find('ShoppingCart').props().items === 0;
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const ShoppingCart = (props) => {
+ return (
+
+
Shopping Cart Component
+
+ )
+};
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+const ShoppingCart = (props) => {
+ return (
+
+
Shopping Cart Component
+
+ )
+};
+
+// Change code below this line
+ShoppingCart.defaultProps = {
+ items: 0
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-proptypes-to-define-the-props-you-expect.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-proptypes-to-define-the-props-you-expect.md
new file mode 100644
index 00000000000..fa24b784729
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-proptypes-to-define-the-props-you-expect.md
@@ -0,0 +1,132 @@
+---
+id: 5a24c314108439a4d403616d
+title: Verwende PropTypes, um die Eigenschaften zu definieren, die du erwartest
+challengeType: 6
+forumTopicId: 301419
+dashedName: use-proptypes-to-define-the-props-you-expect
+---
+
+# --description--
+
+React bietet nützliche Funktionen zur Typüberprüfung, um sicherzustellen, dass Komponenten Eigenschaften des richtigen Typs erhalten. Deine Anwendung macht zum Beispiel einen API-Aufruf, um Daten abzurufen, die du in einem Array erwartest, das dann als Eigenschaft an eine Komponente übergeben wird. Du kannst `propTypes` in deiner Komponente so einstellen, dass die Daten vom Typ `array` sein müssen. Dies führt zu einer nützlichen Warnung, wenn die Daten von einem anderen Typ sind.
+
+Es gilt als bewährte Methode, `propTypes` zu setzen, wenn du den Typ einer Eigenschaft schon vorher kennst. Du kannst eine `propTypes`-Eigenschaft für eine Komponente auf dieselbe Weise definieren wie `defaultProps`. Dadurch wird geprüft, ob Eigenschaften eines bestimmten Schlüssels mit einem bestimmten Typ vorhanden sind. Hier ist ein Beispiel, das den Typ `function` für eine Eigenschaft namens `handleClick` erfordert:
+
+```js
+MyComponent.propTypes = { handleClick: PropTypes.func.isRequired }
+```
+
+Im obigen Beispiel prüft der `PropTypes.func` Teil, dass `handleClick` eine Funktion ist. Das Hinzufügen von `isRequired` sagt React, dass `handleClick` eine erforderliche Eigenschaft für diese Komponente ist. Du wirst eine Warnung sehen, wenn diese Eigenschaft nicht vorhanden ist. Beachte auch, dass `func` eine `function` darstellt. Von den sieben primitiven JavaScript-Typen sind `function` und `boolean` (geschrieben als `bool`) die einzigen beiden, die eine ungewöhnliche Schreibweise verwenden. Zusätzlich zu den primitiven Typen gibt es noch andere Typen. Du kannst zum Beispiel prüfen, ob eine Eigenschaft ein React-Element ist. Alle Optionen findest du in der
Dokumentation .
+
+**Hinweis:** Seit React v15.5.0 wird `PropTypes` unabhängig von React importiert, etwa so: `import PropTypes from 'prop-types';`
+
+# --instructions--
+
+Definiere `propTypes` für die Komponente `Items`, um `quantity` als Eigenschaft zu verlangen und stelle sicher, dass sie vom Typ `number` ist.
+
+# --hints--
+
+Die Komponente `ShoppingCart` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return mockedComponent.find('ShoppingCart').length === 1;
+ })()
+);
+```
+
+Die Komponente `Items` sollte gerendert werden.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
+ return mockedComponent.find('Items').length === 1;
+ })()
+);
+```
+
+Die Komponente `Items` sollte eine `propTypes`-Prüfung enthalten, um einen Wert für `quantity` zu verlangen und sicherzustellen, dass der Wert eine Zahl ist.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
+ return (
+ noWhiteSpace.includes('quantity:PropTypes.number.isRequired') &&
+ noWhiteSpace.includes('Items.propTypes=')
+ );
+ })()
+ );
+```
+
+# --seed--
+
+## --before-user-code--
+
+```jsx
+var PropTypes = {
+ number: { isRequired: true }
+};
+```
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const Items = (props) => {
+ return
Current Quantity of Items in Cart: {props.quantity}
+};
+
+// Change code below this line
+
+// Change code above this line
+
+Items.defaultProps = {
+ quantity: 0
+};
+
+class ShoppingCart extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const Items = (props) => {
+ return
Current Quantity of Items in Cart: {props.quantity}
+};
+
+// Change code below this line
+Items.propTypes = {
+ quantity: PropTypes.number.isRequired
+};
+// Change code above this line
+
+Items.defaultProps = {
+ quantity: 0
+};
+
+class ShoppingCart extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-react-to-render-nested-components.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-react-to-render-nested-components.md
new file mode 100644
index 00000000000..b9c3741a37d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-react-to-render-nested-components.md
@@ -0,0 +1,150 @@
+---
+id: 5a24c314108439a4d4036165
+title: Verwende React, um verschachtelte Komponenten zu rendern
+challengeType: 6
+forumTopicId: 301420
+dashedName: use-react-to-render-nested-components
+---
+
+# --description--
+
+Die letzte Aufgabe zeigte einen einfachen Weg, zwei Komponenten zusammenzufügen, aber es gibt viele verschiedene Möglichkeiten, wie du Komponenten mit React zusammensetzen kannst.
+
+Die Komponentenkomposition ist eine der mächtigen Funktionen von React. Wenn du mit React arbeitest, ist es wichtig, dass du dir deine Benutzeroberfläche in Form von Komponenten vorstellst, wie im App-Beispiel in der letzten Aufgabe. Du zerlegst deine Benutzeroberfläche in ihre Grundbausteine, und diese Teile werden zu den Komponenten. Dies hilft dabei, den Code, der für die Benutzeroberfläche verantwortlich ist, von dem Code zu trennen, der für die Anwendungslogik zuständig ist. Das kann die Entwicklung und Pflege komplexer Projekte erheblich vereinfachen.
+
+# --instructions--
+
+Im Code-Editor sind zwei funktionale Komponenten definiert, die `TypesOfFruit` und `Fruits` heißen. Nimm die Komponente `TypesOfFruit` und füge sie zusammen oder *bette* sie in die Komponente `Fruits` ein. Nimm dann die Komponente `Fruits` und verschachtle sie innerhalb der Komponente `TypesOfFood`. Das Ergebnis sollte eine untergeordnete Komponente sein, die in einer übergeordneten Komponente verschachtelt ist, die wiederum in einer eigenen übergeordneten Komponente verschachtelt ist!
+
+# --hints--
+
+Die Komponente `TypesOfFood` sollte ein einzelnes `div`-Element zurückgeben.
+
+```js
+assert(Enzyme.shallow(React.createElement(TypesOfFood)).type() === 'div');
+```
+
+Die Komponente `TypesOfFood` sollte die Komponente `Fruits` zurückgeben.
+
+```js
+assert(
+ Enzyme.shallow(React.createElement(TypesOfFood)).props().children[1].type
+ .name === 'Fruits'
+);
+```
+
+Die Komponente `Fruits` sollte die Komponente `TypesOfFruit` zurückgeben.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(TypesOfFood)).find('h2').html() ===
+ '
Fruits: '
+);
+```
+
+Die Komponente `TypesOfFruit` sollte die Elemente `h2` und `ul` zurückgeben.
+
+```js
+assert(
+ Enzyme.mount(React.createElement(TypesOfFood)).find('ul').text() ===
+ 'ApplesBlueberriesStrawberriesBananas'
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+const TypesOfFruit = () => {
+ return (
+
+
Fruits:
+
+ Apples
+ Blueberries
+ Strawberries
+ Bananas
+
+
+ );
+};
+
+const Fruits = () => {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+};
+
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Types of Food:
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+const TypesOfFruit = () => {
+ return (
+
+
Fruits:
+
+ Apples
+ Blueberries
+ Strawberries
+ Bananas
+
+
+ );
+};
+
+const Fruits = () => {
+ return (
+
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+};
+
+class TypesOfFood extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Types of Food:
+ { /* Change code below this line */ }
+
+ { /* Change code above this line */ }
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-state-to-toggle-an-element.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-state-to-toggle-an-element.md
new file mode 100644
index 00000000000..12c9365ea15
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-state-to-toggle-an-element.md
@@ -0,0 +1,189 @@
+---
+id: 5a24c314108439a4d4036176
+title: Verwende den Zustand um ein Element umzuschalten
+challengeType: 6
+forumTopicId: 301421
+dashedName: use-state-to-toggle-an-element
+---
+
+# --description--
+
+Manchmal musst du den vorherigen Zustand (state) kennen, wenn du den Zustand aktualisieren willst. Allerdings können Zustandsaktualisierungen asynchron sein - das bedeutet, dass React mehrere `setState()`-Aufrufe zu einer einzigen Aktualisierung zusammenfassen kann. Das bedeutet, dass du dich nicht auf den vorherigen Wert von `this.state` oder `this.props` verlassen kannst, wenn du den nächsten Wert berechnest. Du solltest also keinen Code wie diesen verwenden:
+
+```jsx
+this.setState({
+ counter: this.state.counter + this.props.increment
+});
+```
+
+Stattdessen solltest du `setState` eine Funktion übergeben, mit der du auf den Status und die Eigenschaften (props) zugreifen kannst. Die Verwendung einer Funktion mit `setState` garantiert, dass du mit den aktuellsten Werten des Zustands und der Eigenschaften arbeitest. Das bedeutet, dass die obige Aussage wie folgt umgeschrieben werden sollte:
+
+```jsx
+this.setState((state, props) => ({
+ counter: state.counter + props.increment
+}));
+```
+
+Du kannst auch ein Formular ohne `props` verwenden, wenn du nur den `state` brauchst:
+
+```jsx
+this.setState(state => ({
+ counter: state.counter + 1
+}));
+```
+
+Beachte, dass du das Objektliteral in Klammern einschließen musst, sonst hält JavaScript es für einen Codeblock.
+
+# --instructions--
+
+`MyComponent` hat eine `visibility`-Eigenschaft, die auf `false` initialisiert ist. Die Render-Methode gibt eine Ansicht zurück, wenn der Wert von `visibility` wahr ist, und eine andere Ansicht, wenn er falsch ist.
+
+Derzeit gibt es keine Möglichkeit, die Eigenschaft `visibility` im `state` der Komponente zu aktualisieren. Der Wert sollte zwischen true und false hin- und herwechseln. Es gibt einen Click-Handler auf dem Button, der eine Klassenmethode namens `toggleVisibility()` auslöst. Übergib eine Funktion an `setState`, um diese Methode so zu definieren, dass der `state` von `visibility` auf den entgegengesetzten Wert umschaltet, wenn die Methode aufgerufen wird. Wenn `visibility` `false` ist, setzt die Methode sie auf `true` und umgekehrt.
+
+Klicke abschließend auf den Button, um das bedingte Rendering der Komponente basierend auf ihrem `state` zu sehen.
+
+**Hinweis:** Vergiss nicht, das Schlüsselwort `this` an die Methode im `constructor` zu binden!
+
+# --hints--
+
+`MyComponent` sollte ein `div`-Element zurückgeben, das einen `button` enthält.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MyComponent)).find('div').find('button')
+ .length,
+ 1
+);
+```
+
+Der Zustand von `MyComponent` sollte mit einer `visibility`-Eigenschaft initialisiert werden, die auf `false` gesetzt ist.
+
+```js
+assert.strictEqual(
+ Enzyme.mount(React.createElement(MyComponent)).state('visibility'),
+ false
+);
+```
+
+Wenn du auf das Button-Element klickst, sollte die `visibility`-Eigenschaft im Zustand zwischen `true` und `false` wechseln.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ visibility: false });
+ return mockedComponent.state('visibility');
+ };
+ const second = () => {
+ mockedComponent.find('button').simulate('click');
+ return mockedComponent.state('visibility');
+ };
+ const third = () => {
+ mockedComponent.find('button').simulate('click');
+ return mockedComponent.state('visibility');
+ };
+ const firstValue = first();
+ const secondValue = second();
+ const thirdValue = third();
+ assert(!firstValue && secondValue && !thirdValue);
+})();
+```
+
+Eine anonyme Funktion sollte an `setState` übergeben werden.
+
+```js
+const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?';
+assert(
+ new RegExp(
+ 'this\\.setState\\((function\\(' +
+ paramRegex +
+ '\\){|([a-zA-Z$_]\\w*|\\(' +
+ paramRegex +
+ '\\))=>)'
+ ).test(__helpers.removeWhiteSpace(code))
+);
+```
+
+`this` sollte nicht innerhalb von `setState` verwendet werden
+
+```js
+assert(!/this\.setState\([^}]*this/.test(code));
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visibility: false
+ };
+ // Change code below this line
+
+ // Change code above this line
+ }
+ // Change code below this line
+
+ // Change code above this line
+ render() {
+ if (this.state.visibility) {
+ return (
+
+ Click Me
+
Now you see me!
+
+ );
+ } else {
+ return (
+
+ Click Me
+
+ );
+ }
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visibility: false
+ };
+ this.toggleVisibility = this.toggleVisibility.bind(this);
+ }
+ toggleVisibility() {
+ this.setState(state => ({
+ visibility: !state.visibility
+ }));
+ }
+ render() {
+ if (this.state.visibility) {
+ return (
+
+ Click Me
+
Now you see me!
+
+ );
+ } else {
+ return (
+
+ Click Me
+
+ );
+ }
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentdidmount.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentdidmount.md
new file mode 100644
index 00000000000..d5f3ba83430
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentdidmount.md
@@ -0,0 +1,127 @@
+---
+id: 5a24c314108439a4d403617d
+title: Verwende die Lifecycle Methode componentDidMount
+challengeType: 6
+forumTopicId: 301422
+dashedName: use-the-lifecycle-method-componentdidmount
+---
+
+# --description--
+
+Die meisten Webentwickler müssen irgendwann einen API-Endpunkt aufrufen, um Daten abzurufen. Wenn du mit React arbeitest, ist es wichtig zu wissen, wo du diese Aktion durchführen musst.
+
+Die beste Methode bei React ist, API-Aufrufe oder Aufrufe an deinen Server in der Lifecycle-Methode `componentDidMount()` zu platzieren. Diese Methode wird aufgerufen, nachdem eine Komponente in das DOM eingebaut wurde. Jeder Aufruf von `setState()` hier löst ein neues Rendering deiner Komponente aus. Wenn du mit dieser Methode eine API aufrufst und deinen Status mit den Daten einstellst, die die API zurückgibt, wird automatisch eine Aktualisierung ausgelöst, sobald du die Daten erhältst.
+
+# --instructions--
+
+Es gibt einen Mock-API-Aufruf in `componentDidMount()`. Sie setzt den Zustand nach 2,5 Sekunden, um zu simulieren, dass sie einen Server anruft, um Daten abzurufen. In diesem Beispiel wird die aktuelle Anzahl der aktiven Nutzer für eine Website abgefragt. In der Render-Methode gibst du den Wert von `activeUsers` in dem `h1` nach dem Text `Active Users:` wieder. Sieh dir an, was in der Vorschau passiert und ändere die Zeitspanne, um die verschiedenen Effekte zu sehen.
+
+# --hints--
+
+`MyComponent` soll ein `div`-Element darstellen, das ein `h1`-Tag umhüllt.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return (
+ mockedComponent.find('div').length === 1 &&
+ mockedComponent.find('h1').length === 1
+ );
+ })()
+);
+```
+
+Der Zustand der Komponente sollte mit einer Timeout-Funktion in `componentDidMount` aktualisiert werden.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return new RegExp('setTimeout(.|\n)+setState(.|\n)+activeUsers').test(
+ String(mockedComponent.instance().componentDidMount)
+ );
+ })()
+);
+```
+
+Der `h1`-Tag sollte den `activeUsers`-Wert aus dem Zustand von `MyComponent` wiedergeben.
+
+```js
+(() => {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ const first = () => {
+ mockedComponent.setState({ activeUsers: 1237 });
+ return mockedComponent.find('h1').text();
+ };
+ const second = () => {
+ mockedComponent.setState({ activeUsers: 1000 });
+ return mockedComponent.find('h1').text();
+ };
+ assert(new RegExp('1237').test(first()) && new RegExp('1000').test(second()));
+})();
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'));
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeUsers: null
+ };
+ }
+ componentDidMount() {
+ setTimeout(() => {
+ this.setState({
+ activeUsers: 1273
+ });
+ }, 2500);
+ }
+ render() {
+ return (
+
+ {/* Change code below this line */}
+
Active Users:
+ {/* Change code above this line */}
+
+ );
+ }
+}
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeUsers: null
+ };
+ }
+ componentDidMount() {
+ setTimeout(() => {
+ this.setState({
+ activeUsers: 1273
+ });
+ }, 2500);
+ }
+ render() {
+ return (
+
+
Active Users: {this.state.activeUsers}
+
+ );
+ }
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentwillmount.md b/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentwillmount.md
new file mode 100644
index 00000000000..340b848a587
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/use-the-lifecycle-method-componentwillmount.md
@@ -0,0 +1,87 @@
+---
+id: 5a24c314108439a4d403617c
+title: Verwende die Lifecycle-Methode componentWillMount
+challengeType: 6
+forumTopicId: 301423
+dashedName: use-the-lifecycle-method-componentwillmount
+---
+
+# --description--
+
+React-Komponenten haben mehrere spezielle Methoden, die es ermöglichen, an bestimmten Punkten im Lebenszyklus (Lifecycle) einer Komponente Aktionen durchzuführen. Diese werden Lifecycle-Methoden oder Lifecycle-Hooks genannt und ermöglichen es dir, Komponenten zu bestimmten Zeitpunkten abzufangen. Das kann sein, bevor sie gerendert werden, bevor sie aktualisiert werden, bevor sie Eigenschaften erhalten, bevor sie demontiert werden, und so weiter. Hier ist eine Liste mit einigen der wichtigsten Lifecycle-Methoden: `componentWillMount()` `componentDidMount()` `shouldComponentUpdate()` `componentDidUpdate()` `componentWillUnmount()` In den nächsten Lektionen werden einige der grundlegenden Anwendungsfälle für diese Lifecycle-Methoden behandelt.
+
+**Hinweis:** Die Lifecycle-Methode `componentWillMount` wird in einer zukünftigen Version von 16.X veraltet sein und in Version 17 entfernt werden. Erfahre mehr in diesem Artikel:
+
+# --instructions--
+
+Die Methode `componentWillMount()` wird vor der Methode `render()` aufgerufen, wenn eine Komponente in das DOM eingebunden wird. Protokolliere etwas auf der Konsole innerhalb von `componentWillMount()` - du solltest die Konsole deines Browsers geöffnet haben, um die Ausgabe zu sehen.
+
+# --hints--
+
+`MyComponent` sollte ein `div`-Element darstellen.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('div').length === 1;
+ })()
+);
+```
+
+`console.log` sollte in `componentWillMount` aufgerufen werden.
+
+```js
+assert(
+ (function () {
+ const lifecycle = React.createElement(MyComponent)
+ .type.prototype.componentWillMount.toString()
+ .replace(/ /g, '');
+ return lifecycle.includes('console.log(');
+ })()
+);
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentWillMount() {
+ // Change code below this line
+
+ // Change code above this line
+ }
+ render() {
+ return
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentWillMount() {
+ // Change code below this line
+ console.log('Component is mounting...');
+ // Change code above this line
+ }
+ render() {
+ return
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-react-component-from-scratch.md b/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-react-component-from-scratch.md
new file mode 100644
index 00000000000..e3f4efa686a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-react-component-from-scratch.md
@@ -0,0 +1,84 @@
+---
+id: 5a24c314108439a4d4036168
+title: Eine React-Komponente von Grund auf neu erstellen
+challengeType: 6
+forumTopicId: 301424
+dashedName: write-a-react-component-from-scratch
+---
+
+# --description--
+
+Nachdem du nun die Grundlagen von JSX und React-Komponenten gelernt hast, ist es an der Zeit, selbst eine Komponente zu schreiben. React-Komponenten sind die Kernbausteine von React-Anwendungen. Deshalb ist es wichtig, dass du dich mit dem Schreiben dieser Komponenten gut auskennst. Zur Erinnerung: Eine typische React-Komponente ist eine ES6-Klasse (`class`), die `React.Component` erweitert. Sie hat eine Render-Methode, die HTML (aus JSX) oder `null` zurückgibt. Dies ist die Grundform einer React-Komponente. Wenn du das erst einmal verstanden hast, bist du bereit, auch komplexere React-Projekte zu bauen.
+
+# --instructions--
+
+Definiere eine Klasse `MyComponent`, die `React.Component` erweitert. Die Render-Methode sollte ein `div` zurückgeben, das ein `h1`-Tag mit dem Text: `My First React Component!` enthält. Verwende genau diesen Text, die Groß- und Kleinschreibung und die Zeichensetzung sind wichtig. Achte darauf, dass du auch den Konstruktor für deine Komponente aufrufst.
+
+Rendere diese Komponente mit `ReactDOM.render()` in das DOM. Es gibt ein `div` mit `id='challenge-node'`, das du verwenden kannst.
+
+# --hints--
+
+Es sollte eine React-Komponente namens `MyComponent` vorhanden sein.
+
+```js
+(getUserInput) =>
+ assert(
+ __helpers
+ .removeWhiteSpace(getUserInput('index'))
+ .includes('classMyComponentextendsReact.Component{')
+ );
+```
+
+`MyComponent` sollte einen `h1`-Tag mit dem Text `My First React Component!` enthalten, wobei Groß- und Kleinschreibung wichtig sind.
+
+```js
+assert(
+ (function () {
+ const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
+ return mockedComponent.find('h1').text() === 'My First React Component!';
+ })()
+);
+```
+
+`MyComponent` soll in das DOM gerendert werden.
+
+```js
+assert(document.getElementById('challenge-node').childNodes.length === 1);
+```
+
+`MyComponent` sollte einen Konstruktor haben, der `super` mit `props` aufruft.
+
+```js
+assert(
+ MyComponent.toString().includes('MyComponent(props)') &&
+ MyComponent.toString().includes('_super.call(this, props)')
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```jsx
+// Change code below this line
+```
+
+# --solutions--
+
+```jsx
+// Change code below this line
+class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+
+
My First React Component!
+
+ );
+ }
+};
+
+ReactDOM.render(
, document.getElementById('challenge-node'));
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-simple-counter.md b/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-simple-counter.md
new file mode 100644
index 00000000000..2592cc951b0
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/react/write-a-simple-counter.md
@@ -0,0 +1,146 @@
+---
+id: 5a24c314108439a4d4036177
+title: Einen einfachen Zähler schreiben
+challengeType: 6
+forumTopicId: 301425
+dashedName: write-a-simple-counter
+---
+
+# --description--
+
+Du kannst eine komplexere zustandsabhängige Komponente entwerfen, indem du die bisher behandelten Konzepte kombinierst. Dazu gehören die Initialisierung des `state`, das Schreiben von Methoden, die den `state` setzen, und die Zuweisung von Click-Handlern, die diese Methoden auslösen.
+
+# --instructions--
+
+Die Zähler(`Counter`)-komponente verfolgt einen `count`-Wert im `state`. Es gibt zwei Buttons, die die Methoden `increment()` und `decrement()` aufrufen. Schreibe diese Methoden so, dass der Zählerwert um 1 erhöht oder verringert wird, wenn der entsprechende Button angeklickt wird. Erstelle außerdem eine `reset()`-Methode, damit der Zähler auf 0 gesetzt wird, wenn der Reset-Button angeklickt wird.
+
+**Hinweis:** Achte darauf, dass du die Klassenamen (`className`) der Buttons nicht veränderst. Denke auch daran, die notwendigen Bindungen für die neu erstellten Methoden im Konstruktor hinzuzufügen.
+
+# --hints--
+
+`Counter` sollte ein `div`-Element zurückgeben, das drei Buttons mit Textinhalten in dieser Reihenfolge enthält: `Increment!`, `Decrement!`, `Reset`.
+
+```js
+assert(
+ (() => {
+ const mockedComponent = Enzyme.mount(React.createElement(Counter));
+ return (
+ mockedComponent.find('.inc').text() === 'Increment!' &&
+ mockedComponent.find('.dec').text() === 'Decrement!' &&
+ mockedComponent.find('.reset').text() === 'Reset'
+ );
+ })()
+);
+```
+
+Der Zustand von `Counter` sollte mit einer `count`-Eigenschaft initialisiert werden, die auf `0` gesetzt ist.
+
+```js
+const mockedComponent = Enzyme.mount(React.createElement(Counter));
+assert(mockedComponent.find('h1').text() === 'Current Count: 0');
+```
+
+Wenn du auf den Inkrement-Button klickst, wird die Zahl um `1` erhöht.
+
+```js
+const mockedComponent = Enzyme.mount(React.createElement(Counter));
+mockedComponent.find('.inc').simulate('click');
+assert(mockedComponent.find('h1').text() === 'Current Count: 1');
+```
+
+Wenn du auf den Dekrement-Button klickst, wird die Anzahl um `1` verringert.
+
+```js
+const mockedComponent = Enzyme.mount(React.createElement(Counter));
+mockedComponent.find('.dec').simulate('click');
+assert(mockedComponent.find('h1').text() === 'Current Count: -1');
+```
+
+Ein Klick auf den Reset-Button sollte den Zähler auf `0` zurücksetzen.
+
+```js
+const mockedComponent = Enzyme.mount(React.createElement(Counter));
+mockedComponent.setState({ count: 5 });
+const currentCountElement = mockedComponent.find('h1');
+assert(currentCountElement.text() === 'Current Count: 5');
+mockedComponent.find('.reset').simulate('click');
+assert(currentCountElement.text() === 'Current Count: 0');
+```
+
+# --seed--
+
+## --after-user-code--
+
+```jsx
+ReactDOM.render(
, document.getElementById('root'))
+```
+
+## --seed-contents--
+
+```jsx
+class Counter extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ count: 0
+ };
+ // Change code below this line
+
+ // Change code above this line
+ }
+ // Change code below this line
+
+ // Change code above this line
+ render() {
+ return (
+
+ Increment!
+ Decrement!
+ Reset
+
Current Count: {this.state.count}
+
+ );
+ }
+};
+```
+
+# --solutions--
+
+```jsx
+class Counter extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ count: 0
+ };
+ this.increment = this.increment.bind(this);
+ this.decrement = this.decrement.bind(this);
+ this.reset = this.reset.bind(this);
+ }
+ reset() {
+ this.setState({
+ count: 0
+ });
+ }
+ increment() {
+ this.setState(state => ({
+ count: state.count + 1
+ }));
+ }
+ decrement() {
+ this.setState(state => ({
+ count: state.count - 1
+ }));
+ }
+ render() {
+ return (
+
+ Increment!
+ Decrement!
+ Reset
+
Current Count: {this.state.count}
+
+ );
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/combine-multiple-reducers.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/combine-multiple-reducers.md
new file mode 100644
index 00000000000..6633106f37e
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/combine-multiple-reducers.md
@@ -0,0 +1,175 @@
+---
+id: 5a24c314108439a4d4036154
+title: Kombiniere mehrere Reducer
+challengeType: 6
+forumTopicId: 301436
+dashedName: combine-multiple-reducers
+---
+
+# --description--
+
+Wenn der Zustand deiner App immer komplexer wird, kann es verlockend sein, den Zustand in mehrere Teile aufzuteilen. Erinnere dich stattdessen an das erste Prinzip von Redux: Der gesamte Zustand der App wird in einem einzigen Zustandsobjekt im Store gespeichert. Deshalb bietet Redux die Komposition von Reducern als Lösung für ein komplexes Zustandsmodell. Du definierst mehrere Reducer, um verschiedene Teile des Zustands deiner Anwendung zu verarbeiten, und fügst diese Reducer dann zu einem Root-Reducer zusammen. Der Root-Reducer wird dann an die Redux-Methode `createStore()` übergeben.
+
+Damit wir mehrere Reducer miteinander kombinieren können, bietet Redux die Methode `combineReducers()`. Diese Methode akzeptiert ein Objekt als Argument, in dem du Eigenschaften definierst, die Schlüssel mit bestimmten Reducer-Funktionen verknüpfen. Der Name, den du den Schlüsseln gibst, wird von Redux als Name für den zugehörigen Zustand verwendet.
+
+Normalerweise ist es eine gute Praxis, für jeden Teil des Anwendungszustands einen Reducer zu erstellen, wenn sie sich unterscheiden oder in irgendeiner Weise einzigartig sind. In einer App für Notizen mit Benutzerauthentifizierung könnte zum Beispiel ein Reducer die Authentifizierung übernehmen, während ein anderer den Text und die Notizen bearbeitet, die der Benutzer eingibt. Für eine solche Anwendung könnten wir die Methode `combineReducers()` wie folgt schreiben:
+
+```js
+const rootReducer = Redux.combineReducers({
+ auth: authenticationReducer,
+ notes: notesReducer
+});
+```
+
+Jetzt enthält der Schlüssel `notes` alle Zustände, die mit unseren Notizen verbunden sind und von unserem `notesReducer` bearbeitet werden. Auf diese Weise können mehrere Reducer zusammengesetzt werden, um einen komplexeren Anwendungszustand zu verwalten. In diesem Beispiel wäre der Zustand im Redux-Store dann ein einzelnes Objekt, das die Eigenschaften `auth` und `notes` enthält.
+
+# --instructions--
+
+Es gibt `counterReducer()` und `authReducer()`-Funktionen, die im Code-Editor zusammen mit einem Redux-Store bereitgestellt werden. Stelle die `rootReducer()`-Funktion mit der Methode `Redux.combineReducers()` fertig. Weise `counterReducer` einem Schlüssel namens `count` und `authReducer` einem Schlüssel namens `auth` zu.
+
+# --hints--
+
+`counterReducer` sollte den `state` inkrementieren und dekrementieren.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState().count;
+ store.dispatch({ type: INCREMENT });
+ store.dispatch({ type: INCREMENT });
+ const firstState = store.getState().count;
+ store.dispatch({ type: DECREMENT });
+ const secondState = store.getState().count;
+ return firstState === initialState + 2 && secondState === firstState - 1;
+ })()
+);
+```
+
+`authReducer` sollte den `state` von `authenticated` zwischen `true` und `false` umschalten.
+
+```js
+assert(
+ (function () {
+ store.dispatch({ type: LOGIN });
+ const loggedIn = store.getState().auth.authenticated;
+ store.dispatch({ type: LOGOUT });
+ const loggedOut = store.getState().auth.authenticated;
+ return loggedIn === true && loggedOut === false;
+ })()
+);
+```
+
+Der Store `state` sollte zwei Schlüssel haben: `count`, der eine Zahl enthält, und `auth`, der ein Objekt enthält. Das `auth`-Objekt sollte eine Eigenschaft `authenticated` besitzen, die einen booleschen Wert enthält.
+
+```js
+assert(
+ (function () {
+ const state = store.getState();
+ return (
+ typeof state.auth === 'object' &&
+ typeof state.auth.authenticated === 'boolean' &&
+ typeof state.count === 'number'
+ );
+ })()
+);
+```
+
+Der `rootReducer` sollte eine Funktion sein, die den `counterReducer` und den `authReducer` kombiniert.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
+ return (
+ typeof rootReducer === 'function' &&
+ noWhiteSpace.includes('Redux.combineReducers')
+ );
+ })()
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const INCREMENT = 'INCREMENT';
+const DECREMENT = 'DECREMENT';
+
+const counterReducer = (state = 0, action) => {
+ switch(action.type) {
+ case INCREMENT:
+ return state + 1;
+ case DECREMENT:
+ return state - 1;
+ default:
+ return state;
+ }
+};
+
+const LOGIN = 'LOGIN';
+const LOGOUT = 'LOGOUT';
+
+const authReducer = (state = {authenticated: false}, action) => {
+ switch(action.type) {
+ case LOGIN:
+ return {
+ authenticated: true
+ }
+ case LOGOUT:
+ return {
+ authenticated: false
+ }
+ default:
+ return state;
+ }
+};
+
+const rootReducer = // Define the root reducer here
+
+const store = Redux.createStore(rootReducer);
+```
+
+# --solutions--
+
+```js
+const INCREMENT = 'INCREMENT';
+const DECREMENT = 'DECREMENT';
+
+const counterReducer = (state = 0, action) => {
+ switch(action.type) {
+ case INCREMENT:
+ return state + 1;
+ case DECREMENT:
+ return state - 1;
+ default:
+ return state;
+ }
+};
+
+const LOGIN = 'LOGIN';
+const LOGOUT = 'LOGOUT';
+
+const authReducer = (state = {authenticated: false}, action) => {
+ switch(action.type) {
+ case LOGIN:
+ return {
+ authenticated: true
+ }
+ case LOGOUT:
+ return {
+ authenticated: false
+ }
+ default:
+ return state;
+ }
+};
+
+const rootReducer = Redux.combineReducers({
+ count: counterReducer,
+ auth: authReducer
+});
+
+const store = Redux.createStore(rootReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/copy-an-object-with-object.assign.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/copy-an-object-with-object.assign.md
new file mode 100644
index 00000000000..e0c4d773e19
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/copy-an-object-with-object.assign.md
@@ -0,0 +1,133 @@
+---
+id: 5a24c314108439a4d403615b
+title: Ein Objekt mit Object.assign kopieren
+challengeType: 6
+forumTopicId: 301437
+dashedName: copy-an-object-with-object-assign
+---
+
+# --description--
+
+Die letzten Aufgaben haben mit Arrays gearbeitet, aber es gibt auch Möglichkeiten, die Unveränderlichkeit des Zustands zu erzwingen, wenn der Zustand ein `object` ist. Ein nützliches Werkzeug für den Umgang mit Objekten ist das `Object.assign()` Dienstprogramm. `Object.assign()` nimmt ein Zielobjekt und Quellobjekte und ordnet die Eigenschaften der Quellobjekte dem Zielobjekt zu. Alle übereinstimmenden Eigenschaften werden durch Eigenschaften in den Quellobjekten überschrieben. Dieses Verhalten wird häufig verwendet, um einfache Kopien von Objekten zu erstellen, indem du ein leeres Objekt als erstes Argument übergibst, gefolgt von dem oder den Objekten, die du kopieren willst. Hier ist ein Beispiel:
+
+```js
+const newObject = Object.assign({}, obj1, obj2);
+```
+
+Dadurch wird `newObject` als neues `object` erstellt, das die Eigenschaften enthält, die derzeit in `obj1` und `obj2` existieren.
+
+# --instructions--
+
+Der Redux-Zustand und die Actions wurden geändert, um ein `object` für den `state` zu verwenden. Bearbeite den Code, um ein neues `state`-Objekt für Actions mit dem Typ `ONLINE` zurückzugeben, das die `status`-Eigenschaft auf den String `online` setzt. Versuche, `Object.assign()` zu verwenden, um diese Aufgabe zu lösen.
+
+# --hints--
+
+Der Redux-Store sollte existieren und mit einem Zustand initialisiert werden, der dem `defaultState`-Objekt entspricht, das in Zeile 1 deklariert wurde.
+
+```js
+assert(
+ (function () {
+ const expectedState = {
+ user: 'CamperBot',
+ status: 'offline',
+ friends: '732,982',
+ community: 'freeCodeCamp'
+ };
+ const initialState = store.getState();
+ return DeepEqual(expectedState, initialState);
+ })()
+);
+```
+
+`wakeUp` und `immutableReducer` sollten beide Funktionen sein.
+
+```js
+assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function');
+```
+
+Das Senden einer Aktion vom Typ `ONLINE` sollte die Eigenschaft `status` im Zustand auf `online` aktualisieren und sollte den Zustand NICHT verändern.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ const isFrozen = DeepFreeze(initialState);
+ store.dispatch({ type: 'ONLINE' });
+ const finalState = store.getState();
+ const expectedState = {
+ user: 'CamperBot',
+ status: 'online',
+ friends: '732,982',
+ community: 'freeCodeCamp'
+ };
+ return isFrozen && DeepEqual(finalState, expectedState);
+ })()
+);
+```
+
+`Object.assign` sollte verwendet werden, um einen neuen Zustand zurückzugeben.
+
+```js
+(getUserInput) => assert(getUserInput('index').includes('Object.assign'));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const defaultState = {
+ user: 'CamperBot',
+ status: 'offline',
+ friends: '732,982',
+ community: 'freeCodeCamp'
+};
+
+const immutableReducer = (state = defaultState, action) => {
+ switch(action.type) {
+ case 'ONLINE':
+ // Don't mutate state here or the tests will fail
+ return
+ default:
+ return state;
+ }
+};
+
+const wakeUp = () => {
+ return {
+ type: 'ONLINE'
+ }
+};
+
+const store = Redux.createStore(immutableReducer);
+```
+
+# --solutions--
+
+```js
+const defaultState = {
+ user: 'CamperBot',
+ status: 'offline',
+ friends: '732,982',
+ community: 'freeCodeCamp'
+};
+
+const immutableReducer = (state = defaultState, action) => {
+ switch(action.type) {
+ case 'ONLINE':
+ return Object.assign({}, state, {
+ status: 'online'
+ });
+ default:
+ return state;
+ }
+};
+
+const wakeUp = () => {
+ return {
+ type: 'ONLINE'
+ }
+};
+
+const store = Redux.createStore(immutableReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/create-a-redux-store.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/create-a-redux-store.md
new file mode 100644
index 00000000000..e91a964975e
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/create-a-redux-store.md
@@ -0,0 +1,61 @@
+---
+id: 5a24c314108439a4d403614b
+title: Einen Redux Store erstellen
+challengeType: 6
+forumTopicId: 301439
+dashedName: create-a-redux-store
+---
+
+# --description--
+
+Redux ist ein Zustandsmanagement-Framework, das mit einer Reihe verschiedener Webtechnologien verwendet werden kann, darunter auch React.
+
+In Redux gibt es ein einziges Zustandsobjekt (state object), das für den gesamten Zustand deiner Anwendung verantwortlich ist. Das heißt, wenn du eine React-App mit zehn Komponenten hast und jede Komponente ihren eigenen lokalen Zustand hat, wird der gesamte Zustand deiner App durch ein einziges Zustandsobjekt definiert, das sich im Redux `store` befindet. Dies ist das erste wichtige Prinzip, das du verstehen musst, wenn du Redux lernst: Der Redux-Store ist die einzige Quelle der Wahrheit, wenn es um den Zustand der Anwendung geht.
+
+Das bedeutet auch, dass jedes Mal, wenn ein Teil deiner App den Zustand aktualisieren will, **das über den Redux-Store geschehen muss**. Der unidirektionale Datenfluss macht es einfacher, das Zustandsmanagement in deiner App zu verfolgen.
+
+# --instructions--
+
+Der Redux `store` ist ein Objekt, das den Zustand (`state`) der Anwendung hält und verwaltet. Es gibt eine Methode namens `createStore()` auf dem Redux-Objekt, mit der du den Redux `store` erstellen kannst. Diese Methode nimmt eine `reducer`-Funktion als erforderliches Argument entgegen. Die `reducer`-Funktion wird in einer späteren Aufgabe behandelt und ist bereits im Code-Editor für dich definiert. Sie nimmt einfach `state` als Argument und gibt `state` zurück.
+
+Deklariere eine `store`-Variable und weise sie der `createStore()`-Methode zu, indem du den `reducer` als Argument übergibst.
+
+**Hinweis:** Der Code im Editor verwendet die ES6-Standardargumentsyntax, um diesen Zustand mit einem Wert von `5` zu initialisieren. Wenn du mit Standardargumenten nicht vertraut bist, kannst auf
ES6-Abschnitt in den Lehrinhalten zurückgreifen, in dem dieses Thema behandelt wird.
+
+# --hints--
+
+Der Redux Store sollte existieren.
+
+```js
+assert(typeof store.getState === 'function');
+```
+
+Der Redux-Store sollte einen Wert von 5 für den Zustand haben.
+
+```js
+assert(store.getState() === 5);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const reducer = (state = 5) => {
+ return state;
+}
+
+// Redux methods are available from a Redux object
+// For example: Redux.createStore()
+// Define the store here:
+```
+
+# --solutions--
+
+```js
+const reducer = (state = 5) => {
+ return state;
+}
+
+const store = Redux.createStore(reducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/define-a-redux-action.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/define-a-redux-action.md
new file mode 100644
index 00000000000..103be89798d
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/define-a-redux-action.md
@@ -0,0 +1,55 @@
+---
+id: 5a24c314108439a4d403614d
+title: Definiere eine Redux-Aktion
+challengeType: 6
+forumTopicId: 301440
+dashedName: define-a-redux-action
+---
+
+# --description--
+
+Da Redux ein Zustandsmanagement-Framework ist, ist die Aktualisierung des Zustands eine seiner Kernaufgaben. In Redux werden alle Zustandsaktualisierungen durch Dispatching-Aktionen ausgelöst. Eine Aktion ist einfach ein JavaScript-Objekt, das Informationen über ein eingetretenes Aktionsereignis enthält. Der Redux-Store empfängt diese Aktionsobjekte und aktualisiert dann seinen Status entsprechend. Manchmal enthält eine Redux-Aktion auch Daten. Zum Beispiel überträgt die Aktion einen Benutzernamen, nachdem sich ein Benutzer angemeldet hat. Während die Daten optional sind, müssen Aktionen eine `type`-Eigenschaft haben, die den "Typ" (type) der aufgetretenen Aktion angibt.
+
+Stell dir Redux-Aktionen als Boten vor, die Informationen über Ereignisse in deiner App an den Redux-Store liefern. Der Store führt dann die Aktualisierung des Zustands auf der Grundlage der erfolgten Aktion durch.
+
+# --instructions--
+
+Das Schreiben einer Redux-Aktion ist so einfach wie das Deklarieren eines Objekts mit einer Typeigenschaft. Deklariere ein Objekt `action` und gib ihm eine Eigenschaft `type`, die auf den String `'LOGIN'` gesetzt ist.
+
+# --hints--
+
+Ein `action`-Objekt sollte existieren.
+
+```js
+assert(
+ (function () {
+ return typeof action === 'object';
+ })()
+);
+```
+
+Das `action`-Objekt sollte eine Schlüsseleigenschaft `type` mit dem Wert `LOGIN` besitzen.
+
+```js
+assert(
+ (function () {
+ return action.type === 'LOGIN';
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+// Define an action here:
+```
+
+# --solutions--
+
+```js
+const action = {
+ type: 'LOGIN'
+}
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/define-an-action-creator.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/define-an-action-creator.md
new file mode 100644
index 00000000000..2c530eb681f
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/define-an-action-creator.md
@@ -0,0 +1,57 @@
+---
+id: 5a24c314108439a4d403614e
+title: Definiere einen Action Creator
+challengeType: 6
+forumTopicId: 301441
+dashedName: define-an-action-creator
+---
+
+# --description--
+
+Nachdem du eine Aktion erstellt hast, wird sie im nächsten Schritt an den Redux-Store gesendet, damit sie ihren Zustand aktualisieren kann. In Redux definierst du Action Creators, um dies zu erreichen. Ein Action Creator ist einfach eine JavaScript-Funktion, die eine Aktion zurückgibt. Mit anderen Worten: Action Creators erstellen Objekte, die Aktionsereignisse darstellen.
+
+# --instructions--
+
+Definiere eine Funktion namens `actionCreator()`, die beim Aufruf das `action`-Objekt zurückgibt.
+
+# --hints--
+
+Die Funktion `actionCreator` sollte existieren.
+
+```js
+assert(typeof actionCreator === 'function');
+```
+
+Das Ausführen der Funktion `actionCreator` sollte das `action`-Objekt zurückgeben.
+
+```js
+assert(typeof action === 'object');
+```
+
+Die zurückgegebene `action` sollte eine Schlüsseleigenschaft `type` mit dem Wert `LOGIN` besitzen.
+
+```js
+assert(action.type === 'LOGIN');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const action = {
+ type: 'LOGIN'
+}
+// Define an action creator here:
+```
+
+# --solutions--
+
+```js
+const action = {
+ type: 'LOGIN'
+}
+const actionCreator = () => {
+ return action;
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/dispatch-an-action-event.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/dispatch-an-action-event.md
new file mode 100644
index 00000000000..0de8cbaffe1
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/dispatch-an-action-event.md
@@ -0,0 +1,85 @@
+---
+id: 5a24c314108439a4d403614f
+title: Ein Aktionsereignis auslösen
+challengeType: 6
+forumTopicId: 301442
+dashedName: dispatch-an-action-event
+---
+
+# --description--
+
+`dispatch` ist die Methode, mit der du Aktionen an den Redux Store sendest. Der Aufruf von `store.dispatch()` und die Übergabe des Wertes, der von einem Action Creator zurückgegeben wird, sendet eine Aktion zurück an den Store.
+
+Erinnere dich daran, dass Action Creators ein Objekt mit einer Typeigenschaft zurückgeben, die die aufgetretene Aktion angibt. Dann sendet die Methode ein Aktionsobjekt an den Redux Store. Basierend auf dem Beispiel der vorherigen Aufgabe sind die folgenden Zeilen gleichwertig und senden beide die Aktion vom Typ `LOGIN`:
+
+```js
+store.dispatch(actionCreator());
+store.dispatch({ type: 'LOGIN' });
+```
+
+# --instructions--
+
+Der Redux Store im Code-Editor hat einen initialisierten Zustand, der ein Objekt mit einer `login`-Eigenschaft ist, das derzeit auf `false` gesetzt ist. Es gibt auch einen Action Creator namens `loginAction()`, der eine Aktion vom Typ `LOGIN` zurückgibt. Sende die `LOGIN`-Aktion an den Redux Store, indem du die Methode `dispatch` aufrufst und die mit `loginAction()` erstellte Aktion übergibst.
+
+# --hints--
+
+Der Aufruf der Funktion `loginAction` sollte ein Objekt zurückgeben, dessen Eigenschaft `type` auf den String `LOGIN` gesetzt ist.
+
+```js
+assert(loginAction().type === 'LOGIN');
+```
+
+Der Store sollte mit einem Objekt initialisiert werden, dessen Eigenschaft `login` auf `false` gesetzt ist.
+
+```js
+assert(store.getState().login === false);
+```
+
+Die Methode `store.dispatch()` sollte verwendet werden, um eine Aktion vom Typ `LOGIN` zu versenden.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ let noWhiteSpace = getUserInput('index').replace(/\s/g, '');
+ return (
+ noWhiteSpace.includes('store.dispatch(loginAction())') ||
+ noWhiteSpace.includes("store.dispatch({type: 'LOGIN'})") === true
+ );
+ })()
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const store = Redux.createStore(
+ (state = {login: false}) => state
+);
+
+const loginAction = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+
+// Dispatch the action here:
+```
+
+# --solutions--
+
+```js
+const store = Redux.createStore(
+ (state = {login: false}) => state
+);
+
+const loginAction = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+
+store.dispatch(loginAction());
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/get-state-from-the-redux-store.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/get-state-from-the-redux-store.md
new file mode 100644
index 00000000000..d400258b6ac
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/get-state-from-the-redux-store.md
@@ -0,0 +1,55 @@
+---
+id: 5a24c314108439a4d403614c
+title: Zustand aus dem Redux Store abrufen
+challengeType: 6
+forumTopicId: 301443
+dashedName: get-state-from-the-redux-store
+---
+
+# --description--
+
+Das Redux-Store-Objekt bietet mehrere Methoden, mit denen du mit ihm interagieren kannst. Du kannst zum Beispiel den aktuellen `state` im Redux-Store-Objekt mit der Methode `getState()` abrufen.
+
+# --instructions--
+
+Der Code aus der vorherigen Aufgabe wird im Code-Editor noch einmal übersichtlicher geschrieben. Verwende `store.getState()`, um den `state` aus dem `store` abzurufen, und weise ihn einer neuen Variablen `currentState` zu.
+
+# --hints--
+
+Der Redux-Store sollte einen Wert von 5 für den initialen Zustand besitzen.
+
+```js
+assert(store.getState() === 5);
+```
+
+Es sollte eine Variable `currentState` existieren, der der aktuelle Zustand des Redux-Stores zugewiesen wird.
+
+```js
+(getUserInput) =>
+ assert(
+ currentState === 5 && getUserInput('index').includes('store.getState()')
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const store = Redux.createStore(
+ (state = 5) => state
+);
+
+// Change code below this line
+```
+
+# --solutions--
+
+```js
+const store = Redux.createStore(
+ (state = 5) => state
+);
+
+// Change code below this line
+const currentState = store.getState();
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/handle-an-action-in-the-store.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/handle-an-action-in-the-store.md
new file mode 100644
index 00000000000..eebeed4c480
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/handle-an-action-in-the-store.md
@@ -0,0 +1,108 @@
+---
+id: 5a24c314108439a4d4036150
+title: Verwalte eine Aktion im Store
+challengeType: 6
+forumTopicId: 301444
+dashedName: handle-an-action-in-the-store
+---
+
+# --description--
+
+Nachdem eine Aktion erstellt und versendet wurde, muss der Redux Store wissen, wie er auf diese Aktion reagieren soll. Das ist die Aufgabe einer `reducer`-Funktion. Reducer in Redux sind für die Zustandsänderungen verantwortlich, die als Reaktion auf Aktionen stattfinden. Ein `reducer` nimmt `state` und `action` als Argumente und gibt immer einen neuen `state` zurück. Es ist wichtig zu wissen, dass dies die **einzige** Rolle des Reducer ist. Sie hat keine Nebenwirkungen - sie ruft nie einen API-Endpunkt auf und birgt keine versteckten Überraschungen. Der Reducer ist einfach eine reine Funktion, die einen Zustand und eine Aktion annimmt und dann einen neuen Zustand zurückgibt.
+
+Ein weiteres wichtiges Prinzip in Redux ist, dass der `state`schreibgeschützt (read-only) ist. Mit anderen Worten: Die `reducer`-Funktion muss **immer** eine neue Kopie von `state` zurückgeben und darf den Zustand niemals direkt verändern. Redux erzwingt keine Zustandsunveränderlichkeit, du bist jedoch dafür verantwortlich, sie im Code deiner Reducer-Funktionen zu erzwingen. Das wirst du in späteren Aufgaben üben.
+
+# --instructions--
+
+Der Code-Editor enthält das vorherige Beispiel sowie den Anfang einer `reducer`-Funktion für dich. Fülle den Körper der `reducer`-Funktion so aus, dass sie, wenn sie eine Aktion des Typs `'LOGIN'` empfängt, ein Zustandsobjekt zurückgibt, bei dem `login` auf `true` gesetzt ist. Ansonsten wird der aktuelle `state` zurückgegeben. Beachte, dass der aktuelle `state` und die gesendete `action` an den Reducer übergeben werden, sodass du mit `action.type` direkt auf den Typ der Aktion zugreifen kannst.
+
+# --hints--
+
+Der Aufruf der Funktion `loginAction` sollte ein Objekt zurückgeben, dessen type-Eigenschaft auf den String `LOGIN` gesetzt ist.
+
+```js
+assert(loginAction().type === 'LOGIN');
+```
+
+Der Store sollte mit einem Objekt initialisiert werden, dessen Eigenschaft `login` auf `false` gesetzt ist.
+
+```js
+assert(store.getState().login === false);
+```
+
+Der Versand von `loginAction` sollte die `login`-Eigenschaft im Store-Zustand auf `true` aktualisieren.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch(loginAction());
+ const afterState = store.getState();
+ return initialState.login === false && afterState.login === true;
+ })()
+);
+```
+
+Wenn die Aktion nicht vom Typ `LOGIN` ist, sollte der Store den aktuellen Zustand zurückgeben.
+
+```js
+assert(
+ (function () {
+ store.dispatch({ type: '__TEST__ACTION__' });
+ let afterTest = store.getState();
+ return typeof afterTest === 'object' && afterTest.hasOwnProperty('login');
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const defaultState = {
+ login: false
+};
+
+const reducer = (state = defaultState, action) => {
+ // Change code below this line
+
+ // Change code above this line
+};
+
+const store = Redux.createStore(reducer);
+
+const loginAction = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+```
+
+# --solutions--
+
+```js
+const defaultState = {
+ login: false
+};
+
+const reducer = (state = defaultState, action) => {
+
+ if (action.type === 'LOGIN') {
+ return {login: true}
+ }
+
+ else {
+ return state
+ }
+
+};
+
+const store = Redux.createStore(reducer);
+
+const loginAction = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/never-mutate-state.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/never-mutate-state.md
new file mode 100644
index 00000000000..ee0e5434209
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/never-mutate-state.md
@@ -0,0 +1,133 @@
+---
+id: 5a24c314108439a4d4036158
+title: Niemals einen State verändern
+challengeType: 6
+forumTopicId: 301445
+dashedName: never-mutate-state
+---
+
+# --description--
+
+Diese letzten Aufgaben beschreiben verschiedene Methoden, um das Schlüsselprinzip der Unveränderlichkeit von Zuständen in Redux durchzusetzen. Unveränderlicher Zustand(State) bedeutet, dass du den Zustand nie direkt veränderst, sondern eine neue Kopie des Zustands zurückgibst.
+
+Wenn du einen Schnappschuss des Zustands einer Redux-App im Laufe der Zeit machen würdest, würdest du so etwas sehen wie `state 1`, `state 2`, `state 3`,`state 4`, `...` und so weiter, wobei jeder Zustand dem letzten ähnlich sein kann, aber jeder eine eigene Dateneinheit ist. Diese Unveränderlichkeit ermöglicht Funktionen wie das Zeitreise-Debugging (time-travel debugging), von dem du vielleicht schon gehört hast.
+
+Redux erzwingt nicht aktiv die Unveränderlichkeit des Zustands im Store oder in den Reducern, diese Verantwortung liegt bei den Programmierern. Zum Glück bietet JavaScript (insbesondere ES6) einige nützliche Werkzeuge, mit denen du die Unveränderlichkeit deines Zustands erzwingen kannst, egal ob es sich um `string`, `number`, `array` oder `object` handelt. Beachte, dass Strings und Zahlen einfache Werte und von Natur aus unveränderlich sind. Mit anderen Worten: 3 ist immer 3. Du kannst den Wert der Zahl 3 nicht ändern. Ein `array` oder `object` ist dagegen veränderbar (mutable). In der Praxis wird dein Zustand wahrscheinlich aus einem `array` oder `object` bestehen, da dies nützliche Datenstrukturen sind, um viele Arten von Informationen darzustellen.
+
+# --instructions--
+
+Es gibt einen `store` und einen `reducer` im Code-Editor, um To-Do-Elemente zu verwalten. Vervollständige den `ADD_TO_DO` Fall im Reducer, um ein neues To-Do-Element an den Zustand anzuhängen. Es gibt ein paar Möglichkeiten, dies mit Standard-JavaScript oder ES6 zu erreichen. Schau, ob du einen Weg findest, ein neues Array mit dem Element aus `action.todo` an das Ende anzuhängen.
+
+# --hints--
+
+Der Redux-Store sollte existieren und mit einem Zustand initialisiert werden, der dem Array `todos` im Code-Editor entspricht.
+
+```js
+assert(
+ (function () {
+ const todos = [
+ 'Go to the store',
+ 'Clean the house',
+ 'Cook dinner',
+ 'Learn to code'
+ ];
+ const initialState = store.getState();
+ return (
+ Array.isArray(initialState) && initialState.join(',') === todos.join(',')
+ );
+ })()
+);
+```
+
+`addToDo` und `immutableReducer` sollten beide Funktionen sein.
+
+```js
+assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
+```
+
+Eine Aktion vom Typ `ADD_TO_DO` im Redux-Store sollte ein `todo`-Element hinzufügen und NICHT den Zustand verändern.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ const isFrozen = DeepFreeze(initialState);
+ store.dispatch(addToDo('__TEST__TO__DO__'));
+ const finalState = store.getState();
+ const expectedState = [
+ 'Go to the store',
+ 'Clean the house',
+ 'Cook dinner',
+ 'Learn to code',
+ '__TEST__TO__DO__'
+ ];
+ return isFrozen && DeepEqual(finalState, expectedState);
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const ADD_TO_DO = 'ADD_TO_DO';
+
+// A list of strings representing tasks to do:
+const todos = [
+ 'Go to the store',
+ 'Clean the house',
+ 'Cook dinner',
+ 'Learn to code',
+];
+
+const immutableReducer = (state = todos, action) => {
+ switch(action.type) {
+ case ADD_TO_DO:
+ // Don't mutate state here or the tests will fail
+ return
+ default:
+ return state;
+ }
+};
+
+const addToDo = (todo) => {
+ return {
+ type: ADD_TO_DO,
+ todo
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
+
+# --solutions--
+
+```js
+const ADD_TO_DO = 'ADD_TO_DO';
+
+const todos = [
+ 'Go to the store',
+ 'Clean the house',
+ 'Cook dinner',
+ 'Learn to code',
+];
+
+const immutableReducer = (state = todos, action) => {
+ switch(action.type) {
+ case ADD_TO_DO:
+ return state.concat(action.todo);
+ default:
+ return state;
+ }
+};
+
+const addToDo = (todo) => {
+ return {
+ type: ADD_TO_DO,
+ todo
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/register-a-store-listener.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/register-a-store-listener.md
new file mode 100644
index 00000000000..882873598b1
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/register-a-store-listener.md
@@ -0,0 +1,118 @@
+---
+id: 5a24c314108439a4d4036153
+title: Einen Store Listener registrieren
+challengeType: 6
+forumTopicId: 301446
+dashedName: register-a-store-listener
+---
+
+# --description--
+
+Eine weitere Methode, auf die du beim Redux `store`-Objekt Zugriff hast, ist `store.subscribe()`. Damit kannst du Listener-Funktionen für den Store abonnieren, die immer dann aufgerufen werden, wenn eine Aktion für den Store ausgelöst wird. Eine einfache Anwendung für diese Methode ist es, eine Funktion für deinen Store zu abonnieren, die einfach jedes Mal eine Nachricht protokolliert, wenn eine Aktion empfangen und der Store aktualisiert wird.
+
+# --instructions--
+
+Schreibe eine Callback-Funktion, die die globale Variable `count` jedes Mal erhöht, wenn der Store eine Aktion erhält, und übergebe diese Funktion an die Methode `store.subscribe()`. Du wirst sehen, dass `store.dispatch()` dreimal hintereinander aufgerufen wird, wobei jedes Mal direkt ein Aktionsobjekt übergeben wird. Schau dir die Konsolenausgabe zwischen den gesendeten Aktionen an, um zu sehen, welche Aktualisierungen vorgenommen wurden.
+
+# --hints--
+
+Wenn du die `ADD`-Aktion im Store ausführst, wird der Zustand um `1` erhöht.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch({ type: 'ADD' });
+ const newState = store.getState();
+ return newState === initialState + 1;
+ })()
+);
+```
+
+Es sollte eine Listener-Funktion vorhanden sein, die den Store mit `store.subscribe` abonniert.
+
+```js
+(getUserInput) => assert(getUserInput('index').match(/store\s*\.\s*subscribe\(/gm));
+```
+
+`store.subscribe` sollte eine Funktion erhalten.
+
+```js
+(getUserInput) => assert(getUserInput('index').match(/(\s*function\s*)|(\s*\(\s*\)\s*=>)/gm))
+```
+
+Der Callback zu `store.subscribe` sollte auch die globale `count`-Variable erhöhen, wenn der Store aktualisiert wird.
+
+```js
+assert(store.getState() === count);
+```
+
+# --seed--
+
+## --before-user-code--
+
+```js
+count = 0;
+```
+
+## --seed-contents--
+
+```js
+const ADD = 'ADD';
+
+const reducer = (state = 0, action) => {
+ switch(action.type) {
+ case ADD:
+ return state + 1;
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(reducer);
+
+// Global count variable:
+let count = 0;
+
+// Change code below this line
+
+// Change code above this line
+
+store.dispatch({type: ADD});
+console.log(count);
+store.dispatch({type: ADD});
+console.log(count);
+store.dispatch({type: ADD});
+console.log(count);
+```
+
+# --solutions--
+
+```js
+const ADD = 'ADD';
+
+const reducer = (state = 0, action) => {
+ switch(action.type) {
+ case ADD:
+ return state + 1;
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(reducer);
+ let count = 0;
+// Change code below this line
+
+store.subscribe( () =>
+ {
+ count++;
+ }
+);
+
+// Change code above this line
+
+store.dispatch({type: ADD});
+store.dispatch({type: ADD});
+store.dispatch({type: ADD});
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/remove-an-item-from-an-array.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/remove-an-item-from-an-array.md
new file mode 100644
index 00000000000..fecd46e9064
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/remove-an-item-from-an-array.md
@@ -0,0 +1,114 @@
+---
+id: 5a24c314108439a4d403615a
+title: Ein Element aus einem Array entfernen
+challengeType: 6
+forumTopicId: 301447
+dashedName: remove-an-item-from-an-array
+---
+
+# --description--
+
+Es ist Zeit, das Entfernen von Elementen aus einem Array zu üben. Auch hier kann der Spread-Operator verwendet werden. Andere nützliche JavaScript-Methoden sind `slice()` und `concat()`.
+
+# --instructions--
+
+Der Reducer und der Action Creator wurden geändert, um ein Element aus einem Array zu entfernen, das auf dem Index des Elements basiert. Beende das Schreiben des Reducers, so dass ein neues Zustandsarray zurückgegeben wird, aus dem das Element mit dem bestimmten Index entfernt wurde.
+
+# --hints--
+
+Der Redux-Store sollte existieren und mit einem Zustand gleich `[0,1,2,3,4,5]` initialisiert werden.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ return (
+ Array.isArray(initialState) === true &&
+ DeepEqual(initialState, [0, 1, 2, 3, 4, 5])
+ );
+ })()
+);
+```
+
+`removeItem` und `immutableReducer` sollten beide Funktionen sein.
+
+```js
+assert(
+ typeof removeItem === 'function' && typeof immutableReducer === 'function'
+);
+```
+
+Das Senden des `removeItem` Action Creators sollte Elemente aus dem Zustand entfernen und den Zustand NICHT verändern.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ const isFrozen = DeepFreeze(initialState);
+ store.dispatch(removeItem(3));
+ const state_1 = store.getState();
+ store.dispatch(removeItem(2));
+ const state_2 = store.getState();
+ store.dispatch(removeItem(0));
+ store.dispatch(removeItem(0));
+ store.dispatch(removeItem(0));
+ const state_3 = store.getState();
+ return (
+ isFrozen &&
+ DeepEqual(state_1, [0, 1, 2, 4, 5]) &&
+ DeepEqual(state_2, [0, 1, 4, 5]) &&
+ DeepEqual(state_3, [5])
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const immutableReducer = (state = [0,1,2,3,4,5], action) => {
+ switch(action.type) {
+ case 'REMOVE_ITEM':
+ // Don't mutate state here or the tests will fail
+ return
+ default:
+ return state;
+ }
+};
+
+const removeItem = (index) => {
+ return {
+ type: 'REMOVE_ITEM',
+ index
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
+
+# --solutions--
+
+```js
+const immutableReducer = (state = [0,1,2,3,4,5], action) => {
+ switch(action.type) {
+ case 'REMOVE_ITEM':
+ return [
+ ...state.slice(0, action.index),
+ ...state.slice(action.index + 1)
+ ];
+ default:
+ return state;
+ }
+};
+
+const removeItem = (index) => {
+ return {
+ type: 'REMOVE_ITEM',
+ index
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/send-action-data-to-the-store.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/send-action-data-to-the-store.md
new file mode 100644
index 00000000000..51c2d070e43
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/send-action-data-to-the-store.md
@@ -0,0 +1,107 @@
+---
+id: 5a24c314108439a4d4036155
+title: Aktionsdaten an den Store senden
+challengeType: 6
+forumTopicId: 301448
+dashedName: send-action-data-to-the-store
+---
+
+# --description--
+
+Inzwischen hast du gelernt, wie man Aktionen an den Redux-Store sendet, aber bis jetzt haben diese Aktionen keine anderen Informationen als einen `type` enthalten. Du kannst auch bestimmte Daten zusammen mit deinen Aktionen senden. Das ist sogar sehr häufig der Fall, da Aktionen in der Regel auf eine Benutzerinteraktion zurückgehen und einige Daten mit sich bringen. Der Redux-Store muss oft über diese Daten Bescheid wissen.
+
+# --instructions--
+
+Es gibt einen einfachen `notesReducer()` und einen `addNoteText()` Action Creator, die im Code-Editor definiert sind. Stelle den Körper der `addNoteText()`-Funktion fertig, damit sie ein `action`-Objekt zurückgibt. Das Objekt sollte eine `type`-Eigenschaft mit dem Wert `ADD_NOTE` und eine `text`-Eigenschaft enthalten, die auf die `note`-Daten gesetzt ist, die an den Action Creator übergeben werden. Wenn du den Action Creator aufrufst, übergibst du bestimmte Notizinformationen, auf die du für das Objekt zugreifen kannst.
+
+Als Nächstes schreibst du die `switch`-Anweisung im `notesReducer()` fertig. Du musst einen Fall hinzufügen, der die `addNoteText()`-Aktionen behandelt. Dieser Fall sollte immer dann ausgelöst werden, wenn es eine Aktion vom Typ `ADD_NOTE` gibt und er sollte die `text`-Eigenschaft der eingehenden `action` als neuen `state` zurückgeben.
+
+Die Aktion wird im letzten Teil des Codes gesendet. Wenn du fertig bist, führe den Code aus und beobachte die Konsole. Das ist alles, was du brauchst, um aktionsspezifische Daten an den Store zu senden und sie zu verwenden, wenn du den `state` des Stores aktualisierst.
+
+# --hints--
+
+Der Action Creator `addNoteText` sollte ein Objekt mit den Schlüsseln `type` und `text` zurückgeben.
+
+```js
+assert(
+ (function () {
+ const addNoteFn = addNoteText('__TEST__NOTE');
+ return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE';
+ })()
+);
+```
+
+Wenn du eine Aktion des Typs `ADD_NOTE` mit dem Action Creator `addNoteText` versendest, sollte der `state` auf den String aktualisiert werden, der dem Action Creator übergeben wurde.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch(addNoteText('__TEST__NOTE'));
+ const newState = store.getState();
+ return initialState !== newState && newState === '__TEST__NOTE';
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const ADD_NOTE = 'ADD_NOTE';
+
+const notesReducer = (state = 'Initial State', action) => {
+ switch(action.type) {
+ // Change code below this line
+
+ // Change code above this line
+ default:
+ return state;
+ }
+};
+
+const addNoteText = (note) => {
+ // Change code below this line
+
+ // Change code above this line
+};
+
+const store = Redux.createStore(notesReducer);
+
+console.log(store.getState());
+store.dispatch(addNoteText('Hello!'));
+console.log(store.getState());
+```
+
+# --solutions--
+
+```js
+const ADD_NOTE = 'ADD_NOTE';
+
+const notesReducer = (state = 'Initial State', action) => {
+ switch(action.type) {
+ // Change code below this line
+ case ADD_NOTE:
+ return action.text;
+ // Change code above this line
+ default:
+ return state;
+ }
+};
+
+const addNoteText = (note) => {
+ // Change code below this line
+ return {
+ type: ADD_NOTE,
+ text: note
+ }
+ // Change code above this line
+};
+
+const store = Redux.createStore(notesReducer);
+
+console.log(store.getState());
+store.dispatch(addNoteText('Hello Redux!'));
+console.log(store.getState());
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.md
new file mode 100644
index 00000000000..3d56fd25105
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.md
@@ -0,0 +1,152 @@
+---
+id: 5a24c314108439a4d4036151
+title: Verwende eine Switch-Anweisung, um mehrere Aktionen zu verarbeiten
+challengeType: 6
+forumTopicId: 301449
+dashedName: use-a-switch-statement-to-handle-multiple-actions
+---
+
+# --description--
+
+Du kannst dem Redux-Store mitteilen, wie er mit mehreren Aktionstypen umgehen soll. Angenommen, du verwaltest die Benutzerauthentifizierung in deinem Redux-Store. Du möchtest eine Zustandsrepräsentation dafür haben, wann Nutzer ein- und wann sie abgemeldet sind. Du stellst dies durch ein einzelnes Zustandsobjekt mit der Eigenschaft `authenticated` dar. Du brauchst auch Action Creators, die Aktionen für die Benutzeranmeldung und -abmeldung erstellen, sowie die Aktionsobjekte selbst.
+
+# --instructions--
+
+Der Code-Editor hat einen Store, Aktionen und Action Creators für dich eingerichtet. Fülle die `reducer`-Funktion aus, um mehrere Authentifizierungsaktionen zu behandeln. Verwende eine JavaScript `switch`-Anweisung im `reducer`, um auf verschiedene Aktionsereignisse zu reagieren. Dies ist ein Standardmuster beim Schreiben von Redux-Reducern. Die switch-Anweisung sollte `action.type` umschalten und den entsprechenden Authentifizierungszustand zurückgeben.
+
+**Hinweis:** Mach dir an dieser Stelle keine Gedanken über die Unveränderlichkeit des Zustands, da er diesem Beispiel klein und einfach ist. Für jede Aktion kannst du ein neues Objekt zurückgeben - zum Beispiel `{authenticated: true}`. Vergiss auch nicht, einen `default`-Fall in deine switch-Anweisung zu schreiben, der den aktuellen `state` zurückgibt. Das ist wichtig, denn wenn deine App mehrere Reducer hat, werden sie alle ausgeführt, sobald eine Aktion ausgelöst wird, auch wenn die Aktion nicht mit diesem Reducer verbunden ist. In einem solchen Fall solltest du sicherstellen, dass du den aktuellen `state` zurückgibst.
+
+# --hints--
+
+Der Aufruf der Funktion `loginUser` sollte ein Objekt zurückgeben, dessen type-Eigenschaft auf den String `LOGIN` gesetzt ist.
+
+```js
+assert(loginUser().type === 'LOGIN');
+```
+
+Der Aufruf der Funktion `logoutUser` sollte ein Objekt zurückgeben, dessen Typ-Eigenschaft auf den String `LOGOUT` gesetzt ist.
+
+```js
+assert(logoutUser().type === 'LOGOUT');
+```
+
+Der Store sollte mit einem Objekt initialisiert werden, dessen `authenticated` Eigenschaft auf `false` gesetzt ist.
+
+```js
+assert(store.getState().authenticated === false);
+```
+
+Das Versenden von `loginUser` sollte die Eigenschaft `authenticated` im Store-Zustand auf `true` aktualisieren.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch(loginUser());
+ const afterLogin = store.getState();
+ return (
+ initialState.authenticated === false && afterLogin.authenticated === true
+ );
+ })()
+);
+```
+
+Der Versand von `logoutUser` sollte die Eigenschaft `authenticated` im Store-Zustand auf `false` aktualisieren.
+
+```js
+assert(
+ (function () {
+ store.dispatch(loginUser());
+ const loggedIn = store.getState();
+ store.dispatch(logoutUser());
+ const afterLogout = store.getState();
+ return (
+ loggedIn.authenticated === true && afterLogout.authenticated === false
+ );
+ })()
+);
+```
+
+Die `authReducer`-Funktion sollte mehrere Aktionstypen mit einer `switch`-Anweisung behandeln.
+
+```js
+(getUserInput) =>
+ assert(
+ getUserInput('index').toString().includes('switch') &&
+ getUserInput('index').toString().includes('case') &&
+ getUserInput('index').toString().includes('default')
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const defaultState = {
+ authenticated: false
+};
+
+const authReducer = (state = defaultState, action) => {
+ // Change code below this line
+
+ // Change code above this line
+};
+
+const store = Redux.createStore(authReducer);
+
+const loginUser = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+
+const logoutUser = () => {
+ return {
+ type: 'LOGOUT'
+ }
+};
+```
+
+# --solutions--
+
+```js
+const defaultState = {
+ authenticated: false
+};
+
+const authReducer = (state = defaultState, action) => {
+
+ switch (action.type) {
+
+ case 'LOGIN':
+ return {
+ authenticated: true
+ }
+
+ case 'LOGOUT':
+ return {
+ authenticated: false
+ }
+
+ default:
+ return state;
+
+ }
+
+};
+
+const store = Redux.createStore(authReducer);
+
+const loginUser = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+
+const logoutUser = () => {
+ return {
+ type: 'LOGOUT'
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/use-const-for-action-types.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-const-for-action-types.md
new file mode 100644
index 00000000000..0a97dd01524
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-const-for-action-types.md
@@ -0,0 +1,202 @@
+---
+id: 5a24c314108439a4d4036152
+title: Verwende const für Aktionstypen
+challengeType: 6
+forumTopicId: 301450
+dashedName: use-const-for-action-types
+---
+
+# --description--
+
+Eine gängige Praxis bei der Arbeit mit Redux ist es, Aktionstypen als schreibgeschützte Konstanten zuzuweisen und diese Konstanten dann überall dort zu referenzieren, wo sie verwendet werden. Du kannst den Code, mit dem du arbeitest, so umgestalten, dass du die Aktionstypen als `const`-Deklarationen schreibst.
+
+# --instructions--
+
+Deklariere `LOGIN` und `LOGOUT` als `const`-Werte und weise sie den Strings `'LOGIN'` bzw. `'LOGOUT'` zu. Dann bearbeite die `authReducer()` und die Action Creators so, dass sie diese Konstanten anstelle von String-Werten referenzieren.
+
+**Hinweis:** Es ist allgemein üblich, Konstanten in Großbuchstaben zu schreiben, und das ist auch in Redux Standard.
+
+# --hints--
+
+Der Aufruf der Funktion `loginUser` sollte ein Objekt zurückgeben, dessen `type`-Eigenschaft auf den den String `LOGIN` gesetzt ist.
+
+```js
+assert(loginUser().type === 'LOGIN');
+```
+
+Der Aufruf der Funktion `logoutUser` sollte ein Objekt zurückgeben, dessen `type`-Eigenschaft auf den String `LOGOUT` gesetzt ist.
+
+```js
+assert(logoutUser().type === 'LOGOUT');
+```
+
+Der Store sollte mit einem Objekt initialisiert werden, dessen `login`-Eigenschaft auf `false` gesetzt ist.
+
+```js
+assert(store.getState().authenticated === false);
+```
+
+Das Senden von `loginUser` sollte die Eigenschaft `login` im Store-Zustand auf `true` aktualisieren.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch(loginUser());
+ const afterLogin = store.getState();
+ return (
+ initialState.authenticated === false && afterLogin.authenticated === true
+ );
+ })()
+);
+```
+
+Das Senden von `logoutUser` sollte die Eigenschaft `login` im Store-Zustand auf `false` aktualisieren.
+
+```js
+assert(
+ (function () {
+ store.dispatch(loginUser());
+ const loggedIn = store.getState();
+ store.dispatch(logoutUser());
+ const afterLogout = store.getState();
+ return (
+ loggedIn.authenticated === true && afterLogout.authenticated === false
+ );
+ })()
+);
+```
+
+Die `authReducer`-Funktion sollte mehrere Aktionstypen mit einer Switch-Anweisung behandeln.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ return (
+ typeof authReducer === 'function' &&
+ getUserInput('index').toString().includes('switch') &&
+ getUserInput('index').toString().includes('case') &&
+ getUserInput('index').toString().includes('default')
+ );
+ })()
+ );
+```
+
+`LOGIN` und `LOGOUT` sollten als `const`-Werte deklariert werden und mit den Strings `LOGIN` und `LOGOUT` belegt werden.
+
+```js
+const noWhiteSpace = __helpers.removeWhiteSpace(code);
+assert(LOGIN === 'LOGIN' && LOGOUT === 'LOGOUT')
+assert(noWhiteSpace.includes('const'))
+```
+
+Die Action Creators und der Reducer sollten die Konstanten `LOGIN` und `LOGOUT` referenzieren.
+
+```js
+(getUserInput) =>
+ assert(
+ (function () {
+ const noWhiteSpace = __helpers.removeWhiteSpace(
+ getUserInput('index').toString()
+ );
+ return (
+ noWhiteSpace.includes('caseLOGIN:') &&
+ noWhiteSpace.includes('caseLOGOUT:') &&
+ noWhiteSpace.includes('type:LOGIN') &&
+ noWhiteSpace.includes('type:LOGOUT')
+ );
+ })()
+ );
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+
+
+const defaultState = {
+ authenticated: false
+};
+
+const authReducer = (state = defaultState, action) => {
+
+ switch (action.type) {
+ case 'LOGIN':
+ return {
+ authenticated: true
+ }
+ case 'LOGOUT':
+ return {
+ authenticated: false
+ }
+
+ default:
+ return state;
+
+ }
+
+};
+
+const store = Redux.createStore(authReducer);
+
+const loginUser = () => {
+ return {
+ type: 'LOGIN'
+ }
+};
+
+const logoutUser = () => {
+ return {
+ type: 'LOGOUT'
+ }
+};
+```
+
+# --solutions--
+
+```js
+const LOGIN = 'LOGIN';
+const LOGOUT = 'LOGOUT';
+
+const defaultState = {
+ authenticated: false
+};
+
+const authReducer = (state = defaultState, action) => {
+
+ switch (action.type) {
+
+ case LOGIN:
+ return {
+ authenticated: true
+ }
+
+ case LOGOUT:
+ return {
+ authenticated: false
+ }
+
+ default:
+ return state;
+
+ }
+
+};
+
+const store = Redux.createStore(authReducer);
+
+const loginUser = () => {
+ return {
+ type: LOGIN
+ }
+};
+
+const logoutUser = () => {
+ return {
+ type: LOGOUT
+ }
+};
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/use-middleware-to-handle-asynchronous-actions.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-middleware-to-handle-asynchronous-actions.md
new file mode 100644
index 00000000000..069be1e1565
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-middleware-to-handle-asynchronous-actions.md
@@ -0,0 +1,170 @@
+---
+id: 5a24c314108439a4d4036156
+title: Verwende Middleware, um asynchrone Aktionen abzuwickeln
+challengeType: 6
+forumTopicId: 301451
+dashedName: use-middleware-to-handle-asynchronous-actions
+---
+
+# --description--
+
+Bisher haben wir es vermieden, asynchrone Aktionen zu diskutieren, aber sie sind ein unvermeidbarer Teil der Webentwicklung. Irgendwann wirst du in deiner Redux-App asynchrone Endpunkte aufrufen müssen. Wie gehst du mit solchen Anfragen um? Redux bietet eine Middleware, die speziell für diesen Zweck entwickelt wurde, die Redux Thunk Middleware. Hier ist eine kurze Beschreibung, wie du diese mit Redux nutzen kannst.
+
+Um Redux Thunk Middleware einzubinden, übergibst du sie als Argument an `Redux.applyMiddleware()`. Diese Anweisung wird dann als zweiter optionaler Parameter an die `createStore()`-Funktion übergeben. Sieh dir den Code unten im Editor an, um das zu sehen. Um dann eine asynchrone Aktion zu erstellen, gibst du eine Funktion im Action Creator zurück, die `dispatch` als Argument erhält. Innerhalb dieser Funktion kannst du Aktionen ausführen und asynchrone Anfragen stellen.
+
+In diesem Beispiel wird eine asynchrone Anfrage mit einem `setTimeout()`-Aufruf simuliert. Es ist üblich, eine Aktion auszulösen, bevor ein asynchrones Verhalten ausgelöst wird, damit dein Anwendungszustand weiß, dass Daten angefordert werden (dieser Zustand könnte z. B. ein Ladesymbol anzeigen). Sobald du die Daten erhalten hast, schickst du eine weitere Aktion ab, die die Daten als Nutzlast zusammen mit der Information, dass die Aktion abgeschlossen ist, enthält.
+
+Denke daran, dass du `dispatch` als Parameter an diesen speziellen Action Creator übergibst. Du kannst dies nutzen, um Aktionen zu versenden. Übergib die Aktion einfach direkt an den Dispatcher und die Middleware erledigt den Rest.
+
+# --instructions--
+
+Schreibe beide Dispatches in den `handleAsync()` Action Creator. Schicke `requestingData()` vor dem `setTimeout()` (dem simulierten API-Aufruf). Nachdem du die (simulierten) Daten erhalten hast, schickst du die `receivedData()`-Aktion ab und übergibst diese Daten. Jetzt weißt du, wie du asynchrone Aktionen in Redux handhaben kannst. Alles andere verhält sich weiterhin wie bisher.
+
+# --hints--
+
+Der `requestingData` Action Creator sollte ein Objekt vom Typ gleich dem Wert von `REQUESTING_DATA` zurückgeben.
+
+```js
+assert(requestingData().type === REQUESTING_DATA);
+```
+
+Der `receivedData` Action Creator sollte ein Objekt vom Typ gleich dem Wert von `RECEIVED_DATA` zurückgeben.
+
+```js
+assert(receivedData('data').type === RECEIVED_DATA);
+```
+
+`asyncDataReducer` sollte eine Funktion sein.
+
+```js
+assert(typeof asyncDataReducer === 'function');
+```
+
+Das Ausführen der Aktion `requestingData` sollte die Eigenschaft `state` von fetching auf `true` aktualisieren.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ store.dispatch(requestingData());
+ const reqState = store.getState();
+ return initialState.fetching === false && reqState.fetching === true;
+ })()
+);
+```
+
+Der `handleAsync`-Versand sollte die Datenanforderungsaktion senden und dann nach einer Verzögerung die Datenempfangsaktion senden.
+
+```js
+assert(
+ (function () {
+ const noWhiteSpace = __helpers.removeWhiteSpace(handleAsync.toString());
+ return (
+ noWhiteSpace.includes('dispatch(requestingData())') === true &&
+ noWhiteSpace.includes('dispatch(receivedData(data))') === true
+ );
+ })()
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const REQUESTING_DATA = 'REQUESTING_DATA'
+const RECEIVED_DATA = 'RECEIVED_DATA'
+
+const requestingData = () => { return {type: REQUESTING_DATA} }
+const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
+
+const handleAsync = () => {
+ return function(dispatch) {
+ // Dispatch request action here
+
+ setTimeout(function() {
+ let data = {
+ users: ['Jeff', 'William', 'Alice']
+ }
+ // Dispatch received data action here
+
+ }, 2500);
+ }
+};
+
+const defaultState = {
+ fetching: false,
+ users: []
+};
+
+const asyncDataReducer = (state = defaultState, action) => {
+ switch(action.type) {
+ case REQUESTING_DATA:
+ return {
+ fetching: true,
+ users: []
+ }
+ case RECEIVED_DATA:
+ return {
+ fetching: false,
+ users: action.users
+ }
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(
+ asyncDataReducer,
+ Redux.applyMiddleware(ReduxThunk.default)
+);
+```
+
+# --solutions--
+
+```js
+const REQUESTING_DATA = 'REQUESTING_DATA'
+const RECEIVED_DATA = 'RECEIVED_DATA'
+
+const requestingData = () => { return {type: REQUESTING_DATA} }
+const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
+
+const handleAsync = () => {
+ return function(dispatch) {
+ dispatch(requestingData());
+ setTimeout(function() {
+ let data = {
+ users: ['Jeff', 'William', 'Alice']
+ }
+ dispatch(receivedData(data));
+ }, 2500);
+ }
+};
+
+const defaultState = {
+ fetching: false,
+ users: []
+};
+
+const asyncDataReducer = (state = defaultState, action) => {
+ switch(action.type) {
+ case REQUESTING_DATA:
+ return {
+ fetching: true,
+ users: []
+ }
+ case RECEIVED_DATA:
+ return {
+ fetching: false,
+ users: action.users
+ }
+ default:
+ return state;
+ }
+};
+
+const store = Redux.createStore(
+ asyncDataReducer,
+ Redux.applyMiddleware(ReduxThunk.default)
+);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/use-the-spread-operator-on-arrays.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-the-spread-operator-on-arrays.md
new file mode 100644
index 00000000000..91ecb7b905a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/use-the-spread-operator-on-arrays.md
@@ -0,0 +1,114 @@
+---
+id: 5a24c314108439a4d4036159
+title: Den Spread-Operator auf Arrays anwenden
+challengeType: 6
+forumTopicId: 301452
+dashedName: use-the-spread-operator-on-arrays
+---
+
+# --description--
+
+Eine Lösung aus ES6, um die Unveränderlichkeit des Zustands in Redux zu erzwingen, ist der Spread-Operator: `...`. Der Spread-Operator hat eine Vielzahl von Anwendungsmöglichkeiten, von denen eine gut geeignet ist, um aus einem bestehenden Array ein neues Array zu erstellen. Dies ist eine relativ neue, aber häufig verwendete Syntax. Wenn du zum Beispiel ein Array `myArray` hast und schreibst:
+
+```js
+let newArray = [...myArray];
+```
+
+`newArray` ist jetzt ein Klon von `myArray`. Beide Arrays existieren weiterhin separat im Speicher. Wenn du eine Veränderung durchführst wie `newArray.push(5)`, ändert sich `myArray` nicht. Der `...` * überträgt * die Werte aus `myArray` in ein neues Array. Um ein Array zu klonen, aber zusätzliche Werte im neuen Array hinzuzufügen, könntest du `[...myArray, 'new value']` schreiben. Dies würde ein neues Array zurückgeben, das aus den Werten in `myArray` und dem String `new value` als letztem Wert besteht. Die Spread-Syntax kann in der Array-Zusammensetzung mehrfach verwendet werden, aber es ist wichtig zu beachten, dass sie nur eine einfache Kopie des Arrays erstellt. Das bedeutet, dass es nur unveränderliche Array-Operationen für eindimensionale Arrays bietet.
+
+# --instructions--
+
+Verwende den Spread-Operator, um eine neue Kopie des Zustands zurückzugeben, wenn ein To-Do-Element hinzugefügt wird.
+
+# --hints--
+
+Der Redux-Store sollte existieren und mit einem Zustand gleich `["Do not mutate state!"]` initialisiert werden.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ return (
+ Array.isArray(initialState) === true &&
+ initialState[0] === 'Do not mutate state!'
+ );
+ })()
+);
+```
+
+`addToDo` und `immutableReducer` sollten beide Funktionen sein.
+
+```js
+assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
+```
+
+Das Senden einer Aktion des Typs `ADD_TO_DO` im Redux-Store sollte ein `todo`-Element hinzufügen und NICHT den Zustand verändern.
+
+```js
+assert(
+ (function () {
+ const initialState = store.getState();
+ const isFrozen = DeepFreeze(initialState);
+ store.dispatch(addToDo('__TEST__TO__DO__'));
+ const finalState = store.getState();
+ const expectedState = ['Do not mutate state!', '__TEST__TO__DO__'];
+ return isFrozen && DeepEqual(finalState, expectedState);
+ })()
+);
+```
+
+Der Spread-Operator sollte verwendet werden, um einen neuen Zustand zu erhalten.
+
+```js
+(getUserInput) => assert(getUserInput('index').includes('...state'));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const immutableReducer = (state = ['Do not mutate state!'], action) => {
+ switch(action.type) {
+ case 'ADD_TO_DO':
+ // Don't mutate state here or the tests will fail
+ return
+ default:
+ return state;
+ }
+};
+
+const addToDo = (todo) => {
+ return {
+ type: 'ADD_TO_DO',
+ todo
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
+
+# --solutions--
+
+```js
+const immutableReducer = (state = ['Do not mutate state!'], action) => {
+ switch(action.type) {
+ case 'ADD_TO_DO':
+ return [
+ ...state,
+ action.todo
+ ];
+ default:
+ return state;
+ }
+};
+
+const addToDo = (todo) => {
+ return {
+ type: 'ADD_TO_DO',
+ todo
+ }
+}
+
+const store = Redux.createStore(immutableReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/redux/write-a-counter-with-redux.md b/curriculum/challenges/german/03-front-end-development-libraries/redux/write-a-counter-with-redux.md
new file mode 100644
index 00000000000..9a0821442ee
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/redux/write-a-counter-with-redux.md
@@ -0,0 +1,122 @@
+---
+id: 5a24c314108439a4d4036157
+title: Schreibe einen Zähler mit Redux
+challengeType: 6
+forumTopicId: 301453
+dashedName: write-a-counter-with-redux
+---
+
+# --description--
+
+Jetzt hast du alle Grundprinzipien von Redux kennengelernt! Du hast gesehen, wie du Actions und Action Creators erstellst, einen Redux-Store anlegst, deine Actions mit dem Store verteilst und Zustandsaktualisierungen mit reinen Reducern entwirfst. Du hast sogar gesehen, wie du komplexe Zustände mit der Komposition von Reducern verwalten und asynchrone Aktionen durchführen kannst. Diese Beispiele sind vereinfacht, aber diese Konzepte sind die Kernprinzipien von Redux. Wenn du sie verstanden hast, bist du bereit, deine eigene Redux-App zu entwickeln. In den nächsten Aufgaben geht es um einige Details zur Unveränderlichkeit von `state`, aber zuerst gibt es einen Überblick über alles, was du bis jetzt gelernt hast.
+
+# --instructions--
+
+In dieser Lektion wirst du einen einfachen Zähler mit Redux von Grund auf implementieren. Die Grundlagen werden im Code-Editor bereitgestellt, aber du musst die Details ergänzen! Verwende die bereitgestellten Namen und definiere die Action Creator `incAction` und `decAction`, die `counterReducer()`, `INCREMENT` und `DECREMENT` Aktionstypen und schließlich den Redux `store`. Sobald du fertig bist, solltest du `INCREMENT` oder `DECREMENT` Aktionen ausführen können, um den Zustand im `store` zu erhöhen oder zu verringern. Viel Erfolg beim Erstellen deiner ersten Redux-App!
+
+# --hints--
+
+Der Action Creator `incAction` sollte ein Action-Objekt mit `type` gleich dem Wert von `INCREMENT` zurückgeben.
+
+```js
+assert(incAction().type === INCREMENT);
+```
+
+Der Action Creator `decAction` sollte ein Action-Objekt mit `type` gleich dem Wert von `DECREMENT` zurückgeben
+
+```js
+assert(decAction().type === DECREMENT);
+```
+
+Der Redux-Store sollte mit einem `state` von 0 initialisiert werden.
+
+```js
+assert(_store.getState() === 0);
+```
+
+Das Senden von `incAction` an den Redux-Store sollte den `state` um 1 erhöhen.
+
+```js
+assert(
+ (function () {
+ const initialState = _store.getState();
+ _store.dispatch(incAction());
+ const incState = _store.getState();
+ return initialState + 1 === incState;
+ })()
+);
+```
+
+Das Senden von `decAction` an den Redux-Store sollte den `state` um 1 verringern.
+
+```js
+assert(
+ (function () {
+ const initialState = _store.getState();
+ _store.dispatch(decAction());
+ const decState = _store.getState();
+ return initialState - 1 === decState;
+ })()
+);
+```
+
+`counterReducer` sollte eine Funktion sein
+
+```js
+assert(typeof counterReducer === 'function');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+const INCREMENT = null; // Define a constant for increment action types
+const DECREMENT = null; // Define a constant for decrement action types
+
+const counterReducer = null; // Define the counter reducer which will increment or decrement the state based on the action it receives
+
+const incAction = null; // Define an action creator for incrementing
+
+const decAction = null; // Define an action creator for decrementing
+
+const store = null; // Define the Redux store here, passing in your reducers
+```
+
+## --after-user-code--
+
+```js
+const _store = Redux.createStore(counterReducer)
+```
+
+# --solutions--
+
+```js
+const INCREMENT = 'INCREMENT';
+const DECREMENT = 'DECREMENT';
+
+const counterReducer = (state = 0, action) => {
+ switch(action.type) {
+ case INCREMENT:
+ return state + 1;
+ case DECREMENT:
+ return state - 1;
+ default:
+ return state;
+ }
+};
+
+const incAction = () => {
+ return {
+ type: INCREMENT
+ }
+};
+
+const decAction = () => {
+ return {
+ type: DECREMENT
+ }
+};
+
+const store = Redux.createStore(counterReducer);
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/apply-a-style-until-a-condition-is-met-with-while.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/apply-a-style-until-a-condition-is-met-with-while.md
new file mode 100644
index 00000000000..1adbb48b44a
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/apply-a-style-until-a-condition-is-met-with-while.md
@@ -0,0 +1,117 @@
+---
+id: 587d7dbf367417b2b2512bbb
+title: Verwende mit @while einen Stil, bis eine Bedingung erfüllt ist
+challengeType: 0
+forumTopicId: 301454
+dashedName: apply-a-style-until-a-condition-is-met-with-while
+---
+
+# --description--
+
+Die `@while`-Direktive ist eine Option mit ähnlicher Funktionalität wie die `while`-Schleife in JavaScript. Sie erstellt CSS-Regeln, bis eine Bedingung erfüllt ist.
+
+In der Aufgabe zu `@for` gab es ein Beispiel, um ein einfaches Rastersystem (Grid) zu erstellen. Das kann auch mit `@while` funktionieren.
+
+```scss
+$x: 1;
+@while $x < 13 {
+ .col-#{$x} { width: 100%/12 * $x;}
+ $x: $x + 1;
+}
+```
+
+Zuerst definierst du eine Variable `$x` und setzt sie auf 1. Benutze als nächstes die `@while`-Direktive um das Rastersystem zu erstellen *während (while)* `$x` weniger als 13 ist. Nachdem die CSS-Regel für `width` gesetzt wurde, wird `$x` um 1 erhöht, um eine Endlosschleife zu vermeiden.
+
+# --instructions--
+
+Verwende `@while`, um eine Reihe von Klassen mit verschiedenen `font-sizes` zu erstellen.
+
+Es sollte 5 verschiedene Klassen geben, von `text-1` bis `text-5`. Dann setze `font-size` auf `15px` multipliziert mit der aktuellen Indexnummer. Achte darauf, eine Endlosschleife zu vermeiden!
+
+# --hints--
+
+Dein Code sollte die `@while`-Direktive verwenden.
+
+```js
+assert(code.match(/@while /g));
+```
+
+Dein Code sollte eine Indexvariable verwenden, die bei einem Index von 1 beginnt.
+
+```js
+assert(code.match(/\$.*:\s*?1;/gi));
+```
+
+Dein Code sollte die Zählervariable inkrementieren.
+
+```js
+assert(code.match(/\$(.*)\s*?:\s*\$\1\s*\+\s*1\s*;/gi));
+```
+
+Deine Klasse `.text-1` sollte eine `font-size` von `15px` besitzen.
+
+```js
+assert($('.text-1').css('font-size') == '15px');
+```
+
+Deine Klasse `.text-2` sollte eine `font-size` von `30px` besitzen.
+
+```js
+assert($('.text-2').css('font-size') == '30px');
+```
+
+Deine Klasse `.text-3` sollte eine `font-size` von `45px` besitzen.
+
+```js
+assert($('.text-3').css('font-size') == '45px');
+```
+
+Deine Klasse `.text-4` sollte eine `font-size` von `60px` besitzen.
+
+```js
+assert($('.text-4').css('font-size') == '60px');
+```
+
+Deine Klasse `.text-5` sollte eine `font-size` von `75px` besitzen.
+
+```js
+assert($('.text-5').css('font-size') == '75px');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+```
+
+# --solutions--
+
+```html
+
+
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/create-reusable-css-with-mixins.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/create-reusable-css-with-mixins.md
new file mode 100644
index 00000000000..73807cc37a9
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/create-reusable-css-with-mixins.md
@@ -0,0 +1,132 @@
+---
+id: 587d7dbd367417b2b2512bb6
+title: Erstelle wiederverwendbare CSS mit Mixins
+challengeType: 0
+forumTopicId: 301455
+dashedName: create-reusable-css-with-mixins
+---
+
+# --description--
+
+In Sass ist ein
Mixin eine Gruppe von CSS-Deklarationen, die im gesamten Stylesheet wiederverwendet werden können.
+
+Neuere CSS-Funktionen brauchen Zeit, bis sie vollständig übernommen werden und in allen Browsern einsatzbereit sind. Wenn neue Funktionen zu den Browsern hinzugefügt werden, benötigen CSS-Regeln, die diese nutzen, möglicherweise Herstellerpräfixe. Schau dir `box-shadow` an:
+
+```scss
+div {
+ -webkit-box-shadow: 0px 0px 4px #fff;
+ -moz-box-shadow: 0px 0px 4px #fff;
+ -ms-box-shadow: 0px 0px 4px #fff;
+ box-shadow: 0px 0px 4px #fff;
+}
+```
+
+Es ist eine Menge Tipparbeit, diese Regel für alle Elemente, die einen `box-shadow` haben, neu zu schreiben oder jeden Wert zu ändern, um verschiedene Effekte zu testen. Mixins sind wie Funktionen für CSS. Hier erfährst du, wie du eines schreiben kannst:
+
+```scss
+@mixin box-shadow($x, $y, $blur, $c){
+ -webkit-box-shadow: $x $y $blur $c;
+ -moz-box-shadow: $x $y $blur $c;
+ -ms-box-shadow: $x $y $blur $c;
+ box-shadow: $x $y $blur $c;
+}
+```
+
+Die Definition beginnt mit `@mixin`, gefolgt von einem eigenen Namen. Die Parameter (`$x`, `$y`, `$blur` und `$c` im obigen Beispiel) sind optional. Jedes Mal, wenn eine `box-shadow`-Regel benötigt wird, reicht jetzt eine einzige Zeile, die das Mixin aufruft, um alle Präfixe der Hersteller zu ersetzen. Ein Mixin wird mit der `@include` Direktive aufgerufen:
+
+```scss
+div {
+ @include box-shadow(0px, 0px, 4px, #fff);
+}
+```
+
+# --instructions--
+
+Schreibe ein Mixin für `border-radius` und gib ihm einen Parameter `$radius`. Es sollte alle Anbieter-Präfixe aus dem Beispiel verwenden. Dann benutze das Mixin `border-radius`, um dem Element `#awesome` einen border-radius von `15px` zu geben.
+
+# --hints--
+
+Dein Code sollte ein Mixin namens `border-radius` deklarieren, das einen Parameter mit dem Namen `$radius` hat.
+
+```js
+assert(code.match(/@mixin\s+?border-radius\s*?\(\s*?\$radius\s*?\)\s*?{/gi));
+```
+
+Dein Code sollte das Präfix `-webkit-border-radius` enthalten, das den Parameter `$radius` verwendet.
+
+```js
+assert(
+ __helpers.removeWhiteSpace(code).match(/-webkit-border-radius:\$radius;/gi)
+);
+```
+
+Dein Code sollte das Präfix `-moz-border-radius` enthalten, das den Parameter `$radius` verwendet.
+
+```js
+assert(
+ __helpers.removeWhiteSpace(code).match(/-moz-border-radius:\$radius;/gi)
+);
+```
+
+Dein Code sollte das Präfix `-ms-border-radius` enthalten, das den Parameter `$radius` verwendet.
+
+```js
+assert(__helpers.removeWhiteSpace(code).match(/-ms-border-radius:\$radius;/gi));
+```
+
+Dein Code sollte die allgemeine `border-radius`-Regel enthalten, die den `$radius` Parameter verwendet.
+
+```js
+assert(
+ __helpers.removeWhiteSpace(code).match(/border-radius:\$radius;/gi).length ==
+ 4
+);
+```
+
+Dein Code sollte das `border-radius mixin` mit dem Schlüsselwort `@include` aufrufen und es auf `15px` setzen.
+
+```js
+assert(code.match(/@include\s+?border-radius\(\s*?15px\s*?\)\s*;/gi));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/extend-one-set-of-css-styles-to-another-element.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/extend-one-set-of-css-styles-to-another-element.md
new file mode 100644
index 00000000000..cec790a7063
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/extend-one-set-of-css-styles-to-another-element.md
@@ -0,0 +1,116 @@
+---
+id: 587d7fa5367417b2b2512bbd
+title: Einen Satz CSS-Stile auf ein anderes Element erweitern
+challengeType: 0
+forumTopicId: 301456
+dashedName: extend-one-set-of-css-styles-to-another-element
+---
+
+# --description--
+
+Sass hat eine Funktion namens `extend`, die es einfach macht, die CSS-Regeln von einem Element zu übernehmen und in einem anderen darauf aufzubauen.
+
+Der folgende Block von CSS-Regeln stylt zum Beispiel eine `.panel`-Klasse. Es hat eine `background-color`, `height` und `border`.
+
+```scss
+.panel{
+ background-color: red;
+ height: 70px;
+ border: 2px solid green;
+}
+```
+
+Jetzt willst du ein weiteres Panel namens `.big-panel`. Es hat die gleichen Basiseigenschaften wie `.panel`, braucht aber zusätzlich eine `width` und `font-size`. Es ist möglich, die ursprünglichen CSS-Regeln aus `.panel` zu kopieren und einzufügen, aber der Code wiederholt sich, wenn du mehr Arten von Panels hinzufügst. Die `extend`-Direktive ist eine einfache Möglichkeit, die Regeln, die für ein Element geschrieben wurden, wiederzuverwenden und dann weitere für ein anderes hinzuzufügen:
+
+```scss
+.big-panel{
+ @extend .panel;
+ width: 150px;
+ font-size: 2em;
+}
+```
+
+Das `.big-panel` hat die gleichen Eigenschaften wie `.panel` zusätzlich zu den neuen Stilen.
+
+# --instructions--
+
+Erstelle eine Klasse `.info-important`, die `.info` erweitert und außerdem eine `background-color` hat, die auf Magenta gesetzt ist.
+
+# --hints--
+
+Deine Klasse `info-important` sollte eine `background-color` haben, die auf `magenta` gesetzt ist.
+
+```js
+assert(
+ code.match(
+ /\.info-important\s*?{[\s\S]*background-color\s*?:\s*?magenta\s*?;[\s\S]*}/gi
+ )
+);
+```
+
+Deine Klasse `info-important` sollte `@extend` verwenden, um das Styling von der Klasse `info` zu erben.
+
+```js
+assert(
+ code.match(/\.info-important\s*?{[\s\S]*@extend\s*?.info\s*?;[\s\S]*/gi)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
Posts
+
+
This is an important post. It should extend the class ".info" and have its own CSS styles.
+
+
+
+
This is a simple post. It has basic styling and can be extended for other uses.
+
+```
+
+# --solutions--
+
+```html
+
+
Posts
+
+
This is an important post. It should extend the class ".info" and have its own CSS styles.
+
+
+
+
This is a simple post. It has basic styling and can be extended for other uses.
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/nest-css-with-sass.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/nest-css-with-sass.md
new file mode 100644
index 00000000000..b39c63aaf93
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/nest-css-with-sass.md
@@ -0,0 +1,105 @@
+---
+id: 587d7dbd367417b2b2512bb5
+title: CSS mit Sass verschachteln
+challengeType: 0
+forumTopicId: 301457
+dashedName: nest-css-with-sass
+---
+
+# --description--
+
+Mit Sass kannst du CSS-Regeln verschachteln, was eine nützliche Methode ist, um ein Stylesheet zu organisieren.
+
+Normalerweise wird jedes Element auf eine andere Zeile ausgerichtet, um es zu stylen, etwa so:
+
+```scss
+nav {
+ background-color: red;
+}
+
+nav ul {
+ list-style: none;
+}
+
+nav ul li {
+ display: inline-block;
+}
+```
+
+Bei einem großen Projekt wird die CSS-Datei viele Zeilen und Regeln haben. Hier kann die Verschachtelung helfen, deinen Code zu organisieren, indem du untergeordnete Stilregeln innerhalb der jeweiligen übergeordneten Elemente platzierst:
+
+```scss
+nav {
+ background-color: red;
+
+ ul {
+ list-style: none;
+
+ li {
+ display: inline-block;
+ }
+ }
+}
+
+```
+
+# --instructions--
+
+Verwende die oben gezeigte Verschachtelungstechnik, um die CSS-Regeln für beide Kindelemente des `.blog-post`-Elements neu zu organisieren. Zu Testzwecken sollte das `h1` vor dem `p`-Element stehen.
+
+# --hints--
+
+Dein Code sollte die CSS-Regeln so umgestalten, dass das `h1` und `p` im Elternelement `.blog-post` verschachtelt sind.
+
+```js
+assert(
+ code.match(
+ /\.blog-post\s*?{\s*?h1\s*?{\s*?text-align:\s*?center;\s*?color:\s*?blue;\s*?}\s*?p\s*?{\s*?font-size:\s*?20px;\s*?}\s*?}/gi
+ )
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
Blog Title
+
This is a paragraph
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
Blog Title
+
This is a paragraph
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/split-your-styles-into-smaller-chunks-with-partials.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/split-your-styles-into-smaller-chunks-with-partials.md
new file mode 100644
index 00000000000..43fd069221b
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/split-your-styles-into-smaller-chunks-with-partials.md
@@ -0,0 +1,47 @@
+---
+id: 587d7dbf367417b2b2512bbc
+title: Teile deine Styles in kleinere Stücke mit Partials auf
+challengeType: 0
+forumTopicId: 301459
+dashedName: split-your-styles-into-smaller-chunks-with-partials
+---
+
+# --description--
+
+
Partials sind in Sass separate Dateien, die Segmente von CSS-Code enthalten. Diese werden importiert und in anderen Sass-Dateien verwendet. Dies ist eine gute Möglichkeit, ähnlichen Code in einem Modul zu gruppieren, um ihn zu organisieren.
+
+Namen für Partials beginnen mit dem Unterstrich (`_`), der Sass mitteilt, dass es sich um ein kleines Segment von CSS handelt und es nicht in eine CSS-Datei umgewandelt werden soll. Außerdem enden Sass-Dateien mit der Dateierweiterung `.scss`. Um den Code in der partiellen Datei in eine andere Sass-Datei zu bringen, verwende die Direktive `@import`.
+
+Wenn du zum Beispiel alle deine Mixins in einer Teildatei namens "\_mixins.scss" speicherst und sie in der Datei "main.scss" brauchst, kannst du sie so in der Hauptdatei verwenden:
+
+```scss
+@import 'mixins'
+```
+
+Beachte, dass der Unterstrich und die Dateierweiterung in der `import`-Anweisung nicht benötigt werden - Sass versteht, dass es sich um ein Partial handelt. Sobald ein Partial in eine Datei importiert wurde, können alle Variablen, Mixins und anderer Code verwendet werden.
+
+# --instructions--
+
+Schreibe eine `@import`-Anweisung, um ein Partial namens `_variables.scss` in die Datei main.scss zu importieren.
+
+# --hints--
+
+Dein Code sollte die Direktive `@import` verwenden und den Unterstrich nicht im Dateinamen enthalten.
+
+```js
+assert(code.match(/@import\s+?('|")variables\1/gi));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+```
+
+# --solutions--
+
+```html
+@import 'variables'
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/store-data-with-sass-variables.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/store-data-with-sass-variables.md
new file mode 100644
index 00000000000..11c4223e021
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/store-data-with-sass-variables.md
@@ -0,0 +1,121 @@
+---
+id: 587d7dbd367417b2b2512bb4
+title: Daten mit Sass-Variablen speichern
+challengeType: 0
+forumTopicId: 301460
+dashedName: store-data-with-sass-variables
+---
+
+# --description--
+
+Eine Eigenschaft von Sass, die sich von CSS unterscheidet, ist die Verwendung von Variablen. Sie werden deklariert und festgelegt, um Daten zu speichern, ähnlich wie bei JavaScript.
+
+In JavaScript werden Variablen mit den Schlüsselwörtern `let` und `const` definiert. In Sass beginnen Variablen mit einem `$`, gefolgt von dem Variablennamen.
+
+Hier sind ein paar Beispiele:
+
+```scss
+$main-fonts: Arial, sans-serif;
+$headings-color: green;
+```
+
+Und um die Variablen zu nutzen:
+
+```scss
+h1 {
+ font-family: $main-fonts;
+ color: $headings-color;
+}
+```
+
+Ein Beispiel, bei dem Variablen nützlich sind, ist, wenn mehrere Elemente die gleiche Farbe haben müssen. Wenn diese Farbe geändert wird, kann der Code nur am Variablenwert bearbeitet werden.
+
+# --instructions--
+
+Erstelle eine Variable `$text-color` und setze sie auf `red`. Dann ändere den Wert der Eigenschaft `color` für den `.blog-post` und `h2` auf die Variable `$text-color`.
+
+# --hints--
+
+In deinem Code sollte eine Sass-Variable für `$text-color` mit einem Wert von `red` deklariert sein.
+
+```js
+assert(code.match(/\$text-color\s*:\s*?red\s*;/g));
+```
+
+Dein Code sollte die Variable `$text-color` verwenden, um die Farbe (`color`) für die Elemente `.blog-post` und `h2` zu ändern.
+
+```js
+assert(code.match(/color\s*:\s*\$text-color\s*;?/g));
+```
+
+Dein Element `.blog-post` sollte `color` gleich red sein.
+
+```js
+assert($('.blog-post').css('color') == 'rgb(255, 0, 0)');
+```
+
+Deine `h2`-Elemente sollten `color` gleich red sein.
+
+```js
+assert($('h2').css('color') == 'rgb(255, 0, 0)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
Some random title
+
This is a paragraph with some random text in it
+
+
+
Header #2
+
Here is some more random text.
+
+
+
Here is another header
+
Even more random text within a paragraph
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
Some random title
+
This is a paragraph with some random text in it
+
+
+
Header #2
+
Here is some more random text.
+
+
+
Here is another header
+
Even more random text within a paragraph
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/use-each-to-map-over-items-in-a-list.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-each-to-map-over-items-in-a-list.md
new file mode 100644
index 00000000000..c0fb6a04d22
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-each-to-map-over-items-in-a-list.md
@@ -0,0 +1,135 @@
+---
+id: 587d7dbf367417b2b2512bba
+title: Verwende @each, um über Elemente in einer Liste zu iterieren
+challengeType: 0
+forumTopicId: 301461
+dashedName: use-each-to-map-over-items-in-a-list
+---
+
+# --description--
+
+Die letzte Aufgabe hat gezeigt, wie die `@for`-Direktive einen Start- und Endwert verwendet, um eine Schleife eine bestimmte Anzahl von Malen zu durchlaufen. Sass bietet auch die `@each`-Direktive, die über jedes Element in einer Liste oder Map iteriert. Bei jeder Iteration wird der Variable der aktuelle Wert aus der Liste oder Map zugewiesen.
+
+```scss
+@each $color in blue, red, green {
+ .#{$color}-text {color: $color;}
+}
+```
+
+Eine Map hat eine etwas andere Syntax. Hier ist ein Beispiel:
+
+```scss
+$colors: (color1: blue, color2: red, color3: green);
+
+@each $key, $color in $colors {
+ .#{$color}-text {color: $color;}
+}
+```
+
+Beachte, dass die Variable `$key` benötigt wird, um die Schlüssel (Keys) in der Map zu referenzieren. Sonst würde das kompilierte CSS `color1`, `color2`... enthalten. Die beiden obigen Codebeispiele werden in das folgende CSS umgewandelt:
+
+```scss
+.blue-text {
+ color: blue;
+}
+
+.red-text {
+ color: red;
+}
+
+.green-text {
+ color: green;
+}
+```
+
+# --instructions--
+
+Schreibe eine `@each`-Direktive, die durch eine Liste geht: `blue, black, red` und jede Variable einer Klasse `.color-bg` zuordnet, wobei sich der Teil `color` für jedes Element ändert. Jede Klasse sollte `background-color` auf die jeweilige Farbe setzen.
+
+# --hints--
+
+Dein Code sollte die `@each`-Direktive verwenden.
+
+```js
+assert(code.match(/@each /g));
+```
+
+Deine Klasse `.blue-bg` sollte `background-color` gleich Blau (blue) sein.
+
+```js
+assert($('.blue-bg').css('background-color') == 'rgb(0, 0, 255)');
+```
+
+Deine Klasse `.black-bg` sollte `background-color` gleich Schwarz (black) sein.
+
+```js
+assert($('.black-bg').css('background-color') == 'rgb(0, 0, 0)');
+```
+
+Deine Klasse `.red-bg` sollte `background-color` gleich Rot (red) sein.
+
+```js
+assert($('.red-bg').css('background-color') == 'rgb(255, 0, 0)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+
+```
+
+---
+
+```html
+
+
+
+
+
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/use-for-to-create-a-sass-loop.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-for-to-create-a-sass-loop.md
new file mode 100644
index 00000000000..6a0215ed7a2
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-for-to-create-a-sass-loop.md
@@ -0,0 +1,134 @@
+---
+id: 587d7dbe367417b2b2512bb9
+title: Verwende @for, um eine Sass-Schleife zu erstellen
+challengeType: 0
+forumTopicId: 301462
+dashedName: use-for-to-create-a-sass-loop
+---
+
+# --description--
+
+Die `@for`-Direktive fügt Stile in einer Schleife hinzu, ganz ähnlich wie eine `for`-Schleife in JavaScript.
+
+`@for` wird auf zwei Arten verwendet: "start through end" oder "start to end". Der Hauptunterschied ist, dass "start **to** end" *die Endnummer als Teil der Zählung ausschließt* und "start **trough** end" *die Endnummer als Teil der Zählung einschließt*.
+
+Hier ist ein Beispiel für "start **through** end":
+
+```scss
+@for $i from 1 through 12 {
+ .col-#{$i} { width: 100%/12 * $i; }
+}
+```
+
+Der Teil `#{$i}` ist die Syntax, um eine Variable (`i`) mit Text zu einem String zu kombinieren. Wenn die Sass-Datei in CSS umgewandelt wird, sieht sie so aus:
+
+```scss
+.col-1 {
+ width: 8.33333%;
+}
+
+.col-2 {
+ width: 16.66667%;
+}
+
+...
+
+.col-12 {
+ width: 100%;
+}
+```
+
+Dies ist eine leistungsstarke Methode, um ein Rasterlayout (Grid) zu erstellen. Jetzt hast du zwölf Optionen für Spaltenbreiten als CSS-Klassen zur Verfügung.
+
+# --instructions--
+
+Schreibe eine `@for`-Direktive, die eine Variable `$j` aufnimmt, die von 1 **bis (to)** 6 geht.
+
+Es sollten 5 Klassen mit den Namen `.text-1` bis `.text-5` erstellt werden, wobei jede eine Schriftgröße (`font-size`) von 15px multipliziert mit dem Index hat.
+
+# --hints--
+
+Dein Code sollte die `@for`-Direktive verwenden.
+
+```js
+assert(code.match(/@for /g));
+```
+
+Deine Klasse `.text-1` sollte eine Schriftgröße (`font-size`) `von 15px haben.
+
+
assert($('.text-1').css('font-size') == '15px');
+`
+
+Deine Klasse `.text-2` sollte eine Schriftgröße (`font-size`) `von 30px haben.
+
+
assert($('.text-2').css('font-size') == '30px');
+`
+
+Deine Klasse `.text-3` sollte eine Schriftgröße (`font-size`) `von 45px haben.
+
+
assert($('.text-3').css('font-size') == '45px');
+`
+
+Deine Klasse `.text-4` sollte eine Schriftgröße (`font-size`) `von 60px haben.
+
+
assert($('.text-4').css('font-size') == '60px');
+`
+
+Deine Klasse `.text-5` sollte eine Schriftgröße (`font-size`) `von 75px haben.
+
+
assert($('.text-5').css('font-size') == '75px');
+`
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+```
+
+# --solutions--
+
+```html
+
+
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+```
+
+---
+
+```html
+
+
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+```
diff --git a/curriculum/challenges/german/03-front-end-development-libraries/sass/use-if-and-else-to-add-logic-to-your-styles.md b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-if-and-else-to-add-logic-to-your-styles.md
new file mode 100644
index 00000000000..e4a91d0182b
--- /dev/null
+++ b/curriculum/challenges/german/03-front-end-development-libraries/sass/use-if-and-else-to-add-logic-to-your-styles.md
@@ -0,0 +1,145 @@
+---
+id: 587d7dbe367417b2b2512bb8
+title: Verwende @if und @else, um deine Stile mit Logik zu versehen
+challengeType: 0
+forumTopicId: 301463
+dashedName: use-if-and-else-to-add-logic-to-your-styles
+---
+
+# --description--
+
+Die `@if`-Direktive in Sass ist nützlich, um auf einen bestimmten Fall zu testen - sie funktioniert genau wie die `if`-Anweisung in JavaScript.
+
+```scss
+@mixin make-bold($bool) {
+ @if $bool == true {
+ font-weight: bold;
+ }
+}
+```
+
+Und genau wie in JavaScript testen `@else if` und `@else` auf weitere Bedingungen:
+
+```scss
+@mixin text-effect($val) {
+ @if $val == danger {
+ color: red;
+ }
+ @else if $val == alert {
+ color: yellow;
+ }
+ @else if $val == success {
+ color: green;
+ }
+ @else {
+ color: black;
+ }
+}
+```
+
+# --instructions--
+
+Erstelle ein Mixin namens `border-stroke`, das einen Parameter `$val` erhält. Das Mixin sollte mithilfe von `@if`, `@else if` und `@else` die folgenden Bedingungen überprüfen:
+
+```scss
+light - 1px solid black
+medium - 3px solid black
+heavy - 6px solid black
+```
+
+Wenn `$val` nicht `light`, `medium` oder `heavy` ist, sollte der Rahmen auf `none` gesetzt werden.
+
+# --hints--
+
+Dein Code sollte ein Mixin namens `border-stroke` deklarieren, das einen Parameter namens `$val` besitzt.
+
+```js
+assert(code.match(/@mixin\s+?border-stroke\s*?\(\s*?\$val\s*?\)\s*?{/gi));
+```
+
+Dein Mixin sollte eine `@if`-Anweisung enthalten, um zu prüfen, ob `$val` `light` ist, und um den `border` auf `1px solid black` zu setzen.
+
+```js
+assert(
+ code.match(
+ /@if\s+?\$val\s*?===?\s*?light\s*?{\s*?border\s*?:\s*?1px\s+?solid\s+?black\s*?;\s*?}/gi
+ )
+);
+```
+
+Dein Mixin sollte eine `@else if`-Anweisung enthalten, um zu prüfen, ob `$val` `medium` ist, und um die `border` auf `3px solid black` zu setzen.
+
+```js
+assert(
+ code.match(
+ /@else\s+?if\s+?\$val\s*?===?\s*?medium\s*?{\s*?border\s*?:\s*?3px\s+?solid\s+?black\s*?;\s*?}/gi
+ )
+);
+```
+
+Dein Mixin sollte eine `@else if`-Anweisung enthalten, um zu prüfen, ob `$val` `heavy` ist, und um die `border` auf `6px solid black` zu setzen.
+
+```js
+assert(
+ code.match(
+ /@else\s+?if\s+?\$val\s*?===?\s*?heavy\s*?{\s*?border\s*?:\s*?6px\s+?solid\s+?black\s*?;\s*?}/gi
+ )
+);
+```
+
+Dein Mixin sollte eine `@else`-Anweisung enthalten, die den `border` auf `none` setzt.
+
+```js
+assert(code.match(/@else\s*?{\s*?border\s*?:\s*?none\s*?;\s*?}/gi));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-bar-chart.md b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-bar-chart.md
new file mode 100644
index 00000000000..3886341a7f0
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-bar-chart.md
@@ -0,0 +1,53 @@
+---
+id: bd7168d8c242eddfaeb5bd13
+title: Datenvisualisierung mit einem Balkendiagramm
+challengeType: 3
+forumTopicId: 301464
+dashedName: visualize-data-with-a-bar-chart
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie
https://codepen.io/freeCodeCamp/full/GrZVaM aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst HTML, Javascript, CSS und die D3-SVG-basierte Visualisierungs-Bibliothek verwenden. Zur Erfüllung der Testfälle müssen die Achsen mit der D3-Achseneigenschaft erzeugt werden, welche automatisch Achsenmarkierungen entlang der Achse erstellt. Diese Achsenmarkierungen sind notwendig, um die D3-Tests zu erfüllen, da diese zur Bestimmung der Ausrichtung von grafischen Elementen verwendet werden. Weiter Informationen über die Achsenerstellung findest du hier:
. Erforderliche (nicht-virtuelle) DOM-Elemente werden bei jedem Test abgefragt. Falls du ein Frontend-Framework (wie z.B. Vue) verwendest, kann es passieren, dass die Testergebnisse für dynamische Inhalte ungenau sind. Wir hoffen, dass wir diese irgendwann berücksichtigen können, aber derzeit werden diese Frameworks nicht für D3-Projekte unterstützt.
+
+**User Story #1:** Mein Diagramm sollte einen Titel mit einer passenden `id="title"` haben.
+
+**User Story #2:** Mein Diagramm sollte eine `g`-Element X-Achse mit einer passenden `id="x-axis"` haben.
+
+**User Story #2:** Mein Diagramm sollte eine `g`-Element Y-Achse mit einer passenden `id="y-axis"` haben.
+
+**User Story #4:** Beide Achsen sollten mehrere Markierungen enthalten, jede mit einer entsprechenden `class="tick"`.
+
+**User Story #5:** Mein Diagramm sollte ein `rect` Element für jeden Datenpunkt mit einer entsprechenden `class="bar"` haben, welche die Daten anzeigt.
+
+**User Story #6:** Jeder Balken sollte die Eigenschaften `data-date` und `data-gdp` haben, welche die Werte `date` und `GDP` beinhalten.
+
+**User Story #7:** Die Eigenschaft `data-date` der Balkenelemente sollte mit der Reihenfolge der angegebenen Daten übereinstimmen.
+
+**User Story #8:** Die Eigenschaft `data-gdp` des Balkendiagramms sollte mit den angegebenen Daten übereinstimmen.
+
+**User Story #9:** Die Höhe jedes Balkenelements sollte das zugehörige `GDP` exakt darstellen.
+
+**User Story #10:** Das `data-date`-Attribut und sein zugehöriges Balkenelement sollten sich mit dem entsprechenden Wert auf der x-Achse ausrichten.
+
+**User Story #11:** Das `data-gdp`-Attribut und sein zugehöriges Balkenelement sollten sich mit dem entsprechenden Wert auf der y-Achse ausrichten.
+
+**User Story #12:** Ich kann mit meiner Maus über einen Bereich fahren und einen Tooltip mit einem entsprechenden `id="tooltip"` sehen, das mir mehr Informationen über den Bereich anzeigt.
+
+**User Story #13:** Mein Tooltip sollte eine `data-date` Eigenschaft haben, die dem `data-date` des aktiven Bereichs entspricht.
+
+Hier ist der Datensatz, den du benötigst, um dieses Projekt abzuschließen: `https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json`
+
+Du kannst dein Projekt erstellen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`.
+
+Sobald du fertig bist, übermittle die URL an dein Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-choropleth-map.md b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-choropleth-map.md
new file mode 100644
index 00000000000..f3f17e0f486
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-choropleth-map.md
@@ -0,0 +1,52 @@
+---
+id: 587d7fa6367417b2b2512bbf
+title: Visualisiere Daten mit einer Choroplethenkarte (Flächenkartogramm)
+challengeType: 3
+forumTopicId: 301465
+dashedName: visualize-data-with-a-choropleth-map
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/EZKqza aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst HTML, Javascript, CSS und die D3-SVG-basierte Visualisierungs-Bibliothek verwenden. Erforderliche (nicht-virtuelle) DOM-Elemente werden zum Zeitpunkt jedes Tests abgefragt. Falls du ein Frontend-Framework (wie z.B. Vue) verwendest, kann es passieren, dass die Testergebnisse für dynamische Inhalte ungenau sind. Wir hoffen, dass wir sie irgendwann unterbringen können, aber diese Frameworks werden derzeit nicht für D3-Projekte unterstützt.
+
+**User Story #1:** Meine Choroplethkarte sollte einen Titel mit einer entsprechenden `id="title"` haben.
+
+**User Story #2:** Meine Choroplethkarte sollte eine Beschreibung mit einer entsprechenden `id="description"` haben.
+
+**User Story #3:** Meine Choroplethkarte sollte Bezirke mit einer `class="county"` haben, die die Daten repräsentieren.
+
+**User Story #4:** Es sollten mindestens 4 verschiedene Füllfarben für die Bezirke verwendet werden.
+
+**User Story #5:** Meine Bezirke sollten jeweils `data-fips` und `data-education`-Eigenschaften haben, die ihre entsprechenden `fips` und `education`-Werte beinhalten.
+
+**User Story #6:** Meine Choroplethkarte sollte einen Bezirk für jeden gegebenen Datenpunkt haben.
+
+**User Story #7:** Die Bezirke sollten `data-fips` und `data-education` Werte haben, die mit den Beispieldaten übereinstimmen.
+
+**User Story #8:** Meine Choroplethkarte sollte eine Legende mit entsprechender `id="legend"` haben.
+
+**User Story #9:** Es sollten mindestens 4 verschiedene Füllfarben für die Legende verwendet werden.
+
+**User Story #10:** Ich kann mit meiner Maus über einen Bereich fahren und einen Tooltip mit einer entsprechenden `id="tooltip"` sehen, der mir mehr Informationen über den Bereich anzeigt.
+
+**User Story #11:** Mein Tooltip sollte eine `data-education`-Eigenschaft haben, die der `data-education` des aktiven Bereichs entspricht.
+
+Hier sind die Datensätze, die du für dieses Projekt benötigst:
+
+- **US-Bildungsdaten:**`https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json`
+- **US Bezirksdaten:**`https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json`
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in einer beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-heat-map.md b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-heat-map.md
new file mode 100644
index 00000000000..fe0cea2a04a
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-heat-map.md
@@ -0,0 +1,61 @@
+---
+id: bd7188d8c242eddfaeb5bd13
+title: Datenvisualisierung mit einer Heatmap
+challengeType: 3
+forumTopicId: 301466
+dashedName: visualize-data-with-a-heat-map
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/JEXgeY aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst HTML, JavaScript, CSS und die D3-svg-basierte Visualisierungsbibliothek verwenden. Erforderliche (nicht-virtuelle) DOM-Elemente werden zum Zeitpunkt jeden Tests abgefragt. Wenn du ein Frontend-Framework verwendest (z. B. Vue), sind die Testergebnisse für dynamische Inhalte möglicherweise ungenau. Wir hoffen, dass wir diese irgendwann berücksichtigen können, aber derzeit werden diese Frameworks nicht für D3-Projekte unterstützt.
+
+**User Story #1:** Meine Heatmap sollte einen entsprechenden Titel mit einer entsprechenden `id="title"` haben.
+
+**User Story #2:** Meine Heatmap sollte eine Beschreibung mit einer entsprechenden `id="description"` haben.
+
+**User Story #3:** Meine Heatmap sollte eine x-Achse mit einer entsprechenden `id="x-axis"` haben.
+
+**User Story #4:** Meine Heatmap sollte eine y-Achse mit einer entsprechenden `id="y-axis"` haben.
+
+**User Story #5:** Meine Heatmap sollte `rect` Elemente mit einer `class="cell"` haben, die die Daten repräsentieren.
+
+**User Story #6:** Es sollten mindestens 4 verschiedene Füllfarben für die Zellen verwendet werden.
+
+**User Story #7:** Jede Zelle wird die Eigenschaften `data-month`, `data-year`, `data-temp`, die die entsprechenden `month`, `year`, und `temperature`-Werte enthalten.
+
+**User Story #8:** Die `data-month`, `data-year` jeder Zelle sollten sich im Bereich der Daten befinden.
+
+**User Story #9:** Meine Heatmap sollte Zellen haben, die mit dem entsprechenden Monat auf der y-Achse übereinstimmen.
+
+**User Story #10:** Meine Heatmap sollte Zellen haben, die mit dem entsprechenden Jahr auf der x-Achse übereinstimmen.
+
+**User Story #11:** Meine Heatmap sollte mehrere Tick-Labels auf der y-Achse mit dem vollen Monatsnamen haben.
+
+**User Story #12:** Meine Heatmap sollte mehrere Tick-Labels auf der x-Achse mit den Jahren zwischen 1754 und 2015 haben.
+
+**User Story #13:** Meine Heatmap sollte eine Legende mit der entsprechenden `id="legend"` haben.
+
+**User Story #14:** Meine Legende sollte `rect` Elemente beinhalten.
+
+**User Story #15:** Die `rect`-Elemente in der Legende sollten mindestens 4 verschiedene Füllfarben beinhalten.
+
+**User Story #16:** Ich kann mit meiner Maus über einen Bereich fahren und einen Tooltip mit einer entsprechenden `id="tooltip"` sehen, welche mir mehr Informationen über den Bereich anzeigt.
+
+**User Story #17:** Mein Tooltip sollte eine `data-year`-Eigenschaft haben, die dem `data-year` des aktiven Bereichs entspricht.
+
+Hier ist der Datensatz, den du benötigst, um dieses Projekt abzuschließen: `https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json`
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL an dein Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-scatterplot-graph.md b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-scatterplot-graph.md
new file mode 100644
index 00000000000..6c73c98ede8
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-scatterplot-graph.md
@@ -0,0 +1,57 @@
+---
+id: bd7178d8c242eddfaeb5bd13
+title: Visualisiere Daten mit einem Scatterplot-Diagramm (Streudiagramm)
+challengeType: 3
+forumTopicId: 301467
+dashedName: visualize-data-with-a-scatterplot-graph
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/bgpXyK aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Sie können HTML, JavaScript, CSS und die D3-svg-basierte Visualisierungsbibliothek verwenden. Für die Tests müssen die Achsen mit Hilfe der D3-Achseneigenschaft erzeugt werden, die automatisch Markierungen entlang der Achse erzeugt. Diese Häkchen sind für das Bestehen der D3-Tests erforderlich, da ihre Position zur Bestimmung der Ausrichtung der grafisch dargestellten Elemente verwendet wird. Informationen zum Generieren von Achsen finden Sie unter . Erforderliche (nicht-virtuelle) DOM-Elemente werden zum Zeitpunkt jedes Tests abgefragt. Wenn Sie ein Frontend-Framework verwenden (z. B. Vue), sind die Testergebnisse für dynamische Inhalte möglicherweise ungenau. Wir hoffen, dass wir sie irgendwann unterbringen können, aber diese Frameworks werden derzeit nicht für D3-Projekte unterstützt.
+
+**User Story #1:** Ich kann ein Titelelement sehen, das einen entsprechenden `id="title"` hat.
+
+**User Story #2:** Ich kann eine x-Achse sehen, die eine entsprechende `id="x-axis"` hat.
+
+**User Story #3:** Ich kann eine entsprechende y-Achse sehen, die eine entsprechende `id="y-axis"` hat.
+
+**User Story #4:** Ich kann Punkte sehen, die jeweils eine Klasse von `dot` haben, die die zu zeichnenden Daten darstellen.
+
+**User Story #5:** Jeder Punkt sollte die Eigenschaften `data-xvalue` und `data-yvalue` haben, die die entsprechenden `x` und `y`-Werte beinhalten.
+
+**User Story #6:** Die `data-xvalue` und `data-yvalue` eines jeden Punktes sollte im Bereich der tatsächlichen Daten und im richtigen Datenformat liegen. Für `data-xvalue`, sind ganze Zahlen (ganzes Jahr) oder `Date`-Objekte für die Testauswertung akzeptabel. Für `data-yvalue` (Minuten), verwende `Date`-Objekte.
+
+**User Story #7:** Die `data-xvalue` und der zugehörige Punkt sollte mit dem entsprechenden Punkt/Wert auf der x-Achse übereinstimmen.
+
+**User Story #8:** Die `data-yvalue` und der entsprechende Punkt sollte mit dem entsprechenden Punkt/Wert auf der y-Achse übereinstimmen.
+
+**User Story #9:** Auf der y-Achse sehe ich mehrere Markierungen mit dem Zeitformat `%M:%S`.
+
+**User Story #10:** Ich sehe mehrere Markierungen auf der x-Achse, die das Jahr anzeigen.
+
+**User Story #11:** Ich kann sehen, dass der Bereich der x-Achsenbeschriftungen innerhalb des Bereichs der tatsächlichen x-Achsen-Daten liegt.
+
+**User Story #12:** Ich kann sehen, dass der Bereich der y-Achsenbeschriftungen innerhalb des Bereichs der tatsächlichen y-Achsen-Daten liegt.
+
+**User Story #13:** Ich kann eine Legende mit beschreibendem Text sehen, die `id="legend"` beinhaltet.
+
+**User Story #14:** Wenn ich mit der Maus über einen Bereich fahre, wird ein Tooltip mit einem entsprechenden `id="tooltip"` angezeigt, der weitere Informationen über den Bereich enthält.
+
+**User Story #15:** Mein Tooltip sollte eine `data-year`-Eigenschaft haben, die der `data-xvalue` des aktiven Bereichs entspricht.
+
+Hier ist der Datensatz, den du benötigst, um dieses Projekt abzuschließen: `https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json`
+
+Du kannst dein Projekt aufbauen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL an dein Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-treemap-diagram.md b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-treemap-diagram.md
new file mode 100644
index 00000000000..f45f5189d27
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-projects/visualize-data-with-a-treemap-diagram.md
@@ -0,0 +1,53 @@
+---
+id: 587d7fa6367417b2b2512bc0
+title: Visualisiere Daten mit einer Tree Map (Kacheldiagramm)
+challengeType: 3
+forumTopicId: 301468
+dashedName: visualize-data-with-a-treemap-diagram
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://codepen.io/freeCodeCamp/full/KaNGNR aufweist.
+
+Erfülle die folgenden User Stories und bestehe alle Tests. Verwende Bibliotheken und APIs deiner Wahl. Gib dem Ganzen deinen persönlichen Stil.
+
+Du kannst HTML, JavaScript, CSS und die D3-svg-basierte Visualisierungsbibliothek verwenden. Die Tests erfordern, dass Achsen mit der Eigenschaft der D3-Achse erzeugt werden, welche automatisch Markierungen entlang der Achse bildet. Diese Markierungen sind notwendig für die D3-Tests, da ihre Positionen zur Bestimmung der Ausrichtung von Graphen verwendet werden. Informationen zum Generieren von Achsen findest du hier . Erforderliche (nicht-virtuelle) DOM-Elemente werden zum Zeitpunkt jedes Tests abgefragt. Wenn du ein Frontend-Framework (wie z. B. Vue) verwendest, können die Testergebnisse für dynamische Inhalte ungenau sein. Wir hoffen, dass wir sie irgendwann unterbringen können, aber diese Frameworks werden derzeit nicht für D3-Projekte unterstützt.
+
+**User Story #1:** Meine Tree Map sollte eine Bezeichnung mit der entsprechenden `id="title"` haben.
+
+**User Story #2:** Meine Tree Map sollte eine Beschreibung mit der entsprechenden `id="description"` haben.
+
+**User Story #3:** Meine Tree Map sollte `rect`-Elemente mit einer entsprechenden `class="tile"` haben, die die Daten darstellen.
+
+**User Story #4:** Es sollten mindestens 2 verschiedene Füllfarben für die Kacheln verwendet werden.
+
+**User Story #5:** Jede Kachel sollte die Eigenschaften `data-name`, `data-category`, und `data-value` haben, die die entsprechenden `name`, `category`, und `value` enthalten.
+
+**User Story #6:** Die Fläche der einzelnen Kacheln sollte dem `data-value` entsprechen: Kacheln mit einem größeren `data-value` sollten einen größeren Bereich haben.
+
+**User Story #7:** Meine Tree Map sollte eine Legende mit der entsprechenden `id="legend"` haben.
+
+**User Story #8:** Meine Legende sollte `rect`-Elemente haben mit einem entsprechenden `class="legend-item"`.
+
+**User Story #9:** Die `rect`-Elemente in der Legende sollten mindestens 2 verschiedene Füllfarben verwenden.
+
+**User Story #10:** Wenn ich mit der Maus über einen Bereich fahre, wird ein Tooltip mit einem entsprechenden `id="tooltip"` angezeigt, der weitere Informationen über den Bereich enthält.
+
+**User Story #11:** Mein Tooltip sollte eine `data-value`-Eigenschaft haben, die der `data-value` des aktiven Bereichs entspricht.
+
+Für dieses Projekt kannst du einen der folgenden Datensätze verwenden:
+
+- **Kickstarter-Zusagen:** `https://cdn.freecodecamp.org/testable-projects-fcc/data/tree_map/kickstarter-funding-data.json`
+- **Filmverkäufe:** `https://cdn.freecodecamp.org/testable-projects-fcc/data/tree_map/movie-data.json`
+- **Videospiel-Verkäufe:** `https://cdn.freecodecamp.org/testable-projects-fcc/data/tree_map/video-game-sales-data.json`
+
+Du kannst dein Projekt erstellen, indem du diese CodePen-Vorlage verwendest und auf `Save` klickst, um deinen eigenen Pen zu erstellen. Oder du kannst diesen CDN-Link verwenden, um die Tests in jeder beliebigen Umgebung auszuführen: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
+
+Sobald du fertig bist, übermittle die URL zu deinem Arbeitsprojekt, wenn alle Tests bestanden sind.
+
+# --solutions--
+
+```js
+// solution required
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-hover-effect-to-a-d3-element.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-hover-effect-to-a-d3-element.md
new file mode 100644
index 00000000000..11fddaa4472
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-hover-effect-to-a-d3-element.md
@@ -0,0 +1,118 @@
+---
+id: 587d7faa367417b2b2512bd4
+title: Füge einem D3-Element einen Hover-Effekt hinzu
+challengeType: 6
+forumTopicId: 301469
+dashedName: add-a-hover-effect-to-a-d3-element
+---
+
+# --description--
+
+Es ist möglich, Effekte hinzuzufügen, die einen Balken markieren, wenn der User mit der Maus darüber fährt. Bisher wird das Styling für die Recktecke mit den eingebauten D3 und SVG Methoden angewandt, aber du kannst auch CSS verwenden.
+
+Du kannst die CSS Klasse auf SVG-Elemente mit der `attr()`-Methode setzen. Dann erhält die `:hover` Pseudoklasse für deine neue Klasse die Stilregeln für beliebige Hover-Effekte.
+
+# --instructions--
+
+Benutze die `attr()` Methode um eine Klasse von `bar` zu allen `rect`-Elementen hinzuzufügen. Das ändert die `fill`-Farbe (Füllfarbe) des Balkens in braun, wenn du mit der Maus über sie fährst.
+
+# --hints--
+
+Deine `rect`-Elemente sollten eine Klasse von `bar` haben.
+
+```js
+assert($('rect').attr('class').trim().split(/\s+/g).includes('bar'));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-tooltip-to-a-d3-element.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-tooltip-to-a-d3-element.md
new file mode 100644
index 00000000000..5b97e4dd972
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-a-tooltip-to-a-d3-element.md
@@ -0,0 +1,175 @@
+---
+id: 587d7faa367417b2b2512bd6
+title: Füge einem D3-Element einen Tooltip hinzu
+challengeType: 6
+forumTopicId: 301470
+dashedName: add-a-tooltip-to-a-d3-element
+---
+
+# --description--
+
+Ein Tooltip zeigt mehr Informationen über ein Element auf einer Seite an, wenn der User über das Element fährt. Es gibt mehrere Wege, um einen Tooltip zu einer Visualisierung hinzuzufügen. Diese Aufgabe verwendet das SVG `title`-Element.
+
+`title` wird mit der `text()`-Methode gekoppelt, um dynamisch Daten zu den Balken hinzuzufügen.
+
+# --instructions--
+
+Füge ein `title`-Element unter jedem `rect`-Punkt hinzu. Rufe dann die `text()`-Methode mit einer Callback-Funktion auf, so dass der Text den Datenwert anzeigt.
+
+# --hints--
+
+Dein Code sollte 9 `title`-Elemente haben.
+
+```js
+assert($('title').length == 9);
+```
+
+Das erste `title`-Element sollte einen Tooltip-Text von `12` haben.
+
+```js
+assert($('title').eq(0).text() == '12');
+```
+
+Das zweite `title`-Element sollte einen Tooltip-Text von `31` haben.
+
+```js
+assert($('title').eq(1).text() == '31');
+```
+
+Das dritte `title`-Element sollte einen Tooltip-Text von `22` haben.
+
+```js
+assert($('title').eq(2).text() == '22');
+```
+
+Das vierte `title`-Elemente sollte einen Tooltip-Text von `17` haben.
+
+```js
+assert($('title').eq(3).text() == '17');
+```
+
+Das fünfte `title`-Element sollte einen Tooltip-Text von `25` haben.
+
+```js
+assert($('title').eq(4).text() == '25');
+```
+
+Das sechste `title`-Element sollte einen Tooltip von `18` haben.
+
+```js
+assert($('title').eq(5).text() == '18');
+```
+
+Das siebte `title`-Element sollte einen Tooltip-Text von `29` haben.
+
+```js
+assert($('title').eq(6).text() == '29');
+```
+
+Das achte `title`-Element sollte einen Tooltip-Text von `14` haben.
+
+```js
+assert($('title').eq(7).text() == '14');
+```
+
+Das neunte `title`-Element sollte einen Tooltip-Text von `9` haben.
+
+```js
+assert($('title').eq(8).text() == '9');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-attributes-to-the-circle-elements.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-attributes-to-the-circle-elements.md
new file mode 100644
index 00000000000..10c307016d4
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-attributes-to-the-circle-elements.md
@@ -0,0 +1,211 @@
+---
+id: 587d7fab367417b2b2512bd8
+title: Füge Attribute zu Kreiselementen hinzu
+challengeType: 6
+forumTopicId: 301471
+dashedName: add-attributes-to-the-circle-elements
+---
+
+# --description--
+
+Die letzte Aufgabe erstellte die `circle`-Elemente für jeden Punkt im `dataset` und fügte diese dem SVG-Canvas hinzu. Aber D3 braucht noch mehr Informationen über die Position und Größe jedes `circle`, um sie korrekt anzuzeigen.
+
+Ein `circle` in SVG hat drei Hauptattribute. Die `cx` und `cy` Attribute sind die Koordinaten. Sie sagen D3, wo sie das *center* der Form auf dem SVG-Canvas platzieren sollen. Der Radius (`r`-Attribute) gibt die Größe des `circle`-Elements an.
+
+Genau wie `rect` `y` koordiniert, wird das `cy`- Attribut eines `circle`-Elements von oben auf dem SVG-Canvas gemessen und nicht von unten.
+
+Alle drei Attribute können mit einer Callback-Funktion ihre Werte dynamisch setzen. Denke daran, dass alle Methoden nach `data(dataset)` einmal pro Element in `dataset` ausgeführt werden. Der `d`-Parameter in der Callback-Funktion bezieht sich auf das aktuelle Element in `dataset`, welches ein Array für jeden Punkt ist. Du verwendest Klammern, wie `d[0]` um auf die Werte in diesem Array zugreifen zu können.
+
+# --instructions--
+
+Füge `cx`, `cy`, und `r`-Attribute zu deinen `circle`-Elementen hinzu. Der `cx`-Wert sollte die erste Zahl im Array für jedes Element im `dataset` sein. Der `cy`-Wert sollte sich auf die zweite Zahl im Array beziehen. Aber stelle sicher, dass das Diagramm rechts angezeigt wird und nicht umgedreht. Der `r`-Wert sollte `5` für alle Kreise sein.
+
+# --hints--
+
+Dein Code sollte 10 `circle`-Elemente haben.
+
+```js
+assert($('circle').length == 10);
+```
+
+Das erste `circle`-Element sollte einen `cx`-Wert von `34`, einen `cy`-Wert von `422`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(0).attr('cx') == '34' &&
+ $('circle').eq(0).attr('cy') == '422' &&
+ $('circle').eq(0).attr('r') == '5'
+);
+```
+
+Das zweite `circle`-Element sollte einen `cx`-Wert von `109`, einen `cy`-Wert von `220`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(1).attr('cx') == '109' &&
+ $('circle').eq(1).attr('cy') == '220' &&
+ $('circle').eq(1).attr('r') == '5'
+);
+```
+
+Das dritte `circle`-Element sollte einen `cx`-Wert von `310`, einen `cy`-Wert von `380`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(2).attr('cx') == '310' &&
+ $('circle').eq(2).attr('cy') == '380' &&
+ $('circle').eq(2).attr('r') == '5'
+);
+```
+
+Das vierte `circle`-Element sollte einen `cx`-Wert von `79`, einen `cy`-Wert von `89`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(3).attr('cx') == '79' &&
+ $('circle').eq(3).attr('cy') == '89' &&
+ $('circle').eq(3).attr('r') == '5'
+);
+```
+
+Das fünfte `circle`-Element sollte einen `cx`-Wert von `420`, einen `cy`-Wert von `280`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(4).attr('cx') == '420' &&
+ $('circle').eq(4).attr('cy') == '280' &&
+ $('circle').eq(4).attr('r') == '5'
+);
+```
+
+Das sechste `circle`-Element sollte einen `cx`-Wert von `233`, einen `cy`-Wert von `355`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(5).attr('cx') == '233' &&
+ $('circle').eq(5).attr('cy') == '355' &&
+ $('circle').eq(5).attr('r') == '5'
+);
+```
+
+Das siebte `circle`-Element sollte einen `cx`-Wert von `333`, einen `cy`-Wert von `404`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(6).attr('cx') == '333' &&
+ $('circle').eq(6).attr('cy') == '404' &&
+ $('circle').eq(6).attr('r') == '5'
+);
+```
+
+Das achte `circle`-Element sollte einen `cx`-Wert von `222`, einen `cy`-Wert von `167`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(7).attr('cx') == '222' &&
+ $('circle').eq(7).attr('cy') == '167' &&
+ $('circle').eq(7).attr('r') == '5'
+);
+```
+
+Das neunte `circle`-Element sollte einen `cx`-Wert von `78`, einen `cy`-Wert von `180`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(8).attr('cx') == '78' &&
+ $('circle').eq(8).attr('cy') == '180' &&
+ $('circle').eq(8).attr('r') == '5'
+);
+```
+
+Das zehnte `circle`-Element sollte einen `cx`-Wert von `21`, einen `cy`-Wert von `377`, und einen `r`-Wert von `5` haben.
+
+```js
+assert(
+ $('circle').eq(9).attr('cx') == '21' &&
+ $('circle').eq(9).attr('cy') == '377' &&
+ $('circle').eq(9).attr('r') == '5'
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-axes-to-a-visualization.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-axes-to-a-visualization.md
new file mode 100644
index 00000000000..21094f62be9
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-axes-to-a-visualization.md
@@ -0,0 +1,198 @@
+---
+id: 587d7fad367417b2b2512bdf
+title: Füge Achsen zur Visualisierung hinzu
+challengeType: 6
+forumTopicId: 301472
+dashedName: add-axes-to-a-visualization
+---
+
+# --description--
+
+Eine weitere Möglichkeit, das Streudiagramm zu verbessern, ist eine x-und y-Achse hinzuzufügen.
+
+D3 hat zwei Methoden, `axisLeft()` und `axisBottom()`, um jeweils die y-und x-Achse darzustellen. Hier ist ein Beispiel um die x-Achse, auf der Grundlage der `xScale` in der vorherigen Aufgabe, zu erstellen:
+
+```js
+const xAxis = d3.axisBottom(xScale);
+```
+
+Der nächste Schritt ist, die Achse auf dem SVG-Canvas darzustellen. Dafür kannst du eine allgemeine SVG-Komponente, das `g` Element, verwenden. Das `g` steht für Gruppe. Anders als `rect`, `circle`, und `text`, ist eine Achse nur eine geradlinige Linie, wenn sie gerendert wird. Da sie eine einfache Form ist, funktioniert `g`. Der letzte Schritt besteht darin, das `transform`-Attribut anzuwenden, um die Achse auf die richtige Position auf dem SVG-Canvas zu bringen. Andernfalls würde die Linie entlang der Grenze der SVG Leinwand rendern und wäre für uns nicht sichtbar. SVG unterstützt verschiedene Arten von `transforms`, aber die Positionierung einer Achse benötigt `translate`. Wenn es auf das `g`-Element angewandt wird, wird die gesamte Gruppe um die angegebenen Beträge nach oben und unten verschoben. Hier ist ein Beispiel:
+
+```js
+const xAxis = d3.axisBottom(xScale);
+
+svg.append("g")
+ .attr("transform", "translate(0, " + (h - padding) + ")")
+ .call(xAxis);
+```
+
+Der obige Code platziert die x-Achse am unteren Ende des SVG-Canvas. Dann wird es als Argument an die `call()`-Methode übergeben. Die y-Achse funktioniert ähnlich, außer dass das `translate` Argument in der Form `(x, 0)` ist. Da `translate` ein String in der oben gennanten `attr()`- Methode ist, kannst du die Verkettung verwenden, um variable Werte für die Argumente einzuschließen.
+
+# --instructions--
+
+Das Streudiagramm hat jetzt eine x-Achse. Erstelle eine y-Achse namens `yAxis` mit Hilfe der `axisLeft()`-Methode. Anschließend stelle die Achse mit einem `g`-Element dar. Stelle sicher, dass du ein `transform` Attribut verwendest, um die Achse um die Anzahl der Padding-Einheiten nach rechts und `0` Einheiten nach unten zu versetzen. Vergiss nicht `call()` auf die Achse anzuwenden.
+
+# --hints--
+
+Dein Code sollte die `axisLeft()` Methode mit der `yScale` als Argument verwenden.
+
+```js
+assert(code.match(/\.axisLeft\(yScale\)/g));
+```
+
+Das y-Achse `g`-Element sollte ein `transform`-Attribut besitzen, um die Achse nach `(60, 0)` zu versetzen.
+
+```js
+assert(
+ $('g')
+ .eq(10)
+ .attr('transform')
+ .match(/translate\(60\s*?,\s*?0\)/g)
+);
+```
+
+Dein Code sollte die `yAxis` abrufen.
+
+```js
+assert(code.match(/\.call\(\s*yAxis\s*\)/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-classes-with-d3.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-classes-with-d3.md
new file mode 100644
index 00000000000..695ee072401
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-classes-with-d3.md
@@ -0,0 +1,93 @@
+---
+id: 587d7fa7367417b2b2512bc8
+title: Füge Klassen mit D3 hinzu
+challengeType: 6
+forumTopicId: 301473
+dashedName: add-classes-with-d3
+---
+
+# --description--
+
+Die Verwendung einer Vielzahl von Inline-Styles für HTML-Elemente ist selbst für kleinere Anwendungen schwer zu handhaben. Es ist einfacher, eine Klasse mit CSS-Regeln zu Elementen und Stilen hinzuzufügen. D3 verfügt über die `attr()`-Methode, um einem Element ein beliebiges HTML-Attribut hinzuzufügen, einschließlich eines Klassennamens.
+
+Die `attr()`Methode funktioniert genauso wie `style()`. Es verwendet kommagetrennte Werte und kann eine Callback-Funktion verwenden. Hier ist ein Beispiel, um eine Klasse von `container` einer Auswahl hinzuzufügen:
+
+```js
+selection.attr("class", "container");
+```
+
+Beachte, dass der `class`-Parameter gleich bleiben wird, wenn du eine Klasse hinzufügen musst und nur der `container`-Parameter wird sich ändern.
+
+# --instructions--
+
+Füge die `attr()`-Methode zu dem Code im Editor hinzu und setze die Klasse von `bar` auf die `div`-Elemente.
+
+# --hints--
+
+Deine `div`-Elemente sollten eine Klasse von `bar` haben.
+
+```js
+assert($('div').attr('class').trim().split(/\s+/g).includes('bar'));
+```
+
+Dein Code sollte die `attr()`-Methode verwenden.
+
+```js
+assert(code.match(/\.attr/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-inline-styling-to-elements.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-inline-styling-to-elements.md
new file mode 100644
index 00000000000..43d08f1ffc2
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-inline-styling-to-elements.md
@@ -0,0 +1,76 @@
+---
+id: 587d7fa7367417b2b2512bc6
+title: Füge eine Inline-Gestaltung zu den Elementen hinzu
+challengeType: 6
+forumTopicId: 301475
+dashedName: add-inline-styling-to-elements
+---
+
+# --description--
+
+D3 erlaubt es dir, Inline-CSS-Styles bei dynamischen Elementen mit der `style()`-Methode hinzuzufügen.
+
+Die `style()`-Methode verwendet dafür ein durch Komma getrenntes Key-Value-Paar als Argument. Hier ist ein Beispiel, um die Textfarbe der Auswahl auf blau zu setzen:
+
+```js
+selection.style("color","blue");
+```
+
+# --instructions--
+
+Füge die `style()`-Methode dem Code im Editor hinzu, damit der angezeigte Text eine `font-family` von `verdana` hat.
+
+# --hints--
+
+Deine `h2`-Elemente sollten eine `font-family` von `verdana` haben.
+
+```js
+assert($('h2').css('font-family') == 'verdana');
+```
+
+Dein Code sollte die `style()` Methode verwenden.
+
+```js
+assert(code.match(/\.style/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-labels-to-d3-elements.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-labels-to-d3-elements.md
new file mode 100644
index 00000000000..d8aa7a69ac3
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/add-labels-to-d3-elements.md
@@ -0,0 +1,153 @@
+---
+id: 587d7faa367417b2b2512bd2
+title: Füge Label zu den D3 Elementen hinzu
+challengeType: 6
+forumTopicId: 301476
+dashedName: add-labels-to-d3-elements
+---
+
+# --description--
+
+D3 lässt dich Grafikelemente, wie z. B. Balken benennen, indem du das SVG `text`-Element verwendest.
+
+Wie das `rect`-Element, benötigt ein `text`-Element `x` und `y` Attribute, um es auf dem SVG-Canvas platzieren zu können. Es muss auch auf die Daten zugreifen, um diese Werte anzuzeigen.
+
+D3 gibt dir ein hohes Maß an Kontrolle darüber, wie du deine Balken kennzeichnen kannst.
+
+# --instructions--
+
+Der Code im Editor bindet die Daten bereits an jedes neue `text`-Element. Hänge zuerst `text`-Knoten an `svg` an. Füge als nächstes die Attribute für die Koordinaten `x` und `y` hinzu. Sie sollten genauso wie die `rect`-Elemente berechnet werden, außer, dass der `y`-Wert für den `text` das Label 3 Einheiten höher als den Balken ansetzen soll. Benutze schließlich die D3 `text()`-Methode, um das Label mit dem Datenpunktwert gleichzusetzen.
+
+**Hinweis:** Damit das Label höher als der Balken sitzt, musst du dich entscheiden ob der `y` Wert für den `text` größer als 3 oder 3 kleiner als der `y`-Wert für den Balken sein soll.
+
+# --hints--
+
+Das erste `text`-Element sollte ein Label von `12` und einen `y`-Wert von `61` haben.
+
+```js
+assert($('text').eq(0).text() == '12' && $('text').eq(0).attr('y') == '61');
+```
+
+Das zweite `text`-Element sollte ein Label von `31` und einen `y`-Wert von`4` haben.
+
+```js
+assert($('text').eq(1).text() == '31' && $('text').eq(1).attr('y') == '4');
+```
+
+Das dritte `text`-Element sollte ein Label von `22` und einen `y`-Wert von `31` haben.
+
+```js
+assert($('text').eq(2).text() == '22' && $('text').eq(2).attr('y') == '31');
+```
+
+Das vierte `text`-Element sollte ein Label von `17` und einen `y`-Wert von `46` haben.
+
+```js
+assert($('text').eq(3).text() == '17' && $('text').eq(3).attr('y') == '46');
+```
+
+Das fünfte `text`-Element sollte ein Label von `25` und einen `y`-Wert von `22` haben.
+
+```js
+assert($('text').eq(4).text() == '25' && $('text').eq(4).attr('y') == '22');
+```
+
+Das sechste `text`-Element sollte ein Label von `18` und einen `y`-Wert von `43` haben.
+
+```js
+assert($('text').eq(5).text() == '18' && $('text').eq(5).attr('y') == '43');
+```
+
+Das siebte `text`-Element sollte ein Label von `29` und ein `y`-Wert von `10` haben.
+
+```js
+assert($('text').eq(6).text() == '29' && $('text').eq(6).attr('y') == '10');
+```
+
+Das achte `text`-Element sollte ein Label von `14` und einen `y`-Wert von `55` haben.
+
+```js
+assert($('text').eq(7).text() == '14' && $('text').eq(7).attr('y') == '55');
+```
+
+Das neunte `text`-Element sollte ein Label von `9` und einen `y`-Wert von `70` haben.
+
+```js
+assert($('text').eq(8).text() == '9' && $('text').eq(8).attr('y') == '70');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-color-of-an-svg-element.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-color-of-an-svg-element.md
new file mode 100644
index 00000000000..d10ebcadc1f
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-color-of-an-svg-element.md
@@ -0,0 +1,88 @@
+---
+id: 587d7fa9367417b2b2512bd1
+title: Ändere die Farbe eines SVG-Elements
+challengeType: 6
+forumTopicId: 301480
+dashedName: change-the-color-of-an-svg-element
+---
+
+# --description--
+
+Die Balken sind in der richtigen Position, aber sie haben alle die gleiche schwarze Farbe. SVG hat eine Möglichkeit, die Farbe der Balken zu ändern.
+
+In SVG ist eine `rect`-Form mit dem `fill`-Attribut gefärbt. Es unterstützt Hex-Codes, Farbnamen und Rgb-Werte sowie komplexere Optionen wie Farbverläufe und Transparenz.
+
+# --instructions--
+
+Füge eine `attr()`-Methode hinzu und setze das `fill`-Attribut aller Balken auf die Farbe Marineblau (navy).
+
+# --hints--
+
+Die Balken sollten alle die `fill`-Farbe marineblau (navy) haben.
+
+```js
+assert($('rect').css('fill') == 'rgb(0, 0, 128)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-presentation-of-a-bar-chart.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-presentation-of-a-bar-chart.md
new file mode 100644
index 00000000000..fca17a3de11
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/change-the-presentation-of-a-bar-chart.md
@@ -0,0 +1,161 @@
+---
+id: 587d7fa8367417b2b2512bca
+title: Darstellung eines Balkendiagramms ändern
+challengeType: 6
+forumTopicId: 301481
+dashedName: change-the-presentation-of-a-bar-chart
+---
+
+# --description--
+
+Die letzte Aufgabe hat ein Balkendiagramm erstellt, aber es gibt noch einige Formatierungsänderungen, die es verbessern könnten:
+
+1) Füge einen Abstand zwischen den einzelnen Balken hinzu, um sie optisch voneinander zu trennen. Dies wird erreicht, indem du einen Abstand in das CSS für die Klasse `bar` einfügst
+
+2) Erhöhe die Höhe der Balken, um den Unterschied zwischen den Werten besser sichtbar zu machen, indem du den Wert mit einer Zahl multiplizierst, um die Höhe zu skalieren.
+
+# --instructions--
+
+Füge zuerst eine `margin` von `2px` zu der `bar`-Klasse im `style`-Tag hinzu. Ändere als nächstes die Callback-Funktion in der `style()`-Methode, so dass sie einen Wert von `10` Mal mehr der ursprünglichen Werte zurückgibt (plus `px`).
+
+**Hinweis:** Das Multiplizieren jedes Datenpunktes um die *gleiche* Konstante verändert nur die Skala. Es ist wie ein Hineinzoomen und verändert nicht die Bedeutung der zugrunde liegenden Daten.
+
+# --hints--
+
+Das erste `div` sollte eine `height` von `120` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(0).css('height') == '120px' &&
+ $('div').eq(0).css('margin-right') == '2px'
+);
+```
+
+Das zweite `div` sollte eine `height` von `310` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(1).css('height') == '310px' &&
+ $('div').eq(1).css('margin-right') == '2px'
+);
+```
+
+Das dritte `div` sollte eine `height` von `220` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(2).css('height') == '220px' &&
+ $('div').eq(2).css('margin-right') == '2px'
+);
+```
+
+Das vierte `div` sollte eine `height` von `170` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(3).css('height') == '170px' &&
+ $('div').eq(3).css('margin-right') == '2px'
+);
+```
+
+Das fünfte `div` sollte eine `height` von `250` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(4).css('height') == '250px' &&
+ $('div').eq(4).css('margin-right') == '2px'
+);
+```
+
+Das sechste `div` sollte eine `height` von `180` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(5).css('height') == '180px' &&
+ $('div').eq(5).css('margin-right') == '2px'
+);
+```
+
+Das siebte `div` sollte eine `height` von `290` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(6).css('height') == '290px' &&
+ $('div').eq(6).css('margin-right') == '2px'
+);
+```
+
+Das achte `div` sollte eine `height` von `140` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(7).css('height') == '140px' &&
+ $('div').eq(7).css('margin-right') == '2px'
+);
+```
+
+Das neunte `div` sollte eine `height` von `90` Pixel und eine `margin` von `2` Pixel haben.
+
+```js
+assert(
+ $('div').eq(8).css('height') == '90px' &&
+ $('div').eq(8).css('margin-right') == '2px'
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/create-a-bar-for-each-data-point-in-the-set.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/create-a-bar-for-each-data-point-in-the-set.md
new file mode 100644
index 00000000000..e667a85638a
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/create-a-bar-for-each-data-point-in-the-set.md
@@ -0,0 +1,110 @@
+---
+id: 587d7fa8367417b2b2512bcd
+title: Erstelle einen Balken für jeden Datenpunkt im Set
+challengeType: 6
+forumTopicId: 301482
+dashedName: create-a-bar-for-each-data-point-in-the-set
+---
+
+# --description--
+
+Bei der letzten Aufgabe wurde dem `svg`-Element nur ein Rechteck hinzugefügt, um einen Balken darzustellen. Hier wirst du das kombinieren, was du bereits über `data()`, `enter()`, und SVG-Formen gelernt hast, um ein Rechteck für jeden Datenpunkt im `dataset` hinzuzufügen.
+
+Eine frühere Aufgabe zeigte das Format zum Erstellen und Einfügen eines `div` für jedes Element im `dataset`:
+
+```js
+d3.select("body").selectAll("div")
+ .data(dataset)
+ .enter()
+ .append("div")
+```
+
+Es gibt einige Unterschiede, die beim Arbeiten mit `rect`-Elementen, anstelle von `div`-Elementen beachtet werden müssen. Die `rect`-Elemente müssen an ein `svg`-Element beigefügt werden und nicht direkt in den `body`. Außerdem musst du D3 befehlen, wo es das `rect`-Element innerhalb des `svg`-Bereichs platzieren soll. Die Balkenplatzierung wird in der nächsten Aufgabe behandelt.
+
+# --instructions--
+
+Benutze die `data()`, `enter()`, und `append()`-Methoden, um ein `rect`-Element für jedes Element im `dataset` zu erstellen. Die Balken sollten alle übereinander angezeigt werden; dies wird in der nächsten Aufgabe verbessert.
+
+# --hints--
+
+Dein Dokument sollte 9 `rect`-Elemente besitzen.
+
+```js
+assert($('rect').length == 9);
+```
+
+Dein Code sollte die `data()`-Methode verwenden.
+
+```js
+assert(code.match(/\.data/g));
+```
+
+Dein Code sollte die `enter()`-Methode verwenden.
+
+```js
+assert(code.match(/\.enter/g));
+```
+
+Dein Code sollte die `append()`-Methode verwenden.
+
+```js
+assert(code.match(/\.append/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/use-the-d3.max-and-d3.min-functions-to-find-minimum-and-maximum-values-in-a-dataset.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/use-the-d3.max-and-d3.min-functions-to-find-minimum-and-maximum-values-in-a-dataset.md
new file mode 100644
index 00000000000..01100aa3758
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/use-the-d3.max-and-d3.min-functions-to-find-minimum-and-maximum-values-in-a-dataset.md
@@ -0,0 +1,90 @@
+---
+id: 587d7fac367417b2b2512bdc
+title: >-
+ Verwende die d3.max und d3.min Funktionen, um die Mindest- und Maximalwerte in einem Datensatz zu finden
+challengeType: 6
+forumTopicId: 301496
+dashedName: >-
+ use-the-d3-max-and-d3-min-functions-to-find-minimum-and-maximum-values-in-a-dataset
+---
+
+# --description--
+
+Die D3-Methoden `domain()` und `range()` haben diese Informationen für deine Skala basierend auf den Daten festgelegt. Es gibt ein paar Methoden, um das zu vereinfachen.
+
+Wenn du eine Domain festlegen möchtest, solltest du oft die Mindest- und Maximalwerte eines Datensatzes verwenden. Der Versuch, diese Werte manuell zu finden, kann, insbesondere bei einem großen Datensatz, zu Fehlern führen.
+
+D3 hat zwei Methoden - `min()` und `max()`, um diese Informationen zurückzugeben. Hier ist ein Beispiel:
+
+```js
+const exampleData = [34, 234, 73, 90, 6, 52];
+d3.min(exampleData)
+d3.max(exampleData)
+```
+
+Ein Datensatz hat möglicherweise verschachtelte Arrays, wie die `[x, y]` Koordinatenpaare, die sich im Beispiel des Streudiagramms befanden. In diesem Fall musst du D3 sagen, wie es das Maximum und das Minimum berechnen kann. Glücklicherweise nimmt sowohl die Methode `min()` als auch `max()` eine Callback-Funktion an. In diesem Beispiel ist das Callback-Funktionsargument `d` für das aktuelle interne Array. Der Callback muss das Element aus dem inneren Array zurückgeben (der `x` oder `y` Wert), über das das Maximum oder Minimum berechnet werden soll. Hier ist ein Beispiel dafür, wie man die minimalen und maximalen Werte mit einem Array von Arrays findet:
+
+```js
+const locationData = [[1, 7],[6, 3],[8, 3]];
+const minX = d3.min(locationData, (d) => d[0]);
+```
+
+`minX` würde den Wert `1` haben.
+
+# --instructions--
+
+Das `positionData`-Array enthält Subarrays von x, y, und z Koordinaten. Benutze eine D3 Methode, um den Maximalwert der z-Koordinate (der dritte Wert) von dem Array zu finden und speicher ihn in der `output`-Variable ab.
+
+# --hints--
+
+Der Text in `h2` sollte `8` sein.
+
+```js
+assert(output == 8 && $('h2').text() == '8');
+```
+
+Dein Code sollte die `max()`-Methode verwenden.
+
+```js
+assert(
+ code.match(/\.max/g),
+ 'Your code should use the max() method.'
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-data-in-d3.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-data-in-d3.md
new file mode 100644
index 00000000000..b7a2e97902f
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-data-in-d3.md
@@ -0,0 +1,105 @@
+---
+id: 587d7fa7367417b2b2512bc4
+title: Arbeite mit den Daten in D3
+challengeType: 6
+forumTopicId: 301497
+dashedName: work-with-data-in-d3
+---
+
+# --description--
+
+Die D3-Bibliothek stützt sich auf einen datenbezogenen Ansatz. Hast du einen Datensatz, kannst du Methoden aus der D3-Bibliothek verwenden, um diesen auf der Seite anzuzeigen. Es gibt verschiedene Datenformate, diese Aufgabe aber behandelt einen einfachen Zahlen-Array.
+
+Der erste Schritt ist D3 auf die Daten aufmerksam zu machen. Die Methode `data()` wird auf eine Auswahl von DOM-Elementen angewendet, um die Daten an diese Elemente anzuhängen. Der Datensatz wird anschließend als Argument an die Methode übergeben.
+
+Ein häufig verwendetes Workflow-Muster ist es, für jeden im Datensatz vorhandenen Eintrag, ein neues Element im Dokument zu erstellen. Hierfür gibt es bei D3 die `enter()`-Methode.
+
+Bei einer Kombination der `enter()`- und der `data()`-Methode werden die gewählten Elemente aus der Seite mit der Anzahl von Dateneinträgen in der Sammlung verglichen. Gibt es weniger Elemente als Dateneinträge, werden die fehlenden Elemente erzeugt.
+
+Hier ist ein Beispiel, das ein `ul`-Element auswählt und einen neuen Dateneintrag anhand der Anzahl der Einträge im Array erstellt:
+
+```html
+
+
+
+
+```
+
+Es hört sich womöglich verwirrend an, noch nicht existierende Elemente auszuwählen. Dieser Code weist D3 an, als Erstes den `ul`-Tag der Seite auszuwählen. Als Nächstes werden alle Elemente der Liste ausgewählt, die eine leere Sammlung zurückgeben. Anschließend prüft die `data()`-Methode die Datensammlung und führt folgenden Code dreimal aus – einmal für jedes Element im Array. Die `enter()`-Methode benötigt 3 Elemente – je eines für jeden Eintrag von `dataset` – bemerkt aber, dass keine `li`-Elemente auf der Seite existieren. Neue `li`-Elemente werden an `ul` angehängt und sind durch den Text `New item` gekennzeichnet.
+
+# --instructions--
+
+Wähle den `body`-Knoten aus, dann alle `h2`-Elemente. Lass' D3 für jedes Element im `dataset`-Array einen neuen `h2`-Tag erstellen und anhängen. Der Text innerhalb von `h2` sollte `New Title` sein. Dein Code sollte Gebrauch von der `data()`- und `enter()`-Methode machen.
+
+# --hints--
+
+In deinem Dokument sollten 9 `h2`-Elemente vorkommen.
+
+```js
+assert($('h2').length == 9);
+```
+
+Der Text innerhalb des `h2`-Elements sollte `New Title` sein. Achte hierbei auf Groß- und Kleinschreibung und Abstände – diese müssen exakt übereinstimmen.
+
+```js
+assert(
+ $('h2')
+ .text()
+ .match(/New Title/g).length == 9
+);
+```
+
+Dein Code sollte von der `data()`-Methode Gebrauch machen.
+
+```js
+assert(code.match(/\.data/g));
+```
+
+Dein Code sollte von der `enter()`-Methode Gebrauch machen.
+
+```js
+assert(code.match(/\.enter/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-dynamic-data-in-d3.md b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-dynamic-data-in-d3.md
new file mode 100644
index 00000000000..6094eff02bf
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/data-visualization-with-d3/work-with-dynamic-data-in-d3.md
@@ -0,0 +1,122 @@
+---
+id: 587d7fa7367417b2b2512bc5
+title: Mit Dynamischen Daten in D3 arbeiten
+challengeType: 6
+forumTopicId: 301498
+dashedName: work-with-dynamic-data-in-d3
+---
+
+# --description--
+
+Die letzten zwei Aufgaben beinhalteten die Grundlagen der dynamischen Datenanzeige mit den D3-Methoden `data()` und `enter()`. Diese Methoden benutzen Datensätze, um zusammen mit der `append()`-Methode neue DOM-Elemente für jeden Dateneintrag zu erstellen.
+
+In der vorherigen Aufgabe hast du ein neues `h2`-Element für jeden Eintrag des `dataset`-Arrays erstellt – diese beinhalteten jedoch alle den gleichen Text, `New Title`. Das liegt daran, dass du die Daten, die an jedes der `h2`-Elemente gebunden sind, nicht verwendet hast.
+
+Die `text()`-Methode von D3 kann sowohl mit einem String als auch mit einer Callback-Funktion als Argument arbeiten:
+
+```js
+selection.text((d) => d)
+```
+
+Im obigen Beispiel bezieht sich der Parameter `d` auf einen einzelnen Eintrag im Dataset, an den eine Auswahl gebunden ist.
+
+Wenn man das aktuelle Beispiel im Kontext verwenden, dann ist das erste `h2`-Element an 12 gebunden, das zweite `h2`-Element an 31, das dritte `h2`-Element an 22, und so weiter.
+
+# --instructions--
+
+Ändere die `text()`-Methode so, dass jedes `h2`-Element den dazugehörigen Wert des `dataset`-Arrays mit einem einzigen Leerzeichen und den String `USD` anzeigt. Zum Beispiel sollte die erste Überschrift `12 USD` sein.
+
+# --hints--
+
+Das erste `h2`-Element sollte aus dem Text `12 USD` bestehen.
+
+```js
+assert($('h2').eq(0).text() == '12 USD');
+```
+
+Das zweite `h2`-Element sollte aus dem Text `31 USD` bestehen.
+
+```js
+assert($('h2').eq(1).text() == '31 USD');
+```
+
+Das dritte `h2`-Element sollte aus dem Text `22 USD` bestehen.
+
+```js
+assert($('h2').eq(2).text() == '22 USD');
+```
+
+Das vierte `h2`-Element sollte aus dem Text `17 USD` bestehen.
+
+```js
+assert($('h2').eq(3).text() == '17 USD');
+```
+
+Das fünfte `h2`-Element sollte aus dem Text `25 USD` bestehen.
+
+```js
+assert($('h2').eq(4).text() == '25 USD');
+```
+
+Das sechste `h2`-Element sollte aus dem Text `18 USD` bestehen.
+
+```js
+assert($('h2').eq(5).text() == '18 USD');
+```
+
+Das siebte `h2`-Element sollte aus dem Text `29 USD` bestehen.
+
+```js
+assert($('h2').eq(6).text() == '29 USD');
+```
+
+Das achte `h2`-Element sollte aus dem Text `14 USD` bestehen.
+
+```js
+assert($('h2').eq(7).text() == '14 USD');
+```
+
+Das neunte `h2`-Element sollte aus dem Text `9 USD` bestehen.
+
+```js
+assert($('h2').eq(8).text() == '9 USD');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/access-the-json-data-from-an-api.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/access-the-json-data-from-an-api.md
new file mode 100644
index 00000000000..27bf4ab665a
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/access-the-json-data-from-an-api.md
@@ -0,0 +1,167 @@
+---
+id: 587d7fae367417b2b2512be4
+title: Greife auf JSON-Daten mit einer API zu
+challengeType: 6
+forumTopicId: 301499
+dashedName: access-the-json-data-from-an-api
+---
+
+# --description--
+
+In der vorherigen Herausforderung hast du gelernt, JSON-Daten aus der Katzenfoto-API von freeCodeCamp abzurufen.
+
+Hier wirst du dich genauer mit den zurückgegebenen Daten auseinandersetzen, um das JSON-Format besser zu verstehen. Erinnere dich an folgende Notationen JavaScripts:
+
+[ ] -> Quadratische Klammern symbolisieren ein Array, { } -> Geschweifte Klammern ein Objekt, " " -> und Anführungszeichen eine Zeichenfolge. Sie werden zudem für Schlüsselbezeichnungen in JSON verwendet.
+
+Es ist wichtig, die Struktur der Daten, die eine API zurückgibt, zu verstehen, da sie die Art und Weise, wie du Werte aufrufst, beeinflussen.
+
+Klicke rechts auf den `Get Message`-Button, um die JSON-Daten der Katzenfoto-API von freeCodeCamp in das HTML zu laden.
+
+Das erste und letzte Zeichen der JSON-Daten sind eckige Klammern `[ ]`. Dies bedeutet, dass die Daten in einem Array zurückgegeben werden. Das zweite Zeichen in den JSON-Daten ist eine geschweifte `{` Klammer, welche den Anfang eines Objekts ankündigt. Schaust du genau hin, bemerkst du, dass es sich eigentlich um drei verschiedene Objekte handelt. Die JSON-Daten bestehen aus einem Array dreier Objekte – jedes beinhaltet Informationen über ein Katzenfoto.
+
+Du hast bereits zuvor gelernt, dass Objekte "Schlüssel-Wert-Paare", die jeweils mit einem Komma voneinander getrennt sind, enthalten. Im diesem Beispiel besteht das erste Objekt aus `"id":0` – `id` ist hier der Schlüssel und `0` der dazugehörige Wert. In ähnlicher Weise gibt es Schlüssel für `imageLink`, `altText` und `codeNames`. Jedes Katzenfoto besitzt hierbei die gleichen Schlüssel, nicht aber die gleichen Werte.
+
+Ein weiteres, interessantes "Schlüssel-Wert-Paar" im ersten Objekt ist `"codeNames":["Juggernaut","Mrs. Wallace","ButterCup"]`. Hier ist `codeNames` der Schlüssel, mit einem Array aus drei Strings als Wert. Es ist möglich, einen Array aus Objekten sowie einen Schlüssel mit einem Array als Wert zu haben.
+
+Erinnere dich daran, wie du Daten in Arrays und Objekten aufrufst. Arrays benutzen Klammer-Notation, um einen bestimmten Index eines Elements aufzurufen. Objekte benutzen entweder Klammer- oder Punkt-Notation, um auf den Wert einer bestimmten Eigenschaft zuzugreifen. Hier ist ein Beispiel, das die `altText`-Eigenschaft des ersten Katzenfotos ausgibt – beachte, dass die zergliederten JSON-Daten im Editor in der Variable `json` gespeichert sind.
+
+```js
+console.log(json[0].altText);
+```
+
+Die Konsole würde den String `A white cat wearing a green helmet shaped melon on its head.` ausgeben.
+
+# --instructions--
+
+Für die Katze mit der `id` von 2 gibst du in der Konsole den zweiten Wert des `codeNames`-Arrays aus. Du solltest Klammer- und Punkt-Notation mit dem Objekt (welches in der Variable `json` gespeichert ist) verwenden, um den Wert aufzurufen.
+
+# --hints--
+
+Dein Code sollte auf Klammer- und Punkt-Notation zurückgreifen, um auf den korrekten Codenamen zuzugreifen und `Loki` in der Konsole auszugeben.
+
+```js
+assert(
+ code.match(
+ /console\s*\.\s*log\s*\(\s*json\s*\[2\]\s*(\.\s*codeNames|\[\s*('|`|")codeNames\2\s*\])\s*\[\s*1\s*\]\s*\)/g
+ )
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/convert-json-data-to-html.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/convert-json-data-to-html.md
new file mode 100644
index 00000000000..175d59836ca
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/convert-json-data-to-html.md
@@ -0,0 +1,199 @@
+---
+id: 587d7fae367417b2b2512be5
+title: JSON-Daten zu HTML konvertieren
+challengeType: 6
+forumTopicId: 16807
+dashedName: convert-json-data-to-html
+---
+
+# --description--
+
+Da du nun Daten von der JSON-API erhältst, kannst du sie in HTML anzeigen lassen.
+
+Die Katzenfoto-Objekte befinden sich in einem Array – benutze deshalb die `forEach`-Methode, um die Daten zu durchlaufen. Du kannst nun für jeden Eintrag die HTML-Elemente ändern.
+
+Deklariere zunächst eine HTML-Variable mit `let html = "",`.
+
+Durchlaufe anschließend die JSON-Daten und füge HTML der Variable hinzu, die Schlüsselbezeichnungen, gefolgt vom jeweiligen Wert, in `strong`-Tags setzt. Sobald der Schleifendurchlauf beendet ist, kannst du die Daten wiedergeben.
+
+Hier ist der Code dafür:
+
+```js
+let html = "";
+json.forEach(function(val) {
+ const keys = Object.keys(val);
+ html += "";
+ keys.forEach(function(key) {
+ html += "" + key + " : " + val[key] + " ";
+ });
+ html += "
";
+});
+```
+
+**Hinweis:** In dieser Herausforderung fügst du HTML-Elemente der Seite hinzu, du kannst hier deshalb nicht auf `textContent` setzen. Du musst stattdessen `innerHTML` verwenden – was eine Seite anfällig für seitenübergreifende Scripting-Angriffe macht.
+
+# --instructions--
+
+Füge eine `forEach`-Methode hinzu, welche die JSON-Daten durchläuft und die für die Anzeige nötigen HTML-Elemente erzeugt.
+
+Hier ist ein JSON-Beispiel:
+
+```json
+[
+ {
+ "id":0,
+ "imageLink":"https://s3.amazonaws.com/freecodecamp/funny-cat.jpg",
+ "altText":"A white cat wearing a green helmet shaped melon on its head. ",
+ "codeNames":[ "Juggernaut", "Mrs. Wallace", "Buttercup"
+ ]
+ }
+]
+```
+
+# --hints--
+
+Dein Code sollte die Daten in einer `html`-Variable speichern
+
+```js
+assert(__helpers.removeWhiteSpace(code).match(/html(\+=|=html\+)/g))
+```
+
+Dein Code sollte Gebrauch von der `forEach`-Methode machen, um die JSON-Daten der API zu durchlaufen.
+
+```js
+assert(code.match(/json\.forEach/g));
+```
+
+Dein Code sollte die Schlüsselbezeichnungen in `strong`-Tags setzen.
+
+```js
+assert(code.match(/.+<\/strong>/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-geolocation-data-to-find-a-users-gps-coordinates.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-geolocation-data-to-find-a-users-gps-coordinates.md
new file mode 100644
index 00000000000..1ab902a88f5
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-geolocation-data-to-find-a-users-gps-coordinates.md
@@ -0,0 +1,98 @@
+---
+id: 587d7faf367417b2b2512be8
+title: Geolokalisierungsdaten zur Ermittlung von GPS-Koordinaten eines Nutzers abrufen
+challengeType: 6
+forumTopicId: 18188
+dashedName: get-geolocation-data-to-find-a-users-gps-coordinates
+---
+
+# --description--
+
+Eine weitere coole Sache, die du machen kannst, ist auf den Standort deines Nutzers zuzugreifen. Jeder Browser hat einen eingebauten Navigator, der dir diese Informationen liefern kann.
+
+Der Navigator ermittelt den aktuellen Längen- und Breitengrad des Nutzers.
+
+Du wirst dazu aufgefordert, festzulegen, ob du dieser Seite Zugriff auf deinen Standort geben möchtest oder nicht. Diese Aufgabe kann, solange der Code stimmt, unabhängig davon abgeschlossen werden.
+
+Lässt du den Zugriff zu, verändert sich der Text auf dem Ausgabetelefon zu deinem Breiten- und Längengrad.
+
+Hier ist der Code dafür:
+
+```js
+if (navigator.geolocation){
+ navigator.geolocation.getCurrentPosition(function(position) {
+ document.getElementById('data').innerHTML="latitude: " + position.coords.latitude + " longitude: " + position.coords.longitude;
+ });
+}
+```
+
+Er prüft zunächst, ob das Objekt `navigator.geolocation` existiert. Wenn dies der Fall ist, wird die Methode `getCurrentPosition` für dieses Objekt aufgerufen, die eine asynchrone Anfrage nach der Position des Nutzers startet. Wenn die Anfrage erfolgreich ist, wird die Callback-Funktion in der Methode ausgeführt. Diese Funktion greift auf die Werte für Breiten- und Längengrad des `position`-Objekts mithilfe von Punktnotation zu und aktualisiert die HTML.
+
+# --instructions--
+
+Füge den Beispielcode innerhalb des `script`-Tags ein, um den aktuellen Standort des Nutzers abzurufen und in die HTML einzufügen.
+
+# --hints--
+
+Dein Code sollte `navigator.geolocation` verwenden, um auf den aktuellen Standort des Nutzers zuzugreifen.
+
+```js
+assert(code.match(/navigator\.geolocation\.getCurrentPosition/g));
+```
+
+Dein Code sollte `position.coords.latitude` verwenden, um den Breitengrad des Nutzers anzuzeigen.
+
+```js
+assert(code.match(/position\.coords\.latitude/g));
+```
+
+Dein Code sollte `position.coords.longitude` verwenden, um den Längengrad des Nutzers anzuzeigen.
+
+```js
+assert(code.match(/position\.coords\.longitude/g));
+```
+
+Du solltest die Position des Nutzers innerhalb des `div`-Elements mit der `id="data"` anzeigen.
+
+```js
+assert(
+ code.match(/document\.getElementById\(\s*?('|")data\1\s*?\)\.innerHTML/g)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+You are here:
+
+
+
+```
+
+# --solutions--
+
+```html
+
+You are here:
+
+
+
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-fetch-method.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-fetch-method.md
new file mode 100644
index 00000000000..7ceab92cfdf
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-fetch-method.md
@@ -0,0 +1,174 @@
+---
+id: 5ccfad82bb2dc6c965a848e5
+title: Abrufen von JSON mit der JavaScript-Abrufmethode
+challengeType: 6
+forumTopicId: 301501
+dashedName: get-json-with-the-javascript-fetch-method
+---
+
+# --description--
+
+Eine andere Möglichkeit, externe Daten anzufordern, ist die Verwendung der JavaScript `fetch()`-Methode. Sie ist äquivalent zu `XMLHttpRequest`, aber die Syntax gilt als einfacher zu verstehen.
+
+Hier ist der Code für eine GET-Anfrage an `/json/cats.json`
+
+```js
+
+fetch('/json/cats.json')
+ .then(response => response.json())
+ .then(data => {
+ document.getElementById('message').innerHTML = JSON.stringify(data);
+ })
+
+```
+
+Schau dir jeden einzelnen Teil dieses Codes an.
+
+Die erste Zeile ist diejenige, die den Antrag stellt. `fetch(URL)` stellt also eine `GET`-Anfrage an die angegebene URL. Die Methode gibt ein Promise zurück.
+
+Nachdem ein Promise zurückgegeben wurde, wird bei erfolgreicher Anfrage die Methode `then` ausgeführt, die die Antwort in das JSON-Format konvertiert.
+
+Die `then`-Methode gibt auch ein Promise zurück, das von der nächsten `then`-Methode bearbeitet wird. Das Argument im zweiten `then` ist das JSON-Objekt, nach dem du suchst!
+
+Es wählt nun das Element aus, das die Daten erhalten soll, indem es `document.getElementById()` verwendet. Es ändert dann den HTML-Code des Elements, indem es ein String einfügt, das aus dem von der Anfrage zurückgegebenen JSON-Objekt erstellt wurde.
+
+# --instructions--
+
+Aktualisiere den Code, um eine `GET`-Anfrage an die freeCodeCamp Katzen-Foto-API zu erstellen und zu senden. Diesmal aber mit der Methode `fetch` anstelle von `XMLHttpRequest`.
+
+# --hints--
+
+Dein Code sollte eine `GET`-Anfrage mit `fetch` stellen.
+
+```js
+assert(code.match(/fetch\s*\(\s*('|")\/json\/cats\.json\1\s*\)/g));
+```
+
+Dein Code sollte `then` verwenden, um die Antwort in JSON zu konvertieren.
+
+```js
+assert(
+ code.match(
+ /\.then\s*\(\s*\(?(?\w+)\)?\s*=>\s*\k\s*\.json\s*\(\s*\)\s*\)/g
+ )
+);
+```
+
+Dein Code sollte `then` verwenden, um die Daten zu verarbeiten, die von dem anderen `then` in JSON umgewandelt wurden.
+
+```js
+assert(__helpers.removeWhiteSpace(code).match(/\.then\(\(?\w+\)?=>{[^}]*}\)/g));
+```
+
+Dein Code sollte das Element mit der ID `message` erhalten und sein inneres HTML in den String der JSON-Daten ändern.
+
+```js
+assert(
+ __helpers.removeWhiteSpace(code).match(
+ /document\.getElementById\(('|")message\1\)\.innerHTML=JSON\.stringify\(?\w+\)/g
+ )
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-xmlhttprequest-method.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-xmlhttprequest-method.md
new file mode 100644
index 00000000000..9b4d9045b54
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/get-json-with-the-javascript-xmlhttprequest-method.md
@@ -0,0 +1,196 @@
+---
+id: 587d7fae367417b2b2512be3
+title: JSON mit JavaScripts XMLHttpRequest-Methode erhalten
+challengeType: 6
+forumTopicId: 301502
+dashedName: get-json-with-the-javascript-xmlhttprequest-method
+---
+
+# --description--
+
+Du kannst Daten auch von einer externen Quelle anfordern. Hier kommen APIs ins Spiel.
+
+Bedenke: APIs – oder Application Programming Interfaces – sind von Computern benutzte Werkzeuge, um miteinander zu kommunizieren. Du wirst lernen, wie man HTML mit den Daten, die wir von APIs erhalten, mit einer Technologie namens AJAX aktualisiert.
+
+Die meisten APIs übertragen Daten in einem als JSON bekannten Format. JSON steht für JavaScript Object Notation.
+
+Der JSON-Syntax ähnelt JavaScripts direkter Objekt-Notation. JSON wird aus Objekteigenschaften und deren aktuellen Werte gebildet, welche zwischen einem `{` und einem `}` gesetzt werden.
+
+Diese Eigenschaften und ihre Werte werden oft als "Schlüssel-Wert-Paare" bezeichnet.
+
+Jedoch werden von APIs übermittelte JSON-Daten als `bytes` gesendet, deine Anwendung erhält sie aber als `string`. Zwar können diese in JavaScript-Objekte konvertiert werden, sind aber standardmäßig keine. Die `JSON.parse`-Methode parst einen String und erzeugt ein JavaScript-Objekt, welches diesen beschreibt.
+
+Du kannst die JSON-Daten der Katzenfoto-API von freeCodeCamp anfordern. Hier ist der Code, den du in dein Click-Event setzten kannst, um das durchzuführen:
+
+```js
+const req = new XMLHttpRequest();
+req.open("GET",'/json/cats.json',true);
+req.send();
+req.onload = function(){
+ const json = JSON.parse(req.responseText);
+ document.getElementsByClassName('message')[0].innerHTML = JSON.stringify(json);
+};
+```
+
+Folgend eine Beschreibung über das, was jeder Abschnitt macht. Das `XMLHttpRequest`-JavaScript-Objekt kennt eine Vielzahl von Eigenschaften und Methoden für Datentransfers. Zuerst wird eine neue Instanz des `XMLHttpRequest`-Objekts erzeugt und in der `req`-Variable gespeichert. Die `open`-Methode initialisiert eine Anfrage – hier eine, die Daten von einer API anfordert; es handelt sich deshalb um eine `GET`-Anfrage. Das zweite Argument von `open` ist die URL der API, von der du Daten anforderst. Das dritte Argument ist ein Boolean-Wert – `true` erstellt eine asynchrone Anfrage. Die Methode `send` schickt die Anfrage anschließend ab. Zuletzt parst der `onload`-Eventhandler die zurückgegebenen Daten und wendet die `JSON.stringify`-Methode an, um das JavaScript-Objekt in einen String zu konvertieren. Dieser String wird dann als Nachrichtentext verwendet.
+
+# --instructions--
+
+Aktualisiere den Code, um eine `GET`-Anfrage zu erzeugen, und an freeCodeCamp's Katzenfoto-API zu senden. Klicke dann den `Get Message`-Button. Deine AJAX-Funktion wird `The message will go here` durch den unverarbeiteten JSON-Rückgabewert der API ersetzen.
+
+# --hints--
+
+Dein Code sollte eine neue `XMLHttpRequest` erzeugen.
+
+```js
+assert(code.match(/new\s+?XMLHttpRequest\(\s*?\)/g));
+```
+
+Dein Code sollte von der `open`-Methode Gebrauch machen, um eine `GET`-Anfrage an freeCodeCamp's Katzenfoto-API zu initialisieren.
+
+```js
+assert(
+ code.match(
+ /\.open\(\s*?('|")GET\1\s*?,\s*?('|")\/json\/cats\.json\2\s*?,\s*?true\s*?\)/g
+ )
+);
+```
+
+Dein Code sollte die `send`-Methode verwenden, um die Anfrage abzuschicken.
+
+```js
+assert(code.match(/\.send\(\s*\)/g));
+```
+
+Dein Code sollte über einen auf eine Funktion gesetzten `onload`-Eventhandler verfügen.
+
+```js
+assert(
+ code.match(/\.onload\s*=\s*(function|\(\s*?\))\s*?(\(\s*?\)|\=\>)\s*?{/g)
+);
+```
+
+Dein Code sollte die `JSON.parse`-Methode verwenden, um den `responseText` zu parsen.
+
+```js
+assert(code.match(/JSON\s*\.parse\(\s*.*\.responseText\s*\)/g));
+```
+
+Dein Code sollte das Element mit der Klasse `message` aufrufen und die inneren HTML-Werte auf den String der JSON-Daten setzen.
+
+```js
+assert(
+ code.match(
+ /document\s*\.getElementsByClassName\(\s*?('|")message\1\s*?\)\[0\]\s*\.innerHTML\s*?=\s*?JSON\.stringify\(.+?\)/g
+ )
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/handle-click-events-with-javascript-using-the-onclick-property.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/handle-click-events-with-javascript-using-the-onclick-property.md
new file mode 100644
index 00000000000..959aef6901e
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/handle-click-events-with-javascript-using-the-onclick-property.md
@@ -0,0 +1,140 @@
+---
+id: 587d7fad367417b2b2512be1
+title: Bestimme Klick-Ereignisse mit JavaScript unter Verwendung der Eigenschaft onclick
+challengeType: 6
+forumTopicId: 301503
+dashedName: handle-click-events-with-javascript-using-the-onclick-property
+---
+
+# --description--
+
+Der Code soll erst ausgeführt werden, wenn die Seite vollständig geladen wurde. Zu diesem Zweck kannst du ein JavaScript-Ereignis mit dem Namen `DOMContentLoaded` an das Dokument anhängen. Hier ist der Code dafür:
+
+```js
+document.addEventListener('DOMContentLoaded', function() {
+
+});
+```
+
+Du kannst Event-Handler implementieren, die innerhalb der `DOMContentLoaded` -Funktion laufen. Du kannst ein `onclick` Event-Handler implementieren, der ausgelöst wird, wenn der Nutzer auf das Element mit der ID `getMessage` klickt, indem du den folgenden Code hinzufügst:
+
+```js
+document.getElementById('getMessage').onclick = function(){};
+```
+
+# --instructions--
+
+Füge ein Klick-Event-Handler innerhalb der `DOMContentLoaded`-Funktion für das Element mit der ID `getMessage` hinzu.
+
+# --hints--
+
+Dein Code sollte die `document.getElementById`-Methode verwenden, um das `getMessage`-Element auszuwählen.
+
+```js
+assert(code.match(/document\s*\.getElementById\(\s*?('|")getMessage\1\s*?\)/g));
+```
+
+Dein Code sollte einen `onclick`-Event-Handler hinzufügen.
+
+```js
+assert(typeof document.getElementById('getMessage').onclick === 'function');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/post-data-with-the-javascript-xmlhttprequest-method.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/post-data-with-the-javascript-xmlhttprequest-method.md
new file mode 100644
index 00000000000..e88fdf661b2
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/post-data-with-the-javascript-xmlhttprequest-method.md
@@ -0,0 +1,206 @@
+---
+id: 587d7faf367417b2b2512be9
+title: Daten mit der JavaScript-Methode XMLHttpRequest übermitteln
+challengeType: 6
+forumTopicId: 301504
+dashedName: post-data-with-the-javascript-xmlhttprequest-method
+---
+
+# --description--
+
+In den vorherigen Beispielen hast du Daten von einer externen Ressource empfangen. Du kannst auch Daten an eine externe Ressource senden, solange diese Ressource AJAX-Anfragen unterstützt und du die URL kennst.
+
+Die JavaScript Methode `XMLHttpRequest` wird auch verwendet, um Daten an einen Server zu übermitteln. Hier ist ein Beispiel:
+
+```js
+const xhr = new XMLHttpRequest();
+xhr.open('POST', url, true);
+xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
+xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4 && xhr.status === 201){
+ const serverResponse = JSON.parse(xhr.response);
+ document.getElementsByClassName('message')[0].textContent = serverResponse.userName + serverResponse.suffix;
+ }
+};
+const body = JSON.stringify({ userName: userName, suffix: ' loves cats!' });
+xhr.send(body);
+```
+
+Du hast bereits mehrere dieser Methoden gesehen. Hier initialisiert die `open`-Methode die Anfrage als `POST` an die angegebene URL der externen Ressource und verwendet den `true` Boolean, um es asynchron zu gestalten. Die Methode `setRequestHeader` setzt den Wert eines HTTP-Request-Headers, der Informationen über den Absender und die Anfrage enthält. Es muss nach der `open`-Methode aufgerufen werden, aber noch vor der `send`-Methode. Die beiden Parameter sind der Name des Headers und der Wert, der als der Körper des Headers gesetzt werden soll. Als nächstes bearbeitet der `onreadystatechange` Event-Listener eine Änderung des Status der Abfrage. Ein `readyState` von `4` bedeutet, dass die Operation abgeschlossen ist und ein `status` von `201` bedeutet, dass es eine erfolgreiche Anfrage war. Der HTML-Code des Dokuments kann aktualisiert werden. Schließlich sendet die `send`-Methode eine Anfrage mit dem `body` Wert, dessen `userName`-Schlüssel vom Nutzer im `input`-Feld gegeben wurde.
+
+# --instructions--
+
+Aktualisiere den Code, damit er eine `POST`-Anfrage an den API-Endpunkt stellt. Gib dann deinen Namen in das Eingabefeld ein und klicke auf `Send Message`. Deine AJAX Funktion sollte `Reply from Server will be here.` durch Daten vom Server ersetzen. Formatiere die Antwort so, dass dein Name zusammen mit dem Text `loves cats` angezeigt wird.
+
+# --hints--
+
+Dein Code sollte eine neue `XMLHttpRequest` erstellen.
+
+```js
+assert(code.match(/new\s+?XMLHttpRequest\(\s*?\)/g));
+```
+
+Dein Code sollte die `open`-Methode verwenden, um eine `POST`-Anfrage an den Server zu senden.
+
+```js
+assert(code.match(/\.open\(\s*?('|")POST\1\s*?,\s*?url\s*?,\s*?true\s*?\)/g));
+```
+
+Dein Code sollte die `setRequestHeader`-Methode verwenden.
+
+```js
+assert(
+ code.match(
+ /\.setRequestHeader\(\s*?('|")Content-Type\1\s*?,\s*?('|")application\/json;\s*charset=UTF-8\2\s*?\)/g
+ )
+);
+```
+
+Dein Code sollte einen `onreadystatechange` Event-Handler auf eine Funktion gesetzt haben.
+
+```js
+assert(code.match(/\.onreadystatechange\s*?=/g));
+```
+
+Dein Code sollte das Element mit der Klasse `message` erhalten und dessen `textContent` zu `userName loves cats` ändern
+
+```js
+assert(
+ code.match(
+ /document\.getElementsByClassName\(\s*?('|")message\1\s*?\)\[0\]\.textContent\s*?=\s*?.+?\.userName\s*?\+\s*?.+?\.suffix/g
+ )
+);
+```
+
+Dein Code sollte die `send`-Methode verwenden.
+
+```js
+assert(code.match(/\.send\(\s*?body\s*?\)/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+Cat Friends
+
+ Reply from Server will be here
+
+
+ Your name:
+
+
+
+ Send Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+Cat Friends
+
+ Reply from Server will be here
+
+
+ Your name:
+
+
+
+ Send Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/pre-filter-json-to-get-the-data-you-need.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/pre-filter-json-to-get-the-data-you-need.md
new file mode 100644
index 00000000000..8dd5901c4f9
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/pre-filter-json-to-get-the-data-you-need.md
@@ -0,0 +1,171 @@
+---
+id: 587d7fae367417b2b2512be7
+title: Filtere JSON vor, um von dir benötigte Daten zu erhalten
+challengeType: 6
+forumTopicId: 18257
+dashedName: pre-filter-json-to-get-the-data-you-need
+---
+
+# --description--
+
+Wenn du nicht jedes Katzenfoto rendern möchtest, das du von der Katzenfoto-API freeCodeCamps erhältst, kannst du die JSON-Daten – vor dem Durchlaufen – vorfiltern.
+
+Da die JSON-Daten in einem Array gespeichert sind, kannst du eine `filter`-Methode verwenden, um die Katze mit dem Schlüssel `id` vom Wert `1` herauszufiltern.
+
+Hier ist der Code dafür:
+
+```js
+json = json.filter(function(val) {
+ return (val.id !== 1);
+});
+```
+
+# --instructions--
+
+Füge einen Code hinzu, um einen `filter` auf die json-Daten anzuwenden und die Katze mit einer `id` von `1` zu entfernen.
+
+# --hints--
+
+Dein Code sollte die `filter`-Methode anwenden.
+
+```js
+assert(code.match(/json\.filter/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/render-images-from-data-sources.md b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/render-images-from-data-sources.md
new file mode 100644
index 00000000000..77bb80aaf83
--- /dev/null
+++ b/curriculum/challenges/german/04-data-visualization/json-apis-and-ajax/render-images-from-data-sources.md
@@ -0,0 +1,163 @@
+---
+id: 587d7fae367417b2b2512be6
+title: Bilder aus Datenquellen rendern
+challengeType: 6
+forumTopicId: 18265
+dashedName: render-images-from-data-sources
+---
+
+# --description--
+
+Die letzten Herausforderungen zeigten, dass jedes Objekt im JSON-Array den `imageLink`-Schlüssel mit einem, aus der URL des Katzenfotos bestehenden, Wert enthält.
+
+Wenn du diese Objekte durchläufst, kannst du die Eigenschaft `imageLink` verwenden, um dieses Bild in einem `img`-Element darzustellen.
+
+Hier ist der Code dafür:
+
+```js
+html += " ";
+```
+
+# --instructions--
+
+Füge Code hinzu, um die Eigenschaften `imageLink` und `altText` in einem `img`-Tag zu verwenden.
+
+# --hints--
+
+Du solltest die `imageLink`-Eigenschaft verwenden, um Bilder darzustellen.
+
+```js
+assert(code.match(/val\.imageLink/g));
+```
+
+Du solltest `altText` für die `alt`-Attributwerte der Bilder verwenden.
+
+```js
+assert(code.match(/val\.altText/g));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
+
+# --solutions--
+
+```html
+
+
+Cat Photo Finder
+
+ The message will go here
+
+
+
+ Get Message
+
+
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/back-end-development-and-apis-projects/file-metadata-microservice.md b/curriculum/challenges/german/05-back-end-development-and-apis/back-end-development-and-apis-projects/file-metadata-microservice.md
new file mode 100644
index 00000000000..44c48d0270b
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/back-end-development-and-apis-projects/file-metadata-microservice.md
@@ -0,0 +1,88 @@
+---
+id: bd7158d8c443edefaeb5bd0f
+title: Datei-Metadaten-Microservice
+challengeType: 4
+forumTopicId: 301506
+dashedName: file-metadata-microservice
+---
+
+# --description--
+
+Erstelle eine vollständige JavaScript-Anwendung, die eine ähnliche Funktionalität wie https://file-metadata-microservice.freecodecamp.rocks aufweist. Bei der Arbeit an diesem Projekt musst du deinen Code mit einer der folgenden Methoden schreiben:
+
+- Klone dieses GitHub Repo und schließe dein Projekt lokal ab.
+- Benutze unser Replit Starter Projekt , um dein Projekt fertigzustellen.
+- Verwende einen Site-Builder deiner Wahl, um das Projekt fertigzustellen. Achte darauf, alle Dateien aus unserem GitHub Repo zu integrieren.
+
+Wenn du fertig bist, stelle sicher, dass dein Projekt öffentlich zugänglich gehostet ist. Gib dann die URL in das `Solution Link`-Feld ein. Füge optional einen Link zum Quellcode deines Projekts in das `GitHub Link`-Feld ein.
+
+# --instructions--
+
+**HINWEIS:** Du kannst das `multer` npm-Paket verwenden, um das Hochladen von Dateien zu verwalten.
+
+# --hints--
+
+Du solltest dein eigenes Projekt angeben und nicht die Beispiel-URL.
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/file-metadata-microservice\.freecodecamp\.rocks/.test(
+ getUserInput('url')
+ )
+ );
+};
+```
+
+Du kannst ein Formular übermitteln, das einen Datei-Upload beinhaltet.
+
+```js
+async (getUserInput) => {
+ const site = await fetch(getUserInput('url'));
+ const data = await site.text();
+ const doc = new DOMParser().parseFromString(data, 'text/html');
+ assert(doc.querySelector('input[type="file"]'));
+};
+```
+
+Im Eingabefeld für die Formulardatei ist das `name` Attribut auf `upfile` gesetzt.
+
+```js
+async (getUserInput) => {
+ const site = await fetch(getUserInput('url'));
+ const data = await site.text();
+ const doc = new DOMParser().parseFromString(data, 'text/html');
+ assert(doc.querySelector('input[name="upfile"]'));
+};
+```
+
+Wen du eine Datei übermittelst, empfängst du die Datei `name`, `type`, und `size` in Bytes innerhalb der JSON Antwort.
+
+```js
+async (getUserInput) => {
+ const formData = new FormData();
+ const fileData = await fetch(
+ 'https://cdn.freecodecamp.org/weather-icons/01d.png'
+ );
+ const file = await fileData.blob();
+ formData.append('upfile', file, 'icon');
+ const data = await fetch(getUserInput('url') + '/api/fileanalyse', {
+ method: 'POST',
+ body: formData
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'size');
+ assert.equal(parsed.name, 'icon');
+ assert.equal(parsed.type, 'image/png');
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-data-from-post-requests.md b/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-data-from-post-requests.md
new file mode 100644
index 00000000000..6ba9821f43a
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-data-from-post-requests.md
@@ -0,0 +1,78 @@
+---
+id: 587d7fb2367417b2b2512bf8
+title: Daten aus POST-Anfragen abrufen
+challengeType: 2
+forumTopicId: 301511
+dashedName: get-data-from-post-requests
+---
+
+# --description--
+
+Setze einen POST-Handler unter dem Pfad `/name` auf. Das ist der gleiche Pfad wie zuvor. Wir haben ein Formular auf der html-Startseite vorbereitet. Es werden die gleichen Daten der Übung 10 (Query-String) übermittelt. Ist der Body-Parser richtig konfiguriert, findest du die Parameter in dem Objekt `req.body`. Schaue dir das bekannte Bibliotheksbeispiel an:
+
+Route: POST '/library' urlencoded_body: userId=546&bookId=6754 req.body: {userId: '546', bookId: '6754'}
+
+Antworte mit dem demselben JSON-Objekt wie zuvor: `{name: 'firstname lastname'}`. Teste, ob dein Endpunkt funktioniert, indem du das HTML-Formular verwendest, das wir auf der Startseite der App bereitgestellt haben.
+
+Tipp: Es gibt außer GET und POST noch weitere http-Methoden. Es gibt eine Übereinstimmung zwischen dem http-Verb und der Operation, die du auf dem Server ausführen wirst. Die konventionelle Zuordnung ist:
+
+POST (manchmal PUT) - Erstelle eine neue Ressource anhand mit der Anfrage übermittelten Daten,
+
+GET - Auslesen vorhandener Ressource, ohne diese zu verändern
+
+PUT oder PATCH (manchmal auch POST) - Aktualisieren einer Ressource anhand übermittelter Daten,
+
+DELETE => Löschen einer Ressource.
+
+Es gibt noch eine Reihe anderer Methoden, um eine Verbindung mit dem Server auszuhandeln. Mit Ausnahme von GET können alle anderen oben aufgeführten Methoden eine Payload haben (d.h. die Daten im Anfrage-Körper). Die Body-Parser-Middleware funktioniert mit diesen Methoden ebenfalls.
+
+# --hints--
+
+Test 1: Dein API-Endpunkt sollte mit dem richtigen Namen antworten
+
+```js
+(getUserInput) =>
+ $.post(getUserInput('url') + '/name', { first: 'Mick', last: 'Jagger' }).then(
+ (data) => {
+ assert.equal(
+ data.name,
+ 'Mick Jagger',
+ 'Test 1: "POST /name" route does not behave as expected'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Test 2 : Dein API-Endpunkt sollte mit dem richtigen Namen antworten
+
+```js
+(getUserInput) =>
+ $.post(getUserInput('url') + '/name', {
+ first: 'Keith',
+ last: 'Richards'
+ }).then(
+ (data) => {
+ assert.equal(
+ data.name,
+ 'Keith Richards',
+ 'Test 2: "POST /name" route does not behave as expected'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-query-parameter-input-from-the-client.md b/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-query-parameter-input-from-the-client.md
new file mode 100644
index 00000000000..c0d7544d36f
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/basic-node-and-express/get-query-parameter-input-from-the-client.md
@@ -0,0 +1,67 @@
+---
+id: 587d7fb2367417b2b2512bf6
+title: Erhalte den Input der Query-Parameter vom Nutzer
+challengeType: 2
+forumTopicId: 301512
+dashedName: get-query-parameter-input-from-the-client
+---
+
+# --description--
+
+Eine weitere gängige Methode, um Eingaben vom Nutzer zu erhalten, ist die Kodierung der Daten nach dem Routenpfad unter Verwendung eines Abfrage-Strings. Der Query-String wird durch ein Fragezeichen (?) begrenzt und enthält Feld=Wert-Paare. Jedes Paar ist durch ein kaufmännisches Und-Zeichen (&) getrennt. Express kann die Daten aus dem Query-String auswerten und das Objekt `req.query` füllen. Einige Zeichen, wie z. B. das Prozentzeichen (%), können nicht in URLs enthalten sein und müssen in einem anderen Format kodiert werden, bevor man sie senden kann. Wenn du die API von JavaScript verwendest, kannst du bestimmte Methoden zur Kodierung/Dekodierung dieser Zeichen einsetzen.
+
+route_path: '/library' actual_request_URL: '/library?userId=546&bookId=6754' req.query: {userId: '546', bookId: '6754'}
+
+# --instructions--
+
+Erstelle einen API-Endpunkt, der unter `GET /name` eingebunden ist. Antworte mit einem JSON-Dokument, indem du die Struktur `{ name: 'firstname lastname'}` verwendest. Die Parameter Vor- und Nachname sollten in einem Query-String kodiert werden, z. B. `?first=firstname&last=lastname`.
+
+**Hinweis:** In der folgenden Übung wirst du Daten von einer POST-Anfrage erhalten, und zwar über denselben `/name`-Routenpfad. Wenn du möchtest, kannst du die folgende Methode verwenden `app.route(path).get(handler).post(handler)`. Mit dieser Syntax kannst du verschiedene Verb-Handler auf demselben Pfad verketten. Du kannst dir ein wenig Tipparbeit sparen und du erhälst einen sauberen Code.
+
+# --hints--
+
+Test 1: Dein API-Endpunkt sollte mit dem richtigen Namen antworten
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/name?first=Mick&last=Jagger').then(
+ (data) => {
+ assert.equal(
+ data.name,
+ 'Mick Jagger',
+ 'Test 1: "GET /name" route does not behave as expected'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Test 2: Dein API-Endpunkt sollte mit dem richtigen Namen antworten
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/name?last=Richards&first=Keith').then(
+ (data) => {
+ assert.equal(
+ data.name,
+ 'Keith Richards',
+ 'Test 2: "GET /name" route does not behave as expected'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-description-to-your-package.json.md b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-description-to-your-package.json.md
new file mode 100644
index 00000000000..3abe5a66810
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-description-to-your-package.json.md
@@ -0,0 +1,52 @@
+---
+id: 587d7fb3367417b2b2512bfc
+title: Füge deiner package.json eine Beschreibung hinzu
+challengeType: 2
+forumTopicId: 301522
+dashedName: add-a-description-to-your-package-json
+---
+
+# --description--
+
+Der nächste Teil einer guten package.json-Datei ist das `description` Feld; hier sollte eine kurze, aber informative Beschreibung deines Projekts zu finden sein.
+
+Planst du, eines Tages dein Paket mit npm zu veröffentlichen, ist das der String, der, wenn ein Nutzer überlegt, dein Paket zu installieren, deine Idee diesem näherbringen sollte. Jedoch ist das nicht der einzige Verwendungszweck für die Beschreibung – dieses Feld ist ein toller Weg, um zusammenzufassen, worum es in dem Projekt geht. In jedem Node.js-Projekt ist es ebenso wichtig, anderen Entwicklern, zukünftigen Maintainern oder vielleicht sogar deinem zukünftigen Selbst dabei zu helfen, das Projekt schnell zu verstehen.
+
+Unabhängig davon, was du für dein Projekt planst, wird eine Beschreibung auf jeden Fall empfohlen. Hier ist ein Beispiel:
+
+```json
+"description": "A project that does something awesome",
+```
+
+# --instructions--
+
+Füge der package.json-Datei deines Projekts eine `description` hinzu.
+
+**Hinweis:** Denke daran, doppelte Anführungszeichen für Feldnamen (") und Kommas (,) zu verwenden, um Felder zu trennen.
+
+# --hints--
+
+package.json sollte einen gültigen "description"-Schlüssel haben
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert(packJson.description, '"description" is missing');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-version-to-your-package.json.md b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-version-to-your-package.json.md
new file mode 100644
index 00000000000..7b10fb0db7c
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-a-version-to-your-package.json.md
@@ -0,0 +1,46 @@
+---
+id: 587d7fb4367417b2b2512bff
+title: Füge deiner package.json eine Version hinzu
+challengeType: 2
+forumTopicId: 301525
+dashedName: add-a-version-to-your-package-json
+---
+
+# --description--
+
+`version` ist eines der erforderlichen Felder in deiner package.json-Datei. Dieses Feld beschreibt die aktuelle Version deines Projekts. Hier ist ein Beispiel:
+
+```json
+"version": "1.2.0",
+```
+
+# --instructions--
+
+Füge der package.json-Datei deines Projekts eine `version` hinzu.
+
+# --hints--
+
+package.json sollte einen gültigen "version"-Schlüssel haben
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert(packJson.version, '"version" is missing');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-keywords-to-your-package.json.md b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-keywords-to-your-package.json.md
new file mode 100644
index 00000000000..e94dd77954f
--- /dev/null
+++ b/curriculum/challenges/german/05-back-end-development-and-apis/managing-packages-with-npm/add-keywords-to-your-package.json.md
@@ -0,0 +1,84 @@
+---
+id: 587d7fb4367417b2b2512bfd
+title: Füge deiner package.json Schlüsselwörter hinzu
+challengeType: 2
+forumTopicId: 301526
+dashedName: add-keywords-to-your-package-json
+---
+
+# --description--
+
+Im Feld `keywords` kannst du dein Projekt mit passenden Schlüsselwörtern beschreiben. Hier ist ein Beispiel:
+
+```json
+"keywords": [ "descriptive", "related", "words" ],
+```
+
+Wie du sehen kannst, besteht dieses Feld aus einem Array mit, in Anführungszeichen gesetzten, Strings.
+
+# --instructions--
+
+Füge dem Feld `keywords` in der package.json-Datei deines Projekts einen Array mit geeigneten Strings hinzu.
+
+Eines der Schlüsselwörter sollte "freecodecamp" sein.
+
+# --hints--
+
+package.json sollte einen gültigen "keywords"-Schlüssel haben
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert(packJson.keywords, '"keywords" is missing');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+"keywords"-Feld sollte ein Array sein
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert.isArray(packJson.keywords, '"keywords" is not an array');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+"keywords" sollte "freecodecamp" enthalten
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert.include(
+ packJson.keywords,
+ 'freecodecamp',
+ '"keywords" does not include "freecodecamp"'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/06-quality-assurance/advanced-node-and-express/announce-new-users.md b/curriculum/challenges/german/06-quality-assurance/advanced-node-and-express/announce-new-users.md
new file mode 100644
index 00000000000..dd11acdb4b1
--- /dev/null
+++ b/curriculum/challenges/german/06-quality-assurance/advanced-node-and-express/announce-new-users.md
@@ -0,0 +1,90 @@
+---
+id: 589fc832f9fc0f352b528e78
+title: Ankündigung Neuer Nutzer
+challengeType: 2
+forumTopicId: 301546
+dashedName: announce-new-users
+---
+
+# --description--
+
+Viele Chaträume sind in der Lage, zu erkennen, wann ein Benutzer eine Verbindung herstellt oder unterbricht – dies wird dann allen verbundenen Nutzern im Chat angezeigt. Da du bereits ein Ereignis beim Verbinden und Trennen emittierst, musst du nur dieses Ereignis ändern, um eine solche Funktion zu implementieren. Der logischste Weg, dies zu tun, besteht darin, dreierlei Daten mit dem Ereignis zu übertragen: den Namen des Benutzers, der die Verbindung hergestellt/getrennt hat, die aktuelle Anzahl der Benutzer und ob dieser Name verbunden oder getrennt wurde.
+
+Ändere den Ereignisnamen zu `'user'` und übergebe diesem ein Objekt mit den Feldern 'name', 'currentUser' und 'connected' (`true` bei Verbindung oder `false` bei Verbindungsabbruch des Nutzers). Verändere die Werte beider 'user count'-Ereignisse – das 'disconnect'-Ereignis sollte statt `true`, wie bei dem Event, das bei Verbindungsherstellung übermittelt wird, `false` aussenden.
+
+```js
+io.emit('user', {
+ name: socket.request.user.name,
+ currentUsers,
+ connected: true
+});
+```
+
+Jetzt verfügt der Client über alle notwendigen Informationen, um die aktuelle Benutzerzahl korrekt anzuzeigen und zu melden, wenn ein Benutzer sich verbindet oder die Verbindung trennt! Um dieses Ereignis auf der Client-Seite zu verarbeiten, sollten wir auf `'user'` warten, dann die aktuelle Benutzerzahl aktualisieren – indem wir jQuery verwenden, um den Text von `#num-users` auf `'{NUMBER} users online'` zu setzen –, sowie `` an die ungeordnete Liste der id `messages` mit `'{NAME} has {joined/left} the chat.'` anzuhängen.
+
+Eine Umsetzung könnte wie folgt aussehen:
+
+```js
+socket.on('user', data => {
+ $('#num-users').text(data.currentUsers + ' users online');
+ let message =
+ data.name +
+ (data.connected ? ' has joined the chat.' : ' has left the chat.');
+ $('#messages').append($(' ').html('' + message + ' '));
+});
+```
+
+Schicke deine Seite ab, wenn du davon ausgehst, alles richtig gemacht zu haben. Wenn du Fehler erhälst, kannst du dir das Projekt bis zu diesem Zeitpunkt ansehen.
+
+# --hints--
+
+Ereignis `'user'` sollte mit "name", "currentUsers" und "connected" emittiert werden.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/server.js').then(
+ (data) => {
+ assert.match(
+ data,
+ /io.emit.*('|")user\1.*name.*currentUsers.*connected/gis,
+ 'You should have an event emitted named user sending name, currentUsers, and connected'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.statusText);
+ }
+ );
+```
+
+Der Client sollte die neuen Daten des Ereignisses `'user'` richtig verarbeiten und anzeigen.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/public/client.js').then(
+ (data) => {
+ assert.match(
+ data,
+ /socket.on.*('|")user\1[^]*num-users/gi,
+ 'You should change the text of "#num-users" within on your client within the "user" event listener to show the current users connected'
+ );
+ assert.match(
+ data,
+ /socket.on.*('|")user\1[^]*messages.*li/gi,
+ 'You should append a list item to "#messages" on your client within the "user" event listener to announce a user came or went'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.statusText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-an-api-response-using-chai-http-iii---put-method.md b/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-an-api-response-using-chai-http-iii---put-method.md
new file mode 100644
index 00000000000..c34459fd7fb
--- /dev/null
+++ b/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-an-api-response-using-chai-http-iii---put-method.md
@@ -0,0 +1,148 @@
+---
+id: 587d824f367417b2b2512c5a
+title: Funktionstests für eine API-Antwort mit Chai-HTTP III - PUT-Methode durchführen
+challengeType: 2
+forumTopicId: 301590
+dashedName: run-functional-tests-on-an-api-response-using-chai-http-iii---put-method
+---
+
+# --description--
+
+Zur Erinnerung: Dieses Projekt baut auf dem folgenden Startprojekt auf, welches entweder auf Replit gefunden oder durch GitHub geklont werden kann.
+
+Wenn du eine `PUT`-Anfrage testest, sendest du oft auch Daten mit. Die Daten, die du in deine `PUT`-Anfrage einfügst, nennt man den Body der Anfrage.
+
+Um eine `PUT` Anfrage und ein JSON Objekt an den `'/travellers'` Endpunkt zu senden, kannst du `chai-http` Plugins `put` und `send` Methoden verwenden:
+
+```js
+chai
+ .request(server)
+ .put('/travellers')
+ .send({
+ "surname": [last name of a traveller of the past]
+ })
+ ...
+```
+
+Und der Pfad antwortet mit:
+
+```json
+{
+ "name": [first name],
+ "surname": [last name],
+ "dates": [birth - death years]
+}
+```
+
+Sieh dir den Servercode für die verschiedenen Antworten auf den Endpunkt `'/travellers'` an.
+
+# --instructions--
+
+Innerhalb `tests/2_functional-tests.js`, ändere den `'Send {surname: "Colombo"}'` Test (`// #3`) und verwende die `put` und `send` Methoden, um den `'/travellers'` Endpunkt zu testen.
+
+Sende das folgende JSON-Objekt mit deiner PUT-Anfrage:
+
+```json
+{
+ "surname": "Colombo"
+}
+```
+
+Überprüfe, ob der `request.end` Callback Folgendes enthält:
+
+1. Der `status` sollte `200` betragen
+2. Der `type` sollte `application/json` sein
+3. Der `body.name` sollte `Cristoforo` sein
+4. Der `body.surname` sollte `Colombo` sein
+
+Folge der obigen Behauptungsreihenfolge - wir benötigen sie. Stelle außerdem sicher, dass du `assert.fail()` nach der Fertigstellung entfernst.
+
+# --hints--
+
+Alle Tests sollten bestanden werden.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest testen ob der `res.status` 200 beträgt.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'equal');
+ assert.equal(data.assertions[0].args[0], 'res.status');
+ assert.equal(data.assertions[0].args[1], '200');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest testen ob `res.type` Folgendes ist: `'application/json'`.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'equal');
+ assert.equal(data.assertions[1].args[0], 'res.type');
+ assert.match(data.assertions[1].args[1], /('|")application\/json\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest testen ob `res.body.name` Folgendes ist: `'Cristoforo'`.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'equal');
+ assert.equal(data.assertions[2].args[0], 'res.body.name');
+ assert.match(data.assertions[2].args[1], /('|")Cristoforo\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest testen ob `res.body.surname` Folgendes ist: `'Colombo'`.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[3].method, 'equal');
+ assert.equal(data.assertions[3].args[0], 'res.body.surname');
+ assert.match(data.assertions[3].args[1], /('|")Colombo\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-api-endpoints-using-chai-http.md b/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-api-endpoints-using-chai-http.md
new file mode 100644
index 00000000000..2f18d83fc18
--- /dev/null
+++ b/curriculum/challenges/german/06-quality-assurance/quality-assurance-and-testing-with-chai/run-functional-tests-on-api-endpoints-using-chai-http.md
@@ -0,0 +1,100 @@
+---
+id: 587d824e367417b2b2512c58
+title: Funktionstests auf API-Endpunkten unter Verwendung von Chai-HTTP durchführen
+challengeType: 2
+forumTopicId: 301593
+dashedName: run-functional-tests-on-api-endpoints-using-chai-http
+---
+
+# --description--
+
+Zur Erinnerung: Dieses Projekt baut auf dem folgenden Startprojekt auf, welches entweder auf Replit gefunden oder durch GitHub geklont werden kann.
+
+Mocha erlaubt es, asynchrone Vorgänge wie Aufrufe an API-Endpunkte mit einem Plugin namens `chai-http` zu testen.
+
+Das Folgende ist ein Beispiel für einen Test, in dem `chai-http` für eine Suite namens `'GET /hello?name=[name] => "hello [name]"'` verwendet wird:
+
+```js
+suite('GET /hello?name=[name] => "hello [name]"', function () {
+ test('?name=John', function (done) {
+ chai
+ .request(server)
+ .get('/hello?name=John')
+ .end(function (err, res) {
+ assert.equal(res.status, 200, 'Response status should be 200');
+ assert.equal(res.text, 'hello John', 'Response should be "hello John"');
+ done();
+ });
+ });
+});
+```
+
+Der Test sendet eine `GET` Anfrage an den Server mit einem URL Query String als Namen (`?name=John`). In der Callback Funktion der `end` Methode, wird das Antwortobjekt (`res`) empfangen und enthält die `status` Eigenschaft.
+
+Die erste `assert.equal` überprüft ob der Status gleich `200` ist. Die zweite `assert.equal` prüft, dass der Antwort-String (`res.text`) gleich `"hello John"` ist.
+
+Beachte auch den Parameter `done` in der Callback-Funktion des Tests. Der Aufruf ohne Argument am Ende eines Tests ist notwendig, um zu signalisieren, dass der asynchrone Vorgang abgeschlossen ist.
+
+# --instructions--
+
+Innerhalb von `tests/2_functional-tests.js`, ändere `'Test GET /hello with no name'` Test (`// #1`) um den `status` und den `text` der Antwort geltend zu machen, um en Test zu bestehen. Die an die Asserts übergebenen Argumente dürfen nicht verändert werden.
+
+Es sollte keine URL Query geben. Ohne eine Namens-URL-Abfrage antwortet der Endpunkt mit `hello Guest`.
+
+# --hints--
+
+Alle Tests sollten bestanden werden
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest auf `res.status` == 200 testen
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'equal');
+ assert.equal(data.assertions[0].args[0], 'res.status');
+ assert.equal(data.assertions[0].args[1], '200');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+Du solltest auf `res.text` == `'hello Guest'` testen
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'equal');
+ assert.equal(data.assertions[1].args[0], 'res.text');
+ assert.match(data.assertions[1].args[1], /('|")hello Guest\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md
new file mode 100644
index 00000000000..8f35ed0b14a
--- /dev/null
+++ b/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md
@@ -0,0 +1,369 @@
+---
+id: 587d8249367417b2b2512c42
+title: Issue-Tracker
+challengeType: 4
+forumTopicId: 301569
+dashedName: issue-tracker
+---
+
+# --description--
+
+Erstelle eine vollständige JavaScript-Anwendung, die eine ähnliche Funktionalität wie https://issue-tracker.freecodecamp.rocks/ aufweist. Bei der Arbeit an diesem Projekt schreibst du deinen Code mit einer der folgenden Methoden:
+
+- Klone dieses GitHub Repo und schließe dein Projekt lokal ab.
+- Benutze dieses Replit Starterprojekt um dein Projekt zu vervollständigen.
+- Verwende einen Site-Builder deiner Wahl, um das Projekt fertigzustellen. Achte darauf, alle Dateien aus unserem GitHub Repo zu integrieren.
+
+Wenn du fertig bist, stelle sicher, dass dein Projekt öffentlich zugänglich gehostet ist. Gib dann die URL in das `Solution Link`-Feld ein. Füge optional einen Link zum Quellcode deines Projekts in das `GitHub Link`-Feld ein.
+
+# --instructions--
+
+- Erfülle die notwendigen Pfade in `/routes/api.js`
+- Erstelle alle funktionalen Tests in `tests/2_functional-tests.js`
+- Kopiere die `sample.env` Datei in `.env` und setze Sie die Variablen entsprechend
+- Entkommentiere `NODE_ENV=test` in deiner `.env` Datei, um die Tests durchzuführen
+- Verwende den Befehl `npm run test`, um die Tests in deiner Konsole durchzuführen. Drücke Strg+Umschalt+P (Cmd auf Mac) und gib "open shell" ein, um die Replit-Konsole zu öffnen
+
+Schreibe die folgenden Tests in `tests/2_functional-tests.js`:
+
+- Erstelle ein Issue mit jedem Feld: POST-Request an `/api/issues/{project}`
+- Erstelle ein Issue mit nur erforderlichen Feldern: POST-Request an `/api/issues/{project}`
+- Erstelle ein Issue mit erforderlichen fehlenden Feldern: POST-Request an `/api/issues/{project}`
+- Issues in einem Projekt anzeigen: GET-Request an `/api/issues/{project}`
+- Anzeigen von Issues in einem Projekt mit einem Filter: GET-Request an `/api/issues/{project}`
+- Anzeigen von Issues in einem Projekt mit mehreren Filtern: GET-Request an `/api/issues/{project}`
+- Ein Feld für ein Issue aktualisieren: PUT-Request an `/api/issues/{project}`
+- Aktualisiere mehrere Felder mit einem Issue: PUT-Request an `/api/issues/{project}`
+- Aktualisiere ein Problem mit fehlender `_id`: PUT-Request an `/api/issues/{project}`
+- Aktualisieren eines Issues ohne zu aktualisierende Felder: PUT-Request an `/api/issues/{project}`
+- Aktualisieren eines Issues mit einer ungültigen `_id`: PUT-Request an `/api/issues/{project}`
+- Löschen eines Issues: DELETE-Request an `/api/issues/{project}`
+- Löschen eines Issues mit einer ungültigen `_id`: DELETE-Request an `/api/issues/{project}`
+- Löschen eines Issues mit fehlender `_id`: DELETE-Request an `/api/issues/{project}`
+
+# --hints--
+
+Du kannst dein eigenes Projekt angeben, nicht die Beispiel-URL.
+
+```js
+(getUserInput) => {
+ assert(!/.*\/issue-tracker\.freecodecamp\.rocks/.test(getUserInput('url')));
+};
+```
+
+Du kannst eine `POST`-Anfrage mit Formulardaten an `/api/issues/{projectname}` senden. Die Formulardaten benötigen die Felder `issue_title`, `issue_text`, `created_by`, und optional `assigned_to` sowie `status_text`.
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'Faux Issue Title',
+ issue_text: 'Functional Test - Required Fields Only',
+ created_by: 'fCC'
+ };
+ const data = await $.post(
+ getUserInput('url') + '/api/issues/fcc-project',
+ test_data
+ );
+ assert.isObject(data);
+ assert.nestedInclude(data, test_data);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Die `POST`-Anfrage an `/api/issues/{projectname}` gibt das erstellte Objekt zurück und muss alle übermittelten Felder enthalten. Ausgeschlossene optionale Felder werden als leere Strings zurückgegeben. Inkludiere zusätzlich `created_on` (Datum/Uhrzeit), `updated_on` (Datum/Uhrzeit), `open` (Boolean, `true` für offen - Standardwert, `false` für geschlossen), sowie `_id`.
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'Faux Issue Title 2',
+ issue_text: 'Functional Test - Every field filled in',
+ created_by: 'fCC',
+ assigned_to: 'Chai and Mocha'
+ };
+ const data = await $.post(
+ getUserInput('url') + '/api/issues/fcc-project',
+ test_data
+ );
+ assert.isObject(data);
+ assert.nestedInclude(data, test_data);
+ assert.property(data, 'created_on');
+ assert.isNumber(Date.parse(data.created_on));
+ assert.property(data, 'updated_on');
+ assert.isNumber(Date.parse(data.updated_on));
+ assert.property(data, 'open');
+ assert.isBoolean(data.open);
+ assert.isTrue(data.open);
+ assert.property(data, '_id');
+ assert.isNotEmpty(data._id);
+ assert.property(data, 'status_text');
+ assert.isEmpty(data.status_text);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Wenn du eine `POST`-Anfrage ohne die erforderlichen Felder an `/api/issues/{projectname}` schickst, wird ein Fehler `{ error: 'required field(s) missing' }` zurückgegeben
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = { created_by: 'fCC' };
+ const data = await $.post(getUserInput('url') + '/api/issues/fcc-project', {
+ created_by: 'fCC'
+ });
+ assert.isObject(data);
+ assert.property(data, 'error');
+ assert.equal(data.error, 'required field(s) missing');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Du kannst eine `GET`-Anfrage an `/api/issues/{projectname}` für einen Array aller Issues des spezifischen `projectname` senden, wobei alle Felder für jedes Issue vorhanden sind.
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = { issue_text: 'Get Issues Test', created_by: 'fCC' };
+ const url =
+ getUserInput('url') +
+ '/api/issues/get_issues_test_' +
+ Date.now().toString().substring(7);
+ const data1 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 1' })
+ );
+ assert.isObject(data1);
+ const data2 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 2' })
+ );
+ assert.isObject(data2);
+ const data3 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 3' })
+ );
+ assert.isObject(data3);
+ const getIssues = await $.get(url);
+ assert.isArray(getIssues);
+ assert.lengthOf(getIssues, 3);
+ let re = new RegExp('Faux Issue \\d');
+ getIssues.forEach((issue) => {
+ assert.property(issue, 'issue_title');
+ assert.match(issue.issue_title, re);
+ assert.property(issue, 'issue_text');
+ assert.property(issue, 'created_by');
+ assert.property(issue, 'assigned_to');
+ assert.property(issue, 'status_text');
+ assert.property(issue, 'open');
+ assert.property(issue, 'created_on');
+ assert.property(issue, 'updated_on');
+ assert.property(issue, '_id');
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Du kannst eine `GET`-Anfrage an `/api/issues/{projectname}` senden und die Anfrage filtern, indem du jedes Feld und Wert als Query (ie. `/api/issues/{project}?open=false`) übergibst. Du kannst ein oder mehrere Feld/Wert-Paare auf einmal übergeben.
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'To be Filtered',
+ issue_text: 'Filter Issues Test'
+ };
+ const url =
+ getUserInput('url') +
+ '/api/issues/get_issues_test_' +
+ Date.now().toString().substring(7);
+ const data1 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
+ );
+ const data2 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
+ );
+ const data3 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Eric' })
+ );
+ const data4 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Carol', assigned_to: 'Eric' })
+ );
+ const getSingle = await $.get(url + '?created_by=Alice');
+ assert.isArray(getSingle);
+ assert.lengthOf(getSingle, 3);
+ const getMultiple = await $.get(url + '?created_by=Alice&assigned_to=Bob');
+ assert.isArray(getMultiple);
+ assert.lengthOf(getMultiple, 2);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Du kannst einen `PUT`-Request an `/api/issues/{projectname}` mit einer `_id` und mindestens einem Feld zum updaten schicken. Bei Erfolg, sollte das Feld `updated_on` aktualisiert werden und `{ result: 'successfully updated', '_id': _id }` zurückgeben.
+
+```js
+async (getUserInput) => {
+ try {
+ let initialData = {
+ issue_title: 'Issue to be Updated',
+ issue_text: 'Functional Test - Put target',
+ created_by: 'fCC'
+ };
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const itemToUpdate = await $.post(url, initialData);
+ const updateSucccess = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' }
+ });
+ assert.isObject(updateSucccess);
+ assert.deepEqual(updateSucccess, {
+ result: 'successfully updated',
+ _id: itemToUpdate._id
+ });
+ const getUpdatedId = await $.get(url + '?_id=' + itemToUpdate._id);
+ assert.isArray(getUpdatedId);
+ assert.isObject(getUpdatedId[0]);
+ assert.isAbove(
+ Date.parse(getUpdatedId[0].updated_on),
+ Date.parse(getUpdatedId[0].created_on)
+ );
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Wenn die `PUT`-Anfrage, die an `/api/issues/{projectname}` übermittelt wird, keine `_id` enthält, beträgt der Rückgabewert `{ error: 'missing _id' }`.
+
+```js
+async (getUserInput) => {
+ try {
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const badUpdate = await $.ajax({ url: url, type: 'PUT' });
+ assert.isObject(badUpdate);
+ assert.property(badUpdate, 'error');
+ assert.equal(badUpdate.error, 'missing _id');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Wenn die `PUT`-Anfrage, die an `/api/issues/{projectname}` übermittelt wird, keine Aktualisierungsfelder enthält, beträgt der Rückgabewert `{ error: 'no update field(s) sent', '_id': _id }`. Bei jedem anderen Fehler ist der Rückgabewert `{ error: 'could not update', '_id': _id }`.
+
+```js
+async (getUserInput) => {
+ try {
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const badUpdate = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: '5f665eb46e296f6b9b6a504d' }
+ });
+ assert.deepEqual(badUpdate, {
+ error: 'no update field(s) sent',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ const badIdUpdate = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
+ });
+ assert.deepEqual(badIdUpdate, {
+ error: 'could not update',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Du kannst eine `DELETE`-Anfrage an `/api/issues/{projectname}` mit der `_id` übermitteln, um ein Issue zu löschen. Wenn keine `_id` übermittelt wird, ist der Rückgabewert `{ error: 'missing _id' }`. Bei Erfolg ist der Rückgabewert `{ result: 'successfully deleted', '_id': _id }`. Bei einem Fehler ist der Rückgabewert `{ error: 'could not delete', '_id': _id }`.
+
+```js
+async (getUserInput) => {
+ try {
+ let initialData = {
+ issue_title: 'Issue to be Deleted',
+ issue_text: 'Functional Test - Delete target',
+ created_by: 'fCC'
+ };
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const itemToDelete = await $.post(url, initialData);
+ assert.isObject(itemToDelete);
+ const deleteSuccess = await $.ajax({
+ url: url,
+ type: 'DELETE',
+ data: { _id: itemToDelete._id }
+ });
+ assert.isObject(deleteSuccess);
+ assert.deepEqual(deleteSuccess, {
+ result: 'successfully deleted',
+ _id: itemToDelete._id
+ });
+ const noId = await $.ajax({ url: url, type: 'DELETE' });
+ assert.isObject(noId);
+ assert.deepEqual(noId, { error: 'missing _id' });
+ const badIdDelete = await $.ajax({
+ url: url,
+ type: 'DELETE',
+ data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
+ });
+ assert.isObject(badIdDelete);
+ assert.deepEqual(badIdDelete, {
+ error: 'could not delete',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+Alle 14 Funktionstests sind abgeschlossen und bestanden.
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ assert.isAtLeast(getTests.length, 14, 'At least 14 tests passed');
+ getTests.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Test in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md
new file mode 100644
index 00000000000..9100d2d2f86
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md
@@ -0,0 +1,71 @@
+---
+id: 5e7b9f060b6c005b0e76f05b
+title: Erstelle deine eigenen Funktionen
+challengeType: 11
+videoId: nLDychdBwUg
+bilibiliIds:
+ aid: 249487483
+ bvid: BV1Fv411J7bS
+ cid: 376340281
+dashedName: build-your-own-functions
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Was wird das folgende Python-Programm ausgeben?:
+
+```python
+def fred():
+ print("Zap")
+def jane():
+ print("ABC")
+
+jane()
+fred()
+jane()
+```
+
+## --answers--
+
+Zap
+ABC
+jane
+fred
+jane
+
+---
+
+Zap
+ABC
+Zap
+
+---
+
+ABC
+Zap
+jane
+
+---
+
+ABC
+Zap
+ABC
+
+---
+
+Zap
+Zap
+Zap
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md
new file mode 100644
index 00000000000..f13c66be674
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md
@@ -0,0 +1,61 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06d
+title: Tupel vergleichen und sortieren
+challengeType: 11
+videoId: dZXzBXUxxCs
+bilibiliIds:
+ aid: 931886163
+ bvid: BV1HM4y1T7TK
+ cid: 376533673
+dashedName: comparing-and-sorting-tuples
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Was macht das gleiche wie der folgende Code?:
+
+```python
+lst = []
+for key, val in counts.items():
+ newtup = (val, key)
+ lst.append(newtup)
+lst = sorted(lst, reverse=True)
+print(lst)
+```
+
+## --answers--
+
+```python
+print( sorted( [ (v,k) for k,v in counts.items() ], reverse=True ) )
+```
+
+---
+
+```python
+print( [ (k,v) for k,v in counts.items().sorted() ] )
+```
+
+---
+
+```python
+print( sorted( [ (v,k) for k,v in counts.keys() ] ) )
+```
+
+---
+
+```python
+print( [ (k,v) for k,v in counts.values().sort() ] )
+```
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md
new file mode 100644
index 00000000000..774dbba3831
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md
@@ -0,0 +1,54 @@
+---
+id: 5e7b9f050b6c005b0e76f058
+title: Bedingte Anweisung
+challengeType: 11
+videoId: gz_IfIsZQtc
+bilibiliIds:
+ aid: 206949935
+ bvid: BV1Jh411z7bY
+ cid: 376337035
+dashedName: conditional-execution
+---
+
+# --question--
+
+## --text--
+
+Welcher Code ist korrekt eingerückt, um "Ja" zu drucken, wenn x = 0 und y = 10 ist?
+
+## --answers--
+
+```python
+if 0 == x:
+if y == 10:
+print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+ if y == 10:
+ print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+if y == 10:
+ print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+ if y == 10:
+ print("Yes")
+```
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md
new file mode 100644
index 00000000000..ebd44ddae40
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md
@@ -0,0 +1,56 @@
+---
+id: 5e7b9f6a0b6c005b0e76f097
+title: 'Datenvisualisierung: Mailinglisten'
+challengeType: 11
+videoId: RYdW660KkaQ
+bilibiliIds:
+ aid: 334465586
+ bvid: BV18w411R7dD
+ cid: 377545473
+dashedName: data-visualization-mailing-lists
+---
+
+# --description--
+
+Siehe auch:
+
+\- Aufgabe: Geodata
+
+\- Aufgabe: Gmane Model
+
+\- Aufgabe: Gmane Spider
+
+\- Aufgabe: Gmane Viz
+
+\- Aufgabe: Page Rank
+
+\- Aufgabe: Page Spider
+
+\- Aufgabe: Page Viz
+
+# --question--
+
+## --text--
+
+Welches ist eine gängige JavaScript-Visualisierungsbibliothek?
+
+## --answers--
+
+DataViz.js
+
+---
+
+D3
+
+---
+
+Lowcharts
+
+---
+
+DATA6
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md
new file mode 100644
index 00000000000..0e4ddb38b9c
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f6a0b6c005b0e76f096
+title: 'Datenvisualisierung: Seiten-Ranking'
+challengeType: 11
+videoId: 6-w_qIUwaxU
+bilibiliIds:
+ aid: 376950472
+ bvid: BV1ho4y1Q72u
+ cid: 377544599
+dashedName: data-visualization-page-rank
+---
+
+# --question--
+
+## --text--
+
+Wie funktioniert der Seiten-Ranking Algorithmus?
+
+## --answers--
+
+Er bestimmt, welche Seiten am meisten verlinkt sind.
+
+---
+
+Er bewertet Seiten basierend auf der Anzahl von Besuchern der Seiten.
+
+---
+
+Er ermittelt, welche Seiten die wichtigsten Inhalte enthalten.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md
new file mode 100644
index 00000000000..cf3ad252435
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md
@@ -0,0 +1,53 @@
+---
+id: 5e7b9f0a0b6c005b0e76f069
+title: Wörterbücher und Schleifen
+challengeType: 11
+videoId: EEmekKiKG70
+bilibiliIds:
+ aid: 589401038
+ bvid: BV1eq4y1X7xU
+ cid: 376387132
+dashedName: dictionaries-and-loops
+---
+
+# --description--
+
+Siehe auch:
+
+\- Übung
+
+# --question--
+
+## --text--
+
+Welches Ergebnis liefert folgender Code?
+
+```python
+counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
+for key in counts:
+ if counts[key] > 10:
+ print(key, counts[key])
+```
+
+## --answers--
+
+annie 42
+jan 100
+
+---
+
+chuck 1
+annie 42
+jan 100
+
+---
+
+chuck 1
+
+---
+
+[Error]
+
+## --video-solution--
+
+1
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md
new file mode 100644
index 00000000000..4b0f2515c2e
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md
@@ -0,0 +1,47 @@
+---
+id: 5e7b9f090b6c005b0e76f068
+title: 'Wörterbücher: Häufige Anwendungen'
+challengeType: 11
+videoId: f17xPfIXct0
+bilibiliIds:
+ aid: 805747023
+ bvid: BV1v34y1D7ug
+ cid: 414168867
+dashedName: dictionaries-common-applications
+---
+
+# --question--
+
+## --text--
+
+Welches Ergebnis liefert folgender Code?
+
+```python
+counts = { 'quincy' : 1 , 'mrugesh' : 42, 'beau': 100, '0': 10}
+print(counts.get('kris', 0))
+```
+
+## --answers--
+
+2
+
+---
+
+quincy
+
+---
+
+0
+
+---
+
+10
+
+---
+
+[will return error]
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md
new file mode 100644
index 00000000000..452d693f15c
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md
@@ -0,0 +1,44 @@
+---
+id: 5e7b9f080b6c005b0e76f063
+title: Dateien als Sequenz
+challengeType: 11
+videoId: cIA0EokbaHE
+bilibiliIds:
+ aid: 974380307
+ bvid: BV1p44y1m7br
+ cid: 376388846
+dashedName: files-as-a-sequence
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Was ist der Effekt des Wortes 'continue' mitten in einer Schleife?
+
+## --answers--
+
+Springt nach der Schleife direkt zum Code.
+
+---
+
+Springt zur nächsten Zeile im Code.
+
+---
+
+Springt zur nächsten Iteration der Schleife.
+
+---
+
+Überspringt den nächsten Block des Codes.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md
new file mode 100644
index 00000000000..74492c6ed71
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md
@@ -0,0 +1,56 @@
+---
+id: 5e7b9f050b6c005b0e76f057
+title: Fortgeschrittene Ausdrücke
+challengeType: 11
+videoId: dKgUaIa5ATg
+bilibiliIds:
+ aid: 334428894
+ bvid: BV1uw411R7gH
+ cid: 376318468
+dashedName: intermediate-expressions
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe 1
+
+\- Aufgabe 2
+
+# --question--
+
+## --text--
+
+Was wird nach der Ausführung dieses Codes ausgegeben:
+
+```python
+width = 15
+height = 12.0
+print(height/3)
+```
+
+## --answers--
+
+39
+
+---
+
+4
+
+---
+
+4.0
+
+---
+
+5.0
+
+---
+
+5
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md
new file mode 100644
index 00000000000..c9146dc7c34
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md
@@ -0,0 +1,53 @@
+---
+id: 5e7b9f070b6c005b0e76f061
+title: Fortgeschrittene Strings
+challengeType: 11
+videoId: KgT_fYLXnyk
+bilibiliIds:
+ aid: 291983121
+ bvid: BV1Zf4y157yG
+ cid: 376394116
+dashedName: intermediate-strings
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Wie lautet der Wert von i in dem folgenden Code?
+
+```python
+word = "bananana"
+i = word.find("na")
+```
+
+## --answers--
+
+nanana
+
+---
+
+2
+
+---
+
+3
+
+---
+
+True
+
+---
+
+na
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md
new file mode 100644
index 00000000000..e2f02ce34fc
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md
@@ -0,0 +1,40 @@
+---
+id: 5e6a54c358d3af90110a60a3
+title: 'Einführung: Elemente von Python'
+challengeType: 11
+videoId: aRY_xjL35v0
+bilibiliIds:
+ aid: 674420725
+ bvid: BV1MU4y1H7Lj
+ cid: 376315889
+dashedName: introduction-elements-of-python
+---
+
+# --question--
+
+## --text--
+
+Was wird das folgende Programm ausgeben:
+
+```python
+x = 43
+x = x + 1
+print(x)
+```
+
+## --answers--
+
+x
+
+---
+
+x + 1
+
+---
+
+44
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md
new file mode 100644
index 00000000000..c89faa762f9
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md
@@ -0,0 +1,34 @@
+---
+id: 5e6a54af58d3af90110a60a1
+title: 'Einführung: Hardware-Architektur'
+challengeType: 11
+videoId: H6qtjRTfSog
+bilibiliIds:
+ aid: 206977572
+ bvid: BV1zh411z7Ak
+ cid: 376199262
+dashedName: introduction-hardware-architecture
+---
+
+# --question--
+
+## --text--
+
+Wo werden dein Programme gespeichert, wenn sie ausgeführt werden?
+
+## --answers--
+
+Festplatte.
+
+---
+
+Speicher.
+
+---
+
+Prozessor (CPU).
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md
new file mode 100644
index 00000000000..d49bb3c5132
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md
@@ -0,0 +1,43 @@
+---
+id: 5e6a54ba58d3af90110a60a2
+title: 'Einführung: Python als Sprache'
+challengeType: 11
+videoId: 0QeGbZNS_bY
+bilibiliIds:
+ aid: 674404602
+ bvid: BV1GU4y1H7vB
+ cid: 376315625
+dashedName: introduction-python-as-a-language
+---
+
+# --question--
+
+## --text--
+
+Was wird nach dem Ausführen dieser beiden Codezeilen ausgegeben:
+
+```python
+x = 6
+print(x)
+```
+
+## --answers--
+
+x
+
+---
+
+6
+
+---
+
+x = 6
+
+---
+
+(x)
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md
new file mode 100644
index 00000000000..dfec7aa3a5b
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md
@@ -0,0 +1,46 @@
+---
+id: 5e6a54a558d3af90110a60a0
+title: 'Einleitung: Warum programmieren?'
+challengeType: 11
+videoId: 3muQV-Im3Z0
+bilibiliIds:
+ aid: 206882253
+ bvid: BV1Fh411z7tr
+ cid: 376314257
+videoLocaleIds:
+ espanol: 3muQV-Im3Z0
+ italian: 3muQV-Im3Z0
+ portuguese: 3muQV-Im3Z0
+dashedName: introduction-why-program
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Installiere Python auf Windows
+
+\- Installiere Python auf MacOS
+
+# --question--
+
+## --text--
+
+Wer sollte programmieren lernen?
+
+## --answers--
+
+College Studenten.
+
+---
+
+Menschen, die Softwareentwickler werden wollen.
+
+---
+
+Jeder.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md
new file mode 100644
index 00000000000..16aff413d2f
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md
@@ -0,0 +1,43 @@
+---
+id: 5e7b9f070b6c005b0e76f05d
+title: 'Iterationen: Eindeutige Schleifen'
+challengeType: 11
+videoId: hiRTRAqNlpE
+bilibiliIds:
+ aid: 291987032
+ bvid: BV1ff4y157Q3
+ cid: 376385255
+dashedName: iterations-definite-loops
+---
+
+# --question--
+
+## --text--
+
+Wie viele Zeilen wird der folgende Code ausgeben?:
+
+```python
+for i in [2,1,5]:
+ print(i)
+```
+
+## --answers--
+
+1
+
+---
+
+2
+
+---
+
+3
+
+---
+
+5
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md
new file mode 100644
index 00000000000..7d477a758e4
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md
@@ -0,0 +1,49 @@
+---
+id: 5e7b9f070b6c005b0e76f05e
+title: 'Iterationen: Schleifen-Idiome'
+challengeType: 11
+videoId: AelGAcoMXbI
+bilibiliIds:
+ aid: 334491369
+ bvid: BV1tw411R7Mm
+ cid: 376530765
+dashedName: iterations-loop-idioms
+---
+
+# --question--
+
+## --text--
+
+Nachfolgend ist ein Code aufgeführt, der den kleinsten Wert aus einer Liste von Werten ermittelt. Eine Zeile hat einen Fehler, der dazu führt, dass der Code nicht wie erwartet funktioniert. Welche Zeile ist es?:
+
+```python
+smallest = None
+print("Before:", smallest)
+for itervar in [3, 41, 12, 9, 74, 15]:
+ if smallest is None or itervar < smallest:
+ smallest = itervar
+ break
+ print("Loop:", itervar, smallest)
+print("Smallest:", smallest)
+```
+
+## --answers--
+
+3
+
+---
+
+4
+
+---
+
+6
+
+---
+
+7
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md
new file mode 100644
index 00000000000..615803c726a
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md
@@ -0,0 +1,52 @@
+---
+id: 5e7b9f070b6c005b0e76f05f
+title: 'Iterationen: Mehr Muster'
+challengeType: 11
+videoId: 9Wtqo6vha1M
+bilibiliIds:
+ aid: 674492981
+ bvid: BV1hU4y1H7tF
+ cid: 376531204
+dashedName: iterations-more-patterns
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Welche dieser Auswertungen ist Falsch?
+
+## --answers--
+
+```python
+0 == 0.0
+```
+
+---
+
+```python
+0 is 0.0
+```
+
+---
+
+```python
+0 is not 0.0
+```
+
+---
+
+```python
+0 = 0.0
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md
new file mode 100644
index 00000000000..74245e80a9b
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md
@@ -0,0 +1,55 @@
+---
+id: 5e7b9f060b6c005b0e76f05c
+title: Schleifen und Iterationen
+challengeType: 11
+videoId: dLA-szNRnUY
+bilibiliIds:
+ aid: 674492981
+ bvid: BV1hU4y1H7tF
+ cid: 376531204
+dashedName: loops-and-iterations
+---
+
+# --question--
+
+## --text--
+
+Was wird der folgende Code ausgeben?:
+
+```python
+n = 0
+while True:
+ if n == 3:
+ break
+ print(n)
+ n = n + 1
+```
+
+## --answers--
+
+0
+1
+2
+
+---
+
+0
+1
+2
+3
+
+---
+
+1
+2
+
+---
+
+1
+2
+3
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md
new file mode 100644
index 00000000000..20c8ae862e9
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md
@@ -0,0 +1,52 @@
+---
+id: 5e7b9f170b6c005b0e76f08b
+title: Erstellen einer relationalen Datenbank
+challengeType: 11
+videoId: MQ5z4bdF92U
+bilibiliIds:
+ aid: 249380678
+ bvid: BV1vv411E76L
+ cid: 377531786
+dashedName: make-a-relational-database
+---
+
+# --question--
+
+## --text--
+
+Welchen SQL-Befehl würden Sie verwenden, um alle Benutzer mit der E-Mail-Adresse `quincy@freecodecamp.org` abzurufen?
+
+## --answers--
+
+```sql
+SELECT Users WHERE email="quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT Users WHERE email IS "quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT ALL Users WHERE email="quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT * FROM Users WHERE email IS "quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT * FROM Users WHERE email="quincy@freecodecamp.org"
+```
+
+## --video-solution--
+
+5
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md
new file mode 100644
index 00000000000..c60e18205f1
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md
@@ -0,0 +1,60 @@
+---
+id: 5e7b9f060b6c005b0e76f059
+title: Weitere Bedingungsstrukturen
+challengeType: 11
+videoId: HdL82tAZR20
+bilibiliIds:
+ aid: 631930118
+ bvid: BV1Nb4y1r7z2
+ cid: 376337449
+dashedName: more-conditional-structures
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe 1
+
+\- Aufgabe 2
+
+# --question--
+
+## --text--
+
+Gegeben ist der folgende Code:
+
+```python
+temp = "5 degrees"
+cel = 0
+fahr = float(temp)
+cel = (fahr - 32.0) * 5.0 / 9.0
+print(cel)
+```
+
+Welche Zeile(n) soll(en) von einem `try`-Block umgeben sein?
+
+## --answers--
+
+1
+
+---
+
+3
+
+---
+
+3,4
+
+---
+
+4
+
+---
+
+Keine
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md
new file mode 100644
index 00000000000..ae3db534b33
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f0c0b6c005b0e76f072
+title: Netzwerkprotokoll
+challengeType: 11
+videoId: c6vZGescaSc
+bilibiliIds:
+ aid: 931950996
+ bvid: BV1cM4y1N7K6
+ cid: 376388317
+dashedName: networking-protocol
+---
+
+# --question--
+
+## --text--
+
+Welche Art von HTTP-Anfrage wird normalerweise für den Zugriff auf eine Website verwendet?
+
+## --answers--
+
+POST
+
+---
+
+GET
+
+---
+
+WEB
+
+---
+
+ACCESS
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md
new file mode 100644
index 00000000000..fbe3a32b9da
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f0c0b6c005b0e76f074
+title: 'Vernetzungen: Textverarbeitung'
+challengeType: 11
+videoId: Pv_pJgVu8WI
+bilibiliIds:
+ aid: 804442498
+ bvid: BV16y4y1j7WW
+ cid: 377329124
+dashedName: networking-text-processing
+---
+
+# --question--
+
+## --text--
+
+Welche Art der Kodierung verwenden die meisten Websites?
+
+## --answers--
+
+UTF-8
+
+---
+
+UTF-16
+
+---
+
+UTF-32
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md
new file mode 100644
index 00000000000..5e8ed996ce9
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md
@@ -0,0 +1,41 @@
+---
+id: 5e7b9f0d0b6c005b0e76f075
+title: 'Vernetzungen: Verwendung von urllib in Python'
+challengeType: 11
+videoId: 7lFM1T_CxBs
+bilibiliIds:
+ aid: 546908270
+ bvid: BV1Xq4y1H7e6
+ cid: 377331524
+dashedName: networking-using-urllib-in-python
+---
+
+# --question--
+
+## --text--
+
+Was wird die Ausgabe des folgenden Codes sein?:
+
+```python
+import urllib.request
+fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')
+for line in fhand:
+ print(line.decode().strip())
+```
+
+## --answers--
+
+Nur der Inhalt von "romeo.txt".
+
+---
+
+Ein Header und der Inhalt von "romeo.txt".
+
+---
+
+Ein Header, eine Fußzeile und der Inhalt von "romeo.txt".
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md
new file mode 100644
index 00000000000..488337dfab6
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md
@@ -0,0 +1,60 @@
+---
+id: 5e7b9f0d0b6c005b0e76f076
+title: 'Vernetzung: Web Scraping mit Python'
+challengeType: 11
+videoId: Uyioq2q4cEg
+bilibiliIds:
+ aid: 674382625
+ bvid: BV1oU4y1n7zQ
+ cid: 377331774
+dashedName: networking-web-scraping-with-python
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe: socket1
+
+\- Aufgabe: urllib
+
+\- Aufgabe: urllinks
+
+# --question--
+
+## --text--
+
+Welche Python-Bibliothek wird zum Parsen von HTML-Dokumenten und zum Extrahieren von Daten aus HTML-Dokumenten verwendet?
+
+## --answers--
+
+socket
+
+---
+
+http
+
+---
+
+BeautifulSoup
+
+---
+
+PrettyBiscuit
+
+---
+
+WonderfulSalad
+
+---
+
+HttpParser
+
+---
+
+GrunkleStan
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md
new file mode 100644
index 00000000000..f59ef4055fe
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f0c0b6c005b0e76f071
+title: Vernetzung mit Python
+challengeType: 11
+videoId: _kJvneKVdNM
+bilibiliIds:
+ aid: 419494612
+ bvid: BV1r341167jT
+ cid: 376385858
+dashedName: networking-with-python
+---
+
+# --question--
+
+## --text--
+
+Welche Python-Bibliothek ermöglicht den Zugriff auf TCP-Sockets?
+
+## --answers--
+
+tcp
+
+---
+
+socket
+
+---
+
+http
+
+---
+
+port
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md
new file mode 100644
index 00000000000..b1f23683404
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md
@@ -0,0 +1,54 @@
+---
+id: 5e7b9f0c0b6c005b0e76f073
+title: 'Vernetzung: Programmieren eines Web-Browsers'
+challengeType: 11
+videoId: zjyT9DaAjx4
+bilibiliIds:
+ aid: 761908574
+ bvid: BV1j64y1x7wx
+ cid: 377319579
+dashedName: networking-write-a-web-browser
+---
+
+# --question--
+
+## --text--
+
+Was erstellt der folgende Code?:
+
+```py
+import socket
+
+mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+mysock.connect(('data.pr4e.org', 80))
+cmd = 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\r\n\r\n'.encode()
+mysock.send(cmd)
+
+while True:
+ data = mysock.recv(512)
+ if len(data) < 1:
+ break
+ print(data.decode(),end='')
+mysock.close()
+```
+
+## --answers--
+
+Einen einfachen Webserver.
+
+---
+
+Einen einfachen E-Mail-Client.
+
+---
+
+Eine einfache ToDo-Liste.
+
+---
+
+Einen einfachen Webbrowser.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md
new file mode 100644
index 00000000000..dadd5649665
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md
@@ -0,0 +1,71 @@
+---
+id: 5e7b9f170b6c005b0e76f087
+title: Lebenszyklus von Objekten
+challengeType: 11
+videoId: p1r3h_AMMIM
+bilibiliIds:
+ aid: 461998717
+ bvid: BV1JL411n7Hr
+ cid: 377529681
+dashedName: object-lifecycle
+---
+
+# --question--
+
+## --text--
+
+Was wird das folgende Programm ausgeben?:
+
+```python
+class PartyAnimal:
+ x = 0
+ name = ''
+ def __init__(self, nam):
+ self.name = nam
+ print(self.name,'constructed')
+ def party(self):
+ self.x = self.x + 1
+ print(self.name,'party count',self.x)
+
+q = PartyAnimal('Quincy')
+m = PartyAnimal('Miya')
+
+q.party()
+m.party()
+q.party()
+```
+
+## --answers--
+
+
+Quincy constructed
+Miya constructed
+Quincy party count 1
+Miya party count 2
+Quincy party count 3
+
+
+---
+
+
+Quincy constructed
+Miya constructed
+Quincy party count 1
+Miya party count 1
+Quincy party count 2
+
+
+---
+
+
+Quincy constructed
+Quincy party count 1
+Quincy party count 2
+Miya constructed
+Miya party count 1
+
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md
new file mode 100644
index 00000000000..286f8cb4401
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md
@@ -0,0 +1,62 @@
+---
+id: 5e7b9f160b6c005b0e76f086
+title: 'Objekte: Eine Beispielklasse'
+challengeType: 11
+videoId: FiABKEuaSJ8
+bilibiliIds:
+ aid: 589451777
+ bvid: BV1rq4y1X7TG
+ cid: 377523194
+dashedName: objects-a-sample-class
+---
+
+# --question--
+
+## --text--
+
+Was wird das folgende Programm ausgeben?:
+
+```python
+class PartyAnimal:
+ x = 0
+ def party(self):
+ self.x = self.x + 2
+ print(self.x)
+
+an = PartyAnimal()
+an.party()
+an.party()
+```
+
+## --answers--
+
+
+So far 1
+So far 2
+
+
+---
+
+
+0
+0
+
+
+---
+
+
+2
+2
+
+
+---
+
+
+2
+4
+
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md
new file mode 100644
index 00000000000..ae6383ff01e
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f170b6c005b0e76f088
+title: 'Objekte: Vererbung'
+challengeType: 11
+videoId: FBL3alYrxRM
+bilibiliIds:
+ aid: 631990691
+ bvid: BV1sb4y1r7GF
+ cid: 377529901
+dashedName: objects-inheritance
+---
+
+# --question--
+
+## --text--
+
+Was versteht man unter Vererbung in der objektorientierten Programmierung?
+
+## --answers--
+
+Eine neue Klasse, die erstellt wird, wenn eine Elternklasse erweitert wird.
+
+---
+
+Eine erzeugte Instanz einer Klasse.
+
+---
+
+Die Möglichkeit, eine neue Klasse durch Erweiterung einer bestehenden Klasse zu erstellen.
+
+---
+
+Eine Methode, die in dem Moment aufgerufen wird, in dem eine Klasse verwendet wird, um ein Objekt zu erzeugen.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md
new file mode 100644
index 00000000000..7dded161b69
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md
@@ -0,0 +1,59 @@
+---
+id: 5e7b9f090b6c005b0e76f067
+title: Python Dictionaries
+challengeType: 11
+videoId: dnzvfimrRMg
+bilibiliIds:
+ aid: 631893305
+ bvid: BV19b4y167kj
+ cid: 376386176
+dashedName: python-dictionaries
+---
+
+# --question--
+
+## --text--
+
+Was wird nach der Ausführung dieses Codes ausgegeben?:
+
+```python
+dict = {"Fri": 20, "Thu": 6, "Sat": 1}
+dict["Thu"] = 13
+dict["Sat"] = 2
+dict["Sun"] = 9
+```
+
+## --answers--
+
+```python
+{'Fri': 20, 'Thu': 6, 'Sat': 1}
+```
+
+---
+
+```python
+{'Fri': 20, 'Thu': 6, 'Sat': 1, 'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+---
+
+```python
+{'Sun': 9}
+```
+
+---
+
+```python
+{'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+---
+
+```python
+{'Fri': 20, 'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+## --video-solution--
+
+5
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-functions.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-functions.md
new file mode 100644
index 00000000000..ba8ebe6880d
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-functions.md
@@ -0,0 +1,42 @@
+---
+id: 5e7b9f060b6c005b0e76f05a
+title: Python Funktionen
+challengeType: 11
+videoId: 3JGF-n3tDPU
+bilibiliIds:
+ aid: 631881917
+ bvid: BV1Xb4y167P4
+ cid: 376337920
+dashedName: python-functions
+---
+
+# --question--
+
+## --text--
+
+Was ist der Zweck des Schlüsselworts "def" in Python?
+
+## --answers--
+
+Es ist ein Slang, und bedeutet: "Der folgende Code ist wirklich cool."
+
+---
+
+Es zeigt den Beginn einer Funktion an.
+
+---
+
+Es zeigt an, dass der folgende eingerückte Codeabschnitt für später gespeichert werden soll.
+
+---
+
+Es zeigt den Beginn einer Funktion an, und der folgende eingerückte Codeabschnitt soll für später gespeichert werden.
+
+---
+
+Keine der oben genannten Möglichkeiten.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-lists.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-lists.md
new file mode 100644
index 00000000000..c118cb27047
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-lists.md
@@ -0,0 +1,43 @@
+---
+id: 5e7b9f080b6c005b0e76f064
+title: Python Listen
+challengeType: 11
+videoId: Y0cvfDpYC_c
+bilibiliIds:
+ aid: 249460305
+ bvid: BV1Dv411E7Uj
+ cid: 376532993
+dashedName: python-lists
+---
+
+# --question--
+
+## --text--
+
+Was ist der Wert von x nach der Ausführung dieses Codes:
+
+```python
+fruit = "banana"
+x = fruit[1]
+```
+
+## --answers--
+
+banana
+
+---
+
+a
+
+---
+
+b
+
+---
+
+True
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-objects.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-objects.md
new file mode 100644
index 00000000000..4d2217653dc
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/python-objects.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f160b6c005b0e76f085
+title: Python Objekte
+challengeType: 11
+videoId: uJxGeTYy0us
+bilibiliIds:
+ aid: 889496260
+ bvid: BV1ZP4y1s7G6
+ cid: 377522762
+dashedName: python-objects
+---
+
+# --question--
+
+## --text--
+
+Welche Aussage zu Objekten in Python trifft NICHT zu?
+
+## --answers--
+
+Objekte werden erstellt und verwendet.
+
+---
+
+Objekte sind Bits aus Code und Daten.
+
+---
+
+Objekte verbergen Details.
+
+---
+
+Objekte sind einer der fünf Standard-Datentypen.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/reading-files.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/reading-files.md
new file mode 100644
index 00000000000..660a4c22411
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/reading-files.md
@@ -0,0 +1,42 @@
+---
+id: 5e7b9f080b6c005b0e76f062
+title: Dateien einlesen
+challengeType: 11
+videoId: Fo1tW09KIwo
+bilibiliIds:
+ aid: 334439927
+ bvid: BV1pw411R7UK
+ cid: 376532076
+dashedName: reading-files
+---
+
+# --question--
+
+## --text--
+
+Was wird verwendet, um eine neue Zeile in einem String anzuzeigen?
+
+## --answers--
+
+\\n
+
+---
+
+{new_line}
+
+---
+
+{n}
+
+---
+
+/n
+
+---
+
+/new
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md
new file mode 100644
index 00000000000..221f1999db2
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md
@@ -0,0 +1,45 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06f
+title: 'Reguläre Ausdrücke: Vergleichen und Extrahieren von Daten'
+challengeType: 11
+videoId: LaCZnTbQGkE
+bilibiliIds:
+ aid: 975629041
+ bvid: BV1i44y1b7hE
+ cid: 414167130
+dashedName: regular-expressions-matching-and-extracting-data
+---
+
+# --question--
+
+## --text--
+
+Was wird das folgende Programm ausgeben?:
+
+```python
+import re
+s = 'A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM'
+lst = re.findall('\\S+@\\S+', s)
+print(lst)
+```
+
+## --answers--
+
+['csev@umich.edu', 'cwen@iupui.edu']
+
+---
+
+['csev@umich.edu']
+
+---
+
+['umich.edu', 'iupui.edu']
+
+---
+
+['csev@', 'cwen@']
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md
new file mode 100644
index 00000000000..b9f70557f75
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f0b0b6c005b0e76f070
+title: 'Reguläre Ausdrücke: Praktische Anwendungen'
+challengeType: 11
+videoId: xCjFU9G6x48
+bilibiliIds:
+ aid: 546924659
+ bvid: BV1mq4y1H7rZ
+ cid: 376386493
+dashedName: regular-expressions-practical-applications
+---
+
+# --question--
+
+## --text--
+
+Wie wird nach einem "$" in einem regulären Ausdruck gesucht?
+
+## --answers--
+
+$
+
+---
+
+\\dollar\\
+
+---
+
+\\$
+
+---
+
+!$
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md
new file mode 100644
index 00000000000..649bcc0c22d
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md
@@ -0,0 +1,42 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06e
+title: Reguläre Ausdrücke
+challengeType: 11
+videoId: Yud_COr6pZo
+bilibiliIds:
+ aid: 759422542
+ bvid: BV1W64y167YD
+ cid: 376387549
+dashedName: regular-expressions
+---
+
+# --question--
+
+## --text--
+
+Welcher reguläre Ausdruck (Regex) passt nur auf ein Leerzeichen?
+
+## --answers--
+
+\\S
+
+---
+
+\\s
+
+---
+
+.
+
+---
+
+\_
+
+---
+
+\\.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md
new file mode 100644
index 00000000000..8fa3bdfb38b
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f180b6c005b0e76f08c
+title: Relationales Datenbank-Design
+challengeType: 11
+videoId: AqdfbrpkbHk
+bilibiliIds:
+ aid: 504388066
+ bvid: BV1Qg411j742
+ cid: 377532216
+dashedName: relational-database-design
+---
+
+# --question--
+
+## --text--
+
+Was ist die beste Vorgehensweise dafür, wie oft ein bestimmter Datenstring in einer Datenbank gespeichert werden sollte?
+
+## --answers--
+
+0
+
+---
+
+1
+
+---
+
+2
+
+---
+
+3
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md
new file mode 100644
index 00000000000..5044d2b084c
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md
@@ -0,0 +1,44 @@
+---
+id: 5e7b9f170b6c005b0e76f08a
+title: Relationale Datenbanken und SQLite
+challengeType: 11
+videoId: QlNod5-kFpA
+bilibiliIds:
+ aid: 249449958
+ bvid: BV12v411E74H
+ cid: 377530805
+dashedName: relational-databases-and-sqlite
+---
+
+# --description--
+
+SQLite herunterladen
+DB Browser für SQLite herunterladen
+SQLite usage
+
+# --question--
+
+## --text--
+
+Was ist KEINE primäre Datenstruktur in einer Datenbank?
+
+## --answers--
+
+index
+
+---
+
+table
+
+---
+
+row
+
+---
+
+column
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md
new file mode 100644
index 00000000000..2e195c514aa
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f180b6c005b0e76f08f
+title: 'Relationale Datenbanken: Join-Operation'
+challengeType: 11
+videoId: jvDw3D9GKac
+bilibiliIds:
+ aid: 804461215
+ bvid: BV1Ry4y1j7tv
+ cid: 377542880
+dashedName: relational-databases-join-operation
+---
+
+# --question--
+
+## --text--
+
+Wenn man eine JOIN-Bedingung in einer SQL-Anweisung verwendet, was bewirkt dann ON?
+
+## --answers--
+
+Es gibt an, auf welchen Tabellen der JOIN ausgeführt werden soll.
+
+---
+
+Er gibt die Felder an, die für den JOIN verwendet werden sollen.
+
+---
+
+Es gibt an, wie die beiden Tabellen verbunden werden sollen.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md
new file mode 100644
index 00000000000..8f13d7b6f3c
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md
@@ -0,0 +1,52 @@
+---
+id: 5e7b9f190b6c005b0e76f090
+title: 'Relationale Datenbanken: Viele-zu-viele-Beziehungen'
+challengeType: 11
+videoId: z-SBYcvEQOc
+bilibiliIds:
+ aid: 291965127
+ bvid: BV1Af4y1L7BK
+ cid: 377543409
+dashedName: relational-databases-many-to-many-relationships
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe: Email
+
+\- Aufgabe: Roster
+
+\- Aufgabe: Tracks
+
+\- Aufgabe: Twfriends
+
+\- Aufgabe: Twspider
+
+# --question--
+
+## --text--
+
+Was ist ein Beispiel für eine Viele-zu-Viele-Beziehung?
+
+## --answers--
+
+Lehrer zu Schüler
+
+---
+
+Kunde zu Bestellung
+
+---
+
+Buch zu Seiten
+
+---
+
+Stadt zu Land
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md
new file mode 100644
index 00000000000..d9df190e9a0
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f180b6c005b0e76f08e
+title: 'Relationale Datenbanken: Aufbau von Beziehungen'
+challengeType: 11
+videoId: CSbqczsHVnc
+bilibiliIds:
+ aid: 376996473
+ bvid: BV1jo4y1S7VY
+ cid: 377532966
+dashedName: relational-databases-relationship-building
+---
+
+# --question--
+
+## --text--
+
+Was macht der INSERT-Befehl in SQL?
+
+## --answers--
+
+Es definiert eine neue Zeile, indem es die Felder auflistet, die aufgenommen werden sollen, gefolgt von den Werten, die in der neuen Zeile stehen sollen.
+
+---
+
+Es definiert eine neue Spalte, indem es die Zeilen auflistet, die einbezogen werden sollen, gefolgt von den Werten, die in der neuen Spalte stehen sollen.
+
+---
+
+Es definiert eine neue Tabelle, indem es die Zeilen und Felder auflistet, die wir einbeziehen wollen, gefolgt von den Werten, die wir in der Tabelle platzieren wollen.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md
new file mode 100644
index 00000000000..b83c7ffe2d6
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f180b6c005b0e76f08d
+title: Beziehungen in einer relationalen Datenbank darstellen
+challengeType: 11
+videoId: '-orenCNdC2Q'
+bilibiliIds:
+ aid: 931953070
+ bvid: BV1FM4y1N7hc
+ cid: 377532529
+dashedName: representing-relationships-in-a-relational-database
+---
+
+# --question--
+
+## --text--
+
+Was ist ein Fremdschlüssel?
+
+## --answers--
+
+Ein Schlüssel, der nicht vorhanden sein sollte.
+
+---
+
+Ein Schlüssel, der nicht-lateinische Zeichen verwendet.
+
+---
+
+Eine Zahl, die auf den Primärschlüssel einer zugehörigen Zeile einer anderen Tabelle verweist.
+
+---
+
+Ein Schlüssel, den die "reale Welt" verwenden könnte, um eine Zeile zu suchen.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md
new file mode 100644
index 00000000000..69171503b20
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md
@@ -0,0 +1,51 @@
+---
+id: 5e7b9f090b6c005b0e76f066
+title: Strings und Listen
+challengeType: 11
+videoId: lxcFa7ldCi0
+bilibiliIds:
+ aid: 804401443
+ bvid: BV1By4y1j7F9
+ cid: 376385517
+dashedName: strings-and-lists
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe
+
+# --question--
+
+## --text--
+
+Was entspricht n in diesem Code?
+
+```python
+words = 'His e-mail is q-lar@freecodecamp.org'
+pieces = words.split()
+parts = pieces[3].split('-')
+n = parts[1]
+```
+
+## --answers--
+
+mail
+
+---
+
+q
+
+---
+
+lar
+
+---
+
+`lar@freecodecamp.org`
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md
new file mode 100644
index 00000000000..a74884b86bb
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md
@@ -0,0 +1,63 @@
+---
+id: 5e7b9f070b6c005b0e76f060
+title: Strings in Python
+challengeType: 11
+videoId: LYZj207fKpQ
+bilibiliIds:
+ aid: 504434218
+ bvid: BV1Lg41177s8
+ cid: 376531802
+dashedName: strings-in-python
+---
+
+# --question--
+
+## --text--
+
+Welches Ergebnis liefert folgender Code?
+
+```python
+for n in "banana":
+ print(n)
+```
+
+## --answers--
+
+
+n
+n
+
+
+---
+
+
+0
+1
+
+
+---
+
+
+0
+1
+2
+3
+4
+5
+
+
+---
+
+
+b
+a
+n
+a
+n
+a
+
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md
new file mode 100644
index 00000000000..3613be9a905
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md
@@ -0,0 +1,63 @@
+---
+id: 5e7b9f0a0b6c005b0e76f06c
+title: Die Tuples Sammlung
+challengeType: 11
+videoId: 3Lxpladfh2k
+bilibiliIds:
+ aid: 334468209
+ bvid: BV1aw411R77G
+ cid: 376533308
+dashedName: the-tuples-collection
+---
+
+# --question--
+
+## --text--
+
+Welches Ergebnis liefert folgender Code?
+
+```python
+d = dict()
+d['quincy'] = 1
+d['beau'] = 5
+d['kris'] = 9
+for (k,i) in d.items():
+ print(k, i)
+```
+
+## --answers--
+
+
+k i
+k i
+k i
+
+
+---
+
+
+quincy 0
+beau 1
+kris 2
+
+
+---
+
+
+quincy 1
+beau 5
+kris 9
+
+
+---
+
+
+1 quincy
+5 beau
+9 kris
+
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/using-web-services.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/using-web-services.md
new file mode 100644
index 00000000000..bde00c74192
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/using-web-services.md
@@ -0,0 +1,42 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07a
+title: Webdienste benutzen
+challengeType: 11
+videoId: oNl1OVDPGKE
+bilibiliIds:
+ aid: 759406136
+ bvid: BV1b64y16746
+ cid: 377332189
+dashedName: using-web-services
+---
+
+# --question--
+
+## --text--
+
+Was sind die zwei häufigsten Möglichkeiten, Daten über das Internet zu senden?
+
+## --answers--
+
+JSON und TXT
+
+---
+
+JSON und XML
+
+---
+
+XML und TXT
+
+---
+
+XML und PHP
+
+---
+
+PHP und TXT
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md
new file mode 100644
index 00000000000..4e0311b01b0
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f050b6c005b0e76f056
+title: 'Variablen, Ausdrücke und Anweisungen'
+challengeType: 11
+videoId: nELR-uyyrok
+bilibiliIds:
+ aid: 419396811
+ bvid: BV1iV411p7Mn
+ cid: 376318116
+dashedName: variables-expressions-and-statements
+---
+
+# --question--
+
+## --text--
+
+Was ist das Symbol in einer Zuordnungsanweisung?
+
+## --answers--
+
+~
+
+---
+
+&
+
+---
+
+=
+
+---
+
+\|
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md
new file mode 100644
index 00000000000..7a43905ad03
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md
@@ -0,0 +1,42 @@
+---
+id: 5e7b9f690b6c005b0e76f095
+title: Datenvisualisierung mit Python
+challengeType: 11
+videoId: e3lydkH0prw
+bilibiliIds:
+ aid: 291996462
+ bvid: BV15f4y1L7jH
+ cid: 377544192
+dashedName: visualizing-data-with-python
+---
+
+# --question--
+
+## --text--
+
+Die meisten Daten müssen \_\_\_\_\_\_\_\_ sein, bevor sie verwendet werden können.
+
+## --answers--
+
+in JSON-Format konvertiert
+
+---
+
+grafisch
+
+---
+
+bereinigt
+
+---
+
+gespeichert
+
+---
+
+in ein Lied verwandelt
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md
new file mode 100644
index 00000000000..2a789cd1249
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md
@@ -0,0 +1,50 @@
+---
+id: 5e7b9f150b6c005b0e76f080
+title: 'Webdienste: API-Rate Limiting und Sicherheit'
+challengeType: 11
+videoId: pI-g0lI8ngs
+bilibiliIds:
+ aid: 249456172
+ bvid: BV1Sv411E7qa
+ cid: 377336269
+dashedName: web-services-api-rate-limiting-and-security
+---
+
+# --description--
+
+Weitere Ressourcen:
+
+\- Aufgabe: GeoJSON
+
+\- Aufgabe: JSON
+
+\- Aufgabe: Twitter
+
+\- Aufgabe: XML
+
+# --question--
+
+## --text--
+
+Wenn du eine Anfrage an die Twitter-API stellst, welche Informationen müssen immer mit der Anfrage gesendet werden?
+
+## --answers--
+
+Twitter Benutzername
+
+---
+
+Zeitraum
+
+---
+
+Suchbegriff
+
+---
+
+Schlüssel
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md
new file mode 100644
index 00000000000..c67525f2ec3
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f150b6c005b0e76f07f
+title: 'Webdienste: APIs'
+challengeType: 11
+videoId: oUNn1psfBJg
+bilibiliIds:
+ aid: 589451017
+ bvid: BV1zq4y1X7A9
+ cid: 377336011
+dashedName: web-services-apis
+---
+
+# --question--
+
+## --text--
+
+Wofür steht API?
+
+## --answers--
+
+Application Portable Intelligence
+
+---
+
+Associate Programming International
+
+---
+
+Application Program Interface
+
+---
+
+Action Portable Interface
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-json.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-json.md
new file mode 100644
index 00000000000..9934ae86c9a
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-json.md
@@ -0,0 +1,60 @@
+---
+id: 5e7b9f140b6c005b0e76f07d
+title: 'Webdienste: JSON'
+challengeType: 11
+videoId: ZJE-U56BppM
+bilibiliIds:
+ aid: 419491911
+ bvid: BV1r3411672w
+ cid: 377332928
+dashedName: web-services-json
+---
+
+# --question--
+
+## --text--
+
+Welches Ergebnis liefert folgender Code?
+
+```python
+import json
+data = '''
+ [
+ { "id" : "001",
+ "x" : "2",
+ "name" : "Quincy"
+ } ,
+ { "id" : "009",
+ "x" : "7",
+ "name" : "Mrugesh"
+ }
+ ]
+'''
+info = json.loads(data)
+print(info[1]['name'])
+```
+
+## --answers--
+
+Quincy
+
+---
+
+Mrugesh
+
+---
+
+001
+
+---
+
+009
+
+---
+
+[Error]
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md
new file mode 100644
index 00000000000..b7cd6428cb6
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f140b6c005b0e76f07e
+title: 'Webdienste: Serviceorientierter Ansatz'
+challengeType: 11
+videoId: muerlsCHExI
+bilibiliIds:
+ aid: 846899335
+ bvid: BV1E54y1J7oz
+ cid: 377333277
+dashedName: web-services-service-oriented-approach
+---
+
+# --question--
+
+## --text--
+
+Wo befinden sich Daten mit einem serviceorientierten Ansatz zur Entwicklung von Web-Apps?
+
+## --answers--
+
+Verteilt auf viele Computersysteme, die über das Internet oder das interne Netzwerk verbunden sind.
+
+---
+
+Innerhalb verschiedener Dienste auf dem Haupt-Webserver.
+
+---
+
+Auf einem separaten Datenbankserver.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md
new file mode 100644
index 00000000000..2ba87d2153e
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07c
+title: 'Webdienste: XML-Schema'
+challengeType: 11
+videoId: yWU9kTxW-nc
+bilibiliIds:
+ aid: 631951466
+ bvid: BV1Vb4y1r7m7
+ cid: 377332603
+dashedName: web-services-xml-schema
+---
+
+# --question--
+
+## --text--
+
+Was ist XSD?
+
+## --answers--
+
+Die W3C-Schema Spezifikation für XML.
+
+---
+
+Das Standard-JSON-Schema von MOZ.
+
+---
+
+Erweiterbarer Situationsbezogener Treiber
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md
new file mode 100644
index 00000000000..86457e4733e
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md
@@ -0,0 +1,47 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07b
+title: 'XML-Webdienste'
+challengeType: 11
+videoId: _pZ0srbg7So
+bilibiliIds:
+ aid: 761920032
+ bvid: BV1n64y1x7KW
+ cid: 377332379
+dashedName: web-services-xml
+---
+
+# --question--
+
+## --text--
+
+Was ist falsch mit dem folgenden XML?:
+
+```xml
+
+ Chuck
+
+ +1 734 303 4456
+
+
+```
+
+## --answers--
+
+E-Mail-Tag fehlt ein abschließendes Tag.
+
+---
+
+Durch Leerzeichen wird XML ungültig.
+
+---
+
+Telefon-Tag fehlt ein abschließendes Tag.
+
+---
+
+Einfacher Text sollte mit UTF-8 kodiert werden.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md
new file mode 100644
index 00000000000..e3c3bbe567b
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f090b6c005b0e76f065
+title: Mit Listen arbeiten
+challengeType: 11
+videoId: lCnHfTHkhbE
+bilibiliIds:
+ aid: 376965958
+ bvid: BV1No4y1S7oi
+ cid: 376387989
+dashedName: working-with-lists
+---
+
+# --question--
+
+## --text--
+
+Welche Methode wird verwendet, um ein Element am Ende einer Liste hinzuzufügen?
+
+## --answers--
+
+insert
+
+---
+
+push
+
+---
+
+append
+
+---
+
+new
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md
new file mode 100644
index 00000000000..26ac50c3bd5
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md
@@ -0,0 +1,100 @@
+---
+id: 5e44412c903586ffb414c94c
+title: Arithmetischer Formatierer
+challengeType: 10
+forumTopicId: 462359
+dashedName: arithmetic-formatter
+---
+
+# --description--
+
+Du wirst mit unserem Replit-Startercode an diesem Projekt arbeiten .
+
+# --instructions--
+
+Schüler der Grundschule stellen oft arithmetische Probleme vertikal auf, um sie leichter lösen zu können. Zum Beispiel wird "235 + 52" zu:
+
+```py
+ 235
++ 52
+-----
+```
+
+Erstelle eine Funktion, die eine Liste von Strings mit artihmetischen Problem empfängt und die angeordneten Probleme vertikal und nebeneinander zurückgibt. Optional sollte die Funktion ein zweites Argument verwenden. Wenn das zweite Argument auf `True` gesetzt ist, sollten die Antworten gezeigt werden.
+
+## Beispiel
+
+Function-Call:
+
+```py
+arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"])
+```
+
+Output:
+
+```py
+ 32 3801 45 123
++ 698 - 2 + 43 + 49
+----- ------ ---- -----
+```
+
+Function-Call:
+
+```py
+arithmetic_arranger(["32 + 8", "1 - 3801", "9999 + 9999", "523 - 49"], True)
+```
+
+Output:
+
+```py
+ 32 1 9999 523
++ 8 - 3801 + 9999 - 49
+---- ------ ------ -----
+ 40 -3800 19998 474
+```
+
+## Regeln
+
+Die Funktion gibt die korrekte Konvertierung zurück, wenn die übergebenen Probleme richtig formatiert sind. Andernfalls **gibt** er einen **String** zurück, der einen für den Benutzer aussagekräftigen Fehler beschreibt.
+
+
+- Situationen, die einen Fehler zurückgeben:
+ - Wenn **zu viele Probleme** an die Funktion übermittelt werden. Das Limit liegt bei **fünf**, alles weitere wird `Error: Too many problems.` zurückgeben
+ - Die entsprechenden Operatoren, die die Funktion akzeptieren wird, sind **Addition** und **Subtraktion**. Multiplikation und Division geben einen Fehler zurück. Andere Operatoren, die nicht in diesem Punkt erwähnt werden, müssen nicht getestet werden. Der zurückgegebene Fehler wird `Error: Operator must be '+' or '-'.` lauten
+ - Jede Zahl (Operand) sollte nur Zahlen enthalten. Andernfalls gibt die Funktion zurück: `Error: Numbers must only contain digits.`
+ - Jeder Operand (alias Nummer auf jeder Seite des Operators) hat eine maximale Länge von vier Ziffern. Andernfalls lautet die zurückgegebene Fehlerzeichenfolge: `Error: Numbers cannot be more than four digits.`
+- Wenn der Benutzer das richtige Problemformat angegeben hat, wird die von dir zurückgegebene Konvertierung folgende Regeln folgen:
+ - Zwischen dem Operator und dem längsten der beiden Operanden sollte ein einzelnes Leerzeichen stehen, der Operator steht in derselben Zeile wie der zweite Operand, beide Operanden stehen in derselben Reihenfolge wie angegeben (der erste ist der oberste, der zweite der unterste).
+ - Zahlen sollten rechts-ausgerichtet sein.
+ - Zwischen den einzelnen Aufgaben sollten vier Leerzeichen stehen.
+ - Am Ende jeder Aufgabe sollten Bindestriche stehen. Die Bindestriche sollten sich über die gesamte Länge der einzelnen Aufgaben erstrecken. (Das obige Beispiel zeigt, wie dies aussehen sollte.)
+
+## Entwicklung
+
+Schreibe deinen Code in `arithmetic_arranger.py`. Für die Entwicklung kannst du `main.py` verwenden, um die `arithmetic_arranger()` Funktion zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+## Testen
+
+Die Unit-Tests für dieses Projekt sind in `test_module.py`. Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst. Alternativ können die Tests auch ausgeführt werden, indem du `pytest` in die Konsole eingibst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Es sollte ein arithmetisches Problem korrekt formatieren und alle Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md
new file mode 100644
index 00000000000..d53cb353b34
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md
@@ -0,0 +1,102 @@
+---
+id: 5e44413e903586ffb414c94e
+title: Budget-App
+challengeType: 10
+forumTopicId: 462361
+dashedName: budget-app
+---
+
+# --description--
+
+Du wirst mit unserem Replit-Startercode an diesem Projekt arbeiten .
+
+# --instructions--
+
+Vervollständige die `Category`-Klasse in `budget.py`. Es sollte in der Lage sein, Objekte basierend auf verschiedenen Haushaltskategorien, wie *food*, *clothing*, und *entertainment* zu instanziieren. Wenn Objekte erstellt werden, werden sie im Namen der Kategorie übergeben. Die Klasse sollte eine Instanzvariable namens `ledger` (Hauptbuch) haben, die eine Liste ist. Die Klasse sollte auch die folgenden Methoden beinhalten:
+
+- Eine `deposit`-Methode, die einen Betrag und eine Beschreibung akzeptiert. Wenn keine Beschreibung angegeben wird, sollte standardmäßig ein leerer String ausgegeben werden. Die Methode sollte ein Objekt zu der Hauptbuch-Liste in Form von `{"amount": amount, "description": description}` anhängen.
+- Eine `withdraw`-Methode, die ähnlich zur `deposit`-Methode ist, aber der eingegebene Wert sollte im Hauptbuch als negative Zahl eingespeichert werden. Wenn nicht genügend Mittel zur Verfügung stehen, sollte nichts zum Hauptbuch hinzugefügt werden. Diese Methode sollte `True` zurückgeben, wenn die Auszahlung bereits stattgefunden hat und ansonsten `False` ausgeben.
+- Eine `get_balance`-Methode, die das aktuelle Guthaben der Haushaltskategorie, basierend auf den eingegangen Ein-und Auszahlungen ausgibt.
+- Eine `transfer`-Methode, die einen Betrag und eine andere Budgetkategorie als Arugment akzeptiert. Die Methode sollte eine Auszahlung mit dem Betrag und der Beschreibung "Transfer zu [Zielbudget-Kategorie]" hinzufügen. Die Methode sollte dann eine Einzahlung in die andere Budgetkategorie mit dem Betrag und der Beschreibung "Transfer from [Source Budget Category]" hinzufügen. Wenn nicht genügend Mittel zur Vefügung stehen, sollte nichts zum Hauptbuch hinzugefügt werden. Diese Methode sollte `True` zurückgeben, wenn die Auszahlung bereits stattgefunden hat und ansonsten `False` ausgeben.
+- Eine `check_funds`-Methode, die einen Betrag als Argument akzeptiert. Es gibt `False` zurück, wenn der Betrag größer ist als der Betrag der Budgetkategorie und ansonsten `True`. Die Methode sollte sowohl von der `withdraw`-Methode und der `transfer`-Methode verwendet werden.
+
+Wenn das Budgetobjekt ausgegeben wird, sollte es folgendes anzeigen:
+
+- Eine Titelzeile mit 30 Zeichen, in der der Name der Kategorie in einer Zeile von `*` Zeichen zentriert ist.
+- Eine Liste der Elemente im Hauptbuch. Jede Zeile sollte die Beschreibung und den Betrag anzeigen. Die ersten 23 Zeichen der Beschreibung sollten angezeigt werden, dann der Betrag. Der Betrag sollte rechts ausgerichtet sein, zwei Dezimalstellen enthalten und maximal 7 Zeichen anzeigen.
+- Eine Zeile, die die Summe der Kategorie anzeigt.
+
+Hier ist ein Beispiel für die Ausgabe:
+
+```bash
+*************Food*************
+initial deposit 1000.00
+groceries -10.15
+restaurant and more foo -15.89
+Transfer to Clothing -50.00
+Total: 923.96
+```
+
+Neben der `Category`-Klasse, erstelle eine Funktion (außerhalb der Klasse) namens `create_spend_chart`, die eine Liste der Kategorien als Argument verwendet. Es sollte einen String zurückgeben, das ein Balkendiagramm ist.
+
+Das Diagramm sollte den Prozentsatz jeder Kategorie anzeigen, die an die Funktion übergeben wurde. Der ausgegebene Prozentsatz sollte nur mit den Auszahlungen und nicht mit den Einzahlungen berechnet werden. Unten auf der linken Seite des Diagramms sollten die Labels 0 - 100 stehen. Die "Balken" im Balkendiagramm sollten aus dem "o"-Zeichen gemacht werden. Die Höhe jedes Balken sollte auf die nächstgelegene 10 abgerundet werden. Die horizontale Linie unter den Balken sollte zwei Leerzeichen hinter der Endleiste haben. Jeder Kategoriename sollte vertikal unterhalb des Balkens angegeben werden. Ganz oben sollte ein Titel stehen, der "Prozentsatz der Ausgaben nach Kategorie" angibt.
+
+Die Funktion wird mit bis zu vier Kategorien getestet.
+
+Schau dir die Beispielausgabe unten sehr genau an und stelle sicher, dass der Abstand der Ausgabe genau dem Beispiel entspricht.
+
+```bash
+Percentage spent by category
+100|
+ 90|
+ 80|
+ 70|
+ 60| o
+ 50| o
+ 40| o
+ 30| o
+ 20| o o
+ 10| o o o
+ 0| o o o
+ ----------
+ F C A
+ o l u
+ o o t
+ d t o
+ h
+ i
+ n
+ g
+```
+
+Die Unit-Tests für dieses Projekt sind in `test_module.py`.
+
+## Entwicklung
+
+Schreibe deinen Code in `budget.py`. Für die Entwicklung kannst du `main.py` verwenden, um die `Category`-Klasse zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+## Testen
+
+Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Sie sollte eine Kategorieklasse erstellen und alle Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md
new file mode 100644
index 00000000000..49fadef39e0
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md
@@ -0,0 +1,112 @@
+---
+id: 5e444147903586ffb414c94f
+title: Polygon-Flächenrechner
+challengeType: 10
+forumTopicId: 462363
+dashedName: polygon-area-calculator
+---
+
+# --description--
+
+Du wirst mit unserem Replit-Startercode an diesem Projekt arbeiten .
+
+# --instructions--
+
+In diesem Projekt wirst du mit Hilfe der objektorientierten Programmierung eine Rectangle-Klasse und eine Square-Klasse erstellen. Die Square-Klasse sollte eine Unterklasse von Rechtecke sein und Methoden und Attribute erben.
+
+## Rechteck-Klasse
+
+Es sollte mit den Attributen `width` und `height` initialisiert werden, wenn ein Rechteck-Objekt erstellt wird. Die Klasse sollte ebenfalls folgende Methoden enthalten:
+
+- `set_width`
+- `set_height`
+- `get_area`: Gibt den Bereich zurück (`width * height`)
+- `get_perimeter`: Gibt den Umfang zurück (`2 * width + 2 * height`)
+- `get_diagonal`: Gibt die Diagonale zurück (`(width ** 2 + height ** 2) ** .5`)
+- `get_picture`: Gibt einen String zurück, der die Form mit Hilfe von Linien aus "\*" darstellt. Die Anzahl der Zeilen sollte der Höhe und die Anzahl der "\*" in jeder Zeile sollte der Breite entsprechen. Am Ende jeder Zeile sollte eine neue Zeile (`\n`) stehen. Wenn die Breite oder Höhe größer als 50 ist, sollte dies den String zurückgeben: "Zu groß für das Bild.".
+- `get_amount_inside`: Nimmt eine andere Form (Quadrat oder Rechteck) als Argument. Gibt die Anzahl der Male zurück, die die übergebene Form in die Form passen könnte (ohne Drehungen). Zum Beispiel könnte ein Rechteck mit einer Breite von 4 und einer Höhe von 8 in zwei Quadrate mit 4 Seiten passen.
+
+Er sollte außerdem, wenn eine Instanz eines Rechtecks als String dargestellt wird, wie folgt aussehen: `Rectangle(width=5, height=10)`
+
+## Quadrat-Klasse
+
+Die Quadrat-Klasse sollte eine Unterklasse von Rechtecke sein. Bei der Erstellung eines Quadrat-Objekts wird eine einzelne Seitenlänge angegeben. Die `__init__`-Methode sollte die Seitenlänge in den beiden Attributen `width` und `height` der Rechteck-Klasse speichern.
+
+Die Quadrat-Klasse sollte auf die Methoden der Rechteck-Klasse zugreifen können, aber auch eine `set_side`-Methode enthalten. Es sollte wie folgt aussehen, wenn eine Instanz eines Quadrats als String dargestellt wird: `Square(side=9)`
+
+Außerdem sollten die Methoden `set_width` und `set_height` der Quadrat-Klasse sowohl die Breite als auch die Höhe festlegen.
+
+## Beispiel für die Verwendung
+
+```py
+rect = shape_calculator.Rectangle(10, 5)
+print(rect.get_area())
+rect.set_height(3)
+print(rect.get_perimeter())
+print(rect)
+print(rect.get_picture())
+
+sq = shape_calculator.Square(9)
+print(sq.get_area())
+sq.set_side(4)
+print(sq.get_diagonal())
+print(sq)
+print(sq.get_picture())
+
+rect.set_height(8)
+rect.set_width(16)
+print(rect.get_amount_inside(sq))
+```
+
+Dieser Code sollte zurückgeben:
+
+```bash
+50
+26
+Rectangle(width=10, height=3)
+**********
+**********
+**********
+
+81
+5.656854249492381
+Square(side=4)
+****
+****
+****
+****
+
+8
+```
+
+Die Unit-Tests für dieses Projekt befinden sich in `test_module.py`.
+
+## Entwicklung
+
+Schreibe deinen Code in `shape_calculator.py`. Für die Entwicklung kannst du `main.py` verwenden, um deine `shape_calculator()`-Funktion zu testen. Klicke aus den "Run"- Button und `main.py` wird ausgeführt.
+
+## Prüfung
+
+Wir haben die Tests von `test_module.py` in `main.py` importiert, um dir die Arbeit zu erleichtern. Die Tests werden automatisch ausgeführt, sobald du auf den "Run" - Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Es sollte eine Rechteck-Klasse und eine Quadrat-Klasse erstellen und alle Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md
new file mode 100644
index 00000000000..503928091e0
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md
@@ -0,0 +1,86 @@
+---
+id: 5e44414f903586ffb414c950
+title: Wahrscheinlichkeitsrechner
+challengeType: 10
+forumTopicId: 462364
+dashedName: probability-calculator
+---
+
+# --description--
+
+Du wirst mit unserem Replit-Startercode an diesem Projekt arbeiten .
+
+# --instructions--
+
+Angenommen, es gibt einen Hut mit 5 blauen Kugeln, 4 roten Kugeln und 2 grünen Kugeln. Was ist die Wahrscheinlichkeit, dass bei einer zufälligen Ziehung von 4 Kugeln mindestens 1 rote Kugel und 2 grüne Kugeln gezogen werden? Es wäre zwar möglich, die Wahrscheinlichkeit mit Hilfe fortgeschrittener Mathematik zu berechnen, aber einfacher ist es, ein Programm zu schreiben, das eine große Anzahl von Experimenten durchführt, um eine ungefähre Wahrscheinlichkeit zu schätzen.
+
+Für dieses Projekt wirst du ein Programm schreiben, dass die ungefähre Wahrscheinlichkeit bestimmen soll, bestimmte Kugeln aus einem Hut zu ziehen.
+
+Zuerst erstellen wir eine `Hat`-Klasse in `prob_calculator.py`. Die Klasse sollte eine variable Anzahl von Argumenten verwenden, die die Anzahl der Kugeln jeder Farbe angibt, die im Hut enthalten sind. Zum Beispiel könnte ein Klassenobjekt auf diese Weise erstellt werden:
+
+```py
+hat1 = Hat(yellow=3, blue=2, green=6)
+hat2 = Hat(red=5, orange=4)
+hat3 = Hat(red=5, orange=4, black=1, blue=0, pink=2, striped=9)
+```
+
+Ein Hut wird immer mit mindestens einer Kugel erstellt. Die Argumente, die beim Erstellen an das Hut-Objekt übergeben werden, sollten in eine `contents` Instanzvariable konvertiert werden. `contents` sollte eine Liste von Strings sein, die jeweils ein Element für jede Kugel im Hut enthalten. Jedes Element in der Liste sollte ein Farbname sein, der eine einzelne Kugel dieser Farbe darstellt. Zum Beispiel, wenn dein Hut `{"red": 2, "blue": 1}` enthält, sollte `contents` `["red", "red", "blue"]` beinhalten.
+
+Die `Hat`-Klasse sollte eine `draw`-Methode haben, die ein Argument akzeptiert, dass die Anzahl der Kugeln angibt, die aus dem Hut gezogen werden. Diese Methode sollte Kugeln zufällig aus `contents` entfernen und diese Kugeln als eine Liste von String zurückgeben. Die Kugeln sollten während des Ziehens nicht wieder in den Hut zurückkehren, wie ein Urnen-Experiment ohne Zurücklegen. Wenn die Anzahl der zu ziehenden Kugeln die verfügbare Menge übersteigt, gib alle Kugeln zurück.
+
+Erstelle nun eine `experiment`-Funktion in `prob_calculator.py` (nicht innerhalb der `Hat`-Klasse). Diese Funktion sollte die folgenden Argumente akzeptieren:
+
+- `hat`: Ein Hut-Objekt, dass Kugeln enthält, die innerhalb der Funktion kopiert werden sollen.
+- `expected_balls`: Ein Objekt, dass die exakte Gruppe von Kugeln anzeigt, die aus dem Hut gezogen werden sollen. Zum Beispiel, wenn wir die Wahrscheinlichkeit bestimmen wollen, um 2 blaue Kugeln und 1 rote Kugel aus dem Hut zu ziehen. Setze dafür `expected_balls` zu `{"blue":2, "red":1}`.
+- `num_balls_drawn`: Die Anzahl der Kugeln, die in jedem Experiment aus dem Hut gezogen werden sollen.
+- `num_experiments`: Die Anzahl der durchzuführenden Experimente. (Je mehr Experimente durchgeführt werden, desto genauer wird die ungefähre Wahrscheinlichkeit sein.)
+
+Die `experiment`-Funktion sollte eine Wahrscheinlichkeit zurückgeben.
+
+Wenn du zum Beispiel die Wahrscheinlichkeit bestimmen willst, dass du mindestens zwei rote Kugeln und eine grüne Kugel bekommst, wenn du fünf Kugeln aus einem Hut ziehst, der sechs schwarze, vier rote und drei grüne enthält. Um dies zu tun, wirst du `N` Experimente durchführen, zähle wie viele Male `M` du erhältst. Mindestens zwei rote Bälle und einen grünen Ball und bestimme die Wahrscheinkeit mit `M/N`. Jedes Experiment besteht darin, dass man mit einem Hut beginnt, der die angegebenen Kugeln enthält, mehrere Kugeln zieht und überprüft, ob man die Kugeln, die man zu ziehen versucht hat, auch bekommen hat.
+
+So würdest du die `experiment`-Funktion, basierend auf dem obigen Beispiel von 2000 Experimenten durchführen:
+
+```py
+hat = Hat(black=6, red=4, green=3)
+probability = experiment(hat=hat,
+ expected_balls={"red":2,"green":1},
+ num_balls_drawn=5,
+ num_experiments=2000)
+```
+
+Da dies auf zufälligen Ziehungen basiert, wird die Wahrscheinlichkeit jedes Mal, wenn der Code ausgeführt wird, etwas anders sein.
+
+*Hinweis: Ziehe in Erwägung, die Module, die bereits in `prob_calculator.py` importiert wurden, zu benutzen. Initialisiere keinen zufälligen Seed innerhalb `prob_calculator.py`.*
+
+## Entwicklung
+
+Schreibe deinen Code in `prob_calculator.py`. Für die Entwicklung kannst du `main.py` verwenden, um deinen Code zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+Der Boilerplate-Code enthält `import` Anweisungen für die `copy` und `random` Module. Überlege, ob du diese in deinem Projekt verwenden möchtest.
+
+## Testen
+
+Die Unit-Tests für dieses Projekt sind in `test_module.py`. Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Es sollte die Wahrscheinlichkeiten korrekt berechnen und alle Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md
new file mode 100644
index 00000000000..cbddc313a39
--- /dev/null
+++ b/curriculum/challenges/german/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md
@@ -0,0 +1,79 @@
+---
+id: 5e444136903586ffb414c94d
+title: Zeitrechner
+challengeType: 10
+forumTopicId: 462360
+dashedName: time-calculator
+---
+
+# --description--
+
+Du wirst an diesem Projekt mit unserem Replit-Startercode arbeiten .
+
+# --instructions--
+
+Erstelle eine Funktion namens `add_time`, die zwei notwendige Parameter und einen optionalen Parameter enthält:
+
+- eine Startzeit im 12-Stunden-Format (Ende in AM (0 - 12Uhr) oder PM (13 - 24 Uhr)
+- eine Zeitdauer, die die Anzahl der Stunden und Minuten anzeigt
+- (Optional) ein Starttag der Woche, der die Groß-und Kleinschreibung ignoriert
+
+Die Funktion sollte die Zeitdauer zur Startzeit hinzufügen und das Ergebnis zurückgeben.
+
+Wenn das Ergebnis am nächsten Tag ist, sollte es `(next day)` nach der Zeit anzeigen. Wenn das Ergebnis mehr als einen Tag später ist, sollte es `(n days later)` hinter der Zeit anzeigen, wobei "n" die Anzahl der Tage ist.
+
+Wenn die Funktion den optionalen Starttag des Wochen-Parameters erhalten hat, dann sollte die Ausgabe den Wochentag des Ergebnisses anzeigen. Der Wochentag in der Ausgabe sollte nach der Zeit und vor der Anzahl der Tage später erscheinen.
+
+Unten findest du einige Beispiele für verschiedene Fälle, die deine Funktion bearbeiten soll. Achte auf den Abstand und auf die Satzzeichen der Ergebnisse.
+
+```py
+add_time("3:00 PM", "3:10")
+# Returns: 6:10 PM
+
+add_time("11:30 AM", "2:32", "Monday")
+# Returns: 2:02 PM, Monday
+
+add_time("11:43 AM", "00:20")
+# Returns: 12:03 PM
+
+add_time("10:10 PM", "3:30")
+# Returns: 1:40 AM (next day)
+
+add_time("11:43 PM", "24:20", "tueSday")
+# Returns: 12:03 AM, Thursday (2 days later)
+
+add_time("6:30 PM", "205:12")
+# Returns: 7:42 AM (9 days later)
+```
+
+Importiere keine Python-Bibliotheken. Nimm an, dass die Startzeiten gültige Zeiten sind. Die Minuten in der Zeitdauer werden als ganze Zahl, kleiner als 60 angezeigt. Die Stunde kann aber jede ganze Zahl sein.
+
+## Entwicklung
+
+Schreibe deinen Code in `time_calculator.py`. Für die Entwicklung kannst du `main.py` verwenden, um die `time_calculator()` Funktion zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+## Testen
+
+Die Unit-Tests für dieses Projekt sind in `test_module.py`. Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Es sollte die Zeiten korrekt hinzufügen und alle Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md
new file mode 100644
index 00000000000..1e9755478f5
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md
@@ -0,0 +1,47 @@
+---
+id: 5e9a093a74c4063ca6f7c14d
+title: 'Datenanalyse: Beispiel A'
+challengeType: 11
+videoId: nVAaxZ34khk
+bilibiliIds:
+ aid: 590571151
+ bvid: BV1sq4y1f7gr
+ cid: 409002372
+dashedName: data-analysis-example-a
+---
+
+# --description--
+
+*Anstatt notebooks.ai zu verwenden, wie es im Video gezeigt wird, kannst du stattdessen Google Colab verwenden.*
+
+Weitere Quellen:
+
+- Notebooks auf GitHub
+- Wie man Notebooks über Github mit Google Colab öffnet.
+
+# --question--
+
+## --text--
+
+Was teilt uns die Form unseres Dataframes mit?
+
+## --answers--
+
+Die Größe des in den Speicher geladenen Dataframes in Gigabyte.
+
+---
+
+Wie viele Zeilen und Spalten unser Dataframe hat.
+
+---
+
+Wie viele Zeilen die Quelldaten vor dem Laden hatten.
+
+---
+
+Wie viele Spalten die Quelldaten vor dem Laden hatten.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md
new file mode 100644
index 00000000000..130cc8e4929
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c14e
+title: 'Datenanalyse: Beispiel B'
+challengeType: 11
+videoId: 0kJz0q0pvgQ
+bilibiliIds:
+ aid: 505593432
+ bvid: BV1kg411c7M6
+ cid: 409003530
+dashedName: data-analysis-example-b
+---
+
+# --description--
+
+*Anstatt notebooks.ai zu verwenden, wie es im Video gezeigt wird, kannst du stattdessen Google Colab verwenden.*
+
+Weitere Quellen:
+
+- Notebooks auf GitHub
+- Wie man Notebooks über Github mit Google Colab öffnet.
+
+# --question--
+
+## --text--
+
+Was erlaubt dir die `loc` Methode?
+
+## --answers--
+
+Abrufen einer Teilmenge von Zeilen und Spalten durch Bereitstellung von Integer-Argumenten für die Position.
+
+---
+
+Zugriff auf eine Gruppe von Zeilen und Spalten durch Angabe von Label-Argumenten.
+
+---
+
+Übermittelt die ersten `n`-Zeilen anhand des übergebenen Integer-Arguments.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md
new file mode 100644
index 00000000000..0a1ba0babde
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md
@@ -0,0 +1,62 @@
+---
+id: 5e9a093a74c4063ca6f7c15d
+title: Einführung in die Datenbereinigung
+challengeType: 11
+videoId: ovYNhnltVxY
+bilibiliIds:
+ aid: 250574398
+ bvid: BV1Pv411A7GN
+ cid: 409018611
+dashedName: data-cleaning-introduction
+---
+
+# --description--
+
+*Anstatt, wie in dem Video gezeigt, notebooks.ai zu verwenden, kannst du auch Google Colab verwenden.*
+
+Weitere Ressourcen:
+
+- Notebooks auf GitHub
+- Wie man Notebooks über Github mit Google Colab öffnet.
+
+# --question--
+
+## --text--
+
+Was wird der folgende Code ausgeben?
+
+```py
+import pandas as pd
+import numpy as np
+
+s = pd.Series(['a', 3, np.nan, 1, np.nan])
+
+print(s.notnull().sum())
+```
+
+## --answers--
+
+3
+
+---
+
+0 True
+1 True
+2 False
+3 True
+4 False
+dtype: bool
+
+---
+
+0 False
+1 False
+2 True
+3 False
+4 True
+dtype: bool
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md
new file mode 100644
index 00000000000..4a9d98045b3
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md
@@ -0,0 +1,44 @@
+---
+id: 5e9a093a74c4063ca6f7c14c
+title: Einführung in die Datenanalyse
+challengeType: 11
+videoId: VJrP2FUzKP0
+bilibiliIds:
+ aid: 378034466
+ bvid: BV19f4y1c7nu
+ cid: 409001487
+dashedName: introduction-to-data-analysis
+---
+
+# --description--
+Unter Datenanalyse versteht man die Umwandlung ungeordneter Rohdaten in nützliche Erkenntnisse, indem die Daten bereinigt, umgewandelt, modifiziert und überprüft werden.
+
+Weitere Ressourcen:
+
+\- Nachrichtenartikel
+
+# --question--
+
+## --text--
+
+Welche der folgenden Antworten ist **nicht** Teil der Datenanalyse?
+
+## --answers--
+
+Erstellung statistischer Modelle und Datenvisualisierungen.
+
+---
+
+Auswahl einer gewünschten Schlussfolgerung für die Analyse.
+
+---
+
+Bereinigen falscher Werte und Entfernen ungültiger Daten.
+
+---
+
+Umwandeln von Daten in eine geeignete Datenstruktur.
+
+## --video-solution--
+
+2
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md
new file mode 100644
index 00000000000..07d44eb6f39
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md
@@ -0,0 +1,47 @@
+---
+id: 5e9a093a74c4063ca6f7c157
+title: Numpy-Algebra und Größe
+challengeType: 11
+videoId: XAT97YLOKD8
+bilibiliIds:
+ aid: 250621433
+ bvid: BV1hv41137uM
+ cid: 409013128
+dashedName: numpy-algebra-and-size
+---
+
+# --description--
+
+*Anstatt, wie in dem Video gezeigt, notebooks.ai zu verwenden, kannst du auch Google Colab verwenden.*
+
+Weitere Ressourcen:
+
+- Notebooks auf GitHub
+- Wie man Notebooks über Github mit Google Colab öffnet.
+
+# --question--
+
+## --text--
+
+Was ist die Beziehung zwischen der Größe von Objekten (wie Listen und Datentypen) im Speicher in Pythons Standardbibliothek und der NumPy Bibliothek? Was sind die Auswirkungen auf die Leistung?
+
+## --answers--
+
+Standard Python-Objekte benötigen viel mehr Speicher als NumPy-Objekte; Operationen an ähnlichen Standard-Python- und NumPy-Objekten benötigen etwa gleich viel Zeit.
+
+---
+
+NumPy-Objekte benötigen viel mehr Speicher als standardmäßige Python-Objekte; die Operationen an NumPy-Objekten sind im Vergleich zu ähnlichen Standard-Python-Objekten sehr schnell abgeschlossen.
+
+---
+
+NumPy-Objekte benötigen viel weniger Speicher als Standard-Python-Objekte; die Operationen an Standard-Python-Objekten sind im Vergleich zu ähnlichen NumPy-Objekten sehr schnell abeschlossen.
+
+---
+
+Standard-Python-Objekte benötigen mehr Speicher als NumPy-Objekte; Operationen mit NumPy-Objekten werden im Vergleich zu vergleichbaren Objekten in Standard-Python sehr schnell abgeschlossen.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md
new file mode 100644
index 00000000000..71b17827a7e
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md
@@ -0,0 +1,63 @@
+---
+id: 5e9a093a74c4063ca6f7c154
+title: Numpy Arrays
+challengeType: 11
+videoId: VDYVFHBL1AM
+bilibiliIds:
+ aid: 890607366
+ bvid: BV1zP4y1h7FR
+ cid: 409011400
+dashedName: numpy-arrays
+---
+
+# --description--
+
+*Anstatt, wie in dem Video gezeigt, notebooks.ai zu verwenden, kannst du auch Google Colab verwenden.*
+
+Weitere Ressourcen:
+
+- Notebooks auf GitHub
+- Wie man Notebooks von GitHub unter Verwendung von Google Colab öffnet.
+
+# --question--
+
+## --text--
+
+Was wird der folgende Code ausgeben?
+
+```py
+A = np.array([
+ ['a', 'b', 'c'],
+ ['d', 'e', 'f'],
+ ['g', 'h', 'i']
+])
+
+print(A[:, :2])
+```
+
+## --answers--
+
+```py
+[['a' 'b']]
+```
+
+---
+
+```py
+[['b' 'c']
+['e' 'f']
+['h' 'i']]
+```
+
+---
+
+```py
+[['a' 'b']
+['d' 'e']
+['g' 'h']]
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md
new file mode 100644
index 00000000000..6a3a76d5a56
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md
@@ -0,0 +1,79 @@
+---
+id: 5e46f7e5ac417301a38fb929
+title: Demografischer Datenanalysator
+challengeType: 10
+forumTopicId: 462367
+dashedName: demographic-data-analyzer
+---
+
+# --description--
+
+Du wirst an diesem Projekt mit unserem Replit-Startercode arbeiten .
+
+Wir sind noch dabei, den interaktiven Teil des Python-Kurses zu entwickeln. Hier sind erstmal einige Videos auf dem freeCodeCamp.org YouTube-Kanal, die dir alles beibringen, was du wissen musst, um dieses Projekt abzuschließen:
+
+- Videokurs: Python für jedermann (14 Stunden)
+
+- Wie man Daten mit Python-Pandas analysiert (10 Stunden)
+
+# --instructions--
+
+Bei dieser Aufgabe musst du demographische Daten mit Hilfe von Pandas analysieren. Du erhälst einen Datensatz von demographischen Daten, die aus der Datenbank der Volkszählung 1994 gewonnen wurden. Hier ist ein Beispiel dafür, wie die Daten aussehen:
+
+```markdown
+| | age | workclass | fnlwgt | education | education-num | marital-status | occupation | relationship | race | sex | capital-gain | capital-loss | hours-per-week | native-country | salary |
+|---:|------:|:-----------------|---------:|:------------|----------------:|:-------------------|:------------------|:---------------|:-------|:-------|---------------:|---------------:|-----------------:|:-----------------|:---------|
+| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
+| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
+| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
+| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
+| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
+```
+
+Du musst Pandas verwenden, um die folgenden Fragen zu beantworten:
+
+- Wie viele Personen jeder ethnischen Gruppe sind in diesem Datensatz vertreten? Dies sollte eine Pandas-Reihe mit den Namen der verschiedenen Ethnien als Indexbezeichnungen sein. (`race` Spalte)
+- Wie hoch ist das Durchschnittsalter der Männer?
+- Wie hoch ist der Prozentsatz von Menschen, die einen Bachelorabschluss haben?
+- Wie hoch ist der Prozentsatz der Menschen mit einer weiterführenden Bildung (`Bachelors`, `Masters`, oder `Doctorate`), die mehr als 50K verdienen?
+- Wie hoch ist der Prozentsatz der Menschen ohne weiterführende Bildung, die mehr als 50K verdienen?
+- Wie hoch ist die Mindestanzahl an Arbeitsstunden, die eine Person pro Woche arbeitet?
+- Wie hoch ist der Prozentsatz von Menschen, die die Mindestarbeitszeit arbeiten und mehr als 50K verdienen?
+- Welches Land hat den höchsten Prozentsatz an Menschen, die >50K verdienen und wie viel Prozent sind es?
+- Identifiziere die verbreitetste Tätigkeit derjenigen, die in Indien >50K verdienen.
+
+Benutze den Starter-Code in der Datei `demographic_data_analyzer`. Aktualisiere den Code, damit alle Variablen, die auf "Keine" gesetzt sind, auf die entsprechende Berechnung oder den entsprechenden Code gesetzt werden. Runde alle Dezimalstellen auf die nächste Zehnerstelle.
+
+Unit-Tests werden für dich unter `test_module.py` geschrieben.
+
+## Entwicklung
+
+Für die Entwicklung kannst du `main.py` verwenden, um deinen Code zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+## Testen
+
+Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und übermittle sie an freeCodeCamp.
+
+## Datensatzquelle
+
+Dua, D. and Graff, C. (2019). UCI Machine Learning Repository . Irvine, CA: University of California, School of Information and Computer Science.
+
+# --hints--
+
+Es sollte alle Python-Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md
new file mode 100644
index 00000000000..02d72724f1e
--- /dev/null
+++ b/curriculum/challenges/german/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md
@@ -0,0 +1,81 @@
+---
+id: 5e46f7e5ac417301a38fb928
+title: Berechnungsprogramm für Mittelwert, Varianz, und Standardabweichung
+challengeType: 10
+forumTopicId: 462366
+dashedName: mean-variance-standard-deviation-calculator
+---
+
+# --description--
+
+Du wirst mit unserem Replit-Startercode an diesem Projekt arbeiten .
+
+Wir sind noch dabei, den interaktiven Teil des Python-Kurses zu entwickeln. Hier sind erstmal einige Videos auf dem freeCodeCamp.org YouTube-Kanal, die dir alles beibringen, was du wissen musst, um dieses Projekt abzuschließen:
+
+- Videokurs: Python für jedermann (14 Stunden)
+
+- Wie man Daten mit Python-Pandas analysiert (10 Stunden)
+
+# --instructions--
+
+Erstelle eine Funktion namens `calculate()` in `mean_var_std.py`, die Numpy benutzt, um den Mittelwert, die Varianz, die Standardabweichung, Max, Min, und die Summe der Zeilen, Spalten und Elemente in einer 3 x 3 Matrix anzugeben.
+
+Die Eingabe der Funktion sollte eine Liste mit 9 Ziffern sein. Die Funktion sollte die Liste in ein 3 x 3 Numpy Array konvertieren und dann ein Wörterbuch mit dem Mittelwert, der Varianz, der Standardabweichung, Max, Min, sowie die Summe entlang beider Achsen und für die abgeflachte Matrix zurückgeben.
+
+Das zurückgegebene Wörterbuch sollte diesem Format folgen:
+
+```py
+{
+ 'mean': [axis1, axis2, flattened],
+ 'variance': [axis1, axis2, flattened],
+ 'standard deviation': [axis1, axis2, flattened],
+ 'max': [axis1, axis2, flattened],
+ 'min': [axis1, axis2, flattened],
+ 'sum': [axis1, axis2, flattened]
+}
+```
+
+Wenn eine Liste mit weniger als 9 Elementen an die Funktion übergeben wird, sollte eine `ValueError`-Ausnahme mit der Nachricht "Liste muss neun Nummern enthalten." auftauchen. Die Werte im zurückgegebenen Wörterbuch sollten Listen und keine Numpy Arrays sein.
+
+Zum Beispiel sollte `calculate([0,1,2,3,4,5,6,7,8])` zurückgeben:
+
+```py
+{
+ 'mean': [[3.0, 4.0, 5.0], [1.0, 4.0, 7.0], 4.0],
+ 'variance': [[6.0, 6.0, 6.0], [0.6666666666666666, 0.6666666666666666, 0.6666666666666666], 6.666666666666667],
+ 'standard deviation': [[2.449489742783178, 2.449489742783178, 2.449489742783178], [0.816496580927726, 0.816496580927726, 0.816496580927726], 2.581988897471611],
+ 'max': [[6, 7, 8], [2, 5, 8], 8],
+ 'min': [[0, 1, 2], [0, 3, 6], 0],
+ 'sum': [[9, 12, 15], [3, 12, 21], 36]
+}
+```
+
+Die Unit-Tests für dieses Projekt sind in `test_module.py`.
+
+## Entwicklung
+
+Für die Entwicklung kannst du `main.py` verwenden, um die `calculate()` Funktion zu testen. Klicke den "Run"-Button und `main.py` wird ausgeführt.
+
+## Testen
+
+Wir haben die Tests von `test_module.py` zu `main.py` bereits für dich importiert. Die Tests werden automatisch ausgeführt, wenn du auf den "Run"-Button klickst.
+
+## Absenden
+
+Kopiere die URL deines Projekts und sende sie an freeCodeCamp.
+
+# --hints--
+
+Es sollte alle Python-Tests bestehen.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/german/09-information-security/python-for-penetration-testing/developing-an-nmap-scanner-part-2.md b/curriculum/challenges/german/09-information-security/python-for-penetration-testing/developing-an-nmap-scanner-part-2.md
new file mode 100644
index 00000000000..1df69c11498
--- /dev/null
+++ b/curriculum/challenges/german/09-information-security/python-for-penetration-testing/developing-an-nmap-scanner-part-2.md
@@ -0,0 +1,34 @@
+---
+id: 5ea9997bbec2e9bc47e94db2
+title: Entwicklung eines Nmap-Scanners Teil 2
+challengeType: 11
+videoId: a98PscnUsTg
+bilibiliIds:
+ aid: 505526943
+ bvid: BV1Hg411c7oE
+ cid: 409034761
+dashedName: developing-an-nmap-scanner-part-2
+---
+
+# --question--
+
+## --text--
+
+Mit welcher der folgenden Optionen kannst du nach UDP-Ports zwischen 21 und 443 suchen?
+
+## --answers--
+
+`.scan(ip_addr, '21-443', '-v -sU')`
+
+---
+
+`.scan(ip_addr, '1-1024', '-v -sS')`
+
+---
+
+`.scan(ip_addr, '21-443', '-v -sS')`
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/build-a-personal-portfolio-webpage-project/build-a-personal-portfolio-webpage.md b/curriculum/challenges/german/14-responsive-web-design-22/build-a-personal-portfolio-webpage-project/build-a-personal-portfolio-webpage.md
new file mode 100644
index 00000000000..2290577c9d0
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/build-a-personal-portfolio-webpage-project/build-a-personal-portfolio-webpage.md
@@ -0,0 +1,281 @@
+---
+id: bd7158d8c242eddfaeb5bd13
+title: Eine persönliche Portfolio-Webseite erstellen
+challengeType: 14
+forumTopicId: 301143
+dashedName: build-a-personal-portfolio-webpage
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://personal-portfolio.freecodecamp.rocks aufweist.
+
+**User Stories:**
+
+1. Dein Portfolio sollte einen Begrüßungsbereich mit einer `id` von `welcome-section` besitzen
+1. Der Begrüßungsbereich sollte ein `h1` Element mit Text enthalten
+1. Dein Portfolio sollte einen Projektabschnitt mit einer `id` von `projects` besitzen
+1. Der Projektabschnitt sollte mindestens ein Element mit einer `class` von `project-tile` besitzen, um das Projekt zu halten
+1. Der Projektabschnitt sollte mindestens einen Link zu einem Projekt enthalten
+1. Dein Portfolio sollte eine Navigationsleiste mit einer Id von `navbar` haben
+1. Die Navigationsleiste sollte mindestens einen auswählbaren Link haben, der zu verschiedenen Bereichen der Seite navigiert
+1. Dein Portfolio sollte einen Link mit der ID `profile-link` haben, der dein GitHub- oder freeCodeCamp-Profil in einem neuen Tab öffnet
+1. Dein Portfolio sollte mindestens eine Media Query (Medienabfrage) enthalten
+1. Die Höhe des Begrüßungsabschnitts sollte der Höhe des Ansichtsfensters entsprechen
+1. Die Navigationsleiste sollte sich immer am oberen Rand des Ansichtsfensters befinden
+
+Erfülle die folgenden User Stories und bestehe alle Tests, um dieses Projekt abzuschließen. Gib dem Ganzen deinen persönlichen Stil. Viel Spaß beim Programmieren!
+
+**Hinweis:** Füge unbedingt ` ` in dein HTML ein, um dein Stylesheet zu verlinken und dein CSS anzuwenden
+
+# --hints--
+
+Dein Portfolio sollte einen Begrüßungsbereich mit einer `id` von `welcome-section` beinhalten.
+
+```js
+const el = document.getElementById('welcome-section')
+assert(!!el);
+```
+
+Dein `#welcome-section` Element sollte ein `h1` Element beinhalten.
+
+```js
+assert.isAbove(
+ document.querySelectorAll('#welcome-section h1').length,
+ 0,
+ 'Welcome section should contain an h1 element '
+);
+```
+
+Du solltest keine leeren `h1` Elemente innerhalb des `#welcome-section` Elements haben.
+
+```js
+assert.isAbove(
+ document.querySelectorAll('#welcome-section h1')?.[0]?.innerText?.length,
+ 0,
+ 'h1 element in welcome section should contain your name or camper ' +
+ 'name '
+);
+```
+
+Du solltest einen Projektabschnitt mit einer `id` von `projects` haben.
+
+```js
+const el = document.getElementById('projects')
+assert(!!el);
+```
+
+Dein Portfolio sollte mindestens ein Element mit einer Klasse von `project-tile` haben.
+
+```js
+assert.isAbove(
+ document.querySelectorAll('#projects .project-tile').length,
+ 0
+);
+```
+
+Dein `#projects` Element sollte mindestens ein `a` Element beinhalten.
+
+```js
+assert.isAbove(document.querySelectorAll('#projects a').length, 0);
+```
+
+Dein Portfolio sollte eine Navigationsleiste mit der `id` von `navbar` besitzen.
+
+```js
+const el = document.getElementById('navbar');
+assert(!!el);
+```
+
+Dein `#navbar` Element sollte mindestens ein `a` Element, dessen `href` Attribut mit `#` beginnt, haben.
+
+```js
+const links = [...document.querySelectorAll('#navbar a')].filter(
+ (nav) => (nav?.getAttribute('href') || '').substr(0, 1) === '#'
+);
+
+assert.isAbove(
+ links.length,
+ 0,
+ 'Navbar should contain an anchor link '
+);
+```
+
+Dein Portfolio sollte ein `a` Element mit einer `id` von `profile-link` haben.
+
+```js
+const el = document.getElementById('profile-link');
+assert(!!el && el.tagName === 'A')
+```
+
+Dein `#profile-link` Element sollte ein `target` Attribut von `_blank` haben.
+
+```js
+const el = document.getElementById('profile-link');
+assert(!!el && el.target === '_blank')
+```
+
+Dein Portfolio sollte mindestens eine Media Query (Medienabfrage) enthalten.
+
+```js
+const htmlSourceAttr = Array.from(document.querySelectorAll('source')).map(el => el.getAttribute('media'))
+const cssCheck = new __helpers.CSSHelp(document).getCSSRules('media')
+assert(cssCheck.length > 0 || htmlSourceAttr.length > 0);
+```
+
+Dein `#navbar` Element sollte immer oben im Viewport liegen.
+
+```js
+(async () => {
+ const timeout = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
+
+ const navbar = document.getElementById('navbar');
+ assert.approximately(
+ navbar?.getBoundingClientRect().top,
+ 0,
+ 15,
+ "Navbar's parent should be body and it should be at the top of " +
+ 'the viewport '
+ );
+
+ window.scroll(0, 500);
+
+ await timeout(1);
+
+ assert.approximately(
+ navbar?.getBoundingClientRect().top,
+ 0,
+ 15,
+ 'Navbar should be at the top of the viewport even after ' +
+ 'scrolling '
+ );
+ window.scroll(0, 0);
+})();
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+```
+
+```css
+
+```
+
+## --solutions--
+
+```html
+
+
+
+
+
+ Personal Portfolio
+
+
+
+
+
+ Projects |
+ Contact me
+
+
+
+
+ It's me!
+
+ Naomi Carrigan
+ Welcome to my portfolio page!
+
+
+
+
+
+
+```
+
+```css
+nav{
+ position: fixed;
+ width: 100%;
+ text-align: right;
+ font-size: 24pt;
+ top: 0%;
+ right: 5px;
+ background-color: #000000;
+ color: #ffffff;
+}
+@media (max-width: 500px){
+ nav{
+ display: none;
+ }
+}
+a{
+ color: #ffffff;
+}
+main{
+ text-align: center;
+ background-color: black;
+ font-family:Pacifico
+}
+h1{
+ font-size: 48pt;
+}
+h2{
+ font-size: 24pt;
+}
+p{
+ font-size: 12pt;
+}
+#welcome-section{
+ background-color:#251a4a;
+ color: #FFFFFF;
+ display: table-cell;
+ vertical-align: middle;
+ width: 100vw;
+ height: 100vh;
+}
+#projects{
+ background-color: #060a9c;
+ color: #ffffff;
+ display: table-cell;
+ vertical-align: middle;
+ width: 100vw;
+ height: 100vh;
+}
+#contact{
+ background-color: #03300b;
+ color: #ffffff;
+ display: table-cell;
+ vertical-align: middle;
+ width: 100vw;
+ height: 100vh;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613297a923965e0703b64796.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613297a923965e0703b64796.md
new file mode 100644
index 00000000000..aedfe664aa7
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613297a923965e0703b64796.md
@@ -0,0 +1,56 @@
+---
+id: 613297a923965e0703b64796
+title: Schritt 2
+challengeType: 0
+dashedName: step-2
+---
+
+# --description--
+
+Vielleicht kennst du bereits das `meta`-Element; es wird dazu verwendet, bestimmte Informationen über die Seite, wie beispielsweise Titel, Beschreibung, Schlüsselwörter und Autor anzugeben.
+
+Gib deiner Seite ein `meta`-Element mit einem entsprechenden `charset`-Wert.
+
+Das `charset`-Attribut bestimmt über die Zeichenkodierung der Seite. Heutzutage ist `UTF-8` die einzige Kodierung, die von den meisten Browsern unterstützt wird.
+
+# --hints--
+
+Du solltest ein `meta`-Element innerhalb eines `head`-Elements erstellen.
+
+```js
+assert.exists(document.querySelector('head > meta'));
+```
+
+Du solltest dem `meta`-Tag ein `charset` von `UTF-8` geben.
+
+```js
+assert.equal(document.querySelector('head > meta')?.getAttribute('charset')?.toLowerCase(), 'utf-8');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+--fcc-editable-region--
+
+
+
+--fcc-editable-region--
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61329d52e5010e08d9b9d66b.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61329d52e5010e08d9b9d66b.md
new file mode 100644
index 00000000000..364b18479b6
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61329d52e5010e08d9b9d66b.md
@@ -0,0 +1,68 @@
+---
+id: 61329d52e5010e08d9b9d66b
+title: Schritt 4
+challengeType: 0
+dashedName: step-4
+---
+
+# --description--
+
+Ein weiteres wichtiges `meta` Element für Accessibility und SEO ist die `description` Definition. Der Wert des Attributs `content` wird von Suchmaschinen verwendet, um eine Beschreibung deiner Seite bereitzustellen.
+
+Füge ein `meta` Element mit einem `name` Attribut von `description` hinzu, and gebe ihm ein nützliches `content` Attribut.
+
+# --hints--
+
+Du solltest ein neues `meta` Element zu dem `head` hinzufügen.
+
+```js
+assert.equal(document.querySelectorAll('meta').length, 3);
+```
+
+Du solltest `meta` ein `name` Attribut mit `description` geben.
+
+```js
+assert.exists(document.querySelector('meta[name="description"]'));
+```
+
+Du solltest `meta` ein `content` Attribut geben.
+
+```js
+assert.notEmpty(document.querySelector('meta[name="description"]')?.content);
+```
+
+Der `content` Attributwert sollte nicht mehr als 165 Zeichen lang sein. _Das ist die maximale Beschreibungslänge von Google._
+
+```js
+assert.isAtMost(document.querySelector('meta[name="description"]')?.content?.length, 165);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+--fcc-editable-region--
+
+
+
+
+
+--fcc-editable-region--
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6133acc353338c0bba9cb553.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6133acc353338c0bba9cb553.md
new file mode 100644
index 00000000000..200679cd743
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6133acc353338c0bba9cb553.md
@@ -0,0 +1,63 @@
+---
+id: 6133acc353338c0bba9cb553
+title: Schritt 5
+challengeType: 0
+dashedName: step-5
+---
+
+# --description--
+
+Schließlich ist das Element `title` im `head` für Bildschirmleseprogramme nützlich, um den Seiteninhalt zu verstehen. Außerdem ist es ein wichtiger Bestandteil des _SEO_.
+
+Gib deiner Seite einen beschreibenden und prägnanten `title`.
+
+# --hints--
+
+Du sollest dem `head` ein `title`-Element hinzufügen.
+
+```js
+assert.exists(document.querySelector('head > title'));
+```
+
+Der `title` sollte nicht länger als 60 Zeichen sein.
+
+```js
+assert.isAtMost(document.querySelector('head > title')?.textContent?.length, 60);
+```
+
+Versuche, einen besser beschreibenden `title` zu finden. _Tipp: Mindestens 15 Zeichen_
+
+```js
+assert.isAtLeast(document.querySelector('head > title')?.textContent?.length, 15);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+--fcc-editable-region--
+
+
+
+
+
+
+--fcc-editable-region--
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e2546d0594208229ada50.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e2546d0594208229ada50.md
new file mode 100644
index 00000000000..2e23a6e14e1
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e2546d0594208229ada50.md
@@ -0,0 +1,93 @@
+---
+id: 613e2546d0594208229ada50
+title: Schritt 7
+challengeType: 0
+dashedName: step-7
+---
+
+# --description--
+
+Verschachtele die Elemente `img`, `h1` und `nav` im `header`, um deiner Seite Kontext zu verleihen.
+
+Hierbei sollte `img` auf `https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg` verweisen und eine auf `logo` gesetzte `id` haben.
+
+Das Element `h1` sollte den Text `HTML/CSS Quiz` enthalten.
+
+# --hints--
+
+Du solltest das Element `img` dem `header` hinzufügen.
+
+```js
+assert.exists(document.querySelector('header > img'));
+```
+
+Du solltest das Element `h1` dem `header` hinzufügen.
+
+```js
+assert.exists(document.querySelector('header > h1'));
+```
+
+Du solltest das Element `nav` dem `header` hinzufügen.
+
+```js
+assert.exists(document.querySelector('header > nav'));
+```
+
+Du solltest die Elemente `img`, `h1` und `nav` in genau dieser Reihenfolge platzieren.
+
+```js
+assert.exists(document.querySelector('img + h1 + nav'));
+```
+
+Du solltest dem Element `img` ein `src`-Attribut mit dem Wert `https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg` zuweisen.
+
+```js
+assert.equal(document.querySelector('img')?.src, 'https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg');
+```
+
+Du solltest dem Element `img` ein `id`-Attribut mit dem Wert `logo` zuweisen.
+
+```js
+assert.equal(document.querySelector('img')?.id, 'logo');
+```
+
+Du solltest dem Element `h1` den Text `HTML/CSS Quiz` geben.
+
+```js
+assert.include(document.querySelector('h1')?.innerText?.toLowerCase(), 'html/css quiz');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e275749ebd008e74bb62e.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e275749ebd008e74bb62e.md
new file mode 100644
index 00000000000..6ddd6317a11
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/613e275749ebd008e74bb62e.md
@@ -0,0 +1,66 @@
+---
+id: 613e275749ebd008e74bb62e
+title: Schritt 8
+challengeType: 0
+dashedName: step-8
+---
+
+# --description--
+
+Eine nützliche Eigenschaft einer _SVG_ (scalable vector graphics, skalierbare Vektorgrafiken) ist, dass dieses ein `path`-Attribut enthält, mit welchem ein Bild, ohne die Auflösung des Resultats zu beeinflussen, skaliert werden kann.
+
+Derzeit übernimmt das `img` die Ursprungsgröße, welche aber zu groß ist. Korrigiere das, indem du das Bild mithilfe des Selektors `id` auf eine `width` von `max(100px, 18vw)` stellst.
+
+# --hints--
+
+Du solltest den Selektor `#logo` verwenden, um das `img`-Element auszuwählen.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('#logo'));
+```
+
+Du solltest dem `img` eine `width` von `max(100px, 18vw)` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('#logo')?.width, 'max(100px, 18vw)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61408e4ae3e35d08feb260eb.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61408e4ae3e35d08feb260eb.md
new file mode 100644
index 00000000000..ce8652ecc0d
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/61408e4ae3e35d08feb260eb.md
@@ -0,0 +1,83 @@
+---
+id: 61408e4ae3e35d08feb260eb
+title: Schritt 11
+challengeType: 0
+dashedName: step-11
+---
+
+# --description--
+
+Ändere die `h1` Schriftfarbe auf `#f1be32` und setze die Schriftgröße auf `min(5vw, 1.2em)`.
+
+# --hints--
+
+Du solltest den `h1` Element-Selektor verwenden.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('h1'));
+```
+
+Du solltest `h1` eine `color` von `#f1be32` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('h1')?.color, 'rgb(241, 190, 50)');
+```
+
+Du solltest `h1` eine `font-size` von `min(5vw, 1.2em)` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('h1')?.fontSize, 'min(5vw, 1.2em)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614090d5a22b6f0a5a6b464c.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614090d5a22b6f0a5a6b464c.md
new file mode 100644
index 00000000000..07fd9774523
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614090d5a22b6f0a5a6b464c.md
@@ -0,0 +1,102 @@
+---
+id: 614090d5a22b6f0a5a6b464c
+title: Schritt 13
+challengeType: 0
+dashedName: step-13
+---
+
+
+
+# --description--
+
+Richte die ungeordneten Listenelemente innerhalb der `nav` Elemente aus und verwende _Flexbox_, um die untergeordneten Elemente gleichmäßig zu verteilen.
+
+# --hints--
+
+Du solltest den `nav > ul` Selektor verwenden.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('nav > ul'));
+```
+
+Du solltest den `nav > ul` Elementen ein `display` von `flex` zuweisen.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('nav > ul')?.display, 'flex');
+```
+
+Du solltest dem `nav > ul` Element ein `justify-content` von `space-evenly` zuweisen.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('nav > ul')?.justifyContent, 'space-evenly');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614394fb41985e0d2012a93e.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614394fb41985e0d2012a93e.md
new file mode 100644
index 00000000000..2f90c609fd9
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614394fb41985e0d2012a93e.md
@@ -0,0 +1,125 @@
+---
+id: 614394fb41985e0d2012a93e
+title: Schritt 24
+challengeType: 0
+dashedName: step-24
+---
+
+# --description--
+
+Füge innerhalb des `span` Elements den Text `(Date of Birth)` hinzu.
+
+# --hints--
+
+Du solltest dem `span` Element den Text `(Date of Birth)` geben.
+
+```js
+assert.equal(document.querySelector('.info:nth-of-type(3) > label > span')?.textContent, '(Date of Birth)');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614880dc16070e093e29bc56.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614880dc16070e093e29bc56.md
new file mode 100644
index 00000000000..77b5acf0dc8
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/614880dc16070e093e29bc56.md
@@ -0,0 +1,259 @@
+---
+id: 614880dc16070e093e29bc56
+title: Schritt 51
+challengeType: 0
+dashedName: step-51
+---
+
+# --description--
+
+Ersetze den oberen Rand der `h2` Elemente durch `60px` des oberen Paddings.
+
+# --hints--
+
+Du solltest `h2` ein `margin-top` von `0` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('h2')?.marginTop, '0px');
+```
+
+Du solltest `h2` ein `padding-top` von `60px` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('h2')?.paddingTop, '60px');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+ text-align: center;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ color: #dfdfe2;
+ margin: 0 0.2rem;
+ padding: 0.2rem;
+ display: block;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0 auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+--fcc-editable-region--
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+
+}
+--fcc-editable-region--
+
+p::before {
+ content: "Question #";
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e0bcc13efd10f7d7a6a9.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e0bcc13efd10f7d7a6a9.md
new file mode 100644
index 00000000000..e22f6d07981
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e0bcc13efd10f7d7a6a9.md
@@ -0,0 +1,336 @@
+---
+id: 6148e0bcc13efd10f7d7a6a9
+title: Schritt 62
+challengeType: 0
+dashedName: step-62
+---
+
+# --description--
+
+Setze die `footer` Hintergrundfarbe auf `#2a2a40` und verwende _Flexbox_, um den Text horizontal zu zentrieren.
+
+# --hints--
+
+Du solltest den `footer` Element-Selektor verwenden.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('footer'));
+```
+
+Du solltest dem `footer` eine `background-color` von `#2a2a40` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('footer')?.backgroundColor, 'rgb(42, 42, 64)');
+```
+
+Du solltest dem `footer` ein `display` von `flex` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('footer')?.display, 'flex');
+```
+
+Du solltest dem `footer` ein `justify-content` von `center` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('footer')?.justifyContent, 'center');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+ HTML
+
+
1
+
+
+ The legend element represents a caption for the content of its
+ parent fieldset element
+
+
+
+
+
+
2
+
+
+ A label element nesting an input element is required to have a
+ for attribute with the same value as the input's id
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+ text-align: center;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ color: #dfdfe2;
+ margin: 0 0.2rem;
+ padding: 0.2rem;
+ display: block;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0 auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+.info {
+ padding: 10px 0 0 5px;
+}
+
+.formrow {
+ margin-top: 30px;
+ padding: 0px 15px;
+}
+
+input {
+ font-size: 16px;
+}
+
+.info label, .info input {
+ display: inline-block;
+ text-align: right;
+}
+
+.info input {
+ width: 50%;
+ text-align: left;
+}
+
+.info label {
+ width: 10%;
+ min-width: 55px;
+}
+
+.question-block {
+ text-align: left;
+ display: block;
+ width: 100%;
+ margin-top: 20px;
+ padding-top: 5px;
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 15px;
+ font-size: 20px;
+}
+
+p::before {
+ content: "Question #";
+}
+
+.question {
+ border: none;
+ padding-bottom: 0;
+}
+
+.answers-list {
+ list-style: none;
+ padding: 0;
+}
+
+button {
+ display: block;
+ margin: 40px auto;
+ width: 40%;
+ padding: 15px;
+ font-size: 23px;
+ background: #d0d0d5;
+ border: 3px solid #3b3b4f;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e28706b34912340fd042.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e28706b34912340fd042.md
new file mode 100644
index 00000000000..47a984abb3f
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e28706b34912340fd042.md
@@ -0,0 +1,359 @@
+---
+id: 6148e28706b34912340fd042
+title: Schritt 64
+challengeType: 0
+dashedName: step-64
+---
+
+# --description--
+
+Zentriere den gesamten Text horizontal innerhalb des `address`-Elements und füge einige Padding-Einheiten hinzu.
+
+# --hints--
+
+Du solltest den `address` Element-Selektor verwenden.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('address'));
+```
+
+Du solltest `address` ein `text-align` von `center` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('address')?.textAlign, 'center');
+```
+
+Du solltest `address` ein `padding-top` von mindestens `1px` geben.
+
+```js
+assert.isAtLeast(Number(window.getComputedStyle(document.querySelector('address'), null)?.getPropertyValue('padding-top')?.replace(/\D\D+/, '')), 1);
+```
+
+Du solltest `address` ein `padding-right` von mindestens `1px` geben.
+
+```js
+assert.isAtLeast(Number(window.getComputedStyle(document.querySelector('address'), null)?.getPropertyValue('padding-right')?.replace(/\D\D+/, '')), 1);
+```
+
+Du solltest `address` ein `padding-bottom` von mindestens `1px` geben.
+
+```js
+assert.isAtLeast(Number(window.getComputedStyle(document.querySelector('address'), null)?.getPropertyValue('padding-bottom')?.replace(/\D\D+/, '')), 1);
+```
+
+Du solltest `address` ein `padding-left` von mindestens `1px` geben.
+
+```js
+assert.isAtLeast(Number(window.getComputedStyle(document.querySelector('address'), null)?.getPropertyValue('padding-left')?.replace(/\D\D+/, '')), 1);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+ HTML
+
+
1
+
+
+ The legend element represents a caption for the content of its
+ parent fieldset element
+
+
+
+
+
+
2
+
+
+ A label element nesting an input element is required to have a
+ for attribute with the same value as the input's id
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+```
+
+```css
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+ text-align: center;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ color: #dfdfe2;
+ margin: 0 0.2rem;
+ padding: 0.2rem;
+ display: block;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0 auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+.info {
+ padding: 10px 0 0 5px;
+}
+
+.formrow {
+ margin-top: 30px;
+ padding: 0px 15px;
+}
+
+input {
+ font-size: 16px;
+}
+
+.info label, .info input {
+ display: inline-block;
+ text-align: right;
+}
+
+.info input {
+ width: 50%;
+ text-align: left;
+}
+
+.info label {
+ width: 10%;
+ min-width: 55px;
+}
+
+.question-block {
+ text-align: left;
+ display: block;
+ width: 100%;
+ margin-top: 20px;
+ padding-top: 5px;
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 15px;
+ font-size: 20px;
+}
+
+p::before {
+ content: "Question #";
+}
+
+.question {
+ border: none;
+ padding-bottom: 0;
+}
+
+.answers-list {
+ list-style: none;
+ padding: 0;
+}
+
+button {
+ display: block;
+ margin: 40px auto;
+ width: 40%;
+ padding: 15px;
+ font-size: 23px;
+ background: #d0d0d5;
+ border: 3px solid #3b3b4f;
+}
+
+footer {
+ background-color: #2a2a40;
+ display: flex;
+ justify-content: center;
+}
+
+footer,
+footer a {
+ color: #dfdfe2;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e335c1edd512d00e4691.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e335c1edd512d00e4691.md
new file mode 100644
index 00000000000..2152a6882a5
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e335c1edd512d00e4691.md
@@ -0,0 +1,342 @@
+---
+id: 6148e335c1edd512d00e4691
+title: Schritt 65
+challengeType: 0
+dashedName: step-65
+---
+
+# --description--
+
+Wenn du auf die Navigationslinks klickst, sollte das Ansichtsfenster zu dem entsprechenden Abschnitt springen. Dieses Springen kann jedoch für manche Benutzer verwirrend sein.
+
+Wähle alle Elemente aus und setze das `scroll-behavior` auf `smooth`.
+
+# --hints--
+
+Du solltest den Selektor `*` verwenden.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('*'));
+```
+
+Du solltest `*` ein `scroll-behavior` von `smooth` geben.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('*')?.scrollBehavior, 'smooth');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+ HTML
+
+
1
+
+
+ The legend element represents a caption for the content of its
+ parent fieldset element
+
+
+
+
+
+
2
+
+
+ A label element nesting an input element is required to have a
+ for attribute with the same value as the input's id
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+```
+
+```css
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+ text-align: center;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ color: #dfdfe2;
+ margin: 0 0.2rem;
+ padding: 0.2rem;
+ display: block;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0 auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+.info {
+ padding: 10px 0 0 5px;
+}
+
+.formrow {
+ margin-top: 30px;
+ padding: 0px 15px;
+}
+
+input {
+ font-size: 16px;
+}
+
+.info label, .info input {
+ display: inline-block;
+ text-align: right;
+}
+
+.info input {
+ width: 50%;
+ text-align: left;
+}
+
+.info label {
+ width: 10%;
+ min-width: 55px;
+}
+
+.question-block {
+ text-align: left;
+ display: block;
+ width: 100%;
+ margin-top: 20px;
+ padding-top: 5px;
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 15px;
+ font-size: 20px;
+}
+
+p::before {
+ content: "Question #";
+}
+
+.question {
+ border: none;
+ padding-bottom: 0;
+}
+
+.answers-list {
+ list-style: none;
+ padding: 0;
+}
+
+button {
+ display: block;
+ margin: 40px auto;
+ width: 40%;
+ padding: 15px;
+ font-size: 23px;
+ background: #d0d0d5;
+ border: 3px solid #3b3b4f;
+}
+
+footer {
+ background-color: #2a2a40;
+ display: flex;
+ justify-content: center;
+}
+
+footer,
+footer a {
+ color: #dfdfe2;
+}
+
+address {
+ text-align: center;
+ padding: 0.3em;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e5aeb102e3142de026a2.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e5aeb102e3142de026a2.md
new file mode 100644
index 00000000000..3cc7276c981
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-accessibility-by-building-a-quiz/6148e5aeb102e3142de026a2.md
@@ -0,0 +1,685 @@
+---
+id: 6148e5aeb102e3142de026a2
+title: Schritt 67
+challengeType: 0
+dashedName: step-67
+---
+
+# --description--
+
+Endlich kann die Verfügbarkeit der Navigation durch die Bereitstellung von Tastaturkürzeln verbessert werden.
+
+Das Attribut `accesskey` akzeptiert eine durch Leerzeichen getrennte Liste von Zugangsschlüsseln. Zum Beispiel:
+
+```html
+Submit
+```
+
+Gib jedem der Navigationslinks einen Zugangsschlüssel mit einem Buchstaben.
+
+_Hinweis: Es ist nicht immer ratsam, Zugangsschlüsseln zu verwenden, aber sie können nützlich sein_
+
+Gut gemacht. Du hast das Übungsprojekt _Barrierefreiheit-Quiz_ abgeschlossen.
+
+# --hints--
+
+Du solltest dem ersten `a`-Element einen aus einem einzigen Buchstaben bestehenden `accesskey` geben.
+
+```js
+assert.equal(document.querySelectorAll('a')?.[0]?.getAttribute('accesskey')?.length, 1);
+```
+
+Du solltest dem zweiten `a`-Element einen aus einem einzigen Buchstaben bestehenden `accesskey` geben.
+
+```js
+assert.equal(document.querySelectorAll('a')?.[1]?.getAttribute('accesskey')?.length, 1);
+```
+
+Du solltest dem dritten `a`-Element einen aus einem einzigen Buchstaben bestehenden `accesskey` geben.
+
+```js
+assert.equal(document.querySelectorAll('a')?.[2]?.getAttribute('accesskey')?.length, 1);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+ HTML/CSS Quiz
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+
+
+
+
+
+ HTML
+
+
1
+
+
+ The legend element represents a caption for the content of its
+ parent fieldset element
+
+
+
+
+
+
2
+
+
+ A label element nesting an input element is required to have a
+ for attribute with the same value as the input's id
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+```
+
+```css
+@media (prefers-reduced-motion: no-preference) {
+ * {
+ scroll-behavior: smooth;
+ }
+}
+
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ background-color: #1b1b32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ background-color: #0a0a23;
+ aspect-ratio: 35 / 4;
+ padding: 0.4rem;
+}
+
+h1 {
+ color: #f1be32;
+ font-size: min(5vw, 1.2em);
+ text-align: center;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ color: #dfdfe2;
+ margin: 0 0.2rem;
+ padding: 0.2rem;
+ display: block;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0 auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+.info {
+ padding: 10px 0 0 5px;
+}
+
+.formrow {
+ margin-top: 30px;
+ padding: 0px 15px;
+}
+
+input {
+ font-size: 16px;
+}
+
+.info label, .info input {
+ display: inline-block;
+ text-align: right;
+}
+
+.info input {
+ width: 50%;
+ text-align: left;
+}
+
+.info label {
+ width: 10%;
+ min-width: 55px;
+}
+
+.question-block {
+ text-align: left;
+ display: block;
+ width: 100%;
+ margin-top: 20px;
+ padding-top: 5px;
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 15px;
+ font-size: 20px;
+}
+
+p::before {
+ content: "Question #";
+}
+
+.question {
+ border: none;
+ padding-bottom: 0;
+}
+
+.answers-list {
+ list-style: none;
+ padding: 0;
+}
+
+button {
+ display: block;
+ margin: 40px auto;
+ width: 40%;
+ padding: 15px;
+ font-size: 23px;
+ background: #d0d0d5;
+ border: 3px solid #3b3b4f;
+}
+
+footer {
+ background-color: #2a2a40;
+ display: flex;
+ justify-content: center;
+}
+
+footer,
+footer a {
+ color: #dfdfe2;
+}
+
+address {
+ text-align: center;
+ padding: 0.3em;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
+
+## --solutions--
+
+```html
+
+
+
+
+
+
+
+ Accessibility Quiz
+
+
+
+
+
+
+ HTML/CSS Quiz
+
+
+
+
+
+
+
+
+ HTML
+
+
1
+
+
+ The legend element represents a caption for the content of its
+ parent fieldset element
+
+
+
+
+
+
2
+
+
+ A label element nesting an input element is required to have a
+ for attribute with the same value as the input's id
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+```
+
+```css
+@media (prefers-reduced-motion: no-preference) {
+ * {
+ scroll-behavior: smooth;
+ }
+}
+
+body {
+ background: #f5f6f7;
+ color: #1b1b32;
+ font-family: Helvetica;
+ margin: 0;
+}
+
+header {
+ width: 100%;
+ height: 50px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: fixed;
+ background-color: #1b1b32;
+ top: 0;
+}
+
+#logo {
+ width: max(100px, 18vw);
+ aspect-ratio: 35 / 4;
+ max-height: 100%;
+ background-color: #0a0a23;
+ padding: 0.4rem;
+}
+
+h1 {
+ text-align: center;
+ font-size: min(5vw, 1.2em);
+ color: #f1be32;
+}
+
+nav {
+ width: 50%;
+ max-width: 300px;
+ height: 50px;
+}
+
+nav > ul {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
+ align-items: center;
+ padding-inline-start: 0;
+ margin-block: 0;
+ height: 100%;
+}
+
+nav > ul > li {
+ display: block;
+ margin: 0 0.2rem;
+ color: #dfdfe2;
+ padding: 0.2rem;
+}
+
+nav > ul > li:hover {
+ background-color: #dfdfe2;
+ color: #1b1b32;
+ cursor: pointer;
+}
+
+li > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+main {
+ padding-top: 50px;
+}
+
+section {
+ width: 80%;
+ margin: 0px auto 10px auto;
+ max-width: 600px;
+}
+
+h1,
+h2 {
+ font-family: Verdana, Tahoma;
+}
+
+h2 {
+ border-bottom: 4px solid #dfdfe2;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+
+.info {
+ margin: 0 auto;
+ padding: 10px 0 0 5px;
+}
+.formrow {
+ margin-top: 30px;
+ padding: 0px 15px;
+}
+
+input {
+ font-size: 16px;
+}
+
+.info label,
+.info input {
+ display: inline-block;
+ text-align: right;
+}
+
+.info label {
+ width: 10%;
+ min-width: 55px;
+}
+
+.info input {
+ width: 50%;
+ text-align: left;
+}
+
+.question-block {
+ text-align: left;
+ display: block;
+ width: 100%;
+ margin-top: 20px;
+ padding-top: 5px;
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 15px;
+ font-size: 20px;
+}
+
+p::before {
+ content: "Question #";
+}
+
+.question {
+ border: none;
+ padding-bottom: 0;
+}
+
+.answers-list {
+ list-style: none;
+ padding: 0;
+}
+
+button {
+ display: block;
+ margin: 40px auto;
+ width: 40%;
+ padding: 15px;
+ font-size: 23px;
+ background: #d0d0d5;
+ border: 3px solid #3b3b4f;
+}
+
+footer {
+ background-color: #2a2a40;
+ display: flex;
+ justify-content: center;
+}
+
+footer,
+footer a {
+ color: #dfdfe2;
+}
+
+address {
+ text-align: center;
+ padding: 0.3em;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3ef6e06d34faac0447fc44.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3ef6e06d34faac0447fc44.md
new file mode 100644
index 00000000000..aa23aabc917
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3ef6e06d34faac0447fc44.md
@@ -0,0 +1,135 @@
+---
+id: 5f3ef6e06d34faac0447fc44
+title: Schritt 61
+challengeType: 0
+dashedName: step-61
+---
+
+# --description--
+
+Mache den `Est. 2020`-Text kursiv, indem du einen `established`-Klassenselektor erstellst und ihm die `font-style`-Eigenschaft mit dem Wert `italic` gibst.
+
+# --hints--
+
+Du solltest einen `.established`-Selektor haben.
+
+```js
+const hasEstablished = new __helpers.CSSHelp(document).getStyle('.established');
+assert(hasEstablished);
+```
+
+Du solltest die `font-style`-Eigenschaft auf `italic` setzen.
+
+```js
+const hasFontStyle = new __helpers.CSSHelp(document).getCSSRules().some(x => x.style['font-style'] === 'italic');
+assert(hasFontStyle);
+```
+
+Dein `.established`-Selektor sollte die `font-style`-Eigenschaft auf `italic` setzen.
+
+```js
+const establishedFontStyle = new __helpers.CSSHelp(document).getStyle('.established')?.getPropertyValue('font-style');
+assert(establishedFontStyle === 'italic');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+ Cafe Menu
+
+
+
+
+
+
+```
+
+```css
+body {
+ background-image: url(https://cdn.freecodecamp.org/curriculum/css-cafe/beans.jpg);
+ font-family: sans-serif;
+}
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+h1, h2, p {
+ text-align: center;
+}
+
+.menu {
+ width: 80%;
+ background-color: burlywood;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 20px;
+ max-width: 500px;
+}
+
+h1, h2 {
+ font-family: Impact, serif;
+}
+
+.item p {
+ display: inline-block;
+}
+
+.flavor, .dessert {
+ text-align: left;
+ width: 75%;
+}
+
+.price {
+ text-align: right;
+ width: 25%
+}
+```
+
diff --git a/curriculum/challenges/italian/05-back-end-development-and-apis/basic-node-and-express/use-body-parser-to-parse-post-requests.md b/curriculum/challenges/italian/05-back-end-development-and-apis/basic-node-and-express/use-body-parser-to-parse-post-requests.md
index 46952f222d6..bcf98241d03 100644
--- a/curriculum/challenges/italian/05-back-end-development-and-apis/basic-node-and-express/use-body-parser-to-parse-post-requests.md
+++ b/curriculum/challenges/italian/05-back-end-development-and-apis/basic-node-and-express/use-body-parser-to-parse-post-requests.md
@@ -22,11 +22,11 @@ Content-Length: 20
name=John+Doe&age=25
```
-Come puoi vedere, il corpo è codificato come la query string. Questo è il formato predefinito usato dai moduli HTML. Con Ajax, è anche possibile utilizzare JSON per gestire dati che hanno una struttura più complessa. C'è anche un altro tipo di codifica: multipart/form-data. Questo viene utilizzato per caricare file binari. In questo esercizio, utilizzerai un corpo urlencoded. Per analizzare i dati provenienti dalle richieste POST, dovrai installare il pacchetto `body-parser`. Questo pacchetto consente di utilizzare una serie di middleware, che possono decodificare i dati in diversi formati.
+Come puoi vedere, il corpo è codificato come la query string. Questo è il formato predefinito usato dai moduli HTML. Con Ajax, è anche possibile utilizzare JSON per gestire dati che hanno una struttura più complessa. C'è anche un altro tipo di codifica: multipart/form-data. Questo viene utilizzato per caricare file binari. In questo esercizio, utilizzerai un corpo codificato nell'URL. Per analizzare i dati provenienti dalle richieste POST, dovrai installare il pacchetto `body-parser`. Questo pacchetto consente di utilizzare una serie di middleware, che possono decodificare i dati in diversi formati.
# --instructions--
-Installa il modulo `body-parser` nel tuo `package.json`. Poi, richiedilo con `require` all'inizio del file. Memorizzalo in una variabile chiamata `bodyParser`. Il middleware per gestire i dati urlencoded viene restituito da `bodyParser.urlencoded({extended: false})`. Passa ad `app.use()` la funzione restituita dal metodo invocato prima. Come al solito, il middleware deve essere montato prima di tutte le rotte che dipendono da esso.
+Il pacchetto `body-parser` è già stato installato ed è nel file `package.json` del tuo progetto. Richiedilo con `require` nella parte superiore del file `myApp.js` e memorizzalo in una variabile chiamata `bodyParser`. Il middleware per gestire i dati codificati nell'URL viene restituito da `bodyParser.urlencoded({extended: false})`. Passa ad `app.use()` la funzione restituita dal metodo invocato prima. Come al solito, il middleware deve essere montato prima di tutte le rotte che dipendono da esso.
**Nota:** `extended` è un'opzione di configurazione che dice al `body-parser` quale analisi deve essere utilizzata. Quando `extended=false` viene utilizzata la libreria di codifica classica `querystring`. Quando `extended=true` viene usata per il parsing la libreria `qs`.
diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
index 34f307e827a..92967ecff58 100644
--- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
+++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
@@ -20,7 +20,7 @@ Nella regola CSS `.red`, modifica la proprietà `background-color` in `backgroun
La regola CSS `.red` dovrebbe avere una proprietà `background` con il valore `rgb(255, 0, 0)`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.red').cssText, 'background: rgb(255, 0, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.red\{.*?background:rgb\(255,0,0\)/);
```
# --seed--
diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
index f174c626a23..1d19e682d38 100644
--- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
+++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
@@ -16,7 +16,7 @@ Nella regola CSS `.green`, modifica la proprietà `background-color` in `backgro
La regola CSS `.green` dovrebbe avere una proprietà `background` con il valore `#007F00`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.green').cssText, 'background: rgb(0, 127, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.green\{.*?background:#007F00/);
```
# --seed--
diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
index ec9ca59f377..4c1c4b4b794 100644
--- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
+++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
@@ -16,7 +16,7 @@ Nella regola CSS `.blue`, modifica la proprietà `background-color` in `backgrou
La regola CSS `.blue` dovrebbe avere una proprietà `background` con il valore `hsl(240, 100%, 50%)`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.blue').cssText, 'background: rgb(0, 0, 255)');
+assert.match(__helpers.removeWhiteSpace(code), /\.blue\{.*?background:hsl\(240,100%,50%\)/);
```
# --seed--
diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5dc174fcf86c76b9248c6eb2.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5dc174fcf86c76b9248c6eb2.md
index b592f1e57cd..d46feee7c7d 100644
--- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5dc174fcf86c76b9248c6eb2.md
+++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5dc174fcf86c76b9248c6eb2.md
@@ -9,11 +9,11 @@ dashedName: step-1
Gli elementi HTML hanno un tag di apertura come `` e un tag di chiusura come ` `.
+Il testo di un elemento va tra i tag di apertura e di chiusura.
+
Trova l'elemento `h1` e cambia il suo testo in:
- `CatPhotoApp`
-
-Assicurati che il testo si trovi tra i suoi tag di apertura e chiusura.
+`CatPhotoApp`
# --hints--
diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-intermediate-css-by-building-a-picasso-painting/60b69a66b6ddb80858c5157f.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-intermediate-css-by-building-a-picasso-painting/60b69a66b6ddb80858c5157f.md
index e45d5e35d17..2e068dff799 100644
--- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-intermediate-css-by-building-a-picasso-painting/60b69a66b6ddb80858c5157f.md
+++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-intermediate-css-by-building-a-picasso-painting/60b69a66b6ddb80858c5157f.md
@@ -7,9 +7,9 @@ dashedName: step-9
# --description--
-Tipicamente, l'HTML è renderizzato in modo top-down. Gli elementi in cima al codice sono posizionati nella parte superiore della pagina. Tuttavia, spesso è possibile che tu voglia spostare gli elementi in posizioni diverse. Puoi farlo con l'attributo `position`.
+Tipicamente, l'HTML è renderizzato in modo top-down. Gli elementi in cima al codice sono posizionati nella parte superiore della pagina. Tuttavia, spesso è possibile che tu voglia spostare gli elementi in posizioni diverse. Puoi farlo grazie alla proprietà `position`.
-Imposta l'attributo `position` per l'elemento `back-wall` su `absolute`. Il valore `absolute` toglie l'elemento dal flusso top-down del documento e ti permette di spostarlo relativamente al suo contenitore.
+Imposta la proprietà `position` per l'elemento `back-wall` sul valore `absolute`. Il valore `absolute` toglie l'elemento dal flusso top-down del documento e ti permette di spostarlo relativamente al suo contenitore.
Puoi spostare manualmente il layout di un elemento con `top`, `left`, `right` e `bottom`. Assegna all'elemento `back-wall` una proprietà `top` di `0` e una proprietà `left` di `0`.
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
index 398b3a814a6..5ad21d361cd 100644
--- a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
@@ -20,7 +20,7 @@ CSS で均一な色を設定する方法をいくつか学んできましたが
`.red` CSS ルールには `background` プロパティと値 `rgb(255, 0, 0)` が必要です。
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.red').cssText, 'background: rgb(255, 0, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.red\{.*?background:rgb\(255,0,0\)/);
```
# --seed--
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
index ff0bbc55960..3a76428a3d2 100644
--- a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
@@ -16,7 +16,7 @@ dashedName: step-60
`.green` CSS ルールには `background` プロパティと値 `#007F00` が必要です。
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.green').cssText, 'background: rgb(0, 127, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.green\{.*?background:#007F00/);
```
# --seed--
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
index 5e5e5102539..0ede13691c4 100644
--- a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
@@ -16,7 +16,7 @@ dashedName: step-66
`.blue` CSS ルールには `background` プロパティと値 `hsl(240, 100%, 50%)` が必要です。
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.blue').cssText, 'background: rgb(0, 0, 255)');
+assert.match(__helpers.removeWhiteSpace(code), /\.blue\{.*?background:hsl\(240,100%,50%\)/);
```
# --seed--
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d2.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d2.md
new file mode 100644
index 00000000000..5f6bf5a6f52
--- /dev/null
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d2.md
@@ -0,0 +1,94 @@
+---
+id: 5ef9b03c81a63668521804d2
+title: ステップ 26
+challengeType: 0
+dashedName: step-26
+---
+
+# --description--
+
+順序付きリスト (ordered list、`ol`) のコードは順序なしリストと似ていますが、順序付きリストのリスト項目は番号付きで表示されます。
+
+2 つ目の `section` 要素内で、最後の `h3` 要素の後に、順序付きリストと下記リスト項目を追加してください:
+
+`flea treatment` `thunder` `other cats`
+
+# --hints--
+
+`ol` 要素には開始タグが必要です。 開始タグは `` のような形式の構文です。
+
+```js
+assert(document.querySelector('ol'));
+```
+
+`ol` 要素には終了タグが必要です。 終了タグは `<` の直後に `/` があります。
+
+```js
+assert(code.match(/<\/ol>/));
+```
+
+`ol` 要素は、2 つ目の `section` 要素の終了タグの上にある必要があります。 順番が誤っているようです。
+
+```js
+assert($('main > section')[1].lastElementChild.nodeName === 'OL');
+```
+
+3 つの `li` 要素は、`ol` 要素の中に入れ子にする必要があります。
+
+```js
+assert(
+ [...document.querySelectorAll('li')].filter(
+ (item) => item.parentNode.nodeName === 'OL'
+ ).length === 3
+);
+```
+
+3 つの `li` 要素に、テキスト `flea treatment`、`thunder`、`other cats` が任意の順番で設定されている必要があります。
+
+```js
+assert.deepStrictEqual(
+ [...document.querySelectorAll('li')]
+ .filter((item) => item.parentNode.nodeName === 'OL')
+ .map((item) => item.innerText.toLowerCase())
+ .sort((a, b) => a.localeCompare(b)),
+ ['flea treatment', 'other cats', 'thunder']
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+ CatPhotoApp
+
+
+ Cat Photos
+
+ Click here to view more cat photos .
+
+
+
+ Cat Lists
+ Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
+
+ Cats love lasagna.
+
+--fcc-editable-region--
+ Top 3 things cats hate:
+--fcc-editable-region--
+
+
+
+
+
+```
+
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d3.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d3.md
new file mode 100644
index 00000000000..52bdbc921f6
--- /dev/null
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d3.md
@@ -0,0 +1,72 @@
+---
+id: 5ef9b03c81a63668521804d3
+title: ステップ 27
+challengeType: 0
+dashedName: step-27
+---
+
+# --description--
+
+順序付きリストの後に、もう 1 つ `figure` 要素を追加してください。
+
+# --hints--
+
+`figure` 要素には開始タグが必要です。 開始タグは `` のような形式の構文です。
+
+```js
+assert(document.querySelectorAll('figure').length === 2);
+```
+
+`figure` 要素には終了タグが必要です。 終了タグは `<` の直後に `/` があります。
+
+```js
+assert(code.match(/<\/figure>/g).length === 2);
+```
+
+2 つ目の `section` 要素の終了タグのすぐ上に `figure` 要素が必要です。
+
+```js
+assert($('main > section')[1].lastElementChild.nodeName === 'FIGURE');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+ CatPhotoApp
+
+
+ Cat Photos
+
+ Click here to view more cat photos .
+
+
+
+ Cat Lists
+ Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
+
+ Cats love lasagna.
+
+ Top 3 things cats hate:
+--fcc-editable-region--
+
+ flea treatment
+ thunder
+ other cats
+
+--fcc-editable-region--
+
+
+
+
+```
+
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d4.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d4.md
new file mode 100644
index 00000000000..b295f3297b0
--- /dev/null
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d4.md
@@ -0,0 +1,96 @@
+---
+id: 5ef9b03c81a63668521804d4
+title: ステップ 31
+challengeType: 0
+dashedName: step-31
+---
+
+# --description--
+
+`strong` 要素は、あるテキストの重要性や緊急性が高いことを示すために使われます。
+
+先ほど追加した `figcaption` 内で、`hate` を `strong` 要素で囲んで重要性が高いことを示しましょう。
+
+# --hints--
+
+`strong` 要素には開始タグが必要です。 開始タグは `` のような形式の構文です。
+
+```js
+assert(document.querySelector('strong'));
+```
+
+`strong` 要素には終了タグが必要です。 終了タグは `<` の直後に `/` があります。
+
+```js
+assert(code.match(/<\/strong\>/));
+```
+
+`strong` 要素が、`Cats hate other cats.` というテキスト内の単語 `hate` を囲む必要があります。 テキストが設定されていないか、誤字脱字があります。
+
+```js
+assert(
+ document
+ .querySelectorAll('figcaption')[1]
+ .querySelector('strong')
+ .innerText.toLowerCase() === 'hate'
+);
+```
+
+`figcaption` のテキストは `Cats hate other cats.` でなければなりません。 誤字がないか、`strong` 要素の開始タグと終了タグの前後に適切なスペースがあるか確認してください。
+
+```js
+const secondFigCaption = document.querySelectorAll('figcaption')[1];
+assert(
+ secondFigCaption &&
+ secondFigCaption.innerText
+ .replace(/\s+/gi, ' ')
+ .trim()
+ .match(/cats hate other cats\.?/i)
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+ CatPhotoApp
+
+
+ Cat Photos
+
+ Click here to view more cat photos .
+
+
+
+ Cat Lists
+ Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
+
+ Cats love lasagna.
+
+ Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+--fcc-editable-region--
+ Cats hate other cats.
+--fcc-editable-region--
+
+
+
+
+
+```
+
diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d5.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d5.md
new file mode 100644
index 00000000000..45bf4ad7eb8
--- /dev/null
+++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-html-by-building-a-cat-photo-app/5ef9b03c81a63668521804d5.md
@@ -0,0 +1,105 @@
+---
+id: 5ef9b03c81a63668521804d5
+title: ステップ 33
+challengeType: 0
+dashedName: step-33
+---
+
+# --description--
+
+3 つ目の `section` 要素内に、次のテキストを持つ `h2` 要素を追加してください:
+
+`Cat Form`
+
+# --hints--
+
+3 つ目の `section` 要素が見つかりません。 要素自体、または開始タグや終了タグが誤って削除された可能性があります。
+
+```js
+assert(
+ document.querySelectorAll('section').length === 3 &&
+ code.match(/<\/section>/g).length === 3
+);
+```
+
+`h2` 要素には開始タグおよび終了タグが必要です。 必要なタグの片方、または両方が欠けている可能性があります。
+
+```js
+assert(
+ document.querySelectorAll('h2').length >= 3 &&
+ code.match(/<\/h2>/g).length >= 3
+);
+```
+
+`h2` 要素を 1 つだけ追加してください。 余分なものは削除してください。
+
+```js
+assert(document.querySelectorAll('h2').length === 3);
+```
+
+新しい `h2` 要素は、3 つ目の `section` 要素の終了タグのすぐ上にある必要があります。
+
+```js
+const thirdSection = document.querySelectorAll('section')[2];
+assert(thirdSection.lastElementChild.nodeName === 'H2');
+```
+
+`h2` 要素のテキストは `Cat Form` でなければなりません。
+
+```js
+const thirdSection = document.querySelectorAll('section')[2];
+assert(
+ thirdSection
+ .querySelector('h2')
+ .innerText.toLowerCase()
+ .replace(/\s+/g, ' ') === 'cat form'
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+ CatPhotoApp
+
+
+ Cat Photos
+
+ Click here to view more cat photos .
+
+
+
+ Cat Lists
+ Things cats love:
+
+ cat nip
+ laser pointers
+ lasagna
+
+
+
+ Cats love lasagna.
+
+ Top 3 things cats hate:
+
+ flea treatment
+ thunder
+ other cats
+
+
+
+ Cats hate other cats.
+
+
+--fcc-editable-region--
+
+--fcc-editable-region--
+
+
+
+```
+
diff --git a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
index ba49ea08030..e5fc8ffb0df 100644
--- a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
+++ b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a489b8579e87364b2d2cdb.md
@@ -20,7 +20,7 @@ Na regra do CSS `.red`, altere a propriedade `background-color` para `background
A regra do CSS `.red` deve ter uma propriedade `background` com o valor `rgb(255, 0, 0)`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.red').cssText, 'background: rgb(255, 0, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.red\{.*?background:rgb\(255,0,0\)/);
```
# --seed--
diff --git a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
index 7565c8dec66..e18b2b49071 100644
--- a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
+++ b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a4ada3aabeec822b2975d9.md
@@ -16,7 +16,7 @@ Na regra do CSS `.green`, altere a propriedade `background-color` para `backgrou
A regra do CSS `.green` deve ter uma propriedade `background` com o valor `#007F00`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.green').cssText, 'background: rgb(0, 127, 0)');
+assert.match(__helpers.removeWhiteSpace(code), /\.green\{.*?background:#007F00/);
```
# --seed--
diff --git a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
index 2d88ab5275c..08670c44a1c 100644
--- a/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
+++ b/curriculum/challenges/portuguese/14-responsive-web-design-22/learn-css-colors-by-building-a-set-of-colored-markers/61a5ca57f50ded36d33eef96.md
@@ -16,7 +16,7 @@ Na regra do CSS `.blue`, altere a propriedade `background-color` para `backgroun
A regra do CSS `.blue` deve ter uma propriedade `background` com o valor `hsl(240, 100%, 50%)`.
```js
-assert.include(new __helpers.CSSHelp(document).getStyle('.blue').cssText, 'background: rgb(0, 0, 255)');
+assert.match(__helpers.removeWhiteSpace(code), /\.blue\{.*?background:hsl\(240,100%,50%\)/);
```
# --seed--
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/add-a-box-shadow-to-a-card-like-element.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/add-a-box-shadow-to-a-card-like-element.md
index 527b75cbe42..fa0af8c663e 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/add-a-box-shadow-to-a-card-like-element.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/add-a-box-shadow-to-a-card-like-element.md
@@ -11,14 +11,14 @@ dashedName: add-a-box-shadow-to-a-card-like-element
Властивість `box-shadow` додає одну або більше тіней до елемента.
-Властивість `box-shadow` приймає значення
+Властивість `box-shadow` приймає такі значення, за порядком:
- offset-x (наскільки далеко відсунути тінь від елемента по горизонталі)
- offset-y (наскільки далеко відсунути тінь від елемента по вертикалі)
- blur-radius,
- spread-radius і
- color, в такому порядку.
+ offset-x (наскільки далеко відсунута тінь від елемента по горизонталі)
+ offset-y (наскільки далеко відсунута тінь від елемента по вертикалі)
+ blur-radius
+ spread-radius
+ color
Значення `blur-radius` та `spread-radius` є необов'язковими.
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/adjust-the-hue-of-a-color.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/adjust-the-hue-of-a-color.md
index a9d1899fe07..dbe7174970e 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/adjust-the-hue-of-a-color.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/adjust-the-hue-of-a-color.md
@@ -9,7 +9,7 @@ dashedName: adjust-the-hue-of-a-color
# --description--
-Кольори мають декілька характеристик, включно з відтінком, насиченістю та яскравістю. У CSS3 властивість `hsl()` представлена як альтернативний спосіб вибору кольору, з налаштуванням усіх трьох характеристик.
+Кольори мають декілька характеристик, включно з відтінком, насиченістю та яскравістю. CSS3 представив функцію `hsl()` як альтернативний спосіб вибору кольору шляхом прямого визначення цих характеристик.
Під**відтінком** люди часто розуміють колір. Якщо уявити спектр кольорів, починаючи з червоного зліва, через зелений посередині, і закінчуючи синім справа, то відтінок - це місце на цій лінії, де знаходиться колір. У `hsl()`, для визначення відтінку замість спектру використовується концепція колірного кола, де кут кольору на окружності задається значенням від 0 до 360.
@@ -27,19 +27,19 @@ dashedName: adjust-the-hue-of-a-color
# --hints--
-Ви маєте використати властивість `hsl()`, щоб задати зелений колір `green`.
+Ваш код повинен використовувати функцію `hsl()`, щоб декларувати зелений колір.
```js
assert(code.match(/\.green\s*?{\s*?background-color\s*:\s*?hsl/gi));
```
-Ви маєте використати властивість `hsl()`, щоб задати блакитний колір `cyan`.
+Ваш код повинен використовувати функцію `hsl()`, щоб декларувати блакитний колір.
```js
assert(code.match(/\.cyan\s*?{\s*?background-color\s*:\s*?hsl/gi));
```
-Ви маєте використати властивість `hsl()`, щоб задати синій колір `blue`.
+Ваш код повинен використовувати функцію `hsl()`, щоб декларувати синій колір.
```js
assert(code.match(/\.blue\s*?{\s*?background-color\s*:\s*?hsl/gi));
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-graphic-using-css.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-graphic-using-css.md
index 14c3ef64bbf..6d6361a31bc 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-graphic-using-css.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-graphic-using-css.md
@@ -15,11 +15,11 @@ dashedName: create-a-graphic-using-css
Щоб створити круглий об'єкт, необхідно задати властивості `border-radius` значення 50%.
-Як ви могли помітити в попередньому завданні, властивість `box-shadow` приймає значення: `offset-x`, `offset-y`, `blur-radius`, `spread-radius` і значення кольору. Значення `blur-radius` і `spread-radius` необов'язкові.
+Як ви могли помітити в попередньому завданні, властивість `box-shadow` приймає значення для `offset-x`, `offset-y`, `blur-radius`, `spread-radius` та `color`, в такому ж порядку. Значення `blur-radius` і `spread-radius` необов'язкові.
# --instructions--
-Керуйте квадратним елементом у редакторі, щоб створити форму місяця. Спочатку змініть `background-color` на `transparent`, тоді надайте властивості `border-radius` значення 50%, щоб створити круглу форму. Вкінці змініть властивість `box-shadow`, щоб надати властивості `offset-x` значення 25 пікселів, властивості `offset-y` - 10 пікселів, `blur-radius` - 0, `spread-radius` - 0, а за допомогою властивості `blue` встановити колір.
+Керуйте квадратним елементом у редакторі, щоб створити форму місяця. Спочатку змініть `background-color` на `transparent`, тоді надайте властивості `border-radius` значення 50%, щоб створити круглу форму. Зрештою, змініть властивість `box-shadow`, щоб встановити `offset-x` на 25 пікселів, `offset-y` на 10 пікселів, `blur-radius` на 0, `spread-radius` на 0, а `color` на `blue`.
# --hints--
@@ -35,7 +35,7 @@ assert(code.match(/background-color:\s*?transparent;/gi));
assert(code.match(/border-radius:\s*?50%;/gi));
```
-Значення властивості `box-shadow` повинно мати значення 25 пікселів для `offset-x`, 10 пікселів для `offset-y`, 0 для `blur-radius`, 0 для `spread-radius`, та колір має бути задано за допомогою `blue`.
+Значення властивості `box-shadow` повинне бути 25 пікселів для `offset-x`, 10 пікселів для `offset-y`, 0 для `blur-radius`, 0 для `spread-radius`, а також `blue` для `color`.
```js
assert(
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-more-complex-shape-using-css-and-html.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-more-complex-shape-using-css-and-html.md
index 1ad0052da28..bd3ed4c95bb 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-more-complex-shape-using-css-and-html.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/create-a-more-complex-shape-using-css-and-html.md
@@ -28,15 +28,15 @@ dashedName: create-a-more-complex-shape-using-css-and-html
# --instructions--
-Перетворіть елемент на екрані на серце. У селекторі `heart::after` замініть фоновий колір `background-color` на `pink` і встановіть значення `border-radius` на 50%.
+Перетворіть елемент на екрані на серце. У селекторі `.heart::after` змініть `background-color` на `pink` та `border-radius` на 50%.
Потім оберіть елемент з класом `heart` (тільки `heart`) і заповніть властивість `transform`. Використовуйте функцію `rotate()` з -45 градусами.
-Нарешті, у селекторі `heart::before` встановіть його властивість `content` на порожній рядок.
+Зрештою, у селекторі `.heart::before` встановіть властивість `content` на порожній рядок.
# --hints--
-Властивість `background-color` селектора `heart::after` має бути `pink`.
+Селектор `.heart::after` повинен мати властивість `background-color` зі значенням `pink`.
```js
const heartAfter = code.match(/\.heart::after\s*{[\s\S]+?[^\}]}/g)[0];
@@ -45,7 +45,7 @@ assert(
);
```
-`border-radius` селектора `heart::after` повинен мати значення 50%.
+Властивість `border-radius` селектора `.heart::after` повинна мати значення 50%.
```js
assert(code.match(/border-radius\s*?:\s*?50%/gi).length == 2);
@@ -57,7 +57,7 @@ assert(code.match(/border-radius\s*?:\s*?50%/gi).length == 2);
assert(code.match(/transform\s*?:\s*?rotate\(\s*?-45deg\s*?\)/gi));
```
-Елемент `content` селектора `heart::before` має бути порожнім рядком.
+`content` селектора `.heart::before` повинен бути порожнім рядком.
```js
assert(code.match(/\.heart::before\s*?{\s*?content\s*?:\s*?("|')\1\s*?;/gi));
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.md
index 05568a16b32..e6dac82592f 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.md
@@ -11,7 +11,7 @@ dashedName: use-a-bezier-curve-to-move-a-graphic
У попередньому завданні йшлося про ключове слово `ease-out`, яке описує зміну анімації, що спочатку пришвидшується, а тоді уповільнюється в кінці анімації. Праворуч показано різницю між ключовим словом `ease-out` (для синього елемента) і ключовим словом `linear` (для червоного елемента). Подібну послідовність анімації до ключового слова `ease-out` можна створити за допомогою користувацької функції кубічної кривої Безьє.
-Загалом зміна точок прив'язки `p1` та `p2` керує створенням різних кривих Безьє, що контролює розвиток анімації в часі. Ось приклад кривої Безьє, створеної за допомогою значень для імітації стилю зсунення зі зникненням (ease-out):
+Загалом зміна точок прив'язки `p1` та `p2` керує створенням різних кривих Безьє, що контролює розвиток анімації в часі. Ось приклад кривої Безьє, створеної за допомогою значень для імітації стилю `ease-out`:
```css
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
@@ -21,7 +21,7 @@ animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
# --instructions--
-Щоб побачити результат цієї кривої Безьє в дії, змініть `animation-timing-function` елемента з ідентифікацією `red` на функцію `cubic-bezier` зі значеннями х1, y1, x2, y2, які вказані відповідно до 0, 0, 0.58, 1. Завдяки цьому обидва елементи будуть однаково просуватися по анімації.
+Щоб побачити результат цієї кривої Безьє в дії, змініть `animation-timing-function` елемента з ідентифікацією `red` на функцію `cubic-bezier` зі значеннями х1, y1, x2, y2, які вказані відповідно до `0, 0, 0.58, 1`. Завдяки цьому обидва елементи будуть однаково просуватися по анімації.
# --hints--
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-css-linear-gradient-to-create-a-striped-element.md b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-css-linear-gradient-to-create-a-striped-element.md
index be4cdc4c722..02193d79cc6 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-css-linear-gradient-to-create-a-striped-element.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/applied-visual-design/use-a-css-linear-gradient-to-create-a-striped-element.md
@@ -47,7 +47,7 @@ assert(!code.match(/90deg/gi));
assert(code.match(/yellow\s+?0(px)?/gi));
```
-Один стоп-колір на рівні 40 пікселів повинен бути `yellow` (жовтий).
+Перший стоп-колір на 40 пікселях повинен бути `yellow`.
```js
assert(code.match(/yellow\s+?40px/gi));
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/change-a-variable-for-a-specific-area.md b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/change-a-variable-for-a-specific-area.md
index ed0c65faa4b..7ac5aa3236e 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/change-a-variable-for-a-specific-area.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/change-a-variable-for-a-specific-area.md
@@ -11,7 +11,7 @@ dashedName: change-a-variable-for-a-specific-area
Коли ви створюєте ваші змінні в `:root`, вони установлять значення змінної величини для цілої сторінки.
-Ви можете перезаписати ці змінні вставивши їх знову у спеціальний елемент.
+Ви можете перезаписати ці змінні, вставивши їх знову у спеціальний селектор.
# --instructions--
@@ -27,7 +27,7 @@ assert(
);
```
-Клас `penguin` не повинен містити властивість `background-color`
+Клас `penguin` не повинен містити властивість `background-color`.
```js
assert(
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/create-a-custom-css-variable.md b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/create-a-custom-css-variable.md
index 006a5caa564..9c133289077 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/create-a-custom-css-variable.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/create-a-custom-css-variable.md
@@ -15,7 +15,7 @@ dashedName: create-a-custom-css-variable
--penguin-skin: gray;
```
-Це створить змінну з назвою `--penguin-skin` і надасть їй значення `gray`. Тепер ви можете використовувати цю змінну в іншому місці вашого CSS, щоб змінювати значення інших елементів на сірий.
+Це створить змінну з назвою `--penguin-skin` і надасть їй значення `gray`. Тепер ви можете використовувати цю змінну в іншому місці свого CSS, щоб змінювати значення інших властивостей на сірий.
# --instructions--
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/import-a-google-font.md b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/import-a-google-font.md
index 1300653a331..3f6ad47ee0e 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/import-a-google-font.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/import-a-google-font.md
@@ -11,7 +11,7 @@ dashedName: import-a-google-font
На доповнення до загальних шрифтів, які є в більшості операційних систем, ми також можемо встановити нестандартні, користувацькі веб-шрифти для використання на нашому сайті. У Інтернеті існує багато джерел веб-шрифтів. Але для прикладу ми використаємо бібліотеку Google Fonts.
-[Google Fonts](https://fonts.google.com/) є безкоштовною бібліотекою веб-шрифтів, які можна використати у CSS, посилаючись на URL-адресу шрифту.
+Google Fonts – це безоплатна бібліотека вебшрифтів, які можна використати у CSS, посилаючись на URL-адресу шрифту.
Що ж, давайте почнемо імпортувати і застосувати шрифт Google (зауважте, що якщо Google заблокований у вашій країні, то вам потрібно пропустити це завдання).
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/inherit-styles-from-the-body-element.md b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/inherit-styles-from-the-body-element.md
index f9d32aa9126..9b20e7c6129 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/inherit-styles-from-the-body-element.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/inherit-styles-from-the-body-element.md
@@ -77,7 +77,7 @@ assert(
);
```
-Елемент `h1` повинен успадкувати зелений колір від елемента `body`.
+Елемент `h1` повинен успадкувати колір `green` від елементу `body`.
```js
assert($('h1').length > 0 && $('h1').css('color') === 'rgb(0, 128, 0)');
diff --git a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/override-styles-in-subsequent-css.md b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/override-styles-in-subsequent-css.md
index 0ded4c906fc..f25914c22da 100644
--- a/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/override-styles-in-subsequent-css.md
+++ b/curriculum/challenges/ukrainian/01-responsive-web-design/basic-css/override-styles-in-subsequent-css.md
@@ -27,7 +27,7 @@ class="class1 class2"
**Примітка:** Порядок, в якому перераховані класи в HTML елементі не має значення.
-Натомість, порядок об'яв `class` у розділі `