verbesserte Copy

This commit is contained in:
2026-07-26 23:16:29 +02:00
parent c7c1864fc6
commit 50c0f33dd7
26 changed files with 1476 additions and 142 deletions

View File

@@ -37,11 +37,11 @@ export default function CTA() {
<span className="store-btn-big">{hasIosStoreUrl ? 'App Store' : t.cta.supportLabel}</span>
</div>
</a>
<a
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : '/support'}
className="store-btn"
id="cta-googleplay"
aria-label="Google Play or contact"
<a
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : '/support'}
className="store-btn"
id="cta-googleplay"
aria-label={hasAndroidStoreUrl ? 'Google Play' : 'Contact support'}
>
<svg className="store-btn-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M3.18 23.76c.33.18.7.24 1.08.17L14.84 12 11.17 8.33 3.18 23.76zm17.16-12.38-3.32-1.89-3.78 3.78 3.78 3.78 3.34-1.9c.95-.54.95-1.92-.02-2.77zM2.01 1.11C1.7 1.44 1.5 1.97 1.5 2.67v18.66c0 .7.2 1.23.51 1.56l.08.08L12.16 12v-.22L2.09 1.03l-.08.08zm9.16 10.67 2.67 2.67-10.5 5.97 7.83-8.64z" />

View File

