zum Hauptinhalt springen.
UX/UI Design, Entwicklung & TYPO3 in Karlsruhe Karlsruhe | seit 2006

Integration von strukturierten Daten mit TYPO3

01.11.2024, geschätzte Lesedauer: Minute(n). #seo #entwicklung #stakeholder #typo3

Mit schema.org, dem Standard der großen Suchmaschinenanbieter, lassen sich Webseiteninhalte gezielt kennzeichnen, was die Sichtbarkeit und Darstellung in Suchergebnissen deutlich verbessern kann. Rich Snippets mit Bewertungen, Preisen oder Eventdaten steigern die Klickrate und bieten Nutzern auf einen Blick wichtige Informationen. In diesem Artikel zeigen wir, wie sich strukturierte Daten in TYPO3 optimal umsetzen lassen.

Welche Arten strukturierter Daten gibt es?

Es gibt über 800 verschiedene Arten von Schema, die auf schema.org standardisiert sind. Sie lassen sich in zahlreiche Kategorien und Typen unterteilen, die je nach Inhalt der Webseite spezifische Informationen auszeichnen können. Hier sind einige der gängigen Haupttypen:

  • Artikel: Für Nachrichten, Blogbeiträge und Artikelinhalte.
  • Person: Informationen zu Einzelpersonen, wie Name, Beruf und Kontaktinformationen.
  • Organisation: Angaben zu Unternehmen und Organisationen, wie Name, Standort und Kontakt.
  • Produkt: Details zu Produkten, inklusive Name, Preis, Bewertungen und Verfügbarkeit.
  • Event: Für Veranstaltungen mit Angaben zu Datum, Ort und Tickets.
  • Rezept: Spezielle Infos für Kochrezepte, wie Zutaten und Zubereitungszeit.
  • Bewertung: Informationen zu Bewertungen und Rezensionen.
  • FAQ: Häufig gestellte Fragen und Antworten.

Neben diesen gibt es viele weitere spezifische Markups, etwa für kreative Werke, medizinische Inhalte, Bildungsressourcen oder Immobilien. Neue Typen werden laufend hinzugefügt, da Schema.org sich weiterentwickelt, um die Anforderungen unterschiedlicher Branchen zu erfüllen.

Welche Möglichkeiten der Integration gibt es?

Für die Integration von Schema-Markup in TYPO3 gibt es drei zentrale Ansätze:

  1. Manuell Statisch (mit HTML-Element): Hierbei wird das Schema-Markup direkt in den HTML-Code eingebettet. Diese Methode ist simpel aber zeitintensiv und eignet sich nur für statischen Inhalt, der sich selten ändern, da das Schema-Markup bei jeder Änderung manuell angepasst werden muss.
  2. Automatisch Dynamisch mit TYPO3 Schema Extensions: Mit Extensions wie “schema” lassen sich strukturierte Daten dynamisch erzeugen. Die Erweiterung nutzt die Inhalte des CMS automatisch und erstellen daraus Schema-Markup, das stets aktuell bleibt – ideal für umfangreichere und dynamische Seiten.
  3. Automatisch Dynamisch mit TYPO3 eigenen Möglichkeiten: TYPO3 bietet von Haus aus Flexibilität, um Schema-Markup durch TypoScript und Fluid-Templates einzubinden. Diese Lösung ermöglicht eine tiefe Integration und Anpassung an die eigenen Bedürfnisse.

Die erste Variante kommt für uns nicht in Frage, da bei sich ändernden Inhalten viel Aufwand nötig ist - und für Kunden ist das ohnehin nicht praktikabel. Die zweite Variante ist eine gute Option, zumal die genannte Extension in der Vergangenheit mit neuen TYPO3 Versionen direkt kompatibel war.

Im Folgenden werden wir dennoch die dritte Variante weiterverfolgen. Für uns liegt hier der Vorteil, dass wir von keiner Erweiterung abhängig sind und unsere Lösung sehr einfach auf neue Projekte übertragen können.

Einige Beispiele für die Integration strukturierter Daten in TYPO3

