This commit is contained in:
2026-07-10 13:27:29 +02:00
parent 5c48def4e1
commit 53bac4e2b4
35 changed files with 466 additions and 320 deletions

View File

@@ -1,5 +1,6 @@
import type { Metadata } from 'next'
import Home from '../page'
import { siteConfig } from '@/lib/site'
export const metadata: Metadata = {
title: 'GreenLens - Plant Identifier & Care Planner',
@@ -18,11 +19,18 @@ export const metadata: Metadata = {
title: 'GreenLens - Plant Identifier & Care Planner',
description:
'Scan plants by photo, understand what they need, and keep care, reminders, and your collection in one app.',
url: `${siteConfig.domain}/en`,
type: 'website',
locale: 'en_US',
},
twitter: {
card: 'summary_large_image',
title: 'GreenLens - Plant Identifier & Care Planner',
description:
'Scan plants by photo, understand what they need, and keep care, reminders, and your collection in one app.',
},
}
export default function EnglishHomePage() {
return <Home />
return <Home locale="en" />
}

View File

@@ -23,8 +23,14 @@ export const metadata: Metadata = {
type: 'website',
locale: 'es_ES',
},
twitter: {
card: 'summary_large_image',
title: 'GreenLens - Identificar y cuidar plantas',
description:
'Identifica plantas por foto, organiza cuidados, recibe recordatorios y diagnostica síntomas comunes con GreenLens.',
},
}
export default function SpanishHomePage() {
return <Home />
return <Home locale="es" />
}

View File

