SEO + Stripe
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -48,6 +48,12 @@ docker-compose.override.yml
|
||||
logs
|
||||
*.log
|
||||
|
||||
# project-specific
|
||||
Leads/
|
||||
marketing/
|
||||
output/
|
||||
remotion/
|
||||
|
||||
# local dev script
|
||||
dev-server.js
|
||||
.gstack/
|
||||
|
||||
17
Dockerfile
17
Dockerfile
@@ -51,17 +51,18 @@ ENV NEXT_TELEMETRY_DISABLED=1
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/docker/entrypoint.sh ./docker/entrypoint.sh
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/docker/entrypoint.sh ./docker/entrypoint.sh
|
||||
|
||||
RUN chmod +x ./docker/entrypoint.sh
|
||||
|
||||
# --- NEU: Ordner erstellen und Rechte an den nextjs User geben ---
|
||||
RUN mkdir -p /app/.next/cache && chown nextjs:nodejs /app/.next/cache
|
||||
# Next writes ISR/prerender artifacts under .next/server/app at runtime.
|
||||
RUN mkdir -p /app/.next/cache /app/.next/server/app \
|
||||
&& chown -R nextjs:nodejs /app/.next
|
||||
|
||||
USER nextjs
|
||||
|
||||
|
||||
21
memory/project_summary.md
Normal file
21
memory/project_summary.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# QR Master Project Summary
|
||||
|
||||
## TikTok Growth Positioning
|
||||
|
||||
QR Master should be positioned as control after print, not as another generic QR code generator.
|
||||
|
||||
Core message:
|
||||
|
||||
> A printed QR code is only useful if the destination can change, the scans can be measured, and the business does not need to reprint.
|
||||
|
||||
Best early TikTok angles from the supplied account analysis:
|
||||
|
||||
- Reprint pain after a link changes.
|
||||
- Static QR code risks.
|
||||
- Restaurant QR menu problems.
|
||||
- Event and flyer changes.
|
||||
- Business card QR workflows.
|
||||
- QR scan analytics for print campaigns.
|
||||
|
||||
TikTok content should lead with a concrete business mistake or relatable QR frustration, then connect the fix to dynamic QR codes and QR Master.
|
||||
|
||||
421
scripts/scrape-us-qrmaster-leads.mjs
Normal file
421
scripts/scrape-us-qrmaster-leads.mjs
Normal file
@@ -0,0 +1,421 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const OUTPUT_DIR = path.resolve(process.cwd(), 'output', 'outreach');
|
||||
const TARGET_PER_NICHE = Number(process.env.LEADS_PER_NICHE || 200);
|
||||
const CONCURRENCY = Number(process.env.LEAD_FETCH_CONCURRENCY || 8);
|
||||
const OVERPASS_DELAY_MS = Number(process.env.OVERPASS_DELAY_MS || 20000);
|
||||
const OVERPASS_429_DELAY_MS = Number(process.env.OVERPASS_429_DELAY_MS || 90000);
|
||||
const OVERPASS_MAX_ATTEMPTS = Number(process.env.OVERPASS_MAX_ATTEMPTS || 6);
|
||||
const OVERPASS_URLS = [
|
||||
'https://overpass-api.de/api/interpreter',
|
||||
];
|
||||
|
||||
const metros = [
|
||||
['New York', 'NY', 40.7128, -74.006],
|
||||
['Los Angeles', 'CA', 34.0522, -118.2437],
|
||||
['Chicago', 'IL', 41.8781, -87.6298],
|
||||
['Houston', 'TX', 29.7604, -95.3698],
|
||||
['Phoenix', 'AZ', 33.4484, -112.074],
|
||||
['Philadelphia', 'PA', 39.9526, -75.1652],
|
||||
['San Antonio', 'TX', 29.4241, -98.4936],
|
||||
['San Diego', 'CA', 32.7157, -117.1611],
|
||||
['Dallas', 'TX', 32.7767, -96.797],
|
||||
['San Jose', 'CA', 37.3382, -121.8863],
|
||||
['Austin', 'TX', 30.2672, -97.7431],
|
||||
['Jacksonville', 'FL', 30.3322, -81.6557],
|
||||
['Fort Worth', 'TX', 32.7555, -97.3308],
|
||||
['Columbus', 'OH', 39.9612, -82.9988],
|
||||
['Charlotte', 'NC', 35.2271, -80.8431],
|
||||
['San Francisco', 'CA', 37.7749, -122.4194],
|
||||
['Seattle', 'WA', 47.6062, -122.3321],
|
||||
['Denver', 'CO', 39.7392, -104.9903],
|
||||
['Miami', 'FL', 25.7617, -80.1918],
|
||||
['Nashville', 'TN', 36.1627, -86.7816],
|
||||
];
|
||||
|
||||
const niches = [
|
||||
{
|
||||
id: 'photographers',
|
||||
label: 'Photographers',
|
||||
targetUseCase: 'portfolio, booking, print cards, event galleries',
|
||||
queries: [
|
||||
['craft', 'photographer'],
|
||||
['shop', 'photo_studio'],
|
||||
['shop', 'photo'],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'restaurants',
|
||||
label: 'Restaurants',
|
||||
targetUseCase: 'menu QR codes, table tents, review QR codes, coupons',
|
||||
queries: [
|
||||
['amenity', 'restaurant'],
|
||||
['amenity', 'cafe'],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'real_estate',
|
||||
label: 'Real Estate',
|
||||
targetUseCase: 'yard signs, flyers, open houses, property sheets',
|
||||
queries: [
|
||||
['office', 'estate_agent'],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'events_venues',
|
||||
label: 'Events & Venues',
|
||||
targetUseCase: 'tickets, schedules, check-in, feedback and post-event links',
|
||||
queries: [
|
||||
['amenity', 'events_venue'],
|
||||
['amenity', 'theatre'],
|
||||
['amenity', 'conference_centre'],
|
||||
['tourism', 'attraction'],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'wellness_beauty',
|
||||
label: 'Wellness & Beauty',
|
||||
targetUseCase: 'booking links, price lists, reviews, loyalty offers',
|
||||
queries: [
|
||||
['shop', 'beauty'],
|
||||
['shop', 'hairdresser'],
|
||||
['leisure', 'fitness_centre'],
|
||||
['amenity', 'spa'],
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function csvEscape(value) {
|
||||
const text = String(value ?? '');
|
||||
if (/[",\n\r]/.test(text)) {
|
||||
return `"${text.replaceAll('"', '""')}"`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function normalizeWebsite(raw) {
|
||||
if (!raw) return '';
|
||||
let value = String(raw).trim();
|
||||
if (!value) return '';
|
||||
if (value.startsWith('mailto:') || value.includes('@') && !value.includes('/')) return '';
|
||||
if (!/^https?:\/\//i.test(value)) value = `https://${value}`;
|
||||
try {
|
||||
const url = new URL(value);
|
||||
if (!url.hostname.includes('.')) return '';
|
||||
url.hash = '';
|
||||
return url.toString().replace(/\/$/, '');
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function getTag(tags, names) {
|
||||
for (const name of names) {
|
||||
if (tags?.[name]) return tags[name];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function buildOverpassQuery(niche, metro, offset) {
|
||||
const [, , lat, lon] = metro;
|
||||
const radius = 25000 + offset * 10000;
|
||||
const clauses = niche.queries.flatMap(([key, value]) => [
|
||||
`nwr(around:${radius},${lat},${lon})["${key}"="${value}"]["website"];`,
|
||||
`nwr(around:${radius},${lat},${lon})["${key}"="${value}"]["contact:website"];`,
|
||||
`nwr(around:${radius},${lat},${lon})["${key}"="${value}"]["email"];`,
|
||||
`nwr(around:${radius},${lat},${lon})["${key}"="${value}"]["contact:email"];`,
|
||||
]).join('\n');
|
||||
|
||||
return `[out:json][timeout:45];
|
||||
(
|
||||
${clauses}
|
||||
);
|
||||
out tags center ${Math.min(TARGET_PER_NICHE * 2, 500)};`;
|
||||
}
|
||||
|
||||
async function fetchOverpass(query, attempt = 0) {
|
||||
const endpoint = OVERPASS_URLS[attempt % OVERPASS_URLS.length];
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), 90000);
|
||||
try {
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||
body: new URLSearchParams({ data: query }),
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
if (response.status === 429 && attempt < OVERPASS_MAX_ATTEMPTS) {
|
||||
const waitMs = OVERPASS_429_DELAY_MS + attempt * 30000;
|
||||
console.warn(`Overpass rate limited; waiting ${Math.round(waitMs / 1000)}s before retry ${attempt + 1}/${OVERPASS_MAX_ATTEMPTS}`);
|
||||
await sleep(waitMs);
|
||||
return fetchOverpass(query, attempt + 1);
|
||||
}
|
||||
if (attempt < OVERPASS_MAX_ATTEMPTS) {
|
||||
await sleep(5000 * (attempt + 1));
|
||||
return fetchOverpass(query, attempt + 1);
|
||||
}
|
||||
throw new Error(`Overpass ${response.status} ${response.statusText}`);
|
||||
}
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
if (attempt < OVERPASS_MAX_ATTEMPTS) {
|
||||
await sleep(5000 * (attempt + 1));
|
||||
return fetchOverpass(query, attempt + 1);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function elementToLead(element, niche, metro) {
|
||||
const tags = element.tags || {};
|
||||
const website = normalizeWebsite(getTag(tags, ['contact:website', 'website', 'url']));
|
||||
const email = getTag(tags, ['contact:email', 'email']);
|
||||
const phone = getTag(tags, ['contact:phone', 'phone']);
|
||||
const street = [tags['addr:housenumber'], tags['addr:street']].filter(Boolean).join(' ');
|
||||
const city = tags['addr:city'] || metro[0];
|
||||
const state = tags['addr:state'] || metro[1];
|
||||
|
||||
return {
|
||||
niche: niche.id,
|
||||
niche_label: niche.label,
|
||||
company: tags.name || '',
|
||||
website,
|
||||
email,
|
||||
phone,
|
||||
city,
|
||||
state,
|
||||
country: 'US',
|
||||
street,
|
||||
source: 'OpenStreetMap Overpass',
|
||||
source_id: `${element.type}/${element.id}`,
|
||||
source_url: `https://www.openstreetmap.org/${element.type}/${element.id}`,
|
||||
personalization_signal: '',
|
||||
qr_use_case: niche.targetUseCase,
|
||||
lead_score: 0,
|
||||
email_source: email ? 'osm' : '',
|
||||
opt_out_required: 'yes',
|
||||
};
|
||||
}
|
||||
|
||||
function visibleTextEmails(text) {
|
||||
const normalized = text
|
||||
.replaceAll('[at]', '@')
|
||||
.replaceAll('(at)', '@')
|
||||
.replaceAll(' at ', '@')
|
||||
.replaceAll('[dot]', '.')
|
||||
.replaceAll('(dot)', '.')
|
||||
.replaceAll(' dot ', '.');
|
||||
const matches = normalized.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [];
|
||||
return [...new Set(matches.map((email) => email.toLowerCase()))]
|
||||
.filter((email) => !email.endsWith('.png') && !email.endsWith('.jpg') && !email.includes('example.com'))
|
||||
.filter((email) => !email.includes('wixpress.com') && !email.includes('sentry.io'));
|
||||
}
|
||||
|
||||
function extractContactLinks(html, baseUrl) {
|
||||
const links = [];
|
||||
const regex = /href=["']([^"']+)["']/gi;
|
||||
let match;
|
||||
while ((match = regex.exec(html))) {
|
||||
const href = match[1];
|
||||
if (/^(mailto:|tel:)/i.test(href)) continue;
|
||||
if (!/(contact|about|team|booking|book|wedding|private-events|catering|visit|location)/i.test(href)) continue;
|
||||
try {
|
||||
const url = new URL(href, baseUrl);
|
||||
if (url.hostname === new URL(baseUrl).hostname) {
|
||||
url.hash = '';
|
||||
links.push(url.toString());
|
||||
}
|
||||
} catch {
|
||||
// Ignore malformed links.
|
||||
}
|
||||
}
|
||||
return [...new Set(links)].slice(0, 3);
|
||||
}
|
||||
|
||||
async function fetchText(url) {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), 10000);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'user-agent': 'QR Master lead research bot (+https://qrmaster.net/contact)',
|
||||
accept: 'text/html,application/xhtml+xml',
|
||||
},
|
||||
signal: controller.signal,
|
||||
redirect: 'follow',
|
||||
});
|
||||
if (!response.ok) return '';
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
if (!contentType.includes('text/html')) return '';
|
||||
return await response.text();
|
||||
} catch {
|
||||
return '';
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
async function enrichLead(lead) {
|
||||
if (!lead.website || lead.email) {
|
||||
return scoreLead(lead);
|
||||
}
|
||||
|
||||
const homepage = await fetchText(lead.website);
|
||||
const emails = visibleTextEmails(homepage);
|
||||
const contactLinks = extractContactLinks(homepage, lead.website);
|
||||
|
||||
for (const link of contactLinks) {
|
||||
if (emails.length > 0) break;
|
||||
const html = await fetchText(link);
|
||||
emails.push(...visibleTextEmails(html));
|
||||
}
|
||||
|
||||
const uniqueEmails = [...new Set(emails)];
|
||||
if (uniqueEmails.length > 0) {
|
||||
lead.email = uniqueEmails[0];
|
||||
lead.email_source = 'website';
|
||||
}
|
||||
|
||||
return scoreLead(lead);
|
||||
}
|
||||
|
||||
function scoreLead(lead) {
|
||||
let score = 30;
|
||||
if (lead.website) score += 20;
|
||||
if (lead.email) score += 30;
|
||||
if (lead.phone) score += 5;
|
||||
if (!/(gmail|yahoo|hotmail|outlook|icloud)\.com$/i.test(lead.email || '')) score += lead.email ? 10 : 0;
|
||||
if (lead.niche === 'real_estate' || lead.niche === 'restaurants') score += 5;
|
||||
|
||||
const signalByNiche = {
|
||||
photographers: `${lead.company} can use dynamic QR codes on print cards, gallery cards, event handouts, and portfolio links.`,
|
||||
restaurants: `${lead.company} can use dynamic QR codes for menus, table tents, reviews, coupons, and seasonal specials.`,
|
||||
real_estate: `${lead.company} can use dynamic QR codes on yard signs, flyers, property sheets, and open house material.`,
|
||||
events_venues: `${lead.company} can use dynamic QR codes for schedules, ticketing, venue maps, check-in, and post-event feedback.`,
|
||||
wellness_beauty: `${lead.company} can use dynamic QR codes for booking pages, service menus, price lists, reviews, and loyalty offers.`,
|
||||
};
|
||||
|
||||
lead.lead_score = Math.min(score, 100);
|
||||
lead.personalization_signal = signalByNiche[lead.niche] || '';
|
||||
return lead;
|
||||
}
|
||||
|
||||
async function mapLimit(items, limit, mapper) {
|
||||
const results = [];
|
||||
let index = 0;
|
||||
async function worker() {
|
||||
while (index < items.length) {
|
||||
const current = index++;
|
||||
results[current] = await mapper(items[current], current);
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
||||
return results;
|
||||
}
|
||||
|
||||
async function collectNiche(niche) {
|
||||
const leadsByKey = new Map();
|
||||
for (let pass = 0; pass < 2 && leadsByKey.size < TARGET_PER_NICHE * 2; pass++) {
|
||||
for (const metro of metros) {
|
||||
if (leadsByKey.size >= TARGET_PER_NICHE * 2) break;
|
||||
const query = buildOverpassQuery(niche, metro, pass);
|
||||
try {
|
||||
const data = await fetchOverpass(query);
|
||||
for (const element of data.elements || []) {
|
||||
const lead = elementToLead(element, niche, metro);
|
||||
if (!lead.company) continue;
|
||||
if (!lead.website && !lead.email) continue;
|
||||
const key = lead.website || `${lead.company}|${lead.city}|${lead.state}`.toLowerCase();
|
||||
if (!leadsByKey.has(key)) leadsByKey.set(key, lead);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[${niche.id}] ${metro[0]} skipped: ${error.message}`);
|
||||
}
|
||||
await sleep(OVERPASS_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
const rawLeads = [...leadsByKey.values()].slice(0, TARGET_PER_NICHE * 2);
|
||||
console.log(`[${niche.id}] collected ${rawLeads.length}; enriching...`);
|
||||
const enriched = await mapLimit(rawLeads, CONCURRENCY, enrichLead);
|
||||
return enriched
|
||||
.filter((lead) => lead.website || lead.email)
|
||||
.sort((a, b) => b.lead_score - a.lead_score)
|
||||
.slice(0, TARGET_PER_NICHE);
|
||||
}
|
||||
|
||||
function toCsv(leads) {
|
||||
const headers = [
|
||||
'niche',
|
||||
'niche_label',
|
||||
'company',
|
||||
'website',
|
||||
'email',
|
||||
'email_source',
|
||||
'phone',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'street',
|
||||
'lead_score',
|
||||
'qr_use_case',
|
||||
'personalization_signal',
|
||||
'source',
|
||||
'source_id',
|
||||
'source_url',
|
||||
'opt_out_required',
|
||||
];
|
||||
return [
|
||||
headers.join(','),
|
||||
...leads.map((lead) => headers.map((header) => csvEscape(lead[header])).join(',')),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
||||
const allLeads = [];
|
||||
for (const niche of niches) {
|
||||
const leads = await collectNiche(niche);
|
||||
allLeads.push(...leads);
|
||||
const dated = new Date().toISOString().slice(0, 10);
|
||||
await fs.writeFile(path.join(OUTPUT_DIR, `qrmaster-us-leads-${niche.id}-${dated}.csv`), toCsv(leads), 'utf8');
|
||||
await fs.writeFile(path.join(OUTPUT_DIR, `qrmaster-us-leads-${niche.id}-${dated}.json`), JSON.stringify(leads, null, 2), 'utf8');
|
||||
console.log(`[${niche.id}] kept ${leads.length}`);
|
||||
}
|
||||
|
||||
const byKey = new Map();
|
||||
for (const lead of allLeads) {
|
||||
const key = lead.email || lead.website || `${lead.company}|${lead.city}|${lead.state}`.toLowerCase();
|
||||
if (!byKey.has(key)) byKey.set(key, lead);
|
||||
}
|
||||
const deduped = [...byKey.values()].sort((a, b) => b.lead_score - a.lead_score);
|
||||
const dated = new Date().toISOString().slice(0, 10);
|
||||
const csvPath = path.join(OUTPUT_DIR, `qrmaster-us-leads-${dated}.csv`);
|
||||
const jsonPath = path.join(OUTPUT_DIR, `qrmaster-us-leads-${dated}.json`);
|
||||
await fs.writeFile(csvPath, toCsv(deduped), 'utf8');
|
||||
await fs.writeFile(jsonPath, JSON.stringify(deduped, null, 2), 'utf8');
|
||||
|
||||
const summary = niches.map((niche) => {
|
||||
const leads = deduped.filter((lead) => lead.niche === niche.id);
|
||||
const withEmail = leads.filter((lead) => lead.email).length;
|
||||
return `${niche.label}: ${leads.length} leads, ${withEmail} emails`;
|
||||
}).join('\n');
|
||||
|
||||
console.log(`\nWrote ${deduped.length} leads`);
|
||||
console.log(csvPath);
|
||||
console.log(jsonPath);
|
||||
console.log(summary);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -277,7 +277,7 @@ const howToSchema = {
|
||||
'@id': 'https://www.qrmaster.net/dynamic-qr-code-generator#howto',
|
||||
name: 'How to create a dynamic QR code',
|
||||
datePublished: '2024-01-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
@@ -18,7 +18,7 @@ export function BarcodeGuide() {
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 mb-8 not-prose">
|
||||
By <strong className="text-slate-600">Timo Knuth</strong>, QR Master · Last updated: June 2025 · GS1-verified content
|
||||
By <strong className="text-slate-600">Timo Knuth</strong>, QR Master · Last updated: April 2026 · GS1-verified content
|
||||
</p>
|
||||
|
||||
<p className="lead text-xl text-slate-600">
|
||||
|
||||
@@ -56,7 +56,7 @@ const jsonLd = {
|
||||
'@type': 'HowTo',
|
||||
name: 'How to Create a Barcode',
|
||||
datePublished: '2024-06-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
@@ -50,7 +50,7 @@ const jsonLd = {
|
||||
'@type': 'HowTo',
|
||||
name: 'How to Create a Google Review QR Code',
|
||||
datePublished: '2024-01-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
@@ -54,7 +54,7 @@ const jsonLd = {
|
||||
name: 'How to Create an Instagram QR Code',
|
||||
description: 'Create a QR code that opens an Instagram profile.',
|
||||
datePublished: '2024-01-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
@@ -53,7 +53,7 @@ const jsonLd = {
|
||||
'@type': 'HowTo',
|
||||
name: 'How to Create a vCard QR Code',
|
||||
datePublished: '2024-01-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
@@ -54,7 +54,7 @@ const jsonLd = {
|
||||
'@type': 'HowTo',
|
||||
name: 'How to Create a WiFi QR Code',
|
||||
datePublished: '2024-01-01',
|
||||
dateModified: '2025-06-01',
|
||||
dateModified: '2026-04-27',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Timo Knuth',
|
||||
|
||||
452
src/app/(marketing-de)/barcode-generator/page.tsx
Normal file
452
src/app/(marketing-de)/barcode-generator/page.tsx
Normal file
@@ -0,0 +1,452 @@
|
||||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import SeoJsonLd from '@/components/SeoJsonLd';
|
||||
import { organizationSchema, websiteSchema } from '@/lib/schema';
|
||||
import {
|
||||
generateFaqSchema,
|
||||
generateSoftwareAppSchema,
|
||||
} from '@/lib/schema-utils';
|
||||
import { FreeToolsGrid } from '@/components/marketing/FreeToolsGrid';
|
||||
import de from '@/i18n/de.json';
|
||||
import { FAQ } from '@/components/marketing/FAQ';
|
||||
import { ScrollToTop } from '@/components/ui/ScrollToTop';
|
||||
import {
|
||||
Barcode as BarcodeIcon,
|
||||
Check,
|
||||
Shield,
|
||||
Zap,
|
||||
Printer,
|
||||
Download,
|
||||
} from 'lucide-react';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
absolute: 'Barcode Generator Kostenlos – EAN, UPC, Code 128 Online',
|
||||
},
|
||||
description:
|
||||
'Erstellen Sie Barcodes kostenlos online: EAN-13, UPC-A und Code 128. Sofort druckfertig. Für Einzelhandel, Lager und Logistik. Keine Anmeldung erforderlich.',
|
||||
keywords: [
|
||||
'barcode generator',
|
||||
'barcode generator kostenlos',
|
||||
'ean-13 generator',
|
||||
'upc-a generator',
|
||||
'code 128 generator',
|
||||
'barcode erstellen',
|
||||
'barcode online erstellen',
|
||||
'strichcode generator',
|
||||
'barcode für amazon erstellen',
|
||||
'ean barcode generator',
|
||||
'produktbarcode erstellen',
|
||||
'lagerverwaltung barcode',
|
||||
'barcode drucken',
|
||||
],
|
||||
alternates: {
|
||||
canonical: 'https://www.qrmaster.net/barcode-generator',
|
||||
languages: {
|
||||
'x-default': 'https://www.qrmaster.net/tools/barcode-generator',
|
||||
en: 'https://www.qrmaster.net/tools/barcode-generator',
|
||||
de: 'https://www.qrmaster.net/barcode-generator',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: 'Barcode Generator – Kostenlos EAN, UPC & Code 128 erstellen',
|
||||
description:
|
||||
'Barcode Generator: Erstellen Sie professionelle Barcodes für Einzelhandel, Lager und Logistik. Kostenlos, ohne Anmeldung. Sofort druckfertig.',
|
||||
url: 'https://www.qrmaster.net/barcode-generator',
|
||||
locale: 'de_DE',
|
||||
type: 'website',
|
||||
images: [
|
||||
{
|
||||
url: 'https://www.qrmaster.net/barcode-generator-preview.png',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Barcode Generator – Kostenlos Online erstellen',
|
||||
description:
|
||||
'EAN-13, UPC-A und Code 128 Barcodes kostenlos erstellen. Sofort druckfertig.',
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
};
|
||||
|
||||
const faqQuestions = {
|
||||
'Was ist ein Barcode Generator?': {
|
||||
question: 'Was ist ein Barcode Generator?',
|
||||
answer:
|
||||
'Ein Barcode Generator ist ein Online-Tool, das Zahlen oder Texte in scannbare Barcode-Bilder umwandelt. Diese können für Produkte, Etiketten und Inventarsysteme verwendet werden.',
|
||||
},
|
||||
'Ist der Barcode Generator kostenlos?': {
|
||||
question: 'Ist der Barcode Generator kostenlos?',
|
||||
answer:
|
||||
'Ja, unser Barcode Generator ist vollständig kostenlos nutzbar. Sie können Barcodes ohne Anmeldung generieren, herunterladen und drucken.',
|
||||
},
|
||||
'Welches Barcode-Format brauche ich?': {
|
||||
question: 'Welches Barcode-Format brauche ich?',
|
||||
answer:
|
||||
'EAN-13 ist der Standard für Einzelhandel in Europa und weltweit. UPC-A wird hauptsächlich in den USA und Kanada verwendet. Code 128 eignet sich am besten für Logistik und interne Verfolgung, da er Buchstaben und Zahlen unterstützt.',
|
||||
},
|
||||
'Kann ich Barcodes als Vektorgrafik (SVG) herunterladen?': {
|
||||
question: 'Kann ich Barcodes als Vektorgrafik (SVG) herunterladen?',
|
||||
answer:
|
||||
'Ja! SVG-Dateien sind vektorbasiert und können ohne Qualitätsverlust auf jede Größe skaliert werden. Perfekt für professionelle Produktverpackungen und Etiketten.',
|
||||
},
|
||||
'Wie erstelle ich einen Barcode online?': {
|
||||
question: 'Wie erstelle ich einen Barcode online?',
|
||||
answer:
|
||||
'Geben Sie Ihre Produktnummer oder Ihren Text ein, wählen Sie das gewünschte Barcode-Format (z.B. EAN-13 oder Code 128) und der Barcode wird sofort erstellt. Dann können Sie ihn als PNG oder SVG herunterladen.',
|
||||
},
|
||||
'Sind die erstellten Barcodes scannbar?': {
|
||||
question: 'Sind die erstellten Barcodes scannbar?',
|
||||
answer:
|
||||
'Ja, wir generieren standardkonforme Barcodes, die von jedem handelsüblichen Barcode-Scanner gelesen werden können, einschließlich Smartphone-Kamera-Apps.',
|
||||
},
|
||||
'Kann ich diese Barcodes für Amazon verwenden?': {
|
||||
question: 'Kann ich diese Barcodes für Amazon verwenden?',
|
||||
answer:
|
||||
'Sie können hier das Barcode-Bild erstellen, wenn Sie bereits eine gültige EAN/UPC-Nummer haben. Offizielle GS1-Registrierungsnummern erhalten Sie jedoch nur direkt bei GS1, um Produkte auf Amazon oder in großen Einzelhandelssystemen zu listen.',
|
||||
},
|
||||
'Was ist der Unterschied zwischen Barcode und QR-Code?': {
|
||||
question: 'Was ist der Unterschied zwischen Barcode und QR-Code?',
|
||||
answer:
|
||||
'Ein Barcode speichert Daten horizontal (1D) und wird hauptsächlich für Produktkennungen verwendet. Ein QR-Code speichert Daten in 2D (Matrix) und kann viel mehr Informationen enthalten, wie URLs, Kontaktdaten oder WLAN-Zugangsdaten.',
|
||||
},
|
||||
'Kann ich mehrere Barcodes gleichzeitig erstellen?': {
|
||||
question: 'Kann ich mehrere Barcodes gleichzeitig erstellen?',
|
||||
answer:
|
||||
'Ja, mit einem PRO-Account können Sie mehrere Barcodes gleichzeitig generieren. Dies ist ideal für große Produktmengen oder Lagerverwaltung.',
|
||||
},
|
||||
'Was ist eine Prüfziffer bei Barcodes?': {
|
||||
question: 'Was ist eine Prüfziffer bei Barcodes?',
|
||||
answer:
|
||||
'Die Prüfziffer ist die letzte Ziffer in einem Barcode, die mathematisch aus den anderen Ziffern berechnet wird. Sie stellt sicher, dass der Barcode korrekt gescannt wird, selbst wenn er leicht beschädigt oder verkratzt ist.',
|
||||
},
|
||||
};
|
||||
|
||||
const jsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@graph': [
|
||||
generateSoftwareAppSchema(
|
||||
'Barcode Generator',
|
||||
'Erstellen Sie druckfertige Barcodes für EAN, UPC, Code 128 und weitere Formate. Kostenlos und ohne Anmeldung.',
|
||||
'/barcode-generator-preview.png',
|
||||
'UtilitiesApplication'
|
||||
),
|
||||
generateFaqSchema(faqQuestions),
|
||||
],
|
||||
};
|
||||
|
||||
export default function BarcodeGeneratorDEPage() {
|
||||
const t = de;
|
||||
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
|
||||
{/* SEO Content für Suchmaschinen */}
|
||||
<div className="sr-only" aria-hidden="false">
|
||||
<p>
|
||||
QR Masters kostenloser Barcode Generator ermöglicht es Ihnen,
|
||||
professionelle Barcodes für Produkte, Etiketten und Inventarsysteme zu
|
||||
erstellen. Unterstützt EAN-13 für europäischen Einzelhandel, UPC-A für
|
||||
US-Markt und Code 128 für Logistik und interne Verwendung. Alle
|
||||
Barcodes sind standardkonform und sofort druckfertig.
|
||||
</p>
|
||||
<p>
|
||||
Unser Barcode Generator funktioniert vollständig im Browser. Keine
|
||||
Software-Installation erforderlich. Exportieren Sie Ihre Barcodes als
|
||||
PNG für den sofortigen Druck oder als SVG für professionelle
|
||||
Verpackungsdesigns. Perfekt für kleine Unternehmen, Online-Händler und
|
||||
Lagerverwaltung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative bg-gradient-to-b from-slate-900 to-slate-800 text-white py-16 md:py-24">
|
||||
<div className="absolute inset-0 opacity-10">
|
||||
<svg className="w-full h-full" width="100%" height="100%">
|
||||
<defs>
|
||||
<pattern
|
||||
id="barcode_pattern_de"
|
||||
width="60"
|
||||
height="60"
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<path
|
||||
d="M5 0 V 60 M15 0 V 60 M20 0 V 60 M35 0 V 60 M40 0 V 60 M55 0 V 60"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeOpacity="0.5"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#barcode_pattern_de)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl relative z-10">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/10 text-white/90 text-sm font-medium mb-6 backdrop-blur-sm border border-white/10">
|
||||
<span className="flex h-2 w-2 relative">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-blue-400"></span>
|
||||
</span>
|
||||
Kostenloses Tool – Professionell & Schnell
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold tracking-tight mb-6">
|
||||
Kostenloser{' '}
|
||||
<span className="text-blue-400">Barcode Generator</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-lg md:text-xl text-slate-300 mb-8 leading-relaxed">
|
||||
Erstellen Sie professionelle Barcodes in Sekunden. Für{' '}
|
||||
<strong className="text-white">
|
||||
Einzelhandel, Lager und Logistik
|
||||
</strong>
|
||||
. Unterstützt EAN-13, UPC-A und Code 128. Keine Anmeldung
|
||||
erforderlich.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-4 text-sm font-medium">
|
||||
<div className="flex items-center gap-2 bg-white/5 px-4 py-2.5 rounded-xl border border-white/10">
|
||||
<Check className="w-4 h-4 text-blue-400" />
|
||||
Einzelhandel-fähig
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-white/5 px-4 py-2.5 rounded-xl border border-white/10">
|
||||
<Check className="w-4 h-4 text-blue-400" />
|
||||
SVG-Export
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-white/5 px-4 py-2.5 rounded-xl border border-white/10">
|
||||
<Check className="w-4 h-4 text-blue-400" />
|
||||
Ohne Anmeldung
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Generator Section */}
|
||||
<section className="py-12 px-4 bg-slate-100">
|
||||
<div className="container mx-auto max-w-7xl">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-bold mb-2">Barcode jetzt erstellen</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Geben Sie Ihre Daten ein und laden Sie den Barcode herunter
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-3xl shadow-xl overflow-hidden border border-slate-200">
|
||||
<div className="bg-slate-900 px-6 py-3 flex items-center gap-2">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500"></div>
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
|
||||
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
||||
</div>
|
||||
<span className="text-white/60 text-sm ml-2">
|
||||
qrmaster.net/tools/barcode-generator
|
||||
</span>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://www.qrmaster.net/tools/barcode-generator"
|
||||
className="w-full h-[650px] border-0"
|
||||
title="Barcode Generator"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-6">
|
||||
<a
|
||||
href="https://www.qrmaster.net/tools/barcode-generator"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-slate-900 transition-colors"
|
||||
>
|
||||
<span>Vollbild öffnen</span>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Barcode Types Info */}
|
||||
<section className="py-16 px-4 bg-white">
|
||||
<div className="container mx-auto max-w-7xl">
|
||||
<h2 className="text-3xl font-bold text-center mb-12">
|
||||
Barcode-Formate im Überblick
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
<div className="p-6 border rounded-xl">
|
||||
<div className="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center mb-4">
|
||||
<BarcodeIcon className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<h3 className="font-bold text-lg mb-2">EAN-13</h3>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
Internationaler Standard für Einzelhandel. 13 Ziffern, weltweit
|
||||
anerkannt.
|
||||
</p>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<strong>Einsatz:</strong> Produkte in Europa & global
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 border rounded-xl">
|
||||
<div className="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center mb-4">
|
||||
<BarcodeIcon className="w-6 h-6 text-green-600" />
|
||||
</div>
|
||||
<h3 className="font-bold text-lg mb-2">UPC-A</h3>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
Amerikanischer Standard. 12 Ziffern, hauptsächlich in
|
||||
Nordamerika.
|
||||
</p>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<strong>Einsatz:</strong> USA & Kanada Einzelhandel
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 border rounded-xl">
|
||||
<div className="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center mb-4">
|
||||
<BarcodeIcon className="w-6 h-6 text-purple-600" />
|
||||
</div>
|
||||
<h3 className="font-bold text-lg mb-2">Code 128</h3>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
Flexible Lösung für Logistik. Alphanumerisch, hohe Datendichte.
|
||||
</p>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<strong>Einsatz:</strong> Versand, Lager, Inventar
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works */}
|
||||
<section className="py-16 px-4 bg-slate-50">
|
||||
<div className="container mx-auto max-w-7xl">
|
||||
<h2 className="text-3xl font-bold text-center mb-12">
|
||||
So funktioniert unser Barcode Generator
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-5 gap-8">
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 bg-slate-900 text-white rounded-full flex items-center justify-center mx-auto mb-4 font-bold">
|
||||
1
|
||||
</div>
|
||||
<h3 className="font-bold mb-2">Daten eingeben</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Geben Sie Ihre Produktnummer oder Text ein.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 bg-slate-900 text-white rounded-full flex items-center justify-center mx-auto mb-4 font-bold">
|
||||
2
|
||||
</div>
|
||||
<h3 className="font-bold mb-2">Format wählen</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
EAN-13, UPC-A oder Code 128 auswählen.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 bg-slate-900 text-white rounded-full flex items-center justify-center mx-auto mb-4 font-bold">
|
||||
3
|
||||
</div>
|
||||
<h3 className="font-bold mb-2">Vorschau</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Barcode wird in Echtzeit aktualisiert.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 bg-slate-900 text-white rounded-full flex items-center justify-center mx-auto mb-4 font-bold">
|
||||
4
|
||||
</div>
|
||||
<h3 className="font-bold mb-2">Herunterladen</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Als PNG oder SVG speichern.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 bg-slate-900 text-white rounded-full flex items-center justify-center mx-auto mb-4 font-bold">
|
||||
5
|
||||
</div>
|
||||
<h3 className="font-bold mb-2">Drucken</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Sofort auf Etiketten drucken.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Use Cases */}
|
||||
<section className="py-16 px-4 bg-white">
|
||||
<div className="container mx-auto max-w-7xl">
|
||||
<h2 className="text-3xl font-bold text-center mb-4">
|
||||
Für wen ist unser Barcode Generator geeignet?
|
||||
</h2>
|
||||
<p className="text-center text-muted-foreground mb-12 max-w-2xl mx-auto">
|
||||
Ob Kleinunternehmer, Online-Händler oder Lagerverwaltung – unser
|
||||
Tool unterstützt Sie bei allen Barcode-Bedarfen.
|
||||
</p>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="p-6 bg-slate-50 rounded-xl">
|
||||
<Printer className="w-8 h-8 text-blue-600 mb-3" />
|
||||
<h3 className="font-bold mb-2">Einzelhandel</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
EAN-13 Barcodes für Produktetiketten im stationären Handel.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 bg-slate-50 rounded-xl">
|
||||
<Shield className="w-8 h-8 text-green-600 mb-3" />
|
||||
<h3 className="font-bold mb-2">E-Commerce</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
UPC/EAN für Amazon, eBay und andere Marktplätze.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 bg-slate-50 rounded-xl">
|
||||
<Zap className="w-8 h-8 text-orange-600 mb-3" />
|
||||
<h3 className="font-bold mb-2">Lager & Logistik</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Code 128 für interne Bestandsverfolgung und Versand.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 bg-slate-50 rounded-xl">
|
||||
<Download className="w-8 h-8 text-purple-600 mb-3" />
|
||||
<h3 className="font-bold mb-2">Druckerei</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
SVG-Export für professionelle Verpackungsdesigns.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<FAQ
|
||||
t={{
|
||||
faq: { title: 'Häufig gestellte Fragen', questions: faqQuestions },
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Free Tools Grid */}
|
||||
<FreeToolsGrid />
|
||||
|
||||
{/* Scroll to Top */}
|
||||
<ScrollToTop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -23,11 +23,12 @@ function truncateAtWord(text: string, maxLength: number): string {
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const title = 'QR Code Erstellen – Kostenlos | QR Master';
|
||||
const description = 'Erstellen Sie QR Codes kostenlos in Sekunden. Dynamische QR-Codes für Feedback, PDF und mehr. Mit Tracking, Branding und Massen-Erstellung.';
|
||||
const description =
|
||||
'Erstellen Sie QR Codes kostenlos in Sekunden. Dynamische QR-Codes für Feedback, PDF und mehr. Mit Tracking, Branding und Massen-Erstellung.';
|
||||
|
||||
return {
|
||||
title: {
|
||||
absolute: title
|
||||
absolute: title,
|
||||
},
|
||||
description,
|
||||
keywords: [
|
||||
@@ -42,7 +43,15 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
'feedback qr code',
|
||||
'pdf qr code',
|
||||
'coupon qr code',
|
||||
'app store qr code'
|
||||
'app store qr code',
|
||||
'qr code generator online',
|
||||
'qr code erstellen ohne anmeldung',
|
||||
'qr code für speisekarte erstellen',
|
||||
'qr code erstellen free',
|
||||
'online qr code generator kostenlos',
|
||||
'qr code für restaurant erstellen',
|
||||
'qr code für flyer erstellen',
|
||||
'qr code für visitenkarte erstellen',
|
||||
],
|
||||
alternates: {
|
||||
canonical: 'https://www.qrmaster.net/qr-code-erstellen',
|
||||
@@ -54,7 +63,8 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
},
|
||||
openGraph: {
|
||||
title: 'QR Code Erstellen – Kostenlos & Sofort | QR Master',
|
||||
description: 'Erstellen Sie QR Codes kostenlos in Sekunden. Mit Tracking, Branding und Massen-Erstellung.',
|
||||
description:
|
||||
'Erstellen Sie QR Codes kostenlos in Sekunden. Mit Tracking, Branding und Massen-Erstellung.',
|
||||
url: 'https://www.qrmaster.net/qr-code-erstellen',
|
||||
type: 'website',
|
||||
locale: 'de_DE',
|
||||
@@ -69,7 +79,8 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
},
|
||||
twitter: {
|
||||
title: 'QR Code Erstellen – Kostenlos | QR Master',
|
||||
description: 'QR Codes erstellen in Sekunden. Kostenlos, mit Tracking und individuellem Branding.',
|
||||
description:
|
||||
'QR Codes erstellen in Sekunden. Kostenlos, mit Tracking und individuellem Branding.',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -80,54 +91,205 @@ export default function QRCodeErstellenPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SeoJsonLd data={[organizationSchema(), websiteSchema(), generateFaqSchema(t.faq.questions)]} />
|
||||
|
||||
|
||||
<SeoJsonLd
|
||||
data={[
|
||||
organizationSchema(),
|
||||
websiteSchema(),
|
||||
generateFaqSchema(t.faq.questions),
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Server-rendered SEO content for crawlers - GERMAN */}
|
||||
<div className="sr-only" aria-hidden="false">
|
||||
<p>
|
||||
Erstellen Sie professionelle QR Codes für Ihr Unternehmen mit QR Master. Unser dynamischer QR Code Generator
|
||||
ermöglicht es Ihnen, trackbare QR Codes für Feedback, PDF-Dateien, Coupons und App Stores zu erstellen, Ziel-URLs jederzeit zu ändern und detaillierte Statistiken einzusehen.
|
||||
Perfekt für Restaurants, Einzelhandel, Events und Marketing-Kampagnen.
|
||||
Erstellen Sie professionelle QR Codes für Ihr Unternehmen mit QR
|
||||
Master. Unser dynamischer QR Code Generator ermöglicht es Ihnen,
|
||||
trackbare QR Codes für Feedback, PDF-Dateien, Coupons und App Stores
|
||||
zu erstellen, Ziel-URLs jederzeit zu ändern und detaillierte
|
||||
Statistiken einzusehen. Perfekt für Restaurants, Einzelhandel, Events
|
||||
und Marketing-Kampagnen.
|
||||
</p>
|
||||
<p>
|
||||
Funktionen: Dynamische QR Codes mit Echtzeit-Tracking, Massen-QR-Code-Erstellung aus Excel/CSV,
|
||||
individuelles Branding mit Farben und Logos, erweiterte Scan-Statistiken mit Gerätetypen und Standorten,
|
||||
vCard QR Codes für digitale Visitenkarten und QR Codes für Restaurant-Speisekarten.
|
||||
Funktionen: Dynamische QR Codes mit Echtzeit-Tracking,
|
||||
Massen-QR-Code-Erstellung aus Excel/CSV, individuelles Branding mit
|
||||
Farben und Logos, erweiterte Scan-Statistiken mit Gerätetypen und
|
||||
Standorten, vCard QR Codes für digitale Visitenkarten und QR Codes für
|
||||
Restaurant-Speisekarten.
|
||||
</p>
|
||||
<p>
|
||||
Starten Sie kostenlos mit 3 aktiven dynamischen QR Codes (8 Typen verfügbar) und unbegrenzten statischen Codes. Upgrade auf Pro für 50 Codes
|
||||
mit erweiterten Statistiken, oder Business für 500 Codes mit Massen-Erstellung und Prioritäts-Support.
|
||||
Starten Sie kostenlos mit 3 aktiven dynamischen QR Codes (8 Typen
|
||||
verfügbar) und unbegrenzten statischen Codes. Upgrade auf Pro für 50
|
||||
Codes mit erweiterten Statistiken, oder Business für 500 Codes mit
|
||||
Massen-Erstellung und Prioritäts-Support.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Hero t={t} />
|
||||
|
||||
{/* SEO Content: QR-Code-Typen und Anwendungen */}
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl py-12">
|
||||
<h2 className="text-3xl font-bold text-center mb-8">
|
||||
Beliebte QR-Code-Typen für deutsche Unternehmen
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">Speisekarten QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Ideal für Restaurants, Cafés und Bars. Gäste scannen den Code und
|
||||
sehen Ihre Speisekarte digital auf dem Handy –无需接触实物菜单。
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">Feedback QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Sammeln Sie Kundenfeedback direkt nach dem Besuch. Perfekt für
|
||||
Hotels, Zahnarztpraxen und Dienstleister.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">Coupon QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Erstellen Sie einlösbare Coupons mit Zeitlimit. Steigern Sie die
|
||||
Kundentreue in Ihrem Einzelhandel oder Onlineshop.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">vCard QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Digitale Visitenkarte: Kontaktdaten werden mit einem Scan ins
|
||||
Adressbuch übertragen. Für Vertrieb und Networking.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">PDF QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Verteilen Sie PDFs wie Speisekarten, Preislisten oder Anleitungen.
|
||||
Ändern Sie den Inhalt jederzeit ohne Neudruck.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border rounded-lg">
|
||||
<h3 className="font-semibold text-lg mb-2">WiFi QR-Code</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Gäste verbinden sich automatisch mit Ihrem WLAN. Für Hotels,
|
||||
Coworking Spaces und Gastronomie.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SEO Content: Branchen-Anwendungen */}
|
||||
<div className="bg-slate-50 py-12">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
|
||||
<h2 className="text-3xl font-bold text-center mb-8">
|
||||
QR-Codes nutzen: Für jede Branche
|
||||
</h2>
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start">
|
||||
<div className="md:w-1/3 font-semibold">
|
||||
Restaurants & Gastronomie
|
||||
</div>
|
||||
<div className="md:w-2/3 text-sm text-muted-foreground">
|
||||
Digitale Speisekarten statt Papier, Bestellsysteme über QR-Code,
|
||||
Feedback-Schleifen nach dem Besuch. Spart Druckkosten und
|
||||
reduziert Kontaktpunkte.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start">
|
||||
<div className="md:w-1/3 font-semibold">Einzelhandel</div>
|
||||
<div className="md:w-2/3 text-sm text-muted-foreground">
|
||||
Produktinformationen per QR-Code, Coupons und Aktionen,
|
||||
Cross-Selling zu Ihrem Onlineshop, Abfrage von
|
||||
Produktbewertungen.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start">
|
||||
<div className="md:w-1/3 font-semibold">Events & Messen</div>
|
||||
<div className="md:w-2/3 text-sm text-muted-foreground">
|
||||
Digitale Visitenkarten, Event-Flyer mit Programm-QR-Code,
|
||||
Feedback nach Vorträgen, Social-Media-Verbindungen.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start">
|
||||
<div className="md:w-1/3 font-semibold">
|
||||
Hotels & Ferienwohnungen
|
||||
</div>
|
||||
<div className="md:w-2/3 text-sm text-muted-foreground">
|
||||
Check-in über QR-Code, WLAN-Zugang ohne Passwort, digitale
|
||||
Hausordnung, lokale Empfehlungen und Concierge-Service.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start">
|
||||
<div className="md:w-1/3 font-semibold">
|
||||
Dienstleister & Agenturen
|
||||
</div>
|
||||
<div className="md:w-2/3 text-sm text-muted-foreground">
|
||||
vCards für Vertriebsteams, Projekt-Updates per PDF-QR-Code,
|
||||
Bewertungsanfragen nach Projektabschluss.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SEO Content: Tipps für höhere Scan-Raten */}
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl py-12">
|
||||
<h2 className="text-3xl font-bold text-center mb-8">
|
||||
Tipps für mehr QR-Code-Scans
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h3 className="font-semibold mb-3">Design optimieren</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li>• Hoher Kontrast: Dunkler Code auf hellem Hintergrund</li>
|
||||
<li>• Ausreichende Größe: Mindestens 2x2 cm, besser 3x3 cm</li>
|
||||
<li>
|
||||
• Eigenes Logo einbinden für Vertrauen und Wiedererkennung
|
||||
</li>
|
||||
<li>• Farben Ihrer Marke verwenden (Logo-Bereich)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-3">Placement verbessern</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li>
|
||||
• Augenhöhe: QR-Codes auf Tischhöhe oder Wandhöhe platzieren
|
||||
</li>
|
||||
<li>• Ausreichend Weißraum um den Code herum</li>
|
||||
<li>• In der Nähe des Call-to-Action: "Scannen Sie hier"</li>
|
||||
<li>• Mehrfach platzieren: Eingang, Theke, Tische, Kasse</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-sm text-muted-foreground mt-6">
|
||||
<strong>Tipp:</strong> Testen Sie Ihren QR-Code vor dem Druck mit
|
||||
mindestens 3 verschiedenen Smartphone-Modellen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Answer First Block (SEO/AEO) - German */}
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
|
||||
<AnswerFirstBlock
|
||||
whatIsIt="Ein QR-Code (Quick Response Code) ist ein zweidimensionaler Barcode, der von Smartphones gescannt werden kann, um sofort auf Informationen wie Webseiten, Kontaktdaten oder Speisekarten zuzugreifen. Mit QR Master erstellen Sie dynamische Codes, die Sie nachträglich ändern und statistisch auswerten können."
|
||||
whenToUse={[
|
||||
"Wenn Sie Printmaterialien (Flyer, Plakate) drucken und den Link später ändern möchten",
|
||||
"Wenn Sie wissen wollen, wie oft und wo Ihr Code gescannt wurde",
|
||||
"Wenn Sie professionelles Branding mit eigenem Logo benötigen"
|
||||
'Wenn Sie Printmaterialien (Flyer, Plakate) drucken und den Link später ändern möchten',
|
||||
'Wenn Sie wissen wollen, wie oft und wo Ihr Code gescannt wurde',
|
||||
'Wenn Sie professionelles Branding mit eigenem Logo benötigen',
|
||||
]}
|
||||
comparison={{
|
||||
leftTitle: "Statisch",
|
||||
rightTitle: "Dynamisch",
|
||||
leftTitle: 'Statisch',
|
||||
rightTitle: 'Dynamisch',
|
||||
items: [
|
||||
{ label: "Inhalt änderbar", value: true, text: "Nein" },
|
||||
{ label: "Scan-Statistik", value: true, text: "Nein" },
|
||||
{ label: "Druckqualität", value: true, text: "Hoch" }
|
||||
]
|
||||
{ label: 'Inhalt änderbar', value: true, text: 'Nein' },
|
||||
{ label: 'Scan-Statistik', value: true, text: 'Nein' },
|
||||
{ label: 'Druckqualität', value: true, text: 'Hoch' },
|
||||
],
|
||||
}}
|
||||
howTo={{
|
||||
steps: [
|
||||
"Kostenlos bei QR Master anmelden",
|
||||
"Ziel-URL eingeben und QR-Code Design anpassen",
|
||||
"Code herunterladen, testen und drucken. Der Inhalt ist jederzeit änderbar."
|
||||
]
|
||||
'Kostenlos bei QR Master anmelden',
|
||||
'Ziel-URL eingeben und QR-Code Design anpassen',
|
||||
'Code herunterladen, testen und drucken. Der Inhalt ist jederzeit änderbar.',
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,13 +5,13 @@ import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
|
||||
interface FAQProps {
|
||||
t: any; // i18n translation function
|
||||
t: any;
|
||||
}
|
||||
|
||||
export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
||||
|
||||
const questions = [
|
||||
const defaultQuestions = [
|
||||
'account',
|
||||
'static_vs_dynamic',
|
||||
'forever',
|
||||
@@ -19,6 +19,14 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
'analytics',
|
||||
];
|
||||
|
||||
const questions = t?.faq?.questions
|
||||
? Array.isArray(t.faq.questions)
|
||||
? t.faq.questions
|
||||
: Object.keys(t.faq.questions).length > 5
|
||||
? Object.keys(t.faq.questions).slice(0, 12)
|
||||
: defaultQuestions
|
||||
: defaultQuestions;
|
||||
|
||||
return (
|
||||
<section id="faq" className="py-16 bg-gray-50">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
|
||||
@@ -35,7 +43,7 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
</motion.div>
|
||||
|
||||
<div className="max-w-3xl mx-auto space-y-4">
|
||||
{questions.map((key, index) => (
|
||||
{questions.map((key: string, index: number) => (
|
||||
<motion.div
|
||||
key={key}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
@@ -43,11 +51,16 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
<Card className="cursor-pointer border-gray-200 hover:border-gray-300 transition-colors" onClick={() => setOpenIndex(openIndex === index ? null : index)}>
|
||||
<Card
|
||||
className="cursor-pointer border-gray-200 hover:border-gray-300 transition-colors"
|
||||
onClick={() => setOpenIndex(openIndex === index ? null : index)}
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{t.faq.questions[key].question}
|
||||
{t.faq.questions[key]?.question ||
|
||||
t.faq.questions[key]?.question ||
|
||||
key}
|
||||
</h3>
|
||||
<svg
|
||||
className={`w-5 h-5 text-gray-500 transition-transform duration-300 ${openIndex === index ? 'rotate-180' : ''}`}
|
||||
@@ -56,7 +69,12 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -70,7 +88,9 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="text-gray-600">
|
||||
{t.faq.questions[key].answer}
|
||||
{t.faq.questions[key]?.answer ||
|
||||
t.faq.questions[key]?.answer ||
|
||||
''}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -82,7 +102,10 @@ export const FAQ: React.FC<FAQProps> = ({ t }) => {
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-8">
|
||||
<a href="/faq" className="text-primary-600 hover:text-primary-700 font-medium">
|
||||
<a
|
||||
href="/faq"
|
||||
className="text-primary-600 hover:text-primary-700 font-medium"
|
||||
>
|
||||
View All Questions →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -227,6 +227,26 @@
|
||||
"bulk": {
|
||||
"question": "Kann ich Codes in großen Mengen mit meinen eigenen Daten erstellen?",
|
||||
"answer": "Ja, Sie können CSV- oder Excel-Dateien hochladen, um mehrere QR-Codes auf einmal mit individueller Datenzuordnung zu erstellen."
|
||||
},
|
||||
"no_account": {
|
||||
"question": "Kann ich QR-Codes auch ohne Anmeldung erstellen?",
|
||||
"answer": "Ja, Sie können unbegrenzt statische QR-Codes ohne Registrierung erstellen. Diese sind sofort einsatzbereit und funktionieren für immer. Für dynamische QR-Codes mit Tracking und Bearbeitung ist ein kostenloses Konto erforderlich."
|
||||
},
|
||||
"restaurant_menu": {
|
||||
"question": "Kann ich eine digitale Speisekarte per QR-Code erstellen?",
|
||||
"answer": "Ja, mit dem PDF-QR-Code können Sie Ihre Speisekarte als digitale Version erstellen. Gäste scannen den Code und sehen die Speisekarte auf ihrem Handy. Änderungen an der Karte sind ohne Neudruck möglich."
|
||||
},
|
||||
"scan_rate": {
|
||||
"question": "Was beeinflusst die Scan-Rate meines QR-Codes?",
|
||||
"answer": "Die Scan-Rate hängt von mehreren Faktoren ab: Platzierung (auf Augenhöhe), Größe (mindestens 2x2 cm), Kontrast (dunkel auf hell), Call-to-Action ('Scannen Sie hier') und Vorhandensein Ihres Logos für Vertrauen."
|
||||
},
|
||||
"branding": {
|
||||
"question": "Kann ich meinen QR-Code mit meinem Logo branden?",
|
||||
"answer": "Ja, ab dem Pro-Plan können Sie Ihre QR-Codes mit Ihrem Firmenlogo, individuellen Farben und Stilen anpassen. Dies erhöht die Vertrauenswürdigkeit und Wiedererkennung."
|
||||
},
|
||||
"data_security": {
|
||||
"question": "Sind meine QR-Code-Daten in Deutschland sicher?",
|
||||
"answer": "Wir hosten auf europäischen Servern und folgen der DSGVO. Scan-Daten werden anonymisiert gespeichert. Wir geben keine persönlichen Daten an Dritte weiter."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -29,9 +29,8 @@ export function generateSoftwareAppSchema(
|
||||
price: '0',
|
||||
priceCurrency: 'USD',
|
||||
availability: 'https://schema.org/InStock',
|
||||
priceValidUntil: '2026-12-31',
|
||||
priceValidUntil: '2027-12-31',
|
||||
},
|
||||
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user