This commit is contained in:
root
2025-12-22 06:43:19 +01:00
parent a940d51475
commit 6f4ca75faf
25 changed files with 1350 additions and 221 deletions

109
lib/i18n.tsx Normal file
View File

@@ -0,0 +1,109 @@
'use client'
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'
import enTranslations from './translations/en.json'
import deTranslations from './translations/de.json'
type Language = 'en' | 'de'
type TranslationKey = string
type Translations = typeof enTranslations
interface I18nContextType {
language: Language
setLanguage: (lang: Language) => void
t: (key: TranslationKey, params?: Record<string, string | number>) => string
}
const I18nContext = createContext<I18nContextType | undefined>(undefined)
const translations: Record<Language, Translations> = {
en: enTranslations,
de: deTranslations,
}
export function I18nProvider({ children }: { children: ReactNode }) {
const [language, setLanguageState] = useState<Language>('en')
// Load language from localStorage on mount
useEffect(() => {
const savedLanguage = localStorage.getItem('language') as Language
if (savedLanguage && (savedLanguage === 'en' || savedLanguage === 'de')) {
setLanguageState(savedLanguage)
} else {
// Detect browser language
const browserLang = navigator.language.split('-')[0]
if (browserLang === 'de') {
setLanguageState('de')
} else {
setLanguageState('en')
}
}
}, [])
const setLanguage = (lang: Language) => {
setLanguageState(lang)
localStorage.setItem('language', lang)
// Update HTML lang attribute
if (typeof document !== 'undefined') {
document.documentElement.lang = lang
}
}
const t = (key: TranslationKey, params?: Record<string, string | number>): string => {
const keys = key.split('.')
let value: any = translations[language]
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k]
} else {
// Fallback to English if key not found
value = translations.en
for (const fallbackKey of keys) {
if (value && typeof value === 'object' && fallbackKey in value) {
value = value[fallbackKey]
} else {
return key // Return key if translation not found
}
}
break
}
}
if (typeof value !== 'string') {
return key
}
// Replace parameters in the translation string
if (params) {
return value.replace(/\{(\w+)\}/g, (match, paramKey) => {
return params[paramKey]?.toString() || match
})
}
return value
}
// Update HTML lang attribute when language changes
useEffect(() => {
if (typeof document !== 'undefined') {
document.documentElement.lang = language
}
}, [language])
return (
<I18nContext.Provider value={{ language, setLanguage, t }}>
{children}
</I18nContext.Provider>
)
}
export function useI18n() {
const context = useContext(I18nContext)
if (context === undefined) {
throw new Error('useI18n must be used within an I18nProvider')
}
return context
}