Im folgenden zeigen wir die Integration von:

  • Website.
  • Organisation.
  • Breadcrumb.
  • FAQ.
  • Blog und BlogPosting am Beispiel der Erweiterung "news".

Aus Performance-Gründen platzieren wir übrigens - entgegen der von Google vorgeschlagenen Vorgehensweise - die schema.org Markup-Daten statt im Head der Website vor dem schließenden Body Tag. So laden Browser zunächst die Inhalte und befasst sich erst danach mit den zusätzlichen strukturierten Daten. Das ist unproblematisch, denn die großen Suchmaschinen wie Google und Bing lesen und interpretieren den schema.org Markup unabhängig von dessen Position im HTML-Dokument.

Mehrsprachigkeit

Können die Daten automatisch generiert werden, wie das bei Schemas von Breadcrumb, FAQ und Blog der Fall ist, müssen wir uns nicht weiter um die Mehrsprachigkeit kümmern. Werden die Daten statisch via Partial oder TypoScript erzeugt, können wir mit einer Condition die jeweilige Sprachversion ausliefern.

1. Schema WebSite

Das WebSite Schema wird verwendet, um strukturierte Daten über eine Website bereitzustellen. Das WebSite Schema binden wir als statisches Markup in die Startseite ein, denn die Angabe ändern sich in der Regel nicht. Wir demonstrieren zwei Möglichkeiten der Integration, als Condition oder als Partial. Hier beide Beispiele:

# Beispiel 1 - via TypoScript: [traverse(page, "uid") in [1]] page.footerData.100 = TEXT page.footerData.100.value ( <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "WebSite", "name": "Bauunternehmen Muster GmbH", "url": "https://www.bauunternehmen-muster.com/", "description": "Die offizielle Website der Bauunternehmen Muster GmbH mit Informationen zu Bauleistungen, Projekten und Kontaktmöglichkeiten.", "publisher": { "@type": "Organization", "name": "Bauunternehmen Muster GmbH" } } </script> ) [END]
<!-- Beispiel 2 - via Partial "SchemaWebsite.html":--> <f:asset.script identifier="SchemaWebsite" additionalAttributes="{type:'application/ld+json'}"> { "website": { "@type": "WebSite", "name": <f:format.json>Bauunternehmen Muster GmbH</f:format.json>, "url": "https://www.bauunternehmen-muster.com/", "description": <f:format.json>Die offizielle Website der Bauunternehmen Muster GmbH mit Informationen zu Bauleistungen, Projekten und Kontaktmöglichkeiten.</f:format.json>, "publisher": { "@type": "Organization", "name": <f:format.json>Bauunternehmen Muster GmbH</f:format.json> } } } </f:asset.script>

In der zweite Variante muss jetzt das Partial (hier: “SchemaWebsite.html”) noch im Template der Startseite aufgerufen werden (ganz am Ende vor dem </f:section>):

"description": {data.description -> f:format.json()},

Alternativ könnte der Description Text auch aus der Seiten Beschreibung generiert werden, damit wäre ggfs. auch dieses Schema bei Mehrsprachigkeit dynamisch:

<f:render partial="SchemaWebsite" arguments="{_all}" />

2. Schema “Organisation”

Das Organization Schema wird verwendet, um strukturierte Daten über eine Organisation bereitzustellen. Dies kann Suchmaschinen und anderen Anwendungen helfen, Informationen über die Organisation besser zu verstehen und darzustellen. Auch das Schema Organisation binden wir als statisches Markup über ein Partial in die Startseite ein.

Durch ein Partial kann der Format JSON Viewhelper verwenden werden, der die Sonderzeichen umwandelt. In einer schema.org JSON-LD Datei können Sonderzeichen und Umlaute wie ä, ö, ü, / etc. verwendet werden. JSON unterstützt Unicode, sodass diese Zeichen korrekt dargestellt werden können. Es wäre also nicht notwendig, sorgt aber für eine stringente Schreibweise, denn bei späteren Beispielen wird der Viewhelper notwendig.

