Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 7m26s
Remove unused imports, replace `any` types with proper interfaces in directus.ts and i18n-loader.ts, exclude scripts/ and coverage/ from ESLint, and fix unused variable warnings across the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
138 lines
3.5 KiB
TypeScript
138 lines
3.5 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, ContentPage } 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: unknown; expires: number }>();
|
|
|
|
function setCached(key: string, value: unknown, ttlSeconds = 300) {
|
|
cache.set(key, { value, expires: Date.now() + ttlSeconds * 1000 });
|
|
}
|
|
|
|
function getCached(key: string): unknown | 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 as string;
|
|
|
|
// 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<ContentPage | null> {
|
|
const cacheKey = `page:${slug}:${locale}`;
|
|
const cached = getCached(cacheKey);
|
|
if (cached !== null) return cached as ContentPage;
|
|
if (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: Record<string, unknown>, path: string): string | null {
|
|
const keys = path.split('.');
|
|
let value: unknown = obj;
|
|
for (const key of keys) {
|
|
if (value && typeof value === 'object' && key in value) {
|
|
value = (value as Record<string, unknown>)[key];
|
|
} else {
|
|
return null;
|
|
}
|
|
if (value === undefined) return null;
|
|
}
|
|
return typeof value === 'string' ? value : null;
|
|
}
|
|
|
|
/**
|
|
* Clear cache (useful for webhooks/revalidation)
|
|
*/
|
|
export function clearI18nCache() {
|
|
cache.clear();
|
|
}
|