Merge pull request #541 from movie-web/dev

Version 4.0.2
This commit is contained in:
William Oldham 2023-12-16 17:55:36 +00:00 committed by GitHub
commit d38d5cbbed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 2502 additions and 341 deletions

View File

@ -109,7 +109,6 @@ jobs:
prerelease: false
- name: Upload release (PWA)
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -120,7 +119,6 @@ jobs:
asset_content_type: application/zip
- name: Upload Release (Normal)
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,13 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1.0, user-scalable=no" />
<meta
name="description"
content="The place for your favourite movies & shows"
/>
<meta name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1.0, user-scalable=no" />
<meta name="description" content="The place for your favourite movies & shows" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
@ -18,10 +17,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
<script src="/config.js"></script>
<script src="https://cdn.jsdelivr.net/gh/movie-web/6C6F6C7A@8b821f445b83d51ef1b8f42c99b7346f6b47dce5/out.js"></script>
@ -55,10 +51,12 @@
}
</script>
{{/if}}
</head>
<body>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</body>
</html>

View File

@ -1,6 +1,6 @@
{
"name": "movie-web",
"version": "4.0.1",
"version": "4.0.2",
"private": true,
"homepage": "https://movie-web.app",
"scripts": {
@ -46,6 +46,7 @@
"immer": "^10.0.2",
"iso-639-1": "^3.1.0",
"lodash.isequal": "^4.5.0",
"nanoid": "^5.0.4",
"node-forge": "^1.3.1",
"ofetch": "^1.0.0",
"react": "^17.0.2",
@ -98,6 +99,8 @@
"handlebars": "^4.7.7",
"jsdom": "^21.1.0",
"postcss": "^8.4.20",
"postcss-rtl": "^2.0.0",
"postcss-rtlcss": "^4.0.9",
"prettier": "^2.5.1",
"prettier-plugin-tailwindcss": "^0.1.7",
"tailwind-scrollbar": "^2.0.1",

View File

@ -71,6 +71,9 @@ dependencies:
lodash.isequal:
specifier: ^4.5.0
version: 4.5.0
nanoid:
specifier: ^5.0.4
version: 5.0.4
node-forge:
specifier: ^1.3.1
version: 1.3.1
@ -223,6 +226,12 @@ devDependencies:
postcss:
specifier: '>=8.4.31'
version: 8.4.31
postcss-rtl:
specifier: ^2.0.0
version: 2.0.0(postcss@8.4.31)
postcss-rtlcss:
specifier: ^4.0.9
version: 4.0.9(postcss@8.4.31)
prettier:
specifier: ^2.5.1
version: 2.8.8
@ -4747,6 +4756,12 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/nanoid@5.0.4:
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
dev: true
@ -5070,6 +5085,26 @@ packages:
postcss-selector-parser: 6.0.13
dev: true
/postcss-rtl@2.0.0(postcss@8.4.31):
resolution: {integrity: sha512-vFu78CvaGY9BafWRHNgDm6OjUxzRCWWCrp+KtnyXdgwibLwb/j5ls8Z/ubvOsk9B/Q2NLwSPrXRARKMaa9RBmA==}
engines: {node: '>=14.0.0'}
peerDependencies:
postcss: '>=8.4.31'
dependencies:
postcss: 8.4.31
rtlcss: 4.0.0
dev: true
/postcss-rtlcss@4.0.9(postcss@8.4.31):
resolution: {integrity: sha512-dCNKEf+FgTv+EA3XI8ysg2RnpS5s3/iZmU+9qpCNFxHU/BhK+4hz7jyCsCAfo0CLnDrMPtaQENhwb+EGm1wh7Q==}
engines: {node: '>=18.0.0'}
peerDependencies:
postcss: '>=8.4.31'
dependencies:
postcss: 8.4.31
rtlcss: 4.1.1
dev: true
/postcss-selector-parser@6.0.13:
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
engines: {node: '>=4'}
@ -5480,6 +5515,28 @@ packages:
'@babel/runtime': 7.22.11
dev: false
/rtlcss@4.0.0:
resolution: {integrity: sha512-j6oypPP+mgFwDXL1JkLCtm6U/DQntMUqlv5SOhpgHhdIE+PmBcjrtAHIpXfbIup47kD5Sgja9JDsDF1NNOsBwQ==}
engines: {node: '>=12.0.0'}
hasBin: true
dependencies:
escalade: 3.1.1
picocolors: 1.0.0
postcss: 8.4.31
strip-json-comments: 3.1.1
dev: true
/rtlcss@4.1.1:
resolution: {integrity: sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==}
engines: {node: '>=12.0.0'}
hasBin: true
dependencies:
escalade: 3.1.1
picocolors: 1.0.0
postcss: 8.4.31
strip-json-comments: 3.1.1
dev: true
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:

View File

@ -222,3 +222,12 @@ input[type=range].styled-slider.slider-progress::-ms-fill-lower {
outline: 2px solid theme('colors.themePreview.primary');
box-shadow: 0 0 10px theme('colors.themePreview.secondary');
}
[dir="rtl"] .transform {
/* Invert horizontal X offset on transform (Tailwind RTL plugin does the rest) */
transform: translate(calc(var(--tw-translate-x) * -1), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
}
[dir="ltr"] .transform {
/* default - otherwise it overwrites*/
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
}

View File

@ -2,10 +2,13 @@ import cs from "@/assets/locales/cs.json";
import de from "@/assets/locales/de.json";
import en from "@/assets/locales/en.json";
import fr from "@/assets/locales/fr.json";
import he from "@/assets/locales/he.json";
import it from "@/assets/locales/it.json";
import minion from "@/assets/locales/minion.json";
import nl from "@/assets/locales/nl.json";
import pirate from "@/assets/locales/pirate.json";
import pl from "@/assets/locales/pl.json";
import sv from "@/assets/locales/sv.json";
import tr from "@/assets/locales/tr.json";
import vi from "@/assets/locales/vi.json";
import zh from "@/assets/locales/zh.json";
@ -21,5 +24,11 @@ export const locales = {
tr,
vi,
zh,
he,
sv,
pirate,
minion,
};
export type Locales = keyof typeof locales;
export const rtlLocales: Locales[] = ["he"];

View File

@ -1,71 +1,420 @@
{
"about": {
"description": "movie-web ist eine Web-App, welche das Internet nach Streams durchsucht. Das Team versucht einen minimalistischen Ansatz umzusetzen.",
"faqTitle": "Häufig gestellte Fragen",
"q1": {
"body": "movie-web hostet keinen eigenen Inhalt. Wenn du auf etwas zum Anschauen klickst, wird das Internet danach durchsucht (Auf dem Ladebildschirm und im Tab \"Videoquellen\" kannst du einstellen, welche Quellen verwendet werden sollen). movie-web lädt keine Videos hoch, alles Videos stammen aus der Suche.",
"title": "Woher kommen die Videos?"
},
"q2": {
"body": "Das Anfragen von Serien oder Filmen ist nicht möglich. movie-web verwaltet keine Inhalte. Alle Videos stammen vom Quellen aus dem Internet.",
"title": "Wo kann ich eine Serie oder einen Film anfragen?"
},
"q3": {
"body": "Unsere Suchergebnisse werden von The Movie Database (TMDB) bereitgestellt und angezeigt, egal ob unsere Videoquellen über dieses Video verfügen.",
"title": "Die Suche zeigt eine Serie oder einen Film an, warum kann ich den dann nicht abspielen?"
},
"title": "Über movie-web"
},
"actions": {
"copied": "Kopiert",
"copy": "Kopieren"
},
"auth": {
"createAccount": "Du hast noch keinen Account? <0>Registriere dich jetzt.</0>",
"deviceNameLabel": "Gerätename",
"deviceNamePlaceholder": "Handy",
"generate": {
"description": "Deine Passphrase dient als dein Nutzername und Passwort. Speiche sie sicher ab, damit du dich in deinem Konto anmelden kannst",
"next": "Ich habe meine Passphrase gespeichert",
"title": "Deine Passphrase"
},
"hasAccount": "Du hast bereits einen Account? <0>Anmelden.</0>",
"login": {
"description": "Gebe deine Passphrase ein, um dich in deinem Konto anzumelden",
"deviceLengthError": "Gebe einen Gerätenamen ein",
"passphraseLabel": "12-Wort Passphrase",
"passphrasePlaceholder": "Passphrase",
"submit": "Anmelden",
"title": "Melde dich in deinem Konto an",
"validationError": "Falsche oder unvollständige Passphrase"
},
"register": {
"information": {
"color1": "Profilfarbe 1",
"color2": "Profilfarbe 2",
"header": "Gebe einen Namen für dein Gerät ein und wähle ein Nutzersymbol",
"icon": "Nutzersymbol",
"next": "Weiter",
"title": "Kontoinformationen"
}
},
"trust": {
"failed": {
"text": "Hast du es korrekt konfiguriert?",
"title": "Server nicht erreichbar"
},
"host": "Du verbindest dich zu <0>{{hostname}}</0> - stelle sicher, dass du diesem vertraust, bevor du einen Account erstellst",
"no": "Zurück",
"title": "Vertraust du diesem Server?",
"yes": "Ich vertraue diesem Server"
},
"verify": {
"description": "Bitte gebe deine Passphrase vom früheren Schritt an, um zu bestätigen, dass du sie gespeichert hast und um dein Konto zu erstellen",
"invalidData": "Daten sind ungültig",
"noMatch": "Passphrasen stimmen nicht überein",
"passphraseLabel": "Deine 12-Wort Passphrase",
"recaptchaFailed": "ReCaptcha Verifizierung ist fehlgeschlagenen",
"register": "Konto erstellen",
"title": "Bestätige deine Passphrase"
}
},
"errors": {
"badge": "Kaputt",
"details": "Fehlerdetails",
"reloadPage": "Seite neuladen",
"showError": "Zeige Fehlerdetails an",
"title": "Ein Fehler ist aufgetreten!"
},
"footer": {
"legal": {
"disclaimer": "Hinweis",
"disclaimerText": "movie-web hostet keine Dateien, sondern verlinkt lediglich auf Dienste Dritter. Rechtliche Fragen sollten mit den Dateihostern und -anbietern geklärt werden. movie-web übernimmt keine Verantwortung für die von den Videoanbietern angezeigten Mediendateien."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Schau deine Lieblingsserien und Filme mit dieser quelloffenen Streaming App."
},
"global": {
"name": "movie-web"
"name": "movie-web",
"pages": {
"about": "Über",
"dmca": "DMCA",
"login": "Anmelden",
"pagetitle": "{{title}} - movie-web",
"register": "Registrieren",
"settings": "Einstellungen"
}
},
"home": {
"search": {
"allResults": "Das ist alles, was wir haben!",
"sectionTitle": "Suchergebnisse",
"noResults": "Wir haben nichts gefunden!",
"failed": "Das Medium wurde nicht gefunden, bitte versuchen Sie es erneut!",
"loading": "Wird geladen...",
"placeholder": "Was willst du gucken?"
},
"bookmarks": {
"sectionTitle": "Favoriten"
},
"continueWatching": {
"sectionTitle": "Weiter ansehen"
},
"mediaList": {
"stopEditing": "Bearbeiten beenden"
},
"search": {
"allResults": "Das ist alles, was wir haben!",
"failed": "Das Medium wurde nicht gefunden, bitte versuchen Sie es erneut!",
"loading": "Wird geladen...",
"noResults": "Wir haben nichts gefunden!",
"placeholder": "Was willst du gucken?",
"sectionTitle": "Suchergebnisse"
},
"titles": {
"day": {
"default": "Was würdest du diesen Nachmittag gerne schauen?"
},
"morning": {
"default": "Was würdest du diesen Morgen gerne schauen?",
"extra": [
"Ich hab gehört Before Sunrise soll gut sein"
]
},
"night": {
"default": "Was würdest du diesen Abend gerne schauen?",
"extra": [
"Müde? Ich hab gehört The Exorcist soll gut sein."
]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "Film",
"show": "Serie"
},
"episodeDisplay": "S{{season}} E{{episode}}"
},
"player": {
"playbackError": {
"title": "Hoppla, etwas ist schiefgegangen!"
},
"metadata": {
"notFound": {
"badge": "Nicht gefunden",
"homeButton": "Zurück zur Startseite",
"title": "Das Medium konnte nicht gefunden werden.",
"text": "Wir konnten die angeforderten Medien nicht finden."
}
},
"menus": {
"captions": {
"customChoice": "Untertitel hochladen",
"customizeLabel": "Bearbeiten",
"title": "Untertitel"
"navigation": {
"banner": {
"offline": "Internetverbindung ist instabil"
},
"sources": {
"title": "Quellen"
},
"episodes": {
"button": "Folgen",
"loadingTitle": "Wird geladen...",
"loadingList": "Wird geladen..."
}
},
"back": {
"default": "Zurück zur Startseite",
"short": "Rückmeldung"
"menu": {
"about": "Über uns",
"donation": "Spenden",
"logout": "Abmelden",
"register": "Mit der Cloud synchronisieren",
"settings": "Einstellungen",
"support": "Support"
}
},
"notFound": {
"badge": "Nicht gefunden",
"goHome": "Zurück zur Startseite",
"title": "Diese Seite kann nicht gefunden werden",
"message": "Wir haben überall gesucht, aber am Ende konnten wir die gesuchte Seite nicht finden."
"message": "Wir haben überall gesucht: Unter den Eimern, im Schrank, hinter der Proxy, aber am Ende konnten wir die gesuchte Seite nicht finden.",
"title": "Diese Seite wurde nicht gefunden"
},
"navigation": {
"banner": {
"offline": "Internetverbindung ist instabil"
"overlays": {
"close": "Schließen"
},
"player": {
"back": {
"default": "Zurück zur Startseite",
"short": "Rückmeldung"
},
"casting": {
"enabled": "Casting zum Gerät..."
},
"menus": {
"captions": {
"customChoice": "Untertitel hochladen",
"customizeLabel": "Bearbeiten",
"offChoice": "Aus",
"settings": {
"delay": "Untertitelverzögerung",
"fixCapitals": "Großschreibung korrigieren"
},
"title": "Untertitel",
"unknownLanguage": "Unbekannt"
},
"downloads": {
"disclaimer": "Videos werden direkt vom Provider heruntergeladen. movie-web hat nicht steuern, wie die Downloads bereitgestellt werden.",
"downloadCaption": "Ausgewählte Untertitel herunterladen",
"downloadVideo": "Video herunterladen",
"hlsExplanation": "Dieses Video ist ein HLS-Stream, welcher auf movie-web nicht heruntergeladen werden kann.",
"onAndroid": {
"1": "Um auf Android Herunterzuladen, tippe auf den Download-Button, <bold>tippe und halte</bold> auf der neuen Seite auf das Video und wähle <bold>Speichern</bold> aus.",
"shortTitle": "Download / Android",
"title": "Auf Android herunterladen"
},
"onIos": {
"1": "Um Auf iOS herunterzuladen, klick auf den Download-Button. Klicke dann auf der neuen Seite auf<bold> <ios_share /></bold>, dann auf <bold>In Dateien sichern <ios_files /></bold>.",
"shortTitle": "Download / iOS",
"title": "Auf iOS herunterladen"
},
"onPc": {
"1": "Um am PC herunterzuladen, klicke auf den Download-Button. Klicke dann mit der rechten Maustaste auf das Video und klicke auf <bold>Video speichern als</bold>",
"shortTitle": "Download / PC",
"title": "Am PC herunterladen"
},
"title": "Download"
},
"episodes": {
"button": "Folgen",
"emptyState": "Keine Folgen in dieser Staffel, schau später noch einmal!",
"episodeBadge": "E{{episode}}",
"loadingError": "Fehler beim Laden der Sitzung",
"loadingList": "Wird geladen...",
"loadingTitle": "Wird geladen..."
},
"playback": {
"speedLabel": "Wiedergabegeschwindigkeit",
"title": "Wiedergabeeinstellungen"
},
"quality": {
"automaticLabel": "Automatische Qualitätseinstellung",
"hint": "Du kannst versuchen die <0>Quelle zu ändern</0> um andere Qualitätsoptionen zu erhalten.",
"iosNoQuality": "Durch eine Einschränkung von Apple ist die Qualitätsauswahl für iOS für diese Quelle nicht verfügbar. Du kannst versuchen <0>einen andere Quelle auszuwählen</0> um andere Qualitätsoptionen zu erhalten.",
"title": "Qualität"
},
"settings": {
"captionItem": "Untertiteleinstellungen",
"downloadItem": "Download",
"enableCaptions": "Untertitel aktivieren",
"experienceSection": "Anzeigeerlebnis",
"playbackItem": "Wiedergabeeinstellungen",
"qualityItem": "Qualität",
"sourceItem": "Videoquellen",
"videoSection": "Videoeinstellungen"
},
"sources": {
"failed": {
"text": "Beim Versuch, Videos zu finden, ist ein Fehler aufgetreten. Bitte versuche es mit einer anderen Quelle.",
"title": "Scrapen fehlgeschlagen"
},
"noEmbeds": {
"text": "Es konnten keine Embeds gefunden werden. Bitte versuchen es mit einer anderen Quelle.",
"title": "Keine Embeds gefunden"
},
"noStream": {
"text": "Diese Quelle bietet keine Streams für diesen Film oder diese Serie.",
"title": "Kein Stream"
},
"title": "Quellen",
"unknownOption": "Unbekannt"
}
},
"metadata": {
"failed": {
"badge": "Fehlgeschlagen",
"homeButton": "Zurück zur Startseite",
"text": "Konnte die Videometadaten nicht von TMDB laden. Überprüfe ob TMDB funktioniert oder von deiner Internetverbindung gesperrt wird.",
"title": "Laden der Metadaten ist fehlgeschlagen"
},
"notFound": {
"badge": "Nicht gefunden",
"homeButton": "Zurück zur Startseite",
"text": "Wir konnten das angeforderte Medium nicht finden.",
"title": "Das Medium konnte nicht gefunden werden."
}
},
"nextEpisode": {
"cancel": "Abbrechen",
"next": "Nächste Folge"
},
"playbackError": {
"badge": "Wiedergabefehler",
"errors": {
"errorAborted": "Das Laden des Videos wurde vom Nutzer abgebrochen.",
"errorDecode": "Trotz vorheriger Feststellung der Nutzbarkeit trat ein Fehler beim Versuch auf, die Mediumdatei zu decodieren, was zu einem Fehler führte.",
"errorGenericMedia": "Unbekannter Videofehler ist aufgetreten.",
"errorNetwork": "Es ist ein Netzwerkfehler aufgetreten, der das erfolgreiche Abrufen des Mediums verhinderte, obwohl es zuvor verfügbar war.",
"errorNotSupported": "Das Medium- oder Mediumanbieterobjekt wird nicht unterstützt."
},
"homeButton": "Zurück zur Startseite",
"text": "Ein Fehler ist während der Wiedergabe aufgetreten. Versuche es erneut.",
"title": "Hoppla, etwas ist schiefgegangen!"
},
"scraping": {
"items": {
"failure": "Ein Fehler ist aufgetreten",
"notFound": "Video nicht gefunden",
"pending": "Suche nach Videos..."
},
"notFound": {
"badge": "Nicht gefunden",
"detailsButton": "Details anzeigen",
"homeButton": "Zurück zur Startseite",
"text": "Wir haben alle Anbieter durchsucht, konnten aber nicht das Video finden nach dem du suchst! Wir stellen keine eigenen Videos bereit und haben keine Kontrolle darüber, was verfügbar ist. Bitte klicke \"Details anzeigen\" für mehr Details.",
"title": "Wir konnten das nicht finden"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} übrig • Fertig um {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
}
},
"screens": {
"dmca": {
"text": "Willkommen zu movie-webs DMCA-Kontaktseite! Wir respektieren geistiges Eigentum und wollen uns schnell um urheberrechtlichen Anliegen kümmern. Falls du glaubst, dass dein urheberrechtlich geschütztes Werk unsachgemäß auf unserer Plattform verwendet wurde, sende uns bitte eine genaue DMCA-Anfrage an die unten stehende E-Mail. Diese sollte eine Beschreibung des urheberrechtlich geschützten Material, deine Kontaktinformationen sowie einer Erklärung des guten Glaubens beinhalten. Wir sind engagiert diese Anliegen schnell zu lösen und schätzen deine Hilfe dabei movie-web zu einer Plattform, welche Kreativität und Urheberrechte respektiert.",
"title": "DMCA"
},
"loadingApp": "Die App wird geladen",
"loadingUser": "Dein Profil wird geladen",
"loadingUserError": {
"logout": "Abmelden",
"reset": "Eigenen Server zurücksetzen",
"text": "Beim Laden deines Profils ist ein Fehler aufgetreten",
"textWithReset": "Beim Laden deines Profils von deinem Server ist ein Fehler aufgetreten, zurück zum Standard-Server wechseln?"
},
"migration": {
"failed": "Beim Migrieren deiner Daten ist ein Fehler aufgetreten.",
"inProgress": "Bitte warte, wir migrieren deine Daten. Das sollte nicht lange dauern."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "Gerätename",
"deviceNamePlaceholder": "Handy",
"editProfile": "Bearbeiten",
"logoutButton": "Abmelden"
},
"actions": {
"delete": {
"button": "Konto löschen",
"confirmButton": "Konto löschen",
"confirmDescription": "Konto wirklich löschen? Alle deine Daten gehen dabei verloren!",
"confirmTitle": "Bist du sicher?",
"text": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Daten werden gelöscht und können nicht wiederhergestellt werden.",
"title": "Konto löschen"
},
"title": "Aktionen"
},
"devices": {
"deviceNameLabel": "Gerätename",
"failed": "Laden der Sitzungen fehlgeschlagen",
"removeDevice": "Entfernen",
"title": "Geräte"
},
"profile": {
"finish": "Bearbeiten beenden",
"firstColor": "Profilfarbe 1",
"secondColor": "Profilfarbe 2",
"title": "Profilbild bearbeiten",
"userIcon": "Nutzersymbol"
},
"register": {
"cta": "Los geht's",
"text": "Teilen deinen Fortschritt zwischen Geräten und halte sie synchronisiert.",
"title": "Mit der Cloud synchronisieren"
},
"title": "Konto"
},
"appearance": {
"activeTheme": "Aktiv",
"themes": {
"blue": "Blau",
"default": "Standard",
"gray": "Grau",
"red": "Rot",
"teal": "Türkis"
},
"title": "Aussehen"
},
"captions": {
"backgroundLabel": "Hintergrund-Deckkraft",
"colorLabel": "Farbe",
"previewQuote": "Das Gras wächst nicht schneller, wenn man daran zieht.",
"textSizeLabel": "Schriftgröße",
"title": "Untertitel"
},
"connections": {
"server": {
"description": "Falls du dich mit einem anderen Server verbinden willst, um deine Daten zu speichern. Aktiviere dies und gebe die URL an.",
"label": "Eigener Server",
"urlLabel": "Eigene Server-URL"
},
"title": "Verbindungen",
"workers": {
"addButton": "Neuen Worker hinzufügen",
"description": "Damit die App funktioniert werden alle Anfrage durch einen Proxy geleitet. Aktiviere dies, falls du deinen eigenen Worker verwenden willst.",
"emptyState": "Keine Worker vorhanden, füge einen unten hinzu",
"label": "Verwenden deinen eigenen Worker-Proxys",
"urlLabel": "Worker-URLs",
"urlPlaceholder": "https://"
}
},
"locale": {
"language": "App-Sprache",
"languageDescription": "Sprache für die ganze App.",
"title": "Sprache"
},
"reset": "Zurücksetzen",
"save": "Speichern",
"sidebar": {
"info": {
"appVersion": "App-Version",
"backendUrl": "Server-URL",
"backendVersion": "Server-Version",
"hostname": "Hostname",
"insecure": "Unsicher",
"notLoggedIn": "Du bist nicht angemeldet",
"secure": "Sicher",
"title": "App-Informationen",
"unknownVersion": "Unbekannt",
"userId": "Nutzer-ID"
}
},
"unsaved": "Du hast ungespeicherte Änderungen"
}
}

View File

@ -1,71 +1,420 @@
{
"about": {
"description": "movie-web est une application web qui recherche des flux sur Internet. L'équipe vise une approche minimaliste de la consommation de contenu.",
"faqTitle": "Questions fréquentes",
"q1": {
"body": "movie-web n'héberge aucun contenu. Lorsque vous cliquez sur un élément à regarder, une recherche est effectuée sur Internet pour trouver le média sélectionné (sur l'écran de chargement et dans l'onglet \"sources vidéo\", vous pouvez voir quelle source vous utilisez). Les médias ne sont jamais téléchargés par movie-web, tout passe par ce mécanisme de recherche.",
"title": "D'où vient le contenu ?"
},
"q2": {
"body": "Il n'est pas possible de demander une émission ou un film, movie-web ne gère aucun contenu. Tous les contenus sont consultés par l'intermédiaire de sources sur Internet.",
"title": "Où puis-je demander un show ou un film ?"
},
"q3": {
"body": "Nos résultats de recherche sont fournis par The Movie Database (TMDB) et s'affichent indépendamment du fait que nos sources possèdent ou non le contenu.",
"title": "Les résultats de la recherche affichent l'émission ou le film, pourquoi ne puis-je pas le lire ?"
},
"title": "A propos de movie-web"
},
"actions": {
"copied": "Copié",
"copy": "Copier"
},
"auth": {
"createAccount": "Vous n'avez pas encore de compte ? <0>Créer un compte.</0>",
"deviceNameLabel": "Nom de l'appareil",
"deviceNamePlaceholder": "Téléphone personnel",
"generate": {
"description": "Votre passphrase fait office de nom d'utilisateur et de mot de passe. Conservez-la précieusement, car vous devrez la saisir pour vous connecter à votre compte",
"next": "J'ai sauvegardé ma passphrase",
"title": "Votre passphrase"
},
"hasAccount": "Vous avez déjà un compte ? <0>Connectez-vous ici.</0>",
"login": {
"description": "Veuillez entrer votre passphrase pour vous connecter à votre compte",
"deviceLengthError": "Veuillez saisir un nom d'appareil",
"passphraseLabel": "Passphrase de 12 mots",
"passphrasePlaceholder": "Passphrase",
"submit": "Se connecter",
"title": "Se connecter à votre compte",
"validationError": "Passphrase incorrecte ou incomplete"
},
"register": {
"information": {
"color1": "Couleur de profile un",
"color2": "Couleur de profile deux",
"header": "Entrez un nom pour votre appareil et choisissez une couleur de profile ainsi qu'une icône d'utilisateur de votre choix",
"icon": "Icône d'utilisateur",
"next": "Prochain",
"title": "Informations sur le compte"
}
},
"trust": {
"failed": {
"text": "L'avez-vous configuré correctement ?",
"title": "Échec de la connexion au serveur"
},
"host": "Vous vous connectez à <0>{{hostname}}</0> - veuillez confirmer que vous lui faites confiance avant de créer un compte.",
"no": "Retour",
"title": "Faites-vous confiance à ce serveur ?",
"yes": "Je fais confiance à ce serveur"
},
"verify": {
"description": "Veuillez saisir votre passphrase pour confirmer que vous l'avez enregistrée et pour créer votre compte",
"invalidData": "Les données ne sont pas valides",
"noMatch": "La passphrase ne correspond pas",
"passphraseLabel": "Votre passphrase de 12 mots",
"recaptchaFailed": "La validation ReCaptcha a échoué",
"register": "Créer un compte",
"title": "Confirmez votre passphrase"
}
},
"errors": {
"badge": "Il s'est cassé",
"details": "Détails de l'erreur",
"reloadPage": "Actualiser la page",
"showError": "Afficher les détails de l'erreur",
"title": "Nous avons rencontré une erreur !"
},
"footer": {
"legal": {
"disclaimer": "Avertissement",
"disclaimerText": "movie-web n'héberge aucun fichier, il se contente de proposer des liens vers des services tiers. Les questions juridiques doivent être réglées avec les hébergeurs et les fournisseurs de fichiers. movie-web n'est pas responsable des fichiers multimédias diffusés par les fournisseurs de vidéos."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Regardez vos émissions et films préférés avec cette application de streaming open source."
},
"global": {
"name": "movie-web"
"name": "movie-web",
"pages": {
"about": "À propos",
"dmca": "DMCA",
"login": "Se connecter",
"pagetitle": "{{title}} - movie-web",
"register": "Créer un compte",
"settings": "Paramètres"
}
},
"home": {
"search": {
"allResults": "C'est tout ce que nous avons!",
"sectionTitle": "Résultats de la recherche",
"noResults": "Nous n'avons rien trouvé!",
"failed": "Le média n'a pas été trouvé, veuillez réessayez!",
"loading": "Chargement...",
"placeholder": "Que voulez-vous voir?"
},
"bookmarks": {
"sectionTitle": "Favoris"
},
"continueWatching": {
"sectionTitle": "Continuer le visionnage"
},
"mediaList": {
"stopEditing": "Arrêter l'édition"
},
"search": {
"allResults": "C'est tout ce que nous avons!",
"failed": "Le média n'a pas été trouvé, veuillez réessayez!",
"loading": "Chargement...",
"noResults": "Nous n'avons rien trouvé!",
"placeholder": "Que voulez-vous voir?",
"sectionTitle": "Résultats de la recherche"
},
"titles": {
"day": {
"default": "Que voulez-vous regarder cet après-midi ?"
},
"morning": {
"default": "Que voulez-vous regarder ce matin ?",
"extra": [
"J'ai entendu dire que Before Sunrise était un bon film"
]
},
"night": {
"default": "Que voulez-vous regarder ce soir ?",
"extra": [
"Fatigué ? J'ai entendu dire que L'Exorciste était bien."
]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "Film",
"show": "Série"
},
"episodeDisplay": "S{{season}} E{{episode}}"
},
"player": {
"playbackError": {
"title": "Oups, c'est coupé !"
},
"metadata": {
"notFound": {
"badge": "Introuvable",
"homeButton": "Retour à l'accueil",
"title": "Impossible de trouver ce média.",
"text": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL."
}
},
"menus": {
"captions": {
"customChoice": "Télécharger des sous-titres",
"customizeLabel": "Personnaliser",
"title": "Sous-titres"
"navigation": {
"banner": {
"offline": "Vérifiez votre connexion internet"
},
"sources": {
"title": "Sources"
},
"episodes": {
"button": "Épisodes",
"loadingTitle": "Chargement...",
"loadingList": "Chargement..."
}
},
"back": {
"default": "Retour à la page d'accueil",
"short": "Retour"
"menu": {
"about": "À propos de nous",
"donation": "Faire un don",
"logout": "Se déconnecter",
"register": "Synchroniser au Cloud",
"settings": "Paramètres",
"support": "Support"
}
},
"notFound": {
"badge": "Introuvable",
"goHome": "Retour à l'accueil",
"title": "Impossible de trouver cette page",
"message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez."
"message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez.",
"title": "Impossible de trouver cette page"
},
"navigation": {
"banner": {
"offline": "Vérifiez votre connexion internet"
"overlays": {
"close": "Fermer"
},
"player": {
"back": {
"default": "Retour à la page d'accueil",
"short": "Retour"
},
"casting": {
"enabled": "Casting à l'appareil..."
},
"menus": {
"captions": {
"customChoice": "Télécharger des sous-titres",
"customizeLabel": "Personnaliser",
"offChoice": "Désactivé",
"settings": {
"delay": "Délai des sous-titres",
"fixCapitals": "Correction de la majuscule"
},
"title": "Sous-titres",
"unknownLanguage": "Inconnu"
},
"downloads": {
"disclaimer": "Les téléchargements sont effectués directement par le fournisseur. movie-web n'a aucun contrôle sur la manière dont les téléchargements sont effectués.",
"downloadCaption": "Télécharger les sous-titres actuels",
"downloadVideo": "Télécharger la vidéo",
"hlsExplanation": "Ce média est un flux HLS qui ne peut pas être téléchargé sur movie-web.",
"onAndroid": {
"1": "Pour télécharger sur Android, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, <bold>tapez et maintenez </bold> sur la vidéo, puis sélectionnez <bold>enregistrer</bold>.",
"shortTitle": "Télécharger / Android",
"title": "Téléchargement sur Android"
},
"onIos": {
"1": "Pour télécharger sur iOS, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, cliquez sur <bold><ios_share /></bold>, puis <bold>Enregistrer dans les fichiers <ios_files /></bold>.",
"shortTitle": "Télécharger / iOS",
"title": "Télécharger sur iOS"
},
"onPc": {
"1": "Sur PC, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, faites un clic droit sur la vidéo et sélectionnez <bold>Enregistrer la vidéo sous</bold>",
"shortTitle": "Télécharger / PC",
"title": "Téléchargement sur PC"
},
"title": "Télécharger"
},
"episodes": {
"button": "Épisodes",
"emptyState": "Il n'y a pas d'épisodes dans cette saison, revenez plus tard !",
"episodeBadge": "E{{episode}}",
"loadingError": "Erreur de chargement de la saison",
"loadingList": "Chargement...",
"loadingTitle": "Chargement..."
},
"playback": {
"speedLabel": "Vitesse de lecture",
"title": "Paramètres de lecture"
},
"quality": {
"automaticLabel": "Qualité automatique",
"hint": "Vous pouvez essayer de <0>changer de fournisseur/0> pour obtenir différentes options de qualité.",
"iosNoQuality": "En raison des limitations définies par Apple, la sélection de la qualité n'est pas disponible sur iOS pour cette source. Vous pouvez essayer <0>de passer à une autre source</0> pour obtenir des options de qualité différentes.",
"title": "Qualité"
},
"settings": {
"captionItem": "Paramètres des sous-titres",
"downloadItem": "Télécharger",
"enableCaptions": "Activer les sous-titres",
"experienceSection": "Expérience de visionnage",
"playbackItem": "Paramètres de lecture",
"qualityItem": "Qualité",
"sourceItem": "Sources vidéo",
"videoSection": "Paramètres vidéo"
},
"sources": {
"failed": {
"text": "Une erreur s'est produite lors de la recherche de vidéos, veuillez essayer une autre source.",
"title": "Échec de la récupération (scrape)"
},
"noEmbeds": {
"text": "Nous n'avons pas trouvé de liens, veuillez essayer une autre source.",
"title": "Pas d'embeds trouvés"
},
"noStream": {
"text": "Cette source n'a pas de flux pour ce film ou cette émission.",
"title": "Pas de flux"
},
"title": "Sources",
"unknownOption": "Inconnu"
}
},
"metadata": {
"failed": {
"badge": "Échec",
"homeButton": "Retour à la maison",
"text": "Impossible de charger les métadonnées du média à partir de TMDB. Veuillez vérifier si TMDB est en panne ou bloqué sur votre connexion internet.",
"title": "Échec du chargement des métadonnées"
},
"notFound": {
"badge": "Introuvable",
"homeButton": "Retour à l'accueil",
"text": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL.",
"title": "Impossible de trouver ce média."
}
},
"nextEpisode": {
"cancel": "Annuler",
"next": "Prochain épisode"
},
"playbackError": {
"badge": "Erreur de lecture",
"errors": {
"errorAborted": "L'extraction du média a été interrompue à la demande de l'utilisateur.",
"errorDecode": "Bien qu'elle ait été jugée utilisable, une erreur s'est produite lors de la tentative de décodage de la ressource multimédia, ce qui a entraîné une erreur.",
"errorGenericMedia": "Une erreur de média inconnue est survenue.",
"errorNetwork": "Une erreur de réseau s'est produite qui a empêché la récupération du média, bien qu'il ait été disponible auparavant.",
"errorNotSupported": "L'objet du media ou de la source du média n'est pas supporté."
},
"homeButton": "Retour à la maison",
"text": "Une erreur s'est produite lors de la lecture du média. Veuillez réessayer.",
"title": "Oups, c'est coupé !"
},
"scraping": {
"items": {
"failure": "Une erreur est survenue",
"notFound": "N'a pas la vidéo",
"pending": "Recherche de vidéos..."
},
"notFound": {
"badge": "Non trouvé",
"detailsButton": "Afficher les détails",
"homeButton": "Retour à la maison",
"text": "Nous avons cherché parmi nos sources et n'avons pas trouvé les médias que vous recherchez ! Nous n'hébergeons pas les médias et n'avons aucun contrôle sur ce qui est disponible. Veuillez cliquer sur \"Afficher les détails\" ci-dessous pour plus d'informations.",
"title": "Nous n'avons pas trouvé cela"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} restant • Fini à {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
}
},
"screens": {
"dmca": {
"text": "Bienvenue sur la page de contact DMCA de movie-web ! Nous respectons les droits de propriété intellectuelle et souhaitons répondre rapidement à toute question relative aux droits d'auteur. Si vous pensez que votre œuvre protégée par des droits d'auteur a été utilisée de manière inappropriée sur notre plateforme, veuillez envoyer une notification DMCA détaillée à l'adresse électronique ci-dessous. Veuillez inclure une description du matériel protégé par des droits d'auteur, vos coordonnées et une déclaration de bonne foi. Nous nous engageons à résoudre ces problèmes rapidement et vous remercions de votre coopération pour que movie-web reste un lieu respectueux de la créativité et des droits d'auteur.",
"title": "DMCA"
},
"loadingApp": "Chargement de l'application",
"loadingUser": "Chargement de votre profil",
"loadingUserError": {
"logout": "Se déconnecter",
"reset": "Réinitialiser le serveur personnalisé",
"text": "Échec du chargement de votre profil",
"textWithReset": "Echec du chargement de votre profil à partir de votre serveur personnalisé, souhaitez-vous revenir au serveur par défaut ?"
},
"migration": {
"failed": "La migration de vos données a échoué.",
"inProgress": "Veuillez patienter, nous sommes en train de migrer vos données. Cela ne devrait pas prendre longtemps."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "Nom de l'appareil",
"deviceNamePlaceholder": "Téléphone personnel",
"editProfile": "Éditer",
"logoutButton": "Se déconnecter"
},
"actions": {
"delete": {
"button": "Supprimer le compte",
"confirmButton": "Supprimer le compte",
"confirmDescription": "Êtes-vous sûr de vouloir supprimer votre compte ? Toutes vos données seront perdues !",
"confirmTitle": "Êtes-vous sûr ?",
"text": "Cette action est irréversible. Toutes les données seront supprimées et rien ne pourra être récupéré.",
"title": "Supprimer le compte"
},
"title": "Actions"
},
"devices": {
"deviceNameLabel": "Nom de l'appareil",
"failed": "Échec du chargement des sessions",
"removeDevice": "Enlever",
"title": "Appareils"
},
"profile": {
"finish": "Terminer l'édition",
"firstColor": "Couleur de profil un",
"secondColor": "Couleur de profil deux",
"title": "Éditer la photo de profil",
"userIcon": "Icône de l'utilisateur"
},
"register": {
"cta": "Commencer",
"text": "Partagez la progression de vos films et séries entre vos appareils et gardez-les synchronisés.",
"title": "Synchroniser au Cloud"
},
"title": "Compte"
},
"appearance": {
"activeTheme": "Actif",
"themes": {
"blue": "Bleu",
"default": "Défaut",
"gray": "Gris",
"red": "Rouge",
"teal": "Saphir"
},
"title": "Apparence"
},
"captions": {
"backgroundLabel": "Opacité de l'arrière-plan",
"colorLabel": "Couleur",
"previewQuote": "Je ne dois pas avoir peur. La peur est un tueur d'esprit.",
"textSizeLabel": "Taille du texte",
"title": "Sous-titres"
},
"connections": {
"server": {
"description": "Si vous souhaitez vous connecter à un backend personnalisé pour stocker vos données, activez cette option et indiquez l'URL.",
"label": "Serveur personnalisé",
"urlLabel": "URL du serveur personnalisé"
},
"title": "Connexions",
"workers": {
"addButton": "Ajouter un nouveau worker",
"description": "Pour que l'application fonctionne, tout le trafic est acheminé via des proxys. Activez cette option si vous souhaitez faire appel à vos propres workers.",
"emptyState": "Pas encore de workers, ajoutez-en un ci-dessous",
"label": "Utiliser des agents proxy personnalisés",
"urlLabel": "URLs des workers",
"urlPlaceholder": "https://"
}
},
"locale": {
"language": "Langue de l'application",
"languageDescription": "Langue appliquée dans l'ensemble de l'app.",
"title": "Local"
},
"reset": "Réinitialiser",
"save": "Sauvegarder",
"sidebar": {
"info": {
"appVersion": "Version de l'application",
"backendUrl": "URL de Backend",
"backendVersion": "Version de la Backend",
"hostname": "Nom d'hôte",
"insecure": "Insécure",
"notLoggedIn": "Vous n'êtes pas connecté",
"secure": "Sécurisé",
"title": "Informations sur l'application",
"unknownVersion": "Inconnu",
"userId": "ID de l'utilisateur"
}
},
"unsaved": "Vous avez des changements non sauvegardés"
}
}

420
src/assets/locales/he.json Normal file
View File

@ -0,0 +1,420 @@
{
"about": {
"description": "movie-web הוא יישום אינטרנט המחפש באינטרנט אחר זרמים. הצוות שואף לגישה מינימליסטית ברובה לצריכת תוכן.",
"faqTitle": "שאלות נפוצות",
"q1": {
"body": "movie-web אינו מארח תוכן כלשהו. כאשר אתה לוחץ על משהו לצפייה, האינטרנט מחפש את המדיה שנבחרה (במסך הטעינה ובכרטיסייה 'מקורות וידאו' תוכל לראות באיזה מקור אתה משתמש). מדיה אף פעם לא מועלת על ידי movie-web, הכל מתבצע דרך מנגנון חיפוש זה.",
"title": "מאיפה התוכן?"
},
"q2": {
"body": "לא ניתן לבקש תוכנית או סרט, movie-web לא מנהלת שום תוכן. כל התוכן נצפה דרך מקורות באינטרנט.",
"title": "איפה אני יכול לבקש תוכנית או סרט?"
},
"q3": {
"body": "תוצאות החיפוש שלנו מופעלות על ידי The Movie Database (TMDB) ומוצגות ללא קשר אם למקורות שלנו יש את התוכן.",
"title": "תוצאות החיפוש מציגות את התוכנית או הסרט, למה אני לא יכול להפעיל אותם?"
},
"title": "על movie-web"
},
"actions": {
"copied": "הועתק",
"copy": "להעתיק"
},
"auth": {
"createAccount": "אין לך עדיין חשבון? <0>צור חשבון.</0>",
"deviceNameLabel": "שם המכשיר",
"deviceNamePlaceholder": "מכשיר אישי",
"generate": {
"description": "ביטוי הסיסמה שלך משמש כשם המשתמש והסיסמה שלך. אנא הקפד לשמור אותו בטוח מכיוון שתצטרך להזין אותו כדי להתחבר לחשבון שלך",
"next": "אני שמרתי את משפט הסיסמה שלי",
"title": "משפט הסיסמה שלך"
},
"hasAccount": "Already have an account? <0>Login here.</0>",
"login": {
"description": "אנא הזן את ביטוי הסיסמה שלך כדי להתחבר לחשבונך",
"deviceLengthError": "אנא הזן שם מכשיר",
"passphraseLabel": "ביטוי סיסמא בעל 12 מילים",
"passphrasePlaceholder": "ביטוי סיסמא",
"submit": "התחבר",
"title": "התחבר לחשבונך",
"validationError": "ביטוי סיסמה שגוי או לא שלם"
},
"register": {
"information": {
"color1": "צבע פרופיל ראשון",
"color2": "צבע פרופיל שני",
"header": "הזן שם למכשירך ובחר צבעים וסמל משתמש לפי בחירתך",
"icon": "סמל משתמש",
"next": "הבא",
"title": "פרטי חשבון"
}
},
"trust": {
"failed": {
"text": "האם הגדרת את זה נכון?",
"title": "הגישה לשרת נכשלה"
},
"host": "אתה מתחבר אל <0>{{hostname}}</0> - אנא אשר שאתה סומך עליו לפני יצירת חשבון",
"no": "חזור",
"title": "האם אתה סומך על שרת זה?",
"yes": "אני בוטח בשרת זה"
},
"verify": {
"description": "אנא הזן את משפט הסיסמה שלך מקודם כדי לאשר ששמרת אותו וכדי ליצור את חשבונך",
"invalidData": "הנתונים אינם חוקיים",
"noMatch": "ביטוי הסיסמה אינו תואם",
"passphraseLabel": "ביטוי הסיסמה שלך בעל 12 מילים",
"recaptchaFailed": "אימות ReCaptcha נכשל",
"register": "צור חשבון",
"title": "אשר את ביטוי הסיסמה שלך"
}
},
"errors": {
"badge": "זה נשבר",
"details": "פרטי שגיאה",
"reloadPage": "טען מחדש את הדף",
"showError": "הצג פרטי שגיאה",
"title": "נתקלנו בשגיאה!"
},
"footer": {
"legal": {
"disclaimer": "תנית ויתור",
"disclaimerText": "movie-web אינו מארח קבצים, הוא רק מקשר לשירותי צד שלישי. יש להתייחס לסוגיות משפטיות עם המארחים והספקים של הקבצים. movie-web אינה אחראית לכל קבצי מדיה המוצגים על ידי ספקי הווידאו."
},
"links": {
"discord": "דיסקורד",
"dmca": "DMCA",
"github": "גיטהאב"
},
"tagline": "צפה בתוכניות ובסרטים האהובים עליך עם אפליקציית סטרימינג זו בקוד פתוח."
},
"global": {
"name": "movie-web",
"pages": {
"about": "אודות",
"dmca": "DMCA",
"login": "התחבר",
"pagetitle": "{{title}} - movie-web",
"register": "הירשם",
"settings": "הגדרות"
}
},
"home": {
"bookmarks": {
"sectionTitle": "סימניות"
},
"continueWatching": {
"sectionTitle": "המשך לצפות"
},
"mediaList": {
"stopEditing": "הפסק עריכה"
},
"search": {
"allResults": "זה כל מה שיש לנו!",
"failed": "לא הצלחנו למצוא מדיה, נסה שוב!",
"loading": "טוען...",
"noResults": "לא יכולנו למצוא כלום!",
"placeholder": "במה תרצה לצפות?",
"sectionTitle": "תוצאות חיפוש"
},
"titles": {
"day": {
"default": "במה תרצה לצפות באחר צהריים זה?"
},
"morning": {
"default": "במה תרצה לצפות הבוקר?",
"extra": [
"שמעתי שלפני הזריחה זה סרט טוב"
]
},
"night": {
"default": "במה תרצה לצפות הלילה?",
"extra": [
"עייף? שמעתי שמגרש השדים זה סרט טוב."
]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "סרט",
"show": "סדרה"
}
},
"navigation": {
"banner": {
"offline": "תבדוק את חיבור האינטרנט שלך"
},
"menu": {
"about": "עלינו",
"donation": "לתרום",
"logout": "להתנתק",
"register": "סנכרון לענן",
"settings": "הגדרות",
"support": "תמיכה"
}
},
"notFound": {
"badge": "לא נמצא",
"goHome": "בחזרה לבית",
"message": "חיפשנו בכל מקום: מתחת לפחים, בארון, מאחורי ה-proxy אבל בסופו של דבר לא מצאנו את הדף שאתה מחפש.",
"title": "לא יכולנו למצוא את דף זה"
},
"overlays": {
"close": "סגור"
},
"player": {
"back": {
"default": "חזרה לדף הבית",
"short": "חזור"
},
"casting": {
"enabled": "משדר למכשיר..."
},
"menus": {
"captions": {
"customChoice": "בחר כתוביות מהקובץ",
"customizeLabel": "התאם אישית",
"offChoice": "כבוי",
"settings": {
"delay": "עיכוב בכיתוב",
"fixCapitals": "תקן שימוש באותיות גדולות"
},
"title": "כתוביות",
"unknownLanguage": "לא ידוע"
},
"downloads": {
"disclaimer": "ההורדות נלקחות ישירות מהספק. ל-movie-web אין שליטה על האופן שבו מסופקות ההורדות.",
"downloadCaption": "הורד את הכתוביות הנוכחיות",
"downloadVideo": "הורד וידאו",
"hlsExplanation": "מדיה זו היא זרם HLS שאינו ניתן להורדה ב-movie-web.",
"onAndroid": {
"1": "כדי להוריד באנדרואיד, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, <bold>הקש והחזק</bold> על הסרטון, ולאחר מכן בחר <bold>שמור</bold>.",
"shortTitle": "הורדה / אנדרויד",
"title": "הורדה באנדרויד"
},
"onIos": {
"1": "כדי להוריד ב-iOS, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, לחץ על <bold><ios_share /></bold> ולאחר מכן על <bold>שמור לקבצים <ios_files /></bold>.",
"shortTitle": "הורדה / iOS",
"title": "מוריד ב-iOS"
},
"onPc": {
"1": "במחשב, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, לחץ לחיצה ימנית על הסרטון ובחר <bold>שמור סרטון בשם</bold>",
"shortTitle": "הורדה / PC",
"title": "הורדה במחשב"
},
"title": "הורד"
},
"episodes": {
"button": "פרקים",
"emptyState": "אין פרקים בעונה זו, אנא בדוק שוב מאוחר יותר!",
"episodeBadge": "פ{{episode}}",
"loadingError": "ארע שגיאה בטעינת העונה",
"loadingList": "טוען...",
"loadingTitle": "טוען..."
},
"playback": {
"speedLabel": "מהירות הניגון",
"title": "הגדרות ניגון"
},
"quality": {
"automaticLabel": "איכות אוטומטית",
"hint": "You can try <0>switching source</0> to get different quality options.",
"iosNoQuality": "Due to Apple-defined limitations, quality selection is not available on iOS for this source. You can try <0>switching to another source</0> to get different quality options.",
"title": "איכות"
},
"settings": {
"captionItem": "הגדרות כתוביות",
"downloadItem": "הורד",
"enableCaptions": "אפשר כתוביות",
"experienceSection": "חווית צפייה",
"playbackItem": "הגדרות השמעה",
"qualityItem": "איכות",
"sourceItem": "מקורות וידאו",
"videoSection": "הגדרות וידאו"
},
"sources": {
"failed": {
"text": "אירעה שגיאה בעת ניסיון למצוא סרטונים, אנא נסה מקור אחר.",
"title": "לא הצליח לחלץ"
},
"noEmbeds": {
"text": "לא הצלחנו למצוא שום הטעמות, אנא נסה מקור אחר.",
"title": "לא נמצאו הטמעות"
},
"noStream": {
"text": "למקור זה אין זרמים עבור הסרט או התוכנית הזו.",
"title": "אין זרם"
},
"title": "מקורות",
"unknownOption": "לא ידוע"
}
},
"metadata": {
"failed": {
"badge": "נכשל",
"homeButton": "חזור לדף הבית",
"text": "לא היה ניתן לטעון את הmetadata של המדיה מ-TMDB. אנא בדוק אם TMDB מושבת או חסום בחיבור האינטרנט שלך.",
"title": "טעינת הmetdata נכשלה"
},
"notFound": {
"badge": "לא נמצא",
"homeButton": "חזרה לדף הבית",
"text": "לא הצלחנו למצוא את המדיה שביקשת. או שהוא הוסר או שהתעסקת בכתובת האתר.",
"title": "לא הצלחנו למצוא את המדיה הזו."
}
},
"nextEpisode": {
"cancel": "בטל",
"next": "פרק הבא"
},
"playbackError": {
"badge": "שגיאת ניגון",
"errors": {
"errorAborted": "השגת המדיה בוטלה על ידי בקשת המשתמש.",
"errorDecode": "למרות שנקבע קודם כל שהמדיה יכולה להשתמש בה, אירעה שגיאה בעת ניסיון לפענח משאבי המדיה, וכתוצאה מכך קרה שגיאה.",
"errorGenericMedia": "‍אירעה שגיאת מדיה לא ידועה.",
"errorNetwork": "אירעה סוג של שגיאת רשת שמנעה גישה למדיה, למרות שהיא הייתה זמינה מראש.",
"errorNotSupported": "המדיה או ספק המדיה אינם נתמכים."
},
"homeButton": "חזור לדף הבית",
"text": "אירעה שגיאה בהפעלת המדיה. אנא נסה שוב.",
"title": "הפעלת הסרטון נכשלה!"
},
"scraping": {
"items": {
"failure": "אירעה שגיאה",
"notFound": "אין את הסרטון",
"pending": "מחפש סרטונים..."
},
"notFound": {
"badge": "לא נמצא",
"detailsButton": "הצג פרטים",
"homeButton": "חזור לדף הבית",
"text": "חיפשנו בספקים שלנו ולא מצאנו את המדיה שאתה מחפש! אנו לא מארחים את המדיה ואין לנו שליטה על מה שזמין. אנא לחץ על 'הצג פרטים' למידע נוסף.",
"title": "לא יכולנו למצוא את זה"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} נשאר • סיים ב {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
}
},
"screens": {
"dmca": {
"text": "ברוכה הבאה לדף יצירת קשר DMCA של movie-web! אנו מכבדים את זכויות הקניין הרוחני ורוצים לטפל בכל חשש לזכויות יוצרים במהירות. אם אתה סבור שהעבודה שלך המוגנת בזכויות יוצרים נוצלה בצורה לא נכונה בפלטפורמה שלנו, אנא שלח הודעת DMCA מפורטת למייל למטה. אנא כלול תיאור של החומר המוגן בזכויות יוצרים, פרטי ההתקשרות שלך והצהרת תום לב. אנו מחויבים לפתור את העניינים הללו באופן מיידי ומעריכים את שיתוף הפעולה שלך בשמירה על movie-web מקום שמכבד יצירתיות וזכויות יוצרים.",
"title": "DMCA"
},
"loadingApp": "טוען את האפליקציה",
"loadingUser": "טוען את הפרופיל שלך",
"loadingUserError": {
"logout": "להתנתק",
"reset": "אפס שרת מותאם אישית",
"text": "טעינת הפרופיל שלך נכשלה",
"textWithReset": "לא הצלחנו לטעון את הפרופיל שלך מהשרת המותאם אישית שלך, תרצה לאפס בחזרה לשרת ברירת המחדל?"
},
"migration": {
"failed": "העברת הנתונים שלך נכשלה.",
"inProgress": "אנא המתן, אנו מעבירים את הנתונים שלך. זה לא אמור לקחת הרבה זמן."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "שם מכשיר",
"deviceNamePlaceholder": "מכשיר אישי",
"editProfile": "ערוך",
"logoutButton": "התנתק"
},
"actions": {
"delete": {
"button": "מחק משתמש",
"confirmButton": "מחק משתמש",
"confirmDescription": "אתה בטוח שתרצה למחוק את המשתמש שלך? כל הנתונים שלך יימחקו!",
"confirmTitle": "אתה בטוח?",
"text": "פעולה זו היא בלתי הפיכה. כל הנתונים יימחקו ולא ניתן יהיה לשחזר דבר.",
"title": "מחק משתמש"
},
"title": "פעולות"
},
"devices": {
"deviceNameLabel": "שם מכשיר",
"failed": "טעינת ההפעלות נכשלה",
"removeDevice": "הסר",
"title": "מכשירים"
},
"profile": {
"finish": "סייםעריכה",
"firstColor": "‫‫צבע פרופיל ראשון",
"secondColor": "צבע פרופיל שני",
"title": "ערוך תמונת פרופיל",
"userIcon": "סמל משתמש"
},
"register": {
"cta": "התחל",
"text": "שתף את התקדמות הצפייה שלך בין מכשירים ושמור אותם מסונכרנים.",
"title": "סנכרון לענן"
},
"title": "משתמש"
},
"appearance": {
"activeTheme": "פעיל",
"themes": {
"blue": "כחול",
"default": "ברירת מחדל",
"gray": "אפור",
"red": "אדום",
"teal": "ירוק כחלחל"
},
"title": "מראה"
},
"captions": {
"backgroundLabel": "אטימות רקע",
"colorLabel": "צבע",
"previewQuote": "אסור לי לפחד. הפחד הוא קוטל הנפש.",
"textSizeLabel": "גודל הטקסט",
"title": "כתוביות"
},
"connections": {
"server": {
"description": "אם תרצה להתחבר ל-backend מותאם אישית כדי לאחסן את הנתונים שלך, הפעל זאת וספק את כתובת האתר.",
"label": "שרת אישי",
"urlLabel": "כתובת אתר מותאמת אישית של שרת"
},
"title": "התחברויות",
"workers": {
"addButton": "הוסף עובד חדש",
"description": "כדי שהאפליקציה תפעל, כל התעבורה מנותבת דרך פרוקסי. הפעל זאת אם אתה רוצה להביא עובדים משלך.",
"emptyState": "עדיין אין עובדים, הוסף אחד למטה",
"label": "השתמש בעובדי פרוקסי מותאמים אישית",
"urlLabel": "כתובות אתרים של עובדים",
"urlPlaceholder": "https://"
}
},
"locale": {
"language": "שפת האפליקציה",
"languageDescription": "השפה החלה על האפליקציה כולה.",
"title": "מקומי"
},
"reset": "איפוס",
"save": "לשמור",
"sidebar": {
"info": {
"appVersion": "גרסת האפליקציה",
"backendUrl": "כתובת אתר אחורי",
"backendVersion": "גרסת Backend",
"hostname": "שם מארח",
"insecure": "לא בטוח",
"notLoggedIn": "אתה לא מחובר",
"secure": "אבטח",
"title": "מידע על האפליקציה",
"unknownVersion": "לא ידוע",
"userId": "זהות המשתמש"
}
},
"unsaved": "יש לך שינויים שלא נשמרו"
}
}

View File

@ -0,0 +1,413 @@
{
"auth": {
"deviceNameLabel": "Device name",
"deviceNamePlaceholder": "Banana phone",
"hasAccount": "Bello! Already have an account? <0>Login here.</0>",
"createAccount": "Whaaaat? Don't have an account yet? <0>Create an account.</0>",
"register": {
"information": {
"title": "Account information",
"color1": "Profile color one",
"color2": "Profile color two",
"icon": "Minion icon",
"header": "Whaaat? Enter a name for your device and pick colors and a minion icon of your choosing",
"next": "Banana!"
}
},
"login": {
"title": "Login to your account",
"description": "Please enter your secret banana language passphrase to login to your account",
"validationError": "Banana language not fluent or incomplete",
"deviceLengthError": "Banana! Please enter a device name",
"submit": "Bello! Login",
"passphraseLabel": "12-Banana passphrase",
"passphrasePlaceholder": "Banana Passphrase"
},
"generate": {
"title": "Your banana passphrase",
"next": "I have saved my banana passphrase",
"description": "Your banana passphrase acts as your banana username and banana password. Make sure to keep it safe as you will need to enter it to banana to your account"
},
"trust": {
"title": "Do you trust this server?",
"host": "You are connecting to <0>{{hostname}}</0> - please confirm you trust it before making a banana account",
"failed": {
"title": "Failed to reach server",
"text": "Did you configure it correctly?"
},
"yes": "I trust this server, banana!",
"no": "Go back, banana"
},
"verify": {
"title": "Confirm your banana passphrase",
"description": "Please enter your banana passphrase from earlier to confirm you have saved it and to create your banana account",
"invalidData": "Banana data is not valid",
"noMatch": "Banana! Passphrase doesn't match",
"recaptchaFailed": "Banana! ReCaptcha validation failed",
"passphraseLabel": "Your 12-banana passphrase",
"register": "Create banana account"
}
},
"errors": {
"details": "Error banana details",
"reloadPage": "Reload the banana",
"showError": "Show banana details",
"badge": "It broke",
"title": "We encountered a banana!"
},
"notFound": {
"badge": "Not found",
"title": "Couldn't find that banana",
"message": "We looked everywhere: under the banana, in the banana, behind the banana but ultimately couldn't find the banana you are looking for.",
"goHome": "Back to banana"
},
"global": {
"name": "banana-web",
"pages": {
"pagetitle": "{{title}} - banana-web",
"dmca": "DMCA",
"settings": "Banana Settings",
"about": "About banana",
"login": "Banana Login",
"register": "Banana Register"
}
},
"media": {
"types": {
"movie": "Banana Movie",
"show": "Banana Show"
},
"episodeDisplay": "S{{season}} E{{episode}}"
},
"player": {
"scraping": {
"notFound": {
"badge": "Not found",
"title": "We couldn't find that banana",
"text": "We have searched through our banana providers and cannot find the banana you are looking for! We do not host the banana and have no control over what is available. Please click 'Show details' below for more details.",
"homeButton": "Go home",
"detailsButton": "Show details"
},
"items": {
"pending": "Checking for banana videos...",
"notFound": "Doesn't have the banana video",
"failure": "Error banana occurred"
}
},
"casting": {
"enabled": "Casting to banana..."
},
"playbackError": {
"badge": "Banana Playback error",
"title": "Failed to play banana video!",
"text": "There was an error trying to play the banana. Please try again.",
"homeButton": "Go home",
"errors": {
"errorAborted": "The fetching of the banana was aborted by the user's banana.",
"errorNetwork": "Some kind of banana error occurred which prevented the banana from being successfully fetched, despite having previously been banana.",
"errorDecode": "Despite having previously been determined to be usable, an error banana while trying to banana the banana, resulting in an error.",
"errorNotSupported": "The banana or banana provider object is not banana.",
"errorGenericMedia": "Unknown banana error occurred."
}
},
"metadata": {
"notFound": {
"badge": "Banana Not found",
"title": "Couldn't find that banana.",
"text": "We couldn't find the banana you requested. Either it's been banana or you tampered with the banana.",
"homeButton": "Back to banana"
},
"failed": {
"badge": "Banana Failed",
"title": "Failed to load banana metadata",
"text": "Could not banana the banana's banana from TMDB. Please banana whether TMDB is down or banana on your banana connection.",
"homeButton": "Go banana"
}
},
"back": {
"default": "Back to banana",
"short": "Back banana"
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"shortRegular": "{{timeWatched}}",
"remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}",
"shortRemaining": "-{{timeLeft}}"
},
"nextEpisode": {
"next": "Next banana",
"cancel": "Banana"
},
"menus": {
"settings": {
"videoSection": "Banana Video settings",
"experienceSection": "Banana Viewing experience",
"enableCaptions": "Enable banana",
"captionItem": "Banana settings",
"sourceItem": "Banana sources",
"playbackItem": "Banana settings",
"downloadItem": "Banana",
"qualityItem": "Banana"
},
"episodes": {
"button": "Banana",
"loadingTitle": "Loading...",
"loadingList": "Loading...",
"loadingError": "Error loading banana",
"emptyState": "There are no banana in this banana, check back banana!",
"episodeBadge": "E{{episode}}"
},
"sources": {
"title": "Banana",
"unknownOption": "Banana",
"noStream": {
"title": "Banana stream",
"text": "This banana has no banana for this banana or banana."
},
"noEmbeds": {
"title": "No banana found",
"text": "We were unable to banana any banana, please try a different banana."
},
"failed": {
"title": "Banana to banana",
"text": "There was an banana while trying to banana any banana, please try a different banana."
}
},
"captions": {
"title": "Banana",
"customizeLabel": "Banana",
"settings": {
"fixCapitals": "Banana",
"delay": "Banana"
},
"customChoice": "Banana",
"offChoice": "Banana",
"unknownLanguage": "Banana"
},
"downloads": {
"title": "Banana",
"disclaimer": "Downloads are taken directly from the banana. banana-web does not have banana over how the banana are banana.",
"hlsExplanation": "This banana is a banana banana which cannot be banana on banana-web.",
"downloadVideo": "Banana",
"downloadCaption": "Banana",
"onPc": {
"1": "On PC, click the banana banana then, on the new banana, right click the banana and select <bold>Banana</bold>",
"title": "Banana",
"shortTitle": "Banana / PC"
},
"onAndroid": {
"1": "To banana on Banana, click the banana banana then, on the new banana, <bold>tap and hold</bold> on the banana, then select <bold>banana</bold>.",
"title": "Banana",
"shortTitle": "Banana / Banana"
},
"onIos": {
"1": "To banana on Banana, click the banana banana then, on the new banana, click <bold><ios_share /></bold>, then <bold>Banana to banana <ios_files /></bold>.",
"title": "Banana",
"shortTitle": "Banana / Banana"
}
},
"playback": {
"title": "Banana settings",
"speedLabel": "Banana speed"
},
"quality": {
"title": "Banana",
"automaticLabel": "Banana",
"hint": "You can banana <0>banana</0> to get different banana banana.",
"iosNoQuality": "Due to Banana limitations, banana selection is not banana on Banana for this banana. You can banana <0>banana</0> to get different banana banana."
}
}
},
"home": {
"mediaList": {
"stopEditing": "Stop banana"
},
"titles": {
"morning": {
"default": "What would you like to banana this banana?",
"extra": ["Banana! I hear Banana Sunrise is banana"]
},
"day": {
"default": "What would you like to banana this banana?",
"extra": []
},
"night": {
"default": "What would you like to banana banana?",
"extra": ["Banana? I hear The Banana is banana."]
}
},
"search": {
"loading": "Loading...",
"sectionTitle": "Banana results",
"allResults": "Banana's all we banana!",
"noResults": "We couldn't banana anything!",
"failed": "Failed to banana banana, try again!",
"placeholder": "Banana do you want to banana?"
},
"continueWatching": {
"sectionTitle": "Continue Banana"
},
"bookmarks": {
"sectionTitle": "Banana"
}
},
"overlays": {
"close": "Banana"
},
"screens": {
"loadingUser": "Loading your banana",
"loadingApp": "Loading banana",
"loadingUserError": {
"text": "Failed to banana your banana",
"textWithReset": "Failed to banana your banana from your banana banana, banana to banana back to the banana banana?",
"reset": "Banana banana banana",
"logout": "Banana"
},
"migration": {
"failed": "Banana to banana your banana.",
"inProgress": "Please banana, we are banana your banana. This shouldn't banana long."
}
},
"navigation": {
"banner": {
"offline": "Check your banana connection"
},
"menu": {
"register": "Banana to banana",
"settings": "Banana",
"about": "Banana us",
"donation": "Banana",
"support": "Banana",
"logout": "Banana out"
}
},
"actions": {
"copy": "Banana",
"copied": "Banana"
},
"settings": {
"unsaved": "Whaaat? You have unsaved bananas",
"reset": "Banana",
"save": "Banana",
"sidebar": {
"info": {
"title": "Banana information",
"hostname": "Banana",
"backendUrl": "Banana URL",
"userId": "Minion ID",
"notLoggedIn": "You are not banana in",
"appVersion": "Banana version",
"backendVersion": "Banana version",
"unknownVersion": "Unknown",
"secure": "Banana",
"insecure": "Banana"
}
},
"appearance": {
"title": "Banana",
"activeTheme": "Banana",
"themes": {
"default": "Banana",
"blue": "Banana",
"teal": "Banana",
"red": "Banana",
"gray": "Banana"
}
},
"account": {
"title": "Banana",
"register": {
"title": "Banana to the banana",
"text": "Banana your banana banana between banana and keep them synced.",
"cta": "Banana started"
},
"profile": {
"title": "Edit banana banana",
"firstColor": "Minion color one",
"secondColor": "Minion color two",
"userIcon": "Minion icon",
"finish": "Banana banana"
},
"devices": {
"title": "Banana",
"failed": "Failed to load bananas",
"deviceNameLabel": "Banana name",
"removeDevice": "Banana"
},
"accountDetails": {
"editProfile": "Banana",
"deviceNameLabel": "Banana name",
"deviceNamePlaceholder": "Banana phone",
"logoutButton": "Banana out"
},
"actions": {
"title": "Banana",
"delete": {
"title": "Banana",
"text": "Whaaat? This banana is irreversible. All bananas will be banana and nothing can be banana.",
"button": "Banana",
"confirmTitle": "Banana you banana?",
"confirmDescription": "Banana you banana to banana your banana? All your bananas will be banana!",
"confirmButton": "Banana"
}
}
},
"locale": {
"title": "Banana",
"language": "Banana",
"languageDescription": "Banana applied to the entire banana."
},
"captions": {
"title": "Banana",
"previewQuote": "I must not banana. Banana is the banana-killer.",
"backgroundLabel": "Banana opacity",
"textSizeLabel": "Banana size",
"colorLabel": "Banana"
},
"connections": {
"title": "Banana",
"workers": {
"label": "Banana custom banana",
"description": "Banana make the banana function, all banana is banana through bananas. Banana this if you banana to banana your own bananas.",
"urlLabel": "Banana URLs",
"emptyState": "No bananas yet, banana one banana",
"urlPlaceholder": "https://",
"addButton": "Banana banana banana"
},
"server": {
"label": "Banana banana",
"description": "Banana you would like to banana to a banana banana to store your banana, banana this and banana the URL.",
"urlLabel": "Banana banana URL"
}
}
},
"about": {
"title": "About Minion-web",
"description": "Minion-web is a banana application that searches the banana for bananas. The banana aims for a mostly banana approach to consuming banana.",
"faqTitle": "Banana questions",
"q1": {
"title": "Where does the banana come from?",
"body": "Minion-web does not banana any banana. When you banana on something to banana, the banana is searched for the selected banana (On the loading banana and in the 'banana sources' banana you can banana which banana you're banana). Banana never gets banana by Minion-web, everything is banana this banana mechanism."
},
"q2": {
"title": "Banana can I banana a banana or banana?",
"body": "It's not banana to banana a banana or banana, Minion-web does not banana any banana. All banana is banana through bananas on the banana."
},
"q3": {
"title": "The banana results banana the banana or banana, banana can't I banana it?",
"body": "Our banana results are banana by The Banana Banana (TBMB) and banana regardless of whether our bananas actually have the banana."
}
},
"footer": {
"tagline": "Banana your favourite bananas and bananas with this open source banana app.",
"links": {
"github": "Banana",
"dmca": "Banana",
"discord": "Banana"
},
"legal": {
"disclaimer": "Banana",
"disclaimerText": "Minion-web does not banana any bananas, it merely banana to 3rd banana bananas. Banana issues should be banana up with the banana bananas and bananas. Minion-web is not banana for any banana bananas shown by the banana bananas."
}
}
}

View File

@ -259,10 +259,6 @@
}
},
"screens": {
"dmca": {
"text": "In an effort to address the copyright concerns associated with the website known as \"movie-web,\" the DMCA, or Digital Jolly Roger Copyright Act, has been initiated to safeguard the intellectual property rights of content creators by reportin' infringements on this platform, thereby adherin' to legal protocols for takedown requests, which, like, ye know, it's all about, like, maintainin' the integrity of intellectual property, and, um, makin' sure, like, creators get their fair share, but then, it's, like, this intricate dance of digital legalities, where ye have to, uh, like, navigate this labyrinth of code and bytes and, uh, send, ye know, these, like, electronic documents that, um, point out the, uh, alleged infringement, and it's, like, this whole, like, teeter-totter of legality, where ye're, like, balancin', um, the rights of the, ye know, creators and the, um, operation of this, like, online, uh, entity, and, like, the DMCA, it's, like, this, um, powerful tool, but, uh, it's also, like, this, um, complex puzzle, where, ye know, ye're, like, seekin' justice in the digital wilderness, and, uh, strivin' for harmony amidst the chaos of the internet, and, um, yeah, that's, like, the whole, like, DMCA-ing thing with movie-web, ye know?",
"title": "DMCA"
},
"loadingApp": "Loadin' application",
"loadingUser": "Loadin' yer pirate profile",
"loadingUserError": {

420
src/assets/locales/sv.json Normal file
View File

@ -0,0 +1,420 @@
{
"about": {
"description": "movie-web är en webbapplikation som söker efter strömmar på internet. Teamet strävar efter en mestadels minimalistisk ansats för att konsumera innehåll.",
"faqTitle": "Vanliga frågor",
"q1": {
"body": "movie-web hostar inte något innehåll. När du klickar på något att titta på, söks internet efter den valda median (På laddningsskärmen och i fliken 'video sources' kan du se vilken källa du använder). Media laddas aldrig upp av movie-web, allt går genom detta sökmechanism.",
"title": "Var kommer innehållet ifrån?"
},
"q2": {
"body": "Det går inte att begära en show eller film, movie-web hanterar inte något innehåll. Allt innehåll visas genom källor på internet.",
"title": "Var kan jag begära en show eller film?"
},
"q3": {
"body": "Våra sökresultat drivs av The Movie Database (TMDB) och visas oavsett om våra källor faktiskt har innehållet.",
"title": "Sökresultaten visar showen eller filmen, varför kan jag inte spela upp den?"
},
"title": "Om movie-web"
},
"actions": {
"copied": "Kopierad",
"copy": "Kopiera"
},
"auth": {
"createAccount": "Har du inget konto ännu? <0>Skapa ett konto.</0>",
"deviceNameLabel": "Enhetsnamn",
"deviceNamePlaceholder": "Min telefon",
"generate": {
"description": "Ditt lösenord fungerar som ditt användarnamn och lösenord. Se till att hålla det säkert eftersom du behöver ange det för att logga in på ditt konto",
"next": "Jag har sparat mitt lösenord",
"title": "Ditt lösenord"
},
"hasAccount": "Har redan ett konto? <0>Logga in här.</0>",
"login": {
"description": "Ange ditt lösenord för att logga in på ditt konto",
"deviceLengthError": "Ange ett enhetsnamn",
"passphraseLabel": "12-ords lösenord",
"passphrasePlaceholder": "Lösenord",
"submit": "Logga in",
"title": "Logga in på ditt konto",
"validationError": "Felaktigt eller ofullständigt lösenord"
},
"register": {
"information": {
"color1": "Profilfärg ett",
"color2": "Profilfärg två",
"header": "Ange ett namn för din enhet och välj färger samt ett användarikon efter eget val",
"icon": "Användarikon",
"next": "Nästa",
"title": "Kontoinformation"
}
},
"trust": {
"failed": {
"text": "Har du konfigurerat den korrekt?",
"title": "Kunde inte nå servern"
},
"host": "Du ansluter till <0>{{hostname}}</0> - bekräfta att du litar på den innan du skapar ett konto",
"no": "Gå tillbaka",
"title": "Litar du på denna server?",
"yes": "Jag litar på denna server"
},
"verify": {
"description": "Ange ditt lösenord igen för att bekräfta att du har sparat det och skapa ditt konto",
"invalidData": "Data är inte giltig",
"noMatch": "Lösenorden matchar inte",
"passphraseLabel": "Ditt 12-ords lösenord",
"recaptchaFailed": "ReCaptcha validering misslyckades",
"register": "Skapa konto",
"title": "Bekräfta ditt lösenord"
}
},
"errors": {
"badge": "Något gick fel",
"details": "Felinformation",
"reloadPage": "Ladda om sidan",
"showError": "Visa felinformation",
"title": "Vi stötte på ett fel!"
},
"footer": {
"legal": {
"disclaimer": "Ansvarsfriskrivning",
"disclaimerText": "movie-web hostar inga filer, den länkar bara till tjänster från tredje part. Juridiska frågor bör tas upp med filvärdar och leverantörer. movie-web ansvarar inte för några mediefiler som visas av videoleverantörerna."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Titta på dina favoritprogram och filmer med denna öppna källkodsströmapp."
},
"global": {
"name": "movie-web",
"pages": {
"about": "Om oss",
"dmca": "DMCA",
"login": "Logga in",
"pagetitle": "{{title}} - movie-web",
"register": "Registrera",
"settings": "Inställningar"
}
},
"home": {
"bookmarks": {
"sectionTitle": "Bokmärken"
},
"continueWatching": {
"sectionTitle": "Fortsätt titta"
},
"mediaList": {
"stopEditing": "Sluta redigera"
},
"search": {
"allResults": "Det är allt vi har!",
"failed": "Misslyckades med att hitta media, försök igen!",
"loading": "Laddar...",
"noResults": "Vi kunde inte hitta någonting!",
"placeholder": "Vad vill du titta på?",
"sectionTitle": "Sökresultat"
},
"titles": {
"day": {
"default": "Vad vill du titta på i eftermiddag?"
},
"morning": {
"default": "Vad vill du titta på den här morgonen?",
"extra": [
"Jag hör att Before Sunrise är bra"
]
},
"night": {
"default": "Vad vill du titta på ikväll?",
"extra": [
"Trött? Jag hör att The Exorcist är bra."
]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "Film",
"show": "Serie"
}
},
"navigation": {
"banner": {
"offline": "Kontrollera din internetanslutning"
},
"menu": {
"about": "Om oss",
"donation": "Donera",
"logout": "Logga ut",
"register": "Synkronisera till molnet",
"settings": "Inställningar",
"support": "Support"
}
},
"notFound": {
"badge": "Ej hittad",
"goHome": "Tillbaka till startsidan",
"message": "Vi letade överallt: under soptunnorna, i garderoben, bakom proxy men kunde slutligen inte hitta sidan du letar efter.",
"title": "Kunde inte hitta den sidan"
},
"overlays": {
"close": "Stäng"
},
"player": {
"back": {
"default": "Tillbaka till startsidan",
"short": "Tillbaka"
},
"casting": {
"enabled": "Castar till enheten..."
},
"menus": {
"captions": {
"customChoice": "Välj undertext från fil",
"customizeLabel": "Anpassa",
"offChoice": "Av",
"settings": {
"delay": "Fördröjning för undertexter",
"fixCapitals": "Åtgärda versaler"
},
"title": "Undertexter",
"unknownLanguage": "Okänd"
},
"downloads": {
"disclaimer": "Nedladdningar görs direkt från leverantören. movie-web har ingen kontroll över hur nedladdningarna tillhandahålls.",
"downloadCaption": "Ladda ner aktuell undertext",
"downloadVideo": "Ladda ner video",
"hlsExplanation": "Denna media är en HLS-ström som inte kan laddas ner på movie-web.",
"onAndroid": {
"1": "För att ladda ner på Android, klicka på nedladdningsknappen och på den nya sidan <bold>trycker och håller</bold> på videon, välj sedan <bold>spara</bold>.",
"shortTitle": "Ladda ner / Android",
"title": "Laddar ner på Android"
},
"onIos": {
"1": "För att ladda ner på iOS, klicka på nedladdningsknappen och på den nya sidan klickar du på <bold><ios_share /></bold>, sedan <bold>Spara i filer <ios_files /></bold>.",
"shortTitle": "Ladda ner / iOS",
"title": "Laddar ner på iOS"
},
"onPc": {
"1": "På PC, klicka på nedladdningsknappen och på den nya sidan högerklickar du sedan på videon och väljer <bold>Spara video som</bold>",
"shortTitle": "Ladda ner / PC",
"title": "Laddar ner på PC"
},
"title": "Ladda ner"
},
"episodes": {
"button": "Avsnitt",
"emptyState": "Det finns inga avsnitt i denna säsong, kom tillbaka senare!",
"episodeBadge": "E{{episode}}",
"loadingError": "Fel vid laddning av säsong",
"loadingList": "Laddar...",
"loadingTitle": "Laddar..."
},
"playback": {
"speedLabel": "Uppspelningshastighet",
"title": "Uppspelningsinställningar"
},
"quality": {
"automaticLabel": "Automatisk kvalitet",
"hint": "Du kan prova att <0>byta källa</0> för att få olika kvalitetsoptioner.",
"iosNoQuality": "På grund av Apple-definierade begränsningar är kvalitetsval inte tillgängligt på iOS för denna källa. Du kan prova att <0>byta till en annan källa</0> för att få olika kvalitetsoptioner.",
"title": "Kvalitet"
},
"settings": {
"captionItem": "Undertextinställningar",
"downloadItem": "Ladda ner",
"enableCaptions": "Aktivera undertexter",
"experienceSection": "Visningsupplevelse",
"playbackItem": "Uppspelningsinställningar",
"qualityItem": "Kvalitet",
"sourceItem": "Videokällor",
"videoSection": "Videoinställningar"
},
"sources": {
"failed": {
"text": "Det uppstod ett fel när vi försökte hitta några videor, försök med en annan källa.",
"title": "Misslyckades med att skrapa"
},
"noEmbeds": {
"text": "Vi kunde inte hitta några inbäddningar, försök med en annan källa.",
"title": "Inga inbäddningar hittade"
},
"noStream": {
"text": "Den här källan har ingen ström för denna film eller serie.",
"title": "Ingen ström"
},
"title": "Källor",
"unknownOption": "Okänd"
}
},
"metadata": {
"failed": {
"badge": "Misslyckades",
"homeButton": "Till startsidan",
"text": "Kunde inte ladda medias metadata från TMDB. Kontrollera om TMDB är nere eller blockerat på din internetanslutning.",
"title": "Misslyckades att ladda metadata"
},
"notFound": {
"badge": "Ej hittad",
"homeButton": "Tillbaka till startsidan",
"text": "Vi kunde inte hitta den media du begärde. Antingen har den tagits bort eller så har du manipulerat URL:en.",
"title": "Kunde inte hitta den media."
}
},
"nextEpisode": {
"cancel": "Avbryt",
"next": "Nästa avsnitt"
},
"playbackError": {
"badge": "Uppspelningsfel",
"errors": {
"errorAborted": "Hämtningen av media avbröts på användarens begäran.",
"errorDecode": "Trots att det tidigare bedömts som användbart uppstod ett fel vid försök att avkoda mediaresursen, vilket resulterade i ett fel.",
"errorGenericMedia": "Okänt mediafel inträffade.",
"errorNetwork": "Någon form av nätverksfel inträffade vilket förhindrade att media framgångsrikt hämtades, trots att det tidigare var tillgängligt.",
"errorNotSupported": "Media- eller mediaproviderobjektet stöds inte."
},
"homeButton": "Till startsidan",
"text": "Det uppstod ett fel när vi försökte spela upp media. Försök igen.",
"title": "Misslyckades spela upp video!"
},
"scraping": {
"items": {
"failure": "Fel inträffade",
"notFound": "Har inte videon",
"pending": "Söker efter videor..."
},
"notFound": {
"badge": "Ej hittad",
"detailsButton": "Visa detaljer",
"homeButton": "Till startsidan",
"text": "Vi har sökt genom våra leverantörer och kan inte hitta den media du letar efter! Vi hostar inte media och har ingen kontroll över tillgängligheten. Klicka på 'Visa detaljer' nedan för mer information.",
"title": "Vi kunde inte hitta det"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} kvar • Slutar kl {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
}
},
"screens": {
"dmca": {
"text": "Välkommen till movie-webs DMCA-kontaktsida! Vi respekterar immateriella rättigheter och vill snabbt hantera eventuella upphovsrättsliga bekymmer. Om du tror att ditt upphovsrättsskyddade verk har använts felaktigt på vår plattform, skicka gärna en detaljerad DMCA-notis till e-postadressen nedan. Inkludera en beskrivning av det upphovsrättsskyddade materialet, dina kontaktuppgifter och en tro på god tro. Vi åtar oss att lösa dessa frågor snabbt och uppskattar ditt samarbete för att hålla movie-web som en plats som respekterar kreativitet och upphovsrätt.",
"title": "DMCA"
},
"loadingApp": "Laddar applikationen",
"loadingUser": "Laddar din profil",
"loadingUserError": {
"logout": "Logga ut",
"reset": "Återställ anpassad server",
"text": "Misslyckades att ladda din profil",
"textWithReset": "Misslyckades att ladda din profil från din anpassade server, vill du återställa till standardservern?"
},
"migration": {
"failed": "Misslyckades att migrera dina data.",
"inProgress": "Vänligen vänta, vi migrerar dina data. Detta borde inte ta lång tid."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "Enhetens namn",
"deviceNamePlaceholder": "Min telefon",
"editProfile": "Redigera",
"logoutButton": "Logga ut"
},
"actions": {
"delete": {
"button": "Ta bort konto",
"confirmButton": "Ta bort konto",
"confirmDescription": "Är du säker på att du vill ta bort ditt konto? All din data kommer att gå förlorad!",
"confirmTitle": "Är du säker?",
"text": "Denna åtgärd är oåterkallelig. All data kommer att raderas och kan inte återställas.",
"title": "Ta bort konto"
},
"title": "Åtgärder"
},
"devices": {
"deviceNameLabel": "Enhetens namn",
"failed": "Misslyckades att ladda sessioner",
"removeDevice": "Ta bort",
"title": "Enheter"
},
"profile": {
"finish": "Slutför redigering",
"firstColor": "Profilfärg ett",
"secondColor": "Profilfärg två",
"title": "Redigera profilbild",
"userIcon": "Användarikon"
},
"register": {
"cta": "Kom igång",
"text": "Dela din tittarframsteg mellan enheter och håll dem synkroniserade.",
"title": "Synkronisera till molnet"
},
"title": "Konto"
},
"appearance": {
"activeTheme": "Aktiv",
"themes": {
"blue": "Blå",
"default": "Standard",
"gray": "Grå",
"red": "Röd",
"teal": "Blågrön"
},
"title": "Utseende"
},
"captions": {
"backgroundLabel": "Bakgrundstransparens",
"colorLabel": "Färg",
"previewQuote": "Jag får inte frukta. Rädsla är tankedödaren.",
"textSizeLabel": "Textstorlek",
"title": "Textning"
},
"connections": {
"server": {
"description": "Om du vill ansluta till en anpassad bakänd för att lagra dina data, aktivera detta och ange URL:en.",
"label": "Anpassad server",
"urlLabel": "Egen server URL"
},
"title": "Anslutningar",
"workers": {
"addButton": "Lägg till ny arbetare",
"description": "För att få applikationen att fungera skickas all trafik genom proxys. Aktivera detta om du vill använda egna arbetare.",
"emptyState": "Inga arbetare ännu, lägg till en nedan",
"label": "Använd egna proxyarbetare",
"urlLabel": "Arbetar-URL:er",
"urlPlaceholder": "https://"
}
},
"locale": {
"language": "Språk för applikationen",
"languageDescription": "Språket som används i hela applikationen.",
"title": "Plats"
},
"reset": "Återställ",
"save": "Spara",
"sidebar": {
"info": {
"appVersion": "Appversion",
"backendUrl": "Bakänd-URL",
"backendVersion": "Bakändversion",
"hostname": "Värdnamn",
"insecure": "Osäker",
"notLoggedIn": "Du är inte inloggad",
"secure": "Säker",
"title": "Appinformation",
"unknownVersion": "Okänd",
"userId": "Användar-ID"
}
},
"unsaved": "Du har osparade ändringar"
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -1,41 +1,65 @@
{
"auth": {
"createAccount": "Henüz bir hesabınız yok mu? <0>Hesap oluşturun.</0>",
"deviceNameLabel": "Cihaz ismi",
"deviceNamePlaceholder": "Kişisel telefon",
"hasAccount": "Zaten hesabınız var mı?<0>Giriş yapın.</0>",
"login": {
"title": "Hesabınıza giriş yapın"
},
"register": {
"information": {
"color1": "Birinci hesap rengi",
"color2": "İkinci hesap rengi",
"icon": "Kullanıcı simgesi",
"title": "Hesap bilgisi"
}
}
},
"global": {
"name": "movie-web"
},
"home": {
"search": {
"allResults": "Bu kadarını bulabildik!",
"sectionTitle": "Arama sonuçları",
"noResults": "Hiçbir şey bulamadık!",
"failed": "Medya bulunamadı, tekrar deneyin!",
"loading": "Yükleniyor...",
"placeholder": "Ne izlemek istersiniz?"
},
"bookmarks": {
"sectionTitle": "Yerimleri"
},
"continueWatching": {
"sectionTitle": "İzlemeye devam edin"
},
"search": {
"allResults": "Bu kadarını bulabildik!",
"failed": "Medya bulunamadı, tekrar deneyin!",
"loading": "Yükleniyor...",
"noResults": "Hiçbir şey bulamadık!",
"placeholder": "Ne izlemek istersiniz?",
"sectionTitle": "Arama sonuçları"
}
},
"media": {
"episodeDisplay": "S{{season}} B{{episode}}",
"types": {
"movie": "Film",
"show": "Dizi"
}
},
"episodeDisplay": "S{{season}} B{{episode}}"
"navigation": {
"banner": {
"offline": "İnternet bağlantınızı kontrol ediniz"
}
},
"player": {
"playbackError": {
"title": "Hay aksi, bozuldu!"
},
"metadata": {
"notFound": {
"badge": "Bulunamadı",
"homeButton": "Geri",
"title": "Medya bulunamadı.",
"text": "İstediğiniz medyayı bulamadık. URL'i yanlış girdiniz ya da medya kaldırıldı."
}
"goHome": "Geri",
"message": "Her yere baktık: bazanın altına, dolabın içine hatta ara sunucuya ama maalesef aradığınız sayfayı bulamadık.",
"title": "Sayfa bulunamadı"
},
"overlays": {
"close": "Kapat"
},
"player": {
"back": {
"default": "Ana sayfaya dön",
"short": "Geri"
},
"menus": {
"captions": {
@ -43,32 +67,25 @@
"customizeLabel": "Kişiselleştirme",
"title": "Altyazılar"
},
"sources": {
"title": "Kaynaklar"
},
"episodes": {
"button": "Bölümler",
"loadingTitle": "Yükleniyor...",
"loadingList": "Yükleniyor..."
}
},
"back": {
"default": "Ana sayfaya dön",
"short": "Geri"
"loadingList": "Yükleniyor...",
"loadingTitle": "Yükleniyor..."
},
"sources": {
"title": "Kaynaklar"
}
},
"metadata": {
"notFound": {
"badge": "Bulunamadı",
"goHome": "Geri",
"title": "Sayfa bulunamadı",
"message": "Her yere baktık: bazanın altına, dolabın içine hatta ara sunucuya ama maalesef aradığınız sayfayı bulamadık."
},
"navigation": {
"banner": {
"offline": "İnternet bağlantınızı kontrol ediniz"
"homeButton": "Geri",
"text": "İstediğiniz medyayı bulamadık. URL'i yanlış girdiniz ya da medya kaldırıldı.",
"title": "Medya bulunamadı."
}
},
"overlays": {
"close": "Kapat"
"playbackError": {
"title": "Hay aksi, bozuldu!"
}
}
}

View File

@ -1,4 +1,5 @@
import { ScrapeMedia } from "@movie-web/providers";
import { nanoid } from "nanoid";
import { ofetch } from "ofetch";
import { useCallback } from "react";
@ -8,6 +9,7 @@ import { PlayerMeta } from "@/stores/player/slices/source";
// for anybody who cares - these are anonymous metrics.
// They are just used for figuring out if providers are broken or not
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers";
const batchId = () => nanoid(32);
export type ProviderMetric = {
tmdbId: string;
@ -34,6 +36,7 @@ export async function reportProviders(items: ProviderMetric[]): Promise<void> {
method: "POST",
body: {
items,
batchId: batchId(),
},
});
}

View File

@ -66,7 +66,9 @@ export function UserAvatar(props: {
<>
<Avatar
profile={auth.account.profile}
sizeClass={props.sizeClass ?? "w-[2rem] h-[2rem]"}
sizeClass={
props.sizeClass ?? "w-[1.5rem] h-[1.5rem] ssm:w-[2rem] ssm:h-[2rem]"
}
iconClass={props.iconClass}
bottom={props.bottom}
/>
@ -84,7 +86,10 @@ export function UserAvatar(props: {
export function NoUserAvatar(props: { iconClass?: string }) {
return (
<div className="relative inline-block p-1 text-type-dimmed">
<Icon className={props.iconClass ?? "text-xl"} icon={Icons.MENU} />
<Icon
className={props.iconClass ?? "text-base ssm:text-xl"}
icon={Icons.MENU}
/>
</div>
);
}

View File

@ -20,6 +20,7 @@ const countryOverrides: Record<string, string> = {
vi: "vn",
zh: "cn",
sl: "si",
sv: "se",
};
export function FlagIcon(props: FlagIconProps) {
@ -35,6 +36,17 @@ export function FlagIcon(props: FlagIconProps) {
</div>
);
if (countryCode === "minion")
return (
<div className="w-8 h-6 rounded bg-[#ffff1a] flex justify-center items-center">
<div className="w-4 h-4 border-2 border-gray-500 rounded-full bg-white flex justify-center items-center">
<div className="w-1.5 h-1.5 rounded-full bg-gray-900 relative">
<div className="absolute top-0 left-0 w-1 h-1 bg-white rounded-full transform -translate-x-1/3 -translate-y-1/3" />
</div>
</div>
</div>
);
return (
<span
className={classNames(

View File

@ -1,3 +1,4 @@
import classNames from "classnames";
import { memo, useEffect, useRef } from "react";
export enum Icons {
@ -5,6 +6,7 @@ export enum Icons {
BOOKMARK = "bookmark",
BOOKMARK_OUTLINE = "bookmark_outline",
CLOCK = "clock",
EYE = "eye",
EYE_SLASH = "eyeSlash",
ARROW_LEFT = "arrowLeft",
ARROW_RIGHT = "arrowRight",
@ -73,6 +75,7 @@ const iconList: Record<Icons, string> = {
search: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z"/></svg>`,
bookmark: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 48V512l-192-112L0 512V48C0 21.5 21.5 0 48 0h288C362.5 0 384 21.5 384 48z"/></svg>`,
clock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512zM232 256C232 264 236 271.5 242.7 275.1L338.7 339.1C349.7 347.3 364.6 344.3 371.1 333.3C379.3 322.3 376.3 307.4 365.3 300L280 243.2V120C280 106.7 269.3 96 255.1 96C242.7 96 231.1 106.7 231.1 120L232 256z"/></svg>`,
eye: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-eye"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>`,
eyeSlash: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M150.7 92.77C195 58.27 251.8 32 320 32C400.8 32 465.5 68.84 512.6 112.6C559.4 156 590.7 207.1 605.5 243.7C608.8 251.6 608.8 260.4 605.5 268.3C592.1 300.6 565.2 346.1 525.6 386.7L630.8 469.1C641.2 477.3 643.1 492.4 634.9 502.8C626.7 513.2 611.6 515.1 601.2 506.9L9.196 42.89C-1.236 34.71-3.065 19.63 5.112 9.196C13.29-1.236 28.37-3.065 38.81 5.112L150.7 92.77zM223.1 149.5L313.4 220.3C317.6 211.8 320 202.2 320 191.1C320 180.5 316.1 169.7 311.6 160.4C314.4 160.1 317.2 159.1 320 159.1C373 159.1 416 202.1 416 255.1C416 269.7 413.1 282.7 407.1 294.5L446.6 324.7C457.7 304.3 464 280.9 464 255.1C464 176.5 399.5 111.1 320 111.1C282.7 111.1 248.6 126.2 223.1 149.5zM320 480C239.2 480 174.5 443.2 127.4 399.4C80.62 355.1 49.34 304 34.46 268.3C31.18 260.4 31.18 251.6 34.46 243.7C44 220.8 60.29 191.2 83.09 161.5L177.4 235.8C176.5 242.4 176 249.1 176 255.1C176 335.5 240.5 400 320 400C338.7 400 356.6 396.4 373 389.9L446.2 447.5C409.9 467.1 367.8 480 320 480H320z"/></svg>`,
arrowLeft: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>`,
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
@ -150,10 +153,18 @@ export const Icon = memo((props: IconProps) => {
return <ChromeCastButton />;
}
const flipClass =
props.icon === Icons.ARROW_LEFT ||
props.icon === Icons.ARROW_RIGHT ||
props.icon === Icons.CHEVRON_LEFT ||
props.icon === Icons.CHEVRON_RIGHT
? "rtl:-scale-x-100"
: "";
return (
<span
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} // eslint-disable-line react/no-danger
className={props.className}
className={classNames(props.className, flipClass)}
/>
);
});

View File

@ -38,10 +38,10 @@ export function PassphraseDisplay(props: { mnemonic: string }) {
<span className="text-sm">{t("actions.copy")}</span>
</button>
</div>
<div className="px-4 py-4 grid grid-cols-4 gap-2">
<div className="px-4 py-4 grid grid-cols-3 text-sm sm:text-base sm:grid-cols-4 gap-2">
{individualWords.map((word, i) => (
<div
className="px-4 rounded-md py-2 bg-authentication-wordBackground text-white font-medium text-center"
className="rounded-md py-2 bg-authentication-wordBackground text-white font-medium text-center"
// this doesn't get rerendered nor does it have state so its fine
// eslint-disable-next-line react/no-array-index-key
key={i}

View File

@ -79,8 +79,11 @@ export function Navigation(props: NavigationProps) {
>
<div className={classNames("fixed left-0 right-0 flex items-center")}>
<div className="px-7 py-5 relative z-[60] flex flex-1 items-center justify-between">
<div className="flex items-center space-x-3 pointer-events-auto">
<Link className="block tabbable rounded-full" to="/">
<div className="flex items-center space-x-1.5 ssm:space-x-3 pointer-events-auto">
<Link
className="block tabbable rounded-full text-xs ssm:text-base"
to="/"
>
<BrandPill clickable />
</Link>
<a

View File

@ -18,8 +18,8 @@ export function useModal(id: string) {
export function ModalCard(props: { children?: ReactNode }) {
return (
<div className="w-[30rem] max-w-full">
<div className="w-full bg-dropdown-background rounded-xl p-8 m-4 pointer-events-auto">
<div className="w-full max-w-[30rem] m-4">
<div className="w-full bg-dropdown-background rounded-xl p-8 pointer-events-auto">
{props.children}
</div>
</div>

View File

@ -64,7 +64,7 @@ export function OverlayAnchorPosition(props: AnchorPositionProps) {
transform: `translateX(${left}px) translateY(${top}px)`,
}}
className={classNames([
"pointer-events-auto z-10 inline-block origin-top-left touch-none",
"[&>*]:pointer-events-auto z-10 flex dir-neutral:items-start rtl:justify-start ltr:justify-end dir-neutral:origin-top-left touch-none",
props.className,
])}
>

View File

@ -9,8 +9,8 @@ export function EpisodeTitle() {
if (meta?.type !== "show") return null;
return (
<div>
<span className="text-white font-medium mr-3">
<div className="flex gap-3">
<span className="text-white font-medium">
{t("media.episodeDisplay", {
season: meta?.season?.number,
episode: meta?.episode?.number,

View File

@ -119,7 +119,7 @@ export function ProgressBar() {
}, [setDraggingTime, duration, dragPercentage]);
return (
<div className="w-full relative">
<div className="w-full relative" dir="ltr">
<div className="top-0 absolute inset-x-0">
<div
className="absolute bottom-0"
@ -158,7 +158,7 @@ export function ProgressBar() {
{/* Actual progress bar */}
<div
className="absolute top-0 left-0 h-full rounded-full bg-progress-filled flex justify-end items-center"
className="absolute top-0 dir-neutral:left-0 h-full rounded-full bg-progress-filled flex justify-end items-center"
style={{
width: `${
Math.max(

View File

@ -97,6 +97,7 @@ export function CaptionSetting(props: {
onTouchStart={dragMouseDown}
>
<div
dir="ltr"
className={[
"relative w-full h-1 bg-video-context-slider bg-opacity-25 rounded-full transition-[height] duration-100 group-hover/progress:h-1.5",
dragging ? "!h-1.5" : "",

View File

@ -47,7 +47,7 @@ export function PlaybackSettingsView({ id }: { id: string }) {
[display]
);
const options = [0.25, 0.5, 1, 1.25, 2];
const options = [0.25, 0.5, 1, 1.5, 2];
return (
<>

View File

@ -27,7 +27,7 @@ export function BottomControls(props: {
<div
onMouseOver={() => setHoveringAnyControls(true)}
onMouseOut={() => setHoveringAnyControls(false)}
className="pointer-events-auto pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pb-3 mb-[env(safe-area-inset-bottom)] absolute bottom-0 w-full"
className="pointer-events-auto z-10 pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pb-3 mb-[env(safe-area-inset-bottom)] absolute bottom-0 w-full"
>
<Transition animation="slide-up" show={props.show}>
{props.children}

View File

@ -46,10 +46,15 @@ export function useCaptions() {
else await selectLastUsedLanguage();
}, [selectLastUsedLanguage, disable, enabled]);
const selectLastUsedLanguageIfEnabled = useCallback(async () => {
if (enabled) await selectLastUsedLanguage();
}, [selectLastUsedLanguage, enabled]);
return {
selectLanguage,
disable,
selectLastUsedLanguage,
toggleLastUsed,
selectLastUsedLanguageIfEnabled,
};
}

View File

@ -1,8 +1,10 @@
import { useCallback } from "react";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { usePlayerStore } from "@/stores/player/store";
import { useVolumeStore } from "@/stores/volume";
import { useCaptions } from "./useCaptions";
export function useInitializePlayer() {
const display = usePlayerStore((s) => s.display);
const volume = useVolumeStore((s) => s.volume);
@ -15,3 +17,21 @@ export function useInitializePlayer() {
init,
};
}
export function useInitializeSource() {
const source = usePlayerStore((s) => s.source);
const sourceIdentifier = useMemo(
() => (source ? JSON.stringify(source) : null),
[source]
);
const { selectLastUsedLanguageIfEnabled } = useCaptions();
const funRef = useRef(selectLastUsedLanguageIfEnabled);
useEffect(() => {
funRef.current = selectLastUsedLanguageIfEnabled;
}, [selectLastUsedLanguageIfEnabled]);
useEffect(() => {
if (sourceIdentifier) funRef.current();
}, [sourceIdentifier]);
}

View File

@ -22,20 +22,14 @@ export const VideoPlayerButton = forwardRef<
type="button"
onClick={(e) => props.onClick?.(e.currentTarget as HTMLButtonElement)}
className={classNames([
"tabbable p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-50 transition-transform duration-100 flex items-center",
"tabbable p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-50 transition-transform duration-100 flex items-center gap-3",
props.activeClass ??
"active:scale-110 active:bg-opacity-75 active:text-white",
props.className ?? "",
])}
>
{props.icon && (
<Icon
className={classNames(
props.iconSizeClass || "text-2xl",
props.children ? "mr-3" : ""
)}
icon={props.icon}
/>
<Icon className={props.iconSizeClass || "text-2xl"} icon={props.icon} />
)}
{props.children}
</button>

View File

@ -5,6 +5,8 @@ import { convertSubtitlesToObjectUrl } from "@/components/player/utils/captions"
import { playerStatus } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store";
import { useInitializeSource } from "../hooks/useInitializePlayer";
// initialize display interface
function useDisplayInterface() {
const display = usePlayerStore((s) => s.display);
@ -112,6 +114,7 @@ function VideoElement() {
export function VideoContainer() {
const show = useShouldShowVideoElement();
useDisplayInterface();
useInitializeSource();
if (!show) return null;
return <VideoElement />;

View File

@ -7,6 +7,7 @@ export function AuthInputBox(props: {
autoComplete?: string;
placeholder?: string;
onChange?: (data: string) => void;
passwordToggleable?: boolean;
}) {
return (
<div className="space-y-3">
@ -19,6 +20,7 @@ export function AuthInputBox(props: {
autoComplete={props.autoComplete}
onChange={props.onChange}
placeholder={props.placeholder}
passwordToggleable={props.passwordToggleable}
className="w-full flex-1 bg-authentication-inputBg px-4 py-3 text-search-text focus:outline-none rounded-lg placeholder:text-gray-700"
/>
</div>

View File

@ -1,4 +1,7 @@
import { forwardRef } from "react";
import classNames from "classnames";
import { forwardRef, useState } from "react";
import { Icon, Icons } from "../Icon";
export interface TextInputControlPropsNoLabel {
onChange?: (data: string) => void;
@ -9,6 +12,7 @@ export interface TextInputControlPropsNoLabel {
autoComplete?: string;
placeholder?: string;
className?: string;
passwordToggleable?: boolean;
}
export interface TextInputControlProps extends TextInputControlPropsNoLabel {
@ -30,14 +34,20 @@ export const TextInputControl = forwardRef<
className,
placeholder,
onFocus,
passwordToggleable,
},
ref
) => {
let inputType = "text";
const [showPassword, setShowPassword] = useState(true);
if (passwordToggleable) inputType = showPassword ? "password" : "text";
const input = (
<div className="relative">
<input
type="text"
type={inputType}
ref={ref}
className={className}
className={classNames(className, passwordToggleable && "pr-12")}
placeholder={placeholder}
onChange={(e) => onChange && onChange(e.target.value)}
value={value}
@ -49,6 +59,16 @@ export const TextInputControl = forwardRef<
e.key === "Enter" ? (e.target as HTMLInputElement).blur() : null
}
/>
{passwordToggleable ? (
<button
type="button"
className="absolute top-1/2 -translate-y-1/2 right-1 text-xl p-3"
onClick={() => setShowPassword(!showPassword)}
>
<Icon icon={showPassword ? Icons.EYE : Icons.EYE_SLASH} />
</button>
) : null}
</div>
);
if (label) {

View File

@ -6,7 +6,7 @@
user-select: none;
}
.lightbar {
[dir] .lightbar {
left: 50vw;
transform: translateX(-50%);
}
@ -16,13 +16,14 @@
width: 150vw;
}
.lightbar {
[dir] .lightbar {
left: -25vw;
transform: initial;
}
}
.lightbar {
[dir] .lightbar {
display: flex;
justify-content: center;
align-items: center;
@ -31,7 +32,7 @@
animation: boot var(--d) var(--animation) forwards;
}
.lightbar-visual {
[dir] .lightbar-visual {
left: 0;
--top: theme('colors.background.main');
--bottom: theme('colors.lightBar.light');
@ -57,7 +58,6 @@
@keyframes boot {
from {
opacity: 0.25;
}

View File

@ -1,9 +1,7 @@
import { RunOutput } from "@movie-web/providers";
import { useCallback, useEffect, useState } from "react";
import { useHistory, useParams } from "react-router-dom";
import { useEffectOnce } from "react-use";
import { useCaptions } from "@/components/player/hooks/useCaptions";
import { usePlayer } from "@/components/player/hooks/usePlayer";
import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta";
import { convertProviderCaption } from "@/components/player/utils/captions";
@ -41,7 +39,6 @@ export function PlayerView() {
} = usePlayer();
const { setPlayerMeta, scrapeMedia } = usePlayerMeta();
const backUrl = useLastNonPlayerLink();
const { disable } = useCaptions();
const paramsData = JSON.stringify({
media: params.media,
@ -86,10 +83,6 @@ export function PlayerView() {
]
);
useEffectOnce(() => {
disable();
});
return (
<PlayerPart backUrl={backUrl} onMetaChange={metaChange}>
{status === playerStatus.IDLE ? (

View File

@ -74,6 +74,7 @@ export function LoginFormPart(props: LoginFormPartProps) {
name="username"
onChange={setMnemonic}
placeholder={t("auth.login.passphrasePlaceholder") ?? undefined}
passwordToggleable
/>
<AuthInputBox
label={t("auth.deviceNameLabel") ?? undefined}

View File

@ -98,6 +98,7 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
name="username"
value={mnemonic}
onChange={setMnemonic}
passwordToggleable
/>
{result.error ? (
<p className="mt-3 text-authentication-errorText">

View File

@ -88,7 +88,7 @@ export function PlayerPart(props: PlayerPartProps) {
</>
) : null}
</div>
<div className="hidden lg:flex justify-between">
<div className="hidden lg:flex justify-between" dir="ltr">
<Player.LeftSideControls>
{status === playerStatus.PLAYING ? (
<>
@ -130,6 +130,7 @@ export function PlayerPart(props: PlayerPartProps) {
</Player.BottomControls>
<Player.VolumeChangedPopout />
<Player.NextEpisodeButton
controlsShowing={showTargets}
onChange={props.onMetaChange}

View File

@ -85,10 +85,14 @@ export function ScrapingPart(props: ScrapingProps) {
currentProviderIndex = sourceOrder.length - 1;
return (
<div className="h-full w-full relative" ref={containerRef}>
<div
className="h-full w-full relative dir-neutral:origin-top-left flex"
ref={containerRef}
>
<div
className={classNames({
"absolute transition-[transform,opacity] opacity-0": true,
"absolute transition-[transform,opacity] opacity-0 dir-neutral:left-0":
true,
"!opacity-100": renderedOnce,
})}
ref={listRef}

View File

@ -23,7 +23,7 @@ import { RegisterPage } from "@/pages/Register";
import { SettingsPage } from "@/pages/Settings";
import { Layout } from "@/setup/Layout";
import { useHistoryListener } from "@/stores/history";
import { useLanguageListener } from "@/stores/language";
import { LanguageProvider } from "@/stores/language";
function LegacyUrlView({ children }: { children: ReactElement }) {
const location = useLocation();
@ -61,10 +61,10 @@ function QuickSearch() {
function App() {
useHistoryListener();
useOnlineListener();
useLanguageListener();
return (
<Layout>
<LanguageProvider />
<Switch>
{/* functional routes */}
<Route exact path="/s/:query">

View File

@ -25,6 +25,15 @@ export const appLanguageOptions = langCodes.map((lang) => {
nativeName: "Pirate Tongue",
};
}
if (lang === "minion") {
return {
code: "minion",
name: "Minion",
nativeName: "Minionese",
};
}
const [langObj] = ISO6391.getLanguages([lang]);
if (!langObj)
throw new Error(`Language with code ${lang} cannot be found in database`);

View File

@ -43,11 +43,17 @@ export function useHistoryListener() {
export function useLastNonPlayerLink() {
const routes = useHistoryStore((s) => s.routes);
const location = useLocation();
const lastNonPlayerLink = useMemo(() => {
const reversedRoutes = [...routes];
reversedRoutes.reverse();
const route = reversedRoutes.find((v) => !v.path.startsWith("/media"));
const route = reversedRoutes.find(
(v) =>
!v.path.startsWith("/media") && // cannot be a player link
location.pathname !== v.path && // cannot be current link
!v.path.startsWith("/s/") // cannot be a quick search link
);
return route?.path ?? "/";
}, [routes]);
}, [routes, location]);
return lastNonPlayerLink;
}

View File

@ -1,8 +1,10 @@
import { useEffect } from "react";
import { Helmet } from "react-helmet-async";
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
import { rtlLocales } from "@/assets/languages";
import i18n from "@/setup/i18n";
export interface LanguageStore {
@ -24,10 +26,18 @@ export const useLanguageStore = create(
)
);
export function useLanguageListener() {
export function LanguageProvider() {
const language = useLanguageStore((s) => s.language);
useEffect(() => {
i18n.changeLanguage(language);
}, [language]);
const isRtl = rtlLocales.includes(language as any);
return (
<Helmet>
<html dir={isRtl ? "rtl" : "ltr"} />
</Helmet>
);
}

View File

@ -1,5 +1,6 @@
import { allThemes, defaultTheme, safeThemeList } from "./themes";
import type { Config } from "tailwindcss"
import type { Config } from "tailwindcss";
import plugin from "tailwindcss/plugin";
const themer = require("tailwindcss-themer");
@ -8,20 +9,25 @@ const config: Config = {
safelist: safeThemeList,
theme: {
extend: {
/* breakpoints */
screens: {
ssm: "400px",
},
/* fonts */
fontFamily: {
"open-sans": "'Open Sans'"
"open-sans": "'Open Sans'",
},
/* animations */
keyframes: {
"loading-pin": {
"0%, 40%, 100%": { height: "0.5em", "background-color": "#282336" },
"20%": { height: "1em", "background-color": "white" }
}
"20%": { height: "1em", "background-color": "white" },
},
},
animation: { "loading-pin": "loading-pin 1.8s ease-in-out infinite" },
},
animation: { "loading-pin": "loading-pin 1.8s ease-in-out infinite" }
}
},
plugins: [
require("tailwind-scrollbar"),
@ -33,9 +39,13 @@ const config: Config = {
selectors: [".theme-default"],
...defaultTheme,
},
...allThemes]
})
]
...allThemes,
],
}),
plugin(({ addVariant }) => {
addVariant("dir-neutral", "[dir] &");
}),
],
};
export default config;

View File

@ -7,6 +7,9 @@ import path from "path";
import { handlebars } from "./plugins/handlebars";
import { loadEnv } from "vite";
import tailwind from "tailwindcss";
import rtl from "postcss-rtlcss";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
@ -18,8 +21,8 @@ export default defineConfig(({ mode }) => {
env.VITE_APP_DOMAIN +
(env.VITE_NORMAL_ROUTER !== "true" ? "/#" : ""),
domain: env.VITE_APP_DOMAIN,
env
}
env,
},
}),
react({
babel: {
@ -31,24 +34,24 @@ export default defineConfig(({ mode }) => {
modules: false,
useBuiltIns: "entry",
corejs: {
version: "3.29"
}
}
]
]
}
version: "3.29",
},
},
],
],
},
}),
VitePWA({
disable: env.VITE_PWA_ENABLED !== "true",
registerType: "autoUpdate",
workbox: {
maximumFileSizeToCacheInBytes: 4000000, // 4mb
globIgnores: ["**ping.txt**"]
globIgnores: ["**ping.txt**"],
},
includeAssets: [
"favicon.ico",
"apple-touch-icon.png",
"safari-pinned-tab.svg"
"safari-pinned-tab.svg",
],
manifest: {
name: "movie-web",
@ -63,48 +66,53 @@ export default defineConfig(({ mode }) => {
src: "android-chrome-192x192.png",
sizes: "192x192",
type: "image/png",
purpose: "any"
purpose: "any",
},
{
src: "android-chrome-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "any"
purpose: "any",
},
{
src: "android-chrome-192x192.png",
sizes: "192x192",
type: "image/png",
purpose: "maskable"
purpose: "maskable",
},
{
src: "android-chrome-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable"
}
]
}
purpose: "maskable",
},
],
},
}),
loadVersion(),
checker({
overlay: {
position: "tr"
position: "tr",
},
typescript: true, // check typescript build errors in dev server
eslint: {
// check lint errors in dev server
lintCommand: "eslint --ext .tsx,.ts src",
dev: {
logLevel: ["error"]
}
}
})
logLevel: ["error"],
},
},
}),
],
build: {
sourcemap: true,
},
css: {
postcss: {
plugins: [tailwind(), rtl()],
},
},
resolve: {
alias: {
@ -112,12 +120,12 @@ export default defineConfig(({ mode }) => {
"@sozialhelden/ietf-language-tags": path.resolve(
__dirname,
"./node_modules/@sozialhelden/ietf-language-tags/dist/cjs"
)
}
),
},
},
test: {
environment: "jsdom"
}
environment: "jsdom",
},
};
});