@@ -9,66 +9,66 @@ const faqs = [
question: {
en: 'How does GreenLens identify a plant?',
de: 'Wie erkennt GreenLens eine Pflanze?',
es: '¿Cómo identifica GreenLens una planta?'
es: '¿Cómo identifica GreenLens una planta?'
},
answer: {
en: 'GreenLens analyzes the plant photo and combines that with app-side care guidance so you can move from scan to next steps faster.',
de: 'GreenLens analysiert das Pflanzenfoto und verbindet das Ergebnis mit Pflegehinweisen in der App, damit du schneller zu klaren nächsten Schritten kommst.',
es: 'GreenLens analiza la foto de la planta y combina el resultado con indicaciones de cuidado dentro de la app para que avances más rápido.'
en: 'GreenLens analyzes the plant photo and combines that with app-side care guidance so you can move from scan to next steps faster.',
de: 'GreenLens analysiert das Pflanzenfoto und verbindet das Ergebnis mit Pflegehinweisen in der App, damit du schneller zu klaren nächsten Schritten kommst.',
es: 'GreenLens analiza la foto de la planta y combina el resultado con indicaciones de cuidado dentro de la app para que avances más rápido.'
}
},
{
question: {
en: 'Is GreenLens free to use?',
de: 'Ist GreenLens kostenlos?',
es: '¿Es GreenLens gratuito?'
es: '¿Es GreenLens gratuito?'
},
answer: {
en: 'GreenLens includes free functionality plus paid options such as subscriptions and credit top-ups for advanced AI features.',
de: 'GreenLens bietet kostenlose Funktionen und zusätzlich kostenpflichtige Optionen wie Abos und Credit-Top-ups für erweiterte KI-Funktionen.',
es: 'GreenLens incluye funciones gratuitas y también opciones de pago como suscripciones y créditos para funciones de IA más amplias.'
en: 'One scan is free without an account, and you get three more after signing up. Beyond that GreenLens Pro is required — monthly or yearly, with current pricing shown in the App Store and cancellation any time in your iPhone settings. Optional credit packs exist for heavy use; you do not need them to start.',
de: 'Ein Scan ist gratis, ganz ohne Anmeldung. Nach der Anmeldung bekommst du drei weitere. Danach brauchst du GreenLens Pro — Monats- oder Jahresabo mit den aktuellen Preisen im App Store, jederzeit über die iPhone-Einstellungen kündbar. Optionale Credit-Pakete gibt es für intensive Nutzung; zum Start brauchst du sie nicht.',
es: 'Un escaneo es gratis, sin crear cuenta. Tras registrarte recibes tres más. A partir de ahí necesitas GreenLens Pro: suscripción mensual o anual, con los precios actuales en la App Store y cancelación en cualquier momento desde los ajustes del iPhone. Hay paquetes de créditos opcionales para uso intensivo; no los necesitas para empezar.'
}
},
{
question: {
en: 'Can I use GreenLens offline?',
de: 'Kann ich GreenLens offline nutzen?',
es: '¿Puedo usar GreenLens sin conexión?'
es: '¿Puedo usar GreenLens sin conexión?'
},
answer: {
en: 'Plant identification and health checks require an internet connection. Your saved collection, care notes, and watering reminders are available offline.',
de: 'Pflanzenidentifikation und Gesundheitscheck benötigen eine Internetverbindung. Deine gespeicherte Sammlung, Pflegenotizen und Gieß-Erinnerungen sind offline verfügbar.',
es: 'La identificación de plantas y el control de salud requieren conexión a internet. Tu colección guardada, notas de cuidado y recordatorios de riego están disponibles sin conexión.'
de: 'Pflanzenidentifikation und Gesundheitscheck benötigen eine Internetverbindung. Deine gespeicherte Sammlung, Pflegenotizen und Gieß-Erinnerungen sind offline verfügbar.',
es: 'La identificación de plantas y el control de salud requieren conexión a internet. Tu colección guardada, notas de cuidado y recordatorios de riego están disponibles sin conexión.'
}
},
{
question: {
en: 'What kind of plants can I use GreenLens for?',
de: 'Für welche Pflanzen kann ich GreenLens nutzen?',
es: '¿Para qué tipo de plantas puedo usar GreenLens?'
de: 'Für welche Pflanzen kann ich GreenLens nutzen?',
es: '¿Para qué tipo de plantas puedo usar GreenLens?'
},
answer: {
en: 'GreenLens covers 450+ plant species including houseplants, garden plants, and succulents. It is built for everyday plant owners who want identification and care guidance in one place.',
de: 'GreenLens umfasst über 450 Pflanzenarten, darunter Zimmerpflanzen, Gartenpflanzen und Sukkulenten. Die App richtet sich an Pflanzenbesitzer, die Identifikation und Pflege an einem Ort wollen.',
es: 'GreenLens cubre más de 450 especies de plantas, incluyendo plantas de interior, de jardín y suculentas. Está pensada para quienes quieren identificación y cuidado en un solo lugar.'
de: 'GreenLens umfasst über 450 Pflanzenarten, darunter Zimmerpflanzen, Gartenpflanzen und Sukkulenten. Die App richtet sich an Pflanzenbesitzer, die Identifikation und Pflege an einem Ort wollen.',
es: 'GreenLens cubre más de 450 especies de plantas, incluyendo plantas de interior, de jardín y suculentas. Está pensada para quienes quieren identificación y cuidado en un solo lugar.'
}
},
{
question: {
en: 'How do I start my plant collection?',
de: 'Wie starte ich meine Pflanzensammlung?',
es: '¿Cómo empiezo mi colección de plantas?'
es: '¿Cómo empiezo mi colección de plantas?'
},
answer: {
en: 'Start with a scan, review the result, and save the plant to your collection to keep notes, reminders, and follow-up care in one place.',
de: 'Starte mit einem Scan, prüfe das Ergebnis und speichere die Pflanze in deiner Sammlung, damit Notizen, Erinnerungen und Pflege an einem Ort bleiben.',
es: 'Empieza con un escaneo, revisa el resultado y guarda la planta en tu colección para mantener notas, recordatorios y cuidado en un solo lugar.'
de: 'Starte mit einem Scan, prüfe das Ergebnis und speichere die Pflanze in deiner Sammlung, damit Notizen, Erinnerungen und Pflege an einem Ort bleiben.',
es: 'Empieza con un escaneo, revisa el resultado y guarda la planta en tu colección para mantener notas, recordatorios y cuidado en un solo lugar.'
}
}
];
const TEXT = {
de: { tag: 'Fragen', h2: ['Häufig gestellte', 'Fragen'], desc: 'Alles, was du über GreenLens und den Einstieg wissen musst.' },
de: { tag: 'Fragen', h2: ['Häufig gestellte', 'Fragen'], desc: 'Alles, was du über GreenLens und den Einstieg wissen musst.' },
en: { tag: 'Questions', h2: ['Frequently Asked', 'Questions'], desc: 'Everything you need to know about GreenLens and getting started.' },
es: { tag: 'Preguntas', h2: ['Preguntas', 'Frecuentes'], desc: 'Todo lo que necesitas saber sobre GreenLens y el inicio.' },
}

