Files
Multi-Debrid-Downloader/tests/i18n.test.ts
T
Sucukdeluxe 808c2a432c release: harden archive recovery and lifecycle for v2.0.63
Corroborate CRC failures across extraction backends, retry only implicated multipart volumes, and distinguish corruption, missing volumes, I/O failures, and wrong passwords across native, Zip4j, and JBinding paths.

Make disk retries generation-safe, preserve selective run scopes and cooldowns, protect shared output files during cleanup, validate manual extraction batches atomically, and restore interrupted integrity work safely.

Prioritize active package operations in the UI, strengthen extraction IPC validation, compile the JVM sidecar before release builds, and verify shipped JVM resources byte-for-byte in every Windows artifact.
2026-08-23 03:56:24 +02:00

277 lines
16 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import { normalizeLanguage, translateUiText } from "../src/renderer/i18n";
describe("renderer localization", () => {
it("falls back to English", () => {
expect(normalizeLanguage(undefined)).toBe("en");
expect(normalizeLanguage("fr")).toBe("en");
expect(normalizeLanguage("de")).toBe("de");
});
it("translates exact interface labels in both directions", () => {
expect(translateUiText("Einstellungen speichern", "en")).toBe("Save settings");
expect(translateUiText("Save settings", "de")).toBe("Einstellungen speichern");
expect(translateUiText("Passwort/Zugang", "en")).toBe("Password/access");
expect(translateUiText("Animationen", "en")).toBe("Animations");
expect(translateUiText("Animations", "de")).toBe("Animationen");
});
it.each([
["Erfolgsmeldungen senden", "Send success notifications"],
["Gesammelt (alle 2 Minuten)", "Grouped (every 2 minutes)"],
["Jedes Paket einzeln", "Each package individually"],
["Melden, wenn der gesamte Lauf fertig ist", "Notify when the entire run completes"],
["Melden, wenn die Restmenge unterschritten wird", "Notify when the remaining amount falls below the threshold"],
["Restmengenschwelle (GB)", "Remaining amount threshold (GB)"],
["Melden, wenn Downloads stillstehen", "Notify when downloads stall"],
["Stillstand bestätigen nach (Sek.)", "Confirm stall after (sec.)"],
["Frühestens erneut melden nach (Min.)", "Notify again after at least (min.)"],
["Melden, wenn Downloads wieder laufen", "Notify when downloads resume"]
])("translates notification center setting %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it("translates dynamic update and pagination text", () => {
expect(translateUiText("v2.0.14 ist verfügbar. Installierte Version: 2.0.13.", "en"))
.toBe("v2.0.14 is available. Installed version: 2.0.13.");
expect(translateUiText("146 von 46", "en")).toBe("146 of 46");
expect(translateUiText("100.001100.005 von 100.005", "en")).toBe("100.001100.005 of 100.005");
expect(translateUiText("100.001100.005 of 100.005", "de")).toBe("100.001100.005 von 100.005");
});
it.each([
["Finalisieren - 0%", "Finalizing - 0%"],
["Finalisieren - 50%", "Finalizing - 50%"],
["Finalisieren - 100%", "Finalizing - 100%"]
])("translates compact finalization progress %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["CRC-Check läuft", "CRC check running"],
["Entpacken - Ausstehend", "Extracting - Pending"],
["Entpacken - Warten auf Parts", "Extracting - Waiting for parts"],
["Archive stabilisieren...", "Stabilizing archives..."],
["Entpacken vorbereiten...", "Preparing extraction..."],
["Entpacken wird neu gestartet...", "Restarting extraction..."],
["Nested Entpacken...", "Nested extraction..."],
["Umbenennen...", "Renaming..."],
["Tonspur...", "Audio track..."],
["Aufräumen...", "Cleaning up..."],
["Verschiebe Videos...", "Moving videos..."]
])("translates package runtime state %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Entpacken - 42%", "Extracting - 42%"],
["Passwort gefunden", "Password found"],
["Passwort knacken: 50% (2/4)", "Cracking password: 50% (2/4)"],
["Entpacken (1/3) - Nächstes Archiv...", "Extracting (1/3) - Next archive..."]
])("translates compact runtime text %s for visible and attribute surfaces", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
expect(translateUiText(`Bereit · ${german}`, "en")).toBe(`Ready · ${english}`);
expect(translateUiText(`Ready · ${english}`, "de")).toBe(`Bereit · ${german}`);
});
it("translates the complete history surface including status values", () => {
const translations = new Map([
["Alle Einträge", "All entries"],
["Heute", "Today"],
["Letzte 7 Tage", "Last 7 days"],
["Älter", "Older"],
["Gelöscht", "Deleted"],
["Fehlgeschlagen", "Failed"],
["Verlauf leeren", "Clear history"],
["Gesamtverlauf löschen", "Delete all history"],
["Erneut hinzufügen", "Add again"],
["Im Ordner zeigen", "Show in folder"],
["Auswahl löschen", "Clear selection"],
["Noch kein Verlauf", "No history yet"],
["Keine passenden Einträge", "No matching entries"]
]);
for (const [german, english] of translations) {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
}
});
it("translates dynamic queue and availability values", () => {
expect(translateUiText("26/26 online", "en")).toBe("26/26 online");
expect(translateUiText("3 abgebrochen", "en")).toBe("3 cancelled");
expect(translateUiText("Geplant: 18:30", "en")).toBe("Scheduled: 18:30");
expect(translateUiText("Einträge: 2", "en")).toBe("Entries: 2");
expect(translateUiText("Sichtbar: 2", "en")).toBe("Visible: 2");
expect(translateUiText("Ausgewählt: 0", "en")).toBe("Selected: 0");
expect(translateUiText("Auswahl übergeben (3)", "en")).toBe("Send selection (3)");
expect(translateUiText("Alle übergeben (16)", "en")).toBe("Send all (16)");
expect(translateUiText("Paket SBS14HD auswählen", "en")).toBe("Select package SBS14HD");
expect(translateUiText("16 Dateien", "en")).toBe("16 files");
expect(translateUiText("15/16 geprüft", "en")).toBe("15/16 checked");
expect(translateUiText("2 pro Seite", "en")).toBe("2 per page");
expect(translateUiText("Sichtbar: ", "en")).toBe("Visible: ");
expect(translateUiText(" pro Seite", "en")).toBe(" per page");
expect(translateUiText("Soll die Sammlung Tab 2 mit 5 Link(s) wirklich entfernt werden?", "en"))
.toBe("Do you really want to remove collection Tab 2 with 5 link(s)?");
expect(translateUiText("Soll die leere Sammlung Tab 2 wirklich entfernt werden?", "en"))
.toBe("Do you really want to remove the empty collection Tab 2?");
expect(translateUiText("Link kopieren", "en")).toBe("Copy Link");
expect(translateUiText("example.test Klicken zum Kopieren", "en")).toBe("Click to copy example.test");
expect(translateUiText("Klicken zum Kopieren", "en")).toBe("Click to copy");
expect(translateUiText("Geprüft", "en")).toBe("Checked");
expect(translateUiText("gerade eben", "en")).toBe("just now");
expect(translateUiText("Noch unbekannte Dateigrößen: 3. Die tatsächliche Restmenge kann höher sein.", "en"))
.toBe("Unknown file sizes: 3. The actual remaining amount may be higher.");
expect(translateUiText("Geschwindigkeit verschieben", "en")).toBe("Move Speed");
expect(translateUiText("Geschwindigkeit nach links verschieben", "en")).toBe("Move Speed left");
expect(translateUiText("Move Speed right", "de")).toBe("Geschwindigkeit nach rechts verschieben");
expect(translateUiText("Verlaufsseiten", "en")).toBe("History pages");
expect(translateUiText("Vorherige Verlaufsseite", "en")).toBe("Previous history page");
expect(translateUiText("Nächste Verlaufsseite", "en")).toBe("Next history page");
expect(translateUiText("Zurück", "en")).toBe("Back");
expect(translateUiText("Vor", "en")).toBe("Next");
expect(translateUiText("Seite 2 von 7", "en")).toBe("Page 2 of 7");
expect(translateUiText("Page 2 of 7", "de")).toBe("Seite 2 von 7");
expect(translateUiText("Seite 1.000 von 2.500", "en")).toBe("Page 1.000 of 2.500");
expect(translateUiText("1 von 2 verfügbar", "en")).toBe("1 of 2 available");
expect(translateUiText("vor 2 Min.", "en")).toBe("2 min ago");
expect(translateUiText("Rate-Limit aktiv · 48 Sek.", "en")).toBe("Rate limit active · 48 sec");
expect(translateUiText("Rate limit active · 48 sec", "de")).toBe("Rate-Limit aktiv · 48 Sek.");
expect(translateUiText("Accountdaten werden aktualisiert.", "en")).toBe("Account data is being updated.");
expect(translateUiText("Keine passenden Dienste oder Zugangstypen gefunden.", "en"))
.toBe("No matching services or access types found.");
expect(translateUiText("https://example.test/a aus Sammlung A, Zeile 3 auswählen", "en"))
.toBe("Select https://example.test/a from Sammlung A, line 3");
expect(translateUiText("Select https://example.test/a from Sammlung A, line 3", "de"))
.toBe("https://example.test/a aus Sammlung A, Zeile 3 auswählen");
expect(translateUiText("abc••••\nKlicken zum Kopieren", "en")).toBe("Click to copy abc••••");
});
it.each([
["Verfügbarkeit", "Availability"],
["Hinzugefügt am", "Added on"],
["Ungeprüft", "Unchecked"],
["Paket gestoppt", "Package stopped"],
["Noch keine Downloads", "No downloads yet"],
["Füge Links hinzu, um den ersten Download zu starten.", "Add links to start the first download."],
["Neue Sammlung", "New collection"],
["Links erfassen", "Capture links"],
["Datei importieren", "Import file"],
["Sammlung verarbeiten", "Process collection"],
["An Downloads übergeben", "Send to downloads"],
["Keine passenden Links", "No matching links"],
["Noch keine Links", "No links yet"],
["Eine URL oder Rohzeile pro Zeile", "One URL or raw line per line"],
["Prüfen und speichern", "Check and save"],
["Accounts durchsuchen", "Search accounts"],
["Account-Typ filtern", "Filter account type"],
["Keine passenden Account-Typen.", "No matching account types."],
["Geschützter Zugang", "Protected access"],
["Immer erste Tonspur", "Always first audio track"],
["Keine Archive löschen", "Do not delete archives"],
["Sieben Tage", "Seven days"],
["Zeitraum", "Period"],
["Nicht verfügbar", "Unavailable"],
["Datenmenge", "Data volume"],
["Erfolgsquote", "Success rate"],
["Durchschnitt", "Average"],
["Live aus der aktuellen Renderer-Sitzung", "Live from the current renderer session"],
["Kontextmenü", "Context menu"],
["Die Oberfläche hat einen Fehler ausgelöst", "The interface encountered an error"],
["Oberfläche neu laden", "Reload interface"],
["Unbekannter Fehler", "Unknown error"],
["Details anzeigen", "Show details"],
["Details ausblenden", "Hide details"]
])("translates renderer text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Film.mkv auswählen", "Select Film.mkv"],
["Aktionen für Film.mkv", "Actions for Film.mkv"],
["RapidGator Zuordnung entfernen", "Remove RapidGator assignment"],
["RapidGator nach oben", "Move RapidGator up"],
["Geplant: Heute 22:15", "Scheduled: Today 22:15"],
["Tonspur: 2 OK · 1 ohne DE-Tag · ffmpeg fehlt · 3 Fehler", "Audio track: 2 OK · 1 without DE tag · ffmpeg missing · 3 errors"],
["4/8 fertig · 2 Fehler", "4/8 completed · 2 errors"],
["7 Entpackfehler · 1 Wiederholung", "7 extraction errors · 1 retry"],
["Link-Umwandlung erneut", "Retrying link conversion"],
["7 Entpackfehler · Link-Umwandlung erneut", "7 extraction errors · Retrying link conversion"],
["Download fertig · 1 Entpackfehler", "Download complete · 1 extraction error"],
["Jetzt entpacken (2)", "Extract now (2)"],
["1 Entpackfehler", "1 extraction error"],
["2 Wiederholungen", "2 retries"],
["Download fertig", "Download complete"],
["1 Fehler · 2 abgebrochen", "1 error · 2 cancelled"],
["1 Entpackfehler · 2 Fehler", "1 extraction error · 2 errors"],
["Warte auf Festplatte", "Waiting for disk"],
["Entpacken 52%", "Extracting 52%"],
["Fehlgeschlagen nach 3 Versuchen: HTTP 503 von https://host.test/a", "Failed after 3 attempts: HTTP 503 von https://host.test/a"],
["Update-Check fehlgeschlagen: ECONNRESET https://api.test/v1", "Update check failed: ECONNRESET https://api.test/v1"],
["Account geprüft — Premium bis 2027-01-01", "Account checked — Premium until 2027-01-01"],
["7 Link(s) zur Queue hinzugefügt", "7 link(s) added to the queue"],
["2 Paket(e), 5 Link(s) importiert", "2 package(s), 5 link(s) imported"],
["3 ausgewählt", "3 selected"],
["vor 4 Std", "4 hr ago"],
["Zeitregel 4", "Schedule rule 4"],
["1.5 GB von 10 GB übrig", "1.5 GB of 10 GB remaining"],
["Verbleibend", "Remaining"]
])("translates composed renderer text %s without changing its payload", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
"https://rapidgator.net/file/abc?token=secret",
"C:\\Downloads\\Film [1080p]\\video.mkv",
"video.GERMAN.DL.1080p.mkv",
"ECONNRESET at api.example.test:443",
"RapidGator API: quota=0"
])("keeps free technical content unchanged: %s", (value) => {
expect(translateUiText(value, "en")).toBe(value);
expect(translateUiText(value, "de")).toBe(value);
});
it.each([
["nicht gesetzt", "not set"],
["Schätzwert", "Estimate"],
["ist verfügbar. Installierte Version:", "is available. Installed version:"],
["Tageslimit erreicht. Neue Links wechseln auf den nächsten Hoster.", "Daily limit reached. New links will switch to the next hoster."],
["Der ausgewählte Real-Debrid-Account wurde nicht gefunden.", "The selected Real-Debrid account was not found."],
["Nur lokal", "Local only"],
["Name kopiert", "Name copied"],
["Link kopiert", "Link copied"],
["Bei \"überspringen\" wird nur das erneute Entpacken übersprungen - offene Downloads bleiben in der Queue.", "With \"skip\", only repeated extraction is skipped - open downloads remain in the queue."]
])("translates remaining renderer inventory text %s", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Debug-Server aktiv: 127.0.0.1:9345", "Debug server active: 127.0.0.1:9345"],
["Unbekannter Account-Typ: custom-provider", "Unknown account type: custom-provider"],
["RapidGator: Bitte Passwort eintragen.", "RapidGator: Enter a password."],
["Update-Download: 42% (42 MB / 100 MB)", "Update download: 42% (42 MB / 100 MB)"],
["Zwischenablage: 3 Link(s) erkannt", "Clipboard: 3 link(s) detected"],
["2/4 API-Keys deaktiviert.", "2/4 API keys disabled."],
["Account-Check: 3/4 Login gültig, 2 mit Premium.", "Account check: 3/4 logins valid, 2 with premium."],
["Aktive Accounts: 1/1 Login gültig, 1 mit Premium.", "Active accounts: 1/1 logins valid, 1 with premium."],
["Alle Accounts: 2/5 Login gültig, 2 mit Premium.", "All accounts: 2/5 logins valid, 2 with premium."],
["Soll RapidGator wirklich aus der Accountliste entfernt werden?", "Remove RapidGator from the account list?"],
["Konflikte gelöst: 2 überschrieben, 3 übersprungen", "Conflicts resolved: 2 overwritten, 3 skipped"],
["DLC importiert: 2 Paket(e), 5 Link(s)", "DLC imported: 2 package(s), 5 link(s)"],
["3 Fehler, 2 Warnungen (letzte 5)", "3 errors, 2 warnings (latest 5)"],
["Links für Sammlung 1 lokal erfassen.", "Capture links locally for Sammlung 1."],
["Film.mkv Klicken zum Kopieren", "Click to copy Film.mkv"]
])("translates additional composed renderer text %s", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
});