Add more structured metadata

This commit is contained in:
Kevin Kandlbinder 2021-09-25 14:00:50 +02:00
parent df9e2b2bb8
commit 412ee72112
10 changed files with 148 additions and 5 deletions

View file

@ -1,3 +1,4 @@
{ {
"i18n-ally.localesPaths": "locales" "i18n-ally.localesPaths": "locales",
"i18n-ally.keystyle": "nested"
} }

View file

@ -10,9 +10,18 @@ module.exports = {
languages: ["en", "de"], languages: ["en", "de"],
contactEmail: "kevin@kevink.dev", contactEmail: "kevin@kevink.dev",
contactPhone: "+4941068068004", contactPhone: "+4941068068004",
address: "25451 Quickborn, Schleswig-Holstein, Germany",
mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2", mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2",
contactTwitter: "Unkn0wnKevin", contactTwitter: "Unkn0wnKevin",
contactGitHub: "Unkn0wnCat", contactGitHub: "Unkn0wnCat",
contactMastodon: "@kevin@1in1.net", contactMastodon: "@kevin@1in1.net",
contactMastodonHref: "https://mastodon.1in1.net/@kevin", contactMastodonHref: "https://mastodon.1in1.net/@kevin",
givenName: "Kevin",
familyName: "Kandlbinder",
birthDate: "2001-03-11",
gender: "Male",
height: "168 cm",
nationality: "Germany",
personImage: "./content/images/kevin-kandlbinder-04.jpg",
sameAs: ["https://mastodon.1in1.net/@kevin", "https://github.com/Unkn0wnCat", "https://unkn0wncat.net/", "https://twitter.com/@Unkn0wnKevin", "https://unsplash.com/@unkn0wncat"]
}; };

View file

@ -11,10 +11,19 @@ module.exports = {
contactEmail: extConfig.contactEmail, contactEmail: extConfig.contactEmail,
contactPhone: extConfig.contactPhone, contactPhone: extConfig.contactPhone,
mapsLink: extConfig.mapsLink, mapsLink: extConfig.mapsLink,
address: extConfig.address,
contactTwitter: extConfig.contactTwitter, contactTwitter: extConfig.contactTwitter,
contactGitHub: extConfig.contactGitHub, contactGitHub: extConfig.contactGitHub,
contactMastodon: extConfig.contactMastodon, contactMastodon: extConfig.contactMastodon,
contactMastodonHref: extConfig.contactMastodonHref, contactMastodonHref: extConfig.contactMastodonHref,
givenName: extConfig.givenName,
familyName: extConfig.familyName,
birthDate: extConfig.birthDate,
gender: extConfig.gender,
height: extConfig.height,
nationality: extConfig.nationality,
personImage: extConfig.personImage,
sameAs: extConfig.sameAs
}, },
plugins: [ plugins: [
`gatsby-plugin-eslint`, `gatsby-plugin-eslint`,

View file

@ -2,9 +2,11 @@ import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Helmet } from "gatsby-plugin-react-i18next"; import { Helmet } from "gatsby-plugin-react-i18next";
import { useStaticQuery, graphql } from "gatsby"; import { useStaticQuery, graphql } from "gatsby";
import { useLocation } from "@reach/router"
import { useTranslation } from "gatsby-plugin-react-i18next"; import { useTranslation } from "gatsby-plugin-react-i18next";
import useSiteMetadata from "../helpers/useSiteMetadata";
function SEO({ description, meta, title }) { function SEO({ description, meta, title, speakable, image, children }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { site } = useStaticQuery( const { site } = useStaticQuery(
graphql` graphql`
@ -22,6 +24,10 @@ function SEO({ description, meta, title }) {
const metaDescription = description || t("siteDescription"); const metaDescription = description || t("siteDescription");
const siteMeta = useSiteMetadata();
const location = useLocation();
return ( return (
<Helmet <Helmet
title={title} title={title}
@ -79,6 +85,45 @@ function SEO({ description, meta, title }) {
data-domain="kevink.dev" data-domain="kevink.dev"
src="https://analytics.kevink.dev/js/plausible.js" src="https://analytics.kevink.dev/js/plausible.js"
></script> ></script>
{
image && [
<meta name="twitter:image" content={meta.siteUrl + image} key="twimg"/>,
<meta name="og:image" content={meta.siteUrl + image} key="ogimg"/>,
]
}
<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org/",
"@type": "WebPage",
"name": title,
"url": siteMeta.siteUrl+location.pathname,
"speakable": speakable,
"image": meta.siteUrl + image,
"about": {
"@type": "Person",
"name": siteMeta.givenName + " " + siteMeta.familyName,
"givenName": siteMeta.givenName,
"familyName": siteMeta.familyName,
"birthDate": siteMeta.birthDate,
"address": siteMeta.address,
"email": siteMeta.contactEmail,
"telephone": siteMeta.contactPhone,
"gender": siteMeta.gender,
"height": siteMeta.height,
"nationality": {
"@type": "Country",
"name": siteMeta.nationality
},
"image": siteMeta.siteUrl + "/owner.jpg",
"sameAs": siteMeta.sameAs
}
})}
</script>
{children}
</Helmet> </Helmet>
); );
} }
@ -92,6 +137,9 @@ SEO.propTypes = {
description: PropTypes.string, description: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object), meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
speakable: PropTypes.any,
image: PropTypes.string,
children: PropTypes.any
}; };
export default SEO; export default SEO;

