SEO V2
This commit is contained in:
110
src/app/de/[slug]/page.tsx
Normal file
110
src/app/de/[slug]/page.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
import { UseCasePageTemplate } from '@/components/marketing/UseCasePageTemplate';
|
||||
import {
|
||||
getUseCasePageDe,
|
||||
useCasePagesDe,
|
||||
useCaseTemplateLabelsDe,
|
||||
} from '@/lib/growth-pages-de';
|
||||
import { buildLanguageAlternates, absoluteUrl } from '@/lib/i18n';
|
||||
|
||||
export const dynamicParams = false;
|
||||
|
||||
export function generateStaticParams() {
|
||||
return Object.keys(useCasePagesDe).map((slug) => ({ slug }));
|
||||
}
|
||||
|
||||
export function generateMetadata({ params }: { params: { slug: string } }): Metadata {
|
||||
const page = getUseCasePageDe(params.slug);
|
||||
|
||||
if (!page) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const alternates = buildLanguageAlternates({
|
||||
en: `/use-cases/${page.enSlug}`,
|
||||
de: page.href,
|
||||
});
|
||||
|
||||
return {
|
||||
title: {
|
||||
absolute: `${page.title} | QR Master`,
|
||||
},
|
||||
description: page.metaDescription,
|
||||
alternates: {
|
||||
// The German page canonicalizes to itself; hreflang links the pair.
|
||||
canonical: absoluteUrl(page.href),
|
||||
languages: alternates.languages,
|
||||
},
|
||||
openGraph: {
|
||||
title: `${page.title} | QR Master`,
|
||||
description: page.metaDescription,
|
||||
url: absoluteUrl(page.href),
|
||||
type: 'website',
|
||||
locale: 'de_DE',
|
||||
images: ['/og-image.png'],
|
||||
},
|
||||
twitter: {
|
||||
title: `${page.title} | QR Master`,
|
||||
description: page.metaDescription,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function UseCaseDetailPageDe({
|
||||
params,
|
||||
}: {
|
||||
params: { slug: string };
|
||||
}) {
|
||||
const page = getUseCasePageDe(params.slug);
|
||||
|
||||
if (!page) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<UseCasePageTemplate
|
||||
title={page.title}
|
||||
description={page.metaDescription}
|
||||
eyebrow={page.eyebrow}
|
||||
intro={page.intro}
|
||||
pageType="use_case"
|
||||
cluster={page.cluster}
|
||||
useCase={page.slug}
|
||||
breadcrumbs={[
|
||||
{ name: 'Startseite', url: '/qr-code-erstellen' },
|
||||
{ name: page.title, url: page.href },
|
||||
]}
|
||||
answer={page.answer}
|
||||
whenToUse={page.whenToUse}
|
||||
comparisonItems={page.comparisonItems}
|
||||
howToSteps={page.howToSteps}
|
||||
primaryCta={{
|
||||
// German visitors should convert on the German generator page, not
|
||||
// the English commercial parent.
|
||||
href: '/qr-code-erstellen',
|
||||
label: page.ctaLabel,
|
||||
}}
|
||||
secondaryCta={{
|
||||
href: page.parentHref,
|
||||
label: `Mehr erfahren: ${page.parentTitle}`,
|
||||
}}
|
||||
workflowTitle={page.workflowTitle}
|
||||
workflowIntro={page.workflowIntro}
|
||||
workflowCards={page.workflowCards}
|
||||
checklistTitle={page.checklistTitle}
|
||||
checklist={page.checklist}
|
||||
supportLinks={page.supportLinks}
|
||||
faq={page.faq}
|
||||
heroImage={page.heroImage}
|
||||
heroImageAlt={page.heroImageAlt}
|
||||
authoritySignals={page.authoritySignals}
|
||||
directAnswer={page.directAnswer}
|
||||
labels={{
|
||||
...useCaseTemplateLabelsDe,
|
||||
faqTitle: `FAQ: ${page.title}`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
79
src/app/de/layout.tsx
Normal file
79
src/app/de/layout.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { Suspense } from 'react';
|
||||
import '@/styles/globals.css';
|
||||
import { Providers } from '@/components/Providers';
|
||||
import MarketingDeLayout from '@/components/marketing/MarketingDeLayout';
|
||||
import { organizationSchema, websiteSchema } from '@/lib/schema';
|
||||
import FacebookPixel from '@/components/analytics/FacebookPixel';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'QR Master – QR Code Generator & Analytics',
|
||||
template: '%s | QR Master',
|
||||
},
|
||||
description: 'Erstellen Sie dynamische QR Codes für Feedback, PDF, Coupons und App Stores. Verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.',
|
||||
metadataBase: new URL('https://www.qrmaster.net'),
|
||||
icons: {
|
||||
icon: [
|
||||
{ url: '/favicon.svg', type: 'image/svg+xml' },
|
||||
{ url: '/favicon.ico', sizes: '16x16 32x32', type: 'image/x-icon' },
|
||||
],
|
||||
shortcut: '/favicon.ico',
|
||||
apple: '/logo.svg',
|
||||
},
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
siteName: 'QR Master',
|
||||
title: 'QR Master – QR Code Generator & Analytics',
|
||||
description: 'Erstellen Sie dynamische QR Codes für Feedback, PDF, Coupons und App Stores. Verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.',
|
||||
locale: 'de_DE',
|
||||
images: [
|
||||
{
|
||||
url: 'https://www.qrmaster.net/og-image.png',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'QR Master - Dynamischer QR Code Generator',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
site: '@qrmaster',
|
||||
images: ['https://www.qrmaster.net/og-image.png'],
|
||||
},
|
||||
robots: { index: true, follow: true },
|
||||
verification: {
|
||||
other: {
|
||||
'facebook-domain-verification': process.env.NEXT_PUBLIC_FACEBOOK_DOMAIN_VERIFICATION || '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function GermanRootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body className="font-sans">
|
||||
<Suspense fallback={null}>
|
||||
<Providers>
|
||||
<FacebookPixel />
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }}
|
||||
/>
|
||||
<MarketingDeLayout>
|
||||
{children}
|
||||
</MarketingDeLayout>
|
||||
</Providers>
|
||||
</Suspense>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
85
src/app/de/preise/page.tsx
Normal file
85
src/app/de/preise/page.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import SeoJsonLd from '@/components/SeoJsonLd';
|
||||
import { Pricing } from '@/components/marketing/Pricing';
|
||||
import { FAQSection } from '@/components/aeo/FAQSection';
|
||||
import { breadcrumbSchema, faqPageSchema } from '@/lib/schema';
|
||||
import { getDictionary, buildLanguageAlternates, absoluteUrl } from '@/lib/i18n';
|
||||
|
||||
const alternates = buildLanguageAlternates({ en: '/pricing', de: '/de/preise' });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
absolute: 'Preise & Pläne | QR Master',
|
||||
},
|
||||
description:
|
||||
'QR Master Preise im Vergleich: Free mit 3 aktiven dynamischen QR-Codes, Pro mit 50 und Business mit 500 inklusive Massen-Erstellung.',
|
||||
alternates: {
|
||||
canonical: absoluteUrl('/de/preise'),
|
||||
languages: alternates.languages,
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
openGraph: {
|
||||
title: 'Preise & Pläne | QR Master',
|
||||
description:
|
||||
'QR Master Preise im Vergleich: Free mit 3 aktiven dynamischen QR-Codes, Pro mit 50 und Business mit 500 inklusive Massen-Erstellung.',
|
||||
url: absoluteUrl('/de/preise'),
|
||||
type: 'website',
|
||||
locale: 'de_DE',
|
||||
images: [
|
||||
{
|
||||
url: 'https://www.qrmaster.net/og-image.png',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'QR Master Preise & Pläne',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const faqItems = [
|
||||
{
|
||||
question: 'Wie viele dynamische QR-Codes sind in den einzelnen Plänen enthalten?',
|
||||
answer:
|
||||
'Free enthält 3 aktive dynamische QR-Codes. Pro enthält 50 dynamische QR-Codes. Business enthält 500 dynamische QR-Codes.',
|
||||
},
|
||||
{
|
||||
question: 'Enthalten alle Pläne statische QR-Codes?',
|
||||
answer: 'Ja. Alle Pläne enthalten unbegrenzt viele statische QR-Codes.',
|
||||
},
|
||||
{
|
||||
question: 'Welcher Plan enthält die Massen-Erstellung (Bulk)?',
|
||||
answer: 'Die Massen-Erstellung von QR-Codes ist im Business-Plan enthalten.',
|
||||
},
|
||||
{
|
||||
question: 'Welche Pläne enthalten Statistiken und Branding?',
|
||||
answer:
|
||||
'Der Free-Plan enthält grundlegendes Scan-Tracking. Pro ergänzt erweiterte Statistiken und individuelles Branding. Business enthält alles aus Pro plus Massen-Erstellung und Prioritäts-Support per E-Mail.',
|
||||
},
|
||||
];
|
||||
|
||||
const breadcrumbItems = [
|
||||
{ name: 'Startseite', url: '/qr-code-erstellen' },
|
||||
{ name: 'Preise', url: '/de/preise' },
|
||||
];
|
||||
|
||||
export default function PreisePage() {
|
||||
const t = getDictionary('de');
|
||||
|
||||
return (
|
||||
<>
|
||||
<SeoJsonLd data={[breadcrumbSchema(breadcrumbItems), faqPageSchema(faqItems)]} />
|
||||
|
||||
<div className="bg-white">
|
||||
<Pricing t={t} />
|
||||
|
||||
<div className="container mx-auto max-w-5xl px-4 py-10 sm:px-6 lg:px-8">
|
||||
<FAQSection items={faqItems} title="Fragen zu den Preisen" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user