diff --git a/de-de/crystal-de.html.markdown b/de-de/crystal-de.html.markdown
new file mode 100644
index 00000000..535267ee
--- /dev/null
+++ b/de-de/crystal-de.html.markdown
@@ -0,0 +1,561 @@
+---
+language: crystal
+contributors:
+ - ["Vitalii Elenhaupt", "http://veelenga.com"]
+ - ["Arnaud Fernandés", "https://github.com/TechMagister/"]
+translators:
+ - ["caminsha", "https://github.com/caminsha"]
+filename: learncrystal-de.cr
+lang: de-de
+---
+
+```crystal
+
+# Das ist ein Kommentar
+
+# Alles ist ein Objekt
+nil.class # => Nil
+100.class # => Int32
+true.class # => Bool
+
+# Falschwerte sind: nil, false und Nullpointer
+!nil # => true : Bool
+!false # => true : Bool
+!0 # => false : Bool
+
+# Integer
+
+1.class # => Int32
+
+# Fünf vorzeichenbehaftete Ganzzahlen
+1_i8.class # => Int8
+1_i16.class # => Int16
+1_i32.class # => Int32
+1_i64.class # => Int64
+1_i128.class # => Int128
+
+# Fünf vorzeichenlose Ganzzahlen
+1_u8.class # => UInt8
+1_u16.class # => UInt16
+1_u32.class # => UInt32
+1_u64.class # => UInt64
+1_u128.class # => UInt128
+
+
+2147483648.class # => Int64
+9223372036854775808.class # => UInt64
+
+# Binäre Zahlen
+0b1101 # => 13 : Int32
+
+# Oktalzahlen
+0o123 # => 83 : Int32
+
+# Hexadezimalzahlen
+0xFE012D # => 16646445 : Int32
+0xfe012d # => 16646445 : Int32
+
+# Gleitkommazahlen (floats)
+
+1.0.class # => Float64
+
+# Es gibt zwei Typen von Gleitkommazahlen
+
+1.0_f32.class # => Float32
+1_f32.class # => Float32
+
+1e10.class # => Float64
+1.5e10.class # => Float64
+1.5e-7.class # => Float64
+
+
+# Chars (einzelne Zeichen)
+
+'a'.class # => Char
+
+# Oktale Schreibweise
+'\101' # => 'A' : Char
+
+# Unicode Schreibweise
+'\u0041' # => 'A' : Char
+
+# Strings (Zeichenketten)
+"s".class # => String
+
+# Strings sind unveränderlich
+s = 'hello, " # => "hello, " : String
+s.object_id # => 1234667712 : UInt64
+s += "Crystal" # => "hello, Crystal" : String
+s.object_id # => 142528472 : UInt64
+
+# Interpolation wird unterstützt
+"sum = #{1 + 2}" # => "sum = 3" : String
+
+# Mehrzeilige Strings
+" Dies ist ein
+ mehrzeiliger String."
+
+# String mit doppeltem Anführungszeichen
+%(hello "world") # => "hello \"world\""
+
+# Symbole
+# Unveränderbare, wiederverwendbare Konstanten, welche intern als Int32 Integer
+# Werte repräsentiert werden.
+# Symbole werden oft anstelle von Strings verwendet, um bestimmte Werte zu bestimmen.
+
+:symbol.class # => Symbol
+
+sentence = :question? # :"question?" : Symbol
+
+sentence = :question? # => true : Bool
+sentence = :exclamation! # => false : Bool
+sentence = "question?" # => false : Bool
+
+# Arrays
+[1, 2, 3].class # => Array(Int32)
+[1, "hello", 'x'].class # => Array(Int32 | String | Char)
+
+# Leere Arrays sollten einen Typen definieren
+[] # => Syntaxfehler: für leere Arrays,
+ # verwende `[] of ElementType`
+[] of Int32 # => [] : Array(Int32)
+Array(Int32).new # => [] : Array(Int32)
+
+# Arrays können indiziert werden
+array = [1, 2, 3, 4, 5] # => [1, 2, 3, 4, 5] : Array(Int32)
+array[0] # => 1 : Int32
+array[10] # führt zu einem IndexError
+array[-6] # führt zu einem IndexError
+array[10]? # => nil : (Int32 | Nil)
+array[-6]? # => nil : (Int32 | Nil)
+
+# Starte am Ende des Arrays
+array[-1] # => 5
+
+# Mit einem Startindex und einer Länge
+array[2, 4] # => [3, 4, 5]
+
+# oder mit einem Bereich
+array[1..3] # => [2, 3, 4]
+
+# Füge etwas zu einem Array hinzu
+array << 6 # => [1, 2, 3, 4, 5, 6]
+
+# Entferne Einträge am Ende des Arrays
+array.pop # => 6
+array # => [1, 2, 3, 4, 5]
+
+# Entferne ersten Eintrag im Array
+array.shift # => 1
+array # => [2, 3, 4, 5]
+
+# Überprüfe, ob ein Element in einem Array existiert
+array.includes? 3 # => true
+
+# Spezielle Syntax für String-Arrays und Symbol-Arrays
+%w(one two three) # => ["one", "two", "three"] : Array(String)
+%i(one two three) # 0> [:one, :two, :three] : Array(Symbol)
+
+# Es gibt auch für andere Arrays eine spezielle Syntax, wenn die Methoden
+# `.new` und `#<<` definiert werden.
+set = Set{1, 2, 3} # => [1, 2, 3]
+set.class # => Set(Int32)
+
+# Das obere ist äquivalent zu:
+set = Set(typeof(1, 2, 3)).new
+set << 1
+set << 2
+set << 3
+
+# Hashes
+{1 => 2, 3 => 4}.class # => Hash(Int32, Int32)
+{1 => 2, 'a' => 3}.class # => Hash (Int32 | Char, Int32)
+
+# Leere Hashes sollten einen Typen spezifieren
+{} # Syntaxfehler
+{} of Int32 => Int32 # {}
+Hash(Int32, Int32).new # {}
+
+# Hashes können schnell mit dem Key nachgeschaut werden
+hash = {"color" => "green", "number" => 5}
+hash["color"] # => "green"
+hash["no_such_key"] # => Fehlender hash key: "no_such_key" (KeyError)
+hash["no_such_key"]? # => nil
+
+# Überprüfe die Existenz eines Hashkeys
+hash.has_key? "color" # => true
+
+# Spezielle Schreibweise für Symbol- und Stringkeys
+{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'}
+{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'}
+
+# Die spezielle Syntax für Hash-Literale gibt es auch für andere Typen, sofern
+# diese die Methoden `.new` und `#[]=` Methoden definieren.
+class MyType
+ def []=(key, value)
+ puts "do stuff"
+ end
+end
+
+MyType{"foo" => "bar"}
+
+# Das obere ist äquivalent zu:
+tmp = MyType.new
+tmp["foo"] = "bar"
+tmp
+
+# Ranges (Bereiche)
+1..10 # => Range(Int32, Int32)
+Range.new(1,10).class # => Range(Int32, Int32)
+
+# Ranges können inklusiv oder exklusiv sein.
+(3..5).to_a # => [3, 4, 5]
+(3...5).to_a # => [3, 4]
+
+# Überprüfe, ob ein Range einen Wert enthält oder nicht.
+(1..8).includes? 2 # => true
+
+# Tupel sind unveränderliche, Stack-zugewiese Folgen von Werten mit fester
+# Größe und möglicherweise unterschiedlichen Typen
+{1, "hello", 'x'}.class # => Tuple(Int32, String, Char)
+
+# Erhalte den Wert eines Tupels über den Index
+tuple = {:key1, :key2}
+tuple[1] # => :key2
+tuple[2] # syntax error: Index out of bound
+
+# Können auf mehrere Variablen erweitert werden
+a, b, c = {:a, 'b', "c"}
+a # => :a
+b # => 'b'
+c # => "c"
+
+# Procs repräsentieren ein Funktionspointer mit einem optionalen Kontext.
+# Normalerweise wird ein Proc mit einem proc-Literal erstellt.
+proc = ->(x : Int32) { x.to_s }
+proc.class # => Print(Int32, String)
+# Außerdem kann man auch mit der Methode `new` ein Proc erstellen.
+Proc(Int32, String).new { |x| x.to_s }
+
+# Rufe ein Proc auf mit der Methode `call`
+proc.call 10 # => "10"
+
+# Kontrollstatements
+
+if true
+ "if statement"
+elsif false
+ "else-f, optional"
+else
+ "else, auch optional"
+end
+
+puts "if as a suffix" if true # => if as a suffix
+
+# If als Ausdruck
+a = if 2 > 1
+ 3
+ else
+ 4
+ end
+
+a # => 3
+
+# Bedingter ternärer Ausdruck
+a = 1 > 2 ? 3 : 4 # => 4
+
+# Case-Statement
+cmd = "move"
+
+action = case cmd
+ when "create"
+ "Creating..."
+ when "copy"
+ "Copying..."
+ when "move"
+ "Moving..."
+ when "delete"
+ "Deleting..."
+end
+
+action # => "Moving..."
+
+# Schleifen
+index = 0
+while index <= 3
+ puts "Index: #{index}"
+ index += 1
+end
+# Index: 0
+# Index: 1
+# Index: 2
+# Index: 3
+
+index = 0
+until index > 3
+ puts "Index: #{index}"
+ index += 1
+end
+# Index: 0
+# Index: 1
+# Index: 2
+# Index: 3
+
+# Der bevorzugte Weg, ist `each` zu verwenden.
+(1..3).each do |index|
+ puts "Index: #{index}"
+end
+# Index: 1
+# Index: 2
+# Index: 3
+
+# Der Typ der Variablen hängt vom Typen innerhalb der Kontrollanweisung ab
+if a < 3
+ a = "hello"
+else
+ a = true
+end
+typeof a # => (Bool | String)
+
+if a && b
+ # Hier wird garantiert, dass weder a noch b vom Typ Nil sind
+end
+
+if a.is_a? String
+ a.class # => String
+end
+
+# Funktionen
+def double(x)
+ x * 2
+end
+
+# Funktionen geben implizit den Wert der letzten Anweisung zurück
+# Dies ist auch bei anderen Blöcken der Fall.
+double(2) # => 4
+
+# Klammern müssen nicht gesetzt werden, wenn der Aufruf eindeutig ist
+double 3 # => 6
+double double 3 # => 12
+
+def sum(x, y)
+ x + y
+end
+
+# Funktionsargumente werden mit einem Komma separiert.
+sum 3, 4 # => 7
+
+sum sum(3, 4), 5 # => 12
+
+# yield
+# Alle Methoden haben einen impliziten, optionalen Blockparameter.
+# Dieser kann mit dem Schlüsselwort `yield` aufgerufen werden.
+
+def surround
+ puts '{'
+ yield
+ puts '}'
+end
+
+surround { puts "Hallo Welt" }
+
+# {
+# Hallo Welt
+# }
+
+# Du kannst ein Block einer Funktion übergeben.
+# "&" kennzeichnet eine Referenz zu einem übergebenen Block
+def guests(&block)
+ block.call "some_argument"
+end
+
+# Du kannst eine Liste von Argumenten mitgeben, welche zu einem Array
+# umgewandelt werden.
+# Hierfür ist der Splat-Operator ("*")
+def guests(*array)
+ array.each { |guest| puts guest }
+end
+
+# Wenn eine Methode ein Array zurückgibt, kann destrukturiende Zuordnung
+# verwendet werden.
+def foods
+ ["pancake", "sandwich", "quesadilla"]
+end
+breakfast, lunch, dinner = foods
+breakfast # => "pancake"
+dinner # => "quesadilla"
+
+# Gemäß der Konvention enden alle Methoden, welchen einen Boolean zurückgeben
+# mit einem Fragezeichen.
+5.even? # false
+5.odd? # true
+
+# Und wenn eine Methode mit einem Ausrufezeichen endet, macht sie etwas
+# destruktives. Zum Beispiel wird der Aufrufer verändert. Einige Methoden haben
+# eine !-Version, um eine Änderung zu machen und eine Nicht-!-Version, welche
+# lediglich eine neue veränderte Version zurückgibt.
+
+company_name = "Dunder Mifflin"
+company_name.gsub "Dunder", "Donald" # => "Donald Mifflin"
+company_name # => "Dunder Mifflin"
+company_name.gsub! "Dunder", "Donald"
+company_name # => "Donald Mifflin"
+
+# definiere eine Klasse mit dem Schlüsselwort `class`.
+class Human
+
+# eine Klassenvariable. Diese wird mit allen Instanzen dieser Klasse geteilt.
+ @@species = "H. sapiens"
+
+ # type of name is String
+ @name: String
+
+ # Grundlegender Intialisierer
+ # Weise das Argument der Instanz-Variable "name" zu
+ # Wenn kein Alter angegeben wird, wird der Default (hier 0) genommen.
+ def initialize(@name, @age = 0)
+ end
+
+ # Einfache Setter-Methode
+ def name=(name)
+ @name = name
+ end
+
+ # einfache Getter-Methode
+ def name
+ @name
+ end
+
+ # Die obere Funktionalität kann mit der property-Methode gekapselt werden:
+ property :name
+
+ # Getter/Setter-Methoden können auch individuell erstellt werden:
+ getter :name
+ setter :name
+
+ # eine Klassenmethode verwendet `self` um sich von Instanzmethoden zu
+ # unterscheiden. Diese kann lediglich von einer Klasse aufgerufen werden,
+ # nicht von einer Instanz.
+ def self.say(msg)
+ puts msg
+ end
+
+ def species
+ @@species
+ end
+end
+
+
+# Eine Klasse instanzieren
+jim = Human.new("Jim Halpert")
+
+dwight = Human.new("Dwight K. Schrute")
+
+# Lass uns ein paar Methoden aufrufen
+jim.species # => "H. sapiens"
+jim.name # => "Jim Halpert"
+jim.name = "Jim Halpert II" # => "Jim Halpert II"
+jim.name # => "Jim Halpert II"
+dwight.species # => "H. sapiens"
+dwight.name # => "Dwight K. Schrute"
+
+# Rufe die Klassenmethode auf
+Human.say("Hi") # => gibt Hi aus und gibt `nil` zurück
+
+# Variablen, welche mit @ starten, sind im Scope der Instanz
+class TestClass
+ @var = "Ich bin eine Instanzvariable"
+end
+
+# Variablen, welche mit @@ starten, sind im Scope der Klasse
+class TestClass
+ @@var = "Ich bin eine Klassenvariable"
+end
+
+# Variablen, welche mit einem Großbuchstaben starten, sind Konstanten.
+Var = "Ich bin eine Konstante"
+Var = "Ich kann nicht aktualisiert werden." # Die Konstante Var wurde bereits
+ # initialisiert.
+
+# In Crystal ist Class auch ein Objekt. Dadurch können Klassen Instanzvariablen
+# haben. Klassenvariablen werden mit der Klasse und allen Subklassen geteilt.
+
+# Basisklasse
+class Human
+ @@foo = 0
+
+ def self.foo
+ @@foo
+ end
+
+ def self.foo=(value)
+ @@foo = value
+ end
+end
+
+# abgeleitete Klasse
+class Worker < Human
+end
+
+Human.foo # => 0
+Worker.foo # => 0
+
+Human.foo = 2 # => 2
+Worker.foo # => 0
+
+Worker.foo = 3 # => 3
+Human.foo # => 2
+Worker.foo # => 3
+
+module ModuleExample
+ def foo
+ "foo"
+ end
+end
+
+# Wenn ein Modul mit include eingeschlossen wird, so werden die Methoden an die
+# Instanzen gebunden.
+# Wenn eine Klasse mit einem Modul erweitert wird, so werden die Methoden an die
+# Klasse selbst gebunden.
+
+class Person
+ include ModuleExample
+end
+
+class Book
+ extend ModuleExample
+end
+
+Person.foo # => undefinierte Methode 'foo' für Person:Class
+Person.new.foo # => 'foo'
+Book.foo # => 'foo'
+Book.new.foo # => undefinierte Methode für Book
+
+# Ausnahmebehandlung
+
+# Definiere eine neue Ausnahme
+class MyException < Exception
+end
+
+# Definiere eine weitere Ausnahme
+class MyAnotherException < Exception; end
+
+ex = begin
+ raise MyException.new
+rescue ex1 : IndexError
+ "ex1"
+rescue ex2 : MyException | MyAnotherException
+ "ex2"
+rescue ex3 : Exception
+ "ex3"
+rescue ex4 # fange alle Ausnahmen ab
+ "ex4"
+end
+
+ex # => "ex2"
+```
+
+
+## Weitere Unterlagen
+
+- [offizielle Dokumentation, englisch](https://crystal-lang.org/)