<f:asset.script identifier="SchemaOrganization" additionalAttributes="{type:'application/ld+json'}"> { "@context": "https://schema.org", "@type": "Organization", "name": <f:format.json>Bauunternehmen Muster GmbH</f:format.json>, "url": "https://www.bauunternehmen-muster.com/", "logo": "https://www.bauunternehmen-muster.com/fileadmin/assets/schema/logo.svg", "description": <f:format.json>Bauunternehmen Muster GmbH bietet umfassende Bauleistungen von der Planung bis zur Fertigstellung. Spezialisiert auf Wohn- und Gewerbebauten, Renovierungen und Sanierungen.</f:format.json>, "foundingDate": <f:format.json>1995</f:format.json>, "founder": { "@type": "Person", "name": <f:format.json>Max Mustermann</f:format.json> }, "contactPoint": { "@type": "ContactPoint", "telephone": <f:format.json>+49 (0)123 456 789</f:format.json>, "contactType": <f:format.json>Kundensupport</f:format.json>, "areaServed": DE, "availableLanguage": [German, English] }, "address": { "@type": "PostalAddress", "streetAddress": <f:format.json>Musterstraße 1</f:format.json>, "addressLocality": <f:format.json>Musterstadt</f:format.json>, "postalCode": <f:format.json>12345</f:format.json>, "addressCountry": <f:format.json>DE</f:format.json> }, "makesOffer": [ { "@type": "Offer", "itemOffered": { "@type": "Wohnungsbau", "name": <f:format.json>Full-Service-Webentwicklung</f:format.json>, "description": <f:format.json>Komplettlösungen für den Bau von Wohngebäuden, von der Planung bis zur Schlüsselübergabe.</f:format.json>, "serviceType": <f:format.json>Wohnungsbau</f:format.json> } }, { "@type": "Offer", "itemOffered": { "@type": "Service", "name": <f:format.json>Sanierungen</f:format.json>, "description": <f:format.json>Sanierungsarbeiten zur Verbesserung der Energieeffizienz und der strukturellen Integrität von Gebäuden.</f:format.json>, "serviceType": <f:format.json>Sanierungen</f:format.json> } } } ], "subOrganization": { "@type": "Organization", "name": <f:format.json>Muster Bauprojekte</f:format.json>, "url": "https://www.muster-bauprojekte.com" } } </f:asset.script>

Auch hier muss das Partial (hier: “SchemaOrganization.html”) noch im Template der Startseite aufgerufen werden (ganz am Ende vor dem </f:section>):

<f:render partial="SchemaOrganization" arguments="{_all}" />

3. Schema Breadcrumb

Suchmaschinen können Breadcrumb-Daten verwenden, um die Struktur Ihrer Website besser zu verstehen und diese Informationen in den Suchergebnissen anzuzeigen. Das Breadcrumb Schema integrieren wir via Partial und TypoScript.

# Teil 1 - Das Typoscript: page { 10 { dataProcessing { 100 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor 100 { special = rootline as = SchemaBreadcrumb } } } }
<!-- Teil 2 - Das Partial "SchemaBreadcrumb.html" --> <f:asset.script identifier="SchemaBreadcrumb" additionalAttributes="{type:'application/ld+json'}"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [ <f:for each="{SchemaBreadcrumb}" as="item" iteration="counter"> <f:variable name="pageLink">https://www.bauunternehmen-muster.com{item.link}</f:variable> { "@type": "ListItem", "position": <f:format.json>{counter.cycle}</f:format.json>, "name": <f:format.json>{item.title}</f:format.json>, "item": "{pageLink}" } <f:if condition="{counter.isLast}"> <f:then></f:then> <f:else>,</f:else> </f:if> </f:for> ] } </f:asset.script>

Das Partial muss in alle Templates, in denen das Schema erscheinen soll, aufgerufen werden (vor dem schließenden Section ViewHelper):

<!-- Teil 3: Integration in die Templates der Seite: <f:render partial="SchemaBreadcrumb" arguments="{_all}" />

4. Schema FAQPage

Das FAQPage Schema wird verwendet, um strukturierte Daten für eine Seite mit häufig gestellten Fragen (Frequently Asked Questions, FAQ) bereitzustellen. Dies kann dazu führen, dass die Fragen und Antworten direkt in den Suchergebnissen angezeigt werden.

Das nachfolgende Code Beispiel zeigt das Template eines Inhaltselements “FAQs”, direkt dahinter ist das Schema eingefügt.

<div class="faq"> <f:for each="{data.faq_items}" as="data_item"> <div class="faq_question"> {data_item.faq_question} </div> <div class="faq_answer"> {data_item.faq_answer -> f:format.html()} </div> </f:for> </div> <f:asset.script identifier="faq" additionalAttributes="{type:'application/ld+json'}"> { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ <f:for each="{data.faq_items}" as="data_item" iteration="counter"> { "@type": "Question", "name": <f:format.json>{data_item.faq_question -> f:format.stripTags()}</f:format.json>, "acceptedAnswer": { "@type": "Answer", "text": <f:format.json>{data_item.faq_answer -> f:format.stripTags()}</f:format.json> } } <f:if condition="{counter.isLast}"> <f:then></f:then> <f:else>,</f:else> </f:if> </f:for> ] } </f:asset.script>

