locale upgrade
This commit is contained in:
79
app/api/i18n/[namespace]/route.ts
Normal file
79
app/api/i18n/[namespace]/route.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getLocalizedMessage } from '@/lib/i18n-loader';
|
||||
import enMessages from '@/messages/en.json';
|
||||
import deMessages from '@/messages/de.json';
|
||||
|
||||
// Cache für 5 Minuten
|
||||
export const revalidate = 300;
|
||||
|
||||
const messagesMap = { en: enMessages, de: deMessages };
|
||||
|
||||
/**
|
||||
* GET /api/i18n/[namespace]?locale=en
|
||||
* Lädt alle Keys eines Namespace aus Directus oder JSON
|
||||
*/
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
{ params }: { params: { namespace: string } }
|
||||
) {
|
||||
const namespace = params.namespace;
|
||||
const locale = req.nextUrl.searchParams.get('locale') || 'en';
|
||||
|
||||
// Normalize locale (de-DE -> de)
|
||||
const normalizedLocale = locale.startsWith('de') ? 'de' : 'en';
|
||||
|
||||
try {
|
||||
// Hole alle Keys aus JSON für diesen Namespace
|
||||
const jsonData = messagesMap[normalizedLocale as 'en' | 'de'];
|
||||
const namespaceData = getNestedValue(jsonData, namespace);
|
||||
|
||||
if (!namespaceData || typeof namespaceData !== 'object') {
|
||||
return NextResponse.json({}, { status: 200 });
|
||||
}
|
||||
|
||||
// Flatten das Objekt zu flachen Keys
|
||||
const flatKeys = flattenObject(namespaceData);
|
||||
|
||||
// Lade jeden Key aus Directus (mit Fallback auf JSON)
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(flatKeys).map(async ([key, jsonValue]) => {
|
||||
const fullKey = `${namespace}.${key}`;
|
||||
const value = await getLocalizedMessage(fullKey, locale);
|
||||
result[key] = value || String(jsonValue);
|
||||
})
|
||||
);
|
||||
|
||||
return NextResponse.json(result, {
|
||||
headers: {
|
||||
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('i18n API error:', error);
|
||||
return NextResponse.json({ error: 'Failed to load translations' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Holt verschachtelte Werte aus Objekt
|
||||
function getNestedValue(obj: any, path: string): any {
|
||||
return path.split('.').reduce((current, key) => current?.[key], obj);
|
||||
}
|
||||
|
||||
// Helper: Flatten verschachteltes Objekt zu flachen Keys
|
||||
function flattenObject(obj: any, prefix = ''): Record<string, string> {
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const newKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
Object.assign(result, flattenObject(value, newKey));
|
||||
} else {
|
||||
result[newKey] = String(value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
94
app/api/messages/route.ts
Normal file
94
app/api/messages/route.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getLocalizedMessage } from '@/lib/i18n-loader';
|
||||
import enMessages from '@/messages/en.json';
|
||||
import deMessages from '@/messages/de.json';
|
||||
|
||||
// Cache für 5 Minuten
|
||||
export const revalidate = 300;
|
||||
|
||||
const messagesMap = { en: enMessages, de: deMessages };
|
||||
|
||||
/**
|
||||
* GET /api/messages?locale=en
|
||||
* Lädt ALLE Messages aus Directus + JSON Fallback
|
||||
* Wird von next-intl als messages source verwendet
|
||||
*/
|
||||
export async function GET(req: NextRequest) {
|
||||
const locale = req.nextUrl.searchParams.get('locale') || 'en';
|
||||
|
||||
// Normalize locale (de-DE -> de)
|
||||
const normalizedLocale = locale.startsWith('de') ? 'de' : 'en';
|
||||
|
||||
try {
|
||||
// Starte mit JSON als Basis
|
||||
const jsonMessages = messagesMap[normalizedLocale as 'en' | 'de'];
|
||||
|
||||
// Clone das Objekt
|
||||
const messages = JSON.parse(JSON.stringify(jsonMessages));
|
||||
|
||||
// Flatten alle Keys
|
||||
const allKeys = getAllKeys(messages);
|
||||
|
||||
// Lade jeden Key aus Directus (überschreibt JSON wenn vorhanden)
|
||||
await Promise.all(
|
||||
allKeys.map(async (key) => {
|
||||
try {
|
||||
const value = await getLocalizedMessage(key, locale);
|
||||
if (value && value !== key) {
|
||||
// Überschreibe den Wert im messages Objekt
|
||||
setNestedValue(messages, key, value);
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback auf JSON Wert (schon vorhanden)
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return NextResponse.json(messages, {
|
||||
headers: {
|
||||
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Messages API error:', error);
|
||||
// Fallback: Return nur JSON messages
|
||||
return NextResponse.json(messagesMap[normalizedLocale as 'en' | 'de'], {
|
||||
headers: {
|
||||
'Cache-Control': 'public, s-maxage=60',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Sammle alle Keys aus verschachteltem Objekt
|
||||
function getAllKeys(obj: any, prefix = ''): string[] {
|
||||
const keys: string[] = [];
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const fullKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
keys.push(...getAllKeys(value, fullKey));
|
||||
} else {
|
||||
keys.push(fullKey);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Helper: Setze Wert in verschachteltem Objekt
|
||||
function setNestedValue(obj: any, path: string, value: any) {
|
||||
const keys = path.split('.');
|
||||
const lastKey = keys.pop()!;
|
||||
|
||||
let current = obj;
|
||||
for (const key of keys) {
|
||||
if (!(key in current)) {
|
||||
current[key] = {};
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
current[lastKey] = value;
|
||||
}
|
||||
@@ -42,11 +42,13 @@ export async function PUT(
|
||||
locale?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
content?: string;
|
||||
};
|
||||
|
||||
const locale = body.locale || "en";
|
||||
const title = body.title?.trim();
|
||||
const description = body.description?.trim();
|
||||
const content = typeof body.content === "string" ? body.content.trim() : undefined;
|
||||
|
||||
if (!title || !description) {
|
||||
return NextResponse.json({ error: "title and description are required" }, { status: 400 });
|
||||
@@ -59,10 +61,12 @@ export async function PUT(
|
||||
locale,
|
||||
title,
|
||||
description,
|
||||
content: content ?? null,
|
||||
},
|
||||
update: {
|
||||
title,
|
||||
description,
|
||||
content: content ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user