View file

@ -0,0 +1,37 @@
import { useStaticQuery, graphql } from 'gatsby';
const useSiteMetadata = () => {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
keywords
author
siteUrl
payPalMail
contactEmail
contactPhone
mapsLink
contactTwitter
contactGitHub
contactMastodon
contactMastodonHref
givenName
familyName
birthDate
address
gender
height
nationality
sameAs
}
}
}
`,
);
return site.siteMetadata;
};
export default useSiteMetadata;

View file

@ -16,7 +16,9 @@ class Layout extends React.Component {
lang={this.props.lang} lang={this.props.lang}
meta={this.props.meta} meta={this.props.meta}
title={this.props.title} title={this.props.title}
/> image={this.props.image}
speakable={this.props.speakable}
>{this.props.seoAdditional ?? null}</SEO>
<Navigation isHome={this.props.transparentTopbar} /> <Navigation isHome={this.props.transparentTopbar} />
<div id="content" role="main"> <div id="content" role="main">
{this.props.children} {this.props.children}
@ -57,6 +59,9 @@ Layout.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
transparentTopbar: PropTypes.bool, transparentTopbar: PropTypes.bool,
children: PropTypes.any.isRequired, children: PropTypes.any.isRequired,
seoAdditional: PropTypes.any,
image: PropTypes.string,
speakable: PropTypes.any
}; };
export default Layout; export default Layout;

View file

@ -112,7 +112,16 @@ const IndexPage = (props) => {
let file = props.data.file; let file = props.data.file;
return ( return (
<Layout title="Kevin Kandlbinder" transparentTopbar={true} description={t("siteDescription")}> <Layout title="Kevin Kandlbinder" transparentTopbar={true} description={t("siteDescription")}
image={meta.siteUrl + "/owner.jpg"}
speakable={{
"@type": "SpeakableSpecification",
"xPath": [
"/html/head/title",
"/html/head/meta[@name='description']/@content",
"article"
]
}}>
<section className={styles.heroSection}> <section className={styles.heroSection}>
<div <div
className={styles.heroSectionBg} className={styles.heroSectionBg}

View file

@ -6,6 +6,7 @@ import PropTypes from "prop-types";
import * as styles from "./projects.module.scss"; import * as styles from "./projects.module.scss";
import { GatsbyImage } from "gatsby-plugin-image"; import { GatsbyImage } from "gatsby-plugin-image";
import useSiteMetadata from "../helpers/useSiteMetadata";
export const query = graphql` export const query = graphql`
query GetProjects($language: String) { query GetProjects($language: String) {
@ -21,6 +22,7 @@ export const query = graphql`
childImageSharp { childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH) gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
} }
publicURL
} }
shortDescription shortDescription
} }
@ -39,8 +41,29 @@ export const query = graphql`
const ProjectsPage = ({ data }) => { const ProjectsPage = ({ data }) => {
const { t } = useI18next(); const { t } = useI18next();
const meta = useSiteMetadata();
return ( return (
<Layout title={t("projects")} description={t("projectsDescription")}> <Layout title={t("projects")} description={t("projectsDescription")} seoAdditional={
<script type="application/ld+json">
{JSON.stringify(
{
"@context":"https://schema.org",
"@type":"ItemList",
"itemListElement":data.allProjectsJson.nodes.map((project, i) => {
return {
"@type":"ListItem",
"position":i,
"url": meta.siteUrl+"/projects/"+project.urlname,
"image": project.image.publicURL,
"name": project.name,
"description": project.shortDescription
}
})
}
)}
</script>
}>
<section> <section>
<article> <article>
<h1> <h1>

View file

@ -26,6 +26,7 @@ export const query = graphql`
childImageSharp { childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH) gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
} }
publicURL
} }
shortDescription shortDescription
} }
@ -64,6 +65,7 @@ const ProjectTemplate = ({ data }) => {
description={project.shortDescription} description={project.shortDescription}
title={t("project") + ": " + projectName} title={t("project") + ": " + projectName}
transparentTopbar={true} transparentTopbar={true}
image={project.image.publicURL}
> >
<section className={styles.projectHeader}> <section className={styles.projectHeader}>
<div style={{ paddingTop: 0 }}> <div style={{ paddingTop: 0 }}>

BIN
static/owner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB