235 lines
5.9 KiB
PHP
235 lines
5.9 KiB
PHP
<?php
|
|
// config/i18n.php
|
|
// Zentrale Sprachlogik für das gesamte Projekt
|
|
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
|
|
/**
|
|
* Liest die meta-Infos einer Sprachdatei.
|
|
* Erwartet Struktur:
|
|
* {
|
|
* "meta": { "code": "de", "label": "Deutsch", "flag": "🇩🇪", "enabled": true },
|
|
* ...
|
|
* }
|
|
*
|
|
* Gibt NULL zurück, wenn:
|
|
* - Datei nicht lesbar
|
|
* - JSON ungültig
|
|
* - kein meta vorhanden
|
|
* - meta.enabled existiert und NICHT true ist
|
|
*/
|
|
function app_i18n_load_language_meta_from_file(string $file): ?array
|
|
{
|
|
$json = @file_get_contents($file);
|
|
if ($json === false) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode($json, true);
|
|
if (!is_array($data)) {
|
|
return null;
|
|
}
|
|
|
|
$meta = $data['meta'] ?? null;
|
|
if (!is_array($meta)) {
|
|
return null;
|
|
}
|
|
|
|
// Nur aktive Sprachen (enabled === true)
|
|
if (array_key_exists('enabled', $meta) && !$meta['enabled']) {
|
|
return null;
|
|
}
|
|
|
|
// Code aus meta, Fallback: Dateiname
|
|
$code = '';
|
|
if (!empty($meta['code'])) {
|
|
$code = strtolower(substr((string)$meta['code'], 0, 5));
|
|
} else {
|
|
$base = basename($file, '.json');
|
|
$code = strtolower($base);
|
|
}
|
|
|
|
// Auf 2-Buchstaben-Codes normalisieren (de-DE → de)
|
|
if (strlen($code) > 2) {
|
|
$code = substr($code, 0, 2);
|
|
}
|
|
|
|
if ($code === '') {
|
|
return null;
|
|
}
|
|
|
|
$label = isset($meta['label']) && $meta['label'] !== ''
|
|
? (string)$meta['label']
|
|
: strtoupper($code);
|
|
|
|
$flag = isset($meta['flag']) ? (string)$meta['flag'] : '';
|
|
|
|
return [
|
|
'code' => $code,
|
|
'label' => $label,
|
|
'flag' => $flag,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Alle verfügbaren Sprachen aus /public/assets/i18n/*.json ermitteln.
|
|
* Verfügbar = JSON mit meta.enabled === true.
|
|
* EN wird garantiert hinzugefügt (Fallback), falls nicht gefunden.
|
|
*/
|
|
function app_i18n_detect_available_languages(): array
|
|
{
|
|
$baseDir = realpath(__DIR__ . '/../public/assets/i18n');
|
|
if ($baseDir === false) {
|
|
// Wenn gar kein Verzeichnis da ist: minimaler EN-Fallback
|
|
return [
|
|
'en' => [
|
|
'code' => 'en',
|
|
'label' => 'English',
|
|
'flag' => '',
|
|
],
|
|
];
|
|
}
|
|
|
|
$files = glob($baseDir . '/*.json') ?: [];
|
|
$langs = [];
|
|
|
|
foreach ($files as $file) {
|
|
$meta = app_i18n_load_language_meta_from_file($file);
|
|
if ($meta === null) {
|
|
continue;
|
|
}
|
|
|
|
$code = $meta['code'];
|
|
|
|
// Erste gültige Definition pro Code gewinnt
|
|
if (!isset($langs[$code])) {
|
|
$langs[$code] = $meta;
|
|
}
|
|
}
|
|
|
|
// EN muss immer vorhanden sein (laut deiner Vorgabe)
|
|
if (!isset($langs['en'])) {
|
|
// Versuch: gibt es eine en.json, auch wenn enabled=false?
|
|
foreach ($files as $file) {
|
|
$base = strtolower(basename($file, '.json'));
|
|
if ($base === 'en') {
|
|
$json = @file_get_contents($file);
|
|
$data = json_decode($json, true);
|
|
$meta = is_array($data['meta'] ?? null) ? $data['meta'] : [];
|
|
|
|
$label = isset($meta['label']) && $meta['label'] !== ''
|
|
? (string)$meta['label']
|
|
: 'English';
|
|
$flag = isset($meta['flag']) ? (string)$meta['flag'] : '';
|
|
|
|
$langs['en'] = [
|
|
'code' => 'en',
|
|
'label' => $label,
|
|
'flag' => $flag,
|
|
];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wenn immer noch kein EN → minimaler Stub
|
|
if (!isset($langs['en'])) {
|
|
$langs['en'] = [
|
|
'code' => 'en',
|
|
'label' => 'English',
|
|
'flag' => '',
|
|
];
|
|
}
|
|
|
|
ksort($langs);
|
|
|
|
return $langs;
|
|
}
|
|
|
|
/**
|
|
* Browsersprache aus HTTP_ACCEPT_LANGUAGE extrahieren (2-Buchstaben),
|
|
* aber nur, wenn sie in $available existiert.
|
|
*/
|
|
function app_i18n_detect_browser_lang(array $available): ?string
|
|
{
|
|
$header = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
|
|
if ($header === '') {
|
|
return null;
|
|
}
|
|
|
|
$parts = explode(',', $header);
|
|
foreach ($parts as $part) {
|
|
$part = trim($part);
|
|
if ($part === '') {
|
|
continue;
|
|
}
|
|
|
|
$code = strtolower(substr($part, 0, 2)); // "de-DE" → "de"
|
|
if (isset($available[$code])) {
|
|
return $code;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Aktuelle Sprache bestimmen:
|
|
* 1) ?lang=xx (wenn in $available)
|
|
* 2) Browsersprache (wenn in $available)
|
|
* 3) Fallback "en"
|
|
*/
|
|
function app_i18n_resolve_current_lang(array $available): string
|
|
{
|
|
// 1) URL-Parameter ?lang=xx
|
|
if (!empty($_GET['lang'])) {
|
|
$param = strtolower(substr($_GET['lang'], 0, 2));
|
|
if (isset($available[$param])) {
|
|
return $param;
|
|
}
|
|
}
|
|
|
|
// 2) Browsersprache
|
|
$browser = app_i18n_detect_browser_lang($available);
|
|
if ($browser !== null) {
|
|
return $browser;
|
|
}
|
|
|
|
// 3) Standard EN
|
|
if (isset($available['en'])) {
|
|
return 'en';
|
|
}
|
|
|
|
// Sicherheitsfallback: erste verfügbare Sprache
|
|
$keys = array_keys($available);
|
|
return $keys[0] ?? 'en';
|
|
}
|
|
|
|
// -----------------------------------------------------
|
|
// Bootstrap ausführen
|
|
// -----------------------------------------------------
|
|
|
|
$availableLangs = app_i18n_detect_available_languages();
|
|
$currentLang = app_i18n_resolve_current_lang($availableLangs);
|
|
|
|
// Global bereitstellen
|
|
$GLOBALS['availableLangs'] = $availableLangs;
|
|
$GLOBALS['lang'] = $currentLang;
|
|
|
|
// Optional in Session merken (muss nicht, schadet aber auch nicht)
|
|
$_SESSION['lang'] = $currentLang;
|
|
|
|
/**
|
|
* Frontend-Config für JS (usbConfig.i18n)
|
|
* → benutzt direkt die Meta-Daten aus den JSONs (code, label, flag)
|
|
*/
|
|
function app_i18n_get_frontend_config(): array
|
|
{
|
|
return [
|
|
'current' => $GLOBALS['lang'] ?? 'en',
|
|
'available' => $GLOBALS['availableLangs'] ?? [],
|
|
];
|
|
}
|