Files
2026-10-28 07:54:52 +00:00

103 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Prüft die Übersetzungen, die Plugins selbst mitbringen (lang/<Sprachcode>.json): Sie gelten nur für aktive Plugins und nur in der
* gewählten Sprache, das Sprachpaket hat bei gleichem Text Vorrang, Muster mit {} funktionieren, ungültige Dateien stören nicht,
* und die Übersicht unter Sprachen kennt sie.
*/
$pluginDir = sys_get_temp_dir() . '/deskhub-i18n-pl-' . bin2hex(random_bytes(4));
mkdir($pluginDir, 0755, true);
putenv('DESKHUB_PLUGINS_DIR=' . $pluginDir);
putenv('DESKHUB_TESTING=1');
putenv('DESKHUB_CATALOG_DEFAULTS=' . __DIR__ . '/fixtures/no-catalog.php');
putenv('DESKHUB_SQLITE_PATH=:memory:');
require_once dirname(__DIR__) . '/includes/bootstrap.php';
$failures = [];
$check = function (bool $ok, string $what) use (&$failures): void { if (!$ok) $failures[] = $what; };
$pdo = get_db();
$mkZip = function (array $files): string {
$path = sys_get_temp_dir() . '/plp-' . bin2hex(random_bytes(3)) . '.zip';
$z = new ZipArchive();
$z->open($path, ZipArchive::CREATE);
foreach ($files as $name => $content) $z->addFromString($name, $content);
$z->close();
return $path;
};
$flag = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="#009246"/></svg>';
// Sprachpaket "xx" mit einem Text, der auch im Plugin steht (Sprachpaket gewinnt)
$pack = $mkZip([
'plugin.json' => json_encode(['slug' => 'lang-xx', 'name' => 'Testsprache', 'version' => '1.0.0', 'entry' => 'plugin.php', 'permissions' => [], 'language' => ['code' => 'xx', 'name' => 'Testsprache', 'flag' => 'flag.svg', 'strings' => 'strings.json']]),
'plugin.php' => "<?php\n",
'flag.svg' => $flag,
'strings.json' => json_encode(['Gemeinsamer Text' => 'Shared (pack)', 'Nur im Sprachpaket' => 'Only in pack'], JSON_UNESCAPED_UNICODE),
]);
$check(plugin_install_zip($pdo, $pack)['ok'] && plugin_set_active($pdo, 'lang-xx', true, [])['ok'], 'Sprachpaket installiert und aktiv');
// Plugin mit eigenen Übersetzungen
$mkPlugin = function (string $slug, array $langFiles) use ($mkZip, $pdo): array {
$files = [
'plugin.json' => json_encode(['slug' => $slug, 'name' => 'Plugin ' . $slug, 'version' => '1.0.0', 'entry' => 'plugin.php', 'permissions' => []]),
'plugin.php' => "<?php\n",
];
foreach ($langFiles as $name => $content) $files['lang/' . $name] = $content;
return plugin_install_zip($pdo, $mkZip($files));
};
$strs = fn(array $a) => json_encode($a, JSON_UNESCAPED_UNICODE);
$r1 = $mkPlugin('probe-a', [
'xx.json' => $strs(['_info' => 'Test', 'Plugin-Text' => 'Plugin text', 'Gemeinsamer Text' => 'Shared (plugin)', '{#} Treffer im Plugin' => '{} hits in plugin', 'Hallo, {}' => 'Hi, {}']),
'yy.json' => $strs(['Plugin-Text' => 'Texte du plugin']),
'Kaputt.json' => '{}',
]);
$r2 = $mkPlugin('probe-b', ['xx.json' => '{ das ist kein JSON', 'zz.json' => $strs(['Nur b' => 'Only b'])]);
$check($r1['ok'] && $r2['ok'], 'Test-Plugins lassen sich installieren: ' . json_encode([$r1, $r2]));
$tr = fn(string $html, string $code = 'xx') => i18n_translate_html($html, $code);
// Solange das Plugin nicht aktiv ist, gilt seine Datei nicht
i18n_languages(true);
$check(!str_contains($tr('<p>Plugin-Text</p>'), 'Plugin text'), 'inaktives Plugin: seine Übersetzungen gelten nicht');
$check(plugin_set_active($pdo, 'probe-a', true, [])['ok'], 'Plugin lässt sich aktivieren');
$check(plugin_set_active($pdo, 'probe-b', true, [])['ok'], 'zweites Plugin lässt sich aktivieren');
// Wörterbuch direkt aufbauen (i18n_dictionary() merkt sich je Anfrage ein Ergebnis)
$dict = ['exact' => [], 'patterns' => [], 'count' => 0];
foreach (i18n_plugin_string_files('xx') as $f) i18n_load_strings_file($f, $dict);
i18n_load_strings_file((string) (i18n_languages(true)['xx']['strings'] ?? ''), $dict);
$check(isset($dict['exact']['Plugin-Text']) && $dict['exact']['Plugin-Text'] === 'Plugin text', 'Text aus lang/xx.json eines aktiven Plugins wird geladen');
$check(($dict['exact']['Gemeinsamer Text'] ?? '') === 'Shared (pack)', 'Bei gleichem Text gewinnt das Sprachpaket');
$check(($dict['exact']['Nur im Sprachpaket'] ?? '') === 'Only in pack', 'Texte nur im Sprachpaket bleiben erhalten');
$check(i18n_lookup('7 Treffer im Plugin', $dict) === '7 hits in plugin', 'Muster mit {#} aus dem Plugin funktioniert: ' . var_export(i18n_lookup('7 Treffer im Plugin', $dict), true));
$check(i18n_lookup('Hallo, Anna', $dict) === 'Hi, Anna', 'Muster mit {} aus dem Plugin funktioniert');
$check(!isset($dict['exact']['_info']), 'Schlüssel mit Unterstrich („_info“) werden ignoriert');
$files = i18n_plugin_string_files('xx');
$check(array_keys($files) === ['probe-a', 'probe-b'] && ($dict['count'] ?? 0) >= 5, 'beide Plugins liefern ihre Datei; die kaputte Datei von probe-b stört nicht');
$check(i18n_plugin_string_files('yy') === ['probe-a' => $pluginDir . '/probe-a/lang/yy.json'], 'eine andere Sprache bekommt nur die passenden Dateien');
$check(i18n_plugin_string_files('de') === [] && i18n_plugin_string_files('../etc') === [] && i18n_plugin_string_files('XX') === [], 'Deutsch, ungültige Codes und Großbuchstaben liefern nichts');
// Übersicht unter Sprachen
$ov = i18n_plugin_language_overview();
$check(($ov['probe-a']['xx'] ?? null) === 4 && ($ov['probe-a']['yy'] ?? null) === 1 && ($ov['probe-b']['xx'] ?? null) === 0 && ($ov['probe-b']['zz'] ?? null) === 1, 'Übersicht zählt die Texte je Plugin und Sprache: ' . json_encode($ov));
$check(!isset($ov['probe-a']['Kaputt']) && !isset($ov['probe-a']['kaputt']), 'Dateien mit ungültigem Sprachcode im Namen werden nicht aufgeführt');
// Plugin deaktivieren: Datei gilt nicht mehr
plugin_set_active($pdo, 'probe-a', false, []);
$check(array_keys(i18n_plugin_string_files('xx')) === ['probe-b'], 'deaktiviertes Plugin liefert keine Übersetzungen mehr');
// Fertige Seite: Plugin-Text und Muster werden übersetzt
plugin_set_active($pdo, 'probe-a', true, []);
i18n_dictionary('xx', true);
$page = $tr('<h1>Plugin-Text</h1><p>Hallo, Anna</p><p>Gemeinsamer Text</p><p>Nicht übersetzt</p>');
$check(str_contains($page, '<h1>Plugin text</h1>') && str_contains($page, 'Hi, Anna') && str_contains($page, 'Shared (pack)') && str_contains($page, 'Nicht übersetzt'), 'fertige Seite wird mit den Texten der Plugins übersetzt: ' . $page);
foreach (scandir($pluginDir) ?: [] as $entry) {
if ($entry === '.' || $entry === '..') continue;
is_dir($pluginDir . '/' . $entry) ? plugin_rmdir($pluginDir . '/' . $entry) : @unlink($pluginDir . '/' . $entry);
}
@rmdir($pluginDir);
if ($failures) {
fwrite(STDERR, "Fehlgeschlagen:\n - " . implode("\n - ", $failures) . "\n");
exit(1);
}
echo "Plugin language files checks passed.\n";