5. Schema Blog und BlogPosting

Die Blog und BlogPosting Schema werden verwendet, um strukturierte Daten über einen Blog bereitzustellen. Sie helfen Suchmaschinen, die Inhalte und Struktur des Blogs besser zu verstehen, was die Auffindbarkeit und Darstellung in den Suchergebnissen verbessern kann.

Wir demonstrieren das am Beispiel der beliebten TYPO3 Erweiterung News.

5.1 Schema Blog für die Listenansicht

Für das Schema erstellen wir ein Partial <SchemaList.html>, das wir in den Partials der News-Extension in einem Unterverzeichnis <Schema> platzieren.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers" xmlns:rx="http://typo3.org/ns/Reelworx/RxShariff/ViewHelper" data-namespace-typo3-fluid="true"> <f:asset.script identifier="blogList" additionalAttributes="{type:'application/ld+json'}"> { "@context": "https://schema.org", "@type": "Blog", "name": <f:format.json>Bauunternehmen Muster Blog</f:format.json>, "description": <f:format.json>Alles rund um die Themen Bauen, Sanieren, Renovieren.</f:format.json>, "url": "https://www.bauunternehmen-muster.com/blog", "publisher": { "@type": "Organization", "name": "Bauunternehmen Muster GmbH", "logo": { "@type": "ImageObject", "url": "https://www.bauunternehmen-muster.com/fileadmin/assets/schema/logo.svg" } }, "blogPost": [ <f:for each="{news}" as="newsItem" iteration="counter"> { "@type": "BlogPosting", "headline": <f:format.json>{newsItem.title}</f:format.json>, "description": <f:format.json><f:format.crop maxCharacters="200">{newsItem.teaser -> f:format.stripTags()}</f:format.crop></f:format.json>, "url": "<n:link uriOnly='1' newsItem='{newsItem}' settings='{settings}' configuration="{forceAbsoluteUrl: 1}" />", "datePublished": <f:format.json><f:format.date format="Y-m-d\\TH:i:sP">{newsItem.datetime}</f:format.date></f:format.json> <f:if condition="{newsItem.author}"> , "author": { "@type": "Person", "name": <f:format.json>{newsItem.author}</f:format.json>, "url": "https://bauunternehmen-muster.com/blog/autoren" } </f:if> } <f:if condition="{counter.isLast}"> <f:then></f:then> <f:else>,</f:else> </f:if> </f:for> ] } </f:asset.script> </html>

Dieses Partial muss jetzt noch in der Template Datei <List.html> aufgerufen werden, ganz am Ende vor </f:section>:

<f:render partial="Schema/SchemaList" arguments="{_all}" />

 

5.2 Schema BlogPosting für die Detailansicht

