Files
Greenlens/greenlns-landing/app/layout.tsx
2026-07-27 16:42:04 +02:00

135 lines
4.8 KiB
TypeScript

import type { Metadata } from 'next'
import { cookies } from 'next/headers'
import { headers } from 'next/headers'
import Script from 'next/script'
import './globals.css'
import { LangProvider } from '@/context/LangContext'
import { siteConfig, hasIosStoreUrl, hasAndroidStoreUrl } from '@/lib/site'
export const metadata: Metadata = {
metadataBase: new URL(siteConfig.domain),
title: {
default: 'GreenLens - Pflanzen erkennen & Pflege planen',
template: '%s',
},
description:
'GreenLens erkennt Pflanzen per Foto in Sekunden und gibt dir Pflegepläne, Erinnerungen und Gesundheitschecks in einer App.',
keywords: [
'Pflanzen erkennen App',
'Pflanzen bestimmen per Foto',
'Blumen Scanner',
'Pflanzen Pflege App',
'plant identifier app',
'plant care app',
'plant scanner',
'plant disease identifier',
'identificador de plantas',
'GreenLens',
],
authors: [{ name: siteConfig.name }],
openGraph: {
title: 'GreenLens - Pflanzen erkennen & Pflege planen',
description: 'Pflanzen per Foto erkennen, Pflegeplan erhalten und Pflanzenprobleme in einer App einordnen.',
type: 'website',
url: siteConfig.domain,
},
alternates: {
languages: {
de: '/',
en: '/en',
es: '/es',
'x-default': '/',
},
},
twitter: {
card: 'summary_large_image',
title: 'GreenLens - Pflanzen erkennen & Pflege planen',
description: 'Pflanzen per Foto erkennen, Pflegeplan erhalten und Pflanzenprobleme in einer App einordnen.',
},
}
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies()
const headerStore = await headers()
const routeLang = headerStore.get('x-greenlens-lang')
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}>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify([
{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: siteConfig.name,
url: siteConfig.domain,
inLanguage: ['en', 'de', 'es'],
},
{
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: siteConfig.name,
...(operatingSystems.length > 0 && { operatingSystem: operatingSystems.join(', ') }),
applicationCategory: 'LifestyleApplication',
description: schemaDescription,
inLanguage: htmlLang,
...(hasIosStoreUrl && { downloadUrl: siteConfig.iosAppStoreUrl }),
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'EUR',
},
},
{
'@context': 'https://schema.org',
'@type': 'Organization',
name: siteConfig.name,
url: siteConfig.domain,
description: organizationDescription,
contactPoint: {
'@type': 'ContactPoint',
contactType: 'customer support',
email: siteConfig.supportEmail,
},
...(hasIosStoreUrl && {
sameAs: [siteConfig.iosAppStoreUrl],
}),
},
]),
}}
/>
<Script
src="https://cloud.umami.is/script.js"
data-website-id="816dc2f4-0400-4f70-9506-2d86ee5d43ff"
strategy="afterInteractive"
/>
</head>
<body>
<LangProvider initialLang={htmlLang}>{children}</LangProvider>
</body>
</html>
)
}