Added SEO and structured data
This commit is contained in:
parent
fdc26c9b92
commit
c6d0fa30bb
8 changed files with 253 additions and 24 deletions
|
|
@ -51,5 +51,14 @@
|
|||
"contact_cta_body": "Interesse an einer Zusammenarbeit?",
|
||||
"contact_cta_link": "Nachricht senden",
|
||||
"contact_heading": "Kontakt",
|
||||
"contact_intro": "Interesse an einer Zusammenarbeit oder Feedback zum Blog? Schreib mir gerne über einen meiner Socials oder per E-Mail."
|
||||
"contact_intro": "Interesse an einer Zusammenarbeit oder Feedback zum Blog? Schreib mir gerne über einen meiner Socials oder per E-Mail.",
|
||||
|
||||
"meta_home_title": "Business Analyst & Solution Designer",
|
||||
"meta_home_description": "Aron August Hohmann – Business Analyst & Solution Designer in Wien. Anforderungsanalyse, Solution Design und Systemintegration für digitale Plattformen.",
|
||||
|
||||
"meta_blog_title": "Blog",
|
||||
"meta_blog_description": "Artikel über Solution Design, Plattformentscheidungen und digitale Prozesse von Aron August Hohmann.",
|
||||
|
||||
"meta_contact_title": "Kontakt",
|
||||
"meta_contact_description": "Kontakt aufnehmen mit Aron August Hohmann – Business Analyst & Solution Designer in Wien."
|
||||
}
|
||||
|
|
@ -52,5 +52,14 @@
|
|||
"contact_cta_link": "Send a message",
|
||||
"contact_heading": "Contact",
|
||||
"contact_intro": "Interested in working together or have feedback about the blog? Reach out via one of my socials or drop me an email.",
|
||||
"contact_email_label": "Email"
|
||||
"contact_email_label": "Email",
|
||||
|
||||
"meta_home_title": "Business Analyst & Solution Designer",
|
||||
"meta_home_description": "Aron August Hohmann – Business Analyst & Solution Designer based in Vienna. Requirements analysis, solution design, and system integration for digital platforms.",
|
||||
|
||||
"meta_blog_title": "Blog",
|
||||
"meta_blog_description": "Articles on solution design, platform decisions, and digital processes by Aron August Hohmann.",
|
||||
|
||||
"meta_contact_title": "Contact",
|
||||
"meta_contact_description": "Get in touch with Aron August Hohmann – Business Analyst & Solution Designer based in Vienna."
|
||||
}
|
||||
57
src/lib/components/SEO.svelte
Normal file
57
src/lib/components/SEO.svelte
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<script lang="ts">
|
||||
interface ArticleMeta {
|
||||
publishedTime: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
type?: 'website' | 'article';
|
||||
canonicalUrl: string;
|
||||
article?: ArticleMeta;
|
||||
jsonLd?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
let {
|
||||
title,
|
||||
description,
|
||||
type = 'website',
|
||||
canonicalUrl,
|
||||
article,
|
||||
jsonLd
|
||||
}: Props = $props();
|
||||
|
||||
const fullTitle = `${title} | Aron August Hohmann`;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{fullTitle}</title>
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- Canonical -->
|
||||
<link rel="canonical" href={canonicalUrl} />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content={type} />
|
||||
<meta property="og:title" content={fullTitle} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonicalUrl} />
|
||||
<meta property="og:site_name" content="Aron August Hohmann" />
|
||||
<meta property="og:image" content="https://aronhohmann.eu/images/aron_august_hohmann.avif" />
|
||||
|
||||
<!-- Article-specific OG -->
|
||||
{#if type === 'article' && article}
|
||||
<meta property="article:published_time" content={article.publishedTime} />
|
||||
{#if article.tags}
|
||||
{#each article.tags as tag (tag)}
|
||||
<meta property="article:tag" content={tag} />
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<!-- JSON-LD -->
|
||||
{#if jsonLd}
|
||||
{@html `<script type="application/ld+json">${JSON.stringify(jsonLd)}<\/script>`}
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
|
@ -4,16 +4,29 @@
|
|||
import { getLocale, locales, localizeHref } from '$lib/paraglide/runtime';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { page } from '$app/state';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
const localeNames: Record<string, string> = {
|
||||
en: 'English',
|
||||
de: 'Deutsch'
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
<!-- hreflang alternates -->
|
||||
{#each [...locales] as locale (locale)}
|
||||
<link
|
||||
rel="alternate"
|
||||
hreflang={locale}
|
||||
href={`https://aronhohmann.eu${page.data.translationSlug
|
||||
? localizeHref(`/blog/${page.data.translationSlug}`, { locale })
|
||||
: page.data.metadata
|
||||
? localizeHref('/blog', { locale })
|
||||
: localizeHref(page.url.pathname, { locale })}`}
|
||||
/>
|
||||
{/each}
|
||||
<link rel="alternate" hreflang="x-default" href={`https://aronhohmann.eu${page.url.pathname}`} />
|
||||
</svelte:head>
|
||||
|
||||
<!-- Header -->
|
||||
<header class="border-b fixed border-black w-full z-50">
|
||||
|
|
@ -21,19 +34,18 @@
|
|||
<nav class="flex gap-5 text-white items-center justify-center min-h-10" style="background-color: #0a3d57;">
|
||||
{#each [...locales].sort() as locale}
|
||||
<a
|
||||
href={page.data.translationSlug
|
||||
? localizeHref(`/blog/${page.data.translationSlug}`, { locale })
|
||||
: page.data.metadata
|
||||
href={page.data.translationSlug
|
||||
? localizeHref(`/blog/${page.data.translationSlug}`, { locale })
|
||||
: page.data.metadata
|
||||
? localizeHref('/blog', { locale })
|
||||
: localizeHref(page.url.pathname, { locale })}
|
||||
data-sveltekit-reload
|
||||
class="transition cursor-pointer"
|
||||
data-sveltekit-reload
|
||||
class="transition cursor-pointer"
|
||||
>
|
||||
{getLocale() === locale ? `> ${localeNames[locale]} <` : localeNames[locale]}
|
||||
{getLocale() === locale ? `> ${localeNames[locale]} <` : localeNames[locale]}
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<!-- Main Navigation -->
|
||||
<nav id="main" class="bg-white border-b border-gray-200 flex justify-center items-center gap-8 px-5 min-h-10">
|
||||
<a href="/{getLocale()}">{ m.nav_about() }</a>
|
||||
|
|
@ -51,14 +63,13 @@
|
|||
<footer class="flex flex-col justify-center items-center gap-5 mt-30 text-white px-1.5 pt-12" style="background-color: #0a3d57;">
|
||||
<div class="mb-15">
|
||||
<img
|
||||
src="/images/wappen_siegel.svg"
|
||||
alt=""
|
||||
width="200"
|
||||
height="200"
|
||||
style="filter: invert(1);"
|
||||
src="/images/wappen_siegel.svg"
|
||||
alt=""
|
||||
width="200"
|
||||
height="200"
|
||||
style="filter: invert(1);"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-2 mb-15">
|
||||
<a href="/{getLocale()}">{ m.nav_about() }</a>
|
||||
<span> | </span>
|
||||
|
|
@ -68,13 +79,10 @@
|
|||
<span> | </span>
|
||||
<a href="/{getLocale()}/links">Links</a>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<small>© Aron August Hohmann</small>
|
||||
</div>
|
||||
|
||||
<div class="h-0.5"></div>
|
||||
|
||||
<nav class="text-xs text-center text-gray-200 w-full flex justify-center items-center gap-2 md:gap-5 min-h-10" style="background-color: #072d40;">
|
||||
<a href="/{getLocale()}/l/imp">{ m.footer_imprint() }</a>
|
||||
<span> | </span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,44 @@
|
|||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { getLocale } from '$lib/paraglide/runtime';
|
||||
import SEO from '$lib/components/SEO.svelte';
|
||||
import { page } from '$app/state';
|
||||
|
||||
const locale = getLocale();
|
||||
const canonicalUrl = `https://aronhohmann.eu${page.url.pathname}`;
|
||||
|
||||
const personJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Person',
|
||||
name: 'Aron August Hohmann',
|
||||
url: 'https://aronhohmann.eu',
|
||||
image: 'https://aronhohmann.eu/images/aron_august_hohmann.avif',
|
||||
jobTitle: 'Business Analyst & Solution Designer',
|
||||
worksFor: {
|
||||
'@type': 'Organization',
|
||||
name: 'Holzkern – Time for Nature GmbH'
|
||||
},
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
addressLocality: 'Vienna',
|
||||
addressCountry: 'AT'
|
||||
},
|
||||
sameAs: [
|
||||
'https://www.linkedin.com/in/aron-august-hohmann/',
|
||||
'https://aronhohmann.eu'
|
||||
]
|
||||
};
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title={m.meta_home_title()}
|
||||
description={m.meta_home_description()}
|
||||
type="website"
|
||||
{canonicalUrl}
|
||||
jsonLd={personJsonLd}
|
||||
/>
|
||||
|
||||
|
||||
<!-- Hero -->
|
||||
<section class="flex flex-col md:flex-row items-center md:items-start gap-10 max-w-3xl mx-auto px-4 py-16">
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -1,10 +1,45 @@
|
|||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import {getLocale} from "$lib/paraglide/runtime";
|
||||
import SEO from '$lib/components/SEO.svelte';
|
||||
import { page } from '$app/state';
|
||||
|
||||
const locale = getLocale();
|
||||
const canonicalUrl = `https://aronhohmann.eu${page.url.pathname}`;
|
||||
|
||||
const personJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Person',
|
||||
name: 'Aron August Hohmann',
|
||||
url: 'https://aronhohmann.eu',
|
||||
image: 'https://aronhohmann.eu/images/aron_august_hohmann.avif',
|
||||
jobTitle: 'Business Analyst & Solution Designer',
|
||||
worksFor: {
|
||||
'@type': 'Organization',
|
||||
name: 'Holzkern – Time for Nature GmbH'
|
||||
},
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
addressLocality: 'Vienna',
|
||||
addressCountry: 'AT'
|
||||
},
|
||||
sameAs: [
|
||||
'https://www.linkedin.com/in/aron-august-hohmann/',
|
||||
'https://aronhohmann.eu'
|
||||
]
|
||||
};
|
||||
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title={m.meta_blog_title()}
|
||||
description={m.meta_blog_description()}
|
||||
type="website"
|
||||
{canonicalUrl}
|
||||
jsonLd={personJsonLd}
|
||||
/>
|
||||
|
||||
<div class="max-w-2xl mx-auto px-4 py-12">
|
||||
<h1 class="text-3xl font-bold mb-8">Blog</h1>
|
||||
|
||||
|
|
@ -16,7 +51,7 @@
|
|||
<li class="py-6">
|
||||
<a href={`/${getLocale()}/blog/${post.slug}`} class="group block">
|
||||
<p class="text-sm text-brand/80">{post.date}</p>
|
||||
<h2 class="text-xl font-semibold group-hover:text-[#156793] transition">{post.title}</h2>
|
||||
<h2 class="text-xl font-semibold group-hover:text-brand transition">{post.title}</h2>
|
||||
<p class="text-gray-600">{post.description}</p>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,51 @@
|
|||
<script lang="ts">
|
||||
import { m } from '\$lib/paraglide/messages.js';
|
||||
import SEO from '$lib/components/SEO.svelte';
|
||||
import { page } from '$app/state';
|
||||
|
||||
let { data } = $props();
|
||||
const { metadata, content: Content } = data;
|
||||
|
||||
import { m } from '\$lib/paraglide/messages.js';
|
||||
const canonicalUrl = `https://aronhohmann.eu${page.url.pathname}`;
|
||||
|
||||
const blogPostingJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: metadata.title,
|
||||
description: metadata.description,
|
||||
datePublished: metadata.date,
|
||||
url: canonicalUrl,
|
||||
inLanguage: metadata.lang,
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Aron August Hohmann',
|
||||
url: 'https://aronhohmann.eu'
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Person',
|
||||
name: 'Aron August Hohmann',
|
||||
url: 'https://aronhohmann.eu'
|
||||
},
|
||||
...(metadata.tags && { keywords: metadata.tags.join(', ') })
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title={metadata.title}
|
||||
description={metadata.description}
|
||||
type="article"
|
||||
{canonicalUrl}
|
||||
article={{
|
||||
publishedTime: metadata.date,
|
||||
tags: metadata.tags
|
||||
}}
|
||||
jsonLd={blogPostingJsonLd}
|
||||
/>
|
||||
|
||||
<div class="max-w-2xl mx-auto px-4 py-12">
|
||||
|
||||
<a href="/blog" class="text-sm text-[#156793] hover:text-[#0f4f70] transition mb-8 block">← { m.back() }</a>
|
||||
<a href="/blog" class="text-sm text-brand hover:text-[#0f4f70] transition mb-8 block">← { m.back() }</a>
|
||||
|
||||
<h1 class="text-4xl font-bold mb-2">{data.metadata.title}</h1>
|
||||
<p class="text-gray-400 text-sm mb-8">{data.metadata.date}</p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
const email = atob('bWFpbEBhcm9uaG9obWFubi5ldQ==');
|
||||
import { getLocale } from '$lib/paraglide/runtime';
|
||||
import SEO from '$lib/components/SEO.svelte';
|
||||
import { page } from '$app/state';
|
||||
|
||||
const locale = getLocale();
|
||||
const canonicalUrl = `https://aronhohmann.eu${page.url.pathname}`;
|
||||
|
||||
const personJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Person',
|
||||
name: 'Aron August Hohmann',
|
||||
url: 'https://aronhohmann.eu',
|
||||
image: 'https://aronhohmann.eu/images/aron_august_hohmann.avif',
|
||||
jobTitle: 'Business Analyst & Solution Designer',
|
||||
worksFor: {
|
||||
'@type': 'Organization',
|
||||
name: 'Holzkern – Time for Nature GmbH'
|
||||
},
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
addressLocality: 'Vienna',
|
||||
addressCountry: 'AT'
|
||||
},
|
||||
sameAs: [
|
||||
'https://www.linkedin.com/in/aron-august-hohmann/',
|
||||
'https://aronhohmann.eu'
|
||||
]
|
||||
};
|
||||
|
||||
import { siMastodon, siGithub, siXing } from 'simple-icons';
|
||||
|
||||
|
|
@ -14,8 +42,17 @@
|
|||
},
|
||||
{ icon: siXing, label: 'Xing', href: 'https://www.xing.com/profile/AronAugust_Hohmann', handle: 'AronAugust_Hohmann' },
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title={m.meta_contact_title()}
|
||||
description={m.meta_contact_description()}
|
||||
type="website"
|
||||
{canonicalUrl}
|
||||
jsonLd={personJsonLd}
|
||||
/>
|
||||
|
||||
<div class="max-w-3xl mx-auto px-4 py-16">
|
||||
<h1 class="text-3xl font-bold mb-10">{m.contact_heading()}</h1>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue