feat(website): implement custom i18n static compiler & full 6-language expansion
- Added pure Node.js dynamic i18n static site generator (build.js). - Structured locales for English, German, French, Spanish, Brazilian Portuguese, and Russian. - Replaced two-state toggle with premium glassmorphic language select dropdown. - Integrated robust segment-based locale routing with safe dynamic fallbacks for legal and invite pages. - Audited Core Web Vitals (LCP preloads, CLS dimensions) and SEO structures (robots, sitemap). - Added dedicated Localization section to README and created contributor TRANSLATION guide.
@@ -90,8 +90,17 @@ To connect your extension to a self-hosted server, open the popup → **Room** t
|
||||
|
||||
---
|
||||
|
||||
### 🌐 Localization & Translations
|
||||
|
||||
The official KoalaSync website features a custom static site compiler to offer seamless localization:
|
||||
- **Available Languages**: Manually verified languages (English, German) and auto-generated variants ready for review (French, Spanish, Brazilian Portuguese, and Russian).
|
||||
- **Contributing**: We welcome community translations! Please refer directly to the [TRANSLATION.md](website/TRANSLATION.md) file for step-by-step instructions on how to review auto-generated translations or contribute support for new languages.
|
||||
|
||||
---
|
||||
|
||||
### 📖 Documentation & Links
|
||||
|
||||
- **[TRANSLATION.md](website/TRANSLATION.md)**: Translation and localization guide for contributors.
|
||||
- **[PRIVACY.md](PRIVACY.md)**: Data Handling and Privacy Policy.
|
||||
- **[CONTRIBUTING.md](CONTRIBUTING.md)**: How to help make KoalaSync better.
|
||||
- **[HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md)**: Step-by-step walkthrough of the complete user flow.
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# KoalaSync Translation & Localization Guide
|
||||
|
||||
This guide describes how the localization system works for the KoalaSync website and provides step-by-step instructions on how a developer or an AI agent should add support for a new language.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The KoalaSync website uses a custom, zero-dependency static site generator to compile localized pages from a single template:
|
||||
- **Template Source**: `/website/template.html` (single source of truth).
|
||||
- **Locales Source**: `/website/locales/[lang].json` (language dictionaries).
|
||||
- **Build Pipeline**: `/website/build.js` (compiles pages into `/website/www/`).
|
||||
|
||||
---
|
||||
|
||||
## Supported Languages
|
||||
|
||||
> [!NOTE]
|
||||
> **Contributor Guideline: Translation Quality Distinction**
|
||||
> To maintain the highest standard of UX and accessibility, KoalaSync categorizes languages into two tiers. Core languages (`en` and `de`) are manually translated and verified by native speakers. Extended languages (`fr` and `es`) are currently machine-translated to broaden accessibility, and need native review. Future contributors are encouraged to audit "Auto-Generated" translations and submit PRs to elevate them to "Verified" status.
|
||||
|
||||
The following table provides an overview of all currently active languages on the KoalaSync platform:
|
||||
|
||||
| Language Code | Language Name | Status |
|
||||
| :--- | :--- | :--- |
|
||||
| `en` | English | 100% Manually Verified |
|
||||
| `de` | German | 100% Manually Verified |
|
||||
| `fr` | French | Auto-Generated (May contain errors / Needs Native Speaker Review) |
|
||||
| `es` | Spanish | Auto-Generated (May contain errors / Needs Native Speaker Review) |
|
||||
| `pt-BR` | Portuguese (Brasil) | Auto-Generated (May contain errors / Needs Native Speaker Review) |
|
||||
| `ru` | Russian | Auto-Generated (May contain errors / Needs Native Speaker Review) |
|
||||
|
||||
> [!WARNING]
|
||||
> **Autogeneration Rule**
|
||||
> Any future languages added to the static site generator (e.g., Italian, Dutch) MUST be marked as `"Auto-Generated (May contain errors / Needs Native Speaker Review)"` in this table until a native speaker manually reviews and signs off on the translations.
|
||||
|
||||
---
|
||||
|
||||
## Strict Legal Exclusion Rule
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **DO NOT TRANSLATE LEGAL PAGES**
|
||||
> The imprint and privacy pages ([impressum.html](file:///Users/koala/Documents/KoalaPlay/website/impressum.html) and [datenschutz.html](file:///Users/koala/Documents/KoalaPlay/website/datenschutz.html)) **MUST NOT** be translated into any other languages.
|
||||
> They are strictly restricted to **English** and **German** only.
|
||||
>
|
||||
> **Rationale:** Legal compliance and liability under European Union (GDPR) and German (DDG) laws. Offering legal notices in auto-generated languages introduces risks of mistranslations that could be legally binding or misrepresent liabilities.
|
||||
>
|
||||
> **Technical Fallback:** `lang-init.js` is configured to automatically fall back to **English** for these pages if the user's active preference is French, Spanish, or any other unsupported language, ensuring they see legally verified text while preserving their language state when returning home.
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step: Adding a New Language
|
||||
|
||||
Follow this exact workflow to add a new language (for example, Italian - `it`):
|
||||
|
||||
### Step 1: Create the Translation Dictionary
|
||||
Create a new JSON file inside `/website/locales/` named `[lang].json` (e.g., `/website/locales/it.json`).
|
||||
- Copy `/website/locales/en.json` to act as your baseline template.
|
||||
- Translate all key values while preserving key names.
|
||||
- Update the system configuration keys at the top of the file:
|
||||
```json
|
||||
{
|
||||
"LANG_CODE": "it",
|
||||
"HTML_CLASS": "lang-it",
|
||||
"CANONICAL_PATH": "it/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Register the Language in the Build Script
|
||||
Open `/website/build.js` and simply append the new language code to the `languages` array:
|
||||
```javascript
|
||||
const languages = ['en', 'de', 'fr', 'es', 'pt-BR', 'ru', 'it'];
|
||||
```
|
||||
The dynamic compiler loop will automatically load your JSON dictionary, create `/website/www/it/`, and compile `/website/www/it/index.html` with correct sitemaps, canoncials, and relative assets.
|
||||
|
||||
### Step 3: Run Compilation
|
||||
Run the build script from the repository root:
|
||||
```bash
|
||||
node website/build.js
|
||||
```
|
||||
Verify the output is generated inside `/website/www/[lang]/index.html`.
|
||||
|
||||
### Step 4: Update this Guide
|
||||
Add the new language entry to the **Supported Languages** table above with the appropriate status marking.
|
||||
|
||||
---
|
||||
|
||||
## Future Architecture: Dynamic Utility Pages
|
||||
|
||||
For dynamic utility pages like `join.html`, we need to support unlimited languages in the future under a strict architectural constraint: **the share link URL must never contain language path details or query parameters.**
|
||||
|
||||
### Proposed Client-Side i18n Architecture
|
||||
|
||||
To achieve this without bloating the HTML DOM with duplicate text nodes for every language (which leads to `display: none` sprawl), we propose an **asynchronous JSON dictionary injection architecture**:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Guest as Guest Browser
|
||||
participant JS as lang-init.js (Sync)
|
||||
participant DOM as i18n-client.js (Async)
|
||||
participant Server as Static Web Server
|
||||
|
||||
Guest->>JS: Enters join.html#join:roomID
|
||||
JS->>JS: Read localStorage & navigator.language
|
||||
JS->>JS: Resolve activeLang (e.g., "es")
|
||||
JS->>Guest: Apply html.lang="es" & lang-es class
|
||||
Guest->>DOM: Page elements render with data-i18n attributes
|
||||
DOM->>Server: fetch("/locales/es.json") asynchronously
|
||||
Server-->>DOM: Return JSON dictionary
|
||||
DOM->>DOM: Scan DOM for data-i18n & replace textContent
|
||||
DOM->>Guest: Fully localized UI shown seamlessly
|
||||
```
|
||||
|
||||
#### 1. Markup Definition (Semantic Tags)
|
||||
The HTML file `join.html` will contain only generic, language-independent tags with data attributes for translation keys. English text is placed as a native placeholder fallback:
|
||||
```html
|
||||
<h1 data-i18n="JOIN_TITLE">Ready to sync?</h1>
|
||||
<p id="join-desc" data-i18n="JOIN_SUBTITLE">You've been invited to join a session.</p>
|
||||
```
|
||||
|
||||
#### 2. Client-Side i18n Engine (`i18n-client.js`)
|
||||
We will create a lightweight client-side translation engine that executes asynchronously on page load:
|
||||
```javascript
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// 1. Recover localized preference determined by lang-init.js
|
||||
const activeLang = document.documentElement.lang || 'en';
|
||||
|
||||
// 2. Fetch the corresponding locale JSON file asynchronously
|
||||
try {
|
||||
const response = await fetch(`locales/${activeLang}.json`);
|
||||
if (!response.ok) throw new Error('Locale not found');
|
||||
const dictionary = await response.json();
|
||||
|
||||
// 3. Update DOM elements carrying data-i18n attribute
|
||||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
if (dictionary[key]) {
|
||||
// If it is an image, update alt text instead
|
||||
if (el.tagName === 'IMG') {
|
||||
el.alt = dictionary[key];
|
||||
} else {
|
||||
el.textContent = dictionary[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('i18n dynamic load failed, falling back to English defaults:', err);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Advantages of this Approach
|
||||
1. **Zero URL Contamination**: The share link remains clean (e.g., `/join.html#join:room:pass`), ensuring absolute anonymity and avoiding hardcoding the sender's language onto the receiver.
|
||||
2. **Minimal DOM Footprint**: Eliminates duplicate `<span lang="de">`, `<span lang="en">` blocks entirely, reducing page size by 50% and eliminating slow style recalculations.
|
||||
3. **Infinite Scale**: Support for new languages (e.g., Italian, Japanese) requires zero modifications to `join.html`. The client simply downloads the appropriate locale JSON file asynchronously on demand.
|
||||
|
||||
@@ -310,7 +310,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const updateDynamicVersion = async () => {
|
||||
try {
|
||||
const response = await fetch('version.json');
|
||||
const versionPath = document.documentElement.lang === 'de' ? '../version.json' : 'version.json';
|
||||
const response = await fetch(versionPath);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
const { version, date } = data;
|
||||
@@ -443,41 +444,102 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Language Selection Umschalter
|
||||
const toggleLanguage = (e) => {
|
||||
if (e) e.preventDefault();
|
||||
// Dynamically localize home links on root dynamic pages (impressum, datenschutz, join)
|
||||
const localizeHomeLinks = () => {
|
||||
const html = document.documentElement;
|
||||
const currentIsEnglish = html.classList.contains('lang-en');
|
||||
const newLang = currentIsEnglish ? 'de' : 'en';
|
||||
html.classList.remove('lang-en', 'lang-de');
|
||||
html.classList.add('lang-' + newLang);
|
||||
html.lang = newLang;
|
||||
localStorage.setItem('koala_lang', newLang);
|
||||
|
||||
// Update titles dynamically based on page
|
||||
var path = window.location.pathname;
|
||||
var isIndex = path === '/' || path.endsWith('index.html') || path.split('/').pop() === '';
|
||||
var isJoin = path.includes('join');
|
||||
|
||||
if (isIndex) {
|
||||
const titles = {
|
||||
en: 'KoalaSync | Real-time Video Synchronization for Friends',
|
||||
de: 'KoalaSync | Echtzeit-Video-Synchronisation für Freunde'
|
||||
};
|
||||
document.title = titles[newLang] || titles.en;
|
||||
} else if (isJoin) {
|
||||
const titles = {
|
||||
en: 'Join Room | KoalaSync',
|
||||
de: 'Raum beitreten | KoalaSync'
|
||||
};
|
||||
document.title = titles[newLang] || titles.en;
|
||||
const activeLang = localStorage.getItem('koala_lang') || (navigator.language.startsWith('de') ? 'de' : 'en');
|
||||
const path = window.location.pathname;
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
// Only need to do this dynamic rewrite if we are NOT already inside a localized subdirectory
|
||||
if (!isSubdir) {
|
||||
const homeLinks = document.querySelectorAll('a[href="index.html"], a[href="de/index.html"], a[href="fr/index.html"], a[href="es/index.html"], a[href="pt-BR/index.html"], a[href="ru/index.html"]');
|
||||
homeLinks.forEach(link => {
|
||||
link.href = (activeLang === 'en') ? 'index.html' : `${activeLang}/index.html`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll('.lang-toggle').forEach(btn => {
|
||||
btn.addEventListener('click', toggleLanguage);
|
||||
// Modern Language Selector Navigation and State Toggling
|
||||
const handleLanguageChange = (e) => {
|
||||
const select = e.currentTarget;
|
||||
const newLang = select.value;
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Save the user's preference
|
||||
localStorage.setItem('koala_lang', newLang);
|
||||
|
||||
// Determine if we are on a static landing page versus a dynamic utility page
|
||||
const isLegalOrJoin = path.includes('impressum') || path.includes('datenschutz') || path.includes('join');
|
||||
const isIndex = !isLegalOrJoin;
|
||||
|
||||
if (isIndex) {
|
||||
// Static navigation: Route to correct subdirectory
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
let targetPath;
|
||||
if (newLang === 'en') {
|
||||
if (isSubdir) {
|
||||
targetPath = '../index.html';
|
||||
} else {
|
||||
targetPath = 'index.html';
|
||||
}
|
||||
} else {
|
||||
if (isSubdir) {
|
||||
// Switching from one language subdirectory to another (e.g., /de/ to /fr/)
|
||||
targetPath = '../' + newLang + '/index.html';
|
||||
} else {
|
||||
// Switching from root (English) to a language subdirectory (e.g., / to /fr/)
|
||||
targetPath = newLang + '/index.html';
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = targetPath;
|
||||
} else {
|
||||
// Dynamic page: Toggle classes and update elements dynamically without navigating away
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('lang-en', 'lang-de', 'lang-fr', 'lang-es', 'lang-pt-br', 'lang-ru');
|
||||
|
||||
// Fallback dynamic pages to 'en' if 'de' is not chosen (since fr/es markup is not present)
|
||||
const activeDisplayLang = (newLang === 'de') ? 'de' : 'en';
|
||||
html.classList.add('lang-' + activeDisplayLang);
|
||||
html.lang = activeDisplayLang;
|
||||
|
||||
// Sync all selects on the page to the new value
|
||||
document.querySelectorAll('.lang-dropdown').forEach(sel => {
|
||||
sel.value = newLang;
|
||||
});
|
||||
|
||||
// Update titles dynamically
|
||||
const isJoin = path.includes('join');
|
||||
if (isJoin) {
|
||||
const titles = { en: 'Join Room | KoalaSync', de: 'Raum beitreten | KoalaSync' };
|
||||
document.title = titles[activeDisplayLang] || titles.en;
|
||||
}
|
||||
|
||||
// Localize home links dynamically
|
||||
localizeHomeLinks();
|
||||
}
|
||||
};
|
||||
|
||||
// Register change event listener for the dropdowns
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.addEventListener('change', handleLanguageChange);
|
||||
});
|
||||
|
||||
// Initialize language select elements to show the current preferred language
|
||||
const initLanguageSelectorValue = () => {
|
||||
const savedLang = localStorage.getItem('koala_lang');
|
||||
const browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
const activePref = savedLang || browserLang;
|
||||
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.value = activePref;
|
||||
});
|
||||
};
|
||||
|
||||
// Impressum Email Obfuscation Click Reveal
|
||||
document.querySelectorAll('.email-reveal').forEach(el => {
|
||||
el.addEventListener('click', function() {
|
||||
@@ -595,4 +657,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
detectBrowserAndElevateBadge();
|
||||
checkInvite();
|
||||
updateDynamicVersion();
|
||||
localizeHomeLinks();
|
||||
initLanguageSelectorValue();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* KoalaSync Static Site Generator (i18n compiler)
|
||||
* Pure, dependency-free Node.js build pipeline.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Helper to recursively copy directories
|
||||
function copyDirSync(src, dest) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true });
|
||||
|
||||
for (let entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
copyDirSync(srcPath, destPath);
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function compile() {
|
||||
console.log('Starting KoalaSync i18n compilation...');
|
||||
|
||||
const websiteDir = __dirname;
|
||||
const wwwDir = path.join(websiteDir, 'www');
|
||||
|
||||
// 1. Create build directories
|
||||
fs.mkdirSync(wwwDir, { recursive: true });
|
||||
|
||||
// 2. Read template
|
||||
const templatePath = path.join(websiteDir, 'template.html');
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
console.error('Error: template.html not found! Run from website/ directory or repo root.');
|
||||
process.exit(1);
|
||||
}
|
||||
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
||||
|
||||
const localesDir = path.join(websiteDir, 'locales');
|
||||
const languages = ['en', 'de', 'fr', 'es', 'pt-BR', 'ru'];
|
||||
|
||||
// 3. Compile helper function
|
||||
function compilePage(locale, assetPath, lang) {
|
||||
let compiled = templateContent;
|
||||
|
||||
// Inject asset path prefix first
|
||||
compiled = compiled.replace(/\{\{ASSET_PATH\}\}/g, assetPath);
|
||||
|
||||
// Inject selected state for the dropdown
|
||||
languages.forEach(l => {
|
||||
const placeholder = `{{SELECTED_${l.toUpperCase()}}}`;
|
||||
compiled = compiled.replace(new RegExp(placeholder, 'g'), l === lang ? 'selected' : '');
|
||||
});
|
||||
|
||||
// Inject all translations
|
||||
for (let [key, value] of Object.entries(locale)) {
|
||||
const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
||||
compiled = compiled.replace(regex, value);
|
||||
}
|
||||
|
||||
return compiled;
|
||||
}
|
||||
|
||||
// 4. Generate HTML files
|
||||
for (let lang of languages) {
|
||||
const localePath = path.join(localesDir, `${lang}.json`);
|
||||
if (!fs.existsSync(localePath)) {
|
||||
console.warn(`Warning: Locale file for ${lang} not found.`);
|
||||
continue;
|
||||
}
|
||||
const locale = JSON.parse(fs.readFileSync(localePath, 'utf8'));
|
||||
|
||||
if (lang === 'en') {
|
||||
console.log('Compiling English version (index.html)...');
|
||||
const enHtml = compilePage(locale, '', lang);
|
||||
fs.writeFileSync(path.join(wwwDir, 'index.html'), enHtml, 'utf8');
|
||||
} else {
|
||||
console.log(`Compiling ${lang.toUpperCase()} version (${lang}/index.html)...`);
|
||||
const langDir = path.join(wwwDir, lang);
|
||||
fs.mkdirSync(langDir, { recursive: true });
|
||||
const langHtml = compilePage(locale, '../', lang);
|
||||
fs.writeFileSync(path.join(langDir, 'index.html'), langHtml, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Copy static assets
|
||||
console.log('Copying assets and static website files...');
|
||||
const staticFiles = [
|
||||
'style.css',
|
||||
'app.js',
|
||||
'lang-init.js',
|
||||
'robots.txt',
|
||||
'sitemap.xml',
|
||||
'version.json',
|
||||
'join.html',
|
||||
'impressum.html',
|
||||
'datenschutz.html'
|
||||
];
|
||||
|
||||
for (let file of staticFiles) {
|
||||
const srcPath = path.join(websiteDir, file);
|
||||
const destPath = path.join(wwwDir, file);
|
||||
if (fs.existsSync(srcPath)) {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
console.log(`Copied: ${file}`);
|
||||
} else {
|
||||
console.warn(`Warning: Static file ${file} not found.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy assets folder recursively
|
||||
const srcAssets = path.join(websiteDir, 'assets');
|
||||
const destAssets = path.join(wwwDir, 'assets');
|
||||
if (fs.existsSync(srcAssets)) {
|
||||
copyDirSync(srcAssets, destAssets);
|
||||
console.log('Copied assets directory recursively.');
|
||||
} else {
|
||||
console.error('Error: assets/ directory not found in website/.');
|
||||
}
|
||||
|
||||
console.log('KoalaSync compilation finished successfully! Output is in website/www/');
|
||||
}
|
||||
|
||||
compile();
|
||||
@@ -39,10 +39,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a class="lang-toggle" style="display: inline-flex; align-items: center; gap: 6px; cursor: pointer;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m5 8 6 6"></path><path d="m4 14 6-6 2-3"></path><path d="M2 5h12"></path><path d="M7 2h1"></path><path d="m22 22-5-10-5 10"></path><path d="M14 18h6"></path></svg>
|
||||
EN/DE
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -39,10 +39,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a class="lang-toggle" style="display: inline-flex; align-items: center; gap: 6px; cursor: pointer;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m5 8 6 6"></path><path d="m4 14 6-6 2-3"></path><path d="M2 5h12"></path><path d="M7 2h1"></path><path d="m22 22-5-10-5 10"></path><path d="M14 18h6"></path></svg>
|
||||
EN/DE
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -9,6 +9,19 @@
|
||||
<link rel="icon" type="image/webp" href="assets/NewLogoIcon_64.webp">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://sync.koalastuff.net/join.html">
|
||||
<meta property="og:title" content="Join Room | KoalaSync">
|
||||
<meta property="og:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
|
||||
<meta property="og:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Join Room | KoalaSync">
|
||||
<meta name="twitter:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
|
||||
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
|
||||
|
||||
<!-- Mobile Browser Theme Styling -->
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
@@ -39,10 +52,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a class="lang-toggle" style="display: inline-flex; align-items: center; gap: 6px; cursor: pointer;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m5 8 6 6"></path><path d="m4 14 6-6 2-3"></path><path d="M2 5h12"></path><path d="M7 2h1"></path><path d="m22 22-5-10-5 10"></path><path d="M14 18h6"></path></svg>
|
||||
EN/DE
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,12 +1,52 @@
|
||||
(function() {
|
||||
var savedLang = localStorage.getItem('koala_lang');
|
||||
var browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
var activeLang = savedLang || browserLang;
|
||||
document.documentElement.classList.add('lang-' + activeLang);
|
||||
document.documentElement.lang = activeLang;
|
||||
var html = document.documentElement;
|
||||
var path = window.location.pathname;
|
||||
|
||||
// Check if we are on the root index page (either "/" or "/index.html" at the root)
|
||||
var isRootIndex = path === '/' || path === '/index.html' || path === '';
|
||||
|
||||
if (isRootIndex) {
|
||||
var savedLang = localStorage.getItem('koala_lang');
|
||||
var browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
var preferredLang = savedLang || browserLang;
|
||||
|
||||
if (preferredLang === 'de') {
|
||||
// Redirect to German version
|
||||
window.location.replace('de/');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var htmlClasses = html.className.split(' ');
|
||||
var activeLang = null;
|
||||
var hasStaticLang = false;
|
||||
for (var i = 0; i < htmlClasses.length; i++) {
|
||||
if (htmlClasses[i].indexOf('lang-') === 0) {
|
||||
hasStaticLang = true;
|
||||
var langPart = htmlClasses[i].substring(5);
|
||||
activeLang = langPart === 'pt-br' ? 'pt-BR' : langPart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasStaticLang) {
|
||||
localStorage.setItem('koala_lang', activeLang);
|
||||
} else {
|
||||
var savedLang = localStorage.getItem('koala_lang');
|
||||
var browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
activeLang = savedLang || browserLang;
|
||||
|
||||
// Dynamic utility pages currently only support English and German markup.
|
||||
// Fallback to English for any other language preference (e.g. fr, es) to avoid bilingual text duplication.
|
||||
if (activeLang !== 'de') {
|
||||
activeLang = 'en';
|
||||
}
|
||||
|
||||
html.classList.add('lang-' + activeLang);
|
||||
html.lang = activeLang;
|
||||
}
|
||||
|
||||
// Update titles dynamically based on page
|
||||
var path = window.location.pathname;
|
||||
var isIndex = path === '/' || path.endsWith('index.html') || path.split('/').pop() === '';
|
||||
var isJoin = path.includes('join');
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"LANG_CODE": "de",
|
||||
"HTML_CLASS": "lang-de",
|
||||
"CANONICAL_PATH": "de/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
|
||||
"META_TITLE": "KoalaSync | Netflix, YouTube & jedes Video mit Freunden synchronisieren",
|
||||
"META_DESCRIPTION": "Schaue Netflix, YouTube, Twitch und jedes HTML5-Video perfekt synchronisiert mit Freunden. Kostenlose, quelloffene Browser-Erweiterung für Chrome und Firefox. Keine Anmeldung erforderlich.",
|
||||
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin & fast jedes Video mit Freunden synchronisieren",
|
||||
"OG_DESCRIPTION": "Schaue Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedes HTML5-Video perfekt synchronisiert. Quelloffene, datenschutzfreundliche Browser-Erweiterung für Chrome und Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin & fast jedes Video mit Freunden synchronisieren – Browser-Erweiterung",
|
||||
"TWITTER_DESCRIPTION": "Schaue Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedes HTML5-Video perfekt synchronisiert mit Freunden. Datenschutzfreundliche Browser-Erweiterung für Chrome und Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Funktionen",
|
||||
"NAV_HOW_IT_WORKS": "So funktioniert's",
|
||||
|
||||
"HERO_TITLE": "Gemeinsam schauen.<br>Perfekt synchron.",
|
||||
"HERO_SUBTITLE": "Dein Kino-Abend auf Distanz. Keine Lags, keine Anmeldung. Einfach Link teilen und zusammen schauen.",
|
||||
"HERO_MASCOT_ALT": "Ein niedlicher Koala steht da und schaut nach unten auf die Download-Buttons",
|
||||
"ADD_TO_CHROME": "Zu Chrome hinzufügen",
|
||||
"ADD_TO_FIREFOX": "Zu Firefox hinzufügen",
|
||||
|
||||
"COMPAT_HEADING": "Funktioniert auf deinen Lieblingsplattformen",
|
||||
"COMPAT_MORE": "und viele mehr",
|
||||
"COMPAT_TOOLTIP": "Funktioniert auf fast jeder Seite mit einem Video-Element",
|
||||
|
||||
"USE_CASES_TITLE": "Perfekt für jeden Anlass",
|
||||
"USE_CASES_SUBTITLE": "Ob nah oder fern, KoalaSync bringt Menschen bei ihren Lieblingsvideos zusammen.",
|
||||
"USE_CASE_1_ALT": "Zwei niedliche Koalas sitzen zusammen und teilen sich einen Eimer Popcorn",
|
||||
"USE_CASE_1_TITLE": "Filmabend mit Freunden",
|
||||
"USE_CASE_1_DESC": "Synchronisiere eure Filme in Echtzeit und quatscht nebenbei auf Discord, Zoom oder im Tool eurer Wahl. Es fühlt sich an, als säßet ihr im selben Raum.",
|
||||
"USE_CASE_2_ALT": "Ein Koala-Professor hält einen Vortrag auf einem Bildschirm vor zwei remote zugeschalteten Schüler-Koalas",
|
||||
"USE_CASE_2_TITLE": "Gemeinsam Lernen",
|
||||
"USE_CASE_2_DESC": "Analysiert Online-Tutorials, Vorlesungen oder Streams gemeinsam mit Mitschülern oder Kollegen. Pausiert und besprecht komplizierte Schritte in perfektem Sync.",
|
||||
"USE_CASE_3_ALT": "Ein niedliches Koala-Pärchen schaut remote zusammen; einer sitzt am PC und sie liegt mit dem Laptop im Bett, während Herzen zwischen ihnen fliegen",
|
||||
"USE_CASE_3_TITLE": "Fernbeziehungen",
|
||||
"USE_CASE_3_DESC": "Überbrückt die Distanz und genießt gemeinsame Date-Nächte. Erlebt jeden Plot-Twist, lacht über dieselben Witze und teilt emotionale Momente zur selben Millisekunde.",
|
||||
|
||||
"WHY_TITLE": "Warum KoalaSync?",
|
||||
"WHY_SUBTITLE": "Entwickelt für zuverlässigen Sync, Datenschutz und einfache Einrichtung.",
|
||||
|
||||
"FEATURE_1_TITLE": "Volle Kontrolle, in Echtzeit",
|
||||
"FEATURE_1_DESC": "Einer pausiert, alle pausieren. Einer spult, alle folgen. Unser Zwei-Phasen-Synchronisationsprotokoll koordiniert die Wiedergabe aller Teilnehmer in Echtzeit.",
|
||||
"FEATURE_2_TITLE": "Grenzenloses Bingen",
|
||||
"FEATURE_2_DESC": "Nächste Episode startet für jeden zeitgleich. KoalaSync erkennt den Episodenwechsel und pausiert, bis jeder Teilnehmer das neue Video fertig geladen hat.",
|
||||
"FEATURE_3_TITLE": "Keine Accounts / Datenschutz",
|
||||
"FEATURE_3_DESC": "Kein Login. Keine Daten. Einfach Link teilen und schauen. Der Server läuft flüchtig im RAM und löscht deinen Raum komplett nach dem Verlassen.",
|
||||
"FEATURE_4_TITLE": "Universeller HTML5-Support",
|
||||
"FEATURE_4_DESC": "Unterstützt YouTube, Twitch, Netflix, Jellyfin, Emby und fast jede beliebige Webseite mit einem HTML5-Video-Tag. Ideal auch für eigene Medienbibliotheken.",
|
||||
"FEATURE_5_TITLE": "Self-Hostable & Docker-Ready",
|
||||
"FEATURE_5_DESC": "Unsere Server sind kostenlos und schnell, aber du kannst auch die volle Kontrolle übernehmen, wenn du willst. Starte dein eigenes privates Relay in Sekunden via Docker.",
|
||||
"FEATURE_6_TITLE": "Direkte Einladungen & 1-Klick Beitritt",
|
||||
"FEATURE_6_DESC": "Keine lästigen IPs oder Passwörter austauschen. Teile einfach einen Einladungslink mit deinen Freunden, um sie mit einem Klick in den Raum zu holen.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync vs. Teleparty",
|
||||
"COMP_SUBTITLE": "Erfahre, warum Open Source, Werbefreiheit und echter Datenschutz die bessere Wahl für gemeinsames Schauen sind.",
|
||||
"COMP_COL_FEATURE": "Funktion",
|
||||
"COMP_FEAT_1_NAME": "Kosten / Paywalls",
|
||||
"COMP_FEAT_1_DESC": "Premium-Abos, versteckte Kosten oder gesperrte Funktionen.",
|
||||
"COMP_FEAT_1_KOALA": "100% Kostenlos",
|
||||
"COMP_FEAT_1_TELE": "Premium-Abo / Paywalls",
|
||||
"COMP_FEAT_2_NAME": "Lizenz / Quellcode",
|
||||
"COMP_FEAT_2_DESC": "Ob der Quellcode quelloffen und frei einsehbar ist.",
|
||||
"COMP_FEAT_2_KOALA": "Open Source (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Proprietär",
|
||||
"COMP_FEAT_3_NAME": "Self-Hosting",
|
||||
"COMP_FEAT_3_DESC": "Möglichkeit, einen eigenen privaten Relay-Server zu betreiben.",
|
||||
"COMP_FEAT_3_KOALA": "Ja (Docker-bereit)",
|
||||
"COMP_FEAT_3_TELE": "Nein",
|
||||
"COMP_FEAT_4_NAME": "Datenschutz & Tracking",
|
||||
"COMP_FEAT_4_DESC": "Registrierungszwang, Tracking-Cookies und Analysen.",
|
||||
"COMP_FEAT_4_KOALA": "Keine Speicherung (RAM-only)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics & Cookies",
|
||||
"COMP_FEAT_5_NAME": "Kompatibilität",
|
||||
"COMP_FEAT_5_DESC": "Unterstützte Webseiten und Player-Kompatibilität.",
|
||||
"COMP_FEAT_5_KOALA": "Fast jedes HTML5-Video",
|
||||
"COMP_FEAT_5_TELE": "Nur unterstützte Seiten",
|
||||
"COMP_FOOTNOTE_1": "Stand des Vergleichs: Mai 2026.",
|
||||
"COMP_FOOTNOTE_2": "Details zu Preisen und unterstützten Netzwerken von Teleparty Premium:",
|
||||
"COMP_FOOTNOTE_3": "Datenschutzerklärung und Tracker-Erfassung von Teleparty:",
|
||||
"COMP_FOOTNOTE_4": "Funktioniert auf allen Seiten, die Skript-Injektionen in Standard-HTML5-Videoplayer erlauben. Seiten mit extrem strengen Content Security Policies (CSP), DRM-Kopierschutz oder stark verschachtelten Player-Wrappern (z. B. komplexe Shadow-DOMs) können die automatische Steuerung blockieren.",
|
||||
|
||||
"STEPS_TITLE": "Erste Schritte",
|
||||
"STEP_1_TITLE": "Erweiterung installieren",
|
||||
"STEP_1_DESC": "Füge KoalaSync aus dem Chrome Web Store, den Firefox Add-ons oder über die Entwickler-ZIP von GitHub zu deinem Browser hinzu.",
|
||||
"STEP_1_ILLUS_DESC": "Privatsphäre-fokussierte Video-Synchronisation",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Download für Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Download für Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Erweiterung aktiv",
|
||||
"STEP_1_ILLUS_READY": "Bereit zum Mitschauen!",
|
||||
"STEP_2_TITLE": "Raum erstellen",
|
||||
"STEP_2_DESC": "Öffne die Erweiterung und klicke auf „+ Neuer Raum“. KoalaSync generiert automatisch eine sichere Raum-ID samt Passwort, tritt dem Raum bei und kopiert den Einladungslink in deine Zwischenablage.",
|
||||
"STEP_2_ILLUS_ROOM": "Raum",
|
||||
"STEP_2_ILLUS_SYNC": "Sync",
|
||||
"STEP_2_ILLUS_SETTINGS": "Optionen",
|
||||
"STEP_2_ILLUS_CREATE": "+ Neuer Raum",
|
||||
"STEP_2_ILLUS_MANUAL": "Manuell verbinden / Erweitert",
|
||||
"STEP_2_ILLUS_COPIED": "Einladungslink kopiert!",
|
||||
"STEP_3_TITLE": "Teilen & Synchronisieren",
|
||||
"STEP_3_DESC": "Sende den Einladungslink an deine Freunde. Sobald sie beitreten, wähle deinen Video-Tab aus und genieße die synchronisierte Wiedergabe.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "SYNCHRON",
|
||||
|
||||
"SELF_TITLE": "Für Self-Hoster",
|
||||
"SELF_SUBTITLE": "Du traust dem Server nicht? Behalte die volle Datenhoheit. Richte deinen eigenen privaten Relay-Server in wenigen Minuten ein.",
|
||||
"SELF_MASCOT_ALT": "Ein niedlicher Koala sitzt am Laptop und deployt einen Docker-Container für das Self-Hosting",
|
||||
"SELF_COPY_CODE": "Code kopieren",
|
||||
"SELF_GITHUB_PACKAGES": "Alle Image-Tags auf GitHub Packages ansehen",
|
||||
|
||||
"BOTTOM_TITLE": "Noch nicht überzeugt? Überzeug dich selbst.",
|
||||
"BOTTOM_SUBTITLE": "KoalaSync ist zu 100 % Open Source, werbefrei und trackingsicher. Überprüfe unseren Code auf GitHub oder installiere direkt die Browser-Erweiterung.",
|
||||
"BOTTOM_MASCOT_ALT": "Ein niedlicher Koala hält eine GitHub-Projektseite zusammen mit dem GitHub Maskottchen Octocat",
|
||||
|
||||
"FOOTER_MIT": "Open Source unter der MIT-Lizenz.",
|
||||
"FOOTER_RAM": "Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.",
|
||||
"FOOTER_LEGAL": "Impressum",
|
||||
"FOOTER_PRIVACY": "Datenschutz"
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"LANG_CODE": "en",
|
||||
"HTML_CLASS": "lang-en",
|
||||
"CANONICAL_PATH": "",
|
||||
"LANG_TOGGLE_URL": "de/",
|
||||
"LANG_TOGGLE_TEXT": "DE",
|
||||
|
||||
"META_TITLE": "KoalaSync | Sync Netflix, YouTube & Any Video with Friends",
|
||||
"META_DESCRIPTION": "Watch Netflix, YouTube, Twitch & any HTML5 video in perfect sync with friends. Free, open-source browser extension for Chrome and Firefox. No sign-up needed.",
|
||||
"OG_TITLE": "KoalaSync | Sync Netflix, Emby, Jellyfin & Almost Any Video with Friends",
|
||||
"OG_DESCRIPTION": "Watch Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video in perfect sync. Privacy-first, open-source browser extension for Chrome & Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Sync Netflix, Emby, Jellyfin & Almost Any Video with Friends – Browser Extension",
|
||||
"TWITTER_DESCRIPTION": "Watch Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video in perfect sync with friends. Privacy-first, open-source browser extension for Chrome and Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Features",
|
||||
"NAV_HOW_IT_WORKS": "How it works",
|
||||
|
||||
"HERO_TITLE": "Watch Together.<br>Sync Perfectly.",
|
||||
"HERO_SUBTITLE": "Your remote movie night without lags. No registration, no data collection. Just share a link and watch together.",
|
||||
"HERO_MASCOT_ALT": "A cute koala standing and looking down at the download buttons",
|
||||
"ADD_TO_CHROME": "Add to Chrome",
|
||||
"ADD_TO_FIREFOX": "Add to Firefox",
|
||||
|
||||
"COMPAT_HEADING": "Works on your favorite platforms",
|
||||
"COMPAT_MORE": "and many more",
|
||||
"COMPAT_TOOLTIP": "Works on almost any site with a video element",
|
||||
|
||||
"USE_CASES_TITLE": "Perfect for Any Occasion",
|
||||
"USE_CASES_SUBTITLE": "Whether near or far, KoalaSync brings people together around their favorite videos.",
|
||||
"USE_CASE_1_ALT": "Two cute koalas sitting together and sharing a bucket of popcorn",
|
||||
"USE_CASE_1_TITLE": "Movie Night with Friends",
|
||||
"USE_CASE_1_DESC": "Sync your movies in real time and talk via Discord, Zoom, or your favorite voice call application. It feels just like sitting in the same room.",
|
||||
"USE_CASE_2_ALT": "A koala professor presenting on a screen to two remote student koalas",
|
||||
"USE_CASE_2_TITLE": "Remote Learning",
|
||||
"USE_CASE_2_DESC": "Analyze online tutorials, lectures, or developer training streams together with classmates or colleagues. Pause and discuss complex steps in perfect harmony.",
|
||||
"USE_CASE_3_ALT": "A cute koala couple watching together remotely; one is sitting at a PC and the other is in bed with a laptop, with flying hearts between them",
|
||||
"USE_CASE_3_TITLE": "Long-Distance Relationships",
|
||||
"USE_CASE_3_DESC": "Bridge the distance and enjoy date nights. Experience every plot twist, laugh at the same jokes, and share emotional moments at the exact same millisecond.",
|
||||
|
||||
"WHY_TITLE": "Why KoalaSync?",
|
||||
"WHY_SUBTITLE": "Built for reliable sync, privacy-first design, and easy setup.",
|
||||
|
||||
"FEATURE_1_TITLE": "Full Control / Instant Sync",
|
||||
"FEATURE_1_DESC": "One pauses, everyone pauses. One seeks, everyone follows. Our two-phase sync protocol coordinates playback in real time across all participants.",
|
||||
"FEATURE_2_TITLE": "Endless Binge-Watching",
|
||||
"FEATURE_2_DESC": "Autoplay in sync. KoalaSync automatically detects episode transitions and holds playback until all peers have successfully loaded the next video.",
|
||||
"FEATURE_3_TITLE": "Zero Accounts / Pure Privacy",
|
||||
"FEATURE_3_DESC": "No registration, no tracking, no persistency footprints. The server runs entirely in ephemeral RAM and purges your room when you leave.",
|
||||
"FEATURE_4_TITLE": "Universal HTML5 Support",
|
||||
"FEATURE_4_DESC": "Works on YouTube, Twitch, Netflix, Jellyfin, Emby, and almost any standard webpage containing a HTML5 video element. Also compatible with custom self-hosted setups.",
|
||||
"FEATURE_5_TITLE": "Self-Hostable & Docker-Ready",
|
||||
"FEATURE_5_DESC": "Our official servers are free and fast, but you can take full ownership if you want to. Launch your own private relay server in seconds via Docker.",
|
||||
"FEATURE_6_TITLE": "Instant Invites / 1-Click Join",
|
||||
"FEATURE_6_DESC": "No IP addresses or passwords to exchange. Share a generated invite link with your friends to let them join your room automatically with a single click.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync vs. Teleparty",
|
||||
"COMP_SUBTITLE": "See why open source, ad-free and privacy-first is the superior way to watch together.",
|
||||
"COMP_COL_FEATURE": "Feature",
|
||||
"COMP_FEAT_1_NAME": "Cost / Paywalls",
|
||||
"COMP_FEAT_1_DESC": "Premium subscriptions, hidden fees or locked features.",
|
||||
"COMP_FEAT_1_KOALA": "100% Free",
|
||||
"COMP_FEAT_1_TELE": "Paid Tiers / Premium Locks",
|
||||
"COMP_FEAT_2_NAME": "License / Code Auditable",
|
||||
"COMP_FEAT_2_DESC": "Whether the source code is open and freely auditable.",
|
||||
"COMP_FEAT_2_KOALA": "Open Source (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Proprietary",
|
||||
"COMP_FEAT_3_NAME": "Self-Hosting",
|
||||
"COMP_FEAT_3_DESC": "Ability to deploy your own private relay server.",
|
||||
"COMP_FEAT_3_KOALA": "Yes (Docker-ready)",
|
||||
"COMP_FEAT_3_TELE": "No",
|
||||
"COMP_FEAT_4_NAME": "Privacy & Tracking",
|
||||
"COMP_FEAT_4_DESC": "Registration requirements, tracking cookies and analytics.",
|
||||
"COMP_FEAT_4_KOALA": "Zero-Persistence (RAM-only)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics & Cookies",
|
||||
"COMP_FEAT_5_NAME": "Site Compatibility",
|
||||
"COMP_FEAT_5_DESC": "Supported websites and player compatibility.",
|
||||
"COMP_FEAT_5_KOALA": "Almost any HTML5 Video",
|
||||
"COMP_FEAT_5_TELE": "Supported sites only",
|
||||
"COMP_FOOTNOTE_1": "Comparison state: May 2026.",
|
||||
"COMP_FOOTNOTE_2": "Official Teleparty Premium pricing and supported networks details:",
|
||||
"COMP_FOOTNOTE_3": "Teleparty privacy policies and tracking data collection:",
|
||||
"COMP_FOOTNOTE_4": "Works on websites that allow script injections into standard HTML5 video tags. Websites with highly strict Content Security Policies (CSP), DRM copy protection, or heavily obfuscated player wrappers (like complex shadow DOMs) might restrict automated control or injection.",
|
||||
|
||||
"STEPS_TITLE": "Getting Started",
|
||||
"STEP_1_TITLE": "Install Extension",
|
||||
"STEP_1_DESC": "Add KoalaSync to your browser from the Chrome Web Store, Firefox Add-ons, or download the latest developer ZIP from GitHub.",
|
||||
"STEP_1_ILLUS_DESC": "Privacy-first video synchronizer",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Download for Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Download for Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Extension Active",
|
||||
"STEP_1_ILLUS_READY": "Ready to watch together!",
|
||||
"STEP_2_TITLE": "Create a Room",
|
||||
"STEP_2_DESC": "Open the extension popup and click '+ Create New Room'. KoalaSync automatically generates a secure Room ID and Password, joins it, and copies the invite link to your clipboard.",
|
||||
"STEP_2_ILLUS_ROOM": "Room",
|
||||
"STEP_2_ILLUS_SYNC": "Sync",
|
||||
"STEP_2_ILLUS_SETTINGS": "Settings",
|
||||
"STEP_2_ILLUS_CREATE": "+ Create New Room",
|
||||
"STEP_2_ILLUS_MANUAL": "Manual Connect / Advanced",
|
||||
"STEP_2_ILLUS_COPIED": "Invite link copied!",
|
||||
"STEP_3_TITLE": "Share & Sync",
|
||||
"STEP_3_DESC": "Send the invite link to your friends. Once they join, select your video tab and enjoy synchronized playback.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "IN SYNC",
|
||||
|
||||
"SELF_TITLE": "For Self-Hosters",
|
||||
"SELF_SUBTITLE": "Don't trust our server? Maintain full data sovereignty. Deploy your own private relay server in minutes.",
|
||||
"SELF_MASCOT_ALT": "A cute koala sitting at a laptop deploying a Docker container for self-hosting",
|
||||
"SELF_COPY_CODE": "Copy Code",
|
||||
"SELF_GITHUB_PACKAGES": "View all image tags on GitHub Packages",
|
||||
|
||||
"BOTTOM_TITLE": "Not convinced? See for yourself.",
|
||||
"BOTTOM_SUBTITLE": "KoalaSync is 100% open source, ad-free and tracking-free. Audit our codebase on GitHub or install the browser extension directly.",
|
||||
"BOTTOM_MASCOT_ALT": "A cute koala holding a GitHub repository page next to the GitHub mascot Octocat",
|
||||
|
||||
"FOOTER_MIT": "Open source under the MIT License.",
|
||||
"FOOTER_RAM": "No data is stored on our servers. Pure RAM-based relay.",
|
||||
"FOOTER_LEGAL": "Legal Notice",
|
||||
"FOOTER_PRIVACY": "Privacy Policy"
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"LANG_CODE": "es",
|
||||
"HTML_CLASS": "lang-es",
|
||||
"CANONICAL_PATH": "es/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
|
||||
"META_TITLE": "KoalaSync | Sincroniza Netflix, YouTube y cualquier video con amigos",
|
||||
"META_DESCRIPTION": "Mira Netflix, YouTube, Twitch y cualquier video HTML5 en perfecta sincronización con amigos. Extensión de navegador gratuita y de código abierto para Chrome y Firefox. No se requiere registro.",
|
||||
"OG_TITLE": "KoalaSync | Sincroniza Netflix, Emby, Jellyfin y casi cualquier video con amigos",
|
||||
"OG_DESCRIPTION": "Mira Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier video HTML5 en perfecta sincronización. Extensión de navegador de código abierto y respetuosa con la privacidad para Chrome y Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Sincroniza Netflix, Emby, Jellyfin y casi cualquier video con amigos – Extensión de navegador",
|
||||
"TWITTER_DESCRIPTION": "Mira Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier video HTML5 en perfecta sincronización con amigos. Extensión de navegador de código abierto y respetuosa con la privacidad para Chrome y Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Características",
|
||||
"NAV_HOW_IT_WORKS": "Cómo funciona",
|
||||
|
||||
"HERO_TITLE": "Miren juntos.<br>Sincronización perfecta.",
|
||||
"HERO_SUBTITLE": "Tu noche de películas a distancia sin retrasos. Sin registro ni recopilación de datos. Solo comparte un enlace y miren juntos.",
|
||||
"HERO_MASCOT_ALT": "Un lindo koala de pie mirando hacia abajo a los botones de descarga",
|
||||
"ADD_TO_CHROME": "Añadir a Chrome",
|
||||
"ADD_TO_FIREFOX": "Añadir a Firefox",
|
||||
|
||||
"COMPAT_HEADING": "Funciona en tus plataformas favoritas",
|
||||
"COMPAT_MORE": "y muchas más",
|
||||
"COMPAT_TOOLTIP": "Funciona en casi cualquier sitio con un elemento de video",
|
||||
|
||||
"USE_CASES_TITLE": "Perfecto para cualquier ocasión",
|
||||
"USE_CASES_SUBTITLE": "Ya sea cerca o lejos, KoalaSync une a las personas en torno a sus videos favoritos.",
|
||||
"USE_CASE_1_ALT": "Dos lindos koalas sentados juntos compartiendo un cubo de palomitas de maíz",
|
||||
"USE_CASE_1_TITLE": "Noche de películas con amigos",
|
||||
"USE_CASE_1_DESC": "Sincroniza tus películas en tiempo real y habla por Discord, Zoom o tu aplicación de llamada de voz favorita. Es como estar en la misma habitación.",
|
||||
"USE_CASE_2_ALT": "Un profesor koala presentando en una pantalla a dos estudiantes koala remotos",
|
||||
"USE_CASE_2_TITLE": "Aprendizaje a distancia",
|
||||
"USE_CASE_2_DESC": "Analiza tutoriales en línea, conferencias o transmisiones de capacitación de desarrolladores con compañeros o colegas. Pausa y discute pasos complejos en perfecta armonía.",
|
||||
"USE_CASE_3_ALT": "Una linda pareja de koalas viendo juntos de forma remota; uno está sentado frente a una PC y el otro en la cama con una computadora portátil, con corazones volando entre ellos",
|
||||
"USE_CASE_3_TITLE": "Relaciones a larga distancia",
|
||||
"USE_CASE_3_DESC": "Reduce la distancia y disfruta de noches de citas. Experimenta cada giro de la trama, ríete de los mismos chistes y comparte momentos emotivos en el mismo milisegundo exacto.",
|
||||
|
||||
"WHY_TITLE": "¿Por qué KoalaSync?",
|
||||
"WHY_SUBTITLE": "Diseñado para una sincronización confiable, privacidad y una configuración sencilla.",
|
||||
|
||||
"FEATURE_1_TITLE": "Control total / Sincronización instantánea",
|
||||
"FEATURE_1_DESC": "Uno pausa, todos pausan. Uno avanza, todos siguen. Nuestro protocolo de sincronización en dos fases coordina la reproducción en tiempo real para todos los participantes.",
|
||||
"FEATURE_2_TITLE": "Maratones sin fin",
|
||||
"FEATURE_2_DESC": "Reproducción automática sincronizada. KoalaSync detecta automáticamente transiciones de episodios y suspende la reproducción hasta que todos los participantes hayan cargado el siguiente video.",
|
||||
"FEATURE_3_TITLE": "Sin cuentas / Privacidad absoluta",
|
||||
"FEATURE_3_DESC": "Sin registros, sin seguimiento, sin almacenamiento de datos. El servidor funciona completamente en RAM efímera y elimina tu sala tan pronto como sales.",
|
||||
"FEATURE_4_TITLE": "Soporte universal HTML5",
|
||||
"FEATURE_4_DESC": "Funciona en YouTube, Twitch, Netflix, Jellyfin, Emby y casi cualquier página web estándar que contenga un elemento de video HTML5. También es compatible con configuraciones personalizadas.",
|
||||
"FEATURE_5_TITLE": "Auto-alojable y listo para Docker",
|
||||
"FEATURE_5_DESC": "Nuestros servidores oficiales son gratuitos y rápidos, pero puedes tomar el control total si lo deseas. Inicia tu propio servidor de retransmisión privado en segundos a través de Docker.",
|
||||
"FEATURE_6_TITLE": "Invitaciones al instante / Unirse en 1 clic",
|
||||
"FEATURE_6_DESC": "Sin direcciones IP o contraseñas que intercambiar. Comparte un enlace de invitación generado para permitir que tus amigos se unan automáticamente a tu sala con un solo clic.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync vs Teleparty",
|
||||
"COMP_SUBTITLE": "Descubre por qué una herramienta de código abierto, sin anuncios y respetuosa con la privacidad es la mejor opción para ver juntos.",
|
||||
"COMP_COL_FEATURE": "Característica",
|
||||
"COMP_FEAT_1_NAME": "Costo / Funciones de pago",
|
||||
"COMP_FEAT_1_DESC": "Suscripciones premium, tarifas ocultas o funciones bloqueadas.",
|
||||
"COMP_FEAT_1_KOALA": "100% Gratis",
|
||||
"COMP_FEAT_1_TELE": "Suscripciones de pago / Bloqueos Premium",
|
||||
"COMP_FEAT_2_NAME": "Licence / Code auditable",
|
||||
"COMP_FEAT_2_DESC": "Si el código fuente es abierto y libremente auditable.",
|
||||
"COMP_FEAT_2_KOALA": "Código Abierto (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Propietario",
|
||||
"COMP_FEAT_3_NAME": "Auto-alojamiento",
|
||||
"COMP_FEAT_3_DESC": "Capacidad para desplegar tu propio servidor de retransmisión privado.",
|
||||
"COMP_FEAT_3_KOALA": "Sí (listo para Docker)",
|
||||
"COMP_FEAT_3_TELE": "No",
|
||||
"COMP_FEAT_4_NAME": "Privacidad y Seguimiento",
|
||||
"COMP_FEAT_4_DESC": "Requisitos de registro, cookies de seguimiento y análisis.",
|
||||
"COMP_FEAT_4_KOALA": "Sin persistencia (solo RAM)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics y Cookies",
|
||||
"COMP_FEAT_5_NAME": "Compatibilidad del sitio",
|
||||
"COMP_FEAT_5_DESC": "Sitios web compatibles y compatibilidad de reproductores.",
|
||||
"COMP_FEAT_5_KOALA": "Casi cualquier video HTML5",
|
||||
"COMP_FEAT_5_TELE": "Solo sitios compatibles",
|
||||
"COMP_FOOTNOTE_1": "Estado de la comparación: mayo de 2026.",
|
||||
"COMP_FOOTNOTE_2": "Detalles oficiales de precios de Teleparty Premium y redes compatibles:",
|
||||
"COMP_FOOTNOTE_3": "Políticas de privacidad y recopilación de datos de seguimiento de Teleparty:",
|
||||
"COMP_FOOTNOTE_4": "Funciona en sitios web que permiten inyecciones de scripts en etiquetas de video HTML5 estándar. Los sitios con políticas de seguridad de contenido (CSP) muy estrictas, protección de copia DRM o contenedores de reproductores fuertemente ocultos (como DOMs de sombra complejos) pueden restringir el control automatizado o la inyección.",
|
||||
|
||||
"STEPS_TITLE": "Cómo empezar",
|
||||
"STEP_1_TITLE": "Instalar la extensión",
|
||||
"STEP_1_DESC": "Añade KoalaSync a tu navegador desde Chrome Web Store, complementos de Firefox o descarga el último archivo ZIP de desarrollador desde GitHub.",
|
||||
"STEP_1_ILLUS_DESC": "Sincronizador de video respetuoso con la privacidad",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Descargar para Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Descargar para Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Extensión activa",
|
||||
"STEP_1_ILLUS_READY": "¡Listo para ver juntos!",
|
||||
"STEP_2_TITLE": "Crear una sala",
|
||||
"STEP_2_DESC": "Abre la ventana de la extensión y haz clic en '+ Crear nueva sala'. KoalaSync genera automáticamente un identificador de sala y una contraseña seguros, se conecta y copia el enlace de invitación a tu portapapeles.",
|
||||
"STEP_2_ILLUS_ROOM": "Sala",
|
||||
"STEP_2_ILLUS_SYNC": "Sincro",
|
||||
"STEP_2_ILLUS_SETTINGS": "Opciones",
|
||||
"STEP_2_ILLUS_CREATE": "+ Crear nueva sala",
|
||||
"STEP_2_ILLUS_MANUAL": "Conexión manual / Avanzado",
|
||||
"STEP_2_ILLUS_COPIED": "¡Enlace de invitación copiado!",
|
||||
"STEP_3_TITLE": "Compartir y Sincronizar",
|
||||
"STEP_3_DESC": "Envía el enlace de invitación a tus amigos. Tan pronto como se unan, selecciona tu pestaña de video y disfruta de la reproducción sincronizada.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZADO",
|
||||
|
||||
"SELF_TITLE": "Para auto-alojadores",
|
||||
"SELF_SUBTITLE": "¿No confías en nuestro servidor? Mantén la soberanía total de los datos. Despliega tu propio servidor de retransmisión en minutos.",
|
||||
"SELF_MASCOT_ALT": "Un lindo koala sentado frente a una computadora portátil desplegando un contenedor Docker para auto-alojamiento",
|
||||
"SELF_COPY_CODE": "Copiar código",
|
||||
"SELF_GITHUB_PACKAGES": "Ver todas las etiquetas de imágenes en GitHub Packages",
|
||||
|
||||
"BOTTOM_TITLE": "¿Aún no te convence? Compruébalo tú mismo.",
|
||||
"BOTTOM_SUBTITLE": "KoalaSync es 100% de código abierto, sin anuncios y sin seguimiento. Audita nuestra base de código en GitHub o instala la extensión del navegador directamente.",
|
||||
"BOTTOM_MASCOT_ALT": "Un lindo koala sosteniendo una página de repositorio de GitHub junto a Octocat, la mascota de GitHub",
|
||||
|
||||
"FOOTER_MIT": "Código abierto bajo la Licencia MIT.",
|
||||
"FOOTER_RAM": "No se almacenan datos en nuestros servidores. Retransmisión solo en RAM.",
|
||||
"FOOTER_LEGAL": "Legal Notice",
|
||||
"FOOTER_PRIVACY": "Privacy Policy"
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"LANG_CODE": "fr",
|
||||
"HTML_CLASS": "lang-fr",
|
||||
"CANONICAL_PATH": "fr/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
|
||||
"META_TITLE": "KoalaSync | Synchronisez Netflix, YouTube et n'importe quelle vidéo avec vos amis",
|
||||
"META_DESCRIPTION": "Regardez Netflix, YouTube, Twitch et n'importe quelle vidéo HTML5 en parfaite synchronisation avec vos amis. Extension de navigateur gratuite et open-source pour Chrome et Firefox. Aucune inscription requise.",
|
||||
"OG_TITLE": "KoalaSync | Synchronisez Netflix, Emby, Jellyfin et presque toutes les vidéos avec vos amis",
|
||||
"OG_DESCRIPTION": "Regardez Netflix, Emby, Jellyfin, YouTube, Twitch et presque n'importe quelle vidéo HTML5 en parfaite synchronisation. Extension de navigateur open-source et respectueuse de la vie privée pour Chrome et Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Synchronisez Netflix, Emby, Jellyfin et presque toutes les vidéos avec vos amis – Extension de navigateur",
|
||||
"TWITTER_DESCRIPTION": "Regardez Netflix, Emby, Jellyfin, YouTube, Twitch et presque n'importe quelle vidéo HTML5 en parfaite synchronisation avec vos amis. Extension de navigateur open-source et respectueuse de la vie privée pour Chrome et Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Fonctionnalités",
|
||||
"NAV_HOW_IT_WORKS": "Comment ça marche",
|
||||
|
||||
"HERO_TITLE": "Regardez ensemble.<br>Synchronisation parfaite.",
|
||||
"HERO_SUBTITLE": "Votre soirée cinéma à distance sans décalage. Pas d'inscription, pas de collecte de données. Partagez simplement un lien et regardez ensemble.",
|
||||
"HERO_MASCOT_ALT": "Un koala mignon debout et regardant vers le bas les boutons de téléchargement",
|
||||
"ADD_TO_CHROME": "Ajouter à Chrome",
|
||||
"ADD_TO_FIREFOX": "Ajouter à Firefox",
|
||||
|
||||
"COMPAT_HEADING": "Fonctionne sur vos plateformes préférées",
|
||||
"COMPAT_MORE": "et beaucoup d'autres",
|
||||
"COMPAT_TOOLTIP": "Fonctionne sur presque tous les sites avec un élément vidéo",
|
||||
|
||||
"USE_CASES_TITLE": "Parfait pour toutes les occasions",
|
||||
"USE_CASES_SUBTITLE": "Que ce soit de près ou de loin, KoalaSync rassemble les gens autour de leurs vidéos préférées.",
|
||||
"USE_CASE_1_ALT": "Deux koalas mignons assis ensemble et partageant un seau de pop-corn",
|
||||
"USE_CASE_1_TITLE": "Soirée cinéma entre amis",
|
||||
"USE_CASE_1_DESC": "Synchronisez vos films en temps réel et parlez via Discord, Zoom ou votre application d'appel vocal préférée. C'est comme si vous étiez dans la même pièce.",
|
||||
"USE_CASE_2_ALT": "Un professeur koala faisant une présentation sur un écran à deux élèves koalas à distance",
|
||||
"USE_CASE_2_TITLE": "Apprentissage à distance",
|
||||
"USE_CASE_2_DESC": "Analysez des tutoriels en ligne, des conférences ou des flux de formation de développeurs avec des camarades ou des collègues. Faites une pause et discutez des étapes complexes en parfaite harmonie.",
|
||||
"USE_CASE_3_ALT": "Un couple de koalas mignons regardant ensemble à distance ; l'un est assis devant un PC et l'autre est au lit avec un ordinateur portable, avec des cœurs volants entre eux",
|
||||
"USE_CASE_3_TITLE": "Relations à distance",
|
||||
"USE_CASE_3_DESC": "Comblez la distance et profitez de soirées en tête-à-tête. Vivez chaque rebondissement, riez des mêmes blagues et partagez des moments émotionnels à la milliseconde près.",
|
||||
|
||||
"WHY_TITLE": "Pourquoi KoalaSync ?",
|
||||
"WHY_SUBTITLE": "Conçu pour une synchronisation fiable, le respect de la vie privée et une configuration facile.",
|
||||
|
||||
"FEATURE_1_TITLE": "Contrôle total / Synchro instantanée",
|
||||
"FEATURE_1_DESC": "Un joueur met en pause, tout le monde met en pause. Un joueur avance, tout le monde suit. Notre protocole de synchronisation en deux phases coordonne la lecture en temps réel pour tous les participants.",
|
||||
"FEATURE_2_TITLE": "Binge-watching sans fin",
|
||||
"FEATURE_2_DESC": "Lecture automatique synchronisée. KoalaSync détecte automatiquement les transitions d'épisodes et suspend la lecture jusqu'à ce que tous les participants aient chargé la vidéo suivante.",
|
||||
"FEATURE_3_TITLE": "Aucun compte / Vie privée garantie",
|
||||
"FEATURE_3_DESC": "Pas d'inscription, pas de suivi, pas de stockage de données. Le serveur fonctionne entièrement dans une RAM éphémère et supprime votre salon dès votre départ.",
|
||||
"FEATURE_4_TITLE": "Support HTML5 universel",
|
||||
"FEATURE_4_DESC": "Fonctionne sur YouTube, Twitch, Netflix, Jellyfin, Emby et presque n'importe quelle page web standard contenant un élément vidéo HTML5. Également compatible avec les installations personnalisées.",
|
||||
"FEATURE_5_TITLE": "Hébergeable soi-même & prêt pour Docker",
|
||||
"FEATURE_5_DESC": "Nos serveurs officiels sont gratuits et rapides, mais vous pouvez prendre le contrôle total si vous le souhaitez. Lancez votre propre serveur relais privé en quelques secondes via Docker.",
|
||||
"FEATURE_6_TITLE": "Invitations instantanées / Rejoindre en 1 clic",
|
||||
"FEATURE_6_DESC": "Pas d'adresses IP ou de mots de passe à échanger. Partagez un lien d'invitation généré pour permettre à vos amis de rejoindre automatiquement votre salon en un seul clic.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync vs Teleparty",
|
||||
"COMP_SUBTITLE": "Découvrez pourquoi un outil open-source, sans publicité et respectueux de la vie privée est le meilleur choix pour regarder ensemble.",
|
||||
"COMP_COL_FEATURE": "Fonctionnalité",
|
||||
"COMP_FEAT_1_NAME": "Coût / Limites payantes",
|
||||
"COMP_FEAT_1_DESC": "Abonnements premium, frais cachés ou fonctionnalités verrouillées.",
|
||||
"COMP_FEAT_1_KOALA": "100% Gratuit",
|
||||
"COMP_FEAT_1_TELE": "Abonnements payants / Verrous Premium",
|
||||
"COMP_FEAT_2_NAME": "Licence / Code auditable",
|
||||
"COMP_FEAT_2_DESC": "Si le code source est ouvert et librement auditable.",
|
||||
"COMP_FEAT_2_KOALA": "Open Source (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Propriétaire",
|
||||
"COMP_FEAT_3_NAME": "Auto-hébergement",
|
||||
"COMP_FEAT_3_DESC": "Possibilité de déployer votre propre serveur relais privé.",
|
||||
"COMP_FEAT_3_KOALA": "Oui (prêt pour Docker)",
|
||||
"COMP_FEAT_3_TELE": "Non",
|
||||
"COMP_FEAT_4_NAME": "Vie privée & Suivi",
|
||||
"COMP_FEAT_4_DESC": "Obligation d'inscription, cookies de suivi et analyses.",
|
||||
"COMP_FEAT_4_KOALA": "Zéro persistance (RAM uniquement)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics & Cookies",
|
||||
"COMP_FEAT_5_NAME": "Compatibilité des sites",
|
||||
"COMP_FEAT_5_DESC": "Sites web pris en charge et compatibilité des lecteurs.",
|
||||
"COMP_FEAT_5_KOALA": "Presque toutes les vidéos HTML5",
|
||||
"COMP_FEAT_5_TELE": "Sites pris en charge uniquement",
|
||||
"COMP_FOOTNOTE_1": "État de la comparaison : mai 2026.",
|
||||
"COMP_FOOTNOTE_2": "Détails sur les tarifs officiels de Teleparty Premium et les réseaux pris en charge :",
|
||||
"COMP_FOOTNOTE_3": "Politiques de confidentialité et collecte de données de suivi de Teleparty :",
|
||||
"COMP_FOOTNOTE_4": "Fonctionne sur les sites web qui autorisent les injections de scripts dans les balises vidéo HTML5 standard. Les sites avec des politiques de sécurité du contenu (CSP) très strictes, une protection contre la copie DRM ou des conteneurs de lecteurs fortement obscurcis (comme des DOM fantômes complexes) peuvent restreindre le contrôle automatisé ou l'injection.",
|
||||
|
||||
"STEPS_TITLE": "Pour commencer",
|
||||
"STEP_1_TITLE": "Installer l'extension",
|
||||
"STEP_1_DESC": "Ajoutez KoalaSync à votre navigateur depuis le Chrome Web Store, les modules complémentaires de Firefox, ou téléchargez le dernier fichier ZIP développeur depuis GitHub.",
|
||||
"STEP_1_ILLUS_DESC": "Synchronisateur vidéo respectueux de la vie privée",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Télécharger pour Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Télécharger pour Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Extension active",
|
||||
"STEP_1_ILLUS_READY": "Prêt à regarder ensemble !",
|
||||
"STEP_2_TITLE": "Créer un salon",
|
||||
"STEP_2_DESC": "Ouvrez la fenêtre de l'extension et cliquez sur '+ Créer un nouveau salon'. KoalaSync génère automatiquement un identifiant de salon et un mot de passe sécurisés, s'y connecte et copie le lien d'invitation dans votre presse-papiers.",
|
||||
"STEP_2_ILLUS_ROOM": "Salon",
|
||||
"STEP_2_ILLUS_SYNC": "Synchro",
|
||||
"STEP_2_ILLUS_SETTINGS": "Options",
|
||||
"STEP_2_ILLUS_CREATE": "+ Créer un nouveau salon",
|
||||
"STEP_2_ILLUS_MANUAL": "Connexion manuelle / Avancé",
|
||||
"STEP_2_ILLUS_COPIED": "Lien d'invitation copié !",
|
||||
"STEP_3_TITLE": "Partager & Synchroniser",
|
||||
"STEP_3_DESC": "Envoyez le lien d'invitation à vos amis. Dès qu'ils rejoignent, sélectionnez votre onglet vidéo et profitez de la lecture synchronisée.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "SYNCHRONISÉ",
|
||||
|
||||
"SELF_TITLE": "Pour l'auto-hébergement",
|
||||
"SELF_SUBTITLE": "Vous ne faites pas confiance à notre serveur ? Conservez votre souveraineté totale sur les données. Déployez votre propre serveur relais en quelques minutes.",
|
||||
"SELF_MASCOT_ALT": "Un koala mignon assis devant un ordinateur portable déployant un conteneur Docker pour l'auto-hébergement",
|
||||
"SELF_COPY_CODE": "Copier le code",
|
||||
"SELF_GITHUB_PACKAGES": "Voir tous les tags d'images sur GitHub Packages",
|
||||
|
||||
"BOTTOM_TITLE": "Pas encore convaincu ? Voyez par vous-même.",
|
||||
"BOTTOM_SUBTITLE": "KoalaSync est 100% open source, sans publicité et sans suivi. Auditez notre base de code sur GitHub ou installez directement l'extension de navigateur.",
|
||||
"BOTTOM_MASCOT_ALT": "Un koala mignon tenant une page de dépôt GitHub à côté d'Octocat, la mascotte de GitHub",
|
||||
|
||||
"FOOTER_MIT": "Open source sous licence MIT.",
|
||||
"FOOTER_RAM": "Aucune donnée n'est stockée sur nos serveurs. Relais uniquement en RAM.",
|
||||
"FOOTER_LEGAL": "Legal Notice",
|
||||
"FOOTER_PRIVACY": "Privacy Policy"
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"LANG_CODE": "pt-BR",
|
||||
"HTML_CLASS": "lang-pt-br",
|
||||
"CANONICAL_PATH": "pt-BR/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
|
||||
"META_TITLE": "KoalaSync | Sincronize Netflix, YouTube e qualquer vídeo com amigos",
|
||||
"META_DESCRIPTION": "Assista à Netflix, YouTube, Twitch e qualquer vídeo HTML5 em perfeita sincronia com amigos. Extensão de navegador gratuita e de código aberto para Chrome e Firefox. Sem necessidade de registro.",
|
||||
"OG_TITLE": "KoalaSync | Sincronize Netflix, Emby, Jellyfin e quase qualquer vídeo com amigos",
|
||||
"OG_DESCRIPTION": "Assista à Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5 em perfeita sincronia. Extensão de navegador de código aberto e focada em privacidade para Chrome e Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Sincronize Netflix, Emby, Jellyfin e quase qualquer vídeo com amigos – Extensão de navegador",
|
||||
"TWITTER_DESCRIPTION": "Assista à Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5 em perfeita sincronia com amigos. Extensão de navegador de código aberto e focada em privacidade para Chrome e Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Recursos",
|
||||
"NAV_HOW_IT_WORKS": "Como funciona",
|
||||
|
||||
"HERO_TITLE": "Assistam juntos.<br>Sincronia perfeita.",
|
||||
"HERO_SUBTITLE": "Sua noite de cinema à distância sem atrasos. Sem registro, sem coleta de dados. Apenas compartilhe o link e assistam juntos.",
|
||||
"HERO_MASCOT_ALT": "Um lindo coala em pé olhando para baixo em direção aos botões de download",
|
||||
"ADD_TO_CHROME": "Adicionar ao Chrome",
|
||||
"ADD_TO_FIREFOX": "Adicionar ao Firefox",
|
||||
|
||||
"COMPAT_HEADING": "Funciona nas suas plataformas favoritas",
|
||||
"COMPAT_MORE": "e muitas mais",
|
||||
"COMPAT_TOOLTIP": "Funciona em quase qualquer site com um elemento de vídeo",
|
||||
|
||||
"USE_CASES_TITLE": "Perfeito para qualquer ocasião",
|
||||
"USE_CASES_SUBTITLE": "Seja perto ou longe, o KoalaSync une as pessoas em torno de seus vídeos favoritos.",
|
||||
"USE_CASE_1_ALT": "Dois coalas fofos sentados juntos e compartilhando um balde de pipoca",
|
||||
"USE_CASE_1_TITLE": "Noite de cinema com amigos",
|
||||
"USE_CASE_1_DESC": "Sincronize seus filmes em tempo real e converse pelo Discord, Zoom ou seu aplicativo de chamada de voz favorito. É como se estivessem na mesma sala.",
|
||||
"USE_CASE_2_ALT": "Um professor coala fazendo uma apresentação em uma tela para dois alunos coala remotos",
|
||||
"USE_CASE_2_TITLE": "Aprendizado remoto",
|
||||
"USE_CASE_2_DESC": "Analise tutoriais online, palestras ou transmissões de treinamento de desenvolvedores junto com colegas de classe ou de trabalho. Pause e discuta etapas complexas em perfeita harmonia.",
|
||||
"USE_CASE_3_ALT": "Um casal de coalas fofos assistindo juntos remotamente; um está no PC e o outro deitado com um laptop, com corações voando entre eles",
|
||||
"USE_CASE_3_TITLE": "Relacionamentos à distância",
|
||||
"USE_CASE_3_DESC": "Aproxime a distância e aproveite encontros virtuais. Experimente cada reviravolta, ria das mesmas piadas e compartilhe momentos emocionante no mesmo milissegundo.",
|
||||
|
||||
"WHY_TITLE": "Por que o KoalaSync?",
|
||||
"WHY_SUBTITLE": "Desenvolvido para uma sincronização confiável, privacidade em primeiro lugar e configuração simples.",
|
||||
|
||||
"FEATURE_1_TITLE": "Controle total / Sincronia instantânea",
|
||||
"FEATURE_1_DESC": "Um pausa, todos pausam. Um avança, todos seguem. Nosso protocolo de sincronização em duas fases coordena a reprodução em tempo real entre todos os participantes.",
|
||||
"FEATURE_2_TITLE": "Maratones sem fim",
|
||||
"FEATURE_2_DESC": "Reprodução automática sincronizada. O KoalaSync detecta automaticamente transições de episódios e pausa a reprodução até que todos os participantes tenham carregado o próximo vídeo.",
|
||||
"FEATURE_3_TITLE": "Sem contas / Privacidade absoluta",
|
||||
"FEATURE_3_DESC": "Sem registro, sem rastreamento, sem armazenamento de dados. O servidor roda inteiramente em memória RAM efêmera e limpa sua sala assim que você sai.",
|
||||
"FEATURE_4_TITLE": "Suporte universal a HTML5",
|
||||
"FEATURE_4_DESC": "Funciona no YouTube, Twitch, Netflix, Jellyfin, Emby e em quase qualquer página web padrão com um elemento de vídeo HTML5. Também é compatível com servidores próprios.",
|
||||
"FEATURE_5_TITLE": "Auto-hospedável e pronto para Docker",
|
||||
"FEATURE_5_DESC": "Nossos servidores oficiais são rápidos e gratuitos, mas você pode ter controle total se desejar. Inicie seu próprio servidor de retransmissão privado em segundos via Docker.",
|
||||
"FEATURE_6_TITLE": "Convites instantâneos / Entrada em 1 clique",
|
||||
"FEATURE_6_DESC": "Sem troca de endereços IP ou senhas. Compartilhe um link de convite gerado para que seus amigos entrem automaticamente na sua sala com um único clique.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync vs Teleparty",
|
||||
"COMP_SUBTITLE": "Veja por que o código aberto, sem anúncios e com privacidade em primeiro lugar é a melhor escolha para assistirem juntos.",
|
||||
"COMP_COL_FEATURE": "Recurso",
|
||||
"COMP_FEAT_1_NAME": "Custo / Bloqueios de recursos",
|
||||
"COMP_FEAT_1_DESC": "Assinaturas premium, taxas ocultas ou recursos bloqueados.",
|
||||
"COMP_FEAT_1_KOALA": "100% Gratuito",
|
||||
"COMP_FEAT_1_TELE": "Assinaturas pagas / Bloqueios Premium",
|
||||
"COMP_FEAT_2_NAME": "Licença / Código auditável",
|
||||
"COMP_FEAT_2_DESC": "Se o código-fonte é aberto e de livre auditoria.",
|
||||
"COMP_FEAT_2_KOALA": "Código Aberto (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Proprietário",
|
||||
"COMP_FEAT_3_NAME": "Auto-hospedagem",
|
||||
"COMP_FEAT_3_DESC": "Capacidade de implantar seu próprio servidor de retransmissão privado.",
|
||||
"COMP_FEAT_3_KOALA": "Sim (pronto para Docker)",
|
||||
"COMP_FEAT_3_TELE": "Não",
|
||||
"COMP_FEAT_4_NAME": "Privacidade e Rastreamento",
|
||||
"COMP_FEAT_4_DESC": "Exigências de registro, cookies de rastreamento e análises.",
|
||||
"COMP_FEAT_4_KOALA": "Sem persistência (apenas RAM)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics e Cookies",
|
||||
"COMP_FEAT_5_NAME": "Compatibilidade de sites",
|
||||
"COMP_FEAT_5_DESC": "Sites suportados e compatibilidade do player.",
|
||||
"COMP_FEAT_5_KOALA": "Quase qualquer vídeo HTML5",
|
||||
"COMP_FEAT_5_TELE": "Apenas sites suportados",
|
||||
"COMP_FOOTNOTE_1": "Estado da comparação: maio de 2026.",
|
||||
"COMP_FOOTNOTE_2": "Preços oficiais do Teleparty Premium e detalhes das redes suportadas:",
|
||||
"COMP_FOOTNOTE_3": "Políticas de privacidade do Teleparty e coleta de dados de rastreamento:",
|
||||
"COMP_FOOTNOTE_4": "Funciona em sites que permitem injeções de scripts em tags de vídeo HTML5 padrão. Sites com políticas de segurança de conteúdo (CSP) muito rígidas, proteção contra cópia DRM ou contêineres de player fortemente ocultos (como DOMs de sombra complexos) podem limitar o controle automatizado ou a injeção.",
|
||||
|
||||
"STEPS_TITLE": "Como começar",
|
||||
"STEP_1_TITLE": "Instalar a extensão",
|
||||
"STEP_1_DESC": "Adicione o KoalaSync ao seu navegador a partir da Chrome Web Store, Firefox Add-ons ou baixe o arquivo ZIP de desenvolvedor mais recente do GitHub.",
|
||||
"STEP_1_ILLUS_DESC": "Sincronizador de vídeo focado em privacidade",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Baixar para o Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Baixar para o Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Extensão ativa",
|
||||
"STEP_1_ILLUS_READY": "Prontos para assistir juntos!",
|
||||
|
||||
"STEP_2_TITLE": "Criar uma sala",
|
||||
"STEP_2_DESC": "Abra a janela da extensão e clique em '+ Criar nova sala'. O KoalaSync gera automaticamente um ID de sala e senha seguros, entra nela e copia o link de convite para a sua área de transferência.",
|
||||
"STEP_2_ILLUS_ROOM": "Sala",
|
||||
"STEP_2_ILLUS_SYNC": "Sincro",
|
||||
"STEP_2_ILLUS_SETTINGS": "Opções",
|
||||
"STEP_2_ILLUS_CREATE": "+ Criar nova sala",
|
||||
"STEP_2_ILLUS_MANUAL": "Conexão manual / Avançado",
|
||||
"STEP_2_ILLUS_COPIED": "Link de convite copiado!",
|
||||
|
||||
"STEP_3_TITLE": "Compartilhar e Sincronizar",
|
||||
"STEP_3_DESC": "Envie o link de convite para seus amigos. Assim que eles entrarem, selecione a aba do seu vídeo e aproveite a reprodução sincronizada.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZADO",
|
||||
|
||||
"SELF_TITLE": "Para quem hospeda próprio",
|
||||
"SELF_SUBTITLE": "Não confia no nosso servidor? Mantenha a soberania total dos seus dados. Implante seu próprio servidor de retransmissão em minutos.",
|
||||
"SELF_MASCOT_ALT": "Um coala fofo sentado à frente de um laptop implantando um contêiner Docker para hospedagem própria",
|
||||
"SELF_COPY_CODE": "Copiar código",
|
||||
"SELF_GITHUB_PACKAGES": "Ver todas as tags de imagens no GitHub Packages",
|
||||
|
||||
"BOTTOM_TITLE": "Ainda não se convenceu? Veja por si mesmo.",
|
||||
"BOTTOM_SUBTITLE": "O KoalaSync é 100% de código aberto, livre de anúncios e sem rastreamento. Audite nosso código no GitHub ou instale a extensão do navegador diretamente.",
|
||||
"BOTTOM_MASCOT_ALT": "Um coala fofo segurando uma página de repositório do GitHub ao lado de Octocat, a mascotte do GitHub",
|
||||
|
||||
"FOOTER_MIT": "Código aberto sob a Licença MIT.",
|
||||
"FOOTER_RAM": "Nenhum dado é armazenado em nossos servidores. Retransmissão apenas em RAM.",
|
||||
"FOOTER_LEGAL": "Legal Notice",
|
||||
"FOOTER_PRIVACY": "Privacy Policy"
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"LANG_CODE": "ru",
|
||||
"HTML_CLASS": "lang-ru",
|
||||
"CANONICAL_PATH": "ru/",
|
||||
"LANG_TOGGLE_URL": "../",
|
||||
"LANG_TOGGLE_TEXT": "EN",
|
||||
|
||||
"META_TITLE": "KoalaSync | Синхронизация Netflix, YouTube и любого видео с друзьями",
|
||||
"META_DESCRIPTION": "Смотрите Netflix, YouTube, Twitch и любые HTML5-видео в идеальной синхронизации с друзьями. Бесплатное расширение с открытым исходным кодом для Chrome и Firefox. Регистрация не требуется.",
|
||||
"OG_TITLE": "KoalaSync | Синхронизация Netflix, Emby, Jellyfin и почти любого видео с друзьями",
|
||||
"OG_DESCRIPTION": "Смотрите Netflix, Emby, Jellyfin, YouTube, Twitch и почти любое HTML5-видео в идеальной синхронизации. Открытое и ориентированное на конфиденциальность расширение для Chrome и Firefox.",
|
||||
"TWITTER_TITLE": "KoalaSync | Синхронизация Netflix, Emby, Jellyfin и почти любого видео с друзьями – Расширение",
|
||||
"TWITTER_DESCRIPTION": "Смотрите Netflix, Emby, Jellyfin, YouTube, Twitch и почти любое HTML5-видео в идеальной синхронизации с друзьями. Открытое и конфиденциальное расширение для Chrome и Firefox.",
|
||||
|
||||
"NAV_FEATURES": "Функции",
|
||||
"NAV_HOW_IT_WORKS": "Как это работает",
|
||||
|
||||
"HERO_TITLE": "Смотрите вместе.<br>Синхронно на 100%.",
|
||||
"HERO_SUBTITLE": "Ваш киновечер на расстоянии без задержек. Без регистрации и сбора данных. Просто поделитесь ссылкой и смотрите вместе.",
|
||||
"HERO_MASCOT_ALT": "Милый коала стоит и смотрит вниз на кнопки загрузки",
|
||||
"ADD_TO_CHROME": "Установить в Chrome",
|
||||
"ADD_TO_FIREFOX": "Установить в Firefox",
|
||||
|
||||
"COMPAT_HEADING": "Работает на ваших любимых платформах",
|
||||
"COMPAT_MORE": "и многих других",
|
||||
"COMPAT_TOOLTIP": "Работает почти на любом сайте с видеоэлементом",
|
||||
|
||||
"USE_CASES_TITLE": "Идеально для любого случая",
|
||||
"USE_CASES_SUBTITLE": "Близко или далеко — KoalaSync объединяет людей за просмотром любимых видео.",
|
||||
"USE_CASE_1_ALT": "Два милых коалы сидят вместе и делят ведро попкорна",
|
||||
"USE_CASE_1_TITLE": "Киноночь с друзьями",
|
||||
"USE_CASE_1_DESC": "Синхронизируйте фильмы в реальном времени и общайтесь в Discord, Zoom или вашей любимой программе. Это ощущается так, будто вы сидите в одной комнате.",
|
||||
"USE_CASE_2_ALT": "Профессор коала проводит презентацию на экране для двух удаленных студентов коал",
|
||||
"USE_CASE_2_TITLE": "Удаленное обучение",
|
||||
"USE_CASE_2_DESC": "Разбирайте обучающие видео, лекции или стримы для разработчиков вместе с однокурсниками или коллегами. Ставьте на паузу и обсуждайте сложные шаги в идеальном согласии.",
|
||||
"USE_CASE_3_ALT": "Милая пара коал смотрит видео удаленно; один сидит за ПК, другая в постели с ноутбуком, между ними летают сердечки",
|
||||
"USE_CASE_3_TITLE": "Отношения на расстоянии",
|
||||
"USE_CASE_3_DESC": "Сократите расстояние и наслаждайтесь свиданиями. Переживайте каждый поворот сюжета, смейтесь над шутками и делитесь эмоциями в одну и ту же миллисекунду.",
|
||||
|
||||
"WHY_TITLE": "Почему KoalaSync?",
|
||||
"WHY_SUBTITLE": "Создан для надежной синхронизации, конфиденциальности и простой настройки.",
|
||||
|
||||
"FEATURE_1_TITLE": "Полный контроль и мгновенный синхро",
|
||||
"FEATURE_1_DESC": "Один ставит на паузу — пауза у всех. Один перематывает — все перематывают. Наш двухфазный протокол координирует воспроизведение в реальном времени у всех участников.",
|
||||
"FEATURE_2_TITLE": "Бесконечные марафоны",
|
||||
"FEATURE_2_DESC": "Синхронное автовоспроизведение. KoalaSync автоматически определяет переход на следующую серию и ждет, пока она загрузится у всех участников.",
|
||||
"FEATURE_3_TITLE": "Без аккаунтов и хранения данных",
|
||||
"FEATURE_3_DESC": "Без регистрации, без отслеживания, без следов на диске. Сервер работает исключительно во временной RAM и полностью стирает вашу комнату при выходе.",
|
||||
"FEATURE_4_TITLE": "Универсальная поддержка HTML5",
|
||||
"FEATURE_4_DESC": "Работает на YouTube, Twitch, Netflix, Jellyfin, Emby и почти на любой веб-странице со стандартным HTML5-видеотегом. Подходит для локальных медиатек.",
|
||||
"FEATURE_5_TITLE": "Self-Hosted и готов к Docker",
|
||||
"FEATURE_5_DESC": "Наши официальные серверы бесплатны и быстры, но вы можете получить полный контроль. Запустите свой собственный приватный ретранслятор в Docker за секунды.",
|
||||
"FEATURE_6_TITLE": "Быстрые приглашения в 1 клик",
|
||||
"FEATURE_6_DESC": "Не нужно обмениваться IP-адресами или паролями. Отправьте сгенерированную ссылку друзьям, чтобы они автоматически вошли в комнату в один клик.",
|
||||
|
||||
"COMP_TITLE": "KoalaSync против Teleparty",
|
||||
"COMP_SUBTITLE": "Узнайте, почему открытый исходный код, отсутствие рекламы и конфиденциальность делают нас лучшим выбором для совместного просмотра.",
|
||||
"COMP_COL_FEATURE": "Функция",
|
||||
"COMP_FEAT_1_NAME": "Стоимость и ограничения",
|
||||
"COMP_FEAT_1_DESC": "Платные подписки, скрытые платежи или заблокированные функции.",
|
||||
"COMP_FEAT_1_KOALA": "100% Бесплатно",
|
||||
"COMP_FEAT_1_TELE": "Платные тарифы и ограничения",
|
||||
"COMP_FEAT_2_NAME": "Лицензия и аудит кода",
|
||||
"COMP_FEAT_2_DESC": "Открыт ли исходный код для свободного изучения и аудита безопасности.",
|
||||
"COMP_FEAT_2_KOALA": "Open Source (MIT)",
|
||||
"COMP_FEAT_2_TELE": "Проприетарный",
|
||||
"COMP_FEAT_3_NAME": "Self-Hosting",
|
||||
"COMP_FEAT_3_DESC": "Возможность развернуть свой собственный приватный сервер.",
|
||||
"COMP_FEAT_3_KOALA": "Да (готов к Docker)",
|
||||
"COMP_FEAT_3_TELE": "Нет",
|
||||
"COMP_FEAT_4_NAME": "Конфиденциальность и трекеры",
|
||||
"COMP_FEAT_4_DESC": "Обязательная регистрация, отслеживающие файлы cookie и аналитика.",
|
||||
"COMP_FEAT_4_KOALA": "Без сохранения (только RAM)",
|
||||
"COMP_FEAT_4_TELE": "Google Analytics и Cookies",
|
||||
"COMP_FEAT_5_NAME": "Совместимость с сайтами",
|
||||
"COMP_FEAT_5_DESC": "Поддерживаемые веб-ресурсы и совместимость плееров.",
|
||||
"COMP_FEAT_5_KOALA": "Почти любое HTML5 видео",
|
||||
"COMP_FEAT_5_TELE": "Только поддерживаемые сайты",
|
||||
"COMP_FOOTNOTE_1": "Состояние сравнения: май 2026.",
|
||||
"COMP_FOOTNOTE_2": "Официальные цены Teleparty Premium и поддерживаемые сети:",
|
||||
"COMP_FOOTNOTE_3": "Политика конфиденциальности Teleparty и сбор трекеров:",
|
||||
"COMP_FOOTNOTE_4": "Работает на сайтах, разрешающих инъекцию скриптов в стандартные видеотеги HTML5. Сайты с очень строгой политикой безопасности контента (CSP), защитой от копирования DRM или сильно обфусцированными оболочками плееров (например, сложные теневые DOM) могут блокировать автоматическое управление.",
|
||||
|
||||
"STEPS_TITLE": "С чего начать",
|
||||
"STEP_1_TITLE": "Установить расширение",
|
||||
"STEP_1_DESC": "Добавьте KoalaSync в браузер из Chrome Web Store, Firefox Add-ons or скачайте последний ZIP-архив разработчика с GitHub.",
|
||||
"STEP_1_ILLUS_DESC": "Ориентированный на конфиденциальность синхронизатор видео",
|
||||
"STEP_1_ILLUS_DL_CHROME": "Скачать для Chrome",
|
||||
"STEP_1_ILLUS_DL_FIREFOX": "Скачать для Firefox",
|
||||
"STEP_1_ILLUS_ACTIVE": "Расширение активно",
|
||||
"STEP_1_ILLUS_READY": "Готовы смотреть вместе!",
|
||||
|
||||
"STEP_2_TITLE": "Создать комнату",
|
||||
"STEP_2_DESC": "Откройте меню расширения и нажмите «+ Создать новую комнату». KoalaSync автоматически сгенерирует ID и пароль, войдет в нее и скопирует ссылку-приглашение.",
|
||||
"STEP_2_ILLUS_ROOM": "Комната",
|
||||
"STEP_2_ILLUS_SYNC": "Синхро",
|
||||
"STEP_2_ILLUS_SETTINGS": "Опции",
|
||||
"STEP_2_ILLUS_CREATE": "+ Создать комнату",
|
||||
"STEP_2_ILLUS_MANUAL": "Ручное подключение / Дополнительно",
|
||||
"STEP_2_ILLUS_COPIED": "Ссылка скопирована!",
|
||||
|
||||
"STEP_3_TITLE": "Поделиться и смотреть",
|
||||
"STEP_3_DESC": "Отправьте ссылку друзьям. Как только они присоединятся, выберите вкладку с видео и наслаждайтесь синхронным просмотром.",
|
||||
"STEP_3_ILLUS_IN_SYNC": "В СИНХРОНЕ",
|
||||
|
||||
"SELF_TITLE": "Для селф-хостеров",
|
||||
"SELF_SUBTITLE": "Не доверяете нашему серверу? Сохраняйте полный контроль над данными. Разверните собственный приватный сервер за пару минут.",
|
||||
"SELF_MASCOT_ALT": "Милый коала сидит за ноутбуком и разворачивает контейнер Docker для селф-хостинга",
|
||||
"SELF_COPY_CODE": "Копировать код",
|
||||
"SELF_GITHUB_PACKAGES": "Посмотреть все теги образов на GitHub Packages",
|
||||
|
||||
"BOTTOM_TITLE": "Все еще сомневаетесь? Убедитесь сами.",
|
||||
"BOTTOM_SUBTITLE": "KoalaSync на 100% бесплатен, открыт и безопасен. Изучите наш код на GitHub или установите расширение в браузер напрямую.",
|
||||
"BOTTOM_MASCOT_ALT": "Милый коала держит страницу репозитория GitHub рядом с маскотом GitHub Octocat",
|
||||
|
||||
"FOOTER_MIT": "Открытый исходный код под лицензией MIT.",
|
||||
"FOOTER_RAM": "Никакие данные не сохраняются на наших серверах. Только RAM-трансляция.",
|
||||
"FOOTER_LEGAL": "Legal Notice",
|
||||
"FOOTER_PRIVACY": "Privacy Policy"
|
||||
}
|
||||
@@ -6,15 +6,45 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/de/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/fr/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/es/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/pt-BR/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/ru/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/impressum.html</loc>
|
||||
<lastmod>2026-05-30</lastmod>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/datenschutz.html</loc>
|
||||
<lastmod>2026-05-30</lastmod>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
--accent: #6366f1;
|
||||
--accent-glow: rgba(99, 102, 241, 0.3);
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--text-muted: #cbd5e1; /* Increased contrast for WCAG AA (6.28:1 contrast ratio) */
|
||||
--success: #22c55e;
|
||||
--glass: rgba(30, 41, 59, 0.4);
|
||||
--glass-border: rgba(255, 255, 255, 0.05);
|
||||
@@ -698,20 +698,25 @@ input:checked + .mock-slider:before {
|
||||
}
|
||||
|
||||
/* --- Features --- */
|
||||
/* Bento Grid Layout Configuration */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 2rem;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1.5rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--card);
|
||||
background: rgba(30, 41, 59, 0.45);
|
||||
backdrop-filter: blur(16px) saturate(120%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(120%);
|
||||
padding: 2.5rem;
|
||||
border-radius: 24px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.feature-card::before {
|
||||
@@ -720,7 +725,7 @@ input:checked + .mock-slider:before {
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
border-radius: 24px;
|
||||
padding: 1px;
|
||||
background: linear-gradient(to bottom right, rgba(99, 102, 241, 0.25), transparent);
|
||||
background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.12), transparent);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
@@ -729,28 +734,80 @@ input:checked + .mock-slider:before {
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 30px rgba(99, 102, 241, 0.15);
|
||||
border-color: rgba(99, 102, 241, 0.2);
|
||||
box-shadow: 0 15px 30px rgba(99, 102, 241, 0.2);
|
||||
border-color: rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 1.6rem;
|
||||
margin-right: 0.6rem;
|
||||
display: inline-block;
|
||||
.feature-card:hover::before {
|
||||
background: linear-gradient(to bottom right, rgba(99, 102, 241, 0.3), transparent);
|
||||
}
|
||||
|
||||
/* Feature Icon with Inline SVG Wrapper */
|
||||
.feature-icon-svg {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
background: rgba(99, 102, 241, 0.12);
|
||||
color: var(--accent);
|
||||
border-radius: 12px;
|
||||
margin-right: 12px;
|
||||
vertical-align: middle;
|
||||
filter: drop-shadow(0 2px 8px rgba(0,0,0,0.2));
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-card:hover .feature-icon-svg {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
box-shadow: 0 0 15px var(--accent-glow);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.bento-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-bottom: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Bento Asymmetric Spanning */
|
||||
.feature-card.bento-large {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(135deg, rgba(30, 41, 59, 0.45) 0%, rgba(99, 102, 241, 0.08) 100%);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.features-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.25rem;
|
||||
}
|
||||
.feature-card.bento-large {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
.feature-card.bento-large {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- How it works --- */
|
||||
@@ -1709,21 +1766,84 @@ footer {
|
||||
}
|
||||
|
||||
/* --- Language Toggle --- */
|
||||
html.lang-en [lang="de"],
|
||||
html.lang-de [lang="en"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.lang-toggle {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
color: var(--text-muted);
|
||||
transition: color 0.3s;
|
||||
html:not(.lang-de) [lang="de"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.lang-toggle:hover {
|
||||
color: var(--accent);
|
||||
/* --- Modern Glassmorphic Language Selector --- */
|
||||
.lang-select-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(30, 41, 59, 0.4);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 9999px;
|
||||
padding: 6px 14px 6px 12px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.lang-select-container:hover {
|
||||
background: rgba(30, 41, 59, 0.6);
|
||||
border-color: rgba(99, 102, 241, 0.3);
|
||||
color: var(--text);
|
||||
box-shadow: 0 0 15px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
.lang-select-container .globe-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.8;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.lang-select-container:hover .globe-icon {
|
||||
transform: rotate(15deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.lang-select-container .chevron-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex-shrink: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
.lang-select-container:hover .chevron-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: currentColor;
|
||||
cursor: pointer;
|
||||
padding: 0 12px 0 0;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.lang-dropdown option {
|
||||
background: #0f172a;
|
||||
color: white;
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* --- Hamburger Menu --- */
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
// KoalaSync Landing Page Logic
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Scroll Reveal Logic (IntersectionObserver for performance)
|
||||
const revealElements = document.querySelectorAll('[data-reveal]');
|
||||
|
||||
const revealObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('revealed');
|
||||
revealObserver.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
rootMargin: '0px 0px -150px 0px',
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
revealElements.forEach(el => revealObserver.observe(el));
|
||||
|
||||
// Navbar scroll effect
|
||||
const nav = document.querySelector('nav');
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.scrollY > 50) {
|
||||
nav.style.padding = '0.75rem 0';
|
||||
nav.style.background = 'rgba(15, 23, 42, 0.9)';
|
||||
} else {
|
||||
nav.style.padding = '1rem 0';
|
||||
nav.style.background = 'rgba(30, 41, 59, 0.7)';
|
||||
}
|
||||
});
|
||||
|
||||
// Smooth scroll for anchors
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Invite Detection & Bridge
|
||||
const checkInvite = () => {
|
||||
const isJoinPage = window.location.pathname.includes('join');
|
||||
|
||||
// Dev Simulation Mode via URL Search Parameter (?dev=success) or Hash (#dev=success / #devsuccess)
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let devMode = urlParams.get('dev');
|
||||
|
||||
if (!devMode) {
|
||||
const hashClean = window.location.hash.startsWith('#') ? window.location.hash.substring(1) : window.location.hash;
|
||||
const hashParams = new URLSearchParams(hashClean);
|
||||
devMode = hashParams.get('dev');
|
||||
}
|
||||
|
||||
if (!devMode) {
|
||||
if (window.location.hash.includes('devsuccess') || window.location.search.includes('devsuccess')) devMode = 'success';
|
||||
if (window.location.hash.includes('devfailure') || window.location.search.includes('devfailure')) devMode = 'failure';
|
||||
}
|
||||
|
||||
if (isJoinPage && devMode) {
|
||||
setTimeout(() => {
|
||||
const displayRoom = document.getElementById('display-room-id');
|
||||
const actions = document.getElementById('join-actions');
|
||||
if (displayRoom) displayRoom.textContent = 'DEV-ROOM';
|
||||
|
||||
if (actions) {
|
||||
actions.innerHTML = `
|
||||
<div class="joining-spinner" style="text-align:center; padding: 1rem;">
|
||||
<div class="join-spinner"></div>
|
||||
<div style="font-weight: 600; color: var(--accent);">
|
||||
<span lang="en">Simulating connection (DEV)...</span><span lang="de">Verbindung wird simuliert (DEV)...</span>
|
||||
</div>
|
||||
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem;">
|
||||
<span lang="en">Simulating status event in 1.5 seconds.</span><span lang="de">Status-Event wird in 1,5 Sekunden simuliert.</span>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new CustomEvent('KOALASYNC_STATUS', {
|
||||
detail: {
|
||||
success: devMode === 'success',
|
||||
message: devMode === 'failure' ? 'Simulated Connection Timeout!' : ''
|
||||
}
|
||||
}));
|
||||
}, 1500);
|
||||
}
|
||||
}, 600);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a short timeout to let the bridge script initialize its dataset attribute
|
||||
setTimeout(() => {
|
||||
const isInstalled = document.documentElement.dataset.koalasyncInstalled === 'true';
|
||||
|
||||
if (window.location.hash.startsWith('#join:')) {
|
||||
const parts = window.location.hash.split(':');
|
||||
if (parts.length >= 3) {
|
||||
const roomId = parts[1];
|
||||
const password = parts[2];
|
||||
const serverFlag = parts[3] || '0';
|
||||
const serverUrl = parts[4] ? decodeURIComponent(parts[4]) : '';
|
||||
|
||||
if (isJoinPage) {
|
||||
const displayRoom = document.getElementById('display-room-id');
|
||||
const actions = document.getElementById('join-actions');
|
||||
if (displayRoom) displayRoom.textContent = roomId;
|
||||
|
||||
if (actions) {
|
||||
if (!isInstalled) {
|
||||
const isFirefox = navigator.userAgent.includes('Firefox');
|
||||
if (isFirefox) {
|
||||
actions.innerHTML = `
|
||||
<div class="join-card-actions">
|
||||
<a href="https://addons.mozilla.org/de/firefox/addon/koalasync/" class="btn btn-primary btn-firefox">
|
||||
<img src="assets/firefox.svg" alt="Firefox" width="20" style="display: block;">
|
||||
<span lang="en">GET IT ON MOZILLA ADD-ONS</span><span lang="de">IM FIREFOX ADD-ON STORE HERUNTERLADEN</span>
|
||||
</a>
|
||||
<a href="https://github.com/shik3i/KoalaSync" target="_blank" class="btn btn-secondary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
<span lang="en">Download via GitHub</span><span lang="de">Über GitHub herunterladen</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="text-align:center; font-size:0.8rem; opacity:0.7; margin-top: 1.2rem; color: var(--text-muted);">
|
||||
<span lang="en">The extension is required to join and sync videos.</span>
|
||||
<span lang="de">Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.</span>
|
||||
</p>
|
||||
`;
|
||||
} else {
|
||||
actions.innerHTML = `
|
||||
<div class="join-card-actions">
|
||||
<a href="https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc" class="btn btn-primary">
|
||||
<img src="assets/chrome.svg" alt="Chrome" width="20" style="display: block;">
|
||||
<span lang="en">GET IT ON CHROME WEBSTORE</span><span lang="de">IM CHROME WEB STORE HERUNTERLADEN</span>
|
||||
</a>
|
||||
<a href="https://github.com/shik3i/KoalaSync" target="_blank" class="btn btn-secondary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
<span lang="en">Download via GitHub</span><span lang="de">Über GitHub herunterladen</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="text-align:center; font-size:0.8rem; opacity:0.7; margin-top: 1.2rem; color: var(--text-muted);">
|
||||
<span lang="en">The extension is required to join and sync videos.</span>
|
||||
<span lang="de">Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.</span>
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
actions.innerHTML = `
|
||||
<div class="joining-spinner" style="text-align:center; padding: 1rem;">
|
||||
<div class="join-spinner"></div>
|
||||
<div style="font-weight: 600; color: var(--accent);">
|
||||
<span lang="en">Joining room automatically...</span><span lang="de">Raum wird automatisch betreten...</span>
|
||||
</div>
|
||||
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem;">
|
||||
<span lang="en">Your extension is taking care of it.</span><span lang="de">Deine Erweiterung kümmert sich darum.</span>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// AUTO-TRIGGER JOIN
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new CustomEvent('KOALASYNC_JOIN_REQUEST', {
|
||||
detail: {
|
||||
roomId,
|
||||
password,
|
||||
useCustomServer: serverFlag === '1',
|
||||
serverUrl: serverUrl
|
||||
}
|
||||
}));
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback banner for index.html
|
||||
if (!document.getElementById('koala-banner')) {
|
||||
const banner = document.createElement('div');
|
||||
banner.className = 'invite-banner';
|
||||
banner.id = 'koala-banner';
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'container';
|
||||
container.style.cssText = 'display:flex; justify-content:space-between; align-items:center;';
|
||||
|
||||
const inviteSpan = document.createElement('span');
|
||||
inviteSpan.appendChild(document.createTextNode('🎫 Invitation for '));
|
||||
const boldRoom = document.createElement('b');
|
||||
boldRoom.textContent = roomId;
|
||||
inviteSpan.appendChild(boldRoom);
|
||||
inviteSpan.appendChild(document.createTextNode(' detected!'));
|
||||
|
||||
const joinLink = document.createElement('a');
|
||||
joinLink.href = 'join.html' + window.location.hash;
|
||||
joinLink.className = 'btn-banner';
|
||||
joinLink.textContent = 'OPEN JOIN PAGE';
|
||||
|
||||
container.appendChild(inviteSpan);
|
||||
container.appendChild(joinLink);
|
||||
banner.appendChild(container);
|
||||
document.body.prepend(banner);
|
||||
}
|
||||
}
|
||||
|
||||
// Global listener for Join Button
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target && e.target.id === 'webJoinBtn') {
|
||||
e.target.textContent = 'JOINING...';
|
||||
e.target.disabled = true;
|
||||
window.dispatchEvent(new CustomEvent('KOALASYNC_JOIN_REQUEST', {
|
||||
detail: {
|
||||
roomId,
|
||||
password,
|
||||
useCustomServer: serverFlag === '1',
|
||||
serverUrl: serverUrl
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 600); // 600ms delay to ensure bridge.js has set the dataset
|
||||
};
|
||||
|
||||
// Listen for status from Extension
|
||||
window.addEventListener('KOALASYNC_STATUS', (e) => {
|
||||
const { success, message } = e.detail;
|
||||
const isJoinPage = window.location.pathname.includes('join');
|
||||
|
||||
if (isJoinPage) {
|
||||
const icon = document.getElementById('join-status-icon');
|
||||
const title = document.getElementById('join-title');
|
||||
const actions = document.getElementById('join-actions');
|
||||
const desc = document.getElementById('join-desc');
|
||||
const ring = document.getElementById('status-ring');
|
||||
|
||||
if (success) {
|
||||
if (ring) {
|
||||
ring.classList.remove('active-pulse');
|
||||
ring.style.display = 'none';
|
||||
}
|
||||
if (icon) {
|
||||
icon.innerHTML = '<img src="assets/KoalaThumbsUp.webp" alt="Success" class="join-status-mascot">';
|
||||
icon.style.transform = 'scale(1)';
|
||||
}
|
||||
const isDE = document.documentElement.classList.contains('lang-de');
|
||||
title.textContent = isDE ? 'Erfolgreich!' : 'Success!';
|
||||
desc.innerHTML = isDE
|
||||
? 'Verbunden! <br><span style="color:var(--accent); font-weight:bold;">Wähle jetzt einen Video-Tab in der Erweiterung aus.</span>'
|
||||
: 'Connected! <br><span style="color:var(--accent); font-weight:bold;">Now select a video tab in the extension.</span>';
|
||||
|
||||
let count = 3;
|
||||
const updateCountdown = () => {
|
||||
if (count <= 0) {
|
||||
window.close();
|
||||
desc.textContent = isDE ? 'Beitritt erfolgreich! Du kannst diesen Tab jetzt manuell schließen.' : 'Joined successfully! You can close this tab manually.';
|
||||
} else {
|
||||
count--;
|
||||
setTimeout(updateCountdown, 1000);
|
||||
}
|
||||
};
|
||||
setTimeout(updateCountdown, 1000);
|
||||
|
||||
const closeLabel = isDE ? 'TAB JETZT SCHLIESSEN' : 'CLOSE TAB NOW';
|
||||
actions.innerHTML = `
|
||||
<div class="join-card-actions">
|
||||
<button class="btn btn-success" onclick="window.close()">${closeLabel}</button>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
if (ring) {
|
||||
ring.classList.remove('active-pulse');
|
||||
ring.style.display = 'none';
|
||||
}
|
||||
if (icon) {
|
||||
icon.innerHTML = '<img src="assets/KoalaThumbsDown.webp" alt="Error" class="join-status-mascot" onerror="this.outerHTML=\'❌\'">';
|
||||
icon.style.transform = 'scale(1)';
|
||||
}
|
||||
const isDE = document.documentElement.classList.contains('lang-de');
|
||||
title.textContent = isDE ? 'Fehler' : 'Error';
|
||||
desc.textContent = isDE ? `Beitritt fehlgeschlagen: ${message}` : `Join failed: ${message}`;
|
||||
const retryLabel = isDE ? 'ERNEUT VERSUCHEN' : 'TRY AGAIN';
|
||||
actions.innerHTML = `
|
||||
<div class="join-card-actions">
|
||||
<button class="btn btn-primary" onclick="location.reload()">${retryLabel}</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
const banner = document.getElementById('koala-banner');
|
||||
if (banner) {
|
||||
if (success) {
|
||||
banner.style.background = 'var(--success)';
|
||||
banner.innerHTML = '<div class="container">✅ Joined! This tab will close in 2s...</div>';
|
||||
setTimeout(() => window.close(), 2000);
|
||||
} else {
|
||||
banner.style.background = 'var(--error)';
|
||||
banner.innerHTML = '';
|
||||
const errDiv = document.createElement('div');
|
||||
errDiv.className = 'container';
|
||||
errDiv.textContent = '❌ Error: ' + message;
|
||||
banner.appendChild(errDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const updateDynamicVersion = async () => {
|
||||
try {
|
||||
const versionPath = document.documentElement.lang === 'de' ? '../version.json' : 'version.json';
|
||||
const response = await fetch(versionPath);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
const { version, date } = data;
|
||||
if (!version || !date) return;
|
||||
|
||||
const releaseDate = new Date(date);
|
||||
const now = new Date();
|
||||
const diffMs = now - releaseDate;
|
||||
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
const diffMins = Math.floor(diffMs / (1000 * 60));
|
||||
|
||||
let relativeTimeEn = '';
|
||||
let relativeTimeDe = '';
|
||||
|
||||
if (diffDays > 0) {
|
||||
relativeTimeEn = `${diffDays} ${diffDays === 1 ? 'day' : 'days'} ago`;
|
||||
relativeTimeDe = `vor ${diffDays} ${diffDays === 1 ? 'Tag' : 'Tagen'}`;
|
||||
} else if (diffHours > 0) {
|
||||
relativeTimeEn = `${diffHours} ${diffHours === 1 ? 'hour' : 'hours'} ago`;
|
||||
relativeTimeDe = `vor ${diffHours} ${diffHours === 1 ? 'Stunde' : 'Stunden'}`;
|
||||
} else if (diffMins > 0) {
|
||||
relativeTimeEn = `${diffMins} ${diffMins === 1 ? 'minute' : 'minutes'} ago`;
|
||||
relativeTimeDe = `vor ${diffMins} ${diffMins === 1 ? 'Minute' : 'Minuten'}`;
|
||||
} else {
|
||||
relativeTimeEn = 'just now';
|
||||
relativeTimeDe = 'gerade eben';
|
||||
}
|
||||
|
||||
const badgeEn = document.querySelector('.version-text-en');
|
||||
const badgeDe = document.querySelector('.version-text-de');
|
||||
|
||||
if (badgeEn) {
|
||||
badgeEn.textContent = `v${version} OUT NOW • ${relativeTimeEn}`;
|
||||
}
|
||||
if (badgeDe) {
|
||||
badgeDe.textContent = `v${version} JETZT VERFÜGBAR • ${relativeTimeDe}`;
|
||||
}
|
||||
|
||||
// Update Schema.org structured data dynamically
|
||||
const schemaScript = document.getElementById('schema-software');
|
||||
if (schemaScript) {
|
||||
try {
|
||||
const schema = JSON.parse(schemaScript.textContent);
|
||||
schema.softwareVersion = version;
|
||||
schemaScript.textContent = JSON.stringify(schema, null, 2);
|
||||
} catch (err) {
|
||||
console.warn('Failed to dynamically update schema version:', err);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to fetch dynamic version info:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Extension Mockup Tab Switcher
|
||||
const mockTabs = document.querySelectorAll('.mock-tab');
|
||||
const mockScreens = document.querySelectorAll('.mock-screen');
|
||||
|
||||
mockTabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
mockTabs.forEach(t => t.classList.remove('active'));
|
||||
mockScreens.forEach(s => s.classList.remove('active'));
|
||||
|
||||
tab.classList.add('active');
|
||||
const targetId = tab.getAttribute('data-target');
|
||||
const targetScreen = document.getElementById(targetId);
|
||||
if (targetScreen) {
|
||||
targetScreen.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Terminal Tab Switcher
|
||||
const termTabBtns = document.querySelectorAll('.terminal-tab-btn');
|
||||
const termPanes = document.querySelectorAll('.terminal-pane');
|
||||
|
||||
termTabBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
termTabBtns.forEach(b => b.classList.remove('active'));
|
||||
termPanes.forEach(p => p.classList.remove('active'));
|
||||
|
||||
btn.classList.add('active');
|
||||
const targetPaneId = btn.getAttribute('data-tab');
|
||||
const targetPane = document.getElementById(targetPaneId);
|
||||
if (targetPane) {
|
||||
targetPane.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Terminal Clipboard Copy
|
||||
const copyBtn = document.querySelector('.terminal-copy-btn');
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener('click', () => {
|
||||
const activePane = document.querySelector('.terminal-pane.active');
|
||||
if (!activePane) return;
|
||||
const codeElement = activePane.querySelector('code');
|
||||
if (!codeElement) return;
|
||||
|
||||
const textToCopy = codeElement.innerText || codeElement.textContent;
|
||||
|
||||
navigator.clipboard.writeText(textToCopy).then(() => {
|
||||
const isDE = document.documentElement.classList.contains('lang-de');
|
||||
const originalHTML = copyBtn.innerHTML;
|
||||
|
||||
copyBtn.innerHTML = isDE ? '✅ Kopiert!' : '✅ Copied!';
|
||||
copyBtn.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = originalHTML;
|
||||
copyBtn.disabled = false;
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile Hamburger Menu Toggle
|
||||
const hamburger = document.querySelector('.hamburger');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
if (hamburger && navLinks) {
|
||||
// Initialize accessibility attribute
|
||||
hamburger.setAttribute('aria-expanded', 'false');
|
||||
|
||||
hamburger.addEventListener('click', () => {
|
||||
const isOpen = navLinks.classList.toggle('open');
|
||||
hamburger.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
// Dynamically localize home links on root dynamic pages (impressum, datenschutz, join)
|
||||
const localizeHomeLinks = () => {
|
||||
const html = document.documentElement;
|
||||
const activeLang = localStorage.getItem('koala_lang') || (navigator.language.startsWith('de') ? 'de' : 'en');
|
||||
const path = window.location.pathname;
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
// Only need to do this dynamic rewrite if we are NOT already inside a localized subdirectory
|
||||
if (!isSubdir) {
|
||||
const homeLinks = document.querySelectorAll('a[href="index.html"], a[href="de/index.html"], a[href="fr/index.html"], a[href="es/index.html"], a[href="pt-BR/index.html"], a[href="ru/index.html"]');
|
||||
homeLinks.forEach(link => {
|
||||
link.href = (activeLang === 'en') ? 'index.html' : `${activeLang}/index.html`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Modern Language Selector Navigation and State Toggling
|
||||
const handleLanguageChange = (e) => {
|
||||
const select = e.currentTarget;
|
||||
const newLang = select.value;
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Save the user's preference
|
||||
localStorage.setItem('koala_lang', newLang);
|
||||
|
||||
// Determine if we are on a static landing page versus a dynamic utility page
|
||||
const isLegalOrJoin = path.includes('impressum') || path.includes('datenschutz') || path.includes('join');
|
||||
const isIndex = !isLegalOrJoin;
|
||||
|
||||
if (isIndex) {
|
||||
// Static navigation: Route to correct subdirectory
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
let targetPath;
|
||||
if (newLang === 'en') {
|
||||
if (isSubdir) {
|
||||
targetPath = '../index.html';
|
||||
} else {
|
||||
targetPath = 'index.html';
|
||||
}
|
||||
} else {
|
||||
if (isSubdir) {
|
||||
// Switching from one language subdirectory to another (e.g., /de/ to /fr/)
|
||||
targetPath = '../' + newLang + '/index.html';
|
||||
} else {
|
||||
// Switching from root (English) to a language subdirectory (e.g., / to /fr/)
|
||||
targetPath = newLang + '/index.html';
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = targetPath;
|
||||
} else {
|
||||
// Dynamic page: Toggle classes and update elements dynamically without navigating away
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('lang-en', 'lang-de', 'lang-fr', 'lang-es', 'lang-pt-br', 'lang-ru');
|
||||
|
||||
// Fallback dynamic pages to 'en' if 'de' is not chosen (since fr/es markup is not present)
|
||||
const activeDisplayLang = (newLang === 'de') ? 'de' : 'en';
|
||||
html.classList.add('lang-' + activeDisplayLang);
|
||||
html.lang = activeDisplayLang;
|
||||
|
||||
// Sync all selects on the page to the new value
|
||||
document.querySelectorAll('.lang-dropdown').forEach(sel => {
|
||||
sel.value = newLang;
|
||||
});
|
||||
|
||||
// Update titles dynamically
|
||||
const isJoin = path.includes('join');
|
||||
if (isJoin) {
|
||||
const titles = { en: 'Join Room | KoalaSync', de: 'Raum beitreten | KoalaSync' };
|
||||
document.title = titles[activeDisplayLang] || titles.en;
|
||||
}
|
||||
|
||||
// Localize home links dynamically
|
||||
localizeHomeLinks();
|
||||
}
|
||||
};
|
||||
|
||||
// Register change event listener for the dropdowns
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.addEventListener('change', handleLanguageChange);
|
||||
});
|
||||
|
||||
// Initialize language select elements to show the current preferred language
|
||||
const initLanguageSelectorValue = () => {
|
||||
const savedLang = localStorage.getItem('koala_lang');
|
||||
const browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
const activePref = savedLang || browserLang;
|
||||
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.value = activePref;
|
||||
});
|
||||
};
|
||||
|
||||
// Impressum Email Obfuscation Click Reveal
|
||||
document.querySelectorAll('.email-reveal').forEach(el => {
|
||||
el.addEventListener('click', function() {
|
||||
const user = this.getAttribute('data-user');
|
||||
const domain = this.getAttribute('data-domain');
|
||||
if (user && domain) {
|
||||
this.innerHTML = `${user}@${domain}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Automated Store/Local Badge Linking based on User-Agent
|
||||
const detectBrowserAndElevateBadge = () => {
|
||||
const isFirefox = navigator.userAgent.includes('Firefox');
|
||||
const isChrome = navigator.userAgent.includes('Chrome') || navigator.userAgent.includes('Chromium');
|
||||
const chromeBtns = document.querySelectorAll('.btn-primary');
|
||||
const firefoxBtns = document.querySelectorAll('.btn-firefox');
|
||||
|
||||
if (isFirefox && chromeBtns.length > 0 && firefoxBtns.length > 0) {
|
||||
// User is on Firefox: Elevate Firefox button to primary, make Chrome secondary
|
||||
chromeBtns.forEach(btn => {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-secondary');
|
||||
});
|
||||
|
||||
firefoxBtns.forEach(btn => {
|
||||
// Put Firefox first in visual order
|
||||
btn.style.order = '-1';
|
||||
|
||||
// Add subtle focus scale effect
|
||||
btn.style.transform = 'scale(1.05)';
|
||||
btn.addEventListener('mouseleave', () => {
|
||||
btn.style.transform = 'scale(1)';
|
||||
});
|
||||
btn.addEventListener('mouseenter', () => {
|
||||
btn.style.transform = 'scale(1.05) translateY(-2px)';
|
||||
});
|
||||
});
|
||||
} else if (isChrome && chromeBtns.length > 0 && firefoxBtns.length > 0) {
|
||||
// User is on Chrome: Make Firefox secondary
|
||||
firefoxBtns.forEach(btn => {
|
||||
btn.classList.remove('btn-firefox');
|
||||
btn.classList.add('btn-secondary');
|
||||
btn.style.color = 'var(--text)';
|
||||
btn.style.background = 'var(--card)';
|
||||
btn.style.border = '1px solid var(--glass-border)';
|
||||
btn.style.boxShadow = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Handle Step 1 Landing Page Download Badges & Nav Badge
|
||||
setTimeout(() => {
|
||||
const isInstalled = document.documentElement.dataset.koalasyncInstalled === 'true';
|
||||
|
||||
// Nav Badge Logic
|
||||
const navBadge = document.getElementById('nav-extension-status');
|
||||
if (isInstalled && navBadge) {
|
||||
navBadge.style.display = 'inline-flex';
|
||||
}
|
||||
|
||||
const illusChrome = document.querySelectorAll('.illus-store-btn.chrome');
|
||||
const illusFirefox = document.querySelectorAll('.illus-store-btn.firefox');
|
||||
|
||||
if (isFirefox && illusFirefox.length > 0) {
|
||||
illusFirefox.forEach(btn => {
|
||||
btn.style.order = '-1';
|
||||
if (!isInstalled) {
|
||||
btn.classList.add('install-breathe');
|
||||
btn.style.cursor = 'pointer';
|
||||
btn.onclick = () => window.open('https://addons.mozilla.org/de/firefox/addon/koalasync/', '_blank');
|
||||
}
|
||||
});
|
||||
illusChrome.forEach(btn => {
|
||||
btn.style.opacity = '0.5';
|
||||
btn.style.transform = 'scale(0.95)';
|
||||
});
|
||||
} else if (isChrome && illusChrome.length > 0) {
|
||||
illusChrome.forEach(btn => {
|
||||
btn.style.order = '-1';
|
||||
if (!isInstalled) {
|
||||
btn.classList.add('install-breathe');
|
||||
btn.style.cursor = 'pointer';
|
||||
btn.onclick = () => window.open('https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc', '_blank');
|
||||
}
|
||||
});
|
||||
illusFirefox.forEach(btn => {
|
||||
btn.style.opacity = '0.5';
|
||||
btn.style.transform = 'scale(0.95)';
|
||||
});
|
||||
}
|
||||
|
||||
// Pulse main hero CTA buttons via Web Animations API
|
||||
// (avoids CSS transition/inline-style conflicts from mouse handlers)
|
||||
if (!isInstalled) {
|
||||
const heroBtns = document.querySelectorAll(isFirefox ? '.btn-firefox' : (isChrome ? '.btn-primary' : null));
|
||||
if (heroBtns && heroBtns.length > 0) {
|
||||
heroBtns.forEach(btn => {
|
||||
const isFF = btn.classList.contains('btn-firefox');
|
||||
const glowColor = isFF ? 'rgba(249, 115, 22, ' : 'rgba(99, 102, 241, ';
|
||||
btn.animate([
|
||||
{ transform: 'scale(1)', boxShadow: `0 0 15px ${glowColor}0.2)` },
|
||||
{ transform: 'scale(1.05)', boxShadow: `0 0 25px ${glowColor}0.5)` },
|
||||
{ transform: 'scale(1)', boxShadow: `0 0 15px ${glowColor}0.2)` }
|
||||
], {
|
||||
duration: 2500,
|
||||
iterations: Infinity,
|
||||
easing: 'ease-in-out'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 600);
|
||||
};
|
||||
|
||||
detectBrowserAndElevateBadge();
|
||||
checkInvite();
|
||||
updateDynamicVersion();
|
||||
localizeHomeLinks();
|
||||
initLanguageSelectorValue();
|
||||
});
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" height="48" width="48"><defs><linearGradient id="a" x1="3.2173" y1="15" x2="44.7812" y2="15" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d93025"/><stop offset="1" stop-color="#ea4335"/></linearGradient><linearGradient id="b" x1="20.7219" y1="47.6791" x2="41.5039" y2="11.6837" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fcc934"/><stop offset="1" stop-color="#fbbc04"/></linearGradient><linearGradient id="c" x1="26.5981" y1="46.5015" x2="5.8161" y2="10.506" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1e8e3e"/><stop offset="1" stop-color="#34a853"/></linearGradient></defs><circle cx="24" cy="23.9947" r="12" style="fill:#fff"/><path d="M3.2154,36A24,24,0,1,0,12,3.2154,24,24,0,0,0,3.2154,36ZM34.3923,18A12,12,0,1,1,18,13.6077,12,12,0,0,1,34.3923,18Z" style="fill:none"/><path d="M24,12H44.7812a23.9939,23.9939,0,0,0-41.5639.0029L13.6079,30l.0093-.0024A11.9852,11.9852,0,0,1,24,12Z" style="fill:url(#a)"/><circle cx="24" cy="24" r="9.5" style="fill:#1a73e8"/><path d="M34.3913,30.0029,24.0007,48A23.994,23.994,0,0,0,44.78,12.0031H23.9989l-.0025.0093A11.985,11.985,0,0,1,34.3913,30.0029Z" style="fill:url(#b)"/><path d="M13.6086,30.0031,3.218,12.006A23.994,23.994,0,0,0,24.0025,48L34.3931,30.0029l-.0067-.0068a11.9852,11.9852,0,0,1-20.7778.007Z" style="fill:url(#c)"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#52b54b">
|
||||
<path d="M11.041 0c-.007 0-1.456 1.43-3.219 3.176L4.615 6.352l.512.513.512.512-2.819 2.791L0 12.961l1.83 1.848c1.006 1.016 2.438 2.46 3.182 3.209l1.351 1.359.508-.496c.28-.273.515-.498.524-.498.008 0 1.266 1.264 2.794 2.808L12.97 24l.187-.182c.23-.225 5.007-4.95 5.717-5.656l.52-.516-.502-.513c-.276-.282-.5-.52-.496-.53.003-.009 1.264-1.26 2.802-2.783 1.538-1.522 2.8-2.776 2.803-2.785.005-.012-3.617-3.684-6.107-6.193L17.65 4.6l-.505.505c-.279.278-.517.501-.53.497-.013-.005-1.27-1.267-2.793-2.805A449.655 449.655 0 0011.041 0zM9.223 7.367c.091.038 7.951 4.608 7.957 4.627.003.013-1.781 1.056-3.965 2.32a999.898 999.898 0 01-3.996 2.307c-.019.006-.026-1.266-.026-4.629 0-3.7.007-4.634.03-4.625z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 785 B |
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" fill="#ffffff" xmlns="http://www.w3.org/2000/svg"><title>Firefox Browser</title><path d="M8.824 7.287c.008 0 .004 0 0 0zm-2.8-1.4c.006 0 .003 0 0 0zm16.754 2.161c-.505-1.215-1.53-2.528-2.333-2.943.654 1.283 1.033 2.57 1.177 3.53l.002.02c-1.314-3.278-3.544-4.6-5.366-7.477-.091-.147-.184-.292-.273-.446a3.545 3.545 0 01-.13-.24 2.118 2.118 0 01-.172-.46.03.03 0 00-.027-.03.038.038 0 00-.021 0l-.006.001a.037.037 0 00-.01.005L15.624 0c-2.585 1.515-3.657 4.168-3.932 5.856a6.197 6.197 0 00-2.305.587.297.297 0 00-.147.37c.057.162.24.24.396.17a5.622 5.622 0 012.008-.523l.067-.005a5.847 5.847 0 011.957.222l.095.03a5.816 5.816 0 01.616.228c.08.036.16.073.238.112l.107.055a5.835 5.835 0 01.368.211 5.953 5.953 0 012.034 2.104c-.62-.437-1.733-.868-2.803-.681 4.183 2.09 3.06 9.292-2.737 9.02a5.164 5.164 0 01-1.513-.292 4.42 4.42 0 01-.538-.232c-1.42-.735-2.593-2.121-2.74-3.806 0 0 .537-2 3.845-2 .357 0 1.38-.998 1.398-1.287-.005-.095-2.029-.9-2.817-1.677-.422-.416-.622-.616-.8-.767a3.47 3.47 0 00-.301-.227 5.388 5.388 0 01-.032-2.842c-1.195.544-2.124 1.403-2.8 2.163h-.006c-.46-.584-.428-2.51-.402-2.913-.006-.025-.343.176-.389.206-.406.29-.787.616-1.136.974-.397.403-.76.839-1.085 1.303a9.816 9.816 0 00-1.562 3.52c-.003.013-.11.487-.19 1.073-.013.09-.026.181-.037.272a7.8 7.8 0 00-.069.667l-.002.034-.023.387-.001.06C.386 18.795 5.593 24 12.016 24c5.752 0 10.527-4.176 11.463-9.661.02-.149.035-.298.052-.448.232-1.994-.025-4.09-.753-5.844z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
|
After Width: | Height: | Size: 835 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#00a4dc">
|
||||
<path d="M12 .002C8.826.002-1.398 18.537.16 21.666c1.56 3.129 22.14 3.094 23.682 0C25.384 18.573 15.177 0 12 0zm7.76 18.949c-1.008 2.028-14.493 2.05-15.514 0C3.224 16.9 9.92 4.755 12.003 4.755c2.081 0 8.77 12.166 7.759 14.196zM12 9.198c-1.054 0-4.446 6.15-3.93 7.189.518 1.04 7.348 1.027 7.86 0 .511-1.027-2.874-7.19-3.93-7.19z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 416 B |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#e50914">
|
||||
<path d="M5.398 0 13.746 23.602c2.346.059 4.856.398 4.856.398L10.113 0H5.398zm8.489 0v9.172l4.715 13.33V0h-4.715zM5.398 1.5V24c1.873-.225 2.81-.312 4.715-.398V14.83L5.398 1.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 264 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#00a8e1">
|
||||
<text x="12" y="7.2" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" font-size="7.5" font-weight="900" letter-spacing="-0.1px">prime</text>
|
||||
<text x="12" y="14.8" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" font-size="7.5" font-weight="900" letter-spacing="-0.1px">video</text>
|
||||
<path d="M.045 18.02c.072-.116.187-.124.348-.022 3.636 2.11 7.594 3.166 11.87 3.166 2.852 0 5.668-.533 8.447-1.595l.315-.14c.138-.06.234-.1.293-.13.226-.088.39-.046.525.13.12.174.09.336-.12.48-.256.19-.6.41-1.006.654-1.244.743-2.64 1.316-4.185 1.726a17.617 17.617 0 01-10.951-.577 17.88 17.88 0 01-5.43-3.35c-.1-.074-.151-.15-.151-.22 0-.047.021-.09.051-.13z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 887 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9146ff">
|
||||
<path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 280 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ff0000">
|
||||
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 446 B |
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Datenschutz / Privacy Policy | KoalaSync</title>
|
||||
<link rel="preload" href="style.css" as="style">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" type="image/webp" href="assets/NewLogoIcon_64.webp">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- Mobile Browser Theme Styling -->
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<script src="lang-init.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-blobs">
|
||||
<div class="blob blob-1"></div>
|
||||
<div class="blob blob-2"></div>
|
||||
<div class="blob blob-3"></div>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<div class="container nav-content">
|
||||
<a href="index.html" class="logo-area" style="text-decoration: none;">
|
||||
<img src="assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
|
||||
<span>KoalaSync</span>
|
||||
</a>
|
||||
<button class="hamburger" aria-label="Menu" aria-expanded="false">☰</button>
|
||||
<div class="nav-links">
|
||||
<a href="index.html" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
||||
<span lang="de">Startseite</span><span lang="en">Home</span>
|
||||
</a>
|
||||
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="legal-content">
|
||||
<div class="legal-card" data-reveal style="padding: 2rem;">
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<img src="assets/KoalaPrivacy.webp" alt="Cute Koala representing privacy and data security" class="legal-mascot" lang="en" width="180" height="180">
|
||||
<img src="assets/KoalaPrivacy.webp" alt="Niedlicher Koala, der den Datenschutz repräsentiert" class="legal-mascot" lang="de" width="180" height="180">
|
||||
</div>
|
||||
<h1 lang="de">Datenschutz</h1>
|
||||
<h1 lang="en">Privacy Policy</h1>
|
||||
<p lang="de" style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
|
||||
Sicherheit & Privatsphäre
|
||||
</p>
|
||||
<p lang="en" style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
|
||||
Security & Privacy
|
||||
</p>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">1. Hosting & Logfiles</span>
|
||||
<span lang="en">1. Hosting & Logfiles</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Diese Seite wird auf einem privaten Server gehostet. Zur Gewährleistung der Stabilität werden standardmäßige Server-Logs (IP, Browser, Zeit) erhoben, aber nicht mit Personen verknüpft und nach 7 Tagen automatisch gelöscht.
|
||||
</p>
|
||||
<p lang="en">
|
||||
This site is hosted on a private server. To ensure stability, standard server logs (IP, browser, time) are collected, but not linked to individuals and are automatically deleted after 7 days.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">2. Keine Drittanbieter & Open Source</span>
|
||||
<span lang="en">2. No Third Parties & Open Source</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
KoalaSync verzichtet bewusst auf Analyse-Tools, Tracking-Cookies oder Werbenetzwerke. Wir laden keine Ressourcen von Drittanbietern (wie Google Fonts) nach, um Ihre Privatsphäre maximal zu schützen.
|
||||
</p>
|
||||
<p lang="de" style="margin-top: 0.5rem;">
|
||||
Da KoalaSync vollständig Open Source ist, kann zudem jede Zeile Code auf unserem <a href="https://github.com/shik3i/KoalaSync" target="_blank" style="color: var(--accent);">GitHub-Repository</a> öffentlich eingesehen und auf Sicherheit geprüft werden.
|
||||
</p>
|
||||
<p lang="en">
|
||||
KoalaSync deliberately avoids analytics tools, tracking cookies, or advertising networks. We do not load any third-party resources (such as Google Fonts) to maximize the protection of your privacy.
|
||||
</p>
|
||||
<p lang="en" style="margin-top: 0.5rem;">
|
||||
Since KoalaSync is 100% open-source, every single line of code can also be publicly viewed and audited for security on our <a href="https://github.com/shik3i/KoalaSync" target="_blank" style="color: var(--accent);">GitHub repository</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">3. Relay-Server Architektur</span>
|
||||
<span lang="en">3. Relay Server Architecture</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Unser Relay-Server arbeitet ausschließlich im Arbeitsspeicher (RAM). Nachrichten zwischen Teilnehmern werden nicht auf Festplatten gespeichert und sind flüchtig. Sobald ein Raum geschlossen wird, werden alle zugehörigen Metadaten sofort gelöscht.
|
||||
</p>
|
||||
<p lang="en">
|
||||
Our relay server operates exclusively in memory (RAM). Messages between participants are not stored on hard drives and are volatile. As soon as a room is closed, all associated metadata is immediately deleted.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">4. Browser-Erweiterung (Extension)</span>
|
||||
<span lang="en">4. Browser Extension</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Um die geräteübergreifende Synchronisation zu ermöglichen, erfasst die KoalaSync Browser-Erweiterung temporär Daten des aktuell aktiven Video-Tabs (z. B. Tab-Titel, Medien-Metadaten wie den Videotitel sowie den Wiedergabestatus). Diese Daten werden ausschließlich zur Synchronisation an die anderen Teilnehmer in Ihrem Raum gesendet. Es wird ausdrücklich <strong>kein allgemeiner Browserverlauf (Browsing History)</strong> ausgelesen, gespeichert oder übermittelt.
|
||||
</p>
|
||||
<p lang="en">
|
||||
To enable cross-device synchronization, the KoalaSync browser extension temporarily captures data from the currently active video tab (e.g., tab title, media metadata like the video title, and playback state). This data is exclusively sent to other participants in your room for synchronization. We explicitly <strong>do not read, store, or transmit your general browsing history</strong>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">5. Berechtigungen der Erweiterung</span>
|
||||
<span lang="en">5. Extension Permissions</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Um ihren technischen Zweck zu erfüllen, benötigt die Browser-Erweiterung bestimmte Berechtigungen. Jede Berechtigung wird ausschließlich für die Kernfunktionalität genutzt:
|
||||
</p>
|
||||
<ul lang="de" style="margin-left: 1.5rem; margin-top: 0.5rem; color: var(--text-muted); font-size: 0.9rem; list-style-type: disc; display: flex; flex-direction: column; gap: 0.35rem;">
|
||||
<li><strong>storage</strong>: Erlaubt das lokale Speichern von Benutzernamen, Server-URLs und Raum-Zugangsdaten im Browser, damit Sie sich nicht jedes Mal neu anmelden müssen.</li>
|
||||
<li><strong>tabs</strong>: Wird benötigt, um geöffnete Tabs im Dropdown der Erweiterung aufzulisten und deren Titel auszulesen, damit Sie bequem den richtigen Video-Tab auswählen können.</li>
|
||||
<li><strong>scripting</strong>: Erforderlich, um das Synchronisationsskript (content.js) sicher in den von Ihnen ausgewählten Videotab zu injizieren.</li>
|
||||
<li><strong>alarms</strong>: Verhindert, dass der Hintergrunddienst der Erweiterung (Service Worker) während einer aktiven Synchronisationssitzung vom Browser in den Ruhezustand versetzt wird.</li>
|
||||
<li><strong>activeTab</strong>: Ermöglicht eine sichere, temporäre Interaktion mit dem momentan aktiven Tab für direkte Steuerungsbefehle.</li>
|
||||
<li><strong>notifications</strong>: Ermöglicht optionale Desktop-Benachrichtigungen, wenn beispielsweise ein neuer Freund dem Raum beitritt.</li>
|
||||
<li><strong><all_urls> (Host-Berechtigung)</strong>: Ermöglicht der Erweiterung, auf beliebigen Webseiten nach HTML5-Videoelementen zu suchen, damit die Synchronisation plattformübergreifend (z. B. auf YouTube, Netflix, Jellyfin etc.) funktioniert.</li>
|
||||
</ul>
|
||||
<p lang="en" style="margin-top: 1.5rem;">
|
||||
To fulfill its technical purpose, the browser extension requires certain permissions. Each permission is used exclusively for core functionality:
|
||||
</p>
|
||||
<ul lang="en" style="margin-left: 1.5rem; margin-top: 0.5rem; color: var(--text-muted); font-size: 0.9rem; list-style-type: disc; display: flex; flex-direction: column; gap: 0.35rem;">
|
||||
<li><strong>storage</strong>: Allows local storage of your username, server URL, and room credentials in your browser so you don't have to log in every time.</li>
|
||||
<li><strong>tabs</strong>: Required to list open tabs in the extension's dropdown and read their titles, making it easy for you to select the correct video tab.</li>
|
||||
<li><strong>scripting</strong>: Required to securely inject the synchronization script (content.js) into your selected video tab.</li>
|
||||
<li><strong>alarms</strong>: Prevents the extension's background service worker from being suspended by the browser during an active synchronization session.</li>
|
||||
<li><strong>activeTab</strong>: Enables secure, temporary interaction with the currently active tab for direct playback commands.</li>
|
||||
<li><strong>notifications</strong>: Enables optional desktop notifications, such as when a new friend joins the room.</li>
|
||||
<li><strong><all_urls> (Host permission)</strong>: Allows the extension to scan for HTML5 video elements on any website, enabling cross-platform synchronization (e.g., on YouTube, Netflix, Jellyfin etc.).</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">6. Brute-Force Schutz</span>
|
||||
<span lang="en">6. Brute-Force Protection</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Zur Sicherheit unserer Nutzer speichern wir fehlgeschlagene Login-Versuche (IP-Adresse und Raum-ID) für maximal 15 Minuten im RAM, um automatisierte Angriffe zu verhindern. Diese Daten werden danach rückstandslos gelöscht.
|
||||
</p>
|
||||
<p lang="en">
|
||||
For the security of our users, we store failed login attempts (IP address and room ID) for a maximum of 15 minutes in RAM to prevent automated attacks. This data is deleted without a trace afterwards.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">7. Ihre Rechte</span>
|
||||
<span lang="en">7. Your Rights</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Sie haben das Recht auf Auskunft, Berichtigung oder Löschung Ihrer Daten. Da wir jedoch keine personenbezogenen Daten dauerhaft speichern, ist eine Zuordnung zu Ihrer Person in der Regel technisch nicht möglich.
|
||||
</p>
|
||||
<p lang="en">
|
||||
You have the right to information, correction, or deletion of your data. However, since we do not store any personal data permanently, linking data to your person is technically impossible in most cases.
|
||||
</p>
|
||||
<p lang="de">Kontakt bei Fragen: <span class="email-reveal" data-user="koalasync_datenschutz" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span></p>
|
||||
<p lang="en">Contact for questions: <span class="email-reveal" data-user="koalasync_datenschutz" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[Show Email]</span></p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2026 KoalaSync. Open source under the MIT License.</p>
|
||||
<p lang="de" style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
|
||||
<p lang="en" style="font-size: 0.8rem; margin-top: 0.5rem;">No data is stored on our servers. Pure RAM-based relay.</p>
|
||||
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
|
||||
<a href="impressum.html" style="color: var(--text-muted); text-decoration: none;"><span lang="de">Impressum</span><span lang="en">Legal Notice</span></a>
|
||||
<a href="datenschutz.html" style="color: var(--text-muted); text-decoration: none;"><span lang="de">Datenschutz</span><span lang="en">Privacy Policy</span></a>
|
||||
<a href="https://mastodon.social/@koalastuff" rel="me" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
|
||||
Mastodon
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Impressum / Legal Notice | KoalaSync</title>
|
||||
<link rel="preload" href="style.css" as="style">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" type="image/webp" href="assets/NewLogoIcon_64.webp">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- Mobile Browser Theme Styling -->
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<script src="lang-init.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-blobs">
|
||||
<div class="blob blob-1"></div>
|
||||
<div class="blob blob-2"></div>
|
||||
<div class="blob blob-3"></div>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<div class="container nav-content">
|
||||
<a href="index.html" class="logo-area" style="text-decoration: none;">
|
||||
<img src="assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
|
||||
<span>KoalaSync</span>
|
||||
</a>
|
||||
<button class="hamburger" aria-label="Menu" aria-expanded="false">☰</button>
|
||||
<div class="nav-links">
|
||||
<a href="index.html" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
||||
<span lang="de">Startseite</span><span lang="en">Home</span>
|
||||
</a>
|
||||
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="legal-content">
|
||||
<div class="legal-card" data-reveal style="padding: 2rem;">
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<img src="assets/KoalaImprintl.webp" alt="Cute Koala representing the legal notice page" class="legal-mascot" lang="en" width="180" height="180">
|
||||
<img src="assets/KoalaImprintl.webp" alt="Niedlicher Koala, der das Impressum repräsentiert" class="legal-mascot" lang="de" width="180" height="180">
|
||||
</div>
|
||||
<h1 lang="de">Impressum</h1>
|
||||
<h1 lang="en">Legal Notice</h1>
|
||||
<p lang="de" style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
|
||||
Transparenz & Identifikation
|
||||
</p>
|
||||
<p lang="en" style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
|
||||
Transparency & Identification
|
||||
</p>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">Betreiber & Kontakt</span>
|
||||
<span lang="en">Operator & Contact</span>
|
||||
</h2>
|
||||
<p lang="de">Timo (KoalaDev) – Privatperson</p>
|
||||
<p lang="en">Timo (KoalaDev) – Private Individual</p>
|
||||
<p lang="de" style="margin-top: 0.5rem;">
|
||||
E-Mail: <span class="email-reveal" data-user="koalasync_admin" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span>
|
||||
</p>
|
||||
<p lang="en" style="margin-top: 0.5rem;">
|
||||
E-Mail: <span class="email-reveal" data-user="koalasync_admin" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[Show Email]</span>
|
||||
</p>
|
||||
<p lang="de" style="margin-top: 0.75rem;">
|
||||
<span style="opacity: 0.6;">🔒 Private Nachricht:</span>
|
||||
<a href="https://mastodon.social/@koalastuff" rel="me" target="_blank" style="color: var(--accent); text-decoration: none;">@koalastuff auf Mastodon</a>
|
||||
</p>
|
||||
<p lang="en" style="margin-top: 0.75rem;">
|
||||
<span style="opacity: 0.6;">🔒 Private message:</span>
|
||||
<a href="https://mastodon.social/@koalastuff" rel="me" target="_blank" style="color: var(--accent); text-decoration: none;">@koalastuff on Mastodon</a>
|
||||
</p>
|
||||
<p lang="de" style="margin-top: 0.5rem;">
|
||||
<span style="opacity: 0.6;">🐛 Active Bedenken / Bug Reports:</span>
|
||||
<a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" style="color: var(--accent); text-decoration: none;">GitHub Issues</a>
|
||||
</p>
|
||||
<p lang="en" style="margin-top: 0.5rem;">
|
||||
<span style="opacity: 0.6;">🐛 Active concerns / Bug reports:</span>
|
||||
<a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" style="color: var(--accent); text-decoration: none;">GitHub Issues</a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section style="opacity: 0.8;">
|
||||
<h2>
|
||||
<span lang="de">Privatprojekt-Hinweis</span>
|
||||
<span lang="en">Private Project Notice</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Diese Website ist ein rein privates Hobby-Projekt und dient keinen geschäftsmäßigen Zwecken.
|
||||
Eine Impressumspflicht nach § 5 DDG (ehemals TMG) besteht daher nicht.
|
||||
Diese Angaben erfolgen rein freiwillig zur Transparenz gegenüber der Community.
|
||||
</p>
|
||||
<p lang="en">
|
||||
This website is a purely private hobby project and does not serve any commercial purposes.
|
||||
Therefore, there is no obligation to provide a legal notice according to § 5 DDG (formerly TMG).
|
||||
This information is provided voluntarily for transparency towards the community.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">Haftung für Inhalte</span>
|
||||
<span lang="en">Liability for Content</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Gemäß § 7 Abs.1 DDG sind wir für eigene Inhalte verantwortlich. Nach §§ 8 bis 10 DDG sind wir jedoch nicht verpflichtet,
|
||||
übermittelte oder gespeicherte fremde Informationen zu überwachen.
|
||||
</p>
|
||||
<p lang="en">
|
||||
According to § 7 Abs.1 DDG we are responsible for our own content. According to §§ 8 to 10 DDG, however, we are not obligated to monitor transmitted or stored third-party information.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">Haftung für Links</span>
|
||||
<span lang="en">Liability for Links</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Unser Angebot enthält Links zu externen Websites Dritter. Auf deren Inhalte haben wir keinen Einfluss und
|
||||
können daher keine Gewähr für diese fremden Inhalte übernehmen.
|
||||
</p>
|
||||
<p lang="en">
|
||||
Our offer contains links to external third-party websites. We have no influence on their content and therefore cannot assume any liability for these external contents.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>
|
||||
<span lang="de">Urheberrecht</span>
|
||||
<span lang="en">Copyright</span>
|
||||
</h2>
|
||||
<p lang="de">
|
||||
Die durch die Seitenbetreiber erstellten Inhalte auf diesen Seiten unterliegen dem deutschen Urheberrecht.
|
||||
Vervielfältigung, Bearbeitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung.
|
||||
</p>
|
||||
<p lang="en">
|
||||
The content and works created by the site operators on these pages are subject to German copyright law.
|
||||
Duplication, processing, and any kind of exploitation outside the limits of copyright require written consent.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2026 KoalaSync. Open source under the MIT License.</p>
|
||||
<p lang="de" style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
|
||||
<p lang="en" style="font-size: 0.8rem; margin-top: 0.5rem;">No data is stored on our servers. Pure RAM-based relay.</p>
|
||||
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
|
||||
<a href="impressum.html" style="color: var(--text-muted); text-decoration: none;"><span lang="de">Impressum</span><span lang="en">Legal Notice</span></a>
|
||||
<a href="datenschutz.html" style="color: var(--text-muted); text-decoration: none;"><span lang="de">Datenschutz</span><span lang="en">Privacy Policy</span></a>
|
||||
<a href="https://mastodon.social/@koalastuff" rel="me" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
|
||||
Mastodon
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Join Room | KoalaSync</title>
|
||||
<link rel="preload" href="style.css" as="style">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" type="image/webp" href="assets/NewLogoIcon_64.webp">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://sync.koalastuff.net/join.html">
|
||||
<meta property="og:title" content="Join Room | KoalaSync">
|
||||
<meta property="og:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
|
||||
<meta property="og:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Join Room | KoalaSync">
|
||||
<meta name="twitter:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
|
||||
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
|
||||
|
||||
<!-- Mobile Browser Theme Styling -->
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<script src="lang-init.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-blobs">
|
||||
<div class="blob blob-1"></div>
|
||||
<div class="blob blob-2"></div>
|
||||
<div class="blob blob-3"></div>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<div class="container nav-content">
|
||||
<a href="index.html" class="logo-area" style="text-decoration: none;">
|
||||
<img src="assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
|
||||
<span>KoalaSync</span>
|
||||
</a>
|
||||
<button class="hamburger" aria-label="Menu" aria-expanded="false">☰</button>
|
||||
<div class="nav-links">
|
||||
<a href="index.html" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
||||
<span lang="de">Startseite</span><span lang="en">Home</span>
|
||||
</a>
|
||||
<a href="https://github.com/shik3i/KoalaSync" target="_blank" style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<div class="lang-select-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="globe-icon"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<select class="lang-dropdown" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="pt-BR">Português (Brasil)</option>
|
||||
<option value="ru">Русский</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="legal-content join-card">
|
||||
<div class="legal-card" id="join-container" data-reveal style="padding: 2.5rem; text-align: center;">
|
||||
<div class="room-badge"><span lang="en">INVITATION DETECTED</span><span lang="de">EINLADUNG ERKANNT</span></div>
|
||||
|
||||
<!-- Dynamic Status Icon & Pulsing Radar Wave -->
|
||||
<div class="join-status-visual">
|
||||
<div class="status-ring active-pulse" id="status-ring"></div>
|
||||
<div id="join-status-icon" style="z-index: 2; transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); display: flex; align-items: center; justify-content: center;">
|
||||
<img src="assets/KoalaSearching.webp" alt="A cute koala looking through a telescope searching for your extension" class="join-status-mascot searching-mascot join-status-pulse" lang="en">
|
||||
<img src="assets/KoalaSearching.webp" alt="Ein niedlicher Koala schaut durch ein Fernrohr und sucht nach deiner Erweiterung" class="join-status-mascot searching-mascot join-status-pulse" lang="de">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 id="join-title" style="font-size: 2rem; margin-bottom: 1rem;"><span lang="en">Ready to sync?</span><span lang="de">Bereit zum Synchronisieren?</span></h1>
|
||||
<p id="join-desc" style="text-align: center; color: var(--text-muted); margin-bottom: 2rem; font-size: 0.9rem;">
|
||||
<span lang="en">You've been invited to join a synchronized session.</span>
|
||||
<span lang="de">Du wurdest eingeladen, einer synchronisierten Sitzung beizutreten.</span>
|
||||
</p>
|
||||
|
||||
<div id="room-info-box" style="background: rgba(255,255,255,0.03); padding: 2rem; border-radius: 20px; margin-bottom: 2rem; border: 1px solid var(--glass-border); text-align: center; position: relative; overflow: hidden;">
|
||||
<div style="font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.2em; color: var(--accent); margin-bottom: 0.75rem; font-weight: 700;">Room ID</div>
|
||||
<div id="display-room-id" style="font-size: 1.75rem; font-weight: 800; letter-spacing: 1px; color: white;">-------</div>
|
||||
</div>
|
||||
|
||||
<div id="join-actions" style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<div style="text-align: center; color: var(--text-muted); font-size: 0.8rem;">
|
||||
<div class="join-spinner"></div>
|
||||
<span lang="en">Detecting extension...</span><span lang="de">Erweiterung wird erkannt...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2026 KoalaSync. Open source under the MIT License.</p>
|
||||
<p lang="en" style="font-size: 0.8rem; margin-top: 0.5rem;">No data is stored on our servers. Pure RAM-based relay.</p>
|
||||
<p lang="de" style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
|
||||
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
|
||||
<a href="impressum.html" style="color: var(--text-muted); text-decoration: none;"><span lang="en">Legal Notice</span><span lang="de">Impressum</span></a>
|
||||
<a href="datenschutz.html" style="color: var(--text-muted); text-decoration: none;"><span lang="en">Privacy Policy</span><span lang="de">Datenschutz</span></a>
|
||||
<a href="https://mastodon.social/@koalastuff" rel="me" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
|
||||
Mastodon
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,66 @@
|
||||
(function() {
|
||||
var html = document.documentElement;
|
||||
var path = window.location.pathname;
|
||||
|
||||
// Check if we are on the root index page (either "/" or "/index.html" at the root)
|
||||
var isRootIndex = path === '/' || path === '/index.html' || path === '';
|
||||
|
||||
if (isRootIndex) {
|
||||
var savedLang = localStorage.getItem('koala_lang');
|
||||
var browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
var preferredLang = savedLang || browserLang;
|
||||
|
||||
if (preferredLang === 'de') {
|
||||
// Redirect to German version
|
||||
window.location.replace('de/');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var htmlClasses = html.className.split(' ');
|
||||
var activeLang = null;
|
||||
var hasStaticLang = false;
|
||||
for (var i = 0; i < htmlClasses.length; i++) {
|
||||
if (htmlClasses[i].indexOf('lang-') === 0) {
|
||||
hasStaticLang = true;
|
||||
var langPart = htmlClasses[i].substring(5);
|
||||
activeLang = langPart === 'pt-br' ? 'pt-BR' : langPart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasStaticLang) {
|
||||
localStorage.setItem('koala_lang', activeLang);
|
||||
} else {
|
||||
var savedLang = localStorage.getItem('koala_lang');
|
||||
var browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
activeLang = savedLang || browserLang;
|
||||
|
||||
// Dynamic utility pages currently only support English and German markup.
|
||||
// Fallback to English for any other language preference (e.g. fr, es) to avoid bilingual text duplication.
|
||||
if (activeLang !== 'de') {
|
||||
activeLang = 'en';
|
||||
}
|
||||
|
||||
html.classList.add('lang-' + activeLang);
|
||||
html.lang = activeLang;
|
||||
}
|
||||
|
||||
// Update titles dynamically based on page
|
||||
var isIndex = path === '/' || path.endsWith('index.html') || path.split('/').pop() === '';
|
||||
var isJoin = path.includes('join');
|
||||
|
||||
if (isIndex) {
|
||||
var titles = {
|
||||
en: 'KoalaSync | Real-time Video Synchronization for Friends',
|
||||
de: 'KoalaSync | Echtzeit-Video-Synchronisation für Freunde'
|
||||
};
|
||||
document.title = titles[activeLang] || titles.en;
|
||||
} else if (isJoin) {
|
||||
var titles = {
|
||||
en: 'Join Room | KoalaSync',
|
||||
de: 'Raum beitreten | KoalaSync'
|
||||
};
|
||||
document.title = titles[activeLang] || titles.en;
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
# KoalaSync Website — Allow all crawlers, full indexing
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Sitemap for search engines
|
||||
Sitemap: https://sync.koalastuff.net/sitemap.xml
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/</loc>
|
||||
<lastmod>2026-05-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/de/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/fr/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/es/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/pt-BR/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/ru/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/impressum.html</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sync.koalastuff.net/datenschutz.html</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": "1.9.3",
|
||||
"date": "2026-05-30T00:01:24Z"
|
||||
}
|
||||