.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 = ''; // 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' => " $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' => " $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('
Plugin-Text
'), '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('Hallo, Anna
Gemeinsamer Text
Nicht übersetzt
'); $check(str_contains($page, '