initial migration
This commit is contained in:
299
lib/i18n.ts
Normal file
299
lib/i18n.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
export type Locale = 'en' | 'de';
|
||||
|
||||
export const defaultLocale: Locale = 'en';
|
||||
export const locales: Locale[] = ['en', 'de'];
|
||||
|
||||
// Simple translation dictionary
|
||||
const translations = {
|
||||
en: {
|
||||
site: {
|
||||
title: 'Kabel-Konfigurator',
|
||||
description: 'Professional cable solutions - configure and order custom cables',
|
||||
},
|
||||
nav: {
|
||||
home: 'Home',
|
||||
blog: 'Blog',
|
||||
products: 'Products',
|
||||
contact: 'Contact',
|
||||
privacy: 'Privacy Policy',
|
||||
legal: 'Legal Notice',
|
||||
terms: 'Terms & Conditions',
|
||||
},
|
||||
home: {
|
||||
hero: 'Professional Cable Solutions',
|
||||
heroSubtitle: 'Configure your custom cables online',
|
||||
cta: 'Configure Now',
|
||||
featuredPosts: 'Latest News',
|
||||
featuredProducts: 'Featured Products',
|
||||
},
|
||||
blog: {
|
||||
title: 'Blog',
|
||||
readMore: 'Read more',
|
||||
noPosts: 'No posts available.',
|
||||
backToBlog: '← Back to Blog',
|
||||
},
|
||||
products: {
|
||||
title: 'Products',
|
||||
categories: 'Categories',
|
||||
noProducts: 'No products available.',
|
||||
noCategories: 'No categories available.',
|
||||
inStock: 'In Stock',
|
||||
outOfStock: 'Out of Stock',
|
||||
price: 'Price',
|
||||
sku: 'SKU',
|
||||
viewAll: 'View All Products',
|
||||
},
|
||||
product: {
|
||||
backToProducts: '← Back to Products',
|
||||
description: 'Description',
|
||||
specifications: 'Specifications',
|
||||
price: 'Price',
|
||||
sku: 'SKU',
|
||||
stock: 'Stock Status',
|
||||
inStock: 'In Stock',
|
||||
outOfStock: 'Out of Stock',
|
||||
},
|
||||
productCategory: {
|
||||
backToCategories: '← Back to Categories',
|
||||
productsInCategory: 'Products in this category',
|
||||
},
|
||||
contact: {
|
||||
title: 'Contact Us',
|
||||
name: 'Your Name',
|
||||
email: 'Your Email',
|
||||
message: 'Your Message',
|
||||
submit: 'Send Message',
|
||||
success: 'Message sent successfully!',
|
||||
error: 'Failed to send message. Please try again.',
|
||||
processing: 'Sending...',
|
||||
phone: 'Phone (optional)',
|
||||
company: 'Company (optional)',
|
||||
},
|
||||
consent: {
|
||||
title: 'Cookie & Analytics Consent',
|
||||
description: 'We use analytics cookies to improve our website. Please accept to continue.',
|
||||
accept: 'Accept',
|
||||
decline: 'Decline',
|
||||
analytics: 'Analytics',
|
||||
analyticsDesc: 'Help us understand how visitors use our site',
|
||||
},
|
||||
footer: {
|
||||
rights: 'All rights reserved.',
|
||||
madeWith: 'Made with Next.js',
|
||||
},
|
||||
common: {
|
||||
readMore: 'Read more',
|
||||
back: 'Back',
|
||||
loading: 'Loading...',
|
||||
noContent: 'No content available.',
|
||||
date: 'Date',
|
||||
updated: 'Updated',
|
||||
},
|
||||
form: {
|
||||
success: 'Message sent successfully!',
|
||||
error: {
|
||||
submit: 'Failed to send message. Please try again.',
|
||||
network: 'Network error. Please try again.',
|
||||
},
|
||||
sending: 'Sending...',
|
||||
name: 'Your Name',
|
||||
email: 'Your Email',
|
||||
message: 'Your Message',
|
||||
submit: 'Send Message',
|
||||
},
|
||||
},
|
||||
de: {
|
||||
site: {
|
||||
title: 'Kabel-Konfigurator',
|
||||
description: 'Professionelle Kabel-Lösungen - konfigurieren und bestellen Sie maßgeschneiderte Kabel',
|
||||
},
|
||||
nav: {
|
||||
home: 'Startseite',
|
||||
blog: 'Blog',
|
||||
products: 'Produkte',
|
||||
contact: 'Kontakt',
|
||||
privacy: 'Datenschutz',
|
||||
legal: 'Impressum',
|
||||
terms: 'AGB',
|
||||
},
|
||||
home: {
|
||||
hero: 'Professionelle Kabel-Lösungen',
|
||||
heroSubtitle: 'Konfigurieren Sie Ihre maßgeschneiderten Kabel online',
|
||||
cta: 'Jetzt konfigurieren',
|
||||
featuredPosts: 'Aktuelle Neuigkeiten',
|
||||
featuredProducts: 'Empfohlene Produkte',
|
||||
},
|
||||
blog: {
|
||||
title: 'Blog',
|
||||
readMore: 'Weiterlesen',
|
||||
noPosts: 'Keine Beiträge verfügbar.',
|
||||
backToBlog: '← Zurück zum Blog',
|
||||
},
|
||||
products: {
|
||||
title: 'Produkte',
|
||||
categories: 'Kategorien',
|
||||
noProducts: 'Keine Produkte verfügbar.',
|
||||
noCategories: 'Keine Kategorien verfügbar.',
|
||||
inStock: 'Auf Lager',
|
||||
outOfStock: 'Nicht auf Lager',
|
||||
price: 'Preis',
|
||||
sku: 'Artikelnummer',
|
||||
viewAll: 'Alle Produkte anzeigen',
|
||||
},
|
||||
product: {
|
||||
backToProducts: '← Zurück zu Produkten',
|
||||
description: 'Beschreibung',
|
||||
specifications: 'Spezifikationen',
|
||||
price: 'Preis',
|
||||
sku: 'Artikelnummer',
|
||||
stock: 'Lagerbestand',
|
||||
inStock: 'Auf Lager',
|
||||
outOfStock: 'Nicht auf Lager',
|
||||
},
|
||||
productCategory: {
|
||||
backToCategories: '← Zurück zu Kategorien',
|
||||
productsInCategory: 'Produkte in dieser Kategorie',
|
||||
},
|
||||
contact: {
|
||||
title: 'Kontakt',
|
||||
name: 'Ihr Name',
|
||||
email: 'Ihre E-Mail',
|
||||
message: 'Ihre Nachricht',
|
||||
submit: 'Nachricht senden',
|
||||
success: 'Nachricht erfolgreich gesendet!',
|
||||
error: 'Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es erneut.',
|
||||
processing: 'Wird gesendet...',
|
||||
phone: 'Telefon (optional)',
|
||||
company: 'Firma (optional)',
|
||||
},
|
||||
consent: {
|
||||
title: 'Cookie- & Analyse-Einwilligung',
|
||||
description: 'Wir verwenden Analyse-Cookies, um unsere Website zu verbessern. Bitte akzeptieren Sie zur Fortsetzung.',
|
||||
accept: 'Akzeptieren',
|
||||
decline: 'Ablehnen',
|
||||
analytics: 'Analyse',
|
||||
analyticsDesc: 'Helfen Sie uns zu verstehen, wie Besucher unsere Seite nutzen',
|
||||
},
|
||||
footer: {
|
||||
rights: 'Alle Rechte vorbehalten.',
|
||||
madeWith: 'Erstellt mit Next.js',
|
||||
},
|
||||
common: {
|
||||
readMore: 'Weiterlesen',
|
||||
back: 'Zurück',
|
||||
loading: 'Wird geladen...',
|
||||
noContent: 'Kein Inhalt verfügbar.',
|
||||
date: 'Datum',
|
||||
updated: 'Aktualisiert',
|
||||
},
|
||||
form: {
|
||||
success: 'Nachricht erfolgreich gesendet!',
|
||||
error: {
|
||||
submit: 'Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es erneut.',
|
||||
network: 'Netzwerkfehler. Bitte versuchen Sie es erneut.',
|
||||
},
|
||||
sending: 'Wird gesendet...',
|
||||
name: 'Ihr Name',
|
||||
email: 'Ihre E-Mail',
|
||||
message: 'Ihre Nachricht',
|
||||
submit: 'Nachricht senden',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function t(key: string, locale: Locale = 'en'): string {
|
||||
const keys = key.split('.');
|
||||
let value: any = translations[locale];
|
||||
|
||||
for (const k of keys) {
|
||||
if (value && typeof value === 'object' && k in value) {
|
||||
value = value[k];
|
||||
} else {
|
||||
// Fallback to English
|
||||
value = translations.en;
|
||||
for (const k2 of keys) {
|
||||
if (value && typeof value === 'object' && k2 in value) {
|
||||
value = value[k2];
|
||||
} else {
|
||||
return key; // Return the key itself if translation not found
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we always return a string
|
||||
return typeof value === 'string' ? value : key;
|
||||
}
|
||||
|
||||
export function getLocaleFromPath(path: string): Locale {
|
||||
if (path.startsWith('/de/')) {
|
||||
return 'de';
|
||||
}
|
||||
return 'en';
|
||||
}
|
||||
|
||||
export function getLocalizedPath(path: string, locale: Locale): string {
|
||||
if (locale === 'en') {
|
||||
return path.replace('/de/', '/');
|
||||
}
|
||||
if (locale === 'de') {
|
||||
if (path === '/') return '/de';
|
||||
return path.startsWith('/de/') ? path : `/de${path}`;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export function getPathWithoutLocale(path: string): string {
|
||||
if (path.startsWith('/de/')) {
|
||||
return path.substring(3) || '/';
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export const languageNames: Record<Locale, string> = {
|
||||
en: 'English',
|
||||
de: 'Deutsch',
|
||||
};
|
||||
|
||||
export function getSiteInfo(locale?: Locale) {
|
||||
const loc = locale || defaultLocale;
|
||||
return {
|
||||
title: t('site.title', loc),
|
||||
description: t('site.description', loc),
|
||||
locale: loc,
|
||||
baseUrl: process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com',
|
||||
locales: ['en', 'de'],
|
||||
};
|
||||
}
|
||||
|
||||
// Hook for client components (simplified version)
|
||||
export function useTranslation(namespace?: string) {
|
||||
// This would be used in client components
|
||||
// For now, return a simple t function
|
||||
return {
|
||||
t: (key: string) => t(namespace ? `${namespace}.${key}` : key, defaultLocale)
|
||||
};
|
||||
}
|
||||
|
||||
// Get alternate URLs for SEO
|
||||
export function getAlternateUrls(path: string) {
|
||||
return [
|
||||
{ locale: 'en', url: path.replace('/de/', '/') },
|
||||
{ locale: 'de', url: path.startsWith('/de') ? path : `/de${path}` },
|
||||
];
|
||||
}
|
||||
|
||||
// Hook for client components - returns current locale
|
||||
export function useLocale(): Locale {
|
||||
// This is a simplified version for build purposes
|
||||
// In a real app, this would use next/navigation to get the current path
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
// Get dictionary for client components
|
||||
export function getDictionary(locale: Locale) {
|
||||
return {
|
||||
t: (key: string) => t(key, locale)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user