116 lines
8.4 KiB
PHP
116 lines
8.4 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../includes/bootstrap.php';
|
|
$admin = require_admin_or_permission('manage_languages');
|
|
$pdo = get_db();
|
|
$langs = i18n_languages(true);
|
|
|
|
// Vorlage für Übersetzer: alle gesammelten deutschen Texte, bereits Übersetztes ist eingetragen, der Rest leer
|
|
if (isset($_GET['download'])) {
|
|
$code = strtolower((string) $_GET['download']);
|
|
$dictFile = $langs[$code]['strings'] ?? '';
|
|
$existing = $dictFile !== '' && is_file($dictFile) ? (json_decode((string) file_get_contents($dictFile), true) ?: []) : [];
|
|
$out = [];
|
|
foreach ($pdo->query('SELECT text FROM i18n_missing ORDER BY hits DESC, text')->fetchAll(PDO::FETCH_COLUMN) as $t) $out[$t] = '';
|
|
foreach ($existing as $k => $v) if (is_string($v) && ($k === '' || $k[0] !== '_')) $out[$k] = $v;
|
|
$out = ['_info' => 'Schlüssel = deutscher Text, Wert = Übersetzung. {} steht für wechselnde Teile. Leere Werte werden ignoriert.'] + $out;
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Content-Disposition: attachment; filename="strings-' . preg_replace('/[^a-z\-]/', '', $code) . '.json"');
|
|
echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
verify_csrf();
|
|
$action = $_POST['action'] ?? '';
|
|
if ($action === 'save') {
|
|
$def = strtolower((string) ($_POST['default_language'] ?? 'de'));
|
|
set_setting('default_language', isset($langs[$def]) ? $def : 'de');
|
|
set_setting('i18n_collect', !empty($_POST['i18n_collect']) ? '1' : '0');
|
|
audit_log($admin['id'], 'Sprach-Einstellungen geändert');
|
|
flash('success', 'Gespeichert.');
|
|
} elseif ($action === 'clear') {
|
|
$pdo->exec('DELETE FROM i18n_missing');
|
|
flash('success', 'Gesammelte Texte gelöscht.');
|
|
}
|
|
redirect('/staff/languages.php');
|
|
}
|
|
|
|
$collected = (int) $pdo->query('SELECT COUNT(*) FROM i18n_missing')->fetchColumn();
|
|
$collect = get_setting('i18n_collect', '0') === '1';
|
|
$pageTitle = 'Sprachen';
|
|
$activePage = 'languages';
|
|
require __DIR__ . '/_header.php';
|
|
?>
|
|
<h1>Sprachen</h1>
|
|
<p class="muted" style="max-width:860px">Jede Sprache ist ein Plugin („Sprachpaket“): Deutsch ist eingebaut, weitere Sprachen installieren Sie unter Plugins. Besucher und Mitarbeiter wechseln die Sprache über die Flaggen. Texte ohne Übersetzung bleiben deutsch.</p>
|
|
|
|
<div class="card" style="max-width:860px;margin-bottom:16px">
|
|
<h3>Installierte Sprachen</h3>
|
|
<?php foreach ($langs as $code => $l): $n = $code === 'de' ? null : i18n_dictionary($code)['count']; ?>
|
|
<div style="display:flex;gap:12px;align-items:center;padding:8px 0;border-top:1px solid var(--border,#e3e9ee)">
|
|
<?php if ($l['flag'] !== ''): ?><img src="<?= e($l['flag']) ?>" alt="" width="30" height="20" style="width:30px;height:20px;object-fit:cover;border-radius:3px;border:1px solid rgba(0,0,0,.15)"><?php endif; ?>
|
|
<div style="flex:1"><strong><?= e($l['name']) ?></strong> <span class="muted" style="font-size:.8rem"><?= e($code) ?><?= $l['slug'] !== '' ? ' · Plugin ' . e($l['slug']) : ' · eingebaut (Quellsprache)' ?></span></div>
|
|
<div class="muted" style="font-size:.85rem"><?= $n === null ? '' : $n . ' Übersetzungen' ?></div>
|
|
<a class="btn secondary" style="padding:3px 12px;font-size:.8rem" href="/staff/languages.php?download=<?= e($code) ?>"><?= $code === 'de' ? 'Leere Vorlage laden' : 'Vorlage laden' ?></a>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php if (count($langs) < 2): ?><p class="muted" style="margin-top:8px">Noch kein Sprachpaket aktiv. Installieren Sie zum Beispiel „English“ unter <a href="/staff/plugins.php">Plugins</a>; dann erscheinen die Flaggen.</p><?php endif; ?>
|
|
</div>
|
|
|
|
<?php
|
|
$pluginLangs = i18n_plugin_language_overview();
|
|
$otherCodes = array_values(array_filter(array_keys($langs), fn($c) => $c !== 'de'));
|
|
$activePlugins = [];
|
|
try { $activePlugins = $pdo->query("SELECT slug FROM plugins WHERE is_active = 1 ORDER BY slug")->fetchAll(PDO::FETCH_COLUMN); } catch (Throwable $e) { }
|
|
$activePlugins = array_values(array_filter($activePlugins, fn($sl) => !isset($langs) || !in_array($sl, array_column($langs, 'slug'), true)));
|
|
?>
|
|
<div class="card" style="max-width:860px;margin-bottom:16px">
|
|
<h3>Übersetzungen in den Plugins</h3>
|
|
<p class="muted" style="font-size:.88rem">Jedes Plugin kann seine Texte selbst übersetzen: Es legt dazu im Plugin-Ordner <code>lang/<Sprachcode>.json</code> ab (zum Beispiel <code>lang/en.json</code>). Wird eine Sprache gewählt, nutzt DeskHub diese Dateien aller aktiven Plugins zusätzlich zum Sprachpaket; steht ein Text in beiden, gilt der aus dem Sprachpaket. Eine neue Sprache braucht deshalb nur ein Sprachpaket und je Plugin eine Datei.</p>
|
|
<?php if (!$otherCodes): ?>
|
|
<p class="muted">Noch kein Sprachpaket aktiv. Installieren Sie zum Beispiel „English“ unter <a href="/staff/plugins.php">Plugins</a>.</p>
|
|
<?php elseif (!$activePlugins): ?>
|
|
<p class="muted">Es ist kein Plugin aktiv.</p>
|
|
<?php else: ?>
|
|
<div style="overflow-x:auto"><table style="width:100%">
|
|
<thead><tr><th>Plugin</th><?php foreach ($otherCodes as $c): ?><th><?= e($langs[$c]['name']) ?> (<?= e($c) ?>)</th><?php endforeach; ?></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($activePlugins as $sl): ?>
|
|
<tr><td><?= e((string) $sl) ?></td>
|
|
<?php foreach ($otherCodes as $c): $n = $pluginLangs[$sl][$c] ?? null; ?>
|
|
<td><?php if ($n === null): ?><span class="muted">keine Datei (Texte aus dem Sprachpaket oder deutsch)</span><?php else: ?>✓ <?= (int) $n ?> Texte<?php endif; ?></td>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table></div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<form method="post" class="card" style="max-width:860px;margin-bottom:16px"><?= csrf_field() ?><input type="hidden" name="action" value="save">
|
|
<h3>Einstellungen</h3>
|
|
<label for="default_language">Standardsprache (für alle, die noch keine Flagge gewählt haben)</label>
|
|
<select id="default_language" name="default_language" style="max-width:260px"><?php foreach ($langs as $code => $l): ?><option value="<?= e($code) ?>" <?= i18n_default() === $code ? 'selected' : '' ?>><?= e($l['name']) ?></option><?php endforeach; ?></select>
|
|
<label style="display:flex;gap:8px;align-items:flex-start;margin-top:12px;font-weight:400"><input type="checkbox" name="i18n_collect" value="1" style="width:auto;margin-top:3px" <?= $collect ? 'checked' : '' ?>>
|
|
<span><strong>Sammelmodus für Übersetzer:</strong> merkt sich die Texte aller besuchten Seiten (auch Kundenportal und Mitarbeiterbereich). Klicken Sie die Seiten durch, laden Sie danach die Vorlage. Bitte nach dem Sammeln wieder ausschalten, er verlangsamt die Seiten etwas.</span></label>
|
|
<div class="btn-row"><button class="btn" type="submit">Speichern</button></div>
|
|
</form>
|
|
|
|
<div class="card" style="max-width:860px;margin-bottom:16px">
|
|
<h3>Gesammelte Texte: <?= $collected ?></h3>
|
|
<p class="muted" style="font-size:.88rem">Das sind deutsche Texte, die der Sammelmodus gesehen hat. Sie bilden die Vorlage für ein neues Sprachpaket.</p>
|
|
<?php if ($collected): ?><form method="post" onsubmit="return confirm('Gesammelte Texte löschen?')"><?= csrf_field() ?><input type="hidden" name="action" value="clear"><button class="btn secondary" type="submit">Alle löschen</button></form><?php endif; ?>
|
|
</div>
|
|
|
|
<div class="card" style="max-width:860px">
|
|
<h3>Eigene Sprache als Plugin erstellen</h3>
|
|
<ol style="line-height:1.7;padding-left:20px;font-size:.92rem">
|
|
<li>Sammelmodus einschalten, die Seiten durchklicken, dann bei Deutsch „Leere Vorlage laden“ (alle gesammelten Texte, noch ohne Übersetzung). Für Ergänzungen zu einer vorhandenen Sprache bei dieser „Vorlage laden“: bereits Übersetztes ist eingetragen.</li>
|
|
<li>In der Datei <code>strings.json</code> die Werte übersetzen. Der Schlüssel ist der deutsche Text, <code>{}</code> steht für wechselnde Teile („Willkommen, {}“ → „Welcome, {}“).</li>
|
|
<li>Ordner mit diesen Dateien packen: <code>plugin.json</code>, <code>plugin.php</code> (kann leer sein), <code>flag.svg</code> (Flagge, höchstens 40 KB), <code>strings.json</code>.</li>
|
|
<li>Die <code>plugin.json</code> sieht so aus: <code>{"name":"Français","version":"1.0.0","entry":"plugin.php","language":{"code":"fr","name":"Français","flag":"flag.svg","strings":"strings.json"}}</code></li>
|
|
<li>ZIP unter Plugins hochladen und aktivieren (das Sprachpaket braucht keine Berechtigungen). Die Flagge erscheint sofort.</li>
|
|
</ol>
|
|
</div>
|
|
<?php require __DIR__ . '/_footer.php'; ?>
|