View File

@@ -7,6 +7,7 @@ import {
SeoComparison,
SeoContentSections,
SeoBestFit,
SeoOfferBlock,
SeoFaq,
SeoRelated,
} from '@/components/seo/SeoSections'
@@ -43,18 +44,67 @@ export default function SeoCategoryPage({ profile }: SeoCategoryPageProps) {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: siteConfig.name,
operatingSystem: 'iOS, Android',
operatingSystem: 'iOS',
applicationCategory: 'LifestyleApplication',
description: profile.directAnswer,
...(hasIosStoreUrl && { downloadUrl: siteConfig.iosAppStoreUrl }),
// Kein pauschaler Nullpreis: Der Download ist kostenlos, die Nutzung nach
// vier Scans nicht. Apple liefert die regionalen Preise selbst aus.
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'EUR',
category: 'Free trial, then subscription',
priceSpecification: {
'@type': 'PriceSpecification',
description:
'One scan free without an account, three more after signing up, then a monthly or yearly GreenLens Pro subscription. Current regional pricing is shown in the App Store.',
},
},
}
: null
// HowTo: bevorzugtes Format vieler KI-Antwortsysteme fuer "Wie erkenne ich ..."-Fragen
const howToSchema =
profile.howToSteps && profile.howToSteps.length > 0
? {
'@context': 'https://schema.org',
'@type': 'HowTo',
name: profile.howToName ?? profile.h1,
description: profile.directAnswer,
inLanguage: locale,
totalTime: 'PT5M',
step: profile.howToSteps.map((item, index) => ({
'@type': 'HowToStep',
position: index + 1,
name: item.name,
text: item.text,
})),
}
: null
// WebPage mit Aenderungsdatum, Autor und Sprache — Frische- und E-E-A-T-Signale
const webPageSchema = {
'@context': 'https://schema.org',
'@type': 'WebPage',
name: profile.h1,
description: profile.metaDescription,
url: `${siteConfig.domain}${profile.canonical}`,
inLanguage: locale,
dateModified: profile.lastUpdatedIso ?? undefined,
primaryImageOfPage: profile.heroImage
? { '@type': 'ImageObject', url: `${siteConfig.domain}${profile.heroImage}`, caption: profile.heroImageAlt }
: undefined,
isPartOf: {
'@type': 'WebSite',
name: siteConfig.name,
url: siteConfig.domain,
},
publisher: {
'@type': 'Organization',
name: siteConfig.name,
url: siteConfig.domain,
},
}
const breadcrumbSchema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
@@ -90,6 +140,16 @@ export default function SeoCategoryPage({ profile }: SeoCategoryPageProps) {
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
/>
{howToSchema && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(howToSchema) }}
/>
)}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(webPageSchema) }}
/>
<SeoNavbar locale={locale} methodologyHref={usesPhotoAnalysis ? '#methodology' : '#comparison'} />
@@ -111,6 +171,7 @@ export default function SeoCategoryPage({ profile }: SeoCategoryPageProps) {
{usesPhotoAnalysis && <SeoMethodology copy={copy} intent={templateIntent} />}
<SeoComparison profile={profile} copy={copy} intent={templateIntent} />
<SeoBestFit profile={profile} copy={copy} />
<SeoOfferBlock profile={profile} />
<SeoFaq profile={profile} copy={copy} />
<SeoRelated profile={profile} copy={copy} />
</main>

View File

@@ -38,6 +38,20 @@ export function SeoHero({
{copy.hero.ctaSecondary}
</a>
</div>
<ul className="mt-6 flex flex-wrap gap-x-5 gap-y-2 seo-fade seo-fade-3">
{profile.timeCue && (
<li className="flex items-center gap-2 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-4 h-4 shrink-0 text-primary" />
<span>{profile.timeCue}</span>
</li>
)}
{copy.hero.trustChips.map((chip) => (
<li key={chip} className="flex items-center gap-2 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-4 h-4 shrink-0 text-primary" />
<span>{chip}</span>
</li>
))}
</ul>
<div className="mt-12 grid grid-cols-2 gap-6 pt-8 border-t border-surface-container-high seo-fade seo-fade-3">
<div>
<div className="font-display text-2xl font-semibold text-primary mb-1">{copy.hero.stat1Title}</div>
@@ -52,7 +66,11 @@ export function SeoHero({
<div className="lg:col-span-6 flex justify-center items-center seo-fade seo-fade-2">
<div className="relative w-full max-w-[320px] md:max-w-[380px] h-[550px] md:h-[650px] rounded-3xl overflow-hidden shadow-2xl border-[8px] border-surface-container">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img alt={copy.hero.imageAlt} className="w-full h-full object-cover" src="/hero.jpg" />
<img
alt={profile.heroImageAlt ?? copy.hero.imageAlt}
className="w-full h-full object-cover"
src={profile.heroImage ?? '/hero.jpg'}
/>
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 w-[90%] bg-surface/95 backdrop-blur p-4 rounded-xl border border-surface-container-highest flex items-center gap-3 shadow-xl">
<div className="w-1.5 h-8 bg-primary rounded-full"></div>
<div>
@@ -71,11 +89,9 @@ export function SeoAdvantage({
copy,
showInsights = false,
}: SectionProps & { showInsights?: boolean }) {
const reviewedAt = {
de: 'Juli 2026',
en: 'July 2026',
es: 'Julio 2026',
}[profile.locale ?? 'en']
// Echtes Prüfdatum aus dem Profil. Kein globales Datum — eine Seite, die seit
// April unverändert ist, darf kein aktuelleres Datum behaupten.
const reviewedAt = profile.lastUpdated
return (
<section className={`max-w-[1200px] mx-auto px-6 py-16 grid grid-cols-1 ${showInsights ? 'lg:grid-cols-3' : ''} gap-12 border-t border-surface-container-high`} id="advantage">
@@ -221,6 +237,29 @@ export function SeoContentSections({ profile }: { profile: SeoPageProfile }) {
)
}
export function SeoOfferBlock({ profile }: { profile: SeoPageProfile }) {
if (!profile.offer) return null
const { eyebrow, title, body, terms, ctaLabel } = profile.offer
return (
<section className="max-w-[1200px] mx-auto px-6 py-16">
<div className="bg-surface rounded-2xl p-8 md:p-12 border border-surface-container-high shadow-sm">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-4">{eyebrow}</span>
<h2 className="font-display font-semibold text-2xl md:text-3xl text-primary mb-4 max-w-[26ch]">{title}</h2>
<p className="font-body text-on-surface-variant leading-relaxed mb-6 max-w-[60ch]">{body}</p>
<a
className="inline-flex justify-center items-center px-8 py-3 bg-ink text-white rounded-full font-bold shadow-lg hover:bg-ink-soft transition-colors duration-300"
href="#download"
>
{ctaLabel}
</a>
<p className="font-body text-sm text-on-surface-variant mt-5 max-w-[60ch]">{terms}</p>
</div>
</section>
)
}
export function SeoBestFit({ profile, copy }: SectionProps) {
return (
<section className="max-w-[1200px] mx-auto px-6 py-16 grid grid-cols-1 md:grid-cols-2 gap-8">