@@ -3,7 +3,7 @@ import { cookies } from 'next/headers'
import { headers } from 'next/headers'
import './globals.css'
import { LangProvider } from '@/context/LangContext'
import { siteConfig, hasIosStoreUrl } from '@/lib/site'
import { siteConfig, hasIosStoreUrl, hasAndroidStoreUrl } from '@/lib/site'
export const metadata: Metadata = {
metadataBase: new URL(siteConfig.domain),
@@ -54,6 +54,20 @@ export default async function RootLayout({ children }: { children: React.ReactNo
const lang = (routeLang ?? cookieStore.get('lang')?.value ?? 'de') as 'de' | 'en' | 'es'
const validLangs = ['de', 'en', 'es']
const htmlLang = validLangs.includes(lang) ? lang : 'de'
const schemaDescription = {
de: 'Pflanzen per Foto einordnen, Pflegepläne nutzen und sichtbare Pflanzenprobleme prüfen.',
en: 'Assess plants from photos, use care plans, and review visible plant problems.',
es: 'Evalúa plantas con fotos, utiliza planes de cuidado y revisa problemas visibles.',
}[htmlLang]
const organizationDescription = {
de: 'GreenLens ist eine App zur Pflanzenerkennung und Pflegeplanung.',
en: 'GreenLens is an app for plant identification and care planning.',
es: 'GreenLens es una app para identificar plantas y planificar sus cuidados.',
}[htmlLang]
const operatingSystems = [
hasIosStoreUrl ? 'iOS' : null,
hasAndroidStoreUrl ? 'Android' : null,
].filter((value): value is string => Boolean(value))
return (
<html lang={htmlLang}>
@@ -76,11 +90,10 @@ export default async function RootLayout({ children }: { children: React.ReactNo
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: siteConfig.name,
operatingSystem: 'iOS, Android',
...(operatingSystems.length > 0 && { operatingSystem: operatingSystems.join(', ') }),
applicationCategory: 'LifestyleApplication',
description:
'Pflanzen per Foto erkennen, Pflegepläne nutzen und Pflanzenprobleme einordnen.',
inLanguage: ['en', 'de', 'es'],
description: schemaDescription,
inLanguage: htmlLang,
...(hasIosStoreUrl && { downloadUrl: siteConfig.iosAppStoreUrl }),
offers: {
'@type': 'Offer',
@@ -93,8 +106,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
'@type': 'Organization',
name: siteConfig.name,
url: siteConfig.domain,
description:
'GreenLens ist eine App zur Pflanzenerkennung und Pflegeplanung für iOS und Android.',
description: organizationDescription,
contactPoint: {
'@type': 'ContactPoint',
contactType: 'customer support',
@@ -109,7 +121,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
/>
</head>
<body>
<LangProvider>{children}</LangProvider>
<LangProvider initialLang={htmlLang}>{children}</LangProvider>
</body>
</html>
)

View File

@@ -10,6 +10,8 @@ import HowItWorks from '@/components/HowItWorks'
import FAQ from '@/components/FAQ'
import CTA from '@/components/CTA'
import Footer from '@/components/Footer'
import type { Lang } from '@/lib/i18n'
import { getHomeSchemas } from '@/lib/homeSchema'
export const metadata: Metadata = {
title: 'GreenLens - Pflanzen erkennen & Pflege planen',
@@ -26,86 +28,8 @@ export const metadata: Metadata = {
},
}
const howToSchema = {
'@context': 'https://schema.org',
'@type': 'HowTo',
name: 'So erkennst du eine Pflanze mit GreenLens',
step: [
{
'@type': 'HowToStep',
position: 1,
name: 'Pflanze fotografieren',
text: 'Öffne die App, richte die Kamera auf deine Pflanze und tippe auf Scan.',
},
{
'@type': 'HowToStep',
position: 2,
name: 'KI identifiziert sofort',
text: 'In unter einer Sekunde erhältst du den Namen, die Art und die wichtigsten Eckdaten.',
},
{
'@type': 'HowToStep',
position: 3,
name: 'Pflegeplan erhalten',
text: 'GreenLens erstellt automatisch einen Pflegeplan passend zu deiner Pflanze und deinem Standort.',
},
{
'@type': 'HowToStep',
position: 4,
name: 'Wachstum verfolgen',
text: 'Dokumentiere Fotos, verfolge das Gießen und lass dich an wichtige Pflegetermine erinnern.',
},
],
}
const faqSchema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'Wie erkennt GreenLens eine Pflanze?',
acceptedAnswer: {
'@type': 'Answer',
text: 'GreenLens analysiert das Pflanzenfoto und verbindet das Ergebnis mit Pflegehinweisen in der App, damit du schneller zu klaren nächsten Schritten kommst.',
},
},
{
'@type': 'Question',
name: 'Ist GreenLens kostenlos?',
acceptedAnswer: {
'@type': 'Answer',
text: 'GreenLens bietet kostenlose Funktionen und zusätzlich kostenpflichtige Optionen wie Abos und Credit-Top-ups für erweiterte KI-Funktionen.',
},
},
{
'@type': 'Question',
name: 'Kann ich GreenLens offline nutzen?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Pflanzenidentifikation und Gesundheitscheck benötigen eine Internetverbindung. Deine gespeicherte Sammlung, Pflegenotizen und Gieß-Erinnerungen sind offline verfügbar.',
},
},
{
'@type': 'Question',
name: 'Für welche Pflanzen kann ich GreenLens nutzen?',
acceptedAnswer: {
'@type': 'Answer',
text: 'GreenLens umfasst über 450 Pflanzenarten, darunter Zimmerpflanzen, Gartenpflanzen und Sukkulenten. Die App richtet sich an Pflanzenbesitzer, die Identifikation und Pflege an einem Ort wollen.',
},
},
{
'@type': 'Question',
name: 'Wie starte ich meine Pflanzensammlung?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Starte mit einem Scan, prüfe das Ergebnis und speichere die Pflanze in deiner Sammlung, damit Notizen, Erinnerungen und Pflege an einem Ort bleiben.',
},
},
],
}
export default function Home() {
export default function Home({ locale = 'de' }: { locale?: Lang }) {
const { howTo: howToSchema, faq: faqSchema } = getHomeSchemas(locale)
return (
<>
<script

View File

@@ -8,7 +8,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: baseUrl,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'weekly',
priority: 1,
},
@@ -26,19 +26,19 @@ export default function sitemap(): MetadataRoute.Sitemap {
},
{
url: `${baseUrl}/plant-identifier-app`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/plant-disease-identifier`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.75,
},
{
url: `${baseUrl}/plant-care-app`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.75,
},
@@ -68,43 +68,43 @@ export default function sitemap(): MetadataRoute.Sitemap {
},
{
url: `${baseUrl}/flower-scanner`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/identify-plant-photo`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/plant-scanner`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/houseplant-identifier`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.75,
},
{
url: `${baseUrl}/succulent-identifier`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.75,
},
{
url: `${baseUrl}/best-plant-identification-app`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.85,
},
{
url: `${baseUrl}/plant-health-app`,
lastModified: new Date('2026-04-27'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.8,
},
@@ -130,20 +130,20 @@ export default function sitemap(): MetadataRoute.Sitemap {
const profile = getGermanSeoPageBySlug(slug)
return {
url: `${baseUrl}${profile?.canonical ?? `/${slug}`}`,
lastModified: new Date('2026-05-20'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly' as const,
priority: slug === 'pflanzen-erkennen-kostenlos' || slug === 'pflanzen-erkennen-app' ? 0.85 : 0.75,
}
}),
{
url: `${baseUrl}/es`,
lastModified: new Date('2026-05-20'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly',
priority: 0.85,
},
...spanishSeoPageSlugs.map((slug) => ({
url: `${baseUrl}${spanishSeoPageProfiles[slug].canonical}`,
lastModified: new Date('2026-05-20'),
lastModified: new Date('2026-07-10'),
changeFrequency: 'monthly' as const,
priority: slug === 'identificador-de-plantas' ? 0.85 : 0.75,
})),