i18n
This commit is contained in:
82
src/client/I18nContext.js
Normal file
82
src/client/I18nContext.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';
|
||||
import en from './i18n/en.json';
|
||||
import de from './i18n/de.json';
|
||||
|
||||
const translations = { en, de };
|
||||
|
||||
// Cookie helpers
|
||||
function getCookie(name) {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
return match ? match[2] : null;
|
||||
}
|
||||
|
||||
function setCookie(name, value, days = 365) {
|
||||
const expires = new Date(Date.now() + days * 864e5).toUTCString();
|
||||
document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`;
|
||||
}
|
||||
|
||||
// Get initial language from cookie or browser preference
|
||||
function getInitialLanguage() {
|
||||
const cookieLang = getCookie('lang');
|
||||
if (cookieLang && translations[cookieLang]) {
|
||||
return cookieLang;
|
||||
}
|
||||
// Check browser preference
|
||||
const browserLang = navigator.language?.slice(0, 2);
|
||||
if (browserLang === 'de') return 'de';
|
||||
return 'en';
|
||||
}
|
||||
|
||||
const I18nContext = createContext(null);
|
||||
|
||||
export function I18nProvider({ children }) {
|
||||
const [language, setLanguageState] = useState(getInitialLanguage);
|
||||
|
||||
const setLanguage = useCallback((lang) => {
|
||||
if (translations[lang]) {
|
||||
setLanguageState(lang);
|
||||
setCookie('lang', lang);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Translation function with nested key support (e.g., 'app.title')
|
||||
const t = useCallback((key, params = {}) => {
|
||||
const keys = key.split('.');
|
||||
let value = translations[language];
|
||||
|
||||
for (const k of keys) {
|
||||
if (value && typeof value === 'object') {
|
||||
value = value[k];
|
||||
} else {
|
||||
return key; // fallback to key if not found
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') return key;
|
||||
|
||||
// Replace {param} placeholders
|
||||
return value.replace(/\{(\w+)\}/g, (_, param) =>
|
||||
params[param] !== undefined ? params[param] : `{${param}}`
|
||||
);
|
||||
}, [language]);
|
||||
|
||||
const value = useMemo(() => ({
|
||||
language,
|
||||
setLanguage,
|
||||
t
|
||||
}), [language, setLanguage, t]);
|
||||
|
||||
return (
|
||||
<I18nContext.Provider value={value}>
|
||||
{children}
|
||||
</I18nContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useI18n() {
|
||||
const context = useContext(I18nContext);
|
||||
if (!context) {
|
||||
throw new Error('useI18n must be used within an I18nProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user