Auch hier erstellen wir wieder ein Partial, das wir als <SchemaDetails.html> in den Partials der News-Extension in das Verzeichnis <Schema> neben das bereits erstellte <SchemaList.html> platzieren.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers" xmlns:rx="http://typo3.org/ns/Reelworx/RxShariff/ViewHelper" data-namespace-typo3-fluid="true"> <f:asset.script identifier="blogDetail" additionalAttributes="{type:'application/ld+json'}"> { "@context": "https://schema.org", "@type": "BlogPosting", "mainEntityOfPage": { "@type": "WebPage", "@id": "<n:link uriOnly='1' newsItem='{newsItem}' settings='{settings}' configuration="{forceAbsoluteUrl: 1}" />" }, "headline": <f:format.json>{newsItem.title}</f:format.json>, <f:if condition="{newsItem.author}"> "author": { "@type": "Person", "name": <f:format.json>{newsItem.author}</f:format.json>, "url": "https://bauunternehmen-muster.com/blog/autoren" }, </f:if> "publisher": { "@type": "Organization", "name": <f:format.json>Bauunternehmen Muster GmbH</f:format.json>, "logo": { "@type": "ImageObject", "url": "https://www.bauunternehmen-muster.com/fileadmin/assets/schema/logo.svg" } }, "datePublished": <f:format.json><f:format.date format="Y-m-d\\TH:i:sP">{newsItem.datetime}</f:format.date></f:format.json>, "description": <f:format.json><f:format.crop maxCharacters="200">{newsItem.teaser -> f:format.stripTags()}</f:format.crop></f:format.json>, "articleBody": <f:format.json><f:format.crop maxCharacters="450">{newsItem.bodytext -> f:format.stripTags()}</f:format.crop></f:format.json> } </f:asset.script> </html>

Das BlogPosting Partial muss in der Template Datei <Detail.html> noch aufgerufen werden, ganz am Ende vor </f:section>:

<f:render partial="Schema/SchemaDetails" arguments="{_all}" />

Testen der Schema-Daten

Nachdem die strukturierten Daten in der TYPO3-Website integriert sind, müssen diese getestet werden. So können wir sicherstellen, dass sie korrekt implementiert sind und von Suchmaschinen richtig interpretiert werden. Es gibt mehrere Tools, die dabei helfen:

  1. Google Rich Results Test: Dieses Tool überprüft, ob die strukturierten Daten für die Google-Suchergebnissen geeignet sind.
  2. Schema Markup Validator: Das Tool von schema.org ist hilfreich, um die Struktur und Syntax unserer Schema-Daten zu überprüfen. Es zeigt detaillierte Informationen und mögliche Fehler an, die behoben werden müssen.

Fazit

Bringt man noch keine tiefgreifenden Vorkenntnisse mit, erhält man durch die eigenständige Integration von Schema-Daten ein sehr gutes Verständnis für das Thema. Dies ermöglicht die Implementierung gezielt an die spezifischen Anforderungen der Website anzupassen und flexibel auf Änderungen zu reagieren.

Die Integration von strukturierten Daten in TYPO3 ohne zusätzliche Erweiterungen ist relativ einfach und bietet eine hohe Flexibilität. Durch die Verwendung von Fluid-Templates können die strukturierten Daten dynamisch und wiederverwendbar gestaltet werden. Dies ermöglicht es, den Code effizient zu verwalten und an die spezifischen Anforderungen der Website anzupassen. Aber Vorsicht: In manchen Schemas verbleiben je nach Automatisierungsgrad statische Angaben wie Links, Beschreibungen, Domains oder Unternehmensangaben. Diese müssen bei Änderungen dringend angepasst werden.

Es ist jedoch wichtig, eine klare Strategie zu entwickeln, um sicherzustellen, dass die Daten sinnvoll ausgegeben werden. Beispielsweise wird ein Inhaltselement “Akkordeon” nicht zwangsläufig als FAQ verwendet. Daher sollte beispielsweise sorgfältig überlegt werden, welche strukturierten Daten für welche Inhaltselemente verwendet werden.

Je nach Art der Website können weitere Schema-Typen sinnvoll sein. Insbesondere die Verwendung von Schemas wie Product, Event oder Review kann die Sichtbarkeit und Auffindbarkeit der Website in den Suchergebnissen verbessern.

Insgesamt bietet die Integration von strukturierten Daten in TYPO3 eine leistungsstarke Möglichkeit, die Website zu optimieren und die Benutzererfahrung zu verbessern.

Skyline Karlsruhe