This commit is contained in:
@@ -2,11 +2,15 @@ import Footer from '@/components/Footer';
|
|||||||
import Header from '@/components/Header';
|
import Header from '@/components/Header';
|
||||||
import JsonLd from '@/components/JsonLd';
|
import JsonLd from '@/components/JsonLd';
|
||||||
import AnalyticsProvider from '@/components/analytics/AnalyticsProvider';
|
import AnalyticsProvider from '@/components/analytics/AnalyticsProvider';
|
||||||
import { Viewport } from 'next';
|
import { Metadata, Viewport } from 'next';
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import { getMessages } from 'next-intl/server';
|
import { getMessages } from 'next-intl/server';
|
||||||
import '../../styles/globals.css';
|
import '../../styles/globals.css';
|
||||||
|
import { SITE_URL } from '@/lib/schema';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
metadataBase: new URL(SITE_URL),
|
||||||
|
};
|
||||||
|
|
||||||
export const viewport: Viewport = {
|
export const viewport: Viewport = {
|
||||||
width: 'device-width',
|
width: 'device-width',
|
||||||
|
|||||||
62
app/[locale]/products/[...slug]/opengraph-image.tsx
Normal file
62
app/[locale]/products/[...slug]/opengraph-image.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { ImageResponse } from 'next/og';
|
||||||
|
import { getProductBySlug } from '@/lib/mdx';
|
||||||
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
import { OGImageTemplate } from '@/components/OGImageTemplate';
|
||||||
|
|
||||||
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug: string[] } }) {
|
||||||
|
const t = await getTranslations('Products');
|
||||||
|
const productSlug = slug[slug.length - 1];
|
||||||
|
|
||||||
|
// Check if it's a category page
|
||||||
|
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
|
||||||
|
if (categories.includes(productSlug)) {
|
||||||
|
const categoryKey = productSlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
||||||
|
const categoryTitle = t.has(`categories.${categoryKey}.title`) ? t(`categories.${categoryKey}.title`) : productSlug;
|
||||||
|
const categoryDesc = t.has(`categories.${categoryKey}.description`) ? t(`categories.${categoryKey}.description`) : '';
|
||||||
|
|
||||||
|
return new ImageResponse(
|
||||||
|
(
|
||||||
|
<OGImageTemplate
|
||||||
|
title={categoryTitle}
|
||||||
|
description={categoryDesc}
|
||||||
|
label="Product Category"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
{
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = await getProductBySlug(productSlug, locale);
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
return new ImageResponse(
|
||||||
|
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const featuredImage = product.frontmatter.images?.[0]
|
||||||
|
? (product.frontmatter.images[0].startsWith('http')
|
||||||
|
? product.frontmatter.images[0]
|
||||||
|
: `https://klz-cables.com${product.frontmatter.images[0]}`)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return new ImageResponse(
|
||||||
|
(
|
||||||
|
<OGImageTemplate
|
||||||
|
title={product.frontmatter.title}
|
||||||
|
description={product.frontmatter.description}
|
||||||
|
label={product.frontmatter.categories?.[0] || 'Product'}
|
||||||
|
image={featuredImage}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
{
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,10 +5,31 @@ import { OGImageTemplate } from '@/components/OGImageTemplate';
|
|||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug: string[] } }) {
|
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug?: string[] } }) {
|
||||||
const productSlug = slug[slug.length - 1];
|
|
||||||
const t = await getTranslations('Products');
|
const t = await getTranslations('Products');
|
||||||
|
|
||||||
|
// If no slug, it's the main products page
|
||||||
|
if (!slug || slug.length === 0) {
|
||||||
|
const title = t('meta.title') || t('title');
|
||||||
|
const description = t('meta.description') || t('subtitle');
|
||||||
|
|
||||||
|
return new ImageResponse(
|
||||||
|
(
|
||||||
|
<OGImageTemplate
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
label="Products"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
{
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const productSlug = slug[slug.length - 1];
|
||||||
|
|
||||||
// Check if it's a category page
|
// Check if it's a category page
|
||||||
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
|
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
|
||||||
if (categories.includes(productSlug)) {
|
if (categories.includes(productSlug)) {
|
||||||
|
|||||||
@@ -159,26 +159,69 @@ function guessColumnKey(row: ExcelRow, patterns: RegExp[]): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function technicalFullLabel(args: { key: string; excelKey: string; locale: 'en' | 'de' }): string {
|
function technicalFullLabel(args: { key: string; excelKey: string; locale: 'en' | 'de' }): string {
|
||||||
if (args.locale === 'en') return normalizeValue(args.excelKey);
|
|
||||||
const raw = normalizeValue(args.excelKey);
|
const raw = normalizeValue(args.excelKey);
|
||||||
if (!raw) return '';
|
if (!raw) return '';
|
||||||
|
|
||||||
|
if (args.locale === 'de') {
|
||||||
|
return raw
|
||||||
|
.replace(/\(approx\.?\)/gi, '(ca.)')
|
||||||
|
.replace(/\bconductor material\b/gi, 'Leitermaterial')
|
||||||
|
.replace(/\bconductor class\b/gi, 'Leiterklasse')
|
||||||
|
.replace(/\bcore insulation\b/gi, 'Aderisolation')
|
||||||
|
.replace(/\binsulation\b/gi, 'Aderisolation')
|
||||||
|
.replace(/\bfield control\b/gi, 'Feldsteuerung')
|
||||||
|
.replace(/\bscreen\b/gi, 'Schirm')
|
||||||
|
.replace(/\blongitudinal water tightness\b/gi, 'Längswasserdichtigkeit')
|
||||||
|
.replace(/\btransverse water tightness\b/gi, 'Querwasserdichtigkeit')
|
||||||
|
.replace(/\bsheath material\b/gi, 'Mantelmaterial')
|
||||||
|
.replace(/\bsheath color\b/gi, 'Mantelfarbe')
|
||||||
|
.replace(/\bflame retardancy\b/gi, 'Flammwidrigkeit')
|
||||||
|
.replace(/\buv resistant\b/gi, 'UV-bestandig')
|
||||||
|
.replace(/\bmax\.? permissible conductor temperature\b/gi, 'Max. zulässige Leitertemperatur')
|
||||||
|
.replace(/\bpermissible cable outer temperature, fixed\b/gi, 'Zul. Kabelaußentemperatur, fest verlegt')
|
||||||
|
.replace(/\bpermissible cable outer temperature, in motion\b/gi, 'Zul. Kabelaußentemperatur, in Bewegung')
|
||||||
|
.replace(/\bmaximum short-circuit temperature\b/gi, 'Maximale Kurzschlußtemperatur')
|
||||||
|
.replace(/\bmin\.? bending radius, fixed\b/gi, 'Min. Biegeradius, fest verlegt')
|
||||||
|
.replace(/\bminimum laying temperature\b/gi, 'Mindesttemperatur Verlegung')
|
||||||
|
.replace(/\bmeter marking\b/gi, 'Metermarkierung')
|
||||||
|
.replace(/\bpartial discharge\b/gi, 'Teilentladung')
|
||||||
|
.replace(/\bcapacitance\b/gi, 'Kapazität')
|
||||||
|
.replace(/\binductance\b/gi, 'Induktivität')
|
||||||
|
.replace(/\breactance\b/gi, 'Reaktanz')
|
||||||
|
.replace(/\btest voltage\b/gi, 'Prüfspannung')
|
||||||
|
.replace(/\brated voltage\b/gi, 'Nennspannung')
|
||||||
|
.replace(/\boperating temperature range\b/gi, 'Temperaturbereich')
|
||||||
|
.replace(/\bminimum sheath thickness\b/gi, 'Manteldicke (min.)')
|
||||||
|
.replace(/\bsheath thickness\b/gi, 'Manteldicke')
|
||||||
|
.replace(/\bnominal insulation thickness\b/gi, 'Isolationsdicke (nom.)')
|
||||||
|
.replace(/\binsulation thickness\b/gi, 'Isolationsdicke')
|
||||||
|
.replace(/\bdc resistance at 20\s*°?c\b/gi, 'DC-Leiterwiderstand (20 °C)')
|
||||||
|
.replace(/\bouter diameter(?: of cable)?\b/gi, 'Außen-Ø')
|
||||||
|
.replace(/\bbending radius\b/gi, 'Biegeradius')
|
||||||
|
.replace(/\bpackaging\b/gi, 'Verpackung')
|
||||||
|
.replace(/\bce\s*-?conformity\b/gi, 'CE-Konformität');
|
||||||
|
}
|
||||||
|
|
||||||
return raw
|
return raw
|
||||||
.replace(/\(approx\.?\)/gi, '(ca.)')
|
.replace(/\bconductor material\b/gi, 'Conductor material')
|
||||||
.replace(/\bcapacitance\b/gi, 'Kapazität')
|
.replace(/\bconductor class\b/gi, 'Conductor class')
|
||||||
.replace(/\binductance\b/gi, 'Induktivität')
|
.replace(/\bcore insulation\b/gi, 'Core insulation')
|
||||||
.replace(/\breactance\b/gi, 'Reaktanz')
|
.replace(/\bfield control\b/gi, 'Field control')
|
||||||
.replace(/\btest voltage\b/gi, 'Prüfspannung')
|
.replace(/\bscreen\b/gi, 'Screen')
|
||||||
.replace(/\brated voltage\b/gi, 'Nennspannung')
|
.replace(/\blongitudinal water tightness\b/gi, 'Longitudinal water tightness')
|
||||||
.replace(/\boperating temperature range\b/gi, 'Temperaturbereich')
|
.replace(/\btransverse water tightness\b/gi, 'Transverse water tightness')
|
||||||
.replace(/\bminimum sheath thickness\b/gi, 'Manteldicke (min.)')
|
.replace(/\bsheath material\b/gi, 'Sheath material')
|
||||||
.replace(/\bsheath thickness\b/gi, 'Manteldicke')
|
.replace(/\bsheath color\b/gi, 'Sheath color')
|
||||||
.replace(/\bnominal insulation thickness\b/gi, 'Isolationsdicke (nom.)')
|
.replace(/\bflame retardancy\b/gi, 'Flame retardancy')
|
||||||
.replace(/\binsulation thickness\b/gi, 'Isolationsdicke')
|
.replace(/\buv resistant\b/gi, 'UV resistant')
|
||||||
.replace(/\bdc resistance at 20\s*°?c\b/gi, 'DC-Leiterwiderstand (20 °C)')
|
.replace(/\bmax\.? permissible conductor temperature\b/gi, 'Max. permissible conductor temperature')
|
||||||
.replace(/\bouter diameter(?: of cable)?\b/gi, 'Außen-Ø')
|
.replace(/\bpermissible cable outer temperature, fixed\b/gi, 'Permissible cable outer temperature, fixed')
|
||||||
.replace(/\bbending radius\b/gi, 'Biegeradius')
|
.replace(/\bpermissible cable outer temperature, in motion\b/gi, 'Permissible cable outer temperature, in motion')
|
||||||
.replace(/\bpackaging\b/gi, 'Verpackung')
|
.replace(/\bmaximum short-circuit temperature\b/gi, 'Maximum short-circuit temperature')
|
||||||
.replace(/\bce\s*-?conformity\b/gi, 'CE-Konformität');
|
.replace(/\bmin\.? bending radius, fixed\b/gi, 'Min. bending radius, fixed')
|
||||||
|
.replace(/\bminimum laying temperature\b/gi, 'Minimum laying temperature')
|
||||||
|
.replace(/\bmeter marking\b/gi, 'Meter marking')
|
||||||
|
.replace(/\bpartial discharge\b/gi, 'Partial discharge');
|
||||||
}
|
}
|
||||||
|
|
||||||
function metaFullLabel(args: { key: string; excelKey: string; locale: 'en' | 'de' }): string {
|
function metaFullLabel(args: { key: string; excelKey: string; locale: 'en' | 'de' }): string {
|
||||||
@@ -507,6 +550,26 @@ function buildExcelModel(args: { product: ProductData; locale: 'en' | 'de' }): B
|
|||||||
'flame retardant': { header: 'Flame retardant', unit: '', key: 'flame' },
|
'flame retardant': { header: 'Flame retardant', unit: '', key: 'flame' },
|
||||||
'self-extinguishing of single cable': { header: 'Flame retardant', unit: '', key: 'flame' },
|
'self-extinguishing of single cable': { header: 'Flame retardant', unit: '', key: 'flame' },
|
||||||
|
|
||||||
|
'conductor material': { header: 'Conductor material', unit: '', key: 'cond_mat' },
|
||||||
|
'conductor class': { header: 'Conductor class', unit: '', key: 'cond_class' },
|
||||||
|
'core insulation': { header: 'Core insulation', unit: '', key: 'core_ins' },
|
||||||
|
'field control': { header: 'Field control', unit: '', key: 'field_ctrl' },
|
||||||
|
'screen': { header: 'Screen', unit: '', key: 'screen' },
|
||||||
|
'longitudinal water tightness': { header: 'Longitudinal water tightness', unit: '', key: 'long_water' },
|
||||||
|
'transverse water tightness': { header: 'Transverse water tightness', unit: '', key: 'trans_water' },
|
||||||
|
'sheath material': { header: 'Sheath material', unit: '', key: 'sheath_mat' },
|
||||||
|
'sheath color': { header: 'Sheath color', unit: '', key: 'sheath_color' },
|
||||||
|
'flame retardancy': { header: 'Flame retardancy', unit: '', key: 'flame_ret' },
|
||||||
|
'uv resistant': { header: 'UV resistant', unit: '', key: 'uv_res' },
|
||||||
|
'max. permissible conductor temperature': { header: 'Max. permissible conductor temperature', unit: '°C', key: 'max_cond_temp' },
|
||||||
|
'permissible cable outer temperature, fixed': { header: 'Permissible cable outer temperature, fixed', unit: '°C', key: 'out_temp_fixed' },
|
||||||
|
'permissible cable outer temperature, in motion': { header: 'Permissible cable outer temperature, in motion', unit: '°C', key: 'out_temp_motion' },
|
||||||
|
'maximum short-circuit temperature': { header: 'Maximum short-circuit temperature', unit: '°C', key: 'max_sc_temp_val' },
|
||||||
|
'min. bending radius, fixed': { header: 'Min. bending radius, fixed', unit: '', key: 'min_bend_fixed' },
|
||||||
|
'minimum laying temperature': { header: 'Minimum laying temperature', unit: '°C', key: 'min_lay_temp_val' },
|
||||||
|
'meter marking': { header: 'Meter marking', unit: '', key: 'meter_mark' },
|
||||||
|
'partial discharge': { header: 'Partial discharge', unit: 'pC', key: 'partial_dis' },
|
||||||
|
|
||||||
// High-value electrical/screen columns
|
// High-value electrical/screen columns
|
||||||
'capacitance (approx.)': { header: 'Capacitance', unit: 'uF/km', key: 'cap' },
|
'capacitance (approx.)': { header: 'Capacitance', unit: 'uF/km', key: 'cap' },
|
||||||
'capacitance': { header: 'Capacitance', unit: 'uF/km', key: 'cap' },
|
'capacitance': { header: 'Capacitance', unit: 'uF/km', key: 'cap' },
|
||||||
@@ -892,7 +955,20 @@ export function buildDatasheetModel(args: { product: ProductData; locale: 'en' |
|
|||||||
productUrl,
|
productUrl,
|
||||||
},
|
},
|
||||||
labels,
|
labels,
|
||||||
technicalItems: excelModel.ok ? excelModel.technicalItems : [],
|
technicalItems: [
|
||||||
|
...(excelModel.ok ? excelModel.technicalItems : []),
|
||||||
|
...(args.locale === 'de'
|
||||||
|
? [
|
||||||
|
{ label: 'Prüfspannung 6/10 kV', value: '21 kV' },
|
||||||
|
{ label: 'Prüfspannung 12/20 kV', value: '42 kV' },
|
||||||
|
{ label: 'Prüfspannung 18/30 kV', value: '63 kV' },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{ label: 'Test voltage 6/10 kV', value: '21 kV' },
|
||||||
|
{ label: 'Test voltage 12/20 kV', value: '42 kV' },
|
||||||
|
{ label: 'Test voltage 18/30 kV', value: '63 kV' },
|
||||||
|
]),
|
||||||
|
],
|
||||||
voltageTables,
|
voltageTables,
|
||||||
legendItems: crossSectionModel.legendItems || [],
|
legendItems: crossSectionModel.legendItems || [],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,18 +53,18 @@ export function generateFileName(product: ProductData, locale: 'en' | 'de'): str
|
|||||||
export function getLabels(locale: 'en' | 'de') {
|
export function getLabels(locale: 'en' | 'de') {
|
||||||
return {
|
return {
|
||||||
en: {
|
en: {
|
||||||
datasheet: 'PRODUCT DATASHEET',
|
datasheet: 'Technical Datasheet',
|
||||||
description: 'DESCRIPTION',
|
description: 'DESCRIPTION',
|
||||||
technicalData: 'TECHNICAL DATA',
|
technicalData: 'TECHNICAL DATA',
|
||||||
crossSection: 'CROSS-SECTION DATA',
|
crossSection: 'Cross-sections/Voltage',
|
||||||
sku: 'SKU',
|
sku: 'SKU',
|
||||||
noImage: 'No image available',
|
noImage: 'No image available',
|
||||||
},
|
},
|
||||||
de: {
|
de: {
|
||||||
datasheet: 'PRODUKTDATENBLATT',
|
datasheet: 'Technisches Datenblatt',
|
||||||
description: 'BESCHREIBUNG',
|
description: 'BESCHREIBUNG',
|
||||||
technicalData: 'TECHNISCHE DATEN',
|
technicalData: 'TECHNISCHE DATEN',
|
||||||
crossSection: 'QUERSCHNITTSDATEN',
|
crossSection: 'Querschnitte/Spannung',
|
||||||
sku: 'ARTIKELNUMMER',
|
sku: 'ARTIKELNUMMER',
|
||||||
noImage: 'Kein Bild verfügbar',
|
noImage: 'Kein Bild verfügbar',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user