add localizations for englisch and german

This commit is contained in:
stefan.tollkuehn committed 2026-09-22 09:59:53 +02:00
1 parent 34df35d54c
commit a47ce3650b
1 file changed
+415 -87
+415 -87
View File
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="de">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jitsi Meeting öffnen</title>
<title>Jitsi Meeting</title>
<style>
:root {
@@ -110,7 +110,6 @@
}
@media (prefers-color-scheme: dark) {
body {
background: #181818;
color: #eee;
@@ -143,117 +142,373 @@
<body>
<div class="card">
<!-- NORMALER LAUNCHER -->
<div id="launcher" class="hidden">
<h1>Jitsi Meeting</h1>
<h1 data-i18n="launcher.title">Jitsi Meeting</h1>
<p>
Das Meeting <span class="room"><strong id="roomName"></strong></span> wird geöffnet.
<span data-i18n="launcher.meetingPrefix">
The meeting
</span>
<span class="room">
<strong id="roomName"></strong>
</span>
<span data-i18n="launcher.meetingSuffix">
will be opened.
</span>
</p>
<button id="openButton" class="button primary" type="button">Meeting öffnen</button>
<button id="openButton" class="button primary" type="button" data-i18n="launcher.openButton">
Open meeting
</button>
</div>
<!-- STARTVORGANG -->
<div id="starting" class="hidden">
<h1>Jitsi Meet wird gestartet …</h1>
<h1 data-i18n="starting.title">
Starting Jitsi Meet …
</h1>
<div class="status">
<span class="spinner"></span>
Der Jitsi Meet Desktop-Client wird geöffnet.
<span data-i18n="starting.status">
The Jitsi Meet desktop client is being opened.
</span>
</div>
</div>
<!-- FALLBACK -->
<div id="fallback" class="hidden">
<h1>Jitsi Meet konnte nicht geöffnet werden</h1>
<p>
Der Jitsi Meet Desktop-Client wurde möglicherweise nicht gestartet.
<h1 data-i18n="fallback.title">
Jitsi Meet could not be opened
</h1>
<p data-i18n="fallback.description">
The Jitsi Meet desktop client may not have been started.
</p>
<button id="retryButton" class="button primary" type="button">Erneut versuchen</button>
<button id="retryButton" class="button primary" type="button" data-i18n="fallback.retryButton">
Try again
</button>
<!-- Windows -->
<div id="windowsDownload" class="download hidden">
<h2>Jitsi Meet für Windows</h2>
<h2 data-i18n="downloads.windows.title">
Jitsi Meet for Windows
</h2>
<p>
Installiere den Jitsi Meet Desktop-Client und versuche anschließend erneut, das Meeting zu öffnen.
<p data-i18n="downloads.windows.description">
Install the Jitsi Meet desktop client and then try opening the
meeting again.
</p>
<a id="windowsLink" class="button secondary" target="_blank" rel="noopener noreferrer">Jitsi für Windows herunterladen</a>
<a id="windowsLink" class="button secondary" target="_blank" rel="noopener noreferrer" data-i18n="downloads.windows.button">
Download Jitsi for Windows
</a>
</div>
<!-- macOS -->
<div id="macosDownload" class="download hidden">
<h2>Jitsi Meet für macOS</h2>
<h2 data-i18n="downloads.macos.title">
Jitsi Meet for macOS
</h2>
<p>
Installiere den Jitsi Meet Desktop-Client und versuche anschließend erneut, das Meeting zu öffnen.
<p data-i18n="downloads.macos.description">
Install the Jitsi Meet desktop client and then try opening the
meeting again.
</p>
<a id="macosLink" class="button secondary" target="_blank" rel="noopener noreferrer">Jitsi für macOS herunterladen</a>
<a id="macosLink" class="button secondary" target="_blank" rel="noopener noreferrer" data-i18n="downloads.macos.button">
Download Jitsi for macOS
</a>
</div>
<!-- Linux / unbekannt -->
<div id="otherDownload" class="download hidden">
<h2>Jitsi Meet Desktop</h2>
<h2 data-i18n="downloads.other.title">
Jitsi Meet Desktop
</h2>
<p>
Für dein Betriebssystem ist kein Desktop-Download hinterlegt.
<p data-i18n="downloads.other.description">
No desktop download is configured for your operating system.
</p>
</div>
<!-- Browser -->
<div class="download">
<h2>Im Browser teilnehmen</h2>
<h2 data-i18n="browser.title">
Join in browser
</h2>
<p>
Du kannst dem Meeting auch direkt über deinen Browser beitreten.
<p data-i18n="browser.description">
You can also join the meeting directly in your browser.
</p>
<a id="browserLink" class="button secondary">Im Browser beitreten</a>
<a id="browserLink" class="button secondary" data-i18n="browser.button">
Join in browser
</a>
</div>
</div>
<!-- FEHLER -->
<div id="invalidRoom" class="hidden">
<h1>Kein Meeting angegeben</h1>
<h1 data-i18n="error.title">
No meeting specified
</h1>
<div class="error">
Bitte gib ein Meeting über den Parameter <code>?room=...</code> an.
<span data-i18n="error.description">
Please specify a meeting using the
</span>
<code>?room=...</code>
<span data-i18n="error.descriptionSuffix">
parameter.
</span>
</div>
</div>
</div>
<script>
"use strict";
/* KONFIGURATION */
/* ============================================================
* KONFIGURATION
* ============================================================
*/
const CONFIG = {
/*
* Deine Jitsi-Domain.
*
* Absichtlich fest eingetragen und nicht aus
* window.location.host übernommen.
* Your Jitsi Domain.
*/
jitsiHost: "meet.bnvb-ingenieure.de",
jitsiHost: "<YOURJITSIINSTANCEADDRESS>",
/*
* Timeout für den Versuch, den Desktop-Client
* zu starten.
* Timeout trying to start the jitsi-meet-electron client
*/
launchTimeout: 1750,
launchTimeout: 2000,
/* HIER DIE TATSÄCHLICHEN DOWNLOAD-URLS EINTRAGEN. */
/*
* Download URLs for jitsi-meet-electron client
*/
downloads: {
windows: "https://github.com/jitsi/jitsi-meet-electron/releases",
macos: "https://github.com/jitsi/jitsi-meet-electron/releases"
}
};
/* DOM-ELEMENTE */
/* ============================================================
* Translations
* ============================================================
*/
const TRANSLATIONS = {
en: {
pageTitle: "Jitsi Meeting",
launcher: {
title: "Jitsi Meeting",
meetingPrefix: "The meeting",
meetingSuffix: "will be joined.",
openButton: "Join meeting"
},
starting: {
title: "Starting Jitsi Meet …",
status: "The Jitsi Meet desktop client is being opened."
},
fallback: {
title: "Jitsi Meet could not be opened",
description: "The Jitsi Meet desktop client may not have been started.",
retryButton: "Try again"
},
downloads: {
windows: {
title: "Jitsi Meet for Windows",
description:
"Install the Jitsi Meet desktop client and then try opening the meeting again.",
button: "Download Jitsi for Windows"
},
macos: {
title: "Jitsi Meet for macOS",
description:
"Install the Jitsi Meet desktop client and then try opening the meeting again.",
button: "Download Jitsi for macOS"
},
other: {
title: "Jitsi Meet Desktop",
description:
"No desktop download is configured for your operating system."
}
},
browser: {
title: "Join in browser",
description:
"You can also join the meeting directly in your browser.",
button: "Join in browser"
},
error: {
title: "No meeting specified",
description: "Please specify a meeting using the",
descriptionSuffix: "parameter."
}
},
de: {
pageTitle: "Jitsi Meeting öffnen",
launcher: {
title: "Jitsi Meeting",
meetingPrefix: "Das Meeting",
meetingSuffix: "wird geöffnet.",
openButton: "An Meeting teilnehmen"
},
starting: {
title: "Jitsi Meet wird gestartet …",
status: "Der Jitsi Meet Desktop-Client wird geöffnet."
},
fallback: {
title: "Jitsi Meet konnte nicht geöffnet werden",
description:
"Der Jitsi Meet Desktop-Client wurde möglicherweise nicht gestartet.",
retryButton: "Erneut versuchen"
},
downloads: {
windows: {
title: "Jitsi Meet für Windows",
description:
"Installiere den Jitsi Meet Desktop-Client und versuche anschließend erneut, das Meeting zu öffnen.",
button: "Jitsi für Windows herunterladen"
},
macos: {
title: "Jitsi Meet für macOS",
description:
"Installiere den Jitsi Meet Desktop-Client und versuche anschließend erneut, das Meeting zu öffnen.",
button: "Jitsi für macOS herunterladen"
},
other: {
title: "Jitsi Meet Desktop",
description:
"Für dein Betriebssystem ist kein Desktop-Download hinterlegt."
}
},
browser: {
title: "Im Browser teilnehmen",
description:
"Du kannst dem Meeting auch direkt über deinen Browser beitreten.",
button: "Im Browser beitreten"
},
error: {
title: "Kein Meeting angegeben",
description:
"Bitte gib ein Meeting über den Parameter",
descriptionSuffix: "an."
}
}
};
/* ============================================================
* Detect language
* ============================================================
*/
function detectLanguage() {
const languages = navigator.languages || [navigator.language];
for (const language of languages) {
if (!language) {
continue;
}
const languageCode = language
.toLowerCase()
.split("-")[0];
if (TRANSLATIONS[languageCode]) {
return languageCode;
}
}
/*
* Englisch ist der Standard/Fallback.
*/
return "en";
}
const currentLanguage = detectLanguage();
const translation = TRANSLATIONS[currentLanguage];
/* ============================================================
* Apply translations to webpage
* ============================================================
*/
function applyTranslations() {
document.documentElement.lang = currentLanguage;
document.title = translation.pageTitle;
document
.querySelectorAll("[data-i18n]")
.forEach((element) => {
const key = element.dataset.i18n;
const value = getTranslation(key);
if (value !== null) {
element.textContent = value;
}
});
}
/*
* Übersetzung anhand eines Pfades lesen.
*
* Beispiel:
*
* "downloads.windows.title"
*
* ergibt:
*
* translation.downloads.windows.title
*/
function getTranslation(key) {
const parts = key.split(".");
let value = translation;
for (const part of parts) {
if (
value === null ||
value === undefined ||
typeof value !== "object" ||
!(part in value)
) {
return null;
}
value = value[part];
}
return typeof value === "string" ? value : null;
}
/* ============================================================
* DOM elements
* ============================================================
*/
const launcher = document.getElementById("launcher");
const starting = document.getElementById("starting");
const fallback = document.getElementById("fallback");
@@ -268,14 +523,13 @@
const windowsLink = document.getElementById("windowsLink");
const macosLink = document.getElementById("macosLink");
/*
* ============================================================
* ROOM AUS QUERY-PARAMETER
/* ============================================================
* GET ROOM FROM QUERY-PARAMETER
* ============================================================
*
* Beispiel:
*
* https://meet.bnvb-ingenieure.de/launch.html?room=abc-defg-hij
* https://<YOURJITSIINSTANCEADDRESS>/launch.html?room=abc-defg-hij
*
* ergibt:
*
@@ -283,9 +537,12 @@
*
* ============================================================
*/
function getRoom() {
const params = new URLSearchParams(window.location.search);
function getRoom() {
const params = new URLSearchParams(
window.location.search
);
const room = params.get("room");
if (!room) {
@@ -304,10 +561,8 @@
return trimmed;
}
/*
* ============================================================
* ROOM VALIDIEREN
/* ============================================================
* VALIDATE ROOM
* ============================================================
*
* Jitsi-Roomnamen können je nach Konfiguration relativ
@@ -315,10 +570,11 @@
*
* Wir verhindern hier lediglich Steuerzeichen und
* offensichtliche URL-Manipulation.
*
* ============================================================
*/
function isValidRoom(room) {
function isValidRoom(room) {
if (!room) {
return false;
}
@@ -343,19 +599,40 @@
return true;
}
/* JITSI WEB URL */
/* ============================================================
* JITSI WEB URL
* ============================================================
*/
function createBrowserUrl(room) {
return ("https://" + CONFIG.jitsiHost + "/" + encodeURIComponent(room));
return (
"https://" +
CONFIG.jitsiHost +
"/" +
encodeURIComponent(room)
);
}
/* JITSI DESKTOP PROTOCOL URL */
/* ============================================================
* JITSI DESKTOP PROTOCOL URL
* ============================================================
*/
function createAppUrl(room) {
return ("jitsi-meet://" + CONFIG.jitsiHost + "/" + encodeURIComponent(room));
return (
"jitsi-meet://" +
CONFIG.jitsiHost +
"/" +
encodeURIComponent(room)
);
}
/* BETRIEBSSYSTEM ERKENNEN */
function detectOperatingSystem() {
/* ============================================================
* DETECT OPERATING SYSTEM
* ============================================================
*/
function detectOperatingSystem() {
const ua = navigator.userAgent;
/* iPhone / iPad / iPod */
@@ -380,9 +657,7 @@
}
/* macOS */
if (
/Macintosh|Mac OS X/i.test(ua)
) {
if (/Macintosh|Mac OS X/i.test(ua)) {
return "macos";
}
@@ -394,9 +669,12 @@
return "unknown";
}
/* BROWSER ERKENNEN */
function detectBrowser() {
/* ============================================================
* BROWSER ERKENNEN
* ============================================================
*/
function detectBrowser() {
const ua = navigator.userAgent;
if (/Edg\//i.test(ua)) {
@@ -425,7 +703,11 @@
return "unknown";
}
/* DOWNLOADS */
/* ============================================================
* DOWNLOADS
* ============================================================
*/
function configureDownloads() {
const os = detectOperatingSystem();
@@ -451,15 +733,24 @@
otherDownload.classList.remove("hidden");
}
/* FALLBACK ANZEIGEN */
/* ============================================================
* FALLBACK ANZEIGEN
* ============================================================
*/
function showFallback() {
starting.classList.add("hidden");
launcher.classList.add("hidden");
fallback.classList.remove("hidden");
configureDownloads();
}
/* JITSI DESKTOP STARTEN */
/* ============================================================
* JITSI DESKTOP STARTEN
* ============================================================
*/
function launchJitsi() {
const room = getRoom();
@@ -470,10 +761,14 @@
const protocolUrl = createAppUrl(room);
const browserUrl = createBrowserUrl(room);
/* Browser-Fallback vorbereiten. */
/*
* Browser-Fallback vorbereiten.
*/
browserLink.href = browserUrl;
/* UI auf "Startvorgang" setzen. */
/*
* UI auf "Startvorgang" setzen.
*/
launcher.classList.add("hidden");
fallback.classList.add("hidden");
starting.classList.remove("hidden");
@@ -481,13 +776,16 @@
let completed = false;
let timeout = null;
/* Unsichtbarer iframe für den Protocol Handler. */
/*
* Unsichtbarer iframe für den Protocol Handler.
*/
const iframe = document.createElement("iframe");
iframe.style.display = "none";
/* Aufräumen. */
/*
* Aufräumen.
*/
function cleanup() {
if (completed) {
return;
}
@@ -517,21 +815,23 @@
document.body.appendChild(iframe);
/* Protocol Handler starten. */
/*
* Protocol Handler starten.
*/
try {
iframe.src = protocolUrl;
} catch (error) {
console.error(
"Fehler beim Öffnen des Jitsi Protocol Handlers:",
"Error opening the Jitsi Protocol Handler:",
error
);
cleanup();
showFallback();
return;
}
/*
* --------------------------------------------------------
* FALLBACK
@@ -554,41 +854,53 @@
*/
timeout = setTimeout(() => {
if (completed) {
return;
}
cleanup();
showFallback();
}, CONFIG.launchTimeout);
}
/* INITIALISIERUNG */
function initialize() {
/* ============================================================
* INITIALISIERUNG
* ============================================================
*/
function initialize() {
const room = getRoom();
/* Kein room-Parameter */
/*
* Kein room-Parameter
*/
if (!isValidRoom(room)) {
invalidRoom.classList.remove("hidden");
return;
}
/* Room anzeigen. */
/*
* Room anzeigen.
*/
roomName.textContent = room;
/* Browser-Fallback vorbereiten. */
/*
* Browser-Fallback vorbereiten.
*/
browserLink.href = createBrowserUrl(room);
/* Launcher anzeigen. */
/*
* Launcher anzeigen.
*/
launcher.classList.remove("hidden");
/* Debug-Informationen. */
/*
* Debug-Informationen.
*/
console.log(
"Jitsi Launcher",
{
language: currentLanguage,
room: room,
operatingSystem: detectOperatingSystem(),
browser: detectBrowser(),
@@ -598,11 +910,27 @@
);
}
/* EVENTS */
openButton.addEventListener("click", launchJitsi);
retryButton.addEventListener("click", launchJitsi);
/* ============================================================
* EVENTS
* ============================================================
*/
/* Start */
openButton.addEventListener(
"click",
launchJitsi
);
retryButton.addEventListener(
"click",
launchJitsi
);
/* ============================================================
* START
* ============================================================
*/
applyTranslations();
initialize();
</script>
</body>