134 lines
3.3 KiB
TypeScript
134 lines
3.3 KiB
TypeScript
/**
|
|
* i18n Loader with Directus + JSON Fallback
|
|
* - Fetches from Directus first
|
|
* - Falls back to JSON files if not found
|
|
* - Caches results (5 min TTL)
|
|
*/
|
|
|
|
import { getMessage, getContentPage } from './directus';
|
|
import enMessages from '@/messages/en.json';
|
|
import deMessages from '@/messages/de.json';
|
|
|
|
const jsonFallback = { en: enMessages, de: deMessages };
|
|
|
|
// Simple in-memory cache
|
|
const cache = new Map<string, { value: any; expires: number }>();
|
|
|
|
function setCached(key: string, value: any, ttlSeconds = 300) {
|
|
cache.set(key, { value, expires: Date.now() + ttlSeconds * 1000 });
|
|
}
|
|
|
|
function getCached(key: string): any | null {
|
|
const hit = cache.get(key);
|
|
if (!hit) return null;
|
|
if (Date.now() > hit.expires) {
|
|
cache.delete(key);
|
|
return null;
|
|
}
|
|
return hit.value;
|
|
}
|
|
|
|
/**
|
|
* Get a localized message by key
|
|
* Tries: Directus (requested locale) → Directus (EN) → JSON (requested locale) → JSON (EN)
|
|
*/
|
|
export async function getLocalizedMessage(
|
|
key: string,
|
|
locale: string
|
|
): Promise<string> {
|
|
const cacheKey = `msg:${key}:${locale}`;
|
|
const cached = getCached(cacheKey);
|
|
if (cached !== null) return cached;
|
|
|
|
// Try Directus with requested locale
|
|
const dbValue = await getMessage(key, locale);
|
|
if (dbValue) {
|
|
setCached(cacheKey, dbValue);
|
|
return dbValue;
|
|
}
|
|
|
|
// Fallback to EN in Directus if not EN already
|
|
if (locale !== 'en') {
|
|
const dbValueEn = await getMessage(key, 'en');
|
|
if (dbValueEn) {
|
|
setCached(cacheKey, dbValueEn);
|
|
return dbValueEn;
|
|
}
|
|
}
|
|
|
|
// Fallback to JSON file (normalize locale to 'en' or 'de')
|
|
const normalizedLocale = locale.startsWith('de') ? 'de' : 'en';
|
|
const jsonValue = getNestedValue(jsonFallback[normalizedLocale as 'en' | 'de'], key);
|
|
if (jsonValue) {
|
|
setCached(cacheKey, jsonValue);
|
|
return jsonValue;
|
|
}
|
|
|
|
// Fallback to EN JSON
|
|
if (normalizedLocale !== 'en') {
|
|
const jsonValueEn = getNestedValue(jsonFallback['en'], key);
|
|
if (jsonValueEn) {
|
|
setCached(cacheKey, jsonValueEn);
|
|
return jsonValueEn;
|
|
}
|
|
}
|
|
|
|
// Fallback: return the key itself
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* Get a localized content page by slug
|
|
* Tries: Directus (requested locale) → Directus (EN)
|
|
*/
|
|
export async function getLocalizedContent(
|
|
slug: string,
|
|
locale: string
|
|
): Promise<any | null> {
|
|
const cacheKey = `page:${slug}:${locale}`;
|
|
const cached = getCached(cacheKey);
|
|
if (cached !== null) return cached;
|
|
if (cached === null && cache.has(cacheKey)) return null; // Already checked, not found
|
|
|
|
// Try Directus with requested locale
|
|
const dbPage = await getContentPage(slug, locale);
|
|
if (dbPage) {
|
|
setCached(cacheKey, dbPage);
|
|
return dbPage;
|
|
}
|
|
|
|
// Fallback to EN in Directus
|
|
if (locale !== 'en') {
|
|
const dbPageEn = await getContentPage(slug, 'en');
|
|
if (dbPageEn) {
|
|
setCached(cacheKey, dbPageEn);
|
|
return dbPageEn;
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
setCached(cacheKey, null);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Helper: Get nested value from object
|
|
* Example: "nav.home" → obj.nav.home
|
|
*/
|
|
function getNestedValue(obj: any, path: string): any {
|
|
const keys = path.split('.');
|
|
let value = obj;
|
|
for (const key of keys) {
|
|
value = value?.[key];
|
|
if (value === undefined) return null;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Clear cache (useful for webhooks/revalidation)
|
|
*/
|
|
export function clearI18nCache() {
|
|
cache.clear();